summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Plugins/Meebo/Docs/meebo_changelog.txt6
-rw-r--r--Plugins/Meebo/Docs/meebo_readme.txt23
-rw-r--r--Plugins/Meebo/Docs/meebo_version.txt1
-rw-r--r--Plugins/Meebo/Icons/proto_meebo.dllbin0 -> 20584 bytes
-rw-r--r--Plugins/Meebo/ZIP/doit.bat64
-rw-r--r--Plugins/Meebo/commons.h64
-rw-r--r--Plugins/Meebo/meebo.cpp609
-rw-r--r--Plugins/Meebo/meebo.dsp215
-rw-r--r--Plugins/Meebo/meebo.dsw29
-rw-r--r--Plugins/Meebo/resource.h22
-rw-r--r--Plugins/Meebo/resource.rc120
-rw-r--r--Plugins/Meebo/sdk/m_updater.h146
-rw-r--r--Plugins/avatarhistory/AvatarDlg.cpp651
-rw-r--r--Plugins/avatarhistory/AvatarHistory.cpp1053
-rw-r--r--Plugins/avatarhistory/AvatarHistory.dsp248
-rw-r--r--Plugins/avatarhistory/AvatarHistory.dsw29
-rw-r--r--Plugins/avatarhistory/AvatarHistory.h120
-rw-r--r--Plugins/avatarhistory/AvatarHistory.rc277
-rw-r--r--Plugins/avatarhistory/AvatarOverlay.icobin0 -> 1150 bytes
-rw-r--r--Plugins/avatarhistory/Docs/avatarhist.pngbin0 -> 25934 bytes
-rw-r--r--Plugins/avatarhistory/Docs/avatarhist_changelog.txt44
-rw-r--r--Plugins/avatarhistory/Docs/avatarhist_readme.txt29
-rw-r--r--Plugins/avatarhistory/Docs/avatarhist_version.txt1
-rw-r--r--Plugins/avatarhistory/Docs/langpack_avatarhist.txt114
-rw-r--r--Plugins/avatarhistory/ZIP/doit.bat81
-rw-r--r--Plugins/avatarhistory/history.icobin0 -> 1150 bytes
-rw-r--r--Plugins/avatarhistory/icolib.cpp137
-rw-r--r--Plugins/avatarhistory/m_avatarhist.h55
-rw-r--r--Plugins/avatarhistory/options.cpp239
-rw-r--r--Plugins/avatarhistory/popup.cpp354
-rw-r--r--Plugins/avatarhistory/popup.h52
-rw-r--r--Plugins/avatarhistory/resource.h87
-rw-r--r--Plugins/avatarhistory/sdk/m_folders.h207
-rw-r--r--Plugins/avatarhistory/sdk/m_freeimage.h965
-rw-r--r--Plugins/avatarhistory/sdk/m_imgsrvc.h502
-rw-r--r--Plugins/avatarhistory/sdk/m_metacontacts.h162
-rw-r--r--Plugins/avatarhistory/sdk/m_updater.h146
-rw-r--r--Plugins/avh_import/avh_import.c767
-rw-r--r--Plugins/avh_import/avh_import.dsp168
-rw-r--r--Plugins/avh_import/avh_import.dsw29
-rw-r--r--Plugins/avh_import/sdk/m_avatarhist.h51
-rw-r--r--Plugins/avh_import/sdk/m_folders.h207
-rw-r--r--Plugins/avh_import/sdk/m_freeimage.h965
-rw-r--r--Plugins/avh_import/sdk/m_imgsrvc.h502
-rw-r--r--Plugins/avh_import/sdk/m_metacontacts.h162
-rw-r--r--Plugins/bclist/Docs/bclist.pngbin0 -> 18361 bytes
-rw-r--r--Plugins/bclist/Docs/bclist_changelog.txt30
-rw-r--r--Plugins/bclist/Docs/bclist_readme.txt10
-rw-r--r--Plugins/bclist/Docs/bclist_version.txt1
-rw-r--r--Plugins/bclist/ZIP/doit.bat84
-rw-r--r--Plugins/bclist/bclist.dsp531
-rw-r--r--Plugins/bclist/bclist.dsw29
-rw-r--r--Plugins/bclist/clc.h83
-rw-r--r--Plugins/bclist/clcopts.c277
-rw-r--r--Plugins/bclist/clcpaint.c587
-rw-r--r--Plugins/bclist/clistmenus.c36
-rw-r--r--Plugins/bclist/clistopts.c254
-rw-r--r--Plugins/bclist/cluiopts.c346
-rw-r--r--Plugins/bclist/commonheaders.c1
-rw-r--r--Plugins/bclist/commonheaders.h78
-rw-r--r--Plugins/bclist/forkthread.c94
-rw-r--r--Plugins/bclist/forkthread.h63
-rw-r--r--Plugins/bclist/init.c833
-rw-r--r--Plugins/bclist/m_updater.h146
-rw-r--r--Plugins/bclist/res/blank.icobin0 -> 318 bytes
-rw-r--r--Plugins/bclist/res/delete.icobin0 -> 1406 bytes
-rw-r--r--Plugins/bclist/res/dragcopy.curbin0 -> 326 bytes
-rw-r--r--Plugins/bclist/res/dropuser.curbin0 -> 1086 bytes
-rw-r--r--Plugins/bclist/res/hyperlin.curbin0 -> 326 bytes
-rw-r--r--Plugins/bclist/res/rename.icobin0 -> 1406 bytes
-rw-r--r--Plugins/bclist/resource.h162
-rw-r--r--Plugins/bclist/resource.rc463
-rw-r--r--Plugins/eSpeak/Docs/langpack_meSpeak.txt2
-rw-r--r--Plugins/eSpeak/Docs/meSpeak.pngbin0 -> 34081 bytes
-rw-r--r--Plugins/eSpeak/Docs/meSpeak_changelog.txt57
-rw-r--r--Plugins/eSpeak/Docs/meSpeak_readme.txt30
-rw-r--r--Plugins/eSpeak/Docs/meSpeak_version.txt1
-rw-r--r--Plugins/eSpeak/ZIP/doit.bat132
-rw-r--r--Plugins/eSpeak/commons.h243
-rw-r--r--Plugins/eSpeak/eSpeak.cpp1348
-rw-r--r--Plugins/eSpeak/eSpeak.dsp383
-rw-r--r--Plugins/eSpeak/eSpeak.dsw29
-rw-r--r--Plugins/eSpeak/eSpeak.sln26
-rw-r--r--Plugins/eSpeak/eSpeak.vcproj1402
-rw-r--r--Plugins/eSpeak/eSpeak/StdAfx.h3
-rw-r--r--Plugins/eSpeak/eSpeak/compiledict.cpp1649
-rw-r--r--Plugins/eSpeak/eSpeak/debug.cpp74
-rw-r--r--Plugins/eSpeak/eSpeak/debug.h26
-rw-r--r--Plugins/eSpeak/eSpeak/dictionary.cpp3433
-rw-r--r--Plugins/eSpeak/eSpeak/espeak_command.h129
-rw-r--r--Plugins/eSpeak/eSpeak/event.h51
-rw-r--r--Plugins/eSpeak/eSpeak/fifo.h58
-rw-r--r--Plugins/eSpeak/eSpeak/intonation.cpp1104
-rw-r--r--Plugins/eSpeak/eSpeak/klatt.cpp1282
-rw-r--r--Plugins/eSpeak/eSpeak/klatt.h138
-rw-r--r--Plugins/eSpeak/eSpeak/mbrolib.h205
-rw-r--r--Plugins/eSpeak/eSpeak/numbers.cpp1401
-rw-r--r--Plugins/eSpeak/eSpeak/phoneme.h168
-rw-r--r--Plugins/eSpeak/eSpeak/phonemelist.cpp687
-rw-r--r--Plugins/eSpeak/eSpeak/portaudio.h466
-rw-r--r--Plugins/eSpeak/eSpeak/portaudio18.h466
-rw-r--r--Plugins/eSpeak/eSpeak/portaudio19.h1127
-rw-r--r--Plugins/eSpeak/eSpeak/readclause.cpp2402
-rw-r--r--Plugins/eSpeak/eSpeak/setlengths.cpp673
-rw-r--r--Plugins/eSpeak/eSpeak/sintab.h258
-rw-r--r--Plugins/eSpeak/eSpeak/speak_lib.cpp1153
-rw-r--r--Plugins/eSpeak/eSpeak/speak_lib.h604
-rw-r--r--Plugins/eSpeak/eSpeak/speech.h84
-rw-r--r--Plugins/eSpeak/eSpeak/stdint.h4
-rw-r--r--Plugins/eSpeak/eSpeak/synth_mbrola.cpp760
-rw-r--r--Plugins/eSpeak/eSpeak/synthdata.cpp681
-rw-r--r--Plugins/eSpeak/eSpeak/synthesize.cpp1660
-rw-r--r--Plugins/eSpeak/eSpeak/synthesize.h349
-rw-r--r--Plugins/eSpeak/eSpeak/tr_languages.cpp1310
-rw-r--r--Plugins/eSpeak/eSpeak/translate.cpp2771
-rw-r--r--Plugins/eSpeak/eSpeak/translate.h576
-rw-r--r--Plugins/eSpeak/eSpeak/voice.h81
-rw-r--r--Plugins/eSpeak/eSpeak/voices.cpp1743
-rw-r--r--Plugins/eSpeak/eSpeak/wave.h43
-rw-r--r--Plugins/eSpeak/eSpeak/wavegen.cpp1917
-rw-r--r--Plugins/eSpeak/espeak-data/af_dictbin0 -> 65248 bytes
-rw-r--r--Plugins/eSpeak/espeak-data/ca_dictbin0 -> 4231 bytes
-rw-r--r--Plugins/eSpeak/espeak-data/config9
-rw-r--r--Plugins/eSpeak/espeak-data/cs_dictbin0 -> 7833 bytes
-rw-r--r--Plugins/eSpeak/espeak-data/cy_dictbin0 -> 3541 bytes
-rw-r--r--Plugins/eSpeak/espeak-data/de_dictbin0 -> 19044 bytes
-rw-r--r--Plugins/eSpeak/espeak-data/el_dictbin0 -> 4767 bytes
-rw-r--r--Plugins/eSpeak/espeak-data/en_dictbin0 -> 82255 bytes
-rw-r--r--Plugins/eSpeak/espeak-data/eo_dictbin0 -> 3467 bytes
-rw-r--r--Plugins/eSpeak/espeak-data/es_dictbin0 -> 5489 bytes
-rw-r--r--Plugins/eSpeak/espeak-data/fi_dictbin0 -> 4570 bytes
-rw-r--r--Plugins/eSpeak/espeak-data/fr_dictbin0 -> 16820 bytes
-rw-r--r--Plugins/eSpeak/espeak-data/grc_dictbin0 -> 3390 bytes
-rw-r--r--Plugins/eSpeak/espeak-data/hbs_dictbin0 -> 7444 bytes
-rw-r--r--Plugins/eSpeak/espeak-data/hi_dictbin0 -> 5825 bytes
-rw-r--r--Plugins/eSpeak/espeak-data/hu_dictbin0 -> 5266 bytes
-rw-r--r--Plugins/eSpeak/espeak-data/hy_dictbin0 -> 1752 bytes
-rw-r--r--Plugins/eSpeak/espeak-data/id_dictbin0 -> 3083 bytes
-rw-r--r--Plugins/eSpeak/espeak-data/is_dictbin0 -> 5550 bytes
-rw-r--r--Plugins/eSpeak/espeak-data/it_dictbin0 -> 6457 bytes
-rw-r--r--Plugins/eSpeak/espeak-data/jbo_dictbin0 -> 2025 bytes
-rw-r--r--Plugins/eSpeak/espeak-data/ku_dictbin0 -> 2277 bytes
-rw-r--r--Plugins/eSpeak/espeak-data/la_dictbin0 -> 3911 bytes
-rw-r--r--Plugins/eSpeak/espeak-data/lv_dictbin0 -> 5758 bytes
-rw-r--r--Plugins/eSpeak/espeak-data/mbrola_ph/af1_phtransbin0 -> 1636 bytes
-rw-r--r--Plugins/eSpeak/espeak-data/mbrola_ph/ca1_phtransbin0 -> 1372 bytes
-rw-r--r--Plugins/eSpeak/espeak-data/mbrola_ph/cr1_phtransbin0 -> 2140 bytes
-rw-r--r--Plugins/eSpeak/espeak-data/mbrola_ph/cs_phtransbin0 -> 580 bytes
-rw-r--r--Plugins/eSpeak/espeak-data/mbrola_ph/de2_phtransbin0 -> 1444 bytes
-rw-r--r--Plugins/eSpeak/espeak-data/mbrola_ph/de4_phtransbin0 -> 1588 bytes
-rw-r--r--Plugins/eSpeak/espeak-data/mbrola_ph/de6_phtransbin0 -> 1204 bytes
-rw-r--r--Plugins/eSpeak/espeak-data/mbrola_ph/en1_phtransbin0 -> 772 bytes
-rw-r--r--Plugins/eSpeak/espeak-data/mbrola_ph/es_phtransbin0 -> 1684 bytes
-rw-r--r--Plugins/eSpeak/espeak-data/mbrola_ph/fr1_phtransbin0 -> 1780 bytes
-rw-r--r--Plugins/eSpeak/espeak-data/mbrola_ph/gr2_phtransbin0 -> 2212 bytes
-rw-r--r--Plugins/eSpeak/espeak-data/mbrola_ph/grc-de6_phtransbin0 -> 484 bytes
-rw-r--r--Plugins/eSpeak/espeak-data/mbrola_ph/hu1_phtransbin0 -> 1396 bytes
-rw-r--r--Plugins/eSpeak/espeak-data/mbrola_ph/id1_phtransbin0 -> 820 bytes
-rw-r--r--Plugins/eSpeak/espeak-data/mbrola_ph/in1_phtransbin0 -> 1156 bytes
-rw-r--r--Plugins/eSpeak/espeak-data/mbrola_ph/it3_phtransbin0 -> 892 bytes
-rw-r--r--Plugins/eSpeak/espeak-data/mbrola_ph/la1_phtransbin0 -> 748 bytes
-rw-r--r--Plugins/eSpeak/espeak-data/mbrola_ph/nl_phtransbin0 -> 1612 bytes
-rw-r--r--Plugins/eSpeak/espeak-data/mbrola_ph/pl1_phtransbin0 -> 1540 bytes
-rw-r--r--Plugins/eSpeak/espeak-data/mbrola_ph/pt_phtransbin0 -> 2092 bytes
-rw-r--r--Plugins/eSpeak/espeak-data/mbrola_ph/ptbr4_phtransbin0 -> 2356 bytes
-rw-r--r--Plugins/eSpeak/espeak-data/mbrola_ph/ptbr_phtransbin0 -> 2500 bytes
-rw-r--r--Plugins/eSpeak/espeak-data/mbrola_ph/ro1_phtransbin0 -> 2116 bytes
-rw-r--r--Plugins/eSpeak/espeak-data/mbrola_ph/sv2_phtransbin0 -> 1564 bytes
-rw-r--r--Plugins/eSpeak/espeak-data/mbrola_ph/sv_phtransbin0 -> 1564 bytes
-rw-r--r--Plugins/eSpeak/espeak-data/mbrola_ph/us3_phtransbin0 -> 988 bytes
-rw-r--r--Plugins/eSpeak/espeak-data/mbrola_ph/us_phtransbin0 -> 1060 bytes
-rw-r--r--Plugins/eSpeak/espeak-data/mk_dictbin0 -> 4955 bytes
-rw-r--r--Plugins/eSpeak/espeak-data/nl_dictbin0 -> 4124 bytes
-rw-r--r--Plugins/eSpeak/espeak-data/no_dictbin0 -> 3735 bytes
-rw-r--r--Plugins/eSpeak/espeak-data/phondatabin0 -> 292152 bytes
-rw-r--r--Plugins/eSpeak/espeak-data/phonindexbin0 -> 26836 bytes
-rw-r--r--Plugins/eSpeak/espeak-data/phontabbin0 -> 32824 bytes
-rw-r--r--Plugins/eSpeak/espeak-data/pl_dictbin0 -> 40527 bytes
-rw-r--r--Plugins/eSpeak/espeak-data/pt_dictbin0 -> 15111 bytes
-rw-r--r--Plugins/eSpeak/espeak-data/ro_dictbin0 -> 24984 bytes
-rw-r--r--Plugins/eSpeak/espeak-data/ru_dictbin0 -> 5701 bytes
-rw-r--r--Plugins/eSpeak/espeak-data/sk_dictbin0 -> 8762 bytes
-rw-r--r--Plugins/eSpeak/espeak-data/sq_dictbin0 -> 2114 bytes
-rw-r--r--Plugins/eSpeak/espeak-data/sv_dictbin0 -> 9508 bytes
-rw-r--r--Plugins/eSpeak/espeak-data/sw_dictbin0 -> 3048 bytes
-rw-r--r--Plugins/eSpeak/espeak-data/ta_dictbin0 -> 2582 bytes
-rw-r--r--Plugins/eSpeak/espeak-data/tr_dictbin0 -> 4768 bytes
-rw-r--r--Plugins/eSpeak/espeak-data/vi_dictbin0 -> 4265 bytes
-rw-r--r--Plugins/eSpeak/espeak-data/voices/!v/croak11
-rw-r--r--Plugins/eSpeak/espeak-data/voices/!v/f118
-rw-r--r--Plugins/eSpeak/espeak-data/voices/!v/f220
-rw-r--r--Plugins/eSpeak/espeak-data/voices/!v/f322
-rw-r--r--Plugins/eSpeak/espeak-data/voices/!v/f418
-rw-r--r--Plugins/eSpeak/espeak-data/voices/!v/m119
-rw-r--r--Plugins/eSpeak/espeak-data/voices/!v/m215
-rw-r--r--Plugins/eSpeak/espeak-data/voices/!v/m316
-rw-r--r--Plugins/eSpeak/espeak-data/voices/!v/m417
-rw-r--r--Plugins/eSpeak/espeak-data/voices/!v/m515
-rw-r--r--Plugins/eSpeak/espeak-data/voices/!v/m613
-rw-r--r--Plugins/eSpeak/espeak-data/voices/!v/wisper13
-rw-r--r--Plugins/eSpeak/espeak-data/voices/af8
-rw-r--r--Plugins/eSpeak/espeak-data/voices/bs16
-rw-r--r--Plugins/eSpeak/espeak-data/voices/ca4
-rw-r--r--Plugins/eSpeak/espeak-data/voices/cs4
-rw-r--r--Plugins/eSpeak/espeak-data/voices/cy5
-rw-r--r--Plugins/eSpeak/espeak-data/voices/de5
-rw-r--r--Plugins/eSpeak/espeak-data/voices/default6
-rw-r--r--Plugins/eSpeak/espeak-data/voices/el5
-rw-r--r--Plugins/eSpeak/espeak-data/voices/en/en11
-rw-r--r--Plugins/eSpeak/espeak-data/voices/en/en-n14
-rw-r--r--Plugins/eSpeak/espeak-data/voices/en/en-rp12
-rw-r--r--Plugins/eSpeak/espeak-data/voices/en/en-sc16
-rw-r--r--Plugins/eSpeak/espeak-data/voices/en/en-wi19
-rw-r--r--Plugins/eSpeak/espeak-data/voices/en/en-wm12
-rw-r--r--Plugins/eSpeak/espeak-data/voices/eo4
-rw-r--r--Plugins/eSpeak/espeak-data/voices/es7
-rw-r--r--Plugins/eSpeak/espeak-data/voices/es-la11
-rw-r--r--Plugins/eSpeak/espeak-data/voices/fi4
-rw-r--r--Plugins/eSpeak/espeak-data/voices/fr7
-rw-r--r--Plugins/eSpeak/espeak-data/voices/fr-be7
-rw-r--r--Plugins/eSpeak/espeak-data/voices/hi9
-rw-r--r--Plugins/eSpeak/espeak-data/voices/hr18
-rw-r--r--Plugins/eSpeak/espeak-data/voices/hu3
-rw-r--r--Plugins/eSpeak/espeak-data/voices/id8
-rw-r--r--Plugins/eSpeak/espeak-data/voices/is4
-rw-r--r--Plugins/eSpeak/espeak-data/voices/it6
-rw-r--r--Plugins/eSpeak/espeak-data/voices/ku6
-rw-r--r--Plugins/eSpeak/espeak-data/voices/la13
-rw-r--r--Plugins/eSpeak/espeak-data/voices/lv6
-rw-r--r--Plugins/eSpeak/espeak-data/voices/mb/mb-af17
-rw-r--r--Plugins/eSpeak/espeak-data/voices/mb/mb-af1-en7
-rw-r--r--Plugins/eSpeak/espeak-data/voices/mb/mb-br19
-rw-r--r--Plugins/eSpeak/espeak-data/voices/mb/mb-br39
-rw-r--r--Plugins/eSpeak/espeak-data/voices/mb/mb-br49
-rw-r--r--Plugins/eSpeak/espeak-data/voices/mb/mb-cr19
-rw-r--r--Plugins/eSpeak/espeak-data/voices/mb/mb-cz26
-rw-r--r--Plugins/eSpeak/espeak-data/voices/mb/mb-de26
-rw-r--r--Plugins/eSpeak/espeak-data/voices/mb/mb-de46
-rw-r--r--Plugins/eSpeak/espeak-data/voices/mb/mb-de4-en6
-rw-r--r--Plugins/eSpeak/espeak-data/voices/mb/mb-de510
-rw-r--r--Plugins/eSpeak/espeak-data/voices/mb/mb-de5-en7
-rw-r--r--Plugins/eSpeak/espeak-data/voices/mb/mb-de66
-rw-r--r--Plugins/eSpeak/espeak-data/voices/mb/mb-de6-grc6
-rw-r--r--Plugins/eSpeak/espeak-data/voices/mb/mb-de77
-rw-r--r--Plugins/eSpeak/espeak-data/voices/mb/mb-en17
-rw-r--r--Plugins/eSpeak/espeak-data/voices/mb/mb-es17
-rw-r--r--Plugins/eSpeak/espeak-data/voices/mb/mb-es27
-rw-r--r--Plugins/eSpeak/espeak-data/voices/mb/mb-fr19
-rw-r--r--Plugins/eSpeak/espeak-data/voices/mb/mb-fr1-en8
-rw-r--r--Plugins/eSpeak/espeak-data/voices/mb/mb-fr48
-rw-r--r--Plugins/eSpeak/espeak-data/voices/mb/mb-fr4-en8
-rw-r--r--Plugins/eSpeak/espeak-data/voices/mb/mb-gr26
-rw-r--r--Plugins/eSpeak/espeak-data/voices/mb/mb-gr2-en6
-rw-r--r--Plugins/eSpeak/espeak-data/voices/mb/mb-hu16
-rw-r--r--Plugins/eSpeak/espeak-data/voices/mb/mb-hu1-en6
-rw-r--r--Plugins/eSpeak/espeak-data/voices/mb/mb-id17
-rw-r--r--Plugins/eSpeak/espeak-data/voices/mb/mb-it38
-rw-r--r--Plugins/eSpeak/espeak-data/voices/mb/mb-it48
-rw-r--r--Plugins/eSpeak/espeak-data/voices/mb/mb-la16
-rw-r--r--Plugins/eSpeak/espeak-data/voices/mb/mb-nl27
-rw-r--r--Plugins/eSpeak/espeak-data/voices/mb/mb-nl2-en7
-rw-r--r--Plugins/eSpeak/espeak-data/voices/mb/mb-pl16
-rw-r--r--Plugins/eSpeak/espeak-data/voices/mb/mb-pl1-en6
-rw-r--r--Plugins/eSpeak/espeak-data/voices/mb/mb-pt19
-rw-r--r--Plugins/eSpeak/espeak-data/voices/mb/mb-ro17
-rw-r--r--Plugins/eSpeak/espeak-data/voices/mb/mb-ro1-en7
-rw-r--r--Plugins/eSpeak/espeak-data/voices/mb/mb-sw17
-rw-r--r--Plugins/eSpeak/espeak-data/voices/mb/mb-sw1-en7
-rw-r--r--Plugins/eSpeak/espeak-data/voices/mb/mb-sw27
-rw-r--r--Plugins/eSpeak/espeak-data/voices/mb/mb-sw2-en7
-rw-r--r--Plugins/eSpeak/espeak-data/voices/mb/mb-us112
-rw-r--r--Plugins/eSpeak/espeak-data/voices/mb/mb-us212
-rw-r--r--Plugins/eSpeak/espeak-data/voices/mb/mb-us312
-rw-r--r--Plugins/eSpeak/espeak-data/voices/mk4
-rw-r--r--Plugins/eSpeak/espeak-data/voices/nl3
-rw-r--r--Plugins/eSpeak/espeak-data/voices/no6
-rw-r--r--Plugins/eSpeak/espeak-data/voices/pl5
-rw-r--r--Plugins/eSpeak/espeak-data/voices/pt7
-rw-r--r--Plugins/eSpeak/espeak-data/voices/pt-pt7
-rw-r--r--Plugins/eSpeak/espeak-data/voices/ro5
-rw-r--r--Plugins/eSpeak/espeak-data/voices/ru6
-rw-r--r--Plugins/eSpeak/espeak-data/voices/sk4
-rw-r--r--Plugins/eSpeak/espeak-data/voices/sr15
-rw-r--r--Plugins/eSpeak/espeak-data/voices/sv4
-rw-r--r--Plugins/eSpeak/espeak-data/voices/sw4
-rw-r--r--Plugins/eSpeak/espeak-data/voices/ta6
-rw-r--r--Plugins/eSpeak/espeak-data/voices/tr4
-rw-r--r--Plugins/eSpeak/espeak-data/voices/vi6
-rw-r--r--Plugins/eSpeak/espeak-data/voices/zh30
-rw-r--r--Plugins/eSpeak/espeak-data/zh_dictbin0 -> 41747 bytes
-rw-r--r--Plugins/eSpeak/espeak-data/zhy_dictbin0 -> 1561 bytes
-rw-r--r--Plugins/eSpeak/lib/PAStaticWMME.libbin0 -> 30392 bytes
-rw-r--r--Plugins/eSpeak/m_speak.h278
-rw-r--r--Plugins/eSpeak/options.cpp1036
-rw-r--r--Plugins/eSpeak/options.h78
-rw-r--r--Plugins/eSpeak/res/unknown.icobin0 -> 894 bytes
-rw-r--r--Plugins/eSpeak/resource.h64
-rw-r--r--Plugins/eSpeak/resource.rc220
-rw-r--r--Plugins/eSpeak/sdk/m_folders.h205
-rw-r--r--Plugins/eSpeak/sdk/m_metacontacts.h162
-rw-r--r--Plugins/eSpeak/sdk/m_updater.h146
-rw-r--r--Plugins/eSpeak/sdk/m_variables.h718
-rw-r--r--Plugins/eSpeak/types.cpp232
-rw-r--r--Plugins/eSpeak/types.h34
-rw-r--r--Plugins/ersatz/Docs/ersatz_changelog.txt9
-rw-r--r--Plugins/ersatz/Docs/ersatz_version.txt1
-rw-r--r--Plugins/ersatz/ZIP/doit.bat103
-rw-r--r--Plugins/ersatz/commons.h68
-rw-r--r--Plugins/ersatz/ersatz.cpp154
-rw-r--r--Plugins/ersatz/ersatz.dsp137
-rw-r--r--Plugins/ersatz/ersatz.dsw29
-rw-r--r--Plugins/ersatz/m_ersatz.h46
-rw-r--r--Plugins/ersatz/sdk/m_updater.h146
-rw-r--r--Plugins/ersatz/services.cpp519
-rw-r--r--Plugins/ersatz/services.h33
-rw-r--r--Plugins/historyevents/Docs/historyevents.pngbin0 -> 45947 bytes
-rw-r--r--Plugins/historyevents/Docs/historyevents_changelog.txt33
-rw-r--r--Plugins/historyevents/Docs/historyevents_readme.txt19
-rw-r--r--Plugins/historyevents/Docs/historyevents_version.txt1
-rw-r--r--Plugins/historyevents/ZIP/doit.bat104
-rw-r--r--Plugins/historyevents/commons.h76
-rw-r--r--Plugins/historyevents/historyevents.cpp1264
-rw-r--r--Plugins/historyevents/historyevents.dsp231
-rw-r--r--Plugins/historyevents/historyevents.dsw29
-rw-r--r--Plugins/historyevents/historyevents.sln26
-rw-r--r--Plugins/historyevents/historyevents.vcproj635
-rw-r--r--Plugins/historyevents/m_historyevents.h453
-rw-r--r--Plugins/historyevents/options.cpp536
-rw-r--r--Plugins/historyevents/options.h62
-rw-r--r--Plugins/historyevents/resource.h89
-rw-r--r--Plugins/historyevents/resource.rc109
-rw-r--r--Plugins/historyevents/sdk/m_metacontacts.h162
-rw-r--r--Plugins/historyevents/sdk/m_updater.h146
-rw-r--r--Plugins/historyevents/sdk/m_variables.h718
-rw-r--r--Plugins/historykeeper/Docs/historykeeper_changelog.txt84
-rw-r--r--Plugins/historykeeper/Docs/historykeeper_readme.txt31
-rw-r--r--Plugins/historykeeper/Docs/historykeeper_version.txt1
-rw-r--r--Plugins/historykeeper/Docs/langpack_historykeeper.txt77
-rw-r--r--Plugins/historykeeper/ZIP/doit.bat57
-rw-r--r--Plugins/historykeeper/commons.h152
-rw-r--r--Plugins/historykeeper/config.cpp198
-rw-r--r--Plugins/historykeeper/historykeeper.cpp1257
-rw-r--r--Plugins/historykeeper/historykeeper.dsp315
-rw-r--r--Plugins/historykeeper/historykeeper.dsw29
-rw-r--r--Plugins/historykeeper/m_historykeeper.h95
-rw-r--r--Plugins/historykeeper/options.cpp814
-rw-r--r--Plugins/historykeeper/options.h101
-rw-r--r--Plugins/historykeeper/popup.cpp376
-rw-r--r--Plugins/historykeeper/popup.h47
-rw-r--r--Plugins/historykeeper/res/avatar_change.icobin0 -> 2550 bytes
-rw-r--r--Plugins/historykeeper/res/client_change.icobin0 -> 2038 bytes
-rw-r--r--Plugins/historykeeper/res/fh_client_change.icobin0 -> 2550 bytes
-rw-r--r--Plugins/historykeeper/res/fh_nick_change.icobin0 -> 2550 bytes
-rw-r--r--Plugins/historykeeper/res/fh_sm_change.icobin0 -> 2550 bytes
-rw-r--r--Plugins/historykeeper/res/fh_status_change.icobin0 -> 2550 bytes
-rw-r--r--Plugins/historykeeper/res/fh_xstatus_change.icobin0 -> 2550 bytes
-rw-r--r--Plugins/historykeeper/res/file.icobin0 -> 2550 bytes
-rw-r--r--Plugins/historykeeper/res/history.icobin0 -> 2550 bytes
-rw-r--r--Plugins/historykeeper/res/idle_change.icobin0 -> 2550 bytes
-rw-r--r--Plugins/historykeeper/res/listening_change.icobin0 -> 1782 bytes
-rw-r--r--Plugins/historykeeper/res/nick_change.icobin0 -> 2038 bytes
-rw-r--r--Plugins/historykeeper/res/orig_client_change.icobin0 -> 894 bytes
-rw-r--r--Plugins/historykeeper/res/orig_nick_change.icobin0 -> 2550 bytes
-rw-r--r--Plugins/historykeeper/res/orig_sm_change.icobin0 -> 2550 bytes
-rw-r--r--Plugins/historykeeper/res/orig_status_change.icobin0 -> 2550 bytes
-rw-r--r--Plugins/historykeeper/res/orig_xstatus_change.icobin0 -> 2550 bytes
-rw-r--r--Plugins/historykeeper/res/overlay.icobin0 -> 2038 bytes
-rw-r--r--Plugins/historykeeper/res/popup.icobin0 -> 894 bytes
-rw-r--r--Plugins/historykeeper/res/sm_change.icobin0 -> 2038 bytes
-rw-r--r--Plugins/historykeeper/res/smalldot.icobin0 -> 318 bytes
-rw-r--r--Plugins/historykeeper/res/sound.icobin0 -> 2038 bytes
-rw-r--r--Plugins/historykeeper/res/speak.icobin0 -> 2550 bytes
-rw-r--r--Plugins/historykeeper/res/status_change.icobin0 -> 2038 bytes
-rw-r--r--Plugins/historykeeper/res/xsm_change.icobin0 -> 2038 bytes
-rw-r--r--Plugins/historykeeper/res/xstatus_change.icobin0 -> 2038 bytes
-rw-r--r--Plugins/historykeeper/resource.h91
-rw-r--r--Plugins/historykeeper/resource.rc384
-rw-r--r--Plugins/historykeeper/sdk/m_metacontacts.h162
-rw-r--r--Plugins/historykeeper/sdk/m_proto_listeningto.h143
-rw-r--r--Plugins/historykeeper/sdk/m_speak.h278
-rw-r--r--Plugins/historykeeper/sdk/m_updater.h146
-rw-r--r--Plugins/historykeeper/sdk/m_variables.h718
-rw-r--r--Plugins/jingle/Docs/jingle_changelog.txt6
-rw-r--r--Plugins/jingle/Docs/jingle_readme.txt35
-rw-r--r--Plugins/jingle/Docs/jingle_version.txt1
-rw-r--r--Plugins/jingle/ZIP/doit.bat54
-rw-r--r--Plugins/jingle/commons.h147
-rw-r--r--Plugins/jingle/jingle.cpp869
-rw-r--r--Plugins/jingle/jingle.sln39
-rw-r--r--Plugins/jingle/jingle.vcproj600
-rw-r--r--Plugins/jingle/libjingle/COPYING25
-rw-r--r--Plugins/jingle/libjingle/talk/base/Equifax_Secure_Global_eBusiness_CA-1.h55
-rw-r--r--Plugins/jingle/libjingle/talk/base/asyncfile.h56
-rw-r--r--Plugins/jingle/libjingle/talk/base/asynchttprequest.cc155
-rw-r--r--Plugins/jingle/libjingle/talk/base/asynchttprequest.h140
-rw-r--r--Plugins/jingle/libjingle/talk/base/asyncpacketsocket.cc84
-rw-r--r--Plugins/jingle/libjingle/talk/base/asyncpacketsocket.h63
-rw-r--r--Plugins/jingle/libjingle/talk/base/asyncsocket.h91
-rw-r--r--Plugins/jingle/libjingle/talk/base/asynctcpsocket.cc200
-rw-r--r--Plugins/jingle/libjingle/talk/base/asynctcpsocket.h68
-rw-r--r--Plugins/jingle/libjingle/talk/base/asyncudpsocket.cc85
-rw-r--r--Plugins/jingle/libjingle/talk/base/asyncudpsocket.h59
-rw-r--r--Plugins/jingle/libjingle/talk/base/autodetectproxy.cc178
-rw-r--r--Plugins/jingle/libjingle/talk/base/autodetectproxy.h68
-rw-r--r--Plugins/jingle/libjingle/talk/base/base64.cc196
-rw-r--r--Plugins/jingle/libjingle/talk/base/base64.h32
-rw-r--r--Plugins/jingle/libjingle/talk/base/basicdefs.h37
-rw-r--r--Plugins/jingle/libjingle/talk/base/basictypes.h85
-rw-r--r--Plugins/jingle/libjingle/talk/base/bytebuffer.cc166
-rw-r--r--Plugins/jingle/libjingle/talk/base/bytebuffer.h71
-rw-r--r--Plugins/jingle/libjingle/talk/base/byteorder.h63
-rw-r--r--Plugins/jingle/libjingle/talk/base/common.cc62
-rw-r--r--Plugins/jingle/libjingle/talk/base/common.h120
-rw-r--r--Plugins/jingle/libjingle/talk/base/convert.h149
-rw-r--r--Plugins/jingle/libjingle/talk/base/criticalsection.h120
-rw-r--r--Plugins/jingle/libjingle/talk/base/cryptstring.h185
-rw-r--r--Plugins/jingle/libjingle/talk/base/diskcache.cc362
-rw-r--r--Plugins/jingle/libjingle/talk/base/diskcache.h142
-rw-r--r--Plugins/jingle/libjingle/talk/base/diskcache_win32.cc76
-rw-r--r--Plugins/jingle/libjingle/talk/base/diskcache_win32.h28
-rw-r--r--Plugins/jingle/libjingle/talk/base/diskcachestd.cc30
-rw-r--r--Plugins/jingle/libjingle/talk/base/diskcachestd.h28
-rw-r--r--Plugins/jingle/libjingle/talk/base/event.h79
-rw-r--r--Plugins/jingle/libjingle/talk/base/fileutils.cc273
-rw-r--r--Plugins/jingle/libjingle/talk/base/fileutils.h185
-rw-r--r--Plugins/jingle/libjingle/talk/base/firewallsocketserver.cc213
-rw-r--r--Plugins/jingle/libjingle/talk/base/firewallsocketserver.h95
-rw-r--r--Plugins/jingle/libjingle/talk/base/helpers.cc148
-rw-r--r--Plugins/jingle/libjingle/talk/base/helpers.h55
-rw-r--r--Plugins/jingle/libjingle/talk/base/host.cc100
-rw-r--r--Plugins/jingle/libjingle/talk/base/host.h59
-rw-r--r--Plugins/jingle/libjingle/talk/base/httpbase.cc591
-rw-r--r--Plugins/jingle/libjingle/talk/base/httpbase.h142
-rw-r--r--Plugins/jingle/libjingle/talk/base/httpclient.cc714
-rw-r--r--Plugins/jingle/libjingle/talk/base/httpclient.h155
-rw-r--r--Plugins/jingle/libjingle/talk/base/httpcommon-inl.h114
-rw-r--r--Plugins/jingle/libjingle/talk/base/httpcommon.cc940
-rw-r--r--Plugins/jingle/libjingle/talk/base/httpcommon.h373
-rw-r--r--Plugins/jingle/libjingle/talk/base/httpserver.cc261
-rw-r--r--Plugins/jingle/libjingle/talk/base/httpserver.h143
-rw-r--r--Plugins/jingle/libjingle/talk/base/icftypes.h116
-rw-r--r--Plugins/jingle/libjingle/talk/base/linked_ptr.h115
-rw-r--r--Plugins/jingle/libjingle/talk/base/logging.cc349
-rw-r--r--Plugins/jingle/libjingle/talk/base/logging.h296
-rw-r--r--Plugins/jingle/libjingle/talk/base/md5.h45
-rw-r--r--Plugins/jingle/libjingle/talk/base/md5c.c256
-rw-r--r--Plugins/jingle/libjingle/talk/base/messagequeue.cc360
-rw-r--r--Plugins/jingle/libjingle/talk/base/messagequeue.h207
-rw-r--r--Plugins/jingle/libjingle/talk/base/nat_unittest.cc223
-rw-r--r--Plugins/jingle/libjingle/talk/base/natserver.cc210
-rw-r--r--Plugins/jingle/libjingle/talk/base/natserver.h114
-rw-r--r--Plugins/jingle/libjingle/talk/base/natserver_main.cc57
-rw-r--r--Plugins/jingle/libjingle/talk/base/natsocketfactory.cc228
-rw-r--r--Plugins/jingle/libjingle/talk/base/natsocketfactory.h51
-rw-r--r--Plugins/jingle/libjingle/talk/base/nattypes.cc72
-rw-r--r--Plugins/jingle/libjingle/talk/base/nattypes.h62
-rw-r--r--Plugins/jingle/libjingle/talk/base/netfw.h3767
-rw-r--r--Plugins/jingle/libjingle/talk/base/network.cc381
-rw-r--r--Plugins/jingle/libjingle/talk/base/network.h139
-rw-r--r--Plugins/jingle/libjingle/talk/base/openssladapter.cc809
-rw-r--r--Plugins/jingle/libjingle/talk/base/openssladapter.h94
-rw-r--r--Plugins/jingle/libjingle/talk/base/pathutils.cc426
-rw-r--r--Plugins/jingle/libjingle/talk/base/pathutils.h110
-rw-r--r--Plugins/jingle/libjingle/talk/base/physicalsocketserver.cc1132
-rw-r--r--Plugins/jingle/libjingle/talk/base/physicalsocketserver.h81
-rw-r--r--Plugins/jingle/libjingle/talk/base/proxydetect.cc827
-rw-r--r--Plugins/jingle/libjingle/talk/base/proxydetect.h13
-rw-r--r--Plugins/jingle/libjingle/talk/base/proxyinfo.cc37
-rw-r--r--Plugins/jingle/libjingle/talk/base/proxyinfo.h51
-rw-r--r--Plugins/jingle/libjingle/talk/base/schanneladapter.cc749
-rw-r--r--Plugins/jingle/libjingle/talk/base/schanneladapter.h94
-rw-r--r--Plugins/jingle/libjingle/talk/base/scoped_ptr.h259
-rw-r--r--Plugins/jingle/libjingle/talk/base/sec_buffer.h173
-rw-r--r--Plugins/jingle/libjingle/talk/base/signalthread.cc93
-rw-r--r--Plugins/jingle/libjingle/talk/base/signalthread.h91
-rw-r--r--Plugins/jingle/libjingle/talk/base/sigslot.h2699
-rw-r--r--Plugins/jingle/libjingle/talk/base/socket.h160
-rw-r--r--Plugins/jingle/libjingle/talk/base/socketadapters.cc677
-rw-r--r--Plugins/jingle/libjingle/talk/base/socketadapters.h170
-rw-r--r--Plugins/jingle/libjingle/talk/base/socketaddress.cc312
-rw-r--r--Plugins/jingle/libjingle/talk/base/socketaddress.h174
-rw-r--r--Plugins/jingle/libjingle/talk/base/socketaddresspair.cc58
-rw-r--r--Plugins/jingle/libjingle/talk/base/socketaddresspair.h58
-rw-r--r--Plugins/jingle/libjingle/talk/base/socketfactory.h51
-rw-r--r--Plugins/jingle/libjingle/talk/base/socketpool.cc263
-rw-r--r--Plugins/jingle/libjingle/talk/base/socketpool.h161
-rw-r--r--Plugins/jingle/libjingle/talk/base/socketserver.h57
-rw-r--r--Plugins/jingle/libjingle/talk/base/socketstream.h153
-rw-r--r--Plugins/jingle/libjingle/talk/base/ssladapter.cc191
-rw-r--r--Plugins/jingle/libjingle/talk/base/ssladapter.h74
-rw-r--r--Plugins/jingle/libjingle/talk/base/stl_decl.h84
-rw-r--r--Plugins/jingle/libjingle/talk/base/stream.cc664
-rw-r--r--Plugins/jingle/libjingle/talk/base/stream.h396
-rw-r--r--Plugins/jingle/libjingle/talk/base/streamutils.cc194
-rw-r--r--Plugins/jingle/libjingle/talk/base/streamutils.h94
-rw-r--r--Plugins/jingle/libjingle/talk/base/stringdigest.cc49
-rw-r--r--Plugins/jingle/libjingle/talk/base/stringdigest.h47
-rw-r--r--Plugins/jingle/libjingle/talk/base/stringencode.cc579
-rw-r--r--Plugins/jingle/libjingle/talk/base/stringencode.h166
-rw-r--r--Plugins/jingle/libjingle/talk/base/stringutils.cc84
-rw-r--r--Plugins/jingle/libjingle/talk/base/stringutils.h291
-rw-r--r--Plugins/jingle/libjingle/talk/base/tarstream.cc601
-rw-r--r--Plugins/jingle/libjingle/talk/base/tarstream.h104
-rw-r--r--Plugins/jingle/libjingle/talk/base/task.cc299
-rw-r--r--Plugins/jingle/libjingle/talk/base/task.h218
-rw-r--r--Plugins/jingle/libjingle/talk/base/taskrunner.cc176
-rw-r--r--Plugins/jingle/libjingle/talk/base/taskrunner.h74
-rw-r--r--Plugins/jingle/libjingle/talk/base/testclient.cc161
-rw-r--r--Plugins/jingle/libjingle/talk/base/testclient.h89
-rw-r--r--Plugins/jingle/libjingle/talk/base/thread.cc353
-rw-r--r--Plugins/jingle/libjingle/talk/base/thread.h161
-rw-r--r--Plugins/jingle/libjingle/talk/base/time.cc91
-rw-r--r--Plugins/jingle/libjingle/talk/base/time.h53
-rw-r--r--Plugins/jingle/libjingle/talk/base/unixfilesystem.cc223
-rw-r--r--Plugins/jingle/libjingle/talk/base/unixfilesystem.h86
-rw-r--r--Plugins/jingle/libjingle/talk/base/urlencode.cc120
-rw-r--r--Plugins/jingle/libjingle/talk/base/urlencode.h12
-rw-r--r--Plugins/jingle/libjingle/talk/base/virtualsocket_unittest.cc239
-rw-r--r--Plugins/jingle/libjingle/talk/base/virtualsocketserver.cc616
-rw-r--r--Plugins/jingle/libjingle/talk/base/virtualsocketserver.h156
-rw-r--r--Plugins/jingle/libjingle/talk/base/win32.h70
-rw-r--r--Plugins/jingle/libjingle/talk/base/win32filesystem.cc194
-rw-r--r--Plugins/jingle/libjingle/talk/base/win32filesystem.h91
-rw-r--r--Plugins/jingle/libjingle/talk/base/win32socketserver.cc767
-rw-r--r--Plugins/jingle/libjingle/talk/base/win32socketserver.h124
-rw-r--r--Plugins/jingle/libjingle/talk/base/win32window.h72
-rw-r--r--Plugins/jingle/libjingle/talk/base/winfirewall.cc141
-rw-r--r--Plugins/jingle/libjingle/talk/base/winfirewall.h64
-rw-r--r--Plugins/jingle/libjingle/talk/base/winping.cc317
-rw-r--r--Plugins/jingle/libjingle/talk/base/winping.h105
-rw-r--r--Plugins/jingle/libjingle/talk/examples/call/Makefile.am15
-rw-r--r--Plugins/jingle/libjingle/talk/examples/call/Makefile.in537
-rw-r--r--Plugins/jingle/libjingle/talk/examples/call/call.vcproj262
-rw-r--r--Plugins/jingle/libjingle/talk/examples/call/call_main.cc256
-rw-r--r--Plugins/jingle/libjingle/talk/examples/call/callclient.cc364
-rw-r--r--Plugins/jingle/libjingle/talk/examples/call/callclient.h105
-rw-r--r--Plugins/jingle/libjingle/talk/examples/call/console.cc77
-rw-r--r--Plugins/jingle/libjingle/talk/examples/call/console.h60
-rw-r--r--Plugins/jingle/libjingle/talk/examples/login/Makefile.am26
-rw-r--r--Plugins/jingle/libjingle/talk/examples/login/Makefile.in600
-rw-r--r--Plugins/jingle/libjingle/talk/examples/login/jingleinfotask.cc112
-rw-r--r--Plugins/jingle/libjingle/talk/examples/login/jingleinfotask.h36
-rw-r--r--Plugins/jingle/libjingle/talk/examples/login/login_main.cc63
-rw-r--r--Plugins/jingle/libjingle/talk/examples/login/presenceouttask.cc133
-rw-r--r--Plugins/jingle/libjingle/talk/examples/login/presenceouttask.h46
-rw-r--r--Plugins/jingle/libjingle/talk/examples/login/presencepushtask.cc161
-rw-r--r--Plugins/jingle/libjingle/talk/examples/login/presencepushtask.h44
-rw-r--r--Plugins/jingle/libjingle/talk/examples/login/status.h212
-rw-r--r--Plugins/jingle/libjingle/talk/examples/pcp/Makefile.am15
-rw-r--r--Plugins/jingle/libjingle/talk/examples/pcp/Makefile.in501
-rw-r--r--Plugins/jingle/libjingle/talk/examples/pcp/pcp.vcproj234
-rw-r--r--Plugins/jingle/libjingle/talk/examples/pcp/pcp_main.cc615
-rw-r--r--Plugins/jingle/libjingle/talk/libjingle.sln37
-rw-r--r--Plugins/jingle/libjingle/talk/libjingle.vcproj1014
-rw-r--r--Plugins/jingle/libjingle/talk/p2p/base/candidate.h119
-rw-r--r--Plugins/jingle/libjingle/talk/p2p/base/common.h36
-rw-r--r--Plugins/jingle/libjingle/talk/p2p/base/constants.cc62
-rw-r--r--Plugins/jingle/libjingle/talk/p2p/base/constants.h70
-rw-r--r--Plugins/jingle/libjingle/talk/p2p/base/p2ptransport.cc209
-rw-r--r--Plugins/jingle/libjingle/talk/p2p/base/p2ptransport.h86
-rw-r--r--Plugins/jingle/libjingle/talk/p2p/base/p2ptransportchannel.cc911
-rw-r--r--Plugins/jingle/libjingle/talk/p2p/base/p2ptransportchannel.h156
-rw-r--r--Plugins/jingle/libjingle/talk/p2p/base/port.cc926
-rw-r--r--Plugins/jingle/libjingle/talk/p2p/base/port.h421
-rw-r--r--Plugins/jingle/libjingle/talk/p2p/base/port_unittest.cc363
-rw-r--r--Plugins/jingle/libjingle/talk/p2p/base/portallocator.h104
-rw-r--r--Plugins/jingle/libjingle/talk/p2p/base/pseudotcp.cc1071
-rw-r--r--Plugins/jingle/libjingle/talk/p2p/base/pseudotcp.h182
-rw-r--r--Plugins/jingle/libjingle/talk/p2p/base/rawtransport.cc150
-rw-r--r--Plugins/jingle/libjingle/talk/p2p/base/rawtransport.h84
-rw-r--r--Plugins/jingle/libjingle/talk/p2p/base/rawtransportchannel.cc259
-rw-r--r--Plugins/jingle/libjingle/talk/p2p/base/rawtransportchannel.h117
-rw-r--r--Plugins/jingle/libjingle/talk/p2p/base/relayport.cc634
-rw-r--r--Plugins/jingle/libjingle/talk/p2p/base/relayport.h94
-rw-r--r--Plugins/jingle/libjingle/talk/p2p/base/relayserver.cc671
-rw-r--r--Plugins/jingle/libjingle/talk/p2p/base/relayserver.h215
-rw-r--r--Plugins/jingle/libjingle/talk/p2p/base/relayserver_main.cc75
-rw-r--r--Plugins/jingle/libjingle/talk/p2p/base/session.cc1029
-rw-r--r--Plugins/jingle/libjingle/talk/p2p/base/session.h357
-rw-r--r--Plugins/jingle/libjingle/talk/p2p/base/session_unittest.cc1039
-rw-r--r--Plugins/jingle/libjingle/talk/p2p/base/sessionclient.h71
-rw-r--r--Plugins/jingle/libjingle/talk/p2p/base/sessiondescription.h42
-rw-r--r--Plugins/jingle/libjingle/talk/p2p/base/sessionid.h94
-rw-r--r--Plugins/jingle/libjingle/talk/p2p/base/sessionmanager.cc336
-rw-r--r--Plugins/jingle/libjingle/talk/p2p/base/sessionmanager.h182
-rw-r--r--Plugins/jingle/libjingle/talk/p2p/base/stun.cc578
-rw-r--r--Plugins/jingle/libjingle/talk/p2p/base/stun.h364
-rw-r--r--Plugins/jingle/libjingle/talk/p2p/base/stunport.cc204
-rw-r--r--Plugins/jingle/libjingle/talk/p2p/base/stunport.h94
-rw-r--r--Plugins/jingle/libjingle/talk/p2p/base/stunrequest.cc198
-rw-r--r--Plugins/jingle/libjingle/talk/p2p/base/stunrequest.h126
-rw-r--r--Plugins/jingle/libjingle/talk/p2p/base/stunserver.cc160
-rw-r--r--Plugins/jingle/libjingle/talk/p2p/base/stunserver.h73
-rw-r--r--Plugins/jingle/libjingle/talk/p2p/base/stunserver_main.cc66
-rw-r--r--Plugins/jingle/libjingle/talk/p2p/base/stunserver_unittest.cc107
-rw-r--r--Plugins/jingle/libjingle/talk/p2p/base/tcpport.cc271
-rw-r--r--Plugins/jingle/libjingle/talk/p2p/base/tcpport.h122
-rw-r--r--Plugins/jingle/libjingle/talk/p2p/base/transport.cc441
-rw-r--r--Plugins/jingle/libjingle/talk/p2p/base/transport.h277
-rw-r--r--Plugins/jingle/libjingle/talk/p2p/base/transportchannel.cc56
-rw-r--r--Plugins/jingle/libjingle/talk/p2p/base/transportchannel.h104
-rw-r--r--Plugins/jingle/libjingle/talk/p2p/base/transportchannelimpl.h81
-rw-r--r--Plugins/jingle/libjingle/talk/p2p/base/transportchannelproxy.cc99
-rw-r--r--Plugins/jingle/libjingle/talk/p2p/base/transportchannelproxy.h77
-rw-r--r--Plugins/jingle/libjingle/talk/p2p/base/udpport.cc121
-rw-r--r--Plugins/jingle/libjingle/talk/p2p/base/udpport.h90
-rw-r--r--Plugins/jingle/libjingle/talk/p2p/client/basicportallocator.cc690
-rw-r--r--Plugins/jingle/libjingle/talk/p2p/client/basicportallocator.h175
-rw-r--r--Plugins/jingle/libjingle/talk/p2p/client/httpportallocator.cc190
-rw-r--r--Plugins/jingle/libjingle/talk/p2p/client/httpportallocator.h61
-rw-r--r--Plugins/jingle/libjingle/talk/p2p/client/socketmonitor.cc164
-rw-r--r--Plugins/jingle/libjingle/talk/p2p/client/socketmonitor.h91
-rw-r--r--Plugins/jingle/libjingle/talk/session/fileshare/Makefile.am7
-rw-r--r--Plugins/jingle/libjingle/talk/session/fileshare/Makefile.in446
-rw-r--r--Plugins/jingle/libjingle/talk/session/fileshare/fileshare.cc1313
-rw-r--r--Plugins/jingle/libjingle/talk/session/fileshare/fileshare.h250
-rw-r--r--Plugins/jingle/libjingle/talk/session/phone/Makefile.am27
-rw-r--r--Plugins/jingle/libjingle/talk/session/phone/Makefile.in485
-rw-r--r--Plugins/jingle/libjingle/talk/session/phone/audiomonitor.cc120
-rw-r--r--Plugins/jingle/libjingle/talk/session/phone/audiomonitor.h74
-rw-r--r--Plugins/jingle/libjingle/talk/session/phone/call.cc336
-rw-r--r--Plugins/jingle/libjingle/talk/session/phone/call.h116
-rw-r--r--Plugins/jingle/libjingle/talk/session/phone/channelmanager.cc219
-rw-r--r--Plugins/jingle/libjingle/talk/session/phone/channelmanager.h80
-rw-r--r--Plugins/jingle/libjingle/talk/session/phone/codec.h47
-rw-r--r--Plugins/jingle/libjingle/talk/session/phone/gipslitemediaengine.cc239
-rw-r--r--Plugins/jingle/libjingle/talk/session/phone/gipslitemediaengine.h93
-rw-r--r--Plugins/jingle/libjingle/talk/session/phone/linphonemediaengine.cc207
-rw-r--r--Plugins/jingle/libjingle/talk/session/phone/linphonemediaengine.h88
-rw-r--r--Plugins/jingle/libjingle/talk/session/phone/mediachannel.h78
-rw-r--r--Plugins/jingle/libjingle/talk/session/phone/mediaengine.h70
-rw-r--r--Plugins/jingle/libjingle/talk/session/phone/phonesessionclient.cc278
-rw-r--r--Plugins/jingle/libjingle/talk/session/phone/phonesessionclient.h132
-rw-r--r--Plugins/jingle/libjingle/talk/session/phone/voicechannel.cc330
-rw-r--r--Plugins/jingle/libjingle/talk/session/phone/voicechannel.h137
-rw-r--r--Plugins/jingle/libjingle/talk/session/tunnel/Makefile.am7
-rw-r--r--Plugins/jingle/libjingle/talk/session/tunnel/Makefile.in452
-rw-r--r--Plugins/jingle/libjingle/talk/session/tunnel/pseudotcpchannel.cc554
-rw-r--r--Plugins/jingle/libjingle/talk/session/tunnel/pseudotcpchannel.h125
-rw-r--r--Plugins/jingle/libjingle/talk/session/tunnel/tunnelsessionclient.cc316
-rw-r--r--Plugins/jingle/libjingle/talk/session/tunnel/tunnelsessionclient.h135
-rw-r--r--Plugins/jingle/libjingle/talk/third_party/dll/mediastreamer2.dllbin0 -> 970752 bytes
-rw-r--r--Plugins/jingle/libjingle/talk/third_party/dll/mediastreamer2.libbin0 -> 42786 bytes
-rw-r--r--Plugins/jingle/libjingle/talk/xmllite/qname.cc167
-rw-r--r--Plugins/jingle/libjingle/talk/xmllite/qname.h87
-rw-r--r--Plugins/jingle/libjingle/talk/xmllite/xmlconstants.cc65
-rw-r--r--Plugins/jingle/libjingle/talk/xmllite/xmlconstants.h61
-rw-r--r--Plugins/jingle/libjingle/talk/xmllite/xmlelement.cc501
-rw-r--r--Plugins/jingle/libjingle/talk/xmllite/xmlelement.h232
-rw-r--r--Plugins/jingle/libjingle/talk/xmllite/xmlnsstack.cc205
-rw-r--r--Plugins/jingle/libjingle/talk/xmllite/xmlnsstack.h62
-rw-r--r--Plugins/jingle/libjingle/talk/xmllite/xmlprinter.cc190
-rw-r--r--Plugins/jingle/libjingle/talk/xmllite/xmlprinter.h49
-rw-r--r--Plugins/jingle/libjingle/talk/xmpp/asyncsocket.h86
-rw-r--r--Plugins/jingle/libjingle/talk/xmpp/constants.cc398
-rw-r--r--Plugins/jingle/libjingle/talk/xmpp/constants.h358
-rw-r--r--Plugins/jingle/libjingle/talk/xmpp/jid.cc506
-rw-r--r--Plugins/jingle/libjingle/talk/xmpp/jid.h148
-rw-r--r--Plugins/jingle/libjingle/talk/xmpp/plainsaslhandler.h80
-rw-r--r--Plugins/jingle/libjingle/talk/xmpp/prexmppauth.h86
-rw-r--r--Plugins/jingle/libjingle/talk/xmpp/ratelimitmanager.cc77
-rw-r--r--Plugins/jingle/libjingle/talk/xmpp/ratelimitmanager.h146
-rw-r--r--Plugins/jingle/libjingle/talk/xmpp/saslcookiemechanism.h67
-rw-r--r--Plugins/jingle/libjingle/talk/xmpp/saslhandler.h59
-rw-r--r--Plugins/jingle/libjingle/talk/xmpp/saslmechanism.cc70
-rw-r--r--Plugins/jingle/libjingle/talk/xmpp/saslmechanism.h74
-rw-r--r--Plugins/jingle/libjingle/talk/xmpp/saslplainmechanism.h65
-rw-r--r--Plugins/jingle/libjingle_callclient.cpp281
-rw-r--r--Plugins/jingle/libjingle_callclient.h88
-rw-r--r--Plugins/jingle/sdk/m_updater.h146
-rw-r--r--Plugins/jingle/sdk/m_voice.h158
-rw-r--r--Plugins/jingle/sdk/m_voiceservice.h65
-rw-r--r--Plugins/mydetails/Docs/langpack_MyDetails.txt85
-rw-r--r--Plugins/mydetails/Docs/mydetails.gifbin0 -> 80764 bytes
-rw-r--r--Plugins/mydetails/Docs/mydetails_changelog.txt228
-rw-r--r--Plugins/mydetails/Docs/mydetails_readme.txt41
-rw-r--r--Plugins/mydetails/Docs/mydetails_version.txt1
-rw-r--r--Plugins/mydetails/ZIP/doit.bat104
-rw-r--r--Plugins/mydetails/commons.h148
-rw-r--r--Plugins/mydetails/data.cpp1063
-rw-r--r--Plugins/mydetails/data.h183
-rw-r--r--Plugins/mydetails/data/Skins/Default/MyDetails.msk399
-rw-r--r--Plugins/mydetails/data/Skins/Pidgin/MyDetails.msk59
-rw-r--r--Plugins/mydetails/frame.cpp2428
-rw-r--r--Plugins/mydetails/frame.h38
-rw-r--r--Plugins/mydetails/m_mydetails.h187
-rw-r--r--Plugins/mydetails/m_simpleaway.h84
-rw-r--r--Plugins/mydetails/mydetails.cpp992
-rw-r--r--Plugins/mydetails/mydetails.dsp223
-rw-r--r--Plugins/mydetails/mydetails.dsw29
-rw-r--r--Plugins/mydetails/mydetails.h42
-rw-r--r--Plugins/mydetails/mydetails.vcproj642
-rw-r--r--Plugins/mydetails/options.cpp189
-rw-r--r--Plugins/mydetails/options.h83
-rw-r--r--Plugins/mydetails/res/leftarrow.icobin0 -> 2550 bytes
-rw-r--r--Plugins/mydetails/res/listening_to.icobin0 -> 2038 bytes
-rw-r--r--Plugins/mydetails/res/mail.icobin0 -> 2550 bytes
-rw-r--r--Plugins/mydetails/res/rightarrow.icobin0 -> 2550 bytes
-rw-r--r--Plugins/mydetails/resource.h72
-rw-r--r--Plugins/mydetails/resource.rc326
-rw-r--r--Plugins/mydetails/sdk/m_NewAwaySys.h119
-rw-r--r--Plugins/mydetails/sdk/m_avatars.h297
-rw-r--r--Plugins/mydetails/sdk/m_cluiframes.h338
-rw-r--r--Plugins/mydetails/sdk/m_ersatz.h40
-rw-r--r--Plugins/mydetails/sdk/m_listeningto.h56
-rw-r--r--Plugins/mydetails/sdk/m_metacontacts.h162
-rw-r--r--Plugins/mydetails/sdk/m_proto_listeningto.h137
-rw-r--r--Plugins/mydetails/sdk/m_skin_eng.h435
-rw-r--r--Plugins/mydetails/sdk/m_smileyadd.h172
-rw-r--r--Plugins/mydetails/sdk/m_statusplugins.h156
-rw-r--r--Plugins/mydetails/sdk/m_updater.h146
-rw-r--r--Plugins/mydetails/sdk/m_variables.h668
-rw-r--r--Plugins/nickhistory/Docs/helppack_nickhistory.txt33
-rw-r--r--Plugins/nickhistory/Docs/langpack_nickhistory.txt64
-rw-r--r--Plugins/nickhistory/Docs/nickhistory_changelog.txt46
-rw-r--r--Plugins/nickhistory/Docs/nickhistory_readme.txt11
-rw-r--r--Plugins/nickhistory/Docs/nickhistory_version.txt1
-rw-r--r--Plugins/nickhistory/ZIP/doit.bat80
-rw-r--r--Plugins/nickhistory/commons.h94
-rw-r--r--Plugins/nickhistory/m_nickhistory.h61
-rw-r--r--Plugins/nickhistory/nickhistory.cpp668
-rw-r--r--Plugins/nickhistory/nickhistory.dsp247
-rw-r--r--Plugins/nickhistory/nickhistory.dsw29
-rw-r--r--Plugins/nickhistory/options.cpp292
-rw-r--r--Plugins/nickhistory/options.h89
-rw-r--r--Plugins/nickhistory/popup.cpp319
-rw-r--r--Plugins/nickhistory/popup.h60
-rw-r--r--Plugins/nickhistory/resource.h55
-rw-r--r--Plugins/nickhistory/resource.rc180
-rw-r--r--Plugins/nickhistory/sdk/m_metacontacts.h162
-rw-r--r--Plugins/nickhistory/sdk/m_updater.h146
-rw-r--r--Plugins/notification_history/commons.h76
-rw-r--r--Plugins/notification_history/m_notification_history.h70
-rw-r--r--Plugins/notification_history/notification_history.cpp219
-rw-r--r--Plugins/notification_history/notification_history.dsp213
-rw-r--r--Plugins/notification_history/notification_history.dsw29
-rw-r--r--Plugins/notification_history/notification_history.h50
-rw-r--r--Plugins/notification_history/options.cpp116
-rw-r--r--Plugins/notification_history/options.h36
-rw-r--r--Plugins/notification_history/resource.h26
-rw-r--r--Plugins/notification_history/resource.rc114
-rw-r--r--Plugins/notification_log/commons.h76
-rw-r--r--Plugins/notification_log/m_notification_log.h63
-rw-r--r--Plugins/notification_log/notification_log.cpp203
-rw-r--r--Plugins/notification_log/notification_log.dsp213
-rw-r--r--Plugins/notification_log/notification_log.dsw29
-rw-r--r--Plugins/notification_log/notification_log.h50
-rw-r--r--Plugins/notification_log/options.cpp117
-rw-r--r--Plugins/notification_log/options.h36
-rw-r--r--Plugins/notification_log/resource.h30
-rw-r--r--Plugins/notification_log/resource.rc105
-rw-r--r--Plugins/notification_speak/commons.h77
-rw-r--r--Plugins/notification_speak/m_notification_speak.h57
-rw-r--r--Plugins/notification_speak/notification_speak.cpp164
-rw-r--r--Plugins/notification_speak/notification_speak.dsp213
-rw-r--r--Plugins/notification_speak/notification_speak.dsw29
-rw-r--r--Plugins/notification_speak/notification_speak.h50
-rw-r--r--Plugins/notification_speak/options.cpp110
-rw-r--r--Plugins/notification_speak/options.h36
-rw-r--r--Plugins/notification_speak/resource.h21
-rw-r--r--Plugins/notification_speak/resource.rc101
-rw-r--r--Plugins/quickcontacts/Docs/langpack_quickcontacts.txt40
-rw-r--r--Plugins/quickcontacts/Docs/quickcontacts.pngbin0 -> 8766 bytes
-rw-r--r--Plugins/quickcontacts/Docs/quickcontacts_changelog.txt75
-rw-r--r--Plugins/quickcontacts/Docs/quickcontacts_readme.txt22
-rw-r--r--Plugins/quickcontacts/Docs/quickcontacts_version.txt1
-rw-r--r--Plugins/quickcontacts/ZIP/doit.bat102
-rw-r--r--Plugins/quickcontacts/commons.h111
-rw-r--r--Plugins/quickcontacts/m_quickcontacts.h39
-rw-r--r--Plugins/quickcontacts/options.cpp164
-rw-r--r--Plugins/quickcontacts/options.h65
-rw-r--r--Plugins/quickcontacts/quickcontacts.cpp1379
-rw-r--r--Plugins/quickcontacts/quickcontacts.dsp234
-rw-r--r--Plugins/quickcontacts/quickcontacts.dsw29
-rw-r--r--Plugins/quickcontacts/quickcontacts.sln26
-rw-r--r--Plugins/quickcontacts/quickcontacts.vcproj671
-rw-r--r--Plugins/quickcontacts/resource.h47
-rw-r--r--Plugins/quickcontacts/resource.rc179
-rw-r--r--Plugins/quickcontacts/sdk/m_MagneticWindows.h86
-rw-r--r--Plugins/quickcontacts/sdk/m_hotkeysplus.h22
-rw-r--r--Plugins/quickcontacts/sdk/m_hotkeysservice.h207
-rw-r--r--Plugins/quickcontacts/sdk/m_metacontacts.h166
-rw-r--r--Plugins/quickcontacts/sdk/m_updater.h146
-rw-r--r--Plugins/quickcontacts/sdk/m_voice.h158
-rw-r--r--Plugins/quickcontacts/sdk/m_voiceservice.h86
-rw-r--r--Plugins/qupdater/commons.h84
-rw-r--r--Plugins/qupdater/options.cpp195
-rw-r--r--Plugins/qupdater/options.h58
-rw-r--r--Plugins/qupdater/qupdater.cpp177
-rw-r--r--Plugins/qupdater/qupdater.dsp145
-rw-r--r--Plugins/qupdater/qupdater.dsw29
-rw-r--r--Plugins/qupdater/resource.h21
-rw-r--r--Plugins/qupdater/resource.rc113
-rw-r--r--Plugins/qupdater/sdk/m_updater.h146
-rw-r--r--Plugins/rps/Docs/rps_changelog.txt22
-rw-r--r--Plugins/rps/Docs/rps_readme.txt22
-rw-r--r--Plugins/rps/Docs/rps_version.txt1
-rw-r--r--Plugins/rps/RemovePersonalSettings.ini247
-rw-r--r--Plugins/rps/ZIP/doit.bat58
-rw-r--r--Plugins/rps/rps.c833
-rw-r--r--Plugins/rps/rps.dsp127
-rw-r--r--Plugins/rps/rps.dsw33
-rw-r--r--Plugins/smcnotify/commonheaders.h90
-rw-r--r--Plugins/smcnotify/docs/smcnotify_license.txt340
-rw-r--r--Plugins/smcnotify/docs/smcnotify_readme.txt177
-rw-r--r--Plugins/smcnotify/docs/smcnotify_translation.txt113
-rw-r--r--Plugins/smcnotify/docs/version.txt1
-rw-r--r--Plugins/smcnotify/history.c233
-rw-r--r--Plugins/smcnotify/ignore.c265
-rw-r--r--Plugins/smcnotify/list.c445
-rw-r--r--Plugins/smcnotify/m_smcnotify.h59
-rw-r--r--Plugins/smcnotify/menu.c192
-rw-r--r--Plugins/smcnotify/menu.h42
-rw-r--r--Plugins/smcnotify/options.c496
-rw-r--r--Plugins/smcnotify/options.h104
-rw-r--r--Plugins/smcnotify/popup.c203
-rw-r--r--Plugins/smcnotify/popup.h37
-rw-r--r--Plugins/smcnotify/res/history.icobin0 -> 2550 bytes
-rw-r--r--Plugins/smcnotify/res/list.icobin0 -> 2550 bytes
-rw-r--r--Plugins/smcnotify/res/log.icobin0 -> 2550 bytes
-rw-r--r--Plugins/smcnotify/res/popup.icobin0 -> 2550 bytes
-rw-r--r--Plugins/smcnotify/res/popup_no.icobin0 -> 2550 bytes
-rw-r--r--Plugins/smcnotify/res/url.icobin0 -> 2550 bytes
-rw-r--r--Plugins/smcnotify/resource.h91
-rw-r--r--Plugins/smcnotify/resource.rc303
-rw-r--r--Plugins/smcnotify/smc.c401
-rw-r--r--Plugins/smcnotify/smc.h46
-rw-r--r--Plugins/smcnotify/smcn.dsp194
-rw-r--r--Plugins/smcnotify/smcn.dsw29
-rw-r--r--Plugins/smcnotify/smcn.sln23
-rw-r--r--Plugins/smcnotify/smcn.vcproj569
-rw-r--r--Plugins/smcnotify/smcnotify.c165
-rw-r--r--Plugins/smcnotify/smcnotify.vcproj582
-rw-r--r--Plugins/smcnotify/utils.c238
-rw-r--r--Plugins/smh/Docs/helppack_smh.txt33
-rw-r--r--Plugins/smh/Docs/langpack_smh.txt64
-rw-r--r--Plugins/smh/Docs/smh_changelog.txt38
-rw-r--r--Plugins/smh/Docs/smh_readme.txt11
-rw-r--r--Plugins/smh/Docs/smh_version.txt1
-rw-r--r--Plugins/smh/ZIP/doit.bat80
-rw-r--r--Plugins/smh/commons.h95
-rw-r--r--Plugins/smh/m_smh.h60
-rw-r--r--Plugins/smh/options.cpp292
-rw-r--r--Plugins/smh/options.h89
-rw-r--r--Plugins/smh/popup.cpp319
-rw-r--r--Plugins/smh/popup.h60
-rw-r--r--Plugins/smh/resource.h55
-rw-r--r--Plugins/smh/resource.rc180
-rw-r--r--Plugins/smh/sdk/m_metacontacts.h162
-rw-r--r--Plugins/smh/sdk/m_updater.h146
-rw-r--r--Plugins/smh/smh.cpp671
-rw-r--r--Plugins/smh/smh.dsp247
-rw-r--r--Plugins/smh/smh.dsw29
-rw-r--r--Plugins/smr/Docs/langpack_smr.txt45
-rw-r--r--Plugins/smr/Docs/smr_changelog.txt61
-rw-r--r--Plugins/smr/Docs/smr_readme.txt8
-rw-r--r--Plugins/smr/Docs/smr_version.txt1
-rw-r--r--Plugins/smr/ZIP/doit.bat84
-rw-r--r--Plugins/smr/commons.h93
-rw-r--r--Plugins/smr/m_smr.h70
-rw-r--r--Plugins/smr/options.cpp155
-rw-r--r--Plugins/smr/options.h77
-rw-r--r--Plugins/smr/poll.cpp868
-rw-r--r--Plugins/smr/poll.h62
-rw-r--r--Plugins/smr/resource.h74
-rw-r--r--Plugins/smr/resource.rc163
-rw-r--r--Plugins/smr/sdk/m_updater.h146
-rw-r--r--Plugins/smr/smr.cpp276
-rw-r--r--Plugins/smr/smr.dsp186
-rw-r--r--Plugins/smr/smr.dsw29
-rw-r--r--Plugins/smr/status.cpp63
-rw-r--r--Plugins/smr/status.h42
-rw-r--r--Plugins/smr/status_msg.cpp89
-rw-r--r--Plugins/smr/status_msg.h46
-rw-r--r--Plugins/spellchecker/Docs/langpack_spellchecker.txt74
-rw-r--r--Plugins/spellchecker/Docs/spellchecker.pngbin0 -> 28060 bytes
-rw-r--r--Plugins/spellchecker/Docs/spellchecker_changelog.txt235
-rw-r--r--Plugins/spellchecker/Docs/spellchecker_readme.txt36
-rw-r--r--Plugins/spellchecker/Docs/spellchecker_version.txt1
-rw-r--r--Plugins/spellchecker/Flags-Angelika/Unknown.icobin0 -> 2038 bytes
-rw-r--r--Plugins/spellchecker/Flags-Angelika/af_ZA.icobin0 -> 2038 bytes
-rw-r--r--Plugins/spellchecker/Flags-Angelika/ar_AR.icobin0 -> 2038 bytes
-rw-r--r--Plugins/spellchecker/Flags-Angelika/az_AZ.icobin0 -> 2038 bytes
-rw-r--r--Plugins/spellchecker/Flags-Angelika/bg_BG.icobin0 -> 2038 bytes
-rw-r--r--Plugins/spellchecker/Flags-Angelika/bn_IN.icobin0 -> 2038 bytes
-rw-r--r--Plugins/spellchecker/Flags-Angelika/ca_ES.icobin0 -> 2038 bytes
-rw-r--r--Plugins/spellchecker/Flags-Angelika/cs_CZ.icobin0 -> 2038 bytes
-rw-r--r--Plugins/spellchecker/Flags-Angelika/csb_PO.icobin0 -> 2038 bytes
-rw-r--r--Plugins/spellchecker/Flags-Angelika/cy_GB.icobin0 -> 2038 bytes
-rw-r--r--Plugins/spellchecker/Flags-Angelika/da_DK.icobin0 -> 2038 bytes
-rw-r--r--Plugins/spellchecker/Flags-Angelika/de_AT.icobin0 -> 2038 bytes
-rw-r--r--Plugins/spellchecker/Flags-Angelika/de_CH.icobin0 -> 2038 bytes
-rw-r--r--Plugins/spellchecker/Flags-Angelika/de_DE.icobin0 -> 2038 bytes
-rw-r--r--Plugins/spellchecker/Flags-Angelika/el_GR.icobin0 -> 2038 bytes
-rw-r--r--Plugins/spellchecker/Flags-Angelika/em_ET.icobin0 -> 2038 bytes
-rw-r--r--Plugins/spellchecker/Flags-Angelika/en_AU.icobin0 -> 2038 bytes
-rw-r--r--Plugins/spellchecker/Flags-Angelika/en_CA.icobin0 -> 2038 bytes
-rw-r--r--Plugins/spellchecker/Flags-Angelika/en_GB.icobin0 -> 2038 bytes
-rw-r--r--Plugins/spellchecker/Flags-Angelika/en_NZ.icobin0 -> 2038 bytes
-rw-r--r--Plugins/spellchecker/Flags-Angelika/en_US.icobin0 -> 2038 bytes
-rw-r--r--Plugins/spellchecker/Flags-Angelika/en_ZA.icobin0 -> 2038 bytes
-rw-r--r--Plugins/spellchecker/Flags-Angelika/es_ES.icobin0 -> 2038 bytes
-rw-r--r--Plugins/spellchecker/Flags-Angelika/es_MX.icobin0 -> 2038 bytes
-rw-r--r--Plugins/spellchecker/Flags-Angelika/et_EE.icobin0 -> 2038 bytes
-rw-r--r--Plugins/spellchecker/Flags-Angelika/fa_IR.icobin0 -> 2038 bytes
-rw-r--r--Plugins/spellchecker/Flags-Angelika/fi_FI.icobin0 -> 2038 bytes
-rw-r--r--Plugins/spellchecker/Flags-Angelika/fj_FJ.icobin0 -> 2038 bytes
-rw-r--r--Plugins/spellchecker/Flags-Angelika/fo_FO.icobin0 -> 2038 bytes
-rw-r--r--Plugins/spellchecker/Flags-Angelika/fr_BE.icobin0 -> 2038 bytes
-rw-r--r--Plugins/spellchecker/Flags-Angelika/fr_FR.icobin0 -> 2038 bytes
-rw-r--r--Plugins/spellchecker/Flags-Angelika/ga_IE.icobin0 -> 2038 bytes
-rw-r--r--Plugins/spellchecker/Flags-Angelika/gd_GB.icobin0 -> 2038 bytes
-rw-r--r--Plugins/spellchecker/Flags-Angelika/gl_ES.icobin0 -> 2038 bytes
-rw-r--r--Plugins/spellchecker/Flags-Angelika/he_IL.icobin0 -> 2038 bytes
-rw-r--r--Plugins/spellchecker/Flags-Angelika/hi_IN.icobin0 -> 2038 bytes
-rw-r--r--Plugins/spellchecker/Flags-Angelika/hr_Hr.icobin0 -> 2038 bytes
-rw-r--r--Plugins/spellchecker/Flags-Angelika/hu_HU.icobin0 -> 2038 bytes
-rw-r--r--Plugins/spellchecker/Flags-Angelika/id_ID.icobin0 -> 2038 bytes
-rw-r--r--Plugins/spellchecker/Flags-Angelika/is_IS.icobin0 -> 2038 bytes
-rw-r--r--Plugins/spellchecker/Flags-Angelika/it_IT.icobin0 -> 2038 bytes
-rw-r--r--Plugins/spellchecker/Flags-Angelika/km_KH.icobin0 -> 2038 bytes
-rw-r--r--Plugins/spellchecker/Flags-Angelika/ku_TR.icobin0 -> 2038 bytes
-rw-r--r--Plugins/spellchecker/Flags-Angelika/lt_LT.icobin0 -> 2038 bytes
-rw-r--r--Plugins/spellchecker/Flags-Angelika/lu_LU.icobin0 -> 2038 bytes
-rw-r--r--Plugins/spellchecker/Flags-Angelika/lv_LV.icobin0 -> 2038 bytes
-rw-r--r--Plugins/spellchecker/Flags-Angelika/mg_MG.icobin0 -> 2038 bytes
-rw-r--r--Plugins/spellchecker/Flags-Angelika/mi_NZ.icobin0 -> 2038 bytes
-rw-r--r--Plugins/spellchecker/Flags-Angelika/mn_MN.icobin0 -> 2038 bytes
-rw-r--r--Plugins/spellchecker/Flags-Angelika/mo_BF.icobin0 -> 2038 bytes
-rw-r--r--Plugins/spellchecker/Flags-Angelika/mr_IN.icobin0 -> 2038 bytes
-rw-r--r--Plugins/spellchecker/Flags-Angelika/ms_MY.icobin0 -> 2038 bytes
-rw-r--r--Plugins/spellchecker/Flags-Angelika/nb_NO.icobin0 -> 2038 bytes
-rw-r--r--Plugins/spellchecker/Flags-Angelika/ne_NP.icobin0 -> 2038 bytes
-rw-r--r--Plugins/spellchecker/Flags-Angelika/nl_NL.icobin0 -> 2038 bytes
-rw-r--r--Plugins/spellchecker/Flags-Angelika/nn_NO.icobin0 -> 2038 bytes
-rw-r--r--Plugins/spellchecker/Flags-Angelika/nr_ZA.icobin0 -> 2038 bytes
-rw-r--r--Plugins/spellchecker/Flags-Angelika/ns_ZA.icobin0 -> 2038 bytes
-rw-r--r--Plugins/spellchecker/Flags-Angelika/or_IN.icobin0 -> 2038 bytes
-rw-r--r--Plugins/spellchecker/Flags-Angelika/pl_PL.icobin0 -> 2038 bytes
-rw-r--r--Plugins/spellchecker/Flags-Angelika/pt_BR.icobin0 -> 2038 bytes
-rw-r--r--Plugins/spellchecker/Flags-Angelika/pt_PT.icobin0 -> 2038 bytes
-rw-r--r--Plugins/spellchecker/Flags-Angelika/qu_BO.icobin0 -> 2038 bytes
-rw-r--r--Plugins/spellchecker/Flags-Angelika/ro_RO.icobin0 -> 2038 bytes
-rw-r--r--Plugins/spellchecker/Flags-Angelika/ru_RU.icobin0 -> 2038 bytes
-rw-r--r--Plugins/spellchecker/Flags-Angelika/rw_RW.icobin0 -> 2038 bytes
-rw-r--r--Plugins/spellchecker/Flags-Angelika/sk_SK.icobin0 -> 2038 bytes
-rw-r--r--Plugins/spellchecker/Flags-Angelika/sl_SI.icobin0 -> 2038 bytes
-rw-r--r--Plugins/spellchecker/Flags-Angelika/ss_ZA.icobin0 -> 2038 bytes
-rw-r--r--Plugins/spellchecker/Flags-Angelika/st_ZA.icobin0 -> 2038 bytes
-rw-r--r--Plugins/spellchecker/Flags-Angelika/sv_SE.icobin0 -> 2038 bytes
-rw-r--r--Plugins/spellchecker/Flags-Angelika/ta_IN.icobin0 -> 2038 bytes
-rw-r--r--Plugins/spellchecker/Flags-Angelika/tet_ID.icobin0 -> 2038 bytes
-rw-r--r--Plugins/spellchecker/Flags-Angelika/tl_PH.icobin0 -> 2038 bytes
-rw-r--r--Plugins/spellchecker/Flags-Angelika/tn_ZA.icobin0 -> 2038 bytes
-rw-r--r--Plugins/spellchecker/Flags-Angelika/ts_ZA.icobin0 -> 2038 bytes
-rw-r--r--Plugins/spellchecker/Flags-Angelika/uk_UA.icobin0 -> 2038 bytes
-rw-r--r--Plugins/spellchecker/Flags-Angelika/uz_UZ.icobin0 -> 2038 bytes
-rw-r--r--Plugins/spellchecker/Flags-Angelika/ve_ZA.icobin0 -> 2038 bytes
-rw-r--r--Plugins/spellchecker/Flags-Angelika/vi_VI.icobin0 -> 2038 bytes
-rw-r--r--Plugins/spellchecker/Flags-Angelika/xh_ZA.icobin0 -> 2038 bytes
-rw-r--r--Plugins/spellchecker/Flags-Angelika/zu_ZA.icobin0 -> 2038 bytes
-rw-r--r--Plugins/spellchecker/Flags/ad.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/ae.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/af_ZA.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/ag.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/ai.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/al.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/am.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/an.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/ao.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/ar.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/as.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/aw.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/ax.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/az_AZ.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/ba.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/bb.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/bg_BG.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/bh.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/bi.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/bj.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/bm.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/bn.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/bn_IN.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/bs.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/bt.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/bv.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/by.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/bz.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/cc.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/cd.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/cf.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/cg.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/ci.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/ck.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/cl.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/cm.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/cn.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/co.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/cr.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/cs_CZ.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/cu.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/cv.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/cx.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/cy.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/cy_GB.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/da_DK.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/de_AT.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/de_CH.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/de_DE.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/dj.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/dm.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/do.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/dz.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/ec.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/eg.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/eh.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/el_GR.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/em_ET.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/en_AU.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/en_CA.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/en_GB.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/en_NZ.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/en_US.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/en_ZA.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/england.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/er.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/es_ES.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/es_MX.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/et_EE.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/fa_IR.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/fi_FI.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/fj_FJ.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/fk.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/fm.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/fo_FO.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/fr_BE.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/fr_FR.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/ga.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/ga_IE.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/gd.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/gd_GB.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/ge.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/gi.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/gl.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/gm.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/gn.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/gp.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/gq.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/gs.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/gt.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/gu.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/gw.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/gy.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/he_IL.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/hi_IN.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/hk.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/hn.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/hr_HR.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/ht.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/hu_HU.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/id_ID.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/io.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/iq.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/is_IS.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/it_IT.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/jm.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/jo.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/jp.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/ke.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/kg.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/ki.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/km.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/km_KH.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/kn.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/kp.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/kr.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/ku_TR.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/kw.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/ky.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/kz.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/la.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/lb.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/lc.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/li.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/lk.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/lr.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/ls.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/lt_LT.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/lu_LU.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/lv_LV.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/ly.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/ma.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/mc.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/md.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/mg_MG.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/mh.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/mi_NZ.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/mk.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/ml.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/mm.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/mn_MN.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/mo.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/mo_BF.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/mp.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/mq.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/mr.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/mr_IN.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/ms.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/ms_MY.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/mt.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/mu.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/mv.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/mz.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/na.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/nb_NO.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/nc.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/ne.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/ne_NP.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/nf.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/ng.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/ni.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/nl_NL.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/nn_NO.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/nr.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/nr_ZA.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/ns_ZA.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/nu.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/ny_MW.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/om.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/or_IN.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/pa.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/pe.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/pf.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/pg.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/pk.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/pl_PL.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/pm.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/pn.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/pr.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/ps.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/pt_BR.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/pt_PT.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/pw.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/py.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/qa.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/qu_BO.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/readme.txt8
-rw-r--r--Plugins/spellchecker/Flags/ro_BO.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/ro_RO.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/ru_RU.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/rw_RW.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/sa.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/sb.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/sc.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/sd.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/sg.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/sh.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/sk_SK.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/sl.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/sl_SI.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/sm.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/sn.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/so.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/sr.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/sr_CS.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/ss_ZA.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/st.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/st_ZA.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/sv.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/sv_SE.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/sy.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/tc.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/td.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/tet_ID.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/tf.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/tg.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/th.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/tj.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/tk.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/tl.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/tl_PH.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/tm.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/tn.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/tn_ZA.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/to.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/ts_ZA.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/tt.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/tv.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/tw.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/tz.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/ug.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/uk_UA.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/um.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/uy.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/uz_UZ.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/va.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/vc.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/ve.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/ve_ZA.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/vg.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/vi.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/vi_VI.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/vu.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/wf.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/ws.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/xh_ZA.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/ye.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/yt.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/zm.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/zu_ZA.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/Flags/zw.icobin0 -> 1150 bytes
-rw-r--r--Plugins/spellchecker/RichEdit.cpp332
-rw-r--r--Plugins/spellchecker/RichEdit.h71
-rw-r--r--Plugins/spellchecker/ZIP/Dictionaries/en_US.aff189
-rw-r--r--Plugins/spellchecker/ZIP/Dictionaries/en_US.dic62077
-rw-r--r--Plugins/spellchecker/ZIP/Icons/flags.dllbin0 -> 159744 bytes
-rw-r--r--Plugins/spellchecker/ZIP/doit.bat141
-rw-r--r--Plugins/spellchecker/ZIP/zip.exebin0 -> 290816 bytes
-rw-r--r--Plugins/spellchecker/ardialog.cpp316
-rw-r--r--Plugins/spellchecker/ardialog.h33
-rw-r--r--Plugins/spellchecker/autoreplace.cpp229
-rw-r--r--Plugins/spellchecker/autoreplace.h70
-rw-r--r--Plugins/spellchecker/codepages.cpp307
-rw-r--r--Plugins/spellchecker/commons.h154
-rw-r--r--Plugins/spellchecker/dictionary.cpp735
-rw-r--r--Plugins/spellchecker/dictionary.h86
-rw-r--r--Plugins/spellchecker/flags-angelika.dsp74
-rw-r--r--Plugins/spellchecker/flags-angelika.rc118
-rw-r--r--Plugins/spellchecker/flags-angelika.sln40
-rw-r--r--Plugins/spellchecker/flags-angelika.vcproj508
-rw-r--r--Plugins/spellchecker/flags.dsp74
-rw-r--r--Plugins/spellchecker/flags.rc119
-rw-r--r--Plugins/spellchecker/flags.sln40
-rw-r--r--Plugins/spellchecker/flags.vcproj508
-rw-r--r--Plugins/spellchecker/hunspell/README21
-rw-r--r--Plugins/spellchecker/hunspell/affentry.cxx962
-rw-r--r--Plugins/spellchecker/hunspell/affentry.hxx136
-rw-r--r--Plugins/spellchecker/hunspell/affixmgr.cxx4521
-rw-r--r--Plugins/spellchecker/hunspell/affixmgr.hxx250
-rw-r--r--Plugins/spellchecker/hunspell/atypes.hxx107
-rw-r--r--Plugins/spellchecker/hunspell/baseaffix.hxx28
-rw-r--r--Plugins/spellchecker/hunspell/config.h215
-rw-r--r--Plugins/spellchecker/hunspell/csutil.cxx5834
-rw-r--r--Plugins/spellchecker/hunspell/csutil.hxx220
-rw-r--r--Plugins/spellchecker/hunspell/dictmgr.cxx180
-rw-r--r--Plugins/spellchecker/hunspell/dictmgr.hxx36
-rw-r--r--Plugins/spellchecker/hunspell/filemgr.cxx49
-rw-r--r--Plugins/spellchecker/hunspell/filemgr.hxx25
-rw-r--r--Plugins/spellchecker/hunspell/hashmgr.cxx928
-rw-r--r--Plugins/spellchecker/hunspell/hashmgr.hxx69
-rw-r--r--Plugins/spellchecker/hunspell/htypes.hxx32
-rw-r--r--Plugins/spellchecker/hunspell/hunspell.cxx2011
-rw-r--r--Plugins/spellchecker/hunspell/hunspell.dsp164
-rw-r--r--Plugins/spellchecker/hunspell/hunspell.h95
-rw-r--r--Plugins/spellchecker/hunspell/hunspell.hxx174
-rw-r--r--Plugins/spellchecker/hunspell/hunvisapi.h18
-rw-r--r--Plugins/spellchecker/hunspell/hunzip.cxx193
-rw-r--r--Plugins/spellchecker/hunspell/hunzip.hxx45
-rw-r--r--Plugins/spellchecker/hunspell/langnum.hxx38
-rw-r--r--Plugins/spellchecker/hunspell/license.hunspell59
-rw-r--r--Plugins/spellchecker/hunspell/license.myspell61
-rw-r--r--Plugins/spellchecker/hunspell/phonet.cxx292
-rw-r--r--Plugins/spellchecker/hunspell/phonet.hxx52
-rw-r--r--Plugins/spellchecker/hunspell/replist.cxx87
-rw-r--r--Plugins/spellchecker/hunspell/replist.hxx27
-rw-r--r--Plugins/spellchecker/hunspell/suggestmgr.cxx2001
-rw-r--r--Plugins/spellchecker/hunspell/suggestmgr.hxx111
-rw-r--r--Plugins/spellchecker/hunspell/utf_info.cxx19676
-rw-r--r--Plugins/spellchecker/hunspell/w_char.hxx21
-rw-r--r--Plugins/spellchecker/m_spellchecker.h77
-rw-r--r--Plugins/spellchecker/options.cpp610
-rw-r--r--Plugins/spellchecker/options.h68
-rw-r--r--Plugins/spellchecker/res/no_spellcheck.icobin0 -> 2038 bytes
-rw-r--r--Plugins/spellchecker/res/spellcheck.icobin0 -> 2038 bytes
-rw-r--r--Plugins/spellchecker/res/unknown.icobin0 -> 894 bytes
-rw-r--r--Plugins/spellchecker/resource.h93
-rw-r--r--Plugins/spellchecker/resource.rc201
-rw-r--r--Plugins/spellchecker/sdk/m_folders.h284
-rw-r--r--Plugins/spellchecker/sdk/m_metacontacts.h166
-rw-r--r--Plugins/spellchecker/sdk/m_updater.h150
-rw-r--r--Plugins/spellchecker/sdk/m_userinfoex.h764
-rw-r--r--Plugins/spellchecker/sdk/m_variables.h719
-rw-r--r--Plugins/spellchecker/spellchecker.cpp2322
-rw-r--r--Plugins/spellchecker/spellchecker.dsp407
-rw-r--r--Plugins/spellchecker/spellchecker.dsw29
-rw-r--r--Plugins/spellchecker/spellchecker.sln37
-rw-r--r--Plugins/spellchecker/spellchecker.vcproj2570
-rw-r--r--Plugins/spellchecker/spellchecker.vcxproj883
-rw-r--r--Plugins/spellchecker/spellchecker.vcxproj.filters199
-rw-r--r--Plugins/spellchecker/srmm.spellchecker.patch266
-rw-r--r--Plugins/utils/mir_options_notify.cpp199
-rw-r--r--Plugins/utils/mir_options_notify.h69
-rw-r--r--Plugins/voiceservice/Docs/langpack_voiceservice.txt74
-rw-r--r--Plugins/voiceservice/Docs/voiceservice_changelog.txt35
-rw-r--r--Plugins/voiceservice/Docs/voiceservice_readme.txt18
-rw-r--r--Plugins/voiceservice/Docs/voiceservice_version.txt1
-rw-r--r--Plugins/voiceservice/ZIP/doit.bat82
-rw-r--r--Plugins/voiceservice/codepages.cpp307
-rw-r--r--Plugins/voiceservice/commons.h337
-rw-r--r--Plugins/voiceservice/concepts/concepts.txt42
-rw-r--r--Plugins/voiceservice/concepts/frame.bmml212
-rw-r--r--Plugins/voiceservice/concepts/frame.pngbin0 -> 58447 bytes
-rw-r--r--Plugins/voiceservice/concepts/options.bmml42
-rw-r--r--Plugins/voiceservice/concepts/options.pngbin0 -> 24394 bytes
-rw-r--r--Plugins/voiceservice/concepts/popup.bmml63
-rw-r--r--Plugins/voiceservice/concepts/popup.pngbin0 -> 36650 bytes
-rw-r--r--Plugins/voiceservice/frame.cpp829
-rw-r--r--Plugins/voiceservice/frame.h36
-rw-r--r--Plugins/voiceservice/lib/portaudio/Debug/libportaudio.libbin0 -> 396496 bytes
-rw-r--r--Plugins/voiceservice/lib/portaudio/LICENSE.txt81
-rw-r--r--Plugins/voiceservice/lib/portaudio/Release/libportaudio.libbin0 -> 141138 bytes
-rw-r--r--Plugins/voiceservice/lib/portaudio/portaudio.h1134
-rw-r--r--Plugins/voiceservice/m_voice.h180
-rw-r--r--Plugins/voiceservice/m_voiceservice.h90
-rw-r--r--Plugins/voiceservice/options.cpp616
-rw-r--r--Plugins/voiceservice/options.h67
-rw-r--r--Plugins/voiceservice/popup.cpp340
-rw-r--r--Plugins/voiceservice/popup.h49
-rw-r--r--Plugins/voiceservice/res/Answer.icobin0 -> 2038 bytes
-rw-r--r--Plugins/voiceservice/res/Busy.icobin0 -> 2038 bytes
-rw-r--r--Plugins/voiceservice/res/Call.icobin0 -> 2038 bytes
-rw-r--r--Plugins/voiceservice/res/Calling.icobin0 -> 2038 bytes
-rw-r--r--Plugins/voiceservice/res/Drop.icobin0 -> 2038 bytes
-rw-r--r--Plugins/voiceservice/res/Hold.icobin0 -> 2038 bytes
-rw-r--r--Plugins/voiceservice/res/Main.icobin0 -> 2038 bytes
-rw-r--r--Plugins/voiceservice/res/On hold.icobin0 -> 2038 bytes
-rw-r--r--Plugins/voiceservice/res/Rinning.icobin0 -> 2038 bytes
-rw-r--r--Plugins/voiceservice/res/Talking.icobin0 -> 2038 bytes
-rw-r--r--Plugins/voiceservice/res/dialpad.icobin0 -> 198 bytes
-rw-r--r--Plugins/voiceservice/res/ended.icobin0 -> 2038 bytes
-rw-r--r--Plugins/voiceservice/res/secure.icobin0 -> 2806 bytes
-rw-r--r--Plugins/voiceservice/res/smalldot.icobin0 -> 318 bytes
-rw-r--r--Plugins/voiceservice/resource.h87
-rw-r--r--Plugins/voiceservice/resource.rc290
-rw-r--r--Plugins/voiceservice/sdk/m_cluiframes.h254
-rw-r--r--Plugins/voiceservice/sdk/m_folders.h205
-rw-r--r--Plugins/voiceservice/sdk/m_historyevents.h459
-rw-r--r--Plugins/voiceservice/sdk/m_metacontacts.h162
-rw-r--r--Plugins/voiceservice/sdk/m_updater.h146
-rw-r--r--Plugins/voiceservice/voiceservice.cpp1666
-rw-r--r--Plugins/voiceservice/voiceservice.dsp319
-rw-r--r--Plugins/voiceservice/voiceservice.dsw29
1343 files changed, 286607 insertions, 0 deletions
diff --git a/Plugins/Meebo/Docs/meebo_changelog.txt b/Plugins/Meebo/Docs/meebo_changelog.txt
new file mode 100644
index 0000000..61e7bc7
--- /dev/null
+++ b/Plugins/Meebo/Docs/meebo_changelog.txt
@@ -0,0 +1,6 @@
+Meebo
+
+Changelog:
+
+. 0.0.0.1
+ + Initial version \ No newline at end of file
diff --git a/Plugins/Meebo/Docs/meebo_readme.txt b/Plugins/Meebo/Docs/meebo_readme.txt
new file mode 100644
index 0000000..ef2c989
--- /dev/null
+++ b/Plugins/Meebo/Docs/meebo_readme.txt
@@ -0,0 +1,23 @@
+Meebo - Jabber plugin
+---------------------
+
+CAUTION: THIS IS AN BETA STAGE PLUGIN. IT CAN DO VERY BAD THINGS. USE AT YOUR OWN RISK.
+
+This is a plugin for the jabber protocol, to make it work better with the meebo service (mainly with the meebo me widget).
+
+It has the following changes regarding the official jabber protocol:
+- Option to Automatically authorize all new contacts
+- Option to Always remove meebo me contacts when they go offline (with or without history)
+- Detection of meebo me MirVer
+- Better handling of groups and nicks sent by meebo me
+
+The zip contains also some nice icons made by Angeli-Ka (thanks!).
+
+To make this work you need this mod of jabber that support plugins:
+Ansi: http://pescuma.mirandaim.ru/miranda/jabber_mp.zip
+Unicode: http://pescuma.mirandaim.ru/miranda/jabber_mpW.zip
+With some lucky it will be incorporated in the SVN soon.
+
+It also need the latest version of miranda (at SVN) to correctly handle the option pages.
+
+To report bugs/make suggestions, go to the forum thread: http://forums.miranda-im.org/showthread.php?t=11953 \ No newline at end of file
diff --git a/Plugins/Meebo/Docs/meebo_version.txt b/Plugins/Meebo/Docs/meebo_version.txt
new file mode 100644
index 0000000..7f0ec40
--- /dev/null
+++ b/Plugins/Meebo/Docs/meebo_version.txt
@@ -0,0 +1 @@
+Meebo 0.0.0.1 \ No newline at end of file
diff --git a/Plugins/Meebo/Icons/proto_meebo.dll b/Plugins/Meebo/Icons/proto_meebo.dll
new file mode 100644
index 0000000..8ba8891
--- /dev/null
+++ b/Plugins/Meebo/Icons/proto_meebo.dll
Binary files differ
diff --git a/Plugins/Meebo/ZIP/doit.bat b/Plugins/Meebo/ZIP/doit.bat
new file mode 100644
index 0000000..7a39380
--- /dev/null
+++ b/Plugins/Meebo/ZIP/doit.bat
@@ -0,0 +1,64 @@
+@echo off
+
+rem Batch file to build and upload files
+rem
+rem TODO: Integration with FL
+
+set name=meebo
+
+rem To upload, this var must be set here or in other batch
+rem set ftp=ftp://<user>:<password>@<ftp>/<path>
+
+echo Building %name% ...
+
+msdev ..\%name%.dsp /MAKE "%name% - Win32 Release" /REBUILD
+msdev ..\%name%.dsp /MAKE "%name% - Win32 Unicode Release" /REBUILD
+
+echo Generating files for %name% ...
+
+del *.zip
+del *.dll
+copy ..\..\..\bin\release\Plugins\%name%.dll
+copy ..\..\..\bin\release\Plugins\%name%W.dll
+copy ..\Docs\%name%_changelog.txt
+copy ..\Docs\%name%_version.txt
+copy ..\Docs\%name%_readme.txt
+mkdir Docs
+cd Docs
+del /Q *.*
+copy ..\..\Docs\%name%_readme.txt
+copy ..\..\Docs\langpack_%name%.txt
+cd ..
+mkdir Icons
+cd Icons
+del /Q *.*
+copy ..\..\Icons\*.dll
+cd ..
+
+"C:\Program Files\Filzip\Filzip.exe" -a -rp %name%.zip %name%.dll Docs Icons
+"C:\Program Files\Filzip\Filzip.exe" -a -rp %name%W.zip %name%W.dll Docs Icons
+
+del *.dll
+cd Docs
+del /Q *.*
+cd ..
+rmdir Docs
+cd Icons
+del /Q *.*
+cd ..
+rmdir Icons
+
+if "%ftp%"=="" GOTO END
+
+echo Going to upload files...
+pause
+
+"C:\Program Files\FileZilla\FileZilla.exe" -u .\%name%.zip %ftp% -overwrite -close
+"C:\Program Files\FileZilla\FileZilla.exe" -u .\%name%W.zip %ftp% -overwrite -close
+"C:\Program Files\FileZilla\FileZilla.exe" -u .\%name%_changelog.txt %ftp% -overwrite -close
+"C:\Program Files\FileZilla\FileZilla.exe" -u .\%name%_version.txt %ftp% -overwrite -close
+"C:\Program Files\FileZilla\FileZilla.exe" -u .\%name%_readme.txt %ftp% -overwrite -close
+
+:END
+
+echo Done.
diff --git a/Plugins/Meebo/commons.h b/Plugins/Meebo/commons.h
new file mode 100644
index 0000000..bace8aa
--- /dev/null
+++ b/Plugins/Meebo/commons.h
@@ -0,0 +1,64 @@
+/*
+Copyright (C) 2007 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#ifndef __COMMONS_H__
+# define __COMMONS_H__
+
+
+#include <windows.h>
+
+
+// Miranda headers
+#define MIRANDA_VER 0x0720
+#include <newpluginapi.h>
+#include <m_system.h>
+#include <m_system_cpp.h>
+#include <m_protocols.h>
+#include <m_protosvc.h>
+#include <m_contacts.h>
+#include <m_langpack.h>
+#include <m_database.h>
+#include <m_options.h>
+#include <m_utils.h>
+#include <m_updater.h>
+#include <m_popup.h>
+#include <m_message.h>
+#include "../../protocols/JabberG/m_jabber_plugin.h"
+
+#include "../utils/mir_memory.h"
+#include "../utils/mir_options.h"
+
+#include "resource.h"
+
+
+
+// Global Variables
+extern HINSTANCE hInst;
+extern PLUGINLINK *pluginLink;
+
+
+#define MAX_REGS(_A_) ( sizeof(_A_) / sizeof(_A_[0]) )
+
+
+#define MIID_MEEBO { 0x8b29c468, 0xe4bc, 0x4f73, { 0x89, 0xb2, 0x3, 0x89, 0x58, 0x13, 0x4e, 0xbb } }
+
+
+
+#endif // __COMMONS_H__
diff --git a/Plugins/Meebo/meebo.cpp b/Plugins/Meebo/meebo.cpp
new file mode 100644
index 0000000..da55ae5
--- /dev/null
+++ b/Plugins/Meebo/meebo.cpp
@@ -0,0 +1,609 @@
+/*
+Copyright (C) 2006 Ricardo Pescuma Domenecci
+
+ This is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this file; see the file license.txt. If
+ not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#include "commons.h"
+
+
+// Prototypes ///////////////////////////////////////////////////////////////////////////
+
+
+PLUGININFOEX pluginInfo={
+ sizeof(PLUGININFOEX),
+#ifdef UNICODE
+ "Meebo (Unicode)",
+#else
+ "Meebo",
+#endif
+ PLUGIN_MAKE_VERSION(0,0,0,1),
+ "Meebo plugin for Jabber protocol",
+ "Ricardo Pescuma Domenecci",
+ "",
+ "© 2007 Ricardo Pescuma Domenecci",
+ "http://pescuma.mirandaim.ru/miranda/meebo",
+ UNICODE_AWARE,
+ 0, //doesn't replace anything built-in
+#if defined( _UNICODE )
+ { 0x2dcd3555, 0x9be9, 0x4fbf, { 0x9c, 0xfb, 0x24, 0x29, 0xaf, 0xee, 0xeb, 0x96 } } // {2DCD3555-9BE9-4fbf-9CFB-2429AFEEEB96}
+#else
+ { 0xeb3c7d40, 0xbb83, 0x463d, { 0xb2, 0xbf, 0x7e, 0x5, 0x7c, 0xdb, 0x39, 0xc4 } } // {EB3C7D40-BB83-463d-B2BF-7E057CDB39C4}
+#endif
+};
+
+
+HINSTANCE hInst;
+PLUGINLINK *pluginLink;
+LIST_INTERFACE li;
+
+HANDLE hHooks[4] = {0};
+
+LIST<JABBER_DATA> jabbers(2);
+
+TCHAR *servers[] = {
+ _T("guest.meebo.org"),
+ _T("guest1.meebo.org"),
+ _T("guest2.meebo.org"),
+ _T("guest3.meebo.org"),
+ _T("guest4.meebo.org"),
+ _T("guest5.meebo.org"),
+ _T("guest6.meebo.org"),
+ _T("guest7.meebo.org"),
+ _T("guest8.meebo.org"),
+ _T("guest9.meebo.org"),
+};
+
+
+#define JABBER_FEAT_NICK _T("http://jabber.org/protocol/nick")
+
+
+
+int ModulesLoaded(WPARAM wParam, LPARAM lParam);
+int PreShutdown(WPARAM wParam, LPARAM lParam);
+int DBEventAdded(WPARAM wParam, LPARAM lParam);
+int DBSettingChanged(WPARAM wParam, LPARAM lParam);
+
+void RegisterJabberPlugin(const char *proto);
+__inline static int ProtoServiceExists(const char *szModule,const char *szService);
+static BOOL CALLBACK OptionsDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam);
+
+
+// Functions ////////////////////////////////////////////////////////////////////////////
+
+
+extern "C" BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
+{
+ hInst = hinstDLL;
+ return TRUE;
+}
+
+
+extern "C" __declspec(dllexport) PLUGININFO* MirandaPluginInfo(DWORD mirandaVersion)
+{
+ pluginInfo.cbSize = sizeof(PLUGININFO);
+ return (PLUGININFO*) &pluginInfo;
+}
+
+
+extern "C" __declspec(dllexport) PLUGININFOEX* MirandaPluginInfoEx(DWORD mirandaVersion)
+{
+ pluginInfo.cbSize = sizeof(PLUGININFOEX);
+ return &pluginInfo;
+}
+
+
+static const MUUID interfaces[] = { MIID_MEEBO, MIID_LAST };
+extern "C" __declspec(dllexport) const MUUID* MirandaPluginInterfaces(void)
+{
+ return interfaces;
+}
+
+
+extern "C" int __declspec(dllexport) Load(PLUGINLINK *link)
+{
+ pluginLink = link;
+
+ init_mir_malloc();
+
+ li.cbSize = sizeof(li);
+ CallService(MS_SYSTEM_GET_LI, 0, (LPARAM) &li);
+
+ // hooks
+ hHooks[0] = HookEvent(ME_SYSTEM_MODULESLOADED, ModulesLoaded);
+ hHooks[1] = HookEvent(ME_SYSTEM_PRESHUTDOWN, PreShutdown);
+ hHooks[2] = HookEvent(ME_DB_EVENT_ADDED, DBEventAdded);
+ hHooks[3] = HookEvent(ME_DB_CONTACT_SETTINGCHANGED, DBSettingChanged);
+
+ return 0;
+}
+
+extern "C" int __declspec(dllexport) Unload(void)
+{
+ return 0;
+}
+
+// Called when all the modules are loaded
+int ModulesLoaded(WPARAM wParam, LPARAM lParam)
+{
+ // updater plugin support
+ if(ServiceExists(MS_UPDATE_REGISTER))
+ {
+ Update upd = {0};
+ char szCurrentVersion[30];
+
+ upd.cbSize = sizeof(upd);
+ upd.szComponentName = pluginInfo.shortName;
+
+ upd.szUpdateURL = UPDATER_AUTOREGISTER;
+
+ upd.szBetaVersionURL = "http://pescuma.mirandaim.ru/miranda/meebo_version.txt";
+ upd.szBetaChangelogURL = "http://pescuma.mirandaim.ru/miranda/meebo#Changelog";
+ upd.pbBetaVersionPrefix = (BYTE *)"Meebo ";
+ upd.cpbBetaVersionPrefix = strlen((char *)upd.pbBetaVersionPrefix);
+#ifdef UNICODE
+ upd.szBetaUpdateURL = "http://pescuma.mirandaim.ru/miranda/meeboW.zip";
+#else
+ upd.szBetaUpdateURL = "http://pescuma.mirandaim.ru/miranda/meebo.zip";
+#endif
+
+ upd.pbVersion = (BYTE *)CreateVersionStringPlugin((PLUGININFO*) &pluginInfo, szCurrentVersion);
+ upd.cpbVersion = strlen((char *)upd.pbVersion);
+
+ CallService(MS_UPDATE_REGISTER, 0, (LPARAM)&upd);
+ }
+
+ PROTOCOLDESCRIPTOR **protos;
+ int count;
+ CallService(MS_PROTO_ENUMPROTOCOLS, (WPARAM)&count, (LPARAM)&protos);
+ for (int i = 0; i < count; i++)
+ {
+ if (protos[i]->type != PROTOTYPE_PROTOCOL)
+ continue;
+
+ if (protos[i]->szName == NULL || protos[i]->szName[0] == '\0')
+ continue;
+
+ // Found a protocol
+ RegisterJabberPlugin(protos[i]->szName);
+ }
+
+
+ return 0;
+}
+
+
+int PreShutdown(WPARAM wParam, LPARAM lParam)
+{
+ for (int i = 0; i < MAX_REGS(hHooks); ++i)
+ if (hHooks[i] != NULL)
+ UnhookEvent(hHooks[i]);
+
+ return 0;
+}
+
+
+BOOL OptAutoAuth(JABBER_DATA *data)
+{
+ return DBGetContactSettingByte(NULL, data->protocolName, "Meebome_AutoAuth", TRUE);
+}
+
+
+BOOL OptRemoveContactsWithoutHistory(JABBER_DATA *data)
+{
+ return DBGetContactSettingByte(NULL, data->protocolName, "Meebome_RemoveContactsWithoutHistory", TRUE);
+}
+
+
+BOOL OptRemoveContactsWithHistory(JABBER_DATA *data)
+{
+ return DBGetContactSettingByte(NULL, data->protocolName, "Meebome_RemoveContactsWithHistory", FALSE);
+}
+
+
+BOOL OptMove(JABBER_DATA *data)
+{
+ return DBGetContactSettingByte(NULL, data->protocolName, "Meebome_Move", FALSE);
+}
+
+
+JABBER_DATA * IsMeeboProtocol(const char *proto)
+{
+ for (int i = 0; i < jabbers.getCount(); i++)
+ if (strcmp(jabbers[i]->protocolName, proto) == 0)
+ return jabbers[i];
+
+ return NULL;
+}
+
+
+BOOL IsMeeboMeContact(const char *proto, HANDLE hContact, TCHAR *jid = NULL)
+{
+ BOOL ret = FALSE;
+
+ DBVARIANT dbv;
+ if (jid != NULL || !DBGetContactSettingTString(hContact, proto, "jid", &dbv))
+ {
+ TCHAR *server = _tcschr(jid != NULL ? jid : dbv.ptszVal, _T('@'));
+ if (server != NULL)
+ {
+ server++;
+
+ for (int i = 0; i < MAX_REGS(servers); i++)
+ {
+ if (lstrcmp(server, servers[i]) == 0)
+ {
+ ret = TRUE;
+ break;
+ }
+ }
+ }
+
+ if (jid == NULL)
+ DBFreeVariant(&dbv);
+ }
+
+ if (!ret)
+ {
+ if (!DBGetContactSettingTString(hContact, proto, "MirVer", &dbv))
+ {
+ ret = (lstrcmp(_T("meebome"), dbv.ptszVal) == 0);
+ DBFreeVariant(&dbv);
+ }
+ }
+
+ return ret;
+}
+
+
+BOOL HasUsefullHistory(const char *proto, HANDLE hContact)
+{
+ return DBGetContactSettingByte(hContact, proto, "Meebome_KeepOnList", FALSE);
+}
+
+
+void DeleteContact(const char *proto, HANDLE hContact)
+{
+ // Check if protocol uses server side lists
+ DWORD caps = (DWORD) CallProtoService(proto, PS_GETCAPS, PFLAGNUM_1, 0);
+ if (caps & PF1_SERVERCLIST)
+ {
+ int status = CallProtoService(proto, PS_GETSTATUS, 0, 0);
+ if (status <= ID_STATUS_OFFLINE)
+ {
+ // Set a flag so we remember to delete the contact when the protocol goes online the next time
+ DBWriteContactSettingByte(hContact, "CList", "Delete", 1);
+ DBWriteContactSettingByte(hContact, "CList", "Hidden", 1);
+ return;
+ }
+ }
+
+ CallService(MS_DB_CONTACT_DELETE, (WPARAM) hContact, 0);
+}
+
+
+void CheckAndDeleteContact(JABBER_DATA *data, HANDLE hContact)
+{
+ if (IsMeeboMeContact(data->protocolName, hContact))
+ {
+ if (HasUsefullHistory(data->protocolName, hContact))
+ {
+ if (OptRemoveContactsWithHistory(data))
+ DeleteContact(data->protocolName, hContact);
+ }
+ else
+ {
+ if (OptRemoveContactsWithoutHistory(data))
+ DeleteContact(data->protocolName, hContact);
+ }
+ }
+}
+
+
+void CheckAndDeleteAllContacts(JABBER_DATA *data)
+{
+ if (!OptRemoveContactsWithHistory(data) && !OptRemoveContactsWithoutHistory(data))
+ return;
+
+ HANDLE hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDFIRST, 0, 0);
+ while (hContact != NULL)
+ {
+ HANDLE hNext = (HANDLE) CallService(MS_DB_CONTACT_FINDNEXT, (WPARAM) hContact, 0);
+
+ char *proto = (char *) CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM) hContact, 0);
+ if (proto != NULL && strcmp(proto, data->protocolName) == 0)
+ CheckAndDeleteContact(data, hContact);
+
+ hContact = hNext;
+ }
+}
+
+void OnDisconnect(void *param)
+{
+ JABBER_DATA *data = jabbers[(int) param];
+ if (data == NULL)
+ return;
+
+ CheckAndDeleteAllContacts(data);
+}
+
+
+void FetchNick(JABBER_DATA *data, IXmlNode *node, HANDLE hContact)
+{
+ IXmlNode *nickNode = data->pfXmlGetChildByName(node, "nick");
+ if (nickNode == NULL)
+ return;
+
+ const TCHAR *xmlns = data->pfXmlGetAttrValueStr(nickNode, "xmlns");
+ if (xmlns == NULL || lstrcmp(xmlns, JABBER_FEAT_NICK) != 0)
+ return;
+
+ const TCHAR *nick = data->pfXmlGetNodeText(nickNode);
+ if (nick == NULL)
+ return;
+
+ DBWriteContactSettingTString(hContact, data->protocolName, "Nick", nick);
+}
+
+void AfterXmlReceived(void *param, IXmlNode *node)
+{
+ JABBER_DATA *data = jabbers[(int) param];
+ if (data == NULL)
+ return;
+
+ if (strcmp(data->pfXmlGetNodeName(node), "message") != 0)
+ return;
+
+ const TCHAR *from = data->pfXmlGetAttrValueStr(node, "from");
+ if (from == NULL )
+ return;
+
+ HANDLE hContact = data->pfHContactFromJID(from);
+ if (hContact == NULL)
+ return;
+
+ if (DBGetContactSettingByte(hContact, data->protocolName, "ChatRoom", 0))
+ return;
+
+ FetchNick(data, node, hContact);
+
+ if (OptAutoAuth(data))
+ {
+ if (DBGetContactSettingByte(hContact, "CList", "NotOnList", 0))
+ {
+ if (OptMove(data))
+ {
+ DBVARIANT dbv;
+ if (!DBGetContactSettingTString(NULL, data->protocolName, "Meebome_Group", &dbv))
+ {
+ DBWriteContactSettingTString(hContact, "CList", "Group", dbv.ptszVal);
+ DBFreeVariant(&dbv);
+ }
+ }
+
+ DBDeleteContactSetting(hContact, "CList", "NotOnList");
+ }
+ }
+}
+
+
+// Called when a new contact is added an an auth request must be shown
+// Return TRUE to avoid further processing.
+BOOL ProcessAuthRequest(void *param, HANDLE hContact, TCHAR *jid, TCHAR *nick, IXmlNode *node)
+{
+ JABBER_DATA *data = jabbers[(int) param];
+ if (data == NULL || hContact == NULL || jid == NULL || nick == NULL || node == NULL)
+ return FALSE;
+
+ if (!IsMeeboMeContact(data->protocolName, hContact, jid))
+ return FALSE;
+
+ DBWriteContactSettingTString(hContact, data->protocolName, "MirVer", _T("meebome"));
+
+ if (!OptAutoAuth(data))
+ return FALSE;
+
+ if (nick != NULL)
+ DBWriteContactSettingTString(hContact, data->protocolName, "Nick", nick);
+
+ if (OptMove(data))
+ {
+ DBVARIANT dbv;
+ if (!DBGetContactSettingTString(NULL, data->protocolName, "Meebome_Group", &dbv))
+ {
+ DBWriteContactSettingTString(hContact, "CList", "Group", dbv.ptszVal);
+ DBFreeVariant(&dbv);
+ }
+ }
+ else
+ {
+ const TCHAR *group = data->pfXmlGetAttrValueStr(node, "group");
+ if (group != NULL)
+ DBWriteContactSettingTString(hContact, "CList", "Group", group);
+ }
+
+ DBWriteContactSettingByte(hContact, "CList", "NotOnList", 1);
+
+ IXmlNode *presence = data->pfXmlCreateNode("presence");
+ data->pfXmlAddAttr(presence, "to", jid);
+ data->pfXmlAddAttr(presence, "type", _T("subscribed"));
+ data->pfSendNode(presence);
+ data->pfXmlDeleteNode(presence);
+
+ DBDeleteContactSetting(hContact, "CList", "NotOnList");
+
+ return TRUE;
+}
+
+
+// Callback to add option dialogs
+void AddOptions(void *param, WPARAM wParam)
+{
+ JABBER_DATA *data = jabbers[(int) param];
+ if (data == NULL)
+ return;
+
+ char name[128];
+ CallProtoService(data->protocolName, PS_GETNAME, MAX_REGS(name), (LPARAM) name);
+
+ OPTIONSDIALOGPAGE odp = { 0 };
+ odp.cbSize = sizeof( odp );
+ odp.hInstance = hInst;
+ odp.pszGroup = "Network";
+ odp.pszTitle = name;
+ odp.pszTab = "Meebo";
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT_MEEBO);
+ odp.pfnDlgProc = OptionsDlgProc;
+ odp.flags = ODPF_BOLDGROUPS | ODPF_EXPERTONLY;
+ odp.dwInitParam = (LPARAM) param;
+ CallService(MS_OPT_ADDPAGE, wParam, (LPARAM) &odp);
+}
+
+
+void RegisterJabberPlugin(const char *proto)
+{
+ if (!ProtoServiceExists(proto, PS_REGISTER_JABBER_PLUGIN))
+ return;
+
+ JABBER_PLUGIN_DATA info = {
+ sizeof(JABBER_PLUGIN_DATA),
+ "Meebo",
+ "This is a meebo server",
+ FALSE,
+ (void *) jabbers.getCount(),
+ NULL,
+ OnDisconnect,
+ NULL,
+ AfterXmlReceived,
+ NULL,
+ ProcessAuthRequest,
+ AddOptions
+ };
+
+
+ JABBER_DATA *data = (JABBER_DATA *) CallProtoService(proto, PS_REGISTER_JABBER_PLUGIN, (WPARAM) &info, 1);
+ if (data == NULL)
+ // We are disabled / ignored
+ return;
+
+ jabbers.insert(data, jabbers.getCount());
+
+ CheckAndDeleteAllContacts(data);
+}
+
+
+int DBEventAdded(WPARAM wParam, LPARAM lParam)
+{
+ if (wParam == NULL || lParam == NULL)
+ return 0;
+
+ HANDLE hContact = (HANDLE) wParam;
+ char *proto = (char *) CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM) hContact, 0);
+
+ // Is it our protocol?
+ JABBER_DATA *data = IsMeeboProtocol(proto);
+ if (data == NULL)
+ return 0;
+
+ // Is it a meebome contact?
+ if (!IsMeeboMeContact(proto, hContact))
+ return 0;
+
+ // Is this a valid event?
+ HANDLE hDbEvent = (HANDLE) lParam;
+ DBEVENTINFO dbe = {0};
+ dbe.cbSize = sizeof(dbe);
+ if (CallService(MS_DB_EVENT_GET, (LPARAM) hDbEvent, (WPARAM) &dbe) != 0)
+ return 0;
+
+ if (dbe.eventType != EVENTTYPE_MESSAGE && dbe.eventType != EVENTTYPE_URL && dbe.eventType != EVENTTYPE_FILE)
+ return 0;
+
+ // Ok, let's keep the contact
+ DBWriteContactSettingByte(hContact, proto, "Meebome_KeepOnList", TRUE);
+
+ return 0;
+}
+
+
+int DBSettingChanged(WPARAM wParam, LPARAM lParam)
+{
+ if (wParam == NULL || lParam == NULL)
+ return 0;
+
+ DBCONTACTWRITESETTING* cws = (DBCONTACTWRITESETTING*) lParam;
+ if (strcmp(cws->szSetting, "Status") != 0 || cws->value.type != DBVT_WORD || cws->value.wVal > ID_STATUS_OFFLINE)
+ return 0;
+
+ HANDLE hContact = (HANDLE) wParam;
+ char *proto = (char *) CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM) hContact, 0);
+ if (proto == NULL || proto[0] == '\0')
+ return 0;
+
+ JABBER_DATA *data = IsMeeboProtocol(proto);
+ if (data == NULL)
+ return 0;
+
+ CheckAndDeleteContact(data, hContact);
+
+ return 0;
+}
+
+
+//call a specific protocol service. See the PS_ constants in m_protosvc.h
+__inline static int ProtoServiceExists(const char *szModule,const char *szService)
+{
+ char str[MAXMODULELABELLENGTH];
+ strcpy(str,szModule);
+ strcat(str,szService);
+ return ServiceExists(str);
+}
+
+
+static BOOL CALLBACK OptionsDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam);
+static BOOL CALLBACK PopupsDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam);
+
+
+static OptPageControl optionsControls[] = {
+ { NULL, CONTROL_CHECKBOX, IDC_AUTO_AUTHORIZE, "Meebome_AutoAuth", TRUE },
+ { NULL, CONTROL_CHECKBOX, IDC_REMOVE_NO_HISTORY, "Meebome_RemoveContactsWithoutHistory", TRUE },
+ { NULL, CONTROL_CHECKBOX, IDC_ALWAYS_REMOVE_HISTORY, "Meebome_RemoveContactsWithHistory", FALSE },
+ { NULL, CONTROL_CHECKBOX, IDC_MOVE, "Meebome_Move", FALSE },
+ { NULL, CONTROL_TEXT, IDC_GROUP, "Meebome_Group", (DWORD) _T("meebo me") },
+};
+
+static BOOL CALLBACK OptionsDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg)
+ {
+ case WM_INITDIALOG:
+ {
+ JABBER_DATA *data = jabbers[(int) lParam];
+ if (data == NULL)
+ break;
+ SetWindowLong(hwndDlg, GWL_USERDATA, (LONG) data->protocolName);
+ break;
+ }
+ }
+
+ char *proto = (char *) GetWindowLong(hwndDlg, GWL_USERDATA);
+ if (proto == NULL)
+ return FALSE;
+
+ return SaveOptsDlgProc(optionsControls, MAX_REGS(optionsControls), proto, hwndDlg, msg, wParam, lParam);
+}
+
+
diff --git a/Plugins/Meebo/meebo.dsp b/Plugins/Meebo/meebo.dsp
new file mode 100644
index 0000000..7696205
--- /dev/null
+++ b/Plugins/Meebo/meebo.dsp
@@ -0,0 +1,215 @@
+# Microsoft Developer Studio Project File - Name="meebo" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=meebo - Win32 Release
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "meebo.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "meebo.mak" CFG="meebo - Win32 Release"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "meebo - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "meebo - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "meebo - Win32 Unicode Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "meebo - Win32 Unicode Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "meebo - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MD /W3 /GX /O1 /YX /FD /c
+# SUBTRACT BASE CPP /Fr
+# ADD CPP /nologo /G4 /MT /W3 /GX /O2 /Ob0 /I "../../include" /I "sdk" /D "WIN32" /D "W32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /Fr /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x417 /d "NDEBUG"
+# ADD RSC /l 0x417 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 user32.lib shell32.lib wininet.lib gdi32.lib /nologo /base:"0x67100000" /dll /machine:I386 /filealign:0x200
+# SUBTRACT BASE LINK32 /pdb:none /map
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib /nologo /base:"0x3EC20000" /dll /map /debug /debugtype:both /machine:I386 /out:"..\..\bin\release\Plugins\meebo.dll" /pdbtype:sept /filealign:0x200 /ALIGN:4096 /ignore:4108
+# SUBTRACT LINK32 /profile /pdb:none
+
+!ELSEIF "$(CFG)" == "meebo - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Ignore_Export_Lib 0
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /G4 /MT /W3 /GX /O2 /Ob0 /I "../../include" /FR /YX /FD /c
+# ADD CPP /nologo /G4 /MTd /W3 /GX /ZI /Od /I "../../include" /I "sdk" /D "WIN32" /D "W32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /FR /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x417 /d "NDEBUG"
+# ADD RSC /l 0x417 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 comctl32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 /out:"..\..bin\release\Plugins\meebo.dll" /filealign:0x200 /ALIGN:4096 /ignore:4108
+# SUBTRACT BASE LINK32 /profile /pdb:none
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib /nologo /base:"0x3EC20000" /dll /incremental:yes /debug /machine:I386 /out:"..\..\bin\debug\Plugins\meebo.dll" /filealign:0x200 /ALIGN:4096 /ignore:4108
+# SUBTRACT LINK32 /profile /pdb:none
+
+!ELSEIF "$(CFG)" == "meebo - Win32 Unicode Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "meebo___Win32_Unicode_Debug"
+# PROP BASE Intermediate_Dir "meebo___Win32_Unicode_Debug"
+# PROP BASE Ignore_Export_Lib 0
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Unicode_Debug"
+# PROP Intermediate_Dir "Unicode_Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /G4 /MTd /W3 /GX /ZI /Od /I "../../include" /FR /YX /FD /c
+# ADD CPP /nologo /G4 /MTd /W3 /GX /ZI /Od /I "../../include" /I "sdk" /D "WIN32" /D "W32" /D "_DEBUG" /D "_WINDOWS" /D "_UNICODE" /D "UNICODE" /D "_USRDLL" /FR /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x417 /d "NDEBUG"
+# ADD RSC /l 0x417 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 comctl32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /base:"0x32100000" /dll /incremental:yes /debug /machine:I386 /out:"..\..\bin\debug\Plugins\meebo.dll" /filealign:0x200 /ALIGN:4096 /ignore:4108
+# SUBTRACT BASE LINK32 /profile /pdb:none
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib /nologo /base:"0x3EC20000" /dll /incremental:yes /debug /machine:I386 /out:"..\..\bin\debug unicode\Plugins\meeboW.dll" /filealign:0x200 /ALIGN:4096 /ignore:4108
+# SUBTRACT LINK32 /profile /pdb:none
+
+!ELSEIF "$(CFG)" == "meebo - Win32 Unicode Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "meebo___Win32_Unicode_Release"
+# PROP BASE Intermediate_Dir "meebo___Win32_Unicode_Release"
+# PROP BASE Ignore_Export_Lib 0
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Unicode_Release"
+# PROP Intermediate_Dir "Unicode_Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /G4 /MT /W3 /GX /O2 /Ob0 /I "../../include" /Fr /YX /FD /c
+# ADD CPP /nologo /G4 /MT /W3 /GX /O2 /Ob0 /I "../../include" /I "sdk" /D "WIN32" /D "W32" /D "NDEBUG" /D "_WINDOWS" /D "_UNICODE" /D "UNICODE" /D "_USRDLL" /Fr /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x417 /d "NDEBUG"
+# ADD RSC /l 0x417 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 comctl32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /base:"0x32100000" /dll /map /machine:I386 /out:"..\..\bin\release\Plugins\meebo.dll" /filealign:0x200 /ALIGN:4096 /ignore:4108
+# SUBTRACT BASE LINK32 /profile /pdb:none
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib /nologo /base:"0x3EC20000" /dll /map /debug /debugtype:both /machine:I386 /out:"..\..\bin\release\Plugins\meeboW.dll" /pdbtype:sept /filealign:0x200 /ALIGN:4096 /ignore:4108
+# SUBTRACT LINK32 /profile /pdb:none
+
+!ENDIF
+
+# Begin Target
+
+# Name "meebo - Win32 Release"
+# Name "meebo - Win32 Debug"
+# Name "meebo - Win32 Unicode Debug"
+# Name "meebo - Win32 Unicode Release"
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=.\commons.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\utils\mir_memory.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\utils\mir_options.h
+# End Source File
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
+# Begin Source File
+
+SOURCE=.\resource.rc
+# End Source File
+# End Group
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=.\meebo.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\utils\mir_memory.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\utils\mir_options.cpp
+# End Source File
+# End Group
+# Begin Group "Docs"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=.\Docs\meebo_changelog.txt
+# End Source File
+# Begin Source File
+
+SOURCE=.\Docs\meebo_readme.txt
+# End Source File
+# Begin Source File
+
+SOURCE=.\Docs\meebo_version.txt
+# End Source File
+# End Group
+# End Target
+# End Project
diff --git a/Plugins/Meebo/meebo.dsw b/Plugins/Meebo/meebo.dsw
new file mode 100644
index 0000000..40b8de4
--- /dev/null
+++ b/Plugins/Meebo/meebo.dsw
@@ -0,0 +1,29 @@
+Microsoft Developer Studio Workspace File, Format Version 6.00
+# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
+
+###############################################################################
+
+Project: "meebo"=.\meebo.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Global:
+
+Package=<5>
+{{{
+}}}
+
+Package=<3>
+{{{
+}}}
+
+###############################################################################
+
diff --git a/Plugins/Meebo/resource.h b/Plugins/Meebo/resource.h
new file mode 100644
index 0000000..5081950
--- /dev/null
+++ b/Plugins/Meebo/resource.h
@@ -0,0 +1,22 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Developer Studio generated include file.
+// Used by resource.rc
+//
+#define IDD_DIALOGBAR 103
+#define IDC_AUTO_AUTHORIZE 1240
+#define IDC_REMOVE_NO_HISTORY 1241
+#define IDC_MOVE 1242
+#define IDC_GROUP 1243
+#define IDC_ALWAYS_REMOVE_HISTORY 1244
+#define IDD_OPT_MEEBO 3010
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 101
+#define _APS_NEXT_COMMAND_VALUE 40001
+#define _APS_NEXT_CONTROL_VALUE 1000
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/Plugins/Meebo/resource.rc b/Plugins/Meebo/resource.rc
new file mode 100644
index 0000000..be6b631
--- /dev/null
+++ b/Plugins/Meebo/resource.rc
@@ -0,0 +1,120 @@
+//Microsoft Developer Studio generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "afxres.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (U.S.) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page(1252)
+#endif //_WIN32
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// DESIGNINFO
+//
+
+#ifdef APSTUDIO_INVOKED
+GUIDELINES DESIGNINFO DISCARDABLE
+BEGIN
+ IDD_OPT_MEEBO, DIALOG
+ BEGIN
+ LEFTMARGIN, 8
+ RIGHTMARGIN, 296
+ VERTGUIDE, 9
+ TOPMARGIN, 3
+ BOTTOMMARGIN, 69
+ END
+END
+#endif // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Dialog
+//
+
+IDD_OPT_MEEBO DIALOGEX 0, 0, 304, 74
+STYLE DS_FIXEDSYS | WS_CHILD
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg"
+BEGIN
+ CONTROL "Automatically authorize meebo me contacts",
+ IDC_AUTO_AUTHORIZE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,
+ 16,10,273,10
+ CONTROL "Remove meebo me contacts that don't have history when they go offline",
+ IDC_REMOVE_NO_HISTORY,"Button",BS_AUTOCHECKBOX |
+ WS_TABSTOP,16,23,273,10
+ CONTROL "Remove meebo me contacts that have history when they go offline",
+ IDC_ALWAYS_REMOVE_HISTORY,"Button",BS_AUTOCHECKBOX |
+ WS_TABSTOP,16,36,273,10
+ CONTROL "Move meebo me contacts to group:",IDC_MOVE,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,16,49,130,10
+ EDITTEXT IDC_GROUP,150,48,139,12,ES_AUTOHSCROLL
+END
+
+#endif // English (U.S.) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+/////////////////////////////////////////////////////////////////////////////
+// Portuguese (Brazil) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_PTB)
+#ifdef _WIN32
+LANGUAGE LANG_PORTUGUESE, SUBLANG_PORTUGUESE_BRAZILIAN
+#pragma code_page(1252)
+#endif //_WIN32
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "resource.h\0"
+END
+
+2 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "#include ""afxres.h""\r\n"
+ "\0"
+END
+
+3 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "\r\n"
+ "\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+#endif // Portuguese (Brazil) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
+
diff --git a/Plugins/Meebo/sdk/m_updater.h b/Plugins/Meebo/sdk/m_updater.h
new file mode 100644
index 0000000..371b743
--- /dev/null
+++ b/Plugins/Meebo/sdk/m_updater.h
@@ -0,0 +1,146 @@
+#ifndef _M_UPDATER_H
+#define _M_UPDATER_H
+
+// NOTES:
+// - For langpack updates, include a string of the following format in the langpack text file:
+// ";FLID: <file listing name> <version>"
+// version must be four numbers seperated by '.', in the range 0-255 inclusive
+// - Updater will disable plugins that are downloaded but were not active prior to the update (this is so that, if an archive contains e.g. ansi and
+// unicode versions, the correct plugin will be the only one active after the new version is installed)...so if you add a support plugin, you may need
+// to install an ini file to make the plugin activate when miranda restarts after the update
+// - Updater will replace all dlls that have the same internal shortName as a downloaded update dll (this is so that msn1.dll and msn2.dll, for example,
+// will both be updated) - so if you have a unicode and a non-unicode version of a plugin in your archive, you should make the internal names different (which will break automatic
+// updates from the file listing if there is only one file listing entry for both versions, unless you use the 'MS_UPDATE_REGISTER' service below)
+// - Updater will install all files in the root of the archive into the plugins folder, except for langpack files that contain the FLID string which go into the root folder (same
+// folder as miranda32.exe)...all folders in the archive will also be copied to miranda's root folder, and their contents transferred into the new folders. The only exception is a
+// special folder called 'root_files' - if there is a folder by that name in the archive, it's contents will also be copied into miranda's root folder - this is intended to be used
+// to install additional dlls etc that a plugin may require)
+
+// if you set Update.szUpdateURL to the following value when registering, as well as setting your beta site and version data,
+// Updater will ignore szVersionURL and pbVersionPrefix, and attempt to find the file listing URL's from the backend XML data.
+// for this to work, the plugin name in pluginInfo.shortName must match the file listing exactly (except for case)
+#define UPDATER_AUTOREGISTER "UpdaterAUTOREGISTER"
+// Updater will also use the backend xml data if you provide URL's that reference the miranda file listing for updates (so you can use that method
+// if e.g. your plugin shortName does not match the file listing) - it will grab the file listing id from the end of these URLs
+
+typedef struct Update_tag {
+ int cbSize;
+ char *szComponentName; // component name as it will appear in the UI (will be translated before displaying)
+
+ char *szVersionURL; // URL where the current version can be found (NULL to disable)
+ BYTE *pbVersionPrefix; // bytes occuring in VersionURL before the version, used to locate the version information within the URL data
+ // (note that this URL could point at a binary file - dunno why, but it could :)
+ int cpbVersionPrefix; // number of bytes pointed to by pbVersionPrefix
+ char *szUpdateURL; // URL where dll/zip is located
+ // set to UPDATER_AUTOREGISTER if you want Updater to find the file listing URLs (ensure plugin shortName matches file listing!)
+
+ char *szBetaVersionURL; // URL where the beta version can be found (NULL to disable betas)
+ BYTE *pbBetaVersionPrefix; // bytes occuring in VersionURL before the version, used to locate the version information within the URL data
+ int cpbBetaVersionPrefix; // number of bytes pointed to by pbVersionPrefix
+ char *szBetaUpdateURL; // URL where dll/zip is located
+
+ BYTE *pbVersion; // bytes of current version, used for comparison with those in VersionURL
+ int cpbVersion; // number of bytes pointed to by pbVersion
+
+ char *szBetaChangelogURL; // url for displaying changelog for beta versions
+} Update;
+
+// register a comonent with Updater
+//
+// wparam = 0
+// lparam = (LPARAM)&Update
+#define MS_UPDATE_REGISTER "Update/Register"
+
+// utility functions to create a version string from a DWORD or from pluginInfo
+// point buf at a buffer at least 16 chars wide - but note the version string returned may be shorter
+//
+__inline static char *CreateVersionString(DWORD version, char *buf) {
+ mir_snprintf(buf, 16, "%d.%d.%d.%d", (version >> 24) & 0xFF, (version >> 16) & 0xFF, (version >> 8) & 0xFF, version & 0xFF);
+ return buf;
+}
+
+__inline static char *CreateVersionStringPlugin(PLUGININFO *pluginInfo, char *buf) {
+ return CreateVersionString(pluginInfo->version, buf);
+}
+
+
+// register the 'easy' way - use this method if you have no beta URL and the plugin is on the miranda file listing
+// NOTE: the plugin version string on the file listing must be the string version of the version in pluginInfo (i.e. 0.0.0.1,
+// four numbers between 0 and 255 inclusivem, so no letters, brackets, etc.)
+//
+// wParam = (int)fileID - this is the file ID from the file listing (i.e. the number at the end of the download link)
+// lParam = (PLUGININFO*)&pluginInfo
+#define MS_UPDATE_REGISTERFL "Update/RegisterFL"
+
+// this function can be used to 'unregister' components - useful for plugins that register non-plugin/langpack components and
+// may need to change those components on the fly
+// lParam = (char *)szComponentName
+#define MS_UPDATE_UNREGISTER "Update/Unregister"
+
+// this event is fired when the startup process is complete, but NOT if a restart is imminent
+// it is designed for status managment plugins to use as a trigger for beggining their own startup process
+// wParam = lParam = 0 (unused)
+// (added in version 0.1.6.0)
+#define ME_UPDATE_STARTUPDONE "Update/StartupDone"
+
+// this service can be used to enable/disable Updater's global status control
+// it can be called from the StartupDone event handler
+// wParam = (BOOL)enable
+// lParam = 0
+// (added in version 0.1.6.0)
+#define MS_UPDATE_ENABLESTATUSCONTROL "Update/EnableStatusControl"
+
+// An description of usage of the above service and event:
+// Say you are a status control plugin that normally sets protocol or global statuses in your ModulesLoaded event handler.
+// In order to make yourself 'Updater compatible', you would move the status control code from ModulesLoaded to another function,
+// say DoStartup. Then, in ModulesLoaded you would check for the existence of the MS_UPDATE_ENABLESTATUSCONTROL service.
+// If it does not exist, call DoStartup. If it does exist, hook the ME_UPDATE_STARTUPDONE event and call DoStartup from there. You may
+// also wish to call MS_UPDATE_ENABLESTATUSCONTROL with wParam == FALSE at this time, to disable Updater's own status control feature.
+
+// this service can be used to determine whether updates are possible for a component with the given name
+// wParam = 0
+// lParam = (char *)szComponentName
+// returns TRUE if updates are supported, FALSE otherwise
+#define MS_UPDATE_ISUPDATESUPPORTED "Update/IsUpdateSupported"
+
+#endif
+
+
+/////////////// Usage Example ///////////////
+
+#ifdef EXAMPLE_CODE
+
+// you need to #include "m_updater.h" and HookEvent(ME_SYSTEM_MODULESLOADED, OnModulesLoaded) in your Load function...
+
+int OnModulesLoaded(WPARAM wParam, LPARAM lParam) {
+
+ Update update = {0}; // for c you'd use memset or ZeroMemory...
+ char szVersion[16];
+
+ update.cbSize = sizeof(Update);
+
+ update.szComponentName = pluginInfo.shortName;
+ update.pbVersion = (BYTE *)CreateVersionString(&pluginInfo, szVersion);
+ update.cpbVersion = strlen((char *)update.pbVersion);
+
+ // these are the three lines that matter - the archive, the page containing the version string, and the text (or data)
+ // before the version that we use to locate it on the page
+ // (note that if the update URL and the version URL point to standard file listing entries, the backend xml
+ // data will be used to check for updates rather than the actual web page - this is not true for beta urls)
+ update.szUpdateURL = "http://scottellis.com.au:81/test/updater.zip";
+ update.szVersionURL = "http://scottellis.com.au:81/test/updater_test.html";
+ update.pbVersionPrefix = (BYTE *)"Updater version ";
+
+ update.cpbVersionPrefix = strlen((char *)update.pbVersionPrefix);
+
+ // do the same for the beta versions of the above struct members if you wish to allow beta updates from another URL
+
+ CallService(MS_UPDATE_REGISTER, 0, (WPARAM)&update);
+
+ // Alternatively, to register a plugin with e.g. file ID 2254 on the file listing...
+ // CallService(MS_UPDATE_REGISTERFL, (WPARAM)2254, (LPARAM)&pluginInfo);
+
+ return 0;
+}
+
+#endif
diff --git a/Plugins/avatarhistory/AvatarDlg.cpp b/Plugins/avatarhistory/AvatarDlg.cpp
new file mode 100644
index 0000000..31ad58b
--- /dev/null
+++ b/Plugins/avatarhistory/AvatarDlg.cpp
@@ -0,0 +1,651 @@
+/*
+Avatar History Plugin
+ Copyright (C) 2006 Matthew Wild - Email: mwild1@gmail.com
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+*/
+
+#include "AvatarHistory.h"
+
+#include <commctrl.h>
+#include <prsht.h>
+
+extern HINSTANCE hInst;
+HANDLE hMenu = NULL;
+int OpenAvatarDialog(HANDLE hContact, char* fn);
+DWORD WINAPI AvatarDialogThread(LPVOID param);
+static BOOL CALLBACK AvatarDlgProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam);
+int ShowSaveDialog(HWND hwnd, TCHAR* fn);
+
+BOOL ProtocolEnabled(const char *proto);
+int FillAvatarListFromDB(HWND list, HANDLE hContact);
+int FillAvatarListFromFolder(HWND list, HANDLE hContact);
+int CleanupAvatarPic(HWND hwnd);
+BOOL UpdateAvatarPic(HWND hwnd);
+TCHAR* GetCurrentSelFile(HWND list);
+TCHAR * GetContactFolder(TCHAR *fn, HANDLE hContact);
+BOOL ResolveShortcut(TCHAR *shortcut, TCHAR *file);
+
+static int ShowDialogSvc(WPARAM wParam, LPARAM lParam);
+extern HANDLE hServices[];
+extern HANDLE hHooks[];
+
+struct AvatarDialogData
+{
+ HANDLE hContact;
+ TCHAR fn[MAX_PATH];
+ HWND parent;
+};
+
+
+class ListEntry
+{
+public:
+ ListEntry()
+ {
+ dbe = NULL;
+ filename = NULL;
+ filelink = NULL;
+ }
+
+ ~ListEntry()
+ {
+ mir_free(filename);
+ mir_free(filelink);
+ }
+
+ HANDLE dbe;
+ TCHAR *filename;
+ TCHAR *filelink;
+};
+
+int OpenAvatarDialog(HANDLE hContact, char* fn)
+{
+ DWORD dwId;
+ struct AvatarDialogData* avdlg;
+ avdlg = (struct AvatarDialogData*)malloc(sizeof(struct AvatarDialogData));
+ ZeroMemory(avdlg, sizeof(struct AvatarDialogData));
+ avdlg->hContact = hContact;
+ if (fn == NULL)
+ {
+ avdlg->fn[0] = _T('\0');
+ }
+ else
+ {
+#ifdef UNICODE
+ MultiByteToWideChar(CP_ACP, 0, fn, -1, avdlg->fn, MAX_REGS(avdlg->fn));
+#else
+ lstrcpyn(avdlg->fn, fn, sizeof(avdlg->fn));
+#endif
+ }
+
+ CloseHandle(CreateThread(NULL, 0, AvatarDialogThread, (LPVOID)avdlg, 0, &dwId));
+ return 0;
+}
+
+DWORD WINAPI AvatarDialogThread(LPVOID param)
+{
+ struct AvatarDialogData* data = (struct AvatarDialogData*)param;
+ DialogBoxParam(hInst, MAKEINTRESOURCE(IDD_AVATARDLG), data->parent, AvatarDlgProc, (LPARAM)param);
+ return 0;
+}
+
+void EnableDisableControls(HWND hwnd)
+{
+ HWND list = GetDlgItem(hwnd, IDC_AVATARLIST);
+
+ int cursel = SendMessage(list, LB_GETCURSEL, 0, 0);
+ int count = SendMessage(list, LB_GETCOUNT, 0, 0);
+
+ if (cursel == LB_ERR)
+ {
+ EnableWindow(GetDlgItem(hwnd, IDC_BACK), count > 0);
+ EnableWindow(GetDlgItem(hwnd, IDC_NEXT), count > 0);
+ }
+ else
+ {
+ EnableWindow(GetDlgItem(hwnd, IDC_BACK), cursel > 0);
+ EnableWindow(GetDlgItem(hwnd, IDC_NEXT), cursel < count-1);
+ }
+
+ EnableWindow(GetDlgItem(hwnd, IDC_SAVE), cursel != LB_ERR);
+ EnableWindow(GetDlgItem(hwnd, IDC_DELETE), cursel != LB_ERR);
+}
+
+static BOOL CALLBACK AvatarDlgProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam)
+{
+ switch(uMsg)
+ {
+ case WM_INITDIALOG:
+ {
+ AvatarDialogData *data = (struct AvatarDialogData*) lParam;
+ SendMessage(hwnd, WM_SETICON, ICON_BIG, (LPARAM) createDefaultOverlayedIcon(TRUE));
+ SendMessage(hwnd, WM_SETICON, ICON_SMALL, (LPARAM) createDefaultOverlayedIcon(FALSE));
+ if (db_byte_get(NULL, MODULE_NAME, "LogToHistory", AVH_DEF_LOGTOHISTORY))
+ FillAvatarListFromDB(GetDlgItem(hwnd, IDC_AVATARLIST), data->hContact);
+ else if (opts.log_per_contact_folders)
+ FillAvatarListFromFolder(GetDlgItem(hwnd, IDC_AVATARLIST), data->hContact);
+
+ SetWindowLongPtr(hwnd, GWLP_USERDATA, (ULONG_PTR)data->hContact);
+ UpdateAvatarPic(hwnd);
+ CheckDlgButton(hwnd, IDC_LOGUSER, (UINT)db_byte_get(data->hContact, "AvatarHistory", "LogToDisk", BST_INDETERMINATE));
+ CheckDlgButton(hwnd, IDC_POPUPUSER, (UINT)db_byte_get(data->hContact, "AvatarHistory", "AvatarPopups", BST_INDETERMINATE));
+ CheckDlgButton(hwnd, IDC_HISTORYUSER, (UINT)db_byte_get(data->hContact, "AvatarHistory", "LogToHistory", BST_INDETERMINATE));
+ ShowWindow(GetDlgItem(hwnd, IDC_OPENFOLDER), opts.log_per_contact_folders ? SW_SHOW : SW_HIDE);
+ TranslateDialogDefault(hwnd);
+ EnableDisableControls(hwnd);
+ free(data);
+ data = NULL;
+ break;
+ }
+ case WM_CLOSE:
+ {
+ CleanupAvatarPic(hwnd);
+ EndDialog(hwnd, 0);
+ return TRUE;
+ }
+ case WM_DESTROY:
+ {
+ DestroyIcon((HICON)SendMessage(hwnd, WM_SETICON, ICON_BIG, 0));
+ DestroyIcon((HICON)SendMessage(hwnd, WM_SETICON, ICON_SMALL, 0));
+ HWND list = GetDlgItem(hwnd, IDC_AVATARLIST);
+ int count = SendMessage(list, LB_GETCOUNT, 0, 0);
+ for(int i = 0; i < count; i++)
+ delete (ListEntry *) SendMessage(list, LB_GETITEMDATA, i, 0);
+ break;
+ }
+ case WM_CONTEXTMENU:
+ {
+ HWND list = GetDlgItem(hwnd, IDC_AVATARLIST);
+ HWND pic = GetDlgItem(hwnd, IDC_AVATAR);
+ int pos;
+
+ if ((HANDLE) wParam == list)
+ {
+ POINT p;
+ p.x = LOWORD(lParam);
+ p.y = HIWORD(lParam);
+
+ ScreenToClient(list, &p);
+
+ pos = SendMessage(list, LB_ITEMFROMPOINT, 0, MAKELONG(p.x, p.y));
+ if (HIWORD(pos))
+ break;
+ pos = LOWORD(pos);
+
+ int count = SendMessage(list, LB_GETCOUNT, 0, 0);
+ if (pos >= count)
+ break;
+
+ SendMessage(list, LB_SETCURSEL, pos, 0);
+ EnableDisableControls(hwnd);
+ }
+ else if ((HANDLE) wParam == pic)
+ {
+ pos = SendMessage(list, LB_GETCURSEL, 0, 0);
+ if (pos == LB_ERR)
+ break;
+ }
+ else
+ break;
+
+ HMENU menu = LoadMenu(hInst, MAKEINTRESOURCE(IDR_MENU1));
+ HMENU submenu = GetSubMenu(menu, 0);
+ CallService(MS_LANGPACK_TRANSLATEMENU,(WPARAM)submenu,0);
+
+ if (!UpdateAvatarPic(hwnd))
+ {
+ RemoveMenu(submenu, 2, MF_BYPOSITION);
+ RemoveMenu(submenu, 0, MF_BYPOSITION);
+ }
+
+ POINT p;
+ p.x = LOWORD(lParam);
+ p.y = HIWORD(lParam);
+ int ret = TrackPopupMenu(submenu, TPM_TOPALIGN|TPM_LEFTALIGN|TPM_RIGHTBUTTON|TPM_RETURNCMD, p.x, p.y, 0, list, NULL);
+ DestroyMenu(menu);
+
+ switch(ret)
+ {
+ case ID_AVATARLISTPOPUP_SAVEAS:
+ {
+ ListEntry *le = (ListEntry*) SendMessage(list, LB_GETITEMDATA, pos, 0);
+ ShowSaveDialog(hwnd, le->filename);
+ break;
+ }
+ case ID_AVATARLISTPOPUP_DELETE:
+ {
+ ListEntry *le = (ListEntry*) SendMessage(list, LB_GETITEMDATA, pos, 0);
+
+ BOOL blDelete;
+
+ if(le->dbe)
+ blDelete = MessageBox(hwnd, TranslateT("Are you sure you wish to delete this history entry?\nOnly the entry in history will be deleted, bitmap file will be kept!"),
+ TranslateT("Delete avatar log?"), MB_YESNO|MB_ICONWARNING|MB_DEFBUTTON2|MB_SETFOREGROUND|MB_TOPMOST) == IDYES;
+ else
+ blDelete = MessageBox(hwnd, TranslateT("Are you sure you wish to delete this avatar shortcut?\nOnly shortcut will be deleted, bitmap file will be kept!"),
+ TranslateT("Delete avatar log?"), MB_YESNO|MB_ICONWARNING|MB_DEFBUTTON2|MB_SETFOREGROUND|MB_TOPMOST) == IDYES;
+
+ if (blDelete)
+ {
+ HANDLE hContact = (HANDLE) GetWindowLongPtr(hwnd, GWLP_USERDATA);
+
+ if(le->dbe)
+ CallService(MS_DB_EVENT_DELETE, (WPARAM) hContact, (LPARAM) le->dbe);
+ else
+ DeleteFile(le->filelink);
+
+ delete le;
+
+ SendMessage(list, LB_DELETESTRING, pos, 0);
+
+ int count = SendMessage(list, LB_GETCOUNT, 0, 0);
+ if (count > 0)
+ {
+ if (pos >= count)
+ pos = count -1;
+ SendMessage(list, LB_SETCURSEL, pos, 0);
+ }
+
+ UpdateAvatarPic(hwnd);
+ EnableDisableControls(hwnd);
+ }
+ break;
+ }
+ case ID_AVATARLISTPOPUP_DELETE_BOTH:
+ {
+ ListEntry *le = (ListEntry*) SendMessage(list, LB_GETITEMDATA, pos, 0);
+
+ BOOL blDelete;
+
+ if(le->dbe)
+ blDelete = MessageBox(hwnd, TranslateT("Are you sure you wish to delete this archived avatar?\nThis will delete the history entry and the bitmap file.\nWARNING:This can affect more than one entry in history!"),
+ TranslateT("Delete avatar?"), MB_YESNO|MB_ICONWARNING|MB_DEFBUTTON2|MB_SETFOREGROUND|MB_TOPMOST) == IDYES;
+ else
+ blDelete = MessageBox(hwnd, TranslateT("Are you sure you wish to delete this archived avatar?\nThis will delete the shortcut and the bitmap file.\nWARNING:This can affect more than one shortcut!"),
+ TranslateT("Delete avatar?"), MB_YESNO|MB_ICONWARNING|MB_DEFBUTTON2|MB_SETFOREGROUND|MB_TOPMOST) == IDYES;
+
+ if (blDelete)
+ {
+ HANDLE hContact = (HANDLE) GetWindowLongPtr(hwnd, GWLP_USERDATA);
+
+ DeleteFile(le->filename);
+
+ if(le->dbe)
+ CallService(MS_DB_EVENT_DELETE, (WPARAM) hContact, (LPARAM) le->dbe);
+ else
+ DeleteFile(le->filelink);
+
+ delete le;
+
+ SendMessage(list, LB_DELETESTRING, pos, 0);
+
+ int count = SendMessage(list, LB_GETCOUNT, 0, 0);
+ if (count > 0)
+ {
+ if (pos >= count)
+ pos = count -1;
+ SendMessage(list, LB_SETCURSEL, pos, 0);
+ }
+
+ UpdateAvatarPic(hwnd);
+ EnableDisableControls(hwnd);
+ }
+ break;
+ }
+ }
+ break;
+ }
+ case WM_COMMAND:
+ {
+ switch(LOWORD(wParam))
+ {
+ case IDOK:
+ if(HIWORD(wParam) == BN_CLICKED)
+ {
+ HANDLE hContact = (HANDLE) GetWindowLongPtr(hwnd, GWLP_USERDATA);
+
+ if(IsDlgButtonChecked(hwnd, IDC_POPUPUSER) != BST_INDETERMINATE)
+ {
+ db_byte_set(hContact, "AvatarHistory", "AvatarPopups", (BYTE) IsDlgButtonChecked(hwnd, IDC_POPUPUSER));
+ }
+ else
+ {
+ DBDeleteContactSetting(hContact, "AvatarHistory", "AvatarPopups");
+ }
+
+ if(IsDlgButtonChecked(hwnd, IDC_LOGUSER) != BST_INDETERMINATE)
+ {
+ db_byte_set(hContact, "AvatarHistory", "LogToDisk", (BYTE) IsDlgButtonChecked(hwnd, IDC_LOGUSER));
+ }
+ else
+ {
+ DBDeleteContactSetting(hContact, "AvatarHistory", "LogToDisk");
+ }
+
+ if(IsDlgButtonChecked(hwnd, IDC_HISTORYUSER) != BST_INDETERMINATE)
+ {
+ db_byte_set(hContact, "AvatarHistory", "LogToHistory", (BYTE) IsDlgButtonChecked(hwnd, IDC_HISTORYUSER));
+ }
+ else
+ {
+ DBDeleteContactSetting(hContact, "AvatarHistory", "LogToHistory");
+ }
+
+ CleanupAvatarPic(hwnd);
+ EndDialog(hwnd, 0);
+ return TRUE;
+ }
+ break;
+ case IDC_AVATARLIST:
+ if(HIWORD(wParam) == LBN_SELCHANGE)
+ {
+ UpdateAvatarPic(hwnd);
+ EnableDisableControls(hwnd);
+ return TRUE;
+ }
+ break;
+ case IDC_OPENFOLDER:
+ if(HIWORD(wParam) == BN_CLICKED)
+ {
+ if (opts.log_per_contact_folders)
+ {
+ TCHAR avfolder[MAX_PATH];
+ HWND list = GetDlgItem(hwnd, IDC_AVATARLIST);
+ HANDLE hContact = (HANDLE)GetWindowLongPtr(hwnd, GWLP_USERDATA);
+ GetContactFolder(avfolder, hContact);
+ ShellExecute(NULL, db_byte_get(NULL, "AvatarHistory", "OpenFolderMethod", 0) ? _T("explore") : _T("open"), avfolder, NULL, NULL, SW_SHOWNORMAL);
+ return TRUE;
+ }
+ }
+ break;
+ case IDC_NEXT:
+ if(HIWORD(wParam) == BN_CLICKED)
+ {
+ HWND list = GetDlgItem(hwnd, IDC_AVATARLIST);
+ SendMessage(list, LB_SETCURSEL, SendMessage(list, LB_GETCURSEL, 0, 0) +1, 0);
+ UpdateAvatarPic(hwnd);
+ EnableDisableControls(hwnd);
+ return TRUE;
+ }
+ break;
+ case IDC_BACK:
+ if(HIWORD(wParam) == BN_CLICKED)
+ {
+ HWND list = GetDlgItem(hwnd, IDC_AVATARLIST);
+ int cursel = SendMessage(list, LB_GETCURSEL, 0, 0);
+ if (cursel == LB_ERR)
+ SendMessage(list, LB_SETCURSEL, SendMessage(list, LB_GETCOUNT, 0, 0) -1, 0);
+ else
+ SendMessage(list, LB_SETCURSEL, cursel -1, 0);
+ UpdateAvatarPic(hwnd);
+ EnableDisableControls(hwnd);
+ return TRUE;
+ }
+ break;
+ }
+ break;
+ }
+ }
+ return FALSE;
+}
+
+
+
+int FillAvatarListFromFolder(HWND list, HANDLE hContact)
+{
+ int max_pos = 0;
+ TCHAR dir[MAX_PATH], path[MAX_PATH], lnk[MAX_PATH];
+ WIN32_FIND_DATA finddata;
+
+ GetContactFolder(dir, hContact);
+ mir_sntprintf(path, MAX_PATH, _T("%s\\*.lnk"), dir);
+
+ HANDLE hFind = FindFirstFile(path, &finddata);
+ if (hFind == INVALID_HANDLE_VALUE)
+ return 0;
+
+ do
+ {
+ if(finddata.cFileName[0] != '.')
+ {
+ mir_sntprintf(lnk, MAX_PATH, _T("%s\\%s"), dir, finddata.cFileName);
+ if (ResolveShortcut(lnk, path))
+ {
+ // Add to list
+ ListEntry *le = new ListEntry();
+ le->filename = mir_tstrdup(path);
+ le->filelink = mir_tstrdup(lnk);
+
+ TCHAR *p = _tcschr(finddata.cFileName, _T('.'));
+ if (p != NULL)
+ p[0] = _T('\0');
+ max_pos = SendMessage(list, LB_ADDSTRING, 0, (LPARAM) finddata.cFileName);
+ SendMessage(list, LB_SETITEMDATA, max_pos, (LPARAM) le);
+ }
+ }
+ } while(FindNextFile(hFind, &finddata));
+ FindClose(hFind);
+ SendMessage(list, LB_SETCURSEL, max_pos, 0); // Set to first item
+ return 0;
+}
+
+
+
+int FillAvatarListFromDB(HWND list, HANDLE hContact)
+{
+ int max_pos = 0;
+ BYTE blob[2048];
+ HANDLE dbe = (HANDLE) CallService(MS_DB_EVENT_FINDFIRST, (WPARAM) hContact, 0);
+ while(dbe != NULL)
+ {
+ DBEVENTINFO dbei = {0};
+ dbei.cbSize = sizeof(dbei);
+ dbei.cbBlob = sizeof(blob);
+ dbei.pBlob = blob;
+ if (CallService(MS_DB_EVENT_GET, (WPARAM) dbe, (LPARAM) &dbei) == 0
+ && dbei.eventType == EVENTTYPE_AVATAR_CHANGE)
+ {
+
+ // Get last char from blob
+ int i = dbei.cbBlob - 2;
+ for(; i >= 0 && dbei.pBlob[i] != 0; i--) ;
+
+ if (i != (int) dbei.cbBlob - 2 && i >= 0)
+ {
+ // Oki, found one
+
+ // Get time
+ TCHAR date[64];
+ DBTIMETOSTRINGT tts = {0};
+ tts.szFormat = _T("d s");
+ tts.szDest = date;
+ tts.cbDest = sizeof(date);
+ CallService(MS_DB_TIME_TIMESTAMPTOSTRINGT, (WPARAM) dbei.timestamp, (LPARAM) &tts);
+
+ // Get file in disk
+ char path[MAX_PATH] = "";
+ PathToAbsolute((char *) &dbei.pBlob[i+1], path);
+ TCHAR *filename = mir_a2t(path);
+
+ // Add to list
+ ListEntry *le = new ListEntry();
+ le->dbe = dbe;
+ le->filename = filename;
+
+ max_pos = SendMessage(list,LB_ADDSTRING, 0, (LPARAM) date);
+ SendMessage(list, LB_SETITEMDATA, max_pos, (LPARAM) le);
+ }
+ }
+
+ dbe = (HANDLE) CallService(MS_DB_EVENT_FINDNEXT, (WPARAM) dbe, 0);
+ }
+
+ SendMessage(list, LB_SETCURSEL, max_pos, 0); // Set to first item
+ return 0;
+}
+
+BOOL UpdateAvatarPic(HWND hwnd)
+{
+ HWND hwndpic = GetDlgItem(hwnd, IDC_AVATAR);
+ if (!hwnd || !hwndpic)
+ return -1;
+
+ HWND list = GetDlgItem(hwnd, IDC_AVATARLIST);
+ TCHAR *filename = GetCurrentSelFile(list);
+
+ HBITMAP avpic = (HBITMAP) CallService(MS_IMG_LOAD, (WPARAM) filename, IMGL_TCHAR);
+
+ BOOL found_image = (avpic != NULL);
+
+ avpic = (HBITMAP)SendMessage(hwndpic, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM)avpic);
+ if (avpic)
+ DeleteObject(avpic);
+
+ return found_image;
+}
+
+int CleanupAvatarPic(HWND hwnd)
+{
+ HWND hwndpic = GetDlgItem(hwnd, IDC_AVATAR);
+ if (!hwnd || !hwndpic)
+ return -1;
+
+ HBITMAP avpic = (HBITMAP)SendMessage(hwndpic, STM_GETIMAGE, 0, 0);
+ if(avpic)
+ DeleteObject(avpic);
+ return 0;
+}
+
+int PreBuildContactMenu(WPARAM wParam,LPARAM lParam)
+{
+ CLISTMENUITEM clmi = {0};
+ clmi.cbSize = sizeof(clmi);
+ clmi.flags = CMIM_FLAGS;
+
+ char *proto = (char*) CallService(MS_PROTO_GETCONTACTBASEPROTO, wParam, 0);
+ if (!ProtocolEnabled(proto))
+ clmi.flags |= CMIF_HIDDEN;
+
+ CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM) hMenu, (LPARAM) &clmi);
+
+ return 0;
+}
+
+void InitMenuItem()
+{
+ hHooks[5] = HookEvent(ME_CLIST_PREBUILDCONTACTMENU, PreBuildContactMenu);
+
+ CLISTMENUITEM mi = {0};
+
+ hServices[2] = CreateServiceFunction("AvatarHistory/ShowDialog", ShowDialogSvc);
+
+ mi.cbSize = sizeof(mi);
+ mi.pszName = Translate("View Avatar History");
+ mi.position = 1000090010;
+ mi.hIcon = createDefaultOverlayedIcon(FALSE);
+ mi.pszService = "AvatarHistory/ShowDialog";
+ hMenu = (HANDLE)CallService(MS_CLIST_ADDCONTACTMENUITEM, 0, (LPARAM)&mi);
+ DestroyIcon(mi.hIcon);
+}
+
+static int ShowDialogSvc(WPARAM wParam, LPARAM lParam)
+{
+ OpenAvatarDialog((HANDLE)wParam, (char*)lParam);
+ return 0;
+}
+
+TCHAR* GetCurrentSelFile(HWND list)
+{
+ int cursel = SendMessage(list, LB_GETCURSEL, 0, 0);
+ if (cursel > -1)
+ return ((ListEntry*) SendMessage(list, LB_GETITEMDATA, cursel, 0))->filename;
+ else
+ return NULL;
+}
+
+int ShowSaveDialog(HWND hwnd, TCHAR* fn)
+{
+ TCHAR initdir[MAX_PATH] = _T(".");
+ char filter[MAX_PATH];
+ TCHAR file[MAX_PATH];
+ OPENFILENAME ofn;
+ ZeroMemory(&ofn, sizeof(OPENFILENAME));
+ MyDBGetStringT(NULL, "AvatarHistory", "SavedAvatarFolder", initdir, MAX_PATH);
+ ofn.lStructSize = sizeof(OPENFILENAME);
+ ofn.hwndOwner = hwnd;
+ ofn.hInstance = hInst;
+
+ // Miranda dont have the unicode version of this servicce
+ CallService(MS_UTILS_GETBITMAPFILTERSTRINGS, MAX_PATH, (LPARAM)filter);
+ INPLACE_CHAR_TO_TCHAR(filterT, MAX_PATH, filter);
+ ofn.lpstrFilter = filterT;
+
+ ofn.nFilterIndex = 1;
+ lstrcpy(file, _tcsrchr(fn, '\\')+1);
+ ofn.lpstrFile = file;
+
+ ofn.nMaxFile = MAX_PATH;
+ ofn.Flags = OFN_PATHMUSTEXIST;
+ ofn.lpstrDefExt = _tcsrchr(fn, '.')+1;
+ ofn.lpstrInitialDir = initdir[0]?initdir:NULL;
+ if(GetSaveFileName(&ofn))
+ CopyFile(fn, file, FALSE);
+ return 0;
+}
+
+TCHAR* MyDBGetStringT(HANDLE hContact, char* module, char* setting, TCHAR* out, size_t len)
+{
+ DBCONTACTGETSETTING dbgcs;
+ DBVARIANT dbv;
+ dbgcs.szModule = module;
+ dbgcs.szSetting = setting;
+ dbgcs.pValue = &dbv;
+
+#ifdef UNICODE
+ dbv.type = DBVT_WCHAR;
+#else
+ dbv.type = DBVT_ASCIIZ;
+#endif
+ dbv.ptszVal = out;
+ dbv.cchVal = len;
+ if (CallService(MS_DB_CONTACT_GETSETTINGSTATIC, (WPARAM)hContact, (LPARAM)&dbgcs) != 0)
+ return NULL;
+ else
+ return out;
+}
+
+char * MyDBGetString(HANDLE hContact, char* module, char* setting, char * out, size_t len)
+{
+ DBCONTACTGETSETTING dbgcs;
+ DBVARIANT dbv;
+ dbgcs.szModule = module;
+ dbgcs.szSetting = setting;
+ dbgcs.pValue = &dbv;
+ dbv.type = DBVT_ASCIIZ;
+ dbv.pszVal = out;
+ dbv.cchVal = len;
+ if (CallService(MS_DB_CONTACT_GETSETTINGSTATIC, (WPARAM)hContact, (LPARAM)&dbgcs) != 0)
+ {
+ out[len-1] = '\0';
+ return NULL;
+ }
+ else
+ {
+ out[len-1] = '\0';
+ return out;
+ }
+}
diff --git a/Plugins/avatarhistory/AvatarHistory.cpp b/Plugins/avatarhistory/AvatarHistory.cpp
new file mode 100644
index 0000000..2072627
--- /dev/null
+++ b/Plugins/avatarhistory/AvatarHistory.cpp
@@ -0,0 +1,1053 @@
+/*
+Avatar History Plugin
+---------
+
+ This plugin uses the event provided by Avatar Service to
+ automatically back up contacts' avatars when they change.
+ Copyright (C) 2006 Matthew Wild - Email: mwild1@gmail.com
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+*/
+#include "AvatarHistory.h"
+
+// #define DBGPOPUPS
+
+HINSTANCE hInst;
+PLUGINLINK *pluginLink;
+DWORD mirVer;
+
+HANDLE hHooks[6] = {0};
+HANDLE hServices[3] = {0};
+
+HANDLE hFolder = NULL;
+
+char *metacontacts_proto = NULL;
+
+char profilePath[MAX_PATH]; // database profile path (read at startup only)
+TCHAR basedir[MAX_PATH];
+
+static int ModulesLoaded(WPARAM wParam, LPARAM lParam);
+static int PreShutdown(WPARAM wParam, LPARAM lParam);
+static int AvatarChanged(WPARAM wParam, LPARAM lParam);
+int OptInit(WPARAM wParam,LPARAM lParam);
+
+TCHAR * GetHistoryFolder(TCHAR *fn);
+TCHAR * GetProtocolFolder(TCHAR *fn, char *proto);
+TCHAR * GetContactFolder(TCHAR *fn, HANDLE hContact);
+TCHAR * GetOldStyleAvatarName(TCHAR *fn, HANDLE hContact);
+
+void InitFolders();
+void InitMenuItem();
+
+void * GetHistoryEventText(HANDLE hContact, HANDLE hDbEvent, DBEVENTINFO *dbe, int format);
+
+// Services
+static int IsEnabled(WPARAM wParam, LPARAM lParam);
+static int GetCachedAvatar(WPARAM wParam, LPARAM lParam);
+TCHAR * GetCachedAvatar(char *proto, char *hash);
+BOOL CreateShortcut(TCHAR *file, TCHAR *shortcut);
+
+#ifdef DBGPOPUPS
+#define ShowDebugPopup ShowPopup
+#else
+#define ShowDebugPopup
+#endif
+
+
+PLUGININFOEX pluginInfo={
+ sizeof(PLUGININFOEX),
+#ifdef UNICODE
+ "Avatar History (Unicode)",
+#else
+ "Avatar History",
+#endif
+ PLUGIN_MAKE_VERSION(0,0,2,10),
+ "This plugin keeps backups of all your contacts' avatar changes and/or shows popups",
+ "Matthew Wild (MattJ), Ricardo Pescuma Domenecci",
+ "mwild1@gmail.com",
+ "© 2006-2007 Matthew Wild, Ricardo Pescuma Domenecci",
+ "http://pescuma.mirandaim.ru/miranda/avatarhist",
+ UNICODE_AWARE,
+ 0, //doesn't replace anything built-in
+#ifdef UNICODE
+ { 0xdbe8c990, 0x7aa0, 0x458d, { 0xba, 0xb7, 0x33, 0xeb, 0x7, 0x23, 0x8e, 0x71 } } // {DBE8C990-7AA0-458d-BAB7-33EB07238E71}
+#else
+ { 0x4079923c, 0x8aa1, 0x4a2e, { 0x95, 0x8b, 0x9d, 0xc, 0xd0, 0xe8, 0x2e, 0xb2 } } // {4079923C-8AA1-4a2e-958B-9D0CD0E82EB2}
+#endif
+};
+
+extern "C" BOOL WINAPI DllMain(HINSTANCE hinstDLL,DWORD fdwReason,LPVOID lpvReserved)
+{
+ hInst = hinstDLL;
+ return TRUE;
+}
+
+extern "C" __declspec(dllexport) PLUGININFO* MirandaPluginInfo(DWORD mirandaVersion)
+{
+ mirVer = mirandaVersion;
+ pluginInfo.cbSize = sizeof(PLUGININFO);
+ return (PLUGININFO*) &pluginInfo;
+}
+
+extern "C" __declspec(dllexport) PLUGININFOEX* MirandaPluginInfoEx(DWORD mirandaVersion)
+{
+ mirVer = mirandaVersion;
+ pluginInfo.cbSize = sizeof(PLUGININFOEX);
+ return &pluginInfo;
+}
+
+static const MUUID interfaces[] = { MIID_AVATAR_CHANGE_LOGGER, MIID_AVATAR_CHANGE_NOTIFIER, MIID_LAST };
+extern "C" __declspec(dllexport) const MUUID* MirandaPluginInterfaces(void)
+{
+ return interfaces;
+}
+
+static BOOL CALLBACK FirstRunDlgProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam)
+{
+ switch(uMsg)
+ {
+ case WM_INITDIALOG:
+ {
+ SendMessage(hwnd, WM_SETICON, ICON_BIG, (LPARAM) createDefaultOverlayedIcon(TRUE));
+ SendMessage(hwnd, WM_SETICON, ICON_SMALL, (LPARAM) createDefaultOverlayedIcon(FALSE));
+ TranslateDialogDefault(hwnd);
+
+ CheckDlgButton(hwnd, IDC_MIR_PROTO, BST_CHECKED);
+ break;
+ }
+ case WM_COMMAND:
+ {
+ switch(LOWORD(wParam))
+ {
+ case IDOK:
+ {
+ int ret = 0;
+
+ if (IsDlgButtonChecked(hwnd, IDC_MIR_SAME))
+ ret = IDC_MIR_SAME;
+ else if (IsDlgButtonChecked(hwnd, IDC_MIR_PROTO))
+ ret = IDC_MIR_PROTO;
+ else if (IsDlgButtonChecked(hwnd, IDC_MIR_SHORT))
+ ret = IDC_MIR_SHORT;
+ else if (IsDlgButtonChecked(hwnd, IDC_SHORT))
+ ret = IDC_SHORT;
+ else if (IsDlgButtonChecked(hwnd, IDC_DUP))
+ ret = IDC_DUP;
+
+ EndDialog(hwnd, ret);
+ return TRUE;
+ }
+ }
+ break;
+ }
+ case WM_CLOSE:
+ {
+ EndDialog(hwnd, 0);
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+extern "C" int __declspec(dllexport) Load(PLUGINLINK *link)
+{
+ pluginLink=link;
+
+ init_mir_malloc();
+
+ // Is first run?
+ if (DBGetContactSettingByte(NULL, MODULE_NAME, "FirstRun", 1))
+ {
+ // Show dialog
+ int ret = DialogBoxParam(hInst, MAKEINTRESOURCE(IDD_FIRST_RUN), NULL, FirstRunDlgProc, 0);
+ if (ret == 0)
+ return -1;
+
+ // Write settings
+
+ DBWriteContactSettingByte(NULL, MODULE_NAME, "LogToDisk", 1);
+
+ if (ret == IDC_MIR_SAME)
+ DBWriteContactSettingByte(NULL, MODULE_NAME, "LogKeepSameFolder", 1);
+ else
+ DBWriteContactSettingByte(NULL, MODULE_NAME, "LogKeepSameFolder", 0);
+
+ if (ret == IDC_MIR_SHORT || ret == IDC_SHORT || ret == IDC_DUP)
+ DBWriteContactSettingByte(NULL, MODULE_NAME, "LogPerContactFolders", 1);
+ else
+ DBWriteContactSettingByte(NULL, MODULE_NAME, "LogPerContactFolders", 0);
+
+ if (ret == IDC_DUP)
+ DBWriteContactSettingByte(NULL, MODULE_NAME, "StoreAsHash", 0);
+ else
+ DBWriteContactSettingByte(NULL, MODULE_NAME, "StoreAsHash", 1);
+
+ if (ret == IDC_MIR_SAME || ret == IDC_MIR_PROTO || ret == IDC_MIR_SHORT)
+ DBWriteContactSettingByte(NULL, MODULE_NAME, "LogToHistory", 1);
+ else
+ DBWriteContactSettingByte(NULL, MODULE_NAME, "LogToHistory", 0);
+
+ DBWriteContactSettingByte(NULL, MODULE_NAME, "FirstRun", 0);
+ }
+
+ LoadOptions();
+
+ hHooks[0] = HookEvent(ME_SYSTEM_MODULESLOADED,ModulesLoaded);
+ hHooks[1] = HookEvent(ME_SYSTEM_PRESHUTDOWN, PreShutdown);
+
+ hServices[0] = CreateServiceFunction(MS_AVATARHISTORY_ENABLED, IsEnabled);
+ hServices[1] = CreateServiceFunction(MS_AVATARHISTORY_GET_CACHED_AVATAR, GetCachedAvatar);
+
+ if(CallService(MS_DB_GETPROFILEPATH, MAX_PATH, (LPARAM)profilePath) != 0)
+ strcpy(profilePath, "."); // Failed, use current dir
+ _strlwr(profilePath);
+
+ return 0;
+}
+
+static int ModulesLoaded(WPARAM wParam, LPARAM lParam)
+{
+#ifdef UNICODE
+ mir_sntprintf(basedir, MAX_REGS(basedir), _T("%S\\Avatars History"), profilePath);
+#else
+ mir_sntprintf(basedir, MAX_REGS(basedir), _T("%s\\Avatars History"), profilePath);
+#endif
+
+ hFolder = (HANDLE)FoldersRegisterCustomPathT(Translate("Avatars"), Translate("Avatar History"),
+ _T(PROFILE_PATH) _T("\\") _T(CURRENT_PROFILE) _T("\\Avatars History"));
+
+ hHooks[2] = HookEvent(ME_AV_CONTACTAVATARCHANGED, AvatarChanged);
+ hHooks[3] = HookEvent(ME_OPT_INITIALISE, OptInit);
+ SetupIcoLib();
+ InitMenuItem();
+ InitPopups();
+
+ if (ServiceExists(MS_MC_GETPROTOCOLNAME))
+ metacontacts_proto = (char *) CallService(MS_MC_GETPROTOCOLNAME, 0, 0);
+
+ // updater plugin support
+ if(ServiceExists(MS_UPDATE_REGISTER))
+ {
+ Update upd = {0};
+ char szCurrentVersion[30];
+
+ upd.cbSize = sizeof(upd);
+ upd.szComponentName = pluginInfo.shortName;
+
+ upd.szUpdateURL = UPDATER_AUTOREGISTER;
+
+ upd.szBetaVersionURL = "http://pescuma.mirandaim.ru/miranda/avatarhist_version.txt";
+ upd.szBetaChangelogURL = "http://pescuma.mirandaim.ru/miranda/avatarhist#Changelog";
+ upd.pbBetaVersionPrefix = (BYTE *)"Avatar History ";
+ upd.cpbBetaVersionPrefix = strlen((char *)upd.pbBetaVersionPrefix);
+#ifdef UNICODE
+ upd.szBetaUpdateURL = "http://pescuma.mirandaim.ru/miranda/avatarhistW.zip";
+#else
+ upd.szBetaUpdateURL = "http://pescuma.mirandaim.ru/miranda/avatarhist.zip";
+#endif
+
+ upd.pbVersion = (BYTE *)CreateVersionStringPlugin((PLUGININFO*) &pluginInfo, szCurrentVersion);
+ upd.cpbVersion = strlen((char *)upd.pbVersion);
+
+ CallService(MS_UPDATE_REGISTER, 0, (LPARAM)&upd);
+ }
+
+ if (DBGetContactSettingByte(NULL, MODULE_NAME, "LogToHistory", AVH_DEF_LOGTOHISTORY))
+ {
+ char *templates[] = { "Avatar change\nchanged his/her avatar",
+ "Avatar removal\nremoved his/her avatar" };
+ HICON hIcon = createDefaultOverlayedIcon(FALSE);
+ HistoryEvents_RegisterWithTemplates(MODULE_NAME, "avatarchange", "Avatar change", EVENTTYPE_AVATAR_CHANGE, hIcon,
+ HISTORYEVENTS_FORMAT_CHAR | HISTORYEVENTS_FORMAT_WCHAR | HISTORYEVENTS_FORMAT_RICH_TEXT,
+ HISTORYEVENTS_FLAG_SHOW_IM_SRMM | HISTORYEVENTS_FLAG_EXPECT_CONTACT_NAME_BEFORE,
+ GetHistoryEventText, templates, MAX_REGS(templates));
+ DestroyIcon(hIcon);
+ }
+
+ return 0;
+}
+
+static int PreShutdown(WPARAM wParam, LPARAM lParam)
+{
+ int i;
+
+ for (i = 0; i < MAX_REGS(hHooks); i++)
+ UnhookEvent(hHooks[i]);
+
+ for (i = 0; i < MAX_REGS(hServices); i++)
+ DestroyServiceFunction(hServices[i]);
+
+ return 0;
+}
+
+BOOL ProtocolEnabled(const char *proto)
+{
+ if (proto == NULL)
+ return FALSE;
+
+ char setting[256];
+ mir_snprintf(setting, sizeof(setting), "%sEnabled", proto);
+ return (BOOL) DBGetContactSettingByte(NULL, MODULE_NAME, setting, TRUE);
+}
+
+
+BOOL ContactEnabled(HANDLE hContact, char *setting, int def)
+{
+ if (hContact == NULL)
+ return FALSE;
+
+ char *proto = (char *) CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM) hContact, 0);
+ if (!ProtocolEnabled(proto))
+ return FALSE;
+
+ BYTE globpref = db_byte_get(NULL, MODULE_NAME, setting, def);
+ BYTE userpref = db_byte_get(hContact, MODULE_NAME, setting, BST_INDETERMINATE);
+
+ return (globpref && userpref == BST_INDETERMINATE) || userpref == BST_CHECKED;
+}
+
+// Returns true if the unicode buffer only contains 7-bit characters.
+BOOL IsUnicodeAscii(const WCHAR * pBuffer, int nSize)
+{
+ BOOL bResult = TRUE;
+ int nIndex;
+
+ for (nIndex = 0; nIndex < nSize; nIndex++) {
+ if (pBuffer[nIndex] > 0x7F) {
+ bResult = FALSE;
+ break;
+ }
+ }
+ return bResult;
+}
+
+
+int PathIsAbsolute(const char *path)
+{
+ if (!path || !(strlen(path) > 2))
+ return 0;
+ if ((path[1]==':'&&path[2]=='\\')||(path[0]=='\\'&&path[1]=='\\')) return 1;
+ return 0;
+}
+
+int PathToRelative(const char *pSrc, char *pOut)
+{
+ if (!pSrc||!strlen(pSrc)||strlen(pSrc)>MAX_PATH) return 0;
+ if (!PathIsAbsolute(pSrc)) {
+ mir_snprintf(pOut, MAX_PATH, "%s", pSrc);
+ return strlen(pOut);
+ }
+ else {
+ char szTmp[MAX_PATH];
+ mir_snprintf(szTmp, MAX_REGS(szTmp), "%s", pSrc);
+ _strlwr(szTmp);
+ if (strstr(szTmp, profilePath)) {
+ mir_snprintf(pOut, MAX_PATH, "%s", pSrc + strlen(profilePath) + 1);
+ return strlen(pOut);
+ }
+ else {
+ mir_snprintf(pOut, MAX_PATH, "%s", pSrc);
+ return strlen(pOut);
+ }
+ }
+
+ return 0;
+}
+
+int PathToAbsolute(char *pSrc, char *pOut)
+{
+ if (!pSrc||!strlen(pSrc)||strlen(pSrc)>MAX_PATH)
+ return 0;
+ if (PathIsAbsolute(pSrc)||!isalnum(pSrc[0]))
+ {
+ mir_snprintf(pOut, MAX_PATH, "%s", pSrc);
+ return 1;
+ }
+ else
+ {
+ mir_snprintf(pOut, MAX_PATH, "%s\\%s", profilePath, pSrc);
+ return 1;
+ }
+}
+
+void ConvertToFilename(char *str, size_t size) {
+ for(size_t i = 0; i < size && str[i] != '\0'; i++) {
+ switch(str[i]) {
+ case '/':
+ case '\\':
+ case ':':
+ case '*':
+ case '?':
+ case '"':
+ case '<':
+ case '>':
+ case '|':
+ case '.':
+ str[i] = '_';
+ }
+ }
+}
+
+
+#ifdef UNICODE
+
+#define CS "%S"
+
+#else
+
+#define CS "%s"
+
+#endif
+
+
+TCHAR * GetExtension(TCHAR *file)
+{
+ TCHAR *ext = _tcsrchr(file, _T('.'));
+ if (ext != NULL)
+ ext++;
+ else
+ ext = _T("");
+
+ return ext;
+}
+
+
+void CreateOldStyleShortcut(HANDLE hContact, TCHAR *history_filename)
+{
+ TCHAR shortcut[MAX_PATH] = _T("");
+
+ GetOldStyleAvatarName(shortcut, hContact);
+
+ mir_sntprintf(shortcut, MAX_REGS(shortcut), _T("%s.%s.lnk"), shortcut,
+ GetExtension(history_filename));
+
+ if (!CreateShortcut(history_filename, shortcut))
+ {
+ ShowPopup(hContact, _T("Avatar History"), _T("Unable to create shortcut"));
+ }
+ else
+ {
+ ShowDebugPopup(hContact, _T("AVH Debug"), _T("Shortcut created successfully"));
+ }
+}
+
+
+int CopyImageFile(TCHAR *old_file, TCHAR *new_file)
+{
+ TCHAR *ext = GetExtension(old_file);
+
+ if (lstrcmpi(ext, _T("bmp")) == 0
+ && ServiceExists(MS_IMG_SAVE))
+ {
+ // Store as PNG
+ mir_sntprintf(new_file, MAX_PATH, _T("%s.png"), new_file);
+
+ IMGSRVC_INFO ii = {0};
+ ii.cbSize = sizeof(ii);
+#ifdef UNICODE
+ ii.wszName = new_file;
+#else
+ ii.szName = new_file;
+#endif
+ ii.hbm = (HBITMAP) CallService(MS_IMG_LOAD, (WPARAM) old_file, IMGL_TCHAR);
+ ii.dwMask = IMGI_HBITMAP;
+ ii.fif = FIF_UNKNOWN;
+ return CallService(MS_IMG_SAVE, (WPARAM) &ii, IMGL_TCHAR) != 1;
+ }
+ else
+ {
+ mir_sntprintf(new_file, MAX_PATH, _T("%s.%s"), new_file, ext);
+
+ return !CopyFile(old_file, new_file, TRUE);
+ }
+}
+
+// fired when the contacts avatar changes
+// wParam = hContact
+// lParam = struct avatarCacheEntry *cacheEntry
+// the event CAN pass a NULL pointer in lParam which means that the avatar has changed,
+// but is no longer valid (happens, when a contact removes his avatar, for example).
+// DONT DESTROY the bitmap handle passed in the struct avatarCacheEntry *
+//
+// It is also possible that this event passes 0 as wParam (hContact), in which case,
+// a protocol picture (pseudo - avatar) has been changed.
+static int AvatarChanged(WPARAM wParam, LPARAM lParam)
+{
+ HANDLE hContact = (HANDLE)wParam;
+ CONTACTAVATARCHANGEDNOTIFICATION* avatar = (CONTACTAVATARCHANGEDNOTIFICATION*)lParam;
+
+ if (hContact == NULL)
+ {
+ ShowDebugPopup(NULL, _T("AVH Debug"), _T("Invalid contact/avatar... skipping"));
+ return 0;
+ }
+
+ char *proto = (char *) CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM) hContact, 0);
+ if (proto == NULL)
+ {
+ ShowDebugPopup(NULL, _T("AVH Debug"), _T("Invalid protocol... skipping"));
+ return 0;
+ }
+ else if (metacontacts_proto != NULL && strcmp(metacontacts_proto, proto) == 0)
+ {
+ ShowDebugPopup(NULL, _T("AVH Debug"), _T("Ignoring metacontacts notification"));
+ return 0;
+ }
+
+ BOOL removed = (avatar == NULL && DBGetContactSettingWord(hContact, "ContactPhoto", "Format", 0) == 0);
+
+ char oldhash[1024] = "";
+ char * ret = MyDBGetString(hContact, "AvatarHistory", "AvatarHash", oldhash, sizeof(oldhash));
+
+ BOOL first_time = (ret == NULL);
+
+ if(
+ (avatar != NULL && !strcmp(oldhash, avatar->hash)) // Changed it
+ || (avatar == NULL && oldhash[0] == '\0') // Removed it
+ )
+ {
+ ShowDebugPopup(NULL, "AVH Debug", "Hashes are the same... skipping");
+ return 0;
+ }
+
+ if (removed)
+ {
+ db_string_set(hContact, "AvatarHistory", "AvatarHash", "");
+
+ if (!first_time && ContactEnabled(hContact, "AvatarPopups", AVH_DEF_AVPOPUPS) && opts.popup_show_removed)
+ ShowPopup(hContact, NULL, opts.popup_removed);
+
+ if (ContactEnabled(hContact, "LogToHistory", AVH_DEF_LOGTOHISTORY))
+ HistoryEvents_AddToHistorySimple(hContact, EVENTTYPE_AVATAR_CHANGE, 1, DBEF_READ);
+ }
+ else if (avatar == NULL)
+ {
+ if (!strcmp(oldhash, "-"))
+ {
+ ShowDebugPopup(NULL, "AVH Debug", "Changed from a flash avatar to a flash avatar... skipping");
+ return 0;
+ }
+
+ // Is a flash avatar or avs could not load it
+ db_string_set(hContact, "AvatarHistory", "AvatarHash", "-");
+
+ if (!first_time && ContactEnabled(hContact, "AvatarPopups", AVH_DEF_AVPOPUPS) && opts.popup_show_changed)
+ ShowPopup(hContact, NULL, opts.popup_changed);
+
+ if (ContactEnabled(hContact, "LogToHistory", AVH_DEF_LOGTOHISTORY))
+ HistoryEvents_AddToHistorySimple(hContact, EVENTTYPE_AVATAR_CHANGE, 0, DBEF_READ);
+ }
+ else
+ {
+ db_string_set(hContact, "AvatarHistory", "AvatarHash", avatar->hash);
+
+ TCHAR history_filename[MAX_PATH] = _T("");
+
+ if (ContactEnabled(hContact, "LogToDisk", AVH_DEF_LOGTODISK))
+ {
+ if (!opts.log_store_as_hash)
+ {
+ if (opts.log_per_contact_folders)
+ {
+ INPLACE_CHAR_TO_TCHAR(file, 1024, avatar->filename);
+
+ GetOldStyleAvatarName(history_filename, hContact);
+ if (CopyImageFile(file, history_filename))
+ ShowPopup(hContact, _T("Avatar History"), _T("Unable to save avatar"));
+ else
+ ShowDebugPopup(hContact, _T("AVH Debug"), _T("File copied successfully"));
+
+ if (ServiceExists(MS_MC_GETMETACONTACT))
+ {
+ HANDLE hMetaContact = (HANDLE) CallService(MS_MC_GETMETACONTACT, (WPARAM)hContact, 0);
+
+ if (hMetaContact != NULL && ContactEnabled(hMetaContact, "LogToDisk", AVH_DEF_LOGTOHISTORY))
+ {
+ TCHAR filename[MAX_PATH] = _T("");
+
+ GetOldStyleAvatarName(filename, hMetaContact);
+ if (CopyImageFile(file, filename))
+ ShowPopup(hContact, _T("Avatar History"), _T("Unable to save avatar"));
+ else
+ ShowDebugPopup(hContact, _T("AVH Debug"), _T("File copied successfully"));
+ }
+ }
+ }
+ }
+ else
+ {
+ // See if we already have the avatar
+ char hash[128];
+ lstrcpynA(hash, avatar->hash, sizeof(hash));
+ ConvertToFilename(hash, sizeof(hash));
+
+ TCHAR *file = GetCachedAvatar(proto, hash);
+
+ if (file != NULL)
+ {
+ lstrcpyn(history_filename, file, MAX_REGS(history_filename));
+ mir_free(file);
+ }
+ else
+ {
+ // Needed because path in event is char*
+ INPLACE_CHAR_TO_TCHAR(file, MAX_PATH, avatar->filename);
+ TCHAR *ext = GetExtension(file);
+
+ if (opts.log_keep_same_folder)
+ GetHistoryFolder(history_filename);
+ else
+ GetProtocolFolder(history_filename, proto);
+
+ mir_sntprintf(history_filename, MAX_REGS(history_filename),
+ _T("%s\\") _T(CS), history_filename, hash);
+
+ if (CopyImageFile(file, history_filename))
+ ShowPopup(hContact, _T("Avatar History"), _T("Unable to save avatar"));
+ else
+ ShowDebugPopup(hContact, _T("AVH Debug"), _T("File copied successfully"));
+ }
+
+ if (opts.log_per_contact_folders)
+ {
+ CreateOldStyleShortcut(hContact, history_filename);
+
+ if (ServiceExists(MS_MC_GETMETACONTACT))
+ {
+ HANDLE hMetaContact = (HANDLE) CallService(MS_MC_GETMETACONTACT, (WPARAM)hContact, 0);
+
+ if (hMetaContact != NULL && ContactEnabled(hMetaContact, "LogToDisk", AVH_DEF_LOGTOHISTORY))
+ CreateOldStyleShortcut(hMetaContact, history_filename);
+ }
+ }
+ }
+ }
+
+
+ if (!first_time && ContactEnabled(hContact, "AvatarPopups", AVH_DEF_AVPOPUPS) && opts.popup_show_changed)
+ ShowPopup(hContact, NULL, opts.popup_changed);
+
+ if (ContactEnabled(hContact, "LogToHistory", AVH_DEF_LOGTOHISTORY))
+ {
+ char rel_path[MAX_PATH] = "";
+ INPLACE_TCHAR_TO_CHAR(tmp, MAX_PATH, history_filename);
+ PathToRelative(tmp, rel_path);
+ HistoryEvents_AddToHistoryEx(hContact, EVENTTYPE_AVATAR_CHANGE, 0, NULL, 0, (PBYTE) rel_path, strlen(rel_path) + 1, DBEF_READ);
+ }
+ }
+
+ return 0;
+}
+
+extern "C" int __declspec(dllexport) Unload(void)
+{
+ return 0;
+}
+
+
+static int IsEnabled(WPARAM wParam, LPARAM lParam)
+{
+ HANDLE hContact = (HANDLE) wParam;
+ return ContactEnabled(hContact, "LogToDisk", AVH_DEF_LOGTODISK)
+ || ContactEnabled(hContact, "AvatarPopups", AVH_DEF_AVPOPUPS)
+ || ContactEnabled(hContact, "LogToHistory", AVH_DEF_LOGTOHISTORY);
+}
+
+
+/*
+Get cached avatar
+
+wParam: (char *) protocol name
+lParam: (char *) hash
+return: (TCHAR *) NULL if none is found or the path to the avatar. You need to free this string
+ with mir_free.
+*/
+static int GetCachedAvatar(WPARAM wParam, LPARAM lParam)
+{
+ char hash[128];
+ lstrcpynA(hash, (char *) lParam, sizeof(hash));
+ ConvertToFilename(hash, sizeof(hash));
+ return (int) GetCachedAvatar((char *) wParam, hash);
+}
+
+TCHAR * GetCachedAvatar(char *proto, char *hash)
+{
+ TCHAR *ret = NULL;
+ TCHAR file[1024] = _T("");
+ TCHAR search[1024] = _T("");
+ if (opts.log_keep_same_folder)
+ GetHistoryFolder(file);
+ else
+ GetProtocolFolder(file, proto);
+
+ mir_sntprintf(search, MAX_REGS(search), _T("%s\\") _T(CS) _T(".*"), file, hash);
+
+ WIN32_FIND_DATA finddata;
+ HANDLE hFind = FindFirstFile(search, &finddata);
+ if (hFind == INVALID_HANDLE_VALUE)
+ return NULL;
+
+ do
+ {
+ size_t len = lstrlen(finddata.cFileName);
+ if (len > 4
+ && (!lstrcmpi(&finddata.cFileName[len-4], _T(".png"))
+ || !lstrcmpi(&finddata.cFileName[len-4], _T(".bmp"))
+ || !lstrcmpi(&finddata.cFileName[len-4], _T(".gif"))
+ || !lstrcmpi(&finddata.cFileName[len-4], _T(".jpg"))
+ || !lstrcmpi(&finddata.cFileName[len-5], _T(".jpeg"))))
+ {
+ mir_sntprintf(file, MAX_REGS(file), _T("%s\\%s"), file, finddata.cFileName);
+ ret = mir_tstrdup(file);
+ break;
+ }
+ } while(FindNextFile(hFind, &finddata));
+ FindClose(hFind);
+
+ return ret;
+}
+
+
+int GetUIDFromHContact(HANDLE contact, TCHAR* uinout, size_t uinout_len)
+{
+ CONTACTINFO cinfo;
+
+ ZeroMemory(&cinfo,sizeof(CONTACTINFO));
+ cinfo.cbSize = sizeof(CONTACTINFO);
+ cinfo.hContact = contact;
+ cinfo.dwFlag = CNF_UNIQUEID;
+#ifdef UNICODE
+ cinfo.dwFlag |= CNF_UNICODE;
+#endif
+
+ BOOL found = TRUE;
+ if(CallService(MS_CONTACT_GETCONTACTINFO,0,(LPARAM)&cinfo)==0)
+ {
+ if(cinfo.type == CNFT_ASCIIZ)
+ {
+ lstrcpyn(uinout, cinfo.pszVal, uinout_len);
+ // It is up to us to free the string
+ // The catch? We need to use Miranda's free(), not our CRT's :)
+ mir_free(cinfo.pszVal);
+ }
+ else if(cinfo.type == CNFT_DWORD)
+ {
+ _itot(cinfo.dVal,uinout,10);
+ }
+ else if(cinfo.type == CNFT_WORD)
+ {
+ _itot(cinfo.wVal,uinout,10);
+ }
+ else found = FALSE;
+ }
+ else found = FALSE;
+
+ if (!found)
+ {
+#ifdef UNICODE
+ // Try non unicode ver
+ cinfo.dwFlag = CNF_UNIQUEID;
+
+ found = TRUE;
+ if(CallService(MS_CONTACT_GETCONTACTINFO,0,(LPARAM)&cinfo)==0)
+ {
+ if(cinfo.type == CNFT_ASCIIZ)
+ {
+ MultiByteToWideChar(CP_ACP, 0, (char *) cinfo.pszVal, -1, uinout, uinout_len);
+ // It is up to us to free the string
+ // The catch? We need to use Miranda's free(), not our CRT's :)
+ mir_free(cinfo.pszVal);
+ }
+ else if(cinfo.type == CNFT_DWORD)
+ {
+ _itot(cinfo.dVal,uinout,10);
+ }
+ else if(cinfo.type == CNFT_WORD)
+ {
+ _itot(cinfo.wVal,uinout,10);
+ }
+ else found = FALSE;
+ }
+ else found = FALSE;
+
+ if (!found)
+#endif
+ lstrcpy(uinout, TranslateT("Unknown UIN"));
+ }
+ return 0;
+}
+
+
+TCHAR * GetHistoryFolder(TCHAR *fn)
+{
+ FoldersGetCustomPathT(hFolder, fn, MAX_PATH, basedir);
+ CreateDirectory(fn, NULL);
+
+ return fn;
+}
+
+
+TCHAR * GetProtocolFolder(TCHAR *fn, char *proto)
+{
+ GetHistoryFolder(fn);
+
+ if (proto == NULL)
+ proto = Translate("Unknown Protocol");
+
+ mir_sntprintf(fn, MAX_PATH, _T("%s\\") _T(CS), fn, proto);
+ CreateDirectory(fn, NULL);
+
+ return fn;
+}
+
+
+TCHAR * GetContactFolder(TCHAR *fn, HANDLE hContact)
+{
+ TCHAR uin[MAX_PATH];
+
+ char *proto = (char *) CallService(MS_PROTO_GETCONTACTBASEPROTO,(WPARAM) hContact, 0);
+ GetProtocolFolder(fn, proto);
+
+ GetUIDFromHContact(hContact, uin, MAX_REGS(uin));
+ mir_sntprintf(fn, MAX_PATH, _T("%s\\%s"), fn, uin);
+ CreateDirectory(fn, NULL);
+
+#ifdef DBGPOPUPS
+ char log[1024];
+ mir_snprintf(log, MAX_REGS(log), "Path: " CS "\nProto: %s\nUIN: " CS, fn, proto, uin);
+ ShowPopup(NULL, "AVH Debug: GetContactFolder", log);
+#endif
+
+ return fn;
+}
+
+TCHAR * GetOldStyleAvatarName(TCHAR *fn, HANDLE hContact)
+{
+ GetContactFolder(fn, hContact);
+
+ SYSTEMTIME curtime;
+ GetLocalTime(&curtime);
+ mir_sntprintf(fn, MAX_PATH,
+ _T("%s\\%04d-%02d-%02d %02dh%02dm%02ds"), fn,
+ curtime.wYear, curtime.wMonth, curtime.wDay,
+ curtime.wHour, curtime.wMinute, curtime.wSecond);
+ return fn;
+}
+
+
+#include <ShObjIdl.h>
+#include <ShlGuid.h>
+
+BOOL CreateShortcut(TCHAR *file, TCHAR *shortcut)
+{
+ CoInitialize(NULL);
+
+ IShellLink* psl = NULL;
+
+ HRESULT hr = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (void **) &psl);
+
+ if (SUCCEEDED(hr))
+ {
+ psl->SetPath(file);
+
+ IPersistFile* ppf = NULL;
+ hr = psl->QueryInterface(IID_IPersistFile, (void **) &ppf);
+
+ if (SUCCEEDED(hr))
+ {
+#ifdef UNICODE
+ hr = ppf->Save(shortcut, TRUE);
+#else
+ WCHAR tmp[MAX_PATH];
+ MultiByteToWideChar(CP_ACP, 0, shortcut, -1, tmp, MAX_PATH);
+ hr = ppf->Save(tmp, TRUE);
+#endif
+ ppf->Release();
+ }
+
+ psl->Release();
+ }
+
+ return SUCCEEDED(hr);
+}
+
+
+BOOL ResolveShortcut(TCHAR *shortcut, TCHAR *file)
+{
+ CoInitialize(NULL);
+
+ IShellLink* psl = NULL;
+
+ HRESULT hr = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (void **) &psl);
+
+ if (SUCCEEDED(hr))
+ {
+ IPersistFile* ppf = NULL;
+ hr = psl->QueryInterface(IID_IPersistFile, (void **) &ppf);
+
+ if (SUCCEEDED(hr))
+ {
+#ifdef UNICODE
+ hr = ppf->Load(shortcut, STGM_READ);
+#else
+ WCHAR tmp[MAX_PATH];
+ MultiByteToWideChar(CP_ACP, 0, shortcut, -1, tmp, MAX_PATH);
+ hr = ppf->Load(tmp, STGM_READ);
+#endif
+
+ if (SUCCEEDED(hr))
+ {
+ hr = psl->Resolve(NULL, SLR_UPDATE);
+
+ if (SUCCEEDED(hr))
+ {
+ WIN32_FIND_DATA wfd;
+ hr = psl->GetPath(file, MAX_PATH, &wfd, SLGP_RAWPATH);
+ }
+ }
+
+ ppf->Release();
+ }
+ psl->Release();
+ }
+
+ return SUCCEEDED(hr);
+}
+
+
+template<class T>
+void ConvertToRTF(Buffer<char> *buffer, T *line)
+{
+ buffer->append("{\\uc1 ", 6);
+
+ for (; *line; line++)
+ {
+ if (*line == (T)'\r' && line[1] == (T)'\n') {
+ buffer->append("\\line ", 6);
+ line++;
+ }
+ else if (*line == (T)'\n') {
+ buffer->append("\\line ", 6);
+ }
+ else if (*line == (T)'\t') {
+ buffer->append("\\tab ", 5);
+ }
+ else if (*line == (T)'\\' || *line == (T)'{' || *line == (T)'}') {
+ buffer->append('\\');
+ buffer->append((char) *line);
+ }
+ else if (*line < 128) {
+ buffer->append((char) *line);
+ }
+ else
+ buffer->appendPrintf("\\u%d ?", *line);
+ }
+
+ buffer->append('}');
+}
+
+
+void GetRTFFor(Buffer<char> *buffer, HBITMAP hBitmap)
+{
+ BITMAP bmp;
+ GetObject(hBitmap, sizeof(bmp), &bmp);
+
+ DWORD dwLen = bmp.bmWidth * bmp.bmHeight * (bmp.bmBitsPixel / 8);
+ BYTE *p = (BYTE *) malloc(dwLen);
+ if (p == NULL)
+ return;
+
+ dwLen = GetBitmapBits(hBitmap, dwLen, p);
+
+ buffer->appendPrintf("{\\pict\\wbitmap0\\wbmbitspixel%u\\wbmplanes%u\\wbmwidthbytes%u\\picw%u\\pich%u ",
+ bmp.bmBitsPixel, bmp.bmPlanes, bmp.bmWidthBytes, bmp.bmWidth, bmp.bmHeight);
+
+ for (DWORD i = 0; i < dwLen; i++)
+ buffer->appendPrintf("%02X", p[i]);
+
+ buffer->append('}');
+
+
+/*
+ BITMAPINFOHEADER bih = { 0 };
+ HDC hdc = GetDC(NULL);
+ GetDIBits(hdc, hBitmap, 0, bmp.bmHeight, p, (BITMAPINFO *) & bih, DIB_RGB_COLORS);
+
+ buffer->appendPrintf("{\\pict\\wbitmap0\\wbmbitspixel%u\\wbmplanes%u\\wbmwidthbytes%u\\picw%u\\pich%u ",
+ bmp.bmBitsPixel, bmp.bmPlanes, bmp.bmWidthBytes, bmp.bmWidth, bmp.bmHeight);
+
+ DWORD i;
+ for (i = 0; i < sizeof(BITMAPINFOHEADER); i++)
+ buffer->appendPrintf("%02X", ((PBYTE) & bih)[i]);
+
+ for (i = 0; i < dwLen; i++)
+ buffer->appendPrintf("%02X", p[i]);
+
+ buffer->append('}');
+*/
+
+ free(p);
+}
+
+
+void * GetHistoryEventText(HANDLE hContact, HANDLE hDbEvent, DBEVENTINFO *dbe, int format)
+{
+ void *ret;
+
+ if (format & HISTORYEVENTS_FORMAT_CHAR)
+ {
+ ret = DbGetEventTextA(dbe, CP_ACP);
+ }
+ else if (format & HISTORYEVENTS_FORMAT_WCHAR)
+ {
+ ret = DbGetEventTextW(dbe, CP_ACP);
+ }
+ else if (format & HISTORYEVENTS_FORMAT_RICH_TEXT)
+ {
+ Buffer<char> buffer;
+
+ TCHAR *tmp = DbGetEventTextT(dbe, CP_ACP);
+ ConvertToRTF(&buffer, tmp);
+ mir_free(tmp);
+
+ // Load the image
+ size_t i;
+ for(i = dbe->cbBlob-2; i > 0 && dbe->pBlob[i] != 0; i--) ;
+ i++;
+
+ if (dbe->pBlob[i] != 0)
+ {
+ char absFile[MAX_PATH] = "";
+ PathToAbsolute((char *) & dbe->pBlob[i], absFile);
+ HBITMAP hBmp = (HBITMAP) CallService(MS_IMG_LOAD, (WPARAM) absFile, 0);
+
+ if (hBmp != NULL)
+ {
+ buffer.append("\\line ", 7);
+ GetRTFFor(&buffer, hBmp);
+ DeleteObject(hBmp);
+ }
+ }
+
+ buffer.append(' ');
+ buffer.pack();
+ ret = buffer.detach();
+ }
+
+ return ret;
+}
+
diff --git a/Plugins/avatarhistory/AvatarHistory.dsp b/Plugins/avatarhistory/AvatarHistory.dsp
new file mode 100644
index 0000000..b593377
--- /dev/null
+++ b/Plugins/avatarhistory/AvatarHistory.dsp
@@ -0,0 +1,248 @@
+# Microsoft Developer Studio Project File - Name="AvatarHistory" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=AvatarHistory - Win32 Unicode Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "AvatarHistory.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "AvatarHistory.mak" CFG="AvatarHistory - Win32 Unicode Debug"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "AvatarHistory - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "AvatarHistory - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "AvatarHistory - Win32 Unicode Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "AvatarHistory - Win32 Unicode Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "AvatarHistory - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "AvatarHistory_EXPORTS" /YX /FD /c
+# ADD CPP /nologo /MD /W3 /GX /O1 /I "../../include" /I "sdk" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "AvatarHistory_EXPORTS" /FR /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x809 /d "NDEBUG"
+# ADD RSC /l 0x809 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386
+# ADD LINK32 kernel32.lib user32.lib comctl32.lib comdlg32.lib ole32.lib gdi32.lib shell32.lib /nologo /dll /machine:I386 /out:"..\..\bin\release\plugins\avatarhist.dll"
+# SUBTRACT LINK32 /nodefaultlib
+
+!ELSEIF "$(CFG)" == "AvatarHistory - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "AvatarHistory_EXPORTS" /YX /FD /GZ /c
+# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "../../include" /I "sdk" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "AvatarHistory_EXPORTS" /FR /YX /FD /GZ /c
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x809 /d "_DEBUG"
+# ADD RSC /l 0x809 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 kernel32.lib user32.lib comctl32.lib comdlg32.lib ole32.lib gdi32.lib shell32.lib /nologo /dll /debug /machine:I386 /out:"..\..\bin\debug\plugins\avatarhist.dll" /pdbtype:sept
+
+!ELSEIF "$(CFG)" == "AvatarHistory - Win32 Unicode Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "AvatarHistory___Win32_Debug_Unicode"
+# PROP BASE Intermediate_Dir "AvatarHistory___Win32_Debug_Unicode"
+# PROP BASE Ignore_Export_Lib 0
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Unicode_Debug"
+# PROP Intermediate_Dir "Unicode_Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "../../include" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "AvatarHistory_EXPORTS" /FR /YX /FD /GZ /c
+# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "../../include" /I "sdk" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "UNICODE" /D "_UNICODE" /D "_USRDLL" /D "AvatarHistory_EXPORTS" /FR /YX /FD /GZ /c
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x809 /d "_DEBUG"
+# ADD RSC /l 0x809 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 winspool.lib ole32.lib oleaut32.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib comdlg32.lib advapi32.lib shell32.lib uuid.lib comctl32.lib /nologo /dll /debug /machine:I386 /out:"..\..\bin\debug\plugins\avatarhist.dll" /pdbtype:sept
+# ADD LINK32 shell32.lib kernel32.lib user32.lib comctl32.lib comdlg32.lib ole32.lib gdi32.lib /nologo /dll /debug /machine:I386 /out:"..\..\bin\debug unicode\plugins\avatarhistW.dll" /pdbtype:sept
+
+!ELSEIF "$(CFG)" == "AvatarHistory - Win32 Unicode Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "AvatarHistory___Win32_Release_Unicode"
+# PROP BASE Intermediate_Dir "AvatarHistory___Win32_Release_Unicode"
+# PROP BASE Ignore_Export_Lib 0
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Unicode_Release"
+# PROP Intermediate_Dir "Unicode_Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MD /W3 /GX /O1 /I "../../include" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "AvatarHistory_EXPORTS" /FR /YX /FD /c
+# ADD CPP /nologo /MD /W3 /GX /O1 /I "../../include" /I "sdk" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "UNICODE" /D "_UNICODE" /D "_USRDLL" /D "AvatarHistory_EXPORTS" /FR /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x809 /d "NDEBUG"
+# ADD RSC /l 0x809 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib comdlg32.lib advapi32.lib shell32.lib uuid.lib comctl32.lib /nologo /dll /machine:I386 /out:"..\..\bin\release\plugins\avatarhist.dll"
+# SUBTRACT BASE LINK32 /nodefaultlib
+# ADD LINK32 kernel32.lib user32.lib comctl32.lib comdlg32.lib ole32.lib gdi32.lib shell32.lib /nologo /dll /machine:I386 /out:"..\..\bin\release\plugins\avatarhistW.dll"
+# SUBTRACT LINK32 /nodefaultlib
+
+!ENDIF
+
+# Begin Target
+
+# Name "AvatarHistory - Win32 Release"
+# Name "AvatarHistory - Win32 Debug"
+# Name "AvatarHistory - Win32 Unicode Debug"
+# Name "AvatarHistory - Win32 Unicode Release"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=.\AvatarDlg.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\AvatarHistory.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\icolib.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\utils\mir_memory.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\utils\mir_options.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\options.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\popup.cpp
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=.\AvatarHistory.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\m_avatarhist.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\utils\mir_buffer.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\utils\mir_memory.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\utils\mir_options.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\popup.h
+# End Source File
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
+# Begin Source File
+
+SOURCE=.\AvatarHistory.rc
+# End Source File
+# Begin Source File
+
+SOURCE=.\AvatarOverlay.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\history.ico
+# End Source File
+# End Group
+# Begin Group "Docs"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=.\Docs\avatarhist_changelog.txt
+# End Source File
+# Begin Source File
+
+SOURCE=.\Docs\avatarhist_readme.txt
+# End Source File
+# Begin Source File
+
+SOURCE=.\Docs\avatarhist_version.txt
+# End Source File
+# Begin Source File
+
+SOURCE=.\Docs\langpack_avatarhist.txt
+# End Source File
+# End Group
+# End Target
+# End Project
diff --git a/Plugins/avatarhistory/AvatarHistory.dsw b/Plugins/avatarhistory/AvatarHistory.dsw
new file mode 100644
index 0000000..ce9e840
--- /dev/null
+++ b/Plugins/avatarhistory/AvatarHistory.dsw
@@ -0,0 +1,29 @@
+Microsoft Developer Studio Workspace File, Format Version 6.00
+# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
+
+###############################################################################
+
+Project: "AvatarHistory"=.\AvatarHistory.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Global:
+
+Package=<5>
+{{{
+}}}
+
+Package=<3>
+{{{
+}}}
+
+###############################################################################
+
diff --git a/Plugins/avatarhistory/AvatarHistory.h b/Plugins/avatarhistory/AvatarHistory.h
new file mode 100644
index 0000000..bf325d2
--- /dev/null
+++ b/Plugins/avatarhistory/AvatarHistory.h
@@ -0,0 +1,120 @@
+#include <tchar.h>
+#include <windows.h>
+#include <stdio.h>
+#include <time.h>
+
+
+#include <newpluginapi.h>
+#include <m_folders.h>
+#include <m_clist.h>
+#include <m_skin.h>
+#include <m_avatars.h>
+#include <m_database.h>
+#include <m_system.h>
+#include <m_protocols.h>
+#include <m_protosvc.h>
+#include <m_contacts.h>
+#include <m_popup.h>
+#include <m_options.h>
+#include <m_utils.h>
+#include <m_langpack.h>
+#include <m_metacontacts.h>
+#include <m_history.h>
+#include <m_updater.h>
+#include <m_imgsrvc.h>
+#include <m_icolib.h>
+
+// Globals
+extern HINSTANCE hInst;
+extern PLUGINLINK *pluginLink;
+extern HANDLE hMenu;
+extern DWORD mirVer;
+
+#include "resource.h"
+#include "m_avatarhist.h"
+
+#include "../utils/mir_memory.h"
+#include "../utils/mir_buffer.h"
+
+#include "../historyevents/m_historyevents.h"
+
+#define MODULE_NAME "AvatarHistory"
+
+#define AVH_DEF_POPUPFG 0
+#define AVH_DEF_POPUPBG 0x2DB6FF
+#define AVH_DEF_AVPOPUPS 0
+#define AVH_DEF_LOGTODISK 1
+#define AVH_DEF_LOGKEEPSAMEFOLDER 0
+#define AVH_DEF_LOGOLDSTYLE 0
+#define AVH_DEF_LOGTOHISTORY 1
+#define AVH_DEF_DEFPOPUPS 0
+#define AVH_DEF_SHOWMENU 1
+
+#define DEFAULT_TEMPLATE_REMOVED "removed his/her avatar"
+#define DEFAULT_TEMPLATE_CHANGED "changed his/her avatar"
+
+TCHAR * MyDBGetStringT(HANDLE hContact, char* module, char* setting, TCHAR* out, size_t len);
+char * MyDBGetString(HANDLE hContact, char* module, char* setting, char * out, size_t len);
+void LoadOptions();
+
+ // from icolib.cpp
+void SetupIcoLib();
+
+HICON createDefaultOverlayedIcon(BOOL big);
+HICON createProtoOverlayedIcon(HANDLE hContact);
+
+
+#define MAX_REGS(_A_) ( sizeof(_A_) / sizeof(_A_[0]) )
+
+#define POPUP_ACTION_DONOTHING 0
+#define POPUP_ACTION_CLOSEPOPUP 1
+#define POPUP_ACTION_OPENAVATARHISTORY 2
+#define POPUP_ACTION_OPENHISTORY 3
+
+#define POPUP_DELAY_DEFAULT 0
+#define POPUP_DELAY_CUSTOM 1
+#define POPUP_DELAY_PERMANENT 2
+
+
+struct Options {
+ // Log
+ BOOL log_per_contact_folders;
+ BOOL log_keep_same_folder;
+ BOOL log_store_as_hash;
+
+ // Popup
+ BOOL popup_show_changed;
+ TCHAR popup_changed[1024];
+ BOOL popup_show_removed;
+ TCHAR popup_removed[1024];
+ WORD popup_delay_type;
+ WORD popup_timeout;
+ BYTE popup_use_win_colors;
+ BYTE popup_use_default_colors;
+ COLORREF popup_bkg_color;
+ COLORREF popup_text_color;
+ WORD popup_left_click_action;
+ WORD popup_right_click_action;
+};
+
+extern Options opts;
+
+
+#include "popup.h"
+
+
+#ifdef UNICODE
+
+#define TCHAR_TO_CHAR(dest, orig) mir_snprintf(dest, MAX_REGS(dest), "%S", orig)
+#define CHAR_TO_TCHAR(dest, orig) mir_sntprintf(dest, MAX_REGS(dest), "%S", orig)
+
+#else
+
+#define TCHAR_TO_CHAR(dest, orig) lstrcpynA(dest, orig, MAX_REGS(dest))
+#define CHAR_TO_TCHAR(dest, orig) lstrcpynA(dest, orig, MAX_REGS(dest))
+
+#endif
+
+
+int PathToAbsolute(char *pSrc, char *pOut);
+BOOL ContactEnabled(HANDLE hContact, char *setting, int def);
diff --git a/Plugins/avatarhistory/AvatarHistory.rc b/Plugins/avatarhistory/AvatarHistory.rc
new file mode 100644
index 0000000..817d173
--- /dev/null
+++ b/Plugins/avatarhistory/AvatarHistory.rc
@@ -0,0 +1,277 @@
+//Microsoft Developer Studio generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "afxres.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// Portuguese (Brazil) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_PTB)
+#ifdef _WIN32
+LANGUAGE LANG_PORTUGUESE, SUBLANG_PORTUGUESE_BRAZILIAN
+#pragma code_page(1252)
+#endif //_WIN32
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Menu
+//
+
+IDR_MENU1 MENU DISCARDABLE
+BEGIN
+ POPUP "Avatar List Popup"
+ BEGIN
+ MENUITEM "Save As...", ID_AVATARLISTPOPUP_SAVEAS
+ MENUITEM "Delete this entry", ID_AVATARLISTPOPUP_DELETE
+ MENUITEM "Delete this entry and the bitmap file",
+ ID_AVATARLISTPOPUP_DELETE_BOTH
+
+ END
+END
+
+#endif // Portuguese (Brazil) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+/////////////////////////////////////////////////////////////////////////////
+// English (U.K.) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENG)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_UK
+#pragma code_page(1252)
+#endif //_WIN32
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Dialog
+//
+
+IDD_AVATARDLG DIALOG DISCARDABLE 0, 0, 216, 142
+STYLE DS_SETFOREGROUND | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_VISIBLE |
+ WS_CAPTION | WS_SYSMENU
+CAPTION "Avatar History"
+FONT 8, "MS Shell Dlg"
+BEGIN
+ LISTBOX IDC_AVATARLIST,7,7,120,74,LBS_NOINTEGRALHEIGHT |
+ WS_VSCROLL | WS_TABSTOP
+ CONTROL "",IDC_AVATAR,"Static",SS_BITMAP | SS_NOTIFY |
+ SS_CENTERIMAGE | SS_REALSIZEIMAGE | SS_SUNKEN,131,7,78,
+ 74
+ PUSHBUTTON "Open Folder",IDC_OPENFOLDER,6,81,49,14
+ PUSHBUTTON "<",IDC_BACK,138,82,31,10,BS_CENTER | BS_VCENTER
+ PUSHBUTTON ">",IDC_NEXT,172,82,31,10,BS_CENTER | BS_VCENTER
+ CONTROL "Store this user's old avatars in disk",IDC_LOGUSER,
+ "Button",BS_AUTO3STATE | WS_TABSTOP,7,100,147,10
+ CONTROL "Log this user's avatars changes to history",
+ IDC_HISTORYUSER,"Button",BS_AUTO3STATE | WS_TABSTOP,7,
+ 112,147,10
+ CONTROL "Show popups for this user",IDC_POPUPUSER,"Button",
+ BS_AUTO3STATE | WS_TABSTOP,7,124,147,10
+ DEFPUSHBUTTON "OK",IDOK,159,121,50,14
+END
+
+IDD_OPTIONS DIALOGEX 0, 0, 314, 109
+STYLE DS_FIXEDSYS | WS_CHILD | WS_VISIBLE
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg"
+BEGIN
+ GROUPBOX " Protocols ",IDC_PROTOCOLS_G,3,3,308,103
+ LTEXT "Enable tracking for these protocols:",IDC_PROTOCOLS_L,
+ 13,17,157,11
+ CONTROL "List1",IDC_PROTOCOLS,"SysListView32",LVS_REPORT |
+ LVS_SINGLESEL | LVS_SORTASCENDING | LVS_NOCOLUMNHEADER |
+ LVS_NOSORTHEADER | WS_BORDER | WS_TABSTOP,12,29,158,70
+ LTEXT "MetaContacts have special handling: the info is always copied from subcontacts (notifications from a meta are ignored)",
+ IDC_STATIC,181,29,124,70
+END
+
+IDD_POPUPS DIALOGEX 0, 0, 314, 240
+STYLE DS_FIXEDSYS | WS_CHILD | WS_VISIBLE
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg"
+BEGIN
+ CONTROL "Show popup when a contact change his avatar",IDC_POPUPS,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,3,3,308,12
+ GROUPBOX " Colours ",IDC_COLOURS_G,3,25,158,75
+ CONTROL "",IDC_BGCOLOR,"ColourPicker",WS_TABSTOP,11,37,35,14
+ LTEXT "Background colour",IDC_BGCOLOR_L,55,41,66,8
+ CONTROL "",IDC_TEXTCOLOR,"ColourPicker",WS_TABSTOP,11,55,35,14
+ LTEXT "Text colour",IDC_TEXTCOLOR_L,55,59,66,8
+ CONTROL "Use Windows colours",IDC_WINCOLORS,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,11,74,108,10
+ CONTROL "Use default colours",IDC_DEFAULTCOLORS,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,11,84,107,10
+ GROUPBOX " Delay ",IDC_DELAY_G,166,25,145,75
+ CONTROL "From popup plugin",IDC_DELAYFROMPU,"Button",
+ BS_AUTORADIOBUTTON | WS_GROUP,175,38,122,10
+ CONTROL "Custom",IDC_DELAYCUSTOM,"Button",BS_AUTORADIOBUTTON,175,
+ 52,54,10
+ CONTROL "Permanent",IDC_DELAYPERMANENT,"Button",
+ BS_AUTORADIOBUTTON,175,66,122,10
+ EDITTEXT IDC_DELAY,233,50,31,14,ES_AUTOHSCROLL | ES_NUMBER
+ CONTROL "Spin1",IDC_DELAY_SPIN,"msctls_updown32",UDS_SETBUDDYINT |
+ UDS_ALIGNRIGHT | UDS_ARROWKEYS | UDS_NOTHOUSANDS |
+ UDS_HOTTRACK,268,51,11,11
+ GROUPBOX " Actions ",IDC_ACTIONS_G,3,103,308,47
+ RTEXT "On right click:",IDC_RIGHT_ACTION_L,13,118,62,9
+ COMBOBOX IDC_RIGHT_ACTION,83,116,156,60,CBS_DROPDOWNLIST |
+ WS_VSCROLL | WS_TABSTOP
+ RTEXT "On left click:",IDC_LEFT_ACTION_L,13,132,62,9
+ COMBOBOX IDC_LEFT_ACTION,83,132,156,60,CBS_DROPDOWNLIST |
+ WS_VSCROLL | WS_TABSTOP
+ GROUPBOX " Track ",IDC_TRACK_G,3,153,308,47
+ CONTROL "Avatar change:",IDC_CHANGED_L,"Button",BS_AUTOCHECKBOX |
+ WS_TABSTOP,11,166,89,10
+ EDITTEXT IDC_CHANGED,105,166,201,13,ES_AUTOHSCROLL
+ CONTROL "Avatar removal:",IDC_REMOVED_L,"Button",BS_AUTOCHECKBOX |
+ WS_TABSTOP,11,183,89,10
+ EDITTEXT IDC_REMOVED,105,183,201,13,ES_AUTOHSCROLL
+ PUSHBUTTON "Preview",IDC_PREV,131,213,50,14
+END
+
+IDD_FIRST_RUN DIALOG DISCARDABLE 0, 0, 338, 251
+STYLE DS_MODALFRAME | DS_SETFOREGROUND | DS_CENTER | WS_POPUP | WS_VISIBLE |
+ WS_CAPTION | WS_SYSMENU
+CAPTION "Avatar History: Select how to store history avatars"
+FONT 8, "MS Shell Dlg"
+BEGIN
+ LTEXT "Please select how the avatar history should be stored. This setting can NOT be changed in future, because it would cause all previous history to be lost.",
+ IDC_STATIC,7,7,324,25
+ CONTROL "Store history in miranda history and all history avatars in same folder",
+ IDC_MIR_SAME,"Button",BS_AUTORADIOBUTTON | WS_GROUP,7,33,
+ 324,11
+ CONTROL "Store history in miranda history and history avatars in per protocol folders",
+ IDC_MIR_PROTO,"Button",BS_AUTORADIOBUTTON,7,66,324,11
+ CONTROL "Store history in miranda history and history avatars in per contact folders using shortcuts",
+ IDC_MIR_SHORT,"Button",BS_AUTORADIOBUTTON,7,99,324,11
+ CONTROL "Store history avatars in per contact folders using shortcuts",
+ IDC_SHORT,"Button",BS_AUTORADIOBUTTON,7,140,324,11
+ CONTROL "Store history avatars in per contact folders",IDC_DUP,
+ "Button",BS_AUTORADIOBUTTON,7,181,324,11
+ LTEXT "History is stored inside miranda db. It can be seen by History++ or Avatar History internal viewer.",
+ IDC_STATIC,20,44,311,10
+ LTEXT "All avatars are stored as <Profile>\\Avatars History\\<Avatar Hash>",
+ IDC_STATIC,20,54,311,10
+ LTEXT "History is stored inside miranda db. It can be seen by History++ or Avatar History internal viewer.",
+ IDC_STATIC,20,77,311,10
+ LTEXT "All avatars are stored as <Profile>\\Avatars History\\<Protocol>\\<Avatar Hash>",
+ IDC_STATIC,20,87,311,10
+ LTEXT "History is stored inside miranda db. It can be seen by History++ or Avatar History internal viewer.",
+ IDC_STATIC,20,110,311,10
+ LTEXT "All avatars are stored as <Profile>\\Avatars History\\<Protocol>\\<Avatar Hash> and have a shortcut per history entry in <Profile>\\Avatars History\\<Protocol>\\<Contact ID>\\<Timestamp>",
+ IDC_STATIC,20,120,311,18
+ LTEXT "History is stored only in disk. It can be seen by Avatar History internal viewer.",
+ IDC_STATIC,20,151,311,10
+ LTEXT "All avatars are stored as <Profile>\\Avatars History\\<Protocol>\\<Avatar Hash> and have a shortcut per history entry in <Profile>\\Avatars History\\<Protocol>\\<Contact ID>\\<Timestamp>",
+ IDC_STATIC,20,161,311,18
+ LTEXT "History is stored only in disk. It can be seen by Avatar History internal viewer.",
+ IDC_STATIC,20,192,311,10
+ LTEXT "All avatars are stored as <Profile>\\Avatars History\\<Protocol>\\<Contact ID>\\<Timestamp> (the same image can be stored lot of times)",
+ IDC_STATIC,20,202,311,18
+ DEFPUSHBUTTON "OK",IDOK,151,230,50,14
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// DESIGNINFO
+//
+
+#ifdef APSTUDIO_INVOKED
+GUIDELINES DESIGNINFO DISCARDABLE
+BEGIN
+ IDD_AVATARDLG, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 209
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 135
+ END
+
+ IDD_OPTIONS, DIALOG
+ BEGIN
+ LEFTMARGIN, 3
+ RIGHTMARGIN, 311
+ TOPMARGIN, 3
+ BOTTOMMARGIN, 107
+ END
+
+ IDD_POPUPS, DIALOG
+ BEGIN
+ LEFTMARGIN, 3
+ RIGHTMARGIN, 311
+ TOPMARGIN, 3
+ BOTTOMMARGIN, 237
+ END
+
+ IDD_FIRST_RUN, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 331
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 244
+ END
+END
+#endif // APSTUDIO_INVOKED
+
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "resource.h\0"
+END
+
+2 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "#include ""afxres.h""\r\n"
+ "\0"
+END
+
+3 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "\r\n"
+ "\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Icon
+//
+
+// Icon with lowest ID value placed first to ensure application icon
+// remains consistent on all systems.
+IDI_AVATARHIST ICON DISCARDABLE "history.ico"
+IDI_AVATAROVERLAY ICON DISCARDABLE "AvatarOverlay.ico"
+#endif // English (U.K.) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
+
diff --git a/Plugins/avatarhistory/AvatarOverlay.ico b/Plugins/avatarhistory/AvatarOverlay.ico
new file mode 100644
index 0000000..63557ba
--- /dev/null
+++ b/Plugins/avatarhistory/AvatarOverlay.ico
Binary files differ
diff --git a/Plugins/avatarhistory/Docs/avatarhist.png b/Plugins/avatarhistory/Docs/avatarhist.png
new file mode 100644
index 0000000..e9619f6
--- /dev/null
+++ b/Plugins/avatarhistory/Docs/avatarhist.png
Binary files differ
diff --git a/Plugins/avatarhistory/Docs/avatarhist_changelog.txt b/Plugins/avatarhistory/Docs/avatarhist_changelog.txt
new file mode 100644
index 0000000..a3c2e3b
--- /dev/null
+++ b/Plugins/avatarhistory/Docs/avatarhist_changelog.txt
@@ -0,0 +1,44 @@
+Avatar History
+
+Changelog:
+
+. 0.0.2.10
+ * Fix for get event text
+
+. 0.0.2.9
+ + Depends on history events to add events to history
+
+. 0.0.2.8
+ * Fix for radios in first run dialog
+
+. 0.0.2.7
+ + Added first run dialog and option to store avatars as version 1
+ * Changed to stop using avs services and use freeiamge services (requires miranda 0.7 #28)
+
+. 0.0.2.6
+ * Changed to always use online proto icon as base for popup icon
+
+. 0.0.2.5
+ + Added support for Miranda 0.8
+
+. 0.0.2.4
+ * Try to fix blank images
+
+. 0.0.2.3
+ + Hide contact menu item for disabled protocols
+ + Create shortcuts for metacontacts if it is a enabled protocol
+
+. 0.0.2.2
+ * Bug fixes (thanks borkra)
+ + Use shortcuts in avatar history dialog when history log is disabled (thanks Let)
+ + Option to delete only the entry or it and the avatar image
+ * Moved default place of menu entry to bellow View History
+ * Using default Miranda icon for History if possible
+ * Using icon based on avs for overlay
+ * Small changes in options dialog
+
+. 0.0.2.0
+ * Changed to store avatars in a proto dir (lost compatibility with old version)
+ * Changes in options and avatar dialog
+ + Keep only one copy of an image (even if contact switchs back to it)
+ + Updater support \ No newline at end of file
diff --git a/Plugins/avatarhistory/Docs/avatarhist_readme.txt b/Plugins/avatarhistory/Docs/avatarhist_readme.txt
new file mode 100644
index 0000000..b07d99e
--- /dev/null
+++ b/Plugins/avatarhistory/Docs/avatarhist_readme.txt
@@ -0,0 +1,29 @@
+Avatar History plugin
+-----------------------------------
+
+CAUTION: THIS IS AN BETA STAGE PLUGIN. IT CAN DO VERY BAD THINGS. USE AT YOUR OWN RISK.
+
+This plugins keep a history of contact avatars. It stores the files on the disk and uses miranda history to keep track of then.
+
+This plugin is really made by MattJ, I'm only doing some changes to allow better handling of the avatars.
+
+If you want to import the old history to new format, you can use the Avatar History Importer plugin by TioDuke. It will import old avatars to new format, allowing to keep only one copy of each avatar (but new avatars may appear duplicated, because the hash function used by the importer may not be the same as the one used by the protocol). It can be downloaded at:
+Avatar History Importer Ansi Dll: http://pescuma.mirandaim.ru/miranda/avh_imp.zip
+Avatar History Importer Unicode Dll: http://pescuma.mirandaim.ru/miranda/avh_impW.zip
+Avatar History Importer Source: http://pescuma.mirandaim.ru/miranda/avh_imp_src.zip
+
+To user the importer, you should:
+1. Backup your profile and the old avatar history folder
+2. Install the new version of Avatar History
+3. Start miranda with all protocols offline
+4. Setup Avatar History options (some are better if set once and not changed anymore). You will need 'Show expert options' checkbox enabled
+5. Finish miranda and install Avatar History Importer
+6. Start miranda with all protocols offline
+7. A messagebox will appear, telling that the importer will run. Wait until the importer ended messagebox.
+8. Enjoy ;)
+
+Now all avatars are stored in per protocol folders. This is done to allow avoiding duplicated avatars. (If you enable 'Keep all in same folder' there will be only one folder for all protocols - this may help in case you have more than one instance of the same proto). The relation avatar <-> contact and the avatar history dialog is created from info from the contact history (and not using the files in HD). Since some people like to see the images in contacts folders, there is the option 'Also create per contact folders with shortcuts'. This option will create the contact folders (as previous version) and will create shortcuts inside it to the avatar stored in the protocol folder (if 2 avatars are the same, there will be 2 shortcuts but only one avatar in disk). Importer will use this option too when importing, but you must set it before importing everything.
+
+This plugin needs Avatar Service and Miranda 0.7 to be able to work. To log events to history it needs History Events plugin.
+
+To report bugs/make suggestions, go to the forum thread: http://forums.miranda-im.org/showthread.php?t=9118
diff --git a/Plugins/avatarhistory/Docs/avatarhist_version.txt b/Plugins/avatarhistory/Docs/avatarhist_version.txt
new file mode 100644
index 0000000..7ed8590
--- /dev/null
+++ b/Plugins/avatarhistory/Docs/avatarhist_version.txt
@@ -0,0 +1 @@
+Avatar History 0.0.2.10 \ No newline at end of file
diff --git a/Plugins/avatarhistory/Docs/langpack_avatarhist.txt b/Plugins/avatarhistory/Docs/langpack_avatarhist.txt
new file mode 100644
index 0000000..2bad91a
--- /dev/null
+++ b/Plugins/avatarhistory/Docs/langpack_avatarhist.txt
@@ -0,0 +1,114 @@
+; Avatar History
+; Author: MattJ and Pescuma
+
+[Avatar History]
+
+; Menu
+
+[View Avatar History]
+
+; Custom folders
+
+[Avatars]
+
+; Options
+
+[History]
+[Avatar]
+
+[ Logging options ]
+[Store old avatars on disk]
+[Also create per contact folders with shortcuts]
+[Log avatar changes to history]
+
+[ History templates ]
+[Avatar change:]
+[Track when contacts remove their avatars too]
+[Avatar removal:]
+
+[ Protocols ]
+[Enable tracking for these protocols:]
+[Keep all in same folder]
+
+[Popups]
+[Avatar Change]
+
+[Show popup when a contact change his avatar]
+
+[ Colours ]
+[Background colour]
+[Text colour]
+[Use Windows colours]
+[Use default colours]
+
+[ Delay ]
+[From popup plugin]
+[Custom]
+[Permanent]
+
+[ Actions ]
+[On right click:]
+[On left click:]
+[Do nothing]
+[Close popup]
+[Show avatar history]
+[Show contact history]
+
+[Preview]
+
+
+; Test popup
+
+[Test Contact]
+[Test description]
+
+
+; First run dialog
+
+[Avatar History: Select how to store history avatars]
+[Please select how the avatar history should be stored. This setting can NOT be changed in future, because it would cause all previous history to be lost.]
+[Store history in miranda history and all history avatars in same folder]
+[History is stored inside miranda db. It can be seen by History++ or Avatar History internal viewer.]
+[All avatars are stored as <Profile>\\Avatars History\\<Avatar Hash>]
+[Store history in miranda history and history avatars in per protocol folders]
+[All avatars are stored as <Profile>\\Avatars History\\<Protocol>\\<Avatar Hash>]
+[Store history in miranda history and history avatars in per contact folders using shortcuts]
+[All avatars are stored as <Profile>\\Avatars History\\<Protocol>\\<Avatar Hash> and have a shortcut per history entry in <Profile>\\Avatars History\\<Protocol>\\<Contact ID>\\<Timestamp>]
+[Store history avatars in per contact folders using shortcut]
+[History is stored only in disk. It can be seen by Avatar History internal viewer.]
+[All avatars are stored as <Profile>\\Avatars History\\<Protocol>\\<Avatar Hash> and have a shortcut per history entry in <Profile>\\Avatars History\\<Protocol>\\<Contact ID>\\<Timestamp>]
+[Store history avatars in per contact folders]
+[All avatars are stored as <Profile>\\Avatars History\\<Protocol>\\<Contact ID>\\<Timestamp> (the same image can be stored lot of times)]
+[OK]
+
+; Avatar dialog
+
+[Open Folder]
+[Save]
+[Delete]
+[<]
+[>]
+[Store this user's old avatars in disk]
+[Log this user's avatars changes to history]
+[Show popups for this user]
+[OK]
+[Save As...]
+[Delete this entry]
+[Delete this entry and the bitmap file]
+
+
+; Others
+
+[Please select an avatar from the list]
+[No avatar selected]
+
+[Delete avatar log?]
+[Are you sure you wish to delete this history entry?\nOnly the entry in history will be deleted, bitmap file will be kept!]
+[Are you sure you wish to delete this avatar shortcut?\nOnly shortcut will be deleted, bitmap file will be kept!]
+
+[Delete avatar?]
+[Are you sure you wish to delete this archived avatar?\nThis will delete the history entry and the bitmap file.\nWARNING:This can affect more than one entry in history!]
+[Are you sure you wish to delete this archived avatar?\nThis will delete the shortcut and the bitmap file.\nWARNING:This can affect more than one shortcut!]
+
+[Unknown Protocol]
+[Unknown UIN]
diff --git a/Plugins/avatarhistory/ZIP/doit.bat b/Plugins/avatarhistory/ZIP/doit.bat
new file mode 100644
index 0000000..0d603ce
--- /dev/null
+++ b/Plugins/avatarhistory/ZIP/doit.bat
@@ -0,0 +1,81 @@
+@echo off
+
+rem Batch file to build and upload files
+rem
+rem TODO: Integration with FL
+
+set name=avatarhist
+
+rem To upload, this var must be set here or in other batch
+rem set ftp=ftp://<user>:<password>@<ftp>/<path>
+
+echo Building %name% ...
+
+msdev ..\AvatarHistory.dsp /MAKE "AvatarHistory - Win32 Release" /REBUILD
+msdev ..\AvatarHistory.dsp /MAKE "AvatarHistory - Win32 Unicode Release" /REBUILD
+
+echo Generating files for %name% ...
+
+del *.zip
+del *.dll
+copy ..\..\..\bin\release\Plugins\%name%.dll
+copy ..\..\..\bin\release\Plugins\%name%W.dll
+copy ..\Docs\%name%_changelog.txt
+copy ..\Docs\%name%_version.txt
+copy ..\Docs\%name%_readme.txt
+mkdir Docs
+cd Docs
+del /Q *.*
+copy ..\..\Docs\%name%_readme.txt
+copy ..\..\Docs\langpack_%name%.txt
+rem copy ..\..\Docs\helppack_%name%.txt
+copy ..\..\m_%name%.h
+cd ..
+mkdir src
+cd src
+del /Q *.*
+copy ..\..\*.h
+copy ..\..\*.cpp
+copy ..\..\*.
+copy ..\..\*.rc
+copy ..\..\*.dsp
+copy ..\..\*.dsw
+mkdir Docs
+cd Docs
+del /Q *.*
+copy ..\..\..\Docs\*.*
+cd ..
+cd ..
+
+"C:\Program Files\Filzip\Filzip.exe" -a -rp %name%.zip %name%.dll Docs
+"C:\Program Files\Filzip\Filzip.exe" -a -rp %name%W.zip %name%W.dll Docs
+"C:\Program Files\Filzip\Filzip.exe" -a -rp %name%_src.zip src\*.*
+
+del *.dll
+cd Docs
+del /Q *.*
+cd ..
+rmdir Docs
+cd src
+del /Q *.*
+cd Docs
+del /Q *.*
+cd ..
+rmdir Docs
+cd ..
+rmdir src
+
+if "%ftp%"=="" GOTO END
+
+echo Going to upload files...
+pause
+
+"C:\Program Files\FileZilla\FileZilla.exe" -u .\%name%.zip %ftp% -overwrite -close
+"C:\Program Files\FileZilla\FileZilla.exe" -u .\%name%W.zip %ftp% -overwrite -close
+"C:\Program Files\FileZilla\FileZilla.exe" -u .\%name%_changelog.txt %ftp% -overwrite -close
+"C:\Program Files\FileZilla\FileZilla.exe" -u .\%name%_version.txt %ftp% -overwrite -close
+"C:\Program Files\FileZilla\FileZilla.exe" -u .\%name%_readme.txt %ftp% -overwrite -close
+
+:END
+
+echo Done.
diff --git a/Plugins/avatarhistory/history.ico b/Plugins/avatarhistory/history.ico
new file mode 100644
index 0000000..4b56c86
--- /dev/null
+++ b/Plugins/avatarhistory/history.ico
Binary files differ
diff --git a/Plugins/avatarhistory/icolib.cpp b/Plugins/avatarhistory/icolib.cpp
new file mode 100644
index 0000000..63baa3d
--- /dev/null
+++ b/Plugins/avatarhistory/icolib.cpp
@@ -0,0 +1,137 @@
+#include "AvatarHistory.h"
+#include <commctrl.h> //for ImageList_*
+
+enum IconIndex
+{
+ I_HISTORY,
+ I_OVERLAY
+};
+
+typedef struct
+{
+ char* szDescr;
+ char* szName;
+ int defIconID;
+ BOOL core;
+} IconStruct;
+
+static IconStruct iconList[] =
+{
+ { "History", "core_main_10", IDI_AVATARHIST, TRUE },
+ { "Avatar Overlay", "avh_overlay", IDI_AVATAROVERLAY, FALSE }
+};
+
+extern HANDLE hHooks[];
+
+static HICON LoadIconEx(IconIndex i)
+{
+ HICON hIcon;
+
+ if (hHooks[4])
+ hIcon = (HICON)CallService(MS_SKIN2_GETICON, 0, (LPARAM)iconList[(int)i].szName);
+ else
+ hIcon = (HICON)LoadImage(hInst, MAKEINTRESOURCE(iconList[(int)i].defIconID),
+ IMAGE_ICON, 0, 0, 0);
+
+ return hIcon;
+}
+
+
+static void ReleaseIconEx(HICON hIcon)
+{
+ if (hHooks[4])
+ CallService(MS_SKIN2_RELEASEICON, (WPARAM)hIcon, 0);
+ else
+ DestroyIcon(hIcon);
+}
+
+static void IcoLibUpdateMenus()
+{
+ CLISTMENUITEM mi = {0};
+
+ mi.cbSize = sizeof(mi);
+ mi.flags = CMIM_FLAGS | CMIM_ICON;
+ mi.hIcon = createDefaultOverlayedIcon(FALSE);
+ CallService( MS_CLIST_MODIFYMENUITEM, ( WPARAM )hMenu, ( LPARAM )&mi );
+ DestroyIcon(mi.hIcon);
+}
+
+int IcoLibIconsChanged(WPARAM wParam, LPARAM lParam)
+{
+ IcoLibUpdateMenus();
+ return 0;
+}
+
+void SetupIcoLib()
+{
+ hHooks[4] = HookEvent(ME_SKIN2_ICONSCHANGED, IcoLibIconsChanged);
+
+ if (hHooks[4])
+ {
+ SKINICONDESC sid = {0};
+ char path[MAX_PATH];
+
+ GetModuleFileNameA(hInst, path, sizeof(path));
+
+ sid.cbSize = SKINICONDESC_SIZE_V2;
+ sid.pszSection = Translate("Avatar History");
+ sid.pszDefaultFile = path;
+
+ for (unsigned i = 0; i < MAX_REGS(iconList); i++)
+ {
+ if (mirVer < PLUGIN_MAKE_VERSION(0, 7, 0, 0) || !iconList[i].core)
+ {
+ sid.pszDescription = Translate(iconList[i].szDescr);
+ sid.pszName = iconList[i].szName;
+ sid.iDefaultIndex = -iconList[i].defIconID;
+ CallService(MS_SKIN2_ADDICON, 0, (LPARAM)&sid);
+ }
+ }
+ }
+ IcoLibUpdateMenus();
+}
+
+static HICON getOverlayedIcon(HICON icon, HICON overlay, BOOL big)
+{
+ HIMAGELIST il = ImageList_Create(
+ GetSystemMetrics(big?SM_CXICON:SM_CXSMICON),
+ GetSystemMetrics(big?SM_CYICON:SM_CYSMICON),
+ ILC_COLOR32|ILC_MASK, 2, 2);
+ ImageList_AddIcon(il, icon);
+ ImageList_AddIcon(il, overlay);
+ HIMAGELIST newImage = ImageList_Merge(il,0,il,1,0,0);
+ ImageList_Destroy(il);
+ HICON hIcon = ImageList_GetIcon(newImage, 0, 0);
+ ImageList_Destroy(newImage);
+ return hIcon; // the result should be destroyed by DestroyIcon()
+}
+
+
+HICON createDefaultOverlayedIcon(BOOL big)
+{
+ HICON icon0 = LoadIconEx(I_HISTORY);
+ HICON icon1 = LoadIconEx(I_OVERLAY);
+
+ HICON resIcon = getOverlayedIcon(icon0, icon1, FALSE);
+
+ ReleaseIconEx(icon0);
+ ReleaseIconEx(icon1);
+
+ return resIcon;
+}
+
+
+HICON createProtoOverlayedIcon(HANDLE hContact)
+{
+ HICON icon1 = LoadIconEx(I_OVERLAY);
+
+ char *szProto = (char *)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0);
+ HICON icon0 = LoadSkinnedProtoIcon(szProto, ID_STATUS_ONLINE);
+
+ HICON resIcon = getOverlayedIcon(icon0, icon1, FALSE);
+
+ ReleaseIconEx(icon1);
+ CallService(MS_SKIN2_RELEASEICON, (WPARAM)icon0, 0);
+
+ return resIcon;
+}
diff --git a/Plugins/avatarhistory/m_avatarhist.h b/Plugins/avatarhistory/m_avatarhist.h
new file mode 100644
index 0000000..3431b5e
--- /dev/null
+++ b/Plugins/avatarhistory/m_avatarhist.h
@@ -0,0 +1,55 @@
+/*
+Copyright (C) 2006 MattJ, Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#ifndef __M_AVATARHISTORY_H__
+# define __M_AVATARHISTORY_H__
+
+
+#define MIID_AVATAR_CHANGE_LOGGER { 0x95e3f3d3, 0x9678, 0x4561, { 0x96, 0xf8, 0x95, 0x88, 0x33, 0x7b, 0x86, 0x68 } }
+#define MIID_AVATAR_CHANGE_NOTIFIER { 0x91af9298, 0x8570, 0x4063, { 0xbf, 0x2f, 0xca, 0x68, 0xd0, 0xe3, 0xb3, 0x6a } }
+
+
+#define EVENTTYPE_AVATAR_CHANGE 9003
+
+
+/*
+Return TRUE is Avatar History is enabled for this contact
+
+wParam: hContact
+lParam: ignored
+*/
+#define MS_AVATARHISTORY_ENABLED "AvatarHistory/IsEnabled"
+
+
+/*
+Get cached avatar
+
+wParam: (char *) protocol name
+lParam: (char *) hash
+return: (TCHAR *) NULL if none is found or the path to the avatar. You need to free this string
+ with mir_free.
+*/
+#define MS_AVATARHISTORY_GET_CACHED_AVATAR "AvatarHistory/GetCachedAvatar"
+
+
+
+
+
+#endif // __M_AVATARHISTORY_H__
diff --git a/Plugins/avatarhistory/options.cpp b/Plugins/avatarhistory/options.cpp
new file mode 100644
index 0000000..9ca8766
--- /dev/null
+++ b/Plugins/avatarhistory/options.cpp
@@ -0,0 +1,239 @@
+/*
+Avatar History Plugin
+ Copyright (C) 2006 Matthew Wild - Email: mwild1@gmail.com
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+*/
+
+#include "AvatarHistory.h"
+#include <commctrl.h>
+#include <prsht.h>
+#include "../utils/mir_options.h"
+
+
+
+// Prototypes /////////////////////////////////////////////////////////////////////////////////////
+
+Options opts;
+
+
+static BOOL CALLBACK OptionsDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam);
+static BOOL CALLBACK PopupsDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam);
+
+
+static OptPageControl optionsControls[] = {
+ { NULL, CONTROL_PROTOCOL_LIST, IDC_PROTOCOLS, "%sEnabled", TRUE }
+};
+
+
+static OptPageControl popupsControls[] = {
+ { NULL, CONTROL_CHECKBOX, IDC_POPUPS, "AvatarPopups", AVH_DEF_AVPOPUPS },
+ { &opts.popup_bkg_color, CONTROL_COLOR, IDC_BGCOLOR, "PopupsBgColor", AVH_DEF_POPUPBG },
+ { &opts.popup_text_color, CONTROL_COLOR, IDC_TEXTCOLOR, "PopupsTextColor", AVH_DEF_POPUPFG },
+ { &opts.popup_use_win_colors, CONTROL_CHECKBOX, IDC_WINCOLORS, "PopupsWinColors", FALSE },
+ { &opts.popup_use_default_colors, CONTROL_CHECKBOX, IDC_DEFAULTCOLORS, "PopupsDefaultColors", AVH_DEF_DEFPOPUPS },
+ { &opts.popup_delay_type, CONTROL_RADIO, IDC_DELAYFROMPU, "PopupsDelayType", POPUP_DELAY_DEFAULT, POPUP_DELAY_DEFAULT },
+ { NULL, CONTROL_RADIO, IDC_DELAYCUSTOM, "PopupsDelayType", POPUP_DELAY_DEFAULT, POPUP_DELAY_CUSTOM },
+ { NULL, CONTROL_RADIO, IDC_DELAYPERMANENT, "PopupsDelayType", POPUP_DELAY_DEFAULT, POPUP_DELAY_PERMANENT },
+ { &opts.popup_timeout, CONTROL_SPIN, IDC_DELAY, "PopupsTimeout", 10, IDC_DELAY_SPIN, (WORD) 1, (WORD) 255 },
+ { &opts.popup_right_click_action, CONTROL_COMBO, IDC_RIGHT_ACTION, "PopupsRightClick", POPUP_ACTION_CLOSEPOPUP },
+ { &opts.popup_left_click_action, CONTROL_COMBO, IDC_LEFT_ACTION, "PopupsLeftClick", POPUP_ACTION_OPENAVATARHISTORY },
+ { &opts.popup_show_changed, CONTROL_CHECKBOX, IDC_CHANGED_L, "PopupsShowChanged", TRUE },
+ { &opts.popup_changed, CONTROL_TEXT, IDC_CHANGED, "PopupsTextChanged", (DWORD) _T(DEFAULT_TEMPLATE_CHANGED) },
+ { &opts.popup_show_removed, CONTROL_CHECKBOX, IDC_REMOVED_L, "PopupsShowRemoved", TRUE },
+ { &opts.popup_removed, CONTROL_TEXT, IDC_REMOVED, "PopupsTextRemoved", (DWORD) _T(DEFAULT_TEMPLATE_REMOVED) }
+};
+
+static UINT popupsExpertControls[] = {
+ IDC_COLOURS_G, IDC_BGCOLOR, IDC_BGCOLOR_L, IDC_TEXTCOLOR, IDC_TEXTCOLOR_L, IDC_WINCOLORS, IDC_DEFAULTCOLORS,
+ IDC_DELAY_G, IDC_DELAYFROMPU, IDC_DELAYCUSTOM, IDC_DELAYPERMANENT, IDC_DELAY, IDC_DELAY_SPIN,
+ IDC_ACTIONS_G, IDC_RIGHT_ACTION_L, IDC_RIGHT_ACTION, IDC_LEFT_ACTION_L, IDC_LEFT_ACTION,
+ IDC_PREV
+};
+
+
+// Functions //////////////////////////////////////////////////////////////////////////////////////
+
+
+int OptInit(WPARAM wParam,LPARAM lParam)
+{
+ OPTIONSDIALOGPAGE odp;
+
+ ZeroMemory(&odp,sizeof(odp));
+ odp.cbSize=sizeof(odp);
+ odp.position=0;
+ odp.hInstance=hInst;
+ odp.ptszGroup = TranslateT("History"); // group to put your item under
+ odp.ptszTitle = TranslateT("Avatar"); // name of the item
+ odp.pfnDlgProc = OptionsDlgProc;
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPTIONS);
+ odp.flags = ODPF_BOLDGROUPS | ODPF_TCHAR | ODPF_EXPERTONLY;
+ CallService(MS_OPT_ADDPAGE,wParam,(LPARAM)&odp);
+
+ if(ServiceExists(MS_POPUP_ADDPOPUPEX)
+#ifdef UNICODE
+ || ServiceExists(MS_POPUP_ADDPOPUPW)
+#endif
+ )
+ {
+ ZeroMemory(&odp,sizeof(odp));
+ odp.cbSize=sizeof(odp);
+ odp.position=0;
+ odp.hInstance=hInst;
+ odp.ptszGroup = TranslateT("Popups");
+ odp.ptszTitle = TranslateT("Avatar Change");
+ odp.pfnDlgProc = PopupsDlgProc;
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_POPUPS);
+ odp.flags = ODPF_BOLDGROUPS | ODPF_TCHAR;
+ odp.expertOnlyControls = popupsExpertControls;
+ odp.nExpertOnlyControls = MAX_REGS(popupsExpertControls);
+ odp.nIDBottomSimpleControl = IDC_POPUPS;
+ odp.nIDRightSimpleControl = IDC_POPUPS;
+ CallService(MS_OPT_ADDPAGE,wParam,(LPARAM)&odp);
+ }
+
+ return 0;
+}
+
+
+void LoadOptions()
+{
+ LoadOpts(optionsControls, MAX_REGS(optionsControls), MODULE_NAME);
+ LoadOpts(popupsControls, MAX_REGS(popupsControls), MODULE_NAME);
+
+ opts.log_per_contact_folders = DBGetContactSettingByte(NULL, MODULE_NAME, "LogPerContactFolders", 0);
+ opts.log_keep_same_folder = DBGetContactSettingByte(NULL, MODULE_NAME, "LogKeepSameFolder", 0);
+ opts.log_store_as_hash = DBGetContactSettingByte(NULL, MODULE_NAME, "StoreAsHash", 1);
+}
+
+
+static BOOL CALLBACK OptionsDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ return SaveOptsDlgProc(optionsControls, MAX_REGS(optionsControls), MODULE_NAME, hwndDlg, msg, wParam, lParam);
+}
+
+
+static void PopupsEnableDisableCtrls(HWND hwndDlg)
+{
+ BOOL enabled = IsDlgButtonChecked(hwndDlg, IDC_POPUPS);
+
+ EnableWindow(GetDlgItem(hwndDlg, IDC_COLOURS_G), enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_BGCOLOR_L), enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_TEXTCOLOR_L), enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_DELAY_G), enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_DELAYFROMPU), enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_DELAYCUSTOM), enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_DELAYPERMANENT), enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_ACTIONS_G), enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_RIGHT_ACTION_L), enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_RIGHT_ACTION), enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_LEFT_ACTION_L), enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_LEFT_ACTION), enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_PREV), enabled);
+
+ EnableWindow(GetDlgItem(hwndDlg, IDC_BGCOLOR), enabled &&
+ !IsDlgButtonChecked(hwndDlg, IDC_WINCOLORS) &&
+ !IsDlgButtonChecked(hwndDlg, IDC_DEFAULTCOLORS));
+ EnableWindow(GetDlgItem(hwndDlg, IDC_TEXTCOLOR), enabled &&
+ !IsDlgButtonChecked(hwndDlg, IDC_WINCOLORS) &&
+ !IsDlgButtonChecked(hwndDlg, IDC_DEFAULTCOLORS));
+ EnableWindow(GetDlgItem(hwndDlg, IDC_DEFAULTCOLORS), enabled &&
+ !IsDlgButtonChecked(hwndDlg, IDC_WINCOLORS));
+ EnableWindow(GetDlgItem(hwndDlg, IDC_WINCOLORS), enabled &&
+ !IsDlgButtonChecked(hwndDlg, IDC_DEFAULTCOLORS));
+
+ EnableWindow(GetDlgItem(hwndDlg, IDC_DELAY), enabled &&
+ IsDlgButtonChecked(hwndDlg, IDC_DELAYCUSTOM));
+
+ EnableWindow(GetDlgItem(hwndDlg, IDC_CHANGED_L), enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_REMOVED_L), enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_CHANGED), enabled &&
+ IsDlgButtonChecked(hwndDlg, IDC_CHANGED_L));
+ EnableWindow(GetDlgItem(hwndDlg, IDC_REMOVED), enabled &&
+ IsDlgButtonChecked(hwndDlg, IDC_REMOVED_L));
+
+}
+
+
+static BOOL CALLBACK PopupsDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg)
+ {
+ case WM_INITDIALOG:
+ {
+ SendDlgItemMessage(hwndDlg, IDC_RIGHT_ACTION, CB_ADDSTRING, 0, (LONG) TranslateT("Do nothing"));
+ SendDlgItemMessage(hwndDlg, IDC_RIGHT_ACTION, CB_ADDSTRING, 0, (LONG) TranslateT("Close popup"));
+ SendDlgItemMessage(hwndDlg, IDC_RIGHT_ACTION, CB_ADDSTRING, 0, (LONG) TranslateT("Show avatar history"));
+ SendDlgItemMessage(hwndDlg, IDC_RIGHT_ACTION, CB_ADDSTRING, 0, (LONG) TranslateT("Show contact history"));
+
+ SendDlgItemMessage(hwndDlg, IDC_LEFT_ACTION, CB_ADDSTRING, 0, (LONG) TranslateT("Do nothing"));
+ SendDlgItemMessage(hwndDlg, IDC_LEFT_ACTION, CB_ADDSTRING, 0, (LONG) TranslateT("Close popup"));
+ SendDlgItemMessage(hwndDlg, IDC_LEFT_ACTION, CB_ADDSTRING, 0, (LONG) TranslateT("Show avatar history"));
+ SendDlgItemMessage(hwndDlg, IDC_LEFT_ACTION, CB_ADDSTRING, 0, (LONG) TranslateT("Show contact history"));
+
+ // Needs to be called here in this case
+ BOOL ret = SaveOptsDlgProc(popupsControls, MAX_REGS(popupsControls), MODULE_NAME, hwndDlg, msg, wParam, lParam);
+
+ PopupsEnableDisableCtrls(hwndDlg);
+
+ return ret;
+ }
+ case WM_COMMAND:
+ {
+ switch (LOWORD(wParam))
+ {
+ case IDC_POPUPS:
+ case IDC_WINCOLORS:
+ case IDC_DEFAULTCOLORS:
+ case IDC_DELAYFROMPU:
+ case IDC_DELAYPERMANENT:
+ case IDC_DELAYCUSTOM:
+ case IDC_CHANGED_L:
+ case IDC_REMOVED_L:
+ {
+ if (HIWORD(wParam) == BN_CLICKED)
+ PopupsEnableDisableCtrls(hwndDlg);
+
+ break;
+ }
+ case IDC_PREV:
+ {
+ Options op = opts;
+
+ if (IsDlgButtonChecked(hwndDlg, IDC_DELAYFROMPU))
+ op.popup_delay_type = POPUP_DELAY_DEFAULT;
+ else if (IsDlgButtonChecked(hwndDlg, IDC_DELAYCUSTOM))
+ op.popup_delay_type = POPUP_DELAY_CUSTOM;
+ else if (IsDlgButtonChecked(hwndDlg, IDC_DELAYPERMANENT))
+ op.popup_delay_type = POPUP_DELAY_PERMANENT;
+
+ op.popup_timeout = GetDlgItemInt(hwndDlg,IDC_DELAY, NULL, FALSE);
+ op.popup_bkg_color = SendDlgItemMessage(hwndDlg,IDC_BGCOLOR,CPM_GETCOLOUR,0,0);
+ op.popup_text_color = SendDlgItemMessage(hwndDlg,IDC_TEXTCOLOR,CPM_GETCOLOUR,0,0);
+ op.popup_use_win_colors = IsDlgButtonChecked(hwndDlg, IDC_WINCOLORS) != 0;
+ op.popup_use_default_colors = IsDlgButtonChecked(hwndDlg, IDC_DEFAULTCOLORS) != 0;
+
+ ShowTestPopup(TranslateT("Test Contact"), TranslateT("Test description"), &op);
+
+ break;
+ }
+ }
+ break;
+ }
+ }
+
+ return SaveOptsDlgProc(popupsControls, MAX_REGS(popupsControls), MODULE_NAME, hwndDlg, msg, wParam, lParam);
+}
diff --git a/Plugins/avatarhistory/popup.cpp b/Plugins/avatarhistory/popup.cpp
new file mode 100644
index 0000000..c24bf8b
--- /dev/null
+++ b/Plugins/avatarhistory/popup.cpp
@@ -0,0 +1,354 @@
+/*
+Copyright (C) 2005 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#include "AvatarHistory.h"
+
+
+
+// Prototypes /////////////////////////////////////////////////////////////////////////////////////
+
+#define WMU_ACTION (WM_USER + 1)
+
+
+LRESULT CALLBACK PopupWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
+
+HWND hPopupWindow = NULL;
+
+
+static LRESULT CALLBACK PopupDlgProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
+static LRESULT CALLBACK DumbPopupDlgProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
+
+
+
+// Functions //////////////////////////////////////////////////////////////////////////////////////
+
+
+// Initializations needed by popups
+void InitPopups()
+{
+ // window needed for popup commands
+ hPopupWindow = CreateWindowEx(WS_EX_TOOLWINDOW, _T("static"), _T(MODULE_NAME) _T("_PopupWindow"),
+ 0, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, HWND_DESKTOP,
+ NULL, hInst, NULL);
+ SetWindowLong(hPopupWindow, GWL_WNDPROC, (LONG)(WNDPROC)PopupWndProc);
+}
+
+
+// Deinitializations needed by popups
+void DeInitPopups()
+{
+}
+
+
+// Show an error popup
+void ShowErrPopup(const TCHAR *description, const TCHAR *title)
+{
+ ShowPopupEx(NULL, title == NULL ? _T(MODULE_NAME) _T(" Error") : title, description,
+ NULL, POPUP_TYPE_ERROR, NULL);
+}
+
+
+void ShowTestPopup(const TCHAR *title, const TCHAR *description, const Options *op)
+{
+ ShowPopupEx(NULL, title, description, NULL, POPUP_TYPE_TEST, op);
+}
+
+
+void ShowPopup(HANDLE hContact, const TCHAR *title, const TCHAR *description)
+{
+ ShowPopupEx(hContact, title, description, hContact, POPUP_TYPE_NORMAL, &opts);
+}
+
+typedef struct
+{
+ void* plugin_data;
+ HICON hIcon;
+}
+PopupDataType;
+
+// Show an popup
+void ShowPopupEx(HANDLE hContact, const TCHAR *title, const TCHAR *description,
+ void *plugin_data, int type, const Options *op)
+{
+#ifdef UNICODE
+ if(ServiceExists(MS_POPUP_ADDPOPUPW))
+ {
+ // Make popup
+ POPUPDATAW ppd = {0};
+
+ ppd.lchContact = hContact;
+ ppd.lchIcon = createProtoOverlayedIcon(hContact);
+
+ ppd.PluginData = mir_alloc(sizeof(PopupDataType));
+ ((PopupDataType*)ppd.PluginData)->plugin_data = plugin_data;
+ ((PopupDataType*)ppd.PluginData)->hIcon = ppd.lchIcon;
+
+ if (title != NULL)
+ lstrcpyn(ppd.lpwzContactName, title, MAX_REGS(ppd.lpwzContactName));
+ else if (hContact != NULL)
+ lstrcpyn(ppd.lpwzContactName, (TCHAR *) CallService(MS_CLIST_GETCONTACTDISPLAYNAME, (WPARAM)hContact, GCDNF_TCHAR | GCDNF_NOCACHE),
+ MAX_REGS(ppd.lpwzContactName));
+
+ if (description != NULL)
+ lstrcpyn(ppd.lpwzText, description, MAX_REGS(ppd.lpwzText));
+
+ if (type == POPUP_TYPE_NORMAL || type == POPUP_TYPE_TEST)
+ {
+ if (op->popup_use_default_colors)
+ {
+ ppd.colorBack = 0;
+ ppd.colorText = 0;
+ }
+ else if (op->popup_use_win_colors)
+ {
+ ppd.colorBack = GetSysColor(COLOR_BTNFACE);
+ ppd.colorText = GetSysColor(COLOR_WINDOWTEXT);
+ }
+ else
+ {
+ ppd.colorBack = op->popup_bkg_color;
+ ppd.colorText = op->popup_text_color;
+ }
+ }
+ else // if (type == POPUP_TYPE_ERROR)
+ {
+ ppd.colorBack = RGB(200,0,0);
+ ppd.colorText = RGB(255,255,255);
+ }
+
+ if (type == POPUP_TYPE_NORMAL)
+ {
+ ppd.PluginWindowProc = PopupDlgProc;
+ }
+ else // if (type == POPUP_TYPE_TEST || type == POPUP_TYPE_ERROR)
+ {
+ ppd.PluginWindowProc = DumbPopupDlgProc;
+ }
+
+ if (type == POPUP_TYPE_NORMAL || type == POPUP_TYPE_TEST)
+ {
+ switch (op->popup_delay_type)
+ {
+ case POPUP_DELAY_CUSTOM:
+ ppd.iSeconds = opts.popup_timeout;
+ break;
+
+ case POPUP_DELAY_PERMANENT:
+ ppd.iSeconds = -1;
+ break;
+
+ case POPUP_DELAY_DEFAULT:
+ default:
+ ppd.iSeconds = 0;
+ break;
+ }
+ }
+ else // if (type == POPUP_TYPE_ERROR)
+ {
+ ppd.iSeconds = 0;
+ }
+
+ // Now that every field has been filled, we want to see the popup.
+ CallService(MS_POPUP_ADDPOPUPW, (WPARAM)&ppd,0);
+ }
+ else
+#endif
+ if(ServiceExists(MS_POPUP_ADDPOPUPEX))
+ {
+ // Make popup
+ POPUPDATAEX ppd = {0};
+
+ ppd.lchContact = hContact;
+ ppd.lchIcon = createProtoOverlayedIcon(hContact);
+
+ ppd.PluginData = mir_alloc(sizeof(PopupDataType));
+ ((PopupDataType*)ppd.PluginData)->plugin_data = plugin_data;
+ ((PopupDataType*)ppd.PluginData)->hIcon = ppd.lchIcon;
+
+ if (title != NULL)
+ TCHAR_TO_CHAR(ppd.lpzContactName, title);
+ else
+ lstrcpynA(ppd.lpzContactName, (char *) CallService(MS_CLIST_GETCONTACTDISPLAYNAME, (WPARAM)hContact, GCDNF_NOCACHE),
+ MAX_REGS(ppd.lpzContactName));
+
+ if (description != NULL)
+ TCHAR_TO_CHAR(ppd.lpzText, description);
+
+ if (type == POPUP_TYPE_NORMAL || type == POPUP_TYPE_TEST)
+ {
+ if (op->popup_use_default_colors)
+ {
+ ppd.colorBack = 0;
+ ppd.colorText = 0;
+ }
+ else if (op->popup_use_win_colors)
+ {
+ ppd.colorBack = GetSysColor(COLOR_BTNFACE);
+ ppd.colorText = GetSysColor(COLOR_WINDOWTEXT);
+ }
+ else
+ {
+ ppd.colorBack = op->popup_bkg_color;
+ ppd.colorText = op->popup_text_color;
+ }
+ }
+ else // if (type == POPUP_TYPE_ERROR)
+ {
+ ppd.colorBack = RGB(200,0,0);
+ ppd.colorText = RGB(255,255,255);
+ }
+
+ if (type == POPUP_TYPE_NORMAL)
+ {
+ ppd.PluginWindowProc = PopupDlgProc;
+ }
+ else // if (type == POPUP_TYPE_TEST || type == POPUP_TYPE_ERROR)
+ {
+ ppd.PluginWindowProc = DumbPopupDlgProc;
+ }
+
+ if (type == POPUP_TYPE_NORMAL || type == POPUP_TYPE_TEST)
+ {
+ switch (op->popup_delay_type)
+ {
+ case POPUP_DELAY_CUSTOM:
+ ppd.iSeconds = opts.popup_timeout;
+ break;
+
+ case POPUP_DELAY_PERMANENT:
+ ppd.iSeconds = -1;
+ break;
+
+ case POPUP_DELAY_DEFAULT:
+ default:
+ ppd.iSeconds = 0;
+ break;
+ }
+ }
+ else // if (type == POPUP_TYPE_ERROR)
+ {
+ ppd.iSeconds = 0;
+ }
+
+ // Now that every field has been filled, we want to see the popup.
+ CallService(MS_POPUP_ADDPOPUPEX, (WPARAM)&ppd,0);
+ }
+ else
+ {
+ MessageBox(NULL, description, title ? title : (TCHAR *)CallService(MS_CLIST_GETCONTACTDISPLAYNAME, (WPARAM)hContact, GCDNF_TCHAR),
+ MB_OK);
+ }
+
+}
+
+
+// Handle to the hidden windows to handle actions for popup clicks
+// wParam has the number of MOTD in case of WMU_SHOW_MOTD_DETAILS
+LRESULT CALLBACK PopupWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ if (uMsg == WMU_ACTION)
+ {
+ if (lParam == POPUP_ACTION_OPENAVATARHISTORY)
+ {
+ CallService("AvatarHistory/ShowDialog", wParam, 0);
+ }
+ else if (lParam == POPUP_ACTION_OPENHISTORY)
+ {
+ CallService(MS_HISTORY_SHOWCONTACTHISTORY, wParam, 0);
+ }
+ }
+ return DefWindowProc(hWnd, uMsg, wParam, lParam);
+}
+
+
+// Handle to popup events
+static LRESULT CALLBACK PopupDlgProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ switch(message) {
+ case WM_COMMAND:
+ {
+ PopupDataType* popup = (PopupDataType*)PUGetPluginData(hWnd);
+ PostMessage(hPopupWindow, WMU_ACTION, (WPARAM)popup->plugin_data, opts.popup_left_click_action);
+
+ if (opts.popup_left_click_action != POPUP_ACTION_DONOTHING)
+ PUDeletePopUp(hWnd);
+
+ return TRUE;
+ }
+
+ case WM_CONTEXTMENU:
+ {
+ PopupDataType* popup = (PopupDataType*)PUGetPluginData(hWnd);
+ PostMessage(hPopupWindow, WMU_ACTION, (WPARAM)popup->plugin_data, opts.popup_right_click_action);
+
+ if (opts.popup_right_click_action != POPUP_ACTION_DONOTHING)
+ PUDeletePopUp(hWnd);
+
+ return TRUE;
+ }
+
+ case UM_FREEPLUGINDATA:
+ {
+ PopupDataType* popup = (PopupDataType*)PUGetPluginData(hWnd);
+ if ((unsigned)popup != CALLSERVICE_NOTFOUND)
+ {
+ DestroyIcon(popup->hIcon);
+ mir_free(popup);
+ }
+ return FALSE; //the return value is ignored
+ }
+ }
+
+ return DefWindowProc(hWnd, message, wParam, lParam);
+}
+
+
+// Handle to popup events
+static LRESULT CALLBACK DumbPopupDlgProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ switch(message) {
+ case WM_COMMAND:
+ {
+ PUDeletePopUp(hWnd);
+ return TRUE;
+ }
+
+ case WM_CONTEXTMENU:
+ {
+ PUDeletePopUp(hWnd);
+ return TRUE;
+ }
+
+ case UM_FREEPLUGINDATA:
+ {
+ PopupDataType* popup = (PopupDataType*)PUGetPluginData(hWnd);
+ if ((unsigned)popup != CALLSERVICE_NOTFOUND)
+ {
+ DestroyIcon(popup->hIcon);
+ mir_free(popup);
+ }
+ return FALSE; //the return value is ignored
+ }
+ }
+
+ return DefWindowProc(hWnd, message, wParam, lParam);
+}
+
+
diff --git a/Plugins/avatarhistory/popup.h b/Plugins/avatarhistory/popup.h
new file mode 100644
index 0000000..d86db2d
--- /dev/null
+++ b/Plugins/avatarhistory/popup.h
@@ -0,0 +1,52 @@
+/*
+Copyright (C) 2005 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#ifndef __POPUP_H__
+# define __POPUP_H__
+
+#include <windows.h>
+
+
+// Initializations needed by popups
+void InitPopups();
+
+// Deinitializations needed by popups
+void DeInitPopups();
+
+
+#define POPUP_TYPE_NORMAL 0
+#define POPUP_TYPE_TEST 1
+#define POPUP_TYPE_ERROR 2
+
+// Show an popup
+void ShowPopup(HANDLE hContact, const TCHAR *title, const TCHAR *description);
+
+// Show an test
+void ShowTestPopup(const TCHAR *title, const TCHAR *description, const Options *op);
+
+// Show an error popup
+void ShowErrPopup(const char *description, const char *title = NULL);
+
+void ShowPopupEx(HANDLE hContact, const TCHAR *title, const TCHAR *description,
+ void *plugin_data, int type, const Options *op);
+
+
+
+#endif // __POPUP_H__
diff --git a/Plugins/avatarhistory/resource.h b/Plugins/avatarhistory/resource.h
new file mode 100644
index 0000000..146be59
--- /dev/null
+++ b/Plugins/avatarhistory/resource.h
@@ -0,0 +1,87 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Developer Studio generated include file.
+// Used by AvatarHistory.rc
+//
+#define IDD_OPTIONS_OLD 101
+#define IDC_POPUPBG 102
+#define IDD_AVATARDLG 102
+#define IDC_POPUPFG 103
+#define IDI_AVATARHIST 104
+#define IDI_NEWAVATAR 105
+#define IDI_AVATAROVERLAY 106
+#define IDR_MENU1 108
+#define IDD_FIRST_RUN 109
+#define IDD_OPTIONS 119
+#define IDD_POPUPS 120
+#define IDC_CUSTOM1 1000
+#define IDC_AVATARPOPUPS 1001
+#define IDC_DELAY 1001
+#define IDC_LOGTODISK 1002
+#define IDC_WINCOLORS 1002
+#define IDC_LOGTOHISTORY 1003
+#define IDC_DEFAULTCOLORS 1003
+#define IDC_BGCOLOR 1004
+#define IDC_DEFPOPUPS 1005
+#define IDC_TEXTCOLOR 1005
+#define IDC_PREV 1006
+#define IDC_AVATAR 1007
+#define IDC_DELAYFROMPU 1007
+#define IDC_AVATARLIST 1008
+#define IDC_DELAYCUSTOM 1008
+#define IDC_SAVE 1009
+#define IDC_DELAYPERMANENT 1009
+#define IDC_DELETE 1010
+#define IDC_NEXT 1011
+#define IDC_BACK 1012
+#define IDC_LOGUSER 1013
+#define IDC_POPUPUSER 1014
+#define IDC_OPENFOLDER 1015
+#define IDC_PUFGTEXT 1016
+#define IDC_HISTORYUSER 1016
+#define IDC_PUBGTEXT 1017
+#define IDC_SHOWMENU 1019
+#define IDC_LOGGING_G 1020
+#define IDC_SAME_FOLDER 1021
+#define IDC_RIGHT_ACTION 1022
+#define IDC_MIR_SAME 1022
+#define IDC_LEFT_ACTION 1023
+#define IDC_MIR_PROTO 1023
+#define IDC_MIR_SHORT 1024
+#define IDC_SHORT 1025
+#define IDC_DUP 1026
+#define IDC_PROTOCOLS 1041
+#define IDC_CHANGED 1058
+#define IDC_REMOVED 1059
+#define IDC_POPUPS 1060
+#define IDC_DELAY_SPIN 1061
+#define IDC_LOG_DISK 1061
+#define IDC_LOG_HISTORY 1062
+#define IDC_TRACK_G 1063
+#define IDC_CHANGED_L 1064
+#define IDC_REMOVED_L 1065
+#define IDC_PROTOCOLS_G 1066
+#define IDC_PROTOCOLS_L 1067
+#define IDC_TRACK_CHANGE 1068
+#define IDC_COLOURS_G 1068
+#define IDC_OLD_STYLE 1068
+#define IDC_TRACK_REMOVE 1069
+#define IDC_BGCOLOR_L 1069
+#define IDC_TEXTCOLOR_L 1070
+#define IDC_DELAY_G 1071
+#define IDC_ACTIONS_G 1072
+#define IDC_RIGHT_ACTION_L 1073
+#define IDC_LEFT_ACTION_L 1074
+#define ID_AVATARLISTPOPUP_SAVEAS 40001
+#define ID_AVATARLISTPOPUP_DELETE 40002
+#define ID_AVATARLISTPOPUP_DELETE_BOTH 40003
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 110
+#define _APS_NEXT_COMMAND_VALUE 40004
+#define _APS_NEXT_CONTROL_VALUE 1023
+#define _APS_NEXT_SYMED_VALUE 103
+#endif
+#endif
diff --git a/Plugins/avatarhistory/sdk/m_folders.h b/Plugins/avatarhistory/sdk/m_folders.h
new file mode 100644
index 0000000..a112b05
--- /dev/null
+++ b/Plugins/avatarhistory/sdk/m_folders.h
@@ -0,0 +1,207 @@
+/*
+Custom profile folders plugin for Miranda IM
+
+Copyright © 2005 Cristian Libotean
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef M_CUSTOM_FOLDERS_H
+#define M_CUSTOM_FOLDERS_H
+
+#define FOLDERS_API 501 //dunno why it's here but it is :)
+
+#define PROFILE_PATH "%profile_path%"
+#define CURRENT_PROFILE "%current_profile%"
+#define MIRANDA_PATH "%miranda_path%"
+#define PLUGINS_PATH "%miranda_path%" "\\plugins"
+
+#define TO_WIDE(x) L ## x
+
+#define PROFILE_PATHW L"%profile_path%"
+#define CURRENT_PROFILEW L"%current_profile%"
+#define MIRANDA_PATHW L"%miranda_path%"
+
+#define FOLDER_AVATARS PROFILE_PATH "\\" CURRENT_PROFILE "\\avatars"
+#define FOLDER_VCARDS PROFILE_PATH "\\" CURRENT_PROFILE "\\vcards"
+#define FOLDER_LOGS PROFILE_PATH "\\" CURRENT_PROFILE "\\logs"
+#define FOLDER_RECEIVED_FILES PROFILE_PATH "\\" CURRENT_PROFILE "\\received files"
+#define FOLDER_DOCS MIRANDA_PATH "\\" "docs"
+
+#define FOLDER_CONFIG PLUGINS_PATH "\\" "config"
+
+#define FOLDER_SCRIPTS MIRANDA_PATH "\\" "scripts"
+
+#define FOLDER_UPDATES MIRANDA_PATH "\\" "updates"
+
+#define FOLDER_CUSTOMIZE MIRANDA_PATH "\\" "customize"
+#define FOLDER_CUSTOMIZE_SOUNDS FOLDER_CUSTOMIZE "\\sounds"
+#define FOLDER_CUSTOMIZE_ICONS FOLDER_CUSTOMIZE "\\icons"
+#define FOLDER_CUSTOMIZE_SMILEYS FOLDER_CUSTOMIZE "\\smileys"
+#define FOLDER_CUSTOMIZE_SKINS FOLDER_CUSTOMIZE "\\skins"
+#define FOLDER_CUSTOMIZE_THEMES FOLDER_CUSTOMIZE "\\themes"
+
+
+#define FOLDERS_NAME_MAX_SIZE 64 //maximum name and section size
+
+#define FF_UNICODE 0x00000001
+
+typedef struct{
+ int cbSize; //size of struct
+ char szSection[FOLDERS_NAME_MAX_SIZE]; //section name, if it doesn't exist it will be created otherwise it will just add this entry to it
+ char szName[FOLDERS_NAME_MAX_SIZE]; //entry name - will be shown in options
+ union{
+ const char *szFormat; //default string format. Fallback string in case there's no entry in the database for this folder. This should be the initial value for the path, users will be able to change it later.
+ const wchar_t *szFormatW; //String is dup()'d so you can free it later. If you set the unicode string don't forget to set the flag accordingly.
+ const TCHAR *szFormatT;
+ };
+ DWORD flags; //FF_* flags
+} FOLDERSDATA;
+
+/*Folders/Register/Path service
+ wParam - not used, must be 0
+ lParam - (LPARAM) (const FOLDERDATA *) - Data structure filled with
+ the necessary information.
+ Returns a handle to the registered path or 0 on error.
+ You need to use this to call the other services.
+*/
+#define MS_FOLDERS_REGISTER_PATH "Folders/Register/Path"
+
+/*Folders/Get/PathSize service
+ wParam - (WPARAM) (int) - handle to registered path
+ lParam - (LPARAM) (int *) - pointer to the variable that receives the size of the path
+ string (not including the null character). Depending on the flags set when creating the path
+ it will either call strlen() or wcslen() to get the length of the string.
+ Returns the size of the buffer.
+*/
+#define MS_FOLDERS_GET_SIZE "Folders/Get/PathSize"
+
+typedef struct{
+ int cbSize;
+ int nMaxPathSize; //maximum size of buffer. This represents the number of characters that can be copied to it (so for unicode strings you don't send the number of bytes but the length of the string).
+ union{
+ char *szPath; //pointer to the buffer that receives the path without the last "\\"
+ wchar_t *szPathW; //unicode version of the buffer.
+ TCHAR *szPathT;
+ };
+} FOLDERSGETDATA;
+
+/*Folders/Get/Path service
+ wParam - (WPARAM) (int) - handle to registered path
+ lParam - (LPARAM) (FOLDERSGETDATA *) pointer to a FOLDERSGETDATA that has all the relevant fields filled.
+ Should return 0 on success, or nonzero otherwise.
+*/
+#define MS_FOLDERS_GET_PATH "Folders/Get/Path"
+
+typedef struct{
+ int cbSize;
+ union{
+ char **szPath; //address of a string variable (char *) or (wchar_t*) where the path should be stored (the last \ won't be copied).
+ wchar_t **szPathW; //unicode version of string.
+ TCHAR **szPathT;
+ };
+} FOLDERSGETALLOCDATA;
+
+/*Folders/GetRelativePath/Alloc service
+ wParam - (WPARAM) (int) - Handle to registered path
+ lParam - (LPARAM) (FOLDERSALLOCDATA *) data
+ This service is the same as MS_FOLDERS_GET_PATH with the difference that this service
+ allocates the needed space for the buffer. It uses miranda's memory functions for that and you need
+ to use those to free the resulting buffer.
+ Should return 0 on success, or nonzero otherwise. Currently it only returns 0.
+*/
+#define MS_FOLDERS_GET_PATH_ALLOC "Folders/Get/Path/Alloc"
+
+
+/*Folders/On/Path/Changed
+ wParam - (WPARAM) 0
+ lParam - (LPARAM) 0
+ Triggered when the folders change, you should reget the paths you registered.
+*/
+#define ME_FOLDERS_PATH_CHANGED "Folders/On/Path/Changed"
+
+#ifndef FOLDERS_NO_HELPER_FUNCTIONS
+//#include "../../../include/newpluginapi.h"
+
+__inline static int FoldersRegisterCustomPath(const char *section, const char *name, const char *defaultPath)
+{
+ FOLDERSDATA fd = {0};
+ if (!ServiceExists(MS_FOLDERS_REGISTER_PATH)) return 1;
+ fd.cbSize = sizeof(FOLDERSDATA);
+ strncpy(fd.szSection, section, FOLDERS_NAME_MAX_SIZE);
+ fd.szSection[FOLDERS_NAME_MAX_SIZE - 1] = '\0';
+ strncpy(fd.szName, name, FOLDERS_NAME_MAX_SIZE);
+ fd.szName[FOLDERS_NAME_MAX_SIZE - 1] = '\0';
+ fd.szFormat = defaultPath;
+ return CallService(MS_FOLDERS_REGISTER_PATH, 0, (LPARAM) &fd);
+}
+
+__inline static int FoldersRegisterCustomPathW(const char *section, const char *name, const wchar_t *defaultPathW)
+{
+ FOLDERSDATA fd = {0};
+ if (!ServiceExists(MS_FOLDERS_REGISTER_PATH)) return 1;
+ fd.cbSize = sizeof(FOLDERSDATA);
+ strncpy(fd.szSection, section, FOLDERS_NAME_MAX_SIZE);
+ fd.szSection[FOLDERS_NAME_MAX_SIZE - 1] = '\0'; //make sure it's NULL terminated
+ strncpy(fd.szName, name, FOLDERS_NAME_MAX_SIZE);
+ fd.szName[FOLDERS_NAME_MAX_SIZE - 1] = '\0'; //make sure it's NULL terminated
+ fd.szFormatW = defaultPathW;
+ fd.flags = FF_UNICODE;
+ return CallService(MS_FOLDERS_REGISTER_PATH, 0, (LPARAM) &fd);
+}
+
+__inline static int FoldersGetCustomPath(HANDLE hFolderEntry, char *path, const int size, char *notFound)
+{
+ FOLDERSGETDATA fgd = {0};
+ int res;
+ fgd.cbSize = sizeof(FOLDERSGETDATA);
+ fgd.nMaxPathSize = size;
+ fgd.szPath = path;
+ res = CallService(MS_FOLDERS_GET_PATH, (WPARAM) hFolderEntry, (LPARAM) &fgd);
+ if (res)
+ {
+ strncpy(path, notFound, size);
+ path[size - 1] = '\0'; //make sure it's NULL terminated
+ }
+ return res;
+}
+
+__inline static int FoldersGetCustomPathW(HANDLE hFolderEntry, wchar_t *pathW, const int count, wchar_t *notFoundW)
+{
+ FOLDERSGETDATA fgd = {0};
+ int res;
+ fgd.cbSize = sizeof(FOLDERSGETDATA);
+ fgd.nMaxPathSize = count;
+ fgd.szPathW = pathW;
+ res = CallService(MS_FOLDERS_GET_PATH, (WPARAM) hFolderEntry, (LPARAM) &fgd);
+ if (res)
+ {
+ wcsncpy(pathW, notFoundW, count);
+ pathW[count - 1] = '\0';
+ }
+ return res;
+}
+
+# ifdef _UNICODE
+# define FoldersGetCustomPathT FoldersGetCustomPathW
+# define FoldersRegisterCustomPathT FoldersRegisterCustomPathW
+#else
+# define FoldersGetCustomPathT FoldersGetCustomPath
+# define FoldersRegisterCustomPathT FoldersRegisterCustomPath
+#endif
+
+#endif
+
+#endif //M_CUSTOM_FOLDERS_H \ No newline at end of file
diff --git a/Plugins/avatarhistory/sdk/m_freeimage.h b/Plugins/avatarhistory/sdk/m_freeimage.h
new file mode 100644
index 0000000..c0cceeb
--- /dev/null
+++ b/Plugins/avatarhistory/sdk/m_freeimage.h
@@ -0,0 +1,965 @@
+// ==========================================================
+// FreeImage 3
+//
+// Design and implementation by
+// - Floris van den Berg (flvdberg@wxs.nl)
+// - Hervé Drolon (drolon@infonie.fr)
+//
+// Contributors:
+// - Adam Gates (radad@xoasis.com)
+// - Alex Kwak
+// - Alexander Dymerets (sashad@te.net.ua)
+// - Detlev Vendt (detlev.vendt@brillit.de)
+// - Jan L. Nauta (jln@magentammt.com)
+// - Jani Kajala (janik@remedy.fi)
+// - Juergen Riecker (j.riecker@gmx.de)
+// - Karl-Heinz Bussian (khbussian@moss.de)
+// - Laurent Rocher (rocherl@club-internet.fr)
+// - Luca Piergentili (l.pierge@terra.es)
+// - Machiel ten Brinke (brinkem@uni-one.nl)
+// - Markus Loibl (markus.loibl@epost.de)
+// - Martin Weber (martweb@gmx.net)
+// - Matthias Wandel (mwandel@rim.net)
+// - Michal Novotny (michal@etc.cz)
+// - Petr Pytelka (pyta@lightcomp.com)
+// - Riley McNiff (rmcniff@marexgroup.com)
+// - Ryan Rubley (ryan@lostreality.org)
+// - Volker Gärtner (volkerg@gmx.at)
+//
+// This file is part of FreeImage 3
+//
+// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
+// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
+// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
+// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
+// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
+// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
+// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+// THIS DISCLAIMER.
+//
+// Use at your own risk!
+// ==========================================================
+
+#ifndef FREEIMAGE_H
+#define FREEIMAGE_H
+
+// Version information ------------------------------------------------------
+
+#define FREEIMAGE_MAJOR_VERSION 3
+#define FREEIMAGE_MINOR_VERSION 9
+#define FREEIMAGE_RELEASE_SERIAL 3
+
+// Compiler options ---------------------------------------------------------
+
+#if !defined(_FI_MIMPLUGIN)
+#include <wchar.h> // needed for UNICODE functions
+#endif
+
+#if defined(FREEIMAGE_LIB) || !(defined(_WIN32) || defined(__WIN32__))
+#define DLL_API
+#define DLL_CALLCONV
+#else
+
+#define DLL_CALLCONV __stdcall
+// The following ifdef block is the standard way of creating macros which make exporting
+// from a DLL simpler. All files within this DLL are compiled with the FREEIMAGE_EXPORTS
+// symbol defined on the command line. this symbol should not be defined on any project
+// that uses this DLL. This way any other project whose source files include this file see
+// DLL_API functions as being imported from a DLL, wheras this DLL sees symbols
+// defined with this macro as being exported.
+#ifdef FREEIMAGE_EXPORTS
+#define DLL_API __declspec(dllexport)
+#else
+#define DLL_API __declspec(dllimport)
+#endif // FREEIMAGE_EXPORTS
+#endif // FREEIMAGE_LIB || !WIN32
+
+// Some versions of gcc may have BYTE_ORDER or __BYTE_ORDER defined
+// If your big endian system isn't being detected, add an OS specific check
+#if (defined(BYTE_ORDER) && BYTE_ORDER==BIG_ENDIAN) || \
+ (defined(__BYTE_ORDER) && __BYTE_ORDER==__BIG_ENDIAN) || \
+ defined(__BIG_ENDIAN__)
+#define FREEIMAGE_BIGENDIAN
+#endif // BYTE_ORDER
+
+// Ensure 4-byte enums if we're using Borland C++ compilers
+#if defined(__BORLANDC__)
+#pragma option push -b
+#endif
+
+// For C compatibility --------------------------------------------------------
+
+#ifdef __cplusplus
+#define FI_DEFAULT(x)
+#define FI_ENUM(x) enum x
+#define FI_STRUCT(x) struct x
+#else
+#define FI_DEFAULT(x)
+#define FI_ENUM(x) typedef int x; enum x
+#define FI_STRUCT(x) typedef struct x x; struct x
+#endif
+
+// Bitmap types -------------------------------------------------------------
+
+FI_STRUCT (FIBITMAP) { void *data; };
+FI_STRUCT (FIMULTIBITMAP) { void *data; };
+
+// Types used in the library (directly copied from Windows) -----------------
+
+#ifndef _WINDOWS_
+#define _WINDOWS_
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+#ifndef TRUE
+#define TRUE 1
+#endif
+#ifndef NULL
+#define NULL 0
+#endif
+
+#ifndef SEEK_SET
+#define SEEK_SET 0
+#define SEEK_CUR 1
+#define SEEK_END 2
+#endif
+
+#ifndef _MSC_VER
+// define portable types for 32-bit / 64-bit OS
+#include <inttypes.h>
+typedef int32_t BOOL;
+typedef uint8_t BYTE;
+typedef uint16_t WORD;
+typedef uint32_t DWORD;
+typedef int32_t LONG;
+#else
+// MS is not C99 ISO compliant
+typedef long BOOL;
+typedef unsigned char BYTE;
+typedef unsigned short WORD;
+typedef unsigned long DWORD;
+typedef long LONG;
+#endif // _MSC_VER
+
+#if (defined(_WIN32) || defined(__WIN32__))
+#pragma pack(push, 1)
+#else
+#pragma pack(1)
+#endif // WIN32
+
+typedef struct tagRGBQUAD {
+#ifdef FREEIMAGE_BIGENDIAN
+ BYTE rgbRed;
+ BYTE rgbGreen;
+ BYTE rgbBlue;
+#else
+ BYTE rgbBlue;
+ BYTE rgbGreen;
+ BYTE rgbRed;
+#endif // FREEIMAGE_BIGENDIAN
+ BYTE rgbReserved;
+} RGBQUAD;
+
+typedef struct tagRGBTRIPLE {
+#ifdef FREEIMAGE_BIGENDIAN
+ BYTE rgbtRed;
+ BYTE rgbtGreen;
+ BYTE rgbtBlue;
+#else
+ BYTE rgbtBlue;
+ BYTE rgbtGreen;
+ BYTE rgbtRed;
+#endif // FREEIMAGE_BIGENDIAN
+} RGBTRIPLE;
+
+#if (defined(_WIN32) || defined(__WIN32__))
+#pragma pack(pop)
+#else
+#pragma pack()
+#endif // WIN32
+
+typedef struct tagBITMAPINFOHEADER{
+ DWORD biSize;
+ LONG biWidth;
+ LONG biHeight;
+ WORD biPlanes;
+ WORD biBitCount;
+ DWORD biCompression;
+ DWORD biSizeImage;
+ LONG biXPelsPerMeter;
+ LONG biYPelsPerMeter;
+ DWORD biClrUsed;
+ DWORD biClrImportant;
+} BITMAPINFOHEADER, *PBITMAPINFOHEADER;
+
+typedef struct tagBITMAPINFO {
+ BITMAPINFOHEADER bmiHeader;
+ RGBQUAD bmiColors[1];
+} BITMAPINFO, *PBITMAPINFO;
+
+#endif // _WINDOWS_
+
+// Types used in the library (specific to FreeImage) ------------------------
+
+#if (defined(_WIN32) || defined(__WIN32__))
+#pragma pack(push, 1)
+#else
+#pragma pack(1)
+#endif // WIN32
+
+/** 48-bit RGB
+*/
+typedef struct tagFIRGB16 {
+ WORD red;
+ WORD green;
+ WORD blue;
+} FIRGB16;
+
+/** 64-bit RGBA
+*/
+typedef struct tagFIRGBA16 {
+ WORD red;
+ WORD green;
+ WORD blue;
+ WORD alpha;
+} FIRGBA16;
+
+/** 96-bit RGB Float
+*/
+typedef struct tagFIRGBF {
+ float red;
+ float green;
+ float blue;
+} FIRGBF;
+
+/** 128-bit RGBA Float
+*/
+typedef struct tagFIRGBAF {
+ float red;
+ float green;
+ float blue;
+ float alpha;
+} FIRGBAF;
+
+/** Data structure for COMPLEX type (complex number)
+*/
+typedef struct tagFICOMPLEX {
+ /// real part
+ double r;
+ /// imaginary part
+ double i;
+} FICOMPLEX;
+
+#if (defined(_WIN32) || defined(__WIN32__))
+#pragma pack(pop)
+#else
+#pragma pack()
+#endif // WIN32
+
+// Indexes for byte arrays, masks and shifts for treating pixels as words ---
+// These coincide with the order of RGBQUAD and RGBTRIPLE -------------------
+
+#ifndef FREEIMAGE_BIGENDIAN
+// Little Endian (x86 / MS Windows, Linux) : BGR(A) order
+#define FI_RGBA_RED 2
+#define FI_RGBA_GREEN 1
+#define FI_RGBA_BLUE 0
+#define FI_RGBA_ALPHA 3
+#define FI_RGBA_RED_MASK 0x00FF0000
+#define FI_RGBA_GREEN_MASK 0x0000FF00
+#define FI_RGBA_BLUE_MASK 0x000000FF
+#define FI_RGBA_ALPHA_MASK 0xFF000000
+#define FI_RGBA_RED_SHIFT 16
+#define FI_RGBA_GREEN_SHIFT 8
+#define FI_RGBA_BLUE_SHIFT 0
+#define FI_RGBA_ALPHA_SHIFT 24
+#else
+// Big Endian (PPC / Linux, MaxOSX) : RGB(A) order
+#define FI_RGBA_RED 0
+#define FI_RGBA_GREEN 1
+#define FI_RGBA_BLUE 2
+#define FI_RGBA_ALPHA 3
+#define FI_RGBA_RED_MASK 0xFF000000
+#define FI_RGBA_GREEN_MASK 0x00FF0000
+#define FI_RGBA_BLUE_MASK 0x0000FF00
+#define FI_RGBA_ALPHA_MASK 0x000000FF
+#define FI_RGBA_RED_SHIFT 24
+#define FI_RGBA_GREEN_SHIFT 16
+#define FI_RGBA_BLUE_SHIFT 8
+#define FI_RGBA_ALPHA_SHIFT 0
+#endif // FREEIMAGE_BIGENDIAN
+
+#define FI_RGBA_RGB_MASK (FI_RGBA_RED_MASK|FI_RGBA_GREEN_MASK|FI_RGBA_BLUE_MASK)
+
+// The 16bit macros only include masks and shifts, since each color element is not byte aligned
+
+#define FI16_555_RED_MASK 0x7C00
+#define FI16_555_GREEN_MASK 0x03E0
+#define FI16_555_BLUE_MASK 0x001F
+#define FI16_555_RED_SHIFT 10
+#define FI16_555_GREEN_SHIFT 5
+#define FI16_555_BLUE_SHIFT 0
+#define FI16_565_RED_MASK 0xF800
+#define FI16_565_GREEN_MASK 0x07E0
+#define FI16_565_BLUE_MASK 0x001F
+#define FI16_565_RED_SHIFT 11
+#define FI16_565_GREEN_SHIFT 5
+#define FI16_565_BLUE_SHIFT 0
+
+// ICC profile support ------------------------------------------------------
+
+#define FIICC_DEFAULT 0x00
+#define FIICC_COLOR_IS_CMYK 0x01
+
+FI_STRUCT (FIICCPROFILE) {
+ WORD flags; // info flag
+ DWORD size; // profile's size measured in bytes
+ void *data; // points to a block of contiguous memory containing the profile
+};
+
+// Important enums ----------------------------------------------------------
+
+/** I/O image format identifiers.
+*/
+FI_ENUM(FREE_IMAGE_FORMAT) {
+ FIF_UNKNOWN = -1,
+ FIF_BMP = 0,
+ FIF_ICO = 1,
+ FIF_JPEG = 2,
+ FIF_JNG = 3,
+ FIF_KOALA = 4,
+ FIF_LBM = 5,
+ FIF_IFF = FIF_LBM,
+ FIF_MNG = 6,
+ FIF_PBM = 7,
+ FIF_PBMRAW = 8,
+ FIF_PCD = 9,
+ FIF_PCX = 10,
+ FIF_PGM = 11,
+ FIF_PGMRAW = 12,
+ FIF_PNG = 13,
+ FIF_PPM = 14,
+ FIF_PPMRAW = 15,
+ FIF_RAS = 16,
+ FIF_TARGA = 17,
+ FIF_TIFF = 18,
+ FIF_WBMP = 19,
+ FIF_PSD = 20,
+ FIF_CUT = 21,
+ FIF_XBM = 22,
+ FIF_XPM = 23,
+ FIF_DDS = 24,
+ FIF_GIF = 25,
+ FIF_HDR = 26,
+ FIF_FAXG3 = 27,
+ FIF_SGI = 28
+};
+
+/** Image type used in FreeImage.
+*/
+FI_ENUM(FREE_IMAGE_TYPE) {
+ FIT_UNKNOWN = 0, // unknown type
+ FIT_BITMAP = 1, // standard image : 1-, 4-, 8-, 16-, 24-, 32-bit
+ FIT_UINT16 = 2, // array of unsigned short : unsigned 16-bit
+ FIT_INT16 = 3, // array of short : signed 16-bit
+ FIT_UINT32 = 4, // array of unsigned long : unsigned 32-bit
+ FIT_INT32 = 5, // array of long : signed 32-bit
+ FIT_FLOAT = 6, // array of float : 32-bit IEEE floating point
+ FIT_DOUBLE = 7, // array of double : 64-bit IEEE floating point
+ FIT_COMPLEX = 8, // array of FICOMPLEX : 2 x 64-bit IEEE floating point
+ FIT_RGB16 = 9, // 48-bit RGB image : 3 x 16-bit
+ FIT_RGBA16 = 10, // 64-bit RGBA image : 4 x 16-bit
+ FIT_RGBF = 11, // 96-bit RGB float image : 3 x 32-bit IEEE floating point
+ FIT_RGBAF = 12 // 128-bit RGBA float image : 4 x 32-bit IEEE floating point
+};
+
+/** Image color type used in FreeImage.
+*/
+FI_ENUM(FREE_IMAGE_COLOR_TYPE) {
+ FIC_MINISWHITE = 0, // min value is white
+ FIC_MINISBLACK = 1, // min value is black
+ FIC_RGB = 2, // RGB color model
+ FIC_PALETTE = 3, // color map indexed
+ FIC_RGBALPHA = 4, // RGB color model with alpha channel
+ FIC_CMYK = 5 // CMYK color model
+};
+
+/** Color quantization algorithms.
+Constants used in FreeImage_ColorQuantize.
+*/
+FI_ENUM(FREE_IMAGE_QUANTIZE) {
+ FIQ_WUQUANT = 0, // Xiaolin Wu color quantization algorithm
+ FIQ_NNQUANT = 1 // NeuQuant neural-net quantization algorithm by Anthony Dekker
+};
+
+/** Dithering algorithms.
+Constants used in FreeImage_Dither.
+*/
+FI_ENUM(FREE_IMAGE_DITHER) {
+ FID_FS = 0, // Floyd & Steinberg error diffusion
+ FID_BAYER4x4 = 1, // Bayer ordered dispersed dot dithering (order 2 dithering matrix)
+ FID_BAYER8x8 = 2, // Bayer ordered dispersed dot dithering (order 3 dithering matrix)
+ FID_CLUSTER6x6 = 3, // Ordered clustered dot dithering (order 3 - 6x6 matrix)
+ FID_CLUSTER8x8 = 4, // Ordered clustered dot dithering (order 4 - 8x8 matrix)
+ FID_CLUSTER16x16= 5, // Ordered clustered dot dithering (order 8 - 16x16 matrix)
+ FID_BAYER16x16 = 6 // Bayer ordered dispersed dot dithering (order 4 dithering matrix)
+};
+
+/** Lossless JPEG transformations
+Constants used in FreeImage_JPEGTransform
+*/
+FI_ENUM(FREE_IMAGE_JPEG_OPERATION) {
+ FIJPEG_OP_NONE = 0, // no transformation
+ FIJPEG_OP_FLIP_H = 1, // horizontal flip
+ FIJPEG_OP_FLIP_V = 2, // vertical flip
+ FIJPEG_OP_TRANSPOSE = 3, // transpose across UL-to-LR axis
+ FIJPEG_OP_TRANSVERSE = 4, // transpose across UR-to-LL axis
+ FIJPEG_OP_ROTATE_90 = 5, // 90-degree clockwise rotation
+ FIJPEG_OP_ROTATE_180 = 6, // 180-degree rotation
+ FIJPEG_OP_ROTATE_270 = 7 // 270-degree clockwise (or 90 ccw)
+};
+
+/** Tone mapping operators.
+Constants used in FreeImage_ToneMapping.
+*/
+FI_ENUM(FREE_IMAGE_TMO) {
+ FITMO_DRAGO03 = 0, // Adaptive logarithmic mapping (F. Drago, 2003)
+ FITMO_REINHARD05 = 1, // Dynamic range reduction inspired by photoreceptor physiology (E. Reinhard, 2005)
+};
+
+/** Upsampling / downsampling filters.
+Constants used in FreeImage_Rescale.
+*/
+FI_ENUM(FREE_IMAGE_FILTER) {
+ FILTER_BOX = 0, // Box, pulse, Fourier window, 1st order (constant) b-spline
+ FILTER_BICUBIC = 1, // Mitchell & Netravali's two-param cubic filter
+ FILTER_BILINEAR = 2, // Bilinear filter
+ FILTER_BSPLINE = 3, // 4th order (cubic) b-spline
+ FILTER_CATMULLROM = 4, // Catmull-Rom spline, Overhauser spline
+ FILTER_LANCZOS3 = 5 // Lanczos3 filter
+};
+
+/** Color channels.
+Constants used in color manipulation routines.
+*/
+FI_ENUM(FREE_IMAGE_COLOR_CHANNEL) {
+ FICC_RGB = 0, // Use red, green and blue channels
+ FICC_RED = 1, // Use red channel
+ FICC_GREEN = 2, // Use green channel
+ FICC_BLUE = 3, // Use blue channel
+ FICC_ALPHA = 4, // Use alpha channel
+ FICC_BLACK = 5, // Use black channel
+ FICC_REAL = 6, // Complex images: use real part
+ FICC_IMAG = 7, // Complex images: use imaginary part
+ FICC_MAG = 8, // Complex images: use magnitude
+ FICC_PHASE = 9 // Complex images: use phase
+};
+
+// Metadata support ---------------------------------------------------------
+
+/**
+ Tag data type information (based on TIFF specifications)
+
+ Note: RATIONALs are the ratio of two 32-bit integer values.
+*/
+FI_ENUM(FREE_IMAGE_MDTYPE) {
+ FIDT_NOTYPE = 0, // placeholder
+ FIDT_BYTE = 1, // 8-bit unsigned integer
+ FIDT_ASCII = 2, // 8-bit bytes w/ last byte null
+ FIDT_SHORT = 3, // 16-bit unsigned integer
+ FIDT_LONG = 4, // 32-bit unsigned integer
+ FIDT_RATIONAL = 5, // 64-bit unsigned fraction
+ FIDT_SBYTE = 6, // 8-bit signed integer
+ FIDT_UNDEFINED = 7, // 8-bit untyped data
+ FIDT_SSHORT = 8, // 16-bit signed integer
+ FIDT_SLONG = 9, // 32-bit signed integer
+ FIDT_SRATIONAL = 10, // 64-bit signed fraction
+ FIDT_FLOAT = 11, // 32-bit IEEE floating point
+ FIDT_DOUBLE = 12, // 64-bit IEEE floating point
+ FIDT_IFD = 13, // 32-bit unsigned integer (offset)
+ FIDT_PALETTE = 14 // 32-bit RGBQUAD
+};
+
+/**
+ Metadata models supported by FreeImage
+*/
+FI_ENUM(FREE_IMAGE_MDMODEL) {
+ FIMD_NODATA = -1,
+ FIMD_COMMENTS = 0, // single comment or keywords
+ FIMD_EXIF_MAIN = 1, // Exif-TIFF metadata
+ FIMD_EXIF_EXIF = 2, // Exif-specific metadata
+ FIMD_EXIF_GPS = 3, // Exif GPS metadata
+ FIMD_EXIF_MAKERNOTE = 4, // Exif maker note metadata
+ FIMD_EXIF_INTEROP = 5, // Exif interoperability metadata
+ FIMD_IPTC = 6, // IPTC/NAA metadata
+ FIMD_XMP = 7, // Abobe XMP metadata
+ FIMD_GEOTIFF = 8, // GeoTIFF metadata
+ FIMD_ANIMATION = 9, // Animation metadata
+ FIMD_CUSTOM = 10 // Used to attach other metadata types to a dib
+};
+
+/**
+ Handle to a metadata model
+*/
+FI_STRUCT (FIMETADATA) { void *data; };
+
+/**
+ Handle to a FreeImage tag
+*/
+FI_STRUCT (FITAG) { void *data; };
+
+// File IO routines ---------------------------------------------------------
+
+#ifndef FREEIMAGE_IO
+#define FREEIMAGE_IO
+
+typedef void* fi_handle;
+typedef unsigned (DLL_CALLCONV *FI_ReadProc) (void *buffer, unsigned size, unsigned count, fi_handle handle);
+typedef unsigned (DLL_CALLCONV *FI_WriteProc) (void *buffer, unsigned size, unsigned count, fi_handle handle);
+typedef int (DLL_CALLCONV *FI_SeekProc) (fi_handle handle, long offset, int origin);
+typedef long (DLL_CALLCONV *FI_TellProc) (fi_handle handle);
+
+#if (defined(_WIN32) || defined(__WIN32__))
+#pragma pack(push, 1)
+#else
+#pragma pack(1)
+#endif // WIN32
+
+FI_STRUCT(FreeImageIO) {
+ FI_ReadProc read_proc; // pointer to the function used to read data
+ FI_WriteProc write_proc; // pointer to the function used to write data
+ FI_SeekProc seek_proc; // pointer to the function used to seek
+ FI_TellProc tell_proc; // pointer to the function used to aquire the current position
+};
+
+#if (defined(_WIN32) || defined(__WIN32__))
+#pragma pack(pop)
+#else
+#pragma pack()
+#endif // WIN32
+
+/**
+Handle to a memory I/O stream
+*/
+FI_STRUCT (FIMEMORY) { void *data; };
+
+#endif // FREEIMAGE_IO
+
+// Plugin routines ----------------------------------------------------------
+
+#ifndef PLUGINS
+#define PLUGINS
+
+typedef const char *(DLL_CALLCONV *FI_FormatProc) ();
+typedef const char *(DLL_CALLCONV *FI_DescriptionProc) ();
+typedef const char *(DLL_CALLCONV *FI_ExtensionListProc) ();
+typedef const char *(DLL_CALLCONV *FI_RegExprProc) ();
+typedef void *(DLL_CALLCONV *FI_OpenProc)(FreeImageIO *io, fi_handle handle, BOOL read);
+typedef void (DLL_CALLCONV *FI_CloseProc)(FreeImageIO *io, fi_handle handle, void *data);
+typedef int (DLL_CALLCONV *FI_PageCountProc)(FreeImageIO *io, fi_handle handle, void *data);
+typedef int (DLL_CALLCONV *FI_PageCapabilityProc)(FreeImageIO *io, fi_handle handle, void *data);
+typedef FIBITMAP *(DLL_CALLCONV *FI_LoadProc)(FreeImageIO *io, fi_handle handle, int page, int flags, void *data);
+typedef BOOL (DLL_CALLCONV *FI_SaveProc)(FreeImageIO *io, FIBITMAP *dib, fi_handle handle, int page, int flags, void *data);
+typedef BOOL (DLL_CALLCONV *FI_ValidateProc)(FreeImageIO *io, fi_handle handle);
+typedef const char *(DLL_CALLCONV *FI_MimeProc) ();
+typedef BOOL (DLL_CALLCONV *FI_SupportsExportBPPProc)(int bpp);
+typedef BOOL (DLL_CALLCONV *FI_SupportsExportTypeProc)(FREE_IMAGE_TYPE type);
+typedef BOOL (DLL_CALLCONV *FI_SupportsICCProfilesProc)();
+
+FI_STRUCT (Plugin) {
+ FI_FormatProc format_proc;
+ FI_DescriptionProc description_proc;
+ FI_ExtensionListProc extension_proc;
+ FI_RegExprProc regexpr_proc;
+ FI_OpenProc open_proc;
+ FI_CloseProc close_proc;
+ FI_PageCountProc pagecount_proc;
+ FI_PageCapabilityProc pagecapability_proc;
+ FI_LoadProc load_proc;
+ FI_SaveProc save_proc;
+ FI_ValidateProc validate_proc;
+ FI_MimeProc mime_proc;
+ FI_SupportsExportBPPProc supports_export_bpp_proc;
+ FI_SupportsExportTypeProc supports_export_type_proc;
+ FI_SupportsICCProfilesProc supports_icc_profiles_proc;
+};
+
+typedef void (DLL_CALLCONV *FI_InitProc)(Plugin *plugin, int format_id);
+
+#endif // PLUGINS
+
+
+// Load / Save flag constants -----------------------------------------------
+
+#define BMP_DEFAULT 0
+#define BMP_SAVE_RLE 1
+#define CUT_DEFAULT 0
+#define DDS_DEFAULT 0
+#define FAXG3_DEFAULT 0
+#define GIF_DEFAULT 0
+#define GIF_LOAD256 1 // Load the image as a 256 color image with ununsed palette entries, if it's 16 or 2 color
+#define GIF_PLAYBACK 2 // 'Play' the GIF to generate each frame (as 32bpp) instead of returning raw frame data when loading
+#define HDR_DEFAULT 0
+#define ICO_DEFAULT 0
+#define ICO_MAKEALPHA 1 // convert to 32bpp and create an alpha channel from the AND-mask when loading
+#define IFF_DEFAULT 0
+#define JPEG_DEFAULT 0 // loading (see JPEG_FAST); saving (see JPEG_QUALITYGOOD)
+#define JPEG_FAST 0x0001 // load the file as fast as possible, sacrificing some quality
+#define JPEG_ACCURATE 0x0002 // load the file with the best quality, sacrificing some speed
+#define JPEG_CMYK 0x0004 // load separated CMYK "as is" (use | to combine with other load flags)
+#define JPEG_QUALITYSUPERB 0x80 // save with superb quality (100:1)
+#define JPEG_QUALITYGOOD 0x0100 // save with good quality (75:1)
+#define JPEG_QUALITYNORMAL 0x0200 // save with normal quality (50:1)
+#define JPEG_QUALITYAVERAGE 0x0400 // save with average quality (25:1)
+#define JPEG_QUALITYBAD 0x0800 // save with bad quality (10:1)
+#define JPEG_PROGRESSIVE 0x2000 // save as a progressive-JPEG (use | to combine with other save flags)
+#define KOALA_DEFAULT 0
+#define LBM_DEFAULT 0
+#define MNG_DEFAULT 0
+#define PCD_DEFAULT 0
+#define PCD_BASE 1 // load the bitmap sized 768 x 512
+#define PCD_BASEDIV4 2 // load the bitmap sized 384 x 256
+#define PCD_BASEDIV16 3 // load the bitmap sized 192 x 128
+#define PCX_DEFAULT 0
+#define PNG_DEFAULT 0
+#define PNG_IGNOREGAMMA 1 // avoid gamma correction
+#define PNM_DEFAULT 0
+#define PNM_SAVE_RAW 0 // If set the writer saves in RAW format (i.e. P4, P5 or P6)
+#define PNM_SAVE_ASCII 1 // If set the writer saves in ASCII format (i.e. P1, P2 or P3)
+#define PSD_DEFAULT 0
+#define RAS_DEFAULT 0
+#define SGI_DEFAULT 0
+#define TARGA_DEFAULT 0
+#define TARGA_LOAD_RGB888 1 // If set the loader converts RGB555 and ARGB8888 -> RGB888.
+#define TIFF_DEFAULT 0
+#define TIFF_CMYK 0x0001 // reads/stores tags for separated CMYK (use | to combine with compression flags)
+#define TIFF_PACKBITS 0x0100 // save using PACKBITS compression
+#define TIFF_DEFLATE 0x0200 // save using DEFLATE compression (a.k.a. ZLIB compression)
+#define TIFF_ADOBE_DEFLATE 0x0400 // save using ADOBE DEFLATE compression
+#define TIFF_NONE 0x0800 // save without any compression
+#define TIFF_CCITTFAX3 0x1000 // save using CCITT Group 3 fax encoding
+#define TIFF_CCITTFAX4 0x2000 // save using CCITT Group 4 fax encoding
+#define TIFF_LZW 0x4000 // save using LZW compression
+#define TIFF_JPEG 0x8000 // save using JPEG compression
+#define WBMP_DEFAULT 0
+#define XBM_DEFAULT 0
+#define XPM_DEFAULT 0
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Init / Error routines ----------------------------------------------------
+
+DLL_API void DLL_CALLCONV FreeImage_Initialise(BOOL load_local_plugins_only FI_DEFAULT(FALSE));
+DLL_API void DLL_CALLCONV FreeImage_DeInitialise(void);
+
+// Version routines ---------------------------------------------------------
+
+DLL_API const char *DLL_CALLCONV FreeImage_GetVersion(void);
+DLL_API const char *DLL_CALLCONV FreeImage_GetCopyrightMessage(void);
+
+// Message output functions -------------------------------------------------
+
+DLL_API void DLL_CALLCONV FreeImage_OutputMessageProc(int fif, const char *fmt, ...);
+
+typedef void (*FreeImage_OutputMessageFunction)(FREE_IMAGE_FORMAT fif, const char *msg);
+DLL_API void DLL_CALLCONV FreeImage_SetOutputMessage(FreeImage_OutputMessageFunction omf);
+
+// Allocate / Clone / Unload routines ---------------------------------------
+
+DLL_API FIBITMAP *DLL_CALLCONV FreeImage_Allocate(int width, int height, int bpp, unsigned red_mask FI_DEFAULT(0), unsigned green_mask FI_DEFAULT(0), unsigned blue_mask FI_DEFAULT(0));
+DLL_API FIBITMAP *DLL_CALLCONV FreeImage_AllocateT(FREE_IMAGE_TYPE type, int width, int height, int bpp FI_DEFAULT(8), unsigned red_mask FI_DEFAULT(0), unsigned green_mask FI_DEFAULT(0), unsigned blue_mask FI_DEFAULT(0));
+DLL_API FIBITMAP * DLL_CALLCONV FreeImage_Clone(FIBITMAP *dib);
+DLL_API void DLL_CALLCONV FreeImage_Unload(FIBITMAP *dib);
+
+// Load / Save routines -----------------------------------------------------
+
+DLL_API FIBITMAP *DLL_CALLCONV FreeImage_Load(FREE_IMAGE_FORMAT fif, const char *filename, int flags FI_DEFAULT(0));
+DLL_API FIBITMAP *DLL_CALLCONV FreeImage_LoadU(FREE_IMAGE_FORMAT fif, const wchar_t *filename, int flags FI_DEFAULT(0));
+DLL_API FIBITMAP *DLL_CALLCONV FreeImage_LoadFromHandle(FREE_IMAGE_FORMAT fif, FreeImageIO *io, fi_handle handle, int flags FI_DEFAULT(0));
+DLL_API BOOL DLL_CALLCONV FreeImage_Save(FREE_IMAGE_FORMAT fif, FIBITMAP *dib, const char *filename, int flags FI_DEFAULT(0));
+DLL_API BOOL DLL_CALLCONV FreeImage_SaveU(FREE_IMAGE_FORMAT fif, FIBITMAP *dib, const wchar_t *filename, int flags FI_DEFAULT(0));
+DLL_API BOOL DLL_CALLCONV FreeImage_SaveToHandle(FREE_IMAGE_FORMAT fif, FIBITMAP *dib, FreeImageIO *io, fi_handle handle, int flags FI_DEFAULT(0));
+
+// Memory I/O stream routines -----------------------------------------------
+
+DLL_API FIMEMORY *DLL_CALLCONV FreeImage_OpenMemory(BYTE *data FI_DEFAULT(0), DWORD size_in_bytes FI_DEFAULT(0));
+DLL_API void DLL_CALLCONV FreeImage_CloseMemory(FIMEMORY *stream);
+DLL_API FIBITMAP *DLL_CALLCONV FreeImage_LoadFromMemory(FREE_IMAGE_FORMAT fif, FIMEMORY *stream, int flags FI_DEFAULT(0));
+DLL_API BOOL DLL_CALLCONV FreeImage_SaveToMemory(FREE_IMAGE_FORMAT fif, FIBITMAP *dib, FIMEMORY *stream, int flags FI_DEFAULT(0));
+DLL_API long DLL_CALLCONV FreeImage_TellMemory(FIMEMORY *stream);
+DLL_API BOOL DLL_CALLCONV FreeImage_SeekMemory(FIMEMORY *stream, long offset, int origin);
+DLL_API BOOL DLL_CALLCONV FreeImage_AcquireMemory(FIMEMORY *stream, BYTE **data, DWORD *size_in_bytes);
+DLL_API unsigned DLL_CALLCONV FreeImage_ReadMemory(void *buffer, unsigned size, unsigned count, FIMEMORY *stream);
+DLL_API unsigned DLL_CALLCONV FreeImage_WriteMemory(const void *buffer, unsigned size, unsigned count, FIMEMORY *stream);
+DLL_API FIMULTIBITMAP *DLL_CALLCONV FreeImage_LoadMultiBitmapFromMemory(FREE_IMAGE_FORMAT fif, FIMEMORY *stream, int flags FI_DEFAULT(0));
+
+// Plugin Interface ---------------------------------------------------------
+
+DLL_API FREE_IMAGE_FORMAT DLL_CALLCONV FreeImage_RegisterLocalPlugin(FI_InitProc proc_address, const char *format FI_DEFAULT(0), const char *description FI_DEFAULT(0), const char *extension FI_DEFAULT(0), const char *regexpr FI_DEFAULT(0));
+DLL_API FREE_IMAGE_FORMAT DLL_CALLCONV FreeImage_RegisterExternalPlugin(const char *path, const char *format FI_DEFAULT(0), const char *description FI_DEFAULT(0), const char *extension FI_DEFAULT(0), const char *regexpr FI_DEFAULT(0));
+DLL_API int DLL_CALLCONV FreeImage_GetFIFCount(void);
+DLL_API int DLL_CALLCONV FreeImage_SetPluginEnabled(FREE_IMAGE_FORMAT fif, BOOL enable);
+DLL_API int DLL_CALLCONV FreeImage_IsPluginEnabled(FREE_IMAGE_FORMAT fif);
+DLL_API FREE_IMAGE_FORMAT DLL_CALLCONV FreeImage_GetFIFFromFormat(const char *format);
+DLL_API FREE_IMAGE_FORMAT DLL_CALLCONV FreeImage_GetFIFFromMime(const char *mime);
+DLL_API const char *DLL_CALLCONV FreeImage_GetFormatFromFIF(FREE_IMAGE_FORMAT fif);
+DLL_API const char *DLL_CALLCONV FreeImage_GetFIFExtensionList(FREE_IMAGE_FORMAT fif);
+DLL_API const char *DLL_CALLCONV FreeImage_GetFIFDescription(FREE_IMAGE_FORMAT fif);
+DLL_API const char *DLL_CALLCONV FreeImage_GetFIFRegExpr(FREE_IMAGE_FORMAT fif);
+DLL_API const char *DLL_CALLCONV FreeImage_GetFIFMimeType(FREE_IMAGE_FORMAT fif);
+DLL_API FREE_IMAGE_FORMAT DLL_CALLCONV FreeImage_GetFIFFromFilename(const char *filename);
+DLL_API FREE_IMAGE_FORMAT DLL_CALLCONV FreeImage_GetFIFFromFilenameU(const wchar_t *filename);
+DLL_API BOOL DLL_CALLCONV FreeImage_FIFSupportsReading(FREE_IMAGE_FORMAT fif);
+DLL_API BOOL DLL_CALLCONV FreeImage_FIFSupportsWriting(FREE_IMAGE_FORMAT fif);
+DLL_API BOOL DLL_CALLCONV FreeImage_FIFSupportsExportBPP(FREE_IMAGE_FORMAT fif, int bpp);
+DLL_API BOOL DLL_CALLCONV FreeImage_FIFSupportsExportType(FREE_IMAGE_FORMAT fif, FREE_IMAGE_TYPE type);
+DLL_API BOOL DLL_CALLCONV FreeImage_FIFSupportsICCProfiles(FREE_IMAGE_FORMAT fif);
+
+// Multipaging interface ----------------------------------------------------
+
+DLL_API FIMULTIBITMAP * DLL_CALLCONV FreeImage_OpenMultiBitmap(FREE_IMAGE_FORMAT fif, const char *filename, BOOL create_new, BOOL read_only, BOOL keep_cache_in_memory FI_DEFAULT(FALSE), int flags FI_DEFAULT(0));
+DLL_API BOOL DLL_CALLCONV FreeImage_CloseMultiBitmap(FIMULTIBITMAP *bitmap, int flags FI_DEFAULT(0));
+DLL_API int DLL_CALLCONV FreeImage_GetPageCount(FIMULTIBITMAP *bitmap);
+DLL_API void DLL_CALLCONV FreeImage_AppendPage(FIMULTIBITMAP *bitmap, FIBITMAP *data);
+DLL_API void DLL_CALLCONV FreeImage_InsertPage(FIMULTIBITMAP *bitmap, int page, FIBITMAP *data);
+DLL_API void DLL_CALLCONV FreeImage_DeletePage(FIMULTIBITMAP *bitmap, int page);
+DLL_API FIBITMAP * DLL_CALLCONV FreeImage_LockPage(FIMULTIBITMAP *bitmap, int page);
+DLL_API void DLL_CALLCONV FreeImage_UnlockPage(FIMULTIBITMAP *bitmap, FIBITMAP *data, BOOL changed);
+DLL_API BOOL DLL_CALLCONV FreeImage_MovePage(FIMULTIBITMAP *bitmap, int target, int source);
+DLL_API BOOL DLL_CALLCONV FreeImage_GetLockedPageNumbers(FIMULTIBITMAP *bitmap, int *pages, int *count);
+
+// Filetype request routines ------------------------------------------------
+
+DLL_API FREE_IMAGE_FORMAT DLL_CALLCONV FreeImage_GetFileType(const char *filename, int size FI_DEFAULT(0));
+DLL_API FREE_IMAGE_FORMAT DLL_CALLCONV FreeImage_GetFileTypeU(const wchar_t *filename, int size FI_DEFAULT(0));
+DLL_API FREE_IMAGE_FORMAT DLL_CALLCONV FreeImage_GetFileTypeFromHandle(FreeImageIO *io, fi_handle handle, int size FI_DEFAULT(0));
+DLL_API FREE_IMAGE_FORMAT DLL_CALLCONV FreeImage_GetFileTypeFromMemory(FIMEMORY *stream, int size FI_DEFAULT(0));
+
+// Image type request routine -----------------------------------------------
+
+DLL_API FREE_IMAGE_TYPE DLL_CALLCONV FreeImage_GetImageType(FIBITMAP *dib);
+
+// FreeImage helper routines ------------------------------------------------
+
+DLL_API BOOL DLL_CALLCONV FreeImage_IsLittleEndian(void);
+DLL_API BOOL DLL_CALLCONV FreeImage_LookupX11Color(const char *szColor, BYTE *nRed, BYTE *nGreen, BYTE *nBlue);
+DLL_API BOOL DLL_CALLCONV FreeImage_LookupSVGColor(const char *szColor, BYTE *nRed, BYTE *nGreen, BYTE *nBlue);
+
+
+// Pixel access routines ----------------------------------------------------
+
+DLL_API BYTE *DLL_CALLCONV FreeImage_GetBits(FIBITMAP *dib);
+DLL_API BYTE *DLL_CALLCONV FreeImage_GetScanLine(FIBITMAP *dib, int scanline);
+
+DLL_API BOOL DLL_CALLCONV FreeImage_GetPixelIndex(FIBITMAP *dib, unsigned x, unsigned y, BYTE *value);
+DLL_API BOOL DLL_CALLCONV FreeImage_GetPixelColor(FIBITMAP *dib, unsigned x, unsigned y, RGBQUAD *value);
+DLL_API BOOL DLL_CALLCONV FreeImage_SetPixelIndex(FIBITMAP *dib, unsigned x, unsigned y, BYTE *value);
+DLL_API BOOL DLL_CALLCONV FreeImage_SetPixelColor(FIBITMAP *dib, unsigned x, unsigned y, RGBQUAD *value);
+
+// DIB info routines --------------------------------------------------------
+
+DLL_API unsigned DLL_CALLCONV FreeImage_GetColorsUsed(FIBITMAP *dib);
+DLL_API unsigned DLL_CALLCONV FreeImage_GetBPP(FIBITMAP *dib);
+DLL_API unsigned DLL_CALLCONV FreeImage_GetWidth(FIBITMAP *dib);
+DLL_API unsigned DLL_CALLCONV FreeImage_GetHeight(FIBITMAP *dib);
+DLL_API unsigned DLL_CALLCONV FreeImage_GetLine(FIBITMAP *dib);
+DLL_API unsigned DLL_CALLCONV FreeImage_GetPitch(FIBITMAP *dib);
+DLL_API unsigned DLL_CALLCONV FreeImage_GetDIBSize(FIBITMAP *dib);
+DLL_API RGBQUAD *DLL_CALLCONV FreeImage_GetPalette(FIBITMAP *dib);
+
+DLL_API unsigned DLL_CALLCONV FreeImage_GetDotsPerMeterX(FIBITMAP *dib);
+DLL_API unsigned DLL_CALLCONV FreeImage_GetDotsPerMeterY(FIBITMAP *dib);
+DLL_API void DLL_CALLCONV FreeImage_SetDotsPerMeterX(FIBITMAP *dib, unsigned res);
+DLL_API void DLL_CALLCONV FreeImage_SetDotsPerMeterY(FIBITMAP *dib, unsigned res);
+
+DLL_API BITMAPINFOHEADER *DLL_CALLCONV FreeImage_GetInfoHeader(FIBITMAP *dib);
+DLL_API BITMAPINFO *DLL_CALLCONV FreeImage_GetInfo(FIBITMAP *dib);
+DLL_API FREE_IMAGE_COLOR_TYPE DLL_CALLCONV FreeImage_GetColorType(FIBITMAP *dib);
+
+DLL_API unsigned DLL_CALLCONV FreeImage_GetRedMask(FIBITMAP *dib);
+DLL_API unsigned DLL_CALLCONV FreeImage_GetGreenMask(FIBITMAP *dib);
+DLL_API unsigned DLL_CALLCONV FreeImage_GetBlueMask(FIBITMAP *dib);
+
+DLL_API unsigned DLL_CALLCONV FreeImage_GetTransparencyCount(FIBITMAP *dib);
+DLL_API BYTE * DLL_CALLCONV FreeImage_GetTransparencyTable(FIBITMAP *dib);
+DLL_API void DLL_CALLCONV FreeImage_SetTransparent(FIBITMAP *dib, BOOL enabled);
+DLL_API void DLL_CALLCONV FreeImage_SetTransparencyTable(FIBITMAP *dib, BYTE *table, int count);
+DLL_API BOOL DLL_CALLCONV FreeImage_IsTransparent(FIBITMAP *dib);
+
+DLL_API BOOL DLL_CALLCONV FreeImage_HasBackgroundColor(FIBITMAP *dib);
+DLL_API BOOL DLL_CALLCONV FreeImage_GetBackgroundColor(FIBITMAP *dib, RGBQUAD *bkcolor);
+DLL_API BOOL DLL_CALLCONV FreeImage_SetBackgroundColor(FIBITMAP *dib, RGBQUAD *bkcolor);
+
+
+// ICC profile routines -----------------------------------------------------
+
+DLL_API FIICCPROFILE *DLL_CALLCONV FreeImage_GetICCProfile(FIBITMAP *dib);
+DLL_API FIICCPROFILE *DLL_CALLCONV FreeImage_CreateICCProfile(FIBITMAP *dib, void *data, long size);
+DLL_API void DLL_CALLCONV FreeImage_DestroyICCProfile(FIBITMAP *dib);
+
+// Line conversion routines -------------------------------------------------
+
+DLL_API void DLL_CALLCONV FreeImage_ConvertLine1To4(BYTE *target, BYTE *source, int width_in_pixels);
+DLL_API void DLL_CALLCONV FreeImage_ConvertLine8To4(BYTE *target, BYTE *source, int width_in_pixels, RGBQUAD *palette);
+DLL_API void DLL_CALLCONV FreeImage_ConvertLine16To4_555(BYTE *target, BYTE *source, int width_in_pixels);
+DLL_API void DLL_CALLCONV FreeImage_ConvertLine16To4_565(BYTE *target, BYTE *source, int width_in_pixels);
+DLL_API void DLL_CALLCONV FreeImage_ConvertLine24To4(BYTE *target, BYTE *source, int width_in_pixels);
+DLL_API void DLL_CALLCONV FreeImage_ConvertLine32To4(BYTE *target, BYTE *source, int width_in_pixels);
+DLL_API void DLL_CALLCONV FreeImage_ConvertLine1To8(BYTE *target, BYTE *source, int width_in_pixels);
+DLL_API void DLL_CALLCONV FreeImage_ConvertLine4To8(BYTE *target, BYTE *source, int width_in_pixels);
+DLL_API void DLL_CALLCONV FreeImage_ConvertLine16To8_555(BYTE *target, BYTE *source, int width_in_pixels);
+DLL_API void DLL_CALLCONV FreeImage_ConvertLine16To8_565(BYTE *target, BYTE *source, int width_in_pixels);
+DLL_API void DLL_CALLCONV FreeImage_ConvertLine24To8(BYTE *target, BYTE *source, int width_in_pixels);
+DLL_API void DLL_CALLCONV FreeImage_ConvertLine32To8(BYTE *target, BYTE *source, int width_in_pixels);
+DLL_API void DLL_CALLCONV FreeImage_ConvertLine1To16_555(BYTE *target, BYTE *source, int width_in_pixels, RGBQUAD *palette);
+DLL_API void DLL_CALLCONV FreeImage_ConvertLine4To16_555(BYTE *target, BYTE *source, int width_in_pixels, RGBQUAD *palette);
+DLL_API void DLL_CALLCONV FreeImage_ConvertLine8To16_555(BYTE *target, BYTE *source, int width_in_pixels, RGBQUAD *palette);
+DLL_API void DLL_CALLCONV FreeImage_ConvertLine16_565_To16_555(BYTE *target, BYTE *source, int width_in_pixels);
+DLL_API void DLL_CALLCONV FreeImage_ConvertLine24To16_555(BYTE *target, BYTE *source, int width_in_pixels);
+DLL_API void DLL_CALLCONV FreeImage_ConvertLine32To16_555(BYTE *target, BYTE *source, int width_in_pixels);
+DLL_API void DLL_CALLCONV FreeImage_ConvertLine1To16_565(BYTE *target, BYTE *source, int width_in_pixels, RGBQUAD *palette);
+DLL_API void DLL_CALLCONV FreeImage_ConvertLine4To16_565(BYTE *target, BYTE *source, int width_in_pixels, RGBQUAD *palette);
+DLL_API void DLL_CALLCONV FreeImage_ConvertLine8To16_565(BYTE *target, BYTE *source, int width_in_pixels, RGBQUAD *palette);
+DLL_API void DLL_CALLCONV FreeImage_ConvertLine16_555_To16_565(BYTE *target, BYTE *source, int width_in_pixels);
+DLL_API void DLL_CALLCONV FreeImage_ConvertLine24To16_565(BYTE *target, BYTE *source, int width_in_pixels);
+DLL_API void DLL_CALLCONV FreeImage_ConvertLine32To16_565(BYTE *target, BYTE *source, int width_in_pixels);
+DLL_API void DLL_CALLCONV FreeImage_ConvertLine1To24(BYTE *target, BYTE *source, int width_in_pixels, RGBQUAD *palette);
+DLL_API void DLL_CALLCONV FreeImage_ConvertLine4To24(BYTE *target, BYTE *source, int width_in_pixels, RGBQUAD *palette);
+DLL_API void DLL_CALLCONV FreeImage_ConvertLine8To24(BYTE *target, BYTE *source, int width_in_pixels, RGBQUAD *palette);
+DLL_API void DLL_CALLCONV FreeImage_ConvertLine16To24_555(BYTE *target, BYTE *source, int width_in_pixels);
+DLL_API void DLL_CALLCONV FreeImage_ConvertLine16To24_565(BYTE *target, BYTE *source, int width_in_pixels);
+DLL_API void DLL_CALLCONV FreeImage_ConvertLine32To24(BYTE *target, BYTE *source, int width_in_pixels);
+DLL_API void DLL_CALLCONV FreeImage_ConvertLine1To32(BYTE *target, BYTE *source, int width_in_pixels, RGBQUAD *palette);
+DLL_API void DLL_CALLCONV FreeImage_ConvertLine4To32(BYTE *target, BYTE *source, int width_in_pixels, RGBQUAD *palette);
+DLL_API void DLL_CALLCONV FreeImage_ConvertLine8To32(BYTE *target, BYTE *source, int width_in_pixels, RGBQUAD *palette);
+DLL_API void DLL_CALLCONV FreeImage_ConvertLine16To32_555(BYTE *target, BYTE *source, int width_in_pixels);
+DLL_API void DLL_CALLCONV FreeImage_ConvertLine16To32_565(BYTE *target, BYTE *source, int width_in_pixels);
+DLL_API void DLL_CALLCONV FreeImage_ConvertLine24To32(BYTE *target, BYTE *source, int width_in_pixels);
+
+// Smart conversion routines ------------------------------------------------
+
+DLL_API FIBITMAP *DLL_CALLCONV FreeImage_ConvertTo4Bits(FIBITMAP *dib);
+DLL_API FIBITMAP *DLL_CALLCONV FreeImage_ConvertTo8Bits(FIBITMAP *dib);
+DLL_API FIBITMAP *DLL_CALLCONV FreeImage_ConvertToGreyscale(FIBITMAP *dib);
+DLL_API FIBITMAP *DLL_CALLCONV FreeImage_ConvertTo16Bits555(FIBITMAP *dib);
+DLL_API FIBITMAP *DLL_CALLCONV FreeImage_ConvertTo16Bits565(FIBITMAP *dib);
+DLL_API FIBITMAP *DLL_CALLCONV FreeImage_ConvertTo24Bits(FIBITMAP *dib);
+DLL_API FIBITMAP *DLL_CALLCONV FreeImage_ConvertTo32Bits(FIBITMAP *dib);
+DLL_API FIBITMAP *DLL_CALLCONV FreeImage_ColorQuantize(FIBITMAP *dib, FREE_IMAGE_QUANTIZE quantize);
+DLL_API FIBITMAP *DLL_CALLCONV FreeImage_ColorQuantizeEx(FIBITMAP *dib, FREE_IMAGE_QUANTIZE quantize FI_DEFAULT(FIQ_WUQUANT), int PaletteSize FI_DEFAULT(256), int ReserveSize FI_DEFAULT(0), RGBQUAD *ReservePalette FI_DEFAULT(NULL));
+DLL_API FIBITMAP *DLL_CALLCONV FreeImage_Threshold(FIBITMAP *dib, BYTE T);
+DLL_API FIBITMAP *DLL_CALLCONV FreeImage_Dither(FIBITMAP *dib, FREE_IMAGE_DITHER algorithm);
+
+DLL_API FIBITMAP *DLL_CALLCONV FreeImage_ConvertFromRawBits(BYTE *bits, int width, int height, int pitch, unsigned bpp, unsigned red_mask, unsigned green_mask, unsigned blue_mask, BOOL topdown FI_DEFAULT(FALSE));
+DLL_API void DLL_CALLCONV FreeImage_ConvertToRawBits(BYTE *bits, FIBITMAP *dib, int pitch, unsigned bpp, unsigned red_mask, unsigned green_mask, unsigned blue_mask, BOOL topdown FI_DEFAULT(FALSE));
+
+DLL_API FIBITMAP *DLL_CALLCONV FreeImage_ConvertToRGBF(FIBITMAP *dib);
+
+DLL_API FIBITMAP *DLL_CALLCONV FreeImage_ConvertToStandardType(FIBITMAP *src, BOOL scale_linear FI_DEFAULT(TRUE));
+DLL_API FIBITMAP *DLL_CALLCONV FreeImage_ConvertToType(FIBITMAP *src, FREE_IMAGE_TYPE dst_type, BOOL scale_linear FI_DEFAULT(TRUE));
+
+// tone mapping operators
+DLL_API FIBITMAP *DLL_CALLCONV FreeImage_ToneMapping(FIBITMAP *dib, FREE_IMAGE_TMO tmo, double first_param FI_DEFAULT(0), double second_param FI_DEFAULT(0));
+DLL_API FIBITMAP* DLL_CALLCONV FreeImage_TmoDrago03(FIBITMAP *src, double gamma FI_DEFAULT(2.2), double exposure FI_DEFAULT(0));
+DLL_API FIBITMAP* DLL_CALLCONV FreeImage_TmoReinhard05(FIBITMAP *src, double intensity FI_DEFAULT(0), double contrast FI_DEFAULT(0));
+
+// ZLib interface -----------------------------------------------------------
+
+DLL_API DWORD DLL_CALLCONV FreeImage_ZLibCompress(BYTE *target, DWORD target_size, BYTE *source, DWORD source_size);
+DLL_API DWORD DLL_CALLCONV FreeImage_ZLibUncompress(BYTE *target, DWORD target_size, BYTE *source, DWORD source_size);
+DLL_API DWORD DLL_CALLCONV FreeImage_ZLibGZip(BYTE *target, DWORD target_size, BYTE *source, DWORD source_size);
+DLL_API DWORD DLL_CALLCONV FreeImage_ZLibGUnzip(BYTE *target, DWORD target_size, BYTE *source, DWORD source_size);
+DLL_API DWORD DLL_CALLCONV FreeImage_ZLibCRC32(DWORD crc, BYTE *source, DWORD source_size);
+
+// --------------------------------------------------------------------------
+// Metadata routines --------------------------------------------------------
+// --------------------------------------------------------------------------
+
+// tag creation / destruction
+DLL_API FITAG *DLL_CALLCONV FreeImage_CreateTag();
+DLL_API void DLL_CALLCONV FreeImage_DeleteTag(FITAG *tag);
+DLL_API FITAG *DLL_CALLCONV FreeImage_CloneTag(FITAG *tag);
+
+// tag getters and setters
+DLL_API const char *DLL_CALLCONV FreeImage_GetTagKey(FITAG *tag);
+DLL_API const char *DLL_CALLCONV FreeImage_GetTagDescription(FITAG *tag);
+DLL_API WORD DLL_CALLCONV FreeImage_GetTagID(FITAG *tag);
+DLL_API FREE_IMAGE_MDTYPE DLL_CALLCONV FreeImage_GetTagType(FITAG *tag);
+DLL_API DWORD DLL_CALLCONV FreeImage_GetTagCount(FITAG *tag);
+DLL_API DWORD DLL_CALLCONV FreeImage_GetTagLength(FITAG *tag);
+DLL_API const void *DLL_CALLCONV FreeImage_GetTagValue(FITAG *tag);
+
+DLL_API BOOL DLL_CALLCONV FreeImage_SetTagKey(FITAG *tag, const char *key);
+DLL_API BOOL DLL_CALLCONV FreeImage_SetTagDescription(FITAG *tag, const char *description);
+DLL_API BOOL DLL_CALLCONV FreeImage_SetTagID(FITAG *tag, WORD id);
+DLL_API BOOL DLL_CALLCONV FreeImage_SetTagType(FITAG *tag, FREE_IMAGE_MDTYPE type);
+DLL_API BOOL DLL_CALLCONV FreeImage_SetTagCount(FITAG *tag, DWORD count);
+DLL_API BOOL DLL_CALLCONV FreeImage_SetTagLength(FITAG *tag, DWORD length);
+DLL_API BOOL DLL_CALLCONV FreeImage_SetTagValue(FITAG *tag, const void *value);
+
+// iterator
+DLL_API FIMETADATA *DLL_CALLCONV FreeImage_FindFirstMetadata(FREE_IMAGE_MDMODEL model, FIBITMAP *dib, FITAG **tag);
+DLL_API BOOL DLL_CALLCONV FreeImage_FindNextMetadata(FIMETADATA *mdhandle, FITAG **tag);
+DLL_API void DLL_CALLCONV FreeImage_FindCloseMetadata(FIMETADATA *mdhandle);
+
+// metadata setter and getter
+DLL_API BOOL DLL_CALLCONV FreeImage_SetMetadata(FREE_IMAGE_MDMODEL model, FIBITMAP *dib, const char *key, FITAG *tag);
+DLL_API BOOL DLL_CALLCONV FreeImage_GetMetadata(FREE_IMAGE_MDMODEL model, FIBITMAP *dib, const char *key, FITAG **tag);
+
+// helpers
+DLL_API unsigned DLL_CALLCONV FreeImage_GetMetadataCount(FREE_IMAGE_MDMODEL model, FIBITMAP *dib);
+
+// tag to C string conversion
+DLL_API const char* DLL_CALLCONV FreeImage_TagToString(FREE_IMAGE_MDMODEL model, FITAG *tag, char *Make FI_DEFAULT(NULL));
+
+// --------------------------------------------------------------------------
+// Image manipulation toolkit -----------------------------------------------
+// --------------------------------------------------------------------------
+
+// rotation and flipping
+DLL_API FIBITMAP *DLL_CALLCONV FreeImage_RotateClassic(FIBITMAP *dib, double angle);
+DLL_API FIBITMAP *DLL_CALLCONV FreeImage_RotateEx(FIBITMAP *dib, double angle, double x_shift, double y_shift, double x_origin, double y_origin, BOOL use_mask);
+DLL_API BOOL DLL_CALLCONV FreeImage_FlipHorizontal(FIBITMAP *dib);
+DLL_API BOOL DLL_CALLCONV FreeImage_FlipVertical(FIBITMAP *dib);
+DLL_API BOOL DLL_CALLCONV FreeImage_JPEGTransform(const char *src_file, const char *dst_file, FREE_IMAGE_JPEG_OPERATION operation, BOOL perfect FI_DEFAULT(FALSE));
+
+// upsampling / downsampling
+DLL_API FIBITMAP *DLL_CALLCONV FreeImage_Rescale(FIBITMAP *dib, int dst_width, int dst_height, FREE_IMAGE_FILTER filter);
+DLL_API FIBITMAP *DLL_CALLCONV FreeImage_MakeThumbnail(FIBITMAP *dib, int max_pixel_size, BOOL convert FI_DEFAULT(TRUE));
+
+// color manipulation routines (point operations)
+DLL_API BOOL DLL_CALLCONV FreeImage_AdjustCurve(FIBITMAP *dib, BYTE *LUT, FREE_IMAGE_COLOR_CHANNEL channel);
+DLL_API BOOL DLL_CALLCONV FreeImage_AdjustGamma(FIBITMAP *dib, double gamma);
+DLL_API BOOL DLL_CALLCONV FreeImage_AdjustBrightness(FIBITMAP *dib, double percentage);
+DLL_API BOOL DLL_CALLCONV FreeImage_AdjustContrast(FIBITMAP *dib, double percentage);
+DLL_API BOOL DLL_CALLCONV FreeImage_Invert(FIBITMAP *dib);
+DLL_API BOOL DLL_CALLCONV FreeImage_GetHistogram(FIBITMAP *dib, DWORD *histo, FREE_IMAGE_COLOR_CHANNEL channel FI_DEFAULT(FICC_BLACK));
+
+// channel processing routines
+DLL_API FIBITMAP *DLL_CALLCONV FreeImage_GetChannel(FIBITMAP *dib, FREE_IMAGE_COLOR_CHANNEL channel);
+DLL_API BOOL DLL_CALLCONV FreeImage_SetChannel(FIBITMAP *dib, FIBITMAP *dib8, FREE_IMAGE_COLOR_CHANNEL channel);
+DLL_API FIBITMAP *DLL_CALLCONV FreeImage_GetComplexChannel(FIBITMAP *src, FREE_IMAGE_COLOR_CHANNEL channel);
+DLL_API BOOL DLL_CALLCONV FreeImage_SetComplexChannel(FIBITMAP *dst, FIBITMAP *src, FREE_IMAGE_COLOR_CHANNEL channel);
+
+// copy / paste / composite routines
+DLL_API FIBITMAP *DLL_CALLCONV FreeImage_Copy(FIBITMAP *dib, int left, int top, int right, int bottom);
+DLL_API BOOL DLL_CALLCONV FreeImage_Paste(FIBITMAP *dst, FIBITMAP *src, int left, int top, int alpha);
+DLL_API FIBITMAP *DLL_CALLCONV FreeImage_Composite(FIBITMAP *fg, BOOL useFileBkg FI_DEFAULT(FALSE), RGBQUAD *appBkColor FI_DEFAULT(NULL), FIBITMAP *bg FI_DEFAULT(NULL));
+DLL_API BOOL DLL_CALLCONV FreeImage_JPEGCrop(const char *src_file, const char *dst_file, int left, int top, int right, int bottom);
+
+
+// restore the borland-specific enum size option
+#if defined(__BORLANDC__)
+#pragma option pop
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // FREEIMAGE_H
diff --git a/Plugins/avatarhistory/sdk/m_imgsrvc.h b/Plugins/avatarhistory/sdk/m_imgsrvc.h
new file mode 100644
index 0000000..923f88e
--- /dev/null
+++ b/Plugins/avatarhistory/sdk/m_imgsrvc.h
@@ -0,0 +1,502 @@
+/*
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2007 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+-----------------------------------------------------------------------
+Miranda Image services plugin / API definitions
+Provides various services for image loading, saving and manipulations.
+
+This module is based on the freeimage library, copyrighted by the FreeImage
+Project members.
+
+Miranda plugin code (c) 2007 by Nightwish, silvercircle@gmail.com, all else (C)
+by the FreeImage project (http://freeimage.sourceforge.net)
+
+*/
+
+#ifndef __M_IMGSRVC_H
+#define __M_IMGSRVC_H
+
+#define _FI_MIMPLUGIN 1
+
+#include "m_freeimage.h"
+
+#define FI_IF_VERSION (PLUGIN_MAKE_VERSION(0, 0, 1, 0)) // interface version - must match
+
+// memory i/o defs
+
+/*
+ * this struct defines a memio job.
+ * datalen and filename must match and must be populated to the size of the memory buffer (caution)
+ * data must point to the buffer.
+ * curpos is internal and should be initialized to 0
+ */
+
+typedef struct fiio_mem_handle_s {
+ long filelen,datalen,curpos;
+ void *data;
+} fiio_mem_handle;
+
+/* it is up to the user to create a fiio_mem_handle and init datalen and data
+ * filelen will be pre-set to 0 by SaveToMem
+ * curpos will be pre-set to 0 by SaveToMem and LoadFromMem
+ * IMPORTANT: data should be set to NULL and datalen to 0,
+ * unless the user wants to manually malloc a larger buffer
+ */
+FIBITMAP *FreeImage_LoadFromMem(FREE_IMAGE_FORMAT fif, fiio_mem_handle *handle, int flags);
+BOOL FreeImage_SaveToMem(FREE_IMAGE_FORMAT fif, FIBITMAP *dib, fiio_mem_handle *handle, int flags);
+
+void SetMemIO(FreeImageIO *io);
+unsigned __stdcall fiio_mem_ReadProc(void *buffer, unsigned size, unsigned count, fi_handle handle);
+unsigned __stdcall fiio_mem_WriteProc(void *buffer, unsigned size, unsigned count, fi_handle handle);
+int __stdcall fiio_mem_SeekProc(fi_handle handle, long offset, int origin);
+long __stdcall fiio_mem_TellProc(fi_handle handle);
+
+/*
+ * this interface directly exports most of FreeImage routines
+ * you can use them in your plugin after obtaining the interfasce using MS_IMG_GETINTERFACE
+ */
+
+typedef struct _tagFI_interface {
+
+ DWORD version;
+
+ FIBITMAP *(DLL_CALLCONV *FI_Allocate)(int width, int height, int bpp, unsigned red_mask FI_DEFAULT(0), unsigned green_mask FI_DEFAULT(0), unsigned blue_mask FI_DEFAULT(0));
+ FIBITMAP *(DLL_CALLCONV *FI__AllocateT)(FREE_IMAGE_TYPE type, int width, int height, int bpp FI_DEFAULT(8), unsigned red_mask FI_DEFAULT(0), unsigned green_mask FI_DEFAULT(0), unsigned blue_mask FI_DEFAULT(0));
+ FIBITMAP *(DLL_CALLCONV *FI_Clone)(FIBITMAP *dib);
+ void (DLL_CALLCONV *FI_Unload)(FIBITMAP *dib);
+
+ // Load / Save routines -----------------------------------------------------
+
+ FIBITMAP *(DLL_CALLCONV *FI_Load)(FREE_IMAGE_FORMAT fif, const char *filename, int flags FI_DEFAULT(0));
+ FIBITMAP *(DLL_CALLCONV *FI_LoadU)(FREE_IMAGE_FORMAT fif, const wchar_t *filename, int flags FI_DEFAULT(0));
+ FIBITMAP *(DLL_CALLCONV *FI_LoadFromHandle)(FREE_IMAGE_FORMAT fif, FreeImageIO *io, fi_handle handle, int flags FI_DEFAULT(0));
+ BOOL (DLL_CALLCONV *FI_Save)(FREE_IMAGE_FORMAT fif, FIBITMAP *dib, const char *filename, int flags FI_DEFAULT(0));
+ BOOL (DLL_CALLCONV *FI_SaveU)(FREE_IMAGE_FORMAT fif, FIBITMAP *dib, const wchar_t *filename, int flags FI_DEFAULT(0));
+ BOOL (DLL_CALLCONV *FI_SaveToHandle)(FREE_IMAGE_FORMAT fif, FIBITMAP *dib, FreeImageIO *io, fi_handle handle, int flags FI_DEFAULT(0));
+
+// Memory I/O stream routines -----------------------------------------------
+
+ FIMEMORY *(DLL_CALLCONV *FI_OpenMemory)(BYTE *data FI_DEFAULT(0), DWORD size_in_bytes FI_DEFAULT(0));
+ void (DLL_CALLCONV *FI_CloseMemory)(FIMEMORY *stream);
+ FIBITMAP *(DLL_CALLCONV *FI_LoadFromMemory)(FREE_IMAGE_FORMAT fif, FIMEMORY *stream, int flags FI_DEFAULT(0));
+ BOOL (DLL_CALLCONV *FI_SaveToMemory)(FREE_IMAGE_FORMAT fif, FIBITMAP *dib, FIMEMORY *stream, int flags FI_DEFAULT(0));
+ long (DLL_CALLCONV *FI_TellMemory)(FIMEMORY *stream);
+ BOOL (DLL_CALLCONV *FI_SeekMemory)(FIMEMORY *stream, long offset, int origin);
+ BOOL (DLL_CALLCONV *FI_AcquireMemory)(FIMEMORY *stream, BYTE **data, DWORD *size_in_bytes);
+ unsigned (DLL_CALLCONV *FI_ReadMemory)(void *buffer, unsigned size, unsigned count, FIMEMORY *stream);
+ unsigned (DLL_CALLCONV *FI_WriteMemory)(const void *buffer, unsigned size, unsigned count, FIMEMORY *stream);
+ FIMULTIBITMAP *(DLL_CALLCONV *FI_LoadMultiBitmapFromMemory)(FREE_IMAGE_FORMAT fif, FIMEMORY *stream, int flags FI_DEFAULT(0));
+
+// Plugin Interface ---------------------------------------------------------
+
+ FREE_IMAGE_FORMAT (DLL_CALLCONV *FI_RegisterLocalPlugin)(FI_InitProc proc_address, const char *format FI_DEFAULT(0), const char *description FI_DEFAULT(0), const char *extension FI_DEFAULT(0), const char *regexpr FI_DEFAULT(0));
+ FREE_IMAGE_FORMAT (DLL_CALLCONV *FI_RegisterExternalPlugin)(const char *path, const char *format FI_DEFAULT(0), const char *description FI_DEFAULT(0), const char *extension FI_DEFAULT(0), const char *regexpr FI_DEFAULT(0));
+ int (DLL_CALLCONV *FI_GetFIFCount)(void);
+ int (DLL_CALLCONV *FI_SetPluginEnabled)(FREE_IMAGE_FORMAT fif, BOOL enable);
+ int (DLL_CALLCONV *FI_IsPluginEnabled)(FREE_IMAGE_FORMAT fif);
+ FREE_IMAGE_FORMAT (DLL_CALLCONV *FI_GetFIFFromFormat)(const char *format);
+ FREE_IMAGE_FORMAT (DLL_CALLCONV *FI_GetFIFFromMime)(const char *mime);
+ const char *(DLL_CALLCONV *FI_GetFormatFromFIF)(FREE_IMAGE_FORMAT fif);
+ const char *(DLL_CALLCONV *FI_GetFIFExtensionList)(FREE_IMAGE_FORMAT fif);
+ const char *(DLL_CALLCONV *FI_GetFIFDescription)(FREE_IMAGE_FORMAT fif);
+ const char *(DLL_CALLCONV *FI_GetFIFRegExpr)(FREE_IMAGE_FORMAT fif);
+ const char *(DLL_CALLCONV *FI_GetFIFMimeType)(FREE_IMAGE_FORMAT fif);
+ FREE_IMAGE_FORMAT (DLL_CALLCONV *FI_GetFIFFromFilename)(const char *filename);
+ FREE_IMAGE_FORMAT (DLL_CALLCONV *FI_GetFIFFromFilenameU)(const wchar_t *filename);
+ BOOL (DLL_CALLCONV *FI_FIFSupportsReading)(FREE_IMAGE_FORMAT fif);
+ BOOL (DLL_CALLCONV *FI_FIFSupportsWriting)(FREE_IMAGE_FORMAT fif);
+ BOOL (DLL_CALLCONV *FI_FIFSupportsExportBPP)(FREE_IMAGE_FORMAT fif, int bpp);
+ BOOL (DLL_CALLCONV *FI_FIFSupportsExportType)(FREE_IMAGE_FORMAT fif, FREE_IMAGE_TYPE type);
+ BOOL (DLL_CALLCONV *FI_FIFSupportsICCProfiles)(FREE_IMAGE_FORMAT fif);
+
+// Multipaging interface ----------------------------------------------------
+
+ FIMULTIBITMAP *(DLL_CALLCONV *FI_OpenMultiBitmap)(FREE_IMAGE_FORMAT fif, const char *filename, BOOL create_new, BOOL read_only, BOOL keep_cache_in_memory FI_DEFAULT(FALSE), int flags FI_DEFAULT(0));
+ BOOL (DLL_CALLCONV *FI_CloseMultiBitmap)(FIMULTIBITMAP *bitmap, int flags FI_DEFAULT(0));
+ int (DLL_CALLCONV *FI_GetPageCount)(FIMULTIBITMAP *bitmap);
+ void (DLL_CALLCONV *FI_AppendPage)(FIMULTIBITMAP *bitmap, FIBITMAP *data);
+ void (DLL_CALLCONV *FI_InsertPage)(FIMULTIBITMAP *bitmap, int page, FIBITMAP *data);
+ void (DLL_CALLCONV *FI_DeletePage)(FIMULTIBITMAP *bitmap, int page);
+ FIBITMAP *(DLL_CALLCONV *FI_LockPage)(FIMULTIBITMAP *bitmap, int page);
+ void (DLL_CALLCONV *FI_UnlockPage)(FIMULTIBITMAP *bitmap, FIBITMAP *data, BOOL changed);
+ BOOL (DLL_CALLCONV *FI_MovePage)(FIMULTIBITMAP *bitmap, int target, int source);
+ BOOL (DLL_CALLCONV *FI_GetLockedPageNumbers)(FIMULTIBITMAP *bitmap, int *pages, int *count);
+
+// Filetype request routines ------------------------------------------------
+
+ FREE_IMAGE_FORMAT (DLL_CALLCONV *FI_GetFileType)(const char *filename, int size FI_DEFAULT(0));
+ FREE_IMAGE_FORMAT (DLL_CALLCONV *FI_GetFileTypeU)(const wchar_t *filename, int size FI_DEFAULT(0));
+ FREE_IMAGE_FORMAT (DLL_CALLCONV *FI_GetFileTypeFromHandle)(FreeImageIO *io, fi_handle handle, int size FI_DEFAULT(0));
+ FREE_IMAGE_FORMAT (DLL_CALLCONV *FI_GetFileTypeFromMemory)(FIMEMORY *stream, int size FI_DEFAULT(0));
+
+// FreeImage helper routines ------------------------- MISSING !!!! TODO
+
+ BOOL (DLL_CALLCONV *FI_IsLittleEndian)(void);
+ BOOL (DLL_CALLCONV *FI_LookupX11Color)(const char *szColor, BYTE *nRed, BYTE *nGreen, BYTE *nBlue);
+ BOOL (DLL_CALLCONV *FI_LookupSVGColor)(const char *szColor, BYTE *nRed, BYTE *nGreen, BYTE *nBlue);
+
+// Image type request routine -----------------------------------------------
+
+ FREE_IMAGE_TYPE (DLL_CALLCONV *FI_GetImageType)(FIBITMAP *dib);
+
+// Pixel access routines ----------------------------------------------------
+
+ BYTE *(DLL_CALLCONV *FI_GetBits)(FIBITMAP *dib);
+ BYTE *(DLL_CALLCONV *FI_GetScanLine)(FIBITMAP *dib, int scanline);
+
+ BOOL (DLL_CALLCONV *FI_GetPixelIndex)(FIBITMAP *dib, unsigned x, unsigned y, BYTE *value);
+ BOOL (DLL_CALLCONV *FI_GetPixelColor)(FIBITMAP *dib, unsigned x, unsigned y, RGBQUAD *value);
+ BOOL (DLL_CALLCONV *FI_SetPixelIndex)(FIBITMAP *dib, unsigned x, unsigned y, BYTE *value);
+ BOOL (DLL_CALLCONV *FI_SetPixelColor)(FIBITMAP *dib, unsigned x, unsigned y, RGBQUAD *value);
+
+// DIB info routines --------------------------------------------------------
+
+ unsigned (DLL_CALLCONV *FI_GetColorsUsed)(FIBITMAP *dib);
+ unsigned (DLL_CALLCONV *FI_GetBPP)(FIBITMAP *dib);
+ unsigned (DLL_CALLCONV *FI_GetWidth)(FIBITMAP *dib);
+ unsigned (DLL_CALLCONV *FI_GetHeight)(FIBITMAP *dib);
+ unsigned (DLL_CALLCONV *FI_GetLine)(FIBITMAP *dib);
+ unsigned (DLL_CALLCONV *FI_GetPitch)(FIBITMAP *dib);
+ unsigned (DLL_CALLCONV *FI_GetDIBSize)(FIBITMAP *dib);
+ RGBQUAD *(DLL_CALLCONV *FI_GetPalette)(FIBITMAP *dib);
+
+ unsigned (DLL_CALLCONV *FI_GetDotsPerMeterX)(FIBITMAP *dib);
+ unsigned (DLL_CALLCONV *FI_GetDotsPerMeterY)(FIBITMAP *dib);
+ void (DLL_CALLCONV *FI_SetDotsPerMeterX)(FIBITMAP *dib, unsigned res);
+ void (DLL_CALLCONV *FI_SetDotsPerMeterY)(FIBITMAP *dib, unsigned res);
+
+ BITMAPINFOHEADER *(DLL_CALLCONV *FI_GetInfoHeader)(FIBITMAP *dib);
+ BITMAPINFO *(DLL_CALLCONV *FI_GetInfo)(FIBITMAP *dib);
+ FREE_IMAGE_COLOR_TYPE (DLL_CALLCONV *FI_GetColorType)(FIBITMAP *dib);
+
+ unsigned (DLL_CALLCONV *FI_GetRedMask)(FIBITMAP *dib);
+ unsigned (DLL_CALLCONV *FI_GetGreenMask)(FIBITMAP *dib);
+ unsigned (DLL_CALLCONV *FI_GetBlueMask)(FIBITMAP *dib);
+
+ unsigned (DLL_CALLCONV *FI_GetTransparencyCount)(FIBITMAP *dib);
+ BYTE *(DLL_CALLCONV *FI_GetTransparencyTable)(FIBITMAP *dib);
+ void (DLL_CALLCONV *FI_SetTransparent)(FIBITMAP *dib, BOOL enabled);
+ void (DLL_CALLCONV *FI_SetTransparencyTable)(FIBITMAP *dib, BYTE *table, int count);
+ BOOL (DLL_CALLCONV *FI_IsTransparent)(FIBITMAP *dib);
+
+ BOOL (DLL_CALLCONV *FI_HasBackgroundColor)(FIBITMAP *dib);
+ BOOL (DLL_CALLCONV *FI_GetBackgroundColor)(FIBITMAP *dib, RGBQUAD *bkcolor);
+ BOOL (DLL_CALLCONV *FI_SetBackgroundColor)(FIBITMAP *dib, RGBQUAD *bkcolor);
+
+ // ICC profile routines ------------------------------- MISSING !!! TODO
+
+ FIICCPROFILE *(DLL_CALLCONV *FI_GetICCProfile)(FIBITMAP *dib);
+ FIICCPROFILE *(DLL_CALLCONV *FI_CreateICCProfile)(FIBITMAP *dib, void *data, long size);
+ void (DLL_CALLCONV *FI_DestroyICCProfile)(FIBITMAP *dib);
+
+ // Line conversion routines ----------------------------MISSING !!! TODO
+
+// Smart conversion routines ------------------------------------------------
+
+ FIBITMAP *(DLL_CALLCONV *FI_ConvertTo4Bits)(FIBITMAP *dib);
+ FIBITMAP *(DLL_CALLCONV *FI_ConvertTo8Bits)(FIBITMAP *dib);
+ FIBITMAP *(DLL_CALLCONV *FI_ConvertToGreyscale)(FIBITMAP *dib);
+ FIBITMAP *(DLL_CALLCONV *FI_ConvertTo16Bits555)(FIBITMAP *dib);
+ FIBITMAP *(DLL_CALLCONV *FI_ConvertTo16Bits565)(FIBITMAP *dib);
+ FIBITMAP *(DLL_CALLCONV *FI_ConvertTo24Bits)(FIBITMAP *dib);
+ FIBITMAP *(DLL_CALLCONV *FI_ConvertTo32Bits)(FIBITMAP *dib);
+ FIBITMAP *(DLL_CALLCONV *FI_ColorQuantize)(FIBITMAP *dib, FREE_IMAGE_QUANTIZE quantize);
+
+ FIBITMAP *(DLL_CALLCONV *FI_ColorQuantizeEx)(FIBITMAP *dib, FREE_IMAGE_QUANTIZE quantize /*FI_DEFAULT(FIQ_WUQUANT) */, int PaletteSize FI_DEFAULT(256), int ReserveSize FI_DEFAULT(0), RGBQUAD *ReservePalette FI_DEFAULT(NULL));
+
+ FIBITMAP *(DLL_CALLCONV *FI_Threshold)(FIBITMAP *dib, BYTE T);
+ FIBITMAP *(DLL_CALLCONV *FI_Dither)(FIBITMAP *dib, FREE_IMAGE_DITHER algorithm);
+ FIBITMAP *(DLL_CALLCONV *FI_ConvertFromRawBits)(BYTE *bits, int width, int height, int pitch, unsigned bpp, unsigned red_mask, unsigned green_mask, unsigned blue_mask, BOOL topdown FI_DEFAULT(FALSE));
+ void (DLL_CALLCONV *FI_ConvertToRawBits)(BYTE *bits, FIBITMAP *dib, int pitch, unsigned bpp, unsigned red_mask, unsigned green_mask, unsigned blue_mask, BOOL topdown FI_DEFAULT(FALSE));
+
+ FIBITMAP *(DLL_CALLCONV *FI_ConvertToRGBF)(FIBITMAP *dib);
+ FIBITMAP *(DLL_CALLCONV *FI_ConvertToStandardType)(FIBITMAP *src, BOOL scale_linear FI_DEFAULT(TRUE));
+ FIBITMAP *(DLL_CALLCONV *FI_ConvertToType)(FIBITMAP *src, FREE_IMAGE_TYPE dst_type, BOOL scale_linear FI_DEFAULT(TRUE));
+
+// tone mapping operators
+ FIBITMAP *(DLL_CALLCONV *FI_ToneMapping)(FIBITMAP *dib, FREE_IMAGE_TMO tmo, double first_param FI_DEFAULT(0), double second_param FI_DEFAULT(0));
+ FIBITMAP *(DLL_CALLCONV *FI_TmoDrago03)(FIBITMAP *src, double gamma FI_DEFAULT(2.2), double exposure FI_DEFAULT(0));
+ FIBITMAP *(DLL_CALLCONV *FI_TmoReinhard05)(FIBITMAP *src, double intensity FI_DEFAULT(0), double contrast FI_DEFAULT(0));
+
+// ZLib interface -----------------------------------------------------------
+
+ DWORD (DLL_CALLCONV *FI_ZLibCompress)(BYTE *target, DWORD target_size, BYTE *source, DWORD source_size);
+ DWORD (DLL_CALLCONV *FI_ZLibUncompress)(BYTE *target, DWORD target_size, BYTE *source, DWORD source_size);
+ DWORD (DLL_CALLCONV *FI_ZLibGZip)(BYTE *target, DWORD target_size, BYTE *source, DWORD source_size);
+ DWORD (DLL_CALLCONV *FI_ZLibGUnzip)(BYTE *target, DWORD target_size, BYTE *source, DWORD source_size);
+ DWORD (DLL_CALLCONV *FI_ZLibCRC32)(DWORD crc, BYTE *source, DWORD source_size);
+
+
+// --------------------------------------------------------------------------
+// Metadata routines --------------------------------------------------------
+// --------------------------------------------------------------------------
+
+// tag creation / destruction
+ FITAG *(DLL_CALLCONV *FI_CreateTag)();
+ void (DLL_CALLCONV *FI_DeleteTag)(FITAG *tag);
+ FITAG *(DLL_CALLCONV *FI_CloneTag)(FITAG *tag);
+
+// tag getters and setters
+ const char *(DLL_CALLCONV *FI_GetTagKey)(FITAG *tag);
+ const char *(DLL_CALLCONV *FI_GetTagDescription)(FITAG *tag);
+ WORD (DLL_CALLCONV *FI_GetTagID)(FITAG *tag);
+ FREE_IMAGE_MDTYPE (DLL_CALLCONV *FI_GetTagType)(FITAG *tag);
+ DWORD (DLL_CALLCONV *FI_GetTagCount)(FITAG *tag);
+ DWORD (DLL_CALLCONV *FI_GetTagLength)(FITAG *tag);
+ const void *(DLL_CALLCONV *FI_GetTagValue)(FITAG *tag);
+
+ BOOL (DLL_CALLCONV *FI_SetTagKey)(FITAG *tag, const char *key);
+ BOOL (DLL_CALLCONV *FI_SetTagDescription)(FITAG *tag, const char *description);
+ BOOL (DLL_CALLCONV *FI_SetTagID)(FITAG *tag, WORD id);
+ BOOL (DLL_CALLCONV *FI_SetTagType)(FITAG *tag, FREE_IMAGE_MDTYPE type);
+ BOOL (DLL_CALLCONV *FI_SetTagCount)(FITAG *tag, DWORD count);
+ BOOL (DLL_CALLCONV *FI_SetTagLength)(FITAG *tag, DWORD length);
+ BOOL (DLL_CALLCONV *FI_SetTagValue)(FITAG *tag, const void *value);
+
+// iterator
+ FIMETADATA *(DLL_CALLCONV *FI_FindFirstMetadata)(FREE_IMAGE_MDMODEL model, FIBITMAP *dib, FITAG **tag);
+ BOOL (DLL_CALLCONV *FI_FindNextMetadata)(FIMETADATA *mdhandle, FITAG **tag);
+ void (DLL_CALLCONV *FI_FindCloseMetadata)(FIMETADATA *mdhandle);
+
+// metadata setter and getter
+ BOOL (DLL_CALLCONV *FI_SetMetadata)(FREE_IMAGE_MDMODEL model, FIBITMAP *dib, const char *key, FITAG *tag);
+ BOOL (DLL_CALLCONV *FI_GetMetadata)(FREE_IMAGE_MDMODEL model, FIBITMAP *dib, const char *key, FITAG **tag);
+
+// helpers
+ unsigned (DLL_CALLCONV *FI_GetMetadataCount)(FREE_IMAGE_MDMODEL model, FIBITMAP *dib);
+
+// tag to C string conversion
+ const char *(DLL_CALLCONV *FI_TagToString)(FREE_IMAGE_MDMODEL model, FITAG *tag, char *Make FI_DEFAULT(NULL));
+
+
+// --------------------------------------------------------------------------
+// Image manipulation toolkit -----------------------------------------------
+// --------------------------------------------------------------------------
+
+// rotation and flipping
+ FIBITMAP *(DLL_CALLCONV *FI_RotateClassic)(FIBITMAP *dib, double angle);
+ FIBITMAP *(DLL_CALLCONV *FI_RotateEx)(FIBITMAP *dib, double angle, double x_shift, double y_shift, double x_origin, double y_origin, BOOL use_mask);
+ BOOL (DLL_CALLCONV *FI_FlipHorizontal)(FIBITMAP *dib);
+ BOOL (DLL_CALLCONV *FI_FlipVertical)(FIBITMAP *dib);
+ BOOL (DLL_CALLCONV *FI_JPEGTransform)(const char *src_file, const char *dst_file, FREE_IMAGE_JPEG_OPERATION operation, BOOL perfect FI_DEFAULT(FALSE));
+
+// upsampling / downsampling
+ FIBITMAP *(DLL_CALLCONV *FI_Rescale)(FIBITMAP *dib, int dst_width, int dst_height, FREE_IMAGE_FILTER filter);
+ FIBITMAP *(DLL_CALLCONV *FI_MakeThumbnail)(FIBITMAP *dib, int max_pixel_size, BOOL convert FI_DEFAULT(TRUE));
+
+// color manipulation routines (point operations)
+ BOOL (DLL_CALLCONV *FI_AdjustCurve)(FIBITMAP *dib, BYTE *LUT, FREE_IMAGE_COLOR_CHANNEL channel);
+ BOOL (DLL_CALLCONV *FI_AdjustGamma)(FIBITMAP *dib, double gamma);
+ BOOL (DLL_CALLCONV *FI_AdjustBrightness)(FIBITMAP *dib, double percentage);
+ BOOL (DLL_CALLCONV *FI_AdjustContrast)(FIBITMAP *dib, double percentage);
+ BOOL (DLL_CALLCONV *FI_Invert)(FIBITMAP *dib);
+ BOOL (DLL_CALLCONV *FI_GetHistogram)(FIBITMAP *dib, DWORD *histo, FREE_IMAGE_COLOR_CHANNEL channel);
+
+// channel processing routines
+ FIBITMAP *(DLL_CALLCONV *FI_GetChannel)(FIBITMAP *dib, FREE_IMAGE_COLOR_CHANNEL channel);
+ BOOL (DLL_CALLCONV *FI_SetChannel)(FIBITMAP *dib, FIBITMAP *dib8, FREE_IMAGE_COLOR_CHANNEL channel);
+ FIBITMAP *(DLL_CALLCONV *FI_GetComplexChannel)(FIBITMAP *src, FREE_IMAGE_COLOR_CHANNEL channel);
+ BOOL (DLL_CALLCONV *FI_SetComplexChannel)(FIBITMAP *dst, FIBITMAP *src, FREE_IMAGE_COLOR_CHANNEL channel);
+
+// copy / paste / composite routines
+ FIBITMAP *(DLL_CALLCONV *FI_Copy)(FIBITMAP *dib, int left, int top, int right, int bottom);
+ BOOL (DLL_CALLCONV *FI_Paste)(FIBITMAP *dst, FIBITMAP *src, int left, int top, int alpha);
+ FIBITMAP *(DLL_CALLCONV *FI_Composite)(FIBITMAP *fg, BOOL useFileBkg FI_DEFAULT(FALSE), RGBQUAD *appBkColor FI_DEFAULT(NULL), FIBITMAP *bg FI_DEFAULT(NULL));
+ BOOL (DLL_CALLCONV *FI_JPEGCrop)(const char *src_file, const char *dst_file, int left, int top, int right, int bottom);
+
+// own functions
+
+ // memory I/O
+ FIBITMAP *(*FI_LoadFromMem)(FREE_IMAGE_FORMAT fif, fiio_mem_handle *handle, int flags);
+ BOOL (*FI_SaveToMem)(FREE_IMAGE_FORMAT fif, FIBITMAP *dib, fiio_mem_handle *handle, int flags);
+
+ // helpers
+ FIBITMAP *(*FI_CreateDIBFromHBITMAP)(HBITMAP hBmp);
+ HBITMAP (*FI_CreateHBITMAPFromDIB)(FIBITMAP *dib);
+ BOOL (*FI_Premultiply)(HBITMAP hBmp); // premultiplies alpha channel for usage with AlphaBlend()
+ // original HBITMAP stays valid and must be 32bit RGBA
+ int (*FI_BmpFilterResizeBitmap)(WPARAM wParam,LPARAM lParam); // more generic resizer for avatar images
+ void (*FI_CorrectBitmap32Alpha)(HBITMAP hBitmap, BOOL force); // corrects broken images (when all alpha values are 0)
+
+ BYTE reserved[200]; // future usage
+} FI_INTERFACE;
+
+/*
+ * image services
+ *
+ * only basic functionality is wrapped around Miranda services, because otherwise we would get a huge
+ * load of services with complex parameter marshalling requirements.
+ */
+
+// get the interface version number
+// wParam = lParam = 0
+// returns FI_IF_VERSION
+
+#define MS_IMG_GETIFVERSION "IMG/GetVersion"
+
+// obtain the full FreeImage interface from the library. This interface provides full access to freeimage
+// internal functions, thus enabling devs to fully utilize the FreeImage API. Only popular functions will
+// be exported as miranda services.
+// wParam = (DWORD)version Number // the caller MUST provide the desired version number
+// lParam = **FI_INTERFACE
+// returns S_OK on success, any other value indicates a problem.
+// the interface is populated during the _DllMain() handler, so you can assume it is ready when Miranda
+// calls the Load() handler of your plugin and you can return 1 in your Load() to disable your plugin when
+// it depends on the freeimage interface and the freeimage plugin is missing
+//
+// example:
+//
+// FI_INTERFACE *fii = NULL;
+//
+// result = CallService(MS_IMG_GETINTERFACE, FI_IF_VERSION, (LPARAM)&fii);
+//
+// if(result != S_OK)
+// failed, in this case, fei will be NULL and your plugin will crash when using the interface,
+// so ALWAYS check it
+
+#define MS_IMG_GETINTERFACE "IMG/GetInterface"
+
+
+#define IMGL_RETURNDIB 1 // will NOT return a HBITMAP but a FIBITMAP * instead (useful, if you
+ // want to do further image manipulations before converting to a Win32 bitmap
+ // caller MUST then free the FIBITMAP * using fii->FI_Unload() or MS_IMG_UNLOAD (see below)
+
+#define IMGL_WCHAR 2 // filename is wchar_t
+
+#if defined(UNICODE) || defined(_UNICODE)
+ #define IMGL_TCHAR IMGL_WCHAR
+#else
+ #define IMGL_TCHAR 0
+#endif
+
+// load an image from disk
+// wParam = full path and filename to the image
+// lParam = IMGL_* flags
+// returns a valid HBITMAP or 0 if image cannot be loaded
+// if IMGL_RETURNDIB is set, it returns a pointer to a freeimage bitmap (FIBITMAP *)
+
+#define MS_IMG_LOAD "IMG/Load"
+
+/*
+ * control structure for loading images from memory buffers (e.g. network buffers, memory mapped files).
+ */
+
+typedef struct _tagIMGSRVC_MEMIO {
+ long iLen; // length of the buffer
+ void *pBuf; // the buffer itself (you are responsible for allocating and free'ing it)
+ FREE_IMAGE_FORMAT fif; // the FIF_* image format constant. Make sure to provide the right one.
+ UINT flags; // flags to pass to FreeImage_LoadFromMem() (see freeimage docs)
+} IMGSRVC_MEMIO;
+
+// load an image from a memory buffer
+// wParam = IMGSRVC_MEMIO *
+// lParam = flags (see IMG/Load), valid are IMGL_RETURNDIB
+// you must popupate iLen (buffer length) and pBuf (pointer to memory buffer)
+// you must also specify the format in IMGSRVC_MEMIO.fif using one of the FIF_* constants defined in m_freeimage.h
+
+#define MS_IMG_LOADFROMMEM "IMG/LoadFromMem"
+
+// flags for IMGSRVC_INFO.dwMask
+
+#define IMGI_FBITMAP 1 // the dib member is valid
+#define IMGI_HBITMAP 2 // the hbm member is valid
+
+/*
+ * generic structure for various img functions
+ * you must populate the fields as required, set the mask bits to indicate which member is valid
+ */
+
+typedef struct _tagIMGSRVC_INFO {
+ DWORD cbSize;
+ union {
+ char *szName;
+ wchar_t *wszName;
+ };
+ HBITMAP hbm;
+ FIBITMAP *dib;
+ DWORD dwMask;
+ FREE_IMAGE_FORMAT fif;
+} IMGSRVC_INFO;
+
+// save image to disk
+// wParam = IMGSRVC_INFO * (szName/wszName, hbm OR dib, cbSize, dwMask mandatory. fif optional, if FIF_UNKNOWN is given
+// it will be determined from the filename).
+// lParam = IMG_* flags (IMGL_WCHAR is the only valid - filename will be assumed to be wchar_t and wszName must be used)
+// set IMGSRVC_INFO.dwMask to indicate whether the HBITMAP of FIBITMAP member is valid
+
+#define MS_IMG_SAVE "IMG/Save"
+
+// unload a FIFBITMAP
+// wParam = FIFBITMAP *
+// lParam = 0;
+// this service is useful when you have loaded a bitmap with IMGL_RETURNDIB in which case you do not get
+// a HBITMAP but instead a FBITMAP * which describes the freeimage-internal representation of a bitmap.
+
+#define MS_IMG_UNLOAD "IMG/Unload"
+
+/*
+ * resizer from loadavatars moved to image service plugin
+*/
+
+#define RESIZEBITMAP_STRETCH 0 // Distort bitmap to size in (max_width, max_height)
+#define RESIZEBITMAP_KEEP_PROPORTIONS 1 // Keep bitmap proportions (probabily only one of the
+ // max_width/max_height will be respected, and the other will be
+ // smaller)
+#define RESIZEBITMAP_CROP 2 // Keep bitmap proportions but crop it to fix exactly in (max_width, max_height)
+ // Some image info outside will be lost
+#define RESIZEBITMAP_MAKE_SQUARE 3 // Image will be allways square. Image will be croped and the size
+ // returned will be min(max_width, max_height)
+
+#define RESIZEBITMAP_FLAG_DONT_GROW 0x1000 // If set, the image will not grow. Else, it will grow to fit the max width/height
+
+typedef struct {
+ size_t size; // sizeof(ResizeBitmap);
+
+ HBITMAP hBmp;
+
+ int max_width;
+ int max_height;
+
+ int fit; // One of: RESIZEBITMAP_*
+} ResizeBitmap;
+
+// Returns a copy of the bitmap with the size especified or the original bitmap if nothing has to be changed
+// wParam = ResizeBitmap *
+// lParam = NULL
+// after return, compare the returned HBITMAP with the original. You are responsible for calling DestroyObject()
+// on the original HBITMAP
+
+#define MS_IMG_RESIZE "IMG/ResizeBitmap"
+
+
+/*
+ * format conversion helpers
+ *
+ * these helper macros allow converting HBITMAP to FIBITMAP * format and vice vera. In any case,
+ * the caller is responsible for freeing or deleting the original object.
+ * These macros wrap around the FI_CreateHBITMAPFromDib() and FI_CreateDIBFromHBITMAP() interface
+ * functions.
+ */
+
+//#define FI_HBM2DIB(x) (FI_CreateDIBFromHBITMAP((x)))
+//#define FI_DIB2HBM(x) (FI_CreateHBITMAPFromDIB((x)))
+
+#endif // __M_IMGSRVC_H
diff --git a/Plugins/avatarhistory/sdk/m_metacontacts.h b/Plugins/avatarhistory/sdk/m_metacontacts.h
new file mode 100644
index 0000000..1da12b9
--- /dev/null
+++ b/Plugins/avatarhistory/sdk/m_metacontacts.h
@@ -0,0 +1,162 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright © 2004 Universite Louis PASTEUR, STRASBOURG.
+Copyright © 2004 Scott Ellis (www.scottellis.com.au mail@scottellis.com.au)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef M_METACONTACTS_H__
+#define M_METACONTACTS_H__ 1
+
+//get the handle for a contact's parent metacontact
+//wParam=(HANDLE)hSubContact
+//lParam=0
+//returns a handle to the parent metacontact, or null if this contact is not a subcontact
+#define MS_MC_GETMETACONTACT "MetaContacts/GetMeta"
+
+//gets the handle for the default contact
+//wParam=(HANDLE)hMetaContact
+//lParam=0
+//returns a handle to the default contact, or null on failure
+#define MS_MC_GETDEFAULTCONTACT "MetaContacts/GetDefault"
+
+//gets the contact number for the default contact
+//wParam=(HANDLE)hMetaContact
+//lParam=0
+//returns a DWORD contact number, or -1 on failure
+#define MS_MC_GETDEFAULTCONTACTNUM "MetaContacts/GetDefaultNum"
+
+//gets the handle for the 'most online' contact
+//wParam=(HANDLE)hMetaContact
+//lParam=0
+//returns a handle to the 'most online' contact
+#define MS_MC_GETMOSTONLINECONTACT "MetaContacts/GetMostOnline"
+
+//gets the number of subcontacts for a metacontact
+//wParam=(HANDLE)hMetaContact
+//lParam=0
+//returns a DWORD representing the number of subcontacts for the given metacontact
+#define MS_MC_GETNUMCONTACTS "MetaContacts/GetNumContacts"
+
+//gets the handle of a subcontact, using the subcontact's number
+//wParam=(HANDLE)hMetaContact
+//lParam=(DWORD)contact number
+//returns a handle to the specified subcontact
+#define MS_MC_GETSUBCONTACT "MetaContacts/GetSubContact"
+
+//sets the default contact, using the subcontact's contact number
+//wParam=(HANDLE)hMetaContact
+//lParam=(DWORD)contact number
+//returns 0 on success
+#define MS_MC_SETDEFAULTCONTACTNUM "MetaContacts/SetDefault"
+
+//sets the default contact, using the subcontact's handle
+//wParam=(HANDLE)hMetaContact
+//lParam=(HANDLE)hSubcontact
+//returns 0 on success
+#define MS_MC_SETDEFAULTCONTACT "MetaContacts/SetDefaultByHandle"
+
+//forces the metacontact to send using a specific subcontact, using the subcontact's contact number
+//wParam=(HANDLE)hMetaContact
+//lParam=(DWORD)contact number
+//returns 0 on success
+#define MS_MC_FORCESENDCONTACTNUM "MetaContacts/ForceSendContact"
+
+//forces the metacontact to send using a specific subcontact, using the subcontact's handle
+//wParam=(HANDLE)hMetaContact
+//lParam=(HANDLE)hSubcontact
+//returns 0 on success (will fail if 'force default' is in effect)
+#define MS_MC_FORCESENDCONTACT "MetaContacts/ForceSendContactByHandle"
+
+//'unforces' the metacontact to send using a specific subcontact
+//wParam=(HANDLE)hMetaContact
+//lParam=0
+//returns 0 on success (will fail if 'force default' is in effect)
+#define MS_MC_UNFORCESENDCONTACT "MetaContacts/UnforceSendContact"
+
+//'forces' or 'unforces' (i.e. toggles) the metacontact to send using it's default contact
+// overrides (and clears) 'force send' above, and will even force use of offline contacts
+// will send ME_MC_FORCESEND or ME_MC_UNFORCESEND event
+//wParam=(HANDLE)hMetaContact
+//lParam=0
+//returns 1(true) or 0(false) representing new state of 'force default'
+#define MS_MC_FORCEDEFAULT "MetaContacts/ForceSendDefault"
+
+// method to get state of 'force' for a metacontact
+// wParam=(HANDLE)hMetaContact
+// lParam= (DWORD)&contact_number or NULL
+//
+// if lparam supplied, the contact_number of the contatct 'in force' will be copied to the address it points to,
+// or if none is in force, the value (DWORD)-1 will be copied
+// (v0.8.0.8+ returns 1 if 'force default' is true with *lParam == default contact number, else returns 0 with *lParam as above)
+#define MS_MC_GETFORCESTATE "MetaContacts/GetForceState"
+
+// fired when a metacontact's default contact changes (fired upon creation of metacontact also, when default is initially set)
+// wParam=(HANDLE)hMetaContact
+// lParam=(HANDLE)hDefaultContact
+#define ME_MC_DEFAULTTCHANGED "MetaContacts/DefaultChanged"
+
+// fired when a metacontact's subcontacts change (fired upon creation of metacontact, when contacts are added or removed, and when
+// contacts are reordered) - a signal to re-read metacontact data
+// wParam=(HANDLE)hMetaContact
+// lParam=0
+#define ME_MC_SUBCONTACTSCHANGED "MetaContacts/SubcontactsChanged"
+
+// fired when a metacontact is forced to send using a specific subcontact
+// wParam=(HANDLE)hMetaContact
+// lParam=(HANDLE)hForceContact
+#define ME_MC_FORCESEND "MetaContacts/ForceSend"
+
+// fired when a metacontact is 'unforced' to send using a specific subcontact
+// wParam=(HANDLE)hMetaContact
+// lParam=0
+#define ME_MC_UNFORCESEND "MetaContacts/UnforceSend"
+
+// method to get protocol name - used to be sure you're dealing with a "real" metacontacts plugin :)
+// wParam=lParam=0
+#define MS_MC_GETPROTOCOLNAME "MetaContacts/GetProtoName"
+
+
+// added 0.9.5.0 (22/3/05)
+// wParam=(HANDLE)hContact
+// lParam=0
+// convert a given contact into a metacontact
+#define MS_MC_CONVERTTOMETA "MetaContacts/ConvertToMetacontact"
+
+// added 0.9.5.0 (22/3/05)
+// wParam=(HANDLE)hContact
+// lParam=(HANDLE)hMeta
+// add an existing contact to a metacontact
+#define MS_MC_ADDTOMETA "MetaContacts/AddToMetacontact"
+
+// added 0.9.5.0 (22/3/05)
+// wParam=0
+// lParam=(HANDLE)hContact
+// remove a contact from a metacontact
+#define MS_MC_REMOVEFROMMETA "MetaContacts/RemoveFromMetacontact"
+
+
+// added 0.9.13.2 (6/10/05)
+// wParam=(BOOL)disable
+// lParam=0
+// enable/disable the 'hidden group hack' - for clists that support subcontact hiding using 'IsSubcontact' setting
+// should be called once in the clist 'onmodulesloaded' event handler (which, since it's loaded after the db, will be called
+// before the metacontact onmodulesloaded handler where the subcontact hiding is usually done)
+#define MS_MC_DISABLEHIDDENGROUP "MetaContacts/DisableHiddenGroup"
+
+#endif
diff --git a/Plugins/avatarhistory/sdk/m_updater.h b/Plugins/avatarhistory/sdk/m_updater.h
new file mode 100644
index 0000000..371b743
--- /dev/null
+++ b/Plugins/avatarhistory/sdk/m_updater.h
@@ -0,0 +1,146 @@
+#ifndef _M_UPDATER_H
+#define _M_UPDATER_H
+
+// NOTES:
+// - For langpack updates, include a string of the following format in the langpack text file:
+// ";FLID: <file listing name> <version>"
+// version must be four numbers seperated by '.', in the range 0-255 inclusive
+// - Updater will disable plugins that are downloaded but were not active prior to the update (this is so that, if an archive contains e.g. ansi and
+// unicode versions, the correct plugin will be the only one active after the new version is installed)...so if you add a support plugin, you may need
+// to install an ini file to make the plugin activate when miranda restarts after the update
+// - Updater will replace all dlls that have the same internal shortName as a downloaded update dll (this is so that msn1.dll and msn2.dll, for example,
+// will both be updated) - so if you have a unicode and a non-unicode version of a plugin in your archive, you should make the internal names different (which will break automatic
+// updates from the file listing if there is only one file listing entry for both versions, unless you use the 'MS_UPDATE_REGISTER' service below)
+// - Updater will install all files in the root of the archive into the plugins folder, except for langpack files that contain the FLID string which go into the root folder (same
+// folder as miranda32.exe)...all folders in the archive will also be copied to miranda's root folder, and their contents transferred into the new folders. The only exception is a
+// special folder called 'root_files' - if there is a folder by that name in the archive, it's contents will also be copied into miranda's root folder - this is intended to be used
+// to install additional dlls etc that a plugin may require)
+
+// if you set Update.szUpdateURL to the following value when registering, as well as setting your beta site and version data,
+// Updater will ignore szVersionURL and pbVersionPrefix, and attempt to find the file listing URL's from the backend XML data.
+// for this to work, the plugin name in pluginInfo.shortName must match the file listing exactly (except for case)
+#define UPDATER_AUTOREGISTER "UpdaterAUTOREGISTER"
+// Updater will also use the backend xml data if you provide URL's that reference the miranda file listing for updates (so you can use that method
+// if e.g. your plugin shortName does not match the file listing) - it will grab the file listing id from the end of these URLs
+
+typedef struct Update_tag {
+ int cbSize;
+ char *szComponentName; // component name as it will appear in the UI (will be translated before displaying)
+
+ char *szVersionURL; // URL where the current version can be found (NULL to disable)
+ BYTE *pbVersionPrefix; // bytes occuring in VersionURL before the version, used to locate the version information within the URL data
+ // (note that this URL could point at a binary file - dunno why, but it could :)
+ int cpbVersionPrefix; // number of bytes pointed to by pbVersionPrefix
+ char *szUpdateURL; // URL where dll/zip is located
+ // set to UPDATER_AUTOREGISTER if you want Updater to find the file listing URLs (ensure plugin shortName matches file listing!)
+
+ char *szBetaVersionURL; // URL where the beta version can be found (NULL to disable betas)
+ BYTE *pbBetaVersionPrefix; // bytes occuring in VersionURL before the version, used to locate the version information within the URL data
+ int cpbBetaVersionPrefix; // number of bytes pointed to by pbVersionPrefix
+ char *szBetaUpdateURL; // URL where dll/zip is located
+
+ BYTE *pbVersion; // bytes of current version, used for comparison with those in VersionURL
+ int cpbVersion; // number of bytes pointed to by pbVersion
+
+ char *szBetaChangelogURL; // url for displaying changelog for beta versions
+} Update;
+
+// register a comonent with Updater
+//
+// wparam = 0
+// lparam = (LPARAM)&Update
+#define MS_UPDATE_REGISTER "Update/Register"
+
+// utility functions to create a version string from a DWORD or from pluginInfo
+// point buf at a buffer at least 16 chars wide - but note the version string returned may be shorter
+//
+__inline static char *CreateVersionString(DWORD version, char *buf) {
+ mir_snprintf(buf, 16, "%d.%d.%d.%d", (version >> 24) & 0xFF, (version >> 16) & 0xFF, (version >> 8) & 0xFF, version & 0xFF);
+ return buf;
+}
+
+__inline static char *CreateVersionStringPlugin(PLUGININFO *pluginInfo, char *buf) {
+ return CreateVersionString(pluginInfo->version, buf);
+}
+
+
+// register the 'easy' way - use this method if you have no beta URL and the plugin is on the miranda file listing
+// NOTE: the plugin version string on the file listing must be the string version of the version in pluginInfo (i.e. 0.0.0.1,
+// four numbers between 0 and 255 inclusivem, so no letters, brackets, etc.)
+//
+// wParam = (int)fileID - this is the file ID from the file listing (i.e. the number at the end of the download link)
+// lParam = (PLUGININFO*)&pluginInfo
+#define MS_UPDATE_REGISTERFL "Update/RegisterFL"
+
+// this function can be used to 'unregister' components - useful for plugins that register non-plugin/langpack components and
+// may need to change those components on the fly
+// lParam = (char *)szComponentName
+#define MS_UPDATE_UNREGISTER "Update/Unregister"
+
+// this event is fired when the startup process is complete, but NOT if a restart is imminent
+// it is designed for status managment plugins to use as a trigger for beggining their own startup process
+// wParam = lParam = 0 (unused)
+// (added in version 0.1.6.0)
+#define ME_UPDATE_STARTUPDONE "Update/StartupDone"
+
+// this service can be used to enable/disable Updater's global status control
+// it can be called from the StartupDone event handler
+// wParam = (BOOL)enable
+// lParam = 0
+// (added in version 0.1.6.0)
+#define MS_UPDATE_ENABLESTATUSCONTROL "Update/EnableStatusControl"
+
+// An description of usage of the above service and event:
+// Say you are a status control plugin that normally sets protocol or global statuses in your ModulesLoaded event handler.
+// In order to make yourself 'Updater compatible', you would move the status control code from ModulesLoaded to another function,
+// say DoStartup. Then, in ModulesLoaded you would check for the existence of the MS_UPDATE_ENABLESTATUSCONTROL service.
+// If it does not exist, call DoStartup. If it does exist, hook the ME_UPDATE_STARTUPDONE event and call DoStartup from there. You may
+// also wish to call MS_UPDATE_ENABLESTATUSCONTROL with wParam == FALSE at this time, to disable Updater's own status control feature.
+
+// this service can be used to determine whether updates are possible for a component with the given name
+// wParam = 0
+// lParam = (char *)szComponentName
+// returns TRUE if updates are supported, FALSE otherwise
+#define MS_UPDATE_ISUPDATESUPPORTED "Update/IsUpdateSupported"
+
+#endif
+
+
+/////////////// Usage Example ///////////////
+
+#ifdef EXAMPLE_CODE
+
+// you need to #include "m_updater.h" and HookEvent(ME_SYSTEM_MODULESLOADED, OnModulesLoaded) in your Load function...
+
+int OnModulesLoaded(WPARAM wParam, LPARAM lParam) {
+
+ Update update = {0}; // for c you'd use memset or ZeroMemory...
+ char szVersion[16];
+
+ update.cbSize = sizeof(Update);
+
+ update.szComponentName = pluginInfo.shortName;
+ update.pbVersion = (BYTE *)CreateVersionString(&pluginInfo, szVersion);
+ update.cpbVersion = strlen((char *)update.pbVersion);
+
+ // these are the three lines that matter - the archive, the page containing the version string, and the text (or data)
+ // before the version that we use to locate it on the page
+ // (note that if the update URL and the version URL point to standard file listing entries, the backend xml
+ // data will be used to check for updates rather than the actual web page - this is not true for beta urls)
+ update.szUpdateURL = "http://scottellis.com.au:81/test/updater.zip";
+ update.szVersionURL = "http://scottellis.com.au:81/test/updater_test.html";
+ update.pbVersionPrefix = (BYTE *)"Updater version ";
+
+ update.cpbVersionPrefix = strlen((char *)update.pbVersionPrefix);
+
+ // do the same for the beta versions of the above struct members if you wish to allow beta updates from another URL
+
+ CallService(MS_UPDATE_REGISTER, 0, (WPARAM)&update);
+
+ // Alternatively, to register a plugin with e.g. file ID 2254 on the file listing...
+ // CallService(MS_UPDATE_REGISTERFL, (WPARAM)2254, (LPARAM)&pluginInfo);
+
+ return 0;
+}
+
+#endif
diff --git a/Plugins/avh_import/avh_import.c b/Plugins/avh_import/avh_import.c
new file mode 100644
index 0000000..a61bc0e
--- /dev/null
+++ b/Plugins/avh_import/avh_import.c
@@ -0,0 +1,767 @@
+/*
+Avatar History Import
+---------
+
+ This plugin uses the event provided by Avatar Service to
+ automatically back up contacts' avatars when they change.
+ Copyright (C) 2006 Matthew Wild - Email: mwild1@gmail.com
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+*/
+#include <tchar.h>
+#include <windows.h>
+#include <stdio.h>
+#include <time.h>
+#include <shobjidl.h>
+#include <shlguid.h>
+
+#include <newpluginapi.h>
+#include "sdk/m_folders.h"
+#include <m_clist.h>
+#include <m_skin.h>
+#include <m_avatars.h>
+#include <m_database.h>
+#define _STATIC
+#include <m_system.h>
+#include <m_protocols.h>
+#include <m_protosvc.h>
+#include <m_contacts.h>
+#include <m_popup.h>
+#include <m_options.h>
+#include <m_utils.h>
+#include <m_langpack.h>
+#include "sdk/m_metacontacts.h"
+#include <m_history.h>
+#include "sdk/m_avatarhist.h"
+#include "sdk/m_imgsrvc.h"
+
+#ifdef __GNUC__
+#define mir_i64(x) (x##LL)
+#else
+#define mir_i64(x) (x##i64)
+#endif
+
+#define MODULE_NAME "AvatarHistory"
+#define DEFAULT_TEMPLATE_CHANGED "changed his/her avatar"
+
+#define WIDTH 32
+#define TOPBIT (1 << (WIDTH - 1)) /* MSB */
+#define POLYNOMIAL (0x488781ED) /* This is the CRC Poly */
+
+#ifndef CLSID_ShellLink
+#define MDEF_CLSID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \
+ const CLSID name = { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } }
+
+ MDEF_CLSID(CLSID_ShellLink, 0x00021401L, 0, 0, 0xC0, 0, 0, 0, 0, 0, 0, 0x46);
+ MDEF_CLSID(IID_IShellLinkA, 0x000214ee, 0x0000, 0x0000, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46);
+ MDEF_CLSID(IID_IShellLinkW, 0x000214f9, 0x0000, 0x0000, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46);
+ MDEF_CLSID(IID_IPersistFile, 0x0000010b, 0x0000, 0x0000, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46);
+#endif
+
+
+HINSTANCE hInst;
+PLUGINLINK *pluginLink;
+
+HANDLE hModulesLoadedHook = NULL;
+
+HANDLE hFolder;
+
+BYTE log_old_style;
+BYTE log_keep_same_folder;
+
+char profilePath[MAX_PATH+1];
+TCHAR basedir[MAX_PATH+1];
+TCHAR template_changed[MAX_PATH+1];
+
+
+PLUGININFO pluginInfo={
+ sizeof(PLUGININFO),
+#ifdef UNICODE
+ "Avatar History Import (Unicode)",
+#else
+ "Avatar History Import",
+#endif
+ PLUGIN_MAKE_VERSION(0,0,0,4),
+ "This plugin converts AVH history to new format",
+ "TioDuke, Pescuma",
+ "tioduke@yahoo.ca",
+ "© 2007 TioDuke",
+ "http://www.miranda-im.org",
+ UNICODE_AWARE,
+ 0 //doesn't replace anything built-in
+};
+
+BOOL WINAPI DllMain(HINSTANCE hinstDLL,DWORD fdwReason,LPVOID lpvReserved)
+{
+ hInst=hinstDLL;
+ return TRUE;
+}
+
+__declspec(dllexport) PLUGININFO* MirandaPluginInfo(DWORD mirandaVersion)
+{
+ return &pluginInfo;
+}
+
+char * CopyToANSI(char *out, TCHAR *in, size_t size)
+{
+#ifdef UNICODE
+ WideCharToMultiByte(CP_ACP, 0, in, -1, out, size, NULL, NULL);
+#else
+ lstrcpyn(out, in, size);
+#endif
+ return out;
+}
+
+// Temp buffer
+char tmpANSI[1024];
+char * ConvertToANSI(TCHAR *in)
+{
+ return CopyToANSI(tmpANSI, in, 1024);
+}
+
+
+HANDLE ContactLookup(TCHAR *proto, TCHAR *contactid)
+{
+ HANDLE hContact = NULL;
+
+ for(hContact=(HANDLE)CallService(MS_DB_CONTACT_FINDFIRST, 0, 0); hContact; hContact=(HANDLE)CallService(MS_DB_CONTACT_FINDNEXT, (WPARAM)hContact, 0))
+ {
+ CONTACTINFO cinfo = {0};
+
+ cinfo.cbSize = sizeof(CONTACTINFO);
+ cinfo.dwFlag = CNF_UNIQUEID;
+#ifdef UNICODE
+ cinfo.dwFlag |= CNF_UNICODE;
+#endif
+ cinfo.szProto = ConvertToANSI(proto);
+ cinfo.hContact = hContact;
+
+ if (!CallService(MS_CONTACT_GETCONTACTINFO, 0, (LPARAM)&cinfo))
+ {
+ TCHAR uniqueid[MAX_PATH+1];
+
+ if (cinfo.type == CNFT_ASCIIZ)
+ lstrcpy(uniqueid, cinfo.pszVal);
+ else if (cinfo.type == CNFT_WORD)
+ _ltot(cinfo.wVal, uniqueid, 10);
+ else if (cinfo.type == CNFT_DWORD)
+ _ltot(cinfo.dVal, uniqueid, 10);
+ else
+ continue;
+
+ if (!lstrcmpi(contactid, uniqueid))
+ return hContact;
+ }
+ }
+
+ return NULL;
+}
+
+int PathIsAbsolute(const char *path)
+{
+ if (!path || !(strlen(path) > 2))
+ return 0;
+ if ((path[1] == ':' && path[2] == '\\') || (path[0] == '\\' && path[1] == '\\')) return 1;
+ return 0;
+}
+
+int PathToRelative(const char *pSrc, char *pOut)
+{
+ if (!pSrc || !strlen(pSrc) || strlen(pSrc) > MAX_PATH) return 0;
+ if (!PathIsAbsolute(pSrc)) {
+ mir_snprintf(pOut, MAX_PATH, "%s", pSrc);
+ return strlen(pOut);
+ }
+ else {
+ char szTmp[MAX_PATH+1];
+ mir_snprintf(szTmp, MAX_PATH, "%s", pSrc);
+ _strlwr(szTmp);
+ if (strstr(szTmp, profilePath)) {
+ mir_snprintf(pOut, MAX_PATH, "%s", pSrc + strlen(profilePath) + 1);
+ return strlen(pOut);
+ }
+ else {
+ mir_snprintf(pOut, MAX_PATH, "%s", pSrc);
+ return strlen(pOut);
+ }
+ }
+
+ return 0;
+}
+
+BOOL isOldAVHEvent(HANDLE hDbEvent, char *oldAVHToken)
+{
+ BYTE blob[2048];
+ DBEVENTINFO event={0};
+ int i;
+
+ event.pBlob = blob;
+ event.cbBlob = sizeof(blob);
+ event.cbSize = sizeof(event);
+ if (CallService(MS_DB_EVENT_GET, (WPARAM)hDbEvent, (LPARAM)&event))
+ return FALSE;
+
+ // Verify event type
+ if (event.eventType != EVENTTYPE_AVATAR_CHANGE)
+ return FALSE;
+
+ // Verify event content
+ for(i=(int)(event.cbBlob - 2); i >= 0 && event.pBlob[i] != 0; i--);
+ if (i != (int)(event.cbBlob - 2) && i >= 0 && strstr((char *)(event.pBlob + i + 1), oldAVHToken))
+ return TRUE;
+
+ return FALSE;
+}
+
+void DeleteOldHistoryLogs(HANDLE hContact, TCHAR *proto, TCHAR *contactid)
+{
+ TCHAR path[MAX_PATH+1];
+ char contactToken[MAX_PATH+1];
+ HANDLE hDbEvent=NULL;
+ if (!hContact)
+ return;
+
+ mir_sntprintf(path, MAX_PATH, _T("%s\\%s\\%s\\"), basedir, proto, contactid);
+ PathToRelative(ConvertToANSI(path), contactToken);
+
+ for(hDbEvent=(HANDLE)CallService(MS_DB_EVENT_FINDFIRST, (WPARAM)hContact, 0); hDbEvent; )
+ {
+ if (isOldAVHEvent(hDbEvent, contactToken))
+ {
+ HANDLE hOld = hDbEvent;
+ hDbEvent = (HANDLE)CallService(MS_DB_EVENT_FINDNEXT, (WPARAM)hDbEvent, 0);
+ CallService(MS_DB_EVENT_DELETE, (WPARAM)hContact, (LPARAM)hOld);
+ }
+ else
+ {
+ hDbEvent=(HANDLE)CallService(MS_DB_EVENT_FINDNEXT, (WPARAM)hDbEvent, 0);
+ }
+ }
+
+}
+
+int GetFileHash(TCHAR* filename)
+{
+ int remainder = 0;
+ char data[1024];
+ DWORD dwRead;
+ HANDLE hFile = CreateFile(filename, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
+ if(hFile == INVALID_HANDLE_VALUE)
+ return 0;
+
+ do
+ {
+ int byte;
+ int bit;
+ // Read file chunk
+ dwRead = 0;
+ ReadFile(hFile, data, 1024, &dwRead, NULL);
+
+ /* loop through each byte of data */
+ for (byte = 0; byte < (int) dwRead; ++byte) {
+ /* store the next byte into the remainder */
+ remainder ^= (data[byte] << (WIDTH - 8));
+ /* calculate for all 8 bits in the byte */
+ for (bit = 8; bit > 0; --bit) {
+ /* check if MSB of remainder is a one */
+ if (remainder & TOPBIT)
+ remainder = (remainder << 1) ^ POLYNOMIAL;
+ else
+ remainder = (remainder << 1);
+ }
+ }
+ } while(dwRead == 1024);
+
+ CloseHandle(hFile);
+
+ return remainder;
+}
+
+TCHAR *GenerateShortcutName(TCHAR *baseName, TCHAR *filename_with_extension)
+{
+ TCHAR aux[MAX_PATH+1];
+ TCHAR *ext;
+ static TCHAR shortcutName[MAX_PATH+1];
+
+ lstrcpy(aux, baseName);
+ ext = _tcsrchr(aux, _T('.'));
+ if (ext)
+ *ext = 0;
+
+ ext = _tcsrchr(filename_with_extension, _T('.'));
+ if (!ext)
+ ext = _T("");
+
+ mir_sntprintf(shortcutName, MAX_PATH, _T("%s%s.lnk"), aux, ext);
+ return shortcutName;
+}
+
+BOOL CreateShortcut(TCHAR *file, TCHAR *shortcut)
+{
+ HRESULT hres;
+ IShellLink *psl;
+ IPersistFile *pPf;
+
+ TCHAR szPath[MAX_PATH+1];
+ if (!GetFullPathName(file, MAX_PATH, szPath, NULL))
+ return FALSE;
+
+ CoInitialize(NULL);
+
+ hres = CoCreateInstance(&CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, &IID_IShellLink, (void **)&psl);
+ if (FAILED(hres))
+ {
+ CoUninitialize();
+ return FALSE;
+ }
+
+ hres = psl->lpVtbl->QueryInterface(psl, &IID_IPersistFile, (void **)&pPf);
+ if (FAILED(hres))
+ {
+ psl->lpVtbl->Release(psl);
+ CoUninitialize();
+ return FALSE;
+ }
+ hres = psl->lpVtbl->SetPath(psl, szPath);
+ if (FAILED(hres))
+ {
+ pPf->lpVtbl->Release(pPf);
+ psl->lpVtbl->Release(psl);
+ CoUninitialize();
+ return FALSE;
+ }
+
+#ifdef UNICODE
+ hres = pPf->lpVtbl->Save(pPf, shortcut, TRUE);
+#else
+ {
+ WCHAR tmp[MAX_PATH+1];
+ MultiByteToWideChar(CP_ACP, 0, shortcut, -1, (WCHAR *)tmp, MAX_PATH);
+ hres = pPf->lpVtbl->Save(pPf, tmp, TRUE);
+ }
+#endif
+
+ pPf->lpVtbl->Release(pPf);
+ psl->lpVtbl->Release(psl);
+ CoUninitialize();
+ if (FAILED(hres))
+ return FALSE;
+ return TRUE;
+}
+
+TCHAR *Move2NewFile(TCHAR *proto, TCHAR *contactid, TCHAR *history_filename)
+{
+ static TCHAR history_filename_new[MAX_PATH+1];
+
+ TCHAR search[MAX_PATH+1];
+ TCHAR hash[MAX_PATH+1];
+ WIN32_FIND_DATA finddata;
+ HANDLE hFind;
+ TCHAR *ext;
+
+ mir_sntprintf(hash, MAX_PATH, _T("AVS-HASH-%x"), GetFileHash(history_filename));
+
+ if (log_keep_same_folder)
+ mir_sntprintf(search, MAX_PATH, _T("%s\\%s.*"), basedir, hash);
+ else
+ mir_sntprintf(search, MAX_PATH, _T("%s\\%s\\%s.*"), basedir, proto, hash);
+
+
+ hFind = FindFirstFile(search, &finddata);
+ if (hFind != INVALID_HANDLE_VALUE)
+ {
+ do
+ {
+ size_t len = _tcslen(finddata.cFileName);
+ if (len > 4 && (!lstrcmpi(&finddata.cFileName[len-4], _T(".png")) || !lstrcmpi(&finddata.cFileName[len-4], _T(".bmp")) || !lstrcmpi(&finddata.cFileName[len-4], _T(".gif")) || !lstrcmpi(&finddata.cFileName[len-4], _T(".jpg")) || !lstrcmpi(&finddata.cFileName[len-5], _T(".jpeg"))))
+ {
+ if (log_keep_same_folder)
+ mir_sntprintf(history_filename_new, MAX_PATH, _T("%s\\%s"), basedir, finddata.cFileName);
+ else
+ mir_sntprintf(history_filename_new, MAX_PATH, _T("%s\\%s\\%s"), basedir, proto, finddata.cFileName);
+ FindClose(hFind);
+ DeleteFile(history_filename);
+ if (log_old_style)
+ CreateShortcut(history_filename_new, GenerateShortcutName(history_filename, history_filename_new));
+ return history_filename_new;
+ }
+ } while(FindNextFile(hFind, &finddata));
+ FindClose(hFind);
+ }
+
+ ext = _tcsrchr(history_filename, _T('.'));
+ if (ext)
+ ext++;
+ else
+ ext = _T("");
+
+ if (!lstrcmpi(ext, _T("bmp")) && ServiceExists(MS_AV_CANSAVEBITMAP) && CallService(MS_AV_CANSAVEBITMAP, 0, PA_FORMAT_PNG))
+ {
+ IMGSRVC_INFO ii = {0};
+
+ // Store as PNG
+ if (log_keep_same_folder)
+ mir_sntprintf(history_filename_new, MAX_PATH, _T("%s\\%s.png"), basedir, hash);
+ else
+ mir_sntprintf(history_filename_new, MAX_PATH, _T("%s\\%s\\%s.png"), basedir, proto, hash);
+
+ ii.cbSize = sizeof(ii);
+#ifdef UNICODE
+ ii.wszName = history_filename_new;
+#else
+ ii.szName = history_filename_new;
+#endif
+ ii.hbm = (HBITMAP) CallService(MS_IMG_LOAD, (WPARAM) history_filename, IMGL_TCHAR);
+ ii.dwMask = IMGI_HBITMAP;
+ ii.fif = FIF_UNKNOWN;
+ if (!CallService(MS_IMG_SAVE, (WPARAM) &ii, IMGL_TCHAR))
+ lstrcpy(history_filename_new, history_filename);
+ else
+ {
+ DeleteFile(history_filename);
+ if (log_old_style)
+ CreateShortcut(history_filename_new, GenerateShortcutName(history_filename, history_filename_new));
+ }
+ }
+ else
+ {
+ if (log_keep_same_folder)
+ mir_sntprintf(history_filename_new, MAX_PATH, _T("%s\\%s.%s"), basedir, hash, ext);
+ else
+ mir_sntprintf(history_filename_new, MAX_PATH, _T("%s\\%s\\%s.%s"), basedir, proto, hash, ext);
+
+ if(!CopyFile(history_filename, history_filename_new, TRUE))
+ lstrcpy(history_filename_new, history_filename);
+ else
+ {
+ DeleteFile(history_filename);
+ if (log_old_style)
+ CreateShortcut(history_filename_new, GenerateShortcutName(history_filename, history_filename_new));
+ }
+ }
+
+ return history_filename_new;
+}
+
+DWORD filename2timestamp(TCHAR *filename)
+{
+ char tmp[MAX_PATH+1];
+ SYSTEMTIME lt={0}, st;
+ TIME_ZONE_INFORMATION timezone;
+ FILETIME filetime;
+ LARGE_INTEGER liFiletime;
+
+ CopyToANSI(tmp, filename, MAX_PATH);
+
+ lt.wYear = (WORD)atol(strtok(tmp, "-"));
+ lt.wMonth = (WORD)atol(strtok(NULL, "-"));
+ lt.wDay = (WORD)atol(strtok(NULL, " "));
+ lt.wHour = (WORD)atol(strtok(NULL, "h"));
+ lt.wMinute = (WORD)atol(strtok(NULL, "m"));
+ lt.wSecond = (WORD)atol(strtok(NULL, "s"));
+
+ GetTimeZoneInformation(&timezone);
+ TzSpecificLocalTimeToSystemTime(&timezone, &lt, &st);
+ SystemTimeToFileTime(&st, &filetime);
+
+ liFiletime.LowPart = filetime.dwLowDateTime;
+ liFiletime.HighPart = filetime.dwHighDateTime;
+ return (DWORD)(liFiletime.QuadPart / 10000000 - mir_i64(11644473600));
+}
+
+BOOL ProtocolEnabled(const char *proto)
+{
+ char setting[256];
+
+ if (proto == NULL)
+ return FALSE;
+
+ mir_snprintf(setting, sizeof(setting), "%sEnabled", proto);
+ return (BOOL) DBGetContactSettingByte(NULL, "AvatarHistory", setting, TRUE);
+}
+
+BOOL ContactEnabled(HANDLE hContact, char *setting, int def)
+{
+ char *proto;
+ BYTE globpref;
+ BYTE userpref;
+
+ if (hContact == NULL)
+ return FALSE;
+
+ proto = (char *) CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM) hContact, 0);
+ if (!ProtocolEnabled(proto))
+ return FALSE;
+
+ globpref = db_byte_get(NULL, "AvatarHistory", setting, def);
+ userpref = db_byte_get(hContact, "AvatarHistory", setting, BST_INDETERMINATE);
+
+ return (globpref && userpref == BST_INDETERMINATE) || userpref == BST_CHECKED;
+}
+
+
+// Returns true if the unicode buffer only contains 7-bit characters.
+BOOL IsUnicodeAscii(const WCHAR * pBuffer, int nSize)
+{
+ BOOL bResult = TRUE;
+ int nIndex;
+
+ for (nIndex = 0; nIndex < nSize; nIndex++) {
+ if (pBuffer[nIndex] > 0x7F) {
+ bResult = FALSE;
+ break;
+ }
+ }
+ return bResult;
+}
+
+
+HANDLE HistoryLog(HANDLE hContact, TCHAR *log_text, char *filename, DWORD timestamp)
+{
+ DBEVENTINFO event = { 0 };
+ BYTE *tmp = NULL;
+ size_t file_len;
+ HANDLE ret;
+
+ if (log_text == NULL)
+ return NULL;
+
+ event.cbSize = sizeof(event);
+
+ if (filename != NULL)
+ file_len = strlen(filename) + 1;
+ else
+ file_len = 0;
+
+#ifdef UNICODE
+
+ {
+ size_t needed = WideCharToMultiByte(CP_ACP, 0, log_text, -1, NULL, 0, NULL, NULL);
+ size_t len = lstrlen(log_text) + 1;
+ size_t size;
+ BOOL isAscii = IsUnicodeAscii(log_text, len);
+
+ if (isAscii)
+ size = needed + (filename != NULL ? 2 : 0);
+ else
+ size = needed + len * sizeof(WCHAR);
+
+ tmp = (BYTE *) malloc(size + file_len);
+
+ WideCharToMultiByte(CP_ACP, 0, log_text, -1, (char *) tmp, needed, NULL, NULL);
+
+ if (isAscii)
+ {
+ if (filename != NULL)
+ {
+ tmp[needed] = 0;
+ tmp[needed+1] = 0;
+ }
+ }
+ else
+ {
+ lstrcpyn((WCHAR *) &tmp[needed], log_text, len);
+ }
+
+ if (filename != NULL)
+ strcpy((char *) &tmp[size], filename);
+
+ event.pBlob = tmp;
+ event.cbBlob = size + file_len;
+ }
+
+#else
+
+ if (filename != NULL)
+ {
+ size_t len = lstrlen(log_text) + 1;
+ tmp = (BYTE *) malloc(len + 2 + file_len);
+
+ strcpy((char *) tmp, log_text);
+ tmp[len] = 0;
+ tmp[len + 1] = 0;
+ strcpy((char *) &tmp[len + 2], filename);
+
+ event.pBlob = tmp;
+ event.cbBlob = len + 2 + file_len;
+ }
+ else
+ {
+ event.pBlob = (PBYTE) log_text;
+ event.cbBlob = strlen(log_text) + 1;
+ }
+
+#endif
+
+ event.eventType = EVENTTYPE_AVATAR_CHANGE;
+ event.flags = DBEF_READ;
+ event.timestamp = timestamp;
+
+ event.szModule = MODULE_NAME;
+
+ // Is a subcontact?
+ if (ServiceExists(MS_MC_GETMETACONTACT))
+ {
+ HANDLE hMetaContact = (HANDLE) CallService(MS_MC_GETMETACONTACT, (WPARAM)hContact, 0);
+
+ if (hMetaContact != NULL && ContactEnabled(hMetaContact, "LogToHistory", 1))
+ CallService(MS_DB_EVENT_ADD,(WPARAM)hMetaContact,(LPARAM)&event);
+ }
+
+ ret = (HANDLE) CallService(MS_DB_EVENT_ADD,(WPARAM)hContact,(LPARAM)&event);
+
+ free(tmp);
+
+ return ret;
+}
+
+void ImportContactAVH(TCHAR *proto, TCHAR *contactid)
+{
+ TCHAR search[MAX_PATH+1];
+ WIN32_FIND_DATA finddata;
+ HANDLE hFind;
+ HANDLE hContact = ContactLookup(proto, contactid);
+ if (!hContact)
+ return;
+
+ DeleteOldHistoryLogs(hContact, proto, contactid);
+
+ mir_sntprintf(search, MAX_PATH, _T("%s\\%s\\%s\\*.*"), basedir, proto, contactid);
+
+ hFind = FindFirstFile(search, &finddata);
+ if (hFind == INVALID_HANDLE_VALUE)
+ return;
+
+ do
+ {
+ size_t len = _tcslen(finddata.cFileName);
+ if (len > 4 && (!lstrcmpi(&finddata.cFileName[len-4], _T(".png")) || !lstrcmpi(&finddata.cFileName[len-4], _T(".bmp")) || !lstrcmpi(&finddata.cFileName[len-4], _T(".gif")) || !lstrcmpi(&finddata.cFileName[len-4], _T(".jpg")) || !lstrcmpi(&finddata.cFileName[len-5], _T(".jpeg"))))
+ {
+ char rel_path[MAX_PATH+1];
+ TCHAR history_filename[MAX_PATH+1];
+
+ mir_sntprintf(history_filename, MAX_PATH, _T("%s\\%s\\%s\\%s"), basedir, proto, contactid, finddata.cFileName);
+ PathToRelative(ConvertToANSI(Move2NewFile(proto, contactid, history_filename)), rel_path);
+ if (ContactEnabled(hContact, "LogToHistory", 1))
+ HistoryLog(hContact, template_changed, rel_path, filename2timestamp(finddata.cFileName));
+ }
+ } while(FindNextFile(hFind, &finddata));
+
+ FindClose(hFind);
+}
+
+void ImportProtocolAVH(TCHAR *proto)
+{
+ TCHAR search[MAX_PATH+1];
+ WIN32_FIND_DATA finddata;
+ HANDLE hFind;
+ mir_sntprintf(search, MAX_PATH, _T("%s\\%s\\*"), basedir, proto);
+
+ hFind = FindFirstFile(search, &finddata);
+ if (hFind == INVALID_HANDLE_VALUE)
+ return;
+
+ do
+ {
+ if (finddata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+ if (lstrcmpi(finddata.cFileName, _T(".")) && lstrcmpi(finddata.cFileName, _T("..")))
+ ImportContactAVH(proto, finddata.cFileName);
+ } while(FindNextFile(hFind, &finddata));
+
+ FindClose(hFind);
+}
+
+void __cdecl ImportAVH(void *dummy)
+{
+ TCHAR search[MAX_PATH+1];
+ WIN32_FIND_DATA finddata;
+ HANDLE hFind;
+
+
+ if (MessageBox(NULL, TranslateT("Avatar History will be imported.\nPlease, don't close Miranda before it ends."),
+ _T("Avatar History Import"), MB_OKCANCEL) == IDCANCEL)
+ return;
+
+ mir_sntprintf(search, MAX_PATH, _T("%s\\*"), basedir);
+ hFind = FindFirstFile(search, &finddata);
+ if (hFind == INVALID_HANDLE_VALUE)
+ return;
+
+ do
+ {
+ if (finddata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+ if (lstrcmpi(finddata.cFileName, _T(".")) && lstrcmpi(finddata.cFileName, _T("..")))
+ ImportProtocolAVH(finddata.cFileName);
+ } while(FindNextFile(hFind, &finddata));
+
+ FindClose(hFind);
+ DBWriteContactSettingByte(NULL, MODULE_NAME, "avh_imported", 1);
+ MessageBox(NULL, TranslateT("Previous Avatar History has been successfully imported.\nYou can remove Avatar History Importer plugin now."),
+ _T("Avatar History Import"), MB_OK);
+}
+
+static int ModulesLoaded(WPARAM wParam, LPARAM lParam)
+{
+ DBVARIANT dbv;
+ if (DBGetContactSettingByte(NULL, MODULE_NAME, "avh_imported", 0))
+ return 0;
+
+ if(CallService(MS_DB_GETPROFILEPATH, MAX_PATH, (LPARAM)profilePath) != 0)
+ strcpy(profilePath, "."); // Failed, use current dir
+ _strlwr(profilePath);
+
+#ifdef UNICODE
+ mir_sntprintf(basedir, MAX_PATH, _T("%S\\Avatars History"), profilePath);
+#else
+ mir_sntprintf(basedir, MAX_PATH, _T("%s\\Avatars History"), profilePath);
+#endif
+
+ hFolder = (HANDLE)FoldersRegisterCustomPathT(Translate("Avatars"), Translate("Avatar History"), _T(PROFILE_PATH) _T("\\") _T(CURRENT_PROFILE) _T("\\Avatars History"));
+ if (hFolder) {
+ TCHAR customdir[MAX_PATH+1];
+ FoldersGetCustomPathT(hFolder, customdir, MAX_PATH, basedir);
+ lstrcpy(basedir, customdir);
+ }
+
+ log_old_style = DBGetContactSettingByte(NULL, MODULE_NAME, "LogPerContactFolders", 0);
+ log_keep_same_folder = DBGetContactSettingByte(NULL, MODULE_NAME, "LogKeepSameFolder", 0);
+
+ if (!DBGetContactSettingTString(NULL, MODULE_NAME, "TemplateChanged", &dbv))
+ {
+ mir_sntprintf(template_changed, MAX_PATH, _T("%s"), dbv.ptszVal);
+ DBFreeVariant(&dbv);
+ }
+ else
+ mir_sntprintf(template_changed, MAX_PATH, _T(DEFAULT_TEMPLATE_CHANGED));
+
+ mir_forkthread((pThreadFunc)ImportAVH, NULL);
+
+ return 0;
+}
+
+int __declspec(dllexport) Load(PLUGINLINK *link)
+{
+ pluginLink=link;
+
+ hModulesLoadedHook = HookEvent(ME_SYSTEM_MODULESLOADED, ModulesLoaded);
+ return 0;
+}
+
+int __declspec(dllexport) Unload(void)
+{
+ UnhookEvent(hModulesLoadedHook);
+ return 0;
+}
diff --git a/Plugins/avh_import/avh_import.dsp b/Plugins/avh_import/avh_import.dsp
new file mode 100644
index 0000000..cd8da27
--- /dev/null
+++ b/Plugins/avh_import/avh_import.dsp
@@ -0,0 +1,168 @@
+# Microsoft Developer Studio Project File - Name="avh_import" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=avh_import - Win32 Unicode Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "avh_import.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "avh_import.mak" CFG="avh_import - Win32 Unicode Debug"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "avh_import - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "avh_import - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "avh_import - Win32 Unicode Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "avh_import - Win32 Unicode Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "avh_import - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "avh_import_EXPORTS" /YX /FD /c
+# ADD CPP /nologo /MD /W3 /GX /O1 /I "../../include" /I "sdk" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "avh_import_EXPORTS" /FR /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x809 /d "NDEBUG"
+# ADD RSC /l 0x809 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386
+# ADD LINK32 kernel32.lib user32.lib comctl32.lib comdlg32.lib ole32.lib gdi32.lib /nologo /dll /machine:I386 /out:"..\..\bin\release\plugins\avh_imp.dll"
+# SUBTRACT LINK32 /nodefaultlib
+
+!ELSEIF "$(CFG)" == "avh_import - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "avh_import_EXPORTS" /YX /FD /GZ /c
+# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "../../include" /I "sdk" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "avh_import_EXPORTS" /FR /YX /FD /GZ /c
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x809 /d "_DEBUG"
+# ADD RSC /l 0x809 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 kernel32.lib user32.lib comctl32.lib comdlg32.lib ole32.lib gdi32.lib /nologo /dll /debug /machine:I386 /out:"..\..\bin\debug\plugins\avh_imp.dll" /pdbtype:sept
+
+!ELSEIF "$(CFG)" == "avh_import - Win32 Unicode Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "avh_import___Win32_Debug_Unicode"
+# PROP BASE Intermediate_Dir "avh_import___Win32_Debug_Unicode"
+# PROP BASE Ignore_Export_Lib 0
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Unicode_Debug"
+# PROP Intermediate_Dir "Unicode_Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "../../include" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "avh_import_EXPORTS" /FR /YX /FD /GZ /c
+# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "../../include" /I "sdk" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "UNICODE" /D "_UNICODE" /D "_USRDLL" /D "avh_import_EXPORTS" /FR /YX /FD /GZ /c
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x809 /d "_DEBUG"
+# ADD RSC /l 0x809 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 winspool.lib ole32.lib oleaut32.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib comdlg32.lib advapi32.lib shell32.lib uuid.lib comctl32.lib /nologo /dll /debug /machine:I386 /out:"..\..\bin\debug\plugins\avatarhist.dll" /pdbtype:sept
+# ADD LINK32 kernel32.lib user32.lib comctl32.lib comdlg32.lib ole32.lib gdi32.lib /nologo /dll /debug /machine:I386 /out:"..\..\bin\debug unicode\plugins\avh_impW.dll" /pdbtype:sept
+
+!ELSEIF "$(CFG)" == "avh_import - Win32 Unicode Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "avh_import___Win32_Release_Unicode"
+# PROP BASE Intermediate_Dir "avh_import___Win32_Release_Unicode"
+# PROP BASE Ignore_Export_Lib 0
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Unicode_Release"
+# PROP Intermediate_Dir "Unicode_Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MD /W3 /GX /O1 /I "../../include" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "avh_import_EXPORTS" /FR /YX /FD /c
+# ADD CPP /nologo /MD /W3 /GX /O1 /I "../../include" /I "sdk" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "UNICODE" /D "_UNICODE" /D "_USRDLL" /D "avh_import_EXPORTS" /FR /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x809 /d "NDEBUG"
+# ADD RSC /l 0x809 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib comdlg32.lib advapi32.lib shell32.lib uuid.lib comctl32.lib /nologo /dll /machine:I386 /out:"..\..\bin\release\plugins\avatarhist.dll"
+# SUBTRACT BASE LINK32 /nodefaultlib
+# ADD LINK32 kernel32.lib user32.lib comctl32.lib comdlg32.lib ole32.lib gdi32.lib /nologo /dll /machine:I386 /out:"..\..\bin\release\plugins\avh_impW.dll"
+# SUBTRACT LINK32 /nodefaultlib
+
+!ENDIF
+
+# Begin Target
+
+# Name "avh_import - Win32 Release"
+# Name "avh_import - Win32 Debug"
+# Name "avh_import - Win32 Unicode Debug"
+# Name "avh_import - Win32 Unicode Release"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=.\avh_import.c
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
+# End Group
+# End Target
+# End Project
diff --git a/Plugins/avh_import/avh_import.dsw b/Plugins/avh_import/avh_import.dsw
new file mode 100644
index 0000000..a2e2cdd
--- /dev/null
+++ b/Plugins/avh_import/avh_import.dsw
@@ -0,0 +1,29 @@
+Microsoft Developer Studio Workspace File, Format Version 6.00
+# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
+
+###############################################################################
+
+Project: "avh_import"=.\avh_import.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Global:
+
+Package=<5>
+{{{
+}}}
+
+Package=<3>
+{{{
+}}}
+
+###############################################################################
+
diff --git a/Plugins/avh_import/sdk/m_avatarhist.h b/Plugins/avh_import/sdk/m_avatarhist.h
new file mode 100644
index 0000000..2613dee
--- /dev/null
+++ b/Plugins/avh_import/sdk/m_avatarhist.h
@@ -0,0 +1,51 @@
+/*
+Copyright (C) 2006 MattJ, Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#ifndef __M_AVATARHISTORY_H__
+# define __M_AVATARHISTORY_H__
+
+
+#define EVENTTYPE_AVATAR_CHANGE 9003
+
+
+/*
+Return TRUE is Avatar History is enabled for this contact
+
+wParam: hContact
+lParam: ignored
+*/
+#define MS_AVATARHISTORY_ENABLED "AvatarHistory/IsEnabled"
+
+
+/*
+Get cached avatar
+
+wParam: (char *) protocol name
+lParam: (char *) hash
+return: (TCHAR *) NULL if none is found or the path to the avatar. You need to free this string
+ with mir_free.
+*/
+#define MS_AVATARHISTORY_GET_CACHED_AVATAR "AvatarHistory/GetCachedAvatar"
+
+
+
+
+
+#endif // __M_AVATARHISTORY_H__
diff --git a/Plugins/avh_import/sdk/m_folders.h b/Plugins/avh_import/sdk/m_folders.h
new file mode 100644
index 0000000..a112b05
--- /dev/null
+++ b/Plugins/avh_import/sdk/m_folders.h
@@ -0,0 +1,207 @@
+/*
+Custom profile folders plugin for Miranda IM
+
+Copyright © 2005 Cristian Libotean
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef M_CUSTOM_FOLDERS_H
+#define M_CUSTOM_FOLDERS_H
+
+#define FOLDERS_API 501 //dunno why it's here but it is :)
+
+#define PROFILE_PATH "%profile_path%"
+#define CURRENT_PROFILE "%current_profile%"
+#define MIRANDA_PATH "%miranda_path%"
+#define PLUGINS_PATH "%miranda_path%" "\\plugins"
+
+#define TO_WIDE(x) L ## x
+
+#define PROFILE_PATHW L"%profile_path%"
+#define CURRENT_PROFILEW L"%current_profile%"
+#define MIRANDA_PATHW L"%miranda_path%"
+
+#define FOLDER_AVATARS PROFILE_PATH "\\" CURRENT_PROFILE "\\avatars"
+#define FOLDER_VCARDS PROFILE_PATH "\\" CURRENT_PROFILE "\\vcards"
+#define FOLDER_LOGS PROFILE_PATH "\\" CURRENT_PROFILE "\\logs"
+#define FOLDER_RECEIVED_FILES PROFILE_PATH "\\" CURRENT_PROFILE "\\received files"
+#define FOLDER_DOCS MIRANDA_PATH "\\" "docs"
+
+#define FOLDER_CONFIG PLUGINS_PATH "\\" "config"
+
+#define FOLDER_SCRIPTS MIRANDA_PATH "\\" "scripts"
+
+#define FOLDER_UPDATES MIRANDA_PATH "\\" "updates"
+
+#define FOLDER_CUSTOMIZE MIRANDA_PATH "\\" "customize"
+#define FOLDER_CUSTOMIZE_SOUNDS FOLDER_CUSTOMIZE "\\sounds"
+#define FOLDER_CUSTOMIZE_ICONS FOLDER_CUSTOMIZE "\\icons"
+#define FOLDER_CUSTOMIZE_SMILEYS FOLDER_CUSTOMIZE "\\smileys"
+#define FOLDER_CUSTOMIZE_SKINS FOLDER_CUSTOMIZE "\\skins"
+#define FOLDER_CUSTOMIZE_THEMES FOLDER_CUSTOMIZE "\\themes"
+
+
+#define FOLDERS_NAME_MAX_SIZE 64 //maximum name and section size
+
+#define FF_UNICODE 0x00000001
+
+typedef struct{
+ int cbSize; //size of struct
+ char szSection[FOLDERS_NAME_MAX_SIZE]; //section name, if it doesn't exist it will be created otherwise it will just add this entry to it
+ char szName[FOLDERS_NAME_MAX_SIZE]; //entry name - will be shown in options
+ union{
+ const char *szFormat; //default string format. Fallback string in case there's no entry in the database for this folder. This should be the initial value for the path, users will be able to change it later.
+ const wchar_t *szFormatW; //String is dup()'d so you can free it later. If you set the unicode string don't forget to set the flag accordingly.
+ const TCHAR *szFormatT;
+ };
+ DWORD flags; //FF_* flags
+} FOLDERSDATA;
+
+/*Folders/Register/Path service
+ wParam - not used, must be 0
+ lParam - (LPARAM) (const FOLDERDATA *) - Data structure filled with
+ the necessary information.
+ Returns a handle to the registered path or 0 on error.
+ You need to use this to call the other services.
+*/
+#define MS_FOLDERS_REGISTER_PATH "Folders/Register/Path"
+
+/*Folders/Get/PathSize service
+ wParam - (WPARAM) (int) - handle to registered path
+ lParam - (LPARAM) (int *) - pointer to the variable that receives the size of the path
+ string (not including the null character). Depending on the flags set when creating the path
+ it will either call strlen() or wcslen() to get the length of the string.
+ Returns the size of the buffer.
+*/
+#define MS_FOLDERS_GET_SIZE "Folders/Get/PathSize"
+
+typedef struct{
+ int cbSize;
+ int nMaxPathSize; //maximum size of buffer. This represents the number of characters that can be copied to it (so for unicode strings you don't send the number of bytes but the length of the string).
+ union{
+ char *szPath; //pointer to the buffer that receives the path without the last "\\"
+ wchar_t *szPathW; //unicode version of the buffer.
+ TCHAR *szPathT;
+ };
+} FOLDERSGETDATA;
+
+/*Folders/Get/Path service
+ wParam - (WPARAM) (int) - handle to registered path
+ lParam - (LPARAM) (FOLDERSGETDATA *) pointer to a FOLDERSGETDATA that has all the relevant fields filled.
+ Should return 0 on success, or nonzero otherwise.
+*/
+#define MS_FOLDERS_GET_PATH "Folders/Get/Path"
+
+typedef struct{
+ int cbSize;
+ union{
+ char **szPath; //address of a string variable (char *) or (wchar_t*) where the path should be stored (the last \ won't be copied).
+ wchar_t **szPathW; //unicode version of string.
+ TCHAR **szPathT;
+ };
+} FOLDERSGETALLOCDATA;
+
+/*Folders/GetRelativePath/Alloc service
+ wParam - (WPARAM) (int) - Handle to registered path
+ lParam - (LPARAM) (FOLDERSALLOCDATA *) data
+ This service is the same as MS_FOLDERS_GET_PATH with the difference that this service
+ allocates the needed space for the buffer. It uses miranda's memory functions for that and you need
+ to use those to free the resulting buffer.
+ Should return 0 on success, or nonzero otherwise. Currently it only returns 0.
+*/
+#define MS_FOLDERS_GET_PATH_ALLOC "Folders/Get/Path/Alloc"
+
+
+/*Folders/On/Path/Changed
+ wParam - (WPARAM) 0
+ lParam - (LPARAM) 0
+ Triggered when the folders change, you should reget the paths you registered.
+*/
+#define ME_FOLDERS_PATH_CHANGED "Folders/On/Path/Changed"
+
+#ifndef FOLDERS_NO_HELPER_FUNCTIONS
+//#include "../../../include/newpluginapi.h"
+
+__inline static int FoldersRegisterCustomPath(const char *section, const char *name, const char *defaultPath)
+{
+ FOLDERSDATA fd = {0};
+ if (!ServiceExists(MS_FOLDERS_REGISTER_PATH)) return 1;
+ fd.cbSize = sizeof(FOLDERSDATA);
+ strncpy(fd.szSection, section, FOLDERS_NAME_MAX_SIZE);
+ fd.szSection[FOLDERS_NAME_MAX_SIZE - 1] = '\0';
+ strncpy(fd.szName, name, FOLDERS_NAME_MAX_SIZE);
+ fd.szName[FOLDERS_NAME_MAX_SIZE - 1] = '\0';
+ fd.szFormat = defaultPath;
+ return CallService(MS_FOLDERS_REGISTER_PATH, 0, (LPARAM) &fd);
+}
+
+__inline static int FoldersRegisterCustomPathW(const char *section, const char *name, const wchar_t *defaultPathW)
+{
+ FOLDERSDATA fd = {0};
+ if (!ServiceExists(MS_FOLDERS_REGISTER_PATH)) return 1;
+ fd.cbSize = sizeof(FOLDERSDATA);
+ strncpy(fd.szSection, section, FOLDERS_NAME_MAX_SIZE);
+ fd.szSection[FOLDERS_NAME_MAX_SIZE - 1] = '\0'; //make sure it's NULL terminated
+ strncpy(fd.szName, name, FOLDERS_NAME_MAX_SIZE);
+ fd.szName[FOLDERS_NAME_MAX_SIZE - 1] = '\0'; //make sure it's NULL terminated
+ fd.szFormatW = defaultPathW;
+ fd.flags = FF_UNICODE;
+ return CallService(MS_FOLDERS_REGISTER_PATH, 0, (LPARAM) &fd);
+}
+
+__inline static int FoldersGetCustomPath(HANDLE hFolderEntry, char *path, const int size, char *notFound)
+{
+ FOLDERSGETDATA fgd = {0};
+ int res;
+ fgd.cbSize = sizeof(FOLDERSGETDATA);
+ fgd.nMaxPathSize = size;
+ fgd.szPath = path;
+ res = CallService(MS_FOLDERS_GET_PATH, (WPARAM) hFolderEntry, (LPARAM) &fgd);
+ if (res)
+ {
+ strncpy(path, notFound, size);
+ path[size - 1] = '\0'; //make sure it's NULL terminated
+ }
+ return res;
+}
+
+__inline static int FoldersGetCustomPathW(HANDLE hFolderEntry, wchar_t *pathW, const int count, wchar_t *notFoundW)
+{
+ FOLDERSGETDATA fgd = {0};
+ int res;
+ fgd.cbSize = sizeof(FOLDERSGETDATA);
+ fgd.nMaxPathSize = count;
+ fgd.szPathW = pathW;
+ res = CallService(MS_FOLDERS_GET_PATH, (WPARAM) hFolderEntry, (LPARAM) &fgd);
+ if (res)
+ {
+ wcsncpy(pathW, notFoundW, count);
+ pathW[count - 1] = '\0';
+ }
+ return res;
+}
+
+# ifdef _UNICODE
+# define FoldersGetCustomPathT FoldersGetCustomPathW
+# define FoldersRegisterCustomPathT FoldersRegisterCustomPathW
+#else
+# define FoldersGetCustomPathT FoldersGetCustomPath
+# define FoldersRegisterCustomPathT FoldersRegisterCustomPath
+#endif
+
+#endif
+
+#endif //M_CUSTOM_FOLDERS_H \ No newline at end of file
diff --git a/Plugins/avh_import/sdk/m_freeimage.h b/Plugins/avh_import/sdk/m_freeimage.h
new file mode 100644
index 0000000..c0cceeb
--- /dev/null
+++ b/Plugins/avh_import/sdk/m_freeimage.h
@@ -0,0 +1,965 @@
+// ==========================================================
+// FreeImage 3
+//
+// Design and implementation by
+// - Floris van den Berg (flvdberg@wxs.nl)
+// - Hervé Drolon (drolon@infonie.fr)
+//
+// Contributors:
+// - Adam Gates (radad@xoasis.com)
+// - Alex Kwak
+// - Alexander Dymerets (sashad@te.net.ua)
+// - Detlev Vendt (detlev.vendt@brillit.de)
+// - Jan L. Nauta (jln@magentammt.com)
+// - Jani Kajala (janik@remedy.fi)
+// - Juergen Riecker (j.riecker@gmx.de)
+// - Karl-Heinz Bussian (khbussian@moss.de)
+// - Laurent Rocher (rocherl@club-internet.fr)
+// - Luca Piergentili (l.pierge@terra.es)
+// - Machiel ten Brinke (brinkem@uni-one.nl)
+// - Markus Loibl (markus.loibl@epost.de)
+// - Martin Weber (martweb@gmx.net)
+// - Matthias Wandel (mwandel@rim.net)
+// - Michal Novotny (michal@etc.cz)
+// - Petr Pytelka (pyta@lightcomp.com)
+// - Riley McNiff (rmcniff@marexgroup.com)
+// - Ryan Rubley (ryan@lostreality.org)
+// - Volker Gärtner (volkerg@gmx.at)
+//
+// This file is part of FreeImage 3
+//
+// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
+// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
+// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
+// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
+// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
+// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
+// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+// THIS DISCLAIMER.
+//
+// Use at your own risk!
+// ==========================================================
+
+#ifndef FREEIMAGE_H
+#define FREEIMAGE_H
+
+// Version information ------------------------------------------------------
+
+#define FREEIMAGE_MAJOR_VERSION 3
+#define FREEIMAGE_MINOR_VERSION 9
+#define FREEIMAGE_RELEASE_SERIAL 3
+
+// Compiler options ---------------------------------------------------------
+
+#if !defined(_FI_MIMPLUGIN)
+#include <wchar.h> // needed for UNICODE functions
+#endif
+
+#if defined(FREEIMAGE_LIB) || !(defined(_WIN32) || defined(__WIN32__))
+#define DLL_API
+#define DLL_CALLCONV
+#else
+
+#define DLL_CALLCONV __stdcall
+// The following ifdef block is the standard way of creating macros which make exporting
+// from a DLL simpler. All files within this DLL are compiled with the FREEIMAGE_EXPORTS
+// symbol defined on the command line. this symbol should not be defined on any project
+// that uses this DLL. This way any other project whose source files include this file see
+// DLL_API functions as being imported from a DLL, wheras this DLL sees symbols
+// defined with this macro as being exported.
+#ifdef FREEIMAGE_EXPORTS
+#define DLL_API __declspec(dllexport)
+#else
+#define DLL_API __declspec(dllimport)
+#endif // FREEIMAGE_EXPORTS
+#endif // FREEIMAGE_LIB || !WIN32
+
+// Some versions of gcc may have BYTE_ORDER or __BYTE_ORDER defined
+// If your big endian system isn't being detected, add an OS specific check
+#if (defined(BYTE_ORDER) && BYTE_ORDER==BIG_ENDIAN) || \
+ (defined(__BYTE_ORDER) && __BYTE_ORDER==__BIG_ENDIAN) || \
+ defined(__BIG_ENDIAN__)
+#define FREEIMAGE_BIGENDIAN
+#endif // BYTE_ORDER
+
+// Ensure 4-byte enums if we're using Borland C++ compilers
+#if defined(__BORLANDC__)
+#pragma option push -b
+#endif
+
+// For C compatibility --------------------------------------------------------
+
+#ifdef __cplusplus
+#define FI_DEFAULT(x)
+#define FI_ENUM(x) enum x
+#define FI_STRUCT(x) struct x
+#else
+#define FI_DEFAULT(x)
+#define FI_ENUM(x) typedef int x; enum x
+#define FI_STRUCT(x) typedef struct x x; struct x
+#endif
+
+// Bitmap types -------------------------------------------------------------
+
+FI_STRUCT (FIBITMAP) { void *data; };
+FI_STRUCT (FIMULTIBITMAP) { void *data; };
+
+// Types used in the library (directly copied from Windows) -----------------
+
+#ifndef _WINDOWS_
+#define _WINDOWS_
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+#ifndef TRUE
+#define TRUE 1
+#endif
+#ifndef NULL
+#define NULL 0
+#endif
+
+#ifndef SEEK_SET
+#define SEEK_SET 0
+#define SEEK_CUR 1
+#define SEEK_END 2
+#endif
+
+#ifndef _MSC_VER
+// define portable types for 32-bit / 64-bit OS
+#include <inttypes.h>
+typedef int32_t BOOL;
+typedef uint8_t BYTE;
+typedef uint16_t WORD;
+typedef uint32_t DWORD;
+typedef int32_t LONG;
+#else
+// MS is not C99 ISO compliant
+typedef long BOOL;
+typedef unsigned char BYTE;
+typedef unsigned short WORD;
+typedef unsigned long DWORD;
+typedef long LONG;
+#endif // _MSC_VER
+
+#if (defined(_WIN32) || defined(__WIN32__))
+#pragma pack(push, 1)
+#else
+#pragma pack(1)
+#endif // WIN32
+
+typedef struct tagRGBQUAD {
+#ifdef FREEIMAGE_BIGENDIAN
+ BYTE rgbRed;
+ BYTE rgbGreen;
+ BYTE rgbBlue;
+#else
+ BYTE rgbBlue;
+ BYTE rgbGreen;
+ BYTE rgbRed;
+#endif // FREEIMAGE_BIGENDIAN
+ BYTE rgbReserved;
+} RGBQUAD;
+
+typedef struct tagRGBTRIPLE {
+#ifdef FREEIMAGE_BIGENDIAN
+ BYTE rgbtRed;
+ BYTE rgbtGreen;
+ BYTE rgbtBlue;
+#else
+ BYTE rgbtBlue;
+ BYTE rgbtGreen;
+ BYTE rgbtRed;
+#endif // FREEIMAGE_BIGENDIAN
+} RGBTRIPLE;
+
+#if (defined(_WIN32) || defined(__WIN32__))
+#pragma pack(pop)
+#else
+#pragma pack()
+#endif // WIN32
+
+typedef struct tagBITMAPINFOHEADER{
+ DWORD biSize;
+ LONG biWidth;
+ LONG biHeight;
+ WORD biPlanes;
+ WORD biBitCount;
+ DWORD biCompression;
+ DWORD biSizeImage;
+ LONG biXPelsPerMeter;
+ LONG biYPelsPerMeter;
+ DWORD biClrUsed;
+ DWORD biClrImportant;
+} BITMAPINFOHEADER, *PBITMAPINFOHEADER;
+
+typedef struct tagBITMAPINFO {
+ BITMAPINFOHEADER bmiHeader;
+ RGBQUAD bmiColors[1];
+} BITMAPINFO, *PBITMAPINFO;
+
+#endif // _WINDOWS_
+
+// Types used in the library (specific to FreeImage) ------------------------
+
+#if (defined(_WIN32) || defined(__WIN32__))
+#pragma pack(push, 1)
+#else
+#pragma pack(1)
+#endif // WIN32
+
+/** 48-bit RGB
+*/
+typedef struct tagFIRGB16 {
+ WORD red;
+ WORD green;
+ WORD blue;
+} FIRGB16;
+
+/** 64-bit RGBA
+*/
+typedef struct tagFIRGBA16 {
+ WORD red;
+ WORD green;
+ WORD blue;
+ WORD alpha;
+} FIRGBA16;
+
+/** 96-bit RGB Float
+*/
+typedef struct tagFIRGBF {
+ float red;
+ float green;
+ float blue;
+} FIRGBF;
+
+/** 128-bit RGBA Float
+*/
+typedef struct tagFIRGBAF {
+ float red;
+ float green;
+ float blue;
+ float alpha;
+} FIRGBAF;
+
+/** Data structure for COMPLEX type (complex number)
+*/
+typedef struct tagFICOMPLEX {
+ /// real part
+ double r;
+ /// imaginary part
+ double i;
+} FICOMPLEX;
+
+#if (defined(_WIN32) || defined(__WIN32__))
+#pragma pack(pop)
+#else
+#pragma pack()
+#endif // WIN32
+
+// Indexes for byte arrays, masks and shifts for treating pixels as words ---
+// These coincide with the order of RGBQUAD and RGBTRIPLE -------------------
+
+#ifndef FREEIMAGE_BIGENDIAN
+// Little Endian (x86 / MS Windows, Linux) : BGR(A) order
+#define FI_RGBA_RED 2
+#define FI_RGBA_GREEN 1
+#define FI_RGBA_BLUE 0
+#define FI_RGBA_ALPHA 3
+#define FI_RGBA_RED_MASK 0x00FF0000
+#define FI_RGBA_GREEN_MASK 0x0000FF00
+#define FI_RGBA_BLUE_MASK 0x000000FF
+#define FI_RGBA_ALPHA_MASK 0xFF000000
+#define FI_RGBA_RED_SHIFT 16
+#define FI_RGBA_GREEN_SHIFT 8
+#define FI_RGBA_BLUE_SHIFT 0
+#define FI_RGBA_ALPHA_SHIFT 24
+#else
+// Big Endian (PPC / Linux, MaxOSX) : RGB(A) order
+#define FI_RGBA_RED 0
+#define FI_RGBA_GREEN 1
+#define FI_RGBA_BLUE 2
+#define FI_RGBA_ALPHA 3
+#define FI_RGBA_RED_MASK 0xFF000000
+#define FI_RGBA_GREEN_MASK 0x00FF0000
+#define FI_RGBA_BLUE_MASK 0x0000FF00
+#define FI_RGBA_ALPHA_MASK 0x000000FF
+#define FI_RGBA_RED_SHIFT 24
+#define FI_RGBA_GREEN_SHIFT 16
+#define FI_RGBA_BLUE_SHIFT 8
+#define FI_RGBA_ALPHA_SHIFT 0
+#endif // FREEIMAGE_BIGENDIAN
+
+#define FI_RGBA_RGB_MASK (FI_RGBA_RED_MASK|FI_RGBA_GREEN_MASK|FI_RGBA_BLUE_MASK)
+
+// The 16bit macros only include masks and shifts, since each color element is not byte aligned
+
+#define FI16_555_RED_MASK 0x7C00
+#define FI16_555_GREEN_MASK 0x03E0
+#define FI16_555_BLUE_MASK 0x001F
+#define FI16_555_RED_SHIFT 10
+#define FI16_555_GREEN_SHIFT 5
+#define FI16_555_BLUE_SHIFT 0
+#define FI16_565_RED_MASK 0xF800
+#define FI16_565_GREEN_MASK 0x07E0
+#define FI16_565_BLUE_MASK 0x001F
+#define FI16_565_RED_SHIFT 11
+#define FI16_565_GREEN_SHIFT 5
+#define FI16_565_BLUE_SHIFT 0
+
+// ICC profile support ------------------------------------------------------
+
+#define FIICC_DEFAULT 0x00
+#define FIICC_COLOR_IS_CMYK 0x01
+
+FI_STRUCT (FIICCPROFILE) {
+ WORD flags; // info flag
+ DWORD size; // profile's size measured in bytes
+ void *data; // points to a block of contiguous memory containing the profile
+};
+
+// Important enums ----------------------------------------------------------
+
+/** I/O image format identifiers.
+*/
+FI_ENUM(FREE_IMAGE_FORMAT) {
+ FIF_UNKNOWN = -1,
+ FIF_BMP = 0,
+ FIF_ICO = 1,
+ FIF_JPEG = 2,
+ FIF_JNG = 3,
+ FIF_KOALA = 4,
+ FIF_LBM = 5,
+ FIF_IFF = FIF_LBM,
+ FIF_MNG = 6,
+ FIF_PBM = 7,
+ FIF_PBMRAW = 8,
+ FIF_PCD = 9,
+ FIF_PCX = 10,
+ FIF_PGM = 11,
+ FIF_PGMRAW = 12,
+ FIF_PNG = 13,
+ FIF_PPM = 14,
+ FIF_PPMRAW = 15,
+ FIF_RAS = 16,
+ FIF_TARGA = 17,
+ FIF_TIFF = 18,
+ FIF_WBMP = 19,
+ FIF_PSD = 20,
+ FIF_CUT = 21,
+ FIF_XBM = 22,
+ FIF_XPM = 23,
+ FIF_DDS = 24,
+ FIF_GIF = 25,
+ FIF_HDR = 26,
+ FIF_FAXG3 = 27,
+ FIF_SGI = 28
+};
+
+/** Image type used in FreeImage.
+*/
+FI_ENUM(FREE_IMAGE_TYPE) {
+ FIT_UNKNOWN = 0, // unknown type
+ FIT_BITMAP = 1, // standard image : 1-, 4-, 8-, 16-, 24-, 32-bit
+ FIT_UINT16 = 2, // array of unsigned short : unsigned 16-bit
+ FIT_INT16 = 3, // array of short : signed 16-bit
+ FIT_UINT32 = 4, // array of unsigned long : unsigned 32-bit
+ FIT_INT32 = 5, // array of long : signed 32-bit
+ FIT_FLOAT = 6, // array of float : 32-bit IEEE floating point
+ FIT_DOUBLE = 7, // array of double : 64-bit IEEE floating point
+ FIT_COMPLEX = 8, // array of FICOMPLEX : 2 x 64-bit IEEE floating point
+ FIT_RGB16 = 9, // 48-bit RGB image : 3 x 16-bit
+ FIT_RGBA16 = 10, // 64-bit RGBA image : 4 x 16-bit
+ FIT_RGBF = 11, // 96-bit RGB float image : 3 x 32-bit IEEE floating point
+ FIT_RGBAF = 12 // 128-bit RGBA float image : 4 x 32-bit IEEE floating point
+};
+
+/** Image color type used in FreeImage.
+*/
+FI_ENUM(FREE_IMAGE_COLOR_TYPE) {
+ FIC_MINISWHITE = 0, // min value is white
+ FIC_MINISBLACK = 1, // min value is black
+ FIC_RGB = 2, // RGB color model
+ FIC_PALETTE = 3, // color map indexed
+ FIC_RGBALPHA = 4, // RGB color model with alpha channel
+ FIC_CMYK = 5 // CMYK color model
+};
+
+/** Color quantization algorithms.
+Constants used in FreeImage_ColorQuantize.
+*/
+FI_ENUM(FREE_IMAGE_QUANTIZE) {
+ FIQ_WUQUANT = 0, // Xiaolin Wu color quantization algorithm
+ FIQ_NNQUANT = 1 // NeuQuant neural-net quantization algorithm by Anthony Dekker
+};
+
+/** Dithering algorithms.
+Constants used in FreeImage_Dither.
+*/
+FI_ENUM(FREE_IMAGE_DITHER) {
+ FID_FS = 0, // Floyd & Steinberg error diffusion
+ FID_BAYER4x4 = 1, // Bayer ordered dispersed dot dithering (order 2 dithering matrix)
+ FID_BAYER8x8 = 2, // Bayer ordered dispersed dot dithering (order 3 dithering matrix)
+ FID_CLUSTER6x6 = 3, // Ordered clustered dot dithering (order 3 - 6x6 matrix)
+ FID_CLUSTER8x8 = 4, // Ordered clustered dot dithering (order 4 - 8x8 matrix)
+ FID_CLUSTER16x16= 5, // Ordered clustered dot dithering (order 8 - 16x16 matrix)
+ FID_BAYER16x16 = 6 // Bayer ordered dispersed dot dithering (order 4 dithering matrix)
+};
+
+/** Lossless JPEG transformations
+Constants used in FreeImage_JPEGTransform
+*/
+FI_ENUM(FREE_IMAGE_JPEG_OPERATION) {
+ FIJPEG_OP_NONE = 0, // no transformation
+ FIJPEG_OP_FLIP_H = 1, // horizontal flip
+ FIJPEG_OP_FLIP_V = 2, // vertical flip
+ FIJPEG_OP_TRANSPOSE = 3, // transpose across UL-to-LR axis
+ FIJPEG_OP_TRANSVERSE = 4, // transpose across UR-to-LL axis
+ FIJPEG_OP_ROTATE_90 = 5, // 90-degree clockwise rotation
+ FIJPEG_OP_ROTATE_180 = 6, // 180-degree rotation
+ FIJPEG_OP_ROTATE_270 = 7 // 270-degree clockwise (or 90 ccw)
+};
+
+/** Tone mapping operators.
+Constants used in FreeImage_ToneMapping.
+*/
+FI_ENUM(FREE_IMAGE_TMO) {
+ FITMO_DRAGO03 = 0, // Adaptive logarithmic mapping (F. Drago, 2003)
+ FITMO_REINHARD05 = 1, // Dynamic range reduction inspired by photoreceptor physiology (E. Reinhard, 2005)
+};
+
+/** Upsampling / downsampling filters.
+Constants used in FreeImage_Rescale.
+*/
+FI_ENUM(FREE_IMAGE_FILTER) {
+ FILTER_BOX = 0, // Box, pulse, Fourier window, 1st order (constant) b-spline
+ FILTER_BICUBIC = 1, // Mitchell & Netravali's two-param cubic filter
+ FILTER_BILINEAR = 2, // Bilinear filter
+ FILTER_BSPLINE = 3, // 4th order (cubic) b-spline
+ FILTER_CATMULLROM = 4, // Catmull-Rom spline, Overhauser spline
+ FILTER_LANCZOS3 = 5 // Lanczos3 filter
+};
+
+/** Color channels.
+Constants used in color manipulation routines.
+*/
+FI_ENUM(FREE_IMAGE_COLOR_CHANNEL) {
+ FICC_RGB = 0, // Use red, green and blue channels
+ FICC_RED = 1, // Use red channel
+ FICC_GREEN = 2, // Use green channel
+ FICC_BLUE = 3, // Use blue channel
+ FICC_ALPHA = 4, // Use alpha channel
+ FICC_BLACK = 5, // Use black channel
+ FICC_REAL = 6, // Complex images: use real part
+ FICC_IMAG = 7, // Complex images: use imaginary part
+ FICC_MAG = 8, // Complex images: use magnitude
+ FICC_PHASE = 9 // Complex images: use phase
+};
+
+// Metadata support ---------------------------------------------------------
+
+/**
+ Tag data type information (based on TIFF specifications)
+
+ Note: RATIONALs are the ratio of two 32-bit integer values.
+*/
+FI_ENUM(FREE_IMAGE_MDTYPE) {
+ FIDT_NOTYPE = 0, // placeholder
+ FIDT_BYTE = 1, // 8-bit unsigned integer
+ FIDT_ASCII = 2, // 8-bit bytes w/ last byte null
+ FIDT_SHORT = 3, // 16-bit unsigned integer
+ FIDT_LONG = 4, // 32-bit unsigned integer
+ FIDT_RATIONAL = 5, // 64-bit unsigned fraction
+ FIDT_SBYTE = 6, // 8-bit signed integer
+ FIDT_UNDEFINED = 7, // 8-bit untyped data
+ FIDT_SSHORT = 8, // 16-bit signed integer
+ FIDT_SLONG = 9, // 32-bit signed integer
+ FIDT_SRATIONAL = 10, // 64-bit signed fraction
+ FIDT_FLOAT = 11, // 32-bit IEEE floating point
+ FIDT_DOUBLE = 12, // 64-bit IEEE floating point
+ FIDT_IFD = 13, // 32-bit unsigned integer (offset)
+ FIDT_PALETTE = 14 // 32-bit RGBQUAD
+};
+
+/**
+ Metadata models supported by FreeImage
+*/
+FI_ENUM(FREE_IMAGE_MDMODEL) {
+ FIMD_NODATA = -1,
+ FIMD_COMMENTS = 0, // single comment or keywords
+ FIMD_EXIF_MAIN = 1, // Exif-TIFF metadata
+ FIMD_EXIF_EXIF = 2, // Exif-specific metadata
+ FIMD_EXIF_GPS = 3, // Exif GPS metadata
+ FIMD_EXIF_MAKERNOTE = 4, // Exif maker note metadata
+ FIMD_EXIF_INTEROP = 5, // Exif interoperability metadata
+ FIMD_IPTC = 6, // IPTC/NAA metadata
+ FIMD_XMP = 7, // Abobe XMP metadata
+ FIMD_GEOTIFF = 8, // GeoTIFF metadata
+ FIMD_ANIMATION = 9, // Animation metadata
+ FIMD_CUSTOM = 10 // Used to attach other metadata types to a dib
+};
+
+/**
+ Handle to a metadata model
+*/
+FI_STRUCT (FIMETADATA) { void *data; };
+
+/**
+ Handle to a FreeImage tag
+*/
+FI_STRUCT (FITAG) { void *data; };
+
+// File IO routines ---------------------------------------------------------
+
+#ifndef FREEIMAGE_IO
+#define FREEIMAGE_IO
+
+typedef void* fi_handle;
+typedef unsigned (DLL_CALLCONV *FI_ReadProc) (void *buffer, unsigned size, unsigned count, fi_handle handle);
+typedef unsigned (DLL_CALLCONV *FI_WriteProc) (void *buffer, unsigned size, unsigned count, fi_handle handle);
+typedef int (DLL_CALLCONV *FI_SeekProc) (fi_handle handle, long offset, int origin);
+typedef long (DLL_CALLCONV *FI_TellProc) (fi_handle handle);
+
+#if (defined(_WIN32) || defined(__WIN32__))
+#pragma pack(push, 1)
+#else
+#pragma pack(1)
+#endif // WIN32
+
+FI_STRUCT(FreeImageIO) {
+ FI_ReadProc read_proc; // pointer to the function used to read data
+ FI_WriteProc write_proc; // pointer to the function used to write data
+ FI_SeekProc seek_proc; // pointer to the function used to seek
+ FI_TellProc tell_proc; // pointer to the function used to aquire the current position
+};
+
+#if (defined(_WIN32) || defined(__WIN32__))
+#pragma pack(pop)
+#else
+#pragma pack()
+#endif // WIN32
+
+/**
+Handle to a memory I/O stream
+*/
+FI_STRUCT (FIMEMORY) { void *data; };
+
+#endif // FREEIMAGE_IO
+
+// Plugin routines ----------------------------------------------------------
+
+#ifndef PLUGINS
+#define PLUGINS
+
+typedef const char *(DLL_CALLCONV *FI_FormatProc) ();
+typedef const char *(DLL_CALLCONV *FI_DescriptionProc) ();
+typedef const char *(DLL_CALLCONV *FI_ExtensionListProc) ();
+typedef const char *(DLL_CALLCONV *FI_RegExprProc) ();
+typedef void *(DLL_CALLCONV *FI_OpenProc)(FreeImageIO *io, fi_handle handle, BOOL read);
+typedef void (DLL_CALLCONV *FI_CloseProc)(FreeImageIO *io, fi_handle handle, void *data);
+typedef int (DLL_CALLCONV *FI_PageCountProc)(FreeImageIO *io, fi_handle handle, void *data);
+typedef int (DLL_CALLCONV *FI_PageCapabilityProc)(FreeImageIO *io, fi_handle handle, void *data);
+typedef FIBITMAP *(DLL_CALLCONV *FI_LoadProc)(FreeImageIO *io, fi_handle handle, int page, int flags, void *data);
+typedef BOOL (DLL_CALLCONV *FI_SaveProc)(FreeImageIO *io, FIBITMAP *dib, fi_handle handle, int page, int flags, void *data);
+typedef BOOL (DLL_CALLCONV *FI_ValidateProc)(FreeImageIO *io, fi_handle handle);
+typedef const char *(DLL_CALLCONV *FI_MimeProc) ();
+typedef BOOL (DLL_CALLCONV *FI_SupportsExportBPPProc)(int bpp);
+typedef BOOL (DLL_CALLCONV *FI_SupportsExportTypeProc)(FREE_IMAGE_TYPE type);
+typedef BOOL (DLL_CALLCONV *FI_SupportsICCProfilesProc)();
+
+FI_STRUCT (Plugin) {
+ FI_FormatProc format_proc;
+ FI_DescriptionProc description_proc;
+ FI_ExtensionListProc extension_proc;
+ FI_RegExprProc regexpr_proc;
+ FI_OpenProc open_proc;
+ FI_CloseProc close_proc;
+ FI_PageCountProc pagecount_proc;
+ FI_PageCapabilityProc pagecapability_proc;
+ FI_LoadProc load_proc;
+ FI_SaveProc save_proc;
+ FI_ValidateProc validate_proc;
+ FI_MimeProc mime_proc;
+ FI_SupportsExportBPPProc supports_export_bpp_proc;
+ FI_SupportsExportTypeProc supports_export_type_proc;
+ FI_SupportsICCProfilesProc supports_icc_profiles_proc;
+};
+
+typedef void (DLL_CALLCONV *FI_InitProc)(Plugin *plugin, int format_id);
+
+#endif // PLUGINS
+
+
+// Load / Save flag constants -----------------------------------------------
+
+#define BMP_DEFAULT 0
+#define BMP_SAVE_RLE 1
+#define CUT_DEFAULT 0
+#define DDS_DEFAULT 0
+#define FAXG3_DEFAULT 0
+#define GIF_DEFAULT 0
+#define GIF_LOAD256 1 // Load the image as a 256 color image with ununsed palette entries, if it's 16 or 2 color
+#define GIF_PLAYBACK 2 // 'Play' the GIF to generate each frame (as 32bpp) instead of returning raw frame data when loading
+#define HDR_DEFAULT 0
+#define ICO_DEFAULT 0
+#define ICO_MAKEALPHA 1 // convert to 32bpp and create an alpha channel from the AND-mask when loading
+#define IFF_DEFAULT 0
+#define JPEG_DEFAULT 0 // loading (see JPEG_FAST); saving (see JPEG_QUALITYGOOD)
+#define JPEG_FAST 0x0001 // load the file as fast as possible, sacrificing some quality
+#define JPEG_ACCURATE 0x0002 // load the file with the best quality, sacrificing some speed
+#define JPEG_CMYK 0x0004 // load separated CMYK "as is" (use | to combine with other load flags)
+#define JPEG_QUALITYSUPERB 0x80 // save with superb quality (100:1)
+#define JPEG_QUALITYGOOD 0x0100 // save with good quality (75:1)
+#define JPEG_QUALITYNORMAL 0x0200 // save with normal quality (50:1)
+#define JPEG_QUALITYAVERAGE 0x0400 // save with average quality (25:1)
+#define JPEG_QUALITYBAD 0x0800 // save with bad quality (10:1)
+#define JPEG_PROGRESSIVE 0x2000 // save as a progressive-JPEG (use | to combine with other save flags)
+#define KOALA_DEFAULT 0
+#define LBM_DEFAULT 0
+#define MNG_DEFAULT 0
+#define PCD_DEFAULT 0
+#define PCD_BASE 1 // load the bitmap sized 768 x 512
+#define PCD_BASEDIV4 2 // load the bitmap sized 384 x 256
+#define PCD_BASEDIV16 3 // load the bitmap sized 192 x 128
+#define PCX_DEFAULT 0
+#define PNG_DEFAULT 0
+#define PNG_IGNOREGAMMA 1 // avoid gamma correction
+#define PNM_DEFAULT 0
+#define PNM_SAVE_RAW 0 // If set the writer saves in RAW format (i.e. P4, P5 or P6)
+#define PNM_SAVE_ASCII 1 // If set the writer saves in ASCII format (i.e. P1, P2 or P3)
+#define PSD_DEFAULT 0
+#define RAS_DEFAULT 0
+#define SGI_DEFAULT 0
+#define TARGA_DEFAULT 0
+#define TARGA_LOAD_RGB888 1 // If set the loader converts RGB555 and ARGB8888 -> RGB888.
+#define TIFF_DEFAULT 0
+#define TIFF_CMYK 0x0001 // reads/stores tags for separated CMYK (use | to combine with compression flags)
+#define TIFF_PACKBITS 0x0100 // save using PACKBITS compression
+#define TIFF_DEFLATE 0x0200 // save using DEFLATE compression (a.k.a. ZLIB compression)
+#define TIFF_ADOBE_DEFLATE 0x0400 // save using ADOBE DEFLATE compression
+#define TIFF_NONE 0x0800 // save without any compression
+#define TIFF_CCITTFAX3 0x1000 // save using CCITT Group 3 fax encoding
+#define TIFF_CCITTFAX4 0x2000 // save using CCITT Group 4 fax encoding
+#define TIFF_LZW 0x4000 // save using LZW compression
+#define TIFF_JPEG 0x8000 // save using JPEG compression
+#define WBMP_DEFAULT 0
+#define XBM_DEFAULT 0
+#define XPM_DEFAULT 0
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Init / Error routines ----------------------------------------------------
+
+DLL_API void DLL_CALLCONV FreeImage_Initialise(BOOL load_local_plugins_only FI_DEFAULT(FALSE));
+DLL_API void DLL_CALLCONV FreeImage_DeInitialise(void);
+
+// Version routines ---------------------------------------------------------
+
+DLL_API const char *DLL_CALLCONV FreeImage_GetVersion(void);
+DLL_API const char *DLL_CALLCONV FreeImage_GetCopyrightMessage(void);
+
+// Message output functions -------------------------------------------------
+
+DLL_API void DLL_CALLCONV FreeImage_OutputMessageProc(int fif, const char *fmt, ...);
+
+typedef void (*FreeImage_OutputMessageFunction)(FREE_IMAGE_FORMAT fif, const char *msg);
+DLL_API void DLL_CALLCONV FreeImage_SetOutputMessage(FreeImage_OutputMessageFunction omf);
+
+// Allocate / Clone / Unload routines ---------------------------------------
+
+DLL_API FIBITMAP *DLL_CALLCONV FreeImage_Allocate(int width, int height, int bpp, unsigned red_mask FI_DEFAULT(0), unsigned green_mask FI_DEFAULT(0), unsigned blue_mask FI_DEFAULT(0));
+DLL_API FIBITMAP *DLL_CALLCONV FreeImage_AllocateT(FREE_IMAGE_TYPE type, int width, int height, int bpp FI_DEFAULT(8), unsigned red_mask FI_DEFAULT(0), unsigned green_mask FI_DEFAULT(0), unsigned blue_mask FI_DEFAULT(0));
+DLL_API FIBITMAP * DLL_CALLCONV FreeImage_Clone(FIBITMAP *dib);
+DLL_API void DLL_CALLCONV FreeImage_Unload(FIBITMAP *dib);
+
+// Load / Save routines -----------------------------------------------------
+
+DLL_API FIBITMAP *DLL_CALLCONV FreeImage_Load(FREE_IMAGE_FORMAT fif, const char *filename, int flags FI_DEFAULT(0));
+DLL_API FIBITMAP *DLL_CALLCONV FreeImage_LoadU(FREE_IMAGE_FORMAT fif, const wchar_t *filename, int flags FI_DEFAULT(0));
+DLL_API FIBITMAP *DLL_CALLCONV FreeImage_LoadFromHandle(FREE_IMAGE_FORMAT fif, FreeImageIO *io, fi_handle handle, int flags FI_DEFAULT(0));
+DLL_API BOOL DLL_CALLCONV FreeImage_Save(FREE_IMAGE_FORMAT fif, FIBITMAP *dib, const char *filename, int flags FI_DEFAULT(0));
+DLL_API BOOL DLL_CALLCONV FreeImage_SaveU(FREE_IMAGE_FORMAT fif, FIBITMAP *dib, const wchar_t *filename, int flags FI_DEFAULT(0));
+DLL_API BOOL DLL_CALLCONV FreeImage_SaveToHandle(FREE_IMAGE_FORMAT fif, FIBITMAP *dib, FreeImageIO *io, fi_handle handle, int flags FI_DEFAULT(0));
+
+// Memory I/O stream routines -----------------------------------------------
+
+DLL_API FIMEMORY *DLL_CALLCONV FreeImage_OpenMemory(BYTE *data FI_DEFAULT(0), DWORD size_in_bytes FI_DEFAULT(0));
+DLL_API void DLL_CALLCONV FreeImage_CloseMemory(FIMEMORY *stream);
+DLL_API FIBITMAP *DLL_CALLCONV FreeImage_LoadFromMemory(FREE_IMAGE_FORMAT fif, FIMEMORY *stream, int flags FI_DEFAULT(0));
+DLL_API BOOL DLL_CALLCONV FreeImage_SaveToMemory(FREE_IMAGE_FORMAT fif, FIBITMAP *dib, FIMEMORY *stream, int flags FI_DEFAULT(0));
+DLL_API long DLL_CALLCONV FreeImage_TellMemory(FIMEMORY *stream);
+DLL_API BOOL DLL_CALLCONV FreeImage_SeekMemory(FIMEMORY *stream, long offset, int origin);
+DLL_API BOOL DLL_CALLCONV FreeImage_AcquireMemory(FIMEMORY *stream, BYTE **data, DWORD *size_in_bytes);
+DLL_API unsigned DLL_CALLCONV FreeImage_ReadMemory(void *buffer, unsigned size, unsigned count, FIMEMORY *stream);
+DLL_API unsigned DLL_CALLCONV FreeImage_WriteMemory(const void *buffer, unsigned size, unsigned count, FIMEMORY *stream);
+DLL_API FIMULTIBITMAP *DLL_CALLCONV FreeImage_LoadMultiBitmapFromMemory(FREE_IMAGE_FORMAT fif, FIMEMORY *stream, int flags FI_DEFAULT(0));
+
+// Plugin Interface ---------------------------------------------------------
+
+DLL_API FREE_IMAGE_FORMAT DLL_CALLCONV FreeImage_RegisterLocalPlugin(FI_InitProc proc_address, const char *format FI_DEFAULT(0), const char *description FI_DEFAULT(0), const char *extension FI_DEFAULT(0), const char *regexpr FI_DEFAULT(0));
+DLL_API FREE_IMAGE_FORMAT DLL_CALLCONV FreeImage_RegisterExternalPlugin(const char *path, const char *format FI_DEFAULT(0), const char *description FI_DEFAULT(0), const char *extension FI_DEFAULT(0), const char *regexpr FI_DEFAULT(0));
+DLL_API int DLL_CALLCONV FreeImage_GetFIFCount(void);
+DLL_API int DLL_CALLCONV FreeImage_SetPluginEnabled(FREE_IMAGE_FORMAT fif, BOOL enable);
+DLL_API int DLL_CALLCONV FreeImage_IsPluginEnabled(FREE_IMAGE_FORMAT fif);
+DLL_API FREE_IMAGE_FORMAT DLL_CALLCONV FreeImage_GetFIFFromFormat(const char *format);
+DLL_API FREE_IMAGE_FORMAT DLL_CALLCONV FreeImage_GetFIFFromMime(const char *mime);
+DLL_API const char *DLL_CALLCONV FreeImage_GetFormatFromFIF(FREE_IMAGE_FORMAT fif);
+DLL_API const char *DLL_CALLCONV FreeImage_GetFIFExtensionList(FREE_IMAGE_FORMAT fif);
+DLL_API const char *DLL_CALLCONV FreeImage_GetFIFDescription(FREE_IMAGE_FORMAT fif);
+DLL_API const char *DLL_CALLCONV FreeImage_GetFIFRegExpr(FREE_IMAGE_FORMAT fif);
+DLL_API const char *DLL_CALLCONV FreeImage_GetFIFMimeType(FREE_IMAGE_FORMAT fif);
+DLL_API FREE_IMAGE_FORMAT DLL_CALLCONV FreeImage_GetFIFFromFilename(const char *filename);
+DLL_API FREE_IMAGE_FORMAT DLL_CALLCONV FreeImage_GetFIFFromFilenameU(const wchar_t *filename);
+DLL_API BOOL DLL_CALLCONV FreeImage_FIFSupportsReading(FREE_IMAGE_FORMAT fif);
+DLL_API BOOL DLL_CALLCONV FreeImage_FIFSupportsWriting(FREE_IMAGE_FORMAT fif);
+DLL_API BOOL DLL_CALLCONV FreeImage_FIFSupportsExportBPP(FREE_IMAGE_FORMAT fif, int bpp);
+DLL_API BOOL DLL_CALLCONV FreeImage_FIFSupportsExportType(FREE_IMAGE_FORMAT fif, FREE_IMAGE_TYPE type);
+DLL_API BOOL DLL_CALLCONV FreeImage_FIFSupportsICCProfiles(FREE_IMAGE_FORMAT fif);
+
+// Multipaging interface ----------------------------------------------------
+
+DLL_API FIMULTIBITMAP * DLL_CALLCONV FreeImage_OpenMultiBitmap(FREE_IMAGE_FORMAT fif, const char *filename, BOOL create_new, BOOL read_only, BOOL keep_cache_in_memory FI_DEFAULT(FALSE), int flags FI_DEFAULT(0));
+DLL_API BOOL DLL_CALLCONV FreeImage_CloseMultiBitmap(FIMULTIBITMAP *bitmap, int flags FI_DEFAULT(0));
+DLL_API int DLL_CALLCONV FreeImage_GetPageCount(FIMULTIBITMAP *bitmap);
+DLL_API void DLL_CALLCONV FreeImage_AppendPage(FIMULTIBITMAP *bitmap, FIBITMAP *data);
+DLL_API void DLL_CALLCONV FreeImage_InsertPage(FIMULTIBITMAP *bitmap, int page, FIBITMAP *data);
+DLL_API void DLL_CALLCONV FreeImage_DeletePage(FIMULTIBITMAP *bitmap, int page);
+DLL_API FIBITMAP * DLL_CALLCONV FreeImage_LockPage(FIMULTIBITMAP *bitmap, int page);
+DLL_API void DLL_CALLCONV FreeImage_UnlockPage(FIMULTIBITMAP *bitmap, FIBITMAP *data, BOOL changed);
+DLL_API BOOL DLL_CALLCONV FreeImage_MovePage(FIMULTIBITMAP *bitmap, int target, int source);
+DLL_API BOOL DLL_CALLCONV FreeImage_GetLockedPageNumbers(FIMULTIBITMAP *bitmap, int *pages, int *count);
+
+// Filetype request routines ------------------------------------------------
+
+DLL_API FREE_IMAGE_FORMAT DLL_CALLCONV FreeImage_GetFileType(const char *filename, int size FI_DEFAULT(0));
+DLL_API FREE_IMAGE_FORMAT DLL_CALLCONV FreeImage_GetFileTypeU(const wchar_t *filename, int size FI_DEFAULT(0));
+DLL_API FREE_IMAGE_FORMAT DLL_CALLCONV FreeImage_GetFileTypeFromHandle(FreeImageIO *io, fi_handle handle, int size FI_DEFAULT(0));
+DLL_API FREE_IMAGE_FORMAT DLL_CALLCONV FreeImage_GetFileTypeFromMemory(FIMEMORY *stream, int size FI_DEFAULT(0));
+
+// Image type request routine -----------------------------------------------
+
+DLL_API FREE_IMAGE_TYPE DLL_CALLCONV FreeImage_GetImageType(FIBITMAP *dib);
+
+// FreeImage helper routines ------------------------------------------------
+
+DLL_API BOOL DLL_CALLCONV FreeImage_IsLittleEndian(void);
+DLL_API BOOL DLL_CALLCONV FreeImage_LookupX11Color(const char *szColor, BYTE *nRed, BYTE *nGreen, BYTE *nBlue);
+DLL_API BOOL DLL_CALLCONV FreeImage_LookupSVGColor(const char *szColor, BYTE *nRed, BYTE *nGreen, BYTE *nBlue);
+
+
+// Pixel access routines ----------------------------------------------------
+
+DLL_API BYTE *DLL_CALLCONV FreeImage_GetBits(FIBITMAP *dib);
+DLL_API BYTE *DLL_CALLCONV FreeImage_GetScanLine(FIBITMAP *dib, int scanline);
+
+DLL_API BOOL DLL_CALLCONV FreeImage_GetPixelIndex(FIBITMAP *dib, unsigned x, unsigned y, BYTE *value);
+DLL_API BOOL DLL_CALLCONV FreeImage_GetPixelColor(FIBITMAP *dib, unsigned x, unsigned y, RGBQUAD *value);
+DLL_API BOOL DLL_CALLCONV FreeImage_SetPixelIndex(FIBITMAP *dib, unsigned x, unsigned y, BYTE *value);
+DLL_API BOOL DLL_CALLCONV FreeImage_SetPixelColor(FIBITMAP *dib, unsigned x, unsigned y, RGBQUAD *value);
+
+// DIB info routines --------------------------------------------------------
+
+DLL_API unsigned DLL_CALLCONV FreeImage_GetColorsUsed(FIBITMAP *dib);
+DLL_API unsigned DLL_CALLCONV FreeImage_GetBPP(FIBITMAP *dib);
+DLL_API unsigned DLL_CALLCONV FreeImage_GetWidth(FIBITMAP *dib);
+DLL_API unsigned DLL_CALLCONV FreeImage_GetHeight(FIBITMAP *dib);
+DLL_API unsigned DLL_CALLCONV FreeImage_GetLine(FIBITMAP *dib);
+DLL_API unsigned DLL_CALLCONV FreeImage_GetPitch(FIBITMAP *dib);
+DLL_API unsigned DLL_CALLCONV FreeImage_GetDIBSize(FIBITMAP *dib);
+DLL_API RGBQUAD *DLL_CALLCONV FreeImage_GetPalette(FIBITMAP *dib);
+
+DLL_API unsigned DLL_CALLCONV FreeImage_GetDotsPerMeterX(FIBITMAP *dib);
+DLL_API unsigned DLL_CALLCONV FreeImage_GetDotsPerMeterY(FIBITMAP *dib);
+DLL_API void DLL_CALLCONV FreeImage_SetDotsPerMeterX(FIBITMAP *dib, unsigned res);
+DLL_API void DLL_CALLCONV FreeImage_SetDotsPerMeterY(FIBITMAP *dib, unsigned res);
+
+DLL_API BITMAPINFOHEADER *DLL_CALLCONV FreeImage_GetInfoHeader(FIBITMAP *dib);
+DLL_API BITMAPINFO *DLL_CALLCONV FreeImage_GetInfo(FIBITMAP *dib);
+DLL_API FREE_IMAGE_COLOR_TYPE DLL_CALLCONV FreeImage_GetColorType(FIBITMAP *dib);
+
+DLL_API unsigned DLL_CALLCONV FreeImage_GetRedMask(FIBITMAP *dib);
+DLL_API unsigned DLL_CALLCONV FreeImage_GetGreenMask(FIBITMAP *dib);
+DLL_API unsigned DLL_CALLCONV FreeImage_GetBlueMask(FIBITMAP *dib);
+
+DLL_API unsigned DLL_CALLCONV FreeImage_GetTransparencyCount(FIBITMAP *dib);
+DLL_API BYTE * DLL_CALLCONV FreeImage_GetTransparencyTable(FIBITMAP *dib);
+DLL_API void DLL_CALLCONV FreeImage_SetTransparent(FIBITMAP *dib, BOOL enabled);
+DLL_API void DLL_CALLCONV FreeImage_SetTransparencyTable(FIBITMAP *dib, BYTE *table, int count);
+DLL_API BOOL DLL_CALLCONV FreeImage_IsTransparent(FIBITMAP *dib);
+
+DLL_API BOOL DLL_CALLCONV FreeImage_HasBackgroundColor(FIBITMAP *dib);
+DLL_API BOOL DLL_CALLCONV FreeImage_GetBackgroundColor(FIBITMAP *dib, RGBQUAD *bkcolor);
+DLL_API BOOL DLL_CALLCONV FreeImage_SetBackgroundColor(FIBITMAP *dib, RGBQUAD *bkcolor);
+
+
+// ICC profile routines -----------------------------------------------------
+
+DLL_API FIICCPROFILE *DLL_CALLCONV FreeImage_GetICCProfile(FIBITMAP *dib);
+DLL_API FIICCPROFILE *DLL_CALLCONV FreeImage_CreateICCProfile(FIBITMAP *dib, void *data, long size);
+DLL_API void DLL_CALLCONV FreeImage_DestroyICCProfile(FIBITMAP *dib);
+
+// Line conversion routines -------------------------------------------------
+
+DLL_API void DLL_CALLCONV FreeImage_ConvertLine1To4(BYTE *target, BYTE *source, int width_in_pixels);
+DLL_API void DLL_CALLCONV FreeImage_ConvertLine8To4(BYTE *target, BYTE *source, int width_in_pixels, RGBQUAD *palette);
+DLL_API void DLL_CALLCONV FreeImage_ConvertLine16To4_555(BYTE *target, BYTE *source, int width_in_pixels);
+DLL_API void DLL_CALLCONV FreeImage_ConvertLine16To4_565(BYTE *target, BYTE *source, int width_in_pixels);
+DLL_API void DLL_CALLCONV FreeImage_ConvertLine24To4(BYTE *target, BYTE *source, int width_in_pixels);
+DLL_API void DLL_CALLCONV FreeImage_ConvertLine32To4(BYTE *target, BYTE *source, int width_in_pixels);
+DLL_API void DLL_CALLCONV FreeImage_ConvertLine1To8(BYTE *target, BYTE *source, int width_in_pixels);
+DLL_API void DLL_CALLCONV FreeImage_ConvertLine4To8(BYTE *target, BYTE *source, int width_in_pixels);
+DLL_API void DLL_CALLCONV FreeImage_ConvertLine16To8_555(BYTE *target, BYTE *source, int width_in_pixels);
+DLL_API void DLL_CALLCONV FreeImage_ConvertLine16To8_565(BYTE *target, BYTE *source, int width_in_pixels);
+DLL_API void DLL_CALLCONV FreeImage_ConvertLine24To8(BYTE *target, BYTE *source, int width_in_pixels);
+DLL_API void DLL_CALLCONV FreeImage_ConvertLine32To8(BYTE *target, BYTE *source, int width_in_pixels);
+DLL_API void DLL_CALLCONV FreeImage_ConvertLine1To16_555(BYTE *target, BYTE *source, int width_in_pixels, RGBQUAD *palette);
+DLL_API void DLL_CALLCONV FreeImage_ConvertLine4To16_555(BYTE *target, BYTE *source, int width_in_pixels, RGBQUAD *palette);
+DLL_API void DLL_CALLCONV FreeImage_ConvertLine8To16_555(BYTE *target, BYTE *source, int width_in_pixels, RGBQUAD *palette);
+DLL_API void DLL_CALLCONV FreeImage_ConvertLine16_565_To16_555(BYTE *target, BYTE *source, int width_in_pixels);
+DLL_API void DLL_CALLCONV FreeImage_ConvertLine24To16_555(BYTE *target, BYTE *source, int width_in_pixels);
+DLL_API void DLL_CALLCONV FreeImage_ConvertLine32To16_555(BYTE *target, BYTE *source, int width_in_pixels);
+DLL_API void DLL_CALLCONV FreeImage_ConvertLine1To16_565(BYTE *target, BYTE *source, int width_in_pixels, RGBQUAD *palette);
+DLL_API void DLL_CALLCONV FreeImage_ConvertLine4To16_565(BYTE *target, BYTE *source, int width_in_pixels, RGBQUAD *palette);
+DLL_API void DLL_CALLCONV FreeImage_ConvertLine8To16_565(BYTE *target, BYTE *source, int width_in_pixels, RGBQUAD *palette);
+DLL_API void DLL_CALLCONV FreeImage_ConvertLine16_555_To16_565(BYTE *target, BYTE *source, int width_in_pixels);
+DLL_API void DLL_CALLCONV FreeImage_ConvertLine24To16_565(BYTE *target, BYTE *source, int width_in_pixels);
+DLL_API void DLL_CALLCONV FreeImage_ConvertLine32To16_565(BYTE *target, BYTE *source, int width_in_pixels);
+DLL_API void DLL_CALLCONV FreeImage_ConvertLine1To24(BYTE *target, BYTE *source, int width_in_pixels, RGBQUAD *palette);
+DLL_API void DLL_CALLCONV FreeImage_ConvertLine4To24(BYTE *target, BYTE *source, int width_in_pixels, RGBQUAD *palette);
+DLL_API void DLL_CALLCONV FreeImage_ConvertLine8To24(BYTE *target, BYTE *source, int width_in_pixels, RGBQUAD *palette);
+DLL_API void DLL_CALLCONV FreeImage_ConvertLine16To24_555(BYTE *target, BYTE *source, int width_in_pixels);
+DLL_API void DLL_CALLCONV FreeImage_ConvertLine16To24_565(BYTE *target, BYTE *source, int width_in_pixels);
+DLL_API void DLL_CALLCONV FreeImage_ConvertLine32To24(BYTE *target, BYTE *source, int width_in_pixels);
+DLL_API void DLL_CALLCONV FreeImage_ConvertLine1To32(BYTE *target, BYTE *source, int width_in_pixels, RGBQUAD *palette);
+DLL_API void DLL_CALLCONV FreeImage_ConvertLine4To32(BYTE *target, BYTE *source, int width_in_pixels, RGBQUAD *palette);
+DLL_API void DLL_CALLCONV FreeImage_ConvertLine8To32(BYTE *target, BYTE *source, int width_in_pixels, RGBQUAD *palette);
+DLL_API void DLL_CALLCONV FreeImage_ConvertLine16To32_555(BYTE *target, BYTE *source, int width_in_pixels);
+DLL_API void DLL_CALLCONV FreeImage_ConvertLine16To32_565(BYTE *target, BYTE *source, int width_in_pixels);
+DLL_API void DLL_CALLCONV FreeImage_ConvertLine24To32(BYTE *target, BYTE *source, int width_in_pixels);
+
+// Smart conversion routines ------------------------------------------------
+
+DLL_API FIBITMAP *DLL_CALLCONV FreeImage_ConvertTo4Bits(FIBITMAP *dib);
+DLL_API FIBITMAP *DLL_CALLCONV FreeImage_ConvertTo8Bits(FIBITMAP *dib);
+DLL_API FIBITMAP *DLL_CALLCONV FreeImage_ConvertToGreyscale(FIBITMAP *dib);
+DLL_API FIBITMAP *DLL_CALLCONV FreeImage_ConvertTo16Bits555(FIBITMAP *dib);
+DLL_API FIBITMAP *DLL_CALLCONV FreeImage_ConvertTo16Bits565(FIBITMAP *dib);
+DLL_API FIBITMAP *DLL_CALLCONV FreeImage_ConvertTo24Bits(FIBITMAP *dib);
+DLL_API FIBITMAP *DLL_CALLCONV FreeImage_ConvertTo32Bits(FIBITMAP *dib);
+DLL_API FIBITMAP *DLL_CALLCONV FreeImage_ColorQuantize(FIBITMAP *dib, FREE_IMAGE_QUANTIZE quantize);
+DLL_API FIBITMAP *DLL_CALLCONV FreeImage_ColorQuantizeEx(FIBITMAP *dib, FREE_IMAGE_QUANTIZE quantize FI_DEFAULT(FIQ_WUQUANT), int PaletteSize FI_DEFAULT(256), int ReserveSize FI_DEFAULT(0), RGBQUAD *ReservePalette FI_DEFAULT(NULL));
+DLL_API FIBITMAP *DLL_CALLCONV FreeImage_Threshold(FIBITMAP *dib, BYTE T);
+DLL_API FIBITMAP *DLL_CALLCONV FreeImage_Dither(FIBITMAP *dib, FREE_IMAGE_DITHER algorithm);
+
+DLL_API FIBITMAP *DLL_CALLCONV FreeImage_ConvertFromRawBits(BYTE *bits, int width, int height, int pitch, unsigned bpp, unsigned red_mask, unsigned green_mask, unsigned blue_mask, BOOL topdown FI_DEFAULT(FALSE));
+DLL_API void DLL_CALLCONV FreeImage_ConvertToRawBits(BYTE *bits, FIBITMAP *dib, int pitch, unsigned bpp, unsigned red_mask, unsigned green_mask, unsigned blue_mask, BOOL topdown FI_DEFAULT(FALSE));
+
+DLL_API FIBITMAP *DLL_CALLCONV FreeImage_ConvertToRGBF(FIBITMAP *dib);
+
+DLL_API FIBITMAP *DLL_CALLCONV FreeImage_ConvertToStandardType(FIBITMAP *src, BOOL scale_linear FI_DEFAULT(TRUE));
+DLL_API FIBITMAP *DLL_CALLCONV FreeImage_ConvertToType(FIBITMAP *src, FREE_IMAGE_TYPE dst_type, BOOL scale_linear FI_DEFAULT(TRUE));
+
+// tone mapping operators
+DLL_API FIBITMAP *DLL_CALLCONV FreeImage_ToneMapping(FIBITMAP *dib, FREE_IMAGE_TMO tmo, double first_param FI_DEFAULT(0), double second_param FI_DEFAULT(0));
+DLL_API FIBITMAP* DLL_CALLCONV FreeImage_TmoDrago03(FIBITMAP *src, double gamma FI_DEFAULT(2.2), double exposure FI_DEFAULT(0));
+DLL_API FIBITMAP* DLL_CALLCONV FreeImage_TmoReinhard05(FIBITMAP *src, double intensity FI_DEFAULT(0), double contrast FI_DEFAULT(0));
+
+// ZLib interface -----------------------------------------------------------
+
+DLL_API DWORD DLL_CALLCONV FreeImage_ZLibCompress(BYTE *target, DWORD target_size, BYTE *source, DWORD source_size);
+DLL_API DWORD DLL_CALLCONV FreeImage_ZLibUncompress(BYTE *target, DWORD target_size, BYTE *source, DWORD source_size);
+DLL_API DWORD DLL_CALLCONV FreeImage_ZLibGZip(BYTE *target, DWORD target_size, BYTE *source, DWORD source_size);
+DLL_API DWORD DLL_CALLCONV FreeImage_ZLibGUnzip(BYTE *target, DWORD target_size, BYTE *source, DWORD source_size);
+DLL_API DWORD DLL_CALLCONV FreeImage_ZLibCRC32(DWORD crc, BYTE *source, DWORD source_size);
+
+// --------------------------------------------------------------------------
+// Metadata routines --------------------------------------------------------
+// --------------------------------------------------------------------------
+
+// tag creation / destruction
+DLL_API FITAG *DLL_CALLCONV FreeImage_CreateTag();
+DLL_API void DLL_CALLCONV FreeImage_DeleteTag(FITAG *tag);
+DLL_API FITAG *DLL_CALLCONV FreeImage_CloneTag(FITAG *tag);
+
+// tag getters and setters
+DLL_API const char *DLL_CALLCONV FreeImage_GetTagKey(FITAG *tag);
+DLL_API const char *DLL_CALLCONV FreeImage_GetTagDescription(FITAG *tag);
+DLL_API WORD DLL_CALLCONV FreeImage_GetTagID(FITAG *tag);
+DLL_API FREE_IMAGE_MDTYPE DLL_CALLCONV FreeImage_GetTagType(FITAG *tag);
+DLL_API DWORD DLL_CALLCONV FreeImage_GetTagCount(FITAG *tag);
+DLL_API DWORD DLL_CALLCONV FreeImage_GetTagLength(FITAG *tag);
+DLL_API const void *DLL_CALLCONV FreeImage_GetTagValue(FITAG *tag);
+
+DLL_API BOOL DLL_CALLCONV FreeImage_SetTagKey(FITAG *tag, const char *key);
+DLL_API BOOL DLL_CALLCONV FreeImage_SetTagDescription(FITAG *tag, const char *description);
+DLL_API BOOL DLL_CALLCONV FreeImage_SetTagID(FITAG *tag, WORD id);
+DLL_API BOOL DLL_CALLCONV FreeImage_SetTagType(FITAG *tag, FREE_IMAGE_MDTYPE type);
+DLL_API BOOL DLL_CALLCONV FreeImage_SetTagCount(FITAG *tag, DWORD count);
+DLL_API BOOL DLL_CALLCONV FreeImage_SetTagLength(FITAG *tag, DWORD length);
+DLL_API BOOL DLL_CALLCONV FreeImage_SetTagValue(FITAG *tag, const void *value);
+
+// iterator
+DLL_API FIMETADATA *DLL_CALLCONV FreeImage_FindFirstMetadata(FREE_IMAGE_MDMODEL model, FIBITMAP *dib, FITAG **tag);
+DLL_API BOOL DLL_CALLCONV FreeImage_FindNextMetadata(FIMETADATA *mdhandle, FITAG **tag);
+DLL_API void DLL_CALLCONV FreeImage_FindCloseMetadata(FIMETADATA *mdhandle);
+
+// metadata setter and getter
+DLL_API BOOL DLL_CALLCONV FreeImage_SetMetadata(FREE_IMAGE_MDMODEL model, FIBITMAP *dib, const char *key, FITAG *tag);
+DLL_API BOOL DLL_CALLCONV FreeImage_GetMetadata(FREE_IMAGE_MDMODEL model, FIBITMAP *dib, const char *key, FITAG **tag);
+
+// helpers
+DLL_API unsigned DLL_CALLCONV FreeImage_GetMetadataCount(FREE_IMAGE_MDMODEL model, FIBITMAP *dib);
+
+// tag to C string conversion
+DLL_API const char* DLL_CALLCONV FreeImage_TagToString(FREE_IMAGE_MDMODEL model, FITAG *tag, char *Make FI_DEFAULT(NULL));
+
+// --------------------------------------------------------------------------
+// Image manipulation toolkit -----------------------------------------------
+// --------------------------------------------------------------------------
+
+// rotation and flipping
+DLL_API FIBITMAP *DLL_CALLCONV FreeImage_RotateClassic(FIBITMAP *dib, double angle);
+DLL_API FIBITMAP *DLL_CALLCONV FreeImage_RotateEx(FIBITMAP *dib, double angle, double x_shift, double y_shift, double x_origin, double y_origin, BOOL use_mask);
+DLL_API BOOL DLL_CALLCONV FreeImage_FlipHorizontal(FIBITMAP *dib);
+DLL_API BOOL DLL_CALLCONV FreeImage_FlipVertical(FIBITMAP *dib);
+DLL_API BOOL DLL_CALLCONV FreeImage_JPEGTransform(const char *src_file, const char *dst_file, FREE_IMAGE_JPEG_OPERATION operation, BOOL perfect FI_DEFAULT(FALSE));
+
+// upsampling / downsampling
+DLL_API FIBITMAP *DLL_CALLCONV FreeImage_Rescale(FIBITMAP *dib, int dst_width, int dst_height, FREE_IMAGE_FILTER filter);
+DLL_API FIBITMAP *DLL_CALLCONV FreeImage_MakeThumbnail(FIBITMAP *dib, int max_pixel_size, BOOL convert FI_DEFAULT(TRUE));
+
+// color manipulation routines (point operations)
+DLL_API BOOL DLL_CALLCONV FreeImage_AdjustCurve(FIBITMAP *dib, BYTE *LUT, FREE_IMAGE_COLOR_CHANNEL channel);
+DLL_API BOOL DLL_CALLCONV FreeImage_AdjustGamma(FIBITMAP *dib, double gamma);
+DLL_API BOOL DLL_CALLCONV FreeImage_AdjustBrightness(FIBITMAP *dib, double percentage);
+DLL_API BOOL DLL_CALLCONV FreeImage_AdjustContrast(FIBITMAP *dib, double percentage);
+DLL_API BOOL DLL_CALLCONV FreeImage_Invert(FIBITMAP *dib);
+DLL_API BOOL DLL_CALLCONV FreeImage_GetHistogram(FIBITMAP *dib, DWORD *histo, FREE_IMAGE_COLOR_CHANNEL channel FI_DEFAULT(FICC_BLACK));
+
+// channel processing routines
+DLL_API FIBITMAP *DLL_CALLCONV FreeImage_GetChannel(FIBITMAP *dib, FREE_IMAGE_COLOR_CHANNEL channel);
+DLL_API BOOL DLL_CALLCONV FreeImage_SetChannel(FIBITMAP *dib, FIBITMAP *dib8, FREE_IMAGE_COLOR_CHANNEL channel);
+DLL_API FIBITMAP *DLL_CALLCONV FreeImage_GetComplexChannel(FIBITMAP *src, FREE_IMAGE_COLOR_CHANNEL channel);
+DLL_API BOOL DLL_CALLCONV FreeImage_SetComplexChannel(FIBITMAP *dst, FIBITMAP *src, FREE_IMAGE_COLOR_CHANNEL channel);
+
+// copy / paste / composite routines
+DLL_API FIBITMAP *DLL_CALLCONV FreeImage_Copy(FIBITMAP *dib, int left, int top, int right, int bottom);
+DLL_API BOOL DLL_CALLCONV FreeImage_Paste(FIBITMAP *dst, FIBITMAP *src, int left, int top, int alpha);
+DLL_API FIBITMAP *DLL_CALLCONV FreeImage_Composite(FIBITMAP *fg, BOOL useFileBkg FI_DEFAULT(FALSE), RGBQUAD *appBkColor FI_DEFAULT(NULL), FIBITMAP *bg FI_DEFAULT(NULL));
+DLL_API BOOL DLL_CALLCONV FreeImage_JPEGCrop(const char *src_file, const char *dst_file, int left, int top, int right, int bottom);
+
+
+// restore the borland-specific enum size option
+#if defined(__BORLANDC__)
+#pragma option pop
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // FREEIMAGE_H
diff --git a/Plugins/avh_import/sdk/m_imgsrvc.h b/Plugins/avh_import/sdk/m_imgsrvc.h
new file mode 100644
index 0000000..923f88e
--- /dev/null
+++ b/Plugins/avh_import/sdk/m_imgsrvc.h
@@ -0,0 +1,502 @@
+/*
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2007 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+-----------------------------------------------------------------------
+Miranda Image services plugin / API definitions
+Provides various services for image loading, saving and manipulations.
+
+This module is based on the freeimage library, copyrighted by the FreeImage
+Project members.
+
+Miranda plugin code (c) 2007 by Nightwish, silvercircle@gmail.com, all else (C)
+by the FreeImage project (http://freeimage.sourceforge.net)
+
+*/
+
+#ifndef __M_IMGSRVC_H
+#define __M_IMGSRVC_H
+
+#define _FI_MIMPLUGIN 1
+
+#include "m_freeimage.h"
+
+#define FI_IF_VERSION (PLUGIN_MAKE_VERSION(0, 0, 1, 0)) // interface version - must match
+
+// memory i/o defs
+
+/*
+ * this struct defines a memio job.
+ * datalen and filename must match and must be populated to the size of the memory buffer (caution)
+ * data must point to the buffer.
+ * curpos is internal and should be initialized to 0
+ */
+
+typedef struct fiio_mem_handle_s {
+ long filelen,datalen,curpos;
+ void *data;
+} fiio_mem_handle;
+
+/* it is up to the user to create a fiio_mem_handle and init datalen and data
+ * filelen will be pre-set to 0 by SaveToMem
+ * curpos will be pre-set to 0 by SaveToMem and LoadFromMem
+ * IMPORTANT: data should be set to NULL and datalen to 0,
+ * unless the user wants to manually malloc a larger buffer
+ */
+FIBITMAP *FreeImage_LoadFromMem(FREE_IMAGE_FORMAT fif, fiio_mem_handle *handle, int flags);
+BOOL FreeImage_SaveToMem(FREE_IMAGE_FORMAT fif, FIBITMAP *dib, fiio_mem_handle *handle, int flags);
+
+void SetMemIO(FreeImageIO *io);
+unsigned __stdcall fiio_mem_ReadProc(void *buffer, unsigned size, unsigned count, fi_handle handle);
+unsigned __stdcall fiio_mem_WriteProc(void *buffer, unsigned size, unsigned count, fi_handle handle);
+int __stdcall fiio_mem_SeekProc(fi_handle handle, long offset, int origin);
+long __stdcall fiio_mem_TellProc(fi_handle handle);
+
+/*
+ * this interface directly exports most of FreeImage routines
+ * you can use them in your plugin after obtaining the interfasce using MS_IMG_GETINTERFACE
+ */
+
+typedef struct _tagFI_interface {
+
+ DWORD version;
+
+ FIBITMAP *(DLL_CALLCONV *FI_Allocate)(int width, int height, int bpp, unsigned red_mask FI_DEFAULT(0), unsigned green_mask FI_DEFAULT(0), unsigned blue_mask FI_DEFAULT(0));
+ FIBITMAP *(DLL_CALLCONV *FI__AllocateT)(FREE_IMAGE_TYPE type, int width, int height, int bpp FI_DEFAULT(8), unsigned red_mask FI_DEFAULT(0), unsigned green_mask FI_DEFAULT(0), unsigned blue_mask FI_DEFAULT(0));
+ FIBITMAP *(DLL_CALLCONV *FI_Clone)(FIBITMAP *dib);
+ void (DLL_CALLCONV *FI_Unload)(FIBITMAP *dib);
+
+ // Load / Save routines -----------------------------------------------------
+
+ FIBITMAP *(DLL_CALLCONV *FI_Load)(FREE_IMAGE_FORMAT fif, const char *filename, int flags FI_DEFAULT(0));
+ FIBITMAP *(DLL_CALLCONV *FI_LoadU)(FREE_IMAGE_FORMAT fif, const wchar_t *filename, int flags FI_DEFAULT(0));
+ FIBITMAP *(DLL_CALLCONV *FI_LoadFromHandle)(FREE_IMAGE_FORMAT fif, FreeImageIO *io, fi_handle handle, int flags FI_DEFAULT(0));
+ BOOL (DLL_CALLCONV *FI_Save)(FREE_IMAGE_FORMAT fif, FIBITMAP *dib, const char *filename, int flags FI_DEFAULT(0));
+ BOOL (DLL_CALLCONV *FI_SaveU)(FREE_IMAGE_FORMAT fif, FIBITMAP *dib, const wchar_t *filename, int flags FI_DEFAULT(0));
+ BOOL (DLL_CALLCONV *FI_SaveToHandle)(FREE_IMAGE_FORMAT fif, FIBITMAP *dib, FreeImageIO *io, fi_handle handle, int flags FI_DEFAULT(0));
+
+// Memory I/O stream routines -----------------------------------------------
+
+ FIMEMORY *(DLL_CALLCONV *FI_OpenMemory)(BYTE *data FI_DEFAULT(0), DWORD size_in_bytes FI_DEFAULT(0));
+ void (DLL_CALLCONV *FI_CloseMemory)(FIMEMORY *stream);
+ FIBITMAP *(DLL_CALLCONV *FI_LoadFromMemory)(FREE_IMAGE_FORMAT fif, FIMEMORY *stream, int flags FI_DEFAULT(0));
+ BOOL (DLL_CALLCONV *FI_SaveToMemory)(FREE_IMAGE_FORMAT fif, FIBITMAP *dib, FIMEMORY *stream, int flags FI_DEFAULT(0));
+ long (DLL_CALLCONV *FI_TellMemory)(FIMEMORY *stream);
+ BOOL (DLL_CALLCONV *FI_SeekMemory)(FIMEMORY *stream, long offset, int origin);
+ BOOL (DLL_CALLCONV *FI_AcquireMemory)(FIMEMORY *stream, BYTE **data, DWORD *size_in_bytes);
+ unsigned (DLL_CALLCONV *FI_ReadMemory)(void *buffer, unsigned size, unsigned count, FIMEMORY *stream);
+ unsigned (DLL_CALLCONV *FI_WriteMemory)(const void *buffer, unsigned size, unsigned count, FIMEMORY *stream);
+ FIMULTIBITMAP *(DLL_CALLCONV *FI_LoadMultiBitmapFromMemory)(FREE_IMAGE_FORMAT fif, FIMEMORY *stream, int flags FI_DEFAULT(0));
+
+// Plugin Interface ---------------------------------------------------------
+
+ FREE_IMAGE_FORMAT (DLL_CALLCONV *FI_RegisterLocalPlugin)(FI_InitProc proc_address, const char *format FI_DEFAULT(0), const char *description FI_DEFAULT(0), const char *extension FI_DEFAULT(0), const char *regexpr FI_DEFAULT(0));
+ FREE_IMAGE_FORMAT (DLL_CALLCONV *FI_RegisterExternalPlugin)(const char *path, const char *format FI_DEFAULT(0), const char *description FI_DEFAULT(0), const char *extension FI_DEFAULT(0), const char *regexpr FI_DEFAULT(0));
+ int (DLL_CALLCONV *FI_GetFIFCount)(void);
+ int (DLL_CALLCONV *FI_SetPluginEnabled)(FREE_IMAGE_FORMAT fif, BOOL enable);
+ int (DLL_CALLCONV *FI_IsPluginEnabled)(FREE_IMAGE_FORMAT fif);
+ FREE_IMAGE_FORMAT (DLL_CALLCONV *FI_GetFIFFromFormat)(const char *format);
+ FREE_IMAGE_FORMAT (DLL_CALLCONV *FI_GetFIFFromMime)(const char *mime);
+ const char *(DLL_CALLCONV *FI_GetFormatFromFIF)(FREE_IMAGE_FORMAT fif);
+ const char *(DLL_CALLCONV *FI_GetFIFExtensionList)(FREE_IMAGE_FORMAT fif);
+ const char *(DLL_CALLCONV *FI_GetFIFDescription)(FREE_IMAGE_FORMAT fif);
+ const char *(DLL_CALLCONV *FI_GetFIFRegExpr)(FREE_IMAGE_FORMAT fif);
+ const char *(DLL_CALLCONV *FI_GetFIFMimeType)(FREE_IMAGE_FORMAT fif);
+ FREE_IMAGE_FORMAT (DLL_CALLCONV *FI_GetFIFFromFilename)(const char *filename);
+ FREE_IMAGE_FORMAT (DLL_CALLCONV *FI_GetFIFFromFilenameU)(const wchar_t *filename);
+ BOOL (DLL_CALLCONV *FI_FIFSupportsReading)(FREE_IMAGE_FORMAT fif);
+ BOOL (DLL_CALLCONV *FI_FIFSupportsWriting)(FREE_IMAGE_FORMAT fif);
+ BOOL (DLL_CALLCONV *FI_FIFSupportsExportBPP)(FREE_IMAGE_FORMAT fif, int bpp);
+ BOOL (DLL_CALLCONV *FI_FIFSupportsExportType)(FREE_IMAGE_FORMAT fif, FREE_IMAGE_TYPE type);
+ BOOL (DLL_CALLCONV *FI_FIFSupportsICCProfiles)(FREE_IMAGE_FORMAT fif);
+
+// Multipaging interface ----------------------------------------------------
+
+ FIMULTIBITMAP *(DLL_CALLCONV *FI_OpenMultiBitmap)(FREE_IMAGE_FORMAT fif, const char *filename, BOOL create_new, BOOL read_only, BOOL keep_cache_in_memory FI_DEFAULT(FALSE), int flags FI_DEFAULT(0));
+ BOOL (DLL_CALLCONV *FI_CloseMultiBitmap)(FIMULTIBITMAP *bitmap, int flags FI_DEFAULT(0));
+ int (DLL_CALLCONV *FI_GetPageCount)(FIMULTIBITMAP *bitmap);
+ void (DLL_CALLCONV *FI_AppendPage)(FIMULTIBITMAP *bitmap, FIBITMAP *data);
+ void (DLL_CALLCONV *FI_InsertPage)(FIMULTIBITMAP *bitmap, int page, FIBITMAP *data);
+ void (DLL_CALLCONV *FI_DeletePage)(FIMULTIBITMAP *bitmap, int page);
+ FIBITMAP *(DLL_CALLCONV *FI_LockPage)(FIMULTIBITMAP *bitmap, int page);
+ void (DLL_CALLCONV *FI_UnlockPage)(FIMULTIBITMAP *bitmap, FIBITMAP *data, BOOL changed);
+ BOOL (DLL_CALLCONV *FI_MovePage)(FIMULTIBITMAP *bitmap, int target, int source);
+ BOOL (DLL_CALLCONV *FI_GetLockedPageNumbers)(FIMULTIBITMAP *bitmap, int *pages, int *count);
+
+// Filetype request routines ------------------------------------------------
+
+ FREE_IMAGE_FORMAT (DLL_CALLCONV *FI_GetFileType)(const char *filename, int size FI_DEFAULT(0));
+ FREE_IMAGE_FORMAT (DLL_CALLCONV *FI_GetFileTypeU)(const wchar_t *filename, int size FI_DEFAULT(0));
+ FREE_IMAGE_FORMAT (DLL_CALLCONV *FI_GetFileTypeFromHandle)(FreeImageIO *io, fi_handle handle, int size FI_DEFAULT(0));
+ FREE_IMAGE_FORMAT (DLL_CALLCONV *FI_GetFileTypeFromMemory)(FIMEMORY *stream, int size FI_DEFAULT(0));
+
+// FreeImage helper routines ------------------------- MISSING !!!! TODO
+
+ BOOL (DLL_CALLCONV *FI_IsLittleEndian)(void);
+ BOOL (DLL_CALLCONV *FI_LookupX11Color)(const char *szColor, BYTE *nRed, BYTE *nGreen, BYTE *nBlue);
+ BOOL (DLL_CALLCONV *FI_LookupSVGColor)(const char *szColor, BYTE *nRed, BYTE *nGreen, BYTE *nBlue);
+
+// Image type request routine -----------------------------------------------
+
+ FREE_IMAGE_TYPE (DLL_CALLCONV *FI_GetImageType)(FIBITMAP *dib);
+
+// Pixel access routines ----------------------------------------------------
+
+ BYTE *(DLL_CALLCONV *FI_GetBits)(FIBITMAP *dib);
+ BYTE *(DLL_CALLCONV *FI_GetScanLine)(FIBITMAP *dib, int scanline);
+
+ BOOL (DLL_CALLCONV *FI_GetPixelIndex)(FIBITMAP *dib, unsigned x, unsigned y, BYTE *value);
+ BOOL (DLL_CALLCONV *FI_GetPixelColor)(FIBITMAP *dib, unsigned x, unsigned y, RGBQUAD *value);
+ BOOL (DLL_CALLCONV *FI_SetPixelIndex)(FIBITMAP *dib, unsigned x, unsigned y, BYTE *value);
+ BOOL (DLL_CALLCONV *FI_SetPixelColor)(FIBITMAP *dib, unsigned x, unsigned y, RGBQUAD *value);
+
+// DIB info routines --------------------------------------------------------
+
+ unsigned (DLL_CALLCONV *FI_GetColorsUsed)(FIBITMAP *dib);
+ unsigned (DLL_CALLCONV *FI_GetBPP)(FIBITMAP *dib);
+ unsigned (DLL_CALLCONV *FI_GetWidth)(FIBITMAP *dib);
+ unsigned (DLL_CALLCONV *FI_GetHeight)(FIBITMAP *dib);
+ unsigned (DLL_CALLCONV *FI_GetLine)(FIBITMAP *dib);
+ unsigned (DLL_CALLCONV *FI_GetPitch)(FIBITMAP *dib);
+ unsigned (DLL_CALLCONV *FI_GetDIBSize)(FIBITMAP *dib);
+ RGBQUAD *(DLL_CALLCONV *FI_GetPalette)(FIBITMAP *dib);
+
+ unsigned (DLL_CALLCONV *FI_GetDotsPerMeterX)(FIBITMAP *dib);
+ unsigned (DLL_CALLCONV *FI_GetDotsPerMeterY)(FIBITMAP *dib);
+ void (DLL_CALLCONV *FI_SetDotsPerMeterX)(FIBITMAP *dib, unsigned res);
+ void (DLL_CALLCONV *FI_SetDotsPerMeterY)(FIBITMAP *dib, unsigned res);
+
+ BITMAPINFOHEADER *(DLL_CALLCONV *FI_GetInfoHeader)(FIBITMAP *dib);
+ BITMAPINFO *(DLL_CALLCONV *FI_GetInfo)(FIBITMAP *dib);
+ FREE_IMAGE_COLOR_TYPE (DLL_CALLCONV *FI_GetColorType)(FIBITMAP *dib);
+
+ unsigned (DLL_CALLCONV *FI_GetRedMask)(FIBITMAP *dib);
+ unsigned (DLL_CALLCONV *FI_GetGreenMask)(FIBITMAP *dib);
+ unsigned (DLL_CALLCONV *FI_GetBlueMask)(FIBITMAP *dib);
+
+ unsigned (DLL_CALLCONV *FI_GetTransparencyCount)(FIBITMAP *dib);
+ BYTE *(DLL_CALLCONV *FI_GetTransparencyTable)(FIBITMAP *dib);
+ void (DLL_CALLCONV *FI_SetTransparent)(FIBITMAP *dib, BOOL enabled);
+ void (DLL_CALLCONV *FI_SetTransparencyTable)(FIBITMAP *dib, BYTE *table, int count);
+ BOOL (DLL_CALLCONV *FI_IsTransparent)(FIBITMAP *dib);
+
+ BOOL (DLL_CALLCONV *FI_HasBackgroundColor)(FIBITMAP *dib);
+ BOOL (DLL_CALLCONV *FI_GetBackgroundColor)(FIBITMAP *dib, RGBQUAD *bkcolor);
+ BOOL (DLL_CALLCONV *FI_SetBackgroundColor)(FIBITMAP *dib, RGBQUAD *bkcolor);
+
+ // ICC profile routines ------------------------------- MISSING !!! TODO
+
+ FIICCPROFILE *(DLL_CALLCONV *FI_GetICCProfile)(FIBITMAP *dib);
+ FIICCPROFILE *(DLL_CALLCONV *FI_CreateICCProfile)(FIBITMAP *dib, void *data, long size);
+ void (DLL_CALLCONV *FI_DestroyICCProfile)(FIBITMAP *dib);
+
+ // Line conversion routines ----------------------------MISSING !!! TODO
+
+// Smart conversion routines ------------------------------------------------
+
+ FIBITMAP *(DLL_CALLCONV *FI_ConvertTo4Bits)(FIBITMAP *dib);
+ FIBITMAP *(DLL_CALLCONV *FI_ConvertTo8Bits)(FIBITMAP *dib);
+ FIBITMAP *(DLL_CALLCONV *FI_ConvertToGreyscale)(FIBITMAP *dib);
+ FIBITMAP *(DLL_CALLCONV *FI_ConvertTo16Bits555)(FIBITMAP *dib);
+ FIBITMAP *(DLL_CALLCONV *FI_ConvertTo16Bits565)(FIBITMAP *dib);
+ FIBITMAP *(DLL_CALLCONV *FI_ConvertTo24Bits)(FIBITMAP *dib);
+ FIBITMAP *(DLL_CALLCONV *FI_ConvertTo32Bits)(FIBITMAP *dib);
+ FIBITMAP *(DLL_CALLCONV *FI_ColorQuantize)(FIBITMAP *dib, FREE_IMAGE_QUANTIZE quantize);
+
+ FIBITMAP *(DLL_CALLCONV *FI_ColorQuantizeEx)(FIBITMAP *dib, FREE_IMAGE_QUANTIZE quantize /*FI_DEFAULT(FIQ_WUQUANT) */, int PaletteSize FI_DEFAULT(256), int ReserveSize FI_DEFAULT(0), RGBQUAD *ReservePalette FI_DEFAULT(NULL));
+
+ FIBITMAP *(DLL_CALLCONV *FI_Threshold)(FIBITMAP *dib, BYTE T);
+ FIBITMAP *(DLL_CALLCONV *FI_Dither)(FIBITMAP *dib, FREE_IMAGE_DITHER algorithm);
+ FIBITMAP *(DLL_CALLCONV *FI_ConvertFromRawBits)(BYTE *bits, int width, int height, int pitch, unsigned bpp, unsigned red_mask, unsigned green_mask, unsigned blue_mask, BOOL topdown FI_DEFAULT(FALSE));
+ void (DLL_CALLCONV *FI_ConvertToRawBits)(BYTE *bits, FIBITMAP *dib, int pitch, unsigned bpp, unsigned red_mask, unsigned green_mask, unsigned blue_mask, BOOL topdown FI_DEFAULT(FALSE));
+
+ FIBITMAP *(DLL_CALLCONV *FI_ConvertToRGBF)(FIBITMAP *dib);
+ FIBITMAP *(DLL_CALLCONV *FI_ConvertToStandardType)(FIBITMAP *src, BOOL scale_linear FI_DEFAULT(TRUE));
+ FIBITMAP *(DLL_CALLCONV *FI_ConvertToType)(FIBITMAP *src, FREE_IMAGE_TYPE dst_type, BOOL scale_linear FI_DEFAULT(TRUE));
+
+// tone mapping operators
+ FIBITMAP *(DLL_CALLCONV *FI_ToneMapping)(FIBITMAP *dib, FREE_IMAGE_TMO tmo, double first_param FI_DEFAULT(0), double second_param FI_DEFAULT(0));
+ FIBITMAP *(DLL_CALLCONV *FI_TmoDrago03)(FIBITMAP *src, double gamma FI_DEFAULT(2.2), double exposure FI_DEFAULT(0));
+ FIBITMAP *(DLL_CALLCONV *FI_TmoReinhard05)(FIBITMAP *src, double intensity FI_DEFAULT(0), double contrast FI_DEFAULT(0));
+
+// ZLib interface -----------------------------------------------------------
+
+ DWORD (DLL_CALLCONV *FI_ZLibCompress)(BYTE *target, DWORD target_size, BYTE *source, DWORD source_size);
+ DWORD (DLL_CALLCONV *FI_ZLibUncompress)(BYTE *target, DWORD target_size, BYTE *source, DWORD source_size);
+ DWORD (DLL_CALLCONV *FI_ZLibGZip)(BYTE *target, DWORD target_size, BYTE *source, DWORD source_size);
+ DWORD (DLL_CALLCONV *FI_ZLibGUnzip)(BYTE *target, DWORD target_size, BYTE *source, DWORD source_size);
+ DWORD (DLL_CALLCONV *FI_ZLibCRC32)(DWORD crc, BYTE *source, DWORD source_size);
+
+
+// --------------------------------------------------------------------------
+// Metadata routines --------------------------------------------------------
+// --------------------------------------------------------------------------
+
+// tag creation / destruction
+ FITAG *(DLL_CALLCONV *FI_CreateTag)();
+ void (DLL_CALLCONV *FI_DeleteTag)(FITAG *tag);
+ FITAG *(DLL_CALLCONV *FI_CloneTag)(FITAG *tag);
+
+// tag getters and setters
+ const char *(DLL_CALLCONV *FI_GetTagKey)(FITAG *tag);
+ const char *(DLL_CALLCONV *FI_GetTagDescription)(FITAG *tag);
+ WORD (DLL_CALLCONV *FI_GetTagID)(FITAG *tag);
+ FREE_IMAGE_MDTYPE (DLL_CALLCONV *FI_GetTagType)(FITAG *tag);
+ DWORD (DLL_CALLCONV *FI_GetTagCount)(FITAG *tag);
+ DWORD (DLL_CALLCONV *FI_GetTagLength)(FITAG *tag);
+ const void *(DLL_CALLCONV *FI_GetTagValue)(FITAG *tag);
+
+ BOOL (DLL_CALLCONV *FI_SetTagKey)(FITAG *tag, const char *key);
+ BOOL (DLL_CALLCONV *FI_SetTagDescription)(FITAG *tag, const char *description);
+ BOOL (DLL_CALLCONV *FI_SetTagID)(FITAG *tag, WORD id);
+ BOOL (DLL_CALLCONV *FI_SetTagType)(FITAG *tag, FREE_IMAGE_MDTYPE type);
+ BOOL (DLL_CALLCONV *FI_SetTagCount)(FITAG *tag, DWORD count);
+ BOOL (DLL_CALLCONV *FI_SetTagLength)(FITAG *tag, DWORD length);
+ BOOL (DLL_CALLCONV *FI_SetTagValue)(FITAG *tag, const void *value);
+
+// iterator
+ FIMETADATA *(DLL_CALLCONV *FI_FindFirstMetadata)(FREE_IMAGE_MDMODEL model, FIBITMAP *dib, FITAG **tag);
+ BOOL (DLL_CALLCONV *FI_FindNextMetadata)(FIMETADATA *mdhandle, FITAG **tag);
+ void (DLL_CALLCONV *FI_FindCloseMetadata)(FIMETADATA *mdhandle);
+
+// metadata setter and getter
+ BOOL (DLL_CALLCONV *FI_SetMetadata)(FREE_IMAGE_MDMODEL model, FIBITMAP *dib, const char *key, FITAG *tag);
+ BOOL (DLL_CALLCONV *FI_GetMetadata)(FREE_IMAGE_MDMODEL model, FIBITMAP *dib, const char *key, FITAG **tag);
+
+// helpers
+ unsigned (DLL_CALLCONV *FI_GetMetadataCount)(FREE_IMAGE_MDMODEL model, FIBITMAP *dib);
+
+// tag to C string conversion
+ const char *(DLL_CALLCONV *FI_TagToString)(FREE_IMAGE_MDMODEL model, FITAG *tag, char *Make FI_DEFAULT(NULL));
+
+
+// --------------------------------------------------------------------------
+// Image manipulation toolkit -----------------------------------------------
+// --------------------------------------------------------------------------
+
+// rotation and flipping
+ FIBITMAP *(DLL_CALLCONV *FI_RotateClassic)(FIBITMAP *dib, double angle);
+ FIBITMAP *(DLL_CALLCONV *FI_RotateEx)(FIBITMAP *dib, double angle, double x_shift, double y_shift, double x_origin, double y_origin, BOOL use_mask);
+ BOOL (DLL_CALLCONV *FI_FlipHorizontal)(FIBITMAP *dib);
+ BOOL (DLL_CALLCONV *FI_FlipVertical)(FIBITMAP *dib);
+ BOOL (DLL_CALLCONV *FI_JPEGTransform)(const char *src_file, const char *dst_file, FREE_IMAGE_JPEG_OPERATION operation, BOOL perfect FI_DEFAULT(FALSE));
+
+// upsampling / downsampling
+ FIBITMAP *(DLL_CALLCONV *FI_Rescale)(FIBITMAP *dib, int dst_width, int dst_height, FREE_IMAGE_FILTER filter);
+ FIBITMAP *(DLL_CALLCONV *FI_MakeThumbnail)(FIBITMAP *dib, int max_pixel_size, BOOL convert FI_DEFAULT(TRUE));
+
+// color manipulation routines (point operations)
+ BOOL (DLL_CALLCONV *FI_AdjustCurve)(FIBITMAP *dib, BYTE *LUT, FREE_IMAGE_COLOR_CHANNEL channel);
+ BOOL (DLL_CALLCONV *FI_AdjustGamma)(FIBITMAP *dib, double gamma);
+ BOOL (DLL_CALLCONV *FI_AdjustBrightness)(FIBITMAP *dib, double percentage);
+ BOOL (DLL_CALLCONV *FI_AdjustContrast)(FIBITMAP *dib, double percentage);
+ BOOL (DLL_CALLCONV *FI_Invert)(FIBITMAP *dib);
+ BOOL (DLL_CALLCONV *FI_GetHistogram)(FIBITMAP *dib, DWORD *histo, FREE_IMAGE_COLOR_CHANNEL channel);
+
+// channel processing routines
+ FIBITMAP *(DLL_CALLCONV *FI_GetChannel)(FIBITMAP *dib, FREE_IMAGE_COLOR_CHANNEL channel);
+ BOOL (DLL_CALLCONV *FI_SetChannel)(FIBITMAP *dib, FIBITMAP *dib8, FREE_IMAGE_COLOR_CHANNEL channel);
+ FIBITMAP *(DLL_CALLCONV *FI_GetComplexChannel)(FIBITMAP *src, FREE_IMAGE_COLOR_CHANNEL channel);
+ BOOL (DLL_CALLCONV *FI_SetComplexChannel)(FIBITMAP *dst, FIBITMAP *src, FREE_IMAGE_COLOR_CHANNEL channel);
+
+// copy / paste / composite routines
+ FIBITMAP *(DLL_CALLCONV *FI_Copy)(FIBITMAP *dib, int left, int top, int right, int bottom);
+ BOOL (DLL_CALLCONV *FI_Paste)(FIBITMAP *dst, FIBITMAP *src, int left, int top, int alpha);
+ FIBITMAP *(DLL_CALLCONV *FI_Composite)(FIBITMAP *fg, BOOL useFileBkg FI_DEFAULT(FALSE), RGBQUAD *appBkColor FI_DEFAULT(NULL), FIBITMAP *bg FI_DEFAULT(NULL));
+ BOOL (DLL_CALLCONV *FI_JPEGCrop)(const char *src_file, const char *dst_file, int left, int top, int right, int bottom);
+
+// own functions
+
+ // memory I/O
+ FIBITMAP *(*FI_LoadFromMem)(FREE_IMAGE_FORMAT fif, fiio_mem_handle *handle, int flags);
+ BOOL (*FI_SaveToMem)(FREE_IMAGE_FORMAT fif, FIBITMAP *dib, fiio_mem_handle *handle, int flags);
+
+ // helpers
+ FIBITMAP *(*FI_CreateDIBFromHBITMAP)(HBITMAP hBmp);
+ HBITMAP (*FI_CreateHBITMAPFromDIB)(FIBITMAP *dib);
+ BOOL (*FI_Premultiply)(HBITMAP hBmp); // premultiplies alpha channel for usage with AlphaBlend()
+ // original HBITMAP stays valid and must be 32bit RGBA
+ int (*FI_BmpFilterResizeBitmap)(WPARAM wParam,LPARAM lParam); // more generic resizer for avatar images
+ void (*FI_CorrectBitmap32Alpha)(HBITMAP hBitmap, BOOL force); // corrects broken images (when all alpha values are 0)
+
+ BYTE reserved[200]; // future usage
+} FI_INTERFACE;
+
+/*
+ * image services
+ *
+ * only basic functionality is wrapped around Miranda services, because otherwise we would get a huge
+ * load of services with complex parameter marshalling requirements.
+ */
+
+// get the interface version number
+// wParam = lParam = 0
+// returns FI_IF_VERSION
+
+#define MS_IMG_GETIFVERSION "IMG/GetVersion"
+
+// obtain the full FreeImage interface from the library. This interface provides full access to freeimage
+// internal functions, thus enabling devs to fully utilize the FreeImage API. Only popular functions will
+// be exported as miranda services.
+// wParam = (DWORD)version Number // the caller MUST provide the desired version number
+// lParam = **FI_INTERFACE
+// returns S_OK on success, any other value indicates a problem.
+// the interface is populated during the _DllMain() handler, so you can assume it is ready when Miranda
+// calls the Load() handler of your plugin and you can return 1 in your Load() to disable your plugin when
+// it depends on the freeimage interface and the freeimage plugin is missing
+//
+// example:
+//
+// FI_INTERFACE *fii = NULL;
+//
+// result = CallService(MS_IMG_GETINTERFACE, FI_IF_VERSION, (LPARAM)&fii);
+//
+// if(result != S_OK)
+// failed, in this case, fei will be NULL and your plugin will crash when using the interface,
+// so ALWAYS check it
+
+#define MS_IMG_GETINTERFACE "IMG/GetInterface"
+
+
+#define IMGL_RETURNDIB 1 // will NOT return a HBITMAP but a FIBITMAP * instead (useful, if you
+ // want to do further image manipulations before converting to a Win32 bitmap
+ // caller MUST then free the FIBITMAP * using fii->FI_Unload() or MS_IMG_UNLOAD (see below)
+
+#define IMGL_WCHAR 2 // filename is wchar_t
+
+#if defined(UNICODE) || defined(_UNICODE)
+ #define IMGL_TCHAR IMGL_WCHAR
+#else
+ #define IMGL_TCHAR 0
+#endif
+
+// load an image from disk
+// wParam = full path and filename to the image
+// lParam = IMGL_* flags
+// returns a valid HBITMAP or 0 if image cannot be loaded
+// if IMGL_RETURNDIB is set, it returns a pointer to a freeimage bitmap (FIBITMAP *)
+
+#define MS_IMG_LOAD "IMG/Load"
+
+/*
+ * control structure for loading images from memory buffers (e.g. network buffers, memory mapped files).
+ */
+
+typedef struct _tagIMGSRVC_MEMIO {
+ long iLen; // length of the buffer
+ void *pBuf; // the buffer itself (you are responsible for allocating and free'ing it)
+ FREE_IMAGE_FORMAT fif; // the FIF_* image format constant. Make sure to provide the right one.
+ UINT flags; // flags to pass to FreeImage_LoadFromMem() (see freeimage docs)
+} IMGSRVC_MEMIO;
+
+// load an image from a memory buffer
+// wParam = IMGSRVC_MEMIO *
+// lParam = flags (see IMG/Load), valid are IMGL_RETURNDIB
+// you must popupate iLen (buffer length) and pBuf (pointer to memory buffer)
+// you must also specify the format in IMGSRVC_MEMIO.fif using one of the FIF_* constants defined in m_freeimage.h
+
+#define MS_IMG_LOADFROMMEM "IMG/LoadFromMem"
+
+// flags for IMGSRVC_INFO.dwMask
+
+#define IMGI_FBITMAP 1 // the dib member is valid
+#define IMGI_HBITMAP 2 // the hbm member is valid
+
+/*
+ * generic structure for various img functions
+ * you must populate the fields as required, set the mask bits to indicate which member is valid
+ */
+
+typedef struct _tagIMGSRVC_INFO {
+ DWORD cbSize;
+ union {
+ char *szName;
+ wchar_t *wszName;
+ };
+ HBITMAP hbm;
+ FIBITMAP *dib;
+ DWORD dwMask;
+ FREE_IMAGE_FORMAT fif;
+} IMGSRVC_INFO;
+
+// save image to disk
+// wParam = IMGSRVC_INFO * (szName/wszName, hbm OR dib, cbSize, dwMask mandatory. fif optional, if FIF_UNKNOWN is given
+// it will be determined from the filename).
+// lParam = IMG_* flags (IMGL_WCHAR is the only valid - filename will be assumed to be wchar_t and wszName must be used)
+// set IMGSRVC_INFO.dwMask to indicate whether the HBITMAP of FIBITMAP member is valid
+
+#define MS_IMG_SAVE "IMG/Save"
+
+// unload a FIFBITMAP
+// wParam = FIFBITMAP *
+// lParam = 0;
+// this service is useful when you have loaded a bitmap with IMGL_RETURNDIB in which case you do not get
+// a HBITMAP but instead a FBITMAP * which describes the freeimage-internal representation of a bitmap.
+
+#define MS_IMG_UNLOAD "IMG/Unload"
+
+/*
+ * resizer from loadavatars moved to image service plugin
+*/
+
+#define RESIZEBITMAP_STRETCH 0 // Distort bitmap to size in (max_width, max_height)
+#define RESIZEBITMAP_KEEP_PROPORTIONS 1 // Keep bitmap proportions (probabily only one of the
+ // max_width/max_height will be respected, and the other will be
+ // smaller)
+#define RESIZEBITMAP_CROP 2 // Keep bitmap proportions but crop it to fix exactly in (max_width, max_height)
+ // Some image info outside will be lost
+#define RESIZEBITMAP_MAKE_SQUARE 3 // Image will be allways square. Image will be croped and the size
+ // returned will be min(max_width, max_height)
+
+#define RESIZEBITMAP_FLAG_DONT_GROW 0x1000 // If set, the image will not grow. Else, it will grow to fit the max width/height
+
+typedef struct {
+ size_t size; // sizeof(ResizeBitmap);
+
+ HBITMAP hBmp;
+
+ int max_width;
+ int max_height;
+
+ int fit; // One of: RESIZEBITMAP_*
+} ResizeBitmap;
+
+// Returns a copy of the bitmap with the size especified or the original bitmap if nothing has to be changed
+// wParam = ResizeBitmap *
+// lParam = NULL
+// after return, compare the returned HBITMAP with the original. You are responsible for calling DestroyObject()
+// on the original HBITMAP
+
+#define MS_IMG_RESIZE "IMG/ResizeBitmap"
+
+
+/*
+ * format conversion helpers
+ *
+ * these helper macros allow converting HBITMAP to FIBITMAP * format and vice vera. In any case,
+ * the caller is responsible for freeing or deleting the original object.
+ * These macros wrap around the FI_CreateHBITMAPFromDib() and FI_CreateDIBFromHBITMAP() interface
+ * functions.
+ */
+
+//#define FI_HBM2DIB(x) (FI_CreateDIBFromHBITMAP((x)))
+//#define FI_DIB2HBM(x) (FI_CreateHBITMAPFromDIB((x)))
+
+#endif // __M_IMGSRVC_H
diff --git a/Plugins/avh_import/sdk/m_metacontacts.h b/Plugins/avh_import/sdk/m_metacontacts.h
new file mode 100644
index 0000000..1da12b9
--- /dev/null
+++ b/Plugins/avh_import/sdk/m_metacontacts.h
@@ -0,0 +1,162 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright © 2004 Universite Louis PASTEUR, STRASBOURG.
+Copyright © 2004 Scott Ellis (www.scottellis.com.au mail@scottellis.com.au)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef M_METACONTACTS_H__
+#define M_METACONTACTS_H__ 1
+
+//get the handle for a contact's parent metacontact
+//wParam=(HANDLE)hSubContact
+//lParam=0
+//returns a handle to the parent metacontact, or null if this contact is not a subcontact
+#define MS_MC_GETMETACONTACT "MetaContacts/GetMeta"
+
+//gets the handle for the default contact
+//wParam=(HANDLE)hMetaContact
+//lParam=0
+//returns a handle to the default contact, or null on failure
+#define MS_MC_GETDEFAULTCONTACT "MetaContacts/GetDefault"
+
+//gets the contact number for the default contact
+//wParam=(HANDLE)hMetaContact
+//lParam=0
+//returns a DWORD contact number, or -1 on failure
+#define MS_MC_GETDEFAULTCONTACTNUM "MetaContacts/GetDefaultNum"
+
+//gets the handle for the 'most online' contact
+//wParam=(HANDLE)hMetaContact
+//lParam=0
+//returns a handle to the 'most online' contact
+#define MS_MC_GETMOSTONLINECONTACT "MetaContacts/GetMostOnline"
+
+//gets the number of subcontacts for a metacontact
+//wParam=(HANDLE)hMetaContact
+//lParam=0
+//returns a DWORD representing the number of subcontacts for the given metacontact
+#define MS_MC_GETNUMCONTACTS "MetaContacts/GetNumContacts"
+
+//gets the handle of a subcontact, using the subcontact's number
+//wParam=(HANDLE)hMetaContact
+//lParam=(DWORD)contact number
+//returns a handle to the specified subcontact
+#define MS_MC_GETSUBCONTACT "MetaContacts/GetSubContact"
+
+//sets the default contact, using the subcontact's contact number
+//wParam=(HANDLE)hMetaContact
+//lParam=(DWORD)contact number
+//returns 0 on success
+#define MS_MC_SETDEFAULTCONTACTNUM "MetaContacts/SetDefault"
+
+//sets the default contact, using the subcontact's handle
+//wParam=(HANDLE)hMetaContact
+//lParam=(HANDLE)hSubcontact
+//returns 0 on success
+#define MS_MC_SETDEFAULTCONTACT "MetaContacts/SetDefaultByHandle"
+
+//forces the metacontact to send using a specific subcontact, using the subcontact's contact number
+//wParam=(HANDLE)hMetaContact
+//lParam=(DWORD)contact number
+//returns 0 on success
+#define MS_MC_FORCESENDCONTACTNUM "MetaContacts/ForceSendContact"
+
+//forces the metacontact to send using a specific subcontact, using the subcontact's handle
+//wParam=(HANDLE)hMetaContact
+//lParam=(HANDLE)hSubcontact
+//returns 0 on success (will fail if 'force default' is in effect)
+#define MS_MC_FORCESENDCONTACT "MetaContacts/ForceSendContactByHandle"
+
+//'unforces' the metacontact to send using a specific subcontact
+//wParam=(HANDLE)hMetaContact
+//lParam=0
+//returns 0 on success (will fail if 'force default' is in effect)
+#define MS_MC_UNFORCESENDCONTACT "MetaContacts/UnforceSendContact"
+
+//'forces' or 'unforces' (i.e. toggles) the metacontact to send using it's default contact
+// overrides (and clears) 'force send' above, and will even force use of offline contacts
+// will send ME_MC_FORCESEND or ME_MC_UNFORCESEND event
+//wParam=(HANDLE)hMetaContact
+//lParam=0
+//returns 1(true) or 0(false) representing new state of 'force default'
+#define MS_MC_FORCEDEFAULT "MetaContacts/ForceSendDefault"
+
+// method to get state of 'force' for a metacontact
+// wParam=(HANDLE)hMetaContact
+// lParam= (DWORD)&contact_number or NULL
+//
+// if lparam supplied, the contact_number of the contatct 'in force' will be copied to the address it points to,
+// or if none is in force, the value (DWORD)-1 will be copied
+// (v0.8.0.8+ returns 1 if 'force default' is true with *lParam == default contact number, else returns 0 with *lParam as above)
+#define MS_MC_GETFORCESTATE "MetaContacts/GetForceState"
+
+// fired when a metacontact's default contact changes (fired upon creation of metacontact also, when default is initially set)
+// wParam=(HANDLE)hMetaContact
+// lParam=(HANDLE)hDefaultContact
+#define ME_MC_DEFAULTTCHANGED "MetaContacts/DefaultChanged"
+
+// fired when a metacontact's subcontacts change (fired upon creation of metacontact, when contacts are added or removed, and when
+// contacts are reordered) - a signal to re-read metacontact data
+// wParam=(HANDLE)hMetaContact
+// lParam=0
+#define ME_MC_SUBCONTACTSCHANGED "MetaContacts/SubcontactsChanged"
+
+// fired when a metacontact is forced to send using a specific subcontact
+// wParam=(HANDLE)hMetaContact
+// lParam=(HANDLE)hForceContact
+#define ME_MC_FORCESEND "MetaContacts/ForceSend"
+
+// fired when a metacontact is 'unforced' to send using a specific subcontact
+// wParam=(HANDLE)hMetaContact
+// lParam=0
+#define ME_MC_UNFORCESEND "MetaContacts/UnforceSend"
+
+// method to get protocol name - used to be sure you're dealing with a "real" metacontacts plugin :)
+// wParam=lParam=0
+#define MS_MC_GETPROTOCOLNAME "MetaContacts/GetProtoName"
+
+
+// added 0.9.5.0 (22/3/05)
+// wParam=(HANDLE)hContact
+// lParam=0
+// convert a given contact into a metacontact
+#define MS_MC_CONVERTTOMETA "MetaContacts/ConvertToMetacontact"
+
+// added 0.9.5.0 (22/3/05)
+// wParam=(HANDLE)hContact
+// lParam=(HANDLE)hMeta
+// add an existing contact to a metacontact
+#define MS_MC_ADDTOMETA "MetaContacts/AddToMetacontact"
+
+// added 0.9.5.0 (22/3/05)
+// wParam=0
+// lParam=(HANDLE)hContact
+// remove a contact from a metacontact
+#define MS_MC_REMOVEFROMMETA "MetaContacts/RemoveFromMetacontact"
+
+
+// added 0.9.13.2 (6/10/05)
+// wParam=(BOOL)disable
+// lParam=0
+// enable/disable the 'hidden group hack' - for clists that support subcontact hiding using 'IsSubcontact' setting
+// should be called once in the clist 'onmodulesloaded' event handler (which, since it's loaded after the db, will be called
+// before the metacontact onmodulesloaded handler where the subcontact hiding is usually done)
+#define MS_MC_DISABLEHIDDENGROUP "MetaContacts/DisableHiddenGroup"
+
+#endif
diff --git a/Plugins/bclist/Docs/bclist.png b/Plugins/bclist/Docs/bclist.png
new file mode 100644
index 0000000..a03e870
--- /dev/null
+++ b/Plugins/bclist/Docs/bclist.png
Binary files differ
diff --git a/Plugins/bclist/Docs/bclist_changelog.txt b/Plugins/bclist/Docs/bclist_changelog.txt
new file mode 100644
index 0000000..181976e
--- /dev/null
+++ b/Plugins/bclist/Docs/bclist_changelog.txt
@@ -0,0 +1,30 @@
+BClist
+
+Changelog:
+
+. 0.8.0.1
+ * Sync with latest clist version from SVN
+ + Use account names
+ - Needs Miranda 0.8.0.9
+
+. 0.7.1.0
+ * Sync with latest clist version from SVN
+ * Fix for CTRL-1
+ * Changed version number to match clist
+
+. 0.0.0.5
+ + Added support for miranda 0.8
+
+. 0.0.0.4
+ * Fix for when showing more than one clist at the same time
+
+. 0.0.0.3
+ * Fix for losing selection and focus on rebuild
+
+. 0.0.0.2
+ + Added templates for row types
+ + Added updater support
+ * Fixed rebuild on item data change
+
+. 0.0.0.1
+ + Initial version \ No newline at end of file
diff --git a/Plugins/bclist/Docs/bclist_readme.txt b/Plugins/bclist/Docs/bclist_readme.txt
new file mode 100644
index 0000000..401ca0c
--- /dev/null
+++ b/Plugins/bclist/Docs/bclist_readme.txt
@@ -0,0 +1,10 @@
+BClist plugin
+--------------------
+
+This is a contact list for blind folks. It uses a list control to show all contacts, so screen readers can "read" the clist to the user.
+
+Based on classic contact list.
+
+This plugin needs Miranda 0.8.0.9
+
+To report bugs/make suggestions, go to the forum thread: http://forums.miranda-im.org/showthread.php?t=11778 \ No newline at end of file
diff --git a/Plugins/bclist/Docs/bclist_version.txt b/Plugins/bclist/Docs/bclist_version.txt
new file mode 100644
index 0000000..0f2c1f9
--- /dev/null
+++ b/Plugins/bclist/Docs/bclist_version.txt
@@ -0,0 +1 @@
+BClist 0.8.0.1 \ No newline at end of file
diff --git a/Plugins/bclist/ZIP/doit.bat b/Plugins/bclist/ZIP/doit.bat
new file mode 100644
index 0000000..a13f289
--- /dev/null
+++ b/Plugins/bclist/ZIP/doit.bat
@@ -0,0 +1,84 @@
+@echo off
+
+rem Batch file to build and upload files
+rem
+rem TODO: Integration with FL
+
+set name=bclist
+
+rem To upload, this var must be set here or in other batch
+rem set ftp=ftp://<user>:<password>@<ftp>/<path>
+
+echo Building %name% ...
+
+msdev ..\%name%.dsp /MAKE "%name% - Win32 Release" /REBUILD
+msdev ..\%name%.dsp /MAKE "%name% - Win32 Release Unicode" /REBUILD
+
+echo Generating files for %name% ...
+
+del *.zip
+del *.dll
+copy ..\..\..\bin\release\Plugins\clist_blind.dll
+copy ..\Docs\%name%_changelog.txt
+copy ..\Docs\%name%_version.txt
+copy ..\Docs\%name%_readme.txt
+mkdir Docs
+cd Docs
+del /Q *.*
+copy ..\..\Docs\%name%_readme.txt
+rem copy ..\..\Docs\langpack_%name%.txt
+rem copy ..\..\Docs\helppack_%name%.txt
+rem copy ..\..\m_%name%.h
+cd ..
+mkdir src
+cd src
+del /Q *.*
+copy ..\..\*.h
+copy ..\..\*.c*
+copy ..\..\*.
+copy ..\..\*.rc
+copy ..\..\*.dsp
+copy ..\..\*.dsw
+mkdir Docs
+cd Docs
+del /Q *.*
+copy ..\..\..\Docs\*.*
+cd ..
+cd ..
+
+"C:\Program Files\Filzip\Filzip.exe" -a -rp %name%.zip clist_blind.dll Docs
+
+del *.dll
+copy "..\..\..\bin\release unicode\Plugins\clist_blind.dll"
+"C:\Program Files\Filzip\Filzip.exe" -a -rp %name%W.zip clist_blind.dll Docs
+
+"C:\Program Files\Filzip\Filzip.exe" -a -rp %name%_src.zip src\*.*
+
+del *.dll
+cd Docs
+del /Q *.*
+cd ..
+rmdir Docs
+cd src
+del /Q *.*
+cd Docs
+del /Q *.*
+cd ..
+rmdir Docs
+cd ..
+rmdir src
+
+if "%ftp%"=="" GOTO END
+
+echo Going to upload files...
+pause
+
+"C:\Program Files\FileZilla\FileZilla.exe" -u .\%name%.zip %ftp% -overwrite -close
+"C:\Program Files\FileZilla\FileZilla.exe" -u .\%name%W.zip %ftp% -overwrite -close
+"C:\Program Files\FileZilla\FileZilla.exe" -u .\%name%_changelog.txt %ftp% -overwrite -close
+"C:\Program Files\FileZilla\FileZilla.exe" -u .\%name%_version.txt %ftp% -overwrite -close
+"C:\Program Files\FileZilla\FileZilla.exe" -u .\%name%_readme.txt %ftp% -overwrite -close
+
+:END
+
+echo Done.
diff --git a/Plugins/bclist/bclist.dsp b/Plugins/bclist/bclist.dsp
new file mode 100644
index 0000000..6fe302a
--- /dev/null
+++ b/Plugins/bclist/bclist.dsp
@@ -0,0 +1,531 @@
+# Microsoft Developer Studio Project File - Name="bclist" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=bclist - Win32 Debug Unicode
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "bclist.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "bclist.mak" CFG="bclist - Win32 Debug Unicode"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "bclist - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "bclist - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "bclist - Win32 Release Unicode" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "bclist - Win32 Debug Unicode" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""$/Miranda/miranda/plugins/clist", SKIAAAAA"
+# PROP Scc_LocalPath "."
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "bclist - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Ignore_Export_Lib 1
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "CLIST_EXPORTS" /YX /FD /c
+# ADD CPP /nologo /MD /W3 /Zi /O1 /I "../../include/" /I "../../include" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "CLIST_EXPORTS" /Yu"commonheaders.h" /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x809 /d "NDEBUG"
+# ADD RSC /l 0x809 /i "../../include/" /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib comctl32.lib shell32.lib ole32.lib comdlg32.lib /nologo /base:"0x6590000" /dll /map /debug /machine:I386 /out:"../../bin/release/plugins/clist_blind.dll"
+# SUBTRACT LINK32 /pdb:none /incremental:yes
+
+!ELSEIF "$(CFG)" == "bclist - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "CLIST_EXPORTS" /YX /FD /GZ /c
+# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "../../include" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "CLIST_EXPORTS" /YX /FD /GZ /c
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x809 /d "_DEBUG"
+# ADD RSC /l 0x809 /i "../../include/" /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib comctl32.lib shell32.lib ole32.lib comdlg32.lib /nologo /base:"0x6590000" /dll /debug /machine:I386 /out:"../../bin/debug/plugins/clist_blind.dll" /pdbtype:sept
+# SUBTRACT LINK32 /pdb:none /incremental:no
+
+!ELSEIF "$(CFG)" == "bclist - Win32 Release Unicode"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "clist___Win32_Release_Unicode"
+# PROP BASE Intermediate_Dir "clist___Win32_Release_Unicode"
+# PROP BASE Ignore_Export_Lib 0
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir ".\Release_Unicode"
+# PROP Intermediate_Dir ".\Release_Unicode"
+# PROP Ignore_Export_Lib 1
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MD /W3 /O1 /I "../../include/" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "CLIST_EXPORTS" /Yu"commonheaders.h" /FD /c
+# ADD CPP /nologo /MD /W3 /Zi /O1 /I "../../include/" /I "../../include" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "CLIST_EXPORTS" /D "UNICODE" /Yu"commonheaders.h" /FD /c
+# SUBTRACT CPP /Fr
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x809 /i "../../include/" /d "NDEBUG"
+# ADD RSC /l 0x809 /i "../../include/" /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib comctl32.lib shell32.lib ole32.lib comdlg32.lib /nologo /base:"0x6590000" /dll /map /machine:I386 /out:"../../bin/release/plugins/clist_classic.dll"
+# SUBTRACT BASE LINK32 /debug
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib comctl32.lib shell32.lib ole32.lib comdlg32.lib /nologo /base:"0x6590000" /dll /map /debug /machine:I386 /out:"../../bin/Release Unicode/plugins/clist_blind.dll"
+# SUBTRACT LINK32 /pdb:none /incremental:yes
+
+!ELSEIF "$(CFG)" == "bclist - Win32 Debug Unicode"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "clist___Win32_Debug_Unicode"
+# PROP BASE Intermediate_Dir "clist___Win32_Debug_Unicode"
+# PROP BASE Ignore_Export_Lib 0
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir ".\Debug_Unicode"
+# PROP Intermediate_Dir ".\Debug_Unicode"
+# PROP Ignore_Export_Lib 1
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "../../include" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "CLIST_EXPORTS" /YX /FD /GZ /c
+# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "../../include" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "CLIST_EXPORTS" /D "UNICODE" /FR /YX /FD /GZ /c
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x809 /i "../../include/" /d "_DEBUG"
+# ADD RSC /l 0x809 /i "../../include/" /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib comctl32.lib shell32.lib ole32.lib comdlg32.lib /nologo /base:"0x6590000" /dll /debug /machine:I386 /out:"../../bin/debug/plugins/clist_classic.dll" /pdbtype:sept
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib comctl32.lib shell32.lib ole32.lib comdlg32.lib /nologo /base:"0x6590000" /dll /debug /machine:I386 /out:"../../bin/Debug Unicode/plugins/clist_blind.dll" /pdbtype:sept
+# SUBTRACT LINK32 /pdb:none /incremental:no
+
+!ENDIF
+
+# Begin Target
+
+# Name "bclist - Win32 Release"
+# Name "bclist - Win32 Debug"
+# Name "bclist - Win32 Release Unicode"
+# Name "bclist - Win32 Debug Unicode"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=.\clcopts.c
+
+!IF "$(CFG)" == "bclist - Win32 Release"
+
+# ADD CPP /Yu"commonheaders.h"
+
+!ELSEIF "$(CFG)" == "bclist - Win32 Debug"
+
+!ELSEIF "$(CFG)" == "bclist - Win32 Release Unicode"
+
+# ADD BASE CPP /Yu"commonheaders.h"
+# ADD CPP /Yu"commonheaders.h"
+
+!ELSEIF "$(CFG)" == "bclist - Win32 Debug Unicode"
+
+!ENDIF
+
+# End Source File
+# Begin Source File
+
+SOURCE=.\clcpaint.c
+
+!IF "$(CFG)" == "bclist - Win32 Release"
+
+# ADD CPP /Yu"commonheaders.h"
+
+!ELSEIF "$(CFG)" == "bclist - Win32 Debug"
+
+!ELSEIF "$(CFG)" == "bclist - Win32 Release Unicode"
+
+# ADD BASE CPP /Yu"commonheaders.h"
+# ADD CPP /Yu"commonheaders.h"
+
+!ELSEIF "$(CFG)" == "bclist - Win32 Debug Unicode"
+
+!ENDIF
+
+# End Source File
+# Begin Source File
+
+SOURCE=.\clistmenus.c
+
+!IF "$(CFG)" == "bclist - Win32 Release"
+
+# ADD CPP /Yu"commonheaders.h"
+
+!ELSEIF "$(CFG)" == "bclist - Win32 Debug"
+
+!ELSEIF "$(CFG)" == "bclist - Win32 Release Unicode"
+
+# ADD BASE CPP /Yu"commonheaders.h"
+# ADD CPP /Yu"commonheaders.h"
+
+!ELSEIF "$(CFG)" == "bclist - Win32 Debug Unicode"
+
+!ENDIF
+
+# End Source File
+# Begin Source File
+
+SOURCE=.\clistopts.c
+
+!IF "$(CFG)" == "bclist - Win32 Release"
+
+# ADD CPP /Yu"commonheaders.h"
+
+!ELSEIF "$(CFG)" == "bclist - Win32 Debug"
+
+!ELSEIF "$(CFG)" == "bclist - Win32 Release Unicode"
+
+# ADD BASE CPP /Yu"commonheaders.h"
+# ADD CPP /Yu"commonheaders.h"
+
+!ELSEIF "$(CFG)" == "bclist - Win32 Debug Unicode"
+
+!ENDIF
+
+# End Source File
+# Begin Source File
+
+SOURCE=.\cluiopts.c
+
+!IF "$(CFG)" == "bclist - Win32 Release"
+
+# ADD CPP /Yu"commonheaders.h"
+
+!ELSEIF "$(CFG)" == "bclist - Win32 Debug"
+
+!ELSEIF "$(CFG)" == "bclist - Win32 Release Unicode"
+
+# ADD BASE CPP /Yu"commonheaders.h"
+# ADD CPP /Yu"commonheaders.h"
+
+!ELSEIF "$(CFG)" == "bclist - Win32 Debug Unicode"
+
+!ENDIF
+
+# End Source File
+# Begin Source File
+
+SOURCE=.\commonheaders.c
+
+!IF "$(CFG)" == "bclist - Win32 Release"
+
+# ADD CPP /Yc"commonheaders.h"
+
+!ELSEIF "$(CFG)" == "bclist - Win32 Debug"
+
+!ELSEIF "$(CFG)" == "bclist - Win32 Release Unicode"
+
+# ADD BASE CPP /Yc"commonheaders.h"
+# ADD CPP /Yc"commonheaders.h"
+
+!ELSEIF "$(CFG)" == "bclist - Win32 Debug Unicode"
+
+!ENDIF
+
+# End Source File
+# Begin Source File
+
+SOURCE=.\forkthread.c
+
+!IF "$(CFG)" == "bclist - Win32 Release"
+
+# ADD CPP /Yu"commonheaders.h"
+
+!ELSEIF "$(CFG)" == "bclist - Win32 Debug"
+
+!ELSEIF "$(CFG)" == "bclist - Win32 Release Unicode"
+
+# ADD BASE CPP /Yu"commonheaders.h"
+# ADD CPP /Yu"commonheaders.h"
+
+!ELSEIF "$(CFG)" == "bclist - Win32 Debug Unicode"
+
+!ENDIF
+
+# End Source File
+# Begin Source File
+
+SOURCE=.\init.c
+
+!IF "$(CFG)" == "bclist - Win32 Release"
+
+# ADD CPP /Yu"commonheaders.h"
+
+!ELSEIF "$(CFG)" == "bclist - Win32 Debug"
+
+!ELSEIF "$(CFG)" == "bclist - Win32 Release Unicode"
+
+# ADD BASE CPP /Yu"commonheaders.h"
+# ADD CPP /Yu"commonheaders.h"
+
+!ELSEIF "$(CFG)" == "bclist - Win32 Debug Unicode"
+
+!ENDIF
+
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=.\clc.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\commonheaders.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\forkthread.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\resource.h
+# End Source File
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
+# Begin Source File
+
+SOURCE=.\res\addcontact.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\res\away.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\res\blank.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\res\changefont.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\res\delete.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\res\detailsl.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\res\dnd.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\res\downarrow.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\res\dragcopy.cur
+# End Source File
+# Begin Source File
+
+SOURCE=.\res\dropuser.cur
+# End Source File
+# Begin Source File
+
+SOURCE=.\res\emptyblo.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\res\file.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\res\filledbl.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\res\finduser.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\res\freechat.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\res\groupope.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\res\groupshu.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\res\help.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\res\history.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\res\hyperlin.cur
+# End Source File
+# Begin Source File
+
+SOURCE=.\res\invisible.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\res\message.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\res\miranda.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\res\mirandaw.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\res\multisend.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\res\na2.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\res\notick.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\res\notick1.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\res\occupied.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\res\offline2.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\res\online2.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\res\onthepho.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\res\options.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\res\outtolun.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\res\rename.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\res\reply.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\resource.rc
+# End Source File
+# Begin Source File
+
+SOURCE=.\res\searchal.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\res\sendmail.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\res\smalldot.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\res\sms.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\res\sortcold.bmp
+# End Source File
+# Begin Source File
+
+SOURCE=.\res\sortcolu.bmp
+# End Source File
+# Begin Source File
+
+SOURCE=.\res\timestamp.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\res\url.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\res\useronli.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\res\viewdetails.ico
+# End Source File
+# End Group
+# End Target
+# End Project
diff --git a/Plugins/bclist/bclist.dsw b/Plugins/bclist/bclist.dsw
new file mode 100644
index 0000000..e6820e9
--- /dev/null
+++ b/Plugins/bclist/bclist.dsw
@@ -0,0 +1,29 @@
+Microsoft Developer Studio Workspace File, Format Version 6.00
+# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
+
+###############################################################################
+
+Project: "bclist"=.\bclist.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Global:
+
+Package=<5>
+{{{
+}}}
+
+Package=<3>
+{{{
+}}}
+
+###############################################################################
+
diff --git a/Plugins/bclist/clc.h b/Plugins/bclist/clc.h
new file mode 100644
index 0000000..7b825dd
--- /dev/null
+++ b/Plugins/bclist/clc.h
@@ -0,0 +1,83 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2003 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#define FONTID_LAST 7
+
+struct ClcContact {
+ BYTE type;
+ BYTE flags;
+ union {
+ struct {
+ WORD iImage;
+ HANDLE hContact;
+ };
+ struct {
+ WORD groupId;
+ struct ClcGroup *group;
+ };
+ };
+ BYTE iExtraImage[MAXEXTRACOLUMNS];
+ TCHAR szText[120-MAXEXTRACOLUMNS];
+ char * proto; // MS_PROTO_GETBASEPROTO
+};
+
+struct ClcData {
+ struct ClcGroup list;
+ int rowHeight;
+ int yScroll;
+ int selection;
+ struct ClcFontInfo fontInfo[FONTID_MAX+1];
+ int scrollTime;
+ HIMAGELIST himlHighlight;
+ int groupIndent;
+ TCHAR szQuickSearch[128];
+ int iconXSpace;
+ HWND hwndRenameEdit;
+ COLORREF bkColour,selBkColour,selTextColour,hotTextColour,quickSearchColour;
+ int iDragItem,iInsertionMark;
+ int dragStage;
+ POINT ptDragStart;
+ int dragAutoScrolling;
+ int dragAutoScrollHeight;
+ int leftMargin;
+ int insertionMarkHitHeight;
+ HBITMAP hBmpBackground;
+ int backgroundBmpUse,bkChanged;
+ int iHotTrack;
+ int gammaCorrection;
+ DWORD greyoutFlags; //see m_clc.h
+ DWORD offlineModes;
+ DWORD exStyle;
+ POINT ptInfoTip;
+ int infoTipTimeout;
+ HANDLE hInfoTipItem;
+ HIMAGELIST himlExtraColumns;
+ int extraColumnsCount;
+ int extraColumnSpacing;
+ int checkboxSize;
+ int showSelAlways;
+ int showIdle;
+ int noVScrollbar;
+ int useWindowsColours;
+ int needsResort;
+};
diff --git a/Plugins/bclist/clcopts.c b/Plugins/bclist/clcopts.c
new file mode 100644
index 0000000..bcb383c
--- /dev/null
+++ b/Plugins/bclist/clcopts.c
@@ -0,0 +1,277 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2003 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "commonheaders.h"
+
+TCHAR* MyDBGetContactSettingTString(HANDLE hContact, char* module, char* setting, TCHAR* out, size_t len, TCHAR *def);
+
+
+#define DBFONTF_BOLD 1
+#define DBFONTF_ITALIC 2
+#define DBFONTF_UNDERLINE 4
+
+struct CheckBoxToStyleEx_t
+{
+ int id;
+ DWORD flag;
+ int not;
+}
+
+static const checkBoxToStyleEx[] = {
+// {IDC_DISABLEDRAGDROP, CLS_EX_DISABLEDRAGDROP, 0},
+// {IDC_NOTEDITLABELS, CLS_EX_EDITLABELS, 1},
+// {IDC_SHOWSELALWAYS, CLS_EX_SHOWSELALWAYS, 0},
+ {IDC_TRACKSELECT, CLS_EX_TRACKSELECT, 0},
+ {IDC_SHOWGROUPCOUNTS, CLS_EX_SHOWGROUPCOUNTS, 0},
+ {IDC_HIDECOUNTSWHENEMPTY, CLS_EX_HIDECOUNTSWHENEMPTY, 0},
+// {IDC_DIVIDERONOFF, CLS_EX_DIVIDERONOFF, 0},
+// {IDC_NOTNOTRANSLUCENTSEL, CLS_EX_NOTRANSLUCENTSEL, 1},
+ {IDC_LINEWITHGROUPS, CLS_EX_LINEWITHGROUPS, 0},
+ {IDC_QUICKSEARCHVISONLY, CLS_EX_QUICKSEARCHVISONLY, 0},
+ {IDC_SORTGROUPSALPHA, CLS_EX_SORTGROUPSALPHA, 0},
+ {IDC_NOTNOSMOOTHSCROLLING, CLS_EX_NOSMOOTHSCROLLING, 1}
+};
+
+struct CheckBoxValues_t
+{
+ DWORD style;
+ TCHAR* szDescr;
+};
+
+static const struct CheckBoxValues_t greyoutValues[] = {
+ { GREYF_UNFOCUS, LPGENT("Not focused") },
+ { MODEF_OFFLINE, LPGENT("Offline") },
+ { PF2_ONLINE, LPGENT("Online") },
+ { PF2_SHORTAWAY, LPGENT("Away") },
+ { PF2_LONGAWAY, LPGENT("NA") },
+ { PF2_LIGHTDND, LPGENT("Occupied") },
+ { PF2_HEAVYDND, LPGENT("DND") },
+ { PF2_FREECHAT, LPGENT("Free for chat") },
+ { PF2_INVISIBLE, LPGENT("Invisible") },
+ { PF2_OUTTOLUNCH, LPGENT("Out to lunch") },
+ { PF2_ONTHEPHONE, LPGENT("On the phone") }
+};
+
+static const struct CheckBoxValues_t offlineValues[] = {
+ { MODEF_OFFLINE, LPGENT("Offline") },
+ { PF2_ONLINE, LPGENT("Online") },
+ { PF2_SHORTAWAY, LPGENT("Away") },
+ { PF2_LONGAWAY, LPGENT("NA") },
+ { PF2_LIGHTDND, LPGENT("Occupied") },
+ { PF2_HEAVYDND, LPGENT("DND") },
+ { PF2_FREECHAT, LPGENT("Free for chat") },
+ { PF2_INVISIBLE, LPGENT("Invisible") },
+ { PF2_OUTTOLUNCH, LPGENT("Out to lunch") },
+ { PF2_ONTHEPHONE, LPGENT("On the phone") }
+};
+
+static void FillCheckBoxTree(HWND hwndTree, const struct CheckBoxValues_t *values, int nValues, DWORD style)
+{
+ TVINSERTSTRUCT tvis;
+ int i;
+
+ tvis.hParent = NULL;
+ tvis.hInsertAfter = TVI_LAST;
+ tvis.item.mask = TVIF_PARAM | TVIF_TEXT | TVIF_STATE;
+ for (i = 0; i < nValues; i++) {
+ tvis.item.lParam = values[i].style;
+ tvis.item.pszText = TranslateTS( values[i].szDescr );
+ tvis.item.stateMask = TVIS_STATEIMAGEMASK;
+ tvis.item.state = INDEXTOSTATEIMAGEMASK((style & tvis.item.lParam) != 0 ? 2 : 1);
+ TreeView_InsertItem( hwndTree, &tvis);
+} }
+
+static DWORD MakeCheckBoxTreeFlags(HWND hwndTree)
+{
+ DWORD flags = 0;
+ TVITEM tvi;
+
+ tvi.mask = TVIF_HANDLE | TVIF_PARAM | TVIF_STATE;
+ tvi.hItem = TreeView_GetRoot(hwndTree);
+ while (tvi.hItem) {
+ TreeView_GetItem(hwndTree, &tvi);
+ if (((tvi.state & TVIS_STATEIMAGEMASK) >> 12 == 2))
+ flags |= tvi.lParam;
+ tvi.hItem = TreeView_GetNextSibling(hwndTree, tvi.hItem);
+ }
+ return flags;
+}
+
+
+static BOOL CALLBACK DlgProcClcMainOpts(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ TCHAR tmp[1024];
+
+ switch (msg) {
+ case WM_INITDIALOG:
+ TranslateDialogDefault(hwndDlg);
+ SetWindowLong(GetDlgItem(hwndDlg, IDC_GREYOUTOPTS), GWL_STYLE,
+ GetWindowLong(GetDlgItem(hwndDlg, IDC_GREYOUTOPTS), GWL_STYLE) | TVS_NOHSCROLL | TVS_CHECKBOXES);
+ SetWindowLong(GetDlgItem(hwndDlg, IDC_HIDEOFFLINEOPTS), GWL_STYLE,
+ GetWindowLong(GetDlgItem(hwndDlg, IDC_HIDEOFFLINEOPTS), GWL_STYLE) | TVS_NOHSCROLL | TVS_CHECKBOXES);
+ {
+ int i;
+ DWORD exStyle = DBGetContactSettingDword(NULL, "CLC", "ExStyle", pcli->pfnGetDefaultExStyle());
+ for (i = 0; i < SIZEOF(checkBoxToStyleEx); i++)
+ CheckDlgButton(hwndDlg, checkBoxToStyleEx[i].id,
+ (exStyle & checkBoxToStyleEx[i].flag) ^ (checkBoxToStyleEx[i].flag *
+ checkBoxToStyleEx[i].not) ? BST_CHECKED : BST_UNCHECKED);
+ }
+ {
+ UDACCEL accel[2] = { {0, 10} , {2, 50} };
+ SendDlgItemMessage(hwndDlg, IDC_SMOOTHTIMESPIN, UDM_SETRANGE, 0, MAKELONG(999, 0));
+ SendDlgItemMessage(hwndDlg, IDC_SMOOTHTIMESPIN, UDM_SETACCEL, SIZEOF(accel), (LPARAM) & accel);
+ SendDlgItemMessage(hwndDlg, IDC_SMOOTHTIMESPIN, UDM_SETPOS, 0,
+ MAKELONG(DBGetContactSettingWord(NULL, "CLC", "ScrollTime", CLCDEFAULT_SCROLLTIME), 0));
+ }
+ CheckDlgButton(hwndDlg, IDC_IDLE, DBGetContactSettingByte(NULL, "CLC", "ShowIdle", CLCDEFAULT_SHOWIDLE) ? BST_CHECKED : BST_UNCHECKED);
+ SendDlgItemMessage(hwndDlg, IDC_LEFTMARGINSPIN, UDM_SETRANGE, 0, MAKELONG(64, 0));
+ SendDlgItemMessage(hwndDlg, IDC_LEFTMARGINSPIN, UDM_SETPOS, 0,
+ MAKELONG(DBGetContactSettingByte(NULL, "CLC", "LeftMargin", CLCDEFAULT_LEFTMARGIN), 0));
+ SendDlgItemMessage(hwndDlg, IDC_GROUPINDENTSPIN, UDM_SETRANGE, 0, MAKELONG(50, 0));
+ SendDlgItemMessage(hwndDlg, IDC_GROUPINDENTSPIN, UDM_SETPOS, 0,
+ MAKELONG(DBGetContactSettingByte(NULL, "CLC", "GroupIndent", CLCDEFAULT_GROUPINDENT), 0));
+ CheckDlgButton(hwndDlg, IDC_GREYOUT,
+ DBGetContactSettingDword(NULL, "CLC", "GreyoutFlags", CLCDEFAULT_GREYOUTFLAGS) ? BST_CHECKED : BST_UNCHECKED);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_SMOOTHTIME), IsDlgButtonChecked(hwndDlg, IDC_NOTNOSMOOTHSCROLLING));
+ EnableWindow(GetDlgItem(hwndDlg, IDC_GREYOUTOPTS), IsDlgButtonChecked(hwndDlg, IDC_GREYOUT));
+ FillCheckBoxTree(GetDlgItem(hwndDlg, IDC_GREYOUTOPTS), greyoutValues, SIZEOF(greyoutValues),
+ DBGetContactSettingDword(NULL, "CLC", "FullGreyoutFlags", CLCDEFAULT_FULLGREYOUTFLAGS));
+ FillCheckBoxTree(GetDlgItem(hwndDlg, IDC_HIDEOFFLINEOPTS), offlineValues, SIZEOF(offlineValues),
+ DBGetContactSettingDword(NULL, "CLC", "OfflineModes", CLCDEFAULT_OFFLINEMODES));
+ CheckDlgButton(hwndDlg, IDC_NOSCROLLBAR, DBGetContactSettingByte(NULL, "CLC", "NoVScrollBar", 0) ? BST_CHECKED : BST_UNCHECKED);
+
+ SetDlgItemText(hwndDlg, IDC_T_CONTACT, MyDBGetContactSettingTString(NULL, "CLC", "TemplateContact", tmp, 1024, TranslateT("%name% [%status% %protocol%] %status_message%")));
+ SendDlgItemMessage(hwndDlg, IDC_T_CONTACT, EM_LIMITTEXT, 256, 0);
+ SetDlgItemText(hwndDlg, IDC_T_GROUP, MyDBGetContactSettingTString(NULL, "CLC", "TemplateGroup", tmp, 1024, TranslateT("Group: %name% %count% [%mode%]")));
+ SendDlgItemMessage(hwndDlg, IDC_T_GROUP, EM_LIMITTEXT, 256, 0);
+ SetDlgItemText(hwndDlg, IDC_T_DIVIDER, MyDBGetContactSettingTString(NULL, "CLC", "TemplateDivider", tmp, 1024, TranslateT("Divider: %s")));
+ SendDlgItemMessage(hwndDlg, IDC_T_DIVIDER, EM_LIMITTEXT, 256, 0);
+ SetDlgItemText(hwndDlg, IDC_T_INFO, MyDBGetContactSettingTString(NULL, "CLC", "TemplateInfo", tmp, 1024, TranslateT("Info: %s")));
+ SendDlgItemMessage(hwndDlg, IDC_T_INFO, EM_LIMITTEXT, 256, 0);
+ return TRUE;
+ case WM_VSCROLL:
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ break;
+ case WM_COMMAND:
+ if (LOWORD(wParam) == IDC_NOTNOSMOOTHSCROLLING)
+ EnableWindow(GetDlgItem(hwndDlg, IDC_SMOOTHTIME), IsDlgButtonChecked(hwndDlg, IDC_NOTNOSMOOTHSCROLLING));
+ if (LOWORD(wParam) == IDC_GREYOUT)
+ EnableWindow(GetDlgItem(hwndDlg, IDC_GREYOUTOPTS), IsDlgButtonChecked(hwndDlg, IDC_GREYOUT));
+ if ((LOWORD(wParam) == IDC_LEFTMARGIN || LOWORD(wParam) == IDC_SMOOTHTIME || LOWORD(wParam) == IDC_GROUPINDENT)
+ && (HIWORD(wParam) != EN_CHANGE || (HWND) lParam != GetFocus()))
+ return 0;
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ break;
+ case WM_NOTIFY:
+ switch (((LPNMHDR) lParam)->idFrom) {
+ case IDC_GREYOUTOPTS:
+ case IDC_HIDEOFFLINEOPTS:
+ if (((LPNMHDR) lParam)->code == NM_CLICK) {
+ TVHITTESTINFO hti;
+ hti.pt.x = (short) LOWORD(GetMessagePos());
+ hti.pt.y = (short) HIWORD(GetMessagePos());
+ ScreenToClient(((LPNMHDR) lParam)->hwndFrom, &hti.pt);
+ if (TreeView_HitTest(((LPNMHDR) lParam)->hwndFrom, &hti))
+ if (hti.flags & TVHT_ONITEMSTATEICON) {
+ TVITEM tvi;
+ tvi.mask = TVIF_HANDLE | TVIF_IMAGE | TVIF_SELECTEDIMAGE;
+ tvi.hItem = hti.hItem;
+ TreeView_GetItem(((LPNMHDR) lParam)->hwndFrom, &tvi);
+ tvi.iImage = tvi.iSelectedImage = tvi.iImage == 1 ? 2 : 1;
+ TreeView_SetItem(((LPNMHDR) lParam)->hwndFrom, &tvi);
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ }
+ }
+ break;
+ case 0:
+ if (((LPNMHDR) lParam)->code == PSN_APPLY ) {
+ int i;
+ DWORD exStyle = 0;
+ for (i = 0; i < SIZEOF(checkBoxToStyleEx); i++)
+ if ((IsDlgButtonChecked(hwndDlg, checkBoxToStyleEx[i].id) == 0) == checkBoxToStyleEx[i].not)
+ exStyle |= checkBoxToStyleEx[i].flag;
+
+ DBWriteContactSettingDword(NULL, "CLC", "ExStyle", exStyle);
+ {
+ DWORD fullGreyoutFlags = MakeCheckBoxTreeFlags(GetDlgItem(hwndDlg, IDC_GREYOUTOPTS));
+ DBWriteContactSettingDword(NULL, "CLC", "FullGreyoutFlags", fullGreyoutFlags);
+ if (IsDlgButtonChecked(hwndDlg, IDC_GREYOUT))
+ DBWriteContactSettingDword(NULL, "CLC", "GreyoutFlags", fullGreyoutFlags);
+ else
+ DBWriteContactSettingDword(NULL, "CLC", "GreyoutFlags", 0);
+ }
+ DBWriteContactSettingByte(NULL, "CLC", "ShowIdle", (BYTE) (IsDlgButtonChecked(hwndDlg, IDC_IDLE) ? 1 : 0));
+ DBWriteContactSettingDword(NULL, "CLC", "OfflineModes", MakeCheckBoxTreeFlags(GetDlgItem(hwndDlg, IDC_HIDEOFFLINEOPTS)));
+ DBWriteContactSettingByte(NULL, "CLC", "LeftMargin",
+ (BYTE) SendDlgItemMessage(hwndDlg, IDC_LEFTMARGINSPIN, UDM_GETPOS, 0, 0));
+ DBWriteContactSettingWord(NULL, "CLC", "ScrollTime",
+ (WORD) SendDlgItemMessage(hwndDlg, IDC_SMOOTHTIMESPIN, UDM_GETPOS, 0, 0));
+ DBWriteContactSettingByte(NULL, "CLC", "GroupIndent",
+ (BYTE) SendDlgItemMessage(hwndDlg, IDC_GROUPINDENTSPIN, UDM_GETPOS, 0, 0));
+ DBWriteContactSettingByte(NULL, "CLC", "NoVScrollBar", (BYTE) (IsDlgButtonChecked(hwndDlg, IDC_NOSCROLLBAR) ? 1 : 0));
+
+ GetDlgItemText(hwndDlg, IDC_T_CONTACT, tmp, 1024);
+ DBWriteContactSettingTString(NULL, "CLC", "TemplateContact", tmp);
+ GetDlgItemText(hwndDlg, IDC_T_GROUP, tmp, 1024);
+ DBWriteContactSettingTString(NULL, "CLC", "TemplateGroup", tmp);
+ GetDlgItemText(hwndDlg, IDC_T_DIVIDER, tmp, 1024);
+ DBWriteContactSettingTString(NULL, "CLC", "TemplateDivider", tmp);
+ GetDlgItemText(hwndDlg, IDC_T_INFO, tmp, 1024);
+ DBWriteContactSettingTString(NULL, "CLC", "TemplateInfo", tmp);
+
+ pcli->pfnClcOptionsChanged();
+ return TRUE;
+ }
+ break;
+ }
+ break;
+ case WM_DESTROY:
+ ImageList_Destroy(TreeView_GetImageList(GetDlgItem(hwndDlg, IDC_GREYOUTOPTS), TVSIL_NORMAL));
+ break;
+ }
+ return FALSE;
+}
+
+
+
+
+/****************************************************************************************/
+
+int ClcOptInit(WPARAM wParam, LPARAM lParam)
+{
+ OPTIONSDIALOGPAGE odp;
+
+ ZeroMemory(&odp, sizeof(odp));
+ odp.cbSize = sizeof(odp);
+ odp.position = 0;
+ odp.hInstance = g_hInst;
+ odp.pszGroup = LPGEN("Contact List");
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT_CLC);
+ odp.pszTitle = LPGEN("List");
+ odp.pfnDlgProc = DlgProcClcMainOpts;
+ odp.flags = ODPF_BOLDGROUPS | ODPF_EXPERTONLY;
+ CallService(MS_OPT_ADDPAGE, wParam, (LPARAM) & odp);
+
+ return 0;
+}
diff --git a/Plugins/bclist/clcpaint.c b/Plugins/bclist/clcpaint.c
new file mode 100644
index 0000000..88cc372
--- /dev/null
+++ b/Plugins/bclist/clcpaint.c
@@ -0,0 +1,587 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2003 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include "commonheaders.h"
+
+extern HIMAGELIST himlCListClc;
+static BYTE divide3[765] = { 255 };
+
+static void ChangeToFont(HDC hdc, struct ClcData *dat, int id, int *fontHeight)
+{
+ SelectObject(hdc, dat->fontInfo[id].hFont);
+ SetTextColor(hdc, dat->fontInfo[id].colour);
+ if (fontHeight)
+ *fontHeight = dat->fontInfo[id].fontHeight;
+}
+
+static void __inline SetHotTrackColour(HDC hdc, struct ClcData *dat)
+{
+ if (dat->gammaCorrection) {
+ COLORREF oldCol, newCol;
+ int oldLum, newLum;
+
+ oldCol = GetTextColor(hdc);
+ oldLum = (GetRValue(oldCol) * 30 + GetGValue(oldCol) * 59 + GetBValue(oldCol) * 11) / 100;
+ newLum = (GetRValue(dat->hotTextColour) * 30 + GetGValue(dat->hotTextColour) * 59 + GetBValue(dat->hotTextColour) * 11) / 100;
+ if (newLum == 0) {
+ SetTextColor(hdc, dat->hotTextColour);
+ return;
+ }
+ if (newLum >= oldLum + 20) {
+ oldLum += 20;
+ newCol =
+ RGB(GetRValue(dat->hotTextColour) * oldLum / newLum, GetGValue(dat->hotTextColour) * oldLum / newLum,
+ GetBValue(dat->hotTextColour) * oldLum / newLum);
+ }
+ else if (newLum <= oldLum) {
+ int r, g, b;
+ r = GetRValue(dat->hotTextColour) * oldLum / newLum;
+ g = GetGValue(dat->hotTextColour) * oldLum / newLum;
+ b = GetBValue(dat->hotTextColour) * oldLum / newLum;
+ if (r > 255) {
+ g += (r - 255) * 3 / 7;
+ b += (r - 255) * 3 / 7;
+ r = 255;
+ }
+ if (g > 255) {
+ r += (g - 255) * 59 / 41;
+ if (r > 255)
+ r = 255;
+ b += (g - 255) * 59 / 41;
+ g = 255;
+ }
+ if (b > 255) {
+ r += (b - 255) * 11 / 89;
+ if (r > 255)
+ r = 255;
+ g += (b - 255) * 11 / 89;
+ if (g > 255)
+ g = 255;
+ b = 255;
+ }
+ newCol = RGB(r, g, b);
+ }
+ else
+ newCol = dat->hotTextColour;
+ SetTextColor(hdc, newCol);
+ }
+ else
+ SetTextColor(hdc, dat->hotTextColour);
+}
+
+static int GetStatusOnlineness(int status)
+{
+ switch (status) {
+ case ID_STATUS_FREECHAT: return 110;
+ case ID_STATUS_ONLINE: return 100;
+ case ID_STATUS_OCCUPIED: return 60;
+ case ID_STATUS_ONTHEPHONE: return 50;
+ case ID_STATUS_DND: return 40;
+ case ID_STATUS_AWAY: return 30;
+ case ID_STATUS_OUTTOLUNCH: return 20;
+ case ID_STATUS_NA: return 10;
+ case ID_STATUS_INVISIBLE: return 5;
+ }
+ return 0;
+}
+
+static int GetGeneralisedStatus(void)
+{
+ int i, status, thisStatus, statusOnlineness, thisOnlineness;
+
+ status = ID_STATUS_OFFLINE;
+ statusOnlineness = 0;
+
+ for (i = 0; i < pcli->hClcProtoCount; i++) {
+ thisStatus = pcli->clcProto[i].dwStatus;
+ if (thisStatus == ID_STATUS_INVISIBLE)
+ return ID_STATUS_INVISIBLE;
+ thisOnlineness = GetStatusOnlineness(thisStatus);
+ if (thisOnlineness > statusOnlineness) {
+ status = thisStatus;
+ statusOnlineness = thisOnlineness;
+ }
+ }
+ return status;
+}
+
+static int GetRealStatus(struct ClcContact *contact, int status)
+{
+ int i;
+ char *szProto = contact->proto;
+ if (!szProto)
+ return status;
+ for (i = 0; i < pcli->hClcProtoCount; i++) {
+ if (!lstrcmpA(pcli->clcProto[i].szProto, szProto)) {
+ return pcli->clcProto[i].dwStatus;
+ }
+ }
+ return status;
+}
+
+static HMODULE themeAPIHandle = NULL; // handle to uxtheme.dll
+static HANDLE(WINAPI * MyOpenThemeData) (HWND, LPCWSTR);
+static HRESULT(WINAPI * MyCloseThemeData) (HANDLE);
+static HRESULT(WINAPI * MyDrawThemeBackground) (HANDLE, HDC, int, int, const RECT *, const RECT *);
+
+#define MGPROC(x) GetProcAddress(themeAPIHandle,x)
+void PaintClc(HWND hwnd, struct ClcData *dat, HDC hdc, RECT * rcPaint)
+{
+ HDC hdcMem;
+ RECT clRect;
+ int y, indent, index, fontHeight;
+ struct ClcGroup *group;
+ HBITMAP hBmpOsb, hOldBitmap;
+ HFONT hOldFont;
+ DWORD style = GetWindowLong(hwnd, GWL_STYLE);
+ int status = GetGeneralisedStatus();
+ int grey = 0, groupCountsFontTopShift;
+ HBRUSH hBrushAlternateGrey = NULL;
+ // yes I know about GetSysColorBrush()
+ COLORREF tmpbkcolour = style & CLS_CONTACTLIST ? (dat->useWindowsColours ? GetSysColor(COLOR_3DFACE) : dat->bkColour) : dat->bkColour;
+
+ if (dat->greyoutFlags & pcli->pfnClcStatusToPf2(status) || style & WS_DISABLED)
+ grey = 1;
+ else if (GetFocus() != hwnd && dat->greyoutFlags & GREYF_UNFOCUS)
+ grey = 1;
+ GetClientRect(hwnd, &clRect);
+ if (rcPaint == NULL)
+ rcPaint = &clRect;
+ if (IsRectEmpty(rcPaint))
+ return;
+ y = -dat->yScroll;
+ hdcMem = CreateCompatibleDC(hdc);
+ hBmpOsb = CreateBitmap(clRect.right, clRect.bottom, 1, GetDeviceCaps(hdc, BITSPIXEL), NULL);
+ hOldBitmap = SelectObject(hdcMem, hBmpOsb);
+ {
+ TEXTMETRIC tm;
+ hOldFont = SelectObject(hdcMem, dat->fontInfo[FONTID_GROUPS].hFont);
+ GetTextMetrics(hdcMem, &tm);
+ groupCountsFontTopShift = tm.tmAscent;
+ SelectObject(hdcMem, dat->fontInfo[FONTID_GROUPCOUNTS].hFont);
+ GetTextMetrics(hdcMem, &tm);
+ groupCountsFontTopShift -= tm.tmAscent;
+ }
+ if (style & CLS_GREYALTERNATE)
+ hBrushAlternateGrey =
+ CreateSolidBrush(GetNearestColor(hdcMem, RGB(GetRValue(tmpbkcolour) - 10, GetGValue(tmpbkcolour) - 10, GetBValue(tmpbkcolour) - 10)));
+
+ ChangeToFont(hdcMem, dat, FONTID_CONTACTS, &fontHeight);
+ SetBkMode(hdcMem, TRANSPARENT);
+ {
+ HBRUSH hBrush;
+
+ hBrush = CreateSolidBrush(tmpbkcolour);
+ FillRect(hdcMem, rcPaint, hBrush);
+ DeleteObject(hBrush);
+ if (dat->hBmpBackground) {
+ BITMAP bmp;
+ HDC hdcBmp;
+ int x, y;
+ int maxx, maxy;
+ int destw, desth;
+
+ // XXX: Halftone isnt supported on 9x, however the scretch problems dont happen on 98.
+ SetStretchBltMode(hdcMem, HALFTONE);
+
+ GetObject(dat->hBmpBackground, sizeof(bmp), &bmp);
+ hdcBmp = CreateCompatibleDC(hdcMem);
+ SelectObject(hdcBmp, dat->hBmpBackground);
+ y = dat->backgroundBmpUse & CLBF_SCROLL ? -dat->yScroll : 0;
+ maxx = dat->backgroundBmpUse & CLBF_TILEH ? clRect.right : 1;
+ maxy = dat->backgroundBmpUse & CLBF_TILEV ? maxy = rcPaint->bottom : y + 1;
+ switch (dat->backgroundBmpUse & CLBM_TYPE) {
+ case CLB_STRETCH:
+ if (dat->backgroundBmpUse & CLBF_PROPORTIONAL) {
+ if (clRect.right * bmp.bmHeight < clRect.bottom * bmp.bmWidth) {
+ desth = clRect.bottom;
+ destw = desth * bmp.bmWidth / bmp.bmHeight;
+ }
+ else {
+ destw = clRect.right;
+ desth = destw * bmp.bmHeight / bmp.bmWidth;
+ }
+ }
+ else {
+ destw = clRect.right;
+ desth = clRect.bottom;
+ }
+ break;
+ case CLB_STRETCHH:
+ if (dat->backgroundBmpUse & CLBF_PROPORTIONAL) {
+ destw = clRect.right;
+ desth = destw * bmp.bmHeight / bmp.bmWidth;
+ }
+ else {
+ destw = clRect.right;
+ desth = bmp.bmHeight;
+ }
+ break;
+ case CLB_STRETCHV:
+ if (dat->backgroundBmpUse & CLBF_PROPORTIONAL) {
+ desth = clRect.bottom;
+ destw = desth * bmp.bmWidth / bmp.bmHeight;
+ }
+ else {
+ destw = bmp.bmWidth;
+ desth = clRect.bottom;
+ }
+ break;
+ default: //clb_topleft
+ destw = bmp.bmWidth;
+ desth = bmp.bmHeight;
+ break;
+ }
+ for (; y < maxy; y += desth) {
+ if (y < rcPaint->top - desth)
+ continue;
+ for (x = 0; x < maxx; x += destw)
+ StretchBlt(hdcMem, x, y, destw, desth, hdcBmp, 0, 0, bmp.bmWidth, bmp.bmHeight, SRCCOPY);
+ }
+ DeleteDC(hdcBmp);
+ }
+ }
+ group = &dat->list;
+ group->scanIndex = 0;
+ indent = 0;
+ for (index = 0; y < rcPaint->bottom;) {
+ if (group->scanIndex == group->cl.count) {
+ group = group->parent;
+ indent--;
+ if (group == NULL)
+ break;
+ group->scanIndex++;
+ continue;
+ }
+ if (y > rcPaint->top - dat->rowHeight) {
+ int iImage = -1;
+ int selected = index == dat->selection && (dat->showSelAlways || dat->exStyle & CLS_EX_SHOWSELALWAYS || GetFocus() == hwnd)
+ && group->cl.items[group->scanIndex]->type != CLCIT_DIVIDER;
+ int hottrack = dat->exStyle & CLS_EX_TRACKSELECT && group->cl.items[group->scanIndex]->type != CLCIT_DIVIDER && dat->iHotTrack == index;
+ SIZE textSize, countsSize, spaceSize;
+ int width, checkboxWidth;
+ char *szCounts;
+
+ //alternating grey
+ if (style & CLS_GREYALTERNATE && index & 1) {
+ RECT rc;
+ rc.top = y;
+ rc.bottom = rc.top + dat->rowHeight;
+ rc.left = 0;
+ rc.right = clRect.right;
+ FillRect(hdcMem, &rc, hBrushAlternateGrey);
+ }
+
+ //setup
+ if (group->cl.items[group->scanIndex]->type == CLCIT_GROUP)
+ ChangeToFont(hdcMem, dat, FONTID_GROUPS, &fontHeight);
+ else if (group->cl.items[group->scanIndex]->type == CLCIT_INFO) {
+ if (group->cl.items[group->scanIndex]->flags & CLCIIF_GROUPFONT)
+ ChangeToFont(hdcMem, dat, FONTID_GROUPS, &fontHeight);
+ else
+ ChangeToFont(hdcMem, dat, FONTID_CONTACTS, &fontHeight);
+ }
+ else if (group->cl.items[group->scanIndex]->type == CLCIT_DIVIDER)
+ ChangeToFont(hdcMem, dat, FONTID_DIVIDERS, &fontHeight);
+ else if (group->cl.items[group->scanIndex]->type == CLCIT_CONTACT && group->cl.items[group->scanIndex]->flags & CONTACTF_NOTONLIST)
+ ChangeToFont(hdcMem, dat, FONTID_NOTONLIST, &fontHeight);
+ else if (group->cl.items[group->scanIndex]->type == CLCIT_CONTACT &&
+ ((group->cl.items[group->scanIndex]->flags & CONTACTF_INVISTO
+ && GetRealStatus(group->cl.items[group->scanIndex], status) != ID_STATUS_INVISIBLE)
+ || (group->cl.items[group->scanIndex]->flags & CONTACTF_VISTO
+ && GetRealStatus(group->cl.items[group->scanIndex], status) == ID_STATUS_INVISIBLE)
+ )
+ ) {
+ // the contact is in the always visible list and the proto is invisible
+ // the contact is in the always invisible and the proto is in any other mode
+ ChangeToFont(hdcMem, dat, group->cl.items[group->scanIndex]->flags & CONTACTF_ONLINE ? FONTID_INVIS : FONTID_OFFINVIS, &fontHeight);
+ }
+ else if (group->cl.items[group->scanIndex]->type == CLCIT_CONTACT && !(group->cl.items[group->scanIndex]->flags & CONTACTF_ONLINE))
+ ChangeToFont(hdcMem, dat, FONTID_OFFLINE, &fontHeight);
+ else
+ ChangeToFont(hdcMem, dat, FONTID_CONTACTS, &fontHeight);
+ GetTextExtentPoint32(hdcMem, group->cl.items[group->scanIndex]->szText, lstrlen(group->cl.items[group->scanIndex]->szText), &textSize);
+ width = textSize.cx;
+ if (group->cl.items[group->scanIndex]->type == CLCIT_GROUP) {
+ szCounts = pcli->pfnGetGroupCountsText(dat, group->cl.items[group->scanIndex]);
+ if (szCounts[0]) {
+ GetTextExtentPoint32A(hdcMem, " ", 1, &spaceSize);
+ ChangeToFont(hdcMem, dat, FONTID_GROUPCOUNTS, &fontHeight);
+ GetTextExtentPoint32A(hdcMem, szCounts, lstrlenA(szCounts), &countsSize);
+ width += spaceSize.cx + countsSize.cx;
+ }
+ }
+
+ if ((style & CLS_CHECKBOXES && group->cl.items[group->scanIndex]->type == CLCIT_CONTACT) ||
+ (style & CLS_GROUPCHECKBOXES && group->cl.items[group->scanIndex]->type == CLCIT_GROUP) ||
+ (group->cl.items[group->scanIndex]->type == CLCIT_INFO && group->cl.items[group->scanIndex]->flags & CLCIIF_CHECKBOX))
+ checkboxWidth = dat->checkboxSize + 2;
+ else
+ checkboxWidth = 0;
+
+ //background
+ if (selected) {
+ int x = dat->leftMargin + indent * dat->groupIndent + checkboxWidth + dat->iconXSpace - 2;
+ ImageList_DrawEx(dat->himlHighlight, 0, hdcMem, x, y, min(width + 5, clRect.right - x), dat->rowHeight, CLR_NONE, CLR_NONE,
+ dat->exStyle & CLS_EX_NOTRANSLUCENTSEL ? ILD_NORMAL : ILD_BLEND25);
+ SetTextColor(hdcMem, dat->selTextColour);
+ }
+ else if (hottrack)
+ SetHotTrackColour(hdcMem, dat);
+
+ //checkboxes
+ if (checkboxWidth) {
+ RECT rc;
+ HANDLE hTheme = NULL;
+
+ // THEME
+ if (IsWinVerXPPlus()) {
+ if (!themeAPIHandle) {
+ themeAPIHandle = GetModuleHandleA("uxtheme");
+ if (themeAPIHandle) {
+ MyOpenThemeData = (HANDLE(WINAPI *) (HWND, LPCWSTR)) MGPROC("OpenThemeData");
+ MyCloseThemeData = (HRESULT(WINAPI *) (HANDLE)) MGPROC("CloseThemeData");
+ MyDrawThemeBackground =
+ (HRESULT(WINAPI *) (HANDLE, HDC, int, int, const RECT *, const RECT *)) MGPROC("DrawThemeBackground");
+ }
+ }
+ // Make sure all of these methods are valid (i would hope either all or none work)
+ if (MyOpenThemeData && MyCloseThemeData && MyDrawThemeBackground) {
+ hTheme = MyOpenThemeData(hwnd, L"BUTTON");
+ }
+ }
+ rc.left = dat->leftMargin + indent * dat->groupIndent;
+ rc.right = rc.left + dat->checkboxSize;
+ rc.top = y + ((dat->rowHeight - dat->checkboxSize) >> 1);
+ rc.bottom = rc.top + dat->checkboxSize;
+ if (hTheme) {
+ MyDrawThemeBackground(hTheme, hdcMem, BP_CHECKBOX, group->cl.items[group->scanIndex]->flags & CONTACTF_CHECKED ? (hottrack ? CBS_CHECKEDHOT : CBS_CHECKEDNORMAL) : (hottrack ? CBS_UNCHECKEDHOT : CBS_UNCHECKEDNORMAL), &rc, &rc);
+ }
+ else
+ DrawFrameControl(hdcMem, &rc, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_FLAT | (group->cl.items[group->scanIndex]->flags & CONTACTF_CHECKED ? DFCS_CHECKED : 0) | (hottrack ? DFCS_HOT : 0));
+ if (hTheme && MyCloseThemeData) {
+ MyCloseThemeData(hTheme);
+ hTheme = NULL;
+ }
+ }
+
+ //icon
+ if (group->cl.items[group->scanIndex]->type == CLCIT_GROUP)
+ iImage = group->cl.items[group->scanIndex]->group->expanded ? IMAGE_GROUPOPEN : IMAGE_GROUPSHUT;
+ else if (group->cl.items[group->scanIndex]->type == CLCIT_CONTACT)
+ iImage = group->cl.items[group->scanIndex]->iImage;
+ if (iImage != -1) {
+ /*COLORREF colourFg=dat->selBkColour;
+ int mode=ILD_NORMAL;
+ if(selected) mode=ILD_SELECTED;
+ else if(hottrack) {mode=ILD_FOCUS; colourFg=dat->hotTextColour;}
+ else if(group->cl.items[group->scanIndex]->type==CLCIT_CONTACT && group->cl.items[group->scanIndex]->flags&CONTACTF_NOTONLIST) {colourFg=dat->fontInfo[FONTID_NOTONLIST].colour; mode=ILD_BLEND50;}
+ ImageList_DrawEx(himlCListClc,iImage,hdcMem,dat->leftMargin+indent*dat->groupIndent+checkboxWidth,y+((dat->rowHeight-16)>>1),0,0,CLR_NONE,colourFg,mode);
+ */
+ // this doesnt use CLS_CONTACTLIST since the colour prolly wont match anyway
+ COLORREF colourFg = dat->selBkColour;
+ int mode = ILD_NORMAL;
+ if (hottrack) {
+ colourFg = dat->hotTextColour;
+ }
+ else if (group->cl.items[group->scanIndex]->type == CLCIT_CONTACT && group->cl.items[group->scanIndex]->flags & CONTACTF_NOTONLIST) {
+ colourFg = dat->fontInfo[FONTID_NOTONLIST].colour;
+ mode = ILD_BLEND50;
+ }
+ if (group->cl.items[group->scanIndex]->type == CLCIT_CONTACT && dat->showIdle
+ && (group->cl.items[group->scanIndex]->flags & CONTACTF_IDLE)
+ && GetRealStatus(group->cl.items[group->scanIndex], ID_STATUS_OFFLINE) != ID_STATUS_OFFLINE)
+ mode = ILD_SELECTED;
+ ImageList_DrawEx(himlCListClc, iImage, hdcMem, dat->leftMargin + indent * dat->groupIndent + checkboxWidth,
+ y + ((dat->rowHeight - 16) >> 1), 0, 0, CLR_NONE, colourFg, mode);
+ }
+
+ //text
+ if (group->cl.items[group->scanIndex]->type == CLCIT_DIVIDER) {
+ RECT rc;
+ rc.top = y + (dat->rowHeight >> 1);
+ rc.bottom = rc.top + 2;
+ rc.left = dat->leftMargin + indent * dat->groupIndent;
+ rc.right = rc.left + ((clRect.right - rc.left - textSize.cx) >> 1) - 3;
+ DrawEdge(hdcMem, &rc, BDR_SUNKENOUTER, BF_RECT);
+ TextOut(hdcMem, rc.right + 3, y + ((dat->rowHeight - fontHeight) >> 1), group->cl.items[group->scanIndex]->szText,
+ lstrlen(group->cl.items[group->scanIndex]->szText));
+ rc.left = rc.right + 6 + textSize.cx;
+ rc.right = clRect.right;
+ DrawEdge(hdcMem, &rc, BDR_SUNKENOUTER, BF_RECT);
+ }
+ else if (group->cl.items[group->scanIndex]->type == CLCIT_GROUP) {
+ RECT rc;
+ if (szCounts[0]) {
+ fontHeight = dat->fontInfo[FONTID_GROUPS].fontHeight;
+ rc.left = dat->leftMargin + indent * dat->groupIndent + checkboxWidth + dat->iconXSpace;
+ rc.right = min(clRect.right - countsSize.cx, rc.left + textSize.cx + spaceSize.cx);
+ rc.top = y + ((dat->rowHeight - fontHeight) >> 1);
+ rc.bottom = rc.top + textSize.cy;
+ if (rc.right < rc.left + 4)
+ rc.right = clRect.right + 1;
+ else
+ TextOutA(hdcMem, rc.right, rc.top + groupCountsFontTopShift, szCounts, lstrlenA(szCounts));
+ ChangeToFont(hdcMem, dat, FONTID_GROUPS, &fontHeight);
+ if (selected)
+ SetTextColor(hdcMem, dat->selTextColour);
+ else if (hottrack)
+ SetHotTrackColour(hdcMem, dat);
+ rc.right--;
+ ExtTextOut(hdcMem, rc.left, rc.top, ETO_CLIPPED, &rc, group->cl.items[group->scanIndex]->szText,
+ lstrlen(group->cl.items[group->scanIndex]->szText), NULL);
+ }
+ else
+ TextOut(hdcMem, dat->leftMargin + indent * dat->groupIndent + checkboxWidth + dat->iconXSpace,
+ y + ((dat->rowHeight - fontHeight) >> 1), group->cl.items[group->scanIndex]->szText,
+ lstrlen(group->cl.items[group->scanIndex]->szText));
+ if (dat->exStyle & CLS_EX_LINEWITHGROUPS) {
+ rc.top = y + (dat->rowHeight >> 1);
+ rc.bottom = rc.top + 2;
+ rc.left = dat->leftMargin + indent * dat->groupIndent + checkboxWidth + dat->iconXSpace + width + 3;
+ rc.right = clRect.right - 1 - dat->extraColumnSpacing * dat->extraColumnsCount;
+ if (rc.right - rc.left > 1)
+ DrawEdge(hdcMem, &rc, BDR_SUNKENOUTER, BF_RECT);
+ }
+ }
+ else {
+ TCHAR *szText = group->cl.items[group->scanIndex]->szText;
+ RECT rc;
+ rc.left = dat->leftMargin + indent * dat->groupIndent + checkboxWidth + dat->iconXSpace;
+ rc.top = y + ((dat->rowHeight - fontHeight) >> 1);
+ rc.right = (clRect.right - clRect.left);
+ rc.bottom = rc.top;
+ DrawText(hdcMem, szText, lstrlen(szText), &rc, DT_EDITCONTROL | DT_NOPREFIX | DT_NOCLIP | DT_WORD_ELLIPSIS | DT_SINGLELINE);
+ }
+ if (selected) {
+ if (group->cl.items[group->scanIndex]->type != CLCIT_DIVIDER) {
+ TCHAR *szText = group->cl.items[group->scanIndex]->szText;
+ RECT rc;
+ int qlen = lstrlen(dat->szQuickSearch);
+ SetTextColor(hdcMem, dat->quickSearchColour);
+ rc.left = dat->leftMargin + indent * dat->groupIndent + checkboxWidth + dat->iconXSpace;
+ rc.top = y + ((dat->rowHeight - fontHeight) >> 1);
+ rc.right = (clRect.right - clRect.left);
+ rc.bottom = rc.top;
+ if (qlen)
+ DrawText(hdcMem, szText, qlen, &rc, DT_EDITCONTROL | DT_NOPREFIX | DT_NOCLIP | DT_WORD_ELLIPSIS | DT_SINGLELINE);
+ }
+ }
+
+ //extra icons
+ for (iImage = 0; iImage < dat->extraColumnsCount; iImage++) {
+ COLORREF colourFg = dat->selBkColour;
+ int mode = ILD_NORMAL;
+ if (group->cl.items[group->scanIndex]->iExtraImage[iImage] == 0xFF)
+ continue;
+ if (selected)
+ mode = ILD_SELECTED;
+ else if (hottrack) {
+ mode = ILD_FOCUS;
+ colourFg = dat->hotTextColour;
+ }
+ else if (group->cl.items[group->scanIndex]->type == CLCIT_CONTACT && group->cl.items[group->scanIndex]->flags & CONTACTF_NOTONLIST) {
+ colourFg = dat->fontInfo[FONTID_NOTONLIST].colour;
+ mode = ILD_BLEND50;
+ }
+ ImageList_DrawEx(dat->himlExtraColumns, group->cl.items[group->scanIndex]->iExtraImage[iImage], hdcMem,
+ clRect.right - dat->extraColumnSpacing * (dat->extraColumnsCount - iImage), y + ((dat->rowHeight - 16) >> 1), 0, 0,
+ CLR_NONE, colourFg, mode);
+ }
+ }
+ index++;
+ y += dat->rowHeight;
+ if (group->cl.items[group->scanIndex]->type == CLCIT_GROUP && group->cl.items[group->scanIndex]->group->expanded) {
+ group = group->cl.items[group->scanIndex]->group;
+ indent++;
+ group->scanIndex = 0;
+ continue;
+ }
+ group->scanIndex++;
+ }
+ if (dat->iInsertionMark != -1) { //insertion mark
+ HBRUSH hBrush;
+ POINT pts[8];
+ HRGN hRgn;
+
+ pts[0].x = dat->leftMargin;
+ pts[0].y = dat->iInsertionMark * dat->rowHeight - dat->yScroll - 4;
+ pts[1].x = pts[0].x + 2;
+ pts[1].y = pts[0].y + 3;
+ pts[2].x = clRect.right - 4;
+ pts[2].y = pts[1].y;
+ pts[3].x = clRect.right - 1;
+ pts[3].y = pts[0].y - 1;
+ pts[4].x = pts[3].x;
+ pts[4].y = pts[0].y + 7;
+ pts[5].x = pts[2].x + 1;
+ pts[5].y = pts[1].y + 2;
+ pts[6].x = pts[1].x;
+ pts[6].y = pts[5].y;
+ pts[7].x = pts[0].x;
+ pts[7].y = pts[4].y;
+ hRgn = CreatePolygonRgn(pts, SIZEOF(pts), ALTERNATE);
+ hBrush = CreateSolidBrush(dat->fontInfo[FONTID_CONTACTS].colour);
+ FillRgn(hdcMem, hRgn, hBrush);
+ DeleteObject(hBrush);
+ DeleteObject(hRgn);
+ }
+ if (!grey)
+ BitBlt(hdc, rcPaint->left, rcPaint->top, rcPaint->right - rcPaint->left, rcPaint->bottom - rcPaint->top, hdcMem, rcPaint->left, rcPaint->top,
+ SRCCOPY);
+ SelectObject(hdcMem,hOldBitmap);
+ SelectObject(hdcMem,hOldFont);
+ DeleteDC(hdcMem);
+ if (hBrushAlternateGrey)
+ DeleteObject(hBrushAlternateGrey);
+ if (grey) {
+ PBYTE bits;
+ BITMAPINFOHEADER bmih = { 0 };
+ int i;
+ int greyRed, greyGreen, greyBlue;
+ COLORREF greyColour;
+ bmih.biBitCount = 32;
+ bmih.biSize = sizeof(bmih);
+ bmih.biCompression = BI_RGB;
+ bmih.biHeight = -clRect.bottom;
+ bmih.biPlanes = 1;
+ bmih.biWidth = clRect.right;
+ bits = (PBYTE) malloc(4 * bmih.biWidth * -bmih.biHeight);
+ GetDIBits(hdc, hBmpOsb, 0, clRect.bottom, bits, (BITMAPINFO *) & bmih, DIB_RGB_COLORS);
+ greyColour = GetSysColor(COLOR_3DFACE);
+ greyRed = GetRValue(greyColour) * 2;
+ greyGreen = GetGValue(greyColour) * 2;
+ greyBlue = GetBValue(greyColour) * 2;
+ if (divide3[0] == 255) {
+ for (i = 0; i < SIZEOF(divide3); i++)
+ divide3[i] = (i + 1) / 3;
+ }
+ for (i = 4 * clRect.right * clRect.bottom - 4; i >= 0; i -= 4) {
+ bits[i] = divide3[bits[i] + greyBlue];
+ bits[i + 1] = divide3[bits[i + 1] + greyGreen];
+ bits[i + 2] = divide3[bits[i + 2] + greyRed];
+ }
+ SetDIBitsToDevice(hdc, 0, 0, clRect.right, clRect.bottom, 0, 0, 0, clRect.bottom, bits, (BITMAPINFO *) & bmih, DIB_RGB_COLORS);
+ free(bits);
+ }
+ DeleteObject(hBmpOsb);
+}
diff --git a/Plugins/bclist/clistmenus.c b/Plugins/bclist/clistmenus.c
new file mode 100644
index 0000000..280e794
--- /dev/null
+++ b/Plugins/bclist/clistmenus.c
@@ -0,0 +1,36 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2003 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include "commonheaders.h"
+
+INT_PTR CloseAction(WPARAM wParam,LPARAM lParam)
+{
+ if (CallService(MS_SYSTEM_OKTOEXIT,(WPARAM)0,(LPARAM)0))
+ DestroyWindow(pcli->hwndContactList);
+
+ return(0);
+}
+
+void InitCustomMenus()
+{
+ CreateServiceFunction( "CloseAction", CloseAction );
+}
diff --git a/Plugins/bclist/clistopts.c b/Plugins/bclist/clistopts.c
new file mode 100644
index 0000000..f677b44
--- /dev/null
+++ b/Plugins/bclist/clistopts.c
@@ -0,0 +1,254 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2003 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include "commonheaders.h"
+
+static INT_PTR CALLBACK DlgProcGenOpts(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg) {
+ case WM_USER + 1:
+ {
+ HANDLE hContact = (HANDLE) wParam;
+ DBCONTACTWRITESETTING *ws = (DBCONTACTWRITESETTING *) lParam;
+ if (hContact == NULL && ws != NULL && ws->szModule != NULL && ws->szSetting != NULL
+ && lstrcmpiA(ws->szModule, "CList") == 0 && lstrcmpiA(ws->szSetting, "UseGroups") == 0 && IsWindowVisible(hwndDlg)) {
+ CheckDlgButton(hwndDlg, IDC_DISABLEGROUPS, ws->value.bVal == 0);
+ }
+ break;
+ }
+ case WM_DESTROY:
+ {
+ UnhookEvent((HANDLE) GetWindowLongPtr(hwndDlg, GWLP_USERDATA));
+ break;
+ }
+ case WM_INITDIALOG:
+ TranslateDialogDefault(hwndDlg);
+ SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR) HookEventMessage(ME_DB_CONTACT_SETTINGCHANGED, hwndDlg, WM_USER + 1));
+ CheckDlgButton(hwndDlg, IDC_ONTOP, DBGetContactSettingByte(NULL, "CList", "OnTop", SETTING_ONTOP_DEFAULT) ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_HIDEOFFLINE,
+ DBGetContactSettingByte(NULL, "CList", "HideOffline", SETTING_HIDEOFFLINE_DEFAULT) ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_HIDEEMPTYGROUPS,
+ DBGetContactSettingByte(NULL, "CList", "HideEmptyGroups", SETTING_HIDEEMPTYGROUPS_DEFAULT) ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_DISABLEGROUPS,
+ DBGetContactSettingByte(NULL, "CList", "UseGroups", SETTING_USEGROUPS_DEFAULT) ? BST_UNCHECKED : BST_CHECKED);
+ CheckDlgButton(hwndDlg, IDC_SORTBYNAME, !DBGetContactSettingByte(NULL, "CList", "SortByStatus", SETTING_SORTBYSTATUS_DEFAULT)
+ && !DBGetContactSettingByte(NULL, "CList", "SortByProto", SETTING_SORTBYPROTO_DEFAULT) ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_SORTBYSTATUS,
+ DBGetContactSettingByte(NULL, "CList", "SortByStatus", SETTING_SORTBYSTATUS_DEFAULT) ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_SORTBYPROTO,
+ DBGetContactSettingByte(NULL, "CList", "SortByProto", SETTING_SORTBYPROTO_DEFAULT) ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_CONFIRMDELETE,
+ DBGetContactSettingByte(NULL, "CList", "ConfirmDelete", SETTING_CONFIRMDELETE_DEFAULT) ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_AUTOHIDE,
+ DBGetContactSettingByte(NULL, "CList", "AutoHide", SETTING_AUTOHIDE_DEFAULT) ? BST_CHECKED : BST_UNCHECKED);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_HIDETIME), IsDlgButtonChecked(hwndDlg, IDC_AUTOHIDE));
+ EnableWindow(GetDlgItem(hwndDlg, IDC_HIDETIMESPIN), IsDlgButtonChecked(hwndDlg, IDC_AUTOHIDE));
+ {
+ DWORD caps = CallService(MS_CLUI_GETCAPS, CLUICAPS_FLAGS1, 0);
+ if (!(caps & CLUIF_HIDEEMPTYGROUPS))
+ ShowWindow(GetDlgItem(hwndDlg, IDC_HIDEEMPTYGROUPS), SW_HIDE);
+ if (!(caps & CLUIF_DISABLEGROUPS))
+ ShowWindow(GetDlgItem(hwndDlg, IDC_DISABLEGROUPS), SW_HIDE);
+ if (caps & CLUIF_HASONTOPOPTION)
+ ShowWindow(GetDlgItem(hwndDlg, IDC_ONTOP), SW_HIDE);
+ if (caps & CLUIF_HASAUTOHIDEOPTION) {
+ ShowWindow(GetDlgItem(hwndDlg, IDC_AUTOHIDE), SW_HIDE);
+ ShowWindow(GetDlgItem(hwndDlg, IDC_HIDETIME), SW_HIDE);
+ ShowWindow(GetDlgItem(hwndDlg, IDC_HIDETIMESPIN), SW_HIDE);
+ ShowWindow(GetDlgItem(hwndDlg, IDC_STAUTOHIDESECS), SW_HIDE);
+ }
+ }
+ SendDlgItemMessage(hwndDlg, IDC_HIDETIMESPIN, UDM_SETRANGE, 0, MAKELONG(900, 1));
+ SendDlgItemMessage(hwndDlg, IDC_HIDETIMESPIN, UDM_SETPOS, 0,
+ MAKELONG(DBGetContactSettingWord(NULL, "CList", "HideTime", SETTING_HIDETIME_DEFAULT), 0));
+ CheckDlgButton(hwndDlg, IDC_ONECLK,
+ DBGetContactSettingByte(NULL, "CList", "Tray1Click", SETTING_TRAY1CLICK_DEFAULT) ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_ALWAYSSTATUS,
+ DBGetContactSettingByte(NULL, "CList", "AlwaysStatus", SETTING_ALWAYSSTATUS_DEFAULT) ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_ALWAYSMULTI,
+ !DBGetContactSettingByte(NULL, "CList", "AlwaysMulti", SETTING_ALWAYSMULTI_DEFAULT) ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_DONTCYCLE,
+ DBGetContactSettingByte(NULL, "CList", "TrayIcon",
+ SETTING_TRAYICON_DEFAULT) == SETTING_TRAYICON_SINGLE ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_CYCLE,
+ DBGetContactSettingByte(NULL, "CList", "TrayIcon",
+ SETTING_TRAYICON_DEFAULT) == SETTING_TRAYICON_CYCLE ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_MULTITRAY,
+ DBGetContactSettingByte(NULL, "CList", "TrayIcon",
+ SETTING_TRAYICON_DEFAULT) == SETTING_TRAYICON_MULTI ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_DISABLEBLINK,
+ DBGetContactSettingByte(NULL, "CList", "DisableTrayFlash", 0) == 1 ? BST_CHECKED : BST_UNCHECKED);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_BLINKTIME), !IsDlgButtonChecked(hwndDlg, IDC_DISABLEBLINK));
+ EnableWindow(GetDlgItem(hwndDlg, IDC_BLINKSPIN), !IsDlgButtonChecked(hwndDlg, IDC_DISABLEBLINK));
+ EnableWindow(GetDlgItem(hwndDlg, IDC_STMSDELAY), !IsDlgButtonChecked(hwndDlg, IDC_DISABLEBLINK));
+ if (IsDlgButtonChecked(hwndDlg, IDC_DONTCYCLE)) {
+ EnableWindow(GetDlgItem(hwndDlg, IDC_CYCLETIMESPIN), FALSE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_CYCLETIME), FALSE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_ALWAYSMULTI), FALSE);
+ }
+ if (IsDlgButtonChecked(hwndDlg, IDC_CYCLE)) {
+ EnableWindow(GetDlgItem(hwndDlg, IDC_PRIMARYSTATUS), FALSE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_ALWAYSMULTI), FALSE);
+ }
+ if (IsDlgButtonChecked(hwndDlg, IDC_MULTITRAY)) {
+ EnableWindow(GetDlgItem(hwndDlg, IDC_CYCLETIMESPIN), FALSE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_CYCLETIME), FALSE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_PRIMARYSTATUS), FALSE);
+ }
+ SendDlgItemMessage(hwndDlg, IDC_CYCLETIMESPIN, UDM_SETRANGE, 0, MAKELONG(120, 1));
+ SendDlgItemMessage(hwndDlg, IDC_CYCLETIMESPIN, UDM_SETPOS, 0,
+ MAKELONG(DBGetContactSettingWord(NULL, "CList", "CycleTime", SETTING_CYCLETIME_DEFAULT), 0));
+ {
+ int i, count, item;
+ PROTOACCOUNT **accs;
+ char szName[64];
+ DBVARIANT dbv = { DBVT_DELETED };
+ DBGetContactSetting(NULL, "CList", "PrimaryStatus", &dbv);
+ CallService( MS_PROTO_ENUMACCOUNTS, (WPARAM)&count, (LPARAM)&accs);
+ item = SendDlgItemMessage(hwndDlg, IDC_PRIMARYSTATUS, CB_ADDSTRING, 0, (LPARAM) TranslateT("Global"));
+ SendDlgItemMessage(hwndDlg, IDC_PRIMARYSTATUS, CB_SETITEMDATA, item, (LPARAM) 0);
+ for (i = 0; i < count; i++) {
+ if (!IsAccountEnabled(accs[i]) || CallProtoService( accs[i]->szModuleName, PS_GETCAPS, PFLAGNUM_2, 0) == 0)
+ continue;
+ CallProtoService(accs[i]->szModuleName, PS_GETNAME, SIZEOF(szName), (LPARAM) szName);
+ item = SendDlgItemMessageA(hwndDlg, IDC_PRIMARYSTATUS, CB_ADDSTRING, 0, (LPARAM) szName);
+ SendDlgItemMessage(hwndDlg, IDC_PRIMARYSTATUS, CB_SETITEMDATA, item, (LPARAM) accs[i]);
+ if (dbv.type == DBVT_ASCIIZ && !lstrcmpA(dbv.pszVal, accs[i]->szModuleName))
+ SendDlgItemMessage(hwndDlg, IDC_PRIMARYSTATUS, CB_SETCURSEL, item, 0);
+ }
+ DBFreeVariant(&dbv);
+ }
+ if (-1 == (int) SendDlgItemMessage(hwndDlg, IDC_PRIMARYSTATUS, CB_GETCURSEL, 0, 0))
+ SendDlgItemMessage(hwndDlg, IDC_PRIMARYSTATUS, CB_SETCURSEL, 0, 0);
+ SendDlgItemMessage(hwndDlg, IDC_BLINKSPIN, UDM_SETBUDDY, (WPARAM) GetDlgItem(hwndDlg, IDC_BLINKTIME), 0); // set buddy
+ SendDlgItemMessage(hwndDlg, IDC_BLINKSPIN, UDM_SETRANGE, 0, MAKELONG(0x3FFF, 250));
+ SendDlgItemMessage(hwndDlg, IDC_BLINKSPIN, UDM_SETPOS, 0, MAKELONG(DBGetContactSettingWord(NULL, "CList", "IconFlashTime", 550), 0));
+ return TRUE;
+ case WM_COMMAND:
+ if (LOWORD(wParam) == IDC_AUTOHIDE) {
+ EnableWindow(GetDlgItem(hwndDlg, IDC_HIDETIME), IsDlgButtonChecked(hwndDlg, IDC_AUTOHIDE));
+ EnableWindow(GetDlgItem(hwndDlg, IDC_HIDETIMESPIN), IsDlgButtonChecked(hwndDlg, IDC_AUTOHIDE));
+ }
+ if (LOWORD(wParam) == IDC_DONTCYCLE || LOWORD(wParam) == IDC_CYCLE || LOWORD(wParam) == IDC_MULTITRAY) {
+ EnableWindow(GetDlgItem(hwndDlg, IDC_PRIMARYSTATUS), IsDlgButtonChecked(hwndDlg, IDC_DONTCYCLE));
+ EnableWindow(GetDlgItem(hwndDlg, IDC_CYCLETIME), IsDlgButtonChecked(hwndDlg, IDC_CYCLE));
+ EnableWindow(GetDlgItem(hwndDlg, IDC_CYCLETIMESPIN), IsDlgButtonChecked(hwndDlg, IDC_CYCLE));
+ EnableWindow(GetDlgItem(hwndDlg, IDC_ALWAYSMULTI), IsDlgButtonChecked(hwndDlg, IDC_MULTITRAY));
+ }
+ if (LOWORD(wParam) == IDC_DISABLEBLINK) {
+ EnableWindow(GetDlgItem(hwndDlg, IDC_BLINKTIME), !IsDlgButtonChecked(hwndDlg, IDC_DISABLEBLINK));
+ EnableWindow(GetDlgItem(hwndDlg, IDC_BLINKSPIN), !IsDlgButtonChecked(hwndDlg, IDC_DISABLEBLINK));
+ EnableWindow(GetDlgItem(hwndDlg, IDC_STMSDELAY), !IsDlgButtonChecked(hwndDlg, IDC_DISABLEBLINK));
+ }
+ if ((LOWORD(wParam) == IDC_HIDETIME || LOWORD(wParam) == IDC_CYCLETIME) && HIWORD(wParam) != EN_CHANGE)
+ break;
+ if (LOWORD(wParam) == IDC_PRIMARYSTATUS && HIWORD(wParam) != CBN_SELCHANGE)
+ break;
+ if ((LOWORD(wParam) == IDC_HIDETIME || LOWORD(wParam) == IDC_CYCLETIME) && (HIWORD(wParam) != EN_CHANGE || (HWND) lParam != GetFocus()))
+ return 0;
+ if (LOWORD(wParam) == IDC_BLINKTIME && HIWORD(wParam) != EN_CHANGE || (HWND) lParam != GetFocus())
+ return 0; // dont make apply enabled during buddy set crap
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ break;
+ case WM_NOTIFY:
+ switch (((LPNMHDR) lParam)->idFrom) {
+ case 0:
+ switch (((LPNMHDR) lParam)->code) {
+ case PSN_APPLY:
+ DBWriteContactSettingByte(NULL, "CList", "HideOffline", (BYTE) IsDlgButtonChecked(hwndDlg, IDC_HIDEOFFLINE));
+ {
+ DWORD caps = CallService(MS_CLUI_GETCAPS, CLUICAPS_FLAGS1, 0);
+ if (caps & CLUIF_HIDEEMPTYGROUPS)
+ DBWriteContactSettingByte(NULL, "CList", "HideEmptyGroups",
+ (BYTE) IsDlgButtonChecked(hwndDlg, IDC_HIDEEMPTYGROUPS));
+ if (caps & CLUIF_DISABLEGROUPS)
+ DBWriteContactSettingByte(NULL, "CList", "UseGroups", (BYTE) ! IsDlgButtonChecked(hwndDlg, IDC_DISABLEGROUPS));
+ if (!(caps & CLUIF_HASONTOPOPTION)) {
+ DBWriteContactSettingByte(NULL, "CList", "OnTop", (BYTE) IsDlgButtonChecked(hwndDlg, IDC_ONTOP));
+ SetWindowPos( pcli->hwndContactList,
+ IsDlgButtonChecked(hwndDlg, IDC_ONTOP) ? HWND_TOPMOST : HWND_NOTOPMOST, 0, 0, 0, 0,
+ SWP_NOMOVE | SWP_NOSIZE);
+ }
+ if (!(caps & CLUIF_HASAUTOHIDEOPTION)) {
+ DBWriteContactSettingByte(NULL, "CList", "AutoHide", (BYTE) IsDlgButtonChecked(hwndDlg, IDC_AUTOHIDE));
+ DBWriteContactSettingWord(NULL, "CList", "HideTime",
+ (WORD) SendDlgItemMessage(hwndDlg, IDC_HIDETIMESPIN, UDM_GETPOS, 0, 0));
+ }
+ }
+ DBWriteContactSettingByte(NULL, "CList", "SortByStatus", (BYTE) IsDlgButtonChecked(hwndDlg, IDC_SORTBYSTATUS));
+ DBWriteContactSettingByte(NULL, "CList", "SortByProto", (BYTE) IsDlgButtonChecked(hwndDlg, IDC_SORTBYPROTO));
+ DBWriteContactSettingByte(NULL, "CList", "ConfirmDelete", (BYTE) IsDlgButtonChecked(hwndDlg, IDC_CONFIRMDELETE));
+ DBWriteContactSettingByte(NULL, "CList", "Tray1Click", (BYTE) IsDlgButtonChecked(hwndDlg, IDC_ONECLK));
+ DBWriteContactSettingByte(NULL, "CList", "AlwaysStatus", (BYTE) IsDlgButtonChecked(hwndDlg, IDC_ALWAYSSTATUS));
+ DBWriteContactSettingByte(NULL, "CList", "AlwaysMulti", (BYTE) ! IsDlgButtonChecked(hwndDlg, IDC_ALWAYSMULTI));
+ DBWriteContactSettingByte(NULL, "CList", "TrayIcon",
+ (BYTE) (IsDlgButtonChecked(hwndDlg, IDC_DONTCYCLE) ? SETTING_TRAYICON_SINGLE
+ : (IsDlgButtonChecked(hwndDlg, IDC_CYCLE) ? SETTING_TRAYICON_CYCLE :
+ SETTING_TRAYICON_MULTI)));
+ DBWriteContactSettingWord(NULL, "CList", "CycleTime",
+ (WORD) SendDlgItemMessage(hwndDlg, IDC_CYCLETIMESPIN, UDM_GETPOS, 0, 0));
+ DBWriteContactSettingWord(NULL, "CList", "IconFlashTime",
+ (WORD) SendDlgItemMessage(hwndDlg, IDC_BLINKSPIN, UDM_GETPOS, 0, 0));
+ DBWriteContactSettingByte(NULL, "CList", "DisableTrayFlash", (BYTE) IsDlgButtonChecked(hwndDlg, IDC_DISABLEBLINK));
+ {
+ int cur = SendDlgItemMessage(hwndDlg, IDC_PRIMARYSTATUS, CB_GETCURSEL, 0, 0);
+ PROTOACCOUNT* pa = ( PROTOACCOUNT* )SendDlgItemMessage(hwndDlg, IDC_PRIMARYSTATUS, CB_GETITEMDATA, cur, 0 );
+ if ( pa == NULL )
+ DBDeleteContactSetting(NULL, "CList", "PrimaryStatus");
+ else
+ DBWriteContactSettingString(NULL, "CList", "PrimaryStatus", pa->szModuleName );
+ }
+
+ pcli->pfnTrayIconIconsChanged();
+ pcli->pfnLoadContactTree(); /* this won't do job properly since it only really works when changes happen */
+ pcli->pfnInvalidateDisplayNameCacheEntry( INVALID_HANDLE_VALUE ); /* force reshuffle */
+ return TRUE;
+ }
+ break;
+ }
+ break;
+ }
+ return FALSE;
+}
+
+/****************************************************************************************/
+
+static UINT expertOnlyControls[] = { IDC_ALWAYSSTATUS };
+
+int CListOptInit(WPARAM wParam, LPARAM lParam)
+{
+ OPTIONSDIALOGPAGE odp;
+
+ ZeroMemory(&odp, sizeof(odp));
+ odp.cbSize = sizeof(odp);
+ odp.position = -1000000000;
+ odp.hInstance = g_hInst;
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT_CLIST);
+ odp.pszTitle = LPGEN("Contact List");
+ odp.pfnDlgProc = DlgProcGenOpts;
+ odp.flags = ODPF_BOLDGROUPS;
+ odp.nIDBottomSimpleControl = IDC_STCLISTGROUP;
+ odp.expertOnlyControls = expertOnlyControls;
+ odp.nExpertOnlyControls = SIZEOF(expertOnlyControls);
+ CallService(MS_OPT_ADDPAGE, wParam, (LPARAM) & odp);
+ return 0;
+}
diff --git a/Plugins/bclist/cluiopts.c b/Plugins/bclist/cluiopts.c
new file mode 100644
index 0000000..e32708c
--- /dev/null
+++ b/Plugins/bclist/cluiopts.c
@@ -0,0 +1,346 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2003 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include "commonheaders.h"
+
+extern BOOL(WINAPI * MySetLayeredWindowAttributes) (HWND, COLORREF, BYTE, DWORD);
+
+static INT_PTR CALLBACK DlgProcCluiOpts(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg) {
+ case WM_INITDIALOG:
+ TranslateDialogDefault(hwndDlg);
+ CheckDlgButton(hwndDlg, IDC_BRINGTOFRONT,
+ DBGetContactSettingByte(NULL, "CList", "BringToFront", SETTING_BRINGTOFRONT_DEFAULT) ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_ONTOP, DBGetContactSettingByte(NULL, "CList", "OnTop", SETTING_ONTOP_DEFAULT) ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_TOOLWND,
+ DBGetContactSettingByte(NULL, "CList", "ToolWindow", SETTING_TOOLWINDOW_DEFAULT) ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_MIN2TRAY,
+ DBGetContactSettingByte(NULL, "CList", "Min2Tray", SETTING_MIN2TRAY_DEFAULT) ? BST_CHECKED : BST_UNCHECKED);
+ if (IsDlgButtonChecked(hwndDlg, IDC_TOOLWND))
+ EnableWindow(GetDlgItem(hwndDlg, IDC_MIN2TRAY), FALSE);
+ CheckDlgButton(hwndDlg, IDC_SHOWCAPTION,
+ DBGetContactSettingByte(NULL, "CLUI", "ShowCaption", SETTING_SHOWCAPTION_DEFAULT) ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_SHOWMAINMENU,
+ DBGetContactSettingByte(NULL, "CLUI", "ShowMainMenu", SETTING_SHOWMAINMENU_DEFAULT) ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_CLIENTDRAG,
+ DBGetContactSettingByte(NULL, "CLUI", "ClientAreaDrag", SETTING_CLIENTDRAG_DEFAULT) ? BST_CHECKED : BST_UNCHECKED);
+ if (!IsDlgButtonChecked(hwndDlg, IDC_SHOWCAPTION)) {
+ EnableWindow(GetDlgItem(hwndDlg, IDC_MIN2TRAY), FALSE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_TOOLWND), FALSE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_TITLETEXT), FALSE);
+ }
+ CheckDlgButton(hwndDlg, IDC_FADEINOUT, DBGetContactSettingByte(NULL, "CLUI", "FadeInOut", 0) ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_AUTOSIZE, DBGetContactSettingByte(NULL, "CLUI", "AutoSize", 0) ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_DROPSHADOW, DBGetContactSettingByte(NULL, "CList", "WindowShadow", 0) ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_ONDESKTOP, DBGetContactSettingByte(NULL, "CList", "OnDesktop", 0) ? BST_CHECKED : BST_UNCHECKED);
+ SendDlgItemMessage(hwndDlg, IDC_MAXSIZESPIN, UDM_SETRANGE, 0, MAKELONG(100, 0));
+ SendDlgItemMessage(hwndDlg, IDC_MAXSIZESPIN, UDM_SETPOS, 0, DBGetContactSettingByte(NULL, "CLUI", "MaxSizeHeight", 75));
+ CheckDlgButton(hwndDlg, IDC_AUTOSIZEUPWARD, DBGetContactSettingByte(NULL, "CLUI", "AutoSizeUpward", 0) ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_AUTOHIDE,
+ DBGetContactSettingByte(NULL, "CList", "AutoHide", SETTING_AUTOHIDE_DEFAULT) ? BST_CHECKED : BST_UNCHECKED);
+ SendDlgItemMessage(hwndDlg, IDC_HIDETIMESPIN, UDM_SETRANGE, 0, MAKELONG(900, 1));
+ SendDlgItemMessage(hwndDlg, IDC_HIDETIMESPIN, UDM_SETPOS, 0,
+ MAKELONG(DBGetContactSettingWord(NULL, "CList", "HideTime", SETTING_HIDETIME_DEFAULT), 0));
+ EnableWindow(GetDlgItem(hwndDlg, IDC_HIDETIME), IsDlgButtonChecked(hwndDlg, IDC_AUTOHIDE));
+ EnableWindow(GetDlgItem(hwndDlg, IDC_HIDETIMESPIN), IsDlgButtonChecked(hwndDlg, IDC_AUTOHIDE));
+ EnableWindow(GetDlgItem(hwndDlg, IDC_STATIC01), IsDlgButtonChecked(hwndDlg, IDC_AUTOHIDE));
+ if (!IsDlgButtonChecked(hwndDlg, IDC_AUTOSIZE)) {
+ EnableWindow(GetDlgItem(hwndDlg, IDC_STATIC21), FALSE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_STATIC22), FALSE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_MAXSIZEHEIGHT), FALSE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_MAXSIZESPIN), FALSE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_AUTOSIZEUPWARD), FALSE);
+ }
+
+ { DBVARIANT dbv;
+ if ( !DBGetContactSettingTString(NULL, "CList", "TitleText", &dbv)) {
+ SetDlgItemText(hwndDlg, IDC_TITLETEXT, dbv.ptszVal);
+ DBFreeVariant( &dbv );
+ }
+ else SetDlgItemTextA(hwndDlg, IDC_TITLETEXT, MIRANDANAME);
+ }
+ if (!IsWinVer2000Plus()) {
+ EnableWindow(GetDlgItem(hwndDlg, IDC_FADEINOUT), FALSE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_TRANSPARENT), FALSE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_DROPSHADOW), FALSE);
+ }
+ else
+ CheckDlgButton(hwndDlg, IDC_TRANSPARENT,
+ DBGetContactSettingByte(NULL, "CList", "Transparent", SETTING_TRANSPARENT_DEFAULT) ? BST_CHECKED : BST_UNCHECKED);
+
+ if (!IsDlgButtonChecked(hwndDlg, IDC_TRANSPARENT)) {
+ EnableWindow(GetDlgItem(hwndDlg, IDC_STATIC11), FALSE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_STATIC12), FALSE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_TRANSACTIVE), FALSE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_TRANSINACTIVE), FALSE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_ACTIVEPERC), FALSE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_INACTIVEPERC), FALSE);
+ }
+ SendDlgItemMessage(hwndDlg, IDC_TRANSACTIVE, TBM_SETRANGE, FALSE, MAKELONG(1, 255));
+ SendDlgItemMessage(hwndDlg, IDC_TRANSINACTIVE, TBM_SETRANGE, FALSE, MAKELONG(1, 255));
+ SendDlgItemMessage(hwndDlg, IDC_TRANSACTIVE, TBM_SETPOS, TRUE, DBGetContactSettingByte(NULL, "CList", "Alpha", SETTING_ALPHA_DEFAULT));
+ SendDlgItemMessage(hwndDlg, IDC_TRANSINACTIVE, TBM_SETPOS, TRUE,
+ DBGetContactSettingByte(NULL, "CList", "AutoAlpha", SETTING_AUTOALPHA_DEFAULT));
+ SendMessage(hwndDlg, WM_HSCROLL, 0x12345678, 0);
+ return TRUE;
+
+ case WM_COMMAND:
+ if (LOWORD(wParam) == IDC_AUTOHIDE) {
+ EnableWindow(GetDlgItem(hwndDlg, IDC_HIDETIME), IsDlgButtonChecked(hwndDlg, IDC_AUTOHIDE));
+ EnableWindow(GetDlgItem(hwndDlg, IDC_HIDETIMESPIN), IsDlgButtonChecked(hwndDlg, IDC_AUTOHIDE));
+ EnableWindow(GetDlgItem(hwndDlg, IDC_STATIC01), IsDlgButtonChecked(hwndDlg, IDC_AUTOHIDE));
+ }
+ else if (LOWORD(wParam) == IDC_TRANSPARENT) {
+ EnableWindow(GetDlgItem(hwndDlg, IDC_STATIC11), IsDlgButtonChecked(hwndDlg, IDC_TRANSPARENT));
+ EnableWindow(GetDlgItem(hwndDlg, IDC_STATIC12), IsDlgButtonChecked(hwndDlg, IDC_TRANSPARENT));
+ EnableWindow(GetDlgItem(hwndDlg, IDC_TRANSACTIVE), IsDlgButtonChecked(hwndDlg, IDC_TRANSPARENT));
+ EnableWindow(GetDlgItem(hwndDlg, IDC_TRANSINACTIVE), IsDlgButtonChecked(hwndDlg, IDC_TRANSPARENT));
+ EnableWindow(GetDlgItem(hwndDlg, IDC_ACTIVEPERC), IsDlgButtonChecked(hwndDlg, IDC_TRANSPARENT));
+ EnableWindow(GetDlgItem(hwndDlg, IDC_INACTIVEPERC), IsDlgButtonChecked(hwndDlg, IDC_TRANSPARENT));
+ }
+ else if (LOWORD(wParam) == IDC_AUTOSIZE) {
+ EnableWindow(GetDlgItem(hwndDlg, IDC_STATIC21), IsDlgButtonChecked(hwndDlg, IDC_AUTOSIZE));
+ EnableWindow(GetDlgItem(hwndDlg, IDC_STATIC22), IsDlgButtonChecked(hwndDlg, IDC_AUTOSIZE));
+ EnableWindow(GetDlgItem(hwndDlg, IDC_MAXSIZEHEIGHT), IsDlgButtonChecked(hwndDlg, IDC_AUTOSIZE));
+ EnableWindow(GetDlgItem(hwndDlg, IDC_MAXSIZESPIN), IsDlgButtonChecked(hwndDlg, IDC_AUTOSIZE));
+ EnableWindow(GetDlgItem(hwndDlg, IDC_AUTOSIZEUPWARD), IsDlgButtonChecked(hwndDlg, IDC_AUTOSIZE));
+ }
+ else if (LOWORD(wParam) == IDC_TOOLWND) {
+ EnableWindow(GetDlgItem(hwndDlg, IDC_MIN2TRAY), !IsDlgButtonChecked(hwndDlg, IDC_TOOLWND));
+ }
+ else if (LOWORD(wParam) == IDC_SHOWCAPTION) {
+ EnableWindow(GetDlgItem(hwndDlg, IDC_TOOLWND), IsDlgButtonChecked(hwndDlg, IDC_SHOWCAPTION));
+ EnableWindow(GetDlgItem(hwndDlg, IDC_MIN2TRAY), !IsDlgButtonChecked(hwndDlg, IDC_TOOLWND)
+ && IsDlgButtonChecked(hwndDlg, IDC_SHOWCAPTION));
+ EnableWindow(GetDlgItem(hwndDlg, IDC_TITLETEXT), IsDlgButtonChecked(hwndDlg, IDC_SHOWCAPTION));
+ }
+
+ if ((LOWORD(wParam) == IDC_HIDETIME || LOWORD(wParam) == IDC_TITLETEXT || LOWORD(wParam) == IDC_MAXSIZEHEIGHT) &&
+ (HIWORD(wParam) != EN_CHANGE || (HWND) lParam != GetFocus()))
+ return 0;
+
+ // Enable apply button
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ break;
+
+ case WM_HSCROLL:
+ {
+ char str[10];
+ wsprintfA(str, "%d%%", 100 * SendDlgItemMessage(hwndDlg, IDC_TRANSINACTIVE, TBM_GETPOS, 0, 0) / 255);
+ SetDlgItemTextA(hwndDlg, IDC_INACTIVEPERC, str);
+ wsprintfA(str, "%d%%", 100 * SendDlgItemMessage(hwndDlg, IDC_TRANSACTIVE, TBM_GETPOS, 0, 0) / 255);
+ SetDlgItemTextA(hwndDlg, IDC_ACTIVEPERC, str);
+ }
+ if (wParam != 0x12345678)
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ break;
+ case WM_NOTIFY:
+ if (((LPNMHDR) lParam)->code == PSN_APPLY) {
+ DBWriteContactSettingByte(NULL, "CList", "OnTop", (BYTE) IsDlgButtonChecked(hwndDlg, IDC_ONTOP));
+ DBWriteContactSettingByte(NULL, "CList", "ToolWindow", (BYTE) IsDlgButtonChecked(hwndDlg, IDC_TOOLWND));
+ DBWriteContactSettingByte(NULL, "CList", "BringToFront", (BYTE) IsDlgButtonChecked(hwndDlg, IDC_BRINGTOFRONT));
+ DBWriteContactSettingByte(NULL, "CLUI", "FadeInOut", (BYTE) IsDlgButtonChecked(hwndDlg, IDC_FADEINOUT));
+ DBWriteContactSettingByte(NULL, "CLUI", "AutoSize", (BYTE) IsDlgButtonChecked(hwndDlg, IDC_AUTOSIZE));
+ DBWriteContactSettingByte(NULL, "CLUI", "MaxSizeHeight", (BYTE) GetDlgItemInt(hwndDlg, IDC_MAXSIZEHEIGHT, NULL, FALSE));
+ DBWriteContactSettingByte(NULL, "CLUI", "AutoSizeUpward", (BYTE) IsDlgButtonChecked(hwndDlg, IDC_AUTOSIZEUPWARD));
+ DBWriteContactSettingByte(NULL, "CList", "AutoHide", (BYTE) IsDlgButtonChecked(hwndDlg, IDC_AUTOHIDE));
+ DBWriteContactSettingWord(NULL, "CList", "HideTime", (WORD) SendDlgItemMessage(hwndDlg, IDC_HIDETIMESPIN, UDM_GETPOS, 0, 0));
+ DBWriteContactSettingByte(NULL, "CList", "Transparent", (BYTE) IsDlgButtonChecked(hwndDlg, IDC_TRANSPARENT));
+ DBWriteContactSettingByte(NULL, "CList", "Alpha", (BYTE) SendDlgItemMessage(hwndDlg, IDC_TRANSACTIVE, TBM_GETPOS, 0, 0));
+ DBWriteContactSettingByte(NULL, "CList", "AutoAlpha", (BYTE) SendDlgItemMessage(hwndDlg, IDC_TRANSINACTIVE, TBM_GETPOS, 0, 0));
+ DBWriteContactSettingByte(NULL, "CList", "WindowShadow", (BYTE) IsDlgButtonChecked(hwndDlg, IDC_DROPSHADOW));
+ DBWriteContactSettingByte(NULL, "CList", "OnDesktop", (BYTE) IsDlgButtonChecked(hwndDlg, IDC_ONDESKTOP));
+ DBWriteContactSettingByte(NULL, "CLUI", "ShowCaption", (BYTE) IsDlgButtonChecked(hwndDlg, IDC_SHOWCAPTION));
+ DBWriteContactSettingByte(NULL, "CLUI", "ShowMainMenu", (BYTE) IsDlgButtonChecked(hwndDlg, IDC_SHOWMAINMENU));
+ DBWriteContactSettingByte(NULL, "CLUI", "ClientAreaDrag", (BYTE) IsDlgButtonChecked(hwndDlg, IDC_CLIENTDRAG));
+ DBWriteContactSettingByte(NULL, "CList", "Min2Tray", (BYTE) IsDlgButtonChecked(hwndDlg, IDC_MIN2TRAY));
+ {
+ TCHAR title[256];
+ GetDlgItemText(hwndDlg, IDC_TITLETEXT, title, SIZEOF(title));
+ DBWriteContactSettingTString(NULL, "CList", "TitleText", title);
+ SetWindowText(pcli->hwndContactList, title);
+ }
+ pcli->pfnLoadCluiGlobalOpts();
+ SetWindowPos(pcli->hwndContactList, IsDlgButtonChecked(hwndDlg, IDC_ONTOP) ? HWND_TOPMOST : HWND_NOTOPMOST, 0, 0, 0, 0,
+ SWP_NOMOVE | SWP_NOSIZE);
+ if (IsDlgButtonChecked(hwndDlg, IDC_TOOLWND)) {
+ // Window must be hidden to dynamically remove the taskbar button.
+ // See http://msdn.microsoft.com/library/en-us/shellcc/platform/shell/programmersguide/shell_int/shell_int_programming/taskbar.asp
+ WINDOWPLACEMENT p;
+ p.length = sizeof(p);
+ GetWindowPlacement(pcli->hwndContactList, &p);
+ ShowWindow(pcli->hwndContactList, SW_HIDE);
+ SetWindowLong(pcli->hwndContactList, GWL_EXSTYLE,
+ GetWindowLong(pcli->hwndContactList, GWL_EXSTYLE) | WS_EX_TOOLWINDOW | WS_EX_WINDOWEDGE);
+ SetWindowPlacement(pcli->hwndContactList, &p);
+ }
+ else SetWindowLong(pcli->hwndContactList, GWL_EXSTYLE, GetWindowLong(pcli->hwndContactList, GWL_EXSTYLE) & ~WS_EX_TOOLWINDOW);
+
+ if (IsDlgButtonChecked(hwndDlg, IDC_ONDESKTOP)) {
+ HWND hProgMan = FindWindowA("Progman", NULL);
+ if (IsWindow(hProgMan))
+ SetParent(pcli->hwndContactList, hProgMan);
+ }
+ else SetParent(pcli->hwndContactList, NULL);
+
+ if (IsDlgButtonChecked(hwndDlg, IDC_SHOWCAPTION))
+ SetWindowLong(pcli->hwndContactList, GWL_STYLE,
+ GetWindowLong(pcli->hwndContactList, GWL_STYLE) | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX);
+ else
+ SetWindowLong(pcli->hwndContactList, GWL_STYLE,
+ GetWindowLong(pcli->hwndContactList, GWL_STYLE) & ~(WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX));
+ if (!IsDlgButtonChecked(hwndDlg, IDC_SHOWMAINMENU))
+ SetMenu(pcli->hwndContactList, NULL);
+ else
+ SetMenu(pcli->hwndContactList, pcli->hMenuMain);
+ SetWindowPos(pcli->hwndContactList, 0, 0, 0, 0, 0, SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED);
+ RedrawWindow(pcli->hwndContactList, NULL, NULL, RDW_FRAME | RDW_INVALIDATE);
+ if (IsIconic(pcli->hwndContactList) && !IsDlgButtonChecked(hwndDlg, IDC_TOOLWND))
+ ShowWindow(pcli->hwndContactList, IsDlgButtonChecked(hwndDlg, IDC_MIN2TRAY) ? SW_HIDE : SW_SHOW);
+ if (IsDlgButtonChecked(hwndDlg, IDC_TRANSPARENT)) {
+ SetWindowLong(pcli->hwndContactList, GWL_EXSTYLE, GetWindowLong(pcli->hwndContactList, GWL_EXSTYLE) | WS_EX_LAYERED);
+ if (MySetLayeredWindowAttributes)
+ MySetLayeredWindowAttributes(pcli->hwndContactList, RGB(0, 0, 0),
+ (BYTE) DBGetContactSettingByte(NULL, "CList", "AutoAlpha", SETTING_AUTOALPHA_DEFAULT),
+ LWA_ALPHA);
+ }
+ else SetWindowLong(pcli->hwndContactList, GWL_EXSTYLE, GetWindowLong(pcli->hwndContactList, GWL_EXSTYLE) & ~WS_EX_LAYERED);
+
+ SendMessage(pcli->hwndContactTree, WM_SIZE, 0, 0); //forces it to send a cln_listsizechanged
+ return TRUE;
+ }
+ break;
+ }
+ return FALSE;
+}
+
+static INT_PTR CALLBACK DlgProcSBarOpts(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg) {
+ case WM_INITDIALOG:
+ TranslateDialogDefault(hwndDlg);
+ CheckDlgButton(hwndDlg, IDC_SHOWSBAR, DBGetContactSettingByte(NULL, "CLUI", "ShowSBar", 1) ? BST_CHECKED : BST_UNCHECKED);
+ {
+ BYTE showOpts = DBGetContactSettingByte(NULL, "CLUI", "SBarShow", 1);
+ CheckDlgButton(hwndDlg, IDC_SHOWICON, showOpts & 1 ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_SHOWPROTO, showOpts & 2 ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_SHOWSTATUS, showOpts & 4 ? BST_CHECKED : BST_UNCHECKED);
+ }
+ CheckDlgButton(hwndDlg, IDC_RIGHTSTATUS, DBGetContactSettingByte(NULL, "CLUI", "SBarRightClk", 0) ? BST_UNCHECKED : BST_CHECKED);
+ CheckDlgButton(hwndDlg, IDC_RIGHTMIRANDA, !IsDlgButtonChecked(hwndDlg, IDC_RIGHTSTATUS) ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_EQUALSECTIONS, DBGetContactSettingByte(NULL, "CLUI", "EqualSections", 0) ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_SBPANELBEVEL, DBGetContactSettingByte(NULL, "CLUI", "SBarBevel", 1) ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_SHOWGRIP, DBGetContactSettingByte(NULL, "CLUI", "ShowGrip", 1) ? BST_CHECKED : BST_UNCHECKED);
+ if (!IsDlgButtonChecked(hwndDlg, IDC_SHOWSBAR)) {
+ EnableWindow(GetDlgItem(hwndDlg, IDC_SHOWICON), FALSE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_SHOWPROTO), FALSE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_SHOWSTATUS), FALSE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_RIGHTSTATUS), FALSE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_RIGHTMIRANDA), FALSE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_EQUALSECTIONS), FALSE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_SBPANELBEVEL), FALSE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_SHOWGRIP), FALSE);
+ }
+ return TRUE;
+ case WM_COMMAND:
+ if (LOWORD(wParam) == IDC_SHOWSBAR) {
+ EnableWindow(GetDlgItem(hwndDlg, IDC_SHOWICON), IsDlgButtonChecked(hwndDlg, IDC_SHOWSBAR));
+ EnableWindow(GetDlgItem(hwndDlg, IDC_SHOWPROTO), IsDlgButtonChecked(hwndDlg, IDC_SHOWSBAR));
+ EnableWindow(GetDlgItem(hwndDlg, IDC_SHOWSTATUS), IsDlgButtonChecked(hwndDlg, IDC_SHOWSBAR));
+ EnableWindow(GetDlgItem(hwndDlg, IDC_RIGHTSTATUS), IsDlgButtonChecked(hwndDlg, IDC_SHOWSBAR));
+ EnableWindow(GetDlgItem(hwndDlg, IDC_RIGHTMIRANDA), IsDlgButtonChecked(hwndDlg, IDC_SHOWSBAR));
+ EnableWindow(GetDlgItem(hwndDlg, IDC_EQUALSECTIONS), IsDlgButtonChecked(hwndDlg, IDC_SHOWSBAR));
+ EnableWindow(GetDlgItem(hwndDlg, IDC_SBPANELBEVEL), IsDlgButtonChecked(hwndDlg, IDC_SHOWSBAR));
+ EnableWindow(GetDlgItem(hwndDlg, IDC_SHOWGRIP), IsDlgButtonChecked(hwndDlg, IDC_SHOWSBAR));
+ }
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ break;
+ case WM_NOTIFY:
+ if (((LPNMHDR) lParam)->code == PSN_APPLY ) {
+ DBWriteContactSettingByte(NULL, "CLUI", "ShowSBar", (BYTE) IsDlgButtonChecked(hwndDlg, IDC_SHOWSBAR));
+ DBWriteContactSettingByte(NULL, "CLUI", "SBarShow",
+ (BYTE) ((IsDlgButtonChecked(hwndDlg, IDC_SHOWICON) ? 1 : 0) |
+ (IsDlgButtonChecked(hwndDlg, IDC_SHOWPROTO) ? 2 : 0) |
+ (IsDlgButtonChecked(hwndDlg, IDC_SHOWSTATUS) ? 4 : 0)));
+ DBWriteContactSettingByte(NULL, "CLUI", "SBarRightClk", (BYTE) IsDlgButtonChecked(hwndDlg, IDC_RIGHTMIRANDA));
+ DBWriteContactSettingByte(NULL, "CLUI", "EqualSections", (BYTE) IsDlgButtonChecked(hwndDlg, IDC_EQUALSECTIONS));
+ DBWriteContactSettingByte(NULL, "CLUI", "SBarBevel", (BYTE) IsDlgButtonChecked(hwndDlg, IDC_SBPANELBEVEL));
+ pcli->pfnLoadCluiGlobalOpts();
+ if (DBGetContactSettingByte(NULL, "CLUI", "ShowGrip", 1) != (BYTE) IsDlgButtonChecked(hwndDlg, IDC_SHOWGRIP)) {
+ HWND parent = GetParent(pcli->hwndStatus);
+ int flags = WS_CHILD | CCS_BOTTOM;
+ DBWriteContactSettingByte(NULL, "CLUI", "ShowGrip", (BYTE) IsDlgButtonChecked(hwndDlg, IDC_SHOWGRIP));
+ ShowWindow(pcli->hwndStatus, SW_HIDE);
+ DestroyWindow(pcli->hwndStatus);
+ flags |= DBGetContactSettingByte(NULL, "CLUI", "ShowSBar", 1) ? WS_VISIBLE : 0;
+ flags |= DBGetContactSettingByte(NULL, "CLUI", "ShowGrip", 1) ? SBARS_SIZEGRIP : 0;
+ pcli->hwndStatus = CreateWindow(STATUSCLASSNAME, NULL, flags, 0, 0, 0, 0, parent, NULL, g_hInst, NULL);
+ }
+ if (IsDlgButtonChecked(hwndDlg, IDC_SHOWSBAR))
+ ShowWindow(pcli->hwndStatus, SW_SHOW);
+ else
+ ShowWindow(pcli->hwndStatus, SW_HIDE);
+ SendMessage(pcli->hwndContactList, WM_SIZE, 0, 0);
+ return TRUE;
+ }
+ break;
+ }
+ return FALSE;
+}
+
+/****************************************************************************************/
+
+static UINT expertOnlyControls[] =
+{
+ IDC_BRINGTOFRONT, IDC_AUTOSIZE, IDC_STATIC21, IDC_MAXSIZEHEIGHT, IDC_MAXSIZESPIN,
+ IDC_STATIC22, IDC_AUTOSIZEUPWARD, IDC_SHOWMAINMENU, IDC_SHOWCAPTION, IDC_CLIENTDRAG
+};
+
+int CluiOptInit(WPARAM wParam, LPARAM lParam)
+{
+ OPTIONSDIALOGPAGE odp;
+
+ ZeroMemory(&odp, sizeof(odp));
+ odp.cbSize = sizeof(odp);
+ odp.position = 0;
+ odp.hInstance = g_hInst;
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT_CLUI);
+ odp.pszTitle = LPGEN("Window");
+ odp.pszGroup = LPGEN("Contact List");
+ odp.pfnDlgProc = DlgProcCluiOpts;
+ odp.flags = ODPF_BOLDGROUPS;
+ odp.nIDBottomSimpleControl = IDC_STWINDOWGROUP;
+ odp.expertOnlyControls = expertOnlyControls;
+ odp.nExpertOnlyControls = SIZEOF(expertOnlyControls);
+ CallService(MS_OPT_ADDPAGE, wParam, (LPARAM) & odp);
+
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT_SBAR);
+ odp.pszTitle = LPGEN("Status Bar");
+ odp.pfnDlgProc = DlgProcSBarOpts;
+ odp.flags = ODPF_BOLDGROUPS | ODPF_EXPERTONLY;
+ odp.nIDBottomSimpleControl = 0;
+ odp.nExpertOnlyControls = 0;
+ odp.expertOnlyControls = NULL;
+ CallService(MS_OPT_ADDPAGE, wParam, (LPARAM) & odp);
+ return 0;
+}
diff --git a/Plugins/bclist/commonheaders.c b/Plugins/bclist/commonheaders.c
new file mode 100644
index 0000000..14f99f7
--- /dev/null
+++ b/Plugins/bclist/commonheaders.c
@@ -0,0 +1 @@
+#include "commonheaders.h"
diff --git a/Plugins/bclist/commonheaders.h b/Plugins/bclist/commonheaders.h
new file mode 100644
index 0000000..0a9d2c0
--- /dev/null
+++ b/Plugins/bclist/commonheaders.h
@@ -0,0 +1,78 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2005 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#define MIRANDA_VER 0x0800
+
+#define _WIN32_WINNT 0x0600
+#define _WIN32_IE 0x0501
+
+#include "m_stdhdr.h"
+
+#include <windows.h>
+#include <commctrl.h>
+#include <stdio.h>
+#include <time.h>
+#include <stddef.h>
+#include <process.h>
+#include <io.h>
+#include <string.h>
+#include <direct.h>
+#include "resource.h"
+#include <win2k.h>
+
+#include <newpluginapi.h>
+#include <m_clist.h>
+#include <m_clc.h>
+#include <m_clistint.h>
+#include <m_clui.h>
+#include <m_plugins.h>
+#include <m_system.h>
+#include <m_database.h>
+#include <m_langpack.h>
+#include <m_button.h>
+#include <m_options.h>
+#include <m_protocols.h>
+#include <m_protosvc.h>
+#include <m_utils.h>
+#include <m_skin.h>
+#include <m_contacts.h>
+#include <m_file.h>
+#include <m_addcontact.h>
+#include <m_utils.h>
+#include "m_updater.h"
+#include "clc.h"
+
+// shared vars
+extern HINSTANCE g_hInst;
+
+/* most free()'s are invalid when the code is executed from a dll, so this changes
+ all the bad free()'s to good ones, however it's still incorrect code. The reasons for not
+ changing them include:
+
+ * DBFreeVariant has a CallService() lookup
+ * free() is executed in some large loops to do with clist creation of group data
+ * easy search and replace
+
+*/
+
+#define MAX_REGS(_A_) ( sizeof(_A_) / sizeof(_A_[0]) )
diff --git a/Plugins/bclist/forkthread.c b/Plugins/bclist/forkthread.c
new file mode 100644
index 0000000..920fc1f
--- /dev/null
+++ b/Plugins/bclist/forkthread.c
@@ -0,0 +1,94 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2003 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "commonheaders.h"
+
+struct FORK_ARG
+{
+ HANDLE hEvent;
+ void (__cdecl * threadcode) (void *);
+ unsigned (__stdcall * threadcodeex) (void *);
+ void *arg;
+};
+
+void __cdecl forkthread_r(struct FORK_ARG *fa)
+{
+ void (*callercode) (void *) = fa->threadcode;
+ void *arg = fa->arg;
+ CallService(MS_SYSTEM_THREAD_PUSH, 0, 0);
+ SetEvent(fa->hEvent);
+ __try {
+ callercode(arg);
+ }
+ __finally {
+ CallService(MS_SYSTEM_THREAD_POP, 0, 0);
+ }
+ return;
+}
+
+unsigned long forkthread(void (__cdecl * threadcode) (void *), unsigned long stacksize, void *arg)
+{
+ unsigned long rc;
+ struct FORK_ARG fa;
+ fa.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+ fa.threadcode = threadcode;
+ fa.arg = arg;
+ rc = _beginthread(forkthread_r, stacksize, &fa);
+ if ((unsigned long) -1L != rc) {
+ WaitForSingleObject(fa.hEvent, INFINITE);
+ } //if
+ CloseHandle(fa.hEvent);
+ return rc;
+}
+
+unsigned long __stdcall forkthreadex_r(struct FORK_ARG *fa)
+{
+ unsigned (__stdcall * threadcode) (void *) = fa->threadcodeex;
+ void *arg = fa->arg;
+ unsigned long rc;
+
+ CallService(MS_SYSTEM_THREAD_PUSH, 0, 0);
+ SetEvent(fa->hEvent);
+ __try {
+ rc = threadcode(arg);
+ }
+ __finally {
+ CallService(MS_SYSTEM_THREAD_POP, 0, 0);
+ }
+ return rc;
+}
+
+unsigned long forkthreadex(void *sec, unsigned stacksize, unsigned (__stdcall * threadcode) (void *), void *arg, unsigned cf, unsigned *thraddr)
+{
+ unsigned long rc;
+ struct FORK_ARG fa;
+ fa.threadcodeex = threadcode;
+ fa.arg = arg;
+ fa.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+ rc = _beginthreadex(sec, stacksize, forkthreadex_r, &fa, 0, thraddr);
+ if (rc) {
+ WaitForSingleObject(fa.hEvent, INFINITE);
+ }
+ CloseHandle(fa.hEvent);
+ return rc;
+}
diff --git a/Plugins/bclist/forkthread.h b/Plugins/bclist/forkthread.h
new file mode 100644
index 0000000..9abcf5e
--- /dev/null
+++ b/Plugins/bclist/forkthread.h
@@ -0,0 +1,63 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2003 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+/*
+
+Purpose:
+
+ A safe version of _beginthread()
+
+Description:
+
+ A new thread is created and the source thread is paused until
+ internal code to call MS_SYSTEM_THREAD_PUSH is made in the context
+ if the new thread.
+
+ The source thread is then released and then the user supplied
+ code is called, when that function returns -- MS_SYSTEM_THREAD_POP
+ is called and then the thread returns.
+
+ This insures that Miranda will not exit whilst new threads
+ are trying to be born; and the unwind wait stack will unsure
+ that Miranda will wait for all created threads to return as well.
+
+Cavets:
+
+ The function must be reimplemented across MT plugins, since thread
+ creation depends on CRT which can not be shared.
+
+*/
+unsigned long forkthread (
+ void (__cdecl *threadcode)(void*),
+ unsigned long stacksize,
+ void *arg
+);
+
+unsigned long forkthreadex(
+ void *sec,
+ unsigned stacksize,
+ unsigned (__stdcall *threadcode)(void*),
+ void *arg,
+ unsigned cf,
+ unsigned *thraddr
+);
diff --git a/Plugins/bclist/init.c b/Plugins/bclist/init.c
new file mode 100644
index 0000000..2f1aaa0
--- /dev/null
+++ b/Plugins/bclist/init.c
@@ -0,0 +1,833 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2005 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "commonheaders.h"
+#include <m_icolib.h>
+
+HINSTANCE g_hInst = 0;
+PLUGINLINK *pluginLink;
+CLIST_INTERFACE* pcli = NULL;
+HIMAGELIST himlCListClc = NULL;
+
+struct MM_INTERFACE mmi;
+BOOL(WINAPI * MySetLayeredWindowAttributes) (HWND, COLORREF, BYTE, DWORD) = NULL;
+
+LRESULT CALLBACK ContactListWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
+LRESULT CALLBACK ContactListControlWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
+void RebuildEntireList(HWND hwnd, struct ClcData *dat);
+void RebuildEntireListInternal(HWND hwnd, struct ClcData *dat, BOOL call_orig);
+void SetGroupExpand(HWND hwnd, struct ClcData *dat, struct ClcGroup *group, int newState);
+void ScrollTo( HWND hwnd, struct ClcData *dat, int desty, int noSmooth );
+void RecalcScrollBar( HWND hwnd, struct ClcData *dat );
+void LoadClcOptions( HWND hwnd, struct ClcData *dat );
+int GetRowHeight(struct ClcData *dat, int item);
+void SortCLC(HWND hwnd, struct ClcData *dat, int useInsertionSort);
+
+LRESULT ( CALLBACK *pfnContactListWndProc )( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam );
+LRESULT ( CALLBACK *pfnContactListControlWndProc )( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam );
+void ( *pfnRebuildEntireList )( HWND hwnd, struct ClcData *dat );
+void ( *pfnSetGroupExpand )(HWND hwnd, struct ClcData *dat, struct ClcGroup *group, int newState);
+void ( *pfnScrollTo )( HWND hwnd, struct ClcData *dat, int desty, int noSmooth );
+void ( *pfnRecalcScrollBar )( HWND hwnd, struct ClcData *dat );
+void ( *pfnLoadClcOptions )( HWND hwnd, struct ClcData *dat );
+int ( *pfnGetRowHeight )(struct ClcData *dat, int item);
+void ( *pfnSortCLC )( HWND hwnd, struct ClcData *dat, int useInsertionSort );
+
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// external functions
+
+void InitCustomMenus( void );
+void PaintClc(HWND hwnd, struct ClcData *dat, HDC hdc, RECT * rcPaint);
+
+int ClcOptInit(WPARAM wParam, LPARAM lParam);
+int CluiOptInit(WPARAM wParam, LPARAM lParam);
+int CListOptInit(WPARAM wParam, LPARAM lParam);
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// dll stub
+
+BOOL WINAPI DllMain(HINSTANCE hInstDLL, DWORD dwReason, LPVOID reserved)
+{
+ g_hInst = hInstDLL;
+ DisableThreadLibraryCalls(g_hInst);
+ return TRUE;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// returns the plugin information
+
+PLUGININFOEX pluginInfo = {
+ sizeof(PLUGININFO),
+ #if defined( _UNICODE )
+ "BClist (Unicode)",
+ #else
+ "BClist",
+ #endif
+ PLUGIN_MAKE_VERSION(0, 8, 0, 1),
+ "A contact list for blind folks",
+ "Ricardo Pescuma Domenecci, based on previous work from Miranda IM project",
+ "",
+ "Copyright 2000-2009 Miranda IM project, Ricardo Pescuma Domenecci",
+ "http://pescuma.org/miranda/bclist",
+ UNICODE_AWARE,
+ DEFMOD_CLISTALL,
+ #if defined( _UNICODE )
+ { 0x53e095a3, 0x2695, 0x490a, { 0x9d, 0xad, 0xd2, 0x4, 0x79, 0x9, 0x38, 0x31 } } // {53E095A3-2695-490a-9DAD-D20479093831}
+ #else
+ { 0x924dfbcc, 0x71df, 0x4f46, { 0x81, 0x6, 0x5a, 0xc4, 0x3, 0xca, 0xb2, 0x4b } } // {924DFBCC-71DF-4f46-8106-5AC403CAB24B}
+ #endif
+};
+
+__declspec(dllexport) PLUGININFO *MirandaPluginInfo(DWORD mirandaVersion)
+{
+ if (mirandaVersion < PLUGIN_MAKE_VERSION(0, 8, 0, 9))
+ return NULL;
+ pluginInfo.cbSize = sizeof(PLUGININFO);
+ return (PLUGININFO *) &pluginInfo;
+}
+
+__declspec(dllexport) PLUGININFOEX *MirandaPluginInfoEx(DWORD mirandaVersion)
+{
+ if (mirandaVersion < PLUGIN_MAKE_VERSION(0, 8, 0, 9))
+ return NULL;
+ pluginInfo.cbSize = sizeof(PLUGININFOEX);
+ return &pluginInfo;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// returns plugin's interfaces information
+
+static const MUUID interfaces[] = {MIID_CLIST, MIID_LAST};
+__declspec(dllexport) const MUUID * MirandaPluginInterfaces(void)
+{
+ return interfaces;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// called when number of accounts has been changed
+
+static int OnAccountsChanged( WPARAM wParam, LPARAM lParam )
+{
+ himlCListClc = (HIMAGELIST) CallService(MS_CLIST_GETICONSIMAGELIST, 0, 0);
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// called when all modules got loaded
+
+static int OnModulesLoaded( WPARAM wParam, LPARAM lParam )
+{
+ himlCListClc = (HIMAGELIST) CallService(MS_CLIST_GETICONSIMAGELIST, 0, 0);
+
+ // updater plugin support
+ if(ServiceExists(MS_UPDATE_REGISTER))
+ {
+ Update upd = {0};
+ char szCurrentVersion[30];
+
+ upd.cbSize = sizeof(upd);
+ upd.szComponentName = pluginInfo.shortName;
+
+ upd.szUpdateURL = UPDATER_AUTOREGISTER;
+
+ upd.szBetaVersionURL = "http://pescuma.org/miranda/bclist_version.txt";
+ upd.szBetaChangelogURL = "http://pescuma.org/miranda/?p=bclist#Changelog";
+ upd.pbBetaVersionPrefix = (BYTE *)"BClist ";
+ upd.cpbBetaVersionPrefix = strlen((char *)upd.pbBetaVersionPrefix);
+#ifdef UNICODE
+ upd.szBetaUpdateURL = "http://pescuma.org/miranda/bclistW.zip";
+#else
+ upd.szBetaUpdateURL = "http://pescuma.org/miranda/bclist.zip";
+#endif
+
+ upd.pbVersion = (BYTE *)CreateVersionStringPlugin((PLUGININFO *) &pluginInfo, szCurrentVersion);
+ upd.cpbVersion = strlen((char *)upd.pbVersion);
+
+ CallService(MS_UPDATE_REGISTER, 0, (LPARAM)&upd);
+ }
+
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// options iniatialization
+
+static int OnOptsInit(WPARAM wParam, LPARAM lParam)
+{
+ ClcOptInit(wParam, lParam);
+ CluiOptInit(wParam, lParam);
+ CListOptInit(wParam, lParam);
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// menu status services
+
+static INT_PTR GetStatusMode(WPARAM wParam, LPARAM lParam)
+{
+ return pcli->currentDesiredStatusMode;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// main clist initialization routine
+
+int __declspec(dllexport) CListInitialise(PLUGINLINK * link)
+{
+ pluginLink = link;
+ #ifdef _DEBUG
+ _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
+ #endif
+
+ // get the internal malloc/free()
+ mir_getMMI( &mmi );
+
+ pcli = ( CLIST_INTERFACE* )CallService(MS_CLIST_RETRIEVE_INTERFACE, 0, (LPARAM)g_hInst);
+ if ( (INT_PTR)pcli == CALLSERVICE_NOTFOUND ) {
+LBL_Error:
+ MessageBoxA( NULL, "This version of plugin requires Miranda IM 0.8.0.9 or later", "Fatal error", MB_OK );
+ return 1;
+ }
+ if ( pcli->version < 6 )
+ goto LBL_Error;
+
+
+#define CLIST_SWAP(a) pfn##a = pcli->pfn##a; pcli->pfn##a = a
+
+ CLIST_SWAP(ContactListWndProc);
+ CLIST_SWAP(ContactListControlWndProc);
+ CLIST_SWAP(RebuildEntireList);
+ CLIST_SWAP(SetGroupExpand);
+ CLIST_SWAP(RecalcScrollBar);
+ CLIST_SWAP(ScrollTo);
+ CLIST_SWAP(LoadClcOptions);
+ CLIST_SWAP(GetRowHeight);
+ CLIST_SWAP(SortCLC);
+
+ pcli->pfnPaintClc = PaintClc;
+
+ MySetLayeredWindowAttributes = (BOOL(WINAPI *) (HWND, COLORREF, BYTE, DWORD)) GetProcAddress(
+ LoadLibraryA("user32.dll"), "SetLayeredWindowAttributes");
+
+ CreateServiceFunction(MS_CLIST_GETSTATUSMODE, GetStatusMode);
+
+ HookEvent(ME_SYSTEM_MODULESLOADED, OnModulesLoaded);
+ HookEvent(ME_PROTO_ACCLISTCHANGED, OnAccountsChanged);
+ HookEvent(ME_OPT_INITIALISE, OnOptsInit);
+
+ InitCustomMenus();
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// a plugin loader aware of CList exports will never call this.
+
+int __declspec(dllexport) Load(PLUGINLINK * link)
+{
+ return 1;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// a plugin unloader
+
+int __declspec(dllexport) Unload(void)
+{
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+TCHAR* MyDBGetContactSettingTString(HANDLE hContact, char* module, char* setting, TCHAR* out, size_t len, TCHAR *def)
+{
+ DBVARIANT dbv;
+
+ out[0] = _T('\0');
+
+ if (!DBGetContactSettingTString(hContact, module, setting, &dbv))
+ {
+#ifdef UNICODE
+ if (dbv.type == DBVT_ASCIIZ)
+ {
+ MultiByteToWideChar(CP_ACP, 0, dbv.pszVal, -1, out, len);
+ }
+ else if (dbv.type == DBVT_UTF8)
+ {
+ MultiByteToWideChar(CP_UTF8, 0, dbv.pszVal, -1, out, len);
+ }
+ else if (dbv.type == DBVT_WCHAR)
+ {
+ lstrcpyn(out, dbv.pwszVal, len);
+ }
+#else
+ if (dbv.type == DBVT_ASCIIZ)
+ {
+ lstrcpyn(out, dbv.pszVal, len);
+ }
+#endif
+ else
+ {
+ if (def != NULL)
+ lstrcpyn(out, def, len);
+ }
+
+ DBFreeVariant(&dbv);
+ }
+ else
+ {
+ if (def != NULL)
+ lstrcpyn(out, def, len);
+ }
+
+ return out;
+}
+
+#define DATA_BLOCK 128
+
+typedef struct
+{
+ TCHAR *text;
+ size_t allocated;
+ size_t used;
+
+} StringHelper;
+
+int CopyData(StringHelper *str, const TCHAR *text, size_t len)
+{
+ size_t totalSize;
+
+ if (len == 0)
+ return 0;
+
+ if (text == NULL)
+ return 0;
+
+ totalSize = str->used + len + 1;
+
+ if (totalSize > str->allocated)
+ {
+ totalSize += DATA_BLOCK - (totalSize % DATA_BLOCK);
+
+ if (str->text != NULL)
+ {
+ TCHAR *tmp = (TCHAR *) mir_realloc(str->text, sizeof(TCHAR) * totalSize);
+
+ if (tmp == NULL)
+ {
+ mir_free(str->text);
+ return -1;
+ }
+
+ str->text = tmp;
+ }
+ else
+ {
+ str->text = (TCHAR *) mir_alloc(sizeof(TCHAR) * totalSize);
+
+ if (str->text == NULL)
+ {
+ return -2;
+ }
+ }
+
+ str->allocated = totalSize;
+ }
+
+ memmove(&str->text[str->used], text, sizeof(TCHAR) * len);
+ str->used += len;
+ str->text[str->used] = '\0';
+
+ return 0;
+}
+
+
+TCHAR * ParseText(const TCHAR *text,
+ const TCHAR **variables, size_t variablesSize,
+ const TCHAR **data, size_t dataSize)
+{
+ size_t length = lstrlen(text);
+ size_t nextPos = 0;
+ StringHelper ret = {0};
+ size_t i;
+
+ // length - 1 because a % in last char will be a % and point
+ for (i = 0 ; i < length - 1 ; i++)
+ {
+ if (text[i] == _T('%'))
+ {
+ BOOL found = FALSE;
+
+ if (CopyData(&ret, &text[nextPos], i - nextPos))
+ return NULL;
+
+ if (text[i + 1] == _T('%'))
+ {
+ if (CopyData(&ret, _T("%"), 1))
+ return NULL;
+
+ i++;
+
+ found = TRUE;
+ }
+ else
+ {
+ size_t size = min(variablesSize, dataSize);
+ size_t j;
+
+ // See if can find it
+ for(j = 0 ; j < size ; j++)
+ {
+ size_t vlen = lstrlen(variables[j]);
+
+ if (_tcsnicmp(&text[i], variables[j], vlen) == 0)
+ {
+ if (CopyData(&ret, data[j], lstrlen(data[j])))
+ return NULL;
+
+ i += vlen - 1;
+
+ found = TRUE;
+
+ break;
+ }
+ }
+ }
+
+ if (found)
+ nextPos = i + 1;
+ else
+ nextPos = i;
+ }
+ }
+
+ if (nextPos < length)
+ if (CopyData(&ret, &text[nextPos], length - nextPos))
+ return NULL;
+
+ return ret.text;
+}
+
+LRESULT CALLBACK ContactListWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg) {
+ case WM_NCCREATE:
+ {
+ break;
+ }
+ case WM_CREATE:
+ {
+ break;
+ }
+ }
+ return pfnContactListWndProc(hwnd, msg, wParam, lParam);
+}
+
+struct MyClcData
+{
+ union {
+ struct ClcData;
+ };
+ HWND hwnd_list;
+ BOOL need_rebuild;
+};
+
+LRESULT CALLBACK ContactListControlWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ struct MyClcData *dat = (struct MyClcData *) GetWindowLong(hwnd, 0);
+
+ switch (msg) {
+ case WM_CREATE:
+ {
+ RECT r;
+ LRESULT ret = pfnContactListControlWndProc(hwnd, msg, wParam, lParam);
+
+ // Fix dat
+ struct ClcData *tmp = (struct ClcData *) GetWindowLong(hwnd, 0);
+ dat = (struct MyClcData *) mir_alloc(sizeof(struct MyClcData));
+ memmove(dat, tmp, sizeof(struct ClcData));
+ mir_free(tmp);
+ SetWindowLong(hwnd, 0, (LONG) dat);
+
+ dat->hwnd_list = CreateWindow(_T("LISTBOX"), _T(""),
+ (WS_VISIBLE | WS_CHILD | LBS_NOINTEGRALHEIGHT | LBS_NOTIFY | LBS_WANTKEYBOARDINPUT | WS_VSCROLL),
+ 0, 0, 0, 0, hwnd, NULL, g_hInst,0);
+ dat->need_rebuild = FALSE;
+
+ GetClientRect(hwnd, &r);
+ SetWindowPos(dat->hwnd_list, 0, r.left, r.top, r.right - r.left, r.bottom - r.top, SWP_NOZORDER | SWP_NOACTIVATE);
+
+ return ret;
+ }
+
+ case WM_SIZE:
+ {
+ RECT r;
+ GetClientRect(hwnd, &r);
+ SetWindowPos(dat->hwnd_list, 0, r.left, r.top, r.right - r.left, r.bottom - r.top, SWP_NOZORDER | SWP_NOACTIVATE);
+ break;
+ }
+
+ case WM_PRINTCLIENT:
+ case WM_PAINT:
+ if (dat->need_rebuild)
+ RebuildEntireListInternal(hwnd, (struct ClcData *) dat, FALSE);
+ // no break
+ case WM_VSCROLL:
+ case WM_MOUSEWHEEL:
+ case WM_KEYDOWN:
+ return DefWindowProc(hwnd, msg, wParam, lParam);
+
+ case INTM_SCROLLBARCHANGED:
+ return TRUE;
+
+ case WM_VKEYTOITEM:
+ {
+ int key = LOWORD(wParam);
+ if (key == VK_LEFT || key == VK_RIGHT || key == VK_RETURN || key == VK_DELETE || key == VK_F2)
+ {
+ pfnContactListControlWndProc(hwnd, WM_KEYDOWN, key, 0);
+ return dat->selection;
+ }
+ else
+ {
+ NMKEY nmkey;
+ nmkey.hdr.hwndFrom = hwnd;
+ nmkey.hdr.idFrom = GetDlgCtrlID(hwnd);
+ nmkey.hdr.code = NM_KEYDOWN;
+ nmkey.nVKey = key;
+ nmkey.uFlags = 0;
+ if (SendMessage(GetParent(hwnd), WM_NOTIFY, 0, (LPARAM) & nmkey))
+ return -2;
+ }
+ return -1;
+ }
+
+ case WM_COMMAND:
+ {
+ if ((HANDLE) lParam != dat->hwnd_list || HIWORD(wParam) != LBN_SELCHANGE)
+ break;
+
+ dat->selection = SendMessage(dat->hwnd_list, LB_GETCURSEL, 0, 0);
+
+ KillTimer(hwnd, TIMERID_INFOTIP);
+ KillTimer(hwnd, TIMERID_RENAME);
+ dat->szQuickSearch[0] = 0;
+ pcli->pfnInvalidateRect(hwnd, NULL, FALSE);
+ pcli->pfnEnsureVisible(hwnd, (struct ClcData *) dat, dat->selection, 0);
+ UpdateWindow(hwnd);
+ break;
+ }
+
+ case WM_SETFOCUS:
+ case WM_ENABLE:
+ SetFocus(dat->hwnd_list);
+ break;
+
+ }
+
+ return pfnContactListControlWndProc(hwnd, msg, wParam, lParam);
+}
+
+static int GetRealStatus(struct ClcContact *contact, int status)
+{
+ int i;
+ char *szProto = contact->proto;
+ if (!szProto)
+ return status;
+ for (i = 0; i < pcli->hClcProtoCount; i++) {
+ if (!lstrcmpA(pcli->clcProto[i].szProto, szProto)) {
+ return pcli->clcProto[i].dwStatus;
+ }
+ }
+ return status;
+}
+
+TCHAR status_name[128];
+TCHAR *GetStatusName(struct ClcContact *item)
+{
+ int status;
+
+ status_name[0] = _T('\0');
+ if (item->hContact == NULL || item->proto == NULL)
+ return status_name;
+
+ // Get XStatusName
+ MyDBGetContactSettingTString(item->hContact, item->proto, "XStatusName", status_name, MAX_REGS(status_name), NULL);
+ if (status_name[0] != _T('\0'))
+ return status_name;
+
+ // Get status name
+ status = DBGetContactSettingWord(item->hContact, item->proto, "Status", ID_STATUS_OFFLINE);
+ lstrcpyn(status_name, pcli->pfnGetStatusModeDescription(status, GCMDF_TCHAR), MAX_REGS(status_name));
+
+ return status_name;
+}
+
+
+TCHAR status_message[256];
+TCHAR *GetStatusMessage(struct ClcContact *item)
+{
+ status_message[0] = _T('\0');
+ if (item->hContact == NULL || item->proto == NULL)
+ return status_message;
+
+ // Get XStatusMsg
+ MyDBGetContactSettingTString(item->hContact, item->proto, "XStatusMsg", status_message, MAX_REGS(status_message), NULL);
+ if (status_message[0] != _T('\0'))
+ return status_message;
+
+ // Get status message
+ MyDBGetContactSettingTString(item->hContact, "CList", "StatusMsg", status_message, MAX_REGS(status_message), NULL);
+
+ return status_message;
+}
+
+
+TCHAR proto_name[128];
+TCHAR *GetProtoName(struct ClcContact *item)
+{
+ PROTOACCOUNT *acc;
+#ifdef UNICODE
+ char description[128];
+#endif
+
+ proto_name[0] = '\0';
+ if (item->hContact == NULL || item->proto == NULL)
+ {
+ lstrcpyn(proto_name, TranslateT("Unknown Protocol"), MAX_REGS(proto_name));
+ return proto_name;
+ }
+
+ acc = ProtoGetAccount(item->proto);
+
+ if (acc == NULL)
+ {
+#ifdef UNICODE
+ CallProtoService(item->proto, PS_GETNAME, sizeof(description),(LPARAM) description);
+ mir_sntprintf(proto_name, MAX_REGS(proto_name), L"%S", description);
+#else
+ CallProtoService(item->proto, PS_GETNAME, sizeof(proto_name),(LPARAM) proto_name);
+#endif
+ return proto_name;
+ }
+
+ lstrcpyn(proto_name, acc->tszAccountName, MAX_REGS(proto_name));
+
+ return proto_name;
+}
+
+void RebuildEntireListInternal(HWND hwnd, struct ClcData *tmp_dat, BOOL call_orig)
+{
+ struct MyClcData *dat = (struct MyClcData *) tmp_dat;
+ struct ClcGroup *group;
+ struct ClcContact *item;
+ TCHAR tmp[1024];
+ TCHAR count[128];
+ TCHAR template_contact[1024];
+ TCHAR template_group[1024];
+ TCHAR template_divider[1024];
+ TCHAR template_info[1024];
+ TCHAR *text;
+ size_t size;
+ int selection = dat->selection;
+ BOOL has_focus = (GetFocus() == dat->hwnd_list || GetFocus() == hwnd);
+
+ if (call_orig)
+ pfnRebuildEntireList(hwnd, (struct ClcData *) dat);
+
+ MyDBGetContactSettingTString(NULL, "CLC", "TemplateContact", template_contact, 1024, TranslateT("%name% [%status% %protocol%] %status_message%"));
+ MyDBGetContactSettingTString(NULL, "CLC", "TemplateGroup", template_group, 1024, TranslateT("Group: %name% %count% [%mode%]"));
+ MyDBGetContactSettingTString(NULL, "CLC", "TemplateDivider", template_divider, 1024, TranslateT("Divider: %s"));
+ MyDBGetContactSettingTString(NULL, "CLC", "TemplateInfo", template_info, 1024, TranslateT("Info: %s"));
+
+ SendMessage(dat->hwnd_list, WM_SETREDRAW, FALSE, 0);
+
+ // Reset content
+ SendMessage(dat->hwnd_list, LB_RESETCONTENT, 0, 0);
+
+ // Set font
+ SendMessage(dat->hwnd_list, WM_SETFONT, (WPARAM) dat->fontInfo[FONTID_CONTACTS].hFont, 0);
+
+ // Add all items to the list
+ group = &dat->list;
+ group->scanIndex = 0;
+ text = tmp;
+ size = MAX_REGS(tmp);
+ while(1)
+ {
+ if (group->scanIndex == group->cl.count)
+ {
+ group = group->parent;
+ if (group == NULL)
+ break;
+ text -= 2;
+ size += 2;
+ group->scanIndex++;
+ continue;
+ }
+
+ item = group->cl.items[group->scanIndex];
+ text[0] = _T('\0');
+ switch(item->type)
+ {
+ case CLCIT_GROUP:
+ {
+ char *szCounts = pcli->pfnGetGroupCountsText((struct ClcData *) dat, item);
+ TCHAR *t[] = {
+ _T("%name%"),
+ _T("%count%"),
+ _T("%mode%")
+ };
+ TCHAR *v[] = {
+ item->szText,
+ count,
+ item->group->expanded ? TranslateT("Expanded") : TranslateT("Colapsed")
+ };
+ TCHAR *txt;
+
+ if (szCounts[0] != '\0')
+ {
+#ifdef UNICODE
+ mir_sntprintf(count, MAX_REGS(count), L"%S ", szCounts);
+#else
+ mir_sntprintf(count, MAX_REGS(count), "%s ", szCounts);
+#endif
+ }
+ else
+ {
+ count[0] = _T('\0');
+ }
+
+ txt = ParseText(template_group, t, MAX_REGS(t), v, MAX_REGS(v));
+ if (txt != NULL)
+ lstrcpyn(text, txt, size);
+ mir_free(txt);
+ break;
+ }
+ case CLCIT_CONTACT:
+ {
+ char *szCounts = pcli->pfnGetGroupCountsText((struct ClcData *) dat, item);
+ TCHAR *t[] = {
+ _T("%name%"),
+ _T("%status%"),
+ _T("%protocol%"),
+ _T("%status_message%")
+ };
+ TCHAR *v[] = {
+ item->szText,
+ GetStatusName(item),
+ GetProtoName(item),
+ GetStatusMessage(item)
+ };
+ TCHAR *txt;
+
+ txt = ParseText(template_contact, t, MAX_REGS(t), v, MAX_REGS(v));
+ if (txt != NULL)
+ lstrcpyn(text, txt, size);
+ mir_free(txt);
+ break;
+ }
+ case CLCIT_DIVIDER:
+ {
+ mir_sntprintf(text, size, template_divider, item->szText);
+ break;
+ }
+ case CLCIT_INFO:
+ {
+ mir_sntprintf(text, size, template_info, item->szText);
+ break;
+ }
+ }
+
+ SendMessage(dat->hwnd_list, LB_ADDSTRING, 0, (LPARAM) tmp);
+
+ if (item->type == CLCIT_GROUP && item->group->expanded)
+ {
+ group = item->group;
+ text[0] = _T(' ');
+ text[1] = _T(' ');
+ text += 2;
+ size -= 2;
+ group->scanIndex = 0;
+ continue;
+ }
+ group->scanIndex++;
+ }
+
+ SendMessage(dat->hwnd_list, WM_SETREDRAW, TRUE, 0);
+ InvalidateRect(dat->hwnd_list, NULL, TRUE);
+
+ dat->selection = selection;
+ SendMessage(dat->hwnd_list, LB_SETCURSEL, dat->selection, 0);
+ if (has_focus)
+ SetFocus(dat->hwnd_list);
+
+ dat->need_rebuild = FALSE;
+}
+
+void RebuildEntireList(HWND hwnd, struct ClcData *dat)
+{
+ RebuildEntireListInternal(hwnd, dat, TRUE);
+}
+
+void SetGroupExpand(HWND hwnd, struct ClcData *tmp_dat, struct ClcGroup *group, int newState)
+{
+ struct MyClcData *dat = (struct MyClcData *) tmp_dat;
+
+ pfnSetGroupExpand(hwnd, tmp_dat, group, newState);
+ dat->need_rebuild = TRUE;
+}
+
+void ScrollTo( HWND hwnd, struct ClcData *dat, int desty, int noSmooth )
+{
+}
+
+void RecalcScrollBar( HWND hwnd, struct ClcData *dat )
+{
+}
+
+void LoadClcOptions( HWND hwnd, struct ClcData *tmp_dat )
+{
+ struct MyClcData *dat = (struct MyClcData *) tmp_dat;
+
+ pfnLoadClcOptions(hwnd, tmp_dat);
+ dat->rowHeight = SendMessage(dat->hwnd_list, LB_GETITEMHEIGHT, 0, 0);
+}
+
+int GetRowHeight(struct ClcData *tmp_dat, int item)
+{
+ struct MyClcData *dat = (struct MyClcData *) tmp_dat;
+
+ dat->rowHeight = SendMessage(dat->hwnd_list, LB_GETITEMHEIGHT, 0, 0);
+ return dat->rowHeight;
+}
+
+void SortCLC(HWND hwnd, struct ClcData *tmp_dat, int useInsertionSort)
+{
+ if ( tmp_dat->needsResort )
+ {
+ struct MyClcData *dat = (struct MyClcData *) tmp_dat;
+
+ pfnSortCLC(hwnd, tmp_dat, useInsertionSort);
+ dat->need_rebuild = TRUE;
+ }
+} \ No newline at end of file
diff --git a/Plugins/bclist/m_updater.h b/Plugins/bclist/m_updater.h
new file mode 100644
index 0000000..371b743
--- /dev/null
+++ b/Plugins/bclist/m_updater.h
@@ -0,0 +1,146 @@
+#ifndef _M_UPDATER_H
+#define _M_UPDATER_H
+
+// NOTES:
+// - For langpack updates, include a string of the following format in the langpack text file:
+// ";FLID: <file listing name> <version>"
+// version must be four numbers seperated by '.', in the range 0-255 inclusive
+// - Updater will disable plugins that are downloaded but were not active prior to the update (this is so that, if an archive contains e.g. ansi and
+// unicode versions, the correct plugin will be the only one active after the new version is installed)...so if you add a support plugin, you may need
+// to install an ini file to make the plugin activate when miranda restarts after the update
+// - Updater will replace all dlls that have the same internal shortName as a downloaded update dll (this is so that msn1.dll and msn2.dll, for example,
+// will both be updated) - so if you have a unicode and a non-unicode version of a plugin in your archive, you should make the internal names different (which will break automatic
+// updates from the file listing if there is only one file listing entry for both versions, unless you use the 'MS_UPDATE_REGISTER' service below)
+// - Updater will install all files in the root of the archive into the plugins folder, except for langpack files that contain the FLID string which go into the root folder (same
+// folder as miranda32.exe)...all folders in the archive will also be copied to miranda's root folder, and their contents transferred into the new folders. The only exception is a
+// special folder called 'root_files' - if there is a folder by that name in the archive, it's contents will also be copied into miranda's root folder - this is intended to be used
+// to install additional dlls etc that a plugin may require)
+
+// if you set Update.szUpdateURL to the following value when registering, as well as setting your beta site and version data,
+// Updater will ignore szVersionURL and pbVersionPrefix, and attempt to find the file listing URL's from the backend XML data.
+// for this to work, the plugin name in pluginInfo.shortName must match the file listing exactly (except for case)
+#define UPDATER_AUTOREGISTER "UpdaterAUTOREGISTER"
+// Updater will also use the backend xml data if you provide URL's that reference the miranda file listing for updates (so you can use that method
+// if e.g. your plugin shortName does not match the file listing) - it will grab the file listing id from the end of these URLs
+
+typedef struct Update_tag {
+ int cbSize;
+ char *szComponentName; // component name as it will appear in the UI (will be translated before displaying)
+
+ char *szVersionURL; // URL where the current version can be found (NULL to disable)
+ BYTE *pbVersionPrefix; // bytes occuring in VersionURL before the version, used to locate the version information within the URL data
+ // (note that this URL could point at a binary file - dunno why, but it could :)
+ int cpbVersionPrefix; // number of bytes pointed to by pbVersionPrefix
+ char *szUpdateURL; // URL where dll/zip is located
+ // set to UPDATER_AUTOREGISTER if you want Updater to find the file listing URLs (ensure plugin shortName matches file listing!)
+
+ char *szBetaVersionURL; // URL where the beta version can be found (NULL to disable betas)
+ BYTE *pbBetaVersionPrefix; // bytes occuring in VersionURL before the version, used to locate the version information within the URL data
+ int cpbBetaVersionPrefix; // number of bytes pointed to by pbVersionPrefix
+ char *szBetaUpdateURL; // URL where dll/zip is located
+
+ BYTE *pbVersion; // bytes of current version, used for comparison with those in VersionURL
+ int cpbVersion; // number of bytes pointed to by pbVersion
+
+ char *szBetaChangelogURL; // url for displaying changelog for beta versions
+} Update;
+
+// register a comonent with Updater
+//
+// wparam = 0
+// lparam = (LPARAM)&Update
+#define MS_UPDATE_REGISTER "Update/Register"
+
+// utility functions to create a version string from a DWORD or from pluginInfo
+// point buf at a buffer at least 16 chars wide - but note the version string returned may be shorter
+//
+__inline static char *CreateVersionString(DWORD version, char *buf) {
+ mir_snprintf(buf, 16, "%d.%d.%d.%d", (version >> 24) & 0xFF, (version >> 16) & 0xFF, (version >> 8) & 0xFF, version & 0xFF);
+ return buf;
+}
+
+__inline static char *CreateVersionStringPlugin(PLUGININFO *pluginInfo, char *buf) {
+ return CreateVersionString(pluginInfo->version, buf);
+}
+
+
+// register the 'easy' way - use this method if you have no beta URL and the plugin is on the miranda file listing
+// NOTE: the plugin version string on the file listing must be the string version of the version in pluginInfo (i.e. 0.0.0.1,
+// four numbers between 0 and 255 inclusivem, so no letters, brackets, etc.)
+//
+// wParam = (int)fileID - this is the file ID from the file listing (i.e. the number at the end of the download link)
+// lParam = (PLUGININFO*)&pluginInfo
+#define MS_UPDATE_REGISTERFL "Update/RegisterFL"
+
+// this function can be used to 'unregister' components - useful for plugins that register non-plugin/langpack components and
+// may need to change those components on the fly
+// lParam = (char *)szComponentName
+#define MS_UPDATE_UNREGISTER "Update/Unregister"
+
+// this event is fired when the startup process is complete, but NOT if a restart is imminent
+// it is designed for status managment plugins to use as a trigger for beggining their own startup process
+// wParam = lParam = 0 (unused)
+// (added in version 0.1.6.0)
+#define ME_UPDATE_STARTUPDONE "Update/StartupDone"
+
+// this service can be used to enable/disable Updater's global status control
+// it can be called from the StartupDone event handler
+// wParam = (BOOL)enable
+// lParam = 0
+// (added in version 0.1.6.0)
+#define MS_UPDATE_ENABLESTATUSCONTROL "Update/EnableStatusControl"
+
+// An description of usage of the above service and event:
+// Say you are a status control plugin that normally sets protocol or global statuses in your ModulesLoaded event handler.
+// In order to make yourself 'Updater compatible', you would move the status control code from ModulesLoaded to another function,
+// say DoStartup. Then, in ModulesLoaded you would check for the existence of the MS_UPDATE_ENABLESTATUSCONTROL service.
+// If it does not exist, call DoStartup. If it does exist, hook the ME_UPDATE_STARTUPDONE event and call DoStartup from there. You may
+// also wish to call MS_UPDATE_ENABLESTATUSCONTROL with wParam == FALSE at this time, to disable Updater's own status control feature.
+
+// this service can be used to determine whether updates are possible for a component with the given name
+// wParam = 0
+// lParam = (char *)szComponentName
+// returns TRUE if updates are supported, FALSE otherwise
+#define MS_UPDATE_ISUPDATESUPPORTED "Update/IsUpdateSupported"
+
+#endif
+
+
+/////////////// Usage Example ///////////////
+
+#ifdef EXAMPLE_CODE
+
+// you need to #include "m_updater.h" and HookEvent(ME_SYSTEM_MODULESLOADED, OnModulesLoaded) in your Load function...
+
+int OnModulesLoaded(WPARAM wParam, LPARAM lParam) {
+
+ Update update = {0}; // for c you'd use memset or ZeroMemory...
+ char szVersion[16];
+
+ update.cbSize = sizeof(Update);
+
+ update.szComponentName = pluginInfo.shortName;
+ update.pbVersion = (BYTE *)CreateVersionString(&pluginInfo, szVersion);
+ update.cpbVersion = strlen((char *)update.pbVersion);
+
+ // these are the three lines that matter - the archive, the page containing the version string, and the text (or data)
+ // before the version that we use to locate it on the page
+ // (note that if the update URL and the version URL point to standard file listing entries, the backend xml
+ // data will be used to check for updates rather than the actual web page - this is not true for beta urls)
+ update.szUpdateURL = "http://scottellis.com.au:81/test/updater.zip";
+ update.szVersionURL = "http://scottellis.com.au:81/test/updater_test.html";
+ update.pbVersionPrefix = (BYTE *)"Updater version ";
+
+ update.cpbVersionPrefix = strlen((char *)update.pbVersionPrefix);
+
+ // do the same for the beta versions of the above struct members if you wish to allow beta updates from another URL
+
+ CallService(MS_UPDATE_REGISTER, 0, (WPARAM)&update);
+
+ // Alternatively, to register a plugin with e.g. file ID 2254 on the file listing...
+ // CallService(MS_UPDATE_REGISTERFL, (WPARAM)2254, (LPARAM)&pluginInfo);
+
+ return 0;
+}
+
+#endif
diff --git a/Plugins/bclist/res/blank.ico b/Plugins/bclist/res/blank.ico
new file mode 100644
index 0000000..7845f62
--- /dev/null
+++ b/Plugins/bclist/res/blank.ico
Binary files differ
diff --git a/Plugins/bclist/res/delete.ico b/Plugins/bclist/res/delete.ico
new file mode 100644
index 0000000..9b0f0ff
--- /dev/null
+++ b/Plugins/bclist/res/delete.ico
Binary files differ
diff --git a/Plugins/bclist/res/dragcopy.cur b/Plugins/bclist/res/dragcopy.cur
new file mode 100644
index 0000000..89c7c96
--- /dev/null
+++ b/Plugins/bclist/res/dragcopy.cur
Binary files differ
diff --git a/Plugins/bclist/res/dropuser.cur b/Plugins/bclist/res/dropuser.cur
new file mode 100644
index 0000000..a84b19e
--- /dev/null
+++ b/Plugins/bclist/res/dropuser.cur
Binary files differ
diff --git a/Plugins/bclist/res/hyperlin.cur b/Plugins/bclist/res/hyperlin.cur
new file mode 100644
index 0000000..f0f548c
--- /dev/null
+++ b/Plugins/bclist/res/hyperlin.cur
Binary files differ
diff --git a/Plugins/bclist/res/rename.ico b/Plugins/bclist/res/rename.ico
new file mode 100644
index 0000000..2c6bc2a
--- /dev/null
+++ b/Plugins/bclist/res/rename.ico
Binary files differ
diff --git a/Plugins/bclist/resource.h b/Plugins/bclist/resource.h
new file mode 100644
index 0000000..03fef64
--- /dev/null
+++ b/Plugins/bclist/resource.h
@@ -0,0 +1,162 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Developer Studio generated include file.
+// Used by resource.rc
+//
+#define IDD_OPT_CLIST 126
+#define IDR_CONTEXT 180
+#define IDC_DROP 183
+#define IDD_OPT_HOTKEY 184
+#define IDR_CLISTMENU 199
+#define IDC_HYPERLINKHAND 214
+#define IDC_DROPUSER 215
+#define IDD_OPT_CLUI 218
+#define IDD_OPT_CLC 228
+#define IDD_OPT_CLCBKG 230
+#define IDD_OPT_SBAR 234
+#define IDD_OPT_CLCTEXTSIMPLE 239
+#define IDD_DELETECONTACT 254
+#define IDC_MIN2TRAY 1073
+#define IDC_ONTOP 1074
+#define IDC_SHOWMAINMENU 1075
+#define IDC_CLIENTDRAG 1076
+#define IDC_TOOLWND 1097
+#define IDC_ONECLK 1098
+#define IDC_SHOWCAPTION 1098
+#define IDC_HIDEOFFLINE 1099
+#define IDC_SHOWHIDE 1100
+#define IDC_HIDEEMPTYGROUPS 1100
+#define IDC_SORTBYSTATUS 1101
+#define IDC_FADEINOUT 1101
+#define IDC_READMSG 1102
+#define IDC_AUTOSIZE 1102
+#define IDC_DISABLEGROUPS 1102
+#define IDC_AUTOSIZEUPWARD 1103
+#define IDC_ALWAYSSTATUS 1103
+#define IDC_NETSEARCH 1104
+#define IDC_CONFIRMDELETE 1104
+#define IDC_SORTBYPROTO 1105
+#define IDC_SHOWOPTIONS 1105
+#define IDC_SEARCHURL 1106
+#define IDC_TRANSPARENT 1124
+#define IDC_TRANSINACTIVE 1126
+#define IDC_TRANSACTIVE 1128
+#define IDC_STATIC11 1154
+#define IDC_STATIC12 1155
+#define IDC_STATIC21 1156
+#define IDC_STATIC22 1157
+#define IDC_HKSHOWHIDE 1162
+#define IDC_HKREADMSG 1163
+#define IDC_HKSEARCH 1164
+#define IDC_HKSHOWOPTIONS 1165
+#define IDC_BROWSE 1184
+#define IDC_INACTIVEPERC 1187
+#define IDC_ACTIVEPERC 1188
+#define IDC_SEARCHNEWWND 1188
+#define IDC_TITLETEXT 1196
+#define IDC_AUTOHIDE 1235
+#define IDC_HIDETIME 1236
+#define IDC_MAXSIZEHEIGHT 1254
+#define IDC_MAXSIZESPIN 1255
+#define IDC_BKGCOLOUR 1269
+#define IDC_FILENAME 1271
+#define IDC_SCROLL 1277
+#define IDC_PROPORTIONAL 1278
+#define IDC_SELCOLOUR 1281
+#define IDC_SMOOTHTIME 1283
+#define IDC_SMOOTHTIMESPIN 1284
+#define IDC_GREYOUT 1285
+#define IDC_ROWHEIGHT 1286
+#define IDC_ROWHEIGHTSPIN 1287
+#define IDC_GREYOUTOPTS 1288
+#define IDC_GROUPINDENT 1289
+#define IDC_GROUPINDENTSPIN 1290
+#define IDC_LEFTMARGIN 1291
+#define IDC_SAMPLE 1292
+#define IDC_LEFTMARGINSPIN 1292
+#define IDC_FONTSIZE 1293
+#define IDC_STRETCHH 1298
+#define IDC_STRETCHV 1299
+#define IDC_TILEH 1300
+#define IDC_SCRIPT 1300
+#define IDC_TILEV 1301
+#define IDC_GAMMACORRECT 1302
+#define IDC_HIDEOFFLINEOPTS 1308
+#define IDC_DONTCYCLE 1315
+#define IDC_PRIMARYSTATUS 1316
+#define IDC_CYCLE 1317
+#define IDC_CYCLETIME 1318
+#define IDC_CYCLETIMESPIN 1319
+#define IDC_HIDETIMESPIN 1320
+#define IDC_MULTITRAY 1321
+#define IDC_ALWAYSMULTI 1322
+#define IDC_SHOWICON 1323
+#define IDC_SHOWPROTO 1324
+#define IDC_SHOWSTATUS 1325
+#define IDC_EQUALSECTIONS 1326
+#define IDC_SHOWSBAR 1329
+#define IDC_RIGHTMIRANDA 1330
+#define IDC_RIGHTSTATUS 1331
+#define IDC_SORTBYNAME 1347
+#define IDC_STAUTOHIDESECS 1349
+#define IDC_STCLISTGROUP 1350
+#define IDC_DISABLEDRAGDROP 1351
+#define IDC_NOTEDITLABELS 1352
+#define IDC_SHOWSELALWAYS 1353
+#define IDC_TRACKSELECT 1354
+#define IDC_SHOWGROUPCOUNTS 1355
+#define IDC_HIDECOUNTSWHENEMPTY 1356
+#define IDC_NOTNOTRANSLUCENTSEL 1358
+#define IDC_LINEWITHGROUPS 1359
+#define IDC_QUICKSEARCHVISONLY 1360
+#define IDC_SORTGROUPSALPHA 1361
+#define IDC_NOTNOSMOOTHSCROLLING 1362
+#define IDC_BITMAP 1363
+#define IDC_STWINDOWGROUP 1364
+#define IDC_STATIC01 1365
+#define IDC_HIDE 1534
+#define IDC_TOPLINE 1535
+#define IDC_HOTKEYURLSTR 1567
+#define IDC_BRINGTOFRONT 1579
+#define IDC_BLINKTIME 1607
+#define IDC_BLINKSPIN 1608
+#define IDC_DISABLEBLINK 1609
+#define IDC_IDLE 1610
+#define IDC_SBPANELBEVEL 1611
+#define IDC_DROPSHADOW 1612
+#define IDC_SHOWGRIP 1612
+#define IDC_NOSCROLLBAR 1613
+#define IDC_TXT_TITLE1 1617
+#define IDC_TXT_TITLE2 1618
+#define IDC_ONDESKTOP 1657
+#define IDC_WINCOLOUR 1659
+#define IDC_ICONBLINK 1660
+#define IDC_STMSDELAY 1661
+#define IDC_T_CONTACT 1662
+#define IDC_T_GROUP 1663
+#define IDC_T_DIVIDER 1664
+#define IDC_T_INFO 1665
+#define ID_ICQ_EXIT 40001
+#define POPUP_HIDEEMPTYGROUPS 40003
+#define POPUP_NEWSUBGROUP 40004
+#define POPUP_HIDEOFFLINE 40005
+#define POPUP_GROUPHIDEOFFLINE 40006
+#define POPUP_HIDEOFFLINEROOT 40007
+#define POPUP_DISABLEGROUPS 40008
+#define POPUP_HIDEMIRANDA 40017
+#define ID_TRAY_HIDE 40038
+#define ID_TRAY_EXIT 40040
+#define POPUP_NEWGROUP 40050
+#define POPUP_RENAMEGROUP 40052
+#define POPUP_DELETEGROUP 40053
+#define IDC_STATIC -1
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 292
+#define _APS_NEXT_COMMAND_VALUE 40018
+#define _APS_NEXT_CONTROL_VALUE 1662
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/Plugins/bclist/resource.rc b/Plugins/bclist/resource.rc
new file mode 100644
index 0000000..547b43e
--- /dev/null
+++ b/Plugins/bclist/resource.rc
@@ -0,0 +1,463 @@
+//Microsoft Developer Studio generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include <windows.h>
+#include <winres.h>
+#include <statusmodes.h>
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (U.S.) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page(1252)
+#endif //_WIN32
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Dialog
+//
+
+IDD_DELETECONTACT DIALOGEX 0, 0, 284, 90
+STYLE DS_MODALFRAME | DS_SETFOREGROUND | DS_FIXEDSYS | DS_CENTER | WS_POPUP |
+ WS_VISIBLE | WS_CAPTION | WS_SYSMENU
+EXSTYLE WS_EX_CONTROLPARENT
+CAPTION "Delete Contact"
+FONT 8, "MS Shell Dlg", 0, 0, 0x1
+BEGIN
+ CONTROL "Are you sure you want to delete %s?",IDC_TOPLINE,"Static",
+ SS_SIMPLE | SS_NOPREFIX | WS_GROUP,7,7,270,8
+ LTEXT "This will erase all history and settings for this contact!",
+ IDC_STATIC,7,18,239,14
+ PUSHBUTTON "Yes",IDYES,54,38,65,14
+ DEFPUSHBUTTON "No",IDNO,162,38,65,14
+ CONTROL "Hide from list only, in order to keep their history and ignore/visibility settings",
+ IDC_HIDE,"Button",BS_AUTOCHECKBOX | BS_MULTILINE |
+ WS_TABSTOP,7,65,270,9
+ LTEXT "Use Options->Ignore (expert mode) to unhide contacts.",
+ IDC_STATIC,20,78,257,8
+END
+
+IDD_OPT_HOTKEY DIALOGEX 0, 0, 238, 136
+STYLE DS_FIXEDSYS | WS_CHILD | WS_VISIBLE
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg", 0, 0, 0x1
+BEGIN
+ GROUPBOX "Hotkeys",IDC_STATIC,4,4,230,126
+ CONTROL "Show/Hide:",IDC_SHOWHIDE,"Button",BS_AUTOCHECKBOX |
+ WS_TABSTOP,14,19,78,9
+ CONTROL "HotKey1",IDC_HKSHOWHIDE,"msctls_hotkey32",WS_BORDER |
+ WS_TABSTOP,92,17,93,12
+ CONTROL "Read Message:",IDC_READMSG,"Button",BS_AUTOCHECKBOX |
+ WS_TABSTOP,14,36,78,9
+ CONTROL "HotKey1",IDC_HKREADMSG,"msctls_hotkey32",WS_BORDER |
+ WS_TABSTOP,92,34,93,12
+ CONTROL "Web Search:",IDC_NETSEARCH,"Button",BS_AUTOCHECKBOX |
+ WS_TABSTOP,14,53,78,9
+ CONTROL "HotKey1",IDC_HKSEARCH,"msctls_hotkey32",WS_BORDER |
+ WS_TABSTOP,92,51,93,12
+ LTEXT "URL:",IDC_HOTKEYURLSTR,26,71,22,8
+ EDITTEXT IDC_SEARCHURL,92,69,136,12,ES_AUTOHSCROLL
+ CONTROL "Open in new browser window",IDC_SEARCHNEWWND,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,92,86,136,10
+ CONTROL "Show Options",IDC_SHOWOPTIONS,"Button",BS_AUTOCHECKBOX |
+ WS_TABSTOP,14,107,71,9
+ CONTROL "HotKey1",IDC_HKSHOWOPTIONS,"msctls_hotkey32",WS_BORDER |
+ WS_TABSTOP,92,105,93,12
+END
+
+IDD_OPT_CLIST DIALOGEX 0, 0, 314, 204
+STYLE DS_FIXEDSYS | DS_CENTER | WS_CHILD
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg"
+BEGIN
+ CONTROL "Hide offline users",IDC_HIDEOFFLINE,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,10,17,124,10
+ CONTROL "Hide empty groups",IDC_HIDEEMPTYGROUPS,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,10,33,124,10
+ CONTROL "Disable groups",IDC_DISABLEGROUPS,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,10,50,124,10
+ CONTROL "Ask before deleting contacts",IDC_CONFIRMDELETE,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,10,67,124,10
+ CONTROL "Sort contacts by name",IDC_SORTBYNAME,"Button",
+ BS_AUTORADIOBUTTON | WS_GROUP,153,14,144,10
+ CONTROL "Sort contacts by status",IDC_SORTBYSTATUS,"Button",
+ BS_AUTORADIOBUTTON,153,26,149,10
+ CONTROL "Sort contacts by protocol",IDC_SORTBYPROTO,"Button",
+ BS_AUTORADIOBUTTON,153,38,151,10
+ CONTROL "Single click interface",IDC_ONECLK,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,152,68,150,10
+ CONTROL "Always show status in tooltip",IDC_ALWAYSSTATUS,"Button",
+ BS_AUTOCHECKBOX | BS_TOP | BS_MULTILINE | WS_TABSTOP,152,
+ 81,151,11
+ CONTROL "Disable icon blinking",IDC_DISABLEBLINK,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,152,95,86,9
+ EDITTEXT IDC_BLINKTIME,153,109,35,12,ES_NUMBER
+ LTEXT "ms delay",IDC_STMSDELAY,192,110,113,8
+ CONTROL "Show",IDC_DONTCYCLE,"Button",BS_AUTORADIOBUTTON |
+ WS_GROUP,10,143,97,10
+ COMBOBOX IDC_PRIMARYSTATUS,107,142,78,70,CBS_DROPDOWNLIST |
+ WS_VSCROLL | WS_TABSTOP
+ LTEXT "icon when statuses differ",IDC_STATIC,189,144,116,8,NOT
+ WS_GROUP
+ CONTROL "Cycle icons every",IDC_CYCLE,"Button",
+ BS_AUTORADIOBUTTON,10,160,97,10
+ EDITTEXT IDC_CYCLETIME,107,159,30,12,ES_RIGHT | ES_NUMBER
+ CONTROL "Spin1",IDC_CYCLETIMESPIN,"msctls_updown32",
+ UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY |
+ UDS_ARROWKEYS | UDS_NOTHOUSANDS | UDS_HOTTRACK,127,158,
+ 10,14
+ LTEXT "seconds, when statuses differ",IDC_STATIC,140,161,165,8,
+ NOT WS_GROUP
+ CONTROL "Show multiple icons",IDC_MULTITRAY,"Button",
+ BS_AUTORADIOBUTTON,10,177,98,10
+ CONTROL "Only when statuses differ",IDC_ALWAYSMULTI,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,108,177,198,10
+ GROUPBOX "Contact List",IDC_STCLISTGROUP,2,2,135,126
+ GROUPBOX "System tray icon",IDC_STATIC,143,57,169,70
+ GROUPBOX "System tray icon when using multiple protocols",
+ IDC_STATIC,2,131,310,66
+ GROUPBOX "Contact List Sorting",IDC_STATIC,143,2,169,54,WS_GROUP
+ CONTROL "Spin5",IDC_BLINKSPIN,"msctls_updown32",UDS_SETBUDDYINT |
+ UDS_ALIGNRIGHT | UDS_ARROWKEYS | UDS_NOTHOUSANDS |
+ UDS_HOTTRACK,173,97,11,21
+END
+
+IDD_OPT_CLUI DIALOGEX 0, 0, 313, 245
+STYLE DS_FIXEDSYS | DS_CENTER | WS_CHILD
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg", 0, 0, 0x1
+BEGIN
+ GROUPBOX "Window",IDC_STWINDOWGROUP,4,4,305,154
+ CONTROL "Always on top",IDC_ONTOP,"Button",BS_AUTOCHECKBOX |
+ WS_TABSTOP,13,18,128,10
+ CONTROL "Tool style main window",IDC_TOOLWND,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,13,32,128,10
+ CONTROL "Minimize to tray",IDC_MIN2TRAY,"Button",BS_AUTOCHECKBOX |
+ WS_TABSTOP,13,46,128,10
+ CONTROL "Hide contact list after it has been idle for",
+ IDC_AUTOHIDE,"Button",BS_AUTOCHECKBOX | BS_TOP |
+ BS_MULTILINE | WS_TABSTOP,141,18,162,10
+ EDITTEXT IDC_HIDETIME,151,33,30,12,ES_RIGHT | ES_NUMBER
+ CONTROL "Spin1",IDC_HIDETIMESPIN,"msctls_updown32",
+ UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY |
+ UDS_ARROWKEYS | UDS_NOTHOUSANDS | UDS_HOTTRACK,172,32,10,
+ 14
+ LTEXT "seconds",IDC_STATIC01,186,35,56,8
+ CONTROL "Automatically resize window to height of list",
+ IDC_AUTOSIZE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,141,
+ 55,162,10
+ LTEXT "maximum",IDC_STATIC21,151,71,45,8
+ EDITTEXT IDC_MAXSIZEHEIGHT,197,69,31,12,ES_RIGHT | ES_AUTOHSCROLL |
+ ES_NUMBER
+ CONTROL "Spin1",IDC_MAXSIZESPIN,"msctls_updown32",
+ UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY |
+ UDS_ARROWKEYS | UDS_HOTTRACK,218,68,10,14
+ LTEXT "% of screen",IDC_STATIC22,230,71,69,8
+ CONTROL "Size upwards",IDC_AUTOSIZEUPWARD,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,151,86,147,10
+ LTEXT "Title bar text:",IDC_STATIC,12,106,51,8
+ GROUPBOX "Translucency options (Windows 2000/XP only)",IDC_STATIC,
+ 4,161,305,80
+ CONTROL "Fade contact list in/out",IDC_FADEINOUT,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,13,174,163,10
+ CONTROL "Transparent contact list",IDC_TRANSPARENT,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,13,191,151,9
+ LTEXT "Inactive opacity:",IDC_STATIC11,23,206,89,8
+ CONTROL "Slider2",IDC_TRANSINACTIVE,"msctls_trackbar32",TBS_TOP |
+ TBS_NOTICKS | WS_TABSTOP,112,205,130,11
+ LTEXT "Active opacity:",IDC_STATIC12,23,221,89,8
+ CONTROL "Slider2",IDC_TRANSACTIVE,"msctls_trackbar32",TBS_TOP |
+ TBS_NOTICKS | WS_TABSTOP,112,221,130,11
+ LTEXT "000%",IDC_INACTIVEPERC,246,206,29,8,SS_NOPREFIX
+ LTEXT "000%",IDC_ACTIVEPERC,246,222,29,8,SS_NOPREFIX
+ CONTROL "Show menu bar",IDC_SHOWMAINMENU,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,13,60,128,10
+ CONTROL "Easy move",IDC_CLIENTDRAG,"Button",BS_AUTOCHECKBOX |
+ WS_TABSTOP,13,74,128,10
+ CONTROL "Show title bar",IDC_SHOWCAPTION,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,13,88,128,10
+ CONTROL "If window is partially covered, bring to front instead of hiding",
+ IDC_BRINGTOFRONT,"Button",BS_AUTOCHECKBOX | BS_MULTILINE |
+ WS_TABSTOP,141,102,162,16
+ EDITTEXT IDC_TITLETEXT,63,104,72,12,ES_AUTOHSCROLL
+ CONTROL "Show drop shadow (restart required)",IDC_DROPSHADOW,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,13,122,138,10
+ CONTROL "Pin to desktop",IDC_ONDESKTOP,"Button",BS_AUTOCHECKBOX |
+ WS_TABSTOP,13,137,128,10
+END
+
+IDD_OPT_CLC DIALOGEX 0, 0, 314, 242
+STYLE DS_FIXEDSYS | WS_CHILD | WS_VISIBLE
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg"
+BEGIN
+ GROUPBOX "Items",IDC_STATIC,2,2,310,88
+ LTEXT "'Hide Offline' means to hide:",IDC_STATIC,191,10,112,8
+ CONTROL "Tree1",IDC_HIDEOFFLINEOPTS,"SysTreeView32",
+ TVS_DISABLEDRAGDROP | TVS_NOTOOLTIPS | TVS_NONEVENHEIGHT |
+ WS_BORDER | WS_TABSTOP,191,20,112,63
+ GROUPBOX "Groups",IDC_STATIC,2,93,310,67
+ CONTROL "Show counts of number of contacts in a group",
+ IDC_SHOWGROUPCOUNTS,"Button",BS_AUTOCHECKBOX |
+ BS_MULTILINE | WS_TABSTOP,11,106,292,11
+ CONTROL "Hide group counts when there are none online",
+ IDC_HIDECOUNTSWHENEMPTY,"Button",BS_AUTOCHECKBOX |
+ BS_MULTILINE | WS_TABSTOP,11,119,292,11
+ CONTROL "Sort groups alphabetically",IDC_SORTGROUPSALPHA,"Button",
+ BS_AUTOCHECKBOX | BS_MULTILINE | WS_TABSTOP,11,132,131,
+ 11
+ CONTROL "Quicksearch in open groups only",IDC_QUICKSEARCHVISONLY,
+ "Button",BS_AUTOCHECKBOX | BS_MULTILINE | WS_TABSTOP,11,
+ 145,131,11
+ GROUPBOX "Templates",IDC_STATIC,2,163,310,77
+ LTEXT "Contact:",IDC_STATIC,11,178,42,10
+ EDITTEXT IDC_T_CONTACT,57,176,246,13,ES_AUTOHSCROLL
+ LTEXT "Group:",IDC_STATIC,11,193,42,10
+ EDITTEXT IDC_T_GROUP,57,191,246,13,ES_AUTOHSCROLL
+ LTEXT "Divider:",IDC_STATIC,11,209,42,10
+ EDITTEXT IDC_T_DIVIDER,57,206,246,13,ES_AUTOHSCROLL
+ LTEXT "Info:",IDC_STATIC,11,225,42,10
+ EDITTEXT IDC_T_INFO,57,222,246,13,ES_AUTOHSCROLL
+END
+
+IDD_OPT_SBAR DIALOGEX 0, 0, 178, 212
+STYLE DS_FIXEDSYS | DS_CENTER | WS_CHILD
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg", 0, 0, 0x1
+BEGIN
+ GROUPBOX "Status Bar",IDC_STATIC,4,4,170,204
+ CONTROL "Show status bar",IDC_SHOWSBAR,"Button",BS_AUTOCHECKBOX |
+ WS_TABSTOP,12,17,152,10
+ CONTROL "Show icons",IDC_SHOWICON,"Button",BS_AUTOCHECKBOX |
+ WS_TABSTOP,22,33,142,10
+ CONTROL "Show protocol names",IDC_SHOWPROTO,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,22,48,142,10
+ CONTROL "Show status text",IDC_SHOWSTATUS,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,22,63,142,10
+ CONTROL "Right click opens status menu",IDC_RIGHTSTATUS,"Button",
+ BS_AUTORADIOBUTTON,22,160,142,10
+ CONTROL "Right click opens Miranda IM menu",IDC_RIGHTMIRANDA,
+ "Button",BS_AUTORADIOBUTTON,22,147,142,10
+ CONTROL "Make sections equal width",IDC_EQUALSECTIONS,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,22,78,142,10
+ CONTROL "Show bevels on panels",IDC_SBPANELBEVEL,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,22,94,107,10
+ CONTROL "Show resize grip indicator",IDC_SHOWGRIP,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,22,110,96,10
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// DESIGNINFO
+//
+
+#ifdef APSTUDIO_INVOKED
+GUIDELINES DESIGNINFO MOVEABLE PURE
+BEGIN
+ IDD_DELETECONTACT, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 277
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 85
+ END
+
+ IDD_OPT_HOTKEY, DIALOG
+ BEGIN
+ LEFTMARGIN, 4
+ RIGHTMARGIN, 234
+ TOPMARGIN, 4
+ BOTTOMMARGIN, 132
+ HORZGUIDE, 23
+ HORZGUIDE, 40
+ HORZGUIDE, 57
+ END
+
+ IDD_OPT_CLIST, DIALOG
+ BEGIN
+ LEFTMARGIN, 2
+ RIGHTMARGIN, 312
+ VERTGUIDE, 10
+ VERTGUIDE, 107
+ TOPMARGIN, 2
+ BOTTOMMARGIN, 202
+ HORZGUIDE, 179
+ HORZGUIDE, 196
+ END
+
+ IDD_OPT_CLUI, DIALOG
+ BEGIN
+ LEFTMARGIN, 4
+ RIGHTMARGIN, 309
+ VERTGUIDE, 13
+ VERTGUIDE, 23
+ VERTGUIDE, 112
+ VERTGUIDE, 141
+ VERTGUIDE, 151
+ VERTGUIDE, 175
+ VERTGUIDE, 242
+ VERTGUIDE, 246
+ TOPMARGIN, 4
+ BOTTOMMARGIN, 241
+ HORZGUIDE, 18
+ HORZGUIDE, 39
+ HORZGUIDE, 75
+ HORZGUIDE, 180
+ HORZGUIDE, 196
+ END
+
+ IDD_OPT_CLC, DIALOG
+ BEGIN
+ LEFTMARGIN, 2
+ RIGHTMARGIN, 312
+ VERTGUIDE, 11
+ VERTGUIDE, 303
+ TOPMARGIN, 2
+ BOTTOMMARGIN, 240
+ END
+
+ IDD_OPT_SBAR, DIALOG
+ BEGIN
+ LEFTMARGIN, 4
+ RIGHTMARGIN, 174
+ VERTGUIDE, 12
+ VERTGUIDE, 22
+ VERTGUIDE, 164
+ TOPMARGIN, 4
+ BOTTOMMARGIN, 208
+ END
+END
+#endif // APSTUDIO_INVOKED
+
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE MOVEABLE PURE
+BEGIN
+ "resource.h\0"
+END
+
+2 TEXTINCLUDE MOVEABLE PURE
+BEGIN
+ "#include <windows.h>\r\n"
+ "#include <winres.h>\r\n"
+ "#include <statusmodes.h>\r\n"
+ "\0"
+END
+
+3 TEXTINCLUDE MOVEABLE PURE
+BEGIN
+ "\r\n"
+ "\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Icon
+//
+
+// Icon with lowest ID value placed first to ensure application icon
+// remains consistent on all systems.
+IDI_RENAME ICON DISCARDABLE "res\\rename.ico"
+IDI_BLANK ICON DISCARDABLE "res\\blank.ico"
+IDI_DELETE ICON DISCARDABLE "res\\delete.ico"
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Cursor
+//
+
+IDC_HYPERLINKHAND CURSOR DISCARDABLE "res\\hyperlin.cur"
+IDC_DROP CURSOR DISCARDABLE "res\\dragcopy.cur"
+IDC_DROPUSER CURSOR DISCARDABLE "res\\dropuser.cur"
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Menu
+//
+
+IDR_CLISTMENU MENU DISCARDABLE
+BEGIN
+ POPUP "&¤"
+ BEGIN
+ MENUITEM SEPARATOR
+ MENUITEM "E&xit", ID_ICQ_EXIT
+ END
+ POPUP "&Status"
+ BEGIN
+ MENUITEM "&Offline\tCtrl+0", ID_STATUS_OFFLINE, CHECKED
+ MENUITEM "On&line\tCtrl+1", ID_STATUS_ONLINE
+ MENUITEM "&Away\tCtrl+2", ID_STATUS_AWAY
+ MENUITEM "&NA\tCtrl+3", ID_STATUS_NA
+ MENUITEM "Occ&upied\tCtrl+4", ID_STATUS_OCCUPIED
+ MENUITEM "&DND\tCtrl+5", ID_STATUS_DND
+ MENUITEM "&Free for chat\tCtrl+6", ID_STATUS_FREECHAT
+ MENUITEM "&Invisible\tCtrl+7", ID_STATUS_INVISIBLE
+ MENUITEM "On the &Phone\tCtrl+8", ID_STATUS_ONTHEPHONE
+ MENUITEM "Out to &Lunch\tCtrl+9", ID_STATUS_OUTTOLUNCH
+ END
+END
+
+IDR_CONTEXT MENU DISCARDABLE
+BEGIN
+ POPUP "Tray"
+ BEGIN
+ MENUITEM "&Hide/Show", ID_TRAY_HIDE
+ MENUITEM SEPARATOR
+ MENUITEM "E&xit", ID_TRAY_EXIT
+ END
+ POPUP "Nowhere"
+ BEGIN
+ MENUITEM "&New Group", POPUP_NEWGROUP
+ MENUITEM SEPARATOR
+ MENUITEM "&Hide Offline Users", POPUP_HIDEOFFLINE
+ MENUITEM "Hide &Offline Users out here", POPUP_HIDEOFFLINEROOT
+ MENUITEM "Hide &Empty Groups", POPUP_HIDEEMPTYGROUPS
+ MENUITEM "Disable &Groups", POPUP_DISABLEGROUPS
+ MENUITEM SEPARATOR
+ MENUITEM "Hide Miranda", POPUP_HIDEMIRANDA
+ END
+ POPUP "Group"
+ BEGIN
+ MENUITEM "&New Subgroup", POPUP_NEWSUBGROUP
+ MENUITEM "&Hide Offline Users in here", POPUP_GROUPHIDEOFFLINE
+ MENUITEM SEPARATOR
+ MENUITEM "&Rename Group", POPUP_RENAMEGROUP
+ MENUITEM "&Delete Group", POPUP_DELETEGROUP
+ END
+END
+
+#endif // English (U.S.) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
+
diff --git a/Plugins/eSpeak/Docs/langpack_meSpeak.txt b/Plugins/eSpeak/Docs/langpack_meSpeak.txt
new file mode 100644
index 0000000..ffca544
--- /dev/null
+++ b/Plugins/eSpeak/Docs/langpack_meSpeak.txt
@@ -0,0 +1,2 @@
+; meSpeak
+; Author: Pescuma
diff --git a/Plugins/eSpeak/Docs/meSpeak.png b/Plugins/eSpeak/Docs/meSpeak.png
new file mode 100644
index 0000000..dc0451e
--- /dev/null
+++ b/Plugins/eSpeak/Docs/meSpeak.png
Binary files differ
diff --git a/Plugins/eSpeak/Docs/meSpeak_changelog.txt b/Plugins/eSpeak/Docs/meSpeak_changelog.txt
new file mode 100644
index 0000000..2a05bb3
--- /dev/null
+++ b/Plugins/eSpeak/Docs/meSpeak_changelog.txt
@@ -0,0 +1,57 @@
+meSpeak
+
+Changelog:
+
+. 0.2.0.0
+ + Added SAPI voices
+ + Added option to truncate text by size
+
+. 0.0.0.12
+ * Another fix for stop speaking bug
+
+. 0.0.0.11
+ * Fix for stop speaking bug
+
+. 0.0.0.10
+ * Fix for overlaping options
+ + Option to disable selection of variant based on genre
+
+. 0.0.0.9
+ + Updated to eSpeak 1.39
+ + Option to only talk when idle
+ + Now the types are disabled by default
+ + If contact has gender, select a variant based on it (male1 or female1)
+
+. 0.0.0.8
+ * Fix for test button in contact options to use punctuation data
+ - Removed old mlog function
+
+. 0.0.0.7
+ * Fix to make contacts use system voice and variant if none is set for them
+
+. 0.0.0.6
+ + Updated to eSpeak 1.31
+ + Added punctuation to system settings
+ + Added option to respect SndVol mute (enabled by default)
+ * Fix for variants
+
+. 0.0.0.5
+ * Changed to create services in Load method
+ * Language names should be translatable now
+
+. 0.0.0.4
+ + Added function to Variables plugin to allow reading text
+
+. 0.0.0.3
+ + Option to prepend name of contact for event types without template
+ * Fix for no voices found
+ * Fix to translate dialog
+
+. 0.0.0.2
+ * Fix for test speak (while other speak happening at the same time)
+ * Fix for spelling words with accents
+ + Added options for volume, rate, pitch and range
+ + Renamed plugin to meSpeak
+
+. 0.0.0.1
+ + Initial version \ No newline at end of file
diff --git a/Plugins/eSpeak/Docs/meSpeak_readme.txt b/Plugins/eSpeak/Docs/meSpeak_readme.txt
new file mode 100644
index 0000000..8ad24ea
--- /dev/null
+++ b/Plugins/eSpeak/Docs/meSpeak_readme.txt
@@ -0,0 +1,30 @@
+meSpeak plugin
+--------------
+
+CAUTION: THIS IS A BETA STAGE PLUGIN. IT CAN DO VERY BAD THINGS. USE AT YOUR OWN RISK.
+
+
+This is a service plugin that read text aloud. It is based on eSpeak engine (http://espeak.sourceforge.net/) and also support SAPI voices (ver 5.1).
+
+It provides the following functionality:
+- System voice
+- Per contact voice
+- Options to disable by global status
+- Packed with voices for a lot of languages
+- "fake" support for plugins that support Variables
+
+This is a service plugin, so it does not read any text by itself. Other plugins need to request it!
+
+It also has an iconpack of flags with it. It is built using famfamfam's icons. If you want other set of flags or to download then separated, they can be found here:
+- famfamfam's icons as .ico: http://pescuma.mirandaim.ru/miranda/flags-famfamfam.zip (note that there are a lot of files inside this zip with wrong names. It happens because I don't know which languages they represent - and if they represent a language or not. So, if you think some file name must change, please tell me)
+- famfamfam's icons as .dll: http://pescuma.mirandaim.ru/miranda/flags-dll-famfamfam.zip
+- Angeli-Ka's icons as .ico: http://pescuma.mirandaim.ru/miranda/flags-angelika.zip
+- Angeli-Ka's icons as .ico with language names: http://pescuma.mirandaim.ru/miranda/flags-angelika-name.zip
+- Angeli-Ka's icons as .dll: http://pescuma.mirandaim.ru/miranda/flags-dll-angelika.zip
+
+Additional dictionary data is available for languages:
+Russian, Cantonese at http://espeak.sourceforge.net/data/index.html
+
+Many thanks to the eSpeak team for their engine and to the famfamfam.com site for the icons.
+
+To report bugs/make suggestions, go to the forum thread: http://forums.miranda-im.org/showthread.php?t=16188 \ No newline at end of file
diff --git a/Plugins/eSpeak/Docs/meSpeak_version.txt b/Plugins/eSpeak/Docs/meSpeak_version.txt
new file mode 100644
index 0000000..84eff82
--- /dev/null
+++ b/Plugins/eSpeak/Docs/meSpeak_version.txt
@@ -0,0 +1 @@
+meSpeak 0.2.0.0 \ No newline at end of file
diff --git a/Plugins/eSpeak/ZIP/doit.bat b/Plugins/eSpeak/ZIP/doit.bat
new file mode 100644
index 0000000..17b501a
--- /dev/null
+++ b/Plugins/eSpeak/ZIP/doit.bat
@@ -0,0 +1,132 @@
+rem @echo off
+
+rem Batch file to build and upload files
+rem
+rem TODO: Integration with FL
+
+set name=meSpeak
+
+rem To upload, this var must be set here or in other batch
+rem set ftp=ftp://<user>:<password>@<ftp>/<path>
+
+echo Building %name% ...
+
+msdev ..\eSpeak.dsp /MAKE "eSpeak - Win32 Release" /REBUILD
+msdev ..\eSpeak.dsp /MAKE "eSpeak - Win32 Unicode Release" /REBUILD
+
+echo Generating files for %name% ...
+
+del *.zip
+del *.dll
+copy ..\Docs\%name%_changelog.txt
+copy ..\Docs\%name%_version.txt
+copy ..\Docs\%name%_readme.txt
+mkdir Plugins
+cd Plugins
+del /Q *.*
+copy ..\..\..\..\bin\release\Plugins\%name%.dll
+cd ..
+mkdir Docs
+cd Docs
+del /Q *.*
+copy ..\..\Docs\%name%_readme.txt
+rem copy ..\..\Docs\langpack_%name%.txt
+rem copy ..\..\Docs\helppack_%name%.txt
+copy ..\..\m_speak.h
+cd ..
+mkdir Dictionaries
+cd Dictionaries
+mkdir Voice
+cd Voice
+xcopy ..\..\..\espeak-data\*.* /S
+cd ..
+cd ..
+mkdir Icons
+cd Icons
+copy ..\..\..\spellchecker\Flags\flags.dll
+cd ..
+mkdir src
+cd src
+del /Q *.*
+copy ..\..\*.h
+copy ..\..\*.c*
+copy ..\..\*.
+copy ..\..\*.rc
+copy ..\..\*.dsp
+copy ..\..\*.dsw
+mkdir Docs
+cd Docs
+del /Q *.*
+copy ..\..\..\Docs\*.*
+cd ..
+mkdir sdk
+cd sdk
+del /Q *.*
+copy ..\..\..\sdk\*.*
+cd ..
+mkdir lib
+cd lib
+del /Q *.*
+copy ..\..\..\lib\*.*
+cd ..
+mkdir res
+cd res
+del /Q *.*
+copy ..\..\..\res\*.*
+cd ..
+mkdir eSpeak
+cd eSpeak
+del /Q *.*
+copy ..\..\..\eSpeak\*.*
+cd ..
+cd ..
+copy ..\Release\%name%.pdb
+copy ..\Unicode_Release\%name%W.pdb
+
+
+"C:\Program Files\Filzip\Filzip.exe" -a -rp %name%.zip Plugins Docs Icons Dictionaries
+
+cd Plugins
+del /Q *.*
+copy ..\..\..\..\bin\release\Plugins\%name%W.dll
+cd ..
+
+"C:\Program Files\Filzip\Filzip.exe" -a -rp %name%W.zip Plugins Docs Icons Dictionaries
+
+"C:\Program Files\Filzip\Filzip.exe" -a -rp %name%_src.zip src\*.*
+"C:\Program Files\Filzip\Filzip.exe" -a -rp %name%.pdb.zip %name%.pdb
+"C:\Program Files\Filzip\Filzip.exe" -a -rp %name%W.pdb.zip %name%W.pdb
+
+del *.pdb
+rd /S /Q Plugins
+rd /S /Q Docs
+rd /S /Q Dictionaries
+rd /S /Q src
+rd /S /Q Icons
+
+if "%ftp%"=="" GOTO END
+
+echo Going to upload files...
+pause
+
+"C:\Program Files\FileZilla\FileZilla.exe" -u .\%name%.zip %ftp% -overwrite -close
+"C:\Program Files\FileZilla\FileZilla.exe" -u .\%name%W.zip %ftp% -overwrite -close
+"C:\Program Files\FileZilla\FileZilla.exe" -u .\%name%.pdb.zip %ftp% -overwrite -close
+"C:\Program Files\FileZilla\FileZilla.exe" -u .\%name%W.pdb.zip %ftp% -overwrite -close
+"C:\Program Files\FileZilla\FileZilla.exe" -u .\%name%_changelog.txt %ftp% -overwrite -close
+"C:\Program Files\FileZilla\FileZilla.exe" -u .\%name%_version.txt %ftp% -overwrite -close
+"C:\Program Files\FileZilla\FileZilla.exe" -u .\%name%_readme.txt %ftp% -overwrite -close
+
+if "%ftp2%"=="" GOTO END
+
+"C:\Program Files\FileZilla\FileZilla.exe" -u .\%name%.zip %ftp2% -overwrite -close
+"C:\Program Files\FileZilla\FileZilla.exe" -u .\%name%W.zip %ftp2% -overwrite -close
+"C:\Program Files\FileZilla\FileZilla.exe" -u .\%name%.pdb.zip %ftp2% -overwrite -close
+"C:\Program Files\FileZilla\FileZilla.exe" -u .\%name%W.pdb.zip %ftp2% -overwrite -close
+"C:\Program Files\FileZilla\FileZilla.exe" -u .\%name%_changelog.txt %ftp2% -overwrite -close
+"C:\Program Files\FileZilla\FileZilla.exe" -u .\%name%_version.txt %ftp2% -overwrite -close
+"C:\Program Files\FileZilla\FileZilla.exe" -u .\%name%_readme.txt %ftp2% -overwrite -close
+
+:END
+
+echo Done.
diff --git a/Plugins/eSpeak/commons.h b/Plugins/eSpeak/commons.h
new file mode 100644
index 0000000..c288320
--- /dev/null
+++ b/Plugins/eSpeak/commons.h
@@ -0,0 +1,243 @@
+/*
+Copyright (C) 2007 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#ifndef __COMMONS_H__
+# define __COMMONS_H__
+
+
+#define OEMRESOURCE
+#include <windows.h>
+#include <tchar.h>
+#include <stdio.h>
+#include <time.h>
+#include <commctrl.h>
+#include <sapi.h>
+#include <vector>
+
+
+// Miranda headers
+#define MIRANDA_VER 0x0700
+#include <newpluginapi.h>
+#include <m_system.h>
+#include <m_system_cpp.h>
+#include <m_protocols.h>
+#include <m_protosvc.h>
+#include <m_clist.h>
+#include <m_contacts.h>
+#include <m_langpack.h>
+#include <m_database.h>
+#include <m_options.h>
+#include <m_utils.h>
+#include <m_updater.h>
+#include <m_metacontacts.h>
+#include <m_popup.h>
+#include <m_history.h>
+#include <m_message.h>
+#include <m_folders.h>
+#include <m_icolib.h>
+#include <m_userinfo.h>
+#include <m_idle.h>
+
+#include "../utils/mir_memory.h"
+#include "../utils/mir_options.h"
+#include "../utils/mir_icons.h"
+#include "../utils/mir_buffer.h"
+#include "../utils/ContactAsyncQueue.h"
+#include "../utils/utf8_helpers.h"
+
+#include "resource.h"
+#include "m_speak.h"
+#include "options.h"
+#include "types.h"
+
+#include "eSpeak/speak_lib.h"
+
+
+#define MODULE_NAME "meSpeak"
+
+
+// Global Variables
+extern HINSTANCE hInst;
+extern PLUGINLINK *pluginLink;
+
+
+#define MAX_REGS(_A_) ( sizeof(_A_) / sizeof(_A_[0]) )
+#define RELEASE(_A_) if (_A_ != NULL) { _A_->Release(); _A_ = NULL; }
+
+
+#define ICON_SIZE 16
+#define NAME_SIZE 128
+
+#define GENDER_UNKNOWN 0
+#define GENDER_MALE 1
+#define GENDER_FEMALE 2
+
+int SortVoices(const Voice *voice1, const Voice *voice2);
+
+enum Engine
+{
+ ENGINE_ESPEAK,
+ ENGINE_SAPI
+};
+
+class Variant
+{
+public:
+ TCHAR name[NAME_SIZE];
+ int gender;
+ TCHAR id[NAME_SIZE];
+
+ Variant(const TCHAR *aName, int aGender, const TCHAR *anId)
+ {
+ lstrcpyn(name, aName, MAX_REGS(name));
+ gender = aGender;
+ lstrcpyn(id, anId, MAX_REGS(id));
+ }
+};
+
+
+class Voice
+{
+public:
+ Engine engine;
+ TCHAR name[NAME_SIZE];
+ int prio;
+ int gender;
+ TCHAR age[NAME_SIZE];
+ TCHAR id[NAME_SIZE];
+
+ Voice(Engine anEngine, const TCHAR *aName, int aPrio, int aGender, const TCHAR *anAge, const TCHAR *anId)
+ {
+ engine = anEngine;
+ lstrcpyn(name, aName, MAX_REGS(name));
+ prio = aPrio;
+ gender = aGender;
+ lstrcpyn(age, anAge, MAX_REGS(age));
+ lstrcpyn(id, anId, MAX_REGS(id));
+ }
+};
+
+
+class Language
+{
+public:
+ TCHAR language[NAME_SIZE];
+ TCHAR localized_name[NAME_SIZE];
+ TCHAR english_name[NAME_SIZE];
+ TCHAR full_name[NAME_SIZE];
+
+ LIST<Voice> voices;
+
+ Language(TCHAR *aLanguage)
+ : voices(5, SortVoices)
+ {
+ lstrcpyn(language, aLanguage, MAX_REGS(language));
+ localized_name[0] = _T('\0');
+ english_name[0] = _T('\0');
+ full_name[0] = _T('\0');
+ }
+};
+
+
+#define SCROLL 0
+#define COMBO 1
+
+struct RANGE
+{
+ int min;
+ int max;
+ int def;
+};
+
+static struct {
+ espeak_PARAMETER eparam;
+ RANGE espeak;
+ RANGE sapi;
+ char *setting;
+ int ctrl;
+ int label;
+ int type;
+} PARAMETERS[] = {
+ { espeakRATE, { 80, 389, 165 }, { -10, 10, 0 }, "Rate", IDC_RATE, IDC_RATE_L, SCROLL },
+ { espeakVOLUME, { 10, 190, 100 }, { 0, 100, 100 }, "Volume", IDC_VOLUME, IDC_VOLUME_L, SCROLL },
+ { espeakPITCH, { 0, 99, 50 }, { 0, -1, 0 }, "Pitch", IDC_PITCH, IDC_PITCH_L, SCROLL },
+ { espeakRANGE, { -100, 99, 50 }, { 0, -1, 0 }, "Range", IDC_RANGE, IDC_RANGE_L, SCROLL },
+ { espeakPUNCTUATION, { espeakPUNCT_NONE, espeakPUNCT_SOME, espeakPUNCT_SOME }, { 0, -1, 0 }, "Punctuation", IDC_PUNCT, IDC_PUNCT_L, COMBO }
+};
+
+#define NUM_PARAMETERS MAX_REGS(PARAMETERS)
+
+class SpeakData
+{
+public:
+ Language *lang;
+ Voice *voice;
+ Variant *variant;
+ TCHAR *text;
+ int parameters[NUM_PARAMETERS];
+
+ SpeakData(Language *aLang, Voice *aVoice, Variant *aVariant, TCHAR *aText)
+ {
+ lang = aLang;
+ voice = aVoice;
+ variant = aVariant;
+ text = aText;
+ }
+
+ void setParameter(int param, int value)
+ {
+ parameters[param] = value;
+ }
+
+ int getParameter(int param)
+ {
+ return parameters[param];
+ }
+};
+
+
+extern LIST<Language> languages;
+extern LIST<Variant> variants;
+extern ContactAsyncQueue *queue;
+
+
+int SpeakService(HANDLE hContact, TCHAR *text);
+void Speak(SpeakData *data);
+
+Language *GetLanguage(TCHAR *language, BOOL create = FALSE);
+
+Language *GetContactLanguage(HANDLE hContact);
+Voice *GetContactVoice(HANDLE hContact, Language *lang);
+Variant *GetContactVariant(HANDLE hContact);
+int GetContactParam(HANDLE hContact, int param);
+void SetContactParam(HANDLE hContact, int param, int value);
+
+void GetLangPackLanguage(TCHAR *name, size_t len);
+
+int SAPI_GetDefaultRateFor(TCHAR *id);
+
+HICON LoadIconEx(Language *lang, BOOL copy = FALSE);
+
+#define SPEAK_NAME "SpeakName"
+#define TEMPLATE_ENABLED "Enabled"
+#define TEMPLATE_TEXT "Text"
+
+
+#endif // __COMMONS_H__
diff --git a/Plugins/eSpeak/eSpeak.cpp b/Plugins/eSpeak/eSpeak.cpp
new file mode 100644
index 0000000..609c485
--- /dev/null
+++ b/Plugins/eSpeak/eSpeak.cpp
@@ -0,0 +1,1348 @@
+/*
+Copyright (C) 2006 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+#include "commons.h"
+#include "comutil.h"
+
+
+// Prototypes ///////////////////////////////////////////////////////////////////////////
+
+
+PLUGININFOEX pluginInfo={
+ sizeof(PLUGININFOEX),
+#ifdef UNICODE
+ "meSpeak (Unicode)",
+#else
+ "meSpeak",
+#endif
+ PLUGIN_MAKE_VERSION(0,2,0,0),
+ "Speaker plugin based on eSpeak engine (%s)",
+ "Ricardo Pescuma Domenecci",
+ "",
+ "© 2007-2009 Ricardo Pescuma Domenecci",
+ "http://pescuma.org/miranda/meSpeak",
+ UNICODE_AWARE,
+ 0, //doesn't replace anything built-in
+#ifdef UNICODE
+ { 0x9c1fad62, 0x8f94, 0x484b, { 0x8e, 0x96, 0xff, 0x6d, 0xa4, 0xcd, 0x98, 0x95 } } // {9C1FAD62-8F94-484b-8E96-FF6DA4CD9895}
+#else
+ { 0xb8b98db2, 0xa8e5, 0x4501, { 0x91, 0xf5, 0x6f, 0x3b, 0x44, 0x34, 0x81, 0xa8 } } // {B8B98DB2-A8E5-4501-91F5-6F3B443481A8}
+#endif
+};
+
+
+HINSTANCE hInst;
+PLUGINLINK *pluginLink;
+LIST_INTERFACE li;
+struct MM_INTERFACE mmi;
+struct UTF8_INTERFACE utfi;
+
+static std::vector<HANDLE> hHooks;
+static std::vector<HANDLE> hServices;
+
+LIST<Language> languages(20);
+LIST<Variant> variants(20);
+ContactAsyncQueue *queue;
+
+HANDLE hDictionariesFolder = NULL;
+TCHAR dictionariesFolder[1024];
+
+HANDLE hFlagsDllFolder = NULL;
+TCHAR flagsDllFolder[1024];
+
+HBITMAP hCheckedBmp;
+BITMAP bmpChecked;
+
+char *metacontacts_proto = NULL;
+BOOL loaded = FALSE;
+
+int ModulesLoaded(WPARAM wParam, LPARAM lParam);
+int PreShutdown(WPARAM wParam, LPARAM lParam);
+
+int SpeakAService(WPARAM wParam, LPARAM lParam);
+int SpeakWService(WPARAM wParam, LPARAM lParam);
+TCHAR * VariablesSpeak(ARGUMENTSINFO *ai);
+
+LRESULT CALLBACK MenuWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
+
+void LoadVoices();
+
+Language *GetClosestLanguage(TCHAR *lang_name);
+
+void Speak(HANDLE hContact, void *param);
+
+TCHAR *aditionalLanguages[] = {
+ _T("en_R"), _T("English (Rhotic)"),
+ _T("en_SC"), _T("English (Scottish)"),
+ _T("en_UK"), _T("English - UK"),
+ _T("en_UK_NORTH"), _T("English - UK (Northern)"),
+ _T("en_UK_RP"), _T("English - UK (Received Pronunciation)"),
+ _T("en_UK_WMIDS"), _T("English - UK (West Midlands)"),
+ _T("en_WI"), _T("English - Westindies"),
+ _T("es_LA"), _T("Spanish - Latin American"),
+ _T("eo"), _T("Esperanto"),
+ _T("la"), _T("Latin"),
+ _T("no"), _T("Norwegian"),
+ _T("jbo"), _T("Lojban"),
+ _T("sr"), _T("Serbian"),
+ _T("grc"), _T("Ancient Greek"),
+ _T("yue"), _T("Cantonese"),
+ _T("ku"), _T("Kurdish"),
+ _T("hbs"), _T("Serbo-Croatian"),
+ _T("zh_YUE"), _T("Chinese - Cantonese"),
+};
+
+
+BOOL shutDown = FALSE;
+
+
+// Functions ////////////////////////////////////////////////////////////////////////////
+
+
+extern "C" BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
+{
+ hInst = hinstDLL;
+ return TRUE;
+}
+
+static void FixPluginDescription()
+{
+ static char description[128];
+ _snprintf(description, MAX_REGS(description), "Speaker plugin based on eSpeak engine (%s)", espeak_Info(NULL));
+ description[MAX_REGS(description)-1] = '\0';
+ pluginInfo.description = description;
+}
+
+extern "C" __declspec(dllexport) PLUGININFO* MirandaPluginInfo(DWORD mirandaVersion)
+{
+ pluginInfo.cbSize = sizeof(PLUGININFO);
+ FixPluginDescription();
+ return (PLUGININFO*) &pluginInfo;
+}
+
+
+extern "C" __declspec(dllexport) PLUGININFOEX* MirandaPluginInfoEx(DWORD mirandaVersion)
+{
+ pluginInfo.cbSize = sizeof(PLUGININFOEX);
+ FixPluginDescription();
+ return &pluginInfo;
+}
+
+
+static const MUUID interfaces[] = { MIID_SPEAK, MIID_LAST };
+extern "C" __declspec(dllexport) const MUUID* MirandaPluginInterfaces(void)
+{
+ return interfaces;
+}
+
+
+extern "C" int __declspec(dllexport) Load(PLUGINLINK *link)
+{
+ pluginLink = link;
+
+ CHECK_VERSION( MODULE_NAME )
+
+ mir_getMMI(&mmi);
+ mir_getUTFI(&utfi);
+ mir_getLI(&li);
+
+ // hooks
+ hHooks.push_back( HookEvent(ME_SYSTEM_MODULESLOADED, ModulesLoaded) );
+ hHooks.push_back( HookEvent(ME_SYSTEM_PRESHUTDOWN, PreShutdown) );
+
+ hCheckedBmp = LoadBitmap(NULL, MAKEINTRESOURCE(OBM_CHECK));
+ if (GetObject(hCheckedBmp, sizeof(bmpChecked), &bmpChecked) == 0)
+ bmpChecked.bmHeight = bmpChecked.bmWidth = 10;
+
+ InitTypes();
+
+ TCHAR mirandaFolder[1024];
+ GetModuleFileName(GetModuleHandle(NULL), mirandaFolder, MAX_REGS(mirandaFolder));
+ TCHAR *p = _tcsrchr(mirandaFolder, _T('\\'));
+ if (p != NULL)
+ *p = _T('\0');
+
+ // Folders plugin support
+ if (ServiceExists(MS_FOLDERS_REGISTER_PATH))
+ {
+ hDictionariesFolder = (HANDLE) FoldersRegisterCustomPathT(Translate("meSpeak"),
+ Translate("Languages"),
+ _T(MIRANDA_PATH) _T("\\Dictionaries\\Voice"));
+
+ FoldersGetCustomPathT(hDictionariesFolder, dictionariesFolder, MAX_REGS(dictionariesFolder), _T("."));
+
+ hFlagsDllFolder = (HANDLE) FoldersRegisterCustomPathT(Translate("meSpeak"),
+ Translate("Flags DLL"),
+ _T(MIRANDA_PATH) _T("\\Icons"));
+
+ FoldersGetCustomPathT(hFlagsDllFolder, flagsDllFolder, MAX_REGS(flagsDllFolder), _T("."));
+ }
+ else
+ {
+ mir_sntprintf(dictionariesFolder, MAX_REGS(dictionariesFolder), _T("%s\\Dictionaries\\Voice"), mirandaFolder);
+
+ mir_sntprintf(flagsDllFolder, MAX_REGS(flagsDllFolder), _T("%s\\Icons"), mirandaFolder);
+ }
+
+ LoadVoices();
+
+ hServices.push_back( CreateServiceFunction(MS_SPEAK_SAY_A, SpeakAService) );
+ hServices.push_back( CreateServiceFunction(MS_SPEAK_SAY_W, SpeakWService) );
+
+ queue = new ContactAsyncQueue(&Speak);
+
+ return 0;
+}
+
+extern "C" int __declspec(dllexport) Unload(void)
+{
+ FreeTypes();
+
+ DeleteObject(hCheckedBmp);
+
+ return 0;
+}
+
+
+HICON LoadIconEx(Language *lang, BOOL copy)
+{
+#ifdef UNICODE
+ char tmp[NAME_SIZE];
+ WideCharToMultiByte(CP_ACP, 0, lang->language, -1, tmp, MAX_REGS(tmp), NULL, NULL);
+ return LoadIconEx(tmp, copy);
+#else
+ return LoadIconEx(lang->language, copy);
+#endif
+}
+
+
+// Called when all the modules are loaded
+int ModulesLoaded(WPARAM wParam, LPARAM lParam)
+{
+ if (ServiceExists(MS_MC_GETPROTOCOLNAME))
+ metacontacts_proto = (char *) CallService(MS_MC_GETPROTOCOLNAME, 0, 0);
+
+ // add our modules to the KnownModules list
+ CallService("DBEditorpp/RegisterSingleModule", (WPARAM) MODULE_NAME, 0);
+
+ // updater plugin support
+ if(ServiceExists(MS_UPDATE_REGISTER))
+ {
+ Update upd = {0};
+ char szCurrentVersion[30];
+
+ upd.cbSize = sizeof(upd);
+ upd.szComponentName = pluginInfo.shortName;
+
+ upd.szUpdateURL = UPDATER_AUTOREGISTER;
+
+ upd.szBetaVersionURL = "http://pescuma.org/miranda/meSpeak_version.txt";
+ upd.szBetaChangelogURL = "http://pescuma.org/miranda/mespeak#Changelog";
+ upd.pbBetaVersionPrefix = (BYTE *)"meSpeak ";
+ upd.cpbBetaVersionPrefix = strlen((char *)upd.pbBetaVersionPrefix);
+#ifdef UNICODE
+ upd.szBetaUpdateURL = "http://pescuma.org/miranda/meSpeakW.zip";
+#else
+ upd.szBetaUpdateURL = "http://pescuma.org/miranda/meSpeak.zip";
+#endif
+
+ upd.pbVersion = (BYTE *)CreateVersionStringPlugin((PLUGININFO*) &pluginInfo, szCurrentVersion);
+ upd.cpbVersion = strlen((char *)upd.pbVersion);
+
+ CallService(MS_UPDATE_REGISTER, 0, (LPARAM)&upd);
+ }
+
+ InitOptions();
+
+ if (opts.use_flags)
+ {
+ // Load flags dll
+ TCHAR flag_file[1024];
+ _sntprintf(flag_file, MAX_REGS(flag_file), _T("%s\\flags.dll"), flagsDllFolder);
+ flag_file[1023] = 0;
+ HMODULE hFlagsDll = LoadLibrary(flag_file);
+
+ char path[1024];
+ GetModuleFileNameA(hInst, path, MAX_REGS(path));
+
+ SKINICONDESC sid = {0};
+ sid.cbSize = sizeof(SKINICONDESC);
+ sid.pszDefaultFile = path;
+ sid.flags = SIDF_TCHAR | SIDF_SORTED;
+ sid.ptszSection = TranslateT("Languages/Flags");
+
+ // Get language flags
+ for (int i = 0; i < languages.getCount(); i++)
+ {
+ sid.ptszDescription = languages[i]->full_name;
+#ifdef UNICODE
+ char lang[32];
+ mir_snprintf(lang, MAX_REGS(lang), "%S", languages[i]->language);
+ sid.pszName = lang;
+#else
+ sid.pszName = languages[i]->language;
+#endif
+
+ HICON hFlag = LoadIconEx(sid.pszName);
+ if (hFlag != NULL)
+ {
+ // Already registered
+ ReleaseIconEx(hFlag);
+ continue;
+ }
+
+ if (hFlagsDll != NULL)
+ {
+ hFlag = (HICON) LoadImage(hFlagsDll, languages[i]->language, IMAGE_ICON, 16, 16, 0);
+
+ if (hFlag == NULL)
+ {
+ TCHAR tmp[NAME_SIZE];
+ lstrcpyn(tmp, languages[i]->language, MAX_REGS(tmp));
+ do
+ {
+ TCHAR *p = _tcsrchr(tmp, _T('_'));
+ if (p == NULL)
+ break;
+
+ *p = _T('\0');
+ hFlag = (HICON) LoadImage(hFlagsDll, tmp, IMAGE_ICON, 16, 16, 0);
+ }
+ while(hFlag == NULL);
+ }
+ }
+ else
+ hFlag = NULL;
+
+ if (hFlag != NULL)
+ {
+ sid.hDefaultIcon = hFlag;
+ sid.pszDefaultFile = NULL;
+ sid.iDefaultIndex = 0;
+ }
+ else
+ {
+ sid.hDefaultIcon = NULL;
+ sid.pszDefaultFile = path;
+ sid.iDefaultIndex = - IDI_UNKNOWN_FLAG;
+ }
+
+ // Oki, lets add to IcoLib, then
+ CallService(MS_SKIN2_ADDICON, 0, (LPARAM)&sid);
+
+ if (hFlag != NULL)
+ DestroyIcon(hFlag);
+ }
+ FreeLibrary(hFlagsDll);
+ }
+
+ // Variables support
+ if (ServiceExists(MS_VARS_REGISTERTOKEN))
+ {
+ TOKENREGISTER tr = {0};
+ tr.cbSize = sizeof(TOKENREGISTER);
+ tr.memType = TR_MEM_MIRANDA;
+ tr.flags = TRF_FREEMEM | TRF_PARSEFUNC | TRF_FUNCTION | TRF_TCHAR;
+
+ tr.tszTokenString = _T("speak");
+ tr.parseFunctionT = VariablesSpeak;
+ tr.szHelpText = "Speak\t(x,[y])\tSpeak the text x using the y contact voice (y is optional)";
+ CallService(MS_VARS_REGISTERTOKEN, 0, (LPARAM) &tr);
+ }
+
+ loaded = TRUE;
+
+ return 0;
+}
+
+
+Language *GetLanguage(TCHAR *language, BOOL create)
+{
+ for (int i = 0; i < languages.getCount(); i++)
+ {
+ Language *lang = languages[i];
+ if (lstrcmpi(lang->language, language) == 0)
+ return lang;
+ }
+
+ if (create)
+ {
+ Language *lang = new Language(language);
+ languages.insert(lang);
+ return lang;
+ }
+
+ return NULL;
+}
+
+
+int SortVoices(const Voice *voice1, const Voice *voice2)
+{
+ return (int) voice1->prio - (int) voice2->prio;
+}
+
+
+// To get the names of the languages
+BOOL CALLBACK EnumLocalesProc(LPTSTR lpLocaleString)
+{
+ TCHAR *stopped = NULL;
+ USHORT langID = (USHORT) _tcstol(lpLocaleString, &stopped, 16);
+
+ TCHAR ini[32];
+ TCHAR end[32];
+ GetLocaleInfo(MAKELCID(langID, 0), LOCALE_SISO639LANGNAME, ini, MAX_REGS(ini));
+ GetLocaleInfo(MAKELCID(langID, 0), LOCALE_SISO3166CTRYNAME, end, MAX_REGS(end));
+
+ TCHAR name[64];
+ mir_sntprintf(name, MAX_REGS(name), _T("%s_%s"), ini, end);
+/*
+OutputDebugString(name);
+OutputDebugStringA(" : ");
+TCHAR tmp[128];
+GetLocaleInfo(MAKELCID(langID, 0), LOCALE_SENGLANGUAGE, tmp, MAX_REGS(tmp));
+OutputDebugString(tmp);
+OutputDebugStringA(" | ");
+GetLocaleInfo(MAKELCID(langID, 0), LOCALE_SLANGUAGE, tmp, MAX_REGS(tmp));
+OutputDebugString(tmp);
+OutputDebugStringA(" | ");
+GetLocaleInfo(MAKELCID(langID, 0), LOCALE_SNATIVELANGNAME , tmp, MAX_REGS(tmp));
+OutputDebugString(tmp);
+OutputDebugStringA("\n");
+*/
+ for (int i = 0; i < languages.getCount(); i++)
+ {
+ size_t len = lstrlen(languages[i]->language);
+ if (len > 2 && lstrcmpi(languages[i]->language, name) == 0)
+ {
+ GetLocaleInfo(MAKELCID(langID, 0), LOCALE_SLANGUAGE,
+ languages[i]->localized_name, MAX_REGS(languages[i]->localized_name));
+ GetLocaleInfo(MAKELCID(langID, 0), LOCALE_SENGLANGUAGE,
+ languages[i]->english_name, MAX_REGS(languages[i]->english_name));
+
+ if (languages[i]->localized_name[0] != _T('\0'))
+ {
+ mir_sntprintf(languages[i]->full_name, MAX_REGS(languages[i]->full_name),
+ _T("%s [%s]"), TranslateTS(languages[i]->localized_name), languages[i]->language);
+ }
+ }
+ else if (len == 2 && lstrcmpi(languages[i]->language, ini) == 0 && lstrcmpi(languages[i]->language, end) == 0)
+ {
+ GetLocaleInfo(MAKELCID(langID, 0), LOCALE_SENGLANGUAGE,
+ languages[i]->localized_name, MAX_REGS(languages[i]->localized_name));
+ GetLocaleInfo(MAKELCID(langID, 0), LOCALE_SENGLANGUAGE,
+ languages[i]->english_name, MAX_REGS(languages[i]->english_name));
+
+ if (languages[i]->localized_name[0] != _T('\0'))
+ {
+ mir_sntprintf(languages[i]->full_name, MAX_REGS(languages[i]->full_name),
+ _T("%s [%s]"), TranslateTS(languages[i]->localized_name), languages[i]->language);
+ }
+ }
+ else if (len == 2 && lstrcmpi(languages[i]->language, ini) == 0 && languages[i]->localized_name[0] == _T('\0'))
+ {
+ GetLocaleInfo(MAKELCID(langID, 0), LOCALE_SENGLANGUAGE,
+ languages[i]->localized_name, MAX_REGS(languages[i]->localized_name));
+ GetLocaleInfo(MAKELCID(langID, 0), LOCALE_SENGLANGUAGE,
+ languages[i]->english_name, MAX_REGS(languages[i]->english_name));
+
+ if (languages[i]->localized_name[0] != _T('\0'))
+ {
+ mir_sntprintf(languages[i]->full_name, MAX_REGS(languages[i]->full_name),
+ _T("%s [%s]"), TranslateTS(languages[i]->localized_name), languages[i]->language);
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+
+int SynthCallback(short*, int, espeak_EVENT*)
+{
+ return shutDown;
+}
+
+
+void LoadESpeak()
+{
+ char *tmp = mir_t2a(dictionariesFolder);
+
+ if (espeak_Initialize(AUDIO_OUTPUT_SYNCH_PLAYBACK, 0, tmp, 0) == EE_INTERNAL_ERROR)
+ {
+ MessageBox(NULL, _T("Error initializing eSpeak engine"), _T("meSpeak"), MB_OK | MB_ICONERROR);
+ mir_free(tmp);
+ return;
+ }
+
+ espeak_SetSynthCallback(SynthCallback);
+
+ mir_free(tmp);
+
+ const espeak_VOICE **voices = espeak_ListVoices(NULL);
+ const espeak_VOICE *voice;
+ int i;
+ for (i = 0; (voice = voices[i]) != NULL; i++)
+ {
+ Utf8ToTchar name = voice->name;
+ Utf8ToTchar id = voice->identifier;
+
+ const char *p = voice->languages;
+ while(*p != '\0')
+ {
+ size_t len = strlen(p+1);
+ Utf8ToTchar language = p + 1;
+
+ TCHAR *tmp = language;
+ while((tmp = _tcschr(tmp, _T('-'))) != NULL)
+ {
+ *tmp = _T('_');
+ CharUpper(tmp);
+ }
+
+ Language *lang = GetLanguage(language, TRUE);
+ lang->voices.insert(new Voice(ENGINE_ESPEAK, name, *p, voice->gender, _T(""), id));
+
+ p += len+2;
+ }
+ }
+
+ if (languages.getCount() <= 0)
+ return;
+
+ espeak_VOICE voice_select;
+ voice_select.languages = "variant";
+ voice_select.age = 0;
+ voice_select.gender = 0;
+ voice_select.name = NULL;
+
+ voices = espeak_ListVoices(&voice_select);
+ for (i = 0; (voice = voices[i]) != NULL; i++)
+ {
+ variants.insert(new Variant(Utf8ToTchar(voice->name), voice->gender, Utf8ToTchar(&voice->identifier[3])));
+ }
+}
+
+void LoadSAPI()
+{
+#define CHECK( _X_ ) hr = _X_; if(!SUCCEEDED(hr)) goto err
+
+ HRESULT hr = 0;
+
+ ISpeechVoice *voice = NULL;
+ ISpeechObjectTokens *voices = NULL;
+ ISpeechObjectToken *v = NULL;
+
+ long count = 0;
+ long i;
+
+ if (FAILED(CoInitialize(NULL)))
+ {
+ MessageBox(NULL, _T("Error to intiliaze COM"), _T(MODULE_NAME), MB_OK | MB_ICONERROR);
+ return;
+ }
+
+ CHECK( CoCreateInstance(CLSID_SpVoice, NULL, CLSCTX_ALL, IID_ISpeechVoice, (void **)&voice) );
+
+ CHECK( voice->GetVoices(NULL, NULL, &voices) );
+
+ CHECK( voices->get_Count(&count) );
+
+ for(i = 0; i < count; i++)
+ {
+ CHECK( voices->Item(i, &v) );
+ if (v == NULL)
+ continue;
+
+ BstrToTchar name;
+ CHECK( v->GetDescription(0, name) );
+
+ BstrToTchar id;
+ CHECK( v->get_Id(id) );
+
+ BstrToTchar languageId;
+ hr = v->GetAttribute(BstrToTchar(L"Language"), languageId);
+ if (hr != S_OK)
+ {
+ RELEASE(v);
+ continue;
+ }
+
+ TCHAR *stopped = NULL;
+ USHORT langID = (USHORT) _tcstol(languageId, &stopped, 16);
+
+ TCHAR ini[32];
+ TCHAR end[32];
+ GetLocaleInfo(MAKELCID(langID, 0), LOCALE_SISO639LANGNAME, ini, MAX_REGS(ini));
+ GetLocaleInfo(MAKELCID(langID, 0), LOCALE_SISO3166CTRYNAME, end, MAX_REGS(end));
+ CharUpper(end);
+
+ TCHAR language[64];
+ mir_sntprintf(language, MAX_REGS(language), _T("%s_%s"), ini, end);
+
+ BstrToTchar genderName;
+ hr = v->GetAttribute(BstrToTchar(L"Gender"), genderName);
+ int gender;
+ if (hr != S_OK)
+ gender = GENDER_UNKNOWN;
+ else if (lstrcmpi(_T("Male"), genderName) == 0)
+ gender = GENDER_MALE;
+ else if (lstrcmpi(_T("Female"), genderName) == 0)
+ gender = GENDER_FEMALE;
+ else
+ gender = GENDER_UNKNOWN;
+
+ BstrToTchar age;
+ hr = v->GetAttribute(BstrToTchar(L"Age"), age);
+ if (hr != S_OK)
+ age = L"";
+
+ Language *lang = GetLanguage(language, TRUE);
+ lang->voices.insert(new Voice(ENGINE_SAPI, name, 6, gender, age, id));
+
+ RELEASE( v );
+ }
+
+ goto cleanup;
+
+err:
+ MessageBox(NULL, _T("Error initializing SAPI"), _T(MODULE_NAME), MB_OK | MB_ICONERROR);
+
+cleanup:
+ RELEASE( v );
+ RELEASE( voices );
+ RELEASE( voice );
+ CoUninitialize();
+}
+
+void LoadVoices()
+{
+ LoadESpeak();
+ LoadSAPI();
+
+ if (languages.getCount() <= 0)
+ return;
+
+ EnumSystemLocales(EnumLocalesProc, LCID_SUPPORTED);
+
+ // Try to get name from DB
+ for(int i = 0; i < languages.getCount(); i++)
+ {
+ Language *lang = languages[i];
+ if (lang->full_name[0] == _T('\0'))
+ {
+ DBVARIANT dbv;
+#ifdef UNICODE
+ char tmp[NAME_SIZE];
+ WideCharToMultiByte(CP_ACP, 0, lang->language, -1, tmp, MAX_REGS(tmp), NULL, NULL);
+ if (!DBGetContactSettingTString(NULL, MODULE_NAME, tmp, &dbv))
+#else
+ if (!DBGetContactSettingTString(NULL, MODULE_NAME, lang->language, &dbv))
+#endif
+ {
+ lstrcpyn(lang->localized_name, dbv.ptszVal, MAX_REGS(lang->localized_name));
+ DBFreeVariant(&dbv);
+ }
+
+ if (lang->localized_name[0] == _T('\0'))
+ {
+ for(size_t j = 0; j < MAX_REGS(aditionalLanguages); j+=2)
+ {
+ if (lstrcmp(aditionalLanguages[j], lang->language) == 0)
+ {
+ lstrcpyn(lang->localized_name, aditionalLanguages[j+1], MAX_REGS(lang->localized_name));
+ break;
+ }
+ }
+ }
+
+ if (lang->localized_name[0] != _T('\0'))
+ {
+ mir_sntprintf(lang->full_name, MAX_REGS(lang->full_name),
+ _T("%s [%s]"), TranslateTS(lang->localized_name), lang->language);
+ }
+ else
+ {
+ lstrcpyn(lang->full_name, TranslateTS(lang->language), MAX_REGS(lang->full_name));
+ }
+ }
+ }
+/*
+ for (i = 0; i < languages.getCount(); i++)
+ {
+ Language *lang = languages[i];
+ OutputDebugString(lang->language);
+ OutputDebugStringA(" (");
+ OutputDebugString(lang->full_name);
+ OutputDebugStringA(") ");
+ OutputDebugStringA(":\n");
+ for (int j = 0; j < lang->voices.getCount(); j++)
+ {
+ char tmp[128];
+ Voice *voice = lang->voices[j];
+
+ OutputDebugStringA(" - ");
+ OutputDebugStringA(itoa(voice->prio, tmp, 10));
+ OutputDebugStringA(" : ");
+ OutputDebugStringA(voice->name);
+ OutputDebugStringA(" - ");
+ tmp[0] = voice->gender;
+ tmp[1] = 0;
+ OutputDebugStringA(tmp);
+ OutputDebugStringA("\n");
+ }
+ }
+*/
+}
+
+
+int PreShutdown(WPARAM wParam, LPARAM lParam)
+{
+ shutDown = TRUE;
+
+ delete queue;
+
+ DeInitOptions();
+
+ if (ServiceExists(MS_MSG_REMOVEICON))
+ {
+ StatusIconData sid = {0};
+ sid.cbSize = sizeof(sid);
+ sid.szModule = MODULE_NAME;
+ CallService(MS_MSG_REMOVEICON, 0, (LPARAM) &sid);
+ }
+
+ unsigned i;
+ for(i = 0; i < hHooks.size(); ++i)
+ UnhookEvent(hHooks[i]);
+
+ for(i = 0; i < hServices.size(); ++i)
+ DestroyServiceFunction(hServices[i]);
+
+
+ return 0;
+}
+
+
+void ToLocaleID(TCHAR *szKLName, size_t size)
+{
+ TCHAR *stopped = NULL;
+ USHORT langID = (USHORT) _tcstol(szKLName, &stopped, 16);
+
+ TCHAR ini[32];
+ TCHAR end[32];
+ GetLocaleInfo(MAKELCID(langID, 0), LOCALE_SISO639LANGNAME, ini, MAX_REGS(ini));
+ GetLocaleInfo(MAKELCID(langID, 0), LOCALE_SISO3166CTRYNAME, end, MAX_REGS(end));
+
+ mir_sntprintf(szKLName, size, _T("%s_%s"), ini, end);
+}
+
+
+Language * GetClosestLanguage(TCHAR *lang_name)
+{
+ // Search the language by name
+ for (int i = 0; i < languages.getCount(); i++)
+ {
+ if (lstrcmpi(languages[i]->language, lang_name) == 0)
+ {
+ return languages[i];
+ }
+ }
+
+ // Try searching by the prefix only
+ TCHAR *p = _tcschr(lang_name, _T('_'));
+ if (p != NULL)
+ {
+ *p = _T('\0');
+ for (int i = 0; i < languages.getCount(); i++)
+ {
+ if (lstrcmpi(languages[i]->language, lang_name) == 0)
+ {
+ *p = '_';
+ return languages[i];
+ }
+ }
+ *p = _T('_');
+ }
+
+ // Try any suffix, if one not provided
+ if (p == NULL)
+ {
+ size_t len = lstrlen(lang_name);
+ for (int i = 0; i < languages.getCount(); i++)
+ {
+ if (_tcsnicmp(languages[i]->language, lang_name, len) == 0
+ && languages[i]->language[len] == _T('_'))
+ {
+ return languages[i];
+ }
+ }
+ }
+
+ return NULL;
+}
+
+void GetUserProtoLanguageSetting(TCHAR *lang_name, HANDLE hContact, char *proto, char *setting)
+{
+ DBVARIANT dbv = {0};
+
+ if (!DBGetContactSettingTString(hContact, proto, setting, &dbv))
+ {
+ for (int i = 0; i < languages.getCount(); i++)
+ {
+ if (lstrcmpi(languages[i]->localized_name, dbv.ptszVal) == 0)
+ {
+ lstrcpyn(lang_name, languages[i]->language, NAME_SIZE);
+ break;
+ }
+ if (lstrcmpi(languages[i]->english_name, dbv.ptszVal) == 0)
+ {
+ lstrcpyn(lang_name, languages[i]->language, NAME_SIZE);
+ break;
+ }
+ }
+ DBFreeVariant(&dbv);
+ }
+}
+
+void GetUserLanguageSetting(TCHAR *lang_name, HANDLE hContact, char *setting)
+{
+ DBVARIANT dbv = {0};
+
+ char *proto = (char *) CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM) hContact, 0);
+ if (proto == NULL)
+ return;
+
+ GetUserProtoLanguageSetting(lang_name, hContact, proto, setting);
+
+ // If not found and is inside meta, try to get from the meta
+ if (lang_name[0] != _T('\0'))
+ return;
+
+ // Is a subcontact?
+ if (!ServiceExists(MS_MC_GETMETACONTACT))
+ return;
+
+ HANDLE hMetaContact = (HANDLE) CallService(MS_MC_GETMETACONTACT, (WPARAM) hContact, 0);
+ if (hMetaContact == NULL)
+ return;
+
+ GetUserProtoLanguageSetting(lang_name, hMetaContact, metacontacts_proto, setting);
+}
+
+
+void GetLangPackLanguage(TCHAR *name, size_t len)
+{
+ LCID localeID = CallService(MS_LANGPACK_GETLOCALE, 0, 0);
+ TCHAR ini[32];
+ TCHAR end[32];
+ GetLocaleInfo(localeID, LOCALE_SISO639LANGNAME, ini, MAX_REGS(ini));
+ GetLocaleInfo(localeID, LOCALE_SISO3166CTRYNAME, end, MAX_REGS(end));
+
+ mir_sntprintf(name, len, _T("%s_%s"), ini, end);
+}
+
+
+Language *GetContactLanguage(HANDLE hContact)
+{
+ if (hContact == NULL)
+ {
+ // System language
+
+ // First try the db setting
+ TCHAR lang_name[NAME_SIZE] = _T("");
+ DBVARIANT dbv;
+ if (!DBGetContactSettingTString(NULL, MODULE_NAME, "TalkLanguage", &dbv))
+ {
+ lstrcpyn(lang_name, dbv.ptszVal, MAX_REGS(lang_name));
+ DBFreeVariant(&dbv);
+ }
+
+ // Then the langpack language
+ if (lang_name[0] == _T('\0'))
+ GetLangPackLanguage(lang_name, MAX_REGS(lang_name));
+
+ Language *lang = GetLanguage(lang_name);
+ if (lang != NULL)
+ return lang;
+
+ // Then english
+ lang = GetLanguage(_T("en"));
+ if (lang != NULL)
+ return lang;
+
+ // Last shot: first avaiable language
+ return languages[0];
+ }
+ else
+ {
+ // Contact language
+ TCHAR lang_name[NAME_SIZE] = _T("");
+
+ DBVARIANT dbv = {0};
+ if (!DBGetContactSettingTString(hContact, MODULE_NAME, "TalkLanguage", &dbv))
+ {
+ lstrcpyn(lang_name, dbv.ptszVal, NAME_SIZE);
+ DBFreeVariant(&dbv);
+ }
+
+ if (lang_name[0] == _T('\0') && !DBGetContactSettingTString(hContact, "SpellChecker", "TalkLanguage", &dbv))
+ {
+ lstrcpyn(lang_name, dbv.ptszVal, NAME_SIZE);
+ DBFreeVariant(&dbv);
+ }
+
+ // Try from metacontact
+ if (lang_name[0] == _T('\0') && ServiceExists(MS_MC_GETMETACONTACT))
+ {
+ HANDLE hMetaContact = (HANDLE) CallService(MS_MC_GETMETACONTACT, (WPARAM) hContact, 0);
+ if (hMetaContact != NULL)
+ {
+ if (!DBGetContactSettingTString(hMetaContact, MODULE_NAME, "TalkLanguage", &dbv))
+ {
+ lstrcpyn(lang_name, dbv.ptszVal, NAME_SIZE);
+ DBFreeVariant(&dbv);
+ }
+
+ if (lang_name[0] == _T('\0') && !DBGetContactSettingTString(hMetaContact, "SpellChecker", "TalkLanguage", &dbv))
+ {
+ lstrcpyn(lang_name, dbv.ptszVal, NAME_SIZE);
+ DBFreeVariant(&dbv);
+ }
+ }
+ }
+
+ // Try to get from Language info
+ if (lang_name[0] == _T('\0'))
+ GetUserLanguageSetting(lang_name, hContact, "Language");
+ if (lang_name[0] == _T('\0'))
+ GetUserLanguageSetting(lang_name, hContact, "Language1");
+ if (lang_name[0] == _T('\0'))
+ GetUserLanguageSetting(lang_name, hContact, "Language2");
+ if (lang_name[0] == _T('\0'))
+ GetUserLanguageSetting(lang_name, hContact, "Language3");
+
+ if (lang_name[0] == _T('\0'))
+ // Use default lang
+ return opts.default_language;
+
+ Language *ret = GetClosestLanguage(lang_name);
+ if(ret == NULL)
+ // Lost a lang?
+ return opts.default_language;
+
+ return ret;
+ }
+}
+
+
+Voice *GetContactVoice(HANDLE hContact, Language *lang)
+{
+ int i;
+ DBVARIANT dbv;
+ if (DBGetContactSettingTString(hContact, MODULE_NAME, "Voice", &dbv))
+ goto DEFAULT;
+
+ TCHAR name[NAME_SIZE];
+ lstrcpyn(name, dbv.ptszVal, MAX_REGS(name));
+ DBFreeVariant(&dbv);
+
+ if (name[0] == _T('\0'))
+ goto DEFAULT;
+
+ for (i = 0; i < lang->voices.getCount(); i++)
+ if (lstrcmpi(name, lang->voices[i]->name) == 0)
+ return lang->voices[i];
+
+DEFAULT:
+ if (lang == opts.default_language && opts.default_voice != NULL)
+ return opts.default_voice;
+ else
+ return lang->voices[0];
+}
+
+
+Variant *GetVariant(TCHAR *name)
+{
+ for (int i = 0; i < variants.getCount(); i++)
+ if (lstrcmpi(name, variants[i]->name) == 0)
+ return variants[i];
+ return NULL;
+}
+
+
+Variant *GetContactVariant(HANDLE hContact)
+{
+ Variant *ret;
+
+ DBVARIANT dbv;
+ if (DBGetContactSettingTString(hContact, MODULE_NAME, "Variant", &dbv))
+ goto DEFAULT;
+
+ TCHAR name[NAME_SIZE];
+ lstrcpyn(name, dbv.ptszVal, MAX_REGS(name));
+ DBFreeVariant(&dbv);
+
+ if (name[0] == _T('\0'))
+ goto DEFAULT;
+
+ ret = GetVariant(name);
+ if (ret != NULL)
+ return ret;
+
+DEFAULT:
+
+ if (hContact != NULL && opts.select_variant_per_genre)
+ {
+ CONTACTINFO ci = {0};
+ ci.cbSize = sizeof(ci);
+ ci.hContact = hContact;
+ ci.dwFlag = CNF_GENDER;
+ CallService(MS_CONTACT_GETCONTACTINFO, 0, (LPARAM) &ci);
+
+ if (ci.bVal == 'M')
+ {
+ ret = GetVariant(_T("male1"));
+ if (ret != NULL)
+ return ret;
+ }
+ else if (ci.bVal == 'F')
+ {
+ ret = GetVariant(_T("female1"));
+ if (ret != NULL)
+ return ret;
+ }
+ }
+
+ return opts.default_variant;
+}
+
+
+int GetContactParam(HANDLE hContact, int param)
+{
+ Voice *voice = GetContactVoice(hContact, GetContactLanguage(hContact));
+
+ int def;
+ if (voice->engine == ENGINE_SAPI && PARAMETERS[param].eparam == espeakRATE)
+ def = SAPI_GetDefaultRateFor(voice->id);
+ else if (voice->engine == ENGINE_SAPI)
+ def = PARAMETERS[param].sapi.def;
+ else
+ def = PARAMETERS[param].espeak.def;
+
+ int ret = DBGetContactSettingDword(NULL, MODULE_NAME, PARAMETERS[param].setting, def);
+ if (hContact != NULL)
+ ret = DBGetContactSettingDword(hContact, MODULE_NAME, PARAMETERS[param].setting, ret);
+
+ return ret;
+}
+
+
+void SetContactParam(HANDLE hContact, int param, int value)
+{
+ DBWriteContactSettingDword(hContact, MODULE_NAME, PARAMETERS[param].setting, value);
+}
+
+
+BOOL StatusEnabled(int status)
+{
+ switch(status) {
+ case ID_STATUS_OFFLINE: return !opts.disable_offline;
+ case ID_STATUS_ONLINE: return !opts.disable_online;
+ case ID_STATUS_AWAY: return !opts.disable_away;
+ case ID_STATUS_DND: return !opts.disable_dnd;
+ case ID_STATUS_NA: return !opts.disable_na;
+ case ID_STATUS_OCCUPIED: return !opts.disable_occupied;
+ case ID_STATUS_FREECHAT: return !opts.disable_freechat;
+ case ID_STATUS_INVISIBLE: return !opts.disable_invisible;
+ case ID_STATUS_ONTHEPHONE: return !opts.disable_onthephone;
+ case ID_STATUS_OUTTOLUNCH: return !opts.disable_outtolunch;
+ }
+
+ return !opts.disable_offline;
+}
+
+
+int SpeakService(HANDLE hContact, TCHAR *text)
+{
+ Language *lang;
+ Voice *voice;
+ SpeakData *data;
+ int status, i;
+
+ // Enabled?
+ if (hContact != NULL && !DBGetContactSettingByte(hContact, MODULE_NAME, "Enabled", TRUE))
+ goto RETURN;
+
+ // Check status
+ status = CallService(MS_CLIST_GETSTATUSMODE, 0, 0);
+ if (!StatusEnabled(status))
+ goto RETURN;
+
+ if (opts.enable_only_idle)
+ {
+ MIRANDA_IDLE_INFO idle = {0};
+ CallService(MS_IDLE_GETIDLEINFO, 0, (LPARAM) &idle);
+ if (idle.idleType == 0)
+ goto RETURN;
+ }
+
+ // Check language
+ lang = GetContactLanguage(hContact);
+ if (lang == NULL)
+ goto RETURN;
+
+ voice = GetContactVoice(hContact, lang);
+ if (voice == NULL)
+ goto RETURN;
+
+ data = new SpeakData(lang, voice, GetContactVariant(hContact), text);
+ for (i = 0; i < NUM_PARAMETERS; i++)
+ data->setParameter(i, GetContactParam(hContact, i));
+ queue->Add(0, hContact, data);
+
+ return 0;
+
+RETURN:
+ mir_free(text);
+ return -1;
+}
+
+
+int SpeakAService(WPARAM wParam, LPARAM lParam)
+{
+ char *text = (char *) lParam;
+ if (text == NULL)
+ return -1;
+
+ return SpeakService((HANDLE) wParam, mir_a2t(text));
+}
+
+
+int SpeakWService(WPARAM wParam, LPARAM lParam)
+{
+ WCHAR *text = (WCHAR *) lParam;
+ if (text == NULL)
+ return -1;
+
+ return SpeakService((HANDLE) wParam, mir_u2t(text));
+}
+
+
+void Speak(HANDLE hContact, void *param)
+{
+ int status;
+
+ SpeakData *data = (SpeakData *) param;
+ if (data == NULL)
+ return;
+
+ if (languages.getCount() < 1)
+ goto RETURN;
+
+ if (hContact != (HANDLE) -1)
+ {
+ if (opts.respect_sndvol_mute && !DBGetContactSettingByte(NULL, "Skin", "UseSound", 1))
+ goto RETURN;
+
+ if (hContact != NULL && !DBGetContactSettingByte(hContact, MODULE_NAME, "Enabled", TRUE))
+ goto RETURN;
+
+ status = CallService(MS_CLIST_GETSTATUSMODE, 0, 0);
+ if (!StatusEnabled(status))
+ goto RETURN;
+
+ if (opts.enable_only_idle)
+ {
+ MIRANDA_IDLE_INFO idle = {0};
+ CallService(MS_IDLE_GETIDLEINFO, 0, (LPARAM) &idle);
+ if (idle.idleType == 0)
+ goto RETURN;
+ }
+ }
+
+ Speak(data);
+
+RETURN:
+ mir_free(data->text);
+ delete data;
+}
+
+
+ISpeechObjectToken * SAPI_FindVoice(ISpeechVoice *voice, TCHAR *id)
+{
+ HRESULT hr = 0;
+ ISpeechObjectTokens *voices = NULL;
+ ISpeechObjectToken *v = NULL;
+ long count = 0;
+ long i;
+
+ CHECK( voice->GetVoices(NULL, NULL, &voices) );
+
+ CHECK( voices->get_Count(&count) );
+
+ for(i = 0; i < count; i++)
+ {
+ CHECK( voices->Item(i, &v) );
+ if (v == NULL)
+ continue;
+
+ BstrToTchar id2;
+ CHECK( v->get_Id(id2) );
+
+ if (lstrcmpi(id2, id) == 0)
+ break;
+
+ RELEASE(v);
+ }
+
+ RELEASE( voices );
+ return v;
+
+err:
+ RELEASE( voices );
+ RELEASE( v );
+ return NULL;
+}
+
+
+void Speak(SpeakData *data)
+{
+ size_t len = lstrlen(data->text);
+
+ if (opts.truncate && len > opts.truncate_len)
+ data->text[opts.truncate_len] = 0;
+
+ if (data->voice->engine == ENGINE_ESPEAK)
+ {
+ if (data->variant != NULL)
+ {
+ TCHAR name[NAME_SIZE];
+ mir_sntprintf(name, MAX_REGS(name), _T("%s+%s"), data->voice->id, data->variant->id);
+ espeak_SetVoiceByName(TcharToUtf8(name));
+ }
+ else
+ espeak_SetVoiceByName(TcharToUtf8(data->voice->id));
+
+ for (int i = 0; i < NUM_PARAMETERS; i++)
+ espeak_SetParameter(PARAMETERS[i].eparam, data->getParameter(i), 0);
+
+ espeak_Synth(data->text, (len + 1) * sizeof(TCHAR), 0, POS_CHARACTER,
+ 0, espeakCHARS_TCHAR, NULL, NULL);
+ }
+ else if (data->voice->engine == ENGINE_SAPI)
+ {
+ HRESULT hr = 0;
+ ISpeechVoice *voice = NULL;
+ ISpeechObjectToken *v = NULL;
+
+ if (FAILED(CoInitialize(NULL)))
+ return;
+
+ CHECK( CoCreateInstance(CLSID_SpVoice, NULL, CLSCTX_ALL, IID_ISpeechVoice, (void **)&voice) );
+
+ v = SAPI_FindVoice(voice, data->voice->id);
+ if (v == NULL) goto err;
+
+ CHECK( voice->putref_Voice(v) );
+ CHECK( voice->put_Rate(data->getParameter(0)) );
+ CHECK( voice->put_Volume(data->getParameter(1)) );
+
+ CHECK( voice->Speak(BstrToTchar(data->text), SVSFDefault, NULL) );
+
+err:
+ RELEASE( v );
+ RELEASE( voice );
+
+ CoUninitialize();
+ }
+}
+
+
+int SAPI_GetDefaultRateFor(TCHAR *id)
+{
+ int ret = 0;
+
+ HRESULT hr = 0;
+ ISpeechVoice *voice = NULL;
+ ISpeechObjectToken *v = NULL;
+
+ if (FAILED(CoInitialize(NULL)))
+ return 0;
+
+ CHECK( CoCreateInstance(CLSID_SpVoice, NULL, CLSCTX_ALL, IID_ISpeechVoice, (void **)&voice) );
+
+ v = SAPI_FindVoice(voice, id);
+ if (v == NULL) goto err;
+
+ CHECK( voice->putref_Voice(v) );
+
+ {
+ long rate;
+ CHECK( voice->get_Rate(&rate) );
+ ret = (int) rate;
+ }
+
+err:
+ RELEASE( v );
+ RELEASE( voice );
+
+ CoUninitialize();
+
+ return ret;
+}
+
+
+TCHAR * VariablesSpeak(ARGUMENTSINFO *ai)
+{
+ if (ai->cbSize < sizeof(ARGUMENTSINFO))
+ return NULL;
+
+ ai->flags = AIF_FALSE;
+ if (ai->argc < 2 || ai->argc > 3)
+ return NULL;
+
+ TCHAR *text = ai->targv[1];
+ if (text == NULL)
+ return NULL;
+
+ HANDLE hContact = NULL;
+ if (ai->argc >= 3)
+ {
+ CONTACTSINFO ci = {0};
+ ci.cbSize = sizeof(ci);
+ ci.tszContact = ai->targv[2];
+ ci.flags = 0xFF | CI_UNICODE;
+ int count = CallService(MS_VARS_GETCONTACTFROMSTRING, (WPARAM)&ci, 0);
+ if (count == 1 && ci.hContacts != NULL)
+ {
+ hContact = ci.hContacts[0];
+ }
+ else
+ {
+ if (ci.hContacts != NULL)
+ CallService(MS_VARS_FREEMEMORY, (WPARAM) ci.hContacts, 0);
+
+ return NULL;
+ }
+ }
+
+ SpeakService(hContact, mir_tstrdup(text));
+ ai->flags = 0;
+ return mir_tstrdup(_T(""));
+}
diff --git a/Plugins/eSpeak/eSpeak.dsp b/Plugins/eSpeak/eSpeak.dsp
new file mode 100644
index 0000000..c6c7958
--- /dev/null
+++ b/Plugins/eSpeak/eSpeak.dsp
@@ -0,0 +1,383 @@
+# Microsoft Developer Studio Project File - Name="eSpeak" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=eSpeak - Win32 Release
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "eSpeak.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "eSpeak.mak" CFG="eSpeak - Win32 Release"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "eSpeak - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "eSpeak - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "eSpeak - Win32 Unicode Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "eSpeak - Win32 Unicode Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "eSpeak - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MD /W3 /GX /O1 /YX /FD /c
+# SUBTRACT BASE CPP /Fr
+# ADD CPP /nologo /G4 /MT /W3 /GX /O2 /Ob0 /I "../../include" /I "sdk" /D "WIN32" /D "W32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /Fr /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x417 /d "NDEBUG"
+# ADD RSC /l 0x417 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 user32.lib shell32.lib wininet.lib gdi32.lib /nologo /base:"0x67100000" /dll /machine:I386 /filealign:0x200
+# SUBTRACT BASE LINK32 /pdb:none /map
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winmm.lib advapi32.lib PAStaticWMME.lib ole32.lib oleaut32.lib /nologo /base:"0x3EC20000" /dll /map /debug /debugtype:both /machine:I386 /nodefaultlib:"LIBC" /out:"..\..\bin\release\Plugins\meSpeak.dll" /pdbtype:sept /libpath:"lib" /filealign:0x200 /ALIGN:4096 /ignore:4108
+# SUBTRACT LINK32 /profile /pdb:none
+
+!ELSEIF "$(CFG)" == "eSpeak - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Ignore_Export_Lib 0
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /G4 /MT /W3 /GX /O2 /Ob0 /I "../../include" /FR /YX /FD /c
+# ADD CPP /nologo /G4 /MTd /W3 /GX /ZI /Od /I "../../include" /I "sdk" /D "WIN32" /D "W32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /FR /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x417 /d "NDEBUG"
+# ADD RSC /l 0x417 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 comctl32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 /out:"..\..bin\release\Plugins\eSpeak.dll" /filealign:0x200 /ALIGN:4096 /ignore:4108
+# SUBTRACT BASE LINK32 /profile /pdb:none
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winmm.lib advapi32.lib PAStaticWMME.lib ole32.lib oleaut32.lib /nologo /base:"0x3EC20000" /dll /incremental:yes /debug /machine:I386 /nodefaultlib:"LIBC" /out:"..\..\bin\debug\Plugins\meSpeak.dll" /libpath:"lib" /filealign:0x200 /ALIGN:4096 /ignore:4108
+# SUBTRACT LINK32 /profile /pdb:none
+
+!ELSEIF "$(CFG)" == "eSpeak - Win32 Unicode Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "eSpeak___Win32_Unicode_Debug"
+# PROP BASE Intermediate_Dir "eSpeak___Win32_Unicode_Debug"
+# PROP BASE Ignore_Export_Lib 0
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Unicode_Debug"
+# PROP Intermediate_Dir "Unicode_Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /G4 /MTd /W3 /GX /ZI /Od /I "../../include" /FR /YX /FD /c
+# ADD CPP /nologo /G4 /MTd /W3 /GX /ZI /Od /I "../../include" /I "sdk" /D "WIN32" /D "W32" /D "_DEBUG" /D "_WINDOWS" /D "_UNICODE" /D "UNICODE" /D "_USRDLL" /FR /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x417 /d "NDEBUG"
+# ADD RSC /l 0x417 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 comctl32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /base:"0x32100000" /dll /incremental:yes /debug /machine:I386 /out:"..\..\bin\debug\Plugins\eSpeak.dll" /filealign:0x200 /ALIGN:4096 /ignore:4108
+# SUBTRACT BASE LINK32 /profile /pdb:none
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winmm.lib advapi32.lib PAStaticWMME.lib ole32.lib oleaut32.lib /nologo /base:"0x3EC20000" /dll /incremental:yes /debug /machine:I386 /nodefaultlib:"LIBC" /out:"..\..\bin\debug unicode\Plugins\meSpeakW.dll" /libpath:"lib" /filealign:0x200 /ALIGN:4096 /ignore:4108
+# SUBTRACT LINK32 /profile /pdb:none
+
+!ELSEIF "$(CFG)" == "eSpeak - Win32 Unicode Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "eSpeak___Win32_Unicode_Release"
+# PROP BASE Intermediate_Dir "eSpeak___Win32_Unicode_Release"
+# PROP BASE Ignore_Export_Lib 0
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Unicode_Release"
+# PROP Intermediate_Dir "Unicode_Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /G4 /MT /W3 /GX /O2 /Ob0 /I "../../include" /Fr /YX /FD /c
+# ADD CPP /nologo /G4 /MT /W3 /GX /O2 /Ob0 /I "../../include" /I "sdk" /D "WIN32" /D "W32" /D "NDEBUG" /D "_WINDOWS" /D "_UNICODE" /D "UNICODE" /D "_USRDLL" /Fr /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x417 /d "NDEBUG"
+# ADD RSC /l 0x417 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 comctl32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /base:"0x32100000" /dll /map /machine:I386 /out:"..\..\bin\release\Plugins\eSpeak.dll" /filealign:0x200 /ALIGN:4096 /ignore:4108
+# SUBTRACT BASE LINK32 /profile /pdb:none
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winmm.lib advapi32.lib PAStaticWMME.lib ole32.lib oleaut32.lib /nologo /base:"0x3EC20000" /dll /map /debug /debugtype:both /machine:I386 /nodefaultlib:"LIBC" /out:"..\..\bin\release\Plugins\meSpeakW.dll" /pdbtype:sept /libpath:"lib" /filealign:0x200 /ALIGN:4096 /ignore:4108
+# SUBTRACT LINK32 /profile /pdb:none
+
+!ENDIF
+
+# Begin Target
+
+# Name "eSpeak - Win32 Release"
+# Name "eSpeak - Win32 Debug"
+# Name "eSpeak - Win32 Unicode Debug"
+# Name "eSpeak - Win32 Unicode Release"
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=.\commons.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\utils\ContactAsyncQueue.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\m_speak.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\utils\mir_icons.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\utils\mir_memory.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\utils\mir_options.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\options.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\types.h
+# End Source File
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
+# Begin Source File
+
+SOURCE=.\resource.rc
+# End Source File
+# Begin Source File
+
+SOURCE=.\res\unknown.ico
+# End Source File
+# End Group
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=..\utils\ContactAsyncQueue.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\eSpeak.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\utils\mir_icons.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\utils\mir_options.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\options.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\types.cpp
+# End Source File
+# End Group
+# Begin Group "Docs"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=.\Docs\langpack_meSpeak.txt
+# End Source File
+# Begin Source File
+
+SOURCE=.\Docs\meSpeak_changelog.txt
+# End Source File
+# Begin Source File
+
+SOURCE=.\Docs\meSpeak_readme.txt
+# End Source File
+# Begin Source File
+
+SOURCE=.\Docs\meSpeak_version.txt
+# End Source File
+# End Group
+# Begin Group "eSpeak"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=.\eSpeak\compiledict.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\eSpeak\dictionary.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\eSpeak\event.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\eSpeak\fifo.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\eSpeak\intonation.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\eSpeak\klatt.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\eSpeak\klatt.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\eSpeak\numbers.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\eSpeak\phoneme.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\eSpeak\phonemelist.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\eSpeak\readclause.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\eSpeak\setlengths.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\eSpeak\sintab.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\eSpeak\speak_lib.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\eSpeak\speak_lib.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\eSpeak\speech.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\eSpeak\StdAfx.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\eSpeak\synth_mbrola.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\eSpeak\synthdata.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\eSpeak\synthesize.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\eSpeak\synthesize.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\eSpeak\tr_languages.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\eSpeak\tr_languages.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\eSpeak\translate.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\eSpeak\translate.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\eSpeak\voice.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\eSpeak\voices.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\eSpeak\wave.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\eSpeak\wave_pulse.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\eSpeak\wave_sada.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\eSpeak\wavegen.cpp
+# End Source File
+# End Group
+# End Target
+# End Project
diff --git a/Plugins/eSpeak/eSpeak.dsw b/Plugins/eSpeak/eSpeak.dsw
new file mode 100644
index 0000000..ac273da
--- /dev/null
+++ b/Plugins/eSpeak/eSpeak.dsw
@@ -0,0 +1,29 @@
+Microsoft Developer Studio Workspace File, Format Version 6.00
+# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
+
+###############################################################################
+
+Project: "eSpeak"=.\eSpeak.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Global:
+
+Package=<5>
+{{{
+}}}
+
+Package=<3>
+{{{
+}}}
+
+###############################################################################
+
diff --git a/Plugins/eSpeak/eSpeak.sln b/Plugins/eSpeak/eSpeak.sln
new file mode 100644
index 0000000..a490d6f
--- /dev/null
+++ b/Plugins/eSpeak/eSpeak.sln
@@ -0,0 +1,26 @@
+
+Microsoft Visual Studio Solution File, Format Version 10.00
+# Visual Studio 2008
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "eSpeak", "eSpeak.vcproj", "{AC88F3D2-D114-4174-8F0E-209921DAB63A}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Win32 = Debug|Win32
+ Release|Win32 = Release|Win32
+ Unicode Debug|Win32 = Unicode Debug|Win32
+ Unicode Release|Win32 = Unicode Release|Win32
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {AC88F3D2-D114-4174-8F0E-209921DAB63A}.Debug|Win32.ActiveCfg = Debug|Win32
+ {AC88F3D2-D114-4174-8F0E-209921DAB63A}.Debug|Win32.Build.0 = Debug|Win32
+ {AC88F3D2-D114-4174-8F0E-209921DAB63A}.Release|Win32.ActiveCfg = Release|Win32
+ {AC88F3D2-D114-4174-8F0E-209921DAB63A}.Release|Win32.Build.0 = Release|Win32
+ {AC88F3D2-D114-4174-8F0E-209921DAB63A}.Unicode Debug|Win32.ActiveCfg = Unicode Debug|Win32
+ {AC88F3D2-D114-4174-8F0E-209921DAB63A}.Unicode Debug|Win32.Build.0 = Unicode Debug|Win32
+ {AC88F3D2-D114-4174-8F0E-209921DAB63A}.Unicode Release|Win32.ActiveCfg = Unicode Release|Win32
+ {AC88F3D2-D114-4174-8F0E-209921DAB63A}.Unicode Release|Win32.Build.0 = Unicode Release|Win32
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/Plugins/eSpeak/eSpeak.vcproj b/Plugins/eSpeak/eSpeak.vcproj
new file mode 100644
index 0000000..f73319c
--- /dev/null
+++ b/Plugins/eSpeak/eSpeak.vcproj
@@ -0,0 +1,1402 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="9,00"
+ Name="eSpeak"
+ ProjectGUID="{AC88F3D2-D114-4174-8F0E-209921DAB63A}"
+ RootNamespace="eSpeak"
+ TargetFrameworkVersion="131072"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Unicode Debug|Win32"
+ OutputDirectory=".\Unicode_Debug"
+ IntermediateDirectory=".\Unicode_Debug"
+ ConfigurationType="2"
+ InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC60.vsprops"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ PreprocessorDefinitions="NDEBUG"
+ MkTypLibCompatible="true"
+ SuppressStartupBanner="true"
+ TargetEnvironment="1"
+ TypeLibraryName=".\Unicode_Debug/eSpeak.tlb"
+ HeaderFileName=""
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories="../../include,sdk"
+ PreprocessorDefinitions="WIN32;W32;_DEBUG;_WINDOWS;UNICODE;_USRDLL;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE"
+ RuntimeLibrary="1"
+ PrecompiledHeaderFile=".\Unicode_Debug/eSpeak.pch"
+ AssemblerListingLocation=".\Unicode_Debug/"
+ ObjectFile=".\Unicode_Debug/"
+ ProgramDataBaseFileName=".\Unicode_Debug/"
+ BrowseInformation="1"
+ WarningLevel="3"
+ SuppressStartupBanner="true"
+ DebugInformationFormat="4"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="NDEBUG"
+ Culture="1047"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalOptions="/ALIGN:4096 /filealign:0x200 /ignore:4108 "
+ AdditionalDependencies="winmm.lib PAStaticWMME.lib comsuppw.lib"
+ OutputFile="..\..\bin\debug unicode\Plugins\meSpeakW.dll"
+ LinkIncremental="2"
+ SuppressStartupBanner="true"
+ AdditionalLibraryDirectories="lib"
+ IgnoreDefaultLibraryNames="LIBC"
+ GenerateDebugInformation="true"
+ ProgramDatabaseFile=".\Unicode_Debug/eSpeakW.pdb"
+ BaseAddress="0x3EC20000"
+ RandomizedBaseAddress="1"
+ DataExecutionPrevention="0"
+ ImportLibrary=".\Unicode_Debug/eSpeakW.lib"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ SuppressStartupBanner="true"
+ OutputFile=".\Unicode_Debug/eSpeak.bsc"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Unicode Release|Win32"
+ OutputDirectory=".\Unicode_Release"
+ IntermediateDirectory=".\Unicode_Release"
+ ConfigurationType="2"
+ InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC60.vsprops"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ PreprocessorDefinitions="NDEBUG"
+ MkTypLibCompatible="true"
+ SuppressStartupBanner="true"
+ TargetEnvironment="1"
+ TypeLibraryName=".\Unicode_Release/eSpeak.tlb"
+ HeaderFileName=""
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ InlineFunctionExpansion="0"
+ AdditionalIncludeDirectories="../../include,sdk"
+ PreprocessorDefinitions="WIN32;W32;NDEBUG;_WINDOWS;UNICODE;_USRDLL;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE"
+ StringPooling="true"
+ RuntimeLibrary="0"
+ EnableFunctionLevelLinking="true"
+ PrecompiledHeaderFile=".\Unicode_Release/eSpeak.pch"
+ AssemblerListingLocation=".\Unicode_Release/"
+ ObjectFile=".\Unicode_Release/"
+ ProgramDataBaseFileName=".\Unicode_Release/"
+ BrowseInformation="2"
+ BrowseInformationFile=".\Unicode_Release/"
+ WarningLevel="3"
+ SuppressStartupBanner="true"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="NDEBUG"
+ Culture="1047"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalOptions="/ALIGN:4096 /filealign:0x200 /ignore:4108 "
+ AdditionalDependencies="winmm.lib PAStaticWMME.lib sapi.lib"
+ OutputFile="..\..\bin\release\Plugins\meSpeakW.dll"
+ LinkIncremental="1"
+ SuppressStartupBanner="true"
+ AdditionalLibraryDirectories="lib"
+ IgnoreDefaultLibraryNames="LIBC"
+ GenerateDebugInformation="true"
+ ProgramDatabaseFile=".\Unicode_Release/eSpeakW.pdb"
+ GenerateMapFile="true"
+ MapFileName=".\Unicode_Release/eSpeakW.map"
+ BaseAddress="0x3EC20000"
+ RandomizedBaseAddress="1"
+ DataExecutionPrevention="0"
+ ImportLibrary=".\Unicode_Release/eSpeakW.lib"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ SuppressStartupBanner="true"
+ OutputFile=".\Unicode_Release/eSpeak.bsc"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory=".\Release"
+ IntermediateDirectory=".\Release"
+ ConfigurationType="2"
+ InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC60.vsprops"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="2"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ PreprocessorDefinitions="NDEBUG"
+ MkTypLibCompatible="true"
+ SuppressStartupBanner="true"
+ TargetEnvironment="1"
+ TypeLibraryName=".\Release/eSpeak.tlb"
+ HeaderFileName=""
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ InlineFunctionExpansion="0"
+ AdditionalIncludeDirectories="../../include,sdk"
+ PreprocessorDefinitions="WIN32;W32;NDEBUG;_WINDOWS;_USRDLL;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE"
+ StringPooling="true"
+ RuntimeLibrary="0"
+ EnableFunctionLevelLinking="true"
+ PrecompiledHeaderFile=".\Release/eSpeak.pch"
+ AssemblerListingLocation=".\Release/"
+ ObjectFile=".\Release/"
+ ProgramDataBaseFileName=".\Release/"
+ BrowseInformation="2"
+ BrowseInformationFile=".\Release/"
+ WarningLevel="3"
+ SuppressStartupBanner="true"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="NDEBUG"
+ Culture="1047"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalOptions="/ALIGN:4096 /filealign:0x200 /ignore:4108 "
+ AdditionalDependencies="winmm.lib PAStaticWMME.lib sapi.lib"
+ OutputFile="..\..\bin\release\Plugins\meSpeak.dll"
+ LinkIncremental="1"
+ SuppressStartupBanner="true"
+ AdditionalLibraryDirectories="lib"
+ IgnoreDefaultLibraryNames="LIBC"
+ GenerateDebugInformation="true"
+ ProgramDatabaseFile=".\Release/eSpeak.pdb"
+ GenerateMapFile="true"
+ MapFileName=".\Release/eSpeak.map"
+ BaseAddress="0x3EC20000"
+ RandomizedBaseAddress="1"
+ DataExecutionPrevention="0"
+ ImportLibrary=".\Release/eSpeak.lib"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ SuppressStartupBanner="true"
+ OutputFile=".\Release/eSpeak.bsc"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory=".\Debug"
+ IntermediateDirectory=".\Debug"
+ ConfigurationType="2"
+ InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC60.vsprops"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="2"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ PreprocessorDefinitions="NDEBUG"
+ MkTypLibCompatible="true"
+ SuppressStartupBanner="true"
+ TargetEnvironment="1"
+ TypeLibraryName=".\Debug/eSpeak.tlb"
+ HeaderFileName=""
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories="../../include,sdk"
+ PreprocessorDefinitions="WIN32;W32;_DEBUG;_WINDOWS;_USRDLL;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE"
+ RuntimeLibrary="1"
+ PrecompiledHeaderFile=".\Debug/eSpeak.pch"
+ AssemblerListingLocation=".\Debug/"
+ ObjectFile=".\Debug/"
+ ProgramDataBaseFileName=".\Debug/"
+ BrowseInformation="1"
+ WarningLevel="3"
+ SuppressStartupBanner="true"
+ DebugInformationFormat="4"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="NDEBUG"
+ Culture="1047"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalOptions="/ALIGN:4096 /filealign:0x200 /ignore:4108 "
+ AdditionalDependencies="winmm.lib PAStaticWMME.lib sapi.lib"
+ OutputFile="..\..\bin\debug\Plugins\meSpeak.dll"
+ LinkIncremental="2"
+ SuppressStartupBanner="true"
+ AdditionalLibraryDirectories="lib"
+ IgnoreDefaultLibraryNames="LIBC"
+ GenerateDebugInformation="true"
+ ProgramDatabaseFile=".\Debug/eSpeak.pdb"
+ BaseAddress="0x3EC20000"
+ RandomizedBaseAddress="1"
+ DataExecutionPrevention="0"
+ ImportLibrary=".\Debug/eSpeak.lib"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ SuppressStartupBanner="true"
+ OutputFile=".\Debug/eSpeak.bsc"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Header Files"
+ Filter="h;hpp;hxx;hm;inl"
+ >
+ <File
+ RelativePath="commons.h"
+ >
+ </File>
+ <File
+ RelativePath="..\utils\ContactAsyncQueue.h"
+ >
+ </File>
+ <File
+ RelativePath="m_speak.h"
+ >
+ </File>
+ <File
+ RelativePath="..\utils\mir_buffer.h"
+ >
+ </File>
+ <File
+ RelativePath="..\utils\mir_icons.h"
+ >
+ </File>
+ <File
+ RelativePath="..\utils\mir_memory.h"
+ >
+ </File>
+ <File
+ RelativePath="..\utils\mir_options.h"
+ >
+ </File>
+ <File
+ RelativePath="options.h"
+ >
+ </File>
+ <File
+ RelativePath=".\resource.h"
+ >
+ </File>
+ <File
+ RelativePath=".\types.h"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Resource Files"
+ Filter="ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
+ >
+ <File
+ RelativePath="resource.rc"
+ >
+ <FileConfiguration
+ Name="Unicode Debug|Win32"
+ >
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Unicode Release|Win32"
+ >
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="res\unknown.ico"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Source Files"
+ Filter="cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+ >
+ <File
+ RelativePath="..\utils\ContactAsyncQueue.cpp"
+ >
+ <FileConfiguration
+ Name="Unicode Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Unicode Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="eSpeak.cpp"
+ >
+ <FileConfiguration
+ Name="Unicode Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Unicode Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="..\utils\mir_icons.cpp"
+ >
+ <FileConfiguration
+ Name="Unicode Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Unicode Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="..\utils\mir_options.cpp"
+ >
+ <FileConfiguration
+ Name="Unicode Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Unicode Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="options.cpp"
+ >
+ <FileConfiguration
+ Name="Unicode Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Unicode Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath=".\types.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Docs"
+ >
+ <File
+ RelativePath=".\Docs\langpack_meSpeak.txt"
+ >
+ </File>
+ <File
+ RelativePath=".\Docs\meSpeak_changelog.txt"
+ >
+ </File>
+ <File
+ RelativePath=".\Docs\meSpeak_readme.txt"
+ >
+ </File>
+ <File
+ RelativePath=".\Docs\meSpeak_version.txt"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="eSpeak"
+ >
+ <File
+ RelativePath="eSpeak\compiledict.cpp"
+ >
+ <FileConfiguration
+ Name="Unicode Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Unicode Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="eSpeak\dictionary.cpp"
+ >
+ <FileConfiguration
+ Name="Unicode Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Unicode Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath=".\eSpeak\event.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\eSpeak\fifo.cpp"
+ >
+ </File>
+ <File
+ RelativePath="eSpeak\intonation.cpp"
+ >
+ <FileConfiguration
+ Name="Unicode Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Unicode Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="eSpeak\numbers.cpp"
+ >
+ <FileConfiguration
+ Name="Unicode Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Unicode Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="eSpeak\phoneme.h"
+ >
+ </File>
+ <File
+ RelativePath="eSpeak\phonemelist.cpp"
+ >
+ <FileConfiguration
+ Name="Unicode Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Unicode Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="eSpeak\readclause.cpp"
+ >
+ <FileConfiguration
+ Name="Unicode Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Unicode Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="eSpeak\setlengths.cpp"
+ >
+ <FileConfiguration
+ Name="Unicode Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Unicode Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="eSpeak\sintab.h"
+ >
+ </File>
+ <File
+ RelativePath="eSpeak\speak_lib.cpp"
+ >
+ <FileConfiguration
+ Name="Unicode Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Unicode Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="eSpeak\speak_lib.h"
+ >
+ </File>
+ <File
+ RelativePath="eSpeak\speech.h"
+ >
+ </File>
+ <File
+ RelativePath="eSpeak\StdAfx.h"
+ >
+ </File>
+ <File
+ RelativePath="eSpeak\synth_mbrola.cpp"
+ >
+ <FileConfiguration
+ Name="Unicode Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Unicode Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="eSpeak\synthdata.cpp"
+ >
+ <FileConfiguration
+ Name="Unicode Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Unicode Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="eSpeak\synthesize.cpp"
+ >
+ <FileConfiguration
+ Name="Unicode Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Unicode Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="eSpeak\synthesize.h"
+ >
+ </File>
+ <File
+ RelativePath="eSpeak\tr_languages.cpp"
+ >
+ <FileConfiguration
+ Name="Unicode Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Unicode Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="eSpeak\translate.cpp"
+ >
+ <FileConfiguration
+ Name="Unicode Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Unicode Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="eSpeak\translate.h"
+ >
+ </File>
+ <File
+ RelativePath="eSpeak\voice.h"
+ >
+ </File>
+ <File
+ RelativePath="eSpeak\voices.cpp"
+ >
+ <FileConfiguration
+ Name="Unicode Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Unicode Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath=".\eSpeak\wave.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\eSpeak\wave_pulse.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\eSpeak\wave_sada.cpp"
+ >
+ </File>
+ <File
+ RelativePath="eSpeak\wavegen.cpp"
+ >
+ <FileConfiguration
+ Name="Unicode Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Unicode Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ </File>
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/Plugins/eSpeak/eSpeak/StdAfx.h b/Plugins/eSpeak/eSpeak/StdAfx.h
new file mode 100644
index 0000000..c9a526f
--- /dev/null
+++ b/Plugins/eSpeak/eSpeak/StdAfx.h
@@ -0,0 +1,3 @@
+// This is a dummy file.
+// A file of this name is needed on Windows
+
diff --git a/Plugins/eSpeak/eSpeak/compiledict.cpp b/Plugins/eSpeak/eSpeak/compiledict.cpp
new file mode 100644
index 0000000..9fcaa9a
--- /dev/null
+++ b/Plugins/eSpeak/eSpeak/compiledict.cpp
@@ -0,0 +1,1649 @@
+/***************************************************************************
+ * Copyright (C) 2005 to 2007 by Jonathan Duddington *
+ * email: jonsd@users.sourceforge.net *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 3 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write see: *
+ * <http://www.gnu.org/licenses/>. *
+ ***************************************************************************/
+
+#include "StdAfx.h"
+
+#include <stdio.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include <wctype.h>
+
+#include "speak_lib.h"
+#include "speech.h"
+#include "phoneme.h"
+#include "synthesize.h"
+#include "translate.h"
+
+//#define OPT_FORMAT // format the text and write formatted copy to Log file
+//#define OUTPUT_FORMAT
+
+extern void Write4Bytes(FILE *f, int value);
+int HashDictionary(const char *string);
+
+static FILE *f_log = NULL;
+extern char *dir_dictionary;
+
+static int linenum;
+static int error_count;
+static int transpose_offset; // transpose character range for LookupDictList()
+static int transpose_min;
+static int transpose_max;
+static int text_mode = 0;
+static int debug_flag = 0;
+
+static int hash_counts[N_HASH_DICT];
+static char *hash_chains[N_HASH_DICT];
+static char letterGroupsDefined[N_LETTER_GROUPS];
+
+MNEM_TAB mnem_flags[] = {
+ // these in the first group put a value in bits0-3 of dictionary_flags
+ {"$1", 0x41}, // stress on 1st syllable
+ {"$2", 0x42}, // stress on 2nd syllable
+ {"$3", 0x43},
+ {"$4", 0x44},
+ {"$5", 0x45},
+ {"$6", 0x46},
+ {"$7", 0x47},
+ {"$u", 0x48}, // reduce to unstressed
+ {"$u1", 0x49},
+ {"$u2", 0x4a},
+ {"$u3", 0x4b},
+ {"$u+", 0x4c}, // reduce to unstressed, but stress at end of clause
+ {"$u1+", 0x4d},
+ {"$u2+", 0x4e},
+ {"$u3+", 0x4f},
+
+
+ // these set the corresponding numbered bit if dictionary_flags
+ {"$pause", 8}, /* ensure pause before this word */
+ {"$only", 9}, /* only match on this word without suffix */
+ {"$onlys", 10}, /* only match with none, or with 's' suffix */
+ {"$strend", 11}, /* full stress if at end of clause */
+ {"$strend2", 12}, /* full stress if at end of clause, or only followed by unstressed */
+ {"$unstressend",13}, /* reduce stress at end of clause */
+ {"$atend", 14}, /* use this pronunciation if at end of clause */
+
+ {"$dot", 16}, /* ignore '.' after this word (abbreviation) */
+ {"$abbrev", 17}, /* use this pronuciation rather than split into letters */
+ {"$stem", 18}, // must have a suffix
+
+// language specific
+ {"$double", 19}, // IT double the initial consonant of next word
+ {"$alt", 20}, // use alternative pronunciation
+ {"$alt2", 21},
+
+
+ {"$max3", 27}, // limit to 3 repetitions
+ {"$brk", 28}, // a shorter $pause
+ {"$text", 29}, // word translates to replcement text, not phonemes
+
+// flags in dictionary word 2
+ {"$verbf", 0x20}, /* verb follows */
+ {"$verbsf", 0x21}, /* verb follows, allow -s suffix */
+ {"$nounf", 0x22}, /* noun follows */
+ {"$pastf", 0x23}, /* past tense follows */
+ {"$verb", 0x24}, /* use this pronunciation when its a verb */
+ {"$noun", 0x25}, /* use this pronunciation when its a noun */
+ {"$past", 0x26}, /* use this pronunciation when its past tense */
+ {"$verbextend",0x28}, /* extend influence of 'verb follows' */
+ {"$capital", 0x29}, /* use this pronunciation if initial letter is upper case */
+ {"$allcaps", 0x2a}, /* use this pronunciation if initial letter is upper case */
+ {"$accent", 0x2b}, // character name is base-character name + accent name
+
+ // doesn't set dictionary_flags
+ {"$?", 100}, // conditional rule, followed by byte giving the condition number
+
+ {"$textmode", 200},
+ {"$phonememode", 201},
+ {NULL, -1}
+};
+
+
+#define LEN_GROUP_NAME 12
+
+typedef struct {
+ char name[LEN_GROUP_NAME+1];
+ unsigned int start;
+ unsigned int length;
+} RGROUP;
+
+
+int isspace2(unsigned int c)
+{//=========================
+// can't use isspace() because on Windows, isspace(0xe1) gives TRUE !
+ int c2;
+
+ if(((c2 = (c & 0xff)) == 0) || (c > ' '))
+ return(0);
+ return(1);
+}
+
+
+static const char *LookupMnem2(MNEM_TAB *table, int value)
+{//=======================================================
+ while(table->mnem != NULL)
+ {
+ if(table->value == value)
+ return(table->mnem);
+ table++;
+ }
+ return("");
+}
+
+
+char *print_dictionary_flags(unsigned int *flags)
+{//==============================================
+ static char buf[20];
+
+ sprintf(buf,"%s 0x%x/%x",LookupMnem2(mnem_flags,(flags[0] & 0xf)+0x40), flags[0], flags[1]);
+ return(buf);
+}
+
+
+
+static FILE *fopen_log(const char *fname,const char *access)
+{//==================================================
+// performs fopen, but produces error message to f_log if it fails
+ FILE *f;
+
+ if((f = fopen(fname,access)) == NULL)
+ {
+ if(f_log != NULL)
+ fprintf(f_log,"Can't access (%s) file '%s'\n",access,fname);
+ }
+ return(f);
+}
+
+
+#ifdef OPT_FORMAT
+static const char *lookup_mnem(MNEM_TAB *table, int value)
+//========================================================
+/* Lookup a mnemonic string in a table, return its name */
+{
+ while(table->mnem != NULL)
+ {
+ if(table->value==value)
+ return(table->mnem);
+ table++;
+ }
+ return("??"); /* not found */
+} /* end of mnem */
+#endif
+
+
+
+
+static int compile_line(char *linebuf, char *dict_line, int *hash)
+{//===============================================================
+// Compile a line in the language_list file
+ unsigned char c;
+ char *p;
+ char *word;
+ char *phonetic;
+ unsigned int ix;
+ int step;
+ unsigned int n_flag_codes = 0;
+ int flag_offset;
+ int length;
+ int multiple_words = 0;
+ char *multiple_string = NULL;
+ char *multiple_string_end = NULL;
+
+ int len_word;
+ int len_phonetic;
+ int text_not_phonemes; // this word specifies replacement text, not phonemes
+ unsigned int wc;
+ int all_upper_case;
+
+ char *mnemptr;
+ char *comment;
+ unsigned char flag_codes[100];
+ char encoded_ph[200];
+ unsigned char bad_phoneme[4];
+static char nullstring[] = {0};
+
+ comment = NULL;
+ text_not_phonemes = 0;
+ phonetic = word = nullstring;
+
+if(memcmp(linebuf,"_-",2)==0)
+{
+step=1; // TEST
+}
+ p = linebuf;
+// while(isspace2(*p)) p++;
+
+#ifdef deleted
+ if(*p == '$')
+ {
+ if(memcmp(p,"$textmode",9) == 0)
+ {
+ text_mode = 1;
+ return(0);
+ }
+ if(memcmp(p,"$phonememode",12) == 0)
+ {
+ text_mode = 0;
+ return(0);
+ }
+ }
+#endif
+
+ step = 0;
+
+ c = 0;
+ while(c != '\n')
+ {
+ c = *p;
+
+ if((c == '?') && (step==0))
+ {
+ // conditional rule, allow only if the numbered condition is set for the voice
+ flag_offset = 100;
+
+ p++;
+ if(*p == '!')
+ {
+ // allow only if the numbered condition is NOT set
+ flag_offset = 132;
+ p++;
+ }
+
+ ix = 0;
+ if(isdigit(*p))
+ {
+ ix += (*p-'0');
+ p++;
+ }
+ if(isdigit(*p))
+ {
+ ix = ix*10 + (*p-'0');
+ p++;
+ }
+ flag_codes[n_flag_codes++] = ix + flag_offset;
+ c = *p;
+ }
+
+ if((c == '$') && isalnum(p[1]))
+ {
+ /* read keyword parameter */
+ mnemptr = p;
+ while(!isspace2(c = *p)) p++;
+ *p = 0;
+
+ ix = LookupMnem(mnem_flags,mnemptr);
+ if(ix > 0)
+ {
+ if(ix == 200)
+ {
+ text_mode = 1;
+ }
+ else
+ if(ix == 201)
+ {
+ text_mode = 0;
+ }
+ else
+ if(ix == BITNUM_FLAG_TEXTMODE)
+ {
+ text_not_phonemes = 1;
+ }
+ else
+ {
+ flag_codes[n_flag_codes++] = ix;
+ }
+ }
+ else
+ {
+ fprintf(f_log,"%5d: Unknown keyword: %s\n",linenum,mnemptr);
+ error_count++;
+ }
+ }
+
+ if((c == '/') && (p[1] == '/') && (multiple_words==0))
+ {
+ c = '\n'; /* "//" treat comment as end of line */
+ comment = p;
+ }
+
+ switch(step)
+ {
+ case 0:
+ if(c == '(')
+ {
+ multiple_words = 1;
+ word = p+1;
+ step = 1;
+ }
+ else
+ if(!isspace2(c))
+ {
+ word = p;
+ step = 1;
+ }
+ break;
+
+ case 1:
+ if((c == '-') && (word[0] != '_'))
+ {
+ flag_codes[n_flag_codes++] = BITNUM_FLAG_HYPHENATED;
+ c = ' ';
+ }
+ if(isspace2(c))
+ {
+ p[0] = 0; /* terminate english word */
+
+ if(multiple_words)
+ {
+ multiple_string = multiple_string_end = p+1;
+ step = 2;
+ }
+ else
+ {
+ step = 3;
+ }
+ }
+ else
+ if((c == ')') && multiple_words)
+ {
+ p[0] = 0;
+ step = 3;
+ multiple_words = 0;
+ }
+ break;
+
+ case 2:
+ if(isspace2(c))
+ {
+ multiple_words++;
+ }
+ else
+ if(c == ')')
+ {
+ p[0] = ' '; // terminate extra string
+ multiple_string_end = p+1;
+ step = 3;
+ }
+ break;
+
+ case 3:
+ if(!isspace2(c))
+ {
+ phonetic = p;
+ step = 4;
+ }
+ break;
+
+ case 4:
+ if(isspace2(c))
+ {
+ p[0] = 0; /* terminate phonetic */
+ step = 5;
+ }
+ break;
+
+ case 5:
+ break;
+ }
+ p++;
+ }
+
+ if(word[0] == 0)
+ {
+#ifdef OPT_FORMAT
+ if(comment != NULL)
+ fprintf(f_log,"%s",comment);
+ else
+ fputc('\n',f_log);
+#endif
+ return(0); /* blank line */
+ }
+
+ if(text_mode)
+ text_not_phonemes = 1;
+
+ if(text_not_phonemes != translator->langopts.textmode)
+ {
+ flag_codes[n_flag_codes++] = BITNUM_FLAG_TEXTMODE;
+ }
+
+ if(text_not_phonemes)
+ {
+ // this is replacement text, so don't encode as phonemes. Restrict the length of the replacement word
+ strncpy0(encoded_ph,phonetic,N_WORD_BYTES-4);
+ }
+ else
+ {
+ EncodePhonemes(phonetic,encoded_ph,bad_phoneme);
+ if(strchr(encoded_ph,phonSWITCH) != 0)
+ {
+ flag_codes[n_flag_codes++] = BITNUM_FLAG_ONLY_S; // don't match on suffixes (except 's') when switching languages
+ }
+
+ // check for errors in the phonemes codes
+ for(ix=0; ix<sizeof(encoded_ph); ix++)
+ {
+ c = encoded_ph[ix];
+ if(c == 0) break;
+
+ if(c == 255)
+ {
+ /* unrecognised phoneme, report error */
+ fprintf(f_log,"%5d: Bad phoneme [%c] (0x%x) in: %s %s\n",linenum,bad_phoneme[0],bad_phoneme[0],word,phonetic);
+ error_count++;
+ }
+ }
+ }
+
+ if(sscanf(word,"U+%x",&wc) == 1)
+ {
+ // Character code
+ ix = utf8_out(wc, word);
+ word[ix] = 0;
+ }
+ else
+ if(word[0] != '_')
+ {
+ // convert to lower case, and note if the word is all-capitals
+ int c2;
+
+ all_upper_case = 1;
+ p = word;
+ for(p=word;;)
+ {
+ // this assumes that the lower case char is the same length as the upper case char
+ // OK, except for Turkish "I", but use towlower() rather than towlower2()
+ ix = utf8_in(&c2,p);
+ if(c2 == 0)
+ break;
+ if(iswupper(c2))
+ {
+ utf8_out(towlower(c2),p);
+ }
+ else
+ {
+ all_upper_case = 0;
+ }
+ p += ix;
+ }
+ if(all_upper_case)
+ {
+ flag_codes[n_flag_codes++] = BITNUM_FLAG_ALLCAPS;
+ }
+ }
+
+ len_word = strlen(word);
+
+ if(transpose_offset > 0)
+ {
+ len_word = TransposeAlphabet(word, transpose_offset, transpose_min, transpose_max);
+ }
+
+ *hash = HashDictionary(word);
+ len_phonetic = strlen(encoded_ph);
+
+ dict_line[1] = len_word; // bit 6 indicates whether the word has been compressed
+ len_word &= 0x3f;
+
+ memcpy(&dict_line[2],word,len_word);
+
+ if(len_phonetic == 0)
+ {
+ // no phonemes specified. set bit 7
+ dict_line[1] |= 0x80;
+ length = len_word + 2;
+ }
+ else
+ {
+ length = len_word + len_phonetic + 3;
+ strcpy(&dict_line[(len_word)+2],encoded_ph);
+ }
+
+ for(ix=0; ix<n_flag_codes; ix++)
+ {
+ dict_line[ix+length] = flag_codes[ix];
+ }
+ length += n_flag_codes;
+
+ if((multiple_string != NULL) && (multiple_words > 0))
+ {
+ if(multiple_words > 10)
+ {
+ fprintf(f_log,"%5d: Two many parts in a multi-word entry: %d\n",linenum,multiple_words);
+ }
+ else
+ {
+ dict_line[length++] = 80 + multiple_words;
+ ix = multiple_string_end - multiple_string;
+ memcpy(&dict_line[length],multiple_string,ix);
+ length += ix;
+ }
+ }
+ dict_line[0] = length;
+
+#ifdef OPT_FORMAT
+ spaces = 16;
+ for(ix=0; ix<n_flag_codes; ix++)
+ {
+ if(flag_codes[ix] >= 100)
+ {
+ fprintf(f_log,"?%d ",flag_codes[ix]-100);
+ spaces -= 3;
+ }
+ }
+
+ fprintf(f_log,"%s",word);
+ spaces -= strlen(word);
+ DecodePhonemes(encoded_ph,decoded_ph);
+ while(spaces-- > 0) fputc(' ',f_log);
+ spaces += (14 - strlen(decoded_ph));
+
+ fprintf(f_log," %s",decoded_ph);
+ while(spaces-- > 0) fputc(' ',f_log);
+ for(ix=0; ix<n_flag_codes; ix++)
+ {
+ if(flag_codes[ix] < 100)
+ fprintf(f_log," %s",lookup_mnem(mnem_flags,flag_codes[ix]));
+ }
+ if(comment != NULL)
+ fprintf(f_log," %s",comment);
+ else
+ fputc('\n',f_log);
+#endif
+
+ return(length);
+} /* end of compile_line */
+
+
+
+static void compile_dictlist_start(void)
+{//=====================================
+// initialise dictionary list
+ int ix;
+ char *p;
+ char *p2;
+
+ for(ix=0; ix<N_HASH_DICT; ix++)
+ {
+ p = hash_chains[ix];
+ while(p != NULL)
+ {
+ memcpy(&p2,p,sizeof(char *));
+ free(p);
+ p = p2;
+ }
+ hash_chains[ix] = NULL;
+ hash_counts[ix]=0;
+ }
+}
+
+
+static void compile_dictlist_end(FILE *f_out)
+{//==========================================
+// Write out the compiled dictionary list
+ int hash;
+ int length;
+ char *p;
+
+ if(f_log != NULL)
+ {
+#ifdef OUTPUT_FORMAT
+ for(hash=0; hash<N_HASH_DICT; hash++)
+ {
+ fprintf(f_log,"%8d",hash_counts[hash]);
+ if((hash & 7) == 7)
+ fputc('\n',f_log);
+ }
+ fflush(f_log);
+#endif
+ }
+
+ for(hash=0; hash<N_HASH_DICT; hash++)
+ {
+ p = hash_chains[hash];
+ hash_counts[hash] = (int)ftell(f_out);
+
+ while(p != NULL)
+ {
+ length = *(p+sizeof(char *));
+ fwrite(p+sizeof(char *),length,1,f_out);
+ memcpy(&p,p,sizeof(char *));
+ }
+ fputc(0,f_out);
+ }
+}
+
+
+
+static int compile_dictlist_file(const char *path, const char* filename)
+{//=====================================================================
+ int length;
+ int hash;
+ char *p;
+ int count=0;
+ FILE *f_in;
+ char buf[200];
+ char fname[sizeof(path_home)+45];
+ char dict_line[128];
+
+ text_mode = 0;
+
+ sprintf(fname,"%s%s",path,filename);
+ if((f_in = fopen(fname,"r")) == NULL)
+ return(-1);
+
+ fprintf(f_log,"Compiling: '%s'\n",fname);
+
+ linenum=0;
+
+ while(fgets(buf,sizeof(buf),f_in) != NULL)
+ {
+ linenum++;
+
+ length = compile_line(buf,dict_line,&hash);
+ if(length == 0) continue; /* blank line */
+
+ hash_counts[hash]++;
+
+ p = (char *)malloc(length+sizeof(char *));
+ if(p == NULL)
+ {
+ if(f_log != NULL)
+ {
+ fprintf(f_log,"Can't allocate memory\n");
+ error_count++;
+ }
+ break;
+ }
+
+ memcpy(p,&hash_chains[hash],sizeof(char *));
+ hash_chains[hash] = p;
+ memcpy(p+sizeof(char *),dict_line,length);
+ count++;
+ }
+
+ fprintf(f_log,"\t%d entries\n",count);
+ fclose(f_in);
+ return(0);
+} /* end of compile_dictlist_file */
+
+
+
+static char rule_cond[80];
+static char rule_pre[80];
+static char rule_post[80];
+static char rule_match[80];
+static char rule_phonemes[80];
+static char group_name[LEN_GROUP_NAME+1];
+
+#define N_RULES 2000 // max rules for each group
+
+
+
+static void copy_rule_string(char *string, int &state)
+{//===================================================
+// state 0: conditional, 1=pre, 2=match, 3=post, 4=phonemes
+ static char *outbuf[5] = {rule_cond, rule_pre, rule_match, rule_post, rule_phonemes};
+ static int next_state[5] = {2,2,4,4,4};
+ char *output;
+ char *p;
+ int ix;
+ int len;
+ char c;
+ int sxflags;
+ int value;
+ int literal;
+
+ if(string[0] == 0) return;
+
+ output = outbuf[state];
+ if(state==4)
+ {
+ // append to any previous phoneme string, i.e. allow spaces in the phoneme string
+ len = strlen(rule_phonemes);
+ if(len > 0)
+ rule_phonemes[len++] = ' ';
+ output = &rule_phonemes[len];
+ }
+ sxflags = 0x808000; // to ensure non-zero bytes
+
+ for(p=string,ix=0;;)
+ {
+ literal = 0;
+ c = *p++;
+ if(c == '\\')
+ {
+ c = *p++; // treat next character literally
+ if((c >= '0') && (c <= '3') && (p[0] >= '0') && (p[0] <= '7') && (p[1] >= '0') && (p[1] <= '7'))
+ {
+ // character code given by 3 digit octal value;
+ c = (c-'0')*64 + (p[0]-'0')*8 + (p[1]-'0');
+ p += 2;
+ }
+ literal = 1;
+ }
+
+ if((state==1) || (state==3))
+ {
+ // replace special characters (note: 'E' is reserved for a replaced silent 'e')
+ if(literal == 0)
+ {
+ static const char lettergp_letters[9] = {LETTERGP_A,LETTERGP_B,LETTERGP_C,0,0,LETTERGP_F,LETTERGP_G,LETTERGP_H,LETTERGP_Y};
+ switch(c)
+ {
+ case '_':
+ c = RULE_SPACE;
+ break;
+
+ case 'Y':
+ c = 'I'; // drop through to next case
+ case 'A': // vowel
+ case 'B':
+ case 'C':
+ case 'H':
+ case 'F':
+ case 'G':
+ if(state == 1)
+ {
+ // pre-rule, put the number before the RULE_LETTERGP;
+ output[ix++] = lettergp_letters[c-'A'] + 'A';
+ c = RULE_LETTERGP;
+ }
+ else
+ {
+ output[ix++] = RULE_LETTERGP;
+ c = lettergp_letters[c-'A'] + 'A';
+ }
+ break;
+ case 'D':
+ c = RULE_DIGIT;
+ break;
+ case 'K':
+ c = RULE_NOTVOWEL;
+ break;
+ case 'N':
+ c = RULE_NO_SUFFIX;
+ break;
+ case 'V':
+ c = RULE_IFVERB;
+ break;
+ case 'Z':
+ c = RULE_NONALPHA;
+ break;
+ case '+':
+ c = RULE_INC_SCORE;
+ break;
+ case '@':
+ c = RULE_SYLLABLE;
+ break;
+ case '&':
+ c = RULE_STRESSED;
+ break;
+ case '%':
+ c = RULE_DOUBLE;
+ break;
+ case '#':
+ c = RULE_DEL_FWD;
+ break;
+ case '!':
+ c = RULE_CAPITAL;
+ break;
+ case 'T':
+ c = RULE_ALT1;
+ break;
+ case 'W':
+ c = RULE_SPELLING;
+ break;
+ case 'X':
+ c = RULE_NOVOWELS;
+ break;
+ case 'L':
+ // expect two digits
+ c = *p++ - '0';
+ value = *p++ - '0';
+ c = c * 10 + value;
+ if((value < 0) || (value > 9))
+ {
+ c = 0;
+ fprintf(f_log,"%5d: Expected 2 digits after 'L'\n",linenum);
+ error_count++;
+ }
+ else
+ if((c <= 0) || (c >= N_LETTER_GROUPS) || (letterGroupsDefined[(int)c] == 0))
+ {
+ fprintf(f_log,"%5d: Letter group L%.2d not defined\n",linenum,c);
+ error_count++;
+ }
+ c += 'A';
+ if(state == 1)
+ {
+ // pre-rule, put the group number before the RULE_LETTERGP command
+ output[ix++] = c;
+ c = RULE_LETTERGP2;
+ }
+ else
+ {
+ output[ix++] = RULE_LETTERGP2;
+ }
+ break;
+
+ case '$': // obsolete, replaced by S
+ fprintf(f_log,"%5d: $ now not allowed, use S for suffix",linenum);
+ error_count++;
+ break;
+ case 'P':
+ sxflags |= SUFX_P; // Prefix, now drop through to Suffix
+ case 'S':
+ output[ix++] = RULE_ENDING;
+ value = 0;
+ while(!isspace2(c = *p++) && (c != 0))
+ {
+ switch(c)
+ {
+ case 'e':
+ sxflags |= SUFX_E;
+ break;
+ case 'i':
+ sxflags |= SUFX_I;
+ break;
+ case 'p': // obsolete, replaced by 'P' above
+ sxflags |= SUFX_P;
+ break;
+ case 'v':
+ sxflags |= SUFX_V;
+ break;
+ case 'd':
+ sxflags |= SUFX_D;
+ break;
+ case 'f':
+ sxflags |= SUFX_F;
+ break;
+ case 'q':
+ sxflags |= SUFX_Q;
+ break;
+ case 't':
+ sxflags |= SUFX_T;
+ break;
+ case 'b':
+ sxflags |= SUFX_B;
+ break;
+ default:
+ if(isdigit(c))
+ value = (value*10) + (c - '0');
+ break;
+ }
+ }
+ p--;
+ output[ix++] = sxflags >> 16;
+ output[ix++] = sxflags >> 8;
+ c = value | 0x80;
+ break;
+ }
+ }
+ }
+ output[ix++] = c;
+ if(c == 0) break;
+ }
+
+ state = next_state[state];
+} // end of copy_rule_string
+
+
+
+static char *compile_rule(char *input)
+{//===================================
+ int ix;
+ unsigned char c;
+ int wc;
+ char *p;
+ char *prule;
+ int len;
+ int len_name;
+ int state=2;
+ int finish=0;
+ int pre_bracket=0;
+ char buf[80];
+ char output[150];
+ unsigned char bad_phoneme[4];
+
+ buf[0]=0;
+ rule_cond[0]=0;
+ rule_pre[0]=0;
+ rule_post[0]=0;
+ rule_match[0]=0;
+ rule_phonemes[0]=0;
+
+ p = buf;
+
+ for(ix=0; finish==0; ix++)
+ {
+ c = input[ix];
+
+ switch(c = input[ix])
+ {
+ case ')': // end of prefix section
+ *p = 0;
+ state = 1;
+ pre_bracket = 1;
+ copy_rule_string(buf,state);
+ p = buf;
+ break;
+
+ case '(': // start of suffix section
+ *p = 0;
+ state = 2;
+ copy_rule_string(buf,state);
+ state = 3;
+ p = buf;
+ break;
+
+ case '\n': // end of line
+ case '\r':
+ case 0: // end of line
+ *p = 0;
+ copy_rule_string(buf,state);
+ finish=1;
+ break;
+
+ case '\t': // end of section section
+ case ' ':
+ *p = 0;
+ copy_rule_string(buf,state);
+ p = buf;
+ break;
+
+ case '?':
+ if(state==2)
+ state=0;
+ else
+ *p++ = c;
+ break;
+
+ default:
+ *p++ = c;
+ break;
+ }
+ }
+
+ if(strcmp(rule_match,"$group")==0)
+ strcpy(rule_match,group_name);
+
+ if(rule_match[0]==0)
+ return(NULL);
+
+ EncodePhonemes(rule_phonemes,buf,bad_phoneme);
+ for(ix=0;; ix++)
+ {
+ if((c = buf[ix])==0) break;
+ if(c==255)
+ {
+ fprintf(f_log,"%5d: Bad phoneme [%c] in %s",linenum,bad_phoneme[0],input);
+ error_count++;
+ break;
+ }
+ }
+ strcpy(output,buf);
+ len = strlen(buf)+1;
+
+ len_name = strlen(group_name);
+ if((len_name > 0) && (memcmp(rule_match,group_name,len_name) != 0))
+ {
+ utf8_in(&wc,rule_match);
+ if((group_name[0] == '9') && IsDigit(wc))
+ {
+ // numeric group, rule_match starts with a digit, so OK
+ }
+ else
+ {
+ fprintf(f_log,"%5d: Wrong initial letters '%s' for group '%s'\n",linenum,rule_match,group_name);
+ error_count++;
+ }
+ }
+ strcpy(&output[len],rule_match);
+ len += strlen(rule_match);
+
+ if(debug_flag)
+ {
+ output[len] = RULE_LINENUM;
+ output[len+1] = (linenum % 255) + 1;
+ output[len+2] = (linenum / 255) + 1;
+ len+=3;
+ }
+
+ if(rule_cond[0] != 0)
+ {
+ ix = -1;
+ if(rule_cond[0] == '!')
+ {
+ // allow the rule only if the condition number is NOT set for the voice
+ ix = atoi(&rule_cond[1]) + 32;
+ }
+ else
+ {
+ // allow the rule only if the condition number is set for the voice
+ ix = atoi(rule_cond);
+ }
+
+ if((ix > 0) && (ix < 255))
+ {
+ output[len++] = RULE_CONDITION;
+ output[len++] = ix;
+ }
+ else
+ {
+ fprintf(f_log,"%5d: bad condition number ?%d\n",linenum,ix);
+ error_count++;
+ }
+ }
+ if(rule_pre[0] != 0)
+ {
+ output[len++] = RULE_PRE;
+ // output PRE string in reverse order
+ for(ix = strlen(rule_pre)-1; ix>=0; ix--)
+ output[len++] = rule_pre[ix];
+ }
+
+ if(rule_post[0] != 0)
+ {
+ sprintf(&output[len],"%c%s",RULE_POST,rule_post);
+ len += (strlen(rule_post)+1);
+ }
+ output[len++]=0;
+ prule = (char *)malloc(len);
+ memcpy(prule,output,len);
+ return(prule);
+} // end of compile_rule
+
+
+static int __cdecl string_sorter(char **a, char **b)
+{//=================================================
+ char *pa, *pb;
+ int ix;
+
+ if((ix = strcmp(pa = *a,pb = *b)) != 0)
+ return(ix);
+ pa += (strlen(pa)+1);
+ pb += (strlen(pb)+1);
+ return(strcmp(pa,pb));
+} /* end of string_sorter */
+
+
+static int __cdecl rgroup_sorter(RGROUP *a, RGROUP *b)
+{//===================================================
+ int ix;
+ ix = strcmp(a->name,b->name);
+ if(ix != 0) return(ix);
+ return(a->start-b->start);
+}
+
+
+#ifdef OUTPUT_FORMAT
+static void print_rule_group(FILE *f_out, int n_rules, char **rules, char *name)
+{//=============================================================================
+ int rule;
+ int ix;
+ unsigned char c;
+ int len1;
+ int len2;
+ int spaces;
+ char *p;
+ char *pout;
+ int condition;
+ char buf[80];
+ char suffix[12];
+
+ static unsigned char symbols[] = {'@','&','%','+','#','$','D','Z','A','B','C','F'};
+
+ fprintf(f_out,"\n$group %s\n",name);
+
+ for(rule=0; rule<n_rules; rule++)
+ {
+ p = rules[rule];
+ len1 = strlen(p) + 1;
+ p = &p[len1];
+ len2 = strlen(p);
+
+ rule_match[0]=0;
+ rule_pre[0]=0;
+ rule_post[0]=0;
+ condition = 0;
+
+ pout = rule_match;
+ for(ix=0; ix<len2; ix++)
+ {
+ switch(c = p[ix])
+ {
+ case RULE_PRE:
+ *pout = 0;
+ pout = rule_pre;
+ break;
+ case RULE_POST:
+ *pout = 0;
+ pout = rule_post;
+ break;
+ case RULE_CONDITION:
+ condition = p[++ix];
+ break;
+ case RULE_ENDING:
+ sprintf(suffix,"$%d[%x]",(p[ix+2]),p[ix+1] & 0x7f);
+ ix += 2;
+ strcpy(pout,suffix);
+ pout += strlen(suffix);
+ break;
+ default:
+ if(c <= RULE_LETTER7)
+ c = symbols[c-RULE_SYLLABLE];
+ if(c == ' ')
+ c = '_';
+ *pout++ = c;
+ break;
+ }
+ }
+ *pout = 0;
+
+ spaces = 12;
+ if(condition > 0)
+ {
+ sprintf(buf,"?%d ",condition);
+ spaces -= strlen(buf);
+ fprintf(f_out,"%s",buf);
+ }
+
+ if(rule_pre[0] != 0)
+ {
+ p = buf;
+ for(ix=strlen(rule_pre)-1;ix>=0;ix--)
+ *p++ = rule_pre[ix];
+ sprintf(p,") ");
+ spaces -= strlen(buf);
+ for(ix=0; ix<spaces; ix++)
+ fputc(' ',f_out);
+ fprintf(f_out,"%s",buf);
+ spaces = 0;
+ }
+
+ for(ix=0; ix<spaces; ix++)
+ fputc(' ',f_out);
+
+ spaces = 14;
+ sprintf(buf," %s ",rule_match);
+ if(rule_post[0] != 0)
+ {
+ p = &buf[strlen(buf)];
+ sprintf(p,"(%s ",rule_post);
+ }
+ fprintf(f_out,"%s",buf);
+ spaces -= strlen(buf);
+
+ for(ix=0; ix<spaces; ix++)
+ fputc(' ',f_out);
+ DecodePhonemes(rules[rule],buf);
+ fprintf(f_out,"%s\n",buf); // phonemes
+ }
+}
+#endif
+
+
+//#define LIST_GROUP_INFO
+static void output_rule_group(FILE *f_out, int n_rules, char **rules, char *name)
+{//==============================================================================
+ int ix;
+ int len1;
+ int len2;
+ int len_name;
+ char *p;
+ char *p2, *p3;
+ const char *common;
+
+ short nextchar_count[256];
+ memset(nextchar_count,0,sizeof(nextchar_count));
+
+ len_name = strlen(name);
+
+#ifdef OUTPUT_FORMAT
+ print_rule_group(f_log,n_rules,rules,name);
+#endif
+
+ // sort the rules in this group by their phoneme string
+ common = "";
+ qsort((void *)rules,n_rules,sizeof(char *),(int (__cdecl *)(const void *,const void *))string_sorter);
+
+ if(strcmp(name,"9")==0)
+ len_name = 0; // don't remove characters from numeric match strings
+
+ for(ix=0; ix<n_rules; ix++)
+ {
+ p = rules[ix];
+ len1 = strlen(p) + 1; // phoneme string
+ p3 = &p[len1];
+ p2 = p3 + len_name; // remove group name from start of match string
+ len2 = strlen(p2);
+
+ nextchar_count[(unsigned char)(p2[0])]++; // the next byte after the group name
+
+ if((common[0] != 0) && (strcmp(p,common)==0))
+ {
+ fwrite(p2,len2,1,f_out);
+ fputc(0,f_out); // no phoneme string, it's the same as previous rule
+ }
+ else
+ {
+ if((ix < n_rules-1) && (strcmp(p,rules[ix+1])==0))
+ {
+ common = rules[ix]; // phoneme string is same as next, set as common
+ fputc(RULE_PH_COMMON,f_out);
+ }
+
+ fwrite(p2,len2,1,f_out);
+ fputc(RULE_PHONEMES,f_out);
+ fwrite(p,len1,1,f_out);
+ }
+ }
+
+#ifdef LIST_GROUP_INFO
+ for(ix=32; ix<256; ix++)
+ {
+ if(nextchar_count[ix] > 30)
+ printf("Group %s %c %d\n",name,ix,nextchar_count[ix]);
+ }
+#endif
+} // end of output_rule_group
+
+
+
+static int compile_lettergroup(char *input, FILE *f_out)
+{//=====================================================
+ char *p;
+ char *p_start;
+ int group;
+ int ix;
+ int n_items;
+ int length;
+ int max_length = 0;
+
+ #define N_LETTERGP_ITEMS 200
+ char *items[N_LETTERGP_ITEMS];
+ char item_length[N_LETTERGP_ITEMS];
+
+ p = input;
+ if(!isdigit(p[0]) || !isdigit(p[1]))
+ {
+ fprintf(f_log,"%5d: Expected 2 digits after '.L'\n",linenum);
+ error_count++;
+ return(1);
+ }
+
+ group = atoi(&p[0]);
+ if(group >= N_LETTER_GROUPS)
+ {
+ fprintf(f_log,"%5d: lettergroup out of range (01-%.2d)\n",linenum,N_LETTER_GROUPS-1);
+ error_count++;
+ return(1);
+ }
+
+ while(!isspace2(*p)) p++;
+
+ fputc(RULE_GROUP_START,f_out);
+ fputc(RULE_LETTERGP2,f_out);
+ fputc(group + 'A', f_out);
+ letterGroupsDefined[group] = 1;
+
+ n_items = 0;
+ while(n_items < N_LETTERGP_ITEMS)
+ {
+ while(isspace2(*p)) p++;
+ if(*p == 0)
+ break;
+
+ items[n_items] = p_start = p;
+ while((*p & 0xff) > ' ')
+ {
+ p++;
+ }
+ *p++ = 0;
+ length = p - p_start;
+ if(length > max_length)
+ max_length = length;
+ item_length[n_items++] = length;
+ }
+
+ // write out the items, longest first
+ while(max_length > 1)
+ {
+ for(ix=0; ix < n_items; ix++)
+ {
+ if(item_length[ix] == max_length)
+ {
+ fwrite(items[ix],1,max_length,f_out);
+ }
+ }
+ max_length--;
+ }
+
+ fputc(RULE_GROUP_END,f_out);
+
+ return(0);
+}
+
+
+static int compile_dictrules(FILE *f_in, FILE *f_out, char *fname_temp)
+{//====================================================================
+ char *prule;
+ unsigned char *p;
+ int ix;
+ int c;
+ int gp;
+ FILE *f_temp;
+ int n_rules=0;
+ int count=0;
+ int different;
+ const char *prev_rgroup_name;
+ unsigned int char_code;
+ int compile_mode=0;
+ char *buf;
+ char buf1[200];
+ char *rules[N_RULES];
+
+ int n_rgroups = 0;
+ RGROUP rgroup[N_RULE_GROUP2];
+
+ linenum = 0;
+ group_name[0] = 0;
+
+ if((f_temp = fopen_log(fname_temp,"wb")) == NULL)
+ return(1);
+
+ for(;;)
+ {
+ linenum++;
+ buf = fgets(buf1,sizeof(buf1),f_in);
+ if(buf != NULL)
+ {
+ if((p = (unsigned char *)strstr(buf,"//")) != NULL)
+ *p = 0;
+
+ if(buf[0] == '\r') buf++; // ignore extra \r in \r\n
+ }
+
+ if((buf == NULL) || (buf[0] == '.'))
+ {
+ // next .group or end of file, write out the previous group
+
+ if(n_rules > 0)
+ {
+ strcpy(rgroup[n_rgroups].name,group_name);
+ rgroup[n_rgroups].start = ftell(f_temp);
+ output_rule_group(f_temp,n_rules,rules,group_name);
+ rgroup[n_rgroups].length = ftell(f_temp) - rgroup[n_rgroups].start;
+ n_rgroups++;
+
+ count += n_rules;
+ }
+ n_rules = 0;
+
+ if(compile_mode == 2)
+ {
+ // end of the character replacements section
+ fwrite(&n_rules,1,4,f_out); // write a zero word to terminate the replacemenmt list
+ compile_mode = 0;
+ }
+
+ if(buf == NULL) break; // end of file
+
+ if(memcmp(buf,".L",2)==0)
+ {
+ compile_lettergroup(&buf[2], f_out);
+ continue;
+ }
+
+ if(memcmp(buf,".replace",8)==0)
+ {
+ compile_mode = 2;
+ fputc(RULE_GROUP_START,f_out);
+ fputc(RULE_REPLACEMENTS,f_out);
+
+ // advance to next word boundary
+ while((ftell(f_out) & 3) != 0)
+ fputc(0,f_out);
+ }
+
+ if(memcmp(buf,".group",6)==0)
+ {
+ compile_mode = 1;
+
+ p = (unsigned char *)&buf[6];
+ while((p[0]==' ') || (p[0]=='\t')) p++; // Note: Windows isspace(0xe1) gives TRUE !
+ ix = 0;
+ while((*p > ' ') && (ix < LEN_GROUP_NAME))
+ group_name[ix++] = *p++;
+ group_name[ix]=0;
+
+ if(sscanf(group_name,"0x%x",&char_code)==1)
+ {
+ // group character is given as a character code (max 16 bits)
+ p = (unsigned char *)group_name;
+
+ if(char_code > 0x100)
+ {
+ *p++ = (char_code >> 8);
+ }
+ *p++ = char_code;
+ *p = 0;
+ }
+
+ if(strlen(group_name) > 2)
+ {
+ if(utf8_in(&c,group_name) < 2)
+ {
+ fprintf(f_log,"%5d: Group name longer than 2 bytes (UTF8)",linenum);
+ error_count++;
+ }
+
+ group_name[2] = 0;
+ }
+ }
+
+ continue;
+ }
+
+ switch(compile_mode)
+ {
+ case 1: // .group
+ prule = compile_rule(buf);
+ if((prule != NULL) && (n_rules < N_RULES))
+ {
+ rules[n_rules++] = prule;
+ }
+ break;
+
+ case 2: // .replace
+ {
+ int replace1;
+ int replace2;
+ char *p;
+
+ p = buf;
+ replace1 = 0;
+ replace2 = 0;
+ while(isspace2(*p)) p++;
+ ix = 0;
+ while((unsigned char)(*p) > 0x20) // not space or zero-byte
+ {
+ p += utf8_in(&c,p);
+ replace1 += (c << ix);
+ ix += 16;
+ }
+ while(isspace2(*p)) p++;
+ ix = 0;
+ while((unsigned char)(*p) > 0x20)
+ {
+ p += utf8_in(&c,p);
+ replace2 += (c << ix);
+ ix += 16;
+ }
+ if(replace1 != 0)
+ {
+ Write4Bytes(f_out,replace1); // write as little-endian
+ Write4Bytes(f_out,replace2); // if big-endian, reverse the bytes in LoadDictionary()
+ }
+ }
+ break;
+ }
+ }
+ fclose(f_temp);
+
+ qsort((void *)rgroup,n_rgroups,sizeof(rgroup[0]),(int (__cdecl *)(const void *,const void *))rgroup_sorter);
+
+ if((f_temp = fopen(fname_temp,"rb"))==NULL)
+ return(2);
+
+ prev_rgroup_name = "\n";
+
+ for(gp = 0; gp < n_rgroups; gp++)
+ {
+ fseek(f_temp,rgroup[gp].start,SEEK_SET);
+
+ if((different = strcmp(rgroup[gp].name, prev_rgroup_name)) != 0)
+ {
+ // not the same as the previous group
+ if(gp > 0)
+ fputc(RULE_GROUP_END,f_out);
+ fputc(RULE_GROUP_START,f_out);
+ fprintf(f_out, prev_rgroup_name = rgroup[gp].name);
+ fputc(0,f_out);
+ }
+
+ for(ix=rgroup[gp].length; ix>0; ix--)
+ {
+ c = fgetc(f_temp);
+ fputc(c,f_out);
+ }
+
+ if(different)
+ {
+ }
+ }
+ fputc(RULE_GROUP_END,f_out);
+ fputc(0,f_out);
+
+ fclose(f_temp);
+ remove(fname_temp);
+
+ fprintf(f_log,"\t%d rules, %d groups\n\n",count,n_rgroups);
+ return(0);
+} // end of compile_dictrules
+
+
+
+
+int CompileDictionary(const char *dsource, const char *dict_name, FILE *log, char *fname_err, int flags)
+{//=====================================================================================================
+// fname: space to write the filename in case of error
+// flags: bit 0: include source line number information, for debug purposes.
+
+ FILE *f_in;
+ FILE *f_out;
+ int offset_rules=0;
+ int value;
+ char fname_in[sizeof(path_home)+45];
+ char fname_out[sizeof(path_home)+15];
+ char fname_temp[sizeof(path_home)+15];
+ char path[sizeof(path_home)+40]; // path_dsource+20
+
+ error_count = 0;
+ memset(letterGroupsDefined,0,sizeof(letterGroupsDefined));
+
+ debug_flag = flags & 1;
+
+ if(dsource == NULL)
+ dsource = "";
+
+ f_log = log;
+//f_log = fopen("log2.txt","w");
+ if(f_log == NULL)
+ f_log = stderr;
+
+ sprintf(path,"%s%s_",dsource,dict_name);
+ sprintf(fname_in,"%srules",path);
+ f_in = fopen_log(fname_in,"r");
+ if(f_in == NULL)
+ {
+ if(fname_err)
+ strcpy(fname_err,fname_in);
+ return(-1);
+ }
+
+ sprintf(fname_out,"%s%c%s_dict",path_home,PATHSEP,dict_name);
+ if((f_out = fopen_log(fname_out,"wb+")) == NULL)
+ {
+ if(fname_err)
+ strcpy(fname_err,fname_in);
+ return(-1);
+ }
+ sprintf(fname_temp,"%s%ctemp",path_home,PATHSEP);
+
+ transpose_offset = 0;
+
+ if(strcmp(dict_name,"ru") == 0)
+ {
+ // transpose cyrillic alphabet from unicode to iso8859-5
+// transpose_offset = 0x430-0xd0;
+ transpose_offset = 0x42f; // range 0x01 to 0x22
+ transpose_min = 0x430;
+ transpose_max = 0x451;
+ }
+
+ value = N_HASH_DICT;
+ Write4Bytes(f_out,value);
+ Write4Bytes(f_out,offset_rules);
+
+ compile_dictlist_start();
+
+ fprintf(f_log,"Using phonemetable: '%s'\n",phoneme_tab_list[phoneme_tab_number].name);
+ compile_dictlist_file(path,"roots");
+ if(translator->langopts.listx)
+ {
+ compile_dictlist_file(path,"list");
+ compile_dictlist_file(path,"listx");
+ }
+ else
+ {
+ compile_dictlist_file(path,"listx");
+ compile_dictlist_file(path,"list");
+ }
+ compile_dictlist_file(path,"extra");
+
+ compile_dictlist_end(f_out);
+ offset_rules = ftell(f_out);
+
+ fprintf(f_log,"Compiling: '%s'\n",fname_in);
+
+ compile_dictrules(f_in,f_out,fname_temp);
+ fclose(f_in);
+
+ fseek(f_out,4,SEEK_SET);
+ Write4Bytes(f_out,offset_rules);
+ fclose(f_out);
+
+ LoadDictionary(translator, dict_name, 0);
+
+ return(error_count);
+} // end of compile_dictionary
+
diff --git a/Plugins/eSpeak/eSpeak/debug.cpp b/Plugins/eSpeak/eSpeak/debug.cpp
new file mode 100644
index 0000000..38ea57c
--- /dev/null
+++ b/Plugins/eSpeak/eSpeak/debug.cpp
@@ -0,0 +1,74 @@
+#include <stdio.h>
+#include <stdarg.h>
+#include "speech.h"
+#include "debug.h"
+
+#ifdef DEBUG_ENABLED
+#include <sys/time.h>
+#include <unistd.h>
+
+static FILE* fd_log=NULL;
+static const char* FILENAME="/tmp/espeak.log";
+
+void debug_init()
+{
+ if((fd_log = fopen(FILENAME,"a")) != NULL)
+ setvbuf(fd_log, NULL, _IONBF, 0);
+}
+
+void debug_enter(const char* text)
+{
+ struct timeval tv;
+
+ gettimeofday(&tv, NULL);
+
+ // fd_log = fopen(FILENAME,"a");
+ if (!fd_log)
+ {
+ debug_init();
+ }
+
+ if (fd_log)
+ {
+ fprintf(fd_log, "%03d.%03dms > ENTER %s\n",(int)(tv.tv_sec%1000), (int)(tv.tv_usec/1000), text);
+ // fclose(fd_log);
+ }
+}
+
+
+void debug_show(const char *format, ...)
+{
+ va_list args;
+ va_start(args, format);
+ // fd_log = fopen(FILENAME,"a");
+ if (!fd_log)
+ {
+ debug_init();
+ }
+ if (fd_log)
+ {
+ vfprintf(fd_log, format, args);
+ // fclose(fd_log);
+ }
+ va_end(args);
+}
+
+void debug_time(const char* text)
+{
+ struct timeval tv;
+
+ gettimeofday(&tv, NULL);
+
+ // fd_log = fopen(FILENAME,"a");
+ if (!fd_log)
+ {
+ debug_init();
+ }
+ if (fd_log)
+ {
+ fprintf(fd_log, "%03d.%03dms > %s\n",(int)(tv.tv_sec%1000), (int)(tv.tv_usec/1000), text);
+ // fclose(fd_log);
+ }
+}
+
+#endif
diff --git a/Plugins/eSpeak/eSpeak/debug.h b/Plugins/eSpeak/eSpeak/debug.h
new file mode 100644
index 0000000..c3fb9c9
--- /dev/null
+++ b/Plugins/eSpeak/eSpeak/debug.h
@@ -0,0 +1,26 @@
+#ifndef DEBUG_H
+#define DEBUG_H
+
+//#define DEBUG_ENABLED
+
+#ifdef DEBUG_ENABLED
+#define ENTER(text) debug_enter(text)
+#define SHOW(format,...) debug_show(format,__VA_ARGS__);
+#define SHOW_TIME(text) debug_time(text);
+extern void debug_enter(const char* text);
+extern void debug_show(const char* format,...);
+extern void debug_time(const char* text);
+
+#else
+
+#ifdef PLATFORM_WINDOWS
+#define SHOW(format) // VC6 doesn't allow "..."
+#else
+#define SHOW(format,...)
+#endif
+#define SHOW_TIME(text)
+#define ENTER(text)
+#endif
+
+
+#endif
diff --git a/Plugins/eSpeak/eSpeak/dictionary.cpp b/Plugins/eSpeak/eSpeak/dictionary.cpp
new file mode 100644
index 0000000..f19fd85
--- /dev/null
+++ b/Plugins/eSpeak/eSpeak/dictionary.cpp
@@ -0,0 +1,3433 @@
+/***************************************************************************
+ * Copyright (C) 2005 to 2007 by Jonathan Duddington *
+ * email: jonsd@users.sourceforge.net *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 3 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write see: *
+ * <http://www.gnu.org/licenses/>. *
+ ***************************************************************************/
+
+#include "StdAfx.h"
+
+#define LOG_TRANSLATE
+
+#include <stdio.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <wctype.h>
+#include <wchar.h>
+
+#include "speak_lib.h"
+#include "speech.h"
+#include "phoneme.h"
+#include "synthesize.h"
+#include "translate.h"
+
+
+int dictionary_skipwords;
+char dictionary_name[40];
+
+extern char *print_dictionary_flags(unsigned int *flags);
+
+// accented characters which indicate (in some languages) the start of a separate syllable
+//static const unsigned short diereses_list[7] = {L'ä',L'ë',L'ï',L'ö',L'ü',L'ÿ',0};
+static const unsigned short diereses_list[7] = {0xe4,0xeb,0xef,0xf6,0xfc,0xff,0};
+
+// convert characters to an approximate 7 bit ascii equivalent
+// used for checking for vowels
+static unsigned char remove_accent[] = {
+'a','a','a','a','a','a','a','c','e','e','e','e','i','i','i','i', // 0c0
+'d','n','o','o','o','o','o', 0, 'o','u','u','u','u','y','t','s', // 0d0
+'a','a','a','a','a','a','a','c','e','e','e','e','i','i','i','i', // 0e0
+'d','n','o','o','o','o','o', 0 ,'o','u','u','u','u','y','t','y', // 0f0
+
+'a','a','a','a','a','a','c','c','c','c','c','c','c','c','d','d', // 100
+'d','d','e','e','e','e','e','e','e','e','e','e','g','g','g','g', // 110
+'g','g','g','g','h','h','h','h','i','i','i','i','i','i','i','i', // 120
+'i','i','i','i','j','j','k','k','k','l','l','l','l','l','l','l', // 130
+'l','l','l','n','n','n','n','n','n','n','n','n','o','o','o','o', // 140
+'o','o','o','o','r','r','r','r','r','r','s','s','s','s','s','s', // 150
+'s','s','t','t','t','t','t','t','u','u','u','u','u','u','u','u', // 160
+'u','u','u','u','w','w','y','y','y','z','z','z','z','z','z','s', // 170
+'b','b','b','b', 0, 0, 'o','c','c','d','d','d','d','d','e','e', // 180
+'e','f','f','g','g','h','i','i','k','k','l','l','m','n','n','o', // 190
+'o','o','o','o','p','p','y', 0, 0, 's','s','t','t','t','t','u', // 1a0
+'u','u','v','y','y','z','z','z','z','z','z','z', 0, 0, 0, 'w', // 1b0
+'t','t','t','k','d','d','d','l','l','l','n','n','n','a','a','i', // 1c0
+'i','o','o','u','u','u','u','u','u','u','u','u','u','e','a','a', // 1d0
+'a','a','a','a','g','g','g','g','k','k','o','o','o','o','z','z', // 1e0
+'j','d','d','d','g','g','w','w','n','n','a','a','a','a','o','o', // 1f0
+
+'a','a','a','a','e','e','e','e','i','i','i','i','o','o','o','o', // 200
+'r','r','r','r','u','u','u','u','s','s','t','t','y','y','h','h', // 210
+'n','d','o','o','z','z','a','a','e','e','o','o','o','o','o','o', // 220
+'o','o','y','y','l','n','t','j','d','q','a','c','c','l','t','s', // 230
+'z', 0 };
+
+
+
+
+void strncpy0(char *to,const char *from, int size)
+{//===============================================
+ // strcpy with limit, ensures a zero terminator
+ strncpy(to,from,size);
+ to[size-1] = 0;
+}
+
+
+static int reverse_word_bytes(int word)
+{//=============================
+ // reverse the order of bytes from little-endian to big-endian
+#ifdef ARCH_BIG
+ int ix;
+ int word2 = 0;
+
+ for(ix=0; ix<=24; ix+=8)
+ {
+ word2 = word2 << 8;
+ word2 |= (word >> ix) & 0xff;
+ }
+ return(word2);
+#else
+ return(word);
+#endif
+}
+
+
+int LookupMnem(MNEM_TAB *table, char *string)
+{//==========================================
+ while(table->mnem != NULL)
+ {
+ if(strcmp(string,table->mnem)==0)
+ return(table->value);
+ table++;
+ }
+ return(table->value);
+}
+
+
+
+//=============================================================================================
+// Read pronunciation rules and pronunciation lookup dictionary
+//
+//=============================================================================================
+
+
+static void InitGroups(Translator *tr)
+{//===================================
+/* Called after dictionary 1 is loaded, to set up table of entry points for translation rule chains
+ for single-letters and two-letter combinations
+*/
+
+ int ix;
+ char *p;
+ char *p_name;
+ unsigned int *pw;
+ unsigned char c, c2;
+ int len;
+
+ tr->n_groups2 = 0;
+ for(ix=0; ix<256; ix++)
+ {
+ tr->groups1[ix]=NULL;
+ tr->groups2_count[ix]=0;
+ tr->groups2_start[ix]=255; // indicates "not set"
+ }
+ memset(tr->letterGroups,0,sizeof(tr->letterGroups));
+
+ p = tr->data_dictrules;
+ while(*p != 0)
+ {
+ if(*p != RULE_GROUP_START)
+ {
+ fprintf(stderr,"Bad rules data in '%s_dict' at 0x%x\n",dictionary_name,(unsigned int)(p - tr->data_dictrules));
+ break;
+ }
+ p++;
+
+ if(p[0] == RULE_REPLACEMENTS)
+ {
+ pw = (unsigned int *)(((long)p+4) & ~3); // advance to next word boundary
+ tr->langopts.replace_chars = pw;
+ while(pw[0] != 0)
+ {
+ pw += 2; // find the end of the replacement list, each entry is 2 words.
+ }
+ p = (char *)(pw+1);
+
+#ifdef ARCH_BIG
+ pw = (unsigned int *)(tr->langopts.replace_chars);
+ while(*pw != 0)
+ {
+ *pw = reverse_word_bytes(*pw);
+ pw++;
+ *pw = reverse_word_bytes(*pw);
+ pw++;
+ }
+#endif
+ continue;
+ }
+
+ if(p[0] == RULE_LETTERGP2)
+ {
+ ix = p[1] - 'A';
+ p += 2;
+ if((ix >= 0) && (ix < N_LETTER_GROUPS))
+ {
+ tr->letterGroups[ix] = p;
+ }
+ }
+ else
+ {
+ len = strlen(p);
+ p_name = p;
+ c = p_name[0];
+
+ p += (len+1);
+ if(len == 1)
+ {
+ tr->groups1[c] = p;
+ }
+ else
+ if(len == 0)
+ {
+ tr->groups1[0] = p;
+ }
+ else
+ {
+ if(tr->groups2_start[c] == 255)
+ tr->groups2_start[c] = tr->n_groups2;
+
+ tr->groups2_count[c]++;
+ tr->groups2[tr->n_groups2] = p;
+ c2 = p_name[1];
+ tr->groups2_name[tr->n_groups2++] = (c + (c2 << 8));
+ }
+ }
+
+ // skip over all the rules in this group
+ while(*p != RULE_GROUP_END)
+ {
+ p += (strlen(p) + 1);
+ }
+ p++;
+ }
+
+} // end of InitGroups
+
+
+
+int LoadDictionary(Translator *tr, const char *name, int no_error)
+{//===============================================================
+ int hash;
+ char *p;
+ int *pw;
+ int length;
+ FILE *f;
+ unsigned int size;
+ char fname[sizeof(path_home)+20];
+
+ strcpy(dictionary_name,name); // currently loaded dictionary name
+
+ if(no_error) // don't load dictionary, just set the dictionary_name
+ return(1);
+
+ // Load a pronunciation data file into memory
+ // bytes 0-3: offset to rules data
+ // bytes 4-7: number of hash table entries
+ sprintf(fname,"%s%c%s_dict",path_home,PATHSEP,name);
+ size = GetFileLength(fname);
+
+ f = fopen(fname,"rb");
+ if((f == NULL) || (size <= 0))
+ {
+ if(no_error == 0)
+ {
+ fprintf(stderr,"Can't read dictionary file: '%s'\n",fname);
+ }
+ return(1);
+ }
+
+ if(tr->data_dictlist != NULL)
+ Free(tr->data_dictlist);
+
+ tr->data_dictlist = Alloc(size);
+ fread(tr->data_dictlist,size,1,f);
+ fclose(f);
+
+
+ pw = (int *)(tr->data_dictlist);
+ length = reverse_word_bytes(pw[1]);
+
+ if(size <= (N_HASH_DICT + sizeof(int)*2))
+ {
+ fprintf(stderr,"Empty _dict file: '%s\n",fname);
+ return(2);
+ }
+
+ if((reverse_word_bytes(pw[0]) != N_HASH_DICT) ||
+ (length <= 0) || (length > 0x8000000))
+ {
+ fprintf(stderr,"Bad data: '%s' (%x length=%x)\n",fname,reverse_word_bytes(pw[0]),length);
+ return(2);
+ }
+ tr->data_dictrules = &(tr->data_dictlist[length]);
+
+ // set up indices into data_dictrules
+ InitGroups(tr);
+ if(tr->groups1[0] == NULL)
+ {
+ fprintf(stderr,"Error in %s_rules, no default rule group\n",name);
+ }
+
+ // set up hash table for data_dictlist
+ p = &(tr->data_dictlist[8]);
+
+ for(hash=0; hash<N_HASH_DICT; hash++)
+ {
+ tr->dict_hashtab[hash] = p;
+ while((length = *p) != 0)
+ {
+ p += length;
+ }
+ p++; // skip over the zero which terminates the list for this hash value
+ }
+
+ return(0);
+} // end of LoadDictionary
+
+
+int HashDictionary(const char *string)
+//====================================
+/* Generate a hash code from the specified string
+ This is used to access the dictionary_2 word-lookup dictionary
+*/
+{
+ int c;
+ int chars=0;
+ int hash=0;
+
+ while((c = (*string++ & 0xff)) != 0)
+ {
+ hash = hash * 8 + c;
+ hash = (hash & 0x3ff) ^ (hash >> 8); /* exclusive or */
+ chars++;
+ }
+
+ return((hash+chars) & 0x3ff); // a 10 bit hash code
+} // end of HashDictionary
+
+
+
+//=============================================================================================
+// Translate between internal representation of phonemes and a mnemonic form for display
+//
+//=============================================================================================
+
+
+
+char *EncodePhonemes(char *p, char *outptr, unsigned char *bad_phoneme)
+/*********************************************************************/
+/* Translate a phoneme string from ascii mnemonics to internal phoneme numbers,
+ from 'p' up to next blank .
+ Returns advanced 'p'
+ outptr contains encoded phonemes, unrecognised phonemes are encoded as 255
+ bad_phoneme must point to char array of length 2 of more
+*/
+{
+ int ix;
+ unsigned char c;
+ int count; /* num. of matching characters */
+ int max; /* highest num. of matching found so far */
+ int max_ph; /* corresponding phoneme with highest matching */
+ int consumed;
+ unsigned int mnemonic_word;
+
+ bad_phoneme[0] = 0;
+
+ // skip initial blanks
+ while(isspace(*p))
+ {
+ p++;
+ }
+
+ while(((c = *p) != 0) && !isspace(c))
+ {
+ consumed = 0;
+
+ switch(c)
+ {
+ case '|':
+ // used to separate phoneme mnemonics if needed, to prevent characters being treated
+ // as a multi-letter mnemonic
+
+ if((c = p[1]) == '|')
+ {
+ // treat double || as a word-break symbol, drop through
+ // to the default case with c = '|'
+ }
+ else
+ {
+ p++;
+ break;
+ }
+
+ default:
+ // lookup the phoneme mnemonic, find the phoneme with the highest number of
+ // matching characters
+ max= -1;
+ max_ph= 0;
+
+ for(ix=1; ix<n_phoneme_tab; ix++)
+ {
+ if(phoneme_tab[ix] == NULL)
+ continue;
+ if(phoneme_tab[ix]->type == phINVALID)
+ continue; // this phoneme is not defined for this language
+
+ count = 0;
+ mnemonic_word = phoneme_tab[ix]->mnemonic;
+
+ while(((c = p[count]) > ' ') && (count < 4) &&
+ (c == ((mnemonic_word >> (count*8)) & 0xff)))
+ count++;
+
+ if((count > max) &&
+ ((count == 4) || (((mnemonic_word >> (count*8)) & 0xff)==0)))
+ {
+ max = count;
+ max_ph = phoneme_tab[ix]->code;
+ }
+ }
+
+ if(max_ph == 0)
+ {
+ max_ph = 255; /* not recognised */
+ bad_phoneme[0] = *p;
+ bad_phoneme[1] = 0;
+ }
+
+ if(max <= 0)
+ max = 1;
+ p += (consumed + max);
+ *outptr++ = (char)(max_ph);
+
+ if(max_ph == phonSWITCH)
+ {
+ // Switch Language: this phoneme is followed by a text string
+ char *p_lang = outptr;
+ while(!isspace(c = *p) && (c != 0))
+ {
+ p++;
+ *outptr++ = tolower(c);
+ }
+ *outptr = 0;
+ if(c == 0)
+ {
+ if(strcmp(p_lang,"en")==0)
+ {
+ *p_lang = 0; // don't need "en", it's assumed by default
+ return(p);
+ }
+ }
+ else
+ {
+ *outptr++ = '|'; // more phonemes follow, terminate language string with separator
+ }
+ }
+ break;
+ }
+ }
+ /* terminate the encoded string */
+ *outptr = 0;
+ return(p);
+} // end of EncodePhonemes
+
+
+
+void DecodePhonemes(const char *inptr, char *outptr)
+//==================================================
+// Translate from internal phoneme codes into phoneme mnemonics
+{
+ unsigned char phcode;
+ unsigned char c;
+ unsigned int mnem;
+ PHONEME_TAB *ph;
+ static const char *stress_chars = "==,,'* ";
+
+ while((phcode = *inptr++) > 0)
+ {
+ if(phcode == 255)
+ continue; /* indicates unrecognised phoneme */
+ if((ph = phoneme_tab[phcode]) == NULL)
+ continue;
+
+ if((ph->type == phSTRESS) && (ph->std_length <= 4) && (ph->spect == 0))
+ {
+ if(ph->std_length > 1)
+ *outptr++ = stress_chars[ph->std_length];
+ }
+ else
+ {
+ mnem = ph->mnemonic;
+
+ while((c = (mnem & 0xff)) != 0)
+ {
+ *outptr++ = c;
+ mnem = mnem >> 8;
+ }
+ if(phcode == phonSWITCH)
+ {
+ while(isalpha(*inptr))
+ {
+ *outptr++ = *inptr++;
+ }
+ }
+ }
+ }
+ *outptr = 0; /* string terminator */
+} // end of DecodePhonemes
+
+
+
+static void WriteMnemonic(char *phon_out, int *ix, int mnem)
+{//=========================================================
+ unsigned char c;
+
+ while((c = mnem & 0xff) != 0)
+ {
+ if((c == '/') && (option_phoneme_variants==0))
+ break; // discard phoneme variant indicator
+ phon_out[(*ix)++]= c;
+ // phon_out[phon_out_ix++]= ipa1[c];
+ mnem = mnem >> 8;
+ }
+}
+
+
+
+void GetTranslatedPhonemeString(char *phon_out, int n_phon_out)
+{//============================================================
+/* Can be called after a clause has been translated into phonemes, in order
+ to display the clause in phoneme mnemonic form.
+*/
+
+ int ix;
+ int phon_out_ix=0;
+ int stress;
+ char *p;
+ PHONEME_LIST *plist;
+
+ static const char *stress_chars = "==,,''";
+
+ if(phon_out != NULL)
+ {
+ for(ix=1; ix<(n_phoneme_list-2) && (phon_out_ix < (n_phon_out - 6)); ix++)
+ {
+ plist = &phoneme_list[ix];
+ if(plist->newword)
+ phon_out[phon_out_ix++] = ' ';
+
+ if(plist->synthflags & SFLAG_SYLLABLE)
+ {
+ if((stress = plist->tone) > 1)
+ {
+ if(stress > 5) stress = 5;
+ phon_out[phon_out_ix++] = stress_chars[stress];
+ }
+ }
+ WriteMnemonic(phon_out, &phon_out_ix, plist->ph->mnemonic);
+
+ if(plist->synthflags & SFLAG_LENGTHEN)
+ {
+ WriteMnemonic(phon_out, &phon_out_ix, phoneme_tab[phonLENGTHEN]->mnemonic);
+ }
+ if((plist->synthflags & SFLAG_SYLLABLE) && (plist->type != phVOWEL))
+ {
+ // syllablic consonant
+ WriteMnemonic(phon_out, &phon_out_ix, phoneme_tab[phonSYLLABIC]->mnemonic);
+ }
+ if(plist->ph->code == phonSWITCH)
+ {
+ // the tone_ph field contains a phoneme table number
+ p = phoneme_tab_list[plist->tone_ph].name;
+ while(*p != 0)
+ {
+ phon_out[phon_out_ix++] = *p++;
+ }
+ phon_out[phon_out_ix++] = ' ';
+ }
+ else
+ if(plist->tone_ph > 0)
+ {
+ WriteMnemonic(phon_out, &phon_out_ix, phoneme_tab[plist->tone_ph]->mnemonic);
+ }
+ }
+
+ if(phon_out_ix >= n_phon_out)
+ phon_out_ix = n_phon_out - 1;
+ phon_out[phon_out_ix] = 0;
+ }
+} // end of GetTranslatedPhonemeString
+
+
+
+//=============================================================================================
+// Is a word Unpronouncable - and so should be spoken as individual letters
+//
+//=============================================================================================
+
+
+
+static int IsLetterGroup(Translator *tr, char *word, int group, int pre)
+{//=====================================================================
+ // match the word against a list of utf-8 strings
+ char *p;
+ char *w;
+ int len=0;
+
+ p = tr->letterGroups[group];
+ if(p == NULL)
+ return(0);
+
+ while(*p != RULE_GROUP_END)
+ {
+ if(pre)
+ {
+ len = strlen(p);
+ w = word - len + 1;
+ }
+ else
+ {
+ w = word;
+ }
+ while(*p == *w)
+ {
+ w++;
+ p++;
+ }
+ if(*p == 0)
+ {
+ if(pre)
+ return(len);
+ return(w-word); // matched a complete string
+ }
+
+ while(*p++ != 0); // skip to end of string
+ }
+ return(0);
+}
+
+
+static int IsLetter(Translator *tr, int letter, int group)
+{//=======================================================
+ int letter2;
+
+ if(tr->letter_groups[group] != NULL)
+ {
+ if(wcschr(tr->letter_groups[group],letter))
+ return(1);
+ return(0);
+ }
+
+ if(group > 7)
+ return(0);
+
+ if(tr->letter_bits_offset > 0)
+ {
+ if(((letter2 = (letter - tr->letter_bits_offset)) > 0) && (letter2 < 0x80))
+ letter = letter2;
+ else
+ return(0);
+ }
+ else
+ {
+ if((letter >= 0xc0) && (letter <= 0x241))
+ return(tr->letter_bits[remove_accent[letter-0xc0]] & (1L << group));
+ }
+
+ if((letter >= 0) && (letter < 0x80))
+ return(tr->letter_bits[letter] & (1L << group));
+
+ return(0);
+}
+
+
+static int IsVowel(Translator *tr, int letter)
+{//===========================================
+ return(IsLetter(tr, letter, 0));
+}
+
+
+
+
+static int Unpronouncable_en(Translator *tr, char *word)
+{//=====================================================
+/* Determines whether a word in 'unpronouncable', i.e. whether it should
+ be spoken as individual letters.
+
+ This function is language specific.
+*/
+
+ int c;
+ int vowel_posn=9;
+ int index;
+ int count;
+ int ix;
+ int apostrophe=0;
+
+ static unsigned char initials_bitmap[86] = {
+ 0x00, 0x00, 0x00, 0x00, 0x22, 0x08, 0x00, 0x88, // 0
+ 0x20, 0x24, 0x20, 0x80, 0x10, 0x00, 0x00, 0x00,
+ 0x00, 0x28, 0x08, 0x00, 0x88, 0x22, 0x04, 0x00, // 16
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x88, 0x22, 0x04, 0x00, 0x02, 0x00, 0x04, // 32
+ 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x28, 0x8a, 0x03, 0x00, 0x00, 0x40, 0x00, // 48
+ 0x02, 0x00, 0x41, 0xca, 0x9b, 0x06, 0x20, 0x80,
+ 0x91, 0x00, 0x00, 0x00, 0x00, 0x20, 0x08, 0x00, // 64
+ 0x08, 0x20, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x22, 0x00, 0x01, 0x00, };
+
+
+ // words which we pass through to the dictionary, even though they look unpronouncable
+ static const char *exceptions[] = {
+ "'s ", "st ","nd ","rd ","th ",NULL };
+
+ if((*word == ' ') || (*word == 0))
+ return(0);
+
+ for(ix=0; exceptions[ix] != NULL; ix++)
+ {
+ // Seemingly uncpronouncable words, but to be looked in the dictionary rules instead
+ if(memcmp(word,exceptions[ix],3)==0)
+ return(0);
+ }
+
+ index=0;
+ count=0;
+ for(;;)
+ {
+ index += utf8_in(&c,&word[index]);
+ count++;
+
+ if((c==0) || (c==' '))
+ break;
+
+ if(IsVowel(tr, c) || (c == 'y'))
+ {
+ vowel_posn = count;
+ break;
+ }
+
+ if(c == '\'')
+ apostrophe = 1;
+ else
+ if(!IsAlpha(c))
+ return(0); // letter (not vowel) outside Latin character range or apostrophe, abort test
+ }
+ if((vowel_posn > 5) || ((word[0]!='s') && (vowel_posn > 4)))
+ return(1); // no vowel, or no vowel in first four letters
+
+ /* there is at least one vowel, is the initial letter combination valid ? */
+
+ if(vowel_posn < 3)
+ return(0); /* vowel in first two letters, OK */
+
+ if(apostrophe)
+ return(0); // first two letters not a-z, abort test
+
+ index = (word[0]-'a') * 26 + (word[1]-'a');
+ if(initials_bitmap[index >> 3] & (1L << (index & 7)))
+ return(0);
+ else
+ return(1); /****/
+} /* end of Unpronounceable */
+
+
+
+
+int Unpronouncable(Translator *tr, char *word)
+{//===========================================
+/* Determines whether a word in 'unpronouncable', i.e. whether it should
+ be spoken as individual letters.
+
+ This function may be language specific. This is a generic version.
+*/
+
+ int c;
+ int c1=0;
+ int vowel_posn=9;
+ int index;
+ int count;
+ int apostrophe=0;
+
+ if(tr->translator_name == L('e','n'))
+ {
+ return(Unpronouncable_en(tr,word));
+ }
+
+ utf8_in(&c,word);
+ if((tr->letter_bits_offset > 0) && (c < 0x241))
+ {
+ // Latin characters for a language with a non-latin alphabet
+ return(0); // so we can re-translate the word as English
+ }
+
+ if(tr->langopts.param[LOPT_UNPRONOUNCABLE] == 1)
+ return(0);
+
+ if((*word == ' ') || (*word == 0))
+ return(0);
+
+ index = 0;
+ count = 0;
+ for(;;)
+ {
+ index += utf8_in(&c,&word[index]);
+ if((c==0) || (c==' '))
+ break;
+
+ if(count==0)
+ c1 = c;
+ count++;
+
+ if(IsVowel(tr, c))
+ {
+ vowel_posn = count; // position of the first vowel
+ break;
+ }
+
+ if(c == '\'')
+ apostrophe = 1;
+ else
+ if(!iswalpha(c))
+ return(0); // letter (not vowel) outside a-z range or apostrophe, abort test
+ }
+
+ if((vowel_posn < 9) && (tr->langopts.param[LOPT_UNPRONOUNCABLE] == 2))
+ return(0); // option means allow any word with a vowel
+
+ if(c1 == tr->langopts.param[LOPT_UNPRONOUNCABLE])
+ vowel_posn--; // disregard this as the initial letter when counting
+
+ if(vowel_posn > (tr->langopts.max_initial_consonants+1))
+ return(1); // no vowel, or no vowel in first four letters
+
+return(0);
+
+} /* end of Unpronounceable */
+
+
+
+//=============================================================================================
+// Determine the stress pattern of a word
+//
+//=============================================================================================
+
+
+
+static int GetVowelStress(Translator *tr, unsigned char *phonemes, unsigned char *vowel_stress, int &vowel_count, int &stressed_syllable, int control)
+{//====================================================================================================================================================
+// control = 1, set stress to 1 for forced unstressed vowels
+ unsigned char phcode;
+ PHONEME_TAB *ph;
+ unsigned char *ph_out = phonemes;
+ int count = 1;
+ int max_stress = 0;
+ int ix;
+ int j;
+ int stress = 0;
+ int primary_posn = 0;
+
+ vowel_stress[0] = 0;
+ while(((phcode = *phonemes++) != 0) && (count < (N_WORD_PHONEMES/2)-1))
+ {
+ if((ph = phoneme_tab[phcode]) == NULL)
+ continue;
+
+ if((ph->type == phSTRESS) && (ph->spect == 0))
+ {
+ /* stress marker, use this for the following vowel */
+
+ if(phcode == phonSTRESS_PREV)
+ {
+ /* primary stress on preceeding vowel */
+ j = count - 1;
+ while((j > 0) && (stressed_syllable == 0) && (vowel_stress[j] < 4))
+ {
+ if(vowel_stress[j] != 1)
+ {
+ // don't promote a phoneme which must be unstressed
+ vowel_stress[j] = 4;
+
+ if(max_stress < 4)
+ {
+ max_stress = 4;
+ primary_posn = j;
+ }
+
+ /* reduce any preceding primary stress markers */
+ for(ix=1; ix<j; ix++)
+ {
+ if(vowel_stress[ix] == 4)
+ vowel_stress[ix] = 3;
+ }
+ break;
+ }
+ j--;
+ }
+ }
+ else
+ {
+ if((ph->std_length < 4) || (stressed_syllable == 0))
+ {
+ stress = ph->std_length;
+
+ if(stress > max_stress)
+ max_stress = stress;
+ }
+ }
+ continue;
+ }
+
+ if((ph->type == phVOWEL) && !(ph->phflags & phNONSYLLABIC))
+ {
+ vowel_stress[count] = (char)stress;
+ if((stress >= 4) && (stress >= max_stress))
+ {
+ primary_posn = count;
+ max_stress = stress;
+ }
+
+ if((stress == 0) && (control & 1) && (ph->phflags & phUNSTRESSED))
+ vowel_stress[count] = 1; /* weak vowel, must be unstressed */
+
+ count++;
+ stress = 0;
+ }
+ else
+ if(phcode == phonSYLLABIC)
+ {
+ // previous consonant phoneme is syllablic
+ vowel_stress[count] = (char)stress;
+ if((stress == 0) && (control & 1))
+ vowel_stress[count++] = 1; // syllabic consonant, usually unstressed
+ }
+
+ *ph_out++ = phcode;
+ }
+ vowel_stress[count] = 0;
+ *ph_out = 0;
+
+ /* has the position of the primary stress been specified by $1, $2, etc? */
+ if(stressed_syllable > 0)
+ {
+ if(stressed_syllable >= count)
+ stressed_syllable = count-1; // the final syllable
+
+ vowel_stress[stressed_syllable] = 4;
+ max_stress = 4;
+ primary_posn = stressed_syllable;
+ }
+
+ if(max_stress == 5)
+ {
+ // priority stress, replaces any other primary stress marker
+ for(ix=1; ix<count; ix++)
+ {
+ if(vowel_stress[ix] == 4)
+ {
+ if(tr->langopts.stress_flags & 0x20000)
+ vowel_stress[ix] = 0;
+ else
+ vowel_stress[ix] = 3;
+ }
+
+ if(vowel_stress[ix] == 5)
+ {
+ vowel_stress[ix] = 4;
+ primary_posn = ix;
+ }
+ }
+ max_stress = 4;
+ }
+
+ stressed_syllable = primary_posn;
+ vowel_count = count;
+ return(max_stress);
+} // end of GetVowelStress
+
+
+
+static char stress_phonemes[] = {phonSTRESS_U, phonSTRESS_D, phonSTRESS_2, phonSTRESS_3,
+ phonSTRESS_P, phonSTRESS_P2, phonSTRESS_TONIC};
+
+
+void ChangeWordStress(Translator *tr, char *word, int new_stress)
+{//==============================================================
+ int ix;
+ unsigned char *p;
+ int max_stress;
+ int vowel_count; // num of vowels + 1
+ int stressed_syllable=0; // position of stressed syllable
+ unsigned char phonetic[N_WORD_PHONEMES];
+ unsigned char vowel_stress[N_WORD_PHONEMES/2];
+
+ strcpy((char *)phonetic,word);
+ max_stress = GetVowelStress(tr, phonetic, vowel_stress, vowel_count, stressed_syllable, 0);
+
+ if(new_stress >= 4)
+ {
+ // promote to primary stress
+ for(ix=1; ix<vowel_count; ix++)
+ {
+ if(vowel_stress[ix] >= max_stress)
+ {
+ vowel_stress[ix] = new_stress;
+ break;
+ }
+ }
+ }
+ else
+ {
+ // remove primary stress
+ for(ix=1; ix<vowel_count; ix++)
+ {
+ if(vowel_stress[ix] > new_stress) // >= allows for diminished stress (=1)
+ vowel_stress[ix] = new_stress;
+ }
+ }
+
+ // write out phonemes
+ ix = 1;
+ p = phonetic;
+ while(*p != 0)
+ {
+ if((phoneme_tab[*p]->type == phVOWEL) && !(phoneme_tab[*p]->phflags & phNONSYLLABIC))
+ {
+ if(vowel_stress[ix] != 0)
+ *word++ = stress_phonemes[vowel_stress[ix]];
+
+ ix++;
+ }
+ *word++ = *p++;
+ }
+ *word = 0;
+} // end of ChangeWordStress
+
+
+
+void SetWordStress(Translator *tr, char *output, unsigned int dictionary_flags, int tonic, int prev_stress)
+{//========================================================================================================
+/* Guess stress pattern of word. This is language specific
+
+ 'dictionary_flags' has bits 0-3 position of stressed vowel (if > 0)
+ or unstressed (if == 7) or syllables 1 and 2 (if == 6)
+ bits 8... dictionary flags
+
+ If 'tonic' is set (>= 0), replace highest stress by this value.
+
+ Parameter used for input and output
+*/
+
+ unsigned char phcode;
+ unsigned char *p;
+ PHONEME_TAB *ph;
+ int stress;
+ int max_stress;
+ int vowel_count; // num of vowels + 1
+ int ix;
+ int v;
+ int v_stress;
+ int stressed_syllable; // position of stressed syllable
+ int max_stress_posn;
+ int unstressed_word = 0;
+ char *max_output;
+ int final_ph;
+ int final_ph2;
+ int mnem;
+ int mnem2;
+ int post_tonic;
+ int opt_length;
+ int done;
+ int stressflags;
+
+ unsigned char vowel_stress[N_WORD_PHONEMES/2];
+ char syllable_weight[N_WORD_PHONEMES/2];
+ char vowel_length[N_WORD_PHONEMES/2];
+ unsigned char phonetic[N_WORD_PHONEMES];
+
+ static char consonant_types[16] = {0,0,0,1,1,1,1,1,1,1,0,0,0,0,0,0};
+
+
+ /* stress numbers STRESS_BASE +
+ 0 diminished, unstressed within a word
+ 1 unstressed, weak
+ 2
+ 3 secondary stress
+ 4 main stress */
+
+ stressflags = tr->langopts.stress_flags;
+
+ /* copy input string into internal buffer */
+ for(ix=0; ix<N_WORD_PHONEMES; ix++)
+ {
+ phonetic[ix] = output[ix];
+ // check for unknown phoneme codes
+ if(phonetic[ix] >= n_phoneme_tab)
+ phonetic[ix] = phonSCHWA;
+ if(phonetic[ix] == 0)
+ break;
+ }
+ if(ix == 0) return;
+ final_ph = phonetic[ix-1];
+ final_ph2 = phonetic[ix-2];
+
+ max_output = output + (N_WORD_PHONEMES-3); /* check for overrun */
+
+ // any stress position marked in the xx_list dictionary ?
+ stressed_syllable = dictionary_flags & 0x7;
+ if(dictionary_flags & 0x8)
+ {
+ // this indicates a word without a primary stress
+ stressed_syllable = dictionary_flags & 0x3;
+ unstressed_word = 1;
+ }
+
+ max_stress = GetVowelStress(tr, phonetic, vowel_stress, vowel_count, stressed_syllable, 1);
+
+ // heavy or light syllables
+ ix = 1;
+ for(p = phonetic; *p != 0; p++)
+ {
+ if((phoneme_tab[p[0]]->type == phVOWEL) && !(phoneme_tab[p[0]]->phflags & phNONSYLLABIC))
+ {
+ int weight = 0;
+ int lengthened = 0;
+
+ if(phoneme_tab[p[1]]->code == phonLENGTHEN)
+ lengthened = 1;
+
+ if(lengthened || (phoneme_tab[p[0]]->phflags & phLONG))
+ {
+ // long vowel, increase syllable weight
+ weight++;
+ }
+ vowel_length[ix] = weight;
+
+ if(lengthened) p++; // advance over phonLENGTHEN
+
+ if(consonant_types[phoneme_tab[p[1]]->type] && ((phoneme_tab[p[2]]->type != phVOWEL) || (phoneme_tab[p[1]]->phflags & phLONG)))
+ {
+ // followed by two consonants, a long consonant, or consonant and end-of-word
+ weight++;
+ }
+ syllable_weight[ix] = weight;
+ ix++;
+ }
+ }
+
+ switch(tr->langopts.stress_rule)
+ {
+ case 8:
+ // stress on first syllable, unless it is a light syllable
+ if(syllable_weight[1] > 0)
+ break;
+ // else drop through to case 1
+ case 1:
+ // stress on second syllable
+ if((stressed_syllable == 0) && (vowel_count > 2))
+ {
+ stressed_syllable = 2;
+ if(max_stress == 0)
+ {
+ vowel_stress[stressed_syllable] = 4;
+ }
+ max_stress = 4;
+ }
+ break;
+
+ case 2:
+ // a language with stress on penultimate vowel
+
+ if(stressed_syllable == 0)
+ {
+ /* no explicit stress - stress the penultimate vowel */
+ max_stress = 4;
+
+ if(vowel_count > 2)
+ {
+ stressed_syllable = vowel_count - 2;
+
+ if(stressflags & 0x300)
+ {
+ // LANG=Spanish, stress on last vowel if the word ends in a consonant other than 'n' or 's'
+ if(phoneme_tab[final_ph]->type != phVOWEL)
+ {
+ if(stressflags & 0x100)
+ {
+ stressed_syllable = vowel_count - 1;
+ }
+ else
+ {
+ mnem = phoneme_tab[final_ph]->mnemonic;
+ mnem2 = phoneme_tab[final_ph2]->mnemonic;
+
+ if((mnem == 's') && (mnem2 == 'n'))
+ {
+ // -ns stress remains on penultimate syllable
+ }
+ else
+ if(((mnem != 'n') && (mnem != 's')) || (phoneme_tab[final_ph2]->type != phVOWEL))
+ {
+ stressed_syllable = vowel_count - 1;
+ }
+ }
+ }
+ }
+ if(stressflags & 0x80000)
+ {
+ // stress on last syllable if it has a long vowel, but previous syllable has a short vowel
+ if(vowel_length[vowel_count - 1] > vowel_length[vowel_count - 2])
+ {
+ stressed_syllable = vowel_count - 1;
+ }
+ }
+
+ if(vowel_stress[stressed_syllable] == 1)
+ {
+ // but this vowel is explicitly marked as unstressed
+ if(stressed_syllable > 1)
+ stressed_syllable--;
+ else
+ stressed_syllable++;
+ }
+ }
+ else
+ {
+ stressed_syllable = 1;
+ if(stressflags & 0x1)
+ max_stress = 3; // don't give full stress to monosyllables
+ }
+
+ // only set the stress if it's not already marked explicitly
+ if(vowel_stress[stressed_syllable] == 0)
+ {
+ // don't stress if next and prev syllables are stressed
+ if((vowel_stress[stressed_syllable-1] < 4) || (vowel_stress[stressed_syllable+1] < 4))
+ vowel_stress[stressed_syllable] = max_stress;
+ }
+ }
+ break;
+
+ case 3:
+ // stress on last vowel
+ if(stressed_syllable == 0)
+ {
+ /* no explicit stress - stress the final vowel */
+ stressed_syllable = vowel_count - 1;
+ if(max_stress == 0)
+ {
+ while(stressed_syllable > 0)
+ {
+ if(vowel_stress[stressed_syllable] == 0)
+ {
+ vowel_stress[stressed_syllable] = 4;
+ break;
+ }
+ else
+ stressed_syllable--;
+ }
+ }
+ max_stress = 4;
+ }
+ break;
+
+ case 4: // stress on antipenultimate vowel
+ if(stressed_syllable == 0)
+ {
+ stressed_syllable = vowel_count - 3;
+ if(stressed_syllable < 1)
+ stressed_syllable = 1;
+
+ if(max_stress == 0)
+ {
+ vowel_stress[stressed_syllable] = 4;
+ }
+ max_stress = 4;
+ }
+ break;
+
+ case 5:
+ // LANG=Russian
+ if(stressed_syllable == 0)
+ {
+ /* no explicit stress - guess the stress from the number of syllables */
+ static char guess_ru[16] = {0,0,1,1,2,3,3,4,5,6,7,7,8,9,10,11};
+ static char guess_ru_v[16] = {0,0,1,1,2,2,3,3,4,5,6,7,7,8,9,10}; // for final phoneme is a vowel
+ static char guess_ru_t[16] = {0,0,1,2,3,3,3,4,5,6,7,7,7,8,9,10}; // for final phoneme is an unvoiced stop
+
+ stressed_syllable = vowel_count - 3;
+ if(vowel_count < 16)
+ {
+ if(phoneme_tab[final_ph]->type == phVOWEL)
+ stressed_syllable = guess_ru_v[vowel_count];
+ else
+ if(phoneme_tab[final_ph]->type == phSTOP)
+ stressed_syllable = guess_ru_t[vowel_count];
+ else
+ stressed_syllable = guess_ru[vowel_count];
+ }
+ vowel_stress[stressed_syllable] = 4;
+ max_stress = 4;
+ }
+ break;
+
+ case 6: // LANG=hi stress on the last heaviest syllable
+ if(stressed_syllable == 0)
+ {
+ int wt;
+ int max_weight = -1;
+ int prev_stressed;
+
+ // find the heaviest syllable, excluding the final syllable
+ for(ix = 1; ix < (vowel_count-1); ix++)
+ {
+ if(vowel_stress[ix] == 0)
+ {
+ if((wt = syllable_weight[ix]) >= max_weight)
+ {
+ max_weight = wt;
+ prev_stressed = stressed_syllable;
+ stressed_syllable = ix;
+ }
+ }
+ }
+
+ if((syllable_weight[vowel_count-1] == 2) && (max_weight< 2))
+ {
+ // the only double=heavy syllable is the final syllable, so stress this
+ stressed_syllable = vowel_count-1;
+ }
+ else
+ if(max_weight <= 0)
+ {
+ // all syllables, exclusing the last, are light. Stress the first syllable
+ stressed_syllable = 1;
+ }
+
+ vowel_stress[stressed_syllable] = 4;
+ max_stress = 4;
+ }
+ break;
+
+ case 7: // LANG=tr, the last syllable for any vowel markes explicitly as unstressed
+ if(stressed_syllable == 0)
+ {
+ stressed_syllable = vowel_count - 1;
+ for(ix=1; ix < vowel_count; ix++)
+ {
+ if(vowel_stress[ix] == 1)
+ {
+ stressed_syllable = ix-1;
+ break;
+ }
+ }
+ vowel_stress[stressed_syllable] = 4;
+ max_stress = 4;
+ }
+ break;
+
+ case 9: // mark all as stressed
+ for(ix=1; ix<vowel_count; ix++)
+ {
+ if(vowel_stress[ix] == 0)
+ vowel_stress[ix] = 4;
+ }
+ break;
+ }
+
+ /* now guess the complete stress pattern */
+ if(max_stress < 4)
+ stress = 4; /* no primary stress marked, use for 1st syllable */
+ else
+ stress = 3;
+
+
+ if((stressflags & 0x1000) && (vowel_count == 2))
+ {
+ // Two syllable word, if one syllable has primary stress, then give the other secondary stress
+ if(vowel_stress[1] == 4)
+ vowel_stress[2] = 3;
+ if(vowel_stress[2] == 4)
+ vowel_stress[1] = 3;
+ }
+#if deleted
+ if((stressflags & 0x2000) && (vowel_stress[1] == 0))
+ {
+ // If there is only one syllable before the primary stress, give it a secondary stress
+ if((vowel_count > 2) && (vowel_stress[2] >= 4))
+ {
+ vowel_stress[1] = 3;
+ }
+ }
+#endif
+
+ done = 0;
+ for(v=1; v<vowel_count; v++)
+ {
+ if(vowel_stress[v] == 0)
+ {
+ if((stressflags & 0x10) && (stress < 4) && (v == vowel_count-1))
+ {
+ // flag: don't give secondary stress to final vowel
+ }
+ else
+ if((stressflags & 0x8000) && (done == 0))
+ {
+ vowel_stress[v] = (char)stress;
+ done =1;
+ stress = 3; /* use secondary stress for remaining syllables */
+ }
+ else
+ if((vowel_stress[v-1] <= 1) && (vowel_stress[v+1] <= 1))
+ {
+ /* trochaic: give stress to vowel surrounded by unstressed vowels */
+
+ if((stress == 3) && (stressflags & 0x20))
+ continue; // don't use secondary stress
+
+ if((v > 1) && (stressflags & 0x40) && (syllable_weight[v]==0) && (syllable_weight[v+1]>0))
+ {
+ // don't put secondary stress on a light syllable which is followed by a heavy syllable
+ continue;
+ }
+
+// should start with secondary stress on the first syllable, or should it count back from
+// the primary stress and put secondary stress on alternate syllables?
+ vowel_stress[v] = (char)stress;
+ done =1;
+ stress = 3; /* use secondary stress for remaining syllables */
+ }
+ }
+ }
+
+ if((unstressed_word) && (tonic < 0))
+ {
+ if(vowel_count <= 2)
+ tonic = tr->langopts.unstressed_wd1; /* monosyllable - unstressed */
+ else
+ tonic = tr->langopts.unstressed_wd2; /* more than one syllable, used secondary stress as the main stress */
+ }
+
+ max_stress = 0;
+ max_stress_posn = 0;
+ for(v=1; v<vowel_count; v++)
+ {
+ if(vowel_stress[v] >= max_stress)
+ {
+ max_stress = vowel_stress[v];
+ max_stress_posn = v;
+ }
+ }
+
+ if(tonic >= 0)
+ {
+ /* find position of highest stress, and replace it by 'tonic' */
+
+ /* don't disturb an explicitly set stress by 'unstress-at-end' flag */
+ if((tonic > max_stress) || (max_stress <= 4))
+ vowel_stress[max_stress_posn] = (char)tonic;
+ max_stress = tonic;
+ }
+
+
+ /* produce output phoneme string */
+ p = phonetic;
+ v = 1;
+
+ if((ph = phoneme_tab[*p]) != NULL)
+ {
+
+ if(ph->type == phSTRESS)
+ ph = phoneme_tab[p[1]];
+
+#ifdef deleted
+ int gap = tr->langopts.word_gap & 0x700;
+ if((gap) && (vowel_stress[1] >= 4) && (prev_stress >= 4))
+ {
+ /* two primary stresses together, insert a short pause */
+ *output++ = pause_phonemes[gap >> 8];
+ }
+ else
+#endif
+ if((tr->langopts.vowel_pause & 0x30) && (ph->type == phVOWEL))
+ {
+ // word starts with a vowel
+
+ if((tr->langopts.vowel_pause & 0x20) && (vowel_stress[1] >= 4))
+ {
+ *output++ = phonPAUSE_NOLINK; // not to be replaced by link
+ }
+ else
+ {
+ *output++ = phonPAUSE_VSHORT; // break, but no pause
+ }
+ }
+ }
+
+ p = phonetic;
+ post_tonic = 0;
+ while(((phcode = *p++) != 0) && (output < max_output))
+ {
+ if((ph = phoneme_tab[phcode]) == NULL)
+ continue;
+
+// if(ph->type == phSTRESS)
+// continue;
+
+ if(ph->type == phPAUSE)
+ {
+ tr->prev_last_stress = 0;
+ }
+ else
+ if(((ph->type == phVOWEL) && !(ph->phflags & phNONSYLLABIC)) || (*p == phonSYLLABIC))
+ {
+ // a vowel, or a consonant followed by a syllabic consonant marker
+
+ v_stress = vowel_stress[v];
+ tr->prev_last_stress = v_stress;
+
+ if(vowel_stress[v-1] >= max_stress)
+ post_tonic = 1;
+
+ if(v_stress <= 1)
+ {
+ if((v > 1) && (max_stress >= 4) && (stressflags & 4) && (v == (vowel_count-1)))
+ {
+ // option: mark unstressed final syllable as diminished
+ v_stress = 1;
+ }
+ else
+ if((stressflags & 2) || (v == 1) || (v == (vowel_count-1)))
+ {
+ // first or last syllable, or option 'don't set diminished stress'
+ v_stress = 0;
+ }
+ else
+ if((v == (vowel_count-2)) && (vowel_stress[vowel_count-1] <= 1))
+ {
+ // penultimate syllable, followed by an unstressed final syllable
+ v_stress = 0;
+ }
+ else
+ {
+ // unstressed syllable within a word
+ if((vowel_stress[v-1] != 1) || ((stressflags & 0x10000) == 0))
+ {
+ v_stress = 1; /* change from 0 (unstressed) to 1 (diminished stress) */
+ vowel_stress[v] = v_stress;
+ }
+ }
+ }
+
+ if(v_stress > 0)
+ *output++ = stress_phonemes[v_stress]; // mark stress of all vowels except 0 (unstressed)
+
+
+ if(vowel_stress[v] > max_stress)
+ {
+ max_stress = vowel_stress[v];
+ }
+
+ if((*p == phonLENGTHEN) && ((opt_length = tr->langopts.param[LOPT_IT_LENGTHEN]) != 0))
+ {
+ // remove lengthen indicator from non-stressed syllables
+ int shorten=0;
+
+ if(opt_length & 0x10)
+ {
+ // only allow lengthen indicator on the highest stress syllable in the word
+ if(v != max_stress_posn)
+ shorten = 1;
+ }
+ else
+ if(v_stress < 4)
+ {
+ // only allow lengthen indicator if stress >= 4.
+ shorten = 1;
+ }
+
+ if(((opt_length & 0xf)==2) && (v != (vowel_count - 2)))
+ shorten = 1; // LANG=Italian, remove lengthen indicator from non-penultimate syllables
+
+ if(shorten)
+ p++;
+ }
+
+ v++;
+ }
+
+ if(phcode != 1)
+ *output++ = phcode;
+ }
+ *output++ = 0;
+
+} /* end of SetWordStress */
+
+
+
+
+//=============================================================================================
+// Look up a word in the pronunciation rules
+//
+//=============================================================================================
+
+
+#ifdef LOG_TRANSLATE
+static char *DecodeRule(const char *group, char *rule)
+{//==================================================
+/* Convert compiled match template to ascii */
+
+ unsigned char rb;
+ unsigned char c;
+ char *p;
+ int ix;
+ int match_type;
+ int finished=0;
+ int value;
+ int linenum=0;
+ int flags;
+ int suffix_char;
+ int condition_num=0;
+ char buf[60];
+ char buf_pre[60];
+ char suffix[20];
+ static char output[60];
+
+ static char symbols[] = {' ',' ',' ',' ',' ',' ',' ',' ',' ',
+ '@','&','%','+','#','S','D','Z','A','L',' ',' ',' ',' ',' ','N','K','V',' ','T','X','?','W'};
+
+ static char symbols_lg[] = {'A','B','C','H','F','G','Y'};
+
+ match_type = 0;
+ buf_pre[0] = 0;
+ strcpy(buf,group);
+ p = &buf[strlen(buf)];
+ while(!finished)
+ {
+ rb = *rule++;
+
+ if(rb <= RULE_LINENUM)
+ {
+ switch(rb)
+ {
+ case 0:
+ case RULE_PHONEMES:
+ finished=1;
+ break;
+ case RULE_PRE:
+ match_type = RULE_PRE;
+ *p = 0;
+ p = buf_pre;
+ break;
+ case RULE_POST:
+ match_type = RULE_POST;
+ *p = 0;
+ strcat(buf," (");
+ p = &buf[strlen(buf)];
+ break;
+ case RULE_PH_COMMON:
+ break;
+ case RULE_CONDITION:
+ /* conditional rule, next byte gives condition number */
+ condition_num = *rule++;
+ break;
+ case RULE_LINENUM:
+ value = (rule[1] & 0xff) - 1;
+ linenum = (rule[0] & 0xff) - 1 + (value * 255);
+ rule+=2;
+ break;
+ }
+ continue;
+ }
+
+ if(rb == RULE_ENDING)
+ {
+ static const char *flag_chars = "ei vtfq t";
+ flags = ((rule[0] & 0x7f)<< 8) + (rule[1] & 0x7f);
+ suffix_char = 'S';
+ if(flags & (SUFX_P >> 8))
+ suffix_char = 'P';
+ sprintf(suffix,"%c%d",suffix_char,rule[2] & 0x7f);
+ rule += 3;
+ for(ix=0;ix<9;ix++)
+ {
+ if(flags & 1)
+ sprintf(&suffix[strlen(suffix)],"%c",flag_chars[ix]);
+ flags = (flags >> 1);
+ }
+ strcpy(p,suffix);
+ p += strlen(suffix);
+ c = ' ';
+ }
+ else
+ if(rb == RULE_LETTERGP)
+ {
+ c = symbols_lg[*rule++ - 'A'];
+ }
+ else
+ if(rb == RULE_LETTERGP2)
+ {
+ value = *rule++ - 'A';
+ p[0] = 'L';
+ p[1] = (value / 10) + '0';
+ c = (value % 10) + '0';
+
+ if(match_type == RULE_PRE)
+ {
+ p[0] = c;
+ c = 'L';
+ }
+ p+=2;
+ }
+ else
+ if(rb <= RULE_LAST_RULE)
+ c = symbols[rb];
+ else
+ if(rb == RULE_SPACE)
+ c = '_';
+ else
+ c = rb;
+ *p++ = c;
+ }
+ *p = 0;
+
+ p = output;
+ if(linenum > 0)
+ {
+ sprintf(p,"%5d:\t",linenum);
+ p += 7;
+ }
+ if(condition_num > 0)
+ {
+ sprintf(p,"?%d ",condition_num);
+ p = &p[strlen(p)];
+ }
+ if((ix = strlen(buf_pre)) > 0)
+ {
+ while(--ix >= 0)
+ *p++ = buf_pre[ix];
+ *p++ = ')';
+ *p++ = ' ';
+ }
+ *p = 0;
+ strcat(p,buf);
+ ix = strlen(output);
+ while(ix < 8)
+ output[ix++]=' ';
+ output[ix]=0;
+ return(output);
+} /* end of decode_match */
+#endif
+
+
+
+void AppendPhonemes(Translator *tr, char *string, int size, const char *ph)
+{//========================================================================
+/* Add new phoneme string "ph" to "string"
+ Keeps count of the number of vowel phonemes in the word, and whether these
+ can be stressed syllables. These values can be used in translation rules
+*/
+ const char *p;
+ unsigned char c;
+ int unstress_mark;
+ int length;
+
+ length = strlen(ph) + strlen(string);
+ if(length >= size)
+ {
+ return;
+ }
+
+ /* any stressable vowel ? */
+ unstress_mark = 0;
+ p = ph;
+ while((c = *p++) != 0)
+ {
+ if(c >= n_phoneme_tab) continue;
+
+ if(phoneme_tab[c]->type == phSTRESS)
+ {
+ if(phoneme_tab[c]->std_length < 4)
+ unstress_mark = 1;
+ }
+ else
+ {
+ if(phoneme_tab[c]->type == phVOWEL)
+ {
+ if(((phoneme_tab[c]->phflags & phUNSTRESSED) == 0) &&
+ (unstress_mark == 0))
+ {
+ tr->word_stressed_count++;
+ }
+ unstress_mark = 0;
+ tr->word_vowel_count++;
+ }
+ }
+ }
+
+ if(string != NULL)
+ strcat(string,ph);
+} /* end of AppendPhonemes */
+
+
+
+static void MatchRule(Translator *tr, char *word[], const char *group, char *rule, MatchRecord *match_out, int word_flags, int dict_flags)
+{//=======================================================================================================================================
+/* Checks a specified word against dictionary rules.
+ Returns with phoneme code string, or NULL if no match found.
+
+ word (indirect) points to current character group within the input word
+ This is advanced by this procedure as characters are consumed
+
+ group: the initial characters used to choose the rules group
+
+ rule: address of dictionary rule data for this character group
+
+ match_out: returns best points score
+
+ word_flags: indicates whether this is a retranslation after a suffix has been removed
+*/
+
+ unsigned char rb; // current instuction from rule
+ unsigned char letter; // current letter from input word, single byte
+ int letter_w; // current letter, wide character
+ int letter_xbytes; // number of extra bytes of multibyte character (num bytes - 1)
+ unsigned char last_letter;
+
+ char *pre_ptr;
+ char *post_ptr; /* pointer to first character after group */
+
+ char *rule_start; /* start of current match template */
+ char *p;
+
+ int match_type; /* left, right, or consume */
+ int failed;
+ int consumed; /* number of letters consumed from input */
+ int count; /* count through rules in the group */
+ int syllable_count;
+ int vowel;
+ int letter_group;
+ int distance_right;
+ int distance_left;
+ int lg_pts;
+ int n_bytes;
+ int add_points;
+
+ MatchRecord match;
+ static MatchRecord best;
+
+ int total_consumed; /* letters consumed for best match */
+ int group_length;
+
+ unsigned char condition_num;
+ char *common_phonemes; /* common to a group of entries */
+
+
+
+ if(rule == NULL)
+ {
+ match_out->points = 0;
+ (*word)++;
+ return;
+ }
+
+
+ total_consumed = 0;
+ count = 0;
+ common_phonemes = NULL;
+ match_type = 0;
+
+ best.points = 0;
+ best.phonemes = "";
+ best.end_type = 0;
+ best.del_fwd = NULL;
+
+ group_length = strlen(group);
+
+ /* search through dictionary rules */
+ while(rule[0] != RULE_GROUP_END)
+ {
+ match_type=0;
+ consumed = 0;
+ letter = 0;
+ distance_right= -6; /* used to reduce points for matches further away the current letter */
+ distance_left= -2;
+ count++;
+
+ match.points = 1;
+ match.end_type = 0;
+ match.del_fwd = NULL;
+
+ pre_ptr = *word;
+ post_ptr = *word + group_length;
+
+ /* work through next rule until end, or until no-match proved */
+ rule_start = rule;
+ failed = 0;
+ while(!failed)
+ {
+ rb = *rule++;
+
+ if(rb <= RULE_LINENUM)
+ {
+ switch(rb)
+ {
+ case 0: // no phoneme string for this rule, use previous common rule
+ if(common_phonemes != NULL)
+ {
+ match.phonemes = common_phonemes;
+ while(((rb = *match.phonemes++) != 0) && (rb != RULE_PHONEMES))
+ {
+ if(rb == RULE_CONDITION)
+ match.phonemes++; // skip over condition number
+ }
+ }
+ else
+ {
+ match.phonemes = "";
+ }
+ rule--; // so we are still pointing at the 0
+ failed=2; // matched OK
+ break;
+ case RULE_PRE:
+ match_type = RULE_PRE;
+ break;
+ case RULE_POST:
+ match_type = RULE_POST;
+ break;
+ case RULE_PHONEMES:
+ match.phonemes = rule;
+ failed=2; // matched OK
+ break;
+ case RULE_PH_COMMON:
+ common_phonemes = rule;
+ break;
+ case RULE_CONDITION:
+ /* conditional rule, next byte gives condition number */
+ condition_num = *rule++;
+
+ if(condition_num >= 32)
+ {
+ // allow the rule only if the condition number is NOT set
+ if((tr->dict_condition & (1L << (condition_num-32))) != 0)
+ failed = 1;
+ }
+ else
+ {
+ // allow the rule only if the condition number is set
+ if((tr->dict_condition & (1L << condition_num)) == 0)
+ failed = 1;
+ }
+
+ if(!failed)
+ match.points++; // add one point for a matched conditional rule
+ break;
+ case RULE_LINENUM:
+ rule+=2;
+ break;
+ }
+ continue;
+ }
+
+ add_points = 0;
+
+ switch(match_type)
+ {
+ case 0:
+ /* match and consume this letter */
+ last_letter = letter;
+ letter = *post_ptr++;
+
+ if((letter == rb) || ((letter==(unsigned char)REPLACED_E) && (rb=='e')))
+ {
+ add_points = 21;
+ consumed++;
+ }
+ else
+ failed = 1;
+ break;
+
+
+ case RULE_POST:
+ /* continue moving fowards */
+ distance_right += 6;
+ if(distance_right > 18)
+ distance_right = 19;
+ last_letter = letter;
+ letter_xbytes = utf8_in(&letter_w,post_ptr)-1;
+ letter = *post_ptr++;
+
+ switch(rb)
+ {
+ case RULE_LETTERGP:
+ letter_group = *rule++ - 'A';
+ if(IsLetter(tr, letter_w, letter_group))
+ {
+ lg_pts = 20;
+ if(letter_group==2)
+ lg_pts = 19; // fewer points for C, general consonant
+ add_points = (lg_pts-distance_right);
+ post_ptr += letter_xbytes;
+ }
+ else
+ failed = 1;
+ break;
+
+ case RULE_LETTERGP2: // match against a list of utf-8 strings
+ letter_group = *rule++ - 'A';
+ if((n_bytes = IsLetterGroup(tr, post_ptr-1,letter_group,0)) >0)
+ {
+ add_points = (20-distance_right);
+ post_ptr += (n_bytes-1);
+ }
+ else
+ failed =1;
+ break;
+
+ case RULE_NOTVOWEL:
+ if(!IsLetter(tr, letter_w,0))
+ {
+ add_points = (20-distance_right);
+ post_ptr += letter_xbytes;
+ }
+ else
+ failed = 1;
+ break;
+
+ case RULE_DIGIT:
+ if(IsDigit(letter_w))
+ {
+ add_points = (20-distance_right);
+ post_ptr += letter_xbytes;
+ }
+ else
+ if(tr->langopts.tone_numbers)
+ {
+ // also match if there is no digit
+ add_points = (20-distance_right);
+ post_ptr--;
+ }
+ else
+ failed = 1;
+ break;
+
+ case RULE_NONALPHA:
+ if(!iswalpha(letter_w))
+ {
+ add_points = (21-distance_right);
+ post_ptr += letter_xbytes;
+ }
+ else
+ failed = 1;
+ break;
+
+ case RULE_DOUBLE:
+ if(letter == last_letter)
+ add_points = (21-distance_right);
+ else
+ failed = 1;
+ break;
+
+ case RULE_ALT1:
+ if(dict_flags & FLAG_ALT_TRANS)
+ add_points = 1;
+ else
+ failed = 1;
+ break;
+
+ case '-':
+ if((letter == '-') || ((letter == ' ') && (word_flags & FLAG_HYPHEN_AFTER)))
+ {
+ add_points = (22-distance_right); // one point more than match against space
+ }
+ else
+ failed = 1;
+ break;
+
+ case RULE_SYLLABLE:
+ {
+ /* more than specified number of vowel letters to the right */
+ char *p = post_ptr + letter_xbytes;
+
+ syllable_count = 1;
+ while(*rule == RULE_SYLLABLE)
+ {
+ rule++;
+ syllable_count+=1; /* number of syllables to match */
+ }
+ vowel = 0;
+ while(letter_w != RULE_SPACE)
+ {
+ if((vowel==0) && IsLetter(tr, letter_w,LETTERGP_VOWEL2))
+ {
+ // this is counting vowels which are separated by non-vowels
+ syllable_count--;
+ }
+ vowel = IsLetter(tr, letter_w,LETTERGP_VOWEL2);
+ p += utf8_in(&letter_w,p);
+ }
+ if(syllable_count <= 0)
+ add_points = (19-distance_right);
+ else
+ failed = 1;
+ }
+ break;
+
+ case RULE_NOVOWELS:
+ {
+ char *p = post_ptr + letter_xbytes;
+ while(letter_w != RULE_SPACE)
+ {
+ if(IsLetter(tr, letter_w,LETTERGP_VOWEL2))
+ {
+ failed = 1;
+ break;
+ }
+ p += utf8_in(&letter_w,p);
+ }
+ if(!failed)
+ add_points = (19-distance_right);
+ }
+ break;
+
+ case RULE_INC_SCORE:
+ add_points = 20; // force an increase in points
+ break;
+
+ case RULE_DEL_FWD:
+ // find the next 'e' in the word and replace by ''
+ for(p = *word + group_length; *p != ' '; p++)
+ {
+ if(*p == 'e')
+ {
+ match.del_fwd = p;
+ break;
+ }
+ }
+ break;
+
+ case RULE_ENDING:
+ // next 3 bytes are a (non-zero) ending type. 2 bytes of flags + suffix length
+ match.end_type = (rule[0] << 16) + ((rule[1] & 0x7f) << 8) + (rule[2] & 0x7f);
+ rule += 3;
+ break;
+
+ case RULE_NO_SUFFIX:
+ if(word_flags & FLAG_SUFFIX_REMOVED)
+ failed = 1; // a suffix has been removed
+ else
+ add_points = 1;
+ break;
+
+ default:
+ if(letter == rb)
+ {
+ if(letter == RULE_SPACE)
+ add_points = (21-distance_right);
+ else
+ add_points = (21-distance_right);
+ }
+ else
+ failed = 1;
+ break;
+ }
+ break;
+
+
+ case RULE_PRE:
+ /* match backwards from start of current group */
+ distance_left += 2;
+ if(distance_left > 18)
+ distance_left = 19;
+
+ last_letter = *pre_ptr;
+ pre_ptr--;
+ letter_xbytes = utf8_in2(&letter_w,pre_ptr,1)-1;
+ letter = *pre_ptr;
+
+ switch(rb)
+ {
+ case RULE_LETTERGP:
+ letter_group = *rule++ - 'A';
+ if(IsLetter(tr, letter_w,letter_group))
+ {
+ lg_pts = 20;
+ if(letter_group==2)
+ lg_pts = 19; // fewer points for C, general consonant
+ add_points = (lg_pts-distance_left);
+ pre_ptr -= letter_xbytes;
+ }
+ else
+ failed = 1;
+ break;
+
+ case RULE_LETTERGP2: // match against a list of utf-8 strings
+ letter_group = *rule++ - 'A';
+ if((n_bytes = IsLetterGroup(tr, pre_ptr-letter_xbytes,letter_group,1)) >0)
+ {
+ add_points = (20-distance_right);
+ pre_ptr -= (n_bytes-1);
+ }
+ else
+ failed =1;
+ break;
+
+ case RULE_NOTVOWEL:
+ if(!IsLetter(tr, letter_w,0))
+ {
+ add_points = (20-distance_left);
+ pre_ptr -= letter_xbytes;
+ }
+ else
+ failed = 1;
+ break;
+
+ case RULE_DOUBLE:
+ if(letter == last_letter)
+ add_points = (21-distance_left);
+ else
+ failed = 1;
+ break;
+
+ case RULE_DIGIT:
+ if(IsDigit(letter_w))
+ {
+ add_points = (21-distance_left);
+ pre_ptr -= letter_xbytes;
+ }
+ else
+ failed = 1;
+ break;
+
+ case RULE_NONALPHA:
+ if(!iswalpha(letter_w))
+ {
+ add_points = (21-distance_right);
+ pre_ptr -= letter_xbytes;
+ }
+ else
+ failed = 1;
+ break;
+
+ case RULE_SYLLABLE:
+ /* more than specified number of vowels to the left */
+ syllable_count = 1;
+ while(*rule == RULE_SYLLABLE)
+ {
+ rule++;
+ syllable_count++; /* number of syllables to match */
+ }
+ if(syllable_count <= tr->word_vowel_count)
+ add_points = (19-distance_left);
+ else
+ failed = 1;
+ break;
+
+ case RULE_STRESSED:
+ if(tr->word_stressed_count > 0)
+ add_points = 19;
+ else
+ failed = 1;
+ break;
+
+ case RULE_NOVOWELS:
+ {
+ char *p = pre_ptr - letter_xbytes - 1;
+ while(letter_w != RULE_SPACE)
+ {
+ if(IsLetter(tr, letter_w,LETTERGP_VOWEL2))
+ {
+ failed = 1;
+ break;
+ }
+ p -= utf8_in2(&letter_w,p,1);
+ }
+ if(!failed)
+ add_points = 3;
+ }
+ break;
+
+ case RULE_IFVERB:
+ if(tr->expect_verb)
+ add_points = 1;
+ else
+ failed = 1;
+ break;
+
+ case RULE_CAPITAL:
+ if(word_flags & FLAG_FIRST_UPPER)
+ add_points = 1;
+ else
+ failed = 1;
+ break;
+
+ case '.':
+ // dot in pre- section, match on any dot before this point in the word
+ for(p=pre_ptr; *p != ' '; p--)
+ {
+ if(*p == '.')
+ {
+ add_points = 50;
+ break;
+ }
+ }
+ if(*p == ' ')
+ failed = 1;
+ break;
+
+ case '-':
+ if((letter == '-') || ((letter == ' ') && (word_flags & FLAG_HYPHEN)))
+ {
+ add_points = (22-distance_right); // one point more than match against space
+ }
+ else
+ failed = 1;
+ break;
+
+ default:
+ if(letter == rb)
+ {
+ if(letter == RULE_SPACE)
+ add_points = 4;
+ else
+ add_points = (21-distance_left);
+ }
+ else
+ failed = 1;
+ break;
+ }
+ break;
+ }
+
+ if(failed == 0)
+ match.points += add_points;
+ }
+
+ if(failed == 2)
+ {
+ /* matched OK, is this better than the last best match ? */
+ if(match.points >= best.points)
+ {
+ memcpy(&best,&match,sizeof(match));
+ total_consumed = consumed;
+ }
+
+#ifdef LOG_TRANSLATE
+ if((option_phonemes == 2) && (match.points > 0) && ((word_flags & FLAG_NO_TRACE) == 0))
+ {
+ // show each rule that matches, and it's points score
+ int pts;
+ char decoded_phonemes[80];
+
+ // note: 'count' contains the rule number, if we want to include it
+ pts = match.points;
+ if(group_length > 1)
+ pts += 35; // to account for an extra letter matching
+ DecodePhonemes(match.phonemes,decoded_phonemes);
+ fprintf(f_trans,"%3d\t%s [%s]\n",pts,DecodeRule(group,rule_start),decoded_phonemes);
+ }
+#endif
+
+ }
+
+ /* skip phoneme string to reach start of next template */
+ while(*rule++ != 0);
+ }
+
+#ifdef LOG_TRANSLATE
+ if((option_phonemes == 2) && ((word_flags & FLAG_NO_TRACE)==0))
+ {
+ if(group_length <= 1)
+ fprintf(f_trans,"\n");
+ }
+#endif
+
+ /* advance input data pointer */
+ total_consumed += group_length;
+ if(total_consumed == 0)
+ total_consumed = 1; /* always advance over 1st letter */
+
+ *word += total_consumed;
+
+ if(best.points == 0)
+ best.phonemes = "";
+ memcpy(match_out,&best,sizeof(MatchRecord));
+} /* end of MatchRule */
+
+
+
+
+int TranslateRules(Translator *tr, char *p_start, char *phonemes, int ph_size, char *end_phonemes, int word_flags, unsigned int *dict_flags)
+{//=====================================================================================================================================
+/* Translate a word bounded by space characters
+ Append the result to 'phonemes' and any standard prefix/suffix in 'end_phonemes' */
+
+ unsigned char c, c2;
+ unsigned int c12;
+ int wc=0;
+ int wc_prev;
+ int wc_bytes;
+ char *p2; /* copy of p for use in double letter chain match */
+ int found;
+ int g; /* group chain number */
+ int g1; /* first group for this letter */
+ int n;
+ int letter;
+ int any_alpha=0;
+ int ix;
+ unsigned int digit_count=0;
+ char *p;
+ int dict_flags0=0;
+ MatchRecord match1;
+ MatchRecord match2;
+ char ph_buf[40];
+ char word_copy[N_WORD_BYTES];
+ static const char str_pause[2] = {phonPAUSE_NOLINK,0};
+
+ char group_name[4];
+
+ if(tr->data_dictrules == NULL)
+ return(0);
+
+ if(dict_flags != NULL)
+ dict_flags0 = dict_flags[0];
+
+ for(ix=0; ix<(N_WORD_BYTES-1);)
+ {
+ c = p_start[ix];
+ word_copy[ix++] = c;
+ if(c == 0)
+ break;
+ }
+ word_copy[ix] = 0;
+
+
+#ifdef LOG_TRANSLATE
+ if((option_phonemes == 2) && ((word_flags & FLAG_NO_TRACE)==0))
+ {
+ char wordbuf[120];
+ int ix;
+
+ for(ix=0; ((c = p_start[ix]) != ' ') && (c != 0); ix++)
+ {
+ wordbuf[ix] = c;
+ }
+ wordbuf[ix] = 0;
+ fprintf(f_trans,"Translate '%s'\n",wordbuf);
+ }
+#endif
+
+ p = p_start;
+ tr->word_vowel_count = 0;
+ tr->word_stressed_count = 0;
+
+ if(end_phonemes != NULL)
+ end_phonemes[0] = 0;
+
+ while(((c = *p) != ' ') && (c != 0))
+ {
+ wc_prev = wc;
+ wc_bytes = utf8_in(&wc,p);
+ if(IsAlpha(wc))
+ any_alpha++;
+
+ n = tr->groups2_count[c];
+ if(IsDigit(wc) && ((tr->langopts.tone_numbers == 0) || !any_alpha))
+ {
+ // lookup the number in *_list not *_rules
+ char string[8];
+ char buf[40];
+ string[0] = '_';
+ memcpy(&string[1],p,wc_bytes);
+ string[1+wc_bytes] = 0;
+ Lookup(tr, string,buf);
+ if(++digit_count >= 2)
+ {
+ strcat(buf,str_pause);
+ digit_count=0;
+ }
+ AppendPhonemes(tr,phonemes,ph_size,buf);
+ p += wc_bytes;
+ continue;
+ }
+ else
+ {
+ digit_count = 0;
+ found = 0;
+
+ if(n > 0)
+ {
+ /* there are some 2 byte chains for this initial letter */
+ c2 = p[1];
+ c12 = c + (c2 << 8); /* 2 characters */
+
+ g1 = tr->groups2_start[c];
+ for(g=g1; g < (g1+n); g++)
+ {
+ if(tr->groups2_name[g] == c12)
+ {
+ found = 1;
+
+ group_name[0] = c;
+ group_name[1] = c2;
+ group_name[2] = 0;
+ p2 = p;
+ MatchRule(tr, &p2, group_name, tr->groups2[g], &match2, word_flags, dict_flags0);
+ if(match2.points > 0)
+ match2.points += 35; /* to acount for 2 letters matching */
+
+ /* now see whether single letter chain gives a better match ? */
+ group_name[1] = 0;
+ MatchRule(tr, &p, group_name, tr->groups1[c], &match1, word_flags, dict_flags0);
+
+ if(match2.points >= match1.points)
+ {
+ // use match from the 2-letter group
+ memcpy(&match1,&match2,sizeof(MatchRecord));
+ p = p2;
+ }
+ }
+ }
+ }
+
+ if(!found)
+ {
+ /* alphabetic, single letter chain */
+ group_name[0] = c;
+ group_name[1] = 0;
+
+ if(tr->groups1[c] != NULL)
+ MatchRule(tr, &p, group_name, tr->groups1[c], &match1, word_flags, dict_flags0);
+ else
+ {
+ // no group for this letter, use default group
+ MatchRule(tr, &p, "", tr->groups1[0], &match1, word_flags, dict_flags0);
+
+ if((match1.points == 0) && ((option_sayas & 0x10) == 0))
+ {
+ n = utf8_in(&letter,p-1)-1;
+
+ if(tr->letter_bits_offset > 0)
+ {
+ // not a Latin alphabet, switch to the default Latin alphabet language
+ if((letter <= 0x241) && iswalpha(letter))
+ {
+ sprintf(phonemes,"%c%s",phonSWITCH,tr->langopts.ascii_language);
+ return(0);
+ }
+ }
+#ifdef deleted
+// can't switch to a tone language, because the tone-phoneme numbers are not valid for the original language
+ if((letter >= 0x4e00) && (letter < 0xa000) && (tr->langopts.ideographs != 1))
+ {
+ // Chinese ideogram
+ sprintf(phonemes,"%czh",phonSWITCH);
+ return(0);
+ }
+#endif
+ // no match, try removing the accent and re-translating the word
+ if((letter >= 0xc0) && (letter <= 0x241) && ((ix = remove_accent[letter-0xc0]) != 0))
+ {
+ // within range of the remove_accent table
+ if((p[-2] != ' ') || (p[n] != ' '))
+ {
+ // not the only letter in the word
+ p2 = p-1;
+ p[-1] = ix;
+ while((p[0] = p[n]) != ' ') p++;
+ while(n-- > 0) *p++ = ' '; // replacement character must be no longer than original
+
+ if(tr->langopts.param[LOPT_DIERESES] && (lookupwchar(diereses_list,letter) > 0))
+ {
+ // vowel with dieresis, replace and continue from this point
+ p = p2;
+ continue;
+ }
+
+ phonemes[0] = 0; // delete any phonemes which have been produced so far
+ p = p_start;
+ tr->word_vowel_count = 0;
+ tr->word_stressed_count = 0;
+ continue; // start again at the beginning of the word
+ }
+ }
+ else
+ if((letter >= 0x3200) && (letter < 0xa700) && (end_phonemes != NULL))
+ {
+ // ideograms
+ // outside the range of the accent table, speak the unknown symbol sound
+ Lookup(tr, "_??", ph_buf);
+ match1.phonemes = ph_buf;
+ match1.points = 1;
+ p += (wc_bytes-1);
+ }
+ }
+ }
+
+ if(match1.points == 0)
+ {
+ if((wc >= 0x300) && (wc <= 0x36f))
+ {
+ // combining accent inside a word, ignore
+ }
+ else
+ if(IsAlpha(wc))
+ {
+ if((any_alpha > 1) || (p[wc_bytes-1] > ' '))
+ {
+ // an unrecognised character in a word, abort and then spell the word
+ phonemes[0] = 0;
+ if(dict_flags != NULL)
+ dict_flags[0] |= FLAG_SPELLWORD;
+ break;
+ }
+ }
+ else
+ {
+ LookupLetter(tr, wc, -1, ph_buf);
+ if(ph_buf[0])
+ {
+ match1.phonemes = ph_buf;
+ match1.points = 1;
+ }
+ }
+ p += (wc_bytes-1);
+ }
+ else
+ {
+ tr->phonemes_repeat_count = 0;
+ }
+ }
+ }
+
+ if(match1.phonemes == NULL)
+ match1.phonemes = "";
+
+ if(match1.points > 0)
+ {
+ if((match1.phonemes[0] == phonSWITCH) && ((word_flags & FLAG_DONT_SWITCH_TRANSLATOR)==0))
+ {
+ // an instruction to switch language, return immediately so we can re-translate
+ strcpy(phonemes,match1.phonemes);
+ return(0);
+ }
+
+ if((match1.end_type != 0) && (end_phonemes != NULL))
+ {
+ /* a standard ending has been found, re-translate the word without it */
+ if((match1.end_type & SUFX_P) && (word_flags & FLAG_NO_PREFIX))
+ {
+ // ignore the match on a prefix
+ }
+ else
+ {
+ if((match1.end_type & SUFX_P) && ((match1.end_type & 0x7f) == 0))
+ {
+ // no prefix length specified
+ match1.end_type |= p - p_start;
+ }
+ strcpy(end_phonemes,match1.phonemes);
+ memcpy(p_start,word_copy,strlen(word_copy));
+ return(match1.end_type);
+ }
+ }
+ if(match1.del_fwd != NULL)
+ *match1.del_fwd = REPLACED_E;
+ AppendPhonemes(tr,phonemes,ph_size,match1.phonemes);
+ }
+ }
+
+ // any language specific changes ?
+ ApplySpecialAttribute(tr,phonemes,dict_flags0);
+ memcpy(p_start,word_copy,strlen(word_copy));
+
+ return(0);
+} /* end of TranslateRules */
+
+
+
+void ApplySpecialAttribute(Translator *tr, char *phonemes, int dict_flags)
+{//=======================================================================
+// Amend the translated phonemes according to an attribute which is specific for the language.
+ int len;
+ int ix;
+ char *p_end;
+ int phoneme_1;
+
+ if((dict_flags & (FLAG_ALT_TRANS | FLAG_ALT2_TRANS)) == 0)
+ return;
+
+ len = strlen(phonemes);
+ p_end = &phonemes[len-1];
+
+ switch(tr->translator_name)
+ {
+ case L('d','e'):
+ if(p_end[0] == PhonemeCode2('i',':'))
+ {
+ // words ends in ['i:], change to [=I@]
+ p_end[-1] = phonSTRESS_PREV;
+ p_end[0] = PhonemeCode('I');
+ p_end[1] = phonSCHWA;
+ p_end[2] = 0;
+ }
+ break;
+
+ case L('p','t'):
+ phoneme_1 = PhonemeCode('o');
+ for(ix=0; ix<(len-1); ix++)
+ {
+ if(phonemes[ix] == phoneme_1)
+ {
+ phonemes[ix] = PhonemeCode('O');
+ break;
+ }
+ }
+ break;
+
+ case L('r','o'):
+ if(p_end[0] == PhonemeCode('j'))
+ {
+ // word end in [j], change to ['i]
+ p_end[0] = phonSTRESS_P;
+ p_end[1] = PhonemeCode('i');
+ p_end[2] = 0;
+ }
+ break;
+ }
+} // end of ApplySpecialAttribute
+
+
+
+//=============================================================================================
+// Look up a word in the pronunciation dictionary list
+// - exceptions which override the usual pronunciation rules, or which give a word
+// special properties, such as pronounce as unstressed
+//=============================================================================================
+
+// common letter pairs, encode these as a single byte
+static const short pairs_ru[] = {
+0x010c, // ла 21052 0x23
+0x010e, // на 18400
+0x0113, // та 14254
+0x0301, // ав 31083
+0x030f, // ов 13420
+0x060e, // не 21798
+0x0611, // ре 19458
+0x0903, // ви 16226
+0x0b01, // ак 14456
+0x0b0f, // ок 17836
+0x0c01, // ал 13324
+0x0c09, // ил 16877
+0x0e01, // ан 15359
+0x0e06, // ен 13543 0x30
+0x0e09, // ин 17168
+0x0e0e, // нн 15973
+0x0e0f, // он 22373
+0x0e1c, // ын 15052
+0x0f03, // во 24947
+0x0f11, // ро 13552
+0x0f12, // Ñо 16368
+0x100f, // оп 19054
+0x1011, // рп 17067
+0x1101, // ар 23967
+0x1106, // ер 18795
+0x1109, // ир 13797
+0x110f, // ор 21737
+0x1213, // Ñ‚Ñ 25076
+0x1220, // ÑÑ 14310
+0x7fff};
+//0x040f ог 12976
+//0x1306 ет 12826
+//0x0f0d мо 12688
+
+
+int TransposeAlphabet(char *text, int offset, int min, int max)
+{//============================================================
+// transpose cyrillic alphabet (for example) into ascii (single byte) character codes
+// return: number of bytes, bit 6: 1=used compression
+ int c;
+ int c2;
+ int ix;
+ char *p = text;
+ char *p2 = text;
+ int all_alpha=1;
+ int bits;
+ int acc;
+
+ do {
+ p += utf8_in(&c,p);
+ if((c >= min) && (c <= max))
+ {
+ *p2++ = c - offset;
+ }
+ else
+ if(c != 0)
+ {
+ p2 += utf8_out(c,p2);
+ all_alpha=0;
+ }
+ } while (c != 0);
+ *p2 = 0;
+
+ if(all_alpha)
+ {
+ // compress to 6 bits per character
+ acc=0;
+ bits=0;
+
+ p = text;
+ p2 = text;
+ while((c = *p++) != 0)
+ {
+ c2 = c + (*p << 8);
+ for(ix=0; c2 >= pairs_ru[ix]; ix++)
+ {
+ if(c2 == pairs_ru[ix])
+ {
+ // found an encoding for a 2-character pair
+ c = ix + 0x23; // 2-character codes start at 0x23
+ p++;
+ break;
+ }
+ }
+ acc = (acc << 6) + (c & 0x3f);
+ bits += 6;
+
+ if(bits >= 8)
+ {
+ bits -= 8;
+ *p2++ = (acc >> bits);
+ }
+ }
+ if(bits > 0)
+ {
+ *p2++ = (acc << (8-bits));
+ }
+ *p2 = 0;
+ return((p2 - text) | 0x40); // bit 6 indicates compressed characters
+ }
+ return(p2 - text);
+} // end of TransposeAlphabet
+
+
+
+
+static const char *LookupDict2(Translator *tr, const char *word, const char *word2,
+ char *phonetic, unsigned int *flags, int end_flags, WORD_TAB *wtab)
+//=====================================================================================
+/* Find an entry in the word_dict file for a specified word.
+ Returns NULL if no match, else returns 'word_end'
+
+ word zero terminated word to match
+ word2 pointer to next word(s) in the input text (terminated by space)
+
+ flags: returns dictionary flags which are associated with a matched word
+
+ end_flags: indicates whether this is a retranslation after removing a suffix
+*/
+{
+ char *p;
+ char *next;
+ int hash;
+ int phoneme_len;
+ int wlen;
+ unsigned char flag;
+ unsigned int dictionary_flags;
+ unsigned int dictionary_flags2;
+ int condition_failed=0;
+ int n_chars;
+ int no_phonemes;
+ int skipwords;
+ int ix;
+ const char *word_end;
+ const char *word1;
+ int wflags = 0;
+ char word_buf[N_WORD_BYTES];
+
+ if(wtab != NULL)
+ {
+ wflags = wtab->flags;
+ }
+
+ word1 = word;
+ if(tr->transpose_offset > 0)
+ {
+ strcpy(word_buf,word);
+ wlen = TransposeAlphabet(word_buf, tr->transpose_offset, tr->transpose_min, tr->transpose_max);
+ word = word_buf;
+ }
+ else
+ {
+ wlen = strlen(word);
+ }
+
+ hash = HashDictionary(word);
+ p = tr->dict_hashtab[hash];
+
+ if(p == NULL)
+ {
+ if(flags != NULL)
+ *flags = 0;
+ return(0);
+ }
+
+ // Find the first entry in the list for this hash value which matches.
+ // This corresponds to the last matching entry in the *_list file.
+
+ while(*p != 0)
+ {
+ next = p + p[0];
+
+ if(((p[1] & 0x7f) != wlen) || (memcmp(word,&p[2],wlen & 0x3f) != 0))
+ {
+ // bit 6 of wlen indicates whether the word has been compressed; so we need to match on this also.
+ p = next;
+ continue;
+ }
+
+ /* found matching entry. Decode the phonetic string */
+ word_end = word2;
+
+ dictionary_flags = 0;
+ dictionary_flags2 = 0;
+ no_phonemes = p[1] & 0x80;
+
+ p += ((p[1] & 0x3f) + 2);
+
+ if(no_phonemes)
+ {
+ phonetic[0] = 0;
+ phoneme_len = 0;
+ }
+ else
+ {
+ strcpy(phonetic,p);
+ phoneme_len = strlen(p);
+ p += (phoneme_len + 1);
+ }
+
+ while(p < next)
+ {
+ // examine the flags which follow the phoneme string
+
+ flag = *p++;
+ if(flag >= 100)
+ {
+ // conditional rule
+ if(flag >= 132)
+ {
+ // fail if this condition is set
+ if((tr->dict_condition & (1 << (flag-132))) != 0)
+ condition_failed = 1;
+ }
+ else
+ {
+ // allow only if this condition is set
+ if((tr->dict_condition & (1 << (flag-100))) == 0)
+ condition_failed = 1;
+ }
+ }
+ else
+ if(flag > 80)
+ {
+ // flags 81 to 90 match more than one word
+ // This comes after the other flags
+ n_chars = next - p;
+ skipwords = flag - 80;
+
+ // don't use the contraction if any of the words are emphasized
+ for(ix=0; ix <= skipwords; ix++)
+ {
+ if(wflags & FLAG_EMPHASIZED2)
+ {
+ condition_failed = 1;
+ }
+ }
+
+ if(memcmp(word2,p,n_chars) != 0)
+ condition_failed = 1;
+
+ if(condition_failed)
+ {
+ p = next;
+ break;
+ }
+
+ dictionary_flags |= FLAG_SKIPWORDS;
+ dictionary_skipwords = skipwords;
+ p = next;
+ word_end = word2 + n_chars;
+ }
+ else
+ if(flag > 64)
+ {
+ // stressed syllable information, put in bits 0-3
+ dictionary_flags = (dictionary_flags & ~0xf) | (flag & 0xf);
+ if((flag & 0xc) == 0xc)
+ dictionary_flags |= FLAG_STRESS_END;
+ }
+ else
+ if(flag >= 32)
+ {
+ dictionary_flags2 |= (1L << (flag-32));
+ }
+ else
+ {
+ dictionary_flags |= (1L << flag);
+ }
+ }
+
+ if(condition_failed)
+ {
+ condition_failed=0;
+ continue;
+ }
+
+ if((end_flags & FLAG_SUFX)==0)
+ {
+ // no suffix has been removed
+ if(dictionary_flags & FLAG_STEM)
+ continue; // this word must have a suffix
+ }
+
+ if((end_flags & SUFX_P) && (dictionary_flags & (FLAG_ONLY | FLAG_ONLY_S)))
+ continue; // $only or $onlys, don't match if a prefix has been removed
+
+ if(end_flags & FLAG_SUFX)
+ {
+ // a suffix was removed from the word
+ if(dictionary_flags & FLAG_ONLY)
+ continue; // no match if any suffix
+
+ if((dictionary_flags & FLAG_ONLY_S) && ((end_flags & FLAG_SUFX_S)==0))
+ {
+ // only a 's' suffix allowed, but the suffix wasn't 's'
+ continue;
+ }
+ }
+
+ if(dictionary_flags2 & FLAG_HYPHENATED)
+ {
+ if(!(wflags & FLAG_HYPHEN_AFTER))
+ {
+ continue;
+ }
+ }
+ if(dictionary_flags2 & FLAG_CAPITAL)
+ {
+ if(!(wflags & FLAG_FIRST_UPPER))
+ {
+ continue;
+ }
+ }
+ if(dictionary_flags2 & FLAG_ALLCAPS)
+ {
+ if(!(wflags & FLAG_ALL_UPPER))
+ {
+ continue;
+ }
+ }
+
+ if((dictionary_flags & FLAG_ATEND) && (word_end < tr->clause_end))
+ {
+ // only use this pronunciation if it's the last word of the clause
+ continue;
+ }
+
+ if(dictionary_flags2 & FLAG_VERB)
+ {
+ // this is a verb-form pronunciation
+
+ if(tr->expect_verb || (tr->expect_verb_s && (end_flags & FLAG_SUFX_S)))
+ {
+ // OK, we are expecting a verb
+ }
+ else
+ {
+ /* don't use the 'verb' pronunciation unless we are
+ expecting a verb */
+ continue;
+ }
+ }
+ if(dictionary_flags2 & FLAG_PAST)
+ {
+ if(!tr->expect_past)
+ {
+ /* don't use the 'past' pronunciation unless we are
+ expecting past tense */
+ continue;
+ }
+ }
+ if(dictionary_flags2 & FLAG_NOUN)
+ {
+ if(!tr->expect_noun)
+ {
+ /* don't use the 'noun' pronunciation unless we are
+ expecting a noun */
+ continue;
+ }
+ }
+
+ if(flags != NULL)
+ {
+ flags[0] = dictionary_flags | FLAG_FOUND_ATTRIBUTES;
+ flags[1] = dictionary_flags2;
+ }
+
+ if(phoneme_len == 0)
+ {
+ if(option_phonemes == 2)
+ {
+ fprintf(f_trans,"Flags: %s %s\n",word1,print_dictionary_flags(flags));
+ }
+ return(0); // no phoneme translation found here, only flags. So use rules
+ }
+
+ if(flags != NULL)
+ flags[0] |= FLAG_FOUND; // this flag indicates word was found in dictionary
+
+ if(option_phonemes == 2)
+ {
+ unsigned int flags1 = 0;
+ char ph_decoded[N_WORD_PHONEMES];
+ int textmode;
+
+ DecodePhonemes(phonetic,ph_decoded);
+ if(flags != NULL)
+ flags1 = flags[0];
+
+ if((dictionary_flags & FLAG_TEXTMODE) == 0)
+ textmode = 0;
+ else
+ textmode = 1;
+
+ if(textmode == translator->langopts.textmode)
+ {
+ // only show this line if the word translates to phonemes, not replacement text
+ fprintf(f_trans,"Found: %s [%s] %s\n",word1,ph_decoded,print_dictionary_flags(flags));
+ }
+ }
+ return(word_end);
+
+ }
+ return(0);
+} // end of LookupDict2
+
+
+
+int LookupDictList(Translator *tr, char **wordptr, char *ph_out, unsigned int *flags, int end_flags, WORD_TAB *wtab)
+//==================================================================================================================
+/* Lookup a specified word in the word dictionary.
+ Returns phonetic data in 'phonetic' and bits in 'flags'
+
+ end_flags: indicates if a suffix has been removed
+*/
+{
+ int length;
+ const char *found;
+ const char *word1;
+ const char *word2;
+ unsigned char c;
+ int nbytes;
+ int len;
+ char word[N_WORD_BYTES];
+ static char word_replacement[N_WORD_BYTES];
+
+ length = 0;
+ word2 = word1 = *wordptr;
+
+ while((word2[nbytes = utf8_nbytes(word2)]==' ') && (word2[nbytes+1]=='.'))
+ {
+ // look for an abbreviation of the form a.b.c
+ // try removing the spaces between the dots and looking for a match
+ memcpy(&word[length],word2,nbytes);
+ length += nbytes;
+ word[length++] = '.';
+ word2 += nbytes+3;
+ }
+ if(length > 0)
+ {
+ // found an abbreviation containing dots
+ nbytes = 0;
+ while(((c = word2[nbytes]) != 0) && (c != ' '))
+ {
+ nbytes++;
+ }
+ memcpy(&word[length],word2,nbytes);
+ word[length+nbytes] = 0;
+ found = LookupDict2(tr, word, word2, ph_out, flags, end_flags, wtab);
+ if(found)
+ {
+ // set the skip words flag
+ flags[0] |= FLAG_SKIPWORDS;
+ dictionary_skipwords = length;
+ return(1);
+ }
+ }
+
+ for(length=0; length<N_WORD_BYTES; length++)
+ {
+ if(((c = *word1++)==0) || (c == ' '))
+ break;
+ word[length] = c;
+ }
+ word[length] = 0;
+
+ found = LookupDict2(tr, word, word1, ph_out, flags, end_flags, wtab);
+
+ if(flags[0] & FLAG_MAX3)
+ {
+ if(strcmp(ph_out, tr->phonemes_repeat) == 0)
+ {
+ tr->phonemes_repeat_count++;
+ if(tr->phonemes_repeat_count > 3)
+ {
+ ph_out[0] = 0;
+ }
+ }
+ else
+ {
+ strncpy0(tr->phonemes_repeat, ph_out, sizeof(tr->phonemes_repeat));
+ tr->phonemes_repeat_count = 1;
+ }
+ }
+ else
+ {
+ tr->phonemes_repeat_count = 0;
+ }
+
+
+ if((found == 0) && (flags[1] & FLAG_ACCENT))
+ {
+ int letter;
+ word2 = word;
+ if(*word2 == '_') word2++;
+ len = utf8_in(&letter, word2);
+ LookupAccentedLetter(tr,letter, ph_out);
+ found = word2 + len;
+ }
+
+ if(found == 0)
+ {
+ ph_out[0] = 0;
+
+ // try modifications to find a recognised word
+
+ if((end_flags & FLAG_SUFX_E_ADDED) && (word[length-1] == 'e'))
+ {
+ // try removing an 'e' which has been added by RemoveEnding
+ word[length-1] = 0;
+ found = LookupDict2(tr, word, word1, ph_out, flags, end_flags, wtab);
+ }
+ else
+ if((end_flags & SUFX_D) && (word[length-1] == word[length-2]))
+ {
+ // try removing a double letter
+ word[length-1] = 0;
+ found = LookupDict2(tr, word, word1, ph_out, flags, end_flags, wtab);
+ }
+ }
+
+ if(found)
+ {
+ // if textmode is the default, then words which have phonemes are marked.
+ if(tr->langopts.textmode)
+ *flags ^= FLAG_TEXTMODE;
+
+ if(*flags & FLAG_TEXTMODE)
+ {
+ // the word translates to replacement text, not to phonemes
+
+ if(end_flags & FLAG_ALLOW_TEXTMODE)
+ {
+ // only use replacement text if this is the original word, not if a prefix or suffix has been removed
+ word_replacement[0] = 0;
+ word_replacement[1] = ' ';
+ sprintf(&word_replacement[2],"%s ",ph_out); // replacement word, preceded by zerochar and space
+
+ word1 = *wordptr;
+ *wordptr = &word_replacement[2];
+
+ if(option_phonemes == 2)
+ {
+ len = found - word1;
+ memcpy(word,word1,len); // include multiple matching words
+ word[len] = 0;
+ fprintf(f_trans,"Replace: %s %s\n",word,*wordptr);
+ }
+ }
+
+ ph_out[0] = 0;
+ return(0);
+ }
+
+ return(1);
+ }
+
+ ph_out[0] = 0;
+ return(0);
+} // end of LookupDictList
+
+
+
+int Lookup(Translator *tr, const char *word, char *ph_out)
+{//===================================================
+ unsigned int flags[2];
+ flags[0] = flags[1] = 0;
+ char *word1 = (char *)word;
+ return(LookupDictList(tr, &word1, ph_out, flags, 0, NULL));
+}
+
+
+
+int RemoveEnding(Translator *tr, char *word, int end_type, char *word_copy)
+{//========================================================================
+/* Removes a standard suffix from a word, once it has been indicated by the dictionary rules.
+ end_type: bits 0-6 number of letters
+ bits 8-14 suffix flags
+
+ word_copy: make a copy of the original word
+ This routine is language specific. In English it deals with reversing y->i and e-dropping
+ that were done when the suffix was added to the original word.
+*/
+
+ int i;
+ char *word_end;
+ int len_ending;
+ int end_flags;
+ const char *p;
+ int len;
+ static char ending[12];
+
+ // these lists are language specific, but are only relevent if the 'e' suffix flag is used
+ static const char *add_e_exceptions[] = {
+ "ion", NULL };
+
+ static const char *add_e_additions[] = {
+ "c", "rs", "ir", "ur", "ath", "ns", "lu", NULL };
+
+ for(word_end = word; *word_end != ' '; word_end++)
+ {
+ /* replace discarded 'e's */
+ if(*word_end == REPLACED_E)
+ *word_end = 'e';
+ }
+ i = word_end - word;
+ memcpy(word_copy,word,i);
+ word_copy[i] = 0;
+
+ // look for multibyte characters to increase the number of bytes to remove
+ for(len_ending = i = (end_type & 0x3f); i>0 ;i--) // num.of characters of the suffix
+ {
+ word_end--;
+ while((*word_end & 0xc0) == 0x80)
+ {
+ word_end--; // for multibyte characters
+ len_ending++;
+ }
+ }
+
+ // remove bytes from the end of the word and replace them by spaces
+ for(i=0; i<len_ending; i++)
+ {
+ ending[i] = word_end[i];
+ word_end[i] = ' ';
+ }
+ ending[i] = 0;
+ word_end--; /* now pointing at last character of stem */
+
+ end_flags = (end_type & 0xfff0) | FLAG_SUFX;
+
+ /* add an 'e' to the stem if appropriate,
+ if stem ends in vowel+consonant
+ or stem ends in 'c' (add 'e' to soften it) */
+
+ if(end_type & SUFX_I)
+ {
+ if(word_end[0] == 'i')
+ word_end[0] = 'y';
+ }
+
+ if(end_type & SUFX_E)
+ {
+ // add 'e' to end of stem
+ if(IsLetter(tr, word_end[-1],LETTERGP_VOWEL2) && IsLetter(tr, word_end[0],1))
+ {
+ // vowel(incl.'y') + hard.consonant
+
+ for(i=0; (p = add_e_exceptions[i]) != NULL; i++)
+ {
+ len = strlen(p);
+ if(memcmp(p,&word_end[1-len],len)==0)
+ {
+ break;
+ }
+ }
+ if(p == NULL)
+ end_flags |= FLAG_SUFX_E_ADDED; // no exception found
+ }
+ else
+ {
+ for(i=0; (p = add_e_additions[i]) != NULL; i++)
+ {
+ len = strlen(p);
+ if(memcmp(p,&word_end[1-len],len)==0)
+ {
+ end_flags |= FLAG_SUFX_E_ADDED;
+ break;
+ }
+ }
+ }
+
+ if(end_flags & FLAG_SUFX_E_ADDED)
+ {
+ word_end[1] = 'e';
+#ifdef LOG_TRANSLATE
+if(option_phonemes == 2)
+{
+ fprintf(f_trans,"add e\n");
+}
+#endif
+ }
+ }
+
+ if((end_type & SUFX_V) && (tr->expect_verb==0))
+ tr->expect_verb = 1; // this suffix indicates the verb pronunciation
+
+
+ if((strcmp(ending,"s")==0) || (strcmp(ending,"es")==0))
+ end_flags |= FLAG_SUFX_S;
+
+ if(strcmp(ending,"'s")==0)
+ end_flags &= ~FLAG_SUFX; // don't consider 's as an added suffix
+
+ return(end_flags);
+} /* end of RemoveEnding */
+
+
diff --git a/Plugins/eSpeak/eSpeak/espeak_command.h b/Plugins/eSpeak/eSpeak/espeak_command.h
new file mode 100644
index 0000000..c3bf35d
--- /dev/null
+++ b/Plugins/eSpeak/eSpeak/espeak_command.h
@@ -0,0 +1,129 @@
+#ifndef ESPEAK_COMMAND_H
+#define ESPEAK_COMMAND_H
+
+#ifndef PLATFORM_WINDOWS
+#include <unistd.h>
+#endif
+#include "speak_lib.h"
+
+enum t_espeak_type
+ {
+ ET_TEXT,
+ ET_MARK,
+ ET_KEY,
+ ET_CHAR,
+ ET_PARAMETER,
+ ET_PUNCTUATION_LIST,
+ ET_VOICE_NAME,
+ ET_VOICE_SPEC,
+ ET_TERMINATED_MSG
+ };
+
+typedef struct
+{
+ unsigned int unique_identifier;
+ void* text;
+ size_t size;
+ unsigned int position;
+ espeak_POSITION_TYPE position_type;
+ unsigned int end_position;
+ unsigned int flags;
+ void* user_data;
+} t_espeak_text;
+
+typedef struct
+{
+ unsigned int unique_identifier;
+ void* text;
+ size_t size;
+ const char* index_mark;
+ unsigned int end_position;
+ unsigned int flags;
+ void* user_data;
+} t_espeak_mark;
+
+typedef struct
+{
+ unsigned int unique_identifier;
+ void* user_data;
+} t_espeak_terminated_msg;
+
+typedef struct
+{
+ espeak_PARAMETER parameter;
+ int value;
+ int relative;
+} t_espeak_parameter;
+
+enum t_command_state
+{
+ CS_UNDEFINED, // The command has just been created
+ CS_PENDING, // stored in the fifo
+ CS_PROCESSED // processed
+};
+
+typedef struct
+{
+ enum t_espeak_type type;
+ t_command_state state;
+
+ union command
+ {
+ t_espeak_text my_text;
+ t_espeak_mark my_mark;
+ const char* my_key;
+ wchar_t my_char;
+ t_espeak_parameter my_param;
+ const wchar_t* my_punctuation_list;
+ const char *my_voice_name;
+ espeak_VOICE my_voice_spec;
+ t_espeak_terminated_msg my_terminated_msg;
+ } u;
+} t_espeak_command;
+
+
+t_espeak_command* create_espeak_text(const void *text, size_t size, unsigned int position, espeak_POSITION_TYPE position_type, unsigned int end_position, unsigned int flags, void* user_data);
+
+t_espeak_command* create_espeak_mark(const void *text, size_t size, const char *index_mark, unsigned int end_position, unsigned int flags, void* user_data);
+
+t_espeak_command* create_espeak_terminated_msg(unsigned int unique_identifier, void* user_data);
+
+t_espeak_command* create_espeak_key(const char *key_name);
+
+t_espeak_command* create_espeak_char(wchar_t character);
+
+t_espeak_command* create_espeak_parameter(espeak_PARAMETER parameter, int value, int relative);
+
+t_espeak_command* create_espeak_punctuation_list(const wchar_t *punctlist);
+
+t_espeak_command* create_espeak_voice_name(const char *name);
+
+t_espeak_command* create_espeak_voice_spec(espeak_VOICE *voice_spec);
+
+void process_espeak_command( t_espeak_command* the_command);
+
+int delete_espeak_command( t_espeak_command* the_command);
+
+void display_espeak_command(t_espeak_command* the_command);
+
+
+espeak_ERROR sync_espeak_Synth(unsigned int unique_identifier, const void *text, size_t size,
+ unsigned int position, espeak_POSITION_TYPE position_type,
+ unsigned int end_position, unsigned int flags, void* user_data);
+espeak_ERROR sync_espeak_Synth_Mark(unsigned int unique_identifier, const void *text, size_t size,
+ const char *index_mark, unsigned int end_position,
+ unsigned int flags, void* user_data);
+void sync_espeak_Key(const char *key);
+void sync_espeak_Char(wchar_t character);
+void sync_espeak_SetPunctuationList(const wchar_t *punctlist);
+void sync_espeak_SetParameter(espeak_PARAMETER parameter, int value, int relative);
+int sync_espeak_SetVoiceByName(const char *name);
+int sync_espeak_SetVoiceByProperties(espeak_VOICE *voice_selector);
+espeak_ERROR SetVoiceByName(const char *name);
+espeak_ERROR SetVoiceByProperties(espeak_VOICE *voice_selector);
+void SetParameter(int parameter, int value, int relative);
+
+int sync_espeak_terminated_msg(unsigned int unique_identifier, void* user_data);
+
+//>
+#endif
diff --git a/Plugins/eSpeak/eSpeak/event.h b/Plugins/eSpeak/eSpeak/event.h
new file mode 100644
index 0000000..c9ee482
--- /dev/null
+++ b/Plugins/eSpeak/eSpeak/event.h
@@ -0,0 +1,51 @@
+#ifndef EVENT_H
+#define EVENT_H
+
+/*
+Manage events (sentence, word, mark, end,...), is responsible of calling the external
+callback as soon as the relevant audio sample is played.
+
+
+The audio stream is composed of samples from synthetised messages or audio icons.
+Each event is associated to a sample.
+
+Scenario:
+
+- event_declare is called for each expected event.
+
+- A timeout is started for the first pending event.
+
+- When the timeout happens, the synth_callback is called.
+
+Note: the timeout is checked against the real progress of the audio stream, which depends on pauses or underruns. If the real progress is lower than the expected one, a new timeout starts.
+
+*/
+
+#include "speak_lib.h"
+
+// Initialize the event component.
+// First function to be called.
+// the callback will be called when the event actually occurs.
+// The callback is detailled in speak_lib.h .
+void event_init(void);
+void event_set_callback(t_espeak_callback* cb);
+
+// Clear any pending event.
+//
+// Return: EE_OK: operation achieved
+// EE_INTERNAL_ERROR.
+espeak_ERROR event_clear_all ();
+
+// Declare a future event
+//
+// Return: EE_OK: operation achieved
+// EE_BUFFER_FULL: the event can not be buffered;
+// you may try after a while to call the function again.
+// EE_INTERNAL_ERROR.
+espeak_ERROR event_declare (espeak_EVENT* event);
+
+// Terminate the event component.
+// Last function to be called.
+void event_terminate();
+
+#endif
diff --git a/Plugins/eSpeak/eSpeak/fifo.h b/Plugins/eSpeak/eSpeak/fifo.h
new file mode 100644
index 0000000..b3699ff
--- /dev/null
+++ b/Plugins/eSpeak/eSpeak/fifo.h
@@ -0,0 +1,58 @@
+#ifndef FIFO_H
+#define FIFO_H
+
+// Helps to add espeak commands in a first-in first-out queue
+// and run them asynchronously.
+
+#include "espeak_command.h"
+#include "speak_lib.h"
+
+// Initialize the fifo component.
+// First function to be called.
+void fifo_init();
+
+// Add an espeak command.
+//
+// Note: this function fails if too many commands are already buffered.
+// In such a case, the calling function could wait and then add again its command.
+//
+// Return: EE_OK: operation achieved
+// EE_BUFFER_FULL: the command can not be buffered;
+// you may try after a while to call the function again.
+// EE_INTERNAL_ERROR.
+espeak_ERROR fifo_add_command (t_espeak_command* c);
+
+// Add two espeak commands in a single transaction.
+//
+// Note: this function fails if too many commands are already buffered.
+// In such a case, the calling function could wait and then add again these commands.
+//
+// Return: EE_OK: operation achieved
+// EE_BUFFER_FULL: at least one command can not be buffered;
+// you may try after a while to call the function again.
+// EE_INTERNAL_ERROR.
+espeak_ERROR fifo_add_commands (t_espeak_command* c1, t_espeak_command* c2);
+
+// The current running command must be stopped and the awaiting commands are cleared.
+// Return: EE_OK: operation achieved
+// EE_INTERNAL_ERROR.
+espeak_ERROR fifo_stop ();
+
+// Is there a running command?
+// Returns 1 if yes; 0 otherwise.
+int fifo_is_busy ();
+
+// Terminate the fifo component.
+// Last function to be called.
+void fifo_terminate();
+
+// Indicates if the running command is still enabled.
+//
+// Note: this function is mainly called by the SynthCallback (speak_lib.cpp)
+// It indicates if the actual wave sample can still be played. It is helpful for
+// stopping speech as soon as a cancel command is applied.
+//
+// Returns 1 if yes, or 0 otherwise.
+int fifo_is_command_enabled();
+
+#endif
diff --git a/Plugins/eSpeak/eSpeak/intonation.cpp b/Plugins/eSpeak/eSpeak/intonation.cpp
new file mode 100644
index 0000000..6cc4f91
--- /dev/null
+++ b/Plugins/eSpeak/eSpeak/intonation.cpp
@@ -0,0 +1,1104 @@
+/***************************************************************************
+ * Copyright (C) 2005 to 2007 by Jonathan Duddington *
+ * email: jonsd@users.sourceforge.net *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 3 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write see: *
+ * <http://www.gnu.org/licenses/>. *
+ ***************************************************************************/
+
+#include "StdAfx.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <wctype.h>
+
+#include "speak_lib.h"
+#include "speech.h"
+#include "phoneme.h"
+#include "synthesize.h"
+#include "voice.h"
+#include "translate.h"
+
+
+/* Note this module is mostly old code that needs to be rewritten to
+ provide a more flexible intonation system.
+*/
+
+// bits in SYLLABLE.flags
+#define SYL_RISE 1
+#define SYL_EMPHASIS 2
+#define SYL_END_CLAUSE 4
+
+typedef struct {
+ char stress;
+ char env;
+ char flags; //bit 0=pitch rising, bit1=emnphasized, bit2=end of clause
+ char nextph_type;
+ short pitch1;
+ short pitch2;
+} SYLLABLE;
+
+static SYLLABLE *syllable_tab;
+
+
+static int tone_pitch_env; /* used to return pitch envelope */
+
+
+
+/* Pitch data for tone types */
+/*****************************/
+
+
+#define PITCHfall 0
+#define PITCHrise 1
+#define PITCHfrise 2 // and 3 must be for the varient preceded by 'r'
+#define PITCHfrise2 4 // and 5 must be the 'r' variant
+#define PITCHrisefall 6
+
+/* 0 fall */
+unsigned char env_fall[128] = {
+ 0xff, 0xfd, 0xfa, 0xf8, 0xf6, 0xf4, 0xf2, 0xf0, 0xee, 0xec, 0xea, 0xe8, 0xe6, 0xe4, 0xe2, 0xe0,
+ 0xde, 0xdc, 0xda, 0xd8, 0xd6, 0xd4, 0xd2, 0xd0, 0xce, 0xcc, 0xca, 0xc8, 0xc6, 0xc4, 0xc2, 0xc0,
+ 0xbe, 0xbc, 0xba, 0xb8, 0xb6, 0xb4, 0xb2, 0xb0, 0xae, 0xac, 0xaa, 0xa8, 0xa6, 0xa4, 0xa2, 0xa0,
+ 0x9e, 0x9c, 0x9a, 0x98, 0x96, 0x94, 0x92, 0x90, 0x8e, 0x8c, 0x8a, 0x88, 0x86, 0x84, 0x82, 0x80,
+ 0x7e, 0x7c, 0x7a, 0x78, 0x76, 0x74, 0x72, 0x70, 0x6e, 0x6c, 0x6a, 0x68, 0x66, 0x64, 0x62, 0x60,
+ 0x5e, 0x5c, 0x5a, 0x58, 0x56, 0x54, 0x52, 0x50, 0x4e, 0x4c, 0x4a, 0x48, 0x46, 0x44, 0x42, 0x40,
+ 0x3e, 0x3c, 0x3a, 0x38, 0x36, 0x34, 0x32, 0x30, 0x2e, 0x2c, 0x2a, 0x28, 0x26, 0x24, 0x22, 0x20,
+ 0x1e, 0x1c, 0x1a, 0x18, 0x16, 0x14, 0x12, 0x10, 0x0e, 0x0c, 0x0a, 0x08, 0x06, 0x04, 0x02, 0x00 };
+
+/* 1 rise */
+unsigned char env_rise[128] = {
+ 0x00, 0x02, 0x04, 0x06, 0x08, 0x0a, 0x0c, 0x0e, 0x10, 0x12, 0x14, 0x16, 0x18, 0x1a, 0x1c, 0x1e,
+ 0x20, 0x22, 0x24, 0x26, 0x28, 0x2a, 0x2c, 0x2e, 0x30, 0x32, 0x34, 0x36, 0x38, 0x3a, 0x3c, 0x3e,
+ 0x40, 0x42, 0x44, 0x46, 0x48, 0x4a, 0x4c, 0x4e, 0x50, 0x52, 0x54, 0x56, 0x58, 0x5a, 0x5c, 0x5e,
+ 0x60, 0x62, 0x64, 0x66, 0x68, 0x6a, 0x6c, 0x6e, 0x70, 0x72, 0x74, 0x76, 0x78, 0x7a, 0x7c, 0x7e,
+ 0x80, 0x82, 0x84, 0x86, 0x88, 0x8a, 0x8c, 0x8e, 0x90, 0x92, 0x94, 0x96, 0x98, 0x9a, 0x9c, 0x9e,
+ 0xa0, 0xa2, 0xa4, 0xa6, 0xa8, 0xaa, 0xac, 0xae, 0xb0, 0xb2, 0xb4, 0xb6, 0xb8, 0xba, 0xbc, 0xbe,
+ 0xc0, 0xc2, 0xc4, 0xc6, 0xc8, 0xca, 0xcc, 0xce, 0xd0, 0xd2, 0xd4, 0xd6, 0xd8, 0xda, 0xdc, 0xde,
+ 0xe0, 0xe2, 0xe4, 0xe6, 0xe8, 0xea, 0xec, 0xee, 0xf0, 0xf2, 0xf4, 0xf6, 0xf8, 0xfa, 0xfd, 0xff };
+
+unsigned char env_frise[128] = {
+ 0xff, 0xf4, 0xea, 0xe0, 0xd6, 0xcc, 0xc3, 0xba, 0xb1, 0xa8, 0x9f, 0x97, 0x8f, 0x87, 0x7f, 0x78,
+ 0x71, 0x6a, 0x63, 0x5c, 0x56, 0x50, 0x4a, 0x44, 0x3f, 0x39, 0x34, 0x2f, 0x2b, 0x26, 0x22, 0x1e,
+ 0x1a, 0x17, 0x13, 0x10, 0x0d, 0x0b, 0x08, 0x06, 0x04, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x07, 0x08, 0x0a, 0x0c, 0x0e, 0x10, 0x13, 0x15, 0x17,
+ 0x1a, 0x1d, 0x1f, 0x22, 0x25, 0x28, 0x2c, 0x2e, 0x30, 0x32, 0x34, 0x36, 0x39, 0x3b, 0x3d, 0x40,
+ 0x42, 0x45, 0x47, 0x4a, 0x4c, 0x4f, 0x51, 0x54, 0x57, 0x5a, 0x5d, 0x5f, 0x62, 0x65, 0x68, 0x6b,
+ 0x6e, 0x71, 0x74, 0x78, 0x7b, 0x7e, 0x81, 0x85, 0x88, 0x8b, 0x8f, 0x92, 0x96, 0x99, 0x9d, 0xa0,
+ 0xa4, 0xa8, 0xac, 0xaf, 0xb3, 0xb7, 0xbb, 0xbf, 0xc3, 0xc7, 0xcb, 0xcf, 0xd3, 0xd7, 0xdb, 0xe0 };
+
+static unsigned char env_r_frise[128] = {
+ 0xcf, 0xcc, 0xc9, 0xc6, 0xc3, 0xc0, 0xbd, 0xb9, 0xb4, 0xb0, 0xab, 0xa7, 0xa2, 0x9c, 0x97, 0x92,
+ 0x8c, 0x86, 0x81, 0x7b, 0x75, 0x6f, 0x69, 0x63, 0x5d, 0x57, 0x50, 0x4a, 0x44, 0x3e, 0x38, 0x33,
+ 0x2d, 0x27, 0x22, 0x1c, 0x17, 0x12, 0x0d, 0x08, 0x04, 0x02, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x01, 0x01, 0x02, 0x03, 0x04, 0x05, 0x07, 0x08, 0x0a, 0x0c, 0x0d, 0x0f, 0x12, 0x14, 0x16,
+ 0x19, 0x1b, 0x1e, 0x21, 0x24, 0x27, 0x2a, 0x2d, 0x30, 0x34, 0x36, 0x38, 0x3a, 0x3c, 0x3f, 0x41,
+ 0x43, 0x46, 0x48, 0x4b, 0x4d, 0x50, 0x52, 0x55, 0x58, 0x5a, 0x5d, 0x60, 0x63, 0x66, 0x69, 0x6c,
+ 0x6f, 0x72, 0x75, 0x78, 0x7b, 0x7e, 0x81, 0x85, 0x88, 0x8b, 0x8f, 0x92, 0x96, 0x99, 0x9d, 0xa0,
+ 0xa4, 0xa8, 0xac, 0xaf, 0xb3, 0xb7, 0xbb, 0xbf, 0xc3, 0xc7, 0xcb, 0xcf, 0xd3, 0xd7, 0xdb, 0xe0 };
+
+static unsigned char env_frise2[128] = {
+ 0xff, 0xf9, 0xf4, 0xee, 0xe9, 0xe4, 0xdf, 0xda, 0xd5, 0xd0, 0xcb, 0xc6, 0xc1, 0xbd, 0xb8, 0xb3,
+ 0xaf, 0xaa, 0xa6, 0xa1, 0x9d, 0x99, 0x95, 0x90, 0x8c, 0x88, 0x84, 0x80, 0x7d, 0x79, 0x75, 0x71,
+ 0x6e, 0x6a, 0x67, 0x63, 0x60, 0x5d, 0x59, 0x56, 0x53, 0x50, 0x4d, 0x4a, 0x47, 0x44, 0x41, 0x3e,
+ 0x3c, 0x39, 0x37, 0x34, 0x32, 0x2f, 0x2d, 0x2b, 0x28, 0x26, 0x24, 0x22, 0x20, 0x1e, 0x1c, 0x1a,
+ 0x19, 0x17, 0x15, 0x14, 0x12, 0x11, 0x0f, 0x0e, 0x0d, 0x0c, 0x0a, 0x09, 0x08, 0x07, 0x06, 0x05,
+ 0x05, 0x04, 0x03, 0x02, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x03, 0x04, 0x04, 0x05, 0x06, 0x07, 0x08,
+ 0x09, 0x0a, 0x0b, 0x0c, 0x0e, 0x0f, 0x10, 0x12, 0x13, 0x15, 0x17, 0x18, 0x1a, 0x1c, 0x1e, 0x20 };
+
+static unsigned char env_r_frise2[128] = {
+ 0xd0, 0xce, 0xcd, 0xcc, 0xca, 0xc8, 0xc7, 0xc5, 0xc3, 0xc1, 0xc0, 0xbd, 0xbb, 0xb8, 0xb5, 0xb3,
+ 0xb0, 0xad, 0xaa, 0xa7, 0xa3, 0xa0, 0x9d, 0x99, 0x96, 0x92, 0x8f, 0x8b, 0x87, 0x84, 0x80, 0x7c,
+ 0x78, 0x74, 0x70, 0x6d, 0x69, 0x65, 0x61, 0x5d, 0x59, 0x55, 0x51, 0x4d, 0x4a, 0x46, 0x42, 0x3e,
+ 0x3b, 0x37, 0x34, 0x31, 0x2f, 0x2d, 0x2a, 0x28, 0x26, 0x24, 0x22, 0x20, 0x1e, 0x1c, 0x1a, 0x19,
+ 0x17, 0x15, 0x14, 0x12, 0x11, 0x0f, 0x0e, 0x0d, 0x0c, 0x0a, 0x09, 0x08, 0x07, 0x06, 0x05, 0x05,
+ 0x04, 0x03, 0x02, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x03, 0x04, 0x04, 0x05, 0x06, 0x07, 0x08,
+ 0x09, 0x0a, 0x0b, 0x0c, 0x0e, 0x0f, 0x10, 0x12, 0x13, 0x15, 0x17, 0x18, 0x1a, 0x1c, 0x1e, 0x20 };
+
+static unsigned char env_risefall[128] = {
+ 0x98, 0x99, 0x99, 0x9a, 0x9c, 0x9d, 0x9f, 0xa1, 0xa4, 0xa7, 0xa9, 0xac, 0xb0, 0xb3, 0xb6, 0xba,
+ 0xbe, 0xc1, 0xc5, 0xc9, 0xcd, 0xd1, 0xd4, 0xd8, 0xdc, 0xdf, 0xe3, 0xe6, 0xea, 0xed, 0xf0, 0xf2,
+ 0xf5, 0xf7, 0xf9, 0xfb, 0xfc, 0xfd, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfd,
+ 0xfb, 0xfa, 0xf8, 0xf6, 0xf3, 0xf1, 0xee, 0xec, 0xe9, 0xe6, 0xe4, 0xe0, 0xdd, 0xda, 0xd7, 0xd3,
+ 0xd0, 0xcc, 0xc8, 0xc4, 0xc0, 0xbc, 0xb8, 0xb4, 0xb0, 0xac, 0xa7, 0xa3, 0x9f, 0x9a, 0x96, 0x91,
+ 0x8d, 0x88, 0x84, 0x7f, 0x7b, 0x76, 0x72, 0x6d, 0x69, 0x65, 0x60, 0x5c, 0x58, 0x54, 0x50, 0x4c,
+ 0x48, 0x44, 0x40, 0x3c, 0x39, 0x35, 0x32, 0x2f, 0x2b, 0x28, 0x26, 0x23, 0x20, 0x1d, 0x1a, 0x17,
+ 0x15, 0x12, 0x0f, 0x0d, 0x0a, 0x08, 0x07, 0x05, 0x03, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+static unsigned char env_rise2[128] = {
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x03, 0x03, 0x04, 0x04, 0x05, 0x06, 0x06,
+ 0x07, 0x08, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14,
+ 0x16, 0x17, 0x18, 0x19, 0x1b, 0x1c, 0x1d, 0x1f, 0x20, 0x22, 0x23, 0x25, 0x26, 0x28, 0x29, 0x2b,
+ 0x2d, 0x2f, 0x30, 0x32, 0x34, 0x36, 0x38, 0x3a, 0x3c, 0x3e, 0x40, 0x42, 0x44, 0x47, 0x49, 0x4b,
+ 0x4e, 0x50, 0x52, 0x55, 0x57, 0x5a, 0x5d, 0x5f, 0x62, 0x65, 0x67, 0x6a, 0x6d, 0x70, 0x73, 0x76,
+ 0x79, 0x7c, 0x7f, 0x82, 0x86, 0x89, 0x8c, 0x90, 0x93, 0x96, 0x9a, 0x9d, 0xa0, 0xa3, 0xa6, 0xa9,
+ 0xac, 0xaf, 0xb2, 0xb5, 0xb8, 0xbb, 0xbe, 0xc1, 0xc4, 0xc7, 0xca, 0xcd, 0xd0, 0xd3, 0xd6, 0xd9,
+ 0xdc, 0xdf, 0xe2, 0xe4, 0xe7, 0xe9, 0xec, 0xee, 0xf0, 0xf2, 0xf4, 0xf6, 0xf8, 0xfa, 0xfb, 0xfd };
+
+static unsigned char env_fall2[128] = {
+ 0xfe, 0xfe, 0xfd, 0xfd, 0xfc, 0xfb, 0xfb, 0xfa, 0xfa, 0xf9, 0xf8, 0xf8, 0xf7, 0xf7, 0xf6, 0xf6,
+ 0xf5, 0xf4, 0xf4, 0xf3, 0xf3, 0xf2, 0xf2, 0xf1, 0xf0, 0xf0, 0xef, 0xee, 0xee, 0xed, 0xec, 0xeb,
+ 0xea, 0xea, 0xe9, 0xe8, 0xe7, 0xe6, 0xe5, 0xe4, 0xe3, 0xe2, 0xe1, 0xe0, 0xde, 0xdd, 0xdc, 0xdb,
+ 0xd9, 0xd8, 0xd6, 0xd5, 0xd3, 0xd2, 0xd0, 0xce, 0xcc, 0xcb, 0xc9, 0xc7, 0xc5, 0xc3, 0xc0, 0xbe,
+ 0xbc, 0xb9, 0xb7, 0xb5, 0xb2, 0xaf, 0xad, 0xaa, 0xa7, 0xa4, 0xa1, 0x9e, 0x9a, 0x97, 0x94, 0x90,
+ 0x8d, 0x89, 0x85, 0x81, 0x7d, 0x79, 0x75, 0x71, 0x6d, 0x68, 0x64, 0x61, 0x5e, 0x5b, 0x57, 0x54,
+ 0x51, 0x4d, 0x4a, 0x46, 0x43, 0x40, 0x3c, 0x39, 0x35, 0x32, 0x2e, 0x2a, 0x27, 0x23, 0x1f, 0x1c,
+ 0x18, 0x14, 0x11, 0x0d, 0x0b, 0x09, 0x07, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00 };
+
+static unsigned char env_fallrise3[128] = {
+ 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfd, 0xfc, 0xfa, 0xf8, 0xf6, 0xf4, 0xf1, 0xee, 0xeb,
+ 0xe8, 0xe5, 0xe1, 0xde, 0xda, 0xd6, 0xd2, 0xcd, 0xc9, 0xc4, 0xbf, 0xba, 0xb6, 0xb0, 0xab, 0xa6,
+ 0xa1, 0x9c, 0x96, 0x91, 0x8b, 0x86, 0x80, 0x7b, 0x75, 0x6f, 0x6a, 0x64, 0x5f, 0x59, 0x54, 0x4f,
+ 0x49, 0x44, 0x3f, 0x3a, 0x35, 0x30, 0x2b, 0x26, 0x22, 0x1d, 0x19, 0x15, 0x11, 0x0d, 0x0a, 0x07,
+ 0x04, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, 0x04, 0x05,
+ 0x07, 0x09, 0x0b, 0x0d, 0x10, 0x12, 0x15, 0x18, 0x1b, 0x1e, 0x22, 0x25, 0x29, 0x2d, 0x31, 0x35,
+ 0x3a, 0x3e, 0x43, 0x48, 0x4c, 0x51, 0x57, 0x5b, 0x5e, 0x62, 0x65, 0x68, 0x6b, 0x6e, 0x71, 0x74,
+ 0x76, 0x78, 0x7b, 0x7c, 0x7e, 0x80, 0x81, 0x82, 0x83, 0x83, 0x84, 0x84, 0x83, 0x83, 0x82, 0x81 };
+
+static unsigned char env_fallrise4[128] = {
+ 0x72, 0x72, 0x71, 0x71, 0x70, 0x6f, 0x6d, 0x6c, 0x6a, 0x68, 0x66, 0x64, 0x61, 0x5f, 0x5c, 0x5a,
+ 0x57, 0x54, 0x51, 0x4e, 0x4b, 0x48, 0x45, 0x42, 0x3f, 0x3b, 0x38, 0x35, 0x32, 0x2f, 0x2c, 0x29,
+ 0x26, 0x23, 0x20, 0x1d, 0x1b, 0x18, 0x16, 0x14, 0x12, 0x10, 0x0e, 0x0c, 0x0b, 0x0a, 0x09, 0x08,
+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x06, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x06,
+ 0x07, 0x07, 0x08, 0x09, 0x0a, 0x0c, 0x0d, 0x0f, 0x10, 0x12, 0x14, 0x16, 0x18, 0x1b, 0x1d, 0x20,
+ 0x23, 0x26, 0x29, 0x2c, 0x2f, 0x33, 0x37, 0x3b, 0x3f, 0x43, 0x47, 0x4c, 0x51, 0x56, 0x5b, 0x60,
+ 0x65, 0x6a, 0x6f, 0x74, 0x79, 0x7f, 0x84, 0x89, 0x8f, 0x95, 0x9b, 0xa1, 0xa7, 0xad, 0xb3, 0xba,
+ 0xc0, 0xc7, 0xce, 0xd5, 0xdc, 0xe3, 0xea, 0xf1, 0xf5, 0xf7, 0xfa, 0xfc, 0xfd, 0xfe, 0xff, 0xff };
+
+static unsigned char env_risefallrise[128] = {
+ 0x7f, 0x7f, 0x7f, 0x80, 0x81, 0x83, 0x84, 0x87, 0x89, 0x8c, 0x8f, 0x92, 0x96, 0x99, 0x9d, 0xa1,
+ 0xa5, 0xaa, 0xae, 0xb2, 0xb7, 0xbb, 0xc0, 0xc5, 0xc9, 0xcd, 0xd2, 0xd6, 0xda, 0xde, 0xe2, 0xe6,
+ 0xea, 0xed, 0xf0, 0xf3, 0xf5, 0xf8, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xfe, 0xfd, 0xfc, 0xfb, 0xf9,
+ 0xf7, 0xf4, 0xf0, 0xec, 0xe7, 0xe2, 0xdc, 0xd5, 0xce, 0xc6, 0xbd, 0xb4, 0xa9, 0x9e, 0x92, 0x88,
+ 0x82, 0x7d, 0x77, 0x72, 0x6c, 0x66, 0x60, 0x5a, 0x54, 0x4e, 0x49, 0x42, 0x3c, 0x37, 0x32, 0x2d,
+ 0x28, 0x24, 0x1f, 0x1b, 0x18, 0x14, 0x11, 0x0e, 0x0c, 0x09, 0x07, 0x06, 0x05, 0x04, 0x04, 0x04,
+ 0x04, 0x05, 0x06, 0x08, 0x0a, 0x0d, 0x10, 0x14, 0x18, 0x1d, 0x23, 0x29, 0x2f, 0x37, 0x3e, 0x47,
+ 0x50, 0x5a, 0x64, 0x70, 0x7c, 0x83, 0x85, 0x88, 0x8a, 0x8c, 0x8e, 0x8f, 0x91, 0x92, 0x93, 0x93 };
+
+
+
+
+unsigned char *envelope_data[18] = {
+ env_fall,
+ env_rise,
+ env_frise, env_r_frise,
+ env_frise2, env_r_frise2,
+ env_risefall, env_risefall,
+
+ env_fallrise3, env_fallrise3,
+ env_fallrise4, env_fallrise4,
+ env_fall2, env_fall2,
+ env_rise2, env_rise2,
+ env_risefallrise, env_risefallrise
+ };
+
+
+/* all pitches given in Hz above pitch_base */
+
+// pitch change during the main part of the clause
+static int drops_0[8] = {0x400,0x400,0x700,0x700,0x700,0xa00,0x1800,0x0e00};
+//static int drops_1[8] = {0x400,0x400,0x600,0x600,0xc00,0xc00,0x0e00,0x0e00};
+//static int drops_2[8] = {0x400,0x400,0x600,0x600,-0x800,0xc00,0x0e00,0x0e00};
+
+static short oflow[] = {0, 20, 12, 4, 0};
+static short oflow_emf[] = {5, 24, 15, 10, 5};
+static short oflow_less[] = {3, 19, 12, 7, 2};
+// static short oflow_test2[] = {20, 0, 20, 0, 20};
+// static short back_emf[] = {35, 32, 0};
+
+
+#define N_TONE_HEAD_TABLE 13
+#define N_TONE_NUCLEUS_TABLE 13
+
+
+typedef struct {
+ unsigned char pre_start;
+ unsigned char pre_end;
+
+ unsigned char body_start;
+ unsigned char body_end;
+
+ int *body_drops;
+ unsigned char body_max_steps;
+ char body_lower_u;
+
+ char n_overflow;
+ short *overflow;
+} TONE_HEAD;
+
+
+typedef struct {
+ unsigned char pitch_env0; /* pitch envelope, tonic syllable at end */
+ unsigned char tonic_max0;
+ unsigned char tonic_min0;
+
+ unsigned char pitch_env1; /* followed by unstressed */
+ unsigned char tonic_max1;
+ unsigned char tonic_min1;
+
+ short *backwards;
+
+ unsigned char tail_start;
+ unsigned char tail_end;
+ unsigned char flags;
+} TONE_NUCLEUS;
+
+#define T_EMPH 1
+
+static TONE_HEAD tone_head_table[N_TONE_HEAD_TABLE] = {
+ {20, 25, 34, 22, drops_0, 3, 3, 5, oflow}, // 0 statement
+ {20, 25, 34, 20, drops_0, 3, 3, 5, oflow}, // 1 comma
+ {20, 25, 34, 20, drops_0, 3, 3, 5, oflow}, // 2 question
+ {20, 25, 36, 22, drops_0, 3, 4, 5, oflow_emf}, // 3 exclamation
+ {20, 25, 34, 22, drops_0, 3, 3, 5, oflow}, // 4 statement, emphatic
+ {20, 25, 32, 24, drops_0, 4, 3, 5, oflow_less}, // 5 statement, less intonation
+ {20, 25, 32, 24, drops_0, 4, 3, 5, oflow_less}, // 6 comma, less intonation
+ {20, 25, 32, 24, drops_0, 4, 3, 5, oflow_less}, // 7 comma, less intonation, less rise
+ {20, 25, 34, 22, drops_0, 3, 3, 5, oflow}, // 8 pitch raises at end of sentence
+ {20, 25, 34, 20, drops_0, 3, 3, 5, oflow}, // 9 comma
+ {20, 25, 34, 22, drops_0, 3, 3, 5, oflow}, // 10 question
+ {15, 18, 18, 14, drops_0, 3, 3, 5, oflow_less}, // 11 test
+ {20, 25, 24, 22, drops_0, 3, 3, 5, oflow_less}, // 12 test
+};
+
+static TONE_NUCLEUS tone_nucleus_table[N_TONE_NUCLEUS_TABLE] = {
+ {PITCHfall, 30, 5, PITCHfall, 32, 9, NULL, 12, 7, 0}, // 0 statement
+ {PITCHfrise, 35, 8, PITCHfrise2, 35,10, NULL, 15, 23, 0}, // 1 comma
+ {PITCHfrise, 39,10, PITCHfrise2, 36,10, NULL, 15, 28, 0}, // 2 question
+// {PITCHfall, 41, 4, PITCHfall, 41,27, NULL, 16, 4, T_EMPH}, // 3 exclamation
+ {PITCHfall, 41, 4, PITCHfall, 41,35, NULL, 35, 4, T_EMPH}, // 3 exclamation
+ {PITCHfall, 38, 2, PITCHfall, 42,30, NULL, 15, 5, 0}, // 4 statement, emphatic
+ {PITCHfall, 28, 5, PITCHfall, 28, 9, NULL, 12, 7, 0}, // 5 statement, less intonation
+ {PITCHfrise, 30, 8, PITCHfrise2, 30,10, NULL, 13, 20, 0}, // 6 comma, less intonation
+ {PITCHfrise2, 28, 7, PITCHfall, 29,14, NULL, 14, 8, 0}, // 7 comma, less intonation, less rise
+ {PITCHrise, 30,20, PITCHfall, 19,14, NULL, 20, 26, 0}, // 8 pitch raises at end of sentence
+ {PITCHfrise, 35,11, PITCHfrise2, 32,10, NULL, 19, 24, 0}, // 9 comma
+ {PITCHfrise, 39,15, PITCHfall, 28,14, NULL, 20, 36, 0}, // 10 question
+ {PITCHfall, 28, 6, PITCHfall, 28,10, NULL, 12, 6, 0}, // 11 test
+ {PITCHfall, 35, 9, PITCHfall, 35,12, NULL, 16, 10, 0}, // 12 test
+};
+
+
+
+/* index by 0=. 1=, 2=?, 3=! 4=none, 5=emphasized */
+unsigned char punctuation_to_tone[INTONATION_TYPES][PUNCT_INTONATIONS] = {
+ {0,1,2,3,0,4},
+ {0,1,2,3,0,4},
+ {5,6,2,3,0,4},
+ {5,7,1,3,0,4},
+ {8,9,10,3,0,0},
+ {8,8,10,3,0,0},
+ {11,11,11,11,0,0}, // 6 test
+ {12,12,12,12,0,0}
+};
+
+
+
+/* indexed by stress */
+static int min_drop[] = {0x300,0x300,0x400,0x400,0x900,0x900,0x900,0xb00};
+
+
+
+
+#define SECONDARY 3
+#define PRIMARY 4
+#define PRIMARY_STRESSED 6
+#define PRIMARY_LAST 7
+
+
+static int number_pre;
+static int number_body;
+static int number_tail;
+static int last_primary;
+static int tone_posn;
+static int tone_posn2;
+static int no_tonic;
+
+
+static void count_pitch_vowels(int start, int end, int clause_end)
+/****************************************************************/
+{
+ int ix;
+ int stress;
+ int max_stress = 0;
+ int max_stress_posn = 0; // last syllable ot the highest stress
+ int max_stress_posn2 = 0; // penuntimate syllable of the highest stress
+
+ number_pre = -1; /* number of vowels before 1st primary stress */
+ number_body = 0;
+ number_tail = 0; /* number between tonic syllable and next primary */
+ last_primary = -1;
+
+ for(ix=start; ix<end; ix++)
+ {
+ stress = syllable_tab[ix].stress; /* marked stress level */
+
+ if(stress >= max_stress)
+ {
+ if(stress > max_stress)
+ {
+ max_stress_posn2 = ix;
+ }
+ else
+ {
+ max_stress_posn2 = max_stress_posn;
+ }
+ max_stress_posn = ix;
+ max_stress = stress;
+ }
+ if(stress >= PRIMARY)
+ {
+ if(number_pre < 0)
+ number_pre = ix - start;
+
+ last_primary = ix;
+ }
+
+ }
+
+ if(number_pre < 0)
+ number_pre = end;
+
+ number_tail = end - max_stress_posn - 1;
+ tone_posn = max_stress_posn;
+ tone_posn2 = max_stress_posn2;
+
+ if(no_tonic)
+ {
+ tone_posn = tone_posn2 = end; // next position after the end of the truncated clause
+ }
+ else
+ if(last_primary >= 0)
+ {
+ if(end == clause_end)
+ {
+ syllable_tab[last_primary].stress = PRIMARY_LAST;
+ }
+ }
+ else
+ {
+ // no primary stress. Use the highest stress
+ syllable_tab[tone_posn].stress = PRIMARY_LAST;
+ }
+} /* end of count_pitch_vowels */
+
+
+
+
+static int count_increments(int ix, int end_ix, int min_stress)
+/*************************************************************/
+/* Count number of primary stresses up to tonic syllable or body_reset */
+{
+ int count = 0;
+ int stress;
+
+ while(ix < end_ix)
+ {
+ stress = syllable_tab[ix++].stress;
+ if(stress >= PRIMARY_LAST)
+ break;
+
+ if(stress >= min_stress)
+ count++;
+ }
+ return(count);
+} /* end of count_increments */
+
+
+
+static void set_pitch(SYLLABLE *syl, int base, int drop)
+/******************************************************/
+// Set the pitch of a vowel in syllable_tab. Base & drop are Hz * 256
+{
+ int pitch1, pitch2;
+ int flags = 0;
+
+ /* adjust experimentally */
+ int pitch_range2 = 148;
+ int pitch_base2 = 72;
+
+ if(base < 0) base = 0;
+
+ pitch2 = ((base * pitch_range2 ) >> 15) + pitch_base2;
+
+ if(drop < 0)
+ {
+ flags = SYL_RISE;
+ drop = -drop;
+ }
+
+ pitch1 = pitch2 + ((drop * pitch_range2) >> 15);
+
+ if(pitch1 > 511) pitch1 = 511;
+ if(pitch2 > 511) pitch2 = 511;
+
+ syl->pitch1 = pitch1;
+ syl->pitch2 = pitch2;
+ syl->flags |= flags;
+} /* end of set_pitch */
+
+
+
+static int calc_pitch_segment(int ix, int end_ix, TONE_HEAD *th, TONE_NUCLEUS *tn, int min_stress, int continuing)
+/**********************************************************************************************/
+/* Calculate pitches until next RESET or tonic syllable, or end.
+ Increment pitch if stress is >= min_stress.
+ Used for tonic segment */
+{
+ int stress;
+ int pitch=0;
+ int increment=0;
+ int n_primary=0;
+ int n_steps=0;
+ int initial;
+ int overflow=0;
+ int n_overflow;
+ int *drops;
+ short *overflow_tab;
+ SYLLABLE *syl;
+
+ static short continue_tab[5] = {-13, 16, 10, 4, 0};
+
+ drops = th->body_drops;
+
+ if(continuing)
+ {
+ initial =0;
+ overflow = 0;
+ n_overflow = 5;
+ overflow_tab = continue_tab;
+ increment = (th->body_end - th->body_start) << 8;
+ increment = increment / (th->body_max_steps -1);
+ }
+ else
+ {
+ n_overflow = th->n_overflow;
+ overflow_tab = th->overflow;
+ initial = 1;
+ }
+
+ while(ix < end_ix)
+ {
+ syl = &syllable_tab[ix];
+ stress = syl->stress;
+
+// if(stress == PRIMARY_MARKED)
+// initial = 1; // reset the intonation pattern
+
+ if(initial || (stress >= min_stress))
+ {
+ // a primary stress
+
+ if((initial) || (stress == 5))
+ {
+ initial = 0;
+ overflow = 0;
+ n_steps = n_primary = count_increments(ix,end_ix,min_stress);
+
+ if(n_steps > th->body_max_steps)
+ n_steps = th->body_max_steps;
+
+ if(n_steps > 1)
+ {
+ increment = (th->body_end - th->body_start) << 8;
+ increment = increment / (n_steps -1);
+ }
+ else
+ increment = 0;
+
+ pitch = th->body_start << 8;
+ }
+ else
+ {
+ if(n_steps > 0)
+ pitch += increment;
+ else
+ {
+ pitch = (th->body_end << 8) - (increment * overflow_tab[overflow++])/16;
+ if(overflow >= n_overflow)
+ {
+ overflow = 0;
+ overflow_tab = th->overflow;
+ }
+ }
+ }
+
+ n_steps--;
+
+ n_primary--;
+ if((tn->backwards) && (n_primary < 2))
+ {
+ pitch = tn->backwards[n_primary] << 8;
+ }
+ }
+
+ if(stress >= PRIMARY)
+ {
+ syl->stress = PRIMARY_STRESSED;
+ set_pitch(syl,pitch,drops[stress]);
+ }
+ else
+ if(stress >= SECONDARY)
+ {
+ set_pitch(syl,pitch,drops[stress]);
+ }
+ else
+ {
+ /* unstressed, drop pitch if preceded by PRIMARY */
+ if((syllable_tab[ix-1].stress & 0x3f) >= SECONDARY)
+ set_pitch(syl,pitch - (th->body_lower_u << 8), drops[stress]);
+ else
+ set_pitch(syl,pitch,drops[stress]);
+ }
+
+ ix++;
+ }
+ return(ix);
+} /* end of calc_pitch_segment */
+
+
+
+
+
+static int calc_pitch_segment2(int ix, int end_ix, int start_p, int end_p, int min_stress)
+/****************************************************************************************/
+/* Linear pitch rise/fall, change pitch at min_stress or stronger
+ Used for pre-head and tail */
+{
+ int stress;
+ int pitch;
+ int increment;
+ int n_increments;
+ int drop;
+ SYLLABLE *syl;
+
+ if(ix >= end_ix)
+ return(ix);
+
+ n_increments = count_increments(ix,end_ix,min_stress);
+ increment = (end_p - start_p) << 8;
+
+ if(n_increments > 1)
+ {
+ increment = increment / n_increments;
+ }
+
+
+ pitch = start_p << 8;
+ while(ix < end_ix)
+ {
+ syl = &syllable_tab[ix];
+ stress = syl->stress;
+
+ if(increment > 0)
+ {
+ set_pitch(syl,pitch,-increment);
+ pitch += increment;
+ }
+ else
+ {
+ drop = -increment;
+ if(drop < min_drop[stress])
+ drop = min_drop[stress];
+
+ pitch += increment;
+
+ if(drop > 0x900)
+ drop = 0x900;
+ set_pitch(syl, pitch, drop);
+ }
+
+ ix++;
+ }
+ return(ix);
+} /* end of calc_pitch_segment2 */
+
+
+
+
+
+
+static int calc_pitches(int start, int end, int head_tone, int nucleus_tone)
+//===========================================================================
+// Calculate pitch values for the vowels in this tone group
+{
+ int ix;
+ TONE_HEAD *th;
+ TONE_NUCLEUS *tn;
+ int drop;
+ int continuing = 0;
+
+ if(start > 0)
+ continuing = 1;
+
+ th = &tone_head_table[head_tone];
+ tn = &tone_nucleus_table[nucleus_tone];
+ ix = start;
+
+ /* vowels before the first primary stress */
+ /******************************************/
+
+ if(number_pre > 0)
+ {
+ ix = calc_pitch_segment2(ix, ix+number_pre, th->pre_start, th->pre_end, 0);
+ }
+
+ /* body of tonic segment */
+ /*************************/
+
+ if(option_tone_flags & OPTION_EMPHASIZE_PENULTIMATE)
+ {
+ tone_posn = tone_posn2; // put tone on the penultimate stressed word
+ }
+ ix = calc_pitch_segment(ix,tone_posn, th, tn, PRIMARY, continuing);
+
+ if(no_tonic)
+ return(0);
+
+ /* tonic syllable */
+ /******************/
+
+ if(tn->flags & T_EMPH)
+ {
+ syllable_tab[ix].flags |= SYL_EMPHASIS;
+ }
+
+ if(number_tail == 0)
+ {
+ tone_pitch_env = tn->pitch_env0;
+ drop = tn->tonic_max0 - tn->tonic_min0;
+ set_pitch(&syllable_tab[ix++],tn->tonic_min0 << 8,drop << 8);
+ }
+ else
+ {
+ tone_pitch_env = tn->pitch_env1;
+ drop = tn->tonic_max1 - tn->tonic_min1;
+ set_pitch(&syllable_tab[ix++],tn->tonic_min1 << 8,drop << 8);
+ }
+
+ syllable_tab[tone_posn].env = tone_pitch_env;
+ if(syllable_tab[tone_posn].stress == PRIMARY)
+ syllable_tab[tone_posn].stress = PRIMARY_STRESSED;
+
+ /* tail, after the tonic syllable */
+ /**********************************/
+
+ calc_pitch_segment2(ix, end, tn->tail_start, tn->tail_end, 0);
+
+ return(tone_pitch_env);
+} /* end of calc_pitches */
+
+
+
+
+
+
+static void CalcPitches_Tone(Translator *tr, int clause_tone)
+{//==========================================================
+// clause_tone: 0=. 1=, 2=?, 3=! 4=none
+ PHONEME_LIST *p;
+ int ix;
+ int count_stressed=0;
+ int final_stressed=0;
+
+ int tone_ph;
+ int pause;
+ int tone_promoted;
+ PHONEME_TAB *tph;
+ PHONEME_TAB *prev_tph; // forget across word boundary
+ PHONEME_TAB *prevw_tph; // remember across word boundary
+ PHONEME_TAB *prev2_tph; // 2 tones previous
+ PHONEME_LIST *prev_p;
+
+ int pitch_adjust = 0; // pitch gradient through the clause - inital value
+ int pitch_decrement = 0; // decrease by this for each stressed syllable
+ int pitch_low = 0; // until it drops to this
+ int pitch_high = 0; // then reset to this
+
+ p = &phoneme_list[0];
+
+ // count number of stressed syllables
+ p = &phoneme_list[0];
+ for(ix=0; ix<n_phoneme_list; ix++, p++)
+ {
+ if((p->type == phVOWEL) && (p->tone >= 4))
+ {
+ if(count_stressed == 0)
+ final_stressed = ix;
+
+ if(p->tone >= 4)
+ {
+ final_stressed = ix;
+ count_stressed++;
+ }
+ }
+ }
+
+ phoneme_list[final_stressed].tone = 7;
+
+ // language specific, changes to tones
+ if(tr->translator_name == L('v','i'))
+ {
+ // LANG=vi
+ p = &phoneme_list[final_stressed];
+ if(p->tone_ph == 0)
+ p->tone_ph = PhonemeCode('7'); // change default tone (tone 1) to falling tone at end of clause
+ }
+
+
+ pause = 1;
+ tone_promoted = 0;
+
+ prev_p = p = &phoneme_list[0];
+ prev_tph = prevw_tph = phoneme_tab[phonPAUSE];
+
+ // perform tone sandhi
+ for(ix=0; ix<n_phoneme_list; ix++, p++)
+ {
+ if((p->type == phPAUSE) && (p->ph->std_length > 50))
+ {
+ pause = 1; // there is a pause since the previous vowel
+ prevw_tph = phoneme_tab[phonPAUSE]; // forget previous tone
+ }
+
+ if(p->newword)
+ {
+ prev_tph = phoneme_tab[phonPAUSE]; // forget across word boundaries
+ }
+
+ if(p->synthflags & SFLAG_SYLLABLE)
+ {
+ tone_ph = p->tone_ph;
+ tph = phoneme_tab[tone_ph];
+
+ // Mandarin
+ if(tr->translator_name == L('z','h'))
+ {
+ if(tone_ph == 0)
+ {
+ if(pause || tone_promoted)
+ {
+ tone_ph = PhonemeCode2('5','5'); // no previous vowel, use tone 1
+ tone_promoted = 1;
+ }
+ else
+ {
+ tone_ph = PhonemeCode2('1','1'); // default tone 5
+ }
+
+ p->tone_ph = tone_ph;
+ tph = phoneme_tab[tone_ph];
+
+ }
+ else
+ {
+ tone_promoted = 0;
+ }
+
+ if(ix == final_stressed)
+ {
+ if((tph->mnemonic == 0x3535 ) || (tph->mnemonic == 0x3135))
+ {
+ // change sentence final tone 1 or 4 to stress 6, not 7
+ phoneme_list[final_stressed].tone = 6;
+ }
+ }
+
+ if(prevw_tph->mnemonic == 0x343132) // [214]
+ {
+ if(tph->mnemonic == 0x343132) // [214]
+ prev_p->tone_ph = PhonemeCode2('3','5');
+ else
+ prev_p->tone_ph = PhonemeCode2('2','1');
+ }
+ if((prev_tph->mnemonic == 0x3135) && (tph->mnemonic == 0x3135)) // [51] + [51]
+ {
+ prev_p->tone_ph = PhonemeCode2('5','3');
+ }
+
+ if(tph->mnemonic == 0x3131) // [11] Tone 5
+ {
+ // tone 5, change its level depending on the previous tone (across word boundaries)
+ if(prevw_tph->mnemonic == 0x3535)
+ p->tone_ph = PhonemeCode2('2','2');
+ if(prevw_tph->mnemonic == 0x3533)
+ p->tone_ph = PhonemeCode2('3','3');
+ if(prevw_tph->mnemonic == 0x343132)
+ p->tone_ph = PhonemeCode2('4','4');
+
+ // tone 5 is unstressed (shorter)
+ p->tone = 1; // diminished stress
+ }
+ }
+
+ prev_p = p;
+ prev2_tph = prevw_tph;
+ prevw_tph = prev_tph = tph;
+ pause = 0;
+ }
+ }
+
+ // convert tone numbers to pitch
+ p = &phoneme_list[0];
+ for(ix=0; ix<n_phoneme_list; ix++, p++)
+ {
+ if(p->synthflags & SFLAG_SYLLABLE)
+ {
+ tone_ph = p->tone_ph;
+
+ if(p->tone != 1) // TEST, consider all syllables as stressed
+ {
+ if(ix == final_stressed)
+ {
+ // the last stressed syllable
+ pitch_adjust = pitch_low;
+ }
+ else
+ {
+ pitch_adjust -= pitch_decrement;
+ if(pitch_adjust <= pitch_low)
+ pitch_adjust = pitch_high;
+ }
+ }
+
+ if(tone_ph ==0)
+ {
+ tone_ph = phonDEFAULTTONE; // no tone specified, use default tone 1
+ p->tone_ph = tone_ph;
+ }
+ p->pitch1 = pitch_adjust + phoneme_tab[tone_ph]->start_type;
+ p->pitch2 = pitch_adjust + phoneme_tab[tone_ph]->end_type;
+ }
+ }
+
+
+} // end of Translator::CalcPitches_Tone
+
+
+
+void CalcPitches(Translator *tr, int clause_type)
+{//==============================================
+// clause_type: 0=. 1=, 2=?, 3=! 4=none
+ PHONEME_LIST *p;
+ SYLLABLE *syl;
+ int ix;
+ int x;
+ int st_ix;
+ int n_st;
+ int option;
+ int group_tone;
+ int group_tone_emph;
+ int group_tone_comma;
+ int ph_start=0;
+ int st_start;
+ int st_clause_end;
+ int count;
+ int n_primary;
+ int count_primary;
+ PHONEME_TAB *ph;
+ int ph_end=n_phoneme_list;
+
+ SYLLABLE syllable_tab2[N_PHONEME_LIST];
+
+ syllable_tab = syllable_tab2; // don't use permanent storage. it's only needed during the call of CalcPitches()
+ n_st = 0;
+ n_primary = 0;
+ for(ix=0; ix<(n_phoneme_list-1); ix++)
+ {
+ p = &phoneme_list[ix];
+ if(p->synthflags & SFLAG_SYLLABLE)
+ {
+ syllable_tab[n_st].flags = 0;
+ syllable_tab[n_st].env = PITCHfall;
+ syllable_tab[n_st].nextph_type = phoneme_list[ix+1].type;
+ syllable_tab[n_st++].stress = p->tone; // stress level
+
+ if(p->tone >= 4)
+ n_primary++;
+ }
+ else
+ if((p->ph->code == phonPAUSE_CLAUSE) && (n_st > 0))
+ {
+ syllable_tab[n_st-1].flags |= SYL_END_CLAUSE;
+ }
+ }
+ syllable_tab[n_st].stress = 0; // extra 0 entry at the end
+
+ if(n_st == 0)
+ return; // nothing to do
+
+
+
+ if(tr->langopts.tone_language == 1)
+ {
+ CalcPitches_Tone(tr,clause_type);
+ return;
+ }
+
+
+ option = tr->langopts.intonation_group;
+ if(option >= INTONATION_TYPES)
+ option = 0;
+
+ group_tone = tr->punct_to_tone[option][clause_type];
+ group_tone_emph = tr->punct_to_tone[option][5]; // emphatic form of statement
+ group_tone_comma = tr->punct_to_tone[option][1]; // emphatic form of statement
+
+ if(clause_type == 4)
+ no_tonic = 1; /* incomplete clause, used for abbreviations such as Mr. Dr. Mrs. */
+ else
+ no_tonic = 0;
+
+ st_start = 0;
+ count_primary=0;
+ for(st_ix=0; st_ix<n_st; st_ix++)
+ {
+ syl = &syllable_tab[st_ix];
+
+ if(syl->stress >= 4)
+ count_primary++;
+
+ if(syl->stress == 6)
+ {
+ // reduce the stress of the previous stressed syllable (review only the previous few syllables)
+ for(ix=st_ix-1; ix>=st_start && ix>=(st_ix-3); ix--)
+ {
+ if(syllable_tab[ix].stress == 6)
+ break;
+ if(syllable_tab[ix].stress == 4)
+ {
+ syllable_tab[ix].stress = 3;
+ break;
+ }
+ }
+
+ // are the next primary syllables also emphasized ?
+ for(ix=st_ix+1; ix<n_st; ix++)
+ {
+ if(syllable_tab[ix].stress == 4)
+ break;
+ if(syllable_tab[ix].stress == 6)
+ {
+ // emphasize this syllable, but don't end the current tone group
+ syllable_tab[st_ix].flags = SYL_EMPHASIS;
+ syl->stress = 5;
+ break;
+ }
+ }
+ }
+
+ if(syl->stress == 6)
+ {
+ // an emphasized syllable, end the tone group after the next primary stress
+ syllable_tab[st_ix].flags = SYL_EMPHASIS;
+
+ count = 0;
+ if((n_primary - count_primary) > 1)
+ count =1;
+
+ for(ix=st_ix+1; ix<n_st; ix++)
+ {
+ if(syllable_tab[ix].stress > 4)
+ break;
+ if(syllable_tab[ix].stress == 4)
+ {
+ count++;
+ if(count > 1)
+ break;
+ }
+ }
+
+ count_pitch_vowels(st_start, ix, n_st);
+ if((ix < n_st) || (clause_type == 0))
+ calc_pitches(st_start, ix, group_tone_emph, group_tone_emph); // split into > 1 tone groups, use emphatic tone
+ else
+ calc_pitches(st_start, ix, group_tone, group_tone);
+
+ st_start = ix;
+ }
+ if((st_start < st_ix) && (syl->flags & SYL_END_CLAUSE))
+ {
+ // end of clause after this syllable, indicated by a phonPAUSE_CLAUSE phoneme
+ st_clause_end = st_ix+1;
+ count_pitch_vowels(st_start, st_clause_end, st_clause_end);
+ calc_pitches(st_start, st_clause_end, group_tone_comma, group_tone_comma);
+ st_start = st_clause_end;
+ }
+ }
+
+ if(st_start < st_ix)
+ {
+ count_pitch_vowels(st_start, st_ix, n_st);
+ calc_pitches(st_start, st_ix, group_tone, group_tone);
+ }
+
+
+ // unpack pitch data
+ st_ix=0;
+ for(ix=ph_start; ix < ph_end; ix++)
+ {
+ p = &phoneme_list[ix];
+ p->tone = syllable_tab[st_ix].stress;
+
+ if(p->synthflags & SFLAG_SYLLABLE)
+ {
+ syl = &syllable_tab[st_ix];
+
+ x = syl->pitch1 - 72;
+ if(x < 0) x = 0;
+ p->pitch1 = x;
+
+ x = syl->pitch2 - 72;
+ if(x < 0) x = 0;
+ p->pitch2 = x;
+
+ p->env = PITCHfall;
+ if(syl->flags & SYL_RISE)
+ {
+ p->env = PITCHrise;
+ }
+ else
+ if(p->tone > 5)
+ p->env = syl->env;
+
+ if(p->pitch1 > p->pitch2)
+ {
+ // swap so that pitch2 is the higher
+ x = p->pitch1;
+ p->pitch1 = p->pitch2;
+ p->pitch2 = x;
+ }
+
+if(p->tone_ph)
+{
+ ph = phoneme_tab[p->tone_ph];
+ x = (p->pitch1 + p->pitch2)/2;
+ p->pitch2 = x + ph->end_type;
+ p->pitch1 = x + ph->start_type;
+}
+
+ if(syl->flags & SYL_EMPHASIS)
+ {
+ p->tone |= 8; // emphasized
+ }
+
+ st_ix++;
+ }
+ }
+
+} // end of Translator::CalcPitches
+
+
diff --git a/Plugins/eSpeak/eSpeak/klatt.cpp b/Plugins/eSpeak/eSpeak/klatt.cpp
new file mode 100644
index 0000000..da0baaa
--- /dev/null
+++ b/Plugins/eSpeak/eSpeak/klatt.cpp
@@ -0,0 +1,1282 @@
+
+/***************************************************************************
+ * Copyright (C) 2008 by Jonathan Duddington *
+ * email: jonsd@users.sourceforge.net *
+ * *
+ * Based on a re-implementation by: *
+ * (c) 1993,94 Jon Iles and Nick Ing-Simmons *
+ * of the Klatt cascade-parallel formant synthesizer *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 3 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, see: *
+ * <http://www.gnu.org/licenses/>. *
+ ***************************************************************************/
+
+// See URL: ftp://svr-ftp.eng.cam.ac.uk/pub/comp.speech/synthesis/klatt.3.04.tar.gz
+
+#include "StdAfx.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+#include "klatt.h"
+
+#include "speak_lib.h"
+#include "speech.h"
+#include "phoneme.h"
+#include "synthesize.h"
+#include "voice.h"
+
+#ifdef INCLUDE_KLATT // conditional compilation for the whole file
+
+extern unsigned char *out_ptr; // **JSD
+extern unsigned char *out_start;
+extern unsigned char *out_end;
+extern WGEN_DATA wdata;
+static int nsamples;
+static int sample_count;
+
+
+#ifdef _MSC_VER
+#define getrandom(min,max) ((rand()%(int)(((max)+1)-(min)))+(min))
+#else
+#define getrandom(min,max) ((rand()%(long)(((max)+1)-(min)))+(min))
+#endif
+
+
+/* function prototypes for functions private to this file */
+
+static void flutter(klatt_frame_ptr);
+static double sampled_source (void);
+static double impulsive_source (void);
+static double natural_source (void);
+static void pitch_synch_par_reset (klatt_frame_ptr);
+static double gen_noise (double);
+static double DBtoLIN (long);
+static void frame_init (klatt_frame_ptr);
+static void setabc (long,long,resonator_ptr);
+static void setzeroabc (long,long,resonator_ptr);
+
+static klatt_frame_t kt_frame;
+static klatt_global_t kt_globals;
+
+/*
+function RESONATOR
+
+This is a generic resonator function. Internal memory for the resonator
+is stored in the globals structure.
+*/
+
+static double resonator(resonator_ptr r, double input)
+{
+ double x;
+
+ x = (double) ((double)r->a * (double)input + (double)r->b * (double)r->p1 + (double)r->c * (double)r->p2);
+ r->p2 = (double)r->p1;
+ r->p1 = (double)x;
+
+ return (double)x;
+}
+
+static double resonator2(resonator_ptr r, double input)
+{
+ double x;
+
+ x = (double) ((double)r->a * (double)input + (double)r->b * (double)r->p1 + (double)r->c * (double)r->p2);
+ r->p2 = (double)r->p1;
+ r->p1 = (double)x;
+
+ r->a += r->a_inc;
+ r->b += r->b_inc;
+ r->c += r->c_inc;
+ return (double)x;
+}
+
+
+
+/*
+function ANTIRESONATOR
+
+This is a generic anti-resonator function. The code is the same as resonator
+except that a,b,c need to be set with setzeroabc() and we save inputs in
+p1/p2 rather than outputs. There is currently only one of these - "rnz"
+Output = (rnz.a * input) + (rnz.b * oldin1) + (rnz.c * oldin2)
+*/
+
+#ifdef deleted
+static double antiresonator(resonator_ptr r, double input)
+{
+ register double x = (double)r->a * (double)input + (double)r->b * (double)r->p1 + (double)r->c * (double)r->p2;
+ r->p2 = (double)r->p1;
+ r->p1 = (double)input;
+ return (double)x;
+}
+#endif
+
+static double antiresonator2(resonator_ptr r, double input)
+{
+ register double x = (double)r->a * (double)input + (double)r->b * (double)r->p1 + (double)r->c * (double)r->p2;
+ r->p2 = (double)r->p1;
+ r->p1 = (double)input;
+
+ r->a += r->a_inc;
+ r->b += r->b_inc;
+ r->c += r->c_inc;
+ return (double)x;
+}
+
+
+
+/*
+function FLUTTER
+
+This function adds F0 flutter, as specified in:
+
+"Analysis, synthesis and perception of voice quality variations among
+female and male talkers" D.H. Klatt and L.C. Klatt JASA 87(2) February 1990.
+
+Flutter is added by applying a quasi-random element constructed from three
+slowly varying sine waves.
+*/
+
+static void flutter(klatt_frame_ptr frame)
+{
+ static int time_count;
+ double delta_f0;
+ double fla,flb,flc,fld,fle;
+
+ fla = (double) kt_globals.f0_flutter / 50;
+ flb = (double) kt_globals.original_f0 / 100;
+// flc = sin(2*PI*12.7*time_count);
+// fld = sin(2*PI*7.1*time_count);
+// fle = sin(2*PI*4.7*time_count);
+ flc = sin(PI*12.7*time_count); // because we are calling flutter() more frequently, every 2.9mS
+ fld = sin(PI*7.1*time_count);
+ fle = sin(PI*4.7*time_count);
+ delta_f0 = fla * flb * (flc + fld + fle) * 10;
+ frame->F0hz10 = frame->F0hz10 + (long) delta_f0;
+ time_count++;
+}
+
+
+
+/*
+function SAMPLED_SOURCE
+
+Allows the use of a glottal excitation waveform sampled from a real
+voice.
+*/
+
+static double sampled_source()
+{
+ int itemp;
+ double ftemp;
+ double result;
+ double diff_value;
+ int current_value;
+ int next_value;
+ double temp_diff;
+
+ if(kt_globals.T0!=0)
+ {
+ ftemp = (double) kt_globals.nper;
+ ftemp = ftemp / kt_globals.T0;
+ ftemp = ftemp * kt_globals.num_samples;
+ itemp = (int) ftemp;
+
+ temp_diff = ftemp - (double) itemp;
+
+ current_value = kt_globals.natural_samples[itemp];
+ next_value = kt_globals.natural_samples[itemp+1];
+
+ diff_value = (double) next_value - (double) current_value;
+ diff_value = diff_value * temp_diff;
+
+ result = kt_globals.natural_samples[itemp] + diff_value;
+ result = result * kt_globals.sample_factor;
+ }
+ else
+ {
+ result = 0;
+ }
+ return(result);
+}
+
+
+
+
+/*
+function PARWAVE
+
+Converts synthesis parameters to a waveform.
+*/
+
+
+static int parwave(klatt_frame_ptr frame)
+{
+ double temp;
+ double outbypas;
+ double out;
+ long n4;
+ double frics;
+ double glotout;
+ double aspiration;
+ double casc_next_in;
+ double par_glotout;
+ static double noise;
+ static double voice;
+ static double vlast;
+ static double glotlast;
+ static double sourc;
+ int ix;
+
+ frame_init(frame); /* get parameters for next frame of speech */
+
+ flutter(frame); /* add f0 flutter */
+
+#ifdef deleted
+{
+ FILE *f;
+ f=fopen("klatt_log","a");
+ fprintf(f,"%4dhz %2dAV %4d %3d, %4d %3d, %4d %3d, %4d %3d, %4d, %3d, %4d %3d TLT=%2d\n",frame->F0hz10,frame->AVdb,
+ frame->F1hz,frame->B1hz,frame->F2hz,frame->B2hz,frame->F3hz,frame->B3hz,frame->F4hz,frame->B4hz,frame->F5hz,frame->B5hz,frame->F6hz,frame->B6hz,frame->TLTdb);
+ fclose(f);
+}
+#endif
+
+ /* MAIN LOOP, for each output sample of current frame: */
+
+ for (kt_globals.ns=0; kt_globals.ns<kt_globals.nspfr; kt_globals.ns++)
+ {
+ /* Get low-passed random number for aspiration and frication noise */
+ noise = gen_noise(noise);
+
+ /*
+ Amplitude modulate noise (reduce noise amplitude during
+ second half of glottal period) if voicing simultaneously present.
+ */
+
+ if (kt_globals.nper > kt_globals.nmod)
+ {
+ noise *= (double) 0.5;
+ }
+
+ /* Compute frication noise */
+ frics = kt_globals.amp_frica * noise;
+
+ /*
+ Compute voicing waveform. Run glottal source simulation at 4
+ times normal sample rate to minimize quantization noise in
+ period of female voice.
+ */
+
+ for (n4=0; n4<4; n4++)
+ {
+ switch(kt_globals.glsource)
+ {
+ case IMPULSIVE:
+ voice = impulsive_source();
+ break;
+ case NATURAL:
+ voice = natural_source();
+ break;
+ case SAMPLED:
+ voice = sampled_source();
+ break;
+ }
+
+ /* Reset period when counter 'nper' reaches T0 */
+ if (kt_globals.nper >= kt_globals.T0)
+ {
+ kt_globals.nper = 0;
+ pitch_synch_par_reset(frame);
+ }
+
+ /*
+ Low-pass filter voicing waveform before downsampling from 4*samrate
+ to samrate samples/sec. Resonator f=.09*samrate, bw=.06*samrate
+ */
+
+ voice = resonator(&(kt_globals.rsn[RLP]),voice);
+
+ /* Increment counter that keeps track of 4*samrate samples per sec */
+ kt_globals.nper++;
+ }
+
+ /*
+ Tilt spectrum of voicing source down by soft low-pass filtering, amount
+ of tilt determined by TLTdb
+ */
+
+ voice = (voice * kt_globals.onemd) + (vlast * kt_globals.decay);
+ vlast = voice;
+
+ /*
+ Add breathiness during glottal open phase. Amount of breathiness
+ determined by parameter Aturb Use nrand rather than noise because
+ noise is low-passed.
+ */
+
+
+ if (kt_globals.nper < kt_globals.nopen)
+ {
+ voice += kt_globals.amp_breth * kt_globals.nrand;
+ }
+
+ /* Set amplitude of voicing */
+ glotout = kt_globals.amp_voice * voice;
+ par_glotout = kt_globals.par_amp_voice * voice;
+
+ /* Compute aspiration amplitude and add to voicing source */
+ aspiration = kt_globals.amp_aspir * noise;
+ glotout += aspiration;
+
+ par_glotout += aspiration;
+
+ /*
+ Cascade vocal tract, excited by laryngeal sources.
+ Nasal antiresonator, then formants FNP, F5, F4, F3, F2, F1
+ */
+
+ out=0;
+ if(kt_globals.synthesis_model != ALL_PARALLEL)
+ {
+ casc_next_in = antiresonator2(&(kt_globals.rsn[Rnz]),glotout);
+ casc_next_in = resonator(&(kt_globals.rsn[Rnpc]),casc_next_in);
+ casc_next_in = resonator(&(kt_globals.rsn[R8c]),casc_next_in);
+ casc_next_in = resonator(&(kt_globals.rsn[R7c]),casc_next_in);
+ casc_next_in = resonator(&(kt_globals.rsn[R6c]),casc_next_in);
+ casc_next_in = resonator2(&(kt_globals.rsn[R5c]),casc_next_in);
+ casc_next_in = resonator2(&(kt_globals.rsn[R4c]),casc_next_in);
+ casc_next_in = resonator2(&(kt_globals.rsn[R3c]),casc_next_in);
+ casc_next_in = resonator2(&(kt_globals.rsn[R2c]),casc_next_in);
+ out = resonator2(&(kt_globals.rsn[R1c]),casc_next_in);
+ }
+
+ /* Excite parallel F1 and FNP by voicing waveform */
+ sourc = par_glotout; /* Source is voicing plus aspiration */
+
+ /*
+ Standard parallel vocal tract Formants F6,F5,F4,F3,F2,
+ outputs added with alternating sign. Sound source for other
+ parallel resonators is frication plus first difference of
+ voicing waveform.
+ */
+
+ out += resonator(&(kt_globals.rsn[R1p]),sourc);
+ out += resonator(&(kt_globals.rsn[Rnpp]),sourc);
+
+ sourc = frics + par_glotout - glotlast;
+ glotlast = par_glotout;
+
+ for(ix=R2p; ix<=R6p; ix++)
+ {
+ out = resonator(&(kt_globals.rsn[ix]),sourc) - out;
+ }
+
+ outbypas = kt_globals.amp_bypas * sourc;
+
+ out = outbypas - out;
+
+#ifdef deleted
+// for testing
+ if (kt_globals.outsl != 0)
+ {
+ switch(kt_globals.outsl)
+ {
+ case 1:
+ out = voice;
+ break;
+ case 2:
+ out = aspiration;
+ break;
+ case 3:
+ out = frics;
+ break;
+ case 4:
+ out = glotout;
+ break;
+ case 5:
+ out = par_glotout;
+ break;
+ case 6:
+ out = outbypas;
+ break;
+ case 7:
+ out = sourc;
+ break;
+ }
+ }
+#endif
+
+ out = resonator(&(kt_globals.rsn[Rout]),out);
+ temp = (out * wdata.amplitude * kt_globals.amp_gain0) ; /* Convert back to integer */
+
+
+ // mix with a recorded WAV if required for this phoneme
+ {
+ int z2;
+ signed char c;
+ int sample;
+
+ z2 = 0;
+ if(wdata.mix_wavefile_ix < wdata.n_mix_wavefile)
+ {
+ if(wdata.mix_wave_scale == 0)
+ {
+ // a 16 bit sample
+ c = wdata.mix_wavefile[wdata.mix_wavefile_ix+1];
+ sample = wdata.mix_wavefile[wdata.mix_wavefile_ix] + (c * 256);
+ wdata.mix_wavefile_ix += 2;
+ }
+ else
+ {
+ // a 8 bit sample, scaled
+ sample = (signed char)wdata.mix_wavefile[wdata.mix_wavefile_ix++] * wdata.mix_wave_scale;
+ }
+ z2 = sample * wdata.amplitude_v / 1024;
+ z2 = (z2 * wdata.mix_wave_amp)/40;
+ temp += z2;
+ }
+ }
+
+ // if fadeout is set, fade to zero over 64 samples, to avoid clicks at end of synthesis
+ if(kt_globals.fadeout > 0)
+ {
+ kt_globals.fadeout--;
+ temp = (temp * kt_globals.fadeout) / 64;
+ }
+
+ if (temp < -32768.0)
+ {
+ temp = -32768.0;
+ }
+
+ if (temp > 32767.0)
+ {
+ temp = 32767.0;
+ }
+
+ *out_ptr++ = int(temp); // **JSD
+ *out_ptr++ = int(temp) >> 8;
+ sample_count++;
+ if(out_ptr >= out_end)
+ {
+ return(1);
+ }
+ }
+ return(0);
+} // end of parwave
+
+
+
+
+/*
+function PARWAVE_INIT
+
+Initialises all parameters used in parwave, sets resonator internal memory
+to zero.
+*/
+
+static void reset_resonators()
+{
+ int r_ix;
+
+ for(r_ix=0; r_ix < N_RSN; r_ix++)
+ {
+ kt_globals.rsn[r_ix].p1 = 0;
+ kt_globals.rsn[r_ix].p2 = 0;
+ }
+}
+
+static void parwave_init()
+{
+ kt_globals.FLPhz = (950 * kt_globals.samrate) / 10000;
+ kt_globals.BLPhz = (630 * kt_globals.samrate) / 10000;
+ kt_globals.minus_pi_t = -PI / kt_globals.samrate;
+ kt_globals.two_pi_t = -2.0 * kt_globals.minus_pi_t;
+ setabc(kt_globals.FLPhz,kt_globals.BLPhz,&(kt_globals.rsn[RLP]));
+ kt_globals.nper = 0;
+ kt_globals.T0 = 0;
+ kt_globals.nopen = 0;
+ kt_globals.nmod = 0;
+
+ reset_resonators();
+}
+
+
+/*
+function FRAME_INIT
+
+Use parameters from the input frame to set up resonator coefficients.
+*/
+
+static void frame_init(klatt_frame_ptr frame)
+{
+ double amp_par[7];
+ static double amp_par_factor[7] = {0.6, 0.4, 0.15, 0.06, 0.04, 0.022, 0.03};
+ long Gain0_tmp;
+ int ix;
+
+ kt_globals.original_f0 = frame->F0hz10 / 10;
+
+ frame->AVdb_tmp = frame->AVdb - 7;
+ if (frame->AVdb_tmp < 0)
+ {
+ frame->AVdb_tmp = 0;
+ }
+
+ kt_globals.amp_aspir = DBtoLIN(frame->ASP) * 0.05;
+ kt_globals.amp_frica = DBtoLIN(frame->AF) * 0.25;
+ kt_globals.par_amp_voice = DBtoLIN(frame->AVpdb);
+ kt_globals.amp_bypas = DBtoLIN(frame->AB) * 0.05;
+
+ for(ix=0; ix <= 6; ix++)
+ {
+ // parallel amplitudes F1 to F6, and parallel nasal pole
+ amp_par[ix] = DBtoLIN(frame->Ap[ix]) * amp_par_factor[ix];
+ }
+
+ Gain0_tmp = frame->Gain0 - 3;
+ if (Gain0_tmp <= 0)
+ {
+ Gain0_tmp = 57;
+ }
+ kt_globals.amp_gain0 = DBtoLIN(Gain0_tmp) / kt_globals.scale_wav;
+
+ /* Set coefficients of variable cascade resonators */
+ for(ix=0; ix<=8; ix++)
+ {
+ // formants 1 to 8, plus nasal pole
+ setabc(frame->Fhz[ix],frame->Bhz[ix],&(kt_globals.rsn[ix]));
+
+ if(ix <= 5)
+ {
+ setabc(frame->Fhz_next[ix],frame->Bhz_next[ix],&(kt_globals.rsn_next[ix]));
+
+ kt_globals.rsn[ix].a_inc = (kt_globals.rsn_next[ix].a - kt_globals.rsn[ix].a) / 64.0;
+ kt_globals.rsn[ix].b_inc = (kt_globals.rsn_next[ix].b - kt_globals.rsn[ix].b) / 64.0;
+ kt_globals.rsn[ix].c_inc = (kt_globals.rsn_next[ix].c - kt_globals.rsn[ix].c) / 64.0;
+ }
+ }
+
+ // nasal zero anti-resonator
+ setzeroabc(frame->Fhz[F_NZ],frame->Bhz[F_NZ],&(kt_globals.rsn[Rnz]));
+ setzeroabc(frame->Fhz_next[F_NZ],frame->Bhz_next[F_NZ],&(kt_globals.rsn_next[Rnz]));
+ kt_globals.rsn[F_NZ].a_inc = (kt_globals.rsn_next[F_NZ].a - kt_globals.rsn[F_NZ].a) / 64.0;
+ kt_globals.rsn[F_NZ].b_inc = (kt_globals.rsn_next[F_NZ].b - kt_globals.rsn[F_NZ].b) / 64.0;
+ kt_globals.rsn[F_NZ].c_inc = (kt_globals.rsn_next[F_NZ].c - kt_globals.rsn[F_NZ].c) / 64.0;
+
+
+ /* Set coefficients of parallel resonators, and amplitude of outputs */
+
+ for(ix=0; ix<=6; ix++)
+ {
+ setabc(frame->Fhz[ix],frame->Bphz[ix],&(kt_globals.rsn[Rparallel+ix]));
+ kt_globals.rsn[Rparallel+ix].a *= amp_par[ix];
+ }
+
+ /* output low-pass filter */
+
+ setabc((long)0.0,(long)(kt_globals.samrate/2),&(kt_globals.rsn[Rout]));
+
+}
+
+
+
+/*
+function IMPULSIVE_SOURCE
+
+Generate a low pass filtered train of impulses as an approximation of
+a natural excitation waveform. Low-pass filter the differentiated impulse
+with a critically-damped second-order filter, time constant proportional
+to Kopen.
+*/
+
+
+static double impulsive_source()
+{
+ static double doublet[] = {0.0,13000000.0,-13000000.0};
+ static double vwave;
+
+ if (kt_globals.nper < 3)
+ {
+ vwave = doublet[kt_globals.nper];
+ }
+ else
+ {
+ vwave = 0.0;
+ }
+
+ return(resonator(&(kt_globals.rsn[RGL]),vwave));
+}
+
+
+
+/*
+function NATURAL_SOURCE
+
+Vwave is the differentiated glottal flow waveform, there is a weak
+spectral zero around 800 Hz, magic constants a,b reset pitch synchronously.
+*/
+
+static double natural_source()
+{
+ double lgtemp;
+ static double vwave;
+
+ if (kt_globals.nper < kt_globals.nopen)
+ {
+ kt_globals.pulse_shape_a -= kt_globals.pulse_shape_b;
+ vwave += kt_globals.pulse_shape_a;
+ lgtemp=vwave * 0.028;
+
+ return(lgtemp);
+ }
+ else
+ {
+ vwave = 0.0;
+ return(0.0);
+ }
+}
+
+
+
+
+
+/*
+function PITCH_SYNC_PAR_RESET
+
+Reset selected parameters pitch-synchronously.
+
+
+Constant B0 controls shape of glottal pulse as a function
+of desired duration of open phase N0
+(Note that N0 is specified in terms of 40,000 samples/sec of speech)
+
+Assume voicing waveform V(t) has form: k1 t**2 - k2 t**3
+
+ If the radiation characterivative, a temporal derivative
+ is folded in, and we go from continuous time to discrete
+ integers n: dV/dt = vwave[n]
+ = sum over i=1,2,...,n of { a - (i * b) }
+ = a n - b/2 n**2
+
+ where the constants a and b control the detailed shape
+ and amplitude of the voicing waveform over the open
+ potion of the voicing cycle "nopen".
+
+ Let integral of dV/dt have no net dc flow --> a = (b * nopen) / 3
+
+ Let maximum of dUg(n)/dn be constant --> b = gain / (nopen * nopen)
+ meaning as nopen gets bigger, V has bigger peak proportional to n
+
+ Thus, to generate the table below for 40 <= nopen <= 263:
+
+ B0[nopen - 40] = 1920000 / (nopen * nopen)
+*/
+
+static void pitch_synch_par_reset(klatt_frame_ptr frame)
+{
+ long temp;
+ double temp1;
+ static long skew;
+ static short B0[224] =
+ {
+ 1200,1142,1088,1038, 991, 948, 907, 869, 833, 799, 768, 738, 710, 683, 658,
+ 634, 612, 590, 570, 551, 533, 515, 499, 483, 468, 454, 440, 427, 415, 403,
+ 391, 380, 370, 360, 350, 341, 332, 323, 315, 307, 300, 292, 285, 278, 272,
+ 265, 259, 253, 247, 242, 237, 231, 226, 221, 217, 212, 208, 204, 199, 195,
+ 192, 188, 184, 180, 177, 174, 170, 167, 164, 161, 158, 155, 153, 150, 147,
+ 145, 142, 140, 137, 135, 133, 131, 128, 126, 124, 122, 120, 119, 117, 115,
+ 113,111, 110, 108, 106, 105, 103, 102, 100, 99, 97, 96, 95, 93, 92, 91, 90,
+ 88, 87, 86, 85, 84, 83, 82, 80, 79, 78, 77, 76, 75, 75, 74, 73, 72, 71,
+ 70, 69, 68, 68, 67, 66, 65, 64, 64, 63, 62, 61, 61, 60, 59, 59, 58, 57,
+ 57, 56, 56, 55, 55, 54, 54, 53, 53, 52, 52, 51, 51, 50, 50, 49, 49, 48, 48,
+ 47, 47, 46, 46, 45, 45, 44, 44, 43, 43, 42, 42, 41, 41, 41, 41, 40, 40,
+ 39, 39, 38, 38, 38, 38, 37, 37, 36, 36, 36, 36, 35, 35, 35, 35, 34, 34,33,
+ 33, 33, 33, 32, 32, 32, 32, 31, 31, 31, 31, 30, 30, 30, 30, 29, 29, 29, 29,
+ 28, 28, 28, 28, 27, 27
+ };
+
+ if (frame->F0hz10 > 0)
+ {
+ /* T0 is 4* the number of samples in one pitch period */
+
+ kt_globals.T0 = (40 * kt_globals.samrate) / frame->F0hz10;
+
+
+ kt_globals.amp_voice = DBtoLIN(frame->AVdb_tmp);
+
+ /* Duration of period before amplitude modulation */
+
+ kt_globals.nmod = kt_globals.T0;
+ if (frame->AVdb_tmp > 0)
+ {
+ kt_globals.nmod >>= 1;
+ }
+
+ /* Breathiness of voicing waveform */
+
+ kt_globals.amp_breth = DBtoLIN(frame->Aturb) * 0.1;
+
+ /* Set open phase of glottal period where 40 <= open phase <= 263 */
+
+ kt_globals.nopen = 4 * frame->Kopen;
+
+ if ((kt_globals.glsource == IMPULSIVE) && (kt_globals.nopen > 263))
+ {
+ kt_globals.nopen = 263;
+ }
+
+ if (kt_globals.nopen >= (kt_globals.T0-1))
+ {
+// printf("Warning: glottal open period cannot exceed T0, truncated\n");
+ kt_globals.nopen = kt_globals.T0 - 2;
+ }
+
+ if (kt_globals.nopen < 40)
+ {
+ /* F0 max = 1000 Hz */
+// printf("Warning: minimum glottal open period is 10 samples.\n");
+// printf("truncated, nopen = %d\n",kt_globals.nopen);
+ kt_globals.nopen = 40;
+ }
+
+
+ /* Reset a & b, which determine shape of "natural" glottal waveform */
+
+ kt_globals.pulse_shape_b = B0[kt_globals.nopen-40];
+ kt_globals.pulse_shape_a = (kt_globals.pulse_shape_b * kt_globals.nopen) * 0.333;
+
+ /* Reset width of "impulsive" glottal pulse */
+
+ temp = kt_globals.samrate / kt_globals.nopen;
+
+ setabc((long)0,temp,&(kt_globals.rsn[RGL]));
+
+ /* Make gain at F1 about constant */
+
+ temp1 = kt_globals.nopen *.00833;
+ kt_globals.rsn[RGL].a *= temp1 * temp1;
+
+ /*
+ Truncate skewness so as not to exceed duration of closed phase
+ of glottal period.
+ */
+
+
+ temp = kt_globals.T0 - kt_globals.nopen;
+ if (frame->Kskew > temp)
+ {
+// printf("Kskew duration=%d > glottal closed period=%d, truncate\n", frame->Kskew, kt_globals.T0 - kt_globals.nopen);
+ frame->Kskew = temp;
+ }
+ if (skew >= 0)
+ {
+ skew = frame->Kskew;
+ }
+ else
+ {
+ skew = - frame->Kskew;
+ }
+
+ /* Add skewness to closed portion of voicing period */
+ kt_globals.T0 = kt_globals.T0 + skew;
+ skew = - skew;
+ }
+ else
+ {
+ kt_globals.T0 = 4; /* Default for f0 undefined */
+ kt_globals.amp_voice = 0.0;
+ kt_globals.nmod = kt_globals.T0;
+ kt_globals.amp_breth = 0.0;
+ kt_globals.pulse_shape_a = 0.0;
+ kt_globals.pulse_shape_b = 0.0;
+ }
+
+ /* Reset these pars pitch synchronously or at update rate if f0=0 */
+
+ if ((kt_globals.T0 != 4) || (kt_globals.ns == 0))
+ {
+ /* Set one-pole low-pass filter that tilts glottal source */
+
+ kt_globals.decay = (0.033 * frame->TLTdb);
+
+ if (kt_globals.decay > 0.0)
+ {
+ kt_globals.onemd = 1.0 - kt_globals.decay;
+ }
+ else
+ {
+ kt_globals.onemd = 1.0;
+ }
+ }
+}
+
+
+
+/*
+function SETABC
+
+Convert formant freqencies and bandwidth into resonator difference
+equation constants.
+*/
+
+
+static void setabc(long int f, long int bw, resonator_ptr rp)
+{
+ double r;
+ double arg;
+
+ /* Let r = exp(-pi bw t) */
+ arg = kt_globals.minus_pi_t * bw;
+ r = exp(arg);
+
+ /* Let c = -r**2 */
+ rp->c = -(r * r);
+
+ /* Let b = r * 2*cos(2 pi f t) */
+ arg = kt_globals.two_pi_t * f;
+ rp->b = r * cos(arg) * 2.0;
+
+ /* Let a = 1.0 - b - c */
+ rp->a = 1.0 - rp->b - rp->c;
+}
+
+
+/*
+function SETZEROABC
+
+Convert formant freqencies and bandwidth into anti-resonator difference
+equation constants.
+*/
+
+static void setzeroabc(long int f, long int bw, resonator_ptr rp)
+{
+ double r;
+ double arg;
+
+ f = -f;
+
+ if(f>=0)
+ {
+ f = -1;
+ }
+
+ /* First compute ordinary resonator coefficients */
+ /* Let r = exp(-pi bw t) */
+ arg = kt_globals.minus_pi_t * bw;
+ r = exp(arg);
+
+ /* Let c = -r**2 */
+ rp->c = -(r * r);
+
+ /* Let b = r * 2*cos(2 pi f t) */
+ arg = kt_globals.two_pi_t * f;
+ rp->b = r * cos(arg) * 2.;
+
+ /* Let a = 1.0 - b - c */
+ rp->a = 1.0 - rp->b - rp->c;
+
+ /* Now convert to antiresonator coefficients (a'=1/a, b'=b/a, c'=c/a) */
+ rp->a = 1.0 / rp->a;
+ rp->c *= -rp->a;
+ rp->b *= -rp->a;
+}
+
+
+/*
+function GEN_NOISE
+
+Random number generator (return a number between -8191 and +8191)
+Noise spectrum is tilted down by soft low-pass filter having a pole near
+the origin in the z-plane, i.e. output = input + (0.75 * lastoutput)
+*/
+
+
+static double gen_noise(double noise)
+{
+ long temp;
+ static double nlast;
+
+ temp = (long) getrandom(-8191,8191);
+ kt_globals.nrand = (long) temp;
+
+ noise = kt_globals.nrand + (0.75 * nlast);
+ nlast = noise;
+
+ return(noise);
+}
+
+
+/*
+function DBTOLIN
+
+Convert from decibels to a linear scale factor
+
+
+Conversion table, db to linear, 87 dB --> 32767
+ 86 dB --> 29491 (1 dB down = 0.5**1/6)
+ ...
+ 81 dB --> 16384 (6 dB down = 0.5)
+ ...
+ 0 dB --> 0
+
+The just noticeable difference for a change in intensity of a vowel
+is approximately 1 dB. Thus all amplitudes are quantized to 1 dB
+steps.
+*/
+
+
+static double DBtoLIN(long dB)
+{
+ static short amptable[88] =
+ {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 7,
+ 8, 9, 10, 11, 13, 14, 16, 18, 20, 22, 25, 28, 32,
+ 35, 40, 45, 51, 57, 64, 71, 80, 90, 101, 114, 128,
+ 142, 159, 179, 202, 227, 256, 284, 318, 359, 405,
+ 455, 512, 568, 638, 719, 881, 911, 1024, 1137, 1276,
+ 1438, 1622, 1823, 2048, 2273, 2552, 2875, 3244, 3645,
+ 4096, 4547, 5104, 5751, 6488, 7291, 8192, 9093, 10207,
+ 11502, 12976, 14582, 16384, 18350, 20644, 23429,
+ 26214, 29491, 32767 };
+
+ if ((dB < 0) || (dB > 87))
+ {
+ return(0);
+ }
+
+ return(double(amptable[dB]) * 0.001);
+}
+
+
+
+
+
+extern voice_t *wvoice;
+static wavegen_peaks_t peaks[N_PEAKS];
+static int end_wave;
+static int klattp[N_KLATTP];
+static double klattp1[N_KLATTP];
+static double klattp_inc[N_KLATTP];
+
+static int scale_wav_tab[] = {45,38,45,45}; // scale output from different voicing sources
+
+
+
+int Wavegen_Klatt(int resume)
+{//==========================
+ int pk;
+ int x;
+ int ix;
+
+ if(resume==0)
+ {
+ sample_count = 0;
+ }
+
+ while(sample_count < nsamples)
+ {
+ kt_frame.F0hz10 = (wdata.pitch * 10) / 4096;
+
+ kt_frame.Fhz[F_NP] = peaks[0].freq;
+ kt_frame.Fhz[F_NZ] = peaks[0].left;
+
+ // formants F6,F7,F8 are fixed values for cascade resonators, set in KlattInit()
+ // but F6 is used for parallel resonator
+ for(ix=1; ix<=6; ix++)
+ {
+ if(ix < 6)
+ {
+ kt_frame.Fhz[ix] = peaks[ix].freq;
+ kt_frame.Bhz[ix] = peaks[ix].height;
+ }
+ kt_frame.Bphz[ix] = peaks[ix].left;
+ kt_frame.Ap[ix] = peaks[ix].right;
+ }
+
+ kt_frame.AVdb = klattp[KLATT_AV];
+ kt_frame.AVpdb = klattp[KLATT_AVp];
+ kt_frame.AF = klattp[KLATT_Fric];
+ kt_frame.AB = klattp[KLATT_FricBP];
+ kt_frame.ASP = klattp[KLATT_Aspr];
+ kt_frame.Aturb = klattp[KLATT_Turb];
+ kt_frame.Kskew = klattp[KLATT_Skew];
+ kt_frame.TLTdb = klattp[KLATT_Tilt];
+ kt_frame.Kopen = klattp[KLATT_Kopen];
+
+ // advance formants
+ for(pk=0; pk<N_PEAKS; pk++)
+ {
+ peaks[pk].freq1 += peaks[pk].freq_inc;
+ peaks[pk].freq = (int)peaks[pk].freq1;
+ peaks[pk].height1 += peaks[pk].height_inc;
+ peaks[pk].height = (int)peaks[pk].height1;
+ peaks[pk].left1 += peaks[pk].left_inc;
+ peaks[pk].left = (int)peaks[pk].left1;
+ peaks[pk].right1 += peaks[pk].right_inc;
+ peaks[pk].right = (int)peaks[pk].right1;
+ }
+
+ for(ix=1; ix<=6; ix++)
+ {
+ kt_frame.Fhz_next[ix] = peaks[ix].freq;
+ kt_frame.Bhz_next[ix] = peaks[ix].height;
+ }
+ kt_frame.Fhz_next[F_NZ] = peaks[0].left;
+
+ for(ix=0; ix < N_KLATTP; ix++)
+ {
+ klattp1[ix] += klattp_inc[ix];
+ klattp[ix] = int(klattp1[ix]);
+ }
+
+ // advance the pitch
+ wdata.pitch_ix += wdata.pitch_inc;
+ if((ix = wdata.pitch_ix>>8) > 127) ix = 127;
+ x = wdata.pitch_env[ix] * wdata.pitch_range;
+ wdata.pitch = (x>>8) + wdata.pitch_base;
+
+ kt_globals.nspfr = (nsamples - sample_count);
+ if(kt_globals.nspfr > STEPSIZE)
+ kt_globals.nspfr = STEPSIZE;
+
+ if(parwave(&kt_frame) == 1)
+ {
+ return(1);
+ }
+ }
+
+ if(end_wave == 1)
+ {
+ // fade out to avoid a click
+ kt_globals.fadeout = 64;
+ end_wave = 0;
+ sample_count -= 64;
+ kt_globals.nspfr = 64;
+ if(parwave(&kt_frame) == 1)
+ {
+ return(1);
+ }
+ }
+
+ return(0);
+}
+
+
+void SetSynth_Klatt(int length, int modn, frame_t *fr1, frame_t *fr2, voice_t *v, int control)
+{//===========================================================================================
+ int ix;
+ DOUBLEX next;
+ int qix;
+ int cmd;
+ static frame_t prev_fr;
+
+ if(wvoice != NULL)
+ {
+ if((wvoice->klatt[0] > 0) && (wvoice->klatt[0] <=3 ))
+ {
+ kt_globals.glsource = wvoice->klatt[0];
+ kt_globals.scale_wav = scale_wav_tab[kt_globals.glsource];
+ }
+ kt_globals.f0_flutter = wvoice->flutter/32;
+ }
+
+ end_wave = 0;
+ if(control & 2)
+ {
+ end_wave = 1; // fadeout at the end
+ }
+ if(control & 1)
+ {
+ end_wave = 1;
+ for(qix=wcmdq_head+1;;qix++)
+ {
+ if(qix >= N_WCMDQ) qix = 0;
+ if(qix == wcmdq_tail) break;
+
+ cmd = wcmdq[qix][0];
+ if(cmd==WCMD_KLATT)
+ {
+ end_wave = 0; // next wave generation is from another spectrum
+ break;
+ }
+ if((cmd==WCMD_WAVE) || (cmd==WCMD_PAUSE))
+ break; // next is not from spectrum, so continue until end of wave cycle
+ }
+ }
+
+{
+//FILE *f;
+//f=fopen("klatt_log","a");
+//fprintf(f,"len %4d (%3d %4d %4d) (%3d %4d %4d)\n",length,fr1->ffreq[1],fr1->ffreq[2],fr1->ffreq[3],fr2->ffreq[1],fr2->ffreq[2],fr2->ffreq[3]);
+//fclose(f);
+}
+
+ if(control & 1)
+ {
+ if(wdata.prev_was_synth == 0)
+ {
+ // A break, not following on from another synthesized sound.
+ // Reset the synthesizer
+ //reset_resonators(&kt_globals);
+ parwave_init();
+ }
+ else
+ {
+ if((prev_fr.ffreq[1] != fr1->ffreq[1]) || (prev_fr.ffreq[2] != fr1->ffreq[2]))
+ {
+
+ // fade out to avoid a click, but only up to the end of output buffer
+ ix = (out_end - out_ptr)/2;
+ if(ix > 64)
+ ix = 64;
+ kt_globals.fadeout = ix;
+ kt_globals.nspfr = ix;
+ parwave(&kt_frame);
+
+ //reset_resonators(&kt_globals);
+ parwave_init();
+ }
+ }
+ wdata.prev_was_synth = 1;
+ memcpy(&prev_fr,fr2,sizeof(prev_fr));
+ }
+ if(fr2->frflags & FRFLAG_BREAK)
+ {
+// fr2 = fr1;
+// reset_resonators(&kt_globals);
+ }
+
+ for(ix=0; ix<N_KLATTP; ix++)
+ {
+ klattp1[ix] = klattp[ix] = fr1->klattp[ix];
+ klattp_inc[ix] = double((fr2->klattp[ix] - klattp[ix]) * STEPSIZE)/length;
+
+ if((ix>0) && (ix < KLATT_AVp))
+ klattp1[ix] = klattp[ix] = (klattp[ix] + wvoice->klatt[ix]);
+ }
+
+ nsamples = length;
+
+ for(ix=0; ix<N_PEAKS; ix++)
+ {
+ peaks[ix].freq1 = (fr1->ffreq[ix] * v->freq[ix] / 256.0) + v->freqadd[ix];
+ peaks[ix].freq = int(peaks[ix].freq1);
+ next = (fr2->ffreq[ix] * v->freq[ix] / 256.0) + v->freqadd[ix];
+ peaks[ix].freq_inc = ((next - peaks[ix].freq1) * STEPSIZE) / length; // lower headroom for fixed point math
+
+ peaks[ix].height1 = fr1->fheight[ix] * 2;
+ peaks[ix].height = int(peaks[ix].height1);
+ next = fr2->fheight[ix] * 2;
+ peaks[ix].height_inc = ((next - peaks[ix].height1) * STEPSIZE) / length;
+
+ if(ix < 6)
+ {
+ peaks[ix].left1 = fr1->fwidth[ix] * 2;
+ peaks[ix].left = int(peaks[ix].left1);
+ next = fr2->fwidth[ix] * 2;
+ peaks[ix].left_inc = ((next - peaks[ix].left1) * STEPSIZE) / length;
+
+ peaks[ix].right1 = fr1->fright[ix];
+ peaks[ix].right = int(peaks[ix].right1);
+ next = fr2->fright[ix];
+ peaks[ix].right_inc = ((next - peaks[ix].right1) * STEPSIZE) / length;
+ }
+ peaks[6].left1 = fr1->fwidth6 * 2;
+ peaks[6].left = int(peaks[6].left1);
+ next = fr2->fwidth6 * 2;
+ peaks[6].left_inc = ((next - peaks[6].left1) * STEPSIZE) / length;
+
+ peaks[6].right1 = fr1->fright6;
+ peaks[6].right = int(peaks[6].right1);
+ next = fr2->fright6;
+ peaks[6].right_inc = ((next - peaks[6].right1) * STEPSIZE) / length;
+ }
+} // end of SetSynth_Klatt
+
+
+int Wavegen_Klatt2(int length, int modulation, int resume, frame_t *fr1, frame_t *fr2)
+{//===================================================================================
+ if(resume==0)
+ SetSynth_Klatt(length, modulation, fr1, fr2, wvoice, 1);
+
+ return(Wavegen_Klatt(resume));
+}
+
+
+
+void KlattInit()
+{
+#define NUMBER_OF_SAMPLES 100
+
+ static short natural_samples[NUMBER_OF_SAMPLES]=
+ {
+ -310,-400,530,356,224,89,23,-10,-58,-16,461,599,536,701,770,
+ 605,497,461,560,404,110,224,131,104,-97,155,278,-154,-1165,
+ -598,737,125,-592,41,11,-247,-10,65,92,80,-304,71,167,-1,122,
+ 233,161,-43,278,479,485,407,266,650,134,80,236,68,260,269,179,
+ 53,140,275,293,296,104,257,152,311,182,263,245,125,314,140,44,
+ 203,230,-235,-286,23,107,92,-91,38,464,443,176,98,-784,-2449,
+ -1891,-1045,-1600,-1462,-1384,-1261,-949,-730
+ };
+ static short formant_hz[10] = {280,688,1064,2806,3260,3700,6500,7000,8000,280};
+ static short bandwidth[10] = {89,160,70,160,200,200,500,500,500,89};
+ static short parallel_amp[10] = { 0,59,59,59,59,59,59,0,0,0};
+ static short parallel_bw[10] = {59,59,89,149,200,200,500,0,0,0};
+
+ int ix;
+
+ sample_count=0;
+
+ kt_globals.synthesis_model = CASCADE_PARALLEL;
+ kt_globals.samrate = 22050;
+
+ kt_globals.glsource = IMPULSIVE; // IMPULSIVE, NATURAL, SAMPLED
+ kt_globals.scale_wav = scale_wav_tab[kt_globals.glsource];
+ kt_globals.natural_samples = natural_samples;
+ kt_globals.num_samples = NUMBER_OF_SAMPLES;
+ kt_globals.sample_factor = 3.0;
+ kt_globals.nspfr = (kt_globals.samrate * 10) / 1000;
+ kt_globals.outsl = 0;
+ kt_globals.f0_flutter = 20;
+
+ parwave_init();
+
+ // set default values for frame parameters
+ for(ix=0; ix<=9; ix++)
+ {
+ kt_frame.Fhz[ix] = formant_hz[ix];
+ kt_frame.Bhz[ix] = bandwidth[ix];
+ kt_frame.Ap[ix] = parallel_amp[ix];
+ kt_frame.Bphz[ix] = parallel_bw[ix];
+ }
+ kt_frame.Bhz_next[F_NZ] = bandwidth[F_NZ];
+
+ kt_frame.F0hz10 = 1000;
+ kt_frame.AVdb = 59; // 59
+ kt_frame.ASP = 0;
+ kt_frame.Kopen = 40; // 40
+ kt_frame.Aturb = 0;
+ kt_frame.TLTdb = 0;
+ kt_frame.AF =50;
+ kt_frame.Kskew = 0;
+ kt_frame.AB = 0;
+ kt_frame.AVpdb = 0;
+ kt_frame.Gain0 = 62;
+} // end of KlattInit
+
+#endif // INCLUDE_KLATT
diff --git a/Plugins/eSpeak/eSpeak/klatt.h b/Plugins/eSpeak/eSpeak/klatt.h
new file mode 100644
index 0000000..812385e
--- /dev/null
+++ b/Plugins/eSpeak/eSpeak/klatt.h
@@ -0,0 +1,138 @@
+
+
+#define CASCADE_PARALLEL 1 /* Type of synthesis model */
+#define ALL_PARALLEL 2
+
+#define IMPULSIVE 1 /* Type of voicing source */
+#define NATURAL 2
+#define SAMPLED 3
+
+#define PI 3.1415927
+
+
+/* typedef's that need to be exported */
+
+typedef long flag;
+
+/* Resonator Structure */
+
+typedef struct
+{
+ double a;
+ double b;
+ double c;
+ double p1;
+ double p2;
+ double a_inc;
+ double b_inc;
+ double c_inc;
+} resonator_t, *resonator_ptr;
+
+/* Structure for Klatt Globals */
+
+typedef struct
+{
+ flag synthesis_model; /* cascade-parallel or all-parallel */
+ flag outsl; /* Output waveform selector */
+ long samrate; /* Number of output samples per second */
+ long FLPhz ; /* Frequeny of glottal downsample low-pass filter */
+ long BLPhz ; /* Bandwidth of glottal downsample low-pass filter */
+ flag glsource; /* Type of glottal source */
+ int f0_flutter; /* Percentage of f0 flutter 0-100 */
+ long nspfr; /* number of samples per frame */
+ long nper; /* Counter for number of samples in a pitch period */
+ long ns;
+ long T0; /* Fundamental period in output samples times 4 */
+ long nopen; /* Number of samples in open phase of period */
+ long nmod; /* Position in period to begin noise amp. modul */
+ long nrand; /* Varible used by random number generator */
+ double pulse_shape_a; /* Makes waveshape of glottal pulse when open */
+ double pulse_shape_b; /* Makes waveshape of glottal pulse when open */
+ double minus_pi_t;
+ double two_pi_t;
+ double onemd;
+ double decay;
+ double amp_bypas; /* AB converted to linear gain */
+ double amp_voice; /* AVdb converted to linear gain */
+ double par_amp_voice; /* AVpdb converted to linear gain */
+ double amp_aspir; /* AP converted to linear gain */
+ double amp_frica; /* AF converted to linear gain */
+ double amp_breth; /* ATURB converted to linear gain */
+ double amp_gain0; /* G0 converted to linear gain */
+ int num_samples; /* number of glottal samples */
+ double sample_factor; /* multiplication factor for glottal samples */
+ short *natural_samples; /* pointer to an array of glottal samples */
+ long original_f0; /* original value of f0 not modified by flutter */
+
+ int fadeout; // set to 64 to cause fadeout over 64 samples
+ int scale_wav; // depends on the voicing source
+
+#define N_RSN 20
+#define Rnpc 0
+#define R1c 1
+#define R2c 2
+#define R3c 3
+#define R4c 4
+#define R5c 5
+#define R6c 6
+#define R7c 7
+#define R8c 8
+#define Rnz 9
+
+#define Rparallel 10
+#define Rnpp 10
+#define R1p 11
+#define R2p 12
+#define R3p 13
+#define R4p 14
+#define R5p 15
+#define R6p 16
+
+#define RGL 17
+#define RLP 18
+#define Rout 19
+
+ resonator_t rsn[N_RSN]; // internal storage for resonators
+ resonator_t rsn_next[N_RSN];
+
+} klatt_global_t, *klatt_global_ptr;
+
+/* Structure for Klatt Parameters */
+
+#define F_NP 0 // nasal zero formant
+#define F1 1
+#define F2 2
+#define F3 3
+#define F4 4
+#define F5 5
+#define F6 6
+#define F_NZ 9 // nasal pole formant
+
+
+typedef struct
+{
+ long F0hz10; /* Voicing fund freq in Hz */
+ long AVdb; /* Amp of voicing in dB, 0 to 70 */
+ int Fhz[10]; // formant Hz, F_NZ to F6 to F_NP
+ int Bhz[10];
+ int Ap[10]; /* Amp of parallel formants in dB, 0 to 80 */
+ int Bphz[10]; /* Parallel formants bw in Hz, 40 to 1000 */
+
+ long ASP; /* Amp of aspiration in dB, 0 to 70 */
+ long Kopen; /* # of samples in open period, 10 to 65 */
+ long Aturb; /* Breathiness in voicing, 0 to 80 */
+ long TLTdb; /* Voicing spectral tilt in dB, 0 to 24 */
+ long AF; /* Amp of frication in dB, 0 to 80 */
+ long Kskew; /* Skewness of alternate periods, 0 to 40 in sample#/2 */
+
+ long AB; /* Amp of bypass fric. in dB, 0 to 80 */
+ long AVpdb; /* Amp of voicing, par in dB, 0 to 70 */
+ long Gain0; /* Overall gain, 60 dB is unity, 0 to 60 */
+
+ long AVdb_tmp; //copy of AVdb, which is changed within parwave()
+ int Fhz_next[10]; // Fhz for the next chunk, so we can do interpolation of resonator (a,b,c) parameters
+ int Bhz_next[10];
+ } klatt_frame_t, *klatt_frame_ptr;
+
+
+
diff --git a/Plugins/eSpeak/eSpeak/mbrolib.h b/Plugins/eSpeak/eSpeak/mbrolib.h
new file mode 100644
index 0000000..0616b46
--- /dev/null
+++ b/Plugins/eSpeak/eSpeak/mbrolib.h
@@ -0,0 +1,205 @@
+#ifndef MBROLIB_H
+#define MBROLIB_H
+
+/*
+ * mbrolib: mbrola wrapper.
+ *
+ * Copyright (C) 2007 Gilles Casse <gcasse@oralux.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+*/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* < types */
+
+/** Parameters */
+
+typedef struct {
+ int ignore_error; /* 1=Ignore any fatal error or unknown diphone */
+ char comment_char; /* Comment character */
+ float volume_ratio; /* Volume ratio */
+ float frequency_ratio; /* Applied to pitch points */
+ float time_ratio; /* Applied to phone durations */
+} mbrolib_parameter;
+
+
+/** Returned errors */
+
+typedef enum {
+ MBROLIB_OK=0,
+ MBROLIB_DATABASE_NOT_INSTALLED,
+ MBROLIB_INVAL,
+ MBROLIB_OUT_OF_MEMORY,
+ MBROLIB_OUT_OF_RANGE,
+ MBROLIB_READ_ERROR,
+ MBROLIB_WRITE_ERROR
+} MBROLIB_ERROR;
+
+
+
+/** Gender */
+
+typedef enum {
+ MBROLIB_FEMALE,
+ MBROLIB_MALE
+} MBROLIB_GENDER;
+
+
+
+/** Voice descriptor */
+
+typedef struct {
+ char *name; /* name (for example: "en1") */
+ char *filename; /* database pathname (for example: "/usr/share/mbrola/voices/en1) */
+ int rate; /* database sample rate */
+ MBROLIB_GENDER gender;
+ const char *language; /* Language and optional dialect qualifier in ascii (e.g. en, fr_ca). */
+} mbrolib_voice;
+
+/* > */
+
+
+/** Initialization, returns a new handle.
+ First function.
+
+ @param the_sample_rate: output rate in Hz (for example 22050). If 0, keep the original database rate.
+
+ @return handle (or NULL if error).
+*/
+void* mbrolib_init( int sample_rate);
+typedef void* (t_mbrolib_init)(int);
+
+
+/** Returns the list of the installed mbrola databases.
+ The databases are searched according to the MBROLA_PATH environment variable if set,
+ or under a default path otherwise (see MBROLA_PATH in mbrolib.c).
+
+ An array of voices is returned. The last item is set to NULL.
+ The caller must not free the returned items or the array.
+
+ @param the_handle previously given by mbrolib_init.
+
+ @return An array of voices.
+*/
+const mbrolib_voice ** mbrolib_list_voices( void* the_handle);
+typedef const mbrolib_voice ** (t_mbrolib_list_voices)(void*);
+
+
+
+/** Set voice
+
+ @param the_handle.
+
+ @param the_database (for example, "en1").
+
+ @return error code (MBROLIB_OK, MBROLIB_DATABASE_NOT_INSTALLED, MBROLIB_INVAL).
+
+*/
+MBROLIB_ERROR mbrolib_set_voice( void* the_handle, const char* the_name);
+typedef MBROLIB_ERROR (t_mbrolib_set_voice)( void*, const char*);
+
+
+
+/** Get the current database parameters.
+ The caller supplies a pointer to an already allocated structure.
+
+ @param the_handle previously given by mbrolib_init.
+
+ @param the_parameters: pointer to the structure.
+
+ @return error code (MBROLIB_OK, MBROLIB_INVAL).
+*/
+MBROLIB_ERROR mbrolib_get_parameter(void* the_handle, mbrolib_parameter* the_parameter);
+typedef MBROLIB_ERROR (t_mbrolib_get_parameter)(void*, mbrolib_parameter*);
+
+
+
+/** Set the database parameters using the supplied data.
+
+ @param the_handle previously given by mbrolib_init.
+
+ @param the_parameters: pointer to the wished parameters.
+
+ @return error code (MBROLIB_OK, MBROLIB_INVAL).
+*/
+MBROLIB_ERROR mbrolib_set_parameter(void* the_handle, const mbrolib_parameter* the_parameter);
+typedef MBROLIB_ERROR (t_mbrolib_set_parameter)(void*, const mbrolib_parameter*);
+
+
+
+/** Write the mbrola phonemes in the internal buffer.
+
+ @param the_handle.
+
+ @param the_mbrola_phonemes.
+
+ @param the_size in bytes.
+
+ @return error code (MBROLIB_OK, MBROLIB_INVAL, MBROLIB_WRITE_ERROR, MBROLIB_READ_ERROR).
+*/
+MBROLIB_ERROR mbrolib_write(void* the_handle, const char* the_mbrola_phonemes, size_t the_size);
+typedef MBROLIB_ERROR (t_mbrolib_write)(void*, const char*, size_t);
+
+
+
+/** Read n bytes of the output samples.
+
+ @param the_handle.
+
+ @param the_samples (raw audio data, 16bits, mono).
+
+ @param the_size max number of int16 to read.
+
+ @param the_size number of int16 read.
+
+ @return error code (MBROLIB_OK, MBROLIB_INVAL, MBROLIB_READ_ERROR).
+
+*/
+MBROLIB_ERROR mbrolib_read(void* the_handle, short* the_samples, int the_max_size, int* the_read_size);
+typedef MBROLIB_ERROR (t_mbrolib_read)(void*, short*, int, int*);
+
+
+
+/** Flush
+
+ @param the_handle.
+
+*/
+void mbrolib_flush(void* the_handle);
+typedef void (t_mbrolib_flush)(void*);
+
+
+
+/** Release the handle
+
+ @param the_handle.
+
+ @return error code (MBROLIB_OK, MBROLIB_INVAL).
+
+*/
+MBROLIB_ERROR mbrolib_terminate(void* the_handle);
+typedef MBROLIB_ERROR (t_mbrolib_terminate)(void*);
+
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/Plugins/eSpeak/eSpeak/numbers.cpp b/Plugins/eSpeak/eSpeak/numbers.cpp
new file mode 100644
index 0000000..2b0cfd6
--- /dev/null
+++ b/Plugins/eSpeak/eSpeak/numbers.cpp
@@ -0,0 +1,1401 @@
+/***************************************************************************
+ * Copyright (C) 2005 to 2007 by Jonathan Duddington *
+ * email: jonsd@users.sourceforge.net *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 3 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, see: *
+ * <http://www.gnu.org/licenses/>. *
+ ***************************************************************************/
+
+#include "StdAfx.h"
+
+#include <stdio.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <wctype.h>
+#include <wchar.h>
+
+#include "speak_lib.h"
+#include "speech.h"
+#include "phoneme.h"
+#include "synthesize.h"
+#include "voice.h"
+#include "translate.h"
+
+
+
+#define M_NAME 0
+#define M_SMALLCAP 1
+#define M_TURNED 2
+#define M_REVERSED 3
+#define M_CURL 4
+
+#define M_ACUTE 5
+#define M_BREVE 6
+#define M_CARON 7
+#define M_CEDILLA 8
+#define M_CIRCUMFLEX 9
+#define M_DIAERESIS 10
+#define M_DOUBLE_ACUTE 11
+#define M_DOT_ABOVE 12
+#define M_GRAVE 13
+#define M_MACRON 14
+#define M_OGONEK 15
+#define M_RING 16
+#define M_STROKE 17
+#define M_TILDE 18
+
+#define M_BAR 19
+#define M_RETROFLEX 20
+#define M_HOOK 21
+
+
+#define M_MIDDLE_DOT M_DOT_ABOVE // duplicate of M_DOT_ABOVE
+#define M_IMPLOSIVE M_HOOK
+
+typedef struct {
+const char *name;
+int flags;
+} ACCENTS;
+
+// these are tokens to look up in the *_list file.
+static ACCENTS accents_tab[] = {
+{"_lig", 1},
+{"_smc", 1}, // smallcap
+{"_tur", 1}, // turned
+{"_rev", 1}, // reversed
+{"_crl", 0}, // curl
+
+{"_acu", 0}, // acute
+{"_brv", 0}, // breve
+{"_hac", 0}, // caron/hacek
+{"_ced", 0}, // cedilla
+{"_cir", 0}, // circumflex
+{"_dia", 0}, // diaeresis
+{"_ac2", 0}, // double acute
+{"_dot", 0}, // dot
+{"_grv", 0}, // grave
+{"_mcn", 0}, // macron
+{"_ogo", 0}, // ogonek
+{"_rng", 0}, // ring
+{"_stk", 0}, // stroke
+{"_tld", 0}, // tilde
+
+{"_bar", 0}, // bar
+{"_rfx", 0}, // retroflex
+{"_hok", 0}, // hook
+};
+
+
+#define CAPITAL 0
+#define LETTER(ch,mod1,mod2) (ch-59)+(mod1 << 6)+(mod2 << 11)
+#define LIGATURE(ch1,ch2,mod1) (ch1-59)+((ch2-59) << 6)+(mod1 << 12)+0x8000
+
+
+#define L_ALPHA 60 // U+3B1
+#define L_SCHWA 61 // U+259
+#define L_OPEN_E 62 // U+25B
+#define L_GAMMA 63 // U+3B3
+#define L_IOTA 64 // U+3B9
+#define L_OE 65 // U+153
+#define L_OMEGA 66 // U+3C9
+
+#define L_PHI 67 // U+3C6
+#define L_ESH 68 // U+283
+#define L_UPSILON 69 // U+3C5
+#define L_EZH 70 // U+292
+#define L_GLOTTAL 71 // U+294
+#define L_RTAP 72 // U+27E
+
+
+static const short non_ascii_tab[] = {
+ 0, 0x3b1, 0x259, 0x25b, 0x3b3, 0x3b9, 0x153, 0x3c9,
+0x3c6, 0x283, 0x3c5, 0x292, 0x294, 0x27e };
+
+
+// characters U+00e0 to U+017f
+static const unsigned short letter_accents_0e0[] = {
+LETTER('a',M_GRAVE,0), // U+00e0
+LETTER('a',M_ACUTE,0),
+LETTER('a',M_CIRCUMFLEX,0),
+LETTER('a',M_TILDE,0),
+LETTER('a',M_DIAERESIS,0),
+LETTER('a',M_RING,0),
+LIGATURE('a','e',0),
+LETTER('c',M_CEDILLA,0),
+LETTER('e',M_GRAVE,0),
+LETTER('e',M_ACUTE,0),
+LETTER('e',M_CIRCUMFLEX,0),
+LETTER('e',M_DIAERESIS,0),
+LETTER('i',M_GRAVE,0),
+LETTER('i',M_ACUTE,0),
+LETTER('i',M_CIRCUMFLEX,0),
+LETTER('i',M_DIAERESIS,0),
+LETTER('d',M_NAME,0), // eth // U+00f0
+LETTER('n',M_TILDE,0),
+LETTER('o',M_GRAVE,0),
+LETTER('o',M_ACUTE,0),
+LETTER('o',M_CIRCUMFLEX,0),
+LETTER('o',M_TILDE,0),
+LETTER('o',M_DIAERESIS,0),
+0, // division sign
+LETTER('o',M_STROKE,0),
+LETTER('u',M_GRAVE,0),
+LETTER('u',M_ACUTE,0),
+LETTER('u',M_CIRCUMFLEX,0),
+LETTER('u',M_DIAERESIS,0),
+LETTER('y',M_ACUTE,0),
+LETTER('t',M_NAME,0), // thorn
+LETTER('y',M_DIAERESIS,0),
+CAPITAL, // U+0100
+LETTER('a',M_MACRON,0),
+CAPITAL,
+LETTER('a',M_BREVE,0),
+CAPITAL,
+LETTER('a',M_OGONEK,0),
+CAPITAL,
+LETTER('c',M_ACUTE,0),
+CAPITAL,
+LETTER('c',M_CIRCUMFLEX,0),
+CAPITAL,
+LETTER('c',M_DOT_ABOVE,0),
+CAPITAL,
+LETTER('c',M_CARON,0),
+CAPITAL,
+LETTER('d',M_CARON,0),
+CAPITAL, // U+0110
+LETTER('d',M_STROKE,0),
+CAPITAL,
+LETTER('e',M_MACRON,0),
+CAPITAL,
+LETTER('e',M_BREVE,0),
+CAPITAL,
+LETTER('e',M_DOT_ABOVE,0),
+CAPITAL,
+LETTER('e',M_OGONEK,0),
+CAPITAL,
+LETTER('e',M_CARON,0),
+CAPITAL,
+LETTER('g',M_CIRCUMFLEX,0),
+CAPITAL,
+LETTER('g',M_BREVE,0),
+CAPITAL, // U+0120
+LETTER('g',M_DOT_ABOVE,0),
+CAPITAL,
+LETTER('g',M_CEDILLA,0),
+CAPITAL,
+LETTER('h',M_CIRCUMFLEX,0),
+CAPITAL,
+LETTER('h',M_STROKE,0),
+CAPITAL,
+LETTER('i',M_TILDE,0),
+CAPITAL,
+LETTER('i',M_MACRON,0),
+CAPITAL,
+LETTER('i',M_BREVE,0),
+CAPITAL,
+LETTER('i',M_OGONEK,0),
+CAPITAL, // U+0130
+LETTER('i',M_NAME,0), // dotless i
+CAPITAL,
+LIGATURE('i','j',0),
+CAPITAL,
+LETTER('j',M_CIRCUMFLEX,0),
+CAPITAL,
+LETTER('k',M_CEDILLA,0),
+LETTER('k',M_NAME,0), // kra
+CAPITAL,
+LETTER('l',M_ACUTE,0),
+CAPITAL,
+LETTER('l',M_CEDILLA,0),
+CAPITAL,
+LETTER('l',M_CARON,0),
+CAPITAL,
+LETTER('l',M_MIDDLE_DOT,0), // U+0140
+CAPITAL,
+LETTER('l',M_STROKE,0),
+CAPITAL,
+LETTER('n',M_ACUTE,0),
+CAPITAL,
+LETTER('n',M_CEDILLA,0),
+CAPITAL,
+LETTER('n',M_CARON,0),
+LETTER('n',M_NAME,0), // apostrophe n
+CAPITAL,
+LETTER('n',M_NAME,0), // eng
+CAPITAL,
+LETTER('o',M_MACRON,0),
+CAPITAL,
+LETTER('o',M_BREVE,0),
+CAPITAL, // U+0150
+LETTER('o',M_DOUBLE_ACUTE,0),
+CAPITAL,
+LIGATURE('o','e',0),
+CAPITAL,
+LETTER('r',M_ACUTE,0),
+CAPITAL,
+LETTER('r',M_CEDILLA,0),
+CAPITAL,
+LETTER('r',M_CARON,0),
+CAPITAL,
+LETTER('s',M_ACUTE,0),
+CAPITAL,
+LETTER('s',M_CIRCUMFLEX,0),
+CAPITAL,
+LETTER('s',M_CEDILLA,0),
+CAPITAL, // U+0160
+LETTER('s',M_CARON,0),
+CAPITAL,
+LETTER('t',M_CEDILLA,0),
+CAPITAL,
+LETTER('t',M_CARON,0),
+CAPITAL,
+LETTER('t',M_STROKE,0),
+CAPITAL,
+LETTER('u',M_TILDE,0),
+CAPITAL,
+LETTER('u',M_MACRON,0),
+CAPITAL,
+LETTER('u',M_BREVE,0),
+CAPITAL,
+LETTER('u',M_RING,0),
+CAPITAL, // U+0170
+LETTER('u',M_DOUBLE_ACUTE,0),
+CAPITAL,
+LETTER('u',M_OGONEK,0),
+CAPITAL,
+LETTER('w',M_CIRCUMFLEX,0),
+CAPITAL,
+LETTER('y',M_CIRCUMFLEX,0),
+CAPITAL, // Y-DIAERESIS
+CAPITAL,
+LETTER('z',M_ACUTE,0),
+CAPITAL,
+LETTER('z',M_DOT_ABOVE,0),
+CAPITAL,
+LETTER('z',M_CARON,0),
+LETTER('s',M_NAME,0), // long-s // U+17f
+};
+
+
+// characters U+0250 to U+029F
+static const unsigned short letter_accents_250[] = {
+LETTER('a',M_TURNED,0), // U+250
+LETTER(L_ALPHA,0,0),
+LETTER(L_ALPHA,M_TURNED,0),
+LETTER('b',M_IMPLOSIVE,0),
+0, // open-o
+LETTER('c',M_CURL,0),
+LETTER('d',M_RETROFLEX,0),
+LETTER('d',M_IMPLOSIVE,0),
+LETTER('e',M_REVERSED,0), // U+258
+0, // schwa
+LETTER(L_SCHWA,M_HOOK,0),
+0, // open-e
+LETTER(L_OPEN_E,M_REVERSED,0),
+LETTER(L_OPEN_E,M_HOOK,M_REVERSED),
+0,//LETTER(L_OPEN_E,M_CLOSED,M_REVERSED),
+LETTER('j',M_BAR,0),
+LETTER('g',M_IMPLOSIVE,0), // U+260
+LETTER('g',0,0),
+LETTER('g',M_SMALLCAP,0),
+LETTER(L_GAMMA,0,0),
+0, // ramshorn
+LETTER('h',M_TURNED,0),
+LETTER('h',M_HOOK,0),
+0,//LETTER(L_HENG,M_HOOK,0),
+LETTER('i',M_BAR,0), // U+268
+LETTER(L_IOTA,0,0),
+LETTER('i',M_SMALLCAP,0),
+LETTER('l',M_TILDE,0),
+LETTER('l',M_BAR,0),
+LETTER('l',M_RETROFLEX,0),
+LIGATURE('l','z',0),
+LETTER('m',M_TURNED,0),
+0,//LETTER('m',M_TURNED,M_LEG), // U+270
+LETTER('m',M_HOOK,0),
+0,//LETTER('n',M_LEFTHOOK,0),
+LETTER('n',M_RETROFLEX,0),
+LETTER('n',M_SMALLCAP,0),
+LETTER('o',M_BAR,0),
+LIGATURE('o','e',M_SMALLCAP),
+0,//LETTER(L_OMEGA,M_CLOSED,0),
+LETTER(L_PHI,0,0), // U+278
+LETTER('r',M_TURNED,0),
+0,//LETTER('r',M_TURNED,M_LEG),
+LETTER('r',M_RETROFLEX,M_TURNED),
+0,//LETTER('r',M_LEG,0),
+LETTER('r',M_RETROFLEX,0),
+0, // r-tap
+LETTER(L_RTAP,M_REVERSED,0),
+LETTER('r',M_SMALLCAP,0), // U+280
+LETTER('r',M_TURNED,M_SMALLCAP),
+LETTER('s',M_RETROFLEX,0),
+0, // esh
+0,//LETTER('j',M_BAR,L_IMPLOSIVE),
+LETTER(L_ESH,M_REVERSED,0),
+LETTER(L_ESH,M_CURL,0),
+LETTER('t',M_TURNED,0),
+LETTER('t',M_RETROFLEX,0), // U+288
+LETTER('u',M_BAR,0),
+LETTER(L_UPSILON,0,0),
+LETTER('v',M_HOOK,0),
+LETTER('v',M_TURNED,0),
+LETTER('w',M_TURNED,0),
+LETTER('y',M_TURNED,0),
+LETTER('y',M_SMALLCAP,0),
+LETTER('z',M_RETROFLEX,0), // U+290
+LETTER('z',M_CURL,0),
+0, // ezh
+LETTER(L_EZH,M_CURL,0),
+0, // glottal stop
+LETTER(L_GLOTTAL,M_REVERSED,0),
+LETTER(L_GLOTTAL,M_TURNED,0),
+0,//LETTER('c',M_LONG,0),
+0, // bilabial click // U+298
+LETTER('b',M_SMALLCAP,0),
+0,//LETTER(L_OPEN_E,M_CLOSED,0),
+LETTER('g',M_IMPLOSIVE,M_SMALLCAP),
+LETTER('h',M_SMALLCAP,0),
+LETTER('j',M_CURL,0),
+LETTER('k',M_TURNED,0),
+LETTER('l',M_SMALLCAP,0),
+LETTER('q',M_HOOK,0), // U+2a0
+LETTER(L_GLOTTAL,M_STROKE,0),
+LETTER(L_GLOTTAL,M_STROKE,M_REVERSED),
+LIGATURE('d','z',0),
+0, // dezh
+LIGATURE('d','z',M_CURL),
+LIGATURE('t','s',0),
+0, // tesh
+LIGATURE('t','s',M_CURL),
+};
+
+static int LookupLetter2(Translator *tr, unsigned int letter, char *ph_buf)
+{//========================================================================
+ int len;
+ char single_letter[10];
+
+ single_letter[0] = 0;
+ single_letter[1] = '_';
+ len = utf8_out(letter, &single_letter[2]);
+ single_letter[len+2] = ' ';
+ single_letter[len+3] = 0;
+
+ if(Lookup(tr, &single_letter[1], ph_buf) == 0)
+ {
+ single_letter[1] = ' ';
+ if(Lookup(tr, &single_letter[2], ph_buf) == 0)
+ {
+ TranslateRules(tr, &single_letter[2], ph_buf, 20, NULL,0,NULL);
+ }
+ }
+ return(ph_buf[0]);
+}
+
+
+void LookupAccentedLetter(Translator *tr, unsigned int letter, char *ph_buf)
+{//=========================================================================
+ // lookup the character in the accents table
+ int accent_data = 0;
+ int accent1 = 0;
+ int accent2 = 0;
+ int basic_letter;
+ int letter2=0;
+ char ph_letter1[30];
+ char ph_letter2[30];
+ char ph_accent1[30];
+ char ph_accent2[30];
+
+ ph_accent2[0] = 0;
+
+ if((letter >= 0xe0) && (letter < 0x17f))
+ {
+ accent_data = letter_accents_0e0[letter - 0xe0];
+ }
+ else
+ if((letter >= 0x250) && (letter <= 0x2a8))
+ {
+ accent_data = letter_accents_250[letter - 0x250];
+ }
+
+ if(accent_data != 0)
+ {
+ basic_letter = (accent_data & 0x3f) + 59;
+ if(basic_letter < 'a')
+ basic_letter = non_ascii_tab[basic_letter-59];
+
+ if(accent_data & 0x8000)
+ {
+ letter2 = (accent_data >> 6) & 0x3f;
+ letter2 += 59;
+ accent2 = (accent_data >> 12) & 0x7;
+ }
+ else
+ {
+ accent1 = (accent_data >> 6) & 0x1f;
+ accent2 = (accent_data >> 11) & 0xf;
+ }
+
+
+ if(Lookup(tr, accents_tab[accent1].name, ph_accent1) != 0)
+ {
+
+ if(LookupLetter2(tr, basic_letter, ph_letter1) != 0)
+ {
+ if(accent2 != 0)
+ {
+ if(Lookup(tr, accents_tab[accent2].name, ph_accent2) == 0)
+ {
+// break;
+ }
+
+ if(accents_tab[accent2].flags & 1)
+ {
+ strcpy(ph_buf,ph_accent2);
+ ph_buf += strlen(ph_buf);
+ ph_accent2[0] = 0;
+ }
+ }
+ if(letter2 != 0)
+ {
+ //ligature
+ LookupLetter2(tr, letter2, ph_letter2);
+ sprintf(ph_buf,"%s%c%s%c%s%s",ph_accent1, phonPAUSE_VSHORT, ph_letter1, phonSTRESS_P, ph_letter2, ph_accent2);
+ }
+ else
+ {
+ if(accent1 == 0)
+ strcpy(ph_buf, ph_letter1);
+ else
+ if((tr->langopts.accents & 1) || (accents_tab[accent1].flags & 1))
+ sprintf(ph_buf,"%s%c%c%s", ph_accent1, phonPAUSE_VSHORT, phonSTRESS_P, ph_letter1);
+ else
+ sprintf(ph_buf,"%c%s%c%s%c", phonSTRESS_2, ph_letter1, phonPAUSE_VSHORT, ph_accent1, phonPAUSE_VSHORT);
+ }
+ }
+ }
+ }
+} // end of LookupAccentedLetter
+
+
+
+void LookupLetter(Translator *tr, unsigned int letter, int next_byte, char *ph_buf1)
+{//=================================================================================
+ int len;
+ unsigned char *p;
+ static char single_letter[10] = {0,0};
+ char ph_stress[2];
+ unsigned int dict_flags[2];
+ char ph_buf3[40];
+ char *ptr;
+
+ ph_buf1[0] = 0;
+ len = utf8_out(letter,&single_letter[2]);
+ single_letter[len+2] = ' ';
+
+ if(next_byte == -1)
+ {
+ // speaking normal text, not individual characters
+ if(Lookup(tr, &single_letter[2], ph_buf1) != 0)
+ return;
+
+ single_letter[1] = '_';
+ if(Lookup(tr, &single_letter[1], ph_buf3) != 0)
+ return; // the character is specified as _* so ignore it when speaking normal text
+
+ // check whether this character is specified for English
+ if(tr->translator_name == L('e','n'))
+ return; // we are already using English
+
+ SetTranslator2("en");
+ if(Lookup(translator2, &single_letter[2], ph_buf3) != 0)
+ {
+ // yes, switch to English and re-translate the word
+ sprintf(ph_buf1,"%c",phonSWITCH);
+ }
+ SelectPhonemeTable(voice->phoneme_tab_ix); // revert to original phoneme table
+ return;
+ }
+
+ if((letter <= 32) || iswspace(letter))
+ {
+ // lookup space as _&32 etc.
+ sprintf(&single_letter[1],"_#%d ",letter);
+ Lookup(tr, &single_letter[1], ph_buf1);
+ return;
+ }
+
+ if(next_byte != ' ')
+ next_byte = RULE_SPELLING;
+ single_letter[3+len] = next_byte; // follow by space-space if the end of the word, or space-0x31
+
+ single_letter[1] = '_';
+
+ // if the $accent flag is set for this letter, use the accents table (below)
+ dict_flags[1] = 0;
+ ptr = &single_letter[1];
+
+ if(Lookup(tr, &single_letter[1], ph_buf3) == 0)
+ {
+ single_letter[1] = ' ';
+ if(Lookup(tr, &single_letter[2], ph_buf3) == 0)
+ {
+ TranslateRules(tr, &single_letter[2], ph_buf3, sizeof(ph_buf3), NULL,FLAG_NO_TRACE,NULL);
+ }
+ }
+
+ if(ph_buf3[0] == 0)
+ {
+ LookupAccentedLetter(tr, letter, ph_buf3);
+ }
+
+ if(ph_buf3[0] == 0)
+ {
+ ph_buf1[0] = 0;
+ return;
+ }
+ if(ph_buf3[0] == phonSWITCH)
+ {
+ strcpy(ph_buf1,ph_buf3);
+ return;
+ }
+ // at a stress marker at the start of the letter name, unless one is already marked
+ ph_stress[0] = phonSTRESS_P;
+ ph_stress[1] = 0;
+
+ for(p=(unsigned char *)ph_buf3; *p != 0; p++)
+ {
+ if(phoneme_tab[*p]->type == phSTRESS)
+ ph_stress[0] = 0; // stress is already marked
+ }
+ sprintf(ph_buf1,"%s%s",ph_stress,ph_buf3);
+}
+
+
+
+int TranslateLetter(Translator *tr, char *word, char *phonemes, int control, int word_length)
+{//======================================================================================
+// get pronunciation for an isolated letter
+// return number of bytes used by the letter
+// control 2=say-as glyphs, 3-say-as chars
+ int n_bytes;
+ int letter;
+ int len;
+ int save_option_phonemes;
+ char *p2;
+ char *pbuf;
+ char capital[20];
+ char ph_buf[60];
+ char ph_buf2[60];
+ char hexbuf[6];
+
+ ph_buf[0] = 0;
+ capital[0] = 0;
+
+ n_bytes = utf8_in(&letter,word);
+
+ if((letter & 0xfff00) == 0x0e000)
+ {
+ letter &= 0xff; // uncode private usage area
+ }
+
+ if(control > 2)
+ {
+ // include CAPITAL information
+ if(iswupper(letter))
+ {
+ Lookup(tr, "_cap", capital);
+ }
+ }
+ letter = towlower2(letter);
+
+ LookupLetter(tr, letter, word[n_bytes], ph_buf);
+
+ if(ph_buf[0] == phonSWITCH)
+ {
+ strcpy(phonemes,ph_buf);
+ return(0);
+ }
+
+ if((ph_buf[0] == 0) && (tr->translator_name != L('e','n')))
+ {
+ // speak as English, check whether there is a translation for this character
+ SetTranslator2("en");
+ save_option_phonemes = option_phonemes;
+ option_phonemes = 0;
+ LookupLetter(translator2, letter, word[n_bytes], ph_buf);
+ SelectPhonemeTable(voice->phoneme_tab_ix); // revert to original phoneme table
+ option_phonemes = save_option_phonemes;
+
+ if(ph_buf[0] != 0)
+ {
+ sprintf(phonemes,"%cen",phonSWITCH);
+ return(0);
+ }
+ }
+
+ if(ph_buf[0] == 0)
+ {
+ // character name not found
+ if(iswalpha(letter))
+ Lookup(tr, "_?A", ph_buf);
+
+ if((ph_buf[0]==0) && !iswspace(letter))
+ Lookup(tr, "_??", ph_buf);
+
+ if(ph_buf[0] != 0)
+ {
+ // speak the hexadecimal number of the character code
+ sprintf(hexbuf,"%x",letter);
+ pbuf = ph_buf;
+ for(p2 = hexbuf; *p2 != 0; p2++)
+ {
+ pbuf += strlen(pbuf);
+ *pbuf++ = phonPAUSE_VSHORT;
+ LookupLetter(tr, *p2, 0, pbuf);
+ }
+ }
+ }
+
+ len = strlen(phonemes);
+ if(tr->langopts.accents & 2)
+ sprintf(ph_buf2,"%c%s%s",0xff,ph_buf,capital);
+ else
+ sprintf(ph_buf2,"%c%s%s",0xff,capital,ph_buf); // the 0xff marker will be removed or replaced in SetSpellingStress()
+ if((len + strlen(ph_buf2)) < N_WORD_PHONEMES)
+ {
+ strcpy(&phonemes[len],ph_buf2);
+ }
+ return(n_bytes);
+} // end of TranslateLetter
+
+
+
+void SetSpellingStress(Translator *tr, char *phonemes, int control, int n_chars)
+{//=============================================================================
+// Individual letter names, reduce the stress of some.
+ int ix;
+ unsigned int c;
+ int n_stress=0;
+ int count;
+ unsigned char buf[N_WORD_PHONEMES];
+
+ for(ix=0; (c = phonemes[ix]) != 0; ix++)
+ {
+ if(c == phonSTRESS_P)
+ {
+ n_stress++;
+ }
+ buf[ix] = c;
+ }
+ buf[ix] = 0;
+
+ count = 0;
+ for(ix=0; (c = buf[ix]) != 0; ix++)
+ {
+ if((c == phonSTRESS_P) && (n_chars > 1))
+ {
+ count++;
+
+ if(tr->langopts.spelling_stress == 1)
+ {
+ // stress on initial letter when spelling
+ if(count > 1)
+ c = phonSTRESS_3;
+ }
+ else
+ {
+ if(count != n_stress)
+ {
+ if(((count % 3) != 0) || (count == n_stress-1))
+ c = phonSTRESS_3; // reduce to secondary stress
+ }
+ }
+ }
+ else
+ if(c == 0xff)
+ {
+ if((control < 2) || (ix==0))
+ continue; // don't insert pauses
+
+ if(control == 4)
+ c = phonPAUSE; // pause after each character
+ if(((count % 3) == 0) || (control > 2))
+ c = phonPAUSE_SHORT; // pause following a primary stress
+ else
+ continue; // remove marker
+ }
+ *phonemes++ = c;
+ }
+ if(control >= 2)
+ *phonemes++ = phonPAUSE_NOLINK;
+ *phonemes = 0;
+} // end of SetSpellingStress
+
+
+
+
+int TranslateRoman(Translator *tr, char *word, char *ph_out)
+{//=====================================================
+ int c;
+ char *p;
+ const char *p2;
+ int acc;
+ int prev;
+ int value;
+ int subtract;
+ int repeat = 0;
+ unsigned int flags;
+ char ph_roman[30];
+ char number_chars[N_WORD_BYTES];
+
+ static const char *roman_numbers = "ixcmvld";
+ static int roman_values[] = {1,10,100,1000,5,50,500};
+
+ acc = 0;
+ prev = 0;
+ subtract = 0x7fff;
+
+ while((c = *word++) != ' ')
+ {
+ if((p2 = strchr(roman_numbers,c)) == NULL)
+ return(0);
+
+ value = roman_values[p2 - roman_numbers];
+ if(value == prev)
+ {
+ repeat++;
+ if(repeat >= 3)
+ return(0);
+ }
+ else
+ repeat = 0;
+
+ if((prev > 1) && (prev != 10) && (prev != 100))
+ {
+ if(value >= prev)
+ return(0);
+ }
+ if((prev != 0) && (prev < value))
+ {
+ if(((acc % 10) != 0) || ((prev*10) < value))
+ return(0);
+ subtract = prev;
+ value -= subtract;
+ }
+ else
+ if(value >= subtract)
+ return(0);
+ else
+ acc += prev;
+ prev = value;
+ }
+ acc += prev;
+ if(acc < 2)
+ return(0);
+
+ if(acc > tr->langopts.max_roman)
+ return(0);
+
+ Lookup(tr, "_roman",ph_roman); // precede by "roman" if _rom is defined in *_list
+ p = &ph_out[0];
+
+ if((tr->langopts.numbers & NUM_ROMAN_AFTER) == 0)
+ {
+ strcpy(ph_out,ph_roman);
+ p = &ph_out[strlen(ph_out)];
+ }
+
+ sprintf(number_chars," %d ",acc);
+ TranslateNumber(tr, &number_chars[1], p, &flags, 0);
+
+ if(tr->langopts.numbers & NUM_ROMAN_AFTER)
+ strcat(ph_out,ph_roman);
+ return(1);
+} // end of TranslateRoman
+
+
+static const char *M_Variant(int value)
+{//====================================
+ // returns M, or perhaps MA for some cases
+
+ if((translator->langopts.numbers2 & 0x100) && (value >= 2) && (value <= 4))
+ return("0MA"); // Czech, Slovak
+ else
+ if(((value % 100) < 10) || ((value % 100) > 20)) // but not teens, 10 to 19
+ {
+ if ((translator->langopts.numbers2 & 0x40) &&
+ ((value % 10)>=2) &&
+ ((value % 10)<=4))
+ {
+ // for Polish language - two forms of plural!
+ return("0MA");
+ }
+
+ if((translator->langopts.numbers2 & 0x80) &&
+ ((value % 10)==1))
+ {
+ return("1MA");
+ }
+
+ }
+ return("0M");
+}
+
+
+static int LookupThousands(Translator *tr, int value, int thousandplex, char *ph_out)
+{//==================================================================================
+ int found;
+ char string[12];
+ char ph_of[12];
+ char ph_thousands[40];
+
+ ph_of[0] = 0;
+
+ // first look fora match with the exact value of thousands
+ sprintf(string,"_%dM%d",value,thousandplex);
+
+ if((found = Lookup(tr, string, ph_thousands)) == 0)
+ {
+ if((value % 100) >= 20)
+ {
+ Lookup(tr, "_0of", ph_of);
+ }
+
+ sprintf(string,"_%s%d",M_Variant(value),thousandplex);
+
+ if(Lookup(tr, string, ph_thousands) == 0)
+ {
+ // repeat "thousand" if higher order names are not available
+ sprintf(string,"_%dM1",value);
+ if((found = Lookup(tr, string, ph_thousands)) == 0)
+ Lookup(tr, "_0M1", ph_thousands);
+ }
+ }
+ sprintf(ph_out,"%s%s",ph_of,ph_thousands);
+ return(found);
+}
+
+
+static int LookupNum2(Translator *tr, int value, int control, char *ph_out)
+{//========================================================================
+// Lookup a 2 digit number
+// control bit 0: use special form of '1'
+// control bit 2: use feminine form of '2'
+
+ int found;
+ int ix;
+ int units;
+ int used_and=0;
+ int next_phtype;
+ char string[12]; // for looking up entries in de_list
+ char ph_tens[50];
+ char ph_digits[50];
+ char ph_and[12];
+
+ if((value == 1) && (control & 1))
+ {
+ if(Lookup(tr, "_1a", ph_out) != 0)
+ return(0);
+ }
+ // is there a special pronunciation for this 2-digit number
+ found = 0;
+ if(control & 4)
+ {
+ sprintf(string,"_%df",value);
+ found = Lookup(tr, string, ph_digits);
+ }
+ if(found == 0)
+ {
+ sprintf(string,"_%d",value);
+ found = Lookup(tr, string, ph_digits);
+ }
+
+ // no, speak as tens+units
+ if((control & 2) && (value < 10))
+ {
+ // speak leading zero
+ Lookup(tr, "_0", ph_tens);
+ }
+ else
+ {
+ if(found)
+ {
+ strcpy(ph_out,ph_digits);
+ return(0);
+ }
+
+ if((value % 10) == 0)
+ {
+ sprintf(string,"_%d0",value / 10);
+ found = Lookup(tr, string, ph_tens);
+ }
+ if(!found)
+ {
+ sprintf(string,"_%dX",value / 10);
+ Lookup(tr, string, ph_tens);
+ }
+
+ if((value % 10) == 0)
+ {
+ strcpy(ph_out,ph_tens);
+ return(0);
+ }
+
+ found = 0;
+ units = (value % 10);
+ if(control & 4)
+ {
+ // is there a variant form of this number?
+ sprintf(string,"_%df",units);
+ found = Lookup(tr, string, ph_digits);
+ }
+ if(found == 0)
+ {
+ sprintf(string,"_%d",units);
+ Lookup(tr, string, ph_digits);
+ }
+ }
+
+ if(tr->langopts.numbers & 0x30)
+ {
+ Lookup(tr, "_0and", ph_and);
+ if(tr->langopts.numbers & 0x10)
+ sprintf(ph_out,"%s%s%s",ph_digits,ph_and,ph_tens);
+ else
+ sprintf(ph_out,"%s%s%s",ph_tens,ph_and,ph_digits);
+ used_and = 1;
+ }
+ else
+ {
+ if(tr->langopts.numbers & 0x200)
+ {
+ // remove vowel from the end of tens if units starts with a vowel (LANG=Italian)
+ if((ix = strlen(ph_tens)-1) >= 0)
+ {
+ if((next_phtype = phoneme_tab[(unsigned int)(ph_digits[0])]->type) == phSTRESS)
+ next_phtype = phoneme_tab[(unsigned int)(ph_digits[1])]->type;
+
+ if((phoneme_tab[(unsigned int)(ph_tens[ix])]->type == phVOWEL) && (next_phtype == phVOWEL))
+ ph_tens[ix] = 0;
+ }
+ }
+ sprintf(ph_out,"%s%s",ph_tens,ph_digits);
+ }
+
+ if(tr->langopts.numbers & 0x100)
+ {
+ // only one primary stress
+ found = 0;
+ for(ix=strlen(ph_out)-1; ix>=0; ix--)
+ {
+ if(ph_out[ix] == phonSTRESS_P)
+ {
+ if(found)
+ ph_out[ix] = phonSTRESS_3;
+ else
+ found = 1;
+ }
+ }
+ }
+ return(used_and);
+} // end of LookupNum2
+
+
+static int LookupNum3(Translator *tr, int value, char *ph_out, int suppress_null, int thousandplex, int prev_thousands)
+{//====================================================================================================================
+// Translate a 3 digit number
+ int found;
+ int hundreds;
+ int x;
+ char string[12]; // for looking up entries in **_list
+ char buf1[100];
+ char buf2[100];
+ char ph_100[20];
+ char ph_10T[20];
+ char ph_digits[50];
+ char ph_thousands[50];
+ char ph_hundred_and[12];
+ char ph_thousand_and[12];
+
+ hundreds = value / 100;
+ buf1[0] = 0;
+
+ if(hundreds > 0)
+ {
+ ph_thousands[0] = 0;
+ ph_thousand_and[0] = 0;
+
+ Lookup(tr, "_0C", ph_100);
+
+ if(((tr->langopts.numbers & 0x0800) != 0) && (hundreds == 19))
+ {
+ // speak numbers such as 1984 as years: nineteen-eighty-four
+// ph_100[0] = 0; // don't say "hundred", we also need to surpess "and"
+ }
+ else
+ if(hundreds >= 10)
+ {
+ ph_digits[0] = 0;
+
+ if(LookupThousands(tr, hundreds / 10, thousandplex+1, ph_10T) == 0)
+ {
+ x = 0;
+ if(tr->langopts.numbers2 & (1 << (thousandplex+1)))
+ x = 4;
+ LookupNum2(tr, hundreds/10, x, ph_digits);
+ }
+
+ if(tr->langopts.numbers2 & 0x200)
+ sprintf(ph_thousands,"%s%s%c",ph_10T,ph_digits,phonPAUSE_NOLINK); // say "thousands" before its number, not after
+ else
+ sprintf(ph_thousands,"%s%s%c",ph_digits,ph_10T,phonPAUSE_NOLINK);
+
+ hundreds %= 10;
+ if(hundreds == 0)
+ ph_100[0] = 0;
+ suppress_null = 1;
+ }
+
+ ph_digits[0] = 0;
+ if(hundreds > 0)
+ {
+ if((tr->langopts.numbers & 0x100000) && (prev_thousands || (ph_thousands[0] != 0)))
+ {
+ Lookup(tr, "_0and", ph_thousand_and);
+ }
+
+ suppress_null = 1;
+
+ found = 0;
+ if((value % 1000) == 100)
+ {
+ // is there a special pronunciation for exactly 100 ?
+ found = Lookup(tr, "_1C0", ph_digits);
+ }
+ if(!found)
+ {
+ sprintf(string,"_%dC",hundreds);
+ found = Lookup(tr, string, ph_digits); // is there a specific pronunciation for n-hundred ?
+ }
+
+ if(found)
+ {
+ ph_100[0] = 0;
+ }
+ else
+ {
+ if((hundreds > 1) || ((tr->langopts.numbers & 0x400) == 0))
+ {
+ LookupNum2(tr, hundreds, 0, ph_digits);
+ }
+ }
+ }
+
+ sprintf(buf1,"%s%s%s%s",ph_thousands,ph_thousand_and,ph_digits,ph_100);
+ }
+
+ ph_hundred_and[0] = 0;
+ if((tr->langopts.numbers & 0x40) && ((value % 100) != 0))
+ {
+ if((value > 100) || (prev_thousands && (thousandplex==0)))
+ {
+ Lookup(tr, "_0and", ph_hundred_and);
+ }
+ }
+
+
+ buf2[0] = 0;
+ value = value % 100;
+
+ if(value == 0)
+ {
+ if(suppress_null == 0)
+ Lookup(tr, "_0", buf2);
+ }
+ else
+ {
+ x = 0;
+ if(thousandplex==0)
+ x = 1; // allow "eins" for 1 rather than "ein"
+ else
+ {
+ if(tr->langopts.numbers2 & (1 << thousandplex))
+ x = 4; // use variant (feminine) for before thousands and millions
+ }
+
+ if(LookupNum2(tr, value, x, buf2) != 0)
+ {
+ if(tr->langopts.numbers & 0x80)
+ ph_hundred_and[0] = 0; // don't put 'and' after 'hundred' if there's 'and' between tens and units
+ }
+ }
+
+ sprintf(ph_out,"%s%s%s",buf1,ph_hundred_and,buf2);
+
+ return(0);
+} // end of LookupNum3
+
+
+
+static int TranslateNumber_1(Translator *tr, char *word, char *ph_out, unsigned int *flags, int wflags)
+{//====================================================================================================
+// Number translation with various options
+// the "word" may be up to 4 digits
+// "words" of 3 digits may be preceded by another number "word" for thousands or millions
+
+ int n_digits;
+ int value;
+ int ix;
+ unsigned char c;
+ int suppress_null = 0;
+ int decimal_point = 0;
+ int thousandplex = 0;
+ int thousands_inc = 0;
+ int prev_thousands = 0;
+ int this_value;
+ static int prev_value;
+ int decimal_count;
+ int max_decimal_count;
+ char string[12]; // for looking up entries in de_list
+ char buf1[100];
+ char ph_append[50];
+ char ph_buf[200];
+ char ph_buf2[50];
+
+ static const char str_pause[2] = {phonPAUSE_NOLINK,0};
+
+ for(ix=0; isdigit(word[ix]); ix++) ;
+ n_digits = ix;
+ value = this_value = atoi(word);
+
+ ph_append[0] = 0;
+ ph_buf2[0] = 0;
+
+ // is there a previous thousands part (as a previous "word") ?
+ if((n_digits == 3) && (word[-2] == tr->langopts.thousands_sep) && isdigit(word[-3]))
+ {
+ prev_thousands = 1;
+ }
+ else
+ if((tr->langopts.thousands_sep == ' ') || (tr->langopts.numbers & 0x1000))
+ {
+ // thousands groups can be separated by spaces
+ if((n_digits == 3) && isdigit(word[-2]))
+ {
+ prev_thousands = 1;
+ }
+ }
+
+ if((word[0] == '0') && (prev_thousands == 0) && (word[1] != tr->langopts.decimal_sep))
+ {
+ if((n_digits == 2) && (word[3] == ':') && isdigit(word[5]) && isspace(word[7]))
+ {
+ // looks like a time 02:30, omit the leading zero
+ }
+ else
+ {
+ return(0); // number string with leading zero, speak as individual digits
+ }
+ }
+
+ if((tr->langopts.numbers & 0x1000) && (word[n_digits] == ' '))
+ thousands_inc = 1;
+ else
+ if(word[n_digits] == tr->langopts.thousands_sep)
+ thousands_inc = 2;
+
+ if(thousands_inc > 0)
+ {
+ // if the following "words" are three-digit groups, count them and add
+ // a "thousand"/"million" suffix to this one
+
+ ix = n_digits + thousands_inc;
+ while(isdigit(word[ix]) && isdigit(word[ix+1]) && isdigit(word[ix+2]))
+ {
+ thousandplex++;
+ if(word[ix+3] == tr->langopts.thousands_sep)
+ ix += (3 + thousands_inc);
+ else
+ break;
+ }
+ }
+
+ if((value == 0) && prev_thousands)
+ {
+ suppress_null = 1;
+ }
+
+ if((word[n_digits] == tr->langopts.decimal_sep) && isdigit(word[n_digits+1]))
+ {
+ // this "word" ends with a decimal point
+ Lookup(tr, "_dpt", ph_append);
+ decimal_point = 1;
+ }
+ else
+ if(suppress_null == 0)
+ {
+ if(thousands_inc > 0)
+ {
+ if((thousandplex > 0) && (value < 1000))
+ {
+ if((suppress_null == 0) && (LookupThousands(tr,value,thousandplex,ph_append)))
+ {
+ // found an exact match for N thousand
+ value = 0;
+ suppress_null = 1;
+ }
+ }
+ }
+ }
+ else
+ if((thousandplex > 1) && prev_thousands && (prev_value > 0))
+ {
+ sprintf(string,"_%s%d",M_Variant(value),thousandplex+1);
+ if(Lookup(tr, string, buf1)==0)
+ {
+ // speak this thousandplex if there was no word for the previous thousandplex
+ sprintf(string,"_0M%d",thousandplex);
+ Lookup(tr, string, ph_append);
+ }
+ }
+
+ if((ph_append[0] == 0) && (word[n_digits] == '.') && (thousandplex == 0))
+ {
+ Lookup(tr, "_.", ph_append);
+ }
+
+ LookupNum3(tr, value, ph_buf, suppress_null, thousandplex, prev_thousands);
+ if((thousandplex > 0) && (tr->langopts.numbers2 & 0x200))
+ sprintf(ph_out,"%s%s%s",ph_append,ph_buf2,ph_buf); // say "thousands" before its number
+ else
+ sprintf(ph_out,"%s%s%s",ph_buf2,ph_buf,ph_append);
+
+
+ while(decimal_point)
+ {
+ n_digits++;
+
+ decimal_count = 0;
+ while(isdigit(word[n_digits+decimal_count]))
+ decimal_count++;
+
+ if(decimal_count > 1)
+ {
+ max_decimal_count = 2;
+ switch(tr->langopts.numbers & 0xe000)
+ {
+ case 0x8000:
+ max_decimal_count = 5;
+ case 0x4000:
+ // French/Polish decimal fraction
+ while(word[n_digits] == '0')
+ {
+ Lookup(tr, "_0", buf1);
+ strcat(ph_out,buf1);
+ decimal_count--;
+ n_digits++;
+ }
+ if((decimal_count <= max_decimal_count) && isdigit(word[n_digits]))
+ {
+ LookupNum3(tr, atoi(&word[n_digits]), buf1, 0,0,0);
+ strcat(ph_out,buf1);
+ n_digits += decimal_count;
+ }
+ break;
+
+ case 0x2000:
+ // Italian decimal fractions
+ if((decimal_count < 4) || ((decimal_count==4) && (word[n_digits] != '0')))
+ {
+ LookupNum3(tr, atoi(&word[n_digits]), buf1, 0,0,0);
+ strcat(ph_out,buf1);
+ if(word[n_digits]=='0')
+ {
+ // decimal part has leading zeros, so add a "hundredths" or "thousandths" suffix
+ sprintf(string,"_0Z%d",decimal_count);
+ Lookup(tr, string, buf1);
+ strcat(ph_out,buf1);
+ }
+ n_digits += decimal_count;
+ }
+ break;
+
+ case 0x6000:
+ // Romanian decimal fractions
+ if((decimal_count <= 4) && (word[n_digits] != '0'))
+ {
+ LookupNum3(tr, atoi(&word[n_digits]), buf1, 0,0,0);
+ strcat(ph_out,buf1);
+ n_digits += decimal_count;
+ }
+ break;
+ }
+ }
+
+ while(isdigit(c = word[n_digits]) && (strlen(ph_out) < (N_WORD_PHONEMES - 10)))
+ {
+ value = word[n_digits++] - '0';
+ LookupNum2(tr, value, 1, buf1);
+ strcat(ph_out,buf1);
+ }
+
+ // something after the decimal part ?
+ if(Lookup(tr, "_dpt2", buf1))
+ strcat(ph_out,buf1);
+
+ if((c == tr->langopts.decimal_sep) && isdigit(word[n_digits+1]))
+ {
+ Lookup(tr, "_dpt", buf1);
+ strcat(ph_out,buf1);
+ }
+ else
+ {
+ decimal_point = 0;
+ }
+ }
+ if((ph_out[0] != 0) && (ph_out[0] != phonSWITCH))
+ {
+ int next_char;
+ char *p;
+ p = &word[n_digits+1];
+
+ p += utf8_in(&next_char,p);
+ if((tr->langopts.numbers & NUM_NOPAUSE) && (next_char == ' '))
+ utf8_in(&next_char,p);
+
+ if(!iswalpha(next_char))
+ strcat(ph_out,str_pause); // don't add pause for 100s, 6th, etc.
+ }
+
+ *flags = FLAG_FOUND;
+ prev_value = this_value;
+ return(1);
+} // end of TranslateNumber_1
+
+
+
+int TranslateNumber(Translator *tr, char *word1, char *ph_out, unsigned int *flags, int wflags)
+{//============================================================================================
+ if(option_sayas == SAYAS_DIGITS1)
+ return(0); // speak digits individually
+
+ if((tr->langopts.numbers & 0x3) == 1)
+ return(TranslateNumber_1(tr, word1, ph_out, flags, wflags));
+
+ return(0);
+} // end of TranslateNumber
+
diff --git a/Plugins/eSpeak/eSpeak/phoneme.h b/Plugins/eSpeak/eSpeak/phoneme.h
new file mode 100644
index 0000000..596f457
--- /dev/null
+++ b/Plugins/eSpeak/eSpeak/phoneme.h
@@ -0,0 +1,168 @@
+/***************************************************************************
+ * Copyright (C) 2005 to 2007 by Jonathan Duddington *
+ * email: jonsd@users.sourceforge.net *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 3 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, see: *
+ * <http://www.gnu.org/licenses/>. *
+ ***************************************************************************/
+
+
+
+// phoneme types
+#define phPAUSE 0
+#define phSTRESS 1
+#define phVOWEL 2
+#define phLIQUID 3
+#define phSTOP 4
+#define phVSTOP 5
+#define phFRICATIVE 6
+#define phVFRICATIVE 7
+#define phNASAL 8
+#define phVIRTUAL 9
+#define phDELETED 14
+#define phINVALID 15
+
+
+// phoneme properties
+// bits 16-19 give place of articulation (not currently used)
+#define phWAVE 0x01
+#define phUNSTRESSED 0x02
+#define phFORTIS 0x08
+#define phVOICED 0x10
+#define phSIBILANT 0x20
+#define phNOLINK 0x40
+#define phTRILL 0x80
+#define phVOWEL2 0x100 // liquid that is considered a vowel
+#define phPALATAL 0x200
+#define phAPPENDPH 0x2000 // always insert another phoneme (link_out) after this one
+#define phBRKAFTER 0x4000 // [*] add a post-pause
+#define phBEFOREPAUSE 0x8000 // replace with the link_out phoneme if the next phoneme is a pause
+
+#define phALTERNATIVE 0x1c00 // bits 10,11,12 specifying use of alternative_ph
+#define phBEFOREVOWEL 0x0000
+#define phBEFOREVOWELPAUSE 0x0400
+#define phBEFORENOTVOWEL 0x0c00
+#define phBEFORENOTVOWEL2 0x1000
+#define phSWITCHVOICING 0x0800
+#define phBEFORE_R 0x1400
+
+#define phNONSYLLABIC 0x100000 // don't count this vowel as a syllable when finding the stress position
+#define phLONG 0x200000
+#define phLENGTHENSTOP 0x400000 // make the pre-pause slightly longer
+#define phRHOTIC 0x800000
+
+// fixed phoneme code numbers, these can be used from the program code
+#define phonCONTROL 1
+#define phonSTRESS_U 2
+#define phonSTRESS_D 3
+#define phonSTRESS_2 4
+#define phonSTRESS_3 5
+#define phonSTRESS_P 6
+#define phonSTRESS_P2 7 // priority stress within a word
+#define phonSTRESS_PREV 8
+#define phonPAUSE 9
+#define phonPAUSE_SHORT 10
+#define phonPAUSE_NOLINK 11
+#define phonLENGTHEN 12
+#define phonSCHWA 13
+#define phonSCHWA_SHORT 14
+#define phonEND_WORD 15
+#define phonSONORANT 16
+#define phonDEFAULTTONE 17
+#define phonCAPITAL 18
+#define phonGLOTTALSTOP 19
+#define phonSYLLABIC 20
+#define phonSWITCH 21
+#define phonX1 22 // a language specific action
+#define phonPAUSE_VSHORT 23
+#define phonPAUSE_LONG 24
+#define phonT_REDUCED 25
+#define phonSTRESS_TONIC 26
+#define phonPAUSE_CLAUSE 27
+
+extern const unsigned char pause_phonemes[8]; // 0, vshort, short, pause, long, glottalstop
+
+// place of articulation
+#define phPLACE 0xf0000
+#define phPLACE_pla 0x60000
+
+#define N_PHONEME_TABS 100 // number of phoneme tables
+#define N_PHONEME_TAB 256 // max phonemes in a phoneme table
+#define N_PHONEME_TAB_NAME 32 // must be multiple of 4
+
+// main table of phonemes, index by phoneme number (1-254)
+typedef struct {
+ unsigned int mnemonic; // 1st char is in the l.s.byte
+ unsigned int phflags; // bits 28-30 reduce_to level, bits 16-19 place of articulation
+ // bits 10-11 alternative ph control
+
+ unsigned short std_length; // for vowels, in mS; for phSTRESS, the stress/tone type
+ unsigned short spect;
+ unsigned short before;
+ unsigned short after;
+
+ unsigned char code; // the phoneme number
+ unsigned char type; // phVOWEL, phPAUSE, phSTOP etc
+ unsigned char start_type;
+ unsigned char end_type;
+
+ unsigned char length_mod; // a length_mod group number, used to access length_mod_tab
+ unsigned char reduce_to; // change to this phoneme if unstressed
+ unsigned char alternative_ph; // change to this phoneme if a vowel follows/doesn't follow
+ unsigned char link_out; // insert linking phoneme if a vowel follows
+
+} PHONEME_TAB;
+
+
+// Several phoneme tables may be loaded into memory. phoneme_tab points to
+// one for the current voice
+extern int n_phoneme_tab;
+extern int current_phoneme_table;
+extern PHONEME_TAB *phoneme_tab[N_PHONEME_TAB];
+extern unsigned char phoneme_tab_flags[N_PHONEME_TAB]; // bit 0: not inherited
+
+typedef struct {
+ char name[N_PHONEME_TAB_NAME];
+ PHONEME_TAB *phoneme_tab_ptr;
+ int n_phonemes;
+ int includes; // also include the phonemes from this other phoneme table
+} PHONEME_TAB_LIST;
+
+
+
+// table of phonemes to be replaced with different phonemes, for the current voice
+#define N_REPLACE_PHONEMES 60
+typedef struct {
+ unsigned char old_ph;
+ unsigned char new_ph;
+ char type; // 0=always replace, 1=only at end of word
+} REPLACE_PHONEMES;
+
+extern int n_replace_phonemes;
+extern REPLACE_PHONEMES replace_phonemes[N_REPLACE_PHONEMES];
+
+
+#define PH(c1,c2) (c2<<8)+c1 // combine two characters into an integer for phoneme name
+#define PH3(c1,c2,c3) (c3<<16)+(c2<<8)+c1
+#define PhonemeCode2(c1,c2) PhonemeCode((c2<<8)+c1)
+int LookupPhonemeString(const char *string);
+int PhonemeCode(unsigned int mnem);
+
+char *EncodePhonemes(char *p, char *outptr, unsigned char *bad_phoneme);
+void DecodePhonemes(const char *inptr, char *outptr);
+
+extern const char *WordToString(unsigned int word);
+
+extern PHONEME_TAB_LIST phoneme_tab_list[N_PHONEME_TABS];
+extern int phoneme_tab_number;
diff --git a/Plugins/eSpeak/eSpeak/phonemelist.cpp b/Plugins/eSpeak/eSpeak/phonemelist.cpp
new file mode 100644
index 0000000..7f1c65e
--- /dev/null
+++ b/Plugins/eSpeak/eSpeak/phonemelist.cpp
@@ -0,0 +1,687 @@
+/***************************************************************************
+ * Copyright (C) 2005 to 2007 by Jonathan Duddington *
+ * email: jonsd@users.sourceforge.net *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 3 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, see: *
+ * <http://www.gnu.org/licenses/>. *
+ ***************************************************************************/
+
+#include "StdAfx.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "speak_lib.h"
+#include "speech.h"
+#include "phoneme.h"
+#include "synthesize.h"
+#include "translate.h"
+
+
+const unsigned char pause_phonemes[8] = {0, phonPAUSE_VSHORT, phonPAUSE_SHORT, phonPAUSE, phonPAUSE_LONG, phonGLOTTALSTOP, phonPAUSE_LONG, phonPAUSE_LONG};
+
+
+extern int n_ph_list2;
+extern PHONEME_LIST2 ph_list2[N_PHONEME_LIST]; // first stage of text->phonemes
+
+
+
+static int ChangePhonemes(Translator *tr, PHONEME_LIST2 *phlist, int n_ph, int index, PHONEME_TAB *ph, CHANGEPH *ch)
+{//=================================================================================================================
+// Called for each phoneme in the phoneme list, to allow a language to make changes
+// ph The current phoneme
+
+ if(tr->translator_name == L('r','u'))
+ return(ChangePhonemes_ru(tr, phlist, n_ph, index, ph, ch));
+
+ return(0);
+}
+
+
+static int SubstitutePhonemes(Translator *tr, PHONEME_LIST2 *plist_out)
+{//====================================================================
+// Copy the phonemes list and perform any substitutions that are required for the
+// current voice
+ int ix;
+ int k;
+ int replace_flags;
+ int n_plist_out = 0;
+ int word_end;
+ int max_stress = -1;
+ int switched_language = 0;
+ int max_stress_posn=0;
+ int n_syllables = 0;
+ int syllable = 0;
+ int syllable_stressed = 0;
+ PHONEME_LIST2 *plist2;
+ PHONEME_LIST2 *pl;
+ PHONEME_TAB *next=NULL;
+
+ for(ix=0; (ix < n_ph_list2) && (n_plist_out < N_PHONEME_LIST); ix++)
+ {
+ plist2 = &ph_list2[ix];
+
+ if(plist2->phcode == phonSWITCH)
+ switched_language ^= 1;
+
+ // don't do any substitution if the language has been temporarily changed
+ if(switched_language == 0)
+ {
+ if(ix < (n_ph_list2 -1))
+ next = phoneme_tab[ph_list2[ix+1].phcode];
+
+ word_end = 0;
+ if((plist2+1)->sourceix || ((next != 0) && (next->type == phPAUSE)))
+ word_end = 1; // this phoneme is the end of a word
+
+ if(tr->langopts.phoneme_change != 0)
+ {
+ // this language does changes to phonemes after translation
+
+ if(plist2->sourceix)
+ {
+ // start of a word, find the stressed vowel
+ syllable = 0;
+ syllable_stressed = 0;
+ n_syllables = 0;
+
+ max_stress = -1;
+ max_stress_posn = ix;
+ for(k=ix; k < n_ph_list2; k++)
+ {
+ if(((pl = &ph_list2[k])->sourceix != 0) && (k > ix))
+ break;
+
+ pl->stress &= 0xf;
+
+ if(phoneme_tab[pl->phcode]->type == phVOWEL)
+ {
+ n_syllables++;
+
+ if(pl->stress > max_stress)
+ {
+ syllable_stressed = n_syllables;
+ max_stress = pl->stress;
+ max_stress_posn = k;
+ }
+ }
+ }
+ }
+
+ if(phoneme_tab[plist2->phcode]->type == phVOWEL)
+ {
+ syllable++;
+ }
+
+ // make any language specific changes
+ int flags;
+ CHANGEPH ch;
+ flags = 0;
+ if(ix == max_stress_posn)
+ flags |= 2;
+ if(ix > max_stress_posn)
+ flags |= 4;
+ if(ph_list2[ix].synthflags & SFLAG_DICTIONARY)
+ flags |= 8;
+ ch.flags = flags | word_end;
+
+ ch.stress = plist2->stress;
+ ch.stress_highest = max_stress;
+ ch.n_vowels = n_syllables;
+ ch.vowel_this = syllable;
+ ch.vowel_stressed = syllable_stressed;
+
+ ChangePhonemes(tr, ph_list2, n_ph_list2, ix, phoneme_tab[ph_list2[ix].phcode], &ch);
+ }
+
+ // check whether a Voice has specified that we should replace this phoneme
+ for(k=0; k<n_replace_phonemes; k++)
+ {
+ if(plist2->phcode == replace_phonemes[k].old_ph)
+ {
+ replace_flags = replace_phonemes[k].type;
+
+ if((replace_flags & 1) && (word_end == 0))
+ continue; // this replacement only occurs at the end of a word
+
+ if((replace_flags & 2) && ((plist2->stress & 0x7) > 3))
+ continue; // this replacement doesn't occur in stressed syllables
+
+ // substitute the replacement phoneme
+ plist2->phcode = replace_phonemes[k].new_ph;
+ if((plist2->stress > 1) && (phoneme_tab[plist2->phcode]->phflags & phUNSTRESSED))
+ plist2->stress = 0; // the replacement must be unstressed
+ break;
+ }
+ }
+
+ if(plist2->phcode == 0)
+ {
+ continue; // phoneme has been replaced by NULL, so don't copy it
+ }
+ }
+
+ // copy phoneme into the output list
+ memcpy(&plist_out[n_plist_out++],plist2,sizeof(PHONEME_LIST2));
+ }
+ return(n_plist_out);
+} // end of SubstitutePhonemes
+
+
+
+void MakePhonemeList(Translator *tr, int post_pause, int start_sentence)
+{//=====================================================================
+
+ int ix=0;
+ int j;
+ int insert_ph = 0;
+ PHONEME_LIST *phlist;
+ PHONEME_TAB *ph;
+ PHONEME_TAB *prev, *next, *next2;
+ int unstress_count = 0;
+ int word_stress = 0;
+ int switched_language = 0;
+ int max_stress;
+ int voicing;
+ int regression;
+ int end_sourceix;
+ int alternative;
+ int first_vowel=0; // first vowel in a word
+ PHONEME_LIST2 ph_list3[N_PHONEME_LIST];
+
+ static PHONEME_LIST2 ph_list2_null = {0,0,0,0,0};
+ PHONEME_LIST2 *plist2 = &ph_list2_null;
+ PHONEME_LIST2 *plist2_inserted = NULL;
+
+ plist2 = ph_list2;
+ phlist = phoneme_list;
+ end_sourceix = plist2[n_ph_list2-1].sourceix;
+
+ // is the last word of the clause unstressed ?
+ max_stress = 0;
+ for(j = n_ph_list2-3; j>=0; j--)
+ {
+ // start with the last phoneme (before the terminating pauses) and move forwards
+ if((plist2[j].stress & 0x7f) > max_stress)
+ max_stress = plist2[j].stress & 0x7f;
+ if(plist2[j].sourceix != 0)
+ break;
+ }
+ if(max_stress < 4)
+ {
+ // the last word is unstressed, look for a previous word that can be stressed
+ while(--j >= 0)
+ {
+ if(plist2[j].synthflags & SFLAG_PROMOTE_STRESS) // dictionary flags indicated that this stress can be promoted
+ {
+ plist2[j].stress = 4; // promote to stressed
+ break;
+ }
+ if(plist2[j].stress >= 4)
+ {
+ // found a stressed syllable, so stop looking
+ break;
+ }
+ }
+ }
+
+ if((regression = tr->langopts.param[LOPT_REGRESSIVE_VOICING]) != 0)
+ {
+ // set consonant clusters to all voiced or all unvoiced
+ // Regressive
+ int type;
+ voicing = 0;
+
+ for(j=n_ph_list2-1; j>=0; j--)
+ {
+ ph = phoneme_tab[plist2[j].phcode];
+ if(ph == NULL)
+ continue;
+
+ if(ph->code == phonSWITCH)
+ switched_language ^= 1;
+ if(switched_language)
+ continue;
+
+ type = ph->type;
+
+ if(regression & 0x2)
+ {
+ // LANG=Russian, [v] amd [v;] don't cause regression, or [R^]
+ if((ph->mnemonic == 'v') || (ph->mnemonic == ((';'<<8)+'v')) || ((ph->mnemonic & 0xff)== 'R'))
+ type = phLIQUID;
+ }
+
+ if((type==phSTOP) || type==(phFRICATIVE))
+ {
+ if(voicing==0)
+ {
+ voicing = 1;
+ }
+ else
+ if((voicing==2) && ((ph->phflags & phALTERNATIVE)==phSWITCHVOICING))
+ {
+ plist2[j].phcode = ph->alternative_ph; // change to voiced equivalent
+ }
+ }
+ else
+ if((type==phVSTOP) || type==(phVFRICATIVE))
+ {
+ if(voicing==0)
+ {
+ voicing = 2;
+ }
+ else
+ if((voicing==1) && ((ph->phflags & phALTERNATIVE)==phSWITCHVOICING))
+ {
+ plist2[j].phcode = ph->alternative_ph; // change to unvoiced equivalent
+ }
+ }
+ else
+ {
+ if(regression & 0x8)
+ {
+ // LANG=Polish, propagate through liquids and nasals
+ if((type == phPAUSE) || (type == phVOWEL))
+ voicing = 0;
+ }
+ else
+ {
+ voicing = 0;
+ }
+ }
+ if((regression & 0x4) && (plist2[j].sourceix))
+ {
+ // stop propagation at a word boundary
+ voicing = 0;
+ }
+ }
+ }
+
+ n_ph_list2 = SubstitutePhonemes(tr,ph_list3) - 2;
+
+ // transfer all the phonemes of the clause into phoneme_list
+ ph = phoneme_tab[phonPAUSE];
+ switched_language = 0;
+
+ for(j=0; insert_ph || ((j < n_ph_list2) && (ix < N_PHONEME_LIST-3)); j++)
+ {
+ prev = ph;
+
+ plist2 = &ph_list3[j];
+
+ if(insert_ph != 0)
+ {
+ // we have a (linking) phoneme which we need to insert here
+ next = phoneme_tab[plist2->phcode]; // this phoneme, i.e. after the insert
+
+ // re-use the previous entry for the inserted phoneme.
+ // That's OK because we don't look backwards from plist2
+ j--;
+ plist2 = plist2_inserted = &ph_list3[j];
+ memset(plist2, 0, sizeof(*plist2));
+ plist2->phcode = insert_ph;
+ ph = phoneme_tab[insert_ph];
+ insert_ph = 0;
+ }
+ else
+ {
+ // otherwise get the next phoneme from the list
+ ph = phoneme_tab[plist2->phcode];
+
+ if(plist2->phcode == phonSWITCH)
+ {
+ // change phoneme table
+ SelectPhonemeTable(plist2->tone_number);
+ switched_language ^= SFLAG_SWITCHED_LANG;
+ }
+ next = phoneme_tab[(plist2+1)->phcode]; // the phoneme after this one
+ }
+
+ if(plist2->sourceix)
+ {
+ // start of a word
+ int k;
+ word_stress = 0;
+ first_vowel = 1;
+
+ // find the highest stress level in this word
+ for(k=j+1; k < n_ph_list2; k++)
+ {
+ if(ph_list3[k].sourceix)
+ break; // start of the next word
+
+ if(ph_list3[k].stress > word_stress)
+ word_stress = ph_list3[k].stress;
+ }
+ }
+
+ if(ph == NULL) continue;
+
+ if(ph->type == phVOWEL)
+ {
+ // check for consecutive unstressed syllables
+ if(plist2->stress == 0)
+ {
+ // an unstressed vowel
+ unstress_count++;
+ if((unstress_count > 1) && ((unstress_count & 1)==0))
+ {
+ // in a sequence of unstressed syllables, reduce alternate syllables to 'diminished'
+ // stress. But not for the last phoneme of a stressed word
+ if((tr->langopts.stress_flags & 0x2) || ((word_stress > 3) && ((plist2+1)->sourceix!=0)))
+ {
+ // An unstressed final vowel of a stressed word
+ unstress_count=1; // try again for next syllable
+ }
+ else
+ {
+ plist2->stress = 1; // change stress to 'diminished'
+ }
+ }
+ }
+ else
+ {
+ unstress_count = 0;
+ }
+ }
+
+ alternative = 0;
+
+ if(ph->alternative_ph > 0)
+ {
+ switch(ph->phflags & phALTERNATIVE)
+ {
+ // This phoneme changes if vowel follows, or doesn't follow, depending on its phNOTFOLLOWS flag
+ case phBEFORENOTVOWEL:
+ if(next->type != phVOWEL)
+ alternative = ph->alternative_ph;
+ break;
+
+ case phBEFORENOTVOWEL2: // LANG=tr
+ if(((plist2+1)->sourceix != 0) ||
+ ((next->type != phVOWEL) && ((phoneme_tab[(plist2+2)->phcode]->type != phVOWEL) || ((plist2+2)->sourceix != 0))))
+ {
+ alternative = ph->alternative_ph;
+ }
+ break;
+
+ case phBEFOREVOWELPAUSE:
+ if((next->type == phVOWEL) || (next->type == phPAUSE))
+ alternative = ph->alternative_ph;
+ break;
+
+ case phBEFOREVOWEL:
+ if(next->type == phVOWEL)
+ alternative = ph->alternative_ph;
+ break;
+
+ case phBEFORE_R:
+ if(next->phflags & phRHOTIC)
+ {
+ alternative = ph->alternative_ph;
+ }
+ break;
+ }
+ }
+ if(ph->phflags & phBEFOREPAUSE)
+ {
+ if(next->type == phPAUSE)
+ alternative = ph->link_out; // replace with the link_out phoneme
+ }
+
+ if(alternative == 1)
+ continue; // NULL phoneme, discard
+
+ if(alternative > 1)
+ {
+ PHONEME_TAB *ph2;
+ ph2 = ph;
+ ph = phoneme_tab[alternative];
+
+ if(ph->type == phVOWEL)
+ {
+ plist2->synthflags |= SFLAG_SYLLABLE;
+ if(ph2->type != phVOWEL)
+ plist2->stress = 0; // change from non-vowel to vowel, make sure it's unstressed
+ }
+ else
+ plist2->synthflags &= ~SFLAG_SYLLABLE;
+ }
+
+ if(tr->langopts.param[LOPT_REDUCE_T])
+ {
+ if((ph->mnemonic == 't') && (plist2->sourceix == 0) && ((prev->type == phVOWEL) || (prev->mnemonic == 'n')))
+ {
+ if(((plist2+1)->sourceix == 0) && ((plist2+1)->stress < 3) && (next->type == phVOWEL))
+ {
+ ph = phoneme_tab[phonT_REDUCED];
+ }
+ }
+ }
+
+
+ while((ph->reduce_to != 0) && (!(plist2->synthflags & SFLAG_DICTIONARY) || (tr->langopts.param[LOPT_REDUCE] & 1)))
+ {
+ int reduce_level;
+ int stress_level;
+ int reduce = 0;
+
+ reduce_level = (ph->phflags >> 28) & 7;
+
+ if(ph->type == phVOWEL)
+ {
+ stress_level = plist2->stress;
+ }
+ else
+ {
+ // consonant, get stress from the following vowel
+ if(next->type == phVOWEL)
+ stress_level = (plist2+1)->stress;
+ else
+ break;
+ }
+
+ if((stress_level == 1) && (first_vowel))
+ stress_level = 0; // ignore 'dimished' stress on first syllable
+
+ if(stress_level == 1)
+ reduce = 1; // stress = 'reduced'
+
+ if(stress_level < reduce_level)
+ reduce =1;
+
+ if((word_stress < 4) && (tr->langopts.param[LOPT_REDUCE] & 0x2) && (stress_level >= word_stress))
+ {
+ // don't reduce the most stressed syllable in an unstressed word
+ reduce = 0;
+ }
+
+ if(reduce)
+ ph = phoneme_tab[ph->reduce_to];
+ else
+ break;
+ }
+
+ if(ph->type == phVOWEL)
+ first_vowel = 0;
+
+ if((plist2+1)->synthflags & SFLAG_LENGTHEN)
+ {
+ static char types_double[] = {phFRICATIVE,phVFRICATIVE,phNASAL,phLIQUID,0};
+ if(strchr(types_double,next->type))
+ {
+ // lengthen this consonant by doubling it
+ insert_ph = next->code;
+ (plist2+1)->synthflags ^= SFLAG_LENGTHEN;
+ }
+ }
+
+ if((plist2+1)->sourceix != 0)
+ {
+ int x;
+
+ if(tr->langopts.vowel_pause && (ph->type != phPAUSE))
+ {
+
+ if((ph->type != phVOWEL) && (tr->langopts.vowel_pause & 0x200))
+ {
+ // add a pause after a word which ends in a consonant
+ insert_ph = phonPAUSE_NOLINK;
+ }
+
+ if(next->type == phVOWEL)
+ {
+ if((x = tr->langopts.vowel_pause & 0x0c) != 0)
+ {
+ // break before a word which starts with a vowel
+ if(x == 0xc)
+ insert_ph = phonPAUSE_NOLINK;
+ else
+ insert_ph = phonPAUSE_VSHORT;
+ }
+
+ if((ph->type == phVOWEL) && ((x = tr->langopts.vowel_pause & 0x03) != 0))
+ {
+ // adjacent vowels over a word boundary
+ if(x == 2)
+ insert_ph = phonPAUSE_SHORT;
+ else
+ insert_ph = phonPAUSE_VSHORT;
+ }
+
+ if(((plist2+1)->stress >= 4) && (tr->langopts.vowel_pause & 0x100))
+ {
+ // pause before a words which starts with a stressed vowel
+ insert_ph = phonPAUSE_SHORT;
+ }
+ }
+ }
+
+ if(plist2 != plist2_inserted)
+ {
+ if((x = (tr->langopts.word_gap & 0x7)) != 0)
+ {
+ if((x > 1) || ((insert_ph != phonPAUSE_SHORT) && (insert_ph != phonPAUSE_NOLINK)))
+ {
+ // don't reduce the pause
+ insert_ph = pause_phonemes[x];
+ }
+ }
+ if(option_wordgap > 0)
+ {
+ insert_ph = phonPAUSE_LONG;
+ }
+ }
+ }
+
+ next2 = phoneme_tab[(plist2+2)->phcode];
+
+ if((insert_ph == 0) && (ph->link_out != 0) && !(ph->phflags & phBEFOREPAUSE) && (((plist2+1)->synthflags & SFLAG_EMBEDDED)==0))
+ {
+ if(ph->phflags & phAPPENDPH)
+ {
+ // always append the specified phoneme, unless it already is the next phoneme
+ if((ph->link_out != (plist2+1)->phcode) && (next->type == phVOWEL))
+// if(ph->link_out != (plist2+1)->phcode)
+ {
+ insert_ph = ph->link_out;
+ }
+ }
+ else
+ if(((tr->langopts.word_gap & 8)==0) || ((plist2+1)->sourceix == 0))
+ {
+ // This phoneme can be linked to a following vowel by inserting a linking phoneme
+ if(next->type == phVOWEL)
+ insert_ph = ph->link_out;
+ else
+ if(next->code == phonPAUSE_SHORT)
+ {
+ // Pause followed by Vowel, replace the Short Pause with the linking phoneme,
+ if(next2->type == phVOWEL)
+ (plist2+1)->phcode = ph->link_out; // replace pause by linking phoneme
+ }
+ }
+ }
+
+ if(ph->phflags & phVOICED)
+ {
+ // check that a voiced consonant is preceded or followed by a vowel or liquid
+ // and if not, add a short schwa
+
+ // not yet implemented
+ }
+
+ phlist[ix].ph = ph;
+ phlist[ix].type = ph->type;
+ phlist[ix].env = PITCHfall; // default, can be changed in the "intonation" module
+ phlist[ix].synthflags = plist2->synthflags | switched_language;
+ phlist[ix].tone = plist2->stress & 0xf;
+ phlist[ix].tone_ph = plist2->tone_number;
+ phlist[ix].sourceix = 0;
+
+ if(plist2->sourceix != 0)
+ {
+ phlist[ix].sourceix = plist2->sourceix;
+ phlist[ix].newword = 1; // this phoneme is the start of a word
+
+ if(start_sentence)
+ {
+ phlist[ix].newword = 5; // start of sentence + start of word
+ start_sentence = 0;
+ }
+ }
+ else
+ {
+ phlist[ix].newword = 0;
+ }
+
+ phlist[ix].length = ph->std_length;
+ if((ph->code == phonPAUSE_LONG) && (option_wordgap > 0))
+ {
+ phlist[ix].ph = phoneme_tab[phonPAUSE_SHORT];
+ phlist[ix].length = option_wordgap*14; // 10mS per unit at the default speed
+ }
+
+ if(ph->type==phVOWEL || ph->type==phLIQUID || ph->type==phNASAL || ph->type==phVSTOP || ph->type==phVFRICATIVE)
+ {
+ phlist[ix].length = 128; // length_mod
+ phlist[ix].env = PITCHfall;
+ }
+
+ phlist[ix].prepause = 0;
+ phlist[ix].amp = 20; // default, will be changed later
+ phlist[ix].pitch1 = 0x400;
+ phlist[ix].pitch2 = 0x400;
+ ix++;
+ }
+ phlist[ix].newword = 2; // end of clause
+
+ phlist[ix].type = phPAUSE; // terminate with 2 Pause phonemes
+ phlist[ix].length = post_pause; // length of the pause, depends on the punctuation
+ phlist[ix].sourceix = end_sourceix;
+ phlist[ix].synthflags = 0;
+
+ phlist[ix++].ph = phoneme_tab[phonPAUSE];
+ phlist[ix].type = phPAUSE;
+ phlist[ix].length = 0;
+ phlist[ix].sourceix=0;
+ phlist[ix].synthflags = 0;
+ phlist[ix++].ph = phoneme_tab[phonPAUSE_SHORT];
+
+ n_phoneme_list = ix;
+} // end of MakePhonemeList
+
+
diff --git a/Plugins/eSpeak/eSpeak/portaudio.h b/Plugins/eSpeak/eSpeak/portaudio.h
new file mode 100644
index 0000000..2ab8e02
--- /dev/null
+++ b/Plugins/eSpeak/eSpeak/portaudio.h
@@ -0,0 +1,466 @@
+// NOTE: Copy this file to portaudio.h in order to compile with V18 portaudio
+
+
+#ifndef PORT_AUDIO_H
+#define PORT_AUDIO_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+/*
+ * $Id: portaudio.h,v 1.5 2002/03/26 18:04:22 philburk Exp $
+ * PortAudio Portable Real-Time Audio Library
+ * PortAudio API Header File
+ * Latest version available at: http://www.audiomulch.com/portaudio/
+ *
+ * Copyright (c) 1999-2000 Ross Bencina and Phil Burk
+ *
+ * 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.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * 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.
+ *
+ */
+
+typedef int PaError;
+typedef enum {
+ paNoError = 0,
+
+ paHostError = -10000,
+ paInvalidChannelCount,
+ paInvalidSampleRate,
+ paInvalidDeviceId,
+ paInvalidFlag,
+ paSampleFormatNotSupported,
+ paBadIODeviceCombination,
+ paInsufficientMemory,
+ paBufferTooBig,
+ paBufferTooSmall,
+ paNullCallback,
+ paBadStreamPtr,
+ paTimedOut,
+ paInternalError,
+ paDeviceUnavailable
+} PaErrorNum;
+
+/*
+ Pa_Initialize() is the library initialisation function - call this before
+ using the library.
+
+*/
+
+PaError Pa_Initialize( void );
+
+/*
+ Pa_Terminate() is the library termination function - call this after
+ using the library.
+
+*/
+
+PaError Pa_Terminate( void );
+
+/*
+ Pa_GetHostError() returns a host specific error code.
+ This can be called after receiving a PortAudio error code of paHostError.
+
+*/
+
+long Pa_GetHostError( void );
+
+/*
+ Pa_GetErrorText() translates the supplied PortAudio error number
+ into a human readable message.
+
+*/
+
+const char *Pa_GetErrorText( PaError errnum );
+
+/*
+ Sample formats
+
+ These are formats used to pass sound data between the callback and the
+ stream. Each device has a "native" format which may be used when optimum
+ efficiency or control over conversion is required.
+
+ Formats marked "always available" are supported (emulated) by all
+ PortAudio implementations.
+
+ The floating point representation (paFloat32) uses +1.0 and -1.0 as the
+ maximum and minimum respectively.
+
+ paUInt8 is an unsigned 8 bit format where 128 is considered "ground"
+
+*/
+
+typedef unsigned long PaSampleFormat;
+#define paFloat32 ((PaSampleFormat) (1<<0)) /*always available*/
+#define paInt16 ((PaSampleFormat) (1<<1)) /*always available*/
+#define paInt32 ((PaSampleFormat) (1<<2)) /*always available*/
+#define paInt24 ((PaSampleFormat) (1<<3))
+#define paPackedInt24 ((PaSampleFormat) (1<<4))
+#define paInt8 ((PaSampleFormat) (1<<5))
+#define paUInt8 ((PaSampleFormat) (1<<6))
+#define paCustomFormat ((PaSampleFormat) (1<<16))
+
+/*
+ Device enumeration mechanism.
+
+ Device ids range from 0 to Pa_CountDevices()-1.
+
+ Devices may support input, output or both.
+
+*/
+
+typedef int PaDeviceID;
+#define paNoDevice -1
+
+int Pa_CountDevices( void );
+
+typedef struct
+{
+ int structVersion;
+ const char *name;
+ int maxInputChannels;
+ int maxOutputChannels;
+ /* Number of discrete rates, or -1 if range supported. */
+ int numSampleRates;
+ /* Array of supported sample rates, or {min,max} if range supported. */
+ const double *sampleRates;
+ PaSampleFormat nativeSampleFormats;
+}
+PaDeviceInfo;
+
+/*
+ Pa_GetDefaultInputDeviceID(), Pa_GetDefaultOutputDeviceID() return the
+ default device ids for input and output respectively, or paNoDevice if
+ no device is available.
+ The result can be passed to Pa_OpenStream().
+
+ On the PC, the user can specify a default device by
+ setting an environment variable. For example, to use device #1.
+
+ set PA_RECOMMENDED_OUTPUT_DEVICE=1
+
+ The user should first determine the available device ids by using
+ the supplied application "pa_devs".
+
+*/
+
+PaDeviceID Pa_GetDefaultInputDeviceID( void );
+PaDeviceID Pa_GetDefaultOutputDeviceID( void );
+
+
+
+/*
+ Pa_GetDeviceInfo() returns a pointer to an immutable PaDeviceInfo structure
+ for the device specified.
+ If the device parameter is out of range the function returns NULL.
+
+ PortAudio manages the memory referenced by the returned pointer, the client
+ must not manipulate or free the memory. The pointer is only guaranteed to be
+ valid between calls to Pa_Initialize() and Pa_Terminate().
+
+*/
+
+const PaDeviceInfo* Pa_GetDeviceInfo( PaDeviceID device );
+
+/*
+ PaTimestamp is used to represent a continuous sample clock with arbitrary
+ start time that can be used for syncronization. The type is used for the
+ outTime argument to the PortAudioCallback and as the result of Pa_StreamTime()
+
+*/
+
+typedef double PaTimestamp;
+
+/*
+ PortAudioCallback is implemented by PortAudio clients.
+
+ inputBuffer and outputBuffer are arrays of interleaved samples,
+ the format, packing and number of channels used by the buffers are
+ determined by parameters to Pa_OpenStream() (see below).
+
+ framesPerBuffer is the number of sample frames to be processed by the callback.
+
+ outTime is the time in samples when the buffer(s) processed by
+ this callback will begin being played at the audio output.
+ See also Pa_StreamTime()
+
+ userData is the value of a user supplied pointer passed to Pa_OpenStream()
+ intended for storing synthesis data etc.
+
+ return value:
+ The callback can return a non-zero value to stop the stream. This may be
+ useful in applications such as soundfile players where a specific duration
+ of output is required. However, it is not necessary to utilise this mechanism
+ as StopStream() will also terminate the stream. A callback returning a
+ non-zero value must fill the entire outputBuffer.
+
+ NOTE: None of the other stream functions may be called from within the
+ callback function except for Pa_GetCPULoad().
+
+*/
+
+typedef int (PortAudioCallback)(
+ void *inputBuffer, void *outputBuffer,
+ unsigned long framesPerBuffer,
+ PaTimestamp outTime, void *userData );
+
+
+/*
+ Stream flags
+
+ These flags may be supplied (ored together) in the streamFlags argument to
+ the Pa_OpenStream() function.
+
+*/
+
+#define paNoFlag (0)
+#define paClipOff (1<<0) /* disable default clipping of out of range samples */
+#define paDitherOff (1<<1) /* disable default dithering */
+#define paPlatformSpecificFlags (0x00010000)
+typedef unsigned long PaStreamFlags;
+
+/*
+ A single PortAudioStream provides multiple channels of real-time
+ input and output audio streaming to a client application.
+ Pointers to PortAudioStream objects are passed between PortAudio functions.
+*/
+
+typedef void PortAudioStream;
+#define PaStream PortAudioStream
+
+/*
+ Pa_OpenStream() opens a stream for either input, output or both.
+
+ stream is the address of a PortAudioStream pointer which will receive
+ a pointer to the newly opened stream.
+
+ inputDevice is the id of the device used for input (see PaDeviceID above.)
+ inputDevice may be paNoDevice to indicate that an input device is not required.
+
+ numInputChannels is the number of channels of sound to be delivered to the
+ callback. It can range from 1 to the value of maxInputChannels in the
+ PaDeviceInfo record for the device specified by the inputDevice parameter.
+ If inputDevice is paNoDevice numInputChannels is ignored.
+
+ inputSampleFormat is the sample format of inputBuffer provided to the callback
+ function. inputSampleFormat may be any of the formats described by the
+ PaSampleFormat enumeration (see above). PortAudio guarantees support for
+ the device's native formats (nativeSampleFormats in the device info record)
+ and additionally 16 and 32 bit integer and 32 bit floating point formats.
+ Support for other formats is implementation defined.
+
+ inputDriverInfo is a pointer to an optional driver specific data structure
+ containing additional information for device setup or stream processing.
+ inputDriverInfo is never required for correct operation. If not used
+ inputDriverInfo should be NULL.
+
+ outputDevice is the id of the device used for output (see PaDeviceID above.)
+ outputDevice may be paNoDevice to indicate that an output device is not required.
+
+ numOutputChannels is the number of channels of sound to be supplied by the
+ callback. See the definition of numInputChannels above for more details.
+
+ outputSampleFormat is the sample format of the outputBuffer filled by the
+ callback function. See the definition of inputSampleFormat above for more
+ details.
+
+ outputDriverInfo is a pointer to an optional driver specific data structure
+ containing additional information for device setup or stream processing.
+ outputDriverInfo is never required for correct operation. If not used
+ outputDriverInfo should be NULL.
+
+ sampleRate is the desired sampleRate. For full-duplex streams it is the
+ sample rate for both input and output
+
+ framesPerBuffer is the length in sample frames of all internal sample buffers
+ used for communication with platform specific audio routines. Wherever
+ possible this corresponds to the framesPerBuffer parameter passed to the
+ callback function.
+
+ numberOfBuffers is the number of buffers used for multibuffered communication
+ with the platform specific audio routines. If you pass zero, then an optimum
+ value will be chosen for you internally. This parameter is provided only
+ as a guide - and does not imply that an implementation must use multibuffered
+ i/o when reliable double buffering is available (such as SndPlayDoubleBuffer()
+ on the Macintosh.)
+
+ streamFlags may contain a combination of flags ORed together.
+ These flags modify the behaviour of the streaming process. Some flags may only
+ be relevant to certain buffer formats.
+
+ callback is a pointer to a client supplied function that is responsible
+ for processing and filling input and output buffers (see above for details.)
+
+ userData is a client supplied pointer which is passed to the callback
+ function. It could for example, contain a pointer to instance data necessary
+ for processing the audio buffers.
+
+ return value:
+ Upon success Pa_OpenStream() returns PaNoError and places a pointer to a
+ valid PortAudioStream in the stream argument. The stream is inactive (stopped).
+ If a call to Pa_OpenStream() fails a non-zero error code is returned (see
+ PaError above) and the value of stream is invalid.
+
+*/
+
+PaError Pa_OpenStream( PortAudioStream** stream,
+ PaDeviceID inputDevice,
+ int numInputChannels,
+ PaSampleFormat inputSampleFormat,
+ void *inputDriverInfo,
+ PaDeviceID outputDevice,
+ int numOutputChannels,
+ PaSampleFormat outputSampleFormat,
+ void *outputDriverInfo,
+ double sampleRate,
+ unsigned long framesPerBuffer,
+ unsigned long numberOfBuffers,
+ PaStreamFlags streamFlags,
+ PortAudioCallback *callback,
+ void *userData );
+
+
+/*
+ Pa_OpenDefaultStream() is a simplified version of Pa_OpenStream() that opens
+ the default input and/or output devices. Most parameters have identical meaning
+ to their Pa_OpenStream() counterparts, with the following exceptions:
+
+ If either numInputChannels or numOutputChannels is 0 the respective device
+ is not opened. This has the same effect as passing paNoDevice in the device
+ arguments to Pa_OpenStream().
+
+ sampleFormat applies to both the input and output buffers.
+
+*/
+
+PaError Pa_OpenDefaultStream( PortAudioStream** stream,
+ int numInputChannels,
+ int numOutputChannels,
+ PaSampleFormat sampleFormat,
+ double sampleRate,
+ unsigned long framesPerBuffer,
+ unsigned long numberOfBuffers,
+ PortAudioCallback *callback,
+ void *userData );
+
+/*
+ Pa_CloseStream() closes an audio stream, flushing any pending buffers.
+
+*/
+
+PaError Pa_CloseStream( PortAudioStream* );
+
+/*
+ Pa_StartStream() and Pa_StopStream() begin and terminate audio processing.
+ Pa_StopStream() waits until all pending audio buffers have been played.
+ Pa_AbortStream() stops playing immediately without waiting for pending
+ buffers to complete.
+
+*/
+
+PaError Pa_StartStream( PortAudioStream *stream );
+
+PaError Pa_StopStream( PortAudioStream *stream );
+
+PaError Pa_AbortStream( PortAudioStream *stream );
+
+/*
+ Pa_StreamActive() returns one (1) when the stream is active (ie playing
+ or recording audio), zero (0) when not playing, or a negative error number
+ if the stream is invalid.
+ The stream is active between calls to Pa_StartStream() and Pa_StopStream(),
+ but may also become inactive if the callback returns a non-zero value.
+ In the latter case, the stream is considered inactive after the last
+ buffer has finished playing.
+
+*/
+
+PaError Pa_StreamActive( PortAudioStream *stream );
+
+/*
+ Pa_StreamTime() returns the current output time in samples for the stream.
+ This time may be used as a time reference (for example synchronizing audio to
+ MIDI).
+
+*/
+
+PaTimestamp Pa_StreamTime( PortAudioStream *stream );
+
+/*
+ Pa_GetCPULoad() returns the CPU Load for the stream.
+ The "CPU Load" is a fraction of total CPU time consumed by the stream's
+ audio processing routines including, but not limited to the client supplied
+ callback.
+ A value of 0.5 would imply that PortAudio and the sound generating
+ callback was consuming roughly 50% of the available CPU time.
+ This function may be called from the callback function or the application.
+
+*/
+
+double Pa_GetCPULoad( PortAudioStream* stream );
+
+/*
+ Pa_GetMinNumBuffers() returns the minimum number of buffers required by
+ the current host based on minimum latency.
+ On the PC, for the DirectSound implementation, latency can be optionally set
+ by user by setting an environment variable.
+ For example, to set latency to 200 msec, put:
+
+ set PA_MIN_LATENCY_MSEC=200
+
+ in the AUTOEXEC.BAT file and reboot.
+ If the environment variable is not set, then the latency will be determined
+ based on the OS. Windows NT has higher latency than Win95.
+
+*/
+
+int Pa_GetMinNumBuffers( int framesPerBuffer, double sampleRate );
+
+/*
+ Pa_Sleep() puts the caller to sleep for at least 'msec' milliseconds.
+ You may sleep longer than the requested time so don't rely on this for
+ accurate musical timing.
+
+ Pa_Sleep() is provided as a convenience for authors of portable code (such as
+ the tests and examples in the PortAudio distribution.)
+
+*/
+
+void Pa_Sleep( long msec );
+
+/*
+ Pa_GetSampleSize() returns the size in bytes of a single sample in the
+ supplied PaSampleFormat, or paSampleFormatNotSupported if the format is
+ no supported.
+
+*/
+
+PaError Pa_GetSampleSize( PaSampleFormat format );
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* PORT_AUDIO_H */
diff --git a/Plugins/eSpeak/eSpeak/portaudio18.h b/Plugins/eSpeak/eSpeak/portaudio18.h
new file mode 100644
index 0000000..2ab8e02
--- /dev/null
+++ b/Plugins/eSpeak/eSpeak/portaudio18.h
@@ -0,0 +1,466 @@
+// NOTE: Copy this file to portaudio.h in order to compile with V18 portaudio
+
+
+#ifndef PORT_AUDIO_H
+#define PORT_AUDIO_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+/*
+ * $Id: portaudio.h,v 1.5 2002/03/26 18:04:22 philburk Exp $
+ * PortAudio Portable Real-Time Audio Library
+ * PortAudio API Header File
+ * Latest version available at: http://www.audiomulch.com/portaudio/
+ *
+ * Copyright (c) 1999-2000 Ross Bencina and Phil Burk
+ *
+ * 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.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * 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.
+ *
+ */
+
+typedef int PaError;
+typedef enum {
+ paNoError = 0,
+
+ paHostError = -10000,
+ paInvalidChannelCount,
+ paInvalidSampleRate,
+ paInvalidDeviceId,
+ paInvalidFlag,
+ paSampleFormatNotSupported,
+ paBadIODeviceCombination,
+ paInsufficientMemory,
+ paBufferTooBig,
+ paBufferTooSmall,
+ paNullCallback,
+ paBadStreamPtr,
+ paTimedOut,
+ paInternalError,
+ paDeviceUnavailable
+} PaErrorNum;
+
+/*
+ Pa_Initialize() is the library initialisation function - call this before
+ using the library.
+
+*/
+
+PaError Pa_Initialize( void );
+
+/*
+ Pa_Terminate() is the library termination function - call this after
+ using the library.
+
+*/
+
+PaError Pa_Terminate( void );
+
+/*
+ Pa_GetHostError() returns a host specific error code.
+ This can be called after receiving a PortAudio error code of paHostError.
+
+*/
+
+long Pa_GetHostError( void );
+
+/*
+ Pa_GetErrorText() translates the supplied PortAudio error number
+ into a human readable message.
+
+*/
+
+const char *Pa_GetErrorText( PaError errnum );
+
+/*
+ Sample formats
+
+ These are formats used to pass sound data between the callback and the
+ stream. Each device has a "native" format which may be used when optimum
+ efficiency or control over conversion is required.
+
+ Formats marked "always available" are supported (emulated) by all
+ PortAudio implementations.
+
+ The floating point representation (paFloat32) uses +1.0 and -1.0 as the
+ maximum and minimum respectively.
+
+ paUInt8 is an unsigned 8 bit format where 128 is considered "ground"
+
+*/
+
+typedef unsigned long PaSampleFormat;
+#define paFloat32 ((PaSampleFormat) (1<<0)) /*always available*/
+#define paInt16 ((PaSampleFormat) (1<<1)) /*always available*/
+#define paInt32 ((PaSampleFormat) (1<<2)) /*always available*/
+#define paInt24 ((PaSampleFormat) (1<<3))
+#define paPackedInt24 ((PaSampleFormat) (1<<4))
+#define paInt8 ((PaSampleFormat) (1<<5))
+#define paUInt8 ((PaSampleFormat) (1<<6))
+#define paCustomFormat ((PaSampleFormat) (1<<16))
+
+/*
+ Device enumeration mechanism.
+
+ Device ids range from 0 to Pa_CountDevices()-1.
+
+ Devices may support input, output or both.
+
+*/
+
+typedef int PaDeviceID;
+#define paNoDevice -1
+
+int Pa_CountDevices( void );
+
+typedef struct
+{
+ int structVersion;
+ const char *name;
+ int maxInputChannels;
+ int maxOutputChannels;
+ /* Number of discrete rates, or -1 if range supported. */
+ int numSampleRates;
+ /* Array of supported sample rates, or {min,max} if range supported. */
+ const double *sampleRates;
+ PaSampleFormat nativeSampleFormats;
+}
+PaDeviceInfo;
+
+/*
+ Pa_GetDefaultInputDeviceID(), Pa_GetDefaultOutputDeviceID() return the
+ default device ids for input and output respectively, or paNoDevice if
+ no device is available.
+ The result can be passed to Pa_OpenStream().
+
+ On the PC, the user can specify a default device by
+ setting an environment variable. For example, to use device #1.
+
+ set PA_RECOMMENDED_OUTPUT_DEVICE=1
+
+ The user should first determine the available device ids by using
+ the supplied application "pa_devs".
+
+*/
+
+PaDeviceID Pa_GetDefaultInputDeviceID( void );
+PaDeviceID Pa_GetDefaultOutputDeviceID( void );
+
+
+
+/*
+ Pa_GetDeviceInfo() returns a pointer to an immutable PaDeviceInfo structure
+ for the device specified.
+ If the device parameter is out of range the function returns NULL.
+
+ PortAudio manages the memory referenced by the returned pointer, the client
+ must not manipulate or free the memory. The pointer is only guaranteed to be
+ valid between calls to Pa_Initialize() and Pa_Terminate().
+
+*/
+
+const PaDeviceInfo* Pa_GetDeviceInfo( PaDeviceID device );
+
+/*
+ PaTimestamp is used to represent a continuous sample clock with arbitrary
+ start time that can be used for syncronization. The type is used for the
+ outTime argument to the PortAudioCallback and as the result of Pa_StreamTime()
+
+*/
+
+typedef double PaTimestamp;
+
+/*
+ PortAudioCallback is implemented by PortAudio clients.
+
+ inputBuffer and outputBuffer are arrays of interleaved samples,
+ the format, packing and number of channels used by the buffers are
+ determined by parameters to Pa_OpenStream() (see below).
+
+ framesPerBuffer is the number of sample frames to be processed by the callback.
+
+ outTime is the time in samples when the buffer(s) processed by
+ this callback will begin being played at the audio output.
+ See also Pa_StreamTime()
+
+ userData is the value of a user supplied pointer passed to Pa_OpenStream()
+ intended for storing synthesis data etc.
+
+ return value:
+ The callback can return a non-zero value to stop the stream. This may be
+ useful in applications such as soundfile players where a specific duration
+ of output is required. However, it is not necessary to utilise this mechanism
+ as StopStream() will also terminate the stream. A callback returning a
+ non-zero value must fill the entire outputBuffer.
+
+ NOTE: None of the other stream functions may be called from within the
+ callback function except for Pa_GetCPULoad().
+
+*/
+
+typedef int (PortAudioCallback)(
+ void *inputBuffer, void *outputBuffer,
+ unsigned long framesPerBuffer,
+ PaTimestamp outTime, void *userData );
+
+
+/*
+ Stream flags
+
+ These flags may be supplied (ored together) in the streamFlags argument to
+ the Pa_OpenStream() function.
+
+*/
+
+#define paNoFlag (0)
+#define paClipOff (1<<0) /* disable default clipping of out of range samples */
+#define paDitherOff (1<<1) /* disable default dithering */
+#define paPlatformSpecificFlags (0x00010000)
+typedef unsigned long PaStreamFlags;
+
+/*
+ A single PortAudioStream provides multiple channels of real-time
+ input and output audio streaming to a client application.
+ Pointers to PortAudioStream objects are passed between PortAudio functions.
+*/
+
+typedef void PortAudioStream;
+#define PaStream PortAudioStream
+
+/*
+ Pa_OpenStream() opens a stream for either input, output or both.
+
+ stream is the address of a PortAudioStream pointer which will receive
+ a pointer to the newly opened stream.
+
+ inputDevice is the id of the device used for input (see PaDeviceID above.)
+ inputDevice may be paNoDevice to indicate that an input device is not required.
+
+ numInputChannels is the number of channels of sound to be delivered to the
+ callback. It can range from 1 to the value of maxInputChannels in the
+ PaDeviceInfo record for the device specified by the inputDevice parameter.
+ If inputDevice is paNoDevice numInputChannels is ignored.
+
+ inputSampleFormat is the sample format of inputBuffer provided to the callback
+ function. inputSampleFormat may be any of the formats described by the
+ PaSampleFormat enumeration (see above). PortAudio guarantees support for
+ the device's native formats (nativeSampleFormats in the device info record)
+ and additionally 16 and 32 bit integer and 32 bit floating point formats.
+ Support for other formats is implementation defined.
+
+ inputDriverInfo is a pointer to an optional driver specific data structure
+ containing additional information for device setup or stream processing.
+ inputDriverInfo is never required for correct operation. If not used
+ inputDriverInfo should be NULL.
+
+ outputDevice is the id of the device used for output (see PaDeviceID above.)
+ outputDevice may be paNoDevice to indicate that an output device is not required.
+
+ numOutputChannels is the number of channels of sound to be supplied by the
+ callback. See the definition of numInputChannels above for more details.
+
+ outputSampleFormat is the sample format of the outputBuffer filled by the
+ callback function. See the definition of inputSampleFormat above for more
+ details.
+
+ outputDriverInfo is a pointer to an optional driver specific data structure
+ containing additional information for device setup or stream processing.
+ outputDriverInfo is never required for correct operation. If not used
+ outputDriverInfo should be NULL.
+
+ sampleRate is the desired sampleRate. For full-duplex streams it is the
+ sample rate for both input and output
+
+ framesPerBuffer is the length in sample frames of all internal sample buffers
+ used for communication with platform specific audio routines. Wherever
+ possible this corresponds to the framesPerBuffer parameter passed to the
+ callback function.
+
+ numberOfBuffers is the number of buffers used for multibuffered communication
+ with the platform specific audio routines. If you pass zero, then an optimum
+ value will be chosen for you internally. This parameter is provided only
+ as a guide - and does not imply that an implementation must use multibuffered
+ i/o when reliable double buffering is available (such as SndPlayDoubleBuffer()
+ on the Macintosh.)
+
+ streamFlags may contain a combination of flags ORed together.
+ These flags modify the behaviour of the streaming process. Some flags may only
+ be relevant to certain buffer formats.
+
+ callback is a pointer to a client supplied function that is responsible
+ for processing and filling input and output buffers (see above for details.)
+
+ userData is a client supplied pointer which is passed to the callback
+ function. It could for example, contain a pointer to instance data necessary
+ for processing the audio buffers.
+
+ return value:
+ Upon success Pa_OpenStream() returns PaNoError and places a pointer to a
+ valid PortAudioStream in the stream argument. The stream is inactive (stopped).
+ If a call to Pa_OpenStream() fails a non-zero error code is returned (see
+ PaError above) and the value of stream is invalid.
+
+*/
+
+PaError Pa_OpenStream( PortAudioStream** stream,
+ PaDeviceID inputDevice,
+ int numInputChannels,
+ PaSampleFormat inputSampleFormat,
+ void *inputDriverInfo,
+ PaDeviceID outputDevice,
+ int numOutputChannels,
+ PaSampleFormat outputSampleFormat,
+ void *outputDriverInfo,
+ double sampleRate,
+ unsigned long framesPerBuffer,
+ unsigned long numberOfBuffers,
+ PaStreamFlags streamFlags,
+ PortAudioCallback *callback,
+ void *userData );
+
+
+/*
+ Pa_OpenDefaultStream() is a simplified version of Pa_OpenStream() that opens
+ the default input and/or output devices. Most parameters have identical meaning
+ to their Pa_OpenStream() counterparts, with the following exceptions:
+
+ If either numInputChannels or numOutputChannels is 0 the respective device
+ is not opened. This has the same effect as passing paNoDevice in the device
+ arguments to Pa_OpenStream().
+
+ sampleFormat applies to both the input and output buffers.
+
+*/
+
+PaError Pa_OpenDefaultStream( PortAudioStream** stream,
+ int numInputChannels,
+ int numOutputChannels,
+ PaSampleFormat sampleFormat,
+ double sampleRate,
+ unsigned long framesPerBuffer,
+ unsigned long numberOfBuffers,
+ PortAudioCallback *callback,
+ void *userData );
+
+/*
+ Pa_CloseStream() closes an audio stream, flushing any pending buffers.
+
+*/
+
+PaError Pa_CloseStream( PortAudioStream* );
+
+/*
+ Pa_StartStream() and Pa_StopStream() begin and terminate audio processing.
+ Pa_StopStream() waits until all pending audio buffers have been played.
+ Pa_AbortStream() stops playing immediately without waiting for pending
+ buffers to complete.
+
+*/
+
+PaError Pa_StartStream( PortAudioStream *stream );
+
+PaError Pa_StopStream( PortAudioStream *stream );
+
+PaError Pa_AbortStream( PortAudioStream *stream );
+
+/*
+ Pa_StreamActive() returns one (1) when the stream is active (ie playing
+ or recording audio), zero (0) when not playing, or a negative error number
+ if the stream is invalid.
+ The stream is active between calls to Pa_StartStream() and Pa_StopStream(),
+ but may also become inactive if the callback returns a non-zero value.
+ In the latter case, the stream is considered inactive after the last
+ buffer has finished playing.
+
+*/
+
+PaError Pa_StreamActive( PortAudioStream *stream );
+
+/*
+ Pa_StreamTime() returns the current output time in samples for the stream.
+ This time may be used as a time reference (for example synchronizing audio to
+ MIDI).
+
+*/
+
+PaTimestamp Pa_StreamTime( PortAudioStream *stream );
+
+/*
+ Pa_GetCPULoad() returns the CPU Load for the stream.
+ The "CPU Load" is a fraction of total CPU time consumed by the stream's
+ audio processing routines including, but not limited to the client supplied
+ callback.
+ A value of 0.5 would imply that PortAudio and the sound generating
+ callback was consuming roughly 50% of the available CPU time.
+ This function may be called from the callback function or the application.
+
+*/
+
+double Pa_GetCPULoad( PortAudioStream* stream );
+
+/*
+ Pa_GetMinNumBuffers() returns the minimum number of buffers required by
+ the current host based on minimum latency.
+ On the PC, for the DirectSound implementation, latency can be optionally set
+ by user by setting an environment variable.
+ For example, to set latency to 200 msec, put:
+
+ set PA_MIN_LATENCY_MSEC=200
+
+ in the AUTOEXEC.BAT file and reboot.
+ If the environment variable is not set, then the latency will be determined
+ based on the OS. Windows NT has higher latency than Win95.
+
+*/
+
+int Pa_GetMinNumBuffers( int framesPerBuffer, double sampleRate );
+
+/*
+ Pa_Sleep() puts the caller to sleep for at least 'msec' milliseconds.
+ You may sleep longer than the requested time so don't rely on this for
+ accurate musical timing.
+
+ Pa_Sleep() is provided as a convenience for authors of portable code (such as
+ the tests and examples in the PortAudio distribution.)
+
+*/
+
+void Pa_Sleep( long msec );
+
+/*
+ Pa_GetSampleSize() returns the size in bytes of a single sample in the
+ supplied PaSampleFormat, or paSampleFormatNotSupported if the format is
+ no supported.
+
+*/
+
+PaError Pa_GetSampleSize( PaSampleFormat format );
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* PORT_AUDIO_H */
diff --git a/Plugins/eSpeak/eSpeak/portaudio19.h b/Plugins/eSpeak/eSpeak/portaudio19.h
new file mode 100644
index 0000000..5c060b7
--- /dev/null
+++ b/Plugins/eSpeak/eSpeak/portaudio19.h
@@ -0,0 +1,1127 @@
+// NOTE: Copy this file to portaudio.h in order to compile with V19 portaudio
+
+#ifndef PORTAUDIO_H
+#define PORTAUDIO_H
+/*
+ * $Id: portaudio.h 1061 2006-06-19 22:46:41Z lschwardt $
+ * PortAudio Portable Real-Time Audio Library
+ * PortAudio API Header File
+ * Latest version available at: http://www.portaudio.com/
+ *
+ * Copyright (c) 1999-2002 Ross Bencina and Phil Burk
+ *
+ * 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.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * 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.
+ */
+
+/** @file
+ @brief The PortAudio API.
+*/
+
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+
+/** Retrieve the release number of the currently running PortAudio build,
+ eg 1900.
+*/
+int Pa_GetVersion( void );
+
+
+/** Retrieve a textual description of the current PortAudio build,
+ eg "PortAudio V19-devel 13 October 2002".
+*/
+const char* Pa_GetVersionText( void );
+
+
+/** Error codes returned by PortAudio functions.
+ Note that with the exception of paNoError, all PaErrorCodes are negative.
+*/
+
+typedef int PaError;
+typedef enum PaErrorCode
+{
+ paNoError = 0,
+
+ paNotInitialized = -10000,
+ paUnanticipatedHostError,
+ paInvalidChannelCount,
+ paInvalidSampleRate,
+ paInvalidDevice,
+ paInvalidFlag,
+ paSampleFormatNotSupported,
+ paBadIODeviceCombination,
+ paInsufficientMemory,
+ paBufferTooBig,
+ paBufferTooSmall,
+ paNullCallback,
+ paBadStreamPtr,
+ paTimedOut,
+ paInternalError,
+ paDeviceUnavailable,
+ paIncompatibleHostApiSpecificStreamInfo,
+ paStreamIsStopped,
+ paStreamIsNotStopped,
+ paInputOverflowed,
+ paOutputUnderflowed,
+ paHostApiNotFound,
+ paInvalidHostApi,
+ paCanNotReadFromACallbackStream, /**< @todo review error code name */
+ paCanNotWriteToACallbackStream, /**< @todo review error code name */
+ paCanNotReadFromAnOutputOnlyStream, /**< @todo review error code name */
+ paCanNotWriteToAnInputOnlyStream, /**< @todo review error code name */
+ paIncompatibleStreamHostApi,
+ paBadBufferPtr
+} PaErrorCode;
+
+
+/** Translate the supplied PortAudio error code into a human readable
+ message.
+*/
+const char *Pa_GetErrorText( PaError errorCode );
+
+
+/** Library initialization function - call this before using PortAudio.
+ This function initialises internal data structures and prepares underlying
+ host APIs for use. This function MUST be called before using any other
+ PortAudio API functions.
+
+ If Pa_Initialize() is called multiple times, each successful
+ call must be matched with a corresponding call to Pa_Terminate().
+ Pairs of calls to Pa_Initialize()/Pa_Terminate() may overlap, and are not
+ required to be fully nested.
+
+ Note that if Pa_Initialize() returns an error code, Pa_Terminate() should
+ NOT be called.
+
+ @return paNoError if successful, otherwise an error code indicating the cause
+ of failure.
+
+ @see Pa_Terminate
+*/
+PaError Pa_Initialize( void );
+
+
+/** Library termination function - call this when finished using PortAudio.
+ This function deallocates all resources allocated by PortAudio since it was
+ initializied by a call to Pa_Initialize(). In cases where Pa_Initialise() has
+ been called multiple times, each call must be matched with a corresponding call
+ to Pa_Terminate(). The final matching call to Pa_Terminate() will automatically
+ close any PortAudio streams that are still open.
+
+ Pa_Terminate() MUST be called before exiting a program which uses PortAudio.
+ Failure to do so may result in serious resource leaks, such as audio devices
+ not being available until the next reboot.
+
+ @return paNoError if successful, otherwise an error code indicating the cause
+ of failure.
+
+ @see Pa_Initialize
+*/
+PaError Pa_Terminate( void );
+
+
+
+/** The type used to refer to audio devices. Values of this type usually
+ range from 0 to (Pa_DeviceCount-1), and may also take on the PaNoDevice
+ and paUseHostApiSpecificDeviceSpecification values.
+
+ @see Pa_DeviceCount, paNoDevice, paUseHostApiSpecificDeviceSpecification
+*/
+typedef int PaDeviceIndex;
+
+
+/** A special PaDeviceIndex value indicating that no device is available,
+ or should be used.
+
+ @see PaDeviceIndex
+*/
+#define paNoDevice ((PaDeviceIndex)-1)
+
+
+/** A special PaDeviceIndex value indicating that the device(s) to be used
+ are specified in the host api specific stream info structure.
+
+ @see PaDeviceIndex
+*/
+#define paUseHostApiSpecificDeviceSpecification ((PaDeviceIndex)-2)
+
+
+/* Host API enumeration mechanism */
+
+/** The type used to enumerate to host APIs at runtime. Values of this type
+ range from 0 to (Pa_GetHostApiCount()-1).
+
+ @see Pa_GetHostApiCount
+*/
+typedef int PaHostApiIndex;
+
+
+/** Retrieve the number of available host APIs. Even if a host API is
+ available it may have no devices available.
+
+ @return A non-negative value indicating the number of available host APIs
+ or, a PaErrorCode (which are always negative) if PortAudio is not initialized
+ or an error is encountered.
+
+ @see PaHostApiIndex
+*/
+PaHostApiIndex Pa_GetHostApiCount( void );
+
+
+/** Retrieve the index of the default host API. The default host API will be
+ the lowest common denominator host API on the current platform and is
+ unlikely to provide the best performance.
+
+ @return A non-negative value ranging from 0 to (Pa_GetHostApiCount()-1)
+ indicating the default host API index or, a PaErrorCode (which are always
+ negative) if PortAudio is not initialized or an error is encountered.
+*/
+PaHostApiIndex Pa_GetDefaultHostApi( void );
+
+
+/** Unchanging unique identifiers for each supported host API. This type
+ is used in the PaHostApiInfo structure. The values are guaranteed to be
+ unique and to never change, thus allowing code to be written that
+ conditionally uses host API specific extensions.
+
+ New type ids will be allocated when support for a host API reaches
+ "public alpha" status, prior to that developers should use the
+ paInDevelopment type id.
+
+ @see PaHostApiInfo
+*/
+typedef enum PaHostApiTypeId
+{
+ paInDevelopment=0, /* use while developing support for a new host API */
+ paDirectSound=1,
+ paMME=2,
+ paASIO=3,
+ paSoundManager=4,
+ paCoreAudio=5,
+ paOSS=7,
+ paALSA=8,
+ paAL=9,
+ paBeOS=10,
+ paWDMKS=11,
+ paJACK=12,
+ paWASAPI=13,
+ paAudioScienceHPI=14
+} PaHostApiTypeId;
+
+
+/** A structure containing information about a particular host API. */
+
+typedef struct PaHostApiInfo
+{
+ /** this is struct version 1 */
+ int structVersion;
+ /** The well known unique identifier of this host API @see PaHostApiTypeId */
+ PaHostApiTypeId type;
+ /** A textual description of the host API for display on user interfaces. */
+ const char *name;
+
+ /** The number of devices belonging to this host API. This field may be
+ used in conjunction with Pa_HostApiDeviceIndexToDeviceIndex() to enumerate
+ all devices for this host API.
+ @see Pa_HostApiDeviceIndexToDeviceIndex
+ */
+ int deviceCount;
+
+ /** The default input device for this host API. The value will be a
+ device index ranging from 0 to (Pa_GetDeviceCount()-1), or paNoDevice
+ if no default input device is available.
+ */
+ PaDeviceIndex defaultInputDevice;
+
+ /** The default output device for this host API. The value will be a
+ device index ranging from 0 to (Pa_GetDeviceCount()-1), or paNoDevice
+ if no default output device is available.
+ */
+ PaDeviceIndex defaultOutputDevice;
+
+} PaHostApiInfo;
+
+
+/** Retrieve a pointer to a structure containing information about a specific
+ host Api.
+
+ @param hostApi A valid host API index ranging from 0 to (Pa_GetHostApiCount()-1)
+
+ @return A pointer to an immutable PaHostApiInfo structure describing
+ a specific host API. If the hostApi parameter is out of range or an error
+ is encountered, the function returns NULL.
+
+ The returned structure is owned by the PortAudio implementation and must not
+ be manipulated or freed. The pointer is only guaranteed to be valid between
+ calls to Pa_Initialize() and Pa_Terminate().
+*/
+const PaHostApiInfo * Pa_GetHostApiInfo( PaHostApiIndex hostApi );
+
+
+/** Convert a static host API unique identifier, into a runtime
+ host API index.
+
+ @param type A unique host API identifier belonging to the PaHostApiTypeId
+ enumeration.
+
+ @return A valid PaHostApiIndex ranging from 0 to (Pa_GetHostApiCount()-1) or,
+ a PaErrorCode (which are always negative) if PortAudio is not initialized
+ or an error is encountered.
+
+ The paHostApiNotFound error code indicates that the host API specified by the
+ type parameter is not available.
+
+ @see PaHostApiTypeId
+*/
+PaHostApiIndex Pa_HostApiTypeIdToHostApiIndex( PaHostApiTypeId type );
+
+
+/** Convert a host-API-specific device index to standard PortAudio device index.
+ This function may be used in conjunction with the deviceCount field of
+ PaHostApiInfo to enumerate all devices for the specified host API.
+
+ @param hostApi A valid host API index ranging from 0 to (Pa_GetHostApiCount()-1)
+
+ @param hostApiDeviceIndex A valid per-host device index in the range
+ 0 to (Pa_GetHostApiInfo(hostApi)->deviceCount-1)
+
+ @return A non-negative PaDeviceIndex ranging from 0 to (Pa_GetDeviceCount()-1)
+ or, a PaErrorCode (which are always negative) if PortAudio is not initialized
+ or an error is encountered.
+
+ A paInvalidHostApi error code indicates that the host API index specified by
+ the hostApi parameter is out of range.
+
+ A paInvalidDevice error code indicates that the hostApiDeviceIndex parameter
+ is out of range.
+
+ @see PaHostApiInfo
+*/
+PaDeviceIndex Pa_HostApiDeviceIndexToDeviceIndex( PaHostApiIndex hostApi,
+ int hostApiDeviceIndex );
+
+
+
+/** Structure used to return information about a host error condition.
+*/
+typedef struct PaHostErrorInfo{
+ PaHostApiTypeId hostApiType; /**< the host API which returned the error code */
+ long errorCode; /**< the error code returned */
+ const char *errorText; /**< a textual description of the error if available, otherwise a zero-length string */
+}PaHostErrorInfo;
+
+
+/** Return information about the last host error encountered. The error
+ information returned by Pa_GetLastHostErrorInfo() will never be modified
+ asyncronously by errors occurring in other PortAudio owned threads
+ (such as the thread that manages the stream callback.)
+
+ This function is provided as a last resort, primarily to enhance debugging
+ by providing clients with access to all available error information.
+
+ @return A pointer to an immutable structure constaining information about
+ the host error. The values in this structure will only be valid if a
+ PortAudio function has previously returned the paUnanticipatedHostError
+ error code.
+*/
+const PaHostErrorInfo* Pa_GetLastHostErrorInfo( void );
+
+
+
+/* Device enumeration and capabilities */
+
+/** Retrieve the number of available devices. The number of available devices
+ may be zero.
+
+ @return A non-negative value indicating the number of available devices or,
+ a PaErrorCode (which are always negative) if PortAudio is not initialized
+ or an error is encountered.
+*/
+PaDeviceIndex Pa_GetDeviceCount( void );
+
+
+/** Retrieve the index of the default input device. The result can be
+ used in the inputDevice parameter to Pa_OpenStream().
+
+ @return The default input device index for the default host API, or paNoDevice
+ if no default input device is available or an error was encountered.
+*/
+PaDeviceIndex Pa_GetDefaultInputDevice( void );
+
+
+/** Retrieve the index of the default output device. The result can be
+ used in the outputDevice parameter to Pa_OpenStream().
+
+ @return The default output device index for the defualt host API, or paNoDevice
+ if no default output device is available or an error was encountered.
+
+ @note
+ On the PC, the user can specify a default device by
+ setting an environment variable. For example, to use device #1.
+<pre>
+ set PA_RECOMMENDED_OUTPUT_DEVICE=1
+</pre>
+ The user should first determine the available device ids by using
+ the supplied application "pa_devs".
+*/
+PaDeviceIndex Pa_GetDefaultOutputDevice( void );
+
+
+/** The type used to represent monotonic time in seconds that can be used
+ for syncronisation. The type is used for the outTime argument to the
+ PaStreamCallback and as the result of Pa_GetStreamTime().
+
+ @see PaStreamCallback, Pa_GetStreamTime
+*/
+typedef double PaTime;
+
+
+/** A type used to specify one or more sample formats. Each value indicates
+ a possible format for sound data passed to and from the stream callback,
+ Pa_ReadStream and Pa_WriteStream.
+
+ The standard formats paFloat32, paInt16, paInt32, paInt24, paInt8
+ and aUInt8 are usually implemented by all implementations.
+
+ The floating point representation (paFloat32) uses +1.0 and -1.0 as the
+ maximum and minimum respectively.
+
+ paUInt8 is an unsigned 8 bit format where 128 is considered "ground"
+
+ The paNonInterleaved flag indicates that a multichannel buffer is passed
+ as a set of non-interleaved pointers.
+
+ @see Pa_OpenStream, Pa_OpenDefaultStream, PaDeviceInfo
+ @see paFloat32, paInt16, paInt32, paInt24, paInt8
+ @see paUInt8, paCustomFormat, paNonInterleaved
+*/
+typedef unsigned long PaSampleFormat;
+
+
+#define paFloat32 ((PaSampleFormat) 0x00000001) /**< @see PaSampleFormat */
+#define paInt32 ((PaSampleFormat) 0x00000002) /**< @see PaSampleFormat */
+#define paInt24 ((PaSampleFormat) 0x00000004) /**< Packed 24 bit format. @see PaSampleFormat */
+#define paInt16 ((PaSampleFormat) 0x00000008) /**< @see PaSampleFormat */
+#define paInt8 ((PaSampleFormat) 0x00000010) /**< @see PaSampleFormat */
+#define paUInt8 ((PaSampleFormat) 0x00000020) /**< @see PaSampleFormat */
+#define paCustomFormat ((PaSampleFormat) 0x00010000)/**< @see PaSampleFormat */
+
+#define paNonInterleaved ((PaSampleFormat) 0x80000000)
+
+/** A structure providing information and capabilities of PortAudio devices.
+ Devices may support input, output or both input and output.
+*/
+typedef struct PaDeviceInfo
+{
+ int structVersion; /* this is struct version 2 */
+ const char *name;
+ PaHostApiIndex hostApi; /* note this is a host API index, not a type id*/
+
+ int maxInputChannels;
+ int maxOutputChannels;
+
+ /* Default latency values for interactive performance. */
+ PaTime defaultLowInputLatency;
+ PaTime defaultLowOutputLatency;
+ /* Default latency values for robust non-interactive applications (eg. playing sound files). */
+ PaTime defaultHighInputLatency;
+ PaTime defaultHighOutputLatency;
+
+ double defaultSampleRate;
+} PaDeviceInfo;
+
+
+/** Retrieve a pointer to a PaDeviceInfo structure containing information
+ about the specified device.
+ @return A pointer to an immutable PaDeviceInfo structure. If the device
+ parameter is out of range the function returns NULL.
+
+ @param device A valid device index in the range 0 to (Pa_GetDeviceCount()-1)
+
+ @note PortAudio manages the memory referenced by the returned pointer,
+ the client must not manipulate or free the memory. The pointer is only
+ guaranteed to be valid between calls to Pa_Initialize() and Pa_Terminate().
+
+ @see PaDeviceInfo, PaDeviceIndex
+*/
+const PaDeviceInfo* Pa_GetDeviceInfo( PaDeviceIndex device );
+
+
+/** Parameters for one direction (input or output) of a stream.
+*/
+typedef struct PaStreamParameters
+{
+ /** A valid device index in the range 0 to (Pa_GetDeviceCount()-1)
+ specifying the device to be used or the special constant
+ paUseHostApiSpecificDeviceSpecification which indicates that the actual
+ device(s) to use are specified in hostApiSpecificStreamInfo.
+ This field must not be set to paNoDevice.
+ */
+ PaDeviceIndex device;
+
+ /** The number of channels of sound to be delivered to the
+ stream callback or accessed by Pa_ReadStream() or Pa_WriteStream().
+ It can range from 1 to the value of maxInputChannels in the
+ PaDeviceInfo record for the device specified by the device parameter.
+ */
+ int channelCount;
+
+ /** The sample format of the buffer provided to the stream callback,
+ a_ReadStream() or Pa_WriteStream(). It may be any of the formats described
+ by the PaSampleFormat enumeration.
+ */
+ PaSampleFormat sampleFormat;
+
+ /** The desired latency in seconds. Where practical, implementations should
+ configure their latency based on these parameters, otherwise they may
+ choose the closest viable latency instead. Unless the suggested latency
+ is greater than the absolute upper limit for the device implementations
+ should round the suggestedLatency up to the next practial value - ie to
+ provide an equal or higher latency than suggestedLatency wherever possibe.
+ Actual latency values for an open stream may be retrieved using the
+ inputLatency and outputLatency fields of the PaStreamInfo structure
+ returned by Pa_GetStreamInfo().
+ @see default*Latency in PaDeviceInfo, *Latency in PaStreamInfo
+ */
+ PaTime suggestedLatency;
+
+ /** An optional pointer to a host api specific data structure
+ containing additional information for device setup and/or stream processing.
+ hostApiSpecificStreamInfo is never required for correct operation,
+ if not used it should be set to NULL.
+ */
+ void *hostApiSpecificStreamInfo;
+
+} PaStreamParameters;
+
+
+/** Return code for Pa_IsFormatSupported indicating success. */
+#define paFormatIsSupported (0)
+
+/** Determine whether it would be possible to open a stream with the specified
+ parameters.
+
+ @param inputParameters A structure that describes the input parameters used to
+ open a stream. The suggestedLatency field is ignored. See PaStreamParameters
+ for a description of these parameters. inputParameters must be NULL for
+ output-only streams.
+
+ @param outputParameters A structure that describes the output parameters used
+ to open a stream. The suggestedLatency field is ignored. See PaStreamParameters
+ for a description of these parameters. outputParameters must be NULL for
+ input-only streams.
+
+ @param sampleRate The required sampleRate. For full-duplex streams it is the
+ sample rate for both input and output
+
+ @return Returns 0 if the format is supported, and an error code indicating why
+ the format is not supported otherwise. The constant paFormatIsSupported is
+ provided to compare with the return value for success.
+
+ @see paFormatIsSupported, PaStreamParameters
+*/
+PaError Pa_IsFormatSupported( const PaStreamParameters *inputParameters,
+ const PaStreamParameters *outputParameters,
+ double sampleRate );
+
+
+
+/* Streaming types and functions */
+
+
+/**
+ A single PaStream can provide multiple channels of real-time
+ streaming audio input and output to a client application. A stream
+ provides access to audio hardware represented by one or more
+ PaDevices. Depending on the underlying Host API, it may be possible
+ to open multiple streams using the same device, however this behavior
+ is implementation defined. Portable applications should assume that
+ a PaDevice may be simultaneously used by at most one PaStream.
+
+ Pointers to PaStream objects are passed between PortAudio functions that
+ operate on streams.
+
+ @see Pa_OpenStream, Pa_OpenDefaultStream, Pa_OpenDefaultStream, Pa_CloseStream,
+ Pa_StartStream, Pa_StopStream, Pa_AbortStream, Pa_IsStreamActive,
+ Pa_GetStreamTime, Pa_GetStreamCpuLoad
+
+*/
+typedef void PaStream;
+
+
+/** Can be passed as the framesPerBuffer parameter to Pa_OpenStream()
+ or Pa_OpenDefaultStream() to indicate that the stream callback will
+ accept buffers of any size.
+*/
+#define paFramesPerBufferUnspecified (0)
+
+
+/** Flags used to control the behavior of a stream. They are passed as
+ parameters to Pa_OpenStream or Pa_OpenDefaultStream. Multiple flags may be
+ ORed together.
+
+ @see Pa_OpenStream, Pa_OpenDefaultStream
+ @see paNoFlag, paClipOff, paDitherOff, paNeverDropInput,
+ paPrimeOutputBuffersUsingStreamCallback, paPlatformSpecificFlags
+*/
+typedef unsigned long PaStreamFlags;
+
+/** @see PaStreamFlags */
+#define paNoFlag ((PaStreamFlags) 0)
+
+/** Disable default clipping of out of range samples.
+ @see PaStreamFlags
+*/
+#define paClipOff ((PaStreamFlags) 0x00000001)
+
+/** Disable default dithering.
+ @see PaStreamFlags
+*/
+#define paDitherOff ((PaStreamFlags) 0x00000002)
+
+/** Flag requests that where possible a full duplex stream will not discard
+ overflowed input samples without calling the stream callback. This flag is
+ only valid for full duplex callback streams and only when used in combination
+ with the paFramesPerBufferUnspecified (0) framesPerBuffer parameter. Using
+ this flag incorrectly results in a paInvalidFlag error being returned from
+ Pa_OpenStream and Pa_OpenDefaultStream.
+
+ @see PaStreamFlags, paFramesPerBufferUnspecified
+*/
+#define paNeverDropInput ((PaStreamFlags) 0x00000004)
+
+/** Call the stream callback to fill initial output buffers, rather than the
+ default behavior of priming the buffers with zeros (silence). This flag has
+ no effect for input-only and blocking read/write streams.
+
+ @see PaStreamFlags
+*/
+#define paPrimeOutputBuffersUsingStreamCallback ((PaStreamFlags) 0x00000008)
+
+/** A mask specifying the platform specific bits.
+ @see PaStreamFlags
+*/
+#define paPlatformSpecificFlags ((PaStreamFlags)0xFFFF0000)
+
+/**
+ Timing information for the buffers passed to the stream callback.
+*/
+typedef struct PaStreamCallbackTimeInfo{
+ PaTime inputBufferAdcTime;
+ PaTime currentTime;
+ PaTime outputBufferDacTime;
+} PaStreamCallbackTimeInfo;
+
+
+/**
+ Flag bit constants for the statusFlags to PaStreamCallback.
+
+ @see paInputUnderflow, paInputOverflow, paOutputUnderflow, paOutputOverflow,
+ paPrimingOutput
+*/
+typedef unsigned long PaStreamCallbackFlags;
+
+/** In a stream opened with paFramesPerBufferUnspecified, indicates that
+ input data is all silence (zeros) because no real data is available. In a
+ stream opened without paFramesPerBufferUnspecified, it indicates that one or
+ more zero samples have been inserted into the input buffer to compensate
+ for an input underflow.
+ @see PaStreamCallbackFlags
+*/
+#define paInputUnderflow ((PaStreamCallbackFlags) 0x00000001)
+
+/** In a stream opened with paFramesPerBufferUnspecified, indicates that data
+ prior to the first sample of the input buffer was discarded due to an
+ overflow, possibly because the stream callback is using too much CPU time.
+ Otherwise indicates that data prior to one or more samples in the
+ input buffer was discarded.
+ @see PaStreamCallbackFlags
+*/
+#define paInputOverflow ((PaStreamCallbackFlags) 0x00000002)
+
+/** Indicates that output data (or a gap) was inserted, possibly because the
+ stream callback is using too much CPU time.
+ @see PaStreamCallbackFlags
+*/
+#define paOutputUnderflow ((PaStreamCallbackFlags) 0x00000004)
+
+/** Indicates that output data will be discarded because no room is available.
+ @see PaStreamCallbackFlags
+*/
+#define paOutputOverflow ((PaStreamCallbackFlags) 0x00000008)
+
+/** Some of all of the output data will be used to prime the stream, input
+ data may be zero.
+ @see PaStreamCallbackFlags
+*/
+#define paPrimingOutput ((PaStreamCallbackFlags) 0x00000010)
+
+/**
+ Allowable return values for the PaStreamCallback.
+ @see PaStreamCallback
+*/
+typedef enum PaStreamCallbackResult
+{
+ paContinue=0,
+ paComplete=1,
+ paAbort=2
+} PaStreamCallbackResult;
+
+
+/**
+ Functions of type PaStreamCallback are implemented by PortAudio clients.
+ They consume, process or generate audio in response to requests from an
+ active PortAudio stream.
+
+ @param input and @param output are arrays of interleaved samples,
+ the format, packing and number of channels used by the buffers are
+ determined by parameters to Pa_OpenStream().
+
+ @param frameCount The number of sample frames to be processed by
+ the stream callback.
+
+ @param timeInfo The time in seconds when the first sample of the input
+ buffer was received at the audio input, the time in seconds when the first
+ sample of the output buffer will begin being played at the audio output, and
+ the time in seconds when the stream callback was called.
+ See also Pa_GetStreamTime()
+
+ @param statusFlags Flags indicating whether input and/or output buffers
+ have been inserted or will be dropped to overcome underflow or overflow
+ conditions.
+
+ @param userData The value of a user supplied pointer passed to
+ Pa_OpenStream() intended for storing synthesis data etc.
+
+ @return
+ The stream callback should return one of the values in the
+ PaStreamCallbackResult enumeration. To ensure that the callback continues
+ to be called, it should return paContinue (0). Either paComplete or paAbort
+ can be returned to finish stream processing, after either of these values is
+ returned the callback will not be called again. If paAbort is returned the
+ stream will finish as soon as possible. If paComplete is returned, the stream
+ will continue until all buffers generated by the callback have been played.
+ This may be useful in applications such as soundfile players where a specific
+ duration of output is required. However, it is not necessary to utilise this
+ mechanism as Pa_StopStream(), Pa_AbortStream() or Pa_CloseStream() can also
+ be used to stop the stream. The callback must always fill the entire output
+ buffer irrespective of its return value.
+
+ @see Pa_OpenStream, Pa_OpenDefaultStream
+
+ @note With the exception of Pa_GetStreamCpuLoad() it is not permissable to call
+ PortAudio API functions from within the stream callback.
+*/
+typedef int PaStreamCallback(
+ const void *input, void *output,
+ unsigned long frameCount,
+ const PaStreamCallbackTimeInfo* timeInfo,
+ PaStreamCallbackFlags statusFlags,
+ void *userData );
+
+
+/** Opens a stream for either input, output or both.
+
+ @param stream The address of a PaStream pointer which will receive
+ a pointer to the newly opened stream.
+
+ @param inputParameters A structure that describes the input parameters used by
+ the opened stream. See PaStreamParameters for a description of these parameters.
+ inputParameters must be NULL for output-only streams.
+
+ @param outputParameters A structure that describes the output parameters used by
+ the opened stream. See PaStreamParameters for a description of these parameters.
+ outputParameters must be NULL for input-only streams.
+
+ @param sampleRate The desired sampleRate. For full-duplex streams it is the
+ sample rate for both input and output
+
+ @param framesPerBuffer The number of frames passed to the stream callback
+ function, or the preferred block granularity for a blocking read/write stream.
+ The special value paFramesPerBufferUnspecified (0) may be used to request that
+ the stream callback will recieve an optimal (and possibly varying) number of
+ frames based on host requirements and the requested latency settings.
+ Note: With some host APIs, the use of non-zero framesPerBuffer for a callback
+ stream may introduce an additional layer of buffering which could introduce
+ additional latency. PortAudio guarantees that the additional latency
+ will be kept to the theoretical minimum however, it is strongly recommended
+ that a non-zero framesPerBuffer value only be used when your algorithm
+ requires a fixed number of frames per stream callback.
+
+ @param streamFlags Flags which modify the behaviour of the streaming process.
+ This parameter may contain a combination of flags ORed together. Some flags may
+ only be relevant to certain buffer formats.
+
+ @param streamCallback A pointer to a client supplied function that is responsible
+ for processing and filling input and output buffers. If this parameter is NULL
+ the stream will be opened in 'blocking read/write' mode. In blocking mode,
+ the client can receive sample data using Pa_ReadStream and write sample data
+ using Pa_WriteStream, the number of samples that may be read or written
+ without blocking is returned by Pa_GetStreamReadAvailable and
+ Pa_GetStreamWriteAvailable respectively.
+
+ @param userData A client supplied pointer which is passed to the stream callback
+ function. It could for example, contain a pointer to instance data necessary
+ for processing the audio buffers. This parameter is ignored if streamCallback
+ is NULL.
+
+ @return
+ Upon success Pa_OpenStream() returns paNoError and places a pointer to a
+ valid PaStream in the stream argument. The stream is inactive (stopped).
+ If a call to Pa_OpenStream() fails, a non-zero error code is returned (see
+ PaError for possible error codes) and the value of stream is invalid.
+
+ @see PaStreamParameters, PaStreamCallback, Pa_ReadStream, Pa_WriteStream,
+ Pa_GetStreamReadAvailable, Pa_GetStreamWriteAvailable
+*/
+PaError Pa_OpenStream( PaStream** stream,
+ const PaStreamParameters *inputParameters,
+ const PaStreamParameters *outputParameters,
+ double sampleRate,
+ unsigned long framesPerBuffer,
+ PaStreamFlags streamFlags,
+ PaStreamCallback *streamCallback,
+ void *userData );
+
+
+/** A simplified version of Pa_OpenStream() that opens the default input
+ and/or output devices.
+
+ @param stream The address of a PaStream pointer which will receive
+ a pointer to the newly opened stream.
+
+ @param numInputChannels The number of channels of sound that will be supplied
+ to the stream callback or returned by Pa_ReadStream. It can range from 1 to
+ the value of maxInputChannels in the PaDeviceInfo record for the default input
+ device. If 0 the stream is opened as an output-only stream.
+
+ @param numOutputChannels The number of channels of sound to be delivered to the
+ stream callback or passed to Pa_WriteStream. It can range from 1 to the value
+ of maxOutputChannels in the PaDeviceInfo record for the default output dvice.
+ If 0 the stream is opened as an output-only stream.
+
+ @param sampleFormat The sample format of both the input and output buffers
+ provided to the callback or passed to and from Pa_ReadStream and Pa_WriteStream.
+ sampleFormat may be any of the formats described by the PaSampleFormat
+ enumeration.
+
+ @param sampleRate Same as Pa_OpenStream parameter of the same name.
+ @param framesPerBuffer Same as Pa_OpenStream parameter of the same name.
+ @param streamCallback Same as Pa_OpenStream parameter of the same name.
+ @param userData Same as Pa_OpenStream parameter of the same name.
+
+ @return As for Pa_OpenStream
+
+ @see Pa_OpenStream, PaStreamCallback
+*/
+PaError Pa_OpenDefaultStream( PaStream** stream,
+ int numInputChannels,
+ int numOutputChannels,
+ PaSampleFormat sampleFormat,
+ double sampleRate,
+ unsigned long framesPerBuffer,
+ PaStreamCallback *streamCallback,
+ void *userData );
+
+
+/** Closes an audio stream. If the audio stream is active it
+ discards any pending buffers as if Pa_AbortStream() had been called.
+*/
+PaError Pa_CloseStream( PaStream *stream );
+
+
+/** Functions of type PaStreamFinishedCallback are implemented by PortAudio
+ clients. They can be registered with a stream using the Pa_SetStreamFinishedCallback
+ function. Once registered they are called when the stream becomes inactive
+ (ie once a call to Pa_StopStream() will not block).
+ A stream will become inactive after the stream callback returns non-zero,
+ or when Pa_StopStream or Pa_AbortStream is called. For a stream providing audio
+ output, if the stream callback returns paComplete, or Pa_StopStream is called,
+ the stream finished callback will not be called until all generated sample data
+ has been played.
+
+ @param userData The userData parameter supplied to Pa_OpenStream()
+
+ @see Pa_SetStreamFinishedCallback
+*/
+typedef void PaStreamFinishedCallback( void *userData );
+
+
+/** Register a stream finished callback function which will be called when the
+ stream becomes inactive. See the description of PaStreamFinishedCallback for
+ further details about when the callback will be called.
+
+ @param stream a pointer to a PaStream that is in the stopped state - if the
+ stream is not stopped, the stream's finished callback will remain unchanged
+ and an error code will be returned.
+
+ @param streamFinishedCallback a pointer to a function with the same signature
+ as PaStreamFinishedCallback, that will be called when the stream becomes
+ inactive. Passing NULL for this parameter will un-register a previously
+ registered stream finished callback function.
+
+ @return on success returns paNoError, otherwise an error code indicating the cause
+ of the error.
+
+ @see PaStreamFinishedCallback
+*/
+PaError Pa_SetStreamFinishedCallback( PaStream *stream, PaStreamFinishedCallback* streamFinishedCallback );
+
+
+/** Commences audio processing.
+*/
+PaError Pa_StartStream( PaStream *stream );
+
+
+/** Terminates audio processing. It waits until all pending
+ audio buffers have been played before it returns.
+*/
+PaError Pa_StopStream( PaStream *stream );
+
+
+/** Terminates audio processing immediately without waiting for pending
+ buffers to complete.
+*/
+PaError Pa_AbortStream( PaStream *stream );
+
+
+/** Determine whether the stream is stopped.
+ A stream is considered to be stopped prior to a successful call to
+ Pa_StartStream and after a successful call to Pa_StopStream or Pa_AbortStream.
+ If a stream callback returns a value other than paContinue the stream is NOT
+ considered to be stopped.
+
+ @return Returns one (1) when the stream is stopped, zero (0) when
+ the stream is running or, a PaErrorCode (which are always negative) if
+ PortAudio is not initialized or an error is encountered.
+
+ @see Pa_StopStream, Pa_AbortStream, Pa_IsStreamActive
+*/
+PaError Pa_IsStreamStopped( PaStream *stream );
+
+
+/** Determine whether the stream is active.
+ A stream is active after a successful call to Pa_StartStream(), until it
+ becomes inactive either as a result of a call to Pa_StopStream() or
+ Pa_AbortStream(), or as a result of a return value other than paContinue from
+ the stream callback. In the latter case, the stream is considered inactive
+ after the last buffer has finished playing.
+
+ @return Returns one (1) when the stream is active (ie playing or recording
+ audio), zero (0) when not playing or, a PaErrorCode (which are always negative)
+ if PortAudio is not initialized or an error is encountered.
+
+ @see Pa_StopStream, Pa_AbortStream, Pa_IsStreamStopped
+*/
+PaError Pa_IsStreamActive( PaStream *stream );
+
+
+
+/** A structure containing unchanging information about an open stream.
+ @see Pa_GetStreamInfo
+*/
+
+typedef struct PaStreamInfo
+{
+ /** this is struct version 1 */
+ int structVersion;
+
+ /** The input latency of the stream in seconds. This value provides the most
+ accurate estimate of input latency available to the implementation. It may
+ differ significantly from the suggestedLatency value passed to Pa_OpenStream().
+ The value of this field will be zero (0.) for output-only streams.
+ @see PaTime
+ */
+ PaTime inputLatency;
+
+ /** The output latency of the stream in seconds. This value provides the most
+ accurate estimate of output latency available to the implementation. It may
+ differ significantly from the suggestedLatency value passed to Pa_OpenStream().
+ The value of this field will be zero (0.) for input-only streams.
+ @see PaTime
+ */
+ PaTime outputLatency;
+
+ /** The sample rate of the stream in Hertz (samples per second). In cases
+ where the hardware sample rate is inaccurate and PortAudio is aware of it,
+ the value of this field may be different from the sampleRate parameter
+ passed to Pa_OpenStream(). If information about the actual hardware sample
+ rate is not available, this field will have the same value as the sampleRate
+ parameter passed to Pa_OpenStream().
+ */
+ double sampleRate;
+
+} PaStreamInfo;
+
+
+/** Retrieve a pointer to a PaStreamInfo structure containing information
+ about the specified stream.
+ @return A pointer to an immutable PaStreamInfo structure. If the stream
+ parameter invalid, or an error is encountered, the function returns NULL.
+
+ @param stream A pointer to an open stream previously created with Pa_OpenStream.
+
+ @note PortAudio manages the memory referenced by the returned pointer,
+ the client must not manipulate or free the memory. The pointer is only
+ guaranteed to be valid until the specified stream is closed.
+
+ @see PaStreamInfo
+*/
+const PaStreamInfo* Pa_GetStreamInfo( PaStream *stream );
+
+
+/** Determine the current time for the stream according to the same clock used
+ to generate buffer timestamps. This time may be used for syncronising other
+ events to the audio stream, for example synchronizing audio to MIDI.
+
+ @return The stream's current time in seconds, or 0 if an error occurred.
+
+ @see PaTime, PaStreamCallback
+*/
+PaTime Pa_GetStreamTime( PaStream *stream );
+
+
+/** Retrieve CPU usage information for the specified stream.
+ The "CPU Load" is a fraction of total CPU time consumed by a callback stream's
+ audio processing routines including, but not limited to the client supplied
+ stream callback. This function does not work with blocking read/write streams.
+
+ This function may be called from the stream callback function or the
+ application.
+
+ @return
+ A floating point value, typically between 0.0 and 1.0, where 1.0 indicates
+ that the stream callback is consuming the maximum number of CPU cycles possible
+ to maintain real-time operation. A value of 0.5 would imply that PortAudio and
+ the stream callback was consuming roughly 50% of the available CPU time. The
+ return value may exceed 1.0. A value of 0.0 will always be returned for a
+ blocking read/write stream, or if an error occurrs.
+*/
+double Pa_GetStreamCpuLoad( PaStream* stream );
+
+
+/** Read samples from an input stream. The function doesn't return until
+ the entire buffer has been filled - this may involve waiting for the operating
+ system to supply the data.
+
+ @param stream A pointer to an open stream previously created with Pa_OpenStream.
+
+ @param buffer A pointer to a buffer of sample frames. The buffer contains
+ samples in the format specified by the inputParameters->sampleFormat field
+ used to open the stream, and the number of channels specified by
+ inputParameters->numChannels. If non-interleaved samples were requested,
+ buffer is a pointer to the first element of an array of non-interleaved
+ buffer pointers, one for each channel.
+
+ @param frames The number of frames to be read into buffer. This parameter
+ is not constrained to a specific range, however high performance applications
+ will want to match this parameter to the framesPerBuffer parameter used
+ when opening the stream.
+
+ @return On success PaNoError will be returned, or PaInputOverflowed if input
+ data was discarded by PortAudio after the previous call and before this call.
+*/
+PaError Pa_ReadStream( PaStream* stream,
+ void *buffer,
+ unsigned long frames );
+
+
+/** Write samples to an output stream. This function doesn't return until the
+ entire buffer has been consumed - this may involve waiting for the operating
+ system to consume the data.
+
+ @param stream A pointer to an open stream previously created with Pa_OpenStream.
+
+ @param buffer A pointer to a buffer of sample frames. The buffer contains
+ samples in the format specified by the outputParameters->sampleFormat field
+ used to open the stream, and the number of channels specified by
+ outputParameters->numChannels. If non-interleaved samples were requested,
+ buffer is a pointer to the first element of an array of non-interleaved
+ buffer pointers, one for each channel.
+
+ @param frames The number of frames to be written from buffer. This parameter
+ is not constrained to a specific range, however high performance applications
+ will want to match this parameter to the framesPerBuffer parameter used
+ when opening the stream.
+
+ @return On success PaNoError will be returned, or paOutputUnderflowed if
+ additional output data was inserted after the previous call and before this
+ call.
+*/
+PaError Pa_WriteStream( PaStream* stream,
+ const void *buffer,
+ unsigned long frames );
+
+
+/** Retrieve the number of frames that can be read from the stream without
+ waiting.
+
+ @return Returns a non-negative value representing the maximum number of frames
+ that can be read from the stream without blocking or busy waiting or, a
+ PaErrorCode (which are always negative) if PortAudio is not initialized or an
+ error is encountered.
+*/
+signed long Pa_GetStreamReadAvailable( PaStream* stream );
+
+
+/** Retrieve the number of frames that can be written to the stream without
+ waiting.
+
+ @return Returns a non-negative value representing the maximum number of frames
+ that can be written to the stream without blocking or busy waiting or, a
+ PaErrorCode (which are always negative) if PortAudio is not initialized or an
+ error is encountered.
+*/
+signed long Pa_GetStreamWriteAvailable( PaStream* stream );
+
+
+/* Miscellaneous utilities */
+
+
+/** Retrieve the size of a given sample format in bytes.
+
+ @return The size in bytes of a single sample in the specified format,
+ or paSampleFormatNotSupported if the format is not supported.
+*/
+PaError Pa_GetSampleSize( PaSampleFormat format );
+
+
+/** Put the caller to sleep for at least 'msec' milliseconds. This function is
+ provided only as a convenience for authors of portable code (such as the tests
+ and examples in the PortAudio distribution.)
+
+ The function may sleep longer than requested so don't rely on this for accurate
+ musical timing.
+*/
+void Pa_Sleep( long msec );
+
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* PORTAUDIO_H */
diff --git a/Plugins/eSpeak/eSpeak/readclause.cpp b/Plugins/eSpeak/eSpeak/readclause.cpp
new file mode 100644
index 0000000..3eca8de
--- /dev/null
+++ b/Plugins/eSpeak/eSpeak/readclause.cpp
@@ -0,0 +1,2402 @@
+/***************************************************************************
+ * Copyright (C) 2005 to 2007 by Jonathan Duddington *
+ * email: jonsd@users.sourceforge.net *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 3 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write see: *
+ * <http://www.gnu.org/licenses/>. *
+ ***************************************************************************/
+
+#include "StdAfx.h"
+
+#include <stdio.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include <wctype.h>
+#include <wchar.h>
+#include <math.h>
+
+#include "speak_lib.h"
+#include "speech.h"
+#include "phoneme.h"
+#include "synthesize.h"
+#include "voice.h"
+#include "translate.h"
+
+#ifdef PLATFORM_POSIX
+#include <unistd.h>
+#endif
+
+#include <locale.h>
+#define N_XML_BUF 256
+
+
+static const char *xmlbase = ""; // base URL from <speak>
+
+static int namedata_ix=0;
+static int n_namedata = 0;
+char *namedata = NULL;
+
+
+static FILE *f_input = NULL;
+static int ungot_char2 = 0;
+char *p_textinput;
+wchar_t *p_wchar_input;
+static int ungot_char;
+static const char *ungot_word = NULL;
+static int end_of_input;
+
+static int ignore_text=0; // set during <sub> ... </sub> to ignore text which has been replaced by an alias
+static int clear_skipping_text = 0; // next clause should clear the skipping_text flag
+int count_characters = 0;
+static int sayas_mode;
+static int ssml_ignore_l_angle = 0;
+
+static const char *punct_stop = ".:!?"; // pitch fall if followed by space
+static const char *punct_close = ")]}>;'\""; // always pitch fall unless followed by alnum
+
+// alter tone for announce punctuation or capitals
+static const char *tone_punct_on = "\0016T"; // add reverberation, lower pitch
+static const char *tone_punct_off = "\001T";
+
+// punctuations symbols that can end a clause
+static const unsigned short punct_chars[] = {',','.','?','!',':',';',
+ 0x2013, // en-dash
+ 0x2014, // em-dash
+ 0x2026, // elipsis
+
+ 0x037e, // Greek question mark (looks like semicolon)
+ 0x0387, // Greek semicolon, ano teleia
+ 0x0964, // Devanagari Danda (fullstop)
+
+ 0x0589, // Armenian period
+ 0x055d, // Armenian comma
+ 0x055c, // Armenian exclamation
+ 0x055e, // Armenian question
+ 0x055b, // Armenian emphasis mark
+
+ 0x1362, // Ethiopic period
+ 0x1363,
+ 0x1364,
+ 0x1365,
+ 0x1366,
+ 0x1367,
+ 0x1368,
+
+ 0x3001, // ideograph comma
+ 0x3002, // ideograph period
+
+ 0xff01, // fullwidth exclamation
+ 0xff0c, // fullwidth comma
+ 0xff0e, // fullwidth period
+ 0xff1a, // fullwidth colon
+ 0xff1b, // fullwidth semicolon
+ 0xff1f, // fullwidth question mark
+
+ 0};
+
+
+// indexed by (entry num. in punct_chars) + 1
+// bits 0-7 pause x 10mS, bits 12-14 intonation type, bit 15 don't need following space or bracket
+static const unsigned int punct_attributes [] = { 0,
+ CLAUSE_COMMA, CLAUSE_PERIOD, CLAUSE_QUESTION, CLAUSE_EXCLAMATION, CLAUSE_COLON, CLAUSE_SEMICOLON,
+ CLAUSE_SEMICOLON, // en-dash
+ CLAUSE_SEMICOLON, // em-dash
+ CLAUSE_SEMICOLON, // elipsis
+
+ CLAUSE_QUESTION, // Greek question mark
+ CLAUSE_SEMICOLON, // Greek semicolon
+ CLAUSE_PERIOD+0x8000, // Devanagari Danda (fullstop)
+
+ CLAUSE_PERIOD+0x8000, // Armenian period
+ CLAUSE_COMMA, // Armenian comma
+ CLAUSE_EXCLAMATION + PUNCT_IN_WORD, // Armenian exclamation
+ CLAUSE_QUESTION + PUNCT_IN_WORD, // Armenian question
+ CLAUSE_PERIOD + PUNCT_IN_WORD, // Armenian emphasis mark
+
+ CLAUSE_PERIOD, // Ethiopic period
+ CLAUSE_COMMA, // Ethiopic comma
+ CLAUSE_SEMICOLON, // Ethiopic semicolon
+ CLAUSE_COLON, // Ethiopic colon
+ CLAUSE_COLON, // Ethiopic preface colon
+ CLAUSE_QUESTION, // Ethiopic question mark
+ CLAUSE_PERIOD, // Ethiopic paragraph
+
+ CLAUSE_COMMA+0x8000, // ideograph comma
+ CLAUSE_PERIOD+0x8000, // ideograph period
+
+ CLAUSE_EXCLAMATION+0x8000, // fullwidth
+ CLAUSE_COMMA+0x8000,
+ CLAUSE_PERIOD+0x8000,
+ CLAUSE_COLON+0x8000,
+ CLAUSE_SEMICOLON+0x8000,
+ CLAUSE_QUESTION+0x8000,
+
+ CLAUSE_SEMICOLON, // spare
+ 0 };
+
+
+// stack for language and voice properties
+// frame 0 is for the defaults, before any ssml tags.
+typedef struct {
+ int tag_type;
+ int voice_variant;
+ int voice_gender;
+ int voice_age;
+ char voice_name[40];
+ char language[20];
+} SSML_STACK;
+
+#define N_SSML_STACK 20
+static int n_ssml_stack;
+static SSML_STACK ssml_stack[N_SSML_STACK];
+
+static char current_voice_id[40] = {0};
+
+
+#define N_PARAM_STACK 20
+static int n_param_stack;
+PARAM_STACK param_stack[N_PARAM_STACK];
+
+static int speech_parameters[N_SPEECH_PARAM]; // current values, from param_stack
+
+const int param_defaults[N_SPEECH_PARAM] = {
+ 0, // silence (internal use)
+ 170, // rate wpm
+ 100, // volume
+ 50, // pitch
+ 50, // range
+ 0, // punctuation
+ 0, // capital letters
+ 0, // wordgap
+ 0, // options
+ 0, // intonation
+ 0,
+ 0,
+ 0, // emphasis
+ 0, // line length
+ 0, // voice type
+};
+
+
+#ifdef NEED_WCHAR_FUNCTIONS
+
+// additional Latin characters beyond the Latin1 character set
+#define MAX_WALPHA 0x233
+// indexed by character - 0x100
+// 0=not alphabetic, 0xff=lower case, other=value to add to upper case to convert to lower case
+static unsigned char walpha_tab[MAX_WALPHA-0xff] = {
+ 1,0xff, 1,0xff, 1,0xff, 1,0xff, 1,0xff, 1,0xff, 1,0xff, 1,0xff, // 100
+ 1,0xff, 1,0xff, 1,0xff, 1,0xff, 1,0xff, 1,0xff, 1,0xff, 1,0xff, // 110
+ 1,0xff, 1,0xff, 1,0xff, 1,0xff, 1,0xff, 1,0xff, 1,0xff, 1,0xff, // 120
+ 0xff,0xff, 1,0xff, 1,0xff, 1,0xff,0xff, 1,0xff, 1,0xff, 1,0xff, 1, // 130
+ 0xff, 1,0xff, 1,0xff, 1,0xff, 1,0xff,0xff, 1,0xff, 1,0xff, 1,0xff, // 140
+ 1,0xff, 1,0xff, 1,0xff, 1,0xff, 1,0xff, 1,0xff, 1,0xff, 1,0xff, // 150
+ 1,0xff, 1,0xff, 1,0xff, 1,0xff, 1,0xff, 1,0xff, 1,0xff, 1,0xff, // 160
+ 1,0xff, 1,0xff, 1,0xff, 1,0xff,0xff, 1,0xff, 1,0xff, 1,0xff,0xff, // 170
+ 0xff, 210, 1,0xff, 1,0xff, 206, 1,0xff, 205, 205, 1,0xff,0xff, 79, 202, // 180
+ 203, 1,0xff, 205, 207,0xff, 211, 209, 1,0xff,0xff,0xff, 211, 213,0xff, 214, // 190
+ 1,0xff, 1,0xff, 1,0xff, 218, 1,0xff, 218,0xff,0xff, 1,0xff, 218, 1, // 1a0
+ 0xff, 217, 217, 1,0xff, 1,0xff, 219, 1,0xff,0xff,0xff, 1,0xff,0xff,0xff, // 1b0
+ 0xff,0xff,0xff,0xff, 2, 1,0xff, 2, 1,0xff, 2, 1,0xff, 1,0xff, 1, // 1c0
+ 0xff, 1,0xff, 1,0xff, 1,0xff, 1,0xff, 1,0xff, 1,0xff,0xff, 1,0xff, // 1d0
+ 1,0xff, 1,0xff, 1,0xff, 1,0xff, 1,0xff, 1,0xff, 1,0xff, 1,0xff, // 1e0
+ 0xff, 2, 1,0xff, 1,0xff,0xff,0xff, 1,0xff, 1,0xff, 1,0xff, 1,0xff, // 1f0
+ 1,0xff, 1,0xff, 1,0xff, 1,0xff, 1,0xff, 1,0xff, 1,0xff, 1,0xff, // 200
+ 1,0xff, 1,0xff, 1,0xff, 1,0xff, 1,0xff, 1,0xff, 1,0xff, 1,0xff, // 210
+ 0xff, 0, 1,0xff, 1,0xff, 1,0xff, 1,0xff, 1,0xff, 1,0xff, 1,0xff, // 220
+ 1,0xff, 1,0xff }; // 230
+
+// use ctype.h functions for Latin1 (character < 0x100)
+int iswalpha(int c)
+{
+ if(c < 0x100)
+ return(isalpha(c));
+ if((c > 0x3040) && (c <= 0xa700))
+ return(1); // japanese, chinese characters
+ if(c > MAX_WALPHA)
+ return(0);
+ return(walpha_tab[c-0x100]);
+}
+
+int iswdigit(int c)
+{
+ if(c < 0x100)
+ return(isdigit(c));
+ return(0);
+}
+
+int iswalnum(int c)
+{
+ if(iswdigit(c))
+ return(1);
+ return(iswalpha(c));
+}
+
+int towlower(int c)
+{
+ int x;
+ if(c < 0x100)
+ return(tolower(c));
+ if((c > MAX_WALPHA) || ((x = walpha_tab[c-0x100])==0xff))
+ return(c); // already lower case
+ return(c + x); // convert to lower case
+}
+
+int towupper(int c)
+{
+ // check whether the previous character code is the upper-case equivalent of this character
+ if(tolower(c-1) == c)
+ return(c-1); // yes, use it
+ return(c); // no
+}
+
+int iswupper(int c)
+{
+ int x;
+ if(c < 0x100)
+ return(isupper(c));
+ if(((c > MAX_WALPHA) || (x = walpha_tab[c-0x100])==0) || (x == 0xff))
+ return(0);
+ return(1);
+}
+
+int iswlower(int c)
+{
+ if(c < 0x100)
+ return(islower(c));
+ if((c > MAX_WALPHA) || (walpha_tab[c-0x100] != 0xff))
+ return(0);
+ return(1);
+}
+
+int iswspace(int c)
+{
+ if(c < 0x100)
+ return(isspace(c));
+ return(0);
+}
+
+int iswpunct(int c)
+{
+ if(c < 0x100)
+ return(ispunct(c));
+ return(0);
+}
+
+const wchar_t *wcschr(const wchar_t *str, int c)
+{
+ while(*str != 0)
+ {
+ if(*str == c)
+ return(str);
+ str++;
+ }
+ return(NULL);
+}
+
+const int wcslen(const wchar_t *str)
+{
+ int ix=0;
+
+ while(*str != 0)
+ {
+ ix++;
+ }
+ return(ix);
+}
+
+float wcstod(const wchar_t *str, wchar_t **tailptr)
+{
+ int ix;
+ char buf[80];
+ while(isspace(*str)) str++;
+ for(ix=0; ix<80; ix++)
+ {
+ buf[ix] = str[ix];
+ if(isspace(buf[ix]))
+ break;
+ }
+ *tailptr = (wchar_t *)&str[ix];
+ return(atof(buf));
+}
+#endif
+
+int towlower2(unsigned int c)
+{
+ // check for non-standard upper to lower case conversions
+ if(c == 'I')
+ {
+ if(translator->translator_name == L('t','r'))
+ {
+ c = 0x131; // I -> ı
+ }
+ }
+ return(towlower(c));
+}
+
+static void GetC_unget(int c)
+{//==========================
+// This is only called with UTF8 input, not wchar input
+ if(f_input != NULL)
+ ungetc(c,f_input);
+ else
+ {
+ p_textinput--;
+ *p_textinput = c;
+ end_of_input = 0;
+ }
+}
+
+int Eof(void)
+{//==========
+ if(ungot_char != 0)
+ return(0);
+
+ if(f_input != 0)
+ return(feof(f_input));
+
+ return(end_of_input);
+}
+
+
+static int GetC_get(void)
+{//======================
+ int c;
+
+ if(f_input != NULL)
+ {
+ c = fgetc(f_input);
+ if(feof(f_input)) c = ' ';
+ return(c & 0xff);
+ }
+
+ if(option_multibyte == espeakCHARS_WCHAR)
+ {
+ if(*p_wchar_input == 0)
+ {
+ end_of_input = 1;
+ return(0);
+ }
+
+ if(!end_of_input)
+ return(*p_wchar_input++);
+ }
+ else
+ {
+ if(*p_textinput == 0)
+ {
+ end_of_input = 1;
+ return(0);
+ }
+
+ if(!end_of_input)
+ return(*p_textinput++ & 0xff);
+ }
+ return(0);
+}
+
+
+static int GetC(void)
+{//==================
+// Returns a unicode wide character
+// Performs UTF8 checking and conversion
+
+ int c;
+ int c1;
+ int c2;
+ int cbuf[4];
+ int ix;
+ int n_bytes;
+ unsigned char m;
+ static int ungot2 = 0;
+ static const unsigned char mask[4] = {0xff,0x1f,0x0f,0x07};
+ static const unsigned char mask2[4] = {0,0x80,0x20,0x30};
+
+ if((c1 = ungot_char) != 0)
+ {
+ ungot_char = 0;
+ return(c1);
+ }
+
+ if(ungot2 != 0)
+ {
+ c1 = ungot2;
+ ungot2 = 0;
+ }
+ else
+ {
+ c1 = GetC_get();
+ }
+
+ if(option_multibyte == espeakCHARS_WCHAR)
+ {
+ count_characters++;
+ return(c1); // wchar_t text
+ }
+
+ if((option_multibyte < 2) && (c1 & 0x80))
+ {
+ // multi-byte utf8 encoding, convert to unicode
+ n_bytes = 0;
+
+ if(((c1 & 0xe0) == 0xc0) && ((c1 & 0x1e) != 0))
+ n_bytes = 1;
+ else
+ if((c1 & 0xf0) == 0xe0)
+ n_bytes = 2;
+ else
+ if(((c1 & 0xf8) == 0xf0) && ((c1 & 0x0f) <= 4))
+ n_bytes = 3;
+
+ if((ix = n_bytes) > 0)
+ {
+ c = c1 & mask[ix];
+ m = mask2[ix];
+ while(ix > 0)
+ {
+ if((c2 = cbuf[ix] = GetC_get()) == 0)
+ {
+ if(option_multibyte==espeakCHARS_AUTO)
+ option_multibyte=espeakCHARS_8BIT; // change "auto" option to "no"
+ GetC_unget(' ');
+ break;
+ }
+
+ if((c2 & 0xc0) != 0x80)
+ {
+ // This is not UTF8. Change to 8-bit characterset.
+ if((n_bytes == 2) && (ix == 1))
+ ungot2 = cbuf[2];
+ GetC_unget(c2);
+ break;
+ }
+ m = 0x80;
+ c = (c << 6) + (c2 & 0x3f);
+ ix--;
+ }
+ if(ix == 0)
+ {
+ count_characters++;
+ return(c);
+ }
+ }
+ // top-bit-set character is not utf8, drop through to 8bit charset case
+ if((option_multibyte==espeakCHARS_AUTO) && !Eof())
+ option_multibyte=espeakCHARS_8BIT; // change "auto" option to "no"
+ }
+
+ // 8 bit character set, convert to unicode if
+ count_characters++;
+ if(c1 >= 0xa0)
+ return(translator->charset_a0[c1-0xa0]);
+ return(c1);
+} // end of GetC
+
+
+static void UngetC(int c)
+{//======================
+ ungot_char = c;
+}
+
+
+static const char *WordToString2(unsigned int word)
+{//================================================
+// Convert a language mnemonic word into a string
+ int ix;
+ static char buf[5];
+ char *p;
+
+ p = buf;
+ for(ix=3; ix>=0; ix--)
+ {
+ if((*p = word >> (ix*8)) != 0)
+ p++;
+ }
+ *p = 0;
+ return(buf);
+}
+
+
+static const char *LookupSpecial(Translator *tr, const char *string, char* text_out)
+{//=================================================================================
+ unsigned int flags[2];
+ char phonemes[55];
+ char phonemes2[55];
+ char *string1 = (char *)string;
+
+ if(LookupDictList(tr,&string1,phonemes,flags,0,NULL))
+ {
+ SetWordStress(tr, phonemes, flags[0], -1, 0);
+ DecodePhonemes(phonemes,phonemes2);
+ sprintf(text_out,"[[%s]]",phonemes2);
+ option_phoneme_input |= 2;
+ return(text_out);
+ }
+ return(NULL);
+}
+
+
+static const char *LookupCharName(Translator *tr, int c)
+{//=====================================================
+// Find the phoneme string (in ascii) to speak the name of character c
+// Used for punctuation characters and symbols
+
+ int ix;
+ unsigned int flags[2];
+ char single_letter[24];
+ char phonemes[60];
+ char phonemes2[60];
+ const char *lang_name = NULL;
+ char *string;
+ static char buf[60];
+
+ buf[0] = 0;
+ flags[0] = 0;
+ flags[1] = 0;
+ single_letter[0] = 0;
+ single_letter[1] = '_';
+ ix = utf8_out(c,&single_letter[2]);
+ single_letter[2+ix]=0;
+
+ string = &single_letter[1];
+ if(LookupDictList(tr, &string, phonemes, flags, 0, NULL) == 0)
+ {
+ // try _* then *
+ string = &single_letter[2];
+ if(LookupDictList(tr, &string, phonemes, flags, 0, NULL) == 0)
+ {
+ // now try the rules
+ single_letter[1] = ' ';
+ TranslateRules(tr, &single_letter[2], phonemes, sizeof(phonemes), NULL,0,NULL);
+ }
+ }
+
+ if((phonemes[0] == 0) && (tr->translator_name != L('e','n')))
+ {
+ // not found, try English
+ SetTranslator2("en");
+ string = &single_letter[1];
+ single_letter[1] = '_';
+ if(LookupDictList(translator2, &string, phonemes, flags, 0, NULL) == 0)
+ {
+ string = &single_letter[2];
+ LookupDictList(translator2, &string, phonemes, flags, 0, NULL);
+ }
+ if(phonemes[0])
+ {
+ lang_name = "en";
+ }
+ else
+ {
+ SelectPhonemeTable(voice->phoneme_tab_ix); // revert to original phoneme table
+ }
+ }
+
+ if(phonemes[0])
+ {
+ if(lang_name)
+ {
+ SetWordStress(translator2, phonemes, flags[0], -1, 0);
+ DecodePhonemes(phonemes,phonemes2);
+ sprintf(buf,"[[_^_%s %s _^_%s]]","en",phonemes2,WordToString2(tr->translator_name));
+ SelectPhonemeTable(voice->phoneme_tab_ix); // revert to original phoneme table
+ }
+ else
+ {
+ SetWordStress(tr, phonemes, flags[0], -1, 0);
+ DecodePhonemes(phonemes,phonemes2);
+ sprintf(buf,"[[%s]] ",phonemes2);
+ }
+ option_phoneme_input |= 2;
+ }
+ else
+ {
+ strcpy(buf,"[[(X1)(X1)(X1)]]");
+ option_phoneme_input |= 2;
+ }
+
+ return(buf);
+}
+
+int Read4Bytes(FILE *f)
+{//====================
+// Read 4 bytes (least significant first) into a word
+ int ix;
+ unsigned char c;
+ int acc=0;
+
+ for(ix=0; ix<4; ix++)
+ {
+ c = fgetc(f) & 0xff;
+ acc += (c << (ix*8));
+ }
+ return(acc);
+}
+
+
+static int LoadSoundFile(const char *fname, int index)
+{//===================================================
+ FILE *f;
+ char *p;
+ int *ip;
+ int length;
+ char fname_temp[100];
+ char fname2[sizeof(path_home)+13+40];
+
+ if(fname == NULL)
+ {
+ // filename is already in the table
+ fname = soundicon_tab[index].filename;
+ }
+
+ if(fname==NULL)
+ return(1);
+
+ if(fname[0] != '/')
+ {
+ // a relative path, look in espeak-data/soundicons
+ sprintf(fname2,"%s%csoundicons%c%s",path_home,PATHSEP,PATHSEP,fname);
+ fname = fname2;
+ }
+
+ f = NULL;
+#ifdef PLATFORM_POSIX
+ if((f = fopen(fname,"rb")) != NULL)
+ {
+ int ix;
+ int fd_temp;
+ const char *resample;
+ int header[3];
+ char command[sizeof(fname2)+sizeof(fname2)+40];
+
+ fseek(f,20,SEEK_SET);
+ for(ix=0; ix<3; ix++)
+ header[ix] = Read4Bytes(f);
+
+ // if the sound file is not mono, 16 bit signed, at the correct sample rate, then convert it
+ if((header[0] != 0x10001) || (header[1] != samplerate) || (header[2] != samplerate*2))
+ {
+ fclose(f);
+ f = NULL;
+
+ if(header[2] == samplerate)
+ resample = "";
+ else
+ resample = "polyphase";
+
+ strcpy(fname_temp,"/tmp/espeakXXXXXX");
+ if((fd_temp = mkstemp(fname_temp)) >= 0)
+ {
+ close(fd_temp);
+// sprintf(fname_temp,"%s.wav",tmpnam(NULL));
+ sprintf(command,"sox \"%s\" -r %d -w -s -c1 %s %s\n", fname, samplerate, fname_temp, resample);
+ if(system(command) == 0)
+ {
+ fname = fname_temp;
+ }
+ }
+ }
+ }
+#endif
+
+ if(f == NULL)
+ {
+ f = fopen(fname,"rb");
+ if(f == NULL)
+ {
+ fprintf(stderr,"Can't read temp file: %s\n",fname);
+ return(3);
+ }
+ }
+
+ length = GetFileLength(fname);
+ fseek(f,0,SEEK_SET);
+ if((p = (char *)realloc(soundicon_tab[index].data, length)) == NULL)
+ {
+ fclose(f);
+ return(4);
+ }
+ fread(p,length,1,f);
+ fclose(f);
+ remove(fname_temp);
+
+ ip = (int *)(&p[40]);
+ soundicon_tab[index].length = (*ip) / 2; // length in samples
+ soundicon_tab[index].data = p;
+ return(0);
+} // end of LoadSoundFile
+
+
+static int LookupSoundicon(int c)
+{//==============================
+// Find the sound icon number for a punctuation chatacter
+ int ix;
+
+ for(ix=N_SOUNDICON_SLOTS; ix<n_soundicon_tab; ix++)
+ {
+ if(soundicon_tab[ix].name == c)
+ {
+ if(soundicon_tab[ix].length == 0)
+ {
+ if(LoadSoundFile(NULL,ix)!=0)
+ return(-1); // sound file is not available
+ }
+ return(ix);
+ }
+ }
+ return(-1);
+}
+
+
+static int LoadSoundFile2(const char *fname)
+{//=========================================
+// Load a sound file into one of the reserved slots in the sound icon table
+// (if it'snot already loaded)
+
+ int ix;
+ static int slot = -1;
+
+ for(ix=0; ix<n_soundicon_tab; ix++)
+ {
+ if(((soundicon_tab[ix].filename != NULL) && strcmp(fname, soundicon_tab[ix].filename) == 0))
+ return(ix); // already loaded
+ }
+
+ // load the file into the next slot
+ slot++;
+ if(slot >= N_SOUNDICON_SLOTS)
+ slot = 0;
+
+ if(LoadSoundFile(fname, slot) != 0)
+ return(-1);
+
+ soundicon_tab[slot].filename = (char *)realloc(soundicon_tab[ix].filename, strlen(fname)+1);
+ strcpy(soundicon_tab[slot].filename, fname);
+ return(slot);
+}
+
+
+
+static int AnnouncePunctuation(Translator *tr, int c1, int c2, char *buf, int bufix)
+{//=================================================================================
+ // announce punctuation names
+ // c1: the punctuation character
+ // c2: the following character
+
+ int punct_count;
+ const char *punctname;
+ int found = 0;
+ int soundicon;
+ char *p;
+
+ if((soundicon = LookupSoundicon(c1)) >= 0)
+ {
+ // add an embedded command to play the soundicon
+ sprintf(&buf[bufix],"\001%dI ",soundicon);
+ UngetC(c2);
+ found = 1;
+ }
+ else
+ if((punctname = LookupCharName(tr, c1)) != NULL)
+ {
+ found = 1;
+ if(bufix==0)
+ {
+ punct_count=1;
+ while(c2 == c1)
+ {
+ punct_count++;
+ c2 = GetC();
+ }
+ UngetC(c2);
+
+ p = &buf[bufix];
+ if(punct_count==1)
+ {
+ sprintf(p,"%s %s %s",tone_punct_on,punctname,tone_punct_off);
+ }
+ else
+ if(punct_count < 4)
+ {
+ sprintf(p,"\001+10S%s",tone_punct_on);
+ while(punct_count-- > 0)
+ sprintf(buf,"%s %s",buf,punctname);
+ sprintf(p,"%s %s\001-10S",buf,tone_punct_off);
+ }
+ else
+ {
+ sprintf(p,"%s %s %d %s %s",
+ tone_punct_on,punctname,punct_count,punctname,tone_punct_off);
+ return(CLAUSE_COMMA);
+ }
+ }
+ else
+ {
+ // end the clause now and pick up the punctuation next time
+ UngetC(c2);
+ if(option_ssml)
+ {
+ if((c1 == '<') || (c1 == '&'))
+ ssml_ignore_l_angle = c1; // this was &lt; which was converted to <, don't pick it up again as <
+ }
+ ungot_char2 = c1;
+ buf[bufix] = ' ';
+ buf[bufix+1] = 0;
+ }
+ }
+
+ if(found == 0)
+ return(-1);
+
+ if(c1 == '-')
+ return(CLAUSE_NONE); // no pause
+ if((strchr_w(punct_close,c1) != NULL) && !iswalnum(c2))
+ return(CLAUSE_COLON);
+ if(iswspace(c2) && strchr_w(punct_stop,c1)!=NULL)
+ return(punct_attributes[lookupwchar(punct_chars,c1)]);
+
+ return(CLAUSE_SHORTCOMMA);
+} // end of AnnouncePunctuation
+
+#define SSML_SPEAK 1
+#define SSML_VOICE 2
+#define SSML_PROSODY 3
+#define SSML_SAYAS 4
+#define SSML_MARK 5
+#define SSML_SENTENCE 6
+#define SSML_PARAGRAPH 7
+#define SSML_PHONEME 8
+#define SSML_SUB 9
+#define SSML_STYLE 10
+#define SSML_AUDIO 11
+#define SSML_EMPHASIS 12
+#define SSML_BREAK 13
+#define SSML_IGNORE_TEXT 14
+#define HTML_BREAK 15
+#define SSML_CLOSE 0x10 // for a closing tag, OR this with the tag type
+
+// these tags have no effect if they are self-closing, eg. <voice />
+static char ignore_if_self_closing[] = {0,1,1,1,1,0,0,0,0,1,1,0,1,0,1,0,0};
+
+
+static MNEM_TAB ssmltags[] = {
+ {"speak", SSML_SPEAK},
+ {"voice", SSML_VOICE},
+ {"prosody", SSML_PROSODY},
+ {"say-as", SSML_SAYAS},
+ {"mark", SSML_MARK},
+ {"s", SSML_SENTENCE},
+ {"p", SSML_PARAGRAPH},
+ {"phoneme", SSML_PHONEME},
+ {"sub", SSML_SUB},
+ {"tts:style", SSML_STYLE},
+ {"audio", SSML_AUDIO},
+ {"emphasis", SSML_EMPHASIS},
+ {"break", SSML_BREAK},
+ {"metadata", SSML_IGNORE_TEXT},
+
+ {"br", HTML_BREAK},
+ {"li", HTML_BREAK},
+ {"img", HTML_BREAK},
+ {"td", HTML_BREAK},
+ {"h1", SSML_PARAGRAPH},
+ {"h2", SSML_PARAGRAPH},
+ {"h3", SSML_PARAGRAPH},
+ {"h4", SSML_PARAGRAPH},
+ {"hr", SSML_PARAGRAPH},
+ {"script", SSML_IGNORE_TEXT},
+ {"style", SSML_IGNORE_TEXT},
+ {NULL,0}};
+
+
+
+
+static const char *VoiceFromStack()
+{//================================
+// Use the voice properties from the SSML stack to choose a voice, and switch
+// to that voice if it's not the current voice
+ int ix;
+ SSML_STACK *sp;
+ const char *v_id;
+ int voice_name_specified;
+ int voice_found;
+ espeak_VOICE voice_select;
+ char voice_name[40];
+ char language[40];
+
+ strcpy(voice_name,ssml_stack[0].voice_name);
+ strcpy(language,ssml_stack[0].language);
+ voice_select.age = ssml_stack[0].voice_age;
+ voice_select.gender = ssml_stack[0].voice_gender;
+ voice_select.variant = ssml_stack[0].voice_variant;
+ voice_select.identifier = NULL;
+
+ for(ix=0; ix<n_ssml_stack; ix++)
+ {
+ sp = &ssml_stack[ix];
+ voice_name_specified = 0;
+
+ if((sp->voice_name[0] != 0) && (SelectVoiceByName(NULL,sp->voice_name) != NULL))
+ {
+ voice_name_specified = 1;
+ strcpy(voice_name, sp->voice_name);
+ language[0] = 0;
+ voice_select.gender = 0;
+ voice_select.age = 0;
+ voice_select.variant = 0;
+ }
+ if(sp->language[0] != 0)
+ {
+ strcpy(language, sp->language);
+ if(voice_name_specified == 0)
+ voice_name[0] = 0; // forget a previous voice name if a language is specified
+ }
+ if(sp->voice_gender != 0)
+ voice_select.gender = sp->voice_gender;
+ if(sp->voice_age != 0)
+ voice_select.age = sp->voice_age;
+ if(sp->voice_variant != 0)
+ voice_select.variant = sp->voice_variant;
+ }
+
+ voice_select.name = voice_name;
+ voice_select.languages = language;
+ v_id = SelectVoice(&voice_select, &voice_found);
+ if(v_id == NULL)
+ return("default");
+ return(v_id);
+} // end of VoiceFromStack
+
+
+
+static void ProcessParamStack(char *outbuf, int &outix)
+{//====================================================
+// Set the speech parameters from the parameter stack
+ int param;
+ int ix;
+ int value;
+ char buf[20];
+ int new_parameters[N_SPEECH_PARAM];
+ static char cmd_letter[N_SPEECH_PARAM] = {0, 'S','A','P','R', 0, 0, 0, 0, 0, 0, 0, 'F'}; // embedded command letters
+
+
+ for(param=0; param<N_SPEECH_PARAM; param++)
+ new_parameters[param] = -1;
+
+ for(ix=0; ix<n_param_stack; ix++)
+ {
+ for(param=0; param<N_SPEECH_PARAM; param++)
+ {
+ if(param_stack[ix].parameter[param] >= 0)
+ new_parameters[param] = param_stack[ix].parameter[param];
+ }
+ }
+
+ for(param=0; param<N_SPEECH_PARAM; param++)
+ {
+ if((value = new_parameters[param]) != speech_parameters[param])
+ {
+ buf[0] = 0;
+
+ switch(param)
+ {
+ case espeakPUNCTUATION:
+ option_punctuation = value-1;
+ break;
+
+ case espeakCAPITALS:
+ option_capitals = value;
+ break;
+
+ case espeakRATE:
+ case espeakVOLUME:
+ case espeakPITCH:
+ case espeakRANGE:
+ case espeakEMPHASIS:
+ sprintf(buf,"%c%d%c",CTRL_EMBEDDED,value,cmd_letter[param]);
+ break;
+ }
+
+ speech_parameters[param] = new_parameters[param];
+ strcpy(&outbuf[outix],buf);
+ outix += strlen(buf);
+ }
+ }
+} // end of ProcessParamStack
+
+
+static PARAM_STACK *PushParamStack(int tag_type)
+{//=============================================
+ int ix;
+ PARAM_STACK *sp;
+
+ sp = &param_stack[n_param_stack];
+ if(n_param_stack < (N_PARAM_STACK-1))
+ n_param_stack++;
+
+ sp->type = tag_type;
+ for(ix=0; ix<N_SPEECH_PARAM; ix++)
+ {
+ sp->parameter[ix] = -1;
+ }
+ return(sp);
+} // end of PushParamStack
+
+
+static void PopParamStack(int tag_type, char *outbuf, int &outix)
+{//==============================================================
+ // unwind the stack up to and including the previous tag of this type
+ int ix;
+ int top = 0;
+
+ if(tag_type >= SSML_CLOSE)
+ tag_type -= SSML_CLOSE;
+
+ for(ix=0; ix<n_param_stack; ix++)
+ {
+ if(param_stack[ix].type == tag_type)
+ {
+ top = ix;
+ }
+ }
+ if(top > 0)
+ {
+ n_param_stack = top;
+ }
+ ProcessParamStack(outbuf, outix);
+} // end of PopParamStack
+
+
+
+static wchar_t *GetSsmlAttribute(wchar_t *pw, const char *name)
+{//============================================================
+// Gets the value string for an attribute.
+// Returns NULL if the attribute is not present
+ int ix;
+ static wchar_t empty[1] = {0};
+
+ while(*pw != 0)
+ {
+ if(iswspace(pw[-1]))
+ {
+ ix = 0;
+ while(*pw == name[ix])
+ {
+ pw++;
+ ix++;
+ }
+ if(name[ix]==0)
+ {
+ // found the attribute, now get the value
+ while(iswspace(*pw)) pw++;
+ if(*pw == '=') pw++;
+ while(iswspace(*pw)) pw++;
+ if(*pw == '"')
+ return(pw+1);
+ else
+ return(empty);
+ }
+ }
+ pw++;
+ }
+ return(NULL);
+} // end of GetSsmlAttribute
+
+
+static int attrcmp(const wchar_t *string1, const char *string2)
+{//============================================================
+ int ix;
+
+ if(string1 == NULL)
+ return(1);
+
+ for(ix=0; (string1[ix] == string2[ix]) && (string1[ix] != 0); ix++)
+ {
+ }
+ if((string1[ix]=='"') && (string2[ix]==0))
+ return(0);
+ return(1);
+}
+
+
+static int attrlookup(const wchar_t *string1, const MNEM_TAB *mtab)
+{//================================================================
+ int ix;
+
+ for(ix=0; mtab[ix].mnem != NULL; ix++)
+ {
+ if(attrcmp(string1,mtab[ix].mnem) == 0)
+ return(mtab[ix].value);
+ }
+ return(mtab[ix].value);
+}
+
+
+static int attrnumber(const wchar_t *pw, int default_value, int type)
+{//==================================================================
+ int value = 0;
+
+ if((pw == NULL) || !isdigit(*pw))
+ return(default_value);
+
+ while(isdigit(*pw))
+ {
+ value = value*10 + *pw++ - '0';
+ }
+ if((type==1) && (towlower(*pw)=='s'))
+ {
+ // time: seconds rather than ms
+ value *= 1000;
+ }
+ return(value);
+} // end of attrnumber
+
+
+
+static int attrcopy_utf8(char *buf, const wchar_t *pw, int len)
+{//============================================================
+// Convert attribute string into utf8, write to buf, and return its utf8 length
+ unsigned int c;
+ int ix = 0;
+ int n;
+ int prev_c = 0;
+
+ if(pw != NULL)
+ {
+ while((ix < (len-4)) && ((c = *pw++) != 0))
+ {
+ if((c=='"') && (prev_c != '\\'))
+ break; // " indicates end of attribute, unless preceded by backstroke
+ n = utf8_out(c,&buf[ix]);
+ ix += n;
+ prev_c = c;
+ }
+ }
+ buf[ix] = 0;
+ return(ix);
+} // end of attrcopy_utf8
+
+
+
+static int attr_prosody_value(int param_type, const wchar_t *pw, int *value_out)
+{//=============================================================================
+ int sign = 0;
+ wchar_t *tail;
+ float value;
+
+ while(iswspace(*pw)) pw++;
+ if(*pw == '+')
+ {
+ pw++;
+ sign = 1;
+ }
+ if(*pw == '-')
+ {
+ pw++;
+ sign = -1;
+ }
+ value = (float)wcstod(pw,&tail);
+ if(tail == pw)
+ {
+ // failed to find a number, return 100%
+ *value_out = 100;
+ return(2);
+ }
+
+ if(*tail == '%')
+ {
+ if(sign != 0)
+ value = 100 + (sign * value);
+ *value_out = (int)value;
+ return(2); // percentage
+ }
+
+ if((tail[0]=='s') && (tail[1]=='t'))
+ {
+ double x;
+ // convert from semitones to a frequency percentage
+ x = pow(double(2.0),double((value*sign)/12)) * 100;
+ *value_out = (int)x;
+ return(2); // percentage
+ }
+
+ if(param_type == espeakRATE)
+ {
+ *value_out = (int)(value * 100);
+ return(2); // percentage
+ }
+
+ *value_out = (int)value;
+ return(sign); // -1, 0, or 1
+} // end of attr_prosody_value
+
+
+int AddNameData(const char *name, int wide)
+{//========================================
+// Add the name to the namedata and return its position
+// (Used by the Windows SAPI wrapper)
+ int ix;
+ int len;
+ void *vp;
+
+ if(wide)
+ {
+ len = (wcslen((const wchar_t *)name)+1)*sizeof(wchar_t);
+ n_namedata = (n_namedata + sizeof(wchar_t) - 1) % sizeof(wchar_t); // round to wchar_t boundary
+ }
+ else
+ {
+ len = strlen(name)+1;
+ }
+
+ if(namedata_ix+len >= n_namedata)
+ {
+ // allocate more space for marker names
+ if((vp = realloc(namedata, namedata_ix+len + 300)) == NULL)
+ return(-1); // failed to allocate, original data is unchanged but ignore this new name
+
+ namedata = (char *)vp;
+ n_namedata = namedata_ix+len + 300;
+ }
+ memcpy(&namedata[ix = namedata_ix],name,len);
+ namedata_ix += len;
+ return(ix);
+} // end of AddNameData
+
+
+void SetVoiceStack(espeak_VOICE *v)
+{//================================
+ SSML_STACK *sp;
+ sp = &ssml_stack[0];
+
+ if(v == NULL)
+ {
+ memset(sp,0,sizeof(ssml_stack[0]));
+ return;
+ }
+ if(v->languages != NULL)
+ strcpy(sp->language,v->languages);
+ if(v->name != NULL)
+ strcpy(sp->voice_name,v->name);
+ sp->voice_variant = v->variant;
+ sp->voice_age = v->age;
+ sp->voice_gender = v->gender;
+}
+
+
+static int GetVoiceAttributes(wchar_t *pw, int tag_type)
+{//=====================================================
+// Determines whether voice attribute are specified in this tag, and if so, whether this means
+// a voice change.
+// If it's a closing tag, delete the top frame of the stack and determine whether this implies
+// a voice change.
+// Returns CLAUSE_BIT_VOICE if there is a voice change
+
+ wchar_t *lang;
+ wchar_t *gender;
+ wchar_t *name;
+ wchar_t *age;
+ wchar_t *variant;
+ const char *new_voice_id;
+ SSML_STACK *ssml_sp;
+
+ static const MNEM_TAB mnem_gender[] = {
+ {"male", 1},
+ {"female", 2},
+ {"neutral", 3},
+ {NULL, 0}};
+
+ if(tag_type & SSML_CLOSE)
+ {
+ // delete a stack frame
+ if(n_ssml_stack > 1)
+ {
+ n_ssml_stack--;
+ }
+ }
+ else
+ {
+ // add a stack frame if any voice details are specified
+ lang = GetSsmlAttribute(pw,"xml:lang");
+
+ if(tag_type != SSML_VOICE)
+ {
+ // only expect an xml:lang attribute
+ name = NULL;
+ variant = NULL;
+ age = NULL;
+ gender = NULL;
+ }
+ else
+ {
+ name = GetSsmlAttribute(pw,"name");
+ variant = GetSsmlAttribute(pw,"variant");
+ age = GetSsmlAttribute(pw,"age");
+ gender = GetSsmlAttribute(pw,"gender");
+ }
+
+ if((tag_type != SSML_VOICE) && (lang==NULL))
+ return(0); // <s> or <p> without language spec, nothing to do
+
+ ssml_sp = &ssml_stack[n_ssml_stack++];
+
+ attrcopy_utf8(ssml_sp->language,lang,sizeof(ssml_sp->language));
+ attrcopy_utf8(ssml_sp->voice_name,name,sizeof(ssml_sp->voice_name));
+ ssml_sp->voice_variant = attrnumber(variant,1,0)-1;
+ ssml_sp->voice_age = attrnumber(age,0,0);
+ ssml_sp->voice_gender = attrlookup(gender,mnem_gender);
+ ssml_sp->tag_type = tag_type;
+ }
+
+ new_voice_id = VoiceFromStack();
+ if(strcmp(new_voice_id,current_voice_id) != 0)
+ {
+ // add an embedded command to change the voice
+ strcpy(current_voice_id,new_voice_id);
+ return(CLAUSE_BIT_VOICE); // change of voice
+ }
+
+ return(0);
+} // end of GetVoiceAttributes
+
+
+static void SetProsodyParameter(int param_type, wchar_t *attr1, PARAM_STACK *sp)
+{//=============================================================================
+ int value;
+ int sign;
+
+ static const MNEM_TAB mnem_volume[] = {
+ {"default",100},
+ {"silent",0},
+ {"x-soft",30},
+ {"soft",65},
+ {"medium",100},
+ {"loud",150},
+ {"x-loud",230},
+ {NULL, -1}};
+
+ static const MNEM_TAB mnem_rate[] = {
+ {"default",100},
+ {"x-slow",60},
+ {"slow",80},
+ {"medium",100},
+ {"fast",120},
+ {"x-fast",150},
+ {NULL, -1}};
+
+ static const MNEM_TAB mnem_pitch[] = {
+ {"default",100},
+ {"x-low",70},
+ {"low",85},
+ {"medium",100},
+ {"high",110},
+ {"x-high",120},
+ {NULL, -1}};
+
+ static const MNEM_TAB mnem_range[] = {
+ {"default",100},
+ {"x-low",20},
+ {"low",50},
+ {"medium",100},
+ {"high",140},
+ {"x-high",180},
+ {NULL, -1}};
+
+ static const MNEM_TAB *mnem_tabs[5] = {
+ NULL, mnem_rate, mnem_volume, mnem_pitch, mnem_range };
+
+
+ if((value = attrlookup(attr1,mnem_tabs[param_type])) >= 0)
+ {
+ // mnemonic specifies a value as a percentage of the base pitch/range/rate/volume
+ sp->parameter[param_type] = (param_stack[0].parameter[param_type] * value)/100;
+ }
+ else
+ {
+ sign = attr_prosody_value(param_type,attr1,&value);
+
+ if(sign == 0)
+ sp->parameter[param_type] = value; // absolute value in Hz
+ else
+ if(sign == 2)
+ {
+ // change specified as percentage or in semitones
+ sp->parameter[param_type] = (speech_parameters[param_type] * value)/100;
+ }
+ else
+ {
+ // change specified as plus or minus Hz
+ sp->parameter[param_type] = speech_parameters[param_type] + (value*sign);
+ }
+ }
+} // end of SetProsodyParemeter
+
+
+
+static int ProcessSsmlTag(wchar_t *xml_buf, char *outbuf, int &outix, int n_outbuf, int self_closing)
+{//==================================================================================================
+// xml_buf is the tag and attributes with a zero terminator in place of the original '>'
+// returns a clause terminator value.
+
+ unsigned int ix;
+ int index;
+ int c;
+ int tag_type;
+ int value;
+ int value2;
+ int value3;
+ int voice_change_flag;
+ wchar_t *px;
+ wchar_t *attr1;
+ wchar_t *attr2;
+ wchar_t *attr3;
+ int terminator;
+ char *uri;
+ int param_type;
+ char tag_name[40];
+ char buf[80];
+ PARAM_STACK *sp;
+ SSML_STACK *ssml_sp;
+
+ static const MNEM_TAB mnem_punct[] = {
+ {"none", 1},
+ {"all", 2},
+ {"some", 3},
+ {NULL, -1}};
+
+ static const MNEM_TAB mnem_capitals[] = {
+ {"no", 0},
+ {"spelling", 2},
+ {"icon", 1},
+ {"pitch", 20}, // this is the amount by which to raise the pitch
+ {NULL, -1}};
+
+ static const MNEM_TAB mnem_interpret_as[] = {
+ {"characters",SAYAS_CHARS},
+ {"tts:char",SAYAS_SINGLE_CHARS},
+ {"tts:key",SAYAS_KEY},
+ {"tts:digits",SAYAS_DIGITS},
+ {"telephone",SAYAS_DIGITS1},
+ {NULL, -1}};
+
+ static const MNEM_TAB mnem_sayas_format[] = {
+ {"glyphs",1},
+ {NULL, -1}};
+
+ static const MNEM_TAB mnem_break[] = {
+ {"none",0},
+ {"x-weak",1},
+ {"weak",2},
+ {"medium",3},
+ {"strong",4},
+ {"x-strong",5},
+ {NULL,-1}};
+
+ static const MNEM_TAB mnem_emphasis[] = {
+ {"none",1},
+ {"reduced",2},
+ {"moderate",3},
+ {"strong",4},
+ {NULL,-1}};
+
+ static const char *prosody_attr[5] = {
+ NULL, "rate", "volume", "pitch", "range" };
+
+ for(ix=0; ix<(sizeof(tag_name)-1); ix++)
+ {
+ if(((c = xml_buf[ix]) == 0) || iswspace(c))
+ break;
+ tag_name[ix] = tolower((char)c);
+ }
+ tag_name[ix] = 0;
+
+ px = &xml_buf[ix]; // the tag's attributes
+
+ if(tag_name[0] == '/')
+ {
+ tag_type = LookupMnem(ssmltags,&tag_name[1]) + SSML_CLOSE; // closing tag
+ }
+ else
+ {
+ tag_type = LookupMnem(ssmltags,tag_name);
+
+ if(self_closing && ignore_if_self_closing[tag_type])
+ return(0);
+ }
+
+ voice_change_flag = 0;
+ terminator = CLAUSE_NONE;
+ ssml_sp = &ssml_stack[n_ssml_stack-1];
+
+ switch(tag_type)
+ {
+ case SSML_STYLE:
+ sp = PushParamStack(tag_type);
+ attr1 = GetSsmlAttribute(px,"field");
+ attr2 = GetSsmlAttribute(px,"mode");
+
+
+ if(attrcmp(attr1,"punctuation")==0)
+ {
+ value = attrlookup(attr2,mnem_punct);
+ sp->parameter[espeakPUNCTUATION] = value;
+ }
+ else
+ if(attrcmp(attr1,"capital_letters")==0)
+ {
+ value = attrlookup(attr2,mnem_capitals);
+ sp->parameter[espeakCAPITALS] = value;
+ }
+ ProcessParamStack(outbuf, outix);
+ break;
+
+ case SSML_PROSODY:
+ sp = PushParamStack(tag_type);
+
+ // look for attributes: rate, volume, pitch, range
+ for(param_type=espeakRATE; param_type <= espeakRANGE; param_type++)
+ {
+ if((attr1 = GetSsmlAttribute(px,prosody_attr[param_type])) != NULL)
+ {
+ SetProsodyParameter(param_type, attr1, sp);
+ }
+ }
+
+ ProcessParamStack(outbuf, outix);
+ break;
+
+ case SSML_EMPHASIS:
+ sp = PushParamStack(tag_type);
+ value = 3; // default is "moderate"
+ if((attr1 = GetSsmlAttribute(px,"level")) != NULL)
+ {
+ value = attrlookup(attr1,mnem_emphasis);
+ }
+
+ if(translator->langopts.tone_language == 1)
+ {
+ static unsigned char emphasis_to_pitch_range[] = {50,50,40,70,90,90};
+ static unsigned char emphasis_to_volume[] = {100,100,70,110,140,140};
+ // tone language (eg.Chinese) do emphasis by increasing the pitch range.
+ sp->parameter[espeakRANGE] = emphasis_to_pitch_range[value];
+ sp->parameter[espeakVOLUME] = emphasis_to_volume[value];
+ }
+ else
+ {
+ sp->parameter[espeakEMPHASIS] = value;
+ }
+ ProcessParamStack(outbuf, outix);
+ break;
+
+ case SSML_STYLE + SSML_CLOSE:
+ case SSML_PROSODY + SSML_CLOSE:
+ case SSML_EMPHASIS + SSML_CLOSE:
+ PopParamStack(tag_type, outbuf, outix);
+ break;
+
+ case SSML_SAYAS:
+ attr1 = GetSsmlAttribute(px,"interpret-as");
+ attr2 = GetSsmlAttribute(px,"format");
+ attr3 = GetSsmlAttribute(px,"detail");
+ value = attrlookup(attr1,mnem_interpret_as);
+ value2 = attrlookup(attr2,mnem_sayas_format);
+ if(value2 == 1)
+ value = SAYAS_GLYPHS;
+
+ value3 = attrnumber(attr3,0,0);
+
+ if(value == SAYAS_DIGITS)
+ {
+ if(value3 <= 1)
+ value = SAYAS_DIGITS1;
+ else
+ value = SAYAS_DIGITS + value3;
+ }
+
+ sprintf(buf,"%c%dY",CTRL_EMBEDDED,value);
+ strcpy(&outbuf[outix],buf);
+ outix += strlen(buf);
+
+ sayas_mode = value; // punctuation doesn't end clause during SAY-AS
+ break;
+
+ case SSML_SAYAS + SSML_CLOSE:
+ outbuf[outix++] = CTRL_EMBEDDED;
+ outbuf[outix++] = 'Y';
+ sayas_mode = 0;
+ break;
+
+ case SSML_SUB:
+ if((attr1 = GetSsmlAttribute(px,"alias")) != NULL)
+ {
+ // use the alias rather than the text
+ ignore_text = 1;
+ outix += attrcopy_utf8(&outbuf[outix],attr1,n_outbuf-outix);
+ }
+ break;
+
+ case SSML_IGNORE_TEXT:
+ ignore_text = 1;
+ break;
+
+ case SSML_SUB + SSML_CLOSE:
+ case SSML_IGNORE_TEXT + SSML_CLOSE:
+ ignore_text = 0;
+ break;
+
+ case SSML_MARK:
+ if((attr1 = GetSsmlAttribute(px,"name")) != NULL)
+ {
+ // add name to circular buffer of marker names
+ attrcopy_utf8(buf,attr1,sizeof(buf));
+
+ if(strcmp(skip_marker,buf)==0)
+ {
+ // This is the marker we are waiting for before starting to speak
+ clear_skipping_text = 1;
+ skip_marker[0] = 0;
+ return(CLAUSE_NONE);
+ }
+
+ if((index = AddNameData(buf,0)) >= 0)
+ {
+ sprintf(buf,"%c%dM",CTRL_EMBEDDED,index);
+ strcpy(&outbuf[outix],buf);
+ outix += strlen(buf);
+ }
+ }
+ break;
+
+ case SSML_AUDIO:
+ sp = PushParamStack(tag_type);
+
+ if((attr1 = GetSsmlAttribute(px,"src")) != NULL)
+ {
+ char fname[256];
+ attrcopy_utf8(buf,attr1,sizeof(buf));
+
+ if(uri_callback == NULL)
+ {
+ if((xmlbase != NULL) && (buf[0] != '/'))
+ {
+ sprintf(fname,"%s/%s",xmlbase,buf);
+ index = LoadSoundFile2(fname);
+ }
+ else
+ {
+ index = LoadSoundFile2(buf);
+ }
+ if(index >= 0)
+ {
+ sprintf(buf,"%c%dI",CTRL_EMBEDDED,index);
+ strcpy(&outbuf[outix],buf);
+ outix += strlen(buf);
+ sp->parameter[espeakSILENCE] = 1;
+ }
+ }
+ else
+ {
+ if((index = AddNameData(buf,0)) >= 0)
+ {
+ uri = &namedata[index];
+ if(uri_callback(1,uri,xmlbase) == 0)
+ {
+ sprintf(buf,"%c%dU",CTRL_EMBEDDED,index);
+ strcpy(&outbuf[outix],buf);
+ outix += strlen(buf);
+ sp->parameter[espeakSILENCE] = 1;
+ }
+ }
+ }
+ }
+ ProcessParamStack(outbuf, outix);
+
+ if(self_closing)
+ PopParamStack(tag_type, outbuf, outix);
+ return(CLAUSE_NONE);
+
+ case SSML_AUDIO + SSML_CLOSE:
+ PopParamStack(tag_type, outbuf, outix);
+ return(CLAUSE_NONE);
+
+ case SSML_BREAK:
+ value = 21;
+ terminator = CLAUSE_NONE;
+
+ if((attr1 = GetSsmlAttribute(px,"strength")) != NULL)
+ {
+ static int break_value[6] = {0,7,14,21,40,80}; // *10mS
+ value = attrlookup(attr1,mnem_break);
+ if(value < 3)
+ {
+ // adjust prepause on the following word
+ sprintf(&outbuf[outix],"%c%dB",CTRL_EMBEDDED,value);
+ outix += 3;
+ terminator = 0;
+ }
+ value = break_value[value];
+ }
+ if((attr2 = GetSsmlAttribute(px,"time")) != NULL)
+ {
+ value = (attrnumber(attr2,0,1) * 25) / speed.speed_factor1; // compensate for speaking speed to keep constant pause length
+
+ if(terminator == 0)
+ terminator = CLAUSE_NONE;
+ }
+ if(terminator)
+ {
+ if(value > 0xfff)
+ value = 0xfff;
+ return(terminator + value);
+ }
+ break;
+
+ case SSML_SPEAK:
+ if((attr1 = GetSsmlAttribute(px,"xml:base")) != NULL)
+ {
+ attrcopy_utf8(buf,attr1,sizeof(buf));
+ if((index = AddNameData(buf,0)) >= 0)
+ {
+ xmlbase = &namedata[index];
+ }
+ }
+ if(GetVoiceAttributes(px, tag_type) == 0)
+ return(0); // no voice change
+ return(CLAUSE_VOICE);
+
+ case SSML_VOICE:
+ if(GetVoiceAttributes(px, tag_type) == 0)
+ return(0); // no voice change
+ return(CLAUSE_VOICE);
+
+ case SSML_SPEAK + SSML_CLOSE:
+ // unwind stack until the previous <voice> or <speak> tag
+ while((n_ssml_stack > 1) && (ssml_stack[n_ssml_stack-1].tag_type != SSML_SPEAK))
+ {
+ n_ssml_stack--;
+ }
+ return(CLAUSE_PERIOD + GetVoiceAttributes(px, tag_type));
+
+ case SSML_VOICE + SSML_CLOSE:
+ // unwind stack until the previous <voice> or <speak> tag
+ while((n_ssml_stack > 1) && (ssml_stack[n_ssml_stack-1].tag_type != SSML_VOICE))
+ {
+ n_ssml_stack--;
+ }
+
+terminator=0; // ?? Sentence intonation, but no pause ??
+ return(terminator + GetVoiceAttributes(px, tag_type));
+
+ case HTML_BREAK:
+ case HTML_BREAK + SSML_CLOSE:
+ return(CLAUSE_COLON);
+
+ case SSML_SENTENCE:
+ if(ssml_sp->tag_type == SSML_SENTENCE)
+ {
+ // new sentence implies end-of-sentence
+ voice_change_flag = GetVoiceAttributes(px, SSML_SENTENCE+SSML_CLOSE);
+ }
+ voice_change_flag |= GetVoiceAttributes(px, tag_type);
+ return(CLAUSE_PARAGRAPH + voice_change_flag);
+
+
+ case SSML_PARAGRAPH:
+ if(ssml_sp->tag_type == SSML_SENTENCE)
+ {
+ // new paragraph implies end-of-sentence or end-of-paragraph
+ voice_change_flag = GetVoiceAttributes(px, SSML_SENTENCE+SSML_CLOSE);
+ }
+ if(ssml_sp->tag_type == SSML_PARAGRAPH)
+ {
+ // new paragraph implies end-of-sentence or end-of-paragraph
+ voice_change_flag |= GetVoiceAttributes(px, SSML_PARAGRAPH+SSML_CLOSE);
+ }
+ voice_change_flag |= GetVoiceAttributes(px, tag_type);
+ return(CLAUSE_PARAGRAPH + voice_change_flag);
+
+
+ case SSML_SENTENCE + SSML_CLOSE:
+ if(ssml_sp->tag_type == SSML_SENTENCE)
+ {
+ // end of a sentence which specified a language
+ voice_change_flag = GetVoiceAttributes(px, tag_type);
+ }
+ return(CLAUSE_PERIOD + voice_change_flag);
+
+
+ case SSML_PARAGRAPH + SSML_CLOSE:
+ if((ssml_sp->tag_type == SSML_SENTENCE) || (ssml_sp->tag_type == SSML_PARAGRAPH))
+ {
+ // End of a paragraph which specified a language.
+ // (End-of-paragraph also implies end-of-sentence)
+ return(GetVoiceAttributes(px, tag_type) + CLAUSE_PARAGRAPH);
+ }
+ return(CLAUSE_PARAGRAPH);
+ }
+ return(0);
+} // end of ProcessSsmlTag
+
+
+static MNEM_TAB xml_char_mnemonics[] = {
+ {"gt",'>'},
+ {"lt",'<'},
+ {"amp", '&'},
+ {"quot", '"'},
+ {"nbsp", ' '},
+ {"apos", '\''},
+ {NULL,-1}};
+
+
+int ReadClause(Translator *tr, FILE *f_in, char *buf, short *charix, int n_buf, int *tone_type)
+{//============================================================================================
+/* Find the end of the current clause.
+ Write the clause into buf
+
+ returns: clause type (bits 0-7: pause x10mS, bits 8-11 intonation type)
+
+ Also checks for blank line (paragraph) as end-of-clause indicator.
+
+ Does not end clause for:
+ punctuation immediately followed by alphanumeric eg. 1.23 !Speak :path
+ repeated punctuation, eg. ... !!!
+*/
+ int c1=' '; // current character
+ int c2; // next character
+ int cprev=' '; // previous character
+ int parag;
+ int ix = 0;
+ int j;
+ int nl_count;
+ int linelength = 0;
+ int phoneme_mode = 0;
+ int n_xml_buf;
+ int terminator;
+ int punct;
+ int found;
+ int any_alnum = 0;
+ int self_closing;
+ int punct_data;
+ int stressed_word = 0;
+ const char *p;
+ wchar_t xml_buf[N_XML_BUF+1];
+
+#define N_XML_BUF2 20
+ char xml_buf2[N_XML_BUF2+2]; // for &<name> and &<number> sequences
+ static char ungot_string[N_XML_BUF2+4];
+ static int ungot_string_ix = -1;
+
+ if(clear_skipping_text)
+ {
+ skipping_text = 0;
+ clear_skipping_text = 0;
+ }
+
+ tr->clause_upper_count = 0;
+ tr->clause_lower_count = 0;
+ end_of_input = 0;
+ *tone_type = 0;
+
+f_input = f_in; // for GetC etc
+
+ if(ungot_word != NULL)
+ {
+ strcpy(buf,ungot_word);
+ ix += strlen(ungot_word);
+ ungot_word = NULL;
+ }
+
+ if(ungot_char2 != 0)
+ {
+ c2 = ungot_char2;
+ }
+ else
+ {
+ c2 = GetC();
+ }
+
+ while(!Eof() || (ungot_char != 0) || (ungot_char2 != 0) || (ungot_string_ix >= 0))
+ {
+ if(!iswalnum(c1))
+ {
+ if((end_character_position > 0) && (count_characters > end_character_position))
+ {
+ end_of_input = 1;
+ return(CLAUSE_EOF);
+ }
+
+ if((skip_characters > 0) && (count_characters > skip_characters))
+ {
+ // reached the specified start position
+ // don't break a word
+ clear_skipping_text = 1;
+ skip_characters = 0;
+ UngetC(c2);
+ return(CLAUSE_NONE);
+ }
+ }
+
+ cprev = c1;
+ c1 = c2;
+
+ if(ungot_string_ix >= 0)
+ {
+ if(ungot_string[ungot_string_ix] == 0)
+ ungot_string_ix = -1;
+ }
+
+ if((ungot_string_ix == 0) && (ungot_char2 == 0))
+ {
+ c1 = ungot_string[ungot_string_ix++];
+ }
+ if(ungot_string_ix >= 0)
+ {
+ c2 = ungot_string[ungot_string_ix++];
+ }
+ else
+ {
+ c2 = GetC();
+
+ if(Eof())
+ {
+ c2 = ' ';
+ }
+ }
+ ungot_char2 = 0;
+
+ if((option_ssml) && (phoneme_mode==0))
+ {
+ if((ssml_ignore_l_angle != '&') && (c1 == '&') && ((c2=='#') || ((c2 >= 'a') && (c2 <= 'z'))))
+ {
+ n_xml_buf = 0;
+ c1 = c2;
+ while(!Eof() && (iswalnum(c1) || (c1=='#')) && (n_xml_buf < N_XML_BUF2))
+ {
+ xml_buf2[n_xml_buf++] = c1;
+ c1 = GetC();
+ }
+ xml_buf2[n_xml_buf] = 0;
+ c2 = GetC();
+ sprintf(ungot_string,"%s%c%c",&xml_buf2[0],c1,c2);
+
+ if(c1 == ';')
+ {
+ if(xml_buf2[0] == '#')
+ {
+ // character code number
+ if(xml_buf2[1] == 'x')
+ found = sscanf(&xml_buf2[2],"%x",(unsigned int *)(&c1));
+ else
+ found = sscanf(&xml_buf2[1],"%d",&c1);
+ }
+ else
+ {
+ if((found = LookupMnem(xml_char_mnemonics,xml_buf2)) != -1)
+ {
+ c1 = found;
+ if(c2 == 0)
+ c2 = ' ';
+ }
+ }
+ }
+ else
+ {
+ found = -1;
+ }
+
+ if(found <= 0)
+ {
+ ungot_string_ix = 0;
+ c1 = '&';
+ c2 = ' ';
+ }
+
+ if((c1 <= 0x20) && ((sayas_mode == SAYAS_SINGLE_CHARS) || (sayas_mode == SAYAS_KEY)))
+ {
+ c1 += 0xe000; // move into unicode private usage area
+ }
+ }
+ else
+ if((c1 == '<') && (ssml_ignore_l_angle != '<'))
+ {
+ // SSML Tag
+ n_xml_buf = 0;
+ c1 = c2;
+ while(!Eof() && (c1 != '>') && (n_xml_buf < N_XML_BUF))
+ {
+ xml_buf[n_xml_buf++] = c1;
+ c1 = GetC();
+ }
+ xml_buf[n_xml_buf] = 0;
+ c2 = ' ';
+
+ buf[ix++] = ' ';
+
+ self_closing = 0;
+ if(xml_buf[n_xml_buf-1] == '/')
+ {
+ // a self-closing tag
+ xml_buf[n_xml_buf-1] = ' ';
+ self_closing = 1;
+ }
+
+ terminator = ProcessSsmlTag(xml_buf,buf,ix,n_buf,self_closing);
+
+ if(terminator != 0)
+ {
+ buf[ix] = ' ';
+ buf[ix++] = 0;
+
+ if(terminator & CLAUSE_BIT_VOICE)
+ {
+ // a change in voice, write the new voice name to the end of the buf
+ p = current_voice_id;
+ while((*p != 0) && (ix < (n_buf-1)))
+ {
+ buf[ix++] = *p++;
+ }
+ buf[ix++] = 0;
+ }
+ return(terminator);
+ }
+ continue;
+ }
+ }
+ ssml_ignore_l_angle=0;
+
+ if(ignore_text)
+ continue;
+
+ if((c2=='\n') && (option_linelength == -1))
+ {
+ // single-line mode, return immediately on NL
+ if((punct = lookupwchar(punct_chars,c1)) == 0)
+ {
+ charix[ix] = count_characters - clause_start_char;
+ ix += utf8_out(c1,&buf[ix]);
+ terminator = CLAUSE_PERIOD; // line doesn't end in punctuation, assume period
+ }
+ else
+ {
+ terminator = punct_attributes[punct];
+ }
+ buf[ix] = ' ';
+ buf[ix+1] = 0;
+ return(terminator);
+ }
+
+ if((c1 == CTRL_EMBEDDED) || (c1 == ctrl_embedded))
+ {
+ // an embedded command. If it's a voice change, end the clause
+ if(c2 == 'V')
+ {
+ buf[ix++] = 0; // end the clause at this point
+ while(!iswspace(c1 = GetC()) && !Eof() && (ix < (n_buf-1)))
+ buf[ix++] = c1; // add voice name to end of buffer, after the text
+ buf[ix++] = 0;
+ return(CLAUSE_VOICE);
+ }
+ else
+ if(c2 == 'B')
+ {
+ // set the punctuation option from an embedded command
+ // B0 B1 B<punct list><space>
+ strcpy(&buf[ix]," ");
+ ix += 3;
+
+ if((c2 = GetC()) == '0')
+ option_punctuation = 0;
+ else
+ {
+ option_punctuation = 1;
+ option_punctlist[0] = 0;
+ if(c2 != '1')
+ {
+ // a list of punctuation characters to be spoken, terminated by space
+ j = 0;
+ while(!iswspace(c2) && !Eof())
+ {
+ option_punctlist[j++] = c2;
+ c2 = GetC();
+ buf[ix++] = ' ';
+ }
+ option_punctlist[j] = 0; // terminate punctuation list
+ option_punctuation = 2;
+ }
+ }
+ c2 = GetC();
+ continue;
+ }
+ }
+
+ linelength++;
+
+ if(iswalnum(c1))
+ any_alnum = 1;
+ else
+ {
+ if(stressed_word)
+ {
+ stressed_word = 0;
+ c1 = CHAR_EMPHASIS; // indicate this word is strtessed
+ UngetC(c2);
+ c2 = ' ';
+ }
+
+ if(iswspace(c1))
+ {
+ char *p_word;
+
+ if(tr->translator_name == 0x6a626f)
+ {
+ // language jbo : lojban
+ // treat "i" or ".i" as end-of-sentence
+ p_word = &buf[ix-1];
+ if(p_word[0] == 'i')
+ {
+ if(p_word[-1] == '.')
+ p_word--;
+ if(p_word[-1] == ' ')
+ {
+ ungot_word = "i ";
+ UngetC(c2);
+ p_word[0] = 0;
+ return(CLAUSE_PERIOD);
+ }
+ }
+ }
+ }
+ }
+
+ if(iswupper(c1))
+ {
+ tr->clause_upper_count++;
+ if((option_capitals == 2) && (sayas_mode == 0) && !iswupper(cprev))
+ {
+ char text_buf[40];
+ char text_buf2[30];
+ if(LookupSpecial(tr, "_cap", text_buf2) != NULL)
+ {
+ sprintf(text_buf,"%s%s%s",tone_punct_on,text_buf2,tone_punct_off);
+ j = strlen(text_buf);
+ if((ix + j) < n_buf)
+ {
+ strcpy(&buf[ix],text_buf);
+ ix += j;
+ }
+ }
+ }
+ }
+ else
+ if(iswalpha(c1))
+ tr->clause_lower_count++;
+
+ if(option_phoneme_input)
+ {
+ if(phoneme_mode > 0)
+ phoneme_mode--;
+ else
+ if((c1 == '[') && (c2 == '['))
+ phoneme_mode = -1; // input is phoneme mnemonics, so don't look for punctuation
+ else
+ if((c1 == ']') && (c2 == ']'))
+ phoneme_mode = 2; // set phoneme_mode to zero after the next two characters
+ }
+
+ if(c1 == '\n')
+ {
+ parag = 0;
+
+ // count consecutive newlines, ignoring other spaces
+ while(!Eof() && iswspace(c2))
+ {
+ if(c2 == '\n')
+ parag++;
+ c2 = GetC();
+ }
+ if(parag > 0)
+ {
+ // 2nd newline, assume paragraph
+ UngetC(c2);
+
+ buf[ix] = ' ';
+ buf[ix+1] = 0;
+ if(parag > 3)
+ parag = 3;
+if(option_ssml) parag=1;
+ return((CLAUSE_PARAGRAPH-30) + 30*parag); // several blank lines, longer pause
+ }
+
+ if(linelength <= option_linelength)
+ {
+ // treat lines shorter than a specified length as end-of-clause
+ UngetC(c2);
+ buf[ix] = ' ';
+ buf[ix+1] = 0;
+ return(CLAUSE_COLON);
+ }
+
+ linelength = 0;
+ }
+
+ if(option_punctuation && (phoneme_mode==0) && (sayas_mode==0) && iswpunct(c1))
+ {
+ // option is set to explicitly speak punctuation characters
+ // if a list of allowed punctuation has been set up, check whether the character is in it
+ if((option_punctuation == 1) || (wcschr(option_punctlist,c1) != NULL))
+ {
+ if((terminator = AnnouncePunctuation(tr, c1, c2, buf, ix)) >= 0)
+ return(terminator);
+ }
+ }
+
+ if((phoneme_mode==0) && (sayas_mode==0) && ((punct = lookupwchar(punct_chars,c1)) != 0))
+ {
+ punct_data = punct_attributes[punct];
+
+ if(punct_data & PUNCT_IN_WORD)
+ {
+ // Armenian punctuation inside a word
+ stressed_word = 1;
+ *tone_type = punct_data >> 12 & 0xf; // override the end-of-sentence type
+ continue;
+ }
+
+ if((iswspace(c2) || (punct_data & 0x8000) || IsBracket(c2) || (c2=='?') || (c2=='-') || Eof()))
+ {
+ // note: (c2='?') is for when a smart-quote has been replaced by '?'
+ buf[ix] = ' ';
+ buf[ix+1] = 0;
+
+ if((c1 == '.') && (cprev == '.'))
+ {
+ c1 = 0x2026;
+ punct = 9; // elipsis
+ }
+
+ nl_count = 0;
+ while(!Eof() && iswspace(c2))
+ {
+ if(c2 == '\n')
+ nl_count++;
+ c2 = GetC(); // skip past space(s)
+ }
+ if(!Eof())
+ {
+ UngetC(c2);
+ }
+
+ if((nl_count==0) && (c1 == '.'))
+ {
+ if(iswdigit(cprev) && (tr->langopts.numbers & 0x10000))
+ {
+ // dot after a number indicates an ordinal number
+ c2 = ' ';
+ continue;
+ }
+ if(iswlower(c2))
+ {
+ c2 = ' ';
+ continue; // next word has no capital letter, this dot is probably from an abbreviation
+ }
+ if(any_alnum==0)
+ {
+ c2 = ' '; // no letters or digits yet, so probably not a sentence terminator
+ continue;
+ }
+ }
+
+ punct_data = punct_attributes[punct];
+ if(nl_count > 1)
+ {
+ if((punct_data == CLAUSE_QUESTION) || (punct_data == CLAUSE_EXCLAMATION))
+ return(punct_data + 35); // with a longer pause
+ return(CLAUSE_PARAGRAPH);
+ }
+ return(punct_data); // only recognise punctuation if followed by a blank or bracket/quote
+ }
+ }
+
+ if(speech_parameters[espeakSILENCE]==1)
+ continue;
+
+ j = ix+1;
+ ix += utf8_out(c1,&buf[ix]); // buf[ix++] = c1;
+ if(!iswspace(c1) && !IsBracket(c1))
+ {
+ charix[ix] = count_characters - clause_start_char;
+ while(j < ix)
+ charix[j++] = -1; // subsequent bytes of a multibyte character
+ }
+
+ if(((ix > (n_buf-20)) && !IsAlpha(c1) && !iswdigit(c1)) || (ix >= (n_buf-2)))
+ {
+ // clause too long, getting near end of buffer, so break here
+ // try to break at a word boundary (unless we actually reach the end of buffer).
+ buf[ix] = ' ';
+ buf[ix+1] = 0;
+ UngetC(c2);
+ return(CLAUSE_NONE);
+ }
+ }
+
+ if(stressed_word)
+ {
+ ix += utf8_out(CHAR_EMPHASIS, &buf[ix]);
+ }
+ buf[ix] = ' ';
+ buf[ix+1] = 0;
+ return(CLAUSE_EOF); // end of file
+} // end of ReadClause
+
+
+void InitNamedata(void)
+{//====================
+ namedata_ix = 0;
+ if(namedata != NULL)
+ {
+ free(namedata);
+ namedata = NULL;
+ n_namedata = 0;
+ }
+}
+
+
+void InitText2(void)
+{//=================
+ int param;
+
+ ungot_char = 0;
+
+ n_ssml_stack =1;
+ n_param_stack = 1;
+ ssml_stack[0].tag_type = 0;
+
+ for(param=0; param<N_SPEECH_PARAM; param++)
+ speech_parameters[param] = param_stack[0].parameter[param]; // set all speech parameters to defaults
+
+ option_punctuation = speech_parameters[espeakPUNCTUATION];
+ option_capitals = speech_parameters[espeakCAPITALS];
+
+ current_voice_id[0] = 0;
+
+ ignore_text = 0;
+ clear_skipping_text = 0;
+ count_characters = -1;
+ sayas_mode = 0;
+
+ xmlbase = NULL;
+}
+
diff --git a/Plugins/eSpeak/eSpeak/setlengths.cpp b/Plugins/eSpeak/eSpeak/setlengths.cpp
new file mode 100644
index 0000000..4937fde
--- /dev/null
+++ b/Plugins/eSpeak/eSpeak/setlengths.cpp
@@ -0,0 +1,673 @@
+/***************************************************************************
+ * Copyright (C) 2005 to 2007 by Jonathan Duddington *
+ * email: jonsd@users.sourceforge.net *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 3 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write see: *
+ * <http://www.gnu.org/licenses/>. *
+ ***************************************************************************/
+
+#include "StdAfx.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <wctype.h>
+
+#include "speak_lib.h"
+#include "speech.h"
+#include "phoneme.h"
+#include "synthesize.h"
+#include "voice.h"
+#include "translate.h"
+
+extern int GetAmplitude(void);
+
+
+// convert from words-per-minute to internal speed factor
+static unsigned char speed_lookup[290] = {
+ 250, 246, 243, 239, 236, // 80
+ 233, 229, 226, 223, 220, // 85
+ 217, 214, 211, 208, 205, // 90
+ 202, 197, 194, 192, 190, // 95
+ 187, 185, 183, 180, 178, // 100
+ 176, 174, 172, 170, 168, // 105
+ 166, 164, 161, 159, 158, // 110
+ 156, 154, 152, 150, 148, // 115
+ 146, 145, 143, 141, 137, // 120
+ 136, 135, 133, 132, 131, // 125
+ 129, 128, 127, 126, 125, // 130
+ 124, 122, 121, 120, 119, // 135
+ 117, 116, 115, 114, 113, // 140
+ 112, 111, 110, 108, 107, // 145
+ 106, 105, 104, 103, 102, // 150
+ 101, 100, 99, 98, 97, // 155
+ 96, 95, 93, 92, 92, // 160
+ 91, 90, 89, 89, 88, // 165
+ 87, 87, 86, 85, 85, // 170
+ 84, 83, 83, 82, 81, // 175
+ 80, 80, 79, 78, 78, // 180
+ 77, 76, 76, 75, 73, // 185
+ 72, 72, 71, 71, 70, // 190
+ 70, 69, 69, 68, 67, // 195
+ 67, 66, 66, 65, 65, // 200
+ 64, 64, 63, 63, 62, // 205
+ 62, 61, 60, 60, 59, // 210
+ 59, 58, 58, 57, 57, // 215
+ 56, 56, 55, 55, 55, // 220
+ 54, 54, 53, 53, 52, // 225
+ 52, 51, 51, 50, 50, // 230
+ 49, 49, 49, 48, 48, // 235
+ 47, 47, 46, 46, 46, // 240
+ 45, 45, 44, 44, 43, // 245
+ 43, 43, 42, 42, 41, // 250
+ 41, 41, 40, 40, 39, // 255
+ 39, 39, 38, 38, 38, // 260
+ 37, 37, 37, 36, 36, // 265
+ 35, 35, 35, 34, 34, // 270
+ 34, 33, 33, 33, 32, // 275
+ 32, 32, 32, 31, 31, // 280
+ 31, 30, 30, 30, 29, // 285
+ 29, 29, 29, 28, 28, // 290
+ 28, 28, 27, 27, 27, // 295
+ 26, 26, 26, 26, 25, // 300
+ 25, 25, 22, 22, 22, // 305
+ 22, 22, 22, 22, 22, // 310
+ 21, 21, 21, 21, 21, // 315
+ 21, 20, 20, 20, 20, // 320
+ 20, 15, 15, 15, 15, // 325
+ 15, 15, 15, 15, 16, // 330
+ 16, 16, 16, 15, 15, // 335
+ 15, 15, 15, 15, 15, // 340
+ 15, 17, 17, 16, 16, // 345
+ 15, 15, 14, 14, 13, // 350
+ 13, 12, 12, 11, 11, // 355
+ 10, 10, 9, 8, 8, // 360
+ 7, 6, 5, 5, 4, // 365
+};
+
+// speed_factor2 adjustments for speeds 370 to 390
+static unsigned char faster[] = {
+114,112,110,109,107,105,104,102,100,98, // 370-379
+96,94,92,90,88,85,83,80,78,75,72 }; //380-390
+
+static int speed1 = 130;
+static int speed2 = 121;
+static int speed3 = 118;
+
+
+
+void SetSpeed(int control)
+{//=======================
+ int x;
+ int s1;
+ int wpm;
+ int wpm2;
+
+ wpm = embedded_value[EMBED_S];
+ if(control == 2)
+ wpm = embedded_value[EMBED_S2];
+ wpm2 = wpm;
+
+ if(wpm > 369) wpm = 369;
+ if(wpm < 80) wpm = 80;
+
+ x = speed_lookup[wpm-80];
+
+ if(control & 1)
+ {
+ // set speed factors for different syllable positions within a word
+ // these are used in CalcLengths()
+ speed1 = (x * voice->speedf1)/256;
+ speed2 = (x * voice->speedf2)/256;
+ speed3 = (x * voice->speedf3)/256;
+ }
+
+ if(control & 2)
+ {
+ // these are used in synthesis file
+ s1 = (x * voice->speedf1)/256;
+ speed.speed_factor1 = (256 * s1)/115; // full speed adjustment, used for pause length
+if(speed.speed_factor1 < 15)
+ speed.speed_factor1 = 15;
+ if(wpm >= 170)
+// speed_factor2 = 100 + (166*s1)/128; // reduced speed adjustment, used for playing recorded sounds
+ speed.speed_factor2 = 110 + (150*s1)/128; // reduced speed adjustment, used for playing recorded sounds
+ else
+ speed.speed_factor2 = 128 + (128*s1)/130; // = 215 at 170 wpm
+
+ if(wpm2 > 369)
+ {
+ if(wpm2 > 390)
+ wpm2 = 390;
+ speed.speed_factor2 = faster[wpm2 - 370];
+ }
+ }
+
+ speed.min_sample_len = 450;
+ speed.speed_factor3 = 110; // controls the effect of FRFLAG_LEN_MOD reduce length change
+
+ if(wpm2 >= 370)
+ {
+ // TESTING
+ // use experimental fast settings if they have been specified in the Voice
+ if(speed.fast_settings[0] > 0)
+ speed.speed_factor1 = speed.fast_settings[0];
+ if(speed.fast_settings[1] > 0)
+ speed.speed_factor2 = speed.fast_settings[1];
+ if(speed.fast_settings[2] > 0)
+ speed.speed_factor3 = speed.fast_settings[2];
+ }
+} // end of SetSpeed
+
+
+#ifdef deleted
+void SetAmplitude(int amp)
+{//=======================
+ static unsigned char amplitude_factor[] = {0,5,6,7,9,11,14,17,21,26, 32, 38,44,50,56,63,70,77,84,91,100 };
+
+ if((amp >= 0) && (amp <= 20))
+ {
+ option_amplitude = (amplitude_factor[amp] * 480)/256;
+ }
+}
+#endif
+
+
+
+void SetParameter(int parameter, int value, int relative)
+{//======================================================
+// parameter: reset-all, amp, pitch, speed, linelength, expression, capitals, number grouping
+// relative 0=absolute 1=relative
+
+ int new_value = value;
+ int default_value;
+
+ if(relative)
+ {
+ if(parameter < 5)
+ {
+ default_value = param_defaults[parameter];
+ new_value = default_value + (default_value * value)/100;
+ }
+ }
+ param_stack[0].parameter[parameter] = new_value;
+
+ switch(parameter)
+ {
+ case espeakRATE:
+ embedded_value[EMBED_S] = new_value;
+ embedded_value[EMBED_S2] = new_value;
+ SetSpeed(3);
+ break;
+
+ case espeakVOLUME:
+ embedded_value[EMBED_A] = new_value;
+ GetAmplitude();
+ break;
+
+ case espeakPITCH:
+ if(new_value > 99) new_value = 99;
+ if(new_value < 0) new_value = 0;
+ embedded_value[EMBED_P] = new_value;
+ break;
+
+ case espeakRANGE:
+ if(new_value > 99) new_value = 99;
+ embedded_value[EMBED_R] = new_value;
+ break;
+
+ case espeakLINELENGTH:
+ option_linelength = new_value;
+ break;
+
+ case espeakWORDGAP:
+ option_wordgap = new_value;
+ break;
+
+ case espeakINTONATION:
+ if((new_value & 0xff) != 0)
+ translator->langopts.intonation_group = new_value & 0xff;
+ option_tone_flags = new_value;
+ break;
+
+ default:
+ break;
+ }
+} // end of SetParameter
+
+
+
+static void DoEmbedded2(int &embix)
+{//================================
+ // There were embedded commands in the text at this point
+
+ unsigned int word;
+
+ do {
+ word = embedded_list[embix++];
+
+ if((word & 0x1f) == EMBED_S)
+ {
+ // speed
+ SetEmbedded(word & 0x7f, word >> 8); // adjusts embedded_value[EMBED_S]
+ SetSpeed(1);
+ }
+ } while((word & 0x80) == 0);
+}
+
+
+void CalcLengths(Translator *tr)
+{//==============================
+ int ix;
+ int ix2;
+ PHONEME_LIST *prev;
+ PHONEME_LIST *next;
+ PHONEME_LIST *next2;
+ PHONEME_LIST *next3;
+ PHONEME_LIST *p;
+ PHONEME_LIST *p2;
+
+ int stress;
+ int type;
+ static int more_syllables=0;
+ int pre_sonorant=0;
+ int pre_voiced=0;
+ int last_pitch = 0;
+ int pitch_start;
+ int length_mod;
+ int len;
+ int env2;
+ int end_of_clause;
+ int embedded_ix = 0;
+ int min_drop;
+ int emphasized;
+ int tone_mod;
+ unsigned char *pitch_env=NULL;
+
+ for(ix=1; ix<n_phoneme_list; ix++)
+ {
+ prev = &phoneme_list[ix-1];
+ p = &phoneme_list[ix];
+ stress = p->tone & 0x7;
+ emphasized = p->tone & 0x8;
+
+ next = &phoneme_list[ix+1];
+
+ if(p->synthflags & SFLAG_EMBEDDED)
+ {
+ DoEmbedded2(embedded_ix);
+ }
+
+ type = p->type;
+ if(p->synthflags & SFLAG_SYLLABLE)
+ type = phVOWEL;
+
+ switch(type)
+ {
+ case phPAUSE:
+ last_pitch = 0;
+ break;
+
+ case phSTOP:
+ last_pitch = 0;
+ if(prev->type == phFRICATIVE)
+ p->prepause = 20;
+ else
+ if((more_syllables > 0) || (stress < 4))
+ p->prepause = 40;
+ else
+ p->prepause = 60;
+
+ if(prev->type == phSTOP)
+ p->prepause = 60;
+
+ if((tr->langopts.word_gap & 0x10) && (p->newword))
+ p->prepause = 60;
+
+ if(p->ph->phflags & phLENGTHENSTOP)
+ p->prepause += 30;
+
+ if(p->synthflags & SFLAG_LENGTHEN)
+ p->prepause += tr->langopts.long_stop;
+ break;
+
+ case phVFRICATIVE:
+ if(next->type==phVOWEL)
+ {
+ pre_voiced = 1;
+ } // drop through
+ case phFRICATIVE:
+ if(p->newword)
+ p->prepause = 15;
+
+ if(next->type==phPAUSE && prev->type==phNASAL && !(p->ph->phflags&phFORTIS))
+ p->prepause = 25;
+
+ if(prev->ph->phflags & phBRKAFTER)
+ p->prepause = 30;
+
+ if((p->ph->phflags & phSIBILANT) && next->type==phSTOP && !next->newword)
+ {
+ if(prev->type == phVOWEL)
+ p->length = 200; // ?? should do this if it's from a prefix
+ else
+ p->length = 150;
+ }
+ else
+ p->length = 256;
+
+ if((tr->langopts.word_gap & 0x10) && (p->newword))
+ p->prepause = 30;
+
+ break;
+
+ case phVSTOP:
+ if(prev->type==phVFRICATIVE || prev->type==phFRICATIVE || (prev->ph->phflags & phSIBILANT) || (prev->type == phLIQUID))
+ p->prepause = 30;
+
+ if(next->type==phVOWEL || next->type==phLIQUID)
+ {
+ if((next->type==phVOWEL) || !next->newword)
+ pre_voiced = 1;
+
+ p->prepause = 40;
+
+ if((prev->type == phPAUSE) || (prev->type == phVOWEL)) // || (prev->ph->mnemonic == ('/'*256+'r')))
+ p->prepause = 0;
+ else
+ if(p->newword==0)
+ {
+ if(prev->type==phLIQUID)
+ p->prepause = 20;
+ if(prev->type==phNASAL)
+ p->prepause = 12;
+
+ if(prev->type==phSTOP && !(prev->ph->phflags & phFORTIS))
+ p->prepause = 0;
+ }
+ }
+ if((tr->langopts.word_gap & 0x10) && (p->newword) && (p->prepause < 20))
+ p->prepause = 20;
+
+ break;
+
+ case phLIQUID:
+ case phNASAL:
+ p->amp = tr->stress_amps[1]; // unless changed later
+ p->length = 256; // TEMPORARY
+ min_drop = 0;
+
+ if(p->newword)
+ {
+ if(prev->type==phLIQUID)
+ p->prepause = 25;
+ if(prev->type==phVOWEL)
+ p->prepause = 12;
+ }
+
+ if(next->type==phVOWEL)
+ {
+ pre_sonorant = 1;
+ }
+ else
+ if((prev->type==phVOWEL) || (prev->type == phLIQUID))
+ {
+ p->length = prev->length;
+ p->pitch2 = last_pitch;
+ if(p->pitch2 < 7)
+ p->pitch2 = 7;
+ p->pitch1 = p->pitch2 - 8;
+ p->env = PITCHfall;
+ pre_voiced = 0;
+
+ if(p->type == phLIQUID)
+ {
+ p->length = speed1;
+//p->pitch1 = p->pitch2 - 20; // post vocalic [r/]
+ }
+
+ if(next->type == phVSTOP)
+ {
+ p->length = (p->length * 160)/100;
+ }
+ if(next->type == phVFRICATIVE)
+ {
+ p->length = (p->length * 120)/100;
+ }
+ }
+ else
+ {
+ p->pitch2 = last_pitch;
+ for(ix2=ix; ix2<n_phoneme_list; ix2++)
+ {
+ if(phoneme_list[ix2].type == phVOWEL)
+ {
+ p->pitch2 = phoneme_list[ix2].pitch2;
+ break;
+ }
+ }
+ p->pitch1 = p->pitch2-8;
+ p->env = PITCHfall;
+ pre_voiced = 0;
+ }
+ break;
+
+ case phVOWEL:
+ min_drop = 0;
+ next2 = &phoneme_list[ix+2];
+ next3 = &phoneme_list[ix+3];
+
+ if(stress > 7) stress = 7;
+
+ if(pre_sonorant)
+ p->amp = tr->stress_amps[stress]-1;
+ else
+ p->amp = tr->stress_amps[stress];
+
+ if(emphasized)
+ p->amp = 25;
+
+ if(ix >= (n_phoneme_list-3))
+ {
+ // last phoneme of a clause, limit its amplitude
+ if(p->amp > tr->langopts.param[LOPT_MAXAMP_EOC])
+ p->amp = tr->langopts.param[LOPT_MAXAMP_EOC];
+ }
+
+ // is the last syllable of a word ?
+ more_syllables=0;
+ end_of_clause = 0;
+ for(p2 = p+1; p2->newword== 0; p2++)
+ {
+ if((p2->type == phVOWEL) && !(p2->ph->phflags & phNONSYLLABIC))
+ more_syllables++;
+
+ if(p2->ph->code == phonPAUSE_CLAUSE)
+ end_of_clause = 2;
+ }
+ if(p2->ph->code == phonPAUSE_CLAUSE)
+ end_of_clause = 2;
+
+ if((p2->newword & 2) && (more_syllables==0))
+ {
+ end_of_clause = 2;
+ }
+
+ // calc length modifier
+ if((next->ph->code == phonPAUSE_VSHORT) && (next2->type == phPAUSE))
+ {
+ // if PAUSE_VSHORT is followed by a pause, then use that
+ next = next2;
+ next2 = next3;
+ next3 = &phoneme_list[ix+4];
+ }
+
+ if(more_syllables==0)
+ {
+ len = tr->langopts.length_mods0[next2->ph->length_mod *10+ next->ph->length_mod];
+
+ if((next->newword) && (tr->langopts.word_gap & 0x20))
+ {
+ // consider as a pause + first phoneme of the next word
+ length_mod = (len + tr->langopts.length_mods0[next->ph->length_mod *10+ 1])/2;
+ }
+ else
+ length_mod = len;
+ }
+ else
+ {
+ length_mod = tr->langopts.length_mods[next2->ph->length_mod *10+ next->ph->length_mod];
+
+ if((next->type == phNASAL) && (next2->type == phSTOP || next2->type == phVSTOP) && (next3->ph->phflags & phFORTIS))
+ length_mod -= 15;
+ }
+
+ if(more_syllables==0)
+ length_mod *= speed1;
+ else
+ if(more_syllables==1)
+ length_mod *= speed2;
+ else
+ length_mod *= speed3;
+
+ length_mod = length_mod / 128;
+
+ if(length_mod < 8)
+ length_mod = 8; // restrict how much lengths can be reduced
+
+ if(stress >= 7)
+ {
+ // tonic syllable, include a constant component so it doesn't decrease directly with speed
+ length_mod += 20;
+ if(emphasized)
+ length_mod += 10;
+ }
+ else
+ if(emphasized)
+ {
+ length_mod += 20;
+ }
+
+ if((len = tr->stress_lengths[stress]) == 0)
+ len = tr->stress_lengths[6];
+
+ length_mod = (length_mod * len)/128;
+
+ if(p->tone_ph != 0)
+ {
+ if((tone_mod = phoneme_tab[p->tone_ph]->std_length) > 0)
+ {
+ // a tone phoneme specifies a percentage change to the length
+ length_mod = (length_mod * tone_mod) / 100;
+ }
+ }
+
+ if(end_of_clause == 2)
+ {
+ // this is the last syllable in the clause, lengthen it - more for short vowels
+ len = p->ph->std_length;
+ if(tr->langopts.stress_flags & 0x40000)
+ len=200; // don't lengthen short vowels more than long vowels at end-of-clause
+ length_mod = length_mod * (256 + (280 - len)/3)/256;
+ }
+
+if(p->type != phVOWEL)
+{
+ length_mod = 256; // syllabic consonant
+ min_drop = 8;
+}
+ p->length = length_mod;
+
+ // pre-vocalic part
+ // set last-pitch
+ env2 = p->env;
+ if(env2 > 1) env2++; // version for use with preceding semi-vowel
+
+ if(p->tone_ph != 0)
+ {
+ pitch_env = LookupEnvelope(phoneme_tab[p->tone_ph]->spect);
+ }
+ else
+ {
+ pitch_env = envelope_data[env2];
+ }
+
+ pitch_start = p->pitch1 + ((p->pitch2-p->pitch1)*pitch_env[0])/256;
+
+ if(pre_sonorant || pre_voiced)
+ {
+ // set pitch for pre-vocalic part
+ if(pitch_start == 1024)
+ last_pitch = pitch_start; // pitch is not set
+
+ if(pitch_start - last_pitch > 8) // was 9
+ last_pitch = pitch_start - 8;
+
+ prev->pitch1 = last_pitch;
+ prev->pitch2 = pitch_start;
+ if(last_pitch < pitch_start)
+ {
+ prev->env = PITCHrise;
+ p->env = env2;
+ }
+ else
+ {
+ prev->env = PITCHfall;
+ }
+
+ prev->length = length_mod;
+
+ prev->amp = p->amp;
+ if((prev->type != phLIQUID) && (prev->amp > 18))
+ prev->amp = 18;
+ }
+
+ // vowel & post-vocalic part
+ next->synthflags &= ~SFLAG_SEQCONTINUE;
+ if(next->type == phNASAL && next2->type != phVOWEL)
+ next->synthflags |= SFLAG_SEQCONTINUE;
+
+ if(next->type == phLIQUID)
+ {
+ next->synthflags |= SFLAG_SEQCONTINUE;
+
+ if(next2->type == phVOWEL)
+ {
+ next->synthflags &= ~SFLAG_SEQCONTINUE;
+ }
+
+ if(next2->type != phVOWEL)
+ {
+ if(next->ph->mnemonic == ('/'*256+'r'))
+ {
+ next->synthflags &= ~SFLAG_SEQCONTINUE;
+// min_drop = 15;
+ }
+ }
+ }
+
+ if((min_drop > 0) && ((p->pitch2 - p->pitch1) < min_drop))
+ {
+ p->pitch1 = p->pitch2 - min_drop;
+ if(p->pitch1 < 0)
+ p->pitch1 = 0;
+ }
+
+ last_pitch = p->pitch1 + ((p->pitch2-p->pitch1)*envelope_data[p->env][127])/256;
+ pre_sonorant = 0;
+ pre_voiced = 0;
+ break;
+ }
+ }
+} // end of CalcLengths
+
diff --git a/Plugins/eSpeak/eSpeak/sintab.h b/Plugins/eSpeak/eSpeak/sintab.h
new file mode 100644
index 0000000..08fc18f
--- /dev/null
+++ b/Plugins/eSpeak/eSpeak/sintab.h
@@ -0,0 +1,258 @@
+short int sin_tab[2048] = {
+ 0, -25, -50, -75, -100, -125, -150, -175,
+ -201, -226, -251, -276, -301, -326, -351, -376,
+ -401, -427, -452, -477, -502, -527, -552, -577,
+ -602, -627, -652, -677, -702, -727, -752, -777,
+ -802, -827, -852, -877, -902, -927, -952, -977,
+ -1002, -1027, -1052, -1077, -1102, -1127, -1152, -1177,
+ -1201, -1226, -1251, -1276, -1301, -1326, -1350, -1375,
+ -1400, -1425, -1449, -1474, -1499, -1523, -1548, -1573,
+ -1597, -1622, -1647, -1671, -1696, -1721, -1745, -1770,
+ -1794, -1819, -1843, -1868, -1892, -1917, -1941, -1965,
+ -1990, -2014, -2038, -2063, -2087, -2111, -2136, -2160,
+ -2184, -2208, -2233, -2257, -2281, -2305, -2329, -2353,
+ -2377, -2401, -2425, -2449, -2473, -2497, -2521, -2545,
+ -2569, -2593, -2617, -2640, -2664, -2688, -2712, -2735,
+ -2759, -2783, -2806, -2830, -2853, -2877, -2900, -2924,
+ -2947, -2971, -2994, -3018, -3041, -3064, -3088, -3111,
+ -3134, -3157, -3180, -3204, -3227, -3250, -3273, -3296,
+ -3319, -3342, -3365, -3388, -3410, -3433, -3456, -3479,
+ -3502, -3524, -3547, -3570, -3592, -3615, -3637, -3660,
+ -3682, -3705, -3727, -3749, -3772, -3794, -3816, -3839,
+ -3861, -3883, -3905, -3927, -3949, -3971, -3993, -4015,
+ -4037, -4059, -4080, -4102, -4124, -4146, -4167, -4189,
+ -4211, -4232, -4254, -4275, -4296, -4318, -4339, -4360,
+ -4382, -4403, -4424, -4445, -4466, -4487, -4508, -4529,
+ -4550, -4571, -4592, -4613, -4633, -4654, -4675, -4695,
+ -4716, -4736, -4757, -4777, -4798, -4818, -4838, -4859,
+ -4879, -4899, -4919, -4939, -4959, -4979, -4999, -5019,
+ -5039, -5059, -5078, -5098, -5118, -5137, -5157, -5176,
+ -5196, -5215, -5235, -5254, -5273, -5292, -5311, -5331,
+ -5350, -5369, -5388, -5406, -5425, -5444, -5463, -5482,
+ -5500, -5519, -5537, -5556, -5574, -5593, -5611, -5629,
+ -5648, -5666, -5684, -5702, -5720, -5738, -5756, -5774,
+ -5791, -5809, -5827, -5844, -5862, -5880, -5897, -5914,
+ -5932, -5949, -5966, -5984, -6001, -6018, -6035, -6052,
+ -6069, -6085, -6102, -6119, -6136, -6152, -6169, -6185,
+ -6202, -6218, -6235, -6251, -6267, -6283, -6299, -6315,
+ -6331, -6347, -6363, -6379, -6395, -6410, -6426, -6441,
+ -6457, -6472, -6488, -6503, -6518, -6533, -6549, -6564,
+ -6579, -6594, -6608, -6623, -6638, -6653, -6667, -6682,
+ -6696, -6711, -6725, -6739, -6754, -6768, -6782, -6796,
+ -6810, -6824, -6838, -6852, -6865, -6879, -6893, -6906,
+ -6920, -6933, -6946, -6960, -6973, -6986, -6999, -7012,
+ -7025, -7038, -7051, -7064, -7076, -7089, -7101, -7114,
+ -7126, -7139, -7151, -7163, -7175, -7187, -7199, -7211,
+ -7223, -7235, -7247, -7259, -7270, -7282, -7293, -7305,
+ -7316, -7327, -7338, -7349, -7361, -7372, -7382, -7393,
+ -7404, -7415, -7425, -7436, -7446, -7457, -7467, -7478,
+ -7488, -7498, -7508, -7518, -7528, -7538, -7548, -7557,
+ -7567, -7577, -7586, -7596, -7605, -7614, -7623, -7633,
+ -7642, -7651, -7660, -7668, -7677, -7686, -7695, -7703,
+ -7712, -7720, -7728, -7737, -7745, -7753, -7761, -7769,
+ -7777, -7785, -7793, -7800, -7808, -7816, -7823, -7830,
+ -7838, -7845, -7852, -7859, -7866, -7873, -7880, -7887,
+ -7894, -7900, -7907, -7914, -7920, -7926, -7933, -7939,
+ -7945, -7951, -7957, -7963, -7969, -7975, -7980, -7986,
+ -7991, -7997, -8002, -8008, -8013, -8018, -8023, -8028,
+ -8033, -8038, -8043, -8047, -8052, -8057, -8061, -8066,
+ -8070, -8074, -8078, -8082, -8086, -8090, -8094, -8098,
+ -8102, -8105, -8109, -8113, -8116, -8119, -8123, -8126,
+ -8129, -8132, -8135, -8138, -8141, -8143, -8146, -8149,
+ -8151, -8153, -8156, -8158, -8160, -8162, -8164, -8166,
+ -8168, -8170, -8172, -8174, -8175, -8177, -8178, -8179,
+ -8181, -8182, -8183, -8184, -8185, -8186, -8187, -8187,
+ -8188, -8189, -8189, -8190, -8190, -8190, -8190, -8190,
+ -8191, -8190, -8190, -8190, -8190, -8190, -8189, -8189,
+ -8188, -8187, -8187, -8186, -8185, -8184, -8183, -8182,
+ -8181, -8179, -8178, -8177, -8175, -8174, -8172, -8170,
+ -8168, -8166, -8164, -8162, -8160, -8158, -8156, -8153,
+ -8151, -8149, -8146, -8143, -8141, -8138, -8135, -8132,
+ -8129, -8126, -8123, -8119, -8116, -8113, -8109, -8105,
+ -8102, -8098, -8094, -8090, -8086, -8082, -8078, -8074,
+ -8070, -8066, -8061, -8057, -8052, -8047, -8043, -8038,
+ -8033, -8028, -8023, -8018, -8013, -8008, -8002, -7997,
+ -7991, -7986, -7980, -7975, -7969, -7963, -7957, -7951,
+ -7945, -7939, -7933, -7926, -7920, -7914, -7907, -7900,
+ -7894, -7887, -7880, -7873, -7866, -7859, -7852, -7845,
+ -7838, -7830, -7823, -7816, -7808, -7800, -7793, -7785,
+ -7777, -7769, -7761, -7753, -7745, -7737, -7728, -7720,
+ -7712, -7703, -7695, -7686, -7677, -7668, -7660, -7651,
+ -7642, -7633, -7623, -7614, -7605, -7596, -7586, -7577,
+ -7567, -7557, -7548, -7538, -7528, -7518, -7508, -7498,
+ -7488, -7478, -7467, -7457, -7446, -7436, -7425, -7415,
+ -7404, -7393, -7382, -7372, -7361, -7349, -7338, -7327,
+ -7316, -7305, -7293, -7282, -7270, -7259, -7247, -7235,
+ -7223, -7211, -7199, -7187, -7175, -7163, -7151, -7139,
+ -7126, -7114, -7101, -7089, -7076, -7064, -7051, -7038,
+ -7025, -7012, -6999, -6986, -6973, -6960, -6946, -6933,
+ -6920, -6906, -6893, -6879, -6865, -6852, -6838, -6824,
+ -6810, -6796, -6782, -6768, -6754, -6739, -6725, -6711,
+ -6696, -6682, -6667, -6653, -6638, -6623, -6608, -6594,
+ -6579, -6564, -6549, -6533, -6518, -6503, -6488, -6472,
+ -6457, -6441, -6426, -6410, -6395, -6379, -6363, -6347,
+ -6331, -6315, -6299, -6283, -6267, -6251, -6235, -6218,
+ -6202, -6185, -6169, -6152, -6136, -6119, -6102, -6085,
+ -6069, -6052, -6035, -6018, -6001, -5984, -5966, -5949,
+ -5932, -5914, -5897, -5880, -5862, -5844, -5827, -5809,
+ -5791, -5774, -5756, -5738, -5720, -5702, -5684, -5666,
+ -5648, -5629, -5611, -5593, -5574, -5556, -5537, -5519,
+ -5500, -5482, -5463, -5444, -5425, -5406, -5388, -5369,
+ -5350, -5331, -5311, -5292, -5273, -5254, -5235, -5215,
+ -5196, -5176, -5157, -5137, -5118, -5098, -5078, -5059,
+ -5039, -5019, -4999, -4979, -4959, -4939, -4919, -4899,
+ -4879, -4859, -4838, -4818, -4798, -4777, -4757, -4736,
+ -4716, -4695, -4675, -4654, -4633, -4613, -4592, -4571,
+ -4550, -4529, -4508, -4487, -4466, -4445, -4424, -4403,
+ -4382, -4360, -4339, -4318, -4296, -4275, -4254, -4232,
+ -4211, -4189, -4167, -4146, -4124, -4102, -4080, -4059,
+ -4037, -4015, -3993, -3971, -3949, -3927, -3905, -3883,
+ -3861, -3839, -3816, -3794, -3772, -3749, -3727, -3705,
+ -3682, -3660, -3637, -3615, -3592, -3570, -3547, -3524,
+ -3502, -3479, -3456, -3433, -3410, -3388, -3365, -3342,
+ -3319, -3296, -3273, -3250, -3227, -3204, -3180, -3157,
+ -3134, -3111, -3088, -3064, -3041, -3018, -2994, -2971,
+ -2947, -2924, -2900, -2877, -2853, -2830, -2806, -2783,
+ -2759, -2735, -2712, -2688, -2664, -2640, -2617, -2593,
+ -2569, -2545, -2521, -2497, -2473, -2449, -2425, -2401,
+ -2377, -2353, -2329, -2305, -2281, -2257, -2233, -2208,
+ -2184, -2160, -2136, -2111, -2087, -2063, -2038, -2014,
+ -1990, -1965, -1941, -1917, -1892, -1868, -1843, -1819,
+ -1794, -1770, -1745, -1721, -1696, -1671, -1647, -1622,
+ -1597, -1573, -1548, -1523, -1499, -1474, -1449, -1425,
+ -1400, -1375, -1350, -1326, -1301, -1276, -1251, -1226,
+ -1201, -1177, -1152, -1127, -1102, -1077, -1052, -1027,
+ -1002, -977, -952, -927, -902, -877, -852, -827,
+ -802, -777, -752, -727, -702, -677, -652, -627,
+ -602, -577, -552, -527, -502, -477, -452, -427,
+ -401, -376, -351, -326, -301, -276, -251, -226,
+ -201, -175, -150, -125, -100, -75, -50, -25,
+ 0, 25, 50, 75, 100, 125, 150, 175,
+ 201, 226, 251, 276, 301, 326, 351, 376,
+ 401, 427, 452, 477, 502, 527, 552, 577,
+ 602, 627, 652, 677, 702, 727, 752, 777,
+ 802, 827, 852, 877, 902, 927, 952, 977,
+ 1002, 1027, 1052, 1077, 1102, 1127, 1152, 1177,
+ 1201, 1226, 1251, 1276, 1301, 1326, 1350, 1375,
+ 1400, 1425, 1449, 1474, 1499, 1523, 1548, 1573,
+ 1597, 1622, 1647, 1671, 1696, 1721, 1745, 1770,
+ 1794, 1819, 1843, 1868, 1892, 1917, 1941, 1965,
+ 1990, 2014, 2038, 2063, 2087, 2111, 2136, 2160,
+ 2184, 2208, 2233, 2257, 2281, 2305, 2329, 2353,
+ 2377, 2401, 2425, 2449, 2473, 2497, 2521, 2545,
+ 2569, 2593, 2617, 2640, 2664, 2688, 2712, 2735,
+ 2759, 2783, 2806, 2830, 2853, 2877, 2900, 2924,
+ 2947, 2971, 2994, 3018, 3041, 3064, 3088, 3111,
+ 3134, 3157, 3180, 3204, 3227, 3250, 3273, 3296,
+ 3319, 3342, 3365, 3388, 3410, 3433, 3456, 3479,
+ 3502, 3524, 3547, 3570, 3592, 3615, 3637, 3660,
+ 3682, 3705, 3727, 3749, 3772, 3794, 3816, 3839,
+ 3861, 3883, 3905, 3927, 3949, 3971, 3993, 4015,
+ 4037, 4059, 4080, 4102, 4124, 4146, 4167, 4189,
+ 4211, 4232, 4254, 4275, 4296, 4318, 4339, 4360,
+ 4382, 4403, 4424, 4445, 4466, 4487, 4508, 4529,
+ 4550, 4571, 4592, 4613, 4633, 4654, 4675, 4695,
+ 4716, 4736, 4757, 4777, 4798, 4818, 4838, 4859,
+ 4879, 4899, 4919, 4939, 4959, 4979, 4999, 5019,
+ 5039, 5059, 5078, 5098, 5118, 5137, 5157, 5176,
+ 5196, 5215, 5235, 5254, 5273, 5292, 5311, 5331,
+ 5350, 5369, 5388, 5406, 5425, 5444, 5463, 5482,
+ 5500, 5519, 5537, 5556, 5574, 5593, 5611, 5629,
+ 5648, 5666, 5684, 5702, 5720, 5738, 5756, 5774,
+ 5791, 5809, 5827, 5844, 5862, 5880, 5897, 5914,
+ 5932, 5949, 5966, 5984, 6001, 6018, 6035, 6052,
+ 6069, 6085, 6102, 6119, 6136, 6152, 6169, 6185,
+ 6202, 6218, 6235, 6251, 6267, 6283, 6299, 6315,
+ 6331, 6347, 6363, 6379, 6395, 6410, 6426, 6441,
+ 6457, 6472, 6488, 6503, 6518, 6533, 6549, 6564,
+ 6579, 6594, 6608, 6623, 6638, 6653, 6667, 6682,
+ 6696, 6711, 6725, 6739, 6754, 6768, 6782, 6796,
+ 6810, 6824, 6838, 6852, 6865, 6879, 6893, 6906,
+ 6920, 6933, 6946, 6960, 6973, 6986, 6999, 7012,
+ 7025, 7038, 7051, 7064, 7076, 7089, 7101, 7114,
+ 7126, 7139, 7151, 7163, 7175, 7187, 7199, 7211,
+ 7223, 7235, 7247, 7259, 7270, 7282, 7293, 7305,
+ 7316, 7327, 7338, 7349, 7361, 7372, 7382, 7393,
+ 7404, 7415, 7425, 7436, 7446, 7457, 7467, 7478,
+ 7488, 7498, 7508, 7518, 7528, 7538, 7548, 7557,
+ 7567, 7577, 7586, 7596, 7605, 7614, 7623, 7633,
+ 7642, 7651, 7660, 7668, 7677, 7686, 7695, 7703,
+ 7712, 7720, 7728, 7737, 7745, 7753, 7761, 7769,
+ 7777, 7785, 7793, 7800, 7808, 7816, 7823, 7830,
+ 7838, 7845, 7852, 7859, 7866, 7873, 7880, 7887,
+ 7894, 7900, 7907, 7914, 7920, 7926, 7933, 7939,
+ 7945, 7951, 7957, 7963, 7969, 7975, 7980, 7986,
+ 7991, 7997, 8002, 8008, 8013, 8018, 8023, 8028,
+ 8033, 8038, 8043, 8047, 8052, 8057, 8061, 8066,
+ 8070, 8074, 8078, 8082, 8086, 8090, 8094, 8098,
+ 8102, 8105, 8109, 8113, 8116, 8119, 8123, 8126,
+ 8129, 8132, 8135, 8138, 8141, 8143, 8146, 8149,
+ 8151, 8153, 8156, 8158, 8160, 8162, 8164, 8166,
+ 8168, 8170, 8172, 8174, 8175, 8177, 8178, 8179,
+ 8181, 8182, 8183, 8184, 8185, 8186, 8187, 8187,
+ 8188, 8189, 8189, 8190, 8190, 8190, 8190, 8190,
+ 8191, 8190, 8190, 8190, 8190, 8190, 8189, 8189,
+ 8188, 8187, 8187, 8186, 8185, 8184, 8183, 8182,
+ 8181, 8179, 8178, 8177, 8175, 8174, 8172, 8170,
+ 8168, 8166, 8164, 8162, 8160, 8158, 8156, 8153,
+ 8151, 8149, 8146, 8143, 8141, 8138, 8135, 8132,
+ 8129, 8126, 8123, 8119, 8116, 8113, 8109, 8105,
+ 8102, 8098, 8094, 8090, 8086, 8082, 8078, 8074,
+ 8070, 8066, 8061, 8057, 8052, 8047, 8043, 8038,
+ 8033, 8028, 8023, 8018, 8013, 8008, 8002, 7997,
+ 7991, 7986, 7980, 7975, 7969, 7963, 7957, 7951,
+ 7945, 7939, 7933, 7926, 7920, 7914, 7907, 7900,
+ 7894, 7887, 7880, 7873, 7866, 7859, 7852, 7845,
+ 7838, 7830, 7823, 7816, 7808, 7800, 7793, 7785,
+ 7777, 7769, 7761, 7753, 7745, 7737, 7728, 7720,
+ 7712, 7703, 7695, 7686, 7677, 7668, 7660, 7651,
+ 7642, 7633, 7623, 7614, 7605, 7596, 7586, 7577,
+ 7567, 7557, 7548, 7538, 7528, 7518, 7508, 7498,
+ 7488, 7478, 7467, 7457, 7446, 7436, 7425, 7415,
+ 7404, 7393, 7382, 7372, 7361, 7349, 7338, 7327,
+ 7316, 7305, 7293, 7282, 7270, 7259, 7247, 7235,
+ 7223, 7211, 7199, 7187, 7175, 7163, 7151, 7139,
+ 7126, 7114, 7101, 7089, 7076, 7064, 7051, 7038,
+ 7025, 7012, 6999, 6986, 6973, 6960, 6946, 6933,
+ 6920, 6906, 6893, 6879, 6865, 6852, 6838, 6824,
+ 6810, 6796, 6782, 6768, 6754, 6739, 6725, 6711,
+ 6696, 6682, 6667, 6653, 6638, 6623, 6608, 6594,
+ 6579, 6564, 6549, 6533, 6518, 6503, 6488, 6472,
+ 6457, 6441, 6426, 6410, 6395, 6379, 6363, 6347,
+ 6331, 6315, 6299, 6283, 6267, 6251, 6235, 6218,
+ 6202, 6185, 6169, 6152, 6136, 6119, 6102, 6085,
+ 6069, 6052, 6035, 6018, 6001, 5984, 5966, 5949,
+ 5932, 5914, 5897, 5880, 5862, 5844, 5827, 5809,
+ 5791, 5774, 5756, 5738, 5720, 5702, 5684, 5666,
+ 5648, 5629, 5611, 5593, 5574, 5556, 5537, 5519,
+ 5500, 5482, 5463, 5444, 5425, 5406, 5388, 5369,
+ 5350, 5331, 5311, 5292, 5273, 5254, 5235, 5215,
+ 5196, 5176, 5157, 5137, 5118, 5098, 5078, 5059,
+ 5039, 5019, 4999, 4979, 4959, 4939, 4919, 4899,
+ 4879, 4859, 4838, 4818, 4798, 4777, 4757, 4736,
+ 4716, 4695, 4675, 4654, 4633, 4613, 4592, 4571,
+ 4550, 4529, 4508, 4487, 4466, 4445, 4424, 4403,
+ 4382, 4360, 4339, 4318, 4296, 4275, 4254, 4232,
+ 4211, 4189, 4167, 4146, 4124, 4102, 4080, 4059,
+ 4037, 4015, 3993, 3971, 3949, 3927, 3905, 3883,
+ 3861, 3839, 3816, 3794, 3772, 3749, 3727, 3705,
+ 3682, 3660, 3637, 3615, 3592, 3570, 3547, 3524,
+ 3502, 3479, 3456, 3433, 3410, 3388, 3365, 3342,
+ 3319, 3296, 3273, 3250, 3227, 3204, 3180, 3157,
+ 3134, 3111, 3088, 3064, 3041, 3018, 2994, 2971,
+ 2947, 2924, 2900, 2877, 2853, 2830, 2806, 2783,
+ 2759, 2735, 2712, 2688, 2664, 2640, 2617, 2593,
+ 2569, 2545, 2521, 2497, 2473, 2449, 2425, 2401,
+ 2377, 2353, 2329, 2305, 2281, 2257, 2233, 2208,
+ 2184, 2160, 2136, 2111, 2087, 2063, 2038, 2014,
+ 1990, 1965, 1941, 1917, 1892, 1868, 1843, 1819,
+ 1794, 1770, 1745, 1721, 1696, 1671, 1647, 1622,
+ 1597, 1573, 1548, 1523, 1499, 1474, 1449, 1425,
+ 1400, 1375, 1350, 1326, 1301, 1276, 1251, 1226,
+ 1201, 1177, 1152, 1127, 1102, 1077, 1052, 1027,
+ 1002, 977, 952, 927, 902, 877, 852, 827,
+ 802, 777, 752, 727, 702, 677, 652, 627,
+ 602, 577, 552, 527, 502, 477, 452, 427,
+ 401, 376, 351, 326, 301, 276, 251, 226,
+ 201, 175, 150, 125, 100, 75, 50, 25,
+ };
diff --git a/Plugins/eSpeak/eSpeak/speak_lib.cpp b/Plugins/eSpeak/eSpeak/speak_lib.cpp
new file mode 100644
index 0000000..29c3e84
--- /dev/null
+++ b/Plugins/eSpeak/eSpeak/speak_lib.cpp
@@ -0,0 +1,1153 @@
+/***************************************************************************
+ * Copyright (C) 2005 to 2007 by Jonathan Duddington *
+ * email: jonsd@users.sourceforge.net *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 3 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write see: *
+ * <http://www.gnu.org/licenses/>. *
+ ***************************************************************************/
+
+#include "StdAfx.h"
+
+#include "stdio.h"
+#include "ctype.h"
+#include "string.h"
+#include "stdlib.h"
+#include "wchar.h"
+#include "locale.h"
+#include <assert.h>
+#include <time.h>
+
+#include "speech.h"
+
+#include <sys/stat.h>
+#ifndef PLATFORM_WINDOWS
+#include <unistd.h>
+#endif
+
+#include "speak_lib.h"
+#include "phoneme.h"
+#include "synthesize.h"
+#include "voice.h"
+#include "translate.h"
+#include "debug.h"
+
+#include "fifo.h"
+#include "event.h"
+#include "wave.h"
+
+unsigned char *outbuf=NULL;
+extern espeak_VOICE voice_selected;
+
+espeak_EVENT *event_list=NULL;
+int event_list_ix=0;
+int n_event_list;
+long count_samples;
+void* my_audio=NULL;
+
+static unsigned int my_unique_identifier=0;
+static void* my_user_data=NULL;
+static espeak_AUDIO_OUTPUT my_mode=AUDIO_OUTPUT_SYNCHRONOUS;
+static int synchronous_mode = 1;
+t_espeak_callback* synth_callback = NULL;
+int (* uri_callback)(int, const char *, const char *) = NULL;
+int (* phoneme_callback)(const char *) = NULL;
+
+char path_home[N_PATH_HOME]; // this is the espeak-data directory
+
+
+#ifdef USE_ASYNC
+
+static int dispatch_audio(short* outbuf, int length, espeak_EVENT* event)
+{//======================================================================
+ ENTER("dispatch_audio");
+
+ int a_wave_can_be_played = fifo_is_command_enabled();
+
+#ifdef DEBUG_ENABLED
+ SHOW("*** dispatch_audio > uid=%d, [write=%p (%d bytes)], sample=%d, a_wave_can_be_played = %d\n",
+ (event) ? event->unique_identifier : 0, wave_test_get_write_buffer(), 2*length,
+ (event) ? event->sample : 0,
+ a_wave_can_be_played);
+#endif
+
+ switch(my_mode)
+ {
+ case AUDIO_OUTPUT_PLAYBACK:
+ {
+ if (outbuf && length && a_wave_can_be_played)
+ {
+ wave_write (my_audio, (char*)outbuf, 2*length);
+ }
+
+ while(a_wave_can_be_played) {
+ // TBD: some event are filtered here but some insight might be given
+ // TBD: in synthesise.cpp for avoiding to create WORDs with size=0.
+ // TBD: For example sentence "or ALT)." returns three words
+ // "or", "ALT" and "".
+ // TBD: the last one has its size=0.
+ if (event && (event->type == espeakEVENT_WORD) && (event->length==0))
+ {
+ break;
+ }
+ espeak_ERROR a_error = event_declare(event);
+ if (a_error != EE_BUFFER_FULL)
+ {
+ break;
+ }
+ SHOW_TIME("dispatch_audio > EE_BUFFER_FULL\n");
+ usleep(10000);
+ a_wave_can_be_played = fifo_is_command_enabled();
+ }
+ }
+ break;
+
+ case AUDIO_OUTPUT_RETRIEVAL:
+ if (synth_callback)
+ {
+ synth_callback(outbuf, length, event);
+ }
+ break;
+
+ case AUDIO_OUTPUT_SYNCHRONOUS:
+ case AUDIO_OUTPUT_SYNCH_PLAYBACK:
+ break;
+ }
+
+ if (!a_wave_can_be_played)
+ {
+ SHOW_TIME("dispatch_audio > synth must be stopped!\n");
+ }
+
+ SHOW_TIME("LEAVE dispatch_audio\n");
+
+ return (a_wave_can_be_played==0); // 1 = stop synthesis
+}
+
+
+
+static int create_events(short* outbuf, int length, espeak_EVENT* event, uint32_t the_write_pos)
+{//=====================================================================
+ int finished;
+ int i=0;
+
+ // The audio data are written to the output device.
+ // The list of events in event_list (index: event_list_ix) is read:
+ // Each event is declared to the "event" object which stores them internally.
+ // The event object is responsible of calling the external callback
+ // as soon as the relevant audio sample is played.
+
+ do
+ { // for each event
+ espeak_EVENT* event;
+ if (event_list_ix == 0)
+ {
+ event = NULL;
+ }
+ else
+ {
+ event = event_list + i;
+#ifdef DEBUG_ENABLED
+ SHOW("Synthesize: event->sample(%d) + %d = %d\n", event->sample, the_write_pos, event->sample + the_write_pos);
+#endif
+ event->sample += the_write_pos;
+ }
+#ifdef DEBUG_ENABLED
+ SHOW("*** Synthesize: i=%d (event_list_ix=%d), length=%d\n",i,event_list_ix,length);
+#endif
+ finished = dispatch_audio((short *)outbuf, length, event);
+ length = 0; // the wave data are played once.
+ i++;
+ } while((i < event_list_ix) && !finished);
+ return finished;
+}
+
+
+int sync_espeak_terminated_msg( uint unique_identifier, void* user_data)
+{//=====================================================================
+ ENTER("sync_espeak_terminated_msg");
+
+ int finished=0;
+
+ memset(event_list, 0, 2*sizeof(espeak_EVENT));
+
+ event_list[0].type = espeakEVENT_MSG_TERMINATED;
+ event_list[0].unique_identifier = unique_identifier;
+ event_list[0].user_data = user_data;
+ event_list[1].type = espeakEVENT_LIST_TERMINATED;
+ event_list[1].unique_identifier = unique_identifier;
+ event_list[1].user_data = user_data;
+
+ if (my_mode==AUDIO_OUTPUT_PLAYBACK)
+ {
+ while(1)
+ {
+ espeak_ERROR a_error = event_declare(event_list);
+ if (a_error != EE_BUFFER_FULL)
+ {
+ break;
+ }
+ SHOW_TIME("sync_espeak_terminated_msg > EE_BUFFER_FULL\n");
+ usleep(10000);
+ }
+ }
+ else
+ {
+ if (synth_callback)
+ {
+ finished=synth_callback(NULL,0,event_list);
+ }
+ }
+ return finished;
+}
+
+#endif
+
+
+static void select_output(espeak_AUDIO_OUTPUT output_type)
+{//=======================================================
+ my_mode = output_type;
+ my_audio = NULL;
+ synchronous_mode = 1;
+ option_waveout = 1; // inhibit portaudio callback from wavegen.cpp
+
+ switch(my_mode)
+ {
+ case AUDIO_OUTPUT_PLAYBACK:
+ synchronous_mode = 0;
+#ifdef USE_ASYNC
+ wave_init();
+ wave_set_callback_is_output_enabled( fifo_is_command_enabled);
+ my_audio = wave_open("alsa");
+ event_init();
+#endif
+ break;
+
+ case AUDIO_OUTPUT_RETRIEVAL:
+ synchronous_mode = 0;
+ break;
+
+ case AUDIO_OUTPUT_SYNCHRONOUS:
+ break;
+
+ case AUDIO_OUTPUT_SYNCH_PLAYBACK:
+ option_waveout = 0;
+ WavegenInitSound();
+ break;
+ }
+} // end of select_output
+
+
+
+
+int GetFileLength(const char *filename)
+{//====================================
+ struct stat statbuf;
+
+ if(stat(filename,&statbuf) != 0)
+ return(0);
+
+ if((statbuf.st_mode & S_IFMT) == S_IFDIR)
+ // if(S_ISDIR(statbuf.st_mode))
+ return(-2); // a directory
+
+ return(statbuf.st_size);
+} // end of GetFileLength
+
+
+char *Alloc(int size)
+{//==================
+ char *p;
+ if((p = (char *)malloc(size)) == NULL)
+ fprintf(stderr,"Can't allocate memory\n"); // I was told that size+1 fixes a crash on 64-bit systems
+ return(p);
+}
+
+void Free(void *ptr)
+{//=================
+ if(ptr != NULL)
+ free(ptr);
+}
+
+
+
+static void init_path(const char *path)
+{//====================================
+#ifdef PLATFORM_WINDOWS
+ HKEY RegKey;
+ unsigned long size;
+ unsigned long var_type;
+ char *env;
+ unsigned char buf[sizeof(path_home)-13];
+
+ if(path != NULL)
+ {
+ strncpy(path_home,path,sizeof(path_home)-1);
+ path_home[sizeof(path_home)-1]=0;
+ return;
+ }
+
+ if((env = getenv("ESPEAK_DATA_PATH")) != NULL)
+ {
+ sprintf(path_home,"%s/espeak-data",env);
+ if(GetFileLength(path_home) == -2)
+ return; // an espeak-data directory exists
+ }
+
+ buf[0] = 0;
+ RegOpenKeyExA(HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Speech\\Voices\\Tokens\\eSpeak", 0, KEY_READ, &RegKey);
+ size = sizeof(buf);
+ var_type = REG_SZ;
+ RegQueryValueExA(RegKey, "path", 0, &var_type, buf, &size);
+
+ sprintf(path_home,"%s\\espeak-data",buf);
+
+#else
+ char *env;
+
+ if(path != NULL)
+ {
+ snprintf(path_home,sizeof(path_home),"%s/espeak-data",path);
+ return;
+ }
+
+ // check for environment variable
+ if((env = getenv("ESPEAK_DATA_PATH")) != NULL)
+ {
+ snprintf(path_home,sizeof(path_home),"%s/espeak-data",env);
+ if(GetFileLength(path_home) == -2)
+ return; // an espeak-data directory exists
+ }
+
+ snprintf(path_home,sizeof(path_home),"%s/espeak-data",getenv("HOME"));
+ if(access(path_home,R_OK) != 0)
+ {
+ strcpy(path_home,PATH_ESPEAK_DATA);
+ }
+#endif
+}
+
+static int initialise(void)
+{//========================
+ int param;
+ int result;
+
+ LoadConfig();
+ WavegenInit(22050,0); // 22050
+ if((result = LoadPhData()) != 1)
+ {
+ if(result == -1)
+ fprintf(stderr,"Failed to load espeak-data\n");
+ else
+ fprintf(stderr,"Wrong version of espeak-data 0x%x (expects 0x%x) at %s\n",result,version_phdata,path_home);
+ }
+
+ memset(&voice_selected,0,sizeof(voice_selected));
+ SetVoiceStack(NULL);
+ SynthesizeInit();
+ InitNamedata();
+
+ for(param=0; param<N_SPEECH_PARAM; param++)
+ param_stack[0].parameter[param] = param_defaults[param];
+
+ return(0);
+}
+
+
+static espeak_ERROR Synthesize(unsigned int unique_identifier, const void *text, int flags)
+{//========================================================================================
+ // Fill the buffer with output sound
+ int length;
+ int finished = 0;
+ int count_buffers = 0;
+#ifdef USE_ASYNC
+ uint32_t a_write_pos=0;
+#endif
+
+#ifdef DEBUG_ENABLED
+ ENTER("Synthesize");
+ if (text)
+ {
+ SHOW("Synthesize > uid=%d, flags=%d, >>>text=%s<<<\n", unique_identifier, flags, text);
+ }
+#endif
+
+ if((outbuf==NULL) || (event_list==NULL))
+ return(EE_INTERNAL_ERROR); // espeak_Initialize() has not been called
+
+ option_multibyte = flags & 7;
+ option_ssml = flags & espeakSSML;
+ option_phoneme_input = flags & espeakPHONEMES;
+ option_endpause = flags & espeakENDPAUSE;
+
+ count_samples = 0;
+
+#ifdef USE_ASYNC
+ if(my_mode == AUDIO_OUTPUT_PLAYBACK)
+ {
+ a_write_pos = wave_get_write_position(my_audio);
+ }
+#endif
+
+ if(translator == NULL)
+ {
+ SetVoiceByName("default");
+ }
+
+ SpeakNextClause(NULL,text,0);
+
+ if(my_mode == AUDIO_OUTPUT_SYNCH_PLAYBACK)
+ {
+ for(;;)
+ {
+#ifdef PLATFORM_WINDOWS
+ Sleep(300); // 0.3s
+#else
+#ifdef USE_NANOSLEEP
+ struct timespec period;
+ struct timespec remaining;
+ period.tv_sec = 0;
+ period.tv_nsec = 300000000; // 0.3 sec
+ nanosleep(&period,&remaining);
+#else
+ sleep(1);
+#endif
+#endif
+ if(SynthOnTimer() != 0)
+ break;
+ }
+ return(EE_OK);
+ }
+
+ for(;;)
+ {
+#ifdef DEBUG_ENABLED
+ SHOW("Synthesize > %s\n","for (next)");
+#endif
+ out_ptr = outbuf;
+ out_end = &outbuf[outbuf_size];
+ event_list_ix = 0;
+ WavegenFill(0);
+
+ length = (out_ptr - outbuf)/2;
+ count_samples += length;
+ event_list[event_list_ix].type = espeakEVENT_LIST_TERMINATED; // indicates end of event list
+ event_list[event_list_ix].unique_identifier = my_unique_identifier;
+ event_list[event_list_ix].user_data = my_user_data;
+
+ count_buffers++;
+ if (my_mode==AUDIO_OUTPUT_PLAYBACK)
+ {
+#ifdef USE_ASYNC
+ finished = create_events((short *)outbuf, length, event_list, a_write_pos);
+ length = 0; // the wave data are played once.
+#endif
+ }
+ else
+ {
+ finished = synth_callback((short *)outbuf, length, event_list);
+ }
+ if(finished)
+ {
+ SpeakNextClause(NULL,0,2); // stop
+ break;
+ }
+
+ if(Generate(phoneme_list,&n_phoneme_list,1)==0)
+ {
+ if(WcmdqUsed() == 0)
+ {
+ // don't process the next clause until the previous clause has finished generating speech.
+ // This ensures that <audio> tag (which causes end-of-clause) is at a sound buffer boundary
+
+ event_list[0].type = espeakEVENT_LIST_TERMINATED;
+ event_list[0].unique_identifier = my_unique_identifier;
+ event_list[0].user_data = my_user_data;
+
+ if(SpeakNextClause(NULL,NULL,1)==0)
+ {
+#ifdef USE_ASYNC
+ if (my_mode==AUDIO_OUTPUT_PLAYBACK)
+ {
+ dispatch_audio(NULL, 0, NULL); // TBD: test case
+ }
+ else
+ {
+ synth_callback(NULL, 0, event_list); // NULL buffer ptr indicates end of data
+ }
+#else
+ synth_callback(NULL, 0, event_list); // NULL buffer ptr indicates end of data
+#endif
+ break;
+ }
+ }
+ }
+ }
+
+ return(EE_OK);
+} // end of Synthesize
+
+#ifdef DEBUG_ENABLED
+static const char* label[] = {
+ "END_OF_EVENT_LIST",
+ "WORD",
+ "SENTENCE",
+ "MARK",
+ "PLAY",
+ "END"};
+#endif
+
+
+void MarkerEvent(int type, unsigned int char_position, int value, unsigned char *out_ptr)
+{//======================================================================================
+ // type: 1=word, 2=sentence, 3=named mark, 4=play audio, 5=end
+ ENTER("MarkerEvent");
+ espeak_EVENT *ep;
+ double time;
+
+ if((event_list == NULL) || (event_list_ix >= (n_event_list-2)))
+ return;
+
+ ep = &event_list[event_list_ix++];
+ ep->type = (espeak_EVENT_TYPE)type;
+ ep->unique_identifier = my_unique_identifier;
+ ep->user_data = my_user_data;
+ ep->text_position = char_position & 0xffff;
+ ep->length = char_position >> 24;
+
+ time = (double(count_samples + mbrola_delay + (out_ptr - out_start)/2)*1000.0)/samplerate;
+ ep->audio_position = int(time);
+ ep->sample = (count_samples + mbrola_delay + (out_ptr - out_start)/2);
+
+#ifdef DEBUG_ENABLED
+ SHOW("MarkerEvent > count_samples=%d, out_ptr=%x, out_start=0x%x\n",count_samples, out_ptr, out_start);
+ SHOW("*** MarkerEvent > type=%s, uid=%d, text_pos=%d, length=%d, audio_position=%d, sample=%d\n",
+ label[ep->type], ep->unique_identifier, ep->text_position, ep->length,
+ ep->audio_position, ep->sample);
+#endif
+
+ if((type == espeakEVENT_MARK) || (type == espeakEVENT_PLAY))
+ ep->id.name = &namedata[value];
+ else
+ ep->id.number = value;
+} // end of MarkerEvent
+
+
+
+
+espeak_ERROR sync_espeak_Synth(unsigned int unique_identifier, const void *text, size_t size,
+ unsigned int position, espeak_POSITION_TYPE position_type,
+ unsigned int end_position, unsigned int flags, void* user_data)
+{//===========================================================================
+
+#ifdef DEBUG_ENABLED
+ ENTER("sync_espeak_Synth");
+ SHOW("sync_espeak_Synth > position=%d, position_type=%d, end_position=%d, flags=%d, user_data=0x%x, text=%s\n", position, position_type, end_position, flags, user_data, text);
+#endif
+
+ espeak_ERROR aStatus;
+
+ InitText(flags);
+ my_unique_identifier = unique_identifier;
+ my_user_data = user_data;
+
+ switch(position_type)
+ {
+ case POS_CHARACTER:
+ skip_characters = position;
+ break;
+
+ case POS_WORD:
+ skip_words = position;
+ break;
+
+ case POS_SENTENCE:
+ skip_sentences = position;
+ break;
+
+ }
+ if(skip_characters || skip_words || skip_sentences)
+ skipping_text = 1;
+
+ end_character_position = end_position;
+
+ aStatus = Synthesize(unique_identifier, text, flags);
+ #ifdef USE_ASYNC
+ wave_flush(my_audio);
+ #endif
+
+ SHOW_TIME("LEAVE sync_espeak_Synth");
+ return aStatus;
+} // end of sync_espeak_Synth
+
+
+
+
+espeak_ERROR sync_espeak_Synth_Mark(unsigned int unique_identifier, const void *text, size_t size,
+ const char *index_mark, unsigned int end_position,
+ unsigned int flags, void* user_data)
+{//=========================================================================
+ espeak_ERROR aStatus;
+
+ InitText(flags);
+
+ my_unique_identifier = unique_identifier;
+ my_user_data = user_data;
+
+ if(index_mark != NULL)
+ {
+ strncpy0(skip_marker, index_mark, sizeof(skip_marker));
+ skipping_text = 1;
+ }
+
+ end_character_position = end_position;
+
+
+ aStatus = Synthesize(unique_identifier, text, flags | espeakSSML);
+ SHOW_TIME("LEAVE sync_espeak_Synth_Mark");
+
+ return (aStatus);
+} // end of sync_espeak_Synth_Mark
+
+
+
+void sync_espeak_Key(const char *key)
+{//==================================
+ // symbolic name, symbolicname_character - is there a system resource of symbolic names per language?
+ int letter;
+ int ix;
+
+ ix = utf8_in(&letter,key);
+ if(key[ix] == 0)
+ {
+ // a single character
+ sync_espeak_Char(letter);
+ return;
+ }
+
+ my_unique_identifier = 0;
+ my_user_data = NULL;
+ Synthesize(0, key,0); // speak key as a text string
+}
+
+
+void sync_espeak_Char(wchar_t character)
+{//=====================================
+ // is there a system resource of character names per language?
+ char buf[80];
+ my_unique_identifier = 0;
+ my_user_data = NULL;
+
+ sprintf(buf,"<say-as interpret-as=\"tts:char\">&#%d;</say-as>",character);
+ Synthesize(0, buf,espeakSSML);
+}
+
+
+
+void sync_espeak_SetPunctuationList(const wchar_t *punctlist)
+{//==========================================================
+ // Set the list of punctuation which are spoken for "some".
+ my_unique_identifier = 0;
+ my_user_data = NULL;
+
+ wcsncpy(option_punctlist, punctlist, N_PUNCTLIST);
+ option_punctlist[N_PUNCTLIST-1] = 0;
+} // end of sync_espeak_SetPunctuationList
+
+
+
+
+//#pragma GCC visibility push(default)
+
+
+ESPEAK_API void espeak_SetSynthCallback(t_espeak_callback* SynthCallback)
+{//======================================================================
+ ENTER("espeak_SetSynthCallback");
+ synth_callback = SynthCallback;
+#ifdef USE_ASYNC
+ event_set_callback(synth_callback);
+#endif
+}
+
+ESPEAK_API void espeak_SetUriCallback(int (* UriCallback)(int, const char*, const char *))
+{//=======================================================================================
+ ENTER("espeak_SetUriCallback");
+ uri_callback = UriCallback;
+}
+
+
+ESPEAK_API void espeak_SetPhonemeCallback(int (* PhonemeCallback)(const char*))
+{//===========================================================================
+ phoneme_callback = PhonemeCallback;
+}
+
+ESPEAK_API int espeak_Initialize(espeak_AUDIO_OUTPUT output_type, int buf_length, const char *path, int options)
+{//=============================================================================================================
+ENTER("espeak_Initialize");
+ int param;
+
+ // It seems that the wctype functions don't work until the locale has been set
+ // to something other than the default "C". Then, not only Latin1 but also the
+ // other characters give the correct results with iswalpha() etc.
+#ifdef PLATFORM_RISCOS
+ setlocale(LC_CTYPE,"ISO8859-1");
+#else
+ if(setlocale(LC_CTYPE,"en_US.UTF-8") == NULL)
+ {
+ if(setlocale(LC_CTYPE,"UTF-8") == NULL)
+ setlocale(LC_CTYPE,"");
+ }
+#endif
+
+ init_path(path);
+ initialise();
+ select_output(output_type);
+
+ // buflength is in mS, allocate 2 bytes per sample
+ if(buf_length == 0)
+ buf_length = 200;
+ outbuf_size = (buf_length * samplerate)/500;
+ outbuf = (unsigned char*)realloc(outbuf,outbuf_size);
+ if((out_start = outbuf) == NULL)
+ return(EE_INTERNAL_ERROR);
+
+ // allocate space for event list. Allow 200 events per second.
+ // Add a constant to allow for very small buf_length
+ n_event_list = (buf_length*200)/1000 + 20;
+ if((event_list = (espeak_EVENT *)realloc(event_list,sizeof(espeak_EVENT) * n_event_list)) == NULL)
+ return(EE_INTERNAL_ERROR);
+
+ option_phonemes = 0;
+ option_phoneme_events = (options & 1);
+
+ SetVoiceByName("default");
+
+ for(param=0; param<N_SPEECH_PARAM; param++)
+ param_stack[0].parameter[param] = param_defaults[param];
+
+ SetParameter(espeakRATE,170,0);
+ SetParameter(espeakVOLUME,100,0);
+ SetParameter(espeakCAPITALS,option_capitals,0);
+ SetParameter(espeakPUNCTUATION,option_punctuation,0);
+ SetParameter(espeakWORDGAP,0,0);
+ DoVoiceChange(voice);
+
+#ifdef USE_ASYNC
+ fifo_init();
+#endif
+
+ return(samplerate);
+}
+
+
+
+ESPEAK_API espeak_ERROR espeak_Synth(const void *text, size_t size,
+ unsigned int position,
+ espeak_POSITION_TYPE position_type,
+ unsigned int end_position, unsigned int flags,
+ unsigned int* unique_identifier, void* user_data)
+{//=====================================================================================
+#ifdef DEBUG_ENABLED
+ ENTER("espeak_Synth");
+ SHOW("espeak_Synth > position=%d, position_type=%d, end_position=%d, flags=%d, user_data=0x%x, text=%s\n", position, position_type, end_position, flags, user_data, text);
+#endif
+
+ espeak_ERROR a_error=EE_INTERNAL_ERROR;
+ static unsigned int temp_identifier;
+
+ if (unique_identifier == NULL)
+ {
+ unique_identifier = &temp_identifier;
+ }
+ *unique_identifier = 0;
+
+ if(synchronous_mode)
+ {
+ return(sync_espeak_Synth(0,text,size,position,position_type,end_position,flags,user_data));
+ }
+
+#ifdef USE_ASYNC
+ // Create the text command
+ t_espeak_command* c1 = create_espeak_text(text, size, position, position_type, end_position, flags, user_data);
+
+ // Retrieve the unique identifier
+ *unique_identifier = c1->u.my_text.unique_identifier;
+
+ // Create the "terminated msg" command (same uid)
+ t_espeak_command* c2 = create_espeak_terminated_msg(*unique_identifier, user_data);
+
+ // Try to add these 2 commands (single transaction)
+ if (c1 && c2)
+ {
+ a_error = fifo_add_commands(c1, c2);
+ if (a_error != EE_OK)
+ {
+ delete_espeak_command(c1);
+ delete_espeak_command(c2);
+ c1=c2=NULL;
+ }
+ }
+ else
+ {
+ delete_espeak_command(c1);
+ delete_espeak_command(c2);
+ }
+
+#endif
+ return a_error;
+} // end of espeak_Synth
+
+
+
+ESPEAK_API espeak_ERROR espeak_Synth_Mark(const void *text, size_t size,
+ const char *index_mark,
+ unsigned int end_position,
+ unsigned int flags,
+ unsigned int* unique_identifier,
+ void* user_data)
+{//=========================================================================
+#ifdef DEBUG_ENABLED
+ ENTER("espeak_Synth_Mark");
+ SHOW("espeak_Synth_Mark > index_mark=%s, end_position=%d, flags=%d, text=%s\n", index_mark, end_position, flags, text);
+#endif
+
+ espeak_ERROR a_error=EE_OK;
+ static unsigned int temp_identifier;
+
+ if (unique_identifier == NULL)
+ {
+ unique_identifier = &temp_identifier;
+ }
+ *unique_identifier = 0;
+
+ if(synchronous_mode)
+ {
+ return(sync_espeak_Synth_Mark(0,text,size,index_mark,end_position,flags,user_data));
+ }
+
+#ifdef USE_ASYNC
+ // Create the mark command
+ t_espeak_command* c1 = create_espeak_mark(text, size, index_mark, end_position,
+ flags, user_data);
+
+ // Retrieve the unique identifier
+ *unique_identifier = c1->u.my_mark.unique_identifier;
+
+ // Create the "terminated msg" command (same uid)
+ t_espeak_command* c2 = create_espeak_terminated_msg(*unique_identifier, user_data);
+
+ // Try to add these 2 commands (single transaction)
+ if (c1 && c2)
+ {
+ a_error = fifo_add_commands(c1, c2);
+ if (a_error != EE_OK)
+ {
+ delete_espeak_command(c1);
+ delete_espeak_command(c2);
+ c1=c2=NULL;
+ }
+ }
+ else
+ {
+ delete_espeak_command(c1);
+ delete_espeak_command(c2);
+ }
+
+#endif
+ return a_error;
+} // end of espeak_Synth_Mark
+
+
+
+ESPEAK_API espeak_ERROR espeak_Key(const char *key)
+{//================================================
+ ENTER("espeak_Key");
+ // symbolic name, symbolicname_character - is there a system resource of symbolicnames per language
+
+ espeak_ERROR a_error = EE_OK;
+
+ if(synchronous_mode)
+ {
+ sync_espeak_Key(key);
+ return(EE_OK);
+ }
+
+#ifdef USE_ASYNC
+ t_espeak_command* c = create_espeak_key( key);
+ a_error = fifo_add_command(c);
+ if (a_error != EE_OK)
+ {
+ delete_espeak_command(c);
+ }
+
+#endif
+ return a_error;
+}
+
+
+ESPEAK_API espeak_ERROR espeak_Char(wchar_t character)
+{//===========================================
+ ENTER("espeak_Char");
+ // is there a system resource of character names per language?
+
+#ifdef USE_ASYNC
+ espeak_ERROR a_error;
+
+ if(synchronous_mode)
+ {
+ sync_espeak_Char(character);
+ return(EE_OK);
+ }
+
+ t_espeak_command* c = create_espeak_char( character);
+ a_error = fifo_add_command(c);
+ if (a_error != EE_OK)
+ {
+ delete_espeak_command(c);
+ }
+ return a_error;
+#else
+ sync_espeak_Char(character);
+ return(EE_OK);
+#endif
+}
+
+
+ESPEAK_API espeak_ERROR espeak_SetVoiceByName(const char *name)
+{//============================================================
+ ENTER("espeak_SetVoiceByName");
+
+//#ifdef USE_ASYNC
+// I don't think there's a need to queue change voice requests
+#ifdef deleted
+ espeak_ERROR a_error;
+
+ if(synchronous_mode)
+ {
+ return(SetVoiceByName(name));
+ }
+
+ t_espeak_command* c = create_espeak_voice_name(name);
+ a_error = fifo_add_command(c);
+ if (a_error != EE_OK)
+ {
+ delete_espeak_command(c);
+ }
+ return a_error;
+#else
+ return(SetVoiceByName(name));
+#endif
+} // end of espeak_SetVoiceByName
+
+
+
+ESPEAK_API espeak_ERROR espeak_SetVoiceByProperties(espeak_VOICE *voice_selector)
+{//==============================================================================
+ ENTER("espeak_SetVoiceByProperties");
+
+//#ifdef USE_ASYNC
+#ifdef deleted
+ espeak_ERROR a_error;
+
+ if(synchronous_mode)
+ {
+ return(SetVoiceByProperties(voice_selector));
+ }
+
+ t_espeak_command* c = create_espeak_voice_spec( voice_selector);
+ a_error = fifo_add_command(c);
+ if (a_error != EE_OK)
+ {
+ delete_espeak_command(c);
+ }
+ return a_error;
+#else
+ return(SetVoiceByProperties(voice_selector));
+#endif
+} // end of espeak_SetVoiceByProperties
+
+
+ESPEAK_API int espeak_GetParameter(espeak_PARAMETER parameter, int current)
+{//========================================================================
+ ENTER("espeak_GetParameter");
+ // current: 0=default value, 1=current value
+ if(current)
+ {
+ return(param_stack[0].parameter[parameter]);
+ }
+ else
+ {
+ return(param_defaults[parameter]);
+ }
+} // end of espeak_GetParameter
+
+
+ESPEAK_API espeak_ERROR espeak_SetParameter(espeak_PARAMETER parameter, int value, int relative)
+{//=============================================================================================
+ ENTER("espeak_SetParameter");
+
+#ifdef USE_ASYNC
+ espeak_ERROR a_error;
+
+ if(synchronous_mode)
+ {
+ SetParameter(parameter,value,relative);
+ return(EE_OK);
+ }
+
+ t_espeak_command* c = create_espeak_parameter(parameter, value, relative);
+
+ a_error = fifo_add_command(c);
+ if (a_error != EE_OK)
+ {
+ delete_espeak_command(c);
+ }
+ return a_error;
+#else
+ SetParameter(parameter,value,relative);
+ return(EE_OK);
+#endif
+}
+
+
+ESPEAK_API espeak_ERROR espeak_SetPunctuationList(const wchar_t *punctlist)
+{//================================================================
+ ENTER("espeak_SetPunctuationList");
+ // Set the list of punctuation which are spoken for "some".
+
+#ifdef USE_ASYNC
+ espeak_ERROR a_error;
+
+ if(synchronous_mode)
+ {
+ sync_espeak_SetPunctuationList(punctlist);
+ return(EE_OK);
+ }
+
+ t_espeak_command* c = create_espeak_punctuation_list( punctlist);
+ a_error = fifo_add_command(c);
+ if (a_error != EE_OK)
+ {
+ delete_espeak_command(c);
+ }
+ return a_error;
+#else
+ sync_espeak_SetPunctuationList(punctlist);
+ return(EE_OK);
+#endif
+} // end of espeak_SetPunctuationList
+
+
+ESPEAK_API void espeak_SetPhonemeTrace(int value, FILE *stream)
+{//============================================================
+ ENTER("espeak_SetPhonemes");
+ /* Controls the output of phoneme symbols for the text
+ value=0 No phoneme output (default)
+ value=1 Output the translated phoneme symbols for the text
+ value=2 as (1), but also output a trace of how the translation was done (matching rules and list entries)
+ */
+ option_phonemes = value;
+ f_trans = stream;
+ if(stream == NULL)
+ f_trans = stderr;
+
+} // end of espeak_SetPhonemes
+
+
+ESPEAK_API void espeak_CompileDictionary(const char *path, FILE *log, int flags)
+{//=============================================================================
+ ENTER("espeak_CompileDictionary");
+ CompileDictionary(path, dictionary_name, log, NULL, flags);
+} // end of espeak_CompileDirectory
+
+
+ESPEAK_API espeak_ERROR espeak_Cancel(void)
+{//===============================
+#ifdef USE_ASYNC
+ ENTER("espeak_Cancel");
+ fifo_stop();
+ event_clear_all();
+
+ if(my_mode == AUDIO_OUTPUT_PLAYBACK)
+ {
+ wave_close(my_audio);
+ }
+ SHOW_TIME("espeak_Cancel > LEAVE");
+#endif
+ embedded_value[EMBED_T] = 0; // reset echo for pronunciation announcements
+ return EE_OK;
+} // end of espeak_Cancel
+
+
+ESPEAK_API int espeak_IsPlaying(void)
+{//==================================
+// ENTER("espeak_IsPlaying");
+#ifdef USE_ASYNC
+ if((my_mode == AUDIO_OUTPUT_PLAYBACK) && wave_is_busy(my_audio))
+ return(1);
+
+ return(fifo_is_busy());
+#else
+ return(0);
+#endif
+} // end of espeak_IsPlaying
+
+
+ESPEAK_API espeak_ERROR espeak_Synchronize(void)
+{//=============================================
+#ifdef USE_ASYNC
+ SHOW_TIME("espeak_Synchronize > ENTER");
+ while (espeak_IsPlaying())
+ {
+ usleep(20000);
+ }
+#endif
+ SHOW_TIME("espeak_Synchronize > LEAVE");
+ return EE_OK;
+} // end of espeak_Synchronize
+
+
+extern void FreePhData(void);
+
+ESPEAK_API espeak_ERROR espeak_Terminate(void)
+{//===========================================
+ ENTER("espeak_Terminate");
+#ifdef USE_ASYNC
+ fifo_stop();
+ fifo_terminate();
+ event_terminate();
+
+ if(my_mode == AUDIO_OUTPUT_PLAYBACK)
+ {
+ wave_close(my_audio);
+ wave_terminate();
+ }
+
+#endif
+ Free(event_list);
+ event_list = NULL;
+ Free(outbuf);
+ outbuf = NULL;
+ FreePhData();
+
+ return EE_OK;
+} // end of espeak_Terminate
+
+ESPEAK_API const char *espeak_Info(void *)
+{//=======================================
+ return(version_string);
+}
+
+//#pragma GCC visibility pop
+
+
diff --git a/Plugins/eSpeak/eSpeak/speak_lib.h b/Plugins/eSpeak/eSpeak/speak_lib.h
new file mode 100644
index 0000000..7945fbc
--- /dev/null
+++ b/Plugins/eSpeak/eSpeak/speak_lib.h
@@ -0,0 +1,604 @@
+#ifndef SPEAK_LIB_H
+#define SPEAK_LIB_H
+/***************************************************************************
+ * Copyright (C) 2005 to 2007 by Jonathan Duddington *
+ * email: jonsd@users.sourceforge.net *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 3 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, see: *
+ * <http://www.gnu.org/licenses/>. *
+ ***************************************************************************/
+
+
+/*************************************************************/
+/* This is the header file for the library version of espeak */
+/* */
+/*************************************************************/
+
+#include <stdio.h>
+
+#define ESPEAK_API_REVISION 3
+/*
+Revision 2
+ Added parameter "options" to eSpeakInitialize()
+
+Revision 3
+ Added espeakWORDGAP to espeak_PARAMETER
+
+Revision 4
+ Added flags parameter to espeak_CompileDictionary()
+
+*/
+ /********************/
+ /* Initialization */
+ /********************/
+
+
+typedef enum {
+ espeakEVENT_LIST_TERMINATED = 0, // Retrieval mode: terminates the event list.
+ espeakEVENT_WORD = 1, // Start of word
+ espeakEVENT_SENTENCE, // Start of sentence
+ espeakEVENT_MARK, // Mark
+ espeakEVENT_PLAY, // Audio element
+ espeakEVENT_END, // End of sentence or clause
+ espeakEVENT_MSG_TERMINATED, // End of message
+ espeakEVENT_PHONEME // Phoneme, if enabled in espeak_Initialize()
+} espeak_EVENT_TYPE;
+
+
+
+typedef struct {
+ espeak_EVENT_TYPE type;
+ unsigned int unique_identifier; // message identifier (or 0 for key or character)
+ int text_position; // the number of characters from the start of the text
+ int length; // word length, in characters (for espeakEVENT_WORD)
+ int audio_position; // the time in mS within the generated speech output data
+ int sample; // sample id (internal use)
+ void* user_data; // pointer supplied by the calling program
+ union {
+ int number; // used for WORD and SENTENCE events. For PHONEME events this is the phoneme mnemonic.
+ const char *name; // used for MARK and PLAY events. UTF8 string
+ } id;
+} espeak_EVENT;
+/*
+ When a message is supplied to espeak_synth, the request is buffered and espeak_synth returns. When the message is really processed, the callback function will be repetedly called.
+
+
+ In RETRIEVAL mode, the callback function supplies to the calling program the audio data and an event list terminated by 0 (LIST_TERMINATED).
+
+ In PLAYBACK mode, the callback function is called as soon as an event happens.
+
+ For example suppose that the following message is supplied to espeak_Synth:
+ "hello, hello."
+
+
+ * Once processed in RETRIEVAL mode, it could lead to 3 calls of the callback function :
+
+ ** Block 1:
+ <audio data> +
+ List of events: SENTENCE + WORD + LIST_TERMINATED
+
+ ** Block 2:
+ <audio data> +
+ List of events: WORD + END + LIST_TERMINATED
+
+ ** Block 3:
+ no audio data
+ List of events: MSG_TERMINATED + LIST_TERMINATED
+
+
+ * Once processed in PLAYBACK mode, it could lead to 5 calls of the callback function:
+
+ ** SENTENCE
+ ** WORD (call when the sounds are actually played)
+ ** WORD
+ ** END (call when the end of sentence is actually played.)
+ ** MSG_TERMINATED
+
+
+ The MSG_TERMINATED event is the last event. It can inform the calling program to clear the user data related to the message.
+ So if the synthesis must be stopped, the callback function is called for each pending message with the MSG_TERMINATED event.
+
+ A MARK event indicates a <mark> element in the text.
+ A PLAY event indicates an <audio> element in the text, for which the calling program should play the named sound file.
+*/
+
+
+
+typedef enum {
+ POS_CHARACTER = 1,
+ POS_WORD,
+ POS_SENTENCE
+} espeak_POSITION_TYPE;
+
+
+typedef enum {
+ /* PLAYBACK mode: plays the audio data, supplies events to the calling program*/
+ AUDIO_OUTPUT_PLAYBACK,
+
+ /* RETRIEVAL mode: supplies audio data and events to the calling program */
+ AUDIO_OUTPUT_RETRIEVAL,
+
+ /* SYNCHRONOUS mode: as RETRIEVAL but doesn't return until synthesis is completed */
+ AUDIO_OUTPUT_SYNCHRONOUS,
+
+ /* Synchronous playback */
+ AUDIO_OUTPUT_SYNCH_PLAYBACK
+
+} espeak_AUDIO_OUTPUT;
+
+
+typedef enum {
+ EE_OK=0,
+ EE_INTERNAL_ERROR=-1,
+ EE_BUFFER_FULL=1,
+ EE_NOT_FOUND=2
+} espeak_ERROR;
+
+
+#ifdef __cplusplus
+extern "C"
+#endif
+int espeak_Initialize(espeak_AUDIO_OUTPUT output, int buflength, const char *path, int options);
+/* Must be called before any synthesis functions are called.
+ output: the audio data can either be played by eSpeak or passed back by the SynthCallback function.
+
+ buflength: The length in mS of sound buffers passed to the SynthCallback function.
+
+ path: The directory which contains the espeak-data directory, or NULL for the default location.
+
+ options: bit 0: 1=allow espeakEVENT_PHONEME events.
+
+
+ Returns: sample rate in Hz, or -1 (EE_INTERNAL_ERROR).
+*/
+
+typedef int (t_espeak_callback)(short*, int, espeak_EVENT*);
+
+#ifdef __cplusplus
+extern "C"
+#endif
+void espeak_SetSynthCallback(t_espeak_callback* SynthCallback);
+/* Must be called before any synthesis functions are called.
+ This specifies a function in the calling program which is called when a buffer of
+ speech sound data has been produced.
+
+
+ The callback function is of the form:
+
+int SynthCallback(short *wav, int numsamples, espeak_EVENT *events);
+
+ wav: is the speech sound data which has been produced.
+ NULL indicates that the synthesis has been completed.
+
+ numsamples: is the number of entries in wav. This number may vary, may be less than
+ the value implied by the buflength parameter given in espeak_Initialize, and may
+ sometimes be zero (which does NOT indicate end of synthesis).
+
+ events: an array of espeak_EVENT items which indicate word and sentence events, and
+ also the occurance if <mark> and <audio> elements within the text. The list of
+ events is terminated by an event of type = 0.
+
+
+ Callback returns: 0=continue synthesis, 1=abort synthesis.
+*/
+
+#ifdef __cplusplus
+extern "C"
+#endif
+void espeak_SetUriCallback(int (*UriCallback)(int, const char*, const char*));
+/* This function may be called before synthesis functions are used, in order to deal with
+ <audio> tags. It specifies a callback function which is called when an <audio> element is
+ encountered and allows the calling program to indicate whether the sound file which
+ is specified in the <audio> element is available and is to be played.
+
+ The callback function is of the form:
+
+int UriCallback(int type, const char *uri, const char *base);
+
+ type: type of callback event. Currently only 1= <audio> element
+
+ uri: the "src" attribute from the <audio> element
+
+ base: the "xml:base" attribute (if any) from the <speak> element
+
+ Return: 1=don't play the sound, but speak the text alternative.
+ 0=place a PLAY event in the event list at the point where the <audio> element
+ occurs. The calling program can then play the sound at that point.
+*/
+
+
+ /********************/
+ /* Synthesis */
+ /********************/
+
+
+#define espeakCHARS_AUTO 0
+#define espeakCHARS_UTF8 1
+#define espeakCHARS_8BIT 2
+#define espeakCHARS_WCHAR 3
+
+#ifdef UNICODE
+#define espeakCHARS_TCHAR espeakCHARS_WCHAR
+#else
+#define espeakCHARS_TCHAR espeakCHARS_8BIT
+#endif
+
+#define espeakSSML 0x10
+#define espeakPHONEMES 0x100
+#define espeakENDPAUSE 0x1000
+#define espeakKEEP_NAMEDATA 0x2000
+
+#ifdef __cplusplus
+extern "C"
+#endif
+espeak_ERROR espeak_Synth(const void *text,
+ size_t size,
+ unsigned int position,
+ espeak_POSITION_TYPE position_type,
+ unsigned int end_position,
+ unsigned int flags,
+ unsigned int* unique_identifier,
+ void* user_data);
+/* Synthesize speech for the specified text. The speech sound data is passed to the calling
+ program in buffers by means of the callback function specified by espeak_SetSynthCallback(). The command is asynchronous: it is internally buffered and returns as soon as possible. If espeak_Initialize was previously called with AUDIO_OUTPUT_PLAYBACK as argument, the sound data are played by eSpeak.
+
+ text: The text to be spoken, terminated by a zero character. It may be either 8-bit characters,
+ wide characters (wchar_t), or UTF8 encoding. Which of these is determined by the "flags"
+ parameter.
+
+ size: Equal to (or greatrer than) the size of the text data, in bytes. This is used in order
+ to allocate internal storage space for the text. This value is not used for
+ AUDIO_OUTPUT_SYNCHRONOUS mode.
+
+ position: The position in the text where speaking starts. Zero indicates speak from the
+ start of the text.
+
+ position_type: Determines whether "position" is a number of characters, words, or sentences.
+ Values:
+
+ end_position: If set, this gives a character position at which speaking will stop. A value
+ of zero indicates no end position.
+
+ flags: These may be OR'd together:
+ Type of character codes, one of:
+ espeakCHARS_UTF8 UTF8 encoding
+ espeakCHARS_8BIT The 8 bit ISO-8859 character set for the particular language.
+ espeakCHARS_AUTO 8 bit or UTF8 (this is the default)
+ espeakCHARS_WCHAR Wide characters (wchar_t)
+
+ espeakSSML Elements within < > are treated as SSML elements, or if not recognised are ignored.
+
+ espeakPHONEMES Text within [[ ]] is treated as phonemes codes (in espeak's Hirshenbaum encoding).
+
+ espeakENDPAUSE If set then a sentence pause is added at the end of the text. If not set then
+ this pause is suppressed.
+
+ unique_identifier: message identifier; helpful for identifying later
+ data supplied to the callback.
+
+ user_data: pointer which will be passed to the callback function.
+
+ Return: EE_OK: operation achieved
+ EE_BUFFER_FULL: the command can not be buffered;
+ you may try after a while to call the function again.
+ EE_INTERNAL_ERROR.
+*/
+
+#ifdef __cplusplus
+extern "C"
+#endif
+espeak_ERROR espeak_Synth_Mark(const void *text,
+ size_t size,
+ const char *index_mark,
+ unsigned int end_position,
+ unsigned int flags,
+ unsigned int* unique_identifier,
+ void* user_data);
+/* Synthesize speech for the specified text. Similar to espeak_Synth() but the start position is
+ specified by the name of a <mark> element in the text.
+
+ index_mark: The "name" attribute of a <mark> element within the text which specified the
+ point at which synthesis starts. UTF8 string.
+
+ For the other parameters, see espeak_Synth()
+
+ Return: EE_OK: operation achieved
+ EE_BUFFER_FULL: the command can not be buffered;
+ you may try after a while to call the function again.
+ EE_INTERNAL_ERROR.
+*/
+
+#ifdef __cplusplus
+extern "C"
+#endif
+espeak_ERROR espeak_Key(const char *key_name);
+/* Speak the name of a keyboard key.
+ If key_name is a single character, it speaks the name of the character.
+ Otherwise, it speaks key_name as a text string.
+
+ Return: EE_OK: operation achieved
+ EE_BUFFER_FULL: the command can not be buffered;
+ you may try after a while to call the function again.
+ EE_INTERNAL_ERROR.
+*/
+
+#ifdef __cplusplus
+extern "C"
+#endif
+espeak_ERROR espeak_Char(wchar_t character);
+/* Speak the name of the given character
+
+ Return: EE_OK: operation achieved
+ EE_BUFFER_FULL: the command can not be buffered;
+ you may try after a while to call the function again.
+ EE_INTERNAL_ERROR.
+*/
+
+
+
+
+ /***********************/
+ /* Speech Parameters */
+ /***********************/
+
+typedef enum {
+ espeakSILENCE=0, /* internal use */
+ espeakRATE=1,
+ espeakVOLUME=2,
+ espeakPITCH=3,
+ espeakRANGE=4,
+ espeakPUNCTUATION=5,
+ espeakCAPITALS=6,
+ espeakWORDGAP=7,
+ espeakOPTIONS=8, // reserved for misc. options. not yet used
+ espeakINTONATION=9,
+
+ espeakRESERVED1=10,
+ espeakRESERVED2=11,
+ espeakEMPHASIS, /* internal use */
+ espeakLINELENGTH, /* internal use */
+ espeakVOICETYPE, // internal, 1=mbrola
+ N_SPEECH_PARAM /* last enum */
+} espeak_PARAMETER;
+
+typedef enum {
+ espeakPUNCT_NONE=0,
+ espeakPUNCT_ALL=1,
+ espeakPUNCT_SOME=2
+} espeak_PUNCT_TYPE;
+
+#ifdef __cplusplus
+extern "C"
+#endif
+espeak_ERROR espeak_SetParameter(espeak_PARAMETER parameter, int value, int relative);
+/* Sets the value of the specified parameter.
+ relative=0 Sets the absolute value of the parameter.
+ relative=1 Sets a relative value of the parameter.
+
+ parameter:
+ espeakRATE: speaking speed in word per minute.
+
+ espeakVOLUME: volume in range 0-100 0=silence
+
+ espeakPITCH: base pitch, range 0-100. 50=normal
+
+ espeakRANGE: pitch range, range 0-100. 0-monotone, 50=normal
+
+ espeakPUNCTUATION: which punctuation characters to announce:
+ value in espeak_PUNCT_TYPE (none, all, some),
+ see espeak_GetParameter() to specify which characters are announced.
+
+ espeakCAPITALS: announce capital letters by:
+ 0=none,
+ 1=sound icon,
+ 2=spelling,
+ 3 or higher, by raising pitch. This values gives the amount in Hz by which the pitch
+ of a word raised to indicate it has a capital letter.
+
+ espeakWORDGAP: pause between words, units of 10mS (at the default speed)
+
+ Return: EE_OK: operation achieved
+ EE_BUFFER_FULL: the command can not be buffered;
+ you may try after a while to call the function again.
+ EE_INTERNAL_ERROR.
+*/
+
+#ifdef __cplusplus
+extern "C"
+#endif
+int espeak_GetParameter(espeak_PARAMETER parameter, int current);
+/* current=0 Returns the default value of the specified parameter.
+ current=1 Returns the current value of the specified parameter, as set by SetParameter()
+*/
+
+#ifdef __cplusplus
+extern "C"
+#endif
+espeak_ERROR espeak_SetPunctuationList(const wchar_t *punctlist);
+/* Specified a list of punctuation characters whose names are to be spoken when the
+ value of the Punctuation parameter is set to "some".
+
+ punctlist: A list of character codes, terminated by a zero character.
+
+ Return: EE_OK: operation achieved
+ EE_BUFFER_FULL: the command can not be buffered;
+ you may try after a while to call the function again.
+ EE_INTERNAL_ERROR.
+*/
+
+#ifdef __cplusplus
+extern "C"
+#endif
+void espeak_SetPhonemeTrace(int value, FILE *stream);
+/* Controls the output of phoneme symbols for the text
+ value=0 No phoneme output (default)
+ value=1 Output the translated phoneme symbols for the text
+ value=2 as (1), but also output a trace of how the translation was done (matching rules and list entries)
+
+ stream output stream for the phoneme symbols (and trace). If stream=NULL then it uses stdout.
+*/
+
+#ifdef __cplusplus
+extern "C"
+#endif
+void espeak_CompileDictionary(const char *path, FILE *log, int flags);
+/* Compile pronunciation dictionary for a language which corresponds to the currently
+ selected voice. The required voice should be selected before calling this function.
+
+ path: The directory which contains the language's '_rules' and '_list' files.
+ 'path' should end with a path separator character ('/').
+ log: Stream for error reports and statistics information. If log=NULL then stderr will be used.
+
+ flags: Bit 0: include source line information for debug purposes (This is displayed with the
+ -X command line option).
+*/
+ /***********************/
+ /* Voice Selection */
+ /***********************/
+
+
+// voice table
+typedef struct {
+ const char *name; // a given name for this voice. UTF8 string.
+ const char *languages; // list of pairs of (byte) priority + (string) language (and dialect qualifier)
+ const char *identifier; // the filename for this voice within espeak-data/voices
+ unsigned char gender; // 0=none 1=male, 2=female,
+ unsigned char age; // 0=not specified, or age in years
+ unsigned char variant; // only used when passed as a parameter to espeak_SetVoiceByProperties
+ unsigned char xx1; // for internal use
+ int score; // for internal use
+ void *spare; // for internal use
+} espeak_VOICE;
+
+/* Note: The espeak_VOICE structure is used for two purposes:
+ 1. To return the details of the available voices.
+ 2. As a parameter to espeak_SetVoiceByProperties() in order to specify selection criteria.
+
+ In (1), the "languages" field consists of a list of (UTF8) language names for which this voice
+ may be used, each language name in the list is terminated by a zero byte and is also preceded by
+ a single byte which gives a "priority" number. The list of languages is terminated by an
+ additional zero byte.
+
+ A language name consists of a language code, optionally followed by one or more qualifier (dialect)
+ names separated by hyphens (eg. "en-uk"). A voice might, for example, have languages "en-uk" and
+ "en". Even without "en" listed, voice would still be selected for the "en" language (because
+ "en-uk" is related) but at a lower priority.
+
+ The priority byte indicates how the voice is preferred for the language. A low number indicates a
+ more preferred voice, a higher number indicates a less preferred voice.
+
+ In (2), the "languages" field consists simply of a single (UTF8) language name, with no preceding
+ priority byte.
+*/
+
+#ifdef __cplusplus
+extern "C"
+#endif
+const espeak_VOICE **espeak_ListVoices(espeak_VOICE *voice_spec);
+/* Reads the voice files from espeak-data/voices and creates an array of espeak_VOICE pointers.
+ The list is terminated by a NULL pointer
+
+ If voice_spec is NULL then all voices are listed.
+ If voice spec is give, then only the voices which are compatible with the voice_spec
+ are listed, and they are listed in preference order.
+*/
+
+#ifdef __cplusplus
+extern "C"
+#endif
+espeak_ERROR espeak_SetVoiceByName(const char *name);
+/* Searches for a voice with a matching "name" field. Language is not considered.
+ "name" is a UTF8 string.
+
+ Return: EE_OK: operation achieved
+ EE_BUFFER_FULL: the command can not be buffered;
+ you may try after a while to call the function again.
+ EE_INTERNAL_ERROR.
+*/
+
+#ifdef __cplusplus
+extern "C"
+#endif
+espeak_ERROR espeak_SetVoiceByProperties(espeak_VOICE *voice_spec);
+/* An espeak_VOICE structure is used to pass criteria to select a voice. Any of the following
+ fields may be set:
+
+ name NULL, or a voice name
+
+ languages NULL, or a single language string (with optional dialect), eg. "en-uk", or "en"
+
+ gender 0=not specified, 1=male, 2=female
+
+ age 0=not specified, or an age in years
+
+ variant After a list of candidates is produced, scored and sorted, "variant" is used to index
+ that list and choose a voice.
+ variant=0 takes the top voice (i.e. best match). variant=1 takes the next voice, etc
+*/
+
+#ifdef __cplusplus
+extern "C"
+#endif
+espeak_VOICE *espeak_GetCurrentVoice(void);
+/* Returns the espeak_VOICE data for the currently selected voice.
+ This is not affected by temporary voice changes caused by SSML elements such as <voice> and <s>
+*/
+
+#ifdef __cplusplus
+extern "C"
+#endif
+espeak_ERROR espeak_Cancel(void);
+/* Stop immediately synthesis and audio output of the current text. When this
+ function returns, the audio output is fully stopped and the synthesizer is ready to
+ synthesize a new message.
+
+ Return: EE_OK: operation achieved
+ EE_INTERNAL_ERROR.
+*/
+
+
+#ifdef __cplusplus
+extern "C"
+#endif
+int espeak_IsPlaying(void);
+/* Returns 1 if audio is played, 0 otherwise.
+*/
+
+#ifdef __cplusplus
+extern "C"
+#endif
+espeak_ERROR espeak_Synchronize(void);
+/* This function returns when all data have been spoken.
+ Return: EE_OK: operation achieved
+ EE_INTERNAL_ERROR.
+*/
+
+#ifdef __cplusplus
+extern "C"
+#endif
+espeak_ERROR espeak_Terminate(void);
+/* last function to be called.
+ Return: EE_OK: operation achieved
+ EE_INTERNAL_ERROR.
+*/
+
+
+#ifdef __cplusplus
+extern "C"
+#endif
+const char *espeak_Info(void* ptr);
+/* Returns the version number string.
+ The parameter is for future use, and should be set to NULL
+*/
+#endif
diff --git a/Plugins/eSpeak/eSpeak/speech.h b/Plugins/eSpeak/eSpeak/speech.h
new file mode 100644
index 0000000..bf5fbe8
--- /dev/null
+++ b/Plugins/eSpeak/eSpeak/speech.h
@@ -0,0 +1,84 @@
+/***************************************************************************
+ * Copyright (C) 2005 to 2007 by Jonathan Duddington *
+ * email: jonsd@users.sourceforge.net *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 3 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write see: *
+ * <http://www.gnu.org/licenses/>. *
+ ***************************************************************************/
+
+
+#include <sys/types.h>
+
+// conditional compilation options
+//#define INCLUDE_KLATT
+
+#if defined(BYTE_ORDER) && BYTE_ORDER == BIG_ENDIAN
+#define ARCH_BIG
+#endif
+
+#define PLATFORM_WINDOWS
+#define __WIN32__
+#define NEED_WCSTOF
+#define NEED_GETOPT
+#define PATHSEP '\\'
+// USE_PORTAUDIO or USE_PULSEAUDIO are now defined in the makefile
+#define USE_PORTAUDIO
+//#define USE_PULSEAUDIO
+//#define USE_NANOSLEEP
+//#define __cdecl
+#define ESPEAK_API extern "C"
+
+#define USE_MBROLA_LIB
+#ifdef LIBRARY
+#define USE_ASYNC
+//#define USE_MBROLA_LIB
+#endif
+
+#ifdef _ESPEAKEDIT
+#define USE_PORTAUDIO
+#define USE_ASYNC
+#define LOG_FRAMES // write keyframe info to log-espeakedit
+#endif
+
+// will look for espeak_data directory here, and also in user's home directory
+#ifndef PATH_ESPEAK_DATA
+ #define PATH_ESPEAK_DATA "/usr/share/espeak-data"
+#endif
+
+typedef unsigned short USHORT;
+typedef unsigned char UCHAR;
+typedef double DOUBLEX;
+
+
+typedef struct {
+ const char *mnem;
+ int value;
+} MNEM_TAB;
+int LookupMnem(MNEM_TAB *table, char *string);
+
+
+#ifdef PLATFORM_WINDOWS
+#define N_PATH_HOME 220
+#else
+#define N_PATH_HOME 150
+#endif
+
+extern char path_home[N_PATH_HOME]; // this is the espeak-data directory
+
+extern void strncpy0(char *to,const char *from, int size);
+int GetFileLength(const char *filename);
+char *Alloc(int size);
+void Free(void *ptr);
+
+#include <windows.h>
diff --git a/Plugins/eSpeak/eSpeak/stdint.h b/Plugins/eSpeak/eSpeak/stdint.h
new file mode 100644
index 0000000..6b16207
--- /dev/null
+++ b/Plugins/eSpeak/eSpeak/stdint.h
@@ -0,0 +1,4 @@
+
+// dummy stdint.h file for Windows
+
+typedef unsigned int uint32_t;
diff --git a/Plugins/eSpeak/eSpeak/synth_mbrola.cpp b/Plugins/eSpeak/eSpeak/synth_mbrola.cpp
new file mode 100644
index 0000000..1055f54
--- /dev/null
+++ b/Plugins/eSpeak/eSpeak/synth_mbrola.cpp
@@ -0,0 +1,760 @@
+/***************************************************************************
+ * Copyright (C) 2005 to 2007 by Jonathan Duddington *
+ * email: jonsd@users.sourceforge.net *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 3 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write see: *
+ * <http://www.gnu.org/licenses/>. *
+ ***************************************************************************/
+
+#include "StdAfx.h"
+
+#include <stdio.h>
+#include <ctype.h>
+#include <wctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+
+#include "speak_lib.h"
+#include "speech.h"
+#include "phoneme.h"
+#include "synthesize.h"
+#include "translate.h"
+#include "voice.h"
+
+extern int Read4Bytes(FILE *f);
+extern void SetPitch2(voice_t *voice, int pitch1, int pitch2, int *pitch_base, int *pitch_range);
+
+#ifdef USE_MBROLA_LIB
+
+extern unsigned char *outbuf;
+
+#ifndef PLATFORM_WINDOWS
+
+#include "mbrolib.h"
+void * mb_handle;
+
+#else
+#include <windows.h>
+typedef void (WINAPI *PROCVV)(void);
+typedef void (WINAPI *PROCVI)(int);
+typedef void (WINAPI *PROCVF)(float);
+typedef int (WINAPI *PROCIV)();
+typedef int (WINAPI *PROCIC) (char *);
+typedef int (WINAPI *PROCISI)(short *,int);
+typedef char* (WINAPI *PROCVCI)(char *,int);
+
+PROCIC init_MBR;
+PROCIC write_MBR;
+PROCIV flush_MBR;
+PROCISI read_MBR;
+PROCVV close_MBR;
+PROCVV reset_MBR;
+PROCIV lastError_MBR;
+PROCVCI lastErrorStr_MBR;
+PROCVI setNoError_MBR;
+PROCVI setFreq_MBR;
+PROCVF setVolumeRatio_MBR;
+
+
+
+HINSTANCE hinstDllMBR = NULL;
+
+
+BOOL load_MBR()
+{
+ if(hinstDllMBR != NULL)
+ return TRUE; // already loaded
+
+ if (!(hinstDllMBR=LoadLibraryA("mbrola.dll")))
+ return FALSE;
+ init_MBR =(PROCIC) GetProcAddress(hinstDllMBR,"init_MBR");
+ write_MBR =(PROCIC) GetProcAddress(hinstDllMBR,"write_MBR");
+ flush_MBR =(PROCIV) GetProcAddress(hinstDllMBR,"flush_MBR");
+ read_MBR =(PROCISI) GetProcAddress(hinstDllMBR,"read_MBR");
+ close_MBR =(PROCVV) GetProcAddress(hinstDllMBR,"close_MBR");
+ reset_MBR =(PROCVV) GetProcAddress(hinstDllMBR,"reset_MBR");
+ lastError_MBR =(PROCIV) GetProcAddress(hinstDllMBR,"lastError_MBR");
+ lastErrorStr_MBR =(PROCVCI) GetProcAddress(hinstDllMBR,"lastErrorStr_MBR");
+ setNoError_MBR =(PROCVI) GetProcAddress(hinstDllMBR,"setNoError_MBR");
+ setVolumeRatio_MBR =(PROCVF) GetProcAddress(hinstDllMBR,"setVolumeRatio_MBR");
+ return TRUE;
+}
+
+
+void unload_MBR()
+{
+ if (hinstDllMBR)
+ {
+ FreeLibrary (hinstDllMBR);
+ hinstDllMBR=NULL;
+ }
+}
+
+#endif // windows
+#endif // USE_MBROLA_LIB
+
+
+static MBROLA_TAB *mbrola_tab = NULL;
+static int mbrola_control = 0;
+
+
+
+
+espeak_ERROR LoadMbrolaTable(const char *mbrola_voice, const char *phtrans, int srate)
+{//===================================================================================
+// Load a phoneme name translation table from espeak-data/mbrola
+
+ int size;
+ int ix;
+ int *pw;
+ FILE *f_in;
+ char path[sizeof(path_home)+15];
+
+ mbrola_name[0] = 0;
+ mbrola_delay = 0;
+
+ if(mbrola_voice == NULL)
+ {
+ samplerate = samplerate_native;
+ SetParameter(espeakVOICETYPE,0,0);
+ return(EE_OK);
+ }
+
+ sprintf(path,"%s/mbrola/%s",path_home,mbrola_voice);
+#ifdef USE_MBROLA_LIB
+#ifdef PLATFORM_WINDOWS
+ if(load_MBR() == FALSE) // load mbrola.dll
+ return(EE_INTERNAL_ERROR);
+
+ if(init_MBR(path) != 0) // initialise the required mbrola voice
+ return(EE_NOT_FOUND);
+
+ setNoError_MBR(1); // don't stop on phoneme errors
+#else
+ mb_handle = mbrolib_init(srate);
+ mbrolib_parameter m_parameters;
+
+ if(mb_handle == NULL)
+ return(EE_INTERNAL_ERROR);
+
+ MBROLIB_ERROR a_status = mbrolib_set_voice(mb_handle, mbrola_voice);
+ if(a_status != MBROLIB_OK)
+ return(EE_NOT_FOUND);
+#endif // not windows
+#endif // USE_MBROLA_LIB
+
+ // read eSpeak's mbrola phoneme translation data, eg. en1_phtrans
+ sprintf(path,"%s/mbrola_ph/%s",path_home,phtrans);
+ size = GetFileLength(path);
+ if((f_in = fopen(path,"r")) == NULL)
+ return(EE_NOT_FOUND);
+
+ if((mbrola_tab = (MBROLA_TAB *)realloc(mbrola_tab,size)) == NULL)
+ {
+ fclose(f_in);
+ return(EE_INTERNAL_ERROR);
+ }
+
+ mbrola_control = Read4Bytes(f_in);
+ pw = (int *)mbrola_tab;
+ for(ix=4; ix<size; ix+=4)
+ {
+ *pw++ = Read4Bytes(f_in);
+ }
+ fread(mbrola_tab,size,1,f_in);
+ fclose(f_in);
+
+
+#ifdef USE_MBROLA_LIB
+#ifdef PLATFORM_WINDOWS
+ setVolumeRatio_MBR((float)(mbrola_control & 0xff) /16.0f);
+#else
+ mbrolib_get_parameter(mb_handle,&m_parameters);
+ m_parameters.ignore_error = 1;
+ m_parameters.volume_ratio = (float)(mbrola_control & 0xff) /16.0;
+ mbrolib_set_parameter(mb_handle,&m_parameters);
+#endif // not windows
+#endif // USE_MBROLA_LIB
+
+ option_quiet = 1;
+ samplerate = srate;
+ if(srate == 22050)
+ SetParameter(espeakVOICETYPE,0,0);
+ else
+ SetParameter(espeakVOICETYPE,1,0);
+ strcpy(mbrola_name,mbrola_voice);
+ mbrola_delay = 3800; // improve synchronization of events
+ return(EE_OK);
+} // end of LoadMbrolaTable
+
+
+static int GetMbrName(PHONEME_LIST *plist, PHONEME_TAB *ph, PHONEME_TAB *ph_prev, PHONEME_TAB *ph_next, int *name2, int *split, int *control)
+{//==========================================================================================================================================
+// Look up a phoneme in the mbrola phoneme name translation table
+// It may give none, 1, or 2 mbrola phonemes
+ int mnem = ph->mnemonic;
+ MBROLA_TAB *pr;
+ PHONEME_TAB *other_ph;
+ int found = 0;
+
+ // control
+ // bit 0 skip the next phoneme
+ // bit 1 match this and Previous phoneme
+ // bit 2 only at the start of a word
+ // bit 3 don't match two phonemes across a word boundary
+
+ pr = mbrola_tab;
+ while(pr->name != 0)
+ {
+ if(mnem == pr->name)
+ {
+ if(pr->next_phoneme == 0)
+ found = 1;
+ else
+ if((pr->next_phoneme == ':') && (plist->synthflags & SFLAG_LENGTHEN))
+ {
+ found = 1;
+ }
+ else
+ {
+ if(pr->control & 2)
+ other_ph = ph_prev;
+ else
+ if((pr->control & 8) && ((plist+1)->newword))
+ other_ph = phoneme_tab[phPAUSE]; // don't match the next phoneme over a word boundary
+ else
+ other_ph = ph_next;
+
+ if((pr->next_phoneme == other_ph->mnemonic) ||
+ ((pr->next_phoneme == 2) && (other_ph->type == phVOWEL)) ||
+ ((pr->next_phoneme == '_') && (other_ph->type == phPAUSE)))
+ {
+ found = 1;
+ }
+ }
+
+ if((pr->control & 4) && (plist->newword == 0)) // only at start of word
+ found = 0;
+
+ if(found)
+ {
+ *name2 = pr->mbr_name2;
+ *split = pr->percent;
+ *control = pr->control;
+ return(pr->mbr_name);
+ }
+ }
+
+ pr++;
+ }
+ *name2=0;
+ *split=0;
+ *control=0;
+ return(mnem);
+}
+
+
+static char *WritePitch(int env, int pitch1, int pitch2, int split, int final)
+{//===========================================================================
+// final=1: only give the final pitch value.
+ int x;
+ int ix;
+ int pitch_base;
+ int pitch_range;
+ int p1,p2,p_end;
+ unsigned char *pitch_env;
+ int max = -1;
+ int min = 999;
+ int y_max=0;
+ int y_min=0;
+ int env100 = 80; // apply the pitch change only over this proportion of the mbrola phoneme(s)
+ int y2;
+ int y[4];
+ int env_split;
+ char buf[50];
+ static char output[50];
+
+ output[0] = 0;
+ pitch_env = envelope_data[env];
+
+
+ SetPitch2(voice, pitch1, pitch2, &pitch_base, &pitch_range);
+
+
+ env_split = (split * 128)/100;
+ if(env_split < 0)
+ env_split = 0-env_split;
+
+ // find max and min in the pitch envelope
+ for(x=0; x<128; x++)
+ {
+ if(pitch_env[x] > max)
+ {
+ max = pitch_env[x];
+ y_max = x;
+ }
+ if(pitch_env[x] < min)
+ {
+ min = pitch_env[x];
+ y_min = x;
+ }
+ }
+ // set an additional pitch point half way through the phoneme.
+ // but look for a maximum or a minimum and use that instead
+ y[2] = 64;
+ if((y_max > 0) && (y_max < 127))
+ {
+ y[2] = y_max;
+ }
+ if((y_min > 0) && (y_min < 127))
+ {
+ y[2] = y_min;
+ }
+ y[1] = y[2] / 2;
+ y[3] = y[2] + (127 - y[2])/2;
+
+ // set initial pitch
+ p1 = ((pitch_env[0]*pitch_range)>>8) + pitch_base; // Hz << 12
+ p_end = ((pitch_env[127]*pitch_range)>>8) + pitch_base;
+
+
+ if(split >= 0)
+ {
+ sprintf(buf," 0 %d",p1/4096);
+ strcat(output,buf);
+ }
+
+ // don't use intermediate pitch points for linear rise and fall
+ if(env > 1)
+ {
+ for(ix=1; ix<4; ix++)
+ {
+ p2 = ((pitch_env[y[ix]]*pitch_range)>>8) + pitch_base;
+
+ if(split > 0)
+ {
+ y2 = (y[ix] * env100)/env_split;
+ }
+ else
+ if(split < 0)
+ {
+ y2 = ((y[ix]-env_split) * env100)/env_split;
+ }
+ else
+ {
+ y2 = (y[ix] * env100)/128;
+ }
+ if((y2 > 0) && (y2 <= env100))
+ {
+ sprintf(buf," %d %d",y2,p2/4096);
+ strcat(output,buf);
+ }
+ }
+ }
+
+ p_end = p_end/4096;
+ if(split <= 0)
+ {
+ sprintf(buf," %d %d",env100,p_end);
+ strcat(output,buf);
+ }
+ if(env100 < 100)
+ {
+ sprintf(buf," %d %d",100,p_end);
+ strcat(output,buf);
+ }
+ strcat(output,"\n");
+
+ if(final)
+ sprintf(output,"\t100 %d\n",p_end);
+ return(output);
+} // end of WritePitch
+
+
+#ifdef USE_MBROLA_LIB
+
+static void MbrolaMarker(int type, int char_posn, int length, int value)
+{//=====================================================================
+
+ MarkerEvent(type,(char_posn & 0xffffff) | (length << 24),value,outbuf);
+
+}
+
+
+static void MbrolaEmbedded(int &embix, int sourceix)
+{//=================================================
+ // There were embedded commands in the text at this point
+ unsigned int word; // bit 7=last command for this word, bits 5,6 sign, bits 0-4 command
+ unsigned int value;
+ int command;
+ int sign=0;
+
+ do {
+ word = embedded_list[embix++];
+ value = word >> 8;
+ command = word & 0x1f;
+
+ if((word & 0x60) == 0x60)
+ sign = -1;
+ else
+ if((word & 0x60) == 0x40)
+ sign = 1;
+
+ if(command < N_EMBEDDED_VALUES)
+ {
+ if(sign == 0)
+ embedded_value[command] = value;
+ else
+ embedded_value[command] += (value * sign);
+ }
+
+ switch(command & 0x1f)
+ {
+ case EMBED_M: // named marker
+ MbrolaMarker(espeakEVENT_MARK, (sourceix & 0x7ff) + clause_start_char, 0, value);
+ break;
+ }
+ } while ((word & 0x80) == 0);
+}
+
+
+#ifdef PLATFORM_WINDOWS
+static int MbrolaSynth(char *p_mbrola)
+{//===================================
+// p_mbrola is a string of mbrola pho lines - Windows
+ int len;
+ int finished;
+ int result=0;
+
+ if(synth_callback == NULL)
+ return(1);
+
+ if(p_mbrola == NULL)
+ flush_MBR();
+ else
+ result = write_MBR(p_mbrola);
+
+
+ finished = 0;
+ while(!finished && ((len = read_MBR((short *)outbuf, outbuf_size/2)) > 0))
+ {
+ out_ptr = outbuf + len*2;
+
+ if(event_list)
+ {
+ event_list[event_list_ix].type = espeakEVENT_LIST_TERMINATED; // indicates end of event list
+ event_list[event_list_ix].user_data = 0;
+ }
+ count_samples += len;
+ finished = synth_callback((short *)outbuf, len, event_list);
+ event_list_ix=0;
+ }
+
+ if(finished)
+ {
+ // cancelled by user, discard any unused mbrola speech
+ flush_MBR();
+ while((len = read_MBR((short *)outbuf, outbuf_size/2)) > 0);
+ }
+ return(finished);
+} // end of SynthMbrola
+#else
+
+static int MbrolaSynth(char *p_mbrola)
+{//===================================
+// p_mbrola is a string of mbrola pho lines - Linux
+
+// This is wrong
+// It must be called from WavegenFill()
+
+ int len;
+ int finished;
+ int result=0;
+
+ if(synth_callback == NULL)
+ return(1);
+
+ if(p_mbrola == NULL)
+ mbrolib_flush(mb_handle);
+ else
+ result = mbrolib_write(mb_handle,p_mbrola,strlen(p_mbrola));
+
+
+ finished = 0;
+ while(!finished && (mbrolib_read(mb_handle, (short *)out_ptr, (out_end - out_ptr)/2, &len) == MBROLIB_OK))
+ {
+ if(len == 0)
+ break;
+
+ out_ptr += (len*2);
+
+ if(event_list)
+ {
+ event_list[event_list_ix].type = espeakEVENT_LIST_TERMINATED; // indicates end of event list
+ event_list[event_list_ix].user_data = 0;
+ }
+ count_samples += len;
+ finished = synth_callback((short *)outbuf, len, event_list);
+ event_list_ix=0;
+ }
+
+ if(finished)
+ {
+ // cancelled by user, discard any unused mbrola speech
+ mbrolib_flush(mb_handle);
+ while(mbrolib_read(mb_handle, (short *)outbuf, outbuf_size/2, &len) == MBROLIB_OK)
+ {
+ if(len == 0)
+ break;
+ }
+ }
+ return(finished);
+} // end of SynthMbrola
+#endif // not windows
+#endif // USE_MBROLA_LIB
+
+
+
+void MbrolaTranslate(PHONEME_LIST *plist, int n_phonemes, FILE *f_mbrola)
+{//======================================================================
+// Generate a mbrola pho file
+ unsigned int name;
+ int phix;
+ int len;
+ int len1;
+ PHONEME_TAB *ph;
+ PHONEME_TAB *ph_next;
+ PHONEME_TAB *ph_prev;
+ PHONEME_LIST *p;
+ PHONEME_LIST *next;
+ PHONEME_LIST *prev;
+ int pause = 0;
+ int released;
+ int name2;
+ int control;
+ int done;
+ int len_percent;
+ const char *final_pitch;
+ char buf[80];
+ char mbr_buf[120];
+
+#ifdef USE_MBROLA_LIB
+ int embedded_ix=0;
+ int word_count=0;
+
+ event_list_ix = 0;
+ out_ptr = outbuf;
+#ifdef PLATFORM_WINDOWS
+ setNoError_MBR(1); // don't stop on phoneme errors
+#endif
+#else
+// fprintf(f_mbrola,";; v=%.2f\n",(float)(mbrola_control & 0xff)/16.0); // ;; v= has no effect on mbrola
+#endif
+
+ for(phix=1; phix < n_phonemes; phix++)
+ {
+ mbr_buf[0] = 0;
+
+ p = &plist[phix];
+ next = &plist[phix+1];
+ prev = &plist[phix-1];
+ ph = p->ph;
+ ph_prev = plist[phix-1].ph;
+ ph_next = plist[phix+1].ph;
+
+#ifdef USE_MBROLA_LIB
+ if(p->synthflags & SFLAG_EMBEDDED)
+ {
+ MbrolaEmbedded(embedded_ix, p->sourceix);
+ }
+ if(p->newword & 4)
+ MbrolaMarker(espeakEVENT_SENTENCE, (p->sourceix & 0x7ff) + clause_start_char, 0, count_sentences);
+
+ if(p->newword & 1)
+ MbrolaMarker(espeakEVENT_WORD, (p->sourceix & 0x7ff) + clause_start_char, p->sourceix >> 11, clause_start_word + word_count++);
+#endif
+
+ name = GetMbrName(p,ph,ph_prev,ph_next,&name2,&len_percent,&control);
+ if(control & 1)
+ phix++;
+
+ if(name == 0)
+ continue; // ignore this phoneme
+
+ if((ph->type == phPAUSE) && (name == ph->mnemonic))
+ {
+ // a pause phoneme, which has not been changed by the translation
+ name = '_';
+ len = (p->length * speed.speed_factor1)/256;
+// if(len == 0) continue;
+ if(len == 0)
+ len = 1;
+ }
+ else
+ len = (80 * speed.speed_factor2)/256;
+
+#ifdef USE_MBROLA_LIB
+ MbrolaMarker(espeakEVENT_PHONEME, (p->sourceix & 0x7ff) + clause_start_char, 0, ph->mnemonic);
+#endif
+
+ sprintf(buf,"%s\t",WordToString(name));
+ strcat(mbr_buf,buf);
+
+ if(name2 == '_')
+ {
+ // add a pause after this phoneme
+ pause = PauseLength(len_percent,0);
+ name2 = 0;
+ }
+
+ done = 0;
+ final_pitch = "";
+
+ switch(ph->type)
+ {
+ case phVOWEL:
+ len = ph->std_length;
+ if(p->synthflags & SFLAG_LENGTHEN)
+ len += phoneme_tab[phonLENGTHEN]->std_length; // phoneme was followed by an extra : symbol
+
+ if(ph_next->type == phPAUSE)
+ len += 50; // lengthen vowels before a pause
+ len = (len * p->length)/256;
+
+ if(name2 == 0)
+ {
+ sprintf(buf,"%d\t%s", len, WritePitch(p->env,p->pitch1,p->pitch2,0,0));
+ strcat(mbr_buf,buf);
+ }
+ else
+ {
+ len1 = (len * len_percent)/100;
+ sprintf(buf,"%d\t%s", len1, WritePitch(p->env,p->pitch1,p->pitch2,len_percent,0));
+ strcat(mbr_buf,buf);
+
+ sprintf(buf,"%s\t%d\t%s", WordToString(name2), len-len1, WritePitch(p->env,p->pitch1,p->pitch2,-len_percent,0));
+ strcat(mbr_buf,buf);
+ }
+ done = 1;
+ break;
+
+ case phSTOP:
+ released = 0;
+ if(next->type==phVOWEL) released = 1;
+ if(next->type==phLIQUID && !next->newword) released = 1;
+
+ if(released)
+ len = DoSample(p->ph,next->ph,2,0,-1);
+ else
+ len = DoSample(p->ph,phoneme_tab[phonPAUSE],2,0,-1);
+ len = (len * 1000)/samplerate; // convert to mS
+ len += PauseLength(p->prepause,1);
+ break;
+
+ case phVSTOP:
+ len = (80 * speed.speed_factor2)/256;
+ break;
+
+ case phFRICATIVE:
+ len = 0;
+ if(p->synthflags & SFLAG_LENGTHEN)
+ len = DoSample(ph,ph_next,2,p->length,-1); // play it twice for [s:] etc.
+ len += DoSample(ph,ph_next,2,p->length,-1);
+
+ len = (len * 1000)/samplerate; // convert to mS
+ break;
+
+ case phNASAL:
+ if(next->type != phVOWEL)
+ {
+ len = DoSpect(p->ph,prev->ph,phoneme_tab[phonPAUSE],2,p,-1);
+ len = (len * 1000)/samplerate;
+ if(next->type == phPAUSE)
+ len += 50;
+ final_pitch = WritePitch(p->env,p->pitch1,p->pitch2,0,1);
+ }
+ break;
+
+ case phLIQUID:
+ if(next->type == phPAUSE)
+ {
+ len += 50;
+ final_pitch = WritePitch(p->env,p->pitch1,p->pitch2,0,1);
+ }
+ break;
+ }
+
+ if(!done)
+ {
+ if(name2 != 0)
+ {
+ len1 = (len * len_percent)/100;
+ sprintf(buf,"%d\n%s\t",len1,WordToString(name2));
+ strcat(mbr_buf,buf);
+ len -= len1;
+ }
+ sprintf(buf,"%d%s\n",len,final_pitch);
+ strcat(mbr_buf,buf);
+ }
+
+ if(pause)
+ {
+ sprintf(buf,"_ \t%d\n",PauseLength(pause,0));
+ strcat(mbr_buf,buf);
+ pause = 0;
+ }
+
+ if(f_mbrola)
+ {
+ fwrite(mbr_buf,1,strlen(mbr_buf),f_mbrola); // write .pho to a file
+ }
+ else
+ {
+#ifdef USE_MBROLA_LIB
+ if(MbrolaSynth(mbr_buf) != 0)
+ return;
+#endif
+ }
+ }
+
+#ifdef USE_MBROLA_LIB
+ MbrolaSynth(NULL);
+#endif
+} // end of MbrolaTranslate
+
+
+#ifdef TEST_MBROLA
+
+static PHONEME_LIST mbrola_phlist;
+static int mbrola_n_ph;
+static int mbrola_phix;
+
+
+int MbrolaFill(int fill_zeros)
+{//===========================
+}
+
+int MbrolaGenerate(PHONEME_LIST *phoneme_list, int *n_ph, int resume)
+{//==================================================================
+ if(resume == 0)
+ {
+ mbrola_phlist = phoneme_list;
+ mbrola_n_ph = n_ph;
+ mbrola_phix = 0;
+ }
+
+ resume(0); // finished phoneme list
+}
+#endif
diff --git a/Plugins/eSpeak/eSpeak/synthdata.cpp b/Plugins/eSpeak/eSpeak/synthdata.cpp
new file mode 100644
index 0000000..9f4013f
--- /dev/null
+++ b/Plugins/eSpeak/eSpeak/synthdata.cpp
@@ -0,0 +1,681 @@
+/***************************************************************************
+ * Copyright (C) 2005 to 2007 by Jonathan Duddington *
+ * email: jonsd@users.sourceforge.net *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 3 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, see: *
+ * <http://www.gnu.org/licenses/>. *
+ ***************************************************************************/
+
+
+#include "StdAfx.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <wctype.h>
+#include <string.h>
+
+
+#include "speak_lib.h"
+#include "speech.h"
+#include "phoneme.h"
+#include "synthesize.h"
+#include "voice.h"
+#include "translate.h"
+#include "wave.h"
+
+const char *version_string = "1.40 22.Dec.08";
+const int version_phdata = 0x014000;
+
+int option_device_number = -1;
+
+// copy the current phoneme table into here
+int n_phoneme_tab;
+int current_phoneme_table;
+PHONEME_TAB *phoneme_tab[N_PHONEME_TAB];
+unsigned char phoneme_tab_flags[N_PHONEME_TAB]; // bit 0: not inherited
+
+unsigned int *phoneme_index=NULL;
+char *spects_data=NULL;
+unsigned char *wavefile_data=NULL;
+static unsigned char *phoneme_tab_data = NULL;
+
+int n_phoneme_tables;
+PHONEME_TAB_LIST phoneme_tab_list[N_PHONEME_TABS];
+int phoneme_tab_number = 0;
+
+int wavefile_ix; // a wavefile to play along with the synthesis
+int wavefile_amp;
+int wavefile_ix2;
+int wavefile_amp2;
+
+int seq_len_adjust;
+int vowel_transition[4];
+int vowel_transition0;
+int vowel_transition1;
+
+int FormantTransition2(frameref_t *seq, int &n_frames, unsigned int data1, unsigned int data2, PHONEME_TAB *other_ph, int which);
+
+
+
+static char *ReadPhFile(void *ptr, const char *fname)
+{//==================================================
+ FILE *f_in;
+ char *p;
+ unsigned int length;
+ char buf[sizeof(path_home)+40];
+
+ sprintf(buf,"%s%c%s",path_home,PATHSEP,fname);
+ length = GetFileLength(buf);
+
+ if((f_in = fopen(buf,"rb")) == NULL)
+ {
+ fprintf(stderr,"Can't read data file: '%s'\n",buf);
+ return(NULL);
+ }
+
+ if(ptr != NULL)
+ Free(ptr);
+
+ if((p = Alloc(length)) == NULL)
+ {
+ fclose(f_in);
+ return(NULL);
+ }
+ if(fread(p,1,length,f_in) != length)
+ {
+ fclose(f_in);
+ return(NULL);
+ }
+
+ fclose(f_in);
+ return(p);
+} // end of ReadPhFile
+
+
+int LoadPhData()
+{//=============
+ int ix;
+ int n_phonemes;
+ int version;
+ int result = 1;
+ unsigned char *p;
+
+ if((phoneme_tab_data = (unsigned char *)ReadPhFile((void *)(phoneme_tab_data),"phontab")) == NULL)
+ return(-1);
+ if((phoneme_index = (unsigned int *)ReadPhFile((void *)(phoneme_index),"phonindex")) == NULL)
+ return(-1);
+ if((spects_data = ReadPhFile((void *)(spects_data),"phondata")) == NULL)
+ return(-1);
+ wavefile_data = (unsigned char *)spects_data;
+
+ // read the version number from the first 4 bytes of phondata
+ version = 0;
+ for(ix=0; ix<4; ix++)
+ {
+ version += (wavefile_data[ix] << (ix*8));
+ }
+
+ if(version != version_phdata)
+ {
+ result = version;
+ }
+
+ // set up phoneme tables
+ p = phoneme_tab_data;
+ n_phoneme_tables = p[0];
+ p+=4;
+
+ for(ix=0; ix<n_phoneme_tables; ix++)
+ {
+ n_phonemes = p[0];
+ phoneme_tab_list[ix].n_phonemes = p[0];
+ phoneme_tab_list[ix].includes = p[1];
+ p += 4;
+ memcpy(phoneme_tab_list[ix].name,p,N_PHONEME_TAB_NAME);
+ p += N_PHONEME_TAB_NAME;
+ phoneme_tab_list[ix].phoneme_tab_ptr = (PHONEME_TAB *)p;
+ p += (n_phonemes * sizeof(PHONEME_TAB));
+ }
+
+ if(phoneme_tab_number >= n_phoneme_tables)
+ phoneme_tab_number = 0;
+
+ return(result);
+} // end of LoadPhData
+
+
+void FreePhData(void)
+{//==================
+ Free(phoneme_tab_data);
+ Free(phoneme_index);
+ Free(spects_data);
+ phoneme_tab_data=NULL;
+ phoneme_index=NULL;
+ spects_data=NULL;
+}
+
+
+int PhonemeCode(unsigned int mnem)
+{//===============================
+ int ix;
+
+ for(ix=0; ix<n_phoneme_tab; ix++)
+ {
+ if(phoneme_tab[ix] == NULL)
+ continue;
+ if(phoneme_tab[ix]->mnemonic == mnem)
+ return(phoneme_tab[ix]->code);
+ }
+ return(0);
+}
+
+
+int LookupPhonemeString(const char *string)
+{//========================================
+ int ix;
+ unsigned char c;
+ unsigned int mnem;
+
+ // Pack up to 4 characters into a word
+ mnem = 0;
+ for(ix=0; ix<4; ix++)
+ {
+ if(string[ix]==0) break;
+ c = string[ix];
+ mnem |= (c << (ix*8));
+ }
+
+ return(PhonemeCode(mnem));
+}
+
+
+
+static unsigned int LookupSound2(int index, unsigned int other_phcode, int control)
+{//================================================================================
+// control=1 get formant transition data only
+
+ unsigned int code;
+ unsigned int value, value2;
+
+ while((value = phoneme_index[index++]) != 0)
+ {
+ if((code = (value & 0xff)) == other_phcode)
+ {
+ while(((value2 = phoneme_index[index]) != 0) && ((value2 & 0xff) < 8))
+ {
+ switch(value2 & 0xff)
+ {
+ case 0:
+ // next entry is a wavefile to be played along with the synthesis
+ if(control==0)
+ {
+ wavefile_ix = value2 >> 8;
+ }
+ break;
+ case 1:
+ if(control==0)
+ {
+ seq_len_adjust = value2 >> 8;
+ }
+ break;
+ case 2:
+ if(control==0)
+ {
+ seq_len_adjust = value2 >> 8;
+ seq_len_adjust = -seq_len_adjust;
+ }
+ break;
+ case 3:
+ if(control==0)
+ {
+ wavefile_amp = value2 >> 8;
+ }
+ break;
+ case 4:
+ // formant transition data, 2 words
+ vowel_transition[0] = value2 >> 8;
+ vowel_transition[1] = phoneme_index[index++ + 1];
+ break;
+ case 5:
+ // formant transition data, 2 words
+ vowel_transition[2] = value2 >> 8;
+ vowel_transition[3] = phoneme_index[index++ + 1];
+ break;
+ }
+ index++;
+ }
+ return(value >> 8);
+ }
+ else
+ if((code == 4) || (code == 5))
+ {
+ // formant transition data, ignore next word of data
+ index++;
+ }
+ }
+ return(3); // not found
+} // end of LookupSound2
+
+
+unsigned int LookupSound(PHONEME_TAB *this_ph, PHONEME_TAB *other_ph, int which, int *match_level, int control)
+{//============================================================================================================
+ // follows, 1 other_ph preceeds this_ph, 2 other_ph follows this_ph
+ // control: 1= get formant transition data only
+ int spect_list;
+ int spect_list2;
+ int s_list;
+ unsigned char virtual_ph;
+ int result;
+ int level=0;
+ unsigned int other_code;
+ unsigned int other_virtual;
+
+ if(control==0)
+ {
+ wavefile_ix = 0;
+ wavefile_amp = 32;
+ seq_len_adjust = 0;
+ }
+ memset(vowel_transition,0,sizeof(vowel_transition));
+
+ other_code = other_ph->code;
+ if(phoneme_tab[other_code]->type == phPAUSE)
+ other_code = phonPAUSE_SHORT; // use this version of Pause for matching
+
+ if(which==1)
+ {
+ spect_list = this_ph->after;
+ virtual_ph = this_ph->start_type;
+ spect_list2 = phoneme_tab[virtual_ph]->after;
+ other_virtual = other_ph->end_type;
+ }
+ else
+ {
+ spect_list = this_ph->before;
+ virtual_ph = this_ph->end_type;
+ spect_list2 = phoneme_tab[virtual_ph]->before;
+ other_virtual = other_ph->start_type;
+ }
+
+ result = 3;
+ // look for ph1-ph2 combination
+ if((s_list = spect_list) != 0)
+ {
+ if((result = LookupSound2(s_list,other_code,control)) != 3)
+ {
+ level = 2;
+ }
+ else
+ if(other_virtual != 0)
+ {
+ if((result = LookupSound2(spect_list,other_virtual,control)) != 3)
+ {
+ level = 1;
+ }
+ }
+ }
+ // not found, look in a virtual phoneme if one is given for this phoneme
+ if((result==3) && (virtual_ph != 0) && ((s_list = spect_list2) != 0))
+ {
+ if((result = LookupSound2(s_list,other_code,control)) != 3)
+ {
+ level = 1;
+ }
+ else
+ if(other_virtual != 0)
+ {
+ if((result = LookupSound2(spect_list2,other_virtual,control)) != 3)
+ {
+ level = 1;
+ }
+ }
+ }
+
+ if(match_level != NULL)
+ *match_level = level;
+
+ if(result==0)
+ return(0); // NULL was given in the phoneme source
+
+ // note: values = 1 indicates use the default for this phoneme, even though we found a match
+ // which set a secondary reference
+ if(result >= 4)
+ {
+ // values 1-3 can be used for special codes
+ // 1 = DFT from the phoneme source file
+ return(result);
+ }
+
+ // no match found for other_ph, return the default
+ return(LookupSound2(this_ph->spect,phonPAUSE,control));
+
+} // end of LookupSound
+
+
+
+frameref_t *LookupSpect(PHONEME_TAB *this_ph, PHONEME_TAB *prev_ph, PHONEME_TAB *next_ph,
+ int which, int *match_level, int *n_frames, PHONEME_LIST *plist)
+{//=========================================================================================================
+ int ix;
+ int nf;
+ int nf1;
+ int seq_break;
+ frameref_t *frames;
+ int length1;
+ int length_std;
+ int length_factor;
+ SPECT_SEQ *seq, *seq2;
+ SPECT_SEQK *seqk, *seqk2;
+ PHONEME_TAB *next2_ph;
+ frame_t *frame;
+ static frameref_t frames_buf[N_SEQ_FRAMES];
+
+ PHONEME_TAB *other_ph;
+ if(which == 1)
+ other_ph = prev_ph;
+ else
+ other_ph = next_ph;
+
+ if((ix = LookupSound(this_ph,other_ph,which,match_level,0)) < 4)
+ return(NULL);
+ seq = (SPECT_SEQ *)(&spects_data[ix]);
+ seqk = (SPECT_SEQK *)seq;
+ nf = seq->n_frames;
+
+
+ if(nf >= N_SEQ_FRAMES)
+ nf = N_SEQ_FRAMES - 1;
+
+ seq_break = 0;
+ length1 = 0;
+
+ for(ix=0; ix<nf; ix++)
+ {
+ if(seq->frame[0].frflags & FRFLAG_KLATT)
+ frame = &seqk->frame[ix];
+ else
+ frame = (frame_t *)&seq->frame[ix];
+ frames_buf[ix].frame = frame;
+ frames_buf[ix].frflags = frame->frflags;
+ frames_buf[ix].length = frame->length;
+ if(frame->frflags & FRFLAG_VOWEL_CENTRE)
+ seq_break = ix;
+ }
+
+ frames = &frames_buf[0];
+ if(seq_break > 0)
+ {
+ if(which==1)
+ {
+ nf = seq_break + 1;
+ }
+ else
+ {
+ frames = &frames_buf[seq_break]; // body of vowel, skip past initial frames
+ nf -= seq_break;
+ }
+ }
+
+ // do we need to modify a frame for blending with a consonant?
+ if(this_ph->type == phVOWEL)
+ {
+ if((which==2) && ((frames[nf-1].frflags & FRFLAG_BREAK) == 0))
+ {
+ // lookup formant transition for the following phoneme
+
+ if((*match_level == 0) || (next_ph->type == phNASAL))
+ {
+ LookupSound(next_ph,this_ph,1,NULL,1);
+ seq_len_adjust += FormantTransition2(frames,nf,vowel_transition[2],vowel_transition[3],next_ph,which);
+ }
+ else
+ if(next_ph->phflags == phVOWEL2)
+ {
+ // not really a consonant, rather a coloured vowel
+ if(LookupSound(next_ph,this_ph,1,NULL,1) == 0)
+ {
+ next2_ph = plist[2].ph;
+ LookupSound(next2_ph,next_ph,1,NULL,1);
+ seq_len_adjust += FormantTransition2(frames,nf,vowel_transition[2],vowel_transition[3],next2_ph,which);
+ }
+ }
+ }
+ else
+ {
+ if(*match_level == 0)
+ seq_len_adjust = FormantTransition2(frames,nf,vowel_transition0,vowel_transition1,prev_ph,which);
+ }
+ }
+
+ nf1 = nf - 1;
+ for(ix=0; ix<nf1; ix++)
+ length1 += frames[ix].length;
+
+
+ if((wavefile_ix != 0) && ((wavefile_ix & 0x800000)==0))
+ {
+ // a secondary reference has been returned, which is not a wavefile
+ // add these spectra to the main sequence
+ seq2 = (SPECT_SEQ *)(&spects_data[wavefile_ix]);
+ seqk2 = (SPECT_SEQK *)seq2;
+
+ // first frame of the addition just sets the length of the last frame of the main seq
+ nf--;
+ for(ix=0; ix<seq2->n_frames; ix++)
+ {
+ if(seq2->frame[0].frflags & FRFLAG_KLATT)
+ frame = &seqk2->frame[ix];
+ else
+ frame = (frame_t *)&seq2->frame[ix];
+
+ frames[nf].length = frame->length;
+ if(ix > 0)
+ {
+ frames[nf].frame = frame;
+ frames[nf].frflags = frame->frflags;
+ }
+ nf++;
+ }
+ wavefile_ix = 0;
+ }
+
+ if((this_ph->type == phVOWEL) && (length1 > 0))
+ {
+ if(which==2)
+ {
+ // adjust the length of the main part to match the standard length specified for the vowel
+ // less the front part of the vowel and any added suffix
+
+ length_std = this_ph->std_length + seq_len_adjust - 45;
+ if(length_std < 10)
+ length_std = 10;
+ if(plist->synthflags & SFLAG_LENGTHEN)
+ length_std += phoneme_tab[phonLENGTHEN]->std_length; // phoneme was followed by an extra : symbol
+
+// can adjust vowel length for stressed syllables here
+
+
+ length_factor = (length_std * 256)/ length1;
+
+ for(ix=0; ix<nf1; ix++)
+ {
+ frames[ix].length = (frames[ix].length * length_factor)/256;
+ }
+ }
+ else
+ {
+ // front of a vowel
+ if(*match_level == 0)
+ {
+ // allow very short vowels to have shorter front parts
+ if(this_ph->std_length < 130)
+ frames[0].length = (frames[0].length * this_ph->std_length)/130;
+ }
+
+ if(seq_len_adjust != 0)
+ {
+ length_std = 0;
+ for(ix=0; ix<nf1; ix++)
+ {
+ length_std += frames[ix].length;
+ }
+ length_factor = ((length_std + seq_len_adjust) * 256)/length_std;
+ for(ix=0; ix<nf1; ix++)
+ {
+ frames[ix].length = (frames[ix].length * length_factor)/256;
+ }
+ }
+ }
+ }
+
+ *n_frames = nf;
+ return(frames);
+} // end of LookupSpect
+
+
+unsigned char *LookupEnvelope(int ix)
+{//================================
+ if(ix==0)
+ return(NULL);
+ return((unsigned char *)&spects_data[phoneme_index[ix]]);
+}
+
+
+static void SetUpPhonemeTable(int number, int recursing)
+{//=====================================================
+ int ix;
+ int includes;
+ int ph_code;
+ PHONEME_TAB *phtab;
+
+ if(recursing==0)
+ {
+ memset(phoneme_tab_flags,0,sizeof(phoneme_tab_flags));
+ }
+
+ if((includes = phoneme_tab_list[number].includes) > 0)
+ {
+ // recursively include base phoneme tables
+ SetUpPhonemeTable(includes-1,1);
+ }
+
+ // now add the phonemes from this table
+ phtab = phoneme_tab_list[number].phoneme_tab_ptr;
+ for(ix=0; ix<phoneme_tab_list[number].n_phonemes; ix++)
+ {
+ ph_code = phtab[ix].code;
+ phoneme_tab[ph_code] = &phtab[ix];
+ if(ph_code > n_phoneme_tab)
+ n_phoneme_tab = ph_code;
+
+ if(recursing == 0)
+ phoneme_tab_flags[ph_code] |= 1; // not inherited
+ }
+} // end of SetUpPhonemeTable
+
+
+void SelectPhonemeTable(int number)
+{//================================
+ n_phoneme_tab = 0;
+ SetUpPhonemeTable(number,0); // recursively for included phoneme tables
+ n_phoneme_tab++;
+ current_phoneme_table = number;
+} // end of SelectPhonemeTable
+
+
+int LookupPhonemeTable(const char *name)
+{//=====================================
+ int ix;
+
+ for(ix=0; ix<n_phoneme_tables; ix++)
+ {
+ if(strcmp(name,phoneme_tab_list[ix].name)==0)
+ {
+ phoneme_tab_number = ix;
+ break;
+ }
+ }
+ if(ix == n_phoneme_tables)
+ return(-1);
+
+ return(ix);
+}
+
+
+int SelectPhonemeTableName(const char *name)
+{//=========================================
+// Look up a phoneme set by name, and select it if it exists
+// Returns the phoneme table number
+ int ix;
+
+ if((ix = LookupPhonemeTable(name)) == -1)
+ return(-1);
+
+ SelectPhonemeTable(ix);
+ return(ix);
+} // end of DelectPhonemeTableName
+
+
+
+
+void LoadConfig(void)
+{//==================
+// Load configuration file, if one exists
+ char buf[sizeof(path_home)+10];
+ FILE *f;
+ int ix;
+ char c1;
+ char *p;
+ char string[200];
+
+ for(ix=0; ix<N_SOUNDICON_SLOTS; ix++)
+ {
+ soundicon_tab[ix].filename = NULL;
+ soundicon_tab[ix].data = NULL;
+ }
+
+ sprintf(buf,"%s%c%s",path_home,PATHSEP,"config");
+ if((f = fopen(buf,"r"))==NULL)
+ {
+ return;
+ }
+
+ while(fgets(buf,sizeof(buf),f)!=NULL)
+ {
+ if(memcmp(buf,"tone",4)==0)
+ {
+ ReadTonePoints(&buf[5],tone_points);
+ }
+ else
+ if(memcmp(buf,"pa_device",9)==0)
+ {
+ sscanf(&buf[7],"%d",&option_device_number);
+ }
+ else
+ if(memcmp(buf,"soundicon",9)==0)
+ {
+ ix = sscanf(&buf[10],"_%c %s",&c1,string);
+ if(ix==2)
+ {
+ soundicon_tab[n_soundicon_tab].name = c1;
+ p = Alloc(strlen(string)+1);
+ strcpy(p,string);
+ soundicon_tab[n_soundicon_tab].filename = p;
+ soundicon_tab[n_soundicon_tab++].length = 0;
+ }
+ }
+ }
+} // end of LoadConfig
+
diff --git a/Plugins/eSpeak/eSpeak/synthesize.cpp b/Plugins/eSpeak/eSpeak/synthesize.cpp
new file mode 100644
index 0000000..42d4f0b
--- /dev/null
+++ b/Plugins/eSpeak/eSpeak/synthesize.cpp
@@ -0,0 +1,1660 @@
+/***************************************************************************
+ * Copyright (C) 2005 to 2007 by Jonathan Duddington *
+ * email: jonsd@users.sourceforge.net *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 3 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, see: *
+ * <http://www.gnu.org/licenses/>. *
+ ***************************************************************************/
+
+#include "StdAfx.h"
+
+#include <stdio.h>
+#include <ctype.h>
+#include <wctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+
+#include "speak_lib.h"
+#include "speech.h"
+#include "phoneme.h"
+#include "synthesize.h"
+#include "voice.h"
+#include "translate.h"
+
+
+extern FILE *f_log;
+static void SmoothSpect(void);
+
+
+// list of phonemes in a clause
+int n_phoneme_list=0;
+PHONEME_LIST phoneme_list[N_PHONEME_LIST];
+
+int mbrola_delay;
+char mbrola_name[20];
+
+SPEED_FACTORS speed;
+
+static int last_pitch_cmd;
+static int last_amp_cmd;
+static frame_t *last_frame;
+static int last_wcmdq;
+static int pitch_length;
+static int amp_length;
+static int modn_flags;
+
+static int syllable_start;
+static int syllable_end;
+static int syllable_centre;
+
+static voice_t *new_voice=NULL;
+
+int n_soundicon_tab=N_SOUNDICON_SLOTS;
+SOUND_ICON soundicon_tab[N_SOUNDICON_TAB];
+
+#define RMS_GLOTTAL1 35 // vowel before glottal stop
+#define RMS_START 28 // 28
+
+#define VOWEL_FRONT_LENGTH 50
+
+
+
+// a dummy phoneme_list entry which looks like a pause
+static PHONEME_LIST next_pause;
+
+
+const char *WordToString(unsigned int word)
+{//========================================
+// Convert a phoneme mnemonic word into a string
+ int ix;
+ static char buf[5];
+
+ for(ix=0; ix<3; ix++)
+ buf[ix] = word >> (ix*8);
+ buf[4] = 0;
+ return(buf);
+}
+
+
+
+void SynthesizeInit()
+{//==================
+ last_pitch_cmd = 0;
+ last_amp_cmd = 0;
+ last_frame = NULL;
+ syllable_centre = -1;
+
+ // initialise next_pause, a dummy phoneme_list entry
+// next_pause.ph = phoneme_tab[phonPAUSE]; // this must be done after voice selection
+ next_pause.type = phPAUSE;
+ next_pause.newword = 0;
+}
+
+
+
+static void EndAmplitude(void)
+{//===========================
+ if(amp_length > 0)
+ {
+ if(wcmdq[last_amp_cmd][1] == 0)
+ wcmdq[last_amp_cmd][1] = amp_length;
+ amp_length = 0;
+ }
+}
+
+
+
+static void EndPitch(int voice_break)
+{//==================================
+ // posssible end of pitch envelope, fill in the length
+ if((pitch_length > 0) && (last_pitch_cmd >= 0))
+ {
+ if(wcmdq[last_pitch_cmd][1] == 0)
+ wcmdq[last_pitch_cmd][1] = pitch_length;
+ pitch_length = 0;
+ }
+
+ if(voice_break)
+ {
+ last_wcmdq = -1;
+ last_frame = NULL;
+ syllable_end = wcmdq_tail;
+ SmoothSpect();
+ syllable_centre = -1;
+ memset(vowel_transition,0,sizeof(vowel_transition));
+ }
+} // end of Synthesize::EndPitch
+
+
+
+static void DoAmplitude(int amp, unsigned char *amp_env)
+{//=====================================================
+ long *q;
+
+ last_amp_cmd = wcmdq_tail;
+ amp_length = 0; // total length of vowel with this amplitude envelope
+
+ q = wcmdq[wcmdq_tail];
+ q[0] = WCMD_AMPLITUDE;
+ q[1] = 0; // fill in later from amp_length
+ q[2] = (long)amp_env;
+ q[3] = amp;
+ WcmdqInc();
+} // end of Synthesize::DoAmplitude
+
+
+
+static void DoPitch(unsigned char *env, int pitch1, int pitch2)
+{//============================================================
+ long *q;
+
+ EndPitch(0);
+
+ if(pitch1 == 1024)
+ {
+ // pitch was not set
+ pitch1 = 24;
+ pitch2 = 33;
+ env = envelope_data[PITCHfall];
+ }
+ last_pitch_cmd = wcmdq_tail;
+ pitch_length = 0; // total length of spect with this pitch envelope
+
+ if(pitch2 < 0)
+ pitch2 = 0;
+
+ q = wcmdq[wcmdq_tail];
+ q[0] = WCMD_PITCH;
+ q[1] = 0; // length, fill in later from pitch_length
+ q[2] = (long)env;
+ q[3] = (pitch1 << 16) + pitch2;
+ WcmdqInc();
+} // end of Synthesize::DoPitch
+
+
+
+int PauseLength(int pause, int control)
+{//====================================
+ int len;
+
+ if(control == 0)
+ len = (pause * speed.speed_factor1)/256;
+ else
+ len = (pause * speed.speed_factor2)/256;
+
+ if(len < 5) len = 5; // mS, limit the amount to which pauses can be shortened
+ return(len);
+}
+
+
+static void DoPause(int length, int control)
+{//=========================================
+// control = 1, less shortening at fast speeds
+ int len;
+
+ len = PauseLength(length, control);
+
+ len = (len * samplerate) / 1000; // convert from mS to number of samples
+
+ EndPitch(1);
+ wcmdq[wcmdq_tail][0] = WCMD_PAUSE;
+ wcmdq[wcmdq_tail][1] = len;
+ WcmdqInc();
+ last_frame = NULL;
+} // end of Synthesize::DoPause
+
+
+extern int seq_len_adjust; // temporary fix to advance the start point for playing the wav sample
+
+
+static int DoSample2(int index, int which, int length_mod, int amp)
+{//================================================================
+ int length;
+ int length1;
+ int format;
+ int min_length;
+ int start=0;
+ long *q;
+ unsigned char *p;
+
+ index = index & 0x7fffff;
+ p = &wavefile_data[index];
+ format = p[2];
+ length1 = (p[1] * 256);
+ length1 += p[0]; // length in bytes
+
+ if(seq_len_adjust > 0)
+ {
+ start = (seq_len_adjust * samplerate)/1000;
+ if(format == 0)
+ start *= 2;
+ length1 -= start;
+ index += start;
+ }
+
+
+ if(length_mod > 0)
+ length = (length1 * length_mod) / 256;
+ else
+ length = length1;
+
+
+ length = (length * speed.speed_factor2)/256;
+ min_length = speed.min_sample_len;
+ if(format==0)
+ min_length *= 2;
+
+ if(length < min_length)
+ length = min_length;
+
+ if(length > length1)
+ length = length1; // don't exceed wavefile length
+
+ if(format==0)
+ length /= 2; // 2 byte samples
+
+
+ index += 4;
+
+ if(amp >= 0)
+ {
+ last_wcmdq = wcmdq_tail;
+ q = wcmdq[wcmdq_tail];
+ if(which & 0x100)
+ q[0] = WCMD_WAVE2; // mix this with synthesised wave
+ else
+ q[0] = WCMD_WAVE;
+ q[1] = length; // length in samples
+ q[2] = long(&wavefile_data[index]);
+ q[3] = format + (amp << 8);
+ WcmdqInc();
+ }
+ return(length);
+} // end of Synthesize::DoSample2
+
+
+int DoSample(PHONEME_TAB *ph1, PHONEME_TAB *ph2, int which, int length_mod, int amp)
+{//====================== ==========================================================
+ int index;
+ int match_level;
+ int amp2;
+ int result;
+
+ EndPitch(1);
+ index = LookupSound(ph1,ph2,which & 0xff,&match_level,0);
+ if((index & 0x800000) == 0)
+ return(0); // not wavefile data
+
+ amp2 = wavefile_amp;
+ if(amp != 0)
+ amp2 = (amp * wavefile_amp)/20;
+
+ if(amp == -1)
+ amp2 = amp;
+
+ result = DoSample2(index,which,length_mod,amp2);
+ last_frame = NULL;
+ return(result);
+} // end of Synthesize::DoSample
+
+
+
+
+static frame_t *AllocFrame()
+{//=========================
+ // Allocate a temporary spectrum frame for the wavegen queue. Use a pool which is big
+ // enough to use a round-robin without checks.
+ // Only needed for modifying spectra for blending to consonants
+
+#define N_FRAME_POOL N_WCMDQ
+ static int ix=0;
+ static frame_t frame_pool[N_FRAME_POOL];
+
+ ix++;
+ if(ix >= N_FRAME_POOL)
+ ix = 0;
+ return(&frame_pool[ix]);
+}
+
+
+static void set_frame_rms(frame_t *fr, int new_rms)
+{//=================================================
+// Each frame includes its RMS amplitude value, so to set a new
+// RMS just adjust the formant amplitudes by the appropriate ratio
+
+ int x;
+ int h;
+ int ix;
+
+ static const short sqrt_tab[200] = {
+ 0, 64, 90,110,128,143,156,169,181,192,202,212,221,230,239,247,
+ 256,263,271,278,286,293,300,306,313,320,326,332,338,344,350,356,
+ 362,367,373,378,384,389,394,399,404,409,414,419,424,429,434,438,
+ 443,448,452,457,461,465,470,474,478,483,487,491,495,499,503,507,
+ 512,515,519,523,527,531,535,539,543,546,550,554,557,561,565,568,
+ 572,576,579,583,586,590,593,596,600,603,607,610,613,617,620,623,
+ 627,630,633,636,640,643,646,649,652,655,658,662,665,668,671,674,
+ 677,680,683,686,689,692,695,698,701,704,706,709,712,715,718,721,
+ 724,726,729,732,735,738,740,743,746,749,751,754,757,759,762,765,
+ 768,770,773,775,778,781,783,786,789,791,794,796,799,801,804,807,
+ 809,812,814,817,819,822,824,827,829,832,834,836,839,841,844,846,
+ 849,851,853,856,858,861,863,865,868,870,872,875,877,879,882,884,
+ 886,889,891,893,896,898,900,902};
+
+ if(fr->frflags & FRFLAG_KLATT)
+ {
+ if(new_rms == -1)
+ {
+ fr->klattp[KLATT_AV] = 50;
+ }
+ return;
+ }
+
+ if(fr->rms == 0) return; // check for divide by zero
+ x = (new_rms * 64)/fr->rms;
+ if(x >= 200) x = 199;
+
+ x = sqrt_tab[x]; // sqrt(new_rms/fr->rms)*0x200;
+
+ for(ix=0; ix<N_PEAKS; ix++)
+ {
+ h = fr->fheight[ix] * x;
+ fr->fheight[ix] = h/0x200;
+ }
+} /* end of set_frame_rms */
+
+
+
+static void formants_reduce_hf(frame_t *fr, int level)
+{//====================================================
+// change height of peaks 2 to 8, percentage
+ int ix;
+ int x;
+
+ if(fr->frflags & FRFLAG_KLATT)
+ return;
+
+ for(ix=2; ix<N_PEAKS; ix++)
+ {
+ x = fr->fheight[ix] * level;
+ fr->fheight[ix] = x/100;
+ }
+}
+
+
+static frame_t *CopyFrame(frame_t *frame1, int copy)
+{//=================================================
+// create a copy of the specified frame in temporary buffer
+ frame_t *frame2;
+
+ if((copy==0) && (frame1->frflags & FRFLAG_COPIED))
+ {
+ // this frame has already been copied in temporary rw memory
+ return(frame1);
+ }
+
+ frame2 = AllocFrame();
+ if(frame2 != NULL)
+ {
+ memcpy(frame2,frame1,sizeof(frame_t));
+ frame2->length = 0;
+ frame2->frflags |= FRFLAG_COPIED;
+ }
+ return(frame2);
+}
+
+
+static frame_t *DuplicateLastFrame(frameref_t *seq, int n_frames, int length)
+{//==========================================================================
+ frame_t *fr;
+
+ seq[n_frames-1].length = length;
+ fr = CopyFrame(seq[n_frames-1].frame,1);
+ seq[n_frames].frame = fr;
+ seq[n_frames].length = 0;
+ return fr;
+}
+
+
+static void AdjustFormants(frame_t *fr, int target, int min, int max, int f1_adj, int f3_adj, int hf_reduce, int flags)
+{//====================================================================================================================
+ int x;
+
+//hf_reduce = 70; // ?? using fixed amount rather than the parameter??
+
+ target = (target * voice->formant_factor)/256;
+
+ x = (target - fr->ffreq[2]) / 2;
+ if(x > max) x = max;
+ if(x < min) x = min;
+ fr->ffreq[2] += x;
+ fr->ffreq[3] += f3_adj;
+
+ if(flags & 0x20)
+ {
+ f3_adj = -f3_adj; //. reverse direction for f4,f5 change
+ }
+ fr->ffreq[4] += f3_adj;
+ fr->ffreq[5] += f3_adj;
+
+ if(f1_adj==1)
+ {
+ x = (235 - fr->ffreq[1]);
+ if(x < -100) x = -100;
+ if(x > -60) x = -60;
+ fr->ffreq[1] += x;
+ }
+ if(f1_adj==2)
+ {
+ x = (235 - fr->ffreq[1]);
+ if(x < -300) x = -300;
+ if(x > -150) x = -150;
+ fr->ffreq[1] += x;
+ fr->ffreq[0] += x;
+ }
+ if(f1_adj==3)
+ {
+ x = (100 - fr->ffreq[1]);
+ if(x < -400) x = -400;
+ if(x > -300) x = -400;
+ fr->ffreq[1] += x;
+ fr->ffreq[0] += x;
+ }
+ formants_reduce_hf(fr,hf_reduce);
+}
+
+
+static int VowelCloseness(frame_t *fr)
+{//===================================
+// return a value 0-3 depending on the vowel's f1
+ int f1;
+
+ if((f1 = fr->ffreq[1]) < 300)
+ return(3);
+ if(f1 < 400)
+ return(2);
+ if(f1 < 500)
+ return(1);
+ return(0);
+}
+
+
+int FormantTransition2(frameref_t *seq, int &n_frames, unsigned int data1, unsigned int data2, PHONEME_TAB *other_ph, int which)
+{//==============================================================================================================================
+ int ix;
+ int formant;
+ int next_rms;
+
+ int len;
+ int rms;
+ int f1;
+ int f2;
+ int f2_min;
+ int f2_max;
+ int f3_adj;
+ int f3_amp;
+ int flags;
+ int vcolour;
+
+#define N_VCOLOUR 2
+// percentage change for each formant in 256ths
+static short vcolouring[N_VCOLOUR][5] = {
+ {243,272,256,256,256}, // palatal consonant follows
+ {256,256,240,240,240}, // retroflex
+};
+
+ frame_t *fr = NULL;
+
+ if(n_frames < 2)
+ return(0);
+
+ len = (data1 & 0x3f) * 2;
+ rms = (data1 >> 6) & 0x3f;
+ flags = (data1 >> 12);
+
+ f2 = (data2 & 0x3f) * 50;
+ f2_min = (((data2 >> 6) & 0x1f) - 15) * 50;
+ f2_max = (((data2 >> 11) & 0x1f) - 15) * 50;
+ f3_adj = (((data2 >> 16) & 0x1f) - 15) * 50;
+ f3_amp = ((data2 >> 21) & 0x1f) * 8;
+ f1 = ((data2 >> 26) & 0x7);
+ vcolour = (data2 >> 29);
+
+// fprintf(stderr,"FMT%d %3s %3d-%3d f1=%d f2=%4d %4d %4d f3=%4d %3d\n",
+// which,WordToString(other_ph->mnemonic),len,rms,f1,f2,f2_min,f2_max,f3_adj,f3_amp);
+
+ if(other_ph->mnemonic == '?')
+ flags |= 8;
+
+ if(which == 1)
+ {
+ /* entry to vowel */
+ fr = CopyFrame(seq[0].frame,0);
+ seq[0].frame = fr;
+ seq[0].length = VOWEL_FRONT_LENGTH;
+ if(len > 0)
+ seq[0].length = len;
+ seq[0].frflags |= FRFLAG_LEN_MOD; // reduce length modification
+ fr->frflags |= FRFLAG_LEN_MOD;
+
+ next_rms = seq[1].frame->rms;
+
+if(fr->frflags & FRFLAG_KLATT)
+{
+ fr->klattp[KLATT_AV] = 53; // reduce the amplituide of the start of a vowel
+}
+ if(f2 != 0)
+ {
+ if(rms & 0x20)
+ {
+ set_frame_rms(fr,(next_rms * (rms & 0x1f))/30);
+ }
+ AdjustFormants(fr, f2, f2_min, f2_max, f1, f3_adj, f3_amp, flags);
+
+ if((rms & 0x20) == 0)
+ {
+ set_frame_rms(fr,rms*2);
+ }
+ }
+ else
+ {
+ if(flags & 8)
+ set_frame_rms(fr,(next_rms*24)/32);
+ else
+ set_frame_rms(fr,RMS_START);
+ }
+
+ if(flags & 8)
+ {
+// set_frame_rms(fr,next_rms - 5);
+ modn_flags = 0x800 + (VowelCloseness(fr) << 8);
+ }
+ }
+ else
+ {
+ // exit from vowel
+ rms = rms*2;
+ if((f2 != 0) || (flags != 0))
+ {
+
+ if(flags & 8)
+ {
+ fr = CopyFrame(seq[n_frames-1].frame,0);
+ seq[n_frames-1].frame = fr;
+ rms = RMS_GLOTTAL1;
+
+ // degree of glottal-stop effect depends on closeness of vowel (indicated by f1 freq)
+ modn_flags = 0x400 + (VowelCloseness(fr) << 8);
+ }
+ else
+ {
+ fr = DuplicateLastFrame(seq,n_frames++,len);
+ if(len > 36)
+ seq_len_adjust += (len - 36);
+
+ if(f2 != 0)
+ {
+ AdjustFormants(fr, f2, f2_min, f2_max, f1, f3_adj, f3_amp, flags);
+ }
+ }
+
+ set_frame_rms(fr,rms);
+
+ if((vcolour > 0) && (vcolour <= N_VCOLOUR))
+ {
+ for(ix=0; ix<n_frames; ix++)
+ {
+ fr = CopyFrame(seq[ix].frame,0);
+ seq[ix].frame = fr;
+
+ for(formant=1; formant<=5; formant++)
+ {
+ int x;
+ x = fr->ffreq[formant] * vcolouring[vcolour-1][formant-1];
+ fr->ffreq[formant] = x / 256;
+ }
+ }
+ }
+ }
+ }
+
+ if(fr != NULL)
+ {
+ if(flags & 4)
+ fr->frflags |= FRFLAG_FORMANT_RATE;
+ if(flags & 2)
+ fr->frflags |= FRFLAG_BREAK; // don't merge with next frame
+ }
+
+ if(flags & 0x40)
+ DoPause(12,0); // add a short pause after the consonant
+
+ if(flags & 16)
+ return(len);
+ return(0);
+} // end of FormantTransition2
+
+
+
+static void SmoothSpect(void)
+{//==========================
+ // Limit the rate of frequence change of formants, to reduce chirping
+
+ long *q;
+ frame_t *frame;
+ frame_t *frame2;
+ frame_t *frame1;
+ frame_t *frame_centre;
+ int ix;
+ int len;
+ int pk;
+ int modified;
+ int allowed;
+ int diff;
+
+ if(syllable_start == syllable_end)
+ return;
+
+ if((syllable_centre < 0) || (syllable_centre == syllable_start))
+ {
+ syllable_start = syllable_end;
+ return;
+ }
+
+ q = wcmdq[syllable_centre];
+ frame_centre = (frame_t *)q[2];
+
+//if(frame_centre->frflags & FRFLAG_KLATT)
+// return; // TESTING
+
+ // backwards
+ ix = syllable_centre -1;
+ frame = frame2 = frame_centre;
+ for(;;)
+ {
+ if(ix < 0) ix = N_WCMDQ-1;
+ q = wcmdq[ix];
+
+ if(q[0] == WCMD_PAUSE || q[0] == WCMD_WAVE)
+ break;
+
+ if(q[0] <= WCMD_SPECT2)
+ {
+ len = q[1] & 0xffff;
+
+ frame1 = (frame_t *)q[3];
+ if(frame1 == frame)
+ {
+ q[3] = (long)frame2;
+ frame1 = frame2;
+ }
+ else
+ break; // doesn't follow on from previous frame
+
+ frame = frame2 = (frame_t *)q[2];
+ modified = 0;
+
+ if(frame->frflags & FRFLAG_BREAK)
+ break;
+
+ if(frame->frflags & FRFLAG_FORMANT_RATE)
+ len = (len * 12)/10; // allow slightly greater rate of change for this frame (was 12/10)
+
+ for(pk=0; pk<6; pk++)
+ {
+ int f1, f2;
+
+ if((frame->frflags & FRFLAG_BREAK_LF) && (pk < 3))
+ continue;
+
+ f1 = frame1->ffreq[pk];
+ f2 = frame->ffreq[pk];
+
+ // backwards
+ if((diff = f2 - f1) > 0)
+ {
+ allowed = f1*2 + f2;
+ }
+ else
+ {
+ allowed = f1 + f2*2;
+ }
+
+ // the allowed change is specified as percentage (%*10) of the frequency
+ // take "frequency" as 1/3 from the lower freq
+ allowed = (allowed * formant_rate[pk])/3000;
+ allowed = (allowed * len)/256;
+
+ if(diff > allowed)
+ {
+ if(modified == 0)
+ {
+ frame2 = CopyFrame(frame,0);
+ modified = 1;
+ }
+ frame2->ffreq[pk] = frame1->ffreq[pk] + allowed;
+ q[2] = (long)frame2;
+ }
+ else
+ if(diff < -allowed)
+ {
+ if(modified == 0)
+ {
+ frame2 = CopyFrame(frame,0);
+ modified = 1;
+ }
+ frame2->ffreq[pk] = frame1->ffreq[pk] - allowed;
+ q[2] = (long)frame2;
+ }
+ }
+ }
+
+ if(ix == syllable_start)
+ break;
+ ix--;
+ }
+
+ // forwards
+ ix = syllable_centre;
+
+ frame = NULL;
+ for(;;)
+ {
+ q = wcmdq[ix];
+
+ if(q[0] == WCMD_PAUSE || q[0] == WCMD_WAVE)
+ break;
+
+ if(q[0] <= WCMD_SPECT2)
+ {
+
+ len = q[1] & 0xffff;
+
+ frame1 = (frame_t *)q[2];
+ if(frame != NULL)
+ {
+ if(frame1 == frame)
+ {
+ q[2] = (long)frame2;
+ frame1 = frame2;
+ }
+ else
+ break; // doesn't follow on from previous frame
+ }
+
+ frame = frame2 = (frame_t *)q[3];
+ modified = 0;
+
+ if(frame1->frflags & FRFLAG_BREAK)
+ break;
+
+ if(frame1->frflags & FRFLAG_FORMANT_RATE)
+ len = (len *6)/5; // allow slightly greater rate of change for this frame
+
+ for(pk=0; pk<6; pk++)
+ {
+ int f1, f2;
+ f1 = frame1->ffreq[pk];
+ f2 = frame->ffreq[pk];
+
+ // forwards
+ if((diff = f2 - f1) > 0)
+ {
+ allowed = f1*2 + f2;
+ }
+ else
+ {
+ allowed = f1 + f2*2;
+ }
+ allowed = (allowed * formant_rate[pk])/3000;
+ allowed = (allowed * len)/256;
+
+ if(diff > allowed)
+ {
+ if(modified == 0)
+ {
+ frame2 = CopyFrame(frame,0);
+ modified = 1;
+ }
+ frame2->ffreq[pk] = frame1->ffreq[pk] + allowed;
+ q[3] = (long)frame2;
+ }
+ else
+ if(diff < -allowed)
+ {
+ if(modified == 0)
+ {
+ frame2 = CopyFrame(frame,0);
+ modified = 1;
+ }
+ frame2->ffreq[pk] = frame1->ffreq[pk] - allowed;
+ q[3] = (long)frame2;
+ }
+ }
+ }
+
+ ix++;
+ if(ix >= N_WCMDQ) ix = 0;
+ if(ix == syllable_end)
+ break;
+ }
+
+ syllable_start = syllable_end;
+} // end of SmoothSpect
+
+
+static void StartSyllable(void)
+{//============================
+ // start of syllable, if not already started
+ if(syllable_end == syllable_start)
+ syllable_end = wcmdq_tail;
+}
+
+
+int DoSpect(PHONEME_TAB *this_ph, PHONEME_TAB *prev_ph, PHONEME_TAB *next_ph,
+ int which, PHONEME_LIST *plist, int modulation)
+{//===================================================================================
+ // which 1 start of phoneme, 2 body and end
+ // length_mod: 256 = 100%
+ // modulation: -1 = don't write to wcmdq
+
+ int n_frames;
+ frameref_t *frames;
+ int frameix;
+ frame_t *frame1;
+ frame_t *frame2;
+ frame_t *fr;
+ int ix;
+ long *q;
+ int len;
+ int match_level;
+ int frame_length;
+ int frame1_length;
+ int frame2_length;
+ int length_factor;
+ int length_mod;
+ int total_len = 0;
+ static int wave_flag = 0;
+ int wcmd_spect = WCMD_SPECT;
+
+ length_mod = plist->length;
+ if(length_mod==0) length_mod=256;
+
+if(which==1)
+{
+ // limit the shortening of sonorants before shortened (eg. unstressed vowels)
+ if((this_ph->type==phLIQUID) || (prev_ph->type==phLIQUID) || (prev_ph->type==phNASAL))
+ {
+ if(length_mod < (len = translator->langopts.param[LOPT_SONORANT_MIN]))
+ {
+ length_mod = len;
+ }
+ }
+}
+
+ modn_flags = 0;
+ frames = LookupSpect(this_ph,prev_ph,next_ph,which,&match_level,&n_frames, plist);
+ if(frames == NULL)
+ return(0); // not found
+
+ frame1 = frames[0].frame;
+ frame1_length = frames[0].length;
+ if(frame1->frflags & FRFLAG_KLATT)
+ wcmd_spect = WCMD_KLATT;
+
+ if(wavefile_ix == 0)
+ {
+ if(wave_flag)
+ {
+ // cancel any wavefile that was playing previously
+ wcmd_spect = WCMD_SPECT2;
+ if(frame1->frflags & FRFLAG_KLATT)
+ wcmd_spect = WCMD_KLATT2;
+ wave_flag = 0;
+ }
+ else
+ {
+ wcmd_spect = WCMD_SPECT;
+ if(frame1->frflags & FRFLAG_KLATT)
+ wcmd_spect = WCMD_KLATT;
+ }
+ }
+
+ if(last_frame != NULL)
+ {
+ if(((last_frame->length < 2) || (last_frame->frflags & FRFLAG_VOWEL_CENTRE))
+ && !(last_frame->frflags & FRFLAG_BREAK))
+ {
+ // last frame of previous sequence was zero-length, replace with first of this sequence
+ wcmdq[last_wcmdq][3] = (long)frame1;
+
+ if(last_frame->frflags & FRFLAG_BREAK_LF)
+ {
+ // but flag indicates keep HF peaks in last segment
+ fr = CopyFrame(frame1,1);
+ for(ix=3; ix<N_PEAKS; ix++)
+ {
+ fr->ffreq[ix] = last_frame->ffreq[ix];
+ fr->fheight[ix] = last_frame->fheight[ix];
+ }
+ wcmdq[last_wcmdq][3] = (long)fr;
+ }
+ }
+ }
+
+ if((this_ph->type == phVOWEL) && (which == 2))
+ {
+ SmoothSpect(); // process previous syllable
+
+ // remember the point in the output queue of the centre of the vowel
+ syllable_centre = wcmdq_tail;
+ }
+
+ frame_length = frame1_length;
+ for(frameix=1; frameix<n_frames; frameix++)
+ {
+ frame2 = frames[frameix].frame;
+ frame2_length = frames[frameix].length;
+
+ if((wavefile_ix != 0) && ((frame1->frflags & FRFLAG_DEFER_WAV)==0))
+ {
+ // there is a wave file to play along with this synthesis
+ seq_len_adjust = 0;
+ DoSample2(wavefile_ix,which+0x100,0,wavefile_amp);
+ wave_flag = 1;
+ wavefile_ix = 0;
+ }
+
+ length_factor = length_mod;
+ if(frame1->frflags & FRFLAG_LEN_MOD) // reduce effect of length mod
+ {
+ length_factor = (length_mod*(256-speed.speed_factor3) + 256*speed.speed_factor3)/256;
+ }
+ len = (frame_length * samplerate)/1000;
+ len = (len * length_factor)/256;
+
+ if(modulation >= 0)
+ {
+ if(frame1->frflags & FRFLAG_MODULATE)
+ {
+ modulation = 6;
+ }
+ if((frameix == n_frames-1) && (modn_flags & 0xf00))
+ modulation |= modn_flags; // before or after a glottal stop
+ }
+
+ pitch_length += len;
+ amp_length += len;
+
+ if(frame_length < 2)
+ {
+ last_frame = NULL;
+ frame_length = frame2_length;
+ frame1 = frame2;
+ }
+ else
+ {
+ last_wcmdq = wcmdq_tail;
+
+ if(modulation >= 0)
+ {
+ q = wcmdq[wcmdq_tail];
+ q[0] = wcmd_spect;
+ q[1] = len + (modulation << 16);
+ q[2] = long(frame1);
+ q[3] = long(frame2);
+
+ WcmdqInc();
+ }
+ last_frame = frame1 = frame2;
+ frame_length = frame2_length;
+ total_len += len;
+ }
+ }
+ return(total_len);
+} // end of Synthesize::DoSpect
+
+
+static void DoMarker(int type, int char_posn, int length, int value)
+{//=================================================================
+// This could be used to return an index to the word currently being spoken
+// Type 1=word, 2=sentence, 3=named marker, 4=play audio, 5=end
+ wcmdq[wcmdq_tail][0] = WCMD_MARKER;
+ wcmdq[wcmdq_tail][1] = type;
+ wcmdq[wcmdq_tail][2] = (char_posn & 0xffffff) | (length << 24);
+ wcmdq[wcmdq_tail][3] = value;
+ WcmdqInc();
+
+} // end of Synthesize::DoMarker
+
+
+void DoVoiceChange(voice_t *v)
+{//===========================
+// allocate memory for a copy of the voice data, and free it in wavegenfill()
+ voice_t *v2;
+
+ v2 = (voice_t *)malloc(sizeof(voice_t));
+ memcpy(v2,v,sizeof(voice_t));
+ wcmdq[wcmdq_tail][0] = WCMD_VOICE;
+ wcmdq[wcmdq_tail][1] = (long)(v2);
+ WcmdqInc();
+}
+
+
+static void DoEmbedded(int &embix, int sourceix)
+{//=============================================
+ // There were embedded commands in the text at this point
+ unsigned int word; // bit 7=last command for this word, bits 5,6 sign, bits 0-4 command
+ unsigned int value;
+ int command;
+
+ do {
+ word = embedded_list[embix++];
+ value = word >> 8;
+ command = word & 0x7f;
+
+ switch(command & 0x1f)
+ {
+ case EMBED_S: // speed
+ SetEmbedded((command & 0x60) + EMBED_S2,value); // adjusts embedded_value[EMBED_S2]
+ SetSpeed(2);
+ break;
+
+ case EMBED_I: // play dynamically loaded wav data (sound icon)
+ if((int)value < n_soundicon_tab)
+ {
+ if(soundicon_tab[value].length != 0)
+ {
+ DoPause(10,0); // ensure a break in the speech
+ wcmdq[wcmdq_tail][0] = WCMD_WAVE;
+ wcmdq[wcmdq_tail][1] = soundicon_tab[value].length;
+ wcmdq[wcmdq_tail][2] = (long)soundicon_tab[value].data + 44; // skip WAV header
+ wcmdq[wcmdq_tail][3] = 0x1500; // 16 bit data, amp=21
+ WcmdqInc();
+ }
+ }
+ break;
+
+ case EMBED_M: // named marker
+ DoMarker(espeakEVENT_MARK, (sourceix & 0x7ff) + clause_start_char, 0, value);
+ break;
+
+ case EMBED_U: // play sound
+ DoMarker(espeakEVENT_PLAY, count_characters+1, 0, value); // always occurs at end of clause
+ break;
+
+ default:
+ DoPause(10,0); // ensure a break in the speech
+ wcmdq[wcmdq_tail][0] = WCMD_EMBEDDED;
+ wcmdq[wcmdq_tail][1] = command;
+ wcmdq[wcmdq_tail][2] = value;
+ WcmdqInc();
+ break;
+ }
+ } while ((word & 0x80) == 0);
+}
+
+
+
+int Generate(PHONEME_LIST *phoneme_list, int *n_ph, int resume)
+{//============================================================
+ static int ix;
+ static int embedded_ix;
+ static int word_count;
+ PHONEME_LIST *prev;
+ PHONEME_LIST *next;
+ PHONEME_LIST *next2;
+ PHONEME_LIST *p;
+ int released;
+ int stress;
+ int modulation;
+ int pre_voiced;
+ int free_min;
+ unsigned char *pitch_env=NULL;
+ unsigned char *amp_env;
+ PHONEME_TAB *ph;
+ PHONEME_TAB *prev_ph;
+ static int sourceix=0;
+
+#ifdef TEST_MBROLA
+ if(mbrola_name[0] != 0)
+ return(MbrolaGenerate(phoneme_list,n_ph,resume));
+#endif
+
+ if(option_quiet)
+ return(0);
+
+ if(resume == 0)
+ {
+ ix = 1;
+ embedded_ix=0;
+ word_count = 0;
+ pitch_length = 0;
+ amp_length = 0;
+ last_frame = NULL;
+ last_wcmdq = -1;
+ syllable_start = wcmdq_tail;
+ syllable_end = wcmdq_tail;
+ syllable_centre = -1;
+ last_pitch_cmd = -1;
+ memset(vowel_transition,0,sizeof(vowel_transition));
+ }
+
+ while(ix < (*n_ph))
+ {
+ p = &phoneme_list[ix];
+
+ if(p->type == phPAUSE)
+ free_min = 5;
+ else
+ if(p->type != phVOWEL)
+ free_min = 10; // we need less Q space for non-vowels, and we need to generate phonemes after a vowel so that the pitch_length is filled in
+ else
+ free_min = MIN_WCMDQ; // 22
+
+ if(WcmdqFree() <= free_min)
+ return(1); // wait
+
+ prev = &phoneme_list[ix-1];
+ next = &phoneme_list[ix+1];
+ next2 = &phoneme_list[ix+2];
+
+ if(p->synthflags & SFLAG_EMBEDDED)
+ {
+ DoEmbedded(embedded_ix, p->sourceix);
+ }
+
+ if(p->newword)
+ {
+ if(translator->langopts.param[LOPT_WORD_MERGE] == 0)
+ last_frame = NULL;
+
+ sourceix = (p->sourceix & 0x7ff) + clause_start_char;
+
+ if(p->newword & 4)
+ DoMarker(espeakEVENT_SENTENCE, sourceix, 0, count_sentences); // start of sentence
+
+// if(p->newword & 2)
+// DoMarker(espeakEVENT_END, count_characters, 0, count_sentences); // end of clause
+
+ if(p->newword & 1)
+ DoMarker(espeakEVENT_WORD, sourceix, p->sourceix >> 11, clause_start_word + word_count++);
+ }
+
+ EndAmplitude();
+
+ if(p->prepause > 0)
+ DoPause(p->prepause,1);
+
+ if(option_phoneme_events && (p->type != phVOWEL))
+ {
+ // Note, for vowels, do the phoneme event after the vowel-start
+ DoMarker(espeakEVENT_PHONEME, sourceix, 0, p->ph->mnemonic);
+ }
+
+ switch(p->type)
+ {
+ case phPAUSE:
+ DoPause(p->length,0);
+ break;
+
+ case phSTOP:
+ released = 0;
+ if(next->type==phVOWEL) released = 1;
+ if(next->type==phLIQUID && !next->newword) released = 1;
+
+ if(released)
+ DoSample(p->ph,next->ph,2,0,0);
+ else
+ DoSample(p->ph,phoneme_tab[phonPAUSE],2,0,0);
+ break;
+
+ case phFRICATIVE:
+ if(p->synthflags & SFLAG_LENGTHEN)
+ DoSample(p->ph,next->ph,2,p->length,0); // play it twice for [s:] etc.
+ DoSample(p->ph,next->ph,2,p->length,0);
+ break;
+
+ case phVSTOP:
+ pre_voiced = 0;
+ if(next->type==phVOWEL)
+ {
+ DoAmplitude(p->amp,NULL);
+ DoPitch(envelope_data[p->env],p->pitch1,p->pitch2);
+ pre_voiced = 1;
+ }
+ else
+ if((next->type==phLIQUID) && !next->newword)
+ {
+ DoAmplitude(next->amp,NULL);
+ DoPitch(envelope_data[next->env],next->pitch1,next->pitch2);
+ pre_voiced = 1;
+ }
+ else
+ {
+ if(last_pitch_cmd < 0)
+ {
+ DoAmplitude(next->amp,NULL);
+ DoPitch(envelope_data[p->env],p->pitch1,p->pitch2);
+ }
+ }
+
+ if((prev->type==phVOWEL) || (prev->ph->phflags & phVOWEL2))
+ {
+ // a period of voicing before the release
+ DoSpect(p->ph,phoneme_tab[phonSCHWA],next->ph,1,p,0);
+ if(p->synthflags & SFLAG_LENGTHEN)
+ {
+ DoPause(20,0);
+ DoSpect(p->ph,phoneme_tab[phonSCHWA],next->ph,1,p,0);
+ }
+ }
+ else
+ {
+ if(p->synthflags & SFLAG_LENGTHEN)
+ {
+ DoPause(50,0);
+ }
+ }
+
+ if(pre_voiced)
+ {
+ // followed by a vowel, or liquid + vowel
+ StartSyllable();
+ DoSpect(p->ph,prev->ph,next->ph,2,p,0);
+ }
+ else
+ {
+// if((prev->type != phVOWEL) && ((prev->ph->phflags & phVOICED)==0) && ((next->ph->phflags & phVOICED)==0))
+// DoSpect(p->ph,prev->ph,phoneme_tab[phonPAUSE_SHORT],2,p,0);
+// else
+ DoSpect(p->ph,prev->ph,phoneme_tab[phonPAUSE],2,p,0);
+// DoSpect(p->ph,prev->ph,next->ph,2,p,0);
+ }
+ break;
+
+ case phVFRICATIVE:
+ if(next->type==phVOWEL)
+ {
+ DoAmplitude(p->amp,NULL);
+ DoPitch(envelope_data[p->env],p->pitch1,p->pitch2);
+ }
+ else
+ if(next->type==phLIQUID)
+ {
+ DoAmplitude(next->amp,NULL);
+ DoPitch(envelope_data[next->env],next->pitch1,next->pitch2);
+ }
+ else
+ {
+ if(last_pitch_cmd < 0)
+ {
+ DoAmplitude(p->amp,NULL);
+ DoPitch(envelope_data[p->env],p->pitch1,p->pitch2);
+ }
+ }
+
+ if((next->type==phVOWEL) || ((next->type==phLIQUID)) && (next->newword==0)) // ?? test 14.Aug.2007
+ {
+ StartSyllable();
+ if(p->synthflags & SFLAG_LENGTHEN)
+ DoSpect(p->ph,prev->ph,next->ph,2,p,0);
+ DoSpect(p->ph,prev->ph,next->ph,2,p,0);
+ }
+ else
+ {
+ if(p->synthflags & SFLAG_LENGTHEN)
+ DoSpect(p->ph,prev->ph,phoneme_tab[phonPAUSE],2,p,0);
+ DoSpect(p->ph,prev->ph,phoneme_tab[phonPAUSE],2,p,0);
+ }
+ break;
+
+ case phNASAL:
+ if(!(p->synthflags & SFLAG_SEQCONTINUE))
+ {
+ DoAmplitude(p->amp,NULL);
+ DoPitch(envelope_data[p->env],p->pitch1,p->pitch2);
+ }
+
+ if(prev->type==phNASAL)
+ {
+ last_frame = NULL;
+ }
+
+ if(next->type==phVOWEL)
+ {
+ StartSyllable();
+ DoSpect(p->ph,prev->ph,next->ph,1,p,0);
+ }
+ else
+ if(prev->type==phVOWEL && (p->synthflags & SFLAG_SEQCONTINUE))
+ {
+ DoSpect(p->ph,prev->ph,phoneme_tab[phonPAUSE],2,p,0);
+ }
+ else
+ {
+ last_frame = NULL; // only for nasal ?
+ if(next->type == phLIQUID)
+ DoSpect(p->ph,prev->ph,phoneme_tab[phonSONORANT],2,p,0);
+ else
+ DoSpect(p->ph,prev->ph,phoneme_tab[phonPAUSE],2,p,0);
+ last_frame = NULL;
+ }
+
+ break;
+
+ case phLIQUID:
+ modulation = 0;
+ if(p->ph->phflags & phTRILL)
+ modulation = 5;
+
+ prev_ph = prev->ph;
+// if(p->newword)
+// prev_ph = phoneme_tab[phonPAUSE]; // pronounce fully at the start of a word
+
+ if(!(p->synthflags & SFLAG_SEQCONTINUE))
+ {
+ DoAmplitude(p->amp,NULL);
+ DoPitch(envelope_data[p->env],p->pitch1,p->pitch2);
+ }
+
+ if(prev->type==phNASAL)
+ {
+ last_frame = NULL;
+ }
+
+ if(next->type==phVOWEL)
+ {
+ StartSyllable();
+ DoSpect(p->ph,prev_ph,next->ph,1,p,modulation); // (,)r
+ }
+ else
+ if(prev->type==phVOWEL && (p->synthflags & SFLAG_SEQCONTINUE))
+ {
+ DoSpect(p->ph,prev_ph,next->ph,1,p,modulation);
+ }
+ else
+ {
+ DoSpect(p->ph,prev_ph,next->ph,1,p,modulation);
+ }
+
+ break;
+
+ case phVOWEL:
+ ph = p->ph;
+ stress = p->tone & 0xf;
+
+ // vowel transition from the preceding phoneme
+ vowel_transition0 = vowel_transition[0];
+ vowel_transition1 = vowel_transition[1];
+
+ pitch_env = envelope_data[p->env];
+ amp_env = NULL;
+ if(p->tone_ph != 0)
+ {
+ pitch_env = LookupEnvelope(phoneme_tab[p->tone_ph]->spect);
+ amp_env = LookupEnvelope(phoneme_tab[p->tone_ph]->after);
+ }
+
+ StartSyllable();
+
+ modulation = 2;
+ if(stress <= 1)
+ modulation = 1; // 16ths
+ else
+ if(stress >= 7)
+ modulation = 3;
+
+ if(prev->type == phVSTOP || prev->type == phVFRICATIVE)
+ {
+ DoAmplitude(p->amp,amp_env);
+ DoPitch(pitch_env,p->pitch1,p->pitch2); // don't use prevocalic rising tone
+ DoSpect(ph,prev->ph,next->ph,1,p,modulation);
+ }
+ else
+ if(prev->type==phLIQUID || prev->type==phNASAL)
+ {
+ DoAmplitude(p->amp,amp_env);
+ DoSpect(ph,prev->ph,next->ph,1,p,modulation); // continue with pre-vocalic rising tone
+ DoPitch(pitch_env,p->pitch1,p->pitch2);
+ }
+ else
+ {
+ if(!(p->synthflags & SFLAG_SEQCONTINUE))
+ {
+ DoAmplitude(p->amp,amp_env);
+ DoPitch(pitch_env,p->pitch1,p->pitch2);
+ }
+
+ DoSpect(ph,prev->ph,next->ph,1,p,modulation);
+ }
+
+ if(option_phoneme_events)
+ {
+ DoMarker(espeakEVENT_PHONEME, sourceix, 0, p->ph->mnemonic);
+ }
+
+ DoSpect(p->ph,prev->ph,next->ph,2,p,modulation);
+
+ memset(vowel_transition,0,sizeof(vowel_transition));
+ break;
+ }
+ ix++;
+ }
+ EndPitch(1);
+ if(*n_ph > 0)
+ {
+ DoMarker(espeakEVENT_END, count_characters, 0, count_sentences); // end of clause
+ *n_ph = 0;
+ }
+
+ return(0); // finished the phoneme list
+} // end of Generate
+
+
+
+
+static int timer_on = 0;
+static int paused = 0;
+
+int SynthOnTimer()
+{//===============
+ if(!timer_on)
+ {
+ return(WavegenCloseSound());
+ }
+
+ do {
+ if(WcmdqUsed() > 0)
+ WavegenOpenSound();
+
+ if(Generate(phoneme_list,&n_phoneme_list,1)==0)
+ {
+ SpeakNextClause(NULL,NULL,1);
+ }
+ } while(skipping_text);
+
+ return(0);
+}
+
+
+int SynthStatus()
+{//==============
+ return(timer_on | paused);
+}
+
+
+
+int SpeakNextClause(FILE *f_in, const void *text_in, int control)
+{//==============================================================
+// Speak text from file (f_in) or memory (text_in)
+// control 0: start
+// either f_in or text_in is set, the other must be NULL
+
+// The other calls have f_in and text_in = NULL
+// control 1: speak next text
+// 2: stop
+// 3: pause (toggle)
+// 4: is file being read (0=no, 1=yes)
+// 5: interrupt and flush current text.
+
+ int clause_tone;
+ char *voice_change;
+ static FILE *f_text=NULL;
+ static const void *p_text=NULL;
+
+ if(control == 4)
+ {
+ if((f_text == NULL) && (p_text == NULL))
+ return(0);
+ else
+ return(1);
+ }
+
+ if(control == 2)
+ {
+ // stop speaking
+ timer_on = 0;
+ p_text = NULL;
+ if(f_text != NULL)
+ {
+ fclose(f_text);
+ f_text=NULL;
+ }
+ n_phoneme_list = 0;
+ WcmdqStop();
+
+ embedded_value[EMBED_T] = 0;
+ return(0);
+ }
+
+ if(control == 3)
+ {
+ // toggle pause
+ if(paused == 0)
+ {
+ timer_on = 0;
+ paused = 2;
+ }
+ else
+ {
+ WavegenOpenSound();
+ timer_on = 1;
+ paused = 0;
+ Generate(phoneme_list,&n_phoneme_list,0); // re-start from beginning of clause
+ }
+ return(0);
+ }
+
+ if(control == 5)
+ {
+ // stop speaking, but continue looking for text
+ n_phoneme_list = 0;
+ WcmdqStop();
+ return(0);
+ }
+
+ if((f_in != NULL) || (text_in != NULL))
+ {
+ f_text = f_in;
+ p_text = text_in;
+ timer_on = 1;
+ paused = 0;
+ }
+
+ if((f_text==NULL) && (p_text==NULL))
+ {
+ skipping_text = 0;
+ timer_on = 0;
+ return(0);
+ }
+
+ if((f_text != NULL) && feof(f_text))
+ {
+ timer_on = 0;
+ fclose(f_text);
+ f_text=NULL;
+ return(0);
+ }
+
+ if(current_phoneme_table != voice->phoneme_tab_ix)
+ {
+ SelectPhonemeTable(voice->phoneme_tab_ix);
+ }
+
+ // read the next clause from the input text file, translate it, and generate
+ // entries in the wavegen command queue
+ p_text = TranslateClause(translator, f_text, p_text, &clause_tone, &voice_change);
+
+ CalcPitches(translator, clause_tone);
+ CalcLengths(translator);
+
+ GetTranslatedPhonemeString(translator->phon_out,sizeof(translator->phon_out));
+ if(option_phonemes > 0)
+ {
+ fprintf(f_trans,"%s\n",translator->phon_out);
+
+ if(!iswalpha(0x010d))
+ {
+ // check that c-caron is recognized as an alphabetic character
+ fprintf(stderr,"Warning: Accented letters are not recognized, eg: U+010D\nSet LC_CTYPE to a UTF-8 locale\n");
+ }
+ }
+ if(phoneme_callback != NULL)
+ {
+ phoneme_callback(translator->phon_out);
+ }
+
+
+ if(skipping_text)
+ {
+ n_phoneme_list = 0;
+ return(1);
+ }
+
+ if(mbrola_name[0] != 0)
+ {
+#ifdef USE_MBROLA_LIB
+ MbrolaTranslate(phoneme_list,n_phoneme_list,NULL);
+#else
+ {
+ FILE *f_mbrola;
+ if((f_mbrola = f_trans) == stderr)
+ f_mbrola = stdout;
+ MbrolaTranslate(phoneme_list,n_phoneme_list,f_mbrola);
+ }
+#endif
+ }
+
+ Generate(phoneme_list,&n_phoneme_list,0);
+ WavegenOpenSound();
+
+ if(voice_change != NULL)
+ {
+ // voice change at the end of the clause (i.e. clause was terminated by a voice change)
+ new_voice = LoadVoiceVariant(voice_change,0); // add a Voice instruction to wavegen at the end of the clause
+ }
+
+ if(new_voice)
+ {
+ // finished the current clause, now change the voice if there was an embedded
+ // change voice command at the end of it (i.e. clause was broken at the change voice command)
+ DoVoiceChange(voice);
+ new_voice = NULL;
+ }
+
+ return(1);
+} // end of SpeakNextClause
+
diff --git a/Plugins/eSpeak/eSpeak/synthesize.h b/Plugins/eSpeak/eSpeak/synthesize.h
new file mode 100644
index 0000000..50710f8
--- /dev/null
+++ b/Plugins/eSpeak/eSpeak/synthesize.h
@@ -0,0 +1,349 @@
+/***************************************************************************
+ * Copyright (C) 2005 to 2007 by Jonathan Duddington *
+ * email: jonsd@users.sourceforge.net *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 3 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write see: *
+ * <http://www.gnu.org/licenses/>. *
+ ***************************************************************************/
+
+
+#define N_PHONEME_LIST 1000 // enough for source[N_TR_SOURCE] full of text, else it will truncate
+
+#define MAX_HARMONIC 400 // 400 * 50Hz = 20 kHz, more than enough
+#define N_SEQ_FRAMES 25 // max frames in a spectrum sequence (real max is ablut 8)
+#define STEPSIZE 64 // 2.9mS at 22 kHz sample rate
+
+#define PITCHfall 0
+#define PITCHrise 1
+
+// flags set for frames within a spectrum sequence
+#define FRFLAG_KLATT 0x01 // this frame includes extra data for Klatt synthesizer
+#define FRFLAG_VOWEL_CENTRE 0x02 // centre point of vowel
+#define FRFLAG_LEN_MOD 0x04 // reduce effect of length adjustment
+#define FRFLAG_BREAK_LF 0x08 // but keep f3 upwards
+#define FRFLAG_BREAK 0x10 // don't merge with next frame
+#define FRFLAG_BREAK_2 0x18 // FRFLAG_BREAK_LF or FRFLAG_BREAK
+#define FRFLAG_FORMANT_RATE 0x20 // Flag5 allow increased rate of change of formant freq
+#define FRFLAG_MODULATE 0x40 // Flag6 modulate amplitude of some cycles to give trill
+#define FRFLAG_DEFER_WAV 0x80 // Flag7 defer mixing WAV until the next frame
+#define FRFLAG_COPIED 0x8000 // This frame has been copied into temporary rw memory
+
+#define SFLAG_SEQCONTINUE 0x01 // a liquid or nasal after a vowel, but not followed by a vowel
+#define SFLAG_EMBEDDED 0x02 // there are embedded commands before this phoneme
+#define SFLAG_SYLLABLE 0x04 // vowel or syllabic consonant
+#define SFLAG_LENGTHEN 0x08 // lengthen symbol : included after this phoneme
+#define SFLAG_DICTIONARY 0x10 // the pronunciation of this word was listed in the xx_list dictionary
+#define SFLAG_SWITCHED_LANG 0x20 // this word uses phonemes from a different language
+#define SFLAG_PROMOTE_STRESS 0x40 // this unstressed word can be promoted to stressed
+
+// embedded command numbers
+#define EMBED_P 1 // pitch
+#define EMBED_S 2 // speed (used in setlengths)
+#define EMBED_A 3 // amplitude/volume
+#define EMBED_R 4 // pitch range/expression
+#define EMBED_H 5 // echo/reverberation
+#define EMBED_T 6 // different tone for announcing punctuation
+#define EMBED_I 7 // sound icon
+#define EMBED_S2 8 // speed (used in synthesize)
+#define EMBED_Y 9 // say-as commands
+#define EMBED_M 10 // mark name
+#define EMBED_U 11 // audio uri
+#define EMBED_B 12 // break
+#define EMBED_F 13 // emphasis
+
+#define N_EMBEDDED_VALUES 14
+extern int embedded_value[N_EMBEDDED_VALUES];
+extern int embedded_default[N_EMBEDDED_VALUES];
+
+
+#define N_PEAKS 9
+#define N_MARKERS 8
+
+typedef struct {
+ short pkfreq;
+ short pkheight;
+ short pkwidth;
+ short pkright;
+} peak_t;
+
+#define N_KLATTP 10 // this affects the phoneme data file format
+#define KLATT_AV 0
+#define KLATT_Kopen 1
+#define KLATT_Skew 2
+#define KLATT_Tilt 3
+#define KLATT_Turb 4
+#define KLATT_Aspr 5
+#define KLATT_AVp 6 // this is after the parameters which can be change by the Voice
+#define KLATT_Fric 7
+#define KLATT_FricBP 8
+#define KLATT_spare1 9
+
+typedef struct {
+ short frflags;
+ unsigned char length;
+ unsigned char rms;
+ short ffreq[9];
+ unsigned char fheight[9];
+ unsigned char fwidth[6]; // width/4
+ unsigned char fright[6]; // width/4
+ unsigned char fwidth6, fright6;
+ unsigned char klattp[N_KLATTP];
+} frame_t;
+
+typedef struct {
+ short frflags;
+ unsigned char length;
+ unsigned char rms;
+ short ffreq[9];
+ unsigned char fheight[9];
+ unsigned char fwidth[6]; // width/4
+ unsigned char fright[6]; // width/4
+} frame_t2; // the original, without Klatt additions, used for file "phondata"
+
+
+
+// formant data used by wavegen
+typedef struct {
+ int freq; // Hz<<16
+ int height; // height<<15
+ int left; // Hz<<16
+ int right; // Hz<<16
+ DOUBLEX freq1; // floating point versions of the above
+ DOUBLEX height1;
+ DOUBLEX left1;
+ DOUBLEX right1;
+ DOUBLEX freq_inc; // increment by this every 64 samples
+ DOUBLEX height_inc;
+ DOUBLEX left_inc;
+ DOUBLEX right_inc;
+} wavegen_peaks_t;
+
+typedef struct {
+unsigned char *pitch_env;
+int pitch; // pitch Hz*256
+int pitch_ix; // index into pitch envelope (*256)
+int pitch_inc; // increment to pitch_ix
+int pitch_base; // Hz*256 low, before modified by envelope
+int pitch_range; // Hz*256 range of envelope
+
+unsigned char *mix_wavefile; // wave file to be added to synthesis
+int n_mix_wavefile; // length in bytes
+int mix_wave_scale; // 0=2 byte samples
+int mix_wave_amp;
+int mix_wavefile_ix;
+
+int amplitude;
+int amplitude_v;
+int prev_was_synth; // previous sound was synthesized (not a played wave or pause)
+} WGEN_DATA;
+
+
+typedef struct {
+ double a;
+ double b;
+ double c;
+ double x1;
+ double x2;
+} RESONATOR;
+
+
+typedef struct {
+ short length_total; // not used
+ unsigned char n_frames;
+ unsigned char flags;
+ frame_t2 frame[N_SEQ_FRAMES]; // max. frames in a spectrum sequence
+} SPECT_SEQ; // sequence of espeak formant frames
+
+typedef struct {
+ short length_total; // not used
+ unsigned char n_frames;
+ unsigned char flags;
+ frame_t frame[N_SEQ_FRAMES]; // max. frames in a spectrum sequence
+} SPECT_SEQK; // sequence of klatt formants frames
+
+
+typedef struct {
+ short length;
+ short frflags;
+ frame_t *frame;
+} frameref_t;
+
+
+typedef struct {
+ PHONEME_TAB *ph;
+ unsigned char env; // pitch envelope number
+ unsigned char tone;
+ unsigned char type;
+ unsigned char prepause;
+ unsigned char amp;
+ unsigned char tone_ph; // tone phoneme to use with this vowel
+ unsigned char newword; // bit 0=start of word, bit 1=end of clause, bit 2=start of sentence
+ unsigned char synthflags;
+ short length; // length_mod
+ short pitch1; // pitch, 0-4095 within the Voice's pitch range
+ short pitch2;
+ unsigned short sourceix; // ix into the original source text string, only set at the start of a word
+} PHONEME_LIST;
+
+
+typedef struct {
+ int name;
+ int length;
+ char *data;
+ char *filename;
+} SOUND_ICON;
+
+typedef struct {
+ int name;
+ unsigned int next_phoneme;
+ int mbr_name;
+ int mbr_name2;
+ int percent; // percentage length of first component
+ int control;
+} MBROLA_TAB;
+
+typedef struct {
+ int speed_factor1;
+ int speed_factor2;
+ int speed_factor3;
+ int min_sample_len;
+ int fast_settings[8];
+} SPEED_FACTORS;
+
+
+// phoneme table
+extern PHONEME_TAB *phoneme_tab[N_PHONEME_TAB];
+
+// list of phonemes in a clause
+extern int n_phoneme_list;
+extern PHONEME_LIST phoneme_list[N_PHONEME_LIST];
+extern unsigned int embedded_list[];
+
+extern unsigned char env_fall[128];
+extern unsigned char env_rise[128];
+extern unsigned char env_frise[128];
+
+#define MAX_PITCH_VALUE 101
+extern unsigned char pitch_adjust_tab[MAX_PITCH_VALUE+1];
+
+// queue of commands for wavegen
+#define WCMD_KLATT 1
+#define WCMD_KLATT2 2
+#define WCMD_SPECT 3
+#define WCMD_SPECT2 4
+#define WCMD_PAUSE 5
+#define WCMD_WAVE 6
+#define WCMD_WAVE2 7
+#define WCMD_AMPLITUDE 8
+#define WCMD_PITCH 9
+#define WCMD_MARKER 10
+#define WCMD_VOICE 11
+#define WCMD_EMBEDDED 12
+
+
+#define N_WCMDQ 160
+#define MIN_WCMDQ 22 // need this many free entries before adding new phoneme
+
+extern long wcmdq[N_WCMDQ][4];
+extern int wcmdq_head;
+extern int wcmdq_tail;
+
+// from Wavegen file
+int WcmdqFree();
+void WcmdqStop();
+int WcmdqUsed();
+void WcmdqInc();
+int WavegenOpenSound();
+int WavegenCloseSound();
+int WavegenInitSound();
+void WavegenInit(int rate, int wavemult_fact);
+float polint(float xa[],float ya[],int n,float x);
+int WavegenFill(int fill_zeros);
+void MarkerEvent(int type, unsigned int char_position, int value, unsigned char *out_ptr);
+
+
+extern unsigned char *wavefile_data;
+extern int samplerate;
+extern int samplerate_native;
+
+extern int wavefile_ix;
+extern int wavefile_amp;
+extern int wavefile_ix2;
+extern int wavefile_amp2;
+extern int vowel_transition[4];
+extern int vowel_transition0, vowel_transition1;
+
+extern int mbrola_delay;
+extern char mbrola_name[20];
+
+// from synthdata file
+unsigned int LookupSound(PHONEME_TAB *ph1, PHONEME_TAB *ph2, int which, int *match_level, int control);
+frameref_t *LookupSpect(PHONEME_TAB *ph1, PHONEME_TAB *prev_ph, PHONEME_TAB *next_ph, int which, int *match_level, int *n_frames, PHONEME_LIST *plist);
+
+unsigned char *LookupEnvelope(int ix);
+int LoadPhData();
+
+void SynthesizeInit(void);
+int Generate(PHONEME_LIST *phoneme_list, int *n_ph, int resume);
+void MakeWave2(PHONEME_LIST *p, int n_ph);
+int SynthOnTimer(void);
+int SpeakNextClause(FILE *f_text, const void *text_in, int control);
+int SynthStatus(void);
+void SetSpeed(int control);
+void SetEmbedded(int control, int value);
+void SelectPhonemeTable(int number);
+int SelectPhonemeTableName(const char *name);
+
+void Write4Bytes(FILE *f, int value);
+int Read4Bytes(FILE *f);
+int CompileDictionary(const char *dsource, const char *dict_name, FILE *log, char *err_name,int flags);
+
+
+extern unsigned char *envelope_data[18];
+extern int formant_rate[]; // max rate of change of each formant
+extern SPEED_FACTORS speed;
+
+extern long count_samples;
+extern int outbuf_size;
+extern unsigned char *out_ptr;
+extern unsigned char *out_start;
+extern unsigned char *out_end;
+extern int event_list_ix;
+extern espeak_EVENT *event_list;
+extern t_espeak_callback* synth_callback;
+extern int option_log_frames;
+extern const char *version_string;
+extern const int version_phdata;
+
+#define N_SOUNDICON_TAB 80 // total entries in soundicon_tab
+#define N_SOUNDICON_SLOTS 4 // number of slots reserved for dynamic loading of audio files
+extern int n_soundicon_tab;
+extern SOUND_ICON soundicon_tab[N_SOUNDICON_TAB];
+
+espeak_ERROR SetVoiceByName(const char *name);
+espeak_ERROR SetVoiceByProperties(espeak_VOICE *voice_selector);
+espeak_ERROR LoadMbrolaTable(const char *mbrola_voice, const char *phtrans, int srate);
+void SetParameter(int parameter, int value, int relative);
+void MbrolaTranslate(PHONEME_LIST *plist, int n_phonemes, FILE *f_mbrola);
+//int MbrolaSynth(char *p_mbrola);
+int DoSample(PHONEME_TAB *ph1, PHONEME_TAB *ph2, int which, int length_mod, int amp);
+int DoSpect(PHONEME_TAB *this_ph, PHONEME_TAB *prev_ph, PHONEME_TAB *next_ph,
+ int which, PHONEME_LIST *plist, int modulation);
+int PauseLength(int pause, int control);
+int LookupPhonemeTable(const char *name);
+
+void InitBreath(void);
+
+void KlattInit();
+int Wavegen_Klatt2(int length, int modulation, int resume, frame_t *fr1, frame_t *fr2);
diff --git a/Plugins/eSpeak/eSpeak/tr_languages.cpp b/Plugins/eSpeak/eSpeak/tr_languages.cpp
new file mode 100644
index 0000000..0d8776a
--- /dev/null
+++ b/Plugins/eSpeak/eSpeak/tr_languages.cpp
@@ -0,0 +1,1310 @@
+/***************************************************************************
+ * Copyright (C) 2005 to 2007 by Jonathan Duddington *
+ * email: jonsd@users.sourceforge.net *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 3 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, see: *
+ * <http://www.gnu.org/licenses/>. *
+ ***************************************************************************/
+
+#include "StdAfx.h"
+
+#include <stdio.h>
+#include <ctype.h>
+#include <wctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include <locale.h>
+
+#include <wctype.h>
+
+#include "speak_lib.h"
+#include "speech.h"
+#include "phoneme.h"
+#include "synthesize.h"
+#include "translate.h"
+
+
+
+#define L_qa 0x716100
+#define L_grc 0x677263 // grc Ancient Greek
+#define L_jbo 0x6a626f // jbo Lojban
+#define L_zhy 0x7a6879 // zhy
+
+// start of unicode pages for character sets
+#define OFFSET_GREEK 0x380
+#define OFFSET_CYRILLIC 0x420
+#define OFFSET_ARMENIAN 0x530
+#define OFFSET_DEVANAGARI 0x900
+#define OFFSET_BENGALI 0x980
+#define OFFSET_TAMIL 0xb80
+#define OFFSET_KANNADA 0xc80
+#define OFFSET_MALAYALAM 0xd00
+#define OFFSET_KOREAN 0x1100
+
+static void Translator_Russian(Translator *tr);
+
+static void SetLetterVowel(Translator *tr, int c)
+{//==============================================
+ tr->letter_bits[c] = (tr->letter_bits[c] & 0x40) | 0x81; // keep value for group 6 (front vowels e,i,y)
+}
+
+static void ResetLetterBits(Translator *tr, int groups)
+{//====================================================
+// Clear all the specified groups
+ unsigned int ix;
+ unsigned int mask;
+
+ mask = ~groups;
+
+ for(ix=0; ix<sizeof(tr->letter_bits); ix++)
+ {
+ tr->letter_bits[ix] &= mask;
+ }
+}
+
+static void SetLetterBits(Translator *tr, int group, const char *string)
+{//=====================================================================
+ int bits;
+ unsigned char c;
+
+ bits = (1L << group);
+ while((c = *string++) != 0)
+ tr->letter_bits[c] |= bits;
+}
+
+static void SetLetterBitsRange(Translator *tr, int group, int first, int last)
+{//===========================================================================
+ int bits;
+ int ix;
+
+ bits = (1L << group);
+ for(ix=first; ix<=last; ix++)
+ {
+ tr->letter_bits[ix] |= bits;
+ }
+}
+
+
+static Translator* NewTranslator(void)
+{//===================================
+ Translator *tr;
+ int ix;
+ static const unsigned char stress_amps2[] = {17,17, 20,20, 20,22, 22,20 };
+ static const short stress_lengths2[8] = {182,140, 220,220, 220,240, 260,280};
+ static const wchar_t empty_wstring[1] = {0};
+ static const wchar_t punct_in_word[2] = {'\'', 0}; // allow hyphen within words
+
+ tr = (Translator *)Alloc(sizeof(Translator));
+ if(tr == NULL)
+ return(NULL);
+
+ tr->charset_a0 = charsets[1]; // ISO-8859-1, this is for when the input is not utf8
+ dictionary_name[0] = 0;
+ tr->dict_condition=0;
+ tr->data_dictrules = NULL; // language_1 translation rules file
+ tr->data_dictlist = NULL; // language_2 dictionary lookup file
+
+ tr->transpose_offset = 0;
+
+ // only need lower case
+ tr->letter_bits_offset = 0;
+ memset(tr->letter_bits,0,sizeof(tr->letter_bits));
+ memset(tr->letter_groups,0,sizeof(tr->letter_groups));
+
+ // 0-5 sets of characters matched by A B C E F G in pronunciation rules
+ // these may be set differently for different languages
+ SetLetterBits(tr,0,"aeiou"); // A vowels, except y
+ SetLetterBits(tr,1,"bcdfgjklmnpqstvxz"); // B hard consonants, excluding h,r,w
+ SetLetterBits(tr,2,"bcdfghjklmnpqrstvwxz"); // C all consonants
+ SetLetterBits(tr,3,"hlmnr"); // H 'soft' consonants
+ SetLetterBits(tr,4,"cfhkpqstx"); // F voiceless consonants
+ SetLetterBits(tr,5,"bdgjlmnrvwyz"); // G voiced
+ SetLetterBits(tr,6,"eiy"); // Letter group Y, front vowels
+ SetLetterBits(tr,7,"aeiouy"); // vowels, including y
+
+
+ tr->char_plus_apostrophe = empty_wstring;
+ tr->punct_within_word = punct_in_word;
+
+ for(ix=0; ix<8; ix++)
+ {
+ tr->stress_amps[ix] = stress_amps2[ix];
+ tr->stress_amps_r[ix] = stress_amps2[ix] - 1;
+ tr->stress_lengths[ix] = stress_lengths2[ix];
+ }
+ memset(&(tr->langopts),0,sizeof(tr->langopts));
+
+ tr->langopts.stress_rule = 2;
+ tr->langopts.unstressed_wd1 = 1;
+ tr->langopts.unstressed_wd2 = 3;
+ tr->langopts.param[LOPT_SONORANT_MIN] = 95;
+ tr->langopts.param[LOPT_MAXAMP_EOC] = 19;
+ tr->langopts.param[LOPT_UNPRONOUNCABLE] = 's'; // don't count this character at start of word
+ tr->langopts.max_initial_consonants = 3;
+ tr->langopts.replace_chars = NULL;
+ tr->langopts.ascii_language = ""; // Non-Latin alphabet languages, use this language to speak Latin words, default is English
+
+ SetLengthMods(tr,201);
+// tr->langopts.length_mods = length_mods_en;
+// tr->langopts.length_mods0 = length_mods_en0;
+
+ tr->langopts.long_stop = 100;
+
+ tr->langopts.max_roman = 49;
+ tr->langopts.thousands_sep = ',';
+ tr->langopts.decimal_sep = '.';
+
+ memcpy(tr->punct_to_tone, punctuation_to_tone, sizeof(tr->punct_to_tone));
+
+ return(tr);
+}
+
+
+static const unsigned int replace_cyrillic_latin[] =
+ {0x430,'a',
+ 0x431,'b',
+ 0x446,'c',
+ 0x45b,0x107,
+ 0x447,0x10d,
+ 0x45f,'d'+(0x17e<<16),
+ 0x455,'d'+('z'<<16),
+ 0x434,'d',
+ 0x452,0x111,
+ 0x435,'e',
+ 0x444,'f',
+ 0x433,'g',
+ 0x445,'h',
+ 0x438,'i',
+ 0x458,'j',
+ 0x43a,'k',
+ 0x459,'l'+('j'<<16),
+ 0x43b,'l',
+ 0x43c,'m',
+ 0x45a,'n'+('j'<<16),
+ 0x43d,'n',
+ 0x43e,'o',
+ 0x43f,'p',
+ 0x440,'r',
+ 0x441,'s',
+ 0x448,0x161,
+ 0x442,'t',
+ 0x443,'u',
+ 0x432,'v',
+ 0x437,'z',
+ 0x436,0x17e,
+ 0x453,0x111,
+ 0x45c,0x107,
+0}; // ѓ ѕ ќ
+
+
+void SetIndicLetters(Translator *tr)
+{//=================================
+ // Set letter types for Indic scripts, Devanagari, Tamill, etc
+ static const char dev_consonants2[] = {0x02,0x03,0x58,0x59,0x5a,0x5b,0x5c,0x5d,0x5e,0x5f};
+
+ memset(tr->letter_bits,0,sizeof(tr->letter_bits));
+ SetLetterBitsRange(tr,LETTERGP_A,0x04,0x14); // vowel letters only
+ SetLetterBitsRange(tr,LETTERGP_B,0x3e,0x4d); // vowel signs, and virama
+
+ SetLetterBitsRange(tr,LETTERGP_C,0x15,0x39); // the main consonant range
+ SetLetterBits(tr,LETTERGP_C,dev_consonants2); // + additional consonants
+
+ SetLetterBitsRange(tr,LETTERGP_Y,0x04,0x14); // vowel letters
+ SetLetterBitsRange(tr,LETTERGP_Y,0x3e,0x4c); // + vowel signs
+
+ tr->langopts.param[LOPT_UNPRONOUNCABLE] = 1; // disable check for unpronouncable words
+}
+
+void SetupTranslator(Translator *tr, const short *lengths, const unsigned char *amps)
+{//==================================================================================
+ if(lengths != NULL)
+ memcpy(tr->stress_lengths,lengths,sizeof(tr->stress_lengths));
+ if(amps != NULL)
+ memcpy(tr->stress_amps,amps,sizeof(tr->stress_amps));
+}
+
+
+Translator *SelectTranslator(const char *name)
+{//===========================================
+ int name2 = 0;
+ Translator *tr;
+
+ static const unsigned char stress_amps_sk[8] = {17,17, 20,20, 20,22, 22,21 };
+ static const short stress_lengths_sk[8] = {190,190, 210,210, 0,0, 210,210};
+
+ // convert name string into a word of up to 4 characters, for the switch()
+ while(*name != 0)
+ name2 = (name2 << 8) + *name++;
+
+ tr = NewTranslator();
+
+ switch(name2)
+ {
+ case L('a','f'):
+ {
+ static const short stress_lengths_af[8] = {170,140, 220,220, 0, 0, 250,270};
+ SetupTranslator(tr,stress_lengths_af,NULL);
+
+ tr->langopts.stress_rule = 0;
+ tr->langopts.vowel_pause = 0x30;
+ tr->langopts.param[LOPT_DIERESES] = 1;
+ tr->langopts.param[LOPT_PREFIXES] = 1;
+ SetLetterVowel(tr,'y'); // add 'y' to vowels
+
+ tr->langopts.numbers = 0x8d1 + NUM_ROMAN;
+ tr->langopts.accents = 1;
+ }
+ break;
+
+ case L('b','n'): // Bengali
+ {
+ static const short stress_lengths_bn[8] = {180, 180, 210, 210, 0, 0, 230, 240};
+ static const unsigned char stress_amps_bn[8] = {18,18, 18,18, 20,20, 22,22 };
+
+ SetupTranslator(tr,stress_lengths_bn,stress_amps_bn);
+ tr->langopts.length_mods0 = tr->langopts.length_mods; // don't lengthen vowels in the last syllable
+
+ tr->langopts.stress_rule = 0;
+ tr->langopts.stress_flags = 0x10004; // use 'diminished' for unstressed final syllable
+ tr->letter_bits_offset = OFFSET_BENGALI;
+ SetIndicLetters(tr); // call this after setting OFFSET_BENGALI
+ SetLetterBitsRange(tr,LETTERGP_F,0x3e,0x4c); // vowel signs, but not virama
+
+ tr->langopts.numbers = 0x1;
+ tr->langopts.numbers2 = NUM2_100000;
+ }
+ break;
+
+ case L('c','y'): // Welsh
+ {
+ static const short stress_lengths_cy[8] = {170,220, 180,180, 0, 0, 250,270};
+ static const unsigned char stress_amps_cy[8] = {17,15, 18,18, 0,0, 22,20 }; // 'diminished' is used to mark a quieter, final unstressed syllable
+
+ SetupTranslator(tr,stress_lengths_cy,stress_amps_cy);
+
+ tr->charset_a0 = charsets[14]; // ISO-8859-14
+// tr->langopts.length_mods0 = tr->langopts.length_mods; // don't lengthen vowels in the last syllable
+ tr->langopts.stress_rule = 2;
+// tr->langopts.intonation_group = 4;
+
+ // 'diminished' is an unstressed final syllable
+ tr->langopts.stress_flags = 0x6 | 0x10;
+ tr->langopts.unstressed_wd1 = 0;
+ tr->langopts.unstressed_wd2 = 2;
+ tr->langopts.param[LOPT_SONORANT_MIN] = 120; // limit the shortening of sonorants before short vowels
+
+ tr->langopts.numbers = 0x401;
+
+ SetLetterVowel(tr,'w'); // add letter to vowels and remove from consonants
+ SetLetterVowel(tr,'y');
+ }
+ break;
+
+ case L('d','a'): // Danish
+ {
+ static const short stress_lengths_da[8] = {160,140, 200,200, 0,0, 220,210};
+ SetupTranslator(tr,stress_lengths_da,NULL);
+
+ tr->langopts.stress_rule = 0;
+ SetLetterVowel(tr,'y');
+// tr->langopts.numbers = 0x11849;
+ }
+ break;
+
+
+ case L('d','e'):
+ {
+ static const short stress_lengths_de[8] = {150,130, 190,190, 0, 0, 260,275};
+ tr->langopts.stress_rule = 0;
+ tr->langopts.word_gap = 0x8; // don't use linking phonemes
+ tr->langopts.vowel_pause = 0x30;
+ tr->langopts.param[LOPT_PREFIXES] = 1;
+ memcpy(tr->stress_lengths,stress_lengths_de,sizeof(tr->stress_lengths));
+
+ tr->langopts.numbers = 0x11419 + NUM_ROMAN;
+ SetLetterVowel(tr,'y');
+ }
+ break;
+
+ case L('e','n'):
+ {
+ static const short stress_lengths_en[8] = {182,140, 220,220, 0,0, 248,275};
+ SetupTranslator(tr,stress_lengths_en,NULL);
+
+ tr->langopts.stress_rule = 0;
+ tr->langopts.numbers = 0x841 + NUM_ROMAN;
+ tr->langopts.param[LOPT_COMBINE_WORDS] = 2; // allow "mc" to cmbine with the following word
+ }
+ break;
+
+ case L('e','l'): // Greek
+ case L_grc: // Ancient Greek
+ {
+ static const short stress_lengths_el[8] = {155, 180, 210, 210, 0, 0, 270, 300};
+ static const unsigned char stress_amps_el[8] = {15,12, 20,20, 20,22, 22,21 }; // 'diminished' is used to mark a quieter, final unstressed syllable
+
+ // character codes offset by 0x380
+ static const char el_vowels[] = {0x10,0x2c,0x2d,0x2e,0x2f,0x30,0x31,0x35,0x37,0x39,0x3f,0x45,0x49,0x4a,0x4b,0x4c,0x4d,0x4e,0x4f,0};
+ static const char el_fvowels[] = {0x2d,0x2e,0x2f,0x35,0x37,0x39,0x45,0x4d,0}; // ε η ι Ï… έ ή ί Ï
+ static const char el_voiceless[]= {0x38,0x3a,0x3e,0x40,0x42,0x43,0x44,0x46,0x47,0}; // θ κ ξ π ς σ τ φ χ
+ static const char el_consonants[]={0x32,0x33,0x34,0x36,0x38,0x3a,0x3b,0x3c,0x3d,0x3e,0x40,0x41,0x42,0x43,0x44,0x46,0x47,0x48,0};
+ static const wchar_t el_char_apostrophe[] = {0x3c3,0}; // σ
+
+ SetupTranslator(tr,stress_lengths_el,stress_amps_el);
+
+ tr->charset_a0 = charsets[7]; // ISO-8859-7
+ tr->char_plus_apostrophe = el_char_apostrophe;
+
+ tr->letter_bits_offset = OFFSET_GREEK;
+ memset(tr->letter_bits,0,sizeof(tr->letter_bits));
+ SetLetterBits(tr,LETTERGP_A,el_vowels);
+ SetLetterBits(tr,LETTERGP_B,el_voiceless);
+ SetLetterBits(tr,LETTERGP_C,el_consonants);
+ SetLetterBits(tr,LETTERGP_Y,el_fvowels); // front vowels: ε η ι υ
+
+ tr->langopts.length_mods0 = tr->langopts.length_mods; // don't lengthen vowels in the last syllable
+ tr->langopts.stress_rule = 2;
+ tr->langopts.stress_flags = 0x6; // mark unstressed final syllables as diminished
+ tr->langopts.unstressed_wd1 = 0;
+ tr->langopts.unstressed_wd2 = 2;
+ tr->langopts.param[LOPT_SONORANT_MIN] = 130; // limit the shortening of sonorants before short vowels
+
+ tr->langopts.numbers = 0x309;
+ tr->langopts.numbers2 = 0x2; // variant form of numbers before thousands
+
+ if(name2 == L_grc)
+ {
+ // ancient greek
+ tr->langopts.param[LOPT_UNPRONOUNCABLE] = 1;
+ }
+ }
+ break;
+
+ case L('e','o'):
+ {
+ static const short stress_lengths_eo[8] = {145, 145, 230, 170, 0, 0, 360, 370};
+ static const unsigned char stress_amps_eo[] = {16,14, 20,20, 20,22, 22,21 };
+ static const wchar_t eo_char_apostrophe[2] = {'l',0};
+
+ SetupTranslator(tr,stress_lengths_eo,stress_amps_eo);
+
+ tr->charset_a0 = charsets[3]; // ISO-8859-3
+ tr->char_plus_apostrophe = eo_char_apostrophe;
+
+ tr->langopts.word_gap = 1;
+ tr->langopts.vowel_pause = 2;
+ tr->langopts.stress_rule = 2;
+ tr->langopts.stress_flags = 0x6 | 0x10;
+ tr->langopts.unstressed_wd1 = 3;
+ tr->langopts.unstressed_wd2 = 2;
+
+ tr->langopts.numbers = 0x1409 + NUM_ROMAN;
+ }
+ break;
+
+ case L('e','s'): // Spanish
+ case L('c','a'): // Catalan
+ {
+ static const short stress_lengths_es[8] = {180, 210, 190, 190, 0, 0, 230, 260};
+// static const short stress_lengths_es[8] = {170, 200, 180, 180, 0, 0, 220, 250};
+ static const unsigned char stress_amps_es[8] = {16,12, 18,18, 20,20, 20,20 }; // 'diminished' is used to mark a quieter, final unstressed syllable
+ static const wchar_t ca_punct_within_word[] = {'\'',0xb7,0}; // ca: allow middle-dot within word
+
+ SetupTranslator(tr,stress_lengths_es,stress_amps_es);
+
+ tr->langopts.length_mods0 = tr->langopts.length_mods; // don't lengthen vowels in the last syllable
+ tr->langopts.stress_rule = 2;
+
+ // stress last syllable if it doesn't end in vowel or "s" or "n"
+ // 'diminished' is an unstressed final syllable
+ tr->langopts.stress_flags = 0x200 | 0x6 | 0x10;
+ tr->langopts.unstressed_wd1 = 0;
+ tr->langopts.unstressed_wd2 = 2;
+ tr->langopts.param[LOPT_SONORANT_MIN] = 120; // limit the shortening of sonorants before short vowels
+
+ tr->langopts.numbers = 0x529 + NUM_ROMAN + NUM_ROMAN_AFTER;
+
+ if(name2 == L('c','a'))
+ {
+ tr->punct_within_word = ca_punct_within_word;
+ tr->langopts.stress_flags = 0x200 | 0x6 | 0x30; // stress last syllable unless word ends with a vowel
+ }
+ }
+ break;
+
+
+ case L('f','i'): // Finnish
+ {
+ static const unsigned char stress_amps_fi[8] = {18,16, 22,22, 20,22, 22,22 };
+ static const short stress_lengths_fi[8] = {150,180, 200,200, 0,0, 210,250};
+
+ SetupTranslator(tr,stress_lengths_fi,stress_amps_fi);
+
+ tr->langopts.stress_rule = 0;
+ tr->langopts.stress_flags = 0x56; // move secondary stress from light to a following heavy syllable
+ tr->langopts.param[LOPT_IT_DOUBLING] = 1;
+ tr->langopts.long_stop = 130;
+
+ tr->langopts.numbers = 0x1809;
+ SetLetterVowel(tr,'y');
+ tr->langopts.max_initial_consonants = 2;
+ tr->langopts.spelling_stress = 1;
+ tr->langopts.intonation_group = 3; // less intonation, don't raise pitch at comma
+ }
+ break;
+
+ case L('f','r'): // french
+ {
+ static const short stress_lengths_fr[8] = {190, 170, 190, 200, 0, 0, 235, 240};
+ static const unsigned char stress_amps_fr[8] = {18,16, 20,20, 20,22, 22,21 };
+
+ SetupTranslator(tr,stress_lengths_fr,stress_amps_fr);
+ tr->langopts.stress_rule = 3; // stress on final syllable
+ tr->langopts.stress_flags = 0x0024; // don't use secondary stress
+ tr->langopts.param[LOPT_IT_LENGTHEN] = 1; // remove lengthen indicator from unstressed syllables
+
+ tr->langopts.numbers = 0x1509 + 0x8000 + NUM_NOPAUSE | NUM_ROMAN;
+ SetLetterVowel(tr,'y');
+ }
+ break;
+
+#ifdef deleted
+ case L('g','a'): // Irish Gaelic
+ {
+ tr->langopts.stress_rule = 1;
+ }
+ break;
+#endif
+
+ case L('h','i'): // Hindi
+ case L('n','e'): // Nepali
+ {
+ static const short stress_lengths_hi[8] = {190, 190, 210, 210, 0, 0, 230, 250};
+ static const unsigned char stress_amps_hi[8] = {17,14, 20,19, 20,22, 22,21 };
+
+ SetupTranslator(tr,stress_lengths_hi,stress_amps_hi);
+ tr->charset_a0 = charsets[19]; // ISCII
+ tr->langopts.length_mods0 = tr->langopts.length_mods; // don't lengthen vowels in the last syllable
+
+ tr->langopts.stress_rule = 6; // stress on last heaviest syllable, excluding final syllable
+ tr->langopts.stress_flags = 0x10004; // use 'diminished' for unstressed final syllable
+ tr->langopts.numbers = 0x011;
+ tr->langopts.numbers2 = NUM2_100000;
+ tr->letter_bits_offset = OFFSET_DEVANAGARI;
+ SetIndicLetters(tr);
+ }
+ break;
+
+
+ case L('h','r'): // Croatian
+ case L('b','s'): // Bosnian
+ case L('s','r'): // Serbian
+ {
+ static const unsigned char stress_amps_hr[8] = {17,17, 20,20, 20,22, 22,21 };
+ static const short stress_lengths_hr[8] = {180,160, 200,200, 0,0, 220,230};
+ static const short stress_lengths_sr[8] = {160,150, 200,200, 0,0, 250,260};
+
+ if(name2 == L('s','r'))
+ SetupTranslator(tr,stress_lengths_sr,stress_amps_hr);
+ else
+ SetupTranslator(tr,stress_lengths_hr,stress_amps_hr);
+ tr->charset_a0 = charsets[2]; // ISO-8859-2
+
+ tr->langopts.stress_rule = 0;
+ tr->langopts.stress_flags = 0x10;
+ tr->langopts.param[LOPT_REGRESSIVE_VOICING] = 0x3;
+ tr->langopts.max_initial_consonants = 5;
+ tr->langopts.spelling_stress = 1;
+ tr->langopts.accents = 1;
+
+ tr->langopts.numbers = 0x140d + 0x4000 + NUM_ROMAN_UC;
+ tr->langopts.numbers2 = 0x4a; // variant numbers before thousands,milliards
+ tr->langopts.replace_chars = replace_cyrillic_latin;
+
+ SetLetterVowel(tr,'y');
+ SetLetterVowel(tr,'r');
+ }
+ break;
+
+
+ case L('h','u'): // Hungarian
+ {
+ static const unsigned char stress_amps_hu[8] = {17,17, 19,19, 20,22, 22,21 };
+ static const short stress_lengths_hu[8] = {185,195, 195,190, 0,0, 210,220};
+
+ SetupTranslator(tr,stress_lengths_hu,stress_amps_hu);
+ tr->charset_a0 = charsets[2]; // ISO-8859-2
+
+ tr->langopts.vowel_pause = 0x20;
+ tr->langopts.stress_rule = 0;
+ tr->langopts.stress_flags = 0x8036;
+ tr->langopts.unstressed_wd1 = 2;
+// tr->langopts.param[LOPT_REGRESSIVE_VOICING] = 0x4; // don't propagate over word boundaries
+ tr->langopts.param[LOPT_IT_DOUBLING] = 1;
+ tr->langopts.param[LOPT_COMBINE_WORDS] = 99; // combine some prepositions with the following word
+
+ tr->langopts.numbers = 0x1009 + NUM_ROMAN;
+ SetLetterVowel(tr,'y');
+ tr->langopts.spelling_stress = 1;
+SetLengthMods(tr,3); // all equal
+ }
+ break;
+
+ case L('h','y'): // Armenian
+ {
+ static const short stress_lengths_hy[8] = {250, 200, 250, 250, 0, 0, 250, 250};
+ static const char hy_vowels[] = {0x31, 0x35, 0x37, 0x38, 0x3b, 0x48, 0x55, 0};
+ static const char hy_consonants[] = {0x32,0x33,0x34,0x36,0x39,0x3a,0x3c,0x3d,0x3e,0x3f,
+ 0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x49,0x4a,0x4b,0x4c,0x4d,0x4e,0x4f,0x50,0x51,0x52,0x53,0x54,0x56,0};
+
+ SetupTranslator(tr,stress_lengths_hy,NULL);
+ tr->langopts.stress_rule = 3; // default stress on final syllable
+
+ tr->letter_bits_offset = OFFSET_ARMENIAN;
+ memset(tr->letter_bits,0,sizeof(tr->letter_bits));
+ SetLetterBits(tr,LETTERGP_A,hy_vowels);
+ SetLetterBits(tr,LETTERGP_C,hy_consonants);
+ tr->langopts.max_initial_consonants = 6;
+ tr->langopts.numbers = 0x409;
+// tr->langopts.param[LOPT_UNPRONOUNCABLE] = 1; // disable check for unpronouncable words
+ }
+ break;
+
+ case L('i','d'): // Indonesian
+ {
+ static const short stress_lengths_id[8] = {160, 200, 180, 180, 0, 0, 220, 240};
+ static const unsigned char stress_amps_id[8] = {16,18, 18,18, 20,22, 22,21 };
+
+ SetupTranslator(tr,stress_lengths_id,stress_amps_id);
+ tr->langopts.stress_rule = 2;
+ tr->langopts.numbers = 0x1009 + NUM_ROMAN;
+ tr->langopts.stress_flags = 0x6 | 0x10;
+ tr->langopts.accents = 2; // "capital" after letter name
+ }
+ break;
+
+ case L('i','s'): // Icelandic
+ {
+ static const short stress_lengths_is[8] = {180,160, 200,200, 0,0, 240,250};
+ static const wchar_t is_lettergroup_B[] = {'c','f','h','k','p','t','x',0xfe,0}; // voiceless conants, including 'þ' ?? 's'
+
+ SetupTranslator(tr,stress_lengths_is,NULL);
+ tr->langopts.stress_rule = 0;
+ tr->langopts.stress_flags = 0x10;
+ tr->langopts.param[LOPT_IT_LENGTHEN] = 0x11; // remove lengthen indicator from unstressed vowels
+ tr->langopts.param[LOPT_REDUCE] = 2;
+
+ ResetLetterBits(tr,0x18);
+ SetLetterBits(tr,4,"kpst"); // Letter group F
+ SetLetterBits(tr,3,"jvr"); // Letter group H
+ tr->letter_groups[1] = is_lettergroup_B;
+ SetLetterVowel(tr,'y');
+ tr->langopts.numbers = 0x8e9;
+ tr->langopts.numbers2 = 0x2;
+ }
+ break;
+
+ case L('i','t'): // Italian
+ {
+ static const short stress_lengths_it[8] = {150, 140, 170, 170, 0, 0, 300, 330};
+ static const unsigned char stress_amps_it[8] = {15,14, 19,19, 20,22, 22,20 };
+
+ SetupTranslator(tr,stress_lengths_it,stress_amps_it);
+
+ tr->langopts.length_mods0 = tr->langopts.length_mods; // don't lengthen vowels in the last syllable
+ tr->langopts.stress_rule = 2;
+ tr->langopts.vowel_pause = 1;
+ tr->langopts.unstressed_wd1 = 2;
+ tr->langopts.unstressed_wd2 = 2;
+ tr->langopts.param[LOPT_IT_LENGTHEN] = 2; // remove lengthen indicator from unstressed or non-penultimate syllables
+ tr->langopts.param[LOPT_IT_DOUBLING] = 2; // double the first consonant if the previous word ends in a stressed vowel
+ tr->langopts.param[LOPT_SONORANT_MIN] = 130; // limit the shortening of sonorants before short vowels
+ tr->langopts.param[LOPT_REDUCE] = 1; // reduce vowels even if phonemes are specified in it_list
+ tr->langopts.numbers = 0x2709 + NUM_ROMAN;
+ tr->langopts.accents = 2; // Say "Capital" after the letter.
+ }
+ break;
+
+ case L_jbo: // Lojban
+ {
+ static const short stress_lengths_jbo[8] = {145,145, 170,160, 0,0, 330,350};
+ static const wchar_t jbo_punct_within_word[] = {'.',',','\'',0x2c8,0}; // allow period and comma within a word, also stress marker (from LOPT_SYLLABLE_CAPS)
+
+ SetupTranslator(tr,stress_lengths_jbo,NULL);
+ tr->langopts.stress_rule = 2;
+ tr->langopts.vowel_pause = 0x20c; // pause before a word which starts with a vowel, or after a word which ends in a consonant
+// tr->langopts.word_gap = 1;
+ tr->punct_within_word = jbo_punct_within_word;
+ tr->langopts.param[LOPT_SYLLABLE_CAPS] = 1; // capitals indicate stressed syllables
+ SetLetterVowel(tr,'y');
+ }
+ break;
+
+ case L('k','o'): // Korean, TEST
+ {
+ static const char ko_ivowels[] = {0x63,0x64,0x67,0x68,0x6d,0x72,0x74,0x75,0}; // y and i vowels
+ static const unsigned char ko_voiced[] = {0x02,0x05,0x06,0xab,0xaf,0xb7,0xbc,0}; // voiced consonants, l,m,n,N
+
+ tr->letter_bits_offset = OFFSET_KOREAN;
+ memset(tr->letter_bits,0,sizeof(tr->letter_bits));
+ SetLetterBitsRange(tr,LETTERGP_A,0x61,0x75);
+ SetLetterBits(tr,LETTERGP_Y,ko_ivowels);
+ SetLetterBits(tr,LETTERGP_G,(const char *)ko_voiced);
+
+ tr->langopts.stress_rule = 8; // ?? 1st syllable if it is heavy, else 2nd syllable
+ tr->langopts.param[LOPT_UNPRONOUNCABLE] = 1; // disable check for unpronouncable words
+ tr->langopts.numbers = 0x0401;
+ }
+ break;
+
+ case L('k','u'): // Kurdish
+ {
+ static const unsigned char stress_amps_ku[8] = {18,18, 20,20, 20,22, 22,21 };
+ static const short stress_lengths_ku[8] = {180,180, 190,180, 0,0, 230,240};
+
+ SetupTranslator(tr,stress_lengths_ku,stress_amps_ku);
+ tr->charset_a0 = charsets[9]; // ISO-8859-9 - Latin5
+
+ tr->langopts.stress_rule = 7; // stress on the last syllable, before any explicitly unstressed syllable
+
+ tr->langopts.numbers = 0x100461;
+ tr->langopts.max_initial_consonants = 2;
+ }
+ break;
+
+ case L('l','a'): //Latin
+ {
+ tr->charset_a0 = charsets[4]; // ISO-8859-4, includes a,e,i,o,u-macron
+ tr->langopts.stress_rule = 2;
+ tr->langopts.stress_flags = 0x20;
+ tr->langopts.unstressed_wd1 = 0;
+ tr->langopts.unstressed_wd2 = 2;
+ tr->langopts.param[LOPT_DIERESES] = 1;
+ tr->langopts.numbers = 0x1 + NUM_ROMAN;
+ tr->langopts.max_roman = 5000;
+ }
+ break;
+
+ case L('l','v'): // latvian
+ {
+ static const unsigned char stress_amps_lv[8] = {17,13, 20,20, 20,22, 22,21 };
+ static const short stress_lengths_lv[8] = {180,130, 210,210, 0,0, 210,210};
+
+ SetupTranslator(tr,stress_lengths_lv,stress_amps_lv);
+
+ tr->langopts.stress_rule = 0;
+ tr->langopts.spelling_stress = 1;
+ tr->charset_a0 = charsets[4]; // ISO-8859-4
+ tr->langopts.numbers = 0x409 + 0x8000 + 0x10000;
+ tr->langopts.stress_flags = 0x16 + 0x40000;
+ }
+ break;
+
+ case L('m','k'): // Macedonian
+ {
+ static wchar_t vowels_cyrillic[] = {0x440, // also include 'Ñ€' [R]
+ 0x430,0x435,0x438,0x439,0x43e,0x443,0x44b,0x44d,0x44e,0x44f,0x450,0x451,0x456,0x457,0x45d,0x45e,0};
+ static const unsigned char stress_amps_mk[8] = {17,17, 20,20, 20,22, 22,21 };
+ static const short stress_lengths_mk[8] = {180,160, 200,200, 0,0, 220,230};
+
+ SetupTranslator(tr,stress_lengths_mk,stress_amps_mk);
+ tr->charset_a0 = charsets[5]; // ISO-8859-5
+ tr->letter_groups[0] = vowels_cyrillic;
+
+ tr->langopts.stress_rule = 4; // antipenultimate
+ tr->langopts.numbers = 0x0429 + 0x4000;
+ tr->langopts.numbers2 = 0x8a; // variant numbers before thousands,milliards
+ }
+ break;
+
+
+ case L('n','l'): // Dutch
+ {
+ static const short stress_lengths_nl[8] = {160,135, 210,210, 0, 0, 260,280};
+
+ tr->langopts.stress_rule = 0;
+ tr->langopts.vowel_pause = 1;
+ tr->langopts.param[LOPT_DIERESES] = 1;
+ tr->langopts.param[LOPT_PREFIXES] = 1;
+ SetLetterVowel(tr,'y');
+
+ tr->langopts.numbers = 0x11c19;
+ memcpy(tr->stress_lengths,stress_lengths_nl,sizeof(tr->stress_lengths));
+ }
+ break;
+
+ case L('n','o'): // Norwegian
+ {
+ static const short stress_lengths_no[8] = {160,140, 200,200, 0,0, 220,210};
+
+ SetupTranslator(tr,stress_lengths_no,NULL);
+ tr->langopts.stress_rule = 0;
+ SetLetterVowel(tr,'y');
+ tr->langopts.numbers = 0x11849;
+ }
+ break;
+
+ case L('o','m'):
+ {
+ static const unsigned char stress_amps_om[] = {18,15, 20,20, 20,22, 22,22 };
+ static const short stress_lengths_om[8] = {200,200, 200,200, 0,0, 200,200};
+
+ SetupTranslator(tr,stress_lengths_om,stress_amps_om);
+ tr->langopts.stress_rule = 2;
+ tr->langopts.stress_flags = 0x16 + 0x80000;
+ }
+ break;
+
+ case L('p','l'): // Polish
+ {
+ static const short stress_lengths_pl[8] = {160, 190, 175, 175, 0, 0, 200, 210};
+ static const unsigned char stress_amps_pl[8] = {17,13, 19,19, 20,22, 22,21 }; // 'diminished' is used to mark a quieter, final unstressed syllable
+
+ SetupTranslator(tr,stress_lengths_pl,stress_amps_pl);
+
+ tr->charset_a0 = charsets[2]; // ISO-8859-2
+ tr->langopts.stress_rule = 2;
+ tr->langopts.stress_flags = 0x6; // mark unstressed final syllables as diminished
+ tr->langopts.param[LOPT_REGRESSIVE_VOICING] = 0x8;
+ tr->langopts.max_initial_consonants = 7; // for example: wchrzczony :)
+ tr->langopts.numbers=0x1009 + 0x4000;
+ tr->langopts.numbers2=0x40;
+ tr->langopts.param[LOPT_COMBINE_WORDS] = 4 + 0x100; // combine 'nie' (marked with $alt2) with some 1-syllable (and 2-syllable) words (marked with $alt)
+ SetLetterVowel(tr,'y');
+ }
+ break;
+
+ case L('p','t'): // Portuguese
+ {
+ static const short stress_lengths_pt[8] = {180, 125, 210, 210, 0, 0, 270, 295};
+ static const unsigned char stress_amps_pt[8] = {16,13, 19,19, 20,22, 22,21 }; // 'diminished' is used to mark a quieter, final unstressed syllable
+
+ SetupTranslator(tr,stress_lengths_pt,stress_amps_pt);
+ tr->langopts.length_mods0 = tr->langopts.length_mods; // don't lengthen vowels in the last syllable
+
+ tr->langopts.stress_rule = 3; // stress on final syllable
+ tr->langopts.stress_flags = 0x6 | 0x10 | 0x20000;
+ tr->langopts.numbers = 0x269 + 0x4000 + NUM_ROMAN;
+ SetLetterVowel(tr,'y');
+ ResetLetterBits(tr,0x2);
+ SetLetterBits(tr,1,"bcdfgjkmnpqstvxz"); // B hard consonants, excluding h,l,r,w,y
+ }
+ break;
+
+ case L('r','o'): // Romanian
+ {
+ static const short stress_lengths_ro[8] = {170, 170, 180, 180, 0, 0, 240, 260};
+ static const unsigned char stress_amps_ro[8] = {15,13, 18,18, 20,22, 22,21 };
+
+ SetupTranslator(tr,stress_lengths_ro,stress_amps_ro);
+
+ tr->langopts.stress_rule = 2;
+ tr->langopts.stress_flags = 0x100 + 0x6;
+
+ tr->charset_a0 = charsets[2]; // ISO-8859-2
+ tr->langopts.numbers = 0x1029+0x6000 + NUM_ROMAN;
+ tr->langopts.numbers2 = 0x1e; // variant numbers before all thousandplex
+ }
+ break;
+
+ case L('r','u'): // Russian
+ Translator_Russian(tr);
+ break;
+
+ case L('r','w'): // Kiryarwanda
+ {
+ tr->langopts.stress_rule = 2;
+ tr->langopts.stress_flags = 0x16;
+ tr->langopts.length_mods0 = tr->langopts.length_mods; // don't lengthen vowels in the last syllable
+
+ tr->langopts.numbers = 0x61 + 0x100000 + 0x4000;
+ tr->langopts.numbers2 = 0x200; // say "thousands" before its number
+ }
+ break;
+
+ case L('s','k'): // Slovak
+ case L('c','s'): // Czech
+ {
+ static const char *sk_voiced = "bdgjlmnrvwzaeiouy";
+
+ SetupTranslator(tr,stress_lengths_sk,stress_amps_sk);
+ tr->charset_a0 = charsets[2]; // ISO-8859-2
+
+ tr->langopts.stress_rule = 0;
+ tr->langopts.stress_flags = 0x16;
+ tr->langopts.param[LOPT_REGRESSIVE_VOICING] = 0x3;
+ tr->langopts.max_initial_consonants = 5;
+ tr->langopts.spelling_stress = 1;
+ tr->langopts.param[LOPT_COMBINE_WORDS] = 4; // combine some prepositions with the following word
+
+ tr->langopts.numbers = 0x0401 + 0x4000 + NUM_ROMAN;
+ tr->langopts.numbers2 = 0x100;
+ tr->langopts.thousands_sep = 0; //no thousands separator
+ tr->langopts.decimal_sep = ',';
+
+ if(name2 == L('c','s'))
+ {
+ tr->langopts.numbers2 = 0x108; // variant numbers before milliards
+ }
+
+ SetLetterVowel(tr,'y');
+ SetLetterVowel(tr,'r');
+ ResetLetterBits(tr,0x20);
+ SetLetterBits(tr,5,sk_voiced);
+ }
+ break;
+
+ case L('s','q'): // Albanian
+ {
+ static const short stress_lengths_sq[8] = {150, 150, 180, 180, 0, 0, 300, 300};
+ static const unsigned char stress_amps_sq[8] = {16,12, 16,16, 20,20, 21,19 };
+
+ SetupTranslator(tr,stress_lengths_sq,stress_amps_sq);
+
+ tr->langopts.stress_rule = 2;
+ tr->langopts.stress_flags = 0x16 + 0x100;
+ SetLetterVowel(tr,'y');
+ tr->langopts.numbers = 0x69 + 0x8000;
+ tr->langopts.accents = 2; // "capital" after letter name
+ }
+ break;
+
+
+ case L('s','v'): // Swedish
+ {
+ static const unsigned char stress_amps_sv[] = {16,16, 20,20, 20,22, 22,21 };
+ static const short stress_lengths_sv[8] = {160,135, 220,220, 0,0, 250,280};
+ SetupTranslator(tr,stress_lengths_sv,stress_amps_sv);
+
+ tr->langopts.stress_rule = 0;
+ SetLetterVowel(tr,'y');
+ tr->langopts.numbers = 0x1909;
+ tr->langopts.accents = 1;
+ }
+ break;
+
+ case L('s','w'): // Swahili
+ {
+ static const short stress_lengths_sw[8] = {160, 170, 200, 200, 0, 0, 320, 340};
+ static const unsigned char stress_amps_sw[] = {16,12, 19,19, 20,22, 22,21 };
+
+ SetupTranslator(tr,stress_lengths_sw,stress_amps_sw);
+ tr->langopts.length_mods0 = tr->langopts.length_mods; // don't lengthen vowels in the last syllable
+
+ tr->langopts.vowel_pause = 1;
+ tr->langopts.stress_rule = 2;
+ tr->langopts.stress_flags = 0x6 | 0x10;
+
+ tr->langopts.numbers = 0x4e1;
+ tr->langopts.numbers2 = NUM2_100000a;
+ }
+ break;
+
+ case L('t','a'): // Tamil
+ case L('m','l'): // Malayalam
+ case L('k','n'): // Kannada
+ case L('m','r'): // Marathi
+ {
+ static const short stress_lengths_ta[8] = {200, 200, 210, 210, 0, 0, 230, 230};
+ static const unsigned char stress_amps_ta[8] = {18,18, 18,18, 20,20, 22,22 };
+
+ SetupTranslator(tr,stress_lengths_ta,stress_amps_ta);
+ tr->langopts.length_mods0 = tr->langopts.length_mods; // don't lengthen vowels in the last syllable
+
+ tr->langopts.stress_rule = 0;
+ tr->langopts.stress_flags = 0x10004; // use 'diminished' for unstressed final syllable
+ tr->letter_bits_offset = OFFSET_TAMIL;
+
+ if(name2 == L('m','r'))
+ {
+ tr->letter_bits_offset = OFFSET_DEVANAGARI;
+ }
+ else
+ if(name2 == L('m','l'))
+ {
+ tr->letter_bits_offset = OFFSET_MALAYALAM;
+ }
+ else
+ if(name2 == L('k','n'))
+ {
+ tr->letter_bits_offset = OFFSET_KANNADA;
+ tr->langopts.numbers = 0x1;
+ tr->langopts.numbers2 = NUM2_100000;
+ }
+ tr->langopts.param[LOPT_WORD_MERGE] = 1; // don't break vowels betwen words
+ SetIndicLetters(tr); // call this after setting OFFSET_
+ }
+ break;
+
+#ifdef deleted
+ case L('t','h'): // Thai
+ {
+ static const short stress_lengths_th[8] = {230,150, 230,230, 230,0, 230,250};
+ static const unsigned char stress_amps_th[] = {22,16, 22,22, 22,22, 22,22 };
+
+ SetupTranslator(tr,stress_lengths_th,stress_amps_th);
+
+ tr->langopts.stress_rule = 0; // stress on final syllable of a "word"
+ tr->langopts.stress_flags = 1; // don't automatically set diminished stress (may be set in the intonation module)
+ tr->langopts.tone_language = 1; // Tone language, use CalcPitches_Tone() rather than CalcPitches()
+ tr->langopts.length_mods0 = tr->langopts.length_mods; // don't lengthen vowels in the last syllable
+// tr->langopts.tone_numbers = 1; // a number after letters indicates a tone number (eg. pinyin or jyutping)
+ tr->langopts.word_gap = 0x21; // length of a final vowel is less dependent on the next consonant, don't merge consonant with next word
+ }
+ break;
+#endif
+
+ case L('t','r'): // Turkish
+ {
+ static const unsigned char stress_amps_tr[8] = {18,18, 20,20, 20,22, 22,21 };
+ static const short stress_lengths_tr[8] = {190,190, 190,190, 0,0, 250,270};
+
+ SetupTranslator(tr,stress_lengths_tr,stress_amps_tr);
+ tr->charset_a0 = charsets[9]; // ISO-8859-9 - Latin5
+
+ tr->langopts.stress_rule = 7; // stress on the last syllable, before any explicitly unstressed syllable
+ tr->langopts.stress_flags = 0x20; //no automatic secondary stress
+
+ tr->langopts.numbers = 0x1509 + 0x4000;
+ tr->langopts.max_initial_consonants = 2;
+ }
+ break;
+
+ case L('v','i'): // Vietnamese
+ {
+ static const short stress_lengths_vi[8] = {150, 150, 180, 180, 210, 230, 230, 240};
+ static const unsigned char stress_amps_vi[] = {16,16, 16,16, 22,22, 22,22 };
+ static wchar_t vowels_vi[] = {
+ 0x61, 0xe0, 0xe1, 0x1ea3, 0xe3, 0x1ea1, // a
+ 0x103, 0x1eb1, 0x1eaf, 0x1eb3, 0x1eb5, 0x1eb7, // ă
+ 0xe2, 0x1ea7, 0x1ea5, 0x1ea9, 0x1eab, 0x1ead, // â
+ 0x65, 0xe8, 0xe9, 0x1ebb, 0x1ebd, 0x1eb9, // e
+ 0xea, 0x1ec1, 0x1ebf, 0x1ec3, 0x1ec5, 0x1ec7, // i
+ 0x69, 0xec, 0xed, 0x1ec9, 0x129, 0x1ecb, // i
+ 0x6f, 0xf2, 0xf3, 0x1ecf, 0xf5, 0x1ecd, // o
+ 0xf4, 0x1ed3, 0x1ed1, 0x1ed5, 0x1ed7, 0x1ed9, // ô
+ 0x1a1, 0x1edd, 0x1edb, 0x1edf, 0x1ee1, 0x1ee3, // Æ¡
+ 0x75, 0xf9, 0xfa, 0x1ee7, 0x169, 0x1ee5, // u
+ 0x1b0, 0x1eeb, 0x1ee9, 0x1eed, 0x1eef, 0x1ef1, // ư
+ 0x79, 0x1ef3, 0xfd, 0x1ef7, 0x1ef9, 0x1e, 0 }; // y
+
+ SetupTranslator(tr,stress_lengths_vi,stress_amps_vi);
+ tr->langopts.length_mods0 = tr->langopts.length_mods; // don't lengthen vowels in the last syllable
+
+ tr->langopts.stress_rule = 0;
+ tr->langopts.word_gap = 0x21; // length of a final vowel is less dependent on the next consonant, don't merge consonant with next word
+// tr->langopts.vowel_pause = 4;
+ tr->letter_groups[0] = vowels_vi;
+ tr->langopts.tone_language = 1; // Tone language, use CalcPitches_Tone() rather than CalcPitches()
+ tr->langopts.unstressed_wd1 = 2;
+ tr->langopts.numbers = 0x0049 + 0x8000;
+
+ }
+ break;
+
+ case L('z','h'):
+ case L_zhy:
+ {
+ static const short stress_lengths_zh[8] = {230,150, 230,230, 230,0, 240,250}; // 1=tone5. end-of-sentence, 6=tone 1&4, 7=tone 2&3
+ static const unsigned char stress_amps_zh[] = {22,16, 22,22, 22,22, 22,22 };
+
+ SetupTranslator(tr,stress_lengths_zh,stress_amps_zh);
+
+ tr->langopts.stress_rule = 3; // stress on final syllable of a "word"
+ tr->langopts.stress_flags = 1; // don't automatically set diminished stress (may be set in the intonation module)
+ tr->langopts.vowel_pause = 0;
+ tr->langopts.tone_language = 1; // Tone language, use CalcPitches_Tone() rather than CalcPitches()
+ tr->langopts.length_mods0 = tr->langopts.length_mods; // don't lengthen vowels in the last syllable
+ tr->langopts.tone_numbers = 1; // a number after letters indicates a tone number (eg. pinyin or jyutping)
+ tr->langopts.ideographs = 1;
+ tr->langopts.word_gap = 0x21; // length of a final vowel is less dependent on the next consonant, don't merge consonant with next word
+ if(name2 == L('z','h'))
+ {
+ tr->langopts.textmode = 1;
+ tr->langopts.listx = 1; // compile zh_listx after zh_list
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ tr->translator_name = name2;
+
+ if(tr->langopts.numbers & 0x8)
+ {
+ // use . and ; for thousands and decimal separators
+ tr->langopts.thousands_sep = '.';
+ tr->langopts.decimal_sep = ',';
+ }
+ if(tr->langopts.numbers & 0x4)
+ {
+ tr->langopts.thousands_sep = 0; // don't allow thousands separator, except space
+ }
+ return(tr);
+} // end of SelectTranslator
+
+
+
+//**********************************************************************************************************
+
+
+
+static void Translator_Russian(Translator *tr)
+{//===========================================
+ static const unsigned char stress_amps_ru[] = {16,16, 18,18, 20,24, 24,22 };
+ static const short stress_lengths_ru[8] = {150,140, 220,220, 0,0, 260,280};
+
+
+ // character codes offset by 0x420
+ static const char ru_vowels[] = {0x10,0x15,0x31,0x18,0x1e,0x23,0x2b,0x2d,0x2e,0x2f,0};
+ static const char ru_consonants[] = {0x11,0x12,0x13,0x14,0x16,0x17,0x19,0x1a,0x1b,0x1c,0x1d,0x1f,0x20,0x21,0x22,0x24,0x25,0x26,0x27,0x28,0x29,0x2a,0x2c,0};
+ static const char ru_soft[] = {0x2c,0x19,0x27,0x29,0}; // letter group B [k ts; s;]
+ static const char ru_hard[] = {0x2a,0x16,0x26,0x28,0}; // letter group H [S Z ts]
+ static const char ru_nothard[] = {0x11,0x12,0x13,0x14,0x17,0x19,0x1a,0x1b,0x1c,0x1d,0x1f,0x20,0x21,0x22,0x24,0x25,0x27,0x29,0x2c,0};
+ static const char ru_voiced[] = {0x11,0x12,0x13,0x14,0x16,0x17,0}; // letter group G (voiced obstruents)
+ static const char ru_ivowels[] = {0x2c,0x15,0x31,0x18,0x2e,0x2f,0}; // letter group Y (iotated vowels & soft-sign)
+
+ SetupTranslator(tr,stress_lengths_ru,stress_amps_ru);
+
+ tr->charset_a0 = charsets[18]; // KOI8-R
+ tr->transpose_offset = 0x42f; // convert cyrillic from unicode into range 0x01 to 0x22
+ tr->transpose_min = 0x430;
+ tr->transpose_max = 0x451;
+
+ tr->letter_bits_offset = OFFSET_CYRILLIC;
+ memset(tr->letter_bits,0,sizeof(tr->letter_bits));
+ SetLetterBits(tr,0,ru_vowels);
+ SetLetterBits(tr,1,ru_soft);
+ SetLetterBits(tr,2,ru_consonants);
+ SetLetterBits(tr,3,ru_hard);
+ SetLetterBits(tr,4,ru_nothard);
+ SetLetterBits(tr,5,ru_voiced);
+ SetLetterBits(tr,6,ru_ivowels);
+ SetLetterBits(tr,7,ru_vowels);
+
+ tr->langopts.param[LOPT_UNPRONOUNCABLE] = 0x432; // [v] don't count this character at start of word
+ tr->langopts.param[LOPT_REGRESSIVE_VOICING] = 1;
+ tr->langopts.param[LOPT_REDUCE] = 2;
+ tr->langopts.stress_rule = 5;
+ tr->langopts.stress_flags = 0x0020; // waas 0x1010
+
+ tr->langopts.numbers = 0x0409;
+ tr->langopts.numbers2 = 0xc2; // variant numbers before thousands
+ tr->langopts.phoneme_change = 1;
+ tr->langopts.testing = 2;
+
+} // end of Translator_Russian
+
+
+
+/*
+typedef struct {
+ int flags;
+ unsigned char stress; // stress level of this vowel
+ unsigned char stress_highest; // the highest stress level of a vowel in this word
+ unsigned char n_vowels; // number of vowels in the word
+ unsigned char vowel_this; // syllable number of this vowel (counting from 1)
+ unsigned char vowel_stressed; // syllable number of the highest stressed vowel
+} CHANGEPH;
+*/
+
+
+#define RUSSIAN2
+#ifdef RUSSIAN2
+
+int ChangePhonemes_ru(Translator *tr, PHONEME_LIST2 *phlist, int n_ph, int index, PHONEME_TAB *ph, CHANGEPH *ch)
+{//=============================================================================================================
+// Called for each phoneme in the phoneme list, to allow a language to make changes
+// ph The current phoneme
+
+ int variant;
+ int vowelix;
+ PHONEME_TAB *prev, *next;
+
+ if(ch->flags & 8)
+ return(0); // full phoneme translation has already been given
+ // Russian vowel softening and reduction rules
+
+ if(ph->type == phVOWEL)
+ {
+ int prestressed = ch->vowel_stressed==ch->vowel_this+1; // the next vowel after this has the main stress
+
+ #define N_VOWELS_RU 11
+ static unsigned int vowels_ru[N_VOWELS_RU] = {'a','V','O','I',PH('I','#'),PH('E','#'),PH('E','2'),
+PH('V','#'),PH('I','3'),PH('I','2'),PH('E','3')};
+
+
+ static unsigned int vowel_replace[N_VOWELS_RU][6] = {
+ // stressed, soft, soft-stressed, j+stressed, j+soft, j+soft-stressed
+ /*0*/ {'A', 'I', PH('j','a'), 'a', 'a', 'a'}, // a Uses 3,4,5 columns.
+ /*1*/ {'A', 'V', PH('j','a'), 'a', 'V', 'a'}, // V Uses 3,4,5 columns.
+ /*2*/ {'o', '8', '8', 'o', '8', '8'}, // O
+ /*3*/ {'i', 'I', 'i', 'a', 'I', 'a'}, // I Uses 3,4,5 columns.
+ /*4*/ {'i', PH('I','#'), 'i', 'i', PH('I','#'), 'i'}, // I#
+ /*5*/ {'E', PH('E','#'), 'E', 'e', PH('E','#'), 'e'}, // E#
+ /*6*/ {'E', PH('E','2'), 'E', 'e', PH('E','2'), 'e'}, // E2 Uses 3,4,5 columns.
+ /*7*/ {PH('j','a'), 'V', PH('j','a'), 'A', 'V', 'A'}, // V#
+ /*8*/ {PH('j','a'), 'I', PH('j','a'), 'e', 'I', 'e'}, // I3 Uses 3,4,5 columns.
+ /*9*/ {'e', 'I', 'e', 'e', 'I', 'e'}, // I2
+ /*10*/ {'e', PH('E', '2'), 'e', 'e', PH('E','2'), 'e'} // E3
+ };
+
+ prev = phoneme_tab[phlist[index-1].phcode];
+ next = phoneme_tab[phlist[index+1].phcode];
+
+ // lookup the vowel name to get an index into the vowel_replace[] table
+ for(vowelix=0; vowelix<N_VOWELS_RU; vowelix++)
+ {
+ if(vowels_ru[vowelix] == ph->mnemonic)
+ break;
+ }
+ if(vowelix == N_VOWELS_RU)
+ return(0);
+
+ if(prestressed)
+ {
+ if((vowelix==6)&&(prev->mnemonic=='j'))
+ vowelix=8;
+ if(vowelix==1)
+ vowelix=0;
+ if(vowelix==4)
+ vowelix=3;
+ if(vowelix==6)
+ vowelix=5;
+ if(vowelix==7)
+ vowelix=8;
+ if(vowelix==10)
+ vowelix=9;
+ }
+ // do we need a variant of this vowel, depending on the stress and adjacent phonemes ?
+ variant = -1;
+ int stressed = ch->flags & 2;
+ int soft=prev->phflags & phPALATAL;
+
+ if (soft && stressed)
+ variant = 2; else
+ if (stressed)
+ variant = 0; else
+ if (soft)
+ variant = 1;
+ if(variant >= 0)
+ {
+ if(prev->mnemonic == 'j')
+ variant += 3;
+
+ phlist[index].phcode = PhonemeCode(vowel_replace[vowelix][variant]);
+ }
+ else
+ {
+ phlist[index].phcode = PhonemeCode(vowels_ru[vowelix]);
+ }
+ }
+
+ return(0);
+}
+#else
+
+
+int ChangePhonemes_ru(Translator *tr, PHONEME_LIST2 *phlist, int n_ph, int index, PHONEME_TAB *ph, CHANGEPH *ch)
+{//=============================================================================================================
+// Called for each phoneme in the phoneme list, to allow a language to make changes
+// flags: bit 0=1 last phoneme in a word
+// bit 1=1 this is the highest stressed vowel in the current word
+// bit 2=1 after the highest stressed vowel in the current word
+// bit 3=1 the phonemes were specified explicitly, or found from an entry in the xx_list dictionary
+// ph The current phoneme
+
+ int variant;
+ int vowelix;
+ PHONEME_TAB *prev, *next;
+
+ if(ch->flags & 8)
+ return(0); // full phoneme translation has already been given
+
+ // Russian vowel softening and reduction rules
+ if(ph->type == phVOWEL)
+ {
+ #define N_VOWELS_RU 7
+ static unsigned char vowels_ru[N_VOWELS_RU] = {'a','A','o','E','i','u','y'};
+
+ // each line gives: soft, reduced, soft-reduced, post-tonic
+ static unsigned short vowel_replace[N_VOWELS_RU][4] = {
+ {'&', 'V', 'I', 'V'}, // a
+ {'&', 'V', 'I', 'V'}, // A
+ {'8', 'V', 'I', 'V'}, // o
+ {'e', 'I', 'I', 'I'}, // E
+ {'i', 'I', 'I', 'I'}, // i
+ {'u'+('"'<<8), 'U', 'U', 'U'}, // u
+ {'y', 'Y', 'Y', 'Y'}}; // y
+
+ prev = phoneme_tab[phlist[index-1].phcode];
+ next = phoneme_tab[phlist[index+1].phcode];
+
+if(prev->mnemonic == 'j')
+ return(0);
+
+ // lookup the vowel name to get an index into the vowel_replace[] table
+ for(vowelix=0; vowelix<N_VOWELS_RU; vowelix++)
+ {
+ if(vowels_ru[vowelix] == ph->mnemonic)
+ break;
+ }
+ if(vowelix == N_VOWELS_RU)
+ return(0);
+
+ // do we need a variant of this vowel, depending on the stress and adjacent phonemes ?
+ variant = -1;
+ if(ch->flags & 2)
+ {
+ // a stressed vowel
+ if((prev->phflags & phPALATAL) && ((next->phflags & phPALATAL) || phoneme_tab[phlist[index+2].phcode]->mnemonic == ';'))
+ {
+ // between two palatal consonants, use the soft variant
+ variant = 0;
+ }
+ }
+ else
+ {
+ // an unstressed vowel
+ if(prev->phflags & phPALATAL)
+ {
+ variant = 2; // unstressed soft
+ }
+ else
+ if((ph->mnemonic == 'o') && ((prev->phflags & phPLACE) == phPLACE_pla))
+ {
+ variant = 2; // unstressed soft ([o] vowel following: ш ж
+ }
+ else
+ if(ch->flags & 4)
+ {
+ variant = 3; // post tonic
+ }
+ else
+ {
+ variant = 1; // unstressed
+ }
+ }
+ if(variant >= 0)
+ {
+ phlist[index].phcode = PhonemeCode(vowel_replace[vowelix][variant]);
+ }
+ }
+
+ return(0);
+}
+#endif
+
diff --git a/Plugins/eSpeak/eSpeak/translate.cpp b/Plugins/eSpeak/eSpeak/translate.cpp
new file mode 100644
index 0000000..69259fc
--- /dev/null
+++ b/Plugins/eSpeak/eSpeak/translate.cpp
@@ -0,0 +1,2771 @@
+/***************************************************************************
+ * Copyright (C) 2005 to 2007 by Jonathan Duddington *
+ * email: jonsd@users.sourceforge.net *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 3 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, see: *
+ * <http://www.gnu.org/licenses/>. *
+ ***************************************************************************/
+
+#include "StdAfx.h"
+
+#include <stdio.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <wctype.h>
+#include <wchar.h>
+
+#include "speak_lib.h"
+#include "speech.h"
+#include "phoneme.h"
+#include "synthesize.h"
+#include "voice.h"
+#include "translate.h"
+
+#define WORD_STRESS_CHAR '*'
+
+
+Translator *translator = NULL; // the main translator
+Translator *translator2 = NULL; // secondary translator for certain words
+static char translator2_language[20] = {0};
+
+FILE *f_trans = NULL; // phoneme output text
+int option_tone2 = 0;
+int option_tone_flags = 0; // bit 8=emphasize allcaps, bit 9=emphasize penultimate stress
+int option_phonemes = 0;
+int option_phoneme_events = 0;
+int option_quiet = 0;
+int option_endpause = 0; // suppress pause after end of text
+int option_capitals = 0;
+int option_punctuation = 0;
+int option_sayas = 0;
+static int option_sayas2 = 0; // used in translate_clause()
+static int option_emphasis = 0; // 0=normal, 1=normal, 2=weak, 3=moderate, 4=strong
+int option_ssml = 0;
+int option_phoneme_input = 0; // allow [[phonemes]] in input
+int option_phoneme_variants = 0; // 0= don't display phoneme variant mnemonics
+int option_wordgap = 0;
+
+static int count_sayas_digits;
+int skip_sentences;
+int skip_words;
+int skip_characters;
+char skip_marker[N_MARKER_LENGTH];
+int skipping_text; // waiting until word count, sentence count, or named marker is reached
+int end_character_position;
+int count_sentences;
+int count_words;
+int clause_start_char;
+int clause_start_word;
+int new_sentence;
+static int word_emphasis = 0; // set if emphasis level 3 or 4
+
+static int prev_clause_pause=0;
+static int max_clause_pause = 0;
+
+
+// these were previously in translator class
+char word_phonemes[N_WORD_PHONEMES]; // a word translated into phoneme codes
+int n_ph_list2;
+PHONEME_LIST2 ph_list2[N_PHONEME_LIST]; // first stage of text->phonemes
+
+
+
+wchar_t option_punctlist[N_PUNCTLIST]={0};
+char ctrl_embedded = '\001'; // to allow an alternative CTRL for embedded commands
+int option_multibyte=espeakCHARS_AUTO; // 0=auto, 1=utf8, 2=8bit, 3=wchar
+
+// these are overridden by defaults set in the "speak" file
+int option_linelength = 0;
+
+#define N_EMBEDDED_LIST 250
+static int embedded_ix;
+static int embedded_read;
+unsigned int embedded_list[N_EMBEDDED_LIST];
+
+// the source text of a single clause (UTF8 bytes)
+#define N_TR_SOURCE 700
+static char source[N_TR_SOURCE+40]; // extra space for embedded command & voice change info at end
+
+int n_replace_phonemes;
+REPLACE_PHONEMES replace_phonemes[N_REPLACE_PHONEMES];
+
+
+// brackets, also 0x2014 to 0x021f which don't need to be in this list
+static const unsigned short brackets[] = {
+'(',')','[',']','{','}','<','>','"','\'','`',
+0xab,0xbb, // double angle brackets
+0x300a,0x300b, // double angle brackets (ideograph)
+0};
+
+// other characters which break a word, but don't produce a pause
+static const unsigned short breaks[] = {'_', 0};
+
+// treat these characters as spaces, in addition to iswspace()
+static const wchar_t chars_space[] = {0x2500,0}; // box drawing horiz
+
+
+// Translate character codes 0xA0 to 0xFF into their unicode values
+// ISO_8859_1 is set as default
+static const unsigned short ISO_8859_1[0x60] = {
+ 0x00a0, 0x00a1, 0x00a2, 0x00a3, 0x00a4, 0x00a5, 0x00a6, 0x00a7, // a0
+ 0x00a8, 0x00a9, 0x00aa, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x00af, // a8
+ 0x00b0, 0x00b1, 0x00b2, 0x00b3, 0x00b4, 0x00b5, 0x00b6, 0x00b7, // b0
+ 0x00b8, 0x00b9, 0x00ba, 0x00bb, 0x00bc, 0x00bd, 0x00be, 0x00bf, // b8
+ 0x00c0, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x00c7, // c0
+ 0x00c8, 0x00c9, 0x00ca, 0x00cb, 0x00cc, 0x00cd, 0x00ce, 0x00cf, // c8
+ 0x00d0, 0x00d1, 0x00d2, 0x00d3, 0x00d4, 0x00d5, 0x00d6, 0x00d7, // d0
+ 0x00d8, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x00dd, 0x00de, 0x00df, // d8
+ 0x00e0, 0x00e1, 0x00e2, 0x00e3, 0x00e4, 0x00e5, 0x00e6, 0x00e7, // e0
+ 0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef, // e8
+ 0x00f0, 0x00f1, 0x00f2, 0x00f3, 0x00f4, 0x00f5, 0x00f6, 0x00f7, // f0
+ 0x00f8, 0x00f9, 0x00fa, 0x00fb, 0x00fc, 0x00fd, 0x00fe, 0x00ff, // f8
+};
+
+static const unsigned short ISO_8859_2[0x60] = {
+ 0x00a0, 0x0104, 0x02d8, 0x0141, 0x00a4, 0x013d, 0x015a, 0x00a7, // a0
+ 0x00a8, 0x0160, 0x015e, 0x0164, 0x0179, 0x00ad, 0x017d, 0x017b, // a8
+ 0x00b0, 0x0105, 0x02db, 0x0142, 0x00b4, 0x013e, 0x015b, 0x02c7, // b0
+ 0x00b8, 0x0161, 0x015f, 0x0165, 0x017a, 0x02dd, 0x017e, 0x017c, // b8
+ 0x0154, 0x00c1, 0x00c2, 0x0102, 0x00c4, 0x0139, 0x0106, 0x00c7, // c0
+ 0x010c, 0x00c9, 0x0118, 0x00cb, 0x011a, 0x00cd, 0x00ce, 0x010e, // c8
+ 0x0110, 0x0143, 0x0147, 0x00d3, 0x00d4, 0x0150, 0x00d6, 0x00d7, // d0
+ 0x0158, 0x016e, 0x00da, 0x0170, 0x00dc, 0x00dd, 0x0162, 0x00df, // d8
+ 0x0155, 0x00e1, 0x00e2, 0x0103, 0x00e4, 0x013a, 0x0107, 0x00e7, // e0
+ 0x010d, 0x00e9, 0x0119, 0x00eb, 0x011b, 0x00ed, 0x00ee, 0x010f, // e8
+ 0x0111, 0x0144, 0x0148, 0x00f3, 0x00f4, 0x0151, 0x00f6, 0x00f7, // f0
+ 0x0159, 0x016f, 0x00fa, 0x0171, 0x00fc, 0x00fd, 0x0163, 0x02d9, // f8
+};
+
+static const unsigned short ISO_8859_3[0x60] = {
+ 0x00a0, 0x0126, 0x02d8, 0x00a3, 0x00a4, 0x0000, 0x0124, 0x00a7, // a0
+ 0x00a8, 0x0130, 0x015e, 0x011e, 0x0134, 0x00ad, 0x0000, 0x017b, // a8
+ 0x00b0, 0x0127, 0x00b2, 0x00b3, 0x00b4, 0x00b5, 0x0125, 0x00b7, // b0
+ 0x00b8, 0x0131, 0x015f, 0x011f, 0x0135, 0x00bd, 0x0000, 0x017c, // b8
+ 0x00c0, 0x00c1, 0x00c2, 0x0000, 0x00c4, 0x010a, 0x0108, 0x00c7, // c0
+ 0x00c8, 0x00c9, 0x00ca, 0x00cb, 0x00cc, 0x00cd, 0x00ce, 0x00cf, // c8
+ 0x0000, 0x00d1, 0x00d2, 0x00d3, 0x00d4, 0x0120, 0x00d6, 0x00d7, // d0
+ 0x011c, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x016c, 0x015c, 0x00df, // d8
+ 0x00e0, 0x00e1, 0x00e2, 0x0000, 0x00e4, 0x010b, 0x0109, 0x00e7, // e0
+ 0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef, // e8
+ 0x0000, 0x00f1, 0x00f2, 0x00f3, 0x00f4, 0x0121, 0x00f6, 0x00f7, // f0
+ 0x011d, 0x00f9, 0x00fa, 0x00fb, 0x00fc, 0x016d, 0x015d, 0x02d9, // f8
+};
+
+static const unsigned short ISO_8859_4[0x60] = {
+ 0x00a0, 0x0104, 0x0138, 0x0156, 0x00a4, 0x0128, 0x013b, 0x00a7, // a0
+ 0x00a8, 0x0160, 0x0112, 0x0122, 0x0166, 0x00ad, 0x017d, 0x00af, // a8
+ 0x00b0, 0x0105, 0x02db, 0x0157, 0x00b4, 0x0129, 0x013c, 0x02c7, // b0
+ 0x00b8, 0x0161, 0x0113, 0x0123, 0x0167, 0x014a, 0x017e, 0x014b, // b8
+ 0x0100, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x012e, // c0
+ 0x010c, 0x00c9, 0x0118, 0x00cb, 0x0116, 0x00cd, 0x00ce, 0x012a, // c8
+ 0x0110, 0x0145, 0x014c, 0x0136, 0x00d4, 0x00d5, 0x00d6, 0x00d7, // d0
+ 0x00d8, 0x0172, 0x00da, 0x00db, 0x00dc, 0x0168, 0x016a, 0x00df, // d8
+ 0x0101, 0x00e1, 0x00e2, 0x00e3, 0x00e4, 0x00e5, 0x00e6, 0x012f, // e0
+ 0x010d, 0x00e9, 0x0119, 0x00eb, 0x0117, 0x00ed, 0x00ee, 0x012b, // e8
+ 0x0111, 0x0146, 0x014d, 0x0137, 0x00f4, 0x00f5, 0x00f6, 0x00f7, // f0
+ 0x00f8, 0x0173, 0x00fa, 0x00fb, 0x00fc, 0x0169, 0x016b, 0x02d9, // f8
+};
+
+static const unsigned short ISO_8859_5[0x60] = {
+ 0x00a0, 0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0406, 0x0407, // a0 Cyrillic
+ 0x0408, 0x0409, 0x040a, 0x040b, 0x040c, 0x00ad, 0x040e, 0x040f, // a8
+ 0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417, // b0
+ 0x0418, 0x0419, 0x041a, 0x041b, 0x041c, 0x041d, 0x041e, 0x041f, // b8
+ 0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427, // c0
+ 0x0428, 0x0429, 0x042a, 0x042b, 0x042c, 0x042d, 0x042e, 0x042f, // c8
+ 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437, // d0
+ 0x0438, 0x0439, 0x043a, 0x043b, 0x043c, 0x043d, 0x043e, 0x043f, // d8
+ 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447, // e0
+ 0x0448, 0x0449, 0x044a, 0x044b, 0x044c, 0x044d, 0x044e, 0x044f, // e8
+ 0x2116, 0x0451, 0x0452, 0x0453, 0x0454, 0x0455, 0x0456, 0x0457, // f0
+ 0x0458, 0x0459, 0x045a, 0x045b, 0x045c, 0x00a7, 0x045e, 0x045f, // f8
+};
+
+static const unsigned short ISO_8859_7[0x60] = {
+ 0x00a0, 0x2018, 0x2019, 0x00a3, 0x20ac, 0x20af, 0x00a6, 0x00a7, // a0 Greek
+ 0x00a8, 0x00a9, 0x037a, 0x00ab, 0x00ac, 0x00ad, 0x0000, 0x2015, // a8
+ 0x00b0, 0x00b1, 0x00b2, 0x00b3, 0x0384, 0x0385, 0x0386, 0x00b7, // b0
+ 0x0388, 0x0389, 0x038a, 0x00bb, 0x038c, 0x00bd, 0x038e, 0x038f, // b8
+ 0x0390, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, // c0
+ 0x0398, 0x0399, 0x039a, 0x039b, 0x039c, 0x039d, 0x039e, 0x039f, // c8
+ 0x03a0, 0x03a1, 0x0000, 0x03a3, 0x03a4, 0x03a5, 0x03a6, 0x03a7, // d0
+ 0x03a8, 0x03a9, 0x03aa, 0x03ab, 0x03ac, 0x03ad, 0x03ae, 0x03af, // d8
+ 0x03b0, 0x03b1, 0x03b2, 0x03b3, 0x03b4, 0x03b5, 0x03b6, 0x03b7, // e0
+ 0x03b8, 0x03b9, 0x03ba, 0x03bb, 0x03bc, 0x03bd, 0x03be, 0x03bf, // e8
+ 0x03c0, 0x03c1, 0x03c2, 0x03c3, 0x03c4, 0x03c5, 0x03c6, 0x03c7, // f0
+ 0x03c8, 0x03c9, 0x03ca, 0x03cb, 0x03cc, 0x03cd, 0x03ce, 0x0000, // f8
+};
+
+static const unsigned short ISO_8859_9[0x60] = {
+ 0x00a0, 0x00a1, 0x00a2, 0x00a3, 0x00a4, 0x00a5, 0x00a6, 0x00a7, // a0
+ 0x00a8, 0x00a9, 0x00aa, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x00af, // a8
+ 0x00b0, 0x00b1, 0x00b2, 0x00b3, 0x00b4, 0x00b5, 0x00b6, 0x00b7, // b0
+ 0x00b8, 0x00b9, 0x00ba, 0x00bb, 0x00bc, 0x00bd, 0x00be, 0x00bf, // b8
+ 0x00c0, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x00c7, // c0
+ 0x00c8, 0x00c9, 0x00ca, 0x00cb, 0x00cc, 0x00cd, 0x00ce, 0x00cf, // c8
+ 0x011e, 0x00d1, 0x00d2, 0x00d3, 0x00d4, 0x00d5, 0x00d6, 0x00d7, // d0
+ 0x00d8, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x0130, 0x015e, 0x00df, // d8
+ 0x00e0, 0x00e1, 0x00e2, 0x00e3, 0x00e4, 0x00e5, 0x00e6, 0x00e7, // e0
+ 0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef, // e8
+ 0x011f, 0x00f1, 0x00f2, 0x00f3, 0x00f4, 0x00f5, 0x00f6, 0x00f7, // f0
+ 0x00f8, 0x00f9, 0x00fa, 0x00fb, 0x00fc, 0x0131, 0x015f, 0x00ff, // f8
+};
+
+static const unsigned short ISO_8859_14[0x60] = {
+ 0x00a0, 0x1e02, 0x1e03, 0x00a3, 0x010a, 0x010b, 0x1e0a, 0x00a7, // a0 Welsh
+ 0x1e80, 0x00a9, 0x1e82, 0x1e0b, 0x1ef2, 0x00ad, 0x00ae, 0x0178, // a8
+ 0x1e1e, 0x1e1f, 0x0120, 0x0121, 0x1e40, 0x1e41, 0x00b6, 0x1e56, // b0
+ 0x1e81, 0x1e57, 0x1e83, 0x1e60, 0x1ef3, 0x1e84, 0x1e85, 0x1e61, // b8
+ 0x00c0, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x00c7, // c0
+ 0x00c8, 0x00c9, 0x00ca, 0x00cb, 0x00cc, 0x00cd, 0x00ce, 0x00cf, // c8
+ 0x0174, 0x00d1, 0x00d2, 0x00d3, 0x00d4, 0x00d5, 0x00d6, 0x1e6a, // d0
+ 0x00d8, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x00dd, 0x0176, 0x00df, // d8
+ 0x00e0, 0x00e1, 0x00e2, 0x00e3, 0x00e4, 0x00e5, 0x00e6, 0x00e7, // e0
+ 0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef, // e8
+ 0x0175, 0x00f1, 0x00f2, 0x00f3, 0x00f4, 0x00f5, 0x00f6, 0x1e6b, // f0
+ 0x00f8, 0x00f9, 0x00fa, 0x00fb, 0x00fc, 0x00fd, 0x0177, 0x00ff, // f8
+};
+
+static const unsigned short KOI8_R[0x60] = {
+ 0x2550, 0x2551, 0x2552, 0x0451, 0x2553, 0x2554, 0x2555, 0x2556, // a0 Russian
+ 0x2557, 0x2558, 0x2559, 0x255a, 0x255b, 0x255c, 0x255d, 0x255e, // a8
+ 0x255f, 0x2560, 0x2561, 0x0401, 0x2562, 0x2563, 0x2564, 0x2565, // b0
+ 0x2566, 0x2567, 0x2568, 0x2569, 0x256a, 0x256b, 0x256c, 0x00a9, // b8
+ 0x044e, 0x0430, 0x0431, 0x0446, 0x0434, 0x0435, 0x0444, 0x0433, // c0
+ 0x0445, 0x0438, 0x0439, 0x043a, 0x043b, 0x043c, 0x043d, 0x043e, // c8
+ 0x043f, 0x044f, 0x0440, 0x0441, 0x0442, 0x0443, 0x0436, 0x0432, // d0
+ 0x044c, 0x044b, 0x0437, 0x0448, 0x044d, 0x0449, 0x0447, 0x044a, // d8
+ 0x042e, 0x0410, 0x0411, 0x0426, 0x0414, 0x0415, 0x0424, 0x0413, // e0
+ 0x0425, 0x0418, 0x0419, 0x041a, 0x041b, 0x041c, 0x041d, 0x041e, // e8
+ 0x041f, 0x042f, 0x0420, 0x0421, 0x0422, 0x0423, 0x0416, 0x0412, // f0
+ 0x042c, 0x042b, 0x0417, 0x0428, 0x042d, 0x0429, 0x0427, 0x042a, // f8
+};
+
+static const unsigned short ISCII[0x60] = {
+ 0x0020, 0x0901, 0x0902, 0x0903, 0x0905, 0x0906, 0x0907, 0x0908, // a0
+ 0x0909, 0x090a, 0x090b, 0x090e, 0x090f, 0x0910, 0x090d, 0x0912, // a8
+ 0x0913, 0x0914, 0x0911, 0x0915, 0x0916, 0x0917, 0x0918, 0x0919, // b0
+ 0x091a, 0x091b, 0x091c, 0x091d, 0x091e, 0x091f, 0x0920, 0x0921, // b8
+ 0x0922, 0x0923, 0x0924, 0x0925, 0x0926, 0x0927, 0x0928, 0x0929, // c0
+ 0x092a, 0x092b, 0x092c, 0x092d, 0x092e, 0x092f, 0x095f, 0x0930, // c8
+ 0x0931, 0x0932, 0x0933, 0x0934, 0x0935, 0x0936, 0x0937, 0x0938, // d0
+ 0x0939, 0x0020, 0x093e, 0x093f, 0x0940, 0x0941, 0x0942, 0x0943, // d8
+ 0x0946, 0x0947, 0x0948, 0x0945, 0x094a, 0x094b, 0x094c, 0x0949, // e0
+ 0x094d, 0x093c, 0x0964, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, // e8
+ 0x0020, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, // f0
+ 0x0037, 0x0038, 0x0039, 0x20, 0x20, 0x20, 0x20, 0x20, // f8
+};
+
+const unsigned short *charsets[N_CHARSETS] = {
+ ISO_8859_1,
+ ISO_8859_1,
+ ISO_8859_2,
+ ISO_8859_3,
+ ISO_8859_4,
+ ISO_8859_5,
+ ISO_8859_1,
+ ISO_8859_7,
+ ISO_8859_1,
+ ISO_8859_9,
+ ISO_8859_1,
+ ISO_8859_1,
+ ISO_8859_1,
+ ISO_8859_1,
+ ISO_8859_14,
+ ISO_8859_1,
+ ISO_8859_1,
+ ISO_8859_1,
+ KOI8_R, // 18
+ ISCII };
+
+// Tables of the relative lengths of vowels, depending on the
+// type of the two phonemes that follow
+// indexes are the "length_mod" value for the following phonemes
+
+// use this table if vowel is not the last in the word
+static unsigned char length_mods_en[100] = {
+/* a , t s n d z r N <- next */
+ 100,120,100,105,100,110,110,100, 95, 100, /* a <- next2 */
+ 105,120,105,110,125,130,135,115,125, 100, /* , */
+ 105,120, 75,100, 75,105,120, 85, 75, 100, /* t */
+ 105,120, 85,105, 95,115,120,100, 95, 100, /* s */
+ 110,120, 95,105,100,115,120,100,100, 100, /* n */
+ 105,120,100,105, 95,115,120,110, 95, 100, /* d */
+ 105,120,100,105,105,122,125,110,105, 100, /* z */
+ 105,120,100,105,105,122,125,110,105, 100, /* r */
+ 105,120, 95,105,100,115,120,110,100, 100, /* N */
+ 100,120,100,100,100,100,100,100,100, 100 }; // SPARE
+
+// as above, but for the last syllable in a word
+static unsigned char length_mods_en0[100] = {
+/* a , t s n d z r N <- next */
+ 100,150,100,105,110,115,110,110,110, 100, /* a <- next2 */
+ 105,150,105,110,125,135,140,115,135, 100, /* , */
+ 105,150, 90,105, 90,122,135,100, 90, 100, /* t */
+ 105,150,100,105,100,122,135,100,100, 100, /* s */
+ 105,150,100,105,105,115,135,110,105, 100, /* n */
+ 105,150,100,105,105,122,130,120,125, 100, /* d */
+ 105,150,100,105,110,122,125,115,110, 100, /* z */
+ 105,150,100,105,105,122,135,120,105, 100, /* r */
+ 105,150,100,105,105,115,135,110,105, 100, /* N */
+ 100,100,100,100,100,100,100,100,100, 100 }; // SPARE
+
+
+static unsigned char length_mods_equal[100] = {
+/* a , t s n d z r N <- next */
+ 110,110,110,110,110,110,110,110,110, 110, /* a <- next2 */
+ 110,110,110,110,110,110,110,110,110, 110, /* , */
+ 110,110,110,110,110,110,110,110,110, 110, /* t */
+ 110,110,110,110,110,110,110,110,110, 110, /* s */
+ 110,110,110,110,110,110,110,110,110, 110, /* n */
+ 110,110,110,110,110,110,110,110,110, 110, /* d */
+ 110,110,110,110,110,110,110,110,110, 110, /* z */
+ 110,110,110,110,110,110,110,110,110, 110, /* r */
+ 110,110,110,110,110,110,110,110,110, 110, /* N */
+ 110,110,110,110,110,110,110,110,110, 110 }; // SPARE
+
+
+static unsigned char *length_mod_tabs[6] = {
+ length_mods_en,
+ length_mods_en, // 1
+ length_mods_en0, // 2
+ length_mods_equal, // 3
+ length_mods_equal, // 4
+ length_mods_equal // 5
+ };
+
+
+void SetLengthMods(Translator *tr, int value)
+{//==========================================
+ int value2;
+
+ tr->langopts.length_mods0 = tr->langopts.length_mods = length_mod_tabs[value % 100];
+ if((value2 = value / 100) != 0)
+ {
+ tr->langopts.length_mods0 = length_mod_tabs[value2];
+ }
+}
+
+
+int IsAlpha(unsigned int c)
+{//========================
+// Replacement for iswalph() which also checks for some in-word symbols
+
+ if(iswalpha(c))
+ return(1);
+
+ if((c >= 0x901) && (c <= 0xdf7))
+ {
+ // Indic scripts: Devanagari, Tamil, etc
+ if((c & 0x7f) < 0x64)
+ return(1);
+ return(0);
+ }
+
+ if((c >= 0x300) && (c <= 0x36f))
+ return(1); // combining accents
+
+ if((c >= 0x1100) && (c <= 0x11ff))
+ return(1); //Korean jamo
+
+ if((c > 0x3040) && (c <= 0xa700))
+ return(1); // Chinese/Japanese. Should never get here, but Mac OS 10.4's iswalpha seems to be broken, so just make sure
+
+ return(0);
+}
+
+static int IsDigit09(unsigned int c)
+{//=========================
+ if((c >= '0') && (c <= '9'))
+ return(1);
+ return(0);
+}
+
+int IsDigit(unsigned int c)
+{//========================
+ if(iswdigit(c))
+ return(1);
+
+ if((c >= 0x966) && (c <= 0x96f))
+ return(1);
+
+ return(0);
+}
+
+int IsSpace(unsigned int c)
+{//========================
+ if(c == 0)
+ return(0);
+ if(wcschr(chars_space,c))
+ return(1);
+ return(iswspace(c));
+}
+
+
+void DeleteTranslator(Translator *tr)
+{//==================================
+ if(tr->data_dictlist != NULL)
+ Free(tr->data_dictlist);
+ Free(tr);
+}
+
+
+int lookupwchar(const unsigned short *list,int c)
+{//==============================================
+// Is the character c in the list ?
+ int ix;
+
+ for(ix=0; list[ix] != 0; ix++)
+ {
+ if(list[ix] == c)
+ return(ix+1);
+ }
+ return(0);
+}
+
+int IsBracket(int c)
+{//=================
+ if((c >= 0x2014) && (c <= 0x201f))
+ return(1);
+ return(lookupwchar(brackets,c));
+}
+
+
+int utf8_out(unsigned int c, char *buf)
+{//====================================
+// write a unicode character into a buffer as utf8
+// returns the number of bytes written
+ int n_bytes;
+ int j;
+ int shift;
+ static char unsigned code[4] = {0,0xc0,0xe0,0xf0};
+
+ if(c < 0x80)
+ {
+ buf[0] = c;
+ return(1);
+ }
+ if(c >= 0x110000)
+ {
+ buf[0] = ' '; // out of range character code
+ return(1);
+ }
+ if(c < 0x0800)
+ n_bytes = 1;
+ else
+ if(c < 0x10000)
+ n_bytes = 2;
+ else
+ n_bytes = 3;
+
+ shift = 6*n_bytes;
+ buf[0] = code[n_bytes] | (c >> shift);
+ for(j=0; j<n_bytes; j++)
+ {
+ shift -= 6;
+ buf[j+1] = 0x80 + ((c >> shift) & 0x3f);
+ }
+ return(n_bytes+1);
+} // end of utf8_out
+
+
+int utf8_nbytes(const char *buf)
+{//=============================
+// Returns the number of bytes for the first UTF-8 character in buf
+ unsigned char c = (unsigned char)buf[0];
+ if(c < 0x80)
+ return(1);
+ if(c < 0xe0)
+ return(2);
+ if(c < 0xf0)
+ return(3);
+ return(4);
+}
+
+
+int utf8_in2(int *c, const char *buf, int backwards)
+{//=================================================
+// Read a unicode characater from a UTF8 string
+// Returns the number of UTF8 bytes used.
+// backwards: set if we are moving backwards through the UTF8 string
+ int c1;
+ int n_bytes;
+ int ix;
+ static const unsigned char mask[4] = {0xff,0x1f,0x0f,0x07};
+
+ // find the start of the next/previous character
+ while((*buf & 0xc0) == 0x80)
+ {
+ // skip over non-initial bytes of a multi-byte utf8 character
+ if(backwards)
+ buf--;
+ else
+ buf++;
+ }
+
+ n_bytes = 0;
+
+ if((c1 = *buf++) & 0x80)
+ {
+ if((c1 & 0xe0) == 0xc0)
+ n_bytes = 1;
+ else
+ if((c1 & 0xf0) == 0xe0)
+ n_bytes = 2;
+ else
+ if((c1 & 0xf8) == 0xf0)
+ n_bytes = 3;
+
+ c1 &= mask[n_bytes];
+ for(ix=0; ix<n_bytes; ix++)
+ {
+ c1 = (c1 << 6) + (*buf++ & 0x3f);
+ }
+ }
+ *c = c1;
+ return(n_bytes+1);
+}
+
+
+int utf8_in(int *c, const char *buf)
+{//=================================
+// Read a unicode characater from a UTF8 string
+// Returns the number of UTF8 bytes used.
+ return(utf8_in2(c,buf,0));
+}
+
+
+char *strchr_w(const char *s, int c)
+{//=================================
+// return NULL for any non-ascii character
+ if(c >= 0x80)
+ return(NULL);
+ return(strchr((char *)s,c)); // (char *) is needed for Borland compiler
+}
+
+
+
+
+int TranslateWord(Translator *tr, char *word1, int next_pause, WORD_TAB *wtab)
+{//===========================================================================
+// word1 is terminated by space (0x20) character
+
+ int length;
+ int word_length;
+ int ix;
+ int posn;
+ int pfix;
+ int n_chars;
+ unsigned int dictionary_flags[2];
+ unsigned int dictionary_flags2[2];
+ int end_type=0;
+ int prefix_type=0;
+ char *wordx;
+ char phonemes[N_WORD_PHONEMES];
+ char *ph_limit;
+ char *phonemes_ptr;
+ char prefix_phonemes[N_WORD_PHONEMES];
+ char end_phonemes[N_WORD_PHONEMES];
+ char word_copy[N_WORD_BYTES];
+ char prefix_chars[N_WORD_BYTES];
+ int found=0;
+ int end_flags;
+ char c_temp; // save a character byte while we temporarily replace it with space
+ int first_char;
+ int last_char = 0;
+ int unpron_length;
+ int add_plural_suffix = 0;
+ int prefix_flags = 0;
+ int confirm_prefix;
+ int spell_word;
+ int stress_bits;
+ int emphasize_allcaps = 0;
+ int wflags = wtab->flags;
+ int wmark = wtab->wmark;
+
+ // translate these to get pronunciations of plural 's' suffix (different forms depending on
+ // the preceding letter
+ static char word_zz[4] = {0,'z','z',0};
+ static char word_iz[4] = {0,'i','z',0};
+ static char word_ss[4] = {0,'s','s',0};
+
+ dictionary_flags[0] = 0;
+ dictionary_flags[1] = 0;
+ dictionary_flags2[0] = 0;
+ dictionary_flags2[1] = 0;
+ dictionary_skipwords = 0;
+
+ prefix_phonemes[0] = 0;
+ end_phonemes[0] = 0;
+ ph_limit = &phonemes[N_WORD_PHONEMES];
+
+ // count the length of the word
+ wordx = word1;
+ utf8_in(&first_char,wordx);
+ word_length = 0;
+ while((*wordx != 0) && (*wordx != ' '))
+ {
+ wordx += utf8_in(&last_char,wordx);
+ word_length++;
+ }
+
+ // try an initial lookup in the dictionary list, we may find a pronunciation specified, or
+ // we may just find some flags
+ spell_word = 0;
+ if(option_sayas == SAYAS_KEY)
+ {
+ if(word_length == 1)
+ spell_word = 4;
+ }
+
+ if(option_sayas & 0x10)
+ {
+ // SAYAS_CHAR, SAYAS_GYLPH, or SAYAS_SINGLE_CHAR
+ spell_word = option_sayas & 0xf; // 2,3,4
+ }
+ else
+ {
+ found = LookupDictList(tr, &word1, phonemes, dictionary_flags, FLAG_ALLOW_TEXTMODE, wtab); // the original word
+ if(dictionary_flags[0] & FLAG_TEXTMODE)
+ {
+ stress_bits = dictionary_flags[0] & 0x7f;
+ found = LookupDictList(tr, &word1, phonemes, dictionary_flags2, 0, wtab); // the text replacement
+ if(dictionary_flags2[0]!=0)
+ {
+ dictionary_flags[0] = dictionary_flags2[0];
+ dictionary_flags[1] = dictionary_flags2[1];
+ if(stress_bits != 0)
+ {
+ // keep any stress information from the original word
+ dictionary_flags[0] = (dictionary_flags[0] & ~0x7f) | stress_bits;
+ }
+ }
+ }
+ else
+ if((found==0) && (dictionary_flags[0] & FLAG_SKIPWORDS))
+ {
+ // grouped words, but no translation. Join the words with hyphens.
+ wordx = word1;
+ ix = 0;
+ while(ix < dictionary_skipwords)
+ {
+ if(*wordx == ' ')
+ {
+ *wordx = '-';
+ ix++;
+ }
+ wordx++;
+ }
+ }
+
+ // if textmode, LookupDictList() replaces word1 by the new text and returns found=0
+
+ if(phonemes[0] == phonSWITCH)
+ {
+ // change to another language in order to translate this word
+ strcpy(word_phonemes,phonemes);
+ return(0);
+ }
+
+if((wmark > 0) && (wmark < 8))
+{
+ // the stressed syllable has been specified in the text (TESTING)
+ dictionary_flags[0] = (dictionary_flags[0] & ~0xf) | wmark;
+}
+
+ if(!found && (dictionary_flags[0] & FLAG_ABBREV))
+ {
+ // the word has $abbrev flag, but no pronunciation specified. Speak as individual letters
+ spell_word = 1;
+ }
+
+ if(!found && iswdigit(first_char))
+ {
+ Lookup(tr,"_0lang",word_phonemes);
+ if(word_phonemes[0] == phonSWITCH)
+ return(0);
+
+ found = TranslateNumber(tr,word1,phonemes,dictionary_flags,wflags);
+ }
+
+ if(!found & ((wflags & FLAG_UPPERS) != FLAG_FIRST_UPPER))
+ {
+ // either all upper or all lower case
+
+ if((tr->langopts.numbers & NUM_ROMAN) || ((tr->langopts.numbers & NUM_ROMAN_UC) && (wflags & FLAG_ALL_UPPER)))
+ {
+ if((found = TranslateRoman(tr, word1, phonemes)) != 0)
+ dictionary_flags[0] |= FLAG_ABBREV; // prevent emphasis if capitals
+ }
+ }
+
+ if((wflags & FLAG_ALL_UPPER) && (word_length > 1)&& iswalpha(first_char))
+ {
+ if((option_tone_flags & OPTION_EMPHASIZE_ALLCAPS) && !(dictionary_flags[0] & FLAG_ABBREV))
+ {
+ // emphasize words which are in capitals
+ emphasize_allcaps = FLAG_EMPHASIZED;
+ }
+ else
+ if(!found && !(dictionary_flags[0] & FLAG_SKIPWORDS) && (word_length<4) && (tr->clause_lower_count > 3)
+ && (tr->clause_upper_count <= tr->clause_lower_count))
+ {
+ // An upper case word in a lower case clause. This could be an abbreviation.
+ spell_word = 1;
+ }
+ }
+ }
+
+ if(spell_word > 0)
+ {
+ // Speak as individual letters
+ wordx = word1;
+ posn = 0;
+ phonemes[0] = 0;
+ end_type = 0;
+
+ while(*wordx != ' ')
+ {
+ wordx += TranslateLetter(tr,wordx, phonemes,spell_word, word_length);
+ posn++;
+ if(phonemes[0] == phonSWITCH)
+ {
+ // change to another language in order to translate this word
+ strcpy(word_phonemes,phonemes);
+ if(word_length > 1)
+ return(FLAG_SPELLWORD); // a mixture of languages, retranslate as individual letters, separated by spaces
+ return(0);
+ }
+ }
+ SetSpellingStress(tr,phonemes,spell_word,posn);
+ }
+ else
+ if(found == 0)
+ {
+ // word's pronunciation is not given in the dictionary list, although
+ // dictionary_flags may have ben set there
+
+ posn = 0;
+ length = 999;
+ wordx = word1;
+
+ while(((length < 3) && (length > 0))|| (word_length > 1 && Unpronouncable(tr,wordx)))
+ {
+ char *p;
+ // This word looks "unpronouncable", so speak letters individually until we
+ // find a remainder that we can pronounce.
+ emphasize_allcaps = 0;
+ wordx += TranslateLetter(tr,wordx,phonemes,0, word_length);
+ posn++;
+ if(phonemes[0] == phonSWITCH)
+ {
+ // change to another language in order to translate this word
+ strcpy(word_phonemes,phonemes);
+ if(strcmp(&phonemes[1],"en")==0)
+ return(FLAG_SPELLWORD); // _^_en must have been set in TranslateLetter(), not *_rules
+ return(0);
+ }
+
+ p = &wordx[word_length-3]; // this looks wrong. Doesn't consider multi-byte chars.
+ if(memcmp(p,"'s ",3) == 0)
+ {
+ // remove a 's suffix and pronounce this separately (not as an individual letter)
+ add_plural_suffix = 1;
+ p[0] = ' ';
+ p[1] = ' ';
+ last_char = p[-1];
+ }
+
+ length=0;
+ while(wordx[length] != ' ') length++;
+ if(length > 0)
+ wordx[-1] = ' '; // prevent this affecting the pronunciation of the pronuncable part
+ }
+ SetSpellingStress(tr,phonemes,0,posn);
+
+ // anything left ?
+ if(*wordx != ' ')
+ {
+ // Translate the stem
+ unpron_length = strlen(phonemes);
+ end_type = TranslateRules(tr, wordx, phonemes, N_WORD_PHONEMES, end_phonemes, wflags, dictionary_flags);
+
+ if(phonemes[0] == phonSWITCH)
+ {
+ // change to another language in order to translate this word
+ strcpy(word_phonemes,phonemes);
+ return(0);
+ }
+
+ if((phonemes[0] == 0) && (end_phonemes[0] == 0))
+ {
+ int wc;
+ // characters not recognised, speak them individually
+
+ utf8_in(&wc, wordx);
+ if((word_length == 1) && IsAlpha(wc))
+ {
+ posn = 0;
+ while((*wordx != ' ') && (*wordx != 0))
+ {
+ wordx += TranslateLetter(tr,wordx, phonemes, 4, word_length);
+ posn++;
+ if(phonemes[0] == phonSWITCH)
+ {
+ // change to another language in order to translate this word
+ strcpy(word_phonemes,phonemes);
+ return(0);
+ }
+ }
+ SetSpellingStress(tr,phonemes,spell_word,posn);
+ }
+ }
+
+ c_temp = wordx[-1];
+
+ found = 0;
+ confirm_prefix = 1;
+ while(end_type & SUFX_P)
+ {
+ // Found a standard prefix, remove it and retranslate
+
+ if(confirm_prefix && !(end_type & SUFX_B))
+ {
+ int end2;
+ char phonemes2[N_WORD_PHONEMES];
+ char end_phonemes2[N_WORD_PHONEMES];
+
+ // remove any standard suffix and confirm that the prefix is still recognised
+ phonemes2[0] = 0;
+ end2 = TranslateRules(tr, wordx, phonemes2, N_WORD_PHONEMES, end_phonemes2, wflags|FLAG_NO_PREFIX|FLAG_NO_TRACE, dictionary_flags);
+ if(end2)
+ {
+ RemoveEnding(tr, wordx, end2, word_copy);
+ end_type = TranslateRules(tr, wordx, phonemes, N_WORD_PHONEMES, end_phonemes, wflags|FLAG_NO_TRACE, dictionary_flags);
+ memcpy(wordx,word_copy,strlen(word_copy));
+ if((end_type & SUFX_P) == 0)
+ {
+ // after removing the suffix, the prefix is no longer recognised.
+ // Keep the suffix, but don't use the prefix
+ end_type = end2;
+ strcpy(phonemes,phonemes2);
+ strcpy(end_phonemes,end_phonemes2);
+ if(option_phonemes == 2)
+ {
+ DecodePhonemes(end_phonemes,end_phonemes2);
+ fprintf(f_trans," suffix [%s]\n\n",end_phonemes2);
+ }
+ }
+ confirm_prefix = 0;
+ continue;
+ }
+ }
+
+ prefix_type = end_type;
+
+ if(prefix_type & SUFX_V)
+ {
+ tr->expect_verb = 1; // use the verb form of the word
+ }
+
+ wordx[-1] = c_temp;
+
+ if((prefix_type & SUFX_B) == 0)
+ {
+ for(ix=(prefix_type & 0xf); ix>0; ix--) // num. of characters to remove
+ {
+ wordx++;
+ while((*wordx & 0xc0) == 0x80) wordx++; // for multibyte characters
+ }
+ }
+ else
+ {
+ pfix = 1;
+ prefix_chars[0] = 0;
+ n_chars = prefix_type & 0x3f;
+
+ for(ix=0; ix < n_chars; ix++) // num. of bytes to remove
+ {
+ prefix_chars[pfix++] = *wordx++;
+
+ if((prefix_type & SUFX_B) && (ix == (n_chars-1)))
+ {
+ prefix_chars[pfix-1] = 0; // discard the last character of the prefix, this is the separator character
+ }
+ }
+ prefix_chars[pfix] = 0;
+ }
+ c_temp = wordx[-1];
+ wordx[-1] = ' ';
+ confirm_prefix = 1;
+
+ if(prefix_type & SUFX_B)
+ {
+// SUFX_B is used for Turkish, tr_rules contains "(Pb£
+ // retranslate the prefix part
+ char *wordpf;
+ char prefix_phonemes2[12];
+
+ strncpy0(prefix_phonemes2,end_phonemes,sizeof(prefix_phonemes2));
+ wordpf = &prefix_chars[1];
+ found = LookupDictList(tr, &wordpf, phonemes, dictionary_flags, SUFX_P, wtab); // without prefix
+ if(found == 0)
+ {
+ end_type = TranslateRules(tr, wordpf, phonemes, N_WORD_PHONEMES, end_phonemes, 0, dictionary_flags);
+ sprintf(prefix_phonemes,"%s%s%s",phonemes,end_phonemes,prefix_phonemes2);
+ }
+ prefix_flags = 1;
+ }
+ else
+ {
+ strcat(prefix_phonemes,end_phonemes);
+ }
+ end_phonemes[0] = 0;
+
+ end_type = 0;
+ found = LookupDictList(tr, &wordx, phonemes, dictionary_flags2, SUFX_P, wtab); // without prefix
+ if(dictionary_flags[0]==0)
+ {
+ dictionary_flags[0] = dictionary_flags2[0];
+ dictionary_flags[1] = dictionary_flags2[1];
+ }
+ else
+ prefix_flags = 1;
+ if(found == 0)
+ {
+ end_type = TranslateRules(tr, wordx, phonemes, N_WORD_PHONEMES, end_phonemes, 0, dictionary_flags);
+
+ if(phonemes[0] == phonSWITCH)
+ {
+ // change to another language in order to translate this word
+ wordx[-1] = c_temp;
+ strcpy(word_phonemes,phonemes);
+ return(0);
+ }
+ }
+ }
+
+ if((end_type != 0) && !(end_type & SUFX_P))
+ {
+char phonemes2[N_WORD_PHONEMES];
+strcpy(phonemes2,phonemes);
+
+ // The word has a standard ending, re-translate without this ending
+ end_flags = RemoveEnding(tr, wordx, end_type, word_copy);
+
+ phonemes_ptr = &phonemes[unpron_length];
+ phonemes_ptr[0] = 0;
+
+ if(prefix_phonemes[0] != 0)
+ {
+ // lookup the stem without the prefix removed
+ wordx[-1] = c_temp;
+ found = LookupDictList(tr, &word1, phonemes_ptr, dictionary_flags2, end_flags, wtab); // include prefix, but not suffix
+ wordx[-1] = ' ';
+ if(dictionary_flags[0]==0)
+ {
+ dictionary_flags[0] = dictionary_flags2[0];
+ dictionary_flags[1] = dictionary_flags2[1];
+ }
+ if(found)
+ prefix_phonemes[0] = 0; // matched whole word, don't need prefix now
+
+ if((found==0) && (dictionary_flags2[0] != 0))
+ prefix_flags = 1;
+ }
+ if(found == 0)
+ {
+ found = LookupDictList(tr, &wordx, phonemes_ptr, dictionary_flags2, end_flags, wtab); // without prefix and suffix
+ if(phonemes_ptr[0] == phonSWITCH)
+ {
+ // change to another language in order to translate this word
+ memcpy(wordx,word_copy,strlen(word_copy));
+ strcpy(word_phonemes,phonemes_ptr);
+ return(0);
+ }
+ if(dictionary_flags[0]==0)
+ {
+ dictionary_flags[0] = dictionary_flags2[0];
+ dictionary_flags[1] = dictionary_flags2[1];
+ }
+ }
+ if(found == 0)
+ {
+ if(end_type & SUFX_Q)
+ {
+ // don't retranslate, use the original lookup result
+ strcpy(phonemes,phonemes2);
+
+ // language specific changes
+ ApplySpecialAttribute(tr,phonemes,dictionary_flags[0]);
+ }
+ else
+ {
+ if(end_flags & FLAG_SUFX)
+ TranslateRules(tr, wordx, phonemes, N_WORD_PHONEMES, NULL,wflags | FLAG_SUFFIX_REMOVED, dictionary_flags);
+ else
+ TranslateRules(tr, wordx, phonemes, N_WORD_PHONEMES, NULL,wflags,dictionary_flags);
+
+ if(phonemes[0] == phonSWITCH)
+ {
+ // change to another language in order to translate this word
+ strcpy(word_phonemes,phonemes);
+ memcpy(wordx,word_copy,strlen(word_copy));
+ wordx[-1] = c_temp;
+ return(0);
+ }
+ }
+ }
+
+ if((end_type & SUFX_T) == 0)
+ {
+ // the default is to add the suffix and then determine the word's stress pattern
+ AppendPhonemes(tr,phonemes, N_WORD_PHONEMES, end_phonemes);
+ end_phonemes[0] = 0;
+ }
+ }
+ wordx[-1] = c_temp;
+ }
+ }
+
+ if((add_plural_suffix) || (wflags & FLAG_HAS_PLURAL))
+ {
+ // s or 's suffix, append [s], [z] or [Iz] depending on previous letter
+ if(last_char == 'f')
+ TranslateRules(tr, &word_ss[1], phonemes, N_WORD_PHONEMES, NULL, 0, NULL);
+ else
+ if((last_char==0) || (strchr_w("hsx",last_char)==NULL))
+ TranslateRules(tr, &word_zz[1], phonemes, N_WORD_PHONEMES, NULL, 0, NULL);
+ else
+ TranslateRules(tr, &word_iz[1], phonemes, N_WORD_PHONEMES, NULL, 0, NULL);
+ }
+
+ wflags |= emphasize_allcaps;
+
+
+ /* determine stress pattern for this word */
+ /******************************************/
+ /* NOTE: this also adds a single PAUSE if the previous word ended
+ in a primary stress, and this one starts with one */
+ if(prefix_flags || (strchr(prefix_phonemes,phonSTRESS_P)!=NULL))
+ {
+ if((tr->langopts.param[LOPT_PREFIXES]) || (prefix_type & SUFX_T))
+ {
+ char *p;
+ // German, keep a secondary stress on the stem
+ SetWordStress(tr, phonemes, dictionary_flags[0], 3, 0);
+
+ // reduce all but the first primary stress
+ ix=0;
+ for(p=prefix_phonemes; *p != 0; p++)
+ {
+ if(*p == phonSTRESS_P)
+ {
+ if(ix==0)
+ ix=1;
+ else
+ *p = phonSTRESS_3;
+ }
+ }
+ strcpy(word_phonemes,prefix_phonemes);
+ strcat(word_phonemes,phonemes);
+ SetWordStress(tr, word_phonemes, dictionary_flags[0], -1, 0);
+ }
+ else
+ {
+ // stress position affects the whole word, including prefix
+ strcpy(word_phonemes,prefix_phonemes);
+ strcat(word_phonemes,phonemes);
+ SetWordStress(tr, word_phonemes, dictionary_flags[0], -1, tr->prev_last_stress);
+ }
+ }
+ else
+ {
+ if(prefix_phonemes[0] == 0)
+ SetWordStress(tr, phonemes, dictionary_flags[0], -1, tr->prev_last_stress);
+ else
+ SetWordStress(tr, phonemes, dictionary_flags[0], -1, 0);
+ strcpy(word_phonemes,prefix_phonemes);
+ strcat(word_phonemes,phonemes);
+ }
+
+ if(end_phonemes[0] != 0)
+ {
+ // a suffix had the SUFX_T option set, add the suffix after the stress pattern has been determined
+ strcat(word_phonemes,end_phonemes);
+ }
+
+ if(wflags & FLAG_LAST_WORD)
+ {
+ // don't use $brk pause before the last word of a sentence
+ // (but allow it for emphasis, see below
+ dictionary_flags[0] &= ~FLAG_PAUSE1;
+ }
+
+ if(wflags & FLAG_EMPHASIZED2)
+ {
+ // A word is indicated in the source text as stressed
+ // Give it stress level 6 (for the intonation module)
+ ChangeWordStress(tr,word_phonemes,6);
+
+ if(wflags & FLAG_EMPHASIZED)
+ dictionary_flags[0] |= FLAG_PAUSE1; // precede by short pause
+ }
+ else
+ if(wtab[dictionary_skipwords].flags & FLAG_LAST_WORD)
+ {
+ // the word has attribute to stress or unstress when at end of clause
+ if(dictionary_flags[0] & (FLAG_STRESS_END | FLAG_STRESS_END2))
+ ChangeWordStress(tr,word_phonemes,4);
+ else
+ if(dictionary_flags[0] & FLAG_UNSTRESS_END)
+ ChangeWordStress(tr,word_phonemes,3);
+ }
+
+ // dictionary flags for this word give a clue about which alternative pronunciations of
+ // following words to use.
+ if(end_type & SUFX_F)
+ {
+ // expect a verb form, with or without -s suffix
+ tr->expect_verb = 2;
+ tr->expect_verb_s = 2;
+ }
+
+ if(dictionary_flags[1] & FLAG_PASTF)
+ {
+ /* expect perfect tense in next two words */
+ tr->expect_past = 3;
+ tr->expect_verb = 0;
+ tr->expect_noun = 0;
+ }
+ else
+ if(dictionary_flags[1] & FLAG_VERBF)
+ {
+ /* expect a verb in the next word */
+ tr->expect_verb = 2;
+ tr->expect_verb_s = 0; /* verb won't have -s suffix */
+ tr->expect_noun = 0;
+ }
+ else
+ if(dictionary_flags[1] & FLAG_VERBSF)
+ {
+ // expect a verb, must have a -s suffix
+ tr->expect_verb = 0;
+ tr->expect_verb_s = 2;
+ tr->expect_past = 0;
+ tr->expect_noun = 0;
+ }
+ else
+ if(dictionary_flags[1] & FLAG_NOUNF)
+ {
+ /* not expecting a verb next */
+ tr->expect_noun = 3;
+ tr->expect_verb = 0;
+ tr->expect_verb_s = 0;
+ tr->expect_past = 0;
+ }
+
+ if((wordx[0] != 0) && (!(dictionary_flags[1] & FLAG_VERB_EXT)))
+ {
+ if(tr->expect_verb > 0)
+ tr->expect_verb--;
+
+ if(tr->expect_verb_s > 0)
+ tr->expect_verb_s--;
+
+ if(tr->expect_noun >0)
+ tr->expect_noun--;
+
+ if(tr->expect_past > 0)
+ tr->expect_past--;
+ }
+
+ if((word_length == 1) && iswalpha(first_char) && (first_char != 'i'))
+ {
+// English Specific !!!!
+ // any single letter before a dot is an abbreviation, except 'I'
+ dictionary_flags[0] |= FLAG_DOT;
+ }
+
+ return(dictionary_flags[0]);
+} // end of TranslateWord
+
+
+
+static void SetPlist2(PHONEME_LIST2 *p, unsigned char phcode)
+{//==========================================================
+ p->phcode = phcode;
+ p->stress = 0;
+ p->tone_number = 0;
+ p->synthflags = 0;
+ p->sourceix = 0;
+}
+
+static int CountSyllables(unsigned char *phonemes)
+{//===============================================
+ int count = 0;
+ int phon;
+ while((phon = *phonemes++) != 0)
+ {
+ if(phoneme_tab[phon]->type == phVOWEL)
+ count++;
+ }
+ return(count);
+}
+
+
+int SetTranslator2(const char *new_language)
+{//=========================================
+// Set translator2 to a second language
+ int new_phoneme_tab;
+
+ if((new_phoneme_tab = SelectPhonemeTableName(new_language)) >= 0)
+ {
+ if((translator2 != NULL) && (strcmp(new_language,translator2_language) != 0))
+ {
+ // we already have an alternative translator, but not for the required language, delete it
+ DeleteTranslator(translator2);
+ translator2 = NULL;
+ }
+
+ if(translator2 == NULL)
+ {
+ translator2 = SelectTranslator(new_language);
+ strcpy(translator2_language,new_language);
+
+ if(LoadDictionary(translator2, new_language, 0) != 0)
+ {
+ SelectPhonemeTable(voice->phoneme_tab_ix); // revert to original phoneme table
+ new_phoneme_tab = -1;
+ translator2_language[0] = 0;
+ }
+ }
+ }
+ return(new_phoneme_tab);
+} // end of SetTranslator2
+
+
+
+static int TranslateWord2(Translator *tr, char *word, WORD_TAB *wtab, int pre_pause, int next_pause)
+{//=================================================================================================
+ int flags=0;
+ int stress;
+ int next_stress;
+ int next_tone=0;
+ unsigned char *p;
+ int srcix;
+ int embedded_flag=0;
+ int embedded_cmd;
+ int value;
+ int found_dict_flag;
+ unsigned char ph_code;
+ PHONEME_LIST2 *plist2;
+ PHONEME_TAB *ph;
+ int max_stress;
+ int max_stress_ix=0;
+ int prev_vowel = -1;
+ int pitch_raised = 0;
+ int switch_phonemes = -1;
+ int first_phoneme = 1;
+ int source_ix;
+ int len;
+ int ix;
+ int sylimit; // max. number of syllables in a word to be combined with a preceding preposition
+ const char *new_language;
+ unsigned char bad_phoneme[4];
+ int word_flags;
+ int word_copy_len;
+ char word_copy[N_WORD_BYTES+1];
+
+ len = wtab->length;
+ if(len > 31) len = 31;
+ source_ix = (wtab->sourceix & 0x7ff) | (len << 11); // bits 0-10 sourceix, bits 11-15 word length
+
+ word_flags = wtab[0].flags;
+ if(word_flags & FLAG_EMBEDDED)
+ {
+ embedded_flag = SFLAG_EMBEDDED;
+
+ do
+ {
+ embedded_cmd = embedded_list[embedded_read++];
+ value = embedded_cmd >> 8;
+
+ switch(embedded_cmd & 0x1f)
+ {
+ case EMBED_Y:
+ option_sayas = value;
+ break;
+
+ case EMBED_F:
+ option_emphasis = value;
+ break;
+
+ case EMBED_B:
+ // break command
+ if(value == 0)
+ pre_pause = 0; // break=none
+ else
+ pre_pause += value;
+ break;
+ }
+ } while((embedded_cmd & 0x80) == 0);
+ }
+
+ if(word[0] == 0)
+ {
+ // nothing to translate
+ word_phonemes[0] = 0;
+ return(0);
+ }
+
+ // after a $pause word attribute, ignore a $pause attribute on the next two words
+ if(tr->prepause_timeout > 0)
+ tr->prepause_timeout--;
+
+ if((option_sayas & 0xf0) == 0x10)
+ {
+ if(!(word_flags & FLAG_FIRST_WORD))
+ {
+ // SAYAS_CHARS, SAYAS_GLYPHS, or SAYAS_SINGLECHARS. Pause between each word.
+ pre_pause += 4;
+ }
+ }
+
+ if(word_flags & FLAG_FIRST_UPPER)
+ {
+ if((option_capitals > 2) && (embedded_ix < N_EMBEDDED_LIST-6))
+ {
+ // indicate capital letter by raising pitch
+ if(embedded_flag)
+ embedded_list[embedded_ix-1] &= ~0x80; // already embedded command before this word, remove terminator
+ if((pitch_raised = option_capitals) == 3)
+ pitch_raised = 20; // default pitch raise for capitals
+ embedded_list[embedded_ix++] = EMBED_P+0x40+0x80 + (pitch_raised << 8); // raise pitch
+ embedded_flag = SFLAG_EMBEDDED;
+ }
+ }
+
+ p = (unsigned char *)word_phonemes;
+ if(word_flags & FLAG_PHONEMES)
+ {
+ // The input is in phoneme mnemonics, not language text
+ int c1;
+ char lang_name[12];
+
+ if(memcmp(word,"_^_",3)==0)
+ {
+ // switch languages
+ word+=3;
+ for(ix=0;;)
+ {
+ c1 = *word++;
+ if((c1==' ') || (c1==0))
+ break;
+ lang_name[ix++] = tolower(c1);
+ }
+ lang_name[ix] = 0;
+
+ if((ix = LookupPhonemeTable(lang_name)) > 0)
+ {
+ SelectPhonemeTable(ix);
+ word_phonemes[0] = phonSWITCH;
+ word_phonemes[1] = ix;
+ word_phonemes[2] = 0;
+ }
+ }
+ else
+ {
+ EncodePhonemes(word,word_phonemes,bad_phoneme);
+ }
+ flags = FLAG_FOUND;
+ }
+ else
+ {
+ int c2;
+ ix = 0;
+ while(((c2 = word_copy[ix] = word[ix]) != ' ') && (c2 != 0) && (ix < N_WORD_BYTES)) ix++;
+ word_copy_len = ix;
+
+ flags = TranslateWord(translator, word, next_pause, wtab);
+
+ if(flags & FLAG_SPELLWORD)
+ {
+ // re-translate the word as individual letters, separated by spaces
+ memcpy(word, word_copy, word_copy_len);
+ return(flags);
+ }
+
+ if((flags & FLAG_ALT2_TRANS) && ((sylimit = tr->langopts.param[LOPT_COMBINE_WORDS]) > 0))
+ {
+ char *p2;
+ int ok = 1;
+ int flags2;
+ int c_word2;
+ char ph_buf[N_WORD_PHONEMES];
+
+ // LANG=cs,sk
+ // combine a preposition with the following word
+ p2 = word;
+ while(*p2 != ' ') p2++;
+
+ utf8_in(&c_word2, p2+1); // first character of the next word;
+ if(!iswalpha(c_word2))
+ {
+ ok =0;
+ }
+
+ if(ok != 0)
+ {
+ if(sylimit & 0x100)
+ {
+ // only if the second word has $alt attribute
+ strcpy(ph_buf,word_phonemes);
+ flags2 = TranslateWord(translator, p2+1, 0, wtab+1);
+ if((flags2 & FLAG_ALT_TRANS) == 0)
+ {
+ ok = 0;
+ strcpy(word_phonemes,ph_buf);
+ }
+ }
+
+ if((sylimit & 0x200) && ((wtab+1)->flags & FLAG_LAST_WORD))
+ {
+ // not if the next word is end-of-sentence
+ ok = 0;
+ }
+ }
+
+ if(ok)
+ {
+ *p2 = '-'; // replace next space by hyphen
+ flags = TranslateWord(translator, word, next_pause, wtab); // translate the combined word
+ if(CountSyllables(p) > (sylimit & 0xf))
+ {
+ // revert to separate words
+ *p2 = ' ';
+ flags = TranslateWord(translator, word, next_pause, wtab);
+ }
+ else
+ {
+ flags |= FLAG_SKIPWORDS;
+ dictionary_skipwords = 1;
+ }
+ }
+ }
+
+ if(p[0] == phonSWITCH)
+ {
+ // this word uses a different language
+ memcpy(word, word_copy, word_copy_len);
+
+ new_language = (char *)(&p[1]);
+ if(new_language[0]==0)
+ new_language = "en";
+
+ switch_phonemes = SetTranslator2(new_language);
+
+ if(switch_phonemes >= 0)
+ {
+ // re-translate the word using the new translator
+ flags = TranslateWord(translator2, word, next_pause, wtab);
+// strcpy((char *)p,translator2->word_phonemes);
+ if(p[0] == phonSWITCH)
+ {
+ // the second translator doesn't want to process this word
+ switch_phonemes = -1;
+ }
+ }
+ if(switch_phonemes < 0)
+ {
+ // language code is not recognised or 2nd translator won't translate it
+ p[0] = phonSCHWA; // just say something
+ p[1] = phonSCHWA;
+ p[2] = 0;
+ }
+ }
+
+ if(!(word_flags & FLAG_HYPHEN))
+ {
+ if(flags & FLAG_PAUSE1)
+ {
+ if(pre_pause < 1)
+ pre_pause = 1;
+ }
+ if((flags & FLAG_PREPAUSE) && (tr->prepause_timeout == 0))
+ {
+ // the word is marked in the dictionary list with $pause
+ if(pre_pause < 4) pre_pause = 4;
+ tr->prepause_timeout = 3;
+ }
+ }
+
+ if((option_emphasis >= 3) && (pre_pause < 1))
+ pre_pause = 1;
+ }
+
+ plist2 = &ph_list2[n_ph_list2];
+ stress = 0;
+ next_stress = 0;
+ srcix = 0;
+ max_stress = -1;
+
+ found_dict_flag = 0;
+ if(flags & FLAG_FOUND)
+ found_dict_flag = SFLAG_DICTIONARY;
+
+ while((pre_pause > 0) && (n_ph_list2 < N_PHONEME_LIST-4))
+ {
+ // add pause phonemes here. Either because of punctuation (brackets or quotes) in the
+ // text, or because the word is marked in the dictionary lookup as a conjunction
+ if(pre_pause > 1)
+ {
+ SetPlist2(&ph_list2[n_ph_list2++],phonPAUSE);
+ pre_pause -= 2;
+ }
+ else
+ {
+ SetPlist2(&ph_list2[n_ph_list2++],phonPAUSE_NOLINK);
+ pre_pause--;
+ }
+ tr->end_stressed_vowel = 0; // forget about the previous word
+ tr->prev_dict_flags = 0;
+ }
+
+ if((option_capitals==1) && (word_flags & FLAG_FIRST_UPPER))
+ {
+ SetPlist2(&ph_list2[n_ph_list2++],phonPAUSE_SHORT);
+ SetPlist2(&ph_list2[n_ph_list2++],phonCAPITAL);
+ if((word_flags & FLAG_ALL_UPPER) && IsAlpha(word[1]))
+ {
+ // word > 1 letter and all capitals
+ SetPlist2(&ph_list2[n_ph_list2++],phonPAUSE_SHORT);
+ SetPlist2(&ph_list2[n_ph_list2++],phonCAPITAL);
+ }
+ }
+
+ if(switch_phonemes >= 0)
+ {
+ // this word uses a different phoneme table
+ SetPlist2(&ph_list2[n_ph_list2],phonSWITCH);
+ ph_list2[n_ph_list2++].tone_number = switch_phonemes; // temporary phoneme table number
+ }
+
+ // remove initial pause from a word if it follows a hyphen
+ if((word_flags & FLAG_HYPHEN) && (phoneme_tab[*p]->type == phPAUSE))
+ p++;
+
+ while(((ph_code = *p++) != 0) && (n_ph_list2 < N_PHONEME_LIST-4))
+ {
+ if(ph_code == 255)
+ continue; // unknown phoneme
+
+ // Add the phonemes to the first stage phoneme list (ph_list2)
+ ph = phoneme_tab[ph_code];
+
+ if(ph_code == phonSWITCH)
+ {
+ ph_list2[n_ph_list2].phcode = ph_code;
+ ph_list2[n_ph_list2].sourceix = 0;
+ ph_list2[n_ph_list2].synthflags = embedded_flag;
+ ph_list2[n_ph_list2++].tone_number = *p++;
+ }
+ else
+ if(ph->type == phSTRESS)
+ {
+ // don't add stress phonemes codes to the list, but give their stress
+ // value to the next vowel phoneme
+ // std_length is used to hold stress number or (if >10) a tone number for a tone language
+ if(ph->spect == 0)
+ next_stress = ph->std_length;
+ else
+ {
+ // for tone languages, the tone number for a syllable follows the vowel
+ if(prev_vowel >= 0)
+ {
+ ph_list2[prev_vowel].tone_number = ph_code;
+ }
+ else
+ {
+ next_tone = ph_code; // no previous vowel, apply to the next vowel
+ }
+ }
+ }
+ else
+ if(ph_code == phonSYLLABIC)
+ {
+ // mark the previous phoneme as a syllabic consonant
+ prev_vowel = n_ph_list2-1;
+ ph_list2[prev_vowel].synthflags |= SFLAG_SYLLABLE;
+ ph_list2[prev_vowel].stress = next_stress;
+ }
+ else
+ if(ph_code == phonLENGTHEN)
+ {
+ ph_list2[n_ph_list2-1].synthflags |= SFLAG_LENGTHEN;
+ }
+ else
+ if(ph_code == phonEND_WORD)
+ {
+ // a || symbol in a phoneme string was used to indicate a word boundary
+ // Don't add this phoneme to the list, but make sure the next phoneme has
+ // a newword indication
+ srcix = source_ix+1;
+ }
+ else
+ if(ph_code == phonX1)
+ {
+ // a language specific action
+ if(tr->langopts.param[LOPT_IT_DOUBLING])
+ {
+ flags |= FLAG_DOUBLING;
+ }
+ }
+ else
+ {
+ ph_list2[n_ph_list2].phcode = ph_code;
+ ph_list2[n_ph_list2].tone_number = 0;
+ ph_list2[n_ph_list2].synthflags = embedded_flag | found_dict_flag;
+ embedded_flag = 0;
+ ph_list2[n_ph_list2].sourceix = srcix;
+ srcix = 0;
+
+ if(ph->type == phVOWEL)
+ {
+ stress = next_stress;
+ next_stress = 0;
+
+ if((prev_vowel >= 0) && (n_ph_list2-1) != prev_vowel)
+ ph_list2[n_ph_list2-1].stress = stress; // set stress for previous consonant
+
+ ph_list2[n_ph_list2].synthflags |= SFLAG_SYLLABLE;
+ prev_vowel = n_ph_list2;
+
+ if(stress > max_stress)
+ {
+ max_stress = stress;
+ max_stress_ix = n_ph_list2;
+ }
+ if(next_tone != 0)
+ {
+ ph_list2[n_ph_list2].tone_number = next_tone;
+ next_tone=0;
+ }
+ }
+ else
+ {
+ if(first_phoneme && tr->langopts.param[LOPT_IT_DOUBLING])
+ {
+ if(((tr->prev_dict_flags & FLAG_DOUBLING) && (tr->langopts.param[LOPT_IT_DOUBLING] & 1)) ||
+ (tr->end_stressed_vowel && (tr->langopts.param[LOPT_IT_DOUBLING] & 2)))
+ {
+ // italian, double the initial consonant if the previous word ends with a
+ // stressed vowel, or is marked with a flag
+ ph_list2[n_ph_list2].synthflags |= SFLAG_LENGTHEN;
+ }
+ }
+ }
+
+ ph_list2[n_ph_list2].stress = stress;
+ n_ph_list2++;
+ first_phoneme = 0;
+ }
+ }
+ // don't set new-word if there is a hyphen before it
+ if((word_flags & FLAG_HYPHEN) == 0)
+ {
+ plist2->sourceix = source_ix;
+ }
+
+ tr->end_stressed_vowel = 0;
+ if((stress >= 4) && (phoneme_tab[ph_list2[n_ph_list2-1].phcode]->type == phVOWEL))
+ {
+ tr->end_stressed_vowel = 1; // word ends with a stressed vowel
+ }
+
+ if(switch_phonemes >= 0)
+ {
+ // this word uses a different phoneme table, now switch back
+ SelectPhonemeTable(voice->phoneme_tab_ix);
+ SetPlist2(&ph_list2[n_ph_list2],phonSWITCH);
+ ph_list2[n_ph_list2++].tone_number = voice->phoneme_tab_ix; // original phoneme table number
+ }
+
+
+ if(pitch_raised > 0)
+ {
+ embedded_list[embedded_ix++] = EMBED_P+0x60+0x80 + (pitch_raised << 8); // lower pitch
+ SetPlist2(&ph_list2[n_ph_list2],phonPAUSE_SHORT);
+ ph_list2[n_ph_list2++].synthflags = SFLAG_EMBEDDED;
+ }
+
+ if(flags & FLAG_STRESS_END2)
+ {
+ // this's word's stress could be increased later
+ ph_list2[max_stress_ix].synthflags |= SFLAG_PROMOTE_STRESS;
+ }
+
+ tr->prev_dict_flags = flags;
+ return(flags);
+} // end of TranslateWord2
+
+
+
+static int EmbeddedCommand(unsigned int &source_index)
+{//===================================================
+ // An embedded command to change the pitch, volume, etc.
+ // returns number of commands added to embedded_list
+
+ // pitch,speed,amplitude,expression,reverb,tone,voice,sayas
+ const char *commands = "PSARHTIVYMUBF";
+ int value = -1;
+ int sign = 0;
+ unsigned char c;
+ char *p;
+ int cmd;
+
+ c = source[source_index];
+ if(c == '+')
+ {
+ sign = 0x40;
+ source_index++;
+ }
+ else
+ if(c == '-')
+ {
+ sign = 0x60;
+ source_index++;
+ }
+
+ if(isdigit(source[source_index]))
+ {
+ value = atoi(&source[source_index]);
+ while(isdigit(source[source_index]))
+ source_index++;
+ }
+
+ c = source[source_index++];
+ if(embedded_ix >= (N_EMBEDDED_LIST - 2))
+ return(0); // list is full
+
+ if((p = strchr_w(commands,c)) == NULL)
+ return(0);
+ cmd = (p - commands)+1;
+ if(value == -1)
+ {
+ value = embedded_default[cmd];
+ sign = 0;
+ }
+
+ if(cmd == EMBED_Y)
+ {
+ option_sayas2 = value;
+ count_sayas_digits = 0;
+ }
+ if(cmd == EMBED_F)
+ {
+ if(value >= 3)
+ word_emphasis = FLAG_EMPHASIZED;
+ else
+ word_emphasis = 0;
+ }
+
+ embedded_list[embedded_ix++] = cmd + sign + (value << 8);
+ return(1);
+} // end of EmbeddedCommand
+
+
+
+static int SubstituteChar(Translator *tr, unsigned int c, unsigned int next_in, int *insert)
+{//=========================================================================================
+ int ix;
+ unsigned int word;
+ unsigned int new_c, c2, c_lower;
+ int upper_case = 0;
+ static int ignore_next = 0;
+ const unsigned int *replace_chars;
+
+ if(ignore_next)
+ {
+ ignore_next = 0;
+ return(8);
+ }
+ if(c == 0) return(0);
+
+ if((replace_chars = tr->langopts.replace_chars) == NULL)
+ return(c);
+
+ // there is a list of character codes to be substituted with alternative codes
+
+ if(iswupper(c_lower = c))
+ {
+ c_lower = towlower(c);
+ upper_case = 1;
+ }
+
+ new_c = 0;
+ for(ix=0; (word = replace_chars[ix]) != 0; ix+=2)
+ {
+ if(c_lower == (word & 0xffff))
+ {
+ if((word >> 16) == 0)
+ {
+ new_c = replace_chars[ix+1];
+ break;
+ }
+ if((word >> 16) == (unsigned int)towlower(next_in))
+ {
+ new_c = replace_chars[ix+1];
+ ignore_next = 1;
+ break;
+ }
+ }
+ }
+
+ if(new_c == 0)
+ return(c); // no substitution
+
+ if(new_c & 0xffe00000)
+ {
+ // there is a second character to be inserted
+ // don't convert the case of the second character unless the next letter is also upper case
+ c2 = new_c >> 16;
+ if(upper_case && iswupper(next_in))
+ c2 = towupper(c2);
+ *insert = c2;
+ new_c &= 0xffff;
+ }
+
+ if(upper_case)
+ new_c = towupper(new_c);
+ return(new_c);
+
+}
+
+
+static int TranslateChar(Translator *tr, char *ptr, int prev_in, unsigned int c, unsigned int next_in, int *insert)
+{//================================================================================================================
+ // To allow language specific examination and replacement of characters
+
+ int code;
+ int initial;
+ int medial;
+ int final;
+ int next2;
+
+ static const unsigned char hangul_compatibility[0x34] = {
+ 0, 0x00,0x01,0xaa,0x02,0xac,0xad,0x03,
+ 0x04,0x05,0xb0,0xb1,0xb2,0xb3,0xb4,0xb4,
+ 0xb6,0x06,0x07,0x08,0xb9,0x09,0x0a,0xbc,
+ 0x0c,0x0d,0x0e,0x0f,0x10,0x11,0x12,0x61,
+ 0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,
+ 0x6a,0x6b,0x6c,0x6d,0x6e,0x6f,0x70,0x71,
+ 0x72,0x73,0x74,0x75 };
+
+ switch(tr->translator_name)
+ {
+ case L('a','f'):
+ // look for 'n and replace by a special character (unicode: schwa)
+
+ utf8_in(&next2, &ptr[1]);
+
+ if(!iswalpha(prev_in))
+ {
+ if((c == '\'') && (next_in == 'n') && IsSpace(next2))
+ {
+ // n preceded by either apostrophe or U2019 "right single quotation mark"
+ ptr[0] = ' '; // delete the n
+ return(0x0259); // replace ' by unicode schwa character
+ }
+ }
+ break;
+
+ case L('k','o'):
+ if(((code = c - 0xac00) >= 0) && (c <= 0xd7af))
+ {
+ // break a syllable hangul into 2 or 3 individual jamo
+ initial = (code/28)/21;
+ medial = (code/28) % 21;
+ final = code % 28;
+
+ if(initial == 11)
+ {
+ // null initial
+ c = medial + 0x1161;
+ if(final > 0)
+ *insert = final + 0x11a7;
+ }
+ else
+ {
+ // extact the initial and insert the remainder with a null initial
+ c = initial + 0x1100;
+ *insert = (11*28*21) + (medial*28) + final + 0xac00;
+ }
+ return(c);
+ }
+ else
+ if(((code = c - 0x3130) >= 0) && (code < 0x34))
+ {
+ // Hangul compatibility jamo
+ return(hangul_compatibility[code] + 0x1100);
+ }
+ break;
+ }
+ return(SubstituteChar(tr,c,next_in,insert));
+}
+
+
+void *TranslateClause(Translator *tr, FILE *f_text, const void *vp_input, int *tone_out, char **voice_change)
+{//==========================================================================================================
+ int ix;
+ int c;
+ int cc;
+ unsigned int source_index=0;
+ unsigned int prev_source_index=0;
+ int prev_in;
+ int prev_out=' ';
+ int prev_out2;
+ int prev_in2=0;
+ int next_in;
+ int char_inserted=0;
+ int clause_pause;
+ int pre_pause=0;
+ int pre_pause_add=0;
+ int word_mark = 0;
+ int all_upper_case=FLAG_ALL_UPPER;
+ int finished;
+ int single_quoted;
+ int phoneme_mode = 0;
+ int dict_flags; // returned from dictionary lookup
+ int word_flags; // set here
+ int next_word_flags;
+ int embedded_count = 0;
+ int letter_count = 0;
+ int space_inserted = 0;
+ int syllable_marked = 0;
+ int decimal_sep_count = 0;
+ char *word;
+ char *p;
+ int j, k;
+ int n_digits;
+
+ short charix[N_TR_SOURCE+1];
+ WORD_TAB words[N_CLAUSE_WORDS];
+ int word_count=0; // index into words
+
+ char sbuf[N_TR_SOURCE];
+
+ int terminator;
+ int tone;
+ int tone2;
+
+
+ p_textinput = (char *)vp_input;
+ p_wchar_input = (wchar_t *)vp_input;
+
+ embedded_ix = 0;
+ embedded_read = 0;
+ option_phoneme_input &= ~2; // clear bit 1 (temporary indication)
+
+ if((clause_start_char = count_characters) < 0)
+ clause_start_char = 0;
+ clause_start_word = count_words + 1;
+
+ for(ix=0; ix<N_TR_SOURCE; ix++)
+ charix[ix] = 0;
+ terminator = ReadClause(tr, f_text, source, charix, N_TR_SOURCE, &tone2);
+
+ charix[N_TR_SOURCE] = count_characters;
+
+ clause_pause = (terminator & 0xfff) * 10; // mS
+ tone = (terminator >> 12) & 0xf;
+ if(tone2 != 0)
+ {
+ // override the tone type
+ tone = tone2;
+ }
+
+ for(p=source; *p != 0; p++)
+ {
+ if(!isspace2(*p))
+ {
+ break;
+ }
+ }
+ if(*p == 0)
+ {
+ // No characters except spaces. This is not a sentence.
+ // Don't add this pause, just make up the previous pause to this value;
+ clause_pause -= max_clause_pause;
+ if(clause_pause < 0)
+ clause_pause = 0;
+
+ terminator &= ~CLAUSE_BIT_SENTENCE; // clear sentence bit
+ max_clause_pause += clause_pause;
+ }
+ else
+ {
+ max_clause_pause = clause_pause;
+ }
+
+ if(new_sentence)
+ {
+ count_sentences++;
+ if(skip_sentences > 0)
+ {
+ skip_sentences--;
+ if(skip_sentences == 0)
+ skipping_text = 0;
+ }
+ }
+
+ memset(&ph_list2[0],0,sizeof(ph_list2[0]));
+ ph_list2[0].phcode = phonPAUSE_SHORT;
+
+ n_ph_list2 = 1;
+ tr->prev_last_stress = 0;
+ tr->prepause_timeout = 0;
+ tr->expect_verb=0;
+ tr->expect_noun=0;
+ tr->expect_past=0;
+ tr->expect_verb_s=0;
+ tr->phonemes_repeat_count = 0;
+ tr->end_stressed_vowel=0;
+ tr->prev_dict_flags = 0;
+
+ word_count = 0;
+ single_quoted = 0;
+ word_flags = 0;
+ next_word_flags = 0;
+
+ sbuf[0] = 0;
+ sbuf[1] = ' ';
+ sbuf[2] = ' ';
+ ix = 3;
+ prev_in = ' ';
+
+ words[0].start = ix;
+ words[0].flags = 0;
+ finished = 0;
+
+ for(j=0; charix[j]==0; j++);
+ words[0].sourceix = charix[j];
+ k = 0;
+ while(charix[j] != 0)
+ {
+ // count the number of characters (excluding multibyte continuation bytes)
+ if(charix[j++] != -1)
+ k++;
+ }
+ words[0].length = k;
+
+ while(!finished && (ix < (int)sizeof(sbuf))&& (n_ph_list2 < N_PHONEME_LIST-4))
+ {
+ prev_out2 = prev_out;
+ utf8_in2(&prev_out,&sbuf[ix-1],1); // prev_out = sbuf[ix-1];
+
+ if(tr->langopts.tone_numbers && IsDigit09(prev_out) && IsAlpha(prev_out2))
+ {
+ // tone numbers can be part of a word, consider them as alphabetic
+ prev_out = 'a';
+ }
+
+ if(prev_in2 != 0)
+ {
+ prev_in = prev_in2;
+ prev_in2 = 0;
+ }
+ else
+ if(source_index > 0)
+ {
+ utf8_in2(&prev_in,&source[source_index-1],1); // prev_in = source[source_index-1];
+ }
+
+ prev_source_index = source_index;
+
+ if(char_inserted)
+ {
+ c = char_inserted;
+ char_inserted = 0;
+ }
+ else
+ {
+ source_index += utf8_in(&cc,&source[source_index]); // cc = source[source_index++];
+ c = cc;
+ }
+ utf8_in(&next_in,&source[source_index]);
+
+ if((c == CTRL_EMBEDDED) || (c == ctrl_embedded))
+ {
+ // start of embedded command in the text
+ int srcix = source_index-1;
+
+ if(prev_in != ' ')
+ {
+ c = ' ';
+ prev_in2 = c;
+ source_index--;
+ }
+ else
+ {
+ embedded_count += EmbeddedCommand(source_index);
+ prev_in2 = prev_in;
+ // replace the embedded command by spaces
+ memset(&source[srcix],' ',source_index-srcix);
+ source_index = srcix;
+ continue;
+ }
+ }
+
+ if(option_sayas2 == SAYAS_KEY)
+ {
+ if(((c == '_') || (c == '-')) && IsAlpha(prev_in))
+ {
+ c = ' ';
+ }
+ c = towlower2(c);
+ }
+
+ if(phoneme_mode)
+ {
+ all_upper_case = FLAG_PHONEMES;
+
+ if((c == ']') && (next_in == ']'))
+ {
+ phoneme_mode = 0;
+ source_index++;
+ c = ' ';
+ }
+ }
+ else
+ if((option_sayas2 & 0xf0) == SAYAS_DIGITS)
+ {
+ if(iswdigit(c))
+ {
+ count_sayas_digits++;
+ if(count_sayas_digits > (option_sayas2 & 0xf))
+ {
+ // break after the specified number of digits
+ c = ' ';
+ space_inserted = 1;
+ count_sayas_digits = 0;
+ }
+ }
+ else
+ {
+ count_sayas_digits = 0;
+ if(iswdigit(prev_out))
+ {
+ c = ' ';
+ space_inserted = 1;
+ }
+ }
+ }
+ else
+ if((option_sayas2 & 0x30) == 0)
+ {
+ // speak as words
+
+#ifdef deleted
+if((c == '/') && (tr->langopts.testing & 2) && IsDigit09(next_in) && IsAlpha(prev_out))
+{
+ // TESTING, explicit indication of stressed syllable by /2 after the word
+ word_mark = next_in-'0';
+ source_index++;
+ c = ' ';
+}
+#endif
+ if((c == 0x92) || (c == 0xb4) || (c == 0x2019) || (c == 0x2032))
+ c = '\''; // 'microsoft' quote or sexed closing single quote, or prime - possibly used as apostrophe
+
+ if((c == '?') && IsAlpha(prev_out) && IsAlpha(next_in))
+ {
+ // ? between two letters may be a smart-quote replaced by ?
+ c = '\'';
+ }
+
+ if(c == CHAR_EMPHASIS)
+ {
+ // this character is a marker that the previous word is the focus of the clause
+ c = ' ';
+ word_flags |= FLAG_FOCUS;
+ }
+
+ c = TranslateChar(tr, &source[source_index], prev_in,c, next_in, &char_inserted); // optional language specific function
+ if(c == 8)
+ continue; // ignore this character
+
+ if(char_inserted)
+ next_in = char_inserted;
+
+ // allow certain punctuation within a word (usually only apostrophe)
+ if(!IsAlpha(c) && !IsSpace(c) && (wcschr(tr->punct_within_word,c) == 0))
+ {
+ if(IsAlpha(prev_out))
+ {
+ if(tr->langopts.tone_numbers && IsDigit09(c) && !IsDigit09(next_in))
+ {
+ // allow a tone number as part of the word
+ }
+ else
+ {
+ c = ' '; // ensure we have an end-of-word terminator
+ space_inserted = 1;
+ }
+ }
+ }
+
+ if(iswdigit(prev_out))
+ {
+ if(!iswdigit(c) && (c != '.') && (c != ','))
+ {
+ c = ' '; // terminate digit string with a space
+ space_inserted = 1;
+ }
+ }
+ else
+ {
+ if(prev_in != ',')
+ {
+ decimal_sep_count = 0;
+ }
+ }
+
+ if((c == '[') && (next_in == '[') && option_phoneme_input)
+ {
+ phoneme_mode = FLAG_PHONEMES;
+ source_index++;
+ continue;
+ }
+
+ if(c == 0)
+ {
+ finished = 1;
+ c = ' ';
+ }
+ else
+ if(IsAlpha(c))
+ {
+ if(!IsAlpha(prev_out) || (tr->langopts.ideographs && ((c > 0x3040) || (prev_out > 0x3040))))
+ {
+ if(wcschr(tr->punct_within_word,prev_out) == 0)
+ letter_count = 0; // don't reset count for an apostrophy within a word
+
+ if((prev_out != ' ') && (wcschr(tr->punct_within_word,prev_out) == 0))
+ {
+ // start of word, insert space if not one there already
+ c = ' ';
+ space_inserted = 1;
+ }
+ else
+ {
+ if(iswupper(c))
+ word_flags |= FLAG_FIRST_UPPER;
+
+ if((prev_out == ' ') && iswdigit(sbuf[ix-2]) && !iswdigit(prev_in))
+ {
+ // word, following a number, but with a space between
+ // Add an extra space, to distinguish "2 a" from "2a"
+ sbuf[ix++] = ' ';
+ words[word_count].start++;
+ }
+ }
+ }
+
+ letter_count++;
+
+ if(iswupper(c))
+ {
+ c = towlower2(c);
+
+ if(tr->langopts.param[LOPT_SYLLABLE_CAPS])
+ {
+ if(syllable_marked == 0)
+ {
+ char_inserted = c;
+ c = 0x2c8; // stress marker
+ syllable_marked = 1;
+ }
+ }
+ else
+ {
+ if(iswlower(prev_in))
+ {
+ c = ' '; // lower case followed by upper case, treat as new word
+ space_inserted = 1;
+ prev_in2 = c;
+ }
+ else
+ if((c != ' ') && iswupper(prev_in) && iswlower(next_in) &&
+ (memcmp(&source[source_index],"s ",2) != 0)) // ENGLISH specific plural
+ {
+ c = ' '; // change from upper to lower case, start new word at the last uppercase
+ space_inserted = 1;
+ prev_in2 = c;
+ next_word_flags |= FLAG_NOSPACE;
+ }
+ }
+ }
+ else
+ {
+ if((all_upper_case) && (letter_count > 2))
+ {
+ if((c == 's') && (next_in==' '))
+ {
+ c = ' ';
+ all_upper_case |= FLAG_HAS_PLURAL;
+
+ if(sbuf[ix-1] == '\'')
+ sbuf[ix-1] = ' ';
+ }
+ else
+ all_upper_case = 0; // current word contains lower case letters, not "'s"
+ }
+ else
+ all_upper_case = 0;
+ }
+ }
+ else
+ if(c=='-')
+ {
+ if(IsAlpha(prev_in) && IsAlpha(next_in))
+ {
+ // '-' between two letters is a hyphen, treat as a space
+ word_flags |= FLAG_HYPHEN;
+ words[word_count-1].flags |= FLAG_HYPHEN_AFTER;
+ c = ' ';
+ }
+ else
+ if((prev_in==' ') && (next_in==' '))
+ {
+ // ' - ' dash between two spaces, treat as pause
+ c = ' ';
+ pre_pause_add = 4;
+ }
+ else
+ if(next_in=='-')
+ {
+ // double hyphen, treat as pause
+ source_index++;
+ c = ' ';
+ pre_pause_add = 4;
+ }
+ else
+ if((prev_out == ' ') && IsAlpha(sbuf[ix-2]) && !IsAlpha(prev_in))
+ {
+ // insert extra space between a word + space + hyphen, to distinguish 'a -2' from 'a-2'
+ sbuf[ix++] = ' ';
+ words[word_count].start++;
+ }
+ }
+ else
+ if(c == '\'')
+ {
+ if(iswalnum(prev_in) && IsAlpha(next_in))
+ {
+ // between two letters, consider apostrophe as part of the word
+ single_quoted = 0;
+ }
+ else
+ if((wcschr(tr->char_plus_apostrophe,prev_in) != 0) && (prev_out2 == ' '))
+ {
+ // consider single character plus apostrophe as a word
+ single_quoted = 0;
+ if(next_in == ' ')
+ {
+ source_index++; // skip following space
+ }
+ }
+ else
+ {
+ if((prev_out == 's') && (single_quoted==0))
+ {
+ // looks like apostrophe after an 's'
+ c = ' ';
+ }
+ else
+ {
+ if(IsSpace(prev_out))
+ single_quoted = 1;
+ else
+ single_quoted = 0;
+
+ pre_pause_add = 4; // single quote
+ c = ' ';
+ }
+ }
+ }
+ else
+ if(IsBracket(c))
+ {
+ pre_pause_add = 4;
+ c = ' ';
+ }
+ else
+ if(lookupwchar(breaks,c) != 0)
+ {
+ c = ' '; // various characters to treat as space
+ }
+ else
+ if(iswdigit(c))
+ {
+ if(tr->langopts.tone_numbers && IsAlpha(prev_out) && !IsDigit(next_in))
+ {
+ }
+ else
+ if((prev_out != ' ') && !iswdigit(prev_out))
+ {
+ if((prev_out != tr->langopts.decimal_sep) || ((decimal_sep_count > 0) && (tr->langopts.decimal_sep == ',')))
+ {
+ c = ' ';
+ space_inserted = 1;
+ }
+ else
+ {
+ decimal_sep_count = 1;
+ }
+ }
+ else
+ if((prev_out == ' ') && IsAlpha(sbuf[ix-2]) && !IsAlpha(prev_in))
+ {
+ // insert extra space between a word and a number, to distinguish 'a 2' from 'a2'
+ sbuf[ix++] = ' ';
+ words[word_count].start++;
+ }
+ }
+ }
+
+ if(IsSpace(c))
+ {
+ if(space_inserted)
+ {
+ source_index = prev_source_index; // rewind to the previous character
+ char_inserted = 0;
+ space_inserted = 0;
+ }
+
+ if(prev_out == ' ')
+ {
+ continue; // multiple spaces
+ }
+
+ // end of 'word'
+ sbuf[ix++] = ' ';
+
+ if((ix > words[word_count].start) && (word_count < N_CLAUSE_WORDS-1))
+ {
+ if(embedded_count > 0)
+ {
+ // there are embedded commands before this word
+ embedded_list[embedded_ix-1] |= 0x80; // terminate list of commands for this word
+ words[word_count].flags |= FLAG_EMBEDDED;
+ embedded_count = 0;
+ }
+ words[word_count].pre_pause = pre_pause;
+ words[word_count].flags |= (all_upper_case | word_flags | word_emphasis);
+ words[word_count].wmark = word_mark;
+
+ if(pre_pause > 0)
+ {
+ // insert an extra space before the word, to prevent influence from previous word across the pause
+ for(j=ix; j>words[word_count].start; j--)
+ {
+ sbuf[j] = sbuf[j-1];
+ }
+ sbuf[j] = ' ';
+ words[word_count].start++;
+ ix++;
+ }
+
+ word_count++;
+ words[word_count].start = ix;
+ words[word_count].flags = 0;
+
+ for(j=source_index; charix[j] <= 0; j++); // skip blanks
+ words[word_count].sourceix = charix[j];
+ k = 0;
+ while(charix[j] != 0)
+ {
+ // count the number of characters (excluding multibyte continuation bytes)
+ if(charix[j++] != -1)
+ k++;
+ }
+ words[word_count].length = k;
+
+ word_flags = next_word_flags;
+ next_word_flags = 0;
+ pre_pause = 0;
+ word_mark = 0;
+ all_upper_case = FLAG_ALL_UPPER;
+ syllable_marked = 0;
+ }
+ }
+ else
+ {
+ ix += utf8_out(c,&sbuf[ix]); // sbuf[ix++] = c;
+ }
+ if(pre_pause_add > pre_pause)
+ pre_pause = pre_pause_add;
+ pre_pause_add = 0;
+ }
+
+ if((word_count==0) && (embedded_count > 0))
+ {
+ // add a null 'word' to carry the embedded command flag
+ embedded_list[embedded_ix-1] |= 0x80;
+ words[word_count].flags |= FLAG_EMBEDDED;
+ word_count = 1;
+ }
+
+ tr->clause_end = &sbuf[ix-1];
+ sbuf[ix] = 0;
+ words[0].pre_pause = 0; // don't add extra pause at beginning of clause
+ words[word_count].pre_pause = 8;
+ if(word_count > 0)
+ words[word_count-1].flags |= FLAG_LAST_WORD;
+ words[0].flags |= FLAG_FIRST_WORD;
+
+ for(ix=0; ix<word_count; ix++)
+ {
+ int nx;
+ int c_temp;
+ char *pn;
+ char *pw;
+ static unsigned int break_numbers1 = 0x49249248;
+ static unsigned int break_numbers2 = 0x492492a8; // for languages which have numbers for 100,000 and 100,00,000
+ static unsigned int break_numbers3 = 0x49249268; // for languages which have numbers for 100,000 and 1,000,000
+ unsigned int break_numbers;
+ char number_buf[80];
+
+ // start speaking at a specified word position in the text?
+ count_words++;
+ if(skip_words > 0)
+ {
+ skip_words--;
+ if(skip_words == 0)
+ skipping_text = 0;
+ }
+ if(skipping_text)
+ continue;
+
+
+ // digits should have been converted to Latin alphabet ('0' to '9')
+ word = pw = &sbuf[words[ix].start];
+
+ if(iswdigit(word[0]) && (tr->langopts.numbers2 & NUM2_100000))
+ {
+ // Languages with 100000 numbers. Remove thousands separators so that we can insert them again later
+ pn = number_buf;
+ while(pn < &number_buf[sizeof(number_buf)-3])
+ {
+ if(iswdigit(*pw))
+ {
+ *pn++ = *pw++;
+ }
+ else
+ if((*pw == tr->langopts.thousands_sep) && (pw[1] == ' ') && iswdigit(pw[2]))
+ {
+ pw += 2;
+ ix++; // skip "word"
+ }
+ else
+ {
+ nx = pw - word;
+ memset(word,' ',nx);
+ nx = pn - number_buf;
+ memcpy(word,number_buf,nx);
+ break;
+ }
+ }
+ pw = word;
+ }
+
+ for(n_digits=0; iswdigit(word[n_digits]); n_digits++); // count consecutive digits
+
+ if((n_digits > 4) && (word[0] != '0'))
+ {
+ // word is entirely digits, insert commas and break into 3 digit "words"
+ number_buf[0] = ' ';
+ pn = &number_buf[1];
+ nx = n_digits;
+
+ if(tr->langopts.numbers2 & NUM2_100000a)
+ break_numbers = break_numbers3;
+ else
+ if(tr->langopts.numbers2 & NUM2_100000)
+ break_numbers = break_numbers2;
+ else
+ break_numbers = break_numbers1;
+
+ while(pn < &number_buf[sizeof(number_buf)-3])
+ {
+ if(!isdigit(c = *pw++) && (c != tr->langopts.decimal_sep))
+ break;
+
+ *pn++ = c;
+ if((--nx > 0) && (break_numbers & (1 << nx)))
+ {
+ if(tr->langopts.thousands_sep != ' ')
+ {
+ *pn++ = tr->langopts.thousands_sep;
+ }
+ *pn++ = ' ';
+ if(break_numbers & (1 << (nx-1)))
+ {
+ // the next group only has 1 digits (i.e. NUM2_10000), make it three
+ *pn++ = '0';
+ *pn++ = '0';
+ }
+ if(break_numbers & (1 << (nx-2)))
+ {
+ // the next group only has 2 digits (i.e. NUM2_10000), make it three
+ *pn++ = '0';
+ }
+ }
+ }
+ pn[0] = ' ';
+ pn[1] = 0;
+ word = pw;
+
+ for(pw = &number_buf[1]; pw < pn;)
+ {
+ TranslateWord2(tr, pw, &words[ix], words[ix].pre_pause,0 );
+ while(*pw++ != ' ');
+ words[ix].pre_pause = 0;
+ words[ix].flags = 0;
+ }
+ }
+ else
+ {
+ dict_flags = TranslateWord2(tr, word, &words[ix], words[ix].pre_pause, words[ix+1].pre_pause);
+
+ if(dict_flags & FLAG_SPELLWORD)
+ {
+ // redo the word, speaking single letters
+ for(pw = word; *pw != ' ';)
+ {
+ memset(number_buf,' ',9);
+ nx = utf8_in(&c_temp, pw);
+ memcpy(&number_buf[2],pw,nx);
+ TranslateWord2(tr, &number_buf[2], &words[ix], 0, 0 );
+ pw += nx;
+ }
+ }
+
+ if(dict_flags & FLAG_SKIPWORDS)
+ {
+ ix += dictionary_skipwords; // dictionary indicates skip next word(s)
+ }
+
+ if((dict_flags & FLAG_DOT) && (ix == word_count-1) && (terminator == CLAUSE_PERIOD))
+ {
+ // probably an abbreviation such as Mr. or B. rather than end of sentence
+ clause_pause = 10;
+ tone = 4;
+ }
+ }
+ }
+
+ for(ix=0; ix<2; ix++)
+ {
+ // terminate the clause with 2 PAUSE phonemes
+ PHONEME_LIST2 *p2;
+ p2 = &ph_list2[n_ph_list2 + ix];
+ p2->phcode = phonPAUSE;
+ p2->stress = 0;
+ p2->sourceix = 0;
+ p2->synthflags = 0;
+ }
+ n_ph_list2 += 2;
+
+ if(count_words == 0)
+ {
+ clause_pause = 0;
+ }
+ if(Eof() && ((word_count == 0) || (option_endpause==0)))
+ {
+ clause_pause = 10;
+ }
+
+ MakePhonemeList(tr, clause_pause, new_sentence);
+
+ if(embedded_count) // ???? is this needed
+ {
+ phoneme_list[n_phoneme_list-2].synthflags = SFLAG_EMBEDDED;
+ embedded_list[embedded_ix-1] |= 0x80;
+ }
+
+
+ prev_clause_pause = clause_pause;
+
+ *tone_out = tone;
+
+ new_sentence = 0;
+ if(terminator & CLAUSE_BIT_SENTENCE)
+ {
+ new_sentence = 1; // next clause is a new sentence
+ }
+
+
+ if(voice_change != NULL)
+ {
+ // return new voice name if an embedded voice change command terminated the clause
+ if(terminator & CLAUSE_BIT_VOICE)
+ *voice_change = &source[source_index];
+ else
+ *voice_change = NULL;
+ }
+
+ if(Eof() || (vp_input==NULL))
+ return(NULL);
+
+ if(option_multibyte == espeakCHARS_WCHAR)
+ return((void *)p_wchar_input);
+ else
+ return((void *)p_textinput);
+} // end of TranslateClause
+
+
+
+
+
+void InitText(int control)
+{//=======================
+ count_sentences = 0;
+ count_words = 0;
+ end_character_position = 0;
+ skip_sentences = 0;
+ skip_marker[0] = 0;
+ skip_words = 0;
+ skip_characters = 0;
+ skipping_text = 0;
+ new_sentence = 1;
+
+ prev_clause_pause = 0;
+
+ option_sayas = 0;
+ option_sayas2 = 0;
+ option_emphasis = 0;
+ word_emphasis = 0;
+
+ InitText2();
+
+ if((control & espeakKEEP_NAMEDATA) == 0)
+ {
+ InitNamedata();
+ }
+}
+
diff --git a/Plugins/eSpeak/eSpeak/translate.h b/Plugins/eSpeak/eSpeak/translate.h
new file mode 100644
index 0000000..afa59ca
--- /dev/null
+++ b/Plugins/eSpeak/eSpeak/translate.h
@@ -0,0 +1,576 @@
+/***************************************************************************
+ * Copyright (C) 2005 to 2007 by Jonathan Duddington *
+ * email: jonsd@users.sourceforge.net *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 3 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, see: *
+ * <http://www.gnu.org/licenses/>. *
+ ***************************************************************************/
+
+
+#define L(c1,c2) (c1<<8)+c2 // combine two characters into an integer for translator name
+
+#define CTRL_EMBEDDED 0x01 // control character at the start of an embedded command
+#define REPLACED_E 'E' // 'e' replaced by silent e
+
+#define N_WORD_PHONEMES 160 // max phonemes in a word
+#define N_WORD_BYTES 160 // max bytes for the UTF8 characters in a word
+#define N_CLAUSE_WORDS 300 // max words in a clause
+#define N_RULE_GROUP2 120 // max num of two-letter rule chains
+#define N_HASH_DICT 1024
+#define N_CHARSETS 20
+#define N_LETTER_GROUPS 26
+
+
+/* dictionary flags, word 1 */
+// bits 0-3 stressed syllable, bit 6=unstressed
+#define FLAG_SKIPWORDS 0x80
+#define FLAG_PREPAUSE 0x100
+
+#define FLAG_ONLY 0x200
+#define FLAG_ONLY_S 0x400
+#define BITNUM_FLAG_ONLY 9 // bit 9 is set
+#define BITNUM_FLAG_ONLY_S 10 // bit 10 is set
+
+#define FLAG_STRESS_END 0x800 /* full stress if at end of clause */
+#define FLAG_STRESS_END2 0x1000 /* full stress if at end of clause, or only followed by unstressed */
+#define FLAG_UNSTRESS_END 0x2000 /* reduce stress at end of clause */
+#define FLAG_ATEND 0x4000 /* use this pronunciation if at end of clause */
+#define FLAG_SPELLWORD 0x8000 // re-translate the word as individual letters, separated by spaces
+
+#define FLAG_DOT 0x10000 /* ignore '.' after word (abbreviation) */
+#define FLAG_ABBREV 0x20000 // spell as letters, even with a vowel, OR use specified pronunciation rather than split into letters
+#define FLAG_STEM 0x40000 // must have a suffix
+
+#define FLAG_DOUBLING 0x80000 // doubles the following consonant
+#define FLAG_ALT_TRANS 0x100000 // language specific
+#define FLAG_ALT2_TRANS 0x200000 // language specific
+
+#define FLAG_MAX3 0x08000000 // limit to 3 repeats
+#define FLAG_PAUSE1 0x10000000 // shorter prepause
+#define FLAG_TEXTMODE 0x20000000 // word translates to replacement text, not phonemes
+#define BITNUM_FLAG_TEXTMODE 29
+
+#define FLAG_FOUND_ATTRIBUTES 0x40000000 // word was found in the dictionary list (has attributes)
+#define FLAG_FOUND 0x80000000 // pronunciation was found in the dictionary list
+
+// dictionary flags, word 2
+#define FLAG_VERBF 0x1 /* verb follows */
+#define FLAG_VERBSF 0x2 /* verb follows, may have -s suffix */
+#define FLAG_NOUNF 0x4 /* noun follows */
+#define FLAG_PASTF 0x8 /* past tense follows */
+#define FLAG_VERB 0x10 /* pronunciation for verb */
+#define FLAG_NOUN 0x20 /* pronunciation for noun */
+#define FLAG_PAST 0x40 /* pronunciation for past tense */
+#define FLAG_VERB_EXT 0x100 /* extend the 'verb follows' */
+#define FLAG_CAPITAL 0x200 /* pronunciation if initial letter is upper case */
+#define FLAG_ALLCAPS 0x400 // only if the word is all capitals
+#define BITNUM_FLAG_ALLCAPS 0x2a
+#define FLAG_ACCENT 0x800 // character name is base-character name + accent name
+#define FLAG_HYPHENATED 0x1000 // multiple-words, but needs hyphen between parts 1 and 2
+#define BITNUM_FLAG_HYPHENATED 0x2c
+
+
+// wordflags, flags in source word
+#define FLAG_ALL_UPPER 0x1 /* no lower case letters in the word */
+#define FLAG_FIRST_UPPER 0x2 /* first letter is upper case */
+#define FLAG_UPPERS 0x3 // FLAG_ALL_UPPER | FLAG_FIRST_UPPER
+#define FLAG_HAS_PLURAL 0x4 /* upper-case word with s or 's lower-case ending */
+#define FLAG_PHONEMES 0x8 /* word is phonemes */
+#define FLAG_LAST_WORD 0x10 /* last word in clause */
+#define FLAG_EMBEDDED 0x40 /* word is preceded by embedded commands */
+#define FLAG_HYPHEN 0x80
+#define FLAG_NOSPACE 0x100 // word is not seperated from previous word by a space
+#define FLAG_FIRST_WORD 0x200 // first word in clause
+#define FLAG_FOCUS 0x400 // the focus word of a clause
+#define FLAG_EMPHASIZED 0x800
+#define FLAG_EMPHASIZED2 0xc00 // FLAG_FOCUS | FLAG_EMPHASIZED
+#define FLAG_DONT_SWITCH_TRANSLATOR 0x1000
+#define FLAG_SUFFIX_REMOVED 0x2000
+#define FLAG_HYPHEN_AFTER 0x4000
+
+#define FLAG_NO_TRACE 0x10000 // passed to TranslateRules() to suppress dictionary lookup printout
+#define FLAG_NO_PREFIX 0x20000
+
+// prefix/suffix flags (bits 8 to 14, bits 16 to 22) don't use 0x8000, 0x800000
+#define SUFX_E 0x0100 // e may have been added
+#define SUFX_I 0x0200 // y may have been changed to i
+#define SUFX_P 0x0400 // prefix
+#define SUFX_V 0x0800 // suffix means use the verb form pronunciation
+#define SUFX_D 0x1000 // previous letter may have been doubles
+#define SUFX_F 0x2000 // verb follows
+#define SUFX_Q 0x4000 // don't retranslate
+#define SUFX_T 0x10000 // don't affect the stress position in the stem
+#define SUFX_B 0x20000 // break, this character breaks the word into stem and suffix (used with SUFX_P)
+
+#define FLAG_ALLOW_TEXTMODE 0x02 // allow dictionary to translate to text rather than phonemes
+#define FLAG_SUFX 0x04
+#define FLAG_SUFX_S 0x08
+#define FLAG_SUFX_E_ADDED 0x10
+
+
+// codes in dictionary rules
+#define RULE_PRE 1
+#define RULE_POST 2
+#define RULE_PHONEMES 3
+#define RULE_PH_COMMON 4 // At start of rule. Its phoneme string is used by subsequent rules
+#define RULE_CONDITION 5 // followed by condition number (byte)
+#define RULE_GROUP_START 6
+#define RULE_GROUP_END 7
+#define RULE_LINENUM 8 // next 2 bytes give a line number, for debugging purposes
+
+#define RULE_SPACE 32 // ascii space
+#define RULE_SYLLABLE 9
+#define RULE_STRESSED 10
+#define RULE_DOUBLE 11
+#define RULE_INC_SCORE 12
+#define RULE_DEL_FWD 13
+#define RULE_ENDING 14
+#define RULE_DIGIT 15 // D digit
+#define RULE_NONALPHA 16 // Z non-alpha
+#define RULE_LETTERGP 17 // A B C H F G Y letter group number
+#define RULE_LETTERGP2 18 // L + letter group number
+#define RULE_CAPITAL 19 // word starts with a capital letter
+#define RULE_REPLACEMENTS 20 // section for character replacements
+#define RULE_NO_SUFFIX 24 // N
+#define RULE_NOTVOWEL 25 // K
+#define RULE_IFVERB 26 // V
+#define RULE_ALT1 28 // T word has $alt attribute
+#define RULE_NOVOWELS 29 // X no vowels up to word boundary
+#define RULE_SPELLING 31 // W while spelling letter-by-letter
+#define RULE_LAST_RULE 31
+
+#define LETTERGP_A 0
+#define LETTERGP_B 1
+#define LETTERGP_C 2
+#define LETTERGP_H 3
+#define LETTERGP_F 4
+#define LETTERGP_G 5
+#define LETTERGP_Y 6
+#define LETTERGP_VOWEL2 7
+
+
+// Punctuation types returned by ReadClause()
+// bits 0-7 pause x 10mS, bits 12-14 intonation type,
+// bit 19=sentence, bit 18=clause, bits 17=voice change
+// bit 16 used to distinguish otherwise identical types
+// bit 20= punctuation character can be inside a word (Armenian)
+#define CLAUSE_BIT_SENTENCE 0x80000
+#define CLAUSE_BIT_VOICE 0x20000
+#define PUNCT_IN_WORD 0x100000
+
+#define CLAUSE_NONE 0 + 0x04000
+#define CLAUSE_PARAGRAPH 70 + 0x80000
+#define CLAUSE_EOF 35 + 0x90000
+#define CLAUSE_VOICE 0 + 0x24000
+#define CLAUSE_PERIOD 35 + 0x80000
+#define CLAUSE_COMMA 20 + 0x41000
+#define CLAUSE_SHORTCOMMA 5 + 0x41000
+#define CLAUSE_QUESTION 35 + 0x82000
+#define CLAUSE_EXCLAMATION 40 + 0x83000
+#define CLAUSE_COLON 30 + 0x40000
+#ifdef PLATFORM_RISCOS
+#define CLAUSE_SEMICOLON 30 + 0x40000
+#else
+#define CLAUSE_SEMICOLON 30 + 0x41000
+#endif
+
+#define SAYAS_CHARS 0x12
+#define SAYAS_GLYPHS 0x13
+#define SAYAS_SINGLE_CHARS 0x14
+#define SAYAS_KEY 0x24
+#define SAYAS_DIGITS 0x40 // + number of digits
+#define SAYAS_DIGITS1 0xc1
+
+#define CHAR_EMPHASIS 0x0530 // this is an unused character code
+
+// Rule:
+// [4] [match] [1 pre] [2 post] [3 phonemes] 0
+// match 1 pre 2 post 0 - use common phoneme string
+// match 1 pre 2 post 3 0 - empty phoneme string
+
+typedef const char * constcharptr;
+
+typedef struct {
+ int points;
+ const char *phonemes;
+ int end_type;
+ char *del_fwd;
+} MatchRecord;
+
+
+// used to mark words with the source[] buffer
+typedef struct{
+ unsigned short start;
+ unsigned short sourceix;
+ unsigned short flags;
+ unsigned char pre_pause;
+ unsigned char wmark;
+ unsigned char length;
+} WORD_TAB;
+
+// a clause translated into phoneme codes (first stage)
+typedef struct {
+ unsigned char phcode;
+ unsigned char stress;
+ unsigned char tone_number;
+ unsigned char synthflags;
+ unsigned short sourceix;
+} PHONEME_LIST2;
+
+
+typedef struct {
+ int type;
+ int parameter[N_SPEECH_PARAM];
+} PARAM_STACK;
+
+extern PARAM_STACK param_stack[];
+extern const int param_defaults[N_SPEECH_PARAM];
+
+
+
+#define N_LOPTS 15
+#define LOPT_DIERESES 1
+ // 1=remove [:] from unstressed syllables, 2= remove from unstressed or non-penultimate syllables
+ // bit 4=0, if stress < 4, bit 4=1, if not the highest stress in the word
+#define LOPT_IT_LENGTHEN 2
+
+ // 1=german
+#define LOPT_PREFIXES 3
+
+ // non-zero, change voiced/unoiced to match last consonant in a cluster
+ // bit 1=LANG=ru, don't propagate over [v]
+ // bit 2=don't propagate acress word boundaries
+ // bit 3=LANG=pl, propagate over liquids and nasals
+#define LOPT_REGRESSIVE_VOICING 4
+
+ // 0=default, 1=no check, other allow this character as an extra initial letter (default is 's')
+#define LOPT_UNPRONOUNCABLE 5
+
+ // select length_mods tables, (length_mod_tab) + (length_mod_tab0 * 100)
+#define LOPT_LENGTH_MODS 6
+
+ // increase this to prevent sonorants being shortened before shortened (eg. unstressed) vowels
+#define LOPT_SONORANT_MIN 7
+
+ // don't break vowels at word boundary
+#define LOPT_WORD_MERGE 8
+
+ // max. amplitude for vowel at the end of a clause
+#define LOPT_MAXAMP_EOC 9
+
+ // bit 0=reduce even if phonemes are specified in the **_list file
+ // bit 1=don't reduce the strongest vowel in a word which is marked 'unstressed'
+#define LOPT_REDUCE 10
+
+ // LANG=cs,sk combine some prepositions with the following word, if the combination has N or fewer syllables
+ // bits 0-3 N syllables
+ // bit 4=only if the second word has $alt attribute
+ // bit 5=not if the second word is end-of-sentence
+#define LOPT_COMBINE_WORDS 11
+
+ // change [t] when followed by unstressed vowel
+#define LOPT_REDUCE_T 12
+
+ // stressed syllable is indicated by capitals
+#define LOPT_SYLLABLE_CAPS 13
+
+ // bit 0=Italian "syntactic doubling" of consoants in the word after a word marked with $double attribute
+ // bit 1=also after a word which ends with a stressed vowel
+#define LOPT_IT_DOUBLING 14
+
+
+
+typedef struct {
+// bits0-2 separate words with (1=pause_vshort, 2=pause_short, 3=pause, 4=pause_long 5=[?] phonemme)
+// bit 3=don't use linking phoneme
+// bit4=longer pause before STOP, VSTOP,FRIC
+// bit5=length of a final vowel doesn't depend on the next phoneme
+ int word_gap;
+ int vowel_pause;
+ int stress_rule; // 1=first syllable, 2=penultimate, 3=last
+
+// bit0=don't stress monosyllables,
+// bit1=don't set diminished stress,
+// bit2=mark unstressed final syllables as diminished
+// bit4=don't allow secondary stress on last syllable
+// bit5-don't use automatic secondary stress
+// bit6=light syllable followed by heavy, move secondary stress to the heavy syllable. LANG=Finnish
+// bit8=stress last syllable if it doesn't end in a vowel
+// bit9=stress last syllable if it doesn't end in vowel or "s" or "n" LANG=Spanish
+// bit12= In a 2-syllable word, if one has primary stress then give the other secondary stress
+// bit13= If there is only one syllable before the primary stress, give it a secondary stress
+// bit15= Give stress to the first unstressed syllable
+// bit16= Don't diminish consecutive syllables within a word.
+// bit17= "priority" stress reduces other primary stress to "unstressed" not "secondary"
+// bit18= don't lengthen short vowels more than long vowels at end-of-clause
+// bit19=stress on final syllable if it has a long vowel, but previous syllable has a short vowel
+
+ int stress_flags;
+ int unstressed_wd1; // stress for $u word of 1 syllable
+ int unstressed_wd2; // stress for $u word of >1 syllable
+ int param[N_LOPTS];
+ unsigned char *length_mods;
+ unsigned char *length_mods0;
+
+#define NUM_ROMAN 0x20000
+#define NUM_ROMAN_UC 0x40000
+#define NUM_NOPAUSE 0x80000
+#define NUM_ROMAN_AFTER 0x200000
+
+ // bits0-1=which numbers routine to use.
+ // bit2= thousands separator must be space
+ // bit3= , decimal separator, not .
+ // bit4=use three-and-twenty rather than twenty-three
+ // bit5='and' between tens and units
+ // bit6=add "and" after hundred or thousand
+ // bit7=don't have "and" both after hundreds and also between tens and units
+ // bit8=only one primary stress in tens+units
+ // bit9=only one vowel betwen tens and units
+ // bit10=omit "one" before "hundred"
+ // bit11=say 19** as nineteen hundred
+ // bit12=allow space as thousands separator (in addition to langopts.thousands_sep)
+ // bits13-15 post-decimal-digits 0=single digits, 1=(LANG=it) 2=(LANG=pl) 3=(LANG=ro)
+ // bit16=dot after number indicates ordinal
+ // bit17=recognize roman numbers
+ // bit18=Roman numbers only if upper case
+ // bit19=don't add pause after a number
+ // bit20='and' before hundreds
+ // bit21= say "roman" after the number, not before
+ int numbers;
+
+#define NUM2_100000 0x800 // numbers for 100,000 and 10,000,000
+#define NUM2_100000a 0xc00 // numbers for 100,000 and 1,000,000
+ // bits 1-4 use variant form of numbers before thousands,millions,etc.
+ // bit6=(LANG=pl) two forms of plural, M or MA
+ // bit7=(LANG-ru) use MB for 1 thousand, million, etc
+ // bit8=(LANG=cs,sk) two forms of plural, M or MA
+ // bit9=(LANG=rw) say "thousand" and "million" before its number, not after
+ // bit10=(LANG=sw) special word for 100,000 and 1,000,000
+ // bit11=(LANG=hi) special word for 100,000 and 10,000,000
+ int numbers2;
+
+ int max_roman;
+ int thousands_sep;
+ int decimal_sep;
+
+ // bit 0, accent name before the letter name, bit 1 "capital" after letter name
+ int accents;
+
+ int tone_language; // 1=tone language
+ int intonation_group;
+ int long_stop; // extra mS pause for a lengthened stop
+ int phoneme_change; // TEST, change phonemes, after translation
+ char max_initial_consonants;
+ char spelling_stress; // 0=default, 1=stress first letter
+ char tone_numbers;
+ char ideographs; // treat as separate words
+ char textmode; // the meaning of FLAG_TEXTMODE is reversed (to save data when *_list file is compiled)
+ int testing; // testing options: bit 1= specify stressed syllable in the form: "outdoor/2"
+ int listx; // compile *_listx after *list
+ const unsigned int *replace_chars; // characters to be substitutes
+ const char *ascii_language; // switch to this language for Latin characters
+} LANGUAGE_OPTIONS;
+
+
+// a parameter of ChangePhonemes()
+typedef struct {
+ int flags;
+ unsigned char stress; // stress level of this vowel
+ unsigned char stress_highest; // the highest stress level of a vowel in this word
+ unsigned char n_vowels; // number of vowels in the word
+ unsigned char vowel_this; // syllable number of this vowel (counting from 1)
+ unsigned char vowel_stressed; // syllable number of the highest stressed vowel
+} CHANGEPH;
+
+
+
+#define NUM_SEP_DOT 0x0008 // . , for thousands and decimal separator
+#define NUM_SEP_SPACE 0x1000 // allow space as thousands separator (in addition to langopts.thousands_sep)
+#define NUM_DEC_IT 0x2000 // (LANG=it) speak post-decimal-point digits as a combined number not as single digits
+
+struct Translator
+{//=============
+
+ LANGUAGE_OPTIONS langopts;
+ int translator_name;
+ int transpose_offset;
+ int transpose_max;
+ int transpose_min;
+
+ char phon_out[300];
+ char phonemes_repeat[20];
+ int phonemes_repeat_count;
+
+ unsigned char stress_amps[8];
+ unsigned char stress_amps_r[8];
+ short stress_lengths[8];
+ int dict_condition; // conditional apply some pronunciation rules and dict.lookups
+ const unsigned short *charset_a0; // unicodes for characters 0xa0 to oxff
+ const wchar_t *char_plus_apostrophe; // single chars + apostrophe treated as words
+ const wchar_t *punct_within_word; // allow these punctuation characters within words
+
+// holds properties of characters: vowel, consonant, etc for pronunciation rules
+ unsigned char letter_bits[256];
+ int letter_bits_offset;
+ const wchar_t *letter_groups[8];
+
+ /* index1=option, index2 by 0=. 1=, 2=?, 3=! 4=none */
+#define INTONATION_TYPES 8
+#define PUNCT_INTONATIONS 6
+ unsigned char punct_to_tone[INTONATION_TYPES][PUNCT_INTONATIONS];
+
+ char *data_dictrules; // language_1 translation rules file
+ char *data_dictlist; // language_2 dictionary lookup file
+ char *dict_hashtab[N_HASH_DICT]; // hash table to index dictionary lookup file
+ char *letterGroups[N_LETTER_GROUPS];
+
+ // groups1 and groups2 are indexes into data_dictrules, set up by InitGroups()
+ // the two-letter rules for each letter must be consecutive in the language_rules source
+
+ char *groups1[256]; // translation rule lists, index by single letter
+ char *groups2[N_RULE_GROUP2]; // translation rule lists, indexed by two-letter pairs
+ unsigned int groups2_name[N_RULE_GROUP2]; // the two letter pairs for groups2[]
+ int n_groups2; // number of groups2[] entries used
+
+ unsigned char groups2_count[256]; // number of 2 letter groups for this initial letter
+ unsigned char groups2_start[256]; // index into groups2
+
+
+ int expect_verb;
+ int expect_past; // expect past tense
+ int expect_verb_s;
+ int expect_noun;
+ int prev_last_stress;
+ char *clause_end;
+
+ int word_vowel_count; // number of vowels so far
+ int word_stressed_count; // number of vowels so far which could be stressed
+
+ int clause_upper_count; // number of upper case letters in the clause
+ int clause_lower_count; // number of lower case letters in the clause
+
+ int prepause_timeout;
+ int end_stressed_vowel; // word ends with stressed vowel
+ int prev_dict_flags; // dictionary flags from previous word
+}; // end of class Translator
+
+
+extern int option_tone2;
+#define OPTION_EMPHASIZE_ALLCAPS 0x100
+#define OPTION_EMPHASIZE_PENULTIMATE 0x200
+extern int option_tone_flags;
+extern int option_waveout;
+extern int option_quiet;
+extern int option_phonemes;
+extern int option_phoneme_events;
+extern int option_linelength; // treat lines shorter than this as end-of-clause
+extern int option_multibyte;
+extern int option_capitals;
+extern int option_punctuation;
+extern int option_endpause;
+extern int option_ssml;
+extern int option_phoneme_input; // allow [[phonemes]] in input text
+extern int option_phoneme_variants;
+extern int option_sayas;
+extern int option_wordgap;
+
+extern int count_characters;
+extern int count_words;
+extern int count_sentences;
+extern int skip_characters;
+extern int skip_words;
+extern int skip_sentences;
+extern int skipping_text;
+extern int end_character_position;
+extern int clause_start_char;
+extern int clause_start_word;
+extern char *namedata;
+
+
+
+#define N_MARKER_LENGTH 50 // max.length of a mark name
+extern char skip_marker[N_MARKER_LENGTH];
+
+#define N_PUNCTLIST 60
+extern wchar_t option_punctlist[N_PUNCTLIST]; // which punctuation characters to announce
+extern unsigned char punctuation_to_tone[INTONATION_TYPES][PUNCT_INTONATIONS];
+
+extern Translator *translator;
+extern Translator *translator2;
+extern const unsigned short *charsets[N_CHARSETS];
+extern char dictionary_name[40];
+extern char ctrl_embedded; // to allow an alternative CTRL for embedded commands
+extern char *p_textinput;
+extern wchar_t *p_wchar_input;
+extern int dictionary_skipwords;
+
+extern int (* uri_callback)(int, const char *, const char *);
+extern int (* phoneme_callback)(const char *);
+extern void SetLengthMods(Translator *tr, int value);
+
+void LoadConfig(void);
+int TransposeAlphabet(char *text, int offset, int min, int max);
+int utf8_in(int *c, const char *buf);
+int utf8_in2(int *c, const char *buf, int backwards);
+int utf8_out(unsigned int c, char *buf);
+int utf8_nbytes(const char *buf);
+int lookupwchar(const unsigned short *list,int c);
+int Eof(void);
+char *strchr_w(const char *s, int c);
+int IsBracket(int c);
+void InitNamedata(void);
+void InitText(int flags);
+void InitText2(void);
+int IsDigit(unsigned int c);
+int IsAlpha(unsigned int c);
+int isspace2(unsigned int c);
+int towlower2(unsigned int c);
+void GetTranslatedPhonemeString(char *phon_out, int n_phon_out);
+
+Translator *SelectTranslator(const char *name);
+int SetTranslator2(const char *name);
+void DeleteTranslator(Translator *tr);
+int Lookup(Translator *tr, const char *word, char *ph_out);
+
+int TranslateNumber(Translator *tr, char *word1, char *ph_out, unsigned int *flags, int wflags);
+int TranslateRoman(Translator *tr, char *word, char *ph_out);
+
+void ChangeWordStress(Translator *tr, char *word, int new_stress);
+void SetSpellingStress(Translator *tr, char *phonemes, int control, int n_chars);
+int TranslateLetter(Translator *tr, char *letter, char *phonemes, int control, int word_length);
+void LookupLetter(Translator *tr, unsigned int letter, int next_byte, char *ph_buf);
+void LookupAccentedLetter(Translator *tr, unsigned int letter, char *ph_buf);
+
+int LoadDictionary(Translator *tr, const char *name, int no_error);
+int LookupDictList(Translator *tr, char **wordptr, char *ph_out, unsigned int *flags, int end_flags, WORD_TAB *wtab);
+
+void MakePhonemeList(Translator *tr, int post_pause, int new_sentence);
+int ChangePhonemes_ru(Translator *tr, PHONEME_LIST2 *phlist, int n_ph, int index, PHONEME_TAB *ph, CHANGEPH *ch);
+void ApplySpecialAttribute(Translator *tr, char *phonemes, int dict_flags);
+void AppendPhonemes(Translator *tr, char *string, int size, const char *ph);
+
+void CalcLengths(Translator *tr);
+void CalcPitches(Translator *tr, int clause_tone);
+
+int RemoveEnding(Translator *tr, char *word, int end_type, char *word_copy);
+int Unpronouncable(Translator *tr, char *word);
+void SetWordStress(Translator *tr, char *output, unsigned int dictionary_flags, int tonic, int prev_stress);
+int TranslateRules(Translator *tr, char *p, char *phonemes, int size, char *end_phonemes, int end_flags, unsigned int *dict_flags);
+int TranslateWord(Translator *tr, char *word1, int next_pause, WORD_TAB *wtab);
+void *TranslateClause(Translator *tr, FILE *f_text, const void *vp_input, int *tone, char **voice_change);
+int ReadClause(Translator *tr, FILE *f_in, char *buf, short *charix, int n_buf, int *tone_type);
+
+void SetVoiceStack(espeak_VOICE *v);
+
+extern FILE *f_trans; // for logging
diff --git a/Plugins/eSpeak/eSpeak/voice.h b/Plugins/eSpeak/eSpeak/voice.h
new file mode 100644
index 0000000..2f647c6
--- /dev/null
+++ b/Plugins/eSpeak/eSpeak/voice.h
@@ -0,0 +1,81 @@
+/***************************************************************************
+ * Copyright (C) 2005 to 2007 by Jonathan Duddington *
+ * email: jonsd@users.sourceforge.net *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 3 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write see: *
+ * <http://www.gnu.org/licenses/>. *
+ ***************************************************************************/
+
+
+
+typedef struct {
+ char v_name[40];
+
+ int phoneme_tab_ix; // phoneme table number
+ int pitch_base; // Hz<<12
+ int pitch_range; // standard = 0x1000
+
+ int speedf1;
+ int speedf2;
+ int speedf3;
+
+ int flutter;
+ int roughness;
+ int echo_delay;
+ int echo_amp;
+ int n_harmonic_peaks; // highest formant which is formed from adding harmonics
+ int peak_shape; // alternative shape for formant peaks (0=standard 1=squarer)
+ int voicing; // 100% = 64, level of formant-synthesized sound
+ int formant_factor; // adjust nominal formant frequencies by this because of the voice's pitch (256ths)
+ int consonant_amp; // amplitude of unvoiced consonants
+ int consonant_ampv; // amplitude of the noise component of voiced consonants
+ int klatt[8];
+
+ // parameters used by Wavegen
+ short freq[N_PEAKS]; // 100% = 256
+ short height[N_PEAKS]; // 100% = 256
+ short width[N_PEAKS]; // 100% = 256
+ short freqadd[N_PEAKS]; // Hz
+
+ // copies without temporary adjustments from embedded commands
+ short freq2[N_PEAKS]; // 100% = 256
+ short height2[N_PEAKS]; // 100% = 256
+ short width2[N_PEAKS]; // 100% = 256
+
+ int breath[N_PEAKS]; // amount of breath for each formant. breath[0] indicates whether any are set.
+ int breathw[N_PEAKS]; // width of each breath formant
+
+ // This table provides the opportunity for tone control.
+ // Adjustment of harmonic amplitudes, steps of 8Hz
+ // value of 128 means no change
+ #define N_TONE_ADJUST 1000
+ unsigned char tone_adjust[N_TONE_ADJUST]; // 8Hz steps * 1000 = 8kHz
+
+} voice_t;
+
+// percentages shown to user, ix=N_PEAKS means ALL peaks
+extern USHORT voice_pcnt[N_PEAKS+1][3];
+
+
+extern voice_t *voice;
+extern int tone_points[12];
+
+const char *SelectVoice(espeak_VOICE *voice_select, int *found);
+espeak_VOICE *SelectVoiceByName(espeak_VOICE **voices, const char *name);
+voice_t *LoadVoice(const char *voice_name, int control);
+voice_t *LoadVoiceVariant(const char *voice_name, int variant);
+void DoVoiceChange(voice_t *v);
+void WavegenSetVoice(voice_t *v);
+void ReadTonePoints(char *string, int *tone_pts);
+
diff --git a/Plugins/eSpeak/eSpeak/voices.cpp b/Plugins/eSpeak/eSpeak/voices.cpp
new file mode 100644
index 0000000..74173ca
--- /dev/null
+++ b/Plugins/eSpeak/eSpeak/voices.cpp
@@ -0,0 +1,1743 @@
+/***************************************************************************
+ * Copyright (C) 2005 to 2007 by Jonathan Duddington *
+ * email: jonsd@users.sourceforge.net *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 3 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write see: *
+ * <http://www.gnu.org/licenses/>. *
+ ***************************************************************************/
+
+#include "StdAfx.h"
+
+#include "stdio.h"
+#include "ctype.h"
+#include "wctype.h"
+#include "string.h"
+#include "stdlib.h"
+#include "speech.h"
+
+#ifdef PLATFORM_WINDOWS
+#include "windows.h"
+#else
+#ifdef PLATFORM_RISCOS
+#include "kernel.h"
+#else
+#include "dirent.h"
+#endif
+#endif
+
+#include "speak_lib.h"
+#include "phoneme.h"
+#include "synthesize.h"
+#include "voice.h"
+#include "translate.h"
+
+
+MNEM_TAB genders [] = {
+ {"unknown", 0},
+ {"male", 1},
+ {"female", 2},
+ {NULL, 0 }};
+
+int tone_points[12] = {600,170, 1200,135, 2000,110, 3000,110, -1,0};
+//int tone_points[12] = {250,200, 400,170, 600,170, 1200,135, 2000,110, -1,0};
+
+// limit the rate of change for each formant number
+//static int formant_rate_22050[9] = {50, 104, 165, 230, 220, 220, 220, 220, 220}; // values for 22kHz sample rate
+//static int formant_rate_22050[9] = {240, 180, 180, 180, 180, 180, 180, 180, 180}; // values for 22kHz sample rate
+static int formant_rate_22050[9] = {240, 170, 170, 170, 170, 170, 170, 170, 170}; // values for 22kHz sample rate
+int formant_rate[9]; // values adjusted for actual sample rate
+
+
+
+#define DEFAULT_LANGUAGE_PRIORITY 5
+#define N_VOICES_LIST 150
+static int n_voices_list = 0;
+static espeak_VOICE *voices_list[N_VOICES_LIST];
+static int len_path_voices;
+
+espeak_VOICE voice_selected;
+
+
+enum {
+ V_NAME = 1,
+ V_LANGUAGE,
+ V_GENDER,
+ V_TRANSLATOR,
+ V_PHONEMES,
+ V_DICTIONARY,
+
+// these affect voice quality, are independent of language
+ V_FORMANT,
+ V_PITCH,
+ V_ECHO,
+ V_FLUTTER,
+ V_ROUGHNESS,
+ V_CLARITY,
+ V_TONE,
+ V_VOICING,
+ V_BREATH,
+ V_BREATHW,
+
+// these override defaults set by the translator
+ V_WORDGAP,
+ V_INTONATION,
+ V_STRESSLENGTH,
+ V_STRESSAMP,
+ V_STRESSADD,
+ V_DICTRULES,
+ V_STRESSRULE,
+ V_CHARSET,
+ V_NUMBERS,
+ V_OPTION,
+
+ V_MBROLA,
+ V_KLATT,
+ V_FAST,
+
+// these need a phoneme table to have been specified
+ V_REPLACE,
+ V_CONSONANTS
+};
+
+
+
+typedef struct {
+ const char *mnem;
+ int data;
+} keywtab_t;
+
+static keywtab_t keyword_tab[] = {
+ {"name", V_NAME},
+ {"language", V_LANGUAGE},
+ {"gender", V_GENDER},
+
+ {"formant", V_FORMANT},
+ {"pitch", V_PITCH},
+ {"phonemes", V_PHONEMES},
+ {"translator", V_TRANSLATOR},
+ {"dictionary", V_DICTIONARY},
+ {"stressLength", V_STRESSLENGTH},
+ {"stressAmp", V_STRESSAMP},
+ {"stressAdd", V_STRESSADD},
+ {"intonation", V_INTONATION},
+ {"dictrules", V_DICTRULES},
+ {"stressrule", V_STRESSRULE},
+ {"charset", V_CHARSET},
+ {"replace", V_REPLACE},
+ {"words", V_WORDGAP},
+ {"echo", V_ECHO},
+ {"flutter", V_FLUTTER},
+ {"roughness", V_ROUGHNESS},
+ {"clarity", V_CLARITY},
+ {"tone", V_TONE},
+ {"voicing", V_VOICING},
+ {"breath", V_BREATH},
+ {"breathw", V_BREATHW},
+ {"numbers", V_NUMBERS},
+ {"option", V_OPTION},
+ {"mbrola", V_MBROLA},
+ {"consonants", V_CONSONANTS},
+ {"klatt", V_KLATT},
+ {"fast_test", V_FAST},
+
+ // these just set a value in langopts.param[]
+ {"l_dieresis", 0x100+LOPT_DIERESES},
+// {"l_lengthen", 0x100+LOPT_IT_LENGTHEN},
+ {"l_prefix", 0x100+LOPT_PREFIXES},
+ {"l_regressive_v", 0x100+LOPT_REGRESSIVE_VOICING},
+ {"l_unpronouncable", 0x100+LOPT_UNPRONOUNCABLE},
+ {"l_sonorant_min", 0x100+LOPT_SONORANT_MIN},
+ {"l_length_mods", 0x100+LOPT_LENGTH_MODS},
+ {NULL, 0} };
+
+#define N_VOICE_VARIANTS 12
+const char variants_either[N_VOICE_VARIANTS] = {1,2,12,3,13,4,14,5,11,0};
+const char variants_male[N_VOICE_VARIANTS] = {1,2,3,4,5,0};
+const char variants_female[N_VOICE_VARIANTS] = {11,12,13,14,0};
+const char *variant_lists[3] = {variants_either, variants_male, variants_female};
+
+static voice_t voicedata;
+voice_t *voice = &voicedata;
+
+
+static char *fgets_strip(char *buf, int size, FILE *f_in)
+{//======================================================
+// strip trailing spaces, and truncate lines at // comment
+ int len;
+ char *p;
+
+ if(fgets(buf,size,f_in) == NULL)
+ return(NULL);
+
+ len = strlen(buf);
+ while((--len > 0) && isspace(buf[len]))
+ buf[len] = 0;
+
+ if((p = strstr(buf,"//")) != NULL)
+ *p = 0;
+
+ return(buf);
+}
+
+
+static void SetToneAdjust(voice_t *voice, int *tone_pts)
+{//=====================================================
+ int ix;
+ int pt;
+ int y;
+ int freq1=0;
+ int freq2;
+ int height1 = tone_pts[1];
+ int height2;
+ double rate;
+
+ for(pt=0; pt<12; pt+=2)
+ {
+ if(tone_pts[pt] == -1)
+ {
+ tone_pts[pt] = N_TONE_ADJUST*8;
+ if(pt > 0)
+ tone_pts[pt+1] = tone_pts[pt-1];
+ }
+ freq2 = tone_pts[pt] / 8; // 8Hz steps
+ height2 = tone_pts[pt+1];
+ if((freq2 - freq1) > 0)
+ {
+ rate = double(height2-height1)/(freq2-freq1);
+
+ for(ix=freq1; ix<freq2; ix++)
+ {
+ y = height1 + int(rate * (ix-freq1));
+ if(y > 255)
+ y = 255;
+ voice->tone_adjust[ix] = y;
+ }
+ }
+ freq1 = freq2;
+ height1 = height2;
+ }
+}
+
+
+void ReadTonePoints(char *string, int *tone_pts)
+{//=============================================
+// tone_pts[] is int[12]
+ int ix;
+
+ for(ix=0; ix<12; ix++)
+ tone_pts[ix] = -1;
+
+ sscanf(string,"%d %d %d %d %d %d %d %d %d %d",
+ &tone_pts[0],&tone_pts[1],&tone_pts[2],&tone_pts[3],
+ &tone_pts[4],&tone_pts[5],&tone_pts[6],&tone_pts[7],
+ &tone_pts[8],&tone_pts[9]);
+}
+
+
+
+
+static espeak_VOICE *ReadVoiceFile(FILE *f_in, const char *fname, const char*leafname)
+{//===================================================================================
+// Read a Voice file, allocate a VOICE_DATA and set data from the
+// file's language, gender, name lines
+
+ char linebuf[120];
+ char vname[80];
+ char vgender[80];
+ char vlanguage[80];
+ char languages[300]; // allow space for several alternate language names and priorities
+
+
+ unsigned int len;
+ int langix = 0;
+ int n_languages = 0;
+ char *p;
+ espeak_VOICE *voice_data;
+ int priority;
+ int age;
+ int n_variants = 3; // default, number of variants of this voice before using another voice
+ int gender;
+
+#ifdef PLATFORM_WINDOWS
+ char fname_buf[sizeof(path_home)+15];
+ if(memcmp(leafname,"mb-",3) == 0)
+ {
+ // check whether the mbrola speech data is present for this voice
+ memcpy(vname,&leafname[3],3);
+ vname[3] = 0;
+ sprintf(fname_buf,"%s/mbrola/%s",path_home,vname);
+
+ if(GetFileLength(fname_buf) <= 0)
+ return(0);
+ }
+#endif
+
+ vname[0] = 0;
+ vgender[0] = 0;
+ age = 0;
+
+ while(fgets_strip(linebuf,sizeof(linebuf),f_in) != NULL)
+ {
+ if(memcmp(linebuf,"name",4)==0)
+ {
+ p = &linebuf[4];
+ while(isspace(*p)) p++;
+ strncpy0(vname,p,sizeof(vname));
+ }
+ else
+ if(memcmp(linebuf,"language",8)==0)
+ {
+ priority = DEFAULT_LANGUAGE_PRIORITY;
+ vlanguage[0] = 0;
+
+ sscanf(&linebuf[8],"%s %d",vlanguage,&priority);
+ len = strlen(vlanguage) + 2;
+ // check for space in languages[]
+ if(len < (sizeof(languages)-langix-1))
+ {
+ languages[langix] = priority;
+
+ strcpy(&languages[langix+1],vlanguage);
+ langix += len;
+ n_languages++;
+ }
+ }
+ else
+ if(memcmp(linebuf,"gender",6)==0)
+ {
+ sscanf(&linebuf[6],"%s %d",vgender,&age);
+ }
+ else
+ if(memcmp(linebuf,"variants",8)==0)
+ {
+ sscanf(&linebuf[8],"%d",&n_variants);
+ }
+ }
+ languages[langix++] = 0;
+
+ gender = LookupMnem(genders,vgender);
+
+ if(n_languages == 0)
+ {
+ return(NULL); // no language lines in the voice file
+ }
+
+ p = (char *)calloc(sizeof(espeak_VOICE) + langix + strlen(fname) + strlen(vname) + 3, 1);
+ voice_data = (espeak_VOICE *)p;
+ p = &p[sizeof(espeak_VOICE)];
+
+ memcpy(p,languages,langix);
+ voice_data->languages = p;
+
+ strcpy(&p[langix],fname);
+ voice_data->identifier = &p[langix];
+ voice_data->name = &p[langix];
+
+ if(vname[0] != 0)
+ {
+ langix += strlen(fname)+1;
+ strcpy(&p[langix],vname);
+ voice_data->name = &p[langix];
+ }
+
+ voice_data->age = age;
+ voice_data->gender = gender;
+ voice_data->variant = 0;
+ voice_data->xx1 = n_variants;
+ return(voice_data);
+} // end of ReadVoiceFile
+
+
+
+
+void VoiceReset(int tone_only)
+{//===========================
+// Set voice to the default values
+
+ int pk;
+ static unsigned char default_heights[N_PEAKS] = {255,255,240,240,220,220,255,255,255};
+ static unsigned char default_widths[N_PEAKS] = {128,128,128,160,171,171,128,128,128};
+
+ static int breath_widths[N_PEAKS] = {0,200,200,400,400,400,600,600,600};
+
+ // default is: pitch 82,118
+// voice->pitch_base = 0x49000; // default, 73 << 12;
+// voice->pitch_range = 0x0f30; // default = 0x1000
+ voice->pitch_base = 0x47000;
+ voice->pitch_range = 3996;
+
+ voice->formant_factor = 256;
+
+ voice->echo_delay = 0;
+ voice->echo_amp = 0;
+ voice->flutter = 64;
+ voice->n_harmonic_peaks = 5;
+ voice->peak_shape = 0;
+ voice->voicing = 64;
+ voice->consonant_amp = 100;
+ voice->consonant_ampv = 100;
+ memset(voice->klatt,0,sizeof(voice->klatt));
+ memset(speed.fast_settings,0,sizeof(speed.fast_settings));
+
+#ifdef PLATFORM_RISCOS
+ voice->roughness = 1;
+#else
+ voice->roughness = 2;
+#endif
+
+ InitBreath();
+ for(pk=0; pk<N_PEAKS; pk++)
+ {
+ voice->freq[pk] = 256;
+ voice->height[pk] = default_heights[pk];
+ voice->width[pk] = default_widths[pk]*2;
+ voice->breath[pk] = 0;
+ voice->breathw[pk] = breath_widths[pk]; // default breath formant woidths
+ voice->freqadd[pk] = 0;
+
+ // adjust formant smoothing depending on sample rate
+ formant_rate[pk] = (formant_rate_22050[pk] * 22050)/samplerate;
+ }
+ voice->height[2] = 240; // reduce F2 slightly
+
+ // This table provides the opportunity for tone control.
+ // Adjustment of harmonic amplitudes, steps of 8Hz
+ // value of 128 means no change
+// memset(voice->tone_adjust,128,sizeof(voice->tone_adjust));
+SetToneAdjust(voice,tone_points);
+
+ // default values of speed factors
+ voice->speedf1 = 256;
+ voice->speedf2 = 238;
+ voice->speedf3 = 232;
+
+ if(tone_only == 0)
+ {
+ n_replace_phonemes = 0;
+ option_quiet = 0;
+ LoadMbrolaTable(NULL,NULL,0);
+ }
+} // end of VoiceReset
+
+
+static void VoiceFormant(char *p)
+{//==============================
+ // Set parameters for a formant
+ int ix;
+ int formant;
+ int freq = 100;
+ int height = 100;
+ int width = 100;
+ int freqadd = 0;
+
+ ix = sscanf(p,"%d %d %d %d %d",&formant,&freq,&height,&width,&freqadd);
+ if(ix < 2)
+ return;
+
+ if((formant < 0) || (formant > 8))
+ return;
+
+ if(freq >= 0)
+ voice->freq[formant] = int(freq * 2.56001);
+ if(height >= 0)
+ voice->height[formant] = int(height * 2.56001);
+ if(width >= 0)
+ voice->width[formant] = int(width * 2.56001);
+ voice->freqadd[formant] = freqadd;
+}
+
+
+
+
+
+static void PhonemeReplacement(int type, char *p)
+{//==============================================
+ int n;
+ int phon;
+ int flags = 0;
+ char phon_string1[12];
+ char phon_string2[12];
+
+ strcpy(phon_string2,"NULL");
+ n = sscanf(p,"%d %s %s",&flags,phon_string1,phon_string2);
+ if((n < 2) || (n_replace_phonemes >= N_REPLACE_PHONEMES))
+ return;
+
+ if((phon = LookupPhonemeString(phon_string1)) == 0)
+ return; // not recognised
+
+ replace_phonemes[n_replace_phonemes].old_ph = phon;
+ replace_phonemes[n_replace_phonemes].new_ph = LookupPhonemeString(phon_string2);
+ replace_phonemes[n_replace_phonemes++].type = flags;
+} // end of PhonemeReplacement
+
+
+
+static int Read8Numbers(char *data_in,int *data)
+{//=============================================
+// Read 8 integer numbers
+ return(sscanf(data_in,"%d %d %d %d %d %d %d %d",
+ &data[0],&data[1],&data[2],&data[3],&data[4],&data[5],&data[6],&data[7]));
+}
+
+
+voice_t *LoadVoice(const char *vname, int control)
+{//===============================================
+// control, bit 0 1= no_default
+// bit 1 1 = change tone only, not language
+// bit 2 1 = don't report error on LoadDictionary
+// bit 4 1 = vname = full path
+
+ FILE *f_voice = NULL;
+ keywtab_t *k;
+ char *p;
+ int key;
+ int ix;
+ int n;
+ int value;
+ int error = 0;
+ int langix = 0;
+ int tone_only = control & 2;
+ int language_set = 0;
+ int phonemes_set = 0;
+ int stress_amps_set = 0;
+ int stress_lengths_set = 0;
+ int stress_add_set = 0;
+ int conditional_rules = 0;
+ LANGUAGE_OPTIONS *langopts = NULL;
+
+ Translator *new_translator = NULL;
+
+ char voicename[40];
+ char language_name[40];
+ char translator_name[40];
+ char new_dictionary[40];
+ char phonemes_name[40];
+ const char *language_type;
+ char buf[200];
+ char path_voices[sizeof(path_home)+12];
+ char langname[4];
+
+ int stress_amps[8];
+ int stress_lengths[8];
+ int stress_add[8];
+
+ int pitch1;
+ int pitch2;
+
+ static char voice_identifier[40]; // file name for voice_selected
+ static char voice_name[40]; // voice name for voice_selected
+ static char voice_languages[100]; // list of languages and priorities for voice_selected
+
+ strcpy(voicename,vname);
+ if(voicename[0]==0)
+ strcpy(voicename,"default");
+
+ if(control & 0x10)
+ {
+ strcpy(buf,vname);
+ if(GetFileLength(buf) <= 0)
+ return(NULL);
+ }
+ else
+ {
+ sprintf(path_voices,"%s%cvoices%c",path_home,PATHSEP,PATHSEP);
+ sprintf(buf,"%s%s",path_voices,voicename);
+
+ if(GetFileLength(buf) <= 0)
+ {
+ // look for the voice in a sub-directory of the language name
+ langname[0] = voicename[0];
+ langname[1] = voicename[1];
+ langname[2] = 0;
+ sprintf(buf,"%s%s%c%s",path_voices,langname,PATHSEP,voicename);
+
+ if(GetFileLength(buf) <= 0)
+ {
+ // look in "test" sub-directory
+ sprintf(buf,"%stest%c%s",path_voices,PATHSEP,voicename);
+ }
+ }
+ }
+
+ f_voice = fopen(buf,"r");
+
+ language_type = "en"; // default
+ if(f_voice == NULL)
+ {
+ if(control & 3)
+ return(NULL); // can't open file
+
+ if(SelectPhonemeTableName(voicename) >= 0)
+ language_type = voicename;
+ }
+
+ if(!tone_only && (translator != NULL))
+ {
+ DeleteTranslator(translator);
+ translator = NULL;
+ }
+
+ strcpy(translator_name,language_type);
+ strcpy(new_dictionary,language_type);
+ strcpy(phonemes_name,language_type);
+
+
+ if(!tone_only)
+ {
+ voice = &voicedata;
+ strncpy0(voice_identifier,vname,sizeof(voice_identifier));
+ voice_name[0] = 0;
+ voice_languages[0] = 0;
+
+ voice_selected.identifier = voice_identifier;
+ voice_selected.name = voice_name;
+ voice_selected.languages = voice_languages;
+ }
+ else
+ {
+ // append the variant file name to the voice identifier
+ if((p = strchr(voice_identifier,'+')) != NULL)
+ *p = 0; // remove previous variant name
+ sprintf(buf,"+%s",&vname[3]); // omit !v/ from the variant filename
+ strcat(voice_identifier,buf);
+ langopts = &translator->langopts;
+ }
+ VoiceReset(tone_only);
+
+ if(!tone_only)
+ SelectPhonemeTableName(phonemes_name); // set up phoneme_tab
+
+
+ while((f_voice != NULL) && (fgets_strip(buf,sizeof(buf),f_voice) != NULL))
+ {
+ // isolate the attribute name
+ for(p=buf; (*p != 0) && !isspace(*p); p++);
+ *p++ = 0;
+
+ if(buf[0] == 0) continue;
+
+ key = 0;
+ for(k=keyword_tab; k->mnem != NULL; k++)
+ {
+ if(strcmp(buf,k->mnem)==0)
+ {
+ key = k->data;
+ break;
+ }
+ }
+
+ switch(key)
+ {
+ case V_LANGUAGE:
+ {
+ unsigned int len;
+ int priority;
+
+ if(tone_only)
+ break;
+
+ priority = DEFAULT_LANGUAGE_PRIORITY;
+ language_name[0] = 0;
+
+ sscanf(p,"%s %d",language_name,&priority);
+ if(strcmp(language_name,"variant") == 0)
+ break;
+
+ len = strlen(language_name) + 2;
+ // check for space in languages[]
+ if(len < (sizeof(voice_languages)-langix-1))
+ {
+ voice_languages[langix] = priority;
+
+ strcpy(&voice_languages[langix+1],language_name);
+ langix += len;
+ }
+
+ // only act on the first language line
+ if(language_set == 0)
+ {
+ language_type = strtok(language_name,"-");
+ language_set = 1;
+ strcpy(translator_name,language_type);
+ strcpy(new_dictionary,language_type);
+ strcpy(phonemes_name,language_type);
+ SelectPhonemeTableName(phonemes_name);
+
+ if(new_translator != NULL)
+ DeleteTranslator(new_translator);
+
+ new_translator = SelectTranslator(translator_name);
+ langopts = &new_translator->langopts;
+ }
+ }
+ break;
+
+ case V_NAME:
+ if(tone_only == 0)
+ {
+ while(isspace(*p)) p++;
+ strncpy0(voice_name,p,sizeof(voice_name));
+ }
+ break;
+
+ case V_GENDER:
+ {
+ int age;
+ char vgender[80];
+ sscanf(p,"%s %d",vgender,&age);
+ voice_selected.gender = LookupMnem(genders,vgender);
+ voice_selected.age = age;
+ }
+ break;
+
+ case V_TRANSLATOR:
+ if(tone_only) break;
+
+ sscanf(p,"%s",translator_name);
+
+ if(new_translator != NULL)
+ DeleteTranslator(new_translator);
+
+ new_translator = SelectTranslator(translator_name);
+ langopts = &new_translator->langopts;
+ break;
+
+ case V_DICTIONARY: // dictionary
+ sscanf(p,"%s",new_dictionary);
+ break;
+
+ case V_PHONEMES: // phoneme table
+ sscanf(p,"%s",phonemes_name);
+ break;
+
+ case V_FORMANT:
+ VoiceFormant(p);
+ break;
+
+ case V_PITCH:
+ {
+ double factor;
+ // default is pitch 82 118
+ n = sscanf(p,"%d %d",&pitch1,&pitch2);
+ voice->pitch_base = (pitch1 - 9) << 12;
+ voice->pitch_range = (pitch2 - pitch1) * 108;
+ factor = double(pitch1 - 82)/82;
+ voice->formant_factor = (int)((1+factor/4) * 256); // nominal formant shift for a different voice pitch
+ }
+ break;
+
+ case V_STRESSLENGTH: // stressLength
+ stress_lengths_set = Read8Numbers(p,stress_lengths);
+ break;
+
+ case V_STRESSAMP: // stressAmp
+ stress_amps_set = Read8Numbers(p,stress_amps);
+ break;
+
+ case V_STRESSADD: // stressAdd
+ stress_add_set = Read8Numbers(p,stress_add);
+ break;
+
+ case V_INTONATION: // intonation
+ sscanf(p,"%d %d",&option_tone_flags,&option_tone2);
+ if((option_tone_flags & 0xff) != 0)
+ langopts->intonation_group = option_tone_flags & 0xff;
+ break;
+
+ case V_DICTRULES: // conditional dictionary rules and list entries
+ while(*p != 0)
+ {
+ while(isspace(*p)) p++;
+ n = -1;
+ if(((n = atoi(p)) > 0) && (n < 32))
+ {
+ p++;
+ conditional_rules |= (1 << n);
+ }
+ while(isalnum(*p)) p++;
+ }
+ break;
+
+ case V_REPLACE:
+ if(phonemes_set == 0)
+ {
+ // must set up a phoneme table before we can lookup phoneme mnemonics
+ SelectPhonemeTableName(phonemes_name);
+ phonemes_set = 1;
+ }
+ PhonemeReplacement(key,p);
+ break;
+
+ case V_WORDGAP: // words
+ sscanf(p,"%d %d",&langopts->word_gap, &langopts->vowel_pause);
+ break;
+
+ case V_STRESSRULE:
+ sscanf(p,"%d %d %d %d",&langopts->stress_rule,
+ &langopts->stress_flags,
+ &langopts->unstressed_wd1,
+ &langopts->unstressed_wd2);
+ break;
+
+ case V_CHARSET:
+ if((sscanf(p,"%d",&value)==1) && (value < N_CHARSETS))
+ new_translator->charset_a0 = charsets[value];
+ break;
+
+ case V_NUMBERS:
+ sscanf(p,"%d %d",&langopts->numbers,&langopts->numbers2);
+ break;
+
+ case V_OPTION:
+ if(sscanf(p,"%d %d",&ix,&value) == 2)
+ {
+ if((ix >= 0) && (ix < N_LOPTS))
+ langopts->param[ix] = value;
+ }
+ break;
+
+ case V_ECHO:
+ // echo. suggest: 135mS 11%
+ value = 0;
+ voice->echo_amp = 0;
+ sscanf(p,"%d %d",&voice->echo_delay,&voice->echo_amp);
+ break;
+
+ case V_FLUTTER: // flutter
+ if(sscanf(p,"%d",&value)==1)
+ voice->flutter = value * 32;
+ break;
+
+ case V_ROUGHNESS: // roughness
+ if(sscanf(p,"%d",&value)==1)
+ voice->roughness = value;
+ break;
+
+ case V_CLARITY: // formantshape
+ if(sscanf(p,"%d",&value)==1)
+ {
+ if(value > 4)
+ {
+ voice->peak_shape = 1; // squarer formant peaks
+ value = 4;
+ }
+ voice->n_harmonic_peaks = 1+value;
+ }
+ break;
+
+ case V_TONE:
+ {
+ int tone_data[10];
+ ReadTonePoints(p,tone_data);
+ SetToneAdjust(voice,tone_data);
+ }
+ break;
+
+ case V_VOICING:
+ if(sscanf(p,"%d",&value)==1)
+ voice->voicing = (value * 64)/100;
+ break;
+
+ case V_BREATH:
+ voice->breath[0] = Read8Numbers(p,&voice->breath[1]);
+ for(ix=1; ix<8; ix++)
+ {
+ if(ix % 2)
+ voice->breath[ix] = -voice->breath[ix];
+ }
+ break;
+
+ case V_BREATHW:
+ voice->breathw[0] = Read8Numbers(p,&voice->breathw[1]);
+ break;
+
+ case V_CONSONANTS:
+ value = sscanf(p,"%d %d",&voice->consonant_amp, &voice->consonant_ampv);
+ break;
+
+ case V_MBROLA:
+ {
+ int srate = 16000;
+ char name[40];
+ char phtrans[40];
+
+ phtrans[0] = 0;
+ sscanf(p,"%s %s %d",name,phtrans,&srate);
+ LoadMbrolaTable(name,phtrans,srate);
+ }
+ break;
+
+ case V_KLATT:
+ Read8Numbers(p,voice->klatt);
+ voice->klatt[KLATT_Kopen] -= 40;
+ break;
+
+ case V_FAST:
+ Read8Numbers(p,speed.fast_settings);
+ SetSpeed(2);
+ break;
+
+ default:
+ if((key & 0xff00) == 0x100)
+ {
+ sscanf(p,"%d",&langopts->param[key &0xff]);
+ }
+ else
+ {
+ fprintf(stderr,"Bad voice attribute: %s\n",buf);
+ }
+ break;
+ }
+ }
+ if(f_voice != NULL)
+ fclose(f_voice);
+
+ if((new_translator == NULL) && (!tone_only))
+ {
+ // not set by language attribute
+ new_translator = SelectTranslator(translator_name);
+ }
+
+ for(ix=0; ix<N_PEAKS; ix++)
+ {
+ voice->freq2[ix] = voice->freq[ix];
+ voice->height2[ix] = voice->height[ix];
+ voice->width2[ix] = voice->width[ix];
+ }
+
+ if(tone_only)
+ {
+ new_translator = translator;
+ }
+ else
+ {
+ if((ix = SelectPhonemeTableName(phonemes_name)) < 0)
+ {
+ fprintf(stderr,"Unknown phoneme table: '%s'\n",phonemes_name);
+ }
+ voice->phoneme_tab_ix = ix;
+ error = LoadDictionary(new_translator, new_dictionary, control & 4);
+ if(dictionary_name[0]==0)
+ return(NULL); // no dictionary loaded
+
+ new_translator->dict_condition = conditional_rules;
+
+ voice_languages[langix] = 0;
+ }
+
+ langopts = &new_translator->langopts;
+
+
+ if((value = langopts->param[LOPT_LENGTH_MODS]) != 0)
+ {
+ SetLengthMods(new_translator,value);
+ }
+
+ voice->width[0] = (voice->width[0] * 105)/100;
+
+ if(!tone_only)
+ {
+ translator = new_translator;
+ }
+
+ // relative lengths of different stress syllables
+ for(ix=0; ix<stress_lengths_set; ix++)
+ {
+ translator->stress_lengths[ix] = stress_lengths[ix];
+ }
+ for(ix=0; ix<stress_add_set; ix++)
+ {
+ translator->stress_lengths[ix] += stress_add[ix];
+ }
+ for(ix=0; ix<stress_amps_set; ix++)
+ {
+ translator->stress_amps[ix] = stress_amps[ix];
+ translator->stress_amps_r[ix] = stress_amps[ix] -1;
+ }
+
+ return(voice);
+} // end of LoadVoice
+
+
+static char *ExtractVoiceVariantName(char *vname, int variant_num)
+{//===============================================================
+// Remove any voice variant suffix (name or number) from a voice name
+// Returns the voice variant name
+
+ char *p;
+ static char variant_name[20];
+ char variant_prefix[5];
+
+ variant_name[0] = 0;
+ sprintf(variant_prefix,"!v%c",PATHSEP);
+
+ if(vname != NULL)
+ {
+ if((p = strchr(vname,'+')) != NULL)
+ {
+ // The voice name has a +variant suffix
+ *p++ = 0; // delete the suffix from the voice name
+ if(isdigit(*p))
+ {
+ variant_num = atoi(p); // variant number
+ }
+ else
+ {
+ // voice variant name, not number
+ strcpy(variant_name,variant_prefix);
+ strncpy0(&variant_name[3],p,sizeof(variant_name)-3);
+ }
+ }
+ }
+
+ if(variant_num > 0)
+ {
+ if(variant_num < 10)
+ sprintf(variant_name,"%sm%d",variant_prefix, variant_num); // male
+ else
+ sprintf(variant_name,"%sf%d",variant_prefix, variant_num-10); // female
+ }
+
+ return(variant_name);
+} // end of ExtractVoiceVariantName
+
+
+
+voice_t *LoadVoiceVariant(const char *vname, int variant_num)
+{//==========================================================
+// Load a voice file.
+// Also apply a voice variant if specified by "variant", or by "+number" or "+name" in the "vname"
+
+ voice_t *v;
+ char *variant_name;
+ char buf[60];
+
+ strncpy0(buf,vname,sizeof(buf));
+ variant_name = ExtractVoiceVariantName(buf,variant_num);
+
+ if((v = LoadVoice(buf,0)) == NULL)
+ return(NULL);
+
+ if(variant_name[0] != 0)
+ {
+ v = LoadVoice(variant_name,2);
+ }
+ return(v);
+}
+
+
+
+static int __cdecl VoiceNameSorter(const void *p1, const void *p2)
+{//=======================================================
+ int ix;
+ espeak_VOICE *v1 = *(espeak_VOICE **)p1;
+ espeak_VOICE *v2 = *(espeak_VOICE **)p2;
+
+
+ if((ix = strcmp(&v1->languages[1],&v2->languages[1])) != 0) // primary language name
+ return(ix);
+ if((ix = v1->languages[0] - v2->languages[0]) != 0) // priority number
+ return(ix);
+ return(strcmp(v1->name,v2->name));
+}
+
+
+static int __cdecl VoiceScoreSorter(const void *p1, const void *p2)
+{//========================================================
+ int ix;
+ espeak_VOICE *v1 = *(espeak_VOICE **)p1;
+ espeak_VOICE *v2 = *(espeak_VOICE **)p2;
+
+ if((ix = v2->score - v1->score) != 0)
+ return(ix);
+ return(strcmp(v1->name,v2->name));
+}
+
+
+static int ScoreVoice(espeak_VOICE *voice_spec, const char *spec_language, int spec_n_parts, int spec_lang_len, espeak_VOICE *voice)
+{//=========================================================================================================================
+ int ix;
+ const char *p;
+ int c1, c2;
+ int language_priority;
+ int n_parts;
+ int matching;
+ int matching_parts;
+ int score = 0;
+ int x;
+ int ratio;
+ int required_age;
+ int diff;
+
+ p = voice->languages; // list of languages+dialects for which this voice is suitable
+
+ if(strcmp(spec_language,"mbrola")==0)
+ {
+ // only list mbrola voices
+ if(memcmp(voice->identifier,"mb/",3) == 0)
+ return(100);
+ return(0);
+ }
+
+ if(spec_n_parts == 0)
+ {
+ score = 100;
+ }
+ else
+ {
+ if((*p == 0) && (strcmp(spec_language,"variants")==0))
+ {
+ // match on a voice with no languages if the required language is "variants"
+ score = 100;
+ }
+
+ // compare the required language with each of the languages of this voice
+ while(*p != 0)
+ {
+ language_priority = *p++;
+
+ matching = 1;
+ matching_parts = 0;
+ n_parts = 1;
+
+ for(ix=0; ; ix++)
+ {
+ if((ix >= spec_lang_len) || ((c1 = spec_language[ix]) == '-'))
+ c1 = 0;
+ if((c2 = p[ix]) == '-')
+ c2 = 0;
+
+ if(c1 != c2)
+ {
+ matching = 0;
+ }
+
+ if(p[ix] == '-')
+ {
+ n_parts++;
+ if(matching)
+ matching_parts++;
+ }
+ if(p[ix] == 0)
+ break;
+ }
+ p += (ix+1);
+ matching_parts += matching; // number of parts which match
+
+ if(matching_parts == 0)
+ continue; // no matching parts for this language
+
+ x = 5;
+ // reduce the score if not all parts of the required language match
+ if((diff = (spec_n_parts - matching_parts)) > 0)
+ x -= diff;
+
+ // reduce score if the language is more specific than required
+ if((diff = (n_parts - matching_parts)) > 0)
+ x -= diff;
+
+ x = x*100 - (language_priority * 2);
+
+ if(x > score)
+ score = x;
+ }
+ }
+ if(score == 0)
+ return(0);
+
+ if(voice_spec->name != NULL)
+ {
+ if(strcmp(voice_spec->name,voice->name)==0)
+ {
+ // match on voice name
+ score += 500;
+ }
+ else
+ if(strcmp(voice_spec->name,voice->identifier)==0)
+ {
+ score += 400;
+ }
+ }
+
+ if(((voice_spec->gender == 1) || (voice_spec->gender == 2)) &&
+ ((voice->gender == 1) || (voice->gender == 2)))
+ {
+ if(voice_spec->gender == voice->gender)
+ score += 50;
+ else
+ score -= 50;
+ }
+
+ if((voice_spec->age <= 12) && (voice->gender == 2) && (voice->age > 12))
+ {
+ score += 5; // give some preference for non-child female voice if a child is requested
+ }
+
+ if(voice->age != 0)
+ {
+ if(voice_spec->age == 0)
+ required_age = 30;
+ else
+ required_age = voice_spec->age;
+
+ ratio = (required_age*100)/voice->age;
+ if(ratio < 100)
+ ratio = 10000/ratio;
+ ratio = (ratio - 100)/10; // 0=exact match, 10=out by factor of 2
+ x = 5 - ratio;
+ if(x > 0) x = 0;
+
+ score = score + x;
+
+ if(voice_spec->age > 0)
+ score += 10; // required age specified, favour voices with a specified age (near it)
+ }
+ if(score < 1)
+ score = 1;
+ return(score);
+} // end of ScoreVoice
+
+
+static int SetVoiceScores(espeak_VOICE *voice_select, espeak_VOICE **voices, int control)
+{//======================================================================================
+// control: bit0=1 include mbrola voices
+ int ix;
+ int score;
+ int nv; // number of candidates
+ int n_parts=0;
+ int lang_len=0;
+ espeak_VOICE *vp;
+ char language[80];
+
+ // count number of parts in the specified language
+ if((voice_select->languages != NULL) && (voice_select->languages[0] != 0))
+ {
+ n_parts = 1;
+ lang_len = strlen(voice_select->languages);
+ for(ix=0; (ix<=lang_len) && ((unsigned)ix < sizeof(language)); ix++)
+ {
+ if((language[ix] = tolower(voice_select->languages[ix])) == '-')
+ n_parts++;
+ }
+ }
+ // select those voices which match the specified language
+ nv = 0;
+ for(ix=0; ix<n_voices_list; ix++)
+ {
+ vp = voices_list[ix];
+
+ if(((control & 1) == 0) && (memcmp(vp->identifier,"mb/",3) == 0))
+ continue;
+
+ if((score = ScoreVoice(voice_select, language, n_parts, lang_len, voices_list[ix])) > 0)
+ {
+ voices[nv++] = vp;
+ vp->score = score;
+ }
+ }
+ voices[nv] = NULL; // list terminator
+
+ if(nv==0)
+ return(0);
+
+ // sort the selected voices by their score
+ qsort(voices,nv,sizeof(espeak_VOICE *),(int (__cdecl *)(const void *,const void *))VoiceScoreSorter);
+
+ return(nv);
+} // end of SetVoiceScores
+
+
+
+
+espeak_VOICE *SelectVoiceByName(espeak_VOICE **voices, const char *name)
+{//=====================================================================
+ int ix;
+ int match_fname = -1;
+ int match_fname2 = -1;
+ int match_name = -1;
+ const char *id;
+ int last_part_len;
+ char last_part[41];
+
+ if(voices == NULL)
+ {
+ if(n_voices_list == 0)
+ espeak_ListVoices(NULL); // create the voices list
+ voices = voices_list;
+ }
+
+ sprintf(last_part,"%c%s",PATHSEP,name);
+ last_part_len = strlen(last_part);
+
+ for(ix=0; voices[ix] != NULL; ix++)
+ {
+ if(strcmp(name,voices[ix]->name)==0)
+ {
+ match_name = ix; // found matching voice name
+ break;
+ }
+ else
+ if(strcmp(name,id = voices[ix]->identifier)==0)
+ {
+ match_fname = ix; // matching identifier, use this if no matching name
+ }
+ else
+ if(strcmp(last_part,&id[strlen(id)-last_part_len])==0)
+ {
+ match_fname2 = ix;
+ }
+ }
+
+ if(match_name < 0)
+ {
+ match_name = match_fname; // no matching name, try matching filename
+ if(match_name < 0)
+ match_name = match_fname2; // try matching just the last part of the filename
+ }
+
+ if(match_name < 0)
+ return(NULL);
+
+ return(voices[match_name]);
+} // end of SelectVoiceByName
+
+
+
+
+char const *SelectVoice(espeak_VOICE *voice_select, int *found)
+{//============================================================
+// Returns a path within espeak-voices, with a possible +variant suffix
+// variant is an output-only parameter
+ int nv; // number of candidates
+ int ix, ix2;
+ int j;
+ int n_variants;
+ int variant_number;
+ int gender;
+ int skip;
+ int aged=1;
+ char *variant_name;
+ const char *p, *p_start;
+ espeak_VOICE *vp = NULL;
+ espeak_VOICE *vp2;
+ espeak_VOICE voice_select2;
+ espeak_VOICE *voices[N_VOICES_LIST]; // list of candidates
+ espeak_VOICE *voices2[N_VOICES_LIST+N_VOICE_VARIANTS];
+ static espeak_VOICE voice_variants[N_VOICE_VARIANTS];
+ static char voice_id[50];
+
+ *found = 1;
+ memcpy(&voice_select2,voice_select,sizeof(voice_select2));
+
+ if(n_voices_list == 0)
+ espeak_ListVoices(NULL); // create the voices list
+
+ if((voice_select2.languages == NULL) || (voice_select2.languages[0] == 0))
+ {
+ // no language is specified. Get language from the named voice
+ static char buf[60];
+
+ if(voice_select2.name == NULL)
+ {
+ if((voice_select2.name = voice_select2.identifier) == NULL)
+ voice_select2.name = "default";
+ }
+
+ strncpy0(buf,voice_select2.name,sizeof(buf));
+ variant_name = ExtractVoiceVariantName(buf,0);
+
+ vp = SelectVoiceByName(voices_list,buf);
+ if(vp != NULL)
+ {
+ voice_select2.languages = &(vp->languages[1]);
+
+ if((voice_select2.gender==0) && (voice_select2.age==0) && (voice_select2.variant==0))
+ {
+ if(variant_name[0] != 0)
+ {
+ sprintf(voice_id,"%s+%s",vp->identifier,&variant_name[3]); // omit the !v/ from variant_name
+ return(voice_id);
+ }
+
+ return(vp->identifier);
+ }
+ }
+ }
+
+ // select and sort voices for the required language
+ nv = SetVoiceScores(&voice_select2,voices,0);
+
+ if(nv == 0)
+ {
+ // no matching voice, choose the default
+ *found = 0;
+ if((voices[0] = SelectVoiceByName(voices_list,"default")) != NULL)
+ nv = 1;
+ }
+
+ gender = 0;
+ if((voice_select2.gender == 2) || ((voice_select2.age > 0) && (voice_select2.age < 13)))
+ gender = 2;
+ else
+ if(voice_select2.gender == 1)
+ gender = 1;
+
+#define AGE_OLD 60
+ if(voice_select2.age < AGE_OLD)
+ aged = 0;
+
+ p = p_start = variant_lists[gender];
+ if(aged == 0)
+ p++; // the first voice in the variants list is older
+
+ // add variants for the top voices
+ n_variants = 0;
+ for(ix=0, ix2=0; ix<nv; ix++)
+ {
+ vp = voices[ix];
+ // is the main voice the required gender?
+ skip=0;
+ if((gender != 0) && (vp->gender != gender))
+ {
+ skip=1;
+ }
+ if((ix2==0) && aged && (vp->age < AGE_OLD))
+ {
+ skip=1;
+ }
+ if(skip==0)
+ {
+ voices2[ix2++] = vp;
+ }
+
+ for(j=0; (j < vp->xx1) && (n_variants < N_VOICE_VARIANTS);)
+ {
+ if((variant_number = *p) == 0)
+ {
+ p = p_start;
+ continue;
+ }
+
+ vp2 = &voice_variants[n_variants++]; // allocate space for voice variant
+ memcpy(vp2,vp,sizeof(espeak_VOICE)); // copy from the original voice
+ vp2->variant = variant_number;
+ voices2[ix2++] = vp2;
+ p++;
+ j++;
+ }
+ }
+ // add any more variants to the end of the list
+ while((vp != NULL) && ((variant_number = *p++) != 0) && (n_variants < N_VOICE_VARIANTS))
+ {
+ vp2 = &voice_variants[n_variants++]; // allocate space for voice variant
+ memcpy(vp2,vp,sizeof(espeak_VOICE)); // copy from the original voice
+ vp2->variant = variant_number;
+ voices2[ix2++] = vp2;
+ }
+
+ // index the sorted list by the required variant number
+ vp = voices2[voice_select2.variant % ix2];
+
+ if(vp->variant != 0)
+ {
+ variant_name = ExtractVoiceVariantName(NULL,vp->variant);
+ sprintf(voice_id,"%s+%s",vp->identifier,&variant_name[3]);
+ return(voice_id);
+ }
+
+ return(vp->identifier);
+} // end of SelectVoice
+
+
+
+static void GetVoices(const char *path)
+{//====================================
+ FILE *f_voice;
+ espeak_VOICE *voice_data;
+ int ftype;
+ char fname[sizeof(path_home)+100];
+
+#ifdef PLATFORM_RISCOS
+ int len;
+ int *type;
+ char *p;
+ _kernel_swi_regs regs;
+ _kernel_oserror *error;
+ char buf[80];
+ char directory2[sizeof(path_home)+100];
+
+ regs.r[0] = 10;
+ regs.r[1] = (int)path;
+ regs.r[2] = (int)buf;
+ regs.r[3] = 1;
+ regs.r[4] = 0;
+ regs.r[5] = sizeof(buf);
+ regs.r[6] = 0;
+
+ while(regs.r[3] > 0)
+ {
+ error = _kernel_swi(0x0c+0x20000,&regs,&regs); /* OS_GBPB 10, read directory entries */
+ if((error != NULL) || (regs.r[3] == 0))
+ {
+ break;
+ }
+ type = (int *)(&buf[16]);
+ len = strlen(&buf[20]);
+ sprintf(fname,"%s.%s",path,&buf[20]);
+
+ if(*type == 2)
+ {
+ // a sub-directory
+ GetVoices(fname);
+ }
+ else
+ {
+ // a regular line, add it to the voices list
+ if((f_voice = fopen(fname,"r")) == NULL)
+ continue;
+
+ // pass voice file name within the voices directory
+ voice_data = ReadVoiceFile(f_voice, fname+len_path_voices, &buf[20]);
+ fclose(f_voice);
+
+ if(voice_data != NULL)
+ {
+ voices_list[n_voices_list++] = voice_data;
+ }
+ }
+ }
+#else
+#ifdef PLATFORM_WINDOWS
+ WIN32_FIND_DATAA FindFileData;
+ HANDLE hFind = INVALID_HANDLE_VALUE;
+
+#undef UNICODE // we need FindFirstFileA() which takes an 8-bit c-string
+ sprintf(fname,"%s\\*",path);
+ hFind = FindFirstFileA(fname, &FindFileData);
+ if(hFind == INVALID_HANDLE_VALUE)
+ return;
+
+ do {
+ sprintf(fname,"%s%c%s",path,PATHSEP,FindFileData.cFileName);
+
+ ftype = GetFileLength(fname);
+
+ if((ftype == -2) && (FindFileData.cFileName[0] != '.'))
+ {
+ // a sub-sirectory
+ GetVoices(fname);
+ }
+ else
+ if(ftype > 0)
+ {
+ // a regular line, add it to the voices list
+ if((f_voice = fopen(fname,"r")) == NULL)
+ continue;
+
+ // pass voice file name within the voices directory
+ voice_data = ReadVoiceFile(f_voice, fname+len_path_voices, FindFileData.cFileName);
+ fclose(f_voice);
+
+ if(voice_data != NULL)
+ {
+ voices_list[n_voices_list++] = voice_data;
+ }
+ }
+ } while(FindNextFileA(hFind, &FindFileData) != 0);
+ FindClose(hFind);
+
+#else
+ DIR *dir;
+ struct dirent *ent;
+
+ if((dir = opendir(path)) == NULL)
+ return;
+
+ while((ent = readdir(dir)) != NULL)
+ {
+ if(n_voices_list >= (N_VOICES_LIST-2))
+ break; // voices list is full
+
+ sprintf(fname,"%s%c%s",path,PATHSEP,ent->d_name);
+
+ ftype = GetFileLength(fname);
+
+ if((ftype == -2) && (ent->d_name[0] != '.'))
+ {
+ // a sub-sirectory
+ GetVoices(fname);
+ }
+ else
+ if(ftype > 0)
+ {
+ // a regular line, add it to the voices list
+ if((f_voice = fopen(fname,"r")) == NULL)
+ continue;
+
+ // pass voice file name within the voices directory
+ voice_data = ReadVoiceFile(f_voice, fname+len_path_voices, ent->d_name);
+ fclose(f_voice);
+
+ if(voice_data != NULL)
+ {
+ voices_list[n_voices_list++] = voice_data;
+ }
+ }
+ }
+ closedir(dir);
+#endif
+#endif
+} // end of GetVoices
+
+
+
+espeak_ERROR SetVoiceByName(const char *name)
+{//=========================================
+ espeak_VOICE *v;
+ espeak_VOICE voice_selector;
+ char *variant_name;
+ static char buf[60];
+
+ strncpy0(buf,name,sizeof(buf));
+ variant_name = ExtractVoiceVariantName(buf,0);
+
+ memset(&voice_selector,0,sizeof(voice_selector));
+// voice_selector.name = buf;
+ voice_selector.name = (char *)name; // include variant name in voice stack ??
+
+ // first check for a voice with this filename
+ // This may avoid the need to call espeak_ListVoices().
+
+ if(LoadVoice(buf,1) != NULL)
+ {
+ if(variant_name[0] != 0)
+ {
+ LoadVoice(variant_name,2);
+ }
+
+ DoVoiceChange(voice);
+ SetVoiceStack(&voice_selector);
+ return(EE_OK);
+ }
+
+ if(n_voices_list == 0)
+ espeak_ListVoices(NULL); // create the voices list
+
+ if((v = SelectVoiceByName(voices_list,buf)) != NULL)
+ {
+ if(LoadVoice(v->identifier,0) != NULL)
+ {
+ if(variant_name[0] != 0)
+ {
+ LoadVoice(variant_name,2);
+ }
+ DoVoiceChange(voice);
+ SetVoiceStack(&voice_selector);
+ return(EE_OK);
+ }
+ }
+ return(EE_INTERNAL_ERROR); // voice name not found
+} // end of SetVoiceByName
+
+
+
+espeak_ERROR SetVoiceByProperties(espeak_VOICE *voice_selector)
+{//============================================================
+ const char *voice_id;
+ int voice_found;
+
+ voice_id = SelectVoice(voice_selector, &voice_found);
+
+ if(voice_found == 0)
+ return(EE_NOT_FOUND);
+
+ LoadVoiceVariant(voice_id,0);
+ DoVoiceChange(voice);
+ SetVoiceStack(voice_selector);
+
+ return(EE_OK);
+} // end of SetVoiceByProperties
+
+
+
+
+//=======================================================================
+// Library Interface Functions
+//=======================================================================
+//#pragma GCC visibility push(default)
+
+
+ESPEAK_API const espeak_VOICE **espeak_ListVoices(espeak_VOICE *voice_spec)
+{//========================================================================
+#ifndef PLATFORM_RISCOS
+ int ix;
+ int j;
+ espeak_VOICE *v;
+ static espeak_VOICE *voices[N_VOICES_LIST];
+ char path_voices[sizeof(path_home)+12];
+
+ // free previous voice list data
+
+ for(ix=0; ix<n_voices_list; ix++)
+ {
+ if(voices_list[ix] != NULL)
+ free(voices_list[ix]);
+ }
+ n_voices_list = 0;
+
+ sprintf(path_voices,"%s%cvoices",path_home,PATHSEP);
+ len_path_voices = strlen(path_voices)+1;
+
+ GetVoices(path_voices);
+ voices_list[n_voices_list] = NULL; // voices list terminator
+
+ // sort the voices list
+ qsort(voices_list,n_voices_list,sizeof(espeak_VOICE *),
+ (int (__cdecl *)(const void *,const void *))VoiceNameSorter);
+
+
+ if(voice_spec)
+ {
+ // select the voices which match the voice_spec, and sort them by preference
+ SetVoiceScores(voice_spec,voices,1);
+ }
+ else
+ {
+ // list all: omit variant voices and mbrola voices
+ j = 0;
+ for(ix=0; (v = voices_list[ix]) != NULL; ix++)
+ {
+ if((v->languages[0] != 0) && (strcmp(&v->languages[1],"variant") != 0) && (memcmp(v->identifier,"mb/",3) != 0))
+ {
+ voices[j++] = v;
+ }
+ }
+ voices[j] = NULL;
+ }
+ return((const espeak_VOICE **)voices);
+#endif
+ return((const espeak_VOICE **)voices_list);
+} // end of espeak_ListVoices
+
+
+
+ESPEAK_API espeak_VOICE *espeak_GetCurrentVoice(void)
+{//==================================================
+ return(&voice_selected);
+}
+
+//#pragma GCC visibility pop
+
+
diff --git a/Plugins/eSpeak/eSpeak/wave.h b/Plugins/eSpeak/eSpeak/wave.h
new file mode 100644
index 0000000..8b6e606
--- /dev/null
+++ b/Plugins/eSpeak/eSpeak/wave.h
@@ -0,0 +1,43 @@
+#ifndef WAVE_H
+#define WAVE_H
+
+#ifndef PLATFORM_DOS
+#include "stdint.h"
+#endif
+
+extern int option_device_number;
+
+extern void wave_init();
+// TBD: the arg could be "alsa", "oss",...
+extern void* wave_open(const char* the_api);
+
+extern size_t wave_write(void* theHandler, char* theMono16BitsWaveBuffer, size_t theSize);
+extern int wave_close(void* theHandler);
+extern void wave_flush(void* theHandler);
+extern int wave_is_busy(void* theHandler);
+extern void wave_terminate();
+extern uint32_t wave_get_read_position(void* theHandler);
+extern uint32_t wave_get_write_position(void* theHandler);
+
+// Supply the remaining time in ms before the sample is played
+// (or 0 if the event has been already played).
+// sample: sample identifier
+// time: supplied value in ms
+//
+// return 0 if ok or -1 otherwise (stream not opened).
+extern int wave_get_remaining_time(uint32_t sample, uint32_t* time);
+
+// set the callback which informs if the output is still enabled.
+// Helpful if a new sample is waiting for free space whereas sound must be stopped.
+typedef int (t_wave_callback)(void);
+extern void wave_set_callback_is_output_enabled(t_wave_callback* cb);
+
+
+// general functions
+extern void clock_gettime2(struct timespec *ts);
+extern void add_time_in_ms(struct timespec *ts, int time_in_ms);
+
+// for tests
+extern void *wave_test_get_write_buffer();
+
+#endif
diff --git a/Plugins/eSpeak/eSpeak/wavegen.cpp b/Plugins/eSpeak/eSpeak/wavegen.cpp
new file mode 100644
index 0000000..a5d7df7
--- /dev/null
+++ b/Plugins/eSpeak/eSpeak/wavegen.cpp
@@ -0,0 +1,1917 @@
+/***************************************************************************
+ * Copyright (C) 2005 to 2007 by Jonathan Duddington *
+ * email: jonsd@users.sourceforge.net *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 3 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, see: *
+ * <http://www.gnu.org/licenses/>. *
+ ***************************************************************************/
+
+#include "StdAfx.h"
+
+// this version keeps wavemult window as a constant fraction
+// of the cycle length - but that spreads out the HF peaks too much
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <math.h>
+
+
+#include "speak_lib.h"
+#include "speech.h"
+#include "phoneme.h"
+#include "synthesize.h"
+#include "voice.h"
+
+//#undef INCLUDE_KLATT
+
+#ifdef USE_PORTAUDIO
+#include "portaudio.h"
+#undef USE_PORTAUDIO
+// determine portaudio version by looking for a #define which is not in V18
+#ifdef paNeverDropInput
+#define USE_PORTAUDIO 19
+#else
+#define USE_PORTAUDIO 18
+#endif
+#endif
+
+#define N_SINTAB 2048
+#include "sintab.h"
+
+
+#define PI 3.1415927
+#define PI2 6.283185307
+#define N_WAV_BUF 10
+
+voice_t *wvoice;
+
+FILE *f_log = NULL;
+int option_waveout = 0;
+static int option_harmonic1 = 10; // 10
+int option_log_frames = 0;
+static int flutter_amp = 64;
+
+static int general_amplitude = 60;
+static int consonant_amp = 26; // 24
+
+int embedded_value[N_EMBEDDED_VALUES];
+
+static int PHASE_INC_FACTOR;
+int samplerate = 0; // this is set by Wavegeninit()
+int samplerate_native=0;
+extern int option_device_number;
+extern int option_quiet;
+
+static wavegen_peaks_t peaks[N_PEAKS];
+static int peak_harmonic[N_PEAKS];
+static int peak_height[N_PEAKS];
+
+#define N_ECHO_BUF 5500 // max of 250mS at 22050 Hz
+static int echo_head;
+static int echo_tail;
+static int echo_length = 0; // period (in sample\) to ensure completion of echo at the end of speech, set in WavegenSetEcho()
+static int echo_amp = 0;
+static short echo_buf[N_ECHO_BUF];
+
+static int voicing;
+static RESONATOR rbreath[N_PEAKS];
+
+static int harm_sqrt_n = 0;
+
+
+#define N_LOWHARM 30
+static int harm_inc[N_LOWHARM]; // only for these harmonics do we interpolate amplitude between steps
+static int *harmspect;
+static int hswitch=0;
+static int hspect[2][MAX_HARMONIC]; // 2 copies, we interpolate between then
+static int max_hval=0;
+
+static int nsamples=0; // number to do
+static int modulation_type = 0;
+static int glottal_flag = 0;
+static int glottal_reduce = 0;
+
+
+WGEN_DATA wdata;
+
+static int amp_ix;
+static int amp_inc;
+static unsigned char *amplitude_env = NULL;
+
+static int samplecount=0; // number done
+static int samplecount_start=0; // count at start of this segment
+static int end_wave=0; // continue to end of wave cycle
+static int wavephase;
+static int phaseinc;
+static int cycle_samples; // number of samples in a cycle at current pitch
+static int cbytes;
+static int hf_factor;
+
+static double minus_pi_t;
+static double two_pi_t;
+
+
+unsigned char *out_ptr;
+unsigned char *out_start;
+unsigned char *out_end;
+int outbuf_size = 0;
+
+// the queue of operations passed to wavegen from sythesize
+long wcmdq[N_WCMDQ][4];
+int wcmdq_head=0;
+int wcmdq_tail=0;
+
+// pitch,speed,
+int embedded_default[N_EMBEDDED_VALUES] = {0,50,170,100,50, 0,0, 0,170,0,0,0,0,0};
+static int embedded_max[N_EMBEDDED_VALUES] = {0,0x7fff,600,300,99,99,99, 0,600,0,0,0,0,4};
+
+#define N_CALLBACK_IX N_WAV_BUF-2 // adjust this delay to match display with the currently spoken word
+int current_source_index=0;
+
+extern FILE *f_wave;
+
+#if (USE_PORTAUDIO == 18)
+static PortAudioStream *pa_stream=NULL;
+#endif
+#if (USE_PORTAUDIO == 19)
+static PaStream *pa_stream=NULL;
+#endif
+
+/* default pitch envelope, a steady fall */
+#define ENV_LEN 128
+
+
+/*
+unsigned char Pitch_env0[ENV_LEN] = {
+ 255,253,251,249,247,245,243,241,239,237,235,233,231,229,227,225,
+ 223,221,219,217,215,213,211,209,207,205,203,201,199,197,195,193,
+ 191,189,187,185,183,181,179,177,175,173,171,169,167,165,163,161,
+ 159,157,155,153,151,149,147,145,143,141,139,137,135,133,131,129,
+ 127,125,123,121,119,117,115,113,111,109,107,105,103,101, 99, 97,
+ 95, 93, 91, 89, 87, 85, 83, 81, 79, 77, 75, 73, 71, 69, 67, 65,
+ 63, 61, 59, 57, 55, 53, 51, 49, 47, 45, 43, 41, 39, 37, 35, 33,
+ 31, 29, 27, 25, 23, 21, 19, 17, 15, 13, 11, 9, 7, 5, 3, 1
+};
+*/
+
+/*
+unsigned char Pitch_long[ENV_LEN] = {
+ 254,249,250,251,252,253,254,254, 255,255,255,255,254,254,253,252,
+ 251,250,249,247,244,242,238,234, 230,225,221,217,213,209,206,203,
+ 199,195,191,187,183,179,175,172, 168,165,162,159,156,153,150,148,
+ 145,143,140,138,136,134,132,130, 128,126,123,120,117,114,111,107,
+ 104,100,96,91, 86,82,77,73, 70,66,63,60, 58,55,53,51,
+ 49,47,46,45, 43,42,40,38, 36,34,31,28, 26,24,22,20,
+ 18,16,14,12, 11,10,9,8, 8,8,8,8, 9,8,8,8,
+ 8,8,7,7, 6,6,6,5, 4,4,3,3, 2,1,1,0
+};
+*/
+
+// 1st index=roughness
+// 2nd index=modulation_type
+// value: bits 0-3 amplitude (16ths), bits 4-7 every n cycles
+#define N_ROUGHNESS 8
+static unsigned char modulation_tab[N_ROUGHNESS][8] = {
+ {0, 0x00, 0x00, 0x00, 0, 0x46, 0xf2, 0x29},
+ {0, 0x2f, 0x00, 0x2f, 0, 0x45, 0xf2, 0x29},
+ {0, 0x2f, 0x00, 0x2e, 0, 0x45, 0xf2, 0x28},
+ {0, 0x2e, 0x00, 0x2d, 0, 0x34, 0xf2, 0x28},
+ {0, 0x2d, 0x2d, 0x2c, 0, 0x34, 0xf2, 0x28},
+ {0, 0x2b, 0x2b, 0x2b, 0, 0x34, 0xf2, 0x28},
+ {0, 0x2a, 0x2a, 0x2a, 0, 0x34, 0xf2, 0x28},
+ {0, 0x29, 0x29, 0x29, 0, 0x34, 0xf2, 0x28},
+};
+
+// Flutter table, to add natural variations to the pitch
+#define N_FLUTTER 0x170
+static int Flutter_inc;
+static const unsigned char Flutter_tab[N_FLUTTER] = {
+ 0x80, 0x9b, 0xb5, 0xcb, 0xdc, 0xe8, 0xed, 0xec,
+ 0xe6, 0xdc, 0xce, 0xbf, 0xb0, 0xa3, 0x98, 0x90,
+ 0x8c, 0x8b, 0x8c, 0x8f, 0x92, 0x94, 0x95, 0x92,
+ 0x8c, 0x83, 0x78, 0x69, 0x59, 0x49, 0x3c, 0x31,
+ 0x2a, 0x29, 0x2d, 0x36, 0x44, 0x56, 0x69, 0x7d,
+ 0x8f, 0x9f, 0xaa, 0xb1, 0xb2, 0xad, 0xa4, 0x96,
+ 0x87, 0x78, 0x69, 0x5c, 0x53, 0x4f, 0x4f, 0x55,
+ 0x5e, 0x6b, 0x7a, 0x88, 0x96, 0xa2, 0xab, 0xb0,
+
+ 0xb1, 0xae, 0xa8, 0xa0, 0x98, 0x91, 0x8b, 0x88,
+ 0x89, 0x8d, 0x94, 0x9d, 0xa8, 0xb2, 0xbb, 0xc0,
+ 0xc1, 0xbd, 0xb4, 0xa5, 0x92, 0x7c, 0x63, 0x4a,
+ 0x32, 0x1e, 0x0e, 0x05, 0x02, 0x05, 0x0f, 0x1e,
+ 0x30, 0x44, 0x59, 0x6d, 0x7f, 0x8c, 0x96, 0x9c,
+ 0x9f, 0x9f, 0x9d, 0x9b, 0x99, 0x99, 0x9c, 0xa1,
+ 0xa9, 0xb3, 0xbf, 0xca, 0xd5, 0xdc, 0xe0, 0xde,
+ 0xd8, 0xcc, 0xbb, 0xa6, 0x8f, 0x77, 0x60, 0x4b,
+
+ 0x3a, 0x2e, 0x28, 0x29, 0x2f, 0x3a, 0x48, 0x59,
+ 0x6a, 0x7a, 0x86, 0x90, 0x94, 0x95, 0x91, 0x89,
+ 0x80, 0x75, 0x6b, 0x62, 0x5c, 0x5a, 0x5c, 0x61,
+ 0x69, 0x74, 0x80, 0x8a, 0x94, 0x9a, 0x9e, 0x9d,
+ 0x98, 0x90, 0x86, 0x7c, 0x71, 0x68, 0x62, 0x60,
+ 0x63, 0x6b, 0x78, 0x88, 0x9b, 0xaf, 0xc2, 0xd2,
+ 0xdf, 0xe6, 0xe7, 0xe2, 0xd7, 0xc6, 0xb2, 0x9c,
+ 0x84, 0x6f, 0x5b, 0x4b, 0x40, 0x39, 0x37, 0x38,
+
+ 0x3d, 0x43, 0x4a, 0x50, 0x54, 0x56, 0x55, 0x52,
+ 0x4d, 0x48, 0x42, 0x3f, 0x3e, 0x41, 0x49, 0x56,
+ 0x67, 0x7c, 0x93, 0xab, 0xc3, 0xd9, 0xea, 0xf6,
+ 0xfc, 0xfb, 0xf4, 0xe7, 0xd5, 0xc0, 0xaa, 0x94,
+ 0x80, 0x71, 0x64, 0x5d, 0x5a, 0x5c, 0x61, 0x68,
+ 0x70, 0x77, 0x7d, 0x7f, 0x7f, 0x7b, 0x74, 0x6b,
+ 0x61, 0x57, 0x4e, 0x48, 0x46, 0x48, 0x4e, 0x59,
+ 0x66, 0x75, 0x84, 0x93, 0x9f, 0xa7, 0xab, 0xaa,
+
+ 0xa4, 0x99, 0x8b, 0x7b, 0x6a, 0x5b, 0x4e, 0x46,
+ 0x43, 0x45, 0x4d, 0x5a, 0x6b, 0x7f, 0x92, 0xa6,
+ 0xb8, 0xc5, 0xcf, 0xd3, 0xd2, 0xcd, 0xc4, 0xb9,
+ 0xad, 0xa1, 0x96, 0x8e, 0x89, 0x87, 0x87, 0x8a,
+ 0x8d, 0x91, 0x92, 0x91, 0x8c, 0x84, 0x78, 0x68,
+ 0x55, 0x41, 0x2e, 0x1c, 0x0e, 0x05, 0x01, 0x05,
+ 0x0f, 0x1f, 0x34, 0x4d, 0x68, 0x81, 0x9a, 0xb0,
+ 0xc1, 0xcd, 0xd3, 0xd3, 0xd0, 0xc8, 0xbf, 0xb5,
+
+ 0xab, 0xa4, 0x9f, 0x9c, 0x9d, 0xa0, 0xa5, 0xaa,
+ 0xae, 0xb1, 0xb0, 0xab, 0xa3, 0x96, 0x87, 0x76,
+ 0x63, 0x51, 0x42, 0x36, 0x2f, 0x2d, 0x31, 0x3a,
+ 0x48, 0x59, 0x6b, 0x7e, 0x8e, 0x9c, 0xa6, 0xaa,
+ 0xa9, 0xa3, 0x98, 0x8a, 0x7b, 0x6c, 0x5d, 0x52,
+ 0x4a, 0x48, 0x4a, 0x50, 0x5a, 0x67, 0x75, 0x82
+};
+
+// waveform shape table for HF peaks, formants 6,7,8
+#define N_WAVEMULT 128
+static int wavemult_offset=0;
+static int wavemult_max=0;
+
+// the presets are for 22050 Hz sample rate.
+// A different rate will need to recalculate the presets in WavegenInit()
+static unsigned char wavemult[N_WAVEMULT] = {
+ 0, 0, 0, 2, 3, 5, 8, 11, 14, 18, 22, 27, 32, 37, 43, 49,
+ 55, 62, 69, 76, 83, 90, 98,105,113,121,128,136,144,152,159,166,
+ 174,181,188,194,201,207,213,218,224,228,233,237,240,244,246,249,
+ 251,252,253,253,253,253,252,251,249,246,244,240,237,233,228,224,
+ 218,213,207,201,194,188,181,174,166,159,152,144,136,128,121,113,
+ 105, 98, 90, 83, 76, 69, 62, 55, 49, 43, 37, 32, 27, 22, 18, 14,
+ 11, 8, 5, 3, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+
+
+// set from y = pow(2,x) * 128, x=-1 to 1
+unsigned char pitch_adjust_tab[MAX_PITCH_VALUE+1] = {
+ 64, 65, 66, 67, 68, 69, 70, 71,
+ 72, 73, 74, 75, 76, 77, 78, 79,
+ 80, 81, 82, 83, 84, 86, 87, 88,
+ 89, 91, 92, 93, 94, 96, 97, 98,
+ 100,101,103,104,105,107,108,110,
+ 111,113,115,116,118,119,121,123,
+ 124,126,128,130,132,133,135,137,
+ 139,141,143,145,147,149,151,153,
+ 155,158,160,162,164,167,169,171,
+ 174,176,179,181,184,186,189,191,
+ 194,197,199,202,205,208,211,214,
+ 217,220,223,226,229,232,236,239,
+ 242,246,249,252, 254,255 };
+
+int WavegenFill(int fill_zeros);
+
+
+#ifdef LOG_FRAMES
+static void LogMarker(int type, int value)
+{//=======================================
+ if(option_log_frames == 0)
+ return;
+
+ if((type == espeakEVENT_PHONEME) || (type == espeakEVENT_SENTENCE))
+ {
+ f_log=fopen("log-espeakedit","a");
+ if(f_log)
+ {
+ if(type == espeakEVENT_PHONEME)
+ fprintf(f_log,"Phoneme [%s]\n",WordToString(value));
+ else
+ fprintf(f_log,"\n");
+ fclose(f_log);
+ f_log = NULL;
+ }
+ }
+}
+#endif
+
+void WcmdqStop()
+{//=============
+ wcmdq_head = 0;
+ wcmdq_tail = 0;
+#ifdef USE_PORTAUDIO
+ Pa_AbortStream(pa_stream);
+#endif
+}
+
+
+int WcmdqFree()
+{//============
+ int i;
+ i = wcmdq_head - wcmdq_tail;
+ if(i <= 0) i += N_WCMDQ;
+ return(i);
+}
+
+int WcmdqUsed()
+{//============
+ return(N_WCMDQ - WcmdqFree());
+}
+
+
+void WcmdqInc()
+{//============
+ wcmdq_tail++;
+ if(wcmdq_tail >= N_WCMDQ) wcmdq_tail=0;
+}
+
+static void WcmdqIncHead()
+{//=======================
+ wcmdq_head++;
+ if(wcmdq_head >= N_WCMDQ) wcmdq_head=0;
+}
+
+
+
+// data points from which to make the presets for pk_shape1 and pk_shape2
+#define PEAKSHAPEW 256
+static const float pk_shape_x[2][8] = {
+ {0,-0.6f, 0.0f, 0.6f, 1.4f, 2.5f, 4.5f, 5.5f},
+ {0,-0.6f, 0.0f, 0.6f, 1.4f, 2.0f, 4.5f, 5.5f }};
+static const float pk_shape_y[2][8] = {
+ {0, 67, 81, 67, 31, 14, 0, -6} ,
+ {0, 77, 81, 77, 31, 7, 0, -6 }};
+
+unsigned char pk_shape1[PEAKSHAPEW+1] = {
+ 255,254,254,254,254,254,253,253,252,251,251,250,249,248,247,246,
+ 245,244,242,241,239,238,236,234,233,231,229,227,225,223,220,218,
+ 216,213,211,209,207,205,203,201,199,197,195,193,191,189,187,185,
+ 183,180,178,176,173,171,169,166,164,161,159,156,154,151,148,146,
+ 143,140,138,135,132,129,126,123,120,118,115,112,108,105,102, 99,
+ 96, 95, 93, 91, 90, 88, 86, 85, 83, 82, 80, 79, 77, 76, 74, 73,
+ 72, 70, 69, 68, 67, 66, 64, 63, 62, 61, 60, 59, 58, 57, 56, 55,
+ 55, 54, 53, 52, 52, 51, 50, 50, 49, 48, 48, 47, 47, 46, 46, 46,
+ 45, 45, 45, 44, 44, 44, 44, 44, 44, 44, 43, 43, 43, 43, 44, 43,
+ 42, 42, 41, 40, 40, 39, 38, 38, 37, 36, 36, 35, 35, 34, 33, 33,
+ 32, 32, 31, 30, 30, 29, 29, 28, 28, 27, 26, 26, 25, 25, 24, 24,
+ 23, 23, 22, 22, 21, 21, 20, 20, 19, 19, 18, 18, 18, 17, 17, 16,
+ 16, 15, 15, 15, 14, 14, 13, 13, 13, 12, 12, 11, 11, 11, 10, 10,
+ 10, 9, 9, 9, 8, 8, 8, 7, 7, 7, 7, 6, 6, 6, 5, 5,
+ 5, 5, 4, 4, 4, 4, 4, 3, 3, 3, 3, 2, 2, 2, 2, 2,
+ 2, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0 };
+
+static unsigned char pk_shape2[PEAKSHAPEW+1] = {
+ 255,254,254,254,254,254,254,254,254,254,253,253,253,253,252,252,
+ 252,251,251,251,250,250,249,249,248,248,247,247,246,245,245,244,
+ 243,243,242,241,239,237,235,233,231,229,227,225,223,221,218,216,
+ 213,211,208,205,203,200,197,194,191,187,184,181,178,174,171,167,
+ 163,160,156,152,148,144,140,136,132,127,123,119,114,110,105,100,
+ 96, 94, 91, 88, 86, 83, 81, 78, 76, 74, 71, 69, 66, 64, 62, 60,
+ 57, 55, 53, 51, 49, 47, 44, 42, 40, 38, 36, 34, 32, 30, 29, 27,
+ 25, 23, 21, 19, 18, 16, 14, 12, 11, 9, 7, 6, 4, 3, 1, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0 };
+
+static unsigned char *pk_shape;
+
+
+static void WavegenInitPkData(int which)
+{//=====================================
+// this is only needed to set up the presets for pk_shape1 and pk_shape2
+// These have already been pre-calculated and preset
+#ifdef deleted
+ int ix;
+ int p;
+ float x;
+ float y[PEAKSHAPEW];
+ float maxy=0;
+
+ if(which==0)
+ pk_shape = pk_shape1;
+ else
+ pk_shape = pk_shape2;
+
+ p = 0;
+ for(ix=0;ix<PEAKSHAPEW;ix++)
+ {
+ x = (4.5*ix)/PEAKSHAPEW;
+ if(x >= pk_shape_x[which][p+3]) p++;
+ y[ix] = polint(&pk_shape_x[which][p],&pk_shape_y[which][p],3,x);
+ if(y[ix] > maxy) maxy = y[ix];
+ }
+ for(ix=0;ix<PEAKSHAPEW;ix++)
+ {
+ p = (int)(y[ix]*255/maxy);
+ pk_shape[ix] = (p >= 0) ? p : 0;
+ }
+ pk_shape[PEAKSHAPEW]=0;
+#endif
+} // end of WavegenInitPkData
+
+
+
+#ifdef USE_PORTAUDIO
+// PortAudio interface
+
+static int userdata[4];
+static PaError pa_init_err=0;
+static int out_channels=1;
+
+#if USE_PORTAUDIO == 18
+static int WaveCallback(void *inputBuffer, void *outputBuffer,
+ unsigned long framesPerBuffer, PaTimestamp outTime, void *userData )
+#else
+static int WaveCallback(const void *inputBuffer, void *outputBuffer,
+ long unsigned int framesPerBuffer, const PaStreamCallbackTimeInfo *outTime,
+ PaStreamCallbackFlags flags, void *userData )
+#endif
+{
+ int ix;
+ int result;
+ unsigned char *p;
+
+ out_ptr = out_start = (unsigned char *)outputBuffer;
+ out_end = out_ptr + framesPerBuffer*2;
+
+#ifdef LIBRARY
+ event_list_ix = 0;
+#endif
+
+ result = WavegenFill(1);
+
+#ifdef LIBRARY
+ count_samples += framesPerBuffer;
+ if(synth_callback)
+ {
+ // synchronous-playback mode, allow the calling process to abort the speech
+ event_list[event_list_ix].type = espeakEVENT_LIST_TERMINATED; // indicates end of event list
+ event_list[event_list_ix].user_data = 0;
+
+ if(synth_callback(NULL,0,event_list) == 1)
+ {
+ SpeakNextClause(NULL,NULL,2); // stop speaking
+ result = 1;
+ }
+ }
+#endif
+
+#ifdef ARCH_BIG
+ {
+ // swap the order of bytes in each sound sample in the portaudio buffer
+ int c;
+ out_ptr = (unsigned char *)outputBuffer;
+ out_end = out_ptr + framesPerBuffer*2;
+ while(out_ptr < out_end)
+ {
+ c = out_ptr[0];
+ out_ptr[0] = out_ptr[1];
+ out_ptr[1] = c;
+ out_ptr += 2;
+ }
+ }
+#endif
+
+ if(out_channels == 2)
+ {
+ // sound output can only do stereo, not mono. Duplicate each sound sample to
+ // produce 2 channels.
+ out_ptr = (unsigned char *)outputBuffer;
+ for(ix=framesPerBuffer-1; ix>=0; ix--)
+ {
+ p = &out_ptr[ix*4];
+ p[3] = p[1] = out_ptr[ix*2 + 1];
+ p[2] = p[0] = out_ptr[ix*2];
+ }
+ }
+
+#if USE_PORTAUDIO == 18
+#ifdef PLATFORM_WINDOWS
+ return(result);
+#endif
+ if(result != 0)
+ {
+ static int end_timer = 0;
+ if(end_timer == 0)
+ end_timer = 4;
+ if(end_timer > 0)
+ {
+ end_timer--;
+ if(end_timer == 0)
+ return(1);
+ }
+ }
+ return(0);
+#else
+ return(result);
+#endif
+
+} // end of WaveCallBack
+
+
+#if USE_PORTAUDIO == 19
+/* This is a fixed version of Pa_OpenDefaultStream() for use if the version in portaudio V19
+ is broken */
+
+static PaError Pa_OpenDefaultStream2( PaStream** stream,
+ int inputChannelCount,
+ int outputChannelCount,
+ PaSampleFormat sampleFormat,
+ double sampleRate,
+ unsigned long framesPerBuffer,
+ PaStreamCallback *streamCallback,
+ void *userData )
+{
+ PaError result;
+ PaStreamParameters hostApiOutputParameters;
+
+ if(option_device_number >= 0)
+ hostApiOutputParameters.device = option_device_number;
+ else
+ hostApiOutputParameters.device = Pa_GetDefaultOutputDevice();
+
+ if( hostApiOutputParameters.device == paNoDevice )
+ return paDeviceUnavailable;
+
+ hostApiOutputParameters.channelCount = outputChannelCount;
+ hostApiOutputParameters.sampleFormat = sampleFormat;
+ /* defaultHighOutputLatency is used below instead of
+ defaultLowOutputLatency because it is more important for the default
+ stream to work reliably than it is for it to work with the lowest
+ latency.
+ */
+ hostApiOutputParameters.suggestedLatency =
+ Pa_GetDeviceInfo( hostApiOutputParameters.device )->defaultHighOutputLatency;
+ hostApiOutputParameters.hostApiSpecificStreamInfo = NULL;
+
+ result = Pa_OpenStream(
+ stream, NULL, &hostApiOutputParameters, sampleRate, framesPerBuffer, paNoFlag, streamCallback, userData );
+
+ return(result);
+}
+#endif
+
+
+int WavegenOpenSound()
+{//===================
+ PaError err, err2;
+ PaError active;
+
+ if(option_waveout || option_quiet)
+ {
+ // writing to WAV file, not to portaudio
+ return(0);
+ }
+
+#if USE_PORTAUDIO == 18
+ active = Pa_StreamActive(pa_stream);
+#else
+ active = Pa_IsStreamActive(pa_stream);
+#endif
+
+ if(active == 1)
+ return(0);
+ if(active < 0)
+ {
+ out_channels = 1;
+
+#if USE_PORTAUDIO == 18
+ err2 = Pa_OpenDefaultStream(&pa_stream,0,1,paInt16,samplerate,512,N_WAV_BUF,WaveCallback,(void *)userdata);
+
+ if(err2 == paInvalidChannelCount)
+ {
+ // failed to open with mono, try stereo
+ out_channels=2;
+ err2 = Pa_OpenDefaultStream(&pa_stream,0,2,paInt16,samplerate,512,N_WAV_BUF,WaveCallback,(void *)userdata);
+ }
+#else
+ err2 = Pa_OpenDefaultStream2(&pa_stream,0,1,paInt16,(double)samplerate,512,WaveCallback,(void *)userdata);
+
+ if(err2 == paInvalidChannelCount)
+ {
+ // failed to open with mono, try stereo
+ out_channels=2;
+ err2 = Pa_OpenDefaultStream(&pa_stream,0,2,paInt16,(double)samplerate,512,WaveCallback,(void *)userdata);
+ }
+#endif
+ }
+ err = Pa_StartStream(pa_stream);
+
+#if USE_PORTAUDIO == 19
+ if(err == paStreamIsNotStopped)
+ {
+ // not sure why we need this, but PA v19 seems to need it
+ err = Pa_StopStream(pa_stream);
+ err = Pa_StartStream(pa_stream);
+ }
+#endif
+
+ if(err != paNoError)
+ {
+ // exit speak if we can't open the sound device - this is OK if speak is being run for each utterance
+ exit(2);
+ }
+
+ return(0);
+}
+
+
+
+int WavegenCloseSound()
+{//====================
+ PaError active;
+
+ // check whether speaking has finished, and close the stream
+ if(pa_stream != NULL)
+ {
+#if USE_PORTAUDIO == 18
+ active = Pa_StreamActive(pa_stream);
+#else
+ active = Pa_IsStreamActive(pa_stream);
+#endif
+ if(WcmdqUsed() == 0) // also check that the queue is empty
+ {
+ if(active == 0)
+ {
+ Pa_CloseStream(pa_stream);
+ pa_stream = NULL;
+ return(1);
+ }
+ }
+ else
+ {
+ WavegenOpenSound(); // still items in the queue, shouldn't be closed
+ }
+ }
+ return(0);
+}
+
+
+int WavegenInitSound()
+{//===================
+ PaError err;
+
+ if(option_quiet)
+ return(0);
+
+ // PortAudio sound output library
+ err = Pa_Initialize();
+ pa_init_err = err;
+ if(err != paNoError)
+ {
+ fprintf(stderr,"Failed to initialise the PortAudio sound\n");
+ return(1);
+ }
+ return(0);
+}
+#else
+int WavegenOpenSound()
+{//===================
+ return(0);
+}
+int WavegenCloseSound()
+{//====================
+ return(0);
+}
+int WavegenInitSound()
+{//===================
+ return(0);
+}
+#endif
+
+
+void WavegenInit(int rate, int wavemult_fact)
+{//==========================================
+ int ix;
+ double x;
+
+ if(wavemult_fact == 0)
+ wavemult_fact=60; // default
+
+ wvoice = NULL;
+ samplerate = samplerate_native = rate;
+ PHASE_INC_FACTOR = 0x8000000 / samplerate; // assumes pitch is Hz*32
+ Flutter_inc = (64 * samplerate)/rate;
+ samplecount = 0;
+ nsamples = 0;
+ wavephase = 0x7fffffff;
+ max_hval = 0;
+
+ wdata.amplitude = 32;
+ wdata.prev_was_synth = 0;
+
+ for(ix=0; ix<N_EMBEDDED_VALUES; ix++)
+ embedded_value[ix] = embedded_default[ix];
+
+
+ // set up window to generate a spread of harmonics from a
+ // single peak for HF peaks
+ wavemult_max = (samplerate * wavemult_fact)/(256 * 50);
+ if(wavemult_max > N_WAVEMULT) wavemult_max = N_WAVEMULT;
+
+ wavemult_offset = wavemult_max/2;
+
+ if(samplerate != 22050)
+ {
+ // wavemult table has preset values for 22050 Hz, we only need to
+ // recalculate them if we have a different sample rate
+ for(ix=0; ix<wavemult_max; ix++)
+ {
+ x = 127*(1.0 - cos(PI2*ix/wavemult_max));
+ wavemult[ix] = (int)x;
+ }
+ }
+
+ WavegenInitPkData(1);
+ WavegenInitPkData(0);
+ pk_shape = pk_shape2; // pk_shape2
+
+#ifdef INCLUDE_KLATT
+ KlattInit();
+#endif
+
+#ifdef LOG_FRAMES
+remove("log-espeakedit");
+#endif
+} // end of WavegenInit
+
+
+int GetAmplitude(void)
+{//===================
+ int amp;
+
+ // normal, none, reduced, moderate, strong
+ static const unsigned char amp_emphasis[5] = {16, 16, 10, 16, 22};
+
+ amp = (embedded_value[EMBED_A])*55/100;
+ general_amplitude = amp * amp_emphasis[embedded_value[EMBED_F]] / 16;
+ return(general_amplitude);
+}
+
+
+static void WavegenSetEcho(void)
+{//=============================
+ int delay;
+ int amp;
+
+ voicing = wvoice->voicing;
+ delay = wvoice->echo_delay;
+ amp = wvoice->echo_amp;
+
+ if(delay >= N_ECHO_BUF)
+ delay = N_ECHO_BUF-1;
+ if(amp > 100)
+ amp = 100;
+
+ memset(echo_buf,0,sizeof(echo_buf));
+ echo_tail = 0;
+
+ if(embedded_value[EMBED_H] > 0)
+ {
+ // set echo from an embedded command in the text
+ amp = embedded_value[EMBED_H];
+ delay = 130;
+ }
+ if(embedded_value[EMBED_T] > 0)
+ {
+ // announcing punctuation
+ amp = embedded_value[EMBED_T] * 8;
+ delay = 60;
+ }
+
+ if(delay == 0)
+ amp = 0;
+
+ echo_head = (delay * samplerate)/1000;
+ echo_length = echo_head; // ensure completion of echo at the end of speech. Use 1 delay period?
+ if(amp == 0)
+ echo_length = 0;
+ if(amp > 20)
+ echo_length = echo_head * 2; // perhaps allow 2 echo periods if the echo is loud.
+
+ // echo_amp units are 1/256ths of the amplitude of the original sound.
+ echo_amp = amp;
+ // compensate (partially) for increase in amplitude due to echo
+ general_amplitude = GetAmplitude();
+ general_amplitude = ((general_amplitude * (500-amp))/500);
+} // end of WavegenSetEcho
+
+
+
+int PeaksToHarmspect(wavegen_peaks_t *peaks, int pitch, int *htab, int control)
+{//============================================================================
+// Calculate the amplitude of each harmonics from the formants
+// Only for formants 0 to 5
+
+// control 0=initial call, 1=every 64 cycles
+
+ // pitch and freqs are Hz<<16
+
+ int f;
+ wavegen_peaks_t *p;
+ int fp; // centre freq of peak
+ int fhi; // high freq of peak
+ int h; // harmonic number
+ int pk;
+ int hmax;
+ int hmax_samplerate; // highest harmonic allowed for the samplerate
+ int x;
+ int ix;
+ int h1;
+
+#ifdef SPECT_EDITOR
+ if(harm_sqrt_n > 0)
+ return(HarmToHarmspect(pitch,htab));
+#endif
+
+ // initialise as much of *out as we will need
+ if(wvoice == NULL)
+ return(1);
+ hmax = (peaks[wvoice->n_harmonic_peaks].freq + peaks[wvoice->n_harmonic_peaks].right)/pitch;
+ if(hmax >= MAX_HARMONIC)
+ hmax = MAX_HARMONIC-1;
+
+ // restrict highest harmonic to half the samplerate
+ hmax_samplerate = (((samplerate * 19)/40) << 16)/pitch; // only 95% of Nyquist freq
+// hmax_samplerate = (samplerate << 16)/(pitch*2);
+
+ if(hmax > hmax_samplerate)
+ hmax = hmax_samplerate;
+
+ for(h=0;h<=hmax;h++)
+ htab[h]=0;
+
+ h=0;
+ for(pk=0; pk<=wvoice->n_harmonic_peaks; pk++)
+ {
+ p = &peaks[pk];
+ if((p->height == 0) || (fp = p->freq)==0)
+ continue;
+
+ fhi = p->freq + p->right;
+ h = ((p->freq - p->left) / pitch) + 1;
+ if(h <= 0) h = 1;
+
+ for(f=pitch*h; f < fp; f+=pitch)
+ {
+ htab[h++] += pk_shape[(fp-f)/(p->left>>8)] * p->height;
+ }
+ for(; f < fhi; f+=pitch)
+ {
+ htab[h++] += pk_shape[(f-fp)/(p->right>>8)] * p->height;
+ }
+ }
+
+{
+int y;
+int h2;
+ // increase bass
+ y = peaks[1].height * 10; // addition as a multiple of 1/256s
+ h2 = (1000<<16)/pitch; // decrease until 1000Hz
+ if(h2 > 0)
+ {
+ x = y/h2;
+ h = 1;
+ while(y > 0)
+ {
+ htab[h++] += y;
+ y -= x;
+ }
+ }
+}
+
+ // find the nearest harmonic for HF peaks where we don't use shape
+ for(; pk<N_PEAKS; pk++)
+ {
+ x = peaks[pk].height >> 14;
+ peak_height[pk] = (x * x * 5)/2;
+
+ // find the nearest harmonic for HF peaks where we don't use shape
+ if(control == 0)
+ {
+ // set this initially, but make changes only at the quiet point
+ peak_harmonic[pk] = peaks[pk].freq / pitch;
+ }
+ // only use harmonics up to half the samplerate
+ if(peak_harmonic[pk] >= hmax_samplerate)
+ peak_height[pk] = 0;
+ }
+
+ // convert from the square-rooted values
+ f = 0;
+ for(h=0; h<=hmax; h++, f+=pitch)
+ {
+ x = htab[h] >> 15;
+ htab[h] = (x * x) >> 8;
+
+ if((ix = (f >> 19)) < N_TONE_ADJUST)
+ {
+ htab[h] = (htab[h] * wvoice->tone_adjust[ix]) >> 13; // index tone_adjust with Hz/8
+ }
+ }
+
+ // adjust the amplitude of the first harmonic, affects tonal quality
+ h1 = htab[1] * option_harmonic1;
+ htab[1] = h1/8;
+
+
+ // calc intermediate increments of LF harmonics
+ if(control & 1)
+ {
+ for(h=1; h<N_LOWHARM; h++)
+ {
+ harm_inc[h] = (htab[h] - harmspect[h]) >> 3;
+ }
+ }
+
+ return(hmax); // highest harmonic number
+} // end of PeaksToHarmspect
+
+
+
+static void AdvanceParameters()
+{//============================
+// Called every 64 samples to increment the formant freq, height, and widths
+
+ int x;
+ int ix;
+ static int Flutter_ix = 0;
+
+ // advance the pitch
+ wdata.pitch_ix += wdata.pitch_inc;
+ if((ix = wdata.pitch_ix>>8) > 127) ix = 127;
+ x = wdata.pitch_env[ix] * wdata.pitch_range;
+ wdata.pitch = (x>>8) + wdata.pitch_base;
+
+ amp_ix += amp_inc;
+
+ /* add pitch flutter */
+ if(Flutter_ix >= (N_FLUTTER*64))
+ Flutter_ix = 0;
+ x = ((int)(Flutter_tab[Flutter_ix >> 6])-0x80) * flutter_amp;
+ Flutter_ix += Flutter_inc;
+ wdata.pitch += x;
+ if(wdata.pitch < 102400)
+ wdata.pitch = 102400; // min pitch, 25 Hz (25 << 12)
+
+ if(samplecount == samplecount_start)
+ return;
+
+ for(ix=0; ix <= wvoice->n_harmonic_peaks; ix++)
+ {
+ peaks[ix].freq1 += peaks[ix].freq_inc;
+ peaks[ix].freq = int(peaks[ix].freq1);
+ peaks[ix].height1 += peaks[ix].height_inc;
+ if((peaks[ix].height = int(peaks[ix].height1)) < 0)
+ peaks[ix].height = 0;
+ peaks[ix].left1 += peaks[ix].left_inc;
+ peaks[ix].left = int(peaks[ix].left1);
+ peaks[ix].right1 += peaks[ix].right_inc;
+ peaks[ix].right = int(peaks[ix].right1);
+ }
+ for(;ix < N_PEAKS; ix++)
+ {
+ // formants 6,7,8 don't have a width parameter
+ peaks[ix].freq1 += peaks[ix].freq_inc;
+ peaks[ix].freq = int(peaks[ix].freq1);
+ peaks[ix].height1 += peaks[ix].height_inc;
+ if((peaks[ix].height = int(peaks[ix].height1)) < 0)
+ peaks[ix].height = 0;
+ }
+
+#ifdef SPECT_EDITOR
+ if(harm_sqrt_n != 0)
+ {
+ // We are generating from a harmonic spectrum at a given pitch, not from formant peaks
+ for(ix=0; ix<harm_sqrt_n; ix++)
+ harm_sqrt[ix] += harm_sqrt_inc[ix];
+ }
+#endif
+} // end of AdvanceParameters
+
+
+#ifndef PLATFORM_RISCOS
+static double resonator(RESONATOR *r, double input)
+{//================================================
+ double x;
+
+ x = r->a * input + r->b * r->x1 + r->c * r->x2;
+ r->x2 = r->x1;
+ r->x1 = x;
+
+ return x;
+}
+
+
+
+static void setresonator(RESONATOR *rp, int freq, int bwidth, int init)
+{//====================================================================
+// freq Frequency of resonator in Hz
+// bwidth Bandwidth of resonator in Hz
+// init Initialize internal data
+
+ double x;
+ double arg;
+
+ if(init)
+ {
+ rp->x1 = 0;
+ rp->x2 = 0;
+ }
+
+ // x = exp(-pi * bwidth * t)
+ arg = minus_pi_t * bwidth;
+ x = exp(arg);
+
+ // c = -(x*x)
+ rp->c = -(x * x);
+
+ // b = x * 2*cos(2 pi * freq * t)
+
+ arg = two_pi_t * freq;
+ rp->b = x * cos(arg) * 2.0;
+
+ // a = 1.0 - b - c
+ rp->a = 1.0 - rp->b - rp->c;
+} // end if setresonator
+#endif
+
+
+void InitBreath(void)
+{//==================
+#ifndef PLATFORM_RISCOS
+ int ix;
+
+ minus_pi_t = -PI / samplerate;
+ two_pi_t = -2.0 * minus_pi_t;
+
+ for(ix=0; ix<N_PEAKS; ix++)
+ {
+ setresonator(&rbreath[ix],2000,200,1);
+ }
+#endif
+} // end of InitBreath
+
+
+
+static void SetBreath()
+{//====================
+#ifndef PLATFORM_RISCOS
+ int pk;
+
+ if(wvoice->breath[0] == 0)
+ return;
+
+ for(pk=1; pk<N_PEAKS; pk++)
+ {
+ if(wvoice->breath[pk] != 0)
+ {
+ // breath[0] indicates that some breath formants are needed
+ // set the freq from the current ynthesis formant and the width from the voice data
+ setresonator(&rbreath[pk], peaks[pk].freq >> 16, wvoice->breathw[pk],0);
+ }
+ }
+#endif
+} // end of SetBreath
+
+
+static int ApplyBreath(void)
+{//=========================
+ int value = 0;
+#ifndef PLATFORM_RISCOS
+ int noise;
+ int ix;
+ int amp;
+
+ // use two random numbers, for alternate formants
+ noise = (rand() & 0x3fff) - 0x2000;
+
+ for(ix=1; ix < N_PEAKS; ix++)
+ {
+ if((amp = wvoice->breath[ix]) != 0)
+ {
+ amp *= (peaks[ix].height >> 14);
+ value += int(resonator(&rbreath[ix],noise) * amp);
+ }
+ }
+#endif
+ return (value);
+}
+
+
+
+int Wavegen()
+{//==========
+ unsigned short waveph;
+ unsigned short theta;
+ int total;
+ int h;
+ int ix;
+ int z, z1, z2;
+ int echo;
+ int ov;
+ static int maxh, maxh2;
+ int pk;
+ signed char c;
+ int sample;
+ int amp;
+ int modn_amp, modn_period;
+ static int agc = 256;
+ static int h_switch_sign = 0;
+ static int cycle_count = 0;
+ static int amplitude2 = 0; // adjusted for pitch
+
+ // continue until the output buffer is full, or
+ // the required number of samples have been produced
+
+ for(;;)
+ {
+ if((end_wave==0) && (samplecount==nsamples))
+ return(0);
+
+ if((samplecount & 0x3f) == 0)
+ {
+ // every 64 samples, adjust the parameters
+ if(samplecount == 0)
+ {
+ hswitch = 0;
+ harmspect = hspect[0];
+ maxh2 = PeaksToHarmspect(peaks, wdata.pitch<<4, hspect[0], 0);
+
+ // adjust amplitude to compensate for fewer harmonics at higher pitch
+ amplitude2 = (wdata.amplitude * wdata.pitch)/(100 << 11);
+
+ // switch sign of harmonics above about 900Hz, to reduce max peak amplitude
+ h_switch_sign = 890 / (wdata.pitch >> 12);
+ }
+ else
+ AdvanceParameters();
+
+ // pitch is Hz<<12
+ phaseinc = (wdata.pitch>>7) * PHASE_INC_FACTOR;
+ cycle_samples = samplerate/(wdata.pitch >> 12); // sr/(pitch*2)
+ hf_factor = wdata.pitch >> 11;
+
+ maxh = maxh2;
+ harmspect = hspect[hswitch];
+ hswitch ^= 1;
+ maxh2 = PeaksToHarmspect(peaks, wdata.pitch<<4, hspect[hswitch], 1);
+
+ SetBreath();
+ }
+ else
+ if((samplecount & 0x07) == 0)
+ {
+ for(h=1; h<N_LOWHARM && h<=maxh2 && h<=maxh; h++)
+ {
+ harmspect[h] += harm_inc[h];
+ }
+
+ // bring automctic gain control back towards unity
+ if(agc < 256) agc++;
+ }
+
+ samplecount++;
+
+ if(wavephase > 0)
+ {
+ wavephase += phaseinc;
+ if(wavephase < 0)
+ {
+ // sign has changed, reached a quiet point in the waveform
+ cbytes = wavemult_offset - (cycle_samples)/2;
+ if(samplecount > nsamples)
+ return(0);
+
+ cycle_count++;
+
+ for(pk=wvoice->n_harmonic_peaks+1; pk<N_PEAKS; pk++)
+ {
+ // find the nearest harmonic for HF peaks where we don't use shape
+ peak_harmonic[pk] = peaks[pk].freq / (wdata.pitch*16);
+ }
+
+ // adjust amplitude to compensate for fewer harmonics at higher pitch
+ amplitude2 = (wdata.amplitude * wdata.pitch)/(100 << 11);
+
+ if(glottal_flag > 0)
+ {
+ if(glottal_flag == 3)
+ {
+ if((nsamples-samplecount) < (cycle_samples*2))
+ {
+ // Vowel before glottal-stop.
+ // This is the start of the penultimate cycle, reduce its amplitude
+ glottal_flag = 2;
+ amplitude2 = (amplitude2 * glottal_reduce)/256;
+ }
+ }
+ else
+ if(glottal_flag == 4)
+ {
+ // Vowel following a glottal-stop.
+ // This is the start of the second cycle, reduce its amplitude
+ glottal_flag = 2;
+ amplitude2 = (amplitude2 * glottal_reduce)/256;
+ }
+ else
+ {
+ glottal_flag--;
+ }
+ }
+
+ if(amplitude_env != NULL)
+ {
+ // amplitude envelope is only used for creaky voice effect on certain vowels/tones
+ if((ix = amp_ix>>8) > 127) ix = 127;
+ amp = amplitude_env[ix];
+ amplitude2 = (amplitude2 * amp)/128;
+// if(amp < 255)
+// modulation_type = 7;
+ }
+
+ // introduce roughness into the sound by reducing the amplitude of
+ modn_period = 0;
+ if(voice->roughness < N_ROUGHNESS)
+ {
+ modn_period = modulation_tab[voice->roughness][modulation_type];
+ modn_amp = modn_period & 0xf;
+ modn_period = modn_period >> 4;
+ }
+
+ if(modn_period != 0)
+ {
+ if(modn_period==0xf)
+ {
+ // just once */
+ amplitude2 = (amplitude2 * modn_amp)/16;
+ modulation_type = 0;
+ }
+ else
+ {
+ // reduce amplitude every [modn_period} cycles
+ if((cycle_count % modn_period)==0)
+ amplitude2 = (amplitude2 * modn_amp)/16;
+ }
+ }
+ }
+ }
+ else
+ {
+ wavephase += phaseinc;
+ }
+ waveph = (unsigned short)(wavephase >> 16);
+ total = 0;
+
+ // apply HF peaks, formants 6,7,8
+ // add a single harmonic and then spread this my multiplying by a
+ // window. This is to reduce the processing power needed to add the
+ // higher frequence harmonics.
+ cbytes++;
+ if(cbytes >=0 && cbytes<wavemult_max)
+ {
+ for(pk=wvoice->n_harmonic_peaks+1; pk<N_PEAKS; pk++)
+ {
+ theta = peak_harmonic[pk] * waveph;
+ total += (long)sin_tab[theta >> 5] * peak_height[pk];
+ }
+
+ // spread the peaks by multiplying by a window
+ total = (long)(total / hf_factor) * wavemult[cbytes];
+ }
+
+ // apply main peaks, formants 0 to 5
+#ifdef USE_ASSEMBLER_1
+ // use an optimised routine for this loop, if available
+ total += AddSineWaves(waveph, h_switch_sign, maxh, harmspect); // call an assembler code routine
+#else
+ theta = waveph;
+
+ for(h=1; h<=h_switch_sign; h++)
+ {
+ total += (int(sin_tab[theta >> 5]) * harmspect[h]);
+ theta += waveph;
+ }
+ while(h<=maxh)
+ {
+ total -= (int(sin_tab[theta >> 5]) * harmspect[h]);
+ theta += waveph;
+ h++;
+ }
+#endif
+
+ if(voicing != 64)
+ {
+ total = (total >> 6) * voicing;
+ }
+
+#ifndef PLATFORM_RISCOS
+ if(wvoice->breath[0])
+ {
+ total += ApplyBreath();
+ }
+#endif
+
+ // mix with sampled wave if required
+ z2 = 0;
+ if(wdata.mix_wavefile_ix < wdata.n_mix_wavefile)
+ {
+ if(wdata.mix_wave_scale == 0)
+ {
+ // a 16 bit sample
+ c = wdata.mix_wavefile[wdata.mix_wavefile_ix+1];
+ sample = wdata.mix_wavefile[wdata.mix_wavefile_ix] + (c * 256);
+ wdata.mix_wavefile_ix += 2;
+ }
+ else
+ {
+ // a 8 bit sample, scaled
+ sample = (signed char)wdata.mix_wavefile[wdata.mix_wavefile_ix++] * wdata.mix_wave_scale;
+ }
+ z2 = (sample * wdata.amplitude_v) >> 10;
+ z2 = (z2 * wdata.mix_wave_amp)/32;
+ }
+
+ z1 = z2 + (((total>>8) * amplitude2) >> 13);
+
+ echo = (echo_buf[echo_tail++] * echo_amp);
+ z1 += echo >> 8;
+ if(echo_tail >= N_ECHO_BUF)
+ echo_tail=0;
+
+ z = (z1 * agc) >> 8;
+
+ // check for overflow, 16bit signed samples
+ if(z >= 32768)
+ {
+ ov = 8388608/z1 - 1; // 8388608 is 2^23, i.e. max value * 256
+ if(ov < agc) agc = ov; // set agc to number of 1/256ths to multiply the sample by
+ z = (z1 * agc) >> 8; // reduce sample by agc value to prevent overflow
+ }
+ else
+ if(z <= -32768)
+ {
+ ov = -8388608/z1 - 1;
+ if(ov < agc) agc = ov;
+ z = (z1 * agc) >> 8;
+ }
+ *out_ptr++ = z;
+ *out_ptr++ = z >> 8;
+
+ echo_buf[echo_head++] = z;
+ if(echo_head >= N_ECHO_BUF)
+ echo_head = 0;
+
+ if(out_ptr >= out_end)
+ return(1);
+ }
+ return(0);
+} // end of Wavegen
+
+
+static int PlaySilence(int length, int resume)
+{//===========================================
+ static int n_samples;
+ int value=0;
+
+ if(length == 0)
+ return(0);
+
+ nsamples = 0;
+ samplecount = 0;
+
+ if(resume==0)
+ n_samples = length;
+
+ while(n_samples-- > 0)
+ {
+ value = (echo_buf[echo_tail++] * echo_amp) >> 8;
+
+ if(echo_tail >= N_ECHO_BUF)
+ echo_tail = 0;
+
+ *out_ptr++ = value;
+ *out_ptr++ = value >> 8;
+
+ echo_buf[echo_head++] = value;
+ if(echo_head >= N_ECHO_BUF)
+ echo_head = 0;
+
+ if(out_ptr >= out_end)
+ return(1);
+ }
+ return(0);
+} // end of PlaySilence
+
+
+
+static int PlayWave(int length, int resume, unsigned char *data, int scale, int amp)
+{//=================================================================================
+ static int n_samples;
+ static int ix=0;
+ int value;
+ signed char c;
+
+ if(resume==0)
+ {
+ n_samples = length;
+ ix = 0;
+ }
+
+ nsamples = 0;
+ samplecount = 0;
+
+ while(n_samples-- > 0)
+ {
+ if(scale == 0)
+ {
+ // 16 bits data
+ c = data[ix+1];
+ value = data[ix] + (c * 256);
+ ix+=2;
+ }
+ else
+ {
+ // 8 bit data, shift by the specified scale factor
+ value = (signed char)data[ix++] * scale;
+ }
+ value *= (consonant_amp * general_amplitude); // reduce strength of consonant
+ value = value >> 10;
+ value = (value * amp)/32;
+
+ value += ((echo_buf[echo_tail++] * echo_amp) >> 8);
+
+ if(value > 32767)
+ value = 32768;
+ else
+ if(value < -32768)
+ value = -32768;
+
+ if(echo_tail >= N_ECHO_BUF)
+ echo_tail = 0;
+
+ out_ptr[0] = value;
+ out_ptr[1] = value >> 8;
+ out_ptr+=2;
+
+ echo_buf[echo_head++] = (value*3)/4;
+ if(echo_head >= N_ECHO_BUF)
+ echo_head = 0;
+
+ if(out_ptr >= out_end)
+ return(1);
+ }
+ return(0);
+}
+
+
+static int SetWithRange0(int value, int max)
+{//=========================================
+ if(value < 0)
+ return(0);
+ if(value > max)
+ return(max);
+ return(value);
+}
+
+
+void SetEmbedded(int control, int value)
+{//=====================================
+ // there was an embedded command in the text at this point
+ int sign=0;
+ int command;
+ int ix;
+ int factor;
+ int pitch_value;
+
+ command = control & 0x1f;
+ if((control & 0x60) == 0x60)
+ sign = -1;
+ else
+ if((control & 0x60) == 0x40)
+ sign = 1;
+
+ if(command < N_EMBEDDED_VALUES)
+ {
+ if(sign == 0)
+ embedded_value[command] = value;
+ else
+ embedded_value[command] += (value * sign);
+ embedded_value[command] = SetWithRange0(embedded_value[command],embedded_max[command]);
+ }
+
+ switch(command)
+ {
+ case EMBED_T:
+ WavegenSetEcho(); // and drop through to case P
+ case EMBED_P:
+ // adjust formants to give better results for a different voice pitch
+ if((pitch_value = embedded_value[EMBED_P]) > MAX_PITCH_VALUE)
+ pitch_value = MAX_PITCH_VALUE;
+
+ factor = 256 + (25 * (pitch_value - 50))/50;
+ for(ix=0; ix<=5; ix++)
+ {
+ wvoice->freq[ix] = (wvoice->freq2[ix] * factor)/256;
+ }
+ factor = embedded_value[EMBED_T]*3;
+ wvoice->height[0] = (wvoice->height2[0] * (256 - factor*2))/256;
+ wvoice->height[1] = (wvoice->height2[1] * (256 - factor))/256;
+ break;
+
+ case EMBED_A: // amplitude
+ general_amplitude = GetAmplitude();
+ break;
+
+ case EMBED_F: // emphasiis
+ general_amplitude = GetAmplitude();
+ break;
+
+ case EMBED_H:
+ WavegenSetEcho();
+ break;
+ }
+}
+
+
+void WavegenSetVoice(voice_t *v)
+{//=============================
+ static voice_t v2;
+
+ memcpy(&v2,v,sizeof(v2));
+ wvoice = &v2;
+
+ if(v->peak_shape==0)
+ pk_shape = pk_shape1;
+ else
+ pk_shape = pk_shape2;
+
+ consonant_amp = (v->consonant_amp * 26) /100;
+ if(samplerate <= 11000)
+ {
+ consonant_amp = consonant_amp*2; // emphasize consonants at low sample rates
+ option_harmonic1 = 6;
+ }
+ WavegenSetEcho();
+}
+
+
+static void SetAmplitude(int length, unsigned char *amp_env, int value)
+{//====================================================================
+ amp_ix = 0;
+ if(length==0)
+ amp_inc = 0;
+ else
+ amp_inc = (256 * ENV_LEN * STEPSIZE)/length;
+
+ wdata.amplitude = (value * general_amplitude)/16;
+ wdata.amplitude_v = (wdata.amplitude * wvoice->consonant_ampv * 15)/100; // for wave mixed with voiced sounds
+
+ amplitude_env = amp_env;
+}
+
+
+void SetPitch2(voice_t *voice, int pitch1, int pitch2, int *pitch_base, int *pitch_range)
+{//======================================================================================
+ int x;
+ int base;
+ int range;
+ int pitch_value;
+
+ if(pitch1 > pitch2)
+ {
+ x = pitch1; // swap values
+ pitch1 = pitch2;
+ pitch2 = x;
+ }
+
+ if((pitch_value = embedded_value[EMBED_P]) > MAX_PITCH_VALUE)
+ pitch_value = MAX_PITCH_VALUE;
+ pitch_value -= embedded_value[EMBED_T]; // adjust tone for announcing punctuation
+ if(pitch_value < 0)
+ pitch_value = 0;
+
+ base = (voice->pitch_base * pitch_adjust_tab[pitch_value])/128;
+ range = (voice->pitch_range * embedded_value[EMBED_R])/50;
+
+ // compensate for change in pitch when the range is narrowed or widened
+ base -= (range - voice->pitch_range)*18;
+
+ *pitch_base = base + (pitch1 * range);
+ *pitch_range = base + (pitch2 * range) - *pitch_base;
+}
+
+
+void SetPitch(int length, unsigned char *env, int pitch1, int pitch2)
+{//==================================================================
+// length in samples
+
+#ifdef LOG_FRAMES
+if(option_log_frames)
+{
+ f_log=fopen("log-espeakedit","a");
+ if(f_log != NULL)
+ {
+ fprintf(f_log," pitch %3d %3d %3dmS\n",pitch1,pitch2,(length*1000)/samplerate);
+ fclose(f_log);
+ f_log=NULL;
+ }
+}
+#endif
+ if((wdata.pitch_env = env)==NULL)
+ wdata.pitch_env = env_fall; // default
+
+ wdata.pitch_ix = 0;
+ if(length==0)
+ wdata.pitch_inc = 0;
+ else
+ wdata.pitch_inc = (256 * ENV_LEN * STEPSIZE)/length;
+
+ SetPitch2(wvoice, pitch1, pitch2, &wdata.pitch_base, &wdata.pitch_range);
+ // set initial pitch
+ wdata.pitch = ((wdata.pitch_env[0] * wdata.pitch_range) >>8) + wdata.pitch_base; // Hz << 12
+
+ flutter_amp = wvoice->flutter;
+
+} // end of SetPitch
+
+
+
+
+
+void SetSynth(int length, int modn, frame_t *fr1, frame_t *fr2, voice_t *v)
+{//========================================================================
+ int ix;
+ DOUBLEX next;
+ int length2;
+ int length4;
+ int qix;
+ int cmd;
+ static int glottal_reduce_tab1[4] = {0x30, 0x30, 0x40, 0x50}; // vowel before [?], amp * 1/256
+// static int glottal_reduce_tab1[4] = {0x30, 0x40, 0x50, 0x60}; // vowel before [?], amp * 1/256
+ static int glottal_reduce_tab2[4] = {0x90, 0xa0, 0xb0, 0xc0}; // vowel after [?], amp * 1/256
+
+#ifdef LOG_FRAMES
+if(option_log_frames)
+{
+ f_log=fopen("log-espeakedit","a");
+ if(f_log != NULL)
+ {
+ fprintf(f_log,"%3dmS %3d %3d %4d %4d (%3d %3d %3d %3d) to %3d %3d %4d %4d (%3d %3d %3d %3d)\n",length*1000/samplerate,
+ fr1->ffreq[0],fr1->ffreq[1],fr1->ffreq[2],fr1->ffreq[3], fr1->fheight[0],fr1->fheight[1],fr1->fheight[2],fr1->fheight[3],
+ fr2->ffreq[0],fr2->ffreq[1],fr2->ffreq[2],fr2->ffreq[3], fr2->fheight[0],fr2->fheight[1],fr2->fheight[2],fr2->fheight[3] );
+
+ fclose(f_log);
+ f_log=NULL;
+ }
+}
+#endif
+
+ harm_sqrt_n = 0;
+ end_wave = 1;
+
+ // any additional information in the param1 ?
+ modulation_type = modn & 0xff;
+
+ glottal_flag = 0;
+ if(modn & 0x400)
+ {
+ glottal_flag = 3; // before a glottal stop
+ glottal_reduce = glottal_reduce_tab1[(modn >> 8) & 3];
+ }
+ if(modn & 0x800)
+ {
+ glottal_flag = 4; // after a glottal stop
+ glottal_reduce = glottal_reduce_tab2[(modn >> 8) & 3];
+ }
+
+ for(qix=wcmdq_head+1;;qix++)
+ {
+ if(qix >= N_WCMDQ) qix = 0;
+ if(qix == wcmdq_tail) break;
+
+ cmd = wcmdq[qix][0];
+ if(cmd==WCMD_SPECT)
+ {
+ end_wave = 0; // next wave generation is from another spectrum
+ break;
+ }
+ if((cmd==WCMD_WAVE) || (cmd==WCMD_PAUSE))
+ break; // next is not from spectrum, so continue until end of wave cycle
+ }
+
+ // round the length to a multiple of the stepsize
+ length2 = (length + STEPSIZE/2) & ~0x3f;
+ if(length2 == 0)
+ length2 = STEPSIZE;
+
+ // add this length to any left over from the previous synth
+ samplecount_start = samplecount;
+ nsamples += length2;
+
+ length4 = length2/4;
+ for(ix=0; ix<N_PEAKS; ix++)
+ {
+ peaks[ix].freq1 = (fr1->ffreq[ix] * v->freq[ix] + v->freqadd[ix]*256) << 8;
+ peaks[ix].freq = int(peaks[ix].freq1);
+ next = (fr2->ffreq[ix] * v->freq[ix] + v->freqadd[ix]*256) << 8;
+ peaks[ix].freq_inc = ((next - peaks[ix].freq1) * (STEPSIZE/4)) / length4; // lower headroom for fixed point math
+
+ peaks[ix].height1 = (fr1->fheight[ix] * v->height[ix]) << 6;
+ peaks[ix].height = int(peaks[ix].height1);
+ next = (fr2->fheight[ix] * v->height[ix]) << 6;
+ peaks[ix].height_inc = ((next - peaks[ix].height1) * STEPSIZE) / length2;
+
+ if(ix <= wvoice->n_harmonic_peaks)
+ {
+ peaks[ix].left1 = (fr1->fwidth[ix] * v->width[ix]) << 10;
+ peaks[ix].left = int(peaks[ix].left1);
+ next = (fr2->fwidth[ix] * v->width[ix]) << 10;
+ peaks[ix].left_inc = ((next - peaks[ix].left1) * STEPSIZE) / length2;
+
+ peaks[ix].right1 = (fr1->fright[ix] * v->width[ix]) << 10;
+ peaks[ix].right = int(peaks[ix].right1);
+ next = (fr2->fright[ix] * v->width[ix]) << 10;
+ peaks[ix].right_inc = ((next - peaks[ix].right1) * STEPSIZE) / length2;
+ }
+ }
+} // end of SetSynth
+
+
+static int Wavegen2(int length, int modulation, int resume, frame_t *fr1, frame_t *fr2)
+{//====================================================================================
+ if(resume==0)
+ SetSynth(length, modulation, fr1, fr2, wvoice);
+
+ return(Wavegen());
+}
+
+void Write4Bytes(FILE *f, int value)
+{//=================================
+// Write 4 bytes to a file, least significant first
+ int ix;
+
+ for(ix=0; ix<4; ix++)
+ {
+ fputc(value & 0xff,f);
+ value = value >> 8;
+ }
+}
+
+
+
+
+int WavegenFill(int fill_zeros)
+{//============================
+// Pick up next wavegen commands from the queue
+// return: 0 output buffer has been filled
+// return: 1 input command queue is now empty
+
+ long *q;
+ int length;
+ int result;
+ static int resume=0;
+ static int echo_complete=0;
+
+#ifdef TEST_MBROLA
+ if(mbrola_name[0] != 0)
+ return(MbrolaFill(fill_zeros));
+#endif
+
+ while(out_ptr < out_end)
+ {
+ if(WcmdqUsed() <= 0)
+ {
+ if(echo_complete > 0)
+ {
+ // continue to play silence until echo is completed
+ resume = PlaySilence(echo_complete,resume);
+ if(resume == 1)
+ return(0); // not yet finished
+ }
+
+ if(fill_zeros)
+ {
+ while(out_ptr < out_end)
+ *out_ptr++ = 0;
+ }
+ return(1); // queue empty, close sound channel
+ }
+
+ result = 0;
+ q = wcmdq[wcmdq_head];
+ length = q[1];
+
+ switch(q[0])
+ {
+ case WCMD_PITCH:
+ SetPitch(length,(unsigned char *)q[2],q[3] >> 16,q[3] & 0xffff);
+ break;
+
+ case WCMD_PAUSE:
+ if(resume==0)
+ {
+ echo_complete -= length;
+ }
+ wdata.n_mix_wavefile = 0;
+ wdata.prev_was_synth = 0;
+ result = PlaySilence(length,resume);
+ break;
+
+ case WCMD_WAVE:
+ echo_complete = echo_length;
+ wdata.n_mix_wavefile = 0;
+ wdata.prev_was_synth = 0;
+ result = PlayWave(length,resume,(unsigned char*)q[2], q[3] & 0xff, q[3] >> 8);
+ break;
+
+ case WCMD_WAVE2:
+ // wave file to be played at the same time as synthesis
+ wdata.mix_wave_amp = q[3] >> 8;
+ wdata.mix_wave_scale = q[3] & 0xff;
+ if(wdata.mix_wave_scale == 0)
+ wdata.n_mix_wavefile = length*2;
+ else
+ wdata.n_mix_wavefile = length;
+ wdata.mix_wavefile_ix = 0;
+ wdata.mix_wavefile = (unsigned char *)q[2];
+ break;
+
+ case WCMD_SPECT2: // as WCMD_SPECT but stop any concurrent wave file
+ wdata.n_mix_wavefile = 0; // ... and drop through to WCMD_SPECT case
+ case WCMD_SPECT:
+ echo_complete = echo_length;
+ result = Wavegen2(length & 0xffff,q[1] >> 16,resume,(frame_t *)q[2],(frame_t *)q[3]);
+ break;
+
+#ifdef INCLUDE_KLATT
+ case WCMD_KLATT2: // as WCMD_SPECT but stop any concurrent wave file
+ wdata.n_mix_wavefile = 0; // ... and drop through to WCMD_SPECT case
+ case WCMD_KLATT:
+ echo_complete = echo_length;
+ result = Wavegen_Klatt2(length & 0xffff,q[1] >> 16,resume,(frame_t *)q[2],(frame_t *)q[3]);
+ break;
+#endif
+
+ case WCMD_MARKER:
+ MarkerEvent(q[1],q[2],q[3],out_ptr);
+#ifdef LOG_FRAMES
+ LogMarker(q[1],q[3]);
+#endif
+ if(q[1] == 1)
+ {
+ current_source_index = q[2] & 0xffffff;
+ }
+ break;
+
+ case WCMD_AMPLITUDE:
+ SetAmplitude(length,(unsigned char *)q[2],q[3]);
+ break;
+
+ case WCMD_VOICE:
+ WavegenSetVoice((voice_t *)q[1]);
+ free((voice_t *)q[1]);
+ break;
+
+ case WCMD_EMBEDDED:
+ SetEmbedded(q[1],q[2]);
+ break;
+ }
+
+ if(result==0)
+ {
+ WcmdqIncHead();
+ resume=0;
+ }
+ else
+ {
+ resume=1;
+ }
+ }
+
+ return(0);
+} // end of WavegenFill
+
+
diff --git a/Plugins/eSpeak/espeak-data/af_dict b/Plugins/eSpeak/espeak-data/af_dict
new file mode 100644
index 0000000..061ae17
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/af_dict
Binary files differ
diff --git a/Plugins/eSpeak/espeak-data/ca_dict b/Plugins/eSpeak/espeak-data/ca_dict
new file mode 100644
index 0000000..2803ca9
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/ca_dict
Binary files differ
diff --git a/Plugins/eSpeak/espeak-data/config b/Plugins/eSpeak/espeak-data/config
new file mode 100644
index 0000000..be1b624
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/config
@@ -0,0 +1,9 @@
+//pa_device 7
+
+// play a sound for punctuation, rather than speak its name
+//soundicon _( /usr/share/sounds/sound-icons/left-round-bracket
+//soundicon _) /usr/share/sounds/sound-icons/right-round-bracket
+//soundicon _[ /usr/share/sounds/sound-icons/left-square-bracket
+//soundicon _] /usr/share/sounds/sound-icons/right-square-bracket
+//soundicon _{ /usr/share/sounds/sound-icons/left-brace
+//soundicon _} /usr/share/sounds/sound-icons/right-brace
diff --git a/Plugins/eSpeak/espeak-data/cs_dict b/Plugins/eSpeak/espeak-data/cs_dict
new file mode 100644
index 0000000..54fc382
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/cs_dict
Binary files differ
diff --git a/Plugins/eSpeak/espeak-data/cy_dict b/Plugins/eSpeak/espeak-data/cy_dict
new file mode 100644
index 0000000..c6824d2
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/cy_dict
Binary files differ
diff --git a/Plugins/eSpeak/espeak-data/de_dict b/Plugins/eSpeak/espeak-data/de_dict
new file mode 100644
index 0000000..749e5ea
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/de_dict
Binary files differ
diff --git a/Plugins/eSpeak/espeak-data/el_dict b/Plugins/eSpeak/espeak-data/el_dict
new file mode 100644
index 0000000..e132c0a
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/el_dict
Binary files differ
diff --git a/Plugins/eSpeak/espeak-data/en_dict b/Plugins/eSpeak/espeak-data/en_dict
new file mode 100644
index 0000000..0cf3544
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/en_dict
Binary files differ
diff --git a/Plugins/eSpeak/espeak-data/eo_dict b/Plugins/eSpeak/espeak-data/eo_dict
new file mode 100644
index 0000000..660be3c
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/eo_dict
Binary files differ
diff --git a/Plugins/eSpeak/espeak-data/es_dict b/Plugins/eSpeak/espeak-data/es_dict
new file mode 100644
index 0000000..53dfe04
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/es_dict
Binary files differ
diff --git a/Plugins/eSpeak/espeak-data/fi_dict b/Plugins/eSpeak/espeak-data/fi_dict
new file mode 100644
index 0000000..449c625
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/fi_dict
Binary files differ
diff --git a/Plugins/eSpeak/espeak-data/fr_dict b/Plugins/eSpeak/espeak-data/fr_dict
new file mode 100644
index 0000000..ef4a320
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/fr_dict
Binary files differ
diff --git a/Plugins/eSpeak/espeak-data/grc_dict b/Plugins/eSpeak/espeak-data/grc_dict
new file mode 100644
index 0000000..e56a88a
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/grc_dict
Binary files differ
diff --git a/Plugins/eSpeak/espeak-data/hbs_dict b/Plugins/eSpeak/espeak-data/hbs_dict
new file mode 100644
index 0000000..f26a458
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/hbs_dict
Binary files differ
diff --git a/Plugins/eSpeak/espeak-data/hi_dict b/Plugins/eSpeak/espeak-data/hi_dict
new file mode 100644
index 0000000..6c15876
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/hi_dict
Binary files differ
diff --git a/Plugins/eSpeak/espeak-data/hu_dict b/Plugins/eSpeak/espeak-data/hu_dict
new file mode 100644
index 0000000..9baa276
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/hu_dict
Binary files differ
diff --git a/Plugins/eSpeak/espeak-data/hy_dict b/Plugins/eSpeak/espeak-data/hy_dict
new file mode 100644
index 0000000..eda8060
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/hy_dict
Binary files differ
diff --git a/Plugins/eSpeak/espeak-data/id_dict b/Plugins/eSpeak/espeak-data/id_dict
new file mode 100644
index 0000000..8fdd454
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/id_dict
Binary files differ
diff --git a/Plugins/eSpeak/espeak-data/is_dict b/Plugins/eSpeak/espeak-data/is_dict
new file mode 100644
index 0000000..c6b30f6
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/is_dict
Binary files differ
diff --git a/Plugins/eSpeak/espeak-data/it_dict b/Plugins/eSpeak/espeak-data/it_dict
new file mode 100644
index 0000000..3da73b7
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/it_dict
Binary files differ
diff --git a/Plugins/eSpeak/espeak-data/jbo_dict b/Plugins/eSpeak/espeak-data/jbo_dict
new file mode 100644
index 0000000..a40053b
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/jbo_dict
Binary files differ
diff --git a/Plugins/eSpeak/espeak-data/ku_dict b/Plugins/eSpeak/espeak-data/ku_dict
new file mode 100644
index 0000000..e85acef
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/ku_dict
Binary files differ
diff --git a/Plugins/eSpeak/espeak-data/la_dict b/Plugins/eSpeak/espeak-data/la_dict
new file mode 100644
index 0000000..3ffff7c
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/la_dict
Binary files differ
diff --git a/Plugins/eSpeak/espeak-data/lv_dict b/Plugins/eSpeak/espeak-data/lv_dict
new file mode 100644
index 0000000..308f533
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/lv_dict
Binary files differ
diff --git a/Plugins/eSpeak/espeak-data/mbrola_ph/af1_phtrans b/Plugins/eSpeak/espeak-data/mbrola_ph/af1_phtrans
new file mode 100644
index 0000000..fc9ad01
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/mbrola_ph/af1_phtrans
Binary files differ
diff --git a/Plugins/eSpeak/espeak-data/mbrola_ph/ca1_phtrans b/Plugins/eSpeak/espeak-data/mbrola_ph/ca1_phtrans
new file mode 100644
index 0000000..4fe4188
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/mbrola_ph/ca1_phtrans
Binary files differ
diff --git a/Plugins/eSpeak/espeak-data/mbrola_ph/cr1_phtrans b/Plugins/eSpeak/espeak-data/mbrola_ph/cr1_phtrans
new file mode 100644
index 0000000..3bc162c
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/mbrola_ph/cr1_phtrans
Binary files differ
diff --git a/Plugins/eSpeak/espeak-data/mbrola_ph/cs_phtrans b/Plugins/eSpeak/espeak-data/mbrola_ph/cs_phtrans
new file mode 100644
index 0000000..85ebb03
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/mbrola_ph/cs_phtrans
Binary files differ
diff --git a/Plugins/eSpeak/espeak-data/mbrola_ph/de2_phtrans b/Plugins/eSpeak/espeak-data/mbrola_ph/de2_phtrans
new file mode 100644
index 0000000..c5af1a7
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/mbrola_ph/de2_phtrans
Binary files differ
diff --git a/Plugins/eSpeak/espeak-data/mbrola_ph/de4_phtrans b/Plugins/eSpeak/espeak-data/mbrola_ph/de4_phtrans
new file mode 100644
index 0000000..b10fc84
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/mbrola_ph/de4_phtrans
Binary files differ
diff --git a/Plugins/eSpeak/espeak-data/mbrola_ph/de6_phtrans b/Plugins/eSpeak/espeak-data/mbrola_ph/de6_phtrans
new file mode 100644
index 0000000..4cb62d9
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/mbrola_ph/de6_phtrans
Binary files differ
diff --git a/Plugins/eSpeak/espeak-data/mbrola_ph/en1_phtrans b/Plugins/eSpeak/espeak-data/mbrola_ph/en1_phtrans
new file mode 100644
index 0000000..03cdf4e
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/mbrola_ph/en1_phtrans
Binary files differ
diff --git a/Plugins/eSpeak/espeak-data/mbrola_ph/es_phtrans b/Plugins/eSpeak/espeak-data/mbrola_ph/es_phtrans
new file mode 100644
index 0000000..109bfe7
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/mbrola_ph/es_phtrans
Binary files differ
diff --git a/Plugins/eSpeak/espeak-data/mbrola_ph/fr1_phtrans b/Plugins/eSpeak/espeak-data/mbrola_ph/fr1_phtrans
new file mode 100644
index 0000000..e868378
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/mbrola_ph/fr1_phtrans
Binary files differ
diff --git a/Plugins/eSpeak/espeak-data/mbrola_ph/gr2_phtrans b/Plugins/eSpeak/espeak-data/mbrola_ph/gr2_phtrans
new file mode 100644
index 0000000..b3775ab
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/mbrola_ph/gr2_phtrans
Binary files differ
diff --git a/Plugins/eSpeak/espeak-data/mbrola_ph/grc-de6_phtrans b/Plugins/eSpeak/espeak-data/mbrola_ph/grc-de6_phtrans
new file mode 100644
index 0000000..e41d310
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/mbrola_ph/grc-de6_phtrans
Binary files differ
diff --git a/Plugins/eSpeak/espeak-data/mbrola_ph/hu1_phtrans b/Plugins/eSpeak/espeak-data/mbrola_ph/hu1_phtrans
new file mode 100644
index 0000000..56dba58
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/mbrola_ph/hu1_phtrans
Binary files differ
diff --git a/Plugins/eSpeak/espeak-data/mbrola_ph/id1_phtrans b/Plugins/eSpeak/espeak-data/mbrola_ph/id1_phtrans
new file mode 100644
index 0000000..fd3cc44
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/mbrola_ph/id1_phtrans
Binary files differ
diff --git a/Plugins/eSpeak/espeak-data/mbrola_ph/in1_phtrans b/Plugins/eSpeak/espeak-data/mbrola_ph/in1_phtrans
new file mode 100644
index 0000000..5ec7786
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/mbrola_ph/in1_phtrans
Binary files differ
diff --git a/Plugins/eSpeak/espeak-data/mbrola_ph/it3_phtrans b/Plugins/eSpeak/espeak-data/mbrola_ph/it3_phtrans
new file mode 100644
index 0000000..6d82647
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/mbrola_ph/it3_phtrans
Binary files differ
diff --git a/Plugins/eSpeak/espeak-data/mbrola_ph/la1_phtrans b/Plugins/eSpeak/espeak-data/mbrola_ph/la1_phtrans
new file mode 100644
index 0000000..1f2eb92
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/mbrola_ph/la1_phtrans
Binary files differ
diff --git a/Plugins/eSpeak/espeak-data/mbrola_ph/nl_phtrans b/Plugins/eSpeak/espeak-data/mbrola_ph/nl_phtrans
new file mode 100644
index 0000000..d982c18
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/mbrola_ph/nl_phtrans
Binary files differ
diff --git a/Plugins/eSpeak/espeak-data/mbrola_ph/pl1_phtrans b/Plugins/eSpeak/espeak-data/mbrola_ph/pl1_phtrans
new file mode 100644
index 0000000..9d4e50f
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/mbrola_ph/pl1_phtrans
Binary files differ
diff --git a/Plugins/eSpeak/espeak-data/mbrola_ph/pt_phtrans b/Plugins/eSpeak/espeak-data/mbrola_ph/pt_phtrans
new file mode 100644
index 0000000..9de1630
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/mbrola_ph/pt_phtrans
Binary files differ
diff --git a/Plugins/eSpeak/espeak-data/mbrola_ph/ptbr4_phtrans b/Plugins/eSpeak/espeak-data/mbrola_ph/ptbr4_phtrans
new file mode 100644
index 0000000..0b94de7
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/mbrola_ph/ptbr4_phtrans
Binary files differ
diff --git a/Plugins/eSpeak/espeak-data/mbrola_ph/ptbr_phtrans b/Plugins/eSpeak/espeak-data/mbrola_ph/ptbr_phtrans
new file mode 100644
index 0000000..a1dbba0
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/mbrola_ph/ptbr_phtrans
Binary files differ
diff --git a/Plugins/eSpeak/espeak-data/mbrola_ph/ro1_phtrans b/Plugins/eSpeak/espeak-data/mbrola_ph/ro1_phtrans
new file mode 100644
index 0000000..4aeaf54
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/mbrola_ph/ro1_phtrans
Binary files differ
diff --git a/Plugins/eSpeak/espeak-data/mbrola_ph/sv2_phtrans b/Plugins/eSpeak/espeak-data/mbrola_ph/sv2_phtrans
new file mode 100644
index 0000000..ae119d8
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/mbrola_ph/sv2_phtrans
Binary files differ
diff --git a/Plugins/eSpeak/espeak-data/mbrola_ph/sv_phtrans b/Plugins/eSpeak/espeak-data/mbrola_ph/sv_phtrans
new file mode 100644
index 0000000..bb556eb
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/mbrola_ph/sv_phtrans
Binary files differ
diff --git a/Plugins/eSpeak/espeak-data/mbrola_ph/us3_phtrans b/Plugins/eSpeak/espeak-data/mbrola_ph/us3_phtrans
new file mode 100644
index 0000000..a1b4977
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/mbrola_ph/us3_phtrans
Binary files differ
diff --git a/Plugins/eSpeak/espeak-data/mbrola_ph/us_phtrans b/Plugins/eSpeak/espeak-data/mbrola_ph/us_phtrans
new file mode 100644
index 0000000..4822cb6
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/mbrola_ph/us_phtrans
Binary files differ
diff --git a/Plugins/eSpeak/espeak-data/mk_dict b/Plugins/eSpeak/espeak-data/mk_dict
new file mode 100644
index 0000000..25afdd0
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/mk_dict
Binary files differ
diff --git a/Plugins/eSpeak/espeak-data/nl_dict b/Plugins/eSpeak/espeak-data/nl_dict
new file mode 100644
index 0000000..04caac5
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/nl_dict
Binary files differ
diff --git a/Plugins/eSpeak/espeak-data/no_dict b/Plugins/eSpeak/espeak-data/no_dict
new file mode 100644
index 0000000..91253a3
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/no_dict
Binary files differ
diff --git a/Plugins/eSpeak/espeak-data/phondata b/Plugins/eSpeak/espeak-data/phondata
new file mode 100644
index 0000000..dcbe90a
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/phondata
Binary files differ
diff --git a/Plugins/eSpeak/espeak-data/phonindex b/Plugins/eSpeak/espeak-data/phonindex
new file mode 100644
index 0000000..8aaf092
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/phonindex
Binary files differ
diff --git a/Plugins/eSpeak/espeak-data/phontab b/Plugins/eSpeak/espeak-data/phontab
new file mode 100644
index 0000000..d3f640e
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/phontab
Binary files differ
diff --git a/Plugins/eSpeak/espeak-data/pl_dict b/Plugins/eSpeak/espeak-data/pl_dict
new file mode 100644
index 0000000..f830e4c
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/pl_dict
Binary files differ
diff --git a/Plugins/eSpeak/espeak-data/pt_dict b/Plugins/eSpeak/espeak-data/pt_dict
new file mode 100644
index 0000000..bd51c5f
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/pt_dict
Binary files differ
diff --git a/Plugins/eSpeak/espeak-data/ro_dict b/Plugins/eSpeak/espeak-data/ro_dict
new file mode 100644
index 0000000..86c81d9
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/ro_dict
Binary files differ
diff --git a/Plugins/eSpeak/espeak-data/ru_dict b/Plugins/eSpeak/espeak-data/ru_dict
new file mode 100644
index 0000000..dbbaf7d
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/ru_dict
Binary files differ
diff --git a/Plugins/eSpeak/espeak-data/sk_dict b/Plugins/eSpeak/espeak-data/sk_dict
new file mode 100644
index 0000000..3974516
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/sk_dict
Binary files differ
diff --git a/Plugins/eSpeak/espeak-data/sq_dict b/Plugins/eSpeak/espeak-data/sq_dict
new file mode 100644
index 0000000..b947b25
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/sq_dict
Binary files differ
diff --git a/Plugins/eSpeak/espeak-data/sv_dict b/Plugins/eSpeak/espeak-data/sv_dict
new file mode 100644
index 0000000..e78000d
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/sv_dict
Binary files differ
diff --git a/Plugins/eSpeak/espeak-data/sw_dict b/Plugins/eSpeak/espeak-data/sw_dict
new file mode 100644
index 0000000..330fb42
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/sw_dict
Binary files differ
diff --git a/Plugins/eSpeak/espeak-data/ta_dict b/Plugins/eSpeak/espeak-data/ta_dict
new file mode 100644
index 0000000..3d7e375
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/ta_dict
Binary files differ
diff --git a/Plugins/eSpeak/espeak-data/tr_dict b/Plugins/eSpeak/espeak-data/tr_dict
new file mode 100644
index 0000000..8376542
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/tr_dict
Binary files differ
diff --git a/Plugins/eSpeak/espeak-data/vi_dict b/Plugins/eSpeak/espeak-data/vi_dict
new file mode 100644
index 0000000..dd1f2a9
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/vi_dict
Binary files differ
diff --git a/Plugins/eSpeak/espeak-data/voices/!v/croak b/Plugins/eSpeak/espeak-data/voices/!v/croak
new file mode 100644
index 0000000..ae76a4c
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/voices/!v/croak
@@ -0,0 +1,11 @@
+language variant
+name croak
+gender male 70
+
+pitch 85 117
+flutter 20
+
+formant 0 100 80 110
+
+
+
diff --git a/Plugins/eSpeak/espeak-data/voices/!v/f1 b/Plugins/eSpeak/espeak-data/voices/!v/f1
new file mode 100644
index 0000000..13664a3
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/voices/!v/f1
@@ -0,0 +1,18 @@
+language variant
+name female1
+gender female
+
+pitch 145 200
+flutter 7
+roughness 4
+formant 0 115 80 150
+formant 1 120 80 180
+formant 2 100 70 150 150
+formant 3 115 70 150
+formant 4 110 80 150
+formant 5 110 90 150
+formant 6 105 80 150
+formant 7 110 70 150
+formant 8 110 70 150
+
+stressAdd -10 -10 -20 -20 0 0 40 70
diff --git a/Plugins/eSpeak/espeak-data/voices/!v/f2 b/Plugins/eSpeak/espeak-data/voices/!v/f2
new file mode 100644
index 0000000..e929467
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/voices/!v/f2
@@ -0,0 +1,20 @@
+language variant
+name female2
+gender female
+
+pitch 142 220
+roughness 3
+
+formant 0 105 80 150
+formant 1 110 80 160
+formant 2 110 70 150
+formant 3 110 70 150
+formant 4 115 80 150
+formant 5 115 80 150
+formant 6 110 70 150
+formant 7 110 70 150
+formant 8 110 70 150
+
+stressAdd 0 0 -10 -10 0 0 10 40
+breath 0 2 3 3 3 3 3 2
+echo 140 10
diff --git a/Plugins/eSpeak/espeak-data/voices/!v/f3 b/Plugins/eSpeak/espeak-data/voices/!v/f3
new file mode 100644
index 0000000..92a1582
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/voices/!v/f3
@@ -0,0 +1,22 @@
+language variant
+name female3
+gender female
+
+pitch 140 240
+formant 0 105 80 150
+formant 1 120 75 150 -50
+formant 2 135 70 150 -250
+formant 3 125 80 150
+formant 4 125 80 150
+formant 5 125 80 150
+formant 6 120 70 150
+formant 7 110 70 150
+formant 8 110 70 150
+
+stressAmp 18 18 20 20 20 20 20 20
+//breath 0 2 4 4 4 4 4 4
+breath 0 2 3 3 3 3 3 2
+echo 120 10
+roughness 4
+
+
diff --git a/Plugins/eSpeak/espeak-data/voices/!v/f4 b/Plugins/eSpeak/espeak-data/voices/!v/f4
new file mode 100644
index 0000000..52c5ac9
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/voices/!v/f4
@@ -0,0 +1,18 @@
+language variant
+name female4
+gender female
+
+echo 130 15
+pitch 142 200
+formant 0 120 80 150
+formant 1 115 80 160 -20
+formant 2 130 75 150 -200
+formant 3 123 75 150
+formant 4 125 80 150
+formant 5 125 80 150
+formant 6 110 80 150
+formant 7 110 75 150
+formant 8 110 75 150
+
+stressAdd -20 -20 -20 -20 0 0 20 120
+stressAmp 18 16 20 20 20 20 20 20
diff --git a/Plugins/eSpeak/espeak-data/voices/!v/m1 b/Plugins/eSpeak/espeak-data/voices/!v/m1
new file mode 100644
index 0000000..57603a8
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/voices/!v/m1
@@ -0,0 +1,19 @@
+language variant
+name male1
+gender male 70
+
+pitch 74 109
+flutter 4
+roughness 4
+
+formant 0 98 95 100
+formant 1 97 95 100
+formant 2 97 95 100
+formant 3 97 100 100
+formant 4 97 100 100
+formant 5 105 100 100
+formant 6 95 100 100
+formant 7 100 100 100
+formant 8 100 100 100
+
+stressAdd -10 -10 -20 -20 0 0 40 70
diff --git a/Plugins/eSpeak/espeak-data/voices/!v/m2 b/Plugins/eSpeak/espeak-data/voices/!v/m2
new file mode 100644
index 0000000..c234f46
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/voices/!v/m2
@@ -0,0 +1,15 @@
+language variant
+name male2
+gender male
+
+pitch 88 115
+echo 130 15
+formant 0 100 80 120
+formant 1 90 85 120
+formant 2 110 85 120
+formant 3 105 90 120
+formant 4 100 90 120
+formant 5 100 90 120
+formant 6 100 90 120
+formant 7 100 90 120
+formant 8 100 90 120
diff --git a/Plugins/eSpeak/espeak-data/voices/!v/m3 b/Plugins/eSpeak/espeak-data/voices/!v/m3
new file mode 100644
index 0000000..581cd88
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/voices/!v/m3
@@ -0,0 +1,16 @@
+language variant
+name male3
+gender male
+
+pitch 80 122
+formant 0 100 100 100
+formant 1 96 97 100
+formant 2 96 97 100
+formant 3 96 103 100
+formant 4 95 103 100
+formant 5 95 103 100
+formant 6 100 100 100
+formant 7 100 100 100
+formant 8 100 100 100
+
+stressAdd 10 10 0 0 0 0 -30 -30
diff --git a/Plugins/eSpeak/espeak-data/voices/!v/m4 b/Plugins/eSpeak/espeak-data/voices/!v/m4
new file mode 100644
index 0000000..7199341
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/voices/!v/m4
@@ -0,0 +1,17 @@
+language variant
+name male4
+gender male
+
+pitch 70 110
+
+formant 0 103 100 100
+formant 1 103 100 100
+formant 2 103 100 100
+formant 3 103 100 100
+formant 4 106 100 100
+formant 5 106 100 100
+formant 6 106 100 100
+formant 7 103 100 100
+formant 8 103 100 100
+
+stressAdd -10 -10 -30 -30 0 0 60 90
diff --git a/Plugins/eSpeak/espeak-data/voices/!v/m5 b/Plugins/eSpeak/espeak-data/voices/!v/m5
new file mode 100644
index 0000000..d258656
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/voices/!v/m5
@@ -0,0 +1,15 @@
+language variant
+name male5
+gender male
+
+formant 0 100 85 130
+formant 1 90 85 130 40
+formant 2 80 85 130 310
+formant 3 105 85 130
+formant 4 105 85 130
+formant 5 105 85 130
+formant 6 105 85 150
+formant 7 105 85 150
+formant 8 105 85 150
+
+intonation 2
diff --git a/Plugins/eSpeak/espeak-data/voices/!v/m6 b/Plugins/eSpeak/espeak-data/voices/!v/m6
new file mode 100644
index 0000000..bd336a9
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/voices/!v/m6
@@ -0,0 +1,13 @@
+language variant
+name male6
+gender male
+
+pitch 82 117
+
+formant 0 100 90 120
+formant 1 100 90 140
+formant 2 100 70 140
+formant 3 100 75 140
+formant 4 100 80 140
+formant 5 100 80 140
+
diff --git a/Plugins/eSpeak/espeak-data/voices/!v/wisper b/Plugins/eSpeak/espeak-data/voices/!v/wisper
new file mode 100644
index 0000000..b2f8497
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/voices/!v/wisper
@@ -0,0 +1,13 @@
+language variant
+name wisper
+gender male
+
+pitch 82 117
+flutter 20
+
+formant 0 100 0 100
+formant 1 100 80 100
+
+voicing 17
+breath 75 75 50 40 15 10
+breathw 150 150 200 200 400 400
diff --git a/Plugins/eSpeak/espeak-data/voices/af b/Plugins/eSpeak/espeak-data/voices/af
new file mode 100644
index 0000000..bcbb2a0
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/voices/af
@@ -0,0 +1,8 @@
+name afrikaans
+language af
+gender male
+roughness 0
+pitch 63 120
+
+
+
diff --git a/Plugins/eSpeak/espeak-data/voices/bs b/Plugins/eSpeak/espeak-data/voices/bs
new file mode 100644
index 0000000..eadd707
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/voices/bs
@@ -0,0 +1,16 @@
+name bosnian
+language bs
+phonemes hr
+dictionary hbs
+gender male
+
+pitch 81 120
+formant 0 100 100 100
+formant 1 97 97 100
+formant 2 97 97 100
+formant 3 97 102 100
+formant 4 97 102 100
+formant 5 97 102 100
+
+stressAdd 10 10 0 0 0 0 -30 -30
+dictrules 3 4
diff --git a/Plugins/eSpeak/espeak-data/voices/ca b/Plugins/eSpeak/espeak-data/voices/ca
new file mode 100644
index 0000000..dc51396
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/voices/ca
@@ -0,0 +1,4 @@
+name catalan
+language ca
+gender male
+
diff --git a/Plugins/eSpeak/espeak-data/voices/cs b/Plugins/eSpeak/espeak-data/voices/cs
new file mode 100644
index 0000000..1c2992d
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/voices/cs
@@ -0,0 +1,4 @@
+name czech
+language cs
+gender male
+
diff --git a/Plugins/eSpeak/espeak-data/voices/cy b/Plugins/eSpeak/espeak-data/voices/cy
new file mode 100644
index 0000000..2991e99
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/voices/cy
@@ -0,0 +1,5 @@
+language cy
+name welsh-test
+gender male
+
+intonation 4
diff --git a/Plugins/eSpeak/espeak-data/voices/de b/Plugins/eSpeak/espeak-data/voices/de
new file mode 100644
index 0000000..653c3f5
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/voices/de
@@ -0,0 +1,5 @@
+name german
+language de
+gender male
+
+
diff --git a/Plugins/eSpeak/espeak-data/voices/default b/Plugins/eSpeak/espeak-data/voices/default
new file mode 100644
index 0000000..7accc8b
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/voices/default
@@ -0,0 +1,6 @@
+name default
+language en
+gender male
+
+formant 0 100 100 110
+
diff --git a/Plugins/eSpeak/espeak-data/voices/el b/Plugins/eSpeak/espeak-data/voices/el
new file mode 100644
index 0000000..1e9a757
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/voices/el
@@ -0,0 +1,5 @@
+name greek
+language el
+gender male
+
+
diff --git a/Plugins/eSpeak/espeak-data/voices/en/en b/Plugins/eSpeak/espeak-data/voices/en/en
new file mode 100644
index 0000000..dc6a60c
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/voices/en/en
@@ -0,0 +1,11 @@
+name english
+language en-uk 2
+language en 2
+gender male
+
+//pitch 80 117
+
+replace 03 I i
+replace 03 I2 i
+
+formant 0 100 100 105
diff --git a/Plugins/eSpeak/espeak-data/voices/en/en-n b/Plugins/eSpeak/espeak-data/voices/en/en-n
new file mode 100644
index 0000000..933311d
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/voices/en/en-n
@@ -0,0 +1,14 @@
+name lancashire
+language en-uk-north
+language en-uk 3
+gender male
+
+phonemes en_n
+
+stressLength 160 150 180 180 220 220 290 290
+
+replace 00 i@3 i@
+replace 03 N n
+//replace 03 I i
+//replace 03 I2 i
+
diff --git a/Plugins/eSpeak/espeak-data/voices/en/en-rp b/Plugins/eSpeak/espeak-data/voices/en/en-rp
new file mode 100644
index 0000000..3489f28
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/voices/en/en-rp
@@ -0,0 +1,12 @@
+name english_rp
+language en-uk-rp
+language en-uk 4
+gender male
+
+phonemes en_rp
+replace 00 o@ O@
+replace 00 i@3 i@
+replace 03 I i
+replace 03 I2 i
+replace 03 @ a2
+replace 03 3 a2
diff --git a/Plugins/eSpeak/espeak-data/voices/en/en-sc b/Plugins/eSpeak/espeak-data/voices/en/en-sc
new file mode 100644
index 0000000..e16ae25
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/voices/en/en-sc
@@ -0,0 +1,16 @@
+name en-scottish
+language en-sc
+language en 4
+gender male
+
+phonemes en_sc
+dictrules 5 6 7
+stressLength 180 130 200 200 0 0 250 270
+
+replace 03 @ V
+replace 03 I i
+replace 03 I2 i
+replace 01 aI aI2
+replace 02 a a/
+replace 02 u: U
+replace 02 3: VR
diff --git a/Plugins/eSpeak/espeak-data/voices/en/en-wi b/Plugins/eSpeak/espeak-data/voices/en/en-wi
new file mode 100644
index 0000000..28a42a5
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/voices/en/en-wi
@@ -0,0 +1,19 @@
+name en-westindies
+language en-wi
+language en-uk 4
+gender male
+
+phonemes en_wi
+dictrules 8
+stressLength 175 175 175 175 220 220 250 290
+
+replace 00 D d
+replace 00 T t[
+replace 00 U@ o@
+replace 00 i@3 i@
+replace 03 @ a2
+replace 03 3 a2
+replace 03 N n
+
+formant 1 98 100 100
+formant 2 98 100 100
diff --git a/Plugins/eSpeak/espeak-data/voices/en/en-wm b/Plugins/eSpeak/espeak-data/voices/en/en-wm
new file mode 100644
index 0000000..aa82f88
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/voices/en/en-wm
@@ -0,0 +1,12 @@
+name english_wmids
+language en-uk-wmids
+gender male
+
+phonemes en_wm
+
+replace 00 h NULL
+replace 00 o@ O@
+replace 00 i@3 i@
+dictrules 6
+intonation 4
+stressAdd 0 0 0 0 0 0 0 20
diff --git a/Plugins/eSpeak/espeak-data/voices/eo b/Plugins/eSpeak/espeak-data/voices/eo
new file mode 100644
index 0000000..742b45a
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/voices/eo
@@ -0,0 +1,4 @@
+name esperanto
+language eo
+gender male
+
diff --git a/Plugins/eSpeak/espeak-data/voices/es b/Plugins/eSpeak/espeak-data/voices/es
new file mode 100644
index 0000000..1a9e53b
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/voices/es
@@ -0,0 +1,7 @@
+name spanish
+language es
+gender male
+
+dictrules 1
+intonation 3
+
diff --git a/Plugins/eSpeak/espeak-data/voices/es-la b/Plugins/eSpeak/espeak-data/voices/es-la
new file mode 100644
index 0000000..c326c46
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/voices/es-la
@@ -0,0 +1,11 @@
+name spanish-latin-american
+language es-la
+language es-mx 6
+gender male
+
+phonemes es_la
+dictrules 2
+intonation 2
+stressLength 170 200 180 180 0 0 250 280
+
+replace 00 T s
diff --git a/Plugins/eSpeak/espeak-data/voices/fi b/Plugins/eSpeak/espeak-data/voices/fi
new file mode 100644
index 0000000..6e11c93
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/voices/fi
@@ -0,0 +1,4 @@
+name finnish
+language fi
+gender male
+
diff --git a/Plugins/eSpeak/espeak-data/voices/fr b/Plugins/eSpeak/espeak-data/voices/fr
new file mode 100644
index 0000000..9730731
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/voices/fr
@@ -0,0 +1,7 @@
+language fr
+name french
+gender male
+
+dictrules 1
+intonation 3
+
diff --git a/Plugins/eSpeak/espeak-data/voices/fr-be b/Plugins/eSpeak/espeak-data/voices/fr-be
new file mode 100644
index 0000000..cba9b27
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/voices/fr-be
@@ -0,0 +1,7 @@
+language fr-be
+name french (Belgium)
+gender male
+
+dictrules 2
+intonation 3
+
diff --git a/Plugins/eSpeak/espeak-data/voices/hi b/Plugins/eSpeak/espeak-data/voices/hi
new file mode 100644
index 0000000..de4786c
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/voices/hi
@@ -0,0 +1,9 @@
+name hindi-test
+language hi
+gender male
+
+translator hi
+phonemes hi
+dictionary hi
+
+dictrules 1
diff --git a/Plugins/eSpeak/espeak-data/voices/hr b/Plugins/eSpeak/espeak-data/voices/hr
new file mode 100644
index 0000000..d6811d3
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/voices/hr
@@ -0,0 +1,18 @@
+name croatian
+language hr
+language hbs
+gender male
+
+dictionary hbs
+
+// attributes towards !variant3
+pitch 81 120
+formant 0 100 100 100
+formant 1 97 97 100
+formant 2 97 97 100
+formant 3 97 102 100
+formant 4 97 102 100
+formant 5 97 102 100
+
+stressAdd 10 10 0 0 0 0 -30 -30
+dictrules 1
diff --git a/Plugins/eSpeak/espeak-data/voices/hu b/Plugins/eSpeak/espeak-data/voices/hu
new file mode 100644
index 0000000..ba2bdde
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/voices/hu
@@ -0,0 +1,3 @@
+name hungarian
+language hu
+gender male
diff --git a/Plugins/eSpeak/espeak-data/voices/id b/Plugins/eSpeak/espeak-data/voices/id
new file mode 100644
index 0000000..ce800f7
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/voices/id
@@ -0,0 +1,8 @@
+name indonesian-test
+language id
+gender male
+
+stressLength 160 200 180 180 0 0 220 240
+stressAmp 16 18 18 18 0 0 22 21
+
+consonants 80 80
diff --git a/Plugins/eSpeak/espeak-data/voices/is b/Plugins/eSpeak/espeak-data/voices/is
new file mode 100644
index 0000000..9e9c4e7
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/voices/is
@@ -0,0 +1,4 @@
+name icelandic-test
+language is
+gender male
+
diff --git a/Plugins/eSpeak/espeak-data/voices/it b/Plugins/eSpeak/espeak-data/voices/it
new file mode 100644
index 0000000..53c2a70
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/voices/it
@@ -0,0 +1,6 @@
+name italian
+language it
+gender male
+
+replace 03 i I
+
diff --git a/Plugins/eSpeak/espeak-data/voices/ku b/Plugins/eSpeak/espeak-data/voices/ku
new file mode 100644
index 0000000..536957c
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/voices/ku
@@ -0,0 +1,6 @@
+name kurdish
+language ku
+gender male
+
+//words 1 48
+
diff --git a/Plugins/eSpeak/espeak-data/voices/la b/Plugins/eSpeak/espeak-data/voices/la
new file mode 100644
index 0000000..f3e97b5
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/voices/la
@@ -0,0 +1,13 @@
+name latin
+language la
+gender male
+stressrule 2 33 0 2
+// rule=penultimate
+// flags=0100001 (no automatic secondary stress + don't stres monosyllables)
+// unstressed_wd1=0
+// unstressed_wd2=2
+
+// short gap between words
+words 2
+
+// Note: The Latin voice needs long vowels to be marked with macrons
diff --git a/Plugins/eSpeak/espeak-data/voices/lv b/Plugins/eSpeak/espeak-data/voices/lv
new file mode 100644
index 0000000..0278ea2
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/voices/lv
@@ -0,0 +1,6 @@
+name latvian
+language lv
+gender male
+
+replace 03 o o:
+
diff --git a/Plugins/eSpeak/espeak-data/voices/mb/mb-af1 b/Plugins/eSpeak/espeak-data/voices/mb/mb-af1
new file mode 100644
index 0000000..03dac4f
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/voices/mb/mb-af1
@@ -0,0 +1,7 @@
+name afrikaans-mbrola-1
+language af 7
+gender male
+
+pitch 82 117
+mbrola af1 af1_phtrans
+
diff --git a/Plugins/eSpeak/espeak-data/voices/mb/mb-af1-en b/Plugins/eSpeak/espeak-data/voices/mb/mb-af1-en
new file mode 100644
index 0000000..71ecab7
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/voices/mb/mb-af1-en
@@ -0,0 +1,7 @@
+name en-afrikaans
+language en 11
+gender male
+
+pitch 82 117
+mbrola af1 af1_phtrans
+
diff --git a/Plugins/eSpeak/espeak-data/voices/mb/mb-br1 b/Plugins/eSpeak/espeak-data/voices/mb/mb-br1
new file mode 100644
index 0000000..ba7c42c
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/voices/mb/mb-br1
@@ -0,0 +1,9 @@
+language pt 7
+name brazil-mbrola-1
+gender male
+pitch 82 117
+
+dictrules 2 3 4
+
+mbrola br1 ptbr_phtrans
+
diff --git a/Plugins/eSpeak/espeak-data/voices/mb/mb-br3 b/Plugins/eSpeak/espeak-data/voices/mb/mb-br3
new file mode 100644
index 0000000..8479e65
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/voices/mb/mb-br3
@@ -0,0 +1,9 @@
+language pt 7
+name brazil-mbrola-3
+gender male
+pitch 82 117
+
+dictrules 2 3 4
+
+mbrola br3 ptbr_phtrans
+
diff --git a/Plugins/eSpeak/espeak-data/voices/mb/mb-br4 b/Plugins/eSpeak/espeak-data/voices/mb/mb-br4
new file mode 100644
index 0000000..d3d7720
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/voices/mb/mb-br4
@@ -0,0 +1,9 @@
+language pt 7
+name brazil-mbrola-4
+gender female
+pitch 140 220
+
+dictrules 2 3 4
+
+mbrola br4 ptbr4_phtrans
+
diff --git a/Plugins/eSpeak/espeak-data/voices/mb/mb-cr1 b/Plugins/eSpeak/espeak-data/voices/mb/mb-cr1
new file mode 100644
index 0000000..9b280bf
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/voices/mb/mb-cr1
@@ -0,0 +1,9 @@
+name croatian-mbrola-1
+language hr 7
+gender male
+
+dictionary hbs
+dictrules 1
+
+pitch 82 117
+mbrola cr1 cr1_phtrans
diff --git a/Plugins/eSpeak/espeak-data/voices/mb/mb-cz2 b/Plugins/eSpeak/espeak-data/voices/mb/mb-cz2
new file mode 100644
index 0000000..dbde212
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/voices/mb/mb-cz2
@@ -0,0 +1,6 @@
+name czech-mbrola-2
+language cs 7
+gender male
+
+pitch 82 117
+mbrola cz2 cs_phtrans
diff --git a/Plugins/eSpeak/espeak-data/voices/mb/mb-de2 b/Plugins/eSpeak/espeak-data/voices/mb/mb-de2
new file mode 100644
index 0000000..c0a5475
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/voices/mb/mb-de2
@@ -0,0 +1,6 @@
+name german-mbrola-2
+language de 6
+gender male
+
+mbrola de2 de2_phtrans
+
diff --git a/Plugins/eSpeak/espeak-data/voices/mb/mb-de4 b/Plugins/eSpeak/espeak-data/voices/mb/mb-de4
new file mode 100644
index 0000000..31bd479
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/voices/mb/mb-de4
@@ -0,0 +1,6 @@
+name german-mbrola-4
+language de 6
+gender male
+
+mbrola de4 de4_phtrans
+
diff --git a/Plugins/eSpeak/espeak-data/voices/mb/mb-de4-en b/Plugins/eSpeak/espeak-data/voices/mb/mb-de4-en
new file mode 100644
index 0000000..8fd4a63
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/voices/mb/mb-de4-en
@@ -0,0 +1,6 @@
+name en-german
+language en 9
+gender male
+
+mbrola de4 de4_phtrans
+
diff --git a/Plugins/eSpeak/espeak-data/voices/mb/mb-de5 b/Plugins/eSpeak/espeak-data/voices/mb/mb-de5
new file mode 100644
index 0000000..569f9d0
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/voices/mb/mb-de5
@@ -0,0 +1,10 @@
+name german-mbrola-5
+language de 7
+gender female
+
+pitch 140 220
+mbrola de5 de6_phtrans 22050
+
+// avoid glottal stops. de5 assumes [?] between pause and vowel
+replace 00 _! _
+replace 00 _| _
diff --git a/Plugins/eSpeak/espeak-data/voices/mb/mb-de5-en b/Plugins/eSpeak/espeak-data/voices/mb/mb-de5-en
new file mode 100644
index 0000000..e416c6d
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/voices/mb/mb-de5-en
@@ -0,0 +1,7 @@
+name en-german-5
+language en
+gender female
+
+pitch 140 220
+mbrola de5 de6_phtrans 22050
+
diff --git a/Plugins/eSpeak/espeak-data/voices/mb/mb-de6 b/Plugins/eSpeak/espeak-data/voices/mb/mb-de6
new file mode 100644
index 0000000..35a4a3f
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/voices/mb/mb-de6
@@ -0,0 +1,6 @@
+name german-mbrola-6
+language de 6
+gender male
+
+mbrola de6 de6_phtrans 22050
+
diff --git a/Plugins/eSpeak/espeak-data/voices/mb/mb-de6-grc b/Plugins/eSpeak/espeak-data/voices/mb/mb-de6-grc
new file mode 100644
index 0000000..a6e0f46
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/voices/mb/mb-de6-grc
@@ -0,0 +1,6 @@
+name german-mbrola-6
+language grc 6
+gender male
+
+mbrola de6 grc-de6_phtrans 22050
+
diff --git a/Plugins/eSpeak/espeak-data/voices/mb/mb-de7 b/Plugins/eSpeak/espeak-data/voices/mb/mb-de7
new file mode 100644
index 0000000..aa80eda
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/voices/mb/mb-de7
@@ -0,0 +1,7 @@
+name german-mbrola-7
+language de 7
+gender female
+
+pitch 140 220
+mbrola de7 de6_phtrans 22050
+
diff --git a/Plugins/eSpeak/espeak-data/voices/mb/mb-en1 b/Plugins/eSpeak/espeak-data/voices/mb/mb-en1
new file mode 100644
index 0000000..fc60f41
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/voices/mb/mb-en1
@@ -0,0 +1,7 @@
+name english-mb-en1
+language en-uk 3
+language en 2
+gender male
+
+pitch 82 117
+mbrola en1 en1_phtrans
diff --git a/Plugins/eSpeak/espeak-data/voices/mb/mb-es1 b/Plugins/eSpeak/espeak-data/voices/mb/mb-es1
new file mode 100644
index 0000000..d59fe79
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/voices/mb/mb-es1
@@ -0,0 +1,7 @@
+language es 7
+name spanish-mbrola-1
+gender male
+pitch 82 117
+
+mbrola es1 es_phtrans
+
diff --git a/Plugins/eSpeak/espeak-data/voices/mb/mb-es2 b/Plugins/eSpeak/espeak-data/voices/mb/mb-es2
new file mode 100644
index 0000000..42de588
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/voices/mb/mb-es2
@@ -0,0 +1,7 @@
+language es 7
+name spanish-mbrola-2
+gender male
+pitch 82 117
+
+mbrola es2 es_phtrans
+
diff --git a/Plugins/eSpeak/espeak-data/voices/mb/mb-fr1 b/Plugins/eSpeak/espeak-data/voices/mb/mb-fr1
new file mode 100644
index 0000000..7cbdab3
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/voices/mb/mb-fr1
@@ -0,0 +1,9 @@
+language fr 7
+name french-mbrola-1
+gender male
+
+dictrules 1
+stressLength 180 180 180 180 0 0 220 220
+pitch 82 117
+mbrola fr1 fr1_phtrans
+
diff --git a/Plugins/eSpeak/espeak-data/voices/mb/mb-fr1-en b/Plugins/eSpeak/espeak-data/voices/mb/mb-fr1-en
new file mode 100644
index 0000000..3666531
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/voices/mb/mb-fr1-en
@@ -0,0 +1,8 @@
+name en-french
+language en 10
+gender male
+
+dictrules 1
+pitch 82 117
+mbrola fr1 fr1_phtrans
+
diff --git a/Plugins/eSpeak/espeak-data/voices/mb/mb-fr4 b/Plugins/eSpeak/espeak-data/voices/mb/mb-fr4
new file mode 100644
index 0000000..c276bec
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/voices/mb/mb-fr4
@@ -0,0 +1,8 @@
+language fr 7
+name french-mbrola-4
+gender female
+
+dictrules 1
+pitch 140 220
+mbrola fr1 fr1_phtrans
+
diff --git a/Plugins/eSpeak/espeak-data/voices/mb/mb-fr4-en b/Plugins/eSpeak/espeak-data/voices/mb/mb-fr4-en
new file mode 100644
index 0000000..b8f7829
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/voices/mb/mb-fr4-en
@@ -0,0 +1,8 @@
+language en 10
+name en-french
+gender female
+
+dictrules 1
+pitch 140 220
+mbrola fr1 fr1_phtrans
+
diff --git a/Plugins/eSpeak/espeak-data/voices/mb/mb-gr2 b/Plugins/eSpeak/espeak-data/voices/mb/mb-gr2
new file mode 100644
index 0000000..30dea89
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/voices/mb/mb-gr2
@@ -0,0 +1,6 @@
+name greek-mbrola-1
+language el 7
+gender male
+
+pitch 82 117
+mbrola gr2 gr2_phtrans
diff --git a/Plugins/eSpeak/espeak-data/voices/mb/mb-gr2-en b/Plugins/eSpeak/espeak-data/voices/mb/mb-gr2-en
new file mode 100644
index 0000000..b48b178
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/voices/mb/mb-gr2-en
@@ -0,0 +1,6 @@
+name en-greek
+language en 7
+gender male
+
+pitch 82 117
+mbrola gr2 gr2_phtrans
diff --git a/Plugins/eSpeak/espeak-data/voices/mb/mb-hu1 b/Plugins/eSpeak/espeak-data/voices/mb/mb-hu1
new file mode 100644
index 0000000..b851955
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/voices/mb/mb-hu1
@@ -0,0 +1,6 @@
+name hungarian-mbrola-1
+language hu 7
+gender female
+
+pitch 140 220
+mbrola hu1 hu1_phtrans
diff --git a/Plugins/eSpeak/espeak-data/voices/mb/mb-hu1-en b/Plugins/eSpeak/espeak-data/voices/mb/mb-hu1-en
new file mode 100644
index 0000000..73ac62a
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/voices/mb/mb-hu1-en
@@ -0,0 +1,6 @@
+name en-hungarian
+language en 10
+gender female
+
+pitch 140 220
+mbrola hu1 hu1_phtrans
diff --git a/Plugins/eSpeak/espeak-data/voices/mb/mb-id1 b/Plugins/eSpeak/espeak-data/voices/mb/mb-id1
new file mode 100644
index 0000000..b86f593
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/voices/mb/mb-id1
@@ -0,0 +1,7 @@
+name indonesian-mbrola-1
+language id 7
+gender male
+
+pitch 82 117
+mbrola id1 id1_phtrans
+
diff --git a/Plugins/eSpeak/espeak-data/voices/mb/mb-it3 b/Plugins/eSpeak/espeak-data/voices/mb/mb-it3
new file mode 100644
index 0000000..00e8886
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/voices/mb/mb-it3
@@ -0,0 +1,8 @@
+name italian-mbrola-3
+language it 7
+gender male
+
+pitch 82 117
+mbrola it3 it3_phtrans
+
+replace 03 i I // final unstressed "i"
diff --git a/Plugins/eSpeak/espeak-data/voices/mb/mb-it4 b/Plugins/eSpeak/espeak-data/voices/mb/mb-it4
new file mode 100644
index 0000000..f2130ba
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/voices/mb/mb-it4
@@ -0,0 +1,8 @@
+name italian-mbrola-4
+language it 7
+gender female
+
+pitch 140 220
+mbrola it4 it3_phtrans
+
+replace 03 i I // final unstressed "i"
diff --git a/Plugins/eSpeak/espeak-data/voices/mb/mb-la1 b/Plugins/eSpeak/espeak-data/voices/mb/mb-la1
new file mode 100644
index 0000000..7ef93a5
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/voices/mb/mb-la1
@@ -0,0 +1,6 @@
+name latin-mbrola-1
+language la 7
+gender male
+
+pitch 82 117
+mbrola la1 la1_phtrans
diff --git a/Plugins/eSpeak/espeak-data/voices/mb/mb-nl2 b/Plugins/eSpeak/espeak-data/voices/mb/mb-nl2
new file mode 100644
index 0000000..fc37715
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/voices/mb/mb-nl2
@@ -0,0 +1,7 @@
+language nl 7
+name dutch-mbrola-2
+gender male
+
+pitch 82 117
+mbrola nl2 nl_phtrans
+
diff --git a/Plugins/eSpeak/espeak-data/voices/mb/mb-nl2-en b/Plugins/eSpeak/espeak-data/voices/mb/mb-nl2-en
new file mode 100644
index 0000000..0c2d13a
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/voices/mb/mb-nl2-en
@@ -0,0 +1,7 @@
+language en 10
+name en-dutch
+gender male
+
+pitch 82 117
+mbrola nl2 nl_phtrans
+
diff --git a/Plugins/eSpeak/espeak-data/voices/mb/mb-pl1 b/Plugins/eSpeak/espeak-data/voices/mb/mb-pl1
new file mode 100644
index 0000000..4e2b9d2
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/voices/mb/mb-pl1
@@ -0,0 +1,6 @@
+name polish-mbrola-1
+language pl 7
+gender female
+
+pitch 140 220
+mbrola pl1 pl1_phtrans
diff --git a/Plugins/eSpeak/espeak-data/voices/mb/mb-pl1-en b/Plugins/eSpeak/espeak-data/voices/mb/mb-pl1-en
new file mode 100644
index 0000000..9ba872a
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/voices/mb/mb-pl1-en
@@ -0,0 +1,6 @@
+name en-polish
+language en 11
+gender female
+
+pitch 140 220
+mbrola pl1 pl1_phtrans
diff --git a/Plugins/eSpeak/espeak-data/voices/mb/mb-pt1 b/Plugins/eSpeak/espeak-data/voices/mb/mb-pt1
new file mode 100644
index 0000000..ebd92ff
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/voices/mb/mb-pt1
@@ -0,0 +1,9 @@
+language pt 7
+name portugal-mbrola-1
+gender female
+pitch 140 220
+
+dictrules 1
+
+mbrola pt1 pt1_phtrans
+
diff --git a/Plugins/eSpeak/espeak-data/voices/mb/mb-ro1 b/Plugins/eSpeak/espeak-data/voices/mb/mb-ro1
new file mode 100644
index 0000000..14417c1
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/voices/mb/mb-ro1
@@ -0,0 +1,7 @@
+name romanian-mbrola-1
+language ro 7
+gender male
+
+pitch 82 117
+mbrola ro1 ro1_phtrans
+
diff --git a/Plugins/eSpeak/espeak-data/voices/mb/mb-ro1-en b/Plugins/eSpeak/espeak-data/voices/mb/mb-ro1-en
new file mode 100644
index 0000000..f310f86
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/voices/mb/mb-ro1-en
@@ -0,0 +1,7 @@
+name en-romanian
+language en 9
+gender male
+
+pitch 82 117
+mbrola ro1 ro1_phtrans
+
diff --git a/Plugins/eSpeak/espeak-data/voices/mb/mb-sw1 b/Plugins/eSpeak/espeak-data/voices/mb/mb-sw1
new file mode 100644
index 0000000..4c62392
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/voices/mb/mb-sw1
@@ -0,0 +1,7 @@
+name swedish-mbrola-1
+language sv 7
+gender male
+
+pitch 82 117
+mbrola sw1 sv_phtrans
+
diff --git a/Plugins/eSpeak/espeak-data/voices/mb/mb-sw1-en b/Plugins/eSpeak/espeak-data/voices/mb/mb-sw1-en
new file mode 100644
index 0000000..52692c3
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/voices/mb/mb-sw1-en
@@ -0,0 +1,7 @@
+name en-swedish
+language en 11
+gender male
+
+pitch 82 117
+mbrola sw1 sv_phtrans
+
diff --git a/Plugins/eSpeak/espeak-data/voices/mb/mb-sw2 b/Plugins/eSpeak/espeak-data/voices/mb/mb-sw2
new file mode 100644
index 0000000..c632e26
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/voices/mb/mb-sw2
@@ -0,0 +1,7 @@
+name swedish-mbrola-2
+language sv 8
+gender female
+
+pitch 140 220
+mbrola sw2 sv2_phtrans
+
diff --git a/Plugins/eSpeak/espeak-data/voices/mb/mb-sw2-en b/Plugins/eSpeak/espeak-data/voices/mb/mb-sw2-en
new file mode 100644
index 0000000..f2033dc
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/voices/mb/mb-sw2-en
@@ -0,0 +1,7 @@
+name en-swedish-f
+language en
+gender female
+
+pitch 140 220
+mbrola sw2 sv2_phtrans
+
diff --git a/Plugins/eSpeak/espeak-data/voices/mb/mb-us1 b/Plugins/eSpeak/espeak-data/voices/mb/mb-us1
new file mode 100644
index 0000000..c62589b
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/voices/mb/mb-us1
@@ -0,0 +1,12 @@
+name us-mbrola-1
+language en-us
+language en 8
+gender female
+
+phonemes en_us
+dictrules 3 6
+
+stressLength 170 135 205 205 0 0 245 275
+
+pitch 140 220
+mbrola us1 us_phtrans
diff --git a/Plugins/eSpeak/espeak-data/voices/mb/mb-us2 b/Plugins/eSpeak/espeak-data/voices/mb/mb-us2
new file mode 100644
index 0000000..d94fce5
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/voices/mb/mb-us2
@@ -0,0 +1,12 @@
+name us-mbrola-2
+language en-us
+language en 7
+gender male
+
+phonemes en_us
+dictrules 3 6
+
+stressLength 170 135 205 205 0 0 245 275
+
+pitch 82 117
+mbrola us2 us_phtrans
diff --git a/Plugins/eSpeak/espeak-data/voices/mb/mb-us3 b/Plugins/eSpeak/espeak-data/voices/mb/mb-us3
new file mode 100644
index 0000000..645e1b7
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/voices/mb/mb-us3
@@ -0,0 +1,12 @@
+name us-mbrola-3
+language en-us
+language en 8
+gender male
+
+phonemes en_us
+dictrules 3 6
+
+stressLength 170 135 205 205 0 0 245 275
+
+pitch 82 117
+mbrola us3 us3_phtrans
diff --git a/Plugins/eSpeak/espeak-data/voices/mk b/Plugins/eSpeak/espeak-data/voices/mk
new file mode 100644
index 0000000..4607dd0
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/voices/mk
@@ -0,0 +1,4 @@
+name macedonian-test
+language mk
+gender male
+
diff --git a/Plugins/eSpeak/espeak-data/voices/nl b/Plugins/eSpeak/espeak-data/voices/nl
new file mode 100644
index 0000000..6a8d5ef
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/voices/nl
@@ -0,0 +1,3 @@
+language nl
+name dutch-test
+gender male
diff --git a/Plugins/eSpeak/espeak-data/voices/no b/Plugins/eSpeak/espeak-data/voices/no
new file mode 100644
index 0000000..77b60b9
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/voices/no
@@ -0,0 +1,6 @@
+name norwegian-test
+language no
+language nb
+gender male
+
+intonation 4
diff --git a/Plugins/eSpeak/espeak-data/voices/pl b/Plugins/eSpeak/espeak-data/voices/pl
new file mode 100644
index 0000000..8fc65d4
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/voices/pl
@@ -0,0 +1,5 @@
+name polish
+language pl
+gender male
+
+intonation 2
diff --git a/Plugins/eSpeak/espeak-data/voices/pt b/Plugins/eSpeak/espeak-data/voices/pt
new file mode 100644
index 0000000..53cb314
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/voices/pt
@@ -0,0 +1,7 @@
+name brazil
+language pt
+language pt-br
+gender male
+
+dictrules 2
+
diff --git a/Plugins/eSpeak/espeak-data/voices/pt-pt b/Plugins/eSpeak/espeak-data/voices/pt-pt
new file mode 100644
index 0000000..e23915f
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/voices/pt-pt
@@ -0,0 +1,7 @@
+name portugal
+language pt-pt
+gender male
+phonemes pt_pt
+
+dictrules 1
+intonation 2
diff --git a/Plugins/eSpeak/espeak-data/voices/ro b/Plugins/eSpeak/espeak-data/voices/ro
new file mode 100644
index 0000000..d8ecd25
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/voices/ro
@@ -0,0 +1,5 @@
+name romanian
+language ro
+gender male
+
+
diff --git a/Plugins/eSpeak/espeak-data/voices/ru b/Plugins/eSpeak/espeak-data/voices/ru
new file mode 100644
index 0000000..238c691
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/voices/ru
@@ -0,0 +1,6 @@
+name russian_test
+language ru
+gender male
+
+replace 03 a a#
+
diff --git a/Plugins/eSpeak/espeak-data/voices/sk b/Plugins/eSpeak/espeak-data/voices/sk
new file mode 100644
index 0000000..026363f
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/voices/sk
@@ -0,0 +1,4 @@
+name slovak
+language sk
+gender male
+
diff --git a/Plugins/eSpeak/espeak-data/voices/sr b/Plugins/eSpeak/espeak-data/voices/sr
new file mode 100644
index 0000000..a7a8223
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/voices/sr
@@ -0,0 +1,15 @@
+name serbian
+language sr
+gender male
+dictionary hbs
+
+// attributes towards !variant3 pitch 80 120
+formant 0 100 100 100
+formant 1 97 97 100
+formant 2 97 97 100
+formant 3 97 102 100
+formant 4 97 102 100
+formant 5 97 102 100
+
+stressAdd 10 10 0 0 0 0 -30 -30
+dictrules 2 4
diff --git a/Plugins/eSpeak/espeak-data/voices/sv b/Plugins/eSpeak/espeak-data/voices/sv
new file mode 100644
index 0000000..df70f43
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/voices/sv
@@ -0,0 +1,4 @@
+name swedish
+language sv
+gender male
+
diff --git a/Plugins/eSpeak/espeak-data/voices/sw b/Plugins/eSpeak/espeak-data/voices/sw
new file mode 100644
index 0000000..cf584b7
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/voices/sw
@@ -0,0 +1,4 @@
+name swahihi-test
+language sw
+gender male
+
diff --git a/Plugins/eSpeak/espeak-data/voices/ta b/Plugins/eSpeak/espeak-data/voices/ta
new file mode 100644
index 0000000..8848d68
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/voices/ta
@@ -0,0 +1,6 @@
+name tamil
+language ta
+gender male
+
+intonation 2
+consonants 80
diff --git a/Plugins/eSpeak/espeak-data/voices/tr b/Plugins/eSpeak/espeak-data/voices/tr
new file mode 100644
index 0000000..4f1904e
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/voices/tr
@@ -0,0 +1,4 @@
+name turkish
+language tr
+gender male
+
diff --git a/Plugins/eSpeak/espeak-data/voices/vi b/Plugins/eSpeak/espeak-data/voices/vi
new file mode 100644
index 0000000..1596e3c
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/voices/vi
@@ -0,0 +1,6 @@
+name vietnam-test
+language vi
+gender male
+
+words 1
+pitch 80 118
diff --git a/Plugins/eSpeak/espeak-data/voices/zh b/Plugins/eSpeak/espeak-data/voices/zh
new file mode 100644
index 0000000..03edde4
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/voices/zh
@@ -0,0 +1,30 @@
+name Mandarin
+language zh
+gender male
+words 1
+pitch 80 118
+
+//for some dialects
+
+//[en]: replace ng with n
+//[zh]: �޺�������ng���n
+//replace 0 N n
+
+//[en]: replace rfx consonants
+//[zh]: �޾�������r���l��z��er���e
+//replace 0 ts.h tsh
+//replace 0 ts. ts
+//replace 0 s. s
+//replace 0 i. i[
+//replace 0 z. l
+//replace 0 z. z
+//replace 0 @r @
+
+//[en]: replace beginning n or l
+//[zh]: ����nl��n���l��l���n
+//replace 2 n l
+//replace 2 l n
+
+//[en]: replace beginning w with v
+//[zh]: w���v
+//replace 0 w v \ No newline at end of file
diff --git a/Plugins/eSpeak/espeak-data/zh_dict b/Plugins/eSpeak/espeak-data/zh_dict
new file mode 100644
index 0000000..900e0cd
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/zh_dict
Binary files differ
diff --git a/Plugins/eSpeak/espeak-data/zhy_dict b/Plugins/eSpeak/espeak-data/zhy_dict
new file mode 100644
index 0000000..6626ba4
--- /dev/null
+++ b/Plugins/eSpeak/espeak-data/zhy_dict
Binary files differ
diff --git a/Plugins/eSpeak/lib/PAStaticWMME.lib b/Plugins/eSpeak/lib/PAStaticWMME.lib
new file mode 100644
index 0000000..6ddf559
--- /dev/null
+++ b/Plugins/eSpeak/lib/PAStaticWMME.lib
Binary files differ
diff --git a/Plugins/eSpeak/m_speak.h b/Plugins/eSpeak/m_speak.h
new file mode 100644
index 0000000..78b46c8
--- /dev/null
+++ b/Plugins/eSpeak/m_speak.h
@@ -0,0 +1,278 @@
+/*
+Copyright (C) 2007 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#ifndef __M_SPEAK_H__
+# define __M_SPEAK_H__
+
+
+/*
+There is 2 ways of using the speak plugin:
+
+1. Older and simple way: just call
+ Speak_Say(hContact, _T("text to speak"))
+and the text will be spoken using contact settings. If hContact is NULL, it will use
+system settings.
+Previous versions only had an ascii version, so if you want to support then you need
+to call
+ Speak_SayA(hContact, "text to speak")
+
+
+2. Integrating with meSpeak GUI: for that you have first to register a speak type and
+then call the speak functions. In both case you have 2 options:
+
+2.1 Sending the full text: meSpeak GUI will only allow to enable/disable the type.
+To register call (in modules loaded):
+ Speak_Register("PluginName (DB key)", "name", "Prety name for GUI", "icon_xyz")
+And to speak call:
+ Speak_SayEx("name", hContact, _T("text to speak"))
+
+2.2 Using templates: you will not pass the text, but some variables. meSpeak handles
+the GUI to allow the user to create the text for those variables. These functions
+end with WT (with templates).
+To register call (in modules loaded):
+ char *templates[] = { "Name\nDefault\n%var1%\tDescription 1\n%var2%\tDescription2\n%var3%\tDescription 3" };
+ Speak_RegisterWT("PluginName (DB key)", "name", "Prety name for GUI", "icon_xyz",
+ templates, 1);
+And to speak call:
+ TCHAR *variables[] = { _T("var1"), _T("Value 1"), _T("var2"), _T("Value 2"), _T("var3"), _T("Value 3") };
+ Speak_SayExWT("name", hContact, 0, variables, 3);
+*/
+
+
+#define MIID_SPEAK { 0x1ef72725, 0x6a83, 0x483b, { 0xaa, 0x50, 0x89, 0x53, 0xe3, 0x59, 0xee, 0xad } }
+
+
+/*
+Speak a text
+
+wParam: (HANDLE) hContact
+lParam: (char *) text
+return: 0 on success
+*/
+#define MS_SPEAK_SAY_A "Speak/Say"
+
+
+/*
+Speak a unicode text
+
+wParam: (HANDLE) hContact
+lParam: (WCHAR *) text
+return: 0 on success
+*/
+#define MS_SPEAK_SAY_W "Speak/SayW"
+
+
+typedef struct {
+ int cbSize;
+
+ const char *module;
+ const char *name; // Internal type name
+ const char *description; // Will be translated
+ const char *icon; // Name off icolib icon
+
+ // Aditional data if wants to use add to history services
+ char **templates; // Each entry is: "Name\nDefault\n%var%\tDescription\n%var%\tDescription\n%var%\tDescription"
+ int numTemplates;
+
+} SPEAK_TYPE;
+
+
+/*
+Register and speak type
+
+wParam: (SPEAK_TYPE *) type
+lParam: 0
+return: 0 on success
+*/
+#define MS_SPEAK_REGISTER "Speak/Register"
+
+
+#define SPEAK_CHAR 1
+#define SPEAK_WCHAR 2
+
+typedef struct {
+ int cbSize;
+
+ const char *type; // Internal type name
+ HANDLE hContact;
+ int flags; // SPEAK_*
+
+ int templateNum; // -1 to use text
+ union
+ {
+ const void *text;
+
+ struct
+ {
+ void *variables;
+ int numVariables;
+ };
+ };
+
+} SPEAK_ITEM;
+
+
+/*
+Speak a text
+
+wParam: (SPEAK_ITEM *) Item
+lParam: 0
+return: 0 on success
+*/
+#define MS_SPEAK_SAYEX "Speak/SayEx"
+
+
+
+// Helper functions
+
+static int Speak_SayA(HANDLE hContact, const char *text)
+{
+ return CallService(MS_SPEAK_SAY_A, (WPARAM) hContact, (LPARAM) text);
+}
+
+static int Speak_SayW(HANDLE hContact, const WCHAR *text)
+{
+ return CallService(MS_SPEAK_SAY_W, (WPARAM) hContact, (LPARAM) text);
+}
+
+static int Speak_Register(char *module, char *name, char *description, char *icon)
+{
+ SPEAK_TYPE type;
+
+ if (!ServiceExists(MS_SPEAK_REGISTER))
+ return -1;
+
+ type.cbSize = sizeof(type);
+ type.module = module;
+ type.name = name;
+ type.description = description;
+ type.icon = icon;
+ type.templates = NULL;
+ type.numTemplates = 0;
+
+ return CallService(MS_SPEAK_REGISTER, (WPARAM) &type, 0);
+}
+
+static int Speak_RegisterWT(const char *module, const char *name, const char *description,
+ const char *icon, char **templates, int numTemplates)
+{
+ SPEAK_TYPE type;
+
+ if (!ServiceExists(MS_SPEAK_REGISTER))
+ return -1;
+
+ type.cbSize = sizeof(type);
+ type.module = module;
+ type.name = name;
+ type.description = description;
+ type.icon = icon;
+ type.templates = templates;
+ type.numTemplates = numTemplates;
+
+ return CallService(MS_SPEAK_REGISTER, (WPARAM) &type, 0);
+}
+
+static int Speak_SayExA(char *type, HANDLE hContact, const char *text)
+{
+ SPEAK_ITEM item;
+
+ if (!ServiceExists(MS_SPEAK_SAYEX))
+ // Try old service
+ return Speak_SayA(hContact, text);
+
+ item.cbSize = sizeof(item);
+ item.flags = SPEAK_CHAR;
+ item.type = type;
+ item.hContact = hContact;
+ item.templateNum = -1;
+ item.text = text;
+
+ return CallService(MS_SPEAK_SAYEX, (WPARAM) &item, 0);
+}
+
+static int Speak_SayExW(char *type, HANDLE hContact, const WCHAR *text)
+{
+ SPEAK_ITEM item;
+
+ if (!ServiceExists(MS_SPEAK_SAYEX))
+ // Try old service
+ return Speak_SayW(hContact, text);
+
+ item.cbSize = sizeof(item);
+ item.flags = SPEAK_WCHAR;
+ item.type = type;
+ item.hContact = hContact;
+ item.templateNum = -1;
+ item.text = text;
+
+ return CallService(MS_SPEAK_SAYEX, (WPARAM) &item, 0);
+}
+
+static int Speak_SayExWTA(char *type, HANDLE hContact, int templateNum, char **variables, int numVariables)
+{
+ SPEAK_ITEM item;
+
+ if (!ServiceExists(MS_SPEAK_SAYEX))
+ return -1;
+
+ item.cbSize = sizeof(item);
+ item.flags = SPEAK_CHAR;
+ item.type = type;
+ item.hContact = hContact;
+ item.templateNum = templateNum;
+ item.variables = variables;
+ item.numVariables = numVariables;
+
+ return CallService(MS_SPEAK_SAYEX, (WPARAM) &item, 0);
+}
+
+static int Speak_SayExWTW(char *type, HANDLE hContact, int templateNum, WCHAR **variables, int numVariables)
+{
+ SPEAK_ITEM item;
+
+ if (!ServiceExists(MS_SPEAK_SAYEX))
+ return -1;
+
+ item.cbSize = sizeof(item);
+ item.flags = SPEAK_WCHAR;
+ item.type = type;
+ item.hContact = hContact;
+ item.templateNum = templateNum;
+ item.variables = variables;
+ item.numVariables = numVariables;
+
+ return CallService(MS_SPEAK_SAYEX, (WPARAM) &item, 0);
+}
+
+
+#ifdef UNICODE
+# define MS_SPEAK_SAY MS_SPEAK_SAY_W
+# define Speak_Say Speak_SayW
+# define Speak_SayEx Speak_SayExW
+# define Speak_SayExWT Speak_SayExWTW
+#else
+# define MS_SPEAK_SAY MS_SPEAK_SAY_A
+# define Speak_Say Speak_SayA
+# define Speak_SayEx Speak_SayExA
+# define Speak_SayExWT Speak_SayExWTA
+#endif
+
+
+#endif // __M_SPEAK_H__
diff --git a/Plugins/eSpeak/options.cpp b/Plugins/eSpeak/options.cpp
new file mode 100644
index 0000000..aaada97
--- /dev/null
+++ b/Plugins/eSpeak/options.cpp
@@ -0,0 +1,1036 @@
+/*
+Copyright (C) 2006 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#include "commons.h"
+
+
+// Prototypes /////////////////////////////////////////////////////////////////////////////////////
+
+HANDLE hOptHook = NULL;
+HANDLE hUserInfoInitHook = NULL;
+
+Options opts = {0};
+
+static int UserInfoInitialize(WPARAM wParam, LPARAM lParam);
+
+static BOOL CALLBACK UserInfoDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam);
+static BOOL CALLBACK SystemDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam);
+static BOOL CALLBACK OptionsDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam);
+static BOOL CALLBACK TypesDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam);
+
+static Language *GetLanguage(HWND hwndDlg);
+static Voice *GetVoice(HWND hwndDlg);
+static Variant *GetVariant(HWND hwndDlg);
+
+
+static OptPageControl optionsControls[] = {
+ { &opts.disable_offline, CONTROL_CHECKBOX, ID_OFFLINE, "DisableOffline", FALSE },
+ { &opts.disable_online, CONTROL_CHECKBOX, ID_ONLINE, "DisableOnline", FALSE },
+ { &opts.disable_away, CONTROL_CHECKBOX, ID_AWAY, "DisableAway", FALSE },
+ { &opts.disable_dnd, CONTROL_CHECKBOX, ID_DND, "DisableDND", FALSE },
+ { &opts.disable_na, CONTROL_CHECKBOX, ID_NA, "DisableNA", FALSE },
+ { &opts.disable_occupied, CONTROL_CHECKBOX, ID_OCCUPIED, "DisableOccupied", FALSE },
+ { &opts.disable_freechat, CONTROL_CHECKBOX, ID_FREECHAT, "DisableFreeChat", FALSE },
+ { &opts.disable_invisible, CONTROL_CHECKBOX, ID_INVISIBLE, "DisableInvisible", FALSE },
+ { &opts.disable_onthephone, CONTROL_CHECKBOX, ID_ONTHEPHONE, "DisableOnThePhone", FALSE },
+ { &opts.disable_outtolunch, CONTROL_CHECKBOX, ID_OUTTOLUNCH, "DisableOutToLunch", FALSE },
+ { &opts.enable_only_idle, CONTROL_CHECKBOX, IDC_ONLY_IDLE, "EnableOnlyIfIdle", FALSE },
+ { &opts.truncate, CONTROL_CHECKBOX, IDC_TRUNCATE_L, "Truncate", FALSE },
+ { &opts.truncate_len, CONTROL_SPIN, IDC_TRUNCATE, "TruncateLen", (WORD) 128, IDC_TRUNCATE_SPIN, (WORD) 1, (WORD) 1024 },
+ { &opts.use_flags, CONTROL_CHECKBOX, IDC_USE_FLAGS, "UseFlags", TRUE },
+ { &opts.respect_sndvol_mute, CONTROL_CHECKBOX, IDC_SNDVOL, "RespectSndVolMute", TRUE },
+ { &opts.select_variant_per_genre,CONTROL_CHECKBOX, IDC_PER_GENRE, "SelectVariantPerGenre", TRUE },
+};
+
+
+// Functions //////////////////////////////////////////////////////////////////////////////////////
+
+
+BOOL GetSettingBool(SPEAK_TYPE *type, char *setting, BOOL def)
+{
+ return GetSettingBool(type, -1, setting, def);
+}
+
+BOOL GetSettingBool(SPEAK_TYPE *type, int templ, char *setting, BOOL def)
+{
+ char tmp[128];
+ mir_snprintf(tmp, MAX_REGS(tmp), "%s_%d_%s", type->name, templ, setting);
+ return DBGetContactSettingByte(NULL, type->module == NULL ? MODULE_NAME : type->module, tmp, def) != 0;
+}
+
+void WriteSettingBool(SPEAK_TYPE *type, char *setting, BOOL val)
+{
+ WriteSettingBool(type, -1, setting, val);
+}
+
+void WriteSettingBool(SPEAK_TYPE *type, int templ, char *setting, BOOL val)
+{
+ char tmp[128];
+ mir_snprintf(tmp, MAX_REGS(tmp), "%s_%d_%s", type->name, templ, setting);
+ DBWriteContactSettingByte(NULL, type->module == NULL ? MODULE_NAME : type->module, tmp, val ? 1 : 0);
+}
+
+void WriteSettingTString(SPEAK_TYPE *type, int templ, char *setting, TCHAR *str)
+{
+ char tmp[128];
+ mir_snprintf(tmp, MAX_REGS(tmp), "%s_%d_%s", type->name, templ, setting);
+ DBWriteContactSettingTString(NULL, type->module == NULL ? MODULE_NAME : type->module, tmp, str);
+}
+
+
+int InitOptionsCallback(WPARAM wParam,LPARAM lParam)
+{
+ OPTIONSDIALOGPAGE odp;
+
+ ZeroMemory(&odp,sizeof(odp));
+ odp.cbSize=sizeof(odp);
+ odp.position=0;
+ odp.hInstance=hInst;
+ odp.ptszGroup = TranslateT("Events");
+ odp.ptszTitle = TranslateT("Speak");
+ odp.ptszTab = TranslateT("General");
+ odp.pfnDlgProc = SystemDlgProc;
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPTIONS);
+ odp.flags = ODPF_BOLDGROUPS | ODPF_TCHAR;
+ CallService(MS_OPT_ADDPAGE,wParam,(LPARAM)&odp);
+
+ odp.ptszTab = TranslateT("Advanced");
+ odp.pfnDlgProc = OptionsDlgProc;
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_ADVANCED);
+ odp.flags = ODPF_BOLDGROUPS | ODPF_TCHAR | ODPF_EXPERTONLY;
+ CallService(MS_OPT_ADDPAGE,wParam,(LPARAM)&odp);
+
+ if (types.getCount() > 0)
+ {
+ odp.ptszTab = TranslateT("Types");
+ odp.pfnDlgProc = TypesDlgProc;
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_TYPES);
+ odp.flags = ODPF_BOLDGROUPS | ODPF_TCHAR | ODPF_EXPERTONLY;
+ CallService(MS_OPT_ADDPAGE,wParam,(LPARAM)&odp);
+ }
+
+ return 0;
+}
+
+
+void InitOptions()
+{
+ LoadOptions();
+
+ hOptHook = HookEvent(ME_OPT_INITIALISE, InitOptionsCallback);
+ hUserInfoInitHook = HookEvent(ME_USERINFO_INITIALISE, UserInfoInitialize);
+}
+
+
+void DeInitOptions()
+{
+ UnhookEvent(hOptHook);
+ UnhookEvent(hUserInfoInitHook);
+}
+
+
+void LoadOptions()
+{
+ LoadOpts(optionsControls, MAX_REGS(optionsControls), MODULE_NAME);
+
+ if (languages.getCount() <= 0)
+ return;
+
+ opts.default_language = GetContactLanguage(NULL);
+ opts.default_voice = GetContactVoice(NULL, opts.default_language);
+ opts.default_variant = GetContactVariant(NULL);
+}
+
+
+void FillLanguagesCombo(HWND hwndDlg, HANDLE hContact)
+{
+ Language *def_lang;
+ if (hContact == NULL)
+ def_lang = opts.default_language;
+ else
+ def_lang = GetContactLanguage(hContact);
+
+ for (int i = 0; i < languages.getCount(); i++)
+ {
+ int pos = SendDlgItemMessage(hwndDlg, IDC_DEF_LANG, CB_ADDSTRING, 0, (LPARAM) languages[i]->full_name);
+ if (pos >= 0)
+ SendDlgItemMessage(hwndDlg, IDC_DEF_LANG, CB_SETITEMDATA, pos, (LPARAM) languages[i]);
+ }
+
+ if (def_lang != NULL && SendDlgItemMessage(hwndDlg, IDC_DEF_LANG, CB_SELECTSTRING, -1, (WPARAM) def_lang->full_name) < 0)
+ {
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ SendDlgItemMessage(hwndDlg, IDC_DEF_LANG, CB_SETCURSEL, 0, 0);
+ }
+}
+
+
+void FillVoicesCombo(HWND hwndDlg, HANDLE hContact)
+{
+ Language *lang = GetLanguage(hwndDlg);
+
+ Voice *def_voice;
+ if (hContact == NULL)
+ def_voice = opts.default_voice;
+ else
+ def_voice = GetContactVoice(hContact, lang);
+
+ SendDlgItemMessage(hwndDlg, IDC_VOICE, CB_RESETCONTENT, 0, 0);
+
+ int sel = -1;
+ for (int i = 0; i < lang->voices.getCount(); i++)
+ {
+ TCHAR name[NAME_SIZE];
+ Voice *voice = lang->voices[i];
+
+ TCHAR *gender = NULL;
+ if (voice->gender == GENDER_MALE)
+ gender = TranslateT("Male");
+ else if (voice->gender == GENDER_FEMALE)
+ gender = TranslateT("Female");
+
+ TCHAR *age = NULL;
+ if (voice->age[0] != 0)
+ age = voice->age;
+
+ if (gender != NULL && age != NULL)
+ mir_sntprintf(name, MAX_REGS(name), _T("%s (%s, %s)"), voice->name, gender, age);
+
+ else if (gender != NULL)
+ mir_sntprintf(name, MAX_REGS(name), _T("%s (%s)"), voice->name, gender);
+
+ else if (age != NULL)
+ mir_sntprintf(name, MAX_REGS(name), _T("%s (%s)"), voice->name, age);
+
+ else
+ mir_sntprintf(name, MAX_REGS(name), _T("%s"), voice->name);
+
+ int pos = SendDlgItemMessage(hwndDlg, IDC_VOICE, CB_ADDSTRING, 0, (LPARAM) name);
+ if (pos >= 0)
+ SendDlgItemMessage(hwndDlg, IDC_VOICE, CB_SETITEMDATA, pos, (LPARAM) voice);
+
+ if (def_voice == voice)
+ sel = pos;
+ }
+ if (sel < 0)
+ {
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ sel = 0;
+ }
+
+ SendDlgItemMessage(hwndDlg, IDC_VOICE, CB_SETCURSEL, sel, 0);
+}
+
+
+void FillVariantsCombo(HWND hwndDlg, HANDLE hContact)
+{
+ SendDlgItemMessage(hwndDlg, IDC_VARIANT, CB_RESETCONTENT, 0, 0);
+
+ int pos = SendDlgItemMessage(hwndDlg, IDC_VARIANT, CB_ADDSTRING, 0, (LPARAM) _T("<None>"));
+ if (pos >= 0)
+ SendDlgItemMessage(hwndDlg, IDC_VARIANT, CB_SETITEMDATA, pos, (LPARAM) NULL);
+
+ if (GetVoice(hwndDlg)->engine == ENGINE_SAPI)
+ {
+ SendDlgItemMessage(hwndDlg, IDC_VARIANT, CB_SETCURSEL, 0, 0);
+ }
+ else
+ {
+ Variant *def_var;
+ if (hContact == NULL)
+ def_var = opts.default_variant;
+ else
+ def_var = GetContactVariant(hContact);
+
+ for (int i = 0; i < variants.getCount(); i++)
+ {
+ int pos = SendDlgItemMessage(hwndDlg, IDC_VARIANT, CB_ADDSTRING, 0, (LPARAM) variants[i]->name);
+ if (pos >= 0)
+ SendDlgItemMessage(hwndDlg, IDC_VARIANT, CB_SETITEMDATA, pos, (LPARAM) variants[i]);
+ }
+
+ if (def_var == NULL)
+ {
+ SendDlgItemMessage(hwndDlg, IDC_VARIANT, CB_SETCURSEL, 0, 0);
+ }
+ else if (SendDlgItemMessage(hwndDlg, IDC_VARIANT, CB_SELECTSTRING, -1, (LPARAM) def_var->name) < 0)
+ {
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ SendDlgItemMessage(hwndDlg, IDC_VARIANT, CB_SETCURSEL, 0, 0);
+ }
+ }
+}
+
+
+static Language *GetLanguage(HWND hwndDlg)
+{
+ int sel = SendDlgItemMessage(hwndDlg, IDC_DEF_LANG, CB_GETCURSEL, 0, 0);
+ if (sel < 0)
+ sel = 0;
+
+ return (Language *) SendDlgItemMessage(hwndDlg, IDC_DEF_LANG, CB_GETITEMDATA, sel, 0);
+}
+
+
+static Voice *GetVoice(HWND hwndDlg)
+{
+ int sel = SendDlgItemMessage(hwndDlg, IDC_VOICE, CB_GETCURSEL, 0, 0);
+ if (sel < 0)
+ sel = 0;
+
+ return (Voice *) SendDlgItemMessage(hwndDlg, IDC_VOICE, CB_GETITEMDATA, sel, 0);
+}
+
+
+static Variant *GetVariant(HWND hwndDlg)
+{
+ int sel = SendDlgItemMessage(hwndDlg, IDC_VARIANT, CB_GETCURSEL, 0, 0);
+ if (sel < 0)
+ sel = 0;
+
+ return (Variant *) SendDlgItemMessage(hwndDlg, IDC_VARIANT, CB_GETITEMDATA, sel, 0);
+}
+
+
+static BOOL CALLBACK BaseDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg)
+ {
+ case WM_INITDIALOG:
+ {
+ HANDLE hContact = (HANDLE) GetWindowLong(hwndDlg, GWL_USERDATA);
+ if (languages.getCount() > 0)
+ {
+ FillLanguagesCombo(hwndDlg, hContact);
+ FillVoicesCombo(hwndDlg, hContact);
+ FillVariantsCombo(hwndDlg, hContact);
+ }
+
+ HWND item = GetDlgItem(hwndDlg, IDC_PUNCT);
+ if (item != NULL)
+ {
+ SendMessage(item, CB_ADDSTRING, 0, (LPARAM) TranslateT("None"));
+ SendMessage(item, CB_ADDSTRING, 0, (LPARAM) TranslateT("All"));
+ SendMessage(item, CB_ADDSTRING, 0, (LPARAM) TranslateT("Some"));
+ }
+
+ Voice *voice = GetVoice(hwndDlg);
+
+ for (int i = 0; i < NUM_PARAMETERS; i++)
+ {
+ item = GetDlgItem(hwndDlg, PARAMETERS[i].ctrl);
+ if (item == NULL)
+ continue;
+
+ RANGE *range;
+ if (voice->engine == ENGINE_ESPEAK)
+ range = &PARAMETERS[i].espeak;
+ else if (voice->engine == ENGINE_SAPI)
+ range = &PARAMETERS[i].sapi;
+ else
+ continue;
+
+ BOOL enabled = (range->min < range->max);
+ EnableWindow(item, enabled);
+ EnableWindow(GetDlgItem(hwndDlg, PARAMETERS[i].label), enabled);
+
+ if (enabled)
+ {
+ if (PARAMETERS[i].type == SCROLL)
+ {
+ SendMessage(item, TBM_SETRANGE, FALSE, MAKELONG(0, range->max - range->min));
+ SendMessage(item, TBM_SETPOS, TRUE, GetContactParam(hContact, i) - range->min);
+ }
+ else
+ {
+ SendMessage(item, CB_SETCURSEL, GetContactParam(hContact, i), 0);
+ }
+ }
+ }
+
+ break;
+ }
+
+ case WM_HSCROLL:
+ {
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ break;
+ }
+
+ case WM_COMMAND:
+ {
+ if (HIWORD(wParam) == CBN_SELCHANGE)
+ {
+ if (languages.getCount() <= 0)
+ break;
+
+ if ((HWND)lParam == GetFocus())
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+
+ HANDLE hContact = (HANDLE) GetWindowLong(hwndDlg, GWL_USERDATA);
+
+ if (LOWORD(wParam) == IDC_DEF_LANG)
+ {
+ FillVoicesCombo(hwndDlg, hContact);
+ FillVariantsCombo(hwndDlg, hContact);
+ }
+ else if (LOWORD(wParam) == IDC_VOICE)
+ {
+ FillVariantsCombo(hwndDlg, hContact);
+ }
+
+ if (LOWORD(wParam) == IDC_DEF_LANG || LOWORD(wParam) == IDC_VOICE)
+ {
+ Voice *voice = GetVoice(hwndDlg);
+ for (int i = 0; i < NUM_PARAMETERS; i++)
+ {
+ HWND item = GetDlgItem(hwndDlg, PARAMETERS[i].ctrl);
+ if (item == NULL)
+ continue;
+
+ RANGE *range;
+ if (voice->engine == ENGINE_ESPEAK)
+ range = &PARAMETERS[i].espeak;
+ else if (voice->engine == ENGINE_SAPI)
+ range = &PARAMETERS[i].sapi;
+ else
+ continue;
+
+ BOOL enabled = (range->min < range->max);
+ EnableWindow(item, enabled);
+ EnableWindow(GetDlgItem(hwndDlg, PARAMETERS[i].label), enabled);
+
+ if (enabled)
+ {
+ if (PARAMETERS[i].type == SCROLL)
+ {
+ SendMessage(item, TBM_SETRANGE, FALSE, MAKELONG(0, range->max - range->min));
+
+ int def;
+ if (voice->engine == ENGINE_SAPI && PARAMETERS[i].eparam == espeakRATE)
+ def = SAPI_GetDefaultRateFor(voice->id);
+ else
+ def = range->def;
+
+ SendMessage(item, TBM_SETPOS, TRUE, def - range->min);
+ }
+ else
+ {
+ SendMessage(item, CB_SETCURSEL, range->def, 0);
+ }
+ }
+ }
+ }
+ }
+ else if (LOWORD(wParam) == IDC_SPEAK)
+ {
+ if (languages.getCount() <= 0)
+ break;
+
+ TCHAR text[1024];
+ GetDlgItemText(hwndDlg, IDC_TEST, text, MAX_REGS(text));
+ if (text[0] == _T('\0'))
+ break;
+
+ Language *lang = GetLanguage(hwndDlg);
+ if (lang == NULL)
+ break;
+
+ Voice *voice = GetVoice(hwndDlg);
+ if (voice == NULL)
+ break;
+
+ SpeakData *data = new SpeakData(lang, voice, GetVariant(hwndDlg), mir_tstrdup(text));
+ for (int i = 0; i < NUM_PARAMETERS; i++)
+ {
+ HWND item = GetDlgItem(hwndDlg, PARAMETERS[i].ctrl);
+ if (item == NULL)
+ {
+ data->setParameter(i, GetContactParam(NULL, i));
+ break;
+ }
+
+ RANGE *range;
+ if (voice->engine == ENGINE_ESPEAK)
+ range = &PARAMETERS[i].espeak;
+ else if (voice->engine == ENGINE_SAPI)
+ range = &PARAMETERS[i].sapi;
+ else
+ {
+ data->setParameter(i, GetContactParam(NULL, i));
+ break;
+ }
+
+ if (PARAMETERS[i].type == SCROLL)
+ data->setParameter(i, SendMessage(item, TBM_GETPOS, 0, 0) + range->min);
+ else
+ data->setParameter(i, SendMessage(item, CB_GETCURSEL, 0, 0));
+ }
+ queue->Add(0, (HANDLE) -1, data);
+ }
+
+ break;
+ }
+
+ case WM_NOTIFY:
+ {
+ LPNMHDR lpnmhdr = (LPNMHDR)lParam;
+
+ if (lpnmhdr->idFrom == 0 && lpnmhdr->code == PSN_APPLY)
+ {
+ if (languages.getCount() <= 0)
+ break;
+
+ HANDLE hContact = (HANDLE) GetWindowLong(hwndDlg, GWL_USERDATA);
+
+ // Language
+ Language *lang = GetLanguage(hwndDlg);
+
+ BOOL remove;
+ if (hContact == NULL)
+ {
+ TCHAR def[NAME_SIZE];
+ GetLangPackLanguage(def, MAX_REGS(def));
+
+ remove = (lstrcmpi(def, lang->language) == 0);
+ }
+ else
+ remove = FALSE;
+
+ if (remove)
+ DBDeleteContactSetting(hContact, MODULE_NAME, "TalkLanguage");
+ else
+ DBWriteContactSettingTString(hContact, MODULE_NAME, "TalkLanguage", lang->language);
+
+ if (hContact == NULL)
+ opts.default_language = lang;
+
+ // Voice
+ Voice *voice = GetVoice(hwndDlg);
+ if (voice == NULL)
+ voice = lang->voices[0];
+
+ DBWriteContactSettingTString(hContact, MODULE_NAME, "Voice", voice->name);
+
+ if (hContact == NULL)
+ opts.default_voice = voice;
+
+ // Variant
+ Variant *var = GetVariant(hwndDlg);
+ DBWriteContactSettingTString(hContact, MODULE_NAME, "Variant", var->name);
+
+ if (hContact == NULL)
+ opts.default_variant = var;
+
+ for (int i = 0; i < NUM_PARAMETERS; i++)
+ {
+ HWND item = GetDlgItem(hwndDlg, PARAMETERS[i].ctrl);
+ if (item == NULL)
+ continue;
+
+ RANGE *range;
+ if (voice->engine == ENGINE_ESPEAK)
+ range = &PARAMETERS[i].espeak;
+ else if (voice->engine == ENGINE_SAPI)
+ range = &PARAMETERS[i].sapi;
+ else
+ continue;
+
+ if (PARAMETERS[i].type == SCROLL)
+ SetContactParam(hContact, i, SendMessage(item, TBM_GETPOS, 0, 0) + range->min);
+ else
+ SetContactParam(hContact, i, SendMessage(item, CB_GETCURSEL, 0, 0));
+ }
+ }
+
+ break;
+ }
+
+ case WM_DRAWITEM:
+ {
+ LPDRAWITEMSTRUCT lpdis = (LPDRAWITEMSTRUCT)lParam;
+ if(lpdis->CtlID != IDC_DEF_LANG)
+ break;
+ if(lpdis->itemID == -1)
+ break;
+
+ Language *lang = (Language *) lpdis->itemData;
+
+ TEXTMETRIC tm;
+ RECT rc;
+
+ GetTextMetrics(lpdis->hDC, &tm);
+
+ COLORREF clrfore = SetTextColor(lpdis->hDC,GetSysColor(lpdis->itemState & ODS_DISABLED ? COLOR_GRAYTEXT : (lpdis->itemState & ODS_SELECTED ? COLOR_HIGHLIGHTTEXT : COLOR_WINDOWTEXT)));
+ COLORREF clrback = SetBkColor(lpdis->hDC,GetSysColor(lpdis->itemState & ODS_DISABLED ? COLOR_BTNFACE : (lpdis->itemState & ODS_SELECTED ? COLOR_HIGHLIGHT : COLOR_WINDOW)));
+
+ FillRect(lpdis->hDC, &lpdis->rcItem, GetSysColorBrush(lpdis->itemState & ODS_DISABLED ? COLOR_BTNFACE : (lpdis->itemState & ODS_SELECTED ? COLOR_HIGHLIGHT : COLOR_WINDOW)));
+
+ rc.left = lpdis->rcItem.left + 2;
+
+ // Draw icon
+ if (opts.use_flags)
+ {
+ HICON hFlag = LoadIconEx(lang);
+
+ rc.top = (lpdis->rcItem.bottom + lpdis->rcItem.top - ICON_SIZE) / 2;
+ DrawIconEx(lpdis->hDC, rc.left, rc.top, hFlag, 16, 16, 0, NULL, DI_NORMAL);
+
+ rc.left += ICON_SIZE + 4;
+
+ ReleaseIconEx(hFlag);
+ }
+
+ // Draw text
+ rc.right = lpdis->rcItem.right - 2;
+ rc.top = (lpdis->rcItem.bottom + lpdis->rcItem.top - tm.tmHeight) / 2;
+ rc.bottom = rc.top + tm.tmHeight;
+ DrawText(lpdis->hDC, lang->full_name, lstrlen(lang->full_name), &rc, DT_END_ELLIPSIS | DT_NOPREFIX | DT_SINGLELINE);
+
+ // Restore old colors
+ SetTextColor(lpdis->hDC, clrfore);
+ SetBkColor(lpdis->hDC, clrback);
+
+ return TRUE;
+ }
+
+ case WM_MEASUREITEM:
+ {
+ LPMEASUREITEMSTRUCT lpmis = (LPMEASUREITEMSTRUCT)lParam;
+ if(lpmis->CtlID != IDC_DEF_LANG)
+ break;
+
+ TEXTMETRIC tm;
+ GetTextMetrics(GetDC(hwndDlg), &tm);
+
+ if (opts.use_flags)
+ lpmis->itemHeight = max(ICON_SIZE, tm.tmHeight);
+ else
+ lpmis->itemHeight = tm.tmHeight;
+
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+
+static BOOL CALLBACK OptionsDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ return SaveOptsDlgProc(optionsControls, MAX_REGS(optionsControls), MODULE_NAME, hwndDlg, msg, wParam, lParam);
+}
+
+
+static BOOL CALLBACK SystemDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg)
+ {
+ case WM_INITDIALOG:
+ {
+ TranslateDialogDefault(hwndDlg);
+ SetWindowLong(hwndDlg, GWL_USERDATA, (LONG) NULL);
+ break;
+ }
+ }
+
+ return BaseDlgProc(hwndDlg, msg, wParam, lParam);
+}
+
+
+
+static int UserInfoInitialize(WPARAM wParam, LPARAM lParam)
+{
+ HANDLE hContact = (HANDLE) lParam;
+ if (hContact == NULL)
+ return 0;
+
+ if (languages.getCount() < 0)
+ return 0;
+
+ // Contact dialog
+ OPTIONSDIALOGPAGE odp = {0};
+ odp.cbSize = sizeof(odp);
+ odp.hInstance = hInst;
+ odp.pfnDlgProc = UserInfoDlgProc;
+ odp.position = 1000000000;
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_CONTACT_LANG);
+ odp.pszTitle = LPGEN("Speak");
+ CallService(MS_USERINFO_ADDPAGE, wParam, (LPARAM)&odp);
+
+ return 0;
+}
+
+
+static void EnableDisableControls(HWND hwndDlg)
+{
+ BOOL enabled = IsDlgButtonChecked(hwndDlg, IDC_ENABLE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_DEF_LANG_L), enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_DEF_LANG), enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_VOICE_L), enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_VOICE), enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_VARIANT_L), enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_VARIANT), enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_TEST_L), enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_TEST), enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_SPEAK), enabled);
+}
+
+
+static BOOL CALLBACK UserInfoDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg)
+ {
+ case WM_INITDIALOG:
+ {
+ HANDLE hContact = (HANDLE) lParam;
+ SetWindowLong(hwndDlg, GWL_USERDATA, (LONG) hContact);
+
+ TranslateDialogDefault(hwndDlg);
+
+ CheckDlgButton(hwndDlg, IDC_ENABLE, DBGetContactSettingByte(hContact, MODULE_NAME, "Enabled", TRUE) ? BST_CHECKED : BST_UNCHECKED);
+
+ EnableDisableControls(hwndDlg);
+
+ break;
+ }
+
+ case WM_COMMAND:
+ {
+ if (LOWORD(wParam) == IDC_ENABLE)
+ {
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ EnableDisableControls(hwndDlg);
+ }
+
+ break;
+ }
+ case WM_NOTIFY:
+ {
+ LPNMHDR lpnmhdr = (LPNMHDR)lParam;
+
+ if (lpnmhdr->idFrom == 0 && lpnmhdr->code == PSN_APPLY)
+ {
+ HANDLE hContact = (HANDLE) GetWindowLong(hwndDlg, GWL_USERDATA);
+ DBWriteContactSettingByte(hContact, MODULE_NAME, "Enabled", (BYTE) IsDlgButtonChecked(hwndDlg, IDC_ENABLE));
+ }
+
+ break;
+ }
+ }
+
+ return BaseDlgProc(hwndDlg, msg, wParam, lParam);
+}
+
+
+static BOOL ScreenToClient(HWND hWnd, LPRECT lpRect)
+{
+ BOOL ret;
+
+ POINT pt;
+
+ pt.x = lpRect->left;
+ pt.y = lpRect->top;
+
+ ret = ScreenToClient(hWnd, &pt);
+
+ if (!ret) return ret;
+
+ lpRect->left = pt.x;
+ lpRect->top = pt.y;
+
+
+ pt.x = lpRect->right;
+ pt.y = lpRect->bottom;
+
+ ret = ScreenToClient(hWnd, &pt);
+
+ lpRect->right = pt.x;
+ lpRect->bottom = pt.y;
+
+ return ret;
+}
+
+
+static void GetTextMetric(HFONT hFont, TEXTMETRIC *tm)
+{
+ HDC hdc = GetDC(NULL);
+ HFONT hOldFont = (HFONT) SelectObject(hdc, hFont);
+ GetTextMetrics(hdc, tm);
+ SelectObject(hdc, hOldFont);
+ ReleaseDC(NULL, hdc);
+}
+
+
+static BOOL CALLBACK TypesDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ static int avaiable = 0;
+ static int total = 0;
+ static int current = 0;
+ static int lineHeight = 0;
+
+ switch (msg)
+ {
+ case WM_INITDIALOG:
+ {
+ TranslateDialogDefault(hwndDlg);
+
+ RECT rc;
+ GetWindowRect(GetDlgItem(hwndDlg, IDC_EVENT_TYPES), &rc);
+
+ POINT pt = { rc.left, rc.bottom + 5 };
+ ScreenToClient(hwndDlg, &pt);
+ int origY = pt.y;
+
+ GetClientRect(hwndDlg, &rc);
+
+ HFONT hFont = (HFONT) SendMessage(hwndDlg, WM_GETFONT, 0, 0);
+ TEXTMETRIC font;
+ GetTextMetric(hFont, &font);
+
+ int height = max(font.tmHeight, 16) + 4;
+ int width = rc.right - rc.left - 35;
+
+ lineHeight = height;
+
+ // Create all items
+ int id = IDC_EVENT_TYPES + 1;
+ for(int i = 0; i < types.getCount(); i++)
+ {
+ SPEAK_TYPE *type = types[i];
+
+ int x = pt.x;
+
+ // Event type
+
+ HWND icon = CreateWindow(_T("STATIC"), _T(""), WS_CHILD | WS_VISIBLE | SS_ICON | SS_CENTERIMAGE,
+ x, pt.y + (height - 16) / 2, 16, 16, hwndDlg, NULL, hInst, NULL);
+ x += 20;
+
+ SendMessage(icon, STM_SETICON, (WPARAM) LoadIconEx(type->icon, TRUE), 0);
+
+ HWND tmp = CreateWindowA("STATIC", type->description, WS_CHILD | WS_VISIBLE,
+ x, pt.y + (height - font.tmHeight) / 2, width - (x - pt.x), font.tmHeight,
+ hwndDlg, NULL, hInst, NULL);
+ SendMessage(tmp, WM_SETFONT, (WPARAM) hFont, FALSE);
+
+ if (type->numTemplates <= 0)
+ {
+ // No templates
+
+ pt.y += height + 3;
+ x = pt.x + 20;
+
+ HWND chk = CreateWindow(_T("BUTTON"), TranslateT("Enable"),
+ WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_CHECKBOX | BS_AUTOCHECKBOX,
+ x, pt.y, width - (x - pt.x), height, hwndDlg, (HMENU) id, hInst, NULL);
+ SendMessage(chk, BM_SETCHECK, GetSettingBool(type, TEMPLATE_ENABLED, FALSE) ? BST_CHECKED : BST_UNCHECKED, 0);
+ SendMessage(chk, WM_SETFONT, (WPARAM) hFont, FALSE);
+
+ pt.y += height + 3;
+ x = pt.x + 20;
+
+ chk = CreateWindow(_T("BUTTON"), TranslateT("Speak contact name before text"),
+ WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_CHECKBOX | BS_AUTOCHECKBOX,
+ x, pt.y, width - (x - pt.x), height, hwndDlg, (HMENU) id + 2, hInst, NULL);
+ SendMessage(chk, BM_SETCHECK, GetSettingBool(type, SPEAK_NAME, TRUE) ? BST_CHECKED : BST_UNCHECKED, 0);
+ SendMessage(chk, WM_SETFONT, (WPARAM) hFont, FALSE);
+ }
+ else
+ {
+ // Templates
+
+ Buffer<char> name;
+ Buffer<TCHAR> templ;
+ for (int i = 0; i < type->numTemplates; i++)
+ {
+ pt.y += height + 3;
+ x = pt.x + 20;
+
+ name.clear();
+ const char *end = strchr(type->templates[i], '\n');
+ size_t len = (end == NULL ? strlen(type->templates[i]) : end - type->templates[i]);
+ name.append(type->templates[i], len);
+ name.translate();
+ name.append(':');
+ name.pack();
+
+ HWND chk = CreateWindowA("BUTTON", name.str,
+ WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_CHECKBOX | BS_AUTOCHECKBOX,
+ x, pt.y, 120, height, hwndDlg, (HMENU) (id + 2 * i), hInst, NULL);
+ SendMessage(chk, WM_SETFONT, (WPARAM) hFont, FALSE);
+ SendMessage(chk, BM_SETCHECK, GetSettingBool(type, i, TEMPLATE_ENABLED, FALSE) ? BST_CHECKED : BST_UNCHECKED, 0);
+ x += 120;
+
+ templ.clear();
+ GetTemplate(&templ, type, i);
+ templ.pack();
+
+ HWND edit = CreateWindowEx(WS_EX_CLIENTEDGE, _T("EDIT"), templ.str,
+ WS_CHILD | WS_VISIBLE | WS_TABSTOP | ES_LEFT | ES_AUTOHSCROLL,
+ x, pt.y, width - (x - pt.x), height, hwndDlg, (HMENU) (id + 2 * i + 1), hInst, NULL);
+ SendMessage(edit, WM_SETFONT, (WPARAM) hFont, FALSE);
+ }
+ }
+
+ pt.y += height + 10;
+ id += 60;
+ }
+
+ avaiable = rc.bottom - rc.top;
+ total = pt.y - 7;
+ current = 0;
+
+ SCROLLINFO si;
+ si.cbSize = sizeof(si);
+ si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS;
+ si.nMin = 0;
+ si.nMax = total;
+ si.nPage = avaiable;
+ si.nPos = current;
+ SetScrollInfo(hwndDlg, SB_VERT, &si, TRUE);
+
+ break;
+ }
+
+ case WM_VSCROLL:
+ {
+ if (lParam != 0)
+ break;
+
+ int yDelta; // yDelta = new_pos - current_pos
+ int yNewPos; // new position
+
+ switch (LOWORD(wParam))
+ {
+ case SB_PAGEUP:
+ yNewPos = current - avaiable / 2;
+ break;
+ case SB_PAGEDOWN:
+ yNewPos = current + avaiable / 2;
+ break;
+ case SB_LINEUP:
+ yNewPos = current - lineHeight;
+ break;
+ case SB_LINEDOWN:
+ yNewPos = current + lineHeight;
+ break;
+ case SB_THUMBPOSITION:
+ yNewPos = HIWORD(wParam);
+ break;
+ case SB_THUMBTRACK:
+ yNewPos = HIWORD(wParam);
+ break;
+ default:
+ yNewPos = current;
+ }
+
+ yNewPos = min(total - avaiable, max(0, yNewPos));
+
+ if (yNewPos == current)
+ break;
+
+ yDelta = yNewPos - current;
+ current = yNewPos;
+
+ // Scroll the window. (The system repaints most of the
+ // client area when ScrollWindowEx is called; however, it is
+ // necessary to call UpdateWindow in order to repaint the
+ // rectangle of pixels that were invalidated.)
+
+ ScrollWindowEx(hwndDlg, 0, -yDelta, (CONST RECT *) NULL,
+ (CONST RECT *) NULL, (HRGN) NULL, (LPRECT) NULL,
+ /* SW_ERASE | SW_INVALIDATE | */ SW_SCROLLCHILDREN);
+// UpdateWindow(hwndDlg);
+ InvalidateRect(hwndDlg, NULL, TRUE);
+
+ // Reset the scroll bar.
+
+ SCROLLINFO si;
+ si.cbSize = sizeof(si);
+ si.fMask = SIF_POS;
+ si.nPos = current;
+ SetScrollInfo(hwndDlg, SB_VERT, &si, TRUE);
+
+ break;
+ }
+
+ case WM_COMMAND:
+ {
+ if ((HWND) lParam != GetFocus())
+ break;
+
+ int id = (LOWORD(wParam) - IDC_EVENT_TYPES - 1) % 2;
+ if (id == 0)
+ {
+ // Checkboxes
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ }
+ else
+ {
+ if (HIWORD(wParam) == EN_CHANGE)
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ }
+ break;
+ }
+
+ case WM_NOTIFY:
+ {
+ LPNMHDR lpnmhdr = (LPNMHDR)lParam;
+ if (lpnmhdr->idFrom != 0 || lpnmhdr->code != PSN_APPLY)
+ break;
+
+ int id = IDC_EVENT_TYPES + 1;
+ for(int i = 0; i < types.getCount(); i++)
+ {
+ SPEAK_TYPE *type = types[i];
+
+ if (type->numTemplates <= 0)
+ {
+ // No templates
+
+ WriteSettingBool(type, TEMPLATE_ENABLED, IsDlgButtonChecked(hwndDlg, id));
+ WriteSettingBool(type, SPEAK_NAME, IsDlgButtonChecked(hwndDlg, id + 2));
+ }
+ else
+ {
+ // Templates
+
+ for(int i = 0; i < type->numTemplates; i++)
+ {
+ WriteSettingBool(type, i, TEMPLATE_ENABLED, IsDlgButtonChecked(hwndDlg, id + 2 * i));
+
+ TCHAR tmp[1024];
+ GetDlgItemText(hwndDlg, id + 2 * i + 1, tmp, 1024);
+ WriteSettingTString(type, i, TEMPLATE_TEXT, tmp);
+ }
+ }
+
+ id += 60;
+ }
+
+ return TRUE;
+ }
+
+ }
+
+ return 0;
+}
+
diff --git a/Plugins/eSpeak/options.h b/Plugins/eSpeak/options.h
new file mode 100644
index 0000000..c48962d
--- /dev/null
+++ b/Plugins/eSpeak/options.h
@@ -0,0 +1,78 @@
+/*
+Copyright (C) 2007 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#ifndef __OPTIONS_H__
+# define __OPTIONS_H__
+
+
+#include <windows.h>
+
+class Language;
+class Voice;
+class Variant;
+
+struct Options {
+ Language *default_language;
+ Voice *default_voice;
+ Variant *default_variant;
+
+ BOOL disable_offline;
+ BOOL disable_online;
+ BOOL disable_away;
+ BOOL disable_dnd;
+ BOOL disable_na;
+ BOOL disable_occupied;
+ BOOL disable_freechat;
+ BOOL disable_invisible;
+ BOOL disable_onthephone;
+ BOOL disable_outtolunch;
+ BOOL enable_only_idle;
+ BOOL truncate;
+ WORD truncate_len;
+
+ BOOL use_flags;
+ BOOL respect_sndvol_mute;
+ BOOL select_variant_per_genre;
+};
+
+extern Options opts;
+
+
+// Initializations needed by options
+void InitOptions();
+
+// Deinitializations needed by options
+void DeInitOptions();
+
+
+// Loads the options from DB
+// It don't need to be called, except in some rare cases
+void LoadOptions();
+
+
+BOOL GetSettingBool(SPEAK_TYPE *type, char *setting, BOOL def);
+BOOL GetSettingBool(SPEAK_TYPE *type, int templ, char *setting, BOOL def);
+void WriteSettingBool(SPEAK_TYPE *type, char *setting, BOOL val);
+void WriteSettingBool(SPEAK_TYPE *type, int templ, char *setting, BOOL val);
+void WriteSettingTString(SPEAK_TYPE *type, int templ, char *setting, TCHAR *str);
+
+
+
+#endif // __OPTIONS_H__
diff --git a/Plugins/eSpeak/res/unknown.ico b/Plugins/eSpeak/res/unknown.ico
new file mode 100644
index 0000000..6c21964
--- /dev/null
+++ b/Plugins/eSpeak/res/unknown.ico
Binary files differ
diff --git a/Plugins/eSpeak/resource.h b/Plugins/eSpeak/resource.h
new file mode 100644
index 0000000..9d7cd5a
--- /dev/null
+++ b/Plugins/eSpeak/resource.h
@@ -0,0 +1,64 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by resource.rc
+//
+#define IDI_UNKNOWN_FLAG 101
+#define IDD_OPTIONS 119
+#define IDD_CONTACT_LANG 120
+#define IDD_TYPES 121
+#define IDD_ADVANCED 122
+#define IDC_VOLUME 1000
+#define IDC_RATE 1001
+#define IDC_TRUNCATE_L 1001
+#define IDC_PITCH 1002
+#define IDC_TRUNCATE 1002
+#define IDC_RANGE 1003
+#define IDC_SPIN1 1003
+#define IDC_TRUNCATE_SPIN 1003
+#define ID_OFFLINE 1060
+#define IDC_ENABLE 1060
+#define ID_ONLINE 1061
+#define ID_AWAY 1062
+#define IDC_USE_FLAGS 1063
+#define ID_DND 1064
+#define ID_NA 1065
+#define ID_OCCUPIED 1066
+#define ID_FREECHAT 1067
+#define IDC_SOUNDVOL 1068
+#define IDC_SNDVOL 1068
+#define ID_INVISIBLE 1069
+#define ID_ONTHEPHONE 1070
+#define ID_OUTTOLUNCH 1071
+#define IDC_ONLY_IDLE 1072
+#define IDC_PER_GENRE 1073
+#define IDC_DEF_LANG 1075
+#define IDC_VOICE 1076
+#define IDC_ADVANCED 1077
+#define IDC_STATUS 1078
+#define IDC_SYSTEM 1079
+#define IDC_VARIANT 1080
+#define IDC_PUNCT 1081
+#define IDC_TEST 1084
+#define IDC_SPEAK 1085
+#define IDC_DEF_LANG_L 1086
+#define IDC_VARIANT_L 1088
+#define IDC_TEST_L 1089
+#define IDC_VARIANT_L2 1090
+#define IDC_VOLUME_L 1090
+#define IDC_RATE_L 1091
+#define IDC_PITCH_L 1092
+#define IDC_RANGE_L 1093
+#define IDC_PUNCT_L 1094
+#define IDC_EVENT_TYPES 1102
+#define IDC_VOICE_L 65535
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 103
+#define _APS_NEXT_COMMAND_VALUE 40001
+#define _APS_NEXT_CONTROL_VALUE 1004
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/Plugins/eSpeak/resource.rc b/Plugins/eSpeak/resource.rc
new file mode 100644
index 0000000..987fae2
--- /dev/null
+++ b/Plugins/eSpeak/resource.rc
@@ -0,0 +1,220 @@
+// Microsoft Visual C++ generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "afxres.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (U.S.) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page(1252)
+#endif //_WIN32
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Dialog
+//
+
+IDD_OPTIONS DIALOGEX 0, 0, 252, 189
+STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_VISIBLE
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg", 0, 0, 0x1
+BEGIN
+ GROUPBOX " System voice ",IDC_SYSTEM,2,6,250,177
+ LTEXT "Language:",IDC_DEF_LANG_L,11,24,52,8
+ COMBOBOX IDC_DEF_LANG,66,22,174,60,CBS_DROPDOWNLIST | CBS_OWNERDRAWFIXED | CBS_HASSTRINGS | WS_VSCROLL | WS_TABSTOP
+ LTEXT "Voice:",IDC_VOICE_L,11,39,52,8
+ COMBOBOX IDC_VOICE,66,37,174,60,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
+ LTEXT "Variant:",IDC_VARIANT_L,11,54,52,8
+ COMBOBOX IDC_VARIANT,66,52,107,60,CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP
+ LTEXT "Volume:",IDC_VOLUME_L,11,69,52,8
+ CONTROL "",IDC_VOLUME,"msctls_trackbar32",TBS_BOTH | TBS_NOTICKS | WS_TABSTOP,66,69,174,10
+ LTEXT "Rate:",IDC_RATE_L,11,84,52,8
+ CONTROL "",IDC_RATE,"msctls_trackbar32",TBS_BOTH | TBS_NOTICKS | WS_TABSTOP,66,84,174,10
+ LTEXT "Pitch:",IDC_PITCH_L,11,99,52,8
+ CONTROL "",IDC_PITCH,"msctls_trackbar32",TBS_BOTH | TBS_NOTICKS | WS_TABSTOP,66,99,174,10
+ LTEXT "Range:",IDC_RANGE_L,11,114,52,8
+ CONTROL "",IDC_RANGE,"msctls_trackbar32",TBS_BOTH | TBS_NOTICKS | WS_TABSTOP,66,114,174,10
+ LTEXT "Punctuation:",IDC_PUNCT_L,11,129,52,8
+ COMBOBOX IDC_PUNCT,66,127,107,60,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
+ LTEXT "Test:",IDC_TEST_L,11,163,37,8
+ EDITTEXT IDC_TEST,66,161,130,12,ES_AUTOHSCROLL
+ PUSHBUTTON "Speak",IDC_SPEAK,200,161,40,12
+END
+
+IDD_CONTACT_LANG DIALOGEX 0, 0, 224, 155
+STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_VISIBLE
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg", 0, 0, 0x1
+BEGIN
+ CONTROL "Enable speak for this contact",IDC_ENABLE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,9,4,203,13
+ LTEXT "Language:",IDC_DEF_LANG_L,8,27,52,8
+ COMBOBOX IDC_DEF_LANG,63,25,155,60,CBS_DROPDOWNLIST | CBS_OWNERDRAWFIXED | CBS_HASSTRINGS | WS_VSCROLL | WS_TABSTOP
+ LTEXT "Voice:",IDC_VOICE_L,8,41,52,8
+ COMBOBOX IDC_VOICE,63,40,155,60,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
+ LTEXT "Variant:",IDC_VARIANT_L,8,57,52,8
+ COMBOBOX IDC_VARIANT,63,55,107,60,CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP
+ LTEXT "Volume:",IDC_VOLUME_L,8,72,52,8
+ CONTROL "",IDC_VOLUME,"msctls_trackbar32",TBS_BOTH | TBS_NOTICKS | WS_TABSTOP,63,72,155,10
+ LTEXT "Rate:",IDC_RATE_L,8,87,52,8
+ CONTROL "",IDC_RATE,"msctls_trackbar32",TBS_BOTH | TBS_NOTICKS | WS_TABSTOP,63,87,155,10
+ LTEXT "Pitch:",IDC_PITCH_L,8,102,52,8
+ CONTROL "",IDC_PITCH,"msctls_trackbar32",TBS_BOTH | TBS_NOTICKS | WS_TABSTOP,63,102,155,10
+ LTEXT "Range:",IDC_RANGE_L,8,116,52,8
+ CONTROL "",IDC_RANGE,"msctls_trackbar32",TBS_BOTH | TBS_NOTICKS | WS_TABSTOP,63,116,155,10
+ LTEXT "Test:",IDC_TEST_L,8,138,37,8
+ EDITTEXT IDC_TEST,63,136,110,12,ES_AUTOHSCROLL
+ PUSHBUTTON "Speak",IDC_SPEAK,178,136,40,12
+END
+
+IDD_TYPES DIALOGEX 0, 0, 318, 230
+STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_VISIBLE | WS_VSCROLL
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg", 0, 0, 0x1
+BEGIN
+ LTEXT "Speak the following events:",IDC_EVENT_TYPES,3,4,297,13
+END
+
+IDD_ADVANCED DIALOGEX 0, 0, 252, 206
+STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_VISIBLE
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg", 0, 0, 0x1
+BEGIN
+ GROUPBOX " Enabled when ",IDC_STATUS,1,6,250,132
+ LTEXT " Disable when global status is: ",IDC_STATIC,10,20,230,10
+ CONTROL "Online",ID_ONLINE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,22,34,99,11
+ CONTROL "Away",ID_AWAY,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,22,47,99,11
+ CONTROL "NA",ID_NA,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,22,60,99,11
+ CONTROL "Occupied",ID_OCCUPIED,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,22,73,99,11
+ CONTROL "DND",ID_DND,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,22,86,99,11
+ CONTROL "Free for chat",ID_FREECHAT,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,136,34,99,11
+ CONTROL "On the phone",ID_ONTHEPHONE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,136,47,99,11
+ CONTROL "Out to lunch",ID_OUTTOLUNCH,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,136,60,99,11
+ CONTROL "Invisible",ID_INVISIBLE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,136,73,99,11
+ CONTROL "Offline",ID_OFFLINE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,136,86,99,11
+ CONTROL "Enable only if Miranda is idle",IDC_ONLY_IDLE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,10,105,230,10
+ CONTROL "Truncate text if it is longer than",IDC_TRUNCATE_L,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,10,120,123,10
+ EDITTEXT IDC_TRUNCATE,139,118,38,12,ES_NUMBER
+ CONTROL "",IDC_TRUNCATE_SPIN,"msctls_updown32",UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_ARROWKEYS | UDS_NOTHOUSANDS | UDS_HOTTRACK,179,117,11,14
+ GROUPBOX " Advanced ",IDC_ADVANCED,1,142,250,58
+ CONTROL "Use flags",IDC_USE_FLAGS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,10,157,230,10
+ CONTROL "Respect Miranda sound mute",IDC_SNDVOL,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,10,170,230,10
+ CONTROL "Select voice variant based on contact genre",IDC_PER_GENRE,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,10,183,230,10
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Icon
+//
+
+// Icon with lowest ID value placed first to ensure application icon
+// remains consistent on all systems.
+IDI_UNKNOWN_FLAG ICON "res\\unknown.ico"
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// DESIGNINFO
+//
+
+#ifdef APSTUDIO_INVOKED
+GUIDELINES DESIGNINFO
+BEGIN
+ IDD_OPTIONS, DIALOG
+ BEGIN
+ BOTTOMMARGIN, 161
+ END
+
+ IDD_CONTACT_LANG, DIALOG
+ BEGIN
+ BOTTOMMARGIN, 102
+ END
+
+ IDD_TYPES, DIALOG
+ BEGIN
+ LEFTMARGIN, 3
+ RIGHTMARGIN, 304
+ BOTTOMMARGIN, 227
+ END
+END
+#endif // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Dialog Info
+//
+
+IDD_OPTIONS DLGINIT
+BEGIN
+ IDC_PUNCT, 0x403, 14, 0
+0x6f4e, 0x656e, 0x4120, 0x6c6c, 0x5320, 0x6d6f, 0x0065,
+ 0
+END
+
+#endif // English (U.S.) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+/////////////////////////////////////////////////////////////////////////////
+// Portuguese (Brazil) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_PTB)
+#ifdef _WIN32
+LANGUAGE LANG_PORTUGUESE, SUBLANG_PORTUGUESE_BRAZILIAN
+#pragma code_page(1252)
+#endif //_WIN32
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE
+BEGIN
+ "resource.h\0"
+END
+
+2 TEXTINCLUDE
+BEGIN
+ "#include ""afxres.h""\r\n"
+ "\0"
+END
+
+3 TEXTINCLUDE
+BEGIN
+ "\r\n"
+ "\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+#endif // Portuguese (Brazil) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
+
diff --git a/Plugins/eSpeak/sdk/m_folders.h b/Plugins/eSpeak/sdk/m_folders.h
new file mode 100644
index 0000000..c232410
--- /dev/null
+++ b/Plugins/eSpeak/sdk/m_folders.h
@@ -0,0 +1,205 @@
+/*
+Custom profile folders plugin for Miranda IM
+
+Copyright © 2005 Cristian Libotean
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef M_CUSTOM_FOLDERS_H
+#define M_CUSTOM_FOLDERS_H
+
+#define FOLDERS_API 501 //dunno why it's here but it is :)
+
+#define PROFILE_PATH "%profile_path%"
+#define CURRENT_PROFILE "%current_profile%"
+#define MIRANDA_PATH "%miranda_path%"
+#define PLUGINS_PATH "%miranda_path%" "\\plugins"
+
+#define TO_WIDE(x) L ## x
+
+#define PROFILE_PATHW L"%profile_path%"
+#define CURRENT_PROFILEW L"%current_profile%"
+#define MIRANDA_PATHW L"%miranda_path%"
+
+#define FOLDER_AVATARS PROFILE_PATH "\\" CURRENT_PROFILE "\\avatars"
+#define FOLDER_VCARDS PROFILE_PATH "\\" CURRENT_PROFILE "\\vcards"
+#define FOLDER_LOGS PROFILE_PATH "\\" CURRENT_PROFILE "\\logs"
+#define FOLDER_RECEIVED_FILES PROFILE_PATH "\\" CURRENT_PROFILE "\\received files"
+#define FOLDER_DOCS MIRANDA_PATH "\\" "docs"
+
+#define FOLDER_CONFIG PLUGINS_PATH "\\" "config"
+
+#define FOLDER_SCRIPTS MIRANDA_PATH "\\" "scripts"
+
+#define FOLDER_UPDATES MIRANDA_PATH "\\" "updates"
+
+#define FOLDER_CUSTOMIZE MIRANDA_PATH "\\" "customize"
+#define FOLDER_CUSTOMIZE_SOUNDS FOLDER_CUSTOMIZE "\\sounds"
+#define FOLDER_CUSTOMIZE_ICONS FOLDER_CUSTOMIZE "\\icons"
+#define FOLDER_CUSTOMIZE_SMILEYS FOLDER_CUSTOMIZE "\\smileys"
+#define FOLDER_CUSTOMIZE_SKINS FOLDER_CUSTOMIZE "\\skins"
+#define FOLDER_CUSTOMIZE_THEMES FOLDER_CUSTOMIZE "\\themes"
+
+
+#define FOLDERS_NAME_MAX_SIZE 64 //maximum name and section size
+
+#define FF_UNICODE 0x00000001
+
+typedef struct{
+ int cbSize; //size of struct
+ char szSection[FOLDERS_NAME_MAX_SIZE]; //section name, if it doesn't exist it will be created otherwise it will just add this entry to it
+ char szName[FOLDERS_NAME_MAX_SIZE]; //entry name - will be shown in options
+ union{
+ const char *szFormat; //default string format. Fallback string in case there's no entry in the database for this folder. This should be the initial value for the path, users will be able to change it later.
+ const wchar_t *szFormatW; //String is dup()'d so you can free it later. If you set the unicode string don't forget to set the flag accordingly.
+ const TCHAR *szFormatT;
+ };
+ DWORD flags; //FF_* flags
+} FOLDERSDATA;
+
+/*Folders/Register/Path service
+ wParam - not used, must be 0
+ lParam - (LPARAM) (const FOLDERDATA *) - Data structure filled with
+ the necessary information.
+ Returns a handle to the registered path or 0 on error.
+ You need to use this to call the other services.
+*/
+#define MS_FOLDERS_REGISTER_PATH "Folders/Register/Path"
+
+/*Folders/Get/PathSize service
+ wParam - (WPARAM) (int) - handle to registered path
+ lParam - (LPARAM) (int *) - pointer to the variable that receives the size of the path
+ string (not including the null character). Depending on the flags set when creating the path
+ it will either call strlen() or wcslen() to get the length of the string.
+ Returns the size of the buffer.
+*/
+#define MS_FOLDERS_GET_SIZE "Folders/Get/PathSize"
+
+typedef struct{
+ int cbSize;
+ int nMaxPathSize; //maximum size of buffer. This represents the number of characters that can be copied to it (so for unicode strings you don't send the number of bytes but the length of the string).
+ union{
+ char *szPath; //pointer to the buffer that receives the path without the last "\\"
+ wchar_t *szPathW; //unicode version of the buffer.
+ TCHAR *szPathT;
+ };
+} FOLDERSGETDATA;
+
+/*Folders/Get/Path service
+ wParam - (WPARAM) (int) - handle to registered path
+ lParam - (LPARAM) (FOLDERSGETDATA *) pointer to a FOLDERSGETDATA that has all the relevant fields filled.
+ Should return 0 on success, or nonzero otherwise.
+*/
+#define MS_FOLDERS_GET_PATH "Folders/Get/Path"
+
+typedef struct{
+ int cbSize;
+ union{
+ char **szPath; //address of a string variable (char *) or (wchar_t*) where the path should be stored (the last \ won't be copied).
+ wchar_t **szPathW; //unicode version of string.
+ TCHAR **szPathT;
+ };
+} FOLDERSGETALLOCDATA;
+
+/*Folders/GetRelativePath/Alloc service
+ wParam - (WPARAM) (int) - Handle to registered path
+ lParam - (LPARAM) (FOLDERSALLOCDATA *) data
+ This service is the same as MS_FOLDERS_GET_PATH with the difference that this service
+ allocates the needed space for the buffer. It uses miranda's memory functions for that and you need
+ to use those to free the resulting buffer.
+ Should return 0 on success, or nonzero otherwise. Currently it only returns 0.
+*/
+#define MS_FOLDERS_GET_PATH_ALLOC "Folders/Get/Path/Alloc"
+
+
+/*Folders/On/Path/Changed
+ wParam - (WPARAM) 0
+ lParam - (LPARAM) 0
+ Triggered when the folders change, you should reget the paths you registered.
+*/
+#define ME_FOLDERS_PATH_CHANGED "Folders/On/Path/Changed"
+
+#ifndef FOLDERS_NO_HELPER_FUNCTIONS
+//#include "../../../include/newpluginapi.h"
+
+__inline static int FoldersRegisterCustomPath(const char *section, const char *name, const char *defaultPath)
+{
+ FOLDERSDATA fd = {0};
+ if (!ServiceExists(MS_FOLDERS_REGISTER_PATH)) return 1;
+ fd.cbSize = sizeof(FOLDERSDATA);
+ strncpy(fd.szSection, section, FOLDERS_NAME_MAX_SIZE);
+ fd.szSection[FOLDERS_NAME_MAX_SIZE - 1] = '\0';
+ strncpy(fd.szName, name, FOLDERS_NAME_MAX_SIZE);
+ fd.szName[FOLDERS_NAME_MAX_SIZE - 1] = '\0';
+ fd.szFormat = defaultPath;
+ return CallService(MS_FOLDERS_REGISTER_PATH, 0, (LPARAM) &fd);
+}
+
+__inline static int FoldersRegisterCustomPathW(const char *section, const char *name, const wchar_t *defaultPathW)
+{
+ FOLDERSDATA fd = {0};
+ if (!ServiceExists(MS_FOLDERS_REGISTER_PATH)) return 1;
+ fd.cbSize = sizeof(FOLDERSDATA);
+ strncpy(fd.szSection, section, FOLDERS_NAME_MAX_SIZE);
+ fd.szSection[FOLDERS_NAME_MAX_SIZE - 1] = '\0'; //make sure it's NULL terminated
+ strncpy(fd.szName, name, FOLDERS_NAME_MAX_SIZE);
+ fd.szName[FOLDERS_NAME_MAX_SIZE - 1] = '\0'; //make sure it's NULL terminated
+ fd.szFormatW = defaultPathW;
+ fd.flags = FF_UNICODE;
+ return CallService(MS_FOLDERS_REGISTER_PATH, 0, (LPARAM) &fd);
+}
+
+__inline static int FoldersGetCustomPath(HANDLE hFolderEntry, char *path, const int size, char *notFound)
+{
+ FOLDERSGETDATA fgd = {0};
+ fgd.cbSize = sizeof(FOLDERSGETDATA);
+ fgd.nMaxPathSize = size;
+ fgd.szPath = path;
+ int res = CallService(MS_FOLDERS_GET_PATH, (WPARAM) hFolderEntry, (LPARAM) &fgd);
+ if (res)
+ {
+ strncpy(path, notFound, size);
+ path[size - 1] = '\0'; //make sure it's NULL terminated
+ }
+ return res;
+}
+
+__inline static int FoldersGetCustomPathW(HANDLE hFolderEntry, wchar_t *pathW, const int count, wchar_t *notFoundW)
+{
+ FOLDERSGETDATA fgd = {0};
+ fgd.cbSize = sizeof(FOLDERSGETDATA);
+ fgd.nMaxPathSize = count;
+ fgd.szPathW = pathW;
+ int res = CallService(MS_FOLDERS_GET_PATH, (WPARAM) hFolderEntry, (LPARAM) &fgd);
+ if (res)
+ {
+ wcsncpy(pathW, notFoundW, count);
+ pathW[count - 1] = '\0';
+ }
+ return res;
+}
+
+# ifdef _UNICODE
+# define FoldersGetCustomPathT FoldersGetCustomPathW
+# define FoldersRegisterCustomPathT FoldersRegisterCustomPathW
+#else
+# define FoldersGetCustomPathT FoldersGetCustomPath
+# define FoldersRegisterCustomPathT FoldersRegisterCustomPath
+#endif
+
+#endif
+
+#endif //M_CUSTOM_FOLDERS_H \ No newline at end of file
diff --git a/Plugins/eSpeak/sdk/m_metacontacts.h b/Plugins/eSpeak/sdk/m_metacontacts.h
new file mode 100644
index 0000000..1da12b9
--- /dev/null
+++ b/Plugins/eSpeak/sdk/m_metacontacts.h
@@ -0,0 +1,162 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright © 2004 Universite Louis PASTEUR, STRASBOURG.
+Copyright © 2004 Scott Ellis (www.scottellis.com.au mail@scottellis.com.au)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef M_METACONTACTS_H__
+#define M_METACONTACTS_H__ 1
+
+//get the handle for a contact's parent metacontact
+//wParam=(HANDLE)hSubContact
+//lParam=0
+//returns a handle to the parent metacontact, or null if this contact is not a subcontact
+#define MS_MC_GETMETACONTACT "MetaContacts/GetMeta"
+
+//gets the handle for the default contact
+//wParam=(HANDLE)hMetaContact
+//lParam=0
+//returns a handle to the default contact, or null on failure
+#define MS_MC_GETDEFAULTCONTACT "MetaContacts/GetDefault"
+
+//gets the contact number for the default contact
+//wParam=(HANDLE)hMetaContact
+//lParam=0
+//returns a DWORD contact number, or -1 on failure
+#define MS_MC_GETDEFAULTCONTACTNUM "MetaContacts/GetDefaultNum"
+
+//gets the handle for the 'most online' contact
+//wParam=(HANDLE)hMetaContact
+//lParam=0
+//returns a handle to the 'most online' contact
+#define MS_MC_GETMOSTONLINECONTACT "MetaContacts/GetMostOnline"
+
+//gets the number of subcontacts for a metacontact
+//wParam=(HANDLE)hMetaContact
+//lParam=0
+//returns a DWORD representing the number of subcontacts for the given metacontact
+#define MS_MC_GETNUMCONTACTS "MetaContacts/GetNumContacts"
+
+//gets the handle of a subcontact, using the subcontact's number
+//wParam=(HANDLE)hMetaContact
+//lParam=(DWORD)contact number
+//returns a handle to the specified subcontact
+#define MS_MC_GETSUBCONTACT "MetaContacts/GetSubContact"
+
+//sets the default contact, using the subcontact's contact number
+//wParam=(HANDLE)hMetaContact
+//lParam=(DWORD)contact number
+//returns 0 on success
+#define MS_MC_SETDEFAULTCONTACTNUM "MetaContacts/SetDefault"
+
+//sets the default contact, using the subcontact's handle
+//wParam=(HANDLE)hMetaContact
+//lParam=(HANDLE)hSubcontact
+//returns 0 on success
+#define MS_MC_SETDEFAULTCONTACT "MetaContacts/SetDefaultByHandle"
+
+//forces the metacontact to send using a specific subcontact, using the subcontact's contact number
+//wParam=(HANDLE)hMetaContact
+//lParam=(DWORD)contact number
+//returns 0 on success
+#define MS_MC_FORCESENDCONTACTNUM "MetaContacts/ForceSendContact"
+
+//forces the metacontact to send using a specific subcontact, using the subcontact's handle
+//wParam=(HANDLE)hMetaContact
+//lParam=(HANDLE)hSubcontact
+//returns 0 on success (will fail if 'force default' is in effect)
+#define MS_MC_FORCESENDCONTACT "MetaContacts/ForceSendContactByHandle"
+
+//'unforces' the metacontact to send using a specific subcontact
+//wParam=(HANDLE)hMetaContact
+//lParam=0
+//returns 0 on success (will fail if 'force default' is in effect)
+#define MS_MC_UNFORCESENDCONTACT "MetaContacts/UnforceSendContact"
+
+//'forces' or 'unforces' (i.e. toggles) the metacontact to send using it's default contact
+// overrides (and clears) 'force send' above, and will even force use of offline contacts
+// will send ME_MC_FORCESEND or ME_MC_UNFORCESEND event
+//wParam=(HANDLE)hMetaContact
+//lParam=0
+//returns 1(true) or 0(false) representing new state of 'force default'
+#define MS_MC_FORCEDEFAULT "MetaContacts/ForceSendDefault"
+
+// method to get state of 'force' for a metacontact
+// wParam=(HANDLE)hMetaContact
+// lParam= (DWORD)&contact_number or NULL
+//
+// if lparam supplied, the contact_number of the contatct 'in force' will be copied to the address it points to,
+// or if none is in force, the value (DWORD)-1 will be copied
+// (v0.8.0.8+ returns 1 if 'force default' is true with *lParam == default contact number, else returns 0 with *lParam as above)
+#define MS_MC_GETFORCESTATE "MetaContacts/GetForceState"
+
+// fired when a metacontact's default contact changes (fired upon creation of metacontact also, when default is initially set)
+// wParam=(HANDLE)hMetaContact
+// lParam=(HANDLE)hDefaultContact
+#define ME_MC_DEFAULTTCHANGED "MetaContacts/DefaultChanged"
+
+// fired when a metacontact's subcontacts change (fired upon creation of metacontact, when contacts are added or removed, and when
+// contacts are reordered) - a signal to re-read metacontact data
+// wParam=(HANDLE)hMetaContact
+// lParam=0
+#define ME_MC_SUBCONTACTSCHANGED "MetaContacts/SubcontactsChanged"
+
+// fired when a metacontact is forced to send using a specific subcontact
+// wParam=(HANDLE)hMetaContact
+// lParam=(HANDLE)hForceContact
+#define ME_MC_FORCESEND "MetaContacts/ForceSend"
+
+// fired when a metacontact is 'unforced' to send using a specific subcontact
+// wParam=(HANDLE)hMetaContact
+// lParam=0
+#define ME_MC_UNFORCESEND "MetaContacts/UnforceSend"
+
+// method to get protocol name - used to be sure you're dealing with a "real" metacontacts plugin :)
+// wParam=lParam=0
+#define MS_MC_GETPROTOCOLNAME "MetaContacts/GetProtoName"
+
+
+// added 0.9.5.0 (22/3/05)
+// wParam=(HANDLE)hContact
+// lParam=0
+// convert a given contact into a metacontact
+#define MS_MC_CONVERTTOMETA "MetaContacts/ConvertToMetacontact"
+
+// added 0.9.5.0 (22/3/05)
+// wParam=(HANDLE)hContact
+// lParam=(HANDLE)hMeta
+// add an existing contact to a metacontact
+#define MS_MC_ADDTOMETA "MetaContacts/AddToMetacontact"
+
+// added 0.9.5.0 (22/3/05)
+// wParam=0
+// lParam=(HANDLE)hContact
+// remove a contact from a metacontact
+#define MS_MC_REMOVEFROMMETA "MetaContacts/RemoveFromMetacontact"
+
+
+// added 0.9.13.2 (6/10/05)
+// wParam=(BOOL)disable
+// lParam=0
+// enable/disable the 'hidden group hack' - for clists that support subcontact hiding using 'IsSubcontact' setting
+// should be called once in the clist 'onmodulesloaded' event handler (which, since it's loaded after the db, will be called
+// before the metacontact onmodulesloaded handler where the subcontact hiding is usually done)
+#define MS_MC_DISABLEHIDDENGROUP "MetaContacts/DisableHiddenGroup"
+
+#endif
diff --git a/Plugins/eSpeak/sdk/m_updater.h b/Plugins/eSpeak/sdk/m_updater.h
new file mode 100644
index 0000000..371b743
--- /dev/null
+++ b/Plugins/eSpeak/sdk/m_updater.h
@@ -0,0 +1,146 @@
+#ifndef _M_UPDATER_H
+#define _M_UPDATER_H
+
+// NOTES:
+// - For langpack updates, include a string of the following format in the langpack text file:
+// ";FLID: <file listing name> <version>"
+// version must be four numbers seperated by '.', in the range 0-255 inclusive
+// - Updater will disable plugins that are downloaded but were not active prior to the update (this is so that, if an archive contains e.g. ansi and
+// unicode versions, the correct plugin will be the only one active after the new version is installed)...so if you add a support plugin, you may need
+// to install an ini file to make the plugin activate when miranda restarts after the update
+// - Updater will replace all dlls that have the same internal shortName as a downloaded update dll (this is so that msn1.dll and msn2.dll, for example,
+// will both be updated) - so if you have a unicode and a non-unicode version of a plugin in your archive, you should make the internal names different (which will break automatic
+// updates from the file listing if there is only one file listing entry for both versions, unless you use the 'MS_UPDATE_REGISTER' service below)
+// - Updater will install all files in the root of the archive into the plugins folder, except for langpack files that contain the FLID string which go into the root folder (same
+// folder as miranda32.exe)...all folders in the archive will also be copied to miranda's root folder, and their contents transferred into the new folders. The only exception is a
+// special folder called 'root_files' - if there is a folder by that name in the archive, it's contents will also be copied into miranda's root folder - this is intended to be used
+// to install additional dlls etc that a plugin may require)
+
+// if you set Update.szUpdateURL to the following value when registering, as well as setting your beta site and version data,
+// Updater will ignore szVersionURL and pbVersionPrefix, and attempt to find the file listing URL's from the backend XML data.
+// for this to work, the plugin name in pluginInfo.shortName must match the file listing exactly (except for case)
+#define UPDATER_AUTOREGISTER "UpdaterAUTOREGISTER"
+// Updater will also use the backend xml data if you provide URL's that reference the miranda file listing for updates (so you can use that method
+// if e.g. your plugin shortName does not match the file listing) - it will grab the file listing id from the end of these URLs
+
+typedef struct Update_tag {
+ int cbSize;
+ char *szComponentName; // component name as it will appear in the UI (will be translated before displaying)
+
+ char *szVersionURL; // URL where the current version can be found (NULL to disable)
+ BYTE *pbVersionPrefix; // bytes occuring in VersionURL before the version, used to locate the version information within the URL data
+ // (note that this URL could point at a binary file - dunno why, but it could :)
+ int cpbVersionPrefix; // number of bytes pointed to by pbVersionPrefix
+ char *szUpdateURL; // URL where dll/zip is located
+ // set to UPDATER_AUTOREGISTER if you want Updater to find the file listing URLs (ensure plugin shortName matches file listing!)
+
+ char *szBetaVersionURL; // URL where the beta version can be found (NULL to disable betas)
+ BYTE *pbBetaVersionPrefix; // bytes occuring in VersionURL before the version, used to locate the version information within the URL data
+ int cpbBetaVersionPrefix; // number of bytes pointed to by pbVersionPrefix
+ char *szBetaUpdateURL; // URL where dll/zip is located
+
+ BYTE *pbVersion; // bytes of current version, used for comparison with those in VersionURL
+ int cpbVersion; // number of bytes pointed to by pbVersion
+
+ char *szBetaChangelogURL; // url for displaying changelog for beta versions
+} Update;
+
+// register a comonent with Updater
+//
+// wparam = 0
+// lparam = (LPARAM)&Update
+#define MS_UPDATE_REGISTER "Update/Register"
+
+// utility functions to create a version string from a DWORD or from pluginInfo
+// point buf at a buffer at least 16 chars wide - but note the version string returned may be shorter
+//
+__inline static char *CreateVersionString(DWORD version, char *buf) {
+ mir_snprintf(buf, 16, "%d.%d.%d.%d", (version >> 24) & 0xFF, (version >> 16) & 0xFF, (version >> 8) & 0xFF, version & 0xFF);
+ return buf;
+}
+
+__inline static char *CreateVersionStringPlugin(PLUGININFO *pluginInfo, char *buf) {
+ return CreateVersionString(pluginInfo->version, buf);
+}
+
+
+// register the 'easy' way - use this method if you have no beta URL and the plugin is on the miranda file listing
+// NOTE: the plugin version string on the file listing must be the string version of the version in pluginInfo (i.e. 0.0.0.1,
+// four numbers between 0 and 255 inclusivem, so no letters, brackets, etc.)
+//
+// wParam = (int)fileID - this is the file ID from the file listing (i.e. the number at the end of the download link)
+// lParam = (PLUGININFO*)&pluginInfo
+#define MS_UPDATE_REGISTERFL "Update/RegisterFL"
+
+// this function can be used to 'unregister' components - useful for plugins that register non-plugin/langpack components and
+// may need to change those components on the fly
+// lParam = (char *)szComponentName
+#define MS_UPDATE_UNREGISTER "Update/Unregister"
+
+// this event is fired when the startup process is complete, but NOT if a restart is imminent
+// it is designed for status managment plugins to use as a trigger for beggining their own startup process
+// wParam = lParam = 0 (unused)
+// (added in version 0.1.6.0)
+#define ME_UPDATE_STARTUPDONE "Update/StartupDone"
+
+// this service can be used to enable/disable Updater's global status control
+// it can be called from the StartupDone event handler
+// wParam = (BOOL)enable
+// lParam = 0
+// (added in version 0.1.6.0)
+#define MS_UPDATE_ENABLESTATUSCONTROL "Update/EnableStatusControl"
+
+// An description of usage of the above service and event:
+// Say you are a status control plugin that normally sets protocol or global statuses in your ModulesLoaded event handler.
+// In order to make yourself 'Updater compatible', you would move the status control code from ModulesLoaded to another function,
+// say DoStartup. Then, in ModulesLoaded you would check for the existence of the MS_UPDATE_ENABLESTATUSCONTROL service.
+// If it does not exist, call DoStartup. If it does exist, hook the ME_UPDATE_STARTUPDONE event and call DoStartup from there. You may
+// also wish to call MS_UPDATE_ENABLESTATUSCONTROL with wParam == FALSE at this time, to disable Updater's own status control feature.
+
+// this service can be used to determine whether updates are possible for a component with the given name
+// wParam = 0
+// lParam = (char *)szComponentName
+// returns TRUE if updates are supported, FALSE otherwise
+#define MS_UPDATE_ISUPDATESUPPORTED "Update/IsUpdateSupported"
+
+#endif
+
+
+/////////////// Usage Example ///////////////
+
+#ifdef EXAMPLE_CODE
+
+// you need to #include "m_updater.h" and HookEvent(ME_SYSTEM_MODULESLOADED, OnModulesLoaded) in your Load function...
+
+int OnModulesLoaded(WPARAM wParam, LPARAM lParam) {
+
+ Update update = {0}; // for c you'd use memset or ZeroMemory...
+ char szVersion[16];
+
+ update.cbSize = sizeof(Update);
+
+ update.szComponentName = pluginInfo.shortName;
+ update.pbVersion = (BYTE *)CreateVersionString(&pluginInfo, szVersion);
+ update.cpbVersion = strlen((char *)update.pbVersion);
+
+ // these are the three lines that matter - the archive, the page containing the version string, and the text (or data)
+ // before the version that we use to locate it on the page
+ // (note that if the update URL and the version URL point to standard file listing entries, the backend xml
+ // data will be used to check for updates rather than the actual web page - this is not true for beta urls)
+ update.szUpdateURL = "http://scottellis.com.au:81/test/updater.zip";
+ update.szVersionURL = "http://scottellis.com.au:81/test/updater_test.html";
+ update.pbVersionPrefix = (BYTE *)"Updater version ";
+
+ update.cpbVersionPrefix = strlen((char *)update.pbVersionPrefix);
+
+ // do the same for the beta versions of the above struct members if you wish to allow beta updates from another URL
+
+ CallService(MS_UPDATE_REGISTER, 0, (WPARAM)&update);
+
+ // Alternatively, to register a plugin with e.g. file ID 2254 on the file listing...
+ // CallService(MS_UPDATE_REGISTERFL, (WPARAM)2254, (LPARAM)&pluginInfo);
+
+ return 0;
+}
+
+#endif
diff --git a/Plugins/eSpeak/sdk/m_variables.h b/Plugins/eSpeak/sdk/m_variables.h
new file mode 100644
index 0000000..3f13c96
--- /dev/null
+++ b/Plugins/eSpeak/sdk/m_variables.h
@@ -0,0 +1,718 @@
+/*
+ Variables Plugin for Miranda-IM (www.miranda-im.org)
+ Copyright 2003-2006 P. Boon
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#ifndef __M_VARS
+#define __M_VARS
+
+#if !defined(_TCHAR_DEFINED)
+#include <tchar.h>
+#endif
+
+#ifndef VARIABLES_NOHELPER
+#include <m_button.h>
+#endif
+
+// --------------------------------------------------------------------------
+// Memory management
+// --------------------------------------------------------------------------
+
+// Release memory that was allocated by the Variables plugin, e.g. returned
+// strings.
+
+#define MS_VARS_FREEMEMORY "Vars/FreeMemory"
+
+// Parameters:
+// ------------------------
+// wParam = (WPARAM)(void *)pntr
+// Pointer to memory that was allocated by the Variables plugin (e.g. a
+// returned string) (can be NULL).
+// lParam = 0
+
+// Return Value:
+// ------------------------
+// Does return 0 on success, nozero otherwise.
+
+// Note: Do only use this service to free memory that was *explicitliy*
+// stated that it should be free with this service.
+
+
+
+#define MS_VARS_GET_MMI "Vars/GetMMI"
+
+// Get Variable's RTL/CRT function poiners to malloc(), free() and
+// realloc().
+
+// Parameters:
+// ------------------------
+// wParam = 0
+// lParam = (LPARAM) &MM_INTERFACE
+// Pointer to a memory manager interface struct (see m_system.h).
+
+// Return Value:
+// ------------------------
+// Returns 0 on success, nozero otherwise
+
+// Note: Works exactly the same as the MS_SYSTEM_GET_MMI service
+// service of m_system.h.
+
+// Helper function for easy using:
+#ifndef VARIABLES_NOHELPER
+__inline static void variables_free(void *pntr) {
+
+ CallService(MS_VARS_FREEMEMORY, (WPARAM)pntr, 0);
+}
+#endif
+
+
+
+// --------------------------------------------------------------------------
+// String formatting
+// --------------------------------------------------------------------------
+
+#define MS_VARS_FORMATSTRING "Vars/FormatString"
+
+// This service can be used to parse tokens in a text. The tokens will be
+// replaced by their resolved values. A token can either be a field or a
+// function. A field takes no arguments and is represented between
+// %-characters, e.g. "%winampsong%". A function can take any number of
+// arguments and is represented by a ? or !-character followed by the name
+// of the function and a list of arguments, e.g. "?add(1,2)".
+
+// Parameters:
+// ------------------------
+// wParam = (WPARAM)(FORMATINFO *)&fi
+// See below.
+// lParam = 0
+
+// Return Value:
+// ------------------------
+// Returns a pointer to the resolved string or NULL in case of an error.
+
+// Note: The returned pointer needs to be freed using MS_VARS_FREEMEMORY.
+
+typedef struct {
+ int cbSize; // Set this to sizeof(FORMATINFO).
+ int flags; // Flags to use (see FIF_* below).
+ union {
+ char *szFormat; // Text in which the tokens will be replaced (can't be
+ // NULL).
+ WCHAR *wszFormat;
+ TCHAR *tszFormat;
+ };
+ union {
+ char *szExtraText; // Extra, context-specific string (can be NULL) ->
+ // The field "extratext" will be replaced by this
+ // string. (Previously szSource).
+ WCHAR *wszExtraText;
+ TCHAR *tszExtraText;
+ };
+ HANDLE hContact; // Handle to contact (can be NULL) -> The field "subject"
+ // represents this contact.
+ int pCount; // (output) Number of succesful parsed tokens, needs to be set
+ // to 0 before the call
+ int eCount; // (output) Number of failed tokens, needs to be set to 0
+ // before the call
+ union {
+ char **szaTemporaryVars; // Temporary variables valid only in the duration of the format call
+ TCHAR **tszaTemporaryVars; // By pos: [i] is var name, [i + 1] is var value
+ WCHAR **wszaTemporaryVars;
+ };
+ int cbTemporaryVarsSize; // Number of elements in szaTemporaryVars array
+
+} FORMATINFO;
+
+#define FORMATINFOV2_SIZE 28
+
+// Possible flags:
+#define FIF_UNICODE 0x01 // Expects and returns unicode text (WCHAR*).
+
+#if defined(UNICODE) || defined(_UNICODE)
+#define FIF_TCHAR FIF_UNICODE // Strings in structure are TCHAR*.
+#else
+#define FIF_TCHAR 0
+#endif
+
+// Helper functions for easy using:
+
+// Helper #1: variables_parse
+// ------------------------
+// The returned string needs to be freed using MS_VARS_FREEMEMORY.
+
+#ifndef VARIABLES_NOHELPER
+__inline static TCHAR *variables_parse(TCHAR *tszFormat, TCHAR *tszExtraText, HANDLE hContact) {
+
+ FORMATINFO fi;
+
+ ZeroMemory(&fi, sizeof(fi));
+ fi.cbSize = sizeof(fi);
+ fi.tszFormat = tszFormat;
+ fi.tszExtraText = tszExtraText;
+ fi.hContact = hContact;
+ fi.flags = FIF_TCHAR;
+
+ return (TCHAR *)CallService(MS_VARS_FORMATSTRING, (WPARAM)&fi, 0);
+}
+#endif
+
+__inline static TCHAR *variables_parse_ex(TCHAR *tszFormat, TCHAR *tszExtraText, HANDLE hContact,
+ TCHAR **tszaTemporaryVars, int cbTemporaryVarsSize) {
+
+ FORMATINFO fi;
+
+ ZeroMemory(&fi, sizeof(fi));
+ fi.cbSize = sizeof(fi);
+ fi.tszFormat = tszFormat;
+ fi.tszExtraText = tszExtraText;
+ fi.hContact = hContact;
+ fi.flags = FIF_TCHAR;
+ fi.tszaTemporaryVars = tszaTemporaryVars;
+ fi.cbTemporaryVarsSize = cbTemporaryVarsSize;
+
+ return (TCHAR *)CallService(MS_VARS_FORMATSTRING, (WPARAM)&fi, 0);
+}
+// Helper #2: variables_parsedup
+// ------------------------
+// Returns a _strdup()'ed copy of the unparsed string when Variables is not
+// installed, returns a strdup()'ed copy of the parsed result otherwise.
+
+// Note: The returned pointer needs to be released using your own free().
+
+#ifndef VARIABLES_NOHELPER
+__inline static TCHAR *variables_parsedup(TCHAR *tszFormat, TCHAR *tszExtraText, HANDLE hContact) {
+
+ if (ServiceExists(MS_VARS_FORMATSTRING)) {
+ FORMATINFO fi;
+ TCHAR *tszParsed, *tszResult;
+
+ ZeroMemory(&fi, sizeof(fi));
+ fi.cbSize = sizeof(fi);
+ fi.tszFormat = tszFormat;
+ fi.tszExtraText = tszExtraText;
+ fi.hContact = hContact;
+ fi.flags |= FIF_TCHAR;
+ tszParsed = (TCHAR *)CallService(MS_VARS_FORMATSTRING, (WPARAM)&fi, 0);
+ if (tszParsed) {
+ tszResult = _tcsdup(tszParsed);
+ CallService(MS_VARS_FREEMEMORY, (WPARAM)tszParsed, 0);
+ return tszResult;
+ }
+ }
+ return tszFormat?_tcsdup(tszFormat):tszFormat;
+}
+
+__inline static TCHAR *variables_parsedup_ex(TCHAR *tszFormat, TCHAR *tszExtraText, HANDLE hContact,
+ TCHAR **tszaTemporaryVars, int cbTemporaryVarsSize) {
+
+ if (ServiceExists(MS_VARS_FORMATSTRING)) {
+ FORMATINFO fi;
+ TCHAR *tszParsed, *tszResult;
+
+ ZeroMemory(&fi, sizeof(fi));
+ fi.cbSize = sizeof(fi);
+ fi.tszFormat = tszFormat;
+ fi.tszExtraText = tszExtraText;
+ fi.hContact = hContact;
+ fi.flags |= FIF_TCHAR;
+ fi.tszaTemporaryVars = tszaTemporaryVars;
+ fi.cbTemporaryVarsSize = cbTemporaryVarsSize;
+ tszParsed = (TCHAR *)CallService(MS_VARS_FORMATSTRING, (WPARAM)&fi, 0);
+ if (tszParsed) {
+ tszResult = _tcsdup(tszParsed);
+ CallService(MS_VARS_FREEMEMORY, (WPARAM)tszParsed, 0);
+ return tszResult;
+ }
+ }
+ return tszFormat?_tcsdup(tszFormat):tszFormat;
+}
+#endif
+
+
+
+// --------------------------------------------------------------------------
+// Register tokens
+// --------------------------------------------------------------------------
+
+// Plugins can define tokens which will be parsed by the Variables plugin.
+
+#define MS_VARS_REGISTERTOKEN "Vars/RegisterToken"
+
+// With this service you can define your own token. The newly added tokens
+// using this service are taken into account on every call to
+// MS_VARS_FORMATSTRING.
+
+// Parameters:
+// ------------------------
+// wParam = 0
+// lParam = (LPARAM)(TOKENREGISTER*)&tr
+// See below.
+
+// Return Value:
+// ------------------------
+// Returns 0 on success, nonzero otherwise. Existing tokens will be
+// 'overwritten' if registered twice.
+
+// Needed for szService and parseFunction:
+typedef struct {
+ int cbSize; // You need to check if this is >=sizeof(ARGUMENTSINFO)
+ // (already filled in).
+ FORMATINFO *fi; // Arguments passed to MS_VARS_FORMATSTRING.
+ unsigned int argc; // Number of elements in the argv array.
+ union {
+ char **argv; // Argv[0] will be the token name, the following elements
+ // are the additional arguments.
+ WCHAR **wargv; // If the registered token was registered as a unicode
+ // token, wargv should be accessed.
+ TCHAR **targv;
+ };
+ int flags; // (output) You can set flags here (initially 0), use the
+ // AIF_* flags (see below).
+} ARGUMENTSINFO;
+
+// Available flags for ARGUMENTSINFO:
+// Set the flags of the ARGUMENTSINFO struct to any of these to influence
+// further parsing.
+#define AIF_DONTPARSE 0x01 // Don't parse the result of this function,
+ // usually the result of a token is parsed
+ // again, if the `?` is used as a function
+ // character.
+#define AIF_FALSE 0x02 // The function returned logical false.
+
+// Definition of parse/cleanup functions:
+typedef char* (*VARPARSEFUNCA)(ARGUMENTSINFO *ai);
+typedef WCHAR* (*VARPARSEFUNCW)(ARGUMENTSINFO *ai);
+typedef void (*VARCLEANUPFUNCA)(char *szReturn);
+typedef void (*VARCLEANUPFUNCW)(WCHAR *wszReturn);
+
+#if defined(UNICODE) || defined(_UNICODE)
+#define VARPARSEFUNC VARPARSEFUNCW
+#define VARCLEANUPFUNC VARCLEANUPFUNCW
+#else
+#define VARPARSEFUNC VARPARSEFUNCA
+#define VARCLEANUPFUNC VARCLEANUPFUNCA
+#endif
+
+typedef struct {
+ int cbSize; // Set this to sizeof(TOKENREGISTER).
+ union {
+ char *szTokenString; // Name of the new token to be created, without %,
+ // ?, ! etc. signs (can't be NULL).
+ WCHAR *wszTokenString;
+ TCHAR *tszTokenString;
+ };
+ union {
+ char *szService; // Name of a service that is used to request the
+ // token's value, if no service is used, a function
+ // and TRF_PARSEFUNC must be used.
+ VARPARSEFUNCA parseFunction; // See above, use with TRF_PARSEFUNC.
+ VARPARSEFUNCW parseFunctionW;
+ VARPARSEFUNC parseFunctionT;
+ };
+ union {
+ char *szCleanupService; // Name of a service to be called when the
+ // memory allocated in szService can be freed
+ // (only used when flag VRF_CLEANUP is set,
+ // else set this to NULL).
+ VARCLEANUPFUNCA cleanupFunction; // See above, use with TRF_CLEANUPFUNC.
+ VARCLEANUPFUNCW cleanupFunctionW;
+ VARCLEANUPFUNC cleanupFunctionT;
+ };
+ char *szHelpText; // Help info shown in help dialog (can be NULL). Has to
+ // be in the following format:
+ // "subject\targuments\tdescription"
+ // (Example: "math\t(x, y ,...)\tx + y + ..."), or:
+ // "subject\tdescription"
+ // (Example: "miranda\tPath to the Miranda-IM
+ // executable").
+ // Note: subject and description are translated by
+ // Variables.
+ int memType; // Describes which method Varibale's plugin needs to use to
+ // free the returned buffer, use one of the VR_MEM_* values
+ // (see below). Only valid if the flag VRF_FREEMEM is set,
+ // use TR_MEM_OWNER otherwise).
+ int flags; // Flags to use (see below), one of TRF_* (see below).
+} TOKENREGISTER;
+
+// Available Memory Storage Types:
+// These values describe which method Variables Plugin will use to free the
+// buffer returned by the parse function or service
+#define TR_MEM_VARIABLES 1 // Memory is allocated using the functions
+ // retrieved by MS_VARS_GET_MMI.
+#define TR_MEM_MIRANDA 2 // Memory is allocated using Miranda's Memory
+ // Manager Interface (using the functions
+ // returned by MS_SYSTEM_GET_MMI), if
+ // VRF_FREEMEM is set, the memory will be
+ // freed by Variables.
+#define TR_MEM_OWNER 3 // Memory is owned by the calling plugin
+ // (can't be freed by Variables Plugin
+ // automatically). This should be used if
+ // VRF_FREEMEM is not specified in the flags.
+
+// Available Flags for TOKENREGISTER:
+#define TRF_FREEMEM 0x01 // Variables Plugin will automatically free the
+ // pointer returned by the parse function or
+ // service (which method it will us is
+ // specified in memType -> see above).
+#define TRF_CLEANUP 0x02 // Call cleanup service or function, notifying
+ // that the returned buffer can be freed.
+ // Normally you should use either TRF_FREEMEM
+ // or TRF_CLEANUP.
+#define TRF_PARSEFUNC 0x40 // parseFunction will be used instead of a
+ // service.
+#define TRF_CLEANUPFUNC 0x80 // cleanupFunction will be used instead of a
+ // service.
+#define TRF_USEFUNCS TRF_PARSEFUNC|TRF_CLEANUPFUNC
+#define TRF_UNPARSEDARGS 0x04 // Provide the arguments for the parse
+ // function in their raw (unparsed) form.
+ // By default, arguments are parsed before
+ // presenting them to the parse function.
+#define TRF_FIELD 0x08 // The token can be used as a %field%.
+#define TRF_FUNCTION 0x10 // The token can be used as a ?function().
+ // Normally you should use either TRF_FIELD or
+ // TRF_FUNCTION.
+#define TRF_UNICODE 0x20 // Strings in structure are unicode (WCHAR*).
+ // In this case, the strings pointing to the
+ // arguments in the ARGUMENTS struct are
+ // unicode also. The returned buffer is
+ // expected to be unicode also, and the
+ // unicode parse and cleanup functions are
+ // called.
+
+#if defined(UNICODE) || defined(_UNICODE)
+#define TRF_TCHAR TRF_UNICODE // Strings in structure are TCHAR*.
+#else
+#define TRF_TCHAR 0
+#endif
+
+// Deprecated:
+#define TRF_CALLSVC TRF_CLEANUP
+
+// Callback Service (szService) / parseFunction:
+// ------------------------
+// Service that is called automatically by the Variable's Plugin to resolve a
+// registered variable.
+
+// Parameters:
+// wParam = 0
+// lParam = (LPARAM)(ARGUMENTSINFO *)&ai
+// see above
+
+// Return Value:
+// Needs to return the pointer to a dynamically allocacated string or NULL.
+// A return value of NULL is regarded as an error (eCount will be increaded).
+// Flags in the ARGUMENTSINFO struct can be set (see above).
+
+// Callback Service (szCallbackService) / cleanupFunction:
+// ------------------------
+// This service is called when the memory that was allocated by the parse
+// function or service can be freed. Note: It will only be called when the
+// flag VRF_CLEANUP of TOKENREGISTER is set.
+
+// Parameters:
+// wParam = 0
+// lParam = (LPARAM)(char *)&res
+// Result from parse function or service (pointer to a string).
+
+// Return Value:
+// Should return 0 on success.
+
+
+
+// --------------------------------------------------------------------------
+// Show the help dialog
+// --------------------------------------------------------------------------
+
+// Plugins can invoke Variables' help dialog which can be used for easy input
+// by users.
+
+#define MS_VARS_SHOWHELPEX "Vars/ShowHelpEx"
+
+// This service can be used to open the help dialog of Variables. This dialog
+// provides easy input for the user and/or information about the available
+// tokens.
+
+// Parameters:
+// ------------------------
+// wParam = (WPARAM)(HWND)hwndParent
+// lParam = (LPARAM)(VARHELPINFO)&vhi
+// See below.
+
+// Return Value:
+// ------------------------
+// Returns 0 on succes, any other value on error.
+
+typedef struct {
+ int cbSize; // Set to sizeof(VARHELPINFO).
+ FORMATINFO *fi; // Used for both input and output. If this pointer is not
+ // NULL, the information is used as the initial values for
+ // the dialog.
+ HWND hwndCtrl; // Used for both input and output. The window text of this
+ // window will be read and used as the initial input of the
+ // input dialog. If the user presses the OK button the window
+ // text of this window will be set to the text of the input
+ // field and a EN_CHANGE message via WM_COMMAND is send to
+ // this window. (Can be NULL).
+ char *szSubjectDesc; // The description of the %subject% token will be set
+ // to this text, if not NULL. This is translated
+ // automatically.
+ char *szExtraTextDesc; // The description of the %extratext% token will be
+ // set to this text, if not NULL. This is translated
+ // automatically.
+ int flags; // Flags, see below.
+} VARHELPINFO;
+
+
+// Flags for VARHELPINFO
+#define VHF_TOKENS 0x00000001 // Create a dialog with the list of
+ // tokens
+#define VHF_INPUT 0x00000002 // Create a dialog with an input
+ // field (this contains the list of
+ // tokens as well).
+#define VHF_SUBJECT 0x00000004 // Create a dialog to select a
+ // contact for the %subject% token.
+#define VHF_EXTRATEXT 0x00000008 // Create a dialog to enter a text
+ // for the %extratext% token.
+#define VHF_HELP 0x00000010 // Create a dialog with help info.
+#define VHF_HIDESUBJECTTOKEN 0x00000020 // Hide the %subject% token in the
+ // list of tokens.
+#define VHF_HIDEEXTRATEXTTOKEN 0x00000040 // Hide the %extratext% token in
+ // the list of tokens.
+#define VHF_DONTFILLSTRUCT 0x00000080 // Don't fill the struct with the
+ // new information if OK is pressed
+#define VHF_FULLFILLSTRUCT 0x00000100 // Fill all members of the struct
+ // when OK is pressed. By default
+ // only szFormat is set. With this
+ // flag on, hContact and
+ // szExtraText are also set.
+#define VHF_SETLASTSUBJECT 0x00000200 // Set the last contact that was
+ // used in the %subject% dialog in
+ // case fi.hContact is NULL.
+
+// Predefined flags
+#define VHF_FULLDLG VHF_INPUT|VHF_SUBJECT|VHF_EXTRATEXT|VHF_HELP
+#define VHF_SIMPLEDLG VHF_INPUT|VHF_HELP
+#define VHF_NOINPUTDLG VHF_TOKENS|VHF_HELP
+
+// If the service fills information in the struct for szFormat or szExtraText,
+// these members must be free'd using the free function of Variables.
+// If wParam==NULL, the dialog is created modeless. Only one dialog can be
+// shown at the time.
+// If both hwndCtrl and fi are NULL, the user input will not be retrievable.
+// In this case, the dialog is created with only a "Close" button, instead of
+// the "OK" and "Cancel" buttons.
+// In case of modeless dialog and fi != NULL, please make sure this pointer
+// stays valid while the dialog is open.
+
+// Helper function for easy use in standard case:
+#ifndef VARIABLES_NOHELPER
+__inline static int variables_showhelp(HWND hwndDlg, UINT uIDEdit, int flags, char *szSubjectDesc, char *szExtraDesc) {
+
+ VARHELPINFO vhi;
+
+ ZeroMemory(&vhi, sizeof(VARHELPINFO));
+ vhi.cbSize = sizeof(VARHELPINFO);
+ if (flags == 0) {
+ flags = VHF_SIMPLEDLG;
+ }
+ vhi.flags = flags;
+ vhi.hwndCtrl = GetDlgItem(hwndDlg, uIDEdit);
+ vhi.szSubjectDesc = szSubjectDesc;
+ vhi.szExtraTextDesc = szExtraDesc;
+
+ return CallService(MS_VARS_SHOWHELPEX, (WPARAM)hwndDlg, (LPARAM)&vhi);
+}
+#endif
+
+
+#define MS_VARS_GETSKINITEM "Vars/GetSkinItem"
+
+// This service can be used to get the icon you can use for example on the
+// Variables help button in your options screen. You can also get the tooltip
+// text to use with such a button. If icon library is available the icon will
+// be retrieved from icon library manager, otherwise the default is returned.
+
+// Parameters:
+// ------------------------
+// wParam = (WPARAM)0
+// lParam = (LPARAM)VSI_* (see below)
+
+// Return Value:
+// ------------------------
+// Depends on the information to retrieve (see below).
+
+// VSI_ constants
+#define VSI_HELPICON 1 // Can be used on the button accessing the
+ // Variables help dialog. Returns (HICON)hIcon on
+ // success or NULL on failure;
+#define VSI_HELPTIPTEXT 2 // Returns the tooltip text you can use for the
+ // help button. Returns (char *)szTipText, a
+ // static, translated buffer containing the help
+ // text or NULL on error.
+
+// Helper to set the icon on a button accessing the help dialog.
+// Preferably a 16x14 MButtonClass control, but it works on a standard
+// button control as well. If no icon is availble (because of old version of
+// Variables) the string "V" is shown on the button. If Variables is not
+// available, the button will be hidden.
+#ifndef VARIABLES_NOHELPER
+__inline static int variables_skin_helpbutton(HWND hwndDlg, UINT uIDButton) {
+
+ int res;
+ HICON hIcon;
+ TCHAR tszClass[32];
+
+ hIcon = NULL;
+ res = 0;
+ if (ServiceExists(MS_VARS_GETSKINITEM)) {
+ hIcon = (HICON)CallService(MS_VARS_GETSKINITEM, 0, (LPARAM)VSI_HELPICON);
+ }
+ GetClassName(GetDlgItem(hwndDlg, uIDButton), tszClass, sizeof(tszClass));
+ if (!_tcscmp(tszClass, _T("Button"))) {
+ if (hIcon != NULL) {
+ SetWindowLong(GetDlgItem(hwndDlg, uIDButton), GWL_STYLE, GetWindowLong(GetDlgItem(hwndDlg, uIDButton), GWL_STYLE)|BS_ICON);
+ SendMessage(GetDlgItem(hwndDlg, uIDButton), BM_SETIMAGE, (WPARAM)IMAGE_ICON, (LPARAM)hIcon);
+ }
+ else {
+ SetWindowLong(GetDlgItem(hwndDlg, uIDButton), GWL_STYLE, GetWindowLong(GetDlgItem(hwndDlg, uIDButton), GWL_STYLE)&~BS_ICON);
+ SetDlgItemText(hwndDlg, uIDButton, _T("V"));
+ }
+ }
+ else if (!_tcscmp(tszClass, MIRANDABUTTONCLASS)) {
+ if (hIcon != NULL) {
+ char *szTipInfo;
+
+ SendMessage(GetDlgItem(hwndDlg, uIDButton), BM_SETIMAGE, (WPARAM)IMAGE_ICON, (LPARAM)hIcon);
+ if (ServiceExists(MS_VARS_GETSKINITEM)) {
+ szTipInfo = (char *)CallService(MS_VARS_GETSKINITEM, 0, (LPARAM)VSI_HELPTIPTEXT);
+ }
+ if (szTipInfo == NULL) {
+ szTipInfo = Translate("Open String Formatting Help");
+ }
+ SendMessage(GetDlgItem(hwndDlg, uIDButton), BUTTONADDTOOLTIP, (WPARAM)szTipInfo, 0);
+ SendDlgItemMessage(hwndDlg, uIDButton, BUTTONSETASFLATBTN, 0, 0);
+ }
+ else {
+ SetDlgItemText(hwndDlg, uIDButton, _T("V"));
+ }
+ }
+ else {
+ res = -1;
+ }
+ ShowWindow(GetDlgItem(hwndDlg, uIDButton), ServiceExists(MS_VARS_FORMATSTRING));
+
+ return res;
+}
+#endif
+
+
+#define MS_VARS_SHOWHELP "Vars/ShowHelp"
+
+// WARNING: This service is obsolete, please use MS_VARS_SHOWHELPEX
+
+// Shows a help dialog where all possible tokens are displayed. The tokens
+// are explained on the dialog, too. The user can edit the initial string and
+// insert as many tokens as he likes.
+
+// Parameters:
+// ------------------------
+// wParam = (HWND)hwndEdit
+// Handle to an edit control in which the modified string
+// should be inserted (When the user clicks OK in the dialog the edited
+// string will be set to hwndEdit) (can be NULL).
+// lParam = (char *)pszInitialString
+// String that the user is provided with initially when
+// the dialog gets opened (If this is NULL then the current text in the
+// hwndEdit edit control will be used) (can be NULL).
+
+// Return Value:
+// ------------------------
+// Returns the handle to the help dialog (HWND).
+
+// Note: Only one help dialog can be opened at a time. When the dialog gets
+// closed an EN_CHANGE of the edit controll will be triggered because the
+// contents were updated. (Only when user selected OK).
+
+// Example:
+// CallService(MS_VARS_SHOWHELP, (WPARAM)hwndEdit, (LPARAM)"some initial text");
+
+// --------------------------------------------------------------------------
+// Retrieve a contact's HANDLE given a string
+// --------------------------------------------------------------------------
+
+#define MS_VARS_GETCONTACTFROMSTRING "Vars/GetContactFromString"
+
+// Searching for contacts in the database. You can find contacts in db by
+// searching for their name, e.g first name.
+
+// Parameters:
+// ------------------------
+// wParam = (WPARAM)(CONTACTSINFO *)&ci
+// See below.
+// lParam = 0
+
+// Return Value:
+// ------------------------
+// Returns number of contacts found matching the given string representation.
+// The hContacts array of CONTACTSINFO struct contains these hContacts after
+// the call.
+
+// Note: The hContacts array needs to be freed after use using
+// MS_VARS_FREEMEMORY.
+
+typedef struct {
+ int cbSize; // Set this to sizeof(CONTACTSINFO).
+ union {
+ char *szContact; // String to search for, e.g. last name (can't be NULL).
+ WCHAR *wszContact;
+ TCHAR *tszContact;
+ };
+ HANDLE *hContacts; // (output) Array of contacts found.
+ DWORD flags; // Contact details that will be matched with the search
+ // string (flags can be combined).
+} CONTACTSINFO;
+
+// Possible flags:
+#define CI_PROTOID 0x00000001 // The contact in the string is encoded
+ // in the format <PROTOID:UNIQUEID>, e.g.
+ // <ICQ:12345678>.
+#define CI_NICK 0x00000002 // Search nick names.
+#define CI_LISTNAME 0x00000004 // Search custom names shown in contact
+ // list.
+#define CI_FIRSTNAME 0x00000008 // Search contact's first names (contact
+ // details).
+#define CI_LASTNAME 0x00000010 // Search contact's last names (contact
+ // details).
+#define CI_EMAIL 0x00000020 // Search contact's email adresses
+ // (contact details).
+#define CI_UNIQUEID 0x00000040 // Search unique ids of the contac, e.g.
+ // UIN.
+#define CI_CNFINFO 0x40000000 // Searches one of the CNF_* flags (set
+ // flags to CI_CNFINFO|CNF_X), only one
+ // CNF_ type possible
+#define CI_UNICODE 0x80000000 // tszContact is a unicode string
+ // (WCHAR*).
+
+#if defined(UNICODE) || defined(_UNICODE)
+#define CI_TCHAR CI_UNICODE // Strings in structure are TCHAR*.
+#else
+#define CI_TCHAR 0
+#endif
+
+
+
+#endif //__M_VARS
diff --git a/Plugins/eSpeak/types.cpp b/Plugins/eSpeak/types.cpp
new file mode 100644
index 0000000..6bab65f
--- /dev/null
+++ b/Plugins/eSpeak/types.cpp
@@ -0,0 +1,232 @@
+/*
+Copyright (C) 2007 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+#include "commons.h"
+
+
+// Prototypes ///////////////////////////////////////////////////////////////////////////
+
+static HANDLE hServices[2] = {0};
+
+int SortTypes(const SPEAK_TYPE *type1, const SPEAK_TYPE *type2);
+int RegisterService(WPARAM wParam, LPARAM lParam);
+int SpeakExService(WPARAM wParam, LPARAM lParam);
+
+LIST<SPEAK_TYPE> types(20, SortTypes);
+
+
+// Functions ////////////////////////////////////////////////////////////////////////////
+
+
+void InitTypes()
+{
+ hServices[0] = CreateServiceFunction(MS_SPEAK_REGISTER, RegisterService);
+ hServices[1] = CreateServiceFunction(MS_SPEAK_SAYEX, SpeakExService);
+}
+
+void FreeTypes()
+{
+ // Destroy services
+ int i;
+ for(i = 0; i < MAX_REGS(hServices); ++i)
+ DestroyServiceFunction(hServices[i]);
+
+ // Free internal structs
+ for(i = 0; i < types.getCount(); i++)
+ {
+ SPEAK_TYPE *type = types[i];
+ mir_free((void *) type->module);
+ mir_free((void *) type->name);
+ mir_free((void *) type->icon);
+
+ if (type->numTemplates > 0)
+ {
+ for(int i = 0; i < type->numTemplates; i++)
+ mir_free((void *) type->templates[i]);
+ mir_free(type->templates);
+ }
+
+ mir_free(type);
+ }
+
+ types.destroy();
+}
+
+
+int SortTypes(const SPEAK_TYPE *type1, const SPEAK_TYPE *type2)
+{
+ return stricmp(type1->description, type2->description);
+}
+
+
+int RegisterService(WPARAM wParam, LPARAM lParam)
+{
+ SPEAK_TYPE *orig = (SPEAK_TYPE *) wParam;
+ if (orig == NULL || orig->cbSize < sizeof(SPEAK_TYPE)
+ || orig->name == NULL || orig->description == NULL)
+ return -1;
+
+ SPEAK_TYPE *type = (SPEAK_TYPE *) mir_alloc0(sizeof(SPEAK_TYPE));
+ type->cbSize = orig->cbSize;
+ type->module = mir_strdup(orig->module);
+ type->name = mir_strdup(orig->name);
+ type->description = Translate(mir_strdup(orig->description));
+ type->icon = mir_strdup(orig->icon);
+ type->numTemplates = orig->numTemplates;
+
+ if (orig->numTemplates > 0)
+ {
+ type->templates = (char **) mir_alloc0(orig->numTemplates * sizeof(char *));
+ for(int i = 0; i < orig->numTemplates; i++)
+ type->templates[i] = mir_strdup(orig->templates[i] == NULL ? "" : orig->templates[i]);
+ }
+
+ types.insert(type);
+
+ return 0;
+}
+
+SPEAK_TYPE *GetType(const char *name)
+{
+ for(int i = 0; i < types.getCount(); i++)
+ {
+ SPEAK_TYPE *type = types[i];
+ if (strcmp(type->name, name) == 0)
+ return type;
+ }
+
+ return NULL;
+}
+
+void GetTemplate(Buffer<TCHAR> *buffer, SPEAK_TYPE *type, int templ)
+{
+ DBVARIANT dbv;
+
+ char setting[128];
+ mir_snprintf(setting, MAX_REGS(setting), "%s_%d_" TEMPLATE_TEXT, type->name, templ);
+
+ if (!DBGetContactSettingTString(NULL, type->module == NULL ? MODULE_NAME : type->module, setting, &dbv))
+ {
+ buffer->append(dbv.ptszVal);
+ DBFreeVariant(&dbv);
+ }
+ else
+ {
+ // Get default
+ const char *tmp = type->templates[templ];
+ tmp = strchr(tmp, '\n');
+ if (tmp == NULL)
+ return;
+ tmp++;
+ const char *end = strchr(tmp, '\n');
+ size_t len = (end == NULL ? strlen(tmp) : end - tmp);
+
+#ifdef UNICODE
+ MultiByteToWideChar(CP_ACP, 0, tmp, len, buffer->appender(len), len);
+#else
+ buffer->append(tmp, len);
+#endif
+ }
+}
+
+static TCHAR** CopyVariablesToUnicode(SPEAK_ITEM *item)
+{
+ TCHAR **variables = NULL;
+ if (item->numVariables > 0)
+ {
+ variables = (TCHAR **) mir_alloc0(item->numVariables * sizeof(TCHAR *));
+ for(int i = 0; i < item->numVariables; i++)
+ {
+ if (item->flags & SPEAK_CHAR)
+ variables[i] = mir_a2t(((char **) item->variables)[i]);
+ else
+ variables[i] = mir_u2t(((WCHAR **) item->variables)[i]);
+ }
+ }
+ return variables;
+}
+
+static void FreeVariablesCopy(SPEAK_ITEM *item, TCHAR **variables)
+{
+ if (item->numVariables > 0 && variables != NULL)
+ {
+ for(int i = 0; i < item->numVariables; i++)
+ {
+ mir_free(variables[i]);
+ }
+ mir_free(variables);
+ }
+}
+
+int SpeakExService(WPARAM wParam, LPARAM lParam)
+{
+ SPEAK_ITEM *item = (SPEAK_ITEM *) wParam;
+ if (item == NULL || item->cbSize < sizeof(SPEAK_ITEM))
+ return -1;
+
+ SPEAK_TYPE *type = GetType(item->type);
+ if (type == NULL)
+ return -2;
+
+ // Get the text to speak
+ if (item->templateNum < 0)
+ {
+ if (item->text == NULL)
+ return -3;
+
+ if (!GetSettingBool(type, TEMPLATE_ENABLED, FALSE))
+ return 1;
+
+ Buffer<TCHAR> buff;
+ if (GetSettingBool(type, SPEAK_NAME, TRUE) && item->hContact != NULL && item->hContact != (HANDLE) -1)
+ {
+ buff.append((TCHAR *) CallService(MS_CLIST_GETCONTACTDISPLAYNAME, (WPARAM) item->hContact, GCDNF_TCHAR));
+ buff.append(_T(" : "));
+ }
+
+ if (item->flags & SPEAK_CHAR)
+ buff.append((char *) item->text);
+ else
+ buff.append((WCHAR *) item->text);
+
+ return SpeakService(item->hContact, buff.detach());
+ }
+ else
+ {
+ if (!GetSettingBool(type, item->templateNum, TEMPLATE_ENABLED, FALSE))
+ return 1;
+
+ Buffer<TCHAR> templ;
+ GetTemplate(&templ, type, item->templateNum);
+ templ.pack();
+
+ Buffer<TCHAR> buffer;
+ TCHAR **variables = CopyVariablesToUnicode(item);
+ ReplaceTemplate(&buffer, item->hContact, templ.str, variables, item->numVariables);
+ FreeVariablesCopy(item, variables);
+
+ if (buffer.str == NULL)
+ return -3;
+
+ return SpeakService(item->hContact, buffer.detach());
+ }
+}
+
+
+
diff --git a/Plugins/eSpeak/types.h b/Plugins/eSpeak/types.h
new file mode 100644
index 0000000..faede0b
--- /dev/null
+++ b/Plugins/eSpeak/types.h
@@ -0,0 +1,34 @@
+/*
+Copyright (C) 2007 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#ifndef __TYPES_H__
+# define __TYPES_H__
+
+
+extern LIST<SPEAK_TYPE> types;
+
+
+void InitTypes();
+void FreeTypes();
+
+void GetTemplate(Buffer<TCHAR> *buffer, SPEAK_TYPE *type, int templ);
+
+
+#endif // __TYPES_H__
diff --git a/Plugins/ersatz/Docs/ersatz_changelog.txt b/Plugins/ersatz/Docs/ersatz_changelog.txt
new file mode 100644
index 0000000..fa006ef
--- /dev/null
+++ b/Plugins/ersatz/Docs/ersatz_changelog.txt
@@ -0,0 +1,9 @@
+ersatz 0.0.1.0
+
+Changelog:
+
+. 0.0.1.1
+ + Added uid for 0.8
+
+. 0.0.1.0
+ + A lot of internal changes
diff --git a/Plugins/ersatz/Docs/ersatz_version.txt b/Plugins/ersatz/Docs/ersatz_version.txt
new file mode 100644
index 0000000..0a20aa3
--- /dev/null
+++ b/Plugins/ersatz/Docs/ersatz_version.txt
@@ -0,0 +1 @@
+ersatz 0.0.1.1 \ No newline at end of file
diff --git a/Plugins/ersatz/ZIP/doit.bat b/Plugins/ersatz/ZIP/doit.bat
new file mode 100644
index 0000000..aca00a0
--- /dev/null
+++ b/Plugins/ersatz/ZIP/doit.bat
@@ -0,0 +1,103 @@
+rem @echo off
+
+rem Batch file to build and upload files
+rem
+rem TODO: Integration with FL
+
+set name=ersatz
+
+rem To upload, this var must be set here or in other batch
+rem set ftp=ftp://<user>:<password>@<ftp>/<path>
+
+echo Building %name% ...
+
+msdev ..\%name%.dsp /MAKE "%name% - Win32 Release" /REBUILD
+
+echo Generating files for %name% ...
+
+del *.zip
+del *.dll
+copy ..\..\..\bin\release\Plugins\aaa%name%.dll
+copy ..\Docs\%name%_changelog.txt
+copy ..\Docs\%name%_version.txt
+mkdir Docs
+cd Docs
+del /Q *.*
+copy ..\..\m_%name%.h
+cd ..
+mkdir src
+cd src
+mkdir %name%
+cd %name%
+del /Q *.*
+copy ..\..\..\*.h
+copy ..\..\..\*.cpp
+copy ..\..\..\*.
+copy ..\..\..\*.rc
+copy ..\..\..\*.dsp
+copy ..\..\..\*.dsw
+mkdir Docs
+cd Docs
+del /Q *.*
+copy ..\..\..\..\Docs\*.*
+cd ..
+mkdir sdk
+cd sdk
+del /Q *.*
+copy ..\..\..\..\sdk\*.*
+cd ..
+cd ..
+mkdir utils
+cd utils
+copy ..\..\..\..\utils\*.h
+copy ..\..\..\..\utils\*.cpp
+cd ..
+cd ..
+
+"C:\Program Files\Filzip\Filzip.exe" -a -rp %name%.zip aaa%name%.dll Docs
+"C:\Program Files\Filzip\Filzip.exe" -a -rp %name%_src.zip src\%name% src\utils
+
+del *.dll
+cd Docs
+del /Q *.*
+cd ..
+rmdir Docs
+cd src
+cd %name%
+del /Q *.*
+cd Docs
+del /Q *.*
+cd ..
+rmdir Docs
+cd sdk
+del /Q *.*
+cd ..
+rmdir sdk
+cd ..
+rmdir %name%
+cd utils
+del /Q *.*
+cd ..
+rmdir utils
+cd ..
+rmdir src
+
+if "%ftp%"=="" GOTO END
+
+echo Going to upload files...
+pause
+
+"C:\Program Files\FileZilla\FileZilla.exe" -u .\%name%.zip %ftp% -overwrite -close
+"C:\Program Files\FileZilla\FileZilla.exe" -u .\%name%_changelog.txt %ftp% -overwrite -close
+"C:\Program Files\FileZilla\FileZilla.exe" -u .\%name%_version.txt %ftp% -overwrite -close
+
+if "%ftp2%"=="" GOTO END
+
+"C:\Program Files\FileZilla\FileZilla.exe" -u .\%name%.zip %ftp2% -overwrite -close
+"C:\Program Files\FileZilla\FileZilla.exe" -u .\%name%_changelog.txt %ftp2% -overwrite -close
+"C:\Program Files\FileZilla\FileZilla.exe" -u .\%name%_version.txt %ftp2% -overwrite -close
+
+
+:END
+
+echo Done.
diff --git a/Plugins/ersatz/commons.h b/Plugins/ersatz/commons.h
new file mode 100644
index 0000000..6c5be1c
--- /dev/null
+++ b/Plugins/ersatz/commons.h
@@ -0,0 +1,68 @@
+/*
+Copyright (C) 2006 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#ifndef __COMMONS_H__
+# define __COMMONS_H__
+
+
+#include <windows.h>
+#include <tchar.h>
+#include <stdio.h>
+#include <time.h>
+
+
+// Miranda headers
+#include <newpluginapi.h>
+#include <m_system.h>
+#include <m_protocols.h>
+#include <m_protosvc.h>
+#include <m_protomod.h>
+#include <m_database.h>
+#include <m_utils.h>
+#include <m_updater.h>
+
+#include "../utils/mir_memory.h"
+
+
+#include "m_ersatz.h"
+#include "services.h"
+
+
+#define MODULE_NAME "ERSATZ"
+
+
+// Global Variables
+extern HINSTANCE hInst;
+extern PLUGINLINK *pluginLink;
+
+
+#define MAX_REGS(_A_) ( sizeof(_A_) / sizeof(_A_[0]) )
+
+
+__inline static int ProtoServiceExists(const char *szModule, const char *szService)
+{
+ char str[MAXMODULELABELLENGTH];
+ strcpy(str,szModule);
+ strcat(str,szService);
+ return ServiceExists(str);
+}
+
+
+#endif // __COMMONS_H__
diff --git a/Plugins/ersatz/ersatz.cpp b/Plugins/ersatz/ersatz.cpp
new file mode 100644
index 0000000..76022a6
--- /dev/null
+++ b/Plugins/ersatz/ersatz.cpp
@@ -0,0 +1,154 @@
+/*
+Copyright (C) 2006 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#include "commons.h"
+
+
+// Prototypes ///////////////////////////////////////////////////////////////////////////
+
+
+PLUGININFOEX pluginInfo = {
+ sizeof(PLUGININFOEX),
+ "ersatz",
+ PLUGIN_MAKE_VERSION(0,0,1,1),
+ "Hacks the PS_SETAWAYMSG service in order to provide a new one: PS_GETMYAWAYMSG",
+ "TioDuke, Ricardo Pescuma Domenecci",
+ "tioduke@yahoo.ca",
+ "(c) 2006 TioDuke",
+ "http://miranda-im.org",
+ 0, //not transient
+ 0, //not used
+ { 0x610493fe, 0x37c5, 0x412e, { 0xb1, 0xf3, 0xb8, 0x47, 0xd1, 0xc9, 0xd5, 0x74 } }
+};
+
+
+HINSTANCE hInst;
+PLUGINLINK *pluginLink;
+
+static HANDLE hModulesLoaded = NULL;
+static HANDLE hPreShutdownHook = NULL;
+
+int ModulesLoaded(WPARAM wParam, LPARAM lParam);
+int PreShutdown(WPARAM wParam, LPARAM lParam);
+
+int ErsatzEnabled(WPARAM wParam, LPARAM lParam);
+
+
+// Functions ////////////////////////////////////////////////////////////////////////////
+
+
+extern "C" BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
+{
+ hInst = hinstDLL;
+ return TRUE;
+}
+
+extern "C" __declspec(dllexport) PLUGININFO* MirandaPluginInfo(DWORD mirandaVersion)
+{
+ pluginInfo.cbSize = sizeof(PLUGININFO);
+ return (PLUGININFO*) &pluginInfo;
+}
+
+
+extern "C" __declspec(dllexport) PLUGININFOEX* MirandaPluginInfoEx(DWORD mirandaVersion)
+{
+ pluginInfo.cbSize = sizeof(PLUGININFOEX);
+ return &pluginInfo;
+}
+
+
+static const MUUID interfaces[] = { MIID_ERSATZ, MIID_LAST };
+extern "C" __declspec(dllexport) const MUUID* MirandaPluginInterfaces(void)
+{
+ return interfaces;
+}
+
+
+extern "C" int __declspec(dllexport) Load(PLUGINLINK *link)
+{
+ pluginLink = link;
+
+ init_mir_malloc();
+
+ InitServices();
+
+ // hooks
+ hModulesLoaded = HookEvent(ME_SYSTEM_MODULESLOADED, ModulesLoaded);
+ hPreShutdownHook = HookEvent(ME_SYSTEM_PRESHUTDOWN, PreShutdown);
+
+ return 0;
+}
+
+extern "C" int __declspec(dllexport) Unload(void)
+{
+ return 0;
+}
+
+
+// Called when all the modules are loaded
+int ModulesLoaded(WPARAM wParam, LPARAM lParam)
+{
+ ModulesLoadedServices();
+
+ CreateServiceFunction(MS_ERSATZ_ENABLED, ErsatzEnabled);
+
+ // add our modules to the KnownModules list
+ CallService("DBEditorpp/RegisterSingleModule", (WPARAM) MODULE_NAME, 0);
+
+ // updater plugin support
+ if(ServiceExists(MS_UPDATE_REGISTER))
+ {
+ Update upd = {0};
+ char szCurrentVersion[30];
+
+ upd.cbSize = sizeof(upd);
+ upd.szComponentName = pluginInfo.shortName;
+ upd.szUpdateURL = UPDATER_AUTOREGISTER;
+ upd.szBetaVersionURL = "http://pescuma.org/miranda/ersatz_version.txt";
+ upd.szBetaChangelogURL = "http://pescuma.org/miranda/ersatz_changelog.txt";
+ upd.pbBetaVersionPrefix = (BYTE *)"ersatz ";
+ upd.cpbBetaVersionPrefix = strlen((char *)upd.pbBetaVersionPrefix);
+ upd.szBetaUpdateURL = "http://pescuma.org/miranda/ersatz.zip";
+ upd.pbVersion = (BYTE *)CreateVersionStringPlugin((PLUGININFO *) &pluginInfo, szCurrentVersion);
+ upd.cpbVersion = strlen((char *)upd.pbVersion);
+
+ CallService(MS_UPDATE_REGISTER, 0, (LPARAM)&upd);
+ }
+
+ return 0;
+}
+
+
+int PreShutdown(WPARAM wParam, LPARAM lParam)
+{
+ DestroyServices();
+
+ UnhookEvent(hModulesLoaded);
+ UnhookEvent(hPreShutdownHook);
+
+ return 0;
+}
+
+
+int ErsatzEnabled(WPARAM wParam, LPARAM lParam)
+{
+ return 1;
+}
+
diff --git a/Plugins/ersatz/ersatz.dsp b/Plugins/ersatz/ersatz.dsp
new file mode 100644
index 0000000..935a6f2
--- /dev/null
+++ b/Plugins/ersatz/ersatz.dsp
@@ -0,0 +1,137 @@
+# Microsoft Developer Studio Project File - Name="ersatz" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=ersatz - Win32 Release
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "ersatz.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "ersatz.mak" CFG="ersatz - Win32 Release"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "ersatz - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "ersatz - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "ersatz - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MD /W3 /GX /O1 /YX /FD /c
+# SUBTRACT BASE CPP /Fr
+# ADD CPP /nologo /G4 /MT /W3 /GX /O2 /Ob0 /I "../../include" /I "sdk" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /Fr /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x417 /d "NDEBUG"
+# ADD RSC /l 0x417 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 user32.lib shell32.lib wininet.lib gdi32.lib /nologo /base:"0x67100000" /dll /machine:I386 /filealign:0x200
+# SUBTRACT BASE LINK32 /pdb:none /map
+# ADD LINK32 kernel32.lib user32.lib /nologo /base:"0x3EC20000" /dll /map /machine:I386 /out:"..\..\bin\release\Plugins\aaaersatz.dll" /filealign:0x200 /ALIGN:4096 /ignore:4108
+# SUBTRACT LINK32 /profile /pdb:none
+
+!ELSEIF "$(CFG)" == "ersatz - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Ignore_Export_Lib 0
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /G4 /MT /W3 /GX /O2 /Ob0 /I "../../include" /FR /YX /FD /c
+# ADD CPP /nologo /G4 /MTd /W3 /GX /ZI /Od /I "../../include" /I "sdk" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /FR /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x417 /d "NDEBUG"
+# ADD RSC /l 0x417 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 comctl32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 /out:"..\..bin\release\Plugins\ersatz.dll" /filealign:0x200 /ALIGN:4096 /ignore:4108
+# SUBTRACT BASE LINK32 /profile /pdb:none
+# ADD LINK32 kernel32.lib user32.lib /nologo /base:"0x3EC20000" /dll /incremental:yes /debug /machine:I386 /out:"..\..\bin\debug unicode\Plugins\aaaersatz.dll" /filealign:0x200 /ALIGN:4096 /ignore:4108
+# SUBTRACT LINK32 /profile /pdb:none
+
+!ENDIF
+
+# Begin Target
+
+# Name "ersatz - Win32 Release"
+# Name "ersatz - Win32 Debug"
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=.\commons.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\m_ersatz.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\utils\mir_memory.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\services.h
+# End Source File
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
+# End Group
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=.\ersatz.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\utils\mir_memory.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\services.cpp
+# End Source File
+# End Group
+# End Target
+# End Project
diff --git a/Plugins/ersatz/ersatz.dsw b/Plugins/ersatz/ersatz.dsw
new file mode 100644
index 0000000..3b10790
--- /dev/null
+++ b/Plugins/ersatz/ersatz.dsw
@@ -0,0 +1,29 @@
+Microsoft Developer Studio Workspace File, Format Version 6.00
+# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
+
+###############################################################################
+
+Project: "ersatz"=.\ersatz.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Global:
+
+Package=<5>
+{{{
+}}}
+
+Package=<3>
+{{{
+}}}
+
+###############################################################################
+
diff --git a/Plugins/ersatz/m_ersatz.h b/Plugins/ersatz/m_ersatz.h
new file mode 100644
index 0000000..e027338
--- /dev/null
+++ b/Plugins/ersatz/m_ersatz.h
@@ -0,0 +1,46 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2006 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#ifndef __M_ERSATZ_H__
+# define __M_ERSATZ_H__
+
+
+#define MIID_ERSATZ { 0x3bbc5fbb, 0x5897, 0x4e37, { 0x90, 0xae, 0x48, 0xda, 0xda, 0xbf, 0x49, 0x6 } }
+
+
+
+// Returns the status message for a status
+// wParam=(WORD) 0 for current status or a status
+// lParam=0
+// Returns status msg or NULL if there is none. The protocol have to handle only the current
+// status. Handling messages for other statuses is optional.
+// Remember to mir_free the return value
+#define PS_GETMYAWAYMSG "/GetMyAwayMsg"
+
+// Created if ersatz is installed
+// wParam=0
+// lParam=0
+// returns always 1
+#define MS_ERSATZ_ENABLED "ERSATZ/Enabled"
+
+
+#endif // __M_ERSATZ_H__
diff --git a/Plugins/ersatz/sdk/m_updater.h b/Plugins/ersatz/sdk/m_updater.h
new file mode 100644
index 0000000..371b743
--- /dev/null
+++ b/Plugins/ersatz/sdk/m_updater.h
@@ -0,0 +1,146 @@
+#ifndef _M_UPDATER_H
+#define _M_UPDATER_H
+
+// NOTES:
+// - For langpack updates, include a string of the following format in the langpack text file:
+// ";FLID: <file listing name> <version>"
+// version must be four numbers seperated by '.', in the range 0-255 inclusive
+// - Updater will disable plugins that are downloaded but were not active prior to the update (this is so that, if an archive contains e.g. ansi and
+// unicode versions, the correct plugin will be the only one active after the new version is installed)...so if you add a support plugin, you may need
+// to install an ini file to make the plugin activate when miranda restarts after the update
+// - Updater will replace all dlls that have the same internal shortName as a downloaded update dll (this is so that msn1.dll and msn2.dll, for example,
+// will both be updated) - so if you have a unicode and a non-unicode version of a plugin in your archive, you should make the internal names different (which will break automatic
+// updates from the file listing if there is only one file listing entry for both versions, unless you use the 'MS_UPDATE_REGISTER' service below)
+// - Updater will install all files in the root of the archive into the plugins folder, except for langpack files that contain the FLID string which go into the root folder (same
+// folder as miranda32.exe)...all folders in the archive will also be copied to miranda's root folder, and their contents transferred into the new folders. The only exception is a
+// special folder called 'root_files' - if there is a folder by that name in the archive, it's contents will also be copied into miranda's root folder - this is intended to be used
+// to install additional dlls etc that a plugin may require)
+
+// if you set Update.szUpdateURL to the following value when registering, as well as setting your beta site and version data,
+// Updater will ignore szVersionURL and pbVersionPrefix, and attempt to find the file listing URL's from the backend XML data.
+// for this to work, the plugin name in pluginInfo.shortName must match the file listing exactly (except for case)
+#define UPDATER_AUTOREGISTER "UpdaterAUTOREGISTER"
+// Updater will also use the backend xml data if you provide URL's that reference the miranda file listing for updates (so you can use that method
+// if e.g. your plugin shortName does not match the file listing) - it will grab the file listing id from the end of these URLs
+
+typedef struct Update_tag {
+ int cbSize;
+ char *szComponentName; // component name as it will appear in the UI (will be translated before displaying)
+
+ char *szVersionURL; // URL where the current version can be found (NULL to disable)
+ BYTE *pbVersionPrefix; // bytes occuring in VersionURL before the version, used to locate the version information within the URL data
+ // (note that this URL could point at a binary file - dunno why, but it could :)
+ int cpbVersionPrefix; // number of bytes pointed to by pbVersionPrefix
+ char *szUpdateURL; // URL where dll/zip is located
+ // set to UPDATER_AUTOREGISTER if you want Updater to find the file listing URLs (ensure plugin shortName matches file listing!)
+
+ char *szBetaVersionURL; // URL where the beta version can be found (NULL to disable betas)
+ BYTE *pbBetaVersionPrefix; // bytes occuring in VersionURL before the version, used to locate the version information within the URL data
+ int cpbBetaVersionPrefix; // number of bytes pointed to by pbVersionPrefix
+ char *szBetaUpdateURL; // URL where dll/zip is located
+
+ BYTE *pbVersion; // bytes of current version, used for comparison with those in VersionURL
+ int cpbVersion; // number of bytes pointed to by pbVersion
+
+ char *szBetaChangelogURL; // url for displaying changelog for beta versions
+} Update;
+
+// register a comonent with Updater
+//
+// wparam = 0
+// lparam = (LPARAM)&Update
+#define MS_UPDATE_REGISTER "Update/Register"
+
+// utility functions to create a version string from a DWORD or from pluginInfo
+// point buf at a buffer at least 16 chars wide - but note the version string returned may be shorter
+//
+__inline static char *CreateVersionString(DWORD version, char *buf) {
+ mir_snprintf(buf, 16, "%d.%d.%d.%d", (version >> 24) & 0xFF, (version >> 16) & 0xFF, (version >> 8) & 0xFF, version & 0xFF);
+ return buf;
+}
+
+__inline static char *CreateVersionStringPlugin(PLUGININFO *pluginInfo, char *buf) {
+ return CreateVersionString(pluginInfo->version, buf);
+}
+
+
+// register the 'easy' way - use this method if you have no beta URL and the plugin is on the miranda file listing
+// NOTE: the plugin version string on the file listing must be the string version of the version in pluginInfo (i.e. 0.0.0.1,
+// four numbers between 0 and 255 inclusivem, so no letters, brackets, etc.)
+//
+// wParam = (int)fileID - this is the file ID from the file listing (i.e. the number at the end of the download link)
+// lParam = (PLUGININFO*)&pluginInfo
+#define MS_UPDATE_REGISTERFL "Update/RegisterFL"
+
+// this function can be used to 'unregister' components - useful for plugins that register non-plugin/langpack components and
+// may need to change those components on the fly
+// lParam = (char *)szComponentName
+#define MS_UPDATE_UNREGISTER "Update/Unregister"
+
+// this event is fired when the startup process is complete, but NOT if a restart is imminent
+// it is designed for status managment plugins to use as a trigger for beggining their own startup process
+// wParam = lParam = 0 (unused)
+// (added in version 0.1.6.0)
+#define ME_UPDATE_STARTUPDONE "Update/StartupDone"
+
+// this service can be used to enable/disable Updater's global status control
+// it can be called from the StartupDone event handler
+// wParam = (BOOL)enable
+// lParam = 0
+// (added in version 0.1.6.0)
+#define MS_UPDATE_ENABLESTATUSCONTROL "Update/EnableStatusControl"
+
+// An description of usage of the above service and event:
+// Say you are a status control plugin that normally sets protocol or global statuses in your ModulesLoaded event handler.
+// In order to make yourself 'Updater compatible', you would move the status control code from ModulesLoaded to another function,
+// say DoStartup. Then, in ModulesLoaded you would check for the existence of the MS_UPDATE_ENABLESTATUSCONTROL service.
+// If it does not exist, call DoStartup. If it does exist, hook the ME_UPDATE_STARTUPDONE event and call DoStartup from there. You may
+// also wish to call MS_UPDATE_ENABLESTATUSCONTROL with wParam == FALSE at this time, to disable Updater's own status control feature.
+
+// this service can be used to determine whether updates are possible for a component with the given name
+// wParam = 0
+// lParam = (char *)szComponentName
+// returns TRUE if updates are supported, FALSE otherwise
+#define MS_UPDATE_ISUPDATESUPPORTED "Update/IsUpdateSupported"
+
+#endif
+
+
+/////////////// Usage Example ///////////////
+
+#ifdef EXAMPLE_CODE
+
+// you need to #include "m_updater.h" and HookEvent(ME_SYSTEM_MODULESLOADED, OnModulesLoaded) in your Load function...
+
+int OnModulesLoaded(WPARAM wParam, LPARAM lParam) {
+
+ Update update = {0}; // for c you'd use memset or ZeroMemory...
+ char szVersion[16];
+
+ update.cbSize = sizeof(Update);
+
+ update.szComponentName = pluginInfo.shortName;
+ update.pbVersion = (BYTE *)CreateVersionString(&pluginInfo, szVersion);
+ update.cpbVersion = strlen((char *)update.pbVersion);
+
+ // these are the three lines that matter - the archive, the page containing the version string, and the text (or data)
+ // before the version that we use to locate it on the page
+ // (note that if the update URL and the version URL point to standard file listing entries, the backend xml
+ // data will be used to check for updates rather than the actual web page - this is not true for beta urls)
+ update.szUpdateURL = "http://scottellis.com.au:81/test/updater.zip";
+ update.szVersionURL = "http://scottellis.com.au:81/test/updater_test.html";
+ update.pbVersionPrefix = (BYTE *)"Updater version ";
+
+ update.cpbVersionPrefix = strlen((char *)update.pbVersionPrefix);
+
+ // do the same for the beta versions of the above struct members if you wish to allow beta updates from another URL
+
+ CallService(MS_UPDATE_REGISTER, 0, (WPARAM)&update);
+
+ // Alternatively, to register a plugin with e.g. file ID 2254 on the file listing...
+ // CallService(MS_UPDATE_REGISTERFL, (WPARAM)2254, (LPARAM)&pluginInfo);
+
+ return 0;
+}
+
+#endif
diff --git a/Plugins/ersatz/services.cpp b/Plugins/ersatz/services.cpp
new file mode 100644
index 0000000..8a848f6
--- /dev/null
+++ b/Plugins/ersatz/services.cpp
@@ -0,0 +1,519 @@
+/*
+Translator plugin for Miranda IM
+
+Copyright © 2006 Cristian Libotean
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "services.h"
+
+
+// Prototypes /////////////////////////////////////////////////////////////////////////////////////
+
+typedef int (*CALLSERVICEFUNCTION) (const char *, WPARAM, LPARAM);
+typedef HANDLE (*CREATESERVICEFUNCTION)(const char *,MIRANDASERVICE);
+
+#define MAX_PROTOS 20
+
+typedef struct{
+ char name[64];
+ BOOL initialized;
+ WORD flags;
+ HANDLE handleGetAwayMsg;
+ MIRANDASERVICE setAwayMsg;
+ char *msgs[ID_STATUS_OUTTOLUNCH - ID_STATUS_ONLINE + 1];
+
+} PROTOCOL_INFO;
+
+typedef struct{
+ unsigned int count;
+ PROTOCOL_INFO info[MAX_PROTOS];
+} PROTOCOL_LIST;
+
+static PROTOCOL_LIST protos;
+
+static CREATESERVICEFUNCTION realCreateServiceFunction = NULL;
+
+static int SetAwayMsg(int pos, WPARAM wParam, LPARAM lParam);
+static int GetAwayMsg(int pos, WPARAM wParam, LPARAM lParam);
+
+
+// Just forget that you read this ;)
+static int SA00(WPARAM w, LPARAM l) { return SetAwayMsg( 0, w, l); }
+static int SA01(WPARAM w, LPARAM l) { return SetAwayMsg( 1, w, l); }
+static int SA02(WPARAM w, LPARAM l) { return SetAwayMsg( 2, w, l); }
+static int SA03(WPARAM w, LPARAM l) { return SetAwayMsg( 3, w, l); }
+static int SA04(WPARAM w, LPARAM l) { return SetAwayMsg( 4, w, l); }
+static int SA05(WPARAM w, LPARAM l) { return SetAwayMsg( 5, w, l); }
+static int SA06(WPARAM w, LPARAM l) { return SetAwayMsg( 6, w, l); }
+static int SA07(WPARAM w, LPARAM l) { return SetAwayMsg( 7, w, l); }
+static int SA08(WPARAM w, LPARAM l) { return SetAwayMsg( 8, w, l); }
+static int SA09(WPARAM w, LPARAM l) { return SetAwayMsg( 9, w, l); }
+static int SA10(WPARAM w, LPARAM l) { return SetAwayMsg(10, w, l); }
+static int SA11(WPARAM w, LPARAM l) { return SetAwayMsg(11, w, l); }
+static int SA12(WPARAM w, LPARAM l) { return SetAwayMsg(12, w, l); }
+static int SA13(WPARAM w, LPARAM l) { return SetAwayMsg(13, w, l); }
+static int SA14(WPARAM w, LPARAM l) { return SetAwayMsg(14, w, l); }
+static int SA15(WPARAM w, LPARAM l) { return SetAwayMsg(15, w, l); }
+static int SA16(WPARAM w, LPARAM l) { return SetAwayMsg(16, w, l); }
+static int SA17(WPARAM w, LPARAM l) { return SetAwayMsg(17, w, l); }
+static int SA18(WPARAM w, LPARAM l) { return SetAwayMsg(18, w, l); }
+static int SA19(WPARAM w, LPARAM l) { return SetAwayMsg(19, w, l); }
+
+static MIRANDASERVICE setAwayFuncs[MAX_PROTOS] = {
+ SA00, SA01, SA02, SA03, SA04, SA05, SA06, SA07, SA08, SA09,
+ SA10, SA11, SA12, SA13, SA14, SA15, SA16, SA17, SA18, SA19 };
+
+
+static int GA00(WPARAM w, LPARAM l) { return GetAwayMsg( 0, w, l); }
+static int GA01(WPARAM w, LPARAM l) { return GetAwayMsg( 1, w, l); }
+static int GA02(WPARAM w, LPARAM l) { return GetAwayMsg( 2, w, l); }
+static int GA03(WPARAM w, LPARAM l) { return GetAwayMsg( 3, w, l); }
+static int GA04(WPARAM w, LPARAM l) { return GetAwayMsg( 4, w, l); }
+static int GA05(WPARAM w, LPARAM l) { return GetAwayMsg( 5, w, l); }
+static int GA06(WPARAM w, LPARAM l) { return GetAwayMsg( 6, w, l); }
+static int GA07(WPARAM w, LPARAM l) { return GetAwayMsg( 7, w, l); }
+static int GA08(WPARAM w, LPARAM l) { return GetAwayMsg( 8, w, l); }
+static int GA09(WPARAM w, LPARAM l) { return GetAwayMsg( 9, w, l); }
+static int GA10(WPARAM w, LPARAM l) { return GetAwayMsg(10, w, l); }
+static int GA11(WPARAM w, LPARAM l) { return GetAwayMsg(11, w, l); }
+static int GA12(WPARAM w, LPARAM l) { return GetAwayMsg(12, w, l); }
+static int GA13(WPARAM w, LPARAM l) { return GetAwayMsg(13, w, l); }
+static int GA14(WPARAM w, LPARAM l) { return GetAwayMsg(14, w, l); }
+static int GA15(WPARAM w, LPARAM l) { return GetAwayMsg(15, w, l); }
+static int GA16(WPARAM w, LPARAM l) { return GetAwayMsg(16, w, l); }
+static int GA17(WPARAM w, LPARAM l) { return GetAwayMsg(17, w, l); }
+static int GA18(WPARAM w, LPARAM l) { return GetAwayMsg(18, w, l); }
+static int GA19(WPARAM w, LPARAM l) { return GetAwayMsg(19, w, l); }
+
+static MIRANDASERVICE getAwayFuncs[MAX_PROTOS] = {
+ GA00, GA01, GA02, GA03, GA04, GA05, GA06, GA07, GA08, GA09,
+ GA10, GA11, GA12, GA13, GA14, GA15, GA16, GA17, GA18, GA19 };
+
+
+
+// Functions //////////////////////////////////////////////////////////////////////////////////////
+
+
+void replaceStr(char*& dest, const char* src)
+{
+ if (dest != NULL)
+ {
+ free(dest);
+ dest = NULL;
+ }
+
+ if (src != NULL)
+ {
+ dest = strdup(src);
+ }
+}
+
+
+void DestroyProtoList()
+{
+ for(unsigned int i = 0; i < protos.count; i++)
+ {
+ for(int j = 0; j < ID_STATUS_OUTTOLUNCH - ID_STATUS_ONLINE + 1; j++)
+ if (protos.info[i].msgs[j] != NULL)
+ free(protos.info[i].msgs[j]);
+
+ if(protos.info[i].handleGetAwayMsg)
+ DestroyServiceFunction(protos.info[i].handleGetAwayMsg);
+ }
+}
+
+
+int GetAwayMsg(int pos, WPARAM wParam, LPARAM lParam)
+{
+ WORD status = (WORD) wParam;
+
+ if (status == 0)
+ status = CallProtoService(protos.info[pos].name, PS_GETSTATUS, 0, 0);
+
+ if (status < ID_STATUS_ONLINE || status > ID_STATUS_OUTTOLUNCH)
+ return NULL;
+
+ return (int) mir_strdup(protos.info[pos].msgs[status - ID_STATUS_ONLINE]);
+}
+
+
+int SetAwayMsg(int pos, WPARAM wParam, LPARAM lParam)
+{
+ WORD status = (WORD) wParam;
+ char *msg = (char *) lParam;
+
+ if (protos.info[pos].initialized
+ && status >= ID_STATUS_ONLINE && status <= ID_STATUS_OUTTOLUNCH
+ && (Proto_Status2Flag(status) & protos.info[pos].flags))
+ {
+ replaceStr(protos.info[pos].msgs[status - ID_STATUS_ONLINE], msg);
+ }
+
+ return protos.info[pos].setAwayMsg(wParam, lParam);
+}
+
+
+HANDLE HackCreateServiceFunction(const char *name, MIRANDASERVICE service)
+{
+ if (name == NULL || service == NULL || protos.count >= MAX_REGS(protos.info))
+ return realCreateServiceFunction(name, service);
+
+ // Check if is a set away msg service
+ const char *pos = strstr(name, PS_SETAWAYMSG);
+ if (pos == NULL || *(pos + strlen(PS_SETAWAYMSG)) != '\0')
+ return realCreateServiceFunction(name, service);
+
+ // Get protocol name
+ char proto_name[128];
+ lstrcpyn(proto_name, name, min(sizeof(proto_name), pos - name + 1));
+
+ // Check if it is realy a protocol
+ PROTOCOLDESCRIPTOR **proto;
+ unsigned int count;
+
+ CallService(MS_PROTO_ENUMPROTOCOLS, (WPARAM)&count, (LPARAM)&proto);
+
+ // First count usefull protos
+ for (unsigned int i = 0; i < count; i++)
+ {
+ if (proto[i]->type != PROTOTYPE_PROTOCOL)
+ continue;
+
+ if (proto[i]->szName == NULL || proto[i]->szName[0] == '\0')
+ continue;
+
+ if (strcmp(proto[i]->szName, proto_name) != 0)
+ continue;
+
+ // Found a protocol
+ int pos = protos.count;
+ protos.count++;
+
+ protos.info[pos].initialized = FALSE;
+ strcpy(protos.info[pos].name, proto_name);
+ protos.info[pos].setAwayMsg = service;
+
+ return realCreateServiceFunction(name, setAwayFuncs[pos]);
+ }
+
+ return realCreateServiceFunction(name, service);
+}
+
+void HookRealServices()
+{
+ memset(&protos, 0, sizeof(protos));
+
+ realCreateServiceFunction = pluginLink->CreateServiceFunction;
+ pluginLink->CreateServiceFunction = HackCreateServiceFunction;
+}
+
+void UnhookRealServices()
+{
+ if (realCreateServiceFunction)
+ pluginLink->CreateServiceFunction = realCreateServiceFunction;
+}
+
+void InitServices()
+{
+ HookRealServices();
+}
+
+void ModulesLoadedServices()
+{
+ // Now initialize things
+ for(unsigned int i = 0; i < protos.count; i++)
+ {
+ if (!(CallProtoService(protos.info[i].name, PS_GETCAPS, PFLAGNUM_1, 0) & PF1_MODEMSGSEND))
+ continue;
+
+ if (ProtoServiceExists(protos.info[i].name, PS_GETMYAWAYMSG))
+ continue;
+
+ protos.info[i].flags = CallProtoService(protos.info[i].name, PS_GETCAPS, PFLAGNUM_3, 0);
+
+ if (protos.info[i].flags == 0)
+ continue;
+
+ // Is a valid proto
+ protos.info[i].initialized = TRUE;
+ protos.info[i].handleGetAwayMsg = (HANDLE) CreateProtoServiceFunction(protos.info[i].name,
+ PS_GETMYAWAYMSG, getAwayFuncs[i]);
+ }
+}
+
+void DestroyServices()
+{
+ UnhookRealServices();
+ DestroyProtoList();
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/*
+static CRITICAL_SECTION cs;
+
+
+
+#if __GNUC__
+#define NOINLINEASM
+#endif
+
+DWORD NameHashFunction(const char *szStr)
+{
+#if defined _M_IX86 && !defined _NUMEGA_BC_FINALCHECK && !defined NOINLINEASM
+ __asm { //this breaks if szStr is empty
+ xor edx,edx
+ xor eax,eax
+ mov esi,szStr
+ mov al,[esi]
+ xor cl,cl
+ lph_top: //only 4 of 9 instructions in here don't use AL, so optimal pipe use is impossible
+ xor edx,eax
+ inc esi
+ xor eax,eax
+ and cl,31
+ mov al,[esi]
+ add cl,5
+ test al,al
+ rol eax,cl //rol is u-pipe only, but pairable
+ //rol doesn't touch z-flag
+ jnz lph_top //5 clock tick loop. not bad.
+
+ xor eax,edx
+ }
+#else
+ DWORD hash=0;
+ int i;
+ int shift=0;
+ for(i=0;szStr[i];i++) {
+ hash^=szStr[i]<<shift;
+ if(shift>24) hash^=(szStr[i]>>(32-shift))&0x7F;
+ shift=(shift+5)&0x1F;
+ }
+ return hash;
+#endif
+}
+
+
+int DummyService(WPARAM wParam, LPARAM lParam)
+{
+ return 0;
+}
+
+
+BOOL CreateProtoList()
+{
+ char szServiceName[MAXMODULELABELLENGTH+1];
+ PROTOCOLDESCRIPTOR **proto;
+ unsigned int count, i;
+
+ CallService(MS_PROTO_ENUMPROTOCOLS, (WPARAM)&count, (LPARAM)&proto);
+
+ // First count usefull protos
+ protos.count = 0;
+ for (i = 0; i < count; i++)
+ {
+ if (proto[i]->type != PROTOTYPE_PROTOCOL)
+ continue;
+
+ if (proto[i]->szName == NULL || proto[i]->szName[0] == '\0')
+ continue;
+
+ if (!(CallProtoService(proto[i]->szName, PS_GETCAPS, PFLAGNUM_1, 0) & PF1_MODEMSGSEND))
+ continue;
+
+ if (CallProtoService(proto[i]->szName, PS_GETCAPS, PFLAGNUM_3, 0) == 0)
+ continue;
+
+ if (ProtoServiceExists(proto[i]->szName, PS_GETMYAWAYMSG))
+ continue;
+
+ // Found a protocol
+ protos.count++;
+ }
+
+ if (protos.count <=0)
+ {
+ protos.info = NULL;
+ return FALSE;
+ }
+
+ // Now lets init our array
+ protos.info = (PROTOCOL_INFO *) mir_alloc0(protos.count * sizeof(PROTOCOL_INFO));
+ if (protos.info == NULL)
+ return FALSE;
+
+ protos.count = 0;
+ for (i = 0; i < count; i++)
+ {
+ if (proto[i]->type != PROTOTYPE_PROTOCOL)
+ continue;
+
+ if (proto[i]->szName == NULL || proto[i]->szName[0] == '\0')
+ continue;
+
+ if (!(CallProtoService(proto[i]->szName, PS_GETCAPS, PFLAGNUM_1, 0) & PF1_MODEMSGSEND))
+ continue;
+
+ if (CallProtoService(proto[i]->szName, PS_GETCAPS, PFLAGNUM_3, 0) == 0)
+ continue;
+
+ if (ProtoServiceExists(proto[i]->szName, PS_GETMYAWAYMSG))
+ continue;
+
+ // Found a protocol
+ lstrcpyn(protos.info[protos.count].name, proto[i]->szName, sizeof(protos.info[protos.count].name));
+ protos.info[protos.count].flags = CallProtoService(proto[i]->szName, PS_GETCAPS, PFLAGNUM_3, 0);
+ protos.info[protos.count].handle = (HANDLE) CreateProtoServiceFunction(proto[i]->szName, PS_GETMYAWAYMSG, DummyService);
+
+ mir_snprintf(szServiceName, sizeof(szServiceName), "%s%s", proto[i]->szName, PS_GETMYAWAYMSG);
+ protos.info[protos.count].hashGetAwayMsg = NameHashFunction(szServiceName);
+
+ mir_snprintf(szServiceName, sizeof(szServiceName), "%s%s", proto[i]->szName, PS_SETAWAYMSG);
+ protos.info[protos.count].hashSetAwayMsg = NameHashFunction(szServiceName);
+
+ protos.count++;
+ }
+
+ return TRUE;
+}
+
+void DestroyProtoList()
+{
+ for(unsigned int i = 0; i < protos.count; i++)
+ {
+ for(int j = 0; j < ID_STATUS_OUTTOLUNCH - ID_STATUS_ONLINE + 1; j++)
+ if (protos.info[i].msgs[j] != NULL)
+ free(protos.info[i].msgs[j]);
+
+ if(protos.info[i].handle)
+ DestroyServiceFunction(protos.info[i].handle);
+ }
+
+ protos.count = 0;
+ if(protos.info)
+ mir_free(protos.info);
+}
+
+
+char *GetProtoAwayMsg(PROTOCOL_INFO *proto)
+{
+ int status = CallProtoService(proto->name, PS_GETSTATUS, 0, 0);
+
+ if (status < ID_STATUS_ONLINE || status > ID_STATUS_OUTTOLUNCH)
+ return NULL;
+
+ if (!(Proto_Status2Flag(status) & proto->flags))
+ return NULL;
+
+ return mir_strdup(proto->msgs[status - ID_STATUS_ONLINE]);
+}
+
+void StoreProtoAwayMsg(PROTOCOL_INFO *proto, char *msg, WORD status)
+{
+ if (status < ID_STATUS_ONLINE || status > ID_STATUS_OUTTOLUNCH)
+ return;
+
+ replaceStr(proto->msgs[status - ID_STATUS_ONLINE], msg);
+}
+
+int HackCallServiceFunction(const char *name, WPARAM wParam, LPARAM lParam)
+{
+ DWORD hash = NameHashFunction(name);
+
+ for(unsigned int i = 0; i < protos.count; i++)
+ {
+ if (hash == protos.info[i].hashGetAwayMsg)
+ {
+ return (int) GetProtoAwayMsg(&protos.info[i]);
+ }
+ else if (hash == protos.info[i].hashSetAwayMsg)
+ {
+ int res = realCallServiceFunction(name, wParam, lParam);
+ if(!res)
+ StoreProtoAwayMsg(&protos.info[i], (char *)lParam, (WORD)wParam);
+ return res;
+ }
+ }
+
+ return realCallServiceFunction(name, wParam, lParam);
+}
+
+void HookRealServices()
+{
+ EnterCriticalSection(&cs);
+ realCallServiceFunction = pluginLink->CallService;
+ pluginLink->CallService = HackCallServiceFunction;
+ LeaveCriticalSection(&cs);
+}
+
+void UnhookRealServices()
+{
+ EnterCriticalSection(&cs);
+ if (realCallServiceFunction)
+ pluginLink->CallService = realCallServiceFunction;
+ LeaveCriticalSection(&cs);
+}
+
+void InitServices()
+{
+ InitializeCriticalSection(&cs);
+ if (CreateProtoList())
+ HookRealServices();
+}
+
+void DestroyServices()
+{
+ UnhookRealServices();
+ DeleteCriticalSection(&cs);
+}
+*/ \ No newline at end of file
diff --git a/Plugins/ersatz/services.h b/Plugins/ersatz/services.h
new file mode 100644
index 0000000..c553f56
--- /dev/null
+++ b/Plugins/ersatz/services.h
@@ -0,0 +1,33 @@
+/*
+Translator plugin for Miranda IM
+
+Copyright © 2006 Cristian Libotean
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef __SERVICES_H__
+# define __SERVICES_H__
+
+#include "commons.h"
+
+
+void InitServices();
+void ModulesLoadedServices();
+void DestroyServices();
+
+
+
+#endif // __SERVICES_H__
diff --git a/Plugins/historyevents/Docs/historyevents.png b/Plugins/historyevents/Docs/historyevents.png
new file mode 100644
index 0000000..dc7a3c0
--- /dev/null
+++ b/Plugins/historyevents/Docs/historyevents.png
Binary files differ
diff --git a/Plugins/historyevents/Docs/historyevents_changelog.txt b/Plugins/historyevents/Docs/historyevents_changelog.txt
new file mode 100644
index 0000000..c35e21f
--- /dev/null
+++ b/Plugins/historyevents/Docs/historyevents_changelog.txt
@@ -0,0 +1,33 @@
+History Events
+
+Changelog:
+
+. 0.0.0.8
+ + Added option to return a copy of the icon in GETICON service
+
+. 0.0.0.7
+ + Allow setting history timestamp on addToHistory
+ + Allow adding to meta too on addToHistory
+
+. 0.0.0.6
+ * Fix for get event text
+
+. 0.0.0.5
+ + Added option to get event struct by event type
+ + Added option to register using an icolib icon
+ * Now options page is ordered by name (with core types at top)
+
+. 0.0.0.4
+ * Fix to work with old version of variables too
+
+. 0.0.0.3
+ + Added variables support
+ + Added option: only log if message window open
+
+. 0.0.0.2
+ * DLL needed to be renamed
+ * Bug fixes
+ + Added 6 months, 1 year, max 10 and max 100 to options to delete history
+
+. 0.0.0.1
+ + Initial version \ No newline at end of file
diff --git a/Plugins/historyevents/Docs/historyevents_readme.txt b/Plugins/historyevents/Docs/historyevents_readme.txt
new file mode 100644
index 0000000..aa2270d
--- /dev/null
+++ b/Plugins/historyevents/Docs/historyevents_readme.txt
@@ -0,0 +1,19 @@
+History Events plugin
+---------------------
+
+CAUTION: THIS IS BETA QUALITY SOFTWARE. IT CAN ERASE ALL YOUR HISTORY. USE AT YOUR OWN RISK.
+
+This is a service plugin that allows adding/reading new event types to history. The main idea is a service that plugins use to add events to history and that the *srmm plugins can use to show then.
+
+It also provides options to remove events from history in some pre-configured intervals. This part is not well tested and can cause big problems. To erase the old events it will, after 1 min of miranda start, go through all events and check which to remove. This is done slowly and in other thread (to avoid too much CPU usage), but this means that the event may take longer than the selected value to be removed.
+
+It also support Variables plugin (needs the latest version)
+
+This plugin requires at least Miranda 0.7
+
+To report bugs/make suggestions, go to the forum thread: http://forums.miranda-im.org/showthread.php?t=15467
+
+To do:
+ - Add support in *srmm
+ - Add support for HTML
+ - Add support for color/font formats
diff --git a/Plugins/historyevents/Docs/historyevents_version.txt b/Plugins/historyevents/Docs/historyevents_version.txt
new file mode 100644
index 0000000..dd2c4e6
--- /dev/null
+++ b/Plugins/historyevents/Docs/historyevents_version.txt
@@ -0,0 +1 @@
+HistoryEvents 0.0.0.8 \ No newline at end of file
diff --git a/Plugins/historyevents/ZIP/doit.bat b/Plugins/historyevents/ZIP/doit.bat
new file mode 100644
index 0000000..c317255
--- /dev/null
+++ b/Plugins/historyevents/ZIP/doit.bat
@@ -0,0 +1,104 @@
+rem @echo off
+
+rem Batch file to build and upload files
+rem
+rem TODO: Integration with FL
+
+set name=historyevents
+
+rem To upload, this var must be set here or in other batch
+rem set ftp=ftp://<user>:<password>@<ftp>/<path>
+
+echo Building %name% ...
+
+msdev ..\%name%.dsp /MAKE "%name% - Win32 Release" /REBUILD
+msdev ..\%name%.dsp /MAKE "%name% - Win32 Unicode Release" /REBUILD
+
+echo Generating files for %name% ...
+
+del *.zip
+del *.dll
+del *.pdb
+
+copy ..\Docs\%name%_changelog.txt
+copy ..\Docs\%name%_version.txt
+copy ..\Docs\%name%_readme.txt
+mkdir Docs
+cd Docs
+del /Q *.*
+copy ..\..\Docs\%name%_readme.txt
+rem copy ..\..\Docs\langpack_%name%.txt
+rem copy ..\..\m_%name%.h
+cd ..
+mkdir src
+cd src
+del /Q *.*
+copy ..\..\*.h
+copy ..\..\*.c*
+copy ..\..\*.
+copy ..\..\*.rc
+copy ..\..\*.dsp
+copy ..\..\*.dsw
+mkdir Docs
+cd Docs
+del /Q *.*
+copy ..\..\..\Docs\*.*
+cd ..
+mkdir sdk
+cd sdk
+del /Q *.*
+copy ..\..\..\sdk\*.*
+cd ..
+cd ..
+copy ..\Release\aa_%name%.pdb
+copy "..\Unicode_Release\aa_%name%W.pdb"
+
+mkdir Plugins
+
+cd Plugins
+copy "..\..\..\..\bin\release unicode\Plugins\aa_%name%W.dll"
+cd ..
+
+"C:\Program Files\Filzip\Filzip.exe" -a -rp %name%W.zip Plugins Docs
+
+
+cd Plugins
+del /Q aa_%name%W.dll
+copy "..\..\..\..\bin\release\Plugins\aa_%name%.dll"
+cd ..
+
+"C:\Program Files\Filzip\Filzip.exe" -a -rp %name%.zip Plugins Docs
+
+"C:\Program Files\Filzip\Filzip.exe" -a -rp %name%_src.zip src\*.*
+"C:\Program Files\Filzip\Filzip.exe" -a -rp %name%.pdb.zip aa_%name%.pdb
+"C:\Program Files\Filzip\Filzip.exe" -a -rp %name%W.pdb.zip aa_%name%W.pdb
+
+del *.pdb
+rd /S /Q Plugins
+rd /S /Q Docs
+rd /S /Q src
+
+if "%ftp%"=="" GOTO END
+
+echo Going to upload files...
+pause
+
+"C:\Program Files\FileZilla\FileZilla.exe" -u .\%name%.zip %ftp% -overwrite -close
+"C:\Program Files\FileZilla\FileZilla.exe" -u .\%name%W.zip %ftp% -overwrite -close
+"C:\Program Files\FileZilla\FileZilla.exe" -u .\%name%.pdb.zip %ftp% -overwrite -close
+"C:\Program Files\FileZilla\FileZilla.exe" -u .\%name%W.pdb.zip %ftp% -overwrite -close
+"C:\Program Files\FileZilla\FileZilla.exe" -u .\%name%_changelog.txt %ftp% -overwrite -close
+"C:\Program Files\FileZilla\FileZilla.exe" -u .\%name%_version.txt %ftp% -overwrite -close
+"C:\Program Files\FileZilla\FileZilla.exe" -u .\%name%_readme.txt %ftp% -overwrite -close
+
+if "%ftp2%"=="" GOTO END
+
+"C:\Program Files\FileZilla\FileZilla.exe" -u .\%name%.zip %ftp2% -overwrite -close
+"C:\Program Files\FileZilla\FileZilla.exe" -u .\%name%W.zip %ftp2% -overwrite -close
+"C:\Program Files\FileZilla\FileZilla.exe" -u .\%name%_changelog.txt %ftp2% -overwrite -close
+"C:\Program Files\FileZilla\FileZilla.exe" -u .\%name%_version.txt %ftp2% -overwrite -close
+"C:\Program Files\FileZilla\FileZilla.exe" -u .\%name%_readme.txt %ftp2% -overwrite -close
+
+:END
+
+echo Done.
diff --git a/Plugins/historyevents/commons.h b/Plugins/historyevents/commons.h
new file mode 100644
index 0000000..6af20b2
--- /dev/null
+++ b/Plugins/historyevents/commons.h
@@ -0,0 +1,76 @@
+/*
+Copyright (C) 2006 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#ifndef __COMMONS_H__
+# define __COMMONS_H__
+
+
+#include <windows.h>
+#include <tchar.h>
+#include <stdio.h>
+#include <time.h>
+
+
+// Miranda headers
+#define MIRANDA_VER 0x0700
+
+#include <newpluginapi.h>
+#include <m_system.h>
+#include <m_system_cpp.h>
+#include <m_contacts.h>
+#include <m_langpack.h>
+#include <m_database.h>
+#include <m_options.h>
+#include <m_utils.h>
+#include <m_history.h>
+#include <m_updater.h>
+#include <m_icolib.h>
+#include <m_clist.h>
+#include <m_message.h>
+#include <m_metacontacts.h>
+
+#include "../utils/mir_memory.h"
+#include "../utils/mir_icons.h"
+#include "../utils/mir_buffer.h"
+
+#include "m_historyevents.h"
+#include "resource.h"
+
+
+#define MODULE_NAME "HistoryEvents"
+
+
+// Global Variables
+extern HINSTANCE hInst;
+extern PLUGINLINK *pluginLink;
+extern LIST<HISTORY_EVENT_HANDLER> handlers;
+
+#define MAX_REGS(_A_) ( sizeof(_A_) / sizeof(_A_[0]) )
+
+
+#include "options.h"
+
+HICON LoadIconEx(HISTORY_EVENT_HANDLER *heh, bool copy = false);
+
+
+#define KEEP_FLAG(_x_) ((_x_) & 0xFF00)
+
+
+#endif // __COMMONS_H__
diff --git a/Plugins/historyevents/historyevents.cpp b/Plugins/historyevents/historyevents.cpp
new file mode 100644
index 0000000..af59899
--- /dev/null
+++ b/Plugins/historyevents/historyevents.cpp
@@ -0,0 +1,1264 @@
+/*
+Copyright (C) 2006 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#include "commons.h"
+
+
+#define SIZEOF_HISTORY_EVENT_ADD_V1 36
+
+
+// Prototypes ///////////////////////////////////////////////////////////////////////////
+
+PLUGININFOEX pluginInfo={
+ sizeof(PLUGININFOEX),
+#ifdef UNICODE
+ "History Events (Unicode)",
+#else
+ "History Events",
+#endif
+ PLUGIN_MAKE_VERSION(0,0,0,8),
+ "A service plugin to handle custom history events",
+ "Ricardo Pescuma Domenecci",
+ "",
+ "© 2007 Ricardo Pescuma Domenecci",
+ "http://pescuma.mirandaim.ru/miranda/historyevents",
+ UNICODE_AWARE,
+ 0, //doesn't replace anything built-in
+#ifdef UNICODE
+ { 0x25b9a055, 0x1e7f, 0x4505, { 0x99, 0xef, 0x9b, 0xc7, 0x6e, 0x3f, 0x1b, 0xd0 } } // {25B9A055-1E7F-4505-99EF-9BC76E3F1BD0}
+#else
+ { 0xe502920c, 0xd4b9, 0x44d0, { 0xad, 0x29, 0xf0, 0x3, 0x93, 0x75, 0xc4, 0xf9 } } // {E502920C-D4B9-44d0-AD29-F0039375C4F9}
+#endif
+};
+
+
+HINSTANCE hInst;
+PLUGINLINK *pluginLink;
+MM_INTERFACE mmi;
+UTF8_INTERFACE utfi;
+LIST_INTERFACE li;
+
+int SortHandlers(const HISTORY_EVENT_HANDLER *type1, const HISTORY_EVENT_HANDLER *type2);
+LIST<HISTORY_EVENT_HANDLER> handlers(20, SortHandlers);
+
+static HANDLE hHooks[4] = {0};
+
+int ModulesLoaded(WPARAM wParam, LPARAM lParam);
+int PreShutdown(WPARAM wParam, LPARAM lParam);
+int DbEventFilterAdd(WPARAM wParam, LPARAM lParam);
+int DbEventAdded(WPARAM wParam, LPARAM lParam);
+
+int ServiceGetCount(WPARAM wParam, LPARAM lParam);
+int ServiceGetEvent(WPARAM wParam, LPARAM lParam);
+int ServiceRegister(WPARAM wParam, LPARAM lParam);
+int ServiceCanHandle(WPARAM wParam, LPARAM lParam);
+int ServiceGetIcon(WPARAM wParam, LPARAM lParam);
+int ServiceGetFlags(WPARAM wParam, LPARAM lParam);
+int ServiceGetText(WPARAM wParam, LPARAM lParam);
+int ServiceReleaseText(WPARAM wParam, LPARAM lParam);
+int ServiceAddToHistory(WPARAM wParam, LPARAM lParam);
+int ServiceIsEnabledTemplate(WPARAM wParam, LPARAM lParam);
+
+HANDLE hDeleteThreadEvent;
+DWORD WINAPI DeleteThread(LPVOID vParam);
+static CRITICAL_SECTION cs;
+int shuttingDown = 0;
+
+HISTORY_EVENT_HANDLER *GetHandler(WORD eventType);
+void RegisterDefaultEventType(char *name, char *description, WORD eventType, int flags = 0, char *icon = NULL, fGetHistoryEventText pfGetHistoryEventText = NULL);
+void * GetDefaultHistoryEventText(HANDLE hContact, HANDLE hDbEvent, DBEVENTINFO *dbe, int flags);
+void * GetMessageHistoryEventText(HANDLE hContact, HANDLE hDbEvent, DBEVENTINFO *dbe, int format);
+void * GetFileHistoryEventText(HANDLE hContact, HANDLE hDbEvent, DBEVENTINFO *dbe, int flags);
+template<class T>
+char * ConvertToRTF(T *line, HISTORY_EVENT_HANDLER *heh = NULL, BYTE *extra = NULL);
+
+// SRMM settings
+#define LOADHISTORY_UNREAD 0
+#define LOADHISTORY_COUNT 1
+#define LOADHISTORY_TIME 2
+
+// a list of db events - we'll check them for the 'read' flag periodically and delete them whwen marked as read
+struct HistoryEventNode {
+ HANDLE hContact;
+ HANDLE hDBEvent;
+ HistoryEventNode *next;
+};
+
+HistoryEventNode *firstHistoryEvent = NULL;
+HistoryEventNode *lastHistoryEvent = NULL;
+
+BOOL ItsTimeToDelete(HANDLE hContact, HANDLE hDbEvent, DBEVENTINFO *dbe = NULL, int eventNum = -1);
+void AppendHistoryEvent(HANDLE hContact, HANDLE hDbEvent);
+BOOL MsgWndOpen(HANDLE hContact);
+
+
+// Functions ////////////////////////////////////////////////////////////////////////////
+
+
+extern "C" BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
+{
+ hInst = hinstDLL;
+ return TRUE;
+}
+
+
+extern "C" __declspec(dllexport) PLUGININFO* MirandaPluginInfo(DWORD mirandaVersion)
+{
+ pluginInfo.cbSize = sizeof(PLUGININFO);
+ return (PLUGININFO*) &pluginInfo;
+}
+
+
+extern "C" __declspec(dllexport) PLUGININFOEX* MirandaPluginInfoEx(DWORD mirandaVersion)
+{
+ pluginInfo.cbSize = sizeof(PLUGININFOEX);
+ return &pluginInfo;
+}
+
+
+static const MUUID interfaces[] = { MIID_HISTORYEVENTS, MIID_LAST };
+extern "C" __declspec(dllexport) const MUUID* MirandaPluginInterfaces(void)
+{
+ return interfaces;
+}
+
+extern "C" int __declspec(dllexport) Load(PLUGINLINK *link)
+{
+ pluginLink = link;
+
+ // TODO Assert results here
+ mir_getMMI(&mmi);
+ mir_getUTFI(&utfi);
+ mir_getLI(&li);
+
+ hDeleteThreadEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+
+ // Default events
+ RegisterDefaultEventType("message", "Message", EVENTTYPE_MESSAGE,
+ HISTORYEVENTS_FLAG_SHOW_IM_SRMM
+ | HISTORYEVENTS_FLAG_USE_SENT_FLAG
+ | HISTORYEVENTS_FLAG_EXPECT_CONTACT_NAME_BEFORE,
+ "core_main_1", GetMessageHistoryEventText);
+ RegisterDefaultEventType("url", "URL", EVENTTYPE_URL,
+ HISTORYEVENTS_FLAG_SHOW_IM_SRMM
+ | HISTORYEVENTS_FLAG_USE_SENT_FLAG
+ | HISTORYEVENTS_FLAG_EXPECT_CONTACT_NAME_BEFORE,
+ "core_main_2", GetDefaultHistoryEventText);
+ //RegisterDefaultEventType("contacts", "Contacts", EVENTTYPE_CONTACTS);
+ //RegisterDefaultEventType("added", "Added", EVENTTYPE_ADDED);
+ //RegisterDefaultEventType("authrequest", "Auth Request", EVENTTYPE_AUTHREQUEST);
+ RegisterDefaultEventType("file", "File Transfer", EVENTTYPE_FILE,
+ HISTORYEVENTS_FLAG_SHOW_IM_SRMM
+ | HISTORYEVENTS_FLAG_USE_SENT_FLAG
+ | HISTORYEVENTS_FLAG_EXPECT_CONTACT_NAME_BEFORE,
+ "core_main_3", GetFileHistoryEventText);
+
+ CreateServiceFunction(MS_HISTORYEVENTS_GET_COUNT, ServiceGetCount);
+ CreateServiceFunction(MS_HISTORYEVENTS_GET_EVENT, ServiceGetEvent);
+ CreateServiceFunction(MS_HISTORYEVENTS_REGISTER, ServiceRegister);
+ CreateServiceFunction(MS_HISTORYEVENTS_CAN_HANDLE, ServiceCanHandle);
+ CreateServiceFunction(MS_HISTORYEVENTS_GET_ICON, ServiceGetIcon);
+ CreateServiceFunction(MS_HISTORYEVENTS_GET_FLAGS, ServiceGetFlags);
+ CreateServiceFunction(MS_HISTORYEVENTS_GET_TEXT, ServiceGetText);
+ CreateServiceFunction(MS_HISTORYEVENTS_RELEASE_TEXT, ServiceReleaseText);
+ CreateServiceFunction(MS_HISTORYEVENTS_ADD_TO_HISTORY, ServiceAddToHistory);
+ CreateServiceFunction(MS_HISTORYEVENTS_IS_ENABLED_TEMPLATE, ServiceIsEnabledTemplate);
+
+ // hooks
+ hHooks[0] = HookEvent(ME_SYSTEM_MODULESLOADED, ModulesLoaded);
+ hHooks[1] = HookEvent(ME_SYSTEM_PRESHUTDOWN, PreShutdown);
+ hHooks[2] = HookEvent(ME_DB_EVENT_FILTER_ADD, DbEventFilterAdd);
+ hHooks[3] = HookEvent(ME_DB_EVENT_ADDED, DbEventAdded);
+
+ InitOptions();
+
+ return 0;
+}
+
+extern "C" int __declspec(dllexport) Unload(void)
+{
+ return 0;
+}
+
+// Called when all the modules are loaded
+int ModulesLoaded(WPARAM wParam, LPARAM lParam)
+{
+ // add our modules to the KnownModules list
+ CallService("DBEditorpp/RegisterSingleModule", (WPARAM) MODULE_NAME, 0);
+
+ // updater plugin support
+ if(ServiceExists(MS_UPDATE_REGISTER))
+ {
+ Update upd = {0};
+ char szCurrentVersion[30];
+
+ upd.cbSize = sizeof(upd);
+ upd.szComponentName = pluginInfo.shortName;
+
+ upd.szUpdateURL = UPDATER_AUTOREGISTER;
+
+ upd.szBetaVersionURL = "http://pescuma.org/miranda/historyevents_version.txt";
+ upd.szBetaChangelogURL = "http://pescuma.org/miranda/historyevents#Changelog";
+ upd.pbBetaVersionPrefix = (BYTE *)"HistoryEvents ";
+ upd.cpbBetaVersionPrefix = strlen((char *)upd.pbBetaVersionPrefix);
+#ifdef UNICODE
+ upd.szBetaUpdateURL = "http://pescuma.org/miranda/historyeventsW.zip";
+#else
+ upd.szBetaUpdateURL = "http://pescuma.org/miranda/historyevents.zip";
+#endif
+
+ upd.pbVersion = (BYTE *)CreateVersionStringPlugin((PLUGININFO*) &pluginInfo, szCurrentVersion);
+ upd.cpbVersion = strlen((char *)upd.pbVersion);
+
+ CallService(MS_UPDATE_REGISTER, 0, (LPARAM)&upd);
+ }
+
+ InitializeCriticalSection(&cs);
+ DWORD threadID;
+ CreateThread(NULL, 0, DeleteThread, NULL, 0, &threadID);
+
+ return 0;
+}
+
+
+int PreShutdown(WPARAM wParam, LPARAM lParam)
+{
+ if (shuttingDown == 0)
+ shuttingDown = 1;
+ SetEvent(hDeleteThreadEvent);
+ int count = 0;
+ while(shuttingDown != 2 && ++count < 50)
+ Sleep(10);
+
+ DeInitOptions();
+
+ for(int i = 0; i < MAX_REGS(hHooks); i++)
+ if (hHooks[i] != NULL)
+ UnhookEvent(hHooks[i]);
+
+ while(handlers.getCount() > 0)
+ {
+ mir_free(handlers[0]->templates);
+ mir_free(handlers[0]);
+ handlers.remove(0);
+ }
+
+ return 0;
+}
+
+
+// Avoid add to db if KEEP_DONT
+int DbEventFilterAdd(WPARAM wParam, LPARAM lParam)
+{
+ DBEVENTINFO *dbe = (DBEVENTINFO *) lParam;
+ if (dbe == NULL)
+ return 0;
+
+ // Get handler
+ HISTORY_EVENT_HANDLER *heh = GetHandler(dbe->eventType);
+ if (heh == NULL)
+ return 0;
+
+ int keepInDB = KEEP_FLAG(heh->flags);
+
+ if (keepInDB == HISTORYEVENTS_FLAG_KEEP_DONT)
+ return 1;
+
+ if (keepInDB == HISTORYEVENTS_FLAG_KEEP_FOR_SRMM && !(heh->flags & HISTORYEVENTS_FLAG_SHOW_IM_SRMM))
+ return 1;
+
+ return 0;
+}
+
+HICON LoadIconEx(HISTORY_EVENT_HANDLER *heh, bool copy)
+{
+ if (heh->defaultIcon != NULL)
+ return IcoLib_LoadIcon((char *) heh->defaultIcon, copy);
+
+ char name[128];
+ mir_snprintf(name, sizeof(name), "historyevent_%s", heh->name);
+ return IcoLib_LoadIcon(name, copy);
+}
+
+
+int GetHandlerID(WORD eventType)
+{
+ for (int i = 0; i < handlers.getCount(); i++)
+ {
+ if (handlers[i]->eventType == eventType)
+ return i;
+ }
+
+ return -1;
+}
+
+
+HISTORY_EVENT_HANDLER *GetHandler(WORD eventType)
+{
+ int pos = GetHandlerID(eventType);
+
+ if (pos >= 0)
+ return handlers[pos];
+ else
+ return NULL;
+}
+
+
+void RegisterDefaultEventType(char *name, char *description, WORD eventType, int flags, char *icon, fGetHistoryEventText pfGetHistoryEventText)
+{
+ HISTORY_EVENT_HANDLER *heh = (HISTORY_EVENT_HANDLER *) mir_alloc0(sizeof(HISTORY_EVENT_HANDLER));
+ heh->name = name;
+ heh->description = Translate(description);
+ heh->eventType = eventType;
+ heh->defaultIconName = icon;
+ heh->supports = HISTORYEVENTS_FORMAT_CHAR | HISTORYEVENTS_FORMAT_WCHAR | HISTORYEVENTS_FORMAT_RICH_TEXT;
+ heh->flags = GetSettingDword(heh, FLAGS, flags) | HISTORYEVENTS_FLAG_DEFAULT | HISTORYEVENTS_REGISTERED_IN_ICOLIB;
+ heh->pfGetHistoryEventText = pfGetHistoryEventText;
+
+ if (icon == NULL)
+ {
+ char name[128];
+ mir_snprintf(name, sizeof(name), "historyevent_%s", heh->name);
+ heh->defaultIconName = strdup(name);
+
+ SKINICONDESC sid = {0};
+ sid.cbSize = sizeof(SKINICONDESC);
+ sid.flags = SIDF_SORTED;
+ sid.pszSection = Translate("History/Events");
+ sid.pszDescription = heh->description;
+ sid.pszName = heh->defaultIconName;
+ CallService(MS_SKIN2_ADDICON, 0, (LPARAM) &sid);
+ }
+
+ handlers.insert(heh);
+}
+
+
+int ServiceRegister(WPARAM wParam, LPARAM lParam)
+{
+ HISTORY_EVENT_HANDLER *orig = (HISTORY_EVENT_HANDLER *) wParam;
+ if (orig == NULL
+ || orig->cbSize < sizeof(HISTORY_EVENT_HANDLER)
+ || orig->name == NULL
+ || orig->name[0] == '\0'
+ || orig->description == NULL
+ || orig->description[0] == '\0'
+ || orig->eventType == 0
+ || (orig->pfGetHistoryEventText != NULL && (orig->supports & (HISTORYEVENTS_FORMAT_CHAR | HISTORYEVENTS_FORMAT_WCHAR)) == 0)
+ || orig->flags == -1)
+ return -1;
+
+ // Already have it?
+ if (GetHandler(orig->eventType) != NULL)
+ return -2;
+
+ // Lets add it
+ HISTORY_EVENT_HANDLER *heh = (HISTORY_EVENT_HANDLER *) mir_alloc0(sizeof(HISTORY_EVENT_HANDLER));
+ *heh = *orig;
+ heh->name = strdup(orig->name);
+ heh->description = Translate(orig->description);
+ if (heh->description == orig->description)
+ heh->description = strdup(orig->description);
+ heh->flags = GetSettingDword(orig, FLAGS, orig->flags) | HISTORYEVENTS_REGISTERED_IN_ICOLIB;
+ if (orig->flags & HISTORYEVENTS_REGISTERED_IN_ICOLIB)
+ {
+ heh->defaultIconName = strdup(orig->defaultIconName);
+ }
+ else
+ {
+ char name[128];
+ mir_snprintf(name, sizeof(name), "historyevent_%s", orig->name);
+ heh->defaultIconName = strdup(name);
+
+ // Add to icolib
+ SKINICONDESC sid = {0};
+ sid.cbSize = sizeof(SKINICONDESC);
+ sid.flags = SIDF_SORTED;
+ sid.pszSection = Translate("History/Events");
+ sid.pszDescription = heh->description;
+ sid.pszName = heh->defaultIconName;
+ sid.hDefaultIcon = orig->defaultIcon;
+ CallService(MS_SKIN2_ADDICON, 0, (LPARAM) &sid);
+ }
+
+ if (orig->pfGetHistoryEventText == NULL)
+ {
+ heh->pfGetHistoryEventText = GetDefaultHistoryEventText;
+ heh->supports = HISTORYEVENTS_FORMAT_CHAR | HISTORYEVENTS_FORMAT_WCHAR | HISTORYEVENTS_FORMAT_RICH_TEXT;
+ }
+
+ if (orig->numTemplates > 0)
+ {
+ char **templates = (char **) mir_alloc0(orig->numTemplates * sizeof(char *));
+ for(int i = 0; i < orig->numTemplates; i++)
+ templates[i] = strdup(orig->templates[i] == NULL ? "" : orig->templates[i]);
+ heh->templates = templates;
+ }
+
+ handlers.insert(heh);
+ return 0;
+}
+
+
+int ServiceCanHandle(WPARAM wParam, LPARAM lParam)
+{
+ HISTORY_EVENT_HANDLER *heh = GetHandler(wParam);
+ return heh != NULL;
+}
+
+
+int ServiceGetIcon(WPARAM wParam, LPARAM lParam)
+{
+ // Get handler
+ HISTORY_EVENT_HANDLER *heh = GetHandler(wParam);
+ if (heh == NULL)
+ return NULL;
+
+ // Get icon
+ return (int) LoadIconEx(heh, lParam != FALSE);
+}
+
+
+int ServiceGetFlags(WPARAM wParam, LPARAM lParam)
+{
+ // Get handler
+ HISTORY_EVENT_HANDLER *heh = GetHandler(wParam);
+ if (heh == NULL)
+ return -1;
+
+ // Get icon
+ return heh->flags;
+}
+
+
+int ServiceGetText(WPARAM wParam, LPARAM lParam)
+{
+ HISTORY_EVENT_PARAM *hep = (HISTORY_EVENT_PARAM *) wParam;
+ if (hep == NULL
+ || hep->cbSize < sizeof(HISTORY_EVENT_PARAM)
+ || (hep->format & (HISTORYEVENTS_FORMAT_CHAR | HISTORYEVENTS_FORMAT_WCHAR | HISTORYEVENTS_FORMAT_RICH_TEXT)) == 0
+ || hep->hDbEvent == NULL)
+ return NULL;
+
+ // Get event type
+ DBEVENTINFO dbeTmp = {0};
+ DBEVENTINFO *dbe;
+ if (hep->dbe != NULL && hep->dbe->cbBlob != NULL)
+ {
+ dbe = hep->dbe;
+ }
+ else
+ {
+ dbeTmp.cbSize = sizeof(dbeTmp);
+ dbeTmp.cbBlob = CallService(MS_DB_EVENT_GETBLOBSIZE, (LPARAM) hep->hDbEvent, 0);
+ if (dbeTmp.cbBlob <= 0)
+ return NULL;
+ if (dbeTmp.cbBlob > 0)
+ dbeTmp.pBlob = (PBYTE) malloc(dbeTmp.cbBlob);
+
+ if (CallService(MS_DB_EVENT_GET, (LPARAM) hep->hDbEvent, (WPARAM) &dbeTmp) != 0)
+ {
+ free(dbeTmp.pBlob);
+ return NULL;
+ }
+ dbe = &dbeTmp;
+ }
+
+ // Get handler
+ HISTORY_EVENT_HANDLER *heh = GetHandler(dbe->eventType);
+ if (heh == NULL)
+ {
+ if (hep->dbe != dbe && dbe->pBlob != NULL)
+ free(dbe->pBlob);
+ return NULL;
+ }
+
+ HANDLE hContact = (HANDLE) CallService(MS_DB_EVENT_GETCONTACT, (WPARAM) hep->hDbEvent, 0);
+
+ if (ItsTimeToDelete(hContact, hep->hDbEvent, dbe))
+ AppendHistoryEvent(hContact, hep->hDbEvent);
+
+
+ // Get text
+ void *ret;
+ if (hep->format & HISTORYEVENTS_FORMAT_CHAR)
+ {
+ if (heh->supports & HISTORYEVENTS_FORMAT_CHAR)
+ {
+ ret = heh->pfGetHistoryEventText(hContact, hep->hDbEvent, dbe, HISTORYEVENTS_FORMAT_CHAR);
+ }
+ else
+ {
+ wchar_t *tmp = (wchar_t *) heh->pfGetHistoryEventText(hContact, hep->hDbEvent, dbe, HISTORYEVENTS_FORMAT_WCHAR);
+ ret = mir_u2a(tmp);
+ mir_free(tmp);
+ }
+ }
+ else if (hep->format & HISTORYEVENTS_FORMAT_WCHAR)
+ {
+ if (heh->supports & HISTORYEVENTS_FORMAT_WCHAR)
+ {
+ ret = heh->pfGetHistoryEventText(hContact, hep->hDbEvent, dbe, HISTORYEVENTS_FORMAT_WCHAR);
+ }
+ else
+ {
+ char *tmp = (char *) heh->pfGetHistoryEventText(hContact, hep->hDbEvent, dbe, HISTORYEVENTS_FORMAT_CHAR);
+ ret = mir_a2u(tmp);
+ mir_free(tmp);
+ }
+ }
+ else if (hep->format & HISTORYEVENTS_FORMAT_RICH_TEXT)
+ {
+ if (heh->supports & HISTORYEVENTS_FORMAT_RICH_TEXT)
+ {
+ ret = heh->pfGetHistoryEventText(hContact, hep->hDbEvent, dbe, HISTORYEVENTS_FORMAT_RICH_TEXT);
+ }
+ else if (heh->supports & HISTORYEVENTS_FORMAT_WCHAR)
+ {
+ wchar_t *tmp = (wchar_t *) heh->pfGetHistoryEventText(hContact, hep->hDbEvent, dbe, HISTORYEVENTS_FORMAT_WCHAR);
+ ret = ConvertToRTF(tmp);
+ mir_free(tmp);
+ }
+ else
+ {
+ char *tmp = (char *) heh->pfGetHistoryEventText(hContact, hep->hDbEvent, dbe, HISTORYEVENTS_FORMAT_CHAR);
+ ret = ConvertToRTF(tmp);
+ mir_free(tmp);
+ }
+ }
+
+ if (hep->dbe != dbe && dbe->pBlob != NULL)
+ free(dbe->pBlob);
+ return (int) ret;
+}
+
+
+int ServiceReleaseText(WPARAM wParam, LPARAM lParam)
+{
+ mir_free((void *) wParam);
+ return 0;
+}
+
+
+void GetTemplare(Buffer<TCHAR> *buffer, HISTORY_EVENT_HANDLER *heh, int templ)
+{
+ DBVARIANT dbv;
+
+ char setting[128];
+ mir_snprintf(setting, MAX_REGS(setting), "%d_%d_" TEMPLATE_TEXT, heh->eventType, templ);
+
+ if (!DBGetContactSettingTString(NULL, heh->module == NULL ? MODULE_NAME : heh->module, setting, &dbv))
+ {
+ buffer->append(dbv.ptszVal);
+ DBFreeVariant(&dbv);
+ }
+ else
+ {
+ // Get default
+ char *tmp = heh->templates[templ];
+ tmp = strchr(tmp, '\n');
+ if (tmp == NULL)
+ return;
+ tmp++;
+ char *end = strchr(tmp, '\n');
+ size_t len = (end == NULL ? strlen(tmp) : end - tmp);
+
+ buffer->append(tmp, len);
+ }
+}
+
+
+HANDLE GetMetaContact(HANDLE hContact)
+{
+ if (!ServiceExists(MS_MC_GETMETACONTACT))
+ return NULL;
+
+ return (HANDLE) CallService(MS_MC_GETMETACONTACT, (WPARAM) hContact, 0);
+}
+
+
+int ServiceAddToHistory(WPARAM wParam, LPARAM lParam)
+{
+ HISTORY_EVENT_ADD * heaIn = (HISTORY_EVENT_ADD *) wParam;
+ if (heaIn == NULL || (heaIn->cbSize < sizeof(HISTORY_EVENT_ADD) && heaIn->cbSize != SIZEOF_HISTORY_EVENT_ADD_V1) || heaIn->templateNum < 0)
+ return NULL;
+ HISTORY_EVENT_ADD hea = {0};
+ memmove(&hea, heaIn, heaIn->cbSize);
+
+ HISTORY_EVENT_HANDLER *heh = GetHandler(hea.eventType);
+ if (heh == NULL || heh->numTemplates < 1 || heh->numTemplates < hea.templateNum)
+ return NULL;
+
+ if (!GetSettingBool(heh, hea.templateNum, TEMPLATE_ENABLED, TRUE))
+ return NULL;
+
+ if (heh->flags & HISTORYEVENTS_FLAG_ONLY_LOG_IF_SRMM_OPEN)
+ if (!MsgWndOpen(hea.hContact))
+ return NULL;
+
+ Buffer<TCHAR> templ;
+ GetTemplare(&templ, heh, hea.templateNum);
+ templ.pack();
+
+ Buffer<TCHAR> buffer;
+ ReplaceTemplate(&buffer, hea.hContact, templ.str, hea.variables, hea.numVariables);
+ buffer.pack();
+ if (buffer.str == NULL)
+ return NULL;
+
+ char *text = mir_utf8encodeT(buffer.str);
+ buffer.free();
+
+ DBEVENTINFO event = { 0 };
+ event.cbSize = sizeof(event);
+ event.eventType = heh->eventType;
+ event.flags = DBEF_UTF | hea.flags;
+ event.timestamp = (hea.timestamp == 0 ? (DWORD) time(NULL) : hea.timestamp);
+ event.szModule = heh->module;
+
+ size_t size = strlen(text) + 1;
+
+ if (hea.additionalDataSize > 0 && hea.additionalData != NULL)
+ {
+ text = (char *) mir_realloc(text, size + hea.additionalDataSize);
+ memmove(&text[size], hea.additionalData, hea.additionalDataSize);
+ size += hea.additionalDataSize;
+ }
+
+ event.pBlob = (PBYTE) text;
+ event.cbBlob = size;
+
+ HANDLE ret = (HANDLE) CallService(MS_DB_EVENT_ADD, (WPARAM) hea.hContact, (LPARAM) &event);
+
+ if (hea.addToMetaToo)
+ {
+ HANDLE hMeta = GetMetaContact(hea.hContact);
+ if (hMeta != NULL)
+ CallService(MS_DB_EVENT_ADD, (WPARAM) hMeta, (LPARAM) &event);
+ }
+
+ mir_free(text);
+
+ return (int) ret;
+}
+
+int ServiceIsEnabledTemplate(WPARAM wParam, LPARAM lParam)
+{
+ WORD eventType = wParam;
+ int templateNum = lParam;
+
+ HISTORY_EVENT_HANDLER *heh = GetHandler(eventType);
+ if (heh == NULL || heh->numTemplates < 1 || heh->numTemplates < templateNum)
+ return FALSE;
+
+ return GetSettingBool(heh, templateNum, TEMPLATE_ENABLED, TRUE);
+}
+
+
+char * TrimRight(char *str) {
+ int e;
+ for(e = strlen(str)-1; e >= 0 && (str[e] == ' ' || str[e] == '\t' || str[e] == '\r' || str[e] == '\n'); e--) ;
+ str[e+1] = '\0';
+ return str;
+}
+
+wchar_t * TrimRight(wchar_t *str) {
+ int e;
+ for(e = lstrlenW(str)-1; e >= 0 && (str[e] == L' ' || str[e] == L'\t' || str[e] == L'\r' || str[e] == L'\n'); e--) ;
+ str[e+1] = L'\0';
+ return str;
+}
+
+
+void * GetMessageHistoryEventText(HANDLE hContact, HANDLE hDbEvent, DBEVENTINFO *dbe, int format)
+{
+ void *ret;
+
+ if (format & HISTORYEVENTS_FORMAT_CHAR)
+ {
+ ret = TrimRight(DbGetEventTextA(dbe, CP_ACP));
+ }
+ else if (format & HISTORYEVENTS_FORMAT_WCHAR)
+ {
+ ret = TrimRight(DbGetEventTextW(dbe, CP_ACP));
+ }
+ else if (format & HISTORYEVENTS_FORMAT_RICH_TEXT)
+ {
+ TCHAR *tmp = TrimRight(DbGetEventTextT(dbe, CP_ACP));
+
+ // Check if there is any extra info
+ BYTE *extra = NULL;
+ // Removed by now
+ //if (dbe->flags & DBEF_UTF)
+ //{
+ // size_t size = strlen((char *) dbe->pBlob) + 1;
+ // if (size < dbe->cbBlob)
+ // extra = &dbe->pBlob[size];
+ //}
+
+ ret = ConvertToRTF(tmp, GetHandler(dbe->eventType), extra);
+ mir_free(tmp);
+ }
+
+ return ret;
+}
+
+
+void * GetDefaultHistoryEventText(HANDLE hContact, HANDLE hDbEvent, DBEVENTINFO *dbe, int format)
+{
+ void *ret;
+
+ if (format & HISTORYEVENTS_FORMAT_CHAR)
+ {
+ ret = TrimRight(DbGetEventTextA(dbe, CP_ACP));
+ }
+ else if (format & HISTORYEVENTS_FORMAT_WCHAR)
+ {
+ ret = TrimRight(DbGetEventTextW(dbe, CP_ACP));
+ }
+ else if (format & HISTORYEVENTS_FORMAT_RICH_TEXT)
+ {
+ TCHAR *tmp = TrimRight(DbGetEventTextT(dbe, CP_ACP));
+ ret = ConvertToRTF(tmp);
+ mir_free(tmp);
+ }
+
+ return ret;
+}
+
+
+void * GetFileHistoryEventText(HANDLE hContact, HANDLE hDbEvent, DBEVENTINFO *dbe, int format)
+{
+ void *ret;
+
+ char tmp[1024];
+ char *filename = (char *) dbe->pBlob + sizeof(DWORD);
+ char *descr = filename + lstrlenA(filename) + 1;
+ if (*descr != 0)
+ mir_snprintf(tmp, MAX_REGS(tmp), "%s (%s)", filename, descr);
+ else
+ mir_snprintf(tmp, MAX_REGS(tmp), "%s", filename);
+
+ if (format & HISTORYEVENTS_FORMAT_CHAR)
+ {
+ ret = mir_strdup(tmp);
+ }
+ else if (format & HISTORYEVENTS_FORMAT_WCHAR)
+ {
+ ret = mir_a2u(tmp);
+ }
+ else if (format & HISTORYEVENTS_FORMAT_RICH_TEXT)
+ {
+ ret = ConvertToRTF(tmp);
+ }
+
+ return ret;
+}
+
+
+#define MESSAGE_INFO_FORMAT 1
+#define MESSAGE_FORMAT_BOLD 1
+#define MESSAGE_FORMAT_ITALIC 2
+#define MESSAGE_FORMAT_UNDERLINE 4
+#define MESSAGE_FORMAT_STRIKETHROUGH 8
+#define MESSAGE_FORMAT_COLOR 16
+#define MESSAGE_FORMAT_FONT 32
+
+
+void ReadNextExtraInfo(BYTE *&extra, BYTE &format, int &start, BYTE &r, BYTE &g, BYTE &b, char *&font)
+{
+ format = 0;
+
+ if (extra == NULL)
+ return;
+
+ if (*extra != MESSAGE_INFO_FORMAT)
+ return;
+ extra++;
+
+ format = *extra; extra++;
+ start = *((DWORD *) extra); extra+=4;
+
+ if (format & MESSAGE_FORMAT_COLOR)
+ {
+ r = *extra; extra++;
+ g = *extra; extra++;
+ b = *extra; extra++;
+ }
+
+ if (format & MESSAGE_FORMAT_FONT)
+ {
+ font = (char *) extra;
+ extra += strlen(font);
+ }
+
+ if (*extra != 0)
+ OutputDebugStringA("Wrong extra format");
+
+ extra++;
+}
+
+
+template<class T>
+char * ConvertToRTF(T *line, HISTORY_EVENT_HANDLER *heh, BYTE *extra)
+{
+ Buffer<char> buffer;
+ buffer.append("{\\uc1 ", 6);
+
+ BYTE format;
+ int start;
+ BYTE r, g, b;
+ char *font;
+
+ ReadNextExtraInfo(extra, format, start, r, g, b, font);
+
+ BOOL insideBlock = FALSE;
+ T *orig = line;
+ for (; *line; line++)
+ {
+ if (format != 0 && line - orig == start)
+ {
+ if (insideBlock)
+ buffer.append('}');
+
+ if ((heh == NULL || GetSettingBool(heh, RESPECT_TEXT_FORMAT, TRUE))
+ && (format & (MESSAGE_FORMAT_BOLD | MESSAGE_FORMAT_ITALIC
+ | MESSAGE_FORMAT_UNDERLINE | MESSAGE_FORMAT_STRIKETHROUGH)))
+ {
+ buffer.append('{');
+ if (format & MESSAGE_FORMAT_BOLD)
+ buffer.append("\\b", 2);
+ if (format & MESSAGE_FORMAT_ITALIC)
+ buffer.append("\\i", 2);
+ if (format & MESSAGE_FORMAT_UNDERLINE)
+ buffer.append("\\ul", 3);
+ if (format & MESSAGE_FORMAT_STRIKETHROUGH)
+ buffer.append("\\strike", 7);
+ buffer.append(' ');
+
+ insideBlock = TRUE;
+ }
+
+ ReadNextExtraInfo(extra, format, start, r, g, b, font);
+ }
+
+ if (*line == (T)'\r' && line[1] == (T)'\n') {
+ buffer.append("\\line ", 6);
+ line++;
+ }
+ else if (*line == (T)'\n') {
+ buffer.append("\\line ", 6);
+ }
+ else if (*line == (T)'\t') {
+ buffer.append("\\tab ", 5);
+ }
+ else if (*line == (T)'\\' || *line == (T)'{' || *line == (T)'}') {
+ buffer.append('\\');
+ buffer.append((char) *line);
+ }
+ else if (*line < 128) {
+ buffer.append((char) *line);
+ }
+ else
+ buffer.appendPrintf("\\u%d ?", *line);
+ }
+
+ if (insideBlock)
+ buffer.append('}');
+ buffer.append('}');
+ buffer.pack();
+
+ return buffer.detach();
+}
+
+
+BOOL MsgWndOpen(HANDLE hContact)
+{
+ if (ServiceExists("SRMsg_MOD/MessageDialogOpened")) // tabSRMM service
+ {
+ return CallService("SRMsg_MOD/MessageDialogOpened", (WPARAM) hContact, 0);
+ }
+ else
+ {
+ MessageWindowData mwd = {0};
+ MessageWindowInputData mwid = {0};
+
+ mwid.cbSize = sizeof(MessageWindowInputData);
+ mwid.hContact = hContact;
+ mwid.uFlags = MSG_WINDOW_UFLAG_MSG_BOTH;
+ mwd.cbSize = sizeof(MessageWindowData);
+ mwd.hContact = hContact;
+ if (!CallService(MS_MSG_GETWINDOWDATA, (WPARAM) &mwid, (LPARAM) &mwd))
+ if (mwd.hwndWindow != NULL && (mwd.uState & MSG_WINDOW_STATE_EXISTS))
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+BOOL ItsTimeToDelete(HANDLE hContact, HANDLE hDbEvent, DBEVENTINFO *dbe, int eventNum)
+{
+ // Get all info
+ DBEVENTINFO dbeTmp = {0};
+ if (dbe == NULL)
+ {
+ dbeTmp.cbSize = sizeof(dbeTmp);
+ if (CallService(MS_DB_EVENT_GET, (LPARAM) hDbEvent, (WPARAM) &dbeTmp) != 0)
+ return FALSE;
+ dbe = &dbeTmp;
+ }
+
+ // Get handler
+ HISTORY_EVENT_HANDLER *heh = GetHandler(dbe->eventType);
+ if (heh == NULL)
+ return FALSE;
+
+ DWORD keepInDB = KEEP_FLAG(heh->flags);
+
+ if (keepInDB != HISTORYEVENTS_FLAG_KEEP_DONT && !(dbe->flags & DBEF_SENT) && !(dbe->flags & DBEF_READ))
+ return FALSE;
+
+ switch(keepInDB)
+ {
+ case HISTORYEVENTS_FLAG_KEEP_DONT:
+ {
+ return TRUE;
+ }
+ case HISTORYEVENTS_FLAG_KEEP_ONE_YEAR:
+ {
+ return dbe->timestamp + 365 * 24 * 60 * 60 < (DWORD) time(NULL);
+ }
+ case HISTORYEVENTS_FLAG_KEEP_SIX_MONTHS:
+ {
+ return dbe->timestamp + (6 * 30 + 3) * 24 * 60 * 60 < (DWORD) time(NULL);
+ }
+ case HISTORYEVENTS_FLAG_KEEP_ONE_MONTH:
+ {
+ return dbe->timestamp + 31 * 24 * 60 * 60 < (DWORD) time(NULL);
+ }
+ case HISTORYEVENTS_FLAG_KEEP_ONE_WEEK:
+ {
+ return dbe->timestamp + 7 * 24 * 60 * 60 < (DWORD) time(NULL);
+ }
+ case HISTORYEVENTS_FLAG_KEEP_ONE_DAY:
+ {
+ return dbe->timestamp + 24 * 60 * 60 < (DWORD) time(NULL);
+ }
+ case HISTORYEVENTS_FLAG_KEEP_MAX_TEN:
+ {
+ return (eventNum > 10);
+ }
+ case HISTORYEVENTS_FLAG_KEEP_MAX_HUNDRED:
+ {
+ return (eventNum > 100);
+ }
+ case HISTORYEVENTS_FLAG_KEEP_FOR_SRMM:
+ {
+ // If it is open, let it be
+ if (MsgWndOpen(hContact))
+ return FALSE;
+
+ // Check if it will be shown next time
+ int load = DBGetContactSettingByte(NULL, "SRMM", "LoadHistory", LOADHISTORY_UNREAD);
+ switch(load)
+ {
+ case LOADHISTORY_TIME:
+ {
+ DWORD dt = DBGetContactSettingWord(NULL, "SRMM", "LoadTime", 10) * 60;
+ return dbe->timestamp + dt < (DWORD) time(NULL);
+ }
+ case LOADHISTORY_COUNT:
+ {
+ int count = DBGetContactSettingWord(NULL, "SRMM", "LoadCount", 10);
+
+ HANDLE tmp = (HANDLE) CallService(MS_DB_EVENT_FINDLAST, (WPARAM) hContact, 0);
+
+ int num = 1;
+ while(tmp != NULL)
+ {
+ if (hDbEvent == tmp)
+ return FALSE;
+
+ // Get event
+ DBEVENTINFO dbeTmp2 = {0};
+ dbeTmp2.cbSize = sizeof(dbeTmp2);
+ if (CallService(MS_DB_EVENT_GET, (LPARAM) tmp, (WPARAM) &dbeTmp2) != 0)
+ continue;
+
+ // Get handler
+ HISTORY_EVENT_HANDLER *heh2 = GetHandler(dbeTmp2.eventType);
+ if (heh2 == NULL)
+ continue;
+
+ if (!(heh2->flags & HISTORYEVENTS_FLAG_SHOW_IM_SRMM))
+ continue;
+
+ num++;
+ if (num > count)
+ break;
+
+ tmp = (HANDLE) CallService(MS_DB_EVENT_FINDPREV, (WPARAM) tmp, 0);
+ }
+ return TRUE;
+ }
+ case LOADHISTORY_UNREAD:
+ {
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+ }
+ }
+
+ return FALSE;
+}
+
+
+void wait(int time)
+{
+ if (!shuttingDown)
+ WaitForSingleObject(hDeleteThreadEvent, time);
+}
+
+
+void DoFullPass()
+{
+ DWORD lastFulPass = DBGetContactSettingDword(NULL, MODULE_NAME, "LastFullPass", 0);
+ DWORD now = (DWORD) time(NULL);
+ if (now < lastFulPass + 24 * 60 * 60) // 1 day
+ return;
+
+ // Start after 1 minute
+ wait(60 * 1000);
+ if (shuttingDown)
+ return;
+
+// DWORD t = GetTickCount();
+
+ int *counters = (int *) malloc(sizeof(int) * handlers.getCount());
+
+ int count = 0;
+ HANDLE hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDFIRST, 0, 0);
+ while (hContact != NULL && !shuttingDown)
+ {
+ memset(counters, 0, sizeof(int) * handlers.getCount());
+
+ HANDLE hDbEvent = (HANDLE) CallService(MS_DB_EVENT_FINDLAST, (WPARAM) hContact, 0);
+ while(hDbEvent != NULL && !shuttingDown)
+ {
+ HANDLE hDbEventProx = (HANDLE) CallService(MS_DB_EVENT_FINDPREV, (WPARAM) hDbEvent, 0);
+
+ DBEVENTINFO dbe = {0};
+ dbe.cbSize = sizeof(dbe);
+ if (CallService(MS_DB_EVENT_GET, (LPARAM) hDbEvent, (WPARAM) &dbe) == 0)
+ {
+ int pos = GetHandlerID(dbe.eventType);
+ if (pos >= 0)
+ {
+ counters[pos]++;
+
+ if (ItsTimeToDelete(hContact, hDbEvent, &dbe, counters[pos]))
+ {
+ CallService(MS_DB_EVENT_DELETE, (WPARAM) hContact, (LPARAM) hDbEvent);
+ wait(10);
+ }
+ }
+ }
+
+ if (++count > 30)
+ {
+ count = 0;
+ wait(10);
+ }
+
+ hDbEvent = hDbEventProx;
+ }
+
+ hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDNEXT, (WPARAM) hContact, 0);
+ }
+
+ free(counters);
+
+// t = GetTickCount() - t;
+// char tmp[128];
+// mir_snprintf(tmp, 128, "TIME: %d\n", (int) t);
+// OutputDebugStringA(tmp);
+
+ if (!shuttingDown)
+ DBWriteContactSettingDword(NULL, MODULE_NAME, "LastFullPass", now);
+}
+
+
+HistoryEventNode *GetFirstHistoryEvent()
+{
+ EnterCriticalSection(&cs);
+ HistoryEventNode *ret = firstHistoryEvent;
+ LeaveCriticalSection(&cs);
+ return ret;
+}
+
+
+HistoryEventNode *GetNextHistoryEvent(HistoryEventNode *node)
+{
+ EnterCriticalSection(&cs);
+ HistoryEventNode *ret = node->next;
+ LeaveCriticalSection(&cs);
+ return ret;
+}
+
+
+void DeleteHistoryEvent(HistoryEventNode *node, HistoryEventNode *prev)
+{
+ EnterCriticalSection(&cs);
+
+ if (prev != NULL)
+ prev->next = node->next;
+
+ if (firstHistoryEvent == node)
+ firstHistoryEvent = node->next;
+
+ if (lastHistoryEvent == node)
+ lastHistoryEvent = prev;
+
+ LeaveCriticalSection(&cs);
+
+ free(node);
+}
+
+
+void AppendHistoryEvent(HANDLE hContact, HANDLE hDbEvent)
+{
+ HistoryEventNode *node = (HistoryEventNode *) malloc(sizeof(HistoryEventNode));
+ node->hContact = hContact;
+ node->hDBEvent = hDbEvent;
+ node->next = NULL;
+
+ EnterCriticalSection(&cs);
+
+ if (lastHistoryEvent == NULL)
+ lastHistoryEvent = firstHistoryEvent = node;
+ else
+ lastHistoryEvent = lastHistoryEvent->next = node;
+
+ LeaveCriticalSection(&cs);
+
+ SetEvent(hDeleteThreadEvent);
+}
+
+
+DWORD WINAPI DeleteThread(LPVOID vParam)
+{
+ // First check if must pass through all contacts
+ DoFullPass();
+
+ HistoryEventNode *node = NULL;
+ HistoryEventNode *prev = NULL;
+
+ // Now delete the new events added
+ while(!shuttingDown)
+ {
+ // Process next
+ if (node == NULL)
+ node = GetFirstHistoryEvent();
+ else
+ node = GetNextHistoryEvent(node);
+
+ if (node == NULL)
+ {
+ wait(INFINITE);
+ }
+ else if (ItsTimeToDelete(node->hContact, node->hDBEvent))
+ {
+ int ret = CallService(MS_DB_EVENT_DELETE, (WPARAM) node->hContact, (LPARAM) node->hDBEvent);
+ DeleteHistoryEvent(node, prev);
+ node = prev;
+
+ // Breath baby, breath
+ wait(100);
+ }
+ prev = node;
+ }
+
+ shuttingDown = 2;
+ return 0;
+}
+
+
+int DbEventAdded(WPARAM wParam, LPARAM lParam)
+{
+ HANDLE hContact = (HANDLE) wParam;
+ HANDLE hDbEvent = (HANDLE) lParam;
+
+ // Get all info
+ DBEVENTINFO dbe = {0};
+ dbe.cbSize = sizeof(dbe);
+ if (CallService(MS_DB_EVENT_GET, (LPARAM) hDbEvent, (WPARAM) &dbe) != 0)
+ return 0;
+
+ // Get handler
+ HISTORY_EVENT_HANDLER *heh = GetHandler(dbe.eventType);
+ if (heh == NULL)
+ return 0;
+
+ if (KEEP_FLAG(heh->flags) != HISTORYEVENTS_FLAG_KEEP_FOR_SRMM)
+ return 0;
+
+ AppendHistoryEvent(hContact, hDbEvent);
+
+ return 0;
+}
+
+
+int ServiceGetCount(WPARAM wParam, LPARAM lParam)
+{
+ return handlers.getCount();
+}
+
+
+int ServiceGetEvent(WPARAM wParam, LPARAM lParam)
+{
+ int pos = (int) wParam;
+ if (pos >= 0)
+ {
+ if (pos < handlers.getCount())
+ return (int) handlers[pos];
+ else
+ return NULL;
+ }
+
+ int type = (int) lParam;
+ if (type != -1)
+ return (int) GetHandler(type);
+
+ return NULL;
+}
+
+
+int SortHandlers(const HISTORY_EVENT_HANDLER *type1, const HISTORY_EVENT_HANDLER *type2)
+{
+ if ((type1->flags & HISTORYEVENTS_FLAG_DEFAULT) && !(type2->flags & HISTORYEVENTS_FLAG_DEFAULT))
+ return -1;
+ else if (!(type1->flags & HISTORYEVENTS_FLAG_DEFAULT) && (type2->flags & HISTORYEVENTS_FLAG_DEFAULT))
+ return 1;
+ else
+ return stricmp(type1->description, type2->description);
+} \ No newline at end of file
diff --git a/Plugins/historyevents/historyevents.dsp b/Plugins/historyevents/historyevents.dsp
new file mode 100644
index 0000000..6643982
--- /dev/null
+++ b/Plugins/historyevents/historyevents.dsp
@@ -0,0 +1,231 @@
+# Microsoft Developer Studio Project File - Name="historyevents" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=historyevents - Win32 Release
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "historyevents.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "historyevents.mak" CFG="historyevents - Win32 Release"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "historyevents - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "historyevents - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "historyevents - Win32 Unicode Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "historyevents - Win32 Unicode Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "historyevents - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MD /W3 /GX /O1 /YX /FD /c
+# SUBTRACT BASE CPP /Fr
+# ADD CPP /nologo /G4 /MT /W3 /GX /O2 /Ob0 /I "../../include" /I "sdk" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /Fr /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x417 /d "NDEBUG"
+# ADD RSC /l 0x417 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 user32.lib shell32.lib wininet.lib gdi32.lib /nologo /base:"0x67100000" /dll /machine:I386 /filealign:0x200
+# SUBTRACT BASE LINK32 /pdb:none /map
+# ADD LINK32 gdi32.lib kernel32.lib user32.lib ole32.lib oleaut32.lib /nologo /base:"0x3EC70000" /dll /map /debug /machine:I386 /out:"..\..\bin\release\Plugins\aa_historyevents.dll" /filealign:0x200 /ALIGN:4096 /ignore:4108
+# SUBTRACT LINK32 /profile /pdb:none
+
+!ELSEIF "$(CFG)" == "historyevents - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Ignore_Export_Lib 0
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /G4 /MT /W3 /GX /O2 /Ob0 /I "../../include" /FR /YX /FD /c
+# ADD CPP /nologo /G4 /MTd /W3 /GX /ZI /Od /I "../../include" /I "sdk" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /FR /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x417 /d "NDEBUG"
+# ADD RSC /l 0x417 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 comctl32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 /out:"..\..bin\release\Plugins\historyevents.dll" /filealign:0x200 /ALIGN:4096 /ignore:4108
+# SUBTRACT BASE LINK32 /profile /pdb:none
+# ADD LINK32 gdi32.lib kernel32.lib user32.lib ole32.lib oleaut32.lib /nologo /base:"0x3EC70000" /dll /incremental:yes /debug /machine:I386 /out:"..\..\bin\debug\Plugins\aa_historyevents.dll" /filealign:0x200 /ALIGN:4096 /ignore:4108
+# SUBTRACT LINK32 /profile /pdb:none
+
+!ELSEIF "$(CFG)" == "historyevents - Win32 Unicode Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "historyevents___Win32_Unicode_Debug"
+# PROP BASE Intermediate_Dir "historyevents___Win32_Unicode_Debug"
+# PROP BASE Ignore_Export_Lib 0
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Unicode_Debug"
+# PROP Intermediate_Dir "Unicode_Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /G4 /MTd /W3 /GX /ZI /Od /I "../../include" /FR /YX /FD /c
+# ADD CPP /nologo /G4 /MTd /W3 /GX /ZI /Od /I "../../include" /I "sdk" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_UNICODE" /D "UNICODE" /D "_USRDLL" /FR /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x417 /d "NDEBUG"
+# ADD RSC /l 0x417 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 comctl32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /base:"0x32100000" /dll /incremental:yes /debug /machine:I386 /out:"..\..\bin\debug\Plugins\historyevents.dll" /filealign:0x200 /ALIGN:4096 /ignore:4108
+# SUBTRACT BASE LINK32 /profile /pdb:none
+# ADD LINK32 gdi32.lib kernel32.lib user32.lib ole32.lib oleaut32.lib /nologo /base:"0x3EC70000" /dll /incremental:yes /debug /machine:I386 /out:"..\..\bin\debug unicode\Plugins\aa_historyeventsW.dll" /filealign:0x200 /ALIGN:4096 /ignore:4108
+# SUBTRACT LINK32 /profile /pdb:none
+
+!ELSEIF "$(CFG)" == "historyevents - Win32 Unicode Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "historyevents___Win32_Unicode_Release"
+# PROP BASE Intermediate_Dir "historyevents___Win32_Unicode_Release"
+# PROP BASE Ignore_Export_Lib 0
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Unicode_Release"
+# PROP Intermediate_Dir "Unicode_Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /G4 /MT /W3 /GX /O2 /Ob0 /I "../../include" /Fr /YX /FD /c
+# ADD CPP /nologo /G4 /MT /W3 /GX /O2 /Ob0 /I "../../include" /I "sdk" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_UNICODE" /D "UNICODE" /D "_USRDLL" /Fr /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x417 /d "NDEBUG"
+# ADD RSC /l 0x417 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 comctl32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /base:"0x32100000" /dll /map /machine:I386 /out:"..\..\bin\release\Plugins\historyevents.dll" /filealign:0x200 /ALIGN:4096 /ignore:4108
+# SUBTRACT BASE LINK32 /profile /pdb:none
+# ADD LINK32 gdi32.lib kernel32.lib user32.lib ole32.lib oleaut32.lib /nologo /base:"0x3EC70000" /dll /map /debug /machine:I386 /out:"..\..\bin\release unicode\Plugins\aa_historyeventsW.dll" /filealign:0x200 /ALIGN:4096 /ignore:4108
+# SUBTRACT LINK32 /profile /pdb:none
+
+!ENDIF
+
+# Begin Target
+
+# Name "historyevents - Win32 Release"
+# Name "historyevents - Win32 Debug"
+# Name "historyevents - Win32 Unicode Debug"
+# Name "historyevents - Win32 Unicode Release"
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=.\commons.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\m_historyevents.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\utils\mir_buffer.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\utils\mir_icons.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\utils\mir_memory.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\options.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\resource.h
+# End Source File
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
+# Begin Source File
+
+SOURCE=.\resource.rc
+# End Source File
+# End Group
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=.\historyevents.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\utils\mir_icons.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\options.cpp
+# End Source File
+# End Group
+# Begin Group "Docs"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=.\Docs\historyevents_changelog.txt
+# End Source File
+# Begin Source File
+
+SOURCE=.\Docs\historyevents_readme.txt
+# End Source File
+# Begin Source File
+
+SOURCE=.\Docs\historyevents_version.txt
+# End Source File
+# End Group
+# End Target
+# End Project
diff --git a/Plugins/historyevents/historyevents.dsw b/Plugins/historyevents/historyevents.dsw
new file mode 100644
index 0000000..f999a8f
--- /dev/null
+++ b/Plugins/historyevents/historyevents.dsw
@@ -0,0 +1,29 @@
+Microsoft Developer Studio Workspace File, Format Version 6.00
+# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
+
+###############################################################################
+
+Project: "historyevents"=".\historyevents.dsp" - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Global:
+
+Package=<5>
+{{{
+}}}
+
+Package=<3>
+{{{
+}}}
+
+###############################################################################
+
diff --git a/Plugins/historyevents/historyevents.sln b/Plugins/historyevents/historyevents.sln
new file mode 100644
index 0000000..8cf097f
--- /dev/null
+++ b/Plugins/historyevents/historyevents.sln
@@ -0,0 +1,26 @@
+
+Microsoft Visual Studio Solution File, Format Version 9.00
+# Visual Studio 2005
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "historyevents", "historyevents.vcproj", "{20DC63C3-E2DE-4E0E-8DBB-133F32CFDE1C}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Win32 = Debug|Win32
+ Release|Win32 = Release|Win32
+ Unicode Debug|Win32 = Unicode Debug|Win32
+ Unicode Release|Win32 = Unicode Release|Win32
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {20DC63C3-E2DE-4E0E-8DBB-133F32CFDE1C}.Debug|Win32.ActiveCfg = Debug|Win32
+ {20DC63C3-E2DE-4E0E-8DBB-133F32CFDE1C}.Debug|Win32.Build.0 = Debug|Win32
+ {20DC63C3-E2DE-4E0E-8DBB-133F32CFDE1C}.Release|Win32.ActiveCfg = Release|Win32
+ {20DC63C3-E2DE-4E0E-8DBB-133F32CFDE1C}.Release|Win32.Build.0 = Release|Win32
+ {20DC63C3-E2DE-4E0E-8DBB-133F32CFDE1C}.Unicode Debug|Win32.ActiveCfg = Unicode Debug|Win32
+ {20DC63C3-E2DE-4E0E-8DBB-133F32CFDE1C}.Unicode Debug|Win32.Build.0 = Unicode Debug|Win32
+ {20DC63C3-E2DE-4E0E-8DBB-133F32CFDE1C}.Unicode Release|Win32.ActiveCfg = Unicode Release|Win32
+ {20DC63C3-E2DE-4E0E-8DBB-133F32CFDE1C}.Unicode Release|Win32.Build.0 = Unicode Release|Win32
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/Plugins/historyevents/historyevents.vcproj b/Plugins/historyevents/historyevents.vcproj
new file mode 100644
index 0000000..74cbe13
--- /dev/null
+++ b/Plugins/historyevents/historyevents.vcproj
@@ -0,0 +1,635 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="8,00"
+ Name="historyevents"
+ ProjectGUID="{20DC63C3-E2DE-4E0E-8DBB-133F32CFDE1C}"
+ RootNamespace="historyevents"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory=".\Release"
+ IntermediateDirectory=".\Release"
+ ConfigurationType="2"
+ InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC60.vsprops"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="2"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ PreprocessorDefinitions="NDEBUG"
+ MkTypLibCompatible="true"
+ SuppressStartupBanner="true"
+ TargetEnvironment="1"
+ TypeLibraryName=".\Release/historyevents.tlb"
+ HeaderFileName=""
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ InlineFunctionExpansion="0"
+ AdditionalIncludeDirectories="../../include,sdk"
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE"
+ StringPooling="true"
+ RuntimeLibrary="0"
+ EnableFunctionLevelLinking="true"
+ PrecompiledHeaderFile=".\Release/historyevents.pch"
+ AssemblerListingLocation=".\Release/"
+ ObjectFile=".\Release/"
+ ProgramDataBaseFileName=".\Release/"
+ BrowseInformation="2"
+ BrowseInformationFile=".\Release/"
+ WarningLevel="3"
+ SuppressStartupBanner="true"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="NDEBUG"
+ Culture="1047"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalOptions="/ALIGN:4096 /filealign:0x200 /ignore:4108 "
+ OutputFile="..\..\bin\release\Plugins\aa_historyevents.dll"
+ LinkIncremental="1"
+ SuppressStartupBanner="true"
+ ProgramDatabaseFile=".\Release/aa_historyevents.pdb"
+ GenerateMapFile="true"
+ MapFileName=".\Release/aa_historyevents.map"
+ BaseAddress="0x3EC20000"
+ ImportLibrary=".\Release/aa_historyevents.lib"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ SuppressStartupBanner="true"
+ OutputFile=".\Release/historyevents.bsc"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Unicode Release|Win32"
+ OutputDirectory=".\Unicode_Release"
+ IntermediateDirectory=".\Unicode_Release"
+ ConfigurationType="2"
+ InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC60.vsprops"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ PreprocessorDefinitions="NDEBUG"
+ MkTypLibCompatible="true"
+ SuppressStartupBanner="true"
+ TargetEnvironment="1"
+ TypeLibraryName=".\Unicode_Release/historyevents.tlb"
+ HeaderFileName=""
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ InlineFunctionExpansion="0"
+ AdditionalIncludeDirectories="../../include,sdk"
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;UNICODE;_USRDLL;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE"
+ StringPooling="true"
+ RuntimeLibrary="0"
+ EnableFunctionLevelLinking="true"
+ PrecompiledHeaderFile=".\Unicode_Release/historyevents.pch"
+ AssemblerListingLocation=".\Unicode_Release/"
+ ObjectFile=".\Unicode_Release/"
+ ProgramDataBaseFileName=".\Unicode_Release/"
+ BrowseInformation="2"
+ BrowseInformationFile=".\Unicode_Release/"
+ WarningLevel="3"
+ SuppressStartupBanner="true"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="NDEBUG"
+ Culture="1047"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalOptions="/ALIGN:4096 /filealign:0x200 /ignore:4108 "
+ OutputFile="..\..\bin\release unicode\Plugins\aa_historyeventsW.dll"
+ LinkIncremental="1"
+ SuppressStartupBanner="true"
+ ProgramDatabaseFile=".\Unicode_Release/aa_historyeventsW.pdb"
+ GenerateMapFile="true"
+ MapFileName=".\Unicode_Release/aa_historyeventsW.map"
+ BaseAddress="0x3EC20000"
+ ImportLibrary=".\Unicode_Release/aa_historyeventsW.lib"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ SuppressStartupBanner="true"
+ OutputFile=".\Unicode_Release/historyevents.bsc"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory=".\Debug"
+ IntermediateDirectory=".\Debug"
+ ConfigurationType="2"
+ InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC60.vsprops"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="2"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ PreprocessorDefinitions="NDEBUG"
+ MkTypLibCompatible="true"
+ SuppressStartupBanner="true"
+ TargetEnvironment="1"
+ TypeLibraryName=".\Debug/historyevents.tlb"
+ HeaderFileName=""
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories="../../include,sdk"
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE"
+ RuntimeLibrary="1"
+ PrecompiledHeaderFile=".\Debug/historyevents.pch"
+ AssemblerListingLocation=".\Debug/"
+ ObjectFile=".\Debug/"
+ ProgramDataBaseFileName=".\Debug/"
+ BrowseInformation="1"
+ WarningLevel="3"
+ SuppressStartupBanner="true"
+ DebugInformationFormat="4"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="NDEBUG"
+ Culture="1047"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalOptions="/ALIGN:4096 /filealign:0x200 /ignore:4108 "
+ OutputFile="..\..\bin\debug\Plugins\aa_historyevents.dll"
+ LinkIncremental="2"
+ SuppressStartupBanner="true"
+ GenerateDebugInformation="true"
+ ProgramDatabaseFile=".\Debug/aa_historyevents.pdb"
+ BaseAddress="0x3EC20000"
+ ImportLibrary=".\Debug/aa_historyevents.lib"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ SuppressStartupBanner="true"
+ OutputFile=".\Debug/historyevents.bsc"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Unicode Debug|Win32"
+ OutputDirectory=".\Unicode_Debug"
+ IntermediateDirectory=".\Unicode_Debug"
+ ConfigurationType="2"
+ InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC60.vsprops"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ PreprocessorDefinitions="NDEBUG"
+ MkTypLibCompatible="true"
+ SuppressStartupBanner="true"
+ TargetEnvironment="1"
+ TypeLibraryName=".\Unicode_Debug/historyevents.tlb"
+ HeaderFileName=""
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories="../../include,sdk"
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;UNICODE;_USRDLL;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE"
+ RuntimeLibrary="1"
+ PrecompiledHeaderFile=".\Unicode_Debug/historyevents.pch"
+ AssemblerListingLocation=".\Unicode_Debug/"
+ ObjectFile=".\Unicode_Debug/"
+ ProgramDataBaseFileName=".\Unicode_Debug/"
+ BrowseInformation="1"
+ WarningLevel="3"
+ SuppressStartupBanner="true"
+ DebugInformationFormat="4"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="NDEBUG"
+ Culture="1047"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalOptions="/ALIGN:4096 /filealign:0x200 /ignore:4108 "
+ OutputFile="..\..\bin\debug unicode\Plugins\aa_historyeventsW.dll"
+ LinkIncremental="2"
+ SuppressStartupBanner="true"
+ GenerateDebugInformation="true"
+ ProgramDatabaseFile=".\Unicode_Debug/aa_historyeventsW.pdb"
+ BaseAddress="0x3EC20000"
+ ImportLibrary=".\Unicode_Debug/aa_historyeventsW.lib"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ SuppressStartupBanner="true"
+ OutputFile=".\Unicode_Debug/historyevents.bsc"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Header Files"
+ Filter="h;hpp;hxx;hm;inl"
+ >
+ <File
+ RelativePath="commons.h"
+ >
+ </File>
+ <File
+ RelativePath="m_historyevents.h"
+ >
+ </File>
+ <File
+ RelativePath="..\utils\mir_buffer.h"
+ >
+ </File>
+ <File
+ RelativePath="..\utils\mir_icons.h"
+ >
+ </File>
+ <File
+ RelativePath="..\utils\mir_memory.h"
+ >
+ </File>
+ <File
+ RelativePath="options.h"
+ >
+ </File>
+ <File
+ RelativePath="resource.h"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Resource Files"
+ Filter="ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
+ >
+ <File
+ RelativePath="resource.rc"
+ >
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Unicode Release|Win32"
+ >
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Unicode Debug|Win32"
+ >
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ </File>
+ </Filter>
+ <Filter
+ Name="Source Files"
+ Filter="cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+ >
+ <File
+ RelativePath="historyevents.cpp"
+ >
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Unicode Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Unicode Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="..\utils\mir_icons.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\utils\mir_memory.cpp"
+ >
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Unicode Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Unicode Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="options.cpp"
+ >
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Unicode Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Unicode Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ </File>
+ </Filter>
+ <Filter
+ Name="Docs"
+ >
+ <File
+ RelativePath="Docs\historyevents_changelog.txt"
+ >
+ </File>
+ <File
+ RelativePath="Docs\historyevents_readme.txt"
+ >
+ </File>
+ <File
+ RelativePath="Docs\historyevents_version.txt"
+ >
+ </File>
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/Plugins/historyevents/m_historyevents.h b/Plugins/historyevents/m_historyevents.h
new file mode 100644
index 0000000..aa44637
--- /dev/null
+++ b/Plugins/historyevents/m_historyevents.h
@@ -0,0 +1,453 @@
+/*
+Copyright (C) 2006 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#ifndef __M_HISTORYEVENTS_H__
+# define __M_HISTORYEVENTS_H__
+
+
+#define MIID_HISTORYEVENTS { 0xc8be8543, 0x6618, 0x4030, { 0x85, 0xcf, 0x90, 0x82, 0xc7, 0xde, 0x7f, 0xf7 } }
+
+
+#define HISTORYEVENTS_FORMAT_CHAR 1
+#define HISTORYEVENTS_FORMAT_WCHAR 2
+#define HISTORYEVENTS_FORMAT_RICH_TEXT 4
+#define HISTORYEVENTS_FORMAT_HTML 8
+
+#define HISTORYEVENTS_FLAG_DEFAULT (1 << 0) // Is a miranda core event type
+#define HISTORYEVENTS_FLAG_SHOW_IM_SRMM (1 << 1) // If this event has to be shown in srmm dialog
+#define HISTORYEVENTS_FLAG_USE_SENT_FLAG (1 << 2) // Means that it can be a sent or received and uses DBEF_SENT to mark that
+#define HISTORYEVENTS_FLAG_EXPECT_CONTACT_NAME_BEFORE (1 << 3) // Means that who is drawing this should draw the contact name before the text
+#define HISTORYEVENTS_FLAG_ONLY_LOG_IF_SRMM_OPEN (1 << 4) // If this event will be logged only if the message window is open
+#define HISTORYEVENTS_FLAG_FLASH_MSG_WINDOW (1 << 5) // If this event will trigger the openning/flashing of the message window
+#define HISTORYEVENTS_REGISTERED_IN_ICOLIB (9 << 16) // If the icon is a name already registered in icolib
+#define HISTORYEVENTS_FLAG_KEEP_ONE_YEAR (1 << 8) // By default store in db for 1 year
+#define HISTORYEVENTS_FLAG_KEEP_SIX_MONTHS (2 << 8) // By default store in db for 6 months
+#define HISTORYEVENTS_FLAG_KEEP_ONE_MONTH (3 << 8) // By default store in db for 1 month
+#define HISTORYEVENTS_FLAG_KEEP_ONE_WEEK (4 << 8) // By default store in db for 1 week
+#define HISTORYEVENTS_FLAG_KEEP_ONE_DAY (5 << 8) // By default store in db for 1 day
+#define HISTORYEVENTS_FLAG_KEEP_FOR_SRMM (6 << 8) // By default store in db only enought for message log
+#define HISTORYEVENTS_FLAG_KEEP_MAX_TEN (7 << 8) // By default store in db max 10 entries
+#define HISTORYEVENTS_FLAG_KEEP_MAX_HUNDRED (8 << 8) // By default store in db for 100 entries
+#define HISTORYEVENTS_FLAG_KEEP_DONT (9 << 8) // By default don't store in db (aka ignore it)
+
+
+// This function must be implemented by subscribers. It must return a pointer or NULL
+// to say it can't handle the text
+typedef void * (*fGetHistoryEventText)(HANDLE hContact, HANDLE hDbEvent, DBEVENTINFO *dbe, int format);
+
+typedef struct {
+ int cbSize;
+ char *module;
+ char *name; // Internal event name
+ char *description; // Will be translated. When retrieving it is already translated
+ WORD eventType; // The event type it can handle
+ union {
+ HICON defaultIcon;
+ char * defaultIconName; // if HISTORYEVENTS_REGISTERED_IN_ICOLIB is set. Always use this one when retrieving
+ };
+ int supports; // What kind of return is supported - or of HISTORYEVENTS_FORMAT_*
+ int flags; // or of HISTORYEVENTS_FLAG_*
+ fGetHistoryEventText pfGetHistoryEventText; // NULL to use default get text (similar to message, without extra format)
+
+ // Aditional data if wants to use add to history services
+ char **templates; // Each entry is: "Name\nDefault\n%var%\tDescription\n%var%\tDescription\n%var%\tDescription"
+ int numTemplates;
+
+} HISTORY_EVENT_HANDLER;
+
+
+/*
+Get the number of registered events
+
+wParam: ignored
+lParam: ignored
+Return: The number of events registered with the plugin
+*/
+#define MS_HISTORYEVENTS_GET_COUNT "HistoryEvents/GetCount"
+
+
+/*
+Get an event by number or by type.
+To retrieve by number, pass -1 as type. To retrieve by type, pass -1 as number.
+
+wParam: (int) event number
+lParam: (int) event type
+Return: (const HISTORY_EVENT_HANDLER *) if the event exists, NULL otherwise. Don't change the
+ returned strunc: it is a pointer to the internall struct.
+*/
+#define MS_HISTORYEVENTS_GET_EVENT "HistoryEvents/GetEvent"
+
+
+/*
+Register a plugin that can handle an event type. This must be called during the call to the
+Load function of the plugin. In ModulesLoaded callback all plugins have to be already registered,
+so srmm and history modules can query then.
+
+wParam: HISTORY_EVENT_HANDLER *
+lParam: ignored
+Return: 0 for success
+*/
+#define MS_HISTORYEVENTS_REGISTER "HistoryEvents/Register"
+
+
+typedef struct {
+ int cbSize;
+ HANDLE hDbEvent;
+ DBEVENTINFO *dbe; // Optional
+ int format; // one of HISTORYEVENTS_FORMAT_*
+
+} HISTORY_EVENT_PARAM;
+
+/*
+Check if an event can be handled by any subscribers
+
+wParam: WORD - event type
+lParam: ignored
+Return: BOOL
+*/
+#define MS_HISTORYEVENTS_CAN_HANDLE "HistoryEvents/CanHandle"
+
+/*
+Get the icon for a history event type
+
+wParam: WORD - event type
+lParam: BOOL - TRUE to copy the icon (should be released with DestroyObject),
+ FALSE to use icolib one (should be released with MS_HISTORYEVENTS_RELEASE_ICON)
+Return: HICON
+*/
+#define MS_HISTORYEVENTS_GET_ICON "HistoryEvents/GetIcon"
+
+/*
+Get the flags for a history event type
+
+wParam: WORD - event type
+lParam: ignored
+Return: int - or of HISTORYEVENTS_FLAG_* or -1 if error
+*/
+#define MS_HISTORYEVENTS_GET_FLAGS "HistoryEvents/GetFlags"
+
+/*
+Release the icon for a history event type. This is really just a forward to icolib
+
+wParam: HICON
+lParam: ignored
+*/
+#define MS_HISTORYEVENTS_RELEASE_ICON "Skin2/Icons/ReleaseIcon"
+
+/*
+Get the text for a history event type
+
+wParam: HISTORY_EVENT_PARAM *
+lParam: ignored
+Return: char * or wchar * depending on sent flags. Free with mir_free or MS_HISTORYEVENTS_RELEASE_TEXT
+*/
+#define MS_HISTORYEVENTS_GET_TEXT "HistoryEvents/GetText"
+
+/*
+Release the text for a history event type. Internally is just a call to mir_free
+
+wParam: char * or wchar *
+lParam: ignored
+*/
+#define MS_HISTORYEVENTS_RELEASE_TEXT "HistoryEvents/ReleaseText"
+
+
+
+typedef struct {
+ int cbSize;
+ HANDLE hContact;
+ WORD eventType;
+ int templateNum;
+ TCHAR **variables;
+ int numVariables;
+ PBYTE additionalData;
+ int additionalDataSize;
+ int flags; // Flags for the event type
+ DWORD timestamp; // 0 for now
+ BOOL addToMetaToo;
+} HISTORY_EVENT_ADD;
+
+/*
+Add an registered event to the history. This is a helper service
+
+wParam: HISTORY_EVENT_ADD
+lParam: ignored
+Return: HANDLE to the db event
+*/
+#define MS_HISTORYEVENTS_ADD_TO_HISTORY "HistoryEvents/AddToHistory"
+
+/*
+Check if a template is enabled
+
+wParam: event type
+lParam: template num
+Return: TRUE or FALSE
+*/
+#define MS_HISTORYEVENTS_IS_ENABLED_TEMPLATE "HistoryEvents/IsEnabledTemplate"
+
+
+
+// Helper functions //////////////////////////////////////////////////////////////////////////////
+
+
+
+
+static int HistoryEvents_Register(char *module, char *name, char *description, int eventType, HICON defaultIcon,
+ int supports, int flags, fGetHistoryEventText pfGetHistoryEventText)
+{
+ HISTORY_EVENT_HANDLER heh = {0};
+
+ if (!ServiceExists(MS_HISTORYEVENTS_REGISTER))
+ return 1;
+
+ heh.cbSize = sizeof(heh);
+ heh.module = module;
+ heh.name = name;
+ heh.description = description;
+ heh.eventType = eventType;
+ heh.defaultIcon = defaultIcon;
+ heh.supports = supports;
+ heh.flags = flags;
+ heh.pfGetHistoryEventText = pfGetHistoryEventText;
+ return CallService(MS_HISTORYEVENTS_REGISTER, (WPARAM) &heh, 0);
+}
+
+static int HistoryEvents_RegisterWithTemplates(char *module, char *name, char *description, int eventType, HICON defaultIcon,
+ int supports, int flags, fGetHistoryEventText pfGetHistoryEventText,
+ char **templates, int numTemplates)
+{
+ HISTORY_EVENT_HANDLER heh = {0};
+
+ if (!ServiceExists(MS_HISTORYEVENTS_REGISTER))
+ return 1;
+
+ heh.cbSize = sizeof(heh);
+ heh.module = module;
+ heh.name = name;
+ heh.description = description;
+ heh.eventType = eventType;
+ heh.defaultIcon = defaultIcon;
+ heh.supports = supports;
+ heh.flags = flags;
+ heh.pfGetHistoryEventText = pfGetHistoryEventText;
+ heh.templates = templates;
+ heh.numTemplates = numTemplates;
+ return CallService(MS_HISTORYEVENTS_REGISTER, (WPARAM) &heh, 0);
+}
+
+static int HistoryEvents_RegisterMessageStyle(char *module, char *name, char *description, int eventType, HICON defaultIcon,
+ int flags, char **templates, int numTemplates)
+{
+ HISTORY_EVENT_HANDLER heh = {0};
+
+ if (!ServiceExists(MS_HISTORYEVENTS_REGISTER))
+ return 1;
+
+ heh.cbSize = sizeof(heh);
+ heh.module = module;
+ heh.name = name;
+ heh.description = description;
+ heh.eventType = eventType;
+ heh.defaultIcon = defaultIcon;
+ heh.flags = flags;
+ heh.templates = templates;
+ heh.numTemplates = numTemplates;
+ return CallService(MS_HISTORYEVENTS_REGISTER, (WPARAM) &heh, 0);
+}
+
+static BOOL HistoryEvents_CanHandle(WORD eventType)
+{
+ if (!ServiceExists(MS_HISTORYEVENTS_CAN_HANDLE))
+ return FALSE;
+
+ return (BOOL) CallService(MS_HISTORYEVENTS_CAN_HANDLE, (WPARAM) eventType, 0);
+}
+
+static HICON HistoryEvents_GetIcon(WORD eventType)
+{
+ if (!ServiceExists(MS_HISTORYEVENTS_GET_ICON))
+ return NULL;
+
+ return (HICON) CallService(MS_HISTORYEVENTS_GET_ICON, (WPARAM) eventType, 0);
+}
+
+static int HistoryEvents_GetFlags(WORD eventType)
+{
+ if (!ServiceExists(MS_HISTORYEVENTS_GET_FLAGS))
+ return -1;
+
+ return (int) CallService(MS_HISTORYEVENTS_GET_FLAGS, (WPARAM) eventType, 0);
+}
+
+static void HistoryEvents_ReleaseIcon(HICON icon)
+{
+ CallService(MS_HISTORYEVENTS_RELEASE_ICON, (WPARAM) icon, 0);
+}
+
+static char * HistoryEvents_GetTextA(HANDLE hDbEvent, DBEVENTINFO *dbe)
+{
+ HISTORY_EVENT_PARAM hep = {0};
+
+ if (!ServiceExists(MS_HISTORYEVENTS_GET_TEXT))
+ return NULL;
+
+ hep.cbSize = sizeof(hep);
+ hep.hDbEvent = hDbEvent;
+ hep.dbe = dbe;
+ hep.format = HISTORYEVENTS_FORMAT_CHAR;
+ return (char *) CallService(MS_HISTORYEVENTS_GET_TEXT, (WPARAM) &hep, 0);
+}
+
+static wchar_t * HistoryEvents_GetTextW(HANDLE hDbEvent, DBEVENTINFO *dbe)
+{
+ HISTORY_EVENT_PARAM hep = {0};
+
+ if (!ServiceExists(MS_HISTORYEVENTS_GET_TEXT))
+ return NULL;
+
+ hep.cbSize = sizeof(hep);
+ hep.hDbEvent = hDbEvent;
+ hep.dbe = dbe;
+ hep.format = HISTORYEVENTS_FORMAT_WCHAR;
+ return (wchar_t *) CallService(MS_HISTORYEVENTS_GET_TEXT, (WPARAM) &hep, 0);
+}
+
+static char * HistoryEvents_GetRichText(HANDLE hDbEvent, DBEVENTINFO *dbe)
+{
+ HISTORY_EVENT_PARAM hep = {0};
+
+ if (!ServiceExists(MS_HISTORYEVENTS_GET_TEXT))
+ return NULL;
+
+ hep.cbSize = sizeof(hep);
+ hep.hDbEvent = hDbEvent;
+ hep.dbe = dbe;
+ hep.format = HISTORYEVENTS_FORMAT_RICH_TEXT;
+ return (char *) CallService(MS_HISTORYEVENTS_GET_TEXT, (WPARAM) &hep, 0);
+}
+
+#define HistoryEvents_ReleaseText mir_free
+//static void HistoryEvents_ReleaseText(void *str)
+//{
+// if (!ServiceExists(MS_HISTORYEVENTS_RELEASE_TEXT))
+// return;
+//
+// CallService(MS_HISTORYEVENTS_RELEASE_TEXT, (WPARAM) str, 0);
+//}
+
+
+#ifdef __cplusplus
+static HANDLE HistoryEvents_AddToHistoryEx(HANDLE hContact, WORD eventType, int templateNum,
+ TCHAR **variables, int numVariables,
+ PBYTE additionalData, int additionalDataSize,
+ int flags = 0, DWORD timestamp = 0, BOOL addToMetaToo = FALSE)
+#else
+static HANDLE HistoryEvents_AddToHistoryEx(HANDLE hContact, WORD eventType, int templateNum,
+ TCHAR **variables, int numVariables,
+ PBYTE additionalData, int additionalDataSize,
+ int flags, DWORD timestamp, BOOL addToMetaToo)
+#endif
+{
+ HISTORY_EVENT_ADD hea = {0};
+
+ if (!ServiceExists(MS_HISTORYEVENTS_ADD_TO_HISTORY))
+ return NULL;
+
+ hea.cbSize = sizeof(hea);
+ hea.hContact = hContact;
+ hea.eventType = eventType;
+ hea.templateNum = templateNum;
+ hea.numVariables = numVariables;
+ hea.variables = variables;
+ hea.additionalData = additionalData;
+ hea.additionalDataSize = additionalDataSize;
+ hea.flags = flags;
+ hea.timestamp = timestamp;
+ hea.addToMetaToo = addToMetaToo;
+
+ return (HANDLE) CallService(MS_HISTORYEVENTS_ADD_TO_HISTORY, (WPARAM) &hea, 0);
+}
+
+#ifdef __cplusplus
+static HANDLE HistoryEvents_AddToHistoryVars(HANDLE hContact, WORD eventType, int templateNum,
+ TCHAR **variables, int numVariables,
+ int flags = 0, DWORD timestamp = 0, BOOL addToMetaToo = FALSE)
+#else
+static HANDLE HistoryEvents_AddToHistoryVars(HANDLE hContact, WORD eventType, int templateNum,
+ TCHAR **variables, int numVariables,
+ int flags, DWORD timestamp, BOOL addToMetaToo)
+#endif
+{
+ HISTORY_EVENT_ADD hea = {0};
+
+ if (!ServiceExists(MS_HISTORYEVENTS_ADD_TO_HISTORY))
+ return NULL;
+
+ hea.cbSize = sizeof(hea);
+ hea.hContact = hContact;
+ hea.eventType = eventType;
+ hea.templateNum = templateNum;
+ hea.numVariables = numVariables;
+ hea.variables = variables;
+ hea.flags = flags;
+ hea.timestamp = timestamp;
+ hea.addToMetaToo = addToMetaToo;
+
+ return (HANDLE) CallService(MS_HISTORYEVENTS_ADD_TO_HISTORY, (WPARAM) &hea, 0);
+}
+
+#ifdef __cplusplus
+static HANDLE HistoryEvents_AddToHistorySimple(HANDLE hContact, WORD eventType, int templateNum,
+ int flags = 0, DWORD timestamp = 0, BOOL addToMetaToo = FALSE)
+#else
+static HANDLE HistoryEvents_AddToHistorySimple(HANDLE hContact, WORD eventType, int templateNum,
+ int flags, DWORD timestamp, BOOL addToMetaToo)
+#endif
+{
+ HISTORY_EVENT_ADD hea = {0};
+
+ if (!ServiceExists(MS_HISTORYEVENTS_ADD_TO_HISTORY))
+ return NULL;
+
+ hea.cbSize = sizeof(hea);
+ hea.hContact = hContact;
+ hea.eventType = eventType;
+ hea.templateNum = templateNum;
+ hea.flags = flags;
+ hea.timestamp = timestamp;
+ hea.addToMetaToo = addToMetaToo;
+
+ return (HANDLE) CallService(MS_HISTORYEVENTS_ADD_TO_HISTORY, (WPARAM) &hea, 0);
+}
+
+static BOOL HistoryEvents_IsEnabledTemplate(WORD eventType, int templateNum)
+{
+ return (BOOL) CallService(MS_HISTORYEVENTS_IS_ENABLED_TEMPLATE, eventType, templateNum);
+}
+
+#ifdef UNICODE
+# define HistoryEvents_GetTextT HistoryEvents_GetTextW
+#else
+# define HistoryEvents_GetTextT HistoryEvents_GetTextA
+#endif
+
+
+
+#endif // __M_HISTORYEVENTS_H__
diff --git a/Plugins/historyevents/options.cpp b/Plugins/historyevents/options.cpp
new file mode 100644
index 0000000..fb2bf53
--- /dev/null
+++ b/Plugins/historyevents/options.cpp
@@ -0,0 +1,536 @@
+/*
+Copyright (C) 2006 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#include "commons.h"
+
+
+
+// Prototypes /////////////////////////////////////////////////////////////////////////////////////
+
+HANDLE hOptHook = NULL;
+
+static BOOL CALLBACK OptionsDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam);
+
+void GetTemplare(Buffer<TCHAR> *buffer, HISTORY_EVENT_HANDLER *heh, int templates);
+
+
+
+
+// Functions //////////////////////////////////////////////////////////////////////////////////////
+
+
+DWORD GetSettingDword(HISTORY_EVENT_HANDLER *heh, char *setting, DWORD def)
+{
+ char tmp[128];
+ mir_snprintf(tmp, MAX_REGS(tmp), "%d_%s", (int) heh->eventType, setting);
+ return DBGetContactSettingDword(NULL, heh->module == NULL ? MODULE_NAME : heh->module, tmp, def);
+}
+
+WORD GetSettingWord(HISTORY_EVENT_HANDLER *heh, char *setting, WORD def)
+{
+ char tmp[128];
+ mir_snprintf(tmp, MAX_REGS(tmp), "%d_%s", (int) heh->eventType, setting);
+ return DBGetContactSettingWord(NULL, heh->module == NULL ? MODULE_NAME : heh->module, tmp, def);
+}
+
+BYTE GetSettingByte(HISTORY_EVENT_HANDLER *heh, char *setting, BYTE def)
+{
+ char tmp[128];
+ mir_snprintf(tmp, MAX_REGS(tmp), "%d_%s", (int) heh->eventType, setting);
+ return DBGetContactSettingByte(NULL, heh->module == NULL ? MODULE_NAME : heh->module, tmp, def);
+}
+
+BOOL GetSettingBool(HISTORY_EVENT_HANDLER *heh, char *setting, BOOL def)
+{
+ char tmp[128];
+ mir_snprintf(tmp, MAX_REGS(tmp), "%d_%s", (int) heh->eventType, setting);
+ return DBGetContactSettingByte(NULL, heh->module == NULL ? MODULE_NAME : heh->module, tmp, def) != 0;
+}
+
+BOOL GetSettingBool(HISTORY_EVENT_HANDLER *heh, int templ, char *setting, BOOL def)
+{
+ char tmp[128];
+ mir_snprintf(tmp, MAX_REGS(tmp), "%d_%d_%s", (int) heh->eventType, templ, setting);
+ return DBGetContactSettingByte(NULL, heh->module == NULL ? MODULE_NAME : heh->module, tmp, def) != 0;
+}
+
+
+void WriteSettingDword(HISTORY_EVENT_HANDLER *heh, char *setting, DWORD val)
+{
+ char tmp[128];
+ mir_snprintf(tmp, MAX_REGS(tmp), "%d_%s", (int) heh->eventType, setting);
+ DBWriteContactSettingDword(NULL, heh->module == NULL ? MODULE_NAME : heh->module, tmp, val);
+}
+
+void WriteSettingWord(HISTORY_EVENT_HANDLER *heh, char *setting, WORD val)
+{
+ char tmp[128];
+ mir_snprintf(tmp, MAX_REGS(tmp), "%d_%s", (int) heh->eventType, setting);
+ DBWriteContactSettingWord(NULL, heh->module == NULL ? MODULE_NAME : heh->module, tmp, val);
+}
+
+void WriteSettingByte(HISTORY_EVENT_HANDLER *heh, char *setting, BYTE val)
+{
+ char tmp[128];
+ mir_snprintf(tmp, MAX_REGS(tmp), "%d_%s", (int) heh->eventType, setting);
+ DBWriteContactSettingByte(NULL, heh->module == NULL ? MODULE_NAME : heh->module, tmp, val);
+}
+
+void WriteSettingBool(HISTORY_EVENT_HANDLER *heh, char *setting, BOOL val)
+{
+ char tmp[128];
+ mir_snprintf(tmp, MAX_REGS(tmp), "%d_%s", (int) heh->eventType, setting);
+ DBWriteContactSettingByte(NULL, heh->module == NULL ? MODULE_NAME : heh->module, tmp, val ? 1 : 0);
+}
+
+void WriteSettingBool(HISTORY_EVENT_HANDLER *heh, int templ, char *setting, BOOL val)
+{
+ char tmp[128];
+ mir_snprintf(tmp, MAX_REGS(tmp), "%d_%d_%s", (int) heh->eventType, templ, setting);
+ DBWriteContactSettingByte(NULL, heh->module == NULL ? MODULE_NAME : heh->module, tmp, val ? 1 : 0);
+}
+
+void WriteSettingTString(HISTORY_EVENT_HANDLER *heh, int templ, char *setting, TCHAR *str)
+{
+ char tmp[128];
+ mir_snprintf(tmp, MAX_REGS(tmp), "%d_%d_%s", (int) heh->eventType, templ, setting);
+ DBWriteContactSettingTString(NULL, heh->module == NULL ? MODULE_NAME : heh->module, tmp, str);
+}
+
+
+int InitOptionsCallback(WPARAM wParam,LPARAM lParam)
+{
+ OPTIONSDIALOGPAGE odp = {0};
+ odp.cbSize = sizeof(odp);
+ odp.hInstance = hInst;
+ odp.ptszGroup = TranslateT("History");
+ odp.ptszTitle = TranslateT("Events");
+ odp.pfnDlgProc = OptionsDlgProc;
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPTIONS);
+ odp.flags = ODPF_BOLDGROUPS | ODPF_TCHAR;
+ CallService(MS_OPT_ADDPAGE,wParam,(LPARAM)&odp);
+ return 0;
+}
+
+
+void LoadOptions()
+{
+}
+
+
+void InitOptions()
+{
+ LoadOptions();
+
+ hOptHook = HookEvent(ME_OPT_INITIALISE, InitOptionsCallback);
+}
+
+
+void DeInitOptions()
+{
+ UnhookEvent(hOptHook);
+}
+
+
+BOOL ScreenToClient(HWND hWnd, LPRECT lpRect)
+{
+ BOOL ret;
+
+ POINT pt;
+
+ pt.x = lpRect->left;
+ pt.y = lpRect->top;
+
+ ret = ScreenToClient(hWnd, &pt);
+
+ if (!ret) return ret;
+
+ lpRect->left = pt.x;
+ lpRect->top = pt.y;
+
+
+ pt.x = lpRect->right;
+ pt.y = lpRect->bottom;
+
+ ret = ScreenToClient(hWnd, &pt);
+
+ lpRect->right = pt.x;
+ lpRect->bottom = pt.y;
+
+ return ret;
+}
+
+
+static void GetTextMetric(HFONT hFont, TEXTMETRIC *tm)
+{
+ HDC hdc = GetDC(NULL);
+ HFONT hOldFont = (HFONT) SelectObject(hdc, hFont);
+ GetTextMetrics(hdc, tm);
+ SelectObject(hdc, hOldFont);
+ ReleaseDC(NULL, hdc);
+}
+
+
+static BOOL CALLBACK OptionsDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ static int avaiable = 0;
+ static int total = 0;
+ static int current = 0;
+ static int lineHeigth = 0;
+
+ switch (msg)
+ {
+ case WM_INITDIALOG:
+ {
+ TranslateDialogDefault(hwndDlg);
+
+ RECT rc;
+ GetWindowRect(GetDlgItem(hwndDlg, IDC_EVENT_TYPES), &rc);
+
+ POINT pt = { rc.left, rc.bottom + 5 };
+ ScreenToClient(hwndDlg, &pt);
+ int origY = pt.y;
+
+ GetClientRect(hwndDlg, &rc);
+
+ HFONT hFont = (HFONT) SendMessage(hwndDlg, WM_GETFONT, 0, 0);
+ TEXTMETRIC font;
+ GetTextMetric(hFont, &font);
+
+ int height = max(font.tmHeight, 16) + 4;
+ int width = rc.right - rc.left - 50;
+
+ lineHeigth = height;
+
+ // Create all items
+ int id = IDC_EVENT_TYPES + 1;
+ for (int k = 0; k < handlers.getCount(); k++)
+ {
+ HISTORY_EVENT_HANDLER *heh = handlers[k];
+
+ int x = pt.x;
+
+ // Event type
+
+ HWND icon = CreateWindow(_T("STATIC"), _T(""), WS_CHILD | WS_VISIBLE | SS_ICON | SS_CENTERIMAGE,
+ x, pt.y + (height - 16) / 2, 16, 16, hwndDlg, NULL, hInst, NULL);
+ x += 20;
+
+ SendMessage(icon, STM_SETICON, (WPARAM) LoadIconEx(heh, TRUE), 0);
+
+ HWND tmp = CreateWindowA("STATIC", heh->description, WS_CHILD | WS_VISIBLE,
+ x, pt.y + (height - font.tmHeight) / 2, width - (x - pt.x), font.tmHeight,
+ hwndDlg, NULL, hInst, NULL);
+ SendMessage(tmp, WM_SETFONT, (WPARAM) hFont, FALSE);
+
+ // Show in SRMM
+
+/* if (!(heh->flags & HISTORYEVENTS_FLAG_DEFAULT))
+ {
+ pt.y += height + 3;
+ x = pt.x + 20;
+
+ HWND chk = CreateWindow(_T("BUTTON"), TranslateT("Show in message window"),
+ WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_CHECKBOX | BS_AUTOCHECKBOX,
+ x, pt.y, width - (x - pt.x), height, hwndDlg, (HMENU) id, hInst, NULL);
+ SendMessage(chk, BM_SETCHECK, heh->flags & HISTORYEVENTS_FLAG_SHOW_IM_SRMM ? BST_CHECKED : BST_UNCHECKED, 0);
+ SendMessage(chk, WM_SETFONT, (WPARAM) hFont, FALSE);
+ }
+*/ id++;
+
+ // Message settings
+
+/* if (heh->eventType == EVENTTYPE_MESSAGE)
+ {
+ pt.y += height + 3;
+ x = pt.x + 20;
+
+ HWND chk = CreateWindow(_T("BUTTON"), TranslateT("Respect text format"),
+ WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_CHECKBOX | BS_AUTOCHECKBOX,
+ x, pt.y, width - (x - pt.x), height, hwndDlg, (HMENU) id, hInst, NULL);
+ SendMessage(chk, WM_SETFONT, (WPARAM) hFont, FALSE);
+ SendMessage(chk, BM_SETCHECK, GetSettingBool(heh, RESPECT_TEXT_FORMAT, TRUE) ? BST_CHECKED : BST_UNCHECKED, 0);
+
+ pt.y += height + 3;
+
+ chk = CreateWindow(_T("BUTTON"), TranslateT("Respect text font"),
+ WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_CHECKBOX | BS_AUTOCHECKBOX,
+ x, pt.y, width - (x - pt.x), height, hwndDlg, (HMENU) (id+1), hInst, NULL);
+ SendMessage(chk, WM_SETFONT, (WPARAM) hFont, FALSE);
+ SendMessage(chk, BM_SETCHECK, GetSettingBool(heh, RESPECT_TEXT_FONT, FALSE) ? BST_CHECKED : BST_UNCHECKED, 0);
+ }
+*/ id+=2;
+
+ // Only if SRMM open
+
+ if (!(heh->flags & HISTORYEVENTS_FLAG_DEFAULT))
+ {
+ pt.y += height + 3;
+ x = pt.x + 20;
+
+ HWND chk = CreateWindow(_T("BUTTON"), TranslateT("Only add if message window is open"),
+ WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_CHECKBOX | BS_AUTOCHECKBOX,
+ x, pt.y, width - (x - pt.x), height, hwndDlg, (HMENU) id, hInst, NULL);
+ SendMessage(chk, BM_SETCHECK, heh->flags & HISTORYEVENTS_FLAG_ONLY_LOG_IF_SRMM_OPEN ? BST_CHECKED : BST_UNCHECKED, 0);
+ SendMessage(chk, WM_SETFONT, (WPARAM) hFont, FALSE);
+ }
+ id++;
+
+ // Templates
+
+ Buffer<char> name;
+ Buffer<TCHAR> templ;
+ for(int i = 0; i < heh->numTemplates; i++)
+ {
+ pt.y += height + 3;
+ x = pt.x + 20;
+
+ name.clear();
+ char *end = strchr(heh->templates[i], '\n');
+ size_t len = (end == NULL ? strlen(heh->templates[i]) : end - heh->templates[i]);
+ name.append(heh->templates[i], len);
+ name.translate();
+ name.append(':');
+ name.pack();
+
+ HWND chk = CreateWindowA("BUTTON", name.str,
+ WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_CHECKBOX | BS_AUTOCHECKBOX,
+ x, pt.y, 120, height, hwndDlg, (HMENU) (id + 2 * i + 1), hInst, NULL);
+ SendMessage(chk, WM_SETFONT, (WPARAM) hFont, FALSE);
+ SendMessage(chk, BM_SETCHECK, GetSettingBool(heh, i, TEMPLATE_ENABLED, TRUE) ? BST_CHECKED : BST_UNCHECKED, 0);
+ x += 120;
+
+ templ.clear();
+ GetTemplare(&templ, heh, i);
+ templ.pack();
+
+ HWND edit = CreateWindowEx(WS_EX_CLIENTEDGE, _T("EDIT"), templ.str,
+ WS_CHILD | WS_VISIBLE | WS_TABSTOP | ES_LEFT | ES_AUTOHSCROLL,
+ x, pt.y, width - (x - pt.x), height, hwndDlg, (HMENU) (id + 2 * i + 2), hInst, NULL);
+ SendMessage(edit, WM_SETFONT, (WPARAM) hFont, FALSE);
+ }
+
+ // Keep
+
+ pt.y += height + 3;
+ x = pt.x + 36;
+
+ tmp = CreateWindow(_T("STATIC"), TranslateT("Keep in database:"), WS_CHILD | WS_VISIBLE,
+ x, pt.y + (height - font.tmHeight) / 2, 104, font.tmHeight,
+ hwndDlg, NULL, hInst, NULL);
+ SendMessage(tmp, WM_SETFONT, (WPARAM) hFont, FALSE);
+ x += 104;
+
+ HWND combo = CreateWindow(_T("COMBOBOX"), _T(""),
+ WS_CHILD | WS_VISIBLE | WS_TABSTOP | CBS_DROPDOWNLIST | WS_VSCROLL,
+ x, pt.y, 200, height + 60,
+ hwndDlg, (HMENU) id, hInst, NULL);
+ SendMessage(combo, WM_SETFONT, (WPARAM) hFont, FALSE);
+
+ SendMessage(combo, CB_ADDSTRING, 0, (LPARAM) TranslateT("Forever"));
+ SendMessage(combo, CB_ADDSTRING, 0, (LPARAM) TranslateT("For 1 year"));
+ SendMessage(combo, CB_ADDSTRING, 0, (LPARAM) TranslateT("For 6 months"));
+ SendMessage(combo, CB_ADDSTRING, 0, (LPARAM) TranslateT("For 1 month"));
+ SendMessage(combo, CB_ADDSTRING, 0, (LPARAM) TranslateT("For 1 week"));
+ SendMessage(combo, CB_ADDSTRING, 0, (LPARAM) TranslateT("For 1 day"));
+ SendMessage(combo, CB_ADDSTRING, 0, (LPARAM) TranslateT("Only to view in message window"));
+ SendMessage(combo, CB_ADDSTRING, 0, (LPARAM) TranslateT("Max 10 events"));
+ SendMessage(combo, CB_ADDSTRING, 0, (LPARAM) TranslateT("Max 100 events"));
+
+ if (!(heh->flags & HISTORYEVENTS_FLAG_DEFAULT))
+ SendMessage(combo, CB_ADDSTRING, 0, (LPARAM) TranslateT("Don't store"));
+
+ SendMessage(combo, CB_SETCURSEL, KEEP_FLAG_TO_COMBO(heh->flags), 0);
+
+ pt.y += height + 10;
+ id += 21;
+ }
+
+ avaiable = rc.bottom - rc.top;
+ total = pt.y - 7;
+ current = 0;
+
+ SCROLLINFO si;
+ si.cbSize = sizeof(si);
+ si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS;
+ si.nMin = 0;
+ si.nMax = total;
+ si.nPage = avaiable;
+ si.nPos = current;
+ SetScrollInfo(hwndDlg, SB_VERT, &si, TRUE);
+
+ break;
+ }
+
+ case WM_VSCROLL:
+ {
+ int yDelta; // yDelta = new_pos - current_pos
+ int yNewPos; // new position
+
+ switch (LOWORD(wParam))
+ {
+ case SB_PAGEUP:
+ yNewPos = current - avaiable / 2;
+ break;
+ case SB_PAGEDOWN:
+ yNewPos = current + avaiable / 2;
+ break;
+ case SB_LINEUP:
+ yNewPos = current - lineHeigth;
+ break;
+ case SB_LINEDOWN:
+ yNewPos = current + lineHeigth;
+ break;
+ case SB_THUMBPOSITION:
+ yNewPos = HIWORD(wParam);
+ break;
+ case SB_THUMBTRACK:
+ yNewPos = HIWORD(wParam);
+ break;
+ default:
+ yNewPos = current;
+ }
+
+ yNewPos = min(total - avaiable, max(0, yNewPos));
+
+ if (yNewPos == current)
+ break;
+
+ yDelta = yNewPos - current;
+ current = yNewPos;
+
+ // Scroll the window. (The system repaints most of the
+ // client area when ScrollWindowEx is called; however, it is
+ // necessary to call UpdateWindow in order to repaint the
+ // rectangle of pixels that were invalidated.)
+
+ ScrollWindowEx(hwndDlg, 0, -yDelta, (CONST RECT *) NULL,
+ (CONST RECT *) NULL, (HRGN) NULL, (LPRECT) NULL,
+ SW_ERASE | SW_INVALIDATE | SW_SCROLLCHILDREN);
+ UpdateWindow(hwndDlg);
+
+ // Reset the scroll bar.
+
+ SCROLLINFO si;
+ si.cbSize = sizeof(si);
+ si.fMask = SIF_POS;
+ si.nPos = current;
+ SetScrollInfo(hwndDlg, SB_VERT, &si, TRUE);
+
+ break;
+ }
+
+ case WM_COMMAND:
+ {
+ if ((HWND) lParam != GetFocus())
+ break;
+
+ int id = (LOWORD(wParam) - IDC_EVENT_TYPES - 1) % 25;
+ if (id == 0 || id == 1 || id == 2 || id == 3 || (id >= 5 && (id - 5) % 2 == 0))
+ {
+ // Checkboxes
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ }
+ else if (id == 4)
+ {
+ // Combo
+ if (HIWORD(wParam) == CBN_SELCHANGE)
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ }
+ else if (id >= 5 && (id - 5) % 2 == 1)
+ {
+ if (HIWORD(wParam) == EN_CHANGE)
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ }
+ break;
+ }
+
+ case WM_NOTIFY:
+ {
+ LPNMHDR lpnmhdr = (LPNMHDR)lParam;
+ if (lpnmhdr->idFrom != 0 || lpnmhdr->code != PSN_APPLY)
+ break;
+
+ int id = IDC_EVENT_TYPES + 1;
+ for (int k = 0; k < handlers.getCount(); k++)
+ {
+ HISTORY_EVENT_HANDLER *heh = handlers[k];
+
+ // Show in SRMM
+
+/* if (!(heh->flags & HISTORYEVENTS_FLAG_DEFAULT))
+ {
+ if (IsDlgButtonChecked(hwndDlg, id))
+ heh->flags |= HISTORYEVENTS_FLAG_SHOW_IM_SRMM;
+ else
+ heh->flags &= ~HISTORYEVENTS_FLAG_SHOW_IM_SRMM;
+ }
+*/ id++;
+
+ // Message settings
+
+/* if (heh->eventType == EVENTTYPE_MESSAGE)
+ {
+ WriteSettingBool(heh, RESPECT_TEXT_FORMAT, IsDlgButtonChecked(hwndDlg, id));
+ WriteSettingBool(heh, RESPECT_TEXT_FONT, IsDlgButtonChecked(hwndDlg, id+1));
+ }
+*/ id+=2;
+
+ // Show in SRMM
+
+ if (!(heh->flags & HISTORYEVENTS_FLAG_DEFAULT))
+ {
+ if (IsDlgButtonChecked(hwndDlg, id))
+ heh->flags |= HISTORYEVENTS_FLAG_ONLY_LOG_IF_SRMM_OPEN;
+ else
+ heh->flags &= ~HISTORYEVENTS_FLAG_ONLY_LOG_IF_SRMM_OPEN;
+ }
+ id++;
+
+ // Templates
+
+ Buffer<char> name;
+ Buffer<TCHAR> templ;
+ for(int i = 0; i < heh->numTemplates; i++)
+ {
+ WriteSettingBool(heh, i, TEMPLATE_ENABLED, IsDlgButtonChecked(hwndDlg, id + 2 * i + 1));
+
+ TCHAR tmp[1024];
+ GetDlgItemText(hwndDlg, id + 2 * i + 2, tmp, 1024);
+ WriteSettingTString(heh, i, TEMPLATE_TEXT, tmp);
+ }
+
+ // Keep
+
+ heh->flags -= KEEP_FLAG(heh->flags);
+ heh->flags |= KEEP_COMBO_TO_FLAG(SendDlgItemMessage(hwndDlg, id, CB_GETCURSEL, 0, 0));
+ WriteSettingDword(heh, FLAGS, heh->flags);
+
+ id += 21;
+ }
+
+ return TRUE;
+ }
+
+ }
+
+ return 0;
+}
+
diff --git a/Plugins/historyevents/options.h b/Plugins/historyevents/options.h
new file mode 100644
index 0000000..0fc5009
--- /dev/null
+++ b/Plugins/historyevents/options.h
@@ -0,0 +1,62 @@
+/*
+Copyright (C) 2006 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#ifndef __OPTIONS_H__
+# define __OPTIONS_H__
+
+
+#include "commons.h"
+
+#include <windows.h>
+
+
+#define KEEP_FLAG_TO_COMBO(_x_) (((_x_) & 0xFF00)>>8)
+#define KEEP_COMBO_TO_FLAG(_x_) (((_x_) & 0xFF)<<8)
+
+#define FLAGS "Flags"
+#define RESPECT_TEXT_FORMAT "RespectTextFormat"
+#define RESPECT_TEXT_FONT "RespectTextFont"
+#define TEMPLATE_ENABLED "TemplateEnabled"
+#define TEMPLATE_TEXT "TemplateText"
+
+
+DWORD GetSettingDword(HISTORY_EVENT_HANDLER *heh, char *setting, DWORD def);
+WORD GetSettingWord(HISTORY_EVENT_HANDLER *heh, char *setting, WORD def);
+BYTE GetSettingByte(HISTORY_EVENT_HANDLER *heh, char *setting, BYTE def);
+BOOL GetSettingBool(HISTORY_EVENT_HANDLER *heh, char *setting, BOOL def);
+BOOL GetSettingBool(HISTORY_EVENT_HANDLER *heh, int templ, char *setting, BOOL def);
+
+void WriteSettingDword(HISTORY_EVENT_HANDLER *heh, char *setting, DWORD val);
+void WriteSettingWord(HISTORY_EVENT_HANDLER *heh, char *setting, WORD val);
+void WriteSettingByte(HISTORY_EVENT_HANDLER *heh, char *setting, BYTE val);
+void WriteSettingBool(HISTORY_EVENT_HANDLER *heh, char *setting, BOOL val);
+void WriteSettingBool(HISTORY_EVENT_HANDLER *heh, int templ, char *setting, BOOL val);
+void WriteSettingTString(HISTORY_EVENT_HANDLER *heh, int templ, char *setting, TCHAR *str);
+
+
+// Initializations needed by options
+void InitOptions();
+
+// Deinitializations needed by options
+void DeInitOptions();
+
+
+
+#endif // __OPTIONS_H__
diff --git a/Plugins/historyevents/resource.h b/Plugins/historyevents/resource.h
new file mode 100644
index 0000000..ceaf4f5
--- /dev/null
+++ b/Plugins/historyevents/resource.h
@@ -0,0 +1,89 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Developer Studio generated include file.
+// Used by resource.rc
+//
+#define IDD_OPTIONS 119
+#define IDD_PLAYERS 120
+#define IDD_FORMAT 121
+#define IDB_TTB_UP_DISABLED 122
+#define IDB_TTB_UP_ENABLED 123
+#define IDI_LISTENINGTO 124
+#define IDC_DELAY 1001
+#define IDC_WINCOLORS 1002
+#define IDC_DEFAULTCOLORS 1003
+#define IDC_BGCOLOR 1004
+#define IDC_TEXTCOLOR 1005
+#define IDC_PREV 1006
+#define IDC_DELAYFROMPU 1007
+#define IDC_DELAYCUSTOM 1008
+#define IDC_DELAYPERMANENT 1009
+#define IDC_VARS2_L 1016
+#define IDC_VARS_L 1017
+#define IDC_LISTENING_L 1018
+#define IDC_ADV_ICON 1019
+#define IDC_ENABLE_SEND 1020
+#define IDC_WATRACK 1021
+#define IDC_RIGHT_ACTION 1022
+#define IDC_LEFT_ACTION 1023
+#define IDC_PROTOCOLS 1041
+#define IDC_MESSAGE_L 1055
+#define IDC_LISTENINGTO_G 1056
+#define IDC_XSTATUS_MESSAGE 1057
+#define IDC_TEMPLATE 1058
+#define IDC_UNKNOWN 1059
+#define IDC_SHOW_ADV_ICON 1060
+#define IDC_NOTHING 1060
+#define IDC_CODE_INJECTION 1061
+#define IDC_ENABLE_MENU 1062
+#define IDC_FORMAT_G 1063
+#define IDC_TEMPLATE_L 1064
+#define IDC_UNKNOWN_L 1065
+#define IDC_PROTOCOLS_G 1066
+#define IDC_NOTHING_L 1066
+#define IDC_PROTOCOLS_L 1067
+#define IDC_TRACK_CHANGE 1068
+#define IDC_OVERRIDE_CONTACTS_TEMPLATE 1069
+#define IDC_ONLY_NOT_OFFLINE 1070
+#define IDC_TYPE_L 1075
+#define IDC_PLAYER_L 1076
+#define IDC_LENGTH_L 1077
+#define IDC_GENRE_L 1078
+#define IDC_YEAR_L 1079
+#define IDC_TRACK_L 1080
+#define IDC_TITLE_L 1081
+#define IDC_ALBUM_L 1082
+#define IDC_ARTIST_L 1083
+#define IDC_WINAMP 1085
+#define IDC_ITUNES 1086
+#define IDC_POLL_TIMER 1087
+#define IDC_WMP 1088
+#define IDC_PLAYERS_L 1089
+#define IDC_POLL_TIMER_L 1090
+#define IDC_POLL_TIMER_S_L 1091
+#define IDC_OTHER 1092
+#define IDC_CONTACTS_G 1093
+#define IDC_LISTENING_G 1094
+#define IDC_XSTATUS_G 1095
+#define IDC_XSTATUS_L 1096
+#define IDC_SET_XSTATUS 1097
+#define IDC_CHECK_XSTATUS 1098
+#define IDC_IGNORE_XSTATUS 1099
+#define IDC_XSTATUS_NAME 1100
+#define IDC_CHECK_XSTATUS_MUSIC 1100
+#define IDC_NAME_L 1101
+#define IDC_EVENT_TYPES 1102
+#define IDC_COMBO1 1103
+#define IDC_STATIC -1
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NO_MFC 1
+#define _APS_3D_CONTROLS 1
+#define _APS_NEXT_RESOURCE_VALUE 126
+#define _APS_NEXT_COMMAND_VALUE 40004
+#define _APS_NEXT_CONTROL_VALUE 1104
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/Plugins/historyevents/resource.rc b/Plugins/historyevents/resource.rc
new file mode 100644
index 0000000..0608bc4
--- /dev/null
+++ b/Plugins/historyevents/resource.rc
@@ -0,0 +1,109 @@
+//Microsoft Developer Studio generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "resource.h"
+#include "winresrc.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (U.S.) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page(1252)
+#endif //_WIN32
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Dialog
+//
+
+IDD_OPTIONS DIALOGEX 0, 0, 321, 240
+STYLE DS_FIXEDSYS | WS_CHILD | WS_VISIBLE | WS_VSCROLL
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg", 0, 0, 0x1
+BEGIN
+ LTEXT "Event types:",IDC_EVENT_TYPES,10,0,297,10
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// DESIGNINFO
+//
+
+#ifdef APSTUDIO_INVOKED
+GUIDELINES DESIGNINFO DISCARDABLE
+BEGIN
+ IDD_OPTIONS, DIALOG
+ BEGIN
+ LEFTMARGIN, 3
+ RIGHTMARGIN, 307
+ BOTTOMMARGIN, 237
+ END
+END
+#endif // APSTUDIO_INVOKED
+
+#endif // English (U.S.) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+/////////////////////////////////////////////////////////////////////////////
+// English (Canada) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENC)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_CAN
+#pragma code_page(1252)
+#endif //_WIN32
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "resource.h\0"
+END
+
+2 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "#include ""resource.h""\r\n"
+ "#include ""winresrc.h""\r\n"
+ "\0"
+END
+
+3 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "\r\n"
+ "\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+#endif // English (Canada) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
+
diff --git a/Plugins/historyevents/sdk/m_metacontacts.h b/Plugins/historyevents/sdk/m_metacontacts.h
new file mode 100644
index 0000000..1da12b9
--- /dev/null
+++ b/Plugins/historyevents/sdk/m_metacontacts.h
@@ -0,0 +1,162 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright © 2004 Universite Louis PASTEUR, STRASBOURG.
+Copyright © 2004 Scott Ellis (www.scottellis.com.au mail@scottellis.com.au)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef M_METACONTACTS_H__
+#define M_METACONTACTS_H__ 1
+
+//get the handle for a contact's parent metacontact
+//wParam=(HANDLE)hSubContact
+//lParam=0
+//returns a handle to the parent metacontact, or null if this contact is not a subcontact
+#define MS_MC_GETMETACONTACT "MetaContacts/GetMeta"
+
+//gets the handle for the default contact
+//wParam=(HANDLE)hMetaContact
+//lParam=0
+//returns a handle to the default contact, or null on failure
+#define MS_MC_GETDEFAULTCONTACT "MetaContacts/GetDefault"
+
+//gets the contact number for the default contact
+//wParam=(HANDLE)hMetaContact
+//lParam=0
+//returns a DWORD contact number, or -1 on failure
+#define MS_MC_GETDEFAULTCONTACTNUM "MetaContacts/GetDefaultNum"
+
+//gets the handle for the 'most online' contact
+//wParam=(HANDLE)hMetaContact
+//lParam=0
+//returns a handle to the 'most online' contact
+#define MS_MC_GETMOSTONLINECONTACT "MetaContacts/GetMostOnline"
+
+//gets the number of subcontacts for a metacontact
+//wParam=(HANDLE)hMetaContact
+//lParam=0
+//returns a DWORD representing the number of subcontacts for the given metacontact
+#define MS_MC_GETNUMCONTACTS "MetaContacts/GetNumContacts"
+
+//gets the handle of a subcontact, using the subcontact's number
+//wParam=(HANDLE)hMetaContact
+//lParam=(DWORD)contact number
+//returns a handle to the specified subcontact
+#define MS_MC_GETSUBCONTACT "MetaContacts/GetSubContact"
+
+//sets the default contact, using the subcontact's contact number
+//wParam=(HANDLE)hMetaContact
+//lParam=(DWORD)contact number
+//returns 0 on success
+#define MS_MC_SETDEFAULTCONTACTNUM "MetaContacts/SetDefault"
+
+//sets the default contact, using the subcontact's handle
+//wParam=(HANDLE)hMetaContact
+//lParam=(HANDLE)hSubcontact
+//returns 0 on success
+#define MS_MC_SETDEFAULTCONTACT "MetaContacts/SetDefaultByHandle"
+
+//forces the metacontact to send using a specific subcontact, using the subcontact's contact number
+//wParam=(HANDLE)hMetaContact
+//lParam=(DWORD)contact number
+//returns 0 on success
+#define MS_MC_FORCESENDCONTACTNUM "MetaContacts/ForceSendContact"
+
+//forces the metacontact to send using a specific subcontact, using the subcontact's handle
+//wParam=(HANDLE)hMetaContact
+//lParam=(HANDLE)hSubcontact
+//returns 0 on success (will fail if 'force default' is in effect)
+#define MS_MC_FORCESENDCONTACT "MetaContacts/ForceSendContactByHandle"
+
+//'unforces' the metacontact to send using a specific subcontact
+//wParam=(HANDLE)hMetaContact
+//lParam=0
+//returns 0 on success (will fail if 'force default' is in effect)
+#define MS_MC_UNFORCESENDCONTACT "MetaContacts/UnforceSendContact"
+
+//'forces' or 'unforces' (i.e. toggles) the metacontact to send using it's default contact
+// overrides (and clears) 'force send' above, and will even force use of offline contacts
+// will send ME_MC_FORCESEND or ME_MC_UNFORCESEND event
+//wParam=(HANDLE)hMetaContact
+//lParam=0
+//returns 1(true) or 0(false) representing new state of 'force default'
+#define MS_MC_FORCEDEFAULT "MetaContacts/ForceSendDefault"
+
+// method to get state of 'force' for a metacontact
+// wParam=(HANDLE)hMetaContact
+// lParam= (DWORD)&contact_number or NULL
+//
+// if lparam supplied, the contact_number of the contatct 'in force' will be copied to the address it points to,
+// or if none is in force, the value (DWORD)-1 will be copied
+// (v0.8.0.8+ returns 1 if 'force default' is true with *lParam == default contact number, else returns 0 with *lParam as above)
+#define MS_MC_GETFORCESTATE "MetaContacts/GetForceState"
+
+// fired when a metacontact's default contact changes (fired upon creation of metacontact also, when default is initially set)
+// wParam=(HANDLE)hMetaContact
+// lParam=(HANDLE)hDefaultContact
+#define ME_MC_DEFAULTTCHANGED "MetaContacts/DefaultChanged"
+
+// fired when a metacontact's subcontacts change (fired upon creation of metacontact, when contacts are added or removed, and when
+// contacts are reordered) - a signal to re-read metacontact data
+// wParam=(HANDLE)hMetaContact
+// lParam=0
+#define ME_MC_SUBCONTACTSCHANGED "MetaContacts/SubcontactsChanged"
+
+// fired when a metacontact is forced to send using a specific subcontact
+// wParam=(HANDLE)hMetaContact
+// lParam=(HANDLE)hForceContact
+#define ME_MC_FORCESEND "MetaContacts/ForceSend"
+
+// fired when a metacontact is 'unforced' to send using a specific subcontact
+// wParam=(HANDLE)hMetaContact
+// lParam=0
+#define ME_MC_UNFORCESEND "MetaContacts/UnforceSend"
+
+// method to get protocol name - used to be sure you're dealing with a "real" metacontacts plugin :)
+// wParam=lParam=0
+#define MS_MC_GETPROTOCOLNAME "MetaContacts/GetProtoName"
+
+
+// added 0.9.5.0 (22/3/05)
+// wParam=(HANDLE)hContact
+// lParam=0
+// convert a given contact into a metacontact
+#define MS_MC_CONVERTTOMETA "MetaContacts/ConvertToMetacontact"
+
+// added 0.9.5.0 (22/3/05)
+// wParam=(HANDLE)hContact
+// lParam=(HANDLE)hMeta
+// add an existing contact to a metacontact
+#define MS_MC_ADDTOMETA "MetaContacts/AddToMetacontact"
+
+// added 0.9.5.0 (22/3/05)
+// wParam=0
+// lParam=(HANDLE)hContact
+// remove a contact from a metacontact
+#define MS_MC_REMOVEFROMMETA "MetaContacts/RemoveFromMetacontact"
+
+
+// added 0.9.13.2 (6/10/05)
+// wParam=(BOOL)disable
+// lParam=0
+// enable/disable the 'hidden group hack' - for clists that support subcontact hiding using 'IsSubcontact' setting
+// should be called once in the clist 'onmodulesloaded' event handler (which, since it's loaded after the db, will be called
+// before the metacontact onmodulesloaded handler where the subcontact hiding is usually done)
+#define MS_MC_DISABLEHIDDENGROUP "MetaContacts/DisableHiddenGroup"
+
+#endif
diff --git a/Plugins/historyevents/sdk/m_updater.h b/Plugins/historyevents/sdk/m_updater.h
new file mode 100644
index 0000000..371b743
--- /dev/null
+++ b/Plugins/historyevents/sdk/m_updater.h
@@ -0,0 +1,146 @@
+#ifndef _M_UPDATER_H
+#define _M_UPDATER_H
+
+// NOTES:
+// - For langpack updates, include a string of the following format in the langpack text file:
+// ";FLID: <file listing name> <version>"
+// version must be four numbers seperated by '.', in the range 0-255 inclusive
+// - Updater will disable plugins that are downloaded but were not active prior to the update (this is so that, if an archive contains e.g. ansi and
+// unicode versions, the correct plugin will be the only one active after the new version is installed)...so if you add a support plugin, you may need
+// to install an ini file to make the plugin activate when miranda restarts after the update
+// - Updater will replace all dlls that have the same internal shortName as a downloaded update dll (this is so that msn1.dll and msn2.dll, for example,
+// will both be updated) - so if you have a unicode and a non-unicode version of a plugin in your archive, you should make the internal names different (which will break automatic
+// updates from the file listing if there is only one file listing entry for both versions, unless you use the 'MS_UPDATE_REGISTER' service below)
+// - Updater will install all files in the root of the archive into the plugins folder, except for langpack files that contain the FLID string which go into the root folder (same
+// folder as miranda32.exe)...all folders in the archive will also be copied to miranda's root folder, and their contents transferred into the new folders. The only exception is a
+// special folder called 'root_files' - if there is a folder by that name in the archive, it's contents will also be copied into miranda's root folder - this is intended to be used
+// to install additional dlls etc that a plugin may require)
+
+// if you set Update.szUpdateURL to the following value when registering, as well as setting your beta site and version data,
+// Updater will ignore szVersionURL and pbVersionPrefix, and attempt to find the file listing URL's from the backend XML data.
+// for this to work, the plugin name in pluginInfo.shortName must match the file listing exactly (except for case)
+#define UPDATER_AUTOREGISTER "UpdaterAUTOREGISTER"
+// Updater will also use the backend xml data if you provide URL's that reference the miranda file listing for updates (so you can use that method
+// if e.g. your plugin shortName does not match the file listing) - it will grab the file listing id from the end of these URLs
+
+typedef struct Update_tag {
+ int cbSize;
+ char *szComponentName; // component name as it will appear in the UI (will be translated before displaying)
+
+ char *szVersionURL; // URL where the current version can be found (NULL to disable)
+ BYTE *pbVersionPrefix; // bytes occuring in VersionURL before the version, used to locate the version information within the URL data
+ // (note that this URL could point at a binary file - dunno why, but it could :)
+ int cpbVersionPrefix; // number of bytes pointed to by pbVersionPrefix
+ char *szUpdateURL; // URL where dll/zip is located
+ // set to UPDATER_AUTOREGISTER if you want Updater to find the file listing URLs (ensure plugin shortName matches file listing!)
+
+ char *szBetaVersionURL; // URL where the beta version can be found (NULL to disable betas)
+ BYTE *pbBetaVersionPrefix; // bytes occuring in VersionURL before the version, used to locate the version information within the URL data
+ int cpbBetaVersionPrefix; // number of bytes pointed to by pbVersionPrefix
+ char *szBetaUpdateURL; // URL where dll/zip is located
+
+ BYTE *pbVersion; // bytes of current version, used for comparison with those in VersionURL
+ int cpbVersion; // number of bytes pointed to by pbVersion
+
+ char *szBetaChangelogURL; // url for displaying changelog for beta versions
+} Update;
+
+// register a comonent with Updater
+//
+// wparam = 0
+// lparam = (LPARAM)&Update
+#define MS_UPDATE_REGISTER "Update/Register"
+
+// utility functions to create a version string from a DWORD or from pluginInfo
+// point buf at a buffer at least 16 chars wide - but note the version string returned may be shorter
+//
+__inline static char *CreateVersionString(DWORD version, char *buf) {
+ mir_snprintf(buf, 16, "%d.%d.%d.%d", (version >> 24) & 0xFF, (version >> 16) & 0xFF, (version >> 8) & 0xFF, version & 0xFF);
+ return buf;
+}
+
+__inline static char *CreateVersionStringPlugin(PLUGININFO *pluginInfo, char *buf) {
+ return CreateVersionString(pluginInfo->version, buf);
+}
+
+
+// register the 'easy' way - use this method if you have no beta URL and the plugin is on the miranda file listing
+// NOTE: the plugin version string on the file listing must be the string version of the version in pluginInfo (i.e. 0.0.0.1,
+// four numbers between 0 and 255 inclusivem, so no letters, brackets, etc.)
+//
+// wParam = (int)fileID - this is the file ID from the file listing (i.e. the number at the end of the download link)
+// lParam = (PLUGININFO*)&pluginInfo
+#define MS_UPDATE_REGISTERFL "Update/RegisterFL"
+
+// this function can be used to 'unregister' components - useful for plugins that register non-plugin/langpack components and
+// may need to change those components on the fly
+// lParam = (char *)szComponentName
+#define MS_UPDATE_UNREGISTER "Update/Unregister"
+
+// this event is fired when the startup process is complete, but NOT if a restart is imminent
+// it is designed for status managment plugins to use as a trigger for beggining their own startup process
+// wParam = lParam = 0 (unused)
+// (added in version 0.1.6.0)
+#define ME_UPDATE_STARTUPDONE "Update/StartupDone"
+
+// this service can be used to enable/disable Updater's global status control
+// it can be called from the StartupDone event handler
+// wParam = (BOOL)enable
+// lParam = 0
+// (added in version 0.1.6.0)
+#define MS_UPDATE_ENABLESTATUSCONTROL "Update/EnableStatusControl"
+
+// An description of usage of the above service and event:
+// Say you are a status control plugin that normally sets protocol or global statuses in your ModulesLoaded event handler.
+// In order to make yourself 'Updater compatible', you would move the status control code from ModulesLoaded to another function,
+// say DoStartup. Then, in ModulesLoaded you would check for the existence of the MS_UPDATE_ENABLESTATUSCONTROL service.
+// If it does not exist, call DoStartup. If it does exist, hook the ME_UPDATE_STARTUPDONE event and call DoStartup from there. You may
+// also wish to call MS_UPDATE_ENABLESTATUSCONTROL with wParam == FALSE at this time, to disable Updater's own status control feature.
+
+// this service can be used to determine whether updates are possible for a component with the given name
+// wParam = 0
+// lParam = (char *)szComponentName
+// returns TRUE if updates are supported, FALSE otherwise
+#define MS_UPDATE_ISUPDATESUPPORTED "Update/IsUpdateSupported"
+
+#endif
+
+
+/////////////// Usage Example ///////////////
+
+#ifdef EXAMPLE_CODE
+
+// you need to #include "m_updater.h" and HookEvent(ME_SYSTEM_MODULESLOADED, OnModulesLoaded) in your Load function...
+
+int OnModulesLoaded(WPARAM wParam, LPARAM lParam) {
+
+ Update update = {0}; // for c you'd use memset or ZeroMemory...
+ char szVersion[16];
+
+ update.cbSize = sizeof(Update);
+
+ update.szComponentName = pluginInfo.shortName;
+ update.pbVersion = (BYTE *)CreateVersionString(&pluginInfo, szVersion);
+ update.cpbVersion = strlen((char *)update.pbVersion);
+
+ // these are the three lines that matter - the archive, the page containing the version string, and the text (or data)
+ // before the version that we use to locate it on the page
+ // (note that if the update URL and the version URL point to standard file listing entries, the backend xml
+ // data will be used to check for updates rather than the actual web page - this is not true for beta urls)
+ update.szUpdateURL = "http://scottellis.com.au:81/test/updater.zip";
+ update.szVersionURL = "http://scottellis.com.au:81/test/updater_test.html";
+ update.pbVersionPrefix = (BYTE *)"Updater version ";
+
+ update.cpbVersionPrefix = strlen((char *)update.pbVersionPrefix);
+
+ // do the same for the beta versions of the above struct members if you wish to allow beta updates from another URL
+
+ CallService(MS_UPDATE_REGISTER, 0, (WPARAM)&update);
+
+ // Alternatively, to register a plugin with e.g. file ID 2254 on the file listing...
+ // CallService(MS_UPDATE_REGISTERFL, (WPARAM)2254, (LPARAM)&pluginInfo);
+
+ return 0;
+}
+
+#endif
diff --git a/Plugins/historyevents/sdk/m_variables.h b/Plugins/historyevents/sdk/m_variables.h
new file mode 100644
index 0000000..3f13c96
--- /dev/null
+++ b/Plugins/historyevents/sdk/m_variables.h
@@ -0,0 +1,718 @@
+/*
+ Variables Plugin for Miranda-IM (www.miranda-im.org)
+ Copyright 2003-2006 P. Boon
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#ifndef __M_VARS
+#define __M_VARS
+
+#if !defined(_TCHAR_DEFINED)
+#include <tchar.h>
+#endif
+
+#ifndef VARIABLES_NOHELPER
+#include <m_button.h>
+#endif
+
+// --------------------------------------------------------------------------
+// Memory management
+// --------------------------------------------------------------------------
+
+// Release memory that was allocated by the Variables plugin, e.g. returned
+// strings.
+
+#define MS_VARS_FREEMEMORY "Vars/FreeMemory"
+
+// Parameters:
+// ------------------------
+// wParam = (WPARAM)(void *)pntr
+// Pointer to memory that was allocated by the Variables plugin (e.g. a
+// returned string) (can be NULL).
+// lParam = 0
+
+// Return Value:
+// ------------------------
+// Does return 0 on success, nozero otherwise.
+
+// Note: Do only use this service to free memory that was *explicitliy*
+// stated that it should be free with this service.
+
+
+
+#define MS_VARS_GET_MMI "Vars/GetMMI"
+
+// Get Variable's RTL/CRT function poiners to malloc(), free() and
+// realloc().
+
+// Parameters:
+// ------------------------
+// wParam = 0
+// lParam = (LPARAM) &MM_INTERFACE
+// Pointer to a memory manager interface struct (see m_system.h).
+
+// Return Value:
+// ------------------------
+// Returns 0 on success, nozero otherwise
+
+// Note: Works exactly the same as the MS_SYSTEM_GET_MMI service
+// service of m_system.h.
+
+// Helper function for easy using:
+#ifndef VARIABLES_NOHELPER
+__inline static void variables_free(void *pntr) {
+
+ CallService(MS_VARS_FREEMEMORY, (WPARAM)pntr, 0);
+}
+#endif
+
+
+
+// --------------------------------------------------------------------------
+// String formatting
+// --------------------------------------------------------------------------
+
+#define MS_VARS_FORMATSTRING "Vars/FormatString"
+
+// This service can be used to parse tokens in a text. The tokens will be
+// replaced by their resolved values. A token can either be a field or a
+// function. A field takes no arguments and is represented between
+// %-characters, e.g. "%winampsong%". A function can take any number of
+// arguments and is represented by a ? or !-character followed by the name
+// of the function and a list of arguments, e.g. "?add(1,2)".
+
+// Parameters:
+// ------------------------
+// wParam = (WPARAM)(FORMATINFO *)&fi
+// See below.
+// lParam = 0
+
+// Return Value:
+// ------------------------
+// Returns a pointer to the resolved string or NULL in case of an error.
+
+// Note: The returned pointer needs to be freed using MS_VARS_FREEMEMORY.
+
+typedef struct {
+ int cbSize; // Set this to sizeof(FORMATINFO).
+ int flags; // Flags to use (see FIF_* below).
+ union {
+ char *szFormat; // Text in which the tokens will be replaced (can't be
+ // NULL).
+ WCHAR *wszFormat;
+ TCHAR *tszFormat;
+ };
+ union {
+ char *szExtraText; // Extra, context-specific string (can be NULL) ->
+ // The field "extratext" will be replaced by this
+ // string. (Previously szSource).
+ WCHAR *wszExtraText;
+ TCHAR *tszExtraText;
+ };
+ HANDLE hContact; // Handle to contact (can be NULL) -> The field "subject"
+ // represents this contact.
+ int pCount; // (output) Number of succesful parsed tokens, needs to be set
+ // to 0 before the call
+ int eCount; // (output) Number of failed tokens, needs to be set to 0
+ // before the call
+ union {
+ char **szaTemporaryVars; // Temporary variables valid only in the duration of the format call
+ TCHAR **tszaTemporaryVars; // By pos: [i] is var name, [i + 1] is var value
+ WCHAR **wszaTemporaryVars;
+ };
+ int cbTemporaryVarsSize; // Number of elements in szaTemporaryVars array
+
+} FORMATINFO;
+
+#define FORMATINFOV2_SIZE 28
+
+// Possible flags:
+#define FIF_UNICODE 0x01 // Expects and returns unicode text (WCHAR*).
+
+#if defined(UNICODE) || defined(_UNICODE)
+#define FIF_TCHAR FIF_UNICODE // Strings in structure are TCHAR*.
+#else
+#define FIF_TCHAR 0
+#endif
+
+// Helper functions for easy using:
+
+// Helper #1: variables_parse
+// ------------------------
+// The returned string needs to be freed using MS_VARS_FREEMEMORY.
+
+#ifndef VARIABLES_NOHELPER
+__inline static TCHAR *variables_parse(TCHAR *tszFormat, TCHAR *tszExtraText, HANDLE hContact) {
+
+ FORMATINFO fi;
+
+ ZeroMemory(&fi, sizeof(fi));
+ fi.cbSize = sizeof(fi);
+ fi.tszFormat = tszFormat;
+ fi.tszExtraText = tszExtraText;
+ fi.hContact = hContact;
+ fi.flags = FIF_TCHAR;
+
+ return (TCHAR *)CallService(MS_VARS_FORMATSTRING, (WPARAM)&fi, 0);
+}
+#endif
+
+__inline static TCHAR *variables_parse_ex(TCHAR *tszFormat, TCHAR *tszExtraText, HANDLE hContact,
+ TCHAR **tszaTemporaryVars, int cbTemporaryVarsSize) {
+
+ FORMATINFO fi;
+
+ ZeroMemory(&fi, sizeof(fi));
+ fi.cbSize = sizeof(fi);
+ fi.tszFormat = tszFormat;
+ fi.tszExtraText = tszExtraText;
+ fi.hContact = hContact;
+ fi.flags = FIF_TCHAR;
+ fi.tszaTemporaryVars = tszaTemporaryVars;
+ fi.cbTemporaryVarsSize = cbTemporaryVarsSize;
+
+ return (TCHAR *)CallService(MS_VARS_FORMATSTRING, (WPARAM)&fi, 0);
+}
+// Helper #2: variables_parsedup
+// ------------------------
+// Returns a _strdup()'ed copy of the unparsed string when Variables is not
+// installed, returns a strdup()'ed copy of the parsed result otherwise.
+
+// Note: The returned pointer needs to be released using your own free().
+
+#ifndef VARIABLES_NOHELPER
+__inline static TCHAR *variables_parsedup(TCHAR *tszFormat, TCHAR *tszExtraText, HANDLE hContact) {
+
+ if (ServiceExists(MS_VARS_FORMATSTRING)) {
+ FORMATINFO fi;
+ TCHAR *tszParsed, *tszResult;
+
+ ZeroMemory(&fi, sizeof(fi));
+ fi.cbSize = sizeof(fi);
+ fi.tszFormat = tszFormat;
+ fi.tszExtraText = tszExtraText;
+ fi.hContact = hContact;
+ fi.flags |= FIF_TCHAR;
+ tszParsed = (TCHAR *)CallService(MS_VARS_FORMATSTRING, (WPARAM)&fi, 0);
+ if (tszParsed) {
+ tszResult = _tcsdup(tszParsed);
+ CallService(MS_VARS_FREEMEMORY, (WPARAM)tszParsed, 0);
+ return tszResult;
+ }
+ }
+ return tszFormat?_tcsdup(tszFormat):tszFormat;
+}
+
+__inline static TCHAR *variables_parsedup_ex(TCHAR *tszFormat, TCHAR *tszExtraText, HANDLE hContact,
+ TCHAR **tszaTemporaryVars, int cbTemporaryVarsSize) {
+
+ if (ServiceExists(MS_VARS_FORMATSTRING)) {
+ FORMATINFO fi;
+ TCHAR *tszParsed, *tszResult;
+
+ ZeroMemory(&fi, sizeof(fi));
+ fi.cbSize = sizeof(fi);
+ fi.tszFormat = tszFormat;
+ fi.tszExtraText = tszExtraText;
+ fi.hContact = hContact;
+ fi.flags |= FIF_TCHAR;
+ fi.tszaTemporaryVars = tszaTemporaryVars;
+ fi.cbTemporaryVarsSize = cbTemporaryVarsSize;
+ tszParsed = (TCHAR *)CallService(MS_VARS_FORMATSTRING, (WPARAM)&fi, 0);
+ if (tszParsed) {
+ tszResult = _tcsdup(tszParsed);
+ CallService(MS_VARS_FREEMEMORY, (WPARAM)tszParsed, 0);
+ return tszResult;
+ }
+ }
+ return tszFormat?_tcsdup(tszFormat):tszFormat;
+}
+#endif
+
+
+
+// --------------------------------------------------------------------------
+// Register tokens
+// --------------------------------------------------------------------------
+
+// Plugins can define tokens which will be parsed by the Variables plugin.
+
+#define MS_VARS_REGISTERTOKEN "Vars/RegisterToken"
+
+// With this service you can define your own token. The newly added tokens
+// using this service are taken into account on every call to
+// MS_VARS_FORMATSTRING.
+
+// Parameters:
+// ------------------------
+// wParam = 0
+// lParam = (LPARAM)(TOKENREGISTER*)&tr
+// See below.
+
+// Return Value:
+// ------------------------
+// Returns 0 on success, nonzero otherwise. Existing tokens will be
+// 'overwritten' if registered twice.
+
+// Needed for szService and parseFunction:
+typedef struct {
+ int cbSize; // You need to check if this is >=sizeof(ARGUMENTSINFO)
+ // (already filled in).
+ FORMATINFO *fi; // Arguments passed to MS_VARS_FORMATSTRING.
+ unsigned int argc; // Number of elements in the argv array.
+ union {
+ char **argv; // Argv[0] will be the token name, the following elements
+ // are the additional arguments.
+ WCHAR **wargv; // If the registered token was registered as a unicode
+ // token, wargv should be accessed.
+ TCHAR **targv;
+ };
+ int flags; // (output) You can set flags here (initially 0), use the
+ // AIF_* flags (see below).
+} ARGUMENTSINFO;
+
+// Available flags for ARGUMENTSINFO:
+// Set the flags of the ARGUMENTSINFO struct to any of these to influence
+// further parsing.
+#define AIF_DONTPARSE 0x01 // Don't parse the result of this function,
+ // usually the result of a token is parsed
+ // again, if the `?` is used as a function
+ // character.
+#define AIF_FALSE 0x02 // The function returned logical false.
+
+// Definition of parse/cleanup functions:
+typedef char* (*VARPARSEFUNCA)(ARGUMENTSINFO *ai);
+typedef WCHAR* (*VARPARSEFUNCW)(ARGUMENTSINFO *ai);
+typedef void (*VARCLEANUPFUNCA)(char *szReturn);
+typedef void (*VARCLEANUPFUNCW)(WCHAR *wszReturn);
+
+#if defined(UNICODE) || defined(_UNICODE)
+#define VARPARSEFUNC VARPARSEFUNCW
+#define VARCLEANUPFUNC VARCLEANUPFUNCW
+#else
+#define VARPARSEFUNC VARPARSEFUNCA
+#define VARCLEANUPFUNC VARCLEANUPFUNCA
+#endif
+
+typedef struct {
+ int cbSize; // Set this to sizeof(TOKENREGISTER).
+ union {
+ char *szTokenString; // Name of the new token to be created, without %,
+ // ?, ! etc. signs (can't be NULL).
+ WCHAR *wszTokenString;
+ TCHAR *tszTokenString;
+ };
+ union {
+ char *szService; // Name of a service that is used to request the
+ // token's value, if no service is used, a function
+ // and TRF_PARSEFUNC must be used.
+ VARPARSEFUNCA parseFunction; // See above, use with TRF_PARSEFUNC.
+ VARPARSEFUNCW parseFunctionW;
+ VARPARSEFUNC parseFunctionT;
+ };
+ union {
+ char *szCleanupService; // Name of a service to be called when the
+ // memory allocated in szService can be freed
+ // (only used when flag VRF_CLEANUP is set,
+ // else set this to NULL).
+ VARCLEANUPFUNCA cleanupFunction; // See above, use with TRF_CLEANUPFUNC.
+ VARCLEANUPFUNCW cleanupFunctionW;
+ VARCLEANUPFUNC cleanupFunctionT;
+ };
+ char *szHelpText; // Help info shown in help dialog (can be NULL). Has to
+ // be in the following format:
+ // "subject\targuments\tdescription"
+ // (Example: "math\t(x, y ,...)\tx + y + ..."), or:
+ // "subject\tdescription"
+ // (Example: "miranda\tPath to the Miranda-IM
+ // executable").
+ // Note: subject and description are translated by
+ // Variables.
+ int memType; // Describes which method Varibale's plugin needs to use to
+ // free the returned buffer, use one of the VR_MEM_* values
+ // (see below). Only valid if the flag VRF_FREEMEM is set,
+ // use TR_MEM_OWNER otherwise).
+ int flags; // Flags to use (see below), one of TRF_* (see below).
+} TOKENREGISTER;
+
+// Available Memory Storage Types:
+// These values describe which method Variables Plugin will use to free the
+// buffer returned by the parse function or service
+#define TR_MEM_VARIABLES 1 // Memory is allocated using the functions
+ // retrieved by MS_VARS_GET_MMI.
+#define TR_MEM_MIRANDA 2 // Memory is allocated using Miranda's Memory
+ // Manager Interface (using the functions
+ // returned by MS_SYSTEM_GET_MMI), if
+ // VRF_FREEMEM is set, the memory will be
+ // freed by Variables.
+#define TR_MEM_OWNER 3 // Memory is owned by the calling plugin
+ // (can't be freed by Variables Plugin
+ // automatically). This should be used if
+ // VRF_FREEMEM is not specified in the flags.
+
+// Available Flags for TOKENREGISTER:
+#define TRF_FREEMEM 0x01 // Variables Plugin will automatically free the
+ // pointer returned by the parse function or
+ // service (which method it will us is
+ // specified in memType -> see above).
+#define TRF_CLEANUP 0x02 // Call cleanup service or function, notifying
+ // that the returned buffer can be freed.
+ // Normally you should use either TRF_FREEMEM
+ // or TRF_CLEANUP.
+#define TRF_PARSEFUNC 0x40 // parseFunction will be used instead of a
+ // service.
+#define TRF_CLEANUPFUNC 0x80 // cleanupFunction will be used instead of a
+ // service.
+#define TRF_USEFUNCS TRF_PARSEFUNC|TRF_CLEANUPFUNC
+#define TRF_UNPARSEDARGS 0x04 // Provide the arguments for the parse
+ // function in their raw (unparsed) form.
+ // By default, arguments are parsed before
+ // presenting them to the parse function.
+#define TRF_FIELD 0x08 // The token can be used as a %field%.
+#define TRF_FUNCTION 0x10 // The token can be used as a ?function().
+ // Normally you should use either TRF_FIELD or
+ // TRF_FUNCTION.
+#define TRF_UNICODE 0x20 // Strings in structure are unicode (WCHAR*).
+ // In this case, the strings pointing to the
+ // arguments in the ARGUMENTS struct are
+ // unicode also. The returned buffer is
+ // expected to be unicode also, and the
+ // unicode parse and cleanup functions are
+ // called.
+
+#if defined(UNICODE) || defined(_UNICODE)
+#define TRF_TCHAR TRF_UNICODE // Strings in structure are TCHAR*.
+#else
+#define TRF_TCHAR 0
+#endif
+
+// Deprecated:
+#define TRF_CALLSVC TRF_CLEANUP
+
+// Callback Service (szService) / parseFunction:
+// ------------------------
+// Service that is called automatically by the Variable's Plugin to resolve a
+// registered variable.
+
+// Parameters:
+// wParam = 0
+// lParam = (LPARAM)(ARGUMENTSINFO *)&ai
+// see above
+
+// Return Value:
+// Needs to return the pointer to a dynamically allocacated string or NULL.
+// A return value of NULL is regarded as an error (eCount will be increaded).
+// Flags in the ARGUMENTSINFO struct can be set (see above).
+
+// Callback Service (szCallbackService) / cleanupFunction:
+// ------------------------
+// This service is called when the memory that was allocated by the parse
+// function or service can be freed. Note: It will only be called when the
+// flag VRF_CLEANUP of TOKENREGISTER is set.
+
+// Parameters:
+// wParam = 0
+// lParam = (LPARAM)(char *)&res
+// Result from parse function or service (pointer to a string).
+
+// Return Value:
+// Should return 0 on success.
+
+
+
+// --------------------------------------------------------------------------
+// Show the help dialog
+// --------------------------------------------------------------------------
+
+// Plugins can invoke Variables' help dialog which can be used for easy input
+// by users.
+
+#define MS_VARS_SHOWHELPEX "Vars/ShowHelpEx"
+
+// This service can be used to open the help dialog of Variables. This dialog
+// provides easy input for the user and/or information about the available
+// tokens.
+
+// Parameters:
+// ------------------------
+// wParam = (WPARAM)(HWND)hwndParent
+// lParam = (LPARAM)(VARHELPINFO)&vhi
+// See below.
+
+// Return Value:
+// ------------------------
+// Returns 0 on succes, any other value on error.
+
+typedef struct {
+ int cbSize; // Set to sizeof(VARHELPINFO).
+ FORMATINFO *fi; // Used for both input and output. If this pointer is not
+ // NULL, the information is used as the initial values for
+ // the dialog.
+ HWND hwndCtrl; // Used for both input and output. The window text of this
+ // window will be read and used as the initial input of the
+ // input dialog. If the user presses the OK button the window
+ // text of this window will be set to the text of the input
+ // field and a EN_CHANGE message via WM_COMMAND is send to
+ // this window. (Can be NULL).
+ char *szSubjectDesc; // The description of the %subject% token will be set
+ // to this text, if not NULL. This is translated
+ // automatically.
+ char *szExtraTextDesc; // The description of the %extratext% token will be
+ // set to this text, if not NULL. This is translated
+ // automatically.
+ int flags; // Flags, see below.
+} VARHELPINFO;
+
+
+// Flags for VARHELPINFO
+#define VHF_TOKENS 0x00000001 // Create a dialog with the list of
+ // tokens
+#define VHF_INPUT 0x00000002 // Create a dialog with an input
+ // field (this contains the list of
+ // tokens as well).
+#define VHF_SUBJECT 0x00000004 // Create a dialog to select a
+ // contact for the %subject% token.
+#define VHF_EXTRATEXT 0x00000008 // Create a dialog to enter a text
+ // for the %extratext% token.
+#define VHF_HELP 0x00000010 // Create a dialog with help info.
+#define VHF_HIDESUBJECTTOKEN 0x00000020 // Hide the %subject% token in the
+ // list of tokens.
+#define VHF_HIDEEXTRATEXTTOKEN 0x00000040 // Hide the %extratext% token in
+ // the list of tokens.
+#define VHF_DONTFILLSTRUCT 0x00000080 // Don't fill the struct with the
+ // new information if OK is pressed
+#define VHF_FULLFILLSTRUCT 0x00000100 // Fill all members of the struct
+ // when OK is pressed. By default
+ // only szFormat is set. With this
+ // flag on, hContact and
+ // szExtraText are also set.
+#define VHF_SETLASTSUBJECT 0x00000200 // Set the last contact that was
+ // used in the %subject% dialog in
+ // case fi.hContact is NULL.
+
+// Predefined flags
+#define VHF_FULLDLG VHF_INPUT|VHF_SUBJECT|VHF_EXTRATEXT|VHF_HELP
+#define VHF_SIMPLEDLG VHF_INPUT|VHF_HELP
+#define VHF_NOINPUTDLG VHF_TOKENS|VHF_HELP
+
+// If the service fills information in the struct for szFormat or szExtraText,
+// these members must be free'd using the free function of Variables.
+// If wParam==NULL, the dialog is created modeless. Only one dialog can be
+// shown at the time.
+// If both hwndCtrl and fi are NULL, the user input will not be retrievable.
+// In this case, the dialog is created with only a "Close" button, instead of
+// the "OK" and "Cancel" buttons.
+// In case of modeless dialog and fi != NULL, please make sure this pointer
+// stays valid while the dialog is open.
+
+// Helper function for easy use in standard case:
+#ifndef VARIABLES_NOHELPER
+__inline static int variables_showhelp(HWND hwndDlg, UINT uIDEdit, int flags, char *szSubjectDesc, char *szExtraDesc) {
+
+ VARHELPINFO vhi;
+
+ ZeroMemory(&vhi, sizeof(VARHELPINFO));
+ vhi.cbSize = sizeof(VARHELPINFO);
+ if (flags == 0) {
+ flags = VHF_SIMPLEDLG;
+ }
+ vhi.flags = flags;
+ vhi.hwndCtrl = GetDlgItem(hwndDlg, uIDEdit);
+ vhi.szSubjectDesc = szSubjectDesc;
+ vhi.szExtraTextDesc = szExtraDesc;
+
+ return CallService(MS_VARS_SHOWHELPEX, (WPARAM)hwndDlg, (LPARAM)&vhi);
+}
+#endif
+
+
+#define MS_VARS_GETSKINITEM "Vars/GetSkinItem"
+
+// This service can be used to get the icon you can use for example on the
+// Variables help button in your options screen. You can also get the tooltip
+// text to use with such a button. If icon library is available the icon will
+// be retrieved from icon library manager, otherwise the default is returned.
+
+// Parameters:
+// ------------------------
+// wParam = (WPARAM)0
+// lParam = (LPARAM)VSI_* (see below)
+
+// Return Value:
+// ------------------------
+// Depends on the information to retrieve (see below).
+
+// VSI_ constants
+#define VSI_HELPICON 1 // Can be used on the button accessing the
+ // Variables help dialog. Returns (HICON)hIcon on
+ // success or NULL on failure;
+#define VSI_HELPTIPTEXT 2 // Returns the tooltip text you can use for the
+ // help button. Returns (char *)szTipText, a
+ // static, translated buffer containing the help
+ // text or NULL on error.
+
+// Helper to set the icon on a button accessing the help dialog.
+// Preferably a 16x14 MButtonClass control, but it works on a standard
+// button control as well. If no icon is availble (because of old version of
+// Variables) the string "V" is shown on the button. If Variables is not
+// available, the button will be hidden.
+#ifndef VARIABLES_NOHELPER
+__inline static int variables_skin_helpbutton(HWND hwndDlg, UINT uIDButton) {
+
+ int res;
+ HICON hIcon;
+ TCHAR tszClass[32];
+
+ hIcon = NULL;
+ res = 0;
+ if (ServiceExists(MS_VARS_GETSKINITEM)) {
+ hIcon = (HICON)CallService(MS_VARS_GETSKINITEM, 0, (LPARAM)VSI_HELPICON);
+ }
+ GetClassName(GetDlgItem(hwndDlg, uIDButton), tszClass, sizeof(tszClass));
+ if (!_tcscmp(tszClass, _T("Button"))) {
+ if (hIcon != NULL) {
+ SetWindowLong(GetDlgItem(hwndDlg, uIDButton), GWL_STYLE, GetWindowLong(GetDlgItem(hwndDlg, uIDButton), GWL_STYLE)|BS_ICON);
+ SendMessage(GetDlgItem(hwndDlg, uIDButton), BM_SETIMAGE, (WPARAM)IMAGE_ICON, (LPARAM)hIcon);
+ }
+ else {
+ SetWindowLong(GetDlgItem(hwndDlg, uIDButton), GWL_STYLE, GetWindowLong(GetDlgItem(hwndDlg, uIDButton), GWL_STYLE)&~BS_ICON);
+ SetDlgItemText(hwndDlg, uIDButton, _T("V"));
+ }
+ }
+ else if (!_tcscmp(tszClass, MIRANDABUTTONCLASS)) {
+ if (hIcon != NULL) {
+ char *szTipInfo;
+
+ SendMessage(GetDlgItem(hwndDlg, uIDButton), BM_SETIMAGE, (WPARAM)IMAGE_ICON, (LPARAM)hIcon);
+ if (ServiceExists(MS_VARS_GETSKINITEM)) {
+ szTipInfo = (char *)CallService(MS_VARS_GETSKINITEM, 0, (LPARAM)VSI_HELPTIPTEXT);
+ }
+ if (szTipInfo == NULL) {
+ szTipInfo = Translate("Open String Formatting Help");
+ }
+ SendMessage(GetDlgItem(hwndDlg, uIDButton), BUTTONADDTOOLTIP, (WPARAM)szTipInfo, 0);
+ SendDlgItemMessage(hwndDlg, uIDButton, BUTTONSETASFLATBTN, 0, 0);
+ }
+ else {
+ SetDlgItemText(hwndDlg, uIDButton, _T("V"));
+ }
+ }
+ else {
+ res = -1;
+ }
+ ShowWindow(GetDlgItem(hwndDlg, uIDButton), ServiceExists(MS_VARS_FORMATSTRING));
+
+ return res;
+}
+#endif
+
+
+#define MS_VARS_SHOWHELP "Vars/ShowHelp"
+
+// WARNING: This service is obsolete, please use MS_VARS_SHOWHELPEX
+
+// Shows a help dialog where all possible tokens are displayed. The tokens
+// are explained on the dialog, too. The user can edit the initial string and
+// insert as many tokens as he likes.
+
+// Parameters:
+// ------------------------
+// wParam = (HWND)hwndEdit
+// Handle to an edit control in which the modified string
+// should be inserted (When the user clicks OK in the dialog the edited
+// string will be set to hwndEdit) (can be NULL).
+// lParam = (char *)pszInitialString
+// String that the user is provided with initially when
+// the dialog gets opened (If this is NULL then the current text in the
+// hwndEdit edit control will be used) (can be NULL).
+
+// Return Value:
+// ------------------------
+// Returns the handle to the help dialog (HWND).
+
+// Note: Only one help dialog can be opened at a time. When the dialog gets
+// closed an EN_CHANGE of the edit controll will be triggered because the
+// contents were updated. (Only when user selected OK).
+
+// Example:
+// CallService(MS_VARS_SHOWHELP, (WPARAM)hwndEdit, (LPARAM)"some initial text");
+
+// --------------------------------------------------------------------------
+// Retrieve a contact's HANDLE given a string
+// --------------------------------------------------------------------------
+
+#define MS_VARS_GETCONTACTFROMSTRING "Vars/GetContactFromString"
+
+// Searching for contacts in the database. You can find contacts in db by
+// searching for their name, e.g first name.
+
+// Parameters:
+// ------------------------
+// wParam = (WPARAM)(CONTACTSINFO *)&ci
+// See below.
+// lParam = 0
+
+// Return Value:
+// ------------------------
+// Returns number of contacts found matching the given string representation.
+// The hContacts array of CONTACTSINFO struct contains these hContacts after
+// the call.
+
+// Note: The hContacts array needs to be freed after use using
+// MS_VARS_FREEMEMORY.
+
+typedef struct {
+ int cbSize; // Set this to sizeof(CONTACTSINFO).
+ union {
+ char *szContact; // String to search for, e.g. last name (can't be NULL).
+ WCHAR *wszContact;
+ TCHAR *tszContact;
+ };
+ HANDLE *hContacts; // (output) Array of contacts found.
+ DWORD flags; // Contact details that will be matched with the search
+ // string (flags can be combined).
+} CONTACTSINFO;
+
+// Possible flags:
+#define CI_PROTOID 0x00000001 // The contact in the string is encoded
+ // in the format <PROTOID:UNIQUEID>, e.g.
+ // <ICQ:12345678>.
+#define CI_NICK 0x00000002 // Search nick names.
+#define CI_LISTNAME 0x00000004 // Search custom names shown in contact
+ // list.
+#define CI_FIRSTNAME 0x00000008 // Search contact's first names (contact
+ // details).
+#define CI_LASTNAME 0x00000010 // Search contact's last names (contact
+ // details).
+#define CI_EMAIL 0x00000020 // Search contact's email adresses
+ // (contact details).
+#define CI_UNIQUEID 0x00000040 // Search unique ids of the contac, e.g.
+ // UIN.
+#define CI_CNFINFO 0x40000000 // Searches one of the CNF_* flags (set
+ // flags to CI_CNFINFO|CNF_X), only one
+ // CNF_ type possible
+#define CI_UNICODE 0x80000000 // tszContact is a unicode string
+ // (WCHAR*).
+
+#if defined(UNICODE) || defined(_UNICODE)
+#define CI_TCHAR CI_UNICODE // Strings in structure are TCHAR*.
+#else
+#define CI_TCHAR 0
+#endif
+
+
+
+#endif //__M_VARS
diff --git a/Plugins/historykeeper/Docs/historykeeper_changelog.txt b/Plugins/historykeeper/Docs/historykeeper_changelog.txt
new file mode 100644
index 0000000..a70caa0
--- /dev/null
+++ b/Plugins/historykeeper/Docs/historykeeper_changelog.txt
@@ -0,0 +1,84 @@
+History Keeper
+
+Changelog:
+
+. 0.2.1.0
+ + Only notify when starting/ending idle
+
+. 0.2.0.0
+ + Added tracking of idle
+ + Support for accouts
+ * Fix for icon in popups
+
+. 0.0.1.5
+ + Support for new speak API
+
+. 0.0.1.4
+ + Improved handling of metacontacts
+
+. 0.0.1.3
+ * Fixed typo in options
+ + Added langpack
+
+. 0.0.1.2
+ * Fix for bugs introduced in latest version
+
+. 0.0.1.1
+ + Group popups of status/status messages and x-status/x-status messages
+ + Option to customize time to wait before notifying a change
+ * Fixes to avoid some uneeded popups
+ * Now it won't crash with old version of variables plugin (but will not work very well...)
+
+. 0.0.1.0
+ + Added X-Status Message history
+ * Changed XStatus to X-Status
+ * Fix for storing some settings in db as temporary values
+ * Listening changes will wait 30s before notification (to avoid duplicated notifications)
+
+. 0.0.0.9
+ + Added 2 menus, one for log and one for notify
+ + Added hidden option HistoryKeeper/UnifiedContextMenus
+ + Added 'All Contacts' option to Notifications tab
+
+. 0.0.0.8
+ * Fix for crash
+ + Added listening to history
+
+. 0.0.0.7
+ + Added variables support
+ + Added option page to set log/notification types per contact
+ * Fixes for bugs
+ + Hidden settings: HistoryKeeper/PerDefaultLogSubcontacts , HistoryKeeper/PerDefaultNotifySubcontacts
+ + New icons by Angeli-Ka
+
+. 0.0.0.6
+ * Fix for options pages
+ * Fix for notify on connection
+ * Fix for hangup on exit
+
+. 0.0.0.5
+ + Contact menus moved to a submenu
+ * Fix for popups on status changes
+ + Option 'don't popup on protocol connection' changed to 'dont notify on protocol connection (popup + sound + speak)
+ * All DB entries changed to HistoryKeeper db folder (this affected all *Current entries in db, so some notifications will be fired again - sorry)
+ * It won't log/notify anymore events that happened in a subcontact (you can enable logging per subcontact or to enable logging for all subs create a DB entry HistoryKeeper/PerDefaultLogSubcontacts of type BYTE and set it to 1)
+
+. 0.0.0.4
+ + Added sounds support
+ + Added speak plugin support
+
+. 0.0.0.3
+ * Changed handling of notifications (should appear less notifications now)
+ * Fix for showing popups
+ + Added Open message window popup option
+ + Added variable %msg% for Status Change notifications
+ + Added variables %MirVer_new% and %MirVer_old% for Client Change notifications
+ + Added XStatus
+
+. 0.0.0.2
+ + Added log to file option
+ * Fixed crash on options page
+ * Changes in some default options
+
+. 0.0.0.1
+ + Initial version \ No newline at end of file
diff --git a/Plugins/historykeeper/Docs/historykeeper_readme.txt b/Plugins/historykeeper/Docs/historykeeper_readme.txt
new file mode 100644
index 0000000..be47995
--- /dev/null
+++ b/Plugins/historykeeper/Docs/historykeeper_readme.txt
@@ -0,0 +1,31 @@
+History Keeper plugin
+---------------------
+
+This plugin logs some events to the history and show popups.
+
+The events that are handled are:
+ - Nickname changes
+ - Client changes
+ - Idle changes
+ - Status changes
+ - Status Message changes
+ - X-Status changes
+ - X-Status Message changes
+ - Listening changes
+
+For it to work, it depends on some popup plugin (YAPP or Popup+) and on History Events plugin. It also support Variables plugin.
+
+This plugin requires at least Miranda 0.7
+
+The event icons were created by Angeli-Ka (Thanks!).
+
+Also, Faith Healer created another nice set of icons (Thanks!): http://www.pescuma.org/miranda/historykeeper_fh_icons.zip
+
+To report bugs/make suggestions, go to the forum thread: http://forums.miranda-im.org/showthread.php?t=15468
+
+To do:
+ - Integrate avh with it?
+ - Support remembering status message per status
+ - On status change, allow customizing popups per status change
+
+
diff --git a/Plugins/historykeeper/Docs/historykeeper_version.txt b/Plugins/historykeeper/Docs/historykeeper_version.txt
new file mode 100644
index 0000000..873ff1f
--- /dev/null
+++ b/Plugins/historykeeper/Docs/historykeeper_version.txt
@@ -0,0 +1 @@
+History Keeper 0.2.1.0 \ No newline at end of file
diff --git a/Plugins/historykeeper/Docs/langpack_historykeeper.txt b/Plugins/historykeeper/Docs/langpack_historykeeper.txt
new file mode 100644
index 0000000..d2c37d0
--- /dev/null
+++ b/Plugins/historykeeper/Docs/langpack_historykeeper.txt
@@ -0,0 +1,77 @@
+; History Keeper
+; Author: Pescuma
+
+[Client]
+[Listening]
+[Nickname]
+[Status]
+[Status Message]
+[X-Status]
+[X-Status Message]
+[Idle]
+
+; Popups
+[Popups]
+[%s Change]
+[ Tracking ]
+[Show when contacts change their %s]
+[Template:]
+[Show when contacts remove their %s]
+[ Colours ]
+[Background colour]
+[Text colour]
+[Use Windows colours]
+[Use default colours]
+[ Delay ]
+[From popup plugin]
+[Custom]
+[Permanent]
+[ Actions ]
+[On right click:]
+[On left click:]
+[Do nothing]
+[Close popup]
+[Show history]
+[Open message window]
+[Preview]
+
+; Speak
+[Speak]
+[Announce when contacts change their %s]
+[Announce when contacts remove their %s]
+
+; Options
+[History]
+[General]
+[ Track ]
+[Wait]
+[s before notifying (to avoid duplications)]
+[Don't notify on protocol connection]
+[ Protocols ]
+[Enable tracking for these protocols:]
+[ Log to file ]
+[Filename:]
+[Log when contacts change their %s]
+[Log when contacts remove their %s]
+[Notifications]
+[** All contacts **]
+
+; Notifications
+[Log to History]
+[Log to File]
+[Notify using Popups]
+[Notify using Sounds]
+[Notify using Speak plugin]
+
+; Menus
+[Ignore %s changes]
+[Log %s changes]
+[Don't log %s changes]
+[Notify %s changes]
+[Don't notify %s changes]
+
+; Sounds
+[%s change]
+[%s removal]
+
+[<empty>]
diff --git a/Plugins/historykeeper/ZIP/doit.bat b/Plugins/historykeeper/ZIP/doit.bat
new file mode 100644
index 0000000..abccd17
--- /dev/null
+++ b/Plugins/historykeeper/ZIP/doit.bat
@@ -0,0 +1,57 @@
+@echo off
+
+rem Batch file to build and upload files
+rem
+rem TODO: Integration with FL
+
+set name=historykeeper
+
+rem To upload, this var must be set here or in other batch
+rem set ftp=ftp://<user>:<password>@<ftp>/<path>
+
+echo Building %name% ...
+
+msdev ..\%name%.dsp /MAKE "%name% - Win32 Release" /REBUILD
+msdev ..\%name%.dsp /MAKE "%name% - Win32 Unicode Release" /REBUILD
+
+echo Generating files for %name% ...
+
+del *.zip
+del *.dll
+copy ..\..\..\bin\release\Plugins\%name%.dll
+copy "..\..\..\bin\release unicode\Plugins\%name%W.dll"
+copy ..\Docs\%name%_changelog.txt
+copy ..\Docs\%name%_version.txt
+copy ..\Docs\%name%_readme.txt
+mkdir Docs
+cd Docs
+del /Q *.*
+copy ..\..\Docs\%name%_readme.txt
+copy ..\..\Docs\langpack_%name%.txt
+rem copy ..\..\Docs\helppack_%name%.txt
+copy ..\..\m_%name%.h
+cd ..
+
+"C:\Program Files\Filzip\Filzip.exe" -a -rp %name%.zip %name%.dll Docs
+"C:\Program Files\Filzip\Filzip.exe" -a -rp %name%W.zip %name%W.dll Docs
+
+del *.dll
+cd Docs
+del /Q *.*
+cd ..
+rmdir Docs
+
+if "%ftp%"=="" GOTO END
+
+echo Going to upload files...
+pause
+
+"C:\Program Files\FileZilla\FileZilla.exe" -u .\%name%.zip %ftp% -overwrite -close
+"C:\Program Files\FileZilla\FileZilla.exe" -u .\%name%W.zip %ftp% -overwrite -close
+"C:\Program Files\FileZilla\FileZilla.exe" -u .\%name%_changelog.txt %ftp% -overwrite -close
+"C:\Program Files\FileZilla\FileZilla.exe" -u .\%name%_readme.txt %ftp% -overwrite -close
+"C:\Program Files\FileZilla\FileZilla.exe" -u .\%name%_version.txt %ftp% -overwrite -close
+
+:END
+
+echo Done.
diff --git a/Plugins/historykeeper/commons.h b/Plugins/historykeeper/commons.h
new file mode 100644
index 0000000..0f08bd4
--- /dev/null
+++ b/Plugins/historykeeper/commons.h
@@ -0,0 +1,152 @@
+/*
+Copyright (C) 2006-2009 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#ifndef __COMMONS_H__
+# define __COMMONS_H__
+
+
+#include <windows.h>
+#include <tchar.h>
+#include <stdio.h>
+#include <time.h>
+#include <commctrl.h>
+
+
+#define MIRANDA_VER 0x0710
+
+// Miranda headers
+#include <win2k.h>
+#include <newpluginapi.h>
+#include <m_system.h>
+#include <m_protocols.h>
+#include <m_protosvc.h>
+#include <m_clist.h>
+#include <m_contacts.h>
+#include <m_langpack.h>
+#include <m_database.h>
+#include <m_options.h>
+#include <m_utils.h>
+#include <m_updater.h>
+#include <m_metacontacts.h>
+#include <m_popup.h>
+#include <m_history.h>
+#include <m_message.h>
+#include <m_icq.h>
+#include <m_skin.h>
+#include <m_clc.h>
+#include <m_speak.h>
+#include <m_proto_listeningto.h>
+
+#include "../utils/mir_memory.h"
+#include "../utils/mir_options.h"
+#include "../utils/mir_buffer.h"
+#include "../utils/ContactAsyncQueue.h"
+#include "../historyevents/m_historyevents.h"
+
+#include "resource.h"
+#include "m_historykeeper.h"
+#include "options.h"
+#include "popup.h"
+
+
+#define MODULE_NAME "HistoryKeeper"
+
+
+// Global Variables
+extern HINSTANCE hInst;
+extern PLUGINLINK *pluginLink;
+
+
+#define MAX_REGS(_A_) ( sizeof(_A_) / sizeof(_A_[0]) )
+
+
+typedef BOOL (*pfAllowProtocol)(const char *proto);
+typedef void (*pfFormat)(TCHAR *out, size_t out_size, void *val);
+typedef BOOL (*pfEquals)(TCHAR *a, TCHAR *b);
+typedef void (*pfAddVars)(HANDLE hContact, TCHAR **vars, int startAt);
+
+struct HISTORY_TYPE {
+ char *name;
+ char *description;
+ int icon;
+ WORD eventType;
+ pfAllowProtocol fAllowProtocol;
+ pfEquals fEquals;
+ pfFormat fFormat;
+ int historyFlags;
+ BOOL canBeRemoved;
+ BOOL temporary;
+ int subOf;
+ int parentOf;
+ BOOL handleMetacontactsSeparatelly;
+
+ struct {
+ struct {
+ char *module; // -1 -> protocol
+ char *setting;
+ BOOL type;
+ } db;
+ } track;
+
+ struct {
+ DWORD value;
+ BYTE track_only_not_offline;
+ char *change_template;
+ char *remove_template;
+ TCHAR *change_template_popup;
+ TCHAR *remove_template_popup;
+ int ttw;
+ } defs;
+
+ int numAddVars;
+ pfAddVars fAddVars;
+};
+
+extern HISTORY_TYPE types[];
+#define NUM_TYPES 8
+
+struct QueueData
+{
+ int type;
+ int notifyAlso;
+};
+
+
+#define LOG_HISTORY 0
+#define LOG_FILE 1
+#define NOTIFY_POPUP 2
+#define NOTIFY_SOUND 3
+#define NOTIFY_SPEAK 4
+#define NUM_ITEMS 5
+#define NUM_LOG_ITEMS 2
+
+BOOL AllowProtocol(int type, const char *proto);
+BOOL ProtocolEnabled(int type, const char *proto);
+BOOL ContactEnabled(int type, HANDLE hContact, int min = 0, int max = NUM_ITEMS) ;
+BOOL ItemEnabled(int type, HANDLE hContact, int item);
+BOOL EnableItem(int type, HANDLE hContact, int item, BOOL enable);
+
+
+#define TIME_TO_WAIT_BEFORE_SHOW_POPUP_AFTER_CREATION 60000 // ms
+#define TIME_TO_WAIT_BEFORE_NOTIFY_AFTER_CONNECTION 60000 // ms
+
+
+
+#endif // __COMMONS_H__
diff --git a/Plugins/historykeeper/config.cpp b/Plugins/historykeeper/config.cpp
new file mode 100644
index 0000000..a7b7de7
--- /dev/null
+++ b/Plugins/historykeeper/config.cpp
@@ -0,0 +1,198 @@
+/*
+Copyright (C) 2006-2009 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#include "commons.h"
+
+
+/*
+ * Service SameClients MS_FP_SAMECLIENTS
+ * wParam - char * first MirVer value
+ * lParam - char * second MirVer value
+ * return pointer to char string - client desription (DO NOT DESTROY) if clients are same otherwise NULL
+ */
+#define MS_FP_SAMECLIENTS "Fingerprint/SameClients"
+
+
+
+
+// See if a protocol service exists
+__inline static int ProtoServiceExists(const char *szModule,const char *szService)
+{
+ char str[MAXMODULELABELLENGTH];
+ strcpy(str,szModule);
+ strcat(str,szService);
+ return ServiceExists(str);
+}
+
+
+static TCHAR *GetTString(HANDLE hContact, char *module, char *setting)
+{
+ TCHAR *ret = NULL;
+
+ DBVARIANT db = {0};
+ if (DBGetContactSettingTString(hContact, module, setting, &db) == 0)
+ {
+ if (db.ptszVal != NULL && db.ptszVal[0] != _T('\0'))
+ ret = mir_tstrdup(db.ptszVal);
+ DBFreeVariant(&db);
+ }
+
+ if (ret == NULL)
+ ret = mir_tstrdup(TranslateT("<empty>"));
+
+ return ret;
+}
+
+
+static TCHAR *GetCurrentTString(HANDLE hContact, char *module, char *setting)
+{
+ char tmp[256];
+ mir_snprintf(tmp, MAX_REGS(tmp), "%s_%s_Current", module, setting);
+
+ TCHAR *ret = NULL;
+
+ DBVARIANT db = {0};
+ if (DBGetContactSettingTString(hContact, MODULE_NAME, tmp, &db) == 0)
+ {
+ if (db.ptszVal != NULL && db.ptszVal[0] != _T('\0'))
+ ret = mir_tstrdup(db.ptszVal);
+ DBFreeVariant(&db);
+ }
+
+ if (ret == NULL)
+ ret = mir_tstrdup(TranslateT("<empty>"));
+
+ return ret;
+}
+
+
+static void StatusAddVars(HANDLE hContact, TCHAR **vars, int i)
+{
+ vars[i++] = _T("msg");
+ vars[i++] = GetTString(hContact, "CList", "StatusMsg");
+}
+
+
+static void XStatusAddVars(HANDLE hContact, TCHAR **vars, int i)
+{
+ char *proto = (char *) CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM) hContact, 0);
+
+ vars[i++] = _T("msg");
+ vars[i++] = GetTString(hContact, proto, "XStatusMsg");
+}
+
+
+static void ClientAddVars(HANDLE hContact, TCHAR **vars, int i)
+{
+ char *proto = (char *) CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM) hContact, 0);
+
+ vars[i++] = _T("MirVer_new");
+ vars[i++] = GetTString(hContact, proto, "MirVer");
+ vars[i++] = _T("MirVer_old");
+ vars[i++] = GetCurrentTString(hContact, proto, "MirVer");
+}
+
+
+BOOL SMHAllowProtocol(const char *proto)
+{
+ return (CallProtoService(proto, PS_GETCAPS, PFLAGNUM_1, 0) & PF1_MODEMSGRECV) != 0;
+}
+
+
+BOOL XStatusAllowProtocol(const char *proto)
+{
+ return ProtoServiceExists(proto, PS_ICQ_GETCUSTOMSTATUS);
+}
+
+
+BOOL ListeningToAllowProtocol(const char *proto)
+{
+ return ProtoServiceExists(proto, PS_SET_LISTENINGTO);
+}
+
+
+void StatusFormat(TCHAR *out, size_t out_size, void *val)
+{
+ lstrcpyn(out, (TCHAR *) CallService(MS_CLIST_GETSTATUSMODEDESCRIPTION, (DWORD) val, GCMDF_TCHAR), out_size);
+}
+
+
+void IdleFormat(TCHAR *out, size_t out_size, void *val)
+{
+ out[0] = 0;
+}
+
+
+void ClientFormat(TCHAR *out, size_t out_size, void *val)
+{
+ TCHAR *str = (TCHAR *) val;
+ char *name = NULL;
+
+ if (str[0] != _T('\0') && ServiceExists(MS_FP_SAMECLIENTS))
+ {
+ char *tmp = mir_t2a(str);
+ name = (char *) CallService(MS_FP_SAMECLIENTS, (WPARAM) tmp, (LPARAM) tmp);
+ mir_free(tmp);
+ }
+
+ if (name != NULL)
+ {
+ TCHAR *tmp = mir_a2t(name);
+ lstrcpyn(out, tmp, out_size);
+ mir_free(tmp);
+ }
+ else
+ {
+ lstrcpyn(out, str, out_size);
+ }
+}
+
+BOOL ClientEquals(TCHAR *a, TCHAR *b)
+{
+ if (ServiceExists(MS_FP_SAMECLIENTS))
+ {
+#ifdef UNICODE
+ char *ac = mir_t2a(a);
+ char *bc = mir_t2a(b);
+ char *ret = (char *) CallService(MS_FP_SAMECLIENTS, (WPARAM) ac, (LPARAM) bc);
+ mir_free(ac);
+ mir_free(bc);
+#else
+ char *ret = (char *) CallService(MS_FP_SAMECLIENTS, (WPARAM) a, (LPARAM) b);
+#endif
+ return ret != NULL;
+ }
+ else
+ return lstrcmpi(a, b) == 0;
+}
+
+
+HISTORY_TYPE types[NUM_TYPES] = {
+ { "ClientHistory", "Client", IDI_CLIENT, EVENTTYPE_CLIENT_CHANGE, NULL, ClientEquals, ClientFormat, 0, FALSE, FALSE, -1, -1, FALSE, (char *) -1, "MirVer", DBVT_UTF8, NULL, FALSE, NULL, NULL, NULL, NULL, 3, 2, ClientAddVars },
+ { "ListenHistory", "Listening", IDI_LISTENING, EVENTTYPE_LISTENINGTO_CHANGE, ListeningToAllowProtocol, NULL, NULL, HISTORYEVENTS_FLAG_KEEP_ONE_WEEK, TRUE, FALSE, -1, -1, FALSE, (char *) -1, "ListeningTo", DBVT_UTF8, NULL, TRUE, "is now listening to %new%", "stopped listening to music", NULL, NULL, 30, 0, NULL },
+ { "NickHistory", "Nickname", IDI_NICK, EVENTTYPE_NICKNAME_CHANGE, NULL, NULL, NULL, 0, FALSE, FALSE, -1, -1, FALSE, (char *) -1, "Nick", DBVT_UTF8, NULL, FALSE, NULL, NULL, NULL, NULL, 3, 0, NULL },
+ { "StatusHistory", "Status", IDI_STATUS, EVENTTYPE_STATUSCHANGE, NULL, NULL, StatusFormat, HISTORYEVENTS_FLAG_KEEP_ONE_DAY, FALSE, TRUE, -1, 4, TRUE, (char *) -1, "Status", DBVT_WORD, ID_STATUS_OFFLINE, FALSE, NULL, NULL, _T("changed his/her %s to %%new%%: %%msg%% (was %%old%%)"), NULL, 3, 1, StatusAddVars },
+ { "SMH", "Status Message", IDI_SMH, EVENTTYPE_STATUSMESSAGE_CHANGE, SMHAllowProtocol, NULL, NULL, 0, TRUE, FALSE, 3, -1, TRUE, "CList", "StatusMsg", DBVT_UTF8, NULL, FALSE, NULL, NULL, NULL, NULL, 3, 0, NULL },
+ { "XSMHistory", "X-Status", IDI_XSTATUS, EVENTTYPE_XSTATUS_CHANGE, XStatusAllowProtocol, NULL, NULL, HISTORYEVENTS_FLAG_KEEP_ONE_DAY, TRUE, FALSE, -1, 6, TRUE, (char *) -1, "XStatusName", DBVT_UTF8, NULL, TRUE, NULL, NULL, _T("changed his/her %s to %%new%%: %%msg%% (was %%old%%)"), NULL, 3, 1, XStatusAddVars },
+ { "XStatusHistory", "X-Status Message", IDI_XSM, EVENTTYPE_XSTATUS_MESSAGE_CHANGE, XStatusAllowProtocol, NULL, NULL, HISTORYEVENTS_FLAG_KEEP_ONE_DAY, TRUE, FALSE, 5, -1, TRUE, (char *) -1, "XStatusMsg", DBVT_UTF8, NULL, TRUE, NULL, NULL, NULL, NULL, 3, 0, NULL },
+ { "IdleHistory", "Idle", IDI_IDLE, EVENTTYPE_IDLE_CHANGE, NULL, NULL, IdleFormat, HISTORYEVENTS_FLAG_KEEP_ONE_DAY, TRUE, TRUE, -1, -1, TRUE, (char *) -1, "IdleTS", DBVT_BYTE, 0, FALSE, "is now idle", "returned from idle", _T("is now idle"), _T("returned from idle"), 1, 0, NULL },
+};
+
+
diff --git a/Plugins/historykeeper/historykeeper.cpp b/Plugins/historykeeper/historykeeper.cpp
new file mode 100644
index 0000000..1099c3d
--- /dev/null
+++ b/Plugins/historykeeper/historykeeper.cpp
@@ -0,0 +1,1257 @@
+/*
+Copyright (C) 2006-2009 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#include "commons.h"
+
+
+// Prototypes ///////////////////////////////////////////////////////////////////////////
+
+
+PLUGININFOEX pluginInfo={
+ sizeof(PLUGININFOEX),
+#ifdef UNICODE
+ "History Keeper (Unicode)",
+#else
+ "History Keeper",
+#endif
+ PLUGIN_MAKE_VERSION(0,2,1,0),
+ "Log various types of events to history",
+ "Ricardo Pescuma Domenecci",
+ "",
+ "© 2007-2009 Ricardo Pescuma Domenecci",
+ "http://www.pescuma.org/miranda/historykeeper",
+ UNICODE_AWARE,
+ 0, //doesn't replace anything built-in
+#ifdef UNICODE
+ { 0xca52cf41, 0x12c2, 0x411b, { 0xb8, 0x0, 0xd2, 0xbd, 0x95, 0x9b, 0xe0, 0x99 } } // {CA52CF41-12C2-411b-B800-D2BD959BE099}
+#else
+ { 0x5f33a404, 0x351f, 0x440b, { 0xa7, 0xdb, 0xb9, 0xb5, 0xd3, 0xeb, 0x2b, 0xbd } } // {5F33A404-351F-440b-A7DB-B9B5D3EB2BBD}
+#endif
+};
+
+
+HINSTANCE hInst;
+PLUGINLINK *pluginLink;
+MM_INTERFACE mmi;
+UTF8_INTERFACE utfi;
+LIST_INTERFACE li;
+
+HANDLE hHooks[6] = {0};
+
+HANDLE hEnableMenu[NUM_TYPES * 2] = {0};
+HANDLE hDisableMenu[NUM_TYPES * 2] = {0};
+
+//char *metacontacts_proto = NULL;
+BOOL loaded = FALSE;
+ContactAsyncQueue *queue;
+
+BOOL PER_DEFAULT_LOG_SUBCONTACTS = TRUE;
+BOOL PER_DEFAULT_NOTIFY_SUBCONTACTS = FALSE;
+BOOL UNIFIED_CONTEXT_MENUS = FALSE;
+BOOL HAS_METACONTACTS = FALSE;
+
+int ModulesLoaded(WPARAM wParam, LPARAM lParam);
+int PreBuildContactMenu(WPARAM wParam,LPARAM lParam);
+int SettingChanged(WPARAM wParam,LPARAM lParam);
+int ContactAdded(WPARAM wParam,LPARAM lParam);
+int ProtoAckHook(WPARAM wParam, LPARAM lParam);
+int PreShutdown(WPARAM wParam, LPARAM lParam);
+
+int EnableAll(WPARAM wParam, LPARAM lParam, LPARAM type);
+int DisableAll(WPARAM wParam, LPARAM lParam, LPARAM type);
+int AllEnabled(WPARAM wParam, LPARAM lParam, LPARAM type);
+int EnableLog(WPARAM wParam, LPARAM lParam, LPARAM type);
+int DisableLog(WPARAM wParam, LPARAM lParam, LPARAM type);
+int LogEnabled(WPARAM wParam, LPARAM lParam, LPARAM type);
+int EnableNotification(WPARAM wParam, LPARAM lParam, LPARAM type);
+int DisableNotification(WPARAM wParam, LPARAM lParam, LPARAM type);
+int NotificationEnabled(WPARAM wParam, LPARAM lParam, LPARAM type);
+
+BOOL ProtocolEnabled(int type, const char *protocol);
+
+void Process(HANDLE hContact, void *param);
+
+#define MODULE_PROTOCOL ((char *)-1)
+
+char *item_names[NUM_ITEMS] = {
+ "History",
+ "File",
+ "Popup",
+ "Sound",
+ "Speak",
+};
+
+// Functions ////////////////////////////////////////////////////////////////////////////
+
+
+extern "C" BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
+{
+ hInst = hinstDLL;
+ return TRUE;
+}
+
+
+extern "C" __declspec(dllexport) PLUGININFO* MirandaPluginInfo(DWORD mirandaVersion)
+{
+ pluginInfo.cbSize = sizeof(PLUGININFO);
+ return (PLUGININFO*) &pluginInfo;
+}
+
+
+extern "C" __declspec(dllexport) PLUGININFOEX* MirandaPluginInfoEx(DWORD mirandaVersion)
+{
+ pluginInfo.cbSize = sizeof(PLUGININFOEX);
+ return &pluginInfo;
+}
+
+
+static const MUUID interfaces[] = { MIID_STATUS_MESSAGE_CHANGE_LOGGER, MIID_STATUS_MESSAGE_CHANGE_NOTIFIER, MIID_LAST };
+extern "C" __declspec(dllexport) const MUUID* MirandaPluginInterfaces(void)
+{
+ return interfaces;
+}
+
+
+extern "C" int __declspec(dllexport) Load(PLUGINLINK *link)
+{
+ pluginLink = link;
+
+ CHECK_VERSION("History Keeper")
+
+ mir_getMMI(&mmi);
+ mir_getUTFI(&utfi);
+ mir_getLI(&li);
+
+ // Hidden settings
+ UNIFIED_CONTEXT_MENUS = DBGetContactSettingByte(NULL, MODULE_NAME, "UnifiedContextMenus", FALSE);
+ PER_DEFAULT_LOG_SUBCONTACTS = DBGetContactSettingByte(NULL, MODULE_NAME, "PerDefaultLogSubcontacts", TRUE);
+ PER_DEFAULT_NOTIFY_SUBCONTACTS = DBGetContactSettingByte(NULL, MODULE_NAME, "PerDefaultNotifySubcontacts", FALSE);
+
+ // Add menu item to enable/disable status message check
+ CLISTMENUITEM mi = {0};
+ mi.cbSize = sizeof(mi);
+ mi.popupPosition = mi.position = 1000090020;
+
+ mi.flags = CMIF_NOTOFFLIST|CMIF_ROOTPOPUP;
+ mi.pszPopupName = (char *)-1;
+ mi.pszName = "History Keeper";
+ HANDLE hRootMenu = (HANDLE) CallService(MS_CLIST_ADDCONTACTMENUITEM, 0, (LPARAM)&mi);
+
+ mi.pszPopupName = (char *) hRootMenu;
+ mi.flags = CMIF_NOTOFFLIST|CMIF_CHILDPOPUP;
+ char name[256];
+ mi.pszName = name;
+ char service[256];
+ mi.pszService = service;
+
+ for (int i = 0; i < NUM_TYPES; i++)
+ {
+ HISTORY_TYPE &type = types[i];
+ char tmp[256];
+
+ // DB
+
+ if (type.temporary && type.track.db.module != MODULE_PROTOCOL)
+ {
+ mir_snprintf(tmp, MAX_REGS(tmp), MODULE_NAME "/%s_%s_Current", type.track.db.module, type.track.db.setting);
+ CallService(MS_DB_SETSETTINGRESIDENT, TRUE, (LPARAM) tmp);
+ }
+
+
+ // History
+
+ strncpy(tmp, type.description, MAX_REGS(tmp));
+ CharLowerA(tmp);
+
+ char change[256];
+ if (type.defs.change_template != NULL)
+ mir_snprintf(change, MAX_REGS(change), "Change\n%s\n%%old%%\tOld value\n%%new%%\tNew value",
+ type.defs.change_template);
+ else
+ mir_snprintf(change, MAX_REGS(change), "Change\nchanged his/her %s to %%new%%\n%%old%%\tOld value\n%%new%%\tNew value",
+ tmp);
+
+ if (type.canBeRemoved)
+ {
+ char remove[256];
+ if (type.defs.remove_template != NULL)
+ mir_snprintf(remove, MAX_REGS(remove), "Removal\n%s\n%%old%%\tOld value",
+ type.defs.remove_template);
+ else
+ mir_snprintf(remove, MAX_REGS(remove), "Removal\nremoved his/her %s\n%%old%%\tOld value",
+ tmp);
+
+ char *templates[] = { change, remove };
+
+ char desc[128];
+ mir_snprintf(desc, MAX_REGS(desc), "%s Change", type.description);
+
+ HICON hIcon = (HICON) LoadImage(hInst, MAKEINTRESOURCE(type.icon), IMAGE_ICON, 16, 16, 0);
+ HistoryEvents_RegisterMessageStyle(MODULE_NAME, type.name, desc, type.eventType,
+ hIcon, HISTORYEVENTS_FLAG_SHOW_IM_SRMM | HISTORYEVENTS_FLAG_EXPECT_CONTACT_NAME_BEFORE | type.historyFlags,
+ templates, MAX_REGS(templates));
+ DestroyIcon(hIcon);
+ }
+ else
+ {
+ char *templates[] = { change };
+
+ char desc[128];
+ mir_snprintf(desc, MAX_REGS(desc), "%s Change", type.description);
+
+ HICON hIcon = (HICON) LoadImage(hInst, MAKEINTRESOURCE(type.icon), IMAGE_ICON, 16, 16, 0);
+ HistoryEvents_RegisterMessageStyle(MODULE_NAME, type.name, desc, type.eventType,
+ hIcon, HISTORYEVENTS_FLAG_SHOW_IM_SRMM | HISTORYEVENTS_FLAG_EXPECT_CONTACT_NAME_BEFORE | type.historyFlags,
+ templates, MAX_REGS(templates));
+ DestroyIcon(hIcon);
+ }
+
+
+ // Menus and services
+
+ mi.position = i;
+
+ mi.hIcon = HistoryEvents_GetIcon(type.eventType);
+
+ if (UNIFIED_CONTEXT_MENUS)
+ {
+ mir_snprintf(service, MAX_REGS(service), "%s/Disable", type.name);
+ CreateServiceFunctionParam(service, DisableAll, i);
+
+ mir_snprintf(name, MAX_REGS(name), Translate("Ignore %s changes"), type.description);
+ hDisableMenu[i] = (HANDLE) CallService(MS_CLIST_ADDCONTACTMENUITEM, 0, (LPARAM)&mi);
+
+ mir_snprintf(service, MAX_REGS(service), "%s/Enable", type.name);
+ CreateServiceFunctionParam(service, EnableAll, i);
+
+ mir_snprintf(name, MAX_REGS(name), Translate("Log %s changes"), type.description);
+ hEnableMenu[i] = (HANDLE) CallService(MS_CLIST_ADDCONTACTMENUITEM, 0, (LPARAM)&mi);
+ }
+ else
+ {
+ mir_snprintf(service, MAX_REGS(service), "%s/DisableLog", type.name);
+ CreateServiceFunctionParam(service, DisableLog, i);
+
+ mir_snprintf(name, MAX_REGS(name), Translate("Don't log %s changes"), type.description);
+ hDisableMenu[2*i] = (HANDLE) CallService(MS_CLIST_ADDCONTACTMENUITEM, 0, (LPARAM)&mi);
+
+ mir_snprintf(service, MAX_REGS(service), "%s/DisableNotification", type.name);
+ CreateServiceFunctionParam(service, DisableNotification, i);
+
+ mir_snprintf(name, MAX_REGS(name), Translate("Don't notify %s changes"), type.description);
+ hDisableMenu[2*i+1] = (HANDLE) CallService(MS_CLIST_ADDCONTACTMENUITEM, 0, (LPARAM)&mi);
+
+ mir_snprintf(service, MAX_REGS(service), "%s/EnableLog", type.name);
+ CreateServiceFunctionParam(service, EnableLog, i);
+
+ mir_snprintf(name, MAX_REGS(name), Translate("Log %s changes"), type.description);
+ hEnableMenu[2*i] = (HANDLE) CallService(MS_CLIST_ADDCONTACTMENUITEM, 0, (LPARAM)&mi);
+
+ mir_snprintf(service, MAX_REGS(service), "%s/EnableNotification", type.name);
+ CreateServiceFunctionParam(service, EnableNotification, i);
+
+ mir_snprintf(name, MAX_REGS(name), Translate("Notify %s changes"), type.description);
+ hEnableMenu[2*i+1] = (HANDLE) CallService(MS_CLIST_ADDCONTACTMENUITEM, 0, (LPARAM)&mi);
+ }
+
+ mir_snprintf(service, MAX_REGS(service), "%s/Enabled", type.name);
+ CreateServiceFunctionParam(service, AllEnabled, i);
+
+ mir_snprintf(service, MAX_REGS(service), "%s/LogEnabled", type.name);
+ CreateServiceFunctionParam(service, LogEnabled, i);
+
+ mir_snprintf(service, MAX_REGS(service), "%s/NotificationEnabled", type.name);
+ CreateServiceFunctionParam(service, NotificationEnabled, i);
+
+ HistoryEvents_ReleaseIcon(mi.hIcon);
+
+ // Sounds
+
+ mir_snprintf(change, MAX_REGS(change), Translate("%s change"), type.description);
+ SkinAddNewSoundEx(change, "Notifications", change);
+
+ if (type.canBeRemoved)
+ {
+ mir_snprintf(change, MAX_REGS(change), Translate("%s removal"), type.description);
+ SkinAddNewSoundEx(change, "Notifications", change);
+ }
+ }
+
+ // hooks
+ hHooks[0] = HookEvent(ME_SYSTEM_MODULESLOADED, ModulesLoaded);
+ hHooks[1] = HookEvent(ME_CLIST_PREBUILDCONTACTMENU, PreBuildContactMenu);
+ hHooks[2] = HookEvent(ME_DB_CONTACT_SETTINGCHANGED, SettingChanged);
+ hHooks[3] = HookEvent(ME_DB_CONTACT_ADDED, ContactAdded);
+ hHooks[4] = HookEvent(ME_PROTO_ACK, ProtoAckHook);
+ hHooks[5] = HookEvent(ME_SYSTEM_PRESHUTDOWN, PreShutdown);
+
+ return 0;
+}
+
+extern "C" int __declspec(dllexport) Unload(void)
+{
+ return 0;
+}
+
+
+// Called when all the modules are loaded
+int ModulesLoaded(WPARAM wParam, LPARAM lParam)
+{
+ HAS_METACONTACTS = ServiceExists(MS_MC_GETMETACONTACT);
+
+ int i;
+ for (i = 0; i < NUM_TYPES; i++)
+ {
+ HISTORY_TYPE &type = types[i];
+
+ char tmp[256];
+ strncpy(tmp, type.description, MAX_REGS(tmp));
+ CharLowerA(tmp);
+
+ char desc[128];
+ mir_snprintf(desc, MAX_REGS(desc), "%s Change", type.description);
+
+ // Speak
+
+ char change[256];
+ if (type.defs.change_template != NULL)
+ mir_snprintf(change, MAX_REGS(change), "Change\n%%contact%% %s\n%%old%%\tOld value\n%%new%%\tNew value",
+ type.defs.change_template);
+ else
+ mir_snprintf(change, MAX_REGS(change), "Change\n%%contact%% changed his/her %s to %%new%%\n%%old%%\tOld value\n%%new%%\tNew value",
+ tmp);
+
+ char remove[256];
+ if (type.canBeRemoved)
+ {
+ if (type.defs.remove_template != NULL)
+ mir_snprintf(remove, MAX_REGS(remove), "Removal\n%%contact%% %s\n%%old%%\tOld value",
+ type.defs.remove_template);
+ else
+ mir_snprintf(remove, MAX_REGS(remove), "Removal\n%%contact%% removed his/her %s\n%%old%%\tOld value",
+ tmp);
+ }
+
+ char *templates[] = { change, remove };
+
+ // TODO: This should not be here
+ char icon[128];
+ mir_snprintf(icon, sizeof(icon), "historyevent_%s", type.name);
+
+ Speak_RegisterWT(MODULE_NAME, type.name, desc, icon, templates, type.canBeRemoved ? 2 : 1);
+ }
+
+ CallService(MS_DB_SETSETTINGRESIDENT, TRUE, (LPARAM) MODULE_NAME "/CreationTickCount");
+
+ PROTOCOLDESCRIPTOR **protos;
+ int count;
+ CallService(MS_PROTO_ENUMPROTOCOLS, (WPARAM)&count, (LPARAM)&protos);
+ for (i = 0; i < count; i++)
+ {
+ if (protos[i]->szName == NULL || protos[i]->szName[0] == '\0')
+ continue;
+
+ char tmp[256];
+
+ mir_snprintf(tmp, MAX_REGS(tmp), MODULE_NAME "/%s_OnOfflineTickCount", protos[i]->szName);
+ CallService(MS_DB_SETSETTINGRESIDENT, TRUE, (LPARAM) tmp);
+
+ mir_snprintf(tmp, MAX_REGS(tmp), MODULE_NAME "/%s_LastStatus", protos[i]->szName);
+ CallService(MS_DB_SETSETTINGRESIDENT, TRUE, (LPARAM) tmp);
+
+ for (int j = 0; j < NUM_TYPES; j++)
+ {
+ HISTORY_TYPE &type = types[j];
+
+ if (type.temporary && type.track.db.module == MODULE_PROTOCOL)
+ {
+ mir_snprintf(tmp, MAX_REGS(tmp), MODULE_NAME "/%s_%s_Current", protos[i]->szName, type.track.db.setting);
+ CallService(MS_DB_SETSETTINGRESIDENT, TRUE, (LPARAM) tmp);
+ }
+ }
+ }
+
+ // add our modules to the KnownModules list
+ CallService("DBEditorpp/RegisterSingleModule", (WPARAM) MODULE_NAME, 0);
+
+ // updater plugin support
+ if(ServiceExists(MS_UPDATE_REGISTER))
+ {
+ Update upd = {0};
+ char szCurrentVersion[30];
+
+ upd.cbSize = sizeof(upd);
+ upd.szComponentName = pluginInfo.shortName;
+
+ upd.szUpdateURL = UPDATER_AUTOREGISTER;
+
+ upd.szBetaVersionURL = "http://www.pescuma.org/miranda/historykeeper_version.txt";
+ upd.szBetaChangelogURL = "http://www.pescuma.org/miranda/historykeeper#Changelog";
+ upd.pbBetaVersionPrefix = (BYTE *)"History Keeper ";
+ upd.cpbBetaVersionPrefix = strlen((char *)upd.pbBetaVersionPrefix);
+#ifdef UNICODE
+ upd.szBetaUpdateURL = "http://www.pescuma.org/miranda/historykeeperW.zip";
+#else
+ upd.szBetaUpdateURL = "http://www.pescuma.org/miranda/historykeeper.zip";
+#endif
+
+ upd.pbVersion = (BYTE *)CreateVersionStringPlugin((PLUGININFO*) &pluginInfo, szCurrentVersion);
+ upd.cpbVersion = strlen((char *)upd.pbVersion);
+
+ CallService(MS_UPDATE_REGISTER, 0, (LPARAM)&upd);
+ }
+
+ InitOptions();
+ InitPopups();
+
+ queue = new ContactAsyncQueue(&Process);
+
+ loaded = TRUE;
+
+ return 0;
+}
+
+
+int PreShutdown(WPARAM wParam, LPARAM lParam)
+{
+ DeInitPopups();
+ DeInitOptions();
+
+ for (int i = 0; i < MAX_REGS(hHooks); i++)
+ if (hHooks[i] != NULL)
+ UnhookEvent(hHooks[i]);
+
+ delete queue;
+
+ return 0;
+}
+
+
+int PreBuildContactMenu(WPARAM wParam, LPARAM lParam)
+{
+ CLISTMENUITEM clmi = {0};
+ clmi.cbSize = sizeof(clmi);
+
+ HANDLE hContact = (HANDLE) wParam;
+ char *proto = (char*) CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM) hContact, 0);
+
+ for (int i = 0; i < NUM_TYPES; i ++)
+ {
+ if (UNIFIED_CONTEXT_MENUS)
+ {
+ if (!ProtocolEnabled(i, proto))
+ {
+ clmi.flags = CMIM_FLAGS | CMIF_HIDDEN;
+ CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM) hEnableMenu[i], (LPARAM) &clmi);
+ CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM) hDisableMenu[i], (LPARAM) &clmi);
+ }
+ else if (AllEnabled((WPARAM) hContact, 0, i))
+ {
+ clmi.flags = CMIM_FLAGS | CMIF_HIDDEN;
+ CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM) hEnableMenu[i], (LPARAM) &clmi);
+
+ clmi.flags = CMIM_FLAGS;
+ CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM) hDisableMenu[i], (LPARAM) &clmi);
+ }
+ else
+ {
+ clmi.flags = CMIM_FLAGS;
+ CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM) hEnableMenu[i], (LPARAM) &clmi);
+
+ clmi.flags = CMIM_FLAGS | CMIF_HIDDEN;
+ CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM) hDisableMenu[i], (LPARAM) &clmi);
+ }
+ }
+ else
+ {
+ if (!ProtocolEnabled(i, proto))
+ {
+ clmi.flags = CMIM_FLAGS | CMIF_HIDDEN;
+ CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM) hEnableMenu[2*i], (LPARAM) &clmi);
+ CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM) hDisableMenu[2*i], (LPARAM) &clmi);
+ CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM) hEnableMenu[2*i+1], (LPARAM) &clmi);
+ CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM) hDisableMenu[2*i+1], (LPARAM) &clmi);
+ }
+ else
+ {
+ if (LogEnabled((WPARAM) hContact, 0, i))
+ {
+ clmi.flags = CMIM_FLAGS | CMIF_HIDDEN;
+ CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM) hEnableMenu[2*i], (LPARAM) &clmi);
+
+ clmi.flags = CMIM_FLAGS;
+ CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM) hDisableMenu[2*i], (LPARAM) &clmi);
+ }
+ else
+ {
+ clmi.flags = CMIM_FLAGS;
+ CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM) hEnableMenu[2*i], (LPARAM) &clmi);
+
+ clmi.flags = CMIM_FLAGS | CMIF_HIDDEN;
+ CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM) hDisableMenu[2*i], (LPARAM) &clmi);
+ }
+
+ if (NotificationEnabled((WPARAM) hContact, 0, i))
+ {
+ clmi.flags = CMIM_FLAGS | CMIF_HIDDEN;
+ CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM) hEnableMenu[2*i+1], (LPARAM) &clmi);
+
+ clmi.flags = CMIM_FLAGS;
+ CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM) hDisableMenu[2*i+1], (LPARAM) &clmi);
+ }
+ else
+ {
+ clmi.flags = CMIM_FLAGS;
+ CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM) hEnableMenu[2*i+1], (LPARAM) &clmi);
+
+ clmi.flags = CMIM_FLAGS | CMIF_HIDDEN;
+ CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM) hDisableMenu[2*i+1], (LPARAM) &clmi);
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
+
+int EnableAll(WPARAM wParam, LPARAM lParam, LPARAM type)
+{
+ HANDLE hContact = (HANDLE) wParam;
+ if (hContact == NULL)
+ return 0;
+
+ for (int i = 0; i < NUM_ITEMS; i++)
+ EnableItem(type, hContact, i, TRUE);
+
+ return 0;
+}
+
+int DisableAll(WPARAM wParam, LPARAM lParam, LPARAM type)
+{
+ HANDLE hContact = (HANDLE) wParam;
+ if (hContact == NULL)
+ return 0;
+
+ for (int i = 0; i < NUM_ITEMS; i++)
+ EnableItem(type, hContact, i, FALSE);
+
+ return 0;
+}
+
+
+int EnableLog(WPARAM wParam, LPARAM lParam, LPARAM type)
+{
+ HANDLE hContact = (HANDLE) wParam;
+ if (hContact == NULL)
+ return 0;
+
+ for (int i = 0; i < NUM_LOG_ITEMS; i++)
+ EnableItem(type, hContact, i, TRUE);
+
+ return 0;
+}
+
+int DisableLog(WPARAM wParam, LPARAM lParam, LPARAM type)
+{
+ HANDLE hContact = (HANDLE) wParam;
+ if (hContact == NULL)
+ return 0;
+
+ for (int i = 0; i < NUM_LOG_ITEMS; i++)
+ EnableItem(type, hContact, i, FALSE);
+
+ return 0;
+}
+
+
+int EnableNotification(WPARAM wParam, LPARAM lParam, LPARAM type)
+{
+ HANDLE hContact = (HANDLE) wParam;
+ if (hContact == NULL)
+ return 0;
+
+ for (int i = NUM_LOG_ITEMS; i < NUM_ITEMS; i++)
+ EnableItem(type, hContact, i, TRUE);
+
+ return 0;
+}
+
+int DisableNotification(WPARAM wParam, LPARAM lParam, LPARAM type)
+{
+ HANDLE hContact = (HANDLE) wParam;
+ if (hContact == NULL)
+ return 0;
+
+ for (int i = NUM_LOG_ITEMS; i < NUM_ITEMS; i++)
+ EnableItem(type, hContact, i, FALSE);
+
+ return 0;
+}
+
+
+int AllEnabled(WPARAM wParam, LPARAM lParam, LPARAM type)
+{
+ return ContactEnabled(type, (HANDLE) wParam);
+}
+
+
+int LogEnabled(WPARAM wParam, LPARAM lParam, LPARAM type)
+{
+ return ContactEnabled(type, (HANDLE) wParam, 0, NUM_LOG_ITEMS);
+}
+
+
+int NotificationEnabled(WPARAM wParam, LPARAM lParam, LPARAM type)
+{
+ return ContactEnabled(type, (HANDLE) wParam, NUM_LOG_ITEMS, NUM_ITEMS);
+}
+
+
+BOOL AllowProtocol(int type, const char *proto)
+{
+ if (types[type].fAllowProtocol != NULL && !types[type].fAllowProtocol(proto))
+ return FALSE;
+
+ return TRUE;
+}
+
+
+BOOL ProtocolEnabled(int type, const char *proto)
+{
+ if (proto == NULL)
+ return FALSE;
+
+ if (!AllowProtocol(type, proto))
+ return FALSE;
+
+ char setting[256];
+ mir_snprintf(setting, sizeof(setting), "%s_%sEnabled", types[type].name, proto);
+ return (BOOL) DBGetContactSettingByte(NULL, MODULE_NAME, setting, TRUE);
+}
+
+
+BOOL ContactEnabled(int type, HANDLE hContact, int min, int max)
+{
+ if (hContact == NULL)
+ return FALSE;
+
+ char *proto = (char *) CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM) hContact, 0);
+ if (!ProtocolEnabled(type, proto))
+ return FALSE;
+
+ for (int i = min; i < max; i++)
+ if (ItemEnabled(type, hContact, i))
+ return TRUE;
+
+ if (!types[type].handleMetacontactsSeparatelly)
+ {
+ HANDLE hMetaContact = (HANDLE) CallService(MS_MC_GETMETACONTACT, (WPARAM) hContact, 0);
+ if (hMetaContact != NULL)
+ {
+ for (int i = min; i < max; i++)
+ if (ItemEnabled(type, hMetaContact, i))
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+
+BOOL ItemEnabled(int type, HANDLE hContact, int item)
+{
+ BYTE def = TRUE;
+
+ // Is a subcontact?
+ if (HAS_METACONTACTS)
+ {
+ HANDLE hMetaContact = (HANDLE) CallService(MS_MC_GETMETACONTACT, (WPARAM)hContact, 0);
+ if (hMetaContact != NULL)
+ {
+ def = FALSE;
+
+ if (item < NUM_LOG_ITEMS)
+ {
+ if (PER_DEFAULT_LOG_SUBCONTACTS)
+ def = ItemEnabled(type, hMetaContact, item);
+ }
+ else
+ {
+ if (PER_DEFAULT_NOTIFY_SUBCONTACTS)
+ def = ItemEnabled(type, hMetaContact, item);
+ }
+ }
+ }
+
+ char setting[256];
+ mir_snprintf(setting, MAX_REGS(setting), "%s_%s_Enabled", types[type].name, item_names[item]);
+ return DBGetContactSettingByte(hContact, MODULE_NAME, setting, def);
+}
+
+
+BOOL EnableItem(int type, HANDLE hContact, int item, BOOL enable)
+{
+ char setting[256];
+ mir_snprintf(setting, MAX_REGS(setting), "%s_%s_Enabled", types[type].name, item_names[item]);
+ return DBWriteContactSettingByte(hContact, MODULE_NAME, setting, enable);
+}
+
+
+void LogToFile(HANDLE hContact, int typeNum, int templateNum, TCHAR **vars, int numVars)
+{
+ if (templateNum == 0 && !opts[typeNum].file_track_changes)
+ return;
+ if (templateNum == 1 && !opts[typeNum].file_track_removes)
+ return;
+
+ TCHAR *templ = (templateNum == 1 ? opts[typeNum].file_template_removed : opts[typeNum].file_template_changed);
+ Buffer<TCHAR> txt;
+ ReplaceTemplate(&txt, hContact, templ, vars, numVars);
+
+ // Assert folder exists
+ TCHAR *p = _tcschr(opts[typeNum].file_name, _T('\\'));
+ if (p != NULL)
+ p = _tcschr(p+1, _T('\\'));
+ while(p != NULL)
+ {
+ *p = _T('\0');
+ CreateDirectory(opts[typeNum].file_name, NULL);
+ *p = _T('\\');
+ p = _tcschr(p+1, _T('\\'));
+ }
+
+ FILE *fp = _tfopen(opts[typeNum].file_name, _T("at"));
+ if (fp != NULL)
+ {
+ _ftprintf(fp, _T("%s\n"), txt.str);
+ fclose(fp);
+ }
+}
+
+
+void Speak(HANDLE hContact, int type, int templateNum, TCHAR **vars, int numVars)
+{
+ if (ServiceExists(MS_SPEAK_SAYEX))
+ {
+ // New style
+ Speak_SayExWT(types[type].name, hContact, templateNum, vars, numVars);
+ return;
+ }
+
+ // Old style
+ if (!ServiceExists(MS_SPEAK_SAY_A))
+ return;
+
+ if (templateNum == 0 && !opts[type].speak_track_changes)
+ return;
+ if (templateNum == 1 && !opts[type].speak_track_removes)
+ return;
+
+ TCHAR *templ = (templateNum == 1 ? opts[type].speak_template_removed : opts[type].speak_template_changed);
+ Buffer<TCHAR> txt;
+ ReplaceTemplate(&txt, hContact, templ, vars, numVars);
+
+ if (ServiceExists(MS_SPEAK_SAY))
+ {
+ CallService(MS_SPEAK_SAY, (LPARAM) hContact, (WPARAM) txt.str);
+ }
+ else
+ {
+ char *tmp = mir_t2a(txt.str);
+ CallService(MS_SPEAK_SAY_A, (LPARAM) hContact, (WPARAM) tmp);
+ mir_free(tmp);
+ }
+}
+
+
+void Notify(HANDLE hContact, int type, BOOL found_old, int templateNum, TCHAR **vars, int numVars)
+{
+ if (!found_old && !types[type].canBeRemoved && !types[type].temporary)
+ return;
+
+ if (DBGetContactSettingDword(hContact, MODULE_NAME, "CreationTickCount", 0)
+ + TIME_TO_WAIT_BEFORE_SHOW_POPUP_AFTER_CREATION > GetTickCount())
+ return;
+
+ char *proto = (char*) CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM) hContact, 0);
+ if (proto == NULL || proto[0] == '\0')
+ return;
+
+ int status = CallProtoService(proto, PS_GETSTATUS, 0, 0);
+ if (status <= ID_STATUS_OFFLINE)
+ return;
+
+ if (opts[type].dont_notify_on_connect)
+ {
+ char onOffSetting[256];
+ mir_snprintf(onOffSetting, MAX_REGS(onOffSetting), "%s_OnOfflineTickCount", proto);
+ if (DBGetContactSettingDword(NULL, MODULE_NAME, onOffSetting, 0)
+ + TIME_TO_WAIT_BEFORE_NOTIFY_AFTER_CONNECTION > GetTickCount())
+ return;
+ }
+
+ if (ItemEnabled(type, hContact, NOTIFY_POPUP))
+ ShowPopup(hContact, type, templateNum, vars, numVars);
+
+ if (ItemEnabled(type, hContact, NOTIFY_SOUND))
+ {
+ char tmp[256];
+ mir_snprintf(tmp, MAX_REGS(tmp), templateNum == 0 ? "%s change" : "%s removal", types[type].description);
+ SkinPlaySound(tmp);
+ }
+
+ if (ItemEnabled(type, hContact, NOTIFY_SPEAK))
+ Speak(hContact, type, templateNum, vars, numVars);
+}
+
+
+void Log(int type, BOOL found_old, BOOL changed, HANDLE hContact, TCHAR *oldVal, TCHAR *newVal, BOOL notify)
+{
+ if (newVal == NULL || newVal[0] == _T('\0'))
+ newVal = TranslateT("<empty>");
+ if (oldVal == NULL || oldVal[0] == _T('\0'))
+ oldVal = TranslateT("<empty>");
+
+ int numVars = 4 + 2 * types[type].numAddVars;
+ TCHAR **vars = (TCHAR **) mir_alloc0(sizeof(TCHAR *) * numVars);
+ vars[0] = _T("old");
+ vars[1] = oldVal;
+ vars[2] = _T("new");
+ vars[3] = newVal;
+
+ if (types[type].fAddVars != NULL)
+ types[type].fAddVars(hContact, vars, 4);
+
+ int templateNum = changed ? 0 : 1;
+
+ if (ItemEnabled(type, hContact, LOG_HISTORY))
+ HistoryEvents_AddToHistoryVars(hContact, types[type].eventType, templateNum, vars, numVars, DBEF_READ);
+
+ if (ItemEnabled(type, hContact, LOG_FILE))
+ LogToFile(hContact, type, templateNum, vars, numVars);
+
+ if (notify)
+ Notify(hContact, type, found_old, templateNum, vars, numVars);
+
+ // Notify/Log meta?
+ if (HAS_METACONTACTS && !types[type].handleMetacontactsSeparatelly)
+ {
+ HANDLE hMetaContact = (HANDLE) CallService(MS_MC_GETMETACONTACT, (WPARAM) hContact, 0);
+ if (hMetaContact != NULL)
+ {
+ HANDLE hMostOnline = (HANDLE) CallService(MS_MC_GETMOSTONLINECONTACT, (WPARAM) hMetaContact, 0);
+
+ if (hContact == hMostOnline)
+ {
+ if (ItemEnabled(type, hMetaContact, LOG_HISTORY))
+ HistoryEvents_AddToHistoryVars(hMetaContact, types[type].eventType, templateNum, vars, numVars, DBEF_READ);
+
+ if (ItemEnabled(type, hMetaContact, LOG_FILE))
+ LogToFile(hMetaContact, type, templateNum, vars, numVars);
+
+ if (notify)
+ Notify(hMetaContact, type, found_old, templateNum, vars, numVars);
+ }
+ }
+ }
+
+ for(int i = 0; i < types[type].numAddVars; i++)
+ mir_free(vars[4 + 2 * i + 1]);
+}
+
+
+int inline CheckStr(TCHAR *str, int not_empty, int empty)
+{
+ if (str == NULL || str[0] == _T('\0'))
+ return empty;
+ else
+ return not_empty;
+}
+
+
+int ContactAdded(WPARAM wParam, LPARAM lParam)
+{
+ DBWriteContactSettingDword((HANDLE) wParam, MODULE_NAME, "CreationTickCount", GetTickCount());
+ return 0;
+}
+
+
+int ProtoAckHook(WPARAM wParam, LPARAM lParam)
+{
+ ACKDATA *ack = (ACKDATA*)lParam;
+
+ if (ack->szModule != NULL && ack->type == ACKTYPE_STATUS && ack->result == ACKRESULT_SUCCESS)
+ {
+ const char *proto = ack->szModule;
+ int status = (int) ack->lParam;
+ char lastStatusSetting[256];
+ mir_snprintf(lastStatusSetting, MAX_REGS(lastStatusSetting), "%s_LastStatus", proto);
+
+ int oldStatus = DBGetContactSettingWord(NULL, MODULE_NAME, lastStatusSetting, ID_STATUS_OFFLINE);
+
+ if ( (status > ID_STATUS_OFFLINE && oldStatus <= ID_STATUS_OFFLINE)
+ || (status <= ID_STATUS_OFFLINE && oldStatus > ID_STATUS_OFFLINE) )
+ {
+ char onOffSetting[256];
+ mir_snprintf(onOffSetting, MAX_REGS(onOffSetting), "%s_OnOfflineTickCount", proto);
+
+ DBWriteContactSettingDword(NULL, MODULE_NAME, onOffSetting, GetTickCount());
+ }
+
+ DBWriteContactSettingWord(NULL, MODULE_NAME, lastStatusSetting, status);
+ }
+
+ return 0;
+}
+
+
+int SettingChanged(WPARAM wParam, LPARAM lParam)
+{
+ if (!loaded)
+ return 0;
+
+
+ HANDLE hContact = (HANDLE) wParam;
+ if (hContact == NULL)
+ return 0;
+
+ char *proto = (char *) CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM) hContact, 0);
+ if (proto == NULL || proto[0] == '\0') // || (metacontacts_proto != NULL && !strcmp(proto, metacontacts_proto)))
+ return 0;
+
+ DBCONTACTWRITESETTING *cws = (DBCONTACTWRITESETTING*)lParam;
+
+ for (int i = 0; i < NUM_TYPES; i++)
+ {
+ HISTORY_TYPE &type = types[i];
+
+ if (type.track.db.module == NULL || type.track.db.setting == NULL)
+ continue;
+
+ char *module;
+ if (type.track.db.module == MODULE_PROTOCOL)
+ module = proto;
+ else
+ module = type.track.db.module;
+
+ if (!strcmp(cws->szModule, module) && !strcmp(cws->szSetting, type.track.db.setting))
+ {
+ if (opts[i].track_only_not_offline)
+ if (DBGetContactSettingWord(hContact, proto, "Status", 0) <= ID_STATUS_OFFLINE)
+ continue;
+
+ if (!ContactEnabled(i, hContact))
+ continue;
+
+ if (HAS_METACONTACTS && !type.handleMetacontactsSeparatelly)
+ {
+ int numSubs = (int) CallService(MS_MC_GETNUMCONTACTS, (WPARAM)hContact, 0);
+ if (numSubs > 0)
+ continue;
+ }
+
+ queue->Lock();
+
+ BOOL found = FALSE;
+ int typeNum = i;
+ int notifyAlso = -1;
+
+ if (type.subOf >= 0)
+ {
+ // Check parent
+ for (int j = queue->Size() - 1; j >= 0; --j)
+ {
+ QueueItem *item = queue->Get(j);
+ QueueData *qd = (QueueData *) item->param;
+
+ if (item->hContact == hContact && qd->type == type.subOf)
+ {
+ qd->notifyAlso = typeNum;
+ found = TRUE;
+ break;
+ }
+ }
+ }
+ else if (type.parentOf >= 0)
+ {
+ // Check sub
+ for (int j = queue->Size() - 1; j >= 0; --j)
+ {
+ QueueItem *item = queue->Get(j);
+ QueueData *qd = (QueueData *) item->param;
+
+ if (item->hContact == hContact && qd->type == type.parentOf)
+ {
+ notifyAlso = qd->type;
+ delete qd;
+ queue->Remove(j);
+ break;
+ }
+ }
+ }
+
+ // Check if already have
+ if (!found)
+ {
+ for (int j = queue->Size() - 1; j >= 0; --j)
+ {
+ QueueItem *item = queue->Get(j);
+ QueueData *qd = (QueueData *) item->param;
+
+ if (item->hContact == hContact && qd->type == typeNum)
+ {
+ if (qd->notifyAlso >= 0)
+ notifyAlso = qd->notifyAlso;
+
+ delete qd;
+ queue->Remove(j);
+ break;
+ }
+ }
+
+ QueueData *qd = new QueueData();
+ qd->type = typeNum;
+ qd->notifyAlso = notifyAlso;
+
+ queue->Add(opts[i].ttw * 1000, hContact, qd);
+ }
+
+ queue->Release();
+
+ }
+ }
+
+ return 0;
+}
+
+
+void TrackChangeString(int typeNum, HANDLE hContact, BOOL notify)
+{
+ HISTORY_TYPE &type = types[typeNum];
+
+ char *module;
+ if (type.track.db.module == MODULE_PROTOCOL)
+ module = (char *) CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM) hContact, 0);
+ else
+ module = type.track.db.module;
+
+ char current_setting[256];
+ mir_snprintf(current_setting, MAX_REGS(current_setting), "%s_%s_Current", module, type.track.db.setting);
+
+ DBVARIANT old = {0};
+ BOOL found_old = (DBGetContactSettingTString(hContact, MODULE_NAME, current_setting, &old) == 0);
+
+ DBVARIANT new_ = {0};
+ BOOL found_new = (DBGetContactSettingTString(hContact, module, type.track.db.setting, &new_) == 0);
+
+ TCHAR *oldVal;
+ if (found_old)
+ oldVal = old.ptszVal;
+ else
+ oldVal = (TCHAR *) type.defs.value;
+ if (oldVal == NULL)
+ oldVal = _T("");
+
+ // 0 if not changed, 1 if changed, 2 if removed
+ int ret = 0;
+ if (!found_new)
+ {
+ ret = CheckStr(oldVal, 2, 0);
+ }
+ else
+ {
+ BOOL eq;
+ if (type.fEquals != NULL)
+ eq = type.fEquals(new_.ptszVal, oldVal);
+ else
+ eq = (lstrcmp(new_.ptszVal, oldVal) == 0);
+
+ ret = (eq ? 0 : CheckStr(new_.ptszVal, 1, 2));
+ }
+
+ BOOL track_removes;
+ if (ret == 2)
+ track_removes = type.canBeRemoved &&
+ (opts[typeNum].popup_track_removes
+ || opts[typeNum].file_track_removes
+ || opts[typeNum].speak_track_removes
+ || HistoryEvents_IsEnabledTemplate(type.eventType, 1));
+
+ if (ret == 1 || (ret == 2 && track_removes))
+ {
+ if (type.fFormat != NULL)
+ {
+ TCHAR old_str[256];
+ TCHAR new_str[256];
+
+ type.fFormat(old_str, MAX_REGS(old_str), oldVal);
+ type.fFormat(new_str, MAX_REGS(new_str), new_.ptszVal);
+ Log(typeNum, found_old, ret == 1, hContact, old_str, new_str, notify);
+ }
+ else
+ Log(typeNum, found_old, ret == 1, hContact, oldVal, new_.ptszVal, notify);
+
+ // Copy new to current after notification, so old value can still be accessed
+ if (ret == 2)
+ {
+ DBDeleteContactSetting(hContact, MODULE_NAME, current_setting);
+ }
+ else
+ {
+ DBWriteContactSettingTString(hContact, MODULE_NAME, current_setting, new_.ptszVal);
+ }
+ }
+
+ if (found_old)
+ DBFreeVariant(&old);
+ if (found_new)
+ DBFreeVariant(&new_);
+}
+
+inline static DWORD GetDWORD(const DBVARIANT &dbv)
+{
+ switch(dbv.type)
+ {
+ case DBVT_BYTE: return dbv.bVal;
+ case DBVT_WORD: return dbv.wVal;
+ case DBVT_DWORD: return dbv.dVal;
+ }
+ return 0;
+}
+
+void TrackChangeNumber(int typeNum, HANDLE hContact, BOOL notify)
+{
+ HISTORY_TYPE &type = types[typeNum];
+
+ char *module;
+ if (type.track.db.module == MODULE_PROTOCOL)
+ module = (char *) CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM) hContact, 0);
+ else
+ module = type.track.db.module;
+
+ char current_setting[256];
+ mir_snprintf(current_setting, MAX_REGS(current_setting), "%s_%s_Current", module, type.track.db.setting);
+
+ DBVARIANT old = {0};
+ BOOL found_old = (DBGetContactSetting(hContact, MODULE_NAME, current_setting, &old) == 0);
+
+ DBVARIANT new_ = {0};
+ BOOL found_new = (DBGetContactSetting(hContact, module, type.track.db.setting, &new_) == 0);
+
+ DWORD oldVal;
+ if (found_old)
+ oldVal = GetDWORD(old);
+ else
+ oldVal = type.defs.value;
+ DWORD newVal = GetDWORD(new_);
+
+ if (type.track.db.type == DBVT_BYTE)
+ {
+ oldVal = (oldVal == 0 ? 0 : 1);
+ newVal = (newVal == 0 ? 0 : 1);
+ }
+
+ if (type.canBeRemoved && oldVal == type.defs.value)
+ found_old = FALSE;
+ if (type.canBeRemoved && newVal == type.defs.value)
+ found_new = FALSE;
+
+ // 0 if not changed, 1 if changed, 2 if removed
+ int ret = 0;
+ if (!found_new || new_.type == DBVT_BLOB)
+ {
+ ret = (found_old ? 2 : 0);
+ }
+ else
+ {
+ ret = (newVal != oldVal ? 1 : 0);
+ }
+
+ BOOL track_removes;
+ if (ret == 2)
+ track_removes = type.canBeRemoved &&
+ (opts[typeNum].popup_track_removes
+ || opts[typeNum].file_track_removes
+ || opts[typeNum].speak_track_removes
+ || HistoryEvents_IsEnabledTemplate(type.eventType, ret - 1));
+
+ if (ret == 1 || (ret == 2 && track_removes))
+ {
+ TCHAR tmp_old[256];
+ type.fFormat(tmp_old, MAX_REGS(tmp_old), (void *) oldVal);
+
+ TCHAR tmp_new[256];
+ if (found_new)
+ type.fFormat(tmp_new, MAX_REGS(tmp_new), (void *) newVal);
+ else
+ tmp_new[0] = _T('\0');
+
+ Log(typeNum, found_old, ret == 1, hContact, tmp_old, tmp_new, notify);
+
+ // Copy new to current after notification, so old value can still be accessed
+ if (ret == 2)
+ {
+ if (type.temporary)
+ {
+ switch(old.type)
+ {
+ case DBVT_BYTE: DBWriteContactSettingByte(hContact, MODULE_NAME, current_setting, (BYTE) type.defs.value); break;
+ case DBVT_WORD: DBWriteContactSettingWord(hContact, MODULE_NAME, current_setting, (WORD) type.defs.value); break;
+ case DBVT_DWORD: DBWriteContactSettingDword(hContact, MODULE_NAME, current_setting, type.defs.value); break;
+ }
+ }
+ else
+ DBDeleteContactSetting(hContact, MODULE_NAME, current_setting);
+ }
+ else
+ {
+ switch(new_.type)
+ {
+ case DBVT_BYTE: DBWriteContactSettingByte(hContact, MODULE_NAME, current_setting, new_.bVal); break;
+ case DBVT_WORD: DBWriteContactSettingWord(hContact, MODULE_NAME, current_setting, new_.wVal); break;
+ case DBVT_DWORD: DBWriteContactSettingDword(hContact, MODULE_NAME, current_setting, new_.dVal); break;
+ }
+ }
+ }
+
+ DBFreeVariant(&old);
+ DBFreeVariant(&new_);
+}
+
+
+void Process(HANDLE hContact, void *param)
+{
+ QueueData *qd = (QueueData *) param;
+
+ if (types[qd->type].track.db.type == DBVT_UTF8)
+ TrackChangeString(qd->type, hContact, TRUE);
+ else
+ TrackChangeNumber(qd->type, hContact, TRUE);
+
+ if (qd->notifyAlso >= 0)
+ {
+ if (types[qd->notifyAlso].track.db.type == DBVT_UTF8)
+ TrackChangeString(qd->notifyAlso, hContact, FALSE);
+ else
+ TrackChangeNumber(qd->notifyAlso, hContact, FALSE);
+ }
+
+ delete param;
+}
diff --git a/Plugins/historykeeper/historykeeper.dsp b/Plugins/historykeeper/historykeeper.dsp
new file mode 100644
index 0000000..22ae6e1
--- /dev/null
+++ b/Plugins/historykeeper/historykeeper.dsp
@@ -0,0 +1,315 @@
+# Microsoft Developer Studio Project File - Name="historykeeper" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=historykeeper - Win32 Release
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "historykeeper.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "historykeeper.mak" CFG="historykeeper - Win32 Release"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "historykeeper - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "historykeeper - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "historykeeper - Win32 Unicode Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "historykeeper - Win32 Unicode Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "historykeeper - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MD /W3 /GX /O1 /YX /FD /c
+# SUBTRACT BASE CPP /Fr
+# ADD CPP /nologo /G4 /MT /W3 /GX /O2 /Ob0 /I "../../include" /I "sdk" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /Fr /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x417 /d "NDEBUG"
+# ADD RSC /l 0x417 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 user32.lib shell32.lib wininet.lib gdi32.lib /nologo /base:"0x67100000" /dll /machine:I386 /filealign:0x200
+# SUBTRACT BASE LINK32 /pdb:none /map
+# ADD LINK32 kernel32.lib user32.lib comdlg32.lib comctl32.lib /nologo /base:"0x3EC20000" /dll /map /debug /machine:I386 /out:"..\..\bin\release\Plugins\historykeeper.dll" /filealign:0x200 /ALIGN:4096 /ignore:4108
+# SUBTRACT LINK32 /profile /pdb:none
+
+!ELSEIF "$(CFG)" == "historykeeper - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Ignore_Export_Lib 0
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /G4 /MT /W3 /GX /O2 /Ob0 /I "../../include" /FR /YX /FD /c
+# ADD CPP /nologo /G4 /MTd /W3 /GX /ZI /Od /I "../../include" /I "sdk" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /FR /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x417 /d "NDEBUG"
+# ADD RSC /l 0x417 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 comctl32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 /out:"..\..bin\release\Plugins\historykeeper.dll" /filealign:0x200 /ALIGN:4096 /ignore:4108
+# SUBTRACT BASE LINK32 /profile /pdb:none
+# ADD LINK32 kernel32.lib user32.lib comdlg32.lib comctl32.lib /nologo /base:"0x3EC20000" /dll /incremental:yes /debug /machine:I386 /out:"..\..\bin\debug\Plugins\historykeeper.dll" /filealign:0x200 /ALIGN:4096 /ignore:4108
+# SUBTRACT LINK32 /profile /pdb:none
+
+!ELSEIF "$(CFG)" == "historykeeper - Win32 Unicode Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Unicode_Debug"
+# PROP BASE Intermediate_Dir "Unicode_Debug"
+# PROP BASE Ignore_Export_Lib 0
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Unicode_Debug"
+# PROP Intermediate_Dir "Unicode_Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /G4 /MTd /W3 /GX /ZI /Od /I "../../include" /FR /YX /FD /c
+# ADD CPP /nologo /G4 /MTd /W3 /GX /ZI /Od /I "../../include" /I "sdk" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_UNICODE" /D "UNICODE" /D "_USRDLL" /FR /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x417 /d "NDEBUG"
+# ADD RSC /l 0x417 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 comctl32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /base:"0x32100000" /dll /incremental:yes /debug /machine:I386 /out:"..\..\bin\debug\Plugins\historykeeper.dll" /filealign:0x200 /ALIGN:4096 /ignore:4108
+# SUBTRACT BASE LINK32 /profile /pdb:none
+# ADD LINK32 kernel32.lib user32.lib comdlg32.lib comctl32.lib /nologo /base:"0x3EC20000" /dll /incremental:yes /debug /machine:I386 /out:"..\..\bin\debug unicode\Plugins\historykeeperW.dll" /filealign:0x200 /ALIGN:4096 /ignore:4108
+# SUBTRACT LINK32 /profile /pdb:none
+
+!ELSEIF "$(CFG)" == "historykeeper - Win32 Unicode Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Unicode_Release"
+# PROP BASE Intermediate_Dir "Unicode_Release"
+# PROP BASE Ignore_Export_Lib 0
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Unicode_Release"
+# PROP Intermediate_Dir "Unicode_Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /G4 /MT /W3 /GX /O2 /Ob0 /I "../../include" /Fr /YX /FD /c
+# ADD CPP /nologo /G4 /MT /W3 /GX /O2 /Ob0 /I "../../include" /I "sdk" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_UNICODE" /D "UNICODE" /D "_USRDLL" /Fr /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x417 /d "NDEBUG"
+# ADD RSC /l 0x417 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 comctl32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /base:"0x32100000" /dll /map /machine:I386 /out:"..\..\bin\release\Plugins\historykeeper.dll" /filealign:0x200 /ALIGN:4096 /ignore:4108
+# SUBTRACT BASE LINK32 /profile /pdb:none
+# ADD LINK32 kernel32.lib user32.lib comdlg32.lib comctl32.lib /nologo /base:"0x3EC20000" /dll /map /debug /machine:I386 /out:"..\..\bin\release unicode\Plugins\historykeeperW.dll" /filealign:0x200 /ALIGN:4096 /ignore:4108
+# SUBTRACT LINK32 /profile /pdb:none
+
+!ENDIF
+
+# Begin Target
+
+# Name "historykeeper - Win32 Release"
+# Name "historykeeper - Win32 Debug"
+# Name "historykeeper - Win32 Unicode Debug"
+# Name "historykeeper - Win32 Unicode Release"
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=.\commons.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\utils\ContactAsyncQueue.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\m_historykeeper.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\utils\mir_buffer.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\utils\mir_memory.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\utils\mir_options.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\options.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\popup.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\resource.h
+# End Source File
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
+# Begin Source File
+
+SOURCE=.\res\client_change.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\res\file.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\res\history.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\res\idle_change.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\res\listening_change.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\res\nick_change.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\res\nickname_change.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\res\popup.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\resource.rc
+# End Source File
+# Begin Source File
+
+SOURCE=.\res\sm_change.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\res\smalldot.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\res\sound.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\res\speak.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\res\status_change.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\res\xsm_change.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\res\xstatus_change.ico
+# End Source File
+# End Group
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=.\config.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\utils\ContactAsyncQueue.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\historykeeper.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\utils\mir_options.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\options.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\popup.cpp
+# End Source File
+# End Group
+# Begin Group "Docs"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=.\Docs\historykeeper_changelog.txt
+# End Source File
+# Begin Source File
+
+SOURCE=.\Docs\historykeeper_readme.txt
+# End Source File
+# Begin Source File
+
+SOURCE=.\Docs\historykeeper_version.txt
+# End Source File
+# Begin Source File
+
+SOURCE=.\Docs\langpack_historykeeper.txt
+# End Source File
+# End Group
+# End Target
+# End Project
diff --git a/Plugins/historykeeper/historykeeper.dsw b/Plugins/historykeeper/historykeeper.dsw
new file mode 100644
index 0000000..bf4c71c
--- /dev/null
+++ b/Plugins/historykeeper/historykeeper.dsw
@@ -0,0 +1,29 @@
+Microsoft Developer Studio Workspace File, Format Version 6.00
+# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
+
+###############################################################################
+
+Project: "historykeeper"=".\historykeeper.dsp" - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Global:
+
+Package=<5>
+{{{
+}}}
+
+Package=<3>
+{{{
+}}}
+
+###############################################################################
+
diff --git a/Plugins/historykeeper/m_historykeeper.h b/Plugins/historykeeper/m_historykeeper.h
new file mode 100644
index 0000000..7c4a25d
--- /dev/null
+++ b/Plugins/historykeeper/m_historykeeper.h
@@ -0,0 +1,95 @@
+/*
+Copyright (C) 2006-2009 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#ifndef __M_HISTORYKEEPER_H__
+# define __M_HISTORYKEEPER_H__
+
+
+#define MIID_STATUS_MESSAGE_CHANGE_LOGGER { 0x821be252, 0xe20b, 0x41e7, { 0xa5, 0x1d, 0x3c, 0x34, 0x2e, 0x38, 0xae, 0x22 } }
+#define MIID_STATUS_MESSAGE_CHANGE_NOTIFIER { 0xb628b23b, 0x47ae, 0x430e, { 0x94, 0x81, 0x15, 0x9f, 0xa7, 0x26, 0xc4, 0x3a } }
+#define MIID_NICKNAME_CHANGE_LOGGER { 0x478be45e, 0xd331, 0x4d63, { 0xa6, 0x57, 0x85, 0xda, 0x45, 0xf8, 0xc, 0xe0 } }
+#define MIID_NICKNAME_CHANGE_NOTIFIER { 0xc749d46a, 0x885e, 0x46bf, { 0xaa, 0x4c, 0xe1, 0xae, 0xc5, 0xc9, 0xd0, 0x93 } }
+
+#define EVENTTYPE_STATUSCHANGE 25368
+#define EVENTTYPE_NICKNAME_CHANGE 9001
+#define EVENTTYPE_STATUSMESSAGE_CHANGE 9002
+#define EVENTTYPE_CLIENT_CHANGE 9005
+#define EVENTTYPE_XSTATUS_CHANGE 9006
+#define EVENTTYPE_LISTENINGTO_CHANGE 9007
+#define EVENTTYPE_XSTATUS_MESSAGE_CHANGE 9008
+#define EVENTTYPE_IDLE_CHANGE 9009
+
+
+/*
+Return TRUE is Status Message History is enabled for this contact
+
+wParam: hContact
+lParam: ignored
+*/
+#define MS_SMH_ENABLED "SMH/Enabled"
+
+
+/*
+Enable Status Message History for a contact
+
+wParam: hContact
+lParam: ignored
+*/
+#define MS_SMH_ENABLE "SMH/Enable"
+
+
+/*
+Disable Status Message History for a contact
+
+wParam: hContact
+lParam: ignored
+*/
+#define MS_SMH_DISABLE "SMH/Disable"
+
+
+/*
+Return TRUE is Nick History is enabled for this contact
+
+wParam: hContact
+lParam: ignored
+*/
+#define MS_NICKHISTORY_ENABLED "NickHistory/Enabled"
+
+
+/*
+Enable Nick History for a contact
+
+wParam: hContact
+lParam: ignored
+*/
+#define MS_NICKHISTORY_ENABLE "NickHistory/Enable"
+
+
+/*
+Disable Nick History for a contact
+
+wParam: hContact
+lParam: ignored
+*/
+#define MS_NICKHISTORY_DISABLE "NickHistory/Disable"
+
+
+
+#endif // __M_HISTORYKEEPER_H__
diff --git a/Plugins/historykeeper/options.cpp b/Plugins/historykeeper/options.cpp
new file mode 100644
index 0000000..c4d1403
--- /dev/null
+++ b/Plugins/historykeeper/options.cpp
@@ -0,0 +1,814 @@
+/*
+Copyright (C) 2006-2009 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+// XXX
+// All this file is one BIG HACK
+
+#include "commons.h"
+
+#include <crtdbg.h>
+
+
+// Prototypes /////////////////////////////////////////////////////////////////////////////////////
+
+HANDLE hOptHook = NULL;
+
+
+static BOOL CALLBACK OptionsDlgProc(int type, HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam);
+static BOOL CALLBACK PopupsDlgProc(int type, HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam);
+static BOOL CALLBACK SpeakDlgProc(int type, HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam);
+static BOOL CALLBACK NotificationsDlgProc(int type, HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam);
+
+#define HACK(i) \
+ static BOOL CALLBACK OptionsDlgProc ## i (HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) \
+ { \
+ return OptionsDlgProc(i, hwndDlg, msg, wParam, lParam); \
+ } \
+ static BOOL CALLBACK PopupsDlgProc ## i (HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) \
+ { \
+ return PopupsDlgProc(i, hwndDlg, msg, wParam, lParam); \
+ } \
+ static BOOL CALLBACK SpeakDlgProc ## i (HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) \
+ { \
+ return SpeakDlgProc(i, hwndDlg, msg, wParam, lParam); \
+ } \
+ static BOOL CALLBACK NotificationsDlgProc ## i (HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) \
+ { \
+ return NotificationsDlgProc(i, hwndDlg, msg, wParam, lParam); \
+ } \
+ static BOOL AllowProtocol ## i(const char *proto) \
+ { \
+ return AllowProtocol(i, proto); \
+ }
+
+HACK(0);
+HACK(1);
+HACK(2);
+HACK(3);
+HACK(4);
+HACK(5);
+HACK(6);
+HACK(7);
+
+DLGPROC NotificationsDlgProcArr[] = { NotificationsDlgProc0, NotificationsDlgProc1, NotificationsDlgProc2, NotificationsDlgProc3, NotificationsDlgProc4, NotificationsDlgProc5, NotificationsDlgProc6, NotificationsDlgProc7 };
+DLGPROC OptionsDlgProcArr[] = { OptionsDlgProc0, OptionsDlgProc1, OptionsDlgProc2, OptionsDlgProc3, OptionsDlgProc4, OptionsDlgProc5, OptionsDlgProc6, OptionsDlgProc7 };
+DLGPROC PopupsDlgProcArr[] = { PopupsDlgProc0, PopupsDlgProc1, PopupsDlgProc2, PopupsDlgProc3, PopupsDlgProc4, PopupsDlgProc5, PopupsDlgProc6, PopupsDlgProc7 };
+DLGPROC SpeakDlgProcArr[] = { SpeakDlgProc0, SpeakDlgProc1, SpeakDlgProc2, SpeakDlgProc3, SpeakDlgProc4, SpeakDlgProc5, SpeakDlgProc6, SpeakDlgProc7 };
+FPAllowProtocol AllowProtocolArr[] = { AllowProtocol0, AllowProtocol1, AllowProtocol2, AllowProtocol3, AllowProtocol4, AllowProtocol5, AllowProtocol6, AllowProtocol7 };
+
+
+
+#define OPTIONS_CONTROLS_SIZE 9
+static OptPageControl optionsControls[NUM_TYPES][OPTIONS_CONTROLS_SIZE] = {0};
+
+#define POPUPS_CONTROLS_SIZE 14
+static OptPageControl popupsControls[NUM_TYPES][POPUPS_CONTROLS_SIZE] = {0};
+
+#define SPEAK_CONTROLS_SIZE 4
+static OptPageControl speakControls[NUM_TYPES][SPEAK_CONTROLS_SIZE] = {0};
+
+static UINT popupsExpertControls[] = {
+ IDC_COLOURS_G, IDC_BGCOLOR, IDC_BGCOLOR_L, IDC_TEXTCOLOR, IDC_TEXTCOLOR_L, IDC_WINCOLORS, IDC_DEFAULTCOLORS,
+ IDC_DELAY_G, IDC_DELAYFROMPU, IDC_DELAYCUSTOM, IDC_DELAYPERMANENT, IDC_DELAY, IDC_DELAY_SPIN,
+ IDC_ACTIONS_G, IDC_RIGHT_ACTION_L, IDC_RIGHT_ACTION, IDC_LEFT_ACTION_L, IDC_LEFT_ACTION,
+ IDC_PREV
+};
+
+
+Options opts[NUM_TYPES];
+
+
+// Functions //////////////////////////////////////////////////////////////////////////////////////
+
+
+BOOL HasPopups()
+{
+ return ServiceExists(MS_POPUP_ADDPOPUPEX)
+#ifdef UNICODE
+ || ServiceExists(MS_POPUP_ADDPOPUPW)
+#endif
+ ;
+}
+
+
+BOOL HasOldSpeak()
+{
+ return ServiceExists(MS_SPEAK_SAY_A) && !ServiceExists(MS_SPEAK_SAYEX);
+}
+
+
+int InitOptionsCallback(WPARAM wParam,LPARAM lParam)
+{
+ OPTIONSDIALOGPAGE odp = {0};
+ odp.cbSize = sizeof(odp);
+ odp.hInstance = hInst;
+ odp.pszGroup = "History";
+ odp.flags = ODPF_BOLDGROUPS | ODPF_EXPERTONLY;
+
+ _ASSERT(MAX_REGS(OptionsDlgProcArr) == NUM_TYPES);
+ for (int i = 0; i < NUM_TYPES; i++)
+ {
+ odp.pszTitle = types[i].description;
+
+ if (types[i].canBeRemoved)
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPTIONS_REM);
+ else
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPTIONS_NOREM);
+ odp.pszTab = "General";
+ odp.pfnDlgProc = OptionsDlgProcArr[i];
+ CallService(MS_OPT_ADDPAGE, wParam, (LPARAM)&odp);
+
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_NOTIFICATIONS);
+ odp.pszTab = "Notifications";
+ odp.pfnDlgProc = NotificationsDlgProcArr[i];
+ CallService(MS_OPT_ADDPAGE, wParam, (LPARAM)&odp);
+ }
+
+
+ _ASSERT(MAX_REGS(PopupsDlgProcArr) == NUM_TYPES);
+ if (HasPopups())
+ {
+ ZeroMemory(&odp,sizeof(odp));
+ odp.cbSize = sizeof(odp);
+ odp.hInstance = hInst;
+ odp.pszGroup = Translate("Popups");
+ odp.flags = ODPF_BOLDGROUPS;
+ odp.expertOnlyControls = popupsExpertControls;
+ odp.nExpertOnlyControls = MAX_REGS(popupsExpertControls);
+ odp.nIDBottomSimpleControl = IDC_TRACK_G;
+
+ for (int i = 0; i < NUM_TYPES; i++)
+ {
+ if (types[i].canBeRemoved)
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_POPUPS_REM);
+ else
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_POPUPS_NOREM);
+
+ char tmp[128];
+ mir_snprintf(tmp, MAX_REGS(tmp), Translate("%s Change"), types[i].description);
+
+ odp.pszTitle = tmp;
+ odp.pfnDlgProc = PopupsDlgProcArr[i];
+ CallService(MS_OPT_ADDPAGE, wParam, (LPARAM) &odp);
+ }
+ }
+
+ _ASSERT(MAX_REGS(SpeakDlgProcArr) == NUM_TYPES);
+ if (HasOldSpeak())
+ {
+ ZeroMemory(&odp,sizeof(odp));
+ odp.cbSize = sizeof(odp);
+ odp.hInstance = hInst;
+ odp.pszGroup = Translate("Speak");
+ odp.flags = ODPF_BOLDGROUPS;
+
+ for (int i = 0; i < NUM_TYPES; i++)
+ {
+ if (types[i].canBeRemoved)
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_SPEAK_REM);
+ else
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_SPEAK_NOREM);
+
+ char tmp[128];
+ mir_snprintf(tmp, MAX_REGS(tmp), Translate("%s Change"), types[i].description);
+
+ odp.pszTitle = tmp;
+ odp.pfnDlgProc = SpeakDlgProcArr[i];
+ CallService(MS_OPT_ADDPAGE, wParam, (LPARAM) &odp);
+ }
+ }
+
+ return 0;
+}
+
+
+
+void InitOptions()
+{
+ static TCHAR fileChangeTemplates[NUM_TYPES][128];
+ static TCHAR fileRemoveTemplates[NUM_TYPES][128];
+ static TCHAR changeTemplates[NUM_TYPES][128];
+ static TCHAR removeTemplates[NUM_TYPES][128];
+ static TCHAR speakChangeTemplates[NUM_TYPES][128];
+ static TCHAR speakRemoveTemplates[NUM_TYPES][128];
+ static char optSet[NUM_TYPES][OPTIONS_CONTROLS_SIZE][64];
+ static char popSet[NUM_TYPES][POPUPS_CONTROLS_SIZE][64];
+ static char speakSet[NUM_TYPES][POPUPS_CONTROLS_SIZE][64];
+
+ for (int i = 0; i < NUM_TYPES; i++)
+ {
+ TCHAR *tmp = mir_a2t(types[i].description);
+ CharLower(tmp);
+
+ // Options page
+ if (types[i].defs.change_template != NULL)
+ mir_sntprintf(&fileChangeTemplates[i][0], 128, _T("[%%date%%] %%contact%% ") _T(TCHAR_STR_PARAM), types[i].defs.change_template);
+ else
+ mir_sntprintf(&fileChangeTemplates[i][0], 128, _T("[%%date%%] %%contact%% changed his/her %s to %%new%% (was %%old%%)"), tmp);
+
+ if (types[i].defs.remove_template != NULL)
+ mir_sntprintf(&fileRemoveTemplates[i][0], 128, _T("[%%date%%] %%contact%% ") _T(TCHAR_STR_PARAM), types[i].defs.remove_template);
+ else
+ mir_sntprintf(&fileRemoveTemplates[i][0], 128, _T("[%%date%%] %%contact%% removed his/her %s (was %%old%%)"), tmp);
+
+ OptPageControl opt[] = {
+ { &opts[i].ttw, CONTROL_SPIN, IDC_NWAIT, "Delay", types[i].defs.ttw, IDC_NWAIT_SPIN, (WORD) 0, (WORD) 120 },
+ { &opts[i].track_only_not_offline, CONTROL_CHECKBOX, IDC_ONLY_NOT_OFFLINE, "TrackOnlyWhenNotOffline", types[i].defs.track_only_not_offline },
+ { &opts[i].dont_notify_on_connect, CONTROL_CHECKBOX, IDC_DONT_NOTIFY_ON_CONNECT, "DontNotifyOnConnect", TRUE },
+ { NULL, CONTROL_PROTOCOL_LIST, IDC_PROTOCOLS, "%sEnabled", TRUE, (int) AllowProtocolArr[i] },
+ { &opts[i].file_name, CONTROL_FILE, IDC_FILENAME, "FileName", (DWORD) _T("Log\\history_keeper.log") },
+ { &opts[i].file_track_changes, CONTROL_CHECKBOX, IDC_TRACK_CHANGE, "FileTrackChanges", FALSE },
+ { &opts[i].file_template_changed, CONTROL_TEXT, IDC_CHANGED, "FileTemplateChanged", (DWORD) &fileChangeTemplates[i][0] },
+ { &opts[i].file_track_removes, CONTROL_CHECKBOX, IDC_TRACK_REMOVE, "FileTrackRemoves", FALSE },
+ { &opts[i].file_template_removed, CONTROL_TEXT, IDC_REMOVED, "FileTemplateRemoved", (DWORD) &fileRemoveTemplates[i][0] },
+ };
+
+ _ASSERT(MAX_REGS(opt) == OPTIONS_CONTROLS_SIZE);
+ int j;
+ for(j = 0; j < OPTIONS_CONTROLS_SIZE; j++)
+ {
+ mir_snprintf(&optSet[i][j][0], 64, "%s_%s", types[i].name, opt[j].setting);
+ opt[j].setting = &optSet[i][j][0];
+ }
+ memcpy(&optionsControls[i][0], &opt, sizeof(opt));
+
+ // Popups page
+ if (HasPopups())
+ {
+ if (types[i].defs.change_template_popup != NULL)
+ mir_sntprintf(&changeTemplates[i][0], 128, types[i].defs.change_template_popup, tmp);
+ else if (types[i].defs.change_template != NULL)
+ mir_sntprintf(&changeTemplates[i][0], 128, _T(TCHAR_STR_PARAM), types[i].defs.change_template);
+ else
+ mir_sntprintf(&changeTemplates[i][0], 128, _T("changed his/her %s to %%new%% (was %%old%%)"), tmp);
+
+ if (types[i].defs.remove_template_popup != NULL)
+ mir_sntprintf(&removeTemplates[i][0], 128, types[i].defs.remove_template_popup, tmp);
+ else if (types[i].defs.remove_template != NULL)
+ mir_sntprintf(&removeTemplates[i][0], 128, _T(TCHAR_STR_PARAM), types[i].defs.remove_template);
+ else
+ mir_sntprintf(&removeTemplates[i][0], 128, _T("removed his/her %s (was %%old%%)"), tmp);
+
+ OptPageControl pops[] = {
+ { &opts[i].popup_track_changes, CONTROL_CHECKBOX, IDC_TRACK_CHANGE, "PopupsTrackChanges", TRUE },
+ { &opts[i].popup_template_changed, CONTROL_TEXT, IDC_CHANGED, "PopupsTemplateChanged", (DWORD) &changeTemplates[i][0] },
+ { &opts[i].popup_track_removes, CONTROL_CHECKBOX, IDC_TRACK_REMOVE, "PopupsTrackRemoves", TRUE },
+ { &opts[i].popup_template_removed, CONTROL_TEXT, IDC_REMOVED, "PopupsTemplateRemoved", (DWORD) &removeTemplates[i][0] },
+ { &opts[i].popup_bkg_color, CONTROL_COLOR, IDC_BGCOLOR, "PopupsBgColor", RGB(255,255,255) },
+ { &opts[i].popup_text_color, CONTROL_COLOR, IDC_TEXTCOLOR, "PopupsTextColor", RGB(0,0,0) },
+ { &opts[i].popup_use_win_colors, CONTROL_CHECKBOX, IDC_WINCOLORS, "PopupsWinColors", FALSE },
+ { &opts[i].popup_use_default_colors, CONTROL_CHECKBOX, IDC_DEFAULTCOLORS, "PopupsDefaultColors", FALSE },
+ { &opts[i].popup_delay_type, CONTROL_RADIO, IDC_DELAYFROMPU, "PopupsDelayType", POPUP_DELAY_DEFAULT, POPUP_DELAY_DEFAULT },
+ { NULL, CONTROL_RADIO, IDC_DELAYCUSTOM, "PopupsDelayType", POPUP_DELAY_DEFAULT, POPUP_DELAY_CUSTOM },
+ { NULL, CONTROL_RADIO, IDC_DELAYPERMANENT, "PopupsDelayType", POPUP_DELAY_DEFAULT, POPUP_DELAY_PERMANENT },
+ { &opts[i].popup_timeout, CONTROL_SPIN, IDC_DELAY, "PopupsTimeout", 10, IDC_DELAY_SPIN, (WORD) 1, (WORD) 255 },
+ { &opts[i].popup_right_click_action, CONTROL_COMBO, IDC_RIGHT_ACTION, "PopupsRightClick", POPUP_ACTION_CLOSEPOPUP },
+ { &opts[i].popup_left_click_action, CONTROL_COMBO, IDC_LEFT_ACTION, "PopupsLeftClick", POPUP_ACTION_OPENHISTORY }
+ };
+ _ASSERT(MAX_REGS(pops) == POPUPS_CONTROLS_SIZE);
+ for(j = 0; j < POPUPS_CONTROLS_SIZE; j++)
+ {
+ mir_snprintf(&popSet[i][j][0], 64, "%s_%s", types[i].name, pops[j].setting);
+ pops[j].setting = &popSet[i][j][0];
+ }
+ memcpy(&popupsControls[i][0], &pops, sizeof(pops));
+ }
+
+ if (HasOldSpeak())
+ {
+ // Speak pages
+ if (types[i].defs.change_template != NULL)
+ mir_sntprintf(&speakChangeTemplates[i][0], 128, _T("%%contact%% ") _T(TCHAR_STR_PARAM), types[i].defs.change_template);
+ else
+ mir_sntprintf(&speakChangeTemplates[i][0], 128, TranslateT("%%contact%% changed his/her %s to %%new%%"), tmp);
+
+ if (types[i].defs.remove_template != NULL)
+ mir_sntprintf(&speakRemoveTemplates[i][0], 128, _T("%%contact%% ") _T(TCHAR_STR_PARAM), types[i].defs.remove_template);
+ else
+ mir_sntprintf(&speakRemoveTemplates[i][0], 128, _T("%%contact%% removed his/her %s"), tmp);
+
+ OptPageControl opt[] = {
+ { &opts[i].speak_track_changes, CONTROL_CHECKBOX, IDC_TRACK_CHANGE, "SpeakTrackChanges", FALSE },
+ { &opts[i].speak_template_changed, CONTROL_TEXT, IDC_CHANGED, "SpeakTemplateChanged", (DWORD) &speakChangeTemplates[i][0] },
+ { &opts[i].speak_track_removes, CONTROL_CHECKBOX, IDC_TRACK_REMOVE, "SpeakTrackRemoves", FALSE },
+ { &opts[i].speak_template_removed, CONTROL_TEXT, IDC_REMOVED, "SpeakTemplateRemoved", (DWORD) &speakRemoveTemplates[i][0] },
+ };
+
+ _ASSERT(MAX_REGS(opt) == SPEAK_CONTROLS_SIZE);
+ int j;
+ for(j = 0; j < SPEAK_CONTROLS_SIZE; j++)
+ {
+ mir_snprintf(&speakSet[i][j][0], 64, "%s_%s", types[i].name, opt[j].setting);
+ opt[j].setting = &speakSet[i][j][0];
+ }
+ memcpy(&speakControls[i][0], &opt, sizeof(opt));
+ }
+
+ mir_free(tmp);
+ }
+
+ LoadOptions();
+
+ hOptHook = HookEvent(ME_OPT_INITIALISE, InitOptionsCallback);
+}
+
+
+void DeInitOptions()
+{
+ UnhookEvent(hOptHook);
+}
+
+
+void LoadOptions()
+{
+ for (int i = 0; i < NUM_TYPES; i++)
+ {
+ LoadOpts(&optionsControls[i][0], OPTIONS_CONTROLS_SIZE, MODULE_NAME);
+ if (HasPopups())
+ LoadOpts(&popupsControls[i][0], POPUPS_CONTROLS_SIZE, MODULE_NAME);
+ if (HasOldSpeak())
+ LoadOpts(&speakControls[i][0], SPEAK_CONTROLS_SIZE, MODULE_NAME);
+ }
+}
+
+
+static BOOL CALLBACK OptionsDlgProc(int type, HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg)
+ {
+ case WM_INITDIALOG:
+ {
+ TCHAR *desc = mir_a2t(types[type].description);
+ CharLower(desc);
+
+ TCHAR tmp[256];
+ mir_sntprintf(tmp, MAX_REGS(tmp), TranslateT("Log when contacts change their %s"), desc);
+ SetDlgItemText(hwndDlg, IDC_TRACK_CHANGE, tmp);
+ mir_sntprintf(tmp, MAX_REGS(tmp), TranslateT("Log when contacts remove their %s"), desc);
+ SetDlgItemText(hwndDlg, IDC_TRACK_REMOVE, tmp);
+
+ mir_free(desc);
+
+ break;
+ }
+ case WM_COMMAND:
+ {
+ switch (LOWORD(wParam))
+ {
+ case IDC_SELECT_FILE:
+ {
+ if (HIWORD(wParam) == BN_CLICKED)
+ {
+ TCHAR file[1024] = _T("");
+ GetDlgItemText(hwndDlg, IDC_FILENAME, file, 1024);
+
+ OPENFILENAME ofn = {0};
+ ofn.lStructSize = sizeof(OPENFILENAME);
+ ofn.Flags = OFN_EXPLORER | OFN_ENABLESIZING | OFN_HIDEREADONLY | OFN_NOREADONLYRETURN;
+ ofn.hwndOwner = hwndDlg;
+ ofn.lpstrFile = file;
+ ofn.nMaxFile = MAX_REGS(file);
+ ofn.lpstrFilter = _T("Log Files (*.log;*.txt)\0*.LOG;*.TXT\0All Files (*.*)\0*.*\0\0");
+ ofn.lpstrDefExt = _T(".log");
+ ofn.hInstance = hInst;
+
+ if (GetOpenFileName(&ofn))
+ SetDlgItemText(hwndDlg, IDC_FILENAME, file);
+ }
+ break;
+ }
+ }
+ break;
+ }
+ }
+
+ return SaveOptsDlgProc(&optionsControls[type][0], OPTIONS_CONTROLS_SIZE, MODULE_NAME, hwndDlg, msg, wParam, lParam);
+}
+
+
+static BOOL CALLBACK SpeakDlgProc(int type, HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg)
+ {
+ case WM_INITDIALOG:
+ {
+ TCHAR *desc = mir_a2t(types[type].description);
+ CharLower(desc);
+
+ TCHAR tmp[256];
+ mir_sntprintf(tmp, MAX_REGS(tmp), TranslateT("Announce when contacts change their %s"), desc);
+ SetDlgItemText(hwndDlg, IDC_TRACK_CHANGE, tmp);
+ mir_sntprintf(tmp, MAX_REGS(tmp), TranslateT("Announce when contacts remove their %s"), desc);
+ SetDlgItemText(hwndDlg, IDC_TRACK_REMOVE, tmp);
+
+ mir_free(desc);
+
+ break;
+ }
+ }
+ return SaveOptsDlgProc(&speakControls[type][0], SPEAK_CONTROLS_SIZE, MODULE_NAME, hwndDlg, msg, wParam, lParam);
+}
+
+
+static void PopupsEnableDisableCtrls(HWND hwndDlg)
+{
+ EnableWindow(GetDlgItem(hwndDlg, IDC_CHANGED_L), IsDlgButtonChecked(hwndDlg, IDC_TRACK_CHANGE));
+ EnableWindow(GetDlgItem(hwndDlg, IDC_CHANGED), IsDlgButtonChecked(hwndDlg, IDC_TRACK_CHANGE));
+
+ EnableWindow(GetDlgItem(hwndDlg, IDC_REMOVED_L), IsDlgButtonChecked(hwndDlg, IDC_TRACK_REMOVE));
+ EnableWindow(GetDlgItem(hwndDlg, IDC_REMOVED), IsDlgButtonChecked(hwndDlg, IDC_TRACK_REMOVE));
+
+ EnableWindow(GetDlgItem(hwndDlg, IDC_BGCOLOR),
+ !IsDlgButtonChecked(hwndDlg, IDC_WINCOLORS) &&
+ !IsDlgButtonChecked(hwndDlg, IDC_DEFAULTCOLORS));
+ EnableWindow(GetDlgItem(hwndDlg, IDC_TEXTCOLOR),
+ !IsDlgButtonChecked(hwndDlg, IDC_WINCOLORS) &&
+ !IsDlgButtonChecked(hwndDlg, IDC_DEFAULTCOLORS));
+ EnableWindow(GetDlgItem(hwndDlg, IDC_DEFAULTCOLORS),
+ !IsDlgButtonChecked(hwndDlg, IDC_WINCOLORS));
+ EnableWindow(GetDlgItem(hwndDlg, IDC_WINCOLORS),
+ !IsDlgButtonChecked(hwndDlg, IDC_DEFAULTCOLORS));
+
+ EnableWindow(GetDlgItem(hwndDlg, IDC_DELAY),
+ IsDlgButtonChecked(hwndDlg, IDC_DELAYCUSTOM));
+}
+
+
+static BOOL CALLBACK PopupsDlgProc(int type, HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg)
+ {
+ case WM_INITDIALOG:
+ {
+ TCHAR *desc = mir_a2t(types[type].description);
+ CharLower(desc);
+
+ TCHAR tmp[256];
+ mir_sntprintf(tmp, MAX_REGS(tmp), TranslateT("Show when contacts change their %s"), desc);
+ SetDlgItemText(hwndDlg, IDC_TRACK_CHANGE, tmp);
+ mir_sntprintf(tmp, MAX_REGS(tmp), TranslateT("Show when contacts remove their %s"), desc);
+ SetDlgItemText(hwndDlg, IDC_TRACK_REMOVE, tmp);
+
+ mir_free(desc);
+
+ SendDlgItemMessage(hwndDlg, IDC_RIGHT_ACTION, CB_ADDSTRING, 0, (LONG) TranslateT("Do nothing"));
+ SendDlgItemMessage(hwndDlg, IDC_RIGHT_ACTION, CB_ADDSTRING, 0, (LONG) TranslateT("Close popup"));
+ SendDlgItemMessage(hwndDlg, IDC_RIGHT_ACTION, CB_ADDSTRING, 0, (LONG) TranslateT("Show history"));
+ SendDlgItemMessage(hwndDlg, IDC_RIGHT_ACTION, CB_ADDSTRING, 0, (LONG) TranslateT("Open message window"));
+
+ SendDlgItemMessage(hwndDlg, IDC_LEFT_ACTION, CB_ADDSTRING, 0, (LONG) TranslateT("Do nothing"));
+ SendDlgItemMessage(hwndDlg, IDC_LEFT_ACTION, CB_ADDSTRING, 0, (LONG) TranslateT("Close popup"));
+ SendDlgItemMessage(hwndDlg, IDC_LEFT_ACTION, CB_ADDSTRING, 0, (LONG) TranslateT("Show history"));
+ SendDlgItemMessage(hwndDlg, IDC_LEFT_ACTION, CB_ADDSTRING, 0, (LONG) TranslateT("Open message window"));
+
+ // Needs to be called here in this case
+ BOOL ret = SaveOptsDlgProc(&popupsControls[type][0], POPUPS_CONTROLS_SIZE, MODULE_NAME, hwndDlg, msg, wParam, lParam);
+
+ PopupsEnableDisableCtrls(hwndDlg);
+
+ return ret;
+ }
+ case WM_COMMAND:
+ {
+ switch (LOWORD(wParam))
+ {
+ case IDC_TRACK_REMOVE:
+ case IDC_TRACK_CHANGE:
+ case IDC_POPUPS:
+ case IDC_WINCOLORS:
+ case IDC_DEFAULTCOLORS:
+ case IDC_DELAYFROMPU:
+ case IDC_DELAYPERMANENT:
+ case IDC_DELAYCUSTOM:
+ {
+ if (HIWORD(wParam) == BN_CLICKED)
+ PopupsEnableDisableCtrls(hwndDlg);
+
+ break;
+ }
+ case IDC_PREV:
+ {
+ Options op;
+
+ if (IsDlgButtonChecked(hwndDlg, IDC_DELAYFROMPU))
+ op.popup_delay_type = POPUP_DELAY_DEFAULT;
+ else if (IsDlgButtonChecked(hwndDlg, IDC_DELAYCUSTOM))
+ op.popup_delay_type = POPUP_DELAY_CUSTOM;
+ else if (IsDlgButtonChecked(hwndDlg, IDC_DELAYPERMANENT))
+ op.popup_delay_type = POPUP_DELAY_PERMANENT;
+
+ op.popup_timeout = GetDlgItemInt(hwndDlg,IDC_DELAY, NULL, FALSE);
+ op.popup_bkg_color = SendDlgItemMessage(hwndDlg,IDC_BGCOLOR,CPM_GETCOLOUR,0,0);
+ op.popup_text_color = SendDlgItemMessage(hwndDlg,IDC_TEXTCOLOR,CPM_GETCOLOUR,0,0);
+ op.popup_use_win_colors = IsDlgButtonChecked(hwndDlg, IDC_WINCOLORS) != 0;
+ op.popup_use_default_colors = IsDlgButtonChecked(hwndDlg, IDC_DEFAULTCOLORS) != 0;
+
+ TCHAR tmp[1024];
+ GetDlgItemText(hwndDlg, IDC_CHANGED, tmp, 1024);
+
+ ShowTestPopup(type, TranslateT("Contact Name"), tmp, &op);
+
+ break;
+ }
+ }
+ break;
+ }
+ }
+
+ return SaveOptsDlgProc(&popupsControls[type][0], POPUPS_CONTROLS_SIZE, MODULE_NAME, hwndDlg, msg, wParam, lParam);
+}
+
+static struct {
+ int ico;
+ int ctrl;
+ int item;
+} data[] = {
+ { IDI_HISTORY, IDC_HISTORY, LOG_HISTORY },
+ { IDI_FILE, IDC_FILE, LOG_FILE },
+ { IDI_POPUP, IDC_POPUP, NOTIFY_POPUP },
+ { IDI_SOUND, IDC_SOUND, NOTIFY_SOUND },
+ { IDI_SPEAK, IDC_SPEAK, NOTIFY_SPEAK }
+};
+
+
+static void SetAllContactIcons(int type, HWND hwndList)
+{
+ HANDLE hContact,hItem;
+
+ hContact=(HANDLE)CallService(MS_DB_CONTACT_FINDFIRST,0,0);
+ do
+ {
+ hItem=(HANDLE)SendMessage(hwndList,CLM_FINDCONTACT,(WPARAM)hContact,0);
+ if(hItem)
+ {
+ char *proto = (char *) CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM) hContact, 0);
+ if (!ProtocolEnabled(type, proto))
+ {
+ SendMessage(hwndList,CLM_DELETEITEM,(WPARAM)hItem,0);
+ }
+ else
+ {
+ for (int i = 0; i < MAX_REGS(data); i++) {
+ SendMessage(hwndList,CLM_SETEXTRAIMAGE,(WPARAM)hItem,MAKELPARAM(i,
+ ItemEnabled(type, hContact, data[i].item) ? i+1 : 0));
+ }
+ }
+ }
+ } while(hContact=(HANDLE)CallService(MS_DB_CONTACT_FINDNEXT,(WPARAM)hContact,0));
+}
+
+
+static void SetListGroupIcons(HWND hwndList,HANDLE hFirstItem,HANDLE hParentItem,int *groupChildCount)
+{
+ int typeOfFirst;
+ int iconOn[5]={1,1,1,1,1};
+ int childCount[5]={0,0,0,0,0},i;
+ int iImage;
+ HANDLE hItem,hChildItem;
+
+ typeOfFirst=SendMessage(hwndList,CLM_GETITEMTYPE,(WPARAM)hFirstItem,0);
+ //check groups
+ if(typeOfFirst==CLCIT_GROUP) hItem=hFirstItem;
+ else hItem=(HANDLE)SendMessage(hwndList,CLM_GETNEXTITEM,CLGN_NEXTGROUP,(LPARAM)hFirstItem);
+ while(hItem) {
+ hChildItem=(HANDLE)SendMessage(hwndList,CLM_GETNEXTITEM,CLGN_CHILD,(LPARAM)hItem);
+ if(hChildItem) SetListGroupIcons(hwndList,hChildItem,hItem,childCount);
+ for( i=0; i < MAX_REGS(iconOn); i++)
+ if(iconOn[i] && SendMessage(hwndList,CLM_GETEXTRAIMAGE,(WPARAM)hItem,i)==0) iconOn[i]=0;
+ hItem=(HANDLE)SendMessage(hwndList,CLM_GETNEXTITEM,CLGN_NEXTGROUP,(LPARAM)hItem);
+ }
+ //check contacts
+ if(typeOfFirst==CLCIT_CONTACT) hItem=hFirstItem;
+ else hItem=(HANDLE)SendMessage(hwndList,CLM_GETNEXTITEM,CLGN_NEXTCONTACT,(LPARAM)hFirstItem);
+ while(hItem) {
+ for ( i=0; i < MAX_REGS(iconOn); i++) {
+ iImage=SendMessage(hwndList,CLM_GETEXTRAIMAGE,(WPARAM)hItem,i);
+ if(iconOn[i] && iImage==0) iconOn[i]=0;
+ if(iImage!=0xFF) childCount[i]++;
+ }
+ hItem=(HANDLE)SendMessage(hwndList,CLM_GETNEXTITEM,CLGN_NEXTCONTACT,(LPARAM)hItem);
+ }
+ //set icons
+ if (hParentItem != NULL) {
+ for( i=0; i < MAX_REGS(iconOn); i++) {
+ SendMessage(hwndList,CLM_SETEXTRAIMAGE,(WPARAM)hParentItem,MAKELPARAM(i,childCount[i]?(iconOn[i]?i+1:0):0xFF));
+ if(groupChildCount) groupChildCount[i]+=childCount[i];
+ }
+ }
+}
+
+
+static void SetAllChildIcons(HWND hwndList,HANDLE hFirstItem,int iColumn,int iImage)
+{
+ int typeOfFirst,iOldIcon;
+ HANDLE hItem,hChildItem;
+
+ typeOfFirst=SendMessage(hwndList,CLM_GETITEMTYPE,(WPARAM)hFirstItem,0);
+ //check groups
+ if(typeOfFirst==CLCIT_GROUP) hItem=hFirstItem;
+ else hItem=(HANDLE)SendMessage(hwndList,CLM_GETNEXTITEM,CLGN_NEXTGROUP,(LPARAM)hFirstItem);
+ while(hItem) {
+ hChildItem=(HANDLE)SendMessage(hwndList,CLM_GETNEXTITEM,CLGN_CHILD,(LPARAM)hItem);
+ if(hChildItem) SetAllChildIcons(hwndList,hChildItem,iColumn,iImage);
+ hItem=(HANDLE)SendMessage(hwndList,CLM_GETNEXTITEM,CLGN_NEXTGROUP,(LPARAM)hItem);
+ }
+ //check contacts
+ if(typeOfFirst==CLCIT_CONTACT) hItem=hFirstItem;
+ else hItem=(HANDLE)SendMessage(hwndList,CLM_GETNEXTITEM,CLGN_NEXTCONTACT,(LPARAM)hFirstItem);
+ while(hItem) {
+ iOldIcon=SendMessage(hwndList,CLM_GETEXTRAIMAGE,(WPARAM)hItem,iColumn);
+ if(iOldIcon!=0xFF && iOldIcon!=iImage) SendMessage(hwndList,CLM_SETEXTRAIMAGE,(WPARAM)hItem,MAKELPARAM(iColumn,iImage));
+ hItem=(HANDLE)SendMessage(hwndList,CLM_GETNEXTITEM,CLGN_NEXTCONTACT,(LPARAM)hItem);
+ }
+}
+
+
+static void ResetListOptions(HWND hwndList)
+{
+ int i;
+
+ SendMessage(hwndList,CLM_SETBKBITMAP,0,(LPARAM)(HBITMAP)NULL);
+ SendMessage(hwndList,CLM_SETBKCOLOR,GetSysColor(COLOR_WINDOW),0);
+ SendMessage(hwndList,CLM_SETGREYOUTFLAGS,0,0);
+ SendMessage(hwndList,CLM_SETLEFTMARGIN,4,0);
+ SendMessage(hwndList,CLM_SETINDENT,10,0);
+ SendMessage(hwndList,CLM_SETHIDEEMPTYGROUPS,1,0);
+ for(i=0;i<=FONTID_MAX;i++)
+ SendMessage(hwndList,CLM_SETTEXTCOLOR,i,GetSysColor(COLOR_WINDOWTEXT));
+ SetWindowLong(hwndList,GWL_STYLE,GetWindowLong(hwndList,GWL_STYLE)|CLS_SHOWHIDDEN);
+}
+
+
+static int ImageList_AddIcon_NotShared(HIMAGELIST hIml, int ico)
+{
+ HICON hTempIcon = LoadIcon(hInst, MAKEINTRESOURCE(ico));
+ int res = ImageList_AddIcon(hIml, hTempIcon);
+ //DestroyIcon(hTempIcon);
+ return res;
+}
+
+
+static BOOL CALLBACK NotificationsDlgProc(int type, HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ static HANDLE hItemAll;
+
+ switch (msg)
+ {
+ case WM_INITDIALOG:
+ {
+ TranslateDialogDefault(hwndDlg);
+
+ {
+ HIMAGELIST hIml;
+ hIml=ImageList_Create(GetSystemMetrics(SM_CXSMICON),GetSystemMetrics(SM_CYSMICON),(IsWinVerXPPlus()?ILC_COLOR32:ILC_COLOR16)|ILC_MASK,3,3);
+
+ ImageList_AddIcon_NotShared(hIml, IDI_SMALLDOT);
+ int i;
+ for (i = 0; i < MAX_REGS(data); i++)
+ {
+ ImageList_AddIcon_NotShared(hIml, data[i].ico);
+ }
+
+ SendDlgItemMessage(hwndDlg,IDC_LIST,CLM_SETEXTRAIMAGELIST,0,(LPARAM)hIml);
+
+ for (i = 0; i < MAX_REGS(data); i++)
+ {
+ HICON hIco = ImageList_GetIcon(hIml, i+1, ILD_NORMAL);
+ SendDlgItemMessage(hwndDlg, data[i].ctrl, STM_SETICON, (WPARAM) hIco, 0);
+ }
+ }
+
+ ResetListOptions(GetDlgItem(hwndDlg,IDC_LIST));
+ SendDlgItemMessage(hwndDlg,IDC_LIST,CLM_SETEXTRACOLUMNS, MAX_REGS(data), 0);
+
+ CLCINFOITEM cii={0};
+ cii.cbSize=sizeof(cii);
+ cii.flags=CLCIIF_GROUPFONT;
+ cii.pszText=TranslateT("** All contacts **");
+ hItemAll=(HANDLE)SendDlgItemMessage(hwndDlg,IDC_LIST,CLM_ADDINFOITEM,0,(LPARAM)&cii);
+
+ SetAllContactIcons(type, GetDlgItem(hwndDlg,IDC_LIST));
+ SetListGroupIcons(GetDlgItem(hwndDlg,IDC_LIST),(HANDLE)SendDlgItemMessage(hwndDlg,IDC_LIST,CLM_GETNEXTITEM,CLGN_ROOT,0),hItemAll,NULL);
+ return TRUE;
+ }
+ case WM_SETFOCUS:
+ SetFocus(GetDlgItem(hwndDlg,IDC_LIST));
+ break;
+ case WM_NOTIFY:
+ switch(((LPNMHDR)lParam)->idFrom) {
+ case IDC_LIST:
+ switch (((LPNMHDR)lParam)->code)
+ {
+ case CLN_NEWCONTACT:
+ case CLN_LISTREBUILT:
+ SetAllContactIcons(type, GetDlgItem(hwndDlg,IDC_LIST));
+ //fall through
+ case CLN_CONTACTMOVED:
+ SetListGroupIcons(GetDlgItem(hwndDlg,IDC_LIST),(HANDLE)SendDlgItemMessage(hwndDlg,IDC_LIST,CLM_GETNEXTITEM,CLGN_ROOT,0),hItemAll,NULL);
+ break;
+ case CLN_OPTIONSCHANGED:
+ ResetListOptions(GetDlgItem(hwndDlg,IDC_LIST));
+ break;
+ case NM_CLICK:
+ { HANDLE hItem;
+ NMCLISTCONTROL *nm=(NMCLISTCONTROL*)lParam;
+ DWORD hitFlags;
+ int iImage;
+ int itemType;
+
+ // Make sure we have an extra column
+ if (nm->iColumn == -1)
+ break;
+
+ // Find clicked item
+ hItem = (HANDLE)SendDlgItemMessage(hwndDlg, IDC_LIST, CLM_HITTEST, (WPARAM)&hitFlags, MAKELPARAM(nm->pt.x,nm->pt.y));
+ // Nothing was clicked
+ if (hItem == NULL) break;
+ // It was not a visbility icon
+ if (!(hitFlags & CLCHT_ONITEMEXTRA)) break;
+
+ // Get image in clicked column
+ iImage = SendDlgItemMessage(hwndDlg, IDC_LIST, CLM_GETEXTRAIMAGE, (WPARAM)hItem, MAKELPARAM(nm->iColumn, 0));
+ if (iImage == 0)
+ iImage = nm->iColumn + 1;
+ else
+ iImage = 0;
+
+ // Get item type (contact, group, etc...)
+ itemType = SendDlgItemMessage(hwndDlg, IDC_LIST, CLM_GETITEMTYPE, (WPARAM)hItem, 0);
+
+ // Update list
+ if (itemType == CLCIT_CONTACT)
+ { // A contact
+ SendDlgItemMessage(hwndDlg, IDC_LIST, CLM_SETEXTRAIMAGE, (WPARAM)hItem, MAKELPARAM(nm->iColumn, iImage));
+ }
+ else if (itemType == CLCIT_INFO)
+ {
+ if (hItem == hItemAll)
+ SetAllChildIcons(GetDlgItem(hwndDlg, IDC_LIST), hItem, nm->iColumn, iImage);
+ }
+ else if (itemType == CLCIT_GROUP)
+ { // A group
+ hItem = (HANDLE)SendDlgItemMessage(hwndDlg, IDC_LIST, CLM_GETNEXTITEM, CLGN_CHILD, (LPARAM)hItem);
+ if (hItem)
+ SetAllChildIcons(GetDlgItem(hwndDlg, IDC_LIST), hItem, nm->iColumn, iImage);
+ }
+ // Update the all/none icons
+ SetListGroupIcons(GetDlgItem(hwndDlg, IDC_LIST), (HANDLE)SendDlgItemMessage(hwndDlg, IDC_LIST, CLM_GETNEXTITEM, CLGN_ROOT, 0), hItemAll, NULL);
+
+ // Activate Apply button
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ break;
+ }
+ }
+ break;
+ case 0:
+ switch (((LPNMHDR)lParam)->code)
+ {
+ case PSN_APPLY:
+ { HANDLE hContact,hItem;
+ int iImage;
+
+ hContact=(HANDLE)CallService(MS_DB_CONTACT_FINDFIRST,0,0);
+ do {
+ hItem=(HANDLE)SendDlgItemMessage(hwndDlg,IDC_LIST,CLM_FINDCONTACT,(WPARAM)hContact,0);
+ if(hItem)
+ {
+ for(int i=0; i < MAX_REGS(data); i++) {
+ iImage=SendDlgItemMessage(hwndDlg,IDC_LIST,CLM_GETEXTRAIMAGE,(WPARAM)hItem,MAKELPARAM(i,0));
+ EnableItem(type, hContact, data[i].item, iImage != 0);
+ }
+ }
+ } while(hContact=(HANDLE)CallService(MS_DB_CONTACT_FINDNEXT,(WPARAM)hContact,0));
+ return TRUE;
+ }
+ }
+ break;
+ }
+ break;
+ case WM_DESTROY:
+ {
+ HIMAGELIST hIml=(HIMAGELIST)SendDlgItemMessage(hwndDlg,IDC_LIST,CLM_GETEXTRAIMAGELIST,0,0);
+ ImageList_Destroy(hIml);
+ }
+ break;
+ }
+ return FALSE;
+
+} \ No newline at end of file
diff --git a/Plugins/historykeeper/options.h b/Plugins/historykeeper/options.h
new file mode 100644
index 0000000..5fb57f6
--- /dev/null
+++ b/Plugins/historykeeper/options.h
@@ -0,0 +1,101 @@
+/*
+Copyright (C) 2006-2009 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#ifndef __OPTIONS_H__
+# define __OPTIONS_H__
+
+
+#include "commons.h"
+
+#include <windows.h>
+
+#define POPUP_ACTION_DONOTHING 0
+#define POPUP_ACTION_CLOSEPOPUP 1
+#define POPUP_ACTION_OPENHISTORY 2
+#define POPUP_ACTION_OPENSRMM 3
+
+#define POPUP_DELAY_DEFAULT 0
+#define POPUP_DELAY_CUSTOM 1
+#define POPUP_DELAY_PERMANENT 2
+
+
+struct Options {
+ BYTE track_only_not_offline;
+ BYTE dont_notify_on_connect;
+ int ttw;
+
+ // File
+ TCHAR file_name[1024];
+ BYTE file_track_changes;
+ BYTE file_track_removes;
+ TCHAR file_template_changed[1024];
+ TCHAR file_template_removed[1024];
+
+ // Speak
+ BYTE speak_track_changes;
+ BYTE speak_track_removes;
+ TCHAR speak_template_changed[1024];
+ TCHAR speak_template_removed[1024];
+
+ // Popup
+ BYTE popup_track_changes;
+ BYTE popup_track_removes;
+ TCHAR popup_template_changed[1024];
+ TCHAR popup_template_removed[1024];
+ WORD popup_delay_type;
+ WORD popup_timeout;
+ BYTE popup_use_win_colors;
+ BYTE popup_use_default_colors;
+ COLORREF popup_bkg_color;
+ COLORREF popup_text_color;
+ WORD popup_left_click_action;
+ WORD popup_right_click_action;
+};
+
+extern Options opts[];
+
+DWORD GetSettingDword(int type, char *setting, DWORD def);
+WORD GetSettingWord(int type, char *setting, WORD def);
+BYTE GetSettingByte(int type, char *setting, BYTE def);
+BOOL GetSettingBool(int type, char *setting, BOOL def);
+void GetSettingTString(int type, char *setting, TCHAR *str, size_t str_size, TCHAR *def);
+
+void WriteSettingDword(int type, char *setting, DWORD val);
+void WriteSettingWord(int type, char *setting, WORD val);
+void WriteSettingByte(int type, char *setting, BYTE val);
+void WriteSettingBool(int type, char *setting, BOOL val);
+void WriteSettingTString(int type, char *setting, TCHAR *str);
+
+
+
+// Initializations needed by options
+void InitOptions();
+
+// Deinitializations needed by options
+void DeInitOptions();
+
+
+// Loads the options from DB
+// It don't need to be called, except in some rare cases
+void LoadOptions();
+
+
+
+#endif // __OPTIONS_H__
diff --git a/Plugins/historykeeper/popup.cpp b/Plugins/historykeeper/popup.cpp
new file mode 100644
index 0000000..cd87dec
--- /dev/null
+++ b/Plugins/historykeeper/popup.cpp
@@ -0,0 +1,376 @@
+/*
+Copyright (C) 2005-2009 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#include "commons.h"
+
+
+
+// Prototypes /////////////////////////////////////////////////////////////////////////////////////
+
+#define WMU_ACTION (WM_USER + 1)
+
+
+LRESULT CALLBACK PopupWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
+
+HWND hPopupWindow = NULL;
+
+
+struct PopupData
+{
+ HANDLE hContact;
+ int type;
+ HICON hIcon;
+
+ PopupData(HANDLE aHContact, int aType)
+ {
+ hContact = aHContact;
+ type = aType;
+ hIcon = NULL;
+ }
+
+ PopupData()
+ {
+ hContact = 0;
+ type = 0;
+ hIcon = NULL;
+ }
+};
+
+static LRESULT CALLBACK PopupDlgProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
+static LRESULT CALLBACK DumbPopupDlgProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
+
+static void ShowPopupEx(HANDLE hContact, const TCHAR *title, const TCHAR *description,
+ PopupData *plugin_data, int type, const Options *op, int typeNum);
+
+
+
+// Functions //////////////////////////////////////////////////////////////////////////////////////
+
+// Initializations needed by popups
+void InitPopups()
+{
+ // window needed for popup commands
+ hPopupWindow = CreateWindowEx(WS_EX_TOOLWINDOW, _T("static"), _T(MODULE_NAME) _T("_PopupWindow"),
+ 0, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, HWND_DESKTOP,
+ NULL, hInst, NULL);
+ SetWindowLong(hPopupWindow, GWL_WNDPROC, (LONG)(WNDPROC)PopupWndProc);
+}
+
+
+// Deinitializations needed by popups
+void DeInitPopups()
+{
+}
+
+
+// Show an error popup
+void ShowErrPopup(int typeNum, const TCHAR *description, const TCHAR *title)
+{
+ ShowPopupEx(NULL, title == NULL ? _T(MODULE_NAME) _T(" Error") : title, description,
+ new PopupData(), POPUP_TYPE_ERROR, NULL, typeNum);
+}
+
+
+void ShowTestPopup(int typeNum, const TCHAR *title, const TCHAR *description, const Options *op)
+{
+ ShowPopupEx(NULL, title, description, new PopupData(), POPUP_TYPE_TEST, op, typeNum);
+}
+
+
+void ShowPopup(HANDLE hContact, int type, int templateNum, TCHAR **vars, int numVars)
+{
+ if (templateNum == 0 && !opts[type].popup_track_changes)
+ return;
+ if (templateNum == 1 && !opts[type].popup_track_removes)
+ return;
+
+ TCHAR *templ = (templateNum == 1 ? opts[type].popup_template_removed : opts[type].popup_template_changed);
+ Buffer<TCHAR> txt;
+ ReplaceTemplate(&txt, hContact, templ, vars, numVars);
+
+ ShowPopupEx(hContact, NULL, txt.str, new PopupData(hContact, type), POPUP_TYPE_NORMAL, &opts[type], type);
+}
+
+
+// Show an popup
+static void ShowPopupEx(HANDLE hContact, const TCHAR *title, const TCHAR *description,
+ PopupData *plugin_data, int type, const Options *op, int typeNum)
+{
+#ifdef UNICODE
+ if(ServiceExists(MS_POPUP_ADDPOPUPW))
+ {
+ // Make popup
+ POPUPDATAW ppd;
+
+ ZeroMemory(&ppd, sizeof(ppd));
+ ppd.lchContact = hContact;
+ plugin_data->hIcon = ppd.lchIcon = HistoryEvents_GetIcon(types[typeNum].eventType);
+
+ if (title != NULL)
+ lstrcpyn(ppd.lpwzContactName, title, MAX_REGS(ppd.lpwzContactName));
+ else if (hContact != NULL)
+ lstrcpyn(ppd.lpwzContactName, (TCHAR *) CallService(MS_CLIST_GETCONTACTDISPLAYNAME, (WPARAM)hContact, GCDNF_TCHAR),
+ MAX_REGS(ppd.lpwzContactName));
+
+ if (description != NULL)
+ lstrcpyn(ppd.lpwzText, description, MAX_REGS(ppd.lpwzText));
+
+ if (type == POPUP_TYPE_NORMAL || type == POPUP_TYPE_TEST)
+ {
+ if (op->popup_use_default_colors)
+ {
+ ppd.colorBack = 0;
+ ppd.colorText = 0;
+ }
+ else if (op->popup_use_win_colors)
+ {
+ ppd.colorBack = GetSysColor(COLOR_BTNFACE);
+ ppd.colorText = GetSysColor(COLOR_WINDOWTEXT);
+ }
+ else
+ {
+ ppd.colorBack = op->popup_bkg_color;
+ ppd.colorText = op->popup_text_color;
+ }
+ }
+ else // if (type == POPUP_TYPE_ERROR)
+ {
+ ppd.colorBack = RGB(200,0,0);
+ ppd.colorText = RGB(255,255,255);
+ }
+
+ if (type == POPUP_TYPE_NORMAL)
+ {
+ ppd.PluginWindowProc = PopupDlgProc;
+ ppd.PluginData = plugin_data;
+ }
+ else // if (type == POPUP_TYPE_TEST || type == POPUP_TYPE_ERROR)
+ {
+ ppd.PluginWindowProc = DumbPopupDlgProc;
+ ppd.PluginData = plugin_data;
+ }
+
+ if (type == POPUP_TYPE_NORMAL || type == POPUP_TYPE_TEST)
+ {
+ switch (op->popup_delay_type)
+ {
+ case POPUP_DELAY_CUSTOM:
+ ppd.iSeconds = op->popup_timeout;
+ break;
+
+ case POPUP_DELAY_PERMANENT:
+ ppd.iSeconds = -1;
+ break;
+
+ //case POPUP_DELAY_DEFAULT:
+ default:
+ ppd.iSeconds = 0;
+ break;
+ }
+ }
+ else // if (type == POPUP_TYPE_ERROR)
+ {
+ ppd.iSeconds = 0;
+ }
+
+ // Now that every field has been filled, we want to see the popup.
+ CallService(MS_POPUP_ADDPOPUPW, (WPARAM)&ppd,0);
+ }
+ else
+#endif
+ if(ServiceExists(MS_POPUP_ADDPOPUPEX))
+ {
+ // Make popup
+ POPUPDATAEX ppd;
+
+ ZeroMemory(&ppd, sizeof(ppd));
+ ppd.lchContact = hContact;
+ plugin_data->hIcon = ppd.lchIcon = HistoryEvents_GetIcon(types[typeNum].eventType);
+
+ if (title != NULL)
+ {
+ char *tmp = mir_t2a(title);
+ lstrcpynA(ppd.lpzContactName, tmp, MAX_REGS(ppd.lpzContactName));
+ mir_free(tmp);
+ }
+ else
+ lstrcpynA(ppd.lpzContactName, (char *) CallService(MS_CLIST_GETCONTACTDISPLAYNAME, (WPARAM)hContact, 0),
+ MAX_REGS(ppd.lpzContactName));
+
+ if (description != NULL)
+ {
+ char *tmp = mir_t2a(description);
+ lstrcpynA(ppd.lpzText, tmp, MAX_REGS(ppd.lpzText));
+ mir_free(tmp);
+ }
+
+ if (type == POPUP_TYPE_NORMAL || type == POPUP_TYPE_TEST)
+ {
+ if (op->popup_use_default_colors)
+ {
+ ppd.colorBack = 0;
+ ppd.colorText = 0;
+ }
+ else if (op->popup_use_win_colors)
+ {
+ ppd.colorBack = GetSysColor(COLOR_BTNFACE);
+ ppd.colorText = GetSysColor(COLOR_WINDOWTEXT);
+ }
+ else
+ {
+ ppd.colorBack = op->popup_bkg_color;
+ ppd.colorText = op->popup_text_color;
+ }
+ }
+ else // if (type == POPUP_TYPE_ERROR)
+ {
+ ppd.colorBack = RGB(200,0,0);
+ ppd.colorText = RGB(255,255,255);
+ }
+
+ if (type == POPUP_TYPE_NORMAL)
+ {
+ ppd.PluginWindowProc = PopupDlgProc;
+ ppd.PluginData = plugin_data;
+ }
+ else // if (type == POPUP_TYPE_TEST || type == POPUP_TYPE_ERROR)
+ {
+ ppd.PluginWindowProc = DumbPopupDlgProc;
+ ppd.PluginData = plugin_data;
+ }
+
+ if (type == POPUP_TYPE_NORMAL || type == POPUP_TYPE_TEST)
+ {
+ switch (op->popup_delay_type)
+ {
+ case POPUP_DELAY_CUSTOM:
+ ppd.iSeconds = op->popup_timeout;
+ break;
+
+ case POPUP_DELAY_PERMANENT:
+ ppd.iSeconds = -1;
+ break;
+
+ //case POPUP_DELAY_DEFAULT:
+ default:
+ ppd.iSeconds = 0;
+ break;
+ }
+ }
+ else // if (type == POPUP_TYPE_ERROR)
+ {
+ ppd.iSeconds = 0;
+ }
+
+ // Now that every field has been filled, we want to see the popup.
+ CallService(MS_POPUP_ADDPOPUPEX, (WPARAM)&ppd,0);
+ }
+}
+
+
+// Handle to the hidden windows to handle actions for popup clicks
+LRESULT CALLBACK PopupWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ if (uMsg == WMU_ACTION)
+ {
+ if (lParam == POPUP_ACTION_OPENHISTORY)
+ {
+ CallService(MS_HISTORY_SHOWCONTACTHISTORY, wParam, 0);
+ }
+ else if (lParam == POPUP_ACTION_OPENSRMM)
+ {
+ CallService(MS_MSG_SENDMESSAGE, wParam, 0);
+ }
+ }
+ return DefWindowProc(hWnd, uMsg, wParam, lParam);
+}
+
+
+// Handle to popup events
+static LRESULT CALLBACK PopupDlgProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ switch(message) {
+ case WM_COMMAND:
+ {
+ PopupData *pd = (PopupData *) PUGetPluginData(hWnd);
+ int action = opts[pd->type].popup_left_click_action;
+
+ SendMessage(hPopupWindow, WMU_ACTION, (WPARAM) pd->hContact, action);
+
+ if (action != POPUP_ACTION_DONOTHING)
+ PUDeletePopUp(hWnd);
+
+ return TRUE;
+ }
+
+ case WM_CONTEXTMENU:
+ {
+ PopupData *pd = (PopupData *) PUGetPluginData(hWnd);
+ int action = opts[pd->type].popup_right_click_action;
+
+ SendMessage(hPopupWindow, WMU_ACTION, (WPARAM) pd->hContact, action);
+
+ if (action != POPUP_ACTION_DONOTHING)
+ PUDeletePopUp(hWnd);
+
+ return TRUE;
+ }
+
+ case UM_FREEPLUGINDATA:
+ {
+ PopupData *pd = (PopupData *) PUGetPluginData(hWnd);
+ HistoryEvents_ReleaseIcon(pd->hIcon);
+ delete pd;
+ return TRUE;
+ }
+ }
+
+ return DefWindowProc(hWnd, message, wParam, lParam);
+}
+
+
+// Handle to popup events
+static LRESULT CALLBACK DumbPopupDlgProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ switch(message) {
+ case WM_COMMAND:
+ {
+ PUDeletePopUp(hWnd);
+ return TRUE;
+ }
+
+ case WM_CONTEXTMENU:
+ {
+ PUDeletePopUp(hWnd);
+ return TRUE;
+ }
+
+ case UM_FREEPLUGINDATA:
+ {
+ PopupData *pd = (PopupData *) PUGetPluginData(hWnd);
+ HistoryEvents_ReleaseIcon(pd->hIcon);
+ delete pd;
+ return TRUE;
+ }
+ }
+
+ return DefWindowProc(hWnd, message, wParam, lParam);
+}
+
diff --git a/Plugins/historykeeper/popup.h b/Plugins/historykeeper/popup.h
new file mode 100644
index 0000000..4fbae22
--- /dev/null
+++ b/Plugins/historykeeper/popup.h
@@ -0,0 +1,47 @@
+/*
+Copyright (C) 2005-2009 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#ifndef __POPUP_H__
+# define __POPUP_H__
+
+#include "commons.h"
+
+// Initializations needed by popups
+void InitPopups();
+
+// Deinitializations needed by popups
+void DeInitPopups();
+
+
+#define POPUP_TYPE_NORMAL 0
+#define POPUP_TYPE_TEST 1
+#define POPUP_TYPE_ERROR 2
+
+void ShowPopup(HANDLE hContact, int typeNum, int templateNum, TCHAR **variables, int numVariables);
+
+// Show an test
+void ShowTestPopup(int typeNum, const TCHAR *title, const TCHAR *description, const Options *op);
+
+// Show an error popup
+void ShowErrPopup(int typeNum, const char *description, const char *title = NULL);
+
+
+
+#endif // __POPUP_H__
diff --git a/Plugins/historykeeper/res/avatar_change.ico b/Plugins/historykeeper/res/avatar_change.ico
new file mode 100644
index 0000000..27e33d1
--- /dev/null
+++ b/Plugins/historykeeper/res/avatar_change.ico
Binary files differ
diff --git a/Plugins/historykeeper/res/client_change.ico b/Plugins/historykeeper/res/client_change.ico
new file mode 100644
index 0000000..1a2efa8
--- /dev/null
+++ b/Plugins/historykeeper/res/client_change.ico
Binary files differ
diff --git a/Plugins/historykeeper/res/fh_client_change.ico b/Plugins/historykeeper/res/fh_client_change.ico
new file mode 100644
index 0000000..ae14e63
--- /dev/null
+++ b/Plugins/historykeeper/res/fh_client_change.ico
Binary files differ
diff --git a/Plugins/historykeeper/res/fh_nick_change.ico b/Plugins/historykeeper/res/fh_nick_change.ico
new file mode 100644
index 0000000..d693e8a
--- /dev/null
+++ b/Plugins/historykeeper/res/fh_nick_change.ico
Binary files differ
diff --git a/Plugins/historykeeper/res/fh_sm_change.ico b/Plugins/historykeeper/res/fh_sm_change.ico
new file mode 100644
index 0000000..3788c7e
--- /dev/null
+++ b/Plugins/historykeeper/res/fh_sm_change.ico
Binary files differ
diff --git a/Plugins/historykeeper/res/fh_status_change.ico b/Plugins/historykeeper/res/fh_status_change.ico
new file mode 100644
index 0000000..c01a5ee
--- /dev/null
+++ b/Plugins/historykeeper/res/fh_status_change.ico
Binary files differ
diff --git a/Plugins/historykeeper/res/fh_xstatus_change.ico b/Plugins/historykeeper/res/fh_xstatus_change.ico
new file mode 100644
index 0000000..7a6ee35
--- /dev/null
+++ b/Plugins/historykeeper/res/fh_xstatus_change.ico
Binary files differ
diff --git a/Plugins/historykeeper/res/file.ico b/Plugins/historykeeper/res/file.ico
new file mode 100644
index 0000000..6dd18fb
--- /dev/null
+++ b/Plugins/historykeeper/res/file.ico
Binary files differ
diff --git a/Plugins/historykeeper/res/history.ico b/Plugins/historykeeper/res/history.ico
new file mode 100644
index 0000000..cb3a93b
--- /dev/null
+++ b/Plugins/historykeeper/res/history.ico
Binary files differ
diff --git a/Plugins/historykeeper/res/idle_change.ico b/Plugins/historykeeper/res/idle_change.ico
new file mode 100644
index 0000000..cbd5a4f
--- /dev/null
+++ b/Plugins/historykeeper/res/idle_change.ico
Binary files differ
diff --git a/Plugins/historykeeper/res/listening_change.ico b/Plugins/historykeeper/res/listening_change.ico
new file mode 100644
index 0000000..e5bade1
--- /dev/null
+++ b/Plugins/historykeeper/res/listening_change.ico
Binary files differ
diff --git a/Plugins/historykeeper/res/nick_change.ico b/Plugins/historykeeper/res/nick_change.ico
new file mode 100644
index 0000000..e39a1c1
--- /dev/null
+++ b/Plugins/historykeeper/res/nick_change.ico
Binary files differ
diff --git a/Plugins/historykeeper/res/orig_client_change.ico b/Plugins/historykeeper/res/orig_client_change.ico
new file mode 100644
index 0000000..6c7b052
--- /dev/null
+++ b/Plugins/historykeeper/res/orig_client_change.ico
Binary files differ
diff --git a/Plugins/historykeeper/res/orig_nick_change.ico b/Plugins/historykeeper/res/orig_nick_change.ico
new file mode 100644
index 0000000..31903c3
--- /dev/null
+++ b/Plugins/historykeeper/res/orig_nick_change.ico
Binary files differ
diff --git a/Plugins/historykeeper/res/orig_sm_change.ico b/Plugins/historykeeper/res/orig_sm_change.ico
new file mode 100644
index 0000000..89bbaa5
--- /dev/null
+++ b/Plugins/historykeeper/res/orig_sm_change.ico
Binary files differ
diff --git a/Plugins/historykeeper/res/orig_status_change.ico b/Plugins/historykeeper/res/orig_status_change.ico
new file mode 100644
index 0000000..4927d8b
--- /dev/null
+++ b/Plugins/historykeeper/res/orig_status_change.ico
Binary files differ
diff --git a/Plugins/historykeeper/res/orig_xstatus_change.ico b/Plugins/historykeeper/res/orig_xstatus_change.ico
new file mode 100644
index 0000000..7cfe719
--- /dev/null
+++ b/Plugins/historykeeper/res/orig_xstatus_change.ico
Binary files differ
diff --git a/Plugins/historykeeper/res/overlay.ico b/Plugins/historykeeper/res/overlay.ico
new file mode 100644
index 0000000..04e9acd
--- /dev/null
+++ b/Plugins/historykeeper/res/overlay.ico
Binary files differ
diff --git a/Plugins/historykeeper/res/popup.ico b/Plugins/historykeeper/res/popup.ico
new file mode 100644
index 0000000..fa75448
--- /dev/null
+++ b/Plugins/historykeeper/res/popup.ico
Binary files differ
diff --git a/Plugins/historykeeper/res/sm_change.ico b/Plugins/historykeeper/res/sm_change.ico
new file mode 100644
index 0000000..ad86912
--- /dev/null
+++ b/Plugins/historykeeper/res/sm_change.ico
Binary files differ
diff --git a/Plugins/historykeeper/res/smalldot.ico b/Plugins/historykeeper/res/smalldot.ico
new file mode 100644
index 0000000..47f0556
--- /dev/null
+++ b/Plugins/historykeeper/res/smalldot.ico
Binary files differ
diff --git a/Plugins/historykeeper/res/sound.ico b/Plugins/historykeeper/res/sound.ico
new file mode 100644
index 0000000..e4328ab
--- /dev/null
+++ b/Plugins/historykeeper/res/sound.ico
Binary files differ
diff --git a/Plugins/historykeeper/res/speak.ico b/Plugins/historykeeper/res/speak.ico
new file mode 100644
index 0000000..42bee21
--- /dev/null
+++ b/Plugins/historykeeper/res/speak.ico
Binary files differ
diff --git a/Plugins/historykeeper/res/status_change.ico b/Plugins/historykeeper/res/status_change.ico
new file mode 100644
index 0000000..7e62858
--- /dev/null
+++ b/Plugins/historykeeper/res/status_change.ico
Binary files differ
diff --git a/Plugins/historykeeper/res/xsm_change.ico b/Plugins/historykeeper/res/xsm_change.ico
new file mode 100644
index 0000000..7b76b1d
--- /dev/null
+++ b/Plugins/historykeeper/res/xsm_change.ico
Binary files differ
diff --git a/Plugins/historykeeper/res/xstatus_change.ico b/Plugins/historykeeper/res/xstatus_change.ico
new file mode 100644
index 0000000..5efe093
--- /dev/null
+++ b/Plugins/historykeeper/res/xstatus_change.ico
Binary files differ
diff --git a/Plugins/historykeeper/resource.h b/Plugins/historykeeper/resource.h
new file mode 100644
index 0000000..484b1af
--- /dev/null
+++ b/Plugins/historykeeper/resource.h
@@ -0,0 +1,91 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Developer Studio generated include file.
+// Used by resource.rc
+//
+#define IDD_OPTIONS 119
+#define IDD_OPTIONS_NOREM 119
+#define IDD_POPUPS 120
+#define IDD_POPUPS_REM 120
+#define IDD_OPTIONS_REM 121
+#define IDD_POPUPS_NOREM 122
+#define IDI_NICK 123
+#define IDD_SPEAK_REM 123
+#define IDI_SMH 124
+#define IDD_SPEAK_NOREM 124
+#define IDI_STATUS 125
+#define IDI_CLIENT 126
+#define IDI_XSTATUS 127
+#define IDI_SPEAK 129
+#define IDI_FILE 130
+#define IDI_HISTORY 131
+#define IDI_POPUP 132
+#define IDI_SOUND 133
+#define IDI_SMALLDOT 134
+#define IDI_LISTENING 135
+#define IDI_XSM 136
+#define IDI_IDLE 138
+#define IDD_NOTIFICATIONS 215
+#define IDC_DELAY 1001
+#define IDC_WINCOLORS 1002
+#define IDC_DEFAULTCOLORS 1003
+#define IDC_BGCOLOR 1004
+#define IDC_TEXTCOLOR 1005
+#define IDC_PREV 1006
+#define IDC_DELAYFROMPU 1007
+#define IDC_DELAYCUSTOM 1008
+#define IDC_DELAYPERMANENT 1009
+#define IDC_RIGHT_ACTION 1022
+#define IDC_LEFT_ACTION 1023
+#define IDC_PROTOCOLS 1041
+#define IDC_CHANGED 1058
+#define IDC_REMOVED 1059
+#define IDC_CHECK1 1060
+#define IDC_POPUPS 1060
+#define IDC_FILENAME 1060
+#define IDC_CHECK2 1061
+#define IDC_DELAY_SPIN 1061
+#define IDC_HISTORY 1061
+#define IDC_ANSI 1062
+#define IDC_TRACK_G 1063
+#define IDC_CHANGED_L 1064
+#define IDC_FILE_G 1064
+#define IDC_REMOVED_L 1065
+#define IDC_CHANGED_L2 1065
+#define IDC_PROTOCOLS_G 1066
+#define IDC_PROTOCOLS_L 1067
+#define IDC_COLOURS_G 1068
+#define IDC_REMOVED_L2 1068
+#define IDC_BGCOLOR_L 1069
+#define IDC_FILENAME_L 1069
+#define IDC_TEXTCOLOR_L 1070
+#define IDC_ONLY_NOT_OFFLINE 1070
+#define IDC_WAIT_L 1070
+#define IDC_DELAY_G 1071
+#define IDC_ACTIONS_G 1072
+#define IDC_RIGHT_ACTION_L 1073
+#define IDC_LEFT_ACTION_L 1074
+#define IDC_TRACK_CHANGE 1075
+#define IDC_TRACK_REMOVE 1076
+#define IDC_SELECT_FILE 1077
+#define IDC_DONT_NOTIFY_ON_CONNECT 1078
+#define IDC_LIST 1079
+#define IDC_NWAIT 1080
+#define IDC_NWAIT_SPIN 1081
+#define IDC_POPUP 1204
+#define IDC_SOUND 1205
+#define IDC_FILE 1207
+#define IDC_SPEAK 1208
+#define IDC_STATIC -1
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NO_MFC 1
+#define _APS_3D_CONTROLS 1
+#define _APS_NEXT_RESOURCE_VALUE 139
+#define _APS_NEXT_COMMAND_VALUE 40004
+#define _APS_NEXT_CONTROL_VALUE 1082
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/Plugins/historykeeper/resource.rc b/Plugins/historykeeper/resource.rc
new file mode 100644
index 0000000..b8fc543
--- /dev/null
+++ b/Plugins/historykeeper/resource.rc
@@ -0,0 +1,384 @@
+//Microsoft Developer Studio generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "resource.h"
+#include "winresrc.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// Neutral resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_NEU)
+#ifdef _WIN32
+LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
+#pragma code_page(1252)
+#endif //_WIN32
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Icon
+//
+
+// Icon with lowest ID value placed first to ensure application icon
+// remains consistent on all systems.
+IDI_NICK ICON DISCARDABLE "res\\nick_change.ico"
+IDI_SMH ICON DISCARDABLE "res\\sm_change.ico"
+IDI_STATUS ICON DISCARDABLE "res\\status_change.ico"
+IDI_CLIENT ICON DISCARDABLE "res\\client_change.ico"
+IDI_XSTATUS ICON DISCARDABLE "res\\xstatus_change.ico"
+IDI_SPEAK ICON DISCARDABLE "res\\speak.ico"
+IDI_FILE ICON DISCARDABLE "res\\file.ico"
+IDI_HISTORY ICON DISCARDABLE "res\\history.ico"
+IDI_POPUP ICON DISCARDABLE "res\\popup.ico"
+IDI_SOUND ICON DISCARDABLE "res\\sound.ico"
+IDI_SMALLDOT ICON DISCARDABLE "res\\smalldot.ico"
+IDI_LISTENING ICON DISCARDABLE "res\\listening_change.ico"
+IDI_XSM ICON DISCARDABLE "res\\xsm_change.ico"
+IDI_IDLE ICON DISCARDABLE "res\\idle_change.ico"
+#endif // Neutral resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+/////////////////////////////////////////////////////////////////////////////
+// English (U.S.) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page(1252)
+#endif //_WIN32
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Dialog
+//
+
+IDD_POPUPS_REM DIALOGEX 0, 0, 314, 240
+STYLE DS_FIXEDSYS | WS_CHILD | WS_VISIBLE
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg", 0, 0, 0x1
+BEGIN
+ GROUPBOX " Tracking ",IDC_TRACK_G,3,3,308,73
+ CONTROL "Show when contacts change their %s",IDC_TRACK_CHANGE,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,15,291,11
+ RTEXT "Template:",IDC_CHANGED_L,11,28,55,10
+ EDITTEXT IDC_CHANGED,72,27,233,13,ES_AUTOHSCROLL
+ CONTROL "Show when contacts remove their %s",IDC_TRACK_REMOVE,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,44,291,11
+ RTEXT "Template:",IDC_REMOVED_L,11,58,55,10
+ EDITTEXT IDC_REMOVED,72,57,233,13,ES_AUTOHSCROLL
+ GROUPBOX " Colours ",IDC_COLOURS_G,3,80,158,75
+ CONTROL "",IDC_BGCOLOR,"ColourPicker",WS_TABSTOP,11,92,35,14
+ LTEXT "Background colour",IDC_BGCOLOR_L,55,96,66,8
+ CONTROL "",IDC_TEXTCOLOR,"ColourPicker",WS_TABSTOP,11,110,35,14
+ LTEXT "Text colour",IDC_TEXTCOLOR_L,55,114,66,8
+ CONTROL "Use Windows colours",IDC_WINCOLORS,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,11,129,108,10
+ CONTROL "Use default colours",IDC_DEFAULTCOLORS,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,11,139,107,10
+ GROUPBOX " Delay ",IDC_DELAY_G,166,80,145,75
+ CONTROL "From popup plugin",IDC_DELAYFROMPU,"Button",
+ BS_AUTORADIOBUTTON | WS_GROUP,175,93,122,10
+ CONTROL "Custom",IDC_DELAYCUSTOM,"Button",BS_AUTORADIOBUTTON,175,
+ 107,54,10
+ CONTROL "Permanent",IDC_DELAYPERMANENT,"Button",
+ BS_AUTORADIOBUTTON,175,121,122,10
+ EDITTEXT IDC_DELAY,233,105,31,14,ES_AUTOHSCROLL | ES_NUMBER
+ CONTROL "Spin1",IDC_DELAY_SPIN,"msctls_updown32",UDS_SETBUDDYINT |
+ UDS_ALIGNRIGHT | UDS_ARROWKEYS | UDS_NOTHOUSANDS |
+ UDS_HOTTRACK,268,106,11,11
+ GROUPBOX " Actions ",IDC_ACTIONS_G,3,158,308,46
+ RTEXT "On right click:",IDC_RIGHT_ACTION_L,13,172,62,9
+ COMBOBOX IDC_RIGHT_ACTION,83,170,156,60,CBS_DROPDOWNLIST |
+ WS_VSCROLL | WS_TABSTOP
+ RTEXT "On left click:",IDC_LEFT_ACTION_L,13,186,62,9
+ COMBOBOX IDC_LEFT_ACTION,83,186,156,60,CBS_DROPDOWNLIST |
+ WS_VSCROLL | WS_TABSTOP
+ PUSHBUTTON "Preview",IDC_PREV,131,223,50,14
+END
+
+IDD_OPTIONS_NOREM DIALOGEX 0, 0, 285, 187
+STYLE DS_FIXEDSYS | WS_CHILD | WS_VISIBLE
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg"
+BEGIN
+ GROUPBOX " Track ",IDC_STATIC,3,3,279,46
+ LTEXT "Wait",IDC_WAIT_L,12,17,30,10
+ EDITTEXT IDC_NWAIT,42,15,36,13,ES_AUTOHSCROLL | ES_NUMBER
+ CONTROL "Spin1",IDC_NWAIT_SPIN,"msctls_updown32",UDS_SETBUDDYINT |
+ UDS_ALIGNRIGHT | UDS_ARROWKEYS | UDS_NOTHOUSANDS |
+ UDS_HOTTRACK,59,18,10,14
+ LTEXT "s before notifying (to avoid duplications)",IDC_STATIC,
+ 83,17,190,10
+ CONTROL "Don't notify on protocol connection",
+ IDC_DONT_NOTIFY_ON_CONNECT,"Button",BS_AUTOCHECKBOX |
+ WS_TABSTOP,12,32,264,11
+ GROUPBOX " Protocols ",IDC_PROTOCOLS_G,3,52,279,64
+ LTEXT "Enable tracking for these protocols:",IDC_PROTOCOLS_L,
+ 14,65,158,11
+ CONTROL "List1",IDC_PROTOCOLS,"SysListView32",LVS_REPORT |
+ LVS_SINGLESEL | LVS_SORTASCENDING | LVS_NOCOLUMNHEADER |
+ LVS_NOSORTHEADER | WS_BORDER | WS_TABSTOP,13,77,158,33
+ GROUPBOX " Log to file ",IDC_FILE_G,3,120,279,65
+ RTEXT "Filename:",IDC_FILENAME_L,12,136,41,10
+ EDITTEXT IDC_FILENAME,60,135,198,13,ES_AUTOHSCROLL
+ PUSHBUTTON "...",IDC_SELECT_FILE,261,135,14,13
+ CONTROL "Log when contacts change their %s",IDC_TRACK_CHANGE,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,153,264,11
+ RTEXT "Template:",IDC_CHANGED_L2,11,166,55,10
+ EDITTEXT IDC_CHANGED,72,165,203,13,ES_AUTOHSCROLL
+END
+
+IDD_POPUPS_NOREM DIALOGEX 0, 0, 314, 240
+STYLE DS_FIXEDSYS | WS_CHILD | WS_VISIBLE
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg", 0, 0, 0x1
+BEGIN
+ GROUPBOX " Tracking ",IDC_TRACK_G,3,3,308,43
+ CONTROL "Show when contacts change their %s",IDC_TRACK_CHANGE,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,15,291,11
+ RTEXT "Template:",IDC_CHANGED_L,11,28,55,10
+ EDITTEXT IDC_CHANGED,72,27,233,13,ES_AUTOHSCROLL
+ GROUPBOX " Colours ",IDC_COLOURS_G,3,50,158,75
+ CONTROL "",IDC_BGCOLOR,"ColourPicker",WS_TABSTOP,11,62,35,14
+ LTEXT "Background colour",IDC_BGCOLOR_L,55,66,66,8
+ CONTROL "",IDC_TEXTCOLOR,"ColourPicker",WS_TABSTOP,11,80,35,14
+ LTEXT "Text colour",IDC_TEXTCOLOR_L,55,84,66,8
+ CONTROL "Use Windows colours",IDC_WINCOLORS,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,11,99,108,10
+ CONTROL "Use default colours",IDC_DEFAULTCOLORS,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,11,109,107,10
+ GROUPBOX " Delay ",IDC_DELAY_G,166,50,145,75
+ CONTROL "From popup plugin",IDC_DELAYFROMPU,"Button",
+ BS_AUTORADIOBUTTON | WS_GROUP,175,63,122,10
+ CONTROL "Custom",IDC_DELAYCUSTOM,"Button",BS_AUTORADIOBUTTON,175,
+ 77,54,10
+ CONTROL "Permanent",IDC_DELAYPERMANENT,"Button",
+ BS_AUTORADIOBUTTON,175,91,122,10
+ EDITTEXT IDC_DELAY,233,75,31,14,ES_AUTOHSCROLL | ES_NUMBER
+ CONTROL "Spin1",IDC_DELAY_SPIN,"msctls_updown32",UDS_SETBUDDYINT |
+ UDS_ALIGNRIGHT | UDS_ARROWKEYS | UDS_NOTHOUSANDS |
+ UDS_HOTTRACK,268,76,11,11
+ GROUPBOX " Actions ",IDC_ACTIONS_G,3,128,308,46
+ RTEXT "On right click:",IDC_RIGHT_ACTION_L,13,142,62,9
+ COMBOBOX IDC_RIGHT_ACTION,83,140,156,60,CBS_DROPDOWNLIST |
+ WS_VSCROLL | WS_TABSTOP
+ RTEXT "On left click:",IDC_LEFT_ACTION_L,13,156,62,9
+ COMBOBOX IDC_LEFT_ACTION,83,156,156,60,CBS_DROPDOWNLIST |
+ WS_VSCROLL | WS_TABSTOP
+ PUSHBUTTON "Preview",IDC_PREV,131,223,50,14
+END
+
+IDD_SPEAK_REM DIALOGEX 0, 0, 279, 68
+STYLE DS_FIXEDSYS | WS_CHILD | WS_VISIBLE
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg", 0, 0, 0x1
+BEGIN
+ CONTROL "Announce when contacts change their %s",
+ IDC_TRACK_CHANGE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,
+ 12,3,264,11
+ RTEXT "Template:",IDC_CHANGED_L2,11,16,55,10
+ EDITTEXT IDC_CHANGED,72,15,203,13,ES_AUTOHSCROLL
+ CONTROL "Announce when contacts remove their %s",
+ IDC_TRACK_REMOVE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,
+ 12,32,263,11
+ RTEXT "Template:",IDC_REMOVED_L2,11,46,55,10
+ EDITTEXT IDC_REMOVED,72,45,203,13,ES_AUTOHSCROLL
+END
+
+IDD_SPEAK_NOREM DIALOGEX 0, 0, 279, 68
+STYLE DS_FIXEDSYS | WS_CHILD | WS_VISIBLE
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg", 0, 0, 0x1
+BEGIN
+ CONTROL "Announce when contacts change their %s",
+ IDC_TRACK_CHANGE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,
+ 12,3,264,11
+ RTEXT "Template:",IDC_CHANGED_L2,11,16,55,10
+ EDITTEXT IDC_CHANGED,72,15,203,13,ES_AUTOHSCROLL
+END
+
+IDD_OPTIONS_REM DIALOGEX 0, 0, 285, 218
+STYLE DS_FIXEDSYS | WS_CHILD | WS_VISIBLE
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg"
+BEGIN
+ GROUPBOX " Track ",IDC_STATIC,3,3,279,46
+ LTEXT "Wait",IDC_WAIT_L,12,17,30,10
+ EDITTEXT IDC_NWAIT,42,15,36,13,ES_AUTOHSCROLL | ES_NUMBER
+ CONTROL "Spin1",IDC_NWAIT_SPIN,"msctls_updown32",UDS_SETBUDDYINT |
+ UDS_ALIGNRIGHT | UDS_ARROWKEYS | UDS_NOTHOUSANDS |
+ UDS_HOTTRACK,59,18,10,14
+ LTEXT "s before notifying (to avoid duplications)",IDC_STATIC,
+ 83,17,190,10
+ CONTROL "Don't notify on protocol connection",
+ IDC_DONT_NOTIFY_ON_CONNECT,"Button",BS_AUTOCHECKBOX |
+ WS_TABSTOP,12,32,264,11
+ GROUPBOX " Protocols ",IDC_PROTOCOLS_G,3,52,279,64
+ LTEXT "Enable tracking for these protocols:",IDC_PROTOCOLS_L,
+ 14,65,158,11
+ CONTROL "List1",IDC_PROTOCOLS,"SysListView32",LVS_REPORT |
+ LVS_SINGLESEL | LVS_SORTASCENDING | LVS_NOCOLUMNHEADER |
+ LVS_NOSORTHEADER | WS_BORDER | WS_TABSTOP,13,77,158,33
+ GROUPBOX " Log to file ",IDC_FILE_G,3,120,279,96
+ RTEXT "Filename:",IDC_FILENAME_L,12,136,41,10
+ EDITTEXT IDC_FILENAME,60,135,198,13,ES_AUTOHSCROLL
+ PUSHBUTTON "...",IDC_SELECT_FILE,261,135,14,13
+ CONTROL "Log when contacts change their %s",IDC_TRACK_CHANGE,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,153,264,11
+ RTEXT "Template:",IDC_CHANGED_L2,11,166,55,10
+ EDITTEXT IDC_CHANGED,72,165,203,13,ES_AUTOHSCROLL
+ CONTROL "Log when contacts remove their %s",IDC_TRACK_REMOVE,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,182,263,11
+ RTEXT "Template:",IDC_REMOVED_L2,11,196,55,10
+ EDITTEXT IDC_REMOVED,72,195,203,13,ES_AUTOHSCROLL
+END
+
+IDD_NOTIFICATIONS DIALOGEX 0, 0, 301, 229
+STYLE DS_FIXEDSYS | WS_CHILD | WS_VISIBLE
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg"
+BEGIN
+ CONTROL "",IDC_LIST,"CListControl",WS_TABSTOP | 0x1f0,1,8,297,
+ 143,WS_EX_CLIENTEDGE
+ ICON 108,IDC_HISTORY,1,161,20,20
+ LTEXT "Log to History",IDC_STATIC,19,161,279,8,SS_NOPREFIX |
+ SS_CENTERIMAGE
+ ICON 110,IDC_FILE,1,173,20,20
+ LTEXT "Log to File",IDC_STATIC,19,173,279,8,SS_NOPREFIX |
+ SS_CENTERIMAGE
+ ICON 108,IDC_POPUP,1,185,20,20
+ LTEXT "Notify using Popups",IDC_STATIC,19,185,279,8,
+ SS_NOPREFIX | SS_CENTERIMAGE
+ ICON 110,IDC_SOUND,1,197,20,20
+ LTEXT "Notify using Sounds",IDC_STATIC,19,197,279,8,
+ SS_NOPREFIX | SS_CENTERIMAGE
+ ICON 108,IDC_SPEAK,1,209,20,20
+ LTEXT "Notify using Speak plugin",IDC_STATIC,19,209,279,8,
+ SS_NOPREFIX | SS_CENTERIMAGE
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// DESIGNINFO
+//
+
+#ifdef APSTUDIO_INVOKED
+GUIDELINES DESIGNINFO DISCARDABLE
+BEGIN
+ IDD_POPUPS_REM, DIALOG
+ BEGIN
+ LEFTMARGIN, 3
+ RIGHTMARGIN, 311
+ TOPMARGIN, 3
+ BOTTOMMARGIN, 237
+ END
+
+ IDD_OPTIONS_NOREM, DIALOG
+ BEGIN
+ LEFTMARGIN, 3
+ RIGHTMARGIN, 282
+ TOPMARGIN, 3
+ END
+
+ IDD_POPUPS_NOREM, DIALOG
+ BEGIN
+ LEFTMARGIN, 3
+ RIGHTMARGIN, 311
+ TOPMARGIN, 3
+ BOTTOMMARGIN, 237
+ END
+
+ IDD_SPEAK_REM, DIALOG
+ BEGIN
+ LEFTMARGIN, 3
+ RIGHTMARGIN, 276
+ TOPMARGIN, 3
+ BOTTOMMARGIN, 65
+ END
+
+ IDD_SPEAK_NOREM, DIALOG
+ BEGIN
+ LEFTMARGIN, 3
+ RIGHTMARGIN, 276
+ TOPMARGIN, 3
+ BOTTOMMARGIN, 65
+ END
+
+ IDD_OPTIONS_REM, DIALOG
+ BEGIN
+ LEFTMARGIN, 3
+ RIGHTMARGIN, 282
+ TOPMARGIN, 3
+ END
+
+ IDD_NOTIFICATIONS, DIALOG
+ BEGIN
+ BOTTOMMARGIN, 224
+ END
+END
+#endif // APSTUDIO_INVOKED
+
+#endif // English (U.S.) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+/////////////////////////////////////////////////////////////////////////////
+// English (Canada) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENC)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_CAN
+#pragma code_page(1252)
+#endif //_WIN32
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "resource.h\0"
+END
+
+2 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "#include ""resource.h""\r\n"
+ "#include ""winresrc.h""\r\n"
+ "\0"
+END
+
+3 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "\r\n"
+ "\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+#endif // English (Canada) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
+
diff --git a/Plugins/historykeeper/sdk/m_metacontacts.h b/Plugins/historykeeper/sdk/m_metacontacts.h
new file mode 100644
index 0000000..1da12b9
--- /dev/null
+++ b/Plugins/historykeeper/sdk/m_metacontacts.h
@@ -0,0 +1,162 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright © 2004 Universite Louis PASTEUR, STRASBOURG.
+Copyright © 2004 Scott Ellis (www.scottellis.com.au mail@scottellis.com.au)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef M_METACONTACTS_H__
+#define M_METACONTACTS_H__ 1
+
+//get the handle for a contact's parent metacontact
+//wParam=(HANDLE)hSubContact
+//lParam=0
+//returns a handle to the parent metacontact, or null if this contact is not a subcontact
+#define MS_MC_GETMETACONTACT "MetaContacts/GetMeta"
+
+//gets the handle for the default contact
+//wParam=(HANDLE)hMetaContact
+//lParam=0
+//returns a handle to the default contact, or null on failure
+#define MS_MC_GETDEFAULTCONTACT "MetaContacts/GetDefault"
+
+//gets the contact number for the default contact
+//wParam=(HANDLE)hMetaContact
+//lParam=0
+//returns a DWORD contact number, or -1 on failure
+#define MS_MC_GETDEFAULTCONTACTNUM "MetaContacts/GetDefaultNum"
+
+//gets the handle for the 'most online' contact
+//wParam=(HANDLE)hMetaContact
+//lParam=0
+//returns a handle to the 'most online' contact
+#define MS_MC_GETMOSTONLINECONTACT "MetaContacts/GetMostOnline"
+
+//gets the number of subcontacts for a metacontact
+//wParam=(HANDLE)hMetaContact
+//lParam=0
+//returns a DWORD representing the number of subcontacts for the given metacontact
+#define MS_MC_GETNUMCONTACTS "MetaContacts/GetNumContacts"
+
+//gets the handle of a subcontact, using the subcontact's number
+//wParam=(HANDLE)hMetaContact
+//lParam=(DWORD)contact number
+//returns a handle to the specified subcontact
+#define MS_MC_GETSUBCONTACT "MetaContacts/GetSubContact"
+
+//sets the default contact, using the subcontact's contact number
+//wParam=(HANDLE)hMetaContact
+//lParam=(DWORD)contact number
+//returns 0 on success
+#define MS_MC_SETDEFAULTCONTACTNUM "MetaContacts/SetDefault"
+
+//sets the default contact, using the subcontact's handle
+//wParam=(HANDLE)hMetaContact
+//lParam=(HANDLE)hSubcontact
+//returns 0 on success
+#define MS_MC_SETDEFAULTCONTACT "MetaContacts/SetDefaultByHandle"
+
+//forces the metacontact to send using a specific subcontact, using the subcontact's contact number
+//wParam=(HANDLE)hMetaContact
+//lParam=(DWORD)contact number
+//returns 0 on success
+#define MS_MC_FORCESENDCONTACTNUM "MetaContacts/ForceSendContact"
+
+//forces the metacontact to send using a specific subcontact, using the subcontact's handle
+//wParam=(HANDLE)hMetaContact
+//lParam=(HANDLE)hSubcontact
+//returns 0 on success (will fail if 'force default' is in effect)
+#define MS_MC_FORCESENDCONTACT "MetaContacts/ForceSendContactByHandle"
+
+//'unforces' the metacontact to send using a specific subcontact
+//wParam=(HANDLE)hMetaContact
+//lParam=0
+//returns 0 on success (will fail if 'force default' is in effect)
+#define MS_MC_UNFORCESENDCONTACT "MetaContacts/UnforceSendContact"
+
+//'forces' or 'unforces' (i.e. toggles) the metacontact to send using it's default contact
+// overrides (and clears) 'force send' above, and will even force use of offline contacts
+// will send ME_MC_FORCESEND or ME_MC_UNFORCESEND event
+//wParam=(HANDLE)hMetaContact
+//lParam=0
+//returns 1(true) or 0(false) representing new state of 'force default'
+#define MS_MC_FORCEDEFAULT "MetaContacts/ForceSendDefault"
+
+// method to get state of 'force' for a metacontact
+// wParam=(HANDLE)hMetaContact
+// lParam= (DWORD)&contact_number or NULL
+//
+// if lparam supplied, the contact_number of the contatct 'in force' will be copied to the address it points to,
+// or if none is in force, the value (DWORD)-1 will be copied
+// (v0.8.0.8+ returns 1 if 'force default' is true with *lParam == default contact number, else returns 0 with *lParam as above)
+#define MS_MC_GETFORCESTATE "MetaContacts/GetForceState"
+
+// fired when a metacontact's default contact changes (fired upon creation of metacontact also, when default is initially set)
+// wParam=(HANDLE)hMetaContact
+// lParam=(HANDLE)hDefaultContact
+#define ME_MC_DEFAULTTCHANGED "MetaContacts/DefaultChanged"
+
+// fired when a metacontact's subcontacts change (fired upon creation of metacontact, when contacts are added or removed, and when
+// contacts are reordered) - a signal to re-read metacontact data
+// wParam=(HANDLE)hMetaContact
+// lParam=0
+#define ME_MC_SUBCONTACTSCHANGED "MetaContacts/SubcontactsChanged"
+
+// fired when a metacontact is forced to send using a specific subcontact
+// wParam=(HANDLE)hMetaContact
+// lParam=(HANDLE)hForceContact
+#define ME_MC_FORCESEND "MetaContacts/ForceSend"
+
+// fired when a metacontact is 'unforced' to send using a specific subcontact
+// wParam=(HANDLE)hMetaContact
+// lParam=0
+#define ME_MC_UNFORCESEND "MetaContacts/UnforceSend"
+
+// method to get protocol name - used to be sure you're dealing with a "real" metacontacts plugin :)
+// wParam=lParam=0
+#define MS_MC_GETPROTOCOLNAME "MetaContacts/GetProtoName"
+
+
+// added 0.9.5.0 (22/3/05)
+// wParam=(HANDLE)hContact
+// lParam=0
+// convert a given contact into a metacontact
+#define MS_MC_CONVERTTOMETA "MetaContacts/ConvertToMetacontact"
+
+// added 0.9.5.0 (22/3/05)
+// wParam=(HANDLE)hContact
+// lParam=(HANDLE)hMeta
+// add an existing contact to a metacontact
+#define MS_MC_ADDTOMETA "MetaContacts/AddToMetacontact"
+
+// added 0.9.5.0 (22/3/05)
+// wParam=0
+// lParam=(HANDLE)hContact
+// remove a contact from a metacontact
+#define MS_MC_REMOVEFROMMETA "MetaContacts/RemoveFromMetacontact"
+
+
+// added 0.9.13.2 (6/10/05)
+// wParam=(BOOL)disable
+// lParam=0
+// enable/disable the 'hidden group hack' - for clists that support subcontact hiding using 'IsSubcontact' setting
+// should be called once in the clist 'onmodulesloaded' event handler (which, since it's loaded after the db, will be called
+// before the metacontact onmodulesloaded handler where the subcontact hiding is usually done)
+#define MS_MC_DISABLEHIDDENGROUP "MetaContacts/DisableHiddenGroup"
+
+#endif
diff --git a/Plugins/historykeeper/sdk/m_proto_listeningto.h b/Plugins/historykeeper/sdk/m_proto_listeningto.h
new file mode 100644
index 0000000..66d3b77
--- /dev/null
+++ b/Plugins/historykeeper/sdk/m_proto_listeningto.h
@@ -0,0 +1,143 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2006 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+//this module was created in v0.6.0.0
+
+#ifndef M_PROTO_LISTENINGTO_H__
+#define M_PROTO_LISTENINGTO_H__ 1
+
+
+// Protocol Services /////////////////////////////////////////////////////////////////
+
+// This is the services a protocol have to support to support listening info
+
+typedef struct {
+ int cbSize;
+
+ union {
+ char* pszType; // Media type: Music, Video, etc...
+ TCHAR* ptszType;
+ };
+ union {
+ char* pszArtist; // Artist name
+ TCHAR* ptszArtist;
+ };
+ union {
+ char* pszAlbum; // Album name
+ TCHAR* ptszAlbum;
+ };
+ union {
+ char* pszTitle; // Song name
+ TCHAR* ptszTitle;
+ };
+ union {
+ char* pszTrack; // Track number
+ TCHAR* ptszTrack;
+ };
+ union {
+ char* pszYear; // Song year
+ TCHAR* ptszYear;
+ };
+ union {
+ char* pszGenre; // Song genre
+ TCHAR* ptszGenre;
+ };
+ union {
+ char* pszLength; // Song length
+ TCHAR* ptszLength;
+ };
+ union {
+ char* pszPlayer; // Player name
+ TCHAR* ptszPlayer;
+ };
+
+ DWORD dwFlags;
+
+} LISTENINGTOINFO;
+
+#define LTI_UNICODE 1
+
+#ifdef UNICODE
+ #define LTI_TCHAR LTI_UNICODE
+#else
+ #define LTI_TCHAR 0
+#endif
+
+// Set the listening info for the protocol.
+// Pass NULL to remove it.
+// wParam = NULL
+// lParam = LISTENINGTOINFO *
+#define PS_SET_LISTENINGTO "/SetListeningTo"
+
+// Get the listening info for the protocol
+// wParam = NULL
+// lParam = LISTENINGTOINFO *
+// The strings inside the struct need to be free using miranda free.
+#define PS_GET_LISTENINGTO "/GetListeningTo"
+
+// Also the protocol have to save a string with the text the other user is (probabily)
+// seeing under the main db key: <protocol>/ListeningTo
+
+// For a contact, the protocol should store the listening info as an string inside
+// the contact db key: <protocol>/ListeningTo
+
+
+// ListeningTo configuration plugin //////////////////////////////////////////////////
+
+// One plugin can be used to set some options relative to the listening to information.
+// But protocols should not assume this plugin exists. If it does not exist, protocols
+// have to decide what default to use.
+// This plugin have to support the following services:
+
+// Get the text format the user wants him / his contacts to see. Some strings represents
+// the text information:
+// %artist%, %album%, %title%, %track%, %year%, %genre%, %length%, %player%, %type%
+// This service is optional
+// wParam = TCHAR* - default text for this protocol
+// lParam = 0
+// Returns a TCHAR* containg the user setting. This need to be free using miranda free.
+#define MS_LISTENINGTO_GETTEXTFORMAT "ListeningTo/GetTextFormat"
+
+// Get the text the user wants him / his contacts to see, parsed with the info sent to
+// this service. Uses the same variables as the above service to the default text.
+// wParam = TCHAR* - default text for this protocol
+// lParam = LISTENINGTOINFO *
+// Returns a TCHAR* containg the parsed text. This need to be free using miranda free.
+#define MS_LISTENINGTO_GETPARSEDTEXT "ListeningTo/GetParsedText"
+
+// Get if the contact options about how to show the music info should be overriten or
+// not.
+// wParam = NULL
+// lParam = hContact
+// Returns a BOOL
+#define MS_LISTENINGTO_OVERRIDECONTACTOPTION "ListeningTo/OverrideContactOption"
+
+// Get the text to show if some info of the contact is empty.
+// wParam = NULL
+// lParam = NULL
+// Returns a TCHAR *. Don't free
+#define MS_LISTENINGTO_GETUNKNOWNTEXT "ListeningTo/GetUnknownText"
+
+
+#endif // M_PROTO_LISTENINGTO_H__
+
diff --git a/Plugins/historykeeper/sdk/m_speak.h b/Plugins/historykeeper/sdk/m_speak.h
new file mode 100644
index 0000000..cd05737
--- /dev/null
+++ b/Plugins/historykeeper/sdk/m_speak.h
@@ -0,0 +1,278 @@
+/*
+Copyright (C) 2007 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#ifndef __M_SPEAK_H__
+# define __M_SPEAK_H__
+
+
+/*
+There is 2 ways of using the speak plugin:
+
+1. Older and simple way: just call
+ Speak_Say(hContact, _T("text to speak"))
+and the text will be spoken using contact settings. If hContact is NULL, it will use
+system settings.
+Previous versions only had an ascii version, so if you want to support then you need
+to call
+ Speak_SayA(hContact, "text to speak")
+
+
+2. Integrating with eSpeak GUI: for that you have first to register a speak type and
+then call the speak functions. In both case you have 2 options:
+
+2.1 Sending the full text: eSpeak GUI will only allow to enable/disable the type.
+To register call (in modules loaded):
+ Speak_Register("PluginName (DB key)", "name", "Prety name for GUI", "icon_xyz")
+And to speak call:
+ Speak_SayEx("name", hContact, _T("text to speak"))
+
+2.2 Using templates: you will not pass the text, but some variables. eSpeak handles
+the GUI to allow the user to create the text for those variables. These functions
+end with WT (with templates).
+To register call (in modules loaded):
+ char *templates[] = { "Name\nDefault\n%var1%\tDescription 1\n%var2%\tDescription2\n%var3%\tDescription 3" };
+ Speak_RegisterWT("PluginName (DB key)", "name", "Prety name for GUI", "icon_xyz",
+ templates, 1);
+And to speak call:
+ TCHAR *variables[] = { _T("var1"), _T("Value 1"), _T("var2"), _T("Value 2"), _T("var3"), _T("Value 3") };
+ Speak_SayExWT("name", hContact, 0, variables, 3);
+*/
+
+
+#define MIID_SPEAK { 0x1ef72725, 0x6a83, 0x483b, { 0xaa, 0x50, 0x89, 0x53, 0xe3, 0x59, 0xee, 0xad } }
+
+
+/*
+Speak a text
+
+wParam: (HANDLE) hContact
+lParam: (char *) text
+return: 0 on success
+*/
+#define MS_SPEAK_SAY_A "Speak/Say"
+
+
+/*
+Speak a unicode text
+
+wParam: (HANDLE) hContact
+lParam: (WCHAR *) text
+return: 0 on success
+*/
+#define MS_SPEAK_SAY_W "Speak/SayW"
+
+
+typedef struct {
+ int cbSize;
+
+ const char *module;
+ const char *name; // Internal type name
+ const char *description; // Will be translated
+ const char *icon; // Name off icolib icon
+
+ // Aditional data if wants to use add to history services
+ char **templates; // Each entry is: "Name\nDefault\n%var%\tDescription\n%var%\tDescription\n%var%\tDescription"
+ int numTemplates;
+
+} SPEAK_TYPE;
+
+
+/*
+Register and speak type
+
+wParam: (SPEAK_TYPE *) type
+lParam: 0
+return: 0 on success
+*/
+#define MS_SPEAK_REGISTER "Speak/Register"
+
+
+#define SPEAK_CHAR 1
+#define SPEAK_WCHAR 2
+
+typedef struct {
+ int cbSize;
+
+ const char *type; // Internal type name
+ HANDLE hContact;
+ int flags; // SPEAK_*
+
+ int templateNum; // -1 to use text
+ union
+ {
+ const void *text;
+
+ struct
+ {
+ void *variables;
+ int numVariables;
+ };
+ };
+
+} SPEAK_ITEM;
+
+
+/*
+Speak a text
+
+wParam: (SPEAK_ITEM *) Item
+lParam: 0
+return: 0 on success
+*/
+#define MS_SPEAK_SAYEX "Speak/SayEx"
+
+
+
+// Helper functions
+
+static int Speak_SayA(HANDLE hContact, const char *text)
+{
+ return CallService(MS_SPEAK_SAY_A, (WPARAM) hContact, (LPARAM) text);
+}
+
+static int Speak_SayW(HANDLE hContact, const WCHAR *text)
+{
+ return CallService(MS_SPEAK_SAY_W, (WPARAM) hContact, (LPARAM) text);
+}
+
+static int Speak_Register(char *module, char *name, char *description, char *icon)
+{
+ SPEAK_TYPE type;
+
+ if (!ServiceExists(MS_SPEAK_REGISTER))
+ return -1;
+
+ type.cbSize = sizeof(type);
+ type.module = module;
+ type.name = name;
+ type.description = description;
+ type.icon = icon;
+ type.templates = NULL;
+ type.numTemplates = 0;
+
+ return CallService(MS_SPEAK_REGISTER, (WPARAM) &type, 0);
+}
+
+static int Speak_RegisterWT(const char *module, const char *name, const char *description,
+ const char *icon, char **templates, int numTemplates)
+{
+ SPEAK_TYPE type;
+
+ if (!ServiceExists(MS_SPEAK_REGISTER))
+ return -1;
+
+ type.cbSize = sizeof(type);
+ type.module = module;
+ type.name = name;
+ type.description = description;
+ type.icon = icon;
+ type.templates = templates;
+ type.numTemplates = numTemplates;
+
+ return CallService(MS_SPEAK_REGISTER, (WPARAM) &type, 0);
+}
+
+static int Speak_SayExA(char *type, HANDLE hContact, const char *text)
+{
+ SPEAK_ITEM item;
+
+ if (!ServiceExists(MS_SPEAK_SAYEX))
+ // Try old service
+ return Speak_SayA(hContact, text);
+
+ item.cbSize = sizeof(item);
+ item.flags = SPEAK_CHAR;
+ item.type = type;
+ item.hContact = hContact;
+ item.templateNum = -1;
+ item.text = text;
+
+ return CallService(MS_SPEAK_SAYEX, (WPARAM) &item, 0);
+}
+
+static int Speak_SayExW(char *type, HANDLE hContact, const WCHAR *text)
+{
+ SPEAK_ITEM item;
+
+ if (!ServiceExists(MS_SPEAK_SAYEX))
+ // Try old service
+ return Speak_SayW(hContact, text);
+
+ item.cbSize = sizeof(item);
+ item.flags = SPEAK_WCHAR;
+ item.type = type;
+ item.hContact = hContact;
+ item.templateNum = -1;
+ item.text = text;
+
+ return CallService(MS_SPEAK_SAYEX, (WPARAM) &item, 0);
+}
+
+static int Speak_SayExWTA(char *type, HANDLE hContact, int templateNum, char **variables, int numVariables)
+{
+ SPEAK_ITEM item;
+
+ if (!ServiceExists(MS_SPEAK_SAYEX))
+ return -1;
+
+ item.cbSize = sizeof(item);
+ item.flags = SPEAK_CHAR;
+ item.type = type;
+ item.hContact = hContact;
+ item.templateNum = templateNum;
+ item.variables = variables;
+ item.numVariables = numVariables;
+
+ return CallService(MS_SPEAK_SAYEX, (WPARAM) &item, 0);
+}
+
+static int Speak_SayExWTW(char *type, HANDLE hContact, int templateNum, WCHAR **variables, int numVariables)
+{
+ SPEAK_ITEM item;
+
+ if (!ServiceExists(MS_SPEAK_SAYEX))
+ return -1;
+
+ item.cbSize = sizeof(item);
+ item.flags = SPEAK_WCHAR;
+ item.type = type;
+ item.hContact = hContact;
+ item.templateNum = templateNum;
+ item.variables = variables;
+ item.numVariables = numVariables;
+
+ return CallService(MS_SPEAK_SAYEX, (WPARAM) &item, 0);
+}
+
+
+#ifdef UNICODE
+# define MS_SPEAK_SAY MS_SPEAK_SAY_W
+# define Speak_Say Speak_SayW
+# define Speak_SayEx Speak_SayExW
+# define Speak_SayExWT Speak_SayExWTW
+#else
+# define MS_SPEAK_SAY MS_SPEAK_SAY_A
+# define Speak_Say Speak_SayA
+# define Speak_SayEx Speak_SayExA
+# define Speak_SayExWT Speak_SayExWTA
+#endif
+
+
+#endif // __M_SPEAK_H__
diff --git a/Plugins/historykeeper/sdk/m_updater.h b/Plugins/historykeeper/sdk/m_updater.h
new file mode 100644
index 0000000..371b743
--- /dev/null
+++ b/Plugins/historykeeper/sdk/m_updater.h
@@ -0,0 +1,146 @@
+#ifndef _M_UPDATER_H
+#define _M_UPDATER_H
+
+// NOTES:
+// - For langpack updates, include a string of the following format in the langpack text file:
+// ";FLID: <file listing name> <version>"
+// version must be four numbers seperated by '.', in the range 0-255 inclusive
+// - Updater will disable plugins that are downloaded but were not active prior to the update (this is so that, if an archive contains e.g. ansi and
+// unicode versions, the correct plugin will be the only one active after the new version is installed)...so if you add a support plugin, you may need
+// to install an ini file to make the plugin activate when miranda restarts after the update
+// - Updater will replace all dlls that have the same internal shortName as a downloaded update dll (this is so that msn1.dll and msn2.dll, for example,
+// will both be updated) - so if you have a unicode and a non-unicode version of a plugin in your archive, you should make the internal names different (which will break automatic
+// updates from the file listing if there is only one file listing entry for both versions, unless you use the 'MS_UPDATE_REGISTER' service below)
+// - Updater will install all files in the root of the archive into the plugins folder, except for langpack files that contain the FLID string which go into the root folder (same
+// folder as miranda32.exe)...all folders in the archive will also be copied to miranda's root folder, and their contents transferred into the new folders. The only exception is a
+// special folder called 'root_files' - if there is a folder by that name in the archive, it's contents will also be copied into miranda's root folder - this is intended to be used
+// to install additional dlls etc that a plugin may require)
+
+// if you set Update.szUpdateURL to the following value when registering, as well as setting your beta site and version data,
+// Updater will ignore szVersionURL and pbVersionPrefix, and attempt to find the file listing URL's from the backend XML data.
+// for this to work, the plugin name in pluginInfo.shortName must match the file listing exactly (except for case)
+#define UPDATER_AUTOREGISTER "UpdaterAUTOREGISTER"
+// Updater will also use the backend xml data if you provide URL's that reference the miranda file listing for updates (so you can use that method
+// if e.g. your plugin shortName does not match the file listing) - it will grab the file listing id from the end of these URLs
+
+typedef struct Update_tag {
+ int cbSize;
+ char *szComponentName; // component name as it will appear in the UI (will be translated before displaying)
+
+ char *szVersionURL; // URL where the current version can be found (NULL to disable)
+ BYTE *pbVersionPrefix; // bytes occuring in VersionURL before the version, used to locate the version information within the URL data
+ // (note that this URL could point at a binary file - dunno why, but it could :)
+ int cpbVersionPrefix; // number of bytes pointed to by pbVersionPrefix
+ char *szUpdateURL; // URL where dll/zip is located
+ // set to UPDATER_AUTOREGISTER if you want Updater to find the file listing URLs (ensure plugin shortName matches file listing!)
+
+ char *szBetaVersionURL; // URL where the beta version can be found (NULL to disable betas)
+ BYTE *pbBetaVersionPrefix; // bytes occuring in VersionURL before the version, used to locate the version information within the URL data
+ int cpbBetaVersionPrefix; // number of bytes pointed to by pbVersionPrefix
+ char *szBetaUpdateURL; // URL where dll/zip is located
+
+ BYTE *pbVersion; // bytes of current version, used for comparison with those in VersionURL
+ int cpbVersion; // number of bytes pointed to by pbVersion
+
+ char *szBetaChangelogURL; // url for displaying changelog for beta versions
+} Update;
+
+// register a comonent with Updater
+//
+// wparam = 0
+// lparam = (LPARAM)&Update
+#define MS_UPDATE_REGISTER "Update/Register"
+
+// utility functions to create a version string from a DWORD or from pluginInfo
+// point buf at a buffer at least 16 chars wide - but note the version string returned may be shorter
+//
+__inline static char *CreateVersionString(DWORD version, char *buf) {
+ mir_snprintf(buf, 16, "%d.%d.%d.%d", (version >> 24) & 0xFF, (version >> 16) & 0xFF, (version >> 8) & 0xFF, version & 0xFF);
+ return buf;
+}
+
+__inline static char *CreateVersionStringPlugin(PLUGININFO *pluginInfo, char *buf) {
+ return CreateVersionString(pluginInfo->version, buf);
+}
+
+
+// register the 'easy' way - use this method if you have no beta URL and the plugin is on the miranda file listing
+// NOTE: the plugin version string on the file listing must be the string version of the version in pluginInfo (i.e. 0.0.0.1,
+// four numbers between 0 and 255 inclusivem, so no letters, brackets, etc.)
+//
+// wParam = (int)fileID - this is the file ID from the file listing (i.e. the number at the end of the download link)
+// lParam = (PLUGININFO*)&pluginInfo
+#define MS_UPDATE_REGISTERFL "Update/RegisterFL"
+
+// this function can be used to 'unregister' components - useful for plugins that register non-plugin/langpack components and
+// may need to change those components on the fly
+// lParam = (char *)szComponentName
+#define MS_UPDATE_UNREGISTER "Update/Unregister"
+
+// this event is fired when the startup process is complete, but NOT if a restart is imminent
+// it is designed for status managment plugins to use as a trigger for beggining their own startup process
+// wParam = lParam = 0 (unused)
+// (added in version 0.1.6.0)
+#define ME_UPDATE_STARTUPDONE "Update/StartupDone"
+
+// this service can be used to enable/disable Updater's global status control
+// it can be called from the StartupDone event handler
+// wParam = (BOOL)enable
+// lParam = 0
+// (added in version 0.1.6.0)
+#define MS_UPDATE_ENABLESTATUSCONTROL "Update/EnableStatusControl"
+
+// An description of usage of the above service and event:
+// Say you are a status control plugin that normally sets protocol or global statuses in your ModulesLoaded event handler.
+// In order to make yourself 'Updater compatible', you would move the status control code from ModulesLoaded to another function,
+// say DoStartup. Then, in ModulesLoaded you would check for the existence of the MS_UPDATE_ENABLESTATUSCONTROL service.
+// If it does not exist, call DoStartup. If it does exist, hook the ME_UPDATE_STARTUPDONE event and call DoStartup from there. You may
+// also wish to call MS_UPDATE_ENABLESTATUSCONTROL with wParam == FALSE at this time, to disable Updater's own status control feature.
+
+// this service can be used to determine whether updates are possible for a component with the given name
+// wParam = 0
+// lParam = (char *)szComponentName
+// returns TRUE if updates are supported, FALSE otherwise
+#define MS_UPDATE_ISUPDATESUPPORTED "Update/IsUpdateSupported"
+
+#endif
+
+
+/////////////// Usage Example ///////////////
+
+#ifdef EXAMPLE_CODE
+
+// you need to #include "m_updater.h" and HookEvent(ME_SYSTEM_MODULESLOADED, OnModulesLoaded) in your Load function...
+
+int OnModulesLoaded(WPARAM wParam, LPARAM lParam) {
+
+ Update update = {0}; // for c you'd use memset or ZeroMemory...
+ char szVersion[16];
+
+ update.cbSize = sizeof(Update);
+
+ update.szComponentName = pluginInfo.shortName;
+ update.pbVersion = (BYTE *)CreateVersionString(&pluginInfo, szVersion);
+ update.cpbVersion = strlen((char *)update.pbVersion);
+
+ // these are the three lines that matter - the archive, the page containing the version string, and the text (or data)
+ // before the version that we use to locate it on the page
+ // (note that if the update URL and the version URL point to standard file listing entries, the backend xml
+ // data will be used to check for updates rather than the actual web page - this is not true for beta urls)
+ update.szUpdateURL = "http://scottellis.com.au:81/test/updater.zip";
+ update.szVersionURL = "http://scottellis.com.au:81/test/updater_test.html";
+ update.pbVersionPrefix = (BYTE *)"Updater version ";
+
+ update.cpbVersionPrefix = strlen((char *)update.pbVersionPrefix);
+
+ // do the same for the beta versions of the above struct members if you wish to allow beta updates from another URL
+
+ CallService(MS_UPDATE_REGISTER, 0, (WPARAM)&update);
+
+ // Alternatively, to register a plugin with e.g. file ID 2254 on the file listing...
+ // CallService(MS_UPDATE_REGISTERFL, (WPARAM)2254, (LPARAM)&pluginInfo);
+
+ return 0;
+}
+
+#endif
diff --git a/Plugins/historykeeper/sdk/m_variables.h b/Plugins/historykeeper/sdk/m_variables.h
new file mode 100644
index 0000000..3f13c96
--- /dev/null
+++ b/Plugins/historykeeper/sdk/m_variables.h
@@ -0,0 +1,718 @@
+/*
+ Variables Plugin for Miranda-IM (www.miranda-im.org)
+ Copyright 2003-2006 P. Boon
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#ifndef __M_VARS
+#define __M_VARS
+
+#if !defined(_TCHAR_DEFINED)
+#include <tchar.h>
+#endif
+
+#ifndef VARIABLES_NOHELPER
+#include <m_button.h>
+#endif
+
+// --------------------------------------------------------------------------
+// Memory management
+// --------------------------------------------------------------------------
+
+// Release memory that was allocated by the Variables plugin, e.g. returned
+// strings.
+
+#define MS_VARS_FREEMEMORY "Vars/FreeMemory"
+
+// Parameters:
+// ------------------------
+// wParam = (WPARAM)(void *)pntr
+// Pointer to memory that was allocated by the Variables plugin (e.g. a
+// returned string) (can be NULL).
+// lParam = 0
+
+// Return Value:
+// ------------------------
+// Does return 0 on success, nozero otherwise.
+
+// Note: Do only use this service to free memory that was *explicitliy*
+// stated that it should be free with this service.
+
+
+
+#define MS_VARS_GET_MMI "Vars/GetMMI"
+
+// Get Variable's RTL/CRT function poiners to malloc(), free() and
+// realloc().
+
+// Parameters:
+// ------------------------
+// wParam = 0
+// lParam = (LPARAM) &MM_INTERFACE
+// Pointer to a memory manager interface struct (see m_system.h).
+
+// Return Value:
+// ------------------------
+// Returns 0 on success, nozero otherwise
+
+// Note: Works exactly the same as the MS_SYSTEM_GET_MMI service
+// service of m_system.h.
+
+// Helper function for easy using:
+#ifndef VARIABLES_NOHELPER
+__inline static void variables_free(void *pntr) {
+
+ CallService(MS_VARS_FREEMEMORY, (WPARAM)pntr, 0);
+}
+#endif
+
+
+
+// --------------------------------------------------------------------------
+// String formatting
+// --------------------------------------------------------------------------
+
+#define MS_VARS_FORMATSTRING "Vars/FormatString"
+
+// This service can be used to parse tokens in a text. The tokens will be
+// replaced by their resolved values. A token can either be a field or a
+// function. A field takes no arguments and is represented between
+// %-characters, e.g. "%winampsong%". A function can take any number of
+// arguments and is represented by a ? or !-character followed by the name
+// of the function and a list of arguments, e.g. "?add(1,2)".
+
+// Parameters:
+// ------------------------
+// wParam = (WPARAM)(FORMATINFO *)&fi
+// See below.
+// lParam = 0
+
+// Return Value:
+// ------------------------
+// Returns a pointer to the resolved string or NULL in case of an error.
+
+// Note: The returned pointer needs to be freed using MS_VARS_FREEMEMORY.
+
+typedef struct {
+ int cbSize; // Set this to sizeof(FORMATINFO).
+ int flags; // Flags to use (see FIF_* below).
+ union {
+ char *szFormat; // Text in which the tokens will be replaced (can't be
+ // NULL).
+ WCHAR *wszFormat;
+ TCHAR *tszFormat;
+ };
+ union {
+ char *szExtraText; // Extra, context-specific string (can be NULL) ->
+ // The field "extratext" will be replaced by this
+ // string. (Previously szSource).
+ WCHAR *wszExtraText;
+ TCHAR *tszExtraText;
+ };
+ HANDLE hContact; // Handle to contact (can be NULL) -> The field "subject"
+ // represents this contact.
+ int pCount; // (output) Number of succesful parsed tokens, needs to be set
+ // to 0 before the call
+ int eCount; // (output) Number of failed tokens, needs to be set to 0
+ // before the call
+ union {
+ char **szaTemporaryVars; // Temporary variables valid only in the duration of the format call
+ TCHAR **tszaTemporaryVars; // By pos: [i] is var name, [i + 1] is var value
+ WCHAR **wszaTemporaryVars;
+ };
+ int cbTemporaryVarsSize; // Number of elements in szaTemporaryVars array
+
+} FORMATINFO;
+
+#define FORMATINFOV2_SIZE 28
+
+// Possible flags:
+#define FIF_UNICODE 0x01 // Expects and returns unicode text (WCHAR*).
+
+#if defined(UNICODE) || defined(_UNICODE)
+#define FIF_TCHAR FIF_UNICODE // Strings in structure are TCHAR*.
+#else
+#define FIF_TCHAR 0
+#endif
+
+// Helper functions for easy using:
+
+// Helper #1: variables_parse
+// ------------------------
+// The returned string needs to be freed using MS_VARS_FREEMEMORY.
+
+#ifndef VARIABLES_NOHELPER
+__inline static TCHAR *variables_parse(TCHAR *tszFormat, TCHAR *tszExtraText, HANDLE hContact) {
+
+ FORMATINFO fi;
+
+ ZeroMemory(&fi, sizeof(fi));
+ fi.cbSize = sizeof(fi);
+ fi.tszFormat = tszFormat;
+ fi.tszExtraText = tszExtraText;
+ fi.hContact = hContact;
+ fi.flags = FIF_TCHAR;
+
+ return (TCHAR *)CallService(MS_VARS_FORMATSTRING, (WPARAM)&fi, 0);
+}
+#endif
+
+__inline static TCHAR *variables_parse_ex(TCHAR *tszFormat, TCHAR *tszExtraText, HANDLE hContact,
+ TCHAR **tszaTemporaryVars, int cbTemporaryVarsSize) {
+
+ FORMATINFO fi;
+
+ ZeroMemory(&fi, sizeof(fi));
+ fi.cbSize = sizeof(fi);
+ fi.tszFormat = tszFormat;
+ fi.tszExtraText = tszExtraText;
+ fi.hContact = hContact;
+ fi.flags = FIF_TCHAR;
+ fi.tszaTemporaryVars = tszaTemporaryVars;
+ fi.cbTemporaryVarsSize = cbTemporaryVarsSize;
+
+ return (TCHAR *)CallService(MS_VARS_FORMATSTRING, (WPARAM)&fi, 0);
+}
+// Helper #2: variables_parsedup
+// ------------------------
+// Returns a _strdup()'ed copy of the unparsed string when Variables is not
+// installed, returns a strdup()'ed copy of the parsed result otherwise.
+
+// Note: The returned pointer needs to be released using your own free().
+
+#ifndef VARIABLES_NOHELPER
+__inline static TCHAR *variables_parsedup(TCHAR *tszFormat, TCHAR *tszExtraText, HANDLE hContact) {
+
+ if (ServiceExists(MS_VARS_FORMATSTRING)) {
+ FORMATINFO fi;
+ TCHAR *tszParsed, *tszResult;
+
+ ZeroMemory(&fi, sizeof(fi));
+ fi.cbSize = sizeof(fi);
+ fi.tszFormat = tszFormat;
+ fi.tszExtraText = tszExtraText;
+ fi.hContact = hContact;
+ fi.flags |= FIF_TCHAR;
+ tszParsed = (TCHAR *)CallService(MS_VARS_FORMATSTRING, (WPARAM)&fi, 0);
+ if (tszParsed) {
+ tszResult = _tcsdup(tszParsed);
+ CallService(MS_VARS_FREEMEMORY, (WPARAM)tszParsed, 0);
+ return tszResult;
+ }
+ }
+ return tszFormat?_tcsdup(tszFormat):tszFormat;
+}
+
+__inline static TCHAR *variables_parsedup_ex(TCHAR *tszFormat, TCHAR *tszExtraText, HANDLE hContact,
+ TCHAR **tszaTemporaryVars, int cbTemporaryVarsSize) {
+
+ if (ServiceExists(MS_VARS_FORMATSTRING)) {
+ FORMATINFO fi;
+ TCHAR *tszParsed, *tszResult;
+
+ ZeroMemory(&fi, sizeof(fi));
+ fi.cbSize = sizeof(fi);
+ fi.tszFormat = tszFormat;
+ fi.tszExtraText = tszExtraText;
+ fi.hContact = hContact;
+ fi.flags |= FIF_TCHAR;
+ fi.tszaTemporaryVars = tszaTemporaryVars;
+ fi.cbTemporaryVarsSize = cbTemporaryVarsSize;
+ tszParsed = (TCHAR *)CallService(MS_VARS_FORMATSTRING, (WPARAM)&fi, 0);
+ if (tszParsed) {
+ tszResult = _tcsdup(tszParsed);
+ CallService(MS_VARS_FREEMEMORY, (WPARAM)tszParsed, 0);
+ return tszResult;
+ }
+ }
+ return tszFormat?_tcsdup(tszFormat):tszFormat;
+}
+#endif
+
+
+
+// --------------------------------------------------------------------------
+// Register tokens
+// --------------------------------------------------------------------------
+
+// Plugins can define tokens which will be parsed by the Variables plugin.
+
+#define MS_VARS_REGISTERTOKEN "Vars/RegisterToken"
+
+// With this service you can define your own token. The newly added tokens
+// using this service are taken into account on every call to
+// MS_VARS_FORMATSTRING.
+
+// Parameters:
+// ------------------------
+// wParam = 0
+// lParam = (LPARAM)(TOKENREGISTER*)&tr
+// See below.
+
+// Return Value:
+// ------------------------
+// Returns 0 on success, nonzero otherwise. Existing tokens will be
+// 'overwritten' if registered twice.
+
+// Needed for szService and parseFunction:
+typedef struct {
+ int cbSize; // You need to check if this is >=sizeof(ARGUMENTSINFO)
+ // (already filled in).
+ FORMATINFO *fi; // Arguments passed to MS_VARS_FORMATSTRING.
+ unsigned int argc; // Number of elements in the argv array.
+ union {
+ char **argv; // Argv[0] will be the token name, the following elements
+ // are the additional arguments.
+ WCHAR **wargv; // If the registered token was registered as a unicode
+ // token, wargv should be accessed.
+ TCHAR **targv;
+ };
+ int flags; // (output) You can set flags here (initially 0), use the
+ // AIF_* flags (see below).
+} ARGUMENTSINFO;
+
+// Available flags for ARGUMENTSINFO:
+// Set the flags of the ARGUMENTSINFO struct to any of these to influence
+// further parsing.
+#define AIF_DONTPARSE 0x01 // Don't parse the result of this function,
+ // usually the result of a token is parsed
+ // again, if the `?` is used as a function
+ // character.
+#define AIF_FALSE 0x02 // The function returned logical false.
+
+// Definition of parse/cleanup functions:
+typedef char* (*VARPARSEFUNCA)(ARGUMENTSINFO *ai);
+typedef WCHAR* (*VARPARSEFUNCW)(ARGUMENTSINFO *ai);
+typedef void (*VARCLEANUPFUNCA)(char *szReturn);
+typedef void (*VARCLEANUPFUNCW)(WCHAR *wszReturn);
+
+#if defined(UNICODE) || defined(_UNICODE)
+#define VARPARSEFUNC VARPARSEFUNCW
+#define VARCLEANUPFUNC VARCLEANUPFUNCW
+#else
+#define VARPARSEFUNC VARPARSEFUNCA
+#define VARCLEANUPFUNC VARCLEANUPFUNCA
+#endif
+
+typedef struct {
+ int cbSize; // Set this to sizeof(TOKENREGISTER).
+ union {
+ char *szTokenString; // Name of the new token to be created, without %,
+ // ?, ! etc. signs (can't be NULL).
+ WCHAR *wszTokenString;
+ TCHAR *tszTokenString;
+ };
+ union {
+ char *szService; // Name of a service that is used to request the
+ // token's value, if no service is used, a function
+ // and TRF_PARSEFUNC must be used.
+ VARPARSEFUNCA parseFunction; // See above, use with TRF_PARSEFUNC.
+ VARPARSEFUNCW parseFunctionW;
+ VARPARSEFUNC parseFunctionT;
+ };
+ union {
+ char *szCleanupService; // Name of a service to be called when the
+ // memory allocated in szService can be freed
+ // (only used when flag VRF_CLEANUP is set,
+ // else set this to NULL).
+ VARCLEANUPFUNCA cleanupFunction; // See above, use with TRF_CLEANUPFUNC.
+ VARCLEANUPFUNCW cleanupFunctionW;
+ VARCLEANUPFUNC cleanupFunctionT;
+ };
+ char *szHelpText; // Help info shown in help dialog (can be NULL). Has to
+ // be in the following format:
+ // "subject\targuments\tdescription"
+ // (Example: "math\t(x, y ,...)\tx + y + ..."), or:
+ // "subject\tdescription"
+ // (Example: "miranda\tPath to the Miranda-IM
+ // executable").
+ // Note: subject and description are translated by
+ // Variables.
+ int memType; // Describes which method Varibale's plugin needs to use to
+ // free the returned buffer, use one of the VR_MEM_* values
+ // (see below). Only valid if the flag VRF_FREEMEM is set,
+ // use TR_MEM_OWNER otherwise).
+ int flags; // Flags to use (see below), one of TRF_* (see below).
+} TOKENREGISTER;
+
+// Available Memory Storage Types:
+// These values describe which method Variables Plugin will use to free the
+// buffer returned by the parse function or service
+#define TR_MEM_VARIABLES 1 // Memory is allocated using the functions
+ // retrieved by MS_VARS_GET_MMI.
+#define TR_MEM_MIRANDA 2 // Memory is allocated using Miranda's Memory
+ // Manager Interface (using the functions
+ // returned by MS_SYSTEM_GET_MMI), if
+ // VRF_FREEMEM is set, the memory will be
+ // freed by Variables.
+#define TR_MEM_OWNER 3 // Memory is owned by the calling plugin
+ // (can't be freed by Variables Plugin
+ // automatically). This should be used if
+ // VRF_FREEMEM is not specified in the flags.
+
+// Available Flags for TOKENREGISTER:
+#define TRF_FREEMEM 0x01 // Variables Plugin will automatically free the
+ // pointer returned by the parse function or
+ // service (which method it will us is
+ // specified in memType -> see above).
+#define TRF_CLEANUP 0x02 // Call cleanup service or function, notifying
+ // that the returned buffer can be freed.
+ // Normally you should use either TRF_FREEMEM
+ // or TRF_CLEANUP.
+#define TRF_PARSEFUNC 0x40 // parseFunction will be used instead of a
+ // service.
+#define TRF_CLEANUPFUNC 0x80 // cleanupFunction will be used instead of a
+ // service.
+#define TRF_USEFUNCS TRF_PARSEFUNC|TRF_CLEANUPFUNC
+#define TRF_UNPARSEDARGS 0x04 // Provide the arguments for the parse
+ // function in their raw (unparsed) form.
+ // By default, arguments are parsed before
+ // presenting them to the parse function.
+#define TRF_FIELD 0x08 // The token can be used as a %field%.
+#define TRF_FUNCTION 0x10 // The token can be used as a ?function().
+ // Normally you should use either TRF_FIELD or
+ // TRF_FUNCTION.
+#define TRF_UNICODE 0x20 // Strings in structure are unicode (WCHAR*).
+ // In this case, the strings pointing to the
+ // arguments in the ARGUMENTS struct are
+ // unicode also. The returned buffer is
+ // expected to be unicode also, and the
+ // unicode parse and cleanup functions are
+ // called.
+
+#if defined(UNICODE) || defined(_UNICODE)
+#define TRF_TCHAR TRF_UNICODE // Strings in structure are TCHAR*.
+#else
+#define TRF_TCHAR 0
+#endif
+
+// Deprecated:
+#define TRF_CALLSVC TRF_CLEANUP
+
+// Callback Service (szService) / parseFunction:
+// ------------------------
+// Service that is called automatically by the Variable's Plugin to resolve a
+// registered variable.
+
+// Parameters:
+// wParam = 0
+// lParam = (LPARAM)(ARGUMENTSINFO *)&ai
+// see above
+
+// Return Value:
+// Needs to return the pointer to a dynamically allocacated string or NULL.
+// A return value of NULL is regarded as an error (eCount will be increaded).
+// Flags in the ARGUMENTSINFO struct can be set (see above).
+
+// Callback Service (szCallbackService) / cleanupFunction:
+// ------------------------
+// This service is called when the memory that was allocated by the parse
+// function or service can be freed. Note: It will only be called when the
+// flag VRF_CLEANUP of TOKENREGISTER is set.
+
+// Parameters:
+// wParam = 0
+// lParam = (LPARAM)(char *)&res
+// Result from parse function or service (pointer to a string).
+
+// Return Value:
+// Should return 0 on success.
+
+
+
+// --------------------------------------------------------------------------
+// Show the help dialog
+// --------------------------------------------------------------------------
+
+// Plugins can invoke Variables' help dialog which can be used for easy input
+// by users.
+
+#define MS_VARS_SHOWHELPEX "Vars/ShowHelpEx"
+
+// This service can be used to open the help dialog of Variables. This dialog
+// provides easy input for the user and/or information about the available
+// tokens.
+
+// Parameters:
+// ------------------------
+// wParam = (WPARAM)(HWND)hwndParent
+// lParam = (LPARAM)(VARHELPINFO)&vhi
+// See below.
+
+// Return Value:
+// ------------------------
+// Returns 0 on succes, any other value on error.
+
+typedef struct {
+ int cbSize; // Set to sizeof(VARHELPINFO).
+ FORMATINFO *fi; // Used for both input and output. If this pointer is not
+ // NULL, the information is used as the initial values for
+ // the dialog.
+ HWND hwndCtrl; // Used for both input and output. The window text of this
+ // window will be read and used as the initial input of the
+ // input dialog. If the user presses the OK button the window
+ // text of this window will be set to the text of the input
+ // field and a EN_CHANGE message via WM_COMMAND is send to
+ // this window. (Can be NULL).
+ char *szSubjectDesc; // The description of the %subject% token will be set
+ // to this text, if not NULL. This is translated
+ // automatically.
+ char *szExtraTextDesc; // The description of the %extratext% token will be
+ // set to this text, if not NULL. This is translated
+ // automatically.
+ int flags; // Flags, see below.
+} VARHELPINFO;
+
+
+// Flags for VARHELPINFO
+#define VHF_TOKENS 0x00000001 // Create a dialog with the list of
+ // tokens
+#define VHF_INPUT 0x00000002 // Create a dialog with an input
+ // field (this contains the list of
+ // tokens as well).
+#define VHF_SUBJECT 0x00000004 // Create a dialog to select a
+ // contact for the %subject% token.
+#define VHF_EXTRATEXT 0x00000008 // Create a dialog to enter a text
+ // for the %extratext% token.
+#define VHF_HELP 0x00000010 // Create a dialog with help info.
+#define VHF_HIDESUBJECTTOKEN 0x00000020 // Hide the %subject% token in the
+ // list of tokens.
+#define VHF_HIDEEXTRATEXTTOKEN 0x00000040 // Hide the %extratext% token in
+ // the list of tokens.
+#define VHF_DONTFILLSTRUCT 0x00000080 // Don't fill the struct with the
+ // new information if OK is pressed
+#define VHF_FULLFILLSTRUCT 0x00000100 // Fill all members of the struct
+ // when OK is pressed. By default
+ // only szFormat is set. With this
+ // flag on, hContact and
+ // szExtraText are also set.
+#define VHF_SETLASTSUBJECT 0x00000200 // Set the last contact that was
+ // used in the %subject% dialog in
+ // case fi.hContact is NULL.
+
+// Predefined flags
+#define VHF_FULLDLG VHF_INPUT|VHF_SUBJECT|VHF_EXTRATEXT|VHF_HELP
+#define VHF_SIMPLEDLG VHF_INPUT|VHF_HELP
+#define VHF_NOINPUTDLG VHF_TOKENS|VHF_HELP
+
+// If the service fills information in the struct for szFormat or szExtraText,
+// these members must be free'd using the free function of Variables.
+// If wParam==NULL, the dialog is created modeless. Only one dialog can be
+// shown at the time.
+// If both hwndCtrl and fi are NULL, the user input will not be retrievable.
+// In this case, the dialog is created with only a "Close" button, instead of
+// the "OK" and "Cancel" buttons.
+// In case of modeless dialog and fi != NULL, please make sure this pointer
+// stays valid while the dialog is open.
+
+// Helper function for easy use in standard case:
+#ifndef VARIABLES_NOHELPER
+__inline static int variables_showhelp(HWND hwndDlg, UINT uIDEdit, int flags, char *szSubjectDesc, char *szExtraDesc) {
+
+ VARHELPINFO vhi;
+
+ ZeroMemory(&vhi, sizeof(VARHELPINFO));
+ vhi.cbSize = sizeof(VARHELPINFO);
+ if (flags == 0) {
+ flags = VHF_SIMPLEDLG;
+ }
+ vhi.flags = flags;
+ vhi.hwndCtrl = GetDlgItem(hwndDlg, uIDEdit);
+ vhi.szSubjectDesc = szSubjectDesc;
+ vhi.szExtraTextDesc = szExtraDesc;
+
+ return CallService(MS_VARS_SHOWHELPEX, (WPARAM)hwndDlg, (LPARAM)&vhi);
+}
+#endif
+
+
+#define MS_VARS_GETSKINITEM "Vars/GetSkinItem"
+
+// This service can be used to get the icon you can use for example on the
+// Variables help button in your options screen. You can also get the tooltip
+// text to use with such a button. If icon library is available the icon will
+// be retrieved from icon library manager, otherwise the default is returned.
+
+// Parameters:
+// ------------------------
+// wParam = (WPARAM)0
+// lParam = (LPARAM)VSI_* (see below)
+
+// Return Value:
+// ------------------------
+// Depends on the information to retrieve (see below).
+
+// VSI_ constants
+#define VSI_HELPICON 1 // Can be used on the button accessing the
+ // Variables help dialog. Returns (HICON)hIcon on
+ // success or NULL on failure;
+#define VSI_HELPTIPTEXT 2 // Returns the tooltip text you can use for the
+ // help button. Returns (char *)szTipText, a
+ // static, translated buffer containing the help
+ // text or NULL on error.
+
+// Helper to set the icon on a button accessing the help dialog.
+// Preferably a 16x14 MButtonClass control, but it works on a standard
+// button control as well. If no icon is availble (because of old version of
+// Variables) the string "V" is shown on the button. If Variables is not
+// available, the button will be hidden.
+#ifndef VARIABLES_NOHELPER
+__inline static int variables_skin_helpbutton(HWND hwndDlg, UINT uIDButton) {
+
+ int res;
+ HICON hIcon;
+ TCHAR tszClass[32];
+
+ hIcon = NULL;
+ res = 0;
+ if (ServiceExists(MS_VARS_GETSKINITEM)) {
+ hIcon = (HICON)CallService(MS_VARS_GETSKINITEM, 0, (LPARAM)VSI_HELPICON);
+ }
+ GetClassName(GetDlgItem(hwndDlg, uIDButton), tszClass, sizeof(tszClass));
+ if (!_tcscmp(tszClass, _T("Button"))) {
+ if (hIcon != NULL) {
+ SetWindowLong(GetDlgItem(hwndDlg, uIDButton), GWL_STYLE, GetWindowLong(GetDlgItem(hwndDlg, uIDButton), GWL_STYLE)|BS_ICON);
+ SendMessage(GetDlgItem(hwndDlg, uIDButton), BM_SETIMAGE, (WPARAM)IMAGE_ICON, (LPARAM)hIcon);
+ }
+ else {
+ SetWindowLong(GetDlgItem(hwndDlg, uIDButton), GWL_STYLE, GetWindowLong(GetDlgItem(hwndDlg, uIDButton), GWL_STYLE)&~BS_ICON);
+ SetDlgItemText(hwndDlg, uIDButton, _T("V"));
+ }
+ }
+ else if (!_tcscmp(tszClass, MIRANDABUTTONCLASS)) {
+ if (hIcon != NULL) {
+ char *szTipInfo;
+
+ SendMessage(GetDlgItem(hwndDlg, uIDButton), BM_SETIMAGE, (WPARAM)IMAGE_ICON, (LPARAM)hIcon);
+ if (ServiceExists(MS_VARS_GETSKINITEM)) {
+ szTipInfo = (char *)CallService(MS_VARS_GETSKINITEM, 0, (LPARAM)VSI_HELPTIPTEXT);
+ }
+ if (szTipInfo == NULL) {
+ szTipInfo = Translate("Open String Formatting Help");
+ }
+ SendMessage(GetDlgItem(hwndDlg, uIDButton), BUTTONADDTOOLTIP, (WPARAM)szTipInfo, 0);
+ SendDlgItemMessage(hwndDlg, uIDButton, BUTTONSETASFLATBTN, 0, 0);
+ }
+ else {
+ SetDlgItemText(hwndDlg, uIDButton, _T("V"));
+ }
+ }
+ else {
+ res = -1;
+ }
+ ShowWindow(GetDlgItem(hwndDlg, uIDButton), ServiceExists(MS_VARS_FORMATSTRING));
+
+ return res;
+}
+#endif
+
+
+#define MS_VARS_SHOWHELP "Vars/ShowHelp"
+
+// WARNING: This service is obsolete, please use MS_VARS_SHOWHELPEX
+
+// Shows a help dialog where all possible tokens are displayed. The tokens
+// are explained on the dialog, too. The user can edit the initial string and
+// insert as many tokens as he likes.
+
+// Parameters:
+// ------------------------
+// wParam = (HWND)hwndEdit
+// Handle to an edit control in which the modified string
+// should be inserted (When the user clicks OK in the dialog the edited
+// string will be set to hwndEdit) (can be NULL).
+// lParam = (char *)pszInitialString
+// String that the user is provided with initially when
+// the dialog gets opened (If this is NULL then the current text in the
+// hwndEdit edit control will be used) (can be NULL).
+
+// Return Value:
+// ------------------------
+// Returns the handle to the help dialog (HWND).
+
+// Note: Only one help dialog can be opened at a time. When the dialog gets
+// closed an EN_CHANGE of the edit controll will be triggered because the
+// contents were updated. (Only when user selected OK).
+
+// Example:
+// CallService(MS_VARS_SHOWHELP, (WPARAM)hwndEdit, (LPARAM)"some initial text");
+
+// --------------------------------------------------------------------------
+// Retrieve a contact's HANDLE given a string
+// --------------------------------------------------------------------------
+
+#define MS_VARS_GETCONTACTFROMSTRING "Vars/GetContactFromString"
+
+// Searching for contacts in the database. You can find contacts in db by
+// searching for their name, e.g first name.
+
+// Parameters:
+// ------------------------
+// wParam = (WPARAM)(CONTACTSINFO *)&ci
+// See below.
+// lParam = 0
+
+// Return Value:
+// ------------------------
+// Returns number of contacts found matching the given string representation.
+// The hContacts array of CONTACTSINFO struct contains these hContacts after
+// the call.
+
+// Note: The hContacts array needs to be freed after use using
+// MS_VARS_FREEMEMORY.
+
+typedef struct {
+ int cbSize; // Set this to sizeof(CONTACTSINFO).
+ union {
+ char *szContact; // String to search for, e.g. last name (can't be NULL).
+ WCHAR *wszContact;
+ TCHAR *tszContact;
+ };
+ HANDLE *hContacts; // (output) Array of contacts found.
+ DWORD flags; // Contact details that will be matched with the search
+ // string (flags can be combined).
+} CONTACTSINFO;
+
+// Possible flags:
+#define CI_PROTOID 0x00000001 // The contact in the string is encoded
+ // in the format <PROTOID:UNIQUEID>, e.g.
+ // <ICQ:12345678>.
+#define CI_NICK 0x00000002 // Search nick names.
+#define CI_LISTNAME 0x00000004 // Search custom names shown in contact
+ // list.
+#define CI_FIRSTNAME 0x00000008 // Search contact's first names (contact
+ // details).
+#define CI_LASTNAME 0x00000010 // Search contact's last names (contact
+ // details).
+#define CI_EMAIL 0x00000020 // Search contact's email adresses
+ // (contact details).
+#define CI_UNIQUEID 0x00000040 // Search unique ids of the contac, e.g.
+ // UIN.
+#define CI_CNFINFO 0x40000000 // Searches one of the CNF_* flags (set
+ // flags to CI_CNFINFO|CNF_X), only one
+ // CNF_ type possible
+#define CI_UNICODE 0x80000000 // tszContact is a unicode string
+ // (WCHAR*).
+
+#if defined(UNICODE) || defined(_UNICODE)
+#define CI_TCHAR CI_UNICODE // Strings in structure are TCHAR*.
+#else
+#define CI_TCHAR 0
+#endif
+
+
+
+#endif //__M_VARS
diff --git a/Plugins/jingle/Docs/jingle_changelog.txt b/Plugins/jingle/Docs/jingle_changelog.txt
new file mode 100644
index 0000000..6c4166c
--- /dev/null
+++ b/Plugins/jingle/Docs/jingle_changelog.txt
@@ -0,0 +1,6 @@
+Jingle
+
+Changelog:
+
+. 0.0.0.1
+ + Initial version \ No newline at end of file
diff --git a/Plugins/jingle/Docs/jingle_readme.txt b/Plugins/jingle/Docs/jingle_readme.txt
new file mode 100644
index 0000000..edb5a6e
--- /dev/null
+++ b/Plugins/jingle/Docs/jingle_readme.txt
@@ -0,0 +1,35 @@
+Jingle - Jabber plugin
+---------------------
+
+CAUTION: THIS IS AN ALPHA STAGE PLUGIN. IT CAN DO VERY BAD THINGS. USE AT YOUR OWN RISK.
+
+This is a plugin for the jabber protocol, to allow it to make voice calls using libjingle. This is a very alpha version, that was tested very little and probabilly contains lots of bugs and TODOs.
+
+It can talk to Google Talk client and other jabber clients that implements the jingle spec.
+
+To make this work you need this mod of jabber that support plugins:
+Ansi: http://pescuma.mirandaim.ru/miranda/jabber_mp.zip
+Unicode: http://pescuma.mirandaim.ru/miranda/jabber_mpW.zip
+With some lucky it will be incorporated in the SVN soon.
+
+This plugin depends on Voice Service, that can be downloaded here:
+http://pescuma.mirandaim.ru/miranda/voiceservice
+Ansi: http://pescuma.mirandaim.ru/miranda/voiceservice.zip
+Unicode: http://pescuma.mirandaim.ru/miranda/voiceserviceW.zip
+
+Also, you need to copy the contents of the following zip to the same folder of miranda.exe:
+http://pescuma.mirandaim.ru/miranda/mediastreamer2.zip
+
+This plugin needs Miranda 0.7 to work.
+
+This plugin uses libjingle, code from linphone and a lot of other codecs. Thanks a lot to all the developers of these softwares.
+
+Todo:
+- Everything
+- A little bit more
+
+Known problems:
+- When making a call, takes a few seconds until both parts can hear each other
+- It does not take very well going offline while talking (aka crashes)
+
+To report bugs/make suggestions, go to the forum thread: http://forums.miranda-im.org/showthread.php?p=113763
diff --git a/Plugins/jingle/Docs/jingle_version.txt b/Plugins/jingle/Docs/jingle_version.txt
new file mode 100644
index 0000000..c1c6199
--- /dev/null
+++ b/Plugins/jingle/Docs/jingle_version.txt
@@ -0,0 +1 @@
+Jingle 0.0.0.1 \ No newline at end of file
diff --git a/Plugins/jingle/ZIP/doit.bat b/Plugins/jingle/ZIP/doit.bat
new file mode 100644
index 0000000..a3137a9
--- /dev/null
+++ b/Plugins/jingle/ZIP/doit.bat
@@ -0,0 +1,54 @@
+@echo off
+
+rem Batch file to build and upload files
+rem
+rem TODO: Integration with FL
+
+set name=jingle
+
+rem To upload, this var must be set here or in other batch
+rem set ftp=ftp://<user>:<password>@<ftp>/<path>
+
+echo Building %name% ...
+
+"C:\Program Files\Microsoft Visual Studio 8\Common7\IDE\devenv.com" /Rebuild Release ..\%name%.sln
+"C:\Program Files\Microsoft Visual Studio 8\Common7\IDE\devenv.com" /Rebuild "Unicode Release" ..\%name%.sln
+
+echo Generating files for %name% ...
+
+del *.zip
+del *.dll
+copy ..\..\..\bin\release\Plugins\%name%.dll
+copy ..\..\..\bin\release\Plugins\%name%W.dll
+copy ..\Docs\%name%_changelog.txt
+copy ..\Docs\%name%_version.txt
+copy ..\Docs\%name%_readme.txt
+mkdir Docs
+cd Docs
+del /Q *.*
+copy ..\..\Docs\%name%_readme.txt
+cd ..
+
+"C:\Program Files\Filzip\Filzip.exe" -a -rp %name%.zip %name%.dll Docs
+"C:\Program Files\Filzip\Filzip.exe" -a -rp %name%W.zip %name%W.dll Docs
+
+del *.dll
+cd Docs
+del /Q *.*
+cd ..
+rmdir Docs
+
+if "%ftp%"=="" GOTO END
+
+echo Going to upload files...
+pause
+
+"C:\Program Files\FileZilla\FileZilla.exe" -u .\%name%.zip %ftp% -overwrite -close
+"C:\Program Files\FileZilla\FileZilla.exe" -u .\%name%W.zip %ftp% -overwrite -close
+"C:\Program Files\FileZilla\FileZilla.exe" -u .\%name%_changelog.txt %ftp% -overwrite -close
+"C:\Program Files\FileZilla\FileZilla.exe" -u .\%name%_version.txt %ftp% -overwrite -close
+"C:\Program Files\FileZilla\FileZilla.exe" -u .\%name%_readme.txt %ftp% -overwrite -close
+
+:END
+
+echo Done.
diff --git a/Plugins/jingle/commons.h b/Plugins/jingle/commons.h
new file mode 100644
index 0000000..58b604a
--- /dev/null
+++ b/Plugins/jingle/commons.h
@@ -0,0 +1,147 @@
+/*
+Copyright (C) 2007 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#ifndef __COMMONS_H__
+# define __COMMONS_H__
+
+#include "talk/p2p/base/session.h"
+#include "talk/p2p/base/sessionmanager.h"
+#include "talk/p2p/client/httpportallocator.h"
+#include "talk/xmllite/xmlconstants.h"
+#include "talk/xmllite/xmlelement.h"
+#include "talk/xmllite/xmlnsstack.h"
+#include "talk/xmpp/constants.h"
+#include "talk/xmpp/jid.h"
+#include "talk/base/helpers.h"
+#include "talk/base/thread.h"
+#include "talk/base/network.h"
+#include "talk/base/socketaddress.h"
+#include "talk/session/phone/phonesessionclient.h"
+
+#include <map>
+#include <string>
+#include <vector>
+
+using namespace buzz;
+
+
+#include <windows.h>
+#include <time.h>
+
+
+// Miranda headers
+#define MIRANDA_VER 0x0700
+#include <newpluginapi.h>
+#include <m_system.h>
+#include <m_system_cpp.h>
+#include <m_protocols.h>
+#include <m_protosvc.h>
+#include <m_contacts.h>
+#include <m_langpack.h>
+#include <m_database.h>
+#include <m_options.h>
+#include <m_utils.h>
+#include <m_updater.h>
+#include <m_popup.h>
+#include <m_message.h>
+#include "../../protocols/JabberG/m_jabber_plugin.h"
+#include "sdk/m_voice.h"
+#include "sdk/m_voiceservice.h"
+
+
+#include "../utils/mir_memory.h"
+#include "../utils/mir_options.h"
+
+
+// Global Variables
+extern HINSTANCE hInst;
+extern PLUGINLINK *pluginLink;
+
+class DATA;
+
+class SessionManagerTask : public sigslot::has_slots<> {
+ DATA *data;
+
+public:
+
+ SessionManagerTask(DATA *in) : data(in) {}
+ void OnOutgoingMessage(const XmlElement* stanza);
+};
+
+class CallClient;
+
+struct PendingIq {
+ time_t sent;
+
+ void (*callback)(DATA *data, void *param, IXmlNode *node);
+ void *param;
+};
+
+typedef std::map<int, PendingIq> PendingIqMap;
+
+class DATA
+{
+public:
+
+ JABBER_DATA *jabber;
+ TCHAR fullJID[512];
+
+ HANDLE hVoiceNotify;
+
+ talk_base::NetworkManager *network_manager_;
+ talk_base::Thread *worker_thread_;
+ talk_base::Thread *signaling_thread_;
+ cricket::HttpPortAllocator *port_allocator_;
+ SessionManagerTask *session_manager_task_;
+ cricket::SessionManager *session_manager_;
+ CallClient *call_client_;
+ buzz::XmlnsStack xmlnsstack_;
+
+ CRITICAL_SECTION csPendingIqMap;
+ PendingIqMap pendingIqs;
+
+ bool started;
+};
+
+#include "libjingle_callclient.h"
+
+
+
+#define MAX_REGS(_A_) ( sizeof(_A_) / sizeof(_A_[0]) )
+
+
+#define MIID_JINGLE { 0x9b9f6b3e, 0xd167, 0x4682, { 0x9b, 0x80, 0xef, 0xd0, 0x54, 0xa4, 0x85, 0xb1 } }
+
+
+#define JABBER_CAPS_MIRANDA_NODE "http://miranda-im.org/caps"
+#define JABBER_CAPS_GOOGLETALK_NODE "http://www.google.com/xmpp/client/caps"
+
+#define JABBER_EXT_JINGLE_VOICE "voice-v1"
+#define JABBER_EXT_JINGLE_SHARE "share-v1"
+
+#define JABBER_FEAT_JINGLE_VOICE "http://www.google.com/xmpp/protocol/voice/v1"
+#define JABBER_CAPS_JINGLE_VOICE ((JabberCapsBits)1<<33)
+#define JABBER_FEAT_JINGLE_SHARE "http://www.google.com/xmpp/protocol/share/v1"
+#define JABBER_CAPS_JINGLE_SHARE ((JabberCapsBits)1<<34)
+
+
+std::string ToString(const TCHAR *str);
+
+#endif // __COMMONS_H__
diff --git a/Plugins/jingle/jingle.cpp b/Plugins/jingle/jingle.cpp
new file mode 100644
index 0000000..58606bf
--- /dev/null
+++ b/Plugins/jingle/jingle.cpp
@@ -0,0 +1,869 @@
+/*
+Copyright (C) 2006 Ricardo Pescuma Domenecci
+
+ This is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this file; see the file license.txt. If
+ not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#include "commons.h"
+
+
+
+// Prototypes ///////////////////////////////////////////////////////////////////////////
+
+
+PLUGININFOEX pluginInfo={
+ sizeof(PLUGININFOEX),
+#ifdef UNICODE
+ "Jingle (Unicode)",
+#else
+ "Jingle",
+#endif
+ PLUGIN_MAKE_VERSION(0,0,0,1),
+ "Jingle support for Jabber protocol",
+ "Ricardo Pescuma Domenecci",
+ "",
+ "© 2007 Ricardo Pescuma Domenecci",
+ "http://pescuma.mirandaim.ru/miranda/jingle",
+ UNICODE_AWARE,
+ 0, //doesn't replace anything built-in
+#if defined( _UNICODE )
+ { 0xf34e6d64, 0xc12e, 0x4621, { 0x98, 0x87, 0xbe, 0xa2, 0x42, 0xed, 0xea, 0x8a } } // {F34E6D64-C12E-4621-9887-BEA242EDEA8A}
+#else
+ { 0xaee557dd, 0xbc7b, 0x46d9, { 0xad, 0xcc, 0x82, 0x2a, 0x3f, 0xa3, 0xa, 0x90 } } // {AEE557DD-BC7B-46d9-ADCC-822A3FA30A90}
+#endif
+};
+
+
+HINSTANCE hInst;
+PLUGINLINK *pluginLink;
+LIST_INTERFACE li;
+
+HANDLE hHooks[2] = {0};
+
+LIST<DATA> jabbers(2);
+
+int ModulesLoaded(WPARAM wParam, LPARAM lParam);
+int PreShutdown(WPARAM wParam, LPARAM lParam);
+
+void RegisterJabberPlugin(const char *proto);
+__inline static int ProtoServiceExists(const char *szModule,const char *szService);
+
+static XmlElement *ConvertToXmlElement(DATA *data, IXmlNode *jabberNode);
+
+
+
+// Functions ////////////////////////////////////////////////////////////////////////////
+
+
+extern "C" BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
+{
+ hInst = hinstDLL;
+ return TRUE;
+}
+
+
+extern "C" __declspec(dllexport) PLUGININFO* MirandaPluginInfo(DWORD mirandaVersion)
+{
+ pluginInfo.cbSize = sizeof(PLUGININFO);
+ return (PLUGININFO*) &pluginInfo;
+}
+
+
+extern "C" __declspec(dllexport) PLUGININFOEX* MirandaPluginInfoEx(DWORD mirandaVersion)
+{
+ pluginInfo.cbSize = sizeof(PLUGININFOEX);
+ return &pluginInfo;
+}
+
+
+static const MUUID interfaces[] = { MIID_JINGLE, MIID_LAST };
+extern "C" __declspec(dllexport) const MUUID* MirandaPluginInterfaces(void)
+{
+ return interfaces;
+}
+
+
+extern "C" int __declspec(dllexport) Load(PLUGINLINK *link)
+{
+ pluginLink = link;
+
+ init_mir_malloc();
+
+ li.cbSize = sizeof(li);
+ CallService(MS_SYSTEM_GET_LI, 0, (LPARAM) &li);
+
+ // hooks
+ hHooks[0] = HookEvent(ME_SYSTEM_MODULESLOADED, ModulesLoaded);
+ hHooks[1] = HookEvent(ME_SYSTEM_PRESHUTDOWN, PreShutdown);
+
+ return 0;
+}
+
+extern "C" int __declspec(dllexport) Unload(void)
+{
+ for (int i = 0; i < jabbers.getCount(); i++)
+ {
+ DATA *data = jabbers[i];
+
+ if (data->hVoiceNotify != NULL)
+ DestroyHookableEvent(data->hVoiceNotify);
+
+ if (data->call_client_ != NULL)
+ delete data->call_client_;
+ if (data->network_manager_ != NULL)
+ delete data->network_manager_;
+ if (data->port_allocator_ != NULL)
+ delete data->port_allocator_;
+ if (data->session_manager_ != NULL)
+ delete data->session_manager_;
+ if (data->session_manager_task_ != NULL)
+ delete data->session_manager_task_;
+ if (data->signaling_thread_ != NULL)
+ delete data->signaling_thread_;
+ if (data->worker_thread_ != NULL)
+ delete data->worker_thread_;
+
+ DeleteCriticalSection(&data->csPendingIqMap);
+
+ free(data);
+ }
+ return 0;
+}
+
+// Called when all the modules are loaded
+int ModulesLoaded(WPARAM wParam, LPARAM lParam)
+{
+ // updater plugin support
+ if(ServiceExists(MS_UPDATE_REGISTER))
+ {
+ Update upd = {0};
+ char szCurrentVersion[30];
+
+ upd.cbSize = sizeof(upd);
+ upd.szComponentName = pluginInfo.shortName;
+
+ upd.szUpdateURL = UPDATER_AUTOREGISTER;
+
+ upd.szBetaVersionURL = "http://pescuma.mirandaim.ru/miranda/jingle_version.txt";
+ upd.szBetaChangelogURL = "http://pescuma.mirandaim.ru/miranda/jingle#Changelog";
+ upd.pbBetaVersionPrefix = (BYTE *)"Jingle ";
+ upd.cpbBetaVersionPrefix = strlen((char *)upd.pbBetaVersionPrefix);
+#ifdef UNICODE
+ upd.szBetaUpdateURL = "http://pescuma.mirandaim.ru/miranda/jingleW.zip";
+#else
+ upd.szBetaUpdateURL = "http://pescuma.mirandaim.ru/miranda/jingle.zip";
+#endif
+
+ upd.pbVersion = (BYTE *)CreateVersionStringPlugin((PLUGININFO*) &pluginInfo, szCurrentVersion);
+ upd.cpbVersion = strlen((char *)upd.pbVersion);
+
+ CallService(MS_UPDATE_REGISTER, 0, (LPARAM)&upd);
+ }
+
+
+ if (!ServiceExists(MS_VOICESERVICE_REGISTER))
+ {
+ MessageBox(NULL, _T("Jingle needs Voice Service plugin to work."), _T("Jingle - Jabber plugin"), MB_OK | MB_ICONERROR);
+ return 0;
+ }
+
+ PROTOCOLDESCRIPTOR **protos;
+ int count;
+ CallService(MS_PROTO_ENUMPROTOCOLS, (WPARAM)&count, (LPARAM)&protos);
+ for (int i = 0; i < count; i++)
+ {
+ if (protos[i]->type != PROTOTYPE_PROTOCOL)
+ continue;
+
+ if (protos[i]->szName == NULL || protos[i]->szName[0] == '\0')
+ continue;
+
+ // Found a protocol
+ RegisterJabberPlugin(protos[i]->szName);
+ }
+
+ return 0;
+}
+
+
+int PreShutdown(WPARAM wParam, LPARAM lParam)
+{
+ for (int i = 0; i < MAX_REGS(hHooks); ++i)
+ if (hHooks[i] != NULL)
+ UnhookEvent(hHooks[i]);
+
+ for (int i = 0; i < jabbers.getCount(); i++)
+ {
+ DATA *data = jabbers[i];
+
+ if (data->hVoiceNotify != NULL)
+ DestroyHookableEvent(data->hVoiceNotify);
+ }
+
+ return 0;
+}
+
+
+static void replace(std::string &str, char *find, char *replace)
+{
+ int pos = str.find(find);
+ if (pos >= 0)
+ str.replace(pos, strlen(find), replace);
+}
+
+
+void HandleStanzaReturn(DATA *data, void *param, IXmlNode *jabberNode)
+{
+ XmlElement *newStanza = ConvertToXmlElement(data, jabberNode);
+
+// OutputDebugStringA("<IN>\n\t");
+// OutputDebugStringA(newStanza->Str().c_str());
+// OutputDebugStringA("\n</IN>\n");
+
+ ReplyMessageData *rmd = new ReplyMessageData();
+ rmd->oldStanza = (buzz::XmlElement *) param;
+ rmd->newStanza = newStanza;
+ data->signaling_thread_->Post(data->call_client_, MSG_REPLY_MSG, rmd);
+}
+
+
+void SessionManagerTask ::OnOutgoingMessage(const XmlElement* stanza) {
+ if (!data->started)
+ return;
+
+ if (stanza->Attr(buzz::QN_TYPE) == "set")
+ {
+ buzz::XmlElement *newOne = new buzz::XmlElement(*stanza);
+
+ int iqId;
+ if (!newOne->HasAttr(buzz::QN_ID))
+ {
+ char tmp[100];
+ iqId = data->jabber->pfIqSerialNext();
+ mir_snprintf(tmp, MAX_REGS(tmp), "mir_%d", iqId);
+ newOne->SetAttr(buzz::QN_ID, std::string(tmp));
+ }
+ else
+ {
+ iqId = atoi(newOne->Attr(buzz::QN_ID).c_str());
+ }
+
+ EnterCriticalSection(&data->csPendingIqMap);
+ PendingIq &piq = data->pendingIqs[iqId];
+ piq.sent = time(NULL);
+ piq.callback = HandleStanzaReturn;
+ piq.param = newOne;
+ LeaveCriticalSection(&data->csPendingIqMap);
+
+ std::string str = newOne->Str();
+
+// OutputDebugStringA("<OUT>\n\t");
+// OutputDebugStringA(str.c_str());
+// OutputDebugStringA("\n</OUT>\n");
+
+ data->jabber->pfSendString(str.c_str());
+ }
+ else
+ {
+ std::string str = stanza->Str();
+
+// OutputDebugStringA("<OUT>\n\t");
+// OutputDebugStringA(str.c_str());
+// OutputDebugStringA("\n</OUT>\n");
+
+ data->jabber->pfSendString(str.c_str());
+ }
+}
+
+
+static bool StartsWithXmlns(const char *name) {
+ return name[0] == 'x' &&
+ name[1] == 'm' &&
+ name[2] == 'l' &&
+ name[3] == 'n' &&
+ name[4] == 's';
+}
+
+static QName ResolveQName(buzz::XmlnsStack &xmlnsstack_, const char *qname, bool isAttr) {
+ const char *c;
+ for (c = qname; *c; ++c) {
+ if (*c == ':') {
+ const std::string * result;
+ result = xmlnsstack_.NsForPrefix(std::string(qname, c - qname));
+ if (result == NULL)
+ return QN_EMPTY;
+ const char * localname = c + 1;
+ return QName(*result, localname);
+ }
+ }
+ if (isAttr) {
+ return QName(STR_EMPTY, qname);
+ }
+
+ const std::string * result;
+ result = xmlnsstack_.NsForPrefix(STR_EMPTY);
+ if (result == NULL)
+ return QN_EMPTY;
+
+ return QName(*result, qname);
+}
+
+
+std::string ToString(const TCHAR *str)
+{
+ if (str == NULL)
+ return std::string();
+
+#ifdef UNICODE
+
+ size_t size = lstrlenW(str) + 1;
+ char *tmp = (char *) mir_alloc(size);
+
+ WideCharToMultiByte(CP_UTF8, 0, str, -1, tmp, size, NULL, NULL);
+
+ std::string ret(tmp);
+
+ mir_free(tmp);
+
+ return ret;
+
+#else
+
+ return std::string(str);
+
+#endif
+}
+
+
+static XmlElement *ConvertToXmlElement(DATA *data, buzz::XmlnsStack &xmlnsstack_, IXmlNode *jabberNode)
+{
+ xmlnsstack_.PushFrame();
+
+ for (int i=0; i < data->jabber->pfXmlGetNumAttr(jabberNode); i++)
+ {
+ const char *name = data->jabber->pfXmlGetAttrName(jabberNode, i);
+ if (StartsWithXmlns(name))
+ {
+ const TCHAR *value = data->jabber->pfXmlGetAttrValue(jabberNode, i);
+ if (name[5] == '\0')
+ {
+ xmlnsstack_.AddXmlns(STR_EMPTY, ToString(value));
+ }
+ else if (name[5] == ':')
+ {
+ xmlnsstack_.AddXmlns(std::string(name + 6), ToString(value));
+ }
+ }
+ }
+
+ QName tagName(ResolveQName(xmlnsstack_, data->jabber->pfXmlGetNodeName(jabberNode), false));
+ if (tagName == QN_EMPTY)
+ {
+ xmlnsstack_.PopFrame();
+ return NULL;
+ }
+
+ XmlElement * pelNew = new XmlElement(tagName);
+
+ for ( int i=0; i < data->jabber->pfXmlGetNumAttr(jabberNode); i++ )
+ {
+ QName attName(ResolveQName(xmlnsstack_, data->jabber->pfXmlGetAttrName(jabberNode, i), true));
+ if (attName == QN_EMPTY)
+ continue;
+
+ pelNew->AddAttr(attName, ToString(data->jabber->pfXmlGetAttrValue(jabberNode, i)));
+ }
+
+ const TCHAR *text = data->jabber->pfXmlGetNodeText(jabberNode);
+ if (text != NULL)
+ pelNew->AddText(ToString(text));
+
+ for ( int i=0; i < data->jabber->pfXmlGetNumChild(jabberNode); i++ )
+ {
+ XmlElement *tmp = ConvertToXmlElement(data, data->jabber->pfXmlGetChild(jabberNode, i));
+ if (tmp != NULL)
+ pelNew->AddElement(tmp);
+ }
+
+ xmlnsstack_.PopFrame();
+ return pelNew;
+}
+
+// Convert an XML element from our representation to libjingle's one
+static XmlElement *ConvertToXmlElement(DATA *data, IXmlNode *jabberNode)
+{
+ return ConvertToXmlElement(data, data->xmlnsstack_, jabberNode);
+}
+
+static int VoiceGetInfo(WPARAM wParam, LPARAM lParam, LPARAM param)
+{
+ DATA *data = jabbers[(int) param];
+ if (data == NULL)
+ return 0;
+
+ return VOICE_SUPPORTED | VOICE_CALL_CONTACT | VOICE_CALL_CONTACT_NEED_TEST;
+}
+
+static int ContactValidForVoice(WPARAM wParam, LPARAM lParam, LPARAM param)
+{
+ DATA *data = jabbers[(int) param];
+ if (data == NULL)
+ return FALSE;
+
+ if (lParam == FALSE)
+ return TRUE;
+
+ HANDLE hContact = (HANDLE) wParam;
+ if (hContact == NULL)
+ return FALSE;
+
+ DBVARIANT dbv;
+ if (DBGetContactSettingTString(hContact, data->jabber->protocolName, "jid", &dbv))
+ return FALSE;
+
+ TCHAR jid[512];
+ _tcsncpy(jid, dbv.ptszVal, MAX_REGS(jid));
+ DBFreeVariant(&dbv);
+
+ if (DBGetContactSettingWord(hContact, data->jabber->protocolName, "Status", ID_STATUS_OFFLINE) <= ID_STATUS_OFFLINE)
+ return FALSE;
+
+ return (data->jabber->pfGetResourceCapabilites(jid) & JABBER_CAPS_JINGLE_VOICE) == JABBER_CAPS_JINGLE_VOICE;
+}
+
+int VoiceCall(WPARAM wParam, LPARAM lParam, LPARAM param)
+{
+ if (wParam == NULL)
+ return -1;
+
+ DATA *data = jabbers[(int) param];
+ if (data == NULL)
+ return FALSE;
+
+ if (data->call_client_ != NULL)
+ data->call_client_->MakeCallTo((HANDLE) wParam);
+ return 0;
+}
+
+static int VoiceAnswer(WPARAM wParam, LPARAM lParam, LPARAM param)
+{
+ if (wParam == NULL)
+ return -1;
+
+ DATA *data = jabbers[(int) param];
+ if (data == NULL)
+ return FALSE;
+
+ if (data->call_client_ != NULL)
+ data->call_client_->AnswerCall(atoi((const char *) wParam));
+ return 0;
+}
+
+static int VoiceDrop(WPARAM wParam, LPARAM lParam, LPARAM param)
+{
+ if (wParam == NULL)
+ return -1;
+
+ DATA *data = jabbers[(int) param];
+ if (data == NULL)
+ return FALSE;
+
+ if (data->call_client_ != NULL)
+ data->call_client_->DropCall(atoi((const char *) wParam));
+ return 0;
+}
+
+
+VOID CALLBACK ExpireMessagesTimer(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
+{
+ DATA *data = jabbers[(int) idEvent];
+ if (data == NULL)
+ return;
+
+ data->signaling_thread_->Post(data->call_client_, MSG_TIMER);
+}
+
+
+void AssertLibjingleStarted(int id, DATA *data)
+{
+ if (data->started)
+ return;
+
+ // Libjingle stuff
+ data->network_manager_ = new talk_base::NetworkManager();
+ data->worker_thread_ = new talk_base::Thread();
+ data->signaling_thread_ = new talk_base::Thread();
+ data->port_allocator_ = new cricket::HttpPortAllocator(data->network_manager_, "call");
+ data->session_manager_task_ = new SessionManagerTask(data);
+ data->session_manager_ = new cricket::SessionManager(data->port_allocator_, data->worker_thread_, data->signaling_thread_);
+ data->session_manager_->SignalOutgoingMessage.connect(data->session_manager_task_, &SessionManagerTask::OnOutgoingMessage);
+
+ data->worker_thread_->Start();
+ data->signaling_thread_->Start();
+
+ //talk_base::ThreadManager::SetCurrent(signaling_thread_);
+
+ data->call_client_ = new CallClient(data);
+ data->session_manager_->SignalRequestSignaling.connect(data->call_client_, &CallClient::OnSignalingReady);
+
+ SetTimer(NULL, id, 1000, ExpireMessagesTimer);
+
+ data->started = true;
+}
+
+
+BOOL HandleIqReturn(DATA *data, IXmlNode *node, const TCHAR *id)
+{
+ //TCHAR *from = data->jabber->pfXmlGetAttrValueStr(node, "from");
+ //if (from == NULL)
+ // return FALSE;
+
+ if (lstrlen(id) < 5)
+ return FALSE;
+
+ int iqId = _ttoi(&id[4]);
+
+ EnterCriticalSection(&data->csPendingIqMap);
+
+ PendingIqMap::iterator it = data->pendingIqs.find(iqId);
+ if (it == data->pendingIqs.end())
+ {
+ LeaveCriticalSection(&data->csPendingIqMap);
+ return FALSE;
+ }
+
+ //XmlElement *oldStanza = it->second.stanza;
+ //if (oldStanza->Attr(QN_TO) != ToString(from))
+ //{
+ // LeaveCriticalSection( &csPendingIqMap );
+ // return FALSE;
+ //}
+
+ PendingIq iq = it->second;
+ data->pendingIqs.erase(it);
+
+ LeaveCriticalSection(&data->csPendingIqMap);
+
+ iq.callback(data, iq.param, node);
+ return TRUE;
+}
+
+
+BOOL HandleSet(DATA *data, IXmlNode *node, const TCHAR *id)
+{
+ IXmlNode *sessionNode = data->jabber->pfXmlGetChildWithGivenAttrValue(node, "session", "xmlns", _T("http://www.google.com/session"));
+ if (sessionNode == NULL)
+ return FALSE;
+
+ data->xmlnsstack_.Reset();
+ XmlElement *el = ConvertToXmlElement(data, node);
+ if (el == NULL)
+ return FALSE;
+
+// OutputDebugStringA("<IN>\n\t");
+// OutputDebugStringA(el->Str().c_str());
+// OutputDebugStringA("\n</IN>\n");
+
+ data->signaling_thread_->Post(data->call_client_, MSG_INCOMING_MSG, (talk_base::MessageData *) el);
+ return TRUE;
+}
+
+
+BOOL HandleGet(DATA *data, IXmlNode *node, const TCHAR *id)
+{
+ IXmlNode *query = data->jabber->pfXmlGetChildWithGivenAttrValue(node, "query", "xmlns", _T("http://jabber.org/protocol/disco#info"));
+ if (query == NULL)
+ return FALSE;
+
+ const TCHAR *feat = data->jabber->pfXmlGetAttrValueStr(query, "node");
+ if (feat == NULL)
+ return FALSE;
+
+ if (lstrcmp(feat, _T(JABBER_CAPS_MIRANDA_NODE) _T("#") _T(JABBER_EXT_JINGLE_VOICE)) != 0)
+ return FALSE;
+
+ const TCHAR *from = data->jabber->pfXmlGetAttrValueStr(node, "from");
+ if (from == NULL)
+ return FALSE;
+
+ IXmlNode *iq = data->jabber->pfXmlCreateNode("iq");
+ data->jabber->pfXmlAddAttr(iq, "type", _T("result"));
+ data->jabber->pfXmlAddAttr(iq, "to", from);
+ data->jabber->pfXmlAddAttr(iq, "id", id);
+
+ IXmlNode *q = data->jabber->pfXmlAddChild(iq, "query");
+ data->jabber->pfXmlAddAttr(q, "xmlns", _T("http://jabber.org/protocol/disco#info"));
+
+ char *ver = data->jabber->pfGetVersionText();
+ TCHAR capsVer[512];
+ mir_sntprintf(capsVer, MAX_REGS(capsVer), _T(JABBER_CAPS_MIRANDA_NODE) _T("#") _T(TCHAR_STR_PARAM), ver);
+ data->jabber->pfXmlAddAttr(q, "node", capsVer);
+
+ IXmlNode *f = data->jabber->pfXmlAddChild(q, "feature");
+ data->jabber->pfXmlAddAttr(f, "var", _T(JABBER_FEAT_JINGLE_VOICE));
+
+ data->jabber->pfSendNode(iq);
+
+ mir_free(ver);
+ return TRUE;
+}
+
+
+BOOL OnXmlReceived(void *param, IXmlNode *node)
+{
+ DATA *data = jabbers[(int) param];
+ if (data == NULL)
+ return FALSE;
+
+ if (!data->started)
+ return FALSE;
+
+ const char *name = data->jabber->pfXmlGetNodeName(node);
+ if (name == NULL || strcmp(name, "iq") != 0)
+ return FALSE;
+
+ const TCHAR *type = data->jabber->pfXmlGetAttrValueStr(node, "type");
+ if (type == NULL)
+ return FALSE;
+
+ const TCHAR * id = data->jabber->pfXmlGetAttrValueStr(node, "id");
+ if (id == NULL)
+ return FALSE;
+
+ if (_tcscmp(type, _T("result")) == 0 || _tcscmp(type, _T("error")) == 0)
+ return HandleIqReturn(data, node, id);
+
+ else if (_tcscmp(type, _T("set")) == 0)
+ return HandleSet(data, node, id);
+
+ else if (_tcscmp(type, _T("get")) == 0)
+ return HandleGet(data, node, id);
+
+ else
+ return FALSE;
+}
+
+void JingleInfoResult(DATA *data, void *param, IXmlNode *iqNode)
+{
+ const TCHAR *type = data->jabber->pfXmlGetAttrValueStr(iqNode, "type");
+ if (_tcscmp( type, _T("error")) == 0)
+ return;
+
+ IXmlNode *query = data->jabber->pfXmlGetChildWithGivenAttrValue(iqNode, "query", "xmlns", _T("google:jingleinfo"));
+ if (query == NULL)
+ return;
+
+ std::vector<std::string> relay_hosts;
+ std::vector<talk_base::SocketAddress> stun_hosts;
+ std::string relay_token;
+
+ IXmlNode *stun = data->jabber->pfXmlGetChildByName(query, "stun");
+ if (stun != NULL)
+ {
+ for (int i = 0; i < data->jabber->pfXmlGetNumChild(stun); i++)
+ {
+ IXmlNode *child = data->jabber->pfXmlGetChild(stun, i);
+ const char *name = data->jabber->pfXmlGetNodeName(child);
+ if (name != NULL && !strcmp("server", name))
+ {
+ std::string host = ToString(data->jabber->pfXmlGetAttrValueStr(child, "host"));
+ std::string port = ToString(data->jabber->pfXmlGetAttrValueStr(child, "udp"));
+ if (host.length() > 0 && port.length() > 0)
+ stun_hosts.push_back(talk_base::SocketAddress(host, atoi(port.c_str())));
+ }
+ }
+ }
+
+ IXmlNode *relay = data->jabber->pfXmlGetChildByName(query, "relay");
+ if (relay != NULL)
+ {
+ for (int i = 0; i < data->jabber->pfXmlGetNumChild(relay); i++)
+ {
+ IXmlNode *child = data->jabber->pfXmlGetChild(relay, i);
+ const char *name = data->jabber->pfXmlGetNodeName(child);
+
+ if (name == NULL)
+ continue;
+
+ if (strcmp("server", name) == 0)
+ {
+ std::string host = ToString(data->jabber->pfXmlGetAttrValueStr(child, "host"));
+ if (host.length() > 0)
+ relay_hosts.push_back(host);
+ }
+ else if (strcmp("token", name) == 0)
+ {
+ const TCHAR *text = data->jabber->pfXmlGetNodeText(child);
+ if (text != NULL && text[0] != '\0')
+ relay_token = ToString(text);
+ }
+ }
+ }
+
+ data->port_allocator_->SetStunHosts(stun_hosts);
+ data->port_allocator_->SetRelayHosts(relay_hosts);
+ data->port_allocator_->SetRelayToken(relay_token);
+}
+
+
+void OnConnect(void *param, TCHAR *fullJID)
+{
+ DATA *data = jabbers[(int) param];
+ if (data == NULL)
+ return;
+
+ AssertLibjingleStarted((int) param, data);
+
+ _tcsncpy(data->fullJID, fullJID, MAX_REGS(data->fullJID));
+ data->call_client_->OnConnect();
+
+ int iqId = data->jabber->pfIqSerialNext();
+
+ IXmlNode *iq = data->jabber->pfXmlCreateNode("iq");
+ data->jabber->pfXmlAddAttr(iq, "type", _T("get"));
+ data->jabber->pfXmlAddAttrID(iq, iqId);
+ IXmlNode *query = data->jabber->pfXmlAddChild(iq, "query");
+ data->jabber->pfXmlAddAttr(query, "xmlns", _T("google:jingleinfo"));
+
+ data->jabber->pfSendNode(iq);
+ data->jabber->pfXmlDeleteNode(iq);
+
+ EnterCriticalSection(&data->csPendingIqMap);
+ PendingIq &piq = data->pendingIqs[iqId];
+ piq.sent = time(NULL);
+ piq.callback = JingleInfoResult;
+ piq.param = 0;
+ LeaveCriticalSection(&data->csPendingIqMap);
+}
+
+void OnDisconnect(void *param)
+{
+ DATA *data = jabbers[(int) param];
+ if (data == NULL)
+ return;
+
+ if (!data->started)
+ return;
+
+ data->call_client_->OnDisconnect();
+ data->signaling_thread_->Post(data->call_client_, TERMINATE_ALL, NULL);
+}
+
+BOOL OnXmlSendNode(void *param, IXmlNode *node)
+{
+ DATA *data = jabbers[(int) param];
+ if (data == NULL)
+ return TRUE;
+
+ const char *name = data->jabber->pfXmlGetNodeName(node);
+ if (name == NULL || strcmp(name, "presence") != 0)
+ return TRUE;
+
+ IXmlNode *c = data->jabber->pfXmlGetChildByName(node, "c");
+ if (c == NULL)
+ return TRUE;
+
+ const TCHAR *ext = data->jabber->pfXmlGetAttrValueStr(c, "ext");
+ if (ext == NULL)
+ data->jabber->pfXmlAddAttr(c, "ext", _T(JABBER_EXT_JINGLE_VOICE));
+ else
+ {
+ TCHAR tmp[512];
+ mir_sntprintf(tmp, MAX_REGS(tmp), _T("%s %s"), ext, _T(JABBER_EXT_JINGLE_VOICE));
+ data->jabber->pfXmlSetAttrValueStr(c, "ext", tmp);
+ }
+
+ return TRUE;
+}
+
+
+void RegisterJabberPlugin(const char *proto)
+{
+ if (!ProtoServiceExists(proto, PS_REGISTER_JABBER_PLUGIN))
+ return;
+
+ int id = jabbers.getCount();
+ JABBER_PLUGIN_DATA info = {
+ sizeof(JABBER_PLUGIN_DATA),
+ "Jingle",
+ "Enable voice calls",
+ TRUE,
+ (void *) id,
+ OnConnect,
+ OnDisconnect,
+ OnXmlReceived,
+ NULL,
+ OnXmlSendNode,
+ NULL,
+ NULL
+ };
+
+
+ JABBER_DATA *jabber = (JABBER_DATA *) CallProtoService(proto, PS_REGISTER_JABBER_PLUGIN, (WPARAM) &info, 1);
+ if (jabber == NULL)
+ // We are disabled / ignored
+ return;
+
+ DATA *data = new DATA();
+ data->network_manager_ = NULL;
+ data->worker_thread_ = NULL;
+ data->signaling_thread_ = NULL;
+ data->port_allocator_ = NULL;
+ data->session_manager_task_ = NULL;
+ data->session_manager_ = NULL;
+ data->call_client_ = NULL;
+ data->started = false;
+
+ data->jabber = jabber;
+
+ jabbers.insert(data, jabbers.getCount());
+
+ InitializeCriticalSection(&data->csPendingIqMap);
+
+ // Voice service support
+ char tmp[256];
+
+ mir_snprintf(tmp, MAX_REGS(tmp), "%s" PE_VOICE_CALL_STATE, data->jabber->protocolName);
+ data->hVoiceNotify = CreateHookableEvent(tmp);
+
+ mir_snprintf(tmp, MAX_REGS(tmp), "%s" PS_VOICE_GETINFO, data->jabber->protocolName);
+ CreateServiceFunctionParam(tmp, VoiceGetInfo, id);
+
+ mir_snprintf(tmp, MAX_REGS(tmp), "%s" PS_VOICE_CALL, data->jabber->protocolName);
+ CreateServiceFunctionParam(tmp, VoiceCall, id);
+
+ mir_snprintf(tmp, MAX_REGS(tmp), "%s" PS_VOICE_ANSWERCALL, data->jabber->protocolName);
+ CreateServiceFunctionParam(tmp, VoiceAnswer, id);
+
+ mir_snprintf(tmp, MAX_REGS(tmp), "%s" PS_VOICE_DROPCALL, data->jabber->protocolName);
+ CreateServiceFunctionParam(tmp, VoiceDrop, id);
+
+ mir_snprintf(tmp, MAX_REGS(tmp), "%s" PS_VOICE_CALL_CONTACT_VALID, data->jabber->protocolName);
+ CreateServiceFunctionParam(tmp, ContactValidForVoice, id);
+
+ data->jabber->pfSetClientCaps(_T(JABBER_CAPS_MIRANDA_NODE), _T(JABBER_EXT_JINGLE_VOICE), JABBER_CAPS_JINGLE_VOICE);
+ data->jabber->pfSetClientCaps(_T(JABBER_CAPS_GOOGLETALK_NODE), _T(JABBER_EXT_JINGLE_VOICE), JABBER_CAPS_JINGLE_VOICE);
+ data->jabber->pfSetClientCaps(_T(JABBER_CAPS_GOOGLETALK_NODE), _T(JABBER_EXT_JINGLE_SHARE), JABBER_CAPS_JINGLE_SHARE);
+}
+
+
+__inline static int ProtoServiceExists(const char *szModule,const char *szService)
+{
+ char str[MAXMODULELABELLENGTH];
+ strcpy(str,szModule);
+ strcat(str,szService);
+ return ServiceExists(str);
+}
diff --git a/Plugins/jingle/jingle.sln b/Plugins/jingle/jingle.sln
new file mode 100644
index 0000000..fae28e3
--- /dev/null
+++ b/Plugins/jingle/jingle.sln
@@ -0,0 +1,39 @@
+
+Microsoft Visual Studio Solution File, Format Version 9.00
+# Visual Studio 2005
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "jingle", "jingle.vcproj", "{F21816C0-06BA-4FAA-B9F2-FB90B2BDC2DB}"
+ ProjectSection(ProjectDependencies) = postProject
+ {DC948D76-8503-490C-A07D-11044004FCE3} = {DC948D76-8503-490C-A07D-11044004FCE3}
+ EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libjingle", "libjingle\talk\libjingle.vcproj", "{DC948D76-8503-490C-A07D-11044004FCE3}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Win32 = Debug|Win32
+ Release|Win32 = Release|Win32
+ Unicode Debug|Win32 = Unicode Debug|Win32
+ Unicode Release|Win32 = Unicode Release|Win32
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {F21816C0-06BA-4FAA-B9F2-FB90B2BDC2DB}.Debug|Win32.ActiveCfg = Debug|Win32
+ {F21816C0-06BA-4FAA-B9F2-FB90B2BDC2DB}.Debug|Win32.Build.0 = Debug|Win32
+ {F21816C0-06BA-4FAA-B9F2-FB90B2BDC2DB}.Release|Win32.ActiveCfg = Release|Win32
+ {F21816C0-06BA-4FAA-B9F2-FB90B2BDC2DB}.Release|Win32.Build.0 = Release|Win32
+ {F21816C0-06BA-4FAA-B9F2-FB90B2BDC2DB}.Unicode Debug|Win32.ActiveCfg = Unicode Debug|Win32
+ {F21816C0-06BA-4FAA-B9F2-FB90B2BDC2DB}.Unicode Debug|Win32.Build.0 = Unicode Debug|Win32
+ {F21816C0-06BA-4FAA-B9F2-FB90B2BDC2DB}.Unicode Release|Win32.ActiveCfg = Unicode Release|Win32
+ {F21816C0-06BA-4FAA-B9F2-FB90B2BDC2DB}.Unicode Release|Win32.Build.0 = Unicode Release|Win32
+ {DC948D76-8503-490C-A07D-11044004FCE3}.Debug|Win32.ActiveCfg = Debug|Win32
+ {DC948D76-8503-490C-A07D-11044004FCE3}.Debug|Win32.Build.0 = Debug|Win32
+ {DC948D76-8503-490C-A07D-11044004FCE3}.Release|Win32.ActiveCfg = Release|Win32
+ {DC948D76-8503-490C-A07D-11044004FCE3}.Release|Win32.Build.0 = Release|Win32
+ {DC948D76-8503-490C-A07D-11044004FCE3}.Unicode Debug|Win32.ActiveCfg = Debug|Win32
+ {DC948D76-8503-490C-A07D-11044004FCE3}.Unicode Debug|Win32.Build.0 = Debug|Win32
+ {DC948D76-8503-490C-A07D-11044004FCE3}.Unicode Release|Win32.ActiveCfg = Release|Win32
+ {DC948D76-8503-490C-A07D-11044004FCE3}.Unicode Release|Win32.Build.0 = Release|Win32
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/Plugins/jingle/jingle.vcproj b/Plugins/jingle/jingle.vcproj
new file mode 100644
index 0000000..ec26f47
--- /dev/null
+++ b/Plugins/jingle/jingle.vcproj
@@ -0,0 +1,600 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="8,00"
+ Name="jingle"
+ ProjectGUID="{F21816C0-06BA-4FAA-B9F2-FB90B2BDC2DB}"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Unicode Release|Win32"
+ OutputDirectory=".\Unicode_Release"
+ IntermediateDirectory=".\Unicode_Release"
+ ConfigurationType="2"
+ InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC60.vsprops"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ PreprocessorDefinitions="NDEBUG"
+ MkTypLibCompatible="true"
+ SuppressStartupBanner="true"
+ TargetEnvironment="1"
+ TypeLibraryName=".\Unicode_Release/jingle.tlb"
+ HeaderFileName=""
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ InlineFunctionExpansion="0"
+ AdditionalIncludeDirectories="../../include,sdk,libjingle"
+ PreprocessorDefinitions="WIN32;W32;NDEBUG;_WINDOWS;UNICODE;_USRDLL;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE"
+ StringPooling="true"
+ RuntimeLibrary="0"
+ EnableFunctionLevelLinking="true"
+ PrecompiledHeaderFile=".\Unicode_Release/jingle.pch"
+ AssemblerListingLocation=".\Unicode_Release/"
+ ObjectFile=".\Unicode_Release/"
+ ProgramDataBaseFileName=".\Unicode_Release/"
+ BrowseInformation="0"
+ BrowseInformationFile=".\Unicode_Release/"
+ WarningLevel="3"
+ SuppressStartupBanner="true"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="NDEBUG"
+ Culture="1047"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalOptions="/ALIGN:4096 /filealign:0x200 /ignore:4108 "
+ AdditionalDependencies="mediastreamer2.lib Ws2_32.lib Secur32.lib Crypt32.lib Iphlpapi.lib odbc32.lib odbccp32.lib wsock32.lib version.lib comctl32.lib"
+ OutputFile="..\..\bin\release\Plugins\jingleW.dll"
+ LinkIncremental="1"
+ SuppressStartupBanner="true"
+ AdditionalLibraryDirectories="libjingle\talk\third_party\dll\"
+ GenerateDebugInformation="true"
+ ProgramDatabaseFile=".\Unicode_Release/jingleW.pdb"
+ GenerateMapFile="true"
+ MapFileName=".\Unicode_Release/jingleW.map"
+ BaseAddress="0x3EC20000"
+ ImportLibrary=".\Unicode_Release/jingleW.lib"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ SuppressStartupBanner="true"
+ OutputFile=".\Unicode_Release/jingle.bsc"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory=".\Debug"
+ IntermediateDirectory=".\Debug"
+ ConfigurationType="2"
+ InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC60.vsprops"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="2"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ PreprocessorDefinitions="NDEBUG"
+ MkTypLibCompatible="true"
+ SuppressStartupBanner="true"
+ TargetEnvironment="1"
+ TypeLibraryName=".\Debug/jingle.tlb"
+ HeaderFileName=""
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories="../../include,sdk,libjingle"
+ PreprocessorDefinitions="WIN32;W32;_DEBUG;_WINDOWS;_USRDLL;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE"
+ RuntimeLibrary="3"
+ PrecompiledHeaderFile=".\Debug/jingle.pch"
+ AssemblerListingLocation=".\Debug/"
+ ObjectFile=".\Debug/"
+ ProgramDataBaseFileName=".\Debug/"
+ BrowseInformation="1"
+ WarningLevel="3"
+ SuppressStartupBanner="true"
+ DebugInformationFormat="4"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="NDEBUG"
+ Culture="1047"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalOptions="/ALIGN:4096 /filealign:0x200 /ignore:4108 "
+ AdditionalDependencies="mediastreamer2.lib Ws2_32.lib Secur32.lib Crypt32.lib Iphlpapi.lib odbc32.lib odbccp32.lib wsock32.lib version.lib comctl32.lib"
+ OutputFile="..\..\bin\debug\Plugins\jingle.dll"
+ LinkIncremental="2"
+ SuppressStartupBanner="true"
+ AdditionalLibraryDirectories="libjingle\talk\third_party\dll\"
+ GenerateDebugInformation="true"
+ ProgramDatabaseFile=".\Debug/jingle.pdb"
+ BaseAddress="0x3EC20000"
+ ImportLibrary=".\Debug/jingle.lib"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ SuppressStartupBanner="true"
+ OutputFile=".\Debug/jingle.bsc"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Unicode Debug|Win32"
+ OutputDirectory=".\Unicode_Debug"
+ IntermediateDirectory=".\Unicode_Debug"
+ ConfigurationType="2"
+ InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC60.vsprops"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ PreprocessorDefinitions="NDEBUG"
+ MkTypLibCompatible="true"
+ SuppressStartupBanner="true"
+ TargetEnvironment="1"
+ TypeLibraryName=".\Unicode_Debug/jingle.tlb"
+ HeaderFileName=""
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories="../../include,sdk,libjingle"
+ PreprocessorDefinitions="WIN32;W32;_DEBUG;_WINDOWS;UNICODE;_USRDLL;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE"
+ RuntimeLibrary="3"
+ PrecompiledHeaderFile=".\Unicode_Debug/jingle.pch"
+ AssemblerListingLocation=".\Unicode_Debug/"
+ ObjectFile=".\Unicode_Debug/"
+ ProgramDataBaseFileName=".\Unicode_Debug/"
+ BrowseInformation="1"
+ WarningLevel="3"
+ SuppressStartupBanner="true"
+ DebugInformationFormat="4"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="NDEBUG"
+ Culture="1047"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalOptions="/ALIGN:4096 /filealign:0x200 /ignore:4108 "
+ AdditionalDependencies="mediastreamer2.lib Ws2_32.lib Secur32.lib Crypt32.lib Iphlpapi.lib odbc32.lib odbccp32.lib wsock32.lib version.lib comctl32.lib"
+ OutputFile="..\..\bin\debug unicode\Plugins\jingleW.dll"
+ LinkIncremental="2"
+ SuppressStartupBanner="true"
+ AdditionalLibraryDirectories="libjingle\talk\third_party\dll\"
+ GenerateDebugInformation="true"
+ ProgramDatabaseFile=".\Unicode_Debug/jingleW.pdb"
+ BaseAddress="0x3EC20000"
+ ImportLibrary=".\Unicode_Debug/jingleW.lib"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ SuppressStartupBanner="true"
+ OutputFile=".\Unicode_Debug/jingle.bsc"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory=".\Release"
+ IntermediateDirectory=".\Release"
+ ConfigurationType="2"
+ InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC60.vsprops"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="2"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ PreprocessorDefinitions="NDEBUG"
+ MkTypLibCompatible="true"
+ SuppressStartupBanner="true"
+ TargetEnvironment="1"
+ TypeLibraryName=".\Release/jingle.tlb"
+ HeaderFileName=""
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ InlineFunctionExpansion="0"
+ AdditionalIncludeDirectories="../../include,sdk,libjingle"
+ PreprocessorDefinitions="WIN32;W32;NDEBUG;_WINDOWS;_USRDLL;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE"
+ StringPooling="true"
+ RuntimeLibrary="0"
+ EnableFunctionLevelLinking="true"
+ PrecompiledHeaderFile=".\Release/jingle.pch"
+ AssemblerListingLocation=".\Release/"
+ ObjectFile=".\Release/"
+ ProgramDataBaseFileName=".\Release/"
+ BrowseInformation="2"
+ BrowseInformationFile=".\Release/"
+ WarningLevel="3"
+ SuppressStartupBanner="true"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="NDEBUG"
+ Culture="1047"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalOptions="/ALIGN:4096 /filealign:0x200 /ignore:4108 "
+ AdditionalDependencies="mediastreamer2.lib Ws2_32.lib Secur32.lib Crypt32.lib Iphlpapi.lib odbc32.lib odbccp32.lib wsock32.lib version.lib comctl32.lib"
+ OutputFile="..\..\bin\release\Plugins\jingle.dll"
+ LinkIncremental="1"
+ SuppressStartupBanner="true"
+ AdditionalLibraryDirectories="libjingle\talk\third_party\dll\"
+ GenerateDebugInformation="true"
+ ProgramDatabaseFile=".\Release/jingle.pdb"
+ GenerateMapFile="true"
+ MapFileName=".\Release/jingle.map"
+ BaseAddress="0x3EC20000"
+ ImportLibrary=".\Release/jingle.lib"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ SuppressStartupBanner="true"
+ OutputFile=".\Release/jingle.bsc"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Header Files"
+ Filter="h;hpp;hxx;hm;inl"
+ >
+ <File
+ RelativePath="commons.h"
+ >
+ </File>
+ <File
+ RelativePath=".\libjingle_callclient.h"
+ >
+ </File>
+ <File
+ RelativePath="..\utils\mir_memory.h"
+ >
+ </File>
+ <File
+ RelativePath="..\utils\mir_options.h"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Resource Files"
+ Filter="ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
+ >
+ </Filter>
+ <Filter
+ Name="Source Files"
+ Filter="cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+ >
+ <File
+ RelativePath="jingle.cpp"
+ >
+ <FileConfiguration
+ Name="Unicode Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Unicode Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath=".\libjingle_callclient.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\utils\mir_memory.cpp"
+ >
+ <FileConfiguration
+ Name="Unicode Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Unicode Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="..\utils\mir_options.cpp"
+ >
+ <FileConfiguration
+ Name="Unicode Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Unicode Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ </File>
+ </Filter>
+ <Filter
+ Name="Docs"
+ >
+ <File
+ RelativePath="Docs\jingle_changelog.txt"
+ >
+ </File>
+ <File
+ RelativePath="Docs\jingle_readme.txt"
+ >
+ </File>
+ <File
+ RelativePath="Docs\jingle_version.txt"
+ >
+ </File>
+ <File
+ RelativePath="Docs\langpack_jingle.txt"
+ >
+ </File>
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/Plugins/jingle/libjingle/COPYING b/Plugins/jingle/libjingle/COPYING
new file mode 100644
index 0000000..d11f105
--- /dev/null
+++ b/Plugins/jingle/libjingle/COPYING
@@ -0,0 +1,25 @@
+Copyright (c) 2004--2005, Google Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+ * The name of the author may not be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
+WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE. \ No newline at end of file
diff --git a/Plugins/jingle/libjingle/talk/base/Equifax_Secure_Global_eBusiness_CA-1.h b/Plugins/jingle/libjingle/talk/base/Equifax_Secure_Global_eBusiness_CA-1.h
new file mode 100644
index 0000000..6ff97a6
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/Equifax_Secure_Global_eBusiness_CA-1.h
@@ -0,0 +1,55 @@
+// This file is the Equifax Secure global eBusiness CA-1 certificate
+// in C form.
+
+// It was generated with the following command line:
+// > openssl x509 -in Equifax_Secure_Global_eBusiness_CA-1.cer -noout -C
+
+// The certificate was retrieved from:
+// http://www.geotrust.com/resources/root_certificates/certificates/Equifax_Secure_Global_eBusiness_CA-1.cer
+
+/* subject:/C=US/O=Equifax Secure Inc./CN=Equifax Secure Global eBusiness CA-1 */
+/* issuer :/C=US/O=Equifax Secure Inc./CN=Equifax Secure Global eBusiness CA-1 */
+unsigned char EquifaxSecureGlobalEBusinessCA1_certificate[660]={
+0x30,0x82,0x02,0x90,0x30,0x82,0x01,0xF9,0xA0,0x03,0x02,0x01,0x02,0x02,0x01,0x01,
+0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x04,0x05,0x00,0x30,
+0x5A,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x1C,
+0x30,0x1A,0x06,0x03,0x55,0x04,0x0A,0x13,0x13,0x45,0x71,0x75,0x69,0x66,0x61,0x78,
+0x20,0x53,0x65,0x63,0x75,0x72,0x65,0x20,0x49,0x6E,0x63,0x2E,0x31,0x2D,0x30,0x2B,
+0x06,0x03,0x55,0x04,0x03,0x13,0x24,0x45,0x71,0x75,0x69,0x66,0x61,0x78,0x20,0x53,
+0x65,0x63,0x75,0x72,0x65,0x20,0x47,0x6C,0x6F,0x62,0x61,0x6C,0x20,0x65,0x42,0x75,
+0x73,0x69,0x6E,0x65,0x73,0x73,0x20,0x43,0x41,0x2D,0x31,0x30,0x1E,0x17,0x0D,0x39,
+0x39,0x30,0x36,0x32,0x31,0x30,0x34,0x30,0x30,0x30,0x30,0x5A,0x17,0x0D,0x32,0x30,
+0x30,0x36,0x32,0x31,0x30,0x34,0x30,0x30,0x30,0x30,0x5A,0x30,0x5A,0x31,0x0B,0x30,
+0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x1C,0x30,0x1A,0x06,0x03,
+0x55,0x04,0x0A,0x13,0x13,0x45,0x71,0x75,0x69,0x66,0x61,0x78,0x20,0x53,0x65,0x63,
+0x75,0x72,0x65,0x20,0x49,0x6E,0x63,0x2E,0x31,0x2D,0x30,0x2B,0x06,0x03,0x55,0x04,
+0x03,0x13,0x24,0x45,0x71,0x75,0x69,0x66,0x61,0x78,0x20,0x53,0x65,0x63,0x75,0x72,
+0x65,0x20,0x47,0x6C,0x6F,0x62,0x61,0x6C,0x20,0x65,0x42,0x75,0x73,0x69,0x6E,0x65,
+0x73,0x73,0x20,0x43,0x41,0x2D,0x31,0x30,0x81,0x9F,0x30,0x0D,0x06,0x09,0x2A,0x86,
+0x48,0x86,0xF7,0x0D,0x01,0x01,0x01,0x05,0x00,0x03,0x81,0x8D,0x00,0x30,0x81,0x89,
+0x02,0x81,0x81,0x00,0xBA,0xE7,0x17,0x90,0x02,0x65,0xB1,0x34,0x55,0x3C,0x49,0xC2,
+0x51,0xD5,0xDF,0xA7,0xD1,0x37,0x8F,0xD1,0xE7,0x81,0x73,0x41,0x52,0x60,0x9B,0x9D,
+0xA1,0x17,0x26,0x78,0xAD,0xC7,0xB1,0xE8,0x26,0x94,0x32,0xB5,0xDE,0x33,0x8D,0x3A,
+0x2F,0xDB,0xF2,0x9A,0x7A,0x5A,0x73,0x98,0xA3,0x5C,0xE9,0xFB,0x8A,0x73,0x1B,0x5C,
+0xE7,0xC3,0xBF,0x80,0x6C,0xCD,0xA9,0xF4,0xD6,0x2B,0xC0,0xF7,0xF9,0x99,0xAA,0x63,
+0xA2,0xB1,0x47,0x02,0x0F,0xD4,0xE4,0x51,0x3A,0x12,0x3C,0x6C,0x8A,0x5A,0x54,0x84,
+0x70,0xDB,0xC1,0xC5,0x90,0xCF,0x72,0x45,0xCB,0xA8,0x59,0xC0,0xCD,0x33,0x9D,0x3F,
+0xA3,0x96,0xEB,0x85,0x33,0x21,0x1C,0x3E,0x1E,0x3E,0x60,0x6E,0x76,0x9C,0x67,0x85,
+0xC5,0xC8,0xC3,0x61,0x02,0x03,0x01,0x00,0x01,0xA3,0x66,0x30,0x64,0x30,0x11,0x06,
+0x09,0x60,0x86,0x48,0x01,0x86,0xF8,0x42,0x01,0x01,0x04,0x04,0x03,0x02,0x00,0x07,
+0x30,0x0F,0x06,0x03,0x55,0x1D,0x13,0x01,0x01,0xFF,0x04,0x05,0x30,0x03,0x01,0x01,
+0xFF,0x30,0x1F,0x06,0x03,0x55,0x1D,0x23,0x04,0x18,0x30,0x16,0x80,0x14,0xBE,0xA8,
+0xA0,0x74,0x72,0x50,0x6B,0x44,0xB7,0xC9,0x23,0xD8,0xFB,0xA8,0xFF,0xB3,0x57,0x6B,
+0x68,0x6C,0x30,0x1D,0x06,0x03,0x55,0x1D,0x0E,0x04,0x16,0x04,0x14,0xBE,0xA8,0xA0,
+0x74,0x72,0x50,0x6B,0x44,0xB7,0xC9,0x23,0xD8,0xFB,0xA8,0xFF,0xB3,0x57,0x6B,0x68,
+0x6C,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x04,0x05,0x00,
+0x03,0x81,0x81,0x00,0x30,0xE2,0x01,0x51,0xAA,0xC7,0xEA,0x5F,0xDA,0xB9,0xD0,0x65,
+0x0F,0x30,0xD6,0x3E,0xDA,0x0D,0x14,0x49,0x6E,0x91,0x93,0x27,0x14,0x31,0xEF,0xC4,
+0xF7,0x2D,0x45,0xF8,0xEC,0xC7,0xBF,0xA2,0x41,0x0D,0x23,0xB4,0x92,0xF9,0x19,0x00,
+0x67,0xBD,0x01,0xAF,0xCD,0xE0,0x71,0xFC,0x5A,0xCF,0x64,0xC4,0xE0,0x96,0x98,0xD0,
+0xA3,0x40,0xE2,0x01,0x8A,0xEF,0x27,0x07,0xF1,0x65,0x01,0x8A,0x44,0x2D,0x06,0x65,
+0x75,0x52,0xC0,0x86,0x10,0x20,0x21,0x5F,0x6C,0x6B,0x0F,0x6C,0xAE,0x09,0x1C,0xAF,
+0xF2,0xA2,0x18,0x34,0xC4,0x75,0xA4,0x73,0x1C,0xF1,0x8D,0xDC,0xEF,0xAD,0xF9,0xB3,
+0x76,0xB4,0x92,0xBF,0xDC,0x95,0x10,0x1E,0xBE,0xCB,0xC8,0x3B,0x5A,0x84,0x60,0x19,
+0x56,0x94,0xA9,0x55,
+};
diff --git a/Plugins/jingle/libjingle/talk/base/asyncfile.h b/Plugins/jingle/libjingle/talk/base/asyncfile.h
new file mode 100644
index 0000000..1437979
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/asyncfile.h
@@ -0,0 +1,56 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TALK_BASE_ASYNCFILE_H__
+#define TALK_BASE_ASYNCFILE_H__
+
+#include "talk/base/sigslot.h"
+
+namespace talk_base {
+
+// Provides the ability to perform file I/O asynchronously.
+// TODO: Create a common base class with AsyncSocket.
+class AsyncFile {
+public:
+ virtual ~AsyncFile() {}
+
+ // Determines whether the file will receive read events.
+ virtual bool readable() = 0;
+ virtual void set_readable(bool value) = 0;
+
+ // Determines whether the file will receive write events.
+ virtual bool writable() = 0;
+ virtual void set_writable(bool value) = 0;
+
+ sigslot::signal1<AsyncFile*> SignalReadEvent;
+ sigslot::signal1<AsyncFile*> SignalWriteEvent;
+ sigslot::signal2<AsyncFile*,int> SignalCloseEvent;
+};
+
+} // namespace talk_base
+
+#endif // TALK_BASE_ASYNCFILE_H__
diff --git a/Plugins/jingle/libjingle/talk/base/asynchttprequest.cc b/Plugins/jingle/libjingle/talk/base/asynchttprequest.cc
new file mode 100644
index 0000000..bf7bc85
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/asynchttprequest.cc
@@ -0,0 +1,155 @@
+#include "talk/base/common.h"
+#include "talk/base/firewallsocketserver.h"
+#include "talk/base/httpclient.h"
+#include "talk/base/logging.h"
+#include "talk/base/physicalsocketserver.h"
+#include "talk/base/socketadapters.h"
+#include "talk/base/socketpool.h"
+#include "talk/base/ssladapter.h"
+#include "talk/base/asynchttprequest.h"
+
+using namespace talk_base;
+
+///////////////////////////////////////////////////////////////////////////////
+// HttpMonitor
+///////////////////////////////////////////////////////////////////////////////
+
+HttpMonitor::HttpMonitor(SocketServer *ss) {
+ ASSERT(talk_base::Thread::Current() != NULL);
+ ss_ = ss;
+ reset();
+}
+
+void HttpMonitor::Connect(talk_base::HttpClient *http) {
+ http->SignalHttpClientComplete.connect(this,
+ &HttpMonitor::OnHttpClientComplete);
+}
+
+void HttpMonitor::OnHttpClientComplete(talk_base::HttpClient * http, int err) {
+ complete_ = true;
+ err_ = err;
+ ss_->WakeUp();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// SslSocketFactory
+///////////////////////////////////////////////////////////////////////////////
+
+talk_base::Socket * SslSocketFactory::CreateSocket(int type) {
+ return factory_->CreateSocket(type);
+}
+
+talk_base::AsyncSocket * SslSocketFactory::CreateAsyncSocket(int type) {
+ talk_base::AsyncSocket * socket = factory_->CreateAsyncSocket(type);
+ if (!socket)
+ return 0;
+
+ // Binary logging happens at the lowest level
+ if (!logging_label_.empty() && binary_mode_) {
+ socket = new talk_base::LoggingSocketAdapter(socket, logging_level_,
+ logging_label_.c_str(),
+ binary_mode_);
+ }
+
+ if (proxy_.type) {
+ talk_base::AsyncSocket * proxy_socket = 0;
+ if (proxy_.type == talk_base::PROXY_SOCKS5) {
+ proxy_socket = new talk_base::AsyncSocksProxySocket(socket, proxy_.address,
+ proxy_.username, proxy_.password);
+ } else {
+ // Note: we are trying unknown proxies as HTTPS currently
+ proxy_socket = new talk_base::AsyncHttpsProxySocket(socket,
+ agent_, proxy_.address,
+ proxy_.username, proxy_.password);
+ }
+ if (!proxy_socket) {
+ delete socket;
+ return 0;
+ }
+ socket = proxy_socket; // for our purposes the proxy is now the socket
+ }
+
+ if (!hostname_.empty()) {
+ talk_base::SSLAdapter * ssl_adapter = talk_base::SSLAdapter::Create(socket);
+ ssl_adapter->set_ignore_bad_cert(ignore_bad_cert_);
+ ssl_adapter->StartSSL(hostname_.c_str(), true);
+ socket = ssl_adapter;
+ }
+
+ // Regular logging occurs at the highest level
+ if (!logging_label_.empty() && !binary_mode_) {
+ socket = new talk_base::LoggingSocketAdapter(socket, logging_level_,
+ logging_label_.c_str(),
+ binary_mode_);
+ }
+ return socket;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// AsyncHttpRequest
+///////////////////////////////////////////////////////////////////////////////
+
+const int kDefaultHTTPTimeout = 30 * 1000; // 30 sec
+
+AsyncHttpRequest::AsyncHttpRequest(const std::string &user_agent)
+: firewall_(0), port_(80), secure_(false),
+ timeout_(kDefaultHTTPTimeout), fail_redirect_(false),
+ client_(user_agent.c_str(), NULL)
+{
+}
+
+void AsyncHttpRequest::DoWork() {
+ // TODO: Rewrite this to use the thread's native socket server, and a more
+ // natural flow?
+
+ talk_base::PhysicalSocketServer physical;
+ talk_base::SocketServer * ss = &physical;
+ if (firewall_) {
+ ss = new talk_base::FirewallSocketServer(ss, firewall_);
+ }
+
+ SslSocketFactory factory(ss, client_.agent());
+ factory.SetProxy(proxy_);
+ if (secure_)
+ factory.UseSSL(host_.c_str());
+
+ //factory.SetLogging("AsyncHttpRequest");
+
+ talk_base::ReuseSocketPool pool(&factory);
+ client_.set_pool(&pool);
+
+ bool transparent_proxy = (port_ == 80)
+ && ((proxy_.type == talk_base::PROXY_HTTPS)
+ || (proxy_.type == talk_base::PROXY_UNKNOWN));
+
+ if (transparent_proxy) {
+ client_.set_proxy(proxy_);
+ }
+ client_.set_fail_redirect(fail_redirect_);
+
+ talk_base::SocketAddress server(host_, port_);
+ client_.set_server(server);
+
+ HttpMonitor monitor(ss);
+ monitor.Connect(&client_);
+ client_.start();
+ ss->Wait(timeout_, true);
+ if (!monitor.done()) {
+ LOG(LS_INFO) << "AsyncHttpRequest request timed out";
+ client_.reset();
+ return;
+ }
+
+ if (monitor.error()) {
+ LOG(LS_INFO) << "AsyncHttpRequest request error: " << monitor.error();
+ if (monitor.error() == talk_base::HE_AUTH) {
+ //proxy_auth_required_ = true;
+ }
+ return;
+ }
+
+ std::string value;
+ if (client_.response().hasHeader(HH_LOCATION, &value)) {
+ response_redirect_ = value.c_str();
+ }
+}
diff --git a/Plugins/jingle/libjingle/talk/base/asynchttprequest.h b/Plugins/jingle/libjingle/talk/base/asynchttprequest.h
new file mode 100644
index 0000000..65bfa26
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/asynchttprequest.h
@@ -0,0 +1,140 @@
+#ifndef _ASYNCHTTPREQUEST_H_
+#define _ASYNCHTTPREQUEST_H_
+
+#include "talk/base/httpclient.h"
+#include "talk/base/logging.h"
+#include "talk/base/proxyinfo.h"
+#include "talk/base/socketserver.h"
+#include "talk/base/thread.h"
+#include "talk/base/signalthread.h"
+
+namespace talk_base {
+
+///////////////////////////////////////////////////////////////////////////////
+// AsyncHttpRequest
+// Performs an HTTP request on a background thread. Notifies on the foreground
+// thread once the request is done (successfully or unsuccessfully).
+///////////////////////////////////////////////////////////////////////////////
+
+class FirewallManager;
+class MemoryStream;
+
+class AsyncHttpRequest:
+ public SignalThread,
+ public sigslot::has_slots<> {
+public:
+ AsyncHttpRequest(const std::string &user_agent);
+
+ void set_proxy(const talk_base::ProxyInfo& proxy) {
+ proxy_ = proxy;
+ }
+ void set_firewall(talk_base::FirewallManager * firewall) {
+ firewall_ = firewall;
+ }
+
+ // The DNS name of the host to connect to.
+ const std::string& host() { return host_; }
+ void set_host(const std::string& host) { host_ = host; }
+
+ // The port to connect to on the target host.
+ int port() { return port_; }
+ void set_port(int port) { port_ = port; }
+
+ // Whether the request should use SSL.
+ bool secure() { return secure_; }
+ void set_secure(bool secure) { secure_ = secure; }
+
+ // Returns the redirect when redirection occurs
+ const std::string& response_redirect() { return response_redirect_; }
+
+ // Time to wait on the download, in ms. Default is 5000 (5s)
+ int timeout() { return timeout_; }
+ void set_timeout(int timeout) { timeout_ = timeout; }
+
+ // Fail redirects to allow analysis of redirect urls, etc.
+ bool fail_redirect() const { return fail_redirect_; }
+ void set_fail_redirect(bool fail_redirect) { fail_redirect_ = fail_redirect; }
+
+ HttpRequestData& request() { return client_.request(); }
+ HttpResponseData& response() { return client_.response(); }
+
+private:
+ // SignalThread Interface
+ virtual void DoWork();
+
+ talk_base::ProxyInfo proxy_;
+ talk_base::FirewallManager * firewall_;
+ std::string host_;
+ int port_;
+ bool secure_;
+ int timeout_;
+ bool fail_redirect_;
+ HttpClient client_;
+ std::string response_redirect_;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// HttpMonitor
+///////////////////////////////////////////////////////////////////////////////
+
+class HttpMonitor : public sigslot::has_slots<> {
+public:
+ HttpMonitor(SocketServer *ss);
+
+ void reset() { complete_ = false; }
+
+ bool done() const { return complete_; }
+ int error() const { return err_; }
+
+ void Connect(talk_base::HttpClient* http);
+ void OnHttpClientComplete(talk_base::HttpClient * http, int err);
+
+private:
+ bool complete_;
+ int err_;
+ SocketServer *ss_;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// SslSocketFactory
+///////////////////////////////////////////////////////////////////////////////
+
+class SslSocketFactory : public talk_base::SocketFactory {
+ public:
+ SslSocketFactory(talk_base::SocketFactory * factory, const std::string &user_agent)
+ : factory_(factory), logging_level_(talk_base::LS_VERBOSE),
+ binary_mode_(false), agent_(user_agent) { }
+
+ void UseSSL(const char * hostname) { hostname_ = hostname; }
+ void DisableSSL() { hostname_.clear(); }
+
+ void SetProxy(const talk_base::ProxyInfo& proxy) { proxy_ = proxy; }
+ const talk_base::ProxyInfo& proxy() const { return proxy_; }
+ bool ignore_bad_cert() {return ignore_bad_cert_;}
+ void SetIgnoreBadCert(bool ignore) { ignore_bad_cert_ = ignore; }
+
+ void SetLogging(talk_base::LoggingSeverity level, const std::string& label,
+ bool binary_mode = false) {
+ logging_level_ = level;
+ logging_label_ = label;
+ binary_mode_ = binary_mode;
+ }
+
+ virtual talk_base::Socket * CreateSocket(int type);
+ virtual talk_base::AsyncSocket * CreateAsyncSocket(int type);
+
+private:
+ talk_base::SocketFactory * factory_;
+ talk_base::ProxyInfo proxy_;
+ std::string hostname_, logging_label_;
+ talk_base::LoggingSeverity logging_level_;
+ bool binary_mode_;
+ std::string agent_;
+ bool ignore_bad_cert_;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+} // namespace talk_base_
+
+#endif // _ASYNCHTTPREQUEST_H_
diff --git a/Plugins/jingle/libjingle/talk/base/asyncpacketsocket.cc b/Plugins/jingle/libjingle/talk/base/asyncpacketsocket.cc
new file mode 100644
index 0000000..37ba058
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/asyncpacketsocket.cc
@@ -0,0 +1,84 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#if defined(_MSC_VER) && _MSC_VER < 1300
+#pragma warning(disable:4786)
+#endif
+
+#include "talk/base/asyncpacketsocket.h"
+
+namespace talk_base {
+
+AsyncPacketSocket::AsyncPacketSocket(AsyncSocket* socket) : socket_(socket) {
+}
+
+AsyncPacketSocket::~AsyncPacketSocket() {
+ delete socket_;
+}
+
+SocketAddress AsyncPacketSocket::GetLocalAddress() const {
+ return socket_->GetLocalAddress();
+}
+
+SocketAddress AsyncPacketSocket::GetRemoteAddress() const {
+ return socket_->GetRemoteAddress();
+}
+
+int AsyncPacketSocket::Bind(const SocketAddress& addr) {
+ return socket_->Bind(addr);
+}
+
+int AsyncPacketSocket::Connect(const SocketAddress& addr) {
+ return socket_->Connect(addr);
+}
+
+int AsyncPacketSocket::Send(const void *pv, size_t cb) {
+ return socket_->Send(pv, cb);
+}
+
+int AsyncPacketSocket::SendTo(
+ const void *pv, size_t cb, const SocketAddress& addr) {
+ return socket_->SendTo(pv, cb, addr);
+}
+
+int AsyncPacketSocket::Close() {
+ return socket_->Close();
+}
+
+int AsyncPacketSocket::SetOption(Socket::Option opt, int value) {
+ return socket_->SetOption(opt, value);
+}
+
+int AsyncPacketSocket::GetError() const {
+ return socket_->GetError();
+}
+
+void AsyncPacketSocket::SetError(int error) {
+ return socket_->SetError(error);
+}
+
+} // namespace talk_base
diff --git a/Plugins/jingle/libjingle/talk/base/asyncpacketsocket.h b/Plugins/jingle/libjingle/talk/base/asyncpacketsocket.h
new file mode 100644
index 0000000..c6172a7
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/asyncpacketsocket.h
@@ -0,0 +1,63 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TALK_BASE_ASYNCPACKETSOCKET_H__
+#define TALK_BASE_ASYNCPACKETSOCKET_H__
+
+#include "talk/base/asyncsocket.h"
+
+namespace talk_base {
+
+// Provides the ability to receive packets asynchronously. Sends are not
+// buffered since it is acceptable to drop packets under high load.
+class AsyncPacketSocket : public sigslot::has_slots<> {
+public:
+ AsyncPacketSocket(AsyncSocket* socket);
+ virtual ~AsyncPacketSocket();
+
+ // Relevant socket methods:
+ virtual SocketAddress GetLocalAddress() const;
+ virtual SocketAddress GetRemoteAddress() const;
+ virtual int Bind(const SocketAddress& addr);
+ virtual int Connect(const SocketAddress& addr);
+ virtual int Send(const void *pv, size_t cb);
+ virtual int SendTo(const void *pv, size_t cb, const SocketAddress& addr);
+ virtual int Close();
+ virtual int SetOption(Socket::Option opt, int value);
+ virtual int GetError() const;
+ virtual void SetError(int error);
+
+ // Emitted each time a packet is read.
+ sigslot::signal4<const char*, size_t, const SocketAddress&, AsyncPacketSocket*> SignalReadPacket;
+
+protected:
+ AsyncSocket* socket_;
+};
+
+} // namespace talk_base
+
+#endif // TALK_BASE_ASYNCPACKETSOCKET_H__
diff --git a/Plugins/jingle/libjingle/talk/base/asyncsocket.h b/Plugins/jingle/libjingle/talk/base/asyncsocket.h
new file mode 100644
index 0000000..ad0dfb4
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/asyncsocket.h
@@ -0,0 +1,91 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TALK_BASE_ASYNCSOCKET_H__
+#define TALK_BASE_ASYNCSOCKET_H__
+
+#include "talk/base/sigslot.h"
+#include "talk/base/socket.h"
+
+namespace talk_base {
+
+// Provides the ability to perform socket I/O asynchronously.
+class AsyncSocket : public Socket, public sigslot::has_slots<> {
+public:
+ virtual ~AsyncSocket() {}
+
+ sigslot::signal1<AsyncSocket*> SignalReadEvent; // ready to read
+ sigslot::signal1<AsyncSocket*> SignalWriteEvent; // ready to write
+ sigslot::signal1<AsyncSocket*> SignalConnectEvent; // connected
+ sigslot::signal2<AsyncSocket*,int> SignalCloseEvent; // closed
+ // TODO: error
+};
+
+class AsyncSocketAdapter : public AsyncSocket {
+public:
+ AsyncSocketAdapter(Socket * socket) : socket_(socket) {
+ }
+ AsyncSocketAdapter(AsyncSocket * socket) : socket_(socket) {
+ socket->SignalConnectEvent.connect(this, &AsyncSocketAdapter::OnConnectEvent);
+ socket->SignalReadEvent.connect(this, &AsyncSocketAdapter::OnReadEvent);
+ socket->SignalWriteEvent.connect(this, &AsyncSocketAdapter::OnWriteEvent);
+ socket->SignalCloseEvent.connect(this, &AsyncSocketAdapter::OnCloseEvent);
+ }
+ virtual ~AsyncSocketAdapter() { delete socket_; }
+
+ virtual SocketAddress GetLocalAddress() const { return socket_->GetLocalAddress(); }
+ virtual SocketAddress GetRemoteAddress() const { return socket_->GetRemoteAddress(); }
+
+ virtual int Bind(const SocketAddress& addr) { return socket_->Bind(addr); }
+ virtual int Connect(const SocketAddress& addr) {return socket_->Connect(addr); }
+ virtual int Send(const void *pv, size_t cb) { return socket_->Send(pv, cb); }
+ virtual int SendTo(const void *pv, size_t cb, const SocketAddress& addr) { return socket_->SendTo(pv, cb, addr); }
+ virtual int Recv(void *pv, size_t cb) { return socket_->Recv(pv, cb); }
+ virtual int RecvFrom(void *pv, size_t cb, SocketAddress *paddr) { return socket_->RecvFrom(pv, cb, paddr); }
+ virtual int Listen(int backlog) { return socket_->Listen(backlog); }
+ virtual Socket *Accept(SocketAddress *paddr) { return socket_->Accept(paddr); }
+ virtual int Close() { return socket_->Close(); }
+ virtual int GetError() const { return socket_->GetError(); }
+ virtual void SetError(int error) { return socket_->SetError(error); }
+
+ virtual ConnState GetState() const { return socket_->GetState(); }
+
+ virtual int EstimateMTU(uint16* mtu) { return socket_->EstimateMTU(mtu); }
+ virtual int SetOption(Option opt, int value) { return socket_->SetOption(opt, value); }
+
+protected:
+ virtual void OnConnectEvent(AsyncSocket * socket) { SignalConnectEvent(this); }
+ virtual void OnReadEvent(AsyncSocket * socket) { SignalReadEvent(this); }
+ virtual void OnWriteEvent(AsyncSocket * socket) { SignalWriteEvent(this); }
+ virtual void OnCloseEvent(AsyncSocket * socket, int err) { SignalCloseEvent(this, err); }
+
+ Socket * socket_;
+};
+
+} // namespace talk_base
+
+#endif // TALK_BASE_ASYNCSOCKET_H__
diff --git a/Plugins/jingle/libjingle/talk/base/asynctcpsocket.cc b/Plugins/jingle/libjingle/talk/base/asynctcpsocket.cc
new file mode 100644
index 0000000..84d1401
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/asynctcpsocket.cc
@@ -0,0 +1,200 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#if defined(_MSC_VER) && _MSC_VER < 1300
+#pragma warning(disable:4786)
+#endif
+
+#include "talk/base/asynctcpsocket.h"
+#include "talk/base/byteorder.h"
+#include "talk/base/common.h"
+#include "talk/base/logging.h"
+
+#if defined(_MSC_VER) && _MSC_VER < 1300
+namespace std {
+ using ::strerror;
+}
+#endif
+
+#ifdef POSIX
+extern "C" {
+#include <errno.h>
+}
+#endif // POSIX
+
+namespace talk_base {
+
+const size_t MAX_PACKET_SIZE = 64 * 1024;
+
+typedef uint16 PacketLength;
+const size_t PKT_LEN_SIZE = sizeof(PacketLength);
+
+const size_t BUF_SIZE = MAX_PACKET_SIZE + PKT_LEN_SIZE;
+
+AsyncTCPSocket::AsyncTCPSocket(AsyncSocket* socket) : AsyncPacketSocket(socket), insize_(BUF_SIZE), inpos_(0), outsize_(BUF_SIZE), outpos_(0) {
+ inbuf_ = new char[insize_];
+ outbuf_ = new char[outsize_];
+
+ ASSERT(socket_ != NULL);
+ socket_->SignalConnectEvent.connect(this, &AsyncTCPSocket::OnConnectEvent);
+ socket_->SignalReadEvent.connect(this, &AsyncTCPSocket::OnReadEvent);
+ socket_->SignalWriteEvent.connect(this, &AsyncTCPSocket::OnWriteEvent);
+ socket_->SignalCloseEvent.connect(this, &AsyncTCPSocket::OnCloseEvent);
+}
+
+AsyncTCPSocket::~AsyncTCPSocket() {
+ delete [] inbuf_;
+ delete [] outbuf_;
+}
+
+int AsyncTCPSocket::Send(const void *pv, size_t cb) {
+ if (cb > MAX_PACKET_SIZE) {
+ socket_->SetError(EMSGSIZE);
+ return -1;
+ }
+
+ // If we are blocking on send, then silently drop this packet
+ if (outpos_)
+ return static_cast<int>(cb);
+
+ PacketLength pkt_len = HostToNetwork16(static_cast<PacketLength>(cb));
+ memcpy(outbuf_, &pkt_len, PKT_LEN_SIZE);
+ memcpy(outbuf_ + PKT_LEN_SIZE, pv, cb);
+ outpos_ = PKT_LEN_SIZE + cb;
+
+ int res = Flush();
+ if (res <= 0) {
+ // drop packet if we made no progress
+ outpos_ = 0;
+ return res;
+ }
+
+ // We claim to have sent the whole thing, even if we only sent partial
+ return static_cast<int>(cb);
+}
+
+int AsyncTCPSocket::SendTo(const void *pv, size_t cb, const SocketAddress& addr) {
+ if (addr == GetRemoteAddress())
+ return Send(pv, cb);
+
+ ASSERT(false);
+ socket_->SetError(ENOTCONN);
+ return -1;
+}
+
+int AsyncTCPSocket::SendRaw(const void * pv, size_t cb) {
+ if (outpos_ + cb > outsize_) {
+ socket_->SetError(EMSGSIZE);
+ return -1;
+ }
+
+ memcpy(outbuf_ + outpos_, pv, cb);
+ outpos_ += cb;
+
+ return Flush();
+}
+
+void AsyncTCPSocket::ProcessInput(char * data, size_t& len) {
+ SocketAddress remote_addr(GetRemoteAddress());
+
+ while (true) {
+ if (len < PKT_LEN_SIZE)
+ return;
+
+ PacketLength pkt_len;
+ memcpy(&pkt_len, data, PKT_LEN_SIZE);
+ pkt_len = NetworkToHost16(pkt_len);
+
+ if (len < PKT_LEN_SIZE + pkt_len)
+ return;
+
+ SignalReadPacket(data + PKT_LEN_SIZE, pkt_len, remote_addr, this);
+
+ len -= PKT_LEN_SIZE + pkt_len;
+ if (len > 0) {
+ memmove(data, data + PKT_LEN_SIZE + pkt_len, len);
+ }
+ }
+}
+
+int AsyncTCPSocket::Flush() {
+ int res = socket_->Send(outbuf_, outpos_);
+ if (res <= 0) {
+ return res;
+ }
+ if (static_cast<size_t>(res) <= outpos_) {
+ outpos_ -= res;
+ } else {
+ ASSERT(false);
+ return -1;
+ }
+ if (outpos_ > 0) {
+ memmove(outbuf_, outbuf_ + res, outpos_);
+ }
+ return res;
+}
+
+void AsyncTCPSocket::OnConnectEvent(AsyncSocket* socket) {
+ SignalConnect(this);
+}
+
+void AsyncTCPSocket::OnReadEvent(AsyncSocket* socket) {
+ ASSERT(socket == socket_);
+
+ int len = socket_->Recv(inbuf_ + inpos_, insize_ - inpos_);
+ if (len < 0) {
+ // TODO: Do something better like forwarding the error to the user.
+ if (!socket_->IsBlocking()) {
+ LOG(LS_ERROR) << "recvfrom: " << errno << " " << std::strerror(errno);
+ }
+ return;
+ }
+
+ inpos_ += len;
+
+ ProcessInput(inbuf_, inpos_);
+
+ if (inpos_ >= insize_) {
+ LOG(INFO) << "input buffer overflow";
+ ASSERT(false);
+ inpos_ = 0;
+ }
+}
+
+void AsyncTCPSocket::OnWriteEvent(AsyncSocket* socket) {
+ ASSERT(socket == socket_);
+
+ if (outpos_ > 0) {
+ Flush();
+ }
+}
+
+void AsyncTCPSocket::OnCloseEvent(AsyncSocket* socket, int error) {
+ SignalClose(this, error);
+}
+
+} // namespace talk_base
diff --git a/Plugins/jingle/libjingle/talk/base/asynctcpsocket.h b/Plugins/jingle/libjingle/talk/base/asynctcpsocket.h
new file mode 100644
index 0000000..2509451
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/asynctcpsocket.h
@@ -0,0 +1,68 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TALK_BASE_ASYNCTCPSOCKET_H__
+#define TALK_BASE_ASYNCTCPSOCKET_H__
+
+#include "talk/base/asyncpacketsocket.h"
+
+namespace talk_base {
+
+// Simulates UDP semantics over TCP. Send and Recv packet sizes
+// are preserved, and drops packets silently on Send, rather than
+// buffer them in user space.
+class AsyncTCPSocket : public AsyncPacketSocket {
+public:
+ AsyncTCPSocket(AsyncSocket* socket);
+ virtual ~AsyncTCPSocket();
+
+ virtual int Send(const void *pv, size_t cb);
+ virtual int SendTo(const void *pv, size_t cb, const SocketAddress& addr);
+
+ sigslot::signal1<AsyncTCPSocket*> SignalConnect;
+ sigslot::signal2<AsyncTCPSocket*,int> SignalClose;
+
+protected:
+ int SendRaw(const void * pv, size_t cb);
+ virtual void ProcessInput(char * data, size_t& len);
+
+private:
+ char* inbuf_, * outbuf_;
+ size_t insize_, inpos_, outsize_, outpos_;
+
+ int Flush();
+
+ // Called by the underlying socket
+ void OnConnectEvent(AsyncSocket* socket);
+ void OnReadEvent(AsyncSocket* socket);
+ void OnWriteEvent(AsyncSocket* socket);
+ void OnCloseEvent(AsyncSocket* socket, int error);
+};
+
+} // namespace talk_base
+
+#endif // TALK_BASE_ASYNCTCPSOCKET_H__
diff --git a/Plugins/jingle/libjingle/talk/base/asyncudpsocket.cc b/Plugins/jingle/libjingle/talk/base/asyncudpsocket.cc
new file mode 100644
index 0000000..a0c967d
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/asyncudpsocket.cc
@@ -0,0 +1,85 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#if defined(_MSC_VER) && _MSC_VER < 1300
+#pragma warning(disable:4786)
+#endif
+
+#include <cassert>
+#include <cstring>
+#include <iostream>
+
+#ifdef POSIX
+extern "C" {
+#include <errno.h>
+}
+#endif // POSIX
+
+#include "talk/base/asyncudpsocket.h"
+#include "talk/base/logging.h"
+
+#if defined(_MSC_VER) && _MSC_VER < 1300
+namespace std {
+ using ::strerror;
+}
+#endif
+
+namespace talk_base {
+
+const int BUF_SIZE = 64 * 1024;
+
+AsyncUDPSocket::AsyncUDPSocket(AsyncSocket* socket) : AsyncPacketSocket(socket) {
+ size_ = BUF_SIZE;
+ buf_ = new char[size_];
+
+ assert(socket_);
+ // The socket should start out readable but not writable.
+ socket_->SignalReadEvent.connect(this, &AsyncUDPSocket::OnReadEvent);
+}
+
+AsyncUDPSocket::~AsyncUDPSocket() {
+ delete [] buf_;
+}
+
+void AsyncUDPSocket::OnReadEvent(AsyncSocket* socket) {
+ assert(socket == socket_);
+
+ SocketAddress remote_addr;
+ int len = socket_->RecvFrom(buf_, size_, &remote_addr);
+ if (len < 0) {
+ // TODO: Do something better like forwarding the error to the user.
+ PLOG(LS_ERROR, socket_->GetError()) << "recvfrom";
+ return;
+ }
+
+ // TODO: Make sure that we got all of the packet. If we did not, then we
+ // should resize our buffer to be large enough.
+
+ SignalReadPacket(buf_, (size_t)len, remote_addr, this);
+}
+
+} // namespace talk_base
diff --git a/Plugins/jingle/libjingle/talk/base/asyncudpsocket.h b/Plugins/jingle/libjingle/talk/base/asyncudpsocket.h
new file mode 100644
index 0000000..8232938
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/asyncudpsocket.h
@@ -0,0 +1,59 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TALK_BASE_ASYNCUDPSOCKET_H__
+#define TALK_BASE_ASYNCUDPSOCKET_H__
+
+#include "talk/base/asyncpacketsocket.h"
+#include "talk/base/socketfactory.h"
+
+namespace talk_base {
+
+// Provides the ability to receive packets asynchronously. Sends are not
+// buffered since it is acceptable to drop packets under high load.
+class AsyncUDPSocket : public AsyncPacketSocket {
+public:
+ AsyncUDPSocket(AsyncSocket* socket);
+ virtual ~AsyncUDPSocket();
+
+private:
+ char* buf_;
+ size_t size_;
+
+ // Called when the underlying socket is ready to be read from.
+ void OnReadEvent(AsyncSocket* socket);
+};
+
+// Creates a new socket for sending asynchronous UDP packets using an
+// asynchronous socket from the given factory.
+inline AsyncUDPSocket* CreateAsyncUDPSocket(SocketFactory* factory) {
+ return new AsyncUDPSocket(factory->CreateAsyncSocket(SOCK_DGRAM));
+}
+
+} // namespace talk_base
+
+#endif // TALK_BASE_ASYNCUDPSOCKET_H__
diff --git a/Plugins/jingle/libjingle/talk/base/autodetectproxy.cc b/Plugins/jingle/libjingle/talk/base/autodetectproxy.cc
new file mode 100644
index 0000000..3b8e6f6
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/autodetectproxy.cc
@@ -0,0 +1,178 @@
+/*
+ * libjingle
+ * Copyright 2004--2006, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "talk/base/autodetectproxy.h"
+#include "talk/base/httpcommon.h"
+//#include "talk/xmpp/xmppclientsettings.h"
+#include "talk/base/proxydetect.h"
+
+using namespace talk_base;
+
+enum { MSG_TIMEOUT = SignalThread::ST_MSG_FIRST_AVAILABLE };
+
+const talk_base::ProxyType TEST_ORDER[] = {
+ talk_base::PROXY_HTTPS, /*talk_base::PROXY_SOCKS5,*/ talk_base::PROXY_UNKNOWN
+};
+
+AutoDetectProxy::AutoDetectProxy(const std::string& user_agent)
+: agent_(user_agent), socket_(NULL), next_(0)
+{
+}
+
+AutoDetectProxy::~AutoDetectProxy() {
+ delete socket_;
+}
+
+void AutoDetectProxy::DoWork() {
+ if (!server_url_.empty()) {
+ LOG(LS_INFO) << "GetProxySettingsForUrl(" << server_url_ << ") - start";
+ GetProxySettingsForUrl(agent_.c_str(), server_url_.c_str(), proxy_, true);
+ LOG(LS_INFO) << "GetProxySettingsForUrl - stop";
+ }
+ talk_base::Url<char> url(proxy_.address.IPAsString());
+ if (url.valid()) {
+ LOG(LS_WARNING) << "AutoDetectProxy removing http prefix on proxy host";
+ proxy_.address.SetIP(url.server());
+ }
+ if (proxy_.type == talk_base::PROXY_UNKNOWN) {
+ //LOG(LS_INFO) << "Proxy classification start";
+ Next();
+ // Process I/O until Stop()
+ Thread::Current()->ProcessMessages(kForever);
+ }
+}
+
+void AutoDetectProxy::OnMessage(Message *msg) {
+ if (MSG_TIMEOUT == msg->message_id) {
+ OnCloseEvent(socket_, ETIMEDOUT);
+ } else {
+ SignalThread::OnMessage(msg);
+ }
+}
+
+void AutoDetectProxy::Next() {
+ if (TEST_ORDER[next_] >= talk_base::PROXY_UNKNOWN) {
+ Complete(talk_base::PROXY_UNKNOWN);
+ return;
+ }
+
+ LOG(LS_VERBOSE) << "AutoDetectProxy connecting to "
+ << proxy_.address.ToString();
+
+ if (socket_) {
+ Thread::Current()->Clear(this, MSG_TIMEOUT);
+ socket_->Close();
+ Thread::Current()->Dispose(socket_);
+ socket_ = NULL;
+ }
+
+ socket_ = Thread::Current()->socketserver()->CreateAsyncSocket(SOCK_STREAM);
+ socket_->SignalConnectEvent.connect(this, &AutoDetectProxy::OnConnectEvent);
+ socket_->SignalReadEvent.connect(this, &AutoDetectProxy::OnReadEvent);
+ socket_->SignalCloseEvent.connect(this, &AutoDetectProxy::OnCloseEvent);
+ socket_->Connect(proxy_.address);
+
+ // Timeout after 2 seconds
+ Thread::Current()->PostDelayed(2000, this, MSG_TIMEOUT);
+}
+
+void AutoDetectProxy::Complete(talk_base::ProxyType type) {
+ Thread::Current()->Clear(this, MSG_TIMEOUT);
+ socket_->Close();
+
+ proxy_.type = type;
+ talk_base::LoggingSeverity sev
+ = (proxy_.type == talk_base::PROXY_UNKNOWN)
+ ? talk_base::LS_ERROR : talk_base::LS_VERBOSE;
+ LOG_V(sev) << "AutoDetectProxy detected " << proxy_.address.ToString()
+ << " as type " << proxy_.type;
+
+ Thread::Current()->MessageQueue::Stop();
+}
+
+void AutoDetectProxy::OnConnectEvent(talk_base::AsyncSocket * socket) {
+ std::string probe;
+
+ switch (TEST_ORDER[next_]) {
+ case talk_base::PROXY_HTTPS:
+ probe.assign("\005\001"
+ "CONNECT www.google.com:443 HTTP/1.0\r\n"
+ "User-Agent: ");
+ probe.append(agent_);
+ probe.append("\r\n"
+ "Host: www.google.com\r\n"
+ "Content-Length: 0\r\n"
+ "Proxy-Connection: Keep-Alive\r\n"
+ "\r\n");
+ //probe = "CONNECT www.google.com:443 HTTP/1.0\r\n\r\n";
+ break;
+ case talk_base::PROXY_SOCKS5:
+ probe.assign("\005\001\000", 3);
+ break;
+ }
+
+ LOG(LS_VERBOSE) << "AutoDetectProxy probing type " << TEST_ORDER[next_]
+ << " sending " << probe.size() << " bytes";
+ socket_->Send(probe.data(), probe.size());
+}
+
+void AutoDetectProxy::OnReadEvent(talk_base::AsyncSocket * socket) {
+ char data[257];
+ int len = socket_->Recv(data, 256);
+ if (len > 0) {
+ data[len] = 0;
+ LOG(LS_VERBOSE) << "AutoDetectProxy read " << len << " bytes";
+ }
+
+ switch (TEST_ORDER[next_]) {
+ case talk_base::PROXY_HTTPS:
+ if ((len >= 2) && (data[0] == '\x05')) {
+ Complete(talk_base::PROXY_SOCKS5);
+ return;
+ }
+ if ((len >= 5) && (strncmp(data, "HTTP/", 5) == 0)) {
+ Complete(talk_base::PROXY_HTTPS);
+ return;
+ }
+ break;
+ case talk_base::PROXY_SOCKS5:
+ if ((len >= 2) && (data[0] == '\x05')) {
+ Complete(talk_base::PROXY_SOCKS5);
+ return;
+ }
+ break;
+ }
+
+ ++next_;
+ Next();
+}
+
+void AutoDetectProxy::OnCloseEvent(talk_base::AsyncSocket * socket, int error) {
+ LOG(LS_VERBOSE) << "AutoDetectProxy closed with error: " << error;
+ ++next_;
+ Next();
+}
diff --git a/Plugins/jingle/libjingle/talk/base/autodetectproxy.h b/Plugins/jingle/libjingle/talk/base/autodetectproxy.h
new file mode 100644
index 0000000..9633d31
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/autodetectproxy.h
@@ -0,0 +1,68 @@
+#ifndef _AUTODETECTPROXY_H_
+#define _AUTODETECTPROXY_H_
+
+#include "talk/base/sigslot.h"
+#include "talk/base/physicalsocketserver.h"
+#include "talk/base/proxyinfo.h"
+#include "talk/base/signalthread.h"
+#include "talk/base/cryptstring.h"
+
+namespace buzz {
+class XmppClientSettings;
+}
+
+namespace talk_base {
+
+///////////////////////////////////////////////////////////////////////////////
+// AutoDetectProxy
+///////////////////////////////////////////////////////////////////////////////
+
+class AsyncSocket;
+
+class AutoDetectProxy : public SignalThread, public sigslot::has_slots<> {
+public:
+ AutoDetectProxy(const std::string& user_agent);
+
+ const talk_base::ProxyInfo& proxy() const { return proxy_; }
+
+ void set_server_url(const std::string& url) {
+ server_url_ = url;
+ }
+ void set_proxy(const SocketAddress& proxy) {
+ proxy_.type = PROXY_UNKNOWN;
+ proxy_.address = proxy;
+ }
+ void set_auth_info(bool use_auth, const std::string& username,
+ const CryptString& password) {
+ if (use_auth) {
+ proxy_.username = username;
+ proxy_.password = password;
+ }
+ }
+
+protected:
+ virtual ~AutoDetectProxy();
+
+ // SignalThread Interface
+ virtual void DoWork();
+ virtual void OnMessage(Message *msg);
+
+ void Next();
+ void Complete(ProxyType type);
+
+ void OnConnectEvent(AsyncSocket * socket);
+ void OnReadEvent(AsyncSocket * socket);
+ void OnCloseEvent(AsyncSocket * socket, int error);
+
+private:
+ std::string agent_, server_url_;
+ ProxyInfo proxy_;
+ AsyncSocket * socket_;
+ int next_;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+} // namespace talk_base
+
+#endif // _AUTODETECTPROXY_H_
diff --git a/Plugins/jingle/libjingle/talk/base/base64.cc b/Plugins/jingle/libjingle/talk/base/base64.cc
new file mode 100644
index 0000000..53e5bac
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/base64.cc
@@ -0,0 +1,196 @@
+
+//*********************************************************************
+//* Base64 - a simple base64 encoder and decoder.
+//*
+//* Copyright (c) 1999, Bob Withers - bwit@pobox.com
+//*
+//* This code may be freely used for any purpose, either personal
+//* or commercial, provided the authors copyright notice remains
+//* intact.
+//*
+//* Enhancements by Stanley Yamane:
+//* o reverse lookup table for the decode function
+//* o reserve string buffer space in advance
+//*
+//*********************************************************************
+
+#include "talk/base/base64.h"
+
+using std::string;
+
+namespace talk_base {
+
+static const char fillchar = '=';
+static const string::size_type np = string::npos;
+
+const string Base64::Base64Table(
+ // 0000000000111111111122222222223333333333444444444455555555556666
+ // 0123456789012345678901234567890123456789012345678901234567890123
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/");
+
+// Decode Table gives the index of any valid base64 character in the
+// Base64 table
+// 65 == A, 97 == a, 48 == 0, 43 == +, 47 == /
+
+const string::size_type Base64::DecodeTable[] = {
+// 0 1 2 3 4 5 6 7 8 9
+ np,np,np,np,np,np,np,np,np,np, // 0 - 9
+ np,np,np,np,np,np,np,np,np,np, //10 -19
+ np,np,np,np,np,np,np,np,np,np, //20 -29
+ np,np,np,np,np,np,np,np,np,np, //30 -39
+ np,np,np,62,np,np,np,63,52,53, //40 -49
+ 54,55,56,57,58,59,60,61,np,np, //50 -59
+ np,np,np,np,np, 0, 1, 2, 3, 4, //60 -69
+ 5, 6, 7, 8, 9,10,11,12,13,14, //70 -79
+ 15,16,17,18,19,20,21,22,23,24, //80 -89
+ 25,np,np,np,np,np,np,26,27,28, //90 -99
+ 29,30,31,32,33,34,35,36,37,38, //100 -109
+ 39,40,41,42,43,44,45,46,47,48, //110 -119
+ 49,50,51,np,np,np,np,np,np,np, //120 -129
+ np,np,np,np,np,np,np,np,np,np, //130 -139
+ np,np,np,np,np,np,np,np,np,np, //140 -149
+ np,np,np,np,np,np,np,np,np,np, //150 -159
+ np,np,np,np,np,np,np,np,np,np, //160 -169
+ np,np,np,np,np,np,np,np,np,np, //170 -179
+ np,np,np,np,np,np,np,np,np,np, //180 -189
+ np,np,np,np,np,np,np,np,np,np, //190 -199
+ np,np,np,np,np,np,np,np,np,np, //200 -209
+ np,np,np,np,np,np,np,np,np,np, //210 -219
+ np,np,np,np,np,np,np,np,np,np, //220 -229
+ np,np,np,np,np,np,np,np,np,np, //230 -239
+ np,np,np,np,np,np,np,np,np,np, //240 -249
+ np,np,np,np,np,np //250 -256
+};
+
+string Base64::encodeFromArray(const char * data, size_t len) {
+ size_t i;
+ char c;
+ string ret;
+
+ ret.reserve(len * 2);
+
+ for (i = 0; i < len; ++i) {
+ c = (data[i] >> 2) & 0x3f;
+ ret.append(1, Base64Table[c]);
+ c = (data[i] << 4) & 0x3f;
+ if (++i < len) {
+ c |= (data[i] >> 4) & 0x0f;
+ }
+
+ ret.append(1, Base64Table[c]);
+ if (i < len) {
+ c = (data[i] << 2) & 0x3f;
+ if (++i < len) {
+ c |= (data[i] >> 6) & 0x03;
+ }
+ ret.append(1, Base64Table[c]);
+ } else {
+ ++i;
+ ret.append(1, fillchar);
+ }
+
+ if (i < len) {
+ c = data[i] & 0x3f;
+ ret.append(1, Base64Table[c]);
+ } else {
+ ret.append(1, fillchar);
+ }
+ }
+
+ return(ret);
+}
+
+string Base64::encode(const string& data) {
+ string::size_type i;
+ char c;
+ string::size_type len = data.length();
+ string ret;
+
+ ret.reserve(len * 2);
+
+ for (i = 0; i < len; ++i) {
+ c = (data[i] >> 2) & 0x3f;
+ ret.append(1, Base64Table[c]);
+ c = (data[i] << 4) & 0x3f;
+ if (++i < len) {
+ c |= (data[i] >> 4) & 0x0f;
+ }
+
+ ret.append(1, Base64Table[c]);
+ if (i < len) {
+ c = (data[i] << 2) & 0x3f;
+ if (++i < len) {
+ c |= (data[i] >> 6) & 0x03;
+ }
+
+ ret.append(1, Base64Table[c]);
+ } else {
+ ++i;
+ ret.append(1, fillchar);
+ }
+
+ if (i < len) {
+ c = data[i] & 0x3f;
+ ret.append(1, Base64Table[c]);
+ } else {
+ ret.append(1, fillchar);
+ }
+ }
+
+ return(ret);
+}
+
+string Base64::decode(const string& data) {
+ string::size_type i;
+ char c;
+ char c1;
+ string::size_type len = data.length();
+ string ret;
+
+ ret.reserve(len);
+
+ for (i = 0; i < len; ++i) {
+ do {
+ c = static_cast<char>(DecodeTable[static_cast<unsigned char>(data[i])]);
+ } while (c == np && ++i < len);
+
+ ++i;
+
+ do {
+ c1 = static_cast<char>(DecodeTable[static_cast<unsigned char>(data[i])]);
+ } while (c == np && ++i < len);
+
+ c = (c << 2) | ((c1 >> 4) & 0x3);
+ ret.append(1, c);
+ if (++i < len) {
+ c = data[i];
+ if (fillchar == c) {
+ break;
+ }
+
+ do {
+ c = static_cast<char>(DecodeTable[static_cast<unsigned char>(data[i])]);
+ } while (c == np && ++i < len);
+ c1 = ((c1 << 4) & 0xf0) | ((c >> 2) & 0xf);
+ ret.append(1, c1);
+ }
+
+ if (++i < len) {
+ c1 = data[i];
+ if (fillchar == c1) {
+ break;
+ }
+
+ do {
+ c1 = static_cast<char>(DecodeTable[static_cast<unsigned char>(data[i])]);
+ } while (c == np && ++i < len);
+
+ c = ((c << 6) & 0xc0) | c1;
+ ret.append(1, c);
+ }
+ }
+
+ return(ret);
+}
+
+} // namespace talk_base
diff --git a/Plugins/jingle/libjingle/talk/base/base64.h b/Plugins/jingle/libjingle/talk/base/base64.h
new file mode 100644
index 0000000..2b58761
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/base64.h
@@ -0,0 +1,32 @@
+
+//*********************************************************************
+//* C_Base64 - a simple base64 encoder and decoder.
+//*
+//* Copyright (c) 1999, Bob Withers - bwit@pobox.com
+//*
+//* This code may be freely used for any purpose, either personal
+//* or commercial, provided the authors copyright notice remains
+//* intact.
+//*********************************************************************
+
+#ifndef TALK_BASE_BASE64_H__
+#define TALK_BASE_BASE64_H__
+
+#include <string>
+
+namespace talk_base {
+
+class Base64
+{
+public:
+ static std::string encode(const std::string & data);
+ static std::string decode(const std::string & data);
+ static std::string encodeFromArray(const char * data, size_t len);
+private:
+ static const std::string Base64::Base64Table;
+ static const std::string::size_type Base64::DecodeTable[];
+};
+
+} // namespace talk_base
+
+#endif // TALK_BASE_BASE64_H__
diff --git a/Plugins/jingle/libjingle/talk/base/basicdefs.h b/Plugins/jingle/libjingle/talk/base/basicdefs.h
new file mode 100644
index 0000000..886c287
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/basicdefs.h
@@ -0,0 +1,37 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TALK_BASE_BASICDEFS_H__
+#define TAKL_BASE_BASICDEFS_H__
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#define ARRAY_SIZE(x) (static_cast<int>((sizeof(x)/sizeof(x[0]))))
+
+#endif // TAKL_BASE_BASICDEFS_H__
diff --git a/Plugins/jingle/libjingle/talk/base/basictypes.h b/Plugins/jingle/libjingle/talk/base/basictypes.h
new file mode 100644
index 0000000..47588ad
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/basictypes.h
@@ -0,0 +1,85 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TALK_BASE_BASICTYPES_H__
+#define TALK_BASE_BASICTYPES_H__
+
+#ifdef COMPILER_MSVC
+typedef __int64 int64;
+#else
+typedef long long int64;
+#endif /* COMPILER_MSVC */
+typedef long int32;
+typedef short int16;
+typedef char int8;
+
+#ifdef COMPILER_MSVC
+typedef unsigned __int64 uint64;
+typedef __int64 int64;
+#else
+typedef unsigned long long uint64;
+typedef long long int64;
+#endif /* COMPILER_MSVC */
+typedef unsigned long uint32;
+typedef unsigned short uint16;
+typedef unsigned char uint8;
+
+#ifdef WIN32
+typedef int socklen_t;
+#endif
+
+namespace talk_base {
+ template<class T> inline T _min(T a, T b) { return (a > b) ? b : a; }
+ template<class T> inline T _max(T a, T b) { return (a < b) ? b : a; }
+}
+
+// A macro to disallow the evil copy constructor and operator= functions
+// This should be used in the private: declarations for a class
+#define DISALLOW_EVIL_CONSTRUCTORS(TypeName) \
+ TypeName(const TypeName&); \
+ void operator=(const TypeName&)
+
+// A macro to disallow all the implicit constructors, namely the
+// default constructor, copy constructor and operator= functions.
+//
+// This should be used in the private: declarations for a class
+// that wants to prevent anyone from instantiating it. This is
+// especially useful for classes containing only static methods.
+#define DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \
+ TypeName(); \
+ DISALLOW_EVIL_CONSTRUCTORS(TypeName)
+
+#ifndef UNUSED
+#define UNUSED(x) Unused(static_cast<const void *>(&x))
+#define UNUSED2(x,y) Unused(static_cast<const void *>(&x)); Unused(static_cast<const void *>(&y))
+#define UNUSED3(x,y,z) Unused(static_cast<const void *>(&x)); Unused(static_cast<const void *>(&y)); Unused(static_cast<const void *>(&z))
+#define UNUSED4(x,y,z,a) Unused(static_cast<const void *>(&x)); Unused(static_cast<const void *>(&y)); Unused(static_cast<const void *>(&z)); Unused(static_cast<const void *>(&a))
+#define UNUSED5(x,y,z,a,b) Unused(static_cast<const void *>(&x)); Unused(static_cast<const void *>(&y)); Unused(static_cast<const void *>(&z)); Unused(static_cast<const void *>(&a)); Unused(static_cast<const void *>(&b))
+inline void Unused(const void *) { }
+#endif // UNUSED
+
+#endif // TALK_BASE_BASICTYPES_H__
diff --git a/Plugins/jingle/libjingle/talk/base/bytebuffer.cc b/Plugins/jingle/libjingle/talk/base/bytebuffer.cc
new file mode 100644
index 0000000..7082e1a
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/bytebuffer.cc
@@ -0,0 +1,166 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <algorithm>
+#include <cassert>
+
+#include "talk/base/basictypes.h"
+#include "talk/base/bytebuffer.h"
+#include "talk/base/byteorder.h"
+
+#if defined(_MSC_VER) && _MSC_VER < 1300
+namespace std {
+ using ::memcpy;
+}
+#endif
+
+namespace talk_base {
+
+static const int DEFAULT_SIZE = 4096;
+
+ByteBuffer::ByteBuffer() {
+ start_ = 0;
+ end_ = 0;
+ size_ = DEFAULT_SIZE;
+ bytes_ = new char[size_];
+}
+
+ByteBuffer::ByteBuffer(const char* bytes, size_t len) {
+ start_ = 0;
+ end_ = len;
+ size_ = len;
+ bytes_ = new char[size_];
+ memcpy(bytes_, bytes, end_);
+}
+
+ByteBuffer::ByteBuffer(const char* bytes) {
+ start_ = 0;
+ end_ = strlen(bytes);
+ size_ = end_;
+ bytes_ = new char[size_];
+ memcpy(bytes_, bytes, end_);
+}
+
+ByteBuffer::~ByteBuffer() {
+ delete bytes_;
+}
+
+bool ByteBuffer::ReadUInt8(uint8& val) {
+ return ReadBytes(reinterpret_cast<char*>(&val), 1);
+}
+
+bool ByteBuffer::ReadUInt16(uint16& val) {
+ uint16 v;
+ if (!ReadBytes(reinterpret_cast<char*>(&v), 2)) {
+ return false;
+ } else {
+ val = NetworkToHost16(v);
+ return true;
+ }
+}
+
+bool ByteBuffer::ReadUInt32(uint32& val) {
+ uint32 v;
+ if (!ReadBytes(reinterpret_cast<char*>(&v), 4)) {
+ return false;
+ } else {
+ val = NetworkToHost32(v);
+ return true;
+ }
+}
+
+bool ByteBuffer::ReadString(std::string& val, size_t len) {
+ if (len > Length()) {
+ return false;
+ } else {
+ val.append(bytes_ + start_, len);
+ start_ += len;
+ return true;
+ }
+}
+
+bool ByteBuffer::ReadBytes(char* val, size_t len) {
+ if (len > Length()) {
+ return false;
+ } else {
+ memcpy(val, bytes_ + start_, len);
+ start_ += len;
+ return true;
+ }
+}
+
+void ByteBuffer::WriteUInt8(uint8 val) {
+ WriteBytes(reinterpret_cast<const char*>(&val), 1);
+}
+
+void ByteBuffer::WriteUInt16(uint16 val) {
+ uint16 v = HostToNetwork16(val);
+ WriteBytes(reinterpret_cast<const char*>(&v), 2);
+}
+
+void ByteBuffer::WriteUInt32(uint32 val) {
+ uint32 v = HostToNetwork32(val);
+ WriteBytes(reinterpret_cast<const char*>(&v), 4);
+}
+
+void ByteBuffer::WriteString(const std::string& val) {
+ WriteBytes(val.c_str(), val.size());
+}
+
+void ByteBuffer::WriteBytes(const char* val, size_t len) {
+ if (Length() + len > Capacity())
+ Resize(Length() + len);
+
+ memcpy(bytes_ + end_, val, len);
+ end_ += len;
+}
+
+void ByteBuffer::Resize(size_t size) {
+ if (size > size_)
+ size = _max(size, 3 * size_ / 2);
+
+ size_t len = _min(end_ - start_, size);
+ char* new_bytes = new char[size];
+ memcpy(new_bytes, bytes_ + start_, len);
+ delete [] bytes_;
+
+ start_ = 0;
+ end_ = len;
+ size_ = size;
+ bytes_ = new_bytes;
+}
+
+void ByteBuffer::Shift(size_t size) {
+ if (size > Length())
+ return;
+
+ end_ = Length() - size;
+ memmove(bytes_, bytes_ + start_ + size, end_);
+ start_ = 0;
+}
+
+} // namespace talk_base
diff --git a/Plugins/jingle/libjingle/talk/base/bytebuffer.h b/Plugins/jingle/libjingle/talk/base/bytebuffer.h
new file mode 100644
index 0000000..0a93194
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/bytebuffer.h
@@ -0,0 +1,71 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TALK_BASE_BYTEBUFFER_H__
+#define TALK_BASE_BYTEBUFFER_H__
+
+#include <string>
+#include "talk/base/basictypes.h"
+
+namespace talk_base {
+
+class ByteBuffer {
+public:
+ ByteBuffer();
+ ByteBuffer(const char* bytes, size_t len);
+ ByteBuffer(const char* bytes); // uses strlen
+ ~ByteBuffer();
+
+ const char* Data() const { return bytes_ + start_; }
+ size_t Length() { return end_ - start_; }
+ size_t Capacity() { return size_ - start_; }
+
+ bool ReadUInt8(uint8& val);
+ bool ReadUInt16(uint16& val);
+ bool ReadUInt32(uint32& val);
+ bool ReadString(std::string& val, size_t len); // append to val
+ bool ReadBytes(char* val, size_t len);
+
+ void WriteUInt8(uint8 val);
+ void WriteUInt16(uint16 val);
+ void WriteUInt32(uint32 val);
+ void WriteString(const std::string& val);
+ void WriteBytes(const char* val, size_t len);
+
+ void Resize(size_t size);
+ void Shift(size_t size);
+
+private:
+ char* bytes_;
+ size_t size_;
+ size_t start_;
+ size_t end_;
+};
+
+} // namespace talk_base
+
+#endif // TALK_BASE_BYTEBUFFER_H__
diff --git a/Plugins/jingle/libjingle/talk/base/byteorder.h b/Plugins/jingle/libjingle/talk/base/byteorder.h
new file mode 100644
index 0000000..16834c2
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/byteorder.h
@@ -0,0 +1,63 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TALK_BASE_BYTEORDER_H__
+#define TALK_BASE_BYTEORDER_H__
+
+#include "talk/base/basictypes.h"
+
+#ifdef POSIX
+extern "C" {
+#include <arpa/inet.h>
+}
+#endif
+
+#ifdef WIN32
+#include <winsock2.h>
+#endif
+
+namespace talk_base {
+
+inline uint16 HostToNetwork16(uint16 n) {
+ return htons(n);
+}
+
+inline uint32 HostToNetwork32(uint32 n) {
+ return htonl(n);
+}
+
+inline uint16 NetworkToHost16(uint16 n) {
+ return ntohs(n);
+}
+
+inline uint32 NetworkToHost32(uint32 n) {
+ return ntohl(n);
+}
+
+} // namespace talk_base
+
+#endif // TALK_BASE_BYTEORDER_H__
diff --git a/Plugins/jingle/libjingle/talk/base/common.cc b/Plugins/jingle/libjingle/talk/base/common.cc
new file mode 100644
index 0000000..aa5c56a
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/common.cc
@@ -0,0 +1,62 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <memory.h>
+
+#ifdef WIN32
+#include <windows.h>
+#endif // WIN32
+
+#include <algorithm>
+
+#include "talk/base/common.h"
+
+//////////////////////////////////////////////////////////////////////
+// Assertions
+//////////////////////////////////////////////////////////////////////
+
+namespace talk_base {
+
+void Break() {
+#ifdef WIN32
+ ::DebugBreak();
+#else // !WIN32
+#if _DEBUG_HAVE_BACKTRACE
+ OutputTrace();
+#endif
+ abort();
+#endif // !WIN32
+}
+
+void LogAssert(const char * function, const char * file, int line, const char * expression) {
+ // TODO - if we put hooks in here, we can do a lot fancier logging
+ fprintf(stderr, "%s(%d): %s @ %s\n", file, line, expression, function);
+}
+
+} // namespace talk_base \ No newline at end of file
diff --git a/Plugins/jingle/libjingle/talk/base/common.h b/Plugins/jingle/libjingle/talk/base/common.h
new file mode 100644
index 0000000..d652f81
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/common.h
@@ -0,0 +1,120 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TALK_BASE_COMMON_H__
+#define TALK_BASE_COMMON_H__
+
+#if defined(_MSC_VER)
+// warning C4355: 'this' : used in base member initializer list
+#pragma warning(disable:4355)
+#endif
+
+//////////////////////////////////////////////////////////////////////
+// General Utilities
+//////////////////////////////////////////////////////////////////////
+
+#ifndef UNUSED
+#define UNUSED(x) Unused(static_cast<const void *>(&x))
+#define UNUSED2(x,y) Unused(static_cast<const void *>(&x)); Unused(static_cast<const void *>(&y))
+#define UNUSED3(x,y,z) Unused(static_cast<const void *>(&x)); Unused(static_cast<const void *>(&y)); Unused(static_cast<const void *>(&z))
+#define UNUSED4(x,y,z,a) Unused(static_cast<const void *>(&x)); Unused(static_cast<const void *>(&y)); Unused(static_cast<const void *>(&z)); Unused(static_cast<const void *>(&a))
+#define UNUSED5(x,y,z,a,b) Unused(static_cast<const void *>(&x)); Unused(static_cast<const void *>(&y)); Unused(static_cast<const void *>(&z)); Unused(static_cast<const void *>(&a)); Unused(static_cast<const void *>(&b))
+inline void Unused(const void *) { }
+#endif // UNUSED
+
+#ifndef WIN32
+#define strnicmp(x,y,n) strncasecmp(x,y,n)
+#define stricmp(x,y) strcasecmp(x,y)
+#define stdmax(x,y) std::max(x,y)
+#else
+#define stdmax(x,y) max(x,y)
+#endif
+
+
+#define ARRAY_SIZE(x) (static_cast<int>((sizeof(x)/sizeof(x[0]))))
+
+/////////////////////////////////////////////////////////////////////////////
+// Assertions
+/////////////////////////////////////////////////////////////////////////////
+
+#ifdef ENABLE_DEBUG
+
+namespace talk_base {
+
+// Break causes the debugger to stop executing, or the program to abort
+void Break();
+
+// LogAssert writes information about an assertion to the log
+void LogAssert(const char * function, const char * file, int line, const char * expression);
+
+inline void Assert(bool result, const char * function, const char * file, int line, const char * expression) {
+ if (!result) {
+ LogAssert(function, file, line, expression);
+ Break();
+ }
+}
+
+}; // namespace talk_base
+
+#if defined(_MSC_VER) && _MSC_VER < 1300
+#define __FUNCTION__ ""
+#endif
+
+#ifndef ASSERT
+#define ASSERT(x) talk_base::Assert((x),__FUNCTION__,__FILE__,__LINE__,#x)
+#endif
+
+#ifndef VERIFY
+#define VERIFY(x) talk_base::Assert((x),__FUNCTION__,__FILE__,__LINE__,#x)
+#endif
+
+#else // !ENABLE_DEBUG
+
+#ifndef ASSERT
+#define ASSERT(x) (void)0
+#endif
+
+#ifndef VERIFY
+#define VERIFY(x) (void)(x)
+#endif
+
+#endif // !ENABLE_DEBUG
+
+#define COMPILE_TIME_ASSERT(expr) char CTA_UNIQUE_NAME[expr]
+#define CTA_UNIQUE_NAME MAKE_NAME(__LINE__)
+#define CTA_MAKE_NAME(line) MAKE_NAME2(line)
+#define CTA_MAKE_NAME2(line) constraint_ ## line
+
+//////////////////////////////////////////////////////////////////////
+
+// A macro to disallow the evil copy constructor and operator= functions
+// This should be used in the private: declarations for a class
+#define DISALLOW_EVIL_CONSTRUCTORS(TypeName) \
+ TypeName(const TypeName&); \
+ void operator=(const TypeName&)
+
+#endif // TALK_BASE_COMMON_H__
diff --git a/Plugins/jingle/libjingle/talk/base/convert.h b/Plugins/jingle/libjingle/talk/base/convert.h
new file mode 100644
index 0000000..26c6d32
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/convert.h
@@ -0,0 +1,149 @@
+/*
+ * libjingle
+ * Copyright 2007, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _CONVERT_H_
+#define _CONVERT_H_
+
+#include <string>
+#include <windows.h>
+
+#ifndef NO_ATL
+#include <atlstr.h>
+#endif // NO_ATL
+
+#include "talk/base/basictypes.h"
+
+class Utf8 {
+public:
+#ifndef NO_ATL
+ explicit Utf8(const CString & str) {
+ *this = str;
+ }
+#else
+ explicit Utf8(const wchar_t *str) {
+ *this = str;
+ }
+#endif
+
+ explicit Utf8() {}
+#ifndef NO_ATL
+ inline Utf8& operator =(const CString & str) {
+ // TODO: deal with errors
+ int len8 = WideCharToMultiByte(CP_UTF8, 0, str.GetString(), str.GetLength(),
+ NULL, 0, NULL, NULL);
+ char * ns = static_cast<char*>(_alloca(len8));
+ WideCharToMultiByte(CP_UTF8, 0, str.GetString(), str.GetLength(),
+ ns, len8, NULL, NULL);
+ str_.assign(ns, len8);
+ return *this;
+ }
+#else
+inline Utf8& operator =(const wchar_t *str) {
+ // TODO: deal with errors
+ int len8 = WideCharToMultiByte(CP_UTF8, 0, str, wcslen(str),
+ NULL, 0, NULL, NULL);
+ char * ns = static_cast<char*>(_alloca(len8));
+ WideCharToMultiByte(CP_UTF8, 0, str, wcslen(str),
+ ns, len8, NULL, NULL);
+ str_.assign(ns, len8);
+ return *this;
+ }
+#endif // NO_ATL
+
+ inline operator const std::string & () const {
+ return str_;
+ }
+
+ inline const char * AsSz() const {
+ return str_.c_str();
+ }
+
+ // Deprecated
+ inline const std::string & AsString() const {
+ return str_;
+ }
+
+ // Deprecated
+ inline int Len8() const {
+ return (int)str_.length();
+ }
+
+private:
+ DISALLOW_EVIL_CONSTRUCTORS(Utf8);
+ std::string str_;
+};
+
+class Utf16 {
+public:
+ explicit Utf16(const std::string & str) {
+ // TODO: deal with errors
+ int len16 = MultiByteToWideChar(CP_UTF8, 0, str.data(), -1,
+ NULL, 0);
+#ifndef NO_ATL
+ wchar_t * ws = cstr_.GetBuffer(len16);
+ MultiByteToWideChar(CP_UTF8, 0, str.data(), str.length(), ws, len16);
+ cstr_.ReleaseBuffer(len16);
+#else
+ str_ = new wchar_t[len16];
+ MultiByteToWideChar(CP_UTF8, 0, str.data(), -1, str_, len16);
+#endif
+ }
+
+#ifndef NO_ATL
+ inline operator const CString & () const {
+ return cstr_;
+ }
+ // Deprecated
+ inline const CString & AsCString() const {
+ return cstr_;
+ }
+ // Deprecated
+ inline int Len16() const {
+ return cstr_.GetLength();
+ }
+ inline const wchar_t * AsWz() const {
+ return cstr_.GetString();
+ }
+#else
+ ~Utf16() {
+ delete[] str_;
+ }
+ inline const wchar_t * AsWz() const {
+ return str_;
+ }
+#endif
+
+private:
+ DISALLOW_EVIL_CONSTRUCTORS(Utf16);
+#ifndef NO_ATL
+ CString cstr_;
+#else
+ wchar_t *str_;
+#endif
+};
+
+#endif // _CONVERT_H_
diff --git a/Plugins/jingle/libjingle/talk/base/criticalsection.h b/Plugins/jingle/libjingle/talk/base/criticalsection.h
new file mode 100644
index 0000000..fbe7382
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/criticalsection.h
@@ -0,0 +1,120 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TALK_BASE_CRITICALSECTION_H__
+#define TALK_BASE_CRITICALSECTION_H__
+
+#ifdef WIN32
+#include "talk/base/win32.h"
+#endif
+
+#ifdef POSIX
+#include <pthread.h>
+#endif
+
+#ifdef _DEBUG
+#define CS_TRACK_OWNER 1
+#endif // _DEBUG
+
+#if CS_TRACK_OWNER
+#define TRACK_OWNER(x) x
+#else // !CS_TRACK_OWNER
+#define TRACK_OWNER(x)
+#endif // !CS_TRACK_OWNER
+
+namespace talk_base {
+
+#ifdef WIN32
+class CriticalSection {
+public:
+ CriticalSection() {
+ InitializeCriticalSection(&crit_);
+ // Windows docs say 0 is not a valid thread id
+ TRACK_OWNER(thread_ = 0);
+ }
+ ~CriticalSection() {
+ DeleteCriticalSection(&crit_);
+ }
+ void Enter() {
+ EnterCriticalSection(&crit_);
+ TRACK_OWNER(thread_ = GetCurrentThreadId());
+ }
+ void Leave() {
+ TRACK_OWNER(thread_ = 0);
+ LeaveCriticalSection(&crit_);
+ }
+
+#if CS_TRACK_OWNER
+ bool CurrentThreadIsOwner() const { return thread_ == GetCurrentThreadId(); }
+#endif // CS_TRACK_OWNER
+
+private:
+ CRITICAL_SECTION crit_;
+ TRACK_OWNER(DWORD thread_); // The section's owning thread id
+};
+#endif // WIN32
+
+#ifdef POSIX
+class CriticalSection {
+public:
+ CriticalSection() {
+ pthread_mutexattr_t mutex_attribute;
+ pthread_mutexattr_settype(&mutex_attribute, PTHREAD_MUTEX_RECURSIVE);
+ pthread_mutex_init(&mutex_, &mutex_attribute);
+ }
+ ~CriticalSection() {
+ pthread_mutex_destroy(&mutex_);
+ }
+ void Enter() {
+ pthread_mutex_lock(&mutex_);
+ }
+ void Leave() {
+ pthread_mutex_unlock(&mutex_);
+ }
+private:
+ pthread_mutex_t mutex_;
+};
+#endif // POSIX
+
+// CritScope, for serializing exection through a scope
+
+class CritScope {
+public:
+ CritScope(CriticalSection *pcrit) {
+ pcrit_ = pcrit;
+ pcrit_->Enter();
+ }
+ ~CritScope() {
+ pcrit_->Leave();
+ }
+private:
+ CriticalSection *pcrit_;
+};
+
+} // namespace talk_base
+
+#endif // TALK_BASE_CRITICALSECTION_H__
diff --git a/Plugins/jingle/libjingle/talk/base/cryptstring.h b/Plugins/jingle/libjingle/talk/base/cryptstring.h
new file mode 100644
index 0000000..64a7e57
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/cryptstring.h
@@ -0,0 +1,185 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _TALK_BASE_CRYPTSTRING_H_
+#define _TALK_BASE_CRYPTSTRING_H_
+
+#include <string>
+#include "talk/base/linked_ptr.h"
+#include "talk/base/scoped_ptr.h"
+
+namespace talk_base {
+
+class CryptStringImpl {
+public:
+ virtual ~CryptStringImpl() {}
+ virtual size_t GetLength() const = 0;
+ virtual void CopyTo(char * dest, bool nullterminate) const = 0;
+ virtual std::string UrlEncode() const = 0;
+ virtual CryptStringImpl * Copy() const = 0;
+};
+
+class EmptyCryptStringImpl : public CryptStringImpl {
+public:
+ virtual ~EmptyCryptStringImpl() {}
+ virtual size_t GetLength() const { return 0; }
+ virtual void CopyTo(char * dest, bool nullterminate) const {
+ if (nullterminate) {
+ *dest = '\0';
+ }
+ }
+ virtual std::string UrlEncode() const { return ""; }
+ virtual CryptStringImpl * Copy() const { return new EmptyCryptStringImpl(); }
+};
+
+class CryptString {
+public:
+ CryptString() : impl_(new EmptyCryptStringImpl()) {}
+ size_t GetLength() const { return impl_->GetLength(); }
+ void CopyTo(char * dest, bool nullterminate) const { impl_->CopyTo(dest, nullterminate); }
+ CryptString(const CryptString & other) : impl_(other.impl_->Copy()) {}
+ explicit CryptString(const CryptStringImpl & impl) : impl_(impl.Copy()) {}
+ CryptString & operator=(const CryptString & other) {
+ if (this != &other) {
+ impl_.reset(other.impl_->Copy());
+ }
+ return *this;
+ }
+ void Clear() { impl_.reset(new EmptyCryptStringImpl()); }
+ std::string UrlEncode() const { return impl_->UrlEncode(); }
+
+private:
+ scoped_ptr<const CryptStringImpl> impl_;
+};
+
+
+// Used for constructing strings where a password is involved and we
+// need to ensure that we zero memory afterwards
+class FormatCryptString {
+public:
+ FormatCryptString() {
+ storage_ = new char[32];
+ capacity_ = 32;
+ length_ = 0;
+ storage_[0] = 0;
+ }
+
+ void Append(const std::string & text) {
+ Append(text.data(), text.length());
+ }
+
+ void Append(const char * data, size_t length) {
+ EnsureStorage(length_ + length + 1);
+ memcpy(storage_ + length_, data, length);
+ length_ += length;
+ storage_[length_] = '\0';
+ }
+
+ void Append(const CryptString * password) {
+ size_t len = password->GetLength();
+ EnsureStorage(length_ + len + 1);
+ password->CopyTo(storage_ + length_, true);
+ length_ += len;
+ }
+
+ size_t GetLength() {
+ return length_;
+ }
+
+ const char * GetData() {
+ return storage_;
+ }
+
+
+ // Ensures storage of at least n bytes
+ void EnsureStorage(size_t n) {
+ if (capacity_ >= n) {
+ return;
+ }
+
+ size_t old_capacity = capacity_;
+ char * old_storage = storage_;
+
+ for (;;) {
+ capacity_ *= 2;
+ if (capacity_ >= n)
+ break;
+ }
+
+ storage_ = new char[capacity_];
+
+ if (old_capacity) {
+ memcpy(storage_, old_storage, length_);
+
+ // zero memory in a way that an optimizer won't optimize it out
+ old_storage[0] = 0;
+ for (size_t i = 1; i < old_capacity; i++) {
+ old_storage[i] = old_storage[i - 1];
+ }
+ delete[] old_storage;
+ }
+ }
+
+ ~FormatCryptString() {
+ if (capacity_) {
+ storage_[0] = 0;
+ for (size_t i = 1; i < capacity_; i++) {
+ storage_[i] = storage_[i - 1];
+ }
+ }
+ delete[] storage_;
+ }
+private:
+ char * storage_;
+ size_t capacity_;
+ size_t length_;
+};
+
+class InsecureCryptStringImpl : public CryptStringImpl {
+ public:
+ std::string& password() { return password_; }
+ const std::string& password() const { return password_; }
+
+ virtual ~InsecureCryptStringImpl() {}
+ virtual size_t GetLength() const { return password_.size(); }
+ virtual void CopyTo(char * dest, bool nullterminate) const {
+ memcpy(dest, password_.data(), password_.size());
+ if (nullterminate) dest[password_.size()] = 0;
+ }
+ virtual std::string UrlEncode() const { return password_; }
+ virtual CryptStringImpl * Copy() const {
+ InsecureCryptStringImpl * copy = new InsecureCryptStringImpl;
+ copy->password() = password_;
+ return copy;
+ }
+ private:
+ std::string password_;
+};
+
+}
+
+#endif // _TALK_BASE_CRYPTSTRING_H_
diff --git a/Plugins/jingle/libjingle/talk/base/diskcache.cc b/Plugins/jingle/libjingle/talk/base/diskcache.cc
new file mode 100644
index 0000000..44e37d9
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/diskcache.cc
@@ -0,0 +1,362 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <time.h>
+
+#ifdef WIN32
+#include "talk/base/win32.h"
+#endif
+
+#include "talk/base/basicdefs.h"
+#include "talk/base/common.h"
+#include "talk/base/diskcache.h"
+#include "talk/base/fileutils.h"
+#include "talk/base/pathutils.h"
+#include "talk/base/stream.h"
+#include "talk/base/stringencode.h"
+#include "talk/base/stringutils.h"
+
+#ifdef _DEBUG
+#define TRANSPARENT_CACHE_NAMES 1
+#else // !_DEBUG
+#define TRANSPARENT_CACHE_NAMES 0
+#endif // !_DEBUG
+
+namespace talk_base {
+
+class DiskCache;
+
+///////////////////////////////////////////////////////////////////////////////
+// DiskCacheAdapter
+///////////////////////////////////////////////////////////////////////////////
+
+class DiskCacheAdapter : public StreamAdapterInterface {
+public:
+ DiskCacheAdapter(const DiskCache* cache, const std::string& id, size_t index,
+ StreamInterface* stream)
+ : StreamAdapterInterface(stream), cache_(cache), id_(id), index_(index)
+ { }
+ virtual ~DiskCacheAdapter() {
+ Close();
+ cache_->ReleaseResource(id_, index_);
+ }
+
+private:
+ const DiskCache* cache_;
+ std::string id_;
+ size_t index_;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// DiskCache
+///////////////////////////////////////////////////////////////////////////////
+
+DiskCache::DiskCache() : max_cache_(0), total_size_(0), total_accessors_(0) {
+}
+
+DiskCache::~DiskCache() {
+ ASSERT(0 == total_accessors_);
+}
+
+bool DiskCache::Initialize(const std::string& folder, size_t size) {
+ if (!folder_.empty() || !Filesystem::CreateFolder(folder))
+ return false;
+
+ folder_ = folder;
+ max_cache_ = size;
+ ASSERT(0 == total_size_);
+
+ if (!InitializeEntries())
+ return false;
+
+ return CheckLimit();
+}
+
+bool DiskCache::Purge() {
+ if (folder_.empty())
+ return false;
+
+ if (total_accessors_ > 0) {
+ LOG_F(LS_WARNING) << "Cache files open";
+ return false;
+ }
+
+ if (!PurgeFiles())
+ return false;
+
+ map_.clear();
+ return true;
+}
+
+bool DiskCache::LockResource(const std::string& id) {
+ Entry* entry = GetOrCreateEntry(id, true);
+ if (LS_LOCKED == entry->lock_state)
+ return false;
+ if ((LS_UNLOCKED == entry->lock_state) && (entry->accessors > 0))
+ return false;
+ if ((total_size_ > max_cache_) && !CheckLimit()) {
+ LOG_F(LS_WARNING) << "Cache overfull";
+ return false;
+ }
+ entry->lock_state = LS_LOCKED;
+ return true;
+}
+
+StreamInterface* DiskCache::WriteResource(const std::string& id, size_t index) {
+ Entry* entry = GetOrCreateEntry(id, false);
+ if (LS_LOCKED != entry->lock_state)
+ return NULL;
+
+ size_t previous_size = 0;
+ std::string filename(IdToFilename(id, index));
+ FileStream::GetSize(filename, &previous_size);
+ ASSERT(previous_size <= entry->size);
+ if (previous_size > entry->size) {
+ previous_size = entry->size;
+ }
+
+ scoped_ptr<FileStream> file(new FileStream);
+ if (!file->Open(filename, "wb")) {
+ LOG_F(LS_ERROR) << "Couldn't create cache file";
+ return NULL;
+ }
+
+ entry->streams = stdmax(entry->streams, index + 1);
+ entry->size -= previous_size;
+ total_size_ -= previous_size;
+
+ entry->accessors += 1;
+ total_accessors_ += 1;
+ return new DiskCacheAdapter(this, id, index, file.release());
+}
+
+bool DiskCache::UnlockResource(const std::string& id) {
+ Entry* entry = GetOrCreateEntry(id, false);
+ if (LS_LOCKED != entry->lock_state)
+ return false;
+
+ if (entry->accessors > 0) {
+ entry->lock_state = LS_UNLOCKING;
+ } else {
+ entry->lock_state = LS_UNLOCKED;
+ entry->last_modified = time(0);
+ CheckLimit();
+ }
+ return true;
+}
+
+StreamInterface* DiskCache::ReadResource(const std::string& id,
+ size_t index) const {
+ const Entry* entry = GetEntry(id);
+ if (LS_UNLOCKED != entry->lock_state)
+ return NULL;
+ if (index >= entry->streams)
+ return NULL;
+
+ scoped_ptr<FileStream> file(new FileStream);
+ if (!file->Open(IdToFilename(id, index), "rb"))
+ return NULL;
+
+ entry->accessors += 1;
+ total_accessors_ += 1;
+ return new DiskCacheAdapter(this, id, index, file.release());
+}
+
+bool DiskCache::HasResource(const std::string& id) const {
+ const Entry* entry = GetEntry(id);
+ return (NULL != entry) && (entry->streams > 0);
+}
+
+bool DiskCache::HasResourceStream(const std::string& id, size_t index) const {
+ const Entry* entry = GetEntry(id);
+ if ((NULL == entry) || (index >= entry->streams))
+ return false;
+
+ std::string filename = IdToFilename(id, index);
+
+ return FileExists(filename);
+}
+
+bool DiskCache::DeleteResource(const std::string& id) {
+ Entry* entry = GetOrCreateEntry(id, false);
+ if (!entry)
+ return true;
+
+ if ((LS_UNLOCKED != entry->lock_state) || (entry->accessors > 0))
+ return false;
+
+ bool success = true;
+ for (size_t index = 0; index < entry->streams; ++index) {
+ std::string filename = IdToFilename(id, index);
+
+ if (!FileExists(filename))
+ continue;
+
+ if (!DeleteFile(filename)) {
+ LOG_F(LS_ERROR) << "Couldn't remove cache file: " << filename;
+ success = false;
+ }
+ }
+
+ total_size_ -= entry->size;
+ map_.erase(id);
+ return success;
+}
+
+bool DiskCache::CheckLimit() {
+#ifdef _DEBUG
+ // Temporary check to make sure everything is working correctly.
+ size_t cache_size = 0;
+ for (EntryMap::iterator it = map_.begin(); it != map_.end(); ++it) {
+ cache_size += it->second.size;
+ }
+ ASSERT(cache_size == total_size_);
+#endif // _DEBUG
+
+ // TODO: Replace this with a non-brain-dead algorithm for clearing out the
+ // oldest resources... something that isn't O(n^2)
+ while (total_size_ > max_cache_) {
+ EntryMap::iterator oldest = map_.end();
+ for (EntryMap::iterator it = map_.begin(); it != map_.end(); ++it) {
+ if ((LS_UNLOCKED != it->second.lock_state) || (it->second.accessors > 0))
+ continue;
+ oldest = it;
+ break;
+ }
+ if (oldest == map_.end()) {
+ LOG_F(LS_WARNING) << "All resources are locked!";
+ return false;
+ }
+ for (EntryMap::iterator it = oldest++; it != map_.end(); ++it) {
+ if (it->second.last_modified < oldest->second.last_modified) {
+ oldest = it;
+ }
+ }
+ if (!DeleteResource(oldest->first)) {
+ LOG_F(LS_ERROR) << "Couldn't delete from cache!";
+ return false;
+ }
+ }
+ return true;
+}
+
+std::string DiskCache::IdToFilename(const std::string& id, size_t index) const {
+#ifdef TRANSPARENT_CACHE_NAMES
+ // This escapes colons and other filesystem characters, so the user can't open
+ // special devices (like "COM1:"), or access other directories.
+ size_t buffer_size = id.length()*3 + 1;
+ char* buffer = new char[buffer_size];
+ encode(buffer, buffer_size, id.data(), id.length(),
+ unsafe_filename_characters(), '%');
+ // TODO: ASSERT(strlen(buffer) < FileSystem::MaxBasenameLength());
+#else // !TRANSPARENT_CACHE_NAMES
+ // We might want to just use a hash of the filename at some point, both for
+ // obfuscation, and to avoid both filename length and escaping issues.
+ ASSERT(false);
+#endif // !TRANSPARENT_CACHE_NAMES
+
+ char extension[32];
+ sprintfn(extension, ARRAY_SIZE(extension), ".%u", index);
+
+ Pathname pathname;
+ pathname.SetFolder(folder_);
+ pathname.SetBasename(buffer);
+ pathname.SetExtension(extension);
+
+#ifdef TRANSPARENT_CACHE_NAMES
+ delete [] buffer;
+#endif // TRANSPARENT_CACHE_NAMES
+
+ return pathname.pathname();
+}
+
+bool DiskCache::FilenameToId(const std::string& filename, std::string* id,
+ size_t* index) const {
+ Pathname pathname(filename);
+ if (1 != sscanf(pathname.extension().c_str(), ".%u", index))
+ return false;
+
+ size_t buffer_size = pathname.basename().length() + 1;
+ char* buffer = new char[buffer_size];
+ decode(buffer, buffer_size, pathname.basename().data(),
+ pathname.basename().length(), '%');
+ id->assign(buffer);
+ delete [] buffer;
+ return true;
+}
+
+DiskCache::Entry* DiskCache::GetOrCreateEntry(const std::string& id,
+ bool create) {
+ EntryMap::iterator it = map_.find(id);
+ if (it != map_.end())
+ return &it->second;
+ if (!create)
+ return NULL;
+ Entry e;
+ e.lock_state = LS_UNLOCKED;
+ e.accessors = 0;
+ e.size = 0;
+ e.streams = 0;
+ e.last_modified = time(0);
+ it = map_.insert(EntryMap::value_type(id, e)).first;
+ return &it->second;
+}
+
+void DiskCache::ReleaseResource(const std::string& id, size_t index) const {
+ const Entry* entry = GetEntry(id);
+ if (!entry) {
+ LOG_F(LS_WARNING) << "Missing cache entry";
+ ASSERT(false);
+ return;
+ }
+
+ entry->accessors -= 1;
+ total_accessors_ -= 1;
+
+ if (LS_UNLOCKED != entry->lock_state) {
+ // This is safe, because locked resources only issue WriteResource, which
+ // is non-const. Think about a better way to handle it.
+ DiskCache* this2 = const_cast<DiskCache*>(this);
+ Entry* entry2 = this2->GetOrCreateEntry(id, false);
+
+ size_t new_size = 0;
+ std::string filename(IdToFilename(id, index));
+ FileStream::GetSize(filename, &new_size);
+ entry2->size += new_size;
+ this2->total_size_ += new_size;
+
+ if ((LS_UNLOCKING == entry->lock_state) && (0 == entry->accessors)) {
+ entry2->last_modified = time(0);
+ entry2->lock_state = LS_UNLOCKED;
+ this2->CheckLimit();
+ }
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+} // namespace talk_base
diff --git a/Plugins/jingle/libjingle/talk/base/diskcache.h b/Plugins/jingle/libjingle/talk/base/diskcache.h
new file mode 100644
index 0000000..b42e3e0
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/diskcache.h
@@ -0,0 +1,142 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TALK_BASE_DISKCACHE_H__
+#define TALK_BASE_DISKCACHE_H__
+
+#include <map>
+#include <string>
+
+#ifdef WIN32
+#undef UnlockResource
+#endif // WIN32
+
+namespace talk_base {
+
+class StreamInterface;
+
+///////////////////////////////////////////////////////////////////////////////
+// DiskCache - An LRU cache of streams, stored on disk.
+//
+// Streams are identified by a unique resource id. Multiple streams can be
+// associated with each resource id, distinguished by an index. When old
+// resources are flushed from the cache, all streams associated with those
+// resources are removed together.
+// DiskCache is designed to persist across executions of the program. It is
+// safe for use from an arbitrary number of users on a single thread, but not
+// from multiple threads or other processes.
+///////////////////////////////////////////////////////////////////////////////
+
+class DiskCache {
+public:
+ DiskCache();
+ ~DiskCache();
+
+ bool Initialize(const std::string& folder, size_t size);
+ bool Purge();
+
+ bool LockResource(const std::string& id);
+ StreamInterface* WriteResource(const std::string& id, size_t index);
+ bool UnlockResource(const std::string& id);
+
+ StreamInterface* ReadResource(const std::string& id, size_t index) const;
+
+ bool HasResource(const std::string& id) const;
+ bool HasResourceStream(const std::string& id, size_t index) const;
+ bool DeleteResource(const std::string& id);
+
+ protected:
+ virtual bool InitializeEntries() = 0;
+ virtual bool PurgeFiles() = 0;
+
+ virtual bool FileExists(const std::string& filename) const = 0;
+ virtual bool DeleteFile(const std::string& filename) const = 0;
+
+ enum LockState { LS_UNLOCKED, LS_LOCKED, LS_UNLOCKING };
+ struct Entry {
+ LockState lock_state;
+ mutable size_t accessors;
+ size_t size;
+ size_t streams;
+ time_t last_modified;
+ };
+ typedef std::map<std::string, Entry> EntryMap;
+ friend class DiskCacheAdapter;
+
+ bool CheckLimit();
+
+ std::string IdToFilename(const std::string& id, size_t index) const;
+ bool FilenameToId(const std::string& filename, std::string* id,
+ size_t* index) const;
+
+ const Entry* GetEntry(const std::string& id) const {
+ return const_cast<DiskCache*>(this)->GetOrCreateEntry(id, false);
+ }
+ Entry* GetOrCreateEntry(const std::string& id, bool create);
+
+ void ReleaseResource(const std::string& id, size_t index) const;
+
+ std::string folder_;
+ size_t max_cache_, total_size_;
+ EntryMap map_;
+ mutable size_t total_accessors_;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// CacheLock - Automatically manage locking and unlocking, with optional
+// rollback semantics
+///////////////////////////////////////////////////////////////////////////////
+
+class CacheLock {
+public:
+ CacheLock(DiskCache* cache, const std::string& id, bool rollback = false)
+ : cache_(cache), id_(id), rollback_(rollback)
+ {
+ locked_ = cache_->LockResource(id_);
+ }
+ ~CacheLock() {
+ if (locked_) {
+ cache_->UnlockResource(id_);
+ if (rollback_) {
+ cache_->DeleteResource(id_);
+ }
+ }
+ }
+ bool IsLocked() const { return locked_; }
+ void Commit() { rollback_ = false; }
+
+private:
+ DiskCache* cache_;
+ std::string id_;
+ bool rollback_, locked_;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+} // namespace talk_base
+
+#endif // TALK_BASE_DISKCACHE_H__
diff --git a/Plugins/jingle/libjingle/talk/base/diskcache_win32.cc b/Plugins/jingle/libjingle/talk/base/diskcache_win32.cc
new file mode 100644
index 0000000..3132602
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/diskcache_win32.cc
@@ -0,0 +1,76 @@
+#include "talk/base/win32.h"
+#include <shellapi.h>
+#include <shlobj.h>
+#include <tchar.h>
+
+#include <time.h>
+
+#include "talk/base/common.h"
+#include "talk/base/diskcache.h"
+#include "talk/base/pathutils.h"
+#include "talk/base/stream.h"
+#include "talk/base/stringencode.h"
+#include "talk/base/stringutils.h"
+
+#include "talk/base/diskcache_win32.h"
+
+namespace talk_base {
+
+bool DiskCacheWin32::InitializeEntries() {
+ // Note: We could store the cache information in a separate file, for faster
+ // initialization. Figuring it out empirically works, too.
+
+ std::wstring path16 = ToUtf16(folder_);
+ path16.append(1, '*');
+
+ WIN32_FIND_DATA find_data;
+ HANDLE find_handle = FindFirstFile(path16.c_str(), &find_data);
+ if (find_handle != INVALID_HANDLE_VALUE) {
+ do {
+ size_t index;
+ std::string id;
+ if (!FilenameToId(ToUtf8(find_data.cFileName), &id, &index))
+ continue;
+
+ Entry* entry = GetOrCreateEntry(id, true);
+ entry->size += find_data.nFileSizeLow;
+ total_size_ += find_data.nFileSizeLow;
+ entry->streams = max(entry->streams, index + 1);
+ FileTimeToUnixTime(find_data.ftLastWriteTime, &entry->last_modified);
+
+ } while (FindNextFile(find_handle, &find_data));
+
+ FindClose(find_handle);
+ }
+
+ return true;
+}
+
+bool DiskCacheWin32::PurgeFiles() {
+ std::wstring path16 = ToUtf16(folder_);
+ path16.append(1, '*');
+ path16.append(1, '\0');
+
+ SHFILEOPSTRUCT file_op = { 0 };
+ file_op.wFunc = FO_DELETE;
+ file_op.pFrom = path16.c_str();
+ file_op.fFlags = FOF_NOCONFIRMATION | FOF_NOERRORUI | FOF_SILENT
+ | FOF_NORECURSION | FOF_FILESONLY;
+ if (0 != SHFileOperation(&file_op)) {
+ LOG_F(LS_ERROR) << "Couldn't delete cache files";
+ return false;
+ }
+
+ return true;
+}
+
+bool DiskCacheWin32::FileExists(const std::string& filename) const {
+ DWORD result = ::GetFileAttributes(ToUtf16(filename).c_str());
+ return (INVALID_FILE_ATTRIBUTES != result);
+}
+
+bool DiskCacheWin32::DeleteFile(const std::string& filename) const {
+ return ::DeleteFile(ToUtf16(filename).c_str()) != 0;
+}
+
+}
diff --git a/Plugins/jingle/libjingle/talk/base/diskcache_win32.h b/Plugins/jingle/libjingle/talk/base/diskcache_win32.h
new file mode 100644
index 0000000..aa5a1b6
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/diskcache_win32.h
@@ -0,0 +1,28 @@
+//
+// DiskCacheWin32.h
+// Macshroom
+//
+// Created by Moishe Lettvin on 11/7/06.
+// Copyright (C) 2006 Google Inc. All rights reserved.
+//
+//
+
+#ifndef TALK_BASE_DISKCACHEWIN32_H__
+#define TALK_BASE_DISKCACHEWIN32_H__
+
+#include "talk/base/diskcache.h"
+
+namespace talk_base {
+
+class DiskCacheWin32 : public DiskCache {
+ protected:
+ virtual bool InitializeEntries();
+ virtual bool PurgeFiles();
+
+ virtual bool FileExists(const std::string& filename) const;
+ virtual bool DeleteFile(const std::string& filename) const;
+};
+
+}
+
+#endif // TALK_BASE_DISKCACHEWIN32_H__
diff --git a/Plugins/jingle/libjingle/talk/base/diskcachestd.cc b/Plugins/jingle/libjingle/talk/base/diskcachestd.cc
new file mode 100644
index 0000000..f50c7d7
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/diskcachestd.cc
@@ -0,0 +1,30 @@
+//
+// DiskCacheStd.cc
+// Macshroom
+//
+// Created by Moishe Lettvin on 11/7/06.
+// Copyright (C) 2006 Google Inc. All rights reserved.
+//
+//
+
+#include "diskcachestd.h"
+
+namespace talk_base {
+
+bool DiskCacheStd::InitializeEntries() {
+ return false;
+}
+
+bool DiskCacheStd::PurgeFiles() {
+ return false;
+}
+
+bool DiskCacheStd::FileExists(const std::string& filename) const {
+ return false;
+}
+
+bool DiskCacheStd::DeleteFile(const std::string& filename) const {
+ return false;
+}
+
+}
diff --git a/Plugins/jingle/libjingle/talk/base/diskcachestd.h b/Plugins/jingle/libjingle/talk/base/diskcachestd.h
new file mode 100644
index 0000000..b676771
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/diskcachestd.h
@@ -0,0 +1,28 @@
+//
+// DiskCacheStd.h
+// Macshroom
+//
+// Created by Moishe Lettvin on 11/7/06.
+// Copyright (C) 2006 Google Inc. All rights reserved.
+//
+//
+
+#ifndef TALK_BASE_DISKCACHESTD_H__
+#define TALK_BASE_DISKCACHESTD_H__
+
+#include "talk/base/diskcache.h"
+
+namespace talk_base {
+
+class DiskCacheStd : public DiskCache {
+ protected:
+ virtual bool InitializeEntries();
+ virtual bool PurgeFiles();
+
+ virtual bool FileExists(const std::string& filename) const;
+ virtual bool DeleteFile(const std::string& filename) const;
+};
+
+}
+
+#endif // TALK_BASE_DISKCACHESTD_H__
diff --git a/Plugins/jingle/libjingle/talk/base/event.h b/Plugins/jingle/libjingle/talk/base/event.h
new file mode 100644
index 0000000..ca15c38
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/event.h
@@ -0,0 +1,79 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TALK_BASE_EVENT_H__
+#define TALK_BASE_EVENT_H__
+
+namespace talk_base {
+
+#ifdef WIN32
+class Event {
+public:
+ Event() {
+ event_ = CreateEvent(NULL, FALSE, FALSE, NULL);
+ }
+ ~Event() {
+ CloseHandle(event_);
+ }
+ void Set() {
+ SetEvent(event_);
+ }
+ void Reset() {
+ ResetEvent(event_);
+ }
+ void Wait() {
+ WaitForSingleObject(event_, INFINITE);
+ }
+private:
+ HANDLE event_;
+};
+#endif
+
+#ifdef POSIX
+#include <cassert>
+class Event {
+ Event() {
+ assert(false);
+ }
+ ~Event() {
+ assert(false);
+ }
+ void Set() {
+ assert(false);
+ }
+ void Reset() {
+ assert(false);
+ }
+ void Wait() {
+ assert(false);
+ }
+};
+#endif
+
+} // namespace talk_base
+
+#endif // TALK_BASE_EVENT_H__
diff --git a/Plugins/jingle/libjingle/talk/base/fileutils.cc b/Plugins/jingle/libjingle/talk/base/fileutils.cc
new file mode 100644
index 0000000..12c2576
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/fileutils.cc
@@ -0,0 +1,273 @@
+/*
+ * libjingle
+ * Copyright 2004--2006, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <errno.h>
+#include <cassert>
+
+#ifdef WIN32
+#include "talk/base/convert.h"
+#endif
+
+#include "talk/base/pathutils.h"
+#include "talk/base/fileutils.h"
+#include "talk/base/stringutils.h"
+#include "talk/base/stream.h"
+
+#include "talk/base/unixfilesystem.h"
+#include "talk/base/win32filesystem.h"
+
+#ifndef WIN32
+#define MAX_PATH 256
+#endif
+
+namespace talk_base {
+
+//////////////////////////
+// Directory Iterator //
+//////////////////////////
+
+// A Directoryraverser is created with a given directory. It originally points to
+// the first file in the directory, and can be advanecd with Next(). This allows you
+// to get information about each file.
+
+ // Constructor
+DirectoryIterator::DirectoryIterator() :
+#ifdef _WIN32
+ handle_(INVALID_HANDLE_VALUE)
+#else
+ dir_(NULL), dirent_(NULL)
+#endif
+{}
+
+ // Destructor
+DirectoryIterator::~DirectoryIterator() {
+#ifdef WIN32
+ if (handle_ != INVALID_HANDLE_VALUE)
+ ::FindClose(handle_);
+#else
+ if (dir_)
+ closedir(dir_);
+#endif
+}
+
+ // Starts traversing a directory.
+ // dir is the directory to traverse
+ // returns true if the directory exists and is valid
+bool DirectoryIterator::Iterate(const Pathname &dir) {
+ directory_ = dir.pathname();
+#ifdef WIN32
+ if (handle_ != INVALID_HANDLE_VALUE)
+ ::FindClose(handle_);
+ std::string d = dir.pathname() + '*';
+ handle_ = ::FindFirstFile(Utf16(d).AsWz(), &data_);
+ if (handle_ == INVALID_HANDLE_VALUE)
+ return false;
+#else
+ if (dir_ != NULL)
+ closedir(dir_);
+ dir_ = ::opendir(directory_.c_str());
+ if (dir_ == NULL)
+ return false;
+ dirent_ = readdir(dir_);
+ if (dirent_ == NULL)
+ return false;
+
+ if (::stat(std::string(directory_ + Name()).c_str(), &stat_) != 0)
+ return false;
+#endif
+ return true;
+}
+
+ // Advances to the next file
+ // returns true if there were more files in the directory.
+bool DirectoryIterator::Next() {
+#ifdef WIN32
+ return ::FindNextFile(handle_, &data_) == TRUE;
+#else
+ dirent_ = ::readdir(dir_);
+ if (dirent_ == NULL)
+ return false;
+
+ return ::stat(std::string(directory_ + Name()).c_str(), &stat_) == 0;
+#endif
+}
+
+ // returns true if the file currently pointed to is a directory
+bool DirectoryIterator::IsDirectory() const {
+#ifdef WIN32
+ return (data_.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != FALSE;
+#else
+ return S_ISDIR(stat_.st_mode);
+#endif
+}
+
+ // returns the name of the file currently pointed to
+std::string DirectoryIterator::Name() const {
+#ifdef WIN32
+ return Utf8(data_.cFileName).AsString();
+#else
+ assert(dirent_ != NULL);
+ return dirent_->d_name;
+#endif
+}
+
+ // returns the size of the file currently pointed to
+size_t DirectoryIterator::FileSize() const {
+#ifndef WIN32
+ return stat_.st_size;
+#else
+ return data_.nFileSizeLow;
+#endif
+}
+
+ // returns the last modified time of this file
+time_t DirectoryIterator::FileModifyTime() const {
+#ifdef WIN32
+return 0;
+#else
+ return stat_.st_mtime;
+#endif
+}
+
+Filesystem *Filesystem::default_filesystem_ = 0;
+
+
+bool Filesystem::CreateFolder(const Pathname &pathname)
+{
+ return EnsureDefaultFilesystem()->CreateFolderI(pathname);
+}
+
+FileStream *Filesystem::OpenFile(const Pathname &filename,
+ const std::string &mode)
+{
+ return EnsureDefaultFilesystem()->OpenFileI(filename, mode);
+}
+
+bool Filesystem::DeleteFile(const Pathname &filename)
+{
+ return EnsureDefaultFilesystem()->DeleteFileI(filename);
+}
+
+bool Filesystem::MoveFile(const Pathname &old_path, const Pathname &new_path)
+{
+ return EnsureDefaultFilesystem()->MoveFileI(old_path, new_path);
+}
+
+bool Filesystem::CopyFile(const Pathname &old_path, const Pathname &new_path)
+{
+ return EnsureDefaultFilesystem()->CopyFileI(old_path, new_path);
+}
+
+bool Filesystem::IsFolder(const Pathname& pathname)
+{
+ return EnsureDefaultFilesystem()->IsFolderI(pathname);
+}
+
+bool Filesystem::FileExists(const Pathname& pathname)
+{
+ return EnsureDefaultFilesystem()->FileExistsI(pathname);
+}
+
+bool Filesystem::IsTemporaryPath(const Pathname& pathname)
+{
+ return EnsureDefaultFilesystem()->IsTemporaryPathI(pathname);
+}
+
+bool Filesystem::GetTemporaryFolder(Pathname &path, bool create,
+ const std::string *append)
+{
+ return EnsureDefaultFilesystem()->GetTemporaryFolderI(path,create, append);
+}
+
+std::string Filesystem::TempFilename(const Pathname &dir, const std::string &prefix)
+{
+ return EnsureDefaultFilesystem()->TempFilenameI(dir, prefix);
+}
+
+bool Filesystem::GetFileSize(const Pathname &dir, size_t *size)
+{
+ return EnsureDefaultFilesystem()->GetFileSizeI(dir, size);
+}
+
+Filesystem *Filesystem::EnsureDefaultFilesystem()
+{
+ if (!default_filesystem_)
+#ifdef WIN32
+ default_filesystem_ = new Win32Filesystem();
+#else
+ default_filesystem_ = new UnixFilesystem();
+#endif
+ return default_filesystem_;
+}
+
+bool CreateUniqueFile(Pathname& path, bool create_empty) {
+ LOG(LS_INFO) << "Path " << path.pathname() << std::endl;
+ // If not folder is supplied, use the temporary folder
+ if (path.folder().empty()) {
+ Pathname temporary_path;
+ if (!Filesystem::GetTemporaryFolder(temporary_path, true, NULL)) {
+ printf("Get temp failed\n");
+ return false;
+ }
+ path.SetFolder(temporary_path.pathname());
+ }
+
+ // If not filename is supplied, use a temporary name
+ if (path.filename().empty()) {
+ std::string folder(path.folder());
+ std::string filename = Filesystem::TempFilename(folder, "gt");
+ path.SetFilename(filename);
+ if (!create_empty) {
+ Filesystem::DeleteFile(path.pathname());
+ }
+ return true;
+ }
+
+ // Otherwise, create a unique name based on the given filename
+ // foo.txt -> foo-N.txt
+ const std::string basename = path.basename();
+ const size_t MAX_VERSION = 100;
+ size_t version = 0;
+ while (version < MAX_VERSION) {
+ std::string pathname = path.pathname();
+
+ if (!Filesystem::FileExists(pathname)) {
+ if (create_empty) {
+ FileStream* fs = Filesystem::OpenFile(pathname,"w");
+ delete fs;
+ }
+ return true;
+ }
+ version += 1;
+ char version_base[MAX_PATH];
+ talk_base::sprintfn(version_base, ARRAY_SIZE(version_base), "%s-%u",
+ basename.c_str(), version);
+ path.SetBasename(version_base);
+ }
+ return true;
+}
+}
diff --git a/Plugins/jingle/libjingle/talk/base/fileutils.h b/Plugins/jingle/libjingle/talk/base/fileutils.h
new file mode 100644
index 0000000..868f4c2
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/fileutils.h
@@ -0,0 +1,185 @@
+/*
+ * libjingle
+ * Copyright 2004--2006, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TALK_BASE_FILEUTILS_H__
+#define TALK_BASE_FILEUTILS_H__
+
+#include <string>
+
+#ifdef _WINDOWS
+#include <windows.h>
+#else
+#include <sys/types.h>
+#include <dirent.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#endif
+
+#include "talk/base/common.h"
+
+namespace talk_base {
+
+class FileStream;
+class Pathname;
+
+//////////////////////////
+// Directory Iterator //
+//////////////////////////
+
+// A DirectoryTraverser is created with a given directory. It originally points to
+// the first file in the directory, and can be advanecd with Next(). This allows you
+// to get information about each file.
+
+class DirectoryIterator {
+
+ public:
+ // Constructor
+ DirectoryIterator();
+
+ // Destructor
+ ~DirectoryIterator();
+
+ // Starts traversing a directory
+ // dir is the directory to traverse
+ // returns true if the directory exists and is valid
+ // The iterator will point to the first entry in the directory
+ bool Iterate(const Pathname &path);
+
+ // Advances to the next file
+ // returns true if there were more files in the directory.
+ bool Next();
+
+ // returns true if the file currently pointed to is a directory
+ bool IsDirectory() const;
+
+ // returns the name of the file currently pointed to
+ std::string Name() const;
+
+ // returns the size of the file currently pointed to
+ size_t FileSize() const;
+
+ // returns the last modified time of the file currently poitned to
+ time_t FileModifyTime() const;
+
+ private:
+ std::string directory_;
+#ifdef _WINDOWS
+ WIN32_FIND_DATA data_;
+ HANDLE handle_;
+#else
+ DIR *dir_;
+ struct dirent *dirent_;
+ struct stat stat_;
+#endif
+};
+
+class Filesystem {
+ public:
+
+ virtual bool CreateFolderI(const Pathname &pathname) = 0;
+
+ // Opens a file. Returns an open StreamInterface if function succeeds. Otherwise,
+ // returns NULL.
+ virtual FileStream *OpenFileI(const Pathname &filename,
+ const std::string &mode) = 0;
+
+ // This will attempt to delete the path located at filename. If filename is a file,
+ // it will be unlinked. If the path is a directory, it will recursively unlink and remove
+ // all the files and directory within it
+ virtual bool DeleteFileI(const Pathname &filename) = 0;
+
+ // Creates a directory. This will call itself recursively to create /foo/bar even if
+ // /foo does not exist.
+ // Returns TRUE if function succeeds
+
+ // This moves a file from old_path to new_path, where "file" can be a plain file
+ // or directory, which will be moved recursively.
+ // Returns true if function succeeds.
+ virtual bool MoveFileI(const Pathname &old_path, const Pathname &new_path) = 0;
+
+ // This copies a file from old_path to _new_path where "file" can be a plain file
+ // or directory, which will be copied recursively.
+ // Returns true if function succeeds
+ virtual bool CopyFileI(const Pathname &old_path, const Pathname &new_path) = 0;
+
+ // Returns true if a pathname is a directory
+ virtual bool IsFolderI(const Pathname& pathname) = 0;
+
+ // Returns true if a file exists at this path
+ virtual bool FileExistsI(const Pathname& pathname) = 0;
+
+ // Returns true if pathname represents a temporary location on the system.
+ virtual bool IsTemporaryPathI(const Pathname& pathname) = 0;
+
+ // A folder appropriate for storing temporary files (Contents are
+ // automatically deleted when the program exists)
+ virtual bool GetTemporaryFolderI(Pathname &path, bool create,
+ const std::string *append) = 0;
+
+ virtual std::string TempFilenameI(const Pathname &dir, const std::string &prefix) = 0;
+
+ virtual bool GetFileSizeI(const Pathname &dir, size_t *size) = 0;
+
+ static Filesystem *default_filesystem(void) { ASSERT(default_filesystem_!=NULL); return default_filesystem_; }
+ static void set_default_filesystem(Filesystem *filesystem) {default_filesystem_ = filesystem; }
+
+
+ static bool CreateFolder(const Pathname &pathname);
+
+ static FileStream *OpenFile(const Pathname &filename,
+ const std::string &mode);
+ static bool DeleteFile(const Pathname &filename);
+ static bool MoveFile(const Pathname &old_path, const Pathname &new_path);
+ static bool CopyFile(const Pathname &old_path, const Pathname &new_path);
+ static bool IsFolder(const Pathname& pathname);
+ static bool FileExists(const Pathname &pathname);
+ static bool IsTemporaryPath(const Pathname& pathname);
+ static bool GetTemporaryFolder(Pathname &path, bool create,
+ const std::string *append);
+ static std::string TempFilename(const Pathname &dir, const std::string &prefix);
+ static bool GetFileSize(const Pathname &dir, size_t *size);
+
+ private:
+ static Filesystem *default_filesystem_;
+ static Filesystem *EnsureDefaultFilesystem();
+
+};
+
+// Generates a unique temporary filename in 'directory' with the given 'prefix'
+ std::string TempFilename(const Pathname &dir, const std::string &prefix);
+
+ // Generates a unique filename based on the input path. If no path component
+ // is specified, it uses the temporary directory. If a filename is provided,
+ // up to 100 variations of form basename-N.extension are tried. When
+ // create_empty is true, an empty file of this name is created (which
+ // decreases the chance of a temporary filename collision with another
+ // process).
+ bool CreateUniqueFile(talk_base::Pathname& path, bool create_empty);
+
+}
+
+#endif // TALK_BASE_FILEUTILS_H__
diff --git a/Plugins/jingle/libjingle/talk/base/firewallsocketserver.cc b/Plugins/jingle/libjingle/talk/base/firewallsocketserver.cc
new file mode 100644
index 0000000..49db8a8
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/firewallsocketserver.cc
@@ -0,0 +1,213 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <cassert>
+#include <algorithm>
+#ifdef OSX
+#include <errno.h>
+#endif
+
+#include "talk/base/firewallsocketserver.h"
+#include "talk/base/asyncsocket.h"
+#include "talk/base/logging.h"
+
+namespace talk_base {
+
+class FirewallSocket : public AsyncSocketAdapter {
+public:
+ FirewallSocket(FirewallSocketServer * server, AsyncSocket * socket, int type)
+ : AsyncSocketAdapter(socket), server_(server), type_(type) {
+ }
+ FirewallSocket(FirewallSocketServer * server, Socket * socket, int type)
+ : AsyncSocketAdapter(socket), server_(server), type_(type) {
+ }
+
+ virtual int Connect(const SocketAddress& addr) {
+ if (type_ == SOCK_STREAM) {
+ if (!server_->Check(FP_TCP, FD_OUT, addr)) {
+ //LOG(INFO) << "FirewallSocket::Connect - Outbound TCP connection denied";
+ // Note: handle this asynchronously?
+ SetError(EHOSTUNREACH);
+ return SOCKET_ERROR;
+ }
+ }
+ return AsyncSocketAdapter::Connect(addr);
+ }
+ virtual int Send(const void * pv, size_t cb) {
+ if (type_ == SOCK_DGRAM) {
+ if (!server_->Check(FP_UDP, FD_OUT, GetRemoteAddress())) {
+ //LOG(INFO) << "FirewallSocket::Send - Outbound UDP packet dropped";
+ return static_cast<int>(cb);
+ }
+ }
+ return AsyncSocketAdapter::Send(pv, cb);
+ }
+ virtual int SendTo(const void * pv, size_t cb, const SocketAddress& addr) {
+ if (type_ == SOCK_DGRAM) {
+ if (!server_->Check(FP_UDP, FD_OUT, addr)) {
+ //LOG(INFO) << "FirewallSocket::SendTo - Outbound UDP packet dropped";
+ return static_cast<int>(cb);
+ }
+ }
+ return AsyncSocketAdapter::SendTo(pv, cb, addr);
+ }
+ virtual int Recv(void * pv, size_t cb) {
+ if (type_ == SOCK_DGRAM) {
+ if (!server_->Check(FP_UDP, FD_IN, GetRemoteAddress())) {
+ while (true) {
+ int res = AsyncSocketAdapter::Recv(pv, cb);
+ if (res <= 0)
+ return res;
+ //LOG(INFO) << "FirewallSocket::Recv - Inbound UDP packet dropped";
+ }
+ }
+ }
+ return AsyncSocketAdapter::Recv(pv, cb);
+ }
+ virtual int RecvFrom(void * pv, size_t cb, SocketAddress * paddr) {
+ if (type_ == SOCK_DGRAM) {
+ while (true) {
+ int res = AsyncSocketAdapter::RecvFrom(pv, cb, paddr);
+ if (res <= 0)
+ return res;
+ if (server_->Check(FP_UDP, FD_IN, *paddr))
+ return res;
+ //LOG(INFO) << "FirewallSocket::RecvFrom - Inbound UDP packet dropped";
+ }
+ }
+ return AsyncSocketAdapter::RecvFrom(pv, cb, paddr);
+ }
+ virtual Socket * Accept(SocketAddress *paddr) {
+ while (Socket * sock = AsyncSocketAdapter::Accept(paddr)) {
+ if (server_->Check(FP_TCP, FD_IN, *paddr))
+ return sock;
+ sock->Close();
+ delete sock;
+ //LOG(INFO) << "FirewallSocket::Accept - Inbound TCP connection denied";
+ }
+ return 0;
+ }
+
+private:
+ FirewallSocketServer * server_;
+ int type_;
+};
+
+FirewallSocketServer::FirewallSocketServer(SocketServer * server, FirewallManager * manager) : server_(server), manager_(manager) {
+ if (manager_)
+ manager_->AddServer(this);
+}
+
+FirewallSocketServer::~FirewallSocketServer() {
+ if (manager_)
+ manager_->RemoveServer(this);
+}
+
+void FirewallSocketServer::AddRule(bool allow, FirewallProtocol p, FirewallDirection d, const SocketAddress& addr) {
+ Rule r;
+ r.allow = allow;
+ r.p = p;
+ r.d = d;
+ r.addr = addr;
+ CritScope scope(&crit_);
+ rules_.push_back(r);
+}
+
+void FirewallSocketServer::ClearRules() {
+ CritScope scope(&crit_);
+ rules_.clear();
+}
+
+bool FirewallSocketServer::Check(FirewallProtocol p, FirewallDirection d, const SocketAddress& addr) {
+ CritScope scope(&crit_);
+ for (size_t i=0; i<rules_.size(); ++i) {
+ const Rule& r = rules_[i];
+ if ((r.p != p) && (r.p != FP_ANY))
+ continue;
+ if ((r.d != d) && (r.d != FD_ANY))
+ continue;
+ if ((r.addr.ip() != addr.ip()) && !r.addr.IsAny())
+ continue;
+ if ((r.addr.port() != addr.port()) && (r.addr.port() != 0))
+ continue;
+ return r.allow;
+ }
+ return true;
+}
+
+Socket* FirewallSocketServer::CreateSocket(int type) {
+ return WrapSocket(server_->CreateSocket(type), type);
+}
+
+AsyncSocket* FirewallSocketServer::CreateAsyncSocket(int type) {
+ return WrapSocket(server_->CreateAsyncSocket(type), type);
+}
+
+Socket * FirewallSocketServer::WrapSocket(Socket * sock, int type) {
+ if (!sock)
+ return NULL;
+ return new FirewallSocket(this, sock, type);
+}
+
+AsyncSocket * FirewallSocketServer::WrapSocket(AsyncSocket * sock, int type) {
+ if (!sock)
+ return NULL;
+ return new FirewallSocket(this, sock, type);
+}
+
+FirewallManager::FirewallManager() {
+}
+
+FirewallManager::~FirewallManager() {
+ assert(servers_.empty());
+}
+
+void FirewallManager::AddServer(FirewallSocketServer * server) {
+ CritScope scope(&crit_);
+ servers_.push_back(server);
+}
+
+void FirewallManager::RemoveServer(FirewallSocketServer * server) {
+ CritScope scope(&crit_);
+ servers_.erase(std::remove(servers_.begin(), servers_.end(), server), servers_.end());
+}
+
+void FirewallManager::AddRule(bool allow, FirewallProtocol p, FirewallDirection d, const SocketAddress& addr) {
+ CritScope scope(&crit_);
+ for (std::vector<FirewallSocketServer *>::const_iterator it = servers_.begin(); it != servers_.end(); ++it) {
+ (*it)->AddRule(allow, p, d, addr);
+ }
+}
+
+void FirewallManager::ClearRules() {
+ CritScope scope(&crit_);
+ for (std::vector<FirewallSocketServer *>::const_iterator it = servers_.begin(); it != servers_.end(); ++it) {
+ (*it)->ClearRules();
+ }
+}
+
+} // namespace talk_base
diff --git a/Plugins/jingle/libjingle/talk/base/firewallsocketserver.h b/Plugins/jingle/libjingle/talk/base/firewallsocketserver.h
new file mode 100644
index 0000000..10c07e6
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/firewallsocketserver.h
@@ -0,0 +1,95 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TALK_BASE_FIREWALLSOCKETSERVER_H__
+#define TALK_BASE_FIREWALLSOCKETSERVER_H__
+
+#include <vector>
+#include "talk/base/socketserver.h"
+#include "talk/base/criticalsection.h"
+
+namespace talk_base {
+
+class FirewallManager;
+
+// This SocketServer shim simulates a rule-based firewall server
+
+enum FirewallProtocol { FP_UDP, FP_TCP, FP_ANY };
+enum FirewallDirection { FD_IN, FD_OUT, FD_ANY };
+
+class FirewallSocketServer : public SocketServer {
+public:
+ FirewallSocketServer(SocketServer * server, FirewallManager * manager = 0);
+ virtual ~FirewallSocketServer();
+
+ void AddRule(bool allow, FirewallProtocol p = FP_ANY, FirewallDirection d = FD_ANY, const SocketAddress& addr = SocketAddress());
+ void ClearRules();
+
+ bool Check(FirewallProtocol p, FirewallDirection d, const SocketAddress& addr);
+
+ virtual Socket* CreateSocket(int type);
+ virtual AsyncSocket* CreateAsyncSocket(int type);
+ virtual bool Wait(int cms, bool process_io) { return server_->Wait(cms, process_io); }
+ virtual void WakeUp() { return server_->WakeUp(); }
+
+ Socket * WrapSocket(Socket * sock, int type);
+ AsyncSocket * WrapSocket(AsyncSocket * sock, int type);
+
+private:
+ SocketServer * server_;
+ FirewallManager * manager_;
+ CriticalSection crit_;
+ struct Rule {
+ bool allow;
+ FirewallProtocol p;
+ FirewallDirection d;
+ SocketAddress addr;
+ };
+ std::vector<Rule> rules_;
+};
+
+// FirewallManager allows you to manage firewalls in multiple threads together
+
+class FirewallManager {
+public:
+ FirewallManager();
+ ~FirewallManager();
+
+ void AddServer(FirewallSocketServer * server);
+ void RemoveServer(FirewallSocketServer * server);
+
+ void AddRule(bool allow, FirewallProtocol p = FP_ANY, FirewallDirection d = FD_ANY, const SocketAddress& addr = SocketAddress());
+ void ClearRules();
+
+private:
+ CriticalSection crit_;
+ std::vector<FirewallSocketServer *> servers_;
+};
+
+} // namespace talk_base
+
+#endif // TALK_BASE_FIREWALLSOCKETSERVER_H__
diff --git a/Plugins/jingle/libjingle/talk/base/helpers.cc b/Plugins/jingle/libjingle/talk/base/helpers.cc
new file mode 100644
index 0000000..627edce
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/helpers.cc
@@ -0,0 +1,148 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "talk/base/helpers.h"
+#include "talk/base/time.h"
+#include <cstdlib>
+#include <cassert>
+
+// TODO: Change this implementation to use OpenSSL's RAND_bytes. That will
+// give cryptographically random values on all platforms.
+
+#ifdef WIN32
+#include <time.h>
+#include <windows.h>
+#endif
+
+namespace cricket {
+
+static long g_seed = 1L;
+
+int GetRandom() {
+ return ((g_seed = g_seed * 214013L + 2531011L) >> 16) & 0x7fff;
+}
+
+static bool s_initrandom;
+
+long GetRandomSeed() {
+ return g_seed;
+}
+
+void SetRandomSeed(unsigned long seed)
+{
+ s_initrandom = true;
+ g_seed = (long)seed;
+}
+
+void InitRandom(const char *client_unique, size_t len) {
+ // Hash this string - unique per client
+
+ uint32 hash = 0;
+ if (client_unique != NULL) {
+ for (int i = 0; i < (int)len; i++)
+ hash = ((hash << 2) + hash) + client_unique[i];
+ }
+
+ // Now initialize the seed against a high resolution
+ // counter
+
+ unsigned long seed = GetRandomSeed();
+
+#ifdef WIN32
+ bool success = false;
+ HCRYPTPROV hProv = NULL;
+ if (CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL,
+ CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) {
+ success = (FALSE != CryptGenRandom(hProv,
+ sizeof(seed),
+ reinterpret_cast<BYTE*>(&seed)));
+ CryptReleaseContext(hProv, 0);
+ }
+
+ if (!success) {
+ LARGE_INTEGER big;
+ QueryPerformanceCounter(&big);
+ seed = big.LowPart;
+ }
+#else
+ seed = talk_base::Time();
+#endif
+
+ SetRandomSeed(seed ^ hash);
+}
+
+const char BASE64[64] = {
+ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
+ 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
+ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
+ 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'
+};
+
+// Generates a random string of the given length. We generate base64 values so
+// that they will be printable, though that's not necessary.
+
+std::string CreateRandomString(int len) {
+ // Random number generator should of been initialized!
+ assert(s_initrandom);
+ if (!s_initrandom)
+ InitRandom(0, 0);
+
+ std::string str;
+ for (int i = 0; i < len; i++)
+#if defined(_MSC_VER) && _MSC_VER < 1300
+ str.insert(str.end(), BASE64[GetRandom() & 63]);
+#else
+ str.push_back(BASE64[GetRandom() & 63]);
+#endif
+ return str;
+}
+
+uint32 CreateRandomId() {
+ uint8 b1 = (uint8)(GetRandom() & 255);
+ uint8 b2 = (uint8)(GetRandom() & 255);
+ uint8 b3 = (uint8)(GetRandom() & 255);
+ uint8 b4 = (uint8)(GetRandom() & 255);
+ return b1 | (b2 << 8) | (b3 << 16) | (b4 << 24);
+}
+
+bool IsBase64Char(char ch) {
+ return (('A' <= ch) && (ch <= 'Z')) ||
+ (('a' <= ch) && (ch <= 'z')) ||
+ (('0' <= ch) && (ch <= '9')) ||
+ (ch == '+') || (ch == '/');
+}
+
+bool IsBase64Encoded(const std::string& str) {
+ for (size_t i = 0; i < str.size(); ++i) {
+ if (!IsBase64Char(str.at(i)))
+ return false;
+ }
+ return true;
+}
+
+} // namespace cricket
diff --git a/Plugins/jingle/libjingle/talk/base/helpers.h b/Plugins/jingle/libjingle/talk/base/helpers.h
new file mode 100644
index 0000000..696edbe
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/helpers.h
@@ -0,0 +1,55 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __HELPERS_H__
+#define __HELPERS_H__
+
+#include "talk/base/basictypes.h"
+#include <string>
+
+namespace cricket {
+
+// srand initializer
+void InitRandom(const char *client_unique, size_t len);
+
+// For testing, the random seed can be directly accessed.
+long GetRandomSeed();
+void SetRandomSeed(unsigned long seed);
+
+// Generates a (cryptographically) random string of the given length.
+std::string CreateRandomString(int length);
+
+// Generates a random id
+uint32 CreateRandomId();
+
+// Determines whether the given string consists entirely of valid base64
+// encoded characters.
+bool IsBase64Encoded(const std::string& str);
+
+} // namespace cricket
+
+#endif // __HELPERS_H__
diff --git a/Plugins/jingle/libjingle/talk/base/host.cc b/Plugins/jingle/libjingle/talk/base/host.cc
new file mode 100644
index 0000000..df5f7f7
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/host.cc
@@ -0,0 +1,100 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <string>
+#include <iostream>
+#include <cassert>
+#include <errno.h>
+
+#ifdef POSIX
+extern "C" {
+#include <sys/utsname.h>
+}
+#endif // POSIX
+
+#include "talk/base/host.h"
+#include "talk/base/logging.h"
+#include "talk/base/network.h"
+#include "talk/base/socket.h"
+
+#if defined(_MSC_VER) && _MSC_VER < 1300
+namespace std {
+ using ::strerror;
+ using ::exit;
+}
+#endif
+
+namespace talk_base {
+
+namespace {
+
+void FatalError(const std::string& name, int err) {
+ PLOG(LERROR, err) << name;
+ std::exit(1);
+}
+
+}
+
+#ifdef POSIX
+std::string GetHostName() {
+ struct utsname nm;
+ if (uname(&nm) < 0)
+ FatalError("uname", errno);
+ return std::string(nm.nodename);
+}
+#endif
+
+#ifdef WIN32
+std::string GetHostName() {
+ // TODO: fix this
+ return "cricket";
+}
+#endif
+
+// Records information about the local host.
+Host* gLocalHost = 0;
+
+const Host& LocalHost() {
+ if (!gLocalHost) {
+ std::vector<Network*>* networks = new std::vector<Network*>;
+ NetworkManager::CreateNetworks(*networks);
+#ifdef WIN32
+ // This is sort of problematic... one part of the code (the unittests) wants
+ // 127.0.0.1 to be present and another part (port allocators) don't. Right
+ // now, they use different APIs, so we can have different behavior. But
+ // there is something wrong with this.
+ networks->push_back(new Network("localhost",
+ SocketAddress::StringToIP("127.0.0.1")));
+#endif
+ gLocalHost = new Host(GetHostName(), networks);
+ assert(gLocalHost->networks().size() > 0);
+ }
+
+ return *gLocalHost;
+}
+
+} // namespace talk_base
diff --git a/Plugins/jingle/libjingle/talk/base/host.h b/Plugins/jingle/libjingle/talk/base/host.h
new file mode 100644
index 0000000..7ee603d
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/host.h
@@ -0,0 +1,59 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TALK_BASE_HOST_H__
+#define TALK_BASE_HOST_H__
+
+#include <string>
+#include <vector>
+#include "talk/base/network.h"
+
+namespace talk_base {
+
+// Provides information about a host in the network.
+class Host {
+public:
+ Host(const std::string& name, std::vector<Network*>* networks)
+ : name_(name), networks_(networks) { }
+
+ const std::string& name() const { return name_; }
+ const std::vector<Network*>& networks() const { return *networks_; }
+
+private:
+ std::string name_;
+ std::vector<Network*>* networks_;
+};
+
+// Returns a reference to the description of the local host.
+const Host& LocalHost();
+
+// Returns the name of the local host.
+std::string GetHostName();
+
+} // namespace talk_base
+
+#endif // TALK_BASE_HOST_H__
diff --git a/Plugins/jingle/libjingle/talk/base/httpbase.cc b/Plugins/jingle/libjingle/talk/base/httpbase.cc
new file mode 100644
index 0000000..06b7378
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/httpbase.cc
@@ -0,0 +1,591 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifdef OSX
+#include <errno.h>
+#endif
+
+#ifdef WIN32
+#include "talk/base/win32.h"
+#else // !WIN32
+#define SEC_E_CERT_EXPIRED (-2146893016)
+#endif // !WIN32
+
+#include "talk/base/common.h"
+#include "talk/base/httpbase.h"
+#include "talk/base/logging.h"
+#include "talk/base/socket.h"
+#include "talk/base/stringutils.h"
+
+namespace talk_base {
+
+//////////////////////////////////////////////////////////////////////
+// Helpers
+//////////////////////////////////////////////////////////////////////
+
+bool MatchHeader(const char* str, size_t len, HttpHeader header) {
+ const char* const header_str = ToString(header);
+ const size_t header_len = strlen(header_str);
+ return (len == header_len) && (_strnicmp(str, header_str, header_len) == 0);
+}
+
+//////////////////////////////////////////////////////////////////////
+// HttpParser
+//////////////////////////////////////////////////////////////////////
+
+HttpParser::HttpParser() {
+ reset();
+}
+
+HttpParser::~HttpParser() {
+}
+
+void
+HttpParser::reset() {
+ state_ = ST_LEADER;
+ chunked_ = false;
+ data_size_ = SIZE_UNKNOWN;
+}
+
+bool
+HttpParser::process(const char* buffer, size_t len, size_t& processed,
+ HttpError& err) {
+ processed = 0;
+ err = HE_NONE;
+
+ if (state_ >= ST_COMPLETE) {
+ ASSERT(false);
+ return false;
+ }
+
+ while (true) {
+ if (state_ < ST_DATA) {
+ size_t pos = processed;
+ while ((pos < len) && (buffer[pos] != '\n')) {
+ pos += 1;
+ }
+ if (pos >= len) {
+ break; // don't have a full header
+ }
+ const char* line = buffer + processed;
+ size_t len = (pos - processed);
+ processed = pos + 1;
+ while ((len > 0) && isspace(static_cast<unsigned char>(line[len-1]))) {
+ len -= 1;
+ }
+ if (!process_line(line, len, err)) {
+ return false; // no more processing
+ }
+ } else if (data_size_ == 0) {
+ if (chunked_) {
+ state_ = ST_CHUNKTERM;
+ } else {
+ return false;
+ }
+ } else {
+ size_t available = len - processed;
+ if (available <= 0) {
+ break; // no more data
+ }
+ if ((data_size_ != SIZE_UNKNOWN) && (available > data_size_)) {
+ available = data_size_;
+ }
+ size_t read = 0;
+ err = onHttpRecvData(buffer + processed, available, read);
+ if (err != HE_NONE) {
+ return false; // error occurred
+ }
+ processed += read;
+ if (data_size_ != SIZE_UNKNOWN) {
+ data_size_ -= read;
+ }
+ }
+ }
+
+ return true;
+}
+
+bool
+HttpParser::process_line(const char* line, size_t len, HttpError& err) {
+ switch (state_) {
+ case ST_LEADER:
+ state_ = ST_HEADERS;
+ err = onHttpRecvLeader(line, len);
+ break;
+
+ case ST_HEADERS:
+ if (len > 0) {
+ const char* value = strchrn(line, len, ':');
+ if (!value) {
+ err = HE_PROTOCOL;
+ break;
+ }
+ size_t nlen = (value - line);
+ const char* eol = line + len;
+ do {
+ value += 1;
+ } while ((value < eol) && isspace(static_cast<unsigned char>(*value)));
+ size_t vlen = eol - value;
+ if (MatchHeader(line, nlen, HH_CONTENT_LENGTH)) {
+ if (sscanf(value, "%d", &data_size_) != 1) {
+ err = HE_PROTOCOL;
+ break;
+ }
+ } else if (MatchHeader(line, nlen, HH_TRANSFER_ENCODING)) {
+ if ((vlen == 7) && (_strnicmp(value, "chunked", 7) == 0)) {
+ chunked_ = true;
+ } else if ((vlen == 8) && (_strnicmp(value, "identity", 8) == 0)) {
+ chunked_ = false;
+ } else {
+ err = HE_PROTOCOL;
+ break;
+ }
+ }
+ err = onHttpRecvHeader(line, nlen, value, vlen);
+ } else {
+ state_ = chunked_ ? ST_CHUNKSIZE : ST_DATA;
+ err = onHttpRecvHeaderComplete(chunked_, data_size_);
+ }
+ break;
+
+ case ST_CHUNKSIZE:
+ if (len > 0) {
+ char* ptr = NULL;
+ data_size_ = strtoul(line, &ptr, 16);
+ if (ptr != line + len) {
+ err = HE_PROTOCOL;
+ break;
+ }
+ state_ = (data_size_ == 0) ? ST_TRAILERS : ST_DATA;
+ } else {
+ err = HE_PROTOCOL;
+ }
+ break;
+
+ case ST_CHUNKTERM:
+ if (len > 0) {
+ err = HE_PROTOCOL;
+ } else {
+ state_ = chunked_ ? ST_CHUNKSIZE : ST_DATA;
+ }
+ break;
+
+ case ST_TRAILERS:
+ if (len == 0) {
+ return false;
+ }
+ // err = onHttpRecvTrailer();
+ break;
+
+ default:
+ break;
+ }
+
+ return (err == HE_NONE);
+}
+
+void
+HttpParser::end_of_input() {
+ if ((state_ == ST_DATA) && (data_size_ == SIZE_UNKNOWN)) {
+ complete(HE_NONE);
+ } else {
+ complete(HE_DISCONNECTED);
+ }
+}
+
+void
+HttpParser::complete(HttpError err) {
+ if (state_ < ST_COMPLETE) {
+ state_ = ST_COMPLETE;
+ onHttpRecvComplete(err);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////
+// HttpBase
+//////////////////////////////////////////////////////////////////////
+
+HttpBase::HttpBase() : mode_(HM_NONE), data_(NULL), notify_(NULL),
+ stream_(NULL) {
+}
+
+HttpBase::~HttpBase() {
+}
+
+bool
+HttpBase::isConnected() const {
+ return (stream_ != NULL) && (stream_->GetState() == SS_OPEN);
+}
+
+bool
+HttpBase::attach(StreamInterface* stream) {
+ if ((mode_ != HM_NONE) || (stream_ != NULL) || (stream == NULL)) {
+ ASSERT(false);
+ return false;
+ }
+ stream_ = stream;
+ stream_->SignalEvent.connect(this, &HttpBase::OnEvent);
+ mode_ = (stream_->GetState() == SS_OPENING) ? HM_CONNECT : HM_NONE;
+ return true;
+}
+
+StreamInterface*
+HttpBase::detach() {
+ if (mode_ != HM_NONE) {
+ ASSERT(false);
+ return NULL;
+ }
+ StreamInterface* stream = stream_;
+ stream_ = NULL;
+ if (stream) {
+ stream->SignalEvent.disconnect(this);
+ }
+ return stream;
+}
+
+/*
+bool
+HttpBase::accept(PNSocket& socket) {
+ if (mode_ != HM_NONE) {
+ ASSERT(false);
+ return false;
+ }
+
+ return socket.accept(stream_);
+}
+
+void
+HttpBase::connect(const SocketAddress& addr) {
+ if (mode_ != HM_NONE) {
+ ASSERT(false);
+ return;
+ }
+
+ mode_ = HM_CONNECT;
+
+ SocketAddress local;
+ if (!stream_.connect(local, addr) && !stream_.isBlocking()) {
+ onSocketConnect(&stream_, stream_.getError());
+ }
+}
+*/
+void
+HttpBase::send(HttpData* data) {
+ if (mode_ != HM_NONE) {
+ ASSERT(false);
+ return;
+ } else if (!isConnected()) {
+ OnEvent(stream_, SE_CLOSE, HE_DISCONNECTED);
+ return;
+ }
+
+ mode_ = HM_SEND;
+ data_ = data;
+ len_ = 0;
+ ignore_data_ = chunk_data_ = false;
+
+ std::string encoding;
+ if (data_->hasHeader(HH_TRANSFER_ENCODING, &encoding)
+ && (encoding == "chunked")) {
+ chunk_data_ = true;
+ }
+
+ len_ = data_->formatLeader(buffer_, sizeof(buffer_));
+ len_ += strcpyn(buffer_ + len_, sizeof(buffer_) - len_, "\r\n");
+ header_ = data_->begin();
+ queue_headers();
+
+ OnEvent(stream_, SE_WRITE, 0);
+}
+
+void
+HttpBase::recv(HttpData* data) {
+ if (mode_ != HM_NONE) {
+ ASSERT(false);
+ return;
+ } else if (!isConnected()) {
+ OnEvent(stream_, SE_CLOSE, HE_DISCONNECTED);
+ return;
+ }
+
+ mode_ = HM_RECV;
+ data_ = data;
+ len_ = 0;
+ ignore_data_ = chunk_data_ = false;
+
+ reset();
+ OnEvent(stream_, SE_READ, 0);
+}
+
+void
+HttpBase::abort(HttpError err) {
+ if (mode_ != HM_NONE) {
+ if (stream_ != NULL) {
+ stream_->Close();
+ }
+ do_complete(err);
+ }
+}
+
+void
+HttpBase::flush_data() {
+ while (true) {
+ for (size_t start = 0; start < len_; ) {
+ size_t written;
+ int error;
+ StreamResult result = stream_->Write(buffer_ + start, len_ - start,
+ &written, &error);
+ if (result == SR_SUCCESS) {
+ //LOG_F(LS_INFO) << "wrote " << res << " bytes";
+ start += written;
+ continue;
+ } else if (result == SR_BLOCK) {
+ //LOG_F(LS_INFO) << "blocking";
+ len_ -= start;
+ memmove(buffer_, buffer_ + start, len_);
+ return;
+ } else {
+ ASSERT(result == SR_ERROR);
+ LOG_F(LS_ERROR) << "error";
+ OnEvent(stream_, SE_CLOSE, error);
+ return;
+ }
+ }
+ len_ = 0;
+
+ // Check for more headers
+ if (header_ != data_->end()) {
+ queue_headers();
+ continue;
+ }
+
+ // Check for document data
+ if (!data_->document.get())
+ break;
+
+ size_t offset = 0, reserve = 0;
+ if (chunk_data_) {
+ // Reserve 10 characters at the start for 8-byte hex value and \r\n
+ offset = 10;
+ // ... and 2 characters at the end for \r\n
+ reserve = offset + 2;
+ ASSERT(reserve < sizeof(buffer_));
+ }
+
+ int error = 0;
+ StreamResult result = data_->document->Read(buffer_ + offset,
+ sizeof(buffer_) - reserve,
+ &len_, &error);
+ if (result == SR_SUCCESS) {
+ if (!chunk_data_)
+ continue;
+
+ // Prepend the length and append \r\n
+ sprintfn(buffer_, offset, "%.*x", (offset - 2), len_);
+ memcpy(buffer_ + offset - 2, "\r\n", 2);
+ memcpy(buffer_ + offset + len_, "\r\n", 2);
+ ASSERT(len_ + reserve <= sizeof(buffer_));
+ len_ += reserve;
+ } else if (result == SR_EOS) {
+ if (!chunk_data_)
+ break;
+
+ // Append the empty chunk and empty trailers, then turn off chunking.
+ len_ = sprintfn(buffer_, sizeof(buffer_), "0\r\n\r\n");
+ chunk_data_ = false;
+ } else {
+ LOG_F(LS_ERROR) << "Read error: " << error;
+ do_complete(HE_STREAM);
+ return;
+ }
+ }
+
+ do_complete();
+}
+
+void
+HttpBase::queue_headers() {
+ while (header_ != data_->end()) {
+ size_t len = sprintfn(buffer_ + len_, sizeof(buffer_) - len_,
+ "%.*s: %.*s\r\n",
+ header_->first.size(), header_->first.data(),
+ header_->second.size(), header_->second.data());
+ if (len_ + len < sizeof(buffer_) - 3) {
+ len_ += len;
+ ++header_;
+ } else if (len_ == 0) {
+ LOG(WARNING) << "discarding header that is too long: " << header_->first;
+ ++header_;
+ } else {
+ break;
+ }
+ }
+ if (header_ == data_->end()) {
+ len_ += strcpyn(buffer_ + len_, sizeof(buffer_) - len_, "\r\n");
+ }
+}
+
+void
+HttpBase::do_complete(HttpError err) {
+ ASSERT(mode_ != HM_NONE);
+ HttpMode mode = mode_;
+ mode_ = HM_NONE;
+ data_ = NULL;
+ if (notify_) {
+ notify_->onHttpComplete(mode, err);
+ }
+}
+
+void
+HttpBase::OnEvent(StreamInterface* stream, int events, int error) {
+ if ((events & SE_OPEN) && (mode_ == HM_CONNECT)) {
+ do_complete();
+ return;
+ }
+
+ if ((events & SE_WRITE) && (mode_ == HM_SEND)) {
+ flush_data();
+ return;
+ }
+
+ if ((events & SE_READ) && (mode_ == HM_RECV)) {
+ // Do to the latency between receiving read notifications from
+ // pseudotcpchannel, we rely on repeated calls to read in order to acheive
+ // ideal throughput. The number of reads is limited to prevent starving
+ // the caller.
+ size_t loop_count = 0;
+ const size_t kMaxReadCount = 20;
+ while (true) {
+ if (len_ >= sizeof(buffer_)) {
+ do_complete(HE_OVERFLOW);
+ return;
+ }
+ size_t read;
+ int error;
+ StreamResult result = stream_->Read(buffer_ + len_,
+ sizeof(buffer_) - len_,
+ &read, &error);
+ if ((result == SR_BLOCK) || (result == SR_EOS))
+ return;
+ if (result == SR_ERROR) {
+ OnEvent(stream_, SE_CLOSE, error);
+ return;
+ }
+ ASSERT(result == SR_SUCCESS);
+ //LOG(INFO) << "HttpBase << " << std::string(buffer_ + len_, res);
+ len_ += read;
+ HttpError herr;
+ bool more = process(buffer_, len_, read, herr);
+ len_ -= read;
+ memcpy(buffer_, buffer_ + read, len_);
+ if (!more) {
+ complete(herr);
+ return;
+ }
+ if (++loop_count > kMaxReadCount) {
+ LOG_F(LS_WARNING) << "danger of starvation";
+ break;
+ }
+ }
+ return;
+ }
+
+ if ((events & SE_CLOSE) == 0)
+ return;
+
+ if (stream_ != NULL) {
+ stream_->Close();
+ }
+ HttpError herr;
+ // TODO: Pass through errors instead of translating them?
+ if (error == 0) {
+ herr = HE_DISCONNECTED;
+ } else if (error == SOCKET_EACCES) {
+ herr = HE_AUTH;
+ } else if (error == SEC_E_CERT_EXPIRED) {
+ herr = HE_CERTIFICATE_EXPIRED;
+ } else {
+ LOG_F(LS_ERROR) << "SE_CLOSE error: " << error;
+ herr = HE_SOCKET;
+ }
+ if ((mode_ == HM_RECV) && (error == HE_NONE)) {
+ end_of_input();
+ } else if (mode_ != HM_NONE) {
+ do_complete(mkerr(herr, HE_DISCONNECTED));
+ } else if (notify_) {
+ notify_->onHttpClosed(mkerr(herr, HE_DISCONNECTED));
+ }
+}
+
+//
+// HttpParser Implementation
+//
+
+HttpError
+HttpBase::onHttpRecvLeader(const char* line, size_t len) {
+ return data_->parseLeader(line, len);
+}
+
+HttpError
+HttpBase::onHttpRecvHeader(const char* name, size_t nlen, const char* value,
+ size_t vlen) {
+ std::string sname(name, nlen), svalue(value, vlen);
+ data_->addHeader(sname, svalue);
+ //LOG(INFO) << sname << ": " << svalue;
+ return HE_NONE;
+}
+
+HttpError
+HttpBase::onHttpRecvHeaderComplete(bool chunked, size_t& data_size) {
+ return notify_ ? notify_->onHttpHeaderComplete(chunked, data_size) : HE_NONE;
+}
+
+HttpError
+HttpBase::onHttpRecvData(const char* data, size_t len, size_t& read) {
+ if (ignore_data_ || !data_->document.get()) {
+ read = len;
+ return HE_NONE;
+ }
+ int error = 0;
+ switch (data_->document->Write(data, len, &read, &error)) {
+ case SR_SUCCESS:
+ return HE_NONE;
+ case SR_EOS:
+ case SR_BLOCK:
+ LOG_F(LS_ERROR) << "Write EOS or block";
+ return HE_STREAM;
+ }
+ LOG_F(LS_ERROR) << "Write error: " << error;
+ return HE_STREAM;
+}
+
+void
+HttpBase::onHttpRecvComplete(HttpError err) {
+ do_complete(err);
+}
+
+} // namespace talk_base
diff --git a/Plugins/jingle/libjingle/talk/base/httpbase.h b/Plugins/jingle/libjingle/talk/base/httpbase.h
new file mode 100644
index 0000000..64aefe9
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/httpbase.h
@@ -0,0 +1,142 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TALK_BASE_HTTPBASE_H__
+#define TALK_BASE_HTTPBASE_H__
+
+#include "talk/base/httpcommon.h"
+
+namespace talk_base {
+
+class StreamInterface;
+
+//////////////////////////////////////////////////////////////////////
+// HttpParser
+//////////////////////////////////////////////////////////////////////
+
+class HttpParser {
+public:
+ HttpParser();
+ virtual ~HttpParser();
+
+ void reset();
+ bool process(const char* buffer, size_t len, size_t& read, HttpError& err);
+ void end_of_input();
+ void complete(HttpError err);
+
+protected:
+ bool process_line(const char* line, size_t len, HttpError& err);
+
+ // HttpParser Interface
+ virtual HttpError onHttpRecvLeader(const char* line, size_t len) = 0;
+ virtual HttpError onHttpRecvHeader(const char* name, size_t nlen,
+ const char* value, size_t vlen) = 0;
+ virtual HttpError onHttpRecvHeaderComplete(bool chunked, size_t& data_size) = 0;
+ virtual HttpError onHttpRecvData(const char* data, size_t len, size_t& read) = 0;
+ virtual void onHttpRecvComplete(HttpError err) = 0;
+
+private:
+ enum State {
+ ST_LEADER, ST_HEADERS,
+ ST_CHUNKSIZE, ST_CHUNKTERM, ST_TRAILERS,
+ ST_DATA, ST_COMPLETE
+ } state_;
+ bool chunked_;
+ size_t data_size_;
+};
+
+//////////////////////////////////////////////////////////////////////
+// IHttpNotify
+//////////////////////////////////////////////////////////////////////
+
+enum HttpMode { HM_NONE, HM_CONNECT, HM_RECV, HM_SEND };
+
+class IHttpNotify {
+public:
+ virtual HttpError onHttpHeaderComplete(bool chunked, size_t& data_size) = 0;
+ virtual void onHttpComplete(HttpMode mode, HttpError err) = 0;
+ virtual void onHttpClosed(HttpError err) = 0;
+};
+
+//////////////////////////////////////////////////////////////////////
+// HttpBase
+//////////////////////////////////////////////////////////////////////
+
+class HttpBase : private HttpParser, public sigslot::has_slots<> {
+public:
+ HttpBase();
+ virtual ~HttpBase();
+
+ void notify(IHttpNotify* notify) { notify_ = notify; }
+ bool attach(StreamInterface* stream);
+ StreamInterface* stream() { return stream_; }
+ StreamInterface* detach();
+ bool isConnected() const;
+
+ void send(HttpData* data);
+ void recv(HttpData* data);
+ void abort(HttpError err);
+
+ HttpMode mode() const { return mode_; }
+
+ void set_ignore_data(bool ignore) { ignore_data_ = ignore; }
+ bool ignore_data() const { return ignore_data_; }
+
+protected:
+ void flush_data();
+ void queue_headers();
+ void do_complete(HttpError err = HE_NONE);
+
+ void OnEvent(StreamInterface* stream, int events, int error);
+
+ // HttpParser Interface
+ virtual HttpError onHttpRecvLeader(const char* line, size_t len);
+ virtual HttpError onHttpRecvHeader(const char* name, size_t nlen,
+ const char* value, size_t vlen);
+ virtual HttpError onHttpRecvHeaderComplete(bool chunked, size_t& data_size);
+ virtual HttpError onHttpRecvData(const char* data, size_t len, size_t& read);
+ virtual void onHttpRecvComplete(HttpError err);
+
+private:
+ enum { kBufferSize = 32 * 1024 };
+
+ HttpMode mode_;
+ HttpData* data_;
+ IHttpNotify* notify_;
+ StreamInterface* stream_;
+ char buffer_[kBufferSize];
+ size_t len_;
+
+ bool ignore_data_, chunk_data_;
+ HttpData::const_iterator header_;
+};
+
+//////////////////////////////////////////////////////////////////////
+
+} // namespace talk_base
+
+#endif // TALK_BASE_HTTPBASE_H__
diff --git a/Plugins/jingle/libjingle/talk/base/httpclient.cc b/Plugins/jingle/libjingle/talk/base/httpclient.cc
new file mode 100644
index 0000000..49c5070
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/httpclient.cc
@@ -0,0 +1,714 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <time.h>
+
+#include "talk/base/httpcommon-inl.h"
+
+#include "talk/base/asyncsocket.h"
+#include "talk/base/common.h"
+#include "talk/base/diskcache.h"
+#include "talk/base/httpclient.h"
+#include "talk/base/logging.h"
+#include "talk/base/pathutils.h"
+#include "talk/base/socketstream.h"
+#include "talk/base/stringencode.h"
+#include "talk/base/stringutils.h"
+#include "talk/base/basicdefs.h"
+
+namespace talk_base {
+
+//////////////////////////////////////////////////////////////////////
+// Helpers
+//////////////////////////////////////////////////////////////////////
+
+namespace {
+
+const size_t kCacheHeader = 0;
+const size_t kCacheBody = 1;
+
+std::string HttpAddress(const SocketAddress& address) {
+ return (address.port() == HTTP_DEFAULT_PORT)
+ ? address.hostname() : address.ToString();
+}
+
+// Convert decimal string to integer
+bool HttpStringToInt(const std::string& str, unsigned long* val) {
+ ASSERT(NULL != val);
+ char* eos = NULL;
+ *val = strtoul(str.c_str(), &eos, 10);
+ return (*eos == '\0');
+}
+
+bool HttpShouldCache(const HttpRequestData& request,
+ const HttpResponseData& response) {
+ bool verb_allows_cache = (request.verb == HV_GET)
+ || (request.verb == HV_HEAD);
+ bool is_range_response = response.hasHeader(HH_CONTENT_RANGE, NULL);
+ bool has_expires = response.hasHeader(HH_EXPIRES, NULL);
+ bool request_allows_cache =
+ has_expires || (std::string::npos != request.path.find('?'));
+ bool response_allows_cache =
+ has_expires || HttpCodeIsCacheable(response.scode);
+
+ bool may_cache = verb_allows_cache
+ && request_allows_cache
+ && response_allows_cache
+ && !is_range_response;
+
+ std::string value;
+ if (response.hasHeader(HH_CACHE_CONTROL, &value)) {
+ HttpAttributeList directives;
+ HttpParseAttributes(value.data(), value.size(), directives);
+ // Response Directives Summary:
+ // public - always cacheable
+ // private - do not cache in a shared cache
+ // no-cache - may cache, but must revalidate whether fresh or stale
+ // no-store - sensitive information, do not cache or store in any way
+ // max-age - supplants Expires for staleness
+ // s-maxage - use as max-age for shared caches, ignore otherwise
+ // must-revalidate - may cache, but must revalidate after stale
+ // proxy-revalidate - shared cache must revalidate
+ if (HttpHasAttribute(directives, "no-store", NULL)) {
+ may_cache = false;
+ } else if (HttpHasAttribute(directives, "public", NULL)) {
+ may_cache = true;
+ }
+ }
+ return may_cache;
+}
+
+enum HttpCacheState {
+ HCS_FRESH, // In cache, may use
+ HCS_STALE, // In cache, must revalidate
+ HCS_NONE // Not in cache
+};
+
+HttpCacheState HttpGetCacheState(const HttpRequestData& request,
+ const HttpResponseData& response) {
+ // Temporaries
+ std::string s_temp;
+ unsigned long i_temp;
+
+ // Current time
+ unsigned long now = time(0);
+
+ HttpAttributeList cache_control;
+ if (response.hasHeader(HH_CACHE_CONTROL, &s_temp)) {
+ HttpParseAttributes(s_temp.data(), s_temp.size(), cache_control);
+ }
+
+ // Compute age of cache document
+ unsigned long date;
+ if (!response.hasHeader(HH_DATE, &s_temp)
+ || !HttpDateToSeconds(s_temp, &date))
+ return HCS_NONE;
+
+ // TODO: Timestamp when cache request sent and response received?
+ unsigned long request_time = date;
+ unsigned long response_time = date;
+
+ unsigned long apparent_age = 0;
+ if (response_time > date) {
+ apparent_age = response_time - date;
+ }
+
+ unsigned long corrected_received_age = apparent_age;
+ if (response.hasHeader(HH_AGE, &s_temp)
+ && HttpStringToInt(s_temp, &i_temp)) {
+ corrected_received_age = stdmax(apparent_age, i_temp);
+ }
+
+ unsigned long response_delay = response_time - request_time;
+ unsigned long corrected_initial_age = corrected_received_age + response_delay;
+ unsigned long resident_time = now - response_time;
+ unsigned long current_age = corrected_initial_age + resident_time;
+
+ // Compute lifetime of document
+ unsigned long lifetime;
+ if (HttpHasAttribute(cache_control, "max-age", &s_temp)) {
+ lifetime = atoi(s_temp.c_str());
+ } else if (response.hasHeader(HH_EXPIRES, &s_temp)
+ && HttpDateToSeconds(s_temp, &i_temp)) {
+ lifetime = i_temp - date;
+ } else if (response.hasHeader(HH_LAST_MODIFIED, &s_temp)
+ && HttpDateToSeconds(s_temp, &i_temp)) {
+ // TODO: Issue warning 113 if age > 24 hours
+ lifetime = (now - i_temp) / 10;
+ } else {
+ return HCS_STALE;
+ }
+
+ return (lifetime > current_age) ? HCS_FRESH : HCS_STALE;
+}
+
+enum HttpValidatorStrength {
+ HVS_NONE,
+ HVS_WEAK,
+ HVS_STRONG
+};
+
+HttpValidatorStrength
+HttpRequestValidatorLevel(const HttpRequestData& request) {
+ if (HV_GET != request.verb)
+ return HVS_STRONG;
+ return request.hasHeader(HH_RANGE, NULL) ? HVS_STRONG : HVS_WEAK;
+}
+
+HttpValidatorStrength
+HttpResponseValidatorLevel(const HttpResponseData& response) {
+ std::string value;
+ if (response.hasHeader(HH_ETAG, &value)) {
+ bool is_weak = (strnicmp(value.c_str(), "W/", 2) == 0);
+ return is_weak ? HVS_WEAK : HVS_STRONG;
+ }
+ if (response.hasHeader(HH_LAST_MODIFIED, &value)) {
+ unsigned long last_modified, date;
+ if (HttpDateToSeconds(value, &last_modified)
+ && response.hasHeader(HH_DATE, &value)
+ && HttpDateToSeconds(value, &date)
+ && (last_modified + 60 < date)) {
+ return HVS_STRONG;
+ }
+ return HVS_WEAK;
+ }
+ return HVS_NONE;
+}
+
+std::string GetCacheID(const SocketAddress& server,
+ const HttpRequestData& request) {
+ std::string url;
+ url.append(ToString(request.verb));
+ url.append("_");
+ if ((_strnicmp(request.path.c_str(), "http://", 7) == 0)
+ || (_strnicmp(request.path.c_str(), "https://", 8) == 0)) {
+ url.append(request.path);
+ } else {
+ url.append("http://");
+ url.append(HttpAddress(server));
+ url.append(request.path);
+ }
+ return url;
+}
+
+} // anonymous namespace
+
+//////////////////////////////////////////////////////////////////////
+// HttpClient
+//////////////////////////////////////////////////////////////////////
+
+HttpClient::HttpClient(const std::string& agent, StreamPool* pool)
+: agent_(agent), pool_(pool), fail_redirect_(false), absolute_uri_(false),
+ cache_(NULL), cache_state_(CS_READY)
+{
+ base_.notify(this);
+}
+
+HttpClient::~HttpClient() {
+ base_.notify(NULL);
+ base_.abort(HE_SHUTDOWN);
+ release();
+}
+
+void HttpClient::reset() {
+ server_.Clear();
+ request_.clear(true);
+ response_.clear(true);
+ context_.reset();
+ base_.abort(HE_OPERATION_CANCELLED);
+}
+
+void HttpClient::set_server(const SocketAddress& address) {
+ server_ = address;
+ // Setting 'Host' here allows it to be overridden before starting the request,
+ // if necessary.
+ request_.setHeader(HH_HOST, HttpAddress(server_), true);
+}
+
+void HttpClient::start() {
+ if (base_.mode() != HM_NONE) {
+ // call reset() to abort an in-progress request
+ ASSERT(false);
+ return;
+ }
+
+ ASSERT(!IsCacheActive());
+
+ if (request_.hasHeader(HH_TRANSFER_ENCODING, NULL)) {
+ // Exact size must be known on the client. Instead of using chunked
+ // encoding, wrap data with auto-caching file or memory stream.
+ ASSERT(false);
+ return;
+ }
+
+ // If no content has been specified, using length of 0.
+ request_.setHeader(HH_CONTENT_LENGTH, "0", false);
+
+ request_.setHeader(HH_USER_AGENT, agent_, false);
+ request_.setHeader(HH_CONNECTION, "Keep-Alive", false);
+ if (_strnicmp(request_.path.c_str(), "http", 4) == 0) {
+ request_.setHeader(HH_PROXY_CONNECTION, "Keep-Alive", false);
+ }
+
+ bool absolute_uri = absolute_uri_;
+ if (PROXY_HTTPS == proxy_.type) {
+ request().version = HVER_1_0;
+ // Proxies require canonical form
+ absolute_uri = true;
+ }
+
+ // Convert to canonical form (if not already)
+ if (absolute_uri && (_strnicmp(request().path.c_str(), "http://", 7) != 0)) {
+ std::string canonical_path("http://");
+ canonical_path.append(HttpAddress(server_));
+ canonical_path.append(request().path);
+ request().path = canonical_path;
+ }
+
+ if ((NULL != cache_) && CheckCache()) {
+ return;
+ }
+
+ int stream_err;
+ StreamInterface* stream = pool_->RequestConnectedStream(server_, &stream_err);
+ if (stream == NULL) {
+ if (stream_err)
+ LOG(LS_ERROR) << "RequestConnectedStream returned: " << stream_err;
+ onHttpComplete(HM_CONNECT, (stream_err == 0) ? HE_NONE : HE_SOCKET);
+ } else {
+ base_.attach(stream);
+ if (stream->GetState() == SS_OPEN) {
+ base_.send(&request_);
+ }
+ }
+}
+
+void HttpClient::prepare_get(const std::string& url) {
+ reset();
+ Url<char> purl(url);
+ set_server(SocketAddress(purl.server(), purl.port(), false));
+ request().verb = HV_GET;
+ request().path = purl.full_path();
+}
+
+void HttpClient::prepare_post(const std::string& url,
+ const std::string& content_type,
+ StreamInterface* request_doc) {
+ reset();
+ Url<char> purl(url);
+ set_server(SocketAddress(purl.server(), purl.port(), false));
+ request().verb = HV_POST;
+ request().path = purl.full_path();
+ request().setContent(content_type, request_doc);
+}
+
+void HttpClient::release() {
+ if (StreamInterface* stream = base_.detach()) {
+ pool_->ReturnConnectedStream(stream);
+ }
+}
+
+bool HttpClient::BeginCacheFile() {
+ ASSERT(NULL != cache_);
+ ASSERT(CS_READY == cache_state_);
+
+ std::string id = GetCacheID(server_, request_);
+ CacheLock lock(cache_, id, true);
+ if (!lock.IsLocked()) {
+ LOG_F(LS_WARNING) << "Couldn't lock cache";
+ return false;
+ }
+
+ if (HE_NONE != WriteCacheHeaders(id)) {
+ return false;
+ }
+
+ scoped_ptr<StreamInterface> stream(cache_->WriteResource(id, kCacheBody));
+ if (!stream.get()) {
+ LOG_F(LS_ERROR) << "Couldn't open body cache";
+ return false;
+ }
+ lock.Commit();
+
+ // Let's secretly replace the response document with Folgers Crystals,
+ // er, StreamTap, so that we can mirror the data to our cache.
+ StreamInterface* output = response_.document.release();
+ if (!output) {
+ output = new NullStream;
+ }
+ StreamTap* tap = new StreamTap(output, stream.release());
+ response_.document.reset(tap);
+ return true;
+}
+
+HttpError HttpClient::WriteCacheHeaders(const std::string& id) {
+ scoped_ptr<StreamInterface> stream(cache_->WriteResource(id, kCacheHeader));
+ if (!stream.get()) {
+ LOG_F(LS_ERROR) << "Couldn't open header cache";
+ return HE_CACHE;
+ }
+
+ // Write all unknown and end-to-end headers to a cache file
+ for (HttpData::const_iterator it = response_.begin();
+ it != response_.end(); ++it) {
+ HttpHeader header;
+ if (FromString(header, it->first) && !HttpHeaderIsEndToEnd(header))
+ continue;
+ std::string formatted_header(it->first);
+ formatted_header.append(": ");
+ formatted_header.append(it->second);
+ formatted_header.append("\r\n");
+ StreamResult result = stream->WriteAll(formatted_header.data(),
+ formatted_header.length(),
+ NULL, NULL);
+ if (SR_SUCCESS != result) {
+ LOG_F(LS_ERROR) << "Couldn't write header cache";
+ return HE_CACHE;
+ }
+ }
+
+ return HE_NONE;
+}
+
+void HttpClient::CompleteCacheFile() {
+ // Restore previous response document
+ StreamTap* tap = static_cast<StreamTap*>(response_.document.release());
+ response_.document.reset(tap->Detach());
+
+ int error;
+ StreamResult result = tap->GetTapResult(&error);
+
+ // Delete the tap and cache stream (which completes cache unlock)
+ delete tap;
+
+ if (SR_SUCCESS != result) {
+ LOG(LS_ERROR) << "Cache file error: " << error;
+ cache_->DeleteResource(GetCacheID(server_, request_));
+ }
+}
+
+bool HttpClient::CheckCache() {
+ ASSERT(NULL != cache_);
+ ASSERT(CS_READY == cache_state_);
+
+ std::string id = GetCacheID(server_, request_);
+ if (!cache_->HasResource(id)) {
+ // No cache file available
+ return false;
+ }
+
+ HttpError error = ReadCacheHeaders(id, true);
+
+ if (HE_NONE == error) {
+ switch (HttpGetCacheState(request_, response_)) {
+ case HCS_FRESH:
+ // Cache content is good, read from cache
+ break;
+ case HCS_STALE:
+ // Cache content may be acceptable. Issue a validation request.
+ if (PrepareValidate()) {
+ return false;
+ }
+ // Couldn't validate, fall through.
+ case HCS_NONE:
+ // Cache content is not useable. Issue a regular request.
+ response_.clear(false);
+ return false;
+ }
+ }
+
+ if (HE_NONE == error) {
+ error = ReadCacheBody(id);
+ cache_state_ = CS_READY;
+ }
+
+ if (HE_CACHE == error) {
+ LOG_F(LS_WARNING) << "Cache failure, continuing with normal request";
+ response_.clear(false);
+ return false;
+ }
+
+ SignalHttpClientComplete(this, error);
+ return true;
+}
+
+HttpError HttpClient::ReadCacheHeaders(const std::string& id, bool override) {
+ scoped_ptr<StreamInterface> stream(cache_->ReadResource(id, kCacheHeader));
+ if (!stream.get()) {
+ return HE_CACHE;
+ }
+
+ HttpData::HeaderCombine combine =
+ override ? HttpData::HC_REPLACE : HttpData::HC_AUTO;
+
+ while (true) {
+ std::string formatted_header;
+ StreamResult result = stream->ReadLine(&formatted_header);
+ if (SR_EOS == result)
+ break;
+
+ if (SR_SUCCESS != result) {
+ LOG_F(LS_ERROR) << "ReadLine error in cache headers";
+ return HE_CACHE;
+ }
+ size_t end_of_name = formatted_header.find(':');
+ if (std::string::npos == end_of_name) {
+ LOG_F(LS_WARNING) << "Malformed cache header";
+ continue;
+ }
+ size_t start_of_value = end_of_name + 1;
+ size_t end_of_value = formatted_header.length();
+ while ((start_of_value < end_of_value)
+ && isspace(formatted_header[start_of_value]))
+ ++start_of_value;
+ while ((start_of_value < end_of_value)
+ && isspace(formatted_header[end_of_value-1]))
+ --end_of_value;
+ size_t value_length = end_of_value - start_of_value;
+
+ std::string name(formatted_header.substr(0, end_of_name));
+ std::string value(formatted_header.substr(start_of_value, value_length));
+ response_.changeHeader(name, value, combine);
+ }
+
+ response_.scode = HC_OK;
+ return HE_NONE;
+}
+
+HttpError HttpClient::ReadCacheBody(const std::string& id) {
+ cache_state_ = CS_READING;
+
+ HttpError error = HE_NONE;
+
+ size_t data_size;
+ scoped_ptr<StreamInterface> stream(cache_->ReadResource(id, kCacheBody));
+ if (!stream.get() || !stream->GetSize(&data_size)) {
+ LOG_F(LS_ERROR) << "Unavailable cache body";
+ error = HE_CACHE;
+ } else {
+ error = OnHeaderAvailable(false, false, data_size);
+ }
+
+ if ((HE_NONE == error)
+ && (HV_HEAD != request_.verb)
+ && (NULL != response_.document.get())) {
+ char buffer[1024 * 64];
+ StreamResult result = Flow(stream.get(), buffer, ARRAY_SIZE(buffer),
+ response_.document.get());
+ if (SR_SUCCESS != result) {
+ error = HE_STREAM;
+ }
+ }
+
+ return error;
+}
+
+bool HttpClient::PrepareValidate() {
+ ASSERT(CS_READY == cache_state_);
+ // At this point, request_ contains the pending request, and response_
+ // contains the cached response headers. Reformat the request to validate
+ // the cached content.
+ HttpValidatorStrength vs_required = HttpRequestValidatorLevel(request_);
+ HttpValidatorStrength vs_available = HttpResponseValidatorLevel(response_);
+ if (vs_available < vs_required) {
+ return false;
+ }
+ std::string value;
+ if (response_.hasHeader(HH_ETAG, &value)) {
+ request_.addHeader(HH_IF_NONE_MATCH, value);
+ }
+ if (response_.hasHeader(HH_LAST_MODIFIED, &value)) {
+ request_.addHeader(HH_IF_MODIFIED_SINCE, value);
+ }
+ response_.clear(false);
+ cache_state_ = CS_VALIDATING;
+ return true;
+}
+
+HttpError HttpClient::CompleteValidate() {
+ ASSERT(CS_VALIDATING == cache_state_);
+
+ std::string id = GetCacheID(server_, request_);
+
+ // Merge cached headers with new headers
+ HttpError error = ReadCacheHeaders(id, false);
+ if (HE_NONE != error) {
+ // Rewrite merged headers to cache
+ CacheLock lock(cache_, id);
+ error = WriteCacheHeaders(id);
+ }
+ if (HE_NONE != error) {
+ error = ReadCacheBody(id);
+ }
+ return error;
+}
+
+HttpError HttpClient::OnHeaderAvailable(bool ignore_data, bool chunked,
+ size_t data_size) {
+ if (!ignore_data && !chunked && response_.document.get()) {
+ // Attempt to pre-allocate space for the downloaded data.
+ if (!response_.document->ReserveSize(data_size)) {
+ return HE_OVERFLOW;
+ }
+ }
+ SignalHeaderAvailable(this, chunked, data_size);
+ return HE_NONE;
+}
+
+//
+// HttpBase Implementation
+//
+
+HttpError HttpClient::onHttpHeaderComplete(bool chunked, size_t& data_size) {
+ if (CS_VALIDATING == cache_state_) {
+ if (HC_NOT_MODIFIED == response_.scode) {
+ return CompleteValidate();
+ }
+ // Should we remove conditional headers from request?
+ cache_state_ = CS_READY;
+ cache_->DeleteResource(GetCacheID(server_, request_));
+ // Continue processing response as normal
+ }
+
+ ASSERT(!IsCacheActive());
+ if ((request_.verb == HV_HEAD) || !HttpCodeHasBody(response_.scode)) {
+ // HEAD requests and certain response codes contain no body
+ data_size = 0;
+ }
+ if ((HttpCodeIsRedirection(response_.scode) && !fail_redirect_)
+ || ((HC_PROXY_AUTHENTICATION_REQUIRED == response_.scode)
+ && (PROXY_HTTPS == proxy_.type))) {
+ // We're going to issue another request, so ignore the incoming data.
+ base_.set_ignore_data(true);
+ }
+
+ HttpError error = OnHeaderAvailable(base_.ignore_data(), chunked, data_size);
+ if (HE_NONE != error) {
+ return error;
+ }
+
+ if ((NULL != cache_)
+ && !base_.ignore_data()
+ && HttpShouldCache(request_, response_)) {
+ if (BeginCacheFile()) {
+ cache_state_ = CS_WRITING;
+ }
+ }
+ return HE_NONE;
+}
+
+void HttpClient::onHttpComplete(HttpMode mode, HttpError err) {
+ if (err != HE_NONE) {
+ // fall through
+ } else if (mode == HM_CONNECT) {
+ base_.send(&request_);
+ return;
+ } else if ((mode == HM_SEND) || HttpCodeIsInformational(response_.scode)) {
+ // If you're interested in informational headers, catch
+ // SignalHeaderAvailable.
+ base_.recv(&response_);
+ return;
+ } else {
+ if (!HttpShouldKeepAlive(response_)) {
+ LOG(INFO) << "HttpClient: closing socket";
+ base_.stream()->Close();
+ }
+ if (HttpCodeIsRedirection(response_.scode) && !fail_redirect_) {
+ std::string value;
+ if (!response_.hasHeader(HH_LOCATION, &value)) {
+ err = HE_PROTOCOL;
+ } else {
+ Url<char> purl(value);
+ set_server(SocketAddress(purl.server(), purl.port(), false));
+ request_.path = purl.full_path();
+ if (response_.scode == HC_SEE_OTHER) {
+ request_.verb = HV_GET;
+ request_.clearHeader(HH_CONTENT_TYPE);
+ request_.clearHeader(HH_CONTENT_LENGTH);
+ request_.document.reset();
+ } else if (request_.document.get() && !request_.document->Rewind()) {
+ // Unable to replay the request document.
+ err = HE_STREAM;
+ }
+ }
+ if (err == HE_NONE) {
+ context_.reset();
+ response_.clear(false);
+ release();
+ start();
+ return;
+ }
+ } else if ((HC_PROXY_AUTHENTICATION_REQUIRED == response_.scode)
+ && (PROXY_HTTPS == proxy_.type)) {
+ std::string response, auth_method;
+ HttpData::const_iterator begin = response_.begin(HH_PROXY_AUTHENTICATE);
+ HttpData::const_iterator end = response_.end(HH_PROXY_AUTHENTICATE);
+ for (HttpData::const_iterator it = begin; it != end; ++it) {
+ HttpAuthResult res = HttpAuthenticate(
+ it->second.data(), it->second.size(),
+ proxy_.address,
+ ToString(request_.verb), request_.path,
+ proxy_.username, proxy_.password,
+ *context_.use(), response, auth_method);
+ if (res == HAR_RESPONSE) {
+ request_.setHeader(HH_PROXY_AUTHORIZATION, response);
+ if (request_.document.get() && !request_.document->Rewind()) {
+ err = HE_STREAM;
+ } else {
+ // Explicitly do not reset the HttpAuthContext
+ response_.clear(false);
+ // TODO: Reuse socket when authenticating?
+ release();
+ start();
+ return;
+ }
+ } else if (res == HAR_IGNORE) {
+ LOG(INFO) << "Ignoring Proxy-Authenticate: " << auth_method;
+ continue;
+ } else {
+ break;
+ }
+ }
+ }
+ }
+ if (CS_WRITING == cache_state_) {
+ CompleteCacheFile();
+ cache_state_ = CS_READY;
+ } else if (CS_READING == cache_state_) {
+ cache_state_ = CS_READY;
+ }
+ release();
+ SignalHttpClientComplete(this, err);
+}
+
+void HttpClient::onHttpClosed(HttpError err) {
+ SignalHttpClientClosed(this, err);
+}
+
+//////////////////////////////////////////////////////////////////////
+
+} // namespace talk_base
diff --git a/Plugins/jingle/libjingle/talk/base/httpclient.h b/Plugins/jingle/libjingle/talk/base/httpclient.h
new file mode 100644
index 0000000..789cb59
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/httpclient.h
@@ -0,0 +1,155 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TALK_BASE_HTTPCLIENT_H__
+#define TALK_BASE_HTTPCLIENT_H__
+
+#include "talk/base/common.h"
+#include "talk/base/httpbase.h"
+#include "talk/base/proxyinfo.h"
+#include "talk/base/scoped_ptr.h"
+#include "talk/base/sigslot.h"
+#include "talk/base/socketaddress.h"
+#include "talk/base/socketpool.h"
+
+namespace talk_base {
+
+//////////////////////////////////////////////////////////////////////
+// HttpClient
+//////////////////////////////////////////////////////////////////////
+
+class DiskCache;
+class HttpClient;
+class IPNetPool;
+
+class HttpClient : private IHttpNotify {
+public:
+ HttpClient(const std::string& agent, StreamPool* pool);
+ virtual ~HttpClient();
+
+ void set_pool(StreamPool* pool) { pool_ = pool; }
+
+ const std::string& agent() const { return agent_; }
+
+ void set_proxy(const ProxyInfo& proxy) { proxy_ = proxy; }
+ const ProxyInfo& proxy() const { return proxy_; }
+
+ void set_fail_redirect(bool fail_redirect) { fail_redirect_ = fail_redirect; }
+ bool fail_redirect() const { return fail_redirect_; }
+
+ void use_absolute_uri(bool absolute_uri) { absolute_uri_ = absolute_uri; }
+ bool absolute_uri() const { return absolute_uri_; }
+
+ void set_cache(DiskCache* cache) { ASSERT(!IsCacheActive()); cache_ = cache; }
+ bool cache_enabled() const { return (NULL != cache_); }
+
+ // reset clears the server, request, and response structures. It will also
+ // abort an active request.
+ void reset();
+
+ void set_server(const SocketAddress& address);
+ const SocketAddress& server() const { return server_; }
+
+ HttpRequestData& request() { return request_; }
+ const HttpRequestData& request() const { return request_; }
+ HttpResponseData& response() { return response_; }
+ const HttpResponseData& response() const { return response_; }
+
+ // convenience methods
+ void prepare_get(const std::string& url);
+ void prepare_post(const std::string& url, const std::string& content_type,
+ StreamInterface* request_doc);
+
+ // After you finish setting up your request, call start.
+ void start();
+
+ // Signalled when the header has finished downloading, before the document
+ // content is processed. This notification is for informational purposes
+ // only. Do not modify the client in response to this.
+ sigslot::signal3<const HttpClient*,bool,size_t> SignalHeaderAvailable;
+ // Signalled when the current 'call' finishes. On success, err is 0.
+ sigslot::signal2<HttpClient*,int> SignalHttpClientComplete;
+ // Signalled when the network connection goes down while a call is not
+ // in progress.
+ sigslot::signal2<HttpClient*,int> SignalHttpClientClosed;
+
+protected:
+ void release();
+
+ bool BeginCacheFile();
+ HttpError WriteCacheHeaders(const std::string& id);
+ void CompleteCacheFile();
+
+ bool CheckCache();
+ HttpError ReadCacheHeaders(const std::string& id, bool override);
+ HttpError ReadCacheBody(const std::string& id);
+
+ bool PrepareValidate();
+ HttpError CompleteValidate();
+
+ HttpError OnHeaderAvailable(bool ignore_data, bool chunked, size_t data_size);
+
+ // IHttpNotify Interface
+ virtual HttpError onHttpHeaderComplete(bool chunked, size_t& data_size);
+ virtual void onHttpComplete(HttpMode mode, HttpError err);
+ virtual void onHttpClosed(HttpError err);
+
+private:
+ enum CacheState { CS_READY, CS_WRITING, CS_READING, CS_VALIDATING };
+ bool IsCacheActive() const { return (cache_state_ > CS_READY); }
+
+ std::string agent_;
+ StreamPool* pool_;
+ HttpBase base_;
+ SocketAddress server_;
+ ProxyInfo proxy_;
+ HttpRequestData request_;
+ HttpResponseData response_;
+ bool fail_redirect_, absolute_uri_;
+ scoped_ptr<HttpAuthContext> context_;
+ DiskCache* cache_;
+ CacheState cache_state_;
+};
+
+//////////////////////////////////////////////////////////////////////
+// Default implementation of HttpClient
+//////////////////////////////////////////////////////////////////////
+
+class HttpClientDefault : public ReuseSocketPool, public HttpClient {
+public:
+ HttpClientDefault(SocketFactory* factory, const std::string& agent)
+ : ReuseSocketPool(factory), HttpClient(agent, NULL)
+ {
+ set_pool(this);
+ }
+};
+
+//////////////////////////////////////////////////////////////////////
+
+} // namespace talk_base
+
+#endif // TALK_BASE_HTTPCLIENT_H__
diff --git a/Plugins/jingle/libjingle/talk/base/httpcommon-inl.h b/Plugins/jingle/libjingle/talk/base/httpcommon-inl.h
new file mode 100644
index 0000000..5235b89
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/httpcommon-inl.h
@@ -0,0 +1,114 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TALK_BASE_HTTPCOMMON_INL_H__
+#define TALK_BASE_HTTPCOMMON_INL_H__
+
+#include "talk/base/common.h"
+#include "talk/base/httpcommon.h"
+
+namespace talk_base {
+
+///////////////////////////////////////////////////////////////////////////////
+// Url
+///////////////////////////////////////////////////////////////////////////////
+
+template<class CTYPE>
+Url<CTYPE>::Url(const string& url) {
+ const CTYPE* raw_url = url.c_str();
+ if (ascnicmp(raw_url, "http://", 7) == 0) {
+ raw_url += 7;
+ m_secure = false;
+ } else if (ascnicmp(raw_url, "https://", 8) == 0) {
+ raw_url += 8;
+ m_secure = true;
+ } else {
+ return;
+ }
+ m_port = UrlDefaultPort(m_secure);
+ const CTYPE* colon = ::strchr(raw_url, static_cast<CTYPE>(':'));
+ const CTYPE* slash = ::strchr(raw_url, static_cast<CTYPE>('/'));
+ if (!colon && !slash) {
+ m_server = url;
+ // TODO: rethink this slash
+ m_path.append(1, static_cast<CTYPE>('/'));
+ } else {
+ const CTYPE* ptr;
+ if (colon == 0) {
+ ptr = slash;
+ } else if (slash == 0) {
+ ptr = colon;
+ } else {
+ ptr = _min(colon, slash);
+ }
+ m_server.assign(raw_url, ptr - raw_url);
+ if (ptr == colon) {
+ CTYPE* tmp = 0;
+ m_port = static_cast<uint16>(::strtoul(ptr + 1, &tmp, 10));
+ ptr = tmp;
+ }
+ const CTYPE* query = ::strchr(ptr, static_cast<CTYPE>('?'));
+ if (!query) {
+ m_path.assign(ptr);
+ } else {
+ m_path.assign(ptr, query - ptr);
+ m_query.assign(query);
+ }
+ }
+ ASSERT(m_path.empty() || (m_path[0] == static_cast<CTYPE>('/')));
+ ASSERT(m_query.empty() || (m_query[0] == static_cast<CTYPE>('?')));
+}
+
+template<class CTYPE>
+typename Traits<CTYPE>::string Url<CTYPE>::full_path() {
+ string full_path(m_path);
+ full_path.append(m_query);
+ return full_path;
+}
+
+template<class CTYPE>
+typename Traits<CTYPE>::string Url<CTYPE>::url() {
+ CTYPE protocol[9];
+ asccpyn(protocol, ARRAY_SIZE(protocol), m_secure ? "https://" : "http://");
+ string url(protocol);
+ url.append(m_server);
+ if (m_port != UrlDefaultPort(m_secure)) {
+ CTYPE format[5], port[32];
+ asccpyn(format, ARRAY_SIZE(format), ":%hu");
+ sprintfn(port, ARRAY_SIZE(port), format, m_port);
+ url.append(port);
+ }
+ url.append(m_path);
+ url.append(m_query);
+ return url;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+} // namespace talk_base
+
+#endif // TALK_BASE_HTTPCOMMON_INL_H__
diff --git a/Plugins/jingle/libjingle/talk/base/httpcommon.cc b/Plugins/jingle/libjingle/talk/base/httpcommon.cc
new file mode 100644
index 0000000..0358121
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/httpcommon.cc
@@ -0,0 +1,940 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <time.h>
+
+#ifdef WIN32
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#define _WINSOCKAPI_
+#include <windows.h>
+#define SECURITY_WIN32
+#include <security.h>
+#endif
+
+#include "talk/base/base64.h"
+#include "talk/base/common.h"
+#include "talk/base/cryptstring.h"
+#include "talk/base/httpcommon.h"
+#include "talk/base/socketaddress.h"
+#include "talk/base/stringdigest.h"
+#include "talk/base/stringutils.h"
+
+namespace talk_base {
+
+#ifdef WIN32
+extern const ConstantLabel SECURITY_ERRORS[];
+#endif
+
+//////////////////////////////////////////////////////////////////////
+// Enum - TODO: expose globally later?
+//////////////////////////////////////////////////////////////////////
+
+bool find_string(size_t& index, const std::string& needle,
+ const char* const haystack[], size_t max_index) {
+ for (index=0; index<max_index; ++index) {
+ if (_stricmp(needle.c_str(), haystack[index]) == 0) {
+ return true;
+ }
+ }
+ return false;
+}
+
+template<class E>
+struct Enum {
+ static const char** Names;
+ static size_t Size;
+
+ static inline const char* Name(E val) { return Names[val]; }
+ static inline bool Parse(E& val, const std::string& name) {
+ size_t index;
+ if (!find_string(index, name, Names, Size))
+ return false;
+ val = static_cast<E>(index);
+ return true;
+ }
+
+ E val;
+
+ inline operator E&() { return val; }
+ inline Enum& operator=(E rhs) { val = rhs; return *this; }
+
+ inline const char* name() const { return Name(val); }
+ inline bool assign(const std::string& name) { return Parse(val, name); }
+ inline Enum& operator=(const std::string& rhs) { assign(rhs); return *this; }
+};
+
+#define ENUM(e,n) \
+ template<> const char** Enum<e>::Names = n; \
+ template<> size_t Enum<e>::Size = sizeof(n)/sizeof(n[0])
+
+//////////////////////////////////////////////////////////////////////
+// HttpCommon
+//////////////////////////////////////////////////////////////////////
+
+static const char* kHttpVersions[HVER_LAST+1] = {
+ "1.0", "1.1"
+};
+ENUM(HttpVersion, kHttpVersions);
+
+static const char* kHttpVerbs[HV_LAST+1] = {
+ "GET", "POST", "PUT", "DELETE", "CONNECT", "HEAD"
+};
+ENUM(HttpVerb, kHttpVerbs);
+
+static const char* kHttpHeaders[HH_LAST+1] = {
+ "Age",
+ "Cache-Control",
+ "Connection",
+ "Content-Length",
+ "Content-Range",
+ "Content-Type",
+ "Cookie",
+ "Date",
+ "ETag",
+ "Expires",
+ "Host",
+ "If-Modified-Since",
+ "If-None-Match",
+ "Keep-Alive",
+ "Last-Modified",
+ "Location",
+ "Proxy-Authenticate",
+ "Proxy-Authorization",
+ "Proxy-Connection",
+ "Range",
+ "Set-Cookie",
+ "TE",
+ "Trailers",
+ "Transfer-Encoding",
+ "Upgrade",
+ "User-Agent",
+ "WWW-Authenticate",
+};
+ENUM(HttpHeader, kHttpHeaders);
+
+const char* ToString(HttpVersion version) {
+ return Enum<HttpVersion>::Name(version);
+}
+
+bool FromString(HttpVersion& version, const std::string& str) {
+ return Enum<HttpVersion>::Parse(version, str);
+}
+
+const char* ToString(HttpVerb verb) {
+ return Enum<HttpVerb>::Name(verb);
+}
+
+bool FromString(HttpVerb& verb, const std::string& str) {
+ return Enum<HttpVerb>::Parse(verb, str);
+}
+
+const char* ToString(HttpHeader header) {
+ return Enum<HttpHeader>::Name(header);
+}
+
+bool FromString(HttpHeader& header, const std::string& str) {
+ return Enum<HttpHeader>::Parse(header, str);
+}
+
+bool HttpCodeHasBody(uint32 code) {
+ return !HttpCodeIsInformational(code)
+ && (code != HC_NO_CONTENT) || (code != HC_NOT_MODIFIED);
+}
+
+bool HttpCodeIsCacheable(uint32 code) {
+ switch (code) {
+ case HC_OK:
+ case HC_NON_AUTHORITATIVE:
+ case HC_PARTIAL_CONTENT:
+ case HC_MULTIPLE_CHOICES:
+ case HC_MOVED_PERMANENTLY:
+ case HC_GONE:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool HttpHeaderIsEndToEnd(HttpHeader header) {
+ switch (header) {
+ case HH_CONNECTION:
+ case HH_KEEP_ALIVE:
+ case HH_PROXY_AUTHENTICATE:
+ case HH_PROXY_AUTHORIZATION:
+ //case HH_PROXY_CONNECTION:??
+ case HH_TE:
+ case HH_TRAILERS:
+ case HH_TRANSFER_ENCODING:
+ case HH_UPGRADE:
+ return false;
+ default:
+ return true;
+ }
+}
+
+bool HttpHeaderIsCollapsible(HttpHeader header) {
+ switch (header) {
+ case HH_SET_COOKIE:
+ case HH_PROXY_AUTHENTICATE:
+ case HH_WWW_AUTHENTICATE:
+ return false;
+ default:
+ return true;
+ }
+}
+
+bool HttpShouldKeepAlive(const HttpData& data) {
+ std::string connection;
+ if ((data.hasHeader(HH_PROXY_CONNECTION, &connection)
+ || data.hasHeader(HH_CONNECTION, &connection))) {
+ return (_stricmp(connection.c_str(), "Keep-Alive") == 0);
+ }
+ return (data.version >= HVER_1_1);
+}
+
+namespace {
+
+inline bool IsEndOfAttributeName(size_t pos, size_t len, const char * data) {
+ if (pos >= len)
+ return true;
+ if (isspace(static_cast<unsigned char>(data[pos])))
+ return true;
+ // The reason for this complexity is that some attributes may contain trailing
+ // equal signs (like base64 tokens in Negotiate auth headers)
+ if ((pos+1 < len) && (data[pos] == '=') &&
+ !isspace(static_cast<unsigned char>(data[pos+1])) &&
+ (data[pos+1] != '=')) {
+ return true;
+ }
+ return false;
+}
+
+} // anonymous namespace
+
+void HttpParseAttributes(const char * data, size_t len,
+ HttpAttributeList& attributes) {
+ size_t pos = 0;
+ while (true) {
+ // Skip leading whitespace
+ while ((pos < len) && isspace(static_cast<unsigned char>(data[pos]))) {
+ ++pos;
+ }
+
+ // End of attributes?
+ if (pos >= len)
+ return;
+
+ // Find end of attribute name
+ size_t start = pos;
+ while (!IsEndOfAttributeName(pos, len, data)) {
+ ++pos;
+ }
+
+ HttpAttribute attribute;
+ attribute.first.assign(data + start, data + pos);
+
+ // Attribute has value?
+ if ((pos < len) && (data[pos] == '=')) {
+ ++pos; // Skip '='
+ // Check if quoted value
+ if ((pos < len) && (data[pos] == '"')) {
+ while (++pos < len) {
+ if (data[pos] == '"') {
+ ++pos;
+ break;
+ }
+ if ((data[pos] == '\\') && (pos + 1 < len))
+ ++pos;
+ attribute.second.append(1, data[pos]);
+ }
+ } else {
+ while ((pos < len) &&
+ !isspace(static_cast<unsigned char>(data[pos])) &&
+ (data[pos] != ',')) {
+ attribute.second.append(1, data[pos++]);
+ }
+ }
+ }
+
+ attributes.push_back(attribute);
+ if ((pos < len) && (data[pos] == ',')) ++pos; // Skip ','
+ }
+}
+
+bool HttpHasAttribute(const HttpAttributeList& attributes,
+ const std::string& name,
+ std::string* value) {
+ for (HttpAttributeList::const_iterator it = attributes.begin();
+ it != attributes.end(); ++it) {
+ if (it->first == name) {
+ if (value) {
+ *value = it->second;
+ }
+ return true;
+ }
+ }
+ return false;
+}
+
+bool HttpHasNthAttribute(HttpAttributeList& attributes,
+ size_t index,
+ std::string* name,
+ std::string* value) {
+ if (index >= attributes.size())
+ return false;
+
+ if (name)
+ *name = attributes[index].first;
+ if (value)
+ *value = attributes[index].second;
+ return true;
+}
+
+bool HttpDateToSeconds(const std::string& date, unsigned long* seconds) {
+ const char* const kTimeZones[] = {
+ "UT", "GMT", "EST", "EDT", "CST", "CDT", "MST", "MDT", "PST", "PDT",
+ "A", "B", "C", "D", "E", "F", "G", "H", "I", "K", "L", "M",
+ "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y"
+ };
+ const int kTimeZoneOffsets[] = {
+ 0, 0, -5, -4, -6, -5, -7, -6, -8, -7,
+ -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12,
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12
+ };
+
+ ASSERT(NULL != seconds);
+ struct tm tval;
+ memset(&tval, 0, sizeof(tval));
+ char month[4], zone[6];
+ memset(month, 0, sizeof(month));
+ memset(zone, 0, sizeof(zone));
+
+ if (7 != sscanf(date.c_str(), "%*3s, %d %3s %d %d:%d:%d %5c",
+ &tval.tm_mday, month, &tval.tm_year,
+ &tval.tm_hour, &tval.tm_min, &tval.tm_sec, &zone)) {
+ return false;
+ }
+ switch (toupper(month[2])) {
+ case 'N': tval.tm_mon = (month[1] == 'A') ? 0 : 5; break;
+ case 'B': tval.tm_mon = 1; break;
+ case 'R': tval.tm_mon = (month[0] == 'M') ? 2 : 3; break;
+ case 'Y': tval.tm_mon = 4; break;
+ case 'L': tval.tm_mon = 6; break;
+ case 'G': tval.tm_mon = 7; break;
+ case 'P': tval.tm_mon = 8; break;
+ case 'T': tval.tm_mon = 9; break;
+ case 'V': tval.tm_mon = 10; break;
+ case 'C': tval.tm_mon = 11; break;
+ }
+ tval.tm_year -= 1900;
+ unsigned long gmt, non_gmt = mktime(&tval);
+ if ((zone[0] == '+') || (zone[0] == '-')) {
+ if (!isdigit(zone[1]) || !isdigit(zone[2])
+ || !isdigit(zone[3]) || !isdigit(zone[4])) {
+ return false;
+ }
+ int hours = (zone[1] - '0') * 10 + (zone[2] - '0');
+ int minutes = (zone[3] - '0') * 10 + (zone[4] - '0');
+ int offset = (hours * 60 + minutes) * 60;
+ gmt = non_gmt + (zone[0] == '+') ? offset : -offset;
+ } else {
+ size_t zindex;
+ if (!find_string(zindex, zone, kTimeZones, ARRAY_SIZE(kTimeZones))) {
+ return false;
+ }
+ gmt = non_gmt + kTimeZoneOffsets[zindex] * 60 * 60;
+ }
+#ifdef OSX
+ tm *tm_for_timezone = localtime((time_t *)&gmt);
+ *seconds = gmt + tm_for_timezone->tm_gmtoff;
+#else
+ *seconds = gmt - timezone;
+#endif
+ return true;
+}
+
+//////////////////////////////////////////////////////////////////////
+// HttpData
+//////////////////////////////////////////////////////////////////////
+
+void
+HttpData::clear(bool release_document) {
+ if (release_document) {
+ document.reset();
+ }
+ m_headers.clear();
+}
+
+void
+HttpData::changeHeader(const std::string& name, const std::string& value,
+ HeaderCombine combine) {
+ if (combine == HC_AUTO) {
+ HttpHeader header;
+ // Unrecognized headers are collapsible
+ combine = !FromString(header, name) || HttpHeaderIsCollapsible(header)
+ ? HC_YES : HC_NO;
+ } else if (combine == HC_REPLACE) {
+ m_headers.erase(name);
+ combine = HC_NO;
+ }
+ // At this point, combine is one of (YES, NO, NEW)
+ if (combine != HC_NO) {
+ HeaderMap::iterator it = m_headers.find(name);
+ if (it != m_headers.end()) {
+ if (combine == HC_YES) {
+ it->second.append(",");
+ it->second.append(value);
+ }
+ return;
+ }
+ }
+ m_headers.insert(HeaderMap::value_type(name, value));
+}
+
+void
+HttpData::clearHeader(const std::string& name) {
+ m_headers.erase(name);
+}
+
+bool
+HttpData::hasHeader(const std::string& name, std::string* value) const {
+ HeaderMap::const_iterator it = m_headers.find(name);
+ if (it == m_headers.end()) {
+ return false;
+ } else if (value) {
+ *value = it->second;
+ }
+ return true;
+}
+
+void
+HttpData::setContent(const std::string& content_type,
+ StreamInterface* document) {
+ ASSERT(document != NULL);
+ this->document.reset(document);
+ setHeader(HH_CONTENT_TYPE, content_type);
+ size_t content_length = 0;
+ if (this->document->GetSize(&content_length)) {
+ char buffer[32];
+ sprintfn(buffer, sizeof(buffer), "%d", content_length);
+ setHeader(HH_CONTENT_LENGTH, buffer);
+ } else {
+ setHeader(HH_TRANSFER_ENCODING, "chunked");
+ }
+}
+
+//
+// HttpRequestData
+//
+
+void
+HttpRequestData::clear(bool release_document) {
+ HttpData::clear(release_document);
+ verb = HV_GET;
+ path.clear();
+}
+
+size_t
+HttpRequestData::formatLeader(char* buffer, size_t size) {
+ ASSERT(path.find(' ') == std::string::npos);
+ return sprintfn(buffer, size, "%s %.*s HTTP/%s", ToString(verb), path.size(),
+ path.data(), ToString(version));
+}
+
+HttpError
+HttpRequestData::parseLeader(const char* line, size_t len) {
+ UNUSED(len);
+ uint32 vmajor, vminor;
+ int vend, dstart, dend;
+ if ((sscanf(line, "%*s%n %n%*s%n HTTP/%lu.%lu", &vend, &dstart, &dend,
+ &vmajor, &vminor) != 2)
+ || (vmajor != 1)) {
+ return HE_PROTOCOL;
+ }
+ if (vminor == 0) {
+ version = HVER_1_0;
+ } else if (vminor == 1) {
+ version = HVER_1_1;
+ } else {
+ return HE_PROTOCOL;
+ }
+ std::string sverb(line, vend);
+ if (!FromString(verb, sverb.c_str())) {
+ return HE_PROTOCOL; // !?! HC_METHOD_NOT_SUPPORTED?
+ }
+ path.assign(line + dstart, line + dend);
+ return HE_NONE;
+}
+
+//
+// HttpResponseData
+//
+
+void
+HttpResponseData::clear(bool release_document) {
+ HttpData::clear(release_document);
+ scode = HC_INTERNAL_SERVER_ERROR;
+ message.clear();
+}
+
+void
+HttpResponseData::set_success(uint32 scode) {
+ this->scode = scode;
+ message.clear();
+ setHeader(HH_CONTENT_LENGTH, "0");
+}
+
+void
+HttpResponseData::set_success(const std::string& content_type,
+ StreamInterface* document,
+ uint32 scode) {
+ this->scode = scode;
+ message.erase(message.begin(), message.end());
+ setContent(content_type, document);
+}
+
+void
+HttpResponseData::set_redirect(const std::string& location, uint32 scode) {
+ this->scode = scode;
+ message.clear();
+ setHeader(HH_LOCATION, location);
+ setHeader(HH_CONTENT_LENGTH, "0");
+}
+
+void
+HttpResponseData::set_error(uint32 scode) {
+ this->scode = scode;
+ message.clear();
+ setHeader(HH_CONTENT_LENGTH, "0");
+}
+
+size_t
+HttpResponseData::formatLeader(char* buffer, size_t size) {
+ size_t len = sprintfn(buffer, size, "HTTP/%s %lu", ToString(version), scode);
+ if (!message.empty()) {
+ len += sprintfn(buffer + len, size - len, " %.*s",
+ message.size(), message.data());
+ }
+ return len;
+}
+
+HttpError
+HttpResponseData::parseLeader(const char* line, size_t len) {
+ size_t pos = 0;
+ uint32 vmajor, vminor;
+ if ((sscanf(line, "HTTP/%lu.%lu %lu%n", &vmajor, &vminor, &scode, &pos) != 3)
+ || (vmajor != 1)) {
+ return HE_PROTOCOL;
+ }
+ if (vminor == 0) {
+ version = HVER_1_0;
+ } else if (vminor == 1) {
+ version = HVER_1_1;
+ } else {
+ return HE_PROTOCOL;
+ }
+ while ((pos < len) && isspace(static_cast<unsigned char>(line[pos]))) ++pos;
+ message.assign(line + pos, len - pos);
+ return HE_NONE;
+}
+
+//////////////////////////////////////////////////////////////////////
+// Http Authentication
+//////////////////////////////////////////////////////////////////////
+
+#define TEST_DIGEST 0
+#if TEST_DIGEST
+/*
+const char * const DIGEST_CHALLENGE =
+ "Digest realm=\"testrealm@host.com\","
+ " qop=\"auth,auth-int\","
+ " nonce=\"dcd98b7102dd2f0e8b11d0f600bfb0c093\","
+ " opaque=\"5ccc069c403ebaf9f0171e9517f40e41\"";
+const char * const DIGEST_METHOD = "GET";
+const char * const DIGEST_URI =
+ "/dir/index.html";;
+const char * const DIGEST_CNONCE =
+ "0a4f113b";
+const char * const DIGEST_RESPONSE =
+ "6629fae49393a05397450978507c4ef1";
+//user_ = "Mufasa";
+//pass_ = "Circle Of Life";
+*/
+const char * const DIGEST_CHALLENGE =
+ "Digest realm=\"Squid proxy-caching web server\","
+ " nonce=\"Nny4QuC5PwiSDixJ\","
+ " qop=\"auth\","
+ " stale=false";
+const char * const DIGEST_URI =
+ "/";
+const char * const DIGEST_CNONCE =
+ "6501d58e9a21cee1e7b5fec894ded024";
+const char * const DIGEST_RESPONSE =
+ "edffcb0829e755838b073a4a42de06bc";
+#endif
+
+static std::string quote(const std::string& str) {
+ std::string result;
+ result.push_back('"');
+ for (size_t i=0; i<str.size(); ++i) {
+ if ((str[i] == '"') || (str[i] == '\\'))
+ result.push_back('\\');
+ result.push_back(str[i]);
+ }
+ result.push_back('"');
+ return result;
+}
+
+#ifdef WIN32
+struct NegotiateAuthContext : public HttpAuthContext {
+ CredHandle cred;
+ CtxtHandle ctx;
+ size_t steps;
+ bool specified_credentials;
+
+ NegotiateAuthContext(const std::string& auth, CredHandle c1, CtxtHandle c2)
+ : HttpAuthContext(auth), cred(c1), ctx(c2), steps(0),
+ specified_credentials(false)
+ { }
+
+ virtual ~NegotiateAuthContext() {
+ DeleteSecurityContext(&ctx);
+ FreeCredentialsHandle(&cred);
+ }
+};
+#endif // WIN32
+
+HttpAuthResult HttpAuthenticate(
+ const char * challenge, size_t len,
+ const SocketAddress& server,
+ const std::string& method, const std::string& uri,
+ const std::string& username, const CryptString& password,
+ HttpAuthContext *& context, std::string& response, std::string& auth_method)
+{
+#if TEST_DIGEST
+ challenge = DIGEST_CHALLENGE;
+ len = strlen(challenge);
+#endif
+
+ HttpAttributeList args;
+ HttpParseAttributes(challenge, len, args);
+ HttpHasNthAttribute(args, 0, &auth_method, NULL);
+
+ if (context && (context->auth_method != auth_method))
+ return HAR_IGNORE;
+
+ // BASIC
+ if (_stricmp(auth_method.c_str(), "basic") == 0) {
+ if (context)
+ return HAR_CREDENTIALS; // Bad credentials
+ if (username.empty())
+ return HAR_CREDENTIALS; // Missing credentials
+
+ context = new HttpAuthContext(auth_method);
+
+ // TODO: convert sensitive to a secure buffer that gets securely deleted
+ //std::string decoded = username + ":" + password;
+ size_t len = username.size() + password.GetLength() + 2;
+ char * sensitive = new char[len];
+ size_t pos = strcpyn(sensitive, len, username.data(), username.size());
+ pos += strcpyn(sensitive + pos, len - pos, ":");
+ password.CopyTo(sensitive + pos, true);
+
+ response = auth_method;
+ response.append(" ");
+ // TODO: create a sensitive-source version of Base64::encode
+ response.append(Base64::encode(sensitive));
+ memset(sensitive, 0, len);
+ delete [] sensitive;
+ return HAR_RESPONSE;
+ }
+
+ // DIGEST
+ if (_stricmp(auth_method.c_str(), "digest") == 0) {
+ if (context)
+ return HAR_CREDENTIALS; // Bad credentials
+ if (username.empty())
+ return HAR_CREDENTIALS; // Missing credentials
+
+ context = new HttpAuthContext(auth_method);
+
+ std::string cnonce, ncount;
+#if TEST_DIGEST
+ method = DIGEST_METHOD;
+ uri = DIGEST_URI;
+ cnonce = DIGEST_CNONCE;
+#else
+ char buffer[256];
+ sprintf(buffer, "%d", time(0));
+ cnonce = MD5(buffer);
+#endif
+ ncount = "00000001";
+
+ std::string realm, nonce, qop, opaque;
+ HttpHasAttribute(args, "realm", &realm);
+ HttpHasAttribute(args, "nonce", &nonce);
+ bool has_qop = HttpHasAttribute(args, "qop", &qop);
+ bool has_opaque = HttpHasAttribute(args, "opaque", &opaque);
+
+ // TODO: convert sensitive to be secure buffer
+ //std::string A1 = username + ":" + realm + ":" + password;
+ size_t len = username.size() + realm.size() + password.GetLength() + 3;
+ char * sensitive = new char[len]; // A1
+ size_t pos = strcpyn(sensitive, len, username.data(), username.size());
+ pos += strcpyn(sensitive + pos, len - pos, ":");
+ pos += strcpyn(sensitive + pos, len - pos, realm.c_str());
+ pos += strcpyn(sensitive + pos, len - pos, ":");
+ password.CopyTo(sensitive + pos, true);
+
+ std::string A2 = method + ":" + uri;
+ std::string middle;
+ if (has_qop) {
+ qop = "auth";
+ middle = nonce + ":" + ncount + ":" + cnonce + ":" + qop;
+ } else {
+ middle = nonce;
+ }
+ std::string HA1 = MD5(sensitive);
+ memset(sensitive, 0, len);
+ delete [] sensitive;
+ std::string HA2 = MD5(A2);
+ std::string dig_response = MD5(HA1 + ":" + middle + ":" + HA2);
+
+#if TEST_DIGEST
+ assert(strcmp(dig_response.c_str(), DIGEST_RESPONSE) == 0);
+#endif
+
+ std::stringstream ss;
+ ss << auth_method;
+ ss << " username=" << quote(username);
+ ss << ", realm=" << quote(realm);
+ ss << ", nonce=" << quote(nonce);
+ ss << ", uri=" << quote(uri);
+ if (has_qop) {
+ ss << ", qop=" << qop;
+ ss << ", nc=" << ncount;
+ ss << ", cnonce=" << quote(cnonce);
+ }
+ ss << ", response=\"" << dig_response << "\"";
+ if (has_opaque) {
+ ss << ", opaque=" << quote(opaque);
+ }
+ response = ss.str();
+ return HAR_RESPONSE;
+ }
+
+#ifdef WIN32
+#if 1
+ bool want_negotiate = (_stricmp(auth_method.c_str(), "negotiate") == 0);
+ bool want_ntlm = (_stricmp(auth_method.c_str(), "ntlm") == 0);
+ // SPNEGO & NTLM
+ if (want_negotiate || want_ntlm) {
+ const size_t MAX_MESSAGE = 12000, MAX_SPN = 256;
+ char out_buf[MAX_MESSAGE], spn[MAX_SPN];
+
+#if 0 // Requires funky windows versions
+ DWORD len = MAX_SPN;
+ if (DsMakeSpn("HTTP", server.IPAsString().c_str(), NULL, server.port(),
+ 0, &len, spn) != ERROR_SUCCESS) {
+ LOG_F(WARNING) << "(Negotiate) - DsMakeSpn failed";
+ return HAR_IGNORE;
+ }
+#else
+ sprintfn(spn, MAX_SPN, "HTTP/%s", server.ToString().c_str());
+#endif
+
+ SecBuffer out_sec;
+ out_sec.pvBuffer = out_buf;
+ out_sec.cbBuffer = sizeof(out_buf);
+ out_sec.BufferType = SECBUFFER_TOKEN;
+
+ SecBufferDesc out_buf_desc;
+ out_buf_desc.ulVersion = 0;
+ out_buf_desc.cBuffers = 1;
+ out_buf_desc.pBuffers = &out_sec;
+
+ const ULONG NEG_FLAGS_DEFAULT =
+ //ISC_REQ_ALLOCATE_MEMORY
+ ISC_REQ_CONFIDENTIALITY
+ //| ISC_REQ_EXTENDED_ERROR
+ //| ISC_REQ_INTEGRITY
+ | ISC_REQ_REPLAY_DETECT
+ | ISC_REQ_SEQUENCE_DETECT
+ //| ISC_REQ_STREAM
+ //| ISC_REQ_USE_SUPPLIED_CREDS
+ ;
+
+ TimeStamp lifetime;
+ SECURITY_STATUS ret = S_OK;
+ ULONG ret_flags = 0, flags = NEG_FLAGS_DEFAULT;
+
+ bool specify_credentials = !username.empty();
+ size_t steps = 0;
+
+ //uint32 now = Time();
+
+ NegotiateAuthContext * neg = static_cast<NegotiateAuthContext *>(context);
+ if (neg) {
+ const size_t max_steps = 10;
+ if (++neg->steps >= max_steps) {
+ LOG(WARNING) << "AsyncHttpsProxySocket::Authenticate(Negotiate) too many retries";
+ return HAR_ERROR;
+ }
+ steps = neg->steps;
+
+ std::string decoded_challenge;
+ HttpHasNthAttribute(args, 1, &decoded_challenge, NULL);
+ decoded_challenge = Base64::decode(decoded_challenge);
+ if (!decoded_challenge.empty()) {
+ SecBuffer in_sec;
+ in_sec.pvBuffer = const_cast<char *>(decoded_challenge.data());
+ in_sec.cbBuffer = static_cast<unsigned long>(decoded_challenge.size());
+ in_sec.BufferType = SECBUFFER_TOKEN;
+
+ SecBufferDesc in_buf_desc;
+ in_buf_desc.ulVersion = 0;
+ in_buf_desc.cBuffers = 1;
+ in_buf_desc.pBuffers = &in_sec;
+
+ ret = InitializeSecurityContextA(&neg->cred, &neg->ctx, spn, flags, 0, SECURITY_NATIVE_DREP, &in_buf_desc, 0, &neg->ctx, &out_buf_desc, &ret_flags, &lifetime);
+ //LOG(INFO) << "$$$ InitializeSecurityContext @ " << TimeDiff(talk_base::Time(), now);
+ if (FAILED(ret)) {
+ LOG(LS_ERROR) << "InitializeSecurityContext returned: "
+ << ErrorName(ret, SECURITY_ERRORS);
+ return HAR_ERROR;
+ }
+ } else if (neg->specified_credentials) {
+ // Try again with default credentials
+ specify_credentials = false;
+ delete context;
+ context = neg = 0;
+ } else {
+ return HAR_CREDENTIALS;
+ }
+ }
+
+ if (!neg) {
+ unsigned char userbuf[256], passbuf[256], domainbuf[16];
+ SEC_WINNT_AUTH_IDENTITY_A auth_id, * pauth_id = 0;
+ if (specify_credentials) {
+ memset(&auth_id, 0, sizeof(auth_id));
+ size_t len = password.GetLength()+1;
+ char * sensitive = new char[len];
+ password.CopyTo(sensitive, true);
+ std::string::size_type pos = username.find('\\');
+ if (pos == std::string::npos) {
+ auth_id.UserLength = static_cast<unsigned long>(
+ _min(sizeof(userbuf) - 1, username.size()));
+ memcpy(userbuf, username.c_str(), auth_id.UserLength);
+ userbuf[auth_id.UserLength] = 0;
+ auth_id.DomainLength = 0;
+ domainbuf[auth_id.DomainLength] = 0;
+ auth_id.PasswordLength = static_cast<unsigned long>(
+ _min(sizeof(passbuf) - 1, password.GetLength()));
+ memcpy(passbuf, sensitive, auth_id.PasswordLength);
+ passbuf[auth_id.PasswordLength] = 0;
+ } else {
+ auth_id.UserLength = static_cast<unsigned long>(
+ _min(sizeof(userbuf) - 1, username.size() - pos - 1));
+ memcpy(userbuf, username.c_str() + pos + 1, auth_id.UserLength);
+ userbuf[auth_id.UserLength] = 0;
+ auth_id.DomainLength = static_cast<unsigned long>(
+ _min(sizeof(domainbuf) - 1, pos));
+ memcpy(domainbuf, username.c_str(), auth_id.DomainLength);
+ domainbuf[auth_id.DomainLength] = 0;
+ auth_id.PasswordLength = static_cast<unsigned long>(
+ _min(sizeof(passbuf) - 1, password.GetLength()));
+ memcpy(passbuf, sensitive, auth_id.PasswordLength);
+ passbuf[auth_id.PasswordLength] = 0;
+ }
+ memset(sensitive, 0, len);
+ delete [] sensitive;
+ auth_id.User = userbuf;
+ auth_id.Domain = domainbuf;
+ auth_id.Password = passbuf;
+ auth_id.Flags = SEC_WINNT_AUTH_IDENTITY_ANSI;
+ pauth_id = &auth_id;
+ LOG(LS_VERBOSE) << "Negotiate protocol: Using specified credentials";
+ } else {
+ LOG(LS_VERBOSE) << "Negotiate protocol: Using default credentials";
+ }
+
+ CredHandle cred;
+ ret = AcquireCredentialsHandleA(0, want_negotiate ? NEGOSSP_NAME_A : NTLMSP_NAME_A, SECPKG_CRED_OUTBOUND, 0, pauth_id, 0, 0, &cred, &lifetime);
+ //LOG(INFO) << "$$$ AcquireCredentialsHandle @ " << TimeDiff(talk_base::Time(), now);
+ if (ret != SEC_E_OK) {
+ LOG(LS_ERROR) << "AcquireCredentialsHandle error: "
+ << ErrorName(ret, SECURITY_ERRORS);
+ return HAR_IGNORE;
+ }
+
+ //CSecBufferBundle<5, CSecBufferBase::FreeSSPI> sb_out;
+
+ CtxtHandle ctx;
+ ret = InitializeSecurityContextA(&cred, 0, spn, flags, 0, SECURITY_NATIVE_DREP, 0, 0, &ctx, &out_buf_desc, &ret_flags, &lifetime);
+ //LOG(INFO) << "$$$ InitializeSecurityContext @ " << TimeDiff(talk_base::Time(), now);
+ if (FAILED(ret)) {
+ LOG(LS_ERROR) << "InitializeSecurityContext returned: "
+ << ErrorName(ret, SECURITY_ERRORS);
+ FreeCredentialsHandle(&cred);
+ return HAR_IGNORE;
+ }
+
+ assert(!context);
+ context = neg = new NegotiateAuthContext(auth_method, cred, ctx);
+ neg->specified_credentials = specify_credentials;
+ neg->steps = steps;
+ }
+
+ if ((ret == SEC_I_COMPLETE_NEEDED) || (ret == SEC_I_COMPLETE_AND_CONTINUE)) {
+ ret = CompleteAuthToken(&neg->ctx, &out_buf_desc);
+ //LOG(INFO) << "$$$ CompleteAuthToken @ " << TimeDiff(talk_base::Time(), now);
+ LOG(LS_VERBOSE) << "CompleteAuthToken returned: "
+ << ErrorName(ret, SECURITY_ERRORS);
+ if (FAILED(ret)) {
+ return HAR_ERROR;
+ }
+ }
+
+ //LOG(INFO) << "$$$ NEGOTIATE took " << TimeDiff(talk_base::Time(), now) << "ms";
+
+ std::string decoded(out_buf, out_buf + out_sec.cbBuffer);
+ response = auth_method;
+ response.append(" ");
+ response.append(Base64::encode(decoded));
+ return HAR_RESPONSE;
+ }
+#endif
+#endif // WIN32
+
+ return HAR_IGNORE;
+}
+
+//////////////////////////////////////////////////////////////////////
+
+} // namespace talk_base
diff --git a/Plugins/jingle/libjingle/talk/base/httpcommon.h b/Plugins/jingle/libjingle/talk/base/httpcommon.h
new file mode 100644
index 0000000..2893933
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/httpcommon.h
@@ -0,0 +1,373 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TALK_BASE_HTTPCOMMON_H__
+#define TALK_BASE_HTTPCOMMON_H__
+
+#include <map>
+#include <string>
+#include <vector>
+#include "talk/base/basictypes.h"
+#include "talk/base/scoped_ptr.h"
+#include "talk/base/stringutils.h"
+#include "talk/base/stream.h"
+
+namespace talk_base {
+
+class CryptString;
+class SocketAddress;
+
+//////////////////////////////////////////////////////////////////////
+// Constants
+//////////////////////////////////////////////////////////////////////
+
+enum HttpCode {
+ HC_OK = 200,
+ HC_NON_AUTHORITATIVE = 203,
+ HC_NO_CONTENT = 204,
+ HC_PARTIAL_CONTENT = 206,
+
+ HC_MULTIPLE_CHOICES = 300,
+ HC_MOVED_PERMANENTLY = 301,
+ HC_FOUND = 302,
+ HC_SEE_OTHER = 303,
+ HC_NOT_MODIFIED = 304,
+ HC_MOVED_TEMPORARILY = 307,
+
+ HC_BAD_REQUEST = 400,
+ HC_UNAUTHORIZED = 401,
+ HC_FORBIDDEN = 403,
+ HC_NOT_FOUND = 404,
+ HC_PROXY_AUTHENTICATION_REQUIRED = 407,
+ HC_GONE = 410,
+
+ HC_INTERNAL_SERVER_ERROR = 500
+};
+
+enum HttpVersion {
+ HVER_1_0, HVER_1_1,
+ HVER_LAST = HVER_1_1
+};
+
+enum HttpVerb {
+ HV_GET, HV_POST, HV_PUT, HV_DELETE, HV_CONNECT, HV_HEAD,
+ HV_LAST = HV_HEAD
+};
+
+enum HttpError {
+ HE_NONE,
+ HE_PROTOCOL, HE_DISCONNECTED, HE_OVERFLOW,
+ HE_SOCKET, HE_SHUTDOWN, HE_OPERATION_CANCELLED,
+ HE_AUTH, // Proxy Authentication Required
+ HE_CERTIFICATE_EXPIRED, // During SSL negotiation
+ HE_STREAM, // Problem reading or writing to the document
+ HE_CACHE, // Problem reading from cache
+ HE_DEFAULT
+};
+
+enum HttpHeader {
+ HH_AGE,
+ HH_CACHE_CONTROL,
+ HH_CONNECTION,
+ HH_CONTENT_LENGTH,
+ HH_CONTENT_RANGE,
+ HH_CONTENT_TYPE,
+ HH_COOKIE,
+ HH_DATE,
+ HH_ETAG,
+ HH_EXPIRES,
+ HH_HOST,
+ HH_IF_MODIFIED_SINCE,
+ HH_IF_NONE_MATCH,
+ HH_KEEP_ALIVE,
+ HH_LAST_MODIFIED,
+ HH_LOCATION,
+ HH_PROXY_AUTHENTICATE,
+ HH_PROXY_AUTHORIZATION,
+ HH_PROXY_CONNECTION,
+ HH_RANGE,
+ HH_SET_COOKIE,
+ HH_TE,
+ HH_TRAILERS,
+ HH_TRANSFER_ENCODING,
+ HH_UPGRADE,
+ HH_USER_AGENT,
+ HH_WWW_AUTHENTICATE,
+ HH_LAST = HH_WWW_AUTHENTICATE
+};
+
+const uint16 HTTP_DEFAULT_PORT = 80;
+const uint16 HTTP_SECURE_PORT = 443;
+
+//////////////////////////////////////////////////////////////////////
+// Utility Functions
+//////////////////////////////////////////////////////////////////////
+
+inline HttpError mkerr(HttpError err, HttpError def_err = HE_DEFAULT) {
+ return (err != HE_NONE) ? err : def_err;
+}
+
+const char* ToString(HttpVersion version);
+bool FromString(HttpVersion& version, const std::string& str);
+
+const char* ToString(HttpVerb verb);
+bool FromString(HttpVerb& verb, const std::string& str);
+
+const char* ToString(HttpHeader header);
+bool FromString(HttpHeader& header, const std::string& str);
+
+inline bool HttpCodeIsInformational(uint32 code) { return ((code / 100) == 1); }
+inline bool HttpCodeIsSuccessful(uint32 code) { return ((code / 100) == 2); }
+inline bool HttpCodeIsRedirection(uint32 code) { return ((code / 100) == 3); }
+inline bool HttpCodeIsClientError(uint32 code) { return ((code / 100) == 4); }
+inline bool HttpCodeIsServerError(uint32 code) { return ((code / 100) == 5); }
+
+bool HttpCodeHasBody(uint32 code);
+bool HttpCodeIsCacheable(uint32 code);
+bool HttpHeaderIsEndToEnd(HttpHeader header);
+bool HttpHeaderIsCollapsible(HttpHeader header);
+
+struct HttpData;
+bool HttpShouldKeepAlive(const HttpData& data);
+
+typedef std::pair<std::string, std::string> HttpAttribute;
+typedef std::vector<HttpAttribute> HttpAttributeList;
+void HttpParseAttributes(const char * data, size_t len,
+ HttpAttributeList& attributes);
+bool HttpHasAttribute(const HttpAttributeList& attributes,
+ const std::string& name,
+ std::string* value);
+bool HttpHasNthAttribute(HttpAttributeList& attributes,
+ size_t index,
+ std::string* name,
+ std::string* value);
+
+// Convert RFC1123 date (DoW, DD Mon YYYY HH:MM:SS TZ) to unix timestamp
+bool HttpDateToSeconds(const std::string& date, unsigned long* seconds);
+
+inline const uint16 UrlDefaultPort(bool secure) {
+ return secure ? HTTP_SECURE_PORT : HTTP_DEFAULT_PORT;
+}
+
+// functional for insensitive std::string compare
+struct iless {
+ bool operator()(const std::string& lhs, const std::string& rhs) const {
+ return (::_stricmp(lhs.c_str(), rhs.c_str()) < 0);
+ }
+};
+
+//////////////////////////////////////////////////////////////////////
+// Url
+//////////////////////////////////////////////////////////////////////
+
+template<class CTYPE>
+class Url {
+public:
+ typedef typename Traits<CTYPE>::string string;
+
+ // TODO: Implement Encode/Decode
+ static int Encode(const CTYPE* source, CTYPE* destination, size_t len);
+ static int Encode(const string& source, string& destination);
+ static int Decode(const CTYPE* source, CTYPE* destination, size_t len);
+ static int Decode(const string& source, string& destination);
+
+ Url(const string& url);
+ Url(const string& path, const string& server, uint16 port = HTTP_DEFAULT_PORT)
+ : m_server(server), m_path(path), m_port(port),
+ m_secure(HTTP_SECURE_PORT == port)
+ {
+ ASSERT(m_path.empty() || (m_path[0] == static_cast<CTYPE>('/')));
+ }
+
+ bool valid() const { return !m_server.empty(); }
+ const string& server() const { return m_server; }
+ // Note: path() was renamed to path_, because it now uses the stricter sense
+ // of not including a query string. I'm trying to think of a clearer name.
+ const string& path_() const { return m_path; }
+ const string& query() const { return m_query; }
+ string full_path();
+ string url();
+ uint16 port() const { return m_port; }
+ bool secure() const { return m_secure; }
+
+ void set_server(const string& val) { m_server = val; }
+ void set_path(const string& val) {
+ ASSERT(val.empty() || (val[0] == static_cast<CTYPE>('/')));
+ m_path = val;
+ }
+ void set_query(const string& val) {
+ ASSERT(val.empty() || (val[0] == static_cast<CTYPE>('?')));
+ m_query = val;
+ }
+ void set_port(uint16 val) { m_port = val; }
+ void set_secure(bool val) { m_secure = val; }
+
+private:
+ string m_server, m_path, m_query;
+ uint16 m_port;
+ bool m_secure;
+};
+
+//////////////////////////////////////////////////////////////////////
+// HttpData
+//////////////////////////////////////////////////////////////////////
+
+struct HttpData {
+ typedef std::multimap<std::string, std::string, iless> HeaderMap;
+ typedef HeaderMap::const_iterator const_iterator;
+
+ HttpVersion version;
+ scoped_ptr<StreamInterface> document;
+
+ HttpData() : version(HVER_1_1) { }
+
+ enum HeaderCombine { HC_YES, HC_NO, HC_AUTO, HC_REPLACE, HC_NEW };
+ void changeHeader(const std::string& name, const std::string& value,
+ HeaderCombine combine);
+ inline void addHeader(const std::string& name, const std::string& value,
+ bool append = true) {
+ changeHeader(name, value, append ? HC_AUTO : HC_NO);
+ }
+ inline void setHeader(const std::string& name, const std::string& value,
+ bool overwrite = true) {
+ changeHeader(name, value, overwrite ? HC_REPLACE : HC_NEW);
+ }
+ void clearHeader(const std::string& name);
+
+ // keep in mind, this may not do what you want in the face of multiple headers
+ bool hasHeader(const std::string& name, std::string* value) const;
+
+ inline const_iterator begin() const {
+ return m_headers.begin();
+ }
+ inline const_iterator end() const {
+ return m_headers.end();
+ }
+ inline const_iterator begin(const std::string& name) const {
+ return m_headers.lower_bound(name);
+ }
+ inline const_iterator end(const std::string& name) const {
+ return m_headers.upper_bound(name);
+ }
+
+ // Convenience methods using HttpHeader
+ inline void changeHeader(HttpHeader header, const std::string& value,
+ HeaderCombine combine) {
+ changeHeader(ToString(header), value, combine);
+ }
+ inline void addHeader(HttpHeader header, const std::string& value,
+ bool append = true) {
+ addHeader(ToString(header), value, append);
+ }
+ inline void setHeader(HttpHeader header, const std::string& value,
+ bool overwrite = true) {
+ setHeader(ToString(header), value, overwrite);
+ }
+ inline void clearHeader(HttpHeader header) {
+ clearHeader(ToString(header));
+ }
+ inline bool hasHeader(HttpHeader header, std::string* value) const {
+ return hasHeader(ToString(header), value);
+ }
+ inline const_iterator begin(HttpHeader header) const {
+ return m_headers.lower_bound(ToString(header));
+ }
+ inline const_iterator end(HttpHeader header) const {
+ return m_headers.upper_bound(ToString(header));
+ }
+
+ void setContent(const std::string& content_type, StreamInterface* document);
+
+ virtual size_t formatLeader(char* buffer, size_t size) = 0;
+ virtual HttpError parseLeader(const char* line, size_t len) = 0;
+
+protected:
+ virtual ~HttpData() { }
+ void clear(bool release_document);
+
+private:
+ HeaderMap m_headers;
+};
+
+struct HttpRequestData : public HttpData {
+ HttpVerb verb;
+ std::string path;
+
+ HttpRequestData() : verb(HV_GET) { }
+
+ void clear(bool release_document);
+
+ virtual size_t formatLeader(char* buffer, size_t size);
+ virtual HttpError parseLeader(const char* line, size_t len);
+};
+
+struct HttpResponseData : public HttpData {
+ uint32 scode;
+ std::string message;
+
+ HttpResponseData() : scode(HC_INTERNAL_SERVER_ERROR) { }
+ void clear(bool release_document);
+
+ // Convenience methods
+ void set_success(uint32 scode = HC_OK);
+ void set_success(const std::string& content_type, StreamInterface* document,
+ uint32 scode = HC_OK);
+ void set_redirect(const std::string& location,
+ uint32 scode = HC_MOVED_TEMPORARILY);
+ void set_error(uint32 scode);
+
+ virtual size_t formatLeader(char* buffer, size_t size);
+ virtual HttpError parseLeader(const char* line, size_t len);
+};
+
+//////////////////////////////////////////////////////////////////////
+// Http Authentication
+//////////////////////////////////////////////////////////////////////
+
+struct HttpAuthContext {
+ std::string auth_method;
+ HttpAuthContext(const std::string& auth) : auth_method(auth) { }
+ virtual ~HttpAuthContext() { }
+};
+
+enum HttpAuthResult { HAR_RESPONSE, HAR_IGNORE, HAR_CREDENTIALS, HAR_ERROR };
+
+// 'context' is used by this function to record information between calls.
+// Start by passing a null pointer, then pass the same pointer each additional
+// call. When the authentication attempt is finished, delete the context.
+HttpAuthResult HttpAuthenticate(
+ const char * challenge, size_t len,
+ const SocketAddress& server,
+ const std::string& method, const std::string& uri,
+ const std::string& username, const CryptString& password,
+ HttpAuthContext *& context, std::string& response, std::string& auth_method);
+
+//////////////////////////////////////////////////////////////////////
+
+} // namespace talk_base
+
+#endif // TALK_BASE_HTTPCOMMON_H__
diff --git a/Plugins/jingle/libjingle/talk/base/httpserver.cc b/Plugins/jingle/libjingle/talk/base/httpserver.cc
new file mode 100644
index 0000000..9c27b5c
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/httpserver.cc
@@ -0,0 +1,261 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <algorithm>
+
+#include "talk/base/httpcommon-inl.h"
+
+#include "talk/base/asyncsocket.h"
+#include "talk/base/common.h"
+#include "talk/base/httpserver.h"
+#include "talk/base/logging.h"
+#include "talk/base/socketstream.h"
+
+namespace talk_base {
+
+///////////////////////////////////////////////////////////////////////////////
+// HttpServer
+///////////////////////////////////////////////////////////////////////////////
+
+HttpServer::HttpServer() : next_connection_id_(1) {
+}
+
+HttpServer::~HttpServer() {
+ for (ConnectionMap::iterator it = connections_.begin();
+ it != connections_.end();
+ ++it) {
+ StreamInterface* stream = it->second->EndProcess();
+ delete stream;
+ delete it->second;
+ }
+}
+
+int
+HttpServer::HandleConnection(StreamInterface* stream) {
+ int connection_id = next_connection_id_++;
+ ASSERT(connection_id != HTTP_INVALID_CONNECTION_ID);
+ Connection* connection = new Connection(connection_id, this);
+ connections_.insert(ConnectionMap::value_type(connection_id, connection));
+ connection->BeginProcess(stream);
+ return connection_id;
+}
+
+void
+HttpServer::Respond(HttpTransaction* transaction) {
+ int connection_id = transaction->connection_id();
+ if (Connection* connection = Find(connection_id)) {
+ connection->Respond(transaction);
+ } else {
+ delete transaction;
+ // We may be tempted to SignalHttpComplete, but that implies that a
+ // connection still exists.
+ }
+}
+
+void
+HttpServer::Close(int connection_id, bool force) {
+ if (Connection* connection = Find(connection_id)) {
+ connection->InitiateClose(force);
+ }
+}
+
+void
+HttpServer::CloseAll(bool force) {
+ std::list<Connection*> connections;
+ for (ConnectionMap::const_iterator it = connections_.begin();
+ it != connections_.end(); ++it) {
+ connections.push_back(it->second);
+ }
+ for (std::list<Connection*>::const_iterator it = connections.begin();
+ it != connections.end(); ++it) {
+ (*it)->InitiateClose(force);
+ }
+}
+
+HttpServer::Connection*
+HttpServer::Find(int connection_id) {
+ ConnectionMap::iterator it = connections_.find(connection_id);
+ if (it == connections_.end())
+ return NULL;
+ return it->second;
+}
+
+void
+HttpServer::Remove(int connection_id) {
+ ConnectionMap::iterator it = connections_.find(connection_id);
+ if (it == connections_.end()) {
+ ASSERT(false);
+ return;
+ }
+ Connection* connection = it->second;
+ connections_.erase(it);
+ SignalConnectionClosed(this, connection_id, connection->EndProcess());
+ delete connection;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// HttpServer::Connection
+///////////////////////////////////////////////////////////////////////////////
+
+HttpServer::Connection::Connection(int connection_id, HttpServer* server)
+ : connection_id_(connection_id), server_(server),
+ current_(NULL), signalling_(false), close_(false) {
+}
+
+HttpServer::Connection::~Connection() {
+ delete current_;
+}
+
+void
+HttpServer::Connection::BeginProcess(StreamInterface* stream) {
+ base_.notify(this);
+ base_.attach(stream);
+ current_ = new HttpTransaction(connection_id_);
+ current_->request()->document.reset(new MemoryStream);
+ if (base_.mode() != HM_CONNECT)
+ base_.recv(current_->request());
+}
+
+StreamInterface*
+HttpServer::Connection::EndProcess() {
+ base_.notify(NULL);
+ base_.abort(HE_DISCONNECTED);
+ return base_.detach();
+}
+
+void
+HttpServer::Connection::Respond(HttpTransaction* transaction) {
+ ASSERT(current_ == NULL);
+ current_ = transaction;
+ if (current_->response()->begin() == current_->response()->end()) {
+ current_->response()->set_error(HC_INTERNAL_SERVER_ERROR);
+ }
+ bool keep_alive = HttpShouldKeepAlive(*transaction->request());
+ current_->response()->setHeader(HH_CONNECTION,
+ keep_alive ? "Keep-Alive" : "Close",
+ false);
+ close_ = !HttpShouldKeepAlive(*transaction->response());
+ base_.send(current_->response());
+}
+
+void
+HttpServer::Connection::InitiateClose(bool force) {
+ if (!signalling_ && (force || (base_.mode() != HM_SEND))) {
+ server_->Remove(connection_id_);
+ } else {
+ close_ = true;
+ }
+}
+
+//
+// IHttpNotify Implementation
+//
+
+HttpError
+HttpServer::Connection::onHttpHeaderComplete(bool chunked, size_t& data_size) {
+ if (data_size == SIZE_UNKNOWN) {
+ data_size = 0;
+ }
+ return HE_NONE;
+}
+
+void
+HttpServer::Connection::onHttpComplete(HttpMode mode, HttpError err) {
+ if (mode == HM_SEND) {
+ ASSERT(current_ != NULL);
+ signalling_ = true;
+ server_->SignalHttpRequestComplete(server_, current_, err);
+ signalling_ = false;
+ if (close_) {
+ // Force a close
+ err = HE_DISCONNECTED;
+ }
+ }
+ if (err != HE_NONE) {
+ server_->Remove(connection_id_);
+ } else if (mode == HM_CONNECT) {
+ base_.recv(current_->request());
+ } else if (mode == HM_RECV) {
+ ASSERT(current_ != NULL);
+ // TODO: do we need this?
+ //request_.document_->rewind();
+ HttpTransaction* transaction = current_;
+ current_ = NULL;
+ server_->SignalHttpRequest(server_, transaction);
+ } else if (mode == HM_SEND) {
+ current_->request()->clear(true);
+ current_->request()->document.reset(new MemoryStream);
+ current_->response()->clear(true);
+ base_.recv(current_->request());
+ } else {
+ ASSERT(false);
+ }
+}
+
+void
+HttpServer::Connection::onHttpClosed(HttpError err) {
+ UNUSED(err);
+ server_->Remove(connection_id_);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// HttpListenServer
+///////////////////////////////////////////////////////////////////////////////
+
+HttpListenServer::HttpListenServer(AsyncSocket* listener)
+ : listener_(listener) {
+ listener_->SignalReadEvent.connect(this, &HttpListenServer::OnReadEvent);
+}
+
+HttpListenServer::~HttpListenServer() {
+}
+
+int
+HttpListenServer::Listen(const SocketAddress& address) {
+ if ((listener_->Bind(address) != SOCKET_ERROR) &&
+ (listener_->Listen(5) != SOCKET_ERROR))
+ return 0;
+ return listener_->GetError();
+}
+
+bool
+HttpListenServer::GetAddress(SocketAddress& address) {
+ address = listener_->GetLocalAddress();
+ return !address.IsNil();
+}
+
+void
+HttpListenServer::OnReadEvent(AsyncSocket* socket) {
+ ASSERT(socket == listener_);
+ AsyncSocket* incoming = static_cast<AsyncSocket*>(listener_->Accept(NULL));
+ if (incoming)
+ HandleConnection(new SocketStream(incoming));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+} // namespace talk_base
diff --git a/Plugins/jingle/libjingle/talk/base/httpserver.h b/Plugins/jingle/libjingle/talk/base/httpserver.h
new file mode 100644
index 0000000..a09218c
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/httpserver.h
@@ -0,0 +1,143 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TALK_BASE_HTTPSERVER_H__
+#define TALK_BASE_HTTPSERVER_H__
+
+#include <map>
+#include "talk/base/httpbase.h"
+
+namespace talk_base {
+
+class AsyncSocket;
+class HttpServer;
+class SocketAddress;
+
+//////////////////////////////////////////////////////////////////////
+// HttpServer
+//////////////////////////////////////////////////////////////////////
+
+const int HTTP_INVALID_CONNECTION_ID = 0;
+
+class HttpTransaction {
+public:
+ HttpTransaction(int connection_id) : connection_id_(connection_id) { }
+ ~HttpTransaction() { }
+
+ int connection_id() const { return connection_id_; }
+
+ HttpRequestData* request() { return &request_; }
+ HttpResponseData* response() { return &response_; }
+
+private:
+ int connection_id_;
+ HttpRequestData request_;
+ HttpResponseData response_;
+};
+
+class HttpServer {
+public:
+ HttpServer();
+ virtual ~HttpServer();
+
+ int HandleConnection(StreamInterface* stream);
+ // Due to sigslot issues, we can't destroy some streams at an arbitrary time.
+ sigslot::signal3<HttpServer*, int, StreamInterface*> SignalConnectionClosed;
+
+ // An HTTP request has been made, and is available in the transaction object.
+ // Populate the transaction's response, and then return the object via the
+ // Respond method. Note that during this time, ownership of the transaction
+ // object is transferred, so it may be passed between threads, although
+ // respond must be called on the server's active thread.
+ sigslot::signal2<HttpServer*, HttpTransaction*> SignalHttpRequest;
+ void Respond(HttpTransaction* transaction);
+
+ // If you want to know when a request completes, listen to this event.
+ sigslot::signal3<HttpServer*, HttpTransaction*, int>
+ SignalHttpRequestComplete;
+
+ // Stop processing the connection indicated by connection_id.
+ // Unless force is true, the server will complete sending a response that is
+ // in progress.
+ void Close(int connection_id, bool force);
+ void CloseAll(bool force);
+
+private:
+ class Connection : private IHttpNotify {
+ public:
+ Connection(int connection_id, HttpServer* server);
+ virtual ~Connection();
+
+ void BeginProcess(StreamInterface* stream);
+ StreamInterface* EndProcess();
+
+ void Respond(HttpTransaction* transaction);
+ void InitiateClose(bool force);
+
+ // IHttpNotify Interface
+ virtual HttpError onHttpHeaderComplete(bool chunked, size_t& data_size);
+ virtual void onHttpComplete(HttpMode mode, HttpError err);
+ virtual void onHttpClosed(HttpError err);
+
+ int connection_id_;
+ HttpServer* server_;
+ HttpBase base_;
+ HttpTransaction* current_;
+ bool signalling_, close_;
+ };
+
+ Connection* Find(int connection_id);
+ void Remove(int connection_id);
+
+ friend class Connection;
+ typedef std::map<int,Connection*> ConnectionMap;
+
+ ConnectionMap connections_;
+ int next_connection_id_;
+};
+
+//////////////////////////////////////////////////////////////////////
+
+class HttpListenServer : public HttpServer, public sigslot::has_slots<> {
+public:
+ HttpListenServer(AsyncSocket* listener);
+ virtual ~HttpListenServer();
+
+ int Listen(const SocketAddress& address);
+ bool GetAddress(SocketAddress& address);
+
+private:
+ void OnReadEvent(AsyncSocket* socket);
+
+ AsyncSocket* listener_;
+};
+
+//////////////////////////////////////////////////////////////////////
+
+} // namespace talk_base
+
+#endif // TALK_BASE_HTTPSERVER_H__
diff --git a/Plugins/jingle/libjingle/talk/base/icftypes.h b/Plugins/jingle/libjingle/talk/base/icftypes.h
new file mode 100644
index 0000000..a88c591
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/icftypes.h
@@ -0,0 +1,116 @@
+
+#pragma warning( disable: 4049 ) /* more than 64k source lines */
+
+/* this ALWAYS GENERATED file contains the definitions for the interfaces */
+
+
+ /* File created by MIDL compiler version 6.00.0347 */
+/* Compiler settings for icftypes.idl:
+ Oicf, W1, Zp8, env=Win32 (32b run)
+ protocol : dce , ms_ext, c_ext, robust
+ error checks: allocation ref bounds_check enum stub_data
+ VC __declspec() decoration level:
+ __declspec(uuid()), __declspec(selectany), __declspec(novtable)
+ DECLSPEC_UUID(), MIDL_INTERFACE()
+*/
+//@@MIDL_FILE_HEADING( )
+
+
+/* verify that the <rpcndr.h> version is high enough to compile this file*/
+#ifndef __REQUIRED_RPCNDR_H_VERSION__
+#define __REQUIRED_RPCNDR_H_VERSION__ 475
+#endif
+
+#include "rpc.h"
+#include "rpcndr.h"
+
+#ifndef __RPCNDR_H_VERSION__
+#error this stub requires an updated version of <rpcndr.h>
+#endif // __RPCNDR_H_VERSION__
+
+
+#ifndef __icftypes_h__
+#define __icftypes_h__
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1020)
+#pragma once
+#endif
+
+/* Forward Declarations */
+
+/* header files for imported files */
+#include "wtypes.h"
+
+#ifdef __cplusplus
+extern "C"{
+#endif
+
+void * __RPC_USER MIDL_user_allocate(size_t);
+void __RPC_USER MIDL_user_free( void * );
+
+/* interface __MIDL_itf_icftypes_0000 */
+/* [local] */
+
+typedef
+enum NET_FW_POLICY_TYPE_
+ { NET_FW_POLICY_GROUP = 0,
+ NET_FW_POLICY_LOCAL = NET_FW_POLICY_GROUP + 1,
+ NET_FW_POLICY_EFFECTIVE = NET_FW_POLICY_LOCAL + 1,
+ NET_FW_POLICY_TYPE_MAX = NET_FW_POLICY_EFFECTIVE + 1
+ } NET_FW_POLICY_TYPE;
+
+typedef
+enum NET_FW_PROFILE_TYPE_
+ { NET_FW_PROFILE_DOMAIN = 0,
+ NET_FW_PROFILE_STANDARD = NET_FW_PROFILE_DOMAIN + 1,
+ NET_FW_PROFILE_CURRENT = NET_FW_PROFILE_STANDARD + 1,
+ NET_FW_PROFILE_TYPE_MAX = NET_FW_PROFILE_CURRENT + 1
+ } NET_FW_PROFILE_TYPE;
+
+typedef
+enum NET_FW_IP_VERSION_
+ { NET_FW_IP_VERSION_V4 = 0,
+ NET_FW_IP_VERSION_V6 = NET_FW_IP_VERSION_V4 + 1,
+ NET_FW_IP_VERSION_ANY = NET_FW_IP_VERSION_V6 + 1,
+ NET_FW_IP_VERSION_MAX = NET_FW_IP_VERSION_ANY + 1
+ } NET_FW_IP_VERSION;
+
+typedef
+enum NET_FW_SCOPE_
+ { NET_FW_SCOPE_ALL = 0,
+ NET_FW_SCOPE_LOCAL_SUBNET = NET_FW_SCOPE_ALL + 1,
+ NET_FW_SCOPE_CUSTOM = NET_FW_SCOPE_LOCAL_SUBNET + 1,
+ NET_FW_SCOPE_MAX = NET_FW_SCOPE_CUSTOM + 1
+ } NET_FW_SCOPE;
+
+typedef
+enum NET_FW_IP_PROTOCOL_
+ { NET_FW_IP_PROTOCOL_TCP = 6,
+ NET_FW_IP_PROTOCOL_UDP = 17
+ } NET_FW_IP_PROTOCOL;
+
+typedef
+enum NET_FW_SERVICE_TYPE_
+ { NET_FW_SERVICE_FILE_AND_PRINT = 0,
+ NET_FW_SERVICE_UPNP = NET_FW_SERVICE_FILE_AND_PRINT + 1,
+ NET_FW_SERVICE_REMOTE_DESKTOP = NET_FW_SERVICE_UPNP + 1,
+ NET_FW_SERVICE_NONE = NET_FW_SERVICE_REMOTE_DESKTOP + 1,
+ NET_FW_SERVICE_TYPE_MAX = NET_FW_SERVICE_NONE + 1
+ } NET_FW_SERVICE_TYPE;
+
+
+
+extern RPC_IF_HANDLE __MIDL_itf_icftypes_0000_v0_0_c_ifspec;
+extern RPC_IF_HANDLE __MIDL_itf_icftypes_0000_v0_0_s_ifspec;
+
+/* Additional Prototypes for ALL interfaces */
+
+/* end of Additional Prototypes */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
+
diff --git a/Plugins/jingle/libjingle/talk/base/linked_ptr.h b/Plugins/jingle/libjingle/talk/base/linked_ptr.h
new file mode 100644
index 0000000..8d5ce49
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/linked_ptr.h
@@ -0,0 +1,115 @@
+/*
+ * linked_ptr - simple reference linked pointer
+ * (like reference counting, just using a linked list of the references
+ * instead of their count.)
+ *
+ * The implementation stores three pointers for every linked_ptr, but
+ * does not allocate anything on the free store.
+ */
+
+#ifndef TALK_BASE_LINKED_PTR_H__
+#define TALK_BASE_LINKED_PTR_H__
+
+namespace talk_base {
+
+/* For ANSI-challenged compilers, you may want to #define
+ * NO_MEMBER_TEMPLATES, explicit or mutable */
+#define NO_MEMBER_TEMPLATES
+
+template <class X> class linked_ptr
+{
+public:
+
+#ifndef NO_MEMBER_TEMPLATES
+# define TEMPLATE_FUNCTION template <class Y>
+ TEMPLATE_FUNCTION friend class linked_ptr<Y>;
+#else
+# define TEMPLATE_FUNCTION
+ typedef X Y;
+#endif
+
+ typedef X element_type;
+
+ explicit linked_ptr(X* p = 0) throw()
+ : itsPtr(p) {itsPrev = itsNext = this;}
+ ~linked_ptr()
+ {release();}
+ linked_ptr(const linked_ptr& r) throw()
+ {acquire(r);}
+ linked_ptr& operator=(const linked_ptr& r)
+ {
+ if (this != &r) {
+ release();
+ acquire(r);
+ }
+ return *this;
+ }
+
+#ifndef NO_MEMBER_TEMPLATES
+ template <class Y> friend class linked_ptr<Y>;
+ template <class Y> linked_ptr(const linked_ptr<Y>& r) throw()
+ {acquire(r);}
+ template <class Y> linked_ptr& operator=(const linked_ptr<Y>& r)
+ {
+ if (this != &r) {
+ release();
+ acquire(r);
+ }
+ return *this;
+ }
+#endif // NO_MEMBER_TEMPLATES
+
+ X& operator*() const throw() {return *itsPtr;}
+ X* operator->() const throw() {return itsPtr;}
+ X* get() const throw() {return itsPtr;}
+ bool unique() const throw() {return itsPrev ? itsPrev==this : true;}
+
+private:
+ X* itsPtr;
+ mutable const linked_ptr* itsPrev;
+ mutable const linked_ptr* itsNext;
+
+ void acquire(const linked_ptr& r) throw()
+ { // insert this to the list
+ itsPtr = r.itsPtr;
+ itsNext = r.itsNext;
+ itsNext->itsPrev = this;
+ itsPrev = &r;
+#ifndef mutable
+ r.itsNext = this;
+#else // for ANSI-challenged compilers
+ (const_cast<linked_ptr<X>*>(&r))->itsNext = this;
+#endif
+ }
+
+#ifndef NO_MEMBER_TEMPLATES
+ template <class Y> void acquire(const linked_ptr<Y>& r) throw()
+ { // insert this to the list
+ itsPtr = r.itsPtr;
+ itsNext = r.itsNext;
+ itsNext->itsPrev = this;
+ itsPrev = &r;
+#ifndef mutable
+ r.itsNext = this;
+#else // for ANSI-challenged compilers
+ (const_cast<linked_ptr<X>*>(&r))->itsNext = this;
+#endif
+ }
+#endif // NO_MEMBER_TEMPLATES
+
+ void release()
+ { // erase this from the list, delete if unique
+ if (unique()) delete itsPtr;
+ else {
+ itsPrev->itsNext = itsNext;
+ itsNext->itsPrev = itsPrev;
+ itsPrev = itsNext = 0;
+ }
+ itsPtr = 0;
+ }
+};
+
+} // namespace talk_base
+
+#endif // TALK_BASE_LINKED_PTR_H__
+
diff --git a/Plugins/jingle/libjingle/talk/base/logging.cc b/Plugins/jingle/libjingle/talk/base/logging.cc
new file mode 100644
index 0000000..2a7fbe3
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/logging.cc
@@ -0,0 +1,349 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifdef WIN32
+#include <windows.h>
+#define snprintf _snprintf
+#undef ERROR // wingdi.h
+#endif
+
+#include <iostream>
+#include <iomanip>
+
+#include "talk/base/logging.h"
+#include "talk/base/stream.h"
+#include "talk/base/stringencode.h"
+#include "talk/base/time.h"
+
+namespace talk_base {
+
+/////////////////////////////////////////////////////////////////////////////
+// Constant Labels
+/////////////////////////////////////////////////////////////////////////////
+
+const char * FindLabel(int value, const talk_base::ConstantLabel entries[]) {
+ for (int i=0; entries[i].label; ++i) {
+ if (value == entries[i].value) {
+ return entries[i].label;
+ }
+ }
+ return 0;
+}
+
+std::string ErrorName(int err, const talk_base::ConstantLabel * err_table) {
+ const char * value = 0;
+ if (err == 0)
+ return "No error";
+
+ if (err_table != 0) {
+ if (const char * value = FindLabel(err, err_table))
+ return value;
+ }
+
+ char buffer[16];
+ snprintf(buffer, sizeof(buffer), "0x%08lx", err);
+ return buffer;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// LogMessage
+/////////////////////////////////////////////////////////////////////////////
+
+#if _DEBUG
+static const int LOG_DEFAULT = LS_INFO;
+#else // !_DEBUG
+static const int LOG_DEFAULT = LogMessage::NO_LOGGING;
+#endif // !_DEBUG
+
+// By default, release builds don't log, debug builds at info level
+int LogMessage::min_sev_ = LOG_DEFAULT;
+int LogMessage::dbg_sev_ = LOG_DEFAULT;
+
+// No file logging by default
+int LogMessage::stream_sev_ = NO_LOGGING;
+
+// Don't bother printing context for the ubiquitous INFO log messages
+int LogMessage::ctx_sev_ = LS_WARNING;
+
+// stream_ defaults to NULL
+// Note: we explicitly do not clean this up, because of the uncertain ordering
+// of destructors at program exit. Let the person who sets the stream trigger
+// cleanup by setting to NULL, or let it leak (safe at program exit).
+StreamInterface* LogMessage::stream_;
+
+// Boolean options default to false (0)
+bool LogMessage::thread_, LogMessage::timestamp_;
+
+// Program start time
+uint32 LogMessage::start_ = StartTime();
+
+// if we're in diagnostic mode, we'll be explicitly set that way. default to false
+bool LogMessage::is_diagnostic_mode_ = false;
+
+LogMessage::LogMessage(const char* file, int line, LoggingSeverity sev,
+ LogErrorContext err_ctx, int err, const char* module)
+ : severity_(sev) {
+ if (timestamp_) {
+ uint32 time = TimeDiff(Time(), start_);
+ print_stream_ << "[" << std::setfill('0') << std::setw(3) << (time / 1000)
+ << ":" << std::setw(3) << (time % 1000) << std::setfill(' ')
+ << "] ";
+ }
+
+ if (thread_) {
+#ifdef WIN32
+ DWORD id = GetCurrentThreadId();
+ print_stream_ << "[" << std::hex << id << std::dec << "] ";
+#endif // WIN32
+ }
+
+ if (severity_ >= ctx_sev_) {
+ print_stream_ << Describe(sev) << "(" << DescribeFile(file)
+ << ":" << line << "): ";
+ }
+
+ if (err_ctx != ERRCTX_NONE) {
+ std::ostringstream tmp;
+ tmp << "[0x" << std::setfill('0') << std::hex << std::setw(8) << err << "]";
+ switch (err_ctx) {
+ case ERRCTX_ERRNO:
+ tmp << " " << strerror(err);
+ break;
+ #ifdef WIN32
+ case ERRCTX_HRESULT: {
+ char msgbuf[256];
+ DWORD flags = FORMAT_MESSAGE_FROM_SYSTEM;
+ HMODULE hmod = GetModuleHandleA(module);
+ if (hmod)
+ flags |= FORMAT_MESSAGE_FROM_HMODULE;
+ if (DWORD len = FormatMessageA(
+ flags, hmod, err,
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ msgbuf, sizeof(msgbuf) / sizeof(msgbuf[0]), NULL)) {
+ while ((len > 0) &&
+ isspace(static_cast<unsigned char>(msgbuf[len-1]))) {
+ msgbuf[--len] = 0;
+ }
+ tmp << " " << msgbuf;
+ }
+ break; }
+ #endif // WIN32
+ default:
+ break;
+ }
+ extra_ = tmp.str();
+ }
+}
+
+LogMessage::~LogMessage() {
+ if (!extra_.empty())
+ print_stream_ << " : " << extra_;
+ print_stream_ << std::endl;
+ const std::string& str = print_stream_.str();
+
+ if (severity_ >= dbg_sev_) {
+ bool log_to_stderr = true;
+#ifdef WIN32
+ static bool debugger_present = (IsDebuggerPresent() != FALSE);
+ if (debugger_present) {
+ log_to_stderr = false;
+ OutputDebugStringA(str.c_str());
+ }
+ if (log_to_stderr) {
+ // This handles dynamically allocated consoles, too.
+ if (HANDLE error_handle = ::GetStdHandle(STD_ERROR_HANDLE)) {
+ log_to_stderr = false;
+ unsigned long written;
+ ::WriteFile(error_handle, str.data(), str.size(), &written, 0);
+ }
+ }
+#endif // WIN32
+ if (log_to_stderr) {
+ std::cerr << str;
+ std::cerr.flush();
+ }
+ }
+
+ if (severity_ >= stream_sev_) {
+ // If write isn't fully successful, what are we going to do, log it? :)
+ stream_->WriteAll(str.data(), str.size(), NULL, NULL);
+ }
+}
+
+void LogMessage::LogContext(int min_sev) {
+ ctx_sev_ = min_sev;
+}
+
+void LogMessage::LogThreads(bool on) {
+ thread_ = on;
+}
+
+void LogMessage::LogTimestamps(bool on) {
+ timestamp_ = on;
+}
+
+void LogMessage::ResetTimestamps() {
+ start_ = Time();
+}
+
+void LogMessage::LogToDebug(int min_sev) {
+ dbg_sev_ = min_sev;
+ min_sev_ = _min(dbg_sev_, stream_sev_);
+}
+
+void LogMessage::LogToStream(StreamInterface* stream, int min_sev) {
+ delete stream_;
+ stream_ = stream;
+ stream_sev_ = (stream_ == 0) ? NO_LOGGING : min_sev;
+ min_sev_ = _min(dbg_sev_, stream_sev_);
+}
+
+const char* LogMessage::Describe(LoggingSeverity sev) {
+ switch (sev) {
+ case LS_SENSITIVE: return "Sensitive";
+ case LS_VERBOSE: return "Verbose";
+ case LS_INFO: return "Info";
+ case LS_WARNING: return "Warning";
+ case LS_ERROR: return "Error";
+ default: return "<unknown>";
+ }
+}
+
+const char* LogMessage::DescribeFile(const char* file) {
+ const char* end1 = ::strrchr(file, '/');
+ const char* end2 = ::strrchr(file, '\\');
+ if (!end1 && !end2)
+ return file;
+ else
+ return (end1 > end2) ? end1 + 1 : end2 + 1;
+}
+
+//////////////////////////////////////////////////////////////////////
+// Logging Helpers
+//////////////////////////////////////////////////////////////////////
+
+void LogMultiline(LoggingSeverity level, const char* label, bool input,
+ const char * data, size_t len, bool hex_mode,
+ LogMultilineState* state) {
+ if (!LOG_CHECK_LEVEL_V(level))
+ return;
+
+ const char * direction = (input ? " << " : " >> ");
+ if (hex_mode) {
+ const size_t LINE_SIZE = 24;
+ char hex_line[LINE_SIZE * 9 / 4 + 2], asc_line[LINE_SIZE + 1];
+ while (len > 0) {
+ memset(asc_line, ' ', sizeof(asc_line));
+ memset(hex_line, ' ', sizeof(hex_line));
+ size_t line_len = _min(len, LINE_SIZE);
+ for (size_t i=0; i<line_len; ++i) {
+ unsigned char ch = static_cast<unsigned char>(data[i]);
+ asc_line[i] = isprint(ch) ? data[i] : '.';
+ hex_line[i*2 + i/4] = hex_encode(ch >> 4);
+ hex_line[i*2 + i/4 + 1] = hex_encode(ch & 0xf);
+ }
+ asc_line[sizeof(asc_line)-1] = 0;
+ hex_line[sizeof(hex_line)-1] = 0;
+ LOG_V(level) << label << direction
+ << asc_line << " " << hex_line << " ";
+ data += line_len;
+ len -= line_len;
+ }
+ return;
+ }
+
+ size_t consecutive_unprintable = state ? state->unprintable_count_ : 0;
+
+ std::string str(data, len);
+ while (!str.empty()) {
+ size_t line_end_length = 0;
+ std::string::size_type pos = str.find('\n');
+ std::string substr = str;
+ if (pos == std::string::npos) {
+ substr = str;
+ str.clear();
+ } else if ((pos > 0) && (str[pos-1] == '\r')) {
+ line_end_length = 2;
+ substr = str.substr(0, pos - 1);
+ str = str.substr(pos + 1);
+ } else {
+ line_end_length = 1;
+ substr = str.substr(0, pos);
+ str = str.substr(pos + 1);
+ }
+
+ // Any lines which consist entirely of ascii characters are printed. Other
+ // lines are considered binary, and we just count the number of bytes.
+ // This algorithm should be very compatible with HTTP transfers of binary
+ // data.
+ bool is_ascii = true, is_whitespace = true;
+ for (size_t i=0; i<substr.size(); ++i) {
+ unsigned char ch = static_cast<unsigned char>(substr[i]);
+ if (!isprint(ch)) {
+ is_ascii = false;
+ break;
+ } else if (!isspace(static_cast<unsigned char>(ch))) {
+ is_whitespace = false;
+ }
+ }
+ // Treat an empty line following binary data as binary.
+ if (is_whitespace && consecutive_unprintable) {
+ is_ascii = false;
+ }
+ if (!is_ascii) {
+ consecutive_unprintable += substr.size() + line_end_length;
+ }
+ if (consecutive_unprintable && (is_ascii || str.empty())) {
+ LOG_V(level) << label << direction << "## " << consecutive_unprintable
+ << " consecutive unprintable ##";
+ }
+ if (is_ascii) {
+ consecutive_unprintable = 0;
+ } else {
+ continue;
+ }
+
+ // Filter out any private data
+ std::string::size_type pos_private = substr.find("Email");
+ if (pos_private == std::string::npos) {
+ pos_private = substr.find("Passwd");
+ }
+ if (pos_private == std::string::npos) {
+ LOG_V(level) << label << direction << substr;
+ } else {
+ LOG_V(level) << label << direction << "## omitted for privacy ##";
+ }
+ }
+
+ if (state) {
+ state->unprintable_count_ = consecutive_unprintable;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////
+
+} // namespace talk_base
diff --git a/Plugins/jingle/libjingle/talk/base/logging.h b/Plugins/jingle/libjingle/talk/base/logging.h
new file mode 100644
index 0000000..a4686b7
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/logging.h
@@ -0,0 +1,296 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+// LOG(...) an ostream target that can be used to send formatted
+// output to a variety of logging targets, such as debugger console, stderr,
+// file, or any StreamInterface.
+// The severity level passed as the first argument to the the LOGging
+// functions is used as a filter, to limit the verbosity of the logging.
+// Static members of LogMessage documented below are used to control the
+// verbosity and target of the output.
+// There are several variations on the LOG macro which facilitate logging
+// of common error conditions, detailed below.
+
+#ifndef TALK_BASE_LOGGING_H__
+#define TALK_BASE_LOGGING_H__
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <sstream>
+#include "talk/base/basictypes.h"
+
+namespace talk_base {
+
+class StreamInterface;
+
+///////////////////////////////////////////////////////////////////////////////
+// ConstantLabel can be used to easily generate string names from constant
+// values. This can be useful for logging descriptive names of error messages.
+// Usage:
+// const ConstantLabel LIBRARY_ERRORS[] = {
+// KLABEL(SOME_ERROR),
+// KLABEL(SOME_OTHER_ERROR),
+// ...
+// LASTLABEL
+// }
+//
+// int err = LibraryFunc();
+// LOG(LS_ERROR) << "LibraryFunc returned: "
+// << ErrorName(err, LIBRARY_ERRORS);
+
+struct ConstantLabel { int value; const char * label; };
+#define KLABEL(x) { x, #x }
+#define TLABEL(x,y) { x, y }
+#define LASTLABEL { 0, 0 }
+
+const char * FindLabel(int value, const ConstantLabel entries[]);
+std::string ErrorName(int err, const ConstantLabel* err_table);
+
+//////////////////////////////////////////////////////////////////////
+
+// Note that the non-standard LoggingSeverity aliases exist because they are
+// still in broad use. The meanings of the levels are:
+// LS_SENSITIVE: Information which should only be logged with the consent
+// of the user, due to privacy concerns.
+// LS_VERBOSE: This level is for data which we do not want to appear in the
+// normal debug log, but should appear in diagnostic logs.
+// LS_INFO: Chatty level used in debugging for all sorts of things, the default
+// in debug builds.
+// LS_WARNING: Something that may warrant investigation.
+// LS_ERROR: Something that should not have occurred.
+enum LoggingSeverity { LS_SENSITIVE, LS_VERBOSE, LS_INFO, LS_WARNING, LS_ERROR,
+ INFO = LS_INFO,
+ WARNING = LS_WARNING,
+ LERROR = LS_ERROR };
+
+// LogErrorContext assists in interpreting the meaning of an error value.
+// ERRCTX_ERRNO: the value was read from global 'errno'
+// ERRCTX_HRESULT: the value is a Windows HRESULT
+enum LogErrorContext { ERRCTX_NONE, ERRCTX_ERRNO, ERRCTX_HRESULT };
+
+class LogMessage {
+ public:
+ LogMessage(const char* file, int line, LoggingSeverity sev,
+ LogErrorContext err_ctx = ERRCTX_NONE, int err = 0,
+ const char* module = NULL);
+ ~LogMessage();
+
+ static inline bool Loggable(LoggingSeverity sev) { return (sev >= min_sev_); }
+ std::ostream& stream() { return print_stream_; }
+
+ enum { NO_LOGGING = LS_ERROR + 1 };
+
+ // These are attributes which apply to all logging channels
+ // LogContext: Display the file and line number of the message
+ static void LogContext(int min_sev);
+ // LogThreads: Display the thread identifier of the current thread
+ static void LogThreads(bool on = true);
+ // LogTimestamps: Display the elapsed time of the program
+ static void LogTimestamps(bool on = true);
+
+ // Timestamps begin with program execution, but can be reset with this
+ // function for measuring the duration of an activity, or to synchronize
+ // timestamps between multiple instances.
+ static void ResetTimestamps();
+
+ // These are the available logging channels
+ // Debug: Debug console on Windows, otherwise stderr
+ static void LogToDebug(int min_sev);
+ static int GetLogToDebug() { return dbg_sev_; }
+ // Stream: Any non-blocking stream interface. LogMessage takes ownership of
+ // the stream.
+ static void LogToStream(StreamInterface* stream, int min_sev);
+ static int GetLogToStream() { return stream_sev_; }
+
+ // Testing against MinLogSeverity allows code to avoid potentially expensive
+ // logging operations by pre-checking the logging level.
+ static int GetMinLogSeverity() { return min_sev_; }
+
+ static void SetDiagnosticMode(bool f) { is_diagnostic_mode_ = f; }
+ static bool IsDiagnosticMode() { return is_diagnostic_mode_; }
+
+ private:
+ // These assist in formatting some parts of the debug output.
+ static const char* Describe(LoggingSeverity sev);
+ static const char* DescribeFile(const char* file);
+
+ // The ostream that buffers the formatted message before output
+ std::ostringstream print_stream_;
+
+ // The severity level of this message
+ LoggingSeverity severity_;
+
+ // String data generated in the constructor, that should be appended to
+ // the message before output.
+ std::string extra_;
+
+ // dbg_sev_ and stream_sev_ are the thresholds for those output targets
+ // min_sev_ is the minimum (most verbose) of those levels, and is used
+ // as a short-circuit in the logging macros to identify messages that won't
+ // be logged.
+ // ctx_sev_ is the minimum level at which file context is displayed
+ static int min_sev_, dbg_sev_, stream_sev_, ctx_sev_;
+
+ // The output stream, if any
+ static StreamInterface * stream_;
+
+ // Flags for formatting options
+ static bool thread_, timestamp_;
+
+ // The timestamp at which logging started.
+ static uint32 start_;
+
+ // are we in diagnostic mode (as defined by the app)?
+ static bool is_diagnostic_mode_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(LogMessage);
+};
+
+//////////////////////////////////////////////////////////////////////
+// Logging Helpers
+//////////////////////////////////////////////////////////////////////
+
+class LogMultilineState {
+public:
+ size_t unprintable_count_;
+ LogMultilineState() : unprintable_count_(0) { }
+};
+
+// When possible, pass optional state variable to track various data across
+// multiple calls to LogMultiline. Otherwise, pass NULL.
+void LogMultiline(LoggingSeverity level, const char* label, bool input,
+ const char * data, size_t len, bool hex_mode,
+ LogMultilineState* state);
+
+//////////////////////////////////////////////////////////////////////
+// Macros which automatically disable logging when LOGGING == 0
+//////////////////////////////////////////////////////////////////////
+
+// If LOGGING is not explicitly defined, default to enabled in debug mode
+#if !defined(LOGGING)
+#if defined(_DEBUG) && !defined(NDEBUG)
+#define LOGGING 1
+#else
+#define LOGGING 0
+#endif
+#endif // !defined(LOGGING)
+
+#ifndef LOG
+#if LOGGING
+
+#define LOG(sev) \
+ if (talk_base::LogMessage::Loggable(talk_base::sev)) \
+ talk_base::LogMessage(__FILE__, __LINE__, talk_base::sev).stream()
+
+// The _V version is for when a variable is passed in. It doesn't do the
+// namespace concatination.
+#define LOG_V(sev) \
+ if (talk_base::LogMessage::Loggable(sev)) \
+ talk_base::LogMessage(__FILE__, __LINE__, sev).stream()
+
+// The _F version prefixes the message with the current function name.
+#define LOG_F(sev) LOG(sev) << __FUNCTION__ << ": "
+
+// LOG_CHECK_LEVEL can be used as a test before performing expensive or
+// sensitive operations whose sole purpose is to output logging data at the
+// desired level.
+#define LOG_CHECK_LEVEL(sev) \
+ talk_base::LogCheckLevel(talk_base::sev)
+#define LOG_CHECK_LEVEL_V(sev) \
+ talk_base::LogCheckLevel(sev)
+inline bool LogCheckLevel(LoggingSeverity sev) {
+ return (LogMessage::GetMinLogSeverity() <= sev);
+}
+
+// PLOG and LOG_ERR attempt to provide a string description of an errno derived
+// error. LOG_ERR reads errno directly, so care must be taken to call it before
+// errno is reset.
+#define PLOG(sev, err) \
+ if (talk_base::LogMessage::Loggable(talk_base::sev)) \
+ talk_base::LogMessage(__FILE__, __LINE__, talk_base::sev, \
+ talk_base::ERRCTX_ERRNO, err).stream()
+#define LOG_ERR(sev) \
+ if (talk_base::LogMessage::Loggable(sev)) \
+ talk_base::LogMessage(__FILE__, __LINE__, talk_base::sev, \
+ talk_base::ERRCTX_ERRNO, errno).stream()
+
+// LOG_GLE(M) attempt to provide a string description of the HRESULT returned
+// by GetLastError. The second variant allows searching of a dll's string
+// table for the error description.
+#ifdef WIN32
+#define LOG_GLE(sev) \
+ if (talk_base::LogMessage::Loggable(talk_base::sev)) \
+ talk_base::LogMessage(__FILE__, __LINE__, talk_base::sev, \
+ talk_base::ERRCTX_HRESULT, GetLastError()).stream()
+#define LOG_GLEM(sev, mod) \
+ if (talk_base::LogMessage::Loggable(talk_base::sev)) \
+ talk_base::LogMessage(__FILE__, __LINE__, talk_base::sev, \
+ talk_base::ERRCTX_HRESULT, GetLastError(), mod) \
+ .stream()
+#endif // WIN32
+
+// TODO: Add an "assert" wrapper that logs in the same manner.
+
+#else // !LOGGING
+
+// Hopefully, the compiler will optimize away some of this code.
+// Note: syntax of "1 ? (void)0 : LogMessage" was causing errors in g++,
+// converted to "while (false)"
+#define LOG(sev) \
+ while (false)talk_base:: LogMessage(NULL, 0, talk_base::sev).stream()
+#define LOG_V(sev) \
+ while (false) talk_base::LogMessage(NULL, 0, sev).stream()
+#define LOG_F(sev) LOG(sev) << __FUNCTION__ << ": "
+#define LOG_CHECK_LEVEL(sev) \
+ false
+#define LOG_CHECK_LEVEL_V(sev) \
+ false
+#define PLOG(sev, err) \
+ while (false) talk_base::LogMessage(NULL, 0, talk_base::sev, \
+ talk_base::ERRCTX_ERRNO, 0).stream()
+#define LOG_ERR(sev) \
+ while (false) talk_base::LogMessage(NULL, 0, talk_base::sev, \
+ talk_base::ERRCTX_ERRNO, 0).stream()
+#ifdef WIN32
+#define LOG_GLE(sev) \
+ while (false) talk_base::LogMessage(NULL, 0, talk_base::sev, \
+ talk_base::ERRCTX_HRESULT, 0).stream()
+#define LOG_GLEM(sev, mod) \
+ while (false) talk_base::LogMessage(NULL, 0, talk_base::sev, \
+ talk_base::ERRCTX_HRESULT, 0).stream()
+#endif // WIN32
+
+#endif // !LOGGING
+#endif // LOG
+
+//////////////////////////////////////////////////////////////////////
+
+} // talk_base
+
+#endif // TALK_BASE_LOGGING_H__
diff --git a/Plugins/jingle/libjingle/talk/base/md5.h b/Plugins/jingle/libjingle/talk/base/md5.h
new file mode 100644
index 0000000..ec458d1
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/md5.h
@@ -0,0 +1,45 @@
+/*
+ * This is the header file for the MD5 message-digest algorithm.
+ * The algorithm is due to Ron Rivest. This code was
+ * written by Colin Plumb in 1993, no copyright is claimed.
+ * This code is in the public domain; do with it what you wish.
+ *
+ * Equivalent code is available from RSA Data Security, Inc.
+ * This code has been tested against that, and is equivalent,
+ * except that you don't need to include two pages of legalese
+ * with every copy.
+ * To compute the message digest of a chunk of bytes, declare an
+ * MD5Context structure, pass it to MD5Init, call MD5Update as
+ * needed on buffers full of bytes, and then call MD5Final, which
+ * will fill a supplied 16-byte array with the digest.
+ *
+ */
+
+#ifndef TALK_BASE_MD5_H__
+#define TALK_BASE_MD5_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef long unsigned int uint32;
+typedef struct MD5Context MD5_CTX;
+
+#define md5byte unsigned char
+
+struct MD5Context {
+ uint32 buf[4];
+ uint32 bits[2];
+ uint32 in[16];
+};
+
+void MD5Init(struct MD5Context *context);
+void MD5Update(struct MD5Context *context, md5byte const *buf, unsigned len);
+void MD5Final(unsigned char digest[16], struct MD5Context *context);
+void MD5Transform(uint32 buf[4], uint32 const in[16]);
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif // TALK_BASE_MD5_H__
diff --git a/Plugins/jingle/libjingle/talk/base/md5c.c b/Plugins/jingle/libjingle/talk/base/md5c.c
new file mode 100644
index 0000000..eb2c034
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/md5c.c
@@ -0,0 +1,256 @@
+/*
+ * This code implements the MD5 message-digest algorithm.
+ * The algorithm is due to Ron Rivest. This code was
+ * written by Colin Plumb in 1993, no copyright is claimed.
+ * This code is in the public domain; do with it what you wish.
+ *
+ * Equivalent code is available from RSA Data Security, Inc.
+ * This code has been tested against that, and is equivalent,
+ * except that you don't need to include two pages of legalese
+ * with every copy.
+ *
+ * To compute the message digest of a chunk of bytes, declare an
+ * MD5Context structure, pass it to MD5Init, call MD5Update as
+ * needed on buffers full of bytes, and then call MD5Final, which
+ * will fill a supplied 16-byte array with the digest.
+ */
+#include <string.h> /* for memcpy() */
+#include "md5.h"
+
+#ifndef HIGHFIRST
+#define byteReverse(buf, len) /* Nothing */
+#else
+void byteReverse(unsigned char *buf, unsigned longs);
+
+#ifndef ASM_MD5
+/*
+ * Note: this code is harmless on little-endian machines.
+ */
+void byteReverse(unsigned char *buf, unsigned longs)
+{
+ uint32 t;
+ do {
+ t = (uint32)((unsigned)buf[3]<<8 | buf[2]) << 16 |
+ ((unsigned)buf[1]<<8 | buf[0]);
+ *(uint32 *)buf = t;
+ buf += 4;
+ } while (--longs);
+}
+#endif
+#endif
+
+/*
+ * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious
+ * initialization constants.
+ */
+void
+MD5Init(struct MD5Context *ctx)
+{
+ ctx->buf[0] = 0x67452301;
+ ctx->buf[1] = 0xefcdab89;
+ ctx->buf[2] = 0x98badcfe;
+ ctx->buf[3] = 0x10325476;
+
+ ctx->bits[0] = 0;
+ ctx->bits[1] = 0;
+}
+
+/*
+ * Update context to reflect the concatenation of another buffer full
+ * of bytes.
+ */
+void
+MD5Update(struct MD5Context *ctx, unsigned char const *buf, unsigned len)
+{
+ uint32 t;
+
+ /* Update bitcount */
+
+ t = ctx->bits[0];
+ if ((ctx->bits[0] = t + ((uint32)len << 3)) < t)
+ ctx->bits[1]++; /* Carry from low to high */
+ ctx->bits[1] += len >> 29;
+
+ t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */
+
+ /* Handle any leading odd-sized chunks */
+
+ if ( t ) {
+ unsigned char *p = (unsigned char *)ctx->in + t;
+
+ t = 64-t;
+ if (len < t) {
+ memcpy(p, buf, len);
+ return;
+ }
+ memcpy(p, buf, t);
+ byteReverse(ctx->in, 16);
+ MD5Transform(ctx->buf, (uint32 *)ctx->in);
+ buf += t;
+ len -= t;
+ }
+
+ /* Process data in 64-byte chunks */
+
+ while (len >= 64) {
+ memcpy(ctx->in, buf, 64);
+ byteReverse(ctx->in, 16);
+ MD5Transform(ctx->buf, (uint32 *)ctx->in);
+ buf += 64;
+ len -= 64;
+ }
+
+ /* Handle any remaining bytes of data. */
+
+ memcpy(ctx->in, buf, len);
+}
+
+/*
+ * Final wrapup - pad to 64-byte boundary with the bit pattern
+ * 1 0* (64-bit count of bits processed, MSB-first)
+ */
+void
+MD5Final(unsigned char digest[16], struct MD5Context *ctx)
+{
+ unsigned count;
+ unsigned char *p;
+
+ /* Compute number of bytes mod 64 */
+ count = (ctx->bits[0] >> 3) & 0x3F;
+
+ /* Set the first char of padding to 0x80. This is safe since there is
+ always at least one byte free */
+ p = (unsigned char*)(ctx->in) + count;
+ *p++ = 0x80;
+
+ /* Bytes of padding needed to make 64 bytes */
+ count = 64 - 1 - count;
+
+ /* Pad out to 56 mod 64 */
+ if (count < 8) {
+ /* Two lots of padding: Pad the first block to 64 bytes */
+ memset(p, 0, count);
+ byteReverse(ctx->in, 16);
+ MD5Transform(ctx->buf, (uint32 *)ctx->in);
+
+ /* Now fill the next block with 56 bytes */
+ memset(ctx->in, 0, 56);
+ } else {
+ /* Pad block to 56 bytes */
+ memset(p, 0, count-8);
+ }
+ byteReverse(ctx->in, 14);
+
+ /* Append length in bits and transform */
+ ((uint32 *)ctx->in)[ 14 ] = ctx->bits[0];
+ ((uint32 *)ctx->in)[ 15 ] = ctx->bits[1];
+
+ MD5Transform(ctx->buf, (uint32 *)ctx->in);
+ byteReverse((unsigned char *)ctx->buf, 4);
+ memcpy(digest, ctx->buf, 16);
+ memset(ctx, 0, sizeof(ctx)); /* In case it's sensitive */
+}
+
+#ifndef ASM_MD5
+
+/* The four core functions - F1 is optimized somewhat */
+
+/* #define F1(x, y, z) (x & y | ~x & z) */
+#define F1(x, y, z) (z ^ (x & (y ^ z)))
+#define F2(x, y, z) F1(z, x, y)
+#define F3(x, y, z) (x ^ y ^ z)
+#define F4(x, y, z) (y ^ (x | ~z))
+
+/* This is the central step in the MD5 algorithm. */
+#define MD5STEP(f, w, x, y, z, data, s) \
+ ( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x )
+
+/*
+ * The core of the MD5 algorithm, this alters an existing MD5 hash to
+ * reflect the addition of 16 longwords of new data. MD5Update blocks
+ * the data and converts bytes into longwords for this routine.
+ */
+void
+MD5Transform(uint32 buf[4], uint32 const in[16])
+{
+ register uint32 a, b, c, d;
+
+ a = buf[0];
+ b = buf[1];
+ c = buf[2];
+ d = buf[3];
+
+ MD5STEP(F1, a, b, c, d, in[ 0]+0xd76aa478, 7);
+ MD5STEP(F1, d, a, b, c, in[ 1]+0xe8c7b756, 12);
+ MD5STEP(F1, c, d, a, b, in[ 2]+0x242070db, 17);
+ MD5STEP(F1, b, c, d, a, in[ 3]+0xc1bdceee, 22);
+ MD5STEP(F1, a, b, c, d, in[ 4]+0xf57c0faf, 7);
+ MD5STEP(F1, d, a, b, c, in[ 5]+0x4787c62a, 12);
+ MD5STEP(F1, c, d, a, b, in[ 6]+0xa8304613, 17);
+ MD5STEP(F1, b, c, d, a, in[ 7]+0xfd469501, 22);
+ MD5STEP(F1, a, b, c, d, in[ 8]+0x698098d8, 7);
+ MD5STEP(F1, d, a, b, c, in[ 9]+0x8b44f7af, 12);
+ MD5STEP(F1, c, d, a, b, in[10]+0xffff5bb1, 17);
+ MD5STEP(F1, b, c, d, a, in[11]+0x895cd7be, 22);
+ MD5STEP(F1, a, b, c, d, in[12]+0x6b901122, 7);
+ MD5STEP(F1, d, a, b, c, in[13]+0xfd987193, 12);
+ MD5STEP(F1, c, d, a, b, in[14]+0xa679438e, 17);
+ MD5STEP(F1, b, c, d, a, in[15]+0x49b40821, 22);
+
+ MD5STEP(F2, a, b, c, d, in[ 1]+0xf61e2562, 5);
+ MD5STEP(F2, d, a, b, c, in[ 6]+0xc040b340, 9);
+ MD5STEP(F2, c, d, a, b, in[11]+0x265e5a51, 14);
+ MD5STEP(F2, b, c, d, a, in[ 0]+0xe9b6c7aa, 20);
+ MD5STEP(F2, a, b, c, d, in[ 5]+0xd62f105d, 5);
+ MD5STEP(F2, d, a, b, c, in[10]+0x02441453, 9);
+ MD5STEP(F2, c, d, a, b, in[15]+0xd8a1e681, 14);
+ MD5STEP(F2, b, c, d, a, in[ 4]+0xe7d3fbc8, 20);
+ MD5STEP(F2, a, b, c, d, in[ 9]+0x21e1cde6, 5);
+ MD5STEP(F2, d, a, b, c, in[14]+0xc33707d6, 9);
+ MD5STEP(F2, c, d, a, b, in[ 3]+0xf4d50d87, 14);
+ MD5STEP(F2, b, c, d, a, in[ 8]+0x455a14ed, 20);
+ MD5STEP(F2, a, b, c, d, in[13]+0xa9e3e905, 5);
+ MD5STEP(F2, d, a, b, c, in[ 2]+0xfcefa3f8, 9);
+ MD5STEP(F2, c, d, a, b, in[ 7]+0x676f02d9, 14);
+ MD5STEP(F2, b, c, d, a, in[12]+0x8d2a4c8a, 20);
+
+ MD5STEP(F3, a, b, c, d, in[ 5]+0xfffa3942, 4);
+ MD5STEP(F3, d, a, b, c, in[ 8]+0x8771f681, 11);
+ MD5STEP(F3, c, d, a, b, in[11]+0x6d9d6122, 16);
+ MD5STEP(F3, b, c, d, a, in[14]+0xfde5380c, 23);
+ MD5STEP(F3, a, b, c, d, in[ 1]+0xa4beea44, 4);
+ MD5STEP(F3, d, a, b, c, in[ 4]+0x4bdecfa9, 11);
+ MD5STEP(F3, c, d, a, b, in[ 7]+0xf6bb4b60, 16);
+ MD5STEP(F3, b, c, d, a, in[10]+0xbebfbc70, 23);
+ MD5STEP(F3, a, b, c, d, in[13]+0x289b7ec6, 4);
+ MD5STEP(F3, d, a, b, c, in[ 0]+0xeaa127fa, 11);
+ MD5STEP(F3, c, d, a, b, in[ 3]+0xd4ef3085, 16);
+ MD5STEP(F3, b, c, d, a, in[ 6]+0x04881d05, 23);
+ MD5STEP(F3, a, b, c, d, in[ 9]+0xd9d4d039, 4);
+ MD5STEP(F3, d, a, b, c, in[12]+0xe6db99e5, 11);
+ MD5STEP(F3, c, d, a, b, in[15]+0x1fa27cf8, 16);
+ MD5STEP(F3, b, c, d, a, in[ 2]+0xc4ac5665, 23);
+
+ MD5STEP(F4, a, b, c, d, in[ 0]+0xf4292244, 6);
+ MD5STEP(F4, d, a, b, c, in[ 7]+0x432aff97, 10);
+ MD5STEP(F4, c, d, a, b, in[14]+0xab9423a7, 15);
+ MD5STEP(F4, b, c, d, a, in[ 5]+0xfc93a039, 21);
+ MD5STEP(F4, a, b, c, d, in[12]+0x655b59c3, 6);
+ MD5STEP(F4, d, a, b, c, in[ 3]+0x8f0ccc92, 10);
+ MD5STEP(F4, c, d, a, b, in[10]+0xffeff47d, 15);
+ MD5STEP(F4, b, c, d, a, in[ 1]+0x85845dd1, 21);
+ MD5STEP(F4, a, b, c, d, in[ 8]+0x6fa87e4f, 6);
+ MD5STEP(F4, d, a, b, c, in[15]+0xfe2ce6e0, 10);
+ MD5STEP(F4, c, d, a, b, in[ 6]+0xa3014314, 15);
+ MD5STEP(F4, b, c, d, a, in[13]+0x4e0811a1, 21);
+ MD5STEP(F4, a, b, c, d, in[ 4]+0xf7537e82, 6);
+ MD5STEP(F4, d, a, b, c, in[11]+0xbd3af235, 10);
+ MD5STEP(F4, c, d, a, b, in[ 2]+0x2ad7d2bb, 15);
+ MD5STEP(F4, b, c, d, a, in[ 9]+0xeb86d391, 21);
+
+ buf[0] += a;
+ buf[1] += b;
+ buf[2] += c;
+ buf[3] += d;
+}
+#endif
diff --git a/Plugins/jingle/libjingle/talk/base/messagequeue.cc b/Plugins/jingle/libjingle/talk/base/messagequeue.cc
new file mode 100644
index 0000000..64f57cc
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/messagequeue.cc
@@ -0,0 +1,360 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#if defined(_MSC_VER) && _MSC_VER < 1300
+#pragma warning(disable:4786)
+#endif
+
+#ifdef POSIX
+extern "C" {
+#include <sys/time.h>
+}
+#endif
+
+#include "talk/base/common.h"
+#include "talk/base/logging.h"
+#include "talk/base/messagequeue.h"
+#include "talk/base/physicalsocketserver.h"
+
+
+namespace talk_base {
+
+const uint32 kMaxMsgLatency = 150; // 150 ms
+
+//------------------------------------------------------------------
+// MessageQueueManager
+
+MessageQueueManager* MessageQueueManager::instance_;
+
+MessageQueueManager* MessageQueueManager::Instance() {
+ // Note: This is not thread safe, but it is first called before threads are
+ // spawned.
+ if (!instance_)
+ instance_ = new MessageQueueManager;
+ return instance_;
+}
+
+MessageQueueManager::MessageQueueManager() {
+}
+
+MessageQueueManager::~MessageQueueManager() {
+}
+
+void MessageQueueManager::Add(MessageQueue *message_queue) {
+ // MessageQueueManager methods should be non-reentrant, so we
+ // ASSERT that is the case.
+ ASSERT(!crit_.CurrentThreadIsOwner());
+ CritScope cs(&crit_);
+ message_queues_.push_back(message_queue);
+}
+
+void MessageQueueManager::Remove(MessageQueue *message_queue) {
+ ASSERT(!crit_.CurrentThreadIsOwner()); // See note above.
+ CritScope cs(&crit_);
+ std::vector<MessageQueue *>::iterator iter;
+ iter = std::find(message_queues_.begin(), message_queues_.end(), message_queue);
+ if (iter != message_queues_.end())
+ message_queues_.erase(iter);
+}
+
+void MessageQueueManager::Clear(MessageHandler *handler) {
+ ASSERT(!crit_.CurrentThreadIsOwner()); // See note above.
+ CritScope cs(&crit_);
+ std::vector<MessageQueue *>::iterator iter;
+ for (iter = message_queues_.begin(); iter != message_queues_.end(); iter++)
+ (*iter)->Clear(handler);
+}
+
+//------------------------------------------------------------------
+// MessageQueue
+
+MessageQueue::MessageQueue(SocketServer* ss)
+ : ss_(ss), new_ss(false), fStop_(false), fPeekKeep_(false), active_(false) {
+ if (!ss_) {
+ new_ss = true;
+ ss_ = new PhysicalSocketServer();
+ }
+}
+
+MessageQueue::~MessageQueue() {
+ if (active_) {
+ MessageQueueManager::Instance()->Remove(this);
+ Clear(NULL);
+ }
+ if (new_ss)
+ delete ss_;
+}
+
+void MessageQueue::set_socketserver(SocketServer* ss) {
+ if (new_ss)
+ delete ss_;
+ new_ss = false;
+ ss_ = ss;
+}
+
+void MessageQueue::Stop() {
+ fStop_ = true;
+ ss_->WakeUp();
+}
+
+bool MessageQueue::IsStopping() {
+ return fStop_;
+}
+
+void MessageQueue::Restart() {
+ fStop_ = false;
+}
+
+bool MessageQueue::Peek(Message *pmsg, int cmsWait) {
+ if (fPeekKeep_) {
+ *pmsg = msgPeek_;
+ return true;
+ }
+ if (!Get(pmsg, cmsWait))
+ return false;
+ msgPeek_ = *pmsg;
+ fPeekKeep_ = true;
+ return true;
+}
+
+bool MessageQueue::Get(Message *pmsg, int cmsWait) {
+ // Return and clear peek if present
+ // Always return the peek if it exists so there is Peek/Get symmetry
+
+ if (fPeekKeep_) {
+ *pmsg = msgPeek_;
+ fPeekKeep_ = false;
+ return true;
+ }
+
+ // Get w/wait + timer scan / dispatch + socket / event multiplexer dispatch
+
+ int cmsTotal = cmsWait;
+ int cmsElapsed = 0;
+ uint32 msStart = Time();
+ uint32 msCurrent = msStart;
+ while (true) {
+ // Check for sent messages
+
+ ReceiveSends();
+
+ // Check queues
+
+ int cmsDelayNext = kForever;
+ {
+ CritScope cs(&crit_);
+
+ // Check for delayed messages that have been triggered
+ // Calc the next trigger too
+
+ while (!dmsgq_.empty()) {
+ if (msCurrent < dmsgq_.top().msTrigger_) {
+ cmsDelayNext = dmsgq_.top().msTrigger_ - msCurrent;
+ break;
+ }
+ msgq_.push(dmsgq_.top().msg_);
+ dmsgq_.pop();
+ }
+
+ // Check for posted events
+
+ while (!msgq_.empty()) {
+ *pmsg = msgq_.front();
+ if (pmsg->ts_sensitive) {
+ long delay = TimeDiff(msCurrent, pmsg->ts_sensitive);
+ if (delay > 0) {
+ LOG_F(LS_WARNING) << "id: " << pmsg->message_id << " delay: "
+ << (delay + kMaxMsgLatency) << "ms";
+ }
+ }
+ msgq_.pop();
+ if (MQID_DISPOSE == pmsg->message_id) {
+ ASSERT(NULL == pmsg->phandler);
+ delete pmsg->pdata;
+ continue;
+ }
+ return true;
+ }
+ }
+
+ if (fStop_)
+ break;
+
+ // Which is shorter, the delay wait or the asked wait?
+
+ int cmsNext;
+ if (cmsWait == kForever) {
+ cmsNext = cmsDelayNext;
+ } else {
+ cmsNext = cmsTotal - cmsElapsed;
+ if (cmsNext < 0)
+ cmsNext = 0;
+ if ((cmsDelayNext != kForever) && (cmsDelayNext < cmsNext))
+ cmsNext = cmsDelayNext;
+ }
+
+ // Wait and multiplex in the meantime
+ ss_->Wait(cmsNext, true);
+
+ // If the specified timeout expired, return
+
+ msCurrent = Time();
+ cmsElapsed = msCurrent - msStart;
+ if (cmsWait != kForever) {
+ if (cmsElapsed >= cmsWait)
+ return false;
+ }
+ }
+ return false;
+}
+
+void MessageQueue::ReceiveSends() {
+}
+
+void MessageQueue::Post(MessageHandler *phandler, uint32 id,
+ MessageData *pdata, bool time_sensitive) {
+ if (fStop_)
+ return;
+
+ // Keep thread safe
+ // Add the message to the end of the queue
+ // Signal for the multiplexer to return
+
+ CritScope cs(&crit_);
+ EnsureActive();
+ Message msg;
+ msg.phandler = phandler;
+ msg.message_id = id;
+ msg.pdata = pdata;
+ if (time_sensitive) {
+ msg.ts_sensitive = Time() + kMaxMsgLatency;
+ }
+ msgq_.push(msg);
+ ss_->WakeUp();
+}
+
+void MessageQueue::PostDelayed(int cmsDelay, MessageHandler *phandler,
+ uint32 id, MessageData *pdata) {
+ if (fStop_)
+ return;
+
+ // Keep thread safe
+ // Add to the priority queue. Gets sorted soonest first.
+ // Signal for the multiplexer to return.
+
+ CritScope cs(&crit_);
+ EnsureActive();
+ Message msg;
+ msg.phandler = phandler;
+ msg.message_id = id;
+ msg.pdata = pdata;
+ dmsgq_.push(DelayedMessage(cmsDelay, &msg));
+ ss_->WakeUp();
+}
+
+int MessageQueue::GetDelay() {
+ CritScope cs(&crit_);
+
+ if (!msgq_.empty())
+ return 0;
+
+ if (!dmsgq_.empty()) {
+ int delay = dmsgq_.top().msTrigger_ - Time();
+ if (delay < 0)
+ delay = 0;
+ return delay;
+ }
+
+ return kForever;
+}
+
+void MessageQueue::Clear(MessageHandler *phandler, uint32 id) {
+ CritScope cs(&crit_);
+
+ // Remove messages with phandler
+
+ if (fPeekKeep_) {
+ if (phandler == NULL || msgPeek_.phandler == phandler) {
+ if (id == MQID_ANY || msgPeek_.message_id == id) {
+ delete msgPeek_.pdata;
+ fPeekKeep_ = false;
+ }
+ }
+ }
+
+ // Remove from ordered message queue
+
+ size_t c = msgq_.size();
+ while (c-- != 0) {
+ Message msg = msgq_.front();
+ msgq_.pop();
+ if (phandler != NULL && msg.phandler != phandler) {
+ msgq_.push(msg);
+ } else {
+ if (id == MQID_ANY || msg.message_id == id) {
+ delete msg.pdata;
+ } else {
+ msgq_.push(msg);
+ }
+ }
+ }
+
+ // Remove from priority queue. Not directly iterable, so use this approach
+
+ std::queue<DelayedMessage> dmsgs;
+ while (!dmsgq_.empty()) {
+ DelayedMessage dmsg = dmsgq_.top();
+ dmsgq_.pop();
+ if (phandler != NULL && dmsg.msg_.phandler != phandler) {
+ dmsgs.push(dmsg);
+ } else {
+ if (id == MQID_ANY || dmsg.msg_.message_id == id) {
+ delete dmsg.msg_.pdata;
+ } else {
+ dmsgs.push(dmsg);
+ }
+ }
+ }
+ while (!dmsgs.empty()) {
+ dmsgq_.push(dmsgs.front());
+ dmsgs.pop();
+ }
+}
+
+void MessageQueue::Dispatch(Message *pmsg) {
+ pmsg->phandler->OnMessage(pmsg);
+}
+
+void MessageQueue::EnsureActive() {
+ ASSERT(crit_.CurrentThreadIsOwner());
+ if (!active_) {
+ active_ = true;
+ MessageQueueManager::Instance()->Add(this);
+ }
+}
+
+} // namespace talk_base
diff --git a/Plugins/jingle/libjingle/talk/base/messagequeue.h b/Plugins/jingle/libjingle/talk/base/messagequeue.h
new file mode 100644
index 0000000..ce79700
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/messagequeue.h
@@ -0,0 +1,207 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TALK_BASE_MESSAGEQUEUE_H__
+#define TALK_BASE_MESSAGEQUEUE_H__
+
+#include <vector>
+#include <queue>
+#include <algorithm>
+#include "talk/base/basictypes.h"
+#include "talk/base/criticalsection.h"
+#include "talk/base/socketserver.h"
+#include "talk/base/time.h"
+
+namespace talk_base {
+
+struct Message;
+class MessageQueue;
+class MessageHandler;
+
+// MessageQueueManager does cleanup of of message queues
+
+class MessageQueueManager {
+public:
+ static MessageQueueManager* Instance();
+
+ void Add(MessageQueue *message_queue);
+ void Remove(MessageQueue *message_queue);
+ void Clear(MessageHandler *handler);
+
+private:
+ MessageQueueManager();
+ ~MessageQueueManager();
+
+ static MessageQueueManager* instance_;
+ // This list contains 'active' MessageQueues.
+ std::vector<MessageQueue *> message_queues_;
+ CriticalSection crit_;
+};
+
+// Messages get dispatched to a MessageHandler
+
+class MessageHandler {
+public:
+ virtual ~MessageHandler() {
+ MessageQueueManager::Instance()->Clear(this);
+ }
+
+ virtual void OnMessage(Message *pmsg) = 0;
+};
+
+// Derive from this for specialized data
+// App manages lifetime, except when messages are purged
+
+class MessageData {
+public:
+ MessageData() {}
+ virtual ~MessageData() {}
+};
+
+template <class T>
+class TypedMessageData : public MessageData {
+public:
+ TypedMessageData(const T& data) : data_(data) { }
+ const T& data() const { return data_; }
+ T& data() { return data_; }
+private:
+ T data_;
+};
+
+template<class T>
+inline MessageData* WrapMessageData(const T& data) {
+ return new TypedMessageData<T>(data);
+}
+
+template<class T>
+inline const T& UseMessageData(MessageData* data) {
+ return static_cast< TypedMessageData<T>* >(data)->data();
+}
+
+template<class T>
+class DisposeData : public MessageData {
+public:
+ DisposeData(T* data) : data_(data) { }
+ virtual ~DisposeData() { delete data_; }
+private:
+ T* data_;
+};
+
+const uint32 MQID_ANY = static_cast<uint32>(-1);
+const uint32 MQID_DISPOSE = static_cast<uint32>(-2);
+
+// No destructor
+
+struct Message {
+ Message() {
+ memset(this, 0, sizeof(*this));
+ }
+ MessageHandler *phandler;
+ uint32 message_id;
+ MessageData *pdata;
+ uint32 ts_sensitive;
+};
+
+// DelayedMessage goes into a priority queue, sorted by trigger time
+
+class DelayedMessage {
+public:
+ DelayedMessage(int cmsDelay, Message *pmsg) {
+ cmsDelay_ = cmsDelay;
+ msTrigger_ = GetMillisecondCount() + cmsDelay;
+ msg_ = *pmsg;
+ }
+
+ bool operator< (const DelayedMessage& dmsg) const {
+ return dmsg.msTrigger_ < msTrigger_;
+ }
+
+ int cmsDelay_; // for debugging
+ uint32 msTrigger_;
+ Message msg_;
+};
+
+class MessageQueue {
+public:
+ MessageQueue(SocketServer* ss = 0);
+ virtual ~MessageQueue();
+
+ SocketServer* socketserver() { return ss_; }
+ void set_socketserver(SocketServer* ss);
+
+ // Note: The behavior of MessageQueue has changed. When a MQ is stopped,
+ // futher Posts and Sends will fail. However, any pending Sends and *ready*
+ // Posts (as opposed to unexpired delayed Posts) will be delivered before
+ // Get (or Peek) returns false. By guaranteeing delivery of those messages,
+ // we eliminate the race condition when an MessageHandler and MessageQueue
+ // may be destroyed independently of each other.
+
+ virtual void Stop();
+ virtual bool IsStopping();
+ virtual void Restart();
+
+ // Get() will process I/O until:
+ // 1) A message is available (returns true)
+ // 2) cmsWait seconds have elapsed (returns false)
+ // 3) Stop() is called (returns false)
+ virtual bool Get(Message *pmsg, int cmsWait = kForever);
+ virtual bool Peek(Message *pmsg, int cmsWait = 0);
+ virtual void Post(MessageHandler *phandler, uint32 id = 0,
+ MessageData *pdata = NULL, bool time_sensitive = false);
+ virtual void PostDelayed(int cmsDelay, MessageHandler *phandler,
+ uint32 id = 0, MessageData *pdata = NULL);
+ virtual void Clear(MessageHandler *phandler, uint32 id = MQID_ANY);
+ virtual void Dispatch(Message *pmsg);
+ virtual void ReceiveSends();
+ virtual int GetDelay();
+
+ // Internally posts a message which causes the doomed object to be deleted
+ template<class T> void Dispose(T* doomed) {
+ if (doomed) {
+ Post(NULL, MQID_DISPOSE, new talk_base::DisposeData<T>(doomed));
+ }
+ }
+
+protected:
+ void EnsureActive();
+
+ SocketServer* ss_;
+ bool new_ss;
+ bool fStop_;
+ bool fPeekKeep_;
+ Message msgPeek_;
+ // A message queue is active if it has ever had a message posted to it.
+ // This also corresponds to being in MessageQueueManager's global list.
+ bool active_;
+ std::queue<Message> msgq_;
+ std::priority_queue<DelayedMessage> dmsgq_;
+ CriticalSection crit_;
+};
+
+} // namespace talk_base
+
+#endif // TALK_BASE_MESSAGEQUEUE_H__
diff --git a/Plugins/jingle/libjingle/talk/base/nat_unittest.cc b/Plugins/jingle/libjingle/talk/base/nat_unittest.cc
new file mode 100644
index 0000000..23c122b
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/nat_unittest.cc
@@ -0,0 +1,223 @@
+#include <string>
+#include <cstring>
+#include <iostream>
+#include <cassert>
+
+#include "talk/base/natserver.h"
+#include "talk/base/testclient.h"
+#include "talk/base/physicalsocketserver.h"
+#include "talk/base/virtualsocketserver.h"
+#include "talk/base/natsocketfactory.h"
+#include "talk/base/host.h"
+
+#ifdef POSIX
+extern "C" {
+#include <errno.h>
+}
+#endif // POSIX
+
+using namespace talk_base;
+
+#define CHECK(arg) Check(arg, #arg)
+
+void Check(int result, const char* desc) {
+ if (result < 0) {
+ std::cerr << desc << ": " << std::strerror(errno) << std::endl;
+ exit(1);
+ }
+}
+
+void CheckTest(bool act_val, bool exp_val, std::string desc) {
+ if (act_val && !exp_val) {
+ std::cerr << "error: " << desc << " was true, expected false" << std::endl;
+ exit(1);
+ } else if (!act_val && exp_val) {
+ std::cerr << "error: " << desc << " was false, expected true" << std::endl;
+ exit(1);
+ }
+}
+
+void CheckReceive(
+ TestClient* client, bool should_receive, const char* buf, size_t size) {
+ if (should_receive)
+ client->CheckNextPacket(buf, size, 0);
+ else
+ client->CheckNoPacket();
+}
+
+TestClient* CreateTestClient(
+ SocketFactory* factory, const SocketAddress& local_addr) {
+ AsyncUDPSocket* socket = CreateAsyncUDPSocket(factory);
+ CHECK(socket->Bind(local_addr));
+ return new TestClient(socket);
+}
+
+void TestNATPorts(
+ SocketServer* internal, const SocketAddress& internal_addr,
+ SocketServer* external, const SocketAddress external_addrs[4],
+ NATType nat_type, bool exp_same) {
+
+ Thread th_int(internal);
+ Thread th_ext(external);
+
+ SocketAddress server_addr = internal_addr;
+ server_addr.SetPort(NAT_SERVER_PORT);
+ NATServer* nat = new NATServer(
+ nat_type, internal, server_addr, external, external_addrs[0]);
+ NATSocketFactory* natsf = new NATSocketFactory(internal, server_addr);
+
+ TestClient* in = CreateTestClient(natsf, internal_addr);
+ TestClient* out[4];
+ for (int i = 0; i < 4; i++)
+ out[i] = CreateTestClient(external, external_addrs[i]);
+
+ th_int.Start();
+ th_ext.Start();
+
+ const char* buf = "filter_test";
+ size_t len = strlen(buf);
+
+ in->SendTo(buf, len, external_addrs[0]);
+ SocketAddress trans_addr;
+ out[0]->CheckNextPacket(buf, len, &trans_addr);
+
+ for (int i = 1; i < 4; i++) {
+ in->SendTo(buf, len, external_addrs[i]);
+ SocketAddress trans_addr2;
+ out[i]->CheckNextPacket(buf, len, &trans_addr2);
+ bool are_same = (trans_addr == trans_addr2);
+ CheckTest(are_same, exp_same, "same translated address");
+ }
+
+ th_int.Stop();
+ th_ext.Stop();
+
+ delete nat;
+ delete natsf;
+ delete in;
+ for (int i = 0; i < 4; i++)
+ delete out[i];
+}
+
+void TestPorts(
+ SocketServer* internal, const SocketAddress& internal_addr,
+ SocketServer* external, const SocketAddress external_addrs[4]) {
+ TestNATPorts(internal, internal_addr, external, external_addrs,
+ NAT_OPEN_CONE, true);
+ TestNATPorts(internal, internal_addr, external, external_addrs,
+ NAT_ADDR_RESTRICTED, true);
+ TestNATPorts(internal, internal_addr, external, external_addrs,
+ NAT_PORT_RESTRICTED, true);
+ TestNATPorts(internal, internal_addr, external, external_addrs,
+ NAT_SYMMETRIC, false);
+}
+
+void TestNATFilters(
+ SocketServer* internal, const SocketAddress& internal_addr,
+ SocketServer* external, const SocketAddress external_addrs[4],
+ NATType nat_type, bool filter_ip, bool filter_port) {
+
+ Thread th_int(internal);
+ Thread th_ext(external);
+
+ SocketAddress server_addr = internal_addr;
+ server_addr.SetPort(NAT_SERVER_PORT);
+ NATServer* nat = new NATServer(
+ nat_type, internal, server_addr, external, external_addrs[0]);
+ NATSocketFactory* natsf = new NATSocketFactory(internal, server_addr);
+
+ TestClient* in = CreateTestClient(natsf, internal_addr);
+ TestClient* out[4];
+ for (int i = 0; i < 4; i++)
+ out[i] = CreateTestClient(external, external_addrs[i]);
+
+ th_int.Start();
+ th_ext.Start();
+
+ const char* buf = "filter_test";
+ size_t len = strlen(buf);
+
+ in->SendTo(buf, len, external_addrs[0]);
+ SocketAddress trans_addr;
+ out[0]->CheckNextPacket(buf, len, &trans_addr);
+
+ out[1]->SendTo(buf, len, trans_addr);
+ CheckReceive(in, !filter_ip, buf, len);
+
+ out[2]->SendTo(buf, len, trans_addr);
+ CheckReceive(in, !filter_port, buf, len);
+
+ out[3]->SendTo(buf, len, trans_addr);
+ CheckReceive(in, !filter_ip && !filter_port, buf, len);
+
+ th_int.Stop();
+ th_ext.Stop();
+
+ delete nat;
+ delete natsf;
+ delete in;
+ for (int i = 0; i < 4; i++)
+ delete out[i];
+}
+
+void TestFilters(
+ SocketServer* internal, const SocketAddress& internal_addr,
+ SocketServer* external, const SocketAddress external_addrs[4]) {
+ TestNATFilters(internal, internal_addr, external, external_addrs,
+ NAT_OPEN_CONE, false, false);
+ TestNATFilters(internal, internal_addr, external, external_addrs,
+ NAT_ADDR_RESTRICTED, true, false);
+ TestNATFilters(internal, internal_addr, external, external_addrs,
+ NAT_PORT_RESTRICTED, true, true);
+ TestNATFilters(internal, internal_addr, external, external_addrs,
+ NAT_SYMMETRIC, true, true);
+}
+
+const int PORT0 = 7405;
+const int PORT1 = 7450;
+const int PORT2 = 7505;
+
+int main(int argc, char* argv[]) {
+ assert(LocalHost().networks().size() >= 2);
+ SocketAddress int_addr(LocalHost().networks()[1]->ip(), PORT0);
+
+ std::string ext_ip1 =
+ SocketAddress::IPToString(LocalHost().networks()[0]->ip()); // 127.0.0.1
+ std::string ext_ip2 =
+ SocketAddress::IPToString(LocalHost().networks()[1]->ip()); // 127.0.0.2
+ assert(int_addr.IPAsString() != ext_ip1);
+ //assert(int_addr.IPAsString() != ext_ip2); // uncomment
+
+ SocketAddress ext_addrs[4] = {
+ SocketAddress(ext_ip1, PORT1),
+ SocketAddress(ext_ip2, PORT1),
+ SocketAddress(ext_ip1, PORT2),
+ SocketAddress(ext_ip2, PORT2)
+ };
+
+ PhysicalSocketServer* int_pss = new PhysicalSocketServer();
+ PhysicalSocketServer* ext_pss = new PhysicalSocketServer();
+
+ std::cout << "Testing on physical network:" << std::endl;
+ TestPorts(int_pss, int_addr, ext_pss, ext_addrs);
+ std::cout << "ports: PASS" << std::endl;
+ TestFilters(int_pss, int_addr, ext_pss, ext_addrs);
+ std::cout << "filters: PASS" << std::endl;
+
+ VirtualSocketServer* int_vss = new VirtualSocketServer();
+ VirtualSocketServer* ext_vss = new VirtualSocketServer();
+
+ int_addr.SetIP(int_vss->GetNextIP());
+ ext_addrs[0].SetIP(ext_vss->GetNextIP());
+ ext_addrs[1].SetIP(ext_vss->GetNextIP());
+ ext_addrs[2].SetIP(ext_addrs[0].ip());
+ ext_addrs[3].SetIP(ext_addrs[1].ip());
+
+ std::cout << "Testing on virtual network:" << std::endl;
+ TestPorts(int_vss, int_addr, ext_vss, ext_addrs);
+ std::cout << "ports: PASS" << std::endl;
+ TestFilters(int_vss, int_addr, ext_vss, ext_addrs);
+ std::cout << "filters: PASS" << std::endl;
+
+ return 0;
+}
diff --git a/Plugins/jingle/libjingle/talk/base/natserver.cc b/Plugins/jingle/libjingle/talk/base/natserver.cc
new file mode 100644
index 0000000..336cf64
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/natserver.cc
@@ -0,0 +1,210 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <cassert>
+#include <iostream>
+
+#ifdef POSIX
+extern "C" {
+#include <errno.h>
+}
+#endif // POSIX
+
+#include "talk/base/natserver.h"
+
+namespace talk_base {
+
+RouteCmp::RouteCmp(NAT* nat) : symmetric(nat->IsSymmetric()) {
+}
+
+size_t RouteCmp::operator()(const SocketAddressPair& r) const {
+ size_t h = r.source().Hash();
+ if (symmetric)
+ h ^= r.destination().Hash();
+ return h;
+}
+
+bool RouteCmp::operator()(
+ const SocketAddressPair& r1, const SocketAddressPair& r2) const {
+ if (r1.source() < r2.source())
+ return true;
+ if (r2.source() < r1.source())
+ return false;
+ if (symmetric && (r1.destination() < r2.destination()))
+ return true;
+ if (symmetric && (r2.destination() < r1.destination()))
+ return false;
+ return false;
+}
+
+AddrCmp::AddrCmp(NAT* nat)
+ : use_ip(nat->FiltersIP()), use_port(nat->FiltersPort()) {
+}
+
+size_t AddrCmp::operator()(const SocketAddress& a) const {
+ size_t h = 0;
+ if (use_ip)
+ h ^= a.ip();
+ if (use_port)
+ h ^= a.port() | (a.port() << 16);
+ return h;
+}
+
+bool AddrCmp::operator()(
+ const SocketAddress& a1, const SocketAddress& a2) const {
+ if (use_ip && (a1.ip() < a2.ip()))
+ return true;
+ if (use_ip && (a2.ip() < a1.ip()))
+ return false;
+ if (use_port && (a1.port() < a2.port()))
+ return true;
+ if (use_port && (a2.port() < a1.port()))
+ return false;
+ return false;
+}
+
+NATServer::NATServer(
+ NATType type, SocketFactory* internal, const SocketAddress& internal_addr,
+ SocketFactory* external, const SocketAddress& external_ip)
+ : external_(external), external_ip_(external_ip) {
+ nat_ = NAT::Create(type);
+
+ server_socket_ = CreateAsyncUDPSocket(internal);
+ server_socket_->Bind(internal_addr);
+ server_socket_->SignalReadPacket.connect(this, &NATServer::OnInternalPacket);
+
+ int_map_ = new InternalMap(RouteCmp(nat_));
+ ext_map_ = new ExternalMap();
+}
+
+NATServer::~NATServer() {
+ for (InternalMap::iterator iter = int_map_->begin();
+ iter != int_map_->end();
+ iter++)
+ delete iter->second;
+
+ delete nat_;
+ delete server_socket_;
+ delete int_map_;
+ delete ext_map_;
+}
+
+void NATServer::OnInternalPacket(
+ const char* buf, size_t size, const SocketAddress& addr,
+ AsyncPacketSocket* socket) {
+
+ // Read the intended destination from the wire.
+ SocketAddress dest_addr;
+ dest_addr.Read_(buf, size);
+
+ // Find the translation for these addresses (allocating one if necessary).
+ SocketAddressPair route(addr, dest_addr);
+ InternalMap::iterator iter = int_map_->find(route);
+ if (iter == int_map_->end()) {
+ Translate(route);
+ iter = int_map_->find(route);
+ }
+ assert(iter != int_map_->end());
+
+ // Allow the destination to send packets back to the source.
+ iter->second->whitelist->insert(dest_addr);
+
+ // Send the packet to its intended destination.
+ iter->second->socket->SendTo(
+ buf + dest_addr.Size_(), size - dest_addr.Size_(), dest_addr);
+}
+
+void NATServer::OnExternalPacket(
+ const char* buf, size_t size, const SocketAddress& remote_addr,
+ AsyncPacketSocket* socket) {
+
+ SocketAddress local_addr = socket->GetLocalAddress();
+
+ // Find the translation for this addresses.
+ ExternalMap::iterator iter = ext_map_->find(local_addr);
+ assert(iter != ext_map_->end());
+
+ // Allow the NAT to reject this packet.
+ if (Filter(iter->second, remote_addr)) {
+ std::cerr << "Packet from " << remote_addr.ToString()
+ << " was filtered out by the NAT." << std::endl;
+ return;
+ }
+
+ // Forward this packet to the internal address.
+
+ size_t real_size = size + remote_addr.Size_();
+ char* real_buf = new char[real_size];
+
+ remote_addr.Write_(real_buf, real_size);
+ std::memcpy(real_buf + remote_addr.Size_(), buf, size);
+
+ server_socket_->SendTo(real_buf, real_size, iter->second->route.source());
+
+ delete[] real_buf;
+}
+
+void NATServer::Translate(const SocketAddressPair& route) {
+ AsyncUDPSocket* socket = CreateAsyncUDPSocket(external_);
+
+ SocketAddress ext_addr = external_ip_;
+ for (int i = 0; i < 65536; i++) {
+ ext_addr.SetPort((route.source().port() + i) % 65536);
+ if (ext_map_->find(ext_addr) == ext_map_->end()) {
+ int result = socket->Bind(ext_addr);
+ if ((result < 0) && (socket->GetError() == EADDRINUSE))
+ continue;
+ assert(result >= 0); // TODO: do something better
+
+ TransEntry* entry = new TransEntry(route, socket, nat_);
+ (*int_map_)[route] = entry;
+ (*ext_map_)[ext_addr] = entry;
+ socket->SignalReadPacket.connect(this, &NATServer::OnExternalPacket);
+ return;
+ }
+ }
+
+ std::cerr << "Couldn't find a free port!" << std::endl;
+ delete socket;
+ exit(1);
+}
+
+bool NATServer::Filter(TransEntry* entry, const SocketAddress& ext_addr) {
+ return entry->whitelist->find(ext_addr) == entry->whitelist->end();
+}
+
+NATServer::TransEntry::TransEntry(
+ const SocketAddressPair& r, AsyncUDPSocket* s, NAT* nat)
+ : route(r), socket(s) {
+ whitelist = new AddressSet(AddrCmp(nat));
+}
+
+NATServer::TransEntry::~TransEntry() {
+ delete socket;
+}
+
+} // namespace talk_base
diff --git a/Plugins/jingle/libjingle/talk/base/natserver.h b/Plugins/jingle/libjingle/talk/base/natserver.h
new file mode 100644
index 0000000..61c1dd3
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/natserver.h
@@ -0,0 +1,114 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TALK_BASE_NATSERVER_H__
+#define TALK_BASE_NATSERVER_H__
+
+#include "talk/base/asyncudpsocket.h"
+#include "talk/base/socketaddresspair.h"
+#include "talk/base/thread.h"
+#include "talk/base/socketfactory.h"
+#include "talk/base/nattypes.h"
+#include <map>
+
+namespace talk_base {
+
+// Change how routes (socketaddress pairs) are compared based on the type of
+// NAT. The NAT server maintains a hashtable of the routes that it knows
+// about. So these affect which routes are treated the same.
+struct RouteCmp {
+ RouteCmp(NAT* nat);
+ size_t operator()(const SocketAddressPair& r) const;
+ bool operator()(
+ const SocketAddressPair& r1, const SocketAddressPair& r2) const;
+
+ bool symmetric;
+};
+
+// Changes how addresses are compared based on the filtering rules of the NAT.
+struct AddrCmp {
+ AddrCmp(NAT* nat);
+ size_t operator()(const SocketAddress& r) const;
+ bool operator()(const SocketAddress& r1, const SocketAddress& r2) const;
+
+ bool use_ip;
+ bool use_port;
+};
+
+// Implements the NAT device. It listens for packets on the internal network,
+// translates them, and sends them out over the external network.
+
+const int NAT_SERVER_PORT = 4237;
+
+class NATServer : public sigslot::has_slots<> {
+public:
+ NATServer(
+ NATType type, SocketFactory* internal, const SocketAddress& internal_addr,
+ SocketFactory* external, const SocketAddress& external_ip);
+ ~NATServer();
+
+ // Packets received on one of the networks.
+ void OnInternalPacket(
+ const char* buf, size_t size, const SocketAddress& addr,
+ AsyncPacketSocket* socket);
+ void OnExternalPacket(
+ const char* buf, size_t size, const SocketAddress& remote_addr,
+ AsyncPacketSocket* socket);
+
+private:
+ typedef std::set<SocketAddress,AddrCmp> AddressSet;
+
+ /* Records a translation and the associated external socket. */
+ struct TransEntry {
+ TransEntry(const SocketAddressPair& r, AsyncUDPSocket* s, NAT* nat);
+ ~TransEntry();
+
+ SocketAddressPair route;
+ AsyncUDPSocket* socket;
+ AddressSet* whitelist;
+ };
+
+ typedef std::map<SocketAddressPair,TransEntry*,RouteCmp> InternalMap;
+ typedef std::map<SocketAddress,TransEntry*> ExternalMap;
+
+ NAT* nat_;
+ AsyncUDPSocket* server_socket_;
+ SocketFactory* external_;
+ SocketAddress external_ip_;
+ InternalMap* int_map_;
+ ExternalMap* ext_map_;
+
+ /* Creates a new entry that translates the given route. */
+ void Translate(const SocketAddressPair& route);
+
+ /* Determines whether the NAT would filter out a packet from this address. */
+ bool Filter(TransEntry* entry, const SocketAddress& ext_addr);
+};
+
+} // namespace talk_base
+
+#endif // TALK_BASE_NATSERVER_H__
diff --git a/Plugins/jingle/libjingle/talk/base/natserver_main.cc b/Plugins/jingle/libjingle/talk/base/natserver_main.cc
new file mode 100644
index 0000000..a748108
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/natserver_main.cc
@@ -0,0 +1,57 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <iostream>
+
+#include "talk/base/natserver.h"
+#include "talk/base/host.h"
+#include "talk/base/physicalsocketserver.h"
+
+using namespace talk_base;
+
+int main(int argc, char* argv[]) {
+ if (argc != 3) {
+ std::cerr << "usage: natserver <internal-ip> <external-ip>" << std::endl;
+ exit(1);
+ }
+
+ SocketAddress internal = SocketAddress(argv[1]);
+ SocketAddress external = SocketAddress(argv[2]);
+ if (internal.EqualIPs(external)) {
+ std::cerr << "internal and external IPs must differ" << std::endl;
+ exit(1);
+ }
+
+ Thread* pthMain = Thread::Current();
+ PhysicalSocketServer* ss = new PhysicalSocketServer();
+ pthMain->set_socketserver(ss);
+ NATServer* server = new NATServer(NAT_OPEN_CONE, ss, internal, ss, external);
+ server = server;
+
+ pthMain->Run();
+ return 0;
+}
diff --git a/Plugins/jingle/libjingle/talk/base/natsocketfactory.cc b/Plugins/jingle/libjingle/talk/base/natsocketfactory.cc
new file mode 100644
index 0000000..2b0309e
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/natsocketfactory.cc
@@ -0,0 +1,228 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <iostream>
+#include <cassert>
+#include "talk/base/natsocketfactory.h"
+
+namespace talk_base {
+
+class NATSocket : public AsyncSocket {
+public:
+ NATSocket(Socket* socket, const SocketAddress& server_addr)
+ : async_(false), connected_(false), server_addr_(server_addr),
+ socket_(socket), buf_(0), size_(0) {
+ }
+
+ NATSocket(AsyncSocket* socket, const SocketAddress& server_addr)
+ : async_(true), connected_(false), server_addr_(server_addr),
+ socket_(socket), buf_(0), size_(0) {
+ socket->SignalReadEvent.connect(this, &NATSocket::OnReadEvent);
+ socket->SignalWriteEvent.connect(this, &NATSocket::OnWriteEvent);
+ }
+
+ virtual ~NATSocket() {
+ delete socket_;
+ delete buf_;
+ }
+
+ SocketAddress GetLocalAddress() const {
+ return socket_->GetLocalAddress();
+ }
+
+ SocketAddress GetRemoteAddress() const {
+ return remote_addr_; // will be ANY if not connected
+ }
+
+ int Bind(const SocketAddress& addr) {
+ return socket_->Bind(addr);
+ }
+
+ int Connect(const SocketAddress& addr) {
+ connected_ = true;
+ remote_addr_ = addr;
+ return 0;
+ }
+
+ int Send(const void *pv, size_t cb) {
+ assert(connected_);
+ return SendInternal(pv, cb, remote_addr_);
+ }
+
+ int SendTo(const void *pv, size_t cb, const SocketAddress& addr) {
+ assert(!connected_);
+ return SendInternal(pv, cb, addr);
+ }
+
+ int SendInternal(const void *pv, size_t cb, const SocketAddress& addr) {
+ size_t size = cb + addr.Size_();
+ char* buf = new char[size];
+ Encode(static_cast<const char*>(pv), cb, buf, size, addr);
+
+ int result = socket_->SendTo(buf, size, server_addr_);
+ delete buf;
+ if (result < 0) {
+ return result;
+ } else {
+ assert(result == static_cast<int>(size)); // TODO: This isn't fair.
+ return (int)((size_t)result - addr.Size_());
+ }
+ }
+
+ int Recv(void *pv, size_t cb) {
+ SocketAddress addr;
+ return RecvFrom(pv, cb, &addr);
+ }
+
+ int RecvFrom(void *pv, size_t cb, SocketAddress *paddr) {
+ // Make sure we have enough room to read the requested amount plus the
+ // header address.
+ SocketAddress remote_addr;
+ Grow(cb + remote_addr.Size_());
+
+ // Read the packet from the socket.
+ int result = socket_->RecvFrom(buf_, size_, &remote_addr);
+ if (result < 0)
+ return result;
+ assert(remote_addr == server_addr_);
+
+ // TODO: we need better framing so that we know how many bytes we can
+ // return before we need to read the next address. For UDP, this will be
+ // fine as long as the reader always reads everything in the packet.
+ assert((size_t)result < size_);
+
+ // Decode the wire packet into the actual results.
+ SocketAddress real_remote_addr;
+ size_t real_size = cb;
+ Decode(buf_, result, pv, &real_size, &real_remote_addr);
+
+ // Make sure this packet should be delivered before returning it.
+ if (!connected_ || (real_remote_addr == remote_addr_)) {
+ if (paddr)
+ *paddr = real_remote_addr;
+ return (int)real_size;
+ } else {
+ std::cerr << "Dropping packet from unknown remote address: "
+ << real_remote_addr.ToString() << std::endl;
+ return 0; // Tell the caller we didn't read anything
+ }
+ }
+
+ int Close() {
+ connected_ = false;
+ remote_addr_ = SocketAddress();
+ return socket_->Close();
+ }
+
+ int Listen(int backlog) {
+ assert(false); // not yet implemented
+ return 0;
+ }
+
+ Socket* Accept(SocketAddress *paddr) {
+ assert(false); // not yet implemented
+ return 0;
+ }
+
+ AsyncSocket* asyncsocket() {
+ assert(async_);
+ return static_cast<AsyncSocket*>(socket_);
+ }
+
+ int GetError() const { return socket_->GetError(); }
+ void SetError(int error) { socket_->SetError(error); }
+
+ ConnState GetState() const { return connected_ ? CS_CONNECTED : CS_CLOSED; }
+
+ virtual int EstimateMTU(uint16* mtu) { return socket_->EstimateMTU(mtu); }
+ virtual int SetOption(Option opt, int value) { return socket_->SetOption(opt, value); }
+
+ void OnReadEvent(AsyncSocket* socket) {
+ assert(socket == socket_);
+ SignalReadEvent(this);
+ }
+
+ void OnWriteEvent(AsyncSocket* socket) {
+ assert(socket == socket_);
+ SignalWriteEvent(this);
+ }
+
+private:
+ // Makes sure the buffer is at least the given size.
+ void Grow(size_t new_size) {
+ if (size_ < new_size) {
+ delete buf_;
+ size_ = new_size;
+ buf_ = new char[size_];
+ }
+ }
+
+ // Encodes the given data and intended remote address into a packet to send
+ // to the NAT server.
+ void Encode(const char* data, size_t data_size, char* buf, size_t buf_size,
+ const SocketAddress& remote_addr) {
+ assert(buf_size == data_size + remote_addr.Size_());
+ remote_addr.Write_(buf, (int)buf_size);
+ std::memcpy(buf + remote_addr.Size_(), data, data_size);
+ }
+
+ // Decodes the given packet from the NAT server into the actual remote
+ // address and data.
+ void Decode(const char* data, size_t data_size, void* buf, size_t* buf_size,
+ SocketAddress* remote_addr) {
+ assert(data_size >= remote_addr->Size_());
+ assert(data_size <= *buf_size + remote_addr->Size_());
+ remote_addr->Read_(data, (int)data_size);
+ *buf_size = data_size - remote_addr->Size_();
+ std::memcpy(buf, data + remote_addr->Size_(), *buf_size);
+ }
+
+ bool async_;
+ bool connected_;
+ SocketAddress remote_addr_;
+ SocketAddress server_addr_; // address of the NAT server
+ Socket* socket_;
+ char* buf_;
+ size_t size_;
+};
+
+NATSocketFactory::NATSocketFactory(
+ SocketFactory* factory, const SocketAddress& nat_addr)
+ : factory_(factory), nat_addr_(nat_addr) {
+}
+
+Socket* NATSocketFactory::CreateSocket(int type) {
+ assert(type == SOCK_DGRAM); // TCP is not yet suported
+ return new NATSocket(factory_->CreateSocket(type), nat_addr_);
+}
+
+AsyncSocket* NATSocketFactory::CreateAsyncSocket(int type) {
+ assert(type == SOCK_DGRAM); // TCP is not yet suported
+ return new NATSocket(factory_->CreateAsyncSocket(type), nat_addr_);
+}
+
+} // namespace talk_base
diff --git a/Plugins/jingle/libjingle/talk/base/natsocketfactory.h b/Plugins/jingle/libjingle/talk/base/natsocketfactory.h
new file mode 100644
index 0000000..a689158
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/natsocketfactory.h
@@ -0,0 +1,51 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TALK_BASE_NATSOCKETFACTORY_H__
+#define TALK_BASE_NATSOCKETFACTORY_H__
+
+#include "talk/base/socketfactory.h"
+
+namespace talk_base {
+
+// Creates sockets that will send all traffic through a NAT. The actual data
+// is sent using sockets from a socket factory, given to the constructor.
+class NATSocketFactory : public SocketFactory {
+public:
+ NATSocketFactory(SocketFactory* factory, const SocketAddress& nat_addr);
+
+ virtual Socket* CreateSocket(int type);
+ virtual AsyncSocket* CreateAsyncSocket(int type);
+
+private:
+ SocketFactory* factory_;
+ SocketAddress nat_addr_;
+};
+
+} // namespace talk_base
+
+#endif // TALK_BASE_NATSOCKETFACTORY_H__
diff --git a/Plugins/jingle/libjingle/talk/base/nattypes.cc b/Plugins/jingle/libjingle/talk/base/nattypes.cc
new file mode 100644
index 0000000..290c3ad
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/nattypes.cc
@@ -0,0 +1,72 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <cassert>
+
+#include "talk/base/nattypes.h"
+
+namespace talk_base {
+
+class SymmetricNAT : public NAT {
+public:
+ bool IsSymmetric() { return true; }
+ bool FiltersIP() { return true; }
+ bool FiltersPort() { return true; }
+};
+
+class OpenConeNAT : public NAT {
+public:
+ bool IsSymmetric() { return false; }
+ bool FiltersIP() { return false; }
+ bool FiltersPort() { return false; }
+};
+
+class AddressRestrictedNAT : public NAT {
+public:
+ bool IsSymmetric() { return false; }
+ bool FiltersIP() { return true; }
+ bool FiltersPort() { return false; }
+};
+
+class PortRestrictedNAT : public NAT {
+public:
+ bool IsSymmetric() { return false; }
+ bool FiltersIP() { return true; }
+ bool FiltersPort() { return true; }
+};
+
+NAT* NAT::Create(NATType type) {
+ switch (type) {
+ case NAT_OPEN_CONE: return new OpenConeNAT();
+ case NAT_ADDR_RESTRICTED: return new AddressRestrictedNAT();
+ case NAT_PORT_RESTRICTED: return new PortRestrictedNAT();
+ case NAT_SYMMETRIC: return new SymmetricNAT();
+ default: assert(0); return 0;
+ }
+}
+
+} // namespace talk_base
diff --git a/Plugins/jingle/libjingle/talk/base/nattypes.h b/Plugins/jingle/libjingle/talk/base/nattypes.h
new file mode 100644
index 0000000..aad11bb
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/nattypes.h
@@ -0,0 +1,62 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TALK_BASE_NATTYPE_H__
+#define TALK_BASE_NATTYPE_H__
+
+namespace talk_base {
+
+/* Identifies each type of NAT that can be simulated. */
+enum NATType {
+ NAT_OPEN_CONE,
+ NAT_ADDR_RESTRICTED,
+ NAT_PORT_RESTRICTED,
+ NAT_SYMMETRIC
+};
+
+// Implements the rules for each specific type of NAT.
+class NAT {
+public:
+ // Determines whether this NAT uses both source and destination address when
+ // checking whether a mapping already exists.
+ virtual bool IsSymmetric() = 0;
+
+ // Determines whether this NAT drops packets received from a different IP
+ // the one last sent to.
+ virtual bool FiltersIP() = 0;
+
+ // Determines whether this NAT drops packets received from a different port
+ // the one last sent to.
+ virtual bool FiltersPort() = 0;
+
+ // Returns an implementation of the given type of NAT.
+ static NAT* Create(NATType type);
+};
+
+} // namespace talk_base
+
+#endif // TALK_BASE_NATTYPE_H__
diff --git a/Plugins/jingle/libjingle/talk/base/netfw.h b/Plugins/jingle/libjingle/talk/base/netfw.h
new file mode 100644
index 0000000..3a1e4a7
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/netfw.h
@@ -0,0 +1,3767 @@
+
+#pragma warning( disable: 4049 ) /* more than 64k source lines */
+
+/* this ALWAYS GENERATED file contains the definitions for the interfaces */
+
+
+ /* File created by MIDL compiler version 6.00.0347 */
+/* Compiler settings for netfw.idl:
+ Oicf, W1, Zp8, env=Win32 (32b run)
+ protocol : dce , ms_ext, c_ext, robust
+ error checks: allocation ref bounds_check enum stub_data
+ VC __declspec() decoration level:
+ __declspec(uuid()), __declspec(selectany), __declspec(novtable)
+ DECLSPEC_UUID(), MIDL_INTERFACE()
+*/
+//@@MIDL_FILE_HEADING( )
+
+
+/* verify that the <rpcndr.h> version is high enough to compile this file*/
+#ifndef __REQUIRED_RPCNDR_H_VERSION__
+#define __REQUIRED_RPCNDR_H_VERSION__ 475
+#endif
+
+#include "rpc.h"
+#include "rpcndr.h"
+
+#ifndef __RPCNDR_H_VERSION__
+#error this stub requires an updated version of <rpcndr.h>
+#endif // __RPCNDR_H_VERSION__
+
+#ifndef COM_NO_WINDOWS_H
+#include "windows.h"
+#include "ole2.h"
+#endif /*COM_NO_WINDOWS_H*/
+
+#ifndef __netfw_h__
+#define __netfw_h__
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1020)
+#pragma once
+#endif
+
+/* Forward Declarations */
+
+#ifndef __INetFwRemoteAdminSettings_FWD_DEFINED__
+#define __INetFwRemoteAdminSettings_FWD_DEFINED__
+typedef interface INetFwRemoteAdminSettings INetFwRemoteAdminSettings;
+#endif /* __INetFwRemoteAdminSettings_FWD_DEFINED__ */
+
+
+#ifndef __INetFwIcmpSettings_FWD_DEFINED__
+#define __INetFwIcmpSettings_FWD_DEFINED__
+typedef interface INetFwIcmpSettings INetFwIcmpSettings;
+#endif /* __INetFwIcmpSettings_FWD_DEFINED__ */
+
+
+#ifndef __INetFwOpenPort_FWD_DEFINED__
+#define __INetFwOpenPort_FWD_DEFINED__
+typedef interface INetFwOpenPort INetFwOpenPort;
+#endif /* __INetFwOpenPort_FWD_DEFINED__ */
+
+
+#ifndef __INetFwOpenPorts_FWD_DEFINED__
+#define __INetFwOpenPorts_FWD_DEFINED__
+typedef interface INetFwOpenPorts INetFwOpenPorts;
+#endif /* __INetFwOpenPorts_FWD_DEFINED__ */
+
+
+#ifndef __INetFwService_FWD_DEFINED__
+#define __INetFwService_FWD_DEFINED__
+typedef interface INetFwService INetFwService;
+#endif /* __INetFwService_FWD_DEFINED__ */
+
+
+#ifndef __INetFwServices_FWD_DEFINED__
+#define __INetFwServices_FWD_DEFINED__
+typedef interface INetFwServices INetFwServices;
+#endif /* __INetFwServices_FWD_DEFINED__ */
+
+
+#ifndef __INetFwAuthorizedApplication_FWD_DEFINED__
+#define __INetFwAuthorizedApplication_FWD_DEFINED__
+typedef interface INetFwAuthorizedApplication INetFwAuthorizedApplication;
+#endif /* __INetFwAuthorizedApplication_FWD_DEFINED__ */
+
+
+#ifndef __INetFwAuthorizedApplications_FWD_DEFINED__
+#define __INetFwAuthorizedApplications_FWD_DEFINED__
+typedef interface INetFwAuthorizedApplications INetFwAuthorizedApplications;
+#endif /* __INetFwAuthorizedApplications_FWD_DEFINED__ */
+
+
+#ifndef __INetFwProfile_FWD_DEFINED__
+#define __INetFwProfile_FWD_DEFINED__
+typedef interface INetFwProfile INetFwProfile;
+#endif /* __INetFwProfile_FWD_DEFINED__ */
+
+
+#ifndef __INetFwPolicy_FWD_DEFINED__
+#define __INetFwPolicy_FWD_DEFINED__
+typedef interface INetFwPolicy INetFwPolicy;
+#endif /* __INetFwPolicy_FWD_DEFINED__ */
+
+
+#ifndef __INetFwMgr_FWD_DEFINED__
+#define __INetFwMgr_FWD_DEFINED__
+typedef interface INetFwMgr INetFwMgr;
+#endif /* __INetFwMgr_FWD_DEFINED__ */
+
+
+#ifndef __INetFwRemoteAdminSettings_FWD_DEFINED__
+#define __INetFwRemoteAdminSettings_FWD_DEFINED__
+typedef interface INetFwRemoteAdminSettings INetFwRemoteAdminSettings;
+#endif /* __INetFwRemoteAdminSettings_FWD_DEFINED__ */
+
+
+#ifndef __INetFwIcmpSettings_FWD_DEFINED__
+#define __INetFwIcmpSettings_FWD_DEFINED__
+typedef interface INetFwIcmpSettings INetFwIcmpSettings;
+#endif /* __INetFwIcmpSettings_FWD_DEFINED__ */
+
+
+#ifndef __INetFwOpenPort_FWD_DEFINED__
+#define __INetFwOpenPort_FWD_DEFINED__
+typedef interface INetFwOpenPort INetFwOpenPort;
+#endif /* __INetFwOpenPort_FWD_DEFINED__ */
+
+
+#ifndef __INetFwOpenPorts_FWD_DEFINED__
+#define __INetFwOpenPorts_FWD_DEFINED__
+typedef interface INetFwOpenPorts INetFwOpenPorts;
+#endif /* __INetFwOpenPorts_FWD_DEFINED__ */
+
+
+#ifndef __INetFwService_FWD_DEFINED__
+#define __INetFwService_FWD_DEFINED__
+typedef interface INetFwService INetFwService;
+#endif /* __INetFwService_FWD_DEFINED__ */
+
+
+#ifndef __INetFwServices_FWD_DEFINED__
+#define __INetFwServices_FWD_DEFINED__
+typedef interface INetFwServices INetFwServices;
+#endif /* __INetFwServices_FWD_DEFINED__ */
+
+
+#ifndef __INetFwAuthorizedApplication_FWD_DEFINED__
+#define __INetFwAuthorizedApplication_FWD_DEFINED__
+typedef interface INetFwAuthorizedApplication INetFwAuthorizedApplication;
+#endif /* __INetFwAuthorizedApplication_FWD_DEFINED__ */
+
+
+#ifndef __INetFwAuthorizedApplications_FWD_DEFINED__
+#define __INetFwAuthorizedApplications_FWD_DEFINED__
+typedef interface INetFwAuthorizedApplications INetFwAuthorizedApplications;
+#endif /* __INetFwAuthorizedApplications_FWD_DEFINED__ */
+
+
+#ifndef __INetFwProfile_FWD_DEFINED__
+#define __INetFwProfile_FWD_DEFINED__
+typedef interface INetFwProfile INetFwProfile;
+#endif /* __INetFwProfile_FWD_DEFINED__ */
+
+
+#ifndef __INetFwPolicy_FWD_DEFINED__
+#define __INetFwPolicy_FWD_DEFINED__
+typedef interface INetFwPolicy INetFwPolicy;
+#endif /* __INetFwPolicy_FWD_DEFINED__ */
+
+
+#ifndef __INetFwMgr_FWD_DEFINED__
+#define __INetFwMgr_FWD_DEFINED__
+typedef interface INetFwMgr INetFwMgr;
+#endif /* __INetFwMgr_FWD_DEFINED__ */
+
+
+#ifndef __NetFwOpenPort_FWD_DEFINED__
+#define __NetFwOpenPort_FWD_DEFINED__
+
+#ifdef __cplusplus
+typedef class NetFwOpenPort NetFwOpenPort;
+#else
+typedef struct NetFwOpenPort NetFwOpenPort;
+#endif /* __cplusplus */
+
+#endif /* __NetFwOpenPort_FWD_DEFINED__ */
+
+
+#ifndef __NetFwAuthorizedApplication_FWD_DEFINED__
+#define __NetFwAuthorizedApplication_FWD_DEFINED__
+
+#ifdef __cplusplus
+typedef class NetFwAuthorizedApplication NetFwAuthorizedApplication;
+#else
+typedef struct NetFwAuthorizedApplication NetFwAuthorizedApplication;
+#endif /* __cplusplus */
+
+#endif /* __NetFwAuthorizedApplication_FWD_DEFINED__ */
+
+
+#ifndef __NetFwMgr_FWD_DEFINED__
+#define __NetFwMgr_FWD_DEFINED__
+
+#ifdef __cplusplus
+typedef class NetFwMgr NetFwMgr;
+#else
+typedef struct NetFwMgr NetFwMgr;
+#endif /* __cplusplus */
+
+#endif /* __NetFwMgr_FWD_DEFINED__ */
+
+
+/* header files for imported files */
+#include "icftypes.h"
+#include "oaidl.h"
+
+#ifdef __cplusplus
+extern "C"{
+#endif
+
+void * __RPC_USER MIDL_user_allocate(size_t);
+void __RPC_USER MIDL_user_free( void * );
+
+#ifndef __INetFwRemoteAdminSettings_INTERFACE_DEFINED__
+#define __INetFwRemoteAdminSettings_INTERFACE_DEFINED__
+
+/* interface INetFwRemoteAdminSettings */
+/* [dual][uuid][object] */
+
+
+EXTERN_C const IID IID_INetFwRemoteAdminSettings;
+
+#if defined(__cplusplus) && !defined(CINTERFACE)
+
+ MIDL_INTERFACE("D4BECDDF-6F73-4A83-B832-9C66874CD20E")
+ INetFwRemoteAdminSettings : public IDispatch
+ {
+ public:
+ virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_IpVersion(
+ /* [retval][out] */ NET_FW_IP_VERSION *ipVersion) = 0;
+
+ virtual /* [propput][id] */ HRESULT STDMETHODCALLTYPE put_IpVersion(
+ /* [in] */ NET_FW_IP_VERSION ipVersion) = 0;
+
+ virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_Scope(
+ /* [retval][out] */ NET_FW_SCOPE *scope) = 0;
+
+ virtual /* [propput][id] */ HRESULT STDMETHODCALLTYPE put_Scope(
+ /* [in] */ NET_FW_SCOPE scope) = 0;
+
+ virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_RemoteAddresses(
+ /* [retval][out] */ BSTR *remoteAddrs) = 0;
+
+ virtual /* [propput][id] */ HRESULT STDMETHODCALLTYPE put_RemoteAddresses(
+ /* [in] */ BSTR remoteAddrs) = 0;
+
+ virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_Enabled(
+ /* [retval][out] */ VARIANT_BOOL *enabled) = 0;
+
+ virtual /* [propput][id] */ HRESULT STDMETHODCALLTYPE put_Enabled(
+ /* [in] */ VARIANT_BOOL enabled) = 0;
+
+ };
+
+#else /* C style interface */
+
+ typedef struct INetFwRemoteAdminSettingsVtbl
+ {
+ BEGIN_INTERFACE
+
+ HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
+ INetFwRemoteAdminSettings * This,
+ /* [in] */ REFIID riid,
+ /* [iid_is][out] */ void **ppvObject);
+
+ ULONG ( STDMETHODCALLTYPE *AddRef )(
+ INetFwRemoteAdminSettings * This);
+
+ ULONG ( STDMETHODCALLTYPE *Release )(
+ INetFwRemoteAdminSettings * This);
+
+ HRESULT ( STDMETHODCALLTYPE *GetTypeInfoCount )(
+ INetFwRemoteAdminSettings * This,
+ /* [out] */ UINT *pctinfo);
+
+ HRESULT ( STDMETHODCALLTYPE *GetTypeInfo )(
+ INetFwRemoteAdminSettings * This,
+ /* [in] */ UINT iTInfo,
+ /* [in] */ LCID lcid,
+ /* [out] */ ITypeInfo **ppTInfo);
+
+ HRESULT ( STDMETHODCALLTYPE *GetIDsOfNames )(
+ INetFwRemoteAdminSettings * This,
+ /* [in] */ REFIID riid,
+ /* [size_is][in] */ LPOLESTR *rgszNames,
+ /* [in] */ UINT cNames,
+ /* [in] */ LCID lcid,
+ /* [size_is][out] */ DISPID *rgDispId);
+
+ /* [local] */ HRESULT ( STDMETHODCALLTYPE *Invoke )(
+ INetFwRemoteAdminSettings * This,
+ /* [in] */ DISPID dispIdMember,
+ /* [in] */ REFIID riid,
+ /* [in] */ LCID lcid,
+ /* [in] */ WORD wFlags,
+ /* [out][in] */ DISPPARAMS *pDispParams,
+ /* [out] */ VARIANT *pVarResult,
+ /* [out] */ EXCEPINFO *pExcepInfo,
+ /* [out] */ UINT *puArgErr);
+
+ /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE *get_IpVersion )(
+ INetFwRemoteAdminSettings * This,
+ /* [retval][out] */ NET_FW_IP_VERSION *ipVersion);
+
+ /* [propput][id] */ HRESULT ( STDMETHODCALLTYPE *put_IpVersion )(
+ INetFwRemoteAdminSettings * This,
+ /* [in] */ NET_FW_IP_VERSION ipVersion);
+
+ /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE *get_Scope )(
+ INetFwRemoteAdminSettings * This,
+ /* [retval][out] */ NET_FW_SCOPE *scope);
+
+ /* [propput][id] */ HRESULT ( STDMETHODCALLTYPE *put_Scope )(
+ INetFwRemoteAdminSettings * This,
+ /* [in] */ NET_FW_SCOPE scope);
+
+ /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE *get_RemoteAddresses )(
+ INetFwRemoteAdminSettings * This,
+ /* [retval][out] */ BSTR *remoteAddrs);
+
+ /* [propput][id] */ HRESULT ( STDMETHODCALLTYPE *put_RemoteAddresses )(
+ INetFwRemoteAdminSettings * This,
+ /* [in] */ BSTR remoteAddrs);
+
+ /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE *get_Enabled )(
+ INetFwRemoteAdminSettings * This,
+ /* [retval][out] */ VARIANT_BOOL *enabled);
+
+ /* [propput][id] */ HRESULT ( STDMETHODCALLTYPE *put_Enabled )(
+ INetFwRemoteAdminSettings * This,
+ /* [in] */ VARIANT_BOOL enabled);
+
+ END_INTERFACE
+ } INetFwRemoteAdminSettingsVtbl;
+
+ interface INetFwRemoteAdminSettings
+ {
+ CONST_VTBL struct INetFwRemoteAdminSettingsVtbl *lpVtbl;
+ };
+
+
+
+#ifdef COBJMACROS
+
+
+#define INetFwRemoteAdminSettings_QueryInterface(This,riid,ppvObject) \
+ (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)
+
+#define INetFwRemoteAdminSettings_AddRef(This) \
+ (This)->lpVtbl -> AddRef(This)
+
+#define INetFwRemoteAdminSettings_Release(This) \
+ (This)->lpVtbl -> Release(This)
+
+
+#define INetFwRemoteAdminSettings_GetTypeInfoCount(This,pctinfo) \
+ (This)->lpVtbl -> GetTypeInfoCount(This,pctinfo)
+
+#define INetFwRemoteAdminSettings_GetTypeInfo(This,iTInfo,lcid,ppTInfo) \
+ (This)->lpVtbl -> GetTypeInfo(This,iTInfo,lcid,ppTInfo)
+
+#define INetFwRemoteAdminSettings_GetIDsOfNames(This,riid,rgszNames,cNames,lcid,rgDispId) \
+ (This)->lpVtbl -> GetIDsOfNames(This,riid,rgszNames,cNames,lcid,rgDispId)
+
+#define INetFwRemoteAdminSettings_Invoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr) \
+ (This)->lpVtbl -> Invoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr)
+
+
+#define INetFwRemoteAdminSettings_get_IpVersion(This,ipVersion) \
+ (This)->lpVtbl -> get_IpVersion(This,ipVersion)
+
+#define INetFwRemoteAdminSettings_put_IpVersion(This,ipVersion) \
+ (This)->lpVtbl -> put_IpVersion(This,ipVersion)
+
+#define INetFwRemoteAdminSettings_get_Scope(This,scope) \
+ (This)->lpVtbl -> get_Scope(This,scope)
+
+#define INetFwRemoteAdminSettings_put_Scope(This,scope) \
+ (This)->lpVtbl -> put_Scope(This,scope)
+
+#define INetFwRemoteAdminSettings_get_RemoteAddresses(This,remoteAddrs) \
+ (This)->lpVtbl -> get_RemoteAddresses(This,remoteAddrs)
+
+#define INetFwRemoteAdminSettings_put_RemoteAddresses(This,remoteAddrs) \
+ (This)->lpVtbl -> put_RemoteAddresses(This,remoteAddrs)
+
+#define INetFwRemoteAdminSettings_get_Enabled(This,enabled) \
+ (This)->lpVtbl -> get_Enabled(This,enabled)
+
+#define INetFwRemoteAdminSettings_put_Enabled(This,enabled) \
+ (This)->lpVtbl -> put_Enabled(This,enabled)
+
+#endif /* COBJMACROS */
+
+
+#endif /* C style interface */
+
+
+
+/* [propget][id] */ HRESULT STDMETHODCALLTYPE INetFwRemoteAdminSettings_get_IpVersion_Proxy(
+ INetFwRemoteAdminSettings * This,
+ /* [retval][out] */ NET_FW_IP_VERSION *ipVersion);
+
+
+void __RPC_STUB INetFwRemoteAdminSettings_get_IpVersion_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propput][id] */ HRESULT STDMETHODCALLTYPE INetFwRemoteAdminSettings_put_IpVersion_Proxy(
+ INetFwRemoteAdminSettings * This,
+ /* [in] */ NET_FW_IP_VERSION ipVersion);
+
+
+void __RPC_STUB INetFwRemoteAdminSettings_put_IpVersion_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propget][id] */ HRESULT STDMETHODCALLTYPE INetFwRemoteAdminSettings_get_Scope_Proxy(
+ INetFwRemoteAdminSettings * This,
+ /* [retval][out] */ NET_FW_SCOPE *scope);
+
+
+void __RPC_STUB INetFwRemoteAdminSettings_get_Scope_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propput][id] */ HRESULT STDMETHODCALLTYPE INetFwRemoteAdminSettings_put_Scope_Proxy(
+ INetFwRemoteAdminSettings * This,
+ /* [in] */ NET_FW_SCOPE scope);
+
+
+void __RPC_STUB INetFwRemoteAdminSettings_put_Scope_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propget][id] */ HRESULT STDMETHODCALLTYPE INetFwRemoteAdminSettings_get_RemoteAddresses_Proxy(
+ INetFwRemoteAdminSettings * This,
+ /* [retval][out] */ BSTR *remoteAddrs);
+
+
+void __RPC_STUB INetFwRemoteAdminSettings_get_RemoteAddresses_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propput][id] */ HRESULT STDMETHODCALLTYPE INetFwRemoteAdminSettings_put_RemoteAddresses_Proxy(
+ INetFwRemoteAdminSettings * This,
+ /* [in] */ BSTR remoteAddrs);
+
+
+void __RPC_STUB INetFwRemoteAdminSettings_put_RemoteAddresses_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propget][id] */ HRESULT STDMETHODCALLTYPE INetFwRemoteAdminSettings_get_Enabled_Proxy(
+ INetFwRemoteAdminSettings * This,
+ /* [retval][out] */ VARIANT_BOOL *enabled);
+
+
+void __RPC_STUB INetFwRemoteAdminSettings_get_Enabled_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propput][id] */ HRESULT STDMETHODCALLTYPE INetFwRemoteAdminSettings_put_Enabled_Proxy(
+ INetFwRemoteAdminSettings * This,
+ /* [in] */ VARIANT_BOOL enabled);
+
+
+void __RPC_STUB INetFwRemoteAdminSettings_put_Enabled_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+
+#endif /* __INetFwRemoteAdminSettings_INTERFACE_DEFINED__ */
+
+
+#ifndef __INetFwIcmpSettings_INTERFACE_DEFINED__
+#define __INetFwIcmpSettings_INTERFACE_DEFINED__
+
+/* interface INetFwIcmpSettings */
+/* [dual][uuid][object] */
+
+
+EXTERN_C const IID IID_INetFwIcmpSettings;
+
+#if defined(__cplusplus) && !defined(CINTERFACE)
+
+ MIDL_INTERFACE("A6207B2E-7CDD-426A-951E-5E1CBC5AFEAD")
+ INetFwIcmpSettings : public IDispatch
+ {
+ public:
+ virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_AllowOutboundDestinationUnreachable(
+ /* [retval][out] */ VARIANT_BOOL *allow) = 0;
+
+ virtual /* [propput][id] */ HRESULT STDMETHODCALLTYPE put_AllowOutboundDestinationUnreachable(
+ /* [in] */ VARIANT_BOOL allow) = 0;
+
+ virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_AllowRedirect(
+ /* [retval][out] */ VARIANT_BOOL *allow) = 0;
+
+ virtual /* [propput][id] */ HRESULT STDMETHODCALLTYPE put_AllowRedirect(
+ /* [in] */ VARIANT_BOOL allow) = 0;
+
+ virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_AllowInboundEchoRequest(
+ /* [retval][out] */ VARIANT_BOOL *allow) = 0;
+
+ virtual /* [propput][id] */ HRESULT STDMETHODCALLTYPE put_AllowInboundEchoRequest(
+ /* [in] */ VARIANT_BOOL allow) = 0;
+
+ virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_AllowOutboundTimeExceeded(
+ /* [retval][out] */ VARIANT_BOOL *allow) = 0;
+
+ virtual /* [propput][id] */ HRESULT STDMETHODCALLTYPE put_AllowOutboundTimeExceeded(
+ /* [in] */ VARIANT_BOOL allow) = 0;
+
+ virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_AllowOutboundParameterProblem(
+ /* [retval][out] */ VARIANT_BOOL *allow) = 0;
+
+ virtual /* [propput][id] */ HRESULT STDMETHODCALLTYPE put_AllowOutboundParameterProblem(
+ /* [in] */ VARIANT_BOOL allow) = 0;
+
+ virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_AllowOutboundSourceQuench(
+ /* [retval][out] */ VARIANT_BOOL *allow) = 0;
+
+ virtual /* [propput][id] */ HRESULT STDMETHODCALLTYPE put_AllowOutboundSourceQuench(
+ /* [in] */ VARIANT_BOOL allow) = 0;
+
+ virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_AllowInboundRouterRequest(
+ /* [retval][out] */ VARIANT_BOOL *allow) = 0;
+
+ virtual /* [propput][id] */ HRESULT STDMETHODCALLTYPE put_AllowInboundRouterRequest(
+ /* [in] */ VARIANT_BOOL allow) = 0;
+
+ virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_AllowInboundTimestampRequest(
+ /* [retval][out] */ VARIANT_BOOL *allow) = 0;
+
+ virtual /* [propput][id] */ HRESULT STDMETHODCALLTYPE put_AllowInboundTimestampRequest(
+ /* [in] */ VARIANT_BOOL allow) = 0;
+
+ virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_AllowInboundMaskRequest(
+ /* [retval][out] */ VARIANT_BOOL *allow) = 0;
+
+ virtual /* [propput][id] */ HRESULT STDMETHODCALLTYPE put_AllowInboundMaskRequest(
+ /* [in] */ VARIANT_BOOL allow) = 0;
+
+ virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_AllowOutboundPacketTooBig(
+ /* [retval][out] */ VARIANT_BOOL *allow) = 0;
+
+ virtual /* [propput][id] */ HRESULT STDMETHODCALLTYPE put_AllowOutboundPacketTooBig(
+ /* [in] */ VARIANT_BOOL allow) = 0;
+
+ };
+
+#else /* C style interface */
+
+ typedef struct INetFwIcmpSettingsVtbl
+ {
+ BEGIN_INTERFACE
+
+ HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
+ INetFwIcmpSettings * This,
+ /* [in] */ REFIID riid,
+ /* [iid_is][out] */ void **ppvObject);
+
+ ULONG ( STDMETHODCALLTYPE *AddRef )(
+ INetFwIcmpSettings * This);
+
+ ULONG ( STDMETHODCALLTYPE *Release )(
+ INetFwIcmpSettings * This);
+
+ HRESULT ( STDMETHODCALLTYPE *GetTypeInfoCount )(
+ INetFwIcmpSettings * This,
+ /* [out] */ UINT *pctinfo);
+
+ HRESULT ( STDMETHODCALLTYPE *GetTypeInfo )(
+ INetFwIcmpSettings * This,
+ /* [in] */ UINT iTInfo,
+ /* [in] */ LCID lcid,
+ /* [out] */ ITypeInfo **ppTInfo);
+
+ HRESULT ( STDMETHODCALLTYPE *GetIDsOfNames )(
+ INetFwIcmpSettings * This,
+ /* [in] */ REFIID riid,
+ /* [size_is][in] */ LPOLESTR *rgszNames,
+ /* [in] */ UINT cNames,
+ /* [in] */ LCID lcid,
+ /* [size_is][out] */ DISPID *rgDispId);
+
+ /* [local] */ HRESULT ( STDMETHODCALLTYPE *Invoke )(
+ INetFwIcmpSettings * This,
+ /* [in] */ DISPID dispIdMember,
+ /* [in] */ REFIID riid,
+ /* [in] */ LCID lcid,
+ /* [in] */ WORD wFlags,
+ /* [out][in] */ DISPPARAMS *pDispParams,
+ /* [out] */ VARIANT *pVarResult,
+ /* [out] */ EXCEPINFO *pExcepInfo,
+ /* [out] */ UINT *puArgErr);
+
+ /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE *get_AllowOutboundDestinationUnreachable )(
+ INetFwIcmpSettings * This,
+ /* [retval][out] */ VARIANT_BOOL *allow);
+
+ /* [propput][id] */ HRESULT ( STDMETHODCALLTYPE *put_AllowOutboundDestinationUnreachable )(
+ INetFwIcmpSettings * This,
+ /* [in] */ VARIANT_BOOL allow);
+
+ /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE *get_AllowRedirect )(
+ INetFwIcmpSettings * This,
+ /* [retval][out] */ VARIANT_BOOL *allow);
+
+ /* [propput][id] */ HRESULT ( STDMETHODCALLTYPE *put_AllowRedirect )(
+ INetFwIcmpSettings * This,
+ /* [in] */ VARIANT_BOOL allow);
+
+ /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE *get_AllowInboundEchoRequest )(
+ INetFwIcmpSettings * This,
+ /* [retval][out] */ VARIANT_BOOL *allow);
+
+ /* [propput][id] */ HRESULT ( STDMETHODCALLTYPE *put_AllowInboundEchoRequest )(
+ INetFwIcmpSettings * This,
+ /* [in] */ VARIANT_BOOL allow);
+
+ /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE *get_AllowOutboundTimeExceeded )(
+ INetFwIcmpSettings * This,
+ /* [retval][out] */ VARIANT_BOOL *allow);
+
+ /* [propput][id] */ HRESULT ( STDMETHODCALLTYPE *put_AllowOutboundTimeExceeded )(
+ INetFwIcmpSettings * This,
+ /* [in] */ VARIANT_BOOL allow);
+
+ /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE *get_AllowOutboundParameterProblem )(
+ INetFwIcmpSettings * This,
+ /* [retval][out] */ VARIANT_BOOL *allow);
+
+ /* [propput][id] */ HRESULT ( STDMETHODCALLTYPE *put_AllowOutboundParameterProblem )(
+ INetFwIcmpSettings * This,
+ /* [in] */ VARIANT_BOOL allow);
+
+ /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE *get_AllowOutboundSourceQuench )(
+ INetFwIcmpSettings * This,
+ /* [retval][out] */ VARIANT_BOOL *allow);
+
+ /* [propput][id] */ HRESULT ( STDMETHODCALLTYPE *put_AllowOutboundSourceQuench )(
+ INetFwIcmpSettings * This,
+ /* [in] */ VARIANT_BOOL allow);
+
+ /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE *get_AllowInboundRouterRequest )(
+ INetFwIcmpSettings * This,
+ /* [retval][out] */ VARIANT_BOOL *allow);
+
+ /* [propput][id] */ HRESULT ( STDMETHODCALLTYPE *put_AllowInboundRouterRequest )(
+ INetFwIcmpSettings * This,
+ /* [in] */ VARIANT_BOOL allow);
+
+ /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE *get_AllowInboundTimestampRequest )(
+ INetFwIcmpSettings * This,
+ /* [retval][out] */ VARIANT_BOOL *allow);
+
+ /* [propput][id] */ HRESULT ( STDMETHODCALLTYPE *put_AllowInboundTimestampRequest )(
+ INetFwIcmpSettings * This,
+ /* [in] */ VARIANT_BOOL allow);
+
+ /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE *get_AllowInboundMaskRequest )(
+ INetFwIcmpSettings * This,
+ /* [retval][out] */ VARIANT_BOOL *allow);
+
+ /* [propput][id] */ HRESULT ( STDMETHODCALLTYPE *put_AllowInboundMaskRequest )(
+ INetFwIcmpSettings * This,
+ /* [in] */ VARIANT_BOOL allow);
+
+ /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE *get_AllowOutboundPacketTooBig )(
+ INetFwIcmpSettings * This,
+ /* [retval][out] */ VARIANT_BOOL *allow);
+
+ /* [propput][id] */ HRESULT ( STDMETHODCALLTYPE *put_AllowOutboundPacketTooBig )(
+ INetFwIcmpSettings * This,
+ /* [in] */ VARIANT_BOOL allow);
+
+ END_INTERFACE
+ } INetFwIcmpSettingsVtbl;
+
+ interface INetFwIcmpSettings
+ {
+ CONST_VTBL struct INetFwIcmpSettingsVtbl *lpVtbl;
+ };
+
+
+
+#ifdef COBJMACROS
+
+
+#define INetFwIcmpSettings_QueryInterface(This,riid,ppvObject) \
+ (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)
+
+#define INetFwIcmpSettings_AddRef(This) \
+ (This)->lpVtbl -> AddRef(This)
+
+#define INetFwIcmpSettings_Release(This) \
+ (This)->lpVtbl -> Release(This)
+
+
+#define INetFwIcmpSettings_GetTypeInfoCount(This,pctinfo) \
+ (This)->lpVtbl -> GetTypeInfoCount(This,pctinfo)
+
+#define INetFwIcmpSettings_GetTypeInfo(This,iTInfo,lcid,ppTInfo) \
+ (This)->lpVtbl -> GetTypeInfo(This,iTInfo,lcid,ppTInfo)
+
+#define INetFwIcmpSettings_GetIDsOfNames(This,riid,rgszNames,cNames,lcid,rgDispId) \
+ (This)->lpVtbl -> GetIDsOfNames(This,riid,rgszNames,cNames,lcid,rgDispId)
+
+#define INetFwIcmpSettings_Invoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr) \
+ (This)->lpVtbl -> Invoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr)
+
+
+#define INetFwIcmpSettings_get_AllowOutboundDestinationUnreachable(This,allow) \
+ (This)->lpVtbl -> get_AllowOutboundDestinationUnreachable(This,allow)
+
+#define INetFwIcmpSettings_put_AllowOutboundDestinationUnreachable(This,allow) \
+ (This)->lpVtbl -> put_AllowOutboundDestinationUnreachable(This,allow)
+
+#define INetFwIcmpSettings_get_AllowRedirect(This,allow) \
+ (This)->lpVtbl -> get_AllowRedirect(This,allow)
+
+#define INetFwIcmpSettings_put_AllowRedirect(This,allow) \
+ (This)->lpVtbl -> put_AllowRedirect(This,allow)
+
+#define INetFwIcmpSettings_get_AllowInboundEchoRequest(This,allow) \
+ (This)->lpVtbl -> get_AllowInboundEchoRequest(This,allow)
+
+#define INetFwIcmpSettings_put_AllowInboundEchoRequest(This,allow) \
+ (This)->lpVtbl -> put_AllowInboundEchoRequest(This,allow)
+
+#define INetFwIcmpSettings_get_AllowOutboundTimeExceeded(This,allow) \
+ (This)->lpVtbl -> get_AllowOutboundTimeExceeded(This,allow)
+
+#define INetFwIcmpSettings_put_AllowOutboundTimeExceeded(This,allow) \
+ (This)->lpVtbl -> put_AllowOutboundTimeExceeded(This,allow)
+
+#define INetFwIcmpSettings_get_AllowOutboundParameterProblem(This,allow) \
+ (This)->lpVtbl -> get_AllowOutboundParameterProblem(This,allow)
+
+#define INetFwIcmpSettings_put_AllowOutboundParameterProblem(This,allow) \
+ (This)->lpVtbl -> put_AllowOutboundParameterProblem(This,allow)
+
+#define INetFwIcmpSettings_get_AllowOutboundSourceQuench(This,allow) \
+ (This)->lpVtbl -> get_AllowOutboundSourceQuench(This,allow)
+
+#define INetFwIcmpSettings_put_AllowOutboundSourceQuench(This,allow) \
+ (This)->lpVtbl -> put_AllowOutboundSourceQuench(This,allow)
+
+#define INetFwIcmpSettings_get_AllowInboundRouterRequest(This,allow) \
+ (This)->lpVtbl -> get_AllowInboundRouterRequest(This,allow)
+
+#define INetFwIcmpSettings_put_AllowInboundRouterRequest(This,allow) \
+ (This)->lpVtbl -> put_AllowInboundRouterRequest(This,allow)
+
+#define INetFwIcmpSettings_get_AllowInboundTimestampRequest(This,allow) \
+ (This)->lpVtbl -> get_AllowInboundTimestampRequest(This,allow)
+
+#define INetFwIcmpSettings_put_AllowInboundTimestampRequest(This,allow) \
+ (This)->lpVtbl -> put_AllowInboundTimestampRequest(This,allow)
+
+#define INetFwIcmpSettings_get_AllowInboundMaskRequest(This,allow) \
+ (This)->lpVtbl -> get_AllowInboundMaskRequest(This,allow)
+
+#define INetFwIcmpSettings_put_AllowInboundMaskRequest(This,allow) \
+ (This)->lpVtbl -> put_AllowInboundMaskRequest(This,allow)
+
+#define INetFwIcmpSettings_get_AllowOutboundPacketTooBig(This,allow) \
+ (This)->lpVtbl -> get_AllowOutboundPacketTooBig(This,allow)
+
+#define INetFwIcmpSettings_put_AllowOutboundPacketTooBig(This,allow) \
+ (This)->lpVtbl -> put_AllowOutboundPacketTooBig(This,allow)
+
+#endif /* COBJMACROS */
+
+
+#endif /* C style interface */
+
+
+
+/* [propget][id] */ HRESULT STDMETHODCALLTYPE INetFwIcmpSettings_get_AllowOutboundDestinationUnreachable_Proxy(
+ INetFwIcmpSettings * This,
+ /* [retval][out] */ VARIANT_BOOL *allow);
+
+
+void __RPC_STUB INetFwIcmpSettings_get_AllowOutboundDestinationUnreachable_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propput][id] */ HRESULT STDMETHODCALLTYPE INetFwIcmpSettings_put_AllowOutboundDestinationUnreachable_Proxy(
+ INetFwIcmpSettings * This,
+ /* [in] */ VARIANT_BOOL allow);
+
+
+void __RPC_STUB INetFwIcmpSettings_put_AllowOutboundDestinationUnreachable_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propget][id] */ HRESULT STDMETHODCALLTYPE INetFwIcmpSettings_get_AllowRedirect_Proxy(
+ INetFwIcmpSettings * This,
+ /* [retval][out] */ VARIANT_BOOL *allow);
+
+
+void __RPC_STUB INetFwIcmpSettings_get_AllowRedirect_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propput][id] */ HRESULT STDMETHODCALLTYPE INetFwIcmpSettings_put_AllowRedirect_Proxy(
+ INetFwIcmpSettings * This,
+ /* [in] */ VARIANT_BOOL allow);
+
+
+void __RPC_STUB INetFwIcmpSettings_put_AllowRedirect_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propget][id] */ HRESULT STDMETHODCALLTYPE INetFwIcmpSettings_get_AllowInboundEchoRequest_Proxy(
+ INetFwIcmpSettings * This,
+ /* [retval][out] */ VARIANT_BOOL *allow);
+
+
+void __RPC_STUB INetFwIcmpSettings_get_AllowInboundEchoRequest_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propput][id] */ HRESULT STDMETHODCALLTYPE INetFwIcmpSettings_put_AllowInboundEchoRequest_Proxy(
+ INetFwIcmpSettings * This,
+ /* [in] */ VARIANT_BOOL allow);
+
+
+void __RPC_STUB INetFwIcmpSettings_put_AllowInboundEchoRequest_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propget][id] */ HRESULT STDMETHODCALLTYPE INetFwIcmpSettings_get_AllowOutboundTimeExceeded_Proxy(
+ INetFwIcmpSettings * This,
+ /* [retval][out] */ VARIANT_BOOL *allow);
+
+
+void __RPC_STUB INetFwIcmpSettings_get_AllowOutboundTimeExceeded_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propput][id] */ HRESULT STDMETHODCALLTYPE INetFwIcmpSettings_put_AllowOutboundTimeExceeded_Proxy(
+ INetFwIcmpSettings * This,
+ /* [in] */ VARIANT_BOOL allow);
+
+
+void __RPC_STUB INetFwIcmpSettings_put_AllowOutboundTimeExceeded_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propget][id] */ HRESULT STDMETHODCALLTYPE INetFwIcmpSettings_get_AllowOutboundParameterProblem_Proxy(
+ INetFwIcmpSettings * This,
+ /* [retval][out] */ VARIANT_BOOL *allow);
+
+
+void __RPC_STUB INetFwIcmpSettings_get_AllowOutboundParameterProblem_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propput][id] */ HRESULT STDMETHODCALLTYPE INetFwIcmpSettings_put_AllowOutboundParameterProblem_Proxy(
+ INetFwIcmpSettings * This,
+ /* [in] */ VARIANT_BOOL allow);
+
+
+void __RPC_STUB INetFwIcmpSettings_put_AllowOutboundParameterProblem_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propget][id] */ HRESULT STDMETHODCALLTYPE INetFwIcmpSettings_get_AllowOutboundSourceQuench_Proxy(
+ INetFwIcmpSettings * This,
+ /* [retval][out] */ VARIANT_BOOL *allow);
+
+
+void __RPC_STUB INetFwIcmpSettings_get_AllowOutboundSourceQuench_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propput][id] */ HRESULT STDMETHODCALLTYPE INetFwIcmpSettings_put_AllowOutboundSourceQuench_Proxy(
+ INetFwIcmpSettings * This,
+ /* [in] */ VARIANT_BOOL allow);
+
+
+void __RPC_STUB INetFwIcmpSettings_put_AllowOutboundSourceQuench_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propget][id] */ HRESULT STDMETHODCALLTYPE INetFwIcmpSettings_get_AllowInboundRouterRequest_Proxy(
+ INetFwIcmpSettings * This,
+ /* [retval][out] */ VARIANT_BOOL *allow);
+
+
+void __RPC_STUB INetFwIcmpSettings_get_AllowInboundRouterRequest_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propput][id] */ HRESULT STDMETHODCALLTYPE INetFwIcmpSettings_put_AllowInboundRouterRequest_Proxy(
+ INetFwIcmpSettings * This,
+ /* [in] */ VARIANT_BOOL allow);
+
+
+void __RPC_STUB INetFwIcmpSettings_put_AllowInboundRouterRequest_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propget][id] */ HRESULT STDMETHODCALLTYPE INetFwIcmpSettings_get_AllowInboundTimestampRequest_Proxy(
+ INetFwIcmpSettings * This,
+ /* [retval][out] */ VARIANT_BOOL *allow);
+
+
+void __RPC_STUB INetFwIcmpSettings_get_AllowInboundTimestampRequest_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propput][id] */ HRESULT STDMETHODCALLTYPE INetFwIcmpSettings_put_AllowInboundTimestampRequest_Proxy(
+ INetFwIcmpSettings * This,
+ /* [in] */ VARIANT_BOOL allow);
+
+
+void __RPC_STUB INetFwIcmpSettings_put_AllowInboundTimestampRequest_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propget][id] */ HRESULT STDMETHODCALLTYPE INetFwIcmpSettings_get_AllowInboundMaskRequest_Proxy(
+ INetFwIcmpSettings * This,
+ /* [retval][out] */ VARIANT_BOOL *allow);
+
+
+void __RPC_STUB INetFwIcmpSettings_get_AllowInboundMaskRequest_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propput][id] */ HRESULT STDMETHODCALLTYPE INetFwIcmpSettings_put_AllowInboundMaskRequest_Proxy(
+ INetFwIcmpSettings * This,
+ /* [in] */ VARIANT_BOOL allow);
+
+
+void __RPC_STUB INetFwIcmpSettings_put_AllowInboundMaskRequest_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propget][id] */ HRESULT STDMETHODCALLTYPE INetFwIcmpSettings_get_AllowOutboundPacketTooBig_Proxy(
+ INetFwIcmpSettings * This,
+ /* [retval][out] */ VARIANT_BOOL *allow);
+
+
+void __RPC_STUB INetFwIcmpSettings_get_AllowOutboundPacketTooBig_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propput][id] */ HRESULT STDMETHODCALLTYPE INetFwIcmpSettings_put_AllowOutboundPacketTooBig_Proxy(
+ INetFwIcmpSettings * This,
+ /* [in] */ VARIANT_BOOL allow);
+
+
+void __RPC_STUB INetFwIcmpSettings_put_AllowOutboundPacketTooBig_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+
+#endif /* __INetFwIcmpSettings_INTERFACE_DEFINED__ */
+
+
+#ifndef __INetFwOpenPort_INTERFACE_DEFINED__
+#define __INetFwOpenPort_INTERFACE_DEFINED__
+
+/* interface INetFwOpenPort */
+/* [dual][uuid][object] */
+
+
+EXTERN_C const IID IID_INetFwOpenPort;
+
+#if defined(__cplusplus) && !defined(CINTERFACE)
+
+ MIDL_INTERFACE("E0483BA0-47FF-4D9C-A6D6-7741D0B195F7")
+ INetFwOpenPort : public IDispatch
+ {
+ public:
+ virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_Name(
+ /* [retval][out] */ BSTR *name) = 0;
+
+ virtual /* [propput][id] */ HRESULT STDMETHODCALLTYPE put_Name(
+ /* [in] */ BSTR name) = 0;
+
+ virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_IpVersion(
+ /* [retval][out] */ NET_FW_IP_VERSION *ipVersion) = 0;
+
+ virtual /* [propput][id] */ HRESULT STDMETHODCALLTYPE put_IpVersion(
+ /* [in] */ NET_FW_IP_VERSION ipVersion) = 0;
+
+ virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_Protocol(
+ /* [retval][out] */ NET_FW_IP_PROTOCOL *ipProtocol) = 0;
+
+ virtual /* [propput][id] */ HRESULT STDMETHODCALLTYPE put_Protocol(
+ /* [in] */ NET_FW_IP_PROTOCOL ipProtocol) = 0;
+
+ virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_Port(
+ /* [retval][out] */ LONG *portNumber) = 0;
+
+ virtual /* [propput][id] */ HRESULT STDMETHODCALLTYPE put_Port(
+ /* [in] */ LONG portNumber) = 0;
+
+ virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_Scope(
+ /* [retval][out] */ NET_FW_SCOPE *scope) = 0;
+
+ virtual /* [propput][id] */ HRESULT STDMETHODCALLTYPE put_Scope(
+ /* [in] */ NET_FW_SCOPE scope) = 0;
+
+ virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_RemoteAddresses(
+ /* [retval][out] */ BSTR *remoteAddrs) = 0;
+
+ virtual /* [propput][id] */ HRESULT STDMETHODCALLTYPE put_RemoteAddresses(
+ /* [in] */ BSTR remoteAddrs) = 0;
+
+ virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_Enabled(
+ /* [retval][out] */ VARIANT_BOOL *enabled) = 0;
+
+ virtual /* [propput][id] */ HRESULT STDMETHODCALLTYPE put_Enabled(
+ /* [in] */ VARIANT_BOOL enabled) = 0;
+
+ virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_BuiltIn(
+ /* [retval][out] */ VARIANT_BOOL *builtIn) = 0;
+
+ };
+
+#else /* C style interface */
+
+ typedef struct INetFwOpenPortVtbl
+ {
+ BEGIN_INTERFACE
+
+ HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
+ INetFwOpenPort * This,
+ /* [in] */ REFIID riid,
+ /* [iid_is][out] */ void **ppvObject);
+
+ ULONG ( STDMETHODCALLTYPE *AddRef )(
+ INetFwOpenPort * This);
+
+ ULONG ( STDMETHODCALLTYPE *Release )(
+ INetFwOpenPort * This);
+
+ HRESULT ( STDMETHODCALLTYPE *GetTypeInfoCount )(
+ INetFwOpenPort * This,
+ /* [out] */ UINT *pctinfo);
+
+ HRESULT ( STDMETHODCALLTYPE *GetTypeInfo )(
+ INetFwOpenPort * This,
+ /* [in] */ UINT iTInfo,
+ /* [in] */ LCID lcid,
+ /* [out] */ ITypeInfo **ppTInfo);
+
+ HRESULT ( STDMETHODCALLTYPE *GetIDsOfNames )(
+ INetFwOpenPort * This,
+ /* [in] */ REFIID riid,
+ /* [size_is][in] */ LPOLESTR *rgszNames,
+ /* [in] */ UINT cNames,
+ /* [in] */ LCID lcid,
+ /* [size_is][out] */ DISPID *rgDispId);
+
+ /* [local] */ HRESULT ( STDMETHODCALLTYPE *Invoke )(
+ INetFwOpenPort * This,
+ /* [in] */ DISPID dispIdMember,
+ /* [in] */ REFIID riid,
+ /* [in] */ LCID lcid,
+ /* [in] */ WORD wFlags,
+ /* [out][in] */ DISPPARAMS *pDispParams,
+ /* [out] */ VARIANT *pVarResult,
+ /* [out] */ EXCEPINFO *pExcepInfo,
+ /* [out] */ UINT *puArgErr);
+
+ /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE *get_Name )(
+ INetFwOpenPort * This,
+ /* [retval][out] */ BSTR *name);
+
+ /* [propput][id] */ HRESULT ( STDMETHODCALLTYPE *put_Name )(
+ INetFwOpenPort * This,
+ /* [in] */ BSTR name);
+
+ /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE *get_IpVersion )(
+ INetFwOpenPort * This,
+ /* [retval][out] */ NET_FW_IP_VERSION *ipVersion);
+
+ /* [propput][id] */ HRESULT ( STDMETHODCALLTYPE *put_IpVersion )(
+ INetFwOpenPort * This,
+ /* [in] */ NET_FW_IP_VERSION ipVersion);
+
+ /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE *get_Protocol )(
+ INetFwOpenPort * This,
+ /* [retval][out] */ NET_FW_IP_PROTOCOL *ipProtocol);
+
+ /* [propput][id] */ HRESULT ( STDMETHODCALLTYPE *put_Protocol )(
+ INetFwOpenPort * This,
+ /* [in] */ NET_FW_IP_PROTOCOL ipProtocol);
+
+ /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE *get_Port )(
+ INetFwOpenPort * This,
+ /* [retval][out] */ LONG *portNumber);
+
+ /* [propput][id] */ HRESULT ( STDMETHODCALLTYPE *put_Port )(
+ INetFwOpenPort * This,
+ /* [in] */ LONG portNumber);
+
+ /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE *get_Scope )(
+ INetFwOpenPort * This,
+ /* [retval][out] */ NET_FW_SCOPE *scope);
+
+ /* [propput][id] */ HRESULT ( STDMETHODCALLTYPE *put_Scope )(
+ INetFwOpenPort * This,
+ /* [in] */ NET_FW_SCOPE scope);
+
+ /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE *get_RemoteAddresses )(
+ INetFwOpenPort * This,
+ /* [retval][out] */ BSTR *remoteAddrs);
+
+ /* [propput][id] */ HRESULT ( STDMETHODCALLTYPE *put_RemoteAddresses )(
+ INetFwOpenPort * This,
+ /* [in] */ BSTR remoteAddrs);
+
+ /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE *get_Enabled )(
+ INetFwOpenPort * This,
+ /* [retval][out] */ VARIANT_BOOL *enabled);
+
+ /* [propput][id] */ HRESULT ( STDMETHODCALLTYPE *put_Enabled )(
+ INetFwOpenPort * This,
+ /* [in] */ VARIANT_BOOL enabled);
+
+ /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE *get_BuiltIn )(
+ INetFwOpenPort * This,
+ /* [retval][out] */ VARIANT_BOOL *builtIn);
+
+ END_INTERFACE
+ } INetFwOpenPortVtbl;
+
+ interface INetFwOpenPort
+ {
+ CONST_VTBL struct INetFwOpenPortVtbl *lpVtbl;
+ };
+
+
+
+#ifdef COBJMACROS
+
+
+#define INetFwOpenPort_QueryInterface(This,riid,ppvObject) \
+ (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)
+
+#define INetFwOpenPort_AddRef(This) \
+ (This)->lpVtbl -> AddRef(This)
+
+#define INetFwOpenPort_Release(This) \
+ (This)->lpVtbl -> Release(This)
+
+
+#define INetFwOpenPort_GetTypeInfoCount(This,pctinfo) \
+ (This)->lpVtbl -> GetTypeInfoCount(This,pctinfo)
+
+#define INetFwOpenPort_GetTypeInfo(This,iTInfo,lcid,ppTInfo) \
+ (This)->lpVtbl -> GetTypeInfo(This,iTInfo,lcid,ppTInfo)
+
+#define INetFwOpenPort_GetIDsOfNames(This,riid,rgszNames,cNames,lcid,rgDispId) \
+ (This)->lpVtbl -> GetIDsOfNames(This,riid,rgszNames,cNames,lcid,rgDispId)
+
+#define INetFwOpenPort_Invoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr) \
+ (This)->lpVtbl -> Invoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr)
+
+
+#define INetFwOpenPort_get_Name(This,name) \
+ (This)->lpVtbl -> get_Name(This,name)
+
+#define INetFwOpenPort_put_Name(This,name) \
+ (This)->lpVtbl -> put_Name(This,name)
+
+#define INetFwOpenPort_get_IpVersion(This,ipVersion) \
+ (This)->lpVtbl -> get_IpVersion(This,ipVersion)
+
+#define INetFwOpenPort_put_IpVersion(This,ipVersion) \
+ (This)->lpVtbl -> put_IpVersion(This,ipVersion)
+
+#define INetFwOpenPort_get_Protocol(This,ipProtocol) \
+ (This)->lpVtbl -> get_Protocol(This,ipProtocol)
+
+#define INetFwOpenPort_put_Protocol(This,ipProtocol) \
+ (This)->lpVtbl -> put_Protocol(This,ipProtocol)
+
+#define INetFwOpenPort_get_Port(This,portNumber) \
+ (This)->lpVtbl -> get_Port(This,portNumber)
+
+#define INetFwOpenPort_put_Port(This,portNumber) \
+ (This)->lpVtbl -> put_Port(This,portNumber)
+
+#define INetFwOpenPort_get_Scope(This,scope) \
+ (This)->lpVtbl -> get_Scope(This,scope)
+
+#define INetFwOpenPort_put_Scope(This,scope) \
+ (This)->lpVtbl -> put_Scope(This,scope)
+
+#define INetFwOpenPort_get_RemoteAddresses(This,remoteAddrs) \
+ (This)->lpVtbl -> get_RemoteAddresses(This,remoteAddrs)
+
+#define INetFwOpenPort_put_RemoteAddresses(This,remoteAddrs) \
+ (This)->lpVtbl -> put_RemoteAddresses(This,remoteAddrs)
+
+#define INetFwOpenPort_get_Enabled(This,enabled) \
+ (This)->lpVtbl -> get_Enabled(This,enabled)
+
+#define INetFwOpenPort_put_Enabled(This,enabled) \
+ (This)->lpVtbl -> put_Enabled(This,enabled)
+
+#define INetFwOpenPort_get_BuiltIn(This,builtIn) \
+ (This)->lpVtbl -> get_BuiltIn(This,builtIn)
+
+#endif /* COBJMACROS */
+
+
+#endif /* C style interface */
+
+
+
+/* [propget][id] */ HRESULT STDMETHODCALLTYPE INetFwOpenPort_get_Name_Proxy(
+ INetFwOpenPort * This,
+ /* [retval][out] */ BSTR *name);
+
+
+void __RPC_STUB INetFwOpenPort_get_Name_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propput][id] */ HRESULT STDMETHODCALLTYPE INetFwOpenPort_put_Name_Proxy(
+ INetFwOpenPort * This,
+ /* [in] */ BSTR name);
+
+
+void __RPC_STUB INetFwOpenPort_put_Name_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propget][id] */ HRESULT STDMETHODCALLTYPE INetFwOpenPort_get_IpVersion_Proxy(
+ INetFwOpenPort * This,
+ /* [retval][out] */ NET_FW_IP_VERSION *ipVersion);
+
+
+void __RPC_STUB INetFwOpenPort_get_IpVersion_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propput][id] */ HRESULT STDMETHODCALLTYPE INetFwOpenPort_put_IpVersion_Proxy(
+ INetFwOpenPort * This,
+ /* [in] */ NET_FW_IP_VERSION ipVersion);
+
+
+void __RPC_STUB INetFwOpenPort_put_IpVersion_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propget][id] */ HRESULT STDMETHODCALLTYPE INetFwOpenPort_get_Protocol_Proxy(
+ INetFwOpenPort * This,
+ /* [retval][out] */ NET_FW_IP_PROTOCOL *ipProtocol);
+
+
+void __RPC_STUB INetFwOpenPort_get_Protocol_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propput][id] */ HRESULT STDMETHODCALLTYPE INetFwOpenPort_put_Protocol_Proxy(
+ INetFwOpenPort * This,
+ /* [in] */ NET_FW_IP_PROTOCOL ipProtocol);
+
+
+void __RPC_STUB INetFwOpenPort_put_Protocol_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propget][id] */ HRESULT STDMETHODCALLTYPE INetFwOpenPort_get_Port_Proxy(
+ INetFwOpenPort * This,
+ /* [retval][out] */ LONG *portNumber);
+
+
+void __RPC_STUB INetFwOpenPort_get_Port_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propput][id] */ HRESULT STDMETHODCALLTYPE INetFwOpenPort_put_Port_Proxy(
+ INetFwOpenPort * This,
+ /* [in] */ LONG portNumber);
+
+
+void __RPC_STUB INetFwOpenPort_put_Port_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propget][id] */ HRESULT STDMETHODCALLTYPE INetFwOpenPort_get_Scope_Proxy(
+ INetFwOpenPort * This,
+ /* [retval][out] */ NET_FW_SCOPE *scope);
+
+
+void __RPC_STUB INetFwOpenPort_get_Scope_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propput][id] */ HRESULT STDMETHODCALLTYPE INetFwOpenPort_put_Scope_Proxy(
+ INetFwOpenPort * This,
+ /* [in] */ NET_FW_SCOPE scope);
+
+
+void __RPC_STUB INetFwOpenPort_put_Scope_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propget][id] */ HRESULT STDMETHODCALLTYPE INetFwOpenPort_get_RemoteAddresses_Proxy(
+ INetFwOpenPort * This,
+ /* [retval][out] */ BSTR *remoteAddrs);
+
+
+void __RPC_STUB INetFwOpenPort_get_RemoteAddresses_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propput][id] */ HRESULT STDMETHODCALLTYPE INetFwOpenPort_put_RemoteAddresses_Proxy(
+ INetFwOpenPort * This,
+ /* [in] */ BSTR remoteAddrs);
+
+
+void __RPC_STUB INetFwOpenPort_put_RemoteAddresses_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propget][id] */ HRESULT STDMETHODCALLTYPE INetFwOpenPort_get_Enabled_Proxy(
+ INetFwOpenPort * This,
+ /* [retval][out] */ VARIANT_BOOL *enabled);
+
+
+void __RPC_STUB INetFwOpenPort_get_Enabled_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propput][id] */ HRESULT STDMETHODCALLTYPE INetFwOpenPort_put_Enabled_Proxy(
+ INetFwOpenPort * This,
+ /* [in] */ VARIANT_BOOL enabled);
+
+
+void __RPC_STUB INetFwOpenPort_put_Enabled_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propget][id] */ HRESULT STDMETHODCALLTYPE INetFwOpenPort_get_BuiltIn_Proxy(
+ INetFwOpenPort * This,
+ /* [retval][out] */ VARIANT_BOOL *builtIn);
+
+
+void __RPC_STUB INetFwOpenPort_get_BuiltIn_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+
+#endif /* __INetFwOpenPort_INTERFACE_DEFINED__ */
+
+
+#ifndef __INetFwOpenPorts_INTERFACE_DEFINED__
+#define __INetFwOpenPorts_INTERFACE_DEFINED__
+
+/* interface INetFwOpenPorts */
+/* [dual][uuid][object] */
+
+
+EXTERN_C const IID IID_INetFwOpenPorts;
+
+#if defined(__cplusplus) && !defined(CINTERFACE)
+
+ MIDL_INTERFACE("C0E9D7FA-E07E-430A-B19A-090CE82D92E2")
+ INetFwOpenPorts : public IDispatch
+ {
+ public:
+ virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_Count(
+ /* [retval][out] */ long *count) = 0;
+
+ virtual /* [id] */ HRESULT STDMETHODCALLTYPE Add(
+ /* [in] */ INetFwOpenPort *port) = 0;
+
+ virtual /* [id] */ HRESULT STDMETHODCALLTYPE Remove(
+ /* [in] */ LONG portNumber,
+ /* [in] */ NET_FW_IP_PROTOCOL ipProtocol) = 0;
+
+ virtual /* [id] */ HRESULT STDMETHODCALLTYPE Item(
+ /* [in] */ LONG portNumber,
+ /* [in] */ NET_FW_IP_PROTOCOL ipProtocol,
+ /* [retval][out] */ INetFwOpenPort **openPort) = 0;
+
+ virtual /* [restricted][propget][id] */ HRESULT STDMETHODCALLTYPE get__NewEnum(
+ /* [retval][out] */ IUnknown **newEnum) = 0;
+
+ };
+
+#else /* C style interface */
+
+ typedef struct INetFwOpenPortsVtbl
+ {
+ BEGIN_INTERFACE
+
+ HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
+ INetFwOpenPorts * This,
+ /* [in] */ REFIID riid,
+ /* [iid_is][out] */ void **ppvObject);
+
+ ULONG ( STDMETHODCALLTYPE *AddRef )(
+ INetFwOpenPorts * This);
+
+ ULONG ( STDMETHODCALLTYPE *Release )(
+ INetFwOpenPorts * This);
+
+ HRESULT ( STDMETHODCALLTYPE *GetTypeInfoCount )(
+ INetFwOpenPorts * This,
+ /* [out] */ UINT *pctinfo);
+
+ HRESULT ( STDMETHODCALLTYPE *GetTypeInfo )(
+ INetFwOpenPorts * This,
+ /* [in] */ UINT iTInfo,
+ /* [in] */ LCID lcid,
+ /* [out] */ ITypeInfo **ppTInfo);
+
+ HRESULT ( STDMETHODCALLTYPE *GetIDsOfNames )(
+ INetFwOpenPorts * This,
+ /* [in] */ REFIID riid,
+ /* [size_is][in] */ LPOLESTR *rgszNames,
+ /* [in] */ UINT cNames,
+ /* [in] */ LCID lcid,
+ /* [size_is][out] */ DISPID *rgDispId);
+
+ /* [local] */ HRESULT ( STDMETHODCALLTYPE *Invoke )(
+ INetFwOpenPorts * This,
+ /* [in] */ DISPID dispIdMember,
+ /* [in] */ REFIID riid,
+ /* [in] */ LCID lcid,
+ /* [in] */ WORD wFlags,
+ /* [out][in] */ DISPPARAMS *pDispParams,
+ /* [out] */ VARIANT *pVarResult,
+ /* [out] */ EXCEPINFO *pExcepInfo,
+ /* [out] */ UINT *puArgErr);
+
+ /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE *get_Count )(
+ INetFwOpenPorts * This,
+ /* [retval][out] */ long *count);
+
+ /* [id] */ HRESULT ( STDMETHODCALLTYPE *Add )(
+ INetFwOpenPorts * This,
+ /* [in] */ INetFwOpenPort *port);
+
+ /* [id] */ HRESULT ( STDMETHODCALLTYPE *Remove )(
+ INetFwOpenPorts * This,
+ /* [in] */ LONG portNumber,
+ /* [in] */ NET_FW_IP_PROTOCOL ipProtocol);
+
+ /* [id] */ HRESULT ( STDMETHODCALLTYPE *Item )(
+ INetFwOpenPorts * This,
+ /* [in] */ LONG portNumber,
+ /* [in] */ NET_FW_IP_PROTOCOL ipProtocol,
+ /* [retval][out] */ INetFwOpenPort **openPort);
+
+ /* [restricted][propget][id] */ HRESULT ( STDMETHODCALLTYPE *get__NewEnum )(
+ INetFwOpenPorts * This,
+ /* [retval][out] */ IUnknown **newEnum);
+
+ END_INTERFACE
+ } INetFwOpenPortsVtbl;
+
+ interface INetFwOpenPorts
+ {
+ CONST_VTBL struct INetFwOpenPortsVtbl *lpVtbl;
+ };
+
+
+
+#ifdef COBJMACROS
+
+
+#define INetFwOpenPorts_QueryInterface(This,riid,ppvObject) \
+ (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)
+
+#define INetFwOpenPorts_AddRef(This) \
+ (This)->lpVtbl -> AddRef(This)
+
+#define INetFwOpenPorts_Release(This) \
+ (This)->lpVtbl -> Release(This)
+
+
+#define INetFwOpenPorts_GetTypeInfoCount(This,pctinfo) \
+ (This)->lpVtbl -> GetTypeInfoCount(This,pctinfo)
+
+#define INetFwOpenPorts_GetTypeInfo(This,iTInfo,lcid,ppTInfo) \
+ (This)->lpVtbl -> GetTypeInfo(This,iTInfo,lcid,ppTInfo)
+
+#define INetFwOpenPorts_GetIDsOfNames(This,riid,rgszNames,cNames,lcid,rgDispId) \
+ (This)->lpVtbl -> GetIDsOfNames(This,riid,rgszNames,cNames,lcid,rgDispId)
+
+#define INetFwOpenPorts_Invoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr) \
+ (This)->lpVtbl -> Invoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr)
+
+
+#define INetFwOpenPorts_get_Count(This,count) \
+ (This)->lpVtbl -> get_Count(This,count)
+
+#define INetFwOpenPorts_Add(This,port) \
+ (This)->lpVtbl -> Add(This,port)
+
+#define INetFwOpenPorts_Remove(This,portNumber,ipProtocol) \
+ (This)->lpVtbl -> Remove(This,portNumber,ipProtocol)
+
+#define INetFwOpenPorts_Item(This,portNumber,ipProtocol,openPort) \
+ (This)->lpVtbl -> Item(This,portNumber,ipProtocol,openPort)
+
+#define INetFwOpenPorts_get__NewEnum(This,newEnum) \
+ (This)->lpVtbl -> get__NewEnum(This,newEnum)
+
+#endif /* COBJMACROS */
+
+
+#endif /* C style interface */
+
+
+
+/* [propget][id] */ HRESULT STDMETHODCALLTYPE INetFwOpenPorts_get_Count_Proxy(
+ INetFwOpenPorts * This,
+ /* [retval][out] */ long *count);
+
+
+void __RPC_STUB INetFwOpenPorts_get_Count_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [id] */ HRESULT STDMETHODCALLTYPE INetFwOpenPorts_Add_Proxy(
+ INetFwOpenPorts * This,
+ /* [in] */ INetFwOpenPort *port);
+
+
+void __RPC_STUB INetFwOpenPorts_Add_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [id] */ HRESULT STDMETHODCALLTYPE INetFwOpenPorts_Remove_Proxy(
+ INetFwOpenPorts * This,
+ /* [in] */ LONG portNumber,
+ /* [in] */ NET_FW_IP_PROTOCOL ipProtocol);
+
+
+void __RPC_STUB INetFwOpenPorts_Remove_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [id] */ HRESULT STDMETHODCALLTYPE INetFwOpenPorts_Item_Proxy(
+ INetFwOpenPorts * This,
+ /* [in] */ LONG portNumber,
+ /* [in] */ NET_FW_IP_PROTOCOL ipProtocol,
+ /* [retval][out] */ INetFwOpenPort **openPort);
+
+
+void __RPC_STUB INetFwOpenPorts_Item_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [restricted][propget][id] */ HRESULT STDMETHODCALLTYPE INetFwOpenPorts_get__NewEnum_Proxy(
+ INetFwOpenPorts * This,
+ /* [retval][out] */ IUnknown **newEnum);
+
+
+void __RPC_STUB INetFwOpenPorts_get__NewEnum_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+
+#endif /* __INetFwOpenPorts_INTERFACE_DEFINED__ */
+
+
+#ifndef __INetFwService_INTERFACE_DEFINED__
+#define __INetFwService_INTERFACE_DEFINED__
+
+/* interface INetFwService */
+/* [dual][uuid][object] */
+
+
+EXTERN_C const IID IID_INetFwService;
+
+#if defined(__cplusplus) && !defined(CINTERFACE)
+
+ MIDL_INTERFACE("79FD57C8-908E-4A36-9888-D5B3F0A444CF")
+ INetFwService : public IDispatch
+ {
+ public:
+ virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_Name(
+ /* [retval][out] */ BSTR *name) = 0;
+
+ virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_Type(
+ /* [retval][out] */ NET_FW_SERVICE_TYPE *type) = 0;
+
+ virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_Customized(
+ /* [retval][out] */ VARIANT_BOOL *customized) = 0;
+
+ virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_IpVersion(
+ /* [retval][out] */ NET_FW_IP_VERSION *ipVersion) = 0;
+
+ virtual /* [propput][id] */ HRESULT STDMETHODCALLTYPE put_IpVersion(
+ /* [in] */ NET_FW_IP_VERSION ipVersion) = 0;
+
+ virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_Scope(
+ /* [retval][out] */ NET_FW_SCOPE *scope) = 0;
+
+ virtual /* [propput][id] */ HRESULT STDMETHODCALLTYPE put_Scope(
+ /* [in] */ NET_FW_SCOPE scope) = 0;
+
+ virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_RemoteAddresses(
+ /* [retval][out] */ BSTR *remoteAddrs) = 0;
+
+ virtual /* [propput][id] */ HRESULT STDMETHODCALLTYPE put_RemoteAddresses(
+ /* [in] */ BSTR remoteAddrs) = 0;
+
+ virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_Enabled(
+ /* [retval][out] */ VARIANT_BOOL *enabled) = 0;
+
+ virtual /* [propput][id] */ HRESULT STDMETHODCALLTYPE put_Enabled(
+ /* [in] */ VARIANT_BOOL enabled) = 0;
+
+ virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_GloballyOpenPorts(
+ /* [retval][out] */ INetFwOpenPorts **openPorts) = 0;
+
+ };
+
+#else /* C style interface */
+
+ typedef struct INetFwServiceVtbl
+ {
+ BEGIN_INTERFACE
+
+ HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
+ INetFwService * This,
+ /* [in] */ REFIID riid,
+ /* [iid_is][out] */ void **ppvObject);
+
+ ULONG ( STDMETHODCALLTYPE *AddRef )(
+ INetFwService * This);
+
+ ULONG ( STDMETHODCALLTYPE *Release )(
+ INetFwService * This);
+
+ HRESULT ( STDMETHODCALLTYPE *GetTypeInfoCount )(
+ INetFwService * This,
+ /* [out] */ UINT *pctinfo);
+
+ HRESULT ( STDMETHODCALLTYPE *GetTypeInfo )(
+ INetFwService * This,
+ /* [in] */ UINT iTInfo,
+ /* [in] */ LCID lcid,
+ /* [out] */ ITypeInfo **ppTInfo);
+
+ HRESULT ( STDMETHODCALLTYPE *GetIDsOfNames )(
+ INetFwService * This,
+ /* [in] */ REFIID riid,
+ /* [size_is][in] */ LPOLESTR *rgszNames,
+ /* [in] */ UINT cNames,
+ /* [in] */ LCID lcid,
+ /* [size_is][out] */ DISPID *rgDispId);
+
+ /* [local] */ HRESULT ( STDMETHODCALLTYPE *Invoke )(
+ INetFwService * This,
+ /* [in] */ DISPID dispIdMember,
+ /* [in] */ REFIID riid,
+ /* [in] */ LCID lcid,
+ /* [in] */ WORD wFlags,
+ /* [out][in] */ DISPPARAMS *pDispParams,
+ /* [out] */ VARIANT *pVarResult,
+ /* [out] */ EXCEPINFO *pExcepInfo,
+ /* [out] */ UINT *puArgErr);
+
+ /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE *get_Name )(
+ INetFwService * This,
+ /* [retval][out] */ BSTR *name);
+
+ /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE *get_Type )(
+ INetFwService * This,
+ /* [retval][out] */ NET_FW_SERVICE_TYPE *type);
+
+ /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE *get_Customized )(
+ INetFwService * This,
+ /* [retval][out] */ VARIANT_BOOL *customized);
+
+ /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE *get_IpVersion )(
+ INetFwService * This,
+ /* [retval][out] */ NET_FW_IP_VERSION *ipVersion);
+
+ /* [propput][id] */ HRESULT ( STDMETHODCALLTYPE *put_IpVersion )(
+ INetFwService * This,
+ /* [in] */ NET_FW_IP_VERSION ipVersion);
+
+ /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE *get_Scope )(
+ INetFwService * This,
+ /* [retval][out] */ NET_FW_SCOPE *scope);
+
+ /* [propput][id] */ HRESULT ( STDMETHODCALLTYPE *put_Scope )(
+ INetFwService * This,
+ /* [in] */ NET_FW_SCOPE scope);
+
+ /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE *get_RemoteAddresses )(
+ INetFwService * This,
+ /* [retval][out] */ BSTR *remoteAddrs);
+
+ /* [propput][id] */ HRESULT ( STDMETHODCALLTYPE *put_RemoteAddresses )(
+ INetFwService * This,
+ /* [in] */ BSTR remoteAddrs);
+
+ /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE *get_Enabled )(
+ INetFwService * This,
+ /* [retval][out] */ VARIANT_BOOL *enabled);
+
+ /* [propput][id] */ HRESULT ( STDMETHODCALLTYPE *put_Enabled )(
+ INetFwService * This,
+ /* [in] */ VARIANT_BOOL enabled);
+
+ /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE *get_GloballyOpenPorts )(
+ INetFwService * This,
+ /* [retval][out] */ INetFwOpenPorts **openPorts);
+
+ END_INTERFACE
+ } INetFwServiceVtbl;
+
+ interface INetFwService
+ {
+ CONST_VTBL struct INetFwServiceVtbl *lpVtbl;
+ };
+
+
+
+#ifdef COBJMACROS
+
+
+#define INetFwService_QueryInterface(This,riid,ppvObject) \
+ (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)
+
+#define INetFwService_AddRef(This) \
+ (This)->lpVtbl -> AddRef(This)
+
+#define INetFwService_Release(This) \
+ (This)->lpVtbl -> Release(This)
+
+
+#define INetFwService_GetTypeInfoCount(This,pctinfo) \
+ (This)->lpVtbl -> GetTypeInfoCount(This,pctinfo)
+
+#define INetFwService_GetTypeInfo(This,iTInfo,lcid,ppTInfo) \
+ (This)->lpVtbl -> GetTypeInfo(This,iTInfo,lcid,ppTInfo)
+
+#define INetFwService_GetIDsOfNames(This,riid,rgszNames,cNames,lcid,rgDispId) \
+ (This)->lpVtbl -> GetIDsOfNames(This,riid,rgszNames,cNames,lcid,rgDispId)
+
+#define INetFwService_Invoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr) \
+ (This)->lpVtbl -> Invoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr)
+
+
+#define INetFwService_get_Name(This,name) \
+ (This)->lpVtbl -> get_Name(This,name)
+
+#define INetFwService_get_Type(This,type) \
+ (This)->lpVtbl -> get_Type(This,type)
+
+#define INetFwService_get_Customized(This,customized) \
+ (This)->lpVtbl -> get_Customized(This,customized)
+
+#define INetFwService_get_IpVersion(This,ipVersion) \
+ (This)->lpVtbl -> get_IpVersion(This,ipVersion)
+
+#define INetFwService_put_IpVersion(This,ipVersion) \
+ (This)->lpVtbl -> put_IpVersion(This,ipVersion)
+
+#define INetFwService_get_Scope(This,scope) \
+ (This)->lpVtbl -> get_Scope(This,scope)
+
+#define INetFwService_put_Scope(This,scope) \
+ (This)->lpVtbl -> put_Scope(This,scope)
+
+#define INetFwService_get_RemoteAddresses(This,remoteAddrs) \
+ (This)->lpVtbl -> get_RemoteAddresses(This,remoteAddrs)
+
+#define INetFwService_put_RemoteAddresses(This,remoteAddrs) \
+ (This)->lpVtbl -> put_RemoteAddresses(This,remoteAddrs)
+
+#define INetFwService_get_Enabled(This,enabled) \
+ (This)->lpVtbl -> get_Enabled(This,enabled)
+
+#define INetFwService_put_Enabled(This,enabled) \
+ (This)->lpVtbl -> put_Enabled(This,enabled)
+
+#define INetFwService_get_GloballyOpenPorts(This,openPorts) \
+ (This)->lpVtbl -> get_GloballyOpenPorts(This,openPorts)
+
+#endif /* COBJMACROS */
+
+
+#endif /* C style interface */
+
+
+
+/* [propget][id] */ HRESULT STDMETHODCALLTYPE INetFwService_get_Name_Proxy(
+ INetFwService * This,
+ /* [retval][out] */ BSTR *name);
+
+
+void __RPC_STUB INetFwService_get_Name_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propget][id] */ HRESULT STDMETHODCALLTYPE INetFwService_get_Type_Proxy(
+ INetFwService * This,
+ /* [retval][out] */ NET_FW_SERVICE_TYPE *type);
+
+
+void __RPC_STUB INetFwService_get_Type_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propget][id] */ HRESULT STDMETHODCALLTYPE INetFwService_get_Customized_Proxy(
+ INetFwService * This,
+ /* [retval][out] */ VARIANT_BOOL *customized);
+
+
+void __RPC_STUB INetFwService_get_Customized_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propget][id] */ HRESULT STDMETHODCALLTYPE INetFwService_get_IpVersion_Proxy(
+ INetFwService * This,
+ /* [retval][out] */ NET_FW_IP_VERSION *ipVersion);
+
+
+void __RPC_STUB INetFwService_get_IpVersion_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propput][id] */ HRESULT STDMETHODCALLTYPE INetFwService_put_IpVersion_Proxy(
+ INetFwService * This,
+ /* [in] */ NET_FW_IP_VERSION ipVersion);
+
+
+void __RPC_STUB INetFwService_put_IpVersion_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propget][id] */ HRESULT STDMETHODCALLTYPE INetFwService_get_Scope_Proxy(
+ INetFwService * This,
+ /* [retval][out] */ NET_FW_SCOPE *scope);
+
+
+void __RPC_STUB INetFwService_get_Scope_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propput][id] */ HRESULT STDMETHODCALLTYPE INetFwService_put_Scope_Proxy(
+ INetFwService * This,
+ /* [in] */ NET_FW_SCOPE scope);
+
+
+void __RPC_STUB INetFwService_put_Scope_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propget][id] */ HRESULT STDMETHODCALLTYPE INetFwService_get_RemoteAddresses_Proxy(
+ INetFwService * This,
+ /* [retval][out] */ BSTR *remoteAddrs);
+
+
+void __RPC_STUB INetFwService_get_RemoteAddresses_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propput][id] */ HRESULT STDMETHODCALLTYPE INetFwService_put_RemoteAddresses_Proxy(
+ INetFwService * This,
+ /* [in] */ BSTR remoteAddrs);
+
+
+void __RPC_STUB INetFwService_put_RemoteAddresses_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propget][id] */ HRESULT STDMETHODCALLTYPE INetFwService_get_Enabled_Proxy(
+ INetFwService * This,
+ /* [retval][out] */ VARIANT_BOOL *enabled);
+
+
+void __RPC_STUB INetFwService_get_Enabled_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propput][id] */ HRESULT STDMETHODCALLTYPE INetFwService_put_Enabled_Proxy(
+ INetFwService * This,
+ /* [in] */ VARIANT_BOOL enabled);
+
+
+void __RPC_STUB INetFwService_put_Enabled_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propget][id] */ HRESULT STDMETHODCALLTYPE INetFwService_get_GloballyOpenPorts_Proxy(
+ INetFwService * This,
+ /* [retval][out] */ INetFwOpenPorts **openPorts);
+
+
+void __RPC_STUB INetFwService_get_GloballyOpenPorts_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+
+#endif /* __INetFwService_INTERFACE_DEFINED__ */
+
+
+#ifndef __INetFwServices_INTERFACE_DEFINED__
+#define __INetFwServices_INTERFACE_DEFINED__
+
+/* interface INetFwServices */
+/* [dual][uuid][object] */
+
+
+EXTERN_C const IID IID_INetFwServices;
+
+#if defined(__cplusplus) && !defined(CINTERFACE)
+
+ MIDL_INTERFACE("79649BB4-903E-421B-94C9-79848E79F6EE")
+ INetFwServices : public IDispatch
+ {
+ public:
+ virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_Count(
+ /* [retval][out] */ long *count) = 0;
+
+ virtual /* [id] */ HRESULT STDMETHODCALLTYPE Item(
+ /* [in] */ NET_FW_SERVICE_TYPE svcType,
+ /* [retval][out] */ INetFwService **service) = 0;
+
+ virtual /* [restricted][propget][id] */ HRESULT STDMETHODCALLTYPE get__NewEnum(
+ /* [retval][out] */ IUnknown **newEnum) = 0;
+
+ };
+
+#else /* C style interface */
+
+ typedef struct INetFwServicesVtbl
+ {
+ BEGIN_INTERFACE
+
+ HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
+ INetFwServices * This,
+ /* [in] */ REFIID riid,
+ /* [iid_is][out] */ void **ppvObject);
+
+ ULONG ( STDMETHODCALLTYPE *AddRef )(
+ INetFwServices * This);
+
+ ULONG ( STDMETHODCALLTYPE *Release )(
+ INetFwServices * This);
+
+ HRESULT ( STDMETHODCALLTYPE *GetTypeInfoCount )(
+ INetFwServices * This,
+ /* [out] */ UINT *pctinfo);
+
+ HRESULT ( STDMETHODCALLTYPE *GetTypeInfo )(
+ INetFwServices * This,
+ /* [in] */ UINT iTInfo,
+ /* [in] */ LCID lcid,
+ /* [out] */ ITypeInfo **ppTInfo);
+
+ HRESULT ( STDMETHODCALLTYPE *GetIDsOfNames )(
+ INetFwServices * This,
+ /* [in] */ REFIID riid,
+ /* [size_is][in] */ LPOLESTR *rgszNames,
+ /* [in] */ UINT cNames,
+ /* [in] */ LCID lcid,
+ /* [size_is][out] */ DISPID *rgDispId);
+
+ /* [local] */ HRESULT ( STDMETHODCALLTYPE *Invoke )(
+ INetFwServices * This,
+ /* [in] */ DISPID dispIdMember,
+ /* [in] */ REFIID riid,
+ /* [in] */ LCID lcid,
+ /* [in] */ WORD wFlags,
+ /* [out][in] */ DISPPARAMS *pDispParams,
+ /* [out] */ VARIANT *pVarResult,
+ /* [out] */ EXCEPINFO *pExcepInfo,
+ /* [out] */ UINT *puArgErr);
+
+ /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE *get_Count )(
+ INetFwServices * This,
+ /* [retval][out] */ long *count);
+
+ /* [id] */ HRESULT ( STDMETHODCALLTYPE *Item )(
+ INetFwServices * This,
+ /* [in] */ NET_FW_SERVICE_TYPE svcType,
+ /* [retval][out] */ INetFwService **service);
+
+ /* [restricted][propget][id] */ HRESULT ( STDMETHODCALLTYPE *get__NewEnum )(
+ INetFwServices * This,
+ /* [retval][out] */ IUnknown **newEnum);
+
+ END_INTERFACE
+ } INetFwServicesVtbl;
+
+ interface INetFwServices
+ {
+ CONST_VTBL struct INetFwServicesVtbl *lpVtbl;
+ };
+
+
+
+#ifdef COBJMACROS
+
+
+#define INetFwServices_QueryInterface(This,riid,ppvObject) \
+ (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)
+
+#define INetFwServices_AddRef(This) \
+ (This)->lpVtbl -> AddRef(This)
+
+#define INetFwServices_Release(This) \
+ (This)->lpVtbl -> Release(This)
+
+
+#define INetFwServices_GetTypeInfoCount(This,pctinfo) \
+ (This)->lpVtbl -> GetTypeInfoCount(This,pctinfo)
+
+#define INetFwServices_GetTypeInfo(This,iTInfo,lcid,ppTInfo) \
+ (This)->lpVtbl -> GetTypeInfo(This,iTInfo,lcid,ppTInfo)
+
+#define INetFwServices_GetIDsOfNames(This,riid,rgszNames,cNames,lcid,rgDispId) \
+ (This)->lpVtbl -> GetIDsOfNames(This,riid,rgszNames,cNames,lcid,rgDispId)
+
+#define INetFwServices_Invoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr) \
+ (This)->lpVtbl -> Invoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr)
+
+
+#define INetFwServices_get_Count(This,count) \
+ (This)->lpVtbl -> get_Count(This,count)
+
+#define INetFwServices_Item(This,svcType,service) \
+ (This)->lpVtbl -> Item(This,svcType,service)
+
+#define INetFwServices_get__NewEnum(This,newEnum) \
+ (This)->lpVtbl -> get__NewEnum(This,newEnum)
+
+#endif /* COBJMACROS */
+
+
+#endif /* C style interface */
+
+
+
+/* [propget][id] */ HRESULT STDMETHODCALLTYPE INetFwServices_get_Count_Proxy(
+ INetFwServices * This,
+ /* [retval][out] */ long *count);
+
+
+void __RPC_STUB INetFwServices_get_Count_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [id] */ HRESULT STDMETHODCALLTYPE INetFwServices_Item_Proxy(
+ INetFwServices * This,
+ /* [in] */ NET_FW_SERVICE_TYPE svcType,
+ /* [retval][out] */ INetFwService **service);
+
+
+void __RPC_STUB INetFwServices_Item_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [restricted][propget][id] */ HRESULT STDMETHODCALLTYPE INetFwServices_get__NewEnum_Proxy(
+ INetFwServices * This,
+ /* [retval][out] */ IUnknown **newEnum);
+
+
+void __RPC_STUB INetFwServices_get__NewEnum_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+
+#endif /* __INetFwServices_INTERFACE_DEFINED__ */
+
+
+#ifndef __INetFwAuthorizedApplication_INTERFACE_DEFINED__
+#define __INetFwAuthorizedApplication_INTERFACE_DEFINED__
+
+/* interface INetFwAuthorizedApplication */
+/* [dual][uuid][object] */
+
+
+EXTERN_C const IID IID_INetFwAuthorizedApplication;
+
+#if defined(__cplusplus) && !defined(CINTERFACE)
+
+ MIDL_INTERFACE("B5E64FFA-C2C5-444E-A301-FB5E00018050")
+ INetFwAuthorizedApplication : public IDispatch
+ {
+ public:
+ virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_Name(
+ /* [retval][out] */ BSTR *name) = 0;
+
+ virtual /* [propput][id] */ HRESULT STDMETHODCALLTYPE put_Name(
+ /* [in] */ BSTR name) = 0;
+
+ virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_ProcessImageFileName(
+ /* [retval][out] */ BSTR *imageFileName) = 0;
+
+ virtual /* [propput][id] */ HRESULT STDMETHODCALLTYPE put_ProcessImageFileName(
+ /* [in] */ BSTR imageFileName) = 0;
+
+ virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_IpVersion(
+ /* [retval][out] */ NET_FW_IP_VERSION *ipVersion) = 0;
+
+ virtual /* [propput][id] */ HRESULT STDMETHODCALLTYPE put_IpVersion(
+ /* [in] */ NET_FW_IP_VERSION ipVersion) = 0;
+
+ virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_Scope(
+ /* [retval][out] */ NET_FW_SCOPE *scope) = 0;
+
+ virtual /* [propput][id] */ HRESULT STDMETHODCALLTYPE put_Scope(
+ /* [in] */ NET_FW_SCOPE scope) = 0;
+
+ virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_RemoteAddresses(
+ /* [retval][out] */ BSTR *remoteAddrs) = 0;
+
+ virtual /* [propput][id] */ HRESULT STDMETHODCALLTYPE put_RemoteAddresses(
+ /* [in] */ BSTR remoteAddrs) = 0;
+
+ virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_Enabled(
+ /* [retval][out] */ VARIANT_BOOL *enabled) = 0;
+
+ virtual /* [propput][id] */ HRESULT STDMETHODCALLTYPE put_Enabled(
+ /* [in] */ VARIANT_BOOL enabled) = 0;
+
+ };
+
+#else /* C style interface */
+
+ typedef struct INetFwAuthorizedApplicationVtbl
+ {
+ BEGIN_INTERFACE
+
+ HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
+ INetFwAuthorizedApplication * This,
+ /* [in] */ REFIID riid,
+ /* [iid_is][out] */ void **ppvObject);
+
+ ULONG ( STDMETHODCALLTYPE *AddRef )(
+ INetFwAuthorizedApplication * This);
+
+ ULONG ( STDMETHODCALLTYPE *Release )(
+ INetFwAuthorizedApplication * This);
+
+ HRESULT ( STDMETHODCALLTYPE *GetTypeInfoCount )(
+ INetFwAuthorizedApplication * This,
+ /* [out] */ UINT *pctinfo);
+
+ HRESULT ( STDMETHODCALLTYPE *GetTypeInfo )(
+ INetFwAuthorizedApplication * This,
+ /* [in] */ UINT iTInfo,
+ /* [in] */ LCID lcid,
+ /* [out] */ ITypeInfo **ppTInfo);
+
+ HRESULT ( STDMETHODCALLTYPE *GetIDsOfNames )(
+ INetFwAuthorizedApplication * This,
+ /* [in] */ REFIID riid,
+ /* [size_is][in] */ LPOLESTR *rgszNames,
+ /* [in] */ UINT cNames,
+ /* [in] */ LCID lcid,
+ /* [size_is][out] */ DISPID *rgDispId);
+
+ /* [local] */ HRESULT ( STDMETHODCALLTYPE *Invoke )(
+ INetFwAuthorizedApplication * This,
+ /* [in] */ DISPID dispIdMember,
+ /* [in] */ REFIID riid,
+ /* [in] */ LCID lcid,
+ /* [in] */ WORD wFlags,
+ /* [out][in] */ DISPPARAMS *pDispParams,
+ /* [out] */ VARIANT *pVarResult,
+ /* [out] */ EXCEPINFO *pExcepInfo,
+ /* [out] */ UINT *puArgErr);
+
+ /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE *get_Name )(
+ INetFwAuthorizedApplication * This,
+ /* [retval][out] */ BSTR *name);
+
+ /* [propput][id] */ HRESULT ( STDMETHODCALLTYPE *put_Name )(
+ INetFwAuthorizedApplication * This,
+ /* [in] */ BSTR name);
+
+ /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE *get_ProcessImageFileName )(
+ INetFwAuthorizedApplication * This,
+ /* [retval][out] */ BSTR *imageFileName);
+
+ /* [propput][id] */ HRESULT ( STDMETHODCALLTYPE *put_ProcessImageFileName )(
+ INetFwAuthorizedApplication * This,
+ /* [in] */ BSTR imageFileName);
+
+ /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE *get_IpVersion )(
+ INetFwAuthorizedApplication * This,
+ /* [retval][out] */ NET_FW_IP_VERSION *ipVersion);
+
+ /* [propput][id] */ HRESULT ( STDMETHODCALLTYPE *put_IpVersion )(
+ INetFwAuthorizedApplication * This,
+ /* [in] */ NET_FW_IP_VERSION ipVersion);
+
+ /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE *get_Scope )(
+ INetFwAuthorizedApplication * This,
+ /* [retval][out] */ NET_FW_SCOPE *scope);
+
+ /* [propput][id] */ HRESULT ( STDMETHODCALLTYPE *put_Scope )(
+ INetFwAuthorizedApplication * This,
+ /* [in] */ NET_FW_SCOPE scope);
+
+ /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE *get_RemoteAddresses )(
+ INetFwAuthorizedApplication * This,
+ /* [retval][out] */ BSTR *remoteAddrs);
+
+ /* [propput][id] */ HRESULT ( STDMETHODCALLTYPE *put_RemoteAddresses )(
+ INetFwAuthorizedApplication * This,
+ /* [in] */ BSTR remoteAddrs);
+
+ /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE *get_Enabled )(
+ INetFwAuthorizedApplication * This,
+ /* [retval][out] */ VARIANT_BOOL *enabled);
+
+ /* [propput][id] */ HRESULT ( STDMETHODCALLTYPE *put_Enabled )(
+ INetFwAuthorizedApplication * This,
+ /* [in] */ VARIANT_BOOL enabled);
+
+ END_INTERFACE
+ } INetFwAuthorizedApplicationVtbl;
+
+ interface INetFwAuthorizedApplication
+ {
+ CONST_VTBL struct INetFwAuthorizedApplicationVtbl *lpVtbl;
+ };
+
+
+
+#ifdef COBJMACROS
+
+
+#define INetFwAuthorizedApplication_QueryInterface(This,riid,ppvObject) \
+ (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)
+
+#define INetFwAuthorizedApplication_AddRef(This) \
+ (This)->lpVtbl -> AddRef(This)
+
+#define INetFwAuthorizedApplication_Release(This) \
+ (This)->lpVtbl -> Release(This)
+
+
+#define INetFwAuthorizedApplication_GetTypeInfoCount(This,pctinfo) \
+ (This)->lpVtbl -> GetTypeInfoCount(This,pctinfo)
+
+#define INetFwAuthorizedApplication_GetTypeInfo(This,iTInfo,lcid,ppTInfo) \
+ (This)->lpVtbl -> GetTypeInfo(This,iTInfo,lcid,ppTInfo)
+
+#define INetFwAuthorizedApplication_GetIDsOfNames(This,riid,rgszNames,cNames,lcid,rgDispId) \
+ (This)->lpVtbl -> GetIDsOfNames(This,riid,rgszNames,cNames,lcid,rgDispId)
+
+#define INetFwAuthorizedApplication_Invoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr) \
+ (This)->lpVtbl -> Invoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr)
+
+
+#define INetFwAuthorizedApplication_get_Name(This,name) \
+ (This)->lpVtbl -> get_Name(This,name)
+
+#define INetFwAuthorizedApplication_put_Name(This,name) \
+ (This)->lpVtbl -> put_Name(This,name)
+
+#define INetFwAuthorizedApplication_get_ProcessImageFileName(This,imageFileName) \
+ (This)->lpVtbl -> get_ProcessImageFileName(This,imageFileName)
+
+#define INetFwAuthorizedApplication_put_ProcessImageFileName(This,imageFileName) \
+ (This)->lpVtbl -> put_ProcessImageFileName(This,imageFileName)
+
+#define INetFwAuthorizedApplication_get_IpVersion(This,ipVersion) \
+ (This)->lpVtbl -> get_IpVersion(This,ipVersion)
+
+#define INetFwAuthorizedApplication_put_IpVersion(This,ipVersion) \
+ (This)->lpVtbl -> put_IpVersion(This,ipVersion)
+
+#define INetFwAuthorizedApplication_get_Scope(This,scope) \
+ (This)->lpVtbl -> get_Scope(This,scope)
+
+#define INetFwAuthorizedApplication_put_Scope(This,scope) \
+ (This)->lpVtbl -> put_Scope(This,scope)
+
+#define INetFwAuthorizedApplication_get_RemoteAddresses(This,remoteAddrs) \
+ (This)->lpVtbl -> get_RemoteAddresses(This,remoteAddrs)
+
+#define INetFwAuthorizedApplication_put_RemoteAddresses(This,remoteAddrs) \
+ (This)->lpVtbl -> put_RemoteAddresses(This,remoteAddrs)
+
+#define INetFwAuthorizedApplication_get_Enabled(This,enabled) \
+ (This)->lpVtbl -> get_Enabled(This,enabled)
+
+#define INetFwAuthorizedApplication_put_Enabled(This,enabled) \
+ (This)->lpVtbl -> put_Enabled(This,enabled)
+
+#endif /* COBJMACROS */
+
+
+#endif /* C style interface */
+
+
+
+/* [propget][id] */ HRESULT STDMETHODCALLTYPE INetFwAuthorizedApplication_get_Name_Proxy(
+ INetFwAuthorizedApplication * This,
+ /* [retval][out] */ BSTR *name);
+
+
+void __RPC_STUB INetFwAuthorizedApplication_get_Name_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propput][id] */ HRESULT STDMETHODCALLTYPE INetFwAuthorizedApplication_put_Name_Proxy(
+ INetFwAuthorizedApplication * This,
+ /* [in] */ BSTR name);
+
+
+void __RPC_STUB INetFwAuthorizedApplication_put_Name_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propget][id] */ HRESULT STDMETHODCALLTYPE INetFwAuthorizedApplication_get_ProcessImageFileName_Proxy(
+ INetFwAuthorizedApplication * This,
+ /* [retval][out] */ BSTR *imageFileName);
+
+
+void __RPC_STUB INetFwAuthorizedApplication_get_ProcessImageFileName_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propput][id] */ HRESULT STDMETHODCALLTYPE INetFwAuthorizedApplication_put_ProcessImageFileName_Proxy(
+ INetFwAuthorizedApplication * This,
+ /* [in] */ BSTR imageFileName);
+
+
+void __RPC_STUB INetFwAuthorizedApplication_put_ProcessImageFileName_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propget][id] */ HRESULT STDMETHODCALLTYPE INetFwAuthorizedApplication_get_IpVersion_Proxy(
+ INetFwAuthorizedApplication * This,
+ /* [retval][out] */ NET_FW_IP_VERSION *ipVersion);
+
+
+void __RPC_STUB INetFwAuthorizedApplication_get_IpVersion_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propput][id] */ HRESULT STDMETHODCALLTYPE INetFwAuthorizedApplication_put_IpVersion_Proxy(
+ INetFwAuthorizedApplication * This,
+ /* [in] */ NET_FW_IP_VERSION ipVersion);
+
+
+void __RPC_STUB INetFwAuthorizedApplication_put_IpVersion_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propget][id] */ HRESULT STDMETHODCALLTYPE INetFwAuthorizedApplication_get_Scope_Proxy(
+ INetFwAuthorizedApplication * This,
+ /* [retval][out] */ NET_FW_SCOPE *scope);
+
+
+void __RPC_STUB INetFwAuthorizedApplication_get_Scope_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propput][id] */ HRESULT STDMETHODCALLTYPE INetFwAuthorizedApplication_put_Scope_Proxy(
+ INetFwAuthorizedApplication * This,
+ /* [in] */ NET_FW_SCOPE scope);
+
+
+void __RPC_STUB INetFwAuthorizedApplication_put_Scope_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propget][id] */ HRESULT STDMETHODCALLTYPE INetFwAuthorizedApplication_get_RemoteAddresses_Proxy(
+ INetFwAuthorizedApplication * This,
+ /* [retval][out] */ BSTR *remoteAddrs);
+
+
+void __RPC_STUB INetFwAuthorizedApplication_get_RemoteAddresses_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propput][id] */ HRESULT STDMETHODCALLTYPE INetFwAuthorizedApplication_put_RemoteAddresses_Proxy(
+ INetFwAuthorizedApplication * This,
+ /* [in] */ BSTR remoteAddrs);
+
+
+void __RPC_STUB INetFwAuthorizedApplication_put_RemoteAddresses_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propget][id] */ HRESULT STDMETHODCALLTYPE INetFwAuthorizedApplication_get_Enabled_Proxy(
+ INetFwAuthorizedApplication * This,
+ /* [retval][out] */ VARIANT_BOOL *enabled);
+
+
+void __RPC_STUB INetFwAuthorizedApplication_get_Enabled_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propput][id] */ HRESULT STDMETHODCALLTYPE INetFwAuthorizedApplication_put_Enabled_Proxy(
+ INetFwAuthorizedApplication * This,
+ /* [in] */ VARIANT_BOOL enabled);
+
+
+void __RPC_STUB INetFwAuthorizedApplication_put_Enabled_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+
+#endif /* __INetFwAuthorizedApplication_INTERFACE_DEFINED__ */
+
+
+#ifndef __INetFwAuthorizedApplications_INTERFACE_DEFINED__
+#define __INetFwAuthorizedApplications_INTERFACE_DEFINED__
+
+/* interface INetFwAuthorizedApplications */
+/* [dual][uuid][object] */
+
+
+EXTERN_C const IID IID_INetFwAuthorizedApplications;
+
+#if defined(__cplusplus) && !defined(CINTERFACE)
+
+ MIDL_INTERFACE("644EFD52-CCF9-486C-97A2-39F352570B30")
+ INetFwAuthorizedApplications : public IDispatch
+ {
+ public:
+ virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_Count(
+ /* [retval][out] */ long *count) = 0;
+
+ virtual /* [id] */ HRESULT STDMETHODCALLTYPE Add(
+ /* [in] */ INetFwAuthorizedApplication *app) = 0;
+
+ virtual /* [id] */ HRESULT STDMETHODCALLTYPE Remove(
+ /* [in] */ BSTR imageFileName) = 0;
+
+ virtual /* [id] */ HRESULT STDMETHODCALLTYPE Item(
+ /* [in] */ BSTR imageFileName,
+ /* [retval][out] */ INetFwAuthorizedApplication **app) = 0;
+
+ virtual /* [restricted][propget][id] */ HRESULT STDMETHODCALLTYPE get__NewEnum(
+ /* [retval][out] */ IUnknown **newEnum) = 0;
+
+ };
+
+#else /* C style interface */
+
+ typedef struct INetFwAuthorizedApplicationsVtbl
+ {
+ BEGIN_INTERFACE
+
+ HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
+ INetFwAuthorizedApplications * This,
+ /* [in] */ REFIID riid,
+ /* [iid_is][out] */ void **ppvObject);
+
+ ULONG ( STDMETHODCALLTYPE *AddRef )(
+ INetFwAuthorizedApplications * This);
+
+ ULONG ( STDMETHODCALLTYPE *Release )(
+ INetFwAuthorizedApplications * This);
+
+ HRESULT ( STDMETHODCALLTYPE *GetTypeInfoCount )(
+ INetFwAuthorizedApplications * This,
+ /* [out] */ UINT *pctinfo);
+
+ HRESULT ( STDMETHODCALLTYPE *GetTypeInfo )(
+ INetFwAuthorizedApplications * This,
+ /* [in] */ UINT iTInfo,
+ /* [in] */ LCID lcid,
+ /* [out] */ ITypeInfo **ppTInfo);
+
+ HRESULT ( STDMETHODCALLTYPE *GetIDsOfNames )(
+ INetFwAuthorizedApplications * This,
+ /* [in] */ REFIID riid,
+ /* [size_is][in] */ LPOLESTR *rgszNames,
+ /* [in] */ UINT cNames,
+ /* [in] */ LCID lcid,
+ /* [size_is][out] */ DISPID *rgDispId);
+
+ /* [local] */ HRESULT ( STDMETHODCALLTYPE *Invoke )(
+ INetFwAuthorizedApplications * This,
+ /* [in] */ DISPID dispIdMember,
+ /* [in] */ REFIID riid,
+ /* [in] */ LCID lcid,
+ /* [in] */ WORD wFlags,
+ /* [out][in] */ DISPPARAMS *pDispParams,
+ /* [out] */ VARIANT *pVarResult,
+ /* [out] */ EXCEPINFO *pExcepInfo,
+ /* [out] */ UINT *puArgErr);
+
+ /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE *get_Count )(
+ INetFwAuthorizedApplications * This,
+ /* [retval][out] */ long *count);
+
+ /* [id] */ HRESULT ( STDMETHODCALLTYPE *Add )(
+ INetFwAuthorizedApplications * This,
+ /* [in] */ INetFwAuthorizedApplication *app);
+
+ /* [id] */ HRESULT ( STDMETHODCALLTYPE *Remove )(
+ INetFwAuthorizedApplications * This,
+ /* [in] */ BSTR imageFileName);
+
+ /* [id] */ HRESULT ( STDMETHODCALLTYPE *Item )(
+ INetFwAuthorizedApplications * This,
+ /* [in] */ BSTR imageFileName,
+ /* [retval][out] */ INetFwAuthorizedApplication **app);
+
+ /* [restricted][propget][id] */ HRESULT ( STDMETHODCALLTYPE *get__NewEnum )(
+ INetFwAuthorizedApplications * This,
+ /* [retval][out] */ IUnknown **newEnum);
+
+ END_INTERFACE
+ } INetFwAuthorizedApplicationsVtbl;
+
+ interface INetFwAuthorizedApplications
+ {
+ CONST_VTBL struct INetFwAuthorizedApplicationsVtbl *lpVtbl;
+ };
+
+
+
+#ifdef COBJMACROS
+
+
+#define INetFwAuthorizedApplications_QueryInterface(This,riid,ppvObject) \
+ (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)
+
+#define INetFwAuthorizedApplications_AddRef(This) \
+ (This)->lpVtbl -> AddRef(This)
+
+#define INetFwAuthorizedApplications_Release(This) \
+ (This)->lpVtbl -> Release(This)
+
+
+#define INetFwAuthorizedApplications_GetTypeInfoCount(This,pctinfo) \
+ (This)->lpVtbl -> GetTypeInfoCount(This,pctinfo)
+
+#define INetFwAuthorizedApplications_GetTypeInfo(This,iTInfo,lcid,ppTInfo) \
+ (This)->lpVtbl -> GetTypeInfo(This,iTInfo,lcid,ppTInfo)
+
+#define INetFwAuthorizedApplications_GetIDsOfNames(This,riid,rgszNames,cNames,lcid,rgDispId) \
+ (This)->lpVtbl -> GetIDsOfNames(This,riid,rgszNames,cNames,lcid,rgDispId)
+
+#define INetFwAuthorizedApplications_Invoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr) \
+ (This)->lpVtbl -> Invoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr)
+
+
+#define INetFwAuthorizedApplications_get_Count(This,count) \
+ (This)->lpVtbl -> get_Count(This,count)
+
+#define INetFwAuthorizedApplications_Add(This,app) \
+ (This)->lpVtbl -> Add(This,app)
+
+#define INetFwAuthorizedApplications_Remove(This,imageFileName) \
+ (This)->lpVtbl -> Remove(This,imageFileName)
+
+#define INetFwAuthorizedApplications_Item(This,imageFileName,app) \
+ (This)->lpVtbl -> Item(This,imageFileName,app)
+
+#define INetFwAuthorizedApplications_get__NewEnum(This,newEnum) \
+ (This)->lpVtbl -> get__NewEnum(This,newEnum)
+
+#endif /* COBJMACROS */
+
+
+#endif /* C style interface */
+
+
+
+/* [propget][id] */ HRESULT STDMETHODCALLTYPE INetFwAuthorizedApplications_get_Count_Proxy(
+ INetFwAuthorizedApplications * This,
+ /* [retval][out] */ long *count);
+
+
+void __RPC_STUB INetFwAuthorizedApplications_get_Count_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [id] */ HRESULT STDMETHODCALLTYPE INetFwAuthorizedApplications_Add_Proxy(
+ INetFwAuthorizedApplications * This,
+ /* [in] */ INetFwAuthorizedApplication *app);
+
+
+void __RPC_STUB INetFwAuthorizedApplications_Add_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [id] */ HRESULT STDMETHODCALLTYPE INetFwAuthorizedApplications_Remove_Proxy(
+ INetFwAuthorizedApplications * This,
+ /* [in] */ BSTR imageFileName);
+
+
+void __RPC_STUB INetFwAuthorizedApplications_Remove_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [id] */ HRESULT STDMETHODCALLTYPE INetFwAuthorizedApplications_Item_Proxy(
+ INetFwAuthorizedApplications * This,
+ /* [in] */ BSTR imageFileName,
+ /* [retval][out] */ INetFwAuthorizedApplication **app);
+
+
+void __RPC_STUB INetFwAuthorizedApplications_Item_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [restricted][propget][id] */ HRESULT STDMETHODCALLTYPE INetFwAuthorizedApplications_get__NewEnum_Proxy(
+ INetFwAuthorizedApplications * This,
+ /* [retval][out] */ IUnknown **newEnum);
+
+
+void __RPC_STUB INetFwAuthorizedApplications_get__NewEnum_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+
+#endif /* __INetFwAuthorizedApplications_INTERFACE_DEFINED__ */
+
+
+#ifndef __INetFwProfile_INTERFACE_DEFINED__
+#define __INetFwProfile_INTERFACE_DEFINED__
+
+/* interface INetFwProfile */
+/* [dual][uuid][object] */
+
+
+EXTERN_C const IID IID_INetFwProfile;
+
+#if defined(__cplusplus) && !defined(CINTERFACE)
+
+ MIDL_INTERFACE("174A0DDA-E9F9-449D-993B-21AB667CA456")
+ INetFwProfile : public IDispatch
+ {
+ public:
+ virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_Type(
+ /* [retval][out] */ NET_FW_PROFILE_TYPE *type) = 0;
+
+ virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_FirewallEnabled(
+ /* [retval][out] */ VARIANT_BOOL *enabled) = 0;
+
+ virtual /* [propput][id] */ HRESULT STDMETHODCALLTYPE put_FirewallEnabled(
+ /* [in] */ VARIANT_BOOL enabled) = 0;
+
+ virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_ExceptionsNotAllowed(
+ /* [retval][out] */ VARIANT_BOOL *notAllowed) = 0;
+
+ virtual /* [propput][id] */ HRESULT STDMETHODCALLTYPE put_ExceptionsNotAllowed(
+ /* [in] */ VARIANT_BOOL notAllowed) = 0;
+
+ virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_NotificationsDisabled(
+ /* [retval][out] */ VARIANT_BOOL *disabled) = 0;
+
+ virtual /* [propput][id] */ HRESULT STDMETHODCALLTYPE put_NotificationsDisabled(
+ /* [in] */ VARIANT_BOOL disabled) = 0;
+
+ virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_UnicastResponsesToMulticastBroadcastDisabled(
+ /* [retval][out] */ VARIANT_BOOL *disabled) = 0;
+
+ virtual /* [propput][id] */ HRESULT STDMETHODCALLTYPE put_UnicastResponsesToMulticastBroadcastDisabled(
+ /* [in] */ VARIANT_BOOL disabled) = 0;
+
+ virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_RemoteAdminSettings(
+ /* [retval][out] */ INetFwRemoteAdminSettings **remoteAdminSettings) = 0;
+
+ virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_IcmpSettings(
+ /* [retval][out] */ INetFwIcmpSettings **icmpSettings) = 0;
+
+ virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_GloballyOpenPorts(
+ /* [retval][out] */ INetFwOpenPorts **openPorts) = 0;
+
+ virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_Services(
+ /* [retval][out] */ INetFwServices **services) = 0;
+
+ virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_AuthorizedApplications(
+ /* [retval][out] */ INetFwAuthorizedApplications **apps) = 0;
+
+ };
+
+#else /* C style interface */
+
+ typedef struct INetFwProfileVtbl
+ {
+ BEGIN_INTERFACE
+
+ HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
+ INetFwProfile * This,
+ /* [in] */ REFIID riid,
+ /* [iid_is][out] */ void **ppvObject);
+
+ ULONG ( STDMETHODCALLTYPE *AddRef )(
+ INetFwProfile * This);
+
+ ULONG ( STDMETHODCALLTYPE *Release )(
+ INetFwProfile * This);
+
+ HRESULT ( STDMETHODCALLTYPE *GetTypeInfoCount )(
+ INetFwProfile * This,
+ /* [out] */ UINT *pctinfo);
+
+ HRESULT ( STDMETHODCALLTYPE *GetTypeInfo )(
+ INetFwProfile * This,
+ /* [in] */ UINT iTInfo,
+ /* [in] */ LCID lcid,
+ /* [out] */ ITypeInfo **ppTInfo);
+
+ HRESULT ( STDMETHODCALLTYPE *GetIDsOfNames )(
+ INetFwProfile * This,
+ /* [in] */ REFIID riid,
+ /* [size_is][in] */ LPOLESTR *rgszNames,
+ /* [in] */ UINT cNames,
+ /* [in] */ LCID lcid,
+ /* [size_is][out] */ DISPID *rgDispId);
+
+ /* [local] */ HRESULT ( STDMETHODCALLTYPE *Invoke )(
+ INetFwProfile * This,
+ /* [in] */ DISPID dispIdMember,
+ /* [in] */ REFIID riid,
+ /* [in] */ LCID lcid,
+ /* [in] */ WORD wFlags,
+ /* [out][in] */ DISPPARAMS *pDispParams,
+ /* [out] */ VARIANT *pVarResult,
+ /* [out] */ EXCEPINFO *pExcepInfo,
+ /* [out] */ UINT *puArgErr);
+
+ /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE *get_Type )(
+ INetFwProfile * This,
+ /* [retval][out] */ NET_FW_PROFILE_TYPE *type);
+
+ /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE *get_FirewallEnabled )(
+ INetFwProfile * This,
+ /* [retval][out] */ VARIANT_BOOL *enabled);
+
+ /* [propput][id] */ HRESULT ( STDMETHODCALLTYPE *put_FirewallEnabled )(
+ INetFwProfile * This,
+ /* [in] */ VARIANT_BOOL enabled);
+
+ /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE *get_ExceptionsNotAllowed )(
+ INetFwProfile * This,
+ /* [retval][out] */ VARIANT_BOOL *notAllowed);
+
+ /* [propput][id] */ HRESULT ( STDMETHODCALLTYPE *put_ExceptionsNotAllowed )(
+ INetFwProfile * This,
+ /* [in] */ VARIANT_BOOL notAllowed);
+
+ /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE *get_NotificationsDisabled )(
+ INetFwProfile * This,
+ /* [retval][out] */ VARIANT_BOOL *disabled);
+
+ /* [propput][id] */ HRESULT ( STDMETHODCALLTYPE *put_NotificationsDisabled )(
+ INetFwProfile * This,
+ /* [in] */ VARIANT_BOOL disabled);
+
+ /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE *get_UnicastResponsesToMulticastBroadcastDisabled )(
+ INetFwProfile * This,
+ /* [retval][out] */ VARIANT_BOOL *disabled);
+
+ /* [propput][id] */ HRESULT ( STDMETHODCALLTYPE *put_UnicastResponsesToMulticastBroadcastDisabled )(
+ INetFwProfile * This,
+ /* [in] */ VARIANT_BOOL disabled);
+
+ /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE *get_RemoteAdminSettings )(
+ INetFwProfile * This,
+ /* [retval][out] */ INetFwRemoteAdminSettings **remoteAdminSettings);
+
+ /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE *get_IcmpSettings )(
+ INetFwProfile * This,
+ /* [retval][out] */ INetFwIcmpSettings **icmpSettings);
+
+ /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE *get_GloballyOpenPorts )(
+ INetFwProfile * This,
+ /* [retval][out] */ INetFwOpenPorts **openPorts);
+
+ /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE *get_Services )(
+ INetFwProfile * This,
+ /* [retval][out] */ INetFwServices **services);
+
+ /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE *get_AuthorizedApplications )(
+ INetFwProfile * This,
+ /* [retval][out] */ INetFwAuthorizedApplications **apps);
+
+ END_INTERFACE
+ } INetFwProfileVtbl;
+
+ interface INetFwProfile
+ {
+ CONST_VTBL struct INetFwProfileVtbl *lpVtbl;
+ };
+
+
+
+#ifdef COBJMACROS
+
+
+#define INetFwProfile_QueryInterface(This,riid,ppvObject) \
+ (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)
+
+#define INetFwProfile_AddRef(This) \
+ (This)->lpVtbl -> AddRef(This)
+
+#define INetFwProfile_Release(This) \
+ (This)->lpVtbl -> Release(This)
+
+
+#define INetFwProfile_GetTypeInfoCount(This,pctinfo) \
+ (This)->lpVtbl -> GetTypeInfoCount(This,pctinfo)
+
+#define INetFwProfile_GetTypeInfo(This,iTInfo,lcid,ppTInfo) \
+ (This)->lpVtbl -> GetTypeInfo(This,iTInfo,lcid,ppTInfo)
+
+#define INetFwProfile_GetIDsOfNames(This,riid,rgszNames,cNames,lcid,rgDispId) \
+ (This)->lpVtbl -> GetIDsOfNames(This,riid,rgszNames,cNames,lcid,rgDispId)
+
+#define INetFwProfile_Invoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr) \
+ (This)->lpVtbl -> Invoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr)
+
+
+#define INetFwProfile_get_Type(This,type) \
+ (This)->lpVtbl -> get_Type(This,type)
+
+#define INetFwProfile_get_FirewallEnabled(This,enabled) \
+ (This)->lpVtbl -> get_FirewallEnabled(This,enabled)
+
+#define INetFwProfile_put_FirewallEnabled(This,enabled) \
+ (This)->lpVtbl -> put_FirewallEnabled(This,enabled)
+
+#define INetFwProfile_get_ExceptionsNotAllowed(This,notAllowed) \
+ (This)->lpVtbl -> get_ExceptionsNotAllowed(This,notAllowed)
+
+#define INetFwProfile_put_ExceptionsNotAllowed(This,notAllowed) \
+ (This)->lpVtbl -> put_ExceptionsNotAllowed(This,notAllowed)
+
+#define INetFwProfile_get_NotificationsDisabled(This,disabled) \
+ (This)->lpVtbl -> get_NotificationsDisabled(This,disabled)
+
+#define INetFwProfile_put_NotificationsDisabled(This,disabled) \
+ (This)->lpVtbl -> put_NotificationsDisabled(This,disabled)
+
+#define INetFwProfile_get_UnicastResponsesToMulticastBroadcastDisabled(This,disabled) \
+ (This)->lpVtbl -> get_UnicastResponsesToMulticastBroadcastDisabled(This,disabled)
+
+#define INetFwProfile_put_UnicastResponsesToMulticastBroadcastDisabled(This,disabled) \
+ (This)->lpVtbl -> put_UnicastResponsesToMulticastBroadcastDisabled(This,disabled)
+
+#define INetFwProfile_get_RemoteAdminSettings(This,remoteAdminSettings) \
+ (This)->lpVtbl -> get_RemoteAdminSettings(This,remoteAdminSettings)
+
+#define INetFwProfile_get_IcmpSettings(This,icmpSettings) \
+ (This)->lpVtbl -> get_IcmpSettings(This,icmpSettings)
+
+#define INetFwProfile_get_GloballyOpenPorts(This,openPorts) \
+ (This)->lpVtbl -> get_GloballyOpenPorts(This,openPorts)
+
+#define INetFwProfile_get_Services(This,services) \
+ (This)->lpVtbl -> get_Services(This,services)
+
+#define INetFwProfile_get_AuthorizedApplications(This,apps) \
+ (This)->lpVtbl -> get_AuthorizedApplications(This,apps)
+
+#endif /* COBJMACROS */
+
+
+#endif /* C style interface */
+
+
+
+/* [propget][id] */ HRESULT STDMETHODCALLTYPE INetFwProfile_get_Type_Proxy(
+ INetFwProfile * This,
+ /* [retval][out] */ NET_FW_PROFILE_TYPE *type);
+
+
+void __RPC_STUB INetFwProfile_get_Type_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propget][id] */ HRESULT STDMETHODCALLTYPE INetFwProfile_get_FirewallEnabled_Proxy(
+ INetFwProfile * This,
+ /* [retval][out] */ VARIANT_BOOL *enabled);
+
+
+void __RPC_STUB INetFwProfile_get_FirewallEnabled_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propput][id] */ HRESULT STDMETHODCALLTYPE INetFwProfile_put_FirewallEnabled_Proxy(
+ INetFwProfile * This,
+ /* [in] */ VARIANT_BOOL enabled);
+
+
+void __RPC_STUB INetFwProfile_put_FirewallEnabled_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propget][id] */ HRESULT STDMETHODCALLTYPE INetFwProfile_get_ExceptionsNotAllowed_Proxy(
+ INetFwProfile * This,
+ /* [retval][out] */ VARIANT_BOOL *notAllowed);
+
+
+void __RPC_STUB INetFwProfile_get_ExceptionsNotAllowed_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propput][id] */ HRESULT STDMETHODCALLTYPE INetFwProfile_put_ExceptionsNotAllowed_Proxy(
+ INetFwProfile * This,
+ /* [in] */ VARIANT_BOOL notAllowed);
+
+
+void __RPC_STUB INetFwProfile_put_ExceptionsNotAllowed_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propget][id] */ HRESULT STDMETHODCALLTYPE INetFwProfile_get_NotificationsDisabled_Proxy(
+ INetFwProfile * This,
+ /* [retval][out] */ VARIANT_BOOL *disabled);
+
+
+void __RPC_STUB INetFwProfile_get_NotificationsDisabled_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propput][id] */ HRESULT STDMETHODCALLTYPE INetFwProfile_put_NotificationsDisabled_Proxy(
+ INetFwProfile * This,
+ /* [in] */ VARIANT_BOOL disabled);
+
+
+void __RPC_STUB INetFwProfile_put_NotificationsDisabled_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propget][id] */ HRESULT STDMETHODCALLTYPE INetFwProfile_get_UnicastResponsesToMulticastBroadcastDisabled_Proxy(
+ INetFwProfile * This,
+ /* [retval][out] */ VARIANT_BOOL *disabled);
+
+
+void __RPC_STUB INetFwProfile_get_UnicastResponsesToMulticastBroadcastDisabled_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propput][id] */ HRESULT STDMETHODCALLTYPE INetFwProfile_put_UnicastResponsesToMulticastBroadcastDisabled_Proxy(
+ INetFwProfile * This,
+ /* [in] */ VARIANT_BOOL disabled);
+
+
+void __RPC_STUB INetFwProfile_put_UnicastResponsesToMulticastBroadcastDisabled_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propget][id] */ HRESULT STDMETHODCALLTYPE INetFwProfile_get_RemoteAdminSettings_Proxy(
+ INetFwProfile * This,
+ /* [retval][out] */ INetFwRemoteAdminSettings **remoteAdminSettings);
+
+
+void __RPC_STUB INetFwProfile_get_RemoteAdminSettings_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propget][id] */ HRESULT STDMETHODCALLTYPE INetFwProfile_get_IcmpSettings_Proxy(
+ INetFwProfile * This,
+ /* [retval][out] */ INetFwIcmpSettings **icmpSettings);
+
+
+void __RPC_STUB INetFwProfile_get_IcmpSettings_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propget][id] */ HRESULT STDMETHODCALLTYPE INetFwProfile_get_GloballyOpenPorts_Proxy(
+ INetFwProfile * This,
+ /* [retval][out] */ INetFwOpenPorts **openPorts);
+
+
+void __RPC_STUB INetFwProfile_get_GloballyOpenPorts_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propget][id] */ HRESULT STDMETHODCALLTYPE INetFwProfile_get_Services_Proxy(
+ INetFwProfile * This,
+ /* [retval][out] */ INetFwServices **services);
+
+
+void __RPC_STUB INetFwProfile_get_Services_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propget][id] */ HRESULT STDMETHODCALLTYPE INetFwProfile_get_AuthorizedApplications_Proxy(
+ INetFwProfile * This,
+ /* [retval][out] */ INetFwAuthorizedApplications **apps);
+
+
+void __RPC_STUB INetFwProfile_get_AuthorizedApplications_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+
+#endif /* __INetFwProfile_INTERFACE_DEFINED__ */
+
+
+#ifndef __INetFwPolicy_INTERFACE_DEFINED__
+#define __INetFwPolicy_INTERFACE_DEFINED__
+
+/* interface INetFwPolicy */
+/* [dual][uuid][object] */
+
+
+EXTERN_C const IID IID_INetFwPolicy;
+
+#if defined(__cplusplus) && !defined(CINTERFACE)
+
+ MIDL_INTERFACE("D46D2478-9AC9-4008-9DC7-5563CE5536CC")
+ INetFwPolicy : public IDispatch
+ {
+ public:
+ virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_CurrentProfile(
+ /* [retval][out] */ INetFwProfile **profile) = 0;
+
+ virtual /* [id] */ HRESULT STDMETHODCALLTYPE GetProfileByType(
+ /* [in] */ NET_FW_PROFILE_TYPE profileType,
+ /* [retval][out] */ INetFwProfile **profile) = 0;
+
+ };
+
+#else /* C style interface */
+
+ typedef struct INetFwPolicyVtbl
+ {
+ BEGIN_INTERFACE
+
+ HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
+ INetFwPolicy * This,
+ /* [in] */ REFIID riid,
+ /* [iid_is][out] */ void **ppvObject);
+
+ ULONG ( STDMETHODCALLTYPE *AddRef )(
+ INetFwPolicy * This);
+
+ ULONG ( STDMETHODCALLTYPE *Release )(
+ INetFwPolicy * This);
+
+ HRESULT ( STDMETHODCALLTYPE *GetTypeInfoCount )(
+ INetFwPolicy * This,
+ /* [out] */ UINT *pctinfo);
+
+ HRESULT ( STDMETHODCALLTYPE *GetTypeInfo )(
+ INetFwPolicy * This,
+ /* [in] */ UINT iTInfo,
+ /* [in] */ LCID lcid,
+ /* [out] */ ITypeInfo **ppTInfo);
+
+ HRESULT ( STDMETHODCALLTYPE *GetIDsOfNames )(
+ INetFwPolicy * This,
+ /* [in] */ REFIID riid,
+ /* [size_is][in] */ LPOLESTR *rgszNames,
+ /* [in] */ UINT cNames,
+ /* [in] */ LCID lcid,
+ /* [size_is][out] */ DISPID *rgDispId);
+
+ /* [local] */ HRESULT ( STDMETHODCALLTYPE *Invoke )(
+ INetFwPolicy * This,
+ /* [in] */ DISPID dispIdMember,
+ /* [in] */ REFIID riid,
+ /* [in] */ LCID lcid,
+ /* [in] */ WORD wFlags,
+ /* [out][in] */ DISPPARAMS *pDispParams,
+ /* [out] */ VARIANT *pVarResult,
+ /* [out] */ EXCEPINFO *pExcepInfo,
+ /* [out] */ UINT *puArgErr);
+
+ /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE *get_CurrentProfile )(
+ INetFwPolicy * This,
+ /* [retval][out] */ INetFwProfile **profile);
+
+ /* [id] */ HRESULT ( STDMETHODCALLTYPE *GetProfileByType )(
+ INetFwPolicy * This,
+ /* [in] */ NET_FW_PROFILE_TYPE profileType,
+ /* [retval][out] */ INetFwProfile **profile);
+
+ END_INTERFACE
+ } INetFwPolicyVtbl;
+
+ interface INetFwPolicy
+ {
+ CONST_VTBL struct INetFwPolicyVtbl *lpVtbl;
+ };
+
+
+
+#ifdef COBJMACROS
+
+
+#define INetFwPolicy_QueryInterface(This,riid,ppvObject) \
+ (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)
+
+#define INetFwPolicy_AddRef(This) \
+ (This)->lpVtbl -> AddRef(This)
+
+#define INetFwPolicy_Release(This) \
+ (This)->lpVtbl -> Release(This)
+
+
+#define INetFwPolicy_GetTypeInfoCount(This,pctinfo) \
+ (This)->lpVtbl -> GetTypeInfoCount(This,pctinfo)
+
+#define INetFwPolicy_GetTypeInfo(This,iTInfo,lcid,ppTInfo) \
+ (This)->lpVtbl -> GetTypeInfo(This,iTInfo,lcid,ppTInfo)
+
+#define INetFwPolicy_GetIDsOfNames(This,riid,rgszNames,cNames,lcid,rgDispId) \
+ (This)->lpVtbl -> GetIDsOfNames(This,riid,rgszNames,cNames,lcid,rgDispId)
+
+#define INetFwPolicy_Invoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr) \
+ (This)->lpVtbl -> Invoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr)
+
+
+#define INetFwPolicy_get_CurrentProfile(This,profile) \
+ (This)->lpVtbl -> get_CurrentProfile(This,profile)
+
+#define INetFwPolicy_GetProfileByType(This,profileType,profile) \
+ (This)->lpVtbl -> GetProfileByType(This,profileType,profile)
+
+#endif /* COBJMACROS */
+
+
+#endif /* C style interface */
+
+
+
+/* [propget][id] */ HRESULT STDMETHODCALLTYPE INetFwPolicy_get_CurrentProfile_Proxy(
+ INetFwPolicy * This,
+ /* [retval][out] */ INetFwProfile **profile);
+
+
+void __RPC_STUB INetFwPolicy_get_CurrentProfile_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [id] */ HRESULT STDMETHODCALLTYPE INetFwPolicy_GetProfileByType_Proxy(
+ INetFwPolicy * This,
+ /* [in] */ NET_FW_PROFILE_TYPE profileType,
+ /* [retval][out] */ INetFwProfile **profile);
+
+
+void __RPC_STUB INetFwPolicy_GetProfileByType_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+
+#endif /* __INetFwPolicy_INTERFACE_DEFINED__ */
+
+
+#ifndef __INetFwMgr_INTERFACE_DEFINED__
+#define __INetFwMgr_INTERFACE_DEFINED__
+
+/* interface INetFwMgr */
+/* [dual][uuid][object] */
+
+
+EXTERN_C const IID IID_INetFwMgr;
+
+#if defined(__cplusplus) && !defined(CINTERFACE)
+
+ MIDL_INTERFACE("F7898AF5-CAC4-4632-A2EC-DA06E5111AF2")
+ INetFwMgr : public IDispatch
+ {
+ public:
+ virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_LocalPolicy(
+ /* [retval][out] */ INetFwPolicy **localPolicy) = 0;
+
+ virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_CurrentProfileType(
+ /* [retval][out] */ NET_FW_PROFILE_TYPE *profileType) = 0;
+
+ virtual /* [id] */ HRESULT STDMETHODCALLTYPE RestoreDefaults( void) = 0;
+
+ virtual /* [id] */ HRESULT STDMETHODCALLTYPE IsPortAllowed(
+ /* [in] */ BSTR imageFileName,
+ /* [in] */ NET_FW_IP_VERSION ipVersion,
+ /* [in] */ LONG portNumber,
+ /* [in] */ BSTR localAddress,
+ /* [in] */ NET_FW_IP_PROTOCOL ipProtocol,
+ /* [out] */ VARIANT *allowed,
+ /* [out] */ VARIANT *restricted) = 0;
+
+ virtual /* [id] */ HRESULT STDMETHODCALLTYPE IsIcmpTypeAllowed(
+ /* [in] */ NET_FW_IP_VERSION ipVersion,
+ /* [in] */ BSTR localAddress,
+ /* [in] */ BYTE type,
+ /* [out] */ VARIANT *allowed,
+ /* [out] */ VARIANT *restricted) = 0;
+
+ };
+
+#else /* C style interface */
+
+ typedef struct INetFwMgrVtbl
+ {
+ BEGIN_INTERFACE
+
+ HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
+ INetFwMgr * This,
+ /* [in] */ REFIID riid,
+ /* [iid_is][out] */ void **ppvObject);
+
+ ULONG ( STDMETHODCALLTYPE *AddRef )(
+ INetFwMgr * This);
+
+ ULONG ( STDMETHODCALLTYPE *Release )(
+ INetFwMgr * This);
+
+ HRESULT ( STDMETHODCALLTYPE *GetTypeInfoCount )(
+ INetFwMgr * This,
+ /* [out] */ UINT *pctinfo);
+
+ HRESULT ( STDMETHODCALLTYPE *GetTypeInfo )(
+ INetFwMgr * This,
+ /* [in] */ UINT iTInfo,
+ /* [in] */ LCID lcid,
+ /* [out] */ ITypeInfo **ppTInfo);
+
+ HRESULT ( STDMETHODCALLTYPE *GetIDsOfNames )(
+ INetFwMgr * This,
+ /* [in] */ REFIID riid,
+ /* [size_is][in] */ LPOLESTR *rgszNames,
+ /* [in] */ UINT cNames,
+ /* [in] */ LCID lcid,
+ /* [size_is][out] */ DISPID *rgDispId);
+
+ /* [local] */ HRESULT ( STDMETHODCALLTYPE *Invoke )(
+ INetFwMgr * This,
+ /* [in] */ DISPID dispIdMember,
+ /* [in] */ REFIID riid,
+ /* [in] */ LCID lcid,
+ /* [in] */ WORD wFlags,
+ /* [out][in] */ DISPPARAMS *pDispParams,
+ /* [out] */ VARIANT *pVarResult,
+ /* [out] */ EXCEPINFO *pExcepInfo,
+ /* [out] */ UINT *puArgErr);
+
+ /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE *get_LocalPolicy )(
+ INetFwMgr * This,
+ /* [retval][out] */ INetFwPolicy **localPolicy);
+
+ /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE *get_CurrentProfileType )(
+ INetFwMgr * This,
+ /* [retval][out] */ NET_FW_PROFILE_TYPE *profileType);
+
+ /* [id] */ HRESULT ( STDMETHODCALLTYPE *RestoreDefaults )(
+ INetFwMgr * This);
+
+ /* [id] */ HRESULT ( STDMETHODCALLTYPE *IsPortAllowed )(
+ INetFwMgr * This,
+ /* [in] */ BSTR imageFileName,
+ /* [in] */ NET_FW_IP_VERSION ipVersion,
+ /* [in] */ LONG portNumber,
+ /* [in] */ BSTR localAddress,
+ /* [in] */ NET_FW_IP_PROTOCOL ipProtocol,
+ /* [out] */ VARIANT *allowed,
+ /* [out] */ VARIANT *restricted);
+
+ /* [id] */ HRESULT ( STDMETHODCALLTYPE *IsIcmpTypeAllowed )(
+ INetFwMgr * This,
+ /* [in] */ NET_FW_IP_VERSION ipVersion,
+ /* [in] */ BSTR localAddress,
+ /* [in] */ BYTE type,
+ /* [out] */ VARIANT *allowed,
+ /* [out] */ VARIANT *restricted);
+
+ END_INTERFACE
+ } INetFwMgrVtbl;
+
+ interface INetFwMgr
+ {
+ CONST_VTBL struct INetFwMgrVtbl *lpVtbl;
+ };
+
+
+
+#ifdef COBJMACROS
+
+
+#define INetFwMgr_QueryInterface(This,riid,ppvObject) \
+ (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)
+
+#define INetFwMgr_AddRef(This) \
+ (This)->lpVtbl -> AddRef(This)
+
+#define INetFwMgr_Release(This) \
+ (This)->lpVtbl -> Release(This)
+
+
+#define INetFwMgr_GetTypeInfoCount(This,pctinfo) \
+ (This)->lpVtbl -> GetTypeInfoCount(This,pctinfo)
+
+#define INetFwMgr_GetTypeInfo(This,iTInfo,lcid,ppTInfo) \
+ (This)->lpVtbl -> GetTypeInfo(This,iTInfo,lcid,ppTInfo)
+
+#define INetFwMgr_GetIDsOfNames(This,riid,rgszNames,cNames,lcid,rgDispId) \
+ (This)->lpVtbl -> GetIDsOfNames(This,riid,rgszNames,cNames,lcid,rgDispId)
+
+#define INetFwMgr_Invoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr) \
+ (This)->lpVtbl -> Invoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr)
+
+
+#define INetFwMgr_get_LocalPolicy(This,localPolicy) \
+ (This)->lpVtbl -> get_LocalPolicy(This,localPolicy)
+
+#define INetFwMgr_get_CurrentProfileType(This,profileType) \
+ (This)->lpVtbl -> get_CurrentProfileType(This,profileType)
+
+#define INetFwMgr_RestoreDefaults(This) \
+ (This)->lpVtbl -> RestoreDefaults(This)
+
+#define INetFwMgr_IsPortAllowed(This,imageFileName,ipVersion,portNumber,localAddress,ipProtocol,allowed,restricted) \
+ (This)->lpVtbl -> IsPortAllowed(This,imageFileName,ipVersion,portNumber,localAddress,ipProtocol,allowed,restricted)
+
+#define INetFwMgr_IsIcmpTypeAllowed(This,ipVersion,localAddress,type,allowed,restricted) \
+ (This)->lpVtbl -> IsIcmpTypeAllowed(This,ipVersion,localAddress,type,allowed,restricted)
+
+#endif /* COBJMACROS */
+
+
+#endif /* C style interface */
+
+
+
+/* [propget][id] */ HRESULT STDMETHODCALLTYPE INetFwMgr_get_LocalPolicy_Proxy(
+ INetFwMgr * This,
+ /* [retval][out] */ INetFwPolicy **localPolicy);
+
+
+void __RPC_STUB INetFwMgr_get_LocalPolicy_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propget][id] */ HRESULT STDMETHODCALLTYPE INetFwMgr_get_CurrentProfileType_Proxy(
+ INetFwMgr * This,
+ /* [retval][out] */ NET_FW_PROFILE_TYPE *profileType);
+
+
+void __RPC_STUB INetFwMgr_get_CurrentProfileType_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [id] */ HRESULT STDMETHODCALLTYPE INetFwMgr_RestoreDefaults_Proxy(
+ INetFwMgr * This);
+
+
+void __RPC_STUB INetFwMgr_RestoreDefaults_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [id] */ HRESULT STDMETHODCALLTYPE INetFwMgr_IsPortAllowed_Proxy(
+ INetFwMgr * This,
+ /* [in] */ BSTR imageFileName,
+ /* [in] */ NET_FW_IP_VERSION ipVersion,
+ /* [in] */ LONG portNumber,
+ /* [in] */ BSTR localAddress,
+ /* [in] */ NET_FW_IP_PROTOCOL ipProtocol,
+ /* [out] */ VARIANT *allowed,
+ /* [out] */ VARIANT *restricted);
+
+
+void __RPC_STUB INetFwMgr_IsPortAllowed_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [id] */ HRESULT STDMETHODCALLTYPE INetFwMgr_IsIcmpTypeAllowed_Proxy(
+ INetFwMgr * This,
+ /* [in] */ NET_FW_IP_VERSION ipVersion,
+ /* [in] */ BSTR localAddress,
+ /* [in] */ BYTE type,
+ /* [out] */ VARIANT *allowed,
+ /* [out] */ VARIANT *restricted);
+
+
+void __RPC_STUB INetFwMgr_IsIcmpTypeAllowed_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+
+#endif /* __INetFwMgr_INTERFACE_DEFINED__ */
+
+
+
+#ifndef __NetFwPublicTypeLib_LIBRARY_DEFINED__
+#define __NetFwPublicTypeLib_LIBRARY_DEFINED__
+
+/* library NetFwPublicTypeLib */
+/* [version][uuid] */
+
+
+
+
+
+
+
+
+
+
+
+
+
+EXTERN_C const IID LIBID_NetFwPublicTypeLib;
+
+EXTERN_C const CLSID CLSID_NetFwOpenPort;
+
+#ifdef __cplusplus
+
+class DECLSPEC_UUID("0CA545C6-37AD-4A6C-BF92-9F7610067EF5")
+NetFwOpenPort;
+#endif
+
+EXTERN_C const CLSID CLSID_NetFwAuthorizedApplication;
+
+#ifdef __cplusplus
+
+class DECLSPEC_UUID("EC9846B3-2762-4A6B-A214-6ACB603462D2")
+NetFwAuthorizedApplication;
+#endif
+
+EXTERN_C const CLSID CLSID_NetFwMgr;
+
+#ifdef __cplusplus
+
+class DECLSPEC_UUID("304CE942-6E39-40D8-943A-B913C40C9CD4")
+NetFwMgr;
+#endif
+#endif /* __NetFwPublicTypeLib_LIBRARY_DEFINED__ */
+
+/* Additional Prototypes for ALL interfaces */
+
+unsigned long __RPC_USER BSTR_UserSize( unsigned long *, unsigned long , BSTR * );
+unsigned char * __RPC_USER BSTR_UserMarshal( unsigned long *, unsigned char *, BSTR * );
+unsigned char * __RPC_USER BSTR_UserUnmarshal(unsigned long *, unsigned char *, BSTR * );
+void __RPC_USER BSTR_UserFree( unsigned long *, BSTR * );
+
+unsigned long __RPC_USER VARIANT_UserSize( unsigned long *, unsigned long , VARIANT * );
+unsigned char * __RPC_USER VARIANT_UserMarshal( unsigned long *, unsigned char *, VARIANT * );
+unsigned char * __RPC_USER VARIANT_UserUnmarshal(unsigned long *, unsigned char *, VARIANT * );
+void __RPC_USER VARIANT_UserFree( unsigned long *, VARIANT * );
+
+/* end of Additional Prototypes */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
+
diff --git a/Plugins/jingle/libjingle/talk/base/network.cc b/Plugins/jingle/libjingle/talk/base/network.cc
new file mode 100644
index 0000000..d5a7d24
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/network.cc
@@ -0,0 +1,381 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <algorithm>
+#include <cassert>
+#include <cfloat>
+#include <cmath>
+#include <sstream>
+
+#ifdef POSIX
+extern "C" {
+#include <sys/socket.h>
+#include <sys/utsname.h>
+#include <sys/ioctl.h>
+#include <net/if.h>
+#include <unistd.h>
+#include <errno.h>
+}
+#endif // POSIX
+
+#ifdef WIN32
+#include "talk/base/win32.h"
+#include <Iphlpapi.h>
+#endif
+
+#include "talk/base/host.h"
+#include "talk/base/logging.h"
+#include "talk/base/network.h"
+#include "talk/base/socket.h" // this includes something that makes windows happy
+#include "talk/base/stringencode.h"
+#include "talk/base/time.h"
+#include "talk/base/basicdefs.h"
+
+namespace {
+
+const double kAlpha = 0.5; // weight for data infinitely far in the past
+const double kHalfLife = 2000; // half life of exponential decay (in ms)
+const double kLog2 = 0.693147180559945309417;
+const double kLambda = kLog2 / kHalfLife;
+
+// assume so-so quality unless data says otherwise
+const double kDefaultQuality = talk_base::QUALITY_FAIR;
+
+typedef std::map<std::string,std::string> StrMap;
+
+void BuildMap(const StrMap& map, std::string& str) {
+ str.append("{");
+ bool first = true;
+ for (StrMap::const_iterator i = map.begin(); i != map.end(); ++i) {
+ if (!first) str.append(",");
+ str.append(i->first);
+ str.append("=");
+ str.append(i->second);
+ first = false;
+ }
+ str.append("}");
+}
+
+void ParseCheck(std::istringstream& ist, char ch) {
+ if (ist.get() != ch)
+ LOG(LERROR) << "Expecting '" << ch << "'";
+}
+
+std::string ParseString(std::istringstream& ist) {
+ std::string str;
+ int count = 0;
+ while (ist) {
+ char ch = ist.peek();
+ if ((count == 0) && ((ch == '=') || (ch == ',') || (ch == '}'))) {
+ break;
+ } else if (ch == '{') {
+ count += 1;
+ } else if (ch == '}') {
+ count -= 1;
+ if (count < 0)
+ LOG(LERROR) << "mismatched '{' and '}'";
+ }
+ str.append(1, static_cast<char>(ist.get()));
+ }
+ return str;
+}
+
+void ParseMap(const std::string& str, StrMap& map) {
+ if (str.size() == 0)
+ return;
+ std::istringstream ist(str);
+ ParseCheck(ist, '{');
+ for (;;) {
+ std::string key = ParseString(ist);
+ ParseCheck(ist, '=');
+ std::string val = ParseString(ist);
+ map[key] = val;
+ if (ist.peek() == ',')
+ ist.get();
+ else
+ break;
+ }
+ ParseCheck(ist, '}');
+ if (ist.rdbuf()->in_avail() != 0)
+ LOG(LERROR) << "Unexpected characters at end";
+}
+
+#if 0
+const std::string TEST_MAP0_IN = "";
+const std::string TEST_MAP0_OUT = "{}";
+const std::string TEST_MAP1 = "{a=12345}";
+const std::string TEST_MAP2 = "{a=12345,b=67890}";
+const std::string TEST_MAP3 = "{a=12345,b=67890,c=13579}";
+const std::string TEST_MAP4 = "{a={d=12345,e=67890}}";
+const std::string TEST_MAP5 = "{a={d=12345,e=67890},b=67890}";
+const std::string TEST_MAP6 = "{a=12345,b={d=12345,e=67890}}";
+const std::string TEST_MAP7 = "{a=12345,b={d=12345,e=67890},c=13579}";
+
+class MyTest {
+public:
+ MyTest() {
+ test(TEST_MAP0_IN, TEST_MAP0_OUT);
+ test(TEST_MAP1, TEST_MAP1);
+ test(TEST_MAP2, TEST_MAP2);
+ test(TEST_MAP3, TEST_MAP3);
+ test(TEST_MAP4, TEST_MAP4);
+ test(TEST_MAP5, TEST_MAP5);
+ test(TEST_MAP6, TEST_MAP6);
+ test(TEST_MAP7, TEST_MAP7);
+ }
+ void test(const std::string& input, const std::string& exp_output) {
+ StrMap map;
+ ParseMap(input, map);
+ std::string output;
+ BuildMap(map, output);
+ LOG(INFO) << " ******** " << (output == exp_output);
+ }
+};
+
+static MyTest myTest;
+#endif
+
+} // namespace
+
+namespace talk_base {
+
+#ifdef POSIX
+void NetworkManager::CreateNetworks(std::vector<Network*>& networks) {
+ int fd;
+ if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+ PLOG(LERROR, errno) << "socket";
+ return;
+ }
+
+ struct ifconf ifc;
+ ifc.ifc_len = 64 * sizeof(struct ifreq);
+ ifc.ifc_buf = new char[ifc.ifc_len];
+
+ if (ioctl(fd, SIOCGIFCONF, &ifc) < 0) {
+ PLOG(LERROR, errno) << "ioctl";
+ return;
+ }
+ assert(ifc.ifc_len < static_cast<int>(64 * sizeof(struct ifreq)));
+
+ struct ifreq* ptr = reinterpret_cast<struct ifreq*>(ifc.ifc_buf);
+ struct ifreq* end =
+ reinterpret_cast<struct ifreq*>(ifc.ifc_buf + ifc.ifc_len);
+
+ while (ptr < end) {
+ if (strcmp(ptr->ifr_name, "lo")) { // Ignore the loopback device
+ struct sockaddr_in* inaddr =
+ reinterpret_cast<struct sockaddr_in*>(&ptr->ifr_ifru.ifru_addr);
+ if (inaddr->sin_family == AF_INET) {
+ uint32 ip = ntohl(inaddr->sin_addr.s_addr);
+ networks.push_back(new Network(std::string(ptr->ifr_name), ip));
+ }
+ }
+#ifdef _SIZEOF_ADDR_IFREQ
+ ptr = reinterpret_cast<struct ifreq*>(
+ reinterpret_cast<char*>(ptr) + _SIZEOF_ADDR_IFREQ(*ptr));
+#else
+ ptr++;
+#endif
+ }
+
+ delete [] ifc.ifc_buf;
+ close(fd);
+}
+#endif
+
+#ifdef WIN32
+void NetworkManager::CreateNetworks(std::vector<Network*>& networks) {
+ IP_ADAPTER_INFO info_temp;
+ ULONG len = 0;
+
+ if (GetAdaptersInfo(&info_temp, &len) != ERROR_BUFFER_OVERFLOW)
+ return;
+ IP_ADAPTER_INFO *infos = new IP_ADAPTER_INFO[len];
+ if (GetAdaptersInfo(infos, &len) != NO_ERROR)
+ return;
+
+ int count = 0;
+ for (IP_ADAPTER_INFO *info = infos; info != NULL; info = info->Next) {
+ if (info->Type == MIB_IF_TYPE_LOOPBACK)
+ continue;
+ if (strcmp(info->IpAddressList.IpAddress.String, "0.0.0.0") == 0)
+ continue;
+
+ // In production, don't transmit the network name because of
+ // privacy concerns. Transmit a number instead.
+
+ std::string name;
+#if defined(PRODUCTION)
+ std::ostringstream ost;
+ ost << count;
+ name = ost.str();
+ count++;
+#else
+ name = info->Description;
+#endif
+
+ networks.push_back(new Network(name,
+ SocketAddress::StringToIP(info->IpAddressList.IpAddress.String)));
+ }
+
+ delete infos;
+}
+#endif
+
+void NetworkManager::GetNetworks(std::vector<Network*>& result) {
+ std::vector<Network*> list;
+ CreateNetworks(list);
+
+ for (uint32 i = 0; i < list.size(); ++i) {
+ NetworkMap::iterator iter = networks_.find(list[i]->name());
+
+ Network* network;
+ if (iter == networks_.end()) {
+ network = list[i];
+ } else {
+ network = iter->second;
+ network->set_ip(list[i]->ip());
+ delete list[i];
+ }
+
+ networks_[network->name()] = network;
+ result.push_back(network);
+ }
+}
+
+std::string NetworkManager::GetState() {
+ StrMap map;
+ for (NetworkMap::iterator i = networks_.begin(); i != networks_.end(); ++i)
+ map[i->first] = i->second->GetState();
+
+ std::string str;
+ BuildMap(map, str);
+ return str;
+}
+
+void NetworkManager::SetState(std::string str) {
+ StrMap map;
+ ParseMap(str, map);
+
+ for (StrMap::iterator i = map.begin(); i != map.end(); ++i) {
+ std::string name = i->first;
+ std::string state = i->second;
+
+ Network* network = new Network(name, 0);
+ network->SetState(state);
+ networks_[name] = network;
+ }
+}
+
+Network::Network(const std::string& name, uint32 ip)
+ : name_(name), ip_(ip), uniform_numerator_(0), uniform_denominator_(0),
+ exponential_numerator_(0), exponential_denominator_(0),
+ quality_(kDefaultQuality) {
+
+ last_data_time_ = Time();
+
+ // TODO: seed the historical data with one data point based on the link speed
+ // metric from XP (4.0 if < 50, 3.0 otherwise).
+}
+
+void Network::StartSession(NetworkSession* session) {
+ assert(std::find(sessions_.begin(), sessions_.end(), session) == sessions_.end());
+ sessions_.push_back(session);
+}
+
+void Network::StopSession(NetworkSession* session) {
+ SessionList::iterator iter = std::find(sessions_.begin(), sessions_.end(), session);
+ if (iter != sessions_.end())
+ sessions_.erase(iter);
+}
+
+void Network::EstimateQuality() {
+ uint32 now = Time();
+
+ // Add new data points for the current time.
+ for (uint32 i = 0; i < sessions_.size(); ++i) {
+ if (sessions_[i]->HasQuality())
+ AddDataPoint(now, sessions_[i]->GetCurrentQuality());
+ }
+
+ // Construct the weighted average using both uniform and exponential weights.
+
+ double exp_shift = exp(-kLambda * (now - last_data_time_));
+ double numerator = uniform_numerator_ + exp_shift * exponential_numerator_;
+ double denominator = uniform_denominator_ + exp_shift * exponential_denominator_;
+
+ if (denominator < DBL_EPSILON)
+ quality_ = kDefaultQuality;
+ else
+ quality_ = numerator / denominator;
+}
+
+std::string Network::ToString() const {
+ std::stringstream ss;
+ // Print out the first space-terminated token of the network name, plus
+ // the IP address.
+ ss << "Net[" << name_.substr(0, name_.find(' '))
+ << ":" << SocketAddress::IPToString(ip_) << "]";
+ return ss.str();
+}
+
+void Network::AddDataPoint(uint32 time, double quality) {
+ uniform_numerator_ += kAlpha * quality;
+ uniform_denominator_ += kAlpha;
+
+ double exp_shift = exp(-kLambda * (time - last_data_time_));
+ exponential_numerator_ = (1 - kAlpha) * quality + exp_shift * exponential_numerator_;
+ exponential_denominator_ = (1 - kAlpha) + exp_shift * exponential_denominator_;
+
+ last_data_time_ = time;
+}
+
+std::string Network::GetState() {
+ StrMap map;
+ map["lt"] = talk_base::ToString<uint32>(last_data_time_);
+ map["un"] = talk_base::ToString<double>(uniform_numerator_);
+ map["ud"] = talk_base::ToString<double>(uniform_denominator_);
+ map["en"] = talk_base::ToString<double>(exponential_numerator_);
+ map["ed"] = talk_base::ToString<double>(exponential_denominator_);
+
+ std::string str;
+ BuildMap(map, str);
+ return str;
+}
+
+void Network::SetState(std::string str) {
+ StrMap map;
+ ParseMap(str, map);
+
+ last_data_time_ = FromString<uint32>(map["lt"]);
+ uniform_numerator_ = FromString<double>(map["un"]);
+ uniform_denominator_ = FromString<double>(map["ud"]);
+ exponential_numerator_ = FromString<double>(map["en"]);
+ exponential_denominator_ = FromString<double>(map["ed"]);
+}
+
+} // namespace talk_base
diff --git a/Plugins/jingle/libjingle/talk/base/network.h b/Plugins/jingle/libjingle/talk/base/network.h
new file mode 100644
index 0000000..52785ba
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/network.h
@@ -0,0 +1,139 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TALK_BASE_NETWORK_H__
+#define TALK_BASE_NETWORK_H__
+
+#include <deque>
+#include <map>
+#include <string>
+#include <vector>
+
+#include "talk/base/basictypes.h"
+
+namespace talk_base {
+
+class Network;
+class NetworkSession;
+
+// Keeps track of the available network interfaces over time so that quality
+// information can be aggregated and recorded.
+class NetworkManager {
+public:
+
+ // Updates and returns the current list of networks available on this machine.
+ // This version will make sure that repeated calls return the same object for
+ // a given network, so that quality is tracked appropriately.
+ void GetNetworks(std::vector<Network*>& networks);
+
+ // Reads and writes the state of the quality database in a string format.
+ std::string GetState();
+ void SetState(std::string str);
+
+ // Creates a network object for each network available on the machine.
+ static void CreateNetworks(std::vector<Network*>& networks);
+
+private:
+ typedef std::map<std::string,Network*> NetworkMap;
+
+ NetworkMap networks_;
+};
+
+// Represents a Unix-type network interface, with a name and single address.
+// It also includes the ability to track and estimate quality.
+class Network {
+public:
+ Network(const std::string& name, uint32 ip);
+
+ // Returns the OS name of this network. This is considered the primary key
+ // that identifies each network.
+ const std::string& name() const { return name_; }
+
+ // Identifies the current IP address used by this network.
+ uint32 ip() const { return ip_; }
+ void set_ip(uint32 ip) { ip_ = ip; }
+
+ // Updates the list of sessions that are ongoing.
+ void StartSession(NetworkSession* session);
+ void StopSession(NetworkSession* session);
+
+ // Re-computes the estimate of near-future quality based on the information
+ // as of this exact moment.
+ void EstimateQuality();
+
+ // Returns the current estimate of the near-future quality of connections
+ // that use this local interface.
+ double quality() { return quality_; }
+
+ // Debugging description of this network
+ std::string ToString() const;
+
+private:
+ typedef std::vector<NetworkSession*> SessionList;
+
+ std::string name_;
+ uint32 ip_;
+ SessionList sessions_;
+ double uniform_numerator_;
+ double uniform_denominator_;
+ double exponential_numerator_;
+ double exponential_denominator_;
+ uint32 last_data_time_;
+ double quality_;
+
+ // Updates the statistics maintained to include the given estimate.
+ void AddDataPoint(uint32 time, double quality);
+
+ // Converts the internal state to and from a string. This is used to record
+ // quality information into a permanent store.
+ void SetState(std::string str);
+ std::string GetState();
+
+ friend class NetworkManager;
+};
+
+// Represents a session that is in progress using a particular network and can
+// provide data about the quality of the network at any given moment.
+class NetworkSession {
+public:
+ // Determines whether this session has an estimate at this moment. We will
+ // only call GetCurrentQuality when this returns true.
+ virtual bool HasQuality() = 0;
+
+ // Returns an estimate of the quality at this exact moment. The result should
+ // be a MOS (mean opinion score) value.
+ virtual float GetCurrentQuality() = 0;
+
+};
+
+const double QUALITY_BAD = 3.0;
+const double QUALITY_FAIR = 3.35;
+const double QUALITY_GOOD = 3.7;
+
+} // namespace talk_base
+
+#endif // TALK_BASE_NETWORK_H__
diff --git a/Plugins/jingle/libjingle/talk/base/openssladapter.cc b/Plugins/jingle/libjingle/talk/base/openssladapter.cc
new file mode 100644
index 0000000..1d4f02c
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/openssladapter.cc
@@ -0,0 +1,809 @@
+#include <openssl/bio.h>
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+#include <openssl/x509v3.h>
+
+#include "talk/base/common.h"
+#include "talk/base/logging.h"
+#include "talk/base/openssladapter.h"
+#include "talk/base/stringutils.h"
+#include "talk/base/Equifax_Secure_Global_eBusiness_CA-1.h"
+
+//////////////////////////////////////////////////////////////////////
+// StreamBIO
+//////////////////////////////////////////////////////////////////////
+
+#if 0
+static int stream_write(BIO* h, const char* buf, int num);
+static int stream_read(BIO* h, char* buf, int size);
+static int stream_puts(BIO* h, const char* str);
+static long stream_ctrl(BIO* h, int cmd, long arg1, void* arg2);
+static int stream_new(BIO* h);
+static int stream_free(BIO* data);
+
+static BIO_METHOD methods_stream = {
+ BIO_TYPE_BIO,
+ "stream",
+ stream_write,
+ stream_read,
+ stream_puts,
+ 0,
+ stream_ctrl,
+ stream_new,
+ stream_free,
+ NULL,
+};
+
+BIO_METHOD* BIO_s_stream() { return(&methods_stream); }
+
+BIO* BIO_new_stream(StreamInterface* stream) {
+ BIO* ret = BIO_new(BIO_s_stream());
+ if (ret == NULL)
+ return NULL;
+ ret->ptr = stream;
+ return ret;
+}
+
+static int stream_new(BIO* b) {
+ b->shutdown = 0;
+ b->init = 1;
+ b->num = 0; // 1 means end-of-stream
+ b->ptr = 0;
+ return 1;
+}
+
+static int stream_free(BIO* b) {
+ if (b == NULL)
+ return 0;
+ return 1;
+}
+
+static int stream_read(BIO* b, char* out, int outl) {
+ if (!out)
+ return -1;
+ StreamInterface* stream = static_cast<StreamInterface*>(b->ptr);
+ BIO_clear_retry_flags(b);
+ size_t read;
+ int error;
+ StreamResult result = stream->Read(out, outl, &read, &error);
+ if (result == SR_SUCCESS) {
+ return read;
+ } else if (result == SR_EOS) {
+ b->num = 1;
+ } else if (result == SR_BLOCK) {
+ BIO_set_retry_read(b);
+ }
+ return -1;
+}
+
+static int stream_write(BIO* b, const char* in, int inl) {
+ if (!in)
+ return -1;
+ StreamInterface* stream = static_cast<StreamInterface*>(b->ptr);
+ BIO_clear_retry_flags(b);
+ size_t written;
+ int error;
+ StreamResult result = stream->Write(in, inl, &written, &error);
+ if (result == SR_SUCCESS) {
+ return written;
+ } else if (result == SR_BLOCK) {
+ BIO_set_retry_write(b);
+ }
+ return -1;
+}
+
+static int stream_puts(BIO* b, const char* str) {
+ return stream_write(b, str, strlen(str));
+}
+
+static long stream_ctrl(BIO* b, int cmd, long num, void* ptr) {
+ UNUSED(num);
+ UNUSED(ptr);
+
+ switch (cmd) {
+ case BIO_CTRL_RESET:
+ return 0;
+ case BIO_CTRL_EOF:
+ return b->num;
+ case BIO_CTRL_WPENDING:
+ case BIO_CTRL_PENDING:
+ return 0;
+ case BIO_CTRL_FLUSH:
+ return 1;
+ default:
+ return 0;
+ }
+}
+#endif
+
+//////////////////////////////////////////////////////////////////////
+// SocketBIO
+//////////////////////////////////////////////////////////////////////
+
+static int socket_write(BIO* h, const char* buf, int num);
+static int socket_read(BIO* h, char* buf, int size);
+static int socket_puts(BIO* h, const char* str);
+static long socket_ctrl(BIO* h, int cmd, long arg1, void* arg2);
+static int socket_new(BIO* h);
+static int socket_free(BIO* data);
+
+static BIO_METHOD methods_socket = {
+ BIO_TYPE_BIO,
+ "socket",
+ socket_write,
+ socket_read,
+ socket_puts,
+ 0,
+ socket_ctrl,
+ socket_new,
+ socket_free,
+ NULL,
+};
+
+BIO_METHOD* BIO_s_socket2() { return(&methods_socket); }
+
+BIO* BIO_new_socket(talk_base::AsyncSocket* socket) {
+ BIO* ret = BIO_new(BIO_s_socket2());
+ if (ret == NULL) {
+ return NULL;
+ }
+ ret->ptr = socket;
+ return ret;
+}
+
+static int socket_new(BIO* b) {
+ b->shutdown = 0;
+ b->init = 1;
+ b->num = 0; // 1 means socket closed
+ b->ptr = 0;
+ return 1;
+}
+
+static int socket_free(BIO* b) {
+ if (b == NULL)
+ return 0;
+ return 1;
+}
+
+static int socket_read(BIO* b, char* out, int outl) {
+ if (!out)
+ return -1;
+ talk_base::AsyncSocket* socket = static_cast<talk_base::AsyncSocket*>(b->ptr);
+ BIO_clear_retry_flags(b);
+ int result = socket->Recv(out, outl);
+ if (result > 0) {
+ return result;
+ } else if (result == 0) {
+ b->num = 1;
+ } else if (socket->IsBlocking()) {
+ BIO_set_retry_read(b);
+ }
+ return -1;
+}
+
+static int socket_write(BIO* b, const char* in, int inl) {
+ if (!in)
+ return -1;
+ talk_base::AsyncSocket* socket = static_cast<talk_base::AsyncSocket*>(b->ptr);
+ BIO_clear_retry_flags(b);
+ int result = socket->Send(in, inl);
+ if (result > 0) {
+ return result;
+ } else if (socket->IsBlocking()) {
+ BIO_set_retry_write(b);
+ }
+ return -1;
+}
+
+static int socket_puts(BIO* b, const char* str) {
+ return socket_write(b, str, strlen(str));
+}
+
+static long socket_ctrl(BIO* b, int cmd, long num, void* ptr) {
+ UNUSED(num);
+ UNUSED(ptr);
+
+ switch (cmd) {
+ case BIO_CTRL_RESET:
+ return 0;
+ case BIO_CTRL_EOF:
+ return b->num;
+ case BIO_CTRL_WPENDING:
+ case BIO_CTRL_PENDING:
+ return 0;
+ case BIO_CTRL_FLUSH:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// OpenSSLAdapter
+/////////////////////////////////////////////////////////////////////////////
+
+namespace talk_base {
+
+OpenSSLAdapter::OpenSSLAdapter(AsyncSocket* socket)
+ : SSLAdapter(socket),
+ state_(SSL_NONE),
+ ssl_read_needs_write_(false),
+ ssl_write_needs_read_(false),
+ restartable_(false),
+ ssl_(NULL), ssl_ctx_(NULL) {
+}
+
+OpenSSLAdapter::~OpenSSLAdapter() {
+ Cleanup();
+}
+
+int
+OpenSSLAdapter::StartSSL(const char* hostname, bool restartable) {
+ if (state_ != SSL_NONE)
+ return -1;
+
+ ssl_host_name_ = hostname;
+ restartable_ = restartable;
+
+ if (socket_->GetState() != Socket::CS_CONNECTED) {
+ state_ = SSL_WAIT;
+ return 0;
+ }
+
+ state_ = SSL_CONNECTING;
+ if (int err = BeginSSL()) {
+ Error("BeginSSL", err, false);
+ return err;
+ }
+
+ return 0;
+}
+
+int
+OpenSSLAdapter::BeginSSL() {
+ LOG(LS_INFO) << "BeginSSL: " << ssl_host_name_;
+ ASSERT(state_ == SSL_CONNECTING);
+
+ int err = 0;
+ BIO* bio = NULL;
+
+ // First set up the context
+ if (!ssl_ctx_)
+ ssl_ctx_ = SetupSSLContext();
+
+ if (!ssl_ctx_) {
+ err = -1;
+ goto ssl_error;
+ }
+
+ bio = BIO_new_socket(static_cast<talk_base::AsyncSocketAdapter*>(socket_));
+ if (!bio) {
+ err = -1;
+ goto ssl_error;
+ }
+
+ ssl_ = SSL_new(ssl_ctx_);
+ if (!ssl_) {
+ err = -1;
+ goto ssl_error;
+ }
+
+ SSL_set_app_data(ssl_, this);
+
+ SSL_set_bio(ssl_, bio, bio);
+ SSL_set_mode(ssl_, SSL_MODE_ENABLE_PARTIAL_WRITE |
+ SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
+
+ // the SSL object owns the bio now
+ bio = NULL;
+
+ // Do the connect
+ err = ContinueSSL();
+ if (err != 0)
+ goto ssl_error;
+
+ return err;
+
+ssl_error:
+ Cleanup();
+ if (bio)
+ BIO_free(bio);
+
+ return err;
+}
+
+int
+OpenSSLAdapter::ContinueSSL() {
+ LOG(LS_INFO) << "ContinueSSL";
+ ASSERT(state_ == SSL_CONNECTING);
+
+ int code = SSL_connect(ssl_);
+ switch (SSL_get_error(ssl_, code)) {
+ case SSL_ERROR_NONE:
+ LOG(LS_INFO) << " -- success";
+
+ if (!SSLPostConnectionCheck(ssl_, ssl_host_name_.c_str())) {
+ LOG(LS_ERROR) << "TLS post connection check failed";
+ // make sure we close the socket
+ Cleanup();
+ // The connect failed so return -1 to shut down the socket
+ return -1;
+ }
+
+ state_ = SSL_CONNECTED;
+ AsyncSocketAdapter::OnConnectEvent(this);
+#if 0 // TODO: worry about this
+ // Don't let ourselves go away during the callbacks
+ PRefPtr<OpenSSLAdapter> lock(this);
+ LOG(LS_INFO) << " -- onStreamReadable";
+ AsyncSocketAdapter::OnReadEvent(this);
+ LOG(LS_INFO) << " -- onStreamWriteable";
+ AsyncSocketAdapter::OnWriteEvent(this);
+#endif
+ break;
+
+ case SSL_ERROR_WANT_READ:
+ LOG(LS_INFO) << " -- error want read";
+ break;
+
+ case SSL_ERROR_WANT_WRITE:
+ LOG(LS_INFO) << " -- error want write";
+ break;
+
+ case SSL_ERROR_ZERO_RETURN:
+ default:
+ LOG(LS_INFO) << " -- error " << code;
+ return (code != 0) ? code : -1;
+ }
+
+ return 0;
+}
+
+void
+OpenSSLAdapter::Error(const char* context, int err, bool signal) {
+ LOG(LS_WARNING) << "SChannelAdapter::Error("
+ << context << ", " << err << ")";
+ state_ = SSL_ERROR;
+ SetError(err);
+ if (signal)
+ AsyncSocketAdapter::OnCloseEvent(this, err);
+}
+
+void
+OpenSSLAdapter::Cleanup() {
+ LOG(LS_INFO) << "Cleanup";
+
+ state_ = SSL_NONE;
+ ssl_read_needs_write_ = false;
+ ssl_write_needs_read_ = false;
+
+ if (ssl_) {
+ SSL_free(ssl_);
+ ssl_ = NULL;
+ }
+
+ if (ssl_ctx_) {
+ SSL_CTX_free(ssl_ctx_);
+ ssl_ctx_ = NULL;
+ }
+}
+
+//
+// AsyncSocket Implementation
+//
+
+int
+OpenSSLAdapter::Send(const void* pv, size_t cb) {
+ //LOG(LS_INFO) << "OpenSSLAdapter::Send(" << cb << ")";
+
+ switch (state_) {
+ case SSL_NONE:
+ return AsyncSocketAdapter::Send(pv, cb);
+
+ case SSL_WAIT:
+ case SSL_CONNECTING:
+ SetError(EWOULDBLOCK);
+ return SOCKET_ERROR;
+
+ case SSL_CONNECTED:
+ break;
+
+ case SSL_ERROR:
+ default:
+ return SOCKET_ERROR;
+ }
+
+ // OpenSSL will return an error if we try to write zero bytes
+ if (cb == 0)
+ return 0;
+
+ ssl_write_needs_read_ = false;
+
+ int code = SSL_write(ssl_, pv, cb);
+ switch (SSL_get_error(ssl_, code)) {
+ case SSL_ERROR_NONE:
+ //LOG(LS_INFO) << " -- success";
+ return code;
+ case SSL_ERROR_WANT_READ:
+ //LOG(LS_INFO) << " -- error want read";
+ ssl_write_needs_read_ = true;
+ SetError(EWOULDBLOCK);
+ break;
+ case SSL_ERROR_WANT_WRITE:
+ //LOG(LS_INFO) << " -- error want write";
+ SetError(EWOULDBLOCK);
+ break;
+ case SSL_ERROR_ZERO_RETURN:
+ //LOG(LS_INFO) << " -- remote side closed";
+ SetError(EWOULDBLOCK);
+ // do we need to signal closure?
+ break;
+ default:
+ //LOG(LS_INFO) << " -- error " << code;
+ Error("SSL_write", (code ? code : -1), false);
+ break;
+ }
+
+ return SOCKET_ERROR;
+}
+
+int
+OpenSSLAdapter::Recv(void* pv, size_t cb) {
+ //LOG(LS_INFO) << "OpenSSLAdapter::Recv(" << cb << ")";
+ switch (state_) {
+
+ case SSL_NONE:
+ return AsyncSocketAdapter::Recv(pv, cb);
+
+ case SSL_WAIT:
+ case SSL_CONNECTING:
+ SetError(EWOULDBLOCK);
+ return SOCKET_ERROR;
+
+ case SSL_CONNECTED:
+ break;
+
+ case SSL_ERROR:
+ default:
+ return SOCKET_ERROR;
+ }
+
+ // Don't trust OpenSSL with zero byte reads
+ if (cb == 0)
+ return 0;
+
+ ssl_read_needs_write_ = false;
+
+ int code = SSL_read(ssl_, pv, cb);
+ switch (SSL_get_error(ssl_, code)) {
+ case SSL_ERROR_NONE:
+ //LOG(LS_INFO) << " -- success";
+ return code;
+ case SSL_ERROR_WANT_READ:
+ //LOG(LS_INFO) << " -- error want read";
+ SetError(EWOULDBLOCK);
+ break;
+ case SSL_ERROR_WANT_WRITE:
+ //LOG(LS_INFO) << " -- error want write";
+ ssl_read_needs_write_ = true;
+ SetError(EWOULDBLOCK);
+ break;
+ case SSL_ERROR_ZERO_RETURN:
+ //LOG(LS_INFO) << " -- remote side closed";
+ SetError(EWOULDBLOCK);
+ // do we need to signal closure?
+ break;
+ default:
+ //LOG(LS_INFO) << " -- error " << code;
+ Error("SSL_read", (code ? code : -1), false);
+ break;
+ }
+
+ return SOCKET_ERROR;
+}
+
+int
+OpenSSLAdapter::Close() {
+ Cleanup();
+ state_ = restartable_ ? SSL_WAIT : SSL_NONE;
+ return AsyncSocketAdapter::Close();
+}
+
+Socket::ConnState
+OpenSSLAdapter::GetState() const {
+ //if (signal_close_)
+ // return CS_CONNECTED;
+ ConnState state = socket_->GetState();
+ if ((state == CS_CONNECTED)
+ && ((state_ == SSL_WAIT) || (state_ == SSL_CONNECTING)))
+ state = CS_CONNECTING;
+ return state;
+}
+
+void
+OpenSSLAdapter::OnConnectEvent(AsyncSocket* socket) {
+ LOG(LS_INFO) << "OpenSSLAdapter::OnConnectEvent";
+ if (state_ != SSL_WAIT) {
+ ASSERT(state_ == SSL_NONE);
+ AsyncSocketAdapter::OnConnectEvent(socket);
+ return;
+ }
+
+ state_ = SSL_CONNECTING;
+ if (int err = BeginSSL()) {
+ AsyncSocketAdapter::OnCloseEvent(socket, err);
+ }
+}
+
+void
+OpenSSLAdapter::OnReadEvent(AsyncSocket* socket) {
+ //LOG(LS_INFO) << "OpenSSLAdapter::OnReadEvent";
+
+ if (state_ == SSL_NONE) {
+ AsyncSocketAdapter::OnReadEvent(socket);
+ return;
+ }
+
+ if (state_ == SSL_CONNECTING) {
+ if (int err = ContinueSSL()) {
+ Error("ContinueSSL", err);
+ }
+ return;
+ }
+
+ if (state_ != SSL_CONNECTED)
+ return;
+
+ // Don't let ourselves go away during the callbacks
+ //PRefPtr<OpenSSLAdapter> lock(this); // TODO: fix this
+ if (ssl_write_needs_read_) {
+ //LOG(LS_INFO) << " -- onStreamWriteable";
+ AsyncSocketAdapter::OnWriteEvent(socket);
+ }
+
+ //LOG(LS_INFO) << " -- onStreamReadable";
+ AsyncSocketAdapter::OnReadEvent(socket);
+}
+
+void
+OpenSSLAdapter::OnWriteEvent(AsyncSocket* socket) {
+ //LOG(LS_INFO) << "OpenSSLAdapter::OnWriteEvent";
+
+ if (state_ == SSL_NONE) {
+ AsyncSocketAdapter::OnWriteEvent(socket);
+ return;
+ }
+
+ if (state_ == SSL_CONNECTING) {
+ if (int err = ContinueSSL()) {
+ Error("ContinueSSL", err);
+ }
+ return;
+ }
+
+ if (state_ != SSL_CONNECTED)
+ return;
+
+ // Don't let ourselves go away during the callbacks
+ //PRefPtr<OpenSSLAdapter> lock(this); // TODO: fix this
+
+ if (ssl_read_needs_write_) {
+ //LOG(LS_INFO) << " -- onStreamReadable";
+ AsyncSocketAdapter::OnReadEvent(socket);
+ }
+
+ //LOG(LS_INFO) << " -- onStreamWriteable";
+ AsyncSocketAdapter::OnWriteEvent(socket);
+}
+
+void
+OpenSSLAdapter::OnCloseEvent(AsyncSocket* socket, int err) {
+ LOG(LS_INFO) << "OpenSSLAdapter::OnCloseEvent(" << err << ")";
+ AsyncSocketAdapter::OnCloseEvent(socket, err);
+}
+
+// This code is taken from the "Network Security with OpenSSL"
+// sample in chapter 5
+bool
+OpenSSLAdapter::SSLPostConnectionCheck(SSL* ssl, const char* host) {
+ if (!host)
+ return false;
+
+ // Checking the return from SSL_get_peer_certificate here is not strictly
+ // necessary. With our setup, it is not possible for it to return
+ // NULL. However, it is good form to check the return.
+ X509* certificate = SSL_get_peer_certificate(ssl);
+ if (!certificate)
+ return false;
+
+#ifdef _DEBUG
+ {
+ LOG(LS_INFO) << "Certificate from server:";
+ BIO* mem = BIO_new(BIO_s_mem());
+ X509_print_ex(mem, certificate, XN_FLAG_SEP_CPLUS_SPC, X509_FLAG_NO_HEADER);
+ BIO_write(mem, "\0", 1);
+ char* buffer;
+ BIO_get_mem_data(mem, &buffer);
+ LOG(LS_INFO) << buffer;
+ BIO_free(mem);
+
+ char* cipher_description =
+ SSL_CIPHER_description(SSL_get_current_cipher(ssl), NULL, 128);
+ LOG(LS_INFO) << "Cipher: " << cipher_description;
+ OPENSSL_free(cipher_description);
+ }
+#endif
+
+ bool ok = false;
+ int extension_count = X509_get_ext_count(certificate);
+ for (int i = 0; i < extension_count; ++i) {
+ X509_EXTENSION* extension = X509_get_ext(certificate, i);
+ int extension_nid = OBJ_obj2nid(X509_EXTENSION_get_object(extension));
+
+ if (extension_nid == NID_subject_alt_name) {
+ X509V3_EXT_METHOD* meth = X509V3_EXT_get(extension);
+ if (!meth)
+ break;
+
+ void* ext_str = NULL;
+
+#if OPENSSL_VERSION_NUMBER >= 0x0090800fL
+ const unsigned char **ext_value_data = (const_cast<const unsigned char **>
+ (&extension->value->data));
+#else
+ unsigned char **ext_value_data = &extension->value->data;
+#endif
+
+ if (meth->it) {
+ ext_str = ASN1_item_d2i(NULL, ext_value_data, extension->value->length,
+ ASN1_ITEM_ptr(meth->it));
+ } else {
+ ext_str = meth->d2i(NULL, ext_value_data, extension->value->length);
+ }
+
+ STACK_OF(CONF_VALUE)* value = meth->i2v(meth, ext_str, NULL);
+ for (int j = 0; j < sk_CONF_VALUE_num(value); ++j) {
+ CONF_VALUE* nval = sk_CONF_VALUE_value(value, j);
+ if (!strcmp(nval->name, "DNS") && !strcmp(nval->value, host)) {
+ ok = true;
+ break;
+ }
+ }
+ }
+ if (ok)
+ break;
+ }
+
+ char data[256];
+ X509_name_st* subject;
+ if (!ok
+ && (subject = X509_get_subject_name(certificate))
+ && (X509_NAME_get_text_by_NID(subject, NID_commonName,
+ data, sizeof(data)) > 0)) {
+ data[sizeof(data)-1] = 0;
+ if (_stricmp(data, host) == 0)
+ ok = true;
+ }
+
+ X509_free(certificate);
+
+ if (!ok && ignore_bad_cert()) {
+ LOG(LS_WARNING) << "TLS certificate check FAILED. "
+ << "Allowing connection anyway.";
+ ok = true;
+ }
+
+ if (ok)
+ ok = (SSL_get_verify_result(ssl) == X509_V_OK);
+
+ if (!ok && ignore_bad_cert()) {
+ LOG(LS_INFO) << "Other TLS post connection checks failed.";
+ ok = true;
+ }
+
+ return ok;
+}
+
+#if _DEBUG
+
+// We only use this for tracing and so it is only needed in debug mode
+
+void
+OpenSSLAdapter::SSLInfoCallback(const SSL* s, int where, int ret) {
+ const char* str = "undefined";
+ int w = where & ~SSL_ST_MASK;
+ if (w & SSL_ST_CONNECT) {
+ str = "SSL_connect";
+ } else if (w & SSL_ST_ACCEPT) {
+ str = "SSL_accept";
+ }
+ if (where & SSL_CB_LOOP) {
+ LOG(LS_INFO) << str << ":" << SSL_state_string_long(s);
+ } else if (where & SSL_CB_ALERT) {
+ str = (where & SSL_CB_READ) ? "read" : "write";
+ LOG(LS_INFO) << "SSL3 alert " << str
+ << ":" << SSL_alert_type_string_long(ret)
+ << ":" << SSL_alert_desc_string_long(ret);
+ } else if (where & SSL_CB_EXIT) {
+ if (ret == 0) {
+ LOG(LS_INFO) << str << ":failed in " << SSL_state_string_long(s);
+ } else if (ret < 0) {
+ LOG(LS_INFO) << str << ":error in " << SSL_state_string_long(s);
+ }
+ }
+}
+
+#endif // _DEBUG
+
+int
+OpenSSLAdapter::SSLVerifyCallback(int ok, X509_STORE_CTX* store) {
+#if _DEBUG
+ if (!ok) {
+ char data[256];
+ X509* cert = X509_STORE_CTX_get_current_cert(store);
+ int depth = X509_STORE_CTX_get_error_depth(store);
+ int err = X509_STORE_CTX_get_error(store);
+
+ LOG(LS_INFO) << "Error with certificate at depth: " << depth;
+ X509_NAME_oneline(X509_get_issuer_name(cert), data, sizeof(data));
+ LOG(LS_INFO) << " issuer = " << data;
+ X509_NAME_oneline(X509_get_subject_name(cert), data, sizeof(data));
+ LOG(LS_INFO) << " subject = " << data;
+ LOG(LS_INFO) << " err = " << err
+ << ":" << X509_verify_cert_error_string(err);
+ }
+#endif
+
+ // Get our stream pointer from the store
+ SSL* ssl = reinterpret_cast<SSL*>(
+ X509_STORE_CTX_get_ex_data(store,
+ SSL_get_ex_data_X509_STORE_CTX_idx()));
+
+ OpenSSLAdapter* stream =
+ reinterpret_cast<OpenSSLAdapter*>(SSL_get_app_data(ssl));
+
+ if (!ok && stream->ignore_bad_cert()) {
+ LOG(LS_WARNING) << "Ignoring cert error while verifying cert chain";
+ ok = 1;
+ }
+
+ return ok;
+}
+
+SSL_CTX*
+OpenSSLAdapter::SetupSSLContext() {
+ SSL_CTX* ctx = SSL_CTX_new(TLSv1_client_method());
+ if (ctx == NULL)
+ return NULL;
+
+ // Add the root cert to the SSL context
+#if OPENSSL_VERSION_NUMBER >= 0x0090800fL
+ const unsigned char* cert_buffer
+#else
+ unsigned char* cert_buffer
+#endif
+ = EquifaxSecureGlobalEBusinessCA1_certificate;
+ size_t cert_buffer_len = sizeof(EquifaxSecureGlobalEBusinessCA1_certificate);
+ X509* cert = d2i_X509(NULL, &cert_buffer, cert_buffer_len);
+ if (cert == NULL) {
+ SSL_CTX_free(ctx);
+ return NULL;
+ }
+ if (!X509_STORE_add_cert(SSL_CTX_get_cert_store(ctx), cert)) {
+ X509_free(cert);
+ SSL_CTX_free(ctx);
+ return NULL;
+ }
+
+#ifdef _DEBUG
+ SSL_CTX_set_info_callback(ctx, SSLInfoCallback);
+#endif
+
+ SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, SSLVerifyCallback);
+ SSL_CTX_set_verify_depth(ctx, 4);
+ SSL_CTX_set_cipher_list(ctx, "ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH");
+
+ return ctx;
+}
+
+} // namespace talk_base
diff --git a/Plugins/jingle/libjingle/talk/base/openssladapter.h b/Plugins/jingle/libjingle/talk/base/openssladapter.h
new file mode 100644
index 0000000..b794a7b
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/openssladapter.h
@@ -0,0 +1,94 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TALK_BASE_OPENSSLADAPTER_H__
+#define TALK_BASE_OPENSSLADAPTER_H__
+
+#include <string>
+#include "talk/base/ssladapter.h"
+
+typedef struct ssl_st SSL;
+typedef struct ssl_ctx_st SSL_CTX;
+typedef struct x509_store_ctx_st X509_STORE_CTX;
+
+namespace talk_base {
+
+///////////////////////////////////////////////////////////////////////////////
+
+class OpenSSLAdapter : public SSLAdapter {
+public:
+ OpenSSLAdapter(AsyncSocket* socket);
+ virtual ~OpenSSLAdapter();
+
+ virtual int StartSSL(const char* hostname, bool restartable);
+ virtual int Send(const void* pv, size_t cb);
+ virtual int Recv(void* pv, size_t cb);
+ virtual int Close();
+
+ // Note that the socket returns ST_CONNECTING while SSL is being negotiated.
+ virtual ConnState GetState() const;
+
+protected:
+ virtual void OnConnectEvent(AsyncSocket* socket);
+ virtual void OnReadEvent(AsyncSocket* socket);
+ virtual void OnWriteEvent(AsyncSocket* socket);
+ virtual void OnCloseEvent(AsyncSocket* socket, int err);
+
+private:
+ enum SSLState {
+ SSL_NONE, SSL_WAIT, SSL_CONNECTING, SSL_CONNECTED, SSL_ERROR
+ };
+
+ int BeginSSL();
+ int ContinueSSL();
+ void Error(const char* context, int err, bool signal = true);
+ void Cleanup();
+
+ bool SSLPostConnectionCheck(SSL* ssl, const char* host);
+#if _DEBUG
+ static void SSLInfoCallback(const SSL* s, int where, int ret);
+#endif // !_DEBUG
+ static int SSLVerifyCallback(int ok, X509_STORE_CTX* store);
+
+ static SSL_CTX* SetupSSLContext();
+
+ SSLState state_;
+ bool ssl_read_needs_write_;
+ bool ssl_write_needs_read_;
+ // If true, socket will retain SSL configuration after Close.
+ bool restartable_;
+
+ SSL* ssl_;
+ SSL_CTX* ssl_ctx_;
+ std::string ssl_host_name_;
+};
+
+/////////////////////////////////////////////////////////////////////////////
+
+} // namespace talk_base
+
+#endif // TALK_BASE_OPENSSLADAPTER_H__
diff --git a/Plugins/jingle/libjingle/talk/base/pathutils.cc b/Plugins/jingle/libjingle/talk/base/pathutils.cc
new file mode 100644
index 0000000..4a77879
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/pathutils.cc
@@ -0,0 +1,426 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifdef WIN32
+#include "talk/base/win32.h"
+#include <shellapi.h>
+#include <shlobj.h>
+#include <tchar.h>
+#endif // WIN32
+
+#include "talk/base/common.h"
+#include "talk/base/pathutils.h"
+#include "talk/base/stringutils.h"
+#include "talk/base/urlencode.h"
+
+namespace talk_base {
+
+std::string const EMPTY_STR = "";
+
+// EXT_DELIM separates a file basename from extension
+const char EXT_DELIM = '.';
+
+// FOLDER_DELIMS separate folder segments and the filename
+const char* const FOLDER_DELIMS = "/\\";
+
+// DEFAULT_FOLDER_DELIM is the preferred delimiter for this platform
+#if WIN32
+const char DEFAULT_FOLDER_DELIM = '\\';
+#else // !WIN32
+const char DEFAULT_FOLDER_DELIM = '/';
+#endif // !WIN32
+
+///////////////////////////////////////////////////////////////////////////////
+// Pathname - parsing of pathnames into components, and vice versa
+///////////////////////////////////////////////////////////////////////////////
+
+bool Pathname::IsFolderDelimiter(char ch) {
+ return (NULL != ::strchr(FOLDER_DELIMS, ch));
+}
+
+Pathname::Pathname()
+ : folder_delimiter_(DEFAULT_FOLDER_DELIM) {
+}
+
+Pathname::Pathname(const std::string& pathname)
+ : folder_delimiter_(DEFAULT_FOLDER_DELIM) {
+ SetPathname(pathname);
+}
+
+void Pathname::SetFolderDelimiter(char delimiter) {
+ ASSERT(IsFolderDelimiter(delimiter));
+ folder_delimiter_ = delimiter;
+}
+
+void Pathname::Normalize() {
+ for (size_t i=0; i<folder_.length(); ++i) {
+ if (IsFolderDelimiter(folder_[i])) {
+ folder_[i] = folder_delimiter_;
+ }
+ }
+}
+
+void Pathname::clear() {
+ folder_.clear();
+ basename_.clear();
+ extension_.clear();
+}
+
+std::string Pathname::pathname() const {
+ std::string pathname(folder_);
+ pathname.append(basename_);
+ pathname.append(extension_);
+ return pathname;
+}
+
+std::string Pathname::url() const {
+ std::string s = "file://";
+ for (size_t i=0; i<folder_.length(); ++i) {
+ if (i == 1 && folder_[i] == ':') // drive letter
+ s += '|';
+ else if (IsFolderDelimiter(folder_[i]))
+ s += '/';
+ else
+ s += folder_[i];
+ }
+ s += basename_;
+ s += extension_;
+ return UrlEncodeString(s);
+}
+
+void Pathname::SetPathname(const std::string &pathname) {
+ std::string::size_type pos = pathname.find_last_of(FOLDER_DELIMS);
+ if (pos != std::string::npos) {
+ SetFolder(pathname.substr(0, pos + 1));
+ SetFilename(pathname.substr(pos + 1));
+ } else {
+ SetFolder(EMPTY_STR);
+ SetFilename(pathname);
+ }
+}
+
+void Pathname::AppendPathname(const Pathname& pathname) {
+ std::string full_pathname(folder_);
+ full_pathname.append(pathname.pathname());
+ SetPathname(full_pathname);
+}
+
+std::string Pathname::folder() const {
+ return folder_;
+}
+
+std::string Pathname::folder_name() const {
+ std::string::size_type pos = std::string::npos;
+ if (folder_.size() >= 2) {
+ pos = folder_.find_last_of(FOLDER_DELIMS, folder_.length() - 2);
+ }
+ if (pos != std::string::npos) {
+ return folder_.substr(pos + 1);
+ } else {
+ return folder_;
+ }
+}
+
+std::string Pathname::parent_folder() const {
+ std::string::size_type pos = std::string::npos;
+ if (folder_.size() >= 2) {
+ pos = folder_.find_last_of(FOLDER_DELIMS, folder_.length() - 2);
+ }
+ if (pos != std::string::npos) {
+ return folder_.substr(0, pos + 1);
+ } else {
+ return EMPTY_STR;
+ }
+}
+
+void Pathname::SetFolder(const std::string& folder) {
+ folder_.assign(folder);
+ // Ensure folder ends in a path delimiter
+ if (!folder_.empty() && !IsFolderDelimiter(folder_[folder_.length()-1])) {
+ folder_.push_back(folder_delimiter_);
+ }
+}
+
+void Pathname::AppendFolder(const std::string& folder) {
+ folder_.append(folder);
+ // Ensure folder ends in a path delimiter
+ if (!folder_.empty() && !IsFolderDelimiter(folder_[folder_.length()-1])) {
+ folder_.push_back(folder_delimiter_);
+ }
+}
+
+std::string Pathname::basename() const {
+ return basename_;
+}
+
+void Pathname::SetBasename(const std::string& basename) {
+ ASSERT(basename.find_first_of(FOLDER_DELIMS) == std::string::npos);
+ basename_.assign(basename);
+}
+
+std::string Pathname::extension() const {
+ return extension_;
+}
+
+void Pathname::SetExtension(const std::string& extension) {
+ ASSERT(extension.find_first_of(FOLDER_DELIMS) == std::string::npos);
+ ASSERT(extension.find_first_of(EXT_DELIM, 1) == std::string::npos);
+ extension_.assign(extension);
+ // Ensure extension begins with the extension delimiter
+ if (!extension_.empty() && (extension_[0] != EXT_DELIM)) {
+ extension_.insert(extension_.begin(), EXT_DELIM);
+ }
+}
+
+std::string Pathname::filename() const {
+ std::string filename(basename_);
+ filename.append(extension_);
+ return filename;
+}
+
+void Pathname::SetFilename(const std::string& filename) {
+ std::string::size_type pos = filename.rfind(EXT_DELIM);
+ if ((pos == std::string::npos) || (pos == 0)) {
+ SetBasename(filename);
+ SetExtension(EMPTY_STR);
+ } else {
+ SetBasename(filename.substr(0, pos));
+ SetExtension(filename.substr(pos));
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// CreateUniqueFile
+///////////////////////////////////////////////////////////////////////////////
+
+std::string g_organization_name;
+std::string g_application_name;
+
+void SetOrganizationName(const std::string& organization) {
+ g_organization_name = organization;
+}
+
+void SetApplicationName(const std::string& application) {
+ g_application_name = application;
+}
+
+bool CreateFolder(const talk_base::Pathname& path) {
+#ifdef WIN32
+ if (!path.filename().empty())
+ return false;
+
+ std::wstring pathname16 = ToUtf16(path.pathname());
+ if (!pathname16.empty() && (pathname16[0] != '\\')) {
+ pathname16 = L"\\\\?\\" + pathname16;
+ }
+
+ DWORD res = ::GetFileAttributes(pathname16.c_str());
+ if (res != INVALID_FILE_ATTRIBUTES) {
+ // Something exists at this location, check if it is a directory
+ return ((res & FILE_ATTRIBUTE_DIRECTORY) != 0);
+ } else if ((GetLastError() != ERROR_FILE_NOT_FOUND)
+ && (GetLastError() != ERROR_PATH_NOT_FOUND)) {
+ // Unexpected error
+ return false;
+ }
+
+ // Directory doesn't exist, look up one directory level
+ if (!path.parent_folder().empty()) {
+ talk_base::Pathname parent(path);
+ parent.SetFolder(path.parent_folder());
+ if (!CreateFolder(parent)) {
+ return false;
+ }
+ }
+
+ return (::CreateDirectory(pathname16.c_str(), NULL) != 0);
+#else // !WIN32
+ return false;
+#endif // !WIN32
+}
+
+bool FinishPath(talk_base::Pathname& path, bool create,
+ const std::string& append) {
+ if (!append.empty()) {
+ path.AppendFolder(append);
+ }
+ if (create && !CreateFolder(path))
+ return false;
+ return true;
+}
+
+bool GetTemporaryFolder(talk_base::Pathname& path, bool create,
+ const std::string& append) {
+ ASSERT(!g_application_name.empty());
+#ifdef WIN32
+ TCHAR buffer[MAX_PATH + 1];
+ if (!::GetTempPath(ARRAY_SIZE(buffer), buffer))
+ return false;
+ if (!::GetLongPathName(buffer, buffer, ARRAY_SIZE(buffer)))
+ return false;
+ size_t len = strlen(buffer);
+ if ((len > 0) && (buffer[len-1] != __T('\\'))) {
+ len += talk_base::strcpyn(buffer + len, ARRAY_SIZE(buffer) - len,
+ __T("\\"));
+ }
+ len += talk_base::strcpyn(buffer + len, ARRAY_SIZE(buffer) - len,
+ ToUtf16(g_application_name).c_str());
+ if ((len > 0) && (buffer[len-1] != __T('\\'))) {
+ len += talk_base::strcpyn(buffer + len, ARRAY_SIZE(buffer) - len,
+ __T("\\"));
+ }
+ if (len >= ARRAY_SIZE(buffer) - 1)
+ return false;
+ path.clear();
+ path.SetFolder(ToUtf8(buffer));
+ return FinishPath(path, create, append);
+#else // !WIN32
+ return false;
+#endif // !WIN32
+}
+
+bool GetAppDataFolder(talk_base::Pathname& path, bool create,
+ const std::string& append) {
+ ASSERT(!g_organization_name.empty());
+ ASSERT(!g_application_name.empty());
+#ifdef WIN32
+ TCHAR buffer[MAX_PATH + 1];
+ if (!::SHGetSpecialFolderPath(NULL, buffer, CSIDL_LOCAL_APPDATA, TRUE))
+ return false;
+ if (!::GetLongPathName(buffer, buffer, ARRAY_SIZE(buffer)))
+ return false;
+ size_t len = talk_base::strcatn(buffer, ARRAY_SIZE(buffer), _T("\\"));
+ len += talk_base::strcpyn(buffer + len, ARRAY_SIZE(buffer) - len,
+ ToUtf16(g_organization_name).c_str());
+ if ((len > 0) && (buffer[len-1] != __T('\\'))) {
+ len += talk_base::strcpyn(buffer + len, ARRAY_SIZE(buffer) - len,
+ __T("\\"));
+ }
+ len += talk_base::strcpyn(buffer + len, ARRAY_SIZE(buffer) - len,
+ ToUtf16(g_application_name).c_str());
+ if ((len > 0) && (buffer[len-1] != __T('\\'))) {
+ len += talk_base::strcpyn(buffer + len, ARRAY_SIZE(buffer) - len,
+ __T("\\"));
+ }
+ if (len >= ARRAY_SIZE(buffer) - 1)
+ return false;
+ path.clear();
+ path.SetFolder(ToUtf8(buffer));
+ return FinishPath(path, create, append);
+#else // !WIN32
+ return false;
+#endif // !WIN32
+}
+
+bool CleanupTemporaryFolder() {
+#ifdef WIN32
+ talk_base::Pathname temp_path;
+ if (!GetTemporaryFolder(temp_path, false, ""))
+ return false;
+
+ std::wstring temp_path16 = ToUtf16(temp_path.pathname());
+ temp_path16.append(1, '*');
+ temp_path16.append(1, '\0');
+
+ SHFILEOPSTRUCT file_op = { 0 };
+ file_op.wFunc = FO_DELETE;
+ file_op.pFrom = temp_path16.c_str();
+ file_op.fFlags = FOF_NOCONFIRMATION | FOF_NOERRORUI | FOF_SILENT;
+ return (0 == SHFileOperation(&file_op));
+#else // !WIN32
+ return false;
+#endif // !WIN32
+}
+#if 0
+bool CreateUnijqueFile(talk_base::Pathname& path, bool create_empty) {
+#ifdef WIN32
+ // If not folder is supplied, use the temporary folder
+ if (path.folder().empty()) {
+ talk_base::Pathname temp_path;
+ if (!GetTemporaryFolder(temp_path, true, "")) {
+
+ return false;
+ }
+ path.SetFolder(temp_path.folder());
+ }
+ printf("path: %s\n", path.pathname());
+ // If not filename is supplied, use a temporary name
+ if (path.filename().empty()) {
+ TCHAR filename[MAX_PATH];
+ std::wstring folder((ToUtf16)(path.folder()));
+ if (!::GetTempFileName(folder.c_str(), __T("gt"), 0, filename))
+ return false;
+ ASSERT(wcsncmp(folder.c_str(), filename, folder.length()) == 0);
+ path.SetFilename(ToUtf8(filename + folder.length()));
+ if (!create_empty) {
+ VERIFY(::DeleteFile(ToUtf16(path.pathname()).c_str()) != FALSE);
+ }
+ return true;
+ }
+ // Otherwise, create a unique name based on the given filename
+ // foo.txt -> foo-N.txt
+ const std::string basename = path.basename();
+ const size_t MAX_VERSION = 100;
+ size_t version = 0;
+ while (version < MAX_VERSION) {
+ std::string pathname = path.pathname();
+ std::wstring pathname16 = ToUtf16(pathname).c_str();
+
+ if (pathname16[0] != __T('\\'))
+ pathname16 = __T("\\\\?\\") + pathname16;
+
+ HANDLE hfile = CreateFile(pathname16.c_str(), GENERIC_WRITE, 0,
+ NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (hfile != INVALID_HANDLE_VALUE) {
+ CloseHandle(hfile);
+ if (!create_empty) {
+ VERIFY(::DeleteFile(pathname16.c_str()) != FALSE);
+ }
+ return true;
+ } else {
+ int err = GetLastError();
+ if (err != ERROR_FILE_EXISTS && err != ERROR_ACCESS_DENIED) {
+ return false;
+ }
+ }
+
+ version += 1;
+ char version_base[MAX_PATH];
+ talk_base::sprintfn(version_base, ARRAY_SIZE(version_base), "%s-%u",
+ basename.c_str(), version);
+ path.SetBasename(version_base);
+ }
+ return false;
+#else // !WIN32
+ // TODO: Make this better.
+ path.SetBasename("/tmp/temp-1");
+#endif // !WIN32
+}
+#endif
+///////////////////////////////////////////////////////////////////////////////
+
+} // namespace talk_base
diff --git a/Plugins/jingle/libjingle/talk/base/pathutils.h b/Plugins/jingle/libjingle/talk/base/pathutils.h
new file mode 100644
index 0000000..eb33edf
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/pathutils.h
@@ -0,0 +1,110 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TALK_BASE_PATHUTILS_H__
+#define TALK_BASE_PATHUTILS_H__
+
+#include <string>
+
+namespace talk_base {
+
+///////////////////////////////////////////////////////////////////////////////
+// Pathname - parsing of pathnames into components, and vice versa.
+//
+// To establish consistent terminology, a filename never contains a folder
+// component. A folder never contains a filename. A pathname may include
+// a folder and/or filename component. Here are some examples:
+//
+// pathname() /home/john/example.txt
+// folder() /home/john/
+// filename() example.txt
+// parent_folder() /home/
+// folder_name() john/
+// basename() example
+// extension() .txt
+//
+// Basename may begin, end, and/or include periods, but no folder delimiters.
+// If extension exists, it consists of a period followed by zero or more
+// non-period/non-delimiter characters, and basename is non-empty.
+///////////////////////////////////////////////////////////////////////////////
+
+class Pathname {
+public:
+ // Folder delimiters are slash and backslash
+ static bool IsFolderDelimiter(char ch);
+
+ Pathname();
+ Pathname(const std::string& pathname);
+
+ // Set's the default folder delimiter for this Pathname
+ char folder_delimiter() const { return folder_delimiter_; }
+ void SetFolderDelimiter(char delimiter);
+
+ // Normalize changes all folder delimiters to folder_delimiter()
+ void Normalize();
+
+ // Reset to the empty pathname
+ void clear();
+
+ std::string url() const;
+
+ std::string pathname() const;
+ void SetPathname(const std::string& pathname);
+
+ // Append pathname to the current folder (if any). Any existing filename
+ // will be discarded.
+ void AppendPathname(const Pathname& pathname);
+
+ std::string folder() const;
+ std::string folder_name() const;
+ std::string parent_folder() const;
+ // SetFolder and AppendFolder will append a folder delimiter, if needed.
+ void SetFolder(const std::string& folder);
+ void AppendFolder(const std::string& folder);
+
+ std::string basename() const;
+ void SetBasename(const std::string& basename);
+
+ std::string extension() const;
+ // SetExtension will prefix a period, if needed.
+ void SetExtension(const std::string& extension);
+
+ std::string filename() const;
+ void SetFilename(const std::string& filename);
+
+private:
+
+ std::string folder_, basename_, extension_;
+ char folder_delimiter_;
+};
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+} // namespace talk_base
+
+#endif // TALK_BASE_PATHUTILS_H__
diff --git a/Plugins/jingle/libjingle/talk/base/physicalsocketserver.cc b/Plugins/jingle/libjingle/talk/base/physicalsocketserver.cc
new file mode 100644
index 0000000..9cdacdf
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/physicalsocketserver.cc
@@ -0,0 +1,1132 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#if defined(_MSC_VER) && _MSC_VER < 1300
+#pragma warning(disable:4786)
+#endif
+
+#include <cassert>
+
+#ifdef POSIX
+extern "C" {
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/time.h>
+#include <unistd.h>
+}
+#endif
+
+#ifdef WIN32
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#define _WINSOCKAPI_
+#include <windows.h>
+#undef SetPort
+#endif
+
+#include <algorithm>
+#include <iostream>
+
+#include "talk/base/basictypes.h"
+#include "talk/base/byteorder.h"
+#include "talk/base/common.h"
+#include "talk/base/logging.h"
+#include "talk/base/physicalsocketserver.h"
+#include "talk/base/time.h"
+#include "talk/base/winping.h"
+
+#ifdef __linux
+#define IP_MTU 14 // Until this is integrated from linux/in.h to netinet/in.h
+#endif // __linux
+
+#ifdef WIN32
+class WinsockInitializer {
+public:
+ WinsockInitializer() {
+ WSADATA wsaData;
+ WORD wVersionRequested = MAKEWORD(1, 0);
+ err_ = WSAStartup(wVersionRequested, &wsaData);
+ }
+ ~WinsockInitializer() {
+ WSACleanup();
+ }
+ int error() {
+ return err_;
+ }
+private:
+ int err_;
+};
+WinsockInitializer g_winsockinit;
+#endif
+
+namespace talk_base {
+
+const int kfRead = 0x0001;
+const int kfWrite = 0x0002;
+const int kfConnect = 0x0004;
+const int kfClose = 0x0008;
+
+// Standard MTUs
+const uint16 PACKET_MAXIMUMS[] = {
+ 65535, // Theoretical maximum, Hyperchannel
+ 32000, // Nothing
+ 17914, // 16Mb IBM Token Ring
+ 8166, // IEEE 802.4
+ //4464, // IEEE 802.5 (4Mb max)
+ 4352, // FDDI
+ //2048, // Wideband Network
+ 2002, // IEEE 802.5 (4Mb recommended)
+ //1536, // Expermental Ethernet Networks
+ //1500, // Ethernet, Point-to-Point (default)
+ 1492, // IEEE 802.3
+ 1006, // SLIP, ARPANET
+ //576, // X.25 Networks
+ //544, // DEC IP Portal
+ //512, // NETBIOS
+ 508, // IEEE 802/Source-Rt Bridge, ARCNET
+ 296, // Point-to-Point (low delay)
+ 68, // Official minimum
+ 0, // End of list marker
+};
+
+const uint32 IP_HEADER_SIZE = 20;
+const uint32 ICMP_HEADER_SIZE = 8;
+
+class PhysicalSocket : public AsyncSocket {
+public:
+ PhysicalSocket(PhysicalSocketServer* ss, SOCKET s = INVALID_SOCKET)
+ : ss_(ss), s_(s), enabled_events_(0), error_(0),
+ state_((s == INVALID_SOCKET) ? CS_CLOSED : CS_CONNECTED) {
+ if (s != INVALID_SOCKET)
+ enabled_events_ = kfRead | kfWrite;
+ }
+
+ virtual ~PhysicalSocket() {
+ Close();
+ }
+
+ // Creates the underlying OS socket (same as the "socket" function).
+ virtual bool Create(int type) {
+ Close();
+ s_ = ::socket(AF_INET, type, 0);
+ UpdateLastError();
+ if (type != SOCK_STREAM)
+ enabled_events_ = kfRead | kfWrite;
+ return s_ != INVALID_SOCKET;
+ }
+
+ SocketAddress GetLocalAddress() const {
+ sockaddr_in addr;
+ socklen_t addrlen = sizeof(addr);
+ int result = ::getsockname(s_, (sockaddr*)&addr, &addrlen);
+ ASSERT(addrlen == sizeof(addr));
+ talk_base::SocketAddress address;
+ if (result >= 0) {
+ address.FromSockAddr(addr);
+ } else {
+ ASSERT(result >= 0);
+ }
+ return address;
+ }
+
+ SocketAddress GetRemoteAddress() const {
+ sockaddr_in addr;
+ socklen_t addrlen = sizeof(addr);
+ int result = ::getpeername(s_, (sockaddr*)&addr, &addrlen);
+ ASSERT(addrlen == sizeof(addr));
+ talk_base::SocketAddress address;
+ if (result >= 0) {
+ address.FromSockAddr(addr);
+ } else {
+ ASSERT(errno == ENOTCONN);
+ }
+ return address;
+ }
+
+ int Bind(const SocketAddress& addr) {
+ sockaddr_in saddr;
+ addr.ToSockAddr(&saddr);
+ int err = ::bind(s_, (sockaddr*)&saddr, sizeof(saddr));
+ UpdateLastError();
+ return err;
+ }
+
+ int Connect(const SocketAddress& addr) {
+ // TODO: Implicit creation is required to reconnect...
+ // ...but should we make it more explicit?
+ if ((s_ == INVALID_SOCKET) && !Create(SOCK_STREAM))
+ return SOCKET_ERROR;
+ SocketAddress addr2(addr);
+ if (addr2.IsUnresolved()) {
+ LOG(INFO) << "Resolving addr in PhysicalSocket::Connect";
+ addr2.Resolve(); // TODO: Do this async later?
+ }
+ sockaddr_in saddr;
+ addr2.ToSockAddr(&saddr);
+ int err = ::connect(s_, (sockaddr*)&saddr, sizeof(saddr));
+ UpdateLastError();
+ //LOG(INFO) << "SOCK[" << static_cast<int>(s_) << "] Connect(" << addr2.ToString() << ") Ret: " << err << " Error: " << error_;
+ if (err == 0) {
+ state_ = CS_CONNECTED;
+ } else if (IsBlockingError(error_)) {
+ state_ = CS_CONNECTING;
+ enabled_events_ |= kfConnect;
+ }
+ enabled_events_ |= kfRead | kfWrite;
+ return err;
+ }
+
+ int GetError() const {
+ return error_;
+ }
+
+ void SetError(int error) {
+ error_ = error;
+ }
+
+ ConnState GetState() const {
+ return state_;
+ }
+
+ int SetOption(Option opt, int value) {
+ assert(opt == OPT_DONTFRAGMENT);
+#ifdef WIN32
+ value = (value == 0) ? 0 : 1;
+ return ::setsockopt(
+ s_, IPPROTO_IP, IP_DONTFRAGMENT, reinterpret_cast<char*>(&value),
+ sizeof(value));
+#endif
+#ifdef __linux
+ value = (value == 0) ? IP_PMTUDISC_DONT : IP_PMTUDISC_DO;
+ return ::setsockopt(
+ s_, IPPROTO_IP, IP_MTU_DISCOVER, &value, sizeof(value));
+#endif
+#ifdef OSX
+ // This is not possible on OSX.
+ return -1;
+#endif
+ }
+
+ int Send(const void *pv, size_t cb) {
+ int sent = ::send(s_, reinterpret_cast<const char *>(pv), (int)cb, 0);
+ UpdateLastError();
+ //LOG(INFO) << "SOCK[" << static_cast<int>(s_) << "] Send(" << cb << ") Ret: " << sent << " Error: " << error_;
+ ASSERT(sent <= static_cast<int>(cb)); // We have seen minidumps where this may be false
+ if ((sent < 0) && IsBlockingError(error_)) {
+ enabled_events_ |= kfWrite;
+ }
+ return sent;
+ }
+
+ int SendTo(const void *pv, size_t cb, const SocketAddress& addr) {
+ sockaddr_in saddr;
+ addr.ToSockAddr(&saddr);
+ int sent = ::sendto(
+ s_, (const char *)pv, (int)cb, 0, (sockaddr*)&saddr,
+ sizeof(saddr));
+ UpdateLastError();
+ ASSERT(sent <= static_cast<int>(cb)); // We have seen minidumps where this may be false
+ if ((sent < 0) && IsBlockingError(error_)) {
+ enabled_events_ |= kfWrite;
+ }
+ return sent;
+ }
+
+ int Recv(void *pv, size_t cb) {
+ int received = ::recv(s_, (char *)pv, (int)cb, 0);
+ if ((received == 0) && (cb != 0)) {
+ // Note: on graceful shutdown, recv can return 0. In this case, we
+ // pretend it is blocking, and then signal close, so that simplifying
+ // assumptions can be made about Recv.
+ error_ = EWOULDBLOCK;
+ return SOCKET_ERROR;
+ }
+ UpdateLastError();
+ if ((received >= 0) || IsBlockingError(error_)) {
+ enabled_events_ |= kfRead;
+ }
+ return received;
+ }
+
+ int RecvFrom(void *pv, size_t cb, SocketAddress *paddr) {
+ sockaddr_in saddr;
+ socklen_t cbAddr = sizeof(saddr);
+ int received = ::recvfrom(s_, (char *)pv, (int)cb, 0, (sockaddr*)&saddr,
+ &cbAddr);
+ UpdateLastError();
+ if ((received >= 0) && (paddr != NULL))
+ paddr->FromSockAddr(saddr);
+ if ((received >= 0) || IsBlockingError(error_)) {
+ enabled_events_ |= kfRead;
+ }
+ return received;
+ }
+
+ int Listen(int backlog) {
+ int err = ::listen(s_, backlog);
+ UpdateLastError();
+ if (err == 0)
+ state_ = CS_CONNECTING;
+ enabled_events_ |= kfRead;
+
+ return err;
+ }
+
+ Socket* Accept(SocketAddress *paddr) {
+ sockaddr_in saddr;
+ socklen_t cbAddr = sizeof(saddr);
+ SOCKET s = ::accept(s_, (sockaddr*)&saddr, &cbAddr);
+ UpdateLastError();
+ if (s == INVALID_SOCKET)
+ return NULL;
+ if (paddr != NULL)
+ paddr->FromSockAddr(saddr);
+ enabled_events_ |= kfRead | kfWrite;
+ return ss_->WrapSocket(s);
+ }
+
+ int Close() {
+ if (s_ == INVALID_SOCKET)
+ return 0;
+ int err = ::closesocket(s_);
+ UpdateLastError();
+ //LOG(INFO) << "SOCK[" << static_cast<int>(s_) << "] Close() Ret: " << err << " Error: " << error_;
+ s_ = INVALID_SOCKET;
+ state_ = CS_CLOSED;
+ enabled_events_ = 0;
+ return err;
+ }
+
+ int EstimateMTU(uint16* mtu) {
+ SocketAddress addr = GetRemoteAddress();
+ if (addr.IsAny()) {
+ error_ = ENOTCONN;
+ return -1;
+ }
+
+#ifdef WIN32
+
+ WinPing ping;
+ if (!ping.IsValid()) {
+ error_ = EINVAL; // can't think of a better error ID
+ return -1;
+ }
+
+ for (int level = 0; PACKET_MAXIMUMS[level + 1] > 0; ++level) {
+ int32 size = PACKET_MAXIMUMS[level] - IP_HEADER_SIZE - ICMP_HEADER_SIZE;
+ WinPing::PingResult result = ping.Ping(addr.ip(), size, 0, 1, false);
+ if (result == WinPing::PING_FAIL) {
+ error_ = EINVAL; // can't think of a better error ID
+ return -1;
+ }
+ if (result != WinPing::PING_TOO_LARGE) {
+ *mtu = PACKET_MAXIMUMS[level];
+ return 0;
+ }
+ }
+
+ assert(false);
+ return 0;
+
+#endif // WIN32
+
+#ifdef __linux
+
+ int value;
+ socklen_t vlen = sizeof(value);
+ int err = getsockopt(s_, IPPROTO_IP, IP_MTU, &value, &vlen);
+ if (err < 0) {
+ UpdateLastError();
+ return err;
+ }
+
+ assert((0 <= value) && (value <= 65536));
+ *mtu = uint16(value);
+ return 0;
+
+#endif // __linux
+
+ // TODO: OSX support
+ }
+
+ SocketServer* socketserver() { return ss_; }
+
+protected:
+ PhysicalSocketServer* ss_;
+ SOCKET s_;
+ uint32 enabled_events_;
+ int error_;
+ ConnState state_;
+
+ void UpdateLastError() {
+#ifdef WIN32
+ error_ = WSAGetLastError();
+#endif
+#ifdef POSIX
+ error_ = errno;
+#endif
+ }
+};
+
+#ifdef POSIX
+class Dispatcher {
+public:
+ virtual uint32 GetRequestedEvents() = 0;
+ virtual void OnPreEvent(uint32 ff) = 0;
+ virtual void OnEvent(uint32 ff, int err) = 0;
+ virtual int GetDescriptor() = 0;
+};
+
+class EventDispatcher : public Dispatcher {
+public:
+ EventDispatcher(PhysicalSocketServer* ss) : ss_(ss), fSignaled_(false) {
+ if (pipe(afd_) < 0)
+ LOG(LERROR) << "pipe failed";
+ ss_->Add(this);
+ }
+
+ virtual ~EventDispatcher() {
+ ss_->Remove(this);
+ close(afd_[0]);
+ close(afd_[1]);
+ }
+
+ virtual void Signal() {
+ CritScope cs(&crit_);
+ if (!fSignaled_) {
+ uint8 b = 0;
+ if (write(afd_[1], &b, sizeof(b)) < 0)
+ LOG(LERROR) << "write failed";
+ fSignaled_ = true;
+ }
+ }
+
+ virtual uint32 GetRequestedEvents() {
+ return kfRead;
+ }
+
+ virtual void OnPreEvent(uint32 ff) {
+ // It is not possible to perfectly emulate an auto-resetting event with
+ // pipes. This simulates it by resetting before the event is handled.
+
+ CritScope cs(&crit_);
+ if (fSignaled_) {
+ uint8 b;
+ read(afd_[0], &b, sizeof(b));
+ fSignaled_ = false;
+ }
+ }
+
+ virtual void OnEvent(uint32 ff, int err) {
+ assert(false);
+ }
+
+ virtual int GetDescriptor() {
+ return afd_[0];
+ }
+
+private:
+ PhysicalSocketServer *ss_;
+ int afd_[2];
+ bool fSignaled_;
+ CriticalSection crit_;
+};
+
+class SocketDispatcher : public Dispatcher, public PhysicalSocket {
+public:
+ SocketDispatcher(PhysicalSocketServer *ss) : PhysicalSocket(ss) {
+ ss_->Add(this);
+ }
+ SocketDispatcher(SOCKET s, PhysicalSocketServer *ss) : PhysicalSocket(ss, s) {
+ ss_->Add(this);
+ }
+
+ virtual ~SocketDispatcher() {
+ Close();
+ }
+
+ bool Initialize() {
+ ss_->Add(this);
+ fcntl(s_, F_SETFL, fcntl(s_, F_GETFL, 0) | O_NONBLOCK);
+ return true;
+ }
+
+ virtual bool Create(int type) {
+ // Change the socket to be non-blocking.
+ if (!PhysicalSocket::Create(type))
+ return false;
+
+ return Initialize();
+ }
+
+ virtual int GetDescriptor() {
+ return s_;
+ }
+
+ virtual uint32 GetRequestedEvents() {
+ return enabled_events_;
+ }
+
+ virtual void OnPreEvent(uint32 ff) {
+ if ((ff & kfConnect) != 0)
+ state_ = CS_CONNECTED;
+ }
+
+ virtual void OnEvent(uint32 ff, int err) {
+ if ((ff & kfRead) != 0) {
+ enabled_events_ &= ~kfRead;
+ SignalReadEvent(this);
+ }
+ if ((ff & kfWrite) != 0) {
+ enabled_events_ &= ~kfWrite;
+ SignalWriteEvent(this);
+ }
+ if ((ff & kfConnect) != 0) {
+ enabled_events_ &= ~kfConnect;
+ SignalConnectEvent(this);
+ }
+ if ((ff & kfClose) != 0)
+ SignalCloseEvent(this, err);
+ }
+
+ virtual int Close() {
+ if (s_ == INVALID_SOCKET)
+ return 0;
+
+ ss_->Remove(this);
+ return PhysicalSocket::Close();
+ }
+
+};
+
+class FileDispatcher: public Dispatcher, public AsyncFile {
+public:
+ FileDispatcher(int fd, PhysicalSocketServer *ss) : ss_(ss), fd_(fd) {
+ set_readable(true);
+
+ ss_->Add(this);
+
+ fcntl(fd_, F_SETFL, fcntl(fd_, F_GETFL, 0) | O_NONBLOCK);
+ }
+
+ virtual ~FileDispatcher() {
+ ss_->Remove(this);
+ }
+
+ SocketServer* socketserver() { return ss_; }
+
+ virtual int GetDescriptor() {
+ return fd_;
+ }
+
+ virtual uint32 GetRequestedEvents() {
+ return flags_;
+ }
+
+ virtual void OnPreEvent(uint32 ff) {
+ }
+
+ virtual void OnEvent(uint32 ff, int err) {
+ if ((ff & kfRead) != 0)
+ SignalReadEvent(this);
+ if ((ff & kfWrite) != 0)
+ SignalWriteEvent(this);
+ if ((ff & kfClose) != 0)
+ SignalCloseEvent(this, err);
+ }
+
+ virtual bool readable() {
+ return (flags_ & kfRead) != 0;
+ }
+
+ virtual void set_readable(bool value) {
+ flags_ = value ? (flags_ | kfRead) : (flags_ & ~kfRead);
+ }
+
+ virtual bool writable() {
+ return (flags_ & kfWrite) != 0;
+ }
+
+ virtual void set_writable(bool value) {
+ flags_ = value ? (flags_ | kfWrite) : (flags_ & ~kfWrite);
+ }
+
+private:
+ PhysicalSocketServer* ss_;
+ int fd_;
+ int flags_;
+};
+
+AsyncFile* PhysicalSocketServer::CreateFile(int fd) {
+ return new FileDispatcher(fd, this);
+}
+
+#endif // POSIX
+
+#ifdef WIN32
+class Dispatcher {
+public:
+ virtual uint32 GetRequestedEvents() = 0;
+ virtual void OnPreEvent(uint32 ff) = 0;
+ virtual void OnEvent(uint32 ff, int err) = 0;
+ virtual WSAEVENT GetWSAEvent() = 0;
+ virtual SOCKET GetSocket() = 0;
+ virtual bool CheckSignalClose() = 0;
+};
+
+uint32 FlagsToEvents(uint32 events) {
+ uint32 ffFD = FD_CLOSE | FD_ACCEPT;
+ if (events & kfRead)
+ ffFD |= FD_READ;
+ if (events & kfWrite)
+ ffFD |= FD_WRITE;
+ if (events & kfConnect)
+ ffFD |= FD_CONNECT;
+ return ffFD;
+}
+
+class EventDispatcher : public Dispatcher {
+public:
+ EventDispatcher(PhysicalSocketServer *ss) : ss_(ss) {
+ if (hev_ = WSACreateEvent()) {
+ ss_->Add(this);
+ }
+ }
+
+ ~EventDispatcher() {
+ if (hev_ != NULL) {
+ ss_->Remove(this);
+ WSACloseEvent(hev_);
+ hev_ = NULL;
+ }
+ }
+
+ virtual void Signal() {
+ if (hev_ != NULL)
+ WSASetEvent(hev_);
+ }
+
+ virtual uint32 GetRequestedEvents() {
+ return 0;
+ }
+
+ virtual void OnPreEvent(uint32 ff) {
+ WSAResetEvent(hev_);
+ }
+
+ virtual void OnEvent(uint32 ff, int err) {
+ }
+
+ virtual WSAEVENT GetWSAEvent() {
+ return hev_;
+ }
+
+ virtual SOCKET GetSocket() {
+ return INVALID_SOCKET;
+ }
+
+ virtual bool CheckSignalClose() { return false; }
+
+private:
+ PhysicalSocketServer* ss_;
+ WSAEVENT hev_;
+};
+
+class SocketDispatcher : public Dispatcher, public PhysicalSocket {
+public:
+ static int next_id_;
+ int id_;
+ bool signal_close_;
+ int signal_err_;
+
+ SocketDispatcher(PhysicalSocketServer* ss) : PhysicalSocket(ss), id_(0), signal_close_(false) {
+ }
+ SocketDispatcher(SOCKET s, PhysicalSocketServer* ss) : PhysicalSocket(ss, s), id_(0), signal_close_(false) {
+ }
+
+ virtual ~SocketDispatcher() {
+ Close();
+ }
+
+ bool Initialize() {
+ assert(s_ != INVALID_SOCKET);
+ // Must be a non-blocking
+ u_long argp = 1;
+ ioctlsocket(s_, FIONBIO, &argp);
+ ss_->Add(this);
+ return true;
+ }
+
+ virtual bool Create(int type) {
+ // Create socket
+ if (!PhysicalSocket::Create(type))
+ return false;
+
+ if (!Initialize())
+ return false;
+
+ do { id_ = ++next_id_; } while (id_ == 0);
+ return true;
+ }
+
+ virtual int Close() {
+ if (s_ == INVALID_SOCKET)
+ return 0;
+
+ id_ = 0;
+ signal_close_ = false;
+ ss_->Remove(this);
+ return PhysicalSocket::Close();
+ }
+
+ virtual uint32 GetRequestedEvents() {
+ return enabled_events_;
+ }
+
+ virtual void OnPreEvent(uint32 ff) {
+ if ((ff & kfConnect) != 0)
+ state_ = CS_CONNECTED;
+ }
+
+ virtual void OnEvent(uint32 ff, int err) {
+ int cache_id = id_;
+ if ((ff & kfRead) != 0) {
+ enabled_events_ &= ~kfRead;
+ SignalReadEvent(this);
+ }
+ if (((ff & kfWrite) != 0) && (id_ == cache_id)) {
+ enabled_events_ &= ~kfWrite;
+ SignalWriteEvent(this);
+ }
+ if (((ff & kfConnect) != 0) && (id_ == cache_id)) {
+ if (ff != kfConnect)
+ LOG(LS_VERBOSE) << "Signalled with kfConnect: " << ff;
+ enabled_events_ &= ~kfConnect;
+ SignalConnectEvent(this);
+ }
+ if (((ff & kfClose) != 0) && (id_ == cache_id)) {
+ //LOG(INFO) << "SOCK[" << static_cast<int>(s_) << "] OnClose() Error: " << err;
+ signal_close_ = true;
+ signal_err_ = err;
+ }
+ }
+
+ virtual WSAEVENT GetWSAEvent() {
+ return WSA_INVALID_EVENT;
+ }
+
+ virtual SOCKET GetSocket() {
+ return s_;
+ }
+
+ virtual bool CheckSignalClose() {
+ if (!signal_close_)
+ return false;
+
+ char ch;
+ if (recv(s_, &ch, 1, MSG_PEEK) > 0)
+ return false;
+
+ signal_close_ = false;
+ SignalCloseEvent(this, signal_err_);
+ return true;
+ }
+};
+
+int SocketDispatcher::next_id_ = 0;
+
+#endif // WIN32
+
+// Sets the value of a boolean value to false when signaled.
+class Signaler : public EventDispatcher {
+public:
+ Signaler(PhysicalSocketServer* ss, bool* pf)
+ : EventDispatcher(ss), pf_(pf) {
+ }
+ virtual ~Signaler() { }
+
+ void OnEvent(uint32 ff, int err) {
+ if (pf_)
+ *pf_ = false;
+ }
+
+private:
+ bool *pf_;
+};
+
+PhysicalSocketServer::PhysicalSocketServer() : fWait_(false),
+ last_tick_tracked_(0), last_tick_dispatch_count_(0) {
+ signal_wakeup_ = new Signaler(this, &fWait_);
+}
+
+PhysicalSocketServer::~PhysicalSocketServer() {
+ delete signal_wakeup_;
+ // ASSERT(dispatchers_.empty());
+}
+
+void PhysicalSocketServer::WakeUp() {
+ signal_wakeup_->Signal();
+}
+
+Socket* PhysicalSocketServer::CreateSocket(int type) {
+ PhysicalSocket* socket = new PhysicalSocket(this);
+ if (socket->Create(type)) {
+ return socket;
+ } else {
+ delete socket;
+ return 0;
+ }
+}
+
+AsyncSocket* PhysicalSocketServer::CreateAsyncSocket(int type) {
+ SocketDispatcher* dispatcher = new SocketDispatcher(this);
+ if (dispatcher->Create(type)) {
+ return dispatcher;
+ } else {
+ delete dispatcher;
+ return 0;
+ }
+}
+
+AsyncSocket* PhysicalSocketServer::WrapSocket(SOCKET s) {
+ SocketDispatcher* dispatcher = new SocketDispatcher(s, this);
+ if (dispatcher->Initialize()) {
+ return dispatcher;
+ } else {
+ delete dispatcher;
+ return 0;
+ }
+}
+
+void PhysicalSocketServer::Add(Dispatcher *pdispatcher) {
+ CritScope cs(&crit_);
+ dispatchers_.push_back(pdispatcher);
+}
+
+void PhysicalSocketServer::Remove(Dispatcher *pdispatcher) {
+ CritScope cs(&crit_);
+ dispatchers_.erase(std::remove(dispatchers_.begin(), dispatchers_.end(), pdispatcher), dispatchers_.end());
+}
+
+#ifdef POSIX
+bool PhysicalSocketServer::Wait(int cmsWait, bool process_io) {
+ // Calculate timing information
+
+ struct timeval *ptvWait = NULL;
+ struct timeval tvWait;
+ struct timeval tvStop;
+ if (cmsWait != kForever) {
+ // Calculate wait timeval
+ tvWait.tv_sec = cmsWait / 1000;
+ tvWait.tv_usec = (cmsWait % 1000) * 1000;
+ ptvWait = &tvWait;
+
+ // Calculate when to return in a timeval
+ gettimeofday(&tvStop, NULL);
+ tvStop.tv_sec += tvWait.tv_sec;
+ tvStop.tv_usec += tvWait.tv_usec;
+ if (tvStop.tv_usec >= 1000000) {
+ tvStop.tv_usec -= 1000000;
+ tvStop.tv_sec += 1;
+ }
+ }
+
+ // Zero all fd_sets. Don't need to do this inside the loop since
+ // select() zeros the descriptors not signaled
+
+ fd_set fdsRead;
+ FD_ZERO(&fdsRead);
+ fd_set fdsWrite;
+ FD_ZERO(&fdsWrite);
+
+ fWait_ = true;
+
+ while (fWait_) {
+ int fdmax = -1;
+ {
+ CritScope cr(&crit_);
+ for (unsigned i = 0; i < dispatchers_.size(); i++) {
+ // Query dispatchers for read and write wait state
+
+ Dispatcher *pdispatcher = dispatchers_[i];
+ assert(pdispatcher);
+ if (!process_io && (pdispatcher != signal_wakeup_))
+ continue;
+ int fd = pdispatcher->GetDescriptor();
+ if (fd > fdmax)
+ fdmax = fd;
+ uint32 ff = pdispatcher->GetRequestedEvents();
+ if (ff & kfRead)
+ FD_SET(fd, &fdsRead);
+ if (ff & (kfWrite | kfConnect))
+ FD_SET(fd, &fdsWrite);
+ }
+ }
+
+ // Wait then call handlers as appropriate
+ // < 0 means error
+ // 0 means timeout
+ // > 0 means count of descriptors ready
+ int n = select(fdmax + 1, &fdsRead, &fdsWrite, NULL, ptvWait);
+ // If error, return error
+ // todo: do something intelligent
+ if (n < 0)
+ return false;
+
+ // If timeout, return success
+
+ if (n == 0)
+ return true;
+
+ // We have signaled descriptors
+
+ {
+ CritScope cr(&crit_);
+ for (unsigned i = 0; i < dispatchers_.size(); i++) {
+ Dispatcher *pdispatcher = dispatchers_[i];
+ int fd = pdispatcher->GetDescriptor();
+ uint32 ff = 0;
+ if (FD_ISSET(fd, &fdsRead)) {
+ FD_CLR(fd, &fdsRead);
+ ff |= kfRead;
+ }
+ if (FD_ISSET(fd, &fdsWrite)) {
+ FD_CLR(fd, &fdsWrite);
+ if (pdispatcher->GetRequestedEvents() & kfConnect) {
+ ff |= kfConnect;
+ } else {
+ ff |= kfWrite;
+ }
+ }
+ if (ff != 0) {
+ pdispatcher->OnPreEvent(ff);
+ pdispatcher->OnEvent(ff, 0);
+ }
+ }
+ }
+
+ // Recalc the time remaining to wait. Doing it here means it doesn't get
+ // calced twice the first time through the loop
+
+ if (cmsWait != kForever) {
+ ptvWait->tv_sec = 0;
+ ptvWait->tv_usec = 0;
+ struct timeval tvT;
+ gettimeofday(&tvT, NULL);
+ if (tvStop.tv_sec >= tvT.tv_sec) {
+ ptvWait->tv_sec = tvStop.tv_sec - tvT.tv_sec;
+ ptvWait->tv_usec = tvStop.tv_usec - tvT.tv_usec;
+ if (ptvWait->tv_usec < 0) {
+ ptvWait->tv_usec += 1000000;
+ ptvWait->tv_sec -= 1;
+ }
+ }
+ }
+ }
+
+ return true;
+}
+#endif // POSIX
+
+#ifdef WIN32
+bool PhysicalSocketServer::Wait(int cmsWait, bool process_io)
+{
+ int cmsTotal = cmsWait;
+ int cmsElapsed = 0;
+ uint32 msStart = GetMillisecondCount();
+
+#if LOGGING
+ if (last_tick_dispatch_count_ == 0) {
+ last_tick_tracked_ = msStart;
+ }
+#endif
+
+ WSAEVENT socket_ev = WSACreateEvent();
+
+ fWait_ = true;
+ while (fWait_) {
+ std::vector<WSAEVENT> events;
+ std::vector<Dispatcher *> event_owners;
+
+ events.push_back(socket_ev);
+
+ {
+ CritScope cr(&crit_);
+ for (size_t i = 0; i < dispatchers_.size(); ++i) {
+ Dispatcher * disp = dispatchers_[i];
+ if (!process_io && (disp != signal_wakeup_))
+ continue;
+ SOCKET s = disp->GetSocket();
+ if (disp->CheckSignalClose()) {
+ // We just signalled close, don't poll this socket
+ } else if (s != INVALID_SOCKET) {
+ WSAEventSelect(s, events[0], FlagsToEvents(disp->GetRequestedEvents()));
+ } else {
+ events.push_back(disp->GetWSAEvent());
+ event_owners.push_back(disp);
+ }
+ }
+ }
+
+ // Which is shorter, the delay wait or the asked wait?
+
+ int cmsNext;
+ if (cmsWait == kForever) {
+ cmsNext = cmsWait;
+ } else {
+ cmsNext = cmsTotal - cmsElapsed;
+ if (cmsNext < 0)
+ cmsNext = 0;
+ }
+
+ // Wait for one of the events to signal
+ DWORD dw = WSAWaitForMultipleEvents(static_cast<DWORD>(events.size()), &events[0], false, cmsNext, false);
+
+#if 0 // LOGGING
+ // we track this information purely for logging purposes.
+ last_tick_dispatch_count_++;
+ if (last_tick_dispatch_count_ >= 1000) {
+ uint32 now = GetMillisecondCount();
+ LOG(INFO) << "PhysicalSocketServer took " << TimeDiff(now, last_tick_tracked_) << "ms for 1000 events";
+
+ // If we get more than 1000 events in a second, we are spinning badly
+ // (normally it should take about 8-20 seconds).
+ assert(TimeDiff(now, last_tick_tracked_) > 1000);
+
+ last_tick_tracked_ = now;
+ last_tick_dispatch_count_ = 0;
+ }
+#endif
+
+ // Failed?
+ // todo: need a better strategy than this!
+
+ if (dw == WSA_WAIT_FAILED) {
+ int error = WSAGetLastError();
+ assert(false);
+ WSACloseEvent(socket_ev);
+ return false;
+ }
+
+ // Timeout?
+
+ if (dw == WSA_WAIT_TIMEOUT) {
+ WSACloseEvent(socket_ev);
+ return true;
+ }
+
+ // Figure out which one it is and call it
+
+ {
+ CritScope cr(&crit_);
+ int index = dw - WSA_WAIT_EVENT_0;
+ if (index > 0) {
+ --index; // The first event is the socket event
+ event_owners[index]->OnPreEvent(0);
+ event_owners[index]->OnEvent(0, 0);
+ } else if (process_io) {
+ for (size_t i = 0; i < dispatchers_.size(); ++i) {
+ Dispatcher * disp = dispatchers_[i];
+ SOCKET s = disp->GetSocket();
+ if (s == INVALID_SOCKET)
+ continue;
+
+ WSANETWORKEVENTS wsaEvents;
+ int err = WSAEnumNetworkEvents(s, events[0], &wsaEvents);
+ if (err == 0) {
+
+#if LOGGING
+ {
+ if ((wsaEvents.lNetworkEvents & FD_READ) && wsaEvents.iErrorCode[FD_READ_BIT] != 0) {
+ LOG(WARNING) << "PhysicalSocketServer got FD_READ_BIT error " << wsaEvents.iErrorCode[FD_READ_BIT];
+ }
+ if ((wsaEvents.lNetworkEvents & FD_WRITE) && wsaEvents.iErrorCode[FD_WRITE_BIT] != 0) {
+ LOG(WARNING) << "PhysicalSocketServer got FD_WRITE_BIT error " << wsaEvents.iErrorCode[FD_WRITE_BIT];
+ }
+ if ((wsaEvents.lNetworkEvents & FD_CONNECT) && wsaEvents.iErrorCode[FD_CONNECT_BIT] != 0) {
+ LOG(WARNING) << "PhysicalSocketServer got FD_CONNECT_BIT error " << wsaEvents.iErrorCode[FD_CONNECT_BIT];
+ }
+ if ((wsaEvents.lNetworkEvents & FD_ACCEPT) && wsaEvents.iErrorCode[FD_ACCEPT_BIT] != 0) {
+ LOG(WARNING) << "PhysicalSocketServer got FD_ACCEPT_BIT error " << wsaEvents.iErrorCode[FD_ACCEPT_BIT];
+ }
+ if ((wsaEvents.lNetworkEvents & FD_CLOSE) && wsaEvents.iErrorCode[FD_CLOSE_BIT] != 0) {
+ LOG(WARNING) << "PhysicalSocketServer got FD_CLOSE_BIT error " << wsaEvents.iErrorCode[FD_CLOSE_BIT];
+ }
+ }
+#endif
+ uint32 ff = 0;
+ int errcode = 0;
+ if (wsaEvents.lNetworkEvents & FD_READ)
+ ff |= kfRead;
+ if (wsaEvents.lNetworkEvents & FD_WRITE)
+ ff |= kfWrite;
+ if (wsaEvents.lNetworkEvents & FD_CONNECT) {
+ if (wsaEvents.iErrorCode[FD_CONNECT_BIT] == 0) {
+ ff |= kfConnect;
+ } else {
+ // TODO: Decide whether we want to signal connect, but with an error code
+ ff |= kfClose;
+ errcode = wsaEvents.iErrorCode[FD_CONNECT_BIT];
+ }
+ }
+ if (wsaEvents.lNetworkEvents & FD_ACCEPT)
+ ff |= kfRead;
+ if (wsaEvents.lNetworkEvents & FD_CLOSE) {
+ ff |= kfClose;
+ errcode = wsaEvents.iErrorCode[FD_CLOSE_BIT];
+ }
+ if (ff != 0) {
+ disp->OnPreEvent(ff);
+ disp->OnEvent(ff, errcode);
+ }
+ }
+ }
+ }
+
+ // Reset the network event until new activity occurs
+ WSAResetEvent(socket_ev);
+ }
+
+ // Break?
+
+ if (!fWait_)
+ break;
+ cmsElapsed = GetMillisecondCount() - msStart;
+ if ((cmsWait != kForever) && (cmsElapsed >= cmsWait)) {
+ break;
+ }
+ }
+
+ // Done
+
+ WSACloseEvent(socket_ev);
+ return true;
+}
+#endif // WIN32
+
+} // namespace talk_base
diff --git a/Plugins/jingle/libjingle/talk/base/physicalsocketserver.h b/Plugins/jingle/libjingle/talk/base/physicalsocketserver.h
new file mode 100644
index 0000000..cc2e707
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/physicalsocketserver.h
@@ -0,0 +1,81 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TALK_BASE_PHYSICALSOCKETSERVER_H__
+#define TALK_BASE_PHYSICALSOCKETSERVER_H__
+
+#include <vector>
+
+#include "talk/base/asyncfile.h"
+#include "talk/base/socketserver.h"
+#include "talk/base/criticalsection.h"
+
+#ifdef POSIX
+typedef int SOCKET;
+#endif // POSIX
+
+namespace talk_base {
+
+class Dispatcher;
+class Signaler;
+
+// A socket server that provides the real sockets of the underlying OS.
+class PhysicalSocketServer : public SocketServer {
+public:
+ PhysicalSocketServer();
+ virtual ~PhysicalSocketServer();
+
+ // SocketFactory:
+ virtual Socket* CreateSocket(int type);
+ virtual AsyncSocket* CreateAsyncSocket(int type);
+
+ // Internal Factory for Accept
+ AsyncSocket* WrapSocket(SOCKET s);
+
+ // SocketServer:
+ virtual bool Wait(int cms, bool process_io);
+ virtual void WakeUp();
+
+ void Add(Dispatcher* dispatcher);
+ void Remove(Dispatcher* dispatcher);
+
+#ifdef POSIX
+ AsyncFile* CreateFile(int fd);
+#endif
+
+private:
+ std::vector<Dispatcher*> dispatchers_;
+ Signaler* signal_wakeup_;
+ CriticalSection crit_;
+ bool fWait_;
+ uint32 last_tick_tracked_;
+ int last_tick_dispatch_count_;
+};
+
+} // namespace talk_base
+
+#endif // TALK_BASE_PHYSICALSOCKETSERVER_H__
diff --git a/Plugins/jingle/libjingle/talk/base/proxydetect.cc b/Plugins/jingle/libjingle/talk/base/proxydetect.cc
new file mode 100644
index 0000000..04a9c61
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/proxydetect.cc
@@ -0,0 +1,827 @@
+// TODO: Abstract this better for cross-platformability
+
+#ifdef _WINDOWS
+#include "talk/base/win32.h"
+#include <shlobj.h>
+#endif
+
+#include "talk/base/httpcommon.h"
+#include "talk/base/httpcommon-inl.h"
+#include "talk/base/proxydetect.h"
+#include "talk/base/stringutils.h"
+#include "talk/base/basicdefs.h"
+
+#if _WINDOWS
+#define _TRY_FIREFOX 1
+#define _TRY_WINHTTP 1
+#define _TRY_JSPROXY 0
+#define _TRY_WM_FINDPROXY 0
+#define _TRY_IE_LAN_SETTINGS 1
+#endif // _WINDOWS
+
+#if _TRY_WINHTTP
+//#include <winhttp.h>
+// Note: From winhttp.h
+
+const char WINHTTP[] = "winhttp";
+typedef LPVOID HINTERNET;
+
+typedef struct {
+ DWORD dwAccessType; // see WINHTTP_ACCESS_* types below
+ LPWSTR lpszProxy; // proxy server list
+ LPWSTR lpszProxyBypass; // proxy bypass list
+} WINHTTP_PROXY_INFO, * LPWINHTTP_PROXY_INFO;
+
+typedef struct {
+ DWORD dwFlags;
+ DWORD dwAutoDetectFlags;
+ LPCWSTR lpszAutoConfigUrl;
+ LPVOID lpvReserved;
+ DWORD dwReserved;
+ BOOL fAutoLogonIfChallenged;
+} WINHTTP_AUTOPROXY_OPTIONS;
+
+typedef struct {
+ BOOL fAutoDetect;
+ LPWSTR lpszAutoConfigUrl;
+ LPWSTR lpszProxy;
+ LPWSTR lpszProxyBypass;
+} WINHTTP_CURRENT_USER_IE_PROXY_CONFIG;
+
+extern "C" {
+typedef HINTERNET (WINAPI * pfnWinHttpOpen)
+(
+ IN LPCWSTR pwszUserAgent,
+ IN DWORD dwAccessType,
+ IN LPCWSTR pwszProxyName OPTIONAL,
+ IN LPCWSTR pwszProxyBypass OPTIONAL,
+ IN DWORD dwFlags
+);
+typedef BOOL (STDAPICALLTYPE * pfnWinHttpCloseHandle)
+(
+ IN HINTERNET hInternet
+);
+typedef BOOL (STDAPICALLTYPE * pfnWinHttpGetProxyForUrl)
+(
+ IN HINTERNET hSession,
+ IN LPCWSTR lpcwszUrl,
+ IN WINHTTP_AUTOPROXY_OPTIONS * pAutoProxyOptions,
+ OUT WINHTTP_PROXY_INFO * pProxyInfo
+);
+typedef BOOL (STDAPICALLTYPE * pfnWinHttpGetIEProxyConfig)
+(
+ IN OUT WINHTTP_CURRENT_USER_IE_PROXY_CONFIG * pProxyConfig
+);
+
+} // extern "C"
+
+#define WINHTTP_AUTOPROXY_AUTO_DETECT 0x00000001
+#define WINHTTP_AUTOPROXY_CONFIG_URL 0x00000002
+#define WINHTTP_AUTOPROXY_RUN_INPROCESS 0x00010000
+#define WINHTTP_AUTOPROXY_RUN_OUTPROCESS_ONLY 0x00020000
+#define WINHTTP_AUTO_DETECT_TYPE_DHCP 0x00000001
+#define WINHTTP_AUTO_DETECT_TYPE_DNS_A 0x00000002
+#define WINHTTP_ACCESS_TYPE_DEFAULT_PROXY 0
+#define WINHTTP_ACCESS_TYPE_NO_PROXY 1
+#define WINHTTP_ACCESS_TYPE_NAMED_PROXY 3
+#define WINHTTP_NO_PROXY_NAME NULL
+#define WINHTTP_NO_PROXY_BYPASS NULL
+
+#endif // _TRY_WINHTTP
+
+#if _TRY_JSPROXY
+extern "C" {
+typedef BOOL (STDAPICALLTYPE * pfnInternetGetProxyInfo)
+(
+ LPCSTR lpszUrl,
+ DWORD dwUrlLength,
+ LPSTR lpszUrlHostName,
+ DWORD dwUrlHostNameLength,
+ LPSTR * lplpszProxyHostName,
+ LPDWORD lpdwProxyHostNameLength
+ );
+} // extern "C"
+#endif // _TRY_JSPROXY
+
+#if _TRY_WM_FINDPROXY
+#include <comutil.h>
+#include <wmnetsourcecreator.h>
+#include <wmsinternaladminnetsource.h>
+#endif // _TRY_WM_FINDPROXY
+
+#if _TRY_IE_LAN_SETTINGS
+#include <wininet.h>
+#include <string>
+#endif // _TRY_IE_LAN_SETTINGS
+
+using namespace talk_base;
+
+//////////////////////////////////////////////////////////////////////
+// Utility Functions
+//////////////////////////////////////////////////////////////////////
+
+#ifdef _WINDOWS
+#ifdef _UNICODE
+
+typedef std::wstring tstring;
+std::string Utf8String(const tstring& str) { return ToUtf8(str); }
+
+#else // !_UNICODE
+
+typedef std::string tstring;
+std::string Utf8String(const tstring& str) { return str; }
+
+#endif // !_UNICODE
+#endif // _WINDOWS
+
+//////////////////////////////////////////////////////////////////////
+// GetProxySettingsForUrl
+//////////////////////////////////////////////////////////////////////
+
+bool WildMatch(const char * target, const char * pattern) {
+ while (*pattern) {
+ if (*pattern == '*') {
+ if (!*++pattern) {
+ return true;
+ }
+ while (*target) {
+ if ((toupper(*pattern) == toupper(*target)) && WildMatch(target + 1, pattern + 1)) {
+ return true;
+ }
+ ++target;
+ }
+ return false;
+ } else {
+ if (toupper(*pattern) != toupper(*target)) {
+ return false;
+ }
+ ++target;
+ ++pattern;
+ }
+ }
+ return !*target;
+}
+
+bool ProxyItemMatch(const Url<char>& url, char * item, size_t len) {
+ // hostname:443
+ if (char * port = strchr(item, ':')) {
+ *port++ = '\0';
+ if (url.port() != atol(port)) {
+ return false;
+ }
+ }
+
+ // A.B.C.D or A.B.C.D/24
+ int a, b, c, d, m;
+ int match = sscanf(item, "%d.%d.%d.%d/%d", &a, &b, &c, &d, &m);
+ if (match >= 4) {
+ uint32 ip = ((a & 0xFF) << 24) | ((b & 0xFF) << 16) | ((c & 0xFF) << 8) | (d & 0xFF);
+ if ((match < 5) || (m > 32))
+ m = 32;
+ else if (m < 0)
+ m = 0;
+ uint32 mask = (m == 0) ? 0 : (~0UL) << (32 - m);
+ SocketAddress addr(url.server());
+ return !addr.IsUnresolved() && ((addr.ip() & mask) == (ip & mask));
+ }
+
+ // .foo.com
+ if (*item == '.') {
+ size_t hostlen = url.server().length();
+ return (hostlen > len)
+ && (stricmp(url.server().c_str() + (hostlen - len), item) == 0);
+ }
+
+ // localhost or www.*.com
+ if (!WildMatch(url.server().c_str(), item))
+ return false;
+
+ return true;
+}
+
+bool ProxyListMatch(const Url<char>& url, const std::string& slist, char sep) {
+ const size_t BUFSIZE = 256;
+ char buffer[BUFSIZE];
+ const char* list = slist.c_str();
+ while (*list) {
+ // Remove leading space
+ if (isspace(*list)) {
+ ++list;
+ continue;
+ }
+ // Break on separator
+ size_t len;
+ const char * start = list;
+ if (const char * end = strchr(list, sep)) {
+ len = (end - list);
+ list += len + 1;
+ } else {
+ len = strlen(list);
+ list += len;
+ }
+ // Remove trailing space
+ while ((len > 0) && isspace(start[len-1]))
+ --len;
+ // Check for oversized entry
+ if (len >= BUFSIZE)
+ continue;
+ memcpy(buffer, start, len);
+ buffer[len] = 0;
+ if (!ProxyItemMatch(url, buffer, len))
+ continue;
+ return true;
+ }
+ return false;
+}
+
+bool Better(ProxyType lhs, const ProxyType rhs) {
+ // PROXY_NONE, PROXY_HTTPS, PROXY_SOCKS5, PROXY_UNKNOWN
+ const int PROXY_VALUE[4] = { 0, 2, 3, 1 };
+ return (PROXY_VALUE[lhs] > PROXY_VALUE[rhs]);
+}
+
+bool ParseProxy(const std::string& saddress, ProxyInfo& proxy) {
+ const size_t kMaxAddressLength = 1024;
+ // Allow semicolon, space, or tab as an address separator
+ const char* const kAddressSeparator = " ;\t";
+
+ ProxyType ptype;
+ std::string host;
+ uint16 port;
+
+ const char* address = saddress.c_str();
+ while (*address) {
+ size_t len;
+ const char * start = address;
+ if (const char * sep = strchr(address, kAddressSeparator)) {
+ len = (sep - address);
+ address += len + 1;
+ while (strchr(kAddressSeparator, *address)) {
+ address += 1;
+ }
+ } else {
+ len = strlen(address);
+ address += len;
+ }
+
+ if (len > kMaxAddressLength - 1) {
+ LOG(LS_WARNING) << "Proxy address too long [" << start << "]";
+ continue;
+ }
+
+ char buffer[kMaxAddressLength];
+ memcpy(buffer, start, len);
+ buffer[len] = 0;
+
+ char * colon = strchr(buffer, ':');
+ if (!colon) {
+ LOG(LS_WARNING) << "Proxy address without port [" << buffer << "]";
+ continue;
+ }
+
+ *colon = 0;
+ char * endptr;
+ port = static_cast<uint16>(strtol(colon + 1, &endptr, 0));
+ if (*endptr != 0) {
+ LOG(LS_WARNING) << "Proxy address with invalid port [" << buffer << "]";
+ continue;
+ }
+
+ if (char * equals = strchr(buffer, '=')) {
+ *equals = 0;
+ host = equals + 1;
+ if (_stricmp(buffer, "socks") == 0) {
+ ptype = PROXY_SOCKS5;
+ } else if (_stricmp(buffer, "https") == 0) {
+ ptype = PROXY_HTTPS;
+ } else {
+ LOG(LS_WARNING) << "Proxy address with unknown protocol ["
+ << buffer << "]";
+ ptype = PROXY_UNKNOWN;
+ }
+ } else {
+ host = buffer;
+ ptype = PROXY_UNKNOWN;
+ }
+
+ if (Better(ptype, proxy.type)) {
+ proxy.type = ptype;
+ proxy.address.SetIP(host);
+ proxy.address.SetPort((int)port);
+ }
+ }
+
+ return (proxy.type != PROXY_NONE);
+}
+
+#if _WINDOWS
+bool IsDefaultBrowserFirefox() {
+ HKEY key;
+ LONG result = RegOpenKeyEx(HKEY_CLASSES_ROOT, L"http\\shell\\open\\command",
+ 0, KEY_READ, &key);
+ if (ERROR_SUCCESS != result)
+ return false;
+
+ wchar_t* value = NULL;
+ DWORD size, type;
+ result = RegQueryValueEx(key, L"", 0, &type, NULL, &size);
+ if (REG_SZ != type) {
+ result = ERROR_ACCESS_DENIED; // Any error is fine
+ } else if (ERROR_SUCCESS == result) {
+ value = new wchar_t[size+1];
+ BYTE* buffer = reinterpret_cast<BYTE*>(value);
+ result = RegQueryValueEx(key, L"", 0, &type, buffer, &size);
+ }
+ RegCloseKey(key);
+
+ bool success = false;
+ if (ERROR_SUCCESS == result) {
+ value[size] = L'\0';
+ for (size_t i=0; i<size; ++i) {
+ value[i] = tolowercase(value[i]);
+ }
+ success = (NULL != strstr(value, L"firefox.exe"));
+ }
+ delete [] value;
+ return success;
+}
+#endif
+
+#if _TRY_FIREFOX
+
+#define USE_FIREFOX_PROFILES_INI 1
+
+bool GetDefaultFirefoxProfile(std::wstring* profile) {
+ ASSERT(NULL != profile);
+
+ wchar_t path[MAX_PATH];
+ if (SHGetFolderPath(0, CSIDL_APPDATA, 0, SHGFP_TYPE_CURRENT, path) != S_OK)
+ return false;
+
+ std::wstring profile_root(path);
+ profile_root.append(L"\\Mozilla\\Firefox\\");
+
+#if USE_FIREFOX_PROFILES_INI
+ std::wstring tmp(profile_root);
+ tmp.append(L"profiles.ini");
+
+ FILE * fp = _wfopen(tmp.c_str(), L"rb");
+ if (!fp)
+ return false;
+
+ // [Profile0]
+ // Name=default
+ // IsRelative=1
+ // Path=Profiles/2de53ejb.default
+ // Default=1
+
+ // Note: we are looking for the first entry with "Default=1", or the last entry in the file
+
+ std::wstring candidate;
+ bool relative = true;
+
+ char buffer[1024];
+ while (fgets(buffer, sizeof(buffer), fp)) {
+ size_t len = strlen(buffer);
+ while ((len > 0) && isspace(buffer[len-1]))
+ buffer[--len] = 0;
+ if (buffer[0] == '[') {
+ relative = true;
+ candidate.clear();
+ } else if (strnicmp(buffer, "IsRelative=", 11) == 0) {
+ relative = (buffer[11] != '0');
+ } else if (strnicmp(buffer, "Path=", 5) == 0) {
+ if (relative) {
+ candidate = profile_root;
+ } else {
+ candidate.clear();
+ }
+ candidate.append(ToUtf16(buffer + 5));
+ candidate.append(L"\\");
+ } else if (strnicmp(buffer, "Default=", 8) == 0) {
+ if ((buffer[8] != '0') && !candidate.empty()) {
+ break;
+ }
+ }
+ }
+ fclose(fp);
+ if (candidate.empty())
+ return false;
+ *profile = candidate;
+
+#else // !USE_FIREFOX_PROFILES_INI
+ std::wstring tmp(profile_root);
+ tmp.append(L"Profiles\\*.default");
+ WIN32_FIND_DATA fdata;
+ HANDLE hFind = FindFirstFile(tmp.c_str(), &fdata);
+ if (hFind == INVALID_HANDLE_VALUE)
+ return false;
+
+ profile->assign(profile_root);
+ profile->append(L"Profiles\\");
+ profile->append(fdata.cFileName);
+ profile->append(L"\\");
+ FindClose(hFind);
+#endif // !USE_FIREFOX_PROFILES_INI
+
+ return true;
+}
+
+struct StringMap {
+public:
+ void Add(const char * name, const char * value) { map_[name] = value; }
+ const std::string& Get(const char * name, const char * def = "") const {
+ std::map<std::string, std::string>::const_iterator it =
+ map_.find(name);
+ if (it != map_.end())
+ return it->second;
+ def_ = def;
+ return def_;
+ }
+ bool IsSet(const char * name) const {
+ return (map_.find(name) != map_.end());
+ }
+private:
+ std::map<std::string, std::string> map_;
+ mutable std::string def_;
+};
+
+bool ReadFirefoxPrefs(const std::wstring& filename,
+ const char * prefix,
+ StringMap& settings) {
+ FILE * fp = _wfopen(filename.c_str(), L"rb");
+ if (!fp)
+ return false;
+
+ size_t prefix_len = strlen(prefix);
+ bool overlong_line = false;
+
+ char buffer[1024];
+ while (fgets(buffer, sizeof(buffer), fp)) {
+ size_t len = strlen(buffer);
+ bool missing_newline = (len > 0) && (buffer[len-1] != '\n');
+
+ if (missing_newline) {
+ overlong_line = true;
+ continue;
+ } else if (overlong_line) {
+ LOG_F(LS_INFO) << "Skipping long line";
+ overlong_line = false;
+ continue;
+ }
+
+ while ((len > 0) && isspace(buffer[len-1]))
+ buffer[--len] = 0;
+
+ // Skip blank lines
+ if ((len == 0) || (buffer[0] == '#')
+ || (strncmp(buffer, "/*", 2) == 0)
+ || (strncmp(buffer, " *", 2) == 0))
+ continue;
+
+ int nstart = 0, nend = 0, vstart = 0, vend = 0;
+ sscanf(buffer, "user_pref(\"%n%*[^\"]%n\", %n%*[^)]%n);",
+ &nstart, &nend, &vstart, &vend);
+ if (vend > 0) {
+ char * name = buffer + nstart;
+ name[nend - nstart] = 0;
+ if ((vend - vstart >= 2) && (buffer[vstart] == '"')) {
+ vstart += 1;
+ vend -= 1;
+ }
+ char * value = buffer + vstart;
+ value[vend - vstart] = 0;
+ if ((strncmp(name, prefix, prefix_len) == 0) && *value) {
+ settings.Add(name + prefix_len, value);
+ }
+ } else {
+ LOG_F(LS_WARNING) << "Unparsed pref [" << buffer << "]";
+ }
+ }
+ fclose(fp);
+ return true;
+}
+#endif // _TRY_FIREFOX
+
+#ifdef WIN32
+BOOL MyWinHttpGetProxyForUrl(pfnWinHttpGetProxyForUrl pWHGPFU,
+ HINTERNET hWinHttp, LPCWSTR url, WINHTTP_AUTOPROXY_OPTIONS *options,
+ WINHTTP_PROXY_INFO *info) {
+ // WinHttpGetProxyForUrl() can call plugins which can crash.
+ // In the case of McAfee scriptproxy.dll, it does crash in
+ // older versions. Try to catch crashes here and treat as an
+ // error.
+ BOOL success = FALSE;
+
+#if (_HAS_EXCEPTIONS == 0)
+ __try {
+ success = pWHGPFU(hWinHttp, url, options, info);
+ } __except(EXCEPTION_EXECUTE_HANDLER) {
+ LOG_GLEM(LERROR,WINHTTP) << "WinHttpGetProxyForUrl faulted!!";
+ }
+#else
+ success = pWHGPFU(hWinHttp, url, options, info);
+#endif
+
+ return success;
+}
+#endif
+
+bool GetProxySettingsForUrl(const char* agent, const char* url,
+ ProxyInfo& proxy,
+ bool long_operation) {
+ bool success = false;
+ Url<char> purl(url);
+
+#if 0
+ assert( WildMatch(_T("A.B.C.D"), _T("a.b.c.d")));
+ assert( WildMatch(_T("127.0.0.1"), _T("12*.0.*1")));
+ assert(!WildMatch(_T("127.0.0.0"), _T("12*.0.*1")));
+ assert(!WildMatch(_T("127.0.0.0"), _T("12*.0.*1")));
+ assert( WildMatch(_T("127.1.0.21"), _T("12*.0.*1")));
+ assert(!WildMatch(_T("127.1.1.21"), _T("12*.0.*1")));
+ purl = PUrl(_T("http://a.b.c:500/"));
+ wchar_t item[256];
+ _tcscpy(item, _T("a.b.c"));
+ assert( ProxyItemMatch(purl, item, _tcslen(item)));
+ _tcscpy(item, _T("a.x.c"));
+ assert(!ProxyItemMatch(purl, item, _tcslen(item)));
+ _tcscpy(item, _T("a.b.*"));
+ assert( ProxyItemMatch(purl, item, _tcslen(item)));
+ _tcscpy(item, _T("a.x.*"));
+ assert(!ProxyItemMatch(purl, item, _tcslen(item)));
+ _tcscpy(item, _T(".b.c"));
+ assert( ProxyItemMatch(purl, item, _tcslen(item)));
+ _tcscpy(item, _T(".x.c"));
+ assert(!ProxyItemMatch(purl, item, _tcslen(item)));
+ _tcscpy(item, _T("a.b.c:500"));
+ assert( ProxyItemMatch(purl, item, _tcslen(item)));
+ _tcscpy(item, _T("a.b.c:501"));
+ assert(!ProxyItemMatch(purl, item, _tcslen(item)));
+ purl = PUrl(_T("http://1.2.3.4/"));
+ _tcscpy(item, _T("1.2.3.4"));
+ assert( ProxyItemMatch(purl, item, _tcslen(item)));
+ _tcscpy(item, _T("1.2.3.5"));
+ assert(!ProxyItemMatch(purl, item, _tcslen(item)));
+ _tcscpy(item, _T("1.2.3.5/31"));
+ assert( ProxyItemMatch(purl, item, _tcslen(item)));
+ _tcscpy(item, _T("1.2.3.5/32"));
+ assert(!ProxyItemMatch(purl, item, _tcslen(item)));
+#endif
+
+ bool autoconfig = false;
+ bool use_firefox = false;
+ std::string autoconfig_url;
+
+#if _TRY_FIREFOX
+ use_firefox = IsDefaultBrowserFirefox();
+
+ if (use_firefox) {
+ std::wstring tmp;
+ if (GetDefaultFirefoxProfile(&tmp)) {
+ bool complete = true;
+
+ StringMap settings;
+ tmp.append(L"prefs.js");
+ if (ReadFirefoxPrefs(tmp, "network.proxy.", settings)) {
+ success = true;
+ if (settings.Get("type") == "1") {
+ if (ProxyListMatch(purl, settings.Get("no_proxies_on", "localhost, 127.0.0.1").c_str(), ',')) {
+ // Bypass proxy
+ } else if (settings.Get("share_proxy_settings") == "true") {
+ proxy.type = PROXY_UNKNOWN;
+ proxy.address.SetIP(settings.Get("http"));
+ proxy.address.SetPort(atoi(settings.Get("http_port").c_str()));
+ } else if (settings.IsSet("socks")) {
+ proxy.type = PROXY_SOCKS5;
+ proxy.address.SetIP(settings.Get("socks"));
+ proxy.address.SetPort(atoi(settings.Get("socks_port").c_str()));
+ } else if (settings.IsSet("ssl")) {
+ proxy.type = PROXY_HTTPS;
+ proxy.address.SetIP(settings.Get("ssl"));
+ proxy.address.SetPort(atoi(settings.Get("ssl_port").c_str()));
+ } else if (settings.IsSet("http")) {
+ proxy.type = PROXY_HTTPS;
+ proxy.address.SetIP(settings.Get("http"));
+ proxy.address.SetPort(atoi(settings.Get("http_port").c_str()));
+ }
+ } else if (settings.Get("type") == "2") {
+ complete = success = false;
+ autoconfig_url = settings.Get("autoconfig_url").c_str();
+ } else if (settings.Get("type") == "4") {
+ complete = success = false;
+ autoconfig = true;
+ }
+ }
+ if (complete) { // Otherwise fall through to IE autoproxy code
+ return success;
+ }
+ }
+ }
+#endif // _TRY_FIREFOX
+
+#if _TRY_WINHTTP
+ if (!success) {
+ if (HMODULE hModWH = LoadLibrary(L"winhttp.dll")) {
+ pfnWinHttpOpen pWHO = reinterpret_cast<pfnWinHttpOpen>(GetProcAddress(hModWH, "WinHttpOpen"));
+ pfnWinHttpCloseHandle pWHCH = reinterpret_cast<pfnWinHttpCloseHandle>(GetProcAddress(hModWH, "WinHttpCloseHandle"));
+ pfnWinHttpGetProxyForUrl pWHGPFU = reinterpret_cast<pfnWinHttpGetProxyForUrl>(GetProcAddress(hModWH, "WinHttpGetProxyForUrl"));
+ pfnWinHttpGetIEProxyConfig pWHGIEPC = reinterpret_cast<pfnWinHttpGetIEProxyConfig>(GetProcAddress(hModWH, "WinHttpGetIEProxyConfigForCurrentUser"));
+ if (pWHO && pWHCH && pWHGPFU && pWHGIEPC) {
+ WINHTTP_CURRENT_USER_IE_PROXY_CONFIG iecfg;
+ memset(&iecfg, 0, sizeof(iecfg));
+ if (!use_firefox && !pWHGIEPC(&iecfg)) {
+ LOG_GLEM(LERROR,WINHTTP) << "WinHttpGetIEProxyConfigForCurrentUser";
+ } else {
+ success = true;
+ if (!use_firefox) {
+ if (iecfg.fAutoDetect) {
+ autoconfig = true;
+ }
+ if (iecfg.lpszAutoConfigUrl) {
+ autoconfig_url = ToUtf8(iecfg.lpszAutoConfigUrl);
+ }
+ }
+ if (!long_operation) {
+ // Unless we perform this operation in the background, don't allow
+ // it to take a long time.
+ autoconfig = false;
+ }
+ if (autoconfig || !autoconfig_url.empty()) {
+ if (HINTERNET hWinHttp = pWHO(ToUtf16(agent).c_str(),
+ WINHTTP_ACCESS_TYPE_NO_PROXY,
+ WINHTTP_NO_PROXY_NAME,
+ WINHTTP_NO_PROXY_BYPASS,
+ 0)) {
+ WINHTTP_AUTOPROXY_OPTIONS options;
+ memset(&options, 0, sizeof(options));
+ if (autoconfig) {
+ options.dwFlags |= WINHTTP_AUTOPROXY_AUTO_DETECT;
+ options.dwAutoDetectFlags |= WINHTTP_AUTO_DETECT_TYPE_DHCP
+ | WINHTTP_AUTO_DETECT_TYPE_DNS_A;
+ }
+ std::wstring autoconfig_url16((ToUtf16)(autoconfig_url));
+ if (!autoconfig_url.empty()) {
+ options.dwFlags |= WINHTTP_AUTOPROXY_CONFIG_URL;
+ options.lpszAutoConfigUrl = autoconfig_url16.c_str();
+ }
+ options.fAutoLogonIfChallenged = TRUE;
+ WINHTTP_PROXY_INFO info;
+ memset(&info, 0, sizeof(info));
+
+ BOOL success = MyWinHttpGetProxyForUrl(pWHGPFU,
+ hWinHttp, ToUtf16(url).c_str(), &options, &info);
+
+ if (!success) {
+ LOG_GLEM(LERROR,WINHTTP) << "WinHttpGetProxyForUrl";
+ } else {
+ if (iecfg.lpszProxy)
+ GlobalFree(iecfg.lpszProxy);
+ if (iecfg.lpszProxyBypass)
+ GlobalFree(iecfg.lpszProxyBypass);
+ iecfg.lpszProxy = info.lpszProxy;
+ iecfg.lpszProxyBypass = info.lpszProxyBypass;
+ }
+ pWHCH(hWinHttp);
+ }
+ }
+ if (!ProxyListMatch(purl, ToUtf8(nonnull(iecfg.lpszProxyBypass)), ' ')) {
+ ParseProxy(ToUtf8(nonnull(iecfg.lpszProxy)), proxy);
+ }
+ if (iecfg.lpszAutoConfigUrl)
+ GlobalFree(iecfg.lpszAutoConfigUrl);
+ if (iecfg.lpszProxy)
+ GlobalFree(iecfg.lpszProxy);
+ if (iecfg.lpszProxyBypass)
+ GlobalFree(iecfg.lpszProxyBypass);
+ }
+ }
+ FreeLibrary(hModWH);
+ }
+ }
+#endif // _TRY_WINHTTP
+
+#if _TRY_JSPROXY
+ if (!success) {
+ if (HMODULE hModJS = LoadLibrary(_T("jsproxy.dll"))) {
+ pfnInternetGetProxyInfo pIGPI = reinterpret_cast<pfnInternetGetProxyInfo>(GetProcAddress(hModJS, "InternetGetProxyInfo"));
+ if (pIGPI) {
+ char proxy[256], host[256];
+ memset(proxy, 0, sizeof(proxy));
+ char * ptr = proxy;
+ DWORD proxylen = sizeof(proxy);
+ std::string surl = Utf8String(url);
+ DWORD hostlen = _snprintf(host, sizeof(host), "http%s://%S", purl.secure() ? "s" : "", purl.server());
+ if (pIGPI(surl.data(), surl.size(), host, hostlen, &ptr, &proxylen)) {
+ LOG(INFO) << "Proxy: " << proxy;
+ } else {
+ LOG_GLE(INFO) << "InternetGetProxyInfo";
+ }
+ }
+ FreeLibrary(hModJS);
+ }
+ }
+#endif // _TRY_JSPROXY
+
+#if _TRY_WM_FINDPROXY
+ if (!success) {
+ INSNetSourceCreator * nsc = 0;
+ HRESULT hr = CoCreateInstance(CLSID_ClientNetManager, 0, CLSCTX_ALL, IID_INSNetSourceCreator, (LPVOID *) &nsc);
+ if (SUCCEEDED(hr)) {
+ if (SUCCEEDED(hr = nsc->Initialize())) {
+ VARIANT dispatch;
+ VariantInit(&dispatch);
+ if (SUCCEEDED(hr = nsc->GetNetSourceAdminInterface(L"http", &dispatch))) {
+ IWMSInternalAdminNetSource * ians = 0;
+ if (SUCCEEDED(hr = dispatch.pdispVal->QueryInterface(IID_IWMSInternalAdminNetSource, (LPVOID *) &ians))) {
+ _bstr_t host(purl.server());
+ BSTR proxy = 0;
+ BOOL bProxyEnabled = FALSE;
+ DWORD port, context = 0;
+ if (SUCCEEDED(hr = ians->FindProxyForURL(L"http", host, &bProxyEnabled, &proxy, &port, &context))) {
+ success = true;
+ if (bProxyEnabled) {
+ _bstr_t sproxy = proxy;
+ proxy.ptype = PT_HTTPS;
+ proxy.host = sproxy;
+ proxy.port = port;
+ }
+ }
+ SysFreeString(proxy);
+ if (FAILED(hr = ians->ShutdownProxyContext(context))) {
+ LOG(LS_INFO) << "IWMSInternalAdminNetSource::ShutdownProxyContext failed: " << hr;
+ }
+ ians->Release();
+ }
+ }
+ VariantClear(&dispatch);
+ if (FAILED(hr = nsc->Shutdown())) {
+ LOG(LS_INFO) << "INSNetSourceCreator::Shutdown failed: " << hr;
+ }
+ }
+ nsc->Release();
+ }
+ }
+#endif // _TRY_WM_FINDPROXY
+
+#if _TRY_IE_LAN_SETTINGS
+ if (!success) {
+ wchar_t buffer[1024];
+ memset(buffer, 0, sizeof(buffer));
+ INTERNET_PROXY_INFO * info = reinterpret_cast<INTERNET_PROXY_INFO *>(buffer);
+ DWORD dwSize = sizeof(buffer);
+
+ if (!InternetQueryOption(0, INTERNET_OPTION_PROXY, info, &dwSize)) {
+ LOG(LS_INFO) << "InternetQueryOption failed: " << GetLastError();
+ } else if (info->dwAccessType == INTERNET_OPEN_TYPE_DIRECT) {
+ success = true;
+ } else if (info->dwAccessType == INTERNET_OPEN_TYPE_PROXY) {
+ success = true;
+ if (!ProxyListMatch(purl, nonnull(reinterpret_cast<const char*>(info->lpszProxyBypass)), ' ')) {
+ ParseProxy(nonnull(reinterpret_cast<const char*>(info->lpszProxy)), proxy);
+ }
+ } else {
+ LOG(LS_INFO) << "unknown internet access type: " << info->dwAccessType;
+ }
+ }
+#endif // _TRY_IE_LAN_SETTINGS
+
+#if 0
+ if (!success) {
+ INTERNET_PER_CONN_OPTION_LIST list;
+ INTERNET_PER_CONN_OPTION options[3];
+ memset(&list, 0, sizeof(list));
+ memset(&options, 0, sizeof(options));
+
+ list.dwSize = sizeof(list);
+ list.dwOptionCount = 3;
+ list.pOptions = options;
+ options[0].dwOption = INTERNET_PER_CONN_FLAGS;
+ options[1].dwOption = INTERNET_PER_CONN_PROXY_SERVER;
+ options[2].dwOption = INTERNET_PER_CONN_PROXY_BYPASS;
+ DWORD dwSize = sizeof(list);
+
+ if (!InternetQueryOption(0, INTERNET_OPTION_PER_CONNECTION_OPTION, &list, &dwSize)) {
+ LOG(LS_INFO) << "InternetQueryOption failed: " << GetLastError();
+ } else if ((options[0].Value.dwValue & PROXY_TYPE_PROXY) != 0) {
+ success = true;
+ if (!ProxyListMatch(purl, nonnull(options[2].Value.pszValue), _T(';'))) {
+ ParseProxy(nonnull(options[1].Value.pszValue), proxy);
+ }
+ } else if ((options[0].Value.dwValue & PROXY_TYPE_DIRECT) != 0) {
+ success = true;
+ } else {
+ LOG(LS_INFO) << "unknown internet access type: "
+ << options[0].Value.dwValue;
+ }
+ if (options[1].Value.pszValue) {
+ GlobalFree(options[1].Value.pszValue);
+ }
+ if (options[2].Value.pszValue) {
+ GlobalFree(options[2].Value.pszValue);
+ }
+ }
+#endif // 0
+
+ return success;
+}
diff --git a/Plugins/jingle/libjingle/talk/base/proxydetect.h b/Plugins/jingle/libjingle/talk/base/proxydetect.h
new file mode 100644
index 0000000..3de2598
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/proxydetect.h
@@ -0,0 +1,13 @@
+#ifndef _PROXYDETECT_H_
+#define _PROXYDETECT_H_
+
+#include "talk/base/proxyinfo.h"
+
+// Auto-detect the proxy server. Returns true if a proxy is configured,
+// although hostname may be empty if the proxy is not required for the given URL.
+
+bool GetProxySettingsForUrl(const char* agent, const char* url,
+ talk_base::ProxyInfo& proxy,
+ bool long_operation = false);
+
+#endif // _PROXYDETECT_H_
diff --git a/Plugins/jingle/libjingle/talk/base/proxyinfo.cc b/Plugins/jingle/libjingle/talk/base/proxyinfo.cc
new file mode 100644
index 0000000..1d9c588
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/proxyinfo.cc
@@ -0,0 +1,37 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "talk/base/proxyinfo.h"
+
+namespace talk_base {
+
+const char * ProxyToString(ProxyType proxy) {
+ const char * const PROXY_NAMES[] = { "none", "https", "socks5", "unknown" };
+ return PROXY_NAMES[proxy];
+}
+
+} // namespace talk_base
diff --git a/Plugins/jingle/libjingle/talk/base/proxyinfo.h b/Plugins/jingle/libjingle/talk/base/proxyinfo.h
new file mode 100644
index 0000000..834ec4f
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/proxyinfo.h
@@ -0,0 +1,51 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TALK_BASE_PROXYINFO_H__
+#define TALK_BASE_PROXYINFO_H__
+
+#include <string>
+#include "talk/base/socketaddress.h"
+#include "talk/base/cryptstring.h"
+
+namespace talk_base {
+
+enum ProxyType { PROXY_NONE, PROXY_HTTPS, PROXY_SOCKS5, PROXY_UNKNOWN };
+const char * ProxyToString(ProxyType proxy);
+
+struct ProxyInfo {
+ ProxyType type;
+ SocketAddress address;
+ std::string username;
+ CryptString password;
+
+ ProxyInfo() : type(PROXY_NONE) { }
+};
+
+} // namespace talk_base
+
+#endif // TALK_BASE_PROXYINFO_H__
diff --git a/Plugins/jingle/libjingle/talk/base/schanneladapter.cc b/Plugins/jingle/libjingle/talk/base/schanneladapter.cc
new file mode 100644
index 0000000..d971c7e
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/schanneladapter.cc
@@ -0,0 +1,749 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "talk/base/win32.h"
+#define SECURITY_WIN32
+#include <security.h>
+#include <schannel.h>
+
+#include <iomanip>
+#include <vector>
+
+#include "talk/base/common.h"
+#include "talk/base/logging.h"
+#include "talk/base/schanneladapter.h"
+#include "talk/base/sec_buffer.h"
+#include "talk/base/thread.h"
+
+namespace talk_base {
+
+/////////////////////////////////////////////////////////////////////////////
+// SChannelAdapter
+/////////////////////////////////////////////////////////////////////////////
+
+extern const ConstantLabel SECURITY_ERRORS[];
+
+const ConstantLabel SECURITY_ERRORS[] = {
+ KLABEL(SEC_I_COMPLETE_AND_CONTINUE),
+ KLABEL(SEC_I_COMPLETE_NEEDED),
+ KLABEL(SEC_I_CONTEXT_EXPIRED),
+ KLABEL(SEC_I_CONTINUE_NEEDED),
+ KLABEL(SEC_I_INCOMPLETE_CREDENTIALS),
+ KLABEL(SEC_I_RENEGOTIATE),
+ KLABEL(SEC_E_CERT_EXPIRED),
+ KLABEL(SEC_E_INCOMPLETE_MESSAGE),
+ KLABEL(SEC_E_INSUFFICIENT_MEMORY),
+ KLABEL(SEC_E_INTERNAL_ERROR),
+ KLABEL(SEC_E_INVALID_HANDLE),
+ KLABEL(SEC_E_INVALID_TOKEN),
+ KLABEL(SEC_E_LOGON_DENIED),
+ KLABEL(SEC_E_NO_AUTHENTICATING_AUTHORITY),
+ KLABEL(SEC_E_NO_CREDENTIALS),
+ KLABEL(SEC_E_NOT_OWNER),
+ KLABEL(SEC_E_OK),
+ KLABEL(SEC_E_SECPKG_NOT_FOUND),
+ KLABEL(SEC_E_TARGET_UNKNOWN),
+ KLABEL(SEC_E_UNKNOWN_CREDENTIALS),
+ KLABEL(SEC_E_UNSUPPORTED_FUNCTION),
+ KLABEL(SEC_E_UNTRUSTED_ROOT),
+ KLABEL(SEC_E_WRONG_PRINCIPAL),
+ LASTLABEL
+};
+
+const ConstantLabel SCHANNEL_BUFFER_TYPES[] = {
+ KLABEL(SECBUFFER_EMPTY), // 0
+ KLABEL(SECBUFFER_DATA), // 1
+ KLABEL(SECBUFFER_TOKEN), // 2
+ KLABEL(SECBUFFER_PKG_PARAMS), // 3
+ KLABEL(SECBUFFER_MISSING), // 4
+ KLABEL(SECBUFFER_EXTRA), // 5
+ KLABEL(SECBUFFER_STREAM_TRAILER), // 6
+ KLABEL(SECBUFFER_STREAM_HEADER), // 7
+ KLABEL(SECBUFFER_MECHLIST), // 11
+ KLABEL(SECBUFFER_MECHLIST_SIGNATURE), // 12
+ KLABEL(SECBUFFER_TARGET), // 13
+ KLABEL(SECBUFFER_CHANNEL_BINDINGS), // 14
+ LASTLABEL
+};
+
+void DescribeBuffer(LoggingSeverity severity, const char* prefix,
+ const SecBuffer& sb) {
+ LOG_V(severity)
+ << prefix
+ << "(" << sb.cbBuffer
+ << ", " << FindLabel(sb.BufferType & ~SECBUFFER_ATTRMASK,
+ SCHANNEL_BUFFER_TYPES)
+ << ", " << sb.pvBuffer << ")";
+}
+
+void DescribeBuffers(LoggingSeverity severity, const char* prefix,
+ const SecBufferDesc* sbd) {
+ if (!LOG_CHECK_LEVEL_V(severity))
+ return;
+ LOG_V(severity) << prefix << "(";
+ for (size_t i=0; i<sbd->cBuffers; ++i) {
+ DescribeBuffer(severity, " ", sbd->pBuffers[i]);
+ }
+ LOG_V(severity) << ")";
+}
+
+const ULONG SSL_FLAGS_DEFAULT = ISC_REQ_ALLOCATE_MEMORY
+ | ISC_REQ_CONFIDENTIALITY
+ | ISC_REQ_EXTENDED_ERROR
+ | ISC_REQ_INTEGRITY
+ | ISC_REQ_REPLAY_DETECT
+ | ISC_REQ_SEQUENCE_DETECT
+ | ISC_REQ_STREAM;
+ //| ISC_REQ_USE_SUPPLIED_CREDS;
+
+typedef std::vector<char> SChannelBuffer;
+
+struct SChannelAdapter::SSLImpl {
+ CredHandle cred;
+ CtxtHandle ctx;
+ bool cred_init, ctx_init;
+ SChannelBuffer inbuf, outbuf, readable;
+ SecPkgContext_StreamSizes sizes;
+
+ SSLImpl() : cred_init(false), ctx_init(false) { }
+};
+
+SChannelAdapter::SChannelAdapter(AsyncSocket* socket)
+ : SSLAdapter(socket), state_(SSL_NONE),
+ restartable_(false), signal_close_(false), message_pending_(false),
+ impl_(new SSLImpl) {
+}
+
+SChannelAdapter::~SChannelAdapter() {
+ Cleanup();
+}
+
+int
+SChannelAdapter::StartSSL(const char* hostname, bool restartable) {
+ if (state_ != SSL_NONE)
+ return ERROR_ALREADY_INITIALIZED;
+
+ ssl_host_name_ = hostname;
+ restartable_ = restartable;
+
+ if (socket_->GetState() != Socket::CS_CONNECTED) {
+ state_ = SSL_WAIT;
+ return 0;
+ }
+
+ state_ = SSL_CONNECTING;
+ if (int err = BeginSSL()) {
+ Error("BeginSSL", err, false);
+ return err;
+ }
+
+ return 0;
+}
+
+int
+SChannelAdapter::BeginSSL() {
+ LOG(LS_VERBOSE) << "BeginSSL: " << ssl_host_name_;
+ ASSERT(state_ == SSL_CONNECTING);
+
+ SECURITY_STATUS ret;
+
+ SCHANNEL_CRED sc_cred = { 0 };
+ sc_cred.dwVersion = SCHANNEL_CRED_VERSION;
+ //sc_cred.dwMinimumCipherStrength = 128; // Note: use system default
+ sc_cred.dwFlags = SCH_CRED_NO_DEFAULT_CREDS | SCH_CRED_AUTO_CRED_VALIDATION;
+
+ ret = AcquireCredentialsHandle(NULL, UNISP_NAME, SECPKG_CRED_OUTBOUND, NULL,
+ &sc_cred, NULL, NULL, &impl_->cred, NULL);
+ if (ret != SEC_E_OK) {
+ LOG(LS_ERROR) << "AcquireCredentialsHandle error: "
+ << ErrorName(ret, SECURITY_ERRORS);
+ return ret;
+ }
+ impl_->cred_init = true;
+
+ if (LOG_CHECK_LEVEL(LS_VERBOSE)) {
+ SecPkgCred_CipherStrengths cipher_strengths = { 0 };
+ ret = QueryCredentialsAttributes(&impl_->cred,
+ SECPKG_ATTR_CIPHER_STRENGTHS,
+ &cipher_strengths);
+ if (SUCCEEDED(ret)) {
+ LOG(LS_VERBOSE) << "SChannel cipher strength: "
+ << cipher_strengths.dwMinimumCipherStrength << " - "
+ << cipher_strengths.dwMaximumCipherStrength;
+ }
+
+ SecPkgCred_SupportedAlgs supported_algs = { 0 };
+ ret = QueryCredentialsAttributes(&impl_->cred,
+ SECPKG_ATTR_SUPPORTED_ALGS,
+ &supported_algs);
+ if (SUCCEEDED(ret)) {
+ LOG(LS_VERBOSE) << "SChannel supported algorithms:";
+ for (DWORD i=0; i<supported_algs.cSupportedAlgs; ++i) {
+ ALG_ID alg_id = supported_algs.palgSupportedAlgs[i];
+ PCCRYPT_OID_INFO oinfo = CryptFindOIDInfo(CRYPT_OID_INFO_ALGID_KEY,
+ &alg_id, 0);
+ LPCWSTR alg_name = (NULL != oinfo) ? oinfo->pwszName : L"Unknown";
+ LOG(LS_VERBOSE) << " " << talk_base::ToUtf8(alg_name)
+ << " (" << alg_id << ")";
+ }
+ }
+ }
+
+ ULONG flags = SSL_FLAGS_DEFAULT, ret_flags = 0;
+ if (ignore_bad_cert())
+ flags |= ISC_REQ_MANUAL_CRED_VALIDATION;
+
+ CSecBufferBundle<2, CSecBufferBase::FreeSSPI> sb_out;
+ ret = InitializeSecurityContextA(&impl_->cred, NULL,
+ const_cast<char*>(ssl_host_name_.c_str()),
+ flags, 0, 0, NULL, 0,
+ &impl_->ctx, sb_out.desc(),
+ &ret_flags, NULL);
+ if (SUCCEEDED(ret))
+ impl_->ctx_init = true;
+ return ProcessContext(ret, NULL, sb_out.desc());
+}
+
+int
+SChannelAdapter::ContinueSSL() {
+ LOG(LS_VERBOSE) << "ContinueSSL";
+ ASSERT(state_ == SSL_CONNECTING);
+
+ SECURITY_STATUS ret;
+
+ CSecBufferBundle<2> sb_in;
+ sb_in[0].BufferType = SECBUFFER_TOKEN;
+ sb_in[0].cbBuffer = static_cast<unsigned long>(impl_->inbuf.size());
+ sb_in[0].pvBuffer = &impl_->inbuf[0];
+ //DescribeBuffers(LS_VERBOSE, "Input Buffer ", sb_in.desc());
+
+ ULONG flags = SSL_FLAGS_DEFAULT, ret_flags = 0;
+ if (ignore_bad_cert())
+ flags |= ISC_REQ_MANUAL_CRED_VALIDATION;
+
+ CSecBufferBundle<2, CSecBufferBase::FreeSSPI> sb_out;
+ ret = InitializeSecurityContextA(&impl_->cred, &impl_->ctx,
+ const_cast<char*>(ssl_host_name_.c_str()),
+ flags, 0, 0, sb_in.desc(), 0,
+ NULL, sb_out.desc(),
+ &ret_flags, NULL);
+ return ProcessContext(ret, sb_in.desc(), sb_out.desc());
+}
+
+int
+SChannelAdapter::ProcessContext(long int status, _SecBufferDesc* sbd_in,
+ _SecBufferDesc* sbd_out) {
+ LoggingSeverity level = LS_ERROR;
+ if ((status == SEC_E_OK)
+ || (status != SEC_I_CONTINUE_NEEDED)
+ || (status != SEC_E_INCOMPLETE_MESSAGE)) {
+ level = LS_VERBOSE; // Expected messages
+ }
+ LOG_V(level)
+ << "InitializeSecurityContext error: "
+ << ErrorName(status, SECURITY_ERRORS);
+ //if (sbd_in)
+ // DescribeBuffers(LS_VERBOSE, "Input Buffer ", sbd_in);
+ //if (sbd_out)
+ // DescribeBuffers(LS_VERBOSE, "Output Buffer ", sbd_out);
+
+ if (status == SEC_E_INCOMPLETE_MESSAGE) {
+ // Wait for more input from server.
+ return Flush();
+ }
+
+ if (FAILED(status)) {
+ // We can't continue. Common errors:
+ // SEC_E_CERT_EXPIRED - Typically, this means the computer clock is wrong.
+ return status;
+ }
+
+ // Note: we check both input and output buffers for SECBUFFER_EXTRA.
+ // Experience shows it appearing in the input, but the documentation claims
+ // it should appear in the output.
+ size_t extra = 0;
+ if (sbd_in) {
+ for (size_t i=0; i<sbd_in->cBuffers; ++i) {
+ SecBuffer& buffer = sbd_in->pBuffers[i];
+ if (buffer.BufferType == SECBUFFER_EXTRA) {
+ extra += buffer.cbBuffer;
+ }
+ }
+ }
+ if (sbd_out) {
+ for (size_t i=0; i<sbd_out->cBuffers; ++i) {
+ SecBuffer& buffer = sbd_out->pBuffers[i];
+ if (buffer.BufferType == SECBUFFER_EXTRA) {
+ extra += buffer.cbBuffer;
+ } else if (buffer.BufferType == SECBUFFER_TOKEN) {
+ impl_->outbuf.insert(impl_->outbuf.end(),
+ reinterpret_cast<char*>(buffer.pvBuffer),
+ reinterpret_cast<char*>(buffer.pvBuffer) + buffer.cbBuffer);
+ }
+ }
+ }
+
+ if (extra) {
+ ASSERT(extra <= impl_->inbuf.size());
+ size_t consumed = impl_->inbuf.size() - extra;
+ memmove(&impl_->inbuf[0], &impl_->inbuf[consumed], extra);
+ impl_->inbuf.resize(extra);
+ } else {
+ impl_->inbuf.clear();
+ }
+
+ if (SEC_I_CONTINUE_NEEDED == status) {
+ // Send data to server and wait for response.
+ // Note: ContinueSSL will result in a Flush, anyway.
+ return impl_->inbuf.empty() ? Flush() : ContinueSSL();
+ }
+
+ if (SEC_E_OK == status) {
+ LOG(LS_VERBOSE) << "QueryContextAttributes";
+ status = QueryContextAttributes(&impl_->ctx, SECPKG_ATTR_STREAM_SIZES,
+ &impl_->sizes);
+ if (FAILED(status)) {
+ LOG(LS_ERROR) << "QueryContextAttributes error: "
+ << ErrorName(status, SECURITY_ERRORS);
+ return status;
+ }
+
+ state_ = SSL_CONNECTED;
+
+ if (int err = DecryptData()) {
+ return err;
+ } else if (int err = Flush()) {
+ return err;
+ } else {
+ // If we decrypted any data, queue up a notification here
+ PostEvent();
+ // Signal our connectedness
+ AsyncSocketAdapter::OnConnectEvent(this);
+ }
+ return 0;
+ }
+
+ if (SEC_I_INCOMPLETE_CREDENTIALS == status) {
+ // We don't support client authentication in schannel.
+ return status;
+ }
+
+ // We don't expect any other codes
+ ASSERT(false);
+ return status;
+}
+
+int
+SChannelAdapter::DecryptData() {
+ SChannelBuffer& inbuf = impl_->inbuf;
+ SChannelBuffer& readable = impl_->readable;
+
+ while (!inbuf.empty()) {
+ CSecBufferBundle<4> in_buf;
+ in_buf[0].BufferType = SECBUFFER_DATA;
+ in_buf[0].cbBuffer = static_cast<unsigned long>(inbuf.size());
+ in_buf[0].pvBuffer = &inbuf[0];
+
+ //DescribeBuffers(LS_VERBOSE, "Decrypt In ", in_buf.desc());
+ SECURITY_STATUS status = DecryptMessage(&impl_->ctx, in_buf.desc(), 0, 0);
+ //DescribeBuffers(LS_VERBOSE, "Decrypt Out ", in_buf.desc());
+
+ // Note: We are explicitly treating SEC_E_OK, SEC_I_CONTEXT_EXPIRED, and
+ // any other successful results as continue.
+ if (SUCCEEDED(status)) {
+ size_t data_len = 0, extra_len = 0;
+ for (size_t i=0; i<in_buf.desc()->cBuffers; ++i) {
+ if (in_buf[i].BufferType == SECBUFFER_DATA) {
+ data_len += in_buf[i].cbBuffer;
+ readable.insert(readable.end(),
+ reinterpret_cast<char*>(in_buf[i].pvBuffer),
+ reinterpret_cast<char*>(in_buf[i].pvBuffer) + in_buf[i].cbBuffer);
+ } else if (in_buf[i].BufferType == SECBUFFER_EXTRA) {
+ extra_len += in_buf[i].cbBuffer;
+ }
+ }
+ // There is a bug on Win2K where SEC_I_CONTEXT_EXPIRED is misclassified.
+ if ((data_len == 0) && (inbuf[0] == 0x15)) {
+ status = SEC_I_CONTEXT_EXPIRED;
+ }
+ if (extra_len) {
+ size_t consumed = inbuf.size() - extra_len;
+ memmove(&inbuf[0], &inbuf[consumed], extra_len);
+ inbuf.resize(extra_len);
+ } else {
+ inbuf.clear();
+ }
+ // TODO: Handle SEC_I_CONTEXT_EXPIRED to do clean shutdown
+ if (status != SEC_E_OK) {
+ LOG(LS_INFO) << "DecryptMessage returned continuation code: "
+ << ErrorName(status, SECURITY_ERRORS);
+ }
+ continue;
+ }
+
+ if (status == SEC_E_INCOMPLETE_MESSAGE) {
+ break;
+ } else {
+ return status;
+ }
+ }
+
+ return 0;
+}
+
+void
+SChannelAdapter::Cleanup() {
+ if (impl_->ctx_init)
+ DeleteSecurityContext(&impl_->ctx);
+ if (impl_->cred_init)
+ FreeCredentialsHandle(&impl_->cred);
+ delete impl_;
+}
+
+void
+SChannelAdapter::PostEvent() {
+ // Check if there's anything notable to signal
+ if (impl_->readable.empty() && !signal_close_)
+ return;
+
+ // Only one post in the queue at a time
+ if (message_pending_)
+ return;
+
+ if (Thread* thread = Thread::Current()) {
+ message_pending_ = true;
+ thread->Post(this);
+ } else {
+ LOG(LS_ERROR) << "No thread context available for SChannelAdapter";
+ ASSERT(false);
+ }
+}
+
+void
+SChannelAdapter::Error(const char* context, int err, bool signal) {
+ LOG(LS_WARNING) << "SChannelAdapter::Error("
+ << context << ", "
+ << ErrorName(err, SECURITY_ERRORS) << ")";
+ state_ = SSL_ERROR;
+ SetError(err);
+ if (signal)
+ AsyncSocketAdapter::OnCloseEvent(this, err);
+}
+
+int
+SChannelAdapter::Read() {
+ char buffer[4096];
+ SChannelBuffer& inbuf = impl_->inbuf;
+ while (true) {
+ int ret = AsyncSocketAdapter::Recv(buffer, sizeof(buffer));
+ if (ret > 0) {
+ inbuf.insert(inbuf.end(), buffer, buffer + ret);
+ } else if (GetError() == EWOULDBLOCK) {
+ return 0; // Blocking
+ } else {
+ return GetError();
+ }
+ }
+}
+
+int
+SChannelAdapter::Flush() {
+ int result = 0;
+ size_t pos = 0;
+ SChannelBuffer& outbuf = impl_->outbuf;
+ while (pos < outbuf.size()) {
+ int sent = AsyncSocketAdapter::Send(&outbuf[pos], outbuf.size() - pos);
+ if (sent > 0) {
+ pos += sent;
+ } else if (GetError() == EWOULDBLOCK) {
+ break; // Blocking
+ } else {
+ result = GetError();
+ break;
+ }
+ }
+ if (int remainder = outbuf.size() - pos) {
+ memmove(&outbuf[0], &outbuf[pos], remainder);
+ outbuf.resize(remainder);
+ } else {
+ outbuf.clear();
+ }
+ return result;
+}
+
+//
+// AsyncSocket Implementation
+//
+
+int
+SChannelAdapter::Send(const void* pv, size_t cb) {
+ switch (state_) {
+ case SSL_NONE:
+ return AsyncSocketAdapter::Send(pv, cb);
+
+ case SSL_WAIT:
+ case SSL_CONNECTING:
+ SetError(EWOULDBLOCK);
+ return SOCKET_ERROR;
+
+ case SSL_CONNECTED:
+ break;
+
+ case SSL_ERROR:
+ default:
+ return SOCKET_ERROR;
+ }
+
+ size_t written = 0;
+ SChannelBuffer& outbuf = impl_->outbuf;
+ while (written < cb) {
+ const size_t encrypt_len = std::min<size_t>(cb - written,
+ impl_->sizes.cbMaximumMessage);
+
+ CSecBufferBundle<4> out_buf;
+ out_buf[0].BufferType = SECBUFFER_STREAM_HEADER;
+ out_buf[0].cbBuffer = impl_->sizes.cbHeader;
+ out_buf[1].BufferType = SECBUFFER_DATA;
+ out_buf[1].cbBuffer = static_cast<unsigned long>(encrypt_len);
+ out_buf[2].BufferType = SECBUFFER_STREAM_TRAILER;
+ out_buf[2].cbBuffer = impl_->sizes.cbTrailer;
+
+ size_t packet_len = out_buf[0].cbBuffer
+ + out_buf[1].cbBuffer
+ + out_buf[2].cbBuffer;
+
+ SChannelBuffer message;
+ message.resize(packet_len);
+ out_buf[0].pvBuffer = &message[0];
+ out_buf[1].pvBuffer = &message[out_buf[0].cbBuffer];
+ out_buf[2].pvBuffer = &message[out_buf[0].cbBuffer + out_buf[1].cbBuffer];
+
+ memcpy(out_buf[1].pvBuffer,
+ static_cast<const char*>(pv) + written,
+ encrypt_len);
+
+ //DescribeBuffers(LS_VERBOSE, "Encrypt In ", out_buf.desc());
+ SECURITY_STATUS res = EncryptMessage(&impl_->ctx, 0, out_buf.desc(), 0);
+ //DescribeBuffers(LS_VERBOSE, "Encrypt Out ", out_buf.desc());
+
+ if (FAILED(res)) {
+ Error("EncryptMessage", res, false);
+ return SOCKET_ERROR;
+ }
+
+ // We assume that the header and data segments do not change length,
+ // or else encrypting the concatenated packet in-place is wrong.
+ ASSERT(out_buf[0].cbBuffer == impl_->sizes.cbHeader);
+ ASSERT(out_buf[1].cbBuffer == static_cast<unsigned long>(encrypt_len));
+
+ // However, the length of the trailer may change due to padding.
+ ASSERT(out_buf[2].cbBuffer <= impl_->sizes.cbTrailer);
+
+ packet_len = out_buf[0].cbBuffer
+ + out_buf[1].cbBuffer
+ + out_buf[2].cbBuffer;
+
+ written += encrypt_len;
+ outbuf.insert(outbuf.end(), &message[0], &message[packet_len-1]+1);
+ }
+
+ if (int err = Flush()) {
+ state_ = SSL_ERROR;
+ SetError(err);
+ return SOCKET_ERROR;
+ }
+
+ return static_cast<int>(written);
+}
+
+int
+SChannelAdapter::Recv(void* pv, size_t cb) {
+ switch (state_) {
+ case SSL_NONE:
+ return AsyncSocketAdapter::Recv(pv, cb);
+
+ case SSL_WAIT:
+ case SSL_CONNECTING:
+ SetError(EWOULDBLOCK);
+ return SOCKET_ERROR;
+
+ case SSL_CONNECTED:
+ break;
+
+ case SSL_ERROR:
+ default:
+ return SOCKET_ERROR;
+ }
+
+ SChannelBuffer& readable = impl_->readable;
+ if (readable.empty()) {
+ SetError(EWOULDBLOCK);
+ return SOCKET_ERROR;
+ }
+ size_t read = min(cb, readable.size());
+ memcpy(pv, &readable[0], read);
+ if (size_t remaining = readable.size() - read) {
+ memmove(&readable[0], &readable[read], remaining);
+ readable.resize(remaining);
+ } else {
+ readable.clear();
+ }
+
+ PostEvent();
+ return static_cast<int>(read);
+}
+
+int
+SChannelAdapter::Close() {
+ if (!impl_->readable.empty()) {
+ LOG(WARNING) << "SChannelAdapter::Close with readable data";
+ // Note: this isn't strictly an error, but we're using it temporarily to
+ // track bugs.
+ //ASSERT(false);
+ }
+ if (state_ == SSL_CONNECTED) {
+ DWORD token = SCHANNEL_SHUTDOWN;
+ CSecBufferBundle<1> sb_in;
+ sb_in[0].BufferType = SECBUFFER_TOKEN;
+ sb_in[0].cbBuffer = sizeof(token);
+ sb_in[0].pvBuffer = &token;
+ ApplyControlToken(&impl_->ctx, sb_in.desc());
+ // TODO: In theory, to do a nice shutdown, we need to begin shutdown
+ // negotiation with more calls to InitializeSecurityContext. Since the
+ // socket api doesn't support nice shutdown at this point, we don't bother.
+ }
+ Cleanup();
+ impl_ = new SSLImpl;
+ state_ = restartable_ ? SSL_WAIT : SSL_NONE;
+ signal_close_ = false;
+ message_pending_ = false;
+ return AsyncSocketAdapter::Close();
+}
+
+Socket::ConnState
+SChannelAdapter::GetState() const {
+ if (signal_close_)
+ return CS_CONNECTED;
+ ConnState state = socket_->GetState();
+ if ((state == CS_CONNECTED)
+ && ((state_ == SSL_WAIT) || (state_ == SSL_CONNECTING)))
+ state = CS_CONNECTING;
+ return state;
+}
+
+void
+SChannelAdapter::OnConnectEvent(AsyncSocket* socket) {
+ LOG(LS_VERBOSE) << "SChannelAdapter::OnConnectEvent";
+ if (state_ != SSL_WAIT) {
+ ASSERT(state_ == SSL_NONE);
+ AsyncSocketAdapter::OnConnectEvent(socket);
+ return;
+ }
+
+ state_ = SSL_CONNECTING;
+ if (int err = BeginSSL()) {
+ Error("BeginSSL", err);
+ }
+}
+
+void
+SChannelAdapter::OnReadEvent(AsyncSocket* socket) {
+ if (state_ == SSL_NONE) {
+ AsyncSocketAdapter::OnReadEvent(socket);
+ return;
+ }
+
+ if (int err = Read()) {
+ Error("Read", err);
+ return;
+ }
+
+ if (impl_->inbuf.empty())
+ return;
+
+ if (state_ == SSL_CONNECTED) {
+ if (int err = DecryptData()) {
+ Error("DecryptData", err);
+ } else if (!impl_->readable.empty()) {
+ AsyncSocketAdapter::OnReadEvent(this);
+ }
+ } else if (state_ == SSL_CONNECTING) {
+ if (int err = ContinueSSL()) {
+ Error("ContinueSSL", err);
+ }
+ }
+}
+
+void
+SChannelAdapter::OnWriteEvent(AsyncSocket* socket) {
+ if (state_ == SSL_NONE) {
+ AsyncSocketAdapter::OnWriteEvent(socket);
+ return;
+ }
+
+ if (int err = Flush()) {
+ Error("Flush", err);
+ return;
+ }
+
+ // See if we have more data to write
+ if (!impl_->outbuf.empty())
+ return;
+
+ // Buffer is empty, submit notification
+ if (state_ == SSL_CONNECTED) {
+ AsyncSocketAdapter::OnWriteEvent(socket);
+ }
+}
+
+void
+SChannelAdapter::OnCloseEvent(AsyncSocket* socket, int err) {
+ if ((state_ == SSL_NONE) || impl_->readable.empty()) {
+ AsyncSocketAdapter::OnCloseEvent(socket, err);
+ return;
+ }
+
+ // If readable is non-empty, then we have a pending Message
+ // that will allow us to signal close (eventually).
+ signal_close_ = true;
+}
+
+void
+SChannelAdapter::OnMessage(Message* pmsg) {
+ if (!message_pending_)
+ return; // This occurs when socket is closed
+
+ message_pending_ = false;
+ if (!impl_->readable.empty()) {
+ AsyncSocketAdapter::OnReadEvent(this);
+ } else if (signal_close_) {
+ signal_close_ = false;
+ AsyncSocketAdapter::OnCloseEvent(this, 0); // TODO: cache this error?
+ }
+}
+
+} // namespace talk_base
diff --git a/Plugins/jingle/libjingle/talk/base/schanneladapter.h b/Plugins/jingle/libjingle/talk/base/schanneladapter.h
new file mode 100644
index 0000000..a5ab7b3
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/schanneladapter.h
@@ -0,0 +1,94 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TALK_BASE_SCHANNELADAPTER_H__
+#define TALK_BASE_SCHANNELADAPTER_H__
+
+#include <string>
+#include "talk/base/ssladapter.h"
+#include "talk/base/messagequeue.h"
+struct _SecBufferDesc;
+
+namespace talk_base {
+
+///////////////////////////////////////////////////////////////////////////////
+
+class SChannelAdapter : public SSLAdapter, public MessageHandler {
+public:
+ SChannelAdapter(AsyncSocket* socket);
+ virtual ~SChannelAdapter();
+
+ virtual int StartSSL(const char* hostname, bool restartable);
+ virtual int Send(const void* pv, size_t cb);
+ virtual int Recv(void* pv, size_t cb);
+ virtual int Close();
+
+ // Note that the socket returns ST_CONNECTING while SSL is being negotiated.
+ virtual ConnState GetState() const;
+
+protected:
+ enum SSLState {
+ SSL_NONE, SSL_WAIT, SSL_CONNECTING, SSL_CONNECTED, SSL_ERROR
+ };
+ struct SSLImpl;
+
+ virtual void OnConnectEvent(AsyncSocket* socket);
+ virtual void OnReadEvent(AsyncSocket* socket);
+ virtual void OnWriteEvent(AsyncSocket* socket);
+ virtual void OnCloseEvent(AsyncSocket* socket, int err);
+ virtual void OnMessage(Message* pmsg);
+
+ int BeginSSL();
+ int ContinueSSL();
+ int ProcessContext(long int status, _SecBufferDesc* sbd_in,
+ _SecBufferDesc* sbd_out);
+ int DecryptData();
+
+ int Read();
+ int Flush();
+ void Error(const char* context, int err, bool signal = true);
+ void Cleanup();
+
+ void PostEvent();
+
+private:
+ SSLState state_;
+ std::string ssl_host_name_;
+ // If true, socket will retain SSL configuration after Close.
+ bool restartable_;
+ // If true, we are delaying signalling close until all data is read.
+ bool signal_close_;
+ // If true, we are waiting to be woken up to signal readability or closure.
+ bool message_pending_;
+ SSLImpl* impl_;
+};
+
+/////////////////////////////////////////////////////////////////////////////
+
+} // namespace talk_base
+
+#endif // TALK_BASE_SCHANNELADAPTER_H__
diff --git a/Plugins/jingle/libjingle/talk/base/scoped_ptr.h b/Plugins/jingle/libjingle/talk/base/scoped_ptr.h
new file mode 100644
index 0000000..c5e3c5d
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/scoped_ptr.h
@@ -0,0 +1,259 @@
+// (C) Copyright Greg Colvin and Beman Dawes 1998, 1999.
+// Copyright (c) 2001, 2002 Peter Dimov
+//
+// Permission to copy, use, modify, sell and distribute this software
+// is granted provided this copyright notice appears in all copies.
+// This software is provided "as is" without express or implied
+// warranty, and with no claim as to its suitability for any purpose.
+//
+// See http://www.boost.org/libs/smart_ptr/scoped_ptr.htm for documentation.
+//
+
+// scoped_ptr mimics a built-in pointer except that it guarantees deletion
+// of the object pointed to, either on destruction of the scoped_ptr or via
+// an explicit reset(). scoped_ptr is a simple solution for simple needs;
+// use shared_ptr or std::auto_ptr if your needs are more complex.
+
+// scoped_ptr_malloc added in by Google. When one of
+// these goes out of scope, instead of doing a delete or delete[], it
+// calls free(). scoped_ptr_malloc<char> is likely to see much more
+// use than any other specializations.
+
+// release() added in by Google. Use this to conditionally
+// transfer ownership of a heap-allocated object to the caller, usually on
+// method success.
+#ifndef TALK_BASE_SCOPED_PTR_H__
+#define TALK_BASE_SCOPED_PTR_H__
+
+#include <cstddef> // for std::ptrdiff_t
+#include <assert.h> // for assert
+#include <stdlib.h> // for free() decl
+
+#ifdef _WIN32
+namespace std { using ::ptrdiff_t; };
+#endif // _WIN32
+
+namespace talk_base {
+
+template <typename T>
+class scoped_ptr {
+ private:
+
+ T* ptr;
+
+ scoped_ptr(scoped_ptr const &);
+ scoped_ptr & operator=(scoped_ptr const &);
+
+ public:
+
+ typedef T element_type;
+
+ explicit scoped_ptr(T* p = 0): ptr(p) {}
+
+ ~scoped_ptr() {
+ typedef char type_must_be_complete[sizeof(T)];
+ delete ptr;
+ }
+
+ void reset(T* p = 0) {
+ typedef char type_must_be_complete[sizeof(T)];
+
+ if (ptr != p) {
+ delete ptr;
+ ptr = p;
+ }
+ }
+
+ T& operator*() const {
+ assert(ptr != 0);
+ return *ptr;
+ }
+
+ T* operator->() const {
+ assert(ptr != 0);
+ return ptr;
+ }
+
+ T* get() const {
+ return ptr;
+ }
+
+ void swap(scoped_ptr & b) {
+ T* tmp = b.ptr;
+ b.ptr = ptr;
+ ptr = tmp;
+ }
+
+ T* release() {
+ T* tmp = ptr;
+ ptr = 0;
+ return tmp;
+ }
+
+ T** accept() {
+ if (ptr) {
+ delete ptr;
+ ptr = 0;
+ }
+ return &ptr;
+ }
+
+ T** use() {
+ return &ptr;
+ }
+};
+
+template<typename T> inline
+void swap(scoped_ptr<T>& a, scoped_ptr<T>& b) {
+ a.swap(b);
+}
+
+
+
+
+// scoped_array extends scoped_ptr to arrays. Deletion of the array pointed to
+// is guaranteed, either on destruction of the scoped_array or via an explicit
+// reset(). Use shared_array or std::vector if your needs are more complex.
+
+template<typename T>
+class scoped_array {
+ private:
+
+ T* ptr;
+
+ scoped_array(scoped_array const &);
+ scoped_array & operator=(scoped_array const &);
+
+ public:
+
+ typedef T element_type;
+
+ explicit scoped_array(T* p = 0) : ptr(p) {}
+
+ ~scoped_array() {
+ typedef char type_must_be_complete[sizeof(T)];
+ delete[] ptr;
+ }
+
+ void reset(T* p = 0) {
+ typedef char type_must_be_complete[sizeof(T)];
+
+ if (ptr != p) {
+ delete [] ptr;
+ ptr = p;
+ }
+ }
+
+ T& operator[](std::ptrdiff_t i) const {
+ assert(ptr != 0);
+ assert(i >= 0);
+ return ptr[i];
+ }
+
+ T* get() const {
+ return ptr;
+ }
+
+ void swap(scoped_array & b) {
+ T* tmp = b.ptr;
+ b.ptr = ptr;
+ ptr = tmp;
+ }
+
+ T* release() {
+ T* tmp = ptr;
+ ptr = 0;
+ return tmp;
+ }
+
+ T** accept() {
+ if (ptr) {
+ delete [] ptr;
+ ptr = 0;
+ }
+ return &ptr;
+ }
+};
+
+template<class T> inline
+void swap(scoped_array<T>& a, scoped_array<T>& b) {
+ a.swap(b);
+}
+
+// scoped_ptr_malloc<> is similar to scoped_ptr<>, but it accepts a
+// second template argument, the function used to free the object.
+
+template<typename T, void (*FF)(void*) = free> class scoped_ptr_malloc {
+ private:
+
+ T* ptr;
+
+ scoped_ptr_malloc(scoped_ptr_malloc const &);
+ scoped_ptr_malloc & operator=(scoped_ptr_malloc const &);
+
+ public:
+
+ typedef T element_type;
+
+ explicit scoped_ptr_malloc(T* p = 0): ptr(p) {}
+
+ ~scoped_ptr_malloc() {
+ typedef char type_must_be_complete[sizeof(T)];
+ FF(static_cast<void*>(ptr));
+ }
+
+ void reset(T* p = 0) {
+ typedef char type_must_be_complete[sizeof(T)];
+
+ if (ptr != p) {
+ FF(static_cast<void*>(ptr));
+ ptr = p;
+ }
+ }
+
+ T& operator*() const {
+ assert(ptr != 0);
+ return *ptr;
+ }
+
+ T* operator->() const {
+ assert(ptr != 0);
+ return ptr;
+ }
+
+ T* get() const {
+ return ptr;
+ }
+
+ void swap(scoped_ptr_malloc & b) {
+ T* tmp = b.ptr;
+ b.ptr = ptr;
+ ptr = tmp;
+ }
+
+ T* release() {
+ T* tmp = ptr;
+ ptr = 0;
+ return tmp;
+ }
+
+ T** accept() {
+ if (ptr) {
+ FF(static_cast<void*>(ptr));
+ ptr = 0;
+ }
+ return &ptr;
+ }
+};
+
+template<typename T, void (*FF)(void*)> inline
+void swap(scoped_ptr_malloc<T,FF>& a, scoped_ptr_malloc<T,FF>& b) {
+ a.swap(b);
+}
+
+} // namespace talk_base
+
+// TODO: get rid of this global using
+using talk_base::scoped_ptr;
+
+#endif // #ifndef TALK_BASE_SCOPED_PTR_H__
diff --git a/Plugins/jingle/libjingle/talk/base/sec_buffer.h b/Plugins/jingle/libjingle/talk/base/sec_buffer.h
new file mode 100644
index 0000000..585e27f
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/sec_buffer.h
@@ -0,0 +1,173 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+// @file Contains utility classes that make it easier to use SecBuffers
+
+#ifndef TALK_BASE_SEC_BUFFER_H__
+#define TALK_BASE_SEC_BUFFER_H__
+
+namespace talk_base {
+
+// A base class for CSecBuffer<T>. Contains
+// all implementation that does not require
+// template arguments.
+class CSecBufferBase : public SecBuffer {
+ public:
+ CSecBufferBase() {
+ Clear();
+ }
+
+ // Uses the SSPI to free a pointer, must be
+ // used for buffers returned from SSPI APIs.
+ static void FreeSSPI(void *ptr) {
+ if ( ptr ) {
+ SECURITY_STATUS status;
+ status = ::FreeContextBuffer(ptr);
+ ASSERT(SEC_E_OK == status); // "Freeing context buffer"
+ }
+ }
+
+ // Deletes a buffer with operator delete
+ static void FreeDelete(void *ptr) {
+ delete [] reinterpret_cast<char*>(ptr);
+ }
+
+ // A noop delete, for buffers over other
+ // people's memory
+ static void FreeNone(void *ptr) {
+ }
+
+ protected:
+ // Clears the buffer to EMPTY & NULL
+ void Clear() {
+ this->BufferType = SECBUFFER_EMPTY;
+ this->cbBuffer = 0;
+ this->pvBuffer = NULL;
+ }
+};
+
+// Wrapper class for SecBuffer to take care
+// of initialization and destruction.
+template <void (*pfnFreeBuffer)(void *ptr)>
+class CSecBuffer: public CSecBufferBase {
+ public:
+ // Initializes buffer to empty & NULL
+ CSecBuffer() {
+ }
+
+ // Frees any allocated memory
+ ~CSecBuffer() {
+ Release();
+ }
+
+ // Frees the buffer appropriately, and re-nulls
+ void Release() {
+ pfnFreeBuffer(this->pvBuffer);
+ Clear();
+ }
+
+ private:
+ // A placeholder function for compile-time asserts on the class
+ void CompileAsserts() {
+ // never invoked...
+ assert(false); // _T("Notreached")
+
+ // This class must not extend the size of SecBuffer, since
+ // we use arrays of CSecBuffer in CSecBufferBundle below
+ cassert(sizeof(CSecBuffer<SSPIFree> == sizeof(SecBuffer)));
+ }
+};
+
+// Contains all generic implementation for the
+// SecBufferBundle class
+class SecBufferBundleBase {
+ public:
+};
+
+// A template class that bundles a SecBufferDesc with
+// one or more SecBuffers for convenience. Can take
+// care of deallocating buffers appropriately, as indicated
+// by pfnFreeBuffer function.
+// By default does no deallocation.
+template <int num_buffers,
+ void (*pfnFreeBuffer)(void *ptr) = CSecBufferBase::FreeNone>
+class CSecBufferBundle : public SecBufferBundleBase {
+ public:
+ // Constructs a security buffer bundle with num_buffers
+ // buffers, all of which are empty and nulled.
+ CSecBufferBundle() {
+ desc_.ulVersion = SECBUFFER_VERSION;
+ desc_.cBuffers = num_buffers;
+ desc_.pBuffers = buffers_;
+ }
+
+ // Frees all currently used buffers.
+ ~CSecBufferBundle() {
+ Release();
+ }
+
+ // Accessor for the descriptor
+ PSecBufferDesc desc() {
+ return &desc_;
+ }
+
+ // Accessor for the descriptor
+ const PSecBufferDesc desc() const {
+ return &desc_;
+ }
+
+ // returns the i-th security buffer
+ SecBuffer &operator[] (size_t num) {
+ ASSERT(num < num_buffers); // "Buffer index out of bounds"
+ return buffers_[num];
+ }
+
+ // returns the i-th security buffer
+ const SecBuffer &operator[] (size_t num) const {
+ ASSERT(num < num_buffers); // "Buffer index out of bounds"
+ return buffers_[num];
+ }
+
+ // Frees all non-NULL security buffers,
+ // using the deallocation function
+ void Release() {
+ for ( size_t i = 0; i < num_buffers; ++i ) {
+ buffers_[i].Release();
+ }
+ }
+
+ private:
+ // Our descriptor
+ SecBufferDesc desc_;
+ // Our bundled buffers, each takes care of its own
+ // initialization and destruction
+ CSecBuffer<pfnFreeBuffer> buffers_[num_buffers];
+};
+
+} // namespace talk_base
+
+#endif // TALK_BASE_SEC_BUFFER_H__
diff --git a/Plugins/jingle/libjingle/talk/base/signalthread.cc b/Plugins/jingle/libjingle/talk/base/signalthread.cc
new file mode 100644
index 0000000..919d1c3
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/signalthread.cc
@@ -0,0 +1,93 @@
+#include "talk/base/common.h"
+#include "talk/base/signalthread.h"
+
+using namespace talk_base;
+
+///////////////////////////////////////////////////////////////////////////////
+// SignalThread
+///////////////////////////////////////////////////////////////////////////////
+
+SignalThread::SignalThread()
+: main_(Thread::Current()), state_(kInit)
+{
+ worker_.parent_ = this;
+}
+
+SignalThread::~SignalThread() {
+}
+
+void SignalThread::SetPriority(ThreadPriority priority) {
+ ASSERT(main_->IsCurrent());
+ ASSERT(kInit == state_);
+ worker_.SetPriority(priority);
+}
+
+void SignalThread::Start() {
+ ASSERT(main_->IsCurrent());
+ if (kInit == state_) {
+ state_ = kRunning;
+ OnWorkStart();
+ worker_.Start();
+ } else {
+ ASSERT(false);
+ }
+}
+
+void SignalThread::Destroy() {
+ ASSERT(main_->IsCurrent());
+ if ((kInit == state_) || (kComplete == state_)) {
+ delete this;
+ } else if (kRunning == state_) {
+ state_ = kStopping;
+ // A couple tricky issues here:
+ // 1) Thread::Stop() calls Join(), which we don't want... we just want
+ // to stop the MessageQueue, which causes ContinueWork() to return false.
+ // 2) OnWorkStop() must follow Stop(), so that when the thread wakes up
+ // due to OWS(), ContinueWork() will return false.
+ worker_.MessageQueue::Stop();
+ OnWorkStop();
+ } else {
+ ASSERT(false);
+ }
+}
+
+void SignalThread::Release() {
+ ASSERT(main_->IsCurrent());
+ if (kComplete == state_) {
+ delete this;
+ } else if (kRunning == state_) {
+ state_ = kReleasing;
+ } else {
+ // if (kInit == state_) use Destroy()
+ ASSERT(false);
+ }
+}
+
+bool SignalThread::ContinueWork() {
+ ASSERT(worker_.IsCurrent());
+ return worker_.ProcessMessages(0);
+}
+
+void SignalThread::OnMessage(Message *msg) {
+ if (ST_MSG_WORKER_DONE == msg->message_id) {
+ ASSERT(main_->IsCurrent());
+ OnWorkDone();
+ bool do_delete = false;
+ if (kRunning == state_) {
+ state_ = kComplete;
+ } else {
+ do_delete = true;
+ }
+ if (kStopping != state_) {
+ SignalWorkDone(this);
+ }
+ if (do_delete) {
+ delete this;
+ }
+ }
+}
+
+void SignalThread::Run() {
+ DoWork();
+ main_->Post(this, ST_MSG_WORKER_DONE);
+}
diff --git a/Plugins/jingle/libjingle/talk/base/signalthread.h b/Plugins/jingle/libjingle/talk/base/signalthread.h
new file mode 100644
index 0000000..9e6bd05
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/signalthread.h
@@ -0,0 +1,91 @@
+#ifndef _SIGNALTHREAD_H_
+#define _SIGNALTHREAD_H_
+
+#include "talk/base/thread.h"
+#include "talk/base/sigslot.h"
+
+namespace talk_base {
+
+///////////////////////////////////////////////////////////////////////////////
+// SignalThread - Base class for worker threads. The main thread should call
+// Start() to begin work, and then follow one of these models:
+// Normal: Wait for SignalWorkDone, and then call Release to destroy.
+// Cancellation: Call Release(true), to abort the worker thread.
+// Fire-and-forget: Call Release(false), which allows the thread to run to
+// completion, and then self-destruct without further notification.
+// The subclass should override DoWork() to perform the background task. By
+// periodically calling ContinueWork(), it can check for cancellation.
+// OnWorkStart and OnWorkDone can be overridden to do pre- or post-work
+// tasks in the context of the main thread.
+///////////////////////////////////////////////////////////////////////////////
+
+class SignalThread : protected MessageHandler {
+public:
+ SignalThread();
+
+ // Context: Main Thread. Call before Start to change the worker's priority.
+ void SetPriority(ThreadPriority priority);
+
+ // Context: Main Thread. Call to begin the worker thread.
+ void Start();
+
+ // Context: Main Thread. If the worker thread is not running, deletes the
+ // object immediately. Otherwise, asks the worker thread to abort processing,
+ // and schedules the object to be deleted once the worker exits.
+ // SignalWorkDone will not be signalled.
+ void Destroy();
+
+ // Context: Main Thread. If the worker thread is complete, deletes the
+ // object immediately. Otherwise, schedules the object to be deleted once
+ // the worker thread completes. SignalWorkDone will be signalled.
+ void Release();
+
+ // Context: Main Thread. Signalled when work is complete.
+ sigslot::signal1<SignalThread *> SignalWorkDone;
+
+ enum { ST_MSG_WORKER_DONE, ST_MSG_FIRST_AVAILABLE };
+
+protected:
+ virtual ~SignalThread();
+
+ // Context: Main Thread. Subclass should override to do pre-work setup.
+ virtual void OnWorkStart() { }
+
+ // Context: Worker Thread. Subclass should override to do work.
+ virtual void DoWork() = 0;
+
+ // Context: Worker Thread. Subclass should call periodically to
+ // dispatch messages and determine if the thread should terminate.
+ bool ContinueWork();
+
+ // Context: Worker Thread. Subclass should override when extra work is
+ // needed to abort the worker thread.
+ virtual void OnWorkStop() { }
+
+ // Context: Main Thread. Subclass should override to do post-work cleanup.
+ virtual void OnWorkDone() { }
+
+ // Context: Any Thread. If subclass overrides, be sure to call the base
+ // implementation. Do not use (message_id < ST_MSG_FIRST_AVAILABLE)
+ virtual void OnMessage(Message *msg);
+
+private:
+ friend class Worker;
+ class Worker : public Thread {
+ public:
+ SignalThread* parent_;
+ virtual void Run() { parent_->Run(); }
+ };
+
+ void Run();
+
+ Thread* main_;
+ Worker worker_;
+ enum State { kInit, kRunning, kComplete, kStopping, kReleasing } state_;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+} // namespace talk_base
+
+#endif // _SIGNALTHREAD_H_
diff --git a/Plugins/jingle/libjingle/talk/base/sigslot.h b/Plugins/jingle/libjingle/talk/base/sigslot.h
new file mode 100644
index 0000000..539db8a
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/sigslot.h
@@ -0,0 +1,2699 @@
+// sigslot.h: Signal/Slot classes
+//
+// Written by Sarah Thompson (sarah@telergy.com) 2002.
+//
+// License: Public domain. You are free to use this code however you like, with the proviso that
+// the author takes on no responsibility or liability for any use.
+//
+// QUICK DOCUMENTATION
+//
+// (see also the full documentation at http://sigslot.sourceforge.net/)
+//
+// #define switches
+// SIGSLOT_PURE_ISO - Define this to force ISO C++ compliance. This also disables
+// all of the thread safety support on platforms where it is
+// available.
+//
+// SIGSLOT_USE_POSIX_THREADS - Force use of Posix threads when using a C++ compiler other than
+// gcc on a platform that supports Posix threads. (When using gcc,
+// this is the default - use SIGSLOT_PURE_ISO to disable this if
+// necessary)
+//
+// SIGSLOT_DEFAULT_MT_POLICY - Where thread support is enabled, this defaults to multi_threaded_global.
+// Otherwise, the default is single_threaded. #define this yourself to
+// override the default. In pure ISO mode, anything other than
+// single_threaded will cause a compiler error.
+//
+// PLATFORM NOTES
+//
+// Win32 - On Win32, the WIN32 symbol must be #defined. Most mainstream
+// compilers do this by default, but you may need to define it
+// yourself if your build environment is less standard. This causes
+// the Win32 thread support to be compiled in and used automatically.
+//
+// Unix/Linux/BSD, etc. - If you're using gcc, it is assumed that you have Posix threads
+// available, so they are used automatically. You can override this
+// (as under Windows) with the SIGSLOT_PURE_ISO switch. If you're using
+// something other than gcc but still want to use Posix threads, you
+// need to #define SIGSLOT_USE_POSIX_THREADS.
+//
+// ISO C++ - If none of the supported platforms are detected, or if
+// SIGSLOT_PURE_ISO is defined, all multithreading support is turned off,
+// along with any code that might cause a pure ISO C++ environment to
+// complain. Before you ask, gcc -ansi -pedantic won't compile this
+// library, but gcc -ansi is fine. Pedantic mode seems to throw a lot of
+// errors that aren't really there. If you feel like investigating this,
+// please contact the author.
+//
+//
+// THREADING MODES
+//
+// single_threaded - Your program is assumed to be single threaded from the point of view
+// of signal/slot usage (i.e. all objects using signals and slots are
+// created and destroyed from a single thread). Behaviour if objects are
+// destroyed concurrently is undefined (i.e. you'll get the occasional
+// segmentation fault/memory exception).
+//
+// multi_threaded_global - Your program is assumed to be multi threaded. Objects using signals and
+// slots can be safely created and destroyed from any thread, even when
+// connections exist. In multi_threaded_global mode, this is achieved by a
+// single global mutex (actually a critical section on Windows because they
+// are faster). This option uses less OS resources, but results in more
+// opportunities for contention, possibly resulting in more context switches
+// than are strictly necessary.
+//
+// multi_threaded_local - Behaviour in this mode is essentially the same as multi_threaded_global,
+// except that each signal, and each object that inherits has_slots, all
+// have their own mutex/critical section. In practice, this means that
+// mutex collisions (and hence context switches) only happen if they are
+// absolutely essential. However, on some platforms, creating a lot of
+// mutexes can slow down the whole OS, so use this option with care.
+//
+// USING THE LIBRARY
+//
+// See the full documentation at http://sigslot.sourceforge.net/
+//
+//
+
+#ifndef TALK_BASE_SIGSLOT_H__
+#define TALK_BASE_SIGSLOT_H__
+
+#include <set>
+#include <list>
+
+// On our copy of sigslot.h, we force single threading
+#define SIGSLOT_PURE_ISO
+
+#if defined(SIGSLOT_PURE_ISO) || (!defined(WIN32) && !defined(__GNUG__) && !defined(SIGSLOT_USE_POSIX_THREADS))
+# define _SIGSLOT_SINGLE_THREADED
+#elif defined(WIN32)
+# define _SIGSLOT_HAS_WIN32_THREADS
+# include <windows.h>
+#elif defined(__GNUG__) || defined(SIGSLOT_USE_POSIX_THREADS)
+# define _SIGSLOT_HAS_POSIX_THREADS
+# include <pthread.h>
+#else
+# define _SIGSLOT_SINGLE_THREADED
+#endif
+
+#ifndef SIGSLOT_DEFAULT_MT_POLICY
+# ifdef _SIGSLOT_SINGLE_THREADED
+# define SIGSLOT_DEFAULT_MT_POLICY single_threaded
+# else
+# define SIGSLOT_DEFAULT_MT_POLICY multi_threaded_local
+# endif
+#endif
+
+// TODO: change this namespace to talk_base?
+namespace sigslot {
+
+ class single_threaded
+ {
+ public:
+ single_threaded()
+ {
+ ;
+ }
+
+ virtual ~single_threaded()
+ {
+ ;
+ }
+
+ virtual void lock()
+ {
+ ;
+ }
+
+ virtual void unlock()
+ {
+ ;
+ }
+ };
+
+#ifdef _SIGSLOT_HAS_WIN32_THREADS
+ // The multi threading policies only get compiled in if they are enabled.
+ class multi_threaded_global
+ {
+ public:
+ multi_threaded_global()
+ {
+ static bool isinitialised = false;
+
+ if(!isinitialised)
+ {
+ InitializeCriticalSection(get_critsec());
+ isinitialised = true;
+ }
+ }
+
+ multi_threaded_global(const multi_threaded_global&)
+ {
+ ;
+ }
+
+ virtual ~multi_threaded_global()
+ {
+ ;
+ }
+
+ virtual void lock()
+ {
+ EnterCriticalSection(get_critsec());
+ }
+
+ virtual void unlock()
+ {
+ LeaveCriticalSection(get_critsec());
+ }
+
+ private:
+ CRITICAL_SECTION* get_critsec()
+ {
+ static CRITICAL_SECTION g_critsec;
+ return &g_critsec;
+ }
+ };
+
+ class multi_threaded_local
+ {
+ public:
+ multi_threaded_local()
+ {
+ InitializeCriticalSection(&m_critsec);
+ }
+
+ multi_threaded_local(const multi_threaded_local&)
+ {
+ InitializeCriticalSection(&m_critsec);
+ }
+
+ virtual ~multi_threaded_local()
+ {
+ DeleteCriticalSection(&m_critsec);
+ }
+
+ virtual void lock()
+ {
+ EnterCriticalSection(&m_critsec);
+ }
+
+ virtual void unlock()
+ {
+ LeaveCriticalSection(&m_critsec);
+ }
+
+ private:
+ CRITICAL_SECTION m_critsec;
+ };
+#endif // _SIGSLOT_HAS_WIN32_THREADS
+
+#ifdef _SIGSLOT_HAS_POSIX_THREADS
+ // The multi threading policies only get compiled in if they are enabled.
+ class multi_threaded_global
+ {
+ public:
+ multi_threaded_global()
+ {
+ pthread_mutex_init(get_mutex(), NULL);
+ }
+
+ multi_threaded_global(const multi_threaded_global&)
+ {
+ ;
+ }
+
+ virtual ~multi_threaded_global()
+ {
+ ;
+ }
+
+ virtual void lock()
+ {
+ pthread_mutex_lock(get_mutex());
+ }
+
+ virtual void unlock()
+ {
+ pthread_mutex_unlock(get_mutex());
+ }
+
+ private:
+ pthread_mutex_t* get_mutex()
+ {
+ static pthread_mutex_t g_mutex;
+ return &g_mutex;
+ }
+ };
+
+ class multi_threaded_local
+ {
+ public:
+ multi_threaded_local()
+ {
+ pthread_mutex_init(&m_mutex, NULL);
+ }
+
+ multi_threaded_local(const multi_threaded_local&)
+ {
+ pthread_mutex_init(&m_mutex, NULL);
+ }
+
+ virtual ~multi_threaded_local()
+ {
+ pthread_mutex_destroy(&m_mutex);
+ }
+
+ virtual void lock()
+ {
+ pthread_mutex_lock(&m_mutex);
+ }
+
+ virtual void unlock()
+ {
+ pthread_mutex_unlock(&m_mutex);
+ }
+
+ private:
+ pthread_mutex_t m_mutex;
+ };
+#endif // _SIGSLOT_HAS_POSIX_THREADS
+
+ template<class mt_policy>
+ class lock_block
+ {
+ public:
+ mt_policy *m_mutex;
+
+ lock_block(mt_policy *mtx)
+ : m_mutex(mtx)
+ {
+ m_mutex->lock();
+ }
+
+ ~lock_block()
+ {
+ m_mutex->unlock();
+ }
+ };
+
+ template<class mt_policy>
+ class has_slots;
+
+ template<class mt_policy>
+ class _connection_base0
+ {
+ public:
+ virtual has_slots<mt_policy>* getdest() const = 0;
+ virtual void emit() = 0;
+ virtual _connection_base0* clone() = 0;
+ virtual _connection_base0* duplicate(has_slots<mt_policy>* pnewdest) = 0;
+ };
+
+ template<class arg1_type, class mt_policy>
+ class _connection_base1
+ {
+ public:
+ virtual has_slots<mt_policy>* getdest() const = 0;
+ virtual void emit(arg1_type) = 0;
+ virtual _connection_base1<arg1_type, mt_policy>* clone() = 0;
+ virtual _connection_base1<arg1_type, mt_policy>* duplicate(has_slots<mt_policy>* pnewdest) = 0;
+ };
+
+ template<class arg1_type, class arg2_type, class mt_policy>
+ class _connection_base2
+ {
+ public:
+ virtual has_slots<mt_policy>* getdest() const = 0;
+ virtual void emit(arg1_type, arg2_type) = 0;
+ virtual _connection_base2<arg1_type, arg2_type, mt_policy>* clone() = 0;
+ virtual _connection_base2<arg1_type, arg2_type, mt_policy>* duplicate(has_slots<mt_policy>* pnewdest) = 0;
+ };
+
+ template<class arg1_type, class arg2_type, class arg3_type, class mt_policy>
+ class _connection_base3
+ {
+ public:
+ virtual has_slots<mt_policy>* getdest() const = 0;
+ virtual void emit(arg1_type, arg2_type, arg3_type) = 0;
+ virtual _connection_base3<arg1_type, arg2_type, arg3_type, mt_policy>* clone() = 0;
+ virtual _connection_base3<arg1_type, arg2_type, arg3_type, mt_policy>* duplicate(has_slots<mt_policy>* pnewdest) = 0;
+ };
+
+ template<class arg1_type, class arg2_type, class arg3_type, class arg4_type, class mt_policy>
+ class _connection_base4
+ {
+ public:
+ virtual has_slots<mt_policy>* getdest() const = 0;
+ virtual void emit(arg1_type, arg2_type, arg3_type, arg4_type) = 0;
+ virtual _connection_base4<arg1_type, arg2_type, arg3_type, arg4_type, mt_policy>* clone() = 0;
+ virtual _connection_base4<arg1_type, arg2_type, arg3_type, arg4_type, mt_policy>* duplicate(has_slots<mt_policy>* pnewdest) = 0;
+ };
+
+ template<class arg1_type, class arg2_type, class arg3_type, class arg4_type,
+ class arg5_type, class mt_policy>
+ class _connection_base5
+ {
+ public:
+ virtual has_slots<mt_policy>* getdest() const = 0;
+ virtual void emit(arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type) = 0;
+ virtual _connection_base5<arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, mt_policy>* clone() = 0;
+ virtual _connection_base5<arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, mt_policy>* duplicate(has_slots<mt_policy>* pnewdest) = 0;
+ };
+
+ template<class arg1_type, class arg2_type, class arg3_type, class arg4_type,
+ class arg5_type, class arg6_type, class mt_policy>
+ class _connection_base6
+ {
+ public:
+ virtual has_slots<mt_policy>* getdest() const = 0;
+ virtual void emit(arg1_type, arg2_type, arg3_type, arg4_type, arg5_type,
+ arg6_type) = 0;
+ virtual _connection_base6<arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, arg6_type, mt_policy>* clone() = 0;
+ virtual _connection_base6<arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, arg6_type, mt_policy>* duplicate(has_slots<mt_policy>* pnewdest) = 0;
+ };
+
+ template<class arg1_type, class arg2_type, class arg3_type, class arg4_type,
+ class arg5_type, class arg6_type, class arg7_type, class mt_policy>
+ class _connection_base7
+ {
+ public:
+ virtual has_slots<mt_policy>* getdest() const = 0;
+ virtual void emit(arg1_type, arg2_type, arg3_type, arg4_type, arg5_type,
+ arg6_type, arg7_type) = 0;
+ virtual _connection_base7<arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, arg6_type, arg7_type, mt_policy>* clone() = 0;
+ virtual _connection_base7<arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, arg6_type, arg7_type, mt_policy>* duplicate(has_slots<mt_policy>* pnewdest) = 0;
+ };
+
+ template<class arg1_type, class arg2_type, class arg3_type, class arg4_type,
+ class arg5_type, class arg6_type, class arg7_type, class arg8_type, class mt_policy>
+ class _connection_base8
+ {
+ public:
+ virtual has_slots<mt_policy>* getdest() const = 0;
+ virtual void emit(arg1_type, arg2_type, arg3_type, arg4_type, arg5_type,
+ arg6_type, arg7_type, arg8_type) = 0;
+ virtual _connection_base8<arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, arg6_type, arg7_type, arg8_type, mt_policy>* clone() = 0;
+ virtual _connection_base8<arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, arg6_type, arg7_type, arg8_type, mt_policy>* duplicate(has_slots<mt_policy>* pnewdest) = 0;
+ };
+
+ template<class mt_policy>
+ class _signal_base : public mt_policy
+ {
+ public:
+ virtual void slot_disconnect(has_slots<mt_policy>* pslot) = 0;
+ virtual void slot_duplicate(const has_slots<mt_policy>* poldslot, has_slots<mt_policy>* pnewslot) = 0;
+ };
+
+ template<class mt_policy = SIGSLOT_DEFAULT_MT_POLICY>
+ class has_slots : public mt_policy
+ {
+ private:
+ typedef typename std::set<_signal_base<mt_policy> *> sender_set;
+ typedef typename sender_set::const_iterator const_iterator;
+
+ public:
+ has_slots()
+ {
+ ;
+ }
+
+ has_slots(const has_slots& hs)
+ : mt_policy(hs)
+ {
+ lock_block<mt_policy> lock(this);
+ const_iterator it = hs.m_senders.begin();
+ const_iterator itEnd = hs.m_senders.end();
+
+ while(it != itEnd)
+ {
+ (*it)->slot_duplicate(&hs, this);
+ m_senders.insert(*it);
+ ++it;
+ }
+ }
+
+ void signal_connect(_signal_base<mt_policy>* sender)
+ {
+ lock_block<mt_policy> lock(this);
+ m_senders.insert(sender);
+ }
+
+ void signal_disconnect(_signal_base<mt_policy>* sender)
+ {
+ lock_block<mt_policy> lock(this);
+ m_senders.erase(sender);
+ }
+
+ virtual ~has_slots()
+ {
+ disconnect_all();
+ }
+
+ void disconnect_all()
+ {
+ lock_block<mt_policy> lock(this);
+ const_iterator it = m_senders.begin();
+ const_iterator itEnd = m_senders.end();
+
+ while(it != itEnd)
+ {
+ (*it)->slot_disconnect(this);
+ ++it;
+ }
+
+ m_senders.erase(m_senders.begin(), m_senders.end());
+ }
+
+ private:
+ sender_set m_senders;
+ };
+
+ template<class mt_policy>
+ class _signal_base0 : public _signal_base<mt_policy>
+ {
+ public:
+ typedef std::list<_connection_base0<mt_policy> *> connections_list;
+
+ _signal_base0()
+ {
+ ;
+ }
+
+ _signal_base0(const _signal_base0& s)
+ : _signal_base<mt_policy>(s)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator it = s.m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = s.m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ (*it)->getdest()->signal_connect(this);
+ m_connected_slots.push_back((*it)->clone());
+
+ ++it;
+ }
+ }
+
+ ~_signal_base0()
+ {
+ disconnect_all();
+ }
+
+ void disconnect_all()
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator it = m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ (*it)->getdest()->signal_disconnect(this);
+ delete *it;
+
+ ++it;
+ }
+
+ m_connected_slots.erase(m_connected_slots.begin(), m_connected_slots.end());
+ }
+
+#ifdef _DEBUG
+ bool connected(has_slots<mt_policy>* pclass)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator itNext, it = m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = m_connected_slots.end();
+ while(it != itEnd)
+ {
+ itNext = it;
+ ++itNext;
+ if ((*it)->getdest() == pclass)
+ return true;
+ it = itNext;
+ }
+ return false;
+ }
+#endif
+
+ void disconnect(has_slots<mt_policy>* pclass)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::iterator it = m_connected_slots.begin();
+ typename connections_list::iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ if((*it)->getdest() == pclass)
+ {
+ delete *it;
+ m_connected_slots.erase(it);
+ pclass->signal_disconnect(this);
+ return;
+ }
+
+ ++it;
+ }
+ }
+
+ void slot_disconnect(has_slots<mt_policy>* pslot)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::iterator it = m_connected_slots.begin();
+ typename connections_list::iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ typename connections_list::iterator itNext = it;
+ ++itNext;
+
+ if((*it)->getdest() == pslot)
+ {
+ m_connected_slots.erase(it);
+ // delete *it;
+ }
+
+ it = itNext;
+ }
+ }
+
+ void slot_duplicate(const has_slots<mt_policy>* oldtarget, has_slots<mt_policy>* newtarget)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::iterator it = m_connected_slots.begin();
+ typename connections_list::iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ if((*it)->getdest() == oldtarget)
+ {
+ m_connected_slots.push_back((*it)->duplicate(newtarget));
+ }
+
+ ++it;
+ }
+ }
+
+ protected:
+ connections_list m_connected_slots;
+ };
+
+ template<class arg1_type, class mt_policy>
+ class _signal_base1 : public _signal_base<mt_policy>
+ {
+ public:
+ typedef std::list<_connection_base1<arg1_type, mt_policy> *> connections_list;
+
+ _signal_base1()
+ {
+ ;
+ }
+
+ _signal_base1(const _signal_base1<arg1_type, mt_policy>& s)
+ : _signal_base<mt_policy>(s)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator it = s.m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = s.m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ (*it)->getdest()->signal_connect(this);
+ m_connected_slots.push_back((*it)->clone());
+
+ ++it;
+ }
+ }
+
+ void slot_duplicate(const has_slots<mt_policy>* oldtarget, has_slots<mt_policy>* newtarget)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::iterator it = m_connected_slots.begin();
+ typename connections_list::iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ if((*it)->getdest() == oldtarget)
+ {
+ m_connected_slots.push_back((*it)->duplicate(newtarget));
+ }
+
+ ++it;
+ }
+ }
+
+ ~_signal_base1()
+ {
+ disconnect_all();
+ }
+
+ void disconnect_all()
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator it = m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ (*it)->getdest()->signal_disconnect(this);
+ delete *it;
+
+ ++it;
+ }
+
+ m_connected_slots.erase(m_connected_slots.begin(), m_connected_slots.end());
+ }
+
+#ifdef _DEBUG
+ bool connected(has_slots<mt_policy>* pclass)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator itNext, it = m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = m_connected_slots.end();
+ while(it != itEnd)
+ {
+ itNext = it;
+ ++itNext;
+ if ((*it)->getdest() == pclass)
+ return true;
+ it = itNext;
+ }
+ return false;
+ }
+#endif
+
+ void disconnect(has_slots<mt_policy>* pclass)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::iterator it = m_connected_slots.begin();
+ typename connections_list::iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ if((*it)->getdest() == pclass)
+ {
+ delete *it;
+ m_connected_slots.erase(it);
+ pclass->signal_disconnect(this);
+ return;
+ }
+
+ ++it;
+ }
+ }
+
+ void slot_disconnect(has_slots<mt_policy>* pslot)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::iterator it = m_connected_slots.begin();
+ typename connections_list::iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ typename connections_list::iterator itNext = it;
+ ++itNext;
+
+ if((*it)->getdest() == pslot)
+ {
+ m_connected_slots.erase(it);
+ // delete *it;
+ }
+
+ it = itNext;
+ }
+ }
+
+
+ protected:
+ connections_list m_connected_slots;
+ };
+
+ template<class arg1_type, class arg2_type, class mt_policy>
+ class _signal_base2 : public _signal_base<mt_policy>
+ {
+ public:
+ typedef std::list<_connection_base2<arg1_type, arg2_type, mt_policy> *>
+ connections_list;
+
+ _signal_base2()
+ {
+ ;
+ }
+
+ _signal_base2(const _signal_base2<arg1_type, arg2_type, mt_policy>& s)
+ : _signal_base<mt_policy>(s)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator it = s.m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = s.m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ (*it)->getdest()->signal_connect(this);
+ m_connected_slots.push_back((*it)->clone());
+
+ ++it;
+ }
+ }
+
+ void slot_duplicate(const has_slots<mt_policy>* oldtarget, has_slots<mt_policy>* newtarget)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::iterator it = m_connected_slots.begin();
+ typename connections_list::iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ if((*it)->getdest() == oldtarget)
+ {
+ m_connected_slots.push_back((*it)->duplicate(newtarget));
+ }
+
+ ++it;
+ }
+ }
+
+ ~_signal_base2()
+ {
+ disconnect_all();
+ }
+
+ void disconnect_all()
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator it = m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ (*it)->getdest()->signal_disconnect(this);
+ delete *it;
+
+ ++it;
+ }
+
+ m_connected_slots.erase(m_connected_slots.begin(), m_connected_slots.end());
+ }
+
+#ifdef _DEBUG
+ bool connected(has_slots<mt_policy>* pclass)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator itNext, it = m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = m_connected_slots.end();
+ while(it != itEnd)
+ {
+ itNext = it;
+ ++itNext;
+ if ((*it)->getdest() == pclass)
+ return true;
+ it = itNext;
+ }
+ return false;
+ }
+#endif
+
+ void disconnect(has_slots<mt_policy>* pclass)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::iterator it = m_connected_slots.begin();
+ typename connections_list::iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ if((*it)->getdest() == pclass)
+ {
+ delete *it;
+ m_connected_slots.erase(it);
+ pclass->signal_disconnect(this);
+ return;
+ }
+
+ ++it;
+ }
+ }
+
+ void slot_disconnect(has_slots<mt_policy>* pslot)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::iterator it = m_connected_slots.begin();
+ typename connections_list::iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ typename connections_list::iterator itNext = it;
+ ++itNext;
+
+ if((*it)->getdest() == pslot)
+ {
+ m_connected_slots.erase(it);
+ // delete *it;
+ }
+
+ it = itNext;
+ }
+ }
+
+ protected:
+ connections_list m_connected_slots;
+ };
+
+ template<class arg1_type, class arg2_type, class arg3_type, class mt_policy>
+ class _signal_base3 : public _signal_base<mt_policy>
+ {
+ public:
+ typedef std::list<_connection_base3<arg1_type, arg2_type, arg3_type, mt_policy> *>
+ connections_list;
+
+ _signal_base3()
+ {
+ ;
+ }
+
+ _signal_base3(const _signal_base3<arg1_type, arg2_type, arg3_type, mt_policy>& s)
+ : _signal_base<mt_policy>(s)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator it = s.m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = s.m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ (*it)->getdest()->signal_connect(this);
+ m_connected_slots.push_back((*it)->clone());
+
+ ++it;
+ }
+ }
+
+ void slot_duplicate(const has_slots<mt_policy>* oldtarget, has_slots<mt_policy>* newtarget)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::iterator it = m_connected_slots.begin();
+ typename connections_list::iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ if((*it)->getdest() == oldtarget)
+ {
+ m_connected_slots.push_back((*it)->duplicate(newtarget));
+ }
+
+ ++it;
+ }
+ }
+
+ ~_signal_base3()
+ {
+ disconnect_all();
+ }
+
+ void disconnect_all()
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator it = m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ (*it)->getdest()->signal_disconnect(this);
+ delete *it;
+
+ ++it;
+ }
+
+ m_connected_slots.erase(m_connected_slots.begin(), m_connected_slots.end());
+ }
+
+#ifdef _DEBUG
+ bool connected(has_slots<mt_policy>* pclass)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator itNext, it = m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = m_connected_slots.end();
+ while(it != itEnd)
+ {
+ itNext = it;
+ ++itNext;
+ if ((*it)->getdest() == pclass)
+ return true;
+ it = itNext;
+ }
+ return false;
+ }
+#endif
+
+ void disconnect(has_slots<mt_policy>* pclass)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::iterator it = m_connected_slots.begin();
+ typename connections_list::iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ if((*it)->getdest() == pclass)
+ {
+ delete *it;
+ m_connected_slots.erase(it);
+ pclass->signal_disconnect(this);
+ return;
+ }
+
+ ++it;
+ }
+ }
+
+ void slot_disconnect(has_slots<mt_policy>* pslot)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::iterator it = m_connected_slots.begin();
+ typename connections_list::iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ typename connections_list::iterator itNext = it;
+ ++itNext;
+
+ if((*it)->getdest() == pslot)
+ {
+ m_connected_slots.erase(it);
+ // delete *it;
+ }
+
+ it = itNext;
+ }
+ }
+
+ protected:
+ connections_list m_connected_slots;
+ };
+
+ template<class arg1_type, class arg2_type, class arg3_type, class arg4_type, class mt_policy>
+ class _signal_base4 : public _signal_base<mt_policy>
+ {
+ public:
+ typedef std::list<_connection_base4<arg1_type, arg2_type, arg3_type,
+ arg4_type, mt_policy> *> connections_list;
+
+ _signal_base4()
+ {
+ ;
+ }
+
+ _signal_base4(const _signal_base4<arg1_type, arg2_type, arg3_type, arg4_type, mt_policy>& s)
+ : _signal_base<mt_policy>(s)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator it = s.m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = s.m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ (*it)->getdest()->signal_connect(this);
+ m_connected_slots.push_back((*it)->clone());
+
+ ++it;
+ }
+ }
+
+ void slot_duplicate(const has_slots<mt_policy>* oldtarget, has_slots<mt_policy>* newtarget)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::iterator it = m_connected_slots.begin();
+ typename connections_list::iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ if((*it)->getdest() == oldtarget)
+ {
+ m_connected_slots.push_back((*it)->duplicate(newtarget));
+ }
+
+ ++it;
+ }
+ }
+
+ ~_signal_base4()
+ {
+ disconnect_all();
+ }
+
+ void disconnect_all()
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator it = m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ (*it)->getdest()->signal_disconnect(this);
+ delete *it;
+
+ ++it;
+ }
+
+ m_connected_slots.erase(m_connected_slots.begin(), m_connected_slots.end());
+ }
+
+#ifdef _DEBUG
+ bool connected(has_slots<mt_policy>* pclass)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator itNext, it = m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = m_connected_slots.end();
+ while(it != itEnd)
+ {
+ itNext = it;
+ ++itNext;
+ if ((*it)->getdest() == pclass)
+ return true;
+ it = itNext;
+ }
+ return false;
+ }
+#endif
+
+ void disconnect(has_slots<mt_policy>* pclass)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::iterator it = m_connected_slots.begin();
+ typename connections_list::iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ if((*it)->getdest() == pclass)
+ {
+ delete *it;
+ m_connected_slots.erase(it);
+ pclass->signal_disconnect(this);
+ return;
+ }
+
+ ++it;
+ }
+ }
+
+ void slot_disconnect(has_slots<mt_policy>* pslot)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::iterator it = m_connected_slots.begin();
+ typename connections_list::iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ typename connections_list::iterator itNext = it;
+ ++itNext;
+
+ if((*it)->getdest() == pslot)
+ {
+ m_connected_slots.erase(it);
+ // delete *it;
+ }
+
+ it = itNext;
+ }
+ }
+
+ protected:
+ connections_list m_connected_slots;
+ };
+
+ template<class arg1_type, class arg2_type, class arg3_type, class arg4_type,
+ class arg5_type, class mt_policy>
+ class _signal_base5 : public _signal_base<mt_policy>
+ {
+ public:
+ typedef std::list<_connection_base5<arg1_type, arg2_type, arg3_type,
+ arg4_type, arg5_type, mt_policy> *> connections_list;
+
+ _signal_base5()
+ {
+ ;
+ }
+
+ _signal_base5(const _signal_base5<arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, mt_policy>& s)
+ : _signal_base<mt_policy>(s)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator it = s.m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = s.m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ (*it)->getdest()->signal_connect(this);
+ m_connected_slots.push_back((*it)->clone());
+
+ ++it;
+ }
+ }
+
+ void slot_duplicate(const has_slots<mt_policy>* oldtarget, has_slots<mt_policy>* newtarget)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::iterator it = m_connected_slots.begin();
+ typename connections_list::iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ if((*it)->getdest() == oldtarget)
+ {
+ m_connected_slots.push_back((*it)->duplicate(newtarget));
+ }
+
+ ++it;
+ }
+ }
+
+ ~_signal_base5()
+ {
+ disconnect_all();
+ }
+
+ void disconnect_all()
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator it = m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ (*it)->getdest()->signal_disconnect(this);
+ delete *it;
+
+ ++it;
+ }
+
+ m_connected_slots.erase(m_connected_slots.begin(), m_connected_slots.end());
+ }
+
+#ifdef _DEBUG
+ bool connected(has_slots<mt_policy>* pclass)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator itNext, it = m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = m_connected_slots.end();
+ while(it != itEnd)
+ {
+ itNext = it;
+ ++itNext;
+ if ((*it)->getdest() == pclass)
+ return true;
+ it = itNext;
+ }
+ return false;
+ }
+#endif
+
+ void disconnect(has_slots<mt_policy>* pclass)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::iterator it = m_connected_slots.begin();
+ typename connections_list::iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ if((*it)->getdest() == pclass)
+ {
+ delete *it;
+ m_connected_slots.erase(it);
+ pclass->signal_disconnect(this);
+ return;
+ }
+
+ ++it;
+ }
+ }
+
+ void slot_disconnect(has_slots<mt_policy>* pslot)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::iterator it = m_connected_slots.begin();
+ typename connections_list::iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ typename connections_list::iterator itNext = it;
+ ++itNext;
+
+ if((*it)->getdest() == pslot)
+ {
+ m_connected_slots.erase(it);
+ // delete *it;
+ }
+
+ it = itNext;
+ }
+ }
+
+ protected:
+ connections_list m_connected_slots;
+ };
+
+ template<class arg1_type, class arg2_type, class arg3_type, class arg4_type,
+ class arg5_type, class arg6_type, class mt_policy>
+ class _signal_base6 : public _signal_base<mt_policy>
+ {
+ public:
+ typedef std::list<_connection_base6<arg1_type, arg2_type, arg3_type,
+ arg4_type, arg5_type, arg6_type, mt_policy> *> connections_list;
+
+ _signal_base6()
+ {
+ ;
+ }
+
+ _signal_base6(const _signal_base6<arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, arg6_type, mt_policy>& s)
+ : _signal_base<mt_policy>(s)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator it = s.m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = s.m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ (*it)->getdest()->signal_connect(this);
+ m_connected_slots.push_back((*it)->clone());
+
+ ++it;
+ }
+ }
+
+ void slot_duplicate(const has_slots<mt_policy>* oldtarget, has_slots<mt_policy>* newtarget)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::iterator it = m_connected_slots.begin();
+ typename connections_list::iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ if((*it)->getdest() == oldtarget)
+ {
+ m_connected_slots.push_back((*it)->duplicate(newtarget));
+ }
+
+ ++it;
+ }
+ }
+
+ ~_signal_base6()
+ {
+ disconnect_all();
+ }
+
+ void disconnect_all()
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator it = m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ (*it)->getdest()->signal_disconnect(this);
+ delete *it;
+
+ ++it;
+ }
+
+ m_connected_slots.erase(m_connected_slots.begin(), m_connected_slots.end());
+ }
+
+#ifdef _DEBUG
+ bool connected(has_slots<mt_policy>* pclass)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator itNext, it = m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = m_connected_slots.end();
+ while(it != itEnd)
+ {
+ itNext = it;
+ ++itNext;
+ if ((*it)->getdest() == pclass)
+ return true;
+ it = itNext;
+ }
+ return false;
+ }
+#endif
+
+ void disconnect(has_slots<mt_policy>* pclass)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::iterator it = m_connected_slots.begin();
+ typename connections_list::iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ if((*it)->getdest() == pclass)
+ {
+ delete *it;
+ m_connected_slots.erase(it);
+ pclass->signal_disconnect(this);
+ return;
+ }
+
+ ++it;
+ }
+ }
+
+ void slot_disconnect(has_slots<mt_policy>* pslot)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::iterator it = m_connected_slots.begin();
+ typename connections_list::iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ typename connections_list::iterator itNext = it;
+ ++itNext;
+
+ if((*it)->getdest() == pslot)
+ {
+ m_connected_slots.erase(it);
+ // delete *it;
+ }
+
+ it = itNext;
+ }
+ }
+
+ protected:
+ connections_list m_connected_slots;
+ };
+
+ template<class arg1_type, class arg2_type, class arg3_type, class arg4_type,
+ class arg5_type, class arg6_type, class arg7_type, class mt_policy>
+ class _signal_base7 : public _signal_base<mt_policy>
+ {
+ public:
+ typedef std::list<_connection_base7<arg1_type, arg2_type, arg3_type,
+ arg4_type, arg5_type, arg6_type, arg7_type, mt_policy> *> connections_list;
+
+ _signal_base7()
+ {
+ ;
+ }
+
+ _signal_base7(const _signal_base7<arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, arg6_type, arg7_type, mt_policy>& s)
+ : _signal_base<mt_policy>(s)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator it = s.m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = s.m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ (*it)->getdest()->signal_connect(this);
+ m_connected_slots.push_back((*it)->clone());
+
+ ++it;
+ }
+ }
+
+ void slot_duplicate(const has_slots<mt_policy>* oldtarget, has_slots<mt_policy>* newtarget)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::iterator it = m_connected_slots.begin();
+ typename connections_list::iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ if((*it)->getdest() == oldtarget)
+ {
+ m_connected_slots.push_back((*it)->duplicate(newtarget));
+ }
+
+ ++it;
+ }
+ }
+
+ ~_signal_base7()
+ {
+ disconnect_all();
+ }
+
+ void disconnect_all()
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator it = m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ (*it)->getdest()->signal_disconnect(this);
+ delete *it;
+
+ ++it;
+ }
+
+ m_connected_slots.erase(m_connected_slots.begin(), m_connected_slots.end());
+ }
+
+#ifdef _DEBUG
+ bool connected(has_slots<mt_policy>* pclass)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator itNext, it = m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = m_connected_slots.end();
+ while(it != itEnd)
+ {
+ itNext = it;
+ ++itNext;
+ if ((*it)->getdest() == pclass)
+ return true;
+ it = itNext;
+ }
+ return false;
+ }
+#endif
+
+ void disconnect(has_slots<mt_policy>* pclass)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::iterator it = m_connected_slots.begin();
+ typename connections_list::iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ if((*it)->getdest() == pclass)
+ {
+ delete *it;
+ m_connected_slots.erase(it);
+ pclass->signal_disconnect(this);
+ return;
+ }
+
+ ++it;
+ }
+ }
+
+ void slot_disconnect(has_slots<mt_policy>* pslot)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::iterator it = m_connected_slots.begin();
+ typename connections_list::iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ typename connections_list::iterator itNext = it;
+ ++itNext;
+
+ if((*it)->getdest() == pslot)
+ {
+ m_connected_slots.erase(it);
+ // delete *it;
+ }
+
+ it = itNext;
+ }
+ }
+
+ protected:
+ connections_list m_connected_slots;
+ };
+
+ template<class arg1_type, class arg2_type, class arg3_type, class arg4_type,
+ class arg5_type, class arg6_type, class arg7_type, class arg8_type, class mt_policy>
+ class _signal_base8 : public _signal_base<mt_policy>
+ {
+ public:
+ typedef std::list<_connection_base8<arg1_type, arg2_type, arg3_type,
+ arg4_type, arg5_type, arg6_type, arg7_type, arg8_type, mt_policy> *>
+ connections_list;
+
+ _signal_base8()
+ {
+ ;
+ }
+
+ _signal_base8(const _signal_base8<arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, arg6_type, arg7_type, arg8_type, mt_policy>& s)
+ : _signal_base<mt_policy>(s)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator it = s.m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = s.m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ (*it)->getdest()->signal_connect(this);
+ m_connected_slots.push_back((*it)->clone());
+
+ ++it;
+ }
+ }
+
+ void slot_duplicate(const has_slots<mt_policy>* oldtarget, has_slots<mt_policy>* newtarget)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::iterator it = m_connected_slots.begin();
+ typename connections_list::iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ if((*it)->getdest() == oldtarget)
+ {
+ m_connected_slots.push_back((*it)->duplicate(newtarget));
+ }
+
+ ++it;
+ }
+ }
+
+ ~_signal_base8()
+ {
+ disconnect_all();
+ }
+
+ void disconnect_all()
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator it = m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ (*it)->getdest()->signal_disconnect(this);
+ delete *it;
+
+ ++it;
+ }
+
+ m_connected_slots.erase(m_connected_slots.begin(), m_connected_slots.end());
+ }
+
+#ifdef _DEBUG
+ bool connected(has_slots<mt_policy>* pclass)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator itNext, it = m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = m_connected_slots.end();
+ while(it != itEnd)
+ {
+ itNext = it;
+ ++itNext;
+ if ((*it)->getdest() == pclass)
+ return true;
+ it = itNext;
+ }
+ return false;
+ }
+#endif
+
+ void disconnect(has_slots<mt_policy>* pclass)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::iterator it = m_connected_slots.begin();
+ typename connections_list::iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ if((*it)->getdest() == pclass)
+ {
+ delete *it;
+ m_connected_slots.erase(it);
+ pclass->signal_disconnect(this);
+ return;
+ }
+
+ ++it;
+ }
+ }
+
+ void slot_disconnect(has_slots<mt_policy>* pslot)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::iterator it = m_connected_slots.begin();
+ typename connections_list::iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ typename connections_list::iterator itNext = it;
+ ++itNext;
+
+ if((*it)->getdest() == pslot)
+ {
+ m_connected_slots.erase(it);
+ // delete *it;
+ }
+
+ it = itNext;
+ }
+ }
+
+ protected:
+ connections_list m_connected_slots;
+ };
+
+
+ template<class dest_type, class mt_policy>
+ class _connection0 : public _connection_base0<mt_policy>
+ {
+ public:
+ _connection0()
+ {
+ m_pobject = NULL;
+ m_pmemfun = NULL;
+ }
+
+ _connection0(dest_type* pobject, void (dest_type::*pmemfun)())
+ {
+ m_pobject = pobject;
+ m_pmemfun = pmemfun;
+ }
+
+ virtual _connection_base0<mt_policy>* clone()
+ {
+ return new _connection0<dest_type, mt_policy>(*this);
+ }
+
+ virtual _connection_base0<mt_policy>* duplicate(has_slots<mt_policy>* pnewdest)
+ {
+ return new _connection0<dest_type, mt_policy>((dest_type *)pnewdest, m_pmemfun);
+ }
+
+ virtual void emit()
+ {
+ (m_pobject->*m_pmemfun)();
+ }
+
+ virtual has_slots<mt_policy>* getdest() const
+ {
+ return m_pobject;
+ }
+
+ private:
+ dest_type* m_pobject;
+ void (dest_type::* m_pmemfun)();
+ };
+
+ template<class dest_type, class arg1_type, class mt_policy>
+ class _connection1 : public _connection_base1<arg1_type, mt_policy>
+ {
+ public:
+ _connection1()
+ {
+ m_pobject = NULL;
+ m_pmemfun = NULL;
+ }
+
+ _connection1(dest_type* pobject, void (dest_type::*pmemfun)(arg1_type))
+ {
+ m_pobject = pobject;
+ m_pmemfun = pmemfun;
+ }
+
+ virtual _connection_base1<arg1_type, mt_policy>* clone()
+ {
+ return new _connection1<dest_type, arg1_type, mt_policy>(*this);
+ }
+
+ virtual _connection_base1<arg1_type, mt_policy>* duplicate(has_slots<mt_policy>* pnewdest)
+ {
+ return new _connection1<dest_type, arg1_type, mt_policy>((dest_type *)pnewdest, m_pmemfun);
+ }
+
+ virtual void emit(arg1_type a1)
+ {
+ (m_pobject->*m_pmemfun)(a1);
+ }
+
+ virtual has_slots<mt_policy>* getdest() const
+ {
+ return m_pobject;
+ }
+
+ private:
+ dest_type* m_pobject;
+ void (dest_type::* m_pmemfun)(arg1_type);
+ };
+
+ template<class dest_type, class arg1_type, class arg2_type, class mt_policy>
+ class _connection2 : public _connection_base2<arg1_type, arg2_type, mt_policy>
+ {
+ public:
+ _connection2()
+ {
+ m_pobject = NULL;
+ m_pmemfun = NULL;
+ }
+
+ _connection2(dest_type* pobject, void (dest_type::*pmemfun)(arg1_type,
+ arg2_type))
+ {
+ m_pobject = pobject;
+ m_pmemfun = pmemfun;
+ }
+
+ virtual _connection_base2<arg1_type, arg2_type, mt_policy>* clone()
+ {
+ return new _connection2<dest_type, arg1_type, arg2_type, mt_policy>(*this);
+ }
+
+ virtual _connection_base2<arg1_type, arg2_type, mt_policy>* duplicate(has_slots<mt_policy>* pnewdest)
+ {
+ return new _connection2<dest_type, arg1_type, arg2_type, mt_policy>((dest_type *)pnewdest, m_pmemfun);
+ }
+
+ virtual void emit(arg1_type a1, arg2_type a2)
+ {
+ (m_pobject->*m_pmemfun)(a1, a2);
+ }
+
+ virtual has_slots<mt_policy>* getdest() const
+ {
+ return m_pobject;
+ }
+
+ private:
+ dest_type* m_pobject;
+ void (dest_type::* m_pmemfun)(arg1_type, arg2_type);
+ };
+
+ template<class dest_type, class arg1_type, class arg2_type, class arg3_type, class mt_policy>
+ class _connection3 : public _connection_base3<arg1_type, arg2_type, arg3_type, mt_policy>
+ {
+ public:
+ _connection3()
+ {
+ m_pobject = NULL;
+ m_pmemfun = NULL;
+ }
+
+ _connection3(dest_type* pobject, void (dest_type::*pmemfun)(arg1_type,
+ arg2_type, arg3_type))
+ {
+ m_pobject = pobject;
+ m_pmemfun = pmemfun;
+ }
+
+ virtual _connection_base3<arg1_type, arg2_type, arg3_type, mt_policy>* clone()
+ {
+ return new _connection3<dest_type, arg1_type, arg2_type, arg3_type, mt_policy>(*this);
+ }
+
+ virtual _connection_base3<arg1_type, arg2_type, arg3_type, mt_policy>* duplicate(has_slots<mt_policy>* pnewdest)
+ {
+ return new _connection3<dest_type, arg1_type, arg2_type, arg3_type, mt_policy>((dest_type *)pnewdest, m_pmemfun);
+ }
+
+ virtual void emit(arg1_type a1, arg2_type a2, arg3_type a3)
+ {
+ (m_pobject->*m_pmemfun)(a1, a2, a3);
+ }
+
+ virtual has_slots<mt_policy>* getdest() const
+ {
+ return m_pobject;
+ }
+
+ private:
+ dest_type* m_pobject;
+ void (dest_type::* m_pmemfun)(arg1_type, arg2_type, arg3_type);
+ };
+
+ template<class dest_type, class arg1_type, class arg2_type, class arg3_type,
+ class arg4_type, class mt_policy>
+ class _connection4 : public _connection_base4<arg1_type, arg2_type,
+ arg3_type, arg4_type, mt_policy>
+ {
+ public:
+ _connection4()
+ {
+ m_pobject = NULL;
+ m_pmemfun = NULL;
+ }
+
+ _connection4(dest_type* pobject, void (dest_type::*pmemfun)(arg1_type,
+ arg2_type, arg3_type, arg4_type))
+ {
+ m_pobject = pobject;
+ m_pmemfun = pmemfun;
+ }
+
+ virtual _connection_base4<arg1_type, arg2_type, arg3_type, arg4_type, mt_policy>* clone()
+ {
+ return new _connection4<dest_type, arg1_type, arg2_type, arg3_type, arg4_type, mt_policy>(*this);
+ }
+
+ virtual _connection_base4<arg1_type, arg2_type, arg3_type, arg4_type, mt_policy>* duplicate(has_slots<mt_policy>* pnewdest)
+ {
+ return new _connection4<dest_type, arg1_type, arg2_type, arg3_type, arg4_type, mt_policy>((dest_type *)pnewdest, m_pmemfun);
+ }
+
+ virtual void emit(arg1_type a1, arg2_type a2, arg3_type a3,
+ arg4_type a4)
+ {
+ (m_pobject->*m_pmemfun)(a1, a2, a3, a4);
+ }
+
+ virtual has_slots<mt_policy>* getdest() const
+ {
+ return m_pobject;
+ }
+
+ private:
+ dest_type* m_pobject;
+ void (dest_type::* m_pmemfun)(arg1_type, arg2_type, arg3_type,
+ arg4_type);
+ };
+
+ template<class dest_type, class arg1_type, class arg2_type, class arg3_type,
+ class arg4_type, class arg5_type, class mt_policy>
+ class _connection5 : public _connection_base5<arg1_type, arg2_type,
+ arg3_type, arg4_type, arg5_type, mt_policy>
+ {
+ public:
+ _connection5()
+ {
+ m_pobject = NULL;
+ m_pmemfun = NULL;
+ }
+
+ _connection5(dest_type* pobject, void (dest_type::*pmemfun)(arg1_type,
+ arg2_type, arg3_type, arg4_type, arg5_type))
+ {
+ m_pobject = pobject;
+ m_pmemfun = pmemfun;
+ }
+
+ virtual _connection_base5<arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, mt_policy>* clone()
+ {
+ return new _connection5<dest_type, arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, mt_policy>(*this);
+ }
+
+ virtual _connection_base5<arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, mt_policy>* duplicate(has_slots<mt_policy>* pnewdest)
+ {
+ return new _connection5<dest_type, arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, mt_policy>((dest_type *)pnewdest, m_pmemfun);
+ }
+
+ virtual void emit(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4,
+ arg5_type a5)
+ {
+ (m_pobject->*m_pmemfun)(a1, a2, a3, a4, a5);
+ }
+
+ virtual has_slots<mt_policy>* getdest() const
+ {
+ return m_pobject;
+ }
+
+ private:
+ dest_type* m_pobject;
+ void (dest_type::* m_pmemfun)(arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type);
+ };
+
+ template<class dest_type, class arg1_type, class arg2_type, class arg3_type,
+ class arg4_type, class arg5_type, class arg6_type, class mt_policy>
+ class _connection6 : public _connection_base6<arg1_type, arg2_type,
+ arg3_type, arg4_type, arg5_type, arg6_type, mt_policy>
+ {
+ public:
+ _connection6()
+ {
+ m_pobject = NULL;
+ m_pmemfun = NULL;
+ }
+
+ _connection6(dest_type* pobject, void (dest_type::*pmemfun)(arg1_type,
+ arg2_type, arg3_type, arg4_type, arg5_type, arg6_type))
+ {
+ m_pobject = pobject;
+ m_pmemfun = pmemfun;
+ }
+
+ virtual _connection_base6<arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, arg6_type, mt_policy>* clone()
+ {
+ return new _connection6<dest_type, arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, arg6_type, mt_policy>(*this);
+ }
+
+ virtual _connection_base6<arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, arg6_type, mt_policy>* duplicate(has_slots<mt_policy>* pnewdest)
+ {
+ return new _connection6<dest_type, arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, arg6_type, mt_policy>((dest_type *)pnewdest, m_pmemfun);
+ }
+
+ virtual void emit(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4,
+ arg5_type a5, arg6_type a6)
+ {
+ (m_pobject->*m_pmemfun)(a1, a2, a3, a4, a5, a6);
+ }
+
+ virtual has_slots<mt_policy>* getdest() const
+ {
+ return m_pobject;
+ }
+
+ private:
+ dest_type* m_pobject;
+ void (dest_type::* m_pmemfun)(arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, arg6_type);
+ };
+
+ template<class dest_type, class arg1_type, class arg2_type, class arg3_type,
+ class arg4_type, class arg5_type, class arg6_type, class arg7_type, class mt_policy>
+ class _connection7 : public _connection_base7<arg1_type, arg2_type,
+ arg3_type, arg4_type, arg5_type, arg6_type, arg7_type, mt_policy>
+ {
+ public:
+ _connection7()
+ {
+ m_pobject = NULL;
+ m_pmemfun = NULL;
+ }
+
+ _connection7(dest_type* pobject, void (dest_type::*pmemfun)(arg1_type,
+ arg2_type, arg3_type, arg4_type, arg5_type, arg6_type, arg7_type))
+ {
+ m_pobject = pobject;
+ m_pmemfun = pmemfun;
+ }
+
+ virtual _connection_base7<arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, arg6_type, arg7_type, mt_policy>* clone()
+ {
+ return new _connection7<dest_type, arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, arg6_type, arg7_type, mt_policy>(*this);
+ }
+
+ virtual _connection_base7<arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, arg6_type, arg7_type, mt_policy>* duplicate(has_slots<mt_policy>* pnewdest)
+ {
+ return new _connection7<dest_type, arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, arg6_type, arg7_type, mt_policy>((dest_type *)pnewdest, m_pmemfun);
+ }
+
+ virtual void emit(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4,
+ arg5_type a5, arg6_type a6, arg7_type a7)
+ {
+ (m_pobject->*m_pmemfun)(a1, a2, a3, a4, a5, a6, a7);
+ }
+
+ virtual has_slots<mt_policy>* getdest() const
+ {
+ return m_pobject;
+ }
+
+ private:
+ dest_type* m_pobject;
+ void (dest_type::* m_pmemfun)(arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, arg6_type, arg7_type);
+ };
+
+ template<class dest_type, class arg1_type, class arg2_type, class arg3_type,
+ class arg4_type, class arg5_type, class arg6_type, class arg7_type,
+ class arg8_type, class mt_policy>
+ class _connection8 : public _connection_base8<arg1_type, arg2_type,
+ arg3_type, arg4_type, arg5_type, arg6_type, arg7_type, arg8_type, mt_policy>
+ {
+ public:
+ _connection8()
+ {
+ m_pobject = NULL;
+ m_pmemfun = NULL;
+ }
+
+ _connection8(dest_type* pobject, void (dest_type::*pmemfun)(arg1_type,
+ arg2_type, arg3_type, arg4_type, arg5_type, arg6_type,
+ arg7_type, arg8_type))
+ {
+ m_pobject = pobject;
+ m_pmemfun = pmemfun;
+ }
+
+ virtual _connection_base8<arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, arg6_type, arg7_type, arg8_type, mt_policy>* clone()
+ {
+ return new _connection8<dest_type, arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, arg6_type, arg7_type, arg8_type, mt_policy>(*this);
+ }
+
+ virtual _connection_base8<arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, arg6_type, arg7_type, arg8_type, mt_policy>* duplicate(has_slots<mt_policy>* pnewdest)
+ {
+ return new _connection8<dest_type, arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, arg6_type, arg7_type, arg8_type, mt_policy>((dest_type *)pnewdest, m_pmemfun);
+ }
+
+ virtual void emit(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4,
+ arg5_type a5, arg6_type a6, arg7_type a7, arg8_type a8)
+ {
+ (m_pobject->*m_pmemfun)(a1, a2, a3, a4, a5, a6, a7, a8);
+ }
+
+ virtual has_slots<mt_policy>* getdest() const
+ {
+ return m_pobject;
+ }
+
+ private:
+ dest_type* m_pobject;
+ void (dest_type::* m_pmemfun)(arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, arg6_type, arg7_type, arg8_type);
+ };
+
+ template<class mt_policy = SIGSLOT_DEFAULT_MT_POLICY>
+ class signal0 : public _signal_base0<mt_policy>
+ {
+ public:
+ typedef _signal_base0<mt_policy> base;
+ typedef typename base::connections_list connections_list;
+ using base::m_connected_slots;
+
+ signal0()
+ {
+ ;
+ }
+
+ signal0(const signal0<mt_policy>& s)
+ : _signal_base0<mt_policy>(s)
+ {
+ ;
+ }
+
+ template<class desttype>
+ void connect(desttype* pclass, void (desttype::*pmemfun)())
+ {
+ lock_block<mt_policy> lock(this);
+ _connection0<desttype, mt_policy>* conn =
+ new _connection0<desttype, mt_policy>(pclass, pmemfun);
+ m_connected_slots.push_back(conn);
+ pclass->signal_connect(this);
+ }
+
+ void emit()
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator itNext, it = m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ itNext = it;
+ ++itNext;
+
+ (*it)->emit();
+
+ it = itNext;
+ }
+ }
+
+ void operator()()
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator itNext, it = m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ itNext = it;
+ ++itNext;
+
+ (*it)->emit();
+
+ it = itNext;
+ }
+ }
+ };
+
+ template<class arg1_type, class mt_policy = SIGSLOT_DEFAULT_MT_POLICY>
+ class signal1 : public _signal_base1<arg1_type, mt_policy>
+ {
+ public:
+ typedef _signal_base1<arg1_type, mt_policy> base;
+ typedef typename base::connections_list connections_list;
+ using base::m_connected_slots;
+
+ signal1()
+ {
+ ;
+ }
+
+ signal1(const signal1<arg1_type, mt_policy>& s)
+ : _signal_base1<arg1_type, mt_policy>(s)
+ {
+ ;
+ }
+
+ template<class desttype>
+ void connect(desttype* pclass, void (desttype::*pmemfun)(arg1_type))
+ {
+ lock_block<mt_policy> lock(this);
+ _connection1<desttype, arg1_type, mt_policy>* conn =
+ new _connection1<desttype, arg1_type, mt_policy>(pclass, pmemfun);
+ m_connected_slots.push_back(conn);
+ pclass->signal_connect(this);
+ }
+
+ void emit(arg1_type a1)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator itNext, it = m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ itNext = it;
+ ++itNext;
+
+ (*it)->emit(a1);
+
+ it = itNext;
+ }
+ }
+
+ void operator()(arg1_type a1)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator itNext, it = m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ itNext = it;
+ ++itNext;
+
+ (*it)->emit(a1);
+
+ it = itNext;
+ }
+ }
+ };
+
+ template<class arg1_type, class arg2_type, class mt_policy = SIGSLOT_DEFAULT_MT_POLICY>
+ class signal2 : public _signal_base2<arg1_type, arg2_type, mt_policy>
+ {
+ public:
+ typedef _signal_base2<arg1_type, arg2_type, mt_policy> base;
+ typedef typename base::connections_list connections_list;
+ using base::m_connected_slots;
+
+ signal2()
+ {
+ ;
+ }
+
+ signal2(const signal2<arg1_type, arg2_type, mt_policy>& s)
+ : _signal_base2<arg1_type, arg2_type, mt_policy>(s)
+ {
+ ;
+ }
+
+ template<class desttype>
+ void connect(desttype* pclass, void (desttype::*pmemfun)(arg1_type,
+ arg2_type))
+ {
+ lock_block<mt_policy> lock(this);
+ _connection2<desttype, arg1_type, arg2_type, mt_policy>* conn = new
+ _connection2<desttype, arg1_type, arg2_type, mt_policy>(pclass, pmemfun);
+ m_connected_slots.push_back(conn);
+ pclass->signal_connect(this);
+ }
+
+ void emit(arg1_type a1, arg2_type a2)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator itNext, it = m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ itNext = it;
+ ++itNext;
+
+ (*it)->emit(a1, a2);
+
+ it = itNext;
+ }
+ }
+
+ void operator()(arg1_type a1, arg2_type a2)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator itNext, it = m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ itNext = it;
+ ++itNext;
+
+ (*it)->emit(a1, a2);
+
+ it = itNext;
+ }
+ }
+ };
+
+ template<class arg1_type, class arg2_type, class arg3_type, class mt_policy = SIGSLOT_DEFAULT_MT_POLICY>
+ class signal3 : public _signal_base3<arg1_type, arg2_type, arg3_type, mt_policy>
+ {
+ public:
+ typedef _signal_base3<arg1_type, arg2_type, arg3_type, mt_policy> base;
+ typedef typename base::connections_list connections_list;
+ using base::m_connected_slots;
+
+ signal3()
+ {
+ ;
+ }
+
+ signal3(const signal3<arg1_type, arg2_type, arg3_type, mt_policy>& s)
+ : _signal_base3<arg1_type, arg2_type, arg3_type, mt_policy>(s)
+ {
+ ;
+ }
+
+ template<class desttype>
+ void connect(desttype* pclass, void (desttype::*pmemfun)(arg1_type,
+ arg2_type, arg3_type))
+ {
+ lock_block<mt_policy> lock(this);
+ _connection3<desttype, arg1_type, arg2_type, arg3_type, mt_policy>* conn =
+ new _connection3<desttype, arg1_type, arg2_type, arg3_type, mt_policy>(pclass,
+ pmemfun);
+ m_connected_slots.push_back(conn);
+ pclass->signal_connect(this);
+ }
+
+ void emit(arg1_type a1, arg2_type a2, arg3_type a3)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator itNext, it = m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ itNext = it;
+ ++itNext;
+
+ (*it)->emit(a1, a2, a3);
+
+ it = itNext;
+ }
+ }
+
+ void operator()(arg1_type a1, arg2_type a2, arg3_type a3)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator itNext, it = m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ itNext = it;
+ ++itNext;
+
+ (*it)->emit(a1, a2, a3);
+
+ it = itNext;
+ }
+ }
+ };
+
+ template<class arg1_type, class arg2_type, class arg3_type, class arg4_type, class mt_policy = SIGSLOT_DEFAULT_MT_POLICY>
+ class signal4 : public _signal_base4<arg1_type, arg2_type, arg3_type,
+ arg4_type, mt_policy>
+ {
+ public:
+ typedef _signal_base4<arg1_type, arg2_type, arg3_type, arg4_type, mt_policy> base;
+ typedef typename base::connections_list connections_list;
+ using base::m_connected_slots;
+
+ signal4()
+ {
+ ;
+ }
+
+ signal4(const signal4<arg1_type, arg2_type, arg3_type, arg4_type, mt_policy>& s)
+ : _signal_base4<arg1_type, arg2_type, arg3_type, arg4_type, mt_policy>(s)
+ {
+ ;
+ }
+
+ template<class desttype>
+ void connect(desttype* pclass, void (desttype::*pmemfun)(arg1_type,
+ arg2_type, arg3_type, arg4_type))
+ {
+ lock_block<mt_policy> lock(this);
+ _connection4<desttype, arg1_type, arg2_type, arg3_type, arg4_type, mt_policy>*
+ conn = new _connection4<desttype, arg1_type, arg2_type, arg3_type,
+ arg4_type, mt_policy>(pclass, pmemfun);
+ m_connected_slots.push_back(conn);
+ pclass->signal_connect(this);
+ }
+
+ void emit(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator itNext, it = m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ itNext = it;
+ ++itNext;
+
+ (*it)->emit(a1, a2, a3, a4);
+
+ it = itNext;
+ }
+ }
+
+ void operator()(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator itNext, it = m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ itNext = it;
+ ++itNext;
+
+ (*it)->emit(a1, a2, a3, a4);
+
+ it = itNext;
+ }
+ }
+ };
+
+ template<class arg1_type, class arg2_type, class arg3_type, class arg4_type,
+ class arg5_type, class mt_policy = SIGSLOT_DEFAULT_MT_POLICY>
+ class signal5 : public _signal_base5<arg1_type, arg2_type, arg3_type,
+ arg4_type, arg5_type, mt_policy>
+ {
+ public:
+ typedef _signal_base5<arg1_type, arg2_type, arg3_type, arg4_type, arg5_type, mt_policy> base;
+ typedef typename base::connections_list connections_list;
+ using base::m_connected_slots;
+
+ signal5()
+ {
+ ;
+ }
+
+ signal5(const signal5<arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, mt_policy>& s)
+ : _signal_base5<arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, mt_policy>(s)
+ {
+ ;
+ }
+
+ template<class desttype>
+ void connect(desttype* pclass, void (desttype::*pmemfun)(arg1_type,
+ arg2_type, arg3_type, arg4_type, arg5_type))
+ {
+ lock_block<mt_policy> lock(this);
+ _connection5<desttype, arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, mt_policy>* conn = new _connection5<desttype, arg1_type, arg2_type,
+ arg3_type, arg4_type, arg5_type, mt_policy>(pclass, pmemfun);
+ m_connected_slots.push_back(conn);
+ pclass->signal_connect(this);
+ }
+
+ void emit(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4,
+ arg5_type a5)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator itNext, it = m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ itNext = it;
+ ++itNext;
+
+ (*it)->emit(a1, a2, a3, a4, a5);
+
+ it = itNext;
+ }
+ }
+
+ void operator()(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4,
+ arg5_type a5)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator itNext, it = m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ itNext = it;
+ ++itNext;
+
+ (*it)->emit(a1, a2, a3, a4, a5);
+
+ it = itNext;
+ }
+ }
+ };
+
+
+ template<class arg1_type, class arg2_type, class arg3_type, class arg4_type,
+ class arg5_type, class arg6_type, class mt_policy = SIGSLOT_DEFAULT_MT_POLICY>
+ class signal6 : public _signal_base6<arg1_type, arg2_type, arg3_type,
+ arg4_type, arg5_type, arg6_type, mt_policy>
+ {
+ public:
+ typedef _signal_base6<arg1_type, arg2_type, arg3_type, arg4_type, arg5_type, arg6_type, mt_policy> base;
+ typedef typename base::connections_list connections_list;
+ using base::m_connected_slots;
+
+ signal6()
+ {
+ ;
+ }
+
+ signal6(const signal6<arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, arg6_type, mt_policy>& s)
+ : _signal_base6<arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, arg6_type, mt_policy>(s)
+ {
+ ;
+ }
+
+ template<class desttype>
+ void connect(desttype* pclass, void (desttype::*pmemfun)(arg1_type,
+ arg2_type, arg3_type, arg4_type, arg5_type, arg6_type))
+ {
+ lock_block<mt_policy> lock(this);
+ _connection6<desttype, arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, arg6_type, mt_policy>* conn =
+ new _connection6<desttype, arg1_type, arg2_type, arg3_type,
+ arg4_type, arg5_type, arg6_type, mt_policy>(pclass, pmemfun);
+ m_connected_slots.push_back(conn);
+ pclass->signal_connect(this);
+ }
+
+ void emit(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4,
+ arg5_type a5, arg6_type a6)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator itNext, it = m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ itNext = it;
+ ++itNext;
+
+ (*it)->emit(a1, a2, a3, a4, a5, a6);
+
+ it = itNext;
+ }
+ }
+
+ void operator()(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4,
+ arg5_type a5, arg6_type a6)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator itNext, it = m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ itNext = it;
+ ++itNext;
+
+ (*it)->emit(a1, a2, a3, a4, a5, a6);
+
+ it = itNext;
+ }
+ }
+ };
+
+ template<class arg1_type, class arg2_type, class arg3_type, class arg4_type,
+ class arg5_type, class arg6_type, class arg7_type, class mt_policy = SIGSLOT_DEFAULT_MT_POLICY>
+ class signal7 : public _signal_base7<arg1_type, arg2_type, arg3_type,
+ arg4_type, arg5_type, arg6_type, arg7_type, mt_policy>
+ {
+ public:
+ typedef _signal_base7<arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, arg6_type, arg7_type, mt_policy> base;
+ typedef typename base::connections_list connections_list;
+ using base::m_connected_slots;
+
+ signal7()
+ {
+ ;
+ }
+
+ signal7(const signal7<arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, arg6_type, arg7_type, mt_policy>& s)
+ : _signal_base7<arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, arg6_type, arg7_type, mt_policy>(s)
+ {
+ ;
+ }
+
+ template<class desttype>
+ void connect(desttype* pclass, void (desttype::*pmemfun)(arg1_type,
+ arg2_type, arg3_type, arg4_type, arg5_type, arg6_type,
+ arg7_type))
+ {
+ lock_block<mt_policy> lock(this);
+ _connection7<desttype, arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, arg6_type, arg7_type, mt_policy>* conn =
+ new _connection7<desttype, arg1_type, arg2_type, arg3_type,
+ arg4_type, arg5_type, arg6_type, arg7_type, mt_policy>(pclass, pmemfun);
+ m_connected_slots.push_back(conn);
+ pclass->signal_connect(this);
+ }
+
+ void emit(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4,
+ arg5_type a5, arg6_type a6, arg7_type a7)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator itNext, it = m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ itNext = it;
+ ++itNext;
+
+ (*it)->emit(a1, a2, a3, a4, a5, a6, a7);
+
+ it = itNext;
+ }
+ }
+
+ void operator()(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4,
+ arg5_type a5, arg6_type a6, arg7_type a7)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator itNext, it = m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ itNext = it;
+ ++itNext;
+
+ (*it)->emit(a1, a2, a3, a4, a5, a6, a7);
+
+ it = itNext;
+ }
+ }
+ };
+
+ template<class arg1_type, class arg2_type, class arg3_type, class arg4_type,
+ class arg5_type, class arg6_type, class arg7_type, class arg8_type, class mt_policy = SIGSLOT_DEFAULT_MT_POLICY>
+ class signal8 : public _signal_base8<arg1_type, arg2_type, arg3_type,
+ arg4_type, arg5_type, arg6_type, arg7_type, arg8_type, mt_policy>
+ {
+ public:
+ typedef _signal_base8<arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, arg6_type, arg7_type, arg8_type, mt_policy> base;
+ typedef typename base::connections_list connections_list;
+ using base::m_connected_slots;
+
+ signal8()
+ {
+ ;
+ }
+
+ signal8(const signal8<arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, arg6_type, arg7_type, arg8_type, mt_policy>& s)
+ : _signal_base8<arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, arg6_type, arg7_type, arg8_type, mt_policy>(s)
+ {
+ ;
+ }
+
+ template<class desttype>
+ void connect(desttype* pclass, void (desttype::*pmemfun)(arg1_type,
+ arg2_type, arg3_type, arg4_type, arg5_type, arg6_type,
+ arg7_type, arg8_type))
+ {
+ lock_block<mt_policy> lock(this);
+ _connection8<desttype, arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, arg6_type, arg7_type, arg8_type, mt_policy>* conn =
+ new _connection8<desttype, arg1_type, arg2_type, arg3_type,
+ arg4_type, arg5_type, arg6_type, arg7_type,
+ arg8_type, mt_policy>(pclass, pmemfun);
+ m_connected_slots.push_back(conn);
+ pclass->signal_connect(this);
+ }
+
+ void emit(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4,
+ arg5_type a5, arg6_type a6, arg7_type a7, arg8_type a8)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator itNext, it = m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ itNext = it;
+ ++itNext;
+
+ (*it)->emit(a1, a2, a3, a4, a5, a6, a7, a8);
+
+ it = itNext;
+ }
+ }
+
+ void operator()(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4,
+ arg5_type a5, arg6_type a6, arg7_type a7, arg8_type a8)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator itNext, it = m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ itNext = it;
+ ++itNext;
+
+ (*it)->emit(a1, a2, a3, a4, a5, a6, a7, a8);
+
+ it = itNext;
+ }
+ }
+ };
+
+}; // namespace sigslot
+
+#endif // TALK_BASE_SIGSLOT_H__
diff --git a/Plugins/jingle/libjingle/talk/base/socket.h b/Plugins/jingle/libjingle/talk/base/socket.h
new file mode 100644
index 0000000..0a30c4b
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/socket.h
@@ -0,0 +1,160 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TALK_BASE_SOCKET_H__
+#define TALK_BASE_SOCKET_H__
+
+#include <errno.h>
+
+#ifdef POSIX
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#define SOCKET_EACCES EACCES
+#endif
+
+#ifdef WIN32
+#include "talk/base/win32.h"
+#endif
+
+#include "talk/base/basictypes.h"
+#include "talk/base/socketaddress.h"
+
+// Rather than converting errors into a private namespace,
+// Reuse the POSIX socket api errors. Note this depends on
+// Win32 compatibility.
+
+#ifdef WIN32
+#define EWOULDBLOCK WSAEWOULDBLOCK
+#define EINPROGRESS WSAEINPROGRESS
+#define EALREADY WSAEALREADY
+#define ENOTSOCK WSAENOTSOCK
+#define EDESTADDRREQ WSAEDESTADDRREQ
+#define EMSGSIZE WSAEMSGSIZE
+#define EPROTOTYPE WSAEPROTOTYPE
+#define ENOPROTOOPT WSAENOPROTOOPT
+#define EPROTONOSUPPORT WSAEPROTONOSUPPORT
+#define ESOCKTNOSUPPORT WSAESOCKTNOSUPPORT
+#define EOPNOTSUPP WSAEOPNOTSUPP
+#define EPFNOSUPPORT WSAEPFNOSUPPORT
+#define EAFNOSUPPORT WSAEAFNOSUPPORT
+#define EADDRINUSE WSAEADDRINUSE
+#define EADDRNOTAVAIL WSAEADDRNOTAVAIL
+#define ENETDOWN WSAENETDOWN
+#define ENETUNREACH WSAENETUNREACH
+#define ENETRESET WSAENETRESET
+#define ECONNABORTED WSAECONNABORTED
+#define ECONNRESET WSAECONNRESET
+#define ENOBUFS WSAENOBUFS
+#define EISCONN WSAEISCONN
+#define ENOTCONN WSAENOTCONN
+#define ESHUTDOWN WSAESHUTDOWN
+#define ETOOMANYREFS WSAETOOMANYREFS
+#define ETIMEDOUT WSAETIMEDOUT
+#define ECONNREFUSED WSAECONNREFUSED
+#define ELOOP WSAELOOP
+#undef ENAMETOOLONG // remove errno.h's definition
+#define ENAMETOOLONG WSAENAMETOOLONG
+#define EHOSTDOWN WSAEHOSTDOWN
+#define EHOSTUNREACH WSAEHOSTUNREACH
+#undef ENOTEMPTY // remove errno.h's definition
+#define ENOTEMPTY WSAENOTEMPTY
+#define EPROCLIM WSAEPROCLIM
+#define EUSERS WSAEUSERS
+#define EDQUOT WSAEDQUOT
+#define ESTALE WSAESTALE
+#define EREMOTE WSAEREMOTE
+#undef EACCES
+#define SOCKET_EACCES WSAEACCES
+#endif // WIN32
+
+#ifdef POSIX
+#define INVALID_SOCKET (-1)
+#define SOCKET_ERROR (-1)
+#define closesocket(s) close(s)
+#endif // POSIX
+
+namespace talk_base {
+
+inline bool IsBlockingError(int e) {
+ return (e == EWOULDBLOCK) || (e == EAGAIN) || (e == EINPROGRESS);
+}
+
+// General interface for the socket implementations of various networks. The
+// methods match those of normal UNIX sockets very closely.
+class Socket {
+public:
+ virtual ~Socket() {}
+
+ // Returns the address to which the socket is bound. If the socket is not
+ // bound, then the any-address is returned.
+ virtual SocketAddress GetLocalAddress() const = 0;
+
+ // Returns the address to which the socket is connected. If the socket is
+ // not connected, then the any-address is returned.
+ virtual SocketAddress GetRemoteAddress() const = 0;
+
+ virtual int Bind(const SocketAddress& addr) = 0;
+ virtual int Connect(const SocketAddress& addr) = 0;
+ virtual int Send(const void *pv, size_t cb) = 0;
+ virtual int SendTo(const void *pv, size_t cb, const SocketAddress& addr) = 0;
+ virtual int Recv(void *pv, size_t cb) = 0;
+ virtual int RecvFrom(void *pv, size_t cb, SocketAddress *paddr) = 0;
+ virtual int Listen(int backlog) = 0;
+ virtual Socket *Accept(SocketAddress *paddr) = 0;
+ virtual int Close() = 0;
+ virtual int GetError() const = 0;
+ virtual void SetError(int error) = 0;
+ inline bool IsBlocking() const { return IsBlockingError(GetError()); }
+
+ enum ConnState {
+ CS_CLOSED,
+ CS_CONNECTING,
+ CS_CONNECTED
+ };
+ virtual ConnState GetState() const = 0;
+
+ // Fills in the given uint16 with the current estimate of the MTU along the
+ // path to the address to which this socket is connected.
+ virtual int EstimateMTU(uint16* mtu) = 0;
+
+ enum Option {
+ OPT_DONTFRAGMENT
+ };
+ virtual int SetOption(Option opt, int value) = 0;
+
+protected:
+ Socket() {}
+
+private:
+ DISALLOW_EVIL_CONSTRUCTORS(Socket);
+};
+
+} // namespace talk_base
+
+#endif // TALK_BASE_SOCKET_H__
diff --git a/Plugins/jingle/libjingle/talk/base/socketadapters.cc b/Plugins/jingle/libjingle/talk/base/socketadapters.cc
new file mode 100644
index 0000000..fcbc20b
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/socketadapters.cc
@@ -0,0 +1,677 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#if defined(_MSC_VER) && _MSC_VER < 1300
+#pragma warning(disable:4786)
+#endif
+
+#include <time.h>
+#include <errno.h>
+
+#ifdef WIN32
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#define _WINSOCKAPI_
+#include <windows.h>
+#define SECURITY_WIN32
+#include <security.h>
+#endif
+
+#include "talk/base/basicdefs.h"
+#include "talk/base/bytebuffer.h"
+#include "talk/base/common.h"
+#include "talk/base/httpcommon.h"
+#include "talk/base/logging.h"
+#include "talk/base/socketadapters.h"
+#include "talk/base/stringencode.h"
+#include "talk/base/stringutils.h"
+
+#ifdef WIN32
+#include "talk/base/sec_buffer.h"
+#endif // WIN32
+
+namespace talk_base {
+
+BufferedReadAdapter::BufferedReadAdapter(AsyncSocket* socket, size_t buffer_size)
+ : AsyncSocketAdapter(socket), buffer_size_(buffer_size), data_len_(0), buffering_(false) {
+ buffer_ = new char[buffer_size_];
+}
+
+BufferedReadAdapter::~BufferedReadAdapter() {
+ delete [] buffer_;
+}
+
+int BufferedReadAdapter::Send(const void *pv, size_t cb) {
+ if (buffering_) {
+ // TODO: Spoof error better; Signal Writeable
+ socket_->SetError(EWOULDBLOCK);
+ return -1;
+ }
+ return AsyncSocketAdapter::Send(pv, cb);
+}
+
+int BufferedReadAdapter::Recv(void *pv, size_t cb) {
+ if (buffering_) {
+ socket_->SetError(EWOULDBLOCK);
+ return -1;
+ }
+
+ size_t read = 0;
+
+ if (data_len_) {
+ read = _min(cb, data_len_);
+ memcpy(pv, buffer_, read);
+ data_len_ -= read;
+ if (data_len_ > 0) {
+ memmove(buffer_, buffer_ + read, data_len_);
+ }
+ pv = static_cast<char *>(pv) + read;
+ cb -= read;
+ }
+
+ // FIX: If cb == 0, we won't generate another read event
+
+ int res = AsyncSocketAdapter::Recv(pv, cb);
+ if (res < 0)
+ return res;
+
+ return res + static_cast<int>(read);
+}
+
+void BufferedReadAdapter::BufferInput(bool on) {
+ buffering_ = on;
+}
+
+void BufferedReadAdapter::OnReadEvent(AsyncSocket * socket) {
+ ASSERT(socket == socket_);
+
+ if (!buffering_) {
+ AsyncSocketAdapter::OnReadEvent(socket);
+ return;
+ }
+
+ if (data_len_ >= buffer_size_) {
+ LOG(INFO) << "Input buffer overflow";
+ ASSERT(false);
+ data_len_ = 0;
+ }
+
+ int len = socket_->Recv(buffer_ + data_len_, buffer_size_ - data_len_);
+ if (len < 0) {
+ // TODO: Do something better like forwarding the error to the user.
+ LOG(INFO) << "Recv: " << errno << " " << std::strerror(errno);
+ return;
+ }
+
+ data_len_ += len;
+
+ ProcessInput(buffer_, data_len_);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+const uint8 SSL_SERVER_HELLO[] = {
+ 22,3,1,0,74,2,0,0,70,3,1,66,133,69,167,39,169,93,160,
+ 179,197,231,83,218,72,43,63,198,90,202,137,193,88,82,
+ 161,120,60,91,23,70,0,133,63,32,14,211,6,114,91,91,
+ 27,95,21,172,19,249,136,83,157,155,232,61,123,12,48,
+ 50,110,56,77,162,117,87,65,108,52,92,0,4,0
+};
+
+const char SSL_CLIENT_HELLO[] = {
+ -128,70,1,3,1,0,45,0,0,0,16,1,0,-128,3,0,-128,7,0,-64,6,0,64,2,0,
+ -128,4,0,-128,0,0,4,0,-2,-1,0,0,10,0,-2,-2,0,0,9,0,0,100,0,0,98,0,
+ 0,3,0,0,6,31,23,12,-90,47,0,120,-4,70,85,46,-79,-125,57,-15,-22
+};
+
+AsyncSSLSocket::AsyncSSLSocket(AsyncSocket* socket) : BufferedReadAdapter(socket, 1024) {
+}
+
+int AsyncSSLSocket::Connect(const SocketAddress& addr) {
+ // Begin buffering before we connect, so that there isn't a race condition between
+ // potential senders and receiving the OnConnectEvent signal
+ BufferInput(true);
+ return BufferedReadAdapter::Connect(addr);
+}
+
+void AsyncSSLSocket::OnConnectEvent(AsyncSocket * socket) {
+ ASSERT(socket == socket_);
+
+ // TODO: we could buffer output too...
+ int res = DirectSend(SSL_CLIENT_HELLO, sizeof(SSL_CLIENT_HELLO));
+ ASSERT(res == sizeof(SSL_CLIENT_HELLO));
+}
+
+void AsyncSSLSocket::ProcessInput(char * data, size_t& len) {
+ if (len < sizeof(SSL_SERVER_HELLO))
+ return;
+
+ if (memcmp(SSL_SERVER_HELLO, data, sizeof(SSL_SERVER_HELLO)) != 0) {
+ Close();
+ SignalCloseEvent(this, 0); // TODO: error code?
+ return;
+ }
+
+ len -= sizeof(SSL_SERVER_HELLO);
+ if (len > 0) {
+ memmove(data, data + sizeof(SSL_SERVER_HELLO), len);
+ }
+
+ bool remainder = (len > 0);
+ BufferInput(false);
+ SignalConnectEvent(this);
+
+ // FIX: if SignalConnect causes the socket to be destroyed, we are in trouble
+ if (remainder)
+ SignalReadEvent(this);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+AsyncHttpsProxySocket::AsyncHttpsProxySocket(AsyncSocket* socket,
+ const std::string& user_agent,
+ const SocketAddress& proxy,
+ const std::string& username,
+ const CryptString& password)
+ : BufferedReadAdapter(socket, 1024), proxy_(proxy), agent_(user_agent),
+ user_(username), pass_(password), state_(PS_ERROR), context_(0) {
+}
+
+AsyncHttpsProxySocket::~AsyncHttpsProxySocket() {
+ delete context_;
+}
+
+int AsyncHttpsProxySocket::Connect(const SocketAddress& addr) {
+ LOG(LS_VERBOSE) << "AsyncHttpsProxySocket::Connect("
+ << proxy_.ToString() << ")";
+ dest_ = addr;
+ if (dest_.port() != 80) {
+ BufferInput(true);
+ }
+ return BufferedReadAdapter::Connect(proxy_);
+}
+
+SocketAddress AsyncHttpsProxySocket::GetRemoteAddress() const {
+ return dest_;
+}
+
+int AsyncHttpsProxySocket::Close() {
+ headers_.clear();
+ state_ = PS_ERROR;
+ delete context_;
+ context_ = 0;
+ return BufferedReadAdapter::Close();
+}
+
+void AsyncHttpsProxySocket::OnConnectEvent(AsyncSocket * socket) {
+ LOG(LS_VERBOSE) << "AsyncHttpsProxySocket::OnConnectEvent";
+ // TODO: Decide whether tunneling or not should be explicitly set,
+ // or indicated by destination port (as below)
+ if (dest_.port() == 80) {
+ state_ = PS_TUNNEL;
+ BufferedReadAdapter::OnConnectEvent(socket);
+ return;
+ }
+ SendRequest();
+}
+
+void AsyncHttpsProxySocket::OnCloseEvent(AsyncSocket * socket, int err) {
+ LOG(LS_VERBOSE) << "AsyncHttpsProxySocket::OnCloseEvent(" << err << ")";
+ if ((state_ == PS_WAIT_CLOSE) && (err == 0)) {
+ state_ = PS_ERROR;
+ Connect(dest_);
+ } else {
+ BufferedReadAdapter::OnCloseEvent(socket, err);
+ }
+}
+
+void AsyncHttpsProxySocket::ProcessInput(char * data, size_t& len) {
+ size_t start = 0;
+ for (size_t pos = start; (state_ < PS_TUNNEL) && (pos < len); ) {
+ if (state_ == PS_SKIP_BODY) {
+ size_t consume = _min(len - pos, content_length_);
+ pos += consume;
+ start = pos;
+ content_length_ -= consume;
+ if (content_length_ == 0) {
+ EndResponse();
+ }
+ continue;
+ }
+
+ if (data[pos++] != '\n')
+ continue;
+
+ size_t len = pos - start - 1;
+ if ((len > 0) && (data[start + len - 1] == '\r'))
+ --len;
+
+ data[start + len] = 0;
+ ProcessLine(data + start, len);
+ start = pos;
+ }
+
+ len -= start;
+ if (len > 0) {
+ memmove(data, data + start, len);
+ }
+
+ if (state_ != PS_TUNNEL)
+ return;
+
+ bool remainder = (len > 0);
+ BufferInput(false);
+ SignalConnectEvent(this);
+
+ // FIX: if SignalConnect causes the socket to be destroyed, we are in trouble
+ if (remainder)
+ SignalReadEvent(this); // TODO: signal this??
+}
+
+void AsyncHttpsProxySocket::SendRequest() {
+ std::stringstream ss;
+ ss << "CONNECT " << dest_.ToString() << " HTTP/1.0\r\n";
+ ss << "User-Agent: " << agent_ << "\r\n";
+ ss << "Host: " << dest_.IPAsString() << "\r\n";
+ ss << "Content-Length: 0\r\n";
+ ss << "Proxy-Connection: Keep-Alive\r\n";
+ ss << headers_;
+ ss << "\r\n";
+ std::string str = ss.str();
+ DirectSend(str.c_str(), str.size());
+ state_ = PS_LEADER;
+ expect_close_ = true;
+ content_length_ = 0;
+ headers_.clear();
+
+ LOG(LS_VERBOSE) << "AsyncHttpsProxySocket >> " << str;
+}
+
+void AsyncHttpsProxySocket::ProcessLine(char * data, size_t len) {
+ LOG(LS_VERBOSE) << "AsyncHttpsProxySocket << " << data;
+
+ if (len == 0) {
+ if (state_ == PS_TUNNEL_HEADERS) {
+ state_ = PS_TUNNEL;
+ } else if (state_ == PS_ERROR_HEADERS) {
+ Error(defer_error_);
+ return;
+ } else if (state_ == PS_SKIP_HEADERS) {
+ if (content_length_) {
+ state_ = PS_SKIP_BODY;
+ } else {
+ EndResponse();
+ return;
+ }
+ } else {
+ static bool report = false;
+ if (!unknown_mechanisms_.empty() && !report) {
+ report = true;
+ std::string msg(
+ "Unable to connect to the Google Talk service due to an incompatibility "
+ "with your proxy.\r\nPlease help us resolve this issue by submitting the "
+ "following information to us using our technical issue submission form "
+ "at:\r\n\r\n"
+ "http://www.google.com/support/talk/bin/request.py\r\n\r\n"
+ "We apologize for the inconvenience.\r\n\r\n"
+ "Information to submit to Google: "
+ );
+ //std::string msg("Please report the following information to foo@bar.com:\r\nUnknown methods: ");
+ msg.append(unknown_mechanisms_);
+#ifdef WIN32
+ MessageBoxA(0, msg.c_str(), "Oops!", MB_OK);
+#endif
+#ifdef POSIX
+ //TODO: Raise a signal or something so the UI can be separated.
+ LOG(LS_ERROR) << "Oops!\n\n" << msg;
+#endif
+ }
+ // Unexpected end of headers
+ Error(0);
+ return;
+ }
+ } else if (state_ == PS_LEADER) {
+ uint32 code;
+ if (sscanf(data, "HTTP/%*lu.%*lu %lu", &code) != 1) {
+ Error(0);
+ return;
+ }
+ switch (code) {
+ case 200:
+ // connection good!
+ state_ = PS_TUNNEL_HEADERS;
+ return;
+#if defined(HTTP_STATUS_PROXY_AUTH_REQ) && (HTTP_STATUS_PROXY_AUTH_REQ != 407)
+#error Wrong code for HTTP_STATUS_PROXY_AUTH_REQ
+#endif
+ case 407: // HTTP_STATUS_PROXY_AUTH_REQ
+ state_ = PS_AUTHENTICATE;
+ return;
+ default:
+ defer_error_ = 0;
+ state_ = PS_ERROR_HEADERS;
+ return;
+ }
+ } else if ((state_ == PS_AUTHENTICATE)
+ && (_strnicmp(data, "Proxy-Authenticate:", 19) == 0)) {
+ std::string response, auth_method;
+ switch (HttpAuthenticate(data + 19, len - 19,
+ proxy_, "CONNECT", "/",
+ user_, pass_, context_, response, auth_method)) {
+ case HAR_IGNORE:
+ LOG(LS_VERBOSE) << "Ignoring Proxy-Authenticate: " << auth_method;
+ if (!unknown_mechanisms_.empty())
+ unknown_mechanisms_.append(", ");
+ unknown_mechanisms_.append(auth_method);
+ break;
+ case HAR_RESPONSE:
+ headers_ = "Proxy-Authorization: ";
+ headers_.append(response);
+ headers_.append("\r\n");
+ state_ = PS_SKIP_HEADERS;
+ unknown_mechanisms_.clear();
+ break;
+ case HAR_CREDENTIALS:
+ defer_error_ = SOCKET_EACCES;
+ state_ = PS_ERROR_HEADERS;
+ unknown_mechanisms_.clear();
+ break;
+ case HAR_ERROR:
+ defer_error_ = 0;
+ state_ = PS_ERROR_HEADERS;
+ unknown_mechanisms_.clear();
+ break;
+ }
+ } else if (_strnicmp(data, "Content-Length:", 15) == 0) {
+ content_length_ = strtoul(data + 15, 0, 0);
+ } else if (_strnicmp(data, "Proxy-Connection: Keep-Alive", 28) == 0) {
+ expect_close_ = false;
+ /*
+ } else if (_strnicmp(data, "Connection: close", 17) == 0) {
+ expect_close_ = true;
+ */
+ }
+}
+
+void AsyncHttpsProxySocket::EndResponse() {
+ if (!expect_close_) {
+ SendRequest();
+ return;
+ }
+
+ // No point in waiting for the server to close... let's close now
+ // TODO: Refactor out PS_WAIT_CLOSE
+ state_ = PS_WAIT_CLOSE;
+ BufferedReadAdapter::Close();
+ OnCloseEvent(this, 0);
+}
+
+void AsyncHttpsProxySocket::Error(int error) {
+ BufferInput(false);
+ Close();
+ SetError(error);
+ SignalCloseEvent(this, error);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+AsyncSocksProxySocket::AsyncSocksProxySocket(AsyncSocket* socket, const SocketAddress& proxy,
+ const std::string& username, const CryptString& password)
+ : BufferedReadAdapter(socket, 1024), proxy_(proxy), user_(username), pass_(password),
+ state_(SS_ERROR) {
+}
+
+int AsyncSocksProxySocket::Connect(const SocketAddress& addr) {
+ dest_ = addr;
+ BufferInput(true);
+ return BufferedReadAdapter::Connect(proxy_);
+}
+
+SocketAddress AsyncSocksProxySocket::GetRemoteAddress() const {
+ return dest_;
+}
+
+void AsyncSocksProxySocket::OnConnectEvent(AsyncSocket * socket) {
+ SendHello();
+}
+
+void AsyncSocksProxySocket::ProcessInput(char * data, size_t& len) {
+ ASSERT(state_ < SS_TUNNEL);
+
+ ByteBuffer response(data, len);
+
+ if (state_ == SS_HELLO) {
+ uint8 ver, method;
+ if (!response.ReadUInt8(ver) ||
+ !response.ReadUInt8(method))
+ return;
+
+ if (ver != 5) {
+ Error(0);
+ return;
+ }
+
+ if (method == 0) {
+ SendConnect();
+ } else if (method == 2) {
+ SendAuth();
+ } else {
+ Error(0);
+ return;
+ }
+ } else if (state_ == SS_AUTH) {
+ uint8 ver, status;
+ if (!response.ReadUInt8(ver) ||
+ !response.ReadUInt8(status))
+ return;
+
+ if ((ver != 1) || (status != 0)) {
+ Error(SOCKET_EACCES);
+ return;
+ }
+
+ SendConnect();
+ } else if (state_ == SS_CONNECT) {
+ uint8 ver, rep, rsv, atyp;
+ if (!response.ReadUInt8(ver) ||
+ !response.ReadUInt8(rep) ||
+ !response.ReadUInt8(rsv) ||
+ !response.ReadUInt8(atyp))
+ return;
+
+ if ((ver != 5) || (rep != 0)) {
+ Error(0);
+ return;
+ }
+
+ uint16 port;
+ if (atyp == 1) {
+ uint32 addr;
+ if (!response.ReadUInt32(addr) ||
+ !response.ReadUInt16(port))
+ return;
+ LOG(LS_VERBOSE) << "Bound on " << addr << ":" << port;
+ } else if (atyp == 3) {
+ uint8 len;
+ std::string addr;
+ if (!response.ReadUInt8(len) ||
+ !response.ReadString(addr, len) ||
+ !response.ReadUInt16(port))
+ return;
+ LOG(LS_VERBOSE) << "Bound on " << addr << ":" << port;
+ } else if (atyp == 4) {
+ std::string addr;
+ if (!response.ReadString(addr, 16) ||
+ !response.ReadUInt16(port))
+ return;
+ LOG(LS_VERBOSE) << "Bound on <IPV6>:" << port;
+ } else {
+ Error(0);
+ return;
+ }
+
+ state_ = SS_TUNNEL;
+ }
+
+ // Consume parsed data
+ len = response.Length();
+ memcpy(data, response.Data(), len);
+
+ if (state_ != SS_TUNNEL)
+ return;
+
+ bool remainder = (len > 0);
+ BufferInput(false);
+ SignalConnectEvent(this);
+
+ // FIX: if SignalConnect causes the socket to be destroyed, we are in trouble
+ if (remainder)
+ SignalReadEvent(this); // TODO: signal this??
+}
+
+void AsyncSocksProxySocket::SendHello() {
+ ByteBuffer request;
+ request.WriteUInt8(5); // Socks Version
+ if (user_.empty()) {
+ request.WriteUInt8(1); // Authentication Mechanisms
+ request.WriteUInt8(0); // No authentication
+ } else {
+ request.WriteUInt8(2); // Authentication Mechanisms
+ request.WriteUInt8(0); // No authentication
+ request.WriteUInt8(2); // Username/Password
+ }
+ DirectSend(request.Data(), request.Length());
+ state_ = SS_HELLO;
+}
+
+void AsyncSocksProxySocket::SendAuth() {
+ ByteBuffer request;
+ request.WriteUInt8(1); // Negotiation Version
+ request.WriteUInt8(static_cast<uint8>(user_.size()));
+ request.WriteString(user_); // Username
+ request.WriteUInt8(static_cast<uint8>(pass_.GetLength()));
+ size_t len = pass_.GetLength() + 1;
+ char * sensitive = new char[len];
+ pass_.CopyTo(sensitive, true);
+ request.WriteString(sensitive); // Password
+ memset(sensitive, 0, len);
+ delete [] sensitive;
+ DirectSend(request.Data(), request.Length());
+ state_ = SS_AUTH;
+}
+
+void AsyncSocksProxySocket::SendConnect() {
+ ByteBuffer request;
+ request.WriteUInt8(5); // Socks Version
+ request.WriteUInt8(1); // CONNECT
+ request.WriteUInt8(0); // Reserved
+ if (dest_.IsUnresolved()) {
+ std::string hostname = dest_.IPAsString();
+ request.WriteUInt8(3); // DOMAINNAME
+ request.WriteUInt8(static_cast<uint8>(hostname.size()));
+ request.WriteString(hostname); // Destination Hostname
+ } else {
+ request.WriteUInt8(1); // IPV4
+ request.WriteUInt32(dest_.ip()); // Destination IP
+ }
+ request.WriteUInt16(dest_.port()); // Destination Port
+ DirectSend(request.Data(), request.Length());
+ state_ = SS_CONNECT;
+}
+
+void AsyncSocksProxySocket::Error(int error) {
+ state_ = SS_ERROR;
+ BufferInput(false);
+ Close();
+ SetError(SOCKET_EACCES);
+ SignalCloseEvent(this, error);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+LoggingSocketAdapter::LoggingSocketAdapter(AsyncSocket* socket,
+ LoggingSeverity level,
+ const char * label, bool hex_mode)
+: AsyncSocketAdapter(socket), level_(level), hex_mode_(hex_mode)
+{
+ label_.append("[");
+ label_.append(label);
+ label_.append("]");
+}
+
+int
+LoggingSocketAdapter::Send(const void *pv, size_t cb) {
+ int res = AsyncSocketAdapter::Send(pv, cb);
+ if (res > 0)
+ LogMultiline(level_, label_.c_str(), false,
+ static_cast<const char *>(pv), res, hex_mode_, &lms_);
+ return res;
+}
+
+int
+LoggingSocketAdapter::SendTo(const void *pv, size_t cb,
+ const SocketAddress& addr) {
+ int res = AsyncSocketAdapter::SendTo(pv, cb, addr);
+ if (res > 0)
+ LogMultiline(level_, label_.c_str(), false,
+ static_cast<const char *>(pv), res, hex_mode_, &lms_);
+ return res;
+}
+
+int
+LoggingSocketAdapter::Recv(void *pv, size_t cb) {
+ int res = AsyncSocketAdapter::Recv(pv, cb);
+ if (res > 0)
+ LogMultiline(level_, label_.c_str(), true,
+ static_cast<const char *>(pv), res, hex_mode_, &lms_);
+ return res;
+}
+
+int
+LoggingSocketAdapter::RecvFrom(void *pv, size_t cb, SocketAddress *paddr) {
+ int res = AsyncSocketAdapter::RecvFrom(pv, cb, paddr);
+ if (res > 0)
+ LogMultiline(level_, label_.c_str(), true,
+ static_cast<const char *>(pv), res, hex_mode_, &lms_);
+ return res;
+}
+
+void
+LoggingSocketAdapter::OnConnectEvent(AsyncSocket * socket) {
+ LOG_V(level_) << label_ << " Connected";
+ AsyncSocketAdapter::OnConnectEvent(socket);
+}
+
+void
+LoggingSocketAdapter::OnCloseEvent(AsyncSocket * socket, int err) {
+ LOG_V(level_) << label_ << " Closed with error: " << err;
+ AsyncSocketAdapter::OnCloseEvent(socket, err);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+} // namespace talk_base
diff --git a/Plugins/jingle/libjingle/talk/base/socketadapters.h b/Plugins/jingle/libjingle/talk/base/socketadapters.h
new file mode 100644
index 0000000..6cd6a8f
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/socketadapters.h
@@ -0,0 +1,170 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TALK_BASE_SOCKETADAPTERS_H__
+#define TALK_BASE_SOCKETADAPTERS_H__
+
+#include <map>
+#include <string>
+
+#include "talk/base/asyncsocket.h"
+#include "talk/base/cryptstring.h"
+#include "talk/base/logging.h"
+
+namespace talk_base {
+
+struct HttpAuthContext;
+
+///////////////////////////////////////////////////////////////////////////////
+
+class BufferedReadAdapter : public AsyncSocketAdapter {
+public:
+ BufferedReadAdapter(AsyncSocket* socket, size_t buffer_size);
+ virtual ~BufferedReadAdapter();
+
+ virtual int Send(const void *pv, size_t cb);
+ virtual int Recv(void *pv, size_t cb);
+
+protected:
+ int DirectSend(const void *pv, size_t cb) { return AsyncSocketAdapter::Send(pv, cb); }
+
+ void BufferInput(bool on = true);
+ virtual void ProcessInput(char * data, size_t& len) = 0;
+
+ virtual void OnReadEvent(AsyncSocket * socket);
+
+private:
+ char * buffer_;
+ size_t buffer_size_, data_len_;
+ bool buffering_;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+class AsyncSSLSocket : public BufferedReadAdapter {
+public:
+ AsyncSSLSocket(AsyncSocket* socket);
+
+ virtual int Connect(const SocketAddress& addr);
+
+protected:
+ virtual void OnConnectEvent(AsyncSocket * socket);
+ virtual void ProcessInput(char * data, size_t& len);
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+class AsyncHttpsProxySocket : public BufferedReadAdapter {
+public:
+ AsyncHttpsProxySocket(AsyncSocket* socket, const std::string& user_agent,
+ const SocketAddress& proxy,
+ const std::string& username, const CryptString& password);
+ virtual ~AsyncHttpsProxySocket();
+
+ virtual int Connect(const SocketAddress& addr);
+ virtual SocketAddress GetRemoteAddress() const;
+ virtual int Close();
+
+protected:
+ virtual void OnConnectEvent(AsyncSocket * socket);
+ virtual void OnCloseEvent(AsyncSocket * socket, int err);
+ virtual void ProcessInput(char * data, size_t& len);
+
+ void SendRequest();
+ void ProcessLine(char * data, size_t len);
+ void EndResponse();
+ void Error(int error);
+
+private:
+ SocketAddress proxy_, dest_;
+ std::string agent_, user_, headers_;
+ CryptString pass_;
+ size_t content_length_;
+ int defer_error_;
+ bool expect_close_;
+ enum ProxyState {
+ PS_LEADER, PS_AUTHENTICATE, PS_SKIP_HEADERS, PS_ERROR_HEADERS,
+ PS_TUNNEL_HEADERS, PS_SKIP_BODY, PS_TUNNEL, PS_WAIT_CLOSE, PS_ERROR
+ } state_;
+ HttpAuthContext * context_;
+ std::string unknown_mechanisms_;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+class AsyncSocksProxySocket : public BufferedReadAdapter {
+public:
+ AsyncSocksProxySocket(AsyncSocket* socket, const SocketAddress& proxy,
+ const std::string& username, const CryptString& password);
+
+ virtual int Connect(const SocketAddress& addr);
+ virtual SocketAddress GetRemoteAddress() const;
+
+protected:
+ virtual void OnConnectEvent(AsyncSocket * socket);
+ virtual void ProcessInput(char * data, size_t& len);
+
+ void SendHello();
+ void SendConnect();
+ void SendAuth();
+ void Error(int error);
+
+private:
+ SocketAddress proxy_, dest_;
+ std::string user_;
+ CryptString pass_;
+ enum SocksState { SS_HELLO, SS_AUTH, SS_CONNECT, SS_TUNNEL, SS_ERROR } state_;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+class LoggingSocketAdapter : public AsyncSocketAdapter {
+public:
+ LoggingSocketAdapter(AsyncSocket* socket, LoggingSeverity level,
+ const char * label, bool hex_mode = false);
+
+ virtual int Send(const void *pv, size_t cb);
+ virtual int SendTo(const void *pv, size_t cb, const SocketAddress& addr);
+ virtual int Recv(void *pv, size_t cb);
+ virtual int RecvFrom(void *pv, size_t cb, SocketAddress *paddr);
+
+protected:
+ virtual void OnConnectEvent(AsyncSocket * socket);
+ virtual void OnCloseEvent(AsyncSocket * socket, int err);
+
+private:
+ LoggingSeverity level_;
+ std::string label_;
+ bool hex_mode_;
+ LogMultilineState lms_;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+} // namespace talk_base
+
+#endif // TALK_BASE_SOCKETADAPTERS_H__
diff --git a/Plugins/jingle/libjingle/talk/base/socketaddress.cc b/Plugins/jingle/libjingle/talk/base/socketaddress.cc
new file mode 100644
index 0000000..e3a04d1
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/socketaddress.cc
@@ -0,0 +1,312 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifdef POSIX
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#endif
+
+#include <cstring>
+#include <sstream>
+
+#include "talk/base/byteorder.h"
+#include "talk/base/common.h"
+#include "talk/base/logging.h"
+#include "talk/base/socketaddress.h"
+
+#ifdef WIN32
+#undef SetPort
+int inet_aton(const char * cp, struct in_addr * inp) {
+ inp->s_addr = inet_addr(cp);
+ return (inp->s_addr == INADDR_NONE) ? 0 : 1;
+}
+#endif // WIN32
+
+#ifdef _DEBUG
+#define DISABLE_DNS 0
+#else // !_DEBUG
+#define DISABLE_DNS 0
+#endif // !_DEBUG
+
+namespace talk_base {
+
+SocketAddress::SocketAddress() {
+ Clear();
+}
+
+SocketAddress::SocketAddress(const std::string& hostname, int port, bool use_dns) {
+ SetIP(hostname, use_dns);
+ SetPort(port);
+}
+
+SocketAddress::SocketAddress(uint32 ip, int port) {
+ SetIP(ip);
+ SetPort(port);
+}
+
+SocketAddress::SocketAddress(const SocketAddress& addr) {
+ this->operator=(addr);
+}
+
+void SocketAddress::Clear() {
+ hostname_.clear();
+ ip_ = 0;
+ port_ = 0;
+}
+
+SocketAddress& SocketAddress::operator=(const SocketAddress& addr) {
+ hostname_ = addr.hostname_;
+ ip_ = addr.ip_;
+ port_ = addr.port_;
+ return *this;
+}
+
+void SocketAddress::SetIP(uint32 ip) {
+ hostname_.clear();
+ ip_ = ip;
+}
+
+bool SocketAddress::SetIP(const std::string& hostname, bool use_dns) {
+ hostname_ = hostname;
+ ip_ = 0;
+ return Resolve(true, use_dns);
+}
+
+void SocketAddress::SetResolvedIP(uint32 ip) {
+ ip_ = ip;
+}
+
+void SocketAddress::SetPort(int port) {
+ ASSERT((0 <= port) && (port < 65536));
+ port_ = port;
+}
+
+uint32 SocketAddress::ip() const {
+ return ip_;
+}
+
+uint16 SocketAddress::port() const {
+ return port_;
+}
+
+std::string SocketAddress::IPAsString() const {
+ if (!hostname_.empty())
+ return hostname_;
+ return IPToString(ip_);
+}
+
+std::string SocketAddress::PortAsString() const {
+ std::ostringstream ost;
+ ost << port_;
+ return ost.str();
+}
+
+std::string SocketAddress::ToString() const {
+ std::ostringstream ost;
+ ost << IPAsString();
+ ost << ":";
+ ost << port();
+ return ost.str();
+}
+
+bool SocketAddress::IsAny() const {
+ return (ip_ == 0);
+}
+
+bool SocketAddress::IsLocalIP() const {
+ return (ip_ >> 24) == 127;
+}
+
+bool SocketAddress::IsPrivateIP() const {
+ return ((ip_ >> 24) == 127) ||
+ ((ip_ >> 24) == 10) ||
+ ((ip_ >> 20) == ((172 << 4) | 1)) ||
+ ((ip_ >> 16) == ((192 << 8) | 168));
+}
+
+bool SocketAddress::IsUnresolved() const {
+ return IsAny() && !hostname_.empty();
+}
+
+bool SocketAddress::Resolve(bool force, bool use_dns) {
+ if (hostname_.empty()) {
+ // nothing to resolve
+ } else if (!force && !IsAny()) {
+ // already resolved
+ } else if (uint32 ip = StringToIP(hostname_, use_dns)) {
+ ip_ = ip;
+ } else {
+ return false;
+ }
+ return true;
+}
+
+bool SocketAddress::operator ==(const SocketAddress& addr) const {
+ return EqualIPs(addr) && EqualPorts(addr);
+}
+
+bool SocketAddress::operator <(const SocketAddress& addr) const {
+ if (ip_ < addr.ip_)
+ return true;
+ else if (addr.ip_ < ip_)
+ return false;
+
+ // We only check hostnames if both IPs are zero. This matches EqualIPs()
+ if (addr.ip_ == 0) {
+ if (hostname_ < addr.hostname_)
+ return true;
+ else if (addr.hostname_ < hostname_)
+ return false;
+ }
+
+ return port_ < addr.port_;
+}
+
+bool SocketAddress::EqualIPs(const SocketAddress& addr) const {
+ return (ip_ == addr.ip_) && ((ip_ != 0) || (hostname_ == addr.hostname_));
+}
+
+bool SocketAddress::EqualPorts(const SocketAddress& addr) const {
+ return (port_ == addr.port_);
+}
+
+size_t SocketAddress::Hash() const {
+ size_t h = 0;
+ h ^= ip_;
+ h ^= port_ | (port_ << 16);
+ return h;
+}
+
+size_t SocketAddress::Size_() const {
+ return sizeof(ip_) + sizeof(port_);
+}
+
+void SocketAddress::Write_(char* buf, int len) const {
+ // TODO: Depending on how this is used, we may want/need to write hostname
+ ASSERT((size_t)len >= Size_());
+ reinterpret_cast<uint32*>(buf)[0] = ip_;
+ buf += sizeof(ip_);
+ reinterpret_cast<uint16*>(buf)[0] = port_;
+}
+
+void SocketAddress::Read_(const char* buf, int len) {
+ ASSERT((size_t)len >= Size_());
+ ip_ = reinterpret_cast<const uint32*>(buf)[0];
+ buf += sizeof(ip_);
+ port_ = reinterpret_cast<const uint16*>(buf)[0];
+}
+
+void SocketAddress::ToSockAddr(sockaddr_in* saddr) const {
+ memset(saddr, 0, sizeof(*saddr));
+ saddr->sin_family = AF_INET;
+ saddr->sin_port = HostToNetwork16(port_);
+ if (0 == ip_) {
+ saddr->sin_addr.s_addr = INADDR_ANY;
+ } else {
+ saddr->sin_addr.s_addr = HostToNetwork32(ip_);
+ }
+}
+
+void SocketAddress::FromSockAddr(const sockaddr_in& saddr) {
+ SetIP(NetworkToHost32(saddr.sin_addr.s_addr));
+ SetPort(NetworkToHost16(saddr.sin_port));
+}
+
+std::string SocketAddress::IPToString(uint32 ip) {
+ std::ostringstream ost;
+ ost << ((ip >> 24) & 0xff);
+ ost << '.';
+ ost << ((ip >> 16) & 0xff);
+ ost << '.';
+ ost << ((ip >> 8) & 0xff);
+ ost << '.';
+ ost << ((ip >> 0) & 0xff);
+ return ost.str();
+}
+
+uint32 SocketAddress::StringToIP(const std::string& hostname, bool use_dns) {
+ uint32 ip = 0;
+ in_addr addr;
+ if (inet_aton(hostname.c_str(), &addr) != 0) {
+ ip = NetworkToHost32(addr.s_addr);
+ } else if (use_dns) {
+ // Note: this is here so we can spot spurious DNS resolutions for a while
+ LOG(INFO) << "=== DNS RESOLUTION (" << hostname << ") ===";
+#if DISABLE_DNS
+ LOG(WARNING) << "*** DNS DISABLED ***";
+#if WIN32
+ WSASetLastError(WSAHOST_NOT_FOUND);
+#endif // WIN32
+#endif // DISABLE_DNS
+ if (hostent * pHost = gethostbyname(hostname.c_str())) {
+ ip = NetworkToHost32(*reinterpret_cast<uint32 *>(pHost->h_addr_list[0]));
+ } else {
+#if WIN32
+ LOG(LS_ERROR) << "gethostbyname error: " << WSAGetLastError();
+#else
+ LOG(LS_ERROR) << "gethostbyname error: " << strerror(h_errno);
+#endif
+ }
+ LOG(INFO) << hostname << " resolved to " << IPToString(ip);
+ }
+ return ip;
+}
+
+std::string SocketAddress::GetHostname() {
+ char hostname[256];
+ if (gethostname(hostname, ARRAY_SIZE(hostname)) == 0)
+ return hostname;
+ return "";
+}
+
+bool SocketAddress::GetLocalIPs(std::vector<uint32>& ips) {
+ ips.clear();
+
+ const std::string hostname = GetHostname();
+ if (hostname.empty())
+ return false;
+
+ if (hostent * pHost = gethostbyname(hostname.c_str())) {
+ for (size_t i=0; pHost->h_addr_list[i]; ++i) {
+ uint32 ip =
+ NetworkToHost32(*reinterpret_cast<uint32 *>(pHost->h_addr_list[i]));
+ ips.push_back(ip);
+ }
+ return !ips.empty();
+ }
+#if WIN32
+ LOG(LS_ERROR) << "gethostbyname error: " << WSAGetLastError();
+#else
+ LOG(LS_ERROR) << "gethostbyname error: " << strerror(h_errno);
+#endif
+ return false;
+}
+
+} // namespace talk_base
diff --git a/Plugins/jingle/libjingle/talk/base/socketaddress.h b/Plugins/jingle/libjingle/talk/base/socketaddress.h
new file mode 100644
index 0000000..7fdc22f
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/socketaddress.h
@@ -0,0 +1,174 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TALK_BASE_SOCKETADDRESS_H__
+#define TALK_BASE_SOCKETADDRESS_H__
+
+#include <string>
+#include <vector>
+#include "talk/base/basictypes.h"
+struct sockaddr_in;
+
+#undef SetPort
+
+namespace talk_base {
+
+// Records an IP address and port, which are 32 and 16 bit integers,
+// respectively, both in <b>host byte-order</b>.
+class SocketAddress {
+public:
+ // Creates a missing / unknown address.
+ SocketAddress();
+
+ // Creates the address with the given host and port. If use_dns is true,
+ // the hostname will be immediately resolved to an IP (which may block for
+ // several seconds if DNS is not available). Alternately, set use_dns to
+ // false, and then call Resolve() to complete resolution later, or use
+ // SetResolvedIP to set the IP explictly.
+ SocketAddress(const std::string& hostname, int port = 0, bool use_dns = true);
+
+ // Creates the address with the given IP and port.
+ SocketAddress(uint32 ip, int port);
+
+ // Creates a copy of the given address.
+ SocketAddress(const SocketAddress& addr);
+
+ // Resets to missing / unknown address.
+ void Clear();
+
+ // Replaces our address with the given one.
+ SocketAddress& operator =(const SocketAddress& addr);
+
+ // Changes the IP of this address to the given one, and clears the hostname.
+ void SetIP(uint32 ip);
+
+ // Changes the hostname of this address to the given one.
+ // Calls Resolve and returns the result.
+ bool SetIP(const std::string& hostname, bool use_dns = true);
+
+ // Sets the IP address while retaining the hostname. Useful for bypassing
+ // DNS for a pre-resolved IP.
+ void SetResolvedIP(uint32 ip);
+
+ // Changes the port of this address to the given one.
+ void SetPort(int port);
+
+ // Returns the IP address.
+ uint32 ip() const;
+
+ // Returns the port part of this address.
+ uint16 port() const;
+
+ // Returns the hostname
+ const std::string& hostname() const { return hostname_; };
+
+ // Returns the IP address in dotted form.
+ std::string IPAsString() const;
+
+ // Returns the port as a string
+ std::string PortAsString() const;
+
+ // Returns a display version of the IP/port.
+ std::string ToString() const;
+
+ // Determines whether this represents a missing / any address.
+ bool IsAny() const;
+
+ // Synomym for missing / any.
+ bool IsNil() const { return IsAny(); }
+
+ // Determines whether the IP address refers to the local host, i.e. within
+ // the range 127.0.0.0/8.
+ bool IsLocalIP() const;
+
+ // Determines whether the IP address is in one of the private ranges:
+ // 127.0.0.0/8 10.0.0.0/8 192.168.0.0/16 172.16.0.0/12.
+ bool IsPrivateIP() const;
+
+ // Determines whether the hostname has been resolved to an IP
+ bool IsUnresolved() const;
+
+ // Attempt to resolve a hostname to IP address.
+ // Returns false if resolution is required but failed.
+ // 'force' will cause re-resolution of hostname.
+ //
+ bool Resolve(bool force = false, bool use_dns = true);
+
+ // Determines whether this address is identical to the given one.
+ bool operator ==(const SocketAddress& addr) const;
+
+ inline bool operator !=(const SocketAddress& addr) const {
+ return !this->operator ==(addr);
+ }
+
+ // Compares based on IP and then port.
+ bool operator <(const SocketAddress& addr) const;
+
+ // Determines whether this address has the same IP as the one given.
+ bool EqualIPs(const SocketAddress& addr) const;
+
+ // Deteremines whether this address has the same port as the one given.
+ bool EqualPorts(const SocketAddress& addr) const;
+
+ // Hashes this address into a small number.
+ size_t Hash() const;
+
+ // Returns the size of this address when written.
+ size_t Size_() const;
+
+ // Writes this address into the given buffer.
+ void Write_(char* buf, int len) const;
+
+ // Reads this address from the given buffer.
+ void Read_(const char* buf, int len);
+
+ // Convert to and from sockaddr_in
+ void ToSockAddr(sockaddr_in* saddr) const;
+ void FromSockAddr(const sockaddr_in& saddr);
+
+ // Converts the IP address given in compact form into dotted form.
+ static std::string IPToString(uint32 ip);
+
+ // Converts the IP address given in dotted form into compact form.
+ // Without 'use_dns', only dotted names (A.B.C.D) are resolved.
+ static uint32 StringToIP(const std::string& str, bool use_dns = true);
+
+ // Get local machine's hostname
+ static std::string GetHostname();
+
+ // Get a list of the local machine's ip addresses
+ static bool GetLocalIPs(std::vector<uint32>& ips);
+
+private:
+ std::string hostname_;
+ uint32 ip_;
+ uint16 port_;
+};
+
+} // namespace talk_base
+
+#endif // TALK_BASE_SOCKETADDRESS_H__
diff --git a/Plugins/jingle/libjingle/talk/base/socketaddresspair.cc b/Plugins/jingle/libjingle/talk/base/socketaddresspair.cc
new file mode 100644
index 0000000..7f190a9
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/socketaddresspair.cc
@@ -0,0 +1,58 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "talk/base/socketaddresspair.h"
+
+namespace talk_base {
+
+SocketAddressPair::SocketAddressPair(
+ const SocketAddress& src, const SocketAddress& dest)
+ : src_(src), dest_(dest) {
+}
+
+
+bool SocketAddressPair::operator ==(const SocketAddressPair& p) const {
+ return (src_ == p.src_) && (dest_ == p.dest_);
+}
+
+bool SocketAddressPair::operator <(const SocketAddressPair& p) const {
+ if (src_ < p.src_)
+ return true;
+ if (p.src_ < src_)
+ return false;
+ if (dest_ < p.dest_)
+ return true;
+ if (p.dest_ < dest_)
+ return false;
+ return false;
+}
+
+size_t SocketAddressPair::Hash() const {
+ return src_.Hash() ^ dest_.Hash();
+}
+
+} // namespace talk_base
diff --git a/Plugins/jingle/libjingle/talk/base/socketaddresspair.h b/Plugins/jingle/libjingle/talk/base/socketaddresspair.h
new file mode 100644
index 0000000..10f5d30
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/socketaddresspair.h
@@ -0,0 +1,58 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TALK_BASE_SOCKETADDRESSPAIR_H__
+#define TALK_BASE_SOCKETADDRESSPAIR_H__
+
+#include "talk/base/socketaddress.h"
+
+namespace talk_base {
+
+// Records a pair (source,destination) of socket addresses. The two addresses
+// identify a connection between two machines. (For UDP, this "connection" is
+// not maintained explicitly in a socket.)
+class SocketAddressPair {
+public:
+ SocketAddressPair() {}
+ SocketAddressPair(const SocketAddress& srs, const SocketAddress& dest);
+
+ const SocketAddress& source() const { return src_; }
+ const SocketAddress& destination() const { return dest_; }
+
+ bool operator ==(const SocketAddressPair& r) const;
+ bool operator <(const SocketAddressPair& r) const;
+
+ size_t Hash() const;
+
+private:
+ SocketAddress src_;
+ SocketAddress dest_;
+};
+
+} // namespace talk_base
+
+#endif // TALK_BASE_SOCKETADDRESSPAIR_H__
diff --git a/Plugins/jingle/libjingle/talk/base/socketfactory.h b/Plugins/jingle/libjingle/talk/base/socketfactory.h
new file mode 100644
index 0000000..2a4aee2
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/socketfactory.h
@@ -0,0 +1,51 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TALK_BASE_SOCKETFACTORY_H__
+#define TALK_BASE_SOCKETFACTORY_H__
+
+#include "talk/base/socket.h"
+#include "talk/base/asyncsocket.h"
+
+namespace talk_base {
+
+class SocketFactory {
+public:
+ virtual ~SocketFactory() {}
+
+ // Returns a new socket for blocking communication. The type can be
+ // SOCK_DGRAM and SOCK_STREAM.
+ virtual Socket* CreateSocket(int type) = 0;
+
+ // Returns a new socket for nonblocking communication. The type can be
+ // SOCK_DGRAM and SOCK_STREAM.
+ virtual AsyncSocket* CreateAsyncSocket(int type) = 0;
+};
+
+} // namespace talk_base
+
+#endif // TALK_BASE_SOCKETFACTORY_H__
diff --git a/Plugins/jingle/libjingle/talk/base/socketpool.cc b/Plugins/jingle/libjingle/talk/base/socketpool.cc
new file mode 100644
index 0000000..614ce89
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/socketpool.cc
@@ -0,0 +1,263 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <errno.h>
+
+#include "talk/base/asyncsocket.h"
+#include "talk/base/logging.h"
+#include "talk/base/socketfactory.h"
+#include "talk/base/socketpool.h"
+#include "talk/base/socketstream.h"
+
+namespace talk_base {
+
+///////////////////////////////////////////////////////////////////////////////
+// StreamCache - Caches a set of open streams, defers creation to a separate
+// StreamPool.
+///////////////////////////////////////////////////////////////////////////////
+
+StreamCache::StreamCache(StreamPool* pool) : pool_(pool) {
+}
+
+StreamCache::~StreamCache() {
+ for (ConnectedList::iterator it = active_.begin(); it != active_.end();
+ ++it) {
+ delete it->second;
+ }
+ for (ConnectedList::iterator it = cached_.begin(); it != cached_.end();
+ ++it) {
+ delete it->second;
+ }
+}
+
+StreamInterface* StreamCache::RequestConnectedStream(
+ const SocketAddress& remote, int* err) {
+ LOG_F(LS_VERBOSE) << "(" << remote.ToString() << ")";
+ for (ConnectedList::iterator it = cached_.begin(); it != cached_.end();
+ ++it) {
+ if (remote == it->first) {
+ it->second->SignalEvent.disconnect(this);
+ // Move from cached_ to active_
+ active_.push_front(*it);
+ cached_.erase(it);
+ if (err)
+ *err = 0;
+ LOG_F(LS_VERBOSE) << "Providing cached stream";
+ return active_.front().second;
+ }
+ }
+ if (StreamInterface* stream = pool_->RequestConnectedStream(remote, err)) {
+ // We track active streams so that we can remember their address
+ active_.push_front(ConnectedStream(remote, stream));
+ LOG_F(LS_VERBOSE) << "Providing new stream";
+ return active_.front().second;
+ }
+ return NULL;
+}
+
+void StreamCache::ReturnConnectedStream(StreamInterface* stream) {
+ for (ConnectedList::iterator it = active_.begin(); it != active_.end();
+ ++it) {
+ if (stream == it->second) {
+ LOG_F(LS_VERBOSE) << "(" << it->first.ToString() << ")";
+ if (stream->GetState() == SS_CLOSED) {
+ // Return closed streams
+ LOG_F(LS_VERBOSE) << "Returning closed stream";
+ pool_->ReturnConnectedStream(it->second);
+ } else {
+ // Monitor open streams
+ stream->SignalEvent.connect(this, &StreamCache::OnStreamEvent);
+ LOG_F(LS_VERBOSE) << "Caching stream";
+ cached_.push_front(*it);
+ }
+ active_.erase(it);
+ return;
+ }
+ }
+ ASSERT(false);
+}
+
+void StreamCache::OnStreamEvent(StreamInterface* stream, int events, int err) {
+ if ((events & SE_CLOSE) == 0) {
+ LOG_F(LS_WARNING) << "(" << events << ", " << err
+ << ") received non-close event";
+ return;
+ }
+ for (ConnectedList::iterator it = cached_.begin(); it != cached_.end();
+ ++it) {
+ if (stream == it->second) {
+ LOG_F(LS_VERBOSE) << "(" << it->first.ToString() << ")";
+ // We don't cache closed streams, so return it.
+ it->second->SignalEvent.disconnect(this);
+ LOG_F(LS_VERBOSE) << "Returning closed stream";
+ pool_->ReturnConnectedStream(it->second);
+ cached_.erase(it);
+ return;
+ }
+ }
+ ASSERT(false);
+}
+
+//////////////////////////////////////////////////////////////////////
+// NewSocketPool
+//////////////////////////////////////////////////////////////////////
+
+NewSocketPool::NewSocketPool(SocketFactory* factory) : factory_(factory) {
+}
+
+NewSocketPool::~NewSocketPool() {
+ for (size_t i = 0; i < used_.size(); ++i) {
+ delete used_[i];
+ }
+}
+
+StreamInterface*
+NewSocketPool::RequestConnectedStream(const SocketAddress& remote, int* err) {
+ AsyncSocket* socket = factory_->CreateAsyncSocket(SOCK_STREAM);
+ if (!socket) {
+ ASSERT(false);
+ if (err)
+ *err = -1;
+ return NULL;
+ }
+ if ((socket->Connect(remote) != 0) && !socket->IsBlocking()) {
+ if (err)
+ *err = socket->GetError();
+ delete socket;
+ return NULL;
+ }
+ if (err)
+ *err = 0;
+ return new SocketStream(socket);
+}
+
+void
+NewSocketPool::ReturnConnectedStream(StreamInterface* stream) {
+ used_.push_back(stream);
+}
+
+//////////////////////////////////////////////////////////////////////
+// ReuseSocketPool
+//////////////////////////////////////////////////////////////////////
+
+ReuseSocketPool::ReuseSocketPool(SocketFactory* factory, AsyncSocket* socket)
+ : factory_(factory), stream_(NULL) {
+ stream_ = socket ? new SocketStream(socket) : NULL;
+}
+
+ReuseSocketPool::~ReuseSocketPool() {
+ delete stream_;
+}
+
+void
+ReuseSocketPool::setSocket(AsyncSocket* socket) {
+ ASSERT(false); // TODO: need ref-counting to make this work
+ delete stream_;
+ stream_ = socket ? new SocketStream(socket) : NULL;
+}
+
+StreamInterface*
+ReuseSocketPool::RequestConnectedStream(const SocketAddress& remote, int* err) {
+ if (!stream_) {
+ LOG(LS_INFO) << "ReuseSocketPool - Creating new socket";
+ AsyncSocket* socket = factory_->CreateAsyncSocket(SOCK_STREAM);
+ if (!socket) {
+ ASSERT(false);
+ if (err)
+ *err = -1;
+ return NULL;
+ }
+ stream_ = new SocketStream(socket);
+ }
+ if ((stream_->GetState() == SS_OPEN) &&
+ (stream_->GetSocket()->GetRemoteAddress() == remote)) {
+ LOG(LS_INFO) << "ReuseSocketPool - Reusing connection to: "
+ << remote.ToString();
+ } else {
+ stream_->Close();
+ if ((stream_->GetSocket()->Connect(remote) != 0)
+ && !stream_->GetSocket()->IsBlocking()) {
+ if (err)
+ *err = stream_->GetSocket()->GetError();
+ return NULL;
+ } else {
+ LOG(LS_INFO) << "ReuseSocketPool - Opening connection to: "
+ << remote.ToString();
+ }
+ }
+ if (err)
+ *err = 0;
+ return stream_;
+}
+
+void
+ReuseSocketPool::ReturnConnectedStream(StreamInterface* stream) {
+ // Note: this might not be true with the advent of setSocket
+ ASSERT(stream == stream_);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// LoggingPoolAdapter - Adapts a StreamPool to supply streams with attached
+// LoggingAdapters.
+///////////////////////////////////////////////////////////////////////////////
+
+LoggingPoolAdapter::LoggingPoolAdapter(
+ StreamPool* pool, LoggingSeverity level, const std::string& label,
+ bool binary_mode)
+ : pool_(pool), level_(level), label_(label), binary_mode_(binary_mode) {
+}
+
+LoggingPoolAdapter::~LoggingPoolAdapter() {
+ for (StreamList::iterator it = recycle_bin_.begin();
+ it != recycle_bin_.end(); ++it) {
+ delete *it;
+ }
+}
+
+StreamInterface* LoggingPoolAdapter::RequestConnectedStream(
+ const SocketAddress& remote, int* err) {
+ if (StreamInterface* stream = pool_->RequestConnectedStream(remote, err)) {
+ if (recycle_bin_.empty()) {
+ return new LoggingAdapter(stream, level_, label_, binary_mode_);
+ }
+ LoggingAdapter* logging = recycle_bin_.front();
+ recycle_bin_.pop_front();
+ logging->Attach(stream);
+ return logging;
+ }
+ return NULL;
+}
+
+void LoggingPoolAdapter::ReturnConnectedStream(StreamInterface* stream) {
+ LoggingAdapter* logging = static_cast<LoggingAdapter*>(stream);
+ pool_->ReturnConnectedStream(logging->Detach());
+ recycle_bin_.push_back(logging);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+} // namespace talk_base
diff --git a/Plugins/jingle/libjingle/talk/base/socketpool.h b/Plugins/jingle/libjingle/talk/base/socketpool.h
new file mode 100644
index 0000000..edeebaa
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/socketpool.h
@@ -0,0 +1,161 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TALK_BASE_SOCKETPOOL_H__
+#define TALK_BASE_SOCKETPOOL_H__
+
+#include <deque>
+#include <vector>
+#include "talk/base/logging.h"
+#include "talk/base/sigslot.h"
+
+namespace talk_base {
+
+class AsyncSocket;
+class LoggingAdapter;
+class SocketAddress;
+class SocketFactory;
+class SocketStream;
+class StreamInterface;
+
+//////////////////////////////////////////////////////////////////////
+// StreamPool
+//////////////////////////////////////////////////////////////////////
+
+class StreamPool {
+public:
+ virtual ~StreamPool() { }
+
+ virtual StreamInterface* RequestConnectedStream(const SocketAddress& remote,
+ int* err) = 0;
+ virtual void ReturnConnectedStream(StreamInterface* stream) = 0;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// StreamCache - Caches a set of open streams, defers creation/destruction to
+// the supplied StreamPool.
+///////////////////////////////////////////////////////////////////////////////
+
+class StreamCache : public StreamPool, public sigslot::has_slots<> {
+public:
+ StreamCache(StreamPool* pool);
+ virtual ~StreamCache();
+
+ // StreamPool Interface
+ virtual StreamInterface* RequestConnectedStream(const SocketAddress& remote,
+ int* err);
+ virtual void ReturnConnectedStream(StreamInterface* stream);
+
+private:
+ typedef std::pair<SocketAddress, StreamInterface*> ConnectedStream;
+ typedef std::list<ConnectedStream> ConnectedList;
+
+ void OnStreamEvent(StreamInterface* stream, int events, int err);
+
+ // We delegate stream creation and deletion to this pool.
+ StreamPool* pool_;
+ // Streams that are in use (returned from RequestConnectedStream).
+ ConnectedList active_;
+ // Streams which were returned to us, but are still open.
+ ConnectedList cached_;
+};
+
+//////////////////////////////////////////////////////////////////////
+// NewSocketPool //
+///////////////////
+// Creates a new PTcpSocket every time
+//////////////////////////////////////////////////////////////////////
+
+class NewSocketPool : public StreamPool {
+public:
+ NewSocketPool(SocketFactory* factory);
+ virtual ~NewSocketPool();
+
+ // StreamPool Interface
+ virtual StreamInterface* RequestConnectedStream(const SocketAddress& remote,
+ int* err);
+ virtual void ReturnConnectedStream(StreamInterface* stream);
+
+private:
+ SocketFactory* factory_;
+ std::vector<StreamInterface*> used_;
+};
+
+//////////////////////////////////////////////////////////////////////
+// ReuseSocketPool //
+/////////////////////
+// Pass a PTcpSocket chain to the constructor, and if the connection
+// is still open, it will be reused.
+//////////////////////////////////////////////////////////////////////
+
+class ReuseSocketPool : public StreamPool {
+public:
+ ReuseSocketPool(SocketFactory* factory, AsyncSocket* socket = 0);
+ virtual ~ReuseSocketPool();
+
+ void setSocket(AsyncSocket* socket);
+
+ // StreamPool Interface
+ virtual StreamInterface* RequestConnectedStream(const SocketAddress& remote,
+ int* err);
+ virtual void ReturnConnectedStream(StreamInterface* stream);
+
+private:
+ SocketFactory* factory_;
+ SocketStream* stream_;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// LoggingPoolAdapter - Adapts a StreamPool to supply streams with attached
+// LoggingAdapters.
+///////////////////////////////////////////////////////////////////////////////
+
+class LoggingPoolAdapter : public StreamPool {
+public:
+ LoggingPoolAdapter(StreamPool* pool, LoggingSeverity level,
+ const std::string& label, bool binary_mode);
+ virtual ~LoggingPoolAdapter();
+
+ // StreamPool Interface
+ virtual StreamInterface* RequestConnectedStream(const SocketAddress& remote,
+ int* err);
+ virtual void ReturnConnectedStream(StreamInterface* stream);
+
+private:
+ StreamPool* pool_;
+ LoggingSeverity level_;
+ std::string label_;
+ bool binary_mode_;
+ typedef std::deque<LoggingAdapter*> StreamList;
+ StreamList recycle_bin_;
+};
+
+//////////////////////////////////////////////////////////////////////
+
+} // namespace talk_base
+
+#endif // TALK_BASE_SOCKETPOOL_H__
diff --git a/Plugins/jingle/libjingle/talk/base/socketserver.h b/Plugins/jingle/libjingle/talk/base/socketserver.h
new file mode 100644
index 0000000..e06dd6b
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/socketserver.h
@@ -0,0 +1,57 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TALK_BASE_SOCKETSERVER_H__
+#define TALK_BASE_SOCKETSERVER_H__
+
+#include "talk/base/socketfactory.h"
+
+namespace talk_base {
+
+const int kForever = -1;
+
+// Provides the ability to wait for activity on a set of sockets. The Thread
+// class provides a nice wrapper on a socket server.
+//
+// The server is also a socket factory. The sockets it creates will be
+// notified of asynchronous I/O from this server's Wait method.
+class SocketServer : public SocketFactory {
+public:
+
+ // Sleeps until:
+ // 1) cms milliseconds have elapsed (unless cms == kForever)
+ // 2) WakeUp() is called
+ // While sleeping, I/O is performed if process_io is true.
+ virtual bool Wait(int cms, bool process_io) = 0;
+
+ // Causes the current wait (if one is in progress) to wake up.
+ virtual void WakeUp() = 0;
+};
+
+} // namespace talk_base
+
+#endif // TALK_BASE_SOCKETSERVER_H__
diff --git a/Plugins/jingle/libjingle/talk/base/socketstream.h b/Plugins/jingle/libjingle/talk/base/socketstream.h
new file mode 100644
index 0000000..4d56b75
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/socketstream.h
@@ -0,0 +1,153 @@
+/*
+ * libjingle
+ * Copyright 2004--2006, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TALK_BASE_SOCKET_STREAM_H__
+#define TALK_BASE_SOCKET_STREAM_H__
+
+#include "talk/base/asyncsocket.h"
+#include "talk/base/common.h"
+#include "talk/base/stream.h"
+
+namespace talk_base {
+
+///////////////////////////////////////////////////////////////////////////////
+
+class SocketStream : public StreamInterface, public sigslot::has_slots<> {
+ public:
+ SocketStream(AsyncSocket* socket) : socket_(NULL) {
+ Attach(socket);
+ }
+ virtual ~SocketStream() { delete socket_; }
+
+ void Attach(AsyncSocket* socket) {
+ if (socket_)
+ delete socket_;
+ socket_ = socket;
+ if (socket_) {
+ socket_->SignalConnectEvent.connect(this, &SocketStream::OnConnectEvent);
+ socket_->SignalReadEvent.connect(this, &SocketStream::OnReadEvent);
+ socket_->SignalWriteEvent.connect(this, &SocketStream::OnWriteEvent);
+ socket_->SignalCloseEvent.connect(this, &SocketStream::OnCloseEvent);
+ }
+ }
+
+ AsyncSocket* Detach() {
+ AsyncSocket* socket = socket_;
+ if (socket_) {
+ socket_->SignalConnectEvent.disconnect(this);
+ socket_->SignalReadEvent.disconnect(this);
+ socket_->SignalWriteEvent.disconnect(this);
+ socket_->SignalCloseEvent.disconnect(this);
+ socket_ = NULL;
+ }
+ return socket;
+ }
+
+ AsyncSocket* GetSocket() { return socket_; }
+
+ virtual StreamState GetState() const {
+ ASSERT(socket_ != NULL);
+ switch (socket_->GetState()) {
+ case Socket::CS_CONNECTED:
+ return SS_OPEN;
+ case Socket::CS_CONNECTING:
+ return SS_OPENING;
+ case Socket::CS_CLOSED:
+ default:
+ return SS_CLOSED;
+ }
+ }
+
+ virtual StreamResult Read(void* buffer, size_t buffer_len,
+ size_t* read, int* error) {
+ ASSERT(socket_ != NULL);
+ int result = socket_->Recv(buffer, buffer_len);
+ if (result < 0) {
+ if (socket_->IsBlocking())
+ return SR_BLOCK;
+ if (error)
+ *error = socket_->GetError();
+ return SR_ERROR;
+ }
+ if ((result > 0) || (buffer_len == 0)) {
+ if (read)
+ *read = result;
+ return SR_SUCCESS;
+ }
+ return SR_EOS;
+ }
+
+ virtual StreamResult Write(const void* data, size_t data_len,
+ size_t* written, int* error) {
+ ASSERT(socket_ != NULL);
+ int result = socket_->Send(data, data_len);
+ if (result < 0) {
+ if (socket_->IsBlocking())
+ return SR_BLOCK;
+ if (error)
+ *error = socket_->GetError();
+ return SR_ERROR;
+ }
+ if (written)
+ *written = result;
+ return SR_SUCCESS;
+ }
+
+ virtual void Close() { ASSERT(socket_ != NULL); socket_->Close(); }
+
+ virtual bool GetSize(size_t* size) const { return false; }
+ virtual bool ReserveSize(size_t size) { return true; }
+ virtual bool Rewind() { return false; }
+
+ private:
+ void OnConnectEvent(AsyncSocket* socket) {
+ ASSERT(socket == socket_);
+ SignalEvent(this, SE_OPEN | SE_READ | SE_WRITE, 0);
+ }
+ void OnReadEvent(AsyncSocket* socket) {
+ ASSERT(socket == socket_);
+ SignalEvent(this, SE_READ, 0);
+ }
+ void OnWriteEvent(AsyncSocket* socket) {
+ ASSERT(socket == socket_);
+ SignalEvent(this, SE_WRITE, 0);
+ }
+ void OnCloseEvent(AsyncSocket* socket, int err) {
+ ASSERT(socket == socket_);
+ SignalEvent(this, SE_CLOSE, err);
+ }
+
+ AsyncSocket* socket_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(SocketStream);
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+} // namespace talk_base
+
+#endif // TALK_BASE_SOCKET_STREAM_H__
diff --git a/Plugins/jingle/libjingle/talk/base/ssladapter.cc b/Plugins/jingle/libjingle/talk/base/ssladapter.cc
new file mode 100644
index 0000000..68fe038
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/ssladapter.cc
@@ -0,0 +1,191 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "talk/base/ssladapter.h"
+
+#if !defined(SSL_USE_SCHANNEL) && !defined(SSL_USE_OPENSSL)
+#ifdef WIN32
+#define SSL_USE_SCHANNEL 1
+#else // !WIN32
+#define SSL_USE_OPENSSL 1
+#endif // !WIN32
+#endif
+
+#if SSL_USE_SCHANNEL
+#include "schanneladapter.h"
+namespace talk_base {
+ typedef SChannelAdapter DefaultSSLAdapter;
+}
+#endif // SSL_USE_SCHANNEL
+
+#if SSL_USE_OPENSSL
+#include <openssl/crypto.h>
+#include <openssl/rand.h>
+#include <openssl/ssl.h>
+#include "openssladapter.h"
+namespace talk_base {
+ typedef OpenSSLAdapter DefaultSSLAdapter;
+}
+#if defined(WIN32)
+ #define MUTEX_TYPE HANDLE
+ #define MUTEX_SETUP(x) (x) = CreateMutex(NULL, FALSE, NULL)
+ #define MUTEX_CLEANUP(x) CloseHandle(x)
+ #define MUTEX_LOCK(x) WaitForSingleObject((x), INFINITE)
+ #define MUTEX_UNLOCK(x) ReleaseMutex(x)
+ #define THREAD_ID GetCurrentThreadId()
+#elif defined(_POSIX_THREADS)
+ // _POSIX_THREADS is normally defined in unistd.h if pthreads are available
+ // on your platform.
+ #define MUTEX_TYPE pthread_mutex_t
+ #define MUTEX_SETUP(x) pthread_mutex_init(&(x), NULL)
+ #define MUTEX_CLEANUP(x) pthread_mutex_destroy(&(x))
+ #define MUTEX_LOCK(x) pthread_mutex_lock(&(x))
+ #define MUTEX_UNLOCK(x) pthread_mutex_unlock(&(x))
+ #define THREAD_ID pthread_self()
+#else
+ #error You must define mutex operations appropriate for your platform!
+#endif
+
+struct CRYPTO_dynlock_value {
+ MUTEX_TYPE mutex;
+};
+
+#endif // SSL_USE_OPENSSL
+
+///////////////////////////////////////////////////////////////////////////////
+
+namespace talk_base {
+
+SSLAdapter*
+SSLAdapter::Create(AsyncSocket* socket) {
+ return new DefaultSSLAdapter(socket);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#if SSL_USE_OPENSSL
+
+
+
+// This array will store all of the mutexes available to OpenSSL.
+static MUTEX_TYPE* mutex_buf = NULL;
+
+static void locking_function(int mode, int n, const char * file, int line) {
+ if (mode & CRYPTO_LOCK) {
+ MUTEX_LOCK(mutex_buf[n]);
+ } else {
+ MUTEX_UNLOCK(mutex_buf[n]);
+ }
+}
+
+static pthread_t id_function() {
+ return THREAD_ID;
+}
+
+static CRYPTO_dynlock_value* dyn_create_function(const char* file, int line) {
+ CRYPTO_dynlock_value* value = new CRYPTO_dynlock_value;
+ if (!value)
+ return NULL;
+ MUTEX_SETUP(value->mutex);
+ return value;
+}
+
+static void dyn_lock_function(int mode, CRYPTO_dynlock_value* l,
+ const char* file, int line) {
+ if (mode & CRYPTO_LOCK) {
+ MUTEX_LOCK(l->mutex);
+ } else {
+ MUTEX_UNLOCK(l->mutex);
+ }
+}
+
+static void dyn_destroy_function(CRYPTO_dynlock_value* l,
+ const char* file, int line) {
+ MUTEX_CLEANUP(l->mutex);
+ delete l;
+}
+
+bool InitializeSSL() {
+ if (!InitializeSSLThread() || !SSL_library_init())
+ return false;
+ SSL_load_error_strings();
+ ERR_load_BIO_strings();
+ OpenSSL_add_all_algorithms();
+ RAND_poll();
+ return true;
+}
+
+bool InitializeSSLThread() {
+ mutex_buf = new MUTEX_TYPE[CRYPTO_num_locks()];
+ if (!mutex_buf)
+ return false;
+ for (int i = 0; i < CRYPTO_num_locks(); ++i)
+ MUTEX_SETUP(mutex_buf[i]);
+
+ // we need to cast our id_function to return an unsigned long -- pthread_t is a pointer
+ CRYPTO_set_id_callback((unsigned long (*)())id_function);
+ CRYPTO_set_locking_callback(locking_function);
+ CRYPTO_set_dynlock_create_callback(dyn_create_function);
+ CRYPTO_set_dynlock_lock_callback(dyn_lock_function);
+ CRYPTO_set_dynlock_destroy_callback(dyn_destroy_function);
+ return true;
+}
+
+bool CleanupSSL() {
+ if (!mutex_buf)
+ return false;
+ CRYPTO_set_id_callback(NULL);
+ CRYPTO_set_locking_callback(NULL);
+ CRYPTO_set_dynlock_create_callback(NULL);
+ CRYPTO_set_dynlock_lock_callback(NULL);
+ CRYPTO_set_dynlock_destroy_callback(NULL);
+ for (int i = 0; i < CRYPTO_num_locks(); ++i)
+ MUTEX_CLEANUP(mutex_buf[i]);
+ delete [] mutex_buf;
+ mutex_buf = NULL;
+ return true;
+}
+
+#else // !SSL_USE_OPENSSL
+
+bool InitializeSSL() {
+ return true;
+}
+
+bool InitializeSSLThread() {
+ return true;
+}
+
+bool CleanupSSL() {
+ return true;
+}
+
+#endif // !SSL_USE_OPENSSL
+
+///////////////////////////////////////////////////////////////////////////////
+
+} // namespace talk_base
diff --git a/Plugins/jingle/libjingle/talk/base/ssladapter.h b/Plugins/jingle/libjingle/talk/base/ssladapter.h
new file mode 100644
index 0000000..0f0155c
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/ssladapter.h
@@ -0,0 +1,74 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TALK_BASE_SSLADAPTER_H__
+#define TALK_BASE_SSLADAPTER_H__
+
+#include "talk/base/asyncsocket.h"
+
+namespace talk_base {
+
+///////////////////////////////////////////////////////////////////////////////
+
+class SSLAdapter : public AsyncSocketAdapter {
+public:
+ SSLAdapter(AsyncSocket* socket)
+ : AsyncSocketAdapter(socket), ignore_bad_cert_(false) { }
+
+ bool ignore_bad_cert() const { return ignore_bad_cert_; }
+ void set_ignore_bad_cert(bool ignore) { ignore_bad_cert_ = ignore; }
+
+ // StartSSL returns 0 if successful.
+ // If StartSSL is called while the socket is closed or connecting, the SSL
+ // negotiation will begin as soon as the socket connects.
+ virtual int StartSSL(const char* hostname, bool restartable) = 0;
+
+ // Create the default SSL adapter for this platform
+ static SSLAdapter* Create(AsyncSocket* socket);
+
+private:
+ // If true, the server certificate need not match the configured hostname.
+ bool ignore_bad_cert_;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+// Call this on the main thread, before using SSL.
+// Call CleanupSSLThread when finished with SSL.
+bool InitializeSSL();
+
+// Call to initialize additional threads.
+bool InitializeSSLThread();
+
+// Call to cleanup additional threads, and also the main thread.
+bool CleanupSSL();
+
+///////////////////////////////////////////////////////////////////////////////
+
+} // namespace talk_base
+
+#endif // TALK_BASE_SSLADAPTER_H__
diff --git a/Plugins/jingle/libjingle/talk/base/stl_decl.h b/Plugins/jingle/libjingle/talk/base/stl_decl.h
new file mode 100644
index 0000000..193e7af
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/stl_decl.h
@@ -0,0 +1,84 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TALK_BASE_STL_DECL_H__
+#define TALK_BASE_STL_DECL_H__
+
+#if defined(_MSC_VER) && _MSC_VER <= 1200 // 1200 == VC++ 6.0
+#pragma warning(disable:4786)
+#endif
+
+#include <sys/types.h>
+
+namespace std {
+ template <class Key> struct hash;
+ template <class Key> struct equal_to;
+ template <class Key> struct less;
+ template <class T> class allocator;
+ template <class Key, class Val,
+ class Compare,
+ class Alloc> class map;
+ template <class T, class Alloc> class vector;
+ template <class T, class Alloc> class list;
+ template <class T, class Alloc> class slist;
+ template <class T, class Sequence> class stack;
+ template <class T, class Sequence> class queue;
+ template <class T, class Sequence, class Compare> class priority_queue;
+ template <class T1, class T2> struct pair;
+ template <class Key, class Compare, class Alloc> class set;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// Workaround declaration problem with defaults
+/////////////////////////////////////////////////////////////////////////////
+
+#if defined(_MSC_VER) && _MSC_VER <= 1200 // 1200 == VC++ 6.0
+
+#define STD_MAP(T1, T2) \
+ std::map<T1 , T2, std::less<T1>, std::allocator<T2> >
+
+#define STD_VECTOR(T1) \
+ std::vector<T1, std::allocator<T1> >
+
+#define STD_SET(T1) \
+ std::set<T1, std::less<T1>, std::allocator<T1> >
+
+#else
+
+#define STD_MAP(T1, T2) \
+ std::map<T1, T2, std::less<T1>, std::allocator<std::pair<const T1, T2 > > >
+
+#define STD_VECTOR(T1) \
+ std::vector<T1, std::allocator<T1> >
+
+#define STD_SET(T1) \
+ std::set<T1, std::less<T1>, std::allocator<T1> >
+
+#endif
+
+
+#endif // TALK_BASE_STL_DECL_H__
diff --git a/Plugins/jingle/libjingle/talk/base/stream.cc b/Plugins/jingle/libjingle/talk/base/stream.cc
new file mode 100644
index 0000000..50d49a6
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/stream.cc
@@ -0,0 +1,664 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <string>
+#include "talk/base/basictypes.h"
+#include "talk/base/common.h"
+#include "talk/base/stream.h"
+#include "talk/base/stringencode.h"
+
+#ifdef WIN32
+#include "talk/base/win32.h"
+#define fileno _fileno
+#endif
+
+namespace talk_base {
+
+///////////////////////////////////////////////////////////////////////////////
+
+StreamResult StreamInterface::WriteAll(const void* data, size_t data_len,
+ size_t* written, int* error) {
+ StreamResult result = SR_SUCCESS;
+ size_t total_written = 0, current_written;
+ while (total_written < data_len) {
+ result = Write(static_cast<const char*>(data) + total_written,
+ data_len - total_written, &current_written, error);
+ if (result != SR_SUCCESS)
+ break;
+ total_written += current_written;
+ }
+ if (written)
+ *written = total_written;
+ return result;
+}
+
+StreamResult StreamInterface::ReadAll(void* buffer, size_t buffer_len,
+ size_t* read, int* error) {
+ StreamResult result = SR_SUCCESS;
+ size_t total_read = 0, current_read;
+ while (total_read < buffer_len) {
+ result = Read(static_cast<char*>(buffer) + total_read,
+ buffer_len - total_read, &current_read, error);
+ if (result != SR_SUCCESS)
+ break;
+ total_read += current_read;
+ }
+ if (read)
+ *read = total_read;
+ return result;
+}
+
+StreamResult StreamInterface::ReadLine(std::string* line) {
+ StreamResult result = SR_SUCCESS;
+ while (true) {
+ char ch;
+ result = Read(&ch, sizeof(ch), NULL, NULL);
+ if (result != SR_SUCCESS) {
+ break;
+ }
+ if (ch == '\n') {
+ break;
+ }
+ line->push_back(ch);
+ }
+ if (!line->empty()) { // give back the line we've collected so far with
+ result = SR_SUCCESS; // a success code. Otherwise return the last code
+ }
+ return result;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// StreamTap
+///////////////////////////////////////////////////////////////////////////////
+
+StreamTap::StreamTap(StreamInterface* stream, StreamInterface* tap)
+: StreamAdapterInterface(stream), tap_(NULL), tap_result_(SR_SUCCESS),
+ tap_error_(0)
+{
+ AttachTap(tap);
+}
+
+void StreamTap::AttachTap(StreamInterface* tap) {
+ tap_.reset(tap);
+}
+
+StreamInterface* StreamTap::DetachTap() {
+ return tap_.release();
+}
+
+StreamResult StreamTap::GetTapResult(int* error) {
+ if (error) {
+ *error = tap_error_;
+ }
+ return tap_result_;
+}
+
+StreamResult StreamTap::Read(void* buffer, size_t buffer_len,
+ size_t* read, int* error) {
+ size_t backup_read;
+ if (!read) {
+ read = &backup_read;
+ }
+ StreamResult res = StreamAdapterInterface::Read(buffer, buffer_len,
+ read, error);
+ if ((res == SR_SUCCESS) && (tap_result_ == SR_SUCCESS)) {
+ tap_result_ = tap_->WriteAll(buffer, *read, NULL, &tap_error_);
+ }
+ return res;
+}
+
+StreamResult StreamTap::Write(const void* data, size_t data_len,
+ size_t* written, int* error) {
+ size_t backup_written;
+ if (!written) {
+ written = &backup_written;
+ }
+ StreamResult res = StreamAdapterInterface::Write(data, data_len,
+ written, error);
+ if ((res == SR_SUCCESS) && (tap_result_ == SR_SUCCESS)) {
+ tap_result_ = tap_->WriteAll(data, *written, NULL, &tap_error_);
+ }
+ return res;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// NullStream
+///////////////////////////////////////////////////////////////////////////////
+
+NullStream::NullStream() {
+}
+
+NullStream::~NullStream() {
+}
+
+StreamState NullStream::GetState() const {
+ return SS_OPEN;
+}
+
+StreamResult NullStream::Read(void* buffer, size_t buffer_len,
+ size_t* read, int* error) {
+ if (error) *error = -1;
+ return SR_ERROR;
+}
+
+
+StreamResult NullStream::Write(const void* data, size_t data_len,
+ size_t* written, int* error) {
+ if (written) *written = data_len;
+ return SR_SUCCESS;
+}
+
+void NullStream::Close() {
+}
+
+bool NullStream::GetSize(size_t* size) const {
+ if (size)
+ *size = 0;
+ return true;
+}
+
+bool NullStream::ReserveSize(size_t size) {
+ return true;
+}
+
+bool NullStream::Rewind() {
+ return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// FileStream
+///////////////////////////////////////////////////////////////////////////////
+
+FileStream::FileStream() : file_(NULL) {
+}
+
+FileStream::~FileStream() {
+ FileStream::Close();
+}
+
+bool FileStream::Open(const std::string& filename, const char* mode) {
+ Close();
+#ifdef WIN32
+ int filenamelen = MultiByteToWideChar(CP_UTF8, 0, filename.c_str(),
+ filename.length() + 1, NULL, 0);
+ int modelen = MultiByteToWideChar(CP_UTF8, 0, mode, -1, NULL, 0);
+ wchar_t *wfilename = new wchar_t[filenamelen+4]; // 4 for "\\?\"
+ wchar_t *wfilename_dest = wfilename;
+ wchar_t *wmode = new wchar_t[modelen];
+
+ if (!filename.empty() && (filename[0] != '\\')) {
+ wcscpy(wfilename, L"\\\\?\\");
+ wfilename_dest = wfilename + 4;
+ }
+
+ if ((MultiByteToWideChar(CP_UTF8, 0, filename.c_str(), filename.length() + 1,
+ wfilename_dest, filenamelen) > 0) &&
+ (MultiByteToWideChar(CP_UTF8, 0, mode, -1, wmode, modelen) > 0)) {
+ file_ = _wfopen(wfilename, wmode);
+ } else {
+ file_ = NULL;
+ }
+
+ delete[] wfilename;
+ delete[] wmode;
+#else
+ file_ = fopen(filename.c_str(), mode);
+#endif
+ return (file_ != NULL);
+}
+
+bool FileStream::OpenShare(const std::string& filename, const char* mode,
+ int shflag) {
+ Close();
+#ifdef WIN32
+ int filenamelen = MultiByteToWideChar(CP_UTF8, 0, filename.c_str(),
+ filename.length() + 1, NULL, 0);
+ int modelen = MultiByteToWideChar(CP_UTF8, 0, mode, -1, NULL, 0);
+ wchar_t *wfilename = new wchar_t[filenamelen+4]; // 4 for "\\?\"
+ wchar_t *wfilename_dest = wfilename;
+ wchar_t *wmode = new wchar_t[modelen];
+
+ if (!filename.empty() && (filename[0] != '\\')) {
+ wcscpy(wfilename, L"\\\\?\\");
+ wfilename_dest = wfilename + 4;
+ }
+
+ if ((MultiByteToWideChar(CP_UTF8, 0, filename.c_str(), filename.length() + 1,
+ wfilename_dest, filenamelen) > 0) &&
+ (MultiByteToWideChar(CP_UTF8, 0, mode, -1, wmode, modelen) > 0)) {
+ file_ = _wfsopen(wfilename, wmode, shflag);
+ } else {
+ file_ = NULL;
+ }
+
+ delete[] wfilename;
+ delete[] wmode;
+#else
+ return Open(filename, mode);
+#endif
+ return (file_ != NULL);
+}
+
+bool FileStream::DisableBuffering() {
+ if (!file_)
+ return false;
+ return (setvbuf(file_, NULL, _IONBF, 0) == 0);
+}
+
+StreamState FileStream::GetState() const {
+ return (file_ == NULL) ? SS_CLOSED : SS_OPEN;
+}
+
+StreamResult FileStream::Read(void* buffer, size_t buffer_len,
+ size_t* read, int* error) {
+ if (!file_)
+ return SR_EOS;
+ size_t result = fread(buffer, 1, buffer_len, file_);
+ if ((result == 0) && (buffer_len > 0)) {
+ if (feof(file_))
+ return SR_EOS;
+ if (error)
+ *error = errno;
+ return SR_ERROR;
+ }
+ if (read)
+ *read = result;
+ return SR_SUCCESS;
+}
+
+StreamResult FileStream::Write(const void* data, size_t data_len,
+ size_t* written, int* error) {
+ if (!file_)
+ return SR_EOS;
+ size_t result = fwrite(data, 1, data_len, file_);
+ if ((result == 0) && (data_len > 0)) {
+ if (error)
+ *error = errno;
+ return SR_ERROR;
+ }
+ if (written)
+ *written = result;
+ return SR_SUCCESS;
+}
+
+void FileStream::Close() {
+ if (file_) {
+ fclose(file_);
+ file_ = NULL;
+ }
+}
+
+bool FileStream::SetPosition(size_t position) {
+ if (!file_)
+ return false;
+ return (fseek(file_, position, SEEK_SET) == 0);
+}
+
+bool FileStream::GetPosition(size_t * position) const {
+ ASSERT(position != NULL);
+ if (!file_ || !position)
+ return false;
+ long result = ftell(file_);
+ if (result < 0)
+ return false;
+ *position = result;
+ return true;
+}
+
+bool FileStream::GetSize(size_t * size) const {
+ ASSERT(size != NULL);
+ if (!file_ || !size)
+ return false;
+ struct stat file_stats;
+ if (fstat(fileno(file_), &file_stats) != 0)
+ return false;
+ *size = file_stats.st_size;
+ return true;
+}
+
+bool FileStream::ReserveSize(size_t size) {
+ // TODO: extend the file to the proper length
+ return true;
+}
+
+bool FileStream::GetSize(const std::string& filename, size_t* size) {
+ struct stat file_stats;
+ if (stat(filename.c_str(), &file_stats) != 0)
+ return false;
+ *size = file_stats.st_size;
+ return true;
+}
+
+int FileStream::Flush() {
+ if (file_) {
+ return fflush (file_);
+ }
+ // try to flush empty file?
+ ASSERT(false);
+ return 0;
+}
+///////////////////////////////////////////////////////////////////////////////
+
+
+MemoryStream::MemoryStream()
+ : allocated_length_(0), buffer_(NULL), data_length_(0), seek_position_(0) {
+}
+
+MemoryStream::MemoryStream(const char* data)
+ : allocated_length_(0), buffer_(NULL), data_length_(0), seek_position_(0) {
+ SetContents(data, strlen(data));
+}
+
+MemoryStream::MemoryStream(const char* data, size_t length)
+ : allocated_length_(0), buffer_(NULL), data_length_(0), seek_position_(0) {
+ SetContents(data, length);
+}
+
+MemoryStream::~MemoryStream() {
+ delete [] buffer_;
+}
+
+void MemoryStream::SetContents(const char* data, size_t length) {
+ delete [] buffer_;
+ data_length_ = allocated_length_ = length;
+ buffer_ = new char[allocated_length_];
+ memcpy(buffer_, data, data_length_);
+}
+
+StreamState MemoryStream::GetState() const {
+ return SS_OPEN;
+}
+
+StreamResult MemoryStream::Read(void *buffer, size_t bytes,
+ size_t *bytes_read, int *error) {
+ if (seek_position_ >= data_length_) {
+ // At end of stream
+ if (error) {
+ *error = EOF;
+ }
+ return SR_EOS;
+ }
+
+ size_t remaining_length = data_length_ - seek_position_;
+ if (bytes > remaining_length) {
+ // Read partial buffer
+ bytes = remaining_length;
+ }
+ memcpy(buffer, &buffer_[seek_position_], bytes);
+ seek_position_ += bytes;
+ if (bytes_read) {
+ *bytes_read = bytes;
+ }
+ return SR_SUCCESS;
+}
+
+StreamResult MemoryStream::Write(const void *buffer,
+ size_t bytes, size_t *bytes_written, int *error) {
+ StreamResult sr = SR_SUCCESS;
+ int error_value = 0;
+ size_t bytes_written_value = 0;
+
+ size_t new_position = seek_position_ + bytes;
+ if (new_position > allocated_length_) {
+ // Increase buffer size to the larger of:
+ // a) new position rounded up to next 256 bytes
+ // b) double the previous length
+ size_t new_allocated_length = _max((new_position | 0xFF) + 1,
+ allocated_length_ * 2);
+ if (char* new_buffer = new char[new_allocated_length]) {
+ memcpy(new_buffer, buffer_, data_length_);
+ delete [] buffer_;
+ buffer_ = new_buffer;
+ allocated_length_ = new_allocated_length;
+ } else {
+ error_value = ENOMEM;
+ sr = SR_ERROR;
+ }
+ }
+
+ if (sr == SR_SUCCESS) {
+ bytes_written_value = bytes;
+ memcpy(&buffer_[seek_position_], buffer, bytes);
+ seek_position_ = new_position;
+ if (data_length_ < seek_position_) {
+ data_length_ = seek_position_;
+ }
+ }
+
+ if (bytes_written) {
+ *bytes_written = bytes_written_value;
+ }
+ if (error) {
+ *error = error_value;
+ }
+
+ return sr;
+}
+
+void MemoryStream::Close() {
+ // nothing to do
+}
+
+bool MemoryStream::SetPosition(size_t position) {
+ if (position <= data_length_) {
+ seek_position_ = position;
+ return true;
+ }
+ return false;
+}
+
+bool MemoryStream::GetPosition(size_t *position) const {
+ if (!position) {
+ return false;
+ }
+ *position = seek_position_;
+ return true;
+}
+
+bool MemoryStream::GetSize(size_t *size) const {
+ if (!size) {
+ return false;
+ }
+ *size = data_length_;
+ return true;
+}
+
+bool MemoryStream::ReserveSize(size_t size) {
+ if (allocated_length_ >= size)
+ return true;
+
+ if (char* new_buffer = new char[size]) {
+ memcpy(new_buffer, buffer_, data_length_);
+ delete [] buffer_;
+ buffer_ = new_buffer;
+ allocated_length_ = size;
+ return true;
+ }
+
+ return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+StreamResult Flow(StreamInterface* source,
+ char* buffer, size_t buffer_len,
+ StreamInterface* sink) {
+ ASSERT(buffer_len > 0);
+
+ StreamResult result;
+ size_t count, read_pos, write_pos;
+
+ bool end_of_stream = false;
+ do {
+ // Read until buffer is full, end of stream, or error
+ read_pos = 0;
+ do {
+ result = source->Read(buffer + read_pos, buffer_len - read_pos,
+ &count, NULL);
+ if (result == SR_EOS) {
+ end_of_stream = true;
+ } else if (result != SR_SUCCESS) {
+ return result;
+ } else {
+ read_pos += count;
+ }
+ } while (!end_of_stream && (read_pos < buffer_len));
+
+ // Write until buffer is empty, or error (including end of stream)
+ write_pos = 0;
+ do {
+ result = sink->Write(buffer + write_pos, read_pos - write_pos,
+ &count, NULL);
+ if (result != SR_SUCCESS)
+ return result;
+
+ write_pos += count;
+ } while (write_pos < read_pos);
+ } while (!end_of_stream);
+
+ return SR_SUCCESS;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+LoggingAdapter::LoggingAdapter(StreamInterface* stream, LoggingSeverity level,
+ const std::string& label, bool hex_mode)
+: StreamAdapterInterface(stream), level_(level), hex_mode_(hex_mode)
+{
+ label_.append("[");
+ label_.append(label);
+ label_.append("]");
+}
+
+StreamResult LoggingAdapter::Read(void* buffer, size_t buffer_len,
+ size_t* read, int* error) {
+ size_t local_read; if (!read) read = &local_read;
+ StreamResult result = StreamAdapterInterface::Read(buffer, buffer_len, read, error);
+ if (result == SR_SUCCESS) {
+ LogMultiline(level_, label_.c_str(), true,
+ static_cast<const char *>(buffer), *read, hex_mode_, &lms_);
+ }
+ return result;
+}
+
+StreamResult LoggingAdapter::Write(const void* data, size_t data_len,
+ size_t* written, int* error) {
+ size_t local_written; if (!written) written = &local_written;
+ StreamResult result = StreamAdapterInterface::Write(data, data_len, written, error);
+ if (result == SR_SUCCESS) {
+ LogMultiline(level_, label_.c_str(), false,
+ static_cast<const char *>(data), *written, hex_mode_, &lms_);
+ }
+ return result;
+}
+
+void LoggingAdapter::Close() {
+ LOG_V(level_) << label_ << " Closed locally";
+ StreamAdapterInterface::Close();
+}
+
+void LoggingAdapter::OnEvent(StreamInterface* stream, int events, int err) {
+ if (events & SE_OPEN) {
+ LOG_V(level_) << label_ << " Open";
+ } else if (events & SE_CLOSE) {
+ LOG_V(level_) << label_ << " Closed with error: " << err;
+ }
+ StreamAdapterInterface::OnEvent(stream, events, err);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// StringStream - Reads/Writes to an external std::string
+///////////////////////////////////////////////////////////////////////////////
+
+StringStream::StringStream(std::string& str)
+: str_(str), read_pos_(0), read_only_(false)
+{
+}
+
+StringStream::StringStream(const std::string& str)
+: str_(const_cast<std::string&>(str)), read_pos_(0), read_only_(true)
+{
+}
+
+StreamState StringStream::GetState() const {
+ return SS_OPEN;
+}
+
+StreamResult StringStream::Read(void* buffer, size_t buffer_len,
+ size_t* read, int* error) {
+ size_t available = talk_base::_min(buffer_len, str_.size() - read_pos_);
+ if (!available)
+ return SR_EOS;
+ memcpy(buffer, str_.data() + read_pos_, available);
+ read_pos_ += available;
+ if (read)
+ *read = available;
+ return SR_SUCCESS;
+}
+
+StreamResult StringStream::Write(const void* data, size_t data_len,
+ size_t* written, int* error) {
+ if (read_only_) {
+ if (error) {
+ *error = -1;
+ }
+ return SR_ERROR;
+ }
+ str_.append(static_cast<const char*>(data),
+ static_cast<const char*>(data) + data_len);
+ if (written)
+ *written = data_len;
+ return SR_SUCCESS;
+}
+
+void StringStream::Close() {
+}
+
+bool StringStream::GetSize(size_t* size) const {
+ ASSERT(size != NULL);
+ *size = str_.size();
+ return true;
+}
+
+bool StringStream::ReserveSize(size_t size) {
+ if (read_only_)
+ return false;
+ str_.reserve(size);
+ return true;
+}
+
+bool StringStream::Rewind() {
+ read_pos_ = 0;
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+} // namespace talk_base
diff --git a/Plugins/jingle/libjingle/talk/base/stream.h b/Plugins/jingle/libjingle/talk/base/stream.h
new file mode 100644
index 0000000..cb91bb7
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/stream.h
@@ -0,0 +1,396 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TALK_BASE_STREAM_H__
+#define TALK_BASE_STREAM_H__
+
+#include "talk/base/basictypes.h"
+#include "talk/base/logging.h"
+#include "talk/base/scoped_ptr.h"
+#include "talk/base/sigslot.h"
+
+namespace talk_base {
+
+///////////////////////////////////////////////////////////////////////////////
+// StreamInterface is a generic asynchronous stream interface, supporting read,
+// write, and close operations, and asynchronous signalling of state changes.
+// The interface is designed with file, memory, and socket implementations in
+// mind.
+///////////////////////////////////////////////////////////////////////////////
+
+// The following enumerations are declared outside of the StreamInterface
+// class for brevity in use.
+
+// The SS_OPENING state indicates that the stream will signal open or closed
+// in the future.
+enum StreamState { SS_CLOSED, SS_OPENING, SS_OPEN };
+
+// Stream read/write methods return this value to indicate various success
+// and failure conditions described below.
+enum StreamResult { SR_ERROR, SR_SUCCESS, SR_BLOCK, SR_EOS };
+
+// StreamEvents are used to asynchronously signal state transitionss. The flags
+// may be combined.
+// SE_OPEN: The stream has transitioned to the SS_OPEN state
+// SE_CLOSE: The stream has transitioned to the SS_CLOSED state
+// SE_READ: Data is available, so Read is likely to not return SR_BLOCK
+// SE_WRITE: Data can be written, so Write is likely to not return SR_BLOCK
+enum StreamEvent { SE_OPEN = 1, SE_READ = 2, SE_WRITE = 4, SE_CLOSE = 8 };
+
+class StreamInterface {
+ public:
+ virtual ~StreamInterface() { }
+
+ virtual StreamState GetState() const = 0;
+
+ // Read attempts to fill buffer of size buffer_len. Write attempts to send
+ // data_len bytes stored in data. The variables read and write are set only
+ // on SR_SUCCESS (see below). Likewise, error is only set on SR_ERROR.
+ // Read and Write return a value indicating:
+ // SR_ERROR: an error occurred, which is returned in a non-null error
+ // argument. Interpretation of the error requires knowledge of the
+ // stream's concrete type, which limits its usefulness.
+ // SR_SUCCESS: some number of bytes were successfully written, which is
+ // returned in a non-null read/write argument.
+ // SR_BLOCK: the stream is in non-blocking mode, and the operation would
+ // block, or the stream is in SS_OPENING state.
+ // SR_EOS: the end-of-stream has been reached, or the stream is in the
+ // SS_CLOSED state.
+ virtual StreamResult Read(void* buffer, size_t buffer_len,
+ size_t* read, int* error) = 0;
+ virtual StreamResult Write(const void* data, size_t data_len,
+ size_t* written, int* error) = 0;
+
+ // Attempt to transition to the SS_CLOSED state. SE_CLOSE will not be
+ // signalled as a result of this call.
+ virtual void Close() = 0;
+
+ // Return the number of bytes that will be returned by Read, if known.
+ virtual bool GetSize(size_t* size) const = 0;
+
+ // Communicates the amount of data which will be written to the stream. The
+ // stream may choose to preallocate memory to accomodate this data. The
+ // stream may return false to indicate that there is not enough room (ie,
+ // Write will return SR_EOS/SR_ERROR at some point). Note that calling this
+ // function should not affect the existing state of data in the stream.
+ virtual bool ReserveSize(size_t size) = 0;
+
+ // Returns true if stream could be repositioned to the beginning.
+ virtual bool Rewind() = 0;
+
+ // WriteAll is a helper function which repeatedly calls Write until all the
+ // data is written, or something other than SR_SUCCESS is returned. Note that
+ // unlike Write, the argument 'written' is always set, and may be non-zero
+ // on results other than SR_SUCCESS. The remaining arguments have the
+ // same semantics as Write.
+ StreamResult WriteAll(const void* data, size_t data_len,
+ size_t* written, int* error);
+
+ // Similar to ReadAll. Calls Read until buffer_len bytes have been read, or
+ // until a non-SR_SUCCESS result is returned. 'read' is always set.
+ StreamResult ReadAll(void* buffer, size_t buffer_len,
+ size_t* read, int* error);
+
+ // ReadLine is a helper function which repeatedly calls Read until it hits
+ // the end-of-line character, or something other than SR_SUCCESS.
+ // TODO: this is too inefficient to keep here. Break this out into a buffered
+ // readline object or adapter
+ StreamResult ReadLine(std::string *line);
+
+ // Streams may signal one or more StreamEvents to indicate state changes.
+ // The first argument identifies the stream on which the state change occured.
+ // The second argument is a bit-wise combination of StreamEvents.
+ // If SE_CLOSE is signalled, then the third argument is the associated error
+ // code. Otherwise, the value is undefined.
+ // Note: Not all streams will support asynchronous event signalling. However,
+ // SS_OPENING and SR_BLOCK returned from stream member functions imply that
+ // certain events will be raised in the future.
+ sigslot::signal3<StreamInterface*, int, int> SignalEvent;
+
+ protected:
+ StreamInterface() { }
+
+ private:
+ DISALLOW_EVIL_CONSTRUCTORS(StreamInterface);
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// StreamAdapterInterface is a convenient base-class for adapting a stream.
+// By default, all operations are pass-through. Override the methods that you
+// require adaptation. Note that the adapter will delete the adapted stream.
+///////////////////////////////////////////////////////////////////////////////
+
+class StreamAdapterInterface : public StreamInterface,
+ public sigslot::has_slots<> {
+ public:
+ explicit StreamAdapterInterface(StreamInterface* stream) {
+ Attach(stream);
+ }
+
+ virtual StreamState GetState() const {
+ return stream_->GetState();
+ }
+ virtual StreamResult Read(void* buffer, size_t buffer_len,
+ size_t* read, int* error) {
+ return stream_->Read(buffer, buffer_len, read, error);
+ }
+ virtual StreamResult Write(const void* data, size_t data_len,
+ size_t* written, int* error) {
+ return stream_->Write(data, data_len, written, error);
+ }
+ virtual void Close() {
+ stream_->Close();
+ }
+ virtual bool GetSize(size_t* size) const {
+ return stream_->GetSize(size);
+ }
+ virtual bool ReserveSize(size_t size) {
+ return stream_->ReserveSize(size);
+ }
+ virtual bool Rewind() {
+ return stream_->Rewind();
+ }
+
+ void Attach(StreamInterface* stream) {
+ if (NULL != stream_.get())
+ stream_->SignalEvent.disconnect(this);
+ stream_.reset(stream);
+ if (NULL != stream_.get())
+ stream_->SignalEvent.connect(this, &StreamAdapterInterface::OnEvent);
+ }
+ StreamInterface* Detach() {
+ if (NULL == stream_.get())
+ return NULL;
+ stream_->SignalEvent.disconnect(this);
+ return stream_.release();
+ }
+
+ protected:
+ // Note that the adapter presents itself as the origin of the stream events,
+ // since users of the adapter may not recognize the adapted object.
+ virtual void OnEvent(StreamInterface* stream, int events, int err) {
+ SignalEvent(this, events, err);
+ }
+
+ private:
+ scoped_ptr<StreamInterface> stream_;
+ DISALLOW_EVIL_CONSTRUCTORS(StreamAdapterInterface);
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// StreamTap is a non-modifying, pass-through adapter, which copies all data
+// in either direction to the tap. Note that errors or blocking on writing to
+// the tap will prevent further tap writes from occurring.
+///////////////////////////////////////////////////////////////////////////////
+
+class StreamTap : public StreamAdapterInterface {
+ public:
+ explicit StreamTap(StreamInterface* stream, StreamInterface* tap);
+
+ void AttachTap(StreamInterface* tap);
+ StreamInterface* DetachTap();
+ StreamResult GetTapResult(int* error);
+
+ // StreamAdapterInterface Interface
+ virtual StreamResult Read(void* buffer, size_t buffer_len,
+ size_t* read, int* error);
+ virtual StreamResult Write(const void* data, size_t data_len,
+ size_t* written, int* error);
+
+ private:
+ scoped_ptr<StreamInterface> tap_;
+ StreamResult tap_result_;
+ int tap_error_;
+ DISALLOW_EVIL_CONSTRUCTORS(StreamTap);
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// NullStream gives errors on read, and silently discards all written data.
+///////////////////////////////////////////////////////////////////////////////
+
+class NullStream : public StreamInterface {
+ public:
+ NullStream();
+ virtual ~NullStream();
+
+ // StreamInterface Interface
+ virtual StreamState GetState() const;
+ virtual StreamResult Read(void* buffer, size_t buffer_len,
+ size_t* read, int* error);
+ virtual StreamResult Write(const void* data, size_t data_len,
+ size_t* written, int* error);
+ virtual void Close();
+ virtual bool GetSize(size_t* size) const;
+ virtual bool ReserveSize(size_t size);
+ virtual bool Rewind();
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// FileStream is a simple implementation of a StreamInterface, which does not
+// support asynchronous notification.
+///////////////////////////////////////////////////////////////////////////////
+
+class FileStream : public StreamInterface {
+ public:
+ FileStream();
+ virtual ~FileStream();
+
+ // The semantics of filename and mode are the same as stdio's fopen
+ virtual bool Open(const std::string& filename, const char* mode);
+ virtual bool OpenShare(const std::string& filename, const char* mode,
+ int shflag);
+
+ // By default, reads and writes are buffered for efficiency. Disabling
+ // buffering causes writes to block until the bytes on disk are updated.
+ virtual bool DisableBuffering();
+
+ virtual StreamState GetState() const;
+ virtual StreamResult Read(void* buffer, size_t buffer_len,
+ size_t* read, int* error);
+ virtual StreamResult Write(const void* data, size_t data_len,
+ size_t* written, int* error);
+ virtual void Close();
+ virtual bool GetSize(size_t* size) const;
+ virtual bool ReserveSize(size_t size);
+ virtual bool Rewind() { return SetPosition(0); }
+
+ bool SetPosition(size_t position);
+ bool GetPosition(size_t* position) const;
+ int Flush();
+ static bool GetSize(const std::string& filename, size_t* size);
+
+ private:
+ FILE* file_;
+ DISALLOW_EVIL_CONSTRUCTORS(FileStream);
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// MemoryStream is a simple implementation of a StreamInterface over in-memory
+// data. It does not support asynchronous notification.
+///////////////////////////////////////////////////////////////////////////////
+
+class MemoryStream : public StreamInterface {
+ public:
+ MemoryStream();
+ // Pre-populate stream with the provided data.
+ MemoryStream(const char* data);
+ MemoryStream(const char* data, size_t length);
+ virtual ~MemoryStream();
+
+ virtual StreamState GetState() const;
+ virtual StreamResult Read(void *buffer, size_t bytes, size_t *bytes_read, int *error);
+ virtual StreamResult Write(const void *buffer, size_t bytes, size_t *bytes_written, int *error);
+ virtual void Close();
+ virtual bool GetSize(size_t* size) const;
+ virtual bool ReserveSize(size_t size);
+ virtual bool Rewind() { return SetPosition(0); }
+
+ char* GetBuffer() { return buffer_; }
+ const char* GetBuffer() const { return buffer_; }
+ bool SetPosition(size_t position);
+ bool GetPosition(size_t* position) const;
+
+ private:
+ void SetContents(const char* data, size_t length);
+
+ size_t allocated_length_;
+ char* buffer_;
+ size_t data_length_;
+ size_t seek_position_;
+
+ private:
+ DISALLOW_EVIL_CONSTRUCTORS(MemoryStream);
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+class LoggingAdapter : public StreamAdapterInterface {
+public:
+ LoggingAdapter(StreamInterface* stream, LoggingSeverity level,
+ const std::string& label, bool hex_mode = false);
+
+ virtual StreamResult Read(void* buffer, size_t buffer_len,
+ size_t* read, int* error);
+ virtual StreamResult Write(const void* data, size_t data_len,
+ size_t* written, int* error);
+ virtual void Close();
+
+ protected:
+ virtual void OnEvent(StreamInterface* stream, int events, int err);
+
+ private:
+ LoggingSeverity level_;
+ std::string label_;
+ bool hex_mode_;
+ LogMultilineState lms_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(LoggingAdapter);
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// StringStream - Reads/Writes to an external std::string
+///////////////////////////////////////////////////////////////////////////////
+
+class StringStream : public StreamInterface {
+public:
+ StringStream(std::string& str);
+ StringStream(const std::string& str);
+
+ virtual StreamState GetState() const;
+ virtual StreamResult Read(void* buffer, size_t buffer_len,
+ size_t* read, int* error);
+ virtual StreamResult Write(const void* data, size_t data_len,
+ size_t* written, int* error);
+ virtual void Close();
+ virtual bool GetSize(size_t* size) const;
+ virtual bool ReserveSize(size_t size);
+ virtual bool Rewind();
+
+private:
+ std::string& str_;
+ size_t read_pos_;
+ bool read_only_;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+// Flow attempts to move bytes from source to sink via buffer of size
+// buffer_len. The function returns SR_SUCCESS when source reaches
+// end-of-stream (returns SR_EOS), and all the data has been written successful
+// to sink. Alternately, if source returns SR_BLOCK or SR_ERROR, or if sink
+// returns SR_BLOCK, SR_ERROR, or SR_EOS, then the function immediately returns
+// with the unexpected StreamResult value.
+
+StreamResult Flow(StreamInterface* source,
+ char* buffer, size_t buffer_len,
+ StreamInterface* sink);
+
+///////////////////////////////////////////////////////////////////////////////
+
+} // namespace talk_base
+
+#endif // TALK_BASE_STREAM_H__
diff --git a/Plugins/jingle/libjingle/talk/base/streamutils.cc b/Plugins/jingle/libjingle/talk/base/streamutils.cc
new file mode 100644
index 0000000..52e6da7
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/streamutils.cc
@@ -0,0 +1,194 @@
+/*
+ * libjingle
+ * Copyright 2004--2006, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+#include "talk/base/common.h"
+#include "talk/base/streamutils.h"
+
+///////////////////////////////////////////////////////////////////////////////
+// TODO: Extend so that one side can close, and other side can send
+// buffered data.
+
+StreamRelay::StreamRelay(talk_base::StreamInterface* s1,
+ talk_base::StreamInterface* s2,
+ size_t buffer_size) : buffer_size_(buffer_size) {
+ dir_[0].stream = s1;
+ dir_[1].stream = s2;
+
+ ASSERT(s1->GetState() != talk_base::SS_CLOSED);
+ ASSERT(s2->GetState() != talk_base::SS_CLOSED);
+
+ for (size_t i=0; i<2; ++i) {
+ dir_[i].stream->SignalEvent.connect(this, &StreamRelay::OnEvent);
+ dir_[i].buffer = new char[buffer_size_];
+ dir_[i].data_len = 0;
+ }
+}
+
+StreamRelay::~StreamRelay() {
+ for (size_t i=0; i<2; ++i) {
+ delete dir_[i].stream;
+ delete [] dir_[i].buffer;
+ }
+}
+
+void
+StreamRelay::Circulate() {
+ int error = 0;
+ if (!Flow(0, &error) || !Flow(1, &error)) {
+ Close();
+ SignalClosed(this, error);
+ }
+}
+
+void
+StreamRelay::Close() {
+ for (size_t i=0; i<2; ++i) {
+ dir_[i].stream->SignalEvent.disconnect(this);
+ dir_[i].stream->Close();
+ }
+}
+
+bool
+StreamRelay::Flow(int read_index, int* error) {
+ Direction& reader = dir_[read_index];
+ Direction& writer = dir_[Complement(read_index)];
+
+ bool progress;
+ do {
+ progress = false;
+
+ while (reader.stream->GetState() == talk_base::SS_OPEN) {
+ size_t available = buffer_size_ - reader.data_len;
+ if (available == 0)
+ break;
+
+ *error = 0;
+ size_t read = 0;
+ talk_base::StreamResult result
+ = reader.stream->Read(reader.buffer + reader.data_len, available,
+ &read, error);
+ if ((result == talk_base::SR_BLOCK) || (result == talk_base::SR_EOS))
+ break;
+
+ if (result == talk_base::SR_ERROR)
+ return false;
+
+ progress = true;
+ ASSERT((read > 0) && (read <= available));
+ reader.data_len += read;
+ }
+
+ size_t total_written = 0;
+ while (writer.stream->GetState() == talk_base::SS_OPEN) {
+ size_t available = reader.data_len - total_written;
+ if (available == 0)
+ break;
+
+ *error = 0;
+ size_t written = 0;
+ talk_base::StreamResult result
+ = writer.stream->Write(reader.buffer + total_written,
+ available, &written, error);
+ if ((result == talk_base::SR_BLOCK) || (result == talk_base::SR_EOS))
+ break;
+
+ if (result == talk_base::SR_ERROR)
+ return false;
+
+ progress = true;
+ ASSERT((written > 0) && (written <= available));
+ total_written += written;
+ }
+
+ reader.data_len -= total_written;
+ if (reader.data_len > 0) {
+ memmove(reader.buffer, reader.buffer + total_written, reader.data_len);
+ }
+ } while (progress);
+
+ return true;
+}
+
+void StreamRelay::OnEvent(talk_base::StreamInterface* stream, int events,
+ int error) {
+ int index = Index(stream);
+
+ // Note: In the following cases, we are treating the open event as both
+ // readable and writeable, for robustness. It won't hurt if we are wrong.
+
+ if ((events & talk_base::SE_OPEN | talk_base::SE_READ)
+ && !Flow(index, &error)) {
+ events = talk_base::SE_CLOSE;
+ }
+
+ if ((events & talk_base::SE_OPEN | talk_base::SE_WRITE)
+ && !Flow(Complement(index), &error)) {
+ events = talk_base::SE_CLOSE;
+ }
+
+ if (events & talk_base::SE_CLOSE) {
+ Close();
+ SignalClosed(this, error);
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// StreamCounter - counts the number of bytes which are transferred in either
+// direction.
+///////////////////////////////////////////////////////////////////////////////
+
+StreamCounter::StreamCounter(talk_base::StreamInterface* stream)
+ : StreamAdapterInterface(stream), count_(0) {
+}
+
+talk_base::StreamResult StreamCounter::Read(void* buffer, size_t buffer_len,
+ size_t* read, int* error) {
+ size_t tmp;
+ if (!read)
+ read = &tmp;
+ talk_base::StreamResult result
+ = StreamAdapterInterface::Read(buffer, buffer_len,
+ read, error);
+ if (result == talk_base::SR_SUCCESS)
+ count_ += *read;
+ SignalUpdateByteCount(count_);
+ return result;
+}
+
+talk_base::StreamResult StreamCounter::Write(
+ const void* data, size_t data_len, size_t* written, int* error) {
+ size_t tmp;
+ if (!written)
+ written = &tmp;
+ talk_base::StreamResult result
+ = StreamAdapterInterface::Write(data, data_len, written, error);
+ if (result == talk_base::SR_SUCCESS)
+ count_ += *written;
+ SignalUpdateByteCount(count_);
+ return result;
+}
diff --git a/Plugins/jingle/libjingle/talk/base/streamutils.h b/Plugins/jingle/libjingle/talk/base/streamutils.h
new file mode 100644
index 0000000..7bb07a3
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/streamutils.h
@@ -0,0 +1,94 @@
+/*
+ * libjingle
+ * Copyright 2004--2006, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TALK_APP_STREAMUTILS_H__
+#define TALK_APP_STREAMUTILS_H__
+
+#include "talk/base/sigslot.h"
+#include "talk/base/stream.h"
+
+///////////////////////////////////////////////////////////////////////////////
+// StreamRelay - acts as an intermediary between two asynchronous streams,
+// reading from one stream and writing to the other, using a pre-specified
+// amount of buffering in both directions.
+///////////////////////////////////////////////////////////////////////////////
+
+class StreamRelay : public sigslot::has_slots<> {
+public:
+ StreamRelay(talk_base::StreamInterface* s1,
+ talk_base::StreamInterface* s2, size_t buffer_size);
+ virtual ~StreamRelay();
+
+ void Circulate(); // Simulate events to get things flowing
+ void Close();
+
+ sigslot::signal2<StreamRelay*, int> SignalClosed;
+
+private:
+ inline int Index(talk_base::StreamInterface* s) const
+ { return (s == dir_[1].stream); }
+ inline int Complement(int index) const { return (1-index); }
+
+ bool Flow(int read_index, int* error);
+ void OnEvent(talk_base::StreamInterface* stream, int events, int error);
+
+ struct Direction {
+ talk_base::StreamInterface* stream;
+ char* buffer;
+ size_t data_len;
+ };
+ Direction dir_[2];
+ size_t buffer_size_;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// StreamCounter - counts the number of bytes which are transferred in either
+// direction.
+///////////////////////////////////////////////////////////////////////////////
+
+class StreamCounter : public talk_base::StreamAdapterInterface {
+ public:
+ explicit StreamCounter(talk_base::StreamInterface* stream);
+
+ inline void ResetByteCount() { count_ = 0; }
+ inline size_t GetByteCount() const { return count_; }
+
+ sigslot::signal1<size_t> SignalUpdateByteCount;
+
+ // StreamAdapterInterface
+ virtual talk_base::StreamResult Read(void* buffer, size_t buffer_len,
+ size_t* read, int* error);
+ virtual talk_base::StreamResult Write(const void* data, size_t data_len,
+ size_t* written, int* error);
+
+ private:
+ size_t count_;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+#endif // TALK_APP_STREAMUTILS_H__
diff --git a/Plugins/jingle/libjingle/talk/base/stringdigest.cc b/Plugins/jingle/libjingle/talk/base/stringdigest.cc
new file mode 100644
index 0000000..1f98124
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/stringdigest.cc
@@ -0,0 +1,49 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+#include "talk/base/md5.h"
+#include "talk/base/stringdigest.h"
+#include "talk/base/stringencode.h"
+
+namespace talk_base {
+
+std::string MD5(const std::string& data) {
+ MD5_CTX ctx;
+ MD5Init(&ctx);
+ MD5Update(&ctx, const_cast<unsigned char *>(reinterpret_cast<const unsigned char *>(data.data())), static_cast<unsigned int>(data.size()));
+ unsigned char digest[16];
+ MD5Final(digest, &ctx);
+ std::string hex_digest;
+ for (int i=0; i<16; ++i) {
+ hex_digest += hex_encode(digest[i] >> 4);
+ hex_digest += hex_encode(digest[i] & 0xf);
+ }
+ return hex_digest;
+}
+
+} // namespace talk_base
diff --git a/Plugins/jingle/libjingle/talk/base/stringdigest.h b/Plugins/jingle/libjingle/talk/base/stringdigest.h
new file mode 100644
index 0000000..d75d845
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/stringdigest.h
@@ -0,0 +1,47 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+#ifndef TALK_BASE_STRINGDIGEST_H__
+#define TALK_BASE_STRINGDIGEST_H__
+
+#include <string>
+
+namespace talk_base {
+
+//////////////////////////////////////////////////////////////////////
+// Message Digest Utilities
+//////////////////////////////////////////////////////////////////////
+
+// Compute the MD5 message digest of data, and return it in
+std::string MD5(const std::string& data);
+
+//////////////////////////////////////////////////////////////////////
+
+} // namespace talk_base
+
+#endif // TALK_BASE_STRINGDIGEST_H__
diff --git a/Plugins/jingle/libjingle/talk/base/stringencode.cc b/Plugins/jingle/libjingle/talk/base/stringencode.cc
new file mode 100644
index 0000000..ed6edfc
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/stringencode.cc
@@ -0,0 +1,579 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifdef WIN32
+#include <malloc.h>
+#endif // WIN32
+#ifdef POSIX
+#include <alloca.h>
+#define _alloca alloca
+#endif // POSIX
+
+#include "talk/base/basictypes.h"
+#include "talk/base/common.h"
+#include "talk/base/stringencode.h"
+#include "talk/base/stringutils.h"
+
+namespace talk_base {
+
+/////////////////////////////////////////////////////////////////////////////
+// String Encoding Utilities
+/////////////////////////////////////////////////////////////////////////////
+
+static const char HEX[] = "0123456789abcdef";
+
+char hex_encode(unsigned char val) {
+ ASSERT(val < 16);
+ return (val < 16) ? HEX[val] : '!';
+}
+
+unsigned char hex_decode(char ch) {
+ char lower = tolower(ch);
+ ASSERT(((ch >= '0') && (ch <= '9')) || ((lower >= 'a') && (lower <= 'z')));
+ return (ch <= '9') ? (ch - '0') : ((lower - 'a') + 10);
+}
+
+size_t escape(char * buffer, size_t buflen,
+ const char * source, size_t srclen,
+ const char * illegal, char escape) {
+ ASSERT(NULL != buffer); // TODO: estimate output size
+ if (buflen <= 0)
+ return 0;
+
+ size_t srcpos = 0, bufpos = 0;
+ while ((srcpos < srclen) && (bufpos + 1 < buflen)) {
+ char ch = source[srcpos++];
+ if ((ch == escape) || ::strchr(illegal, ch)) {
+ if (bufpos + 2 >= buflen)
+ break;
+ buffer[bufpos++] = escape;
+ }
+ buffer[bufpos++] = ch;
+ }
+
+ buffer[bufpos] = '\0';
+ return bufpos;
+}
+
+size_t unescape(char * buffer, size_t buflen,
+ const char * source, size_t srclen,
+ char escape) {
+ ASSERT(NULL != buffer); // TODO: estimate output size
+ if (buflen <= 0)
+ return 0;
+
+ size_t srcpos = 0, bufpos = 0;
+ while ((srcpos < srclen) && (bufpos + 1 < buflen)) {
+ char ch = source[srcpos++];
+ if ((ch == escape) && (srcpos < srclen)) {
+ ch = source[srcpos++];
+ }
+ buffer[bufpos++] = ch;
+ }
+ buffer[bufpos] = '\0';
+ return bufpos;
+}
+
+size_t encode(char * buffer, size_t buflen,
+ const char * source, size_t srclen,
+ const char * illegal, char escape) {
+ ASSERT(NULL != buffer); // TODO: estimate output size
+ if (buflen <= 0)
+ return 0;
+
+ size_t srcpos = 0, bufpos = 0;
+ while ((srcpos < srclen) && (bufpos + 1 < buflen)) {
+ char ch = source[srcpos++];
+ if ((ch != escape) && !::strchr(illegal, ch)) {
+ buffer[bufpos++] = ch;
+ } else if (bufpos + 3 >= buflen) {
+ break;
+ } else {
+ buffer[bufpos+0] = escape;
+ buffer[bufpos+1] = hex_encode((static_cast<unsigned char>(ch) >> 4) & 0xF);
+ buffer[bufpos+2] = hex_encode((static_cast<unsigned char>(ch) ) & 0xF);
+ bufpos += 3;
+ }
+ }
+ buffer[bufpos] = '\0';
+ return bufpos;
+}
+
+size_t decode(char * buffer, size_t buflen,
+ const char * source, size_t srclen,
+ char escape) {
+ if (buflen <= 0)
+ return 0;
+
+ size_t srcpos = 0, bufpos = 0;
+ while ((srcpos < srclen) && (bufpos + 1 < buflen)) {
+ char ch = source[srcpos++];
+ if ((ch == escape) && (srcpos + 1 < srclen)) {
+ buffer[bufpos++] = (hex_decode(source[srcpos]) << 4)
+ | hex_decode(source[srcpos+1]);
+ srcpos += 2;
+ } else {
+ buffer[bufpos++] = ch;
+ }
+ }
+ buffer[bufpos] = '\0';
+ return bufpos;
+}
+
+const char* unsafe_filename_characters() {
+ // It might be better to have a single specification which is the union of
+ // all operating systems, unless one system is overly restrictive.
+#ifdef WIN32
+ return "\\/:*?\"<>|";
+#else // !WIN32
+ // TODO
+#endif // !WIN23
+}
+
+const unsigned char URL_UNSAFE = 0x1; // 0-33 "#$%&+,/:;<=>?@[\]^`{|} 127
+const unsigned char XML_UNSAFE = 0x2; // "&'<>
+const unsigned char HTML_UNSAFE = 0x2; // "&'<>
+
+// ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 6 5 7 8 9 : ; < = > ?
+//@ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ \ ] ^ _
+//` a b c d e f g h i j k l m n o p q r s t u v w x y z { | } ~
+
+const unsigned char ASCII_CLASS[128] = {
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,0,3,1,1,1,3,2,0,0,0,1,1,0,0,1,0,0,0,0,0,0,0,0,0,0,1,1,3,1,3,1,
+ 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,
+ 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,1,
+};
+
+size_t url_encode(char * buffer, size_t buflen,
+ const char * source, size_t srclen) {
+ if (NULL == buffer)
+ return srclen * 3 + 1;
+ if (buflen <= 0)
+ return 0;
+
+ size_t srcpos = 0, bufpos = 0;
+ while ((srcpos < srclen) && (bufpos + 1 < buflen)) {
+ unsigned char ch = source[srcpos++];
+ if ((ch < 128) && (ASCII_CLASS[ch] & URL_UNSAFE)) {
+ if (bufpos + 3 >= buflen) {
+ break;
+ }
+ buffer[bufpos+0] = '%';
+ buffer[bufpos+1] = hex_encode((ch >> 4) & 0xF);
+ buffer[bufpos+2] = hex_encode((ch ) & 0xF);
+ bufpos += 3;
+ } else {
+ buffer[bufpos++] = ch;
+ }
+ }
+ buffer[bufpos] = '\0';
+ return bufpos;
+}
+
+size_t url_decode(char * buffer, size_t buflen,
+ const char * source, size_t srclen) {
+ if (NULL == buffer)
+ return srclen + 1;
+ if (buflen <= 0)
+ return 0;
+
+ size_t srcpos = 0, bufpos = 0;
+ while ((srcpos < srclen) && (bufpos + 1 < buflen)) {
+ unsigned char ch = source[srcpos++];
+ if (ch == '+') {
+ buffer[bufpos++] = ' ';
+ } else if ((ch == '%') && (srcpos + 1 < srclen)) {
+ buffer[bufpos++] = (hex_decode(source[srcpos]) << 4)
+ | hex_decode(source[srcpos+1]);
+ srcpos += 2;
+ } else {
+ buffer[bufpos++] = ch;
+ }
+ }
+ buffer[bufpos] = '\0';
+ return bufpos;
+}
+
+size_t utf8_decode(const char* source, size_t srclen, unsigned long* value) {
+ const unsigned char* s = reinterpret_cast<const unsigned char*>(source);
+ if ((s[0] & 0x80) == 0x00) { // Check s[0] == 0xxxxxxx
+ *value = s[0];
+ return 1;
+ }
+ if ((srclen < 2) || ((s[1] & 0xC0) != 0x80)) { // Check s[1] != 10xxxxxx
+ return 0;
+ }
+ // Accumulate the trailer byte values in value16, and combine it with the
+ // relevant bits from s[0], once we've determined the sequence length.
+ unsigned long value16 = (s[1] & 0x3F);
+ if ((s[0] & 0xE0) == 0xC0) { // Check s[0] == 110xxxxx
+ *value = ((s[0] & 0x1F) << 6) | value16;
+ return 2;
+ }
+ if ((srclen < 3) || ((s[2] & 0xC0) != 0x80)) { // Check s[2] != 10xxxxxx
+ return 0;
+ }
+ value16 = (value16 << 6) | (s[2] & 0x3F);
+ if ((s[0] & 0xF0) == 0xE0) { // Check s[0] == 1110xxxx
+ *value = ((s[0] & 0x0F) << 12) | value16;
+ return 3;
+ }
+ if ((srclen < 4) || ((s[3] & 0xC0) != 0x80)) { // Check s[3] != 10xxxxxx
+ return 0;
+ }
+ value16 = (value16 << 6) | (s[3] & 0x3F);
+ if ((s[0] & 0xF8) == 0xF0) { // Check s[0] == 11110xxx
+ *value = ((s[0] & 0x07) << 18) | value16;
+ return 4;
+ }
+ return 0;
+}
+
+size_t utf8_encode(char* buffer, size_t buflen, unsigned long value) {
+ if ((value <= 0x7F) && (buflen >= 1)) {
+ buffer[0] = static_cast<unsigned char>(value);
+ return 1;
+ }
+ if ((value <= 0x7FF) && (buflen >= 2)) {
+ buffer[0] = 0xC0 | static_cast<unsigned char>(value >> 6);
+ buffer[1] = 0x80 | static_cast<unsigned char>(value & 0x3F);
+ return 2;
+ }
+ if ((value <= 0xFFFF) && (buflen >= 3)) {
+ buffer[0] = 0xE0 | static_cast<unsigned char>(value >> 12);
+ buffer[1] = 0x80 | static_cast<unsigned char>((value >> 6) & 0x3F);
+ buffer[2] = 0x80 | static_cast<unsigned char>(value & 0x3F);
+ return 3;
+ }
+ if ((value <= 0x1FFFFF) && (buflen >= 4)) {
+ buffer[0] = 0xF0 | static_cast<unsigned char>(value >> 18);
+ buffer[1] = 0x80 | static_cast<unsigned char>((value >> 12) & 0x3F);
+ buffer[2] = 0x80 | static_cast<unsigned char>((value >> 6) & 0x3F);
+ buffer[3] = 0x80 | static_cast<unsigned char>(value & 0x3F);
+ return 4;
+ }
+ return 0;
+}
+
+size_t html_encode(char * buffer, size_t buflen,
+ const char * source, size_t srclen) {
+ ASSERT(NULL != buffer); // TODO: estimate output size
+ if (buflen <= 0)
+ return 0;
+
+ size_t srcpos = 0, bufpos = 0;
+ while ((srcpos < srclen) && (bufpos + 1 < buflen)) {
+ unsigned char ch = source[srcpos];
+ if (ch < 128) {
+ srcpos += 1;
+ if (ASCII_CLASS[ch] & HTML_UNSAFE) {
+ const char * escseq = 0;
+ size_t esclen = 0;
+ switch (ch) {
+ case '<': escseq = "&lt;"; esclen = 4; break;
+ case '>': escseq = "&gt;"; esclen = 4; break;
+ case '\'': escseq = "&#39;"; esclen = 5; break;
+ case '\"': escseq = "&quot;"; esclen = 6; break;
+ case '&': escseq = "&amp;"; esclen = 5; break;
+ default: ASSERT(false);
+ }
+ if (bufpos + esclen >= buflen) {
+ break;
+ }
+ memcpy(buffer + bufpos, escseq, esclen);
+ bufpos += esclen;
+ } else {
+ buffer[bufpos++] = ch;
+ }
+ } else {
+ // Largest value is 0x1FFFFF => &#2097151; (10 characters)
+ char escseq[11];
+ unsigned long val;
+ if (size_t vallen = utf8_decode(&source[srcpos], srclen - srcpos, &val)) {
+ srcpos += vallen;
+ } else {
+ // Not a valid utf8 sequence, just use the raw character.
+ val = static_cast<unsigned char>(source[srcpos++]);
+ }
+ size_t esclen = sprintfn(escseq, ARRAY_SIZE(escseq), "&#%lu;", val);
+ if (bufpos + esclen >= buflen) {
+ break;
+ }
+ memcpy(buffer + bufpos, escseq, esclen);
+ bufpos += esclen;
+ }
+ }
+ buffer[bufpos] = '\0';
+ return bufpos;
+}
+
+size_t html_decode(char * buffer, size_t buflen,
+ const char * source, size_t srclen) {
+ ASSERT(NULL != buffer); // TODO: estimate output size
+ return xml_decode(buffer, buflen, source, srclen);
+}
+
+size_t xml_encode(char * buffer, size_t buflen,
+ const char * source, size_t srclen) {
+ ASSERT(NULL != buffer); // TODO: estimate output size
+ if (buflen <= 0)
+ return 0;
+
+ size_t srcpos = 0, bufpos = 0;
+ while ((srcpos < srclen) && (bufpos + 1 < buflen)) {
+ unsigned char ch = source[srcpos++];
+ if ((ch < 128) && (ASCII_CLASS[ch] & XML_UNSAFE)) {
+ const char * escseq = 0;
+ size_t esclen = 0;
+ switch (ch) {
+ case '<': escseq = "&lt;"; esclen = 4; break;
+ case '>': escseq = "&gt;"; esclen = 4; break;
+ case '\'': escseq = "&apos;"; esclen = 6; break;
+ case '\"': escseq = "&quot;"; esclen = 6; break;
+ case '&': escseq = "&amp;"; esclen = 5; break;
+ default: ASSERT(false);
+ }
+ if (bufpos + esclen >= buflen) {
+ break;
+ }
+ memcpy(buffer + bufpos, escseq, esclen);
+ bufpos += esclen;
+ } else {
+ buffer[bufpos++] = ch;
+ }
+ }
+ buffer[bufpos] = '\0';
+ return bufpos;
+}
+
+size_t xml_decode(char * buffer, size_t buflen,
+ const char * source, size_t srclen) {
+ ASSERT(NULL != buffer); // TODO: estimate output size
+ if (buflen <= 0)
+ return 0;
+
+ size_t srcpos = 0, bufpos = 0;
+ while ((srcpos < srclen) && (bufpos + 1 < buflen)) {
+ unsigned char ch = source[srcpos++];
+ if (ch != '&') {
+ buffer[bufpos++] = ch;
+ } else if ((srcpos + 2 < srclen)
+ && (memcmp(source + srcpos, "lt;", 3) == 0)) {
+ buffer[bufpos++] = '<';
+ srcpos += 3;
+ } else if ((srcpos + 2 < srclen)
+ && (memcmp(source + srcpos, "gt;", 3) == 0)) {
+ buffer[bufpos++] = '>';
+ srcpos += 3;
+ } else if ((srcpos + 4 < srclen)
+ && (memcmp(source + srcpos, "apos;", 5) == 0)) {
+ buffer[bufpos++] = '\'';
+ srcpos += 5;
+ } else if ((srcpos + 4 < srclen)
+ && (memcmp(source + srcpos, "quot;", 5) == 0)) {
+ buffer[bufpos++] = '\"';
+ srcpos += 5;
+ } else if ((srcpos + 3 < srclen)
+ && (memcmp(source + srcpos, "amp;", 4) == 0)) {
+ buffer[bufpos++] = '&';
+ srcpos += 4;
+ } else if ((srcpos < srclen) && (source[srcpos] == '#')) {
+ int int_base = 10;
+ if ((srcpos + 1 < srclen) && (source[srcpos+1] == 'x')) {
+ int_base = 16;
+ srcpos += 1;
+ }
+ char * ptr;
+ // TODO: Fix hack (ptr may go past end of data)
+ unsigned long val = strtoul(source + srcpos + 1, &ptr, int_base);
+ if ((static_cast<size_t>(ptr - source) < srclen) && (*ptr == ';')) {
+ srcpos = ptr - source + 1;
+ } else {
+ // Not a valid escape sequence.
+ break;
+ }
+ if (size_t esclen = utf8_encode(buffer + bufpos, buflen - bufpos, val)) {
+ bufpos += esclen;
+ } else {
+ // Not enough room to encode the character, or illegal character
+ break;
+ }
+ } else {
+ // Unrecognized escape sequence.
+ break;
+ }
+ }
+ buffer[bufpos] = '\0';
+ return bufpos;
+}
+
+size_t hex_encode(char * buffer, size_t buflen,
+ const char * csource, size_t srclen) {
+ ASSERT(NULL != buffer); // TODO: estimate output size
+ if (buflen <= 0)
+ return 0;
+
+ const unsigned char * bsource =
+ reinterpret_cast<const unsigned char *>(csource);
+
+ size_t srcpos = 0, bufpos = 0;
+ srclen = _min(srclen, (buflen - 1) / 2);
+ while (srcpos < srclen) {
+ unsigned char ch = bsource[srcpos++];
+ buffer[bufpos ] = hex_encode((ch >> 4) & 0xF);
+ buffer[bufpos+1] = hex_encode((ch ) & 0xF);
+ bufpos += 2;
+ }
+ buffer[bufpos] = '\0';
+ return bufpos;
+}
+
+size_t hex_decode(char * cbuffer, size_t buflen,
+ const char * source, size_t srclen) {
+ ASSERT(NULL != cbuffer); // TODO: estimate output size
+ if (buflen <= 0)
+ return 0;
+
+ unsigned char * bbuffer = reinterpret_cast<unsigned char *>(cbuffer);
+
+ size_t srcpos = 0, bufpos = 0;
+ while ((srcpos + 1 < srclen) && (bufpos + 1 < buflen)) {
+ unsigned char v1 = (hex_decode(source[srcpos]) << 4);
+ unsigned char v2 = hex_decode(source[srcpos+1]);
+ bbuffer[bufpos++] = v1 | v2;
+ srcpos += 2;
+ }
+ bbuffer[bufpos] = '\0';
+ return bufpos;
+}
+
+void transform(std::string& value, size_t maxlen, const std::string& source,
+ Transform t) {
+ char * buffer = static_cast<char *>(_alloca(maxlen + 1));
+ value.assign(buffer, t(buffer, maxlen + 1, source.data(), source.length()));
+}
+
+std::string s_transform(const std::string& source, Transform t) {
+ // Ask transformation function to approximate the destination size (returns upper bound)
+ size_t maxlen = t(NULL, 0, source.data(), source.length());
+ char * buffer = static_cast<char *>(_alloca(maxlen));
+ size_t len = t(buffer, maxlen, source.data(), source.length());
+ std::string result(buffer, len);
+ return result;
+}
+
+char make_char_safe_for_filename(char c) {
+ if (c < 32)
+ return '_';
+
+ switch (c) {
+ case '<':
+ case '>':
+ case ':':
+ case '"':
+ case '/':
+ case '\\':
+ case '|':
+ case '*':
+ case '?':
+ return '_';
+
+ default:
+ return c;
+ }
+}
+
+/*
+void sprintf(std::string& value, size_t maxlen, const char * format, ...) {
+ char * buffer = static_cast<char *>(alloca(maxlen + 1));
+ va_list args;
+ va_start(args, format);
+ value.assign(buffer, vsprintfn(buffer, maxlen + 1, format, args));
+ va_end(args);
+}
+*/
+
+/////////////////////////////////////////////////////////////////////////////
+// Unit Tests
+/////////////////////////////////////////////////////////////////////////////
+
+#ifdef _DEBUG
+
+static int utf8_unittest() {
+ const struct Utf8Test {
+ const char* encoded;
+ size_t encsize, enclen;
+ unsigned long decoded;
+ } kTests[] = {
+ { "a ", 5, 1, 'a' },
+ { "\x7F ", 5, 1, 0x7F },
+ { "\xC2\x80 ", 5, 2, 0x80 },
+ { "\xDF\xBF ", 5, 2, 0x7FF },
+ { "\xE0\xA0\x80 ", 5, 3, 0x800 },
+ { "\xEF\xBF\xBF ", 5, 3, 0xFFFF },
+ { "\xF0\x90\x80\x80 ", 5, 4, 0x10000 },
+ { "\xF0\x90\x80\x80 ", 3, 0, 0x10000 },
+ { "\xF0\xF0\x80\x80 ", 5, 0, 0 },
+ { "\xF0\x90\x80 ", 5, 0, 0 },
+ { "\x90\x80\x80 ", 5, 0, 0 },
+ { NULL, 0, 0 },
+ };
+ for (size_t i=0; kTests[i].encoded; ++i) {
+ unsigned long val = 0;
+ ASSERT(kTests[i].enclen == utf8_decode(kTests[i].encoded,
+ kTests[i].encsize,
+ &val));
+ unsigned long result = (kTests[i].enclen == 0) ? 0 : kTests[i].decoded;
+ ASSERT(val == result);
+
+ if (kTests[i].decoded == 0) {
+ // Not an interesting encoding test case
+ continue;
+ }
+
+ char buffer[5];
+ memset(buffer, 0x01, ARRAY_SIZE(buffer));
+ ASSERT(kTests[i].enclen == utf8_encode(buffer,
+ kTests[i].encsize,
+ kTests[i].decoded));
+ ASSERT(memcmp(buffer, kTests[i].encoded, kTests[i].enclen) == 0);
+ // Make sure remainder of buffer is unchanged
+ ASSERT(memory_check(buffer + kTests[i].enclen,
+ 0x1,
+ ARRAY_SIZE(buffer) - kTests[i].enclen));
+ }
+ return 1;
+}
+
+int test = utf8_unittest();
+
+#endif // _DEBUG
+
+/////////////////////////////////////////////////////////////////////////////
+
+} // namespace talk_base
diff --git a/Plugins/jingle/libjingle/talk/base/stringencode.h b/Plugins/jingle/libjingle/talk/base/stringencode.h
new file mode 100644
index 0000000..08e5e4f
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/stringencode.h
@@ -0,0 +1,166 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TALK_BASE_STRINGENCODE_H__
+#define TALK_BASE_STRINGENCODE_H__
+
+#include <string>
+#include <sstream>
+
+namespace talk_base {
+
+//////////////////////////////////////////////////////////////////////
+// String Encoding Utilities
+//////////////////////////////////////////////////////////////////////
+
+// Convert an unsigned value from 0 to 15 to the hex character equivalent...
+char hex_encode(unsigned char val);
+// ...and vice-versa.
+unsigned char hex_decode(char ch);
+
+// Convert an unsigned value to it's utf8 representation. Returns the length
+// of the encoded string, or 0 if the encoding is longer than buflen - 1.
+size_t utf8_encode(char* buffer, size_t buflen, unsigned long value);
+// Decode the utf8 encoded value pointed to by source. Returns the number of
+// bytes used by the encoding, or 0 if the encoding is invalid.
+size_t utf8_decode(const char* source, size_t srclen, unsigned long* value);
+
+// Escaping prefixes illegal characters with the escape character. Compact, but
+// illegal characters still appear in the string.
+size_t escape(char * buffer, size_t buflen,
+ const char * source, size_t srclen,
+ const char * illegal, char escape);
+// Note: in-place unescaping (buffer == source) is allowed.
+size_t unescape(char * buffer, size_t buflen,
+ const char * source, size_t srclen,
+ char escape);
+
+// Encoding replaces illegal characters with the escape character and 2 hex
+// chars, so it's a little less compact than escape, but completely removes
+// illegal characters. note that hex digits should not be used as illegal
+// characters.
+size_t encode(char * buffer, size_t buflen,
+ const char * source, size_t srclen,
+ const char * illegal, char escape);
+// Note: in-place decoding (buffer == source) is allowed.
+size_t decode(char * buffer, size_t buflen,
+ const char * source, size_t srclen,
+ char escape);
+
+// Returns a list of characters that may be unsafe for use in the name of a
+// file, suitable for passing to the 'illegal' member of escape or encode.
+const char* unsafe_filename_characters();
+
+// url_encode is an encode operation with a predefined set of illegal characters
+// and escape character (for use in URLs, obviously).
+size_t url_encode(char * buffer, size_t buflen,
+ const char * source, size_t srclen);
+// Note: in-place decoding (buffer == source) is allowed.
+size_t url_decode(char * buffer, size_t buflen,
+ const char * source, size_t srclen);
+
+// html_encode prevents data embedded in html from containing markup.
+size_t html_encode(char * buffer, size_t buflen,
+ const char * source, size_t srclen);
+// Note: in-place decoding (buffer == source) is allowed.
+size_t html_decode(char * buffer, size_t buflen,
+ const char * source, size_t srclen);
+
+// xml_encode makes data suitable for inside xml attributes and values.
+size_t xml_encode(char * buffer, size_t buflen,
+ const char * source, size_t srclen);
+// Note: in-place decoding (buffer == source) is allowed.
+size_t xml_decode(char * buffer, size_t buflen,
+ const char * source, size_t srclen);
+
+// hex_encode shows the hex representation of binary data in ascii.
+size_t hex_encode(char * buffer, size_t buflen,
+ const char * source, size_t srclen);
+size_t hex_decode(char * buffer, size_t buflen,
+ const char * source, size_t srclen);
+
+// Apply any suitable string transform (including the ones above) to an STL
+// string. Stack-allocated temporary space is used for the transformation,
+// so value and source may refer to the same string.
+typedef size_t (*Transform)(char * buffer, size_t buflen,
+ const char * source, size_t srclen);
+void transform(std::string& value, size_t maxlen, const std::string& source,
+ Transform t);
+
+// Return the result of applying transform t to source.
+std::string s_transform(const std::string& source, Transform t);
+
+// Convenience wrappers
+inline std::string s_url_encode(const std::string& source) {
+ return s_transform(source, url_encode);
+}
+inline std::string s_url_decode(const std::string& source) {
+ return s_transform(source, url_decode);
+}
+
+// Safe sprintf to std::string
+//void sprintf(std::string& value, size_t maxlen, const char * format, ...)
+// PRINTF_FORMAT(3);
+
+// Convert arbitrary values to/from a string.
+
+template <class T>
+static bool ToString(const T &t, std::string* s) {
+ std::ostringstream oss;
+ oss << t;
+ *s = oss.str();
+ return !oss.fail();
+}
+
+template <class T>
+static bool FromString(const std::string& s, T* t) {
+ std::istringstream iss(s);
+ iss >> *t;
+ return !iss.fail();
+}
+
+// Inline versions of the string conversion routines.
+
+template<typename T>
+static inline std::string ToString(T val) {
+ std::string str; ToString(val, &str); return str;
+}
+
+template<typename T>
+static inline T FromString(const std::string& str) {
+ T val; FromString(str, &val); return val;
+}
+
+// simple function to strip out characters which shouldn't be
+// used in filenames
+char make_char_safe_for_filename(char c);
+
+//////////////////////////////////////////////////////////////////////
+
+} // namespace talk_base
+
+#endif // TALK_BASE_STRINGENCODE_H__
diff --git a/Plugins/jingle/libjingle/talk/base/stringutils.cc b/Plugins/jingle/libjingle/talk/base/stringutils.cc
new file mode 100644
index 0000000..1e43637
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/stringutils.cc
@@ -0,0 +1,84 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "talk/base/stringutils.h"
+#include "talk/base/common.h"
+
+namespace talk_base {
+
+bool memory_check(const void* memory, int c, size_t count) {
+ const char* char_memory = static_cast<const char*>(memory);
+ char char_c = static_cast<char>(c);
+ for (size_t i=0; i<count; ++i) {
+ if (char_memory[i] != char_c) {
+ return false;
+ }
+ }
+ return true;
+}
+
+#ifdef WIN32
+int ascii_string_compare(const wchar_t* s1, const char* s2, size_t n,
+ CharacterTransformation transformation) {
+ wchar_t c1, c2;
+ while (true) {
+ if (n-- == 0) return 0;
+ c1 = transformation(*s1);
+ // Double check that characters are not UTF-8
+ ASSERT(static_cast<unsigned char>(*s2) < 128);
+ // Note: *s2 gets implicitly promoted to wchar_t
+ c2 = transformation(*s2);
+ if (c1 != c2) return (c1 < c2) ? -1 : 1;
+ if (!c1) return 0;
+ ++s1;
+ ++s2;
+ }
+}
+
+size_t asccpyn(wchar_t* buffer, size_t buflen,
+ const char* source, size_t srclen) {
+ if (buflen <= 0)
+ return 0;
+
+ if (srclen == SIZE_UNKNOWN) {
+ srclen = strlenn(source, buflen - 1);
+ } else if (srclen >= buflen) {
+ srclen = buflen - 1;
+ }
+#if _DEBUG
+ // Double check that characters are not UTF-8
+ for (size_t pos = 0; pos < srclen; ++pos)
+ ASSERT(static_cast<unsigned char>(source[pos]) < 128);
+#endif // _DEBUG
+ std::copy(source, source + srclen, buffer);
+ buffer[srclen] = 0;
+ return srclen;
+}
+
+#endif // WIN32
+
+} // namespace talk_base
diff --git a/Plugins/jingle/libjingle/talk/base/stringutils.h b/Plugins/jingle/libjingle/talk/base/stringutils.h
new file mode 100644
index 0000000..c3051fd
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/stringutils.h
@@ -0,0 +1,291 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TALK_BASE_STRINGUTILS_H__
+#define TALK_BASE_STRINGUTILS_H__
+
+#include <ctype.h>
+#include <stdarg.h>
+#include <stdio.h>
+#ifdef WIN32
+#include <wchar.h>
+#endif // WIN32
+
+#include <string>
+
+///////////////////////////////////////////////////////////////////////////////
+// Generic string/memory utilities
+///////////////////////////////////////////////////////////////////////////////
+
+namespace talk_base {
+
+// Complement to memset. Verifies memory consists of count bytes of value c.
+bool memory_check(const void* memory, int c, size_t count);
+
+} // namespace talk_base
+
+///////////////////////////////////////////////////////////////////////////////
+// Rename a bunch of common string functions so they are consistent across
+// platforms and between char and wchar_t variants.
+// Here is the full list of functions that are unified:
+// strlen, strcmp, stricmp, strncmp, strnicmp
+// strchr, vsnprintf, strtoul, tolowercase
+// tolowercase is like tolower, but not compatible with end-of-file value
+// Note that the wchar_t versions are not available on Linux
+///////////////////////////////////////////////////////////////////////////////
+
+inline char tolowercase(char c) {
+ return static_cast<char>(tolower(c));
+}
+
+#ifdef WIN32
+
+inline size_t strlen(const wchar_t* s) {
+ return wcslen(s);
+}
+inline int strcmp(const wchar_t* s1, const wchar_t* s2) {
+ return wcscmp(s1, s2);
+}
+inline int stricmp(const wchar_t* s1, const wchar_t* s2) {
+ return _wcsicmp(s1, s2);
+}
+inline int strncmp(const wchar_t* s1, const wchar_t* s2, size_t n) {
+ return wcsncmp(s1, s2, n);
+}
+inline int strnicmp(const wchar_t* s1, const wchar_t* s2, size_t n) {
+ return _wcsnicmp(s1, s2, n);
+}
+inline const wchar_t* strchr(const wchar_t* s, wchar_t c) {
+ return wcschr(s, c);
+}
+inline const wchar_t* strstr(const wchar_t* haystack, const wchar_t* needle) {
+ return wcsstr(haystack, needle);
+}
+inline int vsnprintf(char* buf, size_t n, const char* fmt, va_list args) {
+ return _vsnprintf(buf, n, fmt, args);
+}
+inline int vsnprintf(wchar_t* buf, size_t n, const wchar_t* fmt, va_list args) {
+ return _vsnwprintf(buf, n, fmt, args);
+}
+inline unsigned long strtoul(const wchar_t* snum, wchar_t** end, int base) {
+ return wcstoul(snum, end, base);
+}
+inline wchar_t tolowercase(wchar_t c) {
+ return static_cast<wchar_t>(towlower(c));
+}
+
+#endif // WIN32
+
+#ifdef POSIX
+
+inline int _stricmp(const char* s1, const char* s2) {
+ return strcasecmp(s1, s2);
+}
+inline int _strnicmp(const char* s1, const char* s2, size_t n) {
+ return strncasecmp(s1, s2, n);
+}
+
+#endif // POSIX
+
+///////////////////////////////////////////////////////////////////////////////
+// Traits simplifies porting string functions to be CTYPE-agnostic
+///////////////////////////////////////////////////////////////////////////////
+
+namespace talk_base {
+
+const size_t SIZE_UNKNOWN = static_cast<size_t>(-1);
+
+template<class CTYPE>
+struct Traits {
+ // STL string type
+ //typedef XXX string;
+ // Null-terminated string
+ //inline static const CTYPE* empty_str();
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// String utilities which work with char or wchar_t
+///////////////////////////////////////////////////////////////////////////////
+
+template<class CTYPE>
+inline const CTYPE* nonnull(const CTYPE* str, const CTYPE* def_str = NULL) {
+ return str ? str : (def_str ? def_str : Traits<CTYPE>::empty_str());
+}
+
+template<class CTYPE>
+const CTYPE* strchr(const CTYPE* str, const CTYPE* chs) {
+ for (size_t i=0; str[i]; ++i) {
+ for (size_t j=0; chs[j]; ++j) {
+ if (str[i] == chs[j]) {
+ return str + i;
+ }
+ }
+ }
+ return 0;
+}
+
+template<class CTYPE>
+const CTYPE* strchrn(const CTYPE* str, size_t slen, CTYPE ch) {
+ for (size_t i=0; i<slen && str[i]; ++i) {
+ if (str[i] == ch) {
+ return str + i;
+ }
+ }
+ return 0;
+}
+
+template<class CTYPE>
+size_t strlenn(const CTYPE* buffer, size_t buflen) {
+ size_t bufpos = 0;
+ while (buffer[bufpos] && (bufpos < buflen)) {
+ ++bufpos;
+ }
+ return bufpos;
+}
+
+// Safe versions of strncpy, strncat, snprintf and vsnprintf that always
+// null-terminate.
+
+template<class CTYPE>
+size_t strcpyn(CTYPE* buffer, size_t buflen,
+ const CTYPE* source, size_t srclen = SIZE_UNKNOWN) {
+ if (buflen <= 0)
+ return 0;
+
+ if (srclen == SIZE_UNKNOWN) {
+ srclen = strlenn(source, buflen - 1);
+ } else if (srclen >= buflen) {
+ srclen = buflen - 1;
+ }
+ memcpy(buffer, source, srclen * sizeof(CTYPE));
+ buffer[srclen] = 0;
+ return srclen;
+}
+
+template<class CTYPE>
+size_t strcatn(CTYPE* buffer, size_t buflen,
+ const CTYPE* source, size_t srclen = SIZE_UNKNOWN) {
+ if (buflen <= 0)
+ return 0;
+
+ size_t bufpos = strlenn(buffer, buflen - 1);
+ return bufpos + strcpyn(buffer + bufpos, buflen - bufpos, source, srclen);
+}
+
+template<class CTYPE>
+size_t sprintfn(CTYPE* buffer, size_t buflen, const CTYPE* format, ...) {
+ va_list args;
+ va_start(args, format);
+ size_t len = vsprintfn(buffer, buflen, format, args);
+ va_end(args);
+ return len;
+}
+
+template<class CTYPE>
+size_t vsprintfn(CTYPE* buffer, size_t buflen, const CTYPE* format,
+ va_list args) {
+ int len = vsnprintf(buffer, buflen, format, args);
+ if ((len < 0) || (static_cast<size_t>(len) >= buflen)) {
+ len = static_cast<int>(buflen - 1);
+ buffer[len] = 0;
+ }
+ return len;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Allow safe comparing and copying ascii (not UTF-8) with both wide and
+// non-wide character strings.
+///////////////////////////////////////////////////////////////////////////////
+
+inline int asccmp(const char* s1, const char* s2) {
+ return strcmp(s1, s2);
+}
+inline int ascicmp(const char* s1, const char* s2) {
+ return _stricmp(s1, s2);
+}
+inline int ascncmp(const char* s1, const char* s2, size_t n) {
+ return strncmp(s1, s2, n);
+}
+inline int ascnicmp(const char* s1, const char* s2, size_t n) {
+ return _strnicmp(s1, s2, n);
+}
+inline size_t asccpyn(char* buffer, size_t buflen,
+ const char* source, size_t srclen = SIZE_UNKNOWN) {
+ return strcpyn(buffer, buflen, source, srclen);
+}
+
+#ifdef WIN32
+
+typedef wchar_t(*CharacterTransformation)(wchar_t);
+inline wchar_t identity(wchar_t c) { return c; }
+int ascii_string_compare(const wchar_t* s1, const char* s2, size_t n,
+ CharacterTransformation transformation);
+
+inline int asccmp(const wchar_t* s1, const char* s2) {
+ return ascii_string_compare(s1, s2, static_cast<size_t>(-1), identity);
+}
+inline int ascicmp(const wchar_t* s1, const char* s2) {
+ return ascii_string_compare(s1, s2, static_cast<size_t>(-1), tolowercase);
+}
+inline int ascncmp(const wchar_t* s1, const char* s2, size_t n) {
+ return ascii_string_compare(s1, s2, n, identity);
+}
+inline int ascnicmp(const wchar_t* s1, const char* s2, size_t n) {
+ return ascii_string_compare(s1, s2, n, tolowercase);
+}
+size_t asccpyn(wchar_t* buffer, size_t buflen,
+ const char* source, size_t srclen = SIZE_UNKNOWN);
+
+#endif // WIN32
+
+///////////////////////////////////////////////////////////////////////////////
+// Traits<char> specializations
+///////////////////////////////////////////////////////////////////////////////
+
+template<>
+struct Traits<char> {
+ typedef std::string string;
+ inline static const char* Traits<char>::empty_str() { return ""; }
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// Traits<wchar_t> specializations (Windows only, currently)
+///////////////////////////////////////////////////////////////////////////////
+
+#ifdef WIN32
+
+template<>
+struct Traits<wchar_t> {
+ typedef std::wstring string;
+ inline static const wchar_t* Traits<wchar_t>::empty_str() { return L""; }
+};
+
+#endif // WIN32
+
+} // namespace talk_base
+
+#endif // TALK_BASE_STRINGUTILS_H__
diff --git a/Plugins/jingle/libjingle/talk/base/tarstream.cc b/Plugins/jingle/libjingle/talk/base/tarstream.cc
new file mode 100644
index 0000000..e1f17b1
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/tarstream.cc
@@ -0,0 +1,601 @@
+#include "talk/base/basicdefs.h"
+#include "talk/base/basictypes.h"
+#include "talk/base/tarstream.h"
+#include "talk/base/pathutils.h"
+#include "talk/base/stringutils.h"
+#include "talk/base/common.h"
+
+using namespace talk_base;
+
+///////////////////////////////////////////////////////////////////////////////
+// TarStream
+///////////////////////////////////////////////////////////////////////////////
+
+TarStream::TarStream() : mode_(M_NONE), next_block_(NB_NONE), block_pos_(0),
+ current_(NULL), current_bytes_(0) {
+}
+
+TarStream::~TarStream() {
+ Close();
+}
+
+bool TarStream::AddFilter(const std::string& pathname) {
+ if (pathname.empty())
+ return false;
+ Pathname archive_path(pathname);
+ archive_path.SetFolderDelimiter('/');
+ archive_path.Normalize();
+ filters_.push_back(archive_path.pathname());
+ return true;
+}
+
+bool TarStream::Open(const std::string& folder, bool read) {
+ Close();
+
+ Pathname root_folder;
+ root_folder.SetFolder(folder);
+ root_folder.Normalize();
+ root_folder_.assign(root_folder.folder());
+
+ if (read) {
+ std::string pattern(root_folder_);
+ DirectoryIterator *iter = new DirectoryIterator();
+
+ if (iter->Iterate(pattern) == false) {
+ delete iter;
+ return false;
+ }
+ mode_ = M_READ;
+ find_.push_front(iter);
+ next_block_ = NB_FILE_HEADER;
+ block_pos_ = BLOCK_SIZE;
+ int error;
+ if (SR_SUCCESS != ProcessNextEntry(find_.front(), &error)) {
+ return false;
+ }
+ } else {
+ if (!Filesystem::CreateFolder(root_folder_)) {
+ return false;
+ }
+
+ mode_ = M_WRITE;
+ next_block_ = NB_FILE_HEADER;
+ block_pos_ = 0;
+ }
+ return true;
+}
+
+StreamState TarStream::GetState() const {
+ return (M_NONE == mode_) ? SS_CLOSED : SS_OPEN;
+}
+
+StreamResult TarStream::Read(void* buffer, size_t buffer_len,
+ size_t* read, int* error) {
+ if (M_READ != mode_) {
+ return SR_EOS;
+ }
+ return ProcessBuffer(buffer, buffer_len, read, error);
+}
+
+StreamResult TarStream::Write(const void* data, size_t data_len,
+ size_t* written, int* error) {
+ if (M_WRITE != mode_) {
+ return SR_EOS;
+ }
+ // Note: data is not modified unless M_READ == mode_
+ return ProcessBuffer(const_cast<void*>(data), data_len, written, error);
+}
+
+void TarStream::Close() {
+ root_folder_.clear();
+ next_block_ = NB_NONE;
+ block_pos_ = 0;
+ delete current_;
+ current_ = NULL;
+ current_bytes_ = 0;
+ for (DirectoryList::iterator it = find_.begin(); it != find_.end(); ++it) {
+ delete(*it);
+ }
+ find_.clear();
+ subfolder_.clear();
+}
+
+StreamResult TarStream::ProcessBuffer(void* buffer, size_t buffer_len,
+ size_t* consumed, int* error) {
+ size_t local_consumed;
+ if (!consumed) consumed = &local_consumed;
+ int local_error;
+ if (!error) error = &local_error;
+
+ StreamResult result = SR_SUCCESS;
+ *consumed = 0;
+
+ while (*consumed < buffer_len) {
+ size_t available = BLOCK_SIZE - block_pos_;
+ if (available == 0) {
+ result = ProcessNextBlock(error);
+ if (SR_SUCCESS != result) {
+ break;
+ }
+ } else {
+ size_t bytes_to_copy = talk_base::_min(available, buffer_len - *consumed);
+ char* buffer_ptr = static_cast<char*>(buffer) + *consumed;
+ char* block_ptr = block_ + block_pos_;
+ if (M_READ == mode_) {
+ memcpy(buffer_ptr, block_ptr, bytes_to_copy);
+ } else {
+ memcpy(block_ptr, buffer_ptr, bytes_to_copy);
+ }
+ *consumed += bytes_to_copy;
+ block_pos_ += bytes_to_copy;
+ }
+ }
+
+ // SR_EOS means no data was consumed on this operation. So we may need to
+ // return SR_SUCCESS instead, and then we will return SR_EOS next time.
+ if ((SR_EOS == result) && (*consumed > 0)) {
+ result = SR_SUCCESS;
+ }
+
+ return result;
+}
+
+StreamResult TarStream::ProcessNextBlock(int* error) {
+ ASSERT(NULL != error);
+ ASSERT(M_NONE != mode_);
+ ASSERT(BLOCK_SIZE == block_pos_);
+
+ StreamResult result;
+ if (NB_NONE == next_block_) {
+
+ return SR_EOS;
+
+ } else if (NB_TRAILER == next_block_) {
+
+ // Trailer block is zeroed
+ result = ProcessEmptyBlock(0, error);
+ if (SR_SUCCESS != result)
+ return result;
+ next_block_ = NB_NONE;
+
+ } else if (NB_FILE_HEADER == next_block_) {
+
+ if (M_READ == mode_) {
+ result = ReadNextFile(error);
+ } else {
+ result = WriteNextFile(error);
+ }
+
+ // If there are no more files, we are at the first trailer block
+ if (SR_EOS == result) {
+ block_pos_ = 0;
+ next_block_ = NB_TRAILER;
+ result = ProcessEmptyBlock(0, error);
+ }
+ if (SR_SUCCESS != result)
+ return result;
+
+ } else if (NB_DATA == next_block_) {
+
+ size_t block_consumed = 0;
+ size_t block_available = talk_base::_min<size_t>(BLOCK_SIZE, current_bytes_);
+ while (block_consumed < block_available) {
+ void* block_ptr = static_cast<char*>(block_) + block_consumed;
+ size_t available = block_available - block_consumed, consumed;
+ if (M_READ == mode_) {
+ ASSERT(NULL != current_);
+ result = current_->Read(block_ptr, available, &consumed, error);
+ } else if (current_) {
+ result = current_->Write(block_ptr, available, &consumed, error);
+ } else {
+ consumed = available;
+ result = SR_SUCCESS;
+ }
+ switch (result) {
+ case SR_ERROR:
+ return result;
+ case SR_BLOCK:
+ case SR_EOS:
+ ASSERT(false);
+ *error = 0; // TODO: make errors
+ return SR_ERROR;
+ case SR_SUCCESS:
+ block_consumed += consumed;
+ break;
+ }
+ }
+
+ current_bytes_ -= block_consumed;
+ if (current_bytes_ == 0) {
+ // The remainder of the block is zeroed
+ result = ProcessEmptyBlock(block_consumed, error);
+ if (SR_SUCCESS != result)
+ return result;
+ delete current_;
+ current_ = NULL;
+ next_block_ = NB_FILE_HEADER;
+ }
+
+ } else {
+ ASSERT(false);
+ }
+
+ block_pos_ = 0;
+ return SR_SUCCESS;
+}
+
+StreamResult TarStream::ProcessEmptyBlock(size_t start, int* error) {
+ ASSERT(NULL != error);
+ ASSERT(M_NONE != mode_);
+ if (M_READ == mode_) {
+ memset(block_ + start, 0, BLOCK_SIZE - start);
+ } else {
+ if (!talk_base::memory_check(block_ + start, 0, BLOCK_SIZE - start)) {
+ *error = 0; // TODO: make errors
+ return SR_ERROR;
+ }
+ }
+ return SR_SUCCESS;
+}
+
+StreamResult TarStream::ReadNextFile(int* error) {
+ ASSERT(NULL != error);
+ ASSERT(M_READ == mode_);
+ ASSERT(NB_FILE_HEADER == next_block_);
+ ASSERT(BLOCK_SIZE == block_pos_);
+ ASSERT(NULL == current_);
+
+ // ReadNextFile conducts a depth-first recursive search through the directory
+ // tree. find_ maintains a stack of open directory handles, which
+ // corresponds to our current position in the tree. At any point, the
+ // directory at the top (front) of the stack is being enumerated. If a
+ // directory is found, it is opened and pushed onto the top of the stack.
+ // When a directory enumeration completes, that directory is popped off the
+ // top of the stack.
+
+ // Note: Since ReadNextFile can only return one block of data at a time, we
+ // cannot simultaneously return the entry for a directory, and the entry for
+ // the first element in that directory at the same time. In this case, we
+ // push a NULL entry onto the find_ stack, which indicates that the next
+ // iteration should begin enumeration of the "new" directory.
+ StreamResult result = SR_SUCCESS;
+ while (BLOCK_SIZE == block_pos_) {
+ ASSERT(!find_.empty());
+
+ if (NULL != find_.front()) {
+ if (find_.front()->Next()) {
+ result = ProcessNextEntry(find_.front(), error);
+ if (SR_SUCCESS != result) {
+ return result;
+ }
+ continue;
+ }
+ delete(find_.front());
+ } else {
+ Pathname pattern(root_folder_);
+ pattern.AppendFolder(subfolder_);
+ find_.front() = new DirectoryIterator();
+ if (find_.front()->Iterate(pattern.pathname())) {
+ result = ProcessNextEntry(find_.front(), error);
+ if (SR_SUCCESS != result) {
+ return result;
+ }
+ continue;
+ }
+ // TODO: should this be an error?
+ LOG_F(LS_WARNING) << "Couldn't open folder: " << pattern.pathname();
+ }
+
+ find_.pop_front();
+ subfolder_ = Pathname(subfolder_).parent_folder();
+
+ if (find_.empty()) {
+ return SR_EOS;
+ }
+ }
+
+ ASSERT(0 == block_pos_);
+ return SR_SUCCESS;
+}
+
+StreamResult TarStream::WriteNextFile(int* error) {
+ ASSERT(NULL != error);
+ ASSERT(M_WRITE == mode_);
+ ASSERT(NB_FILE_HEADER == next_block_);
+ ASSERT(BLOCK_SIZE == block_pos_);
+ ASSERT(NULL == current_);
+ ASSERT(0 == current_bytes_);
+
+ std::string pathname, link, linked_name, magic, mversion;
+ size_t file_size, modify_time, unused, checksum;
+
+ size_t block_data = 0;
+ ReadFieldS(block_data, 100, &pathname);
+ ReadFieldN(block_data, 8, &unused); // mode
+ ReadFieldN(block_data, 8, &unused); // owner uid
+ ReadFieldN(block_data, 8, &unused); // owner gid
+ ReadFieldN(block_data, 12, &file_size);
+ ReadFieldN(block_data, 12, &modify_time);
+ ReadFieldN(block_data, 8, &checksum);
+ if (checksum == 0)
+ block_data -= 8; // back-compatiblity
+ ReadFieldS(block_data, 1, &link);
+ ReadFieldS(block_data, 100, &linked_name); // name of linked file
+ ReadFieldS(block_data, 6, &magic);
+ ReadFieldS(block_data, 2, &mversion);
+
+ if (pathname.empty())
+ return SR_EOS;
+
+ std::string user, group, dev_major, dev_minor, prefix;
+ if (magic == "ustar" || magic == "ustar ") {
+ ReadFieldS(block_data, 32, &user);
+ ReadFieldS(block_data, 32, &group);
+ ReadFieldS(block_data, 8, &dev_major);
+ ReadFieldS(block_data, 8, &dev_minor);
+ ReadFieldS(block_data, 155, &prefix);
+
+ pathname = prefix + pathname;
+ }
+
+ // Rest of the block must be empty
+ StreamResult result = ProcessEmptyBlock(block_data, error);
+ if (SR_SUCCESS != result) {
+ return result;
+ }
+
+ Pathname archive_path(pathname);
+ archive_path.SetFolderDelimiter('/');
+ archive_path.Normalize();
+
+ bool is_folder = archive_path.filename().empty();
+ if (is_folder) {
+ ASSERT(NB_FILE_HEADER == next_block_);
+ ASSERT(0 == file_size);
+ } else if (file_size > 0) {
+ // We assign current_bytes_ because we must skip over the upcoming data
+ // segments, regardless of whether we want to write them.
+ next_block_ = NB_DATA;
+ current_bytes_ = file_size;
+ }
+
+ if (!CheckFilter(archive_path.pathname())) {
+ // If it's a directory, we will ignore it and all children by nature of
+ // filter prefix matching. If it is a file, we will ignore it because
+ // current_ is NULL.
+ return SR_SUCCESS;
+ }
+
+ // Sanity checks:
+ // 1) No .. path segments
+ if (archive_path.pathname().find("../") != std::string::npos) {
+ LOG_F(LS_WARNING) << "Skipping path with .. entry: "
+ << archive_path.pathname();
+ return SR_SUCCESS;
+ }
+ // 2) No drive letters
+ if (archive_path.pathname().find(':') != std::string::npos) {
+ LOG_F(LS_WARNING) << "Skipping path with drive letter: "
+ << archive_path.pathname();
+ return SR_SUCCESS;
+ }
+ // 3) No absolute paths
+ if (archive_path.pathname().find("//") != std::string::npos) {
+ LOG_F(LS_WARNING) << "Skipping absolute path: "
+ << archive_path.pathname();
+ return SR_SUCCESS;
+ }
+
+ Pathname local_path(root_folder_);
+ local_path.AppendPathname(archive_path.pathname());
+ local_path.Normalize();
+
+ if (is_folder) {
+ if (!Filesystem::CreateFolder(local_path)) {
+ LOG_F(LS_WARNING) << "Couldn't create folder: " << local_path.pathname();
+ *error = 0; // TODO
+ return SR_ERROR;
+ }
+ } else {
+ FileStream* stream = new FileStream;
+
+ if (!stream->Open(local_path.pathname().c_str(), "wb")) {
+ LOG_F(LS_WARNING) << "Couldn't create file: " << local_path.pathname();
+ *error = 0; // TODO
+ delete stream;
+ return SR_ERROR;
+ }
+ if (file_size > 0) {
+ current_ = stream;
+ } else {
+ stream->Close();
+ delete stream;
+ }
+ }
+
+ SignalNextEntry(archive_path.filename(), current_bytes_);
+
+
+ return SR_SUCCESS;
+}
+
+StreamResult TarStream::ProcessNextEntry(const DirectoryIterator *data, int *error) {
+ ASSERT(M_READ == mode_);
+ ASSERT(NB_FILE_HEADER == next_block_);
+ ASSERT(BLOCK_SIZE == block_pos_);
+ ASSERT(NULL == current_);
+ ASSERT(0 == current_bytes_);
+
+ if (data->IsDirectory() &&
+ (data->Name() == "." || data->Name() == ".."))
+ return SR_SUCCESS;
+
+ Pathname archive_path;
+ archive_path.SetFolder(subfolder_);
+ if (data->IsDirectory()) {
+ archive_path.AppendFolder(data->Name());
+ } else {
+ archive_path.SetFilename(data->Name());
+ }
+ archive_path.SetFolderDelimiter('/');
+ archive_path.Normalize();
+
+ if (!CheckFilter(archive_path.pathname()))
+ return SR_SUCCESS;
+
+ if (archive_path.pathname().length() > 255) {
+ // Cannot send a file name longer than 255 (yet)
+ return SR_ERROR;
+ }
+
+ Pathname local_path(root_folder_);
+ local_path.AppendPathname(archive_path.pathname());
+ local_path.Normalize();
+
+ if (data->IsDirectory()) {
+ // Note: the NULL handle indicates that we need to open the folder next
+ // time.
+ find_.push_front(NULL);
+ subfolder_ = archive_path.pathname();
+ } else {
+ FileStream* stream = new FileStream;
+ if (!stream->Open(local_path.pathname().c_str(), "rb")) {
+ // TODO: Should this be an error?
+ LOG_F(LS_WARNING) << "Couldn't open file: " << local_path.pathname();
+ delete stream;
+ return SR_SUCCESS;
+ }
+ current_ = stream;
+ current_bytes_ = data->FileSize();
+ }
+
+ time_t modify_time = data->FileModifyTime();
+
+ std::string pathname = archive_path.pathname();
+ std::string magic, user, group, dev_major, dev_minor, prefix;
+ std::string name = pathname;
+ bool ustar = false;
+ if (name.length() > 100) {
+ ustar = true;
+ // Put last 100 characters into the name, and rest in prefix
+ size_t path_length = pathname.length();
+ prefix = pathname.substr(0, path_length - 100);
+ name = pathname.substr(path_length - 100);
+ }
+
+ size_t block_data = 0;
+ memset(block_, 0, BLOCK_SIZE);
+ WriteFieldS(block_data, 100, name.c_str());
+ WriteFieldS(block_data, 8, data->IsDirectory() ? "777" : "666"); // mode
+ WriteFieldS(block_data, 8, "5"); // owner uid
+ WriteFieldS(block_data, 8, "5"); // owner gid
+ WriteFieldN(block_data, 12, current_bytes_);
+ WriteFieldN(block_data, 12, modify_time);
+ WriteFieldS(block_data, 8, " "); // Checksum. To be filled in later.
+ WriteFieldS(block_data, 1, data->IsDirectory() ? "5" : "0"); // link indicator (0 == normal file, 5 == directory)
+ WriteFieldS(block_data, 100, ""); // name of linked file
+
+ if (ustar) {
+ WriteFieldS(block_data, 6, "ustar");
+ WriteFieldS(block_data, 2, "");
+ WriteFieldS(block_data, 32, user.c_str());
+ WriteFieldS(block_data, 32, group.c_str());
+ WriteFieldS(block_data, 8, dev_major.c_str());
+ WriteFieldS(block_data, 8, dev_minor.c_str());
+ WriteFieldS(block_data, 155, prefix.c_str());
+ }
+
+ // Rest of the block must be empty
+ StreamResult result = ProcessEmptyBlock(block_data, error);
+ WriteChecksum();
+
+ block_pos_ = 0;
+ if (current_bytes_ > 0) {
+ next_block_ = data->IsDirectory() ? NB_FILE_HEADER : NB_DATA;
+ }
+
+ SignalNextEntry(archive_path.filename(), current_bytes_);
+
+ return result;
+}
+
+void TarStream::WriteChecksum() {
+ unsigned int sum = 0;
+
+ for (int i = 0; i < BLOCK_SIZE; i++)
+ sum += static_cast<unsigned char>(block_[i]);
+
+ sprintf(block_ + 148, "%06o", sum);
+}
+
+bool TarStream::CheckFilter(const std::string& pathname) {
+ if (filters_.empty())
+ return true;
+
+ // pathname is allowed when there is a filter which:
+ // A) Equals name
+ // B) Matches a folder prefix of name
+ for (size_t i=0; i<filters_.size(); ++i) {
+ const std::string& filter = filters_[i];
+ // Make sure the filter is a prefix of name
+ if (_strnicmp(pathname.c_str(), filter.data(), filter.length()) != 0)
+ continue;
+
+ // If the filter is not a directory, must match exactly
+ if (!Pathname::IsFolderDelimiter(filter[filter.length()-1])
+ && (filter.length() != pathname.length()))
+ continue;
+
+ return true;
+ }
+
+ return false;
+}
+
+void TarStream::WriteFieldN(size_t& pos, size_t max_len, size_t numeric_field) {
+ WriteFieldF(pos, max_len, "%.*o", max_len - 1, numeric_field);
+}
+
+void TarStream::WriteFieldS(size_t& pos, size_t max_len,
+ const char* string_field) {
+ ASSERT(pos + max_len <= BLOCK_SIZE);
+ size_t len = strlen(string_field);
+ size_t use_len = _min(len, max_len);
+ memcpy(block_ + pos, string_field, use_len);
+ pos += max_len;
+}
+
+void TarStream::WriteFieldF(size_t& pos, size_t max_len,
+ const char* format, ...) {
+ va_list args;
+ va_start(args, format);
+ char buffer[BLOCK_SIZE];
+ vsprintfn(buffer, ARRAY_SIZE(buffer), format, args);
+ WriteFieldS(pos, max_len, buffer);
+ va_end(args);
+}
+
+void TarStream::ReadFieldN(size_t& pos, size_t max_len, size_t* numeric_field) {
+ ASSERT(NULL != numeric_field);
+ std::string buffer;
+ ReadFieldS(pos, max_len, &buffer);
+
+ int value;
+ if (!buffer.empty() && (1 == sscanf(buffer.c_str(), "%o", &value))) {
+ *numeric_field = value;
+ } else {
+ *numeric_field = 0;
+ }
+}
+
+void TarStream::ReadFieldS(size_t& pos, size_t max_len,
+ std::string* string_field) {
+ ASSERT(NULL != string_field);
+ ASSERT(pos + max_len <= BLOCK_SIZE);
+ size_t value_len = talk_base::strlenn(block_ + pos, max_len);
+ string_field->assign(block_ + pos, value_len);
+ ASSERT(talk_base::memory_check(block_ + pos + value_len,
+ 0,
+ max_len - value_len));
+ pos += max_len;
+}
diff --git a/Plugins/jingle/libjingle/talk/base/tarstream.h b/Plugins/jingle/libjingle/talk/base/tarstream.h
new file mode 100644
index 0000000..772fb14
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/tarstream.h
@@ -0,0 +1,104 @@
+#ifndef TALK_APP_WIN32_TARSTREAM_H__
+#define TALK_APP_WIN32_TARSTREAM_H__
+
+#include <string>
+#include <vector>
+#include "talk/base/fileutils.h"
+#include "talk/base/sigslot.h"
+#include "talk/base/stream.h"
+
+namespace talk_base {
+
+///////////////////////////////////////////////////////////////////////////////
+// TarStream - acts as a source or sink for a tar-encoded collection of files
+// and directories. Operates synchronously.
+///////////////////////////////////////////////////////////////////////////////
+
+class TarStream : public StreamInterface {
+ public:
+ TarStream();
+ virtual ~TarStream();
+
+ // AddFilter is used to limit the elements which will be read or written.
+ // In general, all members of the parent folder are read, and all members
+ // of a tarfile are written. However, if any filters are added, only those
+ // items (and their contents, in the case of folders) are processed. Filters
+ // must be added before opening the stream.
+ bool AddFilter(const std::string& pathname);
+
+ // 'folder' is parent of the tar contents. All paths will be evaluated
+ // relative to it. When 'read' is true, the specified folder will be
+ // traversed, and a tar stream will be generated (via Read). Otherwise, a
+ // tar stream is consumed (via Write), and files and folders will be created.
+ bool Open(const std::string& folder, bool read);
+
+ virtual talk_base::StreamState GetState() const;
+ virtual talk_base::StreamResult Read(void* buffer, size_t buffer_len,
+ size_t* read, int* error);
+ virtual talk_base::StreamResult Write(const void* data, size_t data_len,
+ size_t* written, int* error);
+ virtual void Close();
+
+ virtual bool GetSize(size_t* size) const { return false; }
+ virtual bool ReserveSize(size_t size) { return true; }
+ virtual bool Rewind() { return false; }
+
+ // Every time a new entry header is read/written, this signal is fired with
+ // the entry's name and size.
+ sigslot::signal2<const std::string&, size_t> SignalNextEntry;
+
+ private:
+ typedef std::list<DirectoryIterator*> DirectoryList;
+ enum ModeType { M_NONE, M_READ, M_WRITE };
+ enum NextBlockType { NB_NONE, NB_FILE_HEADER, NB_DATA, NB_TRAILER };
+ enum { BLOCK_SIZE = 512 };
+
+ talk_base::StreamResult ProcessBuffer(void* buffer, size_t buffer_len,
+ size_t* consumed, int* error);
+ talk_base::StreamResult ProcessNextBlock(int* error);
+ talk_base::StreamResult ProcessEmptyBlock(size_t start, int* error);
+ talk_base::StreamResult ReadNextFile(int* error);
+ talk_base::StreamResult WriteNextFile(int* error);
+
+ talk_base::StreamResult ProcessNextEntry(const DirectoryIterator *data,
+ int *error);
+
+ // Determine whether the given entry is allowed by our filters
+ bool CheckFilter(const std::string& pathname);
+
+ void WriteFieldN(size_t& pos, size_t max_len, size_t numeric_field);
+ void WriteFieldS(size_t& pos, size_t max_len, const char* string_field);
+ void WriteFieldF(size_t& pos, size_t max_len, const char* format, ...);
+
+ void ReadFieldN(size_t& pos, size_t max_len, size_t* numeric_field);
+ void ReadFieldS(size_t& pos, size_t max_len, std::string* string_field);
+
+ void WriteChecksum(void);
+
+ // Files and/or folders that should be processed
+ std::vector<std::string> filters_;
+ // Folder passed to Open
+ std::string root_folder_;
+ // Open for read or write?
+ ModeType mode_;
+ // The expected type of the next block
+ NextBlockType next_block_;
+ // The partial contents of the current block
+ char block_[BLOCK_SIZE];
+ size_t block_pos_;
+ // The file which is currently being read or written
+ talk_base::FileStream* current_;
+ // Bytes remaining to be processed for current_
+ size_t current_bytes_;
+ // Note: the following variables are used in M_READ mode only.
+ // Stack of open directory handles, representing depth-first search
+ DirectoryList find_;
+ // Subfolder path corresponding to current position in the directory tree
+ std::string subfolder_;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+} // namespace talk_base
+
+#endif // TALK_APP_WIN32_TARSTREAM_H__
diff --git a/Plugins/jingle/libjingle/talk/base/task.cc b/Plugins/jingle/libjingle/talk/base/task.cc
new file mode 100644
index 0000000..d7e0e82
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/task.cc
@@ -0,0 +1,299 @@
+/*
+ * libjingle
+ * Copyright 2004--2006, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <algorithm>
+
+#include "talk/base/task.h"
+#include "talk/base/common.h"
+#include "talk/base/taskrunner.h"
+
+namespace talk_base {
+
+int32 Task::unique_id_seed_ = 0;
+
+Task::Task(Task *parent)
+ : state_(STATE_INIT),
+ parent_(parent),
+ blocked_(false),
+ done_(false),
+ aborted_(false),
+ busy_(false),
+ error_(false),
+ child_error_(false),
+ start_time_(0),
+ timeout_seconds_(0),
+ timeout_time_(0),
+ timeout_suspended_(false) {
+ children_.reset(new ChildSet());
+ runner_ = ((parent == NULL) ?
+ reinterpret_cast<TaskRunner *>(this) :
+ parent->GetRunner());
+ if (parent_ != NULL) {
+ parent_->AddChild(this);
+ }
+
+ unique_id_ = unique_id_seed_++;
+
+ // sanity check that we didn't roll-over our id seed
+ ASSERT(unique_id_ < unique_id_seed_);
+}
+
+int64 Task::CurrentTime() {
+ return runner_->CurrentTime();
+}
+
+int64 Task::ElapsedTime() {
+ return CurrentTime() - start_time_;
+}
+
+void Task::Start() {
+ if (state_ != STATE_INIT)
+ return;
+ // Set the start time before starting the task. Otherwise if the task
+ // finishes quickly and deletes the Task object, setting start_time_
+ // will crash.
+ start_time_ = CurrentTime();
+ GetRunner()->StartTask(this);
+}
+
+void Task::Step() {
+ if (done_) {
+#ifdef DEBUG
+ // we do not know how !blocked_ happens when done_ - should be impossible.
+ // But it causes problems, so in retail build, we force blocked_, and
+ // under debug we assert.
+ assert(blocked_);
+#else
+ blocked_ = true;
+#endif
+ return;
+ }
+
+ // Async Error() was called
+ if (error_) {
+ done_ = true;
+ state_ = STATE_ERROR;
+ blocked_ = true;
+// obsolete - an errored task is not considered done now
+// SignalDone();
+ Stop();
+ return;
+ }
+
+ busy_ = true;
+ int new_state = Process(state_);
+ busy_ = false;
+
+ if (aborted_) {
+ Abort(true); // no need to wake because we're awake
+ return;
+ }
+
+ if (new_state == STATE_BLOCKED) {
+ blocked_ = true;
+ // Let the timeout continue
+ } else {
+ state_ = new_state;
+ blocked_ = false;
+ ResetTimeout();
+ }
+
+ if (new_state == STATE_DONE) {
+ done_ = true;
+ } else if (new_state == STATE_ERROR) {
+ done_ = true;
+ error_ = true;
+ }
+
+ if (done_) {
+// obsolete - call this yourself
+// SignalDone();
+ Stop();
+ blocked_ = true;
+ }
+}
+
+void Task::Abort(bool nowake) {
+ if (aborted_ || done_)
+ return;
+ aborted_ = true;
+ if (!busy_) {
+ done_ = true;
+ blocked_ = true;
+ error_ = true;
+ Stop();
+ if (!nowake)
+ Wake(); // to self-delete
+ }
+}
+
+void Task::Wake() {
+ if (done_)
+ return;
+ if (blocked_) {
+ blocked_ = false;
+ GetRunner()->WakeTasks();
+ }
+}
+
+void Task::Error() {
+ if (error_ || done_)
+ return;
+ error_ = true;
+ Wake();
+}
+
+std::string Task::GetStateName(int state) const {
+ static const std::string STR_BLOCKED("BLOCKED");
+ static const std::string STR_INIT("INIT");
+ static const std::string STR_START("START");
+ static const std::string STR_DONE("DONE");
+ static const std::string STR_ERROR("ERROR");
+ static const std::string STR_RESPONSE("RESPONSE");
+ static const std::string STR_HUH("??");
+ switch (state) {
+ case STATE_BLOCKED: return STR_BLOCKED;
+ case STATE_INIT: return STR_INIT;
+ case STATE_START: return STR_START;
+ case STATE_DONE: return STR_DONE;
+ case STATE_ERROR: return STR_ERROR;
+ case STATE_RESPONSE: return STR_RESPONSE;
+ }
+ return STR_HUH;
+}
+
+int Task::Process(int state) {
+ int newstate = STATE_ERROR;
+
+ if (TimedOut()) {
+ ClearTimeout();
+ newstate = OnTimeout();
+ SignalTimeout();
+ } else {
+ switch (state) {
+ case STATE_INIT:
+ newstate = STATE_START;
+ break;
+ case STATE_START:
+ newstate = ProcessStart();
+ break;
+ case STATE_RESPONSE:
+ newstate = ProcessResponse();
+ break;
+ case STATE_DONE:
+ case STATE_ERROR:
+ newstate = STATE_BLOCKED;
+ break;
+ }
+ }
+
+ return newstate;
+}
+
+void Task::AddChild(Task *child) {
+ children_->insert(child);
+}
+
+bool Task::AllChildrenDone() {
+ for (ChildSet::iterator it = children_->begin();
+ it != children_->end();
+ ++it) {
+ if (!(*it)->IsDone())
+ return false;
+ }
+ return true;
+}
+
+bool Task::AnyChildError() {
+ return child_error_;
+}
+
+void Task::AbortAllChildren() {
+ if (children_->size() > 0) {
+ ChildSet copy = *children_;
+ for (ChildSet::iterator it = copy.begin(); it != copy.end(); ++it) {
+ (*it)->Abort(true); // Note we do not wake
+ }
+ }
+}
+
+void Task::Stop() {
+ // No need to wake because we're either awake or in abort
+ AbortAllChildren();
+ parent_->OnChildStopped(this);
+}
+
+void Task::OnChildStopped(Task *child) {
+ if (child->HasError())
+ child_error_ = true;
+ children_->erase(child);
+}
+
+void Task::set_timeout_seconds(const int timeout_seconds) {
+ timeout_seconds_ = timeout_seconds;
+ ResetTimeout();
+}
+
+bool Task::TimedOut() {
+ return timeout_seconds_ &&
+ timeout_time_ &&
+ CurrentTime() > timeout_time_;
+}
+
+void Task::ResetTimeout() {
+ bool timeout_allowed = (state_ != STATE_INIT)
+ && (state_ != STATE_DONE)
+ && (state_ != STATE_ERROR);
+ if (timeout_seconds_ && timeout_allowed && !timeout_suspended_)
+ timeout_time_ = CurrentTime() +
+ (timeout_seconds_ * kSecToMsec * kMsecTo100ns);
+ else
+ timeout_time_ = 0;
+
+ GetRunner()->UpdateTaskTimeout(this);
+}
+
+void Task::ClearTimeout() {
+ timeout_time_ = 0;
+ GetRunner()->UpdateTaskTimeout(this);
+}
+
+void Task::SuspendTimeout() {
+ if (!timeout_suspended_) {
+ timeout_suspended_ = true;
+ ResetTimeout();
+ }
+}
+
+void Task::ResumeTimeout() {
+ if (timeout_suspended_) {
+ timeout_suspended_ = false;
+ ResetTimeout();
+ }
+}
+
+} // namespace talk_base
diff --git a/Plugins/jingle/libjingle/talk/base/task.h b/Plugins/jingle/libjingle/talk/base/task.h
new file mode 100644
index 0000000..b524ab7
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/task.h
@@ -0,0 +1,218 @@
+/*
+ * libjingle
+ * Copyright 2004--2006, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TALK_BASE_TASK_H__
+#define TALK_BASE_TASK_H__
+
+#include <string>
+#include "talk/base/scoped_ptr.h"
+#include "talk/base/basictypes.h"
+#include "talk/base/sigslot.h"
+
+/////////////////////////////////////////////////////////////////////
+//
+// TASK
+//
+/////////////////////////////////////////////////////////////////////
+//
+// Task is a state machine infrastructure. States are pushed forward by
+// pushing forwards a TaskRunner that holds on to all Tasks. The purpose
+// of Task is threefold:
+//
+// (1) It manages ongoing work on the UI thread. Multitasking without
+// threads, keeping it easy, keeping it real. :-) It does this by
+// organizing a set of states for each task. When you return from your
+// Process*() function, you return an integer for the next state. You do
+// not go onto the next state yourself. Every time you enter a state,
+// you check to see if you can do anything yet. If not, you return
+// STATE_BLOCKED. If you _could_ do anything, do not return
+// STATE_BLOCKED - even if you end up in the same state, return
+// STATE_mysamestate. When you are done, return STATE_DONE and then the
+// task will self-delete sometimea afterwards.
+//
+// (2) It helps you avoid all those reentrancy problems when you chain
+// too many triggers on one thread. Basically if you want to tell a task
+// to process something for you, you feed your task some information and
+// then you Wake() it. Don't tell it to process it right away. If it
+// might be working on something as you send it infomration, you may want
+// to have a queue in the task.
+//
+// (3) Finally it helps manage parent tasks and children. If a parent
+// task gets aborted, all the children tasks are too. The nice thing
+// about this, for example, is if you have one parent task that
+// represents, say, and Xmpp connection, then you can spawn a whole bunch
+// of infinite lifetime child tasks and now worry about cleaning them up.
+// When the parent task goes to STATE_DONE, the task engine will make
+// sure all those children are aborted and get deleted.
+//
+// Notice that Task has a few built-in states, e.g.,
+//
+// STATE_INIT - the task isn't running yet
+// STATE_START - the task is in its first state
+// STATE_RESPONSE - the task is in its second state
+// STATE_DONE - the task is done
+//
+// STATE_ERROR - indicates an error - we should audit the error code in
+// light of any usage of it to see if it should be improved. When I
+// first put down the task stuff I didn't have a good sense of what was
+// needed for Abort and Error, and now the subclasses of Task will ground
+// the design in a stronger way.
+//
+// STATE_NEXT - the first undefined state number. (like WM_USER) - you
+// can start defining more task states there.
+//
+// When you define more task states, just override Process(int state) and
+// add your own switch statement. If you want to delegate to
+// Task::Process, you can effectively delegate to its switch statement.
+// No fancy method pointers or such - this is all just pretty low tech,
+// easy to debug, and fast.
+//
+// Also notice that Task has some primitive built-in timeout functionality.
+//
+// A timeout is defined as "the task stays in STATE_BLOCKED longer than
+// timeout_seconds_."
+//
+// Descendant classes can override this behavior by calling the
+// various protected methods to change the timeout behavior. For
+// instance, a descendand might call SuspendTimeout() when it knows
+// that it isn't waiting for anything that might timeout, but isn't
+// yet in the STATE_DONE state.
+//
+
+namespace talk_base {
+
+class TaskRunner;
+
+// A task executes a sequence of steps
+
+class Task;
+class RootTask;
+
+class Task {
+ public:
+ Task(Task *parent);
+ virtual ~Task() {}
+
+ int32 get_unique_id() { return unique_id_; }
+
+ void Start();
+ void Step();
+ int GetState() const { return state_; }
+ bool HasError() const { return (GetState() == STATE_ERROR); }
+ bool Blocked() const { return blocked_; }
+ bool IsDone() const { return done_; }
+ int64 ElapsedTime();
+
+ Task *GetParent() { return parent_; }
+ TaskRunner *GetRunner() { return runner_; }
+ virtual Task *GetParent(int code) { return parent_->GetParent(code); }
+
+ // Called from outside to stop task without any more callbacks
+ void Abort(bool nowake = false);
+
+ // For managing children
+ bool AllChildrenDone();
+ bool AnyChildError();
+
+ bool TimedOut();
+
+ int64 get_timeout_time() { return timeout_time_; }
+ void set_timeout_seconds(int timeout_seconds);
+
+ sigslot::signal0<> SignalTimeout;
+
+ // Called inside the task to signal that the task may be unblocked
+ void Wake();
+
+ protected:
+
+ enum {
+ STATE_BLOCKED = -1,
+ STATE_INIT = 0,
+ STATE_START = 1,
+ STATE_DONE = 2,
+ STATE_ERROR = 3,
+ STATE_RESPONSE = 4,
+ STATE_NEXT = 5, // Subclasses which need more states start here and higher
+ };
+
+ // Called inside to advise that the task should wake and signal an error
+ void Error();
+
+ int64 CurrentTime();
+
+ virtual std::string GetStateName(int state) const;
+ virtual int Process(int state);
+ virtual void Stop();
+ virtual int ProcessStart() = 0;
+ virtual int ProcessResponse() { return STATE_DONE; }
+
+ // for managing children (if any)
+ void AddChild(Task *child);
+ void AbortAllChildren();
+
+ void ResetTimeout();
+ void ClearTimeout();
+
+ void SuspendTimeout();
+ void ResumeTimeout();
+
+ protected:
+ virtual int OnTimeout() {
+ // by default, we are finished after timing out
+ return STATE_DONE;
+ }
+
+ private:
+ void Done();
+ void OnChildStopped(Task *child);
+
+ int state_;
+ Task *parent_;
+ TaskRunner *runner_;
+ bool blocked_;
+ bool done_;
+ bool aborted_;
+ bool busy_;
+ bool error_;
+ bool child_error_;
+ int64 start_time_;
+ int64 timeout_time_;
+ int timeout_seconds_;
+ bool timeout_suspended_;
+ int32 unique_id_;
+
+ static int32 unique_id_seed_;
+
+ // for managing children
+ typedef std::set<Task *> ChildSet;
+ scoped_ptr<ChildSet> children_;
+};
+
+} // namespace talk_base
+
+#endif // TALK_BASE_TASK_H__
diff --git a/Plugins/jingle/libjingle/talk/base/taskrunner.cc b/Plugins/jingle/libjingle/talk/base/taskrunner.cc
new file mode 100644
index 0000000..050171f
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/taskrunner.cc
@@ -0,0 +1,176 @@
+/*
+ * libjingle
+ * Copyright 2004--2006, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <algorithm>
+
+#include "talk/base/taskrunner.h"
+
+#include "talk/base/common.h"
+#include "talk/base/scoped_ptr.h"
+#include "talk/base/task.h"
+#include "talk/base/logging.h"
+
+namespace talk_base {
+
+TaskRunner::TaskRunner()
+ : Task(NULL),
+ tasks_running_(false),
+ next_timeout_task_(NULL) {
+}
+
+TaskRunner::~TaskRunner() {
+ // this kills and deletes children silently!
+ AbortAllChildren();
+ RunTasks();
+}
+
+void TaskRunner::StartTask(Task * task) {
+ tasks_.push_back(task);
+
+ // the task we just started could be about to timeout --
+ // make sure our "next timeout task" is correct
+ UpdateTaskTimeout(task);
+
+ WakeTasks();
+}
+
+void TaskRunner::RunTasks() {
+ // Running continues until all tasks are Blocked (ok for a small # of tasks)
+ if (tasks_running_) {
+ return; // don't reenter
+ }
+
+ tasks_running_ = true;
+
+ int did_run = true;
+ while (did_run) {
+ did_run = false;
+ // use indexing instead of iterators because tasks_ may grow
+ for (size_t i = 0; i < tasks_.size(); ++i) {
+ while (!tasks_[i]->Blocked()) {
+ tasks_[i]->Step();
+ did_run = true;
+ }
+ }
+ }
+ // Tasks are deleted when running has paused
+ bool need_timeout_recalc = false;
+ for (size_t i = 0; i < tasks_.size(); ++i) {
+ if (tasks_[i]->IsDone()) {
+ Task* task = tasks_[i];
+ if (next_timeout_task_ &&
+ task->get_unique_id() == next_timeout_task_->get_unique_id()) {
+ next_timeout_task_ = NULL;
+ need_timeout_recalc = true;
+ }
+
+ delete task;
+ tasks_[i] = NULL;
+ }
+ }
+ // Finally, remove nulls
+ std::vector<Task *>::iterator it;
+ it = std::remove(tasks_.begin(),
+ tasks_.end(),
+ reinterpret_cast<Task *>(NULL));
+
+ tasks_.erase(it, tasks_.end());
+
+ if (need_timeout_recalc)
+ RecalcNextTimeout(NULL);
+
+ tasks_running_ = false;
+}
+
+void TaskRunner::PollTasks() {
+ // see if our "next potentially timed-out task" has indeed timed out.
+ // If it has, wake it up, then queue up the next task in line
+ if (next_timeout_task_ &&
+ next_timeout_task_->TimedOut()) {
+ next_timeout_task_->Wake();
+ WakeTasks();
+ }
+}
+
+// this function gets called frequently -- when each task changes
+// state to something other than DONE, ERROR or BLOCKED, it calls
+// ResetTimeout(), which will call this function to make sure that
+// the next timeout-able task hasn't changed. The logic in this function
+// prevents RecalcNextTimeout() from getting called in most cases,
+// effectively making the task scheduler O-1 instead of O-N
+
+void TaskRunner::UpdateTaskTimeout(Task *task) {
+ ASSERT(task != NULL);
+
+ // if the relevant task has a timeout, then
+ // check to see if it's closer than the current
+ // "about to timeout" task
+ if (task->get_timeout_time()) {
+ if (next_timeout_task_ == NULL ||
+ (task->get_timeout_time() <=
+ next_timeout_task_->get_timeout_time())) {
+ next_timeout_task_ = task;
+ }
+ } else if (next_timeout_task_ != NULL &&
+ task->get_unique_id() == next_timeout_task_->get_unique_id()) {
+ // otherwise, if the task doesn't have a timeout,
+ // and it used to be our "about to timeout" task,
+ // walk through all the tasks looking for the real
+ // "about to timeout" task
+ RecalcNextTimeout(task);
+ }
+}
+
+void TaskRunner::RecalcNextTimeout(Task *exclude_task) {
+ // walk through all the tasks looking for the one
+ // which satisfies the following:
+ // it's not finished already
+ // we're not excluding it
+ // it has the closest timeout time
+
+ int64 next_timeout_time = 0;
+ next_timeout_task_ = NULL;
+
+ for (size_t i = 0; i < tasks_.size(); ++i) {
+ Task *task = tasks_[i];
+ // if the task isn't complete, and it actually has a timeout time
+ if (!task->IsDone() &&
+ (task->get_timeout_time() > 0))
+ // if it doesn't match our "exclude" task
+ if (exclude_task == NULL ||
+ exclude_task->get_unique_id() != task->get_unique_id())
+ // if its timeout time is sooner than our current timeout time
+ if (next_timeout_time == 0 ||
+ task->get_timeout_time() <= next_timeout_time) {
+ // set this task as our next-to-timeout
+ next_timeout_time = task->get_timeout_time();
+ next_timeout_task_ = task;
+ }
+ }
+}
+
+} // namespace talk_base
diff --git a/Plugins/jingle/libjingle/talk/base/taskrunner.h b/Plugins/jingle/libjingle/talk/base/taskrunner.h
new file mode 100644
index 0000000..e1c5ed6
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/taskrunner.h
@@ -0,0 +1,74 @@
+/*
+ * libjingle
+ * Copyright 2004--2006, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TALK_BASE_TASKRUNNER_H__
+#define TALK_BASE_TASKRUNNER_H__
+
+#include <vector>
+
+#include "talk/base/sigslot.h"
+#include "talk/base/task.h"
+
+namespace talk_base {
+
+class Task;
+
+const int64 kSecToMsec = 1000;
+const int64 kMsecTo100ns = 10000;
+const int64 kSecTo100ns = kSecToMsec * kMsecTo100ns;
+
+class TaskRunner : public Task, public sigslot::has_slots<> {
+ public:
+ TaskRunner();
+ virtual ~TaskRunner();
+
+ virtual void WakeTasks() = 0;
+
+ // This method returns the current time in 100ns units since 1/1/1601. This
+ // Is the GetSystemTimeAsFileTime method on windows.
+ virtual int64 CurrentTime() = 0 ;
+
+ void StartTask(Task *task);
+ void RunTasks();
+ void PollTasks();
+
+ void UpdateTaskTimeout(Task *task);
+
+ // dummy state machine - never run.
+ virtual int ProcessStart() { return STATE_DONE; }
+
+ private:
+ std::vector<Task *> tasks_;
+ Task *next_timeout_task_;
+ bool tasks_running_;
+
+ void RecalcNextTimeout(Task *exclude_task);
+};
+
+} // namespace talk_base
+
+#endif // TASK_BASE_TASKRUNNER_H__
diff --git a/Plugins/jingle/libjingle/talk/base/testclient.cc b/Plugins/jingle/libjingle/talk/base/testclient.cc
new file mode 100644
index 0000000..ecb45fc
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/testclient.cc
@@ -0,0 +1,161 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <iostream>
+#include <cassert>
+
+#ifdef POSIX
+extern "C" {
+#include <errno.h>
+}
+#endif // POSIX
+
+#include "talk/base/testclient.h"
+#include "talk/base/time.h"
+
+
+namespace talk_base {
+
+// DESIGN: Each packet received is posted to ourselves as a message on the
+// thread given to the constructor. When we receive the message, we
+// put it into a list of packets. We take the latter step so that we
+// can wait for a new packet to arrive by calling Get/Dispatch on the
+// thread.
+
+TestClient::TestClient(AsyncPacketSocket* socket, Thread* thread)
+ : thread_(thread), socket_(socket) {
+ if (!thread_)
+ thread_ = Thread::Current();
+ packets_ = new std::vector<Packet*>();
+ socket_->SignalReadPacket.connect(this, &TestClient::OnPacket);
+}
+
+TestClient::~TestClient() {
+ delete socket_;
+ for (unsigned i = 0; i < packets_->size(); i++)
+ delete (*packets_)[i];
+}
+
+void TestClient::Send(const char* buf, size_t size) {
+ int result = socket_->Send(buf, size);
+ if (result < 0) {
+ std::cerr << "send: " << std::strerror(errno) << std::endl;
+ exit(1);
+ }
+}
+
+void TestClient::SendTo(
+ const char* buf, size_t size, const SocketAddress& dest) {
+ int result = socket_->SendTo(buf, size, dest);
+ if (result < 0) {
+ std::cerr << "sendto: " << std::strerror(errno) << std::endl;
+ exit(1);
+ }
+}
+
+void TestClient::OnPacket(
+ const char* buf, size_t size, const SocketAddress& remote_addr,
+ AsyncPacketSocket* socket) {
+ thread_->Post(this, 0, new Packet(remote_addr, buf, size));
+}
+
+void TestClient::OnMessage(Message *pmsg) {
+ assert(pmsg->pdata);
+ packets_->push_back(new Packet(*static_cast<Packet*>(pmsg->pdata)));
+}
+
+TestClient::Packet* TestClient::NextPacket() {
+
+ // If no packets are currently available, we go into a get/dispatch loop for
+ // at most 1 second. If, during the loop, a packet arrives, then we can stop
+ // early and return it.
+ //
+ // Note that the case where no packet arrives is important. We often want to
+ // test that a packet does not arrive.
+
+ if (packets_->size() == 0) {
+ uint32 msNext = 1000;
+ uint32 msEnd = GetMillisecondCount() + msNext;
+
+ while (true) {
+ Message msg;
+ if (!thread_->Get(&msg, msNext))
+ break;
+ thread_->Dispatch(&msg);
+
+ uint32 msCur = GetMillisecondCount();
+ if (msCur >= msEnd)
+ break;
+ msNext = msEnd - msCur;
+
+ if (packets_->size() > 0)
+ break;
+ }
+ }
+
+ if (packets_->size() == 0) {
+ return 0;
+ } else {
+ // Return the first packet placed in the queue.
+ Packet* packet = packets_->front();
+ packets_->erase(packets_->begin());
+ return packet;
+ }
+}
+
+void TestClient::CheckNextPacket(
+ const char* buf, size_t size, SocketAddress* addr) {
+ Packet* packet = NextPacket();
+ assert(packet);
+ assert(packet->size == size);
+ assert(std::memcmp(packet->buf, buf, size) == 0);
+ if (addr)
+ *addr = packet->addr;
+}
+
+void TestClient::CheckNoPacket() {
+ Packet* packet = NextPacket();
+ assert(!packet);
+}
+
+TestClient::Packet::Packet(const SocketAddress& a, const char* b, size_t s)
+ : addr(a), buf(0), size(s) {
+ buf = new char[size];
+ memcpy(buf, b, size);
+}
+
+TestClient::Packet::Packet(const Packet& p)
+ : addr(p.addr), buf(0), size(p.size) {
+ buf = new char[size];
+ memcpy(buf, p.buf, size);
+}
+
+TestClient::Packet::~Packet() {
+ delete buf;
+}
+
+} // namespace talk_base
diff --git a/Plugins/jingle/libjingle/talk/base/testclient.h b/Plugins/jingle/libjingle/talk/base/testclient.h
new file mode 100644
index 0000000..012e858
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/testclient.h
@@ -0,0 +1,89 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TALK_BASE_TESTCLIENT_H__
+#define TALK_BASE_TESTCLIENT_H__
+
+#include "talk/base/asyncudpsocket.h"
+#include "talk/base/thread.h"
+#include <vector>
+
+namespace talk_base {
+
+class TestClient : public MessageHandler, public sigslot::has_slots<> {
+public:
+ // Creates a client that will send and receive with the given socket and
+ // will post itself messages with the given thread.
+ TestClient(AsyncPacketSocket* socket, Thread* thread = 0);
+ ~TestClient();
+
+ // Sends using the clients socket.
+ void Send(const char* buf, size_t size);
+
+ // Sends using the clients socket to the given destination.
+ void SendTo(const char* buf, size_t size, const SocketAddress& dest);
+
+ // Records the contents of a packet that was received.
+ struct Packet : public MessageData {
+ Packet(const SocketAddress& a, const char* b, size_t s);
+ Packet(const Packet& p);
+ virtual ~Packet();
+
+ SocketAddress addr;
+ char* buf;
+ size_t size;
+ };
+
+ // Returns the next packet received by the client or 0 if none is received
+ // within a reasonable amount of time. The caller must delete the packet
+ // when done with it.
+ Packet* NextPacket();
+
+ // Checks that the next packet has the given contents. Returns the remote
+ // address that the packet was sent from.
+ void CheckNextPacket(const char* buf, size_t len, SocketAddress* addr);
+
+ // Checks that no packets have arrived or will arrive in the next second.
+ void CheckNoPacket();
+
+ // Messagehandler:
+ void OnMessage(Message *pmsg);
+
+private:
+ Thread* thread_;
+ AsyncPacketSocket* socket_;
+ std::vector<Packet*>* packets_;
+
+ // Slot for packets read on the socket.
+ void OnPacket(
+ const char* buf, size_t len, const SocketAddress& remote_addr,
+ AsyncPacketSocket* socket);
+};
+
+} // namespace talk_base
+
+#endif // TALK_BASE_TESTCLIENT_H__
diff --git a/Plugins/jingle/libjingle/talk/base/thread.cc b/Plugins/jingle/libjingle/talk/base/thread.cc
new file mode 100644
index 0000000..8fc45e1
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/thread.cc
@@ -0,0 +1,353 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifdef POSIX
+extern "C" {
+#include <sys/time.h>
+}
+#endif
+
+#include "talk/base/common.h"
+#include "talk/base/logging.h"
+#include "talk/base/thread.h"
+#include "talk/base/time.h"
+
+#define MSDEV_SET_THREAD_NAME 0x406D1388
+
+namespace talk_base {
+
+ThreadManager g_thmgr;
+
+#ifdef POSIX
+pthread_key_t ThreadManager::key_;
+
+ThreadManager::ThreadManager() {
+ pthread_key_create(&key_, NULL);
+ main_thread_ = new Thread();
+ SetCurrent(main_thread_);
+}
+
+ThreadManager::~ThreadManager() {
+ pthread_key_delete(key_);
+ delete main_thread_;
+}
+
+Thread *ThreadManager::CurrentThread() {
+ return (Thread *)pthread_getspecific(key_);
+}
+
+void ThreadManager::SetCurrent(Thread *thread) {
+ pthread_setspecific(key_, thread);
+}
+#endif
+
+#ifdef WIN32
+DWORD ThreadManager::key_;
+
+ThreadManager::ThreadManager() {
+ key_ = TlsAlloc();
+ main_thread_ = new Thread();
+ SetCurrent(main_thread_);
+}
+
+ThreadManager::~ThreadManager() {
+ TlsFree(key_);
+ delete main_thread_;
+}
+
+Thread *ThreadManager::CurrentThread() {
+ return (Thread *)TlsGetValue(key_);
+}
+
+void ThreadManager::SetCurrent(Thread *thread) {
+ TlsSetValue(key_, thread);
+}
+#endif
+
+void ThreadManager::Add(Thread *thread) {
+ CritScope cs(&crit_);
+ threads_.push_back(thread);
+}
+
+void ThreadManager::Remove(Thread *thread) {
+ CritScope cs(&crit_);
+ threads_.erase(std::remove(threads_.begin(), threads_.end(), thread), threads_.end());
+}
+
+Thread::Thread(SocketServer* ss) : MessageQueue(ss), priority_(PRIORITY_NORMAL) {
+ g_thmgr.Add(this);
+ started_ = false;
+ has_sends_ = false;
+}
+
+Thread::~Thread() {
+ Stop();
+ if (active_)
+ Clear(NULL);
+ g_thmgr.Remove(this);
+}
+
+#ifdef POSIX
+void Thread::Start() {
+ pthread_attr_t attr;
+ pthread_attr_init(&attr);
+ if (priority_ == PRIORITY_IDLE) {
+ struct sched_param param;
+ pthread_attr_getschedparam(&attr, &param);
+ param.sched_priority = 15; // +15 =
+ pthread_attr_setschedparam(&attr, &param);
+ }
+ pthread_create(&thread_, &attr, PreRun, this);
+ started_ = true;
+}
+
+void Thread::Join() {
+ if (started_) {
+ void *pv;
+ pthread_join(thread_, &pv);
+ }
+}
+#endif
+
+#ifdef WIN32
+
+typedef struct tagTHREADNAME_INFO
+{
+ DWORD dwType;
+ LPCSTR szName;
+ DWORD dwThreadID;
+ DWORD dwFlags;
+} THREADNAME_INFO;
+
+void SetThreadName( DWORD dwThreadID, LPCSTR szThreadName)
+{
+ THREADNAME_INFO info;
+ {
+ info.dwType = 0x1000;
+ info.szName = szThreadName;
+ info.dwThreadID = dwThreadID;
+ info.dwFlags = 0;
+ }
+ __try
+ {
+ RaiseException(MSDEV_SET_THREAD_NAME, 0, sizeof(info)/sizeof(DWORD), (DWORD*)&info );
+ }
+ __except(EXCEPTION_CONTINUE_EXECUTION)
+ {
+ }
+}
+
+void Thread::Start() {
+ DWORD flags = 0;
+ if (priority_ != PRIORITY_NORMAL) {
+ flags = CREATE_SUSPENDED;
+ }
+ thread_ = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)PreRun, this, flags, NULL);
+ if (thread_) {
+ if (priority_ != PRIORITY_NORMAL) {
+ if (priority_ == PRIORITY_IDLE) {
+ ::SetThreadPriority(thread_, THREAD_PRIORITY_IDLE);
+ }
+ ::ResumeThread(thread_);
+ }
+ }
+ started_ = true;
+}
+
+void Thread::Join() {
+ if (started_) {
+ WaitForSingleObject(thread_, INFINITE);
+ CloseHandle(thread_);
+ started_ = false;
+ }
+}
+#endif
+
+void *Thread::PreRun(void *pv) {
+ Thread *thread = (Thread *)pv;
+ ThreadManager::SetCurrent(thread);
+#if defined(WIN32) && defined(_DEBUG)
+ char buf[256];
+ _snprintf(buf, sizeof(buf), "Thread 0x%.8x", thread);
+ SetThreadName(GetCurrentThreadId(), buf);
+#endif
+ thread->Run();
+ return NULL;
+}
+
+void Thread::Run() {
+ ProcessMessages(kForever);
+}
+
+void Thread::Stop() {
+ MessageQueue::Stop();
+ Join();
+}
+
+void Thread::Send(MessageHandler *phandler, uint32 id, MessageData *pdata) {
+ if (fStop_)
+ return;
+
+ // Sent messages are sent to the MessageHandler directly, in the context
+ // of "thread", like Win32 SendMessage. If in the right context,
+ // call the handler directly.
+
+ Message msg;
+ msg.phandler = phandler;
+ msg.message_id = id;
+ msg.pdata = pdata;
+ if (IsCurrent()) {
+ phandler->OnMessage(&msg);
+ return;
+ }
+
+ AutoThread thread;
+ Thread *current_thread = Thread::Current();
+ ASSERT(current_thread != NULL); // AutoThread ensures this
+
+ bool ready = false;
+ {
+ CritScope cs(&crit_);
+ EnsureActive();
+ _SendMessage smsg;
+ smsg.thread = current_thread;
+ smsg.msg = msg;
+ smsg.ready = &ready;
+ sendlist_.push_back(smsg);
+ has_sends_ = true;
+ }
+
+ // Wait for a reply
+
+ ss_->WakeUp();
+
+ bool waited = false;
+ while (!ready) {
+ current_thread->ReceiveSends();
+ current_thread->socketserver()->Wait(kForever, false);
+ waited = true;
+ }
+
+ // Our Wait loop above may have consumed some WakeUp events for this
+ // MessageQueue, that weren't relevant to this Send. Losing these WakeUps can
+ // cause problems for some SocketServers.
+ //
+ // Concrete example:
+ // Win32SocketServer on thread A calls Send on thread B. While processing the
+ // message, thread B Posts a message to A. We consume the wakeup for that
+ // Post while waiting for the Send to complete, which means that when we exit
+ // this loop, we need to issue another WakeUp, or else the Posted message
+ // won't be processed in a timely manner.
+
+ if (waited) {
+ current_thread->socketserver()->WakeUp();
+ }
+}
+
+void Thread::ReceiveSends() {
+ // Before entering critical section, check boolean.
+
+ if (!has_sends_)
+ return;
+
+ // Receive a sent message. Cleanup scenarios:
+ // - thread sending exits: We don't allow this, since thread can exit
+ // only via Join, so Send must complete.
+ // - thread receiving exits: Wakeup/set ready in Thread::Clear()
+ // - object target cleared: Wakeup/set ready in Thread::Clear()
+ crit_.Enter();
+ while (!sendlist_.empty()) {
+ _SendMessage smsg = sendlist_.front();
+ sendlist_.pop_front();
+ crit_.Leave();
+ smsg.msg.phandler->OnMessage(&smsg.msg);
+ crit_.Enter();
+ *smsg.ready = true;
+ smsg.thread->socketserver()->WakeUp();
+ }
+ has_sends_ = false;
+ crit_.Leave();
+}
+
+void Thread::Clear(MessageHandler *phandler, uint32 id) {
+ CritScope cs(&crit_);
+
+ // Remove messages on sendlist_ with phandler
+ // Object target cleared: remove from send list, wakeup/set ready
+ // if sender not NULL.
+
+ std::list<_SendMessage>::iterator iter = sendlist_.begin();
+ while (iter != sendlist_.end()) {
+ _SendMessage smsg = *iter;
+ if (phandler == NULL || smsg.msg.phandler == phandler) {
+ if (id == (uint32)-1 || smsg.msg.message_id == id) {
+ iter = sendlist_.erase(iter);
+ *smsg.ready = true;
+ smsg.thread->socketserver()->WakeUp();
+ continue;
+ }
+ }
+ ++iter;
+ }
+
+ MessageQueue::Clear(phandler, id);
+}
+
+bool Thread::ProcessMessages(int cmsLoop) {
+ uint32 msEnd;
+ if (cmsLoop != kForever)
+ msEnd = GetMillisecondCount() + cmsLoop;
+ int cmsNext = cmsLoop;
+
+ while (true) {
+ Message msg;
+ if (!Get(&msg, cmsNext))
+ return false;
+ Dispatch(&msg);
+
+ if (cmsLoop != kForever) {
+ uint32 msCur = GetMillisecondCount();
+ if (msCur >= msEnd)
+ return true;
+ cmsNext = msEnd - msCur;
+ }
+ }
+}
+
+AutoThread::AutoThread(SocketServer* ss) : Thread(ss) {
+ if (!ThreadManager::CurrentThread()) {
+ ThreadManager::SetCurrent(this);
+ }
+}
+
+AutoThread::~AutoThread() {
+ if (ThreadManager::CurrentThread() == this) {
+ ThreadManager::SetCurrent(NULL);
+ }
+}
+
+} // namespace talk_base
diff --git a/Plugins/jingle/libjingle/talk/base/thread.h b/Plugins/jingle/libjingle/talk/base/thread.h
new file mode 100644
index 0000000..5da0aed
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/thread.h
@@ -0,0 +1,161 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TALK_BASE_THREAD_H__
+#define TALK_BASE_THREAD_H__
+
+#include <algorithm>
+#include <list>
+#include <vector>
+
+#ifdef POSIX
+#include <pthread.h>
+#endif
+
+#include "talk/base/messagequeue.h"
+
+#ifdef WIN32
+#include "talk/base/win32.h"
+#endif
+
+namespace talk_base {
+
+class Thread;
+
+class ThreadManager {
+public:
+ ThreadManager();
+ ~ThreadManager();
+
+ static Thread *CurrentThread();
+ static void SetCurrent(Thread *thread);
+ void Add(Thread *thread);
+ void Remove(Thread *thread);
+
+private:
+ Thread *main_thread_;
+ std::vector<Thread *> threads_;
+ CriticalSection crit_;
+
+#ifdef POSIX
+ static pthread_key_t key_;
+#endif
+
+#ifdef WIN32
+ static DWORD key_;
+#endif
+};
+
+class Thread;
+
+struct _SendMessage {
+ _SendMessage() {}
+ Thread *thread;
+ Message msg;
+ bool *ready;
+};
+
+enum ThreadPriority {
+ PRIORITY_NORMAL,
+ PRIORITY_IDLE,
+};
+
+class Thread : public MessageQueue {
+public:
+ Thread(SocketServer* ss = 0);
+ virtual ~Thread();
+
+ static inline Thread* Current() {
+ return ThreadManager::CurrentThread();
+ }
+ inline bool IsCurrent() const {
+ return (ThreadManager::CurrentThread() == this);
+ }
+
+ void SetPriority(ThreadPriority priority) {
+ priority_ = priority;
+ }
+
+ virtual void Start();
+ virtual void Stop();
+
+ // By default, Thread::Run() calls ProcessMessages(kForever). To do other
+ // work, override Run(). To receive and dispatch messages, call
+ // ProcessMessages occasionally.
+ virtual void Run();
+
+ virtual void Send(MessageHandler *phandler, uint32 id = 0,
+ MessageData *pdata = NULL);
+
+ // From MessageQueue
+ virtual void Clear(MessageHandler *phandler, uint32 id = (uint32)-1);
+ virtual void ReceiveSends();
+
+ // ProcessMessages will process I/O and dispatch messages until:
+ // 1) cms milliseconds have elapsed (returns true)
+ // 2) Stop() is called (returns false)
+ bool ProcessMessages(int cms);
+
+#ifdef WIN32
+ HANDLE GetHandle() {
+ return thread_;
+ }
+#endif
+
+private:
+ static void *PreRun(void *pv);
+ void Join();
+
+ std::list<_SendMessage> sendlist_;
+ ThreadPriority priority_;
+ bool started_;
+ bool has_sends_;
+
+#ifdef POSIX
+ pthread_t thread_;
+#endif
+
+#ifdef WIN32
+ HANDLE thread_;
+#endif
+
+ friend class ThreadManager;
+};
+
+// AutoThread automatically installs itself at construction
+// uninstalls at destruction, if a Thread object is
+// _not already_ associated with the current OS thread.
+
+class AutoThread : public Thread {
+public:
+ AutoThread(SocketServer* ss = 0);
+ virtual ~AutoThread();
+};
+
+} // namespace talk_base
+
+#endif // TALK_BASE_THREAD_H__
diff --git a/Plugins/jingle/libjingle/talk/base/time.cc b/Plugins/jingle/libjingle/talk/base/time.cc
new file mode 100644
index 0000000..d4a61ac
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/time.cc
@@ -0,0 +1,91 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <iostream>
+#include <cstdlib>
+#include <cstring>
+
+#include "talk/base/time.h"
+
+namespace talk_base {
+
+#ifdef POSIX
+#include <sys/time.h>
+uint32 Time() {
+ struct timeval tv;
+ gettimeofday(&tv, 0);
+ return tv.tv_sec * 1000 + tv.tv_usec / 1000;
+}
+#endif
+
+#ifdef WIN32
+#include <windows.h>
+uint32 Time() {
+ return GetTickCount();
+}
+#endif
+
+uint32 StartTime() {
+ // Close to program execution time
+ static const uint32 g_start = Time();
+ return g_start;
+}
+
+// Make sure someone calls it so that it gets initialized
+static uint32 ignore = StartTime();
+
+uint32 ElapsedTime() {
+ return TimeDiff(Time(), StartTime());
+}
+
+bool TimeIsBetween(uint32 later, uint32 middle, uint32 earlier) {
+ if (earlier <= later) {
+ return ((earlier <= middle) && (middle <= later));
+ } else {
+ return !((later < middle) && (middle < earlier));
+ }
+}
+
+int32 TimeDiff(uint32 later, uint32 earlier) {
+ uint32 LAST = 0xFFFFFFFF;
+ uint32 HALF = 0x80000000;
+ if (TimeIsBetween(earlier + HALF, later, earlier)) {
+ if (earlier <= later) {
+ return static_cast<long>(later - earlier);
+ } else {
+ return static_cast<long>(later + (LAST - earlier) + 1);
+ }
+ } else {
+ if (later <= earlier) {
+ return -static_cast<long>(earlier - later);
+ } else {
+ return -static_cast<long>(earlier + (LAST - later) + 1);
+ }
+ }
+}
+
+} // namespace talk_base
diff --git a/Plugins/jingle/libjingle/talk/base/time.h b/Plugins/jingle/libjingle/talk/base/time.h
new file mode 100644
index 0000000..9088aae
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/time.h
@@ -0,0 +1,53 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TALK_BASE_TIME_H__
+#define TALK_BASE_TIME_H__
+
+#include "talk/base/basictypes.h"
+
+namespace talk_base {
+
+// Returns the current time in milliseconds.
+uint32 Time();
+
+// Approximate time when the program started.
+uint32 StartTime();
+
+// Elapsed milliseconds since StartTime()
+uint32 ElapsedTime();
+
+// TODO: Delete this old version.
+#define GetMillisecondCount Time
+
+// Comparisons between time values, which can wrap around.
+bool TimeIsBetween(uint32 later, uint32 middle, uint32 earlier);
+int32 TimeDiff(uint32 later, uint32 earlier);
+
+} // namespace talk_base
+
+#endif // TALK_BASE_TIME_H__
diff --git a/Plugins/jingle/libjingle/talk/base/unixfilesystem.cc b/Plugins/jingle/libjingle/talk/base/unixfilesystem.cc
new file mode 100644
index 0000000..2c2732f
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/unixfilesystem.cc
@@ -0,0 +1,223 @@
+/*
+ * libjingle
+ * Copyright 2004--2006, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <errno.h>
+#include <cassert>
+
+#include "talk/base/basicdefs.h"
+#include "talk/base/pathutils.h"
+#include "talk/base/fileutils.h"
+#include "talk/base/stringutils.h"
+#include "talk/base/stream.h"
+
+#include "talk/base/unixfilesystem.h"
+
+namespace talk_base {
+
+bool UnixFilesystem::CreateFolderI(const Pathname &path) {
+ LOG(LS_INFO) << "Creating folder: " << path.pathname();
+ int len = path.pathname().length();
+ const char *pathname = path.pathname().c_str();
+ if ((len <= 0) || (pathname[len-1] != '/'))
+ return false;
+ struct stat st;
+ int res = ::stat(pathname, &st);
+ if (res == 0) {
+ // Something exists at this location, check if it is a directory
+ return S_ISDIR(st.st_mode) != 0;
+ } else if (errno != ENOENT) {
+ // Unexpected error
+ return false;
+ }
+ // Directory doesn't exist, look up one directory level
+ do {
+ --len;
+ } while ((len > 0) && (pathname[len-1] !='/'));
+
+ char *newstring = new char[len+1];
+ strncpy(newstring, pathname, len);
+ newstring[len] = '\0';
+
+ if (!CreateFolder(Pathname(newstring))) {
+ delete[] newstring;
+ return false;
+ }
+ delete[] newstring;
+ std::string no_slash(path.pathname(), 0, path.pathname().length()-1);
+
+ return (::mkdir(no_slash.c_str(), 0755) == 0);
+ }
+
+FileStream *UnixFilesystem::OpenFileI(const Pathname &filename,
+ const std::string &mode) {
+ talk_base::FileStream *fs = new talk_base::FileStream();
+ if (fs)
+ fs->Open(filename.pathname().c_str(), mode.c_str());
+ return fs;
+}
+
+bool UnixFilesystem::DeleteFileI(const Pathname &filename) {
+ LOG(LS_INFO) << "Deleting " << filename.pathname();
+
+ if (IsFolder(filename)) {
+ Pathname dir;
+ dir.SetFolder(filename.pathname());
+ DirectoryIterator di;
+ di.Iterate(dir.pathname());
+ while(di.Next()) {
+ if (di.Name() == "." || di.Name() == "..")
+ continue;
+ Pathname subdir;
+ subdir.SetFolder(filename.pathname());
+ subdir.SetFilename(di.Name());
+
+ if (!DeleteFile(subdir.pathname()))
+ return false;
+ }
+ std::string no_slash(filename.pathname(), 0, filename.pathname().length()-1);
+ return ::rmdir(no_slash.c_str()) == 0;
+ }
+ return ::unlink(filename.pathname().c_str()) == 0;
+}
+
+bool UnixFilesystem::GetTemporaryFolderI(Pathname &pathname, bool create,
+ const std::string *append) {
+ pathname.SetPathname("/tmp");
+ if (append) {
+ pathname.AppendFolder(*append);
+ if (create)
+ CreateFolder(pathname);
+ }
+}
+
+std::string UnixFilesystem::TempFilenameI(const Pathname &dir, const std::string &prefix) {
+ int len = dir.pathname().size() + prefix.size() + 2 + 6;
+ char *tempname = new char[len];
+
+ snprintf(tempname, len, "%s/%sXXXXXX", dir.pathname().c_str(), prefix.c_str());
+ int fd = ::mkstemp(tempname);
+ if (fd != -1)
+ ::close(fd);
+ std::string ret(tempname);
+ delete[] tempname;
+
+ return ret;
+}
+
+bool UnixFilesystem::MoveFileI(const Pathname &old_path, const Pathname &new_path)
+{
+ LOG(LS_INFO) << "Moving " << old_path.pathname() << " to " << new_path.pathname();
+ if (rename(old_path.pathname().c_str(), new_path.pathname().c_str()) != 0) {
+ if (errno != EXDEV)
+ return false;
+ if (!CopyFile(old_path, new_path))
+ return false;
+ if (!DeleteFile(old_path))
+ return false;
+ }
+ return true;
+}
+
+bool UnixFilesystem::IsFolderI(const Pathname &path)
+{
+ struct stat st;
+ if (stat(path.pathname().c_str(), &st) < 0)
+ return false;
+
+ return S_ISDIR(st.st_mode);
+}
+
+bool UnixFilesystem::CopyFileI(const Pathname &old_path, const Pathname &new_path)
+{
+ LOG(LS_INFO) << "Copying " << old_path.pathname() << " to " << new_path.pathname();
+ char buf[256];
+ size_t len;
+ if (IsFolder(old_path)) {
+ Pathname new_dir;
+ new_dir.SetFolder(new_path.pathname());
+ Pathname old_dir;
+ old_dir.SetFolder(old_path.pathname());
+
+ if (!CreateFolder(new_dir))
+ return false;
+ DirectoryIterator di;
+ di.Iterate(old_dir.pathname());
+ while(di.Next()) {
+ if (di.Name() == "." || di.Name() == "..")
+ continue;
+ Pathname source;
+ Pathname dest;
+ source.SetFolder(old_dir.pathname());
+ dest.SetFolder(new_path.pathname());
+ source.SetFilename(di.Name());
+ dest.SetFilename(di.Name());
+
+ if (!CopyFile(source, dest))
+ return false;
+ }
+ return true;
+ }
+
+ StreamInterface *source = OpenFile(old_path, "rb");
+ if (!source)
+ return false;
+
+ StreamInterface *dest = OpenFile(new_path, "wb");
+ if (!dest) {
+ delete source;
+ return false;
+ }
+
+ while (source->Read(buf, sizeof(buf), &len, NULL) == talk_base::SR_SUCCESS)
+ dest->Write(buf, len, NULL, NULL);
+
+ delete source;
+ delete dest;
+ return true;
+}
+
+bool UnixFilesystem::IsTemporaryPathI(const Pathname& pathname)
+{
+ return (!strncmp(pathname.pathname().c_str(), "/tmp/", strlen("/tmp/")));
+}
+
+bool UnixFilesystem::FileExistsI(const Pathname& pathname)
+{
+ struct stat st;
+ int res = ::stat(pathname.pathname().c_str(), &st);
+ return res == 0;
+}
+
+bool UnixFilesystem::GetFileSizeI(const Pathname& pathname, size_t *size)
+{
+ struct stat st;
+ if (::stat(pathname.pathname().c_str(), &st) != 0)
+ return false;
+ *size = st.st_size;
+ return true;
+}
+
+}
diff --git a/Plugins/jingle/libjingle/talk/base/unixfilesystem.h b/Plugins/jingle/libjingle/talk/base/unixfilesystem.h
new file mode 100644
index 0000000..bd145bb
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/unixfilesystem.h
@@ -0,0 +1,86 @@
+/*
+ * libjingle
+ * Copyright 2004--2006, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _TALK_BASE_UNIXFILESYSTEM_H__
+#define _TALK_BASE_UNIXFILESYSTEM_H__
+
+#include "fileutils.h"
+
+namespace talk_base {
+
+class UnixFilesystem : public Filesystem{
+ public:
+
+ virtual bool CreateFolderI(const Pathname &pathname);
+
+ // Opens a file. Returns an open StreamInterface if function succeeds. Otherwise,
+ // returns NULL.
+ virtual FileStream *OpenFileI(const Pathname &filename,
+ const std::string &mode);
+
+ // This will attempt to delete the path located at filename. If filename is a file,
+ // it will be unlinked. If the path is a directory, it will recursively unlink and remove
+ // all the files and directory within it
+ virtual bool DeleteFileI(const Pathname &filename);
+
+ // Creates a directory. This will call itself recursively to create /foo/bar even if
+ // /foo does not exist.
+ // Returns TRUE if function succeeds
+
+ // This moves a file from old_path to new_path, where "file" can be a plain file
+ // or directory, which will be moved recursively.
+ // Returns true if function succeeds.
+ virtual bool MoveFileI(const Pathname &old_path, const Pathname &new_path);
+
+ // This copies a file from old_path to _new_path where "file" can be a plain file
+ // or directory, which will be copied recursively.
+ // Returns true if function succeeds
+ virtual bool CopyFileI(const Pathname &old_path, const Pathname &new_path);
+
+ // Returns true if a pathname is a directory
+ virtual bool IsFolderI(const Pathname& pathname);
+
+ // Returns true if pathname represents a temporary location on the system.
+ virtual bool IsTemporaryPathI(const Pathname& pathname);
+
+ // Returns true of pathname represents an existing file
+ virtual bool FileExistsI(const Pathname& pathname);
+
+ virtual std::string TempFilenameI(const Pathname &dir, const std::string &prefix);
+
+ // A folder appropriate for storing temporary files (Contents are
+ // automatically deleted when the program exists)
+ virtual bool GetTemporaryFolderI(Pathname &path, bool create,
+ const std::string *append);
+
+ virtual bool GetFileSizeI(const Pathname &path, size_t *size);
+
+ };
+
+}
+
+#endif // _UNIXFILESYSTEM_H__
diff --git a/Plugins/jingle/libjingle/talk/base/urlencode.cc b/Plugins/jingle/libjingle/talk/base/urlencode.cc
new file mode 100644
index 0000000..b654c35
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/urlencode.cc
@@ -0,0 +1,120 @@
+#include "talk/base/urlencode.h"
+
+static int HexPairValue(const char * code) {
+ int value = 0;
+ const char * pch = code;
+ for (;;) {
+ int digit = *pch++;
+ if (digit >= '0' && digit <= '9') {
+ value += digit - '0';
+ }
+ else if (digit >= 'A' && digit <= 'F') {
+ value += digit - 'A' + 10;
+ }
+ else if (digit >= 'a' && digit <= 'f') {
+ value += digit - 'a' + 10;
+ }
+ else {
+ return -1;
+ }
+ if (pch == code + 2)
+ return value;
+ value <<= 4;
+ }
+}
+
+int UrlDecode(const char *source, char *dest)
+{
+ char * start = dest;
+
+ while (*source) {
+ switch (*source) {
+ case '+':
+ *(dest++) = ' ';
+ break;
+ case '%':
+ if (source[1] && source[2]) {
+ int value = HexPairValue(source + 1);
+ if (value >= 0) {
+ *(dest++) = value;
+ source += 2;
+ }
+ else {
+ *dest++ = '?';
+ }
+ }
+ else {
+ *dest++ = '?';
+ }
+ break;
+ default:
+ *dest++ = *source;
+ }
+ source++;
+ }
+
+ *dest = 0;
+ return dest - start;
+}
+
+int UrlEncode(const char *source, char *dest, unsigned max)
+{
+ static const char *digits = "0123456789ABCDEF";
+ unsigned char ch;
+ unsigned len = 0;
+ char *start = dest;
+
+ while (len < max - 4 && *source)
+ {
+ ch = (unsigned char)*source;
+ if (*source == ' ') {
+ *dest++ = '+';
+ }
+ else if (isalnum(ch) || strchr("-_.!~*'()", ch)) {
+ *dest++ = *source;
+ }
+ else {
+ *dest++ = '%';
+ *dest++ = digits[(ch >> 4) & 0x0F];
+ *dest++ = digits[ ch & 0x0F];
+ }
+ source++;
+ }
+ *dest = 0;
+ return start - dest;
+}
+
+std::string
+UrlDecodeString(const std::string & encoded) {
+ const char * sz_encoded = encoded.c_str();
+ size_t needed_length = encoded.length();
+ for (const char * pch = sz_encoded; *pch; pch++) {
+ if (*pch == '%')
+ needed_length += 2;
+ }
+ needed_length += 10;
+ char stackalloc[64];
+ char * buf = needed_length > sizeof(stackalloc)/sizeof(*stackalloc) ?
+ (char *)malloc(needed_length) : stackalloc;
+ UrlDecode(encoded.c_str(), buf);
+ std::string result(buf);
+ if (buf != stackalloc) {
+ free(buf);
+ }
+ return result;
+}
+
+std::string
+UrlEncodeString(const std::string & decoded) {
+ const char * sz_decoded = decoded.c_str();
+ size_t needed_length = decoded.length() * 3 + 3;
+ char stackalloc[64];
+ char * buf = needed_length > sizeof(stackalloc)/sizeof(*stackalloc) ?
+ (char *)malloc(needed_length) : stackalloc;
+ UrlEncode(decoded.c_str(), buf, needed_length);
+ std::string result(buf);
+ if (buf != stackalloc) {
+ free(buf);
+ }
+ return result;
+}
diff --git a/Plugins/jingle/libjingle/talk/base/urlencode.h b/Plugins/jingle/libjingle/talk/base/urlencode.h
new file mode 100644
index 0000000..9d2b3f6
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/urlencode.h
@@ -0,0 +1,12 @@
+#ifndef _URLENCODE_H_
+#define _URLENCODE_H_
+
+#include <string>
+
+int UrlDecode(const char *source, char *dest);
+int UrlEncode(const char *source, char *dest, unsigned max);
+std::string UrlDecodeString(const std::string & encoded);
+std::string UrlEncodeString(const std::string & decoded);
+
+#endif
+
diff --git a/Plugins/jingle/libjingle/talk/base/virtualsocket_unittest.cc b/Plugins/jingle/libjingle/talk/base/virtualsocket_unittest.cc
new file mode 100644
index 0000000..16456a2
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/virtualsocket_unittest.cc
@@ -0,0 +1,239 @@
+// Copyright 2006, Google Inc.
+
+#include <complex>
+#include <iostream>
+#include <cassert>
+
+#include "talk/base/thread.h"
+#include "talk/base/virtualsocketserver.h"
+#include "talk/base/testclient.h"
+#include "talk/base/time.h"
+
+#ifdef POSIX
+extern "C" {
+#include <errno.h>
+}
+#endif // POSIX
+
+using namespace talk_base;
+
+void test_basic(Thread* thread, VirtualSocketServer* ss) {
+ std::cout << "basic: ";
+ std::cout.flush();
+
+ SocketAddress addr1(ss->GetNextIP(), 5000);
+ AsyncUDPSocket* socket = CreateAsyncUDPSocket(ss);
+ socket->Bind(addr1);
+
+ TestClient* client1 = new TestClient(socket);
+ TestClient* client2 = new TestClient(CreateAsyncUDPSocket(ss));
+
+ SocketAddress addr2;
+ client2->SendTo("foo", 3, addr1);
+ client1->CheckNextPacket("foo", 3, &addr2);
+
+ SocketAddress addr3;
+ client1->SendTo("bizbaz", 6, addr2);
+ client2->CheckNextPacket("bizbaz", 6, &addr3);
+ assert(addr3 == addr1);
+
+ for (int i = 0; i < 10; i++) {
+ client2 = new TestClient(CreateAsyncUDPSocket(ss));
+
+ SocketAddress addr4;
+ client2->SendTo("foo", 3, addr1);
+ client1->CheckNextPacket("foo", 3, &addr4);
+ assert((addr4.ip() == addr2.ip()) && (addr4.port() == addr2.port() + 1));
+
+ SocketAddress addr5;
+ client1->SendTo("bizbaz", 6, addr4);
+ client2->CheckNextPacket("bizbaz", 6, &addr5);
+ assert(addr5 == addr1);
+
+ addr2 = addr4;
+ }
+
+ std::cout << "PASS" << std::endl;
+}
+
+// Sends at a constant rate but with random packet sizes.
+struct Sender : public MessageHandler {
+ Sender(Thread* th, AsyncUDPSocket* s, uint32 rt)
+ : thread(th), socket(s), done(false), rate(rt), count(0) {
+ last_send = GetMillisecondCount();
+ thread->PostDelayed(NextDelay(), this, 1);
+ }
+
+ uint32 NextDelay() {
+ uint32 size = (rand() % 4096) + 1;
+ return 1000 * size / rate;
+ }
+
+ void OnMessage(Message* pmsg) {
+ assert(pmsg->message_id == 1);
+
+ if (done)
+ return;
+
+ uint32 cur_time = GetMillisecondCount();
+ uint32 delay = cur_time - last_send;
+ uint32 size = rate * delay / 1000;
+ size = std::min(size, uint32(4096));
+ size = std::max(size, uint32(4));
+
+ count += size;
+ *reinterpret_cast<uint32*>(dummy) = cur_time;
+ socket->Send(dummy, size);
+
+ last_send = cur_time;
+ thread->PostDelayed(NextDelay(), this, 1);
+ }
+
+ Thread* thread;
+ AsyncUDPSocket* socket;
+ bool done;
+ uint32 rate; // bytes per second
+ uint32 count;
+ uint32 last_send;
+ char dummy[4096];
+};
+
+struct Receiver : public MessageHandler, public sigslot::has_slots<> {
+ Receiver(Thread* th, AsyncUDPSocket* s, uint32 bw)
+ : thread(th), socket(s), bandwidth(bw), done(false), count(0),
+ sec_count(0), sum(0), sum_sq(0), samples(0) {
+ socket->SignalReadPacket.connect(this, &Receiver::OnReadPacket);
+ thread->PostDelayed(1000, this, 1);
+ }
+
+ ~Receiver() {
+ thread->Clear(this);
+ }
+
+ void OnReadPacket(
+ const char* data, size_t size, const SocketAddress& remote_addr,
+ AsyncPacketSocket* s) {
+ assert(s == socket);
+ assert(size >= 4);
+
+ count += size;
+ sec_count += size;
+
+ uint32 send_time = *reinterpret_cast<const uint32*>(data);
+ uint32 recv_time = GetMillisecondCount();
+ uint32 delay = recv_time - send_time;
+ sum += delay;
+ sum_sq += delay * delay;
+ samples += 1;
+ }
+
+ void OnMessage(Message* pmsg) {
+ assert(pmsg->message_id == 1);
+ // It is always possible for us to receive more than expected because
+ // packets can be further delayed in delivery.
+ if (bandwidth > 0)
+ assert(sec_count <= 5 * bandwidth / 4);
+ sec_count = 0;
+ thread->PostDelayed(1000, this, 1);
+ }
+
+ Thread* thread;
+ AsyncUDPSocket* socket;
+ uint32 bandwidth;
+ bool done;
+ uint32 count;
+ uint32 sec_count;
+ double sum;
+ double sum_sq;
+ uint32 samples;
+};
+
+void test_bandwidth(Thread* thread, VirtualSocketServer* ss) {
+ std::cout << "bandwidth: ";
+ std::cout.flush();
+
+ AsyncUDPSocket* send_socket = CreateAsyncUDPSocket(ss);
+ AsyncUDPSocket* recv_socket = CreateAsyncUDPSocket(ss);
+ assert(send_socket->Bind(SocketAddress(ss->GetNextIP(), 1000)) >= 0);
+ assert(recv_socket->Bind(SocketAddress(ss->GetNextIP(), 1000)) >= 0);
+ assert(send_socket->Connect(recv_socket->GetLocalAddress()) >= 0);
+
+ uint32 bandwidth = 64 * 1024;
+ ss->set_bandwidth(bandwidth);
+
+ Sender sender(thread, send_socket, 80 * 1024);
+ Receiver receiver(thread, recv_socket, bandwidth);
+
+ Thread* pthMain = Thread::Current();
+ pthMain->ProcessMessages(5000);
+ sender.done = true;
+ pthMain->ProcessMessages(5000);
+
+ assert(receiver.count >= 5 * 3 * bandwidth / 4);
+ assert(receiver.count <= 6 * bandwidth); // queue could drain for 1 sec
+
+ delete send_socket;
+ delete recv_socket;
+
+ ss->set_bandwidth(0);
+
+ std::cout << "PASS" << std::endl;
+}
+
+void test_delay(Thread* thread, VirtualSocketServer* ss) {
+ std::cout << "delay: ";
+ std::cout.flush();
+
+ uint32 mean = 2000;
+ uint32 stddev = 500;
+
+ ss->set_delay_mean(mean);
+ ss->set_delay_stddev(stddev);
+ ss->UpdateDelayDistribution();
+
+ AsyncUDPSocket* send_socket = CreateAsyncUDPSocket(ss);
+ AsyncUDPSocket* recv_socket = CreateAsyncUDPSocket(ss);
+ assert(send_socket->Bind(SocketAddress(ss->GetNextIP(), 1000)) >= 0);
+ assert(recv_socket->Bind(SocketAddress(ss->GetNextIP(), 1000)) >= 0);
+ assert(send_socket->Connect(recv_socket->GetLocalAddress()) >= 0);
+
+ Sender sender(thread, send_socket, 64 * 1024);
+ Receiver receiver(thread, recv_socket, 0);
+
+ Thread* pthMain = Thread::Current();
+ pthMain->ProcessMessages(5000);
+ sender.done = true;
+ pthMain->ProcessMessages(5000);
+
+ double sample_mean = receiver.sum / receiver.samples;
+ double num = receiver.sum_sq - 2 * sample_mean * receiver.sum +
+ receiver.samples * sample_mean * sample_mean;
+ double sample_stddev = std::sqrt(num / (receiver.samples - 1));
+std::cout << "mean=" << sample_mean << " dev=" << sample_stddev << std::endl;
+
+ assert(0.9 * mean <= sample_mean);
+ assert(sample_mean <= 1.1 * mean);
+ assert(0.9 * stddev <= sample_stddev);
+ assert(sample_stddev <= 1.1 * stddev);
+
+ delete send_socket;
+ delete recv_socket;
+
+ ss->set_delay_mean(0);
+ ss->set_delay_stddev(0);
+ ss->UpdateDelayDistribution();
+
+ std::cout << "PASS" << std::endl;
+}
+
+int main(int argc, char* argv) {
+ Thread *pthMain = Thread::Current();
+ VirtualSocketServer* ss = new VirtualSocketServer();
+ pthMain->set_socketserver(ss);
+
+ test_basic(pthMain, ss);
+ test_bandwidth(pthMain, ss);
+ test_delay(pthMain, ss);
+
+ return 0;
+}
diff --git a/Plugins/jingle/libjingle/talk/base/virtualsocketserver.cc b/Plugins/jingle/libjingle/talk/base/virtualsocketserver.cc
new file mode 100644
index 0000000..884b324
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/virtualsocketserver.cc
@@ -0,0 +1,616 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <algorithm>
+#include <cassert>
+#include <cmath>
+#include <cstring>
+#include <iostream>
+#include <vector>
+#include <errno.h>
+
+#include "talk/base/virtualsocketserver.h"
+#include "talk/base/common.h"
+#include "talk/base/time.h"
+
+namespace talk_base {
+
+const uint32 HEADER_SIZE = 28; // IP + UDP headers
+
+const uint32 MSG_ID_PACKET = 1;
+// TODO: Add a message type for new connections.
+
+// Packets are passed between sockets as messages. We copy the data just like
+// the kernel does.
+class Packet : public MessageData {
+public:
+ Packet(const char* data, size_t size, const SocketAddress& from)
+ : size_(size), from_(from) {
+ assert(data);
+ assert(size_ >= 0);
+ data_ = new char[size_];
+ std::memcpy(data_, data, size_);
+ }
+
+ virtual ~Packet() {
+ delete data_;
+ }
+
+ const char* data() const { return data_; }
+ size_t size() const { return size_; }
+ const SocketAddress& from() const { return from_; }
+
+ // Remove the first size bytes from the data.
+ void Consume(size_t size) {
+ assert(size < size_);
+ size_ -= size;
+ char* new_data = new char[size_];
+ std::memcpy(new_data, data_, size);
+ delete[] data_;
+ data_ = new_data;
+ }
+
+private:
+ char* data_;
+ size_t size_;
+ SocketAddress from_;
+};
+
+// Implements the socket interface using the virtual network. Packets are
+// passed as messages using the message queue of the socket server.
+class VirtualSocket : public AsyncSocket, public MessageHandler {
+public:
+ VirtualSocket(
+ VirtualSocketServer* server, int type, bool async, uint32 ip)
+ : server_(server), type_(type), async_(async), connected_(false),
+ local_ip_(ip), readable_(true), queue_size_(0) {
+ assert((type_ == SOCK_DGRAM) || (type_ == SOCK_STREAM));
+ packets_ = new std::vector<Packet*>();
+ }
+
+ ~VirtualSocket() {
+ Close();
+
+ for (unsigned i = 0; i < packets_->size(); i++)
+ delete (*packets_)[i];
+ delete packets_;
+ }
+
+ SocketAddress GetLocalAddress() const {
+ return local_addr_;
+ }
+
+ SocketAddress GetRemoteAddress() const {
+ return remote_addr_;
+ }
+
+ int Bind(const SocketAddress& addr) {
+ assert(addr.port() != 0);
+ int result = server_->Bind(addr, this);
+ if (result >= 0)
+ local_addr_ = addr;
+ else
+ error_ = EADDRINUSE;
+ return result;
+ }
+
+ int Connect(const SocketAddress& addr) {
+ assert(!connected_);
+ connected_ = true;
+ remote_addr_ = addr;
+ assert(type_ == SOCK_DGRAM); // stream not yet implemented
+ return 0;
+ }
+
+ int Close() {
+ if (!local_addr_.IsAny())
+ server_->Unbind(local_addr_, this);
+
+ connected_ = false;
+ local_addr_ = SocketAddress();
+ remote_addr_ = SocketAddress();
+ return 0;
+ }
+
+ int Send(const void *pv, size_t cb) {
+ assert(connected_);
+ return SendInternal(pv, cb, remote_addr_);
+ }
+
+ int SendTo(const void *pv, size_t cb, const SocketAddress& addr) {
+ assert(!connected_);
+ return SendInternal(pv, cb, addr);
+ }
+
+ int SendInternal(const void *pv, size_t cb, const SocketAddress& addr) {
+ // If we have not been assigned a local port, then get one.
+ if (local_addr_.IsAny()) {
+ local_addr_.SetIP(local_ip_);
+ int result = server_->Bind(this, &local_addr_);
+ if (result < 0) {
+ local_addr_.SetIP(0);
+ error_ = EADDRINUSE;
+ return result;
+ }
+ }
+
+ // Send the data in a message to the appropriate socket.
+ return server_->Send(this, pv, cb, local_addr_, addr);
+ }
+
+ int Recv(void *pv, size_t cb) {
+ SocketAddress addr;
+ return RecvFrom(pv, cb, &addr);
+ }
+
+ int RecvFrom(void *pv, size_t cb, SocketAddress *paddr) {
+ // If we don't have a packet, then either error or wait for one to arrive.
+ if (packets_->size() == 0) {
+ if (async_) {
+ error_ = EAGAIN;
+ return -1;
+ }
+ while (packets_->size() == 0) {
+ Message msg;
+ server_->msg_queue_->Get(&msg);
+ server_->msg_queue_->Dispatch(&msg);
+ }
+ }
+
+ // Return the packet at the front of the queue.
+ Packet* packet = packets_->front();
+ *paddr = packet->from();
+ int size = (int)packet->size();
+ if (size <= (int)cb) {
+ std::memcpy(pv, packet->data(), size);
+ packets_->erase(packets_->begin());
+ delete packet;
+ return size;
+ } else {
+ std::memcpy(pv, packet->data(), cb);
+ packet->Consume(cb);
+ return (int)cb;
+ }
+ }
+
+ int Listen(int backlog) {
+ assert(false); // not yet implemented
+ return 0;
+ }
+
+ Socket* Accept(SocketAddress *paddr) {
+ assert(false); // not yet implemented
+ return 0;
+ }
+
+ bool readable() { return readable_; }
+ void set_readable(bool value) { readable_ = value; }
+
+ bool writable() { return false; }
+ void set_writable(bool value) {
+ // TODO: Send ourselves messages (delayed after the first) to give them a
+ // chance to write.
+ assert(false);
+ }
+
+ int GetError() const {
+ return error_;
+ }
+
+ void SetError(int error) {
+ error_ = error;
+ }
+
+ ConnState GetState() const {
+ return connected_ ? CS_CONNECTED : CS_CLOSED;
+ }
+
+ int SetOption(Option opt, int value) {
+ return 0;
+ }
+
+ int EstimateMTU(uint16* mtu) {
+ if (!connected_)
+ return ENOTCONN;
+ else
+ return 65536;
+ }
+
+ void OnMessage(Message *pmsg) {
+ if (pmsg->message_id == MSG_ID_PACKET) {
+ assert(pmsg->pdata);
+ Packet* packet = static_cast<Packet*>(pmsg->pdata);
+
+ if (!readable_)
+ return;
+
+ packets_->push_back(packet);
+
+ if (async_) {
+ SignalReadEvent(this);
+
+ // TODO: If the listeners don't want to read this packet now, we will
+ // need to send ourselves delayed messages to try again.
+ assert(packets_->size() == 0);
+ }
+ } else {
+ assert(false);
+ }
+ }
+
+private:
+ struct QueueEntry {
+ uint32 size;
+ uint32 done_time;
+ };
+
+ typedef std::deque<QueueEntry> SendQueue;
+
+ VirtualSocketServer* server_;
+ int type_;
+ bool async_;
+ bool connected_;
+ uint32 local_ip_;
+ bool readable_;
+ SocketAddress local_addr_;
+ SocketAddress remote_addr_;
+ std::vector<Packet*>* packets_;
+ int error_;
+ SendQueue queue_;
+ uint32 queue_size_;
+ CriticalSection queue_crit_;
+
+ friend class VirtualSocketServer;
+};
+
+VirtualSocketServer::VirtualSocketServer()
+ : fWait_(false), wait_version_(0), next_ip_(1), next_port_(45000),
+ bandwidth_(0), queue_capacity_(64 * 1024), delay_mean_(0),
+ delay_stddev_(0), delay_dist_(0), drop_prob_(0.0) {
+ msg_queue_ = new MessageQueue(); // uses physical socket server for Wait
+ bindings_ = new AddressMap();
+
+ UpdateDelayDistribution();
+}
+
+VirtualSocketServer::~VirtualSocketServer() {
+ delete bindings_;
+ delete msg_queue_;
+ delete delay_dist_;
+}
+
+uint32 VirtualSocketServer::GetNextIP() {
+ return next_ip_++;
+}
+
+Socket* VirtualSocketServer::CreateSocket(int type) {
+ return CreateSocketInternal(type);
+}
+
+AsyncSocket* VirtualSocketServer::CreateAsyncSocket(int type) {
+ return CreateSocketInternal(type);
+}
+
+VirtualSocket* VirtualSocketServer::CreateSocketInternal(int type) {
+ uint32 ip = (next_ip_ > 1) ? next_ip_ - 1 : 1;
+ return new VirtualSocket(this, type, true, ip);
+}
+
+bool VirtualSocketServer::Wait(int cmsWait, bool process_io) {
+ ASSERT(process_io); // This can't be easily supported.
+
+ uint32 msEnd;
+ if (cmsWait != kForever)
+ msEnd = GetMillisecondCount() + cmsWait;
+ uint32 cmsNext = cmsWait;
+
+ fWait_ = true;
+ wait_version_ += 1;
+
+ while (fWait_) {
+ Message msg;
+ if (!msg_queue_->Get(&msg, cmsNext))
+ return true;
+ msg_queue_->Dispatch(&msg);
+
+ if (cmsWait != kForever) {
+ uint32 msCur = GetMillisecondCount();
+ if (msCur >= msEnd)
+ return true;
+ cmsNext = msEnd - msCur;
+ }
+ }
+ return true;
+}
+
+const uint32 MSG_WAKE_UP = 1;
+
+struct WakeUpMessage : public MessageData {
+ WakeUpMessage(uint32 ver) : wait_version(ver) {}
+ virtual ~WakeUpMessage() {}
+
+ uint32 wait_version;
+};
+
+void VirtualSocketServer::WakeUp() {
+ msg_queue_->Post(this, MSG_WAKE_UP, new WakeUpMessage(wait_version_));
+}
+
+void VirtualSocketServer::OnMessage(Message* pmsg) {
+ assert(pmsg->message_id == MSG_WAKE_UP);
+ assert(pmsg->pdata);
+ WakeUpMessage* wmsg = static_cast<WakeUpMessage*>(pmsg->pdata);
+ if (wmsg->wait_version == wait_version_)
+ fWait_ = false;
+ delete pmsg->pdata;
+}
+
+int VirtualSocketServer::Bind(
+ const SocketAddress& addr, VirtualSocket* socket) {
+ assert(addr.ip() > 0); // don't support any-address right now
+ assert(addr.port() > 0);
+ assert(socket);
+
+ if (bindings_->find(addr) == bindings_->end()) {
+ (*bindings_)[addr] = socket;
+ return 0;
+ } else {
+ return -1;
+ }
+}
+
+int VirtualSocketServer::Bind(VirtualSocket* socket, SocketAddress* addr) {
+ assert(addr->ip() > 0); // don't support any-address right now
+ assert(socket);
+
+ for (int i = 0; i < 65536; i++) {
+ addr->SetPort(next_port_++);
+ if (addr->port() > 0) {
+ AddressMap::iterator iter = bindings_->find(*addr);
+ if (iter == bindings_->end()) {
+ (*bindings_)[*addr] = socket;
+ return 0;
+ }
+ }
+ }
+
+ errno = EADDRINUSE; // TODO: is there a better error number?
+ return -1;
+}
+
+int VirtualSocketServer::Unbind(
+ const SocketAddress& addr, VirtualSocket* socket) {
+ assert((*bindings_)[addr] == socket);
+ bindings_->erase(bindings_->find(addr));
+ return 0;
+}
+
+static double Random() {
+ return double(rand()) / RAND_MAX;
+}
+
+int VirtualSocketServer::Send(
+ VirtualSocket* socket, const void *pv, size_t cb,
+ const SocketAddress& local_addr, const SocketAddress& remote_addr) {
+
+ // See if we want to drop this packet.
+ if (Random() < drop_prob_) {
+ std::cerr << "Dropping packet: bad luck" << std::endl;
+ return 0;
+ }
+
+ uint32 cur_time = GetMillisecondCount();
+ uint32 send_delay;
+
+ // Determine whether we have enough bandwidth to accept this packet. To do
+ // this, we need to update the send queue. Once we know it's current size,
+ // we know whether we can fit this packet.
+ //
+ // NOTE: There are better algorithms for maintaining such a queue (such as
+ // "Derivative Random Drop"); however, this algorithm is a more accurate
+ // simulation of what a normal network would do.
+ {
+ CritScope cs(&socket->queue_crit_);
+
+ while ((socket->queue_.size() > 0) &&
+ (socket->queue_.front().done_time <= cur_time)) {
+ assert(socket->queue_size_ >= socket->queue_.front().size);
+ socket->queue_size_ -= socket->queue_.front().size;
+ socket->queue_.pop_front();
+ }
+
+ VirtualSocket::QueueEntry entry;
+ entry.size = uint32(cb) + HEADER_SIZE;
+
+ if (socket->queue_size_ + entry.size > queue_capacity_) {
+ std::cerr << "Dropping packet: queue capacity exceeded" << std::endl;
+ return 0; // not an error
+ }
+
+ socket->queue_size_ += entry.size;
+ send_delay = SendDelay(socket->queue_size_);
+ entry.done_time = cur_time + send_delay;
+ socket->queue_.push_back(entry);
+ }
+
+ // Find the delay for crossing the many virtual hops of the network.
+ uint32 transit_delay = GetRandomTransitDelay();
+
+ // Post the packet as a message to be delivered (on our own thread)
+
+ AddressMap::iterator iter = bindings_->find(remote_addr);
+ if (iter != bindings_->end()) {
+ Packet* p = new Packet(static_cast<const char*>(pv), cb, local_addr);
+ uint32 delay = send_delay + transit_delay;
+ msg_queue_->PostDelayed(delay, iter->second, MSG_ID_PACKET, p);
+ } else {
+ std::cerr << "No one listening at " << remote_addr.ToString() << std::endl;
+ }
+ return (int)cb;
+}
+
+uint32 VirtualSocketServer::SendDelay(uint32 size) {
+ if (bandwidth_ == 0)
+ return 0;
+ else
+ return 1000 * size / bandwidth_;
+}
+
+void PrintFunction(std::vector<std::pair<double,double> >* f) {
+ for (uint32 i = 0; i < f->size(); i++)
+ std::cout << (*f)[i].first << '\t' << (*f)[i].second << std::endl;
+}
+
+void VirtualSocketServer::UpdateDelayDistribution() {
+ Function* dist = GetDelayDistribution();
+ dist = Resample(Invert(Accumulate(dist)), 0, 1);
+
+ // We take a lock just to make sure we don't leak memory.
+ {
+ CritScope cs(&delay_crit_);
+ delete delay_dist_;
+ delay_dist_ = dist;
+ }
+}
+
+const int NUM_SAMPLES = 100; // 1000;
+
+static double PI = 4 * std::atan(1.0);
+
+static double Normal(double x, double mean, double stddev) {
+ double a = (x - mean) * (x - mean) / (2 * stddev * stddev);
+ return std::exp(-a) / (stddev * sqrt(2 * PI));
+}
+
+#if 0 // static unused gives a warning
+static double Pareto(double x, double min, double k) {
+ if (x < min)
+ return 0;
+ else
+ return k * std::pow(min, k) / std::pow(x, k+1);
+}
+#endif
+
+VirtualSocketServer::Function* VirtualSocketServer::GetDelayDistribution() {
+ Function* f = new Function();
+
+ if (delay_stddev_ == 0) {
+
+ f->push_back(Point(delay_mean_, 1.0));
+
+ } else {
+
+ double start = 0;
+ if (delay_mean_ >= 4 * double(delay_stddev_))
+ start = delay_mean_ - 4 * double(delay_stddev_);
+ double end = delay_mean_ + 4 * double(delay_stddev_);
+
+ double delay_min = 0;
+ if (delay_mean_ >= 1.0 * delay_stddev_)
+ delay_min = delay_mean_ - 1.0 * delay_stddev_;
+
+ for (int i = 0; i < NUM_SAMPLES; i++) {
+ double x = start + (end - start) * i / (NUM_SAMPLES - 1);
+ double y = Normal(x, delay_mean_, delay_stddev_);
+ f->push_back(Point(x, y));
+ }
+
+ }
+
+ return f;
+}
+
+uint32 VirtualSocketServer::GetRandomTransitDelay() {
+ double delay = (*delay_dist_)[rand() % delay_dist_->size()].second;
+ return uint32(delay);
+}
+
+struct FunctionDomainCmp {
+ bool operator ()(const VirtualSocketServer::Point& p1, const VirtualSocketServer::Point& p2) {
+ return p1.first < p2.first;
+ }
+ bool operator ()(double v1, const VirtualSocketServer::Point& p2) {
+ return v1 < p2.first;
+ }
+ bool operator ()(const VirtualSocketServer::Point& p1, double v2) {
+ return p1.first < v2;
+ }
+};
+
+VirtualSocketServer::Function* VirtualSocketServer::Accumulate(Function* f) {
+ assert(f->size() >= 1);
+ double v = 0;
+ for (Function::size_type i = 0; i < f->size() - 1; ++i) {
+ double dx = (*f)[i].second * ((*f)[i+1].first - (*f)[i].first);
+ v = (*f)[i].second = v + dx;
+ }
+ (*f)[f->size()-1].second = v;
+ return f;
+}
+
+VirtualSocketServer::Function* VirtualSocketServer::Invert(Function* f) {
+ for (Function::size_type i = 0; i < f->size(); ++i)
+ std::swap((*f)[i].first, (*f)[i].second);
+
+ std::sort(f->begin(), f->end(), FunctionDomainCmp());
+ return f;
+}
+
+VirtualSocketServer::Function* VirtualSocketServer::Resample(
+ Function* f, double x1, double x2) {
+ Function* g = new Function();
+
+ for (int i = 0; i < NUM_SAMPLES; i++) {
+ double x = x1 + (x2 - x1) * i / (NUM_SAMPLES - 1);
+ double y = Evaluate(f, x);
+ g->push_back(Point(x, y));
+ }
+
+ delete f;
+ return g;
+}
+
+double VirtualSocketServer::Evaluate(Function* f, double x) {
+ Function::iterator iter =
+ std::lower_bound(f->begin(), f->end(), x, FunctionDomainCmp());
+ if (iter == f->begin()) {
+ return (*f)[0].second;
+ } else if (iter == f->end()) {
+ assert(f->size() >= 1);
+ return (*f)[f->size() - 1].second;
+ } else if (iter->first == x) {
+ return iter->second;
+ } else {
+ double x1 = (iter - 1)->first;
+ double y1 = (iter - 1)->second;
+ double x2 = iter->first;
+ double y2 = iter->second;
+ return y1 + (y2 - y1) * (x - x1) / (x2 - x1);
+ }
+}
+
+} // namespace talk_base
diff --git a/Plugins/jingle/libjingle/talk/base/virtualsocketserver.h b/Plugins/jingle/libjingle/talk/base/virtualsocketserver.h
new file mode 100644
index 0000000..d2e894a
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/virtualsocketserver.h
@@ -0,0 +1,156 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TALK_BASE_VIRTUALSOCKETSERVER_H__
+#define TALK_BASE_VIRTUALSOCKETSERVER_H__
+
+#include <cassert>
+#include <deque>
+#include <map>
+
+#include "talk/base/messagequeue.h"
+#include "talk/base/socketserver.h"
+
+namespace talk_base {
+
+class VirtualSocket;
+
+// Simulates a network in the same manner as a loopback interface. The
+// interface can create as many addresses as you want. All of the sockets
+// created by this network will be able to communicate with one another.
+class VirtualSocketServer : public SocketServer, public MessageHandler {
+public:
+ VirtualSocketServer();
+ virtual ~VirtualSocketServer();
+
+ // Returns a new IP not used before in this network.
+ uint32 GetNextIP();
+
+ // Limits the network bandwidth (maximum bytes per second). Zero means that
+ // all sends occur instantly.
+ uint32 bandwidth() { return bandwidth_; }
+ void set_bandwidth(uint32 bandwidth) { bandwidth_ = bandwidth; }
+
+ // Limits the total size of packets that will be kept in the send queue,
+ // waiting for their turn to be written to the network Defaults to 64 KB.
+ uint32 queue_capacity() { return queue_capacity_; }
+ void set_queue_capacity(uint32 queue_capacity) {
+ queue_capacity_ = queue_capacity;
+ }
+
+ // Controls the (transit) delay for packets sent in the network. This does
+ // not inclue the time required to sit in the send queue. Both of these
+ // values are measured in milliseconds.
+ uint32 delay_mean() { return delay_mean_; }
+ uint32 delay_stddev() { return delay_stddev_; }
+ void set_delay_mean(uint32 delay_mean) { delay_mean_ = delay_mean; }
+ void set_delay_stddev(uint32 delay_stddev) {
+ delay_stddev_ = delay_stddev;
+ }
+
+ // If the (transit) delay parameters are modified, this method should be
+ // called to recompute the new distribution.
+ void UpdateDelayDistribution();
+
+ // Controls the (uniform) probability that any sent packet is dropped. This
+ // is separate from calculations to drop based on queue size.
+ double drop_probability() { return drop_prob_; }
+ void set_drop_probability(double drop_prob) {
+ assert((0 <= drop_prob) && (drop_prob <= 1));
+ drop_prob_ = drop_prob;
+ }
+
+ // SocketFactory:
+ virtual Socket* CreateSocket(int type);
+ virtual AsyncSocket* CreateAsyncSocket(int type);
+
+ // SocketServer:
+ virtual bool Wait(int cms, bool process_io);
+ virtual void WakeUp();
+
+ // Used to send internal wake-up messages.
+ virtual void OnMessage(Message* msg);
+
+ typedef std::pair<double,double> Point;
+ typedef std::vector<Point> Function;
+
+private:
+ friend class VirtualSocket;
+
+ typedef std::map<SocketAddress, VirtualSocket*> AddressMap;
+
+ MessageQueue* msg_queue_;
+ bool fWait_;
+ uint32 wait_version_;
+ uint32 next_ip_;
+ uint16 next_port_;
+ AddressMap* bindings_;
+
+ uint32 bandwidth_;
+ uint32 queue_capacity_;
+ uint32 delay_mean_;
+ uint32 delay_stddev_;
+ Function* delay_dist_;
+ CriticalSection delay_crit_;
+
+ double drop_prob_;
+
+ VirtualSocket* CreateSocketInternal(int type);
+
+ // Attempts to bind the given socket to the given (non-zero) address.
+ int Bind(const SocketAddress& addr, VirtualSocket* socket);
+
+ // Binds the given socket to the given (non-zero) IP on an unused port.
+ int Bind(VirtualSocket* socket, SocketAddress* addr);
+
+ // Removes the binding for the given socket.
+ int Unbind(const SocketAddress& addr, VirtualSocket* socket);
+
+ // Sends the given packet to the socket at the given address (if one exists).
+ int Send(VirtualSocket* socket, const void *pv, size_t cb,
+ const SocketAddress& local_addr, const SocketAddress& remote_addr);
+
+ // Computes the number of milliseconds required to send a packet of this size.
+ uint32 SendDelay(uint32 size);
+
+ // Returns the probability density function for the transit delay.
+ Function* GetDelayDistribution();
+
+ // Returns a random transit delay chosen from the appropriate distribution.
+ uint32 GetRandomTransitDelay();
+
+ // Basic operations on functions. Those that return a function also take
+ // ownership of the function given (and hence, may modify or delete it).
+ Function* Accumulate(Function* f);
+ Function* Invert(Function* f);
+ Function* Resample(Function* f, double x1, double x2);
+ double Evaluate(Function* f, double x);
+};
+
+} // namespace talk_base
+
+#endif // TALK_BASE_VIRTUALSOCKETSERVER_H__
diff --git a/Plugins/jingle/libjingle/talk/base/win32.h b/Plugins/jingle/libjingle/talk/base/win32.h
new file mode 100644
index 0000000..082e84f
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/win32.h
@@ -0,0 +1,70 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TALK_BASE_WIN32_H__
+#define TALK_BASE_WIN32_H__
+
+#include <winsock2.h>
+#include <windows.h>
+#include <malloc.h>
+
+#include <string>
+
+namespace talk_base {
+
+///////////////////////////////////////////////////////////////////////////////
+
+inline std::wstring ToUtf16(const std::string& str) {
+ int len16 = ::MultiByteToWideChar(CP_UTF8, 0, str.data(), str.length(),
+ NULL, 0);
+ wchar_t *ws = static_cast<wchar_t*>(_alloca(len16 * sizeof(wchar_t)));
+ ::MultiByteToWideChar(CP_UTF8, 0, str.data(), str.length(), ws, len16);
+ std::wstring result(ws, len16);
+ return result;
+}
+
+inline std::string ToUtf8(const std::wstring& wstr) {
+ int len8 = ::WideCharToMultiByte(CP_UTF8, 0, wstr.data(), wstr.length(),
+ NULL, 0, NULL, NULL);
+ char* ns = static_cast<char*>(_alloca(len8));
+ ::WideCharToMultiByte(CP_UTF8, 0, wstr.data(), wstr.length(),
+ ns, len8, NULL, NULL);
+ std::string result(ns, len8);
+ return result;
+}
+
+// Convert FILETIME to time_t
+void FileTimeToUnixTime(const FILETIME& ft, time_t* ut);
+
+// Convert time_t to FILETIME
+void UnixTimeToFileTime(const time_t& ut, FILETIME * ft);
+
+///////////////////////////////////////////////////////////////////////////////
+
+} // namespace talk_base
+
+#endif // TALK_BASE_WIN32_H__
diff --git a/Plugins/jingle/libjingle/talk/base/win32filesystem.cc b/Plugins/jingle/libjingle/talk/base/win32filesystem.cc
new file mode 100644
index 0000000..0015384
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/win32filesystem.cc
@@ -0,0 +1,194 @@
+/*
+ * libjingle
+ * Copyright 2004--2006, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <errno.h>
+#include <cassert>
+
+#include "talk/base/basicdefs.h"
+#include "talk/base/convert.h"
+#include "talk/base/pathutils.h"
+#include "talk/base/fileutils.h"
+#include "talk/base/stringutils.h"
+#include "talk/base/stream.h"
+
+#include "talk/base/win32filesystem.h"
+
+namespace talk_base {
+
+bool Win32Filesystem::CreateFolderI(const Pathname &pathname) {
+ int len = pathname.pathname().length();
+
+ if ((len <= 0) || (pathname.pathname().c_str()[len-1] != '\\')) {
+ return false;
+ }
+
+ DWORD res = ::GetFileAttributes(Utf16(pathname.pathname()).AsWz());
+ if (res != INVALID_FILE_ATTRIBUTES) {
+ // Something exists at this location, check if it is a directory
+ return ((res & FILE_ATTRIBUTE_DIRECTORY) != 0);
+ } else if ((GetLastError() != ERROR_FILE_NOT_FOUND)
+ && (GetLastError() != ERROR_PATH_NOT_FOUND)) {
+ // Unexpected error
+ return false;
+ }
+ // Directory doesn't exist, look up one directory level
+ do {
+ --len;
+ } while ((len > 0) && (pathname.pathname().c_str()[len-1] != '\\'));
+
+ if (!CreateFolder(std::string(pathname.pathname().c_str(),len)))
+ return false;
+
+ if (pathname.pathname().c_str()[0] != '\\') {
+ std::string long_path = std::string("\\\\?\\") + pathname.pathname();
+ return (::CreateDirectory(Utf16(long_path).AsWz(), NULL) != 0);
+ } else {
+ return (::CreateDirectory(Utf16(pathname.pathname()).AsWz(), NULL) != 0);
+ }
+}
+
+FileStream *Win32Filesystem::OpenFileI(const Pathname &filename,
+ const std::string &mode) {
+ talk_base::FileStream *fs = new talk_base::FileStream();
+ if (fs)
+ fs->Open(filename.pathname().c_str(), mode.c_str());
+ return fs;
+}
+
+bool Win32Filesystem::DeleteFileI(const Pathname &filename) {
+ LOG(LS_INFO) << "Deleting " << filename.pathname();
+
+ if (IsFolder(filename)) {
+ Pathname dir;
+ dir.SetFolder(filename.pathname());
+ DirectoryIterator di;
+ di.Iterate(dir.pathname());
+ while(di.Next()) {
+ if (di.Name() == "." || di.Name() == "..")
+ continue;
+ Pathname subdir;
+ subdir.SetFolder(filename.pathname());
+ subdir.SetFilename(di.Name());
+
+ if (!DeleteFile(subdir.pathname()))
+ return false;
+ }
+ std::string no_slash(filename.pathname(), 0, filename.pathname().length()-1);
+ return ::RemoveDirectory(Utf16(no_slash).AsWz()) == 0;
+ }
+ return ::DeleteFile(Utf16(filename.pathname()).AsWz()) == 0;
+}
+
+bool Win32Filesystem::GetTemporaryFolderI(Pathname &pathname, bool create,
+ const std::string *append) {
+// ASSERT(!g_application_name_.empty());
+ wchar_t buffer[MAX_PATH + 1];
+ if (!::GetTempPath(ARRAY_SIZE(buffer), buffer))
+ return false;
+ if (!::GetLongPathName(buffer, buffer, ARRAY_SIZE(buffer)))
+ return false;
+ size_t len = strlen(buffer);
+ if ((len > 0) && (buffer[len-1] != '\\')) {
+ len += talk_base::strcpyn(buffer + len, ARRAY_SIZE(buffer) - len,
+ L"\\");
+ }
+ if ((len > 0) && (buffer[len-1] != '\\')) {
+ len += talk_base::strcpyn(buffer + len, ARRAY_SIZE(buffer) - len,
+ L"\\");
+ }
+ if (len >= ARRAY_SIZE(buffer) - 1)
+ return false;
+ pathname.clear();
+ pathname.SetFolder(Utf8(buffer).AsSz());
+ if (append != NULL)
+ pathname.AppendFolder(*append);
+ if (create)
+ CreateFolderI(pathname);
+ return true;
+}
+
+std::string Win32Filesystem::TempFilenameI(const Pathname &dir, const std::string &prefix) {
+ wchar_t filename[MAX_PATH];
+ if (::GetTempFileName(Utf16(dir.pathname()).AsWz(), Utf16(prefix).AsWz(), 0, filename) == 0)
+ return Utf8(filename).AsString();
+ return "";
+}
+
+bool Win32Filesystem::MoveFileI(const Pathname &old_path, const Pathname &new_path)
+{
+ LOG(LS_INFO) << "Moving " << old_path.pathname() << " to " << new_path.pathname();
+ if (_wrename(Utf16(old_path.pathname()).AsWz(), Utf16(new_path.pathname()).AsWz()) != 0) {
+ if (errno != EXDEV) {
+ printf("errno: %d\n", errno);
+ return false;
+ }
+ if (!CopyFile(old_path, new_path))
+ return false;
+ if (!DeleteFile(old_path))
+ return false;
+ }
+ return true;
+}
+
+bool Win32Filesystem::IsFolderI(const Pathname &path)
+{
+ WIN32_FILE_ATTRIBUTE_DATA data = {0};
+ if (0 == ::GetFileAttributesEx(Utf16(path.pathname()).AsWz(), GetFileExInfoStandard, &data))
+ return false;
+ return (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY;
+}
+
+bool Win32Filesystem::FileExistsI(const Pathname &path)
+{
+ DWORD res = ::GetFileAttributes(Utf16(path.pathname()).AsWz());
+ return res != INVALID_FILE_ATTRIBUTES;
+}
+
+bool Win32Filesystem::CopyFileI(const Pathname &old_path, const Pathname &new_path)
+{
+ return ::CopyFile(Utf16(old_path.pathname()).AsWz(), Utf16(new_path.pathname()).AsWz(), TRUE) == 0;
+}
+
+bool Win32Filesystem::IsTemporaryPathI(const Pathname& pathname)
+{
+ TCHAR buffer[MAX_PATH + 1];
+ if (!::GetTempPath(ARRAY_SIZE(buffer), buffer))
+ return false;
+ if (!::GetLongPathName(buffer, buffer, ARRAY_SIZE(buffer)))
+ return false;
+ return (::strnicmp(Utf16(pathname.pathname()).AsWz(), buffer, strlen(buffer)) == 0);
+}
+
+bool Win32Filesystem::GetFileSizeI(const Pathname &pathname, size_t *size)
+{
+ WIN32_FILE_ATTRIBUTE_DATA data = {0};
+ if (::GetFileAttributesEx(Utf16(pathname.pathname()).AsWz(), GetFileExInfoStandard, &data) == 0)
+ return false;
+ *size = data.nFileSizeLow;
+ return true;
+}
+
+}
diff --git a/Plugins/jingle/libjingle/talk/base/win32filesystem.h b/Plugins/jingle/libjingle/talk/base/win32filesystem.h
new file mode 100644
index 0000000..1c13157
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/win32filesystem.h
@@ -0,0 +1,91 @@
+/*
+ * libjingle
+ * Copyright 2004--2006, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _TALK_BASE_WIN32FILESYSTEM_H__
+#define _TALK_BASE_WIN32FILESYSTEM_H__
+
+#include "fileutils.h"
+
+namespace talk_base {
+
+class Win32Filesystem : public Filesystem{
+ public:
+
+ virtual bool CreateFolderI(const Pathname &pathname);
+
+ // Opens a file. Returns an open StreamInterface if function succeeds. Otherwise,
+ // returns NULL.
+ virtual FileStream *OpenFileI(const Pathname &filename,
+ const std::string &mode);
+
+ // This will attempt to delete the path located at filename. If filename is a file,
+ // it will be unlinked. If the path is a directory, it will recursively unlink and remove
+ // all the files and directory within it
+ virtual bool DeleteFileI(const Pathname &filename);
+
+ // Creates a directory. This will call itself recursively to create /foo/bar even if
+ // /foo does not exist.
+ // Returns TRUE if function succeeds
+
+ // This moves a file from old_path to new_path, where "file" can be a plain file
+ // or directory, which will be moved recursively.
+ // Returns true if function succeeds.
+ virtual bool MoveFileI(const Pathname &old_path, const Pathname &new_path);
+
+ // This copies a file from old_path to _new_path where "file" can be a plain file
+ // or directory, which will be copied recursively.
+ // Returns true if function succeeds
+ virtual bool CopyFileI(const Pathname &old_path, const Pathname &new_path);
+
+ // Returns true if a pathname is a directory
+ virtual bool IsFolderI(const Pathname& pathname);
+
+ // Returns true if a file exists at path
+ virtual bool FileExistsI(const Pathname &path);
+
+ // Returns true if pathname represents a temporary location on the system.
+ virtual bool IsTemporaryPathI(const Pathname& pathname);
+
+
+ // All of the following functions set pathname and return true if successful.
+ // Returned paths always include a trailing backslash.
+ // If create is true, the path will be recursively created.
+ // If append is non-NULL, it will be appended (and possibly created).
+
+ virtual std::string TempFilenameI(const Pathname &dir, const std::string &prefix);
+
+ virtual bool GetFileSizeI(const Pathname &pathname, size_t *size);
+
+ // A folder appropriate for storing temporary files (Contents are
+ // automatically deleted when the program exists)
+ virtual bool GetTemporaryFolderI(Pathname &path, bool create,
+ const std::string *append);
+ };
+
+}
+
+#endif // _WIN32FILESYSTEM_H__
diff --git a/Plugins/jingle/libjingle/talk/base/win32socketserver.cc b/Plugins/jingle/libjingle/talk/base/win32socketserver.cc
new file mode 100644
index 0000000..49c8fee
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/win32socketserver.cc
@@ -0,0 +1,767 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "talk/base/byteorder.h"
+#include "talk/base/common.h"
+#include "talk/base/logging.h"
+#include "talk/base/winping.h"
+#include "talk/base/win32socketserver.h"
+#include "talk/base/win32window.h"
+#include <ws2tcpip.h>
+
+namespace talk_base {
+
+///////////////////////////////////////////////////////////////////////////////
+// Win32Socket
+///////////////////////////////////////////////////////////////////////////////
+
+static const int kfRead = 0x0001;
+static const int kfWrite = 0x0002;
+
+// Standard MTUs
+static const uint16 PACKET_MAXIMUMS[] = {
+ 65535, // Theoretical maximum, Hyperchannel
+ 32000, // Nothing
+ 17914, // 16Mb IBM Token Ring
+ 8166, // IEEE 802.4
+ //4464, // IEEE 802.5 (4Mb max)
+ 4352, // FDDI
+ //2048, // Wideband Network
+ 2002, // IEEE 802.5 (4Mb recommended)
+ //1536, // Expermental Ethernet Networks
+ //1500, // Ethernet, Point-to-Point (default)
+ 1492, // IEEE 802.3
+ 1006, // SLIP, ARPANET
+ //576, // X.25 Networks
+ //544, // DEC IP Portal
+ //512, // NETBIOS
+ 508, // IEEE 802/Source-Rt Bridge, ARCNET
+ 296, // Point-to-Point (low delay)
+ 68, // Official minimum
+ 0, // End of list marker
+};
+
+static const uint32 IP_HEADER_SIZE = 20;
+static const uint32 ICMP_HEADER_SIZE = 8;
+
+#ifdef DEBUG
+LPCSTR WSAErrorToString(int error, LPCSTR *description_result) {
+ LPCSTR string = "Unspecified";
+ LPCSTR description = "Unspecified description";
+ switch (error) {
+ case ERROR_SUCCESS:
+ string = "SUCCESS";
+ description = "Operation succeeded";
+ break;
+ case WSAEWOULDBLOCK:
+ string = "WSAEWOULDBLOCK";
+ description = "Using a non-blocking socket, will notify later";
+ break;
+ case WSAEACCES:
+ string = "WSAEACCES";
+ description = "Access denied, or sharing violation";
+ break;
+ case WSAEADDRNOTAVAIL:
+ string = "WSAEADDRNOTAVAIL";
+ description = "Address is not valid in this context";
+ break;
+ case WSAENETDOWN:
+ string = "WSAENETDOWN";
+ description = "Network is down";
+ break;
+ case WSAENETUNREACH:
+ string = "WSAENETUNREACH";
+ description = "Network is up, but unreachable";
+ break;
+ case WSAENETRESET:
+ string = "WSANETRESET";
+ description = "Connection has been reset due to keep-alive activity";
+ break;
+ case WSAECONNABORTED:
+ string = "WSAECONNABORTED";
+ description = "Aborted by host";
+ break;
+ case WSAECONNRESET:
+ string = "WSAECONNRESET";
+ description = "Connection reset by host";
+ break;
+ case WSAETIMEDOUT:
+ string = "WSAETIMEDOUT";
+ description = "Timed out, host failed to respond";
+ break;
+ case WSAECONNREFUSED:
+ string = "WSAECONNREFUSED";
+ description = "Host actively refused connection";
+ break;
+ case WSAEHOSTDOWN:
+ string = "WSAEHOSTDOWN";
+ description = "Host is down";
+ break;
+ case WSAEHOSTUNREACH:
+ string = "WSAEHOSTUNREACH";
+ description = "Host is unreachable";
+ break;
+ case WSAHOST_NOT_FOUND:
+ string = "WSAHOST_NOT_FOUND";
+ description = "No such host is known";
+ break;
+ }
+ if (description_result) {
+ *description_result = description;
+ }
+ return string;
+}
+
+void ReportWSAError(LPCSTR context, int error, const sockaddr_in& addr) {
+ talk_base::SocketAddress address;
+ address.FromSockAddr(addr);
+ LPCSTR description_string;
+ LPCSTR error_string = WSAErrorToString(error, &description_string);
+ LOG(LS_INFO) << context << " = " << error
+ << " (" << error_string << ":" << description_string << ") ["
+ << address.ToString() << "]";
+}
+#else
+void ReportWSAError(LPCSTR context, int error, const sockaddr_in& addr) { }
+#endif
+
+/////////////////////////////////////////////////////////////////////////////
+// Win32Socket::EventSink
+/////////////////////////////////////////////////////////////////////////////
+
+#define WM_SOCKETNOTIFY (WM_USER + 50)
+#define WM_DNSNOTIFY (WM_USER + 51)
+
+struct Win32Socket::DnsLookup {
+ HANDLE handle;
+ uint16 port;
+ char buffer[MAXGETHOSTSTRUCT];
+};
+
+class Win32Socket::EventSink : public Win32Window {
+public:
+ EventSink(Win32Socket * parent) : parent_(parent) { }
+
+ void Dispose();
+
+ virtual bool OnMessage(UINT uMsg, WPARAM wParam, LPARAM lParam,
+ LRESULT& result);
+ virtual void OnFinalMessage(HWND hWnd);
+
+private:
+ bool OnSocketNotify(UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT& result);
+ bool OnDnsNotify(WPARAM wParam, LPARAM lParam, LRESULT& result);
+
+ Win32Socket * parent_;
+};
+
+void
+Win32Socket::EventSink::Dispose() {
+ parent_ = NULL;
+ if (::IsWindow(handle())) {
+ ::DestroyWindow(handle());
+ } else {
+ delete this;
+ }
+}
+
+bool Win32Socket::EventSink::OnMessage(UINT uMsg, WPARAM wParam, LPARAM lParam,
+ LRESULT& result) {
+ switch (uMsg) {
+ case WM_SOCKETNOTIFY:
+ case WM_TIMER:
+ return OnSocketNotify(uMsg, wParam, lParam, result);
+ case WM_DNSNOTIFY:
+ return OnDnsNotify(wParam, lParam, result);
+ }
+ return false;
+}
+
+bool
+Win32Socket::EventSink::OnSocketNotify(UINT uMsg, WPARAM wParam, LPARAM lParam,
+ LRESULT& result) {
+ result = 0;
+
+ // Make sure the socket isn't already closed
+ if (!parent_ || (parent_->socket_ == INVALID_SOCKET))
+ return true;
+
+ int event = WSAGETSELECTEVENT(lParam);
+ int wsa_error = WSAGETSELECTERROR(lParam);
+
+ if (uMsg == WM_TIMER) {
+ event = FD_CLOSE;
+ wsa_error = WSAETIMEDOUT;
+ } else if (event == FD_CLOSE) {
+ char ch;
+ if (::recv(parent_->socket_, &ch, 1, MSG_PEEK) > 0) {
+ parent_->signal_close_ = true;
+ return true;
+ }
+ }
+
+ parent_->OnSocketNotify(event, wsa_error);
+ return true;
+}
+
+bool
+Win32Socket::EventSink::OnDnsNotify(WPARAM wParam, LPARAM lParam,
+ LRESULT& result) {
+ result = 0;
+
+ if (!parent_)
+ return true;
+
+ if (!parent_->dns_ ||
+ (parent_->dns_->handle != reinterpret_cast<HANDLE>(wParam))) {
+ ASSERT(false);
+ return true;
+ }
+
+ uint32 ip = 0;
+ int error = WSAGETASYNCERROR(lParam);
+
+ if (error == 0) {
+ hostent * pHost = reinterpret_cast<hostent *>(parent_->dns_->buffer);
+ uint32 net_ip = *reinterpret_cast<uint32 *>(pHost->h_addr_list[0]);
+ ip = talk_base::NetworkToHost32(net_ip);
+ }
+
+ parent_->OnDnsNotify(ip, error);
+ return true;
+}
+
+void
+Win32Socket::EventSink::OnFinalMessage(HWND hWnd) {
+ delete this;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// Win32Socket
+/////////////////////////////////////////////////////////////////////////////
+
+Win32Socket::Win32Socket()
+ : socket_(INVALID_SOCKET), error_(0), state_(CS_CLOSED),
+ signal_close_(false), sink_(NULL), dns_(NULL) {
+ // TODO: replace addr_ with SocketAddress
+ memset(&addr_, 0, sizeof(addr_));
+}
+
+Win32Socket::~Win32Socket() {
+ Close();
+}
+
+int
+Win32Socket::Attach(SOCKET s) {
+ ASSERT(socket_ == INVALID_SOCKET);
+ if (socket_ != INVALID_SOCKET)
+ return SOCKET_ERROR;
+
+ ASSERT(s != INVALID_SOCKET);
+ if (s == INVALID_SOCKET)
+ return SOCKET_ERROR;
+
+ socket_ = s;
+ state_ = CS_CONNECTED;
+
+ if (!Create(FD_READ | FD_WRITE | FD_CLOSE))
+ return SOCKET_ERROR;
+
+ return 0;
+}
+
+void
+Win32Socket::SetTimeout(int ms) {
+ if (sink_)
+ ::SetTimer(sink_->handle(), 1, ms, 0);
+}
+
+talk_base::SocketAddress
+Win32Socket::GetLocalAddress() const {
+ sockaddr_in addr;
+ socklen_t addrlen = sizeof(addr);
+ int result = ::getsockname(socket_, (sockaddr*)&addr, &addrlen);
+ ASSERT(addrlen == sizeof(addr));
+ talk_base::SocketAddress address;
+ if (result >= 0) {
+ address.FromSockAddr(addr);
+ } else {
+ ASSERT(result >= 0);
+ }
+ return address;
+}
+
+talk_base::SocketAddress
+Win32Socket::GetRemoteAddress() const {
+ sockaddr_in addr;
+ socklen_t addrlen = sizeof(addr);
+ int result = ::getpeername(socket_, (sockaddr*)&addr, &addrlen);
+ ASSERT(addrlen == sizeof(addr));
+ talk_base::SocketAddress address;
+ if (result >= 0) {
+ address.FromSockAddr(addr);
+ } else {
+ ASSERT(errno == ENOTCONN);
+ }
+ return address;
+}
+
+int
+Win32Socket::Bind(const talk_base::SocketAddress& addr) {
+ ASSERT(socket_ == INVALID_SOCKET);
+ if (socket_ != INVALID_SOCKET)
+ return SOCKET_ERROR;
+
+ if (!Create(FD_ACCEPT | FD_CLOSE))
+ return SOCKET_ERROR;
+
+ sockaddr_in saddr;
+ addr.ToSockAddr(&saddr);
+ int err = ::bind(socket_, (sockaddr*)&saddr, sizeof(saddr));
+ UpdateLastError();
+ return err;
+}
+
+int
+Win32Socket::Connect(const talk_base::SocketAddress& addr) {
+ ASSERT(socket_ == INVALID_SOCKET);
+ if (socket_ != INVALID_SOCKET)
+ return SOCKET_ERROR;
+
+ if (!Create(FD_READ | FD_WRITE | FD_CONNECT | FD_CLOSE))
+ return SOCKET_ERROR;
+
+ if (!addr.IsUnresolved()) {
+ sockaddr_in saddr;
+ addr.ToSockAddr(&saddr);
+
+ // now connect
+ return DoConnect(saddr);
+ }
+
+ LOG_F(LS_INFO) << "async dns lookup (" << addr.IPAsString() << ")";
+ DnsLookup * dns = new DnsLookup;
+ dns->handle = WSAAsyncGetHostByName(sink_->handle(), WM_DNSNOTIFY,
+ addr.IPAsString().c_str(), dns->buffer, sizeof(dns->buffer));
+
+ if (!dns->handle) {
+ LOG_F(LS_ERROR) << "WSAAsyncGetHostByName error: " << WSAGetLastError();
+ delete dns;
+ UpdateLastError();
+ Close();
+ return SOCKET_ERROR;
+ }
+
+ dns->port = addr.port();
+ dns_ = dns;
+ state_ = CS_CONNECTING;
+ return 0;
+}
+
+int
+Win32Socket::DoConnect(const sockaddr_in& addr) {
+ connect_time_ = talk_base::GetMillisecondCount();
+ int result = connect(socket_, (SOCKADDR*)&addr, sizeof(addr));
+ if (result == SOCKET_ERROR) {
+ int code = WSAGetLastError();
+ if (code != WSAEWOULDBLOCK) {
+ ReportWSAError("WSAAsync:connect", code, addr);
+ error_ = code;
+ Close();
+ return SOCKET_ERROR;
+ }
+ }
+ addr_ = addr;
+ state_ = CS_CONNECTING;
+ return 0;
+}
+
+void
+Win32Socket::OnSocketNotify(int event, int error) {
+ error_ = error;
+ switch (event) {
+ case FD_CONNECT:
+ if (error != ERROR_SUCCESS) {
+ ReportWSAError("WSAAsync:connect notify", error, addr_);
+#ifdef DEBUG
+ int32 duration = talk_base::TimeDiff(talk_base::GetMillisecondCount(),
+ connect_time_);
+ LOG(LS_INFO) << "WSAAsync:connect error (" << duration
+ << " ms), faking close";
+#endif
+ Close();
+ // If you get an error connecting, close doesn't really do anything
+ // and it certainly doesn't send back any close notification, but
+ // we really only maintain a few states, so it is easiest to get
+ // back into a known state by pretending that a close happened, even
+ // though the connect event never did occur.
+ SignalCloseEvent(this, error);
+ } else {
+#ifdef DEBUG
+ int32 duration = talk_base::TimeDiff(talk_base::GetMillisecondCount(),
+ connect_time_);
+ LOG(LS_INFO) << "WSAAsync:connect (" << duration << " ms)";
+#endif
+ state_ = CS_CONNECTED;
+ SignalConnectEvent(this);
+ }
+ break;
+
+ case FD_ACCEPT:
+ case FD_READ:
+ if (error != ERROR_SUCCESS) {
+ ReportWSAError("WSAAsync:read notify", error, addr_);
+ Close();
+ } else {
+ SignalReadEvent(this);
+ }
+ break;
+
+ case FD_WRITE:
+ if (error != ERROR_SUCCESS) {
+ ReportWSAError("WSAAsync:write notify", error, addr_);
+ Close();
+ } else {
+ SignalWriteEvent(this);
+ }
+ break;
+
+ case FD_CLOSE:
+ ReportWSAError("WSAAsync:close notify", error, addr_);
+ Close();
+ SignalCloseEvent(this, error);
+ break;
+ }
+}
+
+void
+Win32Socket::OnDnsNotify(int ip, int error) {
+ LOG_F(LS_INFO) << "(" << talk_base::SocketAddress::IPToString(ip)
+ << ", " << error << ")";
+ if (error == 0) {
+ talk_base::SocketAddress address(ip, dns_->port);
+ sockaddr_in addr;
+ address.ToSockAddr(&addr);
+ error = DoConnect(addr);
+ } else {
+ Close();
+ }
+
+ if (error) {
+ error_ = error;
+ SignalCloseEvent(this, error_);
+ } else {
+ delete dns_;
+ dns_ = NULL;
+ }
+}
+
+int
+Win32Socket::GetError() const {
+ return error_;
+}
+
+void
+Win32Socket::SetError(int error) {
+ error_ = error;
+}
+
+Socket::ConnState
+Win32Socket::GetState() const {
+ return state_;
+}
+
+int
+Win32Socket::SetOption(Option opt, int value) {
+ ASSERT(opt == OPT_DONTFRAGMENT);
+ value = (value == 0) ? 0 : 1;
+ return ::setsockopt(socket_, IPPROTO_IP, IP_DONTFRAGMENT,
+ reinterpret_cast<char*>(&value), sizeof(value));
+}
+
+int
+Win32Socket::Send(const void *pv, size_t cb) {
+ int sent = ::send(socket_, reinterpret_cast<const char *>(pv), (int)cb, 0);
+ UpdateLastError();
+ return sent;
+}
+
+int
+Win32Socket::SendTo(const void *pv, size_t cb,
+ const talk_base::SocketAddress& addr) {
+ sockaddr_in saddr;
+ addr.ToSockAddr(&saddr);
+ int sent = ::sendto(socket_, reinterpret_cast<const char *>(pv), (int)cb, 0,
+ (sockaddr*)&saddr, sizeof(saddr));
+ UpdateLastError();
+ return sent;
+}
+
+int
+Win32Socket::Recv(void *pv, size_t cb) {
+ int received = ::recv(socket_, (char *)pv, (int)cb, 0);
+ UpdateLastError();
+ if (signal_close_ && (received > 0)) {
+ char ch;
+ if (::recv(socket_, &ch, 1, MSG_PEEK) <= 0) {
+ signal_close_ = false;
+ ::PostMessage(sink_->handle(), WM_SOCKETNOTIFY,
+ WSAMAKESELECTREPLY(FD_CLOSE, 0), 0);
+ }
+ }
+ return received;
+}
+
+int
+Win32Socket::RecvFrom(void *pv, size_t cb, talk_base::SocketAddress *paddr) {
+ sockaddr_in saddr;
+ socklen_t cbAddr = sizeof(saddr);
+ int received = ::recvfrom(socket_, (char *)pv, (int)cb, 0, (sockaddr*)&saddr,
+ &cbAddr);
+ UpdateLastError();
+ if (received != SOCKET_ERROR)
+ paddr->FromSockAddr(saddr);
+ if (signal_close_ && (received > 0)) {
+ char ch;
+ if (::recv(socket_, &ch, 1, MSG_PEEK) <= 0) {
+ signal_close_ = false;
+ ::PostMessage(sink_->handle(), WM_SOCKETNOTIFY,
+ WSAMAKESELECTREPLY(FD_CLOSE, 0), 0);
+ }
+ }
+ return received;
+}
+
+int
+Win32Socket::Listen(int backlog) {
+ int err = ::listen(socket_, backlog);
+ UpdateLastError();
+ if (err == 0)
+ state_ = CS_CONNECTING;
+ return err;
+}
+
+talk_base::Socket*
+Win32Socket::Accept(talk_base::SocketAddress *paddr) {
+ sockaddr_in saddr;
+ socklen_t cbAddr = sizeof(saddr);
+ SOCKET s = ::accept(socket_, (sockaddr*)&saddr, &cbAddr);
+ UpdateLastError();
+ if (s == INVALID_SOCKET)
+ return NULL;
+ if (paddr)
+ paddr->FromSockAddr(saddr);
+ Win32Socket* socket = new Win32Socket;
+ if (0 == socket->Attach(s))
+ return socket;
+ delete socket;
+ return NULL;
+}
+
+int
+Win32Socket::Close() {
+ int err = 0;
+ if (socket_ != INVALID_SOCKET) {
+ err = ::closesocket(socket_);
+ socket_ = INVALID_SOCKET;
+ signal_close_ = false;
+ UpdateLastError();
+ }
+ if (dns_) {
+ WSACancelAsyncRequest(dns_->handle);
+ delete dns_;
+ dns_ = NULL;
+ }
+ if (sink_) {
+ sink_->Dispose();
+ sink_ = NULL;
+ }
+ memset(&addr_, 0, sizeof(addr_)); // no longer connected, zero ip/port
+ state_ = CS_CLOSED;
+ return err;
+}
+
+int
+Win32Socket::EstimateMTU(uint16* mtu) {
+ talk_base::SocketAddress addr = GetRemoteAddress();
+ if (addr.IsAny()) {
+ error_ = ENOTCONN;
+ return -1;
+ }
+
+ talk_base::WinPing ping;
+ if (!ping.IsValid()) {
+ error_ = EINVAL; // can't think of a better error ID
+ return -1;
+ }
+
+ for (int level = 0; PACKET_MAXIMUMS[level + 1] > 0; ++level) {
+ int32 size = PACKET_MAXIMUMS[level] - IP_HEADER_SIZE - ICMP_HEADER_SIZE;
+ talk_base::WinPing::PingResult result =
+ ping.Ping(addr.ip(), size, 0, 1, false);
+ if (result == talk_base::WinPing::PING_FAIL) {
+ error_ = EINVAL; // can't think of a better error ID
+ return -1;
+ }
+ if (result != talk_base::WinPing::PING_TOO_LARGE) {
+ *mtu = PACKET_MAXIMUMS[level];
+ return 0;
+ }
+ }
+
+ ASSERT(false);
+ return 0;
+}
+
+bool
+Win32Socket::Create(long events) {
+ ASSERT(NULL == sink_);
+
+ if (INVALID_SOCKET == socket_) {
+ socket_ = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, NULL, 0);
+ if (socket_ == INVALID_SOCKET) {
+ UpdateLastError();
+ return false;
+ }
+ }
+
+ // Create window
+ sink_ = new EventSink(this);
+ sink_->Create(NULL, L"EventSink", 0, 0, 0, 0, 10, 10);
+
+ // start the async select
+ if (WSAAsyncSelect(socket_, sink_->handle(), WM_SOCKETNOTIFY, events)
+ == SOCKET_ERROR) {
+ UpdateLastError();
+ Close();
+ return false;
+ }
+
+ return true;
+}
+
+void
+Win32Socket::UpdateLastError() {
+ error_ = WSAGetLastError();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Win32SocketServer
+///////////////////////////////////////////////////////////////////////////////
+
+static UINT s_wm_wakeup_id;
+
+LRESULT CALLBACK DummyWndProc(HWND hwnd, UINT wm, WPARAM wp, LPARAM lp);
+
+// A socket server that provides cricket base services on top of a win32 gui thread
+
+Win32SocketServer::Win32SocketServer(MessageQueue *message_queue) {
+ if (s_wm_wakeup_id == 0)
+ s_wm_wakeup_id = RegisterWindowMessage(L"WM_WAKEUP");
+ message_queue_ = message_queue;
+ hwnd_ = NULL;
+ CreateDummyWindow();
+}
+
+Win32SocketServer::~Win32SocketServer() {
+ if (hwnd_ != NULL) {
+ KillTimer(hwnd_, 1);
+ ::DestroyWindow(hwnd_);
+ }
+}
+
+Socket* Win32SocketServer::CreateSocket(int type) {
+ ASSERT(SOCK_STREAM == type);
+ return new Win32Socket;
+}
+
+AsyncSocket* Win32SocketServer::CreateAsyncSocket(int type) {
+ ASSERT(SOCK_STREAM == type);
+ return new Win32Socket;
+}
+
+bool Win32SocketServer::Wait(int cms, bool process_io) {
+ ASSERT(!process_io || (cms == 0)); // Should only be used for Thread::Send, or in Pump, below
+ if (cms == -1) {
+ MSG msg;
+ GetMessage(&msg, NULL, s_wm_wakeup_id, s_wm_wakeup_id);
+ } else if (cms != 0) {
+ Sleep(cms);
+ }
+ return true;
+}
+
+void Win32SocketServer::WakeUp() {
+ // Always post for every wakeup, so there are no
+ // critical sections
+ if (hwnd_ != NULL)
+ PostMessage(hwnd_, s_wm_wakeup_id, 0, 0);
+}
+
+void Win32SocketServer::Pump() {
+ // Process messages
+ Message msg;
+ while (message_queue_->Get(&msg, 0))
+ message_queue_->Dispatch(&msg);
+
+ // Anything remaining?
+ int delay = message_queue_->GetDelay();
+ if (delay == -1) {
+ KillTimer(hwnd_, 1);
+ } else {
+ SetTimer(hwnd_, 1, delay, NULL);
+ }
+}
+
+void Win32SocketServer::CreateDummyWindow()
+{
+ static bool s_registered;
+ if (!s_registered) {
+ ::WNDCLASSW wc;
+ memset(&wc, 0, sizeof(wc));
+ wc.cbWndExtra = sizeof(this);
+ wc.lpszClassName = L"Dummy";
+ wc.lpfnWndProc = DummyWndProc;
+ ::RegisterClassW(&wc);
+ s_registered = true;
+ }
+
+ hwnd_ = ::CreateWindowW(L"Dummy", L"", 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL);
+ SetWindowLong(hwnd_, GWL_USERDATA, (LONG)(LONG_PTR)this);
+}
+
+LRESULT CALLBACK DummyWndProc(HWND hwnd, UINT wm, WPARAM wp, LPARAM lp)
+{
+ if (wm == s_wm_wakeup_id || (wm == WM_TIMER && wp == 1)) {
+ Win32SocketServer *ss = (Win32SocketServer *)(LONG_PTR)GetWindowLong(hwnd, GWL_USERDATA);
+ ss->Pump();
+ return 0;
+ }
+ return ::DefWindowProc(hwnd, wm, wp, lp);
+}
+
+} // namespace talk_base
diff --git a/Plugins/jingle/libjingle/talk/base/win32socketserver.h b/Plugins/jingle/libjingle/talk/base/win32socketserver.h
new file mode 100644
index 0000000..770141a
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/win32socketserver.h
@@ -0,0 +1,124 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TALK_BASE_WIN32SOCKETSERVER_H__
+#define TALK_BASE_WIN32SOCKETSERVER_H__
+
+#ifdef WIN32
+
+#include "talk/base/messagequeue.h"
+#include "talk/base/socketserver.h"
+#include "talk/base/socketfactory.h"
+#include "talk/base/socket.h"
+#include "talk/base/asyncsocket.h"
+
+namespace talk_base {
+
+///////////////////////////////////////////////////////////////////////////////
+// Win32Socket
+///////////////////////////////////////////////////////////////////////////////
+
+class Win32Socket : public talk_base::AsyncSocket {
+public:
+ Win32Socket();
+ virtual ~Win32Socket();
+
+ int Attach(SOCKET s);
+ void SetTimeout(int ms);
+
+ // AsyncSocket Interface
+ virtual SocketAddress GetLocalAddress() const;
+ virtual SocketAddress GetRemoteAddress() const;
+ virtual int Bind(const SocketAddress& addr);
+ virtual int Connect(const SocketAddress& addr);
+ virtual int Send(const void *pv, size_t cb);
+ virtual int SendTo(const void *pv, size_t cb, const SocketAddress& addr);
+ virtual int Recv(void *pv, size_t cb);
+ virtual int RecvFrom(void *pv, size_t cb, SocketAddress *paddr);
+ virtual int Listen(int backlog);
+ virtual Socket *Accept(SocketAddress *paddr);
+ virtual int Close();
+ virtual int GetError() const;
+ virtual void SetError(int error);
+ virtual ConnState GetState() const;
+ virtual int EstimateMTU(uint16* mtu);
+ virtual int SetOption(Option opt, int value);
+
+private:
+ bool Create(long events);
+ void UpdateLastError();
+
+ int DoConnect(const sockaddr_in& addr);
+ void OnSocketNotify(int event, int error);
+ void OnDnsNotify(int ip, int error);
+
+ sockaddr_in addr_; // address that we connected to (see DoConnect)
+ SOCKET socket_;
+ int error_;
+ uint32 connect_time_;
+ ConnState state_;
+ bool signal_close_;
+
+ class EventSink;
+ friend class EventSink;
+ EventSink * sink_;
+
+ struct DnsLookup;
+ DnsLookup * dns_;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// Win32SocketServer
+///////////////////////////////////////////////////////////////////////////////
+
+class Win32SocketServer : public SocketServer {
+public:
+ Win32SocketServer(MessageQueue *message_queue);
+ virtual ~Win32SocketServer();
+
+ // SocketServer Interface
+ virtual Socket* CreateSocket(int type);
+ virtual AsyncSocket* CreateAsyncSocket(int type);
+ virtual bool Wait(int cms, bool process_io);
+ virtual void WakeUp();
+
+ void Pump();
+
+private:
+ void CreateDummyWindow();
+
+ MessageQueue *message_queue_;
+ HWND hwnd_;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+} // namespace talk_base
+
+#endif // WIN32
+
+#endif // TALK_BASE_WIN32SOCKETSERVER_H__
diff --git a/Plugins/jingle/libjingle/talk/base/win32window.h b/Plugins/jingle/libjingle/talk/base/win32window.h
new file mode 100644
index 0000000..a4cdb47
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/win32window.h
@@ -0,0 +1,72 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TALK_BASE_WIN32WINDOW_H__
+#define TALK_BASE_WIN32WINDOW_H__
+
+#ifdef WIN32
+
+#include "talk/base/win32.h"
+
+namespace talk_base {
+
+///////////////////////////////////////////////////////////////////////////////
+// Win32Window
+///////////////////////////////////////////////////////////////////////////////
+
+class Win32Window {
+public:
+ Win32Window();
+ virtual ~Win32Window();
+
+ HWND handle() { return wnd_; }
+
+ bool Create(HWND parent, const wchar_t* title, DWORD style, DWORD exstyle,
+ int x, int y, int cx, int cy);
+ void Destroy();
+
+protected:
+ virtual bool OnMessage(UINT uMsg, WPARAM wParam, LPARAM lParam,
+ LRESULT& result);
+
+ virtual bool OnClose() { return true; }
+ virtual void OnDestroyed() { }
+
+private:
+ static LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam,
+ LPARAM lParam);
+
+ HWND wnd_;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+} // namespace talk_base
+
+#endif // WIN32
+
+#endif // TALK_BASE_WIN32WINDOW_H__
diff --git a/Plugins/jingle/libjingle/talk/base/winfirewall.cc b/Plugins/jingle/libjingle/talk/base/winfirewall.cc
new file mode 100644
index 0000000..9323d01
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/winfirewall.cc
@@ -0,0 +1,141 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <comdef.h>
+#include "netfw.h"
+#include "winfirewall.h"
+
+#define RELEASE(lpUnk) do \
+ { if ((lpUnk) != NULL) { (lpUnk)->Release(); (lpUnk) = NULL; } } while (0)
+
+namespace talk_base {
+
+//////////////////////////////////////////////////////////////////////
+// WinFirewall
+//////////////////////////////////////////////////////////////////////
+
+WinFirewall::WinFirewall() : mgr_(NULL), policy_(NULL), profile_(NULL) {
+}
+
+WinFirewall::~WinFirewall() {
+ Shutdown();
+}
+
+bool
+WinFirewall::Initialize() {
+ if (mgr_)
+ return true;
+
+ HRESULT hr = CoCreateInstance(__uuidof(NetFwMgr),
+ 0, CLSCTX_INPROC_SERVER,
+ __uuidof(INetFwMgr),
+ reinterpret_cast<void **>(&mgr_));
+ if (SUCCEEDED(hr) && (mgr_ != NULL))
+ hr = mgr_->get_LocalPolicy(&policy_);
+ if (SUCCEEDED(hr) && (policy_ != NULL))
+ hr = policy_->get_CurrentProfile(&profile_);
+ return SUCCEEDED(hr) && (profile_ != NULL);
+}
+
+void
+WinFirewall::Shutdown() {
+ RELEASE(profile_);
+ RELEASE(policy_);
+ RELEASE(mgr_);
+}
+
+bool
+WinFirewall::Enabled() {
+ if (!profile_)
+ return false;
+
+ VARIANT_BOOL fwEnabled = VARIANT_FALSE;
+ profile_->get_FirewallEnabled(&fwEnabled);
+ return (fwEnabled != VARIANT_FALSE);
+}
+
+bool
+WinFirewall::Authorized(const char * filename, bool * known) {
+ if (known) {
+ *known = false;
+ }
+
+ if (!profile_)
+ return false;
+
+ VARIANT_BOOL fwEnabled = VARIANT_FALSE;
+ _bstr_t bfilename = filename;
+
+ INetFwAuthorizedApplications * apps = NULL;
+ HRESULT hr = profile_->get_AuthorizedApplications(&apps);
+ if (SUCCEEDED(hr) && (apps != NULL)) {
+ INetFwAuthorizedApplication * app = NULL;
+ hr = apps->Item(bfilename, &app);
+ if (SUCCEEDED(hr) && (app != NULL)) {
+ hr = app->get_Enabled(&fwEnabled);
+ app->Release();
+ if (known) {
+ *known = true;
+ }
+ } else if (hr != HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) {
+ // Unexpected error
+ }
+ apps->Release();
+ }
+
+ return (fwEnabled != VARIANT_FALSE);
+}
+
+bool
+WinFirewall::AddApplication(const char * filename, const char * friendly_name,
+ bool authorized) {
+ INetFwAuthorizedApplications * apps = NULL;
+ HRESULT hr = profile_->get_AuthorizedApplications(&apps);
+ if (SUCCEEDED(hr) && (apps != NULL)) {
+ INetFwAuthorizedApplication * app = NULL;
+ hr = CoCreateInstance(__uuidof(NetFwAuthorizedApplication),
+ 0, CLSCTX_INPROC_SERVER,
+ __uuidof(INetFwAuthorizedApplication),
+ reinterpret_cast<void **>(&app));
+ if (SUCCEEDED(hr) && (app != NULL)) {
+ _bstr_t bstr = filename;
+ hr = app->put_ProcessImageFileName(bstr);
+ bstr = friendly_name;
+ if (SUCCEEDED(hr))
+ hr = app->put_Name(bstr);
+ if (SUCCEEDED(hr))
+ hr = app->put_Enabled(authorized ? VARIANT_TRUE : VARIANT_FALSE);
+ if (SUCCEEDED(hr))
+ hr = apps->Add(app);
+ app->Release();
+ }
+ apps->Release();
+ }
+ return SUCCEEDED(hr);
+}
+
+} // namespace talk_base
diff --git a/Plugins/jingle/libjingle/talk/base/winfirewall.h b/Plugins/jingle/libjingle/talk/base/winfirewall.h
new file mode 100644
index 0000000..09d5a2f
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/winfirewall.h
@@ -0,0 +1,64 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TALK_BASE_WINFIREWALL_H__
+#define TALK_BASE_WINFIREWALL_H__
+
+struct INetFwMgr;
+struct INetFwPolicy;
+struct INetFwProfile;
+
+namespace talk_base {
+
+//////////////////////////////////////////////////////////////////////
+// WinFirewall
+//////////////////////////////////////////////////////////////////////
+
+class WinFirewall {
+public:
+ WinFirewall();
+ ~WinFirewall();
+
+ bool Initialize();
+ void Shutdown();
+
+ bool Enabled();
+ bool Authorized(const char * filename, bool * known = 0);
+
+ bool AddApplication(const char * filename, const char * friendly_name, bool authorized = true);
+
+private:
+ INetFwMgr * mgr_;
+ INetFwPolicy * policy_;
+ INetFwProfile * profile_;
+};
+
+//////////////////////////////////////////////////////////////////////
+
+} // namespace talk_base
+
+#endif // TALK_BASE_WINFIREWALL_H__
diff --git a/Plugins/jingle/libjingle/talk/base/winping.cc b/Plugins/jingle/libjingle/talk/base/winping.cc
new file mode 100644
index 0000000..401a1b2
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/winping.cc
@@ -0,0 +1,317 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "talk/base/byteorder.h"
+#include "talk/base/socketaddress.h"
+#include "talk/base/winping.h"
+#include "talk/base/logging.h"
+#include <cassert>
+
+namespace talk_base {
+
+//////////////////////////////////////////////////////////////////////
+// Found in IPExport.h
+//////////////////////////////////////////////////////////////////////
+
+typedef struct icmp_echo_reply {
+ ULONG Address; // Replying address
+ ULONG Status; // Reply IP_STATUS
+ ULONG RoundTripTime; // RTT in milliseconds
+ USHORT DataSize; // Reply data size in bytes
+ USHORT Reserved; // Reserved for system use
+ PVOID Data; // Pointer to the reply data
+ struct ip_option_information Options; // Reply options
+} ICMP_ECHO_REPLY, * PICMP_ECHO_REPLY;
+
+//
+// IP_STATUS codes returned from IP APIs
+//
+
+#define IP_STATUS_BASE 11000
+
+#define IP_SUCCESS 0
+#define IP_BUF_TOO_SMALL (IP_STATUS_BASE + 1)
+#define IP_DEST_NET_UNREACHABLE (IP_STATUS_BASE + 2)
+#define IP_DEST_HOST_UNREACHABLE (IP_STATUS_BASE + 3)
+#define IP_DEST_PROT_UNREACHABLE (IP_STATUS_BASE + 4)
+#define IP_DEST_PORT_UNREACHABLE (IP_STATUS_BASE + 5)
+#define IP_NO_RESOURCES (IP_STATUS_BASE + 6)
+#define IP_BAD_OPTION (IP_STATUS_BASE + 7)
+#define IP_HW_ERROR (IP_STATUS_BASE + 8)
+#define IP_PACKET_TOO_BIG (IP_STATUS_BASE + 9)
+#define IP_REQ_TIMED_OUT (IP_STATUS_BASE + 10)
+#define IP_BAD_REQ (IP_STATUS_BASE + 11)
+#define IP_BAD_ROUTE (IP_STATUS_BASE + 12)
+#define IP_TTL_EXPIRED_TRANSIT (IP_STATUS_BASE + 13)
+#define IP_TTL_EXPIRED_REASSEM (IP_STATUS_BASE + 14)
+#define IP_PARAM_PROBLEM (IP_STATUS_BASE + 15)
+#define IP_SOURCE_QUENCH (IP_STATUS_BASE + 16)
+#define IP_OPTION_TOO_BIG (IP_STATUS_BASE + 17)
+#define IP_BAD_DESTINATION (IP_STATUS_BASE + 18)
+
+#define IP_ADDR_DELETED (IP_STATUS_BASE + 19)
+#define IP_SPEC_MTU_CHANGE (IP_STATUS_BASE + 20)
+#define IP_MTU_CHANGE (IP_STATUS_BASE + 21)
+#define IP_UNLOAD (IP_STATUS_BASE + 22)
+#define IP_ADDR_ADDED (IP_STATUS_BASE + 23)
+#define IP_MEDIA_CONNECT (IP_STATUS_BASE + 24)
+#define IP_MEDIA_DISCONNECT (IP_STATUS_BASE + 25)
+#define IP_BIND_ADAPTER (IP_STATUS_BASE + 26)
+#define IP_UNBIND_ADAPTER (IP_STATUS_BASE + 27)
+#define IP_DEVICE_DOES_NOT_EXIST (IP_STATUS_BASE + 28)
+#define IP_DUPLICATE_ADDRESS (IP_STATUS_BASE + 29)
+#define IP_INTERFACE_METRIC_CHANGE (IP_STATUS_BASE + 30)
+#define IP_RECONFIG_SECFLTR (IP_STATUS_BASE + 31)
+#define IP_NEGOTIATING_IPSEC (IP_STATUS_BASE + 32)
+#define IP_INTERFACE_WOL_CAPABILITY_CHANGE (IP_STATUS_BASE + 33)
+#define IP_DUPLICATE_IPADD (IP_STATUS_BASE + 34)
+
+#define IP_GENERAL_FAILURE (IP_STATUS_BASE + 50)
+#define MAX_IP_STATUS IP_GENERAL_FAILURE
+#define IP_PENDING (IP_STATUS_BASE + 255)
+
+//
+// Values used in the IP header Flags field.
+//
+#define IP_FLAG_DF 0x2 // Don't fragment this packet.
+
+//
+// Supported IP Option Types.
+//
+// These types define the options which may be used in the OptionsData field
+// of the ip_option_information structure. See RFC 791 for a complete
+// description of each.
+//
+#define IP_OPT_EOL 0 // End of list option
+#define IP_OPT_NOP 1 // No operation
+#define IP_OPT_SECURITY 0x82 // Security option
+#define IP_OPT_LSRR 0x83 // Loose source route
+#define IP_OPT_SSRR 0x89 // Strict source route
+#define IP_OPT_RR 0x7 // Record route
+#define IP_OPT_TS 0x44 // Timestamp
+#define IP_OPT_SID 0x88 // Stream ID (obsolete)
+#define IP_OPT_ROUTER_ALERT 0x94 // Router Alert Option
+
+#define MAX_OPT_SIZE 40 // Maximum length of IP options in bytes
+
+//////////////////////////////////////////////////////////////////////
+// Global Constants and Types
+//////////////////////////////////////////////////////////////////////
+
+const char * const ICMP_DLL_NAME = "icmp.dll";
+const char * const ICMP_CREATE_FUNC = "IcmpCreateFile";
+const char * const ICMP_CLOSE_FUNC = "IcmpCloseHandle";
+const char * const ICMP_SEND_FUNC = "IcmpSendEcho";
+
+inline uint32 ReplySize(uint32 data_size) {
+ // A ping error message is 8 bytes long, so make sure we allow for at least
+ // 8 bytes of reply data.
+ return sizeof(ICMP_ECHO_REPLY) + max(8UL, data_size);
+}
+
+//////////////////////////////////////////////////////////////////////
+// WinPing
+//////////////////////////////////////////////////////////////////////
+
+WinPing::WinPing()
+ : dll_(0), hping_(INVALID_HANDLE_VALUE), create_(0), close_(0), send_(0),
+ data_(0), dlen_(0), reply_(0), rlen_(0), valid_(false) {
+
+ dll_ = LoadLibraryA(ICMP_DLL_NAME);
+ if (!dll_) {
+ LOG(LERROR) << "LoadLibrary: " << GetLastError();
+ return;
+ }
+
+ create_ = (PIcmpCreateFile) GetProcAddress(dll_, ICMP_CREATE_FUNC);
+ close_ = (PIcmpCloseHandle) GetProcAddress(dll_, ICMP_CLOSE_FUNC);
+ send_ = (PIcmpSendEcho) GetProcAddress(dll_, ICMP_SEND_FUNC);
+ if (!create_ || !close_ || !send_) {
+ LOG(LERROR) << "GetProcAddress(ICMP_*): " << GetLastError();
+ return;
+ }
+
+ hping_ = create_();
+ if (hping_ == INVALID_HANDLE_VALUE) {
+ LOG(LERROR) << "IcmpCreateFile: " << GetLastError();
+ return;
+ }
+
+ dlen_ = 0;
+ rlen_ = ReplySize(dlen_);
+ data_ = new char[dlen_];
+ reply_ = new char[rlen_];
+
+ valid_ = true;
+}
+
+WinPing::~WinPing() {
+ if (dll_)
+ FreeLibrary(dll_);
+
+ if ((hping_ != INVALID_HANDLE_VALUE) && close_) {
+ if (!close_(hping_))
+ LOG(WARNING) << "IcmpCloseHandle: " << GetLastError();
+ }
+
+ delete[] data_;
+ delete reply_;
+}
+
+WinPing::PingResult WinPing::Ping(
+ uint32 ip, uint32 data_size, uint32 timeout, uint8 ttl,
+ bool allow_fragments) {
+
+ assert(IsValid());
+
+ IP_OPTION_INFORMATION ipopt;
+ memset(&ipopt, 0, sizeof(ipopt));
+ if (!allow_fragments)
+ ipopt.Flags |= IP_FLAG_DF;
+ ipopt.Ttl = ttl;
+
+ uint32 reply_size = ReplySize(data_size);
+
+ if (data_size > dlen_) {
+ delete [] data_;
+ dlen_ = data_size;
+ data_ = new char[dlen_];
+ memset(data_, 'z', dlen_);
+ }
+
+ if (reply_size > rlen_) {
+ delete [] reply_;
+ rlen_ = reply_size;
+ reply_ = new char[rlen_];
+ }
+
+ DWORD result = send_(hping_, talk_base::HostToNetwork32(ip),
+ data_, uint16(data_size), &ipopt,
+ reply_, reply_size, timeout);
+ if (result == 0) {
+ long error = GetLastError();
+ if (error == IP_PACKET_TOO_BIG)
+ return PING_TOO_LARGE;
+ if (error == IP_REQ_TIMED_OUT)
+ return PING_TIMEOUT;
+ LOG(LERROR) << "IcmpSendEcho(" << talk_base::SocketAddress::IPToString(ip)
+ << ", " << data_size << "): " << error;
+ return PING_FAIL;
+ }
+
+ return PING_SUCCESS;
+}
+
+//////////////////////////////////////////////////////////////////////
+// Microsoft Documenation
+//////////////////////////////////////////////////////////////////////
+//
+// Routine Name:
+//
+// IcmpCreateFile
+//
+// Routine Description:
+//
+// Opens a handle on which ICMP Echo Requests can be issued.
+//
+// Arguments:
+//
+// None.
+//
+// Return Value:
+//
+// An open file handle or INVALID_HANDLE_VALUE. Extended error information
+// is available by calling GetLastError().
+//
+//////////////////////////////////////////////////////////////////////
+//
+// Routine Name:
+//
+// IcmpCloseHandle
+//
+// Routine Description:
+//
+// Closes a handle opened by ICMPOpenFile.
+//
+// Arguments:
+//
+// IcmpHandle - The handle to close.
+//
+// Return Value:
+//
+// TRUE if the handle was closed successfully, otherwise FALSE. Extended
+// error information is available by calling GetLastError().
+//
+//////////////////////////////////////////////////////////////////////
+//
+// Routine Name:
+//
+// IcmpSendEcho
+//
+// Routine Description:
+//
+// Sends an ICMP Echo request and returns any replies. The
+// call returns when the timeout has expired or the reply buffer
+// is filled.
+//
+// Arguments:
+//
+// IcmpHandle - An open handle returned by ICMPCreateFile.
+//
+// DestinationAddress - The destination of the echo request.
+//
+// RequestData - A buffer containing the data to send in the
+// request.
+//
+// RequestSize - The number of bytes in the request data buffer.
+//
+// RequestOptions - Pointer to the IP header options for the request.
+// May be NULL.
+//
+// ReplyBuffer - A buffer to hold any replies to the request.
+// On return, the buffer will contain an array of
+// ICMP_ECHO_REPLY structures followed by the
+// options and data for the replies. The buffer
+// should be large enough to hold at least one
+// ICMP_ECHO_REPLY structure plus
+// MAX(RequestSize, 8) bytes of data since an ICMP
+// error message contains 8 bytes of data.
+//
+// ReplySize - The size in bytes of the reply buffer.
+//
+// Timeout - The time in milliseconds to wait for replies.
+//
+// Return Value:
+//
+// Returns the number of ICMP_ECHO_REPLY structures stored in ReplyBuffer.
+// The status of each reply is contained in the structure. If the return
+// value is zero, extended error information is available via
+// GetLastError().
+//
+//////////////////////////////////////////////////////////////////////
+
+} // namespace talk_base \ No newline at end of file
diff --git a/Plugins/jingle/libjingle/talk/base/winping.h b/Plugins/jingle/libjingle/talk/base/winping.h
new file mode 100644
index 0000000..0d0b051
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/base/winping.h
@@ -0,0 +1,105 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TALK_BASE_WINPING_H__
+#define TALK_BASE_WINPING_H__
+
+#ifdef WIN32
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+#include <winsock2.h>
+#define _WINSOCKAPI_
+#include <windows.h>
+#undef SetPort
+
+#include "talk/base/basictypes.h"
+
+namespace talk_base {
+
+// This class wraps a Win32 API for doing ICMP pinging. This API, unlike the
+// the normal socket APIs (as implemented on Win9x), will return an error if
+// an ICMP packet with the dont-fragment bit set is too large. This means this
+// class can be used to detect the MTU to a given address.
+
+typedef struct ip_option_information {
+ UCHAR Ttl; // Time To Live
+ UCHAR Tos; // Type Of Service
+ UCHAR Flags; // IP header flags
+ UCHAR OptionsSize; // Size in bytes of options data
+ PUCHAR OptionsData; // Pointer to options data
+} IP_OPTION_INFORMATION, * PIP_OPTION_INFORMATION;
+
+typedef HANDLE (WINAPI *PIcmpCreateFile)();
+
+typedef BOOL (WINAPI *PIcmpCloseHandle)(HANDLE icmp_handle);
+
+typedef DWORD (WINAPI *PIcmpSendEcho)(
+ HANDLE IcmpHandle,
+ ULONG DestinationAddress,
+ LPVOID RequestData,
+ WORD RequestSize,
+ PIP_OPTION_INFORMATION RequestOptions,
+ LPVOID ReplyBuffer,
+ DWORD ReplySize,
+ DWORD Timeout);
+
+class WinPing {
+public:
+ WinPing();
+ ~WinPing();
+
+ // Determines whether the class was initialized correctly.
+ bool IsValid() { return valid_; }
+
+ // Attempts to send a ping with the given parameters.
+ enum PingResult { PING_FAIL, PING_TOO_LARGE, PING_TIMEOUT, PING_SUCCESS };
+ PingResult Ping(
+ uint32 ip, uint32 data_size, uint32 timeout_millis, uint8 ttl,
+ bool allow_fragments);
+
+private:
+ HMODULE dll_;
+ HANDLE hping_;
+ PIcmpCreateFile create_;
+ PIcmpCloseHandle close_;
+ PIcmpSendEcho send_;
+ char* data_;
+ uint32 dlen_;
+ char* reply_;
+ uint32 rlen_;
+ bool valid_;
+};
+
+} // namespace talk_base
+
+#endif // WIN32
+
+#endif // TALK_BASE_WINPING_H__
+
diff --git a/Plugins/jingle/libjingle/talk/examples/call/Makefile.am b/Plugins/jingle/libjingle/talk/examples/call/Makefile.am
new file mode 100644
index 0000000..30a3dc6
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/examples/call/Makefile.am
@@ -0,0 +1,15 @@
+EXTRA_DIST=call.vcproj
+bin_PROGRAMS = call
+call_CXXFLAGS = $(AM_CXXFLAGS)
+call_SOURCES = call_main.cc callclient.cc console.cc
+noinst_HEADERS = callclient.h console.h
+call_LDADD = \
+ $(top_srcdir)/talk/examples/login/libcricketexampleslogin.la \
+ $(top_srcdir)/talk/session/phone/libcricketsessionphone.la \
+ $(top_srcdir)/talk/p2p/client/libcricketp2pclient.la \
+ $(top_srcdir)/talk/p2p/base/libcricketp2pbase.la \
+ $(top_srcdir)/talk/xmpp/libcricketxmpp.la \
+ $(top_srcdir)/talk/xmllite/libcricketxmllite.la \
+ $(top_srcdir)/talk/base/libcricketbase.la \
+ $(EXPAT_LIBS) $(ORTP_LIBS) -lpthread -lssl -lcrypto $(ILBC_LIBS) $(SPEEX_LIBS) $(GLIB_LIBS) $(MEDIA_LIBS)
+AM_CPPFLAGS = -DPOSIX
diff --git a/Plugins/jingle/libjingle/talk/examples/call/Makefile.in b/Plugins/jingle/libjingle/talk/examples/call/Makefile.in
new file mode 100644
index 0000000..591d3aa
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/examples/call/Makefile.in
@@ -0,0 +1,537 @@
+# Makefile.in generated by automake 1.9.6 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005 Free Software Foundation, Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+top_builddir = ../../..
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+INSTALL = @INSTALL@
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+target_triplet = @target@
+bin_PROGRAMS = call$(EXEEXT)
+subdir = talk/examples/call
+DIST_COMMON = $(noinst_HEADERS) $(srcdir)/Makefile.am \
+ $(srcdir)/Makefile.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/talk/pkg.m4 \
+ $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+am__installdirs = "$(DESTDIR)$(bindir)"
+binPROGRAMS_INSTALL = $(INSTALL_PROGRAM)
+PROGRAMS = $(bin_PROGRAMS)
+am_call_OBJECTS = call-call_main.$(OBJEXT) call-callclient.$(OBJEXT) \
+ call-console.$(OBJEXT)
+call_OBJECTS = $(am_call_OBJECTS)
+am__DEPENDENCIES_1 =
+call_DEPENDENCIES = \
+ $(top_srcdir)/talk/examples/login/libcricketexampleslogin.la \
+ $(top_srcdir)/talk/session/phone/libcricketsessionphone.la \
+ $(top_srcdir)/talk/p2p/client/libcricketp2pclient.la \
+ $(top_srcdir)/talk/p2p/base/libcricketp2pbase.la \
+ $(top_srcdir)/talk/xmpp/libcricketxmpp.la \
+ $(top_srcdir)/talk/xmllite/libcricketxmllite.la \
+ $(top_srcdir)/talk/base/libcricketbase.la \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
+DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+ $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS)
+LTCXXCOMPILE = $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CXXFLAGS) $(CXXFLAGS)
+CXXLD = $(CXX)
+CXXLINK = $(LIBTOOL) --tag=CXX --mode=link $(CXXLD) $(AM_CXXFLAGS) \
+ $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
+SOURCES = $(call_SOURCES)
+DIST_SOURCES = $(call_SOURCES)
+HEADERS = $(noinst_HEADERS)
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+ALSA_LIBS = @ALSA_LIBS@
+AMDEP_FALSE = @AMDEP_FALSE@
+AMDEP_TRUE = @AMDEP_TRUE@
+AMTAR = @AMTAR@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+ECHO = @ECHO@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+EXPAT_LIBS = @EXPAT_LIBS@
+F77 = @F77@
+FFLAGS = @FFLAGS@
+GIPS_FALSE = @GIPS_FALSE@
+GIPS_TRUE = @GIPS_TRUE@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_LIBS = @GLIB_LIBS@
+ILBC_CFLAGS = @ILBC_CFLAGS@
+ILBC_LIBS = @ILBC_LIBS@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LDFLAGS = @LDFLAGS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+MAKEINFO = @MAKEINFO@
+MEDIA_LIBS = @MEDIA_LIBS@
+OBJEXT = @OBJEXT@
+ORTP_CFLAGS = @ORTP_CFLAGS@
+ORTP_LIBS = @ORTP_LIBS@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PHONE_FALSE = @PHONE_FALSE@
+PHONE_TRUE = @PHONE_TRUE@
+PKG_CONFIG = @PKG_CONFIG@
+RANLIB = @RANLIB@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SPEEX_CFLAGS = @SPEEX_CFLAGS@
+SPEEX_LIBS = @SPEEX_LIBS@
+STRIP = @STRIP@
+VERSION = @VERSION@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_F77 = @ac_ct_F77@
+ac_ct_RANLIB = @ac_ct_RANLIB@
+ac_ct_STRIP = @ac_ct_STRIP@
+am__fastdepCC_FALSE = @am__fastdepCC_FALSE@
+am__fastdepCC_TRUE = @am__fastdepCC_TRUE@
+am__fastdepCXX_FALSE = @am__fastdepCXX_FALSE@
+am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+datadir = @datadir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+sysconfdir = @sysconfdir@
+target = @target@
+target_alias = @target_alias@
+target_cpu = @target_cpu@
+target_os = @target_os@
+target_vendor = @target_vendor@
+EXTRA_DIST = call.vcproj
+call_CXXFLAGS = $(AM_CXXFLAGS)
+call_SOURCES = call_main.cc callclient.cc console.cc
+noinst_HEADERS = callclient.h console.h
+call_LDADD = \
+ $(top_srcdir)/talk/examples/login/libcricketexampleslogin.la \
+ $(top_srcdir)/talk/session/phone/libcricketsessionphone.la \
+ $(top_srcdir)/talk/p2p/client/libcricketp2pclient.la \
+ $(top_srcdir)/talk/p2p/base/libcricketp2pbase.la \
+ $(top_srcdir)/talk/xmpp/libcricketxmpp.la \
+ $(top_srcdir)/talk/xmllite/libcricketxmllite.la \
+ $(top_srcdir)/talk/base/libcricketbase.la \
+ $(EXPAT_LIBS) $(ORTP_LIBS) -lpthread -lssl -lcrypto $(ILBC_LIBS) $(SPEEX_LIBS) $(GLIB_LIBS) $(MEDIA_LIBS)
+
+AM_CPPFLAGS = -DPOSIX
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .cc .lo .o .obj
+$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \
+ && exit 0; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu talk/examples/call/Makefile'; \
+ cd $(top_srcdir) && \
+ $(AUTOMAKE) --gnu talk/examples/call/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+install-binPROGRAMS: $(bin_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ test -z "$(bindir)" || $(mkdir_p) "$(DESTDIR)$(bindir)"
+ @list='$(bin_PROGRAMS)'; for p in $$list; do \
+ p1=`echo $$p|sed 's/$(EXEEXT)$$//'`; \
+ if test -f $$p \
+ || test -f $$p1 \
+ ; then \
+ f=`echo "$$p1" | sed 's,^.*/,,;$(transform);s/$$/$(EXEEXT)/'`; \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) --mode=install $(binPROGRAMS_INSTALL) '$$p' '$(DESTDIR)$(bindir)/$$f'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) --mode=install $(binPROGRAMS_INSTALL) "$$p" "$(DESTDIR)$(bindir)/$$f" || exit 1; \
+ else :; fi; \
+ done
+
+uninstall-binPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(bin_PROGRAMS)'; for p in $$list; do \
+ f=`echo "$$p" | sed 's,^.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/'`; \
+ echo " rm -f '$(DESTDIR)$(bindir)/$$f'"; \
+ rm -f "$(DESTDIR)$(bindir)/$$f"; \
+ done
+
+clean-binPROGRAMS:
+ @list='$(bin_PROGRAMS)'; for p in $$list; do \
+ f=`echo $$p|sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f $$p $$f"; \
+ rm -f $$p $$f ; \
+ done
+call$(EXEEXT): $(call_OBJECTS) $(call_DEPENDENCIES)
+ @rm -f call$(EXEEXT)
+ $(CXXLINK) $(call_LDFLAGS) $(call_OBJECTS) $(call_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/call-call_main.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/call-callclient.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/call-console.Po@am__quote@
+
+.cc.o:
+@am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \
+@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ $<
+
+.cc.obj:
+@am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \
+@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.cc.lo:
+@am__fastdepCXX_TRUE@ if $(LTCXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \
+@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Plo"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(LTCXXCOMPILE) -c -o $@ $<
+
+call-call_main.o: call_main.cc
+@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(call_CXXFLAGS) $(CXXFLAGS) -MT call-call_main.o -MD -MP -MF "$(DEPDIR)/call-call_main.Tpo" -c -o call-call_main.o `test -f 'call_main.cc' || echo '$(srcdir)/'`call_main.cc; \
+@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/call-call_main.Tpo" "$(DEPDIR)/call-call_main.Po"; else rm -f "$(DEPDIR)/call-call_main.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='call_main.cc' object='call-call_main.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(call_CXXFLAGS) $(CXXFLAGS) -c -o call-call_main.o `test -f 'call_main.cc' || echo '$(srcdir)/'`call_main.cc
+
+call-call_main.obj: call_main.cc
+@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(call_CXXFLAGS) $(CXXFLAGS) -MT call-call_main.obj -MD -MP -MF "$(DEPDIR)/call-call_main.Tpo" -c -o call-call_main.obj `if test -f 'call_main.cc'; then $(CYGPATH_W) 'call_main.cc'; else $(CYGPATH_W) '$(srcdir)/call_main.cc'; fi`; \
+@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/call-call_main.Tpo" "$(DEPDIR)/call-call_main.Po"; else rm -f "$(DEPDIR)/call-call_main.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='call_main.cc' object='call-call_main.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(call_CXXFLAGS) $(CXXFLAGS) -c -o call-call_main.obj `if test -f 'call_main.cc'; then $(CYGPATH_W) 'call_main.cc'; else $(CYGPATH_W) '$(srcdir)/call_main.cc'; fi`
+
+call-callclient.o: callclient.cc
+@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(call_CXXFLAGS) $(CXXFLAGS) -MT call-callclient.o -MD -MP -MF "$(DEPDIR)/call-callclient.Tpo" -c -o call-callclient.o `test -f 'callclient.cc' || echo '$(srcdir)/'`callclient.cc; \
+@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/call-callclient.Tpo" "$(DEPDIR)/call-callclient.Po"; else rm -f "$(DEPDIR)/call-callclient.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='callclient.cc' object='call-callclient.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(call_CXXFLAGS) $(CXXFLAGS) -c -o call-callclient.o `test -f 'callclient.cc' || echo '$(srcdir)/'`callclient.cc
+
+call-callclient.obj: callclient.cc
+@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(call_CXXFLAGS) $(CXXFLAGS) -MT call-callclient.obj -MD -MP -MF "$(DEPDIR)/call-callclient.Tpo" -c -o call-callclient.obj `if test -f 'callclient.cc'; then $(CYGPATH_W) 'callclient.cc'; else $(CYGPATH_W) '$(srcdir)/callclient.cc'; fi`; \
+@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/call-callclient.Tpo" "$(DEPDIR)/call-callclient.Po"; else rm -f "$(DEPDIR)/call-callclient.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='callclient.cc' object='call-callclient.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(call_CXXFLAGS) $(CXXFLAGS) -c -o call-callclient.obj `if test -f 'callclient.cc'; then $(CYGPATH_W) 'callclient.cc'; else $(CYGPATH_W) '$(srcdir)/callclient.cc'; fi`
+
+call-console.o: console.cc
+@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(call_CXXFLAGS) $(CXXFLAGS) -MT call-console.o -MD -MP -MF "$(DEPDIR)/call-console.Tpo" -c -o call-console.o `test -f 'console.cc' || echo '$(srcdir)/'`console.cc; \
+@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/call-console.Tpo" "$(DEPDIR)/call-console.Po"; else rm -f "$(DEPDIR)/call-console.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='console.cc' object='call-console.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(call_CXXFLAGS) $(CXXFLAGS) -c -o call-console.o `test -f 'console.cc' || echo '$(srcdir)/'`console.cc
+
+call-console.obj: console.cc
+@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(call_CXXFLAGS) $(CXXFLAGS) -MT call-console.obj -MD -MP -MF "$(DEPDIR)/call-console.Tpo" -c -o call-console.obj `if test -f 'console.cc'; then $(CYGPATH_W) 'console.cc'; else $(CYGPATH_W) '$(srcdir)/console.cc'; fi`; \
+@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/call-console.Tpo" "$(DEPDIR)/call-console.Po"; else rm -f "$(DEPDIR)/call-console.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='console.cc' object='call-console.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(call_CXXFLAGS) $(CXXFLAGS) -c -o call-console.obj `if test -f 'console.cc'; then $(CYGPATH_W) 'console.cc'; else $(CYGPATH_W) '$(srcdir)/console.cc'; fi`
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+distclean-libtool:
+ -rm -f libtool
+uninstall-info-am:
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ mkid -fID $$unique
+tags: TAGS
+
+TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ tags=; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$tags $$unique; \
+ fi
+ctags: CTAGS
+CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ tags=; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ test -z "$(CTAGS_ARGS)$$tags$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$tags $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && cd $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) $$here
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \
+ list='$(DISTFILES)'; for file in $$list; do \
+ case $$file in \
+ $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \
+ $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \
+ esac; \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test "$$dir" != "$$file" && test "$$dir" != "."; then \
+ dir="/$$dir"; \
+ $(mkdir_p) "$(distdir)$$dir"; \
+ else \
+ dir=''; \
+ fi; \
+ if test -d $$d/$$file; then \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \
+ fi; \
+ cp -pR $$d/$$file $(distdir)$$dir || exit 1; \
+ else \
+ test -f $(distdir)/$$file \
+ || cp -p $$d/$$file $(distdir)/$$file \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(PROGRAMS) $(HEADERS)
+installdirs:
+ for dir in "$(DESTDIR)$(bindir)"; do \
+ test -z "$$dir" || $(mkdir_p) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ `test -z '$(STRIP)' || \
+ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-binPROGRAMS clean-generic clean-libtool mostlyclean-am
+
+distclean: distclean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-libtool distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-exec-am: install-binPROGRAMS
+
+install-info: install-info-am
+
+install-man:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-binPROGRAMS uninstall-info-am
+
+.PHONY: CTAGS GTAGS all all-am check check-am clean clean-binPROGRAMS \
+ clean-generic clean-libtool ctags distclean distclean-compile \
+ distclean-generic distclean-libtool distclean-tags distdir dvi \
+ dvi-am html html-am info info-am install install-am \
+ install-binPROGRAMS install-data install-data-am install-exec \
+ install-exec-am install-info install-info-am install-man \
+ install-strip installcheck installcheck-am installdirs \
+ maintainer-clean maintainer-clean-generic mostlyclean \
+ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \
+ pdf pdf-am ps ps-am tags uninstall uninstall-am \
+ uninstall-binPROGRAMS uninstall-info-am
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/Plugins/jingle/libjingle/talk/examples/call/call.vcproj b/Plugins/jingle/libjingle/talk/examples/call/call.vcproj
new file mode 100644
index 0000000..3268f2c
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/examples/call/call.vcproj
@@ -0,0 +1,262 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="8,00"
+ Name="call"
+ ProjectGUID="{A4132D45-BAE2-40E5-AC7C-C3C44FB24325}"
+ RootNamespace="call"
+ Keyword="Win32Proj"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories="../../.."
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;_WINDOWS;UNICODE;_UNICODE;WINVER=0x0500;_WIN32_WINNT=0x500;PRODUCTION_BUILD;PRODUCTION;XML_STATIC;FEATURE_ENABLE_SSL;FEATURE_ENABLE_CHAT_ARCHIVING;FEATURE_ENABLE_VOICEMAIL"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="1"
+ TreatWChar_tAsBuiltInType="false"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="true"
+ DebugInformationFormat="4"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="comsupp.lib secur32.lib ws2_32.lib libexpatMT.lib Iphlpapi.lib crypt32.lib mediastreamer2.lib"
+ LinkIncremental="2"
+ AdditionalLibraryDirectories="C:\Desenvolvimento\libjingle-0.4.0\talk\third_party\Expat\StaticLibs;C:\Desenvolvimento\linphone\lin\linphone\mediastreamer2\build\win32native\Debug"
+ IgnoreAllDefaultLibraries="false"
+ IgnoreDefaultLibraryNames=""
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories="../../.."
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;_WINDOWS;UNICODE;_UNICODE;WINVER=0x0500;_WIN32_WINNT=0x500;PRODUCTION_BUILD;PRODUCTION;XML_STATIC;FEATURE_ENABLE_SSL;FEATURE_ENABLE_CHAT_ARCHIVING"
+ RuntimeLibrary="0"
+ TreatWChar_tAsBuiltInType="false"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="true"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="comsupp.lib secur32.lib ws2_32.lib libexpatMT.lib Iphlpapi.lib &quot;$(SolutionDir)\third_party\gips\Library\gipsvoiceenginelite.lib&quot;"
+ LinkIncremental="1"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Source Files"
+ Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
+ UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
+ >
+ <File
+ RelativePath=".\call_main.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\callclient.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\console.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\login\jingleinfotask.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\login\presenceouttask.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\login\presencepushtask.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\login\xmppauth.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\login\xmpppump.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\login\xmppsocket.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\login\xmppthread.cc"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Header Files"
+ Filter="h;hpp;hxx;hm;inl;inc;xsd"
+ UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
+ >
+ <File
+ RelativePath=".\callclient.h"
+ >
+ </File>
+ <File
+ RelativePath=".\console.h"
+ >
+ </File>
+ <File
+ RelativePath=".\presenceouttask.h"
+ >
+ </File>
+ <File
+ RelativePath=".\presencepushtask.h"
+ >
+ </File>
+ <File
+ RelativePath=".\status.h"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Resource Files"
+ Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"
+ UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
+ >
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/Plugins/jingle/libjingle/talk/examples/call/call_main.cc b/Plugins/jingle/libjingle/talk/examples/call/call_main.cc
new file mode 100644
index 0000000..989e3a2
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/examples/call/call_main.cc
@@ -0,0 +1,256 @@
+/*
+ * Jingle call example
+ * Copyright 2004--2005, Google Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <time.h>
+#include <iomanip>
+#include "talk/base/logging.h"
+#include "talk/base/physicalsocketserver.h"
+#include "talk/base/ssladapter.h"
+#include "talk/xmpp/xmppclientsettings.h"
+#include "talk/examples/login/xmppthread.h"
+#include "talk/examples/login/xmppauth.h"
+#include "talk/examples/call/callclient.h"
+#include "talk/examples/call/console.h"
+
+#if defined(_MSC_VER) && (_MSC_VER < 1400)
+// The following are necessary to properly link when compiling STL without
+// /EHsc, otherwise known as C++ exceptions.
+void __cdecl std::_Throw(const std::exception &) {}
+std::_Prhand std::_Raise_handler = 0;
+#endif
+
+void SetConsoleEcho(bool on) {
+#ifdef WIN32
+ HANDLE hIn = GetStdHandle(STD_INPUT_HANDLE);
+ if ((hIn == INVALID_HANDLE_VALUE) || (hIn == NULL))
+ return;
+
+ DWORD mode;
+ if (!GetConsoleMode(hIn, &mode))
+ return;
+
+ if (on) {
+ mode = mode | ENABLE_ECHO_INPUT;
+ } else {
+ mode = mode & ~ENABLE_ECHO_INPUT;
+ }
+
+ SetConsoleMode(hIn, mode);
+#else
+ if (on)
+ system("stty echo");
+ else
+ system("stty -echo");
+#endif
+}
+class DebugLog : public sigslot::has_slots<> {
+public:
+ DebugLog() :
+ debug_input_buf_(NULL), debug_input_len_(0), debug_input_alloc_(0),
+ debug_output_buf_(NULL), debug_output_len_(0), debug_output_alloc_(0),
+ censor_password_(false)
+ {}
+ char * debug_input_buf_;
+ int debug_input_len_;
+ int debug_input_alloc_;
+ char * debug_output_buf_;
+ int debug_output_len_;
+ int debug_output_alloc_;
+ bool censor_password_;
+
+ void Input(const char * data, int len) {
+ if (debug_input_len_ + len > debug_input_alloc_) {
+ char * old_buf = debug_input_buf_;
+ debug_input_alloc_ = 4096;
+ while (debug_input_alloc_ < debug_input_len_ + len) {
+ debug_input_alloc_ *= 2;
+ }
+ debug_input_buf_ = new char[debug_input_alloc_];
+ memcpy(debug_input_buf_, old_buf, debug_input_len_);
+ delete[] old_buf;
+ }
+ memcpy(debug_input_buf_ + debug_input_len_, data, len);
+ debug_input_len_ += len;
+ DebugPrint(debug_input_buf_, &debug_input_len_, false);
+ }
+
+ void Output(const char * data, int len) {
+ if (debug_output_len_ + len > debug_output_alloc_) {
+ char * old_buf = debug_output_buf_;
+ debug_output_alloc_ = 4096;
+ while (debug_output_alloc_ < debug_output_len_ + len) {
+ debug_output_alloc_ *= 2;
+ }
+ debug_output_buf_ = new char[debug_output_alloc_];
+ memcpy(debug_output_buf_, old_buf, debug_output_len_);
+ delete[] old_buf;
+ }
+ memcpy(debug_output_buf_ + debug_output_len_, data, len);
+ debug_output_len_ += len;
+ DebugPrint(debug_output_buf_, &debug_output_len_, true);
+ }
+
+ static bool
+ IsAuthTag(const char * str, size_t len) {
+ if (str[0] == '<' && str[1] == 'a' &&
+ str[2] == 'u' &&
+ str[3] == 't' &&
+ str[4] == 'h' &&
+ str[5] <= ' ') {
+ std::string tag(str, len);
+
+ if (tag.find("mechanism") != std::string::npos)
+ return true;
+
+ }
+ return false;
+ }
+
+ void
+ DebugPrint(char * buf, int * plen, bool output) {
+ int len = *plen;
+ if (len > 0) {
+ time_t tim = time(NULL);
+ struct tm * now = localtime(&tim);
+ char *time_string = asctime(now);
+ if (time_string) {
+ size_t time_len = strlen(time_string);
+ if (time_len > 0) {
+ time_string[time_len-1] = 0; // trim off terminating \n
+ }
+ }
+ LOG(INFO) << (output ? "SEND >>>>>>>>>>>>>>>>>>>>>>>>>" : "RECV <<<<<<<<<<<<<<<<<<<<<<<<<")
+ << " : " << time_string;
+
+ bool indent;
+ int start = 0, nest = 3;
+ for (int i = 0; i < len; i += 1) {
+ if (buf[i] == '>') {
+ if ((i > 0) && (buf[i-1] == '/')) {
+ indent = false;
+ } else if ((start + 1 < len) && (buf[start + 1] == '/')) {
+ indent = false;
+ nest -= 2;
+ } else {
+ indent = true;
+ }
+
+ // Output a tag
+ LOG(INFO) << std::setw(nest) << " " << std::string(buf + start, i + 1 - start);
+
+ if (indent)
+ nest += 2;
+
+ // Note if it's a PLAIN auth tag
+ if (IsAuthTag(buf + start, i + 1 - start)) {
+ censor_password_ = true;
+ }
+
+ // incr
+ start = i + 1;
+ }
+
+ if (buf[i] == '<' && start < i) {
+ if (censor_password_) {
+ LOG(INFO) << std::setw(nest) << " " << "## TEXT REMOVED ##";
+ censor_password_ = false;
+ }
+ else {
+ LOG(INFO) << std::setw(nest) << " " << std::string(buf + start, i - start);
+ }
+ start = i;
+ }
+ }
+ len = len - start;
+ memcpy(buf, buf + start, len);
+ *plen = len;
+ }
+ }
+
+};
+
+static DebugLog debug_log_;
+
+
+int main(int argc, char **argv) {
+ // This app has three threads. The main thread will run the XMPP client,
+ // which will print to the screen in its own thread. A second thread
+ // will get input from the console, parse it, and pass the appropriate
+ // message back to the XMPP client's thread. A third thread is used
+ // by PhoneSessionClient as its worker thread.
+
+ bool debug = false;
+ if (argc > 1 && !strcmp(argv[1], "-d"))
+ debug = true;
+
+ if (debug)
+ talk_base::LogMessage::LogToDebug(talk_base::LS_VERBOSE);
+
+
+ talk_base::InitializeSSL();
+ XmppPump pump;
+ buzz::Jid jid;
+ buzz::XmppClientSettings xcs;
+ talk_base::InsecureCryptStringImpl pass;
+ std::string username;
+
+ std::cout << "JID: ";
+ std::cin >> username;
+ jid = buzz::Jid(username);
+ if (!jid.IsValid() || jid.node() == "") {
+ printf("Invalid JID. JIDs should be in the form user@domain\n");
+ return 1;
+ }
+ SetConsoleEcho(false);
+ std::cout << "Password: ";
+ std::cin >> pass.password();
+ SetConsoleEcho(true);
+ std::cout << std::endl;
+
+ xcs.set_user(jid.node());
+ xcs.set_resource("call");
+ xcs.set_host(jid.domain());
+ xcs.set_use_tls(true);
+
+ xcs.set_pass(talk_base::CryptString(pass));
+ xcs.set_server(talk_base::SocketAddress("talk.google.com", 5222));
+ printf("Logging in as %s\n", jid.Str().c_str());
+
+ talk_base::PhysicalSocketServer ss;
+
+ CallClient *client = new CallClient(pump.client());
+
+ talk_base::Thread main_thread(&ss);
+ talk_base::ThreadManager::SetCurrent(&main_thread);
+ Console *console = new Console(&main_thread, client);
+ client->SetConsole(console);
+ talk_base::Thread *console_thread = new talk_base::Thread(&ss);
+ console_thread->Start();
+ console_thread->Post(console, MSG_START);
+
+ if (debug) {
+ pump.client()->SignalLogInput.connect(&debug_log_, &DebugLog::Input);
+ pump.client()->SignalLogOutput.connect(&debug_log_, &DebugLog::Output);
+ }
+
+ pump.DoLogin(xcs, new XmppSocket(true), NULL);
+ main_thread.Run();
+
+ return 0;
+}
diff --git a/Plugins/jingle/libjingle/talk/examples/call/callclient.cc b/Plugins/jingle/libjingle/talk/examples/call/callclient.cc
new file mode 100644
index 0000000..38b535b
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/examples/call/callclient.cc
@@ -0,0 +1,364 @@
+/*
+ * Jingle call example
+ * Copyright 2004--2005, Google Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <string>
+#include <vector>
+
+#include "talk/xmpp/constants.h"
+#include "talk/base/helpers.h"
+#include "talk/base/thread.h"
+#include "talk/base/network.h"
+#include "talk/base/socketaddress.h"
+#include "talk/p2p/base/sessionmanager.h"
+#include "talk/p2p/client/httpportallocator.h"
+#include "talk/p2p/client/sessionmanagertask.h"
+#include "talk/session/phone/phonesessionclient.h"
+#include "talk/examples/call/callclient.h"
+#include "talk/examples/call/console.h"
+#include "talk/examples/login/presencepushtask.h"
+#include "talk/examples/login/presenceouttask.h"
+#include "talk/examples/login/jingleinfotask.h"
+
+namespace {
+
+const char* DescribeStatus(buzz::Status::Show show, const std::string& desc) {
+ switch (show) {
+ case buzz::Status::SHOW_XA: return desc.c_str();
+ case buzz::Status::SHOW_ONLINE: return "online";
+ case buzz::Status::SHOW_AWAY: return "away";
+ case buzz::Status::SHOW_DND: return "do not disturb";
+ case buzz::Status::SHOW_CHAT: return "ready to chat";
+ default: return "offline";
+ }
+}
+
+} // namespace
+
+const char* CALL_COMMANDS =
+"Available commands:\n"
+"\n"
+" hangup Ends the call.\n"
+" mute Stops sending voice.\n"
+" unmute Re-starts sending voice.\n"
+" quit Quits the application.\n"
+"";
+
+const char* RECEIVE_COMMANDS =
+"Available commands:\n"
+"\n"
+" accept Accepts the incoming call and switches to it.\n"
+" reject Rejects the incoming call and stays with the current call.\n"
+" quit Quits the application.\n"
+"";
+
+const char* CONSOLE_COMMANDS =
+"Available commands:\n"
+"\n"
+" roster Prints the online friends from your roster.\n"
+" call <name> Initiates a call to the friend with the given name.\n"
+" quit Quits the application.\n"
+"";
+
+void CallClient::ParseLine(const std::string& line) {
+ std::vector<std::string> words;
+ int start = -1;
+ int state = 0;
+ for (int index = 0; index <= static_cast<int>(line.size()); ++index) {
+ if (state == 0) {
+ if (!isspace(line[index])) {
+ start = index;
+ state = 1;
+ }
+ } else {
+ assert(state == 1);
+ assert(start >= 0);
+ if (isspace(line[index])) {
+ std::string word(line, start, index - start);
+ words.push_back(word);
+ start = -1;
+ state = 0;
+ }
+ }
+ }
+
+ // Global commands
+ if ((words.size() == 1) && (words[0] == "quit")) {
+ exit(0);
+ }
+
+ if (call_ && incoming_call_) {
+ if ((words.size() == 1) && (words[0] == "accept")) {
+ assert(call_->sessions().size() == 1);
+ call_->AcceptSession(call_->sessions()[0]);
+ phone_client()->SetFocus(call_);
+ incoming_call_ = false;
+ } else if ((words.size() == 1) && (words[0] == "reject")) {
+ call_->RejectSession(call_->sessions()[0]);
+ incoming_call_ = false;
+ } else {
+ console_->Print(RECEIVE_COMMANDS);
+ }
+ } else if (call_) {
+ if ((words.size() == 1) && (words[0] == "hangup")) {
+ call_->Terminate();
+ call_ = NULL;
+ session_ = NULL;
+ console_->SetPrompt(NULL);
+ } else if ((words.size() == 1) && (words[0] == "mute")) {
+ call_->Mute(true);
+ } else if ((words.size() == 1) && (words[0] == "unmute")) {
+ call_->Mute(false);
+ } else {
+ console_->Print(CALL_COMMANDS);
+ }
+ } else {
+ if ((words.size() == 1) && (words[0] == "roster")) {
+ PrintRoster();
+ } else if ((words.size() == 2) && (words[0] == "call")) {
+ MakeCallTo(words[1]);
+ } else {
+ console_->Print(CONSOLE_COMMANDS);
+ }
+ }
+}
+
+CallClient::CallClient(buzz::XmppClient* xmpp_client)
+ : xmpp_client_(xmpp_client), roster_(new RosterMap), call_(NULL),
+ incoming_call_(false) {
+ xmpp_client_->SignalStateChange.connect(this, &CallClient::OnStateChange);
+}
+
+CallClient::~CallClient() {
+ delete roster_;
+}
+
+const std::string CallClient::strerror(buzz::XmppEngine::Error err) {
+ switch (err) {
+ case buzz::XmppEngine::ERROR_NONE:
+ return "";
+ case buzz::XmppEngine::ERROR_XML:
+ return "Malformed XML or encoding error";
+ case buzz::XmppEngine::ERROR_STREAM:
+ return "XMPP stream error";
+ case buzz::XmppEngine::ERROR_VERSION:
+ return "XMPP version error";
+ case buzz::XmppEngine::ERROR_UNAUTHORIZED:
+ return "User is not authorized (Check your username and password)";
+ case buzz::XmppEngine::ERROR_TLS:
+ return "TLS could not be negotiated";
+ case buzz::XmppEngine::ERROR_AUTH:
+ return "Authentication could not be negotiated";
+ case buzz::XmppEngine::ERROR_BIND:
+ return "Resource or session binding could not be negotiated";
+ case buzz::XmppEngine::ERROR_CONNECTION_CLOSED:
+ return "Connection closed by output handler.";
+ case buzz::XmppEngine::ERROR_DOCUMENT_CLOSED:
+ return "Closed by </stream:stream>";
+ case buzz::XmppEngine::ERROR_SOCKET:
+ return "Socket error";
+ default:
+ return "Unknown error";
+ }
+}
+
+void CallClient::OnCallDestroy(cricket::Call* call) {
+ if (call == call_) {
+ console_->SetPrompt(NULL);
+ console_->Print("call destroyed");
+ call_ = NULL;
+ session_ = NULL;
+ }
+}
+
+void CallClient::OnJingleInfo(const std::string &relay_token,
+ const std::vector<std::string> &relay_addresses,
+ const std::vector<talk_base::SocketAddress> &stun_addresses) {
+ port_allocator_->SetStunHosts(stun_addresses);
+ port_allocator_->SetRelayHosts(relay_addresses);
+ port_allocator_->SetRelayToken(relay_token);
+}
+
+void CallClient::OnStateChange(buzz::XmppEngine::State state) {
+ switch (state) {
+ case buzz::XmppEngine::STATE_START:
+ console_->Print("connecting...");
+ break;
+
+ case buzz::XmppEngine::STATE_OPENING:
+ console_->Print("logging in...");
+ break;
+
+ case buzz::XmppEngine::STATE_OPEN:
+ console_->Print("logged in...");
+ InitPhone();
+ InitPresence();
+ break;
+
+ case buzz::XmppEngine::STATE_CLOSED:
+ buzz::XmppEngine::Error error = xmpp_client_->GetError(NULL);
+ console_->Print("logged out..." + strerror(error));
+ exit(0);
+ }
+}
+
+void CallClient::InitPhone() {
+ std::string client_unique = xmpp_client_->jid().Str();
+ cricket::InitRandom(client_unique.c_str(), client_unique.size());
+
+ worker_thread_ = new talk_base::Thread();
+
+ port_allocator_ = new cricket::HttpPortAllocator(&network_manager_, "call");
+
+ session_manager_ = new cricket::SessionManager(
+ port_allocator_, worker_thread_);
+ session_manager_->SignalRequestSignaling.connect(
+ this, &CallClient::OnRequestSignaling);
+ session_manager_->OnSignalingReady();
+
+ session_manager_task_ =
+ new cricket::SessionManagerTask(xmpp_client_, session_manager_);
+ session_manager_task_->EnableOutgoingMessages();
+ session_manager_task_->Start();
+
+ buzz::JingleInfoTask *jit = new buzz::JingleInfoTask(xmpp_client_);
+ jit->RefreshJingleInfoNow();
+ jit->SignalJingleInfo.connect(this, &CallClient::OnJingleInfo);
+ jit->Start();
+
+ phone_client_ = new cricket::PhoneSessionClient(
+ xmpp_client_->jid(),session_manager_);
+ phone_client_->SignalCallCreate.connect(this, &CallClient::OnCallCreate);
+
+ worker_thread_->Start();
+}
+
+void CallClient::OnRequestSignaling() {
+ session_manager_->OnSignalingReady();
+}
+
+void CallClient::OnCallCreate(cricket::Call* call) {
+ call->SignalSessionState.connect(this, &CallClient::OnSessionState);
+}
+
+void CallClient::OnSessionState(cricket::Call* call,
+ cricket::Session* session,
+ cricket::Session::State state) {
+ if (state == cricket::Session::STATE_RECEIVEDINITIATE) {
+ buzz::Jid jid(session->remote_name());
+ console_->Printf("Incoming call from '%s'", jid.Str().c_str());
+ call_ = call;
+ session_ = session;
+ incoming_call_ = true;
+ } else if (state == cricket::Session::STATE_SENTINITIATE) {
+ console_->Print("calling...");
+ } else if (state == cricket::Session::STATE_RECEIVEDACCEPT) {
+ console_->Print("call answered");
+ } else if (state == cricket::Session::STATE_RECEIVEDREJECT) {
+ console_->Print("call not answered");
+ } else if (state == cricket::Session::STATE_INPROGRESS) {
+ console_->Print("call in progress");
+ } else if (state == cricket::Session::STATE_RECEIVEDTERMINATE) {
+ console_->Print("other side hung up");
+ }
+ }
+
+void CallClient::InitPresence() {
+ presence_push_ = new buzz::PresencePushTask(xmpp_client_);
+ presence_push_->SignalStatusUpdate.connect(
+ this, &CallClient::OnStatusUpdate);
+ presence_push_->Start();
+
+ buzz::Status my_status;
+ my_status.set_jid(xmpp_client_->jid());
+ my_status.set_available(true);
+ my_status.set_show(buzz::Status::SHOW_ONLINE);
+ my_status.set_priority(0);
+ my_status.set_know_capabilities(true);
+ my_status.set_phone_capability(true);
+ my_status.set_is_google_client(true);
+ my_status.set_version("1.0.0.66");
+
+ buzz::PresenceOutTask* presence_out_ =
+ new buzz::PresenceOutTask(xmpp_client_);
+ presence_out_->Send(my_status);
+ presence_out_->Start();
+}
+
+void CallClient::OnStatusUpdate(const buzz::Status& status) {
+ RosterItem item;
+ item.jid = status.jid();
+ item.show = status.show();
+ item.status = status.status();
+
+ std::string key = item.jid.Str();
+
+ if (status.available() && status.phone_capability()) {
+ console_->Printf("Adding to roster: %s", key.c_str());
+ (*roster_)[key] = item;
+ } else {
+ console_->Printf("Removing from roster: %s", key.c_str());
+ RosterMap::iterator iter = roster_->find(key);
+ if (iter != roster_->end())
+ roster_->erase(iter);
+ }
+}
+
+void CallClient::PrintRoster() {
+ console_->SetPrompting(false);
+ console_->Printf("Roster contains %d callable", roster_->size());
+ RosterMap::iterator iter = roster_->begin();
+ while (iter != roster_->end()) {
+ console_->Printf("%s - %s",
+ iter->second.jid.BareJid().Str().c_str(),
+ DescribeStatus(iter->second.show, iter->second.status));
+ iter++;
+ }
+ console_->SetPrompting(true);
+}
+
+void CallClient::MakeCallTo(const std::string& name) {
+ bool found = false;
+ buzz::Jid found_jid;
+ buzz::Jid callto_jid = buzz::Jid(name);
+ RosterMap::iterator iter = roster_->begin();
+ while (iter != roster_->end()) {
+ if (iter->second.jid.BareEquals(callto_jid)) {
+ found = true;
+ found_jid = iter->second.jid;
+ break;
+ }
+ ++iter;
+ }
+
+
+ if (found) {
+ console_->Printf("Found online friend '%s'", found_jid.Str().c_str());
+ phone_client()->SignalCallDestroy.connect(
+ this, &CallClient::OnCallDestroy);
+ if (!call_) {
+ call_ = phone_client()->CreateCall();
+ console_->SetPrompt(found_jid.Str().c_str());
+ call_->SignalSessionState.connect(this, &CallClient::OnSessionState);
+ session_ = call_->InitiateSession(found_jid, NULL);
+ }
+ phone_client()->SetFocus(call_);
+ } else {
+ console_->Printf("Could not find online friend '%s'", name.c_str());
+ }
+}
diff --git a/Plugins/jingle/libjingle/talk/examples/call/callclient.h b/Plugins/jingle/libjingle/talk/examples/call/callclient.h
new file mode 100644
index 0000000..1138f52
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/examples/call/callclient.h
@@ -0,0 +1,105 @@
+/*
+ * Jingle call example
+ * Copyright 2004--2005, Google Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef CRICKET_EXAMPLES_CALL_CALLCLIENT_H__
+#define CRICKET_EXAMPLES_CALL_CALLCLIENT_H__
+
+#include <map>
+#include <string>
+#include "talk/base/autodetectproxy.h"
+#include "talk/p2p/base/session.h"
+#include "talk/p2p/client/httpportallocator.h"
+#include "talk/xmpp/xmppclient.h"
+#include "talk/examples/login/status.h"
+#include "talk/examples/call/console.h"
+
+namespace buzz {
+class PresencePushTask;
+class Status;
+}
+
+namespace talk_base {
+class Thread;
+class NetworkManager;
+}
+
+namespace cricket {
+class PortAllocator;
+class PhoneSessionClient;
+class Receiver;
+class Call;
+class SessionManagerTask;
+}
+
+struct RosterItem {
+ buzz::Jid jid;
+ buzz::Status::Show show;
+ std::string status;
+};
+
+class CallClient: public sigslot::has_slots<> {
+public:
+ CallClient(buzz::XmppClient* xmpp_client);
+ ~CallClient();
+
+ cricket::PhoneSessionClient* phone_client() const { return phone_client_; }
+
+ void PrintRoster();
+ void MakeCallTo(const std::string& name);
+ void SetConsole(Console *console) {console_ = console;}
+ void ParseLine(const std::string &str);
+
+private:
+ typedef std::map<std::string,RosterItem> RosterMap;
+
+ Console *console_;
+ buzz::XmppClient* xmpp_client_;
+ talk_base::Thread* worker_thread_;
+ talk_base::NetworkManager network_manager_;
+ talk_base::AutoDetectProxy *proxy_detect_;
+ cricket::HttpPortAllocator* port_allocator_;
+ cricket::SessionManager* session_manager_;
+ cricket::SessionManagerTask* session_manager_task_;
+ cricket::PhoneSessionClient* phone_client_;
+
+ cricket::Call* call_;
+ cricket::Session *session_;
+ bool incoming_call_;
+
+ buzz::PresencePushTask* presence_push_;
+ RosterMap* roster_;
+
+ void OnStateChange(buzz::XmppEngine::State state);
+ void OnJingleInfo(const std::string &relay_token, const std::vector<std::string> &relay_hosts,
+ const std::vector<talk_base::SocketAddress> &stun_hosts);
+ void OnProxyDetect(talk_base::SignalThread *thread);
+ void InitPhone();
+ void OnRequestSignaling();
+ void OnCallCreate(cricket::Call* call);
+ void OnCallDestroy(cricket::Call* call);
+ const std::string strerror(buzz::XmppEngine::Error err);
+ void OnSessionState(cricket::Call* call,
+ cricket::Session* session,
+ cricket::Session::State state);
+
+ void InitPresence();
+ void OnStatusUpdate(const buzz::Status& status);
+};
+
+#endif // CRICKET_EXAMPLES_CALL_CALLCLIENT_H__
diff --git a/Plugins/jingle/libjingle/talk/examples/call/console.cc b/Plugins/jingle/libjingle/talk/examples/call/console.cc
new file mode 100644
index 0000000..d489fd8
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/examples/call/console.cc
@@ -0,0 +1,77 @@
+/*
+ * Jingle call example
+ * Copyright 2004--2005, Google Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#define _CRT_SECURE_NO_DEPRECATE 1
+
+#include <cassert>
+#include "talk/base/messagequeue.h"
+#include "talk/base/stringutils.h"
+#include "talk/examples/call/console.h"
+#include "talk/examples/call/callclient.h"
+
+Console::Console(talk_base::Thread *thread, CallClient *client) :
+ client_thread_(thread), client_(client), prompt_(std::string("call")),
+ prompting_(true) {
+}
+
+void Console::StartConsole() {
+ char input_buffer[64];
+ for (;;) {
+ fgets(input_buffer, sizeof(input_buffer), stdin);
+ client_thread_->Post(this, MSG_INPUT,
+ new talk_base::TypedMessageData<std::string>(input_buffer));
+ }
+}
+
+void Console::OnMessage(talk_base::Message *msg) {
+ switch (msg->message_id) {
+ case MSG_START:
+ StartConsole();
+ break;
+ case MSG_INPUT:
+ talk_base::TypedMessageData<std::string> *data =
+ static_cast<talk_base::TypedMessageData<std::string>*>(msg->pdata);
+ client_->ParseLine(data->data());
+ break;
+ }
+}
+
+void Console::Print(const char* str) {
+ printf("\n%s", str);
+ if (prompting_)
+ printf("\n(%s) ", prompt_.c_str());
+}
+
+void Console::Print(const std::string& str) {
+ Print(str.c_str());
+}
+
+void Console::Printf(const char* format, ...) {
+ va_list ap;
+ va_start(ap, format);
+
+ char buf[4096];
+ int size = vsnprintf(buf, sizeof(buf), format, ap);
+ assert(size >= 0);
+ assert(size < static_cast<int>(sizeof(buf)));
+ buf[size] = '\0';
+ Print(buf);
+
+ va_end(ap);
+} \ No newline at end of file
diff --git a/Plugins/jingle/libjingle/talk/examples/call/console.h b/Plugins/jingle/libjingle/talk/examples/call/console.h
new file mode 100644
index 0000000..264251a
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/examples/call/console.h
@@ -0,0 +1,60 @@
+/*
+ * Jingle call example
+ * Copyright 2004--2005, Google Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef CRICKET_EXAMPLES_CALL_CONSOLE_H__
+#define CRICKET_EXAMPLES_CALL_CONSOLE_H__
+
+#include "talk/base/thread.h"
+#include "talk/base/messagequeue.h"
+
+class CallClient;
+
+enum {
+ MSG_START,
+ MSG_INPUT,
+};
+
+class Console : public talk_base::MessageHandler {
+ public:
+ Console(talk_base::Thread *thread, CallClient *client);
+ virtual void OnMessage(talk_base::Message *msg);
+ void SetPrompt(const char *prompt) {
+ prompt_ = prompt ? std::string(prompt) : std::string("call");
+ }
+ void SetPrompting(bool prompting) {
+ prompting_ = prompting;
+ if (prompting)
+ printf("\n(%s) ", prompt_.c_str());
+ }
+ bool prompting() {return prompting_;}
+
+ void Print(const char* str);
+ void Print(const std::string& str);
+ void Printf(const char* format, ...);
+ private:
+ CallClient *client_;
+ talk_base::Thread *client_thread_;
+ void StartConsole();
+ void ParseLine(std::string &str);
+ std::string prompt_;
+ bool prompting_;
+};
+
+#endif // CRICKET_EXAMPLES_CALL_CONSOLE_H__
+
diff --git a/Plugins/jingle/libjingle/talk/examples/login/Makefile.am b/Plugins/jingle/libjingle/talk/examples/login/Makefile.am
new file mode 100644
index 0000000..7bb7804
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/examples/login/Makefile.am
@@ -0,0 +1,26 @@
+noinst_LTLIBRARIES= libcricketexampleslogin.la
+libcricketexampleslogin_la_SOURCES = xmppsocket.cc \
+ xmppauth.cc \
+ xmppthread.cc \
+ xmpppump.cc \
+ jingleinfotask.cc \
+ presenceouttask.cc \
+ presencepushtask.cc
+
+noinst_HEADERS = xmppauth.h \
+ xmpppump.h \
+ xmppsocket.h \
+ xmppthread.h \
+ jingleinfotask.h \
+ presenceouttask.h \
+ presencepushtask.h \
+ status.h
+
+bin_PROGRAMS = login
+login_CXXFLAGS = $(AM_CXXFLAGS)
+login_SOURCES = login_main.cc xmppsocket.cc xmppthread.cc xmpppump.cc xmppauth.cc
+login_LDADD = $(top_srcdir)/talk/xmpp/libcricketxmpp.la \
+ $(top_srcdir)/talk/xmllite/libcricketxmllite.la \
+ $(top_srcdir)/talk/base/libcricketbase.la \
+ $(EXPAT_LIBS) -lpthread -lssl -lcrypto
+AM_CPPFLAGS = -DPOSIX
diff --git a/Plugins/jingle/libjingle/talk/examples/login/Makefile.in b/Plugins/jingle/libjingle/talk/examples/login/Makefile.in
new file mode 100644
index 0000000..3d70be5
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/examples/login/Makefile.in
@@ -0,0 +1,600 @@
+# Makefile.in generated by automake 1.9.6 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005 Free Software Foundation, Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+top_builddir = ../../..
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+INSTALL = @INSTALL@
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+target_triplet = @target@
+bin_PROGRAMS = login$(EXEEXT)
+subdir = talk/examples/login
+DIST_COMMON = $(noinst_HEADERS) $(srcdir)/Makefile.am \
+ $(srcdir)/Makefile.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/talk/pkg.m4 \
+ $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+LTLIBRARIES = $(noinst_LTLIBRARIES)
+libcricketexampleslogin_la_LIBADD =
+am_libcricketexampleslogin_la_OBJECTS = xmppsocket.lo xmppauth.lo \
+ xmppthread.lo xmpppump.lo jingleinfotask.lo presenceouttask.lo \
+ presencepushtask.lo
+libcricketexampleslogin_la_OBJECTS = \
+ $(am_libcricketexampleslogin_la_OBJECTS)
+am__installdirs = "$(DESTDIR)$(bindir)"
+binPROGRAMS_INSTALL = $(INSTALL_PROGRAM)
+PROGRAMS = $(bin_PROGRAMS)
+am_login_OBJECTS = login-login_main.$(OBJEXT) \
+ login-xmppsocket.$(OBJEXT) login-xmppthread.$(OBJEXT) \
+ login-xmpppump.$(OBJEXT) login-xmppauth.$(OBJEXT)
+login_OBJECTS = $(am_login_OBJECTS)
+am__DEPENDENCIES_1 =
+login_DEPENDENCIES = $(top_srcdir)/talk/xmpp/libcricketxmpp.la \
+ $(top_srcdir)/talk/xmllite/libcricketxmllite.la \
+ $(top_srcdir)/talk/base/libcricketbase.la \
+ $(am__DEPENDENCIES_1)
+DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+ $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS)
+LTCXXCOMPILE = $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CXXFLAGS) $(CXXFLAGS)
+CXXLD = $(CXX)
+CXXLINK = $(LIBTOOL) --tag=CXX --mode=link $(CXXLD) $(AM_CXXFLAGS) \
+ $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
+SOURCES = $(libcricketexampleslogin_la_SOURCES) $(login_SOURCES)
+DIST_SOURCES = $(libcricketexampleslogin_la_SOURCES) $(login_SOURCES)
+HEADERS = $(noinst_HEADERS)
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+ALSA_LIBS = @ALSA_LIBS@
+AMDEP_FALSE = @AMDEP_FALSE@
+AMDEP_TRUE = @AMDEP_TRUE@
+AMTAR = @AMTAR@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+ECHO = @ECHO@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+EXPAT_LIBS = @EXPAT_LIBS@
+F77 = @F77@
+FFLAGS = @FFLAGS@
+GIPS_FALSE = @GIPS_FALSE@
+GIPS_TRUE = @GIPS_TRUE@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_LIBS = @GLIB_LIBS@
+ILBC_CFLAGS = @ILBC_CFLAGS@
+ILBC_LIBS = @ILBC_LIBS@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LDFLAGS = @LDFLAGS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+MAKEINFO = @MAKEINFO@
+MEDIA_LIBS = @MEDIA_LIBS@
+OBJEXT = @OBJEXT@
+ORTP_CFLAGS = @ORTP_CFLAGS@
+ORTP_LIBS = @ORTP_LIBS@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PHONE_FALSE = @PHONE_FALSE@
+PHONE_TRUE = @PHONE_TRUE@
+PKG_CONFIG = @PKG_CONFIG@
+RANLIB = @RANLIB@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SPEEX_CFLAGS = @SPEEX_CFLAGS@
+SPEEX_LIBS = @SPEEX_LIBS@
+STRIP = @STRIP@
+VERSION = @VERSION@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_F77 = @ac_ct_F77@
+ac_ct_RANLIB = @ac_ct_RANLIB@
+ac_ct_STRIP = @ac_ct_STRIP@
+am__fastdepCC_FALSE = @am__fastdepCC_FALSE@
+am__fastdepCC_TRUE = @am__fastdepCC_TRUE@
+am__fastdepCXX_FALSE = @am__fastdepCXX_FALSE@
+am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+datadir = @datadir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+sysconfdir = @sysconfdir@
+target = @target@
+target_alias = @target_alias@
+target_cpu = @target_cpu@
+target_os = @target_os@
+target_vendor = @target_vendor@
+noinst_LTLIBRARIES = libcricketexampleslogin.la
+libcricketexampleslogin_la_SOURCES = xmppsocket.cc \
+ xmppauth.cc \
+ xmppthread.cc \
+ xmpppump.cc \
+ jingleinfotask.cc \
+ presenceouttask.cc \
+ presencepushtask.cc
+
+noinst_HEADERS = xmppauth.h \
+ xmpppump.h \
+ xmppsocket.h \
+ xmppthread.h \
+ jingleinfotask.h \
+ presenceouttask.h \
+ presencepushtask.h \
+ status.h
+
+login_CXXFLAGS = $(AM_CXXFLAGS)
+login_SOURCES = login_main.cc xmppsocket.cc xmppthread.cc xmpppump.cc xmppauth.cc
+login_LDADD = $(top_srcdir)/talk/xmpp/libcricketxmpp.la \
+ $(top_srcdir)/talk/xmllite/libcricketxmllite.la \
+ $(top_srcdir)/talk/base/libcricketbase.la \
+ $(EXPAT_LIBS) -lpthread -lssl -lcrypto
+
+AM_CPPFLAGS = -DPOSIX
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .cc .lo .o .obj
+$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \
+ && exit 0; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu talk/examples/login/Makefile'; \
+ cd $(top_srcdir) && \
+ $(AUTOMAKE) --gnu talk/examples/login/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+clean-noinstLTLIBRARIES:
+ -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES)
+ @list='$(noinst_LTLIBRARIES)'; for p in $$list; do \
+ dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
+ test "$$dir" != "$$p" || dir=.; \
+ echo "rm -f \"$${dir}/so_locations\""; \
+ rm -f "$${dir}/so_locations"; \
+ done
+libcricketexampleslogin.la: $(libcricketexampleslogin_la_OBJECTS) $(libcricketexampleslogin_la_DEPENDENCIES)
+ $(CXXLINK) $(libcricketexampleslogin_la_LDFLAGS) $(libcricketexampleslogin_la_OBJECTS) $(libcricketexampleslogin_la_LIBADD) $(LIBS)
+install-binPROGRAMS: $(bin_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ test -z "$(bindir)" || $(mkdir_p) "$(DESTDIR)$(bindir)"
+ @list='$(bin_PROGRAMS)'; for p in $$list; do \
+ p1=`echo $$p|sed 's/$(EXEEXT)$$//'`; \
+ if test -f $$p \
+ || test -f $$p1 \
+ ; then \
+ f=`echo "$$p1" | sed 's,^.*/,,;$(transform);s/$$/$(EXEEXT)/'`; \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) --mode=install $(binPROGRAMS_INSTALL) '$$p' '$(DESTDIR)$(bindir)/$$f'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) --mode=install $(binPROGRAMS_INSTALL) "$$p" "$(DESTDIR)$(bindir)/$$f" || exit 1; \
+ else :; fi; \
+ done
+
+uninstall-binPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(bin_PROGRAMS)'; for p in $$list; do \
+ f=`echo "$$p" | sed 's,^.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/'`; \
+ echo " rm -f '$(DESTDIR)$(bindir)/$$f'"; \
+ rm -f "$(DESTDIR)$(bindir)/$$f"; \
+ done
+
+clean-binPROGRAMS:
+ @list='$(bin_PROGRAMS)'; for p in $$list; do \
+ f=`echo $$p|sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f $$p $$f"; \
+ rm -f $$p $$f ; \
+ done
+login$(EXEEXT): $(login_OBJECTS) $(login_DEPENDENCIES)
+ @rm -f login$(EXEEXT)
+ $(CXXLINK) $(login_LDFLAGS) $(login_OBJECTS) $(login_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/jingleinfotask.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/login-login_main.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/login-xmppauth.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/login-xmpppump.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/login-xmppsocket.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/login-xmppthread.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/presenceouttask.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/presencepushtask.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xmppauth.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xmpppump.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xmppsocket.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xmppthread.Plo@am__quote@
+
+.cc.o:
+@am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \
+@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ $<
+
+.cc.obj:
+@am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \
+@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.cc.lo:
+@am__fastdepCXX_TRUE@ if $(LTCXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \
+@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Plo"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(LTCXXCOMPILE) -c -o $@ $<
+
+login-login_main.o: login_main.cc
+@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(login_CXXFLAGS) $(CXXFLAGS) -MT login-login_main.o -MD -MP -MF "$(DEPDIR)/login-login_main.Tpo" -c -o login-login_main.o `test -f 'login_main.cc' || echo '$(srcdir)/'`login_main.cc; \
+@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/login-login_main.Tpo" "$(DEPDIR)/login-login_main.Po"; else rm -f "$(DEPDIR)/login-login_main.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='login_main.cc' object='login-login_main.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(login_CXXFLAGS) $(CXXFLAGS) -c -o login-login_main.o `test -f 'login_main.cc' || echo '$(srcdir)/'`login_main.cc
+
+login-login_main.obj: login_main.cc
+@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(login_CXXFLAGS) $(CXXFLAGS) -MT login-login_main.obj -MD -MP -MF "$(DEPDIR)/login-login_main.Tpo" -c -o login-login_main.obj `if test -f 'login_main.cc'; then $(CYGPATH_W) 'login_main.cc'; else $(CYGPATH_W) '$(srcdir)/login_main.cc'; fi`; \
+@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/login-login_main.Tpo" "$(DEPDIR)/login-login_main.Po"; else rm -f "$(DEPDIR)/login-login_main.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='login_main.cc' object='login-login_main.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(login_CXXFLAGS) $(CXXFLAGS) -c -o login-login_main.obj `if test -f 'login_main.cc'; then $(CYGPATH_W) 'login_main.cc'; else $(CYGPATH_W) '$(srcdir)/login_main.cc'; fi`
+
+login-xmppsocket.o: xmppsocket.cc
+@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(login_CXXFLAGS) $(CXXFLAGS) -MT login-xmppsocket.o -MD -MP -MF "$(DEPDIR)/login-xmppsocket.Tpo" -c -o login-xmppsocket.o `test -f 'xmppsocket.cc' || echo '$(srcdir)/'`xmppsocket.cc; \
+@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/login-xmppsocket.Tpo" "$(DEPDIR)/login-xmppsocket.Po"; else rm -f "$(DEPDIR)/login-xmppsocket.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='xmppsocket.cc' object='login-xmppsocket.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(login_CXXFLAGS) $(CXXFLAGS) -c -o login-xmppsocket.o `test -f 'xmppsocket.cc' || echo '$(srcdir)/'`xmppsocket.cc
+
+login-xmppsocket.obj: xmppsocket.cc
+@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(login_CXXFLAGS) $(CXXFLAGS) -MT login-xmppsocket.obj -MD -MP -MF "$(DEPDIR)/login-xmppsocket.Tpo" -c -o login-xmppsocket.obj `if test -f 'xmppsocket.cc'; then $(CYGPATH_W) 'xmppsocket.cc'; else $(CYGPATH_W) '$(srcdir)/xmppsocket.cc'; fi`; \
+@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/login-xmppsocket.Tpo" "$(DEPDIR)/login-xmppsocket.Po"; else rm -f "$(DEPDIR)/login-xmppsocket.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='xmppsocket.cc' object='login-xmppsocket.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(login_CXXFLAGS) $(CXXFLAGS) -c -o login-xmppsocket.obj `if test -f 'xmppsocket.cc'; then $(CYGPATH_W) 'xmppsocket.cc'; else $(CYGPATH_W) '$(srcdir)/xmppsocket.cc'; fi`
+
+login-xmppthread.o: xmppthread.cc
+@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(login_CXXFLAGS) $(CXXFLAGS) -MT login-xmppthread.o -MD -MP -MF "$(DEPDIR)/login-xmppthread.Tpo" -c -o login-xmppthread.o `test -f 'xmppthread.cc' || echo '$(srcdir)/'`xmppthread.cc; \
+@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/login-xmppthread.Tpo" "$(DEPDIR)/login-xmppthread.Po"; else rm -f "$(DEPDIR)/login-xmppthread.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='xmppthread.cc' object='login-xmppthread.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(login_CXXFLAGS) $(CXXFLAGS) -c -o login-xmppthread.o `test -f 'xmppthread.cc' || echo '$(srcdir)/'`xmppthread.cc
+
+login-xmppthread.obj: xmppthread.cc
+@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(login_CXXFLAGS) $(CXXFLAGS) -MT login-xmppthread.obj -MD -MP -MF "$(DEPDIR)/login-xmppthread.Tpo" -c -o login-xmppthread.obj `if test -f 'xmppthread.cc'; then $(CYGPATH_W) 'xmppthread.cc'; else $(CYGPATH_W) '$(srcdir)/xmppthread.cc'; fi`; \
+@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/login-xmppthread.Tpo" "$(DEPDIR)/login-xmppthread.Po"; else rm -f "$(DEPDIR)/login-xmppthread.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='xmppthread.cc' object='login-xmppthread.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(login_CXXFLAGS) $(CXXFLAGS) -c -o login-xmppthread.obj `if test -f 'xmppthread.cc'; then $(CYGPATH_W) 'xmppthread.cc'; else $(CYGPATH_W) '$(srcdir)/xmppthread.cc'; fi`
+
+login-xmpppump.o: xmpppump.cc
+@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(login_CXXFLAGS) $(CXXFLAGS) -MT login-xmpppump.o -MD -MP -MF "$(DEPDIR)/login-xmpppump.Tpo" -c -o login-xmpppump.o `test -f 'xmpppump.cc' || echo '$(srcdir)/'`xmpppump.cc; \
+@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/login-xmpppump.Tpo" "$(DEPDIR)/login-xmpppump.Po"; else rm -f "$(DEPDIR)/login-xmpppump.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='xmpppump.cc' object='login-xmpppump.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(login_CXXFLAGS) $(CXXFLAGS) -c -o login-xmpppump.o `test -f 'xmpppump.cc' || echo '$(srcdir)/'`xmpppump.cc
+
+login-xmpppump.obj: xmpppump.cc
+@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(login_CXXFLAGS) $(CXXFLAGS) -MT login-xmpppump.obj -MD -MP -MF "$(DEPDIR)/login-xmpppump.Tpo" -c -o login-xmpppump.obj `if test -f 'xmpppump.cc'; then $(CYGPATH_W) 'xmpppump.cc'; else $(CYGPATH_W) '$(srcdir)/xmpppump.cc'; fi`; \
+@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/login-xmpppump.Tpo" "$(DEPDIR)/login-xmpppump.Po"; else rm -f "$(DEPDIR)/login-xmpppump.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='xmpppump.cc' object='login-xmpppump.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(login_CXXFLAGS) $(CXXFLAGS) -c -o login-xmpppump.obj `if test -f 'xmpppump.cc'; then $(CYGPATH_W) 'xmpppump.cc'; else $(CYGPATH_W) '$(srcdir)/xmpppump.cc'; fi`
+
+login-xmppauth.o: xmppauth.cc
+@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(login_CXXFLAGS) $(CXXFLAGS) -MT login-xmppauth.o -MD -MP -MF "$(DEPDIR)/login-xmppauth.Tpo" -c -o login-xmppauth.o `test -f 'xmppauth.cc' || echo '$(srcdir)/'`xmppauth.cc; \
+@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/login-xmppauth.Tpo" "$(DEPDIR)/login-xmppauth.Po"; else rm -f "$(DEPDIR)/login-xmppauth.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='xmppauth.cc' object='login-xmppauth.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(login_CXXFLAGS) $(CXXFLAGS) -c -o login-xmppauth.o `test -f 'xmppauth.cc' || echo '$(srcdir)/'`xmppauth.cc
+
+login-xmppauth.obj: xmppauth.cc
+@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(login_CXXFLAGS) $(CXXFLAGS) -MT login-xmppauth.obj -MD -MP -MF "$(DEPDIR)/login-xmppauth.Tpo" -c -o login-xmppauth.obj `if test -f 'xmppauth.cc'; then $(CYGPATH_W) 'xmppauth.cc'; else $(CYGPATH_W) '$(srcdir)/xmppauth.cc'; fi`; \
+@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/login-xmppauth.Tpo" "$(DEPDIR)/login-xmppauth.Po"; else rm -f "$(DEPDIR)/login-xmppauth.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='xmppauth.cc' object='login-xmppauth.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(login_CXXFLAGS) $(CXXFLAGS) -c -o login-xmppauth.obj `if test -f 'xmppauth.cc'; then $(CYGPATH_W) 'xmppauth.cc'; else $(CYGPATH_W) '$(srcdir)/xmppauth.cc'; fi`
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+distclean-libtool:
+ -rm -f libtool
+uninstall-info-am:
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ mkid -fID $$unique
+tags: TAGS
+
+TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ tags=; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$tags $$unique; \
+ fi
+ctags: CTAGS
+CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ tags=; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ test -z "$(CTAGS_ARGS)$$tags$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$tags $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && cd $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) $$here
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \
+ list='$(DISTFILES)'; for file in $$list; do \
+ case $$file in \
+ $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \
+ $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \
+ esac; \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test "$$dir" != "$$file" && test "$$dir" != "."; then \
+ dir="/$$dir"; \
+ $(mkdir_p) "$(distdir)$$dir"; \
+ else \
+ dir=''; \
+ fi; \
+ if test -d $$d/$$file; then \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \
+ fi; \
+ cp -pR $$d/$$file $(distdir)$$dir || exit 1; \
+ else \
+ test -f $(distdir)/$$file \
+ || cp -p $$d/$$file $(distdir)/$$file \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(LTLIBRARIES) $(PROGRAMS) $(HEADERS)
+installdirs:
+ for dir in "$(DESTDIR)$(bindir)"; do \
+ test -z "$$dir" || $(mkdir_p) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ `test -z '$(STRIP)' || \
+ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-binPROGRAMS clean-generic clean-libtool \
+ clean-noinstLTLIBRARIES mostlyclean-am
+
+distclean: distclean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-libtool distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-exec-am: install-binPROGRAMS
+
+install-info: install-info-am
+
+install-man:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-binPROGRAMS uninstall-info-am
+
+.PHONY: CTAGS GTAGS all all-am check check-am clean clean-binPROGRAMS \
+ clean-generic clean-libtool clean-noinstLTLIBRARIES ctags \
+ distclean distclean-compile distclean-generic \
+ distclean-libtool distclean-tags distdir dvi dvi-am html \
+ html-am info info-am install install-am install-binPROGRAMS \
+ install-data install-data-am install-exec install-exec-am \
+ install-info install-info-am install-man install-strip \
+ installcheck installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags uninstall uninstall-am uninstall-binPROGRAMS \
+ uninstall-info-am
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/Plugins/jingle/libjingle/talk/examples/login/jingleinfotask.cc b/Plugins/jingle/libjingle/talk/examples/login/jingleinfotask.cc
new file mode 100644
index 0000000..4f75853
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/examples/login/jingleinfotask.cc
@@ -0,0 +1,112 @@
+#include "talk/examples/login/jingleinfotask.h"
+#include "talk/base/socketaddress.h"
+#include "talk/xmpp/constants.h"
+#include "talk/xmpp/xmppclient.h"
+
+
+namespace buzz {
+
+class JingleInfoTask::JingleInfoGetTask : public XmppTask {
+public:
+ JingleInfoGetTask(Task * parent) : XmppTask(parent, XmppEngine::HL_SINGLE),
+ done_(false) {}
+
+ virtual int ProcessStart() {
+ scoped_ptr<XmlElement> get(MakeIq(STR_GET, JID_EMPTY, task_id()));
+ get->AddElement(new XmlElement(QN_JINGLE_INFO_QUERY, true));
+ if (SendStanza(get.get()) != XMPP_RETURN_OK) {
+ return STATE_ERROR;
+ }
+ return STATE_RESPONSE;
+ }
+ virtual int ProcessResponse() {
+ if (done_)
+ return STATE_DONE;
+ return STATE_BLOCKED;
+ }
+
+protected:
+ virtual bool HandleStanza(const XmlElement * stanza) {
+ if (!MatchResponseIq(stanza, JID_EMPTY, task_id()))
+ return false;
+
+ if (stanza->Attr(QN_TYPE) != STR_RESULT)
+ return false;
+
+ // Queue the stanza with the parent so these don't get handled out of order
+ JingleInfoTask* parent = static_cast<JingleInfoTask*>(GetParent());
+ parent->QueueStanza(stanza);
+
+ // Wake ourselves so we can go into the done state
+ done_ = true;
+ Wake();
+ return true;
+ }
+
+ bool done_;
+};
+
+
+void JingleInfoTask::RefreshJingleInfoNow() {
+ JingleInfoGetTask* get_task = new JingleInfoGetTask(this);
+ get_task->Start();
+}
+
+bool
+JingleInfoTask::HandleStanza(const XmlElement * stanza) {
+ if (!MatchRequestIq(stanza, "set", QN_JINGLE_INFO_QUERY))
+ return false;
+
+ // only respect relay push from the server
+ Jid from(stanza->Attr(QN_FROM));
+ if (from != JID_EMPTY &&
+ !from.BareEquals(GetClient()->jid()) &&
+ from != Jid(GetClient()->jid().domain()))
+ return false;
+
+ QueueStanza(stanza);
+ return true;
+}
+
+int
+JingleInfoTask::ProcessStart() {
+ std::vector<std::string> relay_hosts;
+ std::vector<talk_base::SocketAddress> stun_hosts;
+ std::string relay_token;
+ const XmlElement * stanza = NextStanza();
+ if (stanza == NULL)
+ return STATE_BLOCKED;
+ const XmlElement * query = stanza->FirstNamed(QN_JINGLE_INFO_QUERY);
+ if (query == NULL)
+ return STATE_START;
+ const XmlElement *stun = query->FirstNamed(QN_JINGLE_INFO_STUN);
+ if (stun) {
+ for (const XmlElement *server = stun->FirstNamed(QN_JINGLE_INFO_SERVER);
+ server != NULL; server = server->NextNamed(QN_JINGLE_INFO_SERVER)) {
+ std::string host = server->Attr(QN_JINGLE_INFO_HOST);
+ std::string port = server->Attr(QN_JINGLE_INFO_UDP);
+ if (host != STR_EMPTY && host != STR_EMPTY)
+ stun_hosts.push_back(talk_base::SocketAddress(host, atoi(port.c_str())));
+ }
+ }
+
+ const XmlElement *relay = query->FirstNamed(QN_JINGLE_INFO_RELAY);
+ if (relay) {
+ relay_token = relay->TextNamed(QN_JINGLE_INFO_TOKEN);
+ for (const XmlElement *server = relay->FirstNamed(QN_JINGLE_INFO_SERVER);
+ server != NULL; server = server->NextNamed(QN_JINGLE_INFO_SERVER)) {
+ std::string host = server->Attr(QN_JINGLE_INFO_HOST);
+ if (host != STR_EMPTY) {
+ relay_hosts.push_back(host);
+ }
+ }
+ }
+ SignalJingleInfo(relay_token, relay_hosts, stun_hosts);
+ return STATE_START;
+}
+
+
+}
+
+
+
diff --git a/Plugins/jingle/libjingle/talk/examples/login/jingleinfotask.h b/Plugins/jingle/libjingle/talk/examples/login/jingleinfotask.h
new file mode 100644
index 0000000..be7073b
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/examples/login/jingleinfotask.h
@@ -0,0 +1,36 @@
+#ifndef _JINGLEINFOTASK_H_
+#define _JINGLEINFOTASK_H_
+
+#include "talk/p2p/client/httpportallocator.h"
+#include "talk/xmpp/xmppengine.h"
+#include "talk/xmpp/xmpptask.h"
+#include "talk/base/sigslot.h"
+#include "status.h"
+
+namespace buzz {
+
+class JingleInfoTask : public XmppTask {
+
+ public:
+ JingleInfoTask(Task * parent) :
+ XmppTask(parent, XmppEngine::HL_TYPE) {}
+
+ virtual int ProcessStart();
+ void RefreshJingleInfoNow();
+
+ sigslot::signal3<const std::string &,
+ const std::vector<std::string> &,
+ const std::vector<talk_base::SocketAddress> &> SignalJingleInfo;
+
+ protected:
+ class JingleInfoGetTask;
+ friend class JingleInfoGetTask;
+
+ virtual bool HandleStanza(const XmlElement * stanza);
+};
+
+
+}
+
+#endif
+
diff --git a/Plugins/jingle/libjingle/talk/examples/login/login_main.cc b/Plugins/jingle/libjingle/talk/examples/login/login_main.cc
new file mode 100644
index 0000000..186d6f7
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/examples/login/login_main.cc
@@ -0,0 +1,63 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "talk/base/thread.h"
+#include "talk/xmpp/xmppclientsettings.h"
+#include "talk/examples/login/xmppthread.h"
+#include <iostream>
+
+int main(int argc, char **argv) {
+ printf("Auth Cookie: ");
+ fflush(stdout);
+
+ char auth_cookie[256];
+ scanf("%s", auth_cookie);
+
+ char username[256];
+ scanf("%s", username);
+
+ // Start xmpp on a different thread
+ XmppThread thread;
+ thread.Start();
+
+ buzz::XmppClientSettings xcs;
+ xcs.set_user(username);
+ xcs.set_host("gmail.com");
+ xcs.set_use_tls(false);
+ xcs.set_auth_cookie(auth_cookie);
+ xcs.set_server(talk_base::SocketAddress("talk.google.com", 5222));
+ thread.Login(xcs);
+
+ // Use main thread for console input
+ std::string line;
+ while (std::getline(std::cin, line)) {
+ if (line == "quit")
+ break;
+ }
+ return 0;
+}
+
diff --git a/Plugins/jingle/libjingle/talk/examples/login/presenceouttask.cc b/Plugins/jingle/libjingle/talk/examples/login/presenceouttask.cc
new file mode 100644
index 0000000..1eaf516
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/examples/login/presenceouttask.cc
@@ -0,0 +1,133 @@
+/*
+ * Jingle call example
+ * Copyright 2004--2005, Google Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <time.h>
+#include <sstream>
+#include "talk/base/stringencode.h"
+#include "talk/examples/login/presenceouttask.h"
+#include "talk/xmpp/constants.h"
+#include "talk/xmpp/xmppclient.h"
+
+namespace buzz {
+
+XmppReturnStatus
+PresenceOutTask::Send(const Status & s) {
+ if (GetState() != STATE_INIT)
+ return XMPP_RETURN_BADSTATE;
+
+ stanza_.reset(TranslateStatus(s));
+ return XMPP_RETURN_OK;
+}
+
+XmppReturnStatus
+PresenceOutTask::SendDirected(const Jid & j, const Status & s) {
+ if (GetState() != STATE_INIT)
+ return XMPP_RETURN_BADSTATE;
+
+ XmlElement * presence = TranslateStatus(s);
+ presence->AddAttr(QN_TO, j.Str());
+ stanza_.reset(presence);
+ return XMPP_RETURN_OK;
+}
+
+XmppReturnStatus PresenceOutTask::SendProbe(const Jid & jid) {
+ if (GetState() != STATE_INIT)
+ return XMPP_RETURN_BADSTATE;
+
+ XmlElement * presence = new XmlElement(QN_PRESENCE);
+ presence->AddAttr(QN_TO, jid.Str());
+ presence->AddAttr(QN_TYPE, "probe");
+
+ stanza_.reset(presence);
+ return XMPP_RETURN_OK;
+}
+
+int
+PresenceOutTask::ProcessStart() {
+ if (SendStanza(stanza_.get()) != XMPP_RETURN_OK)
+ return STATE_ERROR;
+ return STATE_DONE;
+}
+
+XmlElement *
+PresenceOutTask::TranslateStatus(const Status & s) {
+ XmlElement * result = new XmlElement(QN_PRESENCE);
+ if (!s.available()) {
+ result->AddAttr(QN_TYPE, STR_UNAVAILABLE);
+ }
+ else {
+ if (s.show() != Status::SHOW_ONLINE && s.show() != Status::SHOW_OFFLINE) {
+ result->AddElement(new XmlElement(QN_SHOW));
+ switch (s.show()) {
+ default:
+ result->AddText(STR_SHOW_AWAY, 1);
+ break;
+ case Status::SHOW_XA:
+ result->AddText(STR_SHOW_XA, 1);
+ break;
+ case Status::SHOW_DND:
+ result->AddText(STR_SHOW_DND, 1);
+ break;
+ case Status::SHOW_CHAT:
+ result->AddText(STR_SHOW_CHAT, 1);
+ break;
+ }
+ }
+
+ result->AddElement(new XmlElement(QN_STATUS));
+ result->AddText(s.status(), 1);
+
+ std::string pri;
+ talk_base::ToString(s.priority(), &pri);
+
+ result->AddElement(new XmlElement(QN_PRIORITY));
+ result->AddText(pri, 1);
+
+ if (s.know_capabilities() && s.is_google_client()) {
+ std::string caps;
+ if (s.fileshare_capability())
+ caps += "share-v1";
+ if (s.phone_capability())
+ caps += " voice-v1";
+ result->AddElement(new XmlElement(QN_CAPS_C, true));
+ result->AddAttr(QN_NODE, GOOGLE_CLIENT_NODE, 1);
+ result->AddAttr(QN_VER, s.version(), 1);
+ result->AddAttr(QN_EXT, caps, 1);
+ }
+
+ // Put the delay mark on the presence according to JEP-0091
+ {
+ result->AddElement(new XmlElement(kQnDelayX, true));
+
+ // This here is why we *love* the C runtime
+ time_t current_time_seconds;
+ time(&current_time_seconds);
+ struct tm* current_time = gmtime(&current_time_seconds);
+ char output[256];
+ strftime(output, ARRAY_SIZE(output), "%Y%m%dT%H:%M:%S", current_time);
+ result->AddAttr(kQnStamp, output, 1);
+ }
+
+ }
+
+ return result;
+}
+
+
+}
diff --git a/Plugins/jingle/libjingle/talk/examples/login/presenceouttask.h b/Plugins/jingle/libjingle/talk/examples/login/presenceouttask.h
new file mode 100644
index 0000000..bd691f4
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/examples/login/presenceouttask.h
@@ -0,0 +1,46 @@
+/*
+ * Jingle call example
+ * Copyright 2004--2005, Google Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _PRESENCEOUTTASK_H_
+#define _PRESENCEOUTTASK_H_
+
+#include "talk/xmpp/xmppengine.h"
+#include "talk/xmpp/xmpptask.h"
+#include "talk/examples/login/status.h"
+
+namespace buzz {
+
+class PresenceOutTask : public XmppTask {
+public:
+ PresenceOutTask(Task * parent) : XmppTask(parent) {}
+ virtual ~PresenceOutTask() {}
+
+ XmppReturnStatus Send(const Status & s);
+ XmppReturnStatus SendDirected(const Jid & j, const Status & s);
+ XmppReturnStatus SendProbe(const Jid& jid);
+
+ virtual int ProcessStart();
+private:
+ XmlElement * TranslateStatus(const Status & s);
+ scoped_ptr<XmlElement> stanza_;
+};
+
+}
+
+#endif
diff --git a/Plugins/jingle/libjingle/talk/examples/login/presencepushtask.cc b/Plugins/jingle/libjingle/talk/examples/login/presencepushtask.cc
new file mode 100644
index 0000000..4895190
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/examples/login/presencepushtask.cc
@@ -0,0 +1,161 @@
+/*
+ * Jingle call example
+ * Copyright 2004--2005, Google Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "talk/base/stringencode.h"
+#include "talk/examples/login/presencepushtask.h"
+#include "talk/xmpp/constants.h"
+#include <sstream>
+
+
+namespace buzz {
+
+// string helper functions -----------------------------------------------------
+
+static bool
+IsXmlSpace(int ch) {
+ return ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t';
+}
+
+static bool
+ListContainsToken(const std::string & list, const std::string & token) {
+ size_t i = list.find(token);
+ if (i == std::string::npos || token.empty())
+ return false;
+ bool boundary_before = (i == 0 || IsXmlSpace(list[i - 1]));
+ bool boundary_after = (i == list.length() - token.length() || IsXmlSpace(list[i + token.length()]));
+ return boundary_before && boundary_after;
+}
+
+
+bool
+PresencePushTask::HandleStanza(const XmlElement * stanza) {
+ if (stanza->Name() != QN_PRESENCE)
+ return false;
+ if (stanza->HasAttr(QN_TYPE) && stanza->Attr(QN_TYPE) != STR_UNAVAILABLE)
+ return false;
+ QueueStanza(stanza);
+ return true;
+}
+
+static bool IsUtf8FirstByte(int c) {
+ return (((c)&0x80)==0) || // is single byte
+ ((unsigned char)((c)-0xc0)<0x3e); // or is lead byte
+}
+
+int
+PresencePushTask::ProcessStart() {
+ const XmlElement * stanza = NextStanza();
+ if (stanza == NULL)
+ return STATE_BLOCKED;
+ Status s;
+
+ s.set_jid(Jid(stanza->Attr(QN_FROM)));
+
+ if (stanza->Attr(QN_TYPE) == STR_UNAVAILABLE) {
+ s.set_available(false);
+ SignalStatusUpdate(s);
+ }
+ else {
+ s.set_available(true);
+ const XmlElement * status = stanza->FirstNamed(QN_STATUS);
+ if (status != NULL) {
+ s.set_status(status->BodyText());
+
+ // Truncate status messages longer than 300 bytes
+ if (s.status().length() > 300) {
+ size_t len = 300;
+
+ // Be careful not to split legal utf-8 chars in half
+ while (!IsUtf8FirstByte(s.status()[len]) && len > 0) {
+ len -= 1;
+ }
+ std::string truncated(s.status(), 0, len);
+ s.set_status(truncated);
+ }
+ }
+
+ const XmlElement * priority = stanza->FirstNamed(QN_PRIORITY);
+ if (priority != NULL) {
+ int pri;
+ if (talk_base::FromString(priority->BodyText(), &pri)) {
+ s.set_priority(pri);
+ }
+ }
+
+ const XmlElement * show = stanza->FirstNamed(QN_SHOW);
+ if (show == NULL || show->FirstChild() == NULL) {
+ s.set_show(Status::SHOW_ONLINE);
+ }
+ else {
+ if (show->BodyText() == "away") {
+ s.set_show(Status::SHOW_AWAY);
+ }
+ else if (show->BodyText() == "xa") {
+ s.set_show(Status::SHOW_XA);
+ }
+ else if (show->BodyText() == "dnd") {
+ s.set_show(Status::SHOW_DND);
+ }
+ else if (show->BodyText() == "chat") {
+ s.set_show(Status::SHOW_CHAT);
+ }
+ else {
+ s.set_show(Status::SHOW_ONLINE);
+ }
+ }
+
+ const XmlElement * caps = stanza->FirstNamed(QN_CAPS_C);
+ if (caps != NULL) {
+ std::string node = caps->Attr(QN_NODE);
+ std::string ver = caps->Attr(QN_VER);
+ std::string exts = caps->Attr(QN_EXT);
+
+ s.set_know_capabilities(true);
+
+ if (node == GOOGLE_CLIENT_NODE) {
+ s.set_is_google_client(true);
+ s.set_version(ver);
+ if (ListContainsToken(exts, "voice-v1")) {
+ s.set_phone_capability(true);
+ }
+ if (ListContainsToken(exts, "share-v1")) {
+ s.set_fileshare_capability(true);
+ }
+ }
+ }
+
+ const XmlElement* delay = stanza->FirstNamed(kQnDelayX);
+ if (delay != NULL) {
+ // Ideally we would parse this according to the Psuedo ISO-8601 rules
+ // that are laid out in JEP-0082:
+ // http://www.jabber.org/jeps/jep-0082.html
+ std::string stamp = delay->Attr(kQnStamp);
+ s.set_sent_time(stamp);
+ }
+
+ SignalStatusUpdate(s);
+ }
+
+ return STATE_START;
+}
+
+
+}
+
+
diff --git a/Plugins/jingle/libjingle/talk/examples/login/presencepushtask.h b/Plugins/jingle/libjingle/talk/examples/login/presencepushtask.h
new file mode 100644
index 0000000..23cd1b8
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/examples/login/presencepushtask.h
@@ -0,0 +1,44 @@
+/*
+ * Jingle call example
+ * Copyright 2004--2005, Google Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _PRESENCEPUSHTASK_H_
+#define _PRESENCEPUSHTASK_H_
+
+#include "talk/xmpp/xmppengine.h"
+#include "talk/xmpp/xmpptask.h"
+#include "talk/base/sigslot.h"
+#include "talk/examples/login/status.h"
+
+namespace buzz {
+
+class PresencePushTask : public XmppTask {
+
+public:
+ PresencePushTask(Task * parent) : XmppTask(parent, XmppEngine::HL_TYPE) {}
+ virtual int ProcessStart();
+ sigslot::signal1<const Status &>SignalStatusUpdate;
+
+protected:
+ virtual bool HandleStanza(const XmlElement * stanza);
+};
+
+
+}
+
+#endif
diff --git a/Plugins/jingle/libjingle/talk/examples/login/status.h b/Plugins/jingle/libjingle/talk/examples/login/status.h
new file mode 100644
index 0000000..69f82b1
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/examples/login/status.h
@@ -0,0 +1,212 @@
+/*
+ * Jingle call example
+ * Copyright 2004--2005, Google Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef _STATUS_H_
+#define _STATUS_H_
+
+#include "talk/xmpp/jid.h"
+#include "talk/xmpp/constants.h"
+
+#define GOOGLE_CLIENT_NODE "http://www.google.com/xmpp/client/caps"
+
+namespace buzz {
+
+class Status {
+public:
+ Status() :
+ pri_(0),
+ show_(SHOW_NONE),
+ available_(false),
+ e_code_(0),
+ phone_capability_(false),
+ fileshare_capability_(false),
+ know_capabilities_(false),
+ is_google_client_(false),
+ feedback_probation_(false) {};
+
+ ~Status() {}
+
+ // These are arranged in "priority order", i.e., if we see
+ // two statuses at the same priority but with different Shows,
+ // we will show the one with the highest show in the following
+ // order.
+ enum Show {
+ SHOW_NONE = 0,
+ SHOW_OFFLINE = 1,
+ SHOW_XA = 2,
+ SHOW_AWAY = 3,
+ SHOW_DND = 4,
+ SHOW_ONLINE = 5,
+ SHOW_CHAT = 6,
+ };
+
+ const Jid & jid() const { return jid_; }
+ int priority() const { return pri_; }
+ Show show() const { return show_; }
+ const std::string & status() const { return status_; }
+ bool available() const { return available_ ; }
+ int error_code() const { return e_code_; }
+ const std::string & error_string() const { return e_str_; }
+ bool know_capabilities() const { return know_capabilities_; }
+ bool phone_capability() const { return phone_capability_; }
+ bool fileshare_capability() const { return fileshare_capability_; }
+ bool is_google_client() const { return is_google_client_; }
+ const std::string & version() const { return version_; }
+ bool feedback_probation() const { return feedback_probation_; }
+ const std::string& sent_time() const { return sent_time_; }
+
+ void set_jid(const Jid & jid) { jid_ = jid; }
+ void set_priority(int pri) { pri_ = pri; }
+ void set_show(Show show) { show_ = show; }
+ void set_status(const std::string & status) { status_ = status; }
+ void set_available(bool a) { available_ = a; }
+ void set_error(int e_code, const std::string e_str)
+ { e_code_ = e_code; e_str_ = e_str; }
+ void set_know_capabilities(bool f) { know_capabilities_ = f; }
+ void set_phone_capability(bool f) { phone_capability_ = f; }
+ void set_fileshare_capability(bool f) { fileshare_capability_ = f; }
+ void set_is_google_client(bool f) { is_google_client_ = f; }
+ void set_version(const std::string & v) { version_ = v; }
+ void set_feedback_probation(bool f) { feedback_probation_ = f; }
+ void set_sent_time(const std::string& time) { sent_time_ = time; }
+
+ void UpdateWith(const Status & new_value) {
+ if (!new_value.know_capabilities()) {
+ bool k = know_capabilities();
+ bool i = is_google_client();
+ bool p = phone_capability();
+ std::string v = version();
+
+ *this = new_value;
+
+ set_know_capabilities(k);
+ set_is_google_client(i);
+ set_phone_capability(p);
+ set_version(v);
+ }
+ else {
+ *this = new_value;
+ }
+ }
+
+ bool HasQuietStatus() const {
+ if (status_.empty())
+ return false;
+ return !(QuietStatus().empty());
+ }
+
+ // Knowledge of other clients' silly automatic status strings -
+ // Don't show these.
+ std::string QuietStatus() const {
+ if (jid_.resource().find("Psi") != std::string::npos) {
+ if (status_ == "Online" ||
+ status_.find("Auto Status") != std::string::npos)
+ return STR_EMPTY;
+ }
+ if (jid_.resource().find("Gaim") != std::string::npos) {
+ if (status_ == "Sorry, I ran out for a bit!")
+ return STR_EMPTY;
+ }
+ return TrimStatus(status_);
+ }
+
+ std::string ExplicitStatus() const {
+ std::string result = QuietStatus();
+ if (result.empty()) {
+ result = ShowStatus();
+ }
+ return result;
+ }
+
+ std::string ShowStatus() const {
+ std::string result;
+ if (!available()) {
+ result = "Offline";
+ }
+ else {
+ switch (show()) {
+ case SHOW_AWAY:
+ case SHOW_XA:
+ result = "Idle";
+ break;
+ case SHOW_DND:
+ result = "Busy";
+ break;
+ case SHOW_CHAT:
+ result = "Chatty";
+ break;
+ default:
+ result = "Available";
+ break;
+ }
+ }
+ return result;
+ }
+
+ static std::string TrimStatus(const std::string & st) {
+ std::string s(st);
+ int j = 0;
+ bool collapsing = true;
+ for (unsigned int i = 0; i < s.length(); i+= 1) {
+ if (s[i] <= ' ' && s[i] >= 0) {
+ if (collapsing) {
+ continue;
+ }
+ else {
+ s[j] = ' ';
+ j += 1;
+ collapsing = true;
+ }
+ }
+ else {
+ s[j] = s[i];
+ j += 1;
+ collapsing = false;
+ }
+ }
+ if (collapsing && j > 0) {
+ j -= 1;
+ }
+ s.erase(j, s.length());
+ return s;
+ }
+
+private:
+ Jid jid_;
+ int pri_;
+ Show show_;
+ std::string status_;
+ bool available_;
+ int e_code_;
+ std::string e_str_;
+ bool feedback_probation_;
+
+ // capabilities (valid only if know_capabilities_
+ bool know_capabilities_;
+ bool phone_capability_;
+ bool fileshare_capability_;
+ bool is_google_client_;
+ std::string version_;
+
+ std::string sent_time_; // from the jabber:x:delay element
+};
+
+}
+
+
+#endif
diff --git a/Plugins/jingle/libjingle/talk/examples/pcp/Makefile.am b/Plugins/jingle/libjingle/talk/examples/pcp/Makefile.am
new file mode 100644
index 0000000..de779cd
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/examples/pcp/Makefile.am
@@ -0,0 +1,15 @@
+bin_PROGRAMS = pcp
+pcp_CXXFLAGS = $(AM_CXXFLAGS)
+pcp_SOURCES = pcp_main.cc
+pcp_LDADD = $(top_srcdir)/talk/examples/login/libcricketexampleslogin.la \
+ $(top_srcdir)/talk/session/fileshare/libcricketsessionfileshare.la \
+ $(top_srcdir)/talk/session/tunnel/libcricketsessiontunnel.la \
+ $(top_srcdir)/talk/p2p/client/libcricketp2pclient.la \
+ $(top_srcdir)/talk/p2p/base/libcricketp2pbase.la \
+ $(top_srcdir)/talk/xmpp/libcricketxmpp.la \
+ $(top_srcdir)/talk/xmllite/libcricketxmllite.la \
+ $(top_srcdir)/talk/base/libcricketbase.la \
+ $(EXPAT_LIBS) -lpthread -lssl -lcrypto
+
+AM_CPPFLAGS = -DPOSIX
+EXTRA_DIST = pcp.vcproj
diff --git a/Plugins/jingle/libjingle/talk/examples/pcp/Makefile.in b/Plugins/jingle/libjingle/talk/examples/pcp/Makefile.in
new file mode 100644
index 0000000..0bba040
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/examples/pcp/Makefile.in
@@ -0,0 +1,501 @@
+# Makefile.in generated by automake 1.9.6 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005 Free Software Foundation, Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+top_builddir = ../../..
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+INSTALL = @INSTALL@
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+target_triplet = @target@
+bin_PROGRAMS = pcp$(EXEEXT)
+subdir = talk/examples/pcp
+DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/talk/pkg.m4 \
+ $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+am__installdirs = "$(DESTDIR)$(bindir)"
+binPROGRAMS_INSTALL = $(INSTALL_PROGRAM)
+PROGRAMS = $(bin_PROGRAMS)
+am_pcp_OBJECTS = pcp-pcp_main.$(OBJEXT)
+pcp_OBJECTS = $(am_pcp_OBJECTS)
+am__DEPENDENCIES_1 =
+pcp_DEPENDENCIES = \
+ $(top_srcdir)/talk/examples/login/libcricketexampleslogin.la \
+ $(top_srcdir)/talk/session/fileshare/libcricketsessionfileshare.la \
+ $(top_srcdir)/talk/session/tunnel/libcricketsessiontunnel.la \
+ $(top_srcdir)/talk/p2p/client/libcricketp2pclient.la \
+ $(top_srcdir)/talk/p2p/base/libcricketp2pbase.la \
+ $(top_srcdir)/talk/xmpp/libcricketxmpp.la \
+ $(top_srcdir)/talk/xmllite/libcricketxmllite.la \
+ $(top_srcdir)/talk/base/libcricketbase.la \
+ $(am__DEPENDENCIES_1)
+DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+ $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS)
+LTCXXCOMPILE = $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CXXFLAGS) $(CXXFLAGS)
+CXXLD = $(CXX)
+CXXLINK = $(LIBTOOL) --tag=CXX --mode=link $(CXXLD) $(AM_CXXFLAGS) \
+ $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
+SOURCES = $(pcp_SOURCES)
+DIST_SOURCES = $(pcp_SOURCES)
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+ALSA_LIBS = @ALSA_LIBS@
+AMDEP_FALSE = @AMDEP_FALSE@
+AMDEP_TRUE = @AMDEP_TRUE@
+AMTAR = @AMTAR@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+ECHO = @ECHO@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+EXPAT_LIBS = @EXPAT_LIBS@
+F77 = @F77@
+FFLAGS = @FFLAGS@
+GIPS_FALSE = @GIPS_FALSE@
+GIPS_TRUE = @GIPS_TRUE@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_LIBS = @GLIB_LIBS@
+ILBC_CFLAGS = @ILBC_CFLAGS@
+ILBC_LIBS = @ILBC_LIBS@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LDFLAGS = @LDFLAGS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+MAKEINFO = @MAKEINFO@
+MEDIA_LIBS = @MEDIA_LIBS@
+OBJEXT = @OBJEXT@
+ORTP_CFLAGS = @ORTP_CFLAGS@
+ORTP_LIBS = @ORTP_LIBS@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PHONE_FALSE = @PHONE_FALSE@
+PHONE_TRUE = @PHONE_TRUE@
+PKG_CONFIG = @PKG_CONFIG@
+RANLIB = @RANLIB@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SPEEX_CFLAGS = @SPEEX_CFLAGS@
+SPEEX_LIBS = @SPEEX_LIBS@
+STRIP = @STRIP@
+VERSION = @VERSION@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_F77 = @ac_ct_F77@
+ac_ct_RANLIB = @ac_ct_RANLIB@
+ac_ct_STRIP = @ac_ct_STRIP@
+am__fastdepCC_FALSE = @am__fastdepCC_FALSE@
+am__fastdepCC_TRUE = @am__fastdepCC_TRUE@
+am__fastdepCXX_FALSE = @am__fastdepCXX_FALSE@
+am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+datadir = @datadir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+sysconfdir = @sysconfdir@
+target = @target@
+target_alias = @target_alias@
+target_cpu = @target_cpu@
+target_os = @target_os@
+target_vendor = @target_vendor@
+pcp_CXXFLAGS = $(AM_CXXFLAGS)
+pcp_SOURCES = pcp_main.cc
+pcp_LDADD = $(top_srcdir)/talk/examples/login/libcricketexampleslogin.la \
+ $(top_srcdir)/talk/session/fileshare/libcricketsessionfileshare.la \
+ $(top_srcdir)/talk/session/tunnel/libcricketsessiontunnel.la \
+ $(top_srcdir)/talk/p2p/client/libcricketp2pclient.la \
+ $(top_srcdir)/talk/p2p/base/libcricketp2pbase.la \
+ $(top_srcdir)/talk/xmpp/libcricketxmpp.la \
+ $(top_srcdir)/talk/xmllite/libcricketxmllite.la \
+ $(top_srcdir)/talk/base/libcricketbase.la \
+ $(EXPAT_LIBS) -lpthread -lssl -lcrypto
+
+AM_CPPFLAGS = -DPOSIX
+EXTRA_DIST = pcp.vcproj
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .cc .lo .o .obj
+$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \
+ && exit 0; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu talk/examples/pcp/Makefile'; \
+ cd $(top_srcdir) && \
+ $(AUTOMAKE) --gnu talk/examples/pcp/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+install-binPROGRAMS: $(bin_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ test -z "$(bindir)" || $(mkdir_p) "$(DESTDIR)$(bindir)"
+ @list='$(bin_PROGRAMS)'; for p in $$list; do \
+ p1=`echo $$p|sed 's/$(EXEEXT)$$//'`; \
+ if test -f $$p \
+ || test -f $$p1 \
+ ; then \
+ f=`echo "$$p1" | sed 's,^.*/,,;$(transform);s/$$/$(EXEEXT)/'`; \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) --mode=install $(binPROGRAMS_INSTALL) '$$p' '$(DESTDIR)$(bindir)/$$f'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) --mode=install $(binPROGRAMS_INSTALL) "$$p" "$(DESTDIR)$(bindir)/$$f" || exit 1; \
+ else :; fi; \
+ done
+
+uninstall-binPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(bin_PROGRAMS)'; for p in $$list; do \
+ f=`echo "$$p" | sed 's,^.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/'`; \
+ echo " rm -f '$(DESTDIR)$(bindir)/$$f'"; \
+ rm -f "$(DESTDIR)$(bindir)/$$f"; \
+ done
+
+clean-binPROGRAMS:
+ @list='$(bin_PROGRAMS)'; for p in $$list; do \
+ f=`echo $$p|sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f $$p $$f"; \
+ rm -f $$p $$f ; \
+ done
+pcp$(EXEEXT): $(pcp_OBJECTS) $(pcp_DEPENDENCIES)
+ @rm -f pcp$(EXEEXT)
+ $(CXXLINK) $(pcp_LDFLAGS) $(pcp_OBJECTS) $(pcp_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pcp-pcp_main.Po@am__quote@
+
+.cc.o:
+@am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \
+@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ $<
+
+.cc.obj:
+@am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \
+@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.cc.lo:
+@am__fastdepCXX_TRUE@ if $(LTCXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \
+@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Plo"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(LTCXXCOMPILE) -c -o $@ $<
+
+pcp-pcp_main.o: pcp_main.cc
+@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pcp_CXXFLAGS) $(CXXFLAGS) -MT pcp-pcp_main.o -MD -MP -MF "$(DEPDIR)/pcp-pcp_main.Tpo" -c -o pcp-pcp_main.o `test -f 'pcp_main.cc' || echo '$(srcdir)/'`pcp_main.cc; \
+@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/pcp-pcp_main.Tpo" "$(DEPDIR)/pcp-pcp_main.Po"; else rm -f "$(DEPDIR)/pcp-pcp_main.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='pcp_main.cc' object='pcp-pcp_main.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pcp_CXXFLAGS) $(CXXFLAGS) -c -o pcp-pcp_main.o `test -f 'pcp_main.cc' || echo '$(srcdir)/'`pcp_main.cc
+
+pcp-pcp_main.obj: pcp_main.cc
+@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pcp_CXXFLAGS) $(CXXFLAGS) -MT pcp-pcp_main.obj -MD -MP -MF "$(DEPDIR)/pcp-pcp_main.Tpo" -c -o pcp-pcp_main.obj `if test -f 'pcp_main.cc'; then $(CYGPATH_W) 'pcp_main.cc'; else $(CYGPATH_W) '$(srcdir)/pcp_main.cc'; fi`; \
+@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/pcp-pcp_main.Tpo" "$(DEPDIR)/pcp-pcp_main.Po"; else rm -f "$(DEPDIR)/pcp-pcp_main.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='pcp_main.cc' object='pcp-pcp_main.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pcp_CXXFLAGS) $(CXXFLAGS) -c -o pcp-pcp_main.obj `if test -f 'pcp_main.cc'; then $(CYGPATH_W) 'pcp_main.cc'; else $(CYGPATH_W) '$(srcdir)/pcp_main.cc'; fi`
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+distclean-libtool:
+ -rm -f libtool
+uninstall-info-am:
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ mkid -fID $$unique
+tags: TAGS
+
+TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ tags=; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$tags $$unique; \
+ fi
+ctags: CTAGS
+CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ tags=; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ test -z "$(CTAGS_ARGS)$$tags$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$tags $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && cd $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) $$here
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \
+ list='$(DISTFILES)'; for file in $$list; do \
+ case $$file in \
+ $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \
+ $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \
+ esac; \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test "$$dir" != "$$file" && test "$$dir" != "."; then \
+ dir="/$$dir"; \
+ $(mkdir_p) "$(distdir)$$dir"; \
+ else \
+ dir=''; \
+ fi; \
+ if test -d $$d/$$file; then \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \
+ fi; \
+ cp -pR $$d/$$file $(distdir)$$dir || exit 1; \
+ else \
+ test -f $(distdir)/$$file \
+ || cp -p $$d/$$file $(distdir)/$$file \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(PROGRAMS)
+installdirs:
+ for dir in "$(DESTDIR)$(bindir)"; do \
+ test -z "$$dir" || $(mkdir_p) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ `test -z '$(STRIP)' || \
+ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-binPROGRAMS clean-generic clean-libtool mostlyclean-am
+
+distclean: distclean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-libtool distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-exec-am: install-binPROGRAMS
+
+install-info: install-info-am
+
+install-man:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-binPROGRAMS uninstall-info-am
+
+.PHONY: CTAGS GTAGS all all-am check check-am clean clean-binPROGRAMS \
+ clean-generic clean-libtool ctags distclean distclean-compile \
+ distclean-generic distclean-libtool distclean-tags distdir dvi \
+ dvi-am html html-am info info-am install install-am \
+ install-binPROGRAMS install-data install-data-am install-exec \
+ install-exec-am install-info install-info-am install-man \
+ install-strip installcheck installcheck-am installdirs \
+ maintainer-clean maintainer-clean-generic mostlyclean \
+ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \
+ pdf pdf-am ps ps-am tags uninstall uninstall-am \
+ uninstall-binPROGRAMS uninstall-info-am
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/Plugins/jingle/libjingle/talk/examples/pcp/pcp.vcproj b/Plugins/jingle/libjingle/talk/examples/pcp/pcp.vcproj
new file mode 100644
index 0000000..03c661a
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/examples/pcp/pcp.vcproj
@@ -0,0 +1,234 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="8,00"
+ Name="pcp"
+ ProjectGUID="{EF5C9C17-874A-44C3-93CB-1165F03DBCD3}"
+ RootNamespace="pcp"
+ Keyword="Win32Proj"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories="../../.."
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;_WINDOWS;UNICODE;_UNICODE;WINVER=0x0500;_WIN32_WINNT=0x500;PRODUCTION_BUILD;PRODUCTION;XML_STATIC;FEATURE_ENABLE_SSL;FEATURE_ENABLE_CHAT_ARCHIVING;FEATURE_ENABLE_VOICEMAIL"
+ MinimalRebuild="true"
+ ExceptionHandling="1"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="1"
+ TreatWChar_tAsBuiltInType="false"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="true"
+ DebugInformationFormat="4"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="comsupp.lib secur32.lib ws2_32.lib libexpatMT.lib Iphlpapi.lib crypt32.lib"
+ LinkIncremental="2"
+ AdditionalLibraryDirectories="C:\Desenvolvimento\libjingle-0.4.0\talk\third_party\Expat\StaticLibs"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories="../../.."
+ PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;_WINDOWS;UNICODE;_UNICODE;WINVER=0x0500;_WIN32_WINNT=0x500;PRODUCTION_BUILD;PRODUCTION;XML_STATIC;FEATURE_ENABLE_SSL;FEATURE_ENABLE_CHAT_ARCHIVING"
+ ExceptionHandling="1"
+ RuntimeLibrary="0"
+ TreatWChar_tAsBuiltInType="false"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="true"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="comsupp.lib secur32.lib ws2_32.lib libexpatMT.lib Iphlpapi.lib"
+ LinkIncremental="1"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Source Files"
+ Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
+ UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
+ >
+ <File
+ RelativePath="..\login\jingleinfotask.cc"
+ >
+ </File>
+ <File
+ RelativePath="pcp_main.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\login\presenceouttask.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\login\presencepushtask.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\login\xmppauth.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\login\xmpppump.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\login\xmppsocket.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\login\xmppthread.cc"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Header Files"
+ Filter="h;hpp;hxx;hm;inl;inc;xsd"
+ UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
+ >
+ </Filter>
+ <Filter
+ Name="Resource Files"
+ Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"
+ UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
+ >
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/Plugins/jingle/libjingle/talk/examples/pcp/pcp_main.cc b/Plugins/jingle/libjingle/talk/examples/pcp/pcp_main.cc
new file mode 100644
index 0000000..4d5646d
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/examples/pcp/pcp_main.cc
@@ -0,0 +1,615 @@
+/*
+ * Jingle call example
+ * Copyright 2004--2005, Google Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Tempe Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <iomanip>
+#include <time.h>
+
+#ifndef WIN32
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <iomanip>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#else
+#include <direct.h>
+//typedef _getcwd getcwd;
+#include "talk/base/win32.h"
+#endif
+
+#include "talk/base/fileutils.h"
+#include "talk/base/pathutils.h"
+#include "talk/base/helpers.h"
+#include "talk/base/httpclient.h"
+#include "talk/base/logging.h"
+#include "talk/base/physicalsocketserver.h"
+#include "talk/base/ssladapter.h"
+#include "talk/xmpp/xmppclientsettings.h"
+#include "talk/examples/login/xmppthread.h"
+#include "talk/examples/login/xmppauth.h"
+#include "talk/p2p/client/httpportallocator.h"
+#include "talk/p2p/client/sessionmanagertask.h"
+#include "talk/session/fileshare/fileshare.h"
+#include "talk/examples/login/presencepushtask.h"
+#include "talk/examples/login/presenceouttask.h"
+#include "talk/examples/login/jingleinfotask.h"
+
+#if defined(_MSC_VER) && (_MSC_VER < 1400)
+// The following are necessary to properly link when compiling STL without
+// /EHsc, otherwise known as C++ exceptions.
+void __cdecl std::_Throw(const std::exception &) {}
+std::_Prhand std::_Raise_handler = 0;
+#endif
+
+void SetConsoleEcho(bool on) {
+#ifdef WIN32
+ HANDLE hIn = GetStdHandle(STD_INPUT_HANDLE);
+ if ((hIn == INVALID_HANDLE_VALUE) || (hIn == NULL))
+ return;
+
+ DWORD mode;
+ if (!GetConsoleMode(hIn, &mode))
+ return;
+
+ if (on) {
+ mode = mode | ENABLE_ECHO_INPUT;
+ } else {
+ mode = mode & ~ENABLE_ECHO_INPUT;
+ }
+
+ SetConsoleMode(hIn, mode);
+#else
+ if (on)
+ system("stty echo");
+ else
+ system("stty -echo");
+#endif
+}
+class DebugLog : public sigslot::has_slots<> {
+public:
+ DebugLog() :
+ debug_input_buf_(NULL), debug_input_len_(0), debug_input_alloc_(0),
+ debug_output_buf_(NULL), debug_output_len_(0), debug_output_alloc_(0),
+ censor_password_(false)
+ {}
+ char * debug_input_buf_;
+ int debug_input_len_;
+ int debug_input_alloc_;
+ char * debug_output_buf_;
+ int debug_output_len_;
+ int debug_output_alloc_;
+ bool censor_password_;
+
+ void Input(const char * data, int len) {
+ if (debug_input_len_ + len > debug_input_alloc_) {
+ char * old_buf = debug_input_buf_;
+ debug_input_alloc_ = 4096;
+ while (debug_input_alloc_ < debug_input_len_ + len) {
+ debug_input_alloc_ *= 2;
+ }
+ debug_input_buf_ = new char[debug_input_alloc_];
+ memcpy(debug_input_buf_, old_buf, debug_input_len_);
+ delete[] old_buf;
+ }
+ memcpy(debug_input_buf_ + debug_input_len_, data, len);
+ debug_input_len_ += len;
+ DebugPrint(debug_input_buf_, &debug_input_len_, false);
+ }
+
+ void Output(const char * data, int len) {
+ if (debug_output_len_ + len > debug_output_alloc_) {
+ char * old_buf = debug_output_buf_;
+ debug_output_alloc_ = 4096;
+ while (debug_output_alloc_ < debug_output_len_ + len) {
+ debug_output_alloc_ *= 2;
+ }
+ debug_output_buf_ = new char[debug_output_alloc_];
+ memcpy(debug_output_buf_, old_buf, debug_output_len_);
+ delete[] old_buf;
+ }
+ memcpy(debug_output_buf_ + debug_output_len_, data, len);
+ debug_output_len_ += len;
+ DebugPrint(debug_output_buf_, &debug_output_len_, true);
+ }
+
+ static bool
+ IsAuthTag(const char * str, size_t len) {
+ if (str[0] == '<' && str[1] == 'a' &&
+ str[2] == 'u' &&
+ str[3] == 't' &&
+ str[4] == 'h' &&
+ str[5] <= ' ') {
+ std::string tag(str, len);
+
+ if (tag.find("mechanism") != std::string::npos)
+ return true;
+
+ }
+ return false;
+ }
+
+ void
+ DebugPrint(char * buf, int * plen, bool output) {
+ int len = *plen;
+ if (len > 0) {
+ time_t tim = time(NULL);
+ struct tm * now = localtime(&tim);
+ char *time_string = asctime(now);
+ if (time_string) {
+ size_t time_len = strlen(time_string);
+ if (time_len > 0) {
+ time_string[time_len-1] = 0; // trim off terminating \n
+ }
+ }
+ LOG(INFO) << (output ? "SEND >>>>>>>>>>>>>>>>>>>>>>>>>" : "RECV <<<<<<<<<<<<<<<<<<<<<<<<<")
+ << " : " << time_string;
+
+ bool indent;
+ int start = 0, nest = 3;
+ for (int i = 0; i < len; i += 1) {
+ if (buf[i] == '>') {
+ if ((i > 0) && (buf[i-1] == '/')) {
+ indent = false;
+ } else if ((start + 1 < len) && (buf[start + 1] == '/')) {
+ indent = false;
+ nest -= 2;
+ } else {
+ indent = true;
+ }
+
+ // Output a tag
+ LOG(INFO) << std::setw(nest) << " " << std::string(buf + start, i + 1 - start);
+
+ if (indent)
+ nest += 2;
+
+ // Note if it's a PLAIN auth tag
+ if (IsAuthTag(buf + start, i + 1 - start)) {
+ censor_password_ = true;
+ }
+
+ // incr
+ start = i + 1;
+ }
+
+ if (buf[i] == '<' && start < i) {
+ if (censor_password_) {
+ LOG(INFO) << std::setw(nest) << " " << "## TEXT REMOVED ##";
+ censor_password_ = false;
+ }
+ else {
+ LOG(INFO) << std::setw(nest) << " " << std::string(buf + start, i - start);
+ }
+ start = i;
+ }
+ }
+ len = len - start;
+ memcpy(buf, buf + start, len);
+ *plen = len;
+ }
+ }
+
+};
+
+static DebugLog debug_log_;
+
+
+class FileShareClient : public sigslot::has_slots<>, public talk_base::MessageHandler {
+ public:
+ FileShareClient(buzz::XmppClient *xmppclient, const buzz::Jid &send_to, const cricket::FileShareManifest *manifest, std::string root_dir) :
+ xmpp_client_(xmppclient),
+ root_dir_(root_dir),
+ send_to_jid_(send_to),
+ waiting_for_file_(send_to == buzz::JID_EMPTY),
+ manifest_(manifest) {}
+
+ void OnStateChange(buzz::XmppEngine::State state) {
+ switch (state) {
+ case buzz::XmppEngine::STATE_START:
+ std::cout << "Connecting..." << std::endl;
+ break;
+ case buzz::XmppEngine::STATE_OPENING:
+ std::cout << "Logging in. " << std::endl;
+ break;
+ case buzz::XmppEngine::STATE_OPEN:
+ std::cout << "Logged in as " << xmpp_client_->jid().Str() << std::endl;
+ if (!waiting_for_file_)
+ std::cout << "Waiting for " << send_to_jid_.Str() << std::endl;
+ OnSignon();
+ break;
+ case buzz::XmppEngine::STATE_CLOSED:
+ std::cout << "Logged out." << std::endl;
+ break;
+ }
+ }
+
+ private:
+
+ enum {
+ MSG_STOP,
+ };
+
+ void OnJingleInfo(const std::string & relay_token,
+ const std::vector<std::string> &relay_addresses,
+ const std::vector<talk_base::SocketAddress> &stun_addresses) {
+ port_allocator_->SetStunHosts(stun_addresses);
+ port_allocator_->SetRelayHosts(relay_addresses);
+ port_allocator_->SetRelayToken(relay_token);
+ }
+
+
+ void OnStatusUpdate(const buzz::Status &status) {
+ if (status.available() && status.fileshare_capability()) {
+
+ // A contact's status has changed. If the person we're looking for is online and able to receive
+ // files, send it.
+ if (send_to_jid_.BareEquals(status.jid())) {
+ std::cout << send_to_jid_.Str() << " has signed on." << std::endl;
+ cricket::FileShareSession* share = file_share_session_client_->CreateFileShareSession();
+ share->Share(status.jid(), const_cast<cricket::FileShareManifest*>(manifest_));
+ send_to_jid_ = buzz::Jid("");
+ }
+
+ }
+ }
+
+ void OnMessage(talk_base::Message *m) {
+ ASSERT(m->message_id == MSG_STOP);
+ talk_base::Thread *thread = talk_base::ThreadManager::CurrentThread();
+ delete session_;
+ thread->Stop();
+ }
+
+ std::string filesize_to_string(unsigned int size) {
+ double size_display;
+ std::string format;
+ std::stringstream ret;
+
+ // the comparisons to 1000 * (2^(n10)) are intentional
+ // it's so you don't see something like "1023 bytes",
+ // instead you'll see ".9 KB"
+
+ if (size < 1000) {
+ format = "Bytes";
+ size_display = size;
+ } else if (size < 1000 * 1024) {
+ format = "KiB";
+ size_display = (double)size / 1024.0;
+ } else if (size < 1000 * 1024 * 1024) {
+ format = "MiB";
+ size_display = (double)size / (1024.0 * 1024.0);
+ } else {
+ format = "GiB";
+ size_display = (double)size / (1024.0 * 1024.0 * 1024.0);
+ }
+
+ ret << std::setprecision(1) << std::setiosflags(std::ios::fixed) << size_display << " " << format;
+ return ret.str();
+ }
+
+ void OnSessionState(cricket::FileShareState state) {
+ talk_base::Thread *thread = talk_base::ThreadManager::CurrentThread();
+ std::stringstream manifest_description;
+
+ switch(state) {
+ case cricket::FS_OFFER:
+
+ // The offer has been made; print a summary of it and, if it's an incoming transfer, accept it
+
+ if (manifest_->size() == 1)
+ manifest_description << session_->manifest()->item(0).name;
+ else if (session_->manifest()->GetFileCount() && session_->manifest()->GetFolderCount())
+ manifest_description << session_->manifest()->GetFileCount() << " files and " <<
+ session_->manifest()->GetFolderCount() << " directories";
+ else if (session_->manifest()->GetFileCount() > 0)
+ manifest_description << session_->manifest()->GetFileCount() << " files";
+ else
+ manifest_description << session_->manifest()->GetFolderCount() << " directories";
+
+ size_t filesize;
+ if (!session_->GetTotalSize(filesize)) {
+ manifest_description << " (Unknown size)";
+ } else {
+ manifest_description << " (" << filesize_to_string(filesize) << ")";
+ }
+ if (session_->is_sender()) {
+ std::cout << "Offering " << manifest_description.str() << " to " << send_to_jid_.Str() << std::endl;
+ } else if (waiting_for_file_) {
+ std::cout << "Receiving " << manifest_description.str() << " from " << session_->jid().BareJid().Str() << std::endl;
+ session_->Accept();
+ waiting_for_file_ = false;
+
+ // If this were a graphical client, we might want to go through the manifest, look for images,
+ // and request previews. There are two ways to go about this:
+ //
+ // If we want to display the preview in a web browser (like the embedded IE control in Google Talk), we could call
+ // GetImagePreviewUrl on the session, with the image's index in the manifest, the size, and a pointer to the URL.
+ // This will cause the session to listen for HTTP requests on localhost, and set url to a localhost URL that any
+ // web browser can use to get the image preview:
+ //
+ // std::string url;
+ // session_->GetImagePreviewUrl(0, 100, 100, &url);
+ // url = std::string("firefox \"") + url + "\"";
+ // system(url.c_str());
+ //
+ // Alternately, you could use libjingle's own HTTP code with the FileShareSession's SocketPool interface to
+ // write the image preview directly into a StreamInterface:
+ //
+ // talk_base::HttpClient *client = new talk_base::HttpClient("pcp", session_);
+ // std::string path;
+ // session_->GetItemNetworkPath(0,1,&path);
+ //
+ // client->request().verb = talk_base::HV_GET;
+ // client->request().path = path + "?width=100&height=100";
+ // talk_base::FileStream *file = new talk_base::FileStream;
+ // file->Open("/home/username/foo.jpg", "wb");
+ // client->response().document.reset(file);
+ // client->start();
+ }
+ break;
+ case cricket::FS_TRANSFER:
+ std::cout << "File transfer started." << std::endl;
+ break;
+ case cricket::FS_COMPLETE:
+ thread->Post(this, MSG_STOP);
+ std::cout << std::endl << "File transfer completed." << std::endl;
+ break;
+ case cricket::FS_LOCAL_CANCEL:
+ case cricket::FS_REMOTE_CANCEL:
+ std::cout << std::endl << "File transfer cancelled." << std::endl;
+ thread->Post(this, MSG_STOP);
+ break;
+ case cricket::FS_FAILURE:
+ std::cout << std::endl << "File transfer failed." << std::endl;
+ thread->Post(this, MSG_STOP);
+ break;
+ }
+ }
+
+ void OnUpdateProgress(cricket::FileShareSession *sess) {
+ // Progress has occured on the transfer; update the UI
+
+ size_t totalsize, progress;
+ std::string itemname;
+ unsigned int width = 79;
+
+#ifndef WIN32
+ struct winsize ws;
+ if ((ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == 0))
+ width = ws.ws_col;
+#endif
+
+ if(sess->GetTotalSize(totalsize) && sess->GetProgress(progress) && sess->GetCurrentItemName(&itemname)) {
+ float percent = (float)progress / totalsize;
+ unsigned int progressbar_width = (width * 4) / 5;
+
+ const char *filename = itemname.c_str();
+ std::cout.put('\r');
+ for (unsigned int l = 0; l < width; l++) {
+ if (l < percent * progressbar_width)
+ std::cout.put('#');
+ else if (l > progressbar_width && l < progressbar_width + 1 + strlen(filename))
+ std::cout.put(filename[l-(progressbar_width + 1)]);
+ else
+ std::cout.put(' ');
+ }
+ std::cout.flush();
+ }
+ }
+
+ void OnResampleImage(std::string path, int width, int height, talk_base::HttpTransaction *trans) {
+
+ // The other side has requested an image preview. This is an asynchronous request. We should resize
+ // the image to the requested size,and send that to ResampleComplete(). For simplicity, here, we
+ // send back the original sized image. Note that because we don't recognize images in our manifest
+ // this will never be called in pcp
+
+ // Even if you don't resize images, you should implement this method and connect to the
+ // SignalResampleImage signal, just to return an error.
+
+ talk_base::FileStream *s = new talk_base::FileStream();
+ if (s->Open(path.c_str(), "rb"))
+ session_->ResampleComplete(s, trans, true);
+ else {
+ delete s;
+ session_->ResampleComplete(NULL, trans, false);
+ }
+ }
+
+ void OnFileShareSessionCreate(cricket::FileShareSession *sess) {
+ session_ = sess;
+ sess->SignalState.connect(this, &FileShareClient::OnSessionState);
+ sess->SignalNextFile.connect(this, &FileShareClient::OnUpdateProgress);
+ sess->SignalUpdateProgress.connect(this, &FileShareClient::OnUpdateProgress);
+ sess->SignalResampleImage.connect(this, &FileShareClient::OnResampleImage);
+ sess->SetLocalFolder(root_dir_);
+ }
+
+ void OnSignon() {
+ std::string client_unique = xmpp_client_->jid().Str();
+ cricket::InitRandom(client_unique.c_str(), client_unique.size());
+
+ buzz::PresencePushTask *presence_push_ = new buzz::PresencePushTask(xmpp_client_);
+ presence_push_->SignalStatusUpdate.connect(this, &FileShareClient::OnStatusUpdate);
+ presence_push_->Start();
+
+ buzz::Status my_status;
+ my_status.set_jid(xmpp_client_->jid());
+ my_status.set_available(true);
+ my_status.set_show(buzz::Status::SHOW_ONLINE);
+ my_status.set_priority(0);
+ my_status.set_know_capabilities(true);
+ my_status.set_fileshare_capability(true);
+ my_status.set_is_google_client(true);
+ my_status.set_version("1.0.0.66");
+
+ buzz::PresenceOutTask* presence_out_ =
+ new buzz::PresenceOutTask(xmpp_client_);
+ presence_out_->Send(my_status);
+ presence_out_->Start();
+
+ port_allocator_.reset(new cricket::HttpPortAllocator(&network_manager_, "pcp"));
+
+ session_manager_.reset(new cricket::SessionManager(port_allocator_.get(), NULL));
+
+ cricket::SessionManagerTask * session_manager_task = new cricket::SessionManagerTask(xmpp_client_, session_manager_.get());
+ session_manager_task->EnableOutgoingMessages();
+ session_manager_task->Start();
+
+ buzz::JingleInfoTask *jingle_info_task = new buzz::JingleInfoTask(xmpp_client_);
+ jingle_info_task->RefreshJingleInfoNow();
+ jingle_info_task->SignalJingleInfo.connect(this, &FileShareClient::OnJingleInfo);
+ jingle_info_task->Start();
+
+ file_share_session_client_.reset(new cricket::FileShareSessionClient(session_manager_.get(), xmpp_client_->jid(), "pcp"));
+ file_share_session_client_->SignalFileShareSessionCreate.connect(this, &FileShareClient::OnFileShareSessionCreate);
+ session_manager_->AddClient(NS_GOOGLE_SHARE, file_share_session_client_.get());
+ }
+
+ talk_base::NetworkManager network_manager_;
+ talk_base::scoped_ptr<cricket::HttpPortAllocator> port_allocator_;
+ talk_base::scoped_ptr<cricket::SessionManager> session_manager_;
+ talk_base::scoped_ptr<cricket::FileShareSessionClient> file_share_session_client_;
+ buzz::XmppClient *xmpp_client_;
+ buzz::Jid send_to_jid_;
+ const cricket::FileShareManifest *manifest_;
+ cricket::FileShareSession *session_;
+ bool waiting_for_file_;
+ std::string root_dir_;
+};
+
+static unsigned int get_dir_size(const char *directory) {
+ unsigned int total = 0;
+ talk_base::DirectoryIterator iter;
+ talk_base::Pathname path;
+ path.AppendFolder(directory);
+ iter.Iterate(path.pathname());
+ while (iter.Next()) {
+ if (iter.Name() == "." || iter.Name() == "..")
+ continue;
+ if (iter.IsDirectory()) {
+ path.AppendPathname(iter.Name());
+ total += get_dir_size(path.pathname().c_str());
+ }
+ else
+ total += iter.FileSize();
+ }
+ return total;
+}
+
+int main(int argc, char **argv) {
+ talk_base::PhysicalSocketServer ss;
+ int i;
+ bool debug = false;
+ bool send_mode = false;
+ char cwd[256];
+ getcwd(cwd, sizeof(cwd));
+ for (i = 1; i < argc && *argv[i] == '-'; i++) {
+ if (!strcmp(argv[i], "-d")) {
+ debug = true;
+ } else {
+ std::cout << "USAGE: " << argv[0] << " [-d][-h] [FILE1 FILE2 ... FILE#] [JID]" << std::endl;
+ std::cout << " To send files, specify a list of files to send, followed by the JID of the recipient" << std::endl;
+ std::cout << " To receive files, specify no files or JID" << std::endl;
+ std::cout << "COMMAND LINE ARGUMENTS" << std::endl;
+ std::cout << " -h -- Prints this help message" << std::endl;
+ std::cout << " -d -- Prints debug messages to stderr" << std::endl;
+ exit(0);
+ }
+ }
+
+ if (debug)
+ talk_base::LogMessage::LogToDebug(talk_base::LS_VERBOSE);
+ else
+ talk_base::LogMessage::LogToDebug(talk_base::LS_ERROR + 1);
+
+
+ talk_base::InitializeSSL();
+ XmppPump pump;
+ buzz::Jid jid;
+ buzz::XmppClientSettings xcs;
+ talk_base::InsecureCryptStringImpl pass;
+ std::string username;
+
+ std::cout << "JID: ";
+ std::cin >> username;
+ jid = buzz::Jid(username);
+ if (!jid.IsValid() || jid.node() == "") {
+ printf("Invalid JID. JIDs should be in the form user@domain\n");
+ return 1;
+ }
+ SetConsoleEcho(false);
+ std::cout << "Password: ";
+ std::cin >> pass.password();
+ SetConsoleEcho(true);
+ std::cout << std::endl;
+
+ xcs.set_user(jid.node());
+ xcs.set_resource("pcp");
+ xcs.set_host(jid.domain());
+ xcs.set_use_tls(true);
+
+ xcs.set_pass(talk_base::CryptString(pass));
+ xcs.set_server(talk_base::SocketAddress("talk.google.com", 5222));
+
+ talk_base::Thread main_thread(&ss);
+ talk_base::ThreadManager::SetCurrent(&main_thread);
+
+ if (debug) {
+ pump.client()->SignalLogInput.connect(&debug_log_, &DebugLog::Input);
+ pump.client()->SignalLogOutput.connect(&debug_log_, &DebugLog::Output);
+ }
+
+ cricket::FileShareManifest *manifest = new cricket::FileShareManifest();
+
+ for (;i < argc - 1;i++) {
+ if (0) {
+ printf("%s is not a valid file\n", argv[i]);
+ continue;
+ }
+ send_mode = true;
+
+ // Additionally, we should check for image files here, and call
+ // AddImage on the manifest with their file size and image size.
+ // The receiving client can then request previews of those images
+ if (talk_base::Filesystem::IsFolder(std::string(argv[i]))) {
+ manifest->AddFolder(argv[i], get_dir_size(argv[i]));
+ } else {
+ size_t size = 0;
+ talk_base::Filesystem::GetFileSize(std::string(argv[i]), &size);
+ manifest->AddFile(argv[i], size);
+ }
+ }
+ buzz::Jid j;
+ if (send_mode)
+ j = buzz::Jid(argv[argc-1]);
+ else
+ j = buzz::JID_EMPTY;
+
+ FileShareClient fs_client(pump.client(), j, manifest, cwd);
+
+ pump.client()->SignalStateChange.connect(&fs_client, &FileShareClient::OnStateChange);
+
+ pump.DoLogin(xcs, new XmppSocket(true), NULL);
+ main_thread.Run();
+ pump.DoDisconnect();
+
+ return 0;
+}
diff --git a/Plugins/jingle/libjingle/talk/libjingle.sln b/Plugins/jingle/libjingle/talk/libjingle.sln
new file mode 100644
index 0000000..fe0cf35
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/libjingle.sln
@@ -0,0 +1,37 @@
+Microsoft Visual Studio Solution File, Format Version 9.00
+# Visual C++ Express 2005
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libjingle", "libjingle.vcproj", "{DC948D76-8503-490C-A07D-11044004FCE3}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pcp", "examples\pcp\pcp.vcproj", "{EF5C9C17-874A-44C3-93CB-1165F03DBCD3}"
+ ProjectSection(ProjectDependencies) = postProject
+ {DC948D76-8503-490C-A07D-11044004FCE3} = {DC948D76-8503-490C-A07D-11044004FCE3}
+ EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "call", "examples\call\call.vcproj", "{A4132D45-BAE2-40E5-AC7C-C3C44FB24325}"
+ ProjectSection(ProjectDependencies) = postProject
+ {DC948D76-8503-490C-A07D-11044004FCE3} = {DC948D76-8503-490C-A07D-11044004FCE3}
+ EndProjectSection
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Win32 = Debug|Win32
+ Release|Win32 = Release|Win32
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {DC948D76-8503-490C-A07D-11044004FCE3}.Debug|Win32.ActiveCfg = Debug|Win32
+ {DC948D76-8503-490C-A07D-11044004FCE3}.Debug|Win32.Build.0 = Debug|Win32
+ {DC948D76-8503-490C-A07D-11044004FCE3}.Release|Win32.ActiveCfg = Release|Win32
+ {DC948D76-8503-490C-A07D-11044004FCE3}.Release|Win32.Build.0 = Release|Win32
+ {EF5C9C17-874A-44C3-93CB-1165F03DBCD3}.Debug|Win32.ActiveCfg = Debug|Win32
+ {EF5C9C17-874A-44C3-93CB-1165F03DBCD3}.Debug|Win32.Build.0 = Debug|Win32
+ {EF5C9C17-874A-44C3-93CB-1165F03DBCD3}.Release|Win32.ActiveCfg = Release|Win32
+ {EF5C9C17-874A-44C3-93CB-1165F03DBCD3}.Release|Win32.Build.0 = Release|Win32
+ {A4132D45-BAE2-40E5-AC7C-C3C44FB24325}.Debug|Win32.ActiveCfg = Debug|Win32
+ {A4132D45-BAE2-40E5-AC7C-C3C44FB24325}.Debug|Win32.Build.0 = Debug|Win32
+ {A4132D45-BAE2-40E5-AC7C-C3C44FB24325}.Release|Win32.ActiveCfg = Release|Win32
+ {A4132D45-BAE2-40E5-AC7C-C3C44FB24325}.Release|Win32.Build.0 = Release|Win32
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/Plugins/jingle/libjingle/talk/libjingle.vcproj b/Plugins/jingle/libjingle/talk/libjingle.vcproj
new file mode 100644
index 0000000..514bfbc
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/libjingle.vcproj
@@ -0,0 +1,1014 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="8,00"
+ Name="libjingle"
+ ProjectGUID="{DC948D76-8503-490C-A07D-11044004FCE3}"
+ RootNamespace="libjingle"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="4"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories="..;C:\Desenvolvimento\linphone\lin\linphone\mediastreamer2\include;C:\Desenvolvimento\linphone\lin\linphone\oRTP\include"
+ PreprocessorDefinitions="ENABLE_DEBUG;_DEBUG;_WINDOWS;WIN32;UNICODE;_UNICODE;WINVER=0x0500;_WIN32_WINNT=0x500;_SCL_SECURE_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;PRODUCTION_BUILD;PRODUCTION;XML_STATIC;FEATURE_ENABLE_SSL;FEATURE_ENABLE_CHAT_ARCHIVING;FEATURE_ENABLE_VOICEMAIL;NO_ATL;HAVE_SPEEX;HAVE_ILBC"
+ ExceptionHandling="1"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ StructMemberAlignment="0"
+ TreatWChar_tAsBuiltInType="false"
+ DebugInformationFormat="4"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLibrarianTool"
+ IgnoreAllDefaultLibraries="true"
+ IgnoreDefaultLibraryNames=""
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="4"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories="..;C:\Desenvolvimento\linphone\lin\linphone\mediastreamer2\include;C:\Desenvolvimento\linphone\lin\linphone\oRTP\include"
+ PreprocessorDefinitions="_WINDOWS;WIN32;UNICODE;_UNICODE;WINVER=0x0500;_WIN32_WINNT=0x500;_SCL_SECURE_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;PRODUCTION_BUILD;PRODUCTION;_SCL_SECURE_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;XML_STATIC;FEATURE_ENABLE_SSL;FEATURE_ENABLE_CHAT_ARCHIVING;FEATURE_ENABLE_VOICEMAIL;NO_ATL;HAVE_SPEEX;HAVE_ILBC"
+ ExceptionHandling="1"
+ RuntimeLibrary="0"
+ TreatWChar_tAsBuiltInType="false"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLibrarianTool"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Source Files"
+ Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
+ UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
+ >
+ <Filter
+ Name="XMLLite"
+ >
+ <File
+ RelativePath=".\xmllite\qname.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\xmllite\xmlconstants.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\xmllite\xmlelement.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\xmllite\xmlprinter.cc"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="XMPP"
+ >
+ <File
+ RelativePath=".\xmpp\constants.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\xmpp\jid.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\xmpp\ratelimitmanager.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\xmpp\saslmechanism.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\xmllite\xmlnsstack.cc"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="base"
+ >
+ <File
+ RelativePath=".\base\asynchttprequest.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\base\asyncpacketsocket.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\base\asynctcpsocket.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\base\asyncudpsocket.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\base\autodetectproxy.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\base\base64.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\base\bytebuffer.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\base\common.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\base\diskcache.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\base\diskcache_win32.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\base\fileutils.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\base\firewallsocketserver.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\base\helpers.cc"
+ >
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ ObjectFile="$(IntDir)\$(InputName)1.obj"
+ XMLDocumentationFileName="$(IntDir)\$(InputName)1.xdc"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ ObjectFile="$(IntDir)\$(InputName)1.obj"
+ XMLDocumentationFileName="$(IntDir)\$(InputName)1.xdc"
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath=".\base\host.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\base\httpbase.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\base\httpclient.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\base\httpcommon.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\base\httpserver.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\base\logging.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\base\md5c.c"
+ >
+ </File>
+ <File
+ RelativePath=".\base\messagequeue.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\base\network.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\base\pathutils.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\base\physicalsocketserver.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\base\proxydetect.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\base\proxyinfo.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\base\schanneladapter.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\base\signalthread.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\base\socketadapters.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\base\socketaddress.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\base\socketpool.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\base\ssladapter.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\base\stream.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\base\streamutils.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\base\stringdigest.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\base\stringencode.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\base\stringutils.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\base\tarstream.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\base\task.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\base\taskrunner.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\base\thread.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\base\time.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\base\win32filesystem.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\base\win32socketserver.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\base\winfirewall.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\base\winping.cc"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="p2p"
+ >
+ <Filter
+ Name="base"
+ >
+ <File
+ RelativePath=".\p2p\base\constants.cc"
+ >
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ ObjectFile="$(IntDir)\$(InputName)1.obj"
+ XMLDocumentationFileName="$(IntDir)\$(InputName)1.xdc"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ ObjectFile="$(IntDir)\$(InputName)1.obj"
+ XMLDocumentationFileName="$(IntDir)\$(InputName)1.xdc"
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath=".\p2p\base\p2ptransport.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\p2p\base\p2ptransportchannel.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\p2p\base\port.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\p2p\base\pseudotcp.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\p2p\base\rawtransport.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\p2p\base\rawtransportchannel.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\p2p\base\relayport.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\p2p\base\session.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\p2p\base\sessionmanager.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\p2p\base\stun.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\p2p\base\stunport.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\p2p\base\stunrequest.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\p2p\base\tcpport.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\p2p\base\transport.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\p2p\base\transportchannel.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\p2p\base\transportchannelproxy.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\p2p\base\udpport.cc"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="client"
+ >
+ <File
+ RelativePath=".\p2p\client\basicportallocator.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\p2p\client\httpportallocator.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\p2p\client\socketmonitor.cc"
+ >
+ </File>
+ </Filter>
+ </Filter>
+ <Filter
+ Name="session"
+ >
+ <Filter
+ Name="tunnel"
+ >
+ <File
+ RelativePath=".\session\tunnel\tunnelsessionclient.cc"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="phone"
+ >
+ <File
+ RelativePath=".\session\phone\audiomonitor.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\session\phone\call.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\session\phone\channelmanager.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\session\phone\linphonemediaengine.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\session\phone\phonesessionclient.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\session\phone\voicechannel.cc"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="fileshare"
+ >
+ <File
+ RelativePath=".\session\fileshare\fileshare.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\session\fileshare\fileshare.h"
+ >
+ </File>
+ </Filter>
+ </Filter>
+ </Filter>
+ <Filter
+ Name="Header Files"
+ Filter="h;hpp;hxx;hm;inl;inc;xsd"
+ UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
+ >
+ <Filter
+ Name="XMLLite"
+ >
+ <File
+ RelativePath=".\xmllite\qname.h"
+ >
+ </File>
+ <File
+ RelativePath=".\xmllite\xmlconstants.h"
+ >
+ </File>
+ <File
+ RelativePath=".\xmllite\xmlelement.h"
+ >
+ </File>
+ <File
+ RelativePath=".\xmllite\xmlnsstack.h"
+ >
+ </File>
+ <File
+ RelativePath=".\xmllite\xmlprinter.h"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="XMPP"
+ >
+ <File
+ RelativePath=".\xmpp\asyncsocket.h"
+ >
+ </File>
+ <File
+ RelativePath=".\xmpp\constants.h"
+ >
+ </File>
+ <File
+ RelativePath=".\xmpp\jid.h"
+ >
+ </File>
+ <File
+ RelativePath=".\xmpp\plainsaslhandler.h"
+ >
+ </File>
+ <File
+ RelativePath=".\xmpp\prexmppauth.h"
+ >
+ </File>
+ <File
+ RelativePath=".\xmpp\saslcookiemechanism.h"
+ >
+ </File>
+ <File
+ RelativePath=".\xmpp\saslhandler.h"
+ >
+ </File>
+ <File
+ RelativePath=".\xmpp\saslmechanism.h"
+ >
+ </File>
+ <File
+ RelativePath=".\xmpp\saslplainmechanism.h"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="base"
+ >
+ <File
+ RelativePath=".\base\asyncfile.h"
+ >
+ </File>
+ <File
+ RelativePath=".\base\asyncpacketsocket.h"
+ >
+ </File>
+ <File
+ RelativePath=".\base\asyncsocket.h"
+ >
+ </File>
+ <File
+ RelativePath=".\base\asynctcpsocket.h"
+ >
+ </File>
+ <File
+ RelativePath=".\base\asyncudpsocket.h"
+ >
+ </File>
+ <File
+ RelativePath=".\base\base64.h"
+ >
+ </File>
+ <File
+ RelativePath=".\base\basicdefs.h"
+ >
+ </File>
+ <File
+ RelativePath=".\base\basictypes.h"
+ >
+ </File>
+ <File
+ RelativePath=".\base\bytebuffer.h"
+ >
+ </File>
+ <File
+ RelativePath=".\base\byteorder.h"
+ >
+ </File>
+ <File
+ RelativePath=".\base\common.h"
+ >
+ </File>
+ <File
+ RelativePath=".\base\criticalsection.h"
+ >
+ </File>
+ <File
+ RelativePath=".\base\helpers.h"
+ >
+ </File>
+ <File
+ RelativePath=".\base\host.h"
+ >
+ </File>
+ <File
+ RelativePath=".\base\linked_ptr.h"
+ >
+ </File>
+ <File
+ RelativePath=".\base\logging.h"
+ >
+ </File>
+ <File
+ RelativePath=".\base\md5.h"
+ >
+ </File>
+ <File
+ RelativePath=".\base\messagequeue.h"
+ >
+ </File>
+ <File
+ RelativePath=".\base\network.h"
+ >
+ </File>
+ <File
+ RelativePath=".\base\physicalsocketserver.h"
+ >
+ </File>
+ <File
+ RelativePath=".\base\proxyinfo.h"
+ >
+ </File>
+ <File
+ RelativePath=".\base\schanneladapter.h"
+ >
+ </File>
+ <File
+ RelativePath=".\base\scoped_ptr.h"
+ >
+ </File>
+ <File
+ RelativePath=".\base\sec_buffer.h"
+ >
+ </File>
+ <File
+ RelativePath=".\base\sigslot.h"
+ >
+ </File>
+ <File
+ RelativePath=".\base\socket.h"
+ >
+ </File>
+ <File
+ RelativePath=".\base\socketadapters.h"
+ >
+ </File>
+ <File
+ RelativePath=".\base\socketaddress.h"
+ >
+ </File>
+ <File
+ RelativePath=".\base\socketaddresspair.h"
+ >
+ </File>
+ <File
+ RelativePath=".\base\socketfactory.h"
+ >
+ </File>
+ <File
+ RelativePath=".\base\socketserver.h"
+ >
+ </File>
+ <File
+ RelativePath=".\base\ssladapter.h"
+ >
+ </File>
+ <File
+ RelativePath=".\base\stl_decl.h"
+ >
+ </File>
+ <File
+ RelativePath=".\base\stream.h"
+ >
+ </File>
+ <File
+ RelativePath=".\base\stringdigest.h"
+ >
+ </File>
+ <File
+ RelativePath=".\base\stringencode.h"
+ >
+ </File>
+ <File
+ RelativePath=".\base\stringutils.h"
+ >
+ </File>
+ <File
+ RelativePath=".\base\task.h"
+ >
+ </File>
+ <File
+ RelativePath=".\base\taskrunner.h"
+ >
+ </File>
+ <File
+ RelativePath=".\base\thread.h"
+ >
+ </File>
+ <File
+ RelativePath=".\base\time.h"
+ >
+ </File>
+ <File
+ RelativePath=".\base\urlencode.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\base\win32.h"
+ >
+ </File>
+ <File
+ RelativePath=".\base\win32socketserver.h"
+ >
+ </File>
+ <File
+ RelativePath=".\base\winfirewall.h"
+ >
+ </File>
+ <File
+ RelativePath=".\base\winping.h"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="p2p"
+ >
+ <Filter
+ Name="base"
+ >
+ <File
+ RelativePath=".\p2p\base\candidate.h"
+ >
+ </File>
+ <File
+ RelativePath=".\p2p\base\helpers.h"
+ >
+ </File>
+ <File
+ RelativePath=".\p2p\base\p2psocket.h"
+ >
+ </File>
+ <File
+ RelativePath=".\p2p\base\port.h"
+ >
+ </File>
+ <File
+ RelativePath=".\p2p\base\portallocator.h"
+ >
+ </File>
+ <File
+ RelativePath=".\p2p\base\pseudotcp.h"
+ >
+ </File>
+ <File
+ RelativePath=".\p2p\base\relayport.h"
+ >
+ </File>
+ <File
+ RelativePath=".\p2p\base\relayserver.h"
+ >
+ </File>
+ <File
+ RelativePath=".\p2p\base\session.h"
+ >
+ </File>
+ <File
+ RelativePath=".\p2p\base\sessiondescription.h"
+ >
+ </File>
+ <File
+ RelativePath=".\p2p\base\sessionid.h"
+ >
+ </File>
+ <File
+ RelativePath=".\p2p\base\sessionmanager.h"
+ >
+ </File>
+ <File
+ RelativePath=".\p2p\base\sessionmessage.h"
+ >
+ </File>
+ <File
+ RelativePath=".\p2p\base\sessionmessagefactory.h"
+ >
+ </File>
+ <File
+ RelativePath=".\p2p\base\sessionresponsemessage.h"
+ >
+ </File>
+ <File
+ RelativePath=".\p2p\base\socketmanager.h"
+ >
+ </File>
+ <File
+ RelativePath=".\p2p\base\stun.h"
+ >
+ </File>
+ <File
+ RelativePath=".\p2p\base\stunport.h"
+ >
+ </File>
+ <File
+ RelativePath=".\p2p\base\stunrequest.h"
+ >
+ </File>
+ <File
+ RelativePath=".\p2p\base\stunserver.h"
+ >
+ </File>
+ <File
+ RelativePath=".\p2p\base\tcpport.h"
+ >
+ </File>
+ <File
+ RelativePath=".\p2p\base\udpport.h"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="client"
+ >
+ <File
+ RelativePath=".\p2p\client\basicportallocator.h"
+ >
+ </File>
+ <File
+ RelativePath=".\p2p\client\sessionclient.h"
+ >
+ </File>
+ <File
+ RelativePath=".\p2p\client\socketmonitor.h"
+ >
+ </File>
+ <File
+ RelativePath=".\p2p\client\xmppsessionmessage.h"
+ >
+ </File>
+ <File
+ RelativePath=".\p2p\client\xmppsessionmessagefactory.h"
+ >
+ </File>
+ <File
+ RelativePath=".\p2p\client\xmppsessionresponsemessage.h"
+ >
+ </File>
+ </Filter>
+ </Filter>
+ <Filter
+ Name="session"
+ >
+ <File
+ RelativePath=".\session\receiver.h"
+ >
+ </File>
+ <File
+ RelativePath=".\session\sessionsendtask.h"
+ >
+ </File>
+ <Filter
+ Name="tunnel"
+ >
+ <File
+ RelativePath=".\session\tunnel\pseudotcpchannel.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\session\tunnel\tunnelsessionclient.h"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="phone"
+ >
+ <File
+ RelativePath=".\session\phone\audiomonitor.h"
+ >
+ </File>
+ <File
+ RelativePath=".\session\phone\call.h"
+ >
+ </File>
+ <File
+ RelativePath=".\session\phone\channelmanager.h"
+ >
+ </File>
+ <File
+ RelativePath=".\session\phone\linphonemediaengine.h"
+ >
+ </File>
+ <File
+ RelativePath=".\session\phone\mediachannel.h"
+ >
+ </File>
+ <File
+ RelativePath=".\session\phone\mediaengine.h"
+ >
+ </File>
+ <File
+ RelativePath=".\session\phone\phonesessionclient.h"
+ >
+ </File>
+ <File
+ RelativePath=".\session\phone\voicechannel.h"
+ >
+ </File>
+ </Filter>
+ </Filter>
+ </Filter>
+ <Filter
+ Name="Resource Files"
+ Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"
+ UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
+ >
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/Plugins/jingle/libjingle/talk/p2p/base/candidate.h b/Plugins/jingle/libjingle/talk/p2p/base/candidate.h
new file mode 100644
index 0000000..ac41311
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/p2p/base/candidate.h
@@ -0,0 +1,119 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _CANDIDATE_H_
+#define _CANDIDATE_H_
+
+#include <string>
+#include <sstream>
+#include "talk/base/socketaddress.h"
+
+namespace cricket {
+
+// Candidate for ICE based connection discovery.
+
+class Candidate {
+public:
+
+ const std::string & name() const { return name_; }
+ void set_name(const std::string & name) { name_ = name; }
+
+ const std::string & protocol() const { return protocol_; }
+ void set_protocol(const std::string & protocol) { protocol_ = protocol; }
+
+ const talk_base::SocketAddress & address() const { return address_; }
+ void set_address(const talk_base::SocketAddress & address)
+ { address_ = address; }
+
+ const float preference() const { return preference_; }
+ void set_preference(const float preference) { preference_ = preference; }
+ const std::string preference_str() const {
+ std::ostringstream ost;
+ ost << preference_;
+ return ost.str();
+ }
+ void set_preference_str(const std::string & preference) {
+ std::istringstream ist(preference);
+ ist >> preference_;
+ }
+
+ const std::string & username() const { return username_; }
+ void set_username(const std::string & username) { username_ = username; }
+
+ const std::string & password() const { return password_; }
+ void set_password(const std::string & password) { password_ = password; }
+
+ const std::string & type() const { return type_; }
+ void set_type(const std::string & type) { type_ = type; }
+
+ const std::string & network_name() const { return network_name_; }
+ void set_network_name(const std::string & network_name) {
+ network_name_ = network_name;
+ }
+
+ // Candidates in a new generation replace those in the old generation.
+ uint32 generation() const { return generation_; }
+ void set_generation(uint32 generation) { generation_ = generation; }
+ const std::string generation_str() const {
+ std::ostringstream ost;
+ ost << generation_;
+ return ost.str();
+ }
+ void set_generation_str(const std::string& str) {
+ std::istringstream ist(str);
+ ist >> generation_;
+ }
+
+ // Determines whether this candidate is equivalent to the given one.
+ bool IsEquivalent(const Candidate& c) const {
+ // We ignore the network name, since that is just debug information, and
+ // the preference, since that should be the same if the rest is (and it's
+ // a float so equality checking is always worrisome).
+ return (name_ == c.name_) &&
+ (protocol_ == c.protocol_) &&
+ (address_ == c.address_) &&
+ (username_ == c.username_) &&
+ (password_ == c.password_) &&
+ (type_ == c.type_) &&
+ (generation_ == c.generation_);
+ }
+
+private:
+ std::string name_;
+ std::string protocol_;
+ talk_base::SocketAddress address_;
+ float preference_;
+ std::string username_;
+ std::string password_;
+ std::string type_;
+ std::string network_name_;
+ uint32 generation_;
+};
+
+} // namespace cricket
+
+#endif // _CANDIDATE_H_
diff --git a/Plugins/jingle/libjingle/talk/p2p/base/common.h b/Plugins/jingle/libjingle/talk/p2p/base/common.h
new file mode 100644
index 0000000..72f8cfa
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/p2p/base/common.h
@@ -0,0 +1,36 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef CRICKET_P2P_BASE_COMMON_H__
+#define CRICKET_P2P_BASE_COMMON_H__
+
+#include "talk/base/logging.h"
+
+// Common log description format for jingle messages
+#define LOG_J(sev,obj) LOG(sev) << "Jingle:" << obj->ToString() << ": "
+
+#endif // CRICKET_P2P_BASE_COMMON_H__
diff --git a/Plugins/jingle/libjingle/talk/p2p/base/constants.cc b/Plugins/jingle/libjingle/talk/p2p/base/constants.cc
new file mode 100644
index 0000000..13b9f2d
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/p2p/base/constants.cc
@@ -0,0 +1,62 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "talk/p2p/base/constants.h"
+
+namespace cricket {
+
+const std::string NS_EMPTY("");
+const std::string NS_GOOGLESESSION("http://www.google.com/session");
+#ifdef FEATURE_ENABLE_VOICEMAIL
+const std::string NS_GOOGLEVOICEMAIL("http://www.google.com/session/voicemail");
+#endif
+
+const buzz::QName QN_SESSION(true, NS_GOOGLESESSION, "session");
+
+const buzz::QName QN_REDIRECT_TARGET(true, NS_GOOGLESESSION, "target");
+const buzz::QName QN_REDIRECT_COOKIE(true, NS_GOOGLESESSION, "cookie");
+const buzz::QName QN_REDIRECT_REGARDING(true, NS_GOOGLESESSION, "regarding");
+
+#ifdef FEATURE_ENABLE_VOICEMAIL
+const buzz::QName QN_VOICEMAIL_REGARDING(true, NS_GOOGLEVOICEMAIL, "regarding");
+#endif
+
+const buzz::QName QN_INITIATOR(true, NS_EMPTY, "initiator");
+
+const buzz::QName QN_ADDRESS(true, cricket::NS_EMPTY, "address");
+const buzz::QName QN_PORT(true, cricket::NS_EMPTY, "port");
+const buzz::QName QN_NETWORK(true, cricket::NS_EMPTY, "network");
+const buzz::QName QN_GENERATION(true, cricket::NS_EMPTY, "generation");
+const buzz::QName QN_USERNAME(true, cricket::NS_EMPTY, "username");
+const buzz::QName QN_PASSWORD(true, cricket::NS_EMPTY, "password");
+const buzz::QName QN_PREFERENCE(true, cricket::NS_EMPTY, "preference");
+const buzz::QName QN_PROTOCOL(true, cricket::NS_EMPTY, "protocol");
+
+// Legacy transport messages
+const buzz::QName kQnLegacyCandidate(true, cricket::NS_GOOGLESESSION,
+ "candidate");
+} // namespace cricket
diff --git a/Plugins/jingle/libjingle/talk/p2p/base/constants.h b/Plugins/jingle/libjingle/talk/p2p/base/constants.h
new file mode 100644
index 0000000..46315eb
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/p2p/base/constants.h
@@ -0,0 +1,70 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _CRICKET_P2P_BASE_CONSTANTS_H_
+#define _CRICKET_P2P_BASE_CONSTANTS_H_
+
+#include <string>
+#include "talk/xmllite/qname.h"
+
+// This file contains constants related to signaling that are used in various
+// classes in this directory.
+
+namespace cricket {
+
+extern const std::string NS_EMPTY;
+extern const std::string NS_GOOGLESESSION;
+#ifdef FEATURE_ENABLE_VOICEMAIL
+extern const std::string NS_GOOGLEVOICEMAIL;
+#endif
+
+extern const buzz::QName QN_SESSION;
+
+extern const buzz::QName QN_REDIRECT_TARGET;
+extern const buzz::QName QN_REDIRECT_COOKIE;
+extern const buzz::QName QN_REDIRECT_REGARDING;
+#ifdef FEATURE_ENABLE_VOICEMAIL
+extern const buzz::QName QN_VOICEMAIL_REGARDING;
+#endif
+
+extern const buzz::QName QN_INITIATOR;
+
+extern const buzz::QName QN_ADDRESS;
+extern const buzz::QName QN_PORT;
+extern const buzz::QName QN_NETWORK;
+extern const buzz::QName QN_GENERATION;
+extern const buzz::QName QN_USERNAME;
+extern const buzz::QName QN_PASSWORD;
+extern const buzz::QName QN_PREFERENCE;
+extern const buzz::QName QN_PROTOCOL;
+
+// Legacy transport messages
+extern const buzz::QName kQnLegacyCandidate;
+
+} // namespace cricket
+
+#endif // _CRICKET_P2P_BASE_CONSTANTS_H_
diff --git a/Plugins/jingle/libjingle/talk/p2p/base/p2ptransport.cc b/Plugins/jingle/libjingle/talk/p2p/base/p2ptransport.cc
new file mode 100644
index 0000000..2221d8c
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/p2p/base/p2ptransport.cc
@@ -0,0 +1,209 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "talk/p2p/base/p2ptransport.h"
+#include "talk/base/common.h"
+#include "talk/p2p/base/candidate.h"
+#include "talk/p2p/base/constants.h"
+#include "talk/base/helpers.h"
+#include "talk/p2p/base/p2ptransportchannel.h"
+#include "talk/p2p/base/sessionmanager.h"
+#include "talk/xmllite/qname.h"
+#include "talk/xmllite/xmlelement.h"
+#include "talk/xmpp/constants.h"
+
+namespace {
+
+// We only allow usernames to be this many characters or fewer.
+const size_t kMaxUsernameSize = 16;
+
+} // namespace
+
+namespace cricket {
+
+const std::string kNsP2pTransport("http://www.google.com/transport/p2p");
+const buzz::QName kQnP2pTransport(true, kNsP2pTransport, "transport");
+const buzz::QName kQnP2pCandidate(true, kNsP2pTransport, "candidate");
+const buzz::QName kQnP2pUnknownChannelName(true, kNsP2pTransport,
+ "unknown-channel-name");
+
+P2PTransport::P2PTransport(SessionManager* session_manager)
+ : Transport(session_manager, kNsP2pTransport) {
+}
+
+P2PTransport::~P2PTransport() {
+ DestroyAllChannels();
+}
+
+buzz::XmlElement* P2PTransport::CreateTransportOffer() {
+ return new buzz::XmlElement(kQnP2pTransport, true);
+}
+
+buzz::XmlElement* P2PTransport::CreateTransportAnswer() {
+ return new buzz::XmlElement(kQnP2pTransport, true);
+}
+
+bool P2PTransport::OnTransportOffer(const buzz::XmlElement* elem) {
+ ASSERT(elem->Name() == kQnP2pTransport);
+ // We don't support any options, so we ignore them.
+ return true;
+}
+
+bool P2PTransport::OnTransportAnswer(const buzz::XmlElement* elem) {
+ ASSERT(elem->Name() == kQnP2pTransport);
+ // We don't support any options. We fail if any are given. The other side
+ // should know from our request that we expected an empty response.
+ return elem->FirstChild() == NULL;
+}
+
+bool P2PTransport::OnTransportMessage(const buzz::XmlElement* msg,
+ const buzz::XmlElement* stanza) {
+ ASSERT(msg->Name() == kQnP2pTransport);
+ for (const buzz::XmlElement* elem = msg->FirstElement();
+ elem != NULL;
+ elem = elem->NextElement()) {
+ if (elem->Name() == kQnP2pCandidate) {
+ // Make sure this candidate is valid.
+ Candidate candidate;
+ if (!ParseCandidate(stanza, elem, &candidate))
+ return false;
+
+ ForwardChannelMessage(elem->Attr(buzz::QN_NAME),
+ new buzz::XmlElement(*elem));
+ }
+ }
+ return true;
+}
+
+bool P2PTransport::OnTransportError(const buzz::XmlElement* session_msg,
+ const buzz::XmlElement* error) {
+ ASSERT(error->Name().Namespace() == kNsP2pTransport);
+ if ((error->Name() == kQnP2pUnknownChannelName)
+ && error->HasAttr(buzz::QN_NAME)) {
+ std::string channel_name = error->Attr(buzz::QN_NAME);
+ if (HasChannel(channel_name)) {
+ SignalChannelGone(this, channel_name);
+ }
+ }
+ return true;
+}
+
+void P2PTransport::OnTransportChannelMessages(
+ const std::vector<buzz::XmlElement*>& candidates) {
+ buzz::XmlElement* transport =
+ new buzz::XmlElement(kQnP2pTransport, true);
+ for (size_t i = 0; i < candidates.size(); ++i)
+ transport->AddElement(candidates[i]);
+
+ std::vector<buzz::XmlElement*> elems;
+ elems.push_back(transport);
+ SignalTransportMessage(this, elems);
+}
+
+bool P2PTransport::ParseCandidate(const buzz::XmlElement* stanza,
+ const buzz::XmlElement* elem,
+ Candidate* candidate) {
+ // Check for all of the required attributes.
+ if (!elem->HasAttr(buzz::QN_NAME) ||
+ !elem->HasAttr(QN_ADDRESS) ||
+ !elem->HasAttr(QN_PORT) ||
+ !elem->HasAttr(QN_USERNAME) ||
+ !elem->HasAttr(QN_PREFERENCE) ||
+ !elem->HasAttr(QN_PROTOCOL) ||
+ !elem->HasAttr(QN_GENERATION)) {
+ return BadRequest(stanza, "candidate missing required attribute", NULL);
+ }
+
+ // Make sure the channel named actually exists.
+ if (!HasChannel(elem->Attr(buzz::QN_NAME))) {
+ scoped_ptr<buzz::XmlElement>
+ extra_info(new buzz::XmlElement(kQnP2pUnknownChannelName));
+ extra_info->AddAttr(buzz::QN_NAME, elem->Attr(buzz::QN_NAME));
+ return BadRequest(stanza, "channel named in candidate does not exist",
+ extra_info.get());
+ }
+
+ // Parse the address given.
+ talk_base::SocketAddress address;
+ if (!ParseAddress(stanza, elem, &address))
+ return false;
+
+ candidate->set_name(elem->Attr(buzz::QN_NAME));
+ candidate->set_address(address);
+ candidate->set_username(elem->Attr(QN_USERNAME));
+ candidate->set_preference_str(elem->Attr(QN_PREFERENCE));
+ candidate->set_protocol(elem->Attr(QN_PROTOCOL));
+ candidate->set_generation_str(elem->Attr(QN_GENERATION));
+
+ // Check that the username is not too long and does not use any bad chars.
+ if (candidate->username().size() > kMaxUsernameSize)
+ return BadRequest(stanza, "candidate username is too long", NULL);
+ if (!IsBase64Encoded(candidate->username()))
+ return BadRequest(stanza,
+ "candidate username has non-base64 encoded characters",
+ NULL);
+
+ // Look for the non-required attributes.
+ if (elem->HasAttr(QN_PASSWORD))
+ candidate->set_password(elem->Attr(QN_PASSWORD));
+ if (elem->HasAttr(buzz::QN_TYPE))
+ candidate->set_type(elem->Attr(buzz::QN_TYPE));
+ if (elem->HasAttr(QN_NETWORK))
+ candidate->set_network_name(elem->Attr(QN_NETWORK));
+
+ return true;
+}
+
+buzz::XmlElement* P2PTransport::TranslateCandidate(const Candidate& c) {
+ buzz::XmlElement* candidate = new buzz::XmlElement(kQnP2pCandidate);
+ candidate->SetAttr(buzz::QN_NAME, c.name());
+ candidate->SetAttr(QN_ADDRESS, c.address().IPAsString());
+ candidate->SetAttr(QN_PORT, c.address().PortAsString());
+ candidate->SetAttr(QN_PREFERENCE, c.preference_str());
+ candidate->SetAttr(QN_USERNAME, c.username());
+ candidate->SetAttr(QN_PROTOCOL, c.protocol());
+ candidate->SetAttr(QN_GENERATION, c.generation_str());
+ if (c.password().size() > 0)
+ candidate->SetAttr(QN_PASSWORD, c.password());
+ if (c.type().size() > 0)
+ candidate->SetAttr(buzz::QN_TYPE, c.type());
+ if (c.network_name().size() > 0)
+ candidate->SetAttr(QN_NETWORK, c.network_name());
+ return candidate;
+}
+
+TransportChannelImpl* P2PTransport::CreateTransportChannel(
+ const std::string& name, const std::string &session_type) {
+ return new P2PTransportChannel(
+ name, session_type, this, session_manager()->port_allocator());
+}
+
+void P2PTransport::DestroyTransportChannel(TransportChannelImpl* channel) {
+ delete channel;
+}
+
+} // namespace cricket
diff --git a/Plugins/jingle/libjingle/talk/p2p/base/p2ptransport.h b/Plugins/jingle/libjingle/talk/p2p/base/p2ptransport.h
new file mode 100644
index 0000000..2027d4a
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/p2p/base/p2ptransport.h
@@ -0,0 +1,86 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _CRICKET_P2P_BASE_P2PTRANSPORT_H_
+#define _CRICKET_P2P_BASE_P2PTRANSPORT_H_
+
+#include "talk/p2p/base/transport.h"
+
+namespace cricket {
+
+class Candidate;
+
+// Xml names used to name this transport and create our elements
+extern const std::string kNsP2pTransport;
+extern const buzz::QName kQnP2pTransport;
+extern const buzz::QName kQnP2pCandidate;
+
+class P2PTransport: public Transport {
+ public:
+ P2PTransport(SessionManager* session_manager);
+ virtual ~P2PTransport();
+
+ // Implements negotiation of the P2P protocol.
+ virtual buzz::XmlElement* CreateTransportOffer();
+ virtual buzz::XmlElement* CreateTransportAnswer();
+ virtual bool OnTransportOffer(const buzz::XmlElement* elem);
+ virtual bool OnTransportAnswer(const buzz::XmlElement* elem);
+
+ // Forwards each candidate message to the appropriate channel.
+ virtual bool OnTransportMessage(const buzz::XmlElement* msg,
+ const buzz::XmlElement* stanza);
+ virtual bool OnTransportError(const buzz::XmlElement* session_msg,
+ const buzz::XmlElement* error);
+
+ protected:
+ // Creates and destroys P2PTransportChannel.
+ virtual TransportChannelImpl* CreateTransportChannel(const std::string& name, const std::string &session_type);
+ virtual void DestroyTransportChannel(TransportChannelImpl* channel);
+
+ // Sends a given set of channel messages, which each describe a candidate,
+ // to the other client as a single transport message.
+ void OnTransportChannelMessages(
+ const std::vector<buzz::XmlElement*>& candidates);
+
+ private:
+ // Attempts to parse the given XML into a candidate. Returns true if the
+ // XML is valid. If not, we will signal an error.
+ bool ParseCandidate(const buzz::XmlElement* stanza,
+ const buzz::XmlElement* elem,
+ Candidate* candidate);
+
+ // Generates a XML element describing the given candidate.
+ buzz::XmlElement* TranslateCandidate(const Candidate& c);
+
+ friend class P2PTransportChannel;
+
+ DISALLOW_EVIL_CONSTRUCTORS(P2PTransport);
+};
+
+} // namespace cricket
+
+#endif // _CRICKET_P2P_BASE_P2PTRANSPORT_H_
diff --git a/Plugins/jingle/libjingle/talk/p2p/base/p2ptransportchannel.cc b/Plugins/jingle/libjingle/talk/p2p/base/p2ptransportchannel.cc
new file mode 100644
index 0000000..717ae70
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/p2p/base/p2ptransportchannel.cc
@@ -0,0 +1,911 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#if defined(_MSC_VER) && _MSC_VER < 1300
+#pragma warning(disable:4786)
+#endif
+
+#include <errno.h>
+
+#include <iostream>
+
+#include "talk/base/common.h"
+#include "talk/base/logging.h"
+#include "talk/p2p/base/common.h"
+#include "talk/p2p/base/p2ptransportchannel.h"
+
+namespace {
+
+// messages for queuing up work for ourselves
+const uint32 MSG_SORT = 1;
+const uint32 MSG_PING = 2;
+const uint32 MSG_ALLOCATE = 3;
+
+// When the socket is unwritable, we will use 10 Kbps (ignoring IP+UDP headers)
+// for pinging. When the socket is writable, we will use only 1 Kbps because
+// we don't want to degrade the quality on a modem. These numbers should work
+// well on a 28.8K modem, which is the slowest connection on which the voice
+// quality is reasonable at all.
+static const uint32 PING_PACKET_SIZE = 60 * 8;
+static const uint32 WRITABLE_DELAY = 1000 * PING_PACKET_SIZE / 1000; // 480ms
+static const uint32 UNWRITABLE_DELAY = 1000 * PING_PACKET_SIZE / 10000;// 50ms
+
+// If there is a current writable connection, then we will also try hard to
+// make sure it is pinged at this rate.
+static const uint32 MAX_CURRENT_WRITABLE_DELAY = 900; // 2*WRITABLE_DELAY - bit
+
+// The minimum improvement in MOS that justifies a switch.
+static const double kMinImprovement = 10;
+
+// Amount of time that we wait when *losing* writability before we try doing
+// another allocation.
+static const int kAllocateDelay = 1 * 1000; // 1 second
+
+// We will try creating a new allocator from scratch after a delay of this
+// length without becoming writable (or timing out).
+static const int kAllocatePeriod = 20 * 1000; // 20 seconds
+
+cricket::Port::CandidateOrigin GetOrigin(cricket::Port* port,
+ cricket::Port* origin_port) {
+ if (!origin_port)
+ return cricket::Port::ORIGIN_MESSAGE;
+ else if (port == origin_port)
+ return cricket::Port::ORIGIN_THIS_PORT;
+ else
+ return cricket::Port::ORIGIN_OTHER_PORT;
+}
+
+// Compares two connections based only on static information about them.
+int CompareConnectionCandidates(cricket::Connection* a,
+ cricket::Connection* b) {
+ // Combine local and remote preferences
+ ASSERT(a->local_candidate().preference() == a->port()->preference());
+ ASSERT(b->local_candidate().preference() == b->port()->preference());
+ double a_pref = a->local_candidate().preference()
+ * a->remote_candidate().preference();
+ double b_pref = b->local_candidate().preference()
+ * b->remote_candidate().preference();
+
+ // Now check combined preferences. Lower values get sorted last.
+ if (a_pref > b_pref)
+ return 1;
+ if (a_pref < b_pref)
+ return -1;
+
+ return 0;
+}
+
+// Compare two connections based on their writability and static preferences.
+int CompareConnections(cricket::Connection *a, cricket::Connection *b) {
+ // Sort based on write-state. Better states have lower values.
+ if (a->write_state() < b->write_state())
+ return 1;
+ if (a->write_state() > b->write_state())
+ return -1;
+
+ // Compare the candidate information.
+ return CompareConnectionCandidates(a, b);
+}
+
+// Wraps the comparison connection into a less than operator that puts higher
+// priority writable connections first.
+class ConnectionCompare {
+public:
+ bool operator()(const cricket::Connection *ca,
+ const cricket::Connection *cb) {
+ cricket::Connection* a = const_cast<cricket::Connection*>(ca);
+ cricket::Connection* b = const_cast<cricket::Connection*>(cb);
+
+ // Compare first on writability and static preferences.
+ int cmp = CompareConnections(a, b);
+ if (cmp > 0)
+ return true;
+ if (cmp < 0)
+ return false;
+
+ // Otherwise, sort based on latency estimate.
+ return a->rtt() < b->rtt();
+
+ // Should we bother checking for the last connection that last received
+ // data? It would help rendezvous on the connection that is also receiving
+ // packets.
+ //
+ // TODO: Yes we should definitely do this. The TCP protocol gains
+ // efficiency by being used bidirectionally, as opposed to two separate
+ // unidirectional streams. This test should probably occur before
+ // comparison of local prefs (assuming combined prefs are the same). We
+ // need to be careful though, not to bounce back and forth with both sides
+ // trying to rendevous with the other.
+ }
+};
+
+// Determines whether we should switch between two connections, based first on
+// static preferences and then (if those are equal) on latency estimates.
+bool ShouldSwitch(cricket::Connection* a_conn, cricket::Connection* b_conn) {
+ if (a_conn == b_conn)
+ return false;
+
+ if ((a_conn == NULL) || (b_conn == NULL)) // don't think the latter should happen
+ return true;
+
+ int prefs_cmp = CompareConnections(a_conn, b_conn);
+ if (prefs_cmp < 0)
+ return true;
+ if (prefs_cmp > 0)
+ return false;
+
+ return b_conn->rtt() <= a_conn->rtt() + kMinImprovement;
+}
+
+} // unnamed namespace
+
+namespace cricket {
+
+P2PTransportChannel::P2PTransportChannel(const std::string &name,
+ const std::string &session_type,
+ P2PTransport* transport,
+ PortAllocator *allocator)
+: TransportChannelImpl(name, session_type), transport_(transport),
+ allocator_(allocator), worker_thread_(talk_base::Thread::Current()),
+ waiting_for_signaling_(false), error_(0), best_connection_(NULL),
+ pinging_started_(false), sort_dirty_(false), was_writable_(false),
+ was_timed_out_(true) {
+}
+
+P2PTransportChannel::~P2PTransportChannel() {
+ ASSERT(worker_thread_ == talk_base::Thread::Current());
+
+ for (uint32 i = 0; i < allocator_sessions_.size(); ++i)
+ delete allocator_sessions_[i];
+}
+
+// Add the allocator session to our list so that we know which sessions
+// are still active.
+void P2PTransportChannel::AddAllocatorSession(PortAllocatorSession* session) {
+ session->set_generation(static_cast<uint32>(allocator_sessions_.size()));
+ allocator_sessions_.push_back(session);
+
+ // We now only want to apply new candidates that we receive to the ports
+ // created by this new session because these are replacing those of the
+ // previous sessions.
+ ports_.clear();
+
+ session->SignalPortReady.connect(this, &P2PTransportChannel::OnPortReady);
+ session->SignalCandidatesReady.connect(
+ this, &P2PTransportChannel::OnCandidatesReady);
+ session->GetInitialPorts();
+ if (pinging_started_)
+ session->StartGetAllPorts();
+}
+
+// Go into the state of processing candidates, and running in general
+void P2PTransportChannel::Connect() {
+ ASSERT(worker_thread_ == talk_base::Thread::Current());
+
+ // Kick off an allocator session
+ OnAllocate();
+
+ // Start pinging as the ports come in.
+ thread()->Post(this, MSG_PING);
+}
+
+// Reset the socket, clear up any previous allocations and start over
+void P2PTransportChannel::Reset() {
+ ASSERT(worker_thread_ == talk_base::Thread::Current());
+
+ // Get rid of all the old allocators. This should clean up everything.
+ for (uint32 i = 0; i < allocator_sessions_.size(); ++i)
+ delete allocator_sessions_[i];
+
+ allocator_sessions_.clear();
+ ports_.clear();
+ connections_.clear();
+ best_connection_ = NULL;
+
+ // Forget about all of the candidates we got before.
+ remote_candidates_.clear();
+
+ // Revert to the initial state.
+ set_readable(false);
+ set_writable(false);
+
+ // Reinitialize the rest of our state.
+ waiting_for_signaling_ = false;
+ pinging_started_ = false;
+ sort_dirty_ = false;
+ was_writable_ = false;
+ was_timed_out_ = true;
+
+ // If we allocated before, start a new one now.
+ if (transport_->connect_requested())
+ OnAllocate();
+
+ // Start pinging as the ports come in.
+ thread()->Clear(this);
+ thread()->Post(this, MSG_PING);
+}
+
+// A new port is available, attempt to make connections for it
+void P2PTransportChannel::OnPortReady(PortAllocatorSession *session,
+ Port* port) {
+ ASSERT(worker_thread_ == talk_base::Thread::Current());
+
+ // Set in-effect options on the new port
+ for (OptionMap::const_iterator it = options_.begin();
+ it != options_.end();
+ ++it) {
+ int val = port->SetOption(it->first, it->second);
+ if (val < 0) {
+ LOG_J(LS_WARNING, port) << "SetOption(" << it->first
+ << ", " << it->second
+ << ") failed: " << port->GetError();
+ }
+ }
+
+ // Remember the ports and candidates, and signal that candidates are ready.
+ // The session will handle this, and send an initiate/accept/modify message
+ // if one is pending.
+
+ ports_.push_back(port);
+ port->SignalUnknownAddress.connect(
+ this, &P2PTransportChannel::OnUnknownAddress);
+ port->SignalDestroyed.connect(this, &P2PTransportChannel::OnPortDestroyed);
+
+ // Attempt to create a connection from this new port to all of the remote
+ // candidates that we were given so far.
+
+ std::vector<RemoteCandidate>::iterator iter;
+ for (iter = remote_candidates_.begin(); iter != remote_candidates_.end();
+ ++iter)
+ CreateConnection(port, *iter, iter->origin_port(), false);
+
+ SortConnections();
+}
+
+// A new candidate is available, let listeners know
+void P2PTransportChannel::OnCandidatesReady(
+ PortAllocatorSession *session, const std::vector<Candidate>& candidates) {
+ for (size_t i = 0; i < candidates.size(); ++i) {
+ buzz::XmlElement* msg = transport_->TranslateCandidate(candidates[i]);
+ SignalChannelMessage(this, msg);
+ }
+}
+
+// Handle stun packets
+void P2PTransportChannel::OnUnknownAddress(
+ Port *port, const talk_base::SocketAddress &address, StunMessage *stun_msg,
+ const std::string &remote_username) {
+ ASSERT(worker_thread_ == talk_base::Thread::Current());
+
+ // Port has received a valid stun packet from an address that no Connection
+ // is currently available for. See if the remote user name is in the remote
+ // candidate list. If it isn't return error to the stun request.
+
+ const Candidate *candidate = NULL;
+ std::vector<RemoteCandidate>::iterator it;
+ for (it = remote_candidates_.begin(); it != remote_candidates_.end(); ++it) {
+ if ((*it).username() == remote_username) {
+ candidate = &(*it);
+ break;
+ }
+ }
+ if (candidate == NULL) {
+ // Don't know about this username, the request is bogus
+ // This sometimes happens if a binding response comes in before the ACCEPT
+ // message. It is totally valid; the retry state machine will try again.
+
+ port->SendBindingErrorResponse(stun_msg, address,
+ STUN_ERROR_STALE_CREDENTIALS, STUN_ERROR_REASON_STALE_CREDENTIALS);
+ delete stun_msg;
+ return;
+ }
+
+ // Check for connectivity to this address. Create connections
+ // to this address across all local ports. First, add this as a new remote
+ // address
+
+ Candidate new_remote_candidate = *candidate;
+ new_remote_candidate.set_address(address);
+ //new_remote_candidate.set_protocol(port->protocol());
+
+ // This remote username exists. Now create connections using this candidate,
+ // and resort
+
+ if (CreateConnections(new_remote_candidate, port, true)) {
+ // Send the pinger a successful stun response.
+ port->SendBindingResponse(stun_msg, address);
+
+ // Update the list of connections since we just added another. We do this
+ // after sending the response since it could (in principle) delete the
+ // connection in question.
+ SortConnections();
+ } else {
+ // Hopefully this won't occur, because changing a destination address
+ // shouldn't cause a new connection to fail
+ ASSERT(false);
+ port->SendBindingErrorResponse(stun_msg, address, STUN_ERROR_SERVER_ERROR,
+ STUN_ERROR_REASON_SERVER_ERROR);
+ }
+
+ delete stun_msg;
+}
+
+// We received a candidate from the other side, make connections so we
+// can try to use these remote candidates with our local candidates.
+void P2PTransportChannel::OnChannelMessage(const buzz::XmlElement* msg) {
+ ASSERT(worker_thread_ == talk_base::Thread::Current());
+
+ Candidate remote_candidate;
+ bool valid = transport_->ParseCandidate(NULL, msg, &remote_candidate);
+ ASSERT(valid);
+
+ // Create connections to this remote candidate.
+ CreateConnections(remote_candidate, NULL, false);
+
+ // Resort the connections list, which may have new elements.
+ SortConnections();
+}
+
+// Creates connections from all of the ports that we care about to the given
+// remote candidate. The return value is true iff we created a connection from
+// the origin port.
+bool P2PTransportChannel::CreateConnections(const Candidate &remote_candidate,
+ Port* origin_port,
+ bool readable) {
+ ASSERT(worker_thread_ == talk_base::Thread::Current());
+
+ // Add a new connection for this candidate to every port that allows such a
+ // connection (i.e., if they have compatible protocols) and that does not
+ // already have a connection to an equivalent candidate. We must be careful
+ // to make sure that the origin port is included, even if it was pruned,
+ // since that may be the only port that can create this connection.
+
+ bool created = false;
+
+ std::vector<Port *>::reverse_iterator it;
+ for (it = ports_.rbegin(); it != ports_.rend(); ++it) {
+ if (CreateConnection(*it, remote_candidate, origin_port, readable)) {
+ if (*it == origin_port)
+ created = true;
+ }
+ }
+
+ if ((origin_port != NULL) &&
+ find(ports_.begin(), ports_.end(), origin_port) == ports_.end()) {
+ if (CreateConnection(origin_port, remote_candidate, origin_port, readable))
+ created = true;
+ }
+
+ // Remember this remote candidate so that we can add it to future ports.
+ RememberRemoteCandidate(remote_candidate, origin_port);
+
+ return created;
+}
+
+// Setup a connection object for the local and remote candidate combination.
+// And then listen to connection object for changes.
+bool P2PTransportChannel::CreateConnection(Port* port,
+ const Candidate& remote_candidate,
+ Port* origin_port,
+ bool readable) {
+ // Look for an existing connection with this remote address. If one is not
+ // found, then we can create a new connection for this address.
+ Connection* connection = port->GetConnection(remote_candidate.address());
+ if (connection != NULL) {
+ // It is not legal to try to change any of the parameters of an existing
+ // connection; however, the other side can send a duplicate candidate.
+ if (!remote_candidate.IsEquivalent(connection->remote_candidate())) {
+ LOG(INFO) << "Attempt to change a remote candidate";
+ return false;
+ }
+ } else {
+ Port::CandidateOrigin origin = GetOrigin(port, origin_port);
+ connection = port->CreateConnection(remote_candidate, origin);
+ if (!connection)
+ return false;
+
+ connections_.push_back(connection);
+ connection->SignalReadPacket.connect(
+ this, &P2PTransportChannel::OnReadPacket);
+ connection->SignalStateChange.connect(
+ this, &P2PTransportChannel::OnConnectionStateChange);
+ connection->SignalDestroyed.connect(
+ this, &P2PTransportChannel::OnConnectionDestroyed);
+ }
+
+ // If we are readable, it is because we are creating this in response to a
+ // ping from the other side. This will cause the state to become readable.
+ if (readable)
+ connection->ReceivedPing();
+
+ return true;
+}
+
+// Maintain our remote candidate list, adding this new remote one.
+void P2PTransportChannel::RememberRemoteCandidate(
+ const Candidate& remote_candidate, Port* origin_port) {
+ // Remove any candidates whose generation is older than this one. The
+ // presence of a new generation indicates that the old ones are not useful.
+ uint32 i = 0;
+ while (i < remote_candidates_.size()) {
+ if (remote_candidates_[i].generation() < remote_candidate.generation()) {
+ LOG(INFO) << "Pruning candidate from old generation: "
+ << remote_candidates_[i].address().ToString();
+ remote_candidates_.erase(remote_candidates_.begin() + i);
+ } else {
+ i += 1;
+ }
+ }
+
+ // Make sure this candidate is not a duplicate.
+ for (uint32 i = 0; i < remote_candidates_.size(); ++i) {
+ if (remote_candidates_[i].IsEquivalent(remote_candidate)) {
+ LOG(INFO) << "Duplicate candidate: "
+ << remote_candidate.address().ToString();
+ return;
+ }
+ }
+
+ // Try this candidate for all future ports.
+ remote_candidates_.push_back(RemoteCandidate(remote_candidate, origin_port));
+
+ // We have some candidates from the other side, we are now serious about
+ // this connection. Let's do the StartGetAllPorts thing.
+ if (!pinging_started_) {
+ pinging_started_ = true;
+ for (size_t i = 0; i < allocator_sessions_.size(); ++i) {
+ if (!allocator_sessions_[i]->IsGettingAllPorts())
+ allocator_sessions_[i]->StartGetAllPorts();
+ }
+ }
+}
+
+// Send data to the other side, using our best connection
+int P2PTransportChannel::SendPacket(const char *data, size_t len) {
+ // This can get called on any thread that is convenient to write from!
+ if (best_connection_ == NULL) {
+ error_ = EWOULDBLOCK;
+ return SOCKET_ERROR;
+ }
+ int sent = best_connection_->Send(data, len);
+ if (sent <= 0) {
+ ASSERT(sent < 0);
+ error_ = best_connection_->GetError();
+ }
+ return sent;
+}
+
+// Monitor connection states
+void P2PTransportChannel::UpdateConnectionStates() {
+ uint32 now = talk_base::Time();
+
+ // We need to copy the list of connections since some may delete themselves
+ // when we call UpdateState.
+ for (uint32 i = 0; i < connections_.size(); ++i)
+ connections_[i]->UpdateState(now);
+}
+
+// Prepare for best candidate sorting
+void P2PTransportChannel::RequestSort() {
+ if (!sort_dirty_) {
+ worker_thread_->Post(this, MSG_SORT);
+ sort_dirty_ = true;
+ }
+}
+
+// Sort the available connections to find the best one. We also monitor
+// the number of available connections and the current state so that we
+// can possibly kick off more allocators (for more connections).
+void P2PTransportChannel::SortConnections() {
+ ASSERT(worker_thread_ == talk_base::Thread::Current());
+
+ // Make sure the connection states are up-to-date since this affects how they
+ // will be sorted.
+ UpdateConnectionStates();
+
+ // Any changes after this point will require a re-sort.
+ sort_dirty_ = false;
+
+ // Get a list of the networks that we are using.
+ std::set<talk_base::Network*> networks;
+ for (uint32 i = 0; i < connections_.size(); ++i)
+ networks.insert(connections_[i]->port()->network());
+
+ // Find the best alternative connection by sorting. It is important to note
+ // that amongst equal preference, writable connections, this will choose the
+ // one whose estimated latency is lowest. So it is the only one that we
+ // need to consider switching to.
+
+ ConnectionCompare cmp;
+ std::stable_sort(connections_.begin(), connections_.end(), cmp);
+ Connection* top_connection = NULL;
+ if (connections_.size() > 0)
+ top_connection = connections_[0];
+
+ // If necessary, switch to the new choice.
+ if (ShouldSwitch(best_connection_, top_connection))
+ SwitchBestConnectionTo(top_connection);
+
+ // We can prune any connection for which there is a writable connection on
+ // the same network with better or equal prefences. We leave those with
+ // better preference just in case they become writable later (at which point,
+ // we would prune out the current best connection). We leave connections on
+ // other networks because they may not be using the same resources and they
+ // may represent very distinct paths over which we can switch.
+ std::set<talk_base::Network*>::iterator network;
+ for (network = networks.begin(); network != networks.end(); ++network) {
+ Connection* primier = GetBestConnectionOnNetwork(*network);
+ if (!primier || (primier->write_state() != Connection::STATE_WRITABLE))
+ continue;
+
+ for (uint32 i = 0; i < connections_.size(); ++i) {
+ if ((connections_[i] != primier) &&
+ (connections_[i]->port()->network() == *network) &&
+ (CompareConnectionCandidates(primier, connections_[i]) >= 0)) {
+ connections_[i]->Prune();
+ }
+ }
+ }
+
+ // Count the number of connections in the various states.
+
+ int writable = 0;
+ int write_connect = 0;
+ int write_timeout = 0;
+
+ for (uint32 i = 0; i < connections_.size(); ++i) {
+ switch (connections_[i]->write_state()) {
+ case Connection::STATE_WRITABLE:
+ ++writable;
+ break;
+ case Connection::STATE_WRITE_CONNECT:
+ ++write_connect;
+ break;
+ case Connection::STATE_WRITE_TIMEOUT:
+ ++write_timeout;
+ break;
+ default:
+ ASSERT(false);
+ }
+ }
+
+ if (writable > 0) {
+ HandleWritable();
+ } else if (write_connect > 0) {
+ HandleNotWritable();
+ } else {
+ HandleAllTimedOut();
+ }
+
+ // Update the state of this channel. This method is called whenever the
+ // state of any connection changes, so this is a good place to do this.
+ UpdateChannelState();
+
+ // Notify of connection state change
+ SignalConnectionMonitor(this);
+}
+
+// Track the best connection, and let listeners know
+void P2PTransportChannel::SwitchBestConnectionTo(Connection* conn) {
+ // Note: the previous best_connection_ may be destroyed by now, so don't
+ // use it.
+ best_connection_ = conn;
+ if (best_connection_) {
+ LOG_J(LS_VERBOSE, this) << "New best connection: " << conn->ToString();
+ SignalRouteChange(this, best_connection_->remote_candidate().address());
+ }
+}
+
+void P2PTransportChannel::UpdateChannelState() {
+ // The Handle* functions already set the writable state. We'll just double-
+ // check it here.
+ bool writable =
+ (best_connection_ != NULL) &&
+ (best_connection_->write_state() == Connection::STATE_WRITABLE);
+ ASSERT(writable == this->writable());
+
+ bool readable = false;
+ for (uint32 i = 0; i < connections_.size(); ++i) {
+ if (connections_[i]->read_state() == Connection::STATE_READABLE)
+ readable = true;
+ }
+ set_readable(readable);
+}
+
+// We checked the status of our connections and we had at least one that
+// was writable, go into the writable state.
+void P2PTransportChannel::HandleWritable() {
+ //
+ // One or more connections writable!
+ //
+ if (!writable()) {
+ for (uint32 i = 0; i < allocator_sessions_.size(); ++i) {
+ if (allocator_sessions_[i]->IsGettingAllPorts()) {
+ allocator_sessions_[i]->StopGetAllPorts();
+ }
+ }
+
+ // Stop further allocations.
+ thread()->Clear(this, MSG_ALLOCATE);
+ }
+
+ // We're writable, obviously we aren't timed out
+ was_writable_ = true;
+ was_timed_out_ = false;
+ set_writable(true);
+}
+
+// We checked the status of our connections and we didn't have any that
+// were writable, go into the connecting state (kick off a new allocator
+// session).
+void P2PTransportChannel::HandleNotWritable() {
+ //
+ // No connections are writable but not timed out!
+ //
+ if (was_writable_) {
+ // If we were writable, let's kick off an allocator session immediately
+ was_writable_ = false;
+ OnAllocate();
+ }
+
+ // We were connecting, obviously not ALL timed out.
+ was_timed_out_ = false;
+ set_writable(false);
+}
+
+// We checked the status of our connections and not only weren't they writable
+// but they were also timed out, we really need a new allocator.
+void P2PTransportChannel::HandleAllTimedOut() {
+ //
+ // No connections... all are timed out!
+ //
+ if (!was_timed_out_) {
+ // We weren't timed out before, so kick off an allocator now (we'll still
+ // be in the fully timed out state until the allocator actually gives back
+ // new ports)
+ OnAllocate();
+ }
+
+ // NOTE: we start was_timed_out_ in the true state so that we don't get
+ // another allocator created WHILE we are in the process of building up
+ // our first allocator.
+ was_timed_out_ = true;
+ was_writable_ = false;
+ set_writable(false);
+}
+
+// If we have a best connection, return it, otherwise return top one in the
+// list (later we will mark it best).
+Connection* P2PTransportChannel::GetBestConnectionOnNetwork(
+ talk_base::Network* network) {
+ // If the best connection is on this network, then it wins.
+ if (best_connection_ && (best_connection_->port()->network() == network))
+ return best_connection_;
+
+ // Otherwise, we return the top-most in sorted order.
+ for (uint32 i = 0; i < connections_.size(); ++i) {
+ if (connections_[i]->port()->network() == network)
+ return connections_[i];
+ }
+
+ return NULL;
+}
+
+// Handle any queued up requests
+void P2PTransportChannel::OnMessage(talk_base::Message *pmsg) {
+ if (pmsg->message_id == MSG_SORT)
+ OnSort();
+ else if (pmsg->message_id == MSG_PING)
+ OnPing();
+ else if (pmsg->message_id == MSG_ALLOCATE)
+ OnAllocate();
+ else
+ ASSERT(false);
+}
+
+// Handle queued up sort request
+void P2PTransportChannel::OnSort() {
+ // Resort the connections based on the new statistics.
+ SortConnections();
+}
+
+// Handle queued up ping request
+void P2PTransportChannel::OnPing() {
+ // Make sure the states of the connections are up-to-date (since this affects
+ // which ones are pingable).
+ UpdateConnectionStates();
+
+ // Find the oldest pingable connection and have it do a ping.
+ Connection* conn = FindNextPingableConnection();
+ if (conn)
+ conn->Ping(talk_base::Time());
+
+ // Post ourselves a message to perform the next ping.
+ uint32 delay = writable() ? WRITABLE_DELAY : UNWRITABLE_DELAY;
+ thread()->PostDelayed(delay, this, MSG_PING);
+}
+
+// Is the connection in a state for us to even consider pinging the other side?
+bool P2PTransportChannel::IsPingable(Connection* conn) {
+ // An unconnected connection cannot be written to at all, so pinging is out
+ // of the question.
+ if (!conn->connected())
+ return false;
+
+ if (writable()) {
+ // If we are writable, then we only want to ping connections that could be
+ // better than this one, i.e., the ones that were not pruned.
+ return (conn->write_state() != Connection::STATE_WRITE_TIMEOUT);
+ } else {
+ // If we are not writable, then we need to try everything that might work.
+ // This includes both connections that do not have write timeout as well as
+ // ones that do not have read timeout. A connection could be readable but
+ // be in write-timeout if we pruned it before. Since the other side is
+ // still pinging it, it very well might still work.
+ return (conn->write_state() != Connection::STATE_WRITE_TIMEOUT) ||
+ (conn->read_state() != Connection::STATE_READ_TIMEOUT);
+ }
+}
+
+// Returns the next pingable connection to ping. This will be the oldest
+// pingable connection unless we have a writable connection that is past the
+// maximum acceptable ping delay.
+Connection* P2PTransportChannel::FindNextPingableConnection() {
+ uint32 now = talk_base::Time();
+ if (best_connection_ &&
+ (best_connection_->write_state() == Connection::STATE_WRITABLE) &&
+ (best_connection_->last_ping_sent()
+ + MAX_CURRENT_WRITABLE_DELAY <= now)) {
+ return best_connection_;
+ }
+
+ Connection* oldest_conn = NULL;
+ uint32 oldest_time = 0xFFFFFFFF;
+ for (uint32 i = 0; i < connections_.size(); ++i) {
+ if (IsPingable(connections_[i])) {
+ if (connections_[i]->last_ping_sent() < oldest_time) {
+ oldest_time = connections_[i]->last_ping_sent();
+ oldest_conn = connections_[i];
+ }
+ }
+ }
+ return oldest_conn;
+}
+
+// return the number of "pingable" connections
+uint32 P2PTransportChannel::NumPingableConnections() {
+ uint32 count = 0;
+ for (uint32 i = 0; i < connections_.size(); ++i) {
+ if (IsPingable(connections_[i]))
+ count += 1;
+ }
+ return count;
+}
+
+// When a connection's state changes, we need to figure out who to use as
+// the best connection again. It could have become usable, or become unusable.
+void P2PTransportChannel::OnConnectionStateChange(Connection *connection) {
+ ASSERT(worker_thread_ == talk_base::Thread::Current());
+
+ // We have to unroll the stack before doing this because we may be changing
+ // the state of connections while sorting.
+ RequestSort();
+}
+
+// When a connection is removed, edit it out, and then update our best
+// connection.
+void P2PTransportChannel::OnConnectionDestroyed(Connection *connection) {
+ ASSERT(worker_thread_ == talk_base::Thread::Current());
+
+ // Note: the previous best_connection_ may be destroyed by now, so don't
+ // use it.
+
+ // Remove this connection from the list.
+ std::vector<Connection*>::iterator iter =
+ find(connections_.begin(), connections_.end(), connection);
+ ASSERT(iter != connections_.end());
+ connections_.erase(iter);
+
+ LOG_J(LS_INFO, this) << "Removed connection ("
+ << static_cast<int>(connections_.size()) << " remaining)";
+
+ // If this is currently the best connection, then we need to pick a new one.
+ // The call to SortConnections will pick a new one. It looks at the current
+ // best connection in order to avoid switching between fairly similar ones.
+ // Since this connection is no longer an option, we can just set best to NULL
+ // and re-choose a best assuming that there was no best connection.
+ if (best_connection_ == connection) {
+ SwitchBestConnectionTo(NULL);
+ RequestSort();
+ }
+}
+
+// When a port is destroyed remove it from our list of ports to use for
+// connection attempts.
+void P2PTransportChannel::OnPortDestroyed(Port* port) {
+ ASSERT(worker_thread_ == talk_base::Thread::Current());
+
+ // Remove this port from the list (if we didn't drop it already).
+ std::vector<Port*>::iterator iter = find(ports_.begin(), ports_.end(), port);
+ if (iter != ports_.end())
+ ports_.erase(iter);
+
+ LOG(INFO) << "Removed port from p2p socket: "
+ << static_cast<int>(ports_.size()) << " remaining";
+}
+
+// We data is available, let listeners know
+void P2PTransportChannel::OnReadPacket(Connection *connection,
+ const char *data, size_t len) {
+ ASSERT(worker_thread_ == talk_base::Thread::Current());
+
+ // Let the client know of an incoming packet
+
+ SignalReadPacket(this, data, len);
+}
+
+// Set options on ourselves is simply setting options on all of our available
+// port objects.
+int P2PTransportChannel::SetOption(talk_base::Socket::Option opt, int value) {
+ OptionMap::iterator it = options_.find(opt);
+ if (it == options_.end()) {
+ options_.insert(std::make_pair(opt, value));
+ } else if (it->second == value) {
+ return 0;
+ } else {
+ it->second = value;
+ }
+
+ for (uint32 i = 0; i < ports_.size(); ++i) {
+ int val = ports_[i]->SetOption(opt, value);
+ if (val < 0) {
+ // Because this also occurs deferred, probably no point in reporting an
+ // error
+ LOG(WARNING) << "SetOption(" << opt << ", " << value << ") failed: "
+ << ports_[i]->GetError();
+ }
+ }
+ return 0;
+}
+
+// Time for a new allocator, lets make sure we have a signalling channel
+// to communicate candidates through first.
+void P2PTransportChannel::OnAllocate() {
+ waiting_for_signaling_ = true;
+ SignalRequestSignaling();
+}
+
+// When the signalling channel is ready, we can really kick off the allocator
+void P2PTransportChannel::OnSignalingReady() {
+ if (waiting_for_signaling_) {
+ waiting_for_signaling_ = false;
+ AddAllocatorSession(allocator_->CreateSession(name(), session_type()));
+ thread()->PostDelayed(kAllocatePeriod, this, MSG_ALLOCATE);
+ }
+}
+
+} // namespace cricket
diff --git a/Plugins/jingle/libjingle/talk/p2p/base/p2ptransportchannel.h b/Plugins/jingle/libjingle/talk/p2p/base/p2ptransportchannel.h
new file mode 100644
index 0000000..bea1537
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/p2p/base/p2ptransportchannel.h
@@ -0,0 +1,156 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+// P2PTransportChannel wraps up the state management of the connection between
+// two P2P clients. Clients have candidate ports for connecting, and
+// connections which are combinations of candidates from each end (Alice and
+// Bob each have candidates, one candidate from Alice and one candidate from
+// Bob are used to make a connection, repeat to make many connections).
+//
+// When all of the available connections become invalid (non-writable), we
+// kick off a process of determining more candidates and more connections.
+//
+#ifndef _CRICKET_P2P_BASE_P2PTRANSPORTCHANNEL_H_
+#define _CRICKET_P2P_BASE_P2PTRANSPORTCHANNEL_H_
+
+#include <vector>
+#include <string>
+#include "talk/base/sigslot.h"
+#include "talk/p2p/base/candidate.h"
+#include "talk/p2p/base/port.h"
+#include "talk/p2p/base/portallocator.h"
+#include "talk/p2p/base/transport.h"
+#include "talk/p2p/base/transportchannelimpl.h"
+#include "talk/p2p/base/p2ptransport.h"
+
+namespace cricket {
+
+// Adds the port on which the candidate originated.
+class RemoteCandidate : public Candidate {
+ public:
+ RemoteCandidate(const Candidate& c, Port* origin_port)
+ : Candidate(c), origin_port_(origin_port) {}
+
+ Port* origin_port() { return origin_port_; }
+
+ private:
+ Port* origin_port_;
+};
+
+// P2PTransportChannel manages the candidates and connection process to keep
+// two P2P clients connected to each other.
+class P2PTransportChannel : public TransportChannelImpl,
+ public talk_base::MessageHandler {
+ public:
+ P2PTransportChannel(const std::string &name,
+ const std::string &session_type,
+ P2PTransport* transport,
+ PortAllocator *allocator);
+ virtual ~P2PTransportChannel();
+
+ // From TransportChannelImpl:
+ virtual Transport* GetTransport() { return transport_; }
+ virtual void Connect();
+ virtual void Reset();
+ virtual void OnSignalingReady();
+ virtual void OnChannelMessage(const buzz::XmlElement* msg);
+
+ // From TransportChannel:
+ virtual int SendPacket(const char *data, size_t len);
+ virtual int SetOption(talk_base::Socket::Option opt, int value);
+ virtual int GetError() { return error_; }
+
+ // These are used by the connection monitor.
+ sigslot::signal1<P2PTransportChannel*> SignalConnectionMonitor;
+ const std::vector<Connection *>& connections() const { return connections_; }
+ Connection* best_connection() const { return best_connection_; }
+
+ // Handler for internal messages.
+ virtual void OnMessage(talk_base::Message *pmsg);
+
+ private:
+ void UpdateConnectionStates();
+ void RequestSort();
+ void SortConnections();
+ void SwitchBestConnectionTo(Connection* conn);
+ void UpdateChannelState();
+ void HandleWritable();
+ void HandleNotWritable();
+ void HandleAllTimedOut();
+ Connection* GetBestConnectionOnNetwork(talk_base::Network* network);
+ bool CreateConnections(const Candidate &remote_candidate, Port* origin_port,
+ bool readable);
+ bool CreateConnection(Port* port, const Candidate& remote_candidate,
+ Port* origin_port, bool readable);
+ void RememberRemoteCandidate(const Candidate& remote_candidate,
+ Port* origin_port);
+ void OnUnknownAddress(Port *port, const talk_base::SocketAddress &addr,
+ StunMessage *stun_msg,
+ const std::string &remote_username);
+ void OnPortReady(PortAllocatorSession *session, Port* port);
+ void OnCandidatesReady(PortAllocatorSession *session,
+ const std::vector<Candidate>& candidates);
+ void OnConnectionStateChange(Connection *connection);
+ void OnConnectionDestroyed(Connection *connection);
+ void OnPortDestroyed(Port* port);
+ void OnAllocate();
+ void OnReadPacket(Connection *connection, const char *data, size_t len);
+ void OnSort();
+ void OnPing();
+ bool IsPingable(Connection* conn);
+ Connection* FindNextPingableConnection();
+ uint32 NumPingableConnections();
+ PortAllocatorSession* allocator_session() {
+ return allocator_sessions_.back();
+ }
+ void AddAllocatorSession(PortAllocatorSession* session);
+
+ talk_base::Thread* thread() const { return worker_thread_; }
+
+ P2PTransport* transport_;
+ PortAllocator *allocator_;
+ talk_base::Thread *worker_thread_;
+ bool waiting_for_signaling_;
+ int error_;
+ std::vector<PortAllocatorSession*> allocator_sessions_;
+ std::vector<Port *> ports_;
+ std::vector<Connection *> connections_;
+ Connection *best_connection_;
+ std::vector<RemoteCandidate> remote_candidates_;
+ bool pinging_started_; // indicates whether StartGetAllCandidates has been called
+ bool sort_dirty_; // indicates whether another sort is needed right now
+ bool was_writable_;
+ bool was_timed_out_;
+ typedef std::map<talk_base::Socket::Option, int> OptionMap;
+ OptionMap options_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(P2PTransportChannel);
+};
+
+} // namespace cricket
+
+#endif // _CRICKET_P2P_BASE_P2PTRANSPORTCHANNEL_H_
diff --git a/Plugins/jingle/libjingle/talk/p2p/base/port.cc b/Plugins/jingle/libjingle/talk/p2p/base/port.cc
new file mode 100644
index 0000000..b5395df
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/p2p/base/port.cc
@@ -0,0 +1,926 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#if defined(_MSC_VER) && _MSC_VER < 1300
+#pragma warning(disable:4786)
+#endif
+
+#include <errno.h>
+
+#include <algorithm>
+#include <iostream>
+#include <vector>
+
+#include "talk/base/asyncudpsocket.h"
+#include "talk/base/asynctcpsocket.h"
+#include "talk/base/helpers.h"
+#include "talk/base/logging.h"
+#include "talk/base/scoped_ptr.h"
+#include "talk/base/socketadapters.h"
+#include "talk/p2p/base/common.h"
+#include "talk/p2p/base/port.h"
+
+#if defined(_MSC_VER) && _MSC_VER < 1300
+namespace std {
+ using ::memcmp;
+}
+#endif
+
+namespace {
+
+// The length of time we wait before timing out readability on a connection.
+const uint32 CONNECTION_READ_TIMEOUT = 30 * 1000; // 30 seconds
+
+// The length of time we wait before timing out writability on a connection.
+const uint32 CONNECTION_WRITE_TIMEOUT = 15 * 1000; // 15 seconds
+
+// The length of time we wait before we become unwritable.
+const uint32 CONNECTION_WRITE_CONNECT_TIMEOUT = 5 * 1000; // 5 seconds
+
+// The number of pings that must fail to respond before we become unwritable.
+const uint32 CONNECTION_WRITE_CONNECT_FAILURES = 5;
+
+// This is the length of time that we wait for a ping response to come back.
+const int CONNECTION_RESPONSE_TIMEOUT = 5 * 1000; // 5 seconds
+
+// Determines whether we have seen at least the given maximum number of
+// pings fail to have a response.
+inline bool TooManyFailures(
+ const std::vector<uint32>& pings_since_last_response,
+ uint32 maximum_failures,
+ uint32 rtt_estimate,
+ uint32 now) {
+
+ // If we haven't sent that many pings, then we can't have failed that many.
+ if (pings_since_last_response.size() < maximum_failures)
+ return false;
+
+ // Check if the window in which we would expect a response to the ping has
+ // already elapsed.
+ return pings_since_last_response[maximum_failures - 1] + rtt_estimate < now;
+}
+
+// Determines whether we have gone too long without seeing any response.
+inline bool TooLongWithoutResponse(
+ const std::vector<uint32>& pings_since_last_response,
+ uint32 maximum_time,
+ uint32 now) {
+
+ if (pings_since_last_response.size() == 0)
+ return false;
+
+ return pings_since_last_response[0] + maximum_time < now;
+}
+
+// We will restrict RTT estimates (when used for determining state) to be
+// within a reasonable range.
+const uint32 MINIMUM_RTT = 100; // 0.1 seconds
+const uint32 MAXIMUM_RTT = 3000; // 3 seconds
+
+// When we don't have any RTT data, we have to pick something reasonable. We
+// use a large value just in case the connection is really slow.
+const uint32 DEFAULT_RTT = MAXIMUM_RTT;
+
+// Computes our estimate of the RTT given the current estimate and the number
+// of data points on which it is based.
+inline uint32 ConservativeRTTEstimate(uint32 rtt, uint32 rtt_data_points) {
+ if (rtt_data_points == 0)
+ return DEFAULT_RTT;
+ else
+ return talk_base::_max(MINIMUM_RTT, talk_base::_min(MAXIMUM_RTT, 2 * rtt));
+}
+
+// Weighting of the old rtt value to new data.
+const int RTT_RATIO = 3; // 3 : 1
+
+// The delay before we begin checking if this port is useless.
+const int kPortTimeoutDelay = 30 * 1000; // 30 seconds
+
+const uint32 MSG_CHECKTIMEOUT = 1;
+const uint32 MSG_DELETE = 1;
+
+}
+
+namespace cricket {
+
+static const char * const PROTO_NAMES[PROTO_LAST+1] = { "udp", "tcp", "ssltcp" };
+
+const char * ProtoToString(ProtocolType proto) {
+ return PROTO_NAMES[proto];
+}
+
+bool StringToProto(const char * value, ProtocolType& proto) {
+ for (size_t i=0; i<=PROTO_LAST; ++i) {
+ if (strcmp(PROTO_NAMES[i], value) == 0) {
+ proto = static_cast<ProtocolType>(i);
+ return true;
+ }
+ }
+ return false;
+}
+
+std::string Port::agent_;
+talk_base::ProxyInfo Port::proxy_;
+
+Port::Port(talk_base::Thread* thread, const std::string& type,
+ talk_base::SocketFactory* factory, talk_base::Network* network)
+ : thread_(thread), factory_(factory), type_(type), network_(network),
+ preference_(-1), lifetime_(LT_PRESTART), enable_port_packets_(false) {
+
+ if (factory_ == NULL)
+ factory_ = thread_->socketserver();
+
+ set_username_fragment(CreateRandomString(16));
+ set_password(CreateRandomString(16));
+}
+
+Port::~Port() {
+ // Delete all of the remaining connections. We copy the list up front
+ // because each deletion will cause it to be modified.
+
+ std::vector<Connection*> list;
+
+ AddressMap::iterator iter = connections_.begin();
+ while (iter != connections_.end()) {
+ list.push_back(iter->second);
+ ++iter;
+ }
+
+ for (uint32 i = 0; i < list.size(); i++)
+ delete list[i];
+}
+
+Connection* Port::GetConnection(const talk_base::SocketAddress& remote_addr) {
+ AddressMap::const_iterator iter = connections_.find(remote_addr);
+ if (iter != connections_.end())
+ return iter->second;
+ else
+ return NULL;
+}
+
+void Port::AddAddress(const talk_base::SocketAddress& address,
+ const std::string& protocol,
+ bool final) {
+ Candidate c;
+ c.set_name(name_);
+ c.set_type(type_);
+ c.set_protocol(protocol);
+ c.set_address(address);
+ c.set_preference(preference_);
+ c.set_username(username_frag_);
+ c.set_password(password_);
+ c.set_network_name(network_->name());
+ c.set_generation(generation_);
+ candidates_.push_back(c);
+
+ if (final)
+ SignalAddressReady(this);
+}
+
+void Port::AddConnection(Connection* conn) {
+ connections_[conn->remote_candidate().address()] = conn;
+ conn->SignalDestroyed.connect(this, &Port::OnConnectionDestroyed);
+ SignalConnectionCreated(this, conn);
+}
+
+void Port::OnReadPacket(
+ const char* data, size_t size, const talk_base::SocketAddress& addr) {
+ // If the user has enabled port packets, just hand this over.
+ if (enable_port_packets_) {
+ SignalReadPacket(this, data, size, addr);
+ return;
+ }
+
+ // If this is an authenticated STUN request, then signal unknown address and
+ // send back a proper binding response.
+ StunMessage* msg;
+ std::string remote_username;
+ if (!GetStunMessage(data, size, addr, msg, remote_username)) {
+ LOG_J(LS_ERROR, this) << "Received non-STUN packet from unknown address ("
+ << addr.ToString() << ")";
+ } else if (!msg) {
+ // STUN message handled already
+ } else if (msg->type() == STUN_BINDING_REQUEST) {
+ SignalUnknownAddress(this, addr, msg, remote_username);
+ } else {
+ LOG_J(LS_ERROR, this) << "Received unexpected STUN message type ("
+ << msg->type() << ") from unknown address ("
+ << addr.ToString() << ")";
+ delete msg;
+ }
+}
+
+void Port::SendBindingRequest(Connection* conn) {
+
+ // Construct the request message.
+
+ StunMessage request;
+ request.SetType(STUN_BINDING_REQUEST);
+ request.SetTransactionID(CreateRandomString(16));
+
+ StunByteStringAttribute* username_attr =
+ StunAttribute::CreateByteString(STUN_ATTR_USERNAME);
+ std::string username = conn->remote_candidate().username();
+ username.append(username_frag_);
+ username_attr->CopyBytes(username.c_str(), (uint16)username.size());
+ request.AddAttribute(username_attr);
+
+ // Send the request message.
+ // NOTE: If we wanted to, this is where we would add the HMAC.
+ talk_base::ByteBuffer buf;
+ request.Write(&buf);
+ SendTo(buf.Data(), buf.Length(), conn->remote_candidate().address(), false);
+}
+
+bool Port::GetStunMessage(const char* data, size_t size,
+ const talk_base::SocketAddress& addr,
+ StunMessage *& msg, std::string& remote_username) {
+ // NOTE: This could clearly be optimized to avoid allocating any memory.
+ // However, at the data rates we'll be looking at on the client side,
+ // this probably isn't worth worrying about.
+
+ msg = 0;
+
+ // Parse the request message. If the packet is not a complete and correct
+ // STUN message, then ignore it.
+ talk_base::scoped_ptr<StunMessage> stun_msg(new StunMessage());
+ talk_base::ByteBuffer buf(data, size);
+ if (!stun_msg->Read(&buf) || (buf.Length() > 0)) {
+ return false;
+ }
+
+ // The packet must include a username that either begins or ends with our
+ // fragment. It should begin with our fragment if it is a request and it
+ // should end with our fragment if it is a response.
+ const StunByteStringAttribute* username_attr =
+ stun_msg->GetByteString(STUN_ATTR_USERNAME);
+
+ int remote_frag_len = (username_attr ? username_attr->length() : 0);
+ remote_frag_len -= static_cast<int>(username_frag_.size());
+
+ if (stun_msg->type() == STUN_BINDING_REQUEST) {
+ if ((remote_frag_len < 0)
+ || (std::memcmp(username_attr->bytes(),
+ username_frag_.c_str(), username_frag_.size()) != 0)) {
+ LOG_J(LS_ERROR, this) << "Received STUN request with bad username";
+ SendBindingErrorResponse(stun_msg.get(), addr, STUN_ERROR_BAD_REQUEST,
+ STUN_ERROR_REASON_BAD_REQUEST);
+ return true;
+ }
+
+ remote_username.assign(username_attr->bytes() + username_frag_.size(),
+ username_attr->bytes() + username_attr->length());
+ } else if ((stun_msg->type() == STUN_BINDING_RESPONSE)
+ || (stun_msg->type() == STUN_BINDING_ERROR_RESPONSE)) {
+ if ((remote_frag_len < 0)
+ || (std::memcmp(username_attr->bytes() + remote_frag_len,
+ username_frag_.c_str(), username_frag_.size()) != 0)) {
+ LOG_J(LS_ERROR, this) << "Received STUN response with bad username";
+ // Do not send error response to a response
+ return true;
+ }
+
+ remote_username.assign(username_attr->bytes(),
+ username_attr->bytes() + remote_frag_len);
+
+ if (stun_msg->type() == STUN_BINDING_ERROR_RESPONSE) {
+ if (const StunErrorCodeAttribute* error_code = stun_msg->GetErrorCode()) {
+ LOG_J(LS_ERROR, this) << "Received STUN binding error:"
+ << " class=" << error_code->error_class()
+ << " number=" << error_code->number()
+ << " reason='" << error_code->reason() << "'";
+ // Return message to allow error-specific processing
+ } else {
+ LOG_J(LS_ERROR, this)
+ << "Received STUN error response with no error code";
+ // Drop corrupt message
+ return true;
+ }
+ }
+ } else {
+ LOG_J(LS_ERROR, this) << "Received STUN packet with invalid type ("
+ << stun_msg->type() << ")";
+ return true;
+ }
+
+ // Return the STUN message found.
+ msg = stun_msg.release();
+ return true;
+}
+
+void Port::SendBindingResponse(
+ StunMessage* request, const talk_base::SocketAddress& addr) {
+
+ assert(request->type() == STUN_BINDING_REQUEST);
+
+ // Retrieve the username from the request.
+ const StunByteStringAttribute* username_attr =
+ request->GetByteString(STUN_ATTR_USERNAME);
+ assert(username_attr);
+
+ // Fill in the response message.
+
+ StunMessage response;
+ response.SetType(STUN_BINDING_RESPONSE);
+ response.SetTransactionID(request->transaction_id());
+
+ StunByteStringAttribute* username2_attr =
+ StunAttribute::CreateByteString(STUN_ATTR_USERNAME);
+ username2_attr->CopyBytes(username_attr->bytes(), username_attr->length());
+ response.AddAttribute(username2_attr);
+
+ StunAddressAttribute* addr_attr =
+ StunAttribute::CreateAddress(STUN_ATTR_MAPPED_ADDRESS);
+ addr_attr->SetFamily(1);
+ addr_attr->SetPort(addr.port());
+ addr_attr->SetIP(addr.ip());
+ response.AddAttribute(addr_attr);
+
+ // Send the response message.
+ // NOTE: If we wanted to, this is where we would add the HMAC.
+ talk_base::ByteBuffer buf;
+ response.Write(&buf);
+ SendTo(buf.Data(), buf.Length(), addr, false);
+
+ // The fact that we received a successful request means that this connection
+ // (if one exists) should now be readable.
+ Connection* conn = GetConnection(addr);
+ assert(conn);
+ if (conn)
+ conn->ReceivedPing();
+}
+
+void Port::SendBindingErrorResponse(
+ StunMessage* request, const talk_base::SocketAddress& addr, int error_code,
+ const std::string& reason) {
+
+ assert(request->type() == STUN_BINDING_REQUEST);
+
+ // Retrieve the username from the request. If it didn't have one, we
+ // shouldn't be responding at all.
+ const StunByteStringAttribute* username_attr =
+ request->GetByteString(STUN_ATTR_USERNAME);
+ assert(username_attr);
+
+ // Fill in the response message.
+
+ StunMessage response;
+ response.SetType(STUN_BINDING_ERROR_RESPONSE);
+ response.SetTransactionID(request->transaction_id());
+
+ StunByteStringAttribute* username2_attr =
+ StunAttribute::CreateByteString(STUN_ATTR_USERNAME);
+ username2_attr->CopyBytes(username_attr->bytes(), username_attr->length());
+ response.AddAttribute(username2_attr);
+
+ StunErrorCodeAttribute* error_attr = StunAttribute::CreateErrorCode();
+ error_attr->SetErrorCode(error_code);
+ error_attr->SetReason(reason);
+ response.AddAttribute(error_attr);
+
+ // Send the response message.
+ // NOTE: If we wanted to, this is where we would add the HMAC.
+ talk_base::ByteBuffer buf;
+ response.Write(&buf);
+ SendTo(buf.Data(), buf.Length(), addr, false);
+}
+
+talk_base::AsyncPacketSocket* Port::CreatePacketSocket(ProtocolType proto) {
+ if (proto == PROTO_UDP) {
+ return new talk_base::AsyncUDPSocket(
+ factory_->CreateAsyncSocket(SOCK_DGRAM));
+ } else if ((proto == PROTO_TCP) || (proto == PROTO_SSLTCP)) {
+ talk_base::AsyncSocket * socket = factory_->CreateAsyncSocket(SOCK_STREAM);
+ switch (proxy().type) {
+ case talk_base::PROXY_NONE:
+ break;
+ case talk_base::PROXY_SOCKS5:
+ socket = new talk_base::AsyncSocksProxySocket(
+ socket, proxy().address, proxy().username, proxy().password);
+ break;
+ case talk_base::PROXY_HTTPS:
+ default:
+ socket = new talk_base::AsyncHttpsProxySocket(
+ socket, user_agent(), proxy().address, proxy().username,
+ proxy().password);
+ break;
+ }
+ if (proto == PROTO_SSLTCP) {
+ socket = new talk_base::AsyncSSLSocket(socket);
+ }
+ return new talk_base::AsyncTCPSocket(socket);
+ } else {
+ LOG_J(LS_INFO, this) << "Unknown protocol (" << proto << ")";
+ return 0;
+ }
+}
+
+void Port::OnMessage(talk_base::Message *pmsg) {
+ assert(pmsg->message_id == MSG_CHECKTIMEOUT);
+ assert(lifetime_ == LT_PRETIMEOUT);
+ lifetime_ = LT_POSTTIMEOUT;
+ CheckTimeout();
+}
+
+std::string Port::ToString() const {
+ std::stringstream ss;
+ ss << "Port[" << name_ << ":" << type_ << ":" << network_->ToString() << "]";
+ return ss.str();
+}
+
+void Port::EnablePortPackets() {
+ enable_port_packets_ = true;
+}
+
+void Port::Start() {
+ // The port sticks around for a minimum lifetime, after which
+ // we destroy it when it drops to zero connections.
+ if (lifetime_ == LT_PRESTART) {
+ lifetime_ = LT_PRETIMEOUT;
+ thread_->PostDelayed(kPortTimeoutDelay, this, MSG_CHECKTIMEOUT);
+ } else {
+ LOG_J(LS_WARNING, this) << "Port restart attempted";
+ }
+}
+
+void Port::OnConnectionDestroyed(Connection* conn) {
+ AddressMap::iterator iter = connections_.find(conn->remote_candidate().address());
+ assert(iter != connections_.end());
+ connections_.erase(iter);
+
+ CheckTimeout();
+}
+
+void Port::Destroy() {
+ assert(connections_.empty());
+ LOG_J(LS_INFO, this) << "Port deleted";
+ SignalDestroyed(this);
+ delete this;
+}
+
+void Port::CheckTimeout() {
+ // If this port has no connections, then there's no reason to keep it around.
+ // When the connections time out (both read and write), they will delete
+ // themselves, so if we have any connections, they are either readable or
+ // writable (or still connecting).
+ if ((lifetime_ == LT_POSTTIMEOUT) && connections_.empty()) {
+ Destroy();
+ }
+}
+
+// A ConnectionRequest is a simple STUN ping used to determine writability.
+class ConnectionRequest : public StunRequest {
+public:
+ ConnectionRequest(Connection* connection) : connection_(connection) {
+ }
+
+ virtual ~ConnectionRequest() {
+ }
+
+ virtual void Prepare(StunMessage* request) {
+ request->SetType(STUN_BINDING_REQUEST);
+ StunByteStringAttribute* username_attr =
+ StunAttribute::CreateByteString(STUN_ATTR_USERNAME);
+ std::string username = connection_->remote_candidate().username();
+ username.append(connection_->port()->username_fragment());
+ username_attr->CopyBytes(username.c_str(), (uint16)username.size());
+ request->AddAttribute(username_attr);
+ }
+
+ virtual void OnResponse(StunMessage* response) {
+ connection_->OnConnectionRequestResponse(response, Elapsed());
+ }
+
+ virtual void OnErrorResponse(StunMessage* response) {
+ connection_->OnConnectionRequestErrorResponse(response, Elapsed());
+ }
+
+ virtual void OnTimeout() {
+ }
+
+ virtual int GetNextDelay() {
+ // Each request is sent only once. After a single delay , the request will
+ // time out.
+ timeout_ = true;
+ return CONNECTION_RESPONSE_TIMEOUT;
+ }
+
+private:
+ Connection* connection_;
+};
+
+//
+// Connection
+//
+
+Connection::Connection(Port* port, size_t index, const Candidate& remote_candidate)
+ : requests_(port->thread()), port_(port), local_candidate_index_(index),
+ remote_candidate_(remote_candidate), read_state_(STATE_READ_TIMEOUT),
+ write_state_(STATE_WRITE_CONNECT), connected_(true), pruned_(false),
+ rtt_(0), rtt_data_points_(0), last_ping_sent_(0), last_ping_received_(0),
+ recv_total_bytes_(0), recv_bytes_second_(0),
+ last_recv_bytes_second_time_((uint32)-1), last_recv_bytes_second_calc_(0),
+ sent_total_bytes_(0), sent_bytes_second_(0),
+ last_sent_bytes_second_time_((uint32)-1), last_sent_bytes_second_calc_(0),
+ reported_(false) {
+
+ // Wire up to send stun packets
+ requests_.SignalSendPacket.connect(this, &Connection::OnSendStunPacket);
+}
+
+Connection::~Connection() {
+}
+
+const Candidate& Connection::local_candidate() const {
+ if (local_candidate_index_ < port_->candidates().size())
+ return port_->candidates()[local_candidate_index_];
+ assert(false);
+ static Candidate foo;
+ return foo;
+}
+
+void Connection::set_read_state(ReadState value) {
+ ReadState old_value = read_state_;
+ read_state_ = value;
+ if (value != old_value) {
+ LOG_J(LS_VERBOSE, this) << "set_read_state";
+ SignalStateChange(this);
+ CheckTimeout();
+ }
+}
+
+void Connection::set_write_state(WriteState value) {
+ WriteState old_value = write_state_;
+ write_state_ = value;
+ if (value != old_value) {
+ LOG_J(LS_VERBOSE, this) << "set_write_state";
+ SignalStateChange(this);
+ CheckTimeout();
+ }
+}
+
+void Connection::set_connected(bool value) {
+ bool old_value = connected_;
+ connected_ = value;
+ if (value != old_value) {
+ LOG_J(LS_VERBOSE, this) << "set_connected";
+ }
+}
+
+void Connection::OnSendStunPacket(
+ const void* data, size_t size, StunRequest* req) {
+ port_->SendTo(data, size, remote_candidate_.address(), false);
+}
+
+void Connection::OnReadPacket(const char* data, size_t size) {
+ StunMessage* msg;
+ std::string remote_username;
+ const talk_base::SocketAddress& addr(remote_candidate_.address());
+ if (!port_->GetStunMessage(data, size, addr, msg, remote_username)) {
+ // The packet did not parse as a valid STUN message
+
+ // If this connection is readable, then pass along the packet.
+ if (read_state_ == STATE_READABLE) {
+ // readable means data from this address is acceptable
+ // Send it on!
+
+ recv_total_bytes_ += size;
+ SignalReadPacket(this, data, size);
+
+ // If timed out sending writability checks, start up again
+ if (!pruned_ && (write_state_ == STATE_WRITE_TIMEOUT))
+ set_write_state(STATE_WRITE_CONNECT);
+ } else {
+ // Not readable means the remote address hasn't send a valid
+ // binding request yet.
+
+ LOG_J(LS_WARNING, this)
+ << "Received non-STUN packet from an unreadable connection.";
+ }
+ } else if (!msg) {
+ // The packet was STUN, but was already handled
+ } else if (remote_username != remote_candidate_.username()) {
+ // Not destined this connection
+ LOG_J(LS_ERROR, this) << "Received STUN packet on wrong address.";
+ if (msg->type() == STUN_BINDING_REQUEST) {
+ port_->SendBindingErrorResponse(msg, addr, STUN_ERROR_BAD_REQUEST,
+ STUN_ERROR_REASON_BAD_REQUEST);
+ }
+ delete msg;
+ } else {
+ // The packet is STUN, with the current username
+ // If this is a STUN request, then update the readable bit and respond.
+ // If this is a STUN response, then update the writable bit.
+
+ switch (msg->type()) {
+ case STUN_BINDING_REQUEST:
+ // Incoming, validated stun request from remote peer.
+ // This call will also set the connection readable.
+
+ port_->SendBindingResponse(msg, addr);
+
+ // If timed out sending writability checks, start up again
+ if (!pruned_ && (write_state_ == STATE_WRITE_TIMEOUT))
+ set_write_state(STATE_WRITE_CONNECT);
+ break;
+
+ case STUN_BINDING_RESPONSE:
+ case STUN_BINDING_ERROR_RESPONSE:
+ // Response from remote peer. Does it match request sent?
+ // This doesn't just check, it makes callbacks if transaction
+ // id's match
+ requests_.CheckResponse(msg);
+ break;
+
+ default:
+ assert(false);
+ break;
+ }
+
+ // Done with the message; delete
+
+ delete msg;
+ }
+}
+
+void Connection::Prune() {
+ if (!pruned_) {
+ LOG_J(LS_VERBOSE, this) << "Connection pruned";
+ pruned_ = true;
+ requests_.Clear();
+ set_write_state(STATE_WRITE_TIMEOUT);
+ }
+}
+
+void Connection::Destroy() {
+ LOG_J(LS_VERBOSE, this) << "Connection destroyed";
+ set_read_state(STATE_READ_TIMEOUT);
+ set_write_state(STATE_WRITE_TIMEOUT);
+}
+
+void Connection::UpdateState(uint32 now) {
+ // Check the readable state.
+ //
+ // Since we don't know how many pings the other side has attempted, the best
+ // test we can do is a simple window.
+
+ if ((read_state_ == STATE_READABLE) &&
+ (last_ping_received_ + CONNECTION_READ_TIMEOUT <= now)) {
+ set_read_state(STATE_READ_TIMEOUT);
+ }
+
+ // Check the writable state. (The order of these checks is important.)
+ //
+ // Before becoming unwritable, we allow for a fixed number of pings to fail
+ // (i.e., receive no response). We also have to give the response time to
+ // get back, so we include a conservative estimate of this.
+ //
+ // Before timing out writability, we give a fixed amount of time. This is to
+ // allow for changes in network conditions.
+
+ uint32 rtt = ConservativeRTTEstimate(rtt_, rtt_data_points_);
+
+ if ((write_state_ == STATE_WRITABLE) &&
+ TooManyFailures(pings_since_last_response_,
+ CONNECTION_WRITE_CONNECT_FAILURES,
+ rtt,
+ now) &&
+ TooLongWithoutResponse(pings_since_last_response_,
+ CONNECTION_WRITE_CONNECT_TIMEOUT,
+ now)) {
+ set_write_state(STATE_WRITE_CONNECT);
+ }
+
+ if ((write_state_ == STATE_WRITE_CONNECT) &&
+ TooLongWithoutResponse(pings_since_last_response_,
+ CONNECTION_WRITE_TIMEOUT,
+ now)) {
+ set_write_state(STATE_WRITE_TIMEOUT);
+ }
+}
+
+void Connection::Ping(uint32 now) {
+ assert(connected_);
+ last_ping_sent_ = now;
+ pings_since_last_response_.push_back(now);
+ requests_.Send(new ConnectionRequest(this));
+}
+
+void Connection::ReceivedPing() {
+ last_ping_received_ = talk_base::Time();
+ set_read_state(STATE_READABLE);
+}
+
+std::string Connection::ToString() const {
+ const char CONNECT_STATE_ABBREV[2] = {
+ '-', // not connected (false)
+ 'C', // connected (true)
+ };
+ const char READ_STATE_ABBREV[2] = {
+ 'R', // STATE_READABLE
+ '-', // STATE_READ_TIMEOUT
+ };
+ const char WRITE_STATE_ABBREV[3] = {
+ 'W', // STATE_WRITABLE
+ 'w', // STATE_WRITE_CONNECT
+ '-', // STATE_WRITE_TIMEOUT
+ };
+ const Candidate& local = local_candidate();
+ const Candidate& remote = remote_candidate();
+ std::stringstream ss;
+ ss << "Conn[" << local.generation()
+ << ":" << local.name() << ":" << local.type() << ":" << local.address().ToString()
+ << "->" << remote.name() << ":" << remote.type() << ":" << remote.address().ToString()
+ << "|"
+ << CONNECT_STATE_ABBREV[connected()]
+ << READ_STATE_ABBREV[read_state()]
+ << WRITE_STATE_ABBREV[write_state()]
+ << "]";
+ return ss.str();
+}
+
+void Connection::OnConnectionRequestResponse(StunMessage *response, uint32 rtt) {
+ // We have a potentially valid reply from the remote address.
+ // The packet must include a username that ends with our fragment,
+ // since it is a response.
+
+ // Check exact message type
+ bool valid = true;
+ if (response->type() != STUN_BINDING_RESPONSE)
+ valid = false;
+
+ // Must have username attribute
+ const StunByteStringAttribute* username_attr =
+ response->GetByteString(STUN_ATTR_USERNAME);
+ if (valid) {
+ if (!username_attr) {
+ LOG_J(LS_ERROR, this) << "Received likely STUN packet with no username";
+ valid = false;
+ }
+ }
+
+ // Length must be at least the size of our fragment (actually, should
+ // be bigger since our fragment is at the end!)
+ if (valid) {
+ if (username_attr->length() <= port_->username_fragment().size()) {
+ LOG_J(LS_ERROR, this) << "Received likely STUN packet with short username";
+ valid = false;
+ }
+ }
+
+ // Compare our fragment with the end of the username - must be exact match
+ if (valid) {
+ std::string username_fragment = port_->username_fragment();
+ int offset = (int)(username_attr->length() - username_fragment.size());
+ if (std::memcmp(username_attr->bytes() + offset,
+ username_fragment.c_str(), username_fragment.size()) != 0) {
+ LOG_J(LS_ERROR, this) << "Received STUN response with bad username";
+ valid = false;
+ }
+ }
+
+ if (valid) {
+ // Valid response. If we're not already, become writable. We may be
+ // bringing a pruned connection back to life, but if we don't really want
+ // it, we can always prune it again.
+ set_write_state(STATE_WRITABLE);
+
+ pings_since_last_response_.clear();
+ rtt_ = (RTT_RATIO * rtt_ + rtt) / (RTT_RATIO + 1);
+ rtt_data_points_ += 1;
+ }
+}
+
+void Connection::OnConnectionRequestErrorResponse(StunMessage *response, uint32 rtt) {
+ const StunErrorCodeAttribute* error = response->GetErrorCode();
+ uint32 error_code = error ? error->error_code() : STUN_ERROR_GLOBAL_FAILURE;
+
+ if ((error_code == STUN_ERROR_UNKNOWN_ATTRIBUTE)
+ || (error_code == STUN_ERROR_SERVER_ERROR)
+ || (error_code == STUN_ERROR_UNAUTHORIZED)) {
+ // Recoverable error, retry
+ } else if (error_code == STUN_ERROR_STALE_CREDENTIALS) {
+ // Race failure, retry
+ } else {
+ // This is not a valid connection.
+ set_write_state(STATE_WRITE_TIMEOUT);
+ }
+}
+
+void Connection::CheckTimeout() {
+ // If both read and write have timed out, then this connection can contribute
+ // no more to p2p socket unless at some later date readability were to come
+ // back. However, we gave readability a long time to timeout, so at this
+ // point, it seems fair to get rid of this connectoin.
+ if ((read_state_ == STATE_READ_TIMEOUT) &&
+ (write_state_ == STATE_WRITE_TIMEOUT)) {
+ port_->thread()->Post(this, MSG_DELETE);
+ }
+}
+
+void Connection::OnMessage(talk_base::Message *pmsg) {
+ assert(pmsg->message_id == MSG_DELETE);
+
+ LOG_J(LS_INFO, this) << "Connection deleted";
+ SignalDestroyed(this);
+ delete this;
+}
+
+size_t Connection::recv_bytes_second() {
+ // Snapshot bytes / second calculator
+
+ uint32 current_time = talk_base::Time();
+ if (last_recv_bytes_second_time_ != (uint32)-1) {
+ int delta = talk_base::TimeDiff(current_time, last_recv_bytes_second_time_);
+ if (delta >= 1000) {
+ int fraction_time = delta % 1000;
+ int seconds_time = delta - fraction_time;
+ int fraction_bytes = (int)(recv_total_bytes_ - last_recv_bytes_second_calc_) * fraction_time / delta;
+ recv_bytes_second_ = (recv_total_bytes_ - last_recv_bytes_second_calc_ - fraction_bytes) * seconds_time / delta;
+ last_recv_bytes_second_time_ = current_time - fraction_time;
+ last_recv_bytes_second_calc_ = recv_total_bytes_ - fraction_bytes;
+ }
+ }
+ if (last_recv_bytes_second_time_ == (uint32)-1) {
+ last_recv_bytes_second_time_ = current_time;
+ last_recv_bytes_second_calc_ = recv_total_bytes_;
+ }
+
+ return recv_bytes_second_;
+}
+
+size_t Connection::recv_total_bytes() {
+ return recv_total_bytes_;
+}
+
+size_t Connection::sent_bytes_second() {
+ // Snapshot bytes / second calculator
+
+ uint32 current_time = talk_base::Time();
+ if (last_sent_bytes_second_time_ != (uint32)-1) {
+ int delta = talk_base::TimeDiff(current_time, last_sent_bytes_second_time_);
+ if (delta >= 1000) {
+ int fraction_time = delta % 1000;
+ int seconds_time = delta - fraction_time;
+ int fraction_bytes = (int)(sent_total_bytes_ - last_sent_bytes_second_calc_) * fraction_time / delta;
+ sent_bytes_second_ = (sent_total_bytes_ - last_sent_bytes_second_calc_ - fraction_bytes) * seconds_time / delta;
+ last_sent_bytes_second_time_ = current_time - fraction_time;
+ last_sent_bytes_second_calc_ = sent_total_bytes_ - fraction_bytes;
+ }
+ }
+ if (last_sent_bytes_second_time_ == (uint32)-1) {
+ last_sent_bytes_second_time_ = current_time;
+ last_sent_bytes_second_calc_ = sent_total_bytes_;
+ }
+
+ return sent_bytes_second_;
+}
+
+size_t Connection::sent_total_bytes() {
+ return sent_total_bytes_;
+}
+
+ProxyConnection::ProxyConnection(Port* port, size_t index, const Candidate& candidate)
+ : Connection(port, index, candidate), error_(0) {
+}
+
+int ProxyConnection::Send(const void* data, size_t size) {
+ if (write_state() != STATE_WRITABLE) {
+ error_ = EWOULDBLOCK;
+ return SOCKET_ERROR;
+ }
+ int sent = port_->SendTo(data, size, remote_candidate_.address(), true);
+ if (sent <= 0) {
+ assert(sent < 0);
+ error_ = port_->GetError();
+ } else {
+ sent_total_bytes_ += sent;
+ }
+ return sent;
+}
+
+} // namespace cricket
diff --git a/Plugins/jingle/libjingle/talk/p2p/base/port.h b/Plugins/jingle/libjingle/talk/p2p/base/port.h
new file mode 100644
index 0000000..57c0727
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/p2p/base/port.h
@@ -0,0 +1,421 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __PORT_H__
+#define __PORT_H__
+
+#include "talk/base/network.h"
+#include "talk/base/socketaddress.h"
+#include "talk/base/proxyinfo.h"
+#include "talk/base/sigslot.h"
+#include "talk/base/thread.h"
+#include "talk/p2p/base/candidate.h"
+#include "talk/p2p/base/stun.h"
+#include "talk/p2p/base/stunrequest.h"
+
+#include <string>
+#include <vector>
+#include <map>
+
+namespace talk_base {
+class AsyncPacketSocket;
+}
+
+namespace cricket {
+
+class Connection;
+
+enum ProtocolType {
+ PROTO_UDP,
+ PROTO_TCP,
+ PROTO_SSLTCP,
+ PROTO_LAST = PROTO_SSLTCP
+};
+
+const char * ProtoToString(ProtocolType proto);
+bool StringToProto(const char * value, ProtocolType& proto);
+
+struct ProtocolAddress {
+ talk_base::SocketAddress address;
+ ProtocolType proto;
+
+ ProtocolAddress(const talk_base::SocketAddress& a, ProtocolType p)
+ : address(a), proto(p) { }
+};
+
+// Represents a local communication mechanism that can be used to create
+// connections to similar mechanisms of the other client. Subclasses of this
+// one add support for specific mechanisms like local UDP ports.
+class Port : public talk_base::MessageHandler, public sigslot::has_slots<> {
+public:
+ Port(talk_base::Thread* thread, const std::string &type,
+ talk_base::SocketFactory* factory, talk_base::Network* network);
+ virtual ~Port();
+
+ // The thread on which this port performs its I/O.
+ talk_base::Thread* thread() { return thread_; }
+
+ // The factory used to create the sockets of this port.
+ talk_base::SocketFactory* socket_factory() const { return factory_; }
+ void set_socket_factory(talk_base::SocketFactory* factory)
+ { factory_ = factory; }
+
+ // Each port is identified by a name (for debugging purposes).
+ const std::string& name() const { return name_; }
+ void set_name(const std::string& name) { name_ = name; }
+
+ // In order to establish a connection to this Port (so that real data can be
+ // sent through), the other side must send us a STUN binding request that is
+ // authenticated with this username and password.
+ const std::string& username_fragment() const { return username_frag_; }
+ const std::string& password() const { return password_; }
+
+ // A value in [0,1] that indicates the preference for this port versus other
+ // ports on this client. (Larger indicates more preference.)
+ float preference() const { return preference_; }
+ void set_preference(float preference) { preference_ = preference; }
+
+ // Identifies the port type.
+ //const std::string& protocol() const { return proto_; }
+ const std::string& type() const { return type_; }
+
+ // Identifies network that this port was allocated on.
+ talk_base::Network* network() { return network_; }
+
+ // Identifies the generation that this port was created in.
+ uint32 generation() { return generation_; }
+ void set_generation(uint32 generation) { generation_ = generation; }
+
+ // PrepareAddress will attempt to get an address for this port that other
+ // clients can send to. It may take some time before the address is read.
+ // Once it is ready, we will send SignalAddressReady. If errors are
+ // preventing the port from getting an address, it may send
+ // SignalAddressError.
+ virtual void PrepareAddress() = 0;
+ sigslot::signal1<Port*> SignalAddressReady;
+ sigslot::signal1<Port*> SignalAddressError;
+
+ // Provides all of the above information in one handy object.
+ const std::vector<Candidate>& candidates() const { return candidates_; }
+
+ // Returns a map containing all of the connections of this port, keyed by the
+ // remote address.
+ typedef std::map<talk_base::SocketAddress, Connection*> AddressMap;
+ const AddressMap& connections() { return connections_; }
+
+ // Returns the connection to the given address or NULL if none exists.
+ Connection* GetConnection(const talk_base::SocketAddress& remote_addr);
+
+ // Creates a new connection to the given address.
+ enum CandidateOrigin { ORIGIN_THIS_PORT, ORIGIN_OTHER_PORT, ORIGIN_MESSAGE };
+ virtual Connection* CreateConnection(const Candidate& remote_candidate,
+ CandidateOrigin origin) = 0;
+
+ // Called each time a connection is created.
+ sigslot::signal2<Port*, Connection*> SignalConnectionCreated;
+
+ // Sends the given packet to the given address, provided that the address is
+ // that of a connection or an address that has sent to us already.
+ virtual int SendTo(
+ const void* data, size_t size, const talk_base::SocketAddress& addr,
+ bool payload) = 0;
+
+ // Indicates that we received a successful STUN binding request from an
+ // address that doesn't correspond to any current connection. To turn this
+ // into a real connection, call CreateConnection.
+ sigslot::signal4<Port*, const talk_base::SocketAddress&, StunMessage*,
+ const std::string&> SignalUnknownAddress;
+
+ // Sends a response message (normal or error) to the given request. One of
+ // these methods should be called as a response to SignalUnknownAddress.
+ // NOTE: You MUST call CreateConnection BEFORE SendBindingResponse.
+ void SendBindingResponse(StunMessage* request,
+ const talk_base::SocketAddress& addr);
+ void SendBindingErrorResponse(
+ StunMessage* request, const talk_base::SocketAddress& addr,
+ int error_code, const std::string& reason);
+
+ // Indicates that errors occurred when performing I/O.
+ sigslot::signal2<Port*, int> SignalReadError;
+ sigslot::signal2<Port*, int> SignalWriteError;
+
+ // Functions on the underlying socket(s).
+ virtual int SetOption(talk_base::Socket::Option opt, int value) = 0;
+ virtual int GetError() = 0;
+
+ static void set_proxy(const std::string& user_agent,
+ const talk_base::ProxyInfo& proxy) {
+ agent_ = user_agent; proxy_ = proxy;
+ }
+ static const std::string& user_agent() { return agent_; }
+ static const talk_base::ProxyInfo& proxy() { return proxy_; }
+
+ talk_base::AsyncPacketSocket * CreatePacketSocket(ProtocolType proto);
+
+ // Normally, packets arrive through a connection (or they result signaling of
+ // unknown address). Calling this method turns off delivery of packets
+ // through their respective connection and instead delivers every packet
+ // through this port.
+ void EnablePortPackets();
+ sigslot::signal4<Port*, const char*, size_t, const talk_base::SocketAddress&>
+ SignalReadPacket;
+
+ // Indicates to the port that its official use has now begun. This will
+ // start the timer that checks to see if the port is being used.
+ void Start();
+
+ // Called if the port has no connections and is no longer useful.
+ void Destroy();
+
+ // Signaled when this port decides to delete itself because it no longer has
+ // any usefulness.
+ sigslot::signal1<Port*> SignalDestroyed;
+
+ virtual void OnMessage(talk_base::Message *pmsg);
+
+ // Debugging description of this port
+ std::string ToString() const;
+
+protected:
+ talk_base::Thread* thread_;
+ talk_base::SocketFactory* factory_;
+ std::string type_;
+ talk_base::Network* network_;
+ uint32 generation_;
+ std::string name_;
+ std::string username_frag_;
+ std::string password_;
+ float preference_;
+ std::vector<Candidate> candidates_;
+ AddressMap connections_;
+ enum Lifetime { LT_PRESTART, LT_PRETIMEOUT, LT_POSTTIMEOUT } lifetime_;
+ bool enable_port_packets_;
+
+ // Fills in the username fragment and password. These will be initially set
+ // in the constructor to random values. Subclasses can override, though.
+ void set_username_fragment(const std::string& username_fragment) {
+ username_frag_ = username_fragment;
+ }
+ void set_password(const std::string& password) { password_ = password; }
+
+ // Fills in the local address of the port.
+ void AddAddress(const talk_base::SocketAddress& address,
+ const std::string& protocol, bool final);
+
+ // Adds the given connection to the list. (Deleting removes them.)
+ void AddConnection(Connection* conn);
+
+ // Called when a packet is received from an unknown address that is not
+ // currently a connection. If this is an authenticated STUN binding request,
+ // then we will signal the client.
+ void OnReadPacket(const char* data, size_t size,
+ const talk_base::SocketAddress& addr);
+
+ // Constructs a STUN binding request for the given connection and sends it.
+ void SendBindingRequest(Connection* conn);
+
+ // If the given data comprises a complete and correct STUN message then the
+ // return value is true, otherwise false. If the message username corresponds
+ // with this port's username fragment, msg will contain the parsed STUN
+ // message. Otherwise, the function may send a STUN response internally.
+ // remote_username contains the remote fragment of the STUN username.
+ bool GetStunMessage(const char* data, size_t size,
+ const talk_base::SocketAddress& addr,
+ StunMessage *& msg, std::string& remote_username);
+
+ friend class Connection;
+
+private:
+ // Called when one of our connections deletes itself.
+ void OnConnectionDestroyed(Connection* conn);
+
+ // Checks if this port is useless, and hence, should be destroyed.
+ void CheckTimeout();
+
+ static std::string agent_;
+ static talk_base::ProxyInfo proxy_;
+};
+
+// Represents a communication link between a port on the local client and a
+// port on the remote client.
+class Connection : public talk_base::MessageHandler,
+ public sigslot::has_slots<> {
+public:
+ virtual ~Connection();
+
+ // The local port where this connection sends and receives packets.
+ Port* port() { return port_; }
+ const Port* port() const { return port_; }
+
+ // Returns the description of the local port
+ virtual const Candidate& local_candidate() const;
+
+ // Returns the description of the remote port to which we communicate.
+ const Candidate& remote_candidate() const { return remote_candidate_; }
+
+ enum ReadState {
+ STATE_READABLE = 0, // we have received pings recently
+ STATE_READ_TIMEOUT = 1 // we haven't received pings in a while
+ };
+
+ ReadState read_state() const { return read_state_; }
+
+ enum WriteState {
+ STATE_WRITABLE = 0, // we have received ping responses recently
+ STATE_WRITE_CONNECT = 1, // we have had a few ping failures
+ STATE_WRITE_TIMEOUT = 2 // we have had a large number of ping failures
+ };
+
+ WriteState write_state() const { return write_state_; }
+
+ // Determines whether the connection has finished connecting. This can only
+ // be false for TCP connections.
+ bool connected() const { return connected_; }
+
+ // Estimate of the round-trip time over this connection.
+ uint32 rtt() const { return rtt_; }
+
+ size_t sent_total_bytes();
+ size_t sent_bytes_second();
+ size_t recv_total_bytes();
+ size_t recv_bytes_second();
+ sigslot::signal1<Connection*> SignalStateChange;
+
+ // Sent when the connection has decided that it is no longer of value. It
+ // will delete itself immediately after this call.
+ sigslot::signal1<Connection*> SignalDestroyed;
+
+ // The connection can send and receive packets asynchronously. This matches
+ // the interface of AsyncPacketSocket, which may use UDP or TCP under the
+ // covers.
+ virtual int Send(const void* data, size_t size) = 0;
+
+ // Error if Send() returns < 0
+ virtual int GetError() = 0;
+
+ sigslot::signal3<Connection*, const char*, size_t> SignalReadPacket;
+
+ // Called when a packet is received on this connection.
+ void OnReadPacket(const char* data, size_t size);
+
+ // Called when a connection is determined to be no longer useful to us. We
+ // still keep it around in case the other side wants to use it. But we can
+ // safely stop pinging on it and we can allow it to time out if the other
+ // side stops using it as well.
+ bool pruned() { return pruned_; }
+ void Prune();
+
+ // Makes the connection go away.
+ void Destroy();
+
+ // Checks that the state of this connection is up-to-date. The argument is
+ // the current time, which is compared against various timeouts.
+ void UpdateState(uint32 now);
+
+ // Called when this connection should try checking writability again.
+ uint32 last_ping_sent() { return last_ping_sent_; }
+ void Ping(uint32 now);
+
+ // Called whenever a valid ping is received on this connection. This is
+ // public because the connection intercepts the first ping for us.
+ void ReceivedPing();
+
+ // Debugging description of this connection
+ std::string ToString() const;
+
+ bool reported() { return reported_; }
+ void set_reported(bool reported) { reported_ = reported;}
+
+protected:
+ Port* port_;
+ size_t local_candidate_index_;
+ Candidate remote_candidate_;
+ ReadState read_state_;
+ WriteState write_state_;
+ bool connected_;
+ bool pruned_;
+ StunRequestManager requests_;
+ uint32 rtt_;
+ uint32 rtt_data_points_;
+ uint32 last_ping_sent_; // last time we sent a ping to the other side
+ uint32 last_ping_received_; // last time we received a ping from the other
+ // side
+ std::vector<uint32> pings_since_last_response_;
+
+ size_t recv_total_bytes_;
+ size_t recv_bytes_second_;
+ uint32 last_recv_bytes_second_time_;
+ size_t last_recv_bytes_second_calc_;
+
+ size_t sent_total_bytes_;
+ size_t sent_bytes_second_;
+ uint32 last_sent_bytes_second_time_;
+ size_t last_sent_bytes_second_calc_;
+
+ // Callbacks from ConnectionRequest
+ void OnConnectionRequestResponse(StunMessage *response, uint32 rtt);
+ void OnConnectionRequestErrorResponse(StunMessage *response, uint32 rtt);
+
+ // Called back when StunRequestManager has a stun packet to send
+ void OnSendStunPacket(const void* data, size_t size, StunRequest* req);
+
+ // Constructs a new connection to the given remote port.
+ Connection(Port* port, size_t index, const Candidate& candidate);
+
+ // Changes the state and signals if necessary.
+ void set_read_state(ReadState value);
+ void set_write_state(WriteState value);
+ void set_connected(bool value);
+
+ // Checks if this connection is useless, and hence, should be destroyed.
+ void CheckTimeout();
+
+ void OnMessage(talk_base::Message *pmsg);
+
+ friend class Port;
+ friend class ConnectionRequest;
+
+private:
+ bool reported_;
+};
+
+// ProxyConnection defers all the interesting work to the port
+
+class ProxyConnection : public Connection {
+public:
+ ProxyConnection(Port* port, size_t index, const Candidate& candidate);
+
+ virtual int Send(const void* data, size_t size);
+ virtual int GetError() { return error_; }
+
+private:
+ int error_;
+};
+
+} // namespace cricket
+
+#endif // __PORT_H__
diff --git a/Plugins/jingle/libjingle/talk/p2p/base/port_unittest.cc b/Plugins/jingle/libjingle/talk/p2p/base/port_unittest.cc
new file mode 100644
index 0000000..4687c19
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/p2p/base/port_unittest.cc
@@ -0,0 +1,363 @@
+#include "talk/base/helpers.h"
+#include "talk/base/host.h"
+#include "talk/base/natserver.h"
+#include "talk/base/natsocketfactory.h"
+#include "talk/base/socketaddress.h"
+#include "talk/base/thread.h"
+#include "talk/base/virtualsocketserver.h"
+#include "talk/p2p/base/udpport.h"
+#include "talk/p2p/base/relayport.h"
+#include "talk/p2p/base/relayserver.h"
+#include "talk/p2p/base/stunport.h"
+#include "talk/p2p/base/stunserver.h"
+#include <iostream>
+
+using namespace cricket;
+
+const uint32 MSG_CONNECT = 1;
+const uint32 MSG_PREP_ADDRESS = 2;
+const uint32 MSG_CREATE_CONN = 3;
+const uint32 MSG_ACCEPT_CONN = 4;
+const uint32 MSG_PING = 5;
+
+Candidate GetCandidate(Port* port) {
+ assert(port->candidates().size() == 1);
+ return port->candidates()[0];
+}
+
+talk_base::SocketAddress GetAddress(Port* port) {
+ return GetCandidate(port).address();
+}
+
+struct Foo : public talk_base::MessageHandler, public sigslot::has_slots<> {
+ int count;
+ talk_base::SocketAddress address;
+ StunMessage* request;
+ std::string remote_frag;
+
+ talk_base::Thread* thread;
+ Port* port1;
+ Port* port2;
+ Connection* conn;
+
+ Foo(talk_base::Thread* th, Port* p1, Port* p2)
+ : count(0), thread(th), port1(p1), port2(p2), conn(0) {
+ }
+
+ void OnAddressReady(Port* port) {
+ count += 1;
+ }
+
+ void OnUnknownAddress(
+ Port* port, const talk_base::SocketAddress& addr, StunMessage* msg,
+ const std::string& rf) {
+ assert(port == port1);
+ if (!address.IsAny()) {
+ assert(addr == address);
+ delete request;
+ }
+ address = addr;
+ request = msg;
+ remote_frag = rf;
+ }
+
+ void OnMessage(talk_base::Message* pmsg) {
+ assert(talk_base::Thread::Current() == thread);
+
+ switch (pmsg->message_id) {
+ case MSG_CONNECT:
+ port1->SignalAddressReady.connect(this, &Foo::OnAddressReady);
+ port1->SignalUnknownAddress.connect(this, &Foo::OnUnknownAddress);
+ break;
+
+ case MSG_PREP_ADDRESS:
+ port1->PrepareAddress();
+ break;
+
+ case MSG_CREATE_CONN:
+ conn = port1->CreateConnection(GetCandidate(port2), Port::ORIGIN_MESSAGE);
+ assert(conn);
+ conn->Ping(0);
+ break;
+
+ case MSG_PING:
+ assert(conn);
+ conn->Ping(0);
+ break;
+
+ case MSG_ACCEPT_CONN: {
+ Candidate c = GetCandidate(port2);
+ c.set_address(address);
+ conn = port1->CreateConnection(c, Port::ORIGIN_MESSAGE);
+ assert(conn);
+ port1->SendBindingResponse(request, address);
+ conn->Ping(0);
+ delete request;
+ break;
+ }
+
+ default:
+ assert(false);
+ break;
+ }
+ }
+};
+
+void test(talk_base::Thread* pthMain, const char* name1, Port* port1,
+ talk_base::Thread* pthBack, const char* name2, Port* port2,
+ bool accept = true, bool same_addr = true) {
+ Foo* foo1 = new Foo(pthMain, port1, port2);
+ Foo* foo2 = new Foo(pthBack, port2, port1);
+
+ std::cout << "Test: " << name1 << " to " << name2 << ": ";
+ std::cout.flush();
+
+ pthBack->Start();
+
+ pthMain->Post(foo1, MSG_CONNECT);
+ pthBack->Post(foo2, MSG_CONNECT);
+ pthMain->ProcessMessages(10);
+ assert(foo1->count == 0);
+ assert(foo2->count == 0);
+
+ pthMain->Post(foo1, MSG_PREP_ADDRESS);
+ pthMain->ProcessMessages(200);
+ assert(foo1->count == 1);
+
+ pthBack->Post(foo2, MSG_PREP_ADDRESS);
+ pthMain->ProcessMessages(200);
+ assert(foo2->count == 1);
+
+ pthMain->Post(foo1, MSG_CREATE_CONN);
+ pthMain->ProcessMessages(200);
+
+ if (accept) {
+
+ assert(foo1->address.IsAny());
+ assert(foo2->remote_frag == port1->username_fragment());
+ assert(!same_addr || (foo2->address == GetAddress(port1)));
+
+ pthBack->Post(foo2, MSG_ACCEPT_CONN);
+ pthMain->ProcessMessages(200);
+
+ } else {
+
+ assert(foo1->address.IsAny());
+ assert(foo2->address.IsAny());
+
+ pthBack->Post(foo2, MSG_CREATE_CONN);
+ pthMain->ProcessMessages(200);
+
+ if (same_addr) {
+ assert(foo1->conn->read_state() == Connection::STATE_READABLE);
+ assert(foo2->conn->write_state() == Connection::STATE_WRITABLE);
+
+ // First connection may not be writable if the first ping did not get
+ // through. So we will have to do another.
+ if (foo1->conn->write_state() == Connection::STATE_WRITE_CONNECT) {
+ pthMain->Post(foo1, MSG_PING);
+ pthMain->ProcessMessages(200);
+ }
+ } else {
+ assert(foo1->address.IsAny());
+ assert(foo2->address.IsAny());
+
+ pthMain->Post(foo1, MSG_PING);
+ pthMain->ProcessMessages(200);
+
+ assert(foo1->address.IsAny());
+ assert(!foo2->address.IsAny());
+
+ pthBack->Post(foo2, MSG_ACCEPT_CONN);
+ pthMain->ProcessMessages(200);
+ }
+ }
+
+ assert(foo1->conn->read_state() == Connection::STATE_READABLE);
+ assert(foo1->conn->write_state() == Connection::STATE_WRITABLE);
+ assert(foo2->conn->read_state() == Connection::STATE_READABLE);
+ assert(foo2->conn->write_state() == Connection::STATE_WRITABLE);
+
+ pthBack->Stop();
+
+ delete port1;
+ delete port2;
+ delete foo1;
+ delete foo2;
+
+ std::cout << "PASS" << std::endl;
+}
+
+const talk_base::SocketAddress local_addr = talk_base::SocketAddress("127.0.0.1", 0);
+const talk_base::SocketAddress relay_int_addr = talk_base::SocketAddress("127.0.0.1", 5000);
+const talk_base::SocketAddress relay_ext_addr = talk_base::SocketAddress("127.0.0.1", 5001);
+const talk_base::SocketAddress stun_addr = talk_base::SocketAddress("127.0.0.1", STUN_SERVER_PORT);
+const talk_base::SocketAddress nat_addr = talk_base::SocketAddress("127.0.0.1", talk_base::NAT_SERVER_PORT);
+
+void test_udp() {
+ talk_base::Thread* pthMain = talk_base::Thread::Current();
+ talk_base::Thread* pthBack = new talk_base::Thread();
+ talk_base::Network* network = new talk_base::Network("network", local_addr.ip());
+
+ test(pthMain, "udp", new UDPPort(pthMain, NULL, network, local_addr),
+ pthBack, "udp", new UDPPort(pthBack, NULL, network, local_addr));
+
+ delete pthBack;
+}
+
+void test_relay() {
+ talk_base::Thread* pthMain = talk_base::Thread::Current();
+ talk_base::Thread* pthBack = new talk_base::Thread();
+ talk_base::Network* network = new talk_base::Network("network", local_addr.ip());
+
+ RelayServer relay_server(pthBack);
+
+ talk_base::AsyncUDPSocket* int_socket = talk_base::CreateAsyncUDPSocket(pthBack->socketserver());
+ assert(int_socket->Bind(relay_int_addr) >= 0);
+ relay_server.AddInternalSocket(int_socket);
+
+ talk_base::AsyncUDPSocket* ext_socket = talk_base::CreateAsyncUDPSocket(pthBack->socketserver());
+ assert(ext_socket->Bind(relay_ext_addr) >= 0);
+ relay_server.AddExternalSocket(ext_socket);
+
+ std::string username = CreateRandomString(16);
+ std::string password = CreateRandomString(16);
+
+ RelayPort* rport =
+ new RelayPort(pthBack, NULL, network, local_addr, username, password, "");
+ rport->AddServerAddress(ProtocolAddress(relay_int_addr, PROTO_UDP));
+
+ test(pthMain, "udp", new UDPPort(pthMain, NULL, network, local_addr),
+ pthBack, "relay", rport);
+
+ delete pthBack;
+}
+
+const char* NATName(talk_base::NATType type) {
+ switch (type) {
+ case talk_base::NAT_OPEN_CONE: return "open cone";
+ case talk_base::NAT_ADDR_RESTRICTED: return "addr restricted";
+ case talk_base::NAT_PORT_RESTRICTED: return "port restricted";
+ case talk_base::NAT_SYMMETRIC: return "symmetric";
+ default:
+ assert(false);
+ return 0;
+ }
+}
+
+void test_stun(talk_base::NATType nat_type) {
+ talk_base::Thread* pthMain = talk_base::Thread::Current();
+ talk_base::Thread* pthBack = new talk_base::Thread();
+ talk_base::Network* network = new talk_base::Network("network", local_addr.ip());
+
+ talk_base::NATServer* nat = new talk_base::NATServer(
+ nat_type, pthMain->socketserver(), nat_addr,
+ pthMain->socketserver(), nat_addr);
+ talk_base::NATSocketFactory* nat_factory =
+ new talk_base::NATSocketFactory(pthMain->socketserver(), nat_addr);
+
+ StunPort* stun_port =
+ new StunPort(pthMain, nat_factory, network, local_addr, stun_addr);
+
+ talk_base::AsyncUDPSocket* stun_socket = talk_base::CreateAsyncUDPSocket(pthMain->socketserver());
+ assert(stun_socket->Bind(stun_addr) >= 0);
+ StunServer* stun_server = new StunServer(stun_socket);
+
+ char name[256];
+ sprintf(name, "stun (%s)", NATName(nat_type));
+
+ test(pthMain, name, stun_port,
+ pthBack, "udp", new UDPPort(pthBack, NULL, network, local_addr),
+ true, nat_type != talk_base::NAT_SYMMETRIC);
+
+ delete stun_server;
+ delete stun_socket;
+ delete nat;
+ delete nat_factory;
+ delete pthBack;
+}
+
+void test_stun(talk_base::NATType nat1_type, talk_base::NATType nat2_type) {
+
+ talk_base::Thread* pthMain = talk_base::Thread::Current();
+ talk_base::Thread* pthBack = new talk_base::Thread();
+ talk_base::Network* network = new talk_base::Network("network", local_addr.ip());
+
+ talk_base::SocketAddress local_addr1(talk_base::LocalHost().networks()[0]->ip(), 0);
+ talk_base::SocketAddress local_addr2(talk_base::LocalHost().networks()[1]->ip(), 0);
+
+ talk_base::SocketAddress nat1_addr(local_addr1.ip(), talk_base::NAT_SERVER_PORT);
+ talk_base::SocketAddress nat2_addr(local_addr2.ip(), talk_base::NAT_SERVER_PORT);
+
+ talk_base::SocketAddress stun1_addr(local_addr1.ip(), STUN_SERVER_PORT);
+ talk_base::SocketAddress stun2_addr(local_addr2.ip(), STUN_SERVER_PORT);
+
+ talk_base::NATServer* nat1 = new talk_base::NATServer(
+ nat1_type, pthMain->socketserver(), nat1_addr,
+ pthMain->socketserver(), nat1_addr);
+ talk_base::NATSocketFactory* nat1_factory =
+ new talk_base::NATSocketFactory(pthMain->socketserver(), nat1_addr);
+
+ StunPort* stun1_port =
+ new StunPort(pthMain, nat1_factory, network, local_addr1, stun1_addr);
+
+ talk_base::NATServer* nat2 = new talk_base::NATServer(
+ nat2_type, pthBack->socketserver(), nat2_addr,
+ pthBack->socketserver(), nat2_addr);
+ talk_base::NATSocketFactory* nat2_factory =
+ new talk_base::NATSocketFactory(pthBack->socketserver(), nat2_addr);
+
+ StunPort* stun2_port =
+ new StunPort(pthMain, nat2_factory, network, local_addr2, stun2_addr);
+
+ talk_base::AsyncUDPSocket* stun1_socket = talk_base::CreateAsyncUDPSocket(pthMain->socketserver());
+ assert(stun1_socket->Bind(stun_addr) >= 0);
+ StunServer* stun1_server = new StunServer(stun1_socket);
+
+ talk_base::AsyncUDPSocket* stun2_socket = talk_base::CreateAsyncUDPSocket(pthBack->socketserver());
+ assert(stun2_socket->Bind(stun2_addr) >= 0);
+ StunServer* stun2_server = new StunServer(stun2_socket);
+
+ char name1[256], name2[256];
+ sprintf(name1, "stun (%s)", NATName(nat1_type));
+ sprintf(name2, "stun (%s)", NATName(nat2_type));
+
+ test(pthMain, name1, stun1_port,
+ pthBack, name2, stun2_port,
+ nat2_type == talk_base::NAT_OPEN_CONE, nat1_type != talk_base::NAT_SYMMETRIC);
+
+ delete stun1_server;
+ delete stun1_socket;
+ delete stun2_server;
+ delete stun2_socket;
+ delete nat1;
+ delete nat1_factory;
+ delete nat2;
+ delete nat2_factory;
+ delete pthBack;
+}
+
+int main(int argc, char* argv[]) {
+ InitRandom(NULL, 0);
+
+ test_udp();
+
+ test_relay();
+
+ test_stun(talk_base::NAT_OPEN_CONE);
+ test_stun(talk_base::NAT_ADDR_RESTRICTED);
+ test_stun(talk_base::NAT_PORT_RESTRICTED);
+ test_stun(talk_base::NAT_SYMMETRIC);
+
+ test_stun(talk_base::NAT_OPEN_CONE, talk_base::NAT_OPEN_CONE);
+ test_stun(talk_base::NAT_ADDR_RESTRICTED, talk_base::NAT_OPEN_CONE);
+ test_stun(talk_base::NAT_PORT_RESTRICTED, talk_base::NAT_OPEN_CONE);
+ test_stun(talk_base::NAT_SYMMETRIC, talk_base::NAT_OPEN_CONE);
+
+ test_stun(talk_base::NAT_ADDR_RESTRICTED, talk_base::NAT_ADDR_RESTRICTED);
+ test_stun(talk_base::NAT_PORT_RESTRICTED, talk_base::NAT_ADDR_RESTRICTED);
+ test_stun(talk_base::NAT_PORT_RESTRICTED, talk_base::NAT_PORT_RESTRICTED);
+ test_stun(talk_base::NAT_SYMMETRIC, talk_base::NAT_ADDR_RESTRICTED);
+
+ return 0;
+}
diff --git a/Plugins/jingle/libjingle/talk/p2p/base/portallocator.h b/Plugins/jingle/libjingle/talk/p2p/base/portallocator.h
new file mode 100644
index 0000000..50f52cf
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/p2p/base/portallocator.h
@@ -0,0 +1,104 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _PORTALLOCATOR_H_
+#define _PORTALLOCATOR_H_
+
+#include "talk/base/sigslot.h"
+#include "talk/p2p/base/port.h"
+#include <string>
+#undef SetPort
+
+namespace cricket {
+
+// PortAllocator is responsible for allocating Port types for a given
+// P2PSocket. It also handles port freeing.
+//
+// Clients can override this class to control port allocation, including
+// what kinds of ports are allocated.
+
+const uint32 PORTALLOCATOR_DISABLE_UDP = 0x01;
+const uint32 PORTALLOCATOR_DISABLE_STUN = 0x02;
+const uint32 PORTALLOCATOR_DISABLE_RELAY = 0x04;
+const uint32 PORTALLOCATOR_DISABLE_TCP = 0x08;
+const uint32 PORTALLOCATOR_ENABLE_SHAKER = 0x10;
+
+const uint32 kDefaultPortAllocatorFlags = 0;
+
+class PortAllocatorSession : public sigslot::has_slots<> {
+public:
+ PortAllocatorSession(uint32 flags) : flags_(flags) {}
+
+ // Subclasses should clean up any ports created.
+ virtual ~PortAllocatorSession() {}
+
+ uint32 flags() const { return flags_; }
+ void set_flags(uint32 flags) { flags_ = flags; }
+
+ // Prepares an initial set of ports to try.
+ virtual void GetInitialPorts() = 0;
+
+ // Starts and stops the flow of additional ports to try.
+ virtual void StartGetAllPorts() = 0;
+ virtual void StopGetAllPorts() = 0;
+ virtual bool IsGettingAllPorts() = 0;
+
+ sigslot::signal2<PortAllocatorSession*, Port*> SignalPortReady;
+ sigslot::signal2<PortAllocatorSession*, const std::vector<Candidate>&> SignalCandidatesReady;
+
+ uint32 generation() { return generation_; }
+ void set_generation(uint32 generation) { generation_ = generation; }
+
+private:
+ uint32 flags_;
+ uint32 generation_;
+};
+
+class PortAllocator {
+public:
+ PortAllocator() : flags_(kDefaultPortAllocatorFlags) {}
+
+ virtual PortAllocatorSession *CreateSession(const std::string &name, const std::string &session_type) = 0;
+
+ uint32 flags() const { return flags_; }
+ void set_flags(uint32 flags) { flags_ = flags; }
+
+ const std::string& user_agent() const { return agent_; }
+ const talk_base::ProxyInfo& proxy() const { return proxy_; }
+ void set_proxy(const std::string& agent, const talk_base::ProxyInfo& proxy) {
+ agent_ = agent; proxy_ = proxy;
+ }
+
+protected:
+ uint32 flags_;
+ std::string agent_;
+ talk_base::ProxyInfo proxy_;
+};
+
+} // namespace cricket
+
+#endif // _PORTALLOCATOR_H_
diff --git a/Plugins/jingle/libjingle/talk/p2p/base/pseudotcp.cc b/Plugins/jingle/libjingle/talk/p2p/base/pseudotcp.cc
new file mode 100644
index 0000000..2f52d65
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/p2p/base/pseudotcp.cc
@@ -0,0 +1,1071 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "talk/base/basicdefs.h"
+#include "talk/base/basictypes.h"
+#include "talk/base/byteorder.h"
+#include "talk/base/common.h"
+#include "talk/base/logging.h"
+#include "talk/base/socket.h"
+#include "talk/base/stringutils.h"
+#include "talk/base/time.h"
+#include "talk/p2p/base/pseudotcp.h"
+
+#ifdef POSIX
+extern "C" {
+#include <errno.h>
+}
+#endif // POSIX
+
+// The following logging is for detailed (packet-level) pseudotcp analysis only.
+#define _DBG_NONE 0
+#define _DBG_NORMAL 1
+#define _DBG_VERBOSE 2
+#define _DEBUGMSG _DBG_NONE
+
+namespace cricket {
+
+//////////////////////////////////////////////////////////////////////
+// Network Constants
+//////////////////////////////////////////////////////////////////////
+
+// Standard MTUs
+const uint16 PACKET_MAXIMUMS[] = {
+ 65535, // Theoretical maximum, Hyperchannel
+ 32000, // Nothing
+ 17914, // 16Mb IBM Token Ring
+ 8166, // IEEE 802.4
+ //4464, // IEEE 802.5 (4Mb max)
+ 4352, // FDDI
+ //2048, // Wideband Network
+ 2002, // IEEE 802.5 (4Mb recommended)
+ //1536, // Expermental Ethernet Networks
+ //1500, // Ethernet, Point-to-Point (default)
+ 1492, // IEEE 802.3
+ 1006, // SLIP, ARPANET
+ //576, // X.25 Networks
+ //544, // DEC IP Portal
+ //512, // NETBIOS
+ 508, // IEEE 802/Source-Rt Bridge, ARCNET
+ 296, // Point-to-Point (low delay)
+ //68, // Official minimum
+ 0, // End of list marker
+};
+
+const uint32 MAX_PACKET = 65535;
+// Note: we removed lowest level because packet overhead was larger!
+const uint32 MIN_PACKET = 296;
+
+const uint32 IP_HEADER_SIZE = 20; // (+ up to 40 bytes of options?)
+const uint32 ICMP_HEADER_SIZE = 8;
+const uint32 UDP_HEADER_SIZE = 8;
+// TODO: Make JINGLE_HEADER_SIZE transparent to this code?
+const uint32 JINGLE_HEADER_SIZE = 64; // when relay framing is in use
+
+//////////////////////////////////////////////////////////////////////
+// Global Constants and Functions
+//////////////////////////////////////////////////////////////////////
+//
+// 0 1 2 3
+// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// 0 | Conversation Number |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// 4 | Sequence Number |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// 8 | Acknowledgment Number |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | | |U|A|P|R|S|F| |
+// 12 | Control | |R|C|S|S|Y|I| Window |
+// | | |G|K|H|T|N|N| |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// 16 | Timestamp sending |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// 20 | Timestamp receiving |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// 24 | data |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+//
+//////////////////////////////////////////////////////////////////////
+
+#define PSEUDO_KEEPALIVE 0
+
+const uint32 MAX_SEQ = 0xFFFFFFFF;
+const uint32 HEADER_SIZE = 24;
+const uint32 PACKET_OVERHEAD = HEADER_SIZE + UDP_HEADER_SIZE + IP_HEADER_SIZE + JINGLE_HEADER_SIZE;
+
+const uint32 MIN_RTO = 250; // 250 ms (RFC1122, Sec 4.2.3.1 "fractions of a second")
+const uint32 DEF_RTO = 3000; // 3 seconds (RFC1122, Sec 4.2.3.1)
+const uint32 MAX_RTO = 60000; // 60 seconds
+const uint32 ACK_DELAY = 100; // 100 milliseconds
+
+const uint8 FLAG_CTL = 0x02;
+const uint8 FLAG_RST = 0x04;
+
+const uint8 CTL_CONNECT = 0;
+//const uint8 CTL_REDIRECT = 1;
+const uint8 CTL_EXTRA = 255;
+
+/*
+const uint8 FLAG_FIN = 0x01;
+const uint8 FLAG_SYN = 0x02;
+const uint8 FLAG_ACK = 0x10;
+*/
+
+const uint32 CTRL_BOUND = 0x80000000;
+
+const long DEFAULT_TIMEOUT = 4000; // If there are no pending clocks, wake up every 4 seconds
+const long CLOSED_TIMEOUT = 60 * 1000; // If the connection is closed, once per minute
+
+#if PSEUDO_KEEPALIVE
+// !?! Rethink these times
+const uint32 IDLE_PING = 20 * 1000; // 20 seconds (note: WinXP SP2 firewall udp timeout is 90 seconds)
+const uint32 IDLE_TIMEOUT = 90 * 1000; // 90 seconds;
+#endif // PSEUDO_KEEPALIVE
+
+//////////////////////////////////////////////////////////////////////
+// Helper Functions
+//////////////////////////////////////////////////////////////////////
+
+inline void long_to_bytes(uint32 val, void* buf) {
+ *static_cast<uint32*>(buf) = talk_base::HostToNetwork32(val);
+}
+
+inline void short_to_bytes(uint16 val, void* buf) {
+ *static_cast<uint16*>(buf) = talk_base::HostToNetwork16(val);
+}
+
+inline uint32 bytes_to_long(const void* buf) {
+ return talk_base::NetworkToHost32(*static_cast<const uint32*>(buf));
+}
+
+inline uint16 bytes_to_short(const void* buf) {
+ return talk_base::NetworkToHost16(*static_cast<const uint16*>(buf));
+}
+
+uint32 bound(uint32 lower, uint32 middle, uint32 upper) {
+ return talk_base::_min(talk_base::_max(lower, middle), upper);
+}
+
+//////////////////////////////////////////////////////////////////////
+// Debugging Statistics
+//////////////////////////////////////////////////////////////////////
+
+#if 0 // Not used yet
+
+enum Stat {
+ S_SENT_PACKET, // All packet sends
+ S_RESENT_PACKET, // All packet sends that are retransmits
+ S_RECV_PACKET, // All packet receives
+ S_RECV_NEW, // All packet receives that are too new
+ S_RECV_OLD, // All packet receives that are too old
+ S_NUM_STATS
+};
+
+const char* const STAT_NAMES[S_NUM_STATS] = {
+ "snt",
+ "snt-r",
+ "rcv"
+ "rcv-n",
+ "rcv-o"
+};
+
+int g_stats[S_NUM_STATS];
+inline void Incr(Stat s) { ++g_stats[s]; }
+void ReportStats() {
+ char buffer[256];
+ size_t len = 0;
+ for (int i=0; i<S_NUM_STATS; ++i) {
+ len += talk_base::sprintfn(buffer, ARRAY_SIZE(buffer), "%s%s:%d",
+ (i == 0) ? "" : ",", STAT_NAMES[i], g_stats[i]);
+ g_stats[i] = 0;
+ }
+ LOG(LS_INFO) << "Stats[" << buffer << "]";
+}
+
+#endif
+
+//////////////////////////////////////////////////////////////////////
+// PseudoTcp
+//////////////////////////////////////////////////////////////////////
+
+uint32 PseudoTcp::Now() {
+#if 0 // Use this to synchronize timers with logging timestamps (easier debug)
+ return talk_base::ElapsedTime();
+#else
+ return talk_base::Time();
+#endif
+}
+
+PseudoTcp::PseudoTcp(IPseudoTcpNotify * notify, uint32 conv)
+ : m_notify(notify), m_shutdown(SD_NONE), m_error(0) {
+
+ // Sanity check on buffer sizes (needed for OnTcpWriteable notification logic)
+ ASSERT(sizeof(m_rbuf) + MIN_PACKET < sizeof(m_sbuf));
+
+ uint32 now = Now();
+
+ m_state = TCP_LISTEN;
+ m_conv = conv;
+ m_rcv_wnd = sizeof(m_rbuf);
+ m_snd_nxt = m_slen = 0;
+ m_snd_wnd = 1;
+ m_snd_una = m_rcv_nxt = m_rlen = 0;
+ m_bReadEnable = true;
+ m_bWriteEnable = false;
+ m_t_ack = 0;
+
+ m_msslevel = 0;
+ m_largest = 0;
+ ASSERT(MIN_PACKET > PACKET_OVERHEAD);
+ m_mss = MIN_PACKET - PACKET_OVERHEAD;
+ m_mtu_advise = MAX_PACKET;
+
+ m_rto_base = 0;
+
+ m_cwnd = 2 * m_mss;
+ m_ssthresh = sizeof(m_rbuf);
+ m_lastrecv = m_lastsend = m_lasttraffic = now;
+ m_bOutgoing = false;
+
+ m_dup_acks = 0;
+ m_recover = 0;
+
+ m_ts_recent = m_ts_lastack = 0;
+
+ m_rx_rto = DEF_RTO;
+ m_rx_srtt = m_rx_rttvar = 0;
+}
+
+PseudoTcp::~PseudoTcp() {
+}
+
+int
+PseudoTcp::Connect() {
+ if (m_state != TCP_LISTEN) {
+ m_error = EINVAL;
+ return -1;
+ }
+
+ m_state = TCP_SYN_SENT;
+ LOG(LS_INFO) << "State: TCP_SYN_SENT";
+
+ char buffer[1];
+ buffer[0] = CTL_CONNECT;
+ queue(buffer, 1, true);
+ attemptSend();
+
+ return 0;
+}
+
+void
+PseudoTcp::NotifyMTU(uint16 mtu) {
+ m_mtu_advise = mtu;
+ if (m_state == TCP_ESTABLISHED) {
+ adjustMTU();
+ }
+}
+
+void
+PseudoTcp::NotifyClock(uint32 now) {
+ if (m_state == TCP_CLOSED)
+ return;
+
+ // Check if it's time to retransmit a segment
+ if (m_rto_base && (talk_base::TimeDiff(m_rto_base + m_rx_rto, now) <= 0)) {
+ if (m_slist.empty()) {
+ ASSERT(false);
+ } else {
+ // Note: (m_slist.front().xmit == 0)) {
+ // retransmit segments
+#if _DEBUGMSG >= _DBG_NORMAL
+ LOG(LS_INFO) << "timeout retransmit (rto: " << m_rx_rto
+ << ") (rto_base: " << m_rto_base
+ << ") (now: " << now
+ << ") (dup_acks: " << static_cast<unsigned>(m_dup_acks)
+ << ")";
+#endif // _DEBUGMSG
+ if (!transmit(m_slist.begin(), now)) {
+ closedown(ECONNABORTED);
+ return;
+ }
+
+ uint32 nInFlight = m_snd_nxt - m_snd_una;
+ m_ssthresh = talk_base::_max(nInFlight / 2, 2 * m_mss);
+ //LOG(LS_INFO) << "m_ssthresh: " << m_ssthresh << " nInFlight: " << nInFlight << " m_mss: " << m_mss;
+ m_cwnd = m_mss;
+
+ // Back off retransmit timer. Note: the limit is lower when connecting.
+ uint32 rto_limit = (m_state < TCP_ESTABLISHED) ? DEF_RTO : MAX_RTO;
+ m_rx_rto = talk_base::_min(rto_limit, m_rx_rto * 2);
+ m_rto_base = now;
+ }
+ }
+
+ // Check if it's time to probe closed windows
+ if ((m_snd_wnd == 0)
+ && (talk_base::TimeDiff(m_lastsend + m_rx_rto, now) <= 0)) {
+ if (talk_base::TimeDiff(now, m_lastrecv) >= 15000) {
+ closedown(ECONNABORTED);
+ return;
+ }
+
+ // probe the window
+ packet(m_snd_nxt - 1, 0, 0, 0);
+ m_lastsend = now;
+
+ // back off retransmit timer
+ m_rx_rto = talk_base::_min(MAX_RTO, m_rx_rto * 2);
+ }
+
+ // Check if it's time to send delayed acks
+ if (m_t_ack && (talk_base::TimeDiff(m_t_ack + ACK_DELAY, now) <= 0)) {
+ packet(m_snd_nxt, 0, 0, 0);
+ }
+
+#if PSEUDO_KEEPALIVE
+ // Check for idle timeout
+ if ((m_state == TCP_ESTABLISHED) && (TimeDiff(m_lastrecv + IDLE_TIMEOUT, now) <= 0)) {
+ closedown(ECONNABORTED);
+ return;
+ }
+
+ // Check for ping timeout (to keep udp mapping open)
+ if ((m_state == TCP_ESTABLISHED) && (TimeDiff(m_lasttraffic + (m_bOutgoing ? IDLE_PING * 3/2 : IDLE_PING), now) <= 0)) {
+ packet(m_snd_nxt, 0, 0, 0);
+ }
+#endif // PSEUDO_KEEPALIVE
+}
+
+bool
+PseudoTcp::NotifyPacket(const char * buffer, size_t len) {
+ if (len > MAX_PACKET) {
+ LOG_F(WARNING) << "packet too large";
+ return false;
+ }
+ return parse(reinterpret_cast<const uint8 *>(buffer), uint32(len));
+}
+
+bool
+PseudoTcp::GetNextClock(uint32 now, long& timeout) {
+ return clock_check(now, timeout);
+}
+
+//
+// IPStream Implementation
+//
+
+int
+PseudoTcp::Recv(char * buffer, size_t len) {
+ if (m_state != TCP_ESTABLISHED) {
+ m_error = ENOTCONN;
+ return SOCKET_ERROR;
+ }
+
+ if (m_rlen == 0) {
+ m_bReadEnable = true;
+ m_error = EWOULDBLOCK;
+ return SOCKET_ERROR;
+ }
+
+ uint32 read = talk_base::_min(uint32(len), m_rlen);
+ memcpy(buffer, m_rbuf, read);
+ m_rlen -= read;
+
+ // !?! until we create a circular buffer, we need to move all of the rest of the buffer up!
+ memmove(m_rbuf, m_rbuf + read, sizeof(m_rbuf) - read/*m_rlen*/);
+
+ if ((sizeof(m_rbuf) - m_rlen - m_rcv_wnd)
+ >= talk_base::_min<uint32>(sizeof(m_rbuf) / 2, m_mss)) {
+ bool bWasClosed = (m_rcv_wnd == 0); // !?! Not sure about this was closed business
+
+ m_rcv_wnd = sizeof(m_rbuf) - m_rlen;
+
+ if (bWasClosed) {
+ attemptSend(sfImmediateAck);
+ }
+ }
+
+ return read;
+}
+
+int
+PseudoTcp::Send(const char * buffer, size_t len) {
+ if (m_state != TCP_ESTABLISHED) {
+ m_error = ENOTCONN;
+ return SOCKET_ERROR;
+ }
+
+ if (m_slen == sizeof(m_sbuf)) {
+ m_bWriteEnable = true;
+ m_error = EWOULDBLOCK;
+ return SOCKET_ERROR;
+ }
+
+ int written = queue(buffer, uint32(len), false);
+ attemptSend();
+ return written;
+}
+
+void
+PseudoTcp::Close(bool force) {
+ LOG_F(LS_VERBOSE) << "(" << (force ? "true" : "false") << ")";
+ m_shutdown = force ? SD_FORCEFUL : SD_GRACEFUL;
+}
+
+int PseudoTcp::GetError() {
+ return m_error;
+}
+
+//
+// Internal Implementation
+//
+
+uint32
+PseudoTcp::queue(const char * data, uint32 len, bool bCtrl) {
+ if (len > sizeof(m_sbuf) - m_slen) {
+ ASSERT(!bCtrl);
+ len = sizeof(m_sbuf) - m_slen;
+ }
+
+ // We can concatenate data if the last segment is the same type
+ // (control v. regular data), and has not been transmitted yet
+ if (!m_slist.empty() && (m_slist.back().bCtrl == bCtrl) && (m_slist.back().xmit == 0)) {
+ m_slist.back().len += len;
+ } else {
+ SSegment sseg(m_snd_una + m_slen, len, bCtrl);
+ m_slist.push_back(sseg);
+ }
+
+ memcpy(m_sbuf + m_slen, data, len);
+ m_slen += len;
+ //LOG(LS_INFO) << "PseudoTcp::queue - m_slen = " << m_slen;
+ return len;
+}
+
+IPseudoTcpNotify::WriteResult
+PseudoTcp::packet(uint32 seq, uint8 flags, const char * data, uint32 len) {
+ ASSERT(HEADER_SIZE + len <= MAX_PACKET);
+
+ uint32 now = Now();
+
+ uint8 buffer[MAX_PACKET];
+ long_to_bytes(m_conv, buffer);
+ long_to_bytes(seq, buffer + 4);
+ long_to_bytes(m_rcv_nxt, buffer + 8);
+ buffer[12] = 0;
+ buffer[13] = flags;
+ short_to_bytes(uint16(m_rcv_wnd), buffer + 14);
+
+ // Timestamp computations
+ long_to_bytes(now, buffer + 16);
+ long_to_bytes(m_ts_recent, buffer + 20);
+ m_ts_lastack = m_rcv_nxt;
+
+ memcpy(buffer + HEADER_SIZE, data, len);
+
+#if _DEBUGMSG >= _DBG_VERBOSE
+ LOG(LS_INFO) << "<-- <CONV=" << m_conv
+ << "><FLG=" << static_cast<unsigned>(flags)
+ << "><SEQ=" << seq << ":" << seq + len
+ << "><ACK=" << m_rcv_nxt
+ << "><WND=" << m_rcv_wnd
+ << "><TS=" << (now % 10000)
+ << "><TSR=" << (m_ts_recent % 10000)
+ << "><LEN=" << len << ">";
+#endif // _DEBUGMSG
+
+ IPseudoTcpNotify::WriteResult wres = m_notify->TcpWritePacket(this, reinterpret_cast<char *>(buffer), len + HEADER_SIZE);
+ // Note: When data is NULL, this is an ACK packet. We don't read the return value for those,
+ // and thus we won't retry. So go ahead and treat the packet as a success (basically simulate
+ // as if it were dropped), which will prevent our timers from being messed up.
+ if ((wres != IPseudoTcpNotify::WR_SUCCESS) && (NULL != data))
+ return wres;
+
+ m_t_ack = 0;
+ if (len > 0) {
+ m_lastsend = now;
+ }
+ m_lasttraffic = now;
+ m_bOutgoing = true;
+
+ return IPseudoTcpNotify::WR_SUCCESS;
+}
+
+bool
+PseudoTcp::parse(const uint8 * buffer, uint32 size) {
+ if (size < 12)
+ return false;
+
+ Segment seg;
+ seg.conv = bytes_to_long(buffer);
+ seg.seq = bytes_to_long(buffer + 4);
+ seg.ack = bytes_to_long(buffer + 8);
+ seg.flags = buffer[13];
+ seg.wnd = bytes_to_short(buffer + 14);
+
+ seg.tsval = bytes_to_long(buffer + 16);
+ seg.tsecr = bytes_to_long(buffer + 20);
+
+ seg.data = reinterpret_cast<const char *>(buffer) + HEADER_SIZE;
+ seg.len = size - HEADER_SIZE;
+
+#if _DEBUGMSG >= _DBG_VERBOSE
+ LOG(LS_INFO) << "--> <CONV=" << seg.conv
+ << "><FLG=" << static_cast<unsigned>(seg.flags)
+ << "><SEQ=" << seg.seq << ":" << seg.seq + seg.len
+ << "><ACK=" << seg.ack
+ << "><WND=" << seg.wnd
+ << "><TS=" << (seg.tsval % 10000)
+ << "><TSR=" << (seg.tsecr % 10000)
+ << "><LEN=" << seg.len << ">";
+#endif // _DEBUGMSG
+
+ return process(seg);
+}
+
+bool
+PseudoTcp::clock_check(uint32 now, long& nTimeout) {
+ if (m_shutdown == SD_FORCEFUL)
+ return false;
+
+ if ((m_shutdown == SD_GRACEFUL)
+ && ((m_state != TCP_ESTABLISHED)
+ || ((m_slen == 0) && (m_t_ack == 0)))) {
+ return false;
+ }
+
+ if (m_state == TCP_CLOSED) {
+ nTimeout = CLOSED_TIMEOUT;
+ return true;
+ }
+
+ nTimeout = DEFAULT_TIMEOUT;
+
+ if (m_t_ack) {
+ nTimeout = talk_base::_min(nTimeout,
+ talk_base::TimeDiff(m_t_ack + ACK_DELAY, now));
+ }
+ if (m_rto_base) {
+ nTimeout = talk_base::_min(nTimeout,
+ talk_base::TimeDiff(m_rto_base + m_rx_rto, now));
+ }
+ if (m_snd_wnd == 0) {
+ nTimeout = talk_base::_min(nTimeout, talk_base::TimeDiff(m_lastsend + m_rx_rto, now));
+ }
+#if PSEUDO_KEEPALIVE
+ if (m_state == TCP_ESTABLISHED) {
+ nTimeout = talk_base::_min(nTimeout,
+ talk_base::TimeDiff(m_lasttraffic + (m_bOutgoing ? IDLE_PING * 3/2 : IDLE_PING), now));
+ }
+#endif // PSEUDO_KEEPALIVE
+ return true;
+}
+
+bool
+PseudoTcp::process(Segment& seg) {
+ // If this is the wrong conversation, send a reset!?! (with the correct conversation?)
+ if (seg.conv != m_conv) {
+ //if ((seg.flags & FLAG_RST) == 0) {
+ // packet(tcb, seg.ack, 0, FLAG_RST, 0, 0);
+ //}
+ LOG_F(LS_ERROR) << "wrong conversation";
+ return false;
+ }
+
+ uint32 now = Now();
+ m_lasttraffic = m_lastrecv = now;
+ m_bOutgoing = false;
+
+ if (m_state == TCP_CLOSED) {
+ // !?! send reset?
+ LOG_F(LS_ERROR) << "closed";
+ return false;
+ }
+
+ // Check if this is a reset segment
+ if (seg.flags & FLAG_RST) {
+ closedown(ECONNRESET);
+ return false;
+ }
+
+ // Check for control data
+ bool bConnect = false;
+ if (seg.flags & FLAG_CTL) {
+ if (seg.len == 0) {
+ LOG_F(LS_ERROR) << "Missing control code";
+ return false;
+ } else if (seg.data[0] == CTL_CONNECT) {
+ bConnect = true;
+ if (m_state == TCP_LISTEN) {
+ m_state = TCP_SYN_RECEIVED;
+ LOG(LS_INFO) << "State: TCP_SYN_RECEIVED";
+ //m_notify->associate(addr);
+ char buffer[1];
+ buffer[0] = CTL_CONNECT;
+ queue(buffer, 1, true);
+ } else if (m_state == TCP_SYN_SENT) {
+ m_state = TCP_ESTABLISHED;
+ LOG(LS_INFO) << "State: TCP_ESTABLISHED";
+ adjustMTU();
+ if (m_notify) {
+ m_notify->OnTcpOpen(this);
+ }
+ //notify(evOpen);
+ }
+ } else {
+ LOG_F(LS_WARNING) << "Unknown control code: " << seg.data[0];
+ return false;
+ }
+ }
+
+ // Update timestamp
+ if ((seg.seq <= m_ts_lastack) && (m_ts_lastack < seg.seq + seg.len)) {
+ m_ts_recent = seg.tsval;
+ }
+
+ // Check if this is a valuable ack
+ if ((seg.ack > m_snd_una) && (seg.ack <= m_snd_nxt)) {
+ // Calculate round-trip time
+ if (seg.tsecr) {
+ long rtt = talk_base::TimeDiff(now, seg.tsecr);
+ if (rtt >= 0) {
+ if (m_rx_srtt == 0) {
+ m_rx_srtt = rtt;
+ m_rx_rttvar = rtt / 2;
+ } else {
+ m_rx_rttvar = (3 * m_rx_rttvar + abs(long(rtt - m_rx_srtt))) / 4;
+ m_rx_srtt = (7 * m_rx_srtt + rtt) / 8;
+ }
+ m_rx_rto = bound(MIN_RTO, m_rx_srtt + talk_base::_max(1LU, 4 * m_rx_rttvar), MAX_RTO);
+#if _DEBUGMSG >= _DBG_VERBOSE
+ LOG(LS_INFO) << "rtt: " << rtt
+ << " srtt: " << m_rx_srtt
+ << " rto: " << m_rx_rto;
+#endif // _DEBUGMSG
+ } else {
+ ASSERT(false);
+ }
+ }
+
+ m_snd_wnd = seg.wnd;
+
+ uint32 nAcked = seg.ack - m_snd_una;
+ m_snd_una = seg.ack;
+
+ m_rto_base = (m_snd_una == m_snd_nxt) ? 0 : now;
+
+ m_slen -= nAcked;
+ memmove(m_sbuf, m_sbuf + nAcked, m_slen);
+ //LOG(LS_INFO) << "PseudoTcp::process - m_slen = " << m_slen;
+
+ for (uint32 nFree = nAcked; nFree > 0; ) {
+ ASSERT(!m_slist.empty());
+ if (nFree < m_slist.front().len) {
+ m_slist.front().len -= nFree;
+ nFree = 0;
+ } else {
+ if (m_slist.front().len > m_largest) {
+ m_largest = m_slist.front().len;
+ }
+ nFree -= m_slist.front().len;
+ m_slist.pop_front();
+ }
+ }
+
+ if (m_dup_acks >= 3) {
+ if (m_snd_una >= m_recover) { // NewReno
+ uint32 nInFlight = m_snd_nxt - m_snd_una;
+ m_cwnd = talk_base::_min(m_ssthresh, nInFlight + m_mss); // (Fast Retransmit)
+#if _DEBUGMSG >= _DBG_NORMAL
+ LOG(LS_INFO) << "exit recovery";
+#endif // _DEBUGMSG
+ m_dup_acks = 0;
+ } else {
+#if _DEBUGMSG >= _DBG_NORMAL
+ LOG(LS_INFO) << "recovery retransmit";
+#endif // _DEBUGMSG
+ if (!transmit(m_slist.begin(), now)) {
+ closedown(ECONNABORTED);
+ return false;
+ }
+ m_cwnd += m_mss - talk_base::_min(nAcked, m_cwnd);
+ }
+ } else {
+ m_dup_acks = 0;
+ // Slow start, congestion avoidance
+ if (m_cwnd < m_ssthresh) {
+ m_cwnd += m_mss;
+ } else {
+ m_cwnd += talk_base::_max(1LU, m_mss * m_mss / m_cwnd);
+ }
+ }
+
+ // !?! A bit hacky
+ if ((m_state == TCP_SYN_RECEIVED) && !bConnect) {
+ m_state = TCP_ESTABLISHED;
+ LOG(LS_INFO) << "State: TCP_ESTABLISHED";
+ adjustMTU();
+ if (m_notify) {
+ m_notify->OnTcpOpen(this);
+ }
+ //notify(evOpen);
+ }
+
+ // If we make room in the send queue, notify the user
+ // The goal it to make sure we always have at least enough data to fill the
+ // window. We'd like to notify the app when we are halfway to that point.
+ const uint32 kIdealRefillSize = (sizeof(m_sbuf) + sizeof(m_rbuf)) / 2;
+ if (m_bWriteEnable && (m_slen < kIdealRefillSize)) {
+ m_bWriteEnable = false;
+ if (m_notify) {
+ m_notify->OnTcpWriteable(this);
+ }
+ //notify(evWrite);
+ }
+ } else if (seg.ack == m_snd_una) {
+ // !?! Note, tcp says don't do this... but otherwise how does a closed window become open?
+ m_snd_wnd = seg.wnd;
+
+ // Check duplicate acks
+ if (seg.len > 0) {
+ // it's a dup ack, but with a data payload, so don't modify m_dup_acks
+ } else if (m_snd_una != m_snd_nxt) {
+ m_dup_acks += 1;
+ if (m_dup_acks == 3) { // (Fast Retransmit)
+#if _DEBUGMSG >= _DBG_NORMAL
+ LOG(LS_INFO) << "enter recovery";
+ LOG(LS_INFO) << "recovery retransmit";
+#endif // _DEBUGMSG
+ if (!transmit(m_slist.begin(), now)) {
+ closedown(ECONNABORTED);
+ return false;
+ }
+ m_recover = m_snd_nxt;
+ uint32 nInFlight = m_snd_nxt - m_snd_una;
+ m_ssthresh = talk_base::_max(nInFlight / 2, 2 * m_mss);
+ //LOG(LS_INFO) << "m_ssthresh: " << m_ssthresh << " nInFlight: " << nInFlight << " m_mss: " << m_mss;
+ m_cwnd = m_ssthresh + 3 * m_mss;
+ } else if (m_dup_acks > 3) {
+ m_cwnd += m_mss;
+ }
+ } else {
+ m_dup_acks = 0;
+ }
+ }
+
+ // Conditions were acks must be sent:
+ // 1) Segment is too old (they missed an ACK) (immediately)
+ // 2) Segment is too new (we missed a segment) (immediately)
+ // 3) Segment has data (so we need to ACK!) (delayed)
+ // ... so the only time we don't need to ACK, is an empty segment that points to rcv_nxt!
+
+ SendFlags sflags = sfNone;
+ if (seg.seq != m_rcv_nxt) {
+ sflags = sfImmediateAck; // (Fast Recovery)
+ } else if (seg.len != 0) {
+ sflags = sfDelayedAck;
+ }
+#if _DEBUGMSG >= _DBG_NORMAL
+ if (sflags == sfImmediateAck) {
+ if (seg.seq > m_rcv_nxt) {
+ LOG_F(LS_INFO) << "too new";
+ } else if (seg.seq + seg.len <= m_rcv_nxt) {
+ LOG_F(LS_INFO) << "too old";
+ }
+ }
+#endif // _DEBUGMSG
+
+ // Adjust the incoming segment to fit our receive buffer
+ if (seg.seq < m_rcv_nxt) {
+ uint32 nAdjust = m_rcv_nxt - seg.seq;
+ if (nAdjust < seg.len) {
+ seg.seq += nAdjust;
+ seg.data += nAdjust;
+ seg.len -= nAdjust;
+ } else {
+ seg.len = 0;
+ }
+ }
+ if ((seg.seq + seg.len - m_rcv_nxt) > (sizeof(m_rbuf) - m_rlen)) {
+ uint32 nAdjust = seg.seq + seg.len - m_rcv_nxt - (sizeof(m_rbuf) - m_rlen);
+ if (nAdjust < seg.len) {
+ seg.len -= nAdjust;
+ } else {
+ seg.len = 0;
+ }
+ }
+
+ bool bIgnoreData = (seg.flags & FLAG_CTL) || (m_shutdown != SD_NONE);
+ bool bNewData = false;
+
+ if (seg.len > 0) {
+ if (bIgnoreData) {
+ if (seg.seq == m_rcv_nxt) {
+ m_rcv_nxt += seg.len;
+ }
+ } else {
+ uint32 nOffset = seg.seq - m_rcv_nxt;
+ memcpy(m_rbuf + m_rlen + nOffset, seg.data, seg.len);
+ if (seg.seq == m_rcv_nxt) {
+ m_rlen += seg.len;
+ m_rcv_nxt += seg.len;
+ m_rcv_wnd -= seg.len;
+ bNewData = true;
+
+ RList::iterator it = m_rlist.begin();
+ while ((it != m_rlist.end()) && (it->seq <= m_rcv_nxt)) {
+ if (it->seq + it->len > m_rcv_nxt) {
+ sflags = sfImmediateAck; // (Fast Recovery)
+ uint32 nAdjust = (it->seq + it->len) - m_rcv_nxt;
+#if _DEBUGMSG >= _DBG_NORMAL
+ LOG(LS_INFO) << "Recovered " << nAdjust << " bytes (" << m_rcv_nxt << " -> " << m_rcv_nxt + nAdjust << ")";
+#endif // _DEBUGMSG
+ m_rlen += nAdjust;
+ m_rcv_nxt += nAdjust;
+ m_rcv_wnd -= nAdjust;
+ }
+ it = m_rlist.erase(it);
+ }
+ } else {
+#if _DEBUGMSG >= _DBG_NORMAL
+ LOG(LS_INFO) << "Saving " << seg.len << " bytes (" << seg.seq << " -> " << seg.seq + seg.len << ")";
+#endif // _DEBUGMSG
+ RSegment rseg;
+ rseg.seq = seg.seq;
+ rseg.len = seg.len;
+ RList::iterator it = m_rlist.begin();
+ while ((it != m_rlist.end()) && (it->seq < rseg.seq)) {
+ ++it;
+ }
+ m_rlist.insert(it, rseg);
+ }
+ }
+ }
+
+ attemptSend(sflags);
+
+ // If we have new data, notify the user
+ if (bNewData && m_bReadEnable) {
+ m_bReadEnable = false;
+ if (m_notify) {
+ m_notify->OnTcpReadable(this);
+ }
+ //notify(evRead);
+ }
+
+ return true;
+}
+
+bool
+PseudoTcp::transmit(const SList::iterator& seg, uint32 now) {
+ if (seg->xmit >= ((m_state == TCP_ESTABLISHED) ? 15 : 30)) {
+ LOG_F(LS_VERBOSE) << "too many retransmits";
+ return false;
+ }
+
+ uint32 nTransmit = talk_base::_min(seg->len, m_mss);
+
+ while (true) {
+ uint32 seq = seg->seq;
+ uint8 flags = (seg->bCtrl ? FLAG_CTL : 0);
+ const char * buffer = m_sbuf + (seg->seq - m_snd_una);
+ IPseudoTcpNotify::WriteResult wres = this->packet(seq, flags, buffer, nTransmit);
+
+ if (wres == IPseudoTcpNotify::WR_SUCCESS)
+ break;
+
+ if (wres == IPseudoTcpNotify::WR_FAIL) {
+ LOG_F(LS_VERBOSE) << "packet failed";
+ return false;
+ }
+
+ ASSERT(wres == IPseudoTcpNotify::WR_TOO_LARGE);
+
+ while (true) {
+ if (PACKET_MAXIMUMS[m_msslevel + 1] == 0) {
+ LOG_F(LS_VERBOSE) << "MTU too small";
+ return false;
+ }
+ // !?! We need to break up all outstanding and pending packets and then retransmit!?!
+
+ m_mss = PACKET_MAXIMUMS[++m_msslevel] - PACKET_OVERHEAD;
+ m_cwnd = 2 * m_mss; // I added this... haven't researched actual formula
+ if (m_mss < nTransmit) {
+ nTransmit = m_mss;
+ break;
+ }
+ }
+#if _DEBUGMSG >= _DBG_NORMAL
+ LOG(LS_INFO) << "Adjusting mss to " << m_mss << " bytes";
+#endif // _DEBUGMSG
+ }
+
+ if (nTransmit < seg->len) {
+ LOG_F(LS_VERBOSE) << "mss reduced to " << m_mss;
+
+ SSegment subseg(seg->seq + nTransmit, seg->len - nTransmit, seg->bCtrl);
+ //subseg.tstamp = seg->tstamp;
+ subseg.xmit = seg->xmit;
+ seg->len = nTransmit;
+
+ SList::iterator next = seg;
+ m_slist.insert(++next, subseg);
+ }
+
+ if (seg->xmit == 0) {
+ m_snd_nxt += seg->len;
+ }
+ seg->xmit += 1;
+ //seg->tstamp = now;
+ if (m_rto_base == 0) {
+ m_rto_base = now;
+ }
+
+ return true;
+}
+
+void
+PseudoTcp::attemptSend(SendFlags sflags) {
+ uint32 now = Now();
+
+ if (talk_base::TimeDiff(now, m_lastsend) > static_cast<long>(m_rx_rto)) {
+ m_cwnd = m_mss;
+ }
+
+#if _DEBUGMSG
+ bool bFirst = true;
+ UNUSED(bFirst);
+#endif // _DEBUGMSG
+
+ while (true) {
+ uint32 cwnd = m_cwnd;
+ if ((m_dup_acks == 1) || (m_dup_acks == 2)) { // Limited Transmit
+ cwnd += m_dup_acks * m_mss;
+ }
+ uint32 nWindow = talk_base::_min(m_snd_wnd, cwnd);
+ uint32 nInFlight = m_snd_nxt - m_snd_una;
+ uint32 nUseable = (nInFlight < nWindow) ? (nWindow - nInFlight) : 0;
+
+ uint32 nAvailable = talk_base::_min(m_slen - nInFlight, m_mss);
+
+ if (nAvailable > nUseable) {
+ if (nUseable * 4 < nWindow) {
+ // RFC 813 - avoid SWS
+ nAvailable = 0;
+ } else {
+ nAvailable = nUseable;
+ }
+ }
+
+#if _DEBUGMSG >= _DBG_VERBOSE
+ if (bFirst) {
+ bFirst = false;
+ LOG(LS_INFO) << "[cwnd: " << m_cwnd
+ << " nWindow: " << nWindow
+ << " nInFlight: " << nInFlight
+ << " nAvailable: " << nAvailable
+ << " nQueued: " << m_slen - nInFlight
+ << " nEmpty: " << sizeof(m_sbuf) - m_slen
+ << " ssthresh: " << m_ssthresh << "]";
+ }
+#endif // _DEBUGMSG
+
+ if (nAvailable == 0) {
+ if (sflags == sfNone)
+ return;
+
+ // If this is an immediate ack, or the second delayed ack
+ if ((sflags == sfImmediateAck) || m_t_ack) {
+ packet(m_snd_nxt, 0, 0, 0);
+ } else {
+ m_t_ack = Now();
+ }
+ return;
+ }
+
+ // Nagle algorithm
+ if ((m_snd_nxt > m_snd_una) && (nAvailable < m_mss)) {
+ return;
+ }
+
+ // Find the next segment to transmit
+ SList::iterator it = m_slist.begin();
+ while (it->xmit > 0) {
+ ++it;
+ ASSERT(it != m_slist.end());
+ }
+ SList::iterator seg = it;
+
+ // If the segment is too large, break it into two
+ if (seg->len > nAvailable) {
+ SSegment subseg(seg->seq + nAvailable, seg->len - nAvailable, seg->bCtrl);
+ seg->len = nAvailable;
+ m_slist.insert(++it, subseg);
+ }
+
+ if (!transmit(seg, now)) {
+ LOG_F(LS_VERBOSE) << "transmit failed";
+ // TODO: consider closing socket
+ return;
+ }
+
+ sflags = sfNone;
+ }
+}
+
+void
+PseudoTcp::closedown(uint32 err) {
+ m_slen = 0;
+
+ LOG(LS_INFO) << "State: TCP_CLOSED";
+ m_state = TCP_CLOSED;
+ if (m_notify) {
+ m_notify->OnTcpClosed(this, err);
+ }
+ //notify(evClose, err);
+}
+
+void
+PseudoTcp::adjustMTU() {
+ // Determine our current mss level, so that we can adjust appropriately later
+ for (m_msslevel = 0; PACKET_MAXIMUMS[m_msslevel + 1] > 0; ++m_msslevel) {
+ if (static_cast<uint16>(PACKET_MAXIMUMS[m_msslevel]) <= m_mtu_advise) {
+ break;
+ }
+ }
+ m_mss = m_mtu_advise - PACKET_OVERHEAD;
+ // !?! Should we reset m_largest here?
+#if _DEBUGMSG >= _DBG_NORMAL
+ LOG(LS_INFO) << "Adjusting mss to " << m_mss << " bytes";
+#endif // _DEBUGMSG
+ // Enforce minimums on ssthresh and cwnd
+ m_ssthresh = talk_base::_max(m_ssthresh, 2 * m_mss);
+ m_cwnd = talk_base::_max(m_cwnd, m_mss);
+}
+
+} // namespace cricket
diff --git a/Plugins/jingle/libjingle/talk/p2p/base/pseudotcp.h b/Plugins/jingle/libjingle/talk/p2p/base/pseudotcp.h
new file mode 100644
index 0000000..cce23e1
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/p2p/base/pseudotcp.h
@@ -0,0 +1,182 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __PSEUDOTCP_H__
+#define __PSEUDOTCP_H__
+
+#include <list>
+#include "talk/base/basictypes.h"
+
+namespace cricket {
+
+//////////////////////////////////////////////////////////////////////
+// IPseudoTcpNotify
+//////////////////////////////////////////////////////////////////////
+
+class PseudoTcp;
+
+class IPseudoTcpNotify {
+public:
+ // Notification of tcp events
+ virtual void OnTcpOpen(PseudoTcp * tcp) = 0;
+ virtual void OnTcpReadable(PseudoTcp * tcp) = 0;
+ virtual void OnTcpWriteable(PseudoTcp * tcp) = 0;
+ virtual void OnTcpClosed(PseudoTcp * tcp, uint32 nError) = 0;
+
+ // Write the packet onto the network
+ enum WriteResult { WR_SUCCESS, WR_TOO_LARGE, WR_FAIL };
+ virtual WriteResult TcpWritePacket(PseudoTcp * tcp, const char * buffer, size_t len) = 0;
+};
+
+//////////////////////////////////////////////////////////////////////
+// PseudoTcp
+//////////////////////////////////////////////////////////////////////
+
+class PseudoTcp {
+public:
+ static uint32 Now();
+
+ PseudoTcp(IPseudoTcpNotify * notify, uint32 conv);
+ virtual ~PseudoTcp();
+
+ int Connect();
+ int Recv(char * buffer, size_t len);
+ int Send(const char * buffer, size_t len);
+ void Close(bool force);
+ int GetError();
+
+ enum TcpState { TCP_LISTEN, TCP_SYN_SENT, TCP_SYN_RECEIVED, TCP_ESTABLISHED, TCP_CLOSED };
+ TcpState State() const { return m_state; }
+
+ // Call this when the PMTU changes.
+ void NotifyMTU(uint16 mtu);
+
+ // Call this based on timeout value returned from GetNextClock.
+ // It's ok to call this too frequently.
+ void NotifyClock(uint32 now);
+
+ // Call this whenever a packet arrives.
+ // Returns true if the packet was processed successfully.
+ bool NotifyPacket(const char * buffer, size_t len);
+
+ // Call this to determine the next time NotifyClock should be called.
+ // Returns false if the socket is ready to be destroyed.
+ bool GetNextClock(uint32 now, long& timeout);
+
+protected:
+ enum SendFlags { sfNone, sfDelayedAck, sfImmediateAck };
+ enum {
+ // Note: can't go as high as 1024 * 64, because of uint16 precision
+ kRcvBufSize = 1024 * 60,
+ // Note: send buffer should be larger to make sure we can always fill the
+ // receiver window
+ kSndBufSize = 1024 * 90
+ };
+
+ struct Segment {
+ uint32 conv, seq, ack;
+ uint8 flags;
+ uint16 wnd;
+ const char * data;
+ uint32 len;
+ uint32 tsval, tsecr;
+ };
+
+ struct SSegment {
+ uint32 seq, len;
+ //uint32 tstamp;
+ uint8 xmit;
+ bool bCtrl;
+
+ SSegment(uint32 s, uint32 l, bool c) : seq(s), len(l), /*tstamp(0),*/ xmit(0), bCtrl(c) { }
+ };
+ typedef std::list<SSegment> SList;
+
+ struct RSegment {
+ uint32 seq, len;
+ };
+
+ uint32 queue(const char * data, uint32 len, bool bCtrl);
+
+ IPseudoTcpNotify::WriteResult packet(uint32 seq, uint8 flags, const char * data, uint32 len);
+ bool parse(const uint8 * buffer, uint32 size);
+
+ void attemptSend(SendFlags sflags = sfNone);
+
+ void closedown(uint32 err = 0);
+
+ bool clock_check(uint32 now, long& nTimeout);
+
+ bool process(Segment& seg);
+ bool transmit(const SList::iterator& seg, uint32 now);
+
+ void adjustMTU();
+
+private:
+ IPseudoTcpNotify * m_notify;
+ enum Shutdown { SD_NONE, SD_GRACEFUL, SD_FORCEFUL } m_shutdown;
+ int m_error;
+
+ // TCB data
+ TcpState m_state;
+ uint32 m_conv;
+ bool m_bReadEnable, m_bWriteEnable, m_bOutgoing;
+ uint32 m_lasttraffic;
+
+ // Incoming data
+ typedef std::list<RSegment> RList;
+ RList m_rlist;
+ char m_rbuf[kRcvBufSize];
+ uint32 m_rcv_nxt, m_rcv_wnd, m_rlen, m_lastrecv;
+
+ // Outgoing data
+ SList m_slist;
+ char m_sbuf[kSndBufSize];
+ uint32 m_snd_nxt, m_snd_wnd, m_slen, m_lastsend, m_snd_una;
+ // Maximum segment size, estimated protocol level, largest segment sent
+ uint32 m_mss, m_msslevel, m_largest, m_mtu_advise;
+ // Retransmit timer
+ uint32 m_rto_base;
+
+ // Timestamp tracking
+ uint32 m_ts_recent, m_ts_lastack;
+
+ // Round-trip calculation
+ uint32 m_rx_rttvar, m_rx_srtt, m_rx_rto;
+
+ // Congestion avoidance, Fast retransmit/recovery, Delayed ACKs
+ uint32 m_ssthresh, m_cwnd;
+ uint8 m_dup_acks;
+ uint32 m_recover;
+ uint32 m_t_ack;
+};
+
+//////////////////////////////////////////////////////////////////////
+
+} // namespace cricket
+
+#endif // __PSEUDOTCP_H__
diff --git a/Plugins/jingle/libjingle/talk/p2p/base/rawtransport.cc b/Plugins/jingle/libjingle/talk/p2p/base/rawtransport.cc
new file mode 100644
index 0000000..f2e230b
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/p2p/base/rawtransport.cc
@@ -0,0 +1,150 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "talk/p2p/base/rawtransport.h"
+#include "talk/base/common.h"
+#include "talk/p2p/base/constants.h"
+#include "talk/p2p/base/sessionmanager.h"
+#include "talk/p2p/base/rawtransportchannel.h"
+#include "talk/xmllite/qname.h"
+#include "talk/xmllite/xmlelement.h"
+#include "talk/xmpp/constants.h"
+
+namespace cricket {
+
+const std::string kNsRawTransport("http://www.google.com/transport/raw-udp");
+const buzz::QName kQnRawTransport(true, kNsRawTransport, "transport");
+const buzz::QName kQnRawChannel(true, kNsRawTransport, "channel");
+const buzz::QName kQnRawBehindSymmetricNat(true, buzz::STR_EMPTY,
+ "behind-symmetric-nat");
+const buzz::QName kQnRawCanReceiveFromSymmetricNat(true, buzz::STR_EMPTY,
+ "can-receive-from-symmetric-nat");
+
+RawTransport::RawTransport(SessionManager* session_manager)
+ : Transport(session_manager, kNsRawTransport) {
+}
+
+RawTransport::~RawTransport() {
+ DestroyAllChannels();
+}
+
+buzz::XmlElement* RawTransport::CreateTransportOffer() {
+ buzz::XmlElement* xml = new buzz::XmlElement(kQnRawTransport, true);
+
+ // Assume that we are behind a symmetric NAT. Also note that we can't
+ // handle the adjustment necessary to talk to someone else who is behind
+ // a symmetric NAT.
+ xml->AddAttr(kQnRawBehindSymmetricNat, "true");
+ xml->AddAttr(kQnRawCanReceiveFromSymmetricNat, "false");
+
+ return xml;
+}
+
+buzz::XmlElement* RawTransport::CreateTransportAnswer() {
+ return new buzz::XmlElement(kQnRawTransport, true);
+}
+
+bool RawTransport::OnTransportOffer(const buzz::XmlElement* elem) {
+ ASSERT(elem->Name() == kQnRawTransport);
+
+ // If the other side is behind a symmetric NAT then we can't talk to him.
+ // We also bail if this attribute isn't specified.
+ if (!elem->HasAttr(kQnRawBehindSymmetricNat)
+ || elem->Attr(kQnRawBehindSymmetricNat) != "false") {
+ return false;
+ }
+
+ // If the other side doesn't explicitly state that he can receive from
+ // someone behind a symmetric NAT, we bail.
+ if (!elem->HasAttr(kQnRawCanReceiveFromSymmetricNat)
+ || elem->Attr(kQnRawCanReceiveFromSymmetricNat) != "true") {
+ return false;
+ }
+
+ // We don't support any options, so we ignore them.
+ return true;
+}
+
+bool RawTransport::OnTransportAnswer(const buzz::XmlElement* elem) {
+ ASSERT(elem->Name() == kQnRawTransport);
+ // We don't support any options. We fail if any are given. The other side
+ // should know from our request that we expected an empty response.
+ return elem->FirstChild() == NULL;
+}
+
+bool RawTransport::OnTransportMessage(const buzz::XmlElement* msg,
+ const buzz::XmlElement* stanza) {
+ ASSERT(msg->Name() == kQnRawTransport);
+ for (const buzz::XmlElement* elem = msg->FirstElement();
+ elem != NULL;
+ elem = elem->NextElement()) {
+ if (elem->Name() == kQnRawChannel) {
+ talk_base::SocketAddress addr;
+ if (!ParseAddress(stanza, elem, &addr))
+ return false;
+
+ ForwardChannelMessage(elem->Attr(buzz::QN_NAME),
+ new buzz::XmlElement(*elem));
+ }
+ }
+ return true;
+}
+
+bool RawTransport::OnTransportError(const buzz::XmlElement* session_msg,
+ const buzz::XmlElement* error) {
+ return true;
+}
+
+bool RawTransport::ParseAddress(const buzz::XmlElement* stanza,
+ const buzz::XmlElement* elem,
+ talk_base::SocketAddress* addr) {
+ // Make sure the required attributes exist
+ if (!elem->HasAttr(buzz::QN_NAME) ||
+ !elem->HasAttr(QN_ADDRESS) ||
+ !elem->HasAttr(QN_PORT)) {
+ return BadRequest(stanza, "channel missing required attribute", NULL);
+ }
+
+ // Make sure the channel named actually exists.
+ if (!HasChannel(elem->Attr(buzz::QN_NAME)))
+ return BadRequest(stanza, "channel named does not exist", NULL);
+
+ // Parse the address.
+ return Transport::ParseAddress(stanza, elem, addr);
+}
+
+TransportChannelImpl* RawTransport::CreateTransportChannel(
+ const std::string& name, const std::string &session_type) {
+ return new RawTransportChannel(
+ name, session_type, this, session_manager()->port_allocator());
+}
+
+void RawTransport::DestroyTransportChannel(TransportChannelImpl* channel) {
+ delete channel;
+}
+
+} // namespace cricket
diff --git a/Plugins/jingle/libjingle/talk/p2p/base/rawtransport.h b/Plugins/jingle/libjingle/talk/p2p/base/rawtransport.h
new file mode 100644
index 0000000..d273b40
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/p2p/base/rawtransport.h
@@ -0,0 +1,84 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _CRICKET_P2P_BASE_RAWTRANSPORT_H_
+#define _CRICKET_P2P_BASE_RAWTRANSPORT_H_
+
+#include "talk/p2p/base/transport.h"
+
+namespace cricket {
+
+// Xml names used to name this transport and create our elements
+extern const std::string kNsRawTransport;
+extern const buzz::QName kQnRawTransport;
+extern const buzz::QName kQnRawChannel;
+extern const buzz::QName kQnRawNatType;
+extern const buzz::QName kQnRawNatTypeAllowed;
+
+// Implements a transport that only sends raw packets, no STUN. As a result,
+// it cannot do pings to determine connectivity, so it only uses a single port
+// that it thinks will work.
+class RawTransport: public Transport {
+ public:
+ RawTransport(SessionManager* session_manager);
+ virtual ~RawTransport();
+
+ // Handles the raw transport protocol descriptions, which are trivial.
+ virtual buzz::XmlElement* CreateTransportOffer();
+ virtual buzz::XmlElement* CreateTransportAnswer();
+ virtual bool OnTransportOffer(const buzz::XmlElement* elem);
+ virtual bool OnTransportAnswer(const buzz::XmlElement* elem);
+
+ // Forwards messages containing channel addresses to the appropriate channel.
+ virtual bool OnTransportMessage(const buzz::XmlElement* msg,
+ const buzz::XmlElement* stanza);
+ virtual bool OnTransportError(const buzz::XmlElement* session_msg,
+ const buzz::XmlElement* error);
+
+ protected:
+ // Creates and destroys raw channels.
+ virtual TransportChannelImpl* CreateTransportChannel(
+ const std::string& name, const std::string &session_type);
+ virtual void DestroyTransportChannel(TransportChannelImpl* channel);
+
+ private:
+ // Parses the given element, which should describe the address to use for a
+ // given channel. This will return false and signal an error if the address
+ // or channel name is bad.
+ bool ParseAddress(const buzz::XmlElement* stanza,
+ const buzz::XmlElement* elem,
+ talk_base::SocketAddress* addr);
+
+ friend class RawTransportChannel; // For ParseAddress.
+
+ DISALLOW_EVIL_CONSTRUCTORS(RawTransport);
+};
+
+} // namespace cricket
+
+
+#endif // _CRICKET_P2P_BASE_RAWTRANSPORT_H_
diff --git a/Plugins/jingle/libjingle/talk/p2p/base/rawtransportchannel.cc b/Plugins/jingle/libjingle/talk/p2p/base/rawtransportchannel.cc
new file mode 100644
index 0000000..13c1886
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/p2p/base/rawtransportchannel.cc
@@ -0,0 +1,259 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "talk/p2p/base/rawtransportchannel.h"
+#include "talk/base/common.h"
+#include "talk/p2p/base/constants.h"
+#include "talk/p2p/base/port.h"
+#include "talk/p2p/base/portallocator.h"
+#include "talk/p2p/base/rawtransport.h"
+#include "talk/p2p/base/relayport.h"
+#include "talk/p2p/base/sessionmanager.h"
+#include "talk/p2p/base/stunport.h"
+#include "talk/xmllite/qname.h"
+#include "talk/xmllite/xmlelement.h"
+#include "talk/xmpp/constants.h"
+
+namespace {
+
+const int MSG_DESTROY_UNUSED_PORTS = 1;
+
+} // namespace
+
+namespace cricket {
+
+RawTransportChannel::RawTransportChannel(
+ const std::string &name, const std::string &session_type, RawTransport* transport,
+ PortAllocator *allocator)
+ : TransportChannelImpl(name, session_type), raw_transport_(transport),
+ allocator_(allocator), allocator_session_(NULL), stun_port_(NULL),
+ relay_port_(NULL), port_(NULL), use_relay_(false) {
+}
+
+RawTransportChannel::~RawTransportChannel() {
+ delete allocator_session_;
+}
+
+int RawTransportChannel::SendPacket(const char *data, size_t size) {
+ if (port_ == NULL)
+ return -1;
+ if (remote_address_.IsAny())
+ return -1;
+ return port_->SendTo(data, size, remote_address_, true);
+}
+
+int RawTransportChannel::SetOption(talk_base::Socket::Option opt, int value) {
+ // TODO: allow these to be set before we have a port
+ if (port_ == NULL)
+ return -1;
+ return port_->SetOption(opt, value);
+}
+
+int RawTransportChannel::GetError() {
+ return (port_ != NULL) ? port_->GetError() : 0;
+}
+
+void RawTransportChannel::Connect() {
+ // Create an allocator that only returns stun and relay ports.
+ allocator_session_ = allocator_->CreateSession(name(), session_type());
+
+ uint32 flags = PORTALLOCATOR_DISABLE_UDP | PORTALLOCATOR_DISABLE_TCP;
+
+#if !defined(FEATURE_ENABLE_STUN_CLASSIFICATION)
+ flags |= PORTALLOCATOR_DISABLE_RELAY;
+#endif
+ allocator_session_->set_flags(flags);
+ allocator_session_->SignalPortReady.connect(
+ this, &RawTransportChannel::OnPortReady);
+ allocator_session_->SignalCandidatesReady.connect(
+ this, &RawTransportChannel::OnCandidatesReady);
+
+ // The initial ports will include stun.
+ allocator_session_->GetInitialPorts();
+}
+
+void RawTransportChannel::Reset() {
+ set_readable(false);
+ set_writable(false);
+
+ delete allocator_session_;
+
+ allocator_session_ = NULL;
+ stun_port_ = NULL;
+ relay_port_ = NULL;
+ port_ = NULL;
+ remote_address_ = talk_base::SocketAddress();
+}
+
+void RawTransportChannel::OnChannelMessage(const buzz::XmlElement* msg) {
+ bool valid = raw_transport_->ParseAddress(NULL, msg, &remote_address_);
+ ASSERT(valid);
+ ASSERT(!remote_address_.IsAny());
+ set_readable(true);
+
+ // We can write once we have a port and a remote address.
+ if (port_ != NULL)
+ SetWritable();
+}
+
+// Note about stun classification
+// Code to classify our NAT type and use the relay port if we are behind an
+// asymmetric NAT is under a FEATURE_ENABLE_STUN_CLASSIFICATION #define.
+// To turn this one we will have to enable a second stun address and make sure
+// that the relay server works for raw UDP.
+//
+// Another option is to classify the NAT type early and not offer the raw
+// transport type at all if we can't support it.
+
+void RawTransportChannel::OnPortReady(
+ PortAllocatorSession* session, Port* port) {
+ ASSERT(session == allocator_session_);
+
+ if (port->type() == STUN_PORT_TYPE) {
+ stun_port_ = static_cast<StunPort*>(port);
+
+#if defined(FEATURE_ENABLE_STUN_CLASSIFICATION)
+ // We need a secondary address to determine the NAT type.
+ stun_port_->PrepareSecondaryAddress();
+#endif
+ } else if (port->type() == RELAY_PORT_TYPE) {
+ relay_port_ = static_cast<RelayPort*>(port);
+ } else {
+ ASSERT(false);
+ }
+}
+
+void RawTransportChannel::OnCandidatesReady(
+ PortAllocatorSession *session, const std::vector<Candidate>& candidates) {
+ ASSERT(session == allocator_session_);
+ ASSERT(candidates.size() >= 1);
+
+ // The most recent candidate is the one we haven't seen yet.
+ Candidate c = candidates[candidates.size() - 1];
+
+ if (c.type() == STUN_PORT_TYPE) {
+ ASSERT(stun_port_ != NULL);
+
+#if defined(FEATURE_ENABLE_STUN_CLASSIFICATION)
+ // We need to wait until we have two addresses.
+ if (stun_port_->candidates().size() < 2)
+ return;
+
+ // This is the second address. If these addresses are the same, then we
+ // are not behind a symmetric NAT. Hence, a stun port should be sufficient.
+ if (stun_port_->candidates()[0].address() ==
+ stun_port_->candidates()[1].address()) {
+ SetPort(stun_port_);
+ return;
+ }
+
+ // We will need to use relay.
+ use_relay_ = true;
+
+ // If we weren't given a relay port, we'll need to request it.
+ if (relay_port_ == NULL) {
+ allocator_session_->StartGetAllPorts();
+ return;
+ }
+
+ // If we already have a relay address, we're good. Otherwise, we will need
+ // to wait until one arrives.
+ if (relay_port_->candidates().size() > 0)
+ SetPort(relay_port_);
+#else // defined(FEATURE_ENABLE_STUN_CLASSIFICATION)
+ // Always use the stun port. We don't classify right now so just assume it
+ // will work fine.
+ SetPort(stun_port_);
+#endif
+ } else if (c.type() == RELAY_PORT_TYPE) {
+ if (use_relay_)
+ SetPort(relay_port_);
+ } else {
+ ASSERT(false);
+ }
+}
+
+void RawTransportChannel::SetPort(Port* port) {
+ ASSERT(port_ == NULL);
+ port_ = port;
+
+ // We don't need any ports other than the one we picked.
+ allocator_session_->StopGetAllPorts();
+ raw_transport_->session_manager()->worker_thread()->Post(
+ this, MSG_DESTROY_UNUSED_PORTS, NULL);
+
+ // Send a message to the other client containing our address.
+
+ ASSERT(port_->candidates().size() >= 1);
+ ASSERT(port_->candidates()[0].protocol() == "udp");
+ talk_base::SocketAddress addr = port_->candidates()[0].address();
+
+ buzz::XmlElement* msg = new buzz::XmlElement(kQnRawChannel);
+ msg->SetAttr(buzz::QN_NAME, name());
+ msg->SetAttr(QN_ADDRESS, addr.IPAsString());
+ msg->SetAttr(QN_PORT, addr.PortAsString());
+ SignalChannelMessage(this, msg);
+
+ // Read all packets from this port.
+ port_->EnablePortPackets();
+ port_->SignalReadPacket.connect(this, &RawTransportChannel::OnReadPacket);
+
+ // We can write once we have a port and a remote address.
+ if (!remote_address_.IsAny())
+ SetWritable();
+}
+
+void RawTransportChannel::SetWritable() {
+ ASSERT(port_ != NULL);
+ ASSERT(!remote_address_.IsAny());
+
+ set_writable(true);
+
+ SignalRouteChange(this, remote_address_);
+}
+
+void RawTransportChannel::OnReadPacket(
+ Port* port, const char* data, size_t size,
+ const talk_base::SocketAddress& addr) {
+ ASSERT(port_ == port);
+ SignalReadPacket(this, data, size);
+}
+
+void RawTransportChannel::OnMessage(talk_base::Message* msg) {
+ ASSERT(msg->message_id == MSG_DESTROY_UNUSED_PORTS);
+ ASSERT(port_ != NULL);
+ if (port_ != stun_port_) {
+ stun_port_->Destroy();
+ stun_port_ = NULL;
+ }
+ if (port_ != relay_port_ && relay_port_ != NULL) {
+ relay_port_->Destroy();
+ relay_port_ = NULL;
+ }
+}
+
+} // namespace cricket
diff --git a/Plugins/jingle/libjingle/talk/p2p/base/rawtransportchannel.h b/Plugins/jingle/libjingle/talk/p2p/base/rawtransportchannel.h
new file mode 100644
index 0000000..9dcbae2
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/p2p/base/rawtransportchannel.h
@@ -0,0 +1,117 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _CRICKET_P2P_BASE_RAWTRANSPORTCHANNEL_H_
+#define _CRICKET_P2P_BASE_RAWTRANSPORTCHANNEL_H_
+
+#include "talk/base/messagequeue.h"
+#include "talk/p2p/base/transportchannelimpl.h"
+#include "talk/p2p/base/rawtransport.h"
+#include "talk/p2p/base/candidate.h"
+
+namespace cricket {
+
+class Port;
+class Connection;
+class StunPort;
+class RelayPort;
+class PortAllocator;
+class PortAllocatorSession;
+
+// Implements a channel that just sends bare packets once we have received the
+// address of the other side. We pick a single address to send them based on
+// a simple investigation of NAT type.
+class RawTransportChannel : public TransportChannelImpl,
+ public talk_base::MessageHandler {
+ public:
+ RawTransportChannel(const std::string &name,
+ const std::string &session_type,
+ RawTransport* transport,
+ PortAllocator *allocator);
+ virtual ~RawTransportChannel();
+
+ // Implementation of normal channel packet sending.
+ virtual int SendPacket(const char *data, size_t len);
+ virtual int SetOption(talk_base::Socket::Option opt, int value);
+ virtual int GetError();
+
+ // Returns the raw transport that created this channel.
+ virtual Transport* GetTransport() { return raw_transport_; }
+
+ // Creates an allocator session to start figuring out which type of port we
+ // should send to the other client. This will send SignalChannelMessage once
+ // we have decided.
+ virtual void Connect();
+
+ // Resets state back to unconnected.
+ virtual void Reset();
+
+ // We don't actually worry about signaling since we can't send new candidates.
+ virtual void OnSignalingReady() {}
+
+ // Handles a message setting the remote address. We are writable once we
+ // have this since we now know where to send.
+ virtual void OnChannelMessage(const buzz::XmlElement* msg);
+
+ private:
+ RawTransport* raw_transport_;
+ PortAllocator* allocator_;
+ PortAllocatorSession* allocator_session_;
+ StunPort* stun_port_;
+ RelayPort* relay_port_;
+ Port* port_;
+ bool use_relay_;
+ talk_base::SocketAddress remote_address_;
+
+ // Called when the allocator creates another port.
+ void OnPortReady(PortAllocatorSession* session, Port* port);
+
+ // Called when one of the ports we are using has determined its address.
+ void OnCandidatesReady(PortAllocatorSession *session,
+ const std::vector<Candidate>& candidates);
+
+ // Called once we have chosen the port to use for communication with the
+ // other client. This will send its address and prepare the port for use.
+ void SetPort(Port* port);
+
+ // Called once we have a port and a remote address. This will set mark the
+ // channel as writable and signal the route to the client.
+ void SetWritable();
+
+ // Called when we receive a packet from the other client.
+ void OnReadPacket(Port* port, const char* data, size_t size,
+ const talk_base::SocketAddress& addr);
+
+ // Handles a message to destroy unused ports.
+ virtual void OnMessage(talk_base::Message *msg);
+
+ DISALLOW_EVIL_CONSTRUCTORS(RawTransportChannel);
+};
+
+} // namespace cricket
+
+#endif // _CRICKET_P2P_BASE_RAWTRANSPORTCHANNEL_H_
diff --git a/Plugins/jingle/libjingle/talk/p2p/base/relayport.cc b/Plugins/jingle/libjingle/talk/p2p/base/relayport.cc
new file mode 100644
index 0000000..a8a0527
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/p2p/base/relayport.cc
@@ -0,0 +1,634 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#if defined(_MSC_VER) && _MSC_VER < 1300
+#pragma warning(disable:4786)
+#endif
+#include "talk/base/logging.h"
+#include "talk/base/asynctcpsocket.h"
+#include "talk/base/helpers.h"
+#include "talk/p2p/base/relayport.h"
+#include <iostream>
+#include <cassert>
+#ifdef OSX
+#include <errno.h>
+#endif
+
+#if defined(_MSC_VER) && _MSC_VER < 1300
+namespace std {
+ using ::strerror;
+}
+#endif
+
+#ifdef POSIX
+extern "C" {
+#include <errno.h>
+}
+#endif // POSIX
+
+namespace talk_base {
+class AsyncTCPSocket;
+};
+
+namespace cricket {
+
+const int KEEPALIVE_DELAY = 10 * 60 * 1000;
+const int RETRY_DELAY = 50; // 50ms, from ICE spec
+const uint32 RETRY_TIMEOUT = 50 * 1000; // ICE says 50 secs
+
+// Manages a single connection to the relayserver. We aim to use each
+// connection for only a specific destination address so that we can avoid
+// wrapping every packet in a STUN send / data indication.
+class RelayEntry : public sigslot::has_slots<> {
+public:
+ RelayEntry(RelayPort* port, const talk_base::SocketAddress& ext_addr,
+ const talk_base::SocketAddress& local_addr);
+ ~RelayEntry();
+
+ RelayPort* port() { return port_; }
+
+ const talk_base::SocketAddress& address() const { return ext_addr_; }
+ void set_address(const talk_base::SocketAddress& addr) { ext_addr_ = addr; }
+
+ talk_base::AsyncPacketSocket* socket() { return socket_; }
+
+ bool connected() const { return connected_; }
+ bool locked() const { return locked_; }
+
+ // Returns the last error on the socket of this entry.
+ int GetError() const { return socket_->GetError(); }
+
+ // Sends the STUN requests to the server to initiate this connection.
+ void Connect();
+
+ // Called when this entry becomes connected. The address given is the one
+ // exposed to the outside world on the relay server.
+ void OnConnect(const talk_base::SocketAddress& mapped_addr);
+
+ // Sends a packet to the given destination address using the socket of this
+ // entry. This will wrap the packet in STUN if necessary.
+ int SendTo(const void* data, size_t size,
+ const talk_base::SocketAddress& addr);
+
+ // Schedules a keep-alive allocate request.
+ void ScheduleKeepAlive();
+
+ void SetServerIndex(size_t sindex) { server_index_ = sindex; }
+ size_t ServerIndex() const { return server_index_; }
+
+ // Try a different server address
+ void HandleConnectFailure();
+
+private:
+ RelayPort* port_;
+ talk_base::SocketAddress ext_addr_, local_addr_;
+ size_t server_index_;
+ talk_base::AsyncPacketSocket* socket_;
+ bool connected_;
+ bool locked_;
+ StunRequestManager requests_;
+
+ // Called when a TCP connection is established or fails
+ void OnSocketConnect(talk_base::AsyncTCPSocket* socket);
+ void OnSocketClose(talk_base::AsyncTCPSocket* socket, int error);
+
+ // Called when a packet is received on this socket.
+ void OnReadPacket(
+ const char* data, size_t size,
+ const talk_base::SocketAddress& remote_addr,
+ talk_base::AsyncPacketSocket* socket);
+
+ // Called on behalf of a StunRequest to write data to the socket. This is
+ // already STUN intended for the server, so no wrapping is necessary.
+ void OnSendPacket(const void* data, size_t size, StunRequest* req);
+
+ // Sends the given data on the socket to the server with no wrapping. This
+ // returns the number of bytes written or -1 if an error occurred.
+ int SendPacket(const void* data, size_t size);
+};
+
+// Handles an allocate request for a particular RelayEntry.
+class AllocateRequest : public StunRequest {
+public:
+ AllocateRequest(RelayEntry* entry);
+ virtual ~AllocateRequest() {}
+
+ virtual void Prepare(StunMessage* request);
+
+ virtual int GetNextDelay();
+
+ virtual void OnResponse(StunMessage* response);
+ virtual void OnErrorResponse(StunMessage* response);
+ virtual void OnTimeout();
+
+private:
+ RelayEntry* entry_;
+ uint32 start_time_;
+};
+
+const std::string RELAY_PORT_TYPE("relay");
+
+RelayPort::RelayPort(
+ talk_base::Thread* thread, talk_base::SocketFactory* factory,
+ talk_base::Network* network, const talk_base::SocketAddress& local_addr,
+ const std::string& username, const std::string& password,
+ const std::string& magic_cookie)
+ : Port(thread, RELAY_PORT_TYPE, factory, network), local_addr_(local_addr),
+ ready_(false), magic_cookie_(magic_cookie), error_(0) {
+
+ entries_.push_back(
+ new RelayEntry(this, talk_base::SocketAddress(), local_addr_));
+
+ set_username_fragment(username);
+ set_password(password);
+
+ if (magic_cookie_.size() == 0)
+ magic_cookie_.append(STUN_MAGIC_COOKIE_VALUE, 4);
+}
+
+RelayPort::~RelayPort() {
+ for (unsigned i = 0; i < entries_.size(); ++i)
+ delete entries_[i];
+ thread_->Clear(this);
+}
+
+void RelayPort::AddServerAddress(const ProtocolAddress& addr) {
+ // Since HTTP proxies usually only allow 443, let's up the priority on PROTO_SSLTCP
+ if ((addr.proto == PROTO_SSLTCP)
+ && ((proxy().type == talk_base::PROXY_HTTPS)
+ || (proxy().type == talk_base::PROXY_UNKNOWN))) {
+ server_addr_.push_front(addr);
+ } else {
+ server_addr_.push_back(addr);
+ }
+}
+
+void RelayPort::AddExternalAddress(const ProtocolAddress& addr) {
+ std::string proto_name = ProtoToString(addr.proto);
+ for (std::vector<Candidate>::const_iterator it = candidates().begin(); it != candidates().end(); ++it) {
+ if ((it->address() == addr.address) && (it->protocol() == proto_name)) {
+ LOG(INFO) << "Redundant relay address: " << proto_name << " @ " << addr.address.ToString();
+ return;
+ }
+ }
+ AddAddress(addr.address, proto_name, false);
+}
+
+void RelayPort::SetReady() {
+ if (!ready_) {
+ ready_ = true;
+ SignalAddressReady(this);
+ }
+}
+
+const ProtocolAddress * RelayPort::ServerAddress(size_t index) const {
+ if ((index >= 0) && (index < server_addr_.size()))
+ return &server_addr_[index];
+ return 0;
+}
+
+bool RelayPort::HasMagicCookie(const char* data, size_t size) {
+ if (size < 24 + magic_cookie_.size()) {
+ return false;
+ } else {
+ return 0 == std::memcmp(data + 24,
+ magic_cookie_.c_str(),
+ magic_cookie_.size());
+ }
+}
+
+void RelayPort::PrepareAddress() {
+ // We initiate a connect on the first entry. If this completes, it will fill
+ // in the server address as the address of this port.
+ assert(entries_.size() == 1);
+ entries_[0]->Connect();
+ ready_ = false;
+}
+
+Connection* RelayPort::CreateConnection(const Candidate& address, CandidateOrigin origin) {
+ // We only create connections to non-udp sockets if they are incoming on this port
+ if ((address.protocol() != "udp") && (origin != ORIGIN_THIS_PORT))
+ return 0;
+
+ // We don't support loopback on relays
+ if (address.type() == type())
+ return 0;
+
+ size_t index = 0;
+ for (size_t i = 0; i < candidates().size(); ++i) {
+ const Candidate& local = candidates()[i];
+ if (local.protocol() == address.protocol()) {
+ index = i;
+ break;
+ }
+ }
+
+ Connection * conn = new ProxyConnection(this, index, address);
+ AddConnection(conn);
+ return conn;
+}
+
+int RelayPort::SendTo(const void* data, size_t size,
+ const talk_base::SocketAddress& addr, bool payload) {
+
+ // Try to find an entry for this specific address. Note that the first entry
+ // created was not given an address initially, so it can be set to the first
+ // address that comes along.
+
+ RelayEntry* entry = 0;
+
+ for (unsigned i = 0; i < entries_.size(); ++i) {
+ if (entries_[i]->address().IsAny() && payload) {
+ entry = entries_[i];
+ entry->set_address(addr);
+ break;
+ } else if (entries_[i]->address() == addr) {
+ entry = entries_[i];
+ break;
+ }
+ }
+
+ // If we did not find one, then we make a new one. This will not be useable
+ // until it becomes connected, however.
+ if (!entry && payload) {
+ entry = new RelayEntry(this, addr, local_addr_);
+ if (!entries_.empty()) {
+ // Use the same port to connect to relay server
+ entry->SetServerIndex(entries_[0]->ServerIndex());
+ }
+ entry->Connect();
+ entries_.push_back(entry);
+ }
+
+ // If the entry is connected, then we can send on it (though wrapping may
+ // still be necessary). Otherwise, we can't yet use this connection, so we
+ // default to the first one.
+ if (!entry || !entry->connected()) {
+ assert(!entries_.empty());
+ entry = entries_[0];
+ if (!entry->connected()) {
+ error_ = EWOULDBLOCK;
+ return SOCKET_ERROR;
+ }
+ }
+
+ // Send the actual contents to the server using the usual mechanism.
+ int sent = entry->SendTo(data, size, addr);
+ if (sent <= 0) {
+ assert(sent < 0);
+ error_ = entry->GetError();
+ return SOCKET_ERROR;
+ }
+
+ // The caller of the function is expecting the number of user data bytes,
+ // rather than the size of the packet.
+ return (int)size;
+}
+
+int RelayPort::SetOption(talk_base::Socket::Option opt, int value) {
+ int result = 0;
+ for (unsigned i = 0; i < entries_.size(); ++i) {
+ if (entries_[i]->socket()->SetOption(opt, value) < 0) {
+ result = -1;
+ error_ = entries_[i]->socket()->GetError();
+ }
+ }
+ options_.push_back(OptionValue(opt, value));
+ return result;
+}
+
+int RelayPort::GetError() {
+ return error_;
+}
+
+void RelayPort::OnReadPacket(
+ const char* data, size_t size,
+ const talk_base::SocketAddress& remote_addr) {
+ if (Connection* conn = GetConnection(remote_addr)) {
+ conn->OnReadPacket(data, size);
+ } else {
+ Port::OnReadPacket(data, size, remote_addr);
+ }
+}
+
+void RelayPort::DisposeSocket(talk_base::AsyncPacketSocket * socket) {
+ thread_->Dispose(socket);
+}
+
+RelayEntry::RelayEntry(RelayPort* port,
+ const talk_base::SocketAddress& ext_addr,
+ const talk_base::SocketAddress& local_addr)
+ : port_(port), ext_addr_(ext_addr), local_addr_(local_addr), server_index_(0),
+ socket_(0), connected_(false), locked_(false), requests_(port->thread()) {
+
+ requests_.SignalSendPacket.connect(this, &RelayEntry::OnSendPacket);
+}
+
+RelayEntry::~RelayEntry() {
+ delete socket_;
+}
+
+void RelayEntry::Connect() {
+ assert(socket_ == 0);
+ const ProtocolAddress * ra = port()->ServerAddress(server_index_);
+ if (!ra) {
+ LOG(INFO) << "Out of relay server connections";
+ return;
+ }
+
+ LOG(INFO) << "Connecting to relay via " << ProtoToString(ra->proto) << " @ " << ra->address.ToString();
+
+ socket_ = port_->CreatePacketSocket(ra->proto);
+ assert(socket_ != 0);
+
+ socket_->SignalReadPacket.connect(this, &RelayEntry::OnReadPacket);
+ if (socket_->Bind(local_addr_) < 0)
+ LOG(INFO) << "bind: " << std::strerror(socket_->GetError());
+
+ for (unsigned i = 0; i < port_->options().size(); ++i)
+ socket_->SetOption(port_->options()[i].first, port_->options()[i].second);
+
+ if ((ra->proto == PROTO_TCP) || (ra->proto == PROTO_SSLTCP)) {
+ talk_base::AsyncTCPSocket * tcp
+ = static_cast<talk_base::AsyncTCPSocket *>(socket_);
+ tcp->SignalClose.connect(this, &RelayEntry::OnSocketClose);
+ tcp->SignalConnect.connect(this, &RelayEntry::OnSocketConnect);
+ tcp->Connect(ra->address);
+ } else {
+ requests_.Send(new AllocateRequest(this));
+ }
+}
+
+void RelayEntry::OnConnect(const talk_base::SocketAddress& mapped_addr) {
+ ProtocolType proto = PROTO_UDP;
+ LOG(INFO) << "Relay allocate succeeded: " << ProtoToString(proto) << " @ " << mapped_addr.ToString();
+ connected_ = true;
+
+ port_->AddExternalAddress(ProtocolAddress(mapped_addr, proto));
+ port_->SetReady();
+}
+
+int RelayEntry::SendTo(const void* data, size_t size,
+ const talk_base::SocketAddress& addr) {
+
+ // If this connection is locked to the address given, then we can send the
+ // packet with no wrapper.
+ if (locked_ && (ext_addr_ == addr))
+ return SendPacket(data, size);
+
+ // Otherwise, we must wrap the given data in a STUN SEND request so that we
+ // can communicate the destination address to the server.
+ //
+ // Note that we do not use a StunRequest here. This is because there is
+ // likely no reason to resend this packet. If it is late, we just drop it.
+ // The next send to this address will try again.
+
+ StunMessage request;
+ request.SetType(STUN_SEND_REQUEST);
+ request.SetTransactionID(CreateRandomString(16));
+
+ StunByteStringAttribute* magic_cookie_attr =
+ StunAttribute::CreateByteString(STUN_ATTR_MAGIC_COOKIE);
+ magic_cookie_attr->CopyBytes(port_->magic_cookie().c_str(),
+ (uint16)port_->magic_cookie().size());
+ request.AddAttribute(magic_cookie_attr);
+
+ StunByteStringAttribute* username_attr =
+ StunAttribute::CreateByteString(STUN_ATTR_USERNAME);
+ username_attr->CopyBytes(port_->username_fragment().c_str(),
+ (uint16)port_->username_fragment().size());
+ request.AddAttribute(username_attr);
+
+ StunAddressAttribute* addr_attr =
+ StunAttribute::CreateAddress(STUN_ATTR_DESTINATION_ADDRESS);
+ addr_attr->SetFamily(1);
+ addr_attr->SetIP(addr.ip());
+ addr_attr->SetPort(addr.port());
+ request.AddAttribute(addr_attr);
+
+ // Attempt to lock
+ if (ext_addr_ == addr) {
+ StunUInt32Attribute* options_attr =
+ StunAttribute::CreateUInt32(STUN_ATTR_OPTIONS);
+ options_attr->SetValue(0x1);
+ request.AddAttribute(options_attr);
+ }
+
+ StunByteStringAttribute* data_attr =
+ StunAttribute::CreateByteString(STUN_ATTR_DATA);
+ data_attr->CopyBytes(data, (uint16)size);
+ request.AddAttribute(data_attr);
+
+ // TODO: compute the HMAC.
+
+ talk_base::ByteBuffer buf;
+ request.Write(&buf);
+
+ return SendPacket(buf.Data(), buf.Length());
+}
+
+void RelayEntry::ScheduleKeepAlive() {
+ requests_.SendDelayed(new AllocateRequest(this), KEEPALIVE_DELAY);
+}
+
+void RelayEntry::HandleConnectFailure() {
+ //if (GetMillisecondCount() - start_time_ > RETRY_TIMEOUT)
+ // return;
+ //ScheduleKeepAlive();
+
+ connected_ = false;
+ port()->DisposeSocket(socket_);
+ socket_ = 0;
+ requests_.Clear();
+
+ server_index_ += 1;
+ Connect();
+}
+
+void RelayEntry::OnSocketConnect(talk_base::AsyncTCPSocket* socket) {
+ assert(socket == socket_);
+ LOG(INFO) << "relay tcp connected to " << socket->GetRemoteAddress().ToString();
+ requests_.Send(new AllocateRequest(this));
+}
+
+void RelayEntry::OnSocketClose(talk_base::AsyncTCPSocket* socket, int error) {
+ assert(socket == socket_);
+ PLOG(LERROR, error) << "relay tcp connect failed";
+ HandleConnectFailure();
+}
+
+void RelayEntry::OnReadPacket(const char* data, size_t size,
+ const talk_base::SocketAddress& remote_addr,
+ talk_base::AsyncPacketSocket* socket) {
+ assert(socket == socket_);
+ //assert(remote_addr == port_->server_addr()); TODO: are we worried about this?
+
+ // If the magic cookie is not present, then this is an unwrapped packet sent
+ // by the server, The actual remote address is the one we recorded.
+ if (!port_->HasMagicCookie(data, size)) {
+ if (locked_) {
+ port_->OnReadPacket(data, size, ext_addr_);
+ } else {
+ LOG(WARNING) << "Dropping packet: entry not locked";
+ }
+ return;
+ }
+
+ talk_base::ByteBuffer buf(data, size);
+ StunMessage msg;
+ if (!msg.Read(&buf)) {
+ LOG(INFO) << "Incoming packet was not STUN";
+ return;
+ }
+
+ // The incoming packet should be a STUN ALLOCATE response, SEND response, or
+ // DATA indication.
+ if (requests_.CheckResponse(&msg)) {
+ return;
+ } else if (msg.type() == STUN_SEND_RESPONSE) {
+ if (const StunUInt32Attribute* options_attr = msg.GetUInt32(STUN_ATTR_OPTIONS)) {
+ if (options_attr->value() & 0x1) {
+ locked_ = true;
+ }
+ }
+ return;
+ } else if (msg.type() != STUN_DATA_INDICATION) {
+ LOG(INFO) << "Received BAD stun type from server: " << msg.type()
+ ;
+ return;
+ }
+
+ // This must be a data indication.
+
+ const StunAddressAttribute* addr_attr =
+ msg.GetAddress(STUN_ATTR_SOURCE_ADDRESS2);
+ if (!addr_attr) {
+ LOG(INFO) << "Data indication has no source address";
+ return;
+ } else if (addr_attr->family() != 1) {
+ LOG(INFO) << "Source address has bad family";
+ return;
+ }
+
+ talk_base::SocketAddress remote_addr2(addr_attr->ip(), addr_attr->port());
+
+ const StunByteStringAttribute* data_attr = msg.GetByteString(STUN_ATTR_DATA);
+ if (!data_attr) {
+ LOG(INFO) << "Data indication has no data";
+ return;
+ }
+
+ // Process the actual data and remote address in the normal manner.
+ port_->OnReadPacket(data_attr->bytes(), data_attr->length(), remote_addr2);
+}
+
+void RelayEntry::OnSendPacket(const void* data, size_t size, StunRequest* req) {
+ SendPacket(data, size);
+}
+
+int RelayEntry::SendPacket(const void* data, size_t size) {
+ const ProtocolAddress * ra = port_->ServerAddress(server_index_);
+ if (!ra) {
+ if (socket_)
+ socket_->SetError(ENOTCONN);
+ return SOCKET_ERROR;
+ }
+ int sent = socket_->SendTo(data, size, ra->address);
+ if (sent <= 0) {
+ LOG(LS_VERBOSE) << "sendto: " << std::strerror(socket_->GetError());
+ assert(sent < 0);
+ }
+ return sent;
+}
+
+AllocateRequest::AllocateRequest(RelayEntry* entry) : entry_(entry) {
+ start_time_ = talk_base::GetMillisecondCount();
+}
+
+void AllocateRequest::Prepare(StunMessage* request) {
+ request->SetType(STUN_ALLOCATE_REQUEST);
+
+ StunByteStringAttribute* magic_cookie_attr =
+ StunAttribute::CreateByteString(STUN_ATTR_MAGIC_COOKIE);
+ magic_cookie_attr->CopyBytes(
+ entry_->port()->magic_cookie().c_str(),
+ (uint16)entry_->port()->magic_cookie().size());
+ request->AddAttribute(magic_cookie_attr);
+
+ StunByteStringAttribute* username_attr =
+ StunAttribute::CreateByteString(STUN_ATTR_USERNAME);
+ username_attr->CopyBytes(
+ entry_->port()->username_fragment().c_str(),
+ (uint16)entry_->port()->username_fragment().size());
+ request->AddAttribute(username_attr);
+}
+
+int AllocateRequest::GetNextDelay() {
+ int delay = 100 * talk_base::_max(1 << count_, 2);
+ count_ += 1;
+ if (count_ == 5)
+ timeout_ = true;
+ return delay;
+}
+
+void AllocateRequest::OnResponse(StunMessage* response) {
+ const StunAddressAttribute* addr_attr =
+ response->GetAddress(STUN_ATTR_MAPPED_ADDRESS);
+ if (!addr_attr) {
+ LOG(INFO) << "Allocate response missing mapped address.";
+ } else if (addr_attr->family() != 1) {
+ LOG(INFO) << "Mapped address has bad family";
+ } else {
+ talk_base::SocketAddress addr(addr_attr->ip(), addr_attr->port());
+ entry_->OnConnect(addr);
+ }
+
+ // We will do a keep-alive regardless of whether this request suceeds.
+ // This should have almost no impact on network usage.
+ entry_->ScheduleKeepAlive();
+}
+
+void AllocateRequest::OnErrorResponse(StunMessage* response) {
+ const StunErrorCodeAttribute* attr = response->GetErrorCode();
+ if (!attr) {
+ LOG(INFO) << "Bad allocate response error code";
+ } else {
+ LOG(INFO) << "Allocate error response:"
+ << " code=" << static_cast<int>(attr->error_code())
+ << " reason='" << attr->reason() << "'";
+ }
+
+ if (talk_base::GetMillisecondCount() - start_time_ <= RETRY_TIMEOUT)
+ entry_->ScheduleKeepAlive();
+}
+
+void AllocateRequest::OnTimeout() {
+ LOG(INFO) << "Allocate request timed out";
+ entry_->HandleConnectFailure();
+}
+
+} // namespace cricket
diff --git a/Plugins/jingle/libjingle/talk/p2p/base/relayport.h b/Plugins/jingle/libjingle/talk/p2p/base/relayport.h
new file mode 100644
index 0000000..5691a6c
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/p2p/base/relayport.h
@@ -0,0 +1,94 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __RELAYPORT_H__
+#define __RELAYPORT_H__
+
+#include "talk/p2p/base/port.h"
+#include "talk/p2p/base/stunrequest.h"
+#include <vector>
+
+namespace cricket {
+
+extern const std::string RELAY_PORT_TYPE;
+class RelayEntry;
+
+// Communicates using an allocated port on the relay server.
+class RelayPort : public Port {
+public:
+ RelayPort(
+ talk_base::Thread* thread, talk_base::SocketFactory* factory,
+ talk_base::Network*, const talk_base::SocketAddress& local_addr,
+ const std::string& username, const std::string& password,
+ const std::string& magic_cookie);
+ virtual ~RelayPort();
+
+ void AddServerAddress(const ProtocolAddress& addr);
+ void AddExternalAddress(const ProtocolAddress& addr);
+
+ typedef std::pair<talk_base::Socket::Option, int> OptionValue;
+ const std::vector<OptionValue>& options() const { return options_; }
+
+ const std::string& magic_cookie() const { return magic_cookie_; }
+ bool HasMagicCookie(const char* data, size_t size);
+
+ virtual void PrepareAddress();
+ virtual Connection* CreateConnection(const Candidate& address, CandidateOrigin origin);
+
+ virtual int SetOption(talk_base::Socket::Option opt, int value);
+ virtual int GetError();
+
+ const ProtocolAddress * ServerAddress(size_t index) const;
+
+ void DisposeSocket(talk_base::AsyncPacketSocket * socket);
+
+protected:
+ void SetReady();
+
+ virtual int SendTo(const void* data, size_t size,
+ const talk_base::SocketAddress& addr, bool payload);
+
+ // Dispatches the given packet to the port or connection as appropriate.
+ void OnReadPacket(
+ const char* data, size_t size,
+ const talk_base::SocketAddress& remote_addr);
+
+private:
+ friend class RelayEntry;
+
+ talk_base::SocketAddress local_addr_;
+ std::deque<ProtocolAddress> server_addr_;
+ bool ready_;
+ std::vector<RelayEntry*> entries_;
+ std::vector<OptionValue> options_;
+ std::string magic_cookie_;
+ int error_;
+};
+
+} // namespace cricket
+
+#endif // __RELAYPORT_H__
diff --git a/Plugins/jingle/libjingle/talk/p2p/base/relayserver.cc b/Plugins/jingle/libjingle/talk/p2p/base/relayserver.cc
new file mode 100644
index 0000000..1f4a979
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/p2p/base/relayserver.cc
@@ -0,0 +1,671 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "talk/p2p/base/relayserver.h"
+#include "talk/base/helpers.h"
+#include <algorithm>
+#include <cassert>
+#include <cstring>
+#include <iostream>
+
+#ifdef POSIX
+extern "C" {
+#include <errno.h>
+}
+#endif // POSIX
+
+namespace cricket {
+
+// By default, we require a ping every 90 seconds.
+const int MAX_LIFETIME = 15 * 60 * 1000;
+
+// The number of bytes in each of the usernames we use.
+const uint32 USERNAME_LENGTH = 16;
+
+// Calls SendTo on the given socket and logs any bad results.
+void Send(talk_base::AsyncPacketSocket* socket, const char* bytes, size_t size,
+ const talk_base::SocketAddress& addr) {
+ int result = socket->SendTo(bytes, size, addr);
+ if (result < int(size)) {
+ std::cerr << "SendTo wrote only " << result << " of " << int(size)
+ << " bytes" << std::endl;
+ } else if (result < 0) {
+ std::cerr << "SendTo: " << std::strerror(errno) << std::endl;
+ }
+}
+
+// Sends the given STUN message on the given socket.
+void SendStun(const StunMessage& msg,
+ talk_base::AsyncPacketSocket* socket,
+ const talk_base::SocketAddress& addr) {
+ talk_base::ByteBuffer buf;
+ msg.Write(&buf);
+ Send(socket, buf.Data(), buf.Length(), addr);
+}
+
+// Constructs a STUN error response and sends it on the given socket.
+void SendStunError(const StunMessage& msg, talk_base::AsyncPacketSocket* socket,
+ const talk_base::SocketAddress& remote_addr, int error_code,
+ const char* error_desc, const std::string& magic_cookie) {
+
+ StunMessage err_msg;
+ err_msg.SetType(GetStunErrorResponseType(msg.type()));
+ err_msg.SetTransactionID(msg.transaction_id());
+
+ StunByteStringAttribute* magic_cookie_attr =
+ StunAttribute::CreateByteString(cricket::STUN_ATTR_MAGIC_COOKIE);
+ if (magic_cookie.size() == 0)
+ magic_cookie_attr->CopyBytes(cricket::STUN_MAGIC_COOKIE_VALUE, 4);
+ else
+ magic_cookie_attr->CopyBytes(magic_cookie.c_str(), magic_cookie.size());
+ err_msg.AddAttribute(magic_cookie_attr);
+
+ StunErrorCodeAttribute* err_code = StunAttribute::CreateErrorCode();
+ err_code->SetErrorClass(error_code / 100);
+ err_code->SetNumber(error_code % 100);
+ err_code->SetReason(error_desc);
+ err_msg.AddAttribute(err_code);
+
+ SendStun(err_msg, socket, remote_addr);
+}
+
+RelayServer::RelayServer(talk_base::Thread* thread)
+ : thread_(thread), log_bindings_(true) {
+}
+
+RelayServer::~RelayServer() {
+ for (unsigned i = 0; i < internal_sockets_.size(); i++)
+ delete internal_sockets_[i];
+ for (unsigned i = 0; i < external_sockets_.size(); i++)
+ delete external_sockets_[i];
+}
+
+void RelayServer::AddInternalSocket(talk_base::AsyncPacketSocket* socket) {
+ assert(internal_sockets_.end() ==
+ std::find(internal_sockets_.begin(), internal_sockets_.end(), socket));
+ internal_sockets_.push_back(socket);
+ socket->SignalReadPacket.connect(this, &RelayServer::OnInternalPacket);
+}
+
+void RelayServer::RemoveInternalSocket(talk_base::AsyncPacketSocket* socket) {
+ SocketList::iterator iter =
+ std::find(internal_sockets_.begin(), internal_sockets_.end(), socket);
+ assert(iter != internal_sockets_.end());
+ internal_sockets_.erase(iter);
+ socket->SignalReadPacket.disconnect(this);
+}
+
+void RelayServer::AddExternalSocket(talk_base::AsyncPacketSocket* socket) {
+ assert(external_sockets_.end() ==
+ std::find(external_sockets_.begin(), external_sockets_.end(), socket));
+ external_sockets_.push_back(socket);
+ socket->SignalReadPacket.connect(this, &RelayServer::OnExternalPacket);
+}
+
+void RelayServer::RemoveExternalSocket(talk_base::AsyncPacketSocket* socket) {
+ SocketList::iterator iter =
+ std::find(external_sockets_.begin(), external_sockets_.end(), socket);
+ assert(iter != external_sockets_.end());
+ external_sockets_.erase(iter);
+ socket->SignalReadPacket.disconnect(this);
+}
+
+void RelayServer::OnInternalPacket(
+ const char* bytes, size_t size, const talk_base::SocketAddress& remote_addr,
+ talk_base::AsyncPacketSocket* socket) {
+
+ // Get the address of the connection we just received on.
+ talk_base::SocketAddressPair ap(remote_addr, socket->GetLocalAddress());
+ assert(!ap.destination().IsAny());
+
+ // If this did not come from an existing connection, it should be a STUN
+ // allocate request.
+ ConnectionMap::iterator piter = connections_.find(ap);
+ if (piter == connections_.end()) {
+ HandleStunAllocate(bytes, size, ap, socket);
+ return;
+ }
+
+ RelayServerConnection* int_conn = piter->second;
+
+ // Handle STUN requests to the server itself.
+ if (int_conn->binding()->HasMagicCookie(bytes, size)) {
+ HandleStun(int_conn, bytes, size);
+ return;
+ }
+
+ // Otherwise, this is a non-wrapped packet that we are to forward. Make sure
+ // that this connection has been locked. (Otherwise, we would not know what
+ // address to forward to.)
+ if (!int_conn->locked()) {
+ std::cerr << "Dropping packet: connection not locked" << std::endl;
+ return;
+ }
+
+ // Forward this to the destination address into the connection.
+ RelayServerConnection* ext_conn = int_conn->binding()->GetExternalConnection(
+ int_conn->default_destination());
+ if (ext_conn && ext_conn->locked()) {
+ // TODO: Check the HMAC.
+ ext_conn->Send(bytes, size);
+ } else {
+ // This happens very often and is not an error.
+ //std::cerr << "Dropping packet: no external connection" << std::endl;
+ }
+}
+
+void RelayServer::OnExternalPacket(
+ const char* bytes, size_t size, const talk_base::SocketAddress& remote_addr,
+ talk_base::AsyncPacketSocket* socket) {
+
+ // Get the address of the connection we just received on.
+ talk_base::SocketAddressPair ap(remote_addr, socket->GetLocalAddress());
+ assert(!ap.destination().IsAny());
+
+ // If this connection already exists, then forward the traffic.
+ ConnectionMap::iterator piter = connections_.find(ap);
+ if (piter != connections_.end()) {
+ // TODO: Check the HMAC.
+ RelayServerConnection* ext_conn = piter->second;
+ RelayServerConnection* int_conn =
+ ext_conn->binding()->GetInternalConnection(
+ ext_conn->addr_pair().source());
+ assert(int_conn);
+ int_conn->Send(bytes, size, ext_conn->addr_pair().source());
+ ext_conn->Lock(); // allow outgoing packets
+ return;
+ }
+
+ // The first packet should always be a STUN / TURN packet. If it isn't, then
+ // we should just ignore this packet.
+ StunMessage msg;
+ talk_base::ByteBuffer buf = talk_base::ByteBuffer(bytes, size);
+ if (!msg.Read(&buf)) {
+ std::cerr << "Dropping packet: first packet not STUN" << std::endl;
+ return;
+ }
+
+ // The initial packet should have a username (which identifies the binding).
+ const StunByteStringAttribute* username_attr =
+ msg.GetByteString(STUN_ATTR_USERNAME);
+ if (!username_attr) {
+ std::cerr << "Dropping packet: no username" << std::endl;
+ return;
+ }
+
+ uint32 length = talk_base::_min(uint32(username_attr->length()), USERNAME_LENGTH);
+ std::string username(username_attr->bytes(), length);
+ // TODO: Check the HMAC.
+
+ // The binding should already be present.
+ BindingMap::iterator biter = bindings_.find(username);
+ if (biter == bindings_.end()) {
+ // TODO: Turn this back on. This is the sign of a client bug.
+ //std::cerr << "Dropping packet: no binding with username" << std::endl;
+ return;
+ }
+
+ // Add this authenticted connection to the binding.
+ RelayServerConnection* ext_conn =
+ new RelayServerConnection(biter->second, ap, socket);
+ ext_conn->binding()->AddExternalConnection(ext_conn);
+ AddConnection(ext_conn);
+
+ // We always know where external packets should be forwarded, so we can lock
+ // them from the beginning.
+ ext_conn->Lock();
+
+ // Send this message on the appropriate internal connection.
+ RelayServerConnection* int_conn = ext_conn->binding()->GetInternalConnection(
+ ext_conn->addr_pair().source());
+ assert(int_conn);
+ int_conn->Send(bytes, size, ext_conn->addr_pair().source());
+}
+
+bool RelayServer::HandleStun(
+ const char* bytes, size_t size, const talk_base::SocketAddress& remote_addr,
+ talk_base::AsyncPacketSocket* socket, std::string* username, StunMessage* msg) {
+
+ // Parse this into a stun message.
+ talk_base::ByteBuffer buf = talk_base::ByteBuffer(bytes, size);
+ if (!msg->Read(&buf)) {
+ SendStunError(*msg, socket, remote_addr, 400, "Bad Request", "");
+ return false;
+ }
+
+ // The initial packet should have a username (which identifies the binding).
+ const StunByteStringAttribute* username_attr =
+ msg->GetByteString(STUN_ATTR_USERNAME);
+ if (!username_attr) {
+ SendStunError(*msg, socket, remote_addr, 432, "Missing Username", "");
+ return false;
+ }
+
+ // Record the username if requested.
+ if (username)
+ username->append(username_attr->bytes(), username_attr->length());
+
+ // TODO: Check for unknown attributes (<= 0x7fff)
+
+ return true;
+}
+
+void RelayServer::HandleStunAllocate(
+ const char* bytes, size_t size, const talk_base::SocketAddressPair& ap,
+ talk_base::AsyncPacketSocket* socket) {
+
+ // Make sure this is a valid STUN request.
+ StunMessage request;
+ std::string username;
+ if (!HandleStun(bytes, size, ap.source(), socket, &username, &request))
+ return;
+
+ // Make sure this is a an allocate request.
+ if (request.type() != STUN_ALLOCATE_REQUEST) {
+ SendStunError(request,
+ socket,
+ ap.source(),
+ 600,
+ "Operation Not Supported",
+ "");
+ return;
+ }
+
+ // TODO: Check the HMAC.
+
+ // Find or create the binding for this username.
+
+ RelayServerBinding* binding;
+
+ BindingMap::iterator biter = bindings_.find(username);
+ if (biter != bindings_.end()) {
+
+ binding = biter->second;
+
+ } else {
+
+ // NOTE: In the future, bindings will be created by the bot only. This
+ // else-branch will then disappear.
+
+ // Compute the appropriate lifetime for this binding.
+ uint32 lifetime = MAX_LIFETIME;
+ const StunUInt32Attribute* lifetime_attr =
+ request.GetUInt32(STUN_ATTR_LIFETIME);
+ if (lifetime_attr)
+ lifetime = talk_base::_min(lifetime, lifetime_attr->value() * 1000);
+
+ binding = new RelayServerBinding(this, username, "0", lifetime);
+ binding->SignalTimeout.connect(this, &RelayServer::OnTimeout);
+ bindings_[username] = binding;
+
+ if (log_bindings_) {
+ std::cout << "Added new binding: " << bindings_.size() << " total"
+ << std::endl;
+ }
+ }
+
+ // Add this connection to the binding. It starts out unlocked.
+ RelayServerConnection* int_conn =
+ new RelayServerConnection(binding, ap, socket);
+ binding->AddInternalConnection(int_conn);
+ AddConnection(int_conn);
+
+ // Now that we have a connection, this other method takes over.
+ HandleStunAllocate(int_conn, request);
+}
+
+void RelayServer::HandleStun(
+ RelayServerConnection* int_conn, const char* bytes, size_t size) {
+
+ // Make sure this is a valid STUN request.
+ StunMessage request;
+ std::string username;
+ if (!HandleStun(bytes, size, int_conn->addr_pair().source(),
+ int_conn->socket(), &username, &request))
+ return;
+
+ // Make sure the username is the one were were expecting.
+ if (username != int_conn->binding()->username()) {
+ int_conn->SendStunError(request, 430, "Stale Credentials");
+ return;
+ }
+
+ // TODO: Check the HMAC.
+
+ // Send this request to the appropriate handler.
+ if (request.type() == STUN_SEND_REQUEST)
+ HandleStunSend(int_conn, request);
+ else if (request.type() == STUN_ALLOCATE_REQUEST)
+ HandleStunAllocate(int_conn, request);
+ else
+ int_conn->SendStunError(request, 600, "Operation Not Supported");
+}
+
+void RelayServer::HandleStunAllocate(
+ RelayServerConnection* int_conn, const StunMessage& request) {
+
+ // Create a response message that includes an address with which external
+ // clients can communicate.
+
+ StunMessage response;
+ response.SetType(STUN_ALLOCATE_RESPONSE);
+ response.SetTransactionID(request.transaction_id());
+
+ StunByteStringAttribute* magic_cookie_attr =
+ StunAttribute::CreateByteString(cricket::STUN_ATTR_MAGIC_COOKIE);
+ magic_cookie_attr->CopyBytes(int_conn->binding()->magic_cookie().c_str(),
+ int_conn->binding()->magic_cookie().size());
+ response.AddAttribute(magic_cookie_attr);
+
+ size_t index = rand() % external_sockets_.size();
+ talk_base::SocketAddress ext_addr = external_sockets_[index]->GetLocalAddress();
+
+ StunAddressAttribute* addr_attr =
+ StunAttribute::CreateAddress(STUN_ATTR_MAPPED_ADDRESS);
+ addr_attr->SetFamily(1);
+ addr_attr->SetIP(ext_addr.ip());
+ addr_attr->SetPort(ext_addr.port());
+ response.AddAttribute(addr_attr);
+
+ StunUInt32Attribute* res_lifetime_attr =
+ StunAttribute::CreateUInt32(STUN_ATTR_LIFETIME);
+ res_lifetime_attr->SetValue(int_conn->binding()->lifetime() / 1000);
+ response.AddAttribute(res_lifetime_attr);
+
+ // TODO: Support transport-prefs (preallocate RTCP port).
+ // TODO: Support bandwidth restrictions.
+ // TODO: Add message integrity check.
+
+ // Send a response to the caller.
+ int_conn->SendStun(response);
+}
+
+void RelayServer::HandleStunSend(
+ RelayServerConnection* int_conn, const StunMessage& request) {
+
+ const StunAddressAttribute* addr_attr =
+ request.GetAddress(STUN_ATTR_DESTINATION_ADDRESS);
+ if (!addr_attr) {
+ int_conn->SendStunError(request, 400, "Bad Request");
+ return;
+ }
+
+ const StunByteStringAttribute* data_attr =
+ request.GetByteString(STUN_ATTR_DATA);
+ if (!data_attr) {
+ int_conn->SendStunError(request, 400, "Bad Request");
+ return;
+ }
+
+ talk_base::SocketAddress ext_addr(addr_attr->ip(), addr_attr->port());
+ RelayServerConnection* ext_conn =
+ int_conn->binding()->GetExternalConnection(ext_addr);
+ if (!ext_conn) {
+ // Create a new connection to establish the relationship with this binding.
+ assert(external_sockets_.size() == 1);
+ talk_base::AsyncPacketSocket* socket = external_sockets_[0];
+ talk_base::SocketAddressPair ap(ext_addr, socket->GetLocalAddress());
+ ext_conn = new RelayServerConnection(int_conn->binding(), ap, socket);
+ ext_conn->binding()->AddExternalConnection(ext_conn);
+ AddConnection(ext_conn);
+ }
+
+ // If this connection has pinged us, then allow outgoing traffic.
+ if (ext_conn->locked())
+ ext_conn->Send(data_attr->bytes(), data_attr->length());
+
+ const StunUInt32Attribute* options_attr =
+ request.GetUInt32(STUN_ATTR_OPTIONS);
+ if (options_attr && (options_attr->value() & 0x01 != 0)) {
+ int_conn->set_default_destination(ext_addr);
+ int_conn->Lock();
+
+ StunMessage response;
+ response.SetType(STUN_SEND_RESPONSE);
+ response.SetTransactionID(request.transaction_id());
+
+ StunByteStringAttribute* magic_cookie_attr =
+ StunAttribute::CreateByteString(cricket::STUN_ATTR_MAGIC_COOKIE);
+ magic_cookie_attr->CopyBytes(int_conn->binding()->magic_cookie().c_str(),
+ int_conn->binding()->magic_cookie().size());
+ response.AddAttribute(magic_cookie_attr);
+
+ StunUInt32Attribute* options2_attr =
+ StunAttribute::CreateUInt32(cricket::STUN_ATTR_OPTIONS);
+ options2_attr->SetValue(0x01);
+ response.AddAttribute(options2_attr);
+
+ int_conn->SendStun(response);
+ }
+}
+
+void RelayServer::AddConnection(RelayServerConnection* conn) {
+ assert(connections_.find(conn->addr_pair()) == connections_.end());
+ connections_[conn->addr_pair()] = conn;
+}
+
+void RelayServer::RemoveConnection(RelayServerConnection* conn) {
+ ConnectionMap::iterator iter = connections_.find(conn->addr_pair());
+ assert(iter != connections_.end());
+ connections_.erase(iter);
+}
+
+void RelayServer::RemoveBinding(RelayServerBinding* binding) {
+ BindingMap::iterator iter = bindings_.find(binding->username());
+ assert(iter != bindings_.end());
+ bindings_.erase(iter);
+
+ if (log_bindings_) {
+ std::cout << "Removed a binding: " << bindings_.size() << " remaining"
+ << std::endl;
+ }
+}
+
+void RelayServer::OnTimeout(RelayServerBinding* binding) {
+ // This call will result in all of the necessary clean-up.
+ delete binding;
+}
+
+RelayServerConnection::RelayServerConnection(
+ RelayServerBinding* binding, const talk_base::SocketAddressPair& addrs,
+ talk_base::AsyncPacketSocket* socket)
+ : binding_(binding), addr_pair_(addrs), socket_(socket), locked_(false) {
+
+ // The creation of a new connection constitutes a use of the binding.
+ binding_->NoteUsed();
+}
+
+RelayServerConnection::~RelayServerConnection() {
+ // Remove this connection from the server's map (if it exists there).
+ binding_->server()->RemoveConnection(this);
+}
+
+void RelayServerConnection::Send(const char* data, size_t size) {
+ // Note that the binding has been used again.
+ binding_->NoteUsed();
+
+ cricket::Send(socket_, data, size, addr_pair_.source());
+}
+
+void RelayServerConnection::Send(
+ const char* data, size_t size, const talk_base::SocketAddress& from_addr) {
+ // If the from address is known to the client, we don't need to send it.
+ if (locked() && (from_addr == default_dest_)) {
+ Send(data, size);
+ return;
+ }
+
+ // Wrap the given data in a data-indication packet.
+
+ StunMessage msg;
+ msg.SetType(STUN_DATA_INDICATION);
+ msg.SetTransactionID("0000000000000000");
+
+ StunByteStringAttribute* magic_cookie_attr =
+ StunAttribute::CreateByteString(cricket::STUN_ATTR_MAGIC_COOKIE);
+ magic_cookie_attr->CopyBytes(binding_->magic_cookie().c_str(),
+ binding_->magic_cookie().size());
+ msg.AddAttribute(magic_cookie_attr);
+
+ StunAddressAttribute* addr_attr =
+ StunAttribute::CreateAddress(STUN_ATTR_SOURCE_ADDRESS2);
+ addr_attr->SetFamily(1);
+ addr_attr->SetIP(from_addr.ip());
+ addr_attr->SetPort(from_addr.port());
+ msg.AddAttribute(addr_attr);
+
+ StunByteStringAttribute* data_attr =
+ StunAttribute::CreateByteString(STUN_ATTR_DATA);
+ assert(size <= 65536);
+ data_attr->CopyBytes(data, uint16(size));
+ msg.AddAttribute(data_attr);
+
+ SendStun(msg);
+}
+
+void RelayServerConnection::SendStun(const StunMessage& msg) {
+ // Note that the binding has been used again.
+ binding_->NoteUsed();
+
+ cricket::SendStun(msg, socket_, addr_pair_.source());
+}
+
+void RelayServerConnection::SendStunError(
+ const StunMessage& request, int error_code, const char* error_desc) {
+ // An error does not indicate use. If no legitimate use off the binding
+ // occurs, we want it to be cleaned up even if errors are still occuring.
+
+ cricket::SendStunError(
+ request, socket_, addr_pair_.source(), error_code, error_desc,
+ binding_->magic_cookie());
+}
+
+void RelayServerConnection::Lock() {
+ locked_ = true;
+}
+
+void RelayServerConnection::Unlock() {
+ locked_ = false;
+}
+
+// IDs used for posted messages:
+const uint32 MSG_LIFETIME_TIMER = 1;
+
+RelayServerBinding::RelayServerBinding(
+ RelayServer* server, const std::string& username,
+ const std::string& password, uint32 lifetime)
+ : server_(server), username_(username), password_(password),
+ lifetime_(lifetime) {
+
+ // For now, every connection uses the standard magic cookie value.
+ magic_cookie_.append(
+ reinterpret_cast<const char*>(STUN_MAGIC_COOKIE_VALUE), 4);
+
+ // Initialize the last-used time to now.
+ NoteUsed();
+
+ // Set the first timeout check.
+ server_->thread()->PostDelayed(lifetime_, this, MSG_LIFETIME_TIMER);
+}
+
+RelayServerBinding::~RelayServerBinding() {
+ // Clear the outstanding timeout check.
+ server_->thread()->Clear(this);
+
+ // Clean up all of the connections.
+ for (size_t i = 0; i < internal_connections_.size(); ++i)
+ delete internal_connections_[i];
+ for (size_t i = 0; i < external_connections_.size(); ++i)
+ delete external_connections_[i];
+
+ // Remove this binding from the server's map.
+ server_->RemoveBinding(this);
+}
+
+void RelayServerBinding::AddInternalConnection(RelayServerConnection* conn) {
+ internal_connections_.push_back(conn);
+}
+
+void RelayServerBinding::AddExternalConnection(RelayServerConnection* conn) {
+ external_connections_.push_back(conn);
+}
+
+void RelayServerBinding::NoteUsed() {
+ last_used_ = talk_base::Time();
+}
+
+bool RelayServerBinding::HasMagicCookie(const char* bytes, size_t size) const {
+ if (size < 24 + magic_cookie_.size()) {
+ return false;
+ } else {
+ return 0 == std::memcmp(
+ bytes + 24, magic_cookie_.c_str(), magic_cookie_.size());
+ }
+}
+
+RelayServerConnection* RelayServerBinding::GetInternalConnection(
+ const talk_base::SocketAddress& ext_addr) {
+
+ // Look for an internal connection that is locked to this address.
+ for (size_t i = 0; i < internal_connections_.size(); ++i) {
+ if (internal_connections_[i]->locked() &&
+ (ext_addr == internal_connections_[i]->default_destination()))
+ return internal_connections_[i];
+ }
+
+ // If one was not found, we send to the first connection.
+ assert(internal_connections_.size() > 0);
+ return internal_connections_[0];
+}
+
+RelayServerConnection* RelayServerBinding::GetExternalConnection(
+ const talk_base::SocketAddress& ext_addr) {
+ for (size_t i = 0; i < external_connections_.size(); ++i) {
+ if (ext_addr == external_connections_[i]->addr_pair().source())
+ return external_connections_[i];
+ }
+ return 0;
+}
+
+void RelayServerBinding::OnMessage(talk_base::Message *pmsg) {
+ if (pmsg->message_id == MSG_LIFETIME_TIMER) {
+ assert(!pmsg->pdata);
+
+ // If the lifetime timeout has been exceeded, then send a signal.
+ // Otherwise, just keep waiting.
+ if (talk_base::Time() >= last_used_ + lifetime_) {
+ SignalTimeout(this);
+ } else {
+ server_->thread()->PostDelayed(lifetime_, this, MSG_LIFETIME_TIMER);
+ }
+
+ } else {
+ assert(false);
+ }
+}
+
+} // namespace cricket
diff --git a/Plugins/jingle/libjingle/talk/p2p/base/relayserver.h b/Plugins/jingle/libjingle/talk/p2p/base/relayserver.h
new file mode 100644
index 0000000..b99c632
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/p2p/base/relayserver.h
@@ -0,0 +1,215 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __RELAYSERVER_H__
+#define __RELAYSERVER_H__
+
+#include "talk/base/asyncudpsocket.h"
+#include "talk/base/socketaddresspair.h"
+#include "talk/base/thread.h"
+#include "talk/base/time.h"
+#include "talk/p2p/base/stun.h"
+
+#include <string>
+#include <vector>
+#include <map>
+
+namespace cricket {
+
+class RelayServerBinding;
+class RelayServerConnection;
+
+// Relays traffic between connections to the server that are "bound" together.
+// All connections created with the same username/password are bound together.
+class RelayServer : public sigslot::has_slots<> {
+public:
+ // Creates a server, which will use this thread to post messages to itself.
+ RelayServer(talk_base::Thread* thread);
+ ~RelayServer();
+
+ talk_base::Thread* thread() { return thread_; }
+
+ // Indicates whether we will print updates of the number of bindings.
+ bool log_bindings() const { return log_bindings_; }
+ void set_log_bindings(bool log_bindings) { log_bindings_ = log_bindings; }
+
+ // Updates the set of sockets that the server uses to talk to "internal"
+ // clients. These are clients that do the "port allocations".
+ void AddInternalSocket(talk_base::AsyncPacketSocket* socket);
+ void RemoveInternalSocket(talk_base::AsyncPacketSocket* socket);
+
+ // Updates the set of sockets that the server uses to talk to "external"
+ // clients. These are the clients that do not do allocations. They do not
+ // know that these addresses represent a relay server.
+ void AddExternalSocket(talk_base::AsyncPacketSocket* socket);
+ void RemoveExternalSocket(talk_base::AsyncPacketSocket* socket);
+
+private:
+ typedef std::vector<talk_base::AsyncPacketSocket*> SocketList;
+ typedef std::map<std::string,RelayServerBinding*> BindingMap;
+ typedef std::map<talk_base::SocketAddressPair,RelayServerConnection*> ConnectionMap;
+
+ talk_base::Thread* thread_;
+ bool log_bindings_;
+ SocketList internal_sockets_;
+ SocketList external_sockets_;
+ BindingMap bindings_;
+ ConnectionMap connections_;
+
+ // Called when a packet is received by the server on one of its sockets.
+ void OnInternalPacket(
+ const char* bytes, size_t size, const talk_base::SocketAddress& remote_addr,
+ talk_base::AsyncPacketSocket* socket);
+ void OnExternalPacket(
+ const char* bytes, size_t size, const talk_base::SocketAddress& remote_addr,
+ talk_base::AsyncPacketSocket* socket);
+
+ // Processes the relevant STUN request types from the client.
+ bool HandleStun(const char* bytes, size_t size,
+ const talk_base::SocketAddress& remote_addr, talk_base::AsyncPacketSocket* socket,
+ std::string* username, StunMessage* msg);
+ void HandleStunAllocate(const char* bytes, size_t size,
+ const talk_base::SocketAddressPair& ap,
+ talk_base::AsyncPacketSocket* socket);
+ void HandleStun(RelayServerConnection* int_conn, const char* bytes,
+ size_t size);
+ void HandleStunAllocate(RelayServerConnection* int_conn,
+ const StunMessage& msg);
+ void HandleStunSend(RelayServerConnection* int_conn, const StunMessage& msg);
+
+ // Adds/Removes the a connection or binding.
+ void AddConnection(RelayServerConnection* conn);
+ void RemoveConnection(RelayServerConnection* conn);
+ void RemoveBinding(RelayServerBinding* binding);
+
+ // Called when the timer for checking lifetime times out.
+ void OnTimeout(RelayServerBinding* binding);
+
+ friend class RelayServerConnection;
+ friend class RelayServerBinding;
+};
+
+// Maintains information about a connection to the server. Each connection is
+// part of one and only one binding.
+class RelayServerConnection {
+public:
+ RelayServerConnection(RelayServerBinding* binding,
+ const talk_base::SocketAddressPair& addrs,
+ talk_base::AsyncPacketSocket* socket);
+ ~RelayServerConnection();
+
+ RelayServerBinding* binding() { return binding_; }
+ talk_base::AsyncPacketSocket* socket() { return socket_; }
+
+ // Returns a pair where the source is the remote address and the destination
+ // is the local address.
+ const talk_base::SocketAddressPair& addr_pair() { return addr_pair_; }
+
+ // Sends a packet to the connected client. If an address is provided, then
+ // we make sure the internal client receives it, wrapping if necessary.
+ void Send(const char* data, size_t size);
+ void Send(const char* data, size_t size, const talk_base::SocketAddress& ext_addr);
+
+ // Sends a STUN message to the connected client with no wrapping.
+ void SendStun(const StunMessage& msg);
+ void SendStunError(const StunMessage& request, int code, const char* desc);
+
+ // A locked connection is one for which we know the intended destination of
+ // any raw packet received.
+ bool locked() const { return locked_; }
+ void Lock();
+ void Unlock();
+
+ // Records the address that raw packets should be forwarded to (for internal
+ // packets only; for external, we already know where they go).
+ const talk_base::SocketAddress& default_destination() const { return default_dest_; }
+ void set_default_destination(const talk_base::SocketAddress& addr) {
+ default_dest_ = addr;
+ }
+
+private:
+ RelayServerBinding* binding_;
+ talk_base::SocketAddressPair addr_pair_;
+ talk_base::AsyncPacketSocket* socket_;
+ bool locked_;
+ talk_base::SocketAddress default_dest_;
+};
+
+// Records a set of internal and external connections that we relay between,
+// or in other words, that are "bound" together.
+class RelayServerBinding : public talk_base::MessageHandler {
+public:
+ RelayServerBinding(
+ RelayServer* server, const std::string& username,
+ const std::string& password, uint32 lifetime);
+ virtual ~RelayServerBinding();
+
+ RelayServer* server() { return server_; }
+ uint32 lifetime() { return lifetime_; }
+ const std::string& username() { return username_; }
+ const std::string& password() { return password_; }
+ const std::string& magic_cookie() { return magic_cookie_; }
+
+ // Adds/Removes a connection into the binding.
+ void AddInternalConnection(RelayServerConnection* conn);
+ void AddExternalConnection(RelayServerConnection* conn);
+
+ // We keep track of the use of each binding. If we detect that it was not
+ // used for longer than the lifetime, then we send a signal.
+ void NoteUsed();
+ sigslot::signal1<RelayServerBinding*> SignalTimeout;
+
+ // Determines whether the given packet has the magic cookie present (in the
+ // right place).
+ bool HasMagicCookie(const char* bytes, size_t size) const;
+
+ // Determines the connection to use to send packets to or from the given
+ // external address.
+ RelayServerConnection* GetInternalConnection(const talk_base::SocketAddress& ext_addr);
+ RelayServerConnection* GetExternalConnection(const talk_base::SocketAddress& ext_addr);
+
+ // MessageHandler:
+ void OnMessage(talk_base::Message *pmsg);
+
+private:
+ RelayServer* server_;
+
+ std::string username_;
+ std::string password_;
+ std::string magic_cookie_;
+
+ std::vector<RelayServerConnection*> internal_connections_;
+ std::vector<RelayServerConnection*> external_connections_;
+
+ uint32 lifetime_;
+ uint32 last_used_;
+ // TODO: bandwidth
+};
+
+} // namespace cricket
+
+#endif // __RELAYSERVER_H__
diff --git a/Plugins/jingle/libjingle/talk/p2p/base/relayserver_main.cc b/Plugins/jingle/libjingle/talk/p2p/base/relayserver_main.cc
new file mode 100644
index 0000000..cdd5fff
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/p2p/base/relayserver_main.cc
@@ -0,0 +1,75 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <cassert>
+#include <iostream>
+#include "talk/base/host.h"
+#include "talk/base/thread.h"
+#include "talk/p2p/base/relayserver.h"
+
+#ifdef POSIX
+extern "C" {
+#include <errno.h>
+}
+#endif // POSIX
+
+using namespace cricket;
+
+int main(int argc, char **argv) {
+ if (argc != 1) {
+ std::cerr << "usage: relayserver" << std::endl;
+ return 1;
+ }
+
+ assert(talk_base::LocalHost().networks().size() >= 2);
+ talk_base::SocketAddress int_addr(talk_base::LocalHost().networks()[1]->ip(), 5000);
+ talk_base::SocketAddress ext_addr(talk_base::LocalHost().networks()[1]->ip(), 5001);
+
+ talk_base::Thread *pthMain = talk_base::Thread::Current();
+
+ talk_base::AsyncUDPSocket* int_socket = talk_base::CreateAsyncUDPSocket(pthMain->socketserver());
+ if (int_socket->Bind(int_addr) < 0) {
+ std::cerr << "bind: " << std::strerror(errno) << std::endl;
+ return 1;
+ }
+
+ talk_base::AsyncUDPSocket* ext_socket = talk_base::CreateAsyncUDPSocket(pthMain->socketserver());
+ if (ext_socket->Bind(ext_addr) < 0) {
+ std::cerr << "bind: " << std::strerror(errno) << std::endl;
+ return 1;
+ }
+
+ RelayServer server(pthMain);
+ server.AddInternalSocket(int_socket);
+ server.AddExternalSocket(ext_socket);
+
+ std::cout << "Listening internally at " << int_addr.ToString() << std::endl;
+ std::cout << "Listening externally at " << ext_addr.ToString() << std::endl;
+
+ pthMain->Run();
+ return 0;
+}
diff --git a/Plugins/jingle/libjingle/talk/p2p/base/session.cc b/Plugins/jingle/libjingle/talk/p2p/base/session.cc
new file mode 100644
index 0000000..1ea0dc3
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/p2p/base/session.cc
@@ -0,0 +1,1029 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "talk/p2p/base/session.h"
+#include "talk/base/common.h"
+#include "talk/base/logging.h"
+#include "talk/base/helpers.h"
+#include "talk/xmpp/constants.h"
+#include "talk/xmpp/jid.h"
+#include "talk/p2p/base/sessionclient.h"
+#include "talk/p2p/base/transport.h"
+#include "talk/p2p/base/transportchannelproxy.h"
+#include "talk/p2p/base/p2ptransport.h"
+#include "talk/p2p/base/constants.h"
+
+namespace {
+
+const uint32 MSG_TIMEOUT = 1;
+const uint32 MSG_ERROR = 2;
+const uint32 MSG_STATE = 3;
+
+// This will be initialized at run time to hold the list of default transports.
+std::string* gDefaultTransports = NULL;
+size_t gNumDefaultTransports = 0;
+
+} // namespace
+
+namespace cricket {
+
+Session::Session(SessionManager *session_manager, const std::string& name,
+ const SessionID& id, const std::string& session_type,
+ SessionClient* client) {
+ ASSERT(session_manager->signaling_thread()->IsCurrent());
+ ASSERT(client != NULL);
+ session_manager_ = session_manager;
+ name_ = name;
+ id_ = id;
+ session_type_ = session_type;
+ client_ = client;
+ error_ = ERROR_NONE;
+ state_ = STATE_INIT;
+ initiator_ = false;
+ description_ = NULL;
+ remote_description_ = NULL;
+ transport_ = NULL;
+ compatibility_mode_ = false;
+}
+
+Session::~Session() {
+ ASSERT(session_manager_->signaling_thread()->IsCurrent());
+
+ ASSERT(state_ != STATE_DEINIT);
+ state_ = STATE_DEINIT;
+ SignalState(this, state_);
+
+ delete description_;
+ delete remote_description_;
+
+ for (ChannelMap::iterator iter = channels_.begin();
+ iter != channels_.end();
+ ++iter) {
+ iter->second->SignalDestroyed(iter->second);
+ delete iter->second;
+ }
+
+ for (TransportList::iterator iter = potential_transports_.begin();
+ iter != potential_transports_.end();
+ ++iter) {
+ delete *iter;
+ }
+
+ delete transport_;
+}
+
+bool Session::Initiate(const std::string &to,
+ std::vector<buzz::XmlElement*>* extra_xml,
+ const SessionDescription *description) {
+ ASSERT(session_manager_->signaling_thread()->IsCurrent());
+
+ // Only from STATE_INIT
+ if (state_ != STATE_INIT)
+ return false;
+
+ // Setup for signaling.
+ remote_name_ = to;
+ initiator_ = true;
+ description_ = description;
+
+ // Make sure we have transports to negotiate.
+ CreateTransports();
+
+ // Send the initiate message, including the application and transport offers.
+ XmlElements elems;
+ elems.push_back(client_->TranslateSessionDescription(description));
+ for (TransportList::iterator iter = potential_transports_.begin();
+ iter != potential_transports_.end();
+ ++iter) {
+ buzz::XmlElement* elem = (*iter)->CreateTransportOffer();
+ elems.push_back(elem);
+ }
+
+ if (extra_xml != NULL) {
+ std::vector<buzz::XmlElement*>::iterator iter = extra_xml->begin();
+ for (std::vector<buzz::XmlElement*>::iterator iter = extra_xml->begin();
+ iter != extra_xml->end();
+ ++iter) {
+ elems.push_back(new buzz::XmlElement(**iter));
+ }
+ }
+
+ SendSessionMessage("initiate", elems);
+
+ SetState(Session::STATE_SENTINITIATE);
+
+ // We speculatively start attempting connection of the P2P transports.
+ ConnectDefaultTransportChannels(true);
+ return true;
+}
+
+void Session::ConnectDefaultTransportChannels(bool create) {
+ Transport* transport = GetTransport(kNsP2pTransport);
+ if (transport) {
+ for (ChannelMap::iterator iter = channels_.begin();
+ iter != channels_.end();
+ ++iter) {
+ ASSERT(create != transport->HasChannel(iter->first));
+ if (create) {
+ transport->CreateChannel(iter->first, session_type());
+ }
+ }
+ transport->ConnectChannels();
+ }
+}
+
+void Session::CreateDefaultTransportChannel(const std::string& name) {
+ // This method is only relevant when we have created the default transport
+ // but not received a transport-accept.
+ ASSERT(transport_ == NULL);
+ ASSERT(state_ == STATE_SENTINITIATE);
+
+ Transport* p2p_transport = GetTransport(kNsP2pTransport);
+ if (p2p_transport) {
+ ASSERT(!p2p_transport->HasChannel(name));
+ p2p_transport->CreateChannel(name, session_type());
+ }
+}
+
+bool Session::Accept(const SessionDescription *description) {
+ ASSERT(session_manager_->signaling_thread()->IsCurrent());
+
+ // Only if just received initiate
+ if (state_ != STATE_RECEIVEDINITIATE)
+ return false;
+
+ // Setup for signaling.
+ initiator_ = false;
+ description_ = description;
+
+ // If we haven't selected a transport, wait for ChooseTransport to complete
+ if (transport_ == NULL)
+ return true;
+
+ // Send the accept message.
+ XmlElements elems;
+ elems.push_back(client_->TranslateSessionDescription(description_));
+ SendSessionMessage("accept", elems);
+ SetState(Session::STATE_SENTACCEPT);
+
+ return true;
+}
+
+bool Session::Reject() {
+ ASSERT(session_manager_->signaling_thread()->IsCurrent());
+
+ // Reject is sent in response to an initiate or modify, to reject the
+ // request
+ if (state_ != STATE_RECEIVEDINITIATE && state_ != STATE_RECEIVEDMODIFY)
+ return false;
+
+ // Setup for signaling.
+ initiator_ = false;
+
+ // Send the reject message.
+ SendSessionMessage("reject", XmlElements());
+ SetState(STATE_SENTREJECT);
+
+ return true;
+}
+
+bool Session::Redirect(const std::string & target) {
+ ASSERT(session_manager_->signaling_thread()->IsCurrent());
+
+ // Redirect is sent in response to an initiate or modify, to redirect the
+ // request
+ if (state_ != STATE_RECEIVEDINITIATE)
+ return false;
+
+ // Setup for signaling.
+ initiator_ = false;
+
+ // Send a redirect message to the given target. We include an element that
+ // names the redirector (us), which may be useful to the other side.
+
+ buzz::XmlElement* target_elem = new buzz::XmlElement(QN_REDIRECT_TARGET);
+ target_elem->AddAttr(buzz::QN_NAME, target);
+
+ buzz::XmlElement* cookie = new buzz::XmlElement(QN_REDIRECT_COOKIE);
+ buzz::XmlElement* regarding = new buzz::XmlElement(QN_REDIRECT_REGARDING);
+ regarding->AddAttr(buzz::QN_NAME, name_);
+ cookie->AddElement(regarding);
+
+ XmlElements elems;
+ elems.push_back(target_elem);
+ elems.push_back(cookie);
+ SendSessionMessage("redirect", elems);
+
+ // A redirect puts us in the same state as reject. It just sends a different
+ // kind of reject message, if you like.
+ SetState(STATE_SENTREDIRECT);
+
+ return true;
+}
+
+bool Session::Terminate() {
+ ASSERT(session_manager_->signaling_thread()->IsCurrent());
+
+ // Either side can terminate, at any time.
+ switch (state_) {
+ case STATE_SENTTERMINATE:
+ case STATE_RECEIVEDTERMINATE:
+ return false;
+
+ case STATE_SENTREDIRECT:
+ // We must not send terminate if we redirect.
+ break;
+
+ case STATE_SENTREJECT:
+ case STATE_RECEIVEDREJECT:
+ // We don't need to send terminate if we sent or received a reject...
+ // it's implicit.
+ break;
+
+ default:
+ SendSessionMessage("terminate", XmlElements());
+ break;
+ }
+
+ SetState(STATE_SENTTERMINATE);
+ return true;
+}
+
+void Session::SendInfoMessage(const XmlElements& elems) {
+ ASSERT(session_manager_->signaling_thread()->IsCurrent());
+ SendSessionMessage("info", elems);
+}
+
+void Session::SetPotentialTransports(const std::string names[], size_t length) {
+ ASSERT(session_manager_->signaling_thread()->IsCurrent());
+ for (size_t i = 0; i < length; ++i) {
+ Transport* transport = NULL;
+ if (names[i] == kNsP2pTransport) {
+ transport = new P2PTransport(session_manager_);
+ } else {
+ ASSERT(false);
+ }
+
+ if (transport) {
+ ASSERT(transport->name() == names[i]);
+ potential_transports_.push_back(transport);
+ transport->SignalConnecting.connect(
+ this, &Session::OnTransportConnecting);
+ transport->SignalWritableState.connect(
+ this, &Session::OnTransportWritable);
+ transport->SignalRequestSignaling.connect(
+ this, &Session::OnTransportRequestSignaling);
+ transport->SignalTransportMessage.connect(
+ this, &Session::OnTransportSendMessage);
+ transport->SignalTransportError.connect(
+ this, &Session::OnTransportSendError);
+ transport->SignalChannelGone.connect(
+ this, &Session::OnTransportChannelGone);
+ }
+ }
+}
+
+Transport* Session::GetTransport(const std::string& name) {
+ if (transport_ != NULL) {
+ if (name == transport_->name())
+ return transport_;
+ } else {
+ for (TransportList::iterator iter = potential_transports_.begin();
+ iter != potential_transports_.end();
+ ++iter) {
+ if (name == (*iter)->name())
+ return *iter;
+ }
+ }
+ return NULL;
+}
+
+TransportChannel* Session::CreateChannel(const std::string& name) {
+ //ASSERT(session_manager_->signaling_thread()->IsCurrent());
+ ASSERT(channels_.find(name) == channels_.end());
+ TransportChannelProxy* channel = new TransportChannelProxy(name, session_type_);
+ channels_[name] = channel;
+ if (transport_) {
+ ASSERT(!transport_->HasChannel(name));
+ channel->SetImplementation(transport_->CreateChannel(name, session_type_));
+ } else if (state_ == STATE_SENTINITIATE) {
+ // In this case, we have already speculatively created the default
+ // transport. We should create this channel as well so that it may begin
+ // early connection.
+ CreateDefaultTransportChannel(name);
+ }
+ return channel;
+}
+
+TransportChannel* Session::GetChannel(const std::string& name) {
+ ChannelMap::iterator iter = channels_.find(name);
+ return (iter != channels_.end()) ? iter->second : NULL;
+}
+
+void Session::DestroyChannel(TransportChannel* channel) {
+ ChannelMap::iterator iter = channels_.find(channel->name());
+ ASSERT(iter != channels_.end());
+ ASSERT(channel == iter->second);
+ channels_.erase(iter);
+ channel->SignalDestroyed(channel);
+ delete channel;
+}
+
+TransportChannelImpl* Session::GetImplementation(TransportChannel* channel) {
+ ChannelMap::iterator iter = channels_.find(channel->name());
+ return (iter != channels_.end()) ? iter->second->impl() : NULL;
+}
+
+void Session::CreateTransports() {
+ ASSERT(session_manager_->signaling_thread()->IsCurrent());
+ ASSERT((state_ == STATE_INIT)
+ || (state_ == STATE_RECEIVEDINITIATE));
+ if (potential_transports_.empty()) {
+ if (gDefaultTransports == NULL) {
+ gNumDefaultTransports = 1;
+ gDefaultTransports = new std::string[1];
+ gDefaultTransports[0] = kNsP2pTransport;
+ }
+ SetPotentialTransports(gDefaultTransports, gNumDefaultTransports);
+ }
+}
+
+bool Session::ChooseTransport(const buzz::XmlElement* stanza) {
+ ASSERT(session_manager_->signaling_thread()->IsCurrent());
+ ASSERT(state_ == STATE_RECEIVEDINITIATE);
+ ASSERT(transport_ == NULL);
+
+ // Make sure we have decided on our own transports.
+ CreateTransports();
+
+ // Retrieve the session message.
+ const buzz::XmlElement* session = stanza->FirstNamed(QN_SESSION);
+ ASSERT(session != NULL);
+
+ // Try the offered transports until we find one that we support.
+ bool found_offer = false;
+ const buzz::XmlElement* elem = session->FirstElement();
+ while (elem) {
+ if (elem->Name().LocalPart() == "transport") {
+ found_offer = true;
+ Transport* transport = GetTransport(elem->Name().Namespace());
+ if (transport && transport->OnTransportOffer(elem)) {
+ SetTransport(transport);
+ break;
+ }
+ }
+ elem = elem->NextElement();
+ }
+
+ // If the offer did not include any transports, then we are talking to an
+ // old client. In that case, we turn on compatibility mode, and we assume
+ // an offer containing just P2P, which is all that old clients support.
+ if (!found_offer) {
+ compatibility_mode_ = true;
+
+ Transport* transport = GetTransport(kNsP2pTransport);
+ ASSERT(transport != NULL);
+
+ scoped_ptr<buzz::XmlElement> transport_offer(
+ new buzz::XmlElement(kQnP2pTransport, true));
+ bool valid = transport->OnTransportOffer(transport_offer.get());
+ ASSERT(valid);
+ if (valid)
+ SetTransport(transport);
+ }
+
+ if (!transport_) {
+ SignalErrorMessage(this, stanza, buzz::QN_STANZA_NOT_ACCEPTABLE, "modify",
+ "no supported transport in offer", NULL);
+ return false;
+ }
+
+ // Get the description of the transport we picked.
+ buzz::XmlElement* answer = transport_->CreateTransportAnswer();
+ ASSERT(answer->Name() == buzz::QName(transport_->name(), "transport"));
+
+ // Send a transport-accept message telling the other side our decision,
+ // unless this is an old client that is not expecting one.
+ if (!compatibility_mode_) {
+ XmlElements elems;
+ elems.push_back(answer);
+ SendSessionMessage("transport-accept", elems);
+ }
+
+ // If the user wants to accept, allow that now
+ if (description_) {
+ Accept(description_);
+ }
+
+ return true;
+}
+
+void Session::SetTransport(Transport* transport) {
+ ASSERT(session_manager_->signaling_thread()->IsCurrent());
+ ASSERT(transport_ == NULL);
+ transport_ = transport;
+
+ // Drop the transports that were not selected.
+ bool found = false;
+ for (TransportList::iterator iter = potential_transports_.begin();
+ iter != potential_transports_.end();
+ ++iter) {
+ if (*iter == transport_) {
+ found = true;
+ } else {
+ delete *iter;
+ }
+ }
+ potential_transports_.clear();
+
+ // We require the selected transport to be one of the potential transports
+ ASSERT(found);
+
+ // Create implementations for all of the channels if they don't exist.
+ for (ChannelMap::iterator iter = channels_.begin();
+ iter != channels_.end();
+ ++iter) {
+ TransportChannelProxy* channel = iter->second;
+ TransportChannelImpl* impl = transport_->GetChannel(channel->name());
+ if (impl == NULL)
+ impl = transport_->CreateChannel(channel->name(), session_type());
+ ASSERT(impl != NULL);
+ channel->SetImplementation(impl);
+ }
+
+ // Have this transport start connecting if it is not already.
+ // (We speculatively connect the most common transport right away.)
+ transport_->ConnectChannels();
+}
+
+void Session::SetState(State state) {
+ ASSERT(session_manager_->signaling_thread()->IsCurrent());
+ if (state != state_) {
+ state_ = state;
+ SignalState(this, state_);
+ session_manager_->signaling_thread()->Post(this, MSG_STATE);
+ }
+}
+
+void Session::SetError(Error error) {
+ ASSERT(session_manager_->signaling_thread()->IsCurrent());
+ if (error != error_) {
+ error_ = error;
+ SignalError(this, error);
+ if (error_ != ERROR_NONE)
+ session_manager_->signaling_thread()->Post(this, MSG_ERROR);
+ }
+}
+
+void Session::OnSignalingReady() {
+ ASSERT(session_manager_->signaling_thread()->IsCurrent());
+
+ // Forward this to every transport. Those that did not request it should
+ // ignore this call.
+ if (transport_ != NULL) {
+ transport_->OnSignalingReady();
+ } else {
+ for (TransportList::iterator iter = potential_transports_.begin();
+ iter != potential_transports_.end();
+ ++iter) {
+ (*iter)->OnSignalingReady();
+ }
+ }
+}
+
+void Session::OnTransportConnecting(Transport* transport) {
+ // This is an indication that we should begin watching the writability
+ // state of the transport.
+ OnTransportWritable(transport);
+}
+
+void Session::OnTransportWritable(Transport* transport) {
+ ASSERT(session_manager_->signaling_thread()->IsCurrent());
+ ASSERT((NULL == transport_) || (transport == transport_));
+
+ // If the transport is not writable, start a timer to make sure that it
+ // becomes writable within a reasonable amount of time. If it does not, we
+ // terminate since we can't actually send data. If the transport is writable,
+ // cancel the timer. Note that writability transitions may occur repeatedly
+ // during the lifetime of the session.
+
+ session_manager_->signaling_thread()->Clear(this, MSG_TIMEOUT);
+ if (transport->HasChannels() && !transport->writable()) {
+ session_manager_->signaling_thread()->PostDelayed(
+ session_manager_->session_timeout() * 1000, this, MSG_TIMEOUT);
+ }
+}
+
+void Session::OnTransportRequestSignaling(Transport* transport) {
+ ASSERT(session_manager_->signaling_thread()->IsCurrent());
+ SignalRequestSignaling(this);
+}
+
+void Session::OnTransportSendMessage(Transport* transport,
+ const XmlElements& elems) {
+ ASSERT(session_manager_->signaling_thread()->IsCurrent());
+ for (size_t i = 0; i < elems.size(); ++i)
+ ASSERT(elems[i]->Name() == buzz::QName(transport->name(), "transport"));
+
+ if (compatibility_mode_) {
+ // In backward compatibility mode, we send a candidates message.
+ XmlElements candidates;
+ for (size_t i = 0; i < elems.size(); ++i) {
+ for (const buzz::XmlElement* elem = elems[i]->FirstElement();
+ elem != NULL;
+ elem = elem->NextElement()) {
+ ASSERT(elem->Name() == kQnP2pCandidate);
+
+ // Convert this candidate to an old style candidate (namespace change)
+ buzz::XmlElement* legacy_candidate = new buzz::XmlElement(*elem);
+ legacy_candidate->SetName(kQnLegacyCandidate);
+ candidates.push_back(legacy_candidate);
+ }
+ delete elems[i];
+ }
+
+ SendSessionMessage("candidates", candidates);
+ } else {
+ // If we haven't finished negotiation, then we may later discover that we
+ // need compatibility mode, in which case, we will need to re-send these.
+ if ((transport_ == NULL) && (transport->name() == kNsP2pTransport)) {
+ for (size_t i = 0; i < elems.size(); ++i)
+ candidates_.push_back(new buzz::XmlElement(*elems[i]));
+ }
+
+ SendSessionMessage("transport-info", elems);
+ }
+}
+
+void Session::OnTransportSendError(Transport* transport,
+ const buzz::XmlElement* stanza,
+ const buzz::QName& name,
+ const std::string& type,
+ const std::string& text,
+ const buzz::XmlElement* extra_info) {
+ ASSERT(session_manager_->signaling_thread()->IsCurrent());
+ SignalErrorMessage(this, stanza, name, type, text, extra_info);
+}
+
+void Session::OnTransportChannelGone(Transport* transport,
+ const std::string& name) {
+ ASSERT(session_manager_->signaling_thread()->IsCurrent());
+ SignalChannelGone(this, name);
+}
+
+void Session::SendSessionMessage(
+ const std::string& type, const std::vector<buzz::XmlElement*>& elems) {
+ scoped_ptr<buzz::XmlElement> iq(new buzz::XmlElement(buzz::QN_IQ));
+ iq->SetAttr(buzz::QN_TO, remote_name_);
+ iq->SetAttr(buzz::QN_TYPE, buzz::STR_SET);
+
+ buzz::XmlElement *session = new buzz::XmlElement(QN_SESSION, true);
+ session->AddAttr(buzz::QN_TYPE, type);
+ session->AddAttr(buzz::QN_ID, id_.id_str());
+ session->AddAttr(QN_INITIATOR, id_.initiator());
+
+ for (size_t i = 0; i < elems.size(); ++i)
+ session->AddElement(elems[i]);
+
+ iq->AddElement(session);
+ SignalOutgoingMessage(this, iq.get());
+}
+
+void Session::SendAcknowledgementMessage(const buzz::XmlElement* stanza) {
+ scoped_ptr<buzz::XmlElement> ack(new buzz::XmlElement(buzz::QN_IQ));
+ ack->SetAttr(buzz::QN_TO, remote_name_);
+ ack->SetAttr(buzz::QN_ID, stanza->Attr(buzz::QN_ID));
+ ack->SetAttr(buzz::QN_TYPE, "result");
+
+ SignalOutgoingMessage(this, ack.get());
+}
+
+void Session::OnIncomingMessage(const buzz::XmlElement* stanza) {
+ ASSERT(session_manager_->signaling_thread()->IsCurrent());
+ ASSERT(stanza->Name() == buzz::QN_IQ);
+ buzz::Jid remote(remote_name_);
+ buzz::Jid from(stanza->Attr(buzz::QN_FROM));
+ ASSERT(state_ == STATE_INIT || from == remote);
+
+ const buzz::XmlElement* session = stanza->FirstNamed(QN_SESSION);
+ ASSERT(session != NULL);
+
+ if (stanza->Attr(buzz::QN_TYPE) != buzz::STR_SET) {
+ ASSERT(false);
+ return;
+ }
+
+ ASSERT(session->HasAttr(buzz::QN_TYPE));
+ std::string type = session->Attr(buzz::QN_TYPE);
+
+ bool valid = false;
+
+ if (type == "initiate") {
+ valid = OnInitiateMessage(stanza, session);
+ } else if (type == "accept") {
+ valid = OnAcceptMessage(stanza, session);
+ } else if (type == "reject") {
+ valid = OnRejectMessage(stanza, session);
+ } else if (type == "redirect") {
+ valid = OnRedirectMessage(stanza, session);
+ } else if (type == "info") {
+ valid = OnInfoMessage(stanza, session);
+ } else if (type == "transport-accept") {
+ valid = OnTransportAcceptMessage(stanza, session);
+ } else if (type == "transport-info") {
+ valid = OnTransportInfoMessage(stanza, session);
+ } else if (type == "terminate") {
+ valid = OnTerminateMessage(stanza, session);
+ } else if (type == "candidates") {
+ // This is provided for backward compatibility.
+ // TODO: Remove this once old candidates are gone.
+ valid = OnCandidatesMessage(stanza, session);
+ } else {
+ SignalErrorMessage(this, stanza, buzz::QN_STANZA_BAD_REQUEST, "modify",
+ "unknown session message type", NULL);
+ }
+
+ // If the message was not valid, we should have sent back an error above.
+ // If it was valid, then we send an acknowledgement here.
+ if (valid)
+ SendAcknowledgementMessage(stanza);
+}
+
+void Session::OnFailedSend(const buzz::XmlElement* orig_stanza,
+ const buzz::XmlElement* error_stanza) {
+ ASSERT(session_manager_->signaling_thread()->IsCurrent());
+
+ const buzz::XmlElement* orig_session = orig_stanza->FirstNamed(QN_SESSION);
+ ASSERT(orig_session != NULL);
+
+ std::string error_type = "cancel";
+
+ const buzz::XmlElement* error = error_stanza->FirstNamed(buzz::QN_ERROR);
+ ASSERT(error != NULL);
+ if (error) {
+ ASSERT(error->HasAttr(buzz::QN_TYPE));
+ error_type = error->Attr(buzz::QN_TYPE);
+
+ LOG(LERROR) << "Session error:\n" << error->Str() << "\n"
+ << "in response to:\n" << orig_session->Str();
+ }
+
+ bool fatal_error = false;
+
+ ASSERT(orig_session->HasAttr(buzz::QN_TYPE));
+ if ((orig_session->Attr(buzz::QN_TYPE) == "transport-info")
+ || (orig_session->Attr(buzz::QN_TYPE) == "candidates")) {
+ // Transport messages frequently generate errors because they are sent right
+ // when we detect a network failure. For that reason, we ignore such
+ // errors, because if we do not establish writability again, we will
+ // terminate anyway. The exceptions are transport-specific error tags,
+ // which we pass on to the respective transport.
+ for (const buzz::XmlElement* elem = error->FirstElement();
+ NULL != elem; elem = elem->NextElement()) {
+ if (Transport* transport = GetTransport(elem->Name().Namespace())) {
+ if (!transport->OnTransportError(orig_session, elem)) {
+ fatal_error = true;
+ break;
+ }
+ }
+ }
+ } else if ((error_type != "continue") && (error_type != "wait")) {
+ // We do not set an error if the other side said it is okay to continue
+ // (possibly after waiting). These errors can be ignored.
+ fatal_error = true;
+ }
+
+ if (fatal_error) {
+ SetError(ERROR_RESPONSE);
+ }
+}
+
+bool Session::OnInitiateMessage(const buzz::XmlElement* stanza,
+ const buzz::XmlElement* session) {
+ if (!CheckState(stanza, STATE_INIT))
+ return false;
+ if (!FindRemoteSessionDescription(stanza, session))
+ return false;
+
+ initiator_ = false;
+ remote_name_ = stanza->Attr(buzz::QN_FROM);
+ SetState(STATE_RECEIVEDINITIATE);
+ return true;
+}
+
+bool Session::OnAcceptMessage(const buzz::XmlElement* stanza,
+ const buzz::XmlElement* session) {
+ if (!CheckState(stanza, STATE_SENTINITIATE))
+ return false;
+ if (!FindRemoteSessionDescription(stanza, session))
+ return false;
+
+ SetState(STATE_RECEIVEDACCEPT);
+ return true;
+}
+
+bool Session::OnRejectMessage(const buzz::XmlElement* stanza,
+ const buzz::XmlElement* session) {
+ if (!CheckState(stanza, STATE_SENTINITIATE))
+ return false;
+
+ SetState(STATE_RECEIVEDREJECT);
+ return true;
+}
+
+bool Session::OnRedirectMessage(const buzz::XmlElement* stanza,
+ const buzz::XmlElement* session) {
+ if (!CheckState(stanza, STATE_SENTINITIATE))
+ return false;
+
+ const buzz::XmlElement *redirect_target;
+ if (!FindRequiredElement(stanza, session, QN_REDIRECT_TARGET,
+ &redirect_target))
+ return false;
+
+ if (!FindRequiredAttribute(stanza, redirect_target, buzz::QN_NAME,
+ &remote_name_))
+ return false;
+
+ const buzz::XmlElement* redirect_cookie =
+ session->FirstNamed(QN_REDIRECT_COOKIE);
+
+ XmlElements elems;
+ elems.push_back(client_->TranslateSessionDescription(description_));
+ if (redirect_cookie)
+ elems.push_back(new buzz::XmlElement(*redirect_cookie));
+ SendSessionMessage("initiate", elems);
+
+ // Clear the connection timeout (if any). We will start the connection
+ // timer from scratch when SignalConnecting fires.
+ session_manager_->signaling_thread()->Clear(this, MSG_TIMEOUT);
+
+ // Reset all of the sockets back into the initial state.
+ for (TransportList::iterator iter = potential_transports_.begin();
+ iter != potential_transports_.end();
+ ++iter) {
+ (*iter)->ResetChannels();
+ }
+
+ ConnectDefaultTransportChannels(false);
+ return true;
+}
+
+bool Session::OnInfoMessage(const buzz::XmlElement* stanza,
+ const buzz::XmlElement* session) {
+ XmlElements elems;
+ for (const buzz::XmlElement* elem = session->FirstElement();
+ elem != NULL;
+ elem = elem->NextElement()) {
+ elems.push_back(new buzz::XmlElement(*elem));
+ }
+
+ SignalInfoMessage(this, elems);
+ return true;
+}
+
+bool Session::OnTransportAcceptMessage(const buzz::XmlElement* stanza,
+ const buzz::XmlElement* session) {
+ if (!CheckState(stanza, STATE_SENTINITIATE))
+ return false;
+
+ Transport* transport = NULL;
+ const buzz::XmlElement* transport_elem = NULL;
+
+ for(const buzz::XmlElement* elem = session->FirstElement();
+ elem != NULL;
+ elem = elem->NextElement()) {
+ if (elem->Name().LocalPart() == "transport") {
+ Transport* transport = GetTransport(elem->Name().Namespace());
+ if (transport) {
+ if (transport_elem) { // trying to accept two transport?
+ SignalErrorMessage(this, stanza, buzz::QN_STANZA_BAD_REQUEST,
+ "modify", "transport-accept has two answers",
+ NULL);
+ return false;
+ }
+
+ transport_elem = elem;
+ if (!transport->OnTransportAnswer(transport_elem)) {
+ SignalErrorMessage(this, stanza, buzz::QN_STANZA_BAD_REQUEST,
+ "modify", "transport-accept is not acceptable",
+ NULL);
+ return false;
+ }
+ SetTransport(transport);
+ }
+ }
+ }
+
+ if (!transport_elem) {
+ SignalErrorMessage(this, stanza, buzz::QN_STANZA_NOT_ALLOWED, "modify",
+ "no supported transport in answer", NULL);
+ return false;
+ }
+
+ // If we discovered that we need compatibility mode and we have sent some
+ // candidates already (using transport-info), then we need to re-send them
+ // using the candidates message.
+ if (compatibility_mode_ && (candidates_.size() > 0)) {
+ ASSERT(transport_ != NULL);
+ ASSERT(transport_->name() == kNsP2pTransport);
+ OnTransportSendMessage(transport_, candidates_);
+ } else {
+ for (size_t i = 0; i < candidates_.size(); ++i)
+ delete candidates_[i];
+ }
+ candidates_.clear();
+
+ return true;
+}
+
+bool Session::OnTransportInfoMessage(const buzz::XmlElement* stanza,
+ const buzz::XmlElement* session) {
+ for(const buzz::XmlElement* elem = session->FirstElement();
+ elem != NULL;
+ elem = elem->NextElement()) {
+ if (elem->Name().LocalPart() == "transport") {
+ Transport* transport = GetTransport(elem->Name().Namespace());
+ if (transport) {
+ if (!transport->OnTransportMessage(elem, stanza))
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+bool Session::OnTerminateMessage(const buzz::XmlElement* stanza,
+ const buzz::XmlElement* session) {
+ for (const buzz::XmlElement *elem = session->FirstElement();
+ elem != NULL;
+ elem = elem->NextElement()) {
+ // elem->Name().LocalPart() is the reason for termination
+ SignalReceivedTerminateReason(this, elem->Name().LocalPart());
+ // elem->FirstElement() might contain a debug string for termination
+ const buzz::XmlElement *debugElement = elem->FirstElement();
+ if (debugElement != NULL) {
+ LOG(LS_VERBOSE) << "Received error on call: "
+ << debugElement->Name().LocalPart();
+ }
+ }
+ SetState(STATE_RECEIVEDTERMINATE);
+ return true;
+}
+
+bool Session::OnCandidatesMessage(const buzz::XmlElement* stanza,
+ const buzz::XmlElement* session) {
+ // If we don't have a transport, then this is the first candidates message.
+ // We first create a fake transport-accept message in order to finish the
+ // negotiation and create a transport.
+ if (!transport_) {
+ compatibility_mode_ = true;
+
+ scoped_ptr<buzz::XmlElement> transport_accept(
+ new buzz::XmlElement(QN_SESSION));
+ transport_accept->SetAttr(buzz::QN_TYPE, "transport-accept");
+
+ buzz::XmlElement* transport_offer =
+ new buzz::XmlElement(kQnP2pTransport, true);
+ transport_accept->AddElement(transport_offer);
+
+ // It is okay to pass the original stanza here. That is only used if we
+ // send an error message. Normal processing looks only at transport_accept.
+ bool valid = OnTransportAcceptMessage(stanza, transport_accept.get());
+ ASSERT(valid);
+ }
+
+ ASSERT(transport_ != NULL);
+ ASSERT(transport_->name() == kNsP2pTransport);
+
+ // Wrap the candidates in a transport element as they would appear in a
+ // transport-info message and send this to the transport.
+ scoped_ptr<buzz::XmlElement> transport_info(
+ new buzz::XmlElement(kQnP2pTransport, true));
+ for (const buzz::XmlElement* elem = session->FirstNamed(kQnLegacyCandidate);
+ elem != NULL;
+ elem = elem->NextNamed(kQnLegacyCandidate)) {
+ buzz::XmlElement* new_candidate = new buzz::XmlElement(*elem);
+ new_candidate->SetName(kQnP2pCandidate);
+ transport_info->AddElement(new_candidate);
+ }
+ return transport_->OnTransportMessage(transport_info.get(), stanza);
+}
+
+bool Session::CheckState(const buzz::XmlElement* stanza, State state) {
+ ASSERT(state_ == state);
+ if (state_ != state) {
+ SignalErrorMessage(this, stanza, buzz::QN_STANZA_NOT_ALLOWED, "modify",
+ "message not allowed in current state", NULL);
+ return false;
+ }
+ return true;
+}
+
+bool Session::FindRequiredElement(const buzz::XmlElement* stanza,
+ const buzz::XmlElement* parent,
+ const buzz::QName& name,
+ const buzz::XmlElement** elem) {
+ *elem = parent->FirstNamed(name);
+ if (*elem == NULL) {
+ std::string text;
+ text += "element '" + parent->Name().Merged() +
+ "' missing required child '" + name.Merged() + "'";
+ SignalErrorMessage(this, stanza, buzz::QN_STANZA_BAD_REQUEST, "modify",
+ text, NULL);
+ return false;
+ }
+ return true;
+}
+
+bool Session::FindRemoteSessionDescription(const buzz::XmlElement* stanza,
+ const buzz::XmlElement* session) {
+ buzz::QName qn_session(session_type_, "description");
+ const buzz::XmlElement* desc;
+ if (!FindRequiredElement(stanza, session, qn_session, &desc))
+ return false;
+ remote_description_ = client_->CreateSessionDescription(desc);
+ return true;
+}
+
+bool Session::FindRequiredAttribute(const buzz::XmlElement* stanza,
+ const buzz::XmlElement* elem,
+ const buzz::QName& name,
+ std::string* value) {
+ if (!elem->HasAttr(name)) {
+ std::string text;
+ text += "element '" + elem->Name().Merged() +
+ "' missing required attribute '" + name.Merged() + "'";
+ SignalErrorMessage(this, stanza, buzz::QN_STANZA_BAD_REQUEST, "modify",
+ text, NULL);
+ return false;
+ } else {
+ *value = elem->Attr(name);
+ return true;
+ }
+}
+
+void Session::OnMessage(talk_base::Message *pmsg) {
+ switch(pmsg->message_id) {
+ case MSG_TIMEOUT:
+ // Session timeout has occured.
+ SetError(ERROR_TIME);
+ break;
+
+ case MSG_ERROR:
+ // Any of the defined errors is most likely fatal.
+ Terminate();
+ break;
+
+ case MSG_STATE:
+ switch (state_) {
+ case STATE_SENTACCEPT:
+ case STATE_RECEIVEDACCEPT:
+ SetState(STATE_INPROGRESS);
+ ASSERT(transport_ != NULL);
+ break;
+
+ case STATE_SENTREJECT:
+ case STATE_SENTREDIRECT:
+ case STATE_RECEIVEDREJECT:
+ Terminate();
+ break;
+
+ case STATE_SENTTERMINATE:
+ case STATE_RECEIVEDTERMINATE:
+ session_manager_->DestroySession(this);
+ break;
+
+ default:
+ // Explicitly ignoring some states here.
+ break;
+ }
+ break;
+ }
+}
+
+} // namespace cricket
diff --git a/Plugins/jingle/libjingle/talk/p2p/base/session.h b/Plugins/jingle/libjingle/talk/p2p/base/session.h
new file mode 100644
index 0000000..5146ea0
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/p2p/base/session.h
@@ -0,0 +1,357 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _SESSION_H_
+#define _SESSION_H_
+
+#include "talk/base/socketaddress.h"
+#include "talk/p2p/base/sessiondescription.h"
+#include "talk/p2p/base/sessionmanager.h"
+#include "talk/p2p/base/sessionclient.h"
+#include "talk/p2p/base/sessionid.h"
+#include "talk/p2p/base/port.h"
+#include "talk/xmllite/xmlelement.h"
+#include <string>
+
+namespace cricket {
+
+class SessionManager;
+class Transport;
+class TransportChannel;
+class TransportChannelProxy;
+class TransportChannelImpl;
+
+// A specific Session created by the SessionManager. A Session manages
+// signaling for session setup and tear down. This setup includes negotiation
+// of both the application-level and network-level protocols: the former
+// defines what will be sent and the latter defines how it will be sent. Each
+// network-level protocol is represented by a Transport object. Each Transport
+// participates in the network-level negotiation. The individual streams of
+// packets are represented by TransportChannels.
+class Session : public talk_base::MessageHandler, public sigslot::has_slots<> {
+ public:
+ enum State {
+ STATE_INIT = 0,
+ STATE_SENTINITIATE, // sent initiate, waiting for Accept or Reject
+ STATE_RECEIVEDINITIATE, // received an initiate. Call Accept or Reject
+ STATE_SENTACCEPT, // sent accept. begin connecting transport
+ STATE_RECEIVEDACCEPT, // received accept. begin connecting transport
+ STATE_SENTMODIFY, // sent modify, waiting for Accept or Reject
+ STATE_RECEIVEDMODIFY, // received modify, call Accept or Reject
+ STATE_SENTREJECT, // sent reject after receiving initiate
+ STATE_RECEIVEDREJECT, // received reject after sending initiate
+ STATE_SENTREDIRECT, // sent direct after receiving initiate
+ STATE_SENTTERMINATE, // sent terminate (any time / either side)
+ STATE_RECEIVEDTERMINATE, // received terminate (any time / either side)
+ STATE_INPROGRESS, // session accepted and in progress
+ STATE_DEINIT, // session is being destroyed
+ };
+
+ enum Error {
+ ERROR_NONE = 0, // no error
+ ERROR_TIME, // no response to signaling
+ ERROR_RESPONSE, // error during signaling
+ ERROR_NETWORK, // network error, could not allocate network resources
+ };
+
+ // Returns the manager that created and owns this session.
+ SessionManager* session_manager() const { return session_manager_; }
+
+ // Returns the XML namespace identifying the type of this session.
+ const std::string& session_type() const { return session_type_; }
+
+ // Returns the client that is handling the application data of this session.
+ SessionClient* client() const { return client_; }
+
+ // Returns the JID this client.
+ const std::string &name() const { return name_; }
+
+ // Returns the JID of the other peer in this session.
+ const std::string &remote_name() const { return remote_name_; }
+
+ // Indicates whether we initiated this session.
+ bool initiator() const { return initiator_; }
+
+ // Holds the ID of this session, which should be unique across the world.
+ const SessionID& id() const { return id_; }
+
+ // Returns the applicication-level description given by our client. This
+ // will be null until Initiate or Accept.
+ const SessionDescription *description() const { return description_; }
+
+ // Returns the applicication-level description given by the other client.
+ // If we are the initiator, this will be null until we receive an accept.
+ const SessionDescription *remote_description() const {
+ return remote_description_;
+ }
+
+ // Returns the current state of the session. See the enum above for details.
+ // Each time the state changes, we will fire this signal.
+ State state() const { return state_; }
+ sigslot::signal2<Session *, State> SignalState;
+
+ // Fired whenever we receive a terminate message along with a reason
+ sigslot::signal2<Session *, const std::string &> SignalReceivedTerminateReason;
+
+ // Returns the last error in the session. See the enum above for details.
+ // Each time the an error occurs, we will fire this signal.
+ Error error() const { return error_; }
+ sigslot::signal2<Session *, Error> SignalError;
+
+ // Returns the transport that has been negotiated or NULL if negotiation is
+ // still in progress.
+ Transport* transport() const { return transport_; }
+
+ // When a session was created by us, we are the initiator, and we send the
+ // initiate message when this method is invoked. The extra_xml parameter is
+ // a list of elements that will get inserted inside <Session> ... </Session>
+ bool Initiate(const std::string &to, std::vector<buzz::XmlElement*>* extra_xml,
+ const SessionDescription *description);
+
+ // When we receive a session initiation from another client, we create a
+ // session in the RECEIVEDINITIATE state. We respond by accepting,
+ // rejecting, or redirecting the session somewhere else.
+ bool Accept(const SessionDescription *description);
+ bool Reject();
+ bool Redirect(const std::string& target);
+
+ // At any time, we may terminate an outstanding session.
+ bool Terminate();
+
+ // The two clients in the session may also send one another arbitrary XML
+ // messages, which are called "info" messages. Both of these functions take
+ // ownership of the XmlElements and delete them when done.
+ typedef std::vector<buzz::XmlElement*> XmlElements;
+ void SendInfoMessage(const XmlElements& elems);
+ sigslot::signal2<Session*, const XmlElements&> SignalInfoMessage;
+
+ // Controls the set of transports that will be allowed for this session. If
+ // we are initiating, then this list will be used to construct the transports
+ // that we will offer to the other side. In that case, the order of the
+ // transport names indicates our preference (first has highest preference).
+ // If we are receiving, then this list indicates the set of transports that
+ // we will allow. We will choose the first transport in the offered list
+ // (1) whose name appears in the given list and (2) that can accept the offer
+ // provided (which may include parameters particular to the transport).
+ //
+ // If this function is not called (or if it is called with a NULL array),
+ // then we will use a default set of transports.
+ void SetPotentialTransports(const std::string names[], size_t length);
+
+ // Once transports have been created (by SetTransports), this function will
+ // return the transport with the given name or NULL if none was created.
+ // Once a particular transport has been chosen, only that transport will be
+ // returned.
+ Transport* GetTransport(const std::string& name);
+
+ // Creates a new channel with the given name. This method may be called
+ // immediately after creating the session. However, the actual
+ // implementation may not be fixed until transport negotiation completes.
+ TransportChannel* CreateChannel(const std::string& name);
+
+ // Returns the channel with the given name.
+ TransportChannel* GetChannel(const std::string& name);
+
+ // Destroys the given channel.
+ void DestroyChannel(TransportChannel* channel);
+
+ // Note: This function is a hack and should not be used.
+ TransportChannelImpl* GetImplementation(TransportChannel* channel);
+
+ // Invoked when we notice that there is no matching channel on our peer.
+ sigslot::signal2<Session*, const std::string&> SignalChannelGone;
+
+ private:
+ typedef std::list<Transport*> TransportList;
+ typedef std::map<std::string, TransportChannelProxy*> ChannelMap;
+
+ SessionManager *session_manager_;
+ std::string name_;
+ std::string remote_name_;
+ bool initiator_;
+ SessionID id_;
+ std::string session_type_;
+ SessionClient* client_;
+ const SessionDescription *description_;
+ const SessionDescription *remote_description_;
+ State state_;
+ Error error_;
+ std::string redirect_target_;
+ // Note that the following two members are mutually exclusive
+ TransportList potential_transports_; // order implies preference
+ Transport* transport_; // negotiated transport
+ ChannelMap channels_;
+ bool compatibility_mode_; // indicates talking to an old client
+ XmlElements candidates_; // holds candidates sent in case of compat-mode
+
+ // Creates or destroys a session. (These are called only SessionManager.)
+ Session(SessionManager *session_manager,
+ const std::string& name,
+ const SessionID& id,
+ const std::string& session_type,
+ SessionClient* client);
+ ~Session();
+
+ // Updates the state, signaling if necessary.
+ void SetState(State state);
+
+ // Updates the error state, signaling if necessary.
+ void SetError(Error error);
+
+ // To improve connection time, this creates the channels on the most common
+ // transport type and initiates connection.
+ void ConnectDefaultTransportChannels(bool create);
+
+ // If a new channel is created after we have created the default transport,
+ // then we should create this channel as well and let it connect.
+ void CreateDefaultTransportChannel(const std::string& name);
+
+ // Creates a default set of transports if the client did not specify some.
+ void CreateTransports();
+
+ // Attempts to choose a transport that is in both our list and the other
+ // clients. This will examine the children of the given XML element to find
+ // the descriptions of the other client's transports. We will pick the first
+ // transport in the other client's list that we also support.
+ // (This is called only by SessionManager.)
+ bool ChooseTransport(const buzz::XmlElement* msg);
+
+ // Called when a single transport has been negotiated.
+ void SetTransport(Transport* transport);
+
+ // Called when the first channel of a transport begins connecting. We use
+ // this to start a timer, to make sure that the connection completes in a
+ // reasonable amount of time.
+ void OnTransportConnecting(Transport* transport);
+
+ // Called when a transport changes its writable state. We track this to make
+ // sure that the transport becomes writable within a reasonable amount of
+ // time. If this does not occur, we signal an error.
+ void OnTransportWritable(Transport* transport);
+
+ // Called when a transport requests signaling.
+ void OnTransportRequestSignaling(Transport* transport);
+
+ // Called when a transport signals that it has a message to send. Note that
+ // these messages are just the transport part of the stanza; they need to be
+ // wrapped in the appropriate session tags.
+ void OnTransportSendMessage(Transport* transport, const XmlElements& elems);
+
+ // Called when a transport signals that it found an error in an incoming
+ // message.
+ void OnTransportSendError(Transport* transport,
+ const buzz::XmlElement* stanza,
+ const buzz::QName& name,
+ const std::string& type,
+ const std::string& text,
+ const buzz::XmlElement* extra_info);
+
+ // Called when we notice that one of our local channels has no peer, so it
+ // should be destroyed.
+ void OnTransportChannelGone(Transport* transport, const std::string& name);
+
+ // When the session needs to send signaling messages, it beings by requesting
+ // signaling. The client should handle this by calling OnSignalingReady once
+ // it is ready to send the messages.
+ // (These are called only by SessionManager.)
+ sigslot::signal1<Session*> SignalRequestSignaling;
+ void OnSignalingReady();
+
+ // Sends a message of the given type to the other client. The body will
+ // contain the given list of elements (which are consumed by the function).
+ void SendSessionMessage(const std::string& type,
+ const XmlElements& elems);
+
+ // Sends a message back to the other client indicating that we have received
+ // and accepted their message.
+ void SendAcknowledgementMessage(const buzz::XmlElement* stanza);
+
+ // Once signaling is ready, the session will use this signal to request the
+ // sending of each message. When messages are received by the other client,
+ // they should be handed to OnIncomingMessage.
+ // (These are called only by SessionManager.)
+ sigslot::signal2<Session *, const buzz::XmlElement*> SignalOutgoingMessage;
+ void OnIncomingMessage(const buzz::XmlElement* stanza);
+
+ void OnFailedSend(const buzz::XmlElement* orig_stanza,
+ const buzz::XmlElement* error_stanza);
+
+ // Invoked when an error is found in an incoming message. This is translated
+ // into the appropriate XMPP response by SessionManager.
+ sigslot::signal6<Session*,
+ const buzz::XmlElement*,
+ const buzz::QName&,
+ const std::string&,
+ const std::string&,
+ const buzz::XmlElement*> SignalErrorMessage;
+
+ // Handlers for the various types of messages. These functions may take
+ // pointers to the whole stanza or to just the session element.
+ bool OnInitiateMessage(const buzz::XmlElement* stanza,
+ const buzz::XmlElement* session);
+ bool OnAcceptMessage(const buzz::XmlElement* stanza,
+ const buzz::XmlElement* session);
+ bool OnRejectMessage(const buzz::XmlElement* stanza,
+ const buzz::XmlElement* session);
+ bool OnRedirectMessage(const buzz::XmlElement* stanza,
+ const buzz::XmlElement* session);
+ bool OnInfoMessage(const buzz::XmlElement* stanza,
+ const buzz::XmlElement* session);
+ bool OnTransportAcceptMessage(const buzz::XmlElement* stanza,
+ const buzz::XmlElement* session);
+ bool OnTransportInfoMessage(const buzz::XmlElement* stanza,
+ const buzz::XmlElement* session);
+ bool OnTerminateMessage(const buzz::XmlElement* stanza,
+ const buzz::XmlElement* session);
+ bool OnCandidatesMessage(const buzz::XmlElement* stanza,
+ const buzz::XmlElement* session);
+
+ // Helper functions for parsing various message types. CheckState verifies
+ // that we are in the appropriate state to receive this message. The latter
+ // three verify that an element has the required child or attribute.
+ bool CheckState(const buzz::XmlElement* stanza, State state);
+ bool FindRequiredElement(const buzz::XmlElement* stanza,
+ const buzz::XmlElement* parent,
+ const buzz::QName& name,
+ const buzz::XmlElement** elem);
+ bool FindRemoteSessionDescription(const buzz::XmlElement* stanza,
+ const buzz::XmlElement* session);
+ bool FindRequiredAttribute(const buzz::XmlElement* stanza,
+ const buzz::XmlElement* elem,
+ const buzz::QName& name,
+ std::string* value);
+
+ // Handles messages posted to us.
+ void OnMessage(talk_base::Message *pmsg);
+
+ friend class SessionManager; // For access to constructor, destructor,
+ // and signaling related methods.
+};
+
+} // namespace cricket
+
+#endif // _SESSION_H_
diff --git a/Plugins/jingle/libjingle/talk/p2p/base/session_unittest.cc b/Plugins/jingle/libjingle/talk/p2p/base/session_unittest.cc
new file mode 100644
index 0000000..972ed9d
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/p2p/base/session_unittest.cc
@@ -0,0 +1,1039 @@
+#include <iostream>
+#include <sstream>
+#include <deque>
+#include <map>
+
+#include "talk/base/common.h"
+#include "talk/base/logging.h"
+#include "talk/base/host.h"
+#include "talk/base/natserver.h"
+#include "talk/base/natsocketfactory.h"
+#include "talk/base/helpers.h"
+#include "talk/xmpp/constants.h"
+#include "talk/p2p/base/constants.h"
+#include "talk/p2p/base/sessionmanager.h"
+#include "talk/p2p/base/sessionclient.h"
+#include "talk/p2p/base/session.h"
+#include "talk/p2p/base/portallocator.h"
+#include "talk/p2p/base/transportchannel.h"
+#include "talk/p2p/base/udpport.h"
+#include "talk/p2p/base/stunport.h"
+#include "talk/p2p/base/relayport.h"
+#include "talk/p2p/base/p2ptransport.h"
+#include "talk/p2p/base/rawtransport.h"
+#include "talk/p2p/base/stunserver.h"
+#include "talk/p2p/base/relayserver.h"
+
+using namespace cricket;
+using namespace buzz;
+
+const std::string kSessionType = "http://oink.splat/session";
+
+const talk_base::SocketAddress kStunServerAddress("127.0.0.1", 7000);
+const talk_base::SocketAddress kStunServerAddress2("127.0.0.1", 7001);
+
+const talk_base::SocketAddress kRelayServerIntAddress("127.0.0.1", 7002);
+const talk_base::SocketAddress kRelayServerExtAddress("127.0.0.1", 7003);
+
+const int kNumPorts = 2;
+
+int gPort = 28653;
+int GetNextPort() {
+ int p = gPort;
+ gPort += 5;
+ return p;
+}
+
+int gID = 0;
+std::string GetNextID() {
+ std::ostringstream ost;
+ ost << gID++;
+ return ost.str();
+}
+
+class TestPortAllocatorSession : public PortAllocatorSession {
+public:
+ TestPortAllocatorSession(talk_base::Thread* worker_thread, talk_base::SocketFactory* factory,
+ const std::string& name, const std::string& session_type)
+ : PortAllocatorSession(0), worker_thread_(worker_thread),
+ factory_(factory), name_(name), ports_(kNumPorts),
+ address_("127.0.0.1", 0), network_("network", address_.ip()),
+ running_(false) {
+ }
+
+ ~TestPortAllocatorSession() {
+ for (int i = 0; i < ports_.size(); i++)
+ delete ports_[i];
+ }
+
+ virtual void GetInitialPorts() {
+ // These are the flags set by the raw transport.
+ uint32 raw_flags = PORTALLOCATOR_DISABLE_UDP | PORTALLOCATOR_DISABLE_TCP;
+
+ // If the client doesn't care, just give them two UDP ports.
+ if (flags() == 0) {
+ for (int i = 0; i < kNumPorts; i++) {
+ ports_[i] = new UDPPort(worker_thread_, factory_, &network_,
+ GetAddress());
+ AddPort(ports_[i]);
+ }
+
+ // If the client requested just stun and relay, we have to oblidge.
+ } else if (flags() == raw_flags) {
+ StunPort* sport = new StunPort(worker_thread_, factory_, &network_,
+ GetAddress(), kStunServerAddress);
+ sport->set_server_addr2(kStunServerAddress2);
+ ports_[0] = sport;
+ AddPort(sport);
+
+ std::string username = CreateRandomString(16);
+ std::string password = CreateRandomString(16);
+ RelayPort* rport = new RelayPort(worker_thread_, factory_, &network_,
+ GetAddress(), username, password, "");
+ rport->AddServerAddress(
+ ProtocolAddress(kRelayServerIntAddress, PROTO_UDP));
+ ports_[1] = rport;
+ AddPort(rport);
+ } else {
+ ASSERT(false);
+ }
+ }
+
+ virtual void StartGetAllPorts() { running_ = true; }
+ virtual void StopGetAllPorts() { running_ = false; }
+ virtual bool IsGettingAllPorts() { return running_; }
+
+ talk_base::SocketAddress GetAddress() const {
+ talk_base::SocketAddress addr(address_);
+ addr.SetPort(GetNextPort());
+ return addr;
+ }
+
+ void AddPort(Port* port) {
+ port->set_name(name_);
+ port->set_preference(1.0);
+ port->set_generation(0);
+ port->SignalDestroyed.connect(
+ this, &TestPortAllocatorSession::OnPortDestroyed);
+ port->SignalAddressReady.connect(
+ this, &TestPortAllocatorSession::OnAddressReady);
+ port->PrepareAddress();
+ SignalPortReady(this, port);
+ }
+
+ void OnPortDestroyed(Port* port) {
+ for (int i = 0; i < ports_.size(); i++) {
+ if (ports_[i] == port)
+ ports_[i] = NULL;
+ }
+ }
+
+ void OnAddressReady(Port* port) {
+ SignalCandidatesReady(this, port->candidates());
+ }
+
+private:
+ talk_base::Thread* worker_thread_;
+ talk_base::SocketFactory* factory_;
+ std::string name_;
+ std::vector<Port*> ports_;
+ talk_base::SocketAddress address_;
+ talk_base::Network network_;
+ bool running_;
+};
+
+class TestPortAllocator : public PortAllocator {
+public:
+ TestPortAllocator(talk_base::Thread* worker_thread, talk_base::SocketFactory* factory)
+ : worker_thread_(worker_thread), factory_(factory) {
+ if (factory_ == NULL)
+ factory_ = worker_thread_->socketserver();
+ }
+
+ virtual PortAllocatorSession *CreateSession(const std::string &name, const std::string &session_type) {
+ return new TestPortAllocatorSession(worker_thread_, factory_, name, session_type);
+ }
+
+private:
+ talk_base::Thread* worker_thread_;
+ talk_base::SocketFactory* factory_;
+};
+
+struct SessionManagerHandler : sigslot::has_slots<> {
+ SessionManagerHandler(SessionManager* m, const std::string& u)
+ : manager(m), username(u), create_count(0), destroy_count(0) {
+ manager->SignalSessionCreate.connect(
+ this, &SessionManagerHandler::OnSessionCreate);
+ manager->SignalSessionDestroy.connect(
+ this, &SessionManagerHandler::OnSessionDestroy);
+ manager->SignalOutgoingMessage.connect(
+ this, &SessionManagerHandler::OnOutgoingMessage);
+ manager->SignalRequestSignaling.connect(
+ this, &SessionManagerHandler::OnRequestSignaling);
+ }
+
+ void OnSessionCreate(Session *session, bool initiate) {
+ create_count += 1;
+ last_id = session->id();
+ }
+
+ void OnSessionDestroy(Session *session) {
+ destroy_count += 1;
+ last_id = session->id();
+ }
+
+ void OnOutgoingMessage(const XmlElement* stanza) {
+ XmlElement* elem = new XmlElement(*stanza);
+ ASSERT(elem->Name() == QN_IQ);
+ ASSERT(elem->HasAttr(QN_TO));
+ ASSERT(!elem->HasAttr(QN_FROM));
+ ASSERT(elem->HasAttr(QN_TYPE));
+ ASSERT((elem->Attr(QN_TYPE) == "set") ||
+ (elem->Attr(QN_TYPE) == "result") ||
+ (elem->Attr(QN_TYPE) == "error"));
+
+ // Add in the appropriate "from".
+ elem->SetAttr(QN_FROM, username);
+
+ // Add in the appropriate IQ ID.
+ if (elem->Attr(QN_TYPE) == "set") {
+ ASSERT(!elem->HasAttr(QN_ID));
+ elem->SetAttr(QN_ID, GetNextID());
+ }
+
+ stanzas_.push_back(elem);
+ }
+
+ void OnRequestSignaling() {
+ manager->OnSignalingReady();
+ }
+
+
+ XmlElement* CheckNextStanza(const std::string& expected) {
+ // Get the next stanza, which should exist.
+ ASSERT(stanzas_.size() > 0);
+ XmlElement* stanza = stanzas_.front();
+ stanzas_.pop_front();
+
+ // Make sure the stanza is correct.
+ std::string actual = stanza->Str();
+ if (actual != expected) {
+ LOG(LERROR) << "Incorrect stanza: expected=\"" << expected
+ << "\" actual=\"" << actual << "\"";
+ ASSERT(actual == expected);
+ }
+
+ return stanza;
+ }
+
+ void CheckNoStanza() {
+ ASSERT(stanzas_.size() == 0);
+ }
+
+ void PrintNextStanza() {
+ ASSERT(stanzas_.size() > 0);
+ printf("Stanza: %s\n", stanzas_.front()->Str().c_str());
+ }
+
+ SessionManager* manager;
+ std::string username;
+ SessionID last_id;
+ uint32 create_count;
+ uint32 destroy_count;
+ std::deque<XmlElement*> stanzas_;
+};
+
+struct SessionHandler : sigslot::has_slots<> {
+ SessionHandler(Session* s) : session(s) {
+ session->SignalState.connect(this, &SessionHandler::OnState);
+ session->SignalError.connect(this, &SessionHandler::OnError);
+ }
+
+ void PrepareTransport() {
+ Transport* transport = session->GetTransport(kNsP2pTransport);
+ if (transport != NULL)
+ transport->set_allow_local_ips(true);
+ }
+
+ void OnState(Session* session, Session::State state) {
+ ASSERT(session == this->session);
+ last_state = state;
+ }
+
+ void OnError(Session* session, Session::Error error) {
+ ASSERT(session == this->session);
+ ASSERT(false); // errors are bad!
+ }
+
+ Session* session;
+ Session::State last_state;
+};
+
+struct MySessionClient: public SessionClient, public sigslot::has_slots<> {
+ MySessionClient() : create_count(0), a(NULL), b(NULL) { }
+
+ void AddManager(SessionManager* manager) {
+ manager->AddClient(kSessionType, this);
+ ASSERT(manager->GetClient(kSessionType) == this);
+ manager->SignalSessionCreate.connect(
+ this, &MySessionClient::OnSessionCreate);
+ }
+
+ const SessionDescription* CreateSessionDescription(
+ const XmlElement* element) {
+ return new SessionDescription();
+ }
+
+ XmlElement* TranslateSessionDescription(
+ const SessionDescription* description) {
+ return new XmlElement(QName(kSessionType, "description"));
+ }
+
+ void OnSessionCreate(Session *session, bool initiate) {
+ create_count += 1;
+ a = session->CreateChannel("a");
+ b = session->CreateChannel("b");
+
+ if (transport_name.size() > 0)
+ session->SetPotentialTransports(&transport_name, 1);
+ }
+
+ void OnSessionDestroy(Session *session)
+ {
+ }
+
+ void SetTransports(bool p2p, bool raw) {
+ if (p2p && raw)
+ return; // this is the default
+
+ if (p2p) {
+ transport_name = kNsP2pTransport;
+ }
+ }
+
+ int create_count;
+ TransportChannel* a;
+ TransportChannel* b;
+ std::string transport_name;
+};
+
+struct ChannelHandler : sigslot::has_slots<> {
+ ChannelHandler(TransportChannel* p)
+ : channel(p), last_readable(false), last_writable(false), data_count(0),
+ last_size(0) {
+ p->SignalReadableState.connect(this, &ChannelHandler::OnReadableState);
+ p->SignalWritableState.connect(this, &ChannelHandler::OnWritableState);
+ p->SignalReadPacket.connect(this, &ChannelHandler::OnReadPacket);
+ }
+
+ void OnReadableState(TransportChannel* p) {
+ ASSERT(p == channel);
+ last_readable = channel->readable();
+ }
+
+ void OnWritableState(TransportChannel* p) {
+ ASSERT(p == channel);
+ last_writable = channel->writable();
+ }
+
+ void OnReadPacket(TransportChannel* p, const char* buf, size_t size) {
+ ASSERT(p == channel);
+ ASSERT(size <= sizeof(last_data));
+ data_count += 1;
+ last_size = size;
+ std::memcpy(last_data, buf, size);
+ }
+
+ void Send(const char* data, size_t size) {
+ int result = channel->SendPacket(data, size);
+ ASSERT(result == static_cast<int>(size));
+ }
+
+ TransportChannel* channel;
+ bool last_readable, last_writable;
+ int data_count;
+ char last_data[4096];
+ size_t last_size;
+};
+
+char* Reverse(const char* str) {
+ int len = strlen(str);
+ char* rev = new char[len+1];
+ for (int i = 0; i < len; i++)
+ rev[i] = str[len-i-1];
+ rev[len] = '\0';
+ return rev;
+}
+
+// Sets up values that should be the same for every test.
+void InitTest() {
+ SetRandomSeed(7);
+ gPort = 28653;
+ gID = 0;
+}
+
+// Tests having client2 accept the session.
+void TestAccept(talk_base::Thread* signaling_thread,
+ Session* session1, Session* session2,
+ SessionHandler* handler1, SessionHandler* handler2,
+ SessionManager* manager1, SessionManager* manager2,
+ SessionManagerHandler* manhandler1,
+ SessionManagerHandler* manhandler2) {
+ // Make sure the IQ ID is 5.
+ ASSERT(gID <= 5);
+ while (gID < 5) GetNextID();
+
+ // Accept the session.
+ SessionDescription* desc2 = new SessionDescription();
+ bool valid = session2->Accept(desc2);
+ ASSERT(valid);
+
+ scoped_ptr<buzz::XmlElement> stanza;
+ stanza.reset(manhandler2->CheckNextStanza(
+ "<cli:iq to=\"foo@baz.com\" type=\"set\" from=\"bar@baz.com\" id=\"5\""
+ " xmlns:cli=\"jabber:client\">"
+ "<session xmlns=\"http://www.google.com/session\" type=\"accept\""
+ " id=\"2154761789\" initiator=\"foo@baz.com\">"
+ "<ses:description xmlns:ses=\"http://oink.splat/session\"/>"
+ "</session>"
+ "</cli:iq>"));
+ manhandler2->CheckNoStanza();
+
+ // Simulate a tiny delay in sending.
+ signaling_thread->ProcessMessages(10);
+
+ // Delivery the accept.
+ manager1->OnIncomingMessage(stanza.get());
+ stanza.reset(manhandler1->CheckNextStanza(
+ "<cli:iq to=\"bar@baz.com\" id=\"5\" type=\"result\" from=\"foo@baz.com\""
+ " xmlns:cli=\"jabber:client\"/>"));
+ manhandler1->CheckNoStanza();
+
+ // Both sessions should be in progress after a short wait.
+ signaling_thread->ProcessMessages(10);
+ ASSERT(handler1->last_state == Session::STATE_INPROGRESS);
+ ASSERT(handler2->last_state == Session::STATE_INPROGRESS);
+}
+
+// Tests sending data between two clients, over two channels.
+void TestSendRecv(ChannelHandler* chanhandler1a, ChannelHandler* chanhandler1b,
+ ChannelHandler* chanhandler2a, ChannelHandler* chanhandler2b,
+ talk_base::Thread* signaling_thread, bool first_dropped) {
+ const char* dat1a = "spamspamspamspamspamspamspambakedbeansspam";
+ const char* dat1b = "Lobster Thermidor a Crevette with a mornay sauce...";
+ const char* dat2a = Reverse(dat1a);
+ const char* dat2b = Reverse(dat1b);
+
+ // Sending from 2 -> 1 will enable 1 to send to 2 below. That will then
+ // enable 2 to send back to 1. So the code below will just work.
+ if (first_dropped) {
+ chanhandler2a->Send(dat2a, strlen(dat2a));
+ chanhandler2b->Send(dat2b, strlen(dat2b));
+ }
+
+ for (int i = 0; i < 20; i++) {
+ chanhandler1a->Send(dat1a, strlen(dat1a));
+ chanhandler1b->Send(dat1b, strlen(dat1b));
+ chanhandler2a->Send(dat2a, strlen(dat2a));
+ chanhandler2b->Send(dat2b, strlen(dat2b));
+
+ signaling_thread->ProcessMessages(10);
+
+ ASSERT(chanhandler1a->data_count == i + 1);
+ ASSERT(chanhandler1b->data_count == i + 1);
+ ASSERT(chanhandler2a->data_count == i + 1);
+ ASSERT(chanhandler2b->data_count == i + 1);
+
+ ASSERT(chanhandler1a->last_size == strlen(dat2a));
+ ASSERT(chanhandler1b->last_size == strlen(dat2b));
+ ASSERT(chanhandler2a->last_size == strlen(dat1a));
+ ASSERT(chanhandler2b->last_size == strlen(dat1b));
+
+ ASSERT(std::memcmp(chanhandler1a->last_data, dat2a, strlen(dat2a)) == 0);
+ ASSERT(std::memcmp(chanhandler1b->last_data, dat2b, strlen(dat2b)) == 0);
+ ASSERT(std::memcmp(chanhandler2a->last_data, dat1a, strlen(dat1a)) == 0);
+ ASSERT(std::memcmp(chanhandler2b->last_data, dat1b, strlen(dat1b)) == 0);
+ }
+}
+
+// Tests a session between two clients. The inputs indicate whether we should
+// replace each client's output with what we would see from an old client.
+void TestP2PCompatibility(const std::string& test_name, bool old1, bool old2) {
+ InitTest();
+
+ talk_base::Thread* signaling_thread = talk_base::Thread::Current();
+ scoped_ptr<talk_base::Thread> worker_thread(new talk_base::Thread());
+ worker_thread->Start();
+
+ scoped_ptr<PortAllocator> allocator(
+ new TestPortAllocator(worker_thread.get(), NULL));
+ scoped_ptr<MySessionClient> client(new MySessionClient());
+ client->SetTransports(true, false);
+
+ scoped_ptr<SessionManager> manager1(
+ new SessionManager(allocator.get(), worker_thread.get()));
+ scoped_ptr<SessionManagerHandler> manhandler1(
+ new SessionManagerHandler(manager1.get(), "foo@baz.com"));
+ client->AddManager(manager1.get());
+
+ Session* session1 = manager1->CreateSession("foo@baz.com", kSessionType);
+ ASSERT(manhandler1->create_count == 1);
+ ASSERT(manhandler1->last_id == session1->id());
+ scoped_ptr<SessionHandler> handler1(new SessionHandler(session1));
+
+ ASSERT(client->create_count == 1);
+ TransportChannel* chan1a = client->a;
+ ASSERT(chan1a->name() == "a");
+ ASSERT(session1->GetChannel("a") == chan1a);
+ scoped_ptr<ChannelHandler> chanhandler1a(new ChannelHandler(chan1a));
+ TransportChannel* chan1b = client->b;
+ ASSERT(chan1b->name() == "b");
+ ASSERT(session1->GetChannel("b") == chan1b);
+ scoped_ptr<ChannelHandler> chanhandler1b(new ChannelHandler(chan1b));
+
+ SessionDescription* desc1 = new SessionDescription();
+ ASSERT(session1->state() == Session::STATE_INIT);
+ bool valid = session1->Initiate("bar@baz.com", NULL, desc1);
+ ASSERT(valid);
+ handler1->PrepareTransport();
+
+ signaling_thread->ProcessMessages(100);
+
+ ASSERT(handler1->last_state == Session::STATE_SENTINITIATE);
+ scoped_ptr<XmlElement> stanza1, stanza2;
+ stanza1.reset(manhandler1->CheckNextStanza(
+ "<cli:iq to=\"bar@baz.com\" type=\"set\" from=\"foo@baz.com\" id=\"0\""
+ " xmlns:cli=\"jabber:client\">"
+ "<session xmlns=\"http://www.google.com/session\" type=\"initiate\""
+ " id=\"2154761789\" initiator=\"foo@baz.com\">"
+ "<ses:description xmlns:ses=\"http://oink.splat/session\"/>"
+ "<p:transport xmlns:p=\"http://www.google.com/transport/p2p\"/>"
+ "</session>"
+ "</cli:iq>"));
+ stanza2.reset(manhandler1->CheckNextStanza(
+ "<cli:iq to=\"bar@baz.com\" type=\"set\" from=\"foo@baz.com\" id=\"1\""
+ " xmlns:cli=\"jabber:client\">"
+ "<session xmlns=\"http://www.google.com/session\" type=\"transport-info\""
+ " id=\"2154761789\" initiator=\"foo@baz.com\">"
+ "<p:transport xmlns:p=\"http://www.google.com/transport/p2p\">"
+ "<candidate name=\"a\" address=\"127.0.0.1\" port=\"28653\""
+ " preference=\"1\" username=\"h0ISP4S5SJKH/9EY\" protocol=\"udp\""
+ " generation=\"0\" password=\"UhnAmO5C89dD2dZ+\" type=\"local\""
+ " network=\"network\"/>"
+ "<candidate name=\"a\" address=\"127.0.0.1\" port=\"28658\""
+ " preference=\"1\" username=\"yid4vfB3zXPvrRB9\" protocol=\"udp\""
+ " generation=\"0\" password=\"SqLXTvcEyriIo+Mj\" type=\"local\""
+ " network=\"network\"/>"
+ "<candidate name=\"b\" address=\"127.0.0.1\" port=\"28663\""
+ " preference=\"1\" username=\"NvT78D7WxPWM1KL8\" protocol=\"udp\""
+ " generation=\"0\" password=\"+mV/QhOapXu4caPX\" type=\"local\""
+ " network=\"network\"/>"
+ "<candidate name=\"b\" address=\"127.0.0.1\" port=\"28668\""
+ " preference=\"1\" username=\"8EzB7MH+TYpIlSp/\" protocol=\"udp\""
+ " generation=\"0\" password=\"h+MelLXupoK5aYqC\" type=\"local\""
+ " network=\"network\"/>"
+ "</p:transport>"
+ "</session>"
+ "</cli:iq>"));
+ manhandler1->CheckNoStanza();
+
+ // If the first client were old, the initiate would have no transports and
+ // the candidates would be sent in a candidates message.
+ if (old1) {
+ stanza1.reset(XmlElement::ForStr(
+ "<cli:iq to=\"bar@baz.com\" type=\"set\" from=\"foo@baz.com\" id=\"0\""
+ " xmlns:cli=\"jabber:client\">"
+ "<session xmlns=\"http://www.google.com/session\" type=\"initiate\""
+ " id=\"2154761789\" initiator=\"foo@baz.com\">"
+ "<ses:description xmlns:ses=\"http://oink.splat/session\"/>"
+ "</session>"
+ "</cli:iq>"));
+ stanza2.reset(XmlElement::ForStr(
+ "<cli:iq to=\"bar@baz.com\" type=\"set\" from=\"foo@baz.com\" id=\"1\""
+ " xmlns:cli=\"jabber:client\">"
+ "<session xmlns=\"http://www.google.com/session\" type=\"candidates\""
+ " id=\"2154761789\" initiator=\"foo@baz.com\">"
+ "<candidate name=\"a\" address=\"127.0.0.1\" port=\"28653\""
+ " preference=\"1\" username=\"h0ISP4S5SJKH/9EY\" protocol=\"udp\""
+ " generation=\"0\" password=\"UhnAmO5C89dD2dZ+\" type=\"local\""
+ " network=\"network\"/>"
+ "<candidate name=\"a\" address=\"127.0.0.1\" port=\"28658\""
+ " preference=\"1\" username=\"yid4vfB3zXPvrRB9\" protocol=\"udp\""
+ " generation=\"0\" password=\"SqLXTvcEyriIo+Mj\" type=\"local\""
+ " network=\"network\"/>"
+ "<candidate name=\"b\" address=\"127.0.0.1\" port=\"28663\""
+ " preference=\"1\" username=\"NvT78D7WxPWM1KL8\" protocol=\"udp\""
+ " generation=\"0\" password=\"+mV/QhOapXu4caPX\" type=\"local\""
+ " network=\"network\"/>"
+ "<candidate name=\"b\" address=\"127.0.0.1\" port=\"28668\""
+ " preference=\"1\" username=\"8EzB7MH+TYpIlSp/\" protocol=\"udp\""
+ " generation=\"0\" password=\"h+MelLXupoK5aYqC\" type=\"local\""
+ " network=\"network\"/>"
+ "</session>"
+ "</cli:iq>"));
+ }
+
+ scoped_ptr<SessionManager> manager2(
+ new SessionManager(allocator.get(), worker_thread.get()));
+ scoped_ptr<SessionManagerHandler> manhandler2(
+ new SessionManagerHandler(manager2.get(), "bar@baz.com"));
+ client->AddManager(manager2.get());
+
+ // Deliver the initiate.
+ manager2->OnIncomingMessage(stanza1.get());
+ stanza1.reset(manhandler2->CheckNextStanza(
+ "<cli:iq to=\"foo@baz.com\" id=\"0\" type=\"result\" from=\"bar@baz.com\""
+ " xmlns:cli=\"jabber:client\"/>"));
+
+ // If client1 is old, we will not see a transport-accept. If client2 is old,
+ // then we should act as if it did not send one.
+ if (!old1) {
+ stanza1.reset(manhandler2->CheckNextStanza(
+ "<cli:iq to=\"foo@baz.com\" type=\"set\" from=\"bar@baz.com\" id=\"2\""
+ " xmlns:cli=\"jabber:client\">"
+ "<session xmlns=\"http://www.google.com/session\""
+ " type=\"transport-accept\" id=\"2154761789\" initiator=\"foo@baz.com\">"
+ "<p:transport xmlns:p=\"http://www.google.com/transport/p2p\"/>"
+ "</session>"
+ "</cli:iq>"));
+ } else {
+ GetNextID(); // Advance the ID count to be the same in all cases.
+ stanza1.reset(NULL);
+ }
+ if (old2) {
+ stanza1.reset(NULL);
+ }
+ manhandler2->CheckNoStanza();
+ ASSERT(manhandler2->create_count == 1);
+ ASSERT(manhandler2->last_id == session1->id());
+
+ Session* session2 = manager2->GetSession(session1->id());
+ ASSERT(session2);
+ ASSERT(session1->id() == session2->id());
+ ASSERT(manhandler2->last_id == session2->id());
+ ASSERT(session2->state() == Session::STATE_RECEIVEDINITIATE);
+ scoped_ptr<SessionHandler> handler2(new SessionHandler(session2));
+ handler2->PrepareTransport();
+
+ ASSERT(session2->name() == session1->remote_name());
+ ASSERT(session1->name() == session2->remote_name());
+
+ ASSERT(session2->transport() != NULL);
+ ASSERT(session2->transport()->name() == kNsP2pTransport);
+
+ ASSERT(client->create_count == 2);
+ TransportChannel* chan2a = client->a;
+ scoped_ptr<ChannelHandler> chanhandler2a(new ChannelHandler(chan2a));
+ TransportChannel* chan2b = client->b;
+ scoped_ptr<ChannelHandler> chanhandler2b(new ChannelHandler(chan2b));
+
+ // Deliver the candidates.
+ manager2->OnIncomingMessage(stanza2.get());
+ stanza2.reset(manhandler2->CheckNextStanza(
+ "<cli:iq to=\"foo@baz.com\" id=\"1\" type=\"result\" from=\"bar@baz.com\""
+ " xmlns:cli=\"jabber:client\"/>"));
+
+ signaling_thread->ProcessMessages(10);
+
+ // If client1 is old, we should see a candidates message instead of a
+ // transport-info. If client2 is old, we should act as if we did.
+ const char* kCandidates2 =
+ "<cli:iq to=\"foo@baz.com\" type=\"set\" from=\"bar@baz.com\" id=\"3\""
+ " xmlns:cli=\"jabber:client\">"
+ "<session xmlns=\"http://www.google.com/session\" type=\"candidates\""
+ " id=\"2154761789\" initiator=\"foo@baz.com\">"
+ "<candidate name=\"a\" address=\"127.0.0.1\" port=\"28673\""
+ " preference=\"1\" username=\"FJDz3iuXjbQJDRjs\" protocol=\"udp\""
+ " generation=\"0\" password=\"Ca5daV9m6G91qhlM\" type=\"local\""
+ " network=\"network\"/>"
+ "<candidate name=\"a\" address=\"127.0.0.1\" port=\"28678\""
+ " preference=\"1\" username=\"xlN53r3Jn/R5XuCt\" protocol=\"udp\""
+ " generation=\"0\" password=\"rgik2pKsjaPSUdJd\" type=\"local\""
+ " network=\"network\"/>"
+ "<candidate name=\"b\" address=\"127.0.0.1\" port=\"28683\""
+ " preference=\"1\" username=\"IBZ8CSq8ot2+pSMp\" protocol=\"udp\""
+ " generation=\"0\" password=\"i7RcDsGntMI6fzdd\" type=\"local\""
+ " network=\"network\"/>"
+ "<candidate name=\"b\" address=\"127.0.0.1\" port=\"28688\""
+ " preference=\"1\" username=\"SEtih9PYtMHCAlMI\" protocol=\"udp\""
+ " generation=\"0\" password=\"wROrHJ3+gDxUUMp1\" type=\"local\""
+ " network=\"network\"/>"
+ "</session>"
+ "</cli:iq>";
+ if (old1) {
+ stanza2.reset(manhandler2->CheckNextStanza(kCandidates2));
+ } else {
+ stanza2.reset(manhandler2->CheckNextStanza(
+ "<cli:iq to=\"foo@baz.com\" type=\"set\" from=\"bar@baz.com\" id=\"3\""
+ " xmlns:cli=\"jabber:client\">"
+ "<session xmlns=\"http://www.google.com/session\" type=\"transport-info\""
+ " id=\"2154761789\" initiator=\"foo@baz.com\">"
+ "<p:transport xmlns:p=\"http://www.google.com/transport/p2p\">"
+ "<candidate name=\"a\" address=\"127.0.0.1\" port=\"28673\""
+ " preference=\"1\" username=\"FJDz3iuXjbQJDRjs\" protocol=\"udp\""
+ " generation=\"0\" password=\"Ca5daV9m6G91qhlM\" type=\"local\""
+ " network=\"network\"/>"
+ "<candidate name=\"a\" address=\"127.0.0.1\" port=\"28678\""
+ " preference=\"1\" username=\"xlN53r3Jn/R5XuCt\" protocol=\"udp\""
+ " generation=\"0\" password=\"rgik2pKsjaPSUdJd\" type=\"local\""
+ " network=\"network\"/>"
+ "<candidate name=\"b\" address=\"127.0.0.1\" port=\"28683\""
+ " preference=\"1\" username=\"IBZ8CSq8ot2+pSMp\" protocol=\"udp\""
+ " generation=\"0\" password=\"i7RcDsGntMI6fzdd\" type=\"local\""
+ " network=\"network\"/>"
+ "<candidate name=\"b\" address=\"127.0.0.1\" port=\"28688\""
+ " preference=\"1\" username=\"SEtih9PYtMHCAlMI\" protocol=\"udp\""
+ " generation=\"0\" password=\"wROrHJ3+gDxUUMp1\" type=\"local\""
+ " network=\"network\"/>"
+ "</p:transport>"
+ "</session>"
+ "</cli:iq>"));
+ }
+ if (old2) {
+ stanza2.reset(XmlElement::ForStr(kCandidates2));
+ }
+ manhandler2->CheckNoStanza();
+
+ // Deliver the transport-accept if one exists.
+ if (stanza1.get() != NULL) {
+ manager1->OnIncomingMessage(stanza1.get());
+ stanza1.reset(manhandler1->CheckNextStanza(
+ "<cli:iq to=\"bar@baz.com\" id=\"2\" type=\"result\" from=\"foo@baz.com\""
+ " xmlns:cli=\"jabber:client\"/>"));
+ manhandler1->CheckNoStanza();
+
+ // The first session should now have a transport.
+ ASSERT(session1->transport() != NULL);
+ ASSERT(session1->transport()->name() == kNsP2pTransport);
+ }
+
+ // Deliver the candidates. If client2 is old (or is acting old because
+ // client1 is), then client1 will correct its earlier mistake of sending
+ // transport-info by sending a candidates message. If client1 is supposed to
+ // be old, then it sent candidates earlier, so we drop this.
+ manager1->OnIncomingMessage(stanza2.get());
+ if (old1 || old2) {
+ stanza2.reset(manhandler1->CheckNextStanza(
+ "<cli:iq to=\"bar@baz.com\" type=\"set\" from=\"foo@baz.com\" id=\"4\""
+ " xmlns:cli=\"jabber:client\">"
+ "<session xmlns=\"http://www.google.com/session\" type=\"candidates\""
+ " id=\"2154761789\" initiator=\"foo@baz.com\">"
+ "<candidate name=\"a\" address=\"127.0.0.1\" port=\"28653\""
+ " preference=\"1\" username=\"h0ISP4S5SJKH/9EY\" protocol=\"udp\""
+ " generation=\"0\" password=\"UhnAmO5C89dD2dZ+\" type=\"local\""
+ " network=\"network\"/>"
+ "<candidate name=\"a\" address=\"127.0.0.1\" port=\"28658\""
+ " preference=\"1\" username=\"yid4vfB3zXPvrRB9\" protocol=\"udp\""
+ " generation=\"0\" password=\"SqLXTvcEyriIo+Mj\" type=\"local\""
+ " network=\"network\"/>"
+ "<candidate name=\"b\" address=\"127.0.0.1\" port=\"28663\""
+ " preference=\"1\" username=\"NvT78D7WxPWM1KL8\" protocol=\"udp\""
+ " generation=\"0\" password=\"+mV/QhOapXu4caPX\" type=\"local\""
+ " network=\"network\"/>"
+ "<candidate name=\"b\" address=\"127.0.0.1\" port=\"28668\""
+ " preference=\"1\" username=\"8EzB7MH+TYpIlSp/\" protocol=\"udp\""
+ " generation=\"0\" password=\"h+MelLXupoK5aYqC\" type=\"local\""
+ " network=\"network\"/>"
+ "</session>"
+ "</cli:iq>"));
+ } else {
+ GetNextID(); // Advance the ID count to be the same in all cases.
+ stanza2.reset(NULL);
+ }
+ if (old1) {
+ stanza2.reset(NULL);
+ }
+ stanza1.reset(manhandler1->CheckNextStanza(
+ "<cli:iq to=\"bar@baz.com\" id=\"3\" type=\"result\" from=\"foo@baz.com\""
+ " xmlns:cli=\"jabber:client\"/>"));
+ manhandler1->CheckNoStanza();
+
+ // The first session must have a transport in either case now.
+ ASSERT(session1->transport() != NULL);
+ ASSERT(session1->transport()->name() == kNsP2pTransport);
+
+ // If client1 just generated a candidates message, then we must deliver it.
+ if (stanza2.get() != NULL) {
+ manager2->OnIncomingMessage(stanza2.get());
+ stanza2.reset(manhandler2->CheckNextStanza(
+ "<cli:iq to=\"foo@baz.com\" id=\"4\" type=\"result\" from=\"bar@baz.com\""
+ " xmlns:cli=\"jabber:client\"/>"));
+ manhandler2->CheckNoStanza();
+ }
+
+ // The channels should be able to become writable at this point. This
+ // requires pinging, so it may take a little while.
+ signaling_thread->ProcessMessages(500);
+ ASSERT(chan1a->writable() && chan1a->readable());
+ ASSERT(chan1b->writable() && chan1b->readable());
+ ASSERT(chan2a->writable() && chan2a->readable());
+ ASSERT(chan2b->writable() && chan2b->readable());
+ ASSERT(chanhandler1a->last_writable);
+ ASSERT(chanhandler1b->last_writable);
+ ASSERT(chanhandler2a->last_writable);
+ ASSERT(chanhandler2b->last_writable);
+
+ // Accept the session.
+ TestAccept(signaling_thread, session1, session2,
+ handler1.get(), handler2.get(),
+ manager1.get(), manager2.get(),
+ manhandler1.get(), manhandler2.get());
+
+ // Send a bunch of data between them.
+ TestSendRecv(chanhandler1a.get(), chanhandler1b.get(), chanhandler2a.get(),
+ chanhandler2b.get(), signaling_thread, false);
+
+ manager1->DestroySession(session1);
+ manager2->DestroySession(session2);
+
+ ASSERT(manhandler1->create_count == 1);
+ ASSERT(manhandler2->create_count == 1);
+ ASSERT(manhandler1->destroy_count == 1);
+ ASSERT(manhandler2->destroy_count == 1);
+
+ worker_thread->Stop();
+
+ std::cout << "P2P Compatibility: " << test_name << ": PASS" << std::endl;
+}
+
+// Tests the P2P transport. The flags indicate whether they clients will
+// advertise support for raw as well.
+void TestP2P(const std::string& test_name, bool raw1, bool raw2) {
+ InitTest();
+
+ talk_base::Thread* signaling_thread = talk_base::Thread::Current();
+ scoped_ptr<talk_base::Thread> worker_thread(new talk_base::Thread());
+ worker_thread->Start();
+
+ scoped_ptr<PortAllocator> allocator(
+ new TestPortAllocator(worker_thread.get(), NULL));
+ scoped_ptr<MySessionClient> client1(new MySessionClient());
+ client1->SetTransports(true, raw1);
+ scoped_ptr<MySessionClient> client2(new MySessionClient());
+ client2->SetTransports(true, raw2);
+
+ scoped_ptr<SessionManager> manager1(
+ new SessionManager(allocator.get(), worker_thread.get()));
+ scoped_ptr<SessionManagerHandler> manhandler1(
+ new SessionManagerHandler(manager1.get(), "foo@baz.com"));
+ client1->AddManager(manager1.get());
+
+ Session* session1 = manager1->CreateSession("foo@baz.com", kSessionType);
+ ASSERT(manhandler1->create_count == 1);
+ ASSERT(manhandler1->last_id == session1->id());
+ scoped_ptr<SessionHandler> handler1(new SessionHandler(session1));
+
+ ASSERT(client1->create_count == 1);
+ TransportChannel* chan1a = client1->a;
+ ASSERT(chan1a->name() == "a");
+ ASSERT(session1->GetChannel("a") == chan1a);
+ scoped_ptr<ChannelHandler> chanhandler1a(new ChannelHandler(chan1a));
+ TransportChannel* chan1b = client1->b;
+ ASSERT(chan1b->name() == "b");
+ ASSERT(session1->GetChannel("b") == chan1b);
+ scoped_ptr<ChannelHandler> chanhandler1b(new ChannelHandler(chan1b));
+
+ SessionDescription* desc1 = new SessionDescription();
+ ASSERT(session1->state() == Session::STATE_INIT);
+ bool valid = session1->Initiate("bar@baz.com", NULL, desc1);
+ ASSERT(valid);
+ handler1->PrepareTransport();
+
+ signaling_thread->ProcessMessages(100);
+
+ ASSERT(handler1->last_state == Session::STATE_SENTINITIATE);
+ scoped_ptr<XmlElement> stanza1, stanza2;
+ if (raw1) {
+ stanza1.reset(manhandler1->CheckNextStanza(
+ "<cli:iq to=\"bar@baz.com\" type=\"set\" from=\"foo@baz.com\" id=\"0\""
+ " xmlns:cli=\"jabber:client\">"
+ "<session xmlns=\"http://www.google.com/session\" type=\"initiate\""
+ " id=\"2154761789\" initiator=\"foo@baz.com\">"
+ "<ses:description xmlns:ses=\"http://oink.splat/session\"/>"
+ "<p:transport xmlns:p=\"http://www.google.com/transport/p2p\"/>"
+ "<raw:transport xmlns:raw=\"http://www.google.com/transport/raw\"/>"
+ "</session>"
+ "</cli:iq>"));
+ } else {
+ stanza1.reset(manhandler1->CheckNextStanza(
+ "<cli:iq to=\"bar@baz.com\" type=\"set\" from=\"foo@baz.com\" id=\"0\""
+ " xmlns:cli=\"jabber:client\">"
+ "<session xmlns=\"http://www.google.com/session\" type=\"initiate\""
+ " id=\"2154761789\" initiator=\"foo@baz.com\">"
+ "<ses:description xmlns:ses=\"http://oink.splat/session\"/>"
+ "<p:transport xmlns:p=\"http://www.google.com/transport/p2p\"/>"
+ "</session>"
+ "</cli:iq>"));
+ }
+ stanza2.reset(manhandler1->CheckNextStanza(
+ "<cli:iq to=\"bar@baz.com\" type=\"set\" from=\"foo@baz.com\" id=\"1\""
+ " xmlns:cli=\"jabber:client\">"
+ "<session xmlns=\"http://www.google.com/session\" type=\"transport-info\""
+ " id=\"2154761789\" initiator=\"foo@baz.com\">"
+ "<p:transport xmlns:p=\"http://www.google.com/transport/p2p\">"
+ "<candidate name=\"a\" address=\"127.0.0.1\" port=\"28653\""
+ " preference=\"1\" username=\"h0ISP4S5SJKH/9EY\" protocol=\"udp\""
+ " generation=\"0\" password=\"UhnAmO5C89dD2dZ+\" type=\"local\""
+ " network=\"network\"/>"
+ "<candidate name=\"a\" address=\"127.0.0.1\" port=\"28658\""
+ " preference=\"1\" username=\"yid4vfB3zXPvrRB9\" protocol=\"udp\""
+ " generation=\"0\" password=\"SqLXTvcEyriIo+Mj\" type=\"local\""
+ " network=\"network\"/>"
+ "<candidate name=\"b\" address=\"127.0.0.1\" port=\"28663\""
+ " preference=\"1\" username=\"NvT78D7WxPWM1KL8\" protocol=\"udp\""
+ " generation=\"0\" password=\"+mV/QhOapXu4caPX\" type=\"local\""
+ " network=\"network\"/>"
+ "<candidate name=\"b\" address=\"127.0.0.1\" port=\"28668\""
+ " preference=\"1\" username=\"8EzB7MH+TYpIlSp/\" protocol=\"udp\""
+ " generation=\"0\" password=\"h+MelLXupoK5aYqC\" type=\"local\""
+ " network=\"network\"/>"
+ "</p:transport>"
+ "</session>"
+ "</cli:iq>"));
+ manhandler1->CheckNoStanza();
+
+ scoped_ptr<SessionManager> manager2(
+ new SessionManager(allocator.get(), worker_thread.get()));
+ scoped_ptr<SessionManagerHandler> manhandler2(
+ new SessionManagerHandler(manager2.get(), "bar@baz.com"));
+ client2->AddManager(manager2.get());
+
+ // Deliver the initiate.
+ manager2->OnIncomingMessage(stanza1.get());
+ stanza1.reset(manhandler2->CheckNextStanza(
+ "<cli:iq to=\"foo@baz.com\" id=\"0\" type=\"result\" from=\"bar@baz.com\""
+ " xmlns:cli=\"jabber:client\"/>"));
+ stanza1.reset(manhandler2->CheckNextStanza(
+ "<cli:iq to=\"foo@baz.com\" type=\"set\" from=\"bar@baz.com\" id=\"2\""
+ " xmlns:cli=\"jabber:client\">"
+ "<session xmlns=\"http://www.google.com/session\""
+ " type=\"transport-accept\" id=\"2154761789\" initiator=\"foo@baz.com\">"
+ "<p:transport xmlns:p=\"http://www.google.com/transport/p2p\"/>"
+ "</session>"
+ "</cli:iq>"));
+ manhandler2->CheckNoStanza();
+ ASSERT(manhandler2->create_count == 1);
+ ASSERT(manhandler2->last_id == session1->id());
+
+ Session* session2 = manager2->GetSession(session1->id());
+ ASSERT(session2);
+ ASSERT(session1->id() == session2->id());
+ ASSERT(manhandler2->last_id == session2->id());
+ ASSERT(session2->state() == Session::STATE_RECEIVEDINITIATE);
+ scoped_ptr<SessionHandler> handler2(new SessionHandler(session2));
+ handler2->PrepareTransport();
+
+ ASSERT(session2->name() == session1->remote_name());
+ ASSERT(session1->name() == session2->remote_name());
+
+ ASSERT(session2->transport() != NULL);
+ ASSERT(session2->transport()->name() == kNsP2pTransport);
+
+ ASSERT(client2->create_count == 1);
+ TransportChannel* chan2a = client2->a;
+ scoped_ptr<ChannelHandler> chanhandler2a(new ChannelHandler(chan2a));
+ TransportChannel* chan2b = client2->b;
+ scoped_ptr<ChannelHandler> chanhandler2b(new ChannelHandler(chan2b));
+
+ // Deliver the candidates.
+ manager2->OnIncomingMessage(stanza2.get());
+ stanza2.reset(manhandler2->CheckNextStanza(
+ "<cli:iq to=\"foo@baz.com\" id=\"1\" type=\"result\" from=\"bar@baz.com\""
+ " xmlns:cli=\"jabber:client\"/>"));
+
+ signaling_thread->ProcessMessages(10);
+
+ stanza2.reset(manhandler2->CheckNextStanza(
+ "<cli:iq to=\"foo@baz.com\" type=\"set\" from=\"bar@baz.com\" id=\"3\""
+ " xmlns:cli=\"jabber:client\">"
+ "<session xmlns=\"http://www.google.com/session\" type=\"transport-info\""
+ " id=\"2154761789\" initiator=\"foo@baz.com\">"
+ "<p:transport xmlns:p=\"http://www.google.com/transport/p2p\">"
+ "<candidate name=\"a\" address=\"127.0.0.1\" port=\"28673\""
+ " preference=\"1\" username=\"FJDz3iuXjbQJDRjs\" protocol=\"udp\""
+ " generation=\"0\" password=\"Ca5daV9m6G91qhlM\" type=\"local\""
+ " network=\"network\"/>"
+ "<candidate name=\"a\" address=\"127.0.0.1\" port=\"28678\""
+ " preference=\"1\" username=\"xlN53r3Jn/R5XuCt\" protocol=\"udp\""
+ " generation=\"0\" password=\"rgik2pKsjaPSUdJd\" type=\"local\""
+ " network=\"network\"/>"
+ "<candidate name=\"b\" address=\"127.0.0.1\" port=\"28683\""
+ " preference=\"1\" username=\"IBZ8CSq8ot2+pSMp\" protocol=\"udp\""
+ " generation=\"0\" password=\"i7RcDsGntMI6fzdd\" type=\"local\""
+ " network=\"network\"/>"
+ "<candidate name=\"b\" address=\"127.0.0.1\" port=\"28688\""
+ " preference=\"1\" username=\"SEtih9PYtMHCAlMI\" protocol=\"udp\""
+ " generation=\"0\" password=\"wROrHJ3+gDxUUMp1\" type=\"local\""
+ " network=\"network\"/>"
+ "</p:transport>"
+ "</session>"
+ "</cli:iq>"));
+ manhandler2->CheckNoStanza();
+
+ // Deliver the transport-accept.
+ manager1->OnIncomingMessage(stanza1.get());
+ stanza1.reset(manhandler1->CheckNextStanza(
+ "<cli:iq to=\"bar@baz.com\" id=\"2\" type=\"result\" from=\"foo@baz.com\""
+ " xmlns:cli=\"jabber:client\"/>"));
+ manhandler1->CheckNoStanza();
+
+ // The first session should now have a transport.
+ ASSERT(session1->transport() != NULL);
+ ASSERT(session1->transport()->name() == kNsP2pTransport);
+
+ // Deliver the candidates.
+ manager1->OnIncomingMessage(stanza2.get());
+ stanza1.reset(manhandler1->CheckNextStanza(
+ "<cli:iq to=\"bar@baz.com\" id=\"3\" type=\"result\" from=\"foo@baz.com\""
+ " xmlns:cli=\"jabber:client\"/>"));
+ manhandler1->CheckNoStanza();
+
+ // The channels should be able to become writable at this point. This
+ // requires pinging, so it may take a little while.
+ signaling_thread->ProcessMessages(500);
+ ASSERT(chan1a->writable() && chan1a->readable());
+ ASSERT(chan1b->writable() && chan1b->readable());
+ ASSERT(chan2a->writable() && chan2a->readable());
+ ASSERT(chan2b->writable() && chan2b->readable());
+ ASSERT(chanhandler1a->last_writable);
+ ASSERT(chanhandler1b->last_writable);
+ ASSERT(chanhandler2a->last_writable);
+ ASSERT(chanhandler2b->last_writable);
+
+ // Accept the session.
+ TestAccept(signaling_thread, session1, session2,
+ handler1.get(), handler2.get(),
+ manager1.get(), manager2.get(),
+ manhandler1.get(), manhandler2.get());
+
+ // Send a bunch of data between them.
+ TestSendRecv(chanhandler1a.get(), chanhandler1b.get(), chanhandler2a.get(),
+ chanhandler2b.get(), signaling_thread, false);
+
+ manager1->DestroySession(session1);
+ manager2->DestroySession(session2);
+
+ ASSERT(manhandler1->create_count == 1);
+ ASSERT(manhandler2->create_count == 1);
+ ASSERT(manhandler1->destroy_count == 1);
+ ASSERT(manhandler2->destroy_count == 1);
+
+ worker_thread->Stop();
+
+ std::cout << "P2P: " << test_name << ": PASS" << std::endl;
+}
+//
+int main(int argc, char* argv[]) {
+ talk_base::LogMessage::LogToDebug(talk_base::LS_WARNING);
+
+ TestP2P("{p2p} => {p2p}", false, false);
+ TestP2P("{p2p} => {p2p,raw}", false, true);
+ TestP2P("{p2p,raw} => {p2p}", true, false);
+ TestP2P("{p2p,raw} => {p2p,raw}", true, true);
+ TestP2PCompatibility("New => New", false, false);
+ TestP2PCompatibility("Old => New", true, false);
+ TestP2PCompatibility("New => Old", false, true);
+ TestP2PCompatibility("Old => Old", true, true);
+
+ return 0;
+}
diff --git a/Plugins/jingle/libjingle/talk/p2p/base/sessionclient.h b/Plugins/jingle/libjingle/talk/p2p/base/sessionclient.h
new file mode 100644
index 0000000..a92df1d
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/p2p/base/sessionclient.h
@@ -0,0 +1,71 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _CRICKET_P2P_BASE_SESSIONCLIENT_H_
+#define _CRICKET_P2P_BASE_SESSIONCLIENT_H_
+
+namespace buzz {
+class XmlElement;
+}
+
+namespace cricket {
+
+class Session;
+class SessionDescription;
+
+// A SessionClient exists in 1-1 relation with each session. The implementor
+// of this interface is the one that understands *what* the two sides are
+// trying to send to one another. The lower-level layers only know how to send
+// data; they do not know what is being sent.
+class SessionClient {
+ public:
+ // Notifies the client of the creation / destruction of sessions of this type.
+ //
+ // IMPORTANT: The SessionClient, in its handling of OnSessionCreate, must
+ // create whatever channels are indicate in the description. This is because
+ // the remote client may already be attempting to connect those channels. If
+ // we do not create our channel right away, then connection may fail or be
+ // delayed.
+ virtual void OnSessionCreate(Session* session, bool received_initiate) = 0;
+ virtual void OnSessionDestroy(Session* session) = 0;
+
+ // Provides functions to convert between the XML description of the session
+ // and the data structures useful to the client. The resulting objects are
+ // held by the Session for easy access.
+ virtual const SessionDescription* CreateSessionDescription(
+ const buzz::XmlElement* element) = 0;
+ virtual buzz::XmlElement* TranslateSessionDescription(
+ const SessionDescription* description) = 0;
+
+protected:
+ // The SessionClient interface explicitly does not include destructor
+ virtual ~SessionClient() { }
+};
+
+} // namespace cricket
+
+#endif // _CRICKET_P2P_BASE_SESSIONCLIENT_H_
diff --git a/Plugins/jingle/libjingle/talk/p2p/base/sessiondescription.h b/Plugins/jingle/libjingle/talk/p2p/base/sessiondescription.h
new file mode 100644
index 0000000..28b7084
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/p2p/base/sessiondescription.h
@@ -0,0 +1,42 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _SESSIONDESCRIPTION_H_
+#define _SESSIONDESCRIPTION_H_
+
+namespace cricket {
+
+// The client overrides this with whatever
+
+class SessionDescription {
+public:
+ virtual ~SessionDescription() {}
+};
+
+} // namespace cricket
+
+#endif // _SESSIONDESCRIPTION_H_
diff --git a/Plugins/jingle/libjingle/talk/p2p/base/sessionid.h b/Plugins/jingle/libjingle/talk/p2p/base/sessionid.h
new file mode 100644
index 0000000..a12535c
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/p2p/base/sessionid.h
@@ -0,0 +1,94 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _SESSIONID_H_
+#define _SESSIONID_H_
+
+#include "talk/base/basictypes.h"
+#include <string>
+#include <sstream>
+
+namespace cricket {
+
+// Each session is identified by a pair (from,id), where id is only
+// assumed to be unique to the machine identified by from.
+class SessionID {
+public:
+ SessionID() : id_str_("0") {
+ }
+ SessionID(const std::string& initiator, uint32 id)
+ : initiator_(initiator) {
+ set_id(id);
+ }
+ SessionID(const SessionID& sid)
+ : id_str_(sid.id_str_), initiator_(sid.initiator_) {
+ }
+
+ void set_id(uint32 id) {
+ std::stringstream st;
+ st << id;
+ st >> id_str_;
+ }
+ const std::string id_str() const {
+ return id_str_;
+ }
+ void set_id_str(const std::string &id_str) {
+ id_str_ = id_str;
+ }
+
+ const std::string &initiator() const {
+ return initiator_;
+ }
+ void set_initiator(const std::string &initiator) {
+ initiator_ = initiator;
+ }
+
+ bool operator <(const SessionID& sid) const {
+ int r = initiator_.compare(sid.initiator_);
+ if (r == 0)
+ r = id_str_.compare(sid.id_str_);
+ return r < 0;
+ }
+
+ bool operator ==(const SessionID& sid) const {
+ return (id_str_ == sid.id_str_) && (initiator_ == sid.initiator_);
+ }
+
+ SessionID& operator =(const SessionID& sid) {
+ id_str_ = sid.id_str_;
+ initiator_ = sid.initiator_;
+ return *this;
+ }
+
+private:
+ std::string id_str_;
+ std::string initiator_;
+};
+
+} // namespace cricket
+
+#endif // _SESSIONID_H_
diff --git a/Plugins/jingle/libjingle/talk/p2p/base/sessionmanager.cc b/Plugins/jingle/libjingle/talk/p2p/base/sessionmanager.cc
new file mode 100644
index 0000000..5d030bb
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/p2p/base/sessionmanager.cc
@@ -0,0 +1,336 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "talk/p2p/base/sessionmanager.h"
+#include "talk/base/common.h"
+#include "talk/base/helpers.h"
+#include "talk/p2p/base/constants.h"
+#include "talk/xmpp/constants.h"
+#include "talk/xmpp/jid.h"
+
+namespace cricket {
+
+SessionManager::SessionManager(PortAllocator *allocator,
+ talk_base::Thread *worker,
+ talk_base::Thread *signaling_thread) {
+ allocator_ = allocator;
+ if (signaling_thread == NULL) {
+ signaling_thread_ = talk_base::Thread::Current();
+ } else {
+ signaling_thread_ = signaling_thread;
+ }
+ if (worker == NULL) {
+ worker_thread_ = talk_base::Thread::Current();
+ } else {
+ worker_thread_ = worker;
+ }
+ timeout_ = 50;
+}
+
+SessionManager::~SessionManager() {
+ // Note: Session::Terminate occurs asynchronously, so it's too late to
+ // delete them now. They better be all gone.
+ ASSERT(session_map_.empty());
+ //TerminateAll();
+}
+
+void SessionManager::AddClient(const std::string& session_type,
+ SessionClient* client) {
+ ASSERT(client_map_.find(session_type) == client_map_.end());
+ client_map_[session_type] = client;
+}
+
+void SessionManager::RemoveClient(const std::string& session_type) {
+ ClientMap::iterator iter = client_map_.find(session_type);
+ ASSERT(iter != client_map_.end());
+ client_map_.erase(iter);
+}
+
+SessionClient* SessionManager::GetClient(const std::string& session_type) {
+ ClientMap::iterator iter = client_map_.find(session_type);
+ return (iter != client_map_.end()) ? iter->second : NULL;
+}
+
+Session *SessionManager::CreateSession(const std::string& name,
+ const std::string& session_type) {
+ return CreateSession(name, SessionID(name, CreateRandomId()), session_type,
+ false);
+}
+
+Session *SessionManager::CreateSession(
+ const std::string &name, const SessionID& id,
+ const std::string& session_type, bool received_initiate) {
+ SessionClient* client = GetClient(session_type);
+ ASSERT(client != NULL);
+
+ Session *session = new Session(this, name, id, session_type, client);
+ session_map_[session->id()] = session;
+ session->SignalRequestSignaling.connect(
+ this, &SessionManager::OnRequestSignaling);
+ session->SignalOutgoingMessage.connect(
+ this, &SessionManager::OnOutgoingMessage);
+ session->SignalErrorMessage.connect(this, &SessionManager::OnErrorMessage);
+ SignalSessionCreate(session, received_initiate);
+ session->client()->OnSessionCreate(session, received_initiate);
+ return session;
+}
+
+void SessionManager::DestroySession(Session *session) {
+ if (session != NULL) {
+ SessionMap::iterator it = session_map_.find(session->id());
+ if (it != session_map_.end()) {
+ SignalSessionDestroy(session);
+ session->client()->OnSessionDestroy(session);
+ session_map_.erase(it);
+ delete session;
+ }
+ }
+}
+
+Session *SessionManager::GetSession(const SessionID& id) {
+ SessionMap::iterator it = session_map_.find(id);
+ if (it != session_map_.end())
+ return it->second;
+ return NULL;
+}
+
+void SessionManager::TerminateAll() {
+ while (session_map_.begin() != session_map_.end()) {
+ Session *session = session_map_.begin()->second;
+ session->Terminate();
+ }
+}
+
+bool SessionManager::IsSessionMessage(const buzz::XmlElement* stanza) {
+ if (stanza->Name() != buzz::QN_IQ)
+ return false;
+ if (!stanza->HasAttr(buzz::QN_TYPE))
+ return false;
+ if (stanza->Attr(buzz::QN_TYPE) != buzz::STR_SET)
+ return false;
+
+ const buzz::XmlElement* session = stanza->FirstNamed(QN_SESSION);
+ if (!session)
+ return false;
+ if (!session->HasAttr(buzz::QN_TYPE))
+ return false;
+ if (!session->HasAttr(buzz::QN_ID) || !session->HasAttr(QN_INITIATOR))
+ return false;
+
+ return true;
+}
+
+Session* SessionManager::FindSessionForStanza(const buzz::XmlElement* stanza,
+ bool incoming) {
+ const buzz::XmlElement* session_xml = stanza->FirstNamed(QN_SESSION);
+ ASSERT(session_xml != NULL);
+
+ SessionID id;
+ id.set_id_str(session_xml->Attr(buzz::QN_ID));
+ id.set_initiator(session_xml->Attr(QN_INITIATOR));
+
+ // Pass this message to the session in question.
+ SessionMap::iterator iter = session_map_.find(id);
+ if (iter == session_map_.end())
+ return NULL;
+
+ Session* session = iter->second;
+
+ // match on "from"? or "to"?
+ buzz::QName attr = buzz::QN_TO;
+ if (incoming) {
+ attr = buzz::QN_FROM;
+ }
+ buzz::Jid remote(session->remote_name());
+ buzz::Jid match(stanza->Attr(attr));
+ if (remote == match) {
+ return session;
+ }
+ return NULL;
+}
+
+void SessionManager::OnIncomingMessage(const buzz::XmlElement* stanza) {
+ ASSERT(stanza->Attr(buzz::QN_TYPE) == buzz::STR_SET);
+
+ Session* session = FindSessionForStanza(stanza, true);
+ if (session) {
+ session->OnIncomingMessage(stanza);
+ return;
+ }
+
+ const buzz::XmlElement* session_xml = stanza->FirstNamed(QN_SESSION);
+ ASSERT(session_xml != NULL);
+ if (session_xml->Attr(buzz::QN_TYPE) == "initiate") {
+ std::string session_type = FindClient(session_xml);
+ if (session_type.size() == 0) {
+ SendErrorMessage(stanza, buzz::QN_STANZA_BAD_REQUEST, "modify",
+ "unknown session description type", NULL);
+ } else {
+ SessionID id;
+ id.set_id_str(session_xml->Attr(buzz::QN_ID));
+ id.set_initiator(session_xml->Attr(QN_INITIATOR));
+
+ session = CreateSession(stanza->Attr(buzz::QN_TO),
+ id,
+ session_type, true);
+ session->OnIncomingMessage(stanza);
+
+ // If we haven't rejected, and we haven't selected a transport yet,
+ // let's do it now.
+ if ((session->state() != Session::STATE_SENTREJECT) &&
+ (session->transport() == NULL)) {
+ session->ChooseTransport(stanza);
+ }
+ }
+ return;
+ }
+
+ SendErrorMessage(stanza, buzz::QN_STANZA_BAD_REQUEST, "modify",
+ "unknown session", NULL);
+}
+
+void SessionManager::OnIncomingResponse(const buzz::XmlElement* orig_stanza,
+ const buzz::XmlElement* response_stanza) {
+ // We don't do anything with the response now. If we need to we can forward
+ // it to the session.
+ return;
+}
+
+void SessionManager::OnFailedSend(const buzz::XmlElement* orig_stanza,
+ const buzz::XmlElement* error_stanza) {
+ Session* session = FindSessionForStanza(orig_stanza, false);
+ if (session) {
+ scoped_ptr<buzz::XmlElement> synthetic_error;
+ if (!error_stanza) {
+ // A failed send is semantically equivalent to an error response, so we
+ // can just turn the former into the latter.
+ synthetic_error.reset(
+ CreateErrorMessage(orig_stanza, buzz::QN_STANZA_ITEM_NOT_FOUND,
+ "cancel", "Recipient did not respond", NULL));
+ error_stanza = synthetic_error.get();
+ }
+
+ session->OnFailedSend(orig_stanza, error_stanza);
+ }
+}
+
+std::string SessionManager::FindClient(const buzz::XmlElement* session) {
+ for (const buzz::XmlElement* elem = session->FirstElement();
+ elem != NULL;
+ elem = elem->NextElement()) {
+ if (elem->Name().LocalPart() == "description") {
+ ClientMap::iterator iter = client_map_.find(elem->Name().Namespace());
+ if (iter != client_map_.end())
+ return iter->first;
+ }
+ }
+ return "";
+}
+
+void SessionManager::SendErrorMessage(const buzz::XmlElement* stanza,
+ const buzz::QName& name,
+ const std::string& type,
+ const std::string& text,
+ const buzz::XmlElement* extra_info) {
+ scoped_ptr<buzz::XmlElement> msg(
+ CreateErrorMessage(stanza, name, type, text, extra_info));
+ SignalOutgoingMessage(msg.get());
+}
+
+buzz::XmlElement* SessionManager::CreateErrorMessage(
+ const buzz::XmlElement* stanza,
+ const buzz::QName& name,
+ const std::string& type,
+ const std::string& text,
+ const buzz::XmlElement* extra_info) {
+ buzz::XmlElement* iq = new buzz::XmlElement(buzz::QN_IQ);
+ iq->SetAttr(buzz::QN_TO, stanza->Attr(buzz::QN_FROM));
+ iq->SetAttr(buzz::QN_ID, stanza->Attr(buzz::QN_ID));
+ iq->SetAttr(buzz::QN_TYPE, "error");
+
+ for (const buzz::XmlElement* elem = stanza->FirstElement();
+ elem != NULL;
+ elem = elem->NextElement()) {
+ iq->AddElement(new buzz::XmlElement(*elem));
+ }
+
+ buzz::XmlElement* error = new buzz::XmlElement(buzz::QN_ERROR);
+ error->SetAttr(buzz::QN_TYPE, type);
+ iq->AddElement(error);
+
+ // If the error name is not in the standard namespace, we have to first add
+ // some error from that namespace.
+ if (name.Namespace() != buzz::NS_STANZA) {
+ error->AddElement(
+ new buzz::XmlElement(buzz::QN_STANZA_UNDEFINED_CONDITION));
+ }
+ error->AddElement(new buzz::XmlElement(name));
+
+ if (extra_info)
+ error->AddElement(new buzz::XmlElement(*extra_info));
+
+ if (text.size() > 0) {
+ // It's okay to always use English here. This text is for debugging
+ // purposes only.
+ buzz::XmlElement* text_elem = new buzz::XmlElement(buzz::QN_STANZA_TEXT);
+ text_elem->SetAttr(buzz::QN_XML_LANG, "en");
+ text_elem->SetBodyText(text);
+ error->AddElement(text_elem);
+ }
+
+ // TODO: Should we include error codes as well for SIP compatibility?
+
+ return iq;
+}
+
+void SessionManager::OnOutgoingMessage(Session* session,
+ const buzz::XmlElement* stanza) {
+ SignalOutgoingMessage(stanza);
+}
+
+void SessionManager::OnErrorMessage(Session* session,
+ const buzz::XmlElement* stanza,
+ const buzz::QName& name,
+ const std::string& type,
+ const std::string& text,
+ const buzz::XmlElement* extra_info) {
+ SendErrorMessage(stanza, name, type, text, extra_info);
+}
+
+void SessionManager::OnSignalingReady() {
+ for (SessionMap::iterator it = session_map_.begin();
+ it != session_map_.end();
+ ++it) {
+ it->second->OnSignalingReady();
+ }
+}
+
+void SessionManager::OnRequestSignaling(Session* session) {
+ SignalRequestSignaling();
+}
+
+} // namespace cricket
diff --git a/Plugins/jingle/libjingle/talk/p2p/base/sessionmanager.h b/Plugins/jingle/libjingle/talk/p2p/base/sessionmanager.h
new file mode 100644
index 0000000..e0691cf
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/p2p/base/sessionmanager.h
@@ -0,0 +1,182 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _SESSIONMANAGER_H_
+#define _SESSIONMANAGER_H_
+
+#include "talk/base/thread.h"
+#include "talk/p2p/base/portallocator.h"
+#include "talk/p2p/base/session.h"
+#include "talk/p2p/base/sessionid.h"
+#include "talk/base/sigslot.h"
+
+#include <string>
+#include <utility>
+#include <map>
+
+namespace buzz {
+class QName;
+class XmlElement;
+}
+
+namespace cricket {
+
+class Session;
+class SessionClient;
+
+// SessionManager manages session instances
+
+class SessionManager : public sigslot::has_slots<> {
+ public:
+ SessionManager(PortAllocator *allocator,
+ talk_base::Thread *worker_thread = NULL,
+ talk_base::Thread *signaling_thread = NULL);
+ virtual ~SessionManager();
+
+ PortAllocator *port_allocator() const { return allocator_; }
+ talk_base::Thread *worker_thread() const { return worker_thread_; }
+ talk_base::Thread *signaling_thread() const { return signaling_thread_; }
+
+ int session_timeout() const { return timeout_; }
+ void set_session_timeout(int timeout) { timeout_ = timeout; }
+
+ // Registers support for the given client. If we receive an initiate
+ // describing a session of the given type, we will automatically create a
+ // Session object and notify this client. The client may then accept or
+ // reject the session.
+ void AddClient(const std::string& session_type, SessionClient* client);
+ void RemoveClient(const std::string& session_type);
+ SessionClient* GetClient(const std::string& session_type);
+
+ // Creates a new session. The given name is the JID of the client on whose
+ // behalf we initiate the session.
+ Session *CreateSession(const std::string& name,
+ const std::string& session_type);
+
+ // Destroys the given session.
+ void DestroySession(Session *session);
+
+ // Returns the session with the given ID or NULL if none exists.
+ Session *GetSession(const SessionID& id);
+
+ // Terminates all of the sessions created by this manager.
+ void TerminateAll();
+
+ // These are signaled whenever the set of existing sessions changes.
+ sigslot::signal2<Session *, bool> SignalSessionCreate;
+ sigslot::signal1<Session *> SignalSessionDestroy;
+
+ // Determines whether the given stanza is intended for some session.
+ bool IsSessionMessage(const buzz::XmlElement* stanza);
+
+ // Given a stanza, this find the Session associated with that stanza
+ Session* FindSessionForStanza(const buzz::XmlElement* stanza, bool incoming);
+
+ // Called when we receive a stanza for which IsSessionMessage is true.
+ void OnIncomingMessage(const buzz::XmlElement* stanza);
+
+ // Called when we get a response to a message that we sent.
+ void OnIncomingResponse(const buzz::XmlElement* orig_stanza,
+ const buzz::XmlElement* response_stanza);
+
+ // Called if an attempted to send times out or an error is returned. In the
+ // timeout case error_stanza will be NULL
+ void OnFailedSend(const buzz::XmlElement* orig_stanza,
+ const buzz::XmlElement* error_stanza);
+
+ // Signalled each time a session generates a signaling message to send.
+ sigslot::signal1<const buzz::XmlElement*> SignalOutgoingMessage;
+
+ // Signaled before sessions try to send certain signaling messages. The
+ // client should call OnSignalingReady once it is safe to send them. These
+ // steps are taken so that we don't send signaling messages trying to
+ // re-establish the connectivity of a session when the client cannot send
+ // the messages (and would probably just drop them on the floor).
+ //
+ // Note: you can connect this directly to OnSignalingReady(), if a signalling
+ // check is not supported.
+ sigslot::signal0<> SignalRequestSignaling;
+ void OnSignalingReady();
+
+ private:
+ typedef std::map<SessionID, Session *> SessionMap;
+ typedef std::map<std::string, SessionClient*> ClientMap;
+
+ PortAllocator *allocator_;
+ talk_base::Thread *signaling_thread_;
+ talk_base::Thread *worker_thread_;
+ int timeout_;
+ SessionMap session_map_;
+ ClientMap client_map_;
+
+ // Helper function for CreateSession. This is also invoked when we receive
+ // a message attempting to initiate a session with this client.
+ Session *CreateSession(const std::string& name,
+ const SessionID& id,
+ const std::string& session_type,
+ bool received_initiate);
+
+ // Attempts to find a registered session type whose description appears as
+ // a child of the session element. Such a child should be present indicating
+ // the application they hope to initiate.
+ std::string FindClient(const buzz::XmlElement* session);
+
+ // Sends a message back to the other client indicating that we found an error
+ // in the stanza they sent. name identifies the error, type is one of the
+ // standard XMPP types (cancel, continue, modify, auth, wait), and text is a
+ // description for debugging purposes.
+ void SendErrorMessage(const buzz::XmlElement* stanza,
+ const buzz::QName& name,
+ const std::string& type,
+ const std::string& text,
+ const buzz::XmlElement* extra_info);
+
+ // Creates and returns an error message from the given components. The
+ // caller is responsible for deleting this.
+ buzz::XmlElement* SessionManager::CreateErrorMessage(
+ const buzz::XmlElement* stanza,
+ const buzz::QName& name,
+ const std::string& type,
+ const std::string& text,
+ const buzz::XmlElement* extra_info);
+
+ // Called each time a session requests signaling.
+ void OnRequestSignaling(Session* session);
+
+ // Called each time a session has an outgoing message.
+ void OnOutgoingMessage(Session* session, const buzz::XmlElement* stanza);
+
+ // Called each time a session has an error to send.
+ void OnErrorMessage(Session* session, const buzz::XmlElement* stanza,
+ const buzz::QName& name, const std::string& type,
+ const std::string& text,
+ const buzz::XmlElement* extra_info);
+};
+
+} // namespace cricket
+
+#endif // _SESSIONMANAGER_H_
diff --git a/Plugins/jingle/libjingle/talk/p2p/base/stun.cc b/Plugins/jingle/libjingle/talk/p2p/base/stun.cc
new file mode 100644
index 0000000..c4a89e8
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/p2p/base/stun.cc
@@ -0,0 +1,578 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "talk/base/logging.h"
+#include "talk/p2p/base/stun.h"
+#include <iostream>
+#include <cassert>
+
+#if defined(_MSC_VER) && _MSC_VER < 1300
+namespace std {
+ using ::memcpy;
+}
+#endif
+
+using talk_base::ByteBuffer;
+
+namespace cricket {
+
+const std::string STUN_ERROR_REASON_BAD_REQUEST = "BAD REQUEST";
+const std::string STUN_ERROR_REASON_UNAUTHORIZED = "UNAUTHORIZED";
+const std::string STUN_ERROR_REASON_UNKNOWN_ATTRIBUTE = "UNKNOWN ATTRIBUTE";
+const std::string STUN_ERROR_REASON_STALE_CREDENTIALS = "STALE CREDENTIALS";
+const std::string STUN_ERROR_REASON_INTEGRITY_CHECK_FAILURE = "INTEGRITY CHECK FAILURE";
+const std::string STUN_ERROR_REASON_MISSING_USERNAME = "MISSING USERNAME";
+const std::string STUN_ERROR_REASON_USE_TLS = "USE TLS";
+const std::string STUN_ERROR_REASON_SERVER_ERROR = "SERVER ERROR";
+const std::string STUN_ERROR_REASON_GLOBAL_FAILURE = "GLOBAL FAILURE";
+
+StunMessage::StunMessage() : type_(0), length_(0),
+ transaction_id_("0000000000000000") {
+ assert(transaction_id_.size() == 16);
+ attrs_ = new std::vector<StunAttribute*>();
+}
+
+StunMessage::~StunMessage() {
+ for (unsigned i = 0; i < attrs_->size(); i++)
+ delete (*attrs_)[i];
+ delete attrs_;
+}
+
+void StunMessage::SetTransactionID(const std::string& str) {
+ assert(str.size() == 16);
+ transaction_id_ = str;
+}
+
+void StunMessage::AddAttribute(StunAttribute* attr) {
+ attrs_->push_back(attr);
+ length_ += attr->length() + 4;
+}
+
+const StunAddressAttribute*
+StunMessage::GetAddress(StunAttributeType type) const {
+ switch (type) {
+ case STUN_ATTR_MAPPED_ADDRESS:
+ case STUN_ATTR_RESPONSE_ADDRESS:
+ case STUN_ATTR_SOURCE_ADDRESS:
+ case STUN_ATTR_CHANGED_ADDRESS:
+ case STUN_ATTR_REFLECTED_FROM:
+ case STUN_ATTR_ALTERNATE_SERVER:
+ case STUN_ATTR_DESTINATION_ADDRESS:
+ case STUN_ATTR_SOURCE_ADDRESS2:
+ return reinterpret_cast<const StunAddressAttribute*>(GetAttribute(type));
+
+ default:
+ assert(0);
+ return 0;
+ }
+}
+
+const StunUInt32Attribute*
+StunMessage::GetUInt32(StunAttributeType type) const {
+ switch (type) {
+ case STUN_ATTR_CHANGE_REQUEST:
+ case STUN_ATTR_LIFETIME:
+ case STUN_ATTR_BANDWIDTH:
+ case STUN_ATTR_OPTIONS:
+ return reinterpret_cast<const StunUInt32Attribute*>(GetAttribute(type));
+
+ default:
+ assert(0);
+ return 0;
+ }
+}
+
+const StunByteStringAttribute*
+StunMessage::GetByteString(StunAttributeType type) const {
+ switch (type) {
+ case STUN_ATTR_USERNAME:
+ case STUN_ATTR_PASSWORD:
+ case STUN_ATTR_MESSAGE_INTEGRITY:
+ case STUN_ATTR_DATA:
+ case STUN_ATTR_MAGIC_COOKIE:
+ return reinterpret_cast<const StunByteStringAttribute*>(GetAttribute(type));
+
+ default:
+ assert(0);
+ return 0;
+ }
+}
+
+const StunErrorCodeAttribute* StunMessage::GetErrorCode() const {
+ return reinterpret_cast<const StunErrorCodeAttribute*>(
+ GetAttribute(STUN_ATTR_ERROR_CODE));
+}
+
+const StunUInt16ListAttribute* StunMessage::GetUnknownAttributes() const {
+ return reinterpret_cast<const StunUInt16ListAttribute*>(
+ GetAttribute(STUN_ATTR_UNKNOWN_ATTRIBUTES));
+}
+
+const StunTransportPrefsAttribute* StunMessage::GetTransportPrefs() const {
+ return reinterpret_cast<const StunTransportPrefsAttribute*>(
+ GetAttribute(STUN_ATTR_TRANSPORT_PREFERENCES));
+}
+
+const StunAttribute* StunMessage::GetAttribute(StunAttributeType type) const {
+ for (unsigned i = 0; i < attrs_->size(); i++) {
+ if ((*attrs_)[i]->type() == type)
+ return (*attrs_)[i];
+ }
+ return 0;
+}
+
+bool StunMessage::Read(ByteBuffer* buf) {
+ if (!buf->ReadUInt16(type_))
+ return false;
+
+ if (!buf->ReadUInt16(length_))
+ return false;
+
+ std::string transaction_id;
+ if (!buf->ReadString(transaction_id, 16))
+ return false;
+ assert(transaction_id.size() == 16);
+ transaction_id_ = transaction_id;
+
+ if (length_ > buf->Length())
+ return false;
+
+ attrs_->resize(0);
+
+ size_t rest = buf->Length() - length_;
+ while (buf->Length() > rest) {
+ uint16 attr_type, attr_length;
+ if (!buf->ReadUInt16(attr_type))
+ return false;
+ if (!buf->ReadUInt16(attr_length))
+ return false;
+
+ StunAttribute* attr = StunAttribute::Create(attr_type, attr_length);
+ if (!attr || !attr->Read(buf))
+ return false;
+
+ attrs_->push_back(attr);
+ }
+
+ if (buf->Length() != rest) {
+ // fixme: shouldn't be doing this
+ LOG(LERROR) << "wrong message length"
+ << " (" << (int)rest << " != " << (int)buf->Length() << ")";
+ return false;
+ }
+
+ return true;
+}
+
+void StunMessage::Write(ByteBuffer* buf) const {
+ buf->WriteUInt16(type_);
+ buf->WriteUInt16(length_);
+ buf->WriteString(transaction_id_);
+
+ for (unsigned i = 0; i < attrs_->size(); i++) {
+ buf->WriteUInt16((*attrs_)[i]->type());
+ buf->WriteUInt16((*attrs_)[i]->length());
+ (*attrs_)[i]->Write(buf);
+ }
+}
+
+StunAttribute::StunAttribute(uint16 type, uint16 length)
+ : type_(type), length_(length) {
+}
+
+StunAttribute* StunAttribute::Create(uint16 type, uint16 length) {
+ switch (type) {
+ case STUN_ATTR_MAPPED_ADDRESS:
+ case STUN_ATTR_RESPONSE_ADDRESS:
+ case STUN_ATTR_SOURCE_ADDRESS:
+ case STUN_ATTR_CHANGED_ADDRESS:
+ case STUN_ATTR_REFLECTED_FROM:
+ case STUN_ATTR_ALTERNATE_SERVER:
+ case STUN_ATTR_DESTINATION_ADDRESS:
+ case STUN_ATTR_SOURCE_ADDRESS2:
+ if (length != StunAddressAttribute::SIZE)
+ return 0;
+ return new StunAddressAttribute(type);
+
+ case STUN_ATTR_CHANGE_REQUEST:
+ case STUN_ATTR_LIFETIME:
+ case STUN_ATTR_BANDWIDTH:
+ case STUN_ATTR_OPTIONS:
+ if (length != StunUInt32Attribute::SIZE)
+ return 0;
+ return new StunUInt32Attribute(type);
+
+ case STUN_ATTR_USERNAME:
+ case STUN_ATTR_PASSWORD:
+ case STUN_ATTR_MAGIC_COOKIE:
+ return (length % 4 == 0) ? new StunByteStringAttribute(type, length) : 0;
+
+ case STUN_ATTR_MESSAGE_INTEGRITY:
+ return (length == 20) ? new StunByteStringAttribute(type, length) : 0;
+
+ case STUN_ATTR_DATA:
+ return new StunByteStringAttribute(type, length);
+
+ case STUN_ATTR_ERROR_CODE:
+ if (length < StunErrorCodeAttribute::MIN_SIZE)
+ return 0;
+ return new StunErrorCodeAttribute(type, length);
+
+ case STUN_ATTR_UNKNOWN_ATTRIBUTES:
+ return (length % 2 == 0) ? new StunUInt16ListAttribute(type, length) : 0;
+
+ case STUN_ATTR_TRANSPORT_PREFERENCES:
+ if ((length != StunTransportPrefsAttribute::SIZE1) &&
+ (length != StunTransportPrefsAttribute::SIZE2))
+ return 0;
+ return new StunTransportPrefsAttribute(type, length);
+
+ default:
+ return 0;
+ }
+}
+
+StunAddressAttribute* StunAttribute::CreateAddress(uint16 type) {
+ switch (type) {
+ case STUN_ATTR_MAPPED_ADDRESS:
+ case STUN_ATTR_RESPONSE_ADDRESS:
+ case STUN_ATTR_SOURCE_ADDRESS:
+ case STUN_ATTR_CHANGED_ADDRESS:
+ case STUN_ATTR_REFLECTED_FROM:
+ case STUN_ATTR_ALTERNATE_SERVER:
+ case STUN_ATTR_DESTINATION_ADDRESS:
+ case STUN_ATTR_SOURCE_ADDRESS2:
+ return new StunAddressAttribute(type);
+
+ default:
+ assert(false);
+ return 0;
+ }
+}
+
+StunUInt32Attribute* StunAttribute::CreateUInt32(uint16 type) {
+ switch (type) {
+ case STUN_ATTR_CHANGE_REQUEST:
+ case STUN_ATTR_LIFETIME:
+ case STUN_ATTR_BANDWIDTH:
+ case STUN_ATTR_OPTIONS:
+ return new StunUInt32Attribute(type);
+
+ default:
+ assert(false);
+ return 0;
+ }
+}
+
+StunByteStringAttribute* StunAttribute::CreateByteString(uint16 type) {
+ switch (type) {
+ case STUN_ATTR_USERNAME:
+ case STUN_ATTR_PASSWORD:
+ case STUN_ATTR_MESSAGE_INTEGRITY:
+ case STUN_ATTR_DATA:
+ case STUN_ATTR_MAGIC_COOKIE:
+ return new StunByteStringAttribute(type, 0);
+
+ default:
+ assert(false);
+ return 0;
+ }
+}
+
+StunErrorCodeAttribute* StunAttribute::CreateErrorCode() {
+ return new StunErrorCodeAttribute(
+ STUN_ATTR_ERROR_CODE, StunErrorCodeAttribute::MIN_SIZE);
+}
+
+StunUInt16ListAttribute* StunAttribute::CreateUnknownAttributes() {
+ return new StunUInt16ListAttribute(STUN_ATTR_UNKNOWN_ATTRIBUTES, 0);
+}
+
+StunTransportPrefsAttribute* StunAttribute::CreateTransportPrefs() {
+ return new StunTransportPrefsAttribute(
+ STUN_ATTR_TRANSPORT_PREFERENCES, StunTransportPrefsAttribute::SIZE1);
+}
+
+StunAddressAttribute::StunAddressAttribute(uint16 type)
+ : StunAttribute(type, SIZE), family_(0), port_(0), ip_(0) {
+}
+
+bool StunAddressAttribute::Read(ByteBuffer* buf) {
+ uint8 dummy;
+ if (!buf->ReadUInt8(dummy))
+ return false;
+ if (!buf->ReadUInt8(family_))
+ return false;
+ if (!buf->ReadUInt16(port_))
+ return false;
+ if (!buf->ReadUInt32(ip_))
+ return false;
+ return true;
+}
+
+void StunAddressAttribute::Write(ByteBuffer* buf) const {
+ buf->WriteUInt8(0);
+ buf->WriteUInt8(family_);
+ buf->WriteUInt16(port_);
+ buf->WriteUInt32(ip_);
+}
+
+StunUInt32Attribute::StunUInt32Attribute(uint16 type)
+ : StunAttribute(type, SIZE), bits_(0) {
+}
+
+bool StunUInt32Attribute::GetBit(int index) const {
+ assert((0 <= index) && (index < 32));
+ return static_cast<bool>((bits_ >> index) & 0x1);
+}
+
+void StunUInt32Attribute::SetBit(int index, bool value) {
+ assert((0 <= index) && (index < 32));
+ bits_ &= ~(1 << index);
+ bits_ |= value ? (1 << index) : 0;
+}
+
+bool StunUInt32Attribute::Read(ByteBuffer* buf) {
+ if (!buf->ReadUInt32(bits_))
+ return false;
+ return true;
+}
+
+void StunUInt32Attribute::Write(ByteBuffer* buf) const {
+ buf->WriteUInt32(bits_);
+}
+
+StunByteStringAttribute::StunByteStringAttribute(uint16 type, uint16 length)
+ : StunAttribute(type, length), bytes_(0) {
+}
+
+StunByteStringAttribute::~StunByteStringAttribute() {
+ delete [] bytes_;
+}
+
+void StunByteStringAttribute::SetBytes(char* bytes, uint16 length) {
+ delete [] bytes_;
+ bytes_ = bytes;
+ SetLength(length);
+}
+
+void StunByteStringAttribute::CopyBytes(const char* bytes) {
+ CopyBytes(bytes, (uint16)strlen(bytes));
+}
+
+void StunByteStringAttribute::CopyBytes(const void* bytes, uint16 length) {
+ char* new_bytes = new char[length];
+ std::memcpy(new_bytes, bytes, length);
+ SetBytes(new_bytes, length);
+}
+
+uint8 StunByteStringAttribute::GetByte(int index) const {
+ assert(bytes_);
+ assert((0 <= index) && (index < length()));
+ return static_cast<uint8>(bytes_[index]);
+}
+
+void StunByteStringAttribute::SetByte(int index, uint8 value) {
+ assert(bytes_);
+ assert((0 <= index) && (index < length()));
+ bytes_[index] = value;
+}
+
+bool StunByteStringAttribute::Read(ByteBuffer* buf) {
+ bytes_ = new char[length()];
+ if (!buf->ReadBytes(bytes_, length()))
+ return false;
+ return true;
+}
+
+void StunByteStringAttribute::Write(ByteBuffer* buf) const {
+ buf->WriteBytes(bytes_, length());
+}
+
+StunErrorCodeAttribute::StunErrorCodeAttribute(uint16 type, uint16 length)
+ : StunAttribute(type, length), class_(0), number_(0) {
+}
+
+StunErrorCodeAttribute::~StunErrorCodeAttribute() {
+}
+
+void StunErrorCodeAttribute::SetErrorCode(uint32 code) {
+ class_ = (uint8)((code >> 8) & 0x7);
+ number_ = (uint8)(code & 0xff);
+}
+
+void StunErrorCodeAttribute::SetReason(const std::string& reason) {
+ SetLength(MIN_SIZE + (uint16)reason.size());
+ reason_ = reason;
+}
+
+bool StunErrorCodeAttribute::Read(ByteBuffer* buf) {
+ uint32 val;
+ if (!buf->ReadUInt32(val))
+ return false;
+
+ if ((val >> 11) != 0)
+ LOG(LERROR) << "error-code bits not zero";
+
+ SetErrorCode(val);
+
+ if (!buf->ReadString(reason_, length() - 4))
+ return false;
+
+ return true;
+}
+
+void StunErrorCodeAttribute::Write(ByteBuffer* buf) const {
+ buf->WriteUInt32(error_code());
+ buf->WriteString(reason_);
+}
+
+StunUInt16ListAttribute::StunUInt16ListAttribute(uint16 type, uint16 length)
+ : StunAttribute(type, length) {
+ attr_types_ = new std::vector<uint16>();
+}
+
+StunUInt16ListAttribute::~StunUInt16ListAttribute() {
+ delete attr_types_;
+}
+
+size_t StunUInt16ListAttribute::Size() const {
+ return attr_types_->size();
+}
+
+uint16 StunUInt16ListAttribute::GetType(int index) const {
+ return (*attr_types_)[index];
+}
+
+void StunUInt16ListAttribute::SetType(int index, uint16 value) {
+ (*attr_types_)[index] = value;
+}
+
+void StunUInt16ListAttribute::AddType(uint16 value) {
+ attr_types_->push_back(value);
+ SetLength((uint16)attr_types_->size() * 2);
+}
+
+bool StunUInt16ListAttribute::Read(ByteBuffer* buf) {
+ for (int i = 0; i < length() / 2; i++) {
+ uint16 attr;
+ if (!buf->ReadUInt16(attr))
+ return false;
+ attr_types_->push_back(attr);
+ }
+ return true;
+}
+
+void StunUInt16ListAttribute::Write(ByteBuffer* buf) const {
+ for (unsigned i = 0; i < attr_types_->size(); i++)
+ buf->WriteUInt16((*attr_types_)[i]);
+}
+
+StunTransportPrefsAttribute::StunTransportPrefsAttribute(
+ uint16 type, uint16 length)
+ : StunAttribute(type, length), preallocate_(false), prefs_(0), addr_(0) {
+}
+
+StunTransportPrefsAttribute::~StunTransportPrefsAttribute() {
+ delete addr_;
+}
+
+void StunTransportPrefsAttribute::SetPreallocateAddress(
+ StunAddressAttribute* addr) {
+ if (!addr) {
+ preallocate_ = false;
+ addr_ = 0;
+ SetLength(SIZE1);
+ } else {
+ preallocate_ = true;
+ addr_ = addr;
+ SetLength(SIZE2);
+ }
+}
+
+bool StunTransportPrefsAttribute::Read(ByteBuffer* buf) {
+ uint32 val;
+ if (!buf->ReadUInt32(val))
+ return false;
+
+ if ((val >> 3) != 0)
+ LOG(LERROR) << "transport-preferences bits not zero";
+
+ preallocate_ = static_cast<bool>((val >> 2) & 0x1);
+ prefs_ = (uint8)(val & 0x3);
+
+ if (preallocate_ && (prefs_ == 3))
+ LOG(LERROR) << "transport-preferences imcompatible P and Typ";
+
+ if (!preallocate_) {
+ if (length() != StunUInt32Attribute::SIZE)
+ return false;
+ } else {
+ if (length() != StunUInt32Attribute::SIZE + StunAddressAttribute::SIZE)
+ return false;
+
+ addr_ = new StunAddressAttribute(STUN_ATTR_SOURCE_ADDRESS);
+ addr_->Read(buf);
+ }
+
+ return true;
+}
+
+void StunTransportPrefsAttribute::Write(ByteBuffer* buf) const {
+ buf->WriteUInt32((preallocate_ ? 4 : 0) | prefs_);
+
+ if (preallocate_)
+ addr_->Write(buf);
+}
+
+StunMessageType GetStunResponseType(StunMessageType request_type) {
+ switch (request_type) {
+ case STUN_SHARED_SECRET_REQUEST:
+ return STUN_SHARED_SECRET_RESPONSE;
+ case STUN_ALLOCATE_REQUEST:
+ return STUN_ALLOCATE_RESPONSE;
+ case STUN_SEND_REQUEST:
+ return STUN_SEND_RESPONSE;
+ default:
+ return STUN_BINDING_RESPONSE;
+ }
+}
+
+StunMessageType GetStunErrorResponseType(StunMessageType request_type) {
+ switch (request_type) {
+ case STUN_SHARED_SECRET_REQUEST:
+ return STUN_SHARED_SECRET_ERROR_RESPONSE;
+ case STUN_ALLOCATE_REQUEST:
+ return STUN_ALLOCATE_ERROR_RESPONSE;
+ case STUN_SEND_REQUEST:
+ return STUN_SEND_ERROR_RESPONSE;
+ default:
+ return STUN_BINDING_ERROR_RESPONSE;
+ }
+}
+
+} // namespace cricket
diff --git a/Plugins/jingle/libjingle/talk/p2p/base/stun.h b/Plugins/jingle/libjingle/talk/p2p/base/stun.h
new file mode 100644
index 0000000..4c0459b
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/p2p/base/stun.h
@@ -0,0 +1,364 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __STUN_H__
+#define __STUN_H__
+
+// This file contains classes for dealing with the STUN and TURN protocols.
+// Both protocols use the same wire format.
+
+#include "talk/base/basictypes.h"
+#include "talk/base/bytebuffer.h"
+#include <string>
+#include <vector>
+
+namespace cricket {
+
+// These are the types of STUN & TURN messages as of last check.
+enum StunMessageType {
+ STUN_BINDING_REQUEST = 0x0001,
+ STUN_BINDING_RESPONSE = 0x0101,
+ STUN_BINDING_ERROR_RESPONSE = 0x0111,
+ STUN_SHARED_SECRET_REQUEST = 0x0002,
+ STUN_SHARED_SECRET_RESPONSE = 0x0102,
+ STUN_SHARED_SECRET_ERROR_RESPONSE = 0x0112,
+ STUN_ALLOCATE_REQUEST = 0x0003,
+ STUN_ALLOCATE_RESPONSE = 0x0103,
+ STUN_ALLOCATE_ERROR_RESPONSE = 0x0113,
+ STUN_SEND_REQUEST = 0x0004,
+ STUN_SEND_RESPONSE = 0x0104,
+ STUN_SEND_ERROR_RESPONSE = 0x0114,
+ STUN_DATA_INDICATION = 0x0115
+};
+
+// These are the types of attributes defined in STUN & TURN. Next to each is
+// the name of the class (T is StunTAttribute) that implements that type.
+enum StunAttributeType {
+ STUN_ATTR_MAPPED_ADDRESS = 0x0001, // Address
+ STUN_ATTR_RESPONSE_ADDRESS = 0x0002, // Address
+ STUN_ATTR_CHANGE_REQUEST = 0x0003, // UInt32
+ STUN_ATTR_SOURCE_ADDRESS = 0x0004, // Address
+ STUN_ATTR_CHANGED_ADDRESS = 0x0005, // Address
+ STUN_ATTR_USERNAME = 0x0006, // ByteString, multiple of 4 bytes
+ STUN_ATTR_PASSWORD = 0x0007, // ByteString, multiple of 4 bytes
+ STUN_ATTR_MESSAGE_INTEGRITY = 0x0008, // ByteString, 20 bytes
+ STUN_ATTR_ERROR_CODE = 0x0009, // ErrorCode
+ STUN_ATTR_UNKNOWN_ATTRIBUTES = 0x000a, // UInt16List
+ STUN_ATTR_REFLECTED_FROM = 0x000b, // Address
+ STUN_ATTR_TRANSPORT_PREFERENCES = 0x000c, // TransportPrefs
+ STUN_ATTR_LIFETIME = 0x000d, // UInt32
+ STUN_ATTR_ALTERNATE_SERVER = 0x000e, // Address
+ STUN_ATTR_MAGIC_COOKIE = 0x000f, // ByteString, 4 bytes
+ STUN_ATTR_BANDWIDTH = 0x0010, // UInt32
+ STUN_ATTR_DESTINATION_ADDRESS = 0x0011, // Address
+ STUN_ATTR_SOURCE_ADDRESS2 = 0x0012, // Address
+ STUN_ATTR_DATA = 0x0013, // ByteString
+ STUN_ATTR_OPTIONS = 0x8001 // UInt32
+};
+
+enum StunErrorCodes {
+ STUN_ERROR_BAD_REQUEST = 400,
+ STUN_ERROR_UNAUTHORIZED = 401,
+ STUN_ERROR_UNKNOWN_ATTRIBUTE = 420,
+ STUN_ERROR_STALE_CREDENTIALS = 430,
+ STUN_ERROR_INTEGRITY_CHECK_FAILURE = 431,
+ STUN_ERROR_MISSING_USERNAME = 432,
+ STUN_ERROR_USE_TLS = 433,
+ STUN_ERROR_SERVER_ERROR = 500,
+ STUN_ERROR_GLOBAL_FAILURE = 600
+};
+
+extern const std::string STUN_ERROR_REASON_BAD_REQUEST;
+extern const std::string STUN_ERROR_REASON_UNAUTHORIZED;
+extern const std::string STUN_ERROR_REASON_UNKNOWN_ATTRIBUTE;
+extern const std::string STUN_ERROR_REASON_STALE_CREDENTIALS;
+extern const std::string STUN_ERROR_REASON_INTEGRITY_CHECK_FAILURE;
+extern const std::string STUN_ERROR_REASON_MISSING_USERNAME;
+extern const std::string STUN_ERROR_REASON_USE_TLS;
+extern const std::string STUN_ERROR_REASON_SERVER_ERROR;
+extern const std::string STUN_ERROR_REASON_GLOBAL_FAILURE;
+
+class StunAttribute;
+class StunAddressAttribute;
+class StunUInt32Attribute;
+class StunByteStringAttribute;
+class StunErrorCodeAttribute;
+class StunUInt16ListAttribute;
+class StunTransportPrefsAttribute;
+
+// Records a complete STUN/TURN message. Each message consists of a type and
+// any number of attributes. Each attribute is parsed into an instance of an
+// appropriate class (see above). The Get* methods will return instances of
+// that attribute class.
+class StunMessage {
+public:
+ StunMessage();
+ ~StunMessage();
+
+ StunMessageType type() const { return static_cast<StunMessageType>(type_); }
+ uint16 length() const { return length_; }
+ const std::string& transaction_id() const { return transaction_id_; }
+
+ void SetType(StunMessageType type) { type_ = type; }
+ void SetTransactionID(const std::string& str);
+
+ const StunAddressAttribute* GetAddress(StunAttributeType type) const;
+ const StunUInt32Attribute* GetUInt32(StunAttributeType type) const;
+ const StunByteStringAttribute* GetByteString(StunAttributeType type) const;
+ const StunErrorCodeAttribute* GetErrorCode() const;
+ const StunUInt16ListAttribute* GetUnknownAttributes() const;
+ const StunTransportPrefsAttribute* GetTransportPrefs() const;
+
+ void AddAttribute(StunAttribute* attr);
+
+ // Parses the STUN/TURN packet in the given buffer and records it here. The
+ // return value indicates whether this was successful.
+ bool Read(talk_base::ByteBuffer* buf);
+
+ // Writes this object into a STUN/TURN packet. Return value is true if
+ // successful.
+ void Write(talk_base::ByteBuffer* buf) const;
+
+private:
+ uint16 type_;
+ uint16 length_;
+ std::string transaction_id_;
+ std::vector<StunAttribute*>* attrs_;
+
+ const StunAttribute* GetAttribute(StunAttributeType type) const;
+};
+
+// Base class for all STUN/TURN attributes.
+class StunAttribute {
+public:
+ virtual ~StunAttribute() {}
+
+ StunAttributeType type() const {
+ return static_cast<StunAttributeType>(type_);
+ }
+ uint16 length() const { return length_; }
+
+ // Reads the body (not the type or length) for this type of attribute from
+ // the given buffer. Return value is true if successful.
+ virtual bool Read(talk_base::ByteBuffer* buf) = 0;
+
+ // Writes the body (not the type or length) to the given buffer. Return
+ // value is true if successful.
+ virtual void Write(talk_base::ByteBuffer* buf) const = 0;
+
+ // Creates an attribute object with the given type and len.
+ static StunAttribute* Create(uint16 type, uint16 length);
+
+ // Creates an attribute object with the given type and smallest length.
+ static StunAddressAttribute* CreateAddress(uint16 type);
+ static StunUInt32Attribute* CreateUInt32(uint16 type);
+ static StunByteStringAttribute* CreateByteString(uint16 type);
+ static StunErrorCodeAttribute* CreateErrorCode();
+ static StunUInt16ListAttribute* CreateUnknownAttributes();
+ static StunTransportPrefsAttribute* CreateTransportPrefs();
+
+protected:
+ StunAttribute(uint16 type, uint16 length);
+
+ void SetLength(uint16 length) { length_ = length; }
+
+private:
+ uint16 type_;
+ uint16 length_;
+};
+
+// Implements STUN/TURN attributes that record an Internet address.
+class StunAddressAttribute : public StunAttribute {
+public:
+ StunAddressAttribute(uint16 type);
+
+#if (_MSC_VER < 1300)
+ enum { SIZE = 8 };
+#else
+ static const uint16 SIZE = 8;
+#endif
+
+ uint8 family() const { return family_; }
+ uint16 port() const { return port_; }
+ uint32 ip() const { return ip_; }
+
+ void SetFamily(uint8 family) { family_ = family; }
+ void SetIP(uint32 ip) { ip_ = ip; }
+ void SetPort(uint16 port) { port_ = port; }
+
+ bool Read(talk_base::ByteBuffer* buf);
+ void Write(talk_base::ByteBuffer* buf) const;
+
+private:
+ uint8 family_;
+ uint16 port_;
+ uint32 ip_;
+};
+
+// Implements STUN/TURN attributs that record a 32-bit integer.
+class StunUInt32Attribute : public StunAttribute {
+public:
+ StunUInt32Attribute(uint16 type);
+
+#if (_MSC_VER < 1300)
+ enum { SIZE = 4 };
+#else
+ static const uint16 SIZE = 4;
+#endif
+
+ uint32 value() const { return bits_; }
+
+ void SetValue(uint32 bits) { bits_ = bits; }
+
+ bool GetBit(int index) const;
+ void SetBit(int index, bool value);
+
+ bool Read(talk_base::ByteBuffer* buf);
+ void Write(talk_base::ByteBuffer* buf) const;
+
+private:
+ uint32 bits_;
+};
+
+// Implements STUN/TURN attributs that record an arbitrary byte string
+class StunByteStringAttribute : public StunAttribute {
+public:
+ StunByteStringAttribute(uint16 type, uint16 length);
+ ~StunByteStringAttribute();
+
+ const char* bytes() const { return bytes_; }
+
+ void SetBytes(char* bytes, uint16 length);
+
+ void CopyBytes(const char* bytes); // uses strlen
+ void CopyBytes(const void* bytes, uint16 length);
+
+ uint8 GetByte(int index) const;
+ void SetByte(int index, uint8 value);
+
+ bool Read(talk_base::ByteBuffer* buf);
+ void Write(talk_base::ByteBuffer* buf) const;
+
+private:
+ char* bytes_;
+};
+
+// Implements STUN/TURN attributs that record an error code.
+class StunErrorCodeAttribute : public StunAttribute {
+public:
+ StunErrorCodeAttribute(uint16 type, uint16 length);
+ ~StunErrorCodeAttribute();
+
+#if (_MSC_VER < 1300)
+ enum { MIN_SIZE = 4 };
+#else
+ static const uint16 MIN_SIZE = 4;
+#endif
+
+ uint32 error_code() const { return (class_ << 8) | number_; }
+ uint8 error_class() const { return class_; }
+ uint8 number() const { return number_; }
+ const std::string& reason() const { return reason_; }
+
+ void SetErrorCode(uint32 code);
+ void SetErrorClass(uint8 eclass) { class_ = eclass; }
+ void SetNumber(uint8 number) { number_ = number; }
+ void SetReason(const std::string& reason);
+
+ bool Read(talk_base::ByteBuffer* buf);
+ void Write(talk_base::ByteBuffer* buf) const;
+
+private:
+ uint8 class_;
+ uint8 number_;
+ std::string reason_;
+};
+
+// Implements STUN/TURN attributs that record a list of attribute names.
+class StunUInt16ListAttribute : public StunAttribute {
+public:
+ StunUInt16ListAttribute(uint16 type, uint16 length);
+ ~StunUInt16ListAttribute();
+
+ size_t Size() const;
+ uint16 GetType(int index) const;
+ void SetType(int index, uint16 value);
+ void AddType(uint16 value);
+
+ bool Read(talk_base::ByteBuffer* buf);
+ void Write(talk_base::ByteBuffer* buf) const;
+
+private:
+ std::vector<uint16>* attr_types_;
+};
+
+// Implements the TURN TRANSPORT-PREFS attribute, which provides information
+// about the ports to allocate.
+class StunTransportPrefsAttribute : public StunAttribute {
+public:
+ StunTransportPrefsAttribute(uint16 type, uint16 length);
+ ~StunTransportPrefsAttribute();
+
+#if (_MSC_VER < 1300)
+ enum { SIZE1 = 4, SIZE2 = 12 };
+#else
+ static const uint16 SIZE1 = 4;
+ static const uint16 SIZE2 = 12;
+#endif
+
+ bool preallocate() const { return preallocate_; }
+ uint8 preference_type() const { return prefs_; }
+ const StunAddressAttribute* address() const { return addr_; }
+
+ void SetPreferenceType(uint8 prefs) { prefs_ = prefs; }
+
+ // Sets the preallocate address to the given value, or if 0 is given, it sets
+ // to not preallocate.
+ void SetPreallocateAddress(StunAddressAttribute* addr);
+
+ bool Read(talk_base::ByteBuffer* buf);
+ void Write(talk_base::ByteBuffer* buf) const;
+
+private:
+ bool preallocate_;
+ uint8 prefs_;
+ StunAddressAttribute* addr_;
+};
+
+// The special MAGIC-COOKIE attribute is used to distinguish TURN packets from
+// other kinds of traffic.
+const char STUN_MAGIC_COOKIE_VALUE[] = { 0x72, char(0xc6), 0x4b, char(0xc6) };
+
+// Returns the (successful) response type for the given request type.
+StunMessageType GetStunResponseType(StunMessageType request_type);
+
+// Returns the error response type for the given request type.
+StunMessageType GetStunErrorResponseType(StunMessageType request_type);
+
+} // namespace cricket
+
+#endif // __STUN_H__
diff --git a/Plugins/jingle/libjingle/talk/p2p/base/stunport.cc b/Plugins/jingle/libjingle/talk/p2p/base/stunport.cc
new file mode 100644
index 0000000..8fa3fd8
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/p2p/base/stunport.cc
@@ -0,0 +1,204 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#if defined(_MSC_VER) && _MSC_VER < 1300
+#pragma warning(disable:4786)
+#endif
+#include <iostream>
+#include <cassert>
+#include "talk/base/common.h"
+#include "talk/base/logging.h"
+#include "talk/base/helpers.h"
+#include "talk/p2p/base/stunport.h"
+
+#if defined(_MSC_VER) && _MSC_VER < 1300
+namespace std {
+ using ::strerror;
+}
+#endif
+
+#ifdef POSIX
+#include <errno.h>
+#endif // POSIX
+
+namespace cricket {
+
+const int KEEPALIVE_DELAY = 10 * 1000; // 10 seconds - sort timeouts
+const int RETRY_DELAY = 50; // 50ms, from ICE spec
+const uint32 RETRY_TIMEOUT = 50 * 1000; // ICE says 50 secs
+
+// Handles a binding request sent to the STUN server.
+class StunPortBindingRequest : public StunRequest {
+public:
+ StunPortBindingRequest(StunPort* port, bool keep_alive,
+ const talk_base::SocketAddress& addr)
+ : port_(port), keep_alive_(keep_alive), server_addr_(addr) {
+ start_time_ = talk_base::GetMillisecondCount();
+ }
+
+ virtual ~StunPortBindingRequest() {
+ }
+
+ const talk_base::SocketAddress& server_addr() const { return server_addr_; }
+
+ virtual void Prepare(StunMessage* request) {
+ request->SetType(STUN_BINDING_REQUEST);
+ }
+
+ virtual void OnResponse(StunMessage* response) {
+ const StunAddressAttribute* addr_attr =
+ response->GetAddress(STUN_ATTR_MAPPED_ADDRESS);
+ if (!addr_attr) {
+ LOG(LERROR) << "Binding response missing mapped address.";
+ } else if (addr_attr->family() != 1) {
+ LOG(LERROR) << "Binding address has bad family";
+ } else {
+ talk_base::SocketAddress addr(addr_attr->ip(), addr_attr->port());
+ port_->AddAddress(addr, "udp", true);
+ }
+
+ // We will do a keep-alive regardless of whether this request suceeds.
+ // This should have almost no impact on network usage.
+ if (keep_alive_) {
+ port_->requests_.SendDelayed(
+ new StunPortBindingRequest(port_, true, server_addr_),
+ KEEPALIVE_DELAY);
+ }
+ }
+
+ virtual void OnErrorResponse(StunMessage* response) {
+ const StunErrorCodeAttribute* attr = response->GetErrorCode();
+ if (!attr) {
+ LOG(LERROR) << "Bad allocate response error code";
+ } else {
+ LOG(LERROR) << "Binding error response:"
+ << " class=" << attr->error_class()
+ << " number=" << attr->number()
+ << " reason='" << attr->reason() << "'";
+ }
+
+ port_->SignalAddressError(port_);
+
+ if (keep_alive_
+ && (talk_base::GetMillisecondCount() - start_time_ <= RETRY_TIMEOUT)) {
+ port_->requests_.SendDelayed(
+ new StunPortBindingRequest(port_, true, server_addr_),
+ KEEPALIVE_DELAY);
+ }
+ }
+
+ virtual void OnTimeout() {
+ LOG(LERROR) << "Binding request timed out from "
+ << port_->GetLocalAddress().ToString()
+ << " (" << port_->network()->name() << ")";
+
+ port_->SignalAddressError(port_);
+
+ if (keep_alive_
+ && (talk_base::GetMillisecondCount() - start_time_ <= RETRY_TIMEOUT)) {
+ port_->requests_.SendDelayed(
+ new StunPortBindingRequest(port_, true, server_addr_),
+ RETRY_DELAY);
+ }
+ }
+
+private:
+ StunPort* port_;
+ bool keep_alive_;
+ talk_base::SocketAddress server_addr_;
+ uint32 start_time_;
+};
+
+const std::string STUN_PORT_TYPE("stun");
+
+StunPort::StunPort(talk_base::Thread* thread, talk_base::SocketFactory* factory,
+ talk_base::Network* network,
+ const talk_base::SocketAddress& local_addr,
+ const talk_base::SocketAddress& server_addr)
+ : UDPPort(thread, STUN_PORT_TYPE, factory, network),
+ server_addr_(server_addr), requests_(thread), error_(0) {
+
+ socket_ = CreatePacketSocket(PROTO_UDP);
+ socket_->SignalReadPacket.connect(this, &StunPort::OnReadPacket);
+ if (socket_->Bind(local_addr) < 0)
+ PLOG(LERROR, socket_->GetError()) << "bind";
+
+ requests_.SignalSendPacket.connect(this, &StunPort::OnSendPacket);
+}
+
+StunPort::~StunPort() {
+ delete socket_;
+}
+
+void StunPort::PrepareAddress() {
+ // We will keep pinging the stun server to make sure our NAT pin-hole stays
+ // open during the call.
+ requests_.Send(new StunPortBindingRequest(this, true, server_addr_));
+}
+
+void StunPort::PrepareSecondaryAddress() {
+ ASSERT(!server_addr2_.IsAny());
+ requests_.Send(new StunPortBindingRequest(this, false, server_addr2_));
+}
+
+int StunPort::SendTo(
+ const void* data, size_t size, const talk_base::SocketAddress& addr,
+ bool payload) {
+ int sent = socket_->SendTo(data, size, addr);
+ if (sent < 0)
+ error_ = socket_->GetError();
+ return sent;
+}
+
+int StunPort::SetOption(talk_base::Socket::Option opt, int value) {
+ return socket_->SetOption(opt, value);
+}
+
+int StunPort::GetError() {
+ return error_;
+}
+
+void StunPort::OnReadPacket(
+ const char* data, size_t size, const talk_base::SocketAddress& remote_addr,
+ talk_base::AsyncPacketSocket* socket) {
+ assert(socket == socket_);
+
+ // Look for a response to a binding request.
+ if (requests_.CheckResponse(data, size))
+ return;
+
+ // Process this data packet in the normal manner.
+ UDPPort::OnReadPacket(data, size, remote_addr);
+}
+
+void StunPort::OnSendPacket(const void* data, size_t size, StunRequest* req) {
+ StunPortBindingRequest* sreq = static_cast<StunPortBindingRequest*>(req);
+ if (socket_->SendTo(data, size, sreq->server_addr()) < 0)
+ PLOG(LERROR, socket_->GetError()) << "sendto";
+}
+
+} // namespace cricket
diff --git a/Plugins/jingle/libjingle/talk/p2p/base/stunport.h b/Plugins/jingle/libjingle/talk/p2p/base/stunport.h
new file mode 100644
index 0000000..4b62181
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/p2p/base/stunport.h
@@ -0,0 +1,94 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __STUNPORT_H__
+#define __STUNPORT_H__
+
+#include "talk/base/asyncudpsocket.h"
+#include "talk/p2p/base/udpport.h"
+#include "talk/p2p/base/stunrequest.h"
+
+namespace cricket {
+
+extern const std::string STUN_PORT_TYPE;
+
+// Communicates using the address on the outside of a NAT.
+class StunPort : public UDPPort {
+public:
+ StunPort(talk_base::Thread* thread, talk_base::SocketFactory* factory,
+ talk_base::Network* network,
+ const talk_base::SocketAddress& local_addr,
+ const talk_base::SocketAddress& server_addr);
+ virtual ~StunPort();
+
+ const talk_base::SocketAddress& server_addr() const { return server_addr_; }
+ void set_server_addr(const talk_base::SocketAddress& addr)
+ { server_addr_ = addr; }
+
+ const talk_base::SocketAddress& server_addr2() const { return server_addr2_; }
+ void set_server_addr2(const talk_base::SocketAddress& addr)
+ { server_addr2_ = addr; }
+
+ virtual void PrepareAddress();
+
+ // This will contact the secondary server and signal another candidate
+ // address for this port (which may be the same as the first address).
+ void PrepareSecondaryAddress();
+
+ talk_base::SocketAddress GetLocalAddress() const {
+ if (socket_)
+ return socket_->GetLocalAddress();
+ return talk_base::SocketAddress();
+ }
+
+ virtual int SetOption(talk_base::Socket::Option opt, int value);
+ virtual int GetError();
+
+protected:
+ virtual int SendTo(const void* data, size_t size,
+ const talk_base::SocketAddress& addr, bool payload);
+
+ void OnReadPacket(
+ const char* data, size_t size, const talk_base::SocketAddress& remote_addr,
+ talk_base::AsyncPacketSocket* socket);
+
+private:
+ talk_base::AsyncPacketSocket* socket_;
+ talk_base::SocketAddress server_addr_;
+ talk_base::SocketAddress server_addr2_;
+ StunRequestManager requests_;
+ int error_;
+
+ friend class StunPortBindingRequest;
+
+ // Sends STUN requests to the server.
+ void OnSendPacket(const void* data, size_t size, StunRequest* req);
+};
+
+} // namespace cricket
+
+#endif // __STUNPORT_H__
diff --git a/Plugins/jingle/libjingle/talk/p2p/base/stunrequest.cc b/Plugins/jingle/libjingle/talk/p2p/base/stunrequest.cc
new file mode 100644
index 0000000..66b8ef6
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/p2p/base/stunrequest.cc
@@ -0,0 +1,198 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#if defined(_MSC_VER) && _MSC_VER < 1300
+#pragma warning(disable:4786)
+#endif
+#include "talk/base/logging.h"
+#include "talk/base/helpers.h"
+#include "talk/p2p/base/stunrequest.h"
+#include <iostream>
+#include <cassert>
+
+namespace cricket {
+
+const uint32 MSG_STUN_SEND = 1;
+
+const int MAX_SENDS = 9;
+const int DELAY_UNIT = 100; // 100 milliseconds
+const int DELAY_MAX_FACTOR = 16;
+
+StunRequestManager::StunRequestManager(talk_base::Thread* thread)
+ : thread_(thread) {
+ }
+
+StunRequestManager::~StunRequestManager() {
+ while (requests_.begin() != requests_.end()) {
+ StunRequest *request = requests_.begin()->second;
+ requests_.erase(requests_.begin());
+ delete request;
+ }
+}
+
+void StunRequestManager::Send(StunRequest* request) {
+ SendDelayed(request, 0);
+}
+
+void StunRequestManager::SendDelayed(StunRequest* request, int delay) {
+ request->set_manager(this);
+ assert(requests_.find(request->id()) == requests_.end());
+ requests_[request->id()] = request;
+ thread_->PostDelayed(delay, request, MSG_STUN_SEND, NULL);
+}
+
+void StunRequestManager::Remove(StunRequest* request) {
+ assert(request->manager() == this);
+ RequestMap::iterator iter = requests_.find(request->id());
+ if (iter != requests_.end()) {
+ assert(iter->second == request);
+ requests_.erase(iter);
+ thread_->Clear(request);
+ }
+}
+
+void StunRequestManager::Clear() {
+ std::vector<StunRequest*> requests;
+ for (RequestMap::iterator i = requests_.begin(); i != requests_.end(); ++i)
+ requests.push_back(i->second);
+
+ for (uint32 i = 0; i < requests.size(); ++i)
+ Remove(requests[i]);
+}
+
+bool StunRequestManager::CheckResponse(StunMessage* msg) {
+ RequestMap::iterator iter = requests_.find(msg->transaction_id());
+ if (iter == requests_.end())
+ return false;
+ StunRequest* request = iter->second;
+ if (msg->type() == GetStunResponseType(request->type())) {
+ request->OnResponse(msg);
+ } else if (msg->type() == GetStunErrorResponseType(request->type())) {
+ request->OnErrorResponse(msg);
+ } else {
+ LOG(LERROR) << "Received response with wrong type: " << msg->type()
+ << " (expecting " << GetStunResponseType(request->type()) << ")";
+ return false;
+ }
+
+ delete request;
+ return true;
+}
+
+bool StunRequestManager::CheckResponse(const char* data, size_t size) {
+ // Check the appropriate bytes of the stream to see if they match the
+ // transaction ID of a response we are expecting.
+
+ if (size < 20)
+ return false;
+
+ std::string id;
+ id.append(data + 4, 16);
+
+ RequestMap::iterator iter = requests_.find(id);
+ if (iter == requests_.end())
+ return false;
+
+ // Parse the STUN message and continue processing as usual.
+
+ talk_base::ByteBuffer buf(data, size);
+ StunMessage msg;
+ if (!msg.Read(&buf))
+ return false;
+
+ return CheckResponse(&msg);
+}
+
+StunRequest::StunRequest()
+ : manager_(0), id_(CreateRandomString(16)), msg_(0), count_(0),
+ timeout_(false), tstamp_(0) {
+}
+
+StunRequest::StunRequest(StunMessage* request)
+ : manager_(0), id_(request->transaction_id()), msg_(request),
+ count_(0), timeout_(false) {
+}
+
+StunRequest::~StunRequest() {
+ assert(manager_ != NULL);
+ if (manager_) {
+ manager_->Remove(this);
+ manager_->thread_->Clear(this);
+ }
+ delete msg_;
+}
+
+const StunMessageType StunRequest::type() {
+ assert(msg_);
+ return msg_->type();
+}
+
+void StunRequest::set_manager(StunRequestManager* manager) {
+ assert(!manager_);
+ manager_ = manager;
+}
+
+void StunRequest::OnMessage(talk_base::Message* pmsg) {
+ assert(manager_);
+ assert(pmsg->message_id == MSG_STUN_SEND);
+
+ if (!msg_) {
+ msg_ = new StunMessage();
+ msg_->SetTransactionID(id_);
+ Prepare(msg_);
+ assert(msg_->transaction_id() == id_);
+ }
+
+ if (timeout_) {
+ OnTimeout();
+ delete this;
+ return;
+ }
+
+ tstamp_ = talk_base::GetMillisecondCount();
+
+ talk_base::ByteBuffer buf;
+ msg_->Write(&buf);
+ manager_->SignalSendPacket(buf.Data(), buf.Length(), this);
+
+ int delay = GetNextDelay();
+ manager_->thread_->PostDelayed(delay, this, MSG_STUN_SEND, NULL);
+}
+
+uint32 StunRequest::Elapsed() const {
+ return (talk_base::GetMillisecondCount() - tstamp_);
+}
+
+int StunRequest::GetNextDelay() {
+ int delay = DELAY_UNIT * talk_base::_min(1 << count_, DELAY_MAX_FACTOR);
+ count_ += 1;
+ if (count_ == MAX_SENDS)
+ timeout_ = true;
+ return delay;
+}
+
+} // namespace cricket
diff --git a/Plugins/jingle/libjingle/talk/p2p/base/stunrequest.h b/Plugins/jingle/libjingle/talk/p2p/base/stunrequest.h
new file mode 100644
index 0000000..b36861c
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/p2p/base/stunrequest.h
@@ -0,0 +1,126 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __STUNREQUESTMANAGER_H__
+#define __STUNREQUESTMANAGER_H__
+
+#include "talk/base/sigslot.h"
+#include "talk/base/thread.h"
+#include "talk/p2p/base/stun.h"
+#include <map>
+#include <string>
+
+namespace cricket {
+
+class StunRequest;
+
+// Manages a set of STUN requests, sending and resending until we receive a
+// response or determine that the request has timed out.
+class StunRequestManager {
+public:
+ StunRequestManager(talk_base::Thread* thread);
+ ~StunRequestManager();
+
+ // Starts sending the given request (perhaps after a delay).
+ void Send(StunRequest* request);
+ void SendDelayed(StunRequest* request, int delay);
+
+ // Removes a stun request that was added previously. This will happen
+ // automatically when a request succeeds, fails, or times out.
+ void Remove(StunRequest* request);
+
+ // Removes all stun requests that were added previously.
+ void Clear();
+
+ // Determines whether the given message is a response to one of the
+ // outstanding requests, and if so, processes it appropriately.
+ bool CheckResponse(StunMessage* msg);
+ bool CheckResponse(const char* data, size_t size);
+
+ // Raised when there are bytes to be sent.
+ sigslot::signal3<const void*, size_t, StunRequest*> SignalSendPacket;
+
+private:
+ typedef std::map<std::string, StunRequest*> RequestMap;
+
+ talk_base::Thread* thread_;
+ RequestMap requests_;
+
+ friend class StunRequest;
+};
+
+// Represents an individual request to be sent. The STUN message can either be
+// constructed beforehand or built on demand.
+class StunRequest : public talk_base::MessageHandler {
+public:
+ StunRequest();
+ StunRequest(StunMessage* request);
+ virtual ~StunRequest();
+
+ // The manager handling this request (if it has been scheduled for sending).
+ StunRequestManager* manager() { return manager_; }
+
+ // Returns the transaction ID of this request.
+ const std::string& id() { return id_; }
+
+ // Returns the STUN type of the request message.
+ const StunMessageType type();
+
+ // Handles messages for sending and timeout.
+ void OnMessage(talk_base::Message* pmsg);
+
+ // Time elapsed since last send (in ms)
+ uint32 Elapsed() const;
+
+protected:
+ int count_;
+ bool timeout_;
+
+ // Fills in the actual request to be sent. Note that the transaction ID will
+ // already be set and cannot be changed.
+ virtual void Prepare(StunMessage* request) {}
+
+ // Called when the message receives a response or times out.
+ virtual void OnResponse(StunMessage* response) {}
+ virtual void OnErrorResponse(StunMessage* response) {}
+ virtual void OnTimeout() {}
+ virtual int GetNextDelay();
+
+private:
+ StunRequestManager* manager_;
+ std::string id_;
+ StunMessage* msg_;
+ uint32 tstamp_;
+
+ void set_manager(StunRequestManager* manager);
+
+ friend class StunRequestManager;
+};
+
+} // namespace cricket
+
+#endif // __STUNREQUESTMANAGER_H__
diff --git a/Plugins/jingle/libjingle/talk/p2p/base/stunserver.cc b/Plugins/jingle/libjingle/talk/p2p/base/stunserver.cc
new file mode 100644
index 0000000..5fc61ac
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/p2p/base/stunserver.cc
@@ -0,0 +1,160 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "talk/base/bytebuffer.h"
+#include "talk/p2p/base/stunserver.h"
+#include <iostream>
+
+#ifdef POSIX
+extern "C" {
+#include <errno.h>
+}
+#endif // POSIX
+
+namespace cricket {
+
+StunServer::StunServer(talk_base::AsyncUDPSocket* socket) : socket_(socket) {
+ socket_->SignalReadPacket.connect(this, &StunServer::OnPacket);
+}
+
+StunServer::~StunServer() {
+ socket_->SignalReadPacket.disconnect(this);
+}
+
+void StunServer::OnPacket(
+ const char* buf, size_t size, const talk_base::SocketAddress& remote_addr,
+ talk_base::AsyncPacketSocket* socket) {
+
+ // TODO: If appropriate, look for the magic cookie before parsing.
+
+ // Parse the STUN message.
+ talk_base::ByteBuffer bbuf(buf, size);
+ StunMessage msg;
+ if (!msg.Read(&bbuf)) {
+ SendErrorResponse(msg, remote_addr, 400, "Bad Request");
+ return;
+ }
+
+ // TODO: If this is UDP, then we shouldn't allow non-fully-parsed messages.
+
+ // TODO: If unknown non-optiional (<= 0x7fff) attributes are found, send a
+ // 420 "Unknown Attribute" response.
+
+ // TODO: Check that a message-integrity attribute was given (or send 401
+ // "Unauthorized"). Check that a username attribute was given (or send
+ // 432 "Missing Username"). Look up the username and password. If it
+ // is missing or the HMAC is wrong, send 431 "Integrity Check Failure".
+
+ // Send the message to the appropriate handler function.
+ switch (msg.type()) {
+ case STUN_BINDING_REQUEST:
+ OnBindingRequest(&msg, remote_addr);
+ return;
+
+ case STUN_ALLOCATE_REQUEST:
+ OnAllocateRequest(&msg, remote_addr);
+ return;
+
+ default:
+ SendErrorResponse(msg, remote_addr, 600, "Operation Not Supported");
+ }
+}
+
+void StunServer::OnBindingRequest(
+ StunMessage* msg, const talk_base::SocketAddress& remote_addr) {
+ StunMessage response;
+ response.SetType(STUN_BINDING_RESPONSE);
+ response.SetTransactionID(msg->transaction_id());
+
+ // Tell the user the address that we received their request from.
+ StunAddressAttribute* mapped_addr =
+ StunAttribute::CreateAddress(STUN_ATTR_MAPPED_ADDRESS);
+ mapped_addr->SetFamily(1);
+ mapped_addr->SetPort(remote_addr.port());
+ mapped_addr->SetIP(remote_addr.ip());
+ response.AddAttribute(mapped_addr);
+
+ // Tell the user the address that we are sending the response from.
+ talk_base::SocketAddress local_addr = socket_->GetLocalAddress();
+ StunAddressAttribute* source_addr =
+ StunAttribute::CreateAddress(STUN_ATTR_SOURCE_ADDRESS);
+ source_addr->SetFamily(1);
+ source_addr->SetPort(local_addr.port());
+ source_addr->SetIP(local_addr.ip());
+ response.AddAttribute(source_addr);
+
+ // TODO: Add username and message-integrity.
+
+ // TODO: Add changed-address. (Keep information about three other servers.)
+
+ SendResponse(response, remote_addr);
+}
+
+void StunServer::OnAllocateRequest(
+ StunMessage* msg, const talk_base::SocketAddress& addr) {
+ SendErrorResponse(*msg, addr, 600, "Operation Not Supported");
+}
+
+void StunServer::OnSharedSecretRequest(
+ StunMessage* msg, const talk_base::SocketAddress& addr) {
+ SendErrorResponse(*msg, addr, 600, "Operation Not Supported");
+}
+
+void StunServer::OnSendRequest(StunMessage* msg, const talk_base::SocketAddress& addr) {
+ SendErrorResponse(*msg, addr, 600, "Operation Not Supported");
+}
+
+void StunServer::SendErrorResponse(
+ const StunMessage& msg, const talk_base::SocketAddress& addr, int error_code,
+ const char* error_desc) {
+
+ StunMessage err_msg;
+ err_msg.SetType(GetStunErrorResponseType(msg.type()));
+ err_msg.SetTransactionID(msg.transaction_id());
+
+ StunErrorCodeAttribute* err_code = StunAttribute::CreateErrorCode();
+ err_code->SetErrorClass(error_code / 100);
+ err_code->SetNumber(error_code % 100);
+ err_code->SetReason(error_desc);
+ err_msg.AddAttribute(err_code);
+
+ SendResponse(err_msg, addr);
+}
+
+void StunServer::SendResponse(
+ const StunMessage& msg, const talk_base::SocketAddress& addr) {
+
+ talk_base::ByteBuffer buf;
+ msg.Write(&buf);
+
+ // TODO: Allow response addr attribute if sent from another stun server.
+
+ if (socket_->SendTo(buf.Data(), buf.Length(), addr) < 0)
+ std::cerr << "sendto: " << std::strerror(errno) << std::endl;
+}
+
+} // namespace cricket
diff --git a/Plugins/jingle/libjingle/talk/p2p/base/stunserver.h b/Plugins/jingle/libjingle/talk/p2p/base/stunserver.h
new file mode 100644
index 0000000..0e57a18
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/p2p/base/stunserver.h
@@ -0,0 +1,73 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __STUNSERVER_H__
+#define __STUNSERVER_H__
+
+#include "talk/base/asyncudpsocket.h"
+#include "talk/p2p/base/stun.h"
+
+namespace cricket {
+
+const int STUN_SERVER_PORT = 3478;
+
+class StunServer : public sigslot::has_slots<> {
+public:
+ // Creates a STUN server, which will listen on the given socket.
+ StunServer(talk_base::AsyncUDPSocket* socket);
+
+ // Removes the STUN server from the socket, but does not delete the socket.
+ ~StunServer();
+
+protected:
+
+ // Slot for AsyncSocket.PacketRead:
+ void OnPacket(
+ const char* buf, size_t size, const talk_base::SocketAddress& remote_addr,
+ talk_base::AsyncPacketSocket* socket);
+
+ // Handlers for the different types of STUN/TURN requests:
+ void OnBindingRequest(StunMessage* msg, const talk_base::SocketAddress& addr);
+ void OnAllocateRequest(StunMessage* msg, const talk_base::SocketAddress& addr);
+ void OnSharedSecretRequest(StunMessage* msg, const talk_base::SocketAddress& addr);
+ void OnSendRequest(StunMessage* msg, const talk_base::SocketAddress& addr);
+
+ // Sends an error response to the given message back to the user.
+ void SendErrorResponse(
+ const StunMessage& msg, const talk_base::SocketAddress& addr, int error_code,
+ const char* error_desc);
+
+ // Sends the given message to the appropriate destination.
+ void SendResponse(const StunMessage& msg, const talk_base::SocketAddress& addr);
+
+private:
+ talk_base::AsyncUDPSocket* socket_;
+};
+
+} // namespace cricket
+
+#endif // __STUNSERVER_H__
diff --git a/Plugins/jingle/libjingle/talk/p2p/base/stunserver_main.cc b/Plugins/jingle/libjingle/talk/p2p/base/stunserver_main.cc
new file mode 100644
index 0000000..8aa200e
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/p2p/base/stunserver_main.cc
@@ -0,0 +1,66 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "talk/base/host.h"
+#include "talk/base/thread.h"
+#include "talk/p2p/base/stunserver.h"
+#include <iostream>
+
+#ifdef POSIX
+extern "C" {
+#include <errno.h>
+}
+#endif // POSIX
+
+using namespace cricket;
+
+int main(int argc, char* argv[]) {
+ if (argc != 1) {
+ std::cerr << "usage: stunserver" << std::endl;
+ return 1;
+ }
+
+ talk_base::SocketAddress server_addr(talk_base::LocalHost().networks()[1]->ip(), 7000);
+
+ talk_base::Thread *pthMain = talk_base::Thread::Current();
+
+ talk_base::AsyncUDPSocket* server_socket = talk_base::CreateAsyncUDPSocket(pthMain->socketserver());
+ if (server_socket->Bind(server_addr) < 0) {
+ std::cerr << "bind: " << std::strerror(errno) << std::endl;
+ return 1;
+ }
+
+ StunServer* server = new StunServer(server_socket);
+
+ std::cout << "Listening at " << server_addr.ToString() << std::endl;
+
+ pthMain->Run();
+
+ delete server;
+ delete server_socket;
+ return 0;
+}
diff --git a/Plugins/jingle/libjingle/talk/p2p/base/stunserver_unittest.cc b/Plugins/jingle/libjingle/talk/p2p/base/stunserver_unittest.cc
new file mode 100644
index 0000000..b56da4e
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/p2p/base/stunserver_unittest.cc
@@ -0,0 +1,107 @@
+#include "talk/base/testclient.h"
+#include "talk/base/thread.h"
+#include "talk/base/physicalsocketserver.h"
+#include "talk/base/host.h"
+#include "talk/p2p/base/stunserver.h"
+#include <cstring>
+#include <iostream>
+#include <cassert>
+
+using namespace cricket;
+
+StunMessage* GetResponse(talk_base::TestClient* client) {
+ talk_base::TestClient::Packet* packet = client->NextPacket();
+ assert(packet);
+ talk_base::ByteBuffer buf(packet->buf, packet->size);
+ StunMessage* msg = new StunMessage();
+ assert(msg->Read(&buf));
+ delete packet;
+ return msg;
+}
+
+int main(int argc, char* argv[]) {
+ assert(talk_base::LocalHost().networks().size() >= 2);
+ talk_base::SocketAddress server_addr(talk_base::LocalHost().networks()[1]->ip(), 7000);
+ talk_base::SocketAddress client_addr(talk_base::LocalHost().networks()[1]->ip(), 6000);
+
+ talk_base::Thread th;
+
+ talk_base::AsyncUDPSocket* server_socket = 0;
+ StunServer* server = 0;
+ if (argc >= 2) {
+ server_addr.SetIP(argv[1]);
+ client_addr.SetIP(0);
+ if (argc == 3)
+ server_addr.SetPort(atoi(argv[2]));
+ std::cout << "Using server at " << server_addr.ToString() << std::endl;
+ } else {
+ server_socket = talk_base::CreateAsyncUDPSocket(th.socketserver());
+ assert(server_socket->Bind(server_addr) >= 0);
+ server = new StunServer(server_socket);
+ }
+
+ talk_base::AsyncUDPSocket* client_socket = talk_base::CreateAsyncUDPSocket(th.socketserver());
+ assert(client_socket->Bind(client_addr) >= 0);
+ talk_base::TestClient* client = new talk_base::TestClient(client_socket, &th);
+
+ th.Start();
+
+ const char* bad = "this is a completely nonsensical message whose only "
+ "purpose is to make the parser go 'ack'. it doesn't "
+ "look anything like a normal stun message";
+
+ client->SendTo(bad, std::strlen(bad), server_addr);
+ StunMessage* msg = GetResponse(client);
+ assert(msg->type() == STUN_BINDING_ERROR_RESPONSE);
+
+ const StunErrorCodeAttribute* err = msg->GetErrorCode();
+ assert(err);
+ assert(err->error_class() == 4);
+ assert(err->number() == 0);
+ assert(err->reason() == std::string("Bad Request"));
+
+ delete msg;
+
+ std::string transaction_id = "0123456789abcdef";
+
+ StunMessage req;
+ req.SetType(STUN_BINDING_REQUEST);
+ req.SetTransactionID(transaction_id);
+
+ talk_base::ByteBuffer buf;
+ req.Write(&buf);
+
+ client->SendTo(buf.Data(), buf.Length(), server_addr);
+ StunMessage* msg2 = GetResponse(client);
+ assert(msg2->type() == STUN_BINDING_RESPONSE);
+ assert(msg2->transaction_id() == transaction_id);
+
+ const StunAddressAttribute* mapped_addr =
+ msg2->GetAddress(STUN_ATTR_MAPPED_ADDRESS);
+ assert(mapped_addr);
+ assert(mapped_addr->family() == 1);
+ assert(mapped_addr->port() == client_addr.port());
+ if (mapped_addr->ip() != client_addr.ip()) {
+ printf("Warning: mapped IP (%s) != local IP (%s)\n",
+ talk_base::SocketAddress::IPToString(mapped_addr->ip()).c_str(),
+ client_addr.IPAsString().c_str());
+ }
+
+ const StunAddressAttribute* source_addr =
+ msg2->GetAddress(STUN_ATTR_SOURCE_ADDRESS);
+ assert(source_addr);
+ assert(source_addr->family() == 1);
+ assert(source_addr->port() == server_addr.port());
+ assert(source_addr->ip() == server_addr.ip());
+
+ delete msg2;
+
+ th.Stop();
+
+ delete server;
+ delete server_socket;
+ delete client;
+
+ std::cout << "PASS" << std::endl;
+ return 0;
+}
diff --git a/Plugins/jingle/libjingle/talk/p2p/base/tcpport.cc b/Plugins/jingle/libjingle/talk/p2p/base/tcpport.cc
new file mode 100644
index 0000000..57e2a4f
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/p2p/base/tcpport.cc
@@ -0,0 +1,271 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#if defined(_MSC_VER) && _MSC_VER < 1300
+#pragma warning(disable:4786)
+#endif
+
+#ifdef POSIX
+extern "C" {
+#include <errno.h>
+}
+#endif // POSIX
+
+#include <cassert>
+#include <iostream>
+
+#include "talk/base/common.h"
+#include "talk/base/logging.h"
+#ifdef WIN32
+#include "talk/base/winfirewall.h"
+#endif // WIN32
+#include "talk/p2p/base/tcpport.h"
+
+namespace cricket {
+
+#ifdef WIN32
+static talk_base::WinFirewall win_firewall;
+#endif // WIN32
+
+TCPPort::TCPPort(talk_base::Thread* thread, talk_base::SocketFactory* factory,
+ talk_base::Network* network,
+ const talk_base::SocketAddress& address)
+ : Port(thread, LOCAL_PORT_TYPE, factory, network), address_(address),
+ incoming_only_(address_.port() != 0), error_(0) {
+ socket_ = thread->socketserver()->CreateAsyncSocket(SOCK_STREAM);
+ socket_->SignalReadEvent.connect(this, &TCPPort::OnAcceptEvent);
+ if (socket_->Bind(address_) < 0) {
+ LOG_F(LS_ERROR) << "Bind error: " << socket_->GetError();
+ }
+}
+
+TCPPort::~TCPPort() {
+ delete socket_;
+}
+
+Connection* TCPPort::CreateConnection(const Candidate& address,
+ CandidateOrigin origin) {
+ // We only support TCP protocols
+ if ((address.protocol() != "tcp") && (address.protocol() != "ssltcp"))
+ return 0;
+
+ // We can't accept TCP connections incoming on other ports
+ if (origin == ORIGIN_OTHER_PORT)
+ return 0;
+
+ // Check if we are allowed to make outgoing TCP connections
+ if (incoming_only_ && (origin == ORIGIN_MESSAGE))
+ return 0;
+
+ // We don't know how to act as an ssl server yet
+ if ((address.protocol() == "ssltcp") && (origin == ORIGIN_THIS_PORT))
+ return 0;
+
+ TCPConnection* conn = 0;
+ if (talk_base::AsyncTCPSocket * socket
+ = GetIncoming(address.address(), true)) {
+ socket->SignalReadPacket.disconnect(this);
+ conn = new TCPConnection(this, address, socket);
+ } else {
+ conn = new TCPConnection(this, address);
+ }
+ AddConnection(conn);
+ return conn;
+}
+
+void TCPPort::PrepareAddress() {
+ assert(socket_);
+
+ bool allow_listen = true;
+#ifdef WIN32
+ if (win_firewall.Initialize()) {
+ char module_path[MAX_PATH + 1] = { 0 };
+ ::GetModuleFileNameA(NULL, module_path, MAX_PATH);
+ if (win_firewall.Enabled() && !win_firewall.Authorized(module_path)) {
+ allow_listen = false;
+ }
+ }
+#endif // WIN32
+ if (!allow_listen) {
+ LOG_F(LS_VERBOSE) << "Not listening due to firewall restrictions";
+ } else if (socket_->Listen(5) < 0) {
+ LOG_F(LS_ERROR) << "Listen error: " << socket_->GetError();
+ }
+ // Note: We still add the address, since otherwise the remote side won't
+ // recognize our incoming TCP connections.
+ AddAddress(socket_->GetLocalAddress(), "tcp", true);
+}
+
+int TCPPort::SendTo(const void* data, size_t size,
+ const talk_base::SocketAddress& addr, bool payload) {
+ talk_base::AsyncTCPSocket * socket = 0;
+
+ if (TCPConnection * conn = static_cast<TCPConnection*>(GetConnection(addr))) {
+ socket = conn->socket();
+ } else {
+ socket = GetIncoming(addr);
+ }
+ if (!socket) {
+ LOG_F(LS_ERROR) << "Unknown destination: " << addr.ToString();
+ return -1; // TODO: Set error_
+ }
+
+ //LOG_F(INFO) << "(" << size << ", " << addr.ToString() << ")";
+
+ int sent = socket->Send(data, size);
+ if (sent < 0) {
+ error_ = socket->GetError();
+ LOG_F(LS_ERROR) << "(" << size << ", " << addr.ToString()
+ << ") Send error: " << error_;
+ }
+ return sent;
+}
+
+int TCPPort::SetOption(talk_base::Socket::Option opt, int value) {
+ return socket_->SetOption(opt, value);
+}
+
+int TCPPort::GetError() {
+ assert(socket_);
+ return error_;
+}
+
+void TCPPort::OnAcceptEvent(talk_base::AsyncSocket* socket) {
+ assert(socket == socket_);
+
+ Incoming incoming;
+ talk_base::AsyncSocket * newsocket
+ = static_cast<talk_base::AsyncSocket *>(socket->Accept(&incoming.addr));
+ if (!newsocket) {
+ // TODO: Do something better like forwarding the error to the user.
+ LOG_F(LS_ERROR) << "Accept error: " << socket_->GetError();
+ return;
+ }
+ incoming.socket = new talk_base::AsyncTCPSocket(newsocket);
+ incoming.socket->SignalReadPacket.connect(this, &TCPPort::OnReadPacket);
+
+ LOG_F(LS_VERBOSE) << "(" << incoming.addr.ToString() << ")";
+ incoming_.push_back(incoming);
+
+ // Prime a read event in case data is waiting
+ newsocket->SignalReadEvent(newsocket);
+}
+
+talk_base::AsyncTCPSocket * TCPPort::GetIncoming(
+ const talk_base::SocketAddress& addr, bool remove) {
+ talk_base::AsyncTCPSocket * socket = 0;
+ for (std::list<Incoming>::iterator it = incoming_.begin();
+ it != incoming_.end(); ++it) {
+ if (it->addr == addr) {
+ socket = it->socket;
+ if (remove)
+ incoming_.erase(it);
+ break;
+ }
+ }
+ return socket;
+}
+
+void TCPPort::OnReadPacket(const char* data, size_t size,
+ const talk_base::SocketAddress& remote_addr,
+ talk_base::AsyncPacketSocket* socket) {
+ Port::OnReadPacket(data, size, remote_addr);
+}
+
+TCPConnection::TCPConnection(TCPPort* port, const Candidate& candidate,
+ talk_base::AsyncTCPSocket* socket)
+ : Connection(port, 0, candidate), socket_(socket), error_(0) {
+ bool outgoing = (socket_ == 0);
+ if (outgoing) {
+ socket_ = static_cast<talk_base::AsyncTCPSocket *>(port->CreatePacketSocket(
+ (candidate.protocol() == "ssltcp") ? PROTO_SSLTCP : PROTO_TCP));
+ } else {
+ // Incoming connections should match the network address
+ ASSERT(socket_->GetLocalAddress().EqualIPs(port->address_));
+ }
+ socket_->SignalReadPacket.connect(this, &TCPConnection::OnReadPacket);
+ socket_->SignalClose.connect(this, &TCPConnection::OnClose);
+ if (outgoing) {
+ set_connected(false);
+ talk_base::SocketAddress local_address(port->address_.ip(), 0);
+ socket_->SignalConnect.connect(this, &TCPConnection::OnConnect);
+ socket_->Bind(local_address);
+ socket_->Connect(candidate.address());
+ LOG_F(LS_VERBOSE) << "Connecting from " << local_address.ToString()
+ << " to " << candidate.address().ToString();
+ }
+}
+
+TCPConnection::~TCPConnection() {
+ delete socket_;
+}
+
+int TCPConnection::Send(const void* data, size_t size) {
+ if (write_state() != STATE_WRITABLE) {
+ // TODO: Should STATE_WRITE_TIMEOUT return a non-blocking error?
+ error_ = EWOULDBLOCK;
+ return SOCKET_ERROR;
+ }
+ int sent = socket_->Send(data, size);
+ if (sent < 0) {
+ error_ = socket_->GetError();
+ } else {
+ sent_total_bytes_ += sent;
+ }
+ return sent;
+}
+
+int TCPConnection::GetError() {
+ return error_;
+}
+
+TCPPort* TCPConnection::tcpport() {
+ return static_cast<TCPPort*>(port_);
+}
+
+void TCPConnection::OnConnect(talk_base::AsyncTCPSocket* socket) {
+ assert(socket == socket_);
+ LOG_F(LS_VERBOSE) << "(" << socket->GetRemoteAddress().ToString() << ")";
+ set_connected(true);
+}
+
+void TCPConnection::OnClose(talk_base::AsyncTCPSocket* socket, int error) {
+ assert(socket == socket_);
+ LOG_F(LS_VERBOSE) << "(" << error << ")";
+ set_connected(false);
+ set_write_state(STATE_WRITE_TIMEOUT);
+}
+
+void TCPConnection::OnReadPacket(const char* data, size_t size,
+ const talk_base::SocketAddress& remote_addr,
+ talk_base::AsyncPacketSocket* socket) {
+ assert(socket == socket_);
+ //LOG_F(LS_INFO) << "(" << size << ", " << remote_addr.ToString() << ")";
+ Connection::OnReadPacket(data, size);
+}
+
+} // namespace cricket
diff --git a/Plugins/jingle/libjingle/talk/p2p/base/tcpport.h b/Plugins/jingle/libjingle/talk/p2p/base/tcpport.h
new file mode 100644
index 0000000..24555ae
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/p2p/base/tcpport.h
@@ -0,0 +1,122 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __TCPPORT_H__
+#define __TCPPORT_H__
+
+#include <list>
+#include "talk/base/asynctcpsocket.h"
+#include "talk/p2p/base/port.h"
+
+namespace cricket {
+
+class TCPConnection;
+
+extern const std::string LOCAL_PORT_TYPE; // type of TCP ports
+
+// Communicates using a local TCP port.
+//
+// This class is designed to allow subclasses to take advantage of the
+// connection management provided by this class. A subclass should take of all
+// packet sending and preparation, but when a packet is received, it should
+// call this TCPPort::OnReadPacket (3 arg) to dispatch to a connection.
+class TCPPort : public Port {
+public:
+ TCPPort(talk_base::Thread* thread, talk_base::SocketFactory* factory,
+ talk_base::Network* network, const talk_base::SocketAddress& address);
+ virtual ~TCPPort();
+
+ virtual Connection* CreateConnection(const Candidate& address, CandidateOrigin origin);
+
+ virtual void PrepareAddress();
+
+ virtual int SetOption(talk_base::Socket::Option opt, int value);
+ virtual int GetError();
+
+protected:
+ // Handles sending using the local TCP socket.
+ virtual int SendTo(const void* data, size_t size,
+ const talk_base::SocketAddress& addr, bool payload);
+
+ // Creates TCPConnection for incoming sockets
+ void OnAcceptEvent(talk_base::AsyncSocket* socket);
+
+ talk_base::AsyncSocket* socket() { return socket_; }
+
+private:
+ // Note: use this until Network ips are stable, then use network->ip
+ talk_base::SocketAddress address_;
+ bool incoming_only_;
+ talk_base::AsyncSocket* socket_;
+ int error_;
+
+ struct Incoming {
+ talk_base::SocketAddress addr;
+ talk_base::AsyncTCPSocket * socket;
+ };
+ std::list<Incoming> incoming_;
+
+ talk_base::AsyncTCPSocket * GetIncoming(const talk_base::SocketAddress& addr,
+ bool remove = false);
+
+ // Receives packet signal from the local TCP Socket.
+ void OnReadPacket(const char* data, size_t size,
+ const talk_base::SocketAddress& remote_addr,
+ talk_base::AsyncPacketSocket* socket);
+
+ friend class TCPConnection;
+};
+
+class TCPConnection : public Connection {
+public:
+ // Connection is outgoing unless socket is specified
+ TCPConnection(TCPPort* port, const Candidate& candidate,
+ talk_base::AsyncTCPSocket* socket = 0);
+ virtual ~TCPConnection();
+
+ virtual int Send(const void* data, size_t size);
+ virtual int GetError();
+
+ talk_base::AsyncTCPSocket * socket() { return socket_; }
+
+private:
+ TCPPort* tcpport();
+ talk_base::AsyncTCPSocket* socket_;
+ int error_;
+
+ void OnConnect(talk_base::AsyncTCPSocket* socket);
+ void OnClose(talk_base::AsyncTCPSocket* socket, int error);
+ void OnReadPacket(const char* data, size_t size,
+ const talk_base::SocketAddress& remote_addr,
+ talk_base::AsyncPacketSocket* socket);
+
+ friend class TCPPort;
+};
+
+} // namespace cricket
+
+#endif // __TCPPORT_H__
diff --git a/Plugins/jingle/libjingle/talk/p2p/base/transport.cc b/Plugins/jingle/libjingle/talk/p2p/base/transport.cc
new file mode 100644
index 0000000..dfa2dfd
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/p2p/base/transport.cc
@@ -0,0 +1,441 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "talk/base/common.h"
+#include "talk/p2p/base/transport.h"
+#include "talk/p2p/base/sessionmanager.h"
+#include "talk/p2p/base/transportchannelimpl.h"
+#include "talk/p2p/base/constants.h"
+#include "talk/xmllite/xmlelement.h"
+#include "talk/xmpp/constants.h"
+
+namespace {
+
+struct ChannelParams {
+ std::string name;
+ std::string session_type;
+ cricket::TransportChannelImpl* channel;
+ buzz::XmlElement* elem;
+
+ ChannelParams() : channel(NULL), elem(NULL) {}
+};
+typedef talk_base::TypedMessageData<ChannelParams*> ChannelMessage;
+
+const int MSG_CREATECHANNEL = 1;
+const int MSG_DESTROYCHANNEL = 2;
+const int MSG_DESTROYALLCHANNELS = 3;
+const int MSG_CONNECTCHANNELS = 4;
+const int MSG_RESETCHANNELS = 5;
+const int MSG_ONSIGNALINGREADY = 6;
+const int MSG_FORWARDCHANNELMESSAGE = 7;
+const int MSG_READSTATE = 8;
+const int MSG_WRITESTATE = 9;
+const int MSG_REQUESTSIGNALING = 10;
+const int MSG_ONCHANNELMESSAGE = 11;
+const int MSG_CONNECTING = 12;
+
+} // namespace
+
+namespace cricket {
+
+Transport::Transport(SessionManager* session_manager, const std::string& name)
+ : session_manager_(session_manager), name_(name), destroyed_(false),
+ readable_(false), writable_(false), connect_requested_(false),
+ allow_local_ips_(false) {
+ ASSERT(session_manager_->signaling_thread()->IsCurrent());
+}
+
+Transport::~Transport() {
+ ASSERT(session_manager_->signaling_thread()->IsCurrent());
+ ASSERT(destroyed_);
+}
+
+TransportChannelImpl* Transport::CreateChannel(const std::string& name, const std::string &session_type) {
+ ChannelParams params;
+ params.name = name;
+ params.session_type = session_type;
+ ChannelMessage msg(&params);
+ session_manager_->worker_thread()->Send(this, MSG_CREATECHANNEL, &msg);
+ return msg.data()->channel;
+}
+
+TransportChannelImpl* Transport::CreateChannel_w(const std::string& name, const std::string &session_type) {
+ ASSERT(session_manager_->worker_thread()->IsCurrent());
+
+ TransportChannelImpl* impl = CreateTransportChannel(name, session_type);
+ impl->SignalReadableState.connect(this, &Transport::OnChannelReadableState);
+ impl->SignalWritableState.connect(this, &Transport::OnChannelWritableState);
+ impl->SignalRequestSignaling.connect(
+ this, &Transport::OnChannelRequestSignaling);
+ impl->SignalChannelMessage.connect(this, &Transport::OnChannelMessage);
+
+ talk_base::CritScope cs(&crit_);
+ ASSERT(channels_.find(name) == channels_.end());
+ channels_[name] = impl;
+ destroyed_ = false;
+ if (connect_requested_) {
+ impl->Connect();
+ if (channels_.size() == 1) {
+ // If this is the first channel, then indicate that we have started
+ // connecting.
+ session_manager_->signaling_thread()->Post(this, MSG_CONNECTING, NULL);
+ }
+ }
+ return impl;
+}
+
+TransportChannelImpl* Transport::GetChannel(const std::string& name) {
+ talk_base::CritScope cs(&crit_);
+ ChannelMap::iterator iter = channels_.find(name);
+ return (iter != channels_.end()) ? iter->second : NULL;
+}
+
+bool Transport::HasChannels() {
+ talk_base::CritScope cs(&crit_);
+ return !channels_.empty();
+}
+
+void Transport::DestroyChannel(const std::string& name) {
+ ChannelParams params;
+ params.name = name;
+ ChannelMessage msg(&params);
+ session_manager_->worker_thread()->Send(this, MSG_DESTROYCHANNEL, &msg);
+}
+
+void Transport::DestroyChannel_w(const std::string& name) {
+ ASSERT(session_manager_->worker_thread()->IsCurrent());
+ TransportChannelImpl* impl = NULL;
+ {
+ talk_base::CritScope cs(&crit_);
+ ChannelMap::iterator iter = channels_.find(name);
+ ASSERT(iter != channels_.end());
+ impl = iter->second;
+ channels_.erase(iter);
+ }
+
+ if (connect_requested_ && channels_.empty()) {
+ // We're not longer attempting to connect.
+ session_manager_->signaling_thread()->Post(this, MSG_CONNECTING, NULL);
+ }
+
+ if (impl)
+ DestroyTransportChannel(impl);
+}
+
+void Transport::ConnectChannels() {
+ ASSERT(session_manager_->signaling_thread()->IsCurrent());
+ session_manager_->worker_thread()->Post(this, MSG_CONNECTCHANNELS, NULL);
+}
+
+void Transport::ConnectChannels_w() {
+ ASSERT(session_manager_->worker_thread()->IsCurrent());
+ if (connect_requested_)
+ return;
+ connect_requested_ = true;
+ session_manager_->signaling_thread()->Post(this, MSG_ONCHANNELMESSAGE, NULL);
+ CallChannels_w(&TransportChannelImpl::Connect);
+ if (!channels_.empty()) {
+ session_manager_->signaling_thread()->Post(this, MSG_CONNECTING, NULL);
+ }
+}
+
+void Transport::OnConnecting_s() {
+ ASSERT(session_manager_->signaling_thread()->IsCurrent());
+ SignalConnecting(this);
+}
+
+void Transport::DestroyAllChannels() {
+ ASSERT(session_manager_->signaling_thread()->IsCurrent());
+ session_manager_->worker_thread()->Send(this, MSG_DESTROYALLCHANNELS, NULL);
+ destroyed_ = true;
+}
+
+void Transport::DestroyAllChannels_w() {
+ ASSERT(session_manager_->worker_thread()->IsCurrent());
+ std::vector<TransportChannelImpl*> impls;
+ {
+ talk_base::CritScope cs(&crit_);
+ for (ChannelMap::iterator iter = channels_.begin();
+ iter != channels_.end();
+ ++iter) {
+ impls.push_back(iter->second);
+ }
+ channels_.clear();
+ }
+
+ for (size_t i = 0; i < impls.size(); ++i)
+ DestroyTransportChannel(impls[i]);
+}
+
+void Transport::ResetChannels() {
+ ASSERT(session_manager_->signaling_thread()->IsCurrent());
+ session_manager_->worker_thread()->Post(this, MSG_RESETCHANNELS, NULL);
+}
+
+void Transport::ResetChannels_w() {
+ ASSERT(session_manager_->worker_thread()->IsCurrent());
+
+ // We are no longer attempting to connect
+ connect_requested_ = false;
+
+ // Clear out the old messages, they aren't relevant
+ talk_base::CritScope cs(&crit_);
+ for (size_t i=0; i<messages_.size(); ++i) {
+ delete messages_[i];
+ }
+ messages_.clear();
+
+ // Reset all of the channels
+ CallChannels_w(&TransportChannelImpl::Reset);
+}
+
+void Transport::OnSignalingReady() {
+ ASSERT(session_manager_->signaling_thread()->IsCurrent());
+ session_manager_->worker_thread()->Post(this, MSG_ONSIGNALINGREADY, NULL);
+
+ // Notify the subclass.
+ OnTransportSignalingReady();
+}
+
+void Transport::CallChannels_w(TransportChannelFunc func) {
+ ASSERT(session_manager_->worker_thread()->IsCurrent());
+ talk_base::CritScope cs(&crit_);
+ for (ChannelMap::iterator iter = channels_.begin();
+ iter != channels_.end();
+ ++iter) {
+ ((iter->second)->*func)();
+ }
+}
+
+void Transport::ForwardChannelMessage(const std::string& name,
+ buzz::XmlElement* elem) {
+ ASSERT(session_manager_->signaling_thread()->IsCurrent());
+ ASSERT(HasChannel(name));
+ ChannelParams* params = new ChannelParams();
+ params->name = name;
+ params->elem = elem;
+ ChannelMessage* msg = new ChannelMessage(params);
+ session_manager_->worker_thread()->Post(this, MSG_FORWARDCHANNELMESSAGE, msg);
+}
+
+void Transport::ForwardChannelMessage_w(const std::string& name,
+ buzz::XmlElement* elem) {
+ ASSERT(session_manager_->worker_thread()->IsCurrent());
+ ChannelMap::iterator iter = channels_.find(name);
+ // It's ok for a channel to go away while this message is in transit.
+ if (iter != channels_.end()) {
+ iter->second->OnChannelMessage(elem);
+ }
+ delete elem;
+}
+
+void Transport::OnChannelReadableState(TransportChannel* channel) {
+ ASSERT(session_manager_->worker_thread()->IsCurrent());
+ session_manager_->signaling_thread()->Post(this, MSG_READSTATE, NULL);
+}
+
+void Transport::OnChannelReadableState_s() {
+ ASSERT(session_manager_->signaling_thread()->IsCurrent());
+ bool readable = GetTransportState_s(true);
+ if (readable_ != readable) {
+ readable_ = readable;
+ SignalReadableState(this);
+ }
+}
+
+void Transport::OnChannelWritableState(TransportChannel* channel) {
+ ASSERT(session_manager_->worker_thread()->IsCurrent());
+ session_manager_->signaling_thread()->Post(this, MSG_WRITESTATE, NULL);
+}
+
+void Transport::OnChannelWritableState_s() {
+ ASSERT(session_manager_->signaling_thread()->IsCurrent());
+ bool writable = GetTransportState_s(false);
+ if (writable_ != writable) {
+ writable_ = writable;
+ SignalWritableState(this);
+ }
+}
+
+bool Transport::GetTransportState_s(bool read) {
+ ASSERT(session_manager_->signaling_thread()->IsCurrent());
+ bool result = false;
+ talk_base::CritScope cs(&crit_);
+ for (ChannelMap::iterator iter = channels_.begin();
+ iter != channels_.end();
+ ++iter) {
+ bool b = (read ? iter->second->readable() : iter->second->writable());
+ result = result || b;
+ }
+ return result;
+}
+
+void Transport::OnChannelRequestSignaling() {
+ ASSERT(session_manager_->worker_thread()->IsCurrent());
+ session_manager_->signaling_thread()->Post(this, MSG_REQUESTSIGNALING, NULL);
+}
+
+void Transport::OnChannelRequestSignaling_s() {
+ ASSERT(session_manager_->signaling_thread()->IsCurrent());
+ SignalRequestSignaling(this);
+}
+
+void Transport::OnChannelMessage(TransportChannelImpl* impl,
+ buzz::XmlElement* elem) {
+ ASSERT(session_manager_->worker_thread()->IsCurrent());
+ talk_base::CritScope cs(&crit_);
+ messages_.push_back(elem);
+
+ // We hold any messages until the client lets us connect.
+ if (connect_requested_) {
+ session_manager_->signaling_thread()->Post(
+ this, MSG_ONCHANNELMESSAGE, NULL);
+ }
+}
+
+void Transport::OnChannelMessage_s() {
+ ASSERT(session_manager_->signaling_thread()->IsCurrent());
+ ASSERT(connect_requested_);
+
+ std::vector<buzz::XmlElement*> msgs;
+ {
+ talk_base::CritScope cs(&crit_);
+ msgs.swap(messages_);
+ }
+
+ if (!msgs.empty())
+ OnTransportChannelMessages(msgs);
+}
+
+void Transport::OnTransportChannelMessages(
+ const std::vector<buzz::XmlElement*>& msgs) {
+ std::vector<buzz::XmlElement*> elems;
+ for (size_t i = 0; i < msgs.size(); ++i) {
+ buzz::XmlElement* elem =
+ new buzz::XmlElement(buzz::QName(name(), "transport"));
+ elem->AddElement(msgs[i]);
+ elems.push_back(elem);
+ }
+ SignalTransportMessage(this, elems);
+}
+
+void Transport::OnMessage(talk_base::Message* msg) {
+ switch (msg->message_id) {
+ case MSG_CREATECHANNEL:
+ {
+ ChannelParams* params = static_cast<ChannelMessage*>(msg->pdata)->data();
+ params->channel = CreateChannel_w(params->name, params->session_type);
+ }
+ break;
+ case MSG_DESTROYCHANNEL:
+ {
+ ChannelParams* params = static_cast<ChannelMessage*>(msg->pdata)->data();
+ DestroyChannel_w(params->name);
+ }
+ break;
+ case MSG_CONNECTCHANNELS:
+ ConnectChannels_w();
+ break;
+ case MSG_RESETCHANNELS:
+ ResetChannels_w();
+ break;
+ case MSG_DESTROYALLCHANNELS:
+ DestroyAllChannels_w();
+ break;
+ case MSG_ONSIGNALINGREADY:
+ CallChannels_w(&TransportChannelImpl::OnSignalingReady);
+ break;
+ case MSG_FORWARDCHANNELMESSAGE:
+ {
+ ChannelParams* params = static_cast<ChannelMessage*>(msg->pdata)->data();
+ ForwardChannelMessage_w(params->name, params->elem);
+ delete params;
+ }
+ break;
+ case MSG_CONNECTING:
+ OnConnecting_s();
+ break;
+ case MSG_READSTATE:
+ OnChannelReadableState_s();
+ break;
+ case MSG_WRITESTATE:
+ OnChannelWritableState_s();
+ break;
+ case MSG_REQUESTSIGNALING:
+ OnChannelRequestSignaling_s();
+ break;
+ case MSG_ONCHANNELMESSAGE:
+ OnChannelMessage_s();
+ break;
+ }
+}
+
+bool Transport::BadRequest(const buzz::XmlElement* stanza,
+ const std::string& text,
+ const buzz::XmlElement* extra_info) {
+ SignalTransportError(this, stanza, buzz::QN_STANZA_BAD_REQUEST, "modify",
+ text, extra_info);
+ return false;
+}
+
+bool Transport::ParseAddress(const buzz::XmlElement* stanza,
+ const buzz::XmlElement* elem,
+ talk_base::SocketAddress* address) {
+ ASSERT(elem->HasAttr(QN_ADDRESS));
+ ASSERT(elem->HasAttr(QN_PORT));
+
+ // Record the parts of the address.
+ address->SetIP(elem->Attr(QN_ADDRESS));
+ std::istringstream ist(elem->Attr(QN_PORT));
+ int port;
+ ist >> port;
+ address->SetPort(port);
+
+ // No address zero.
+ if (address->IsAny())
+ return BadRequest(stanza, "candidate has address of zero", NULL);
+
+ // Always disallow addresses that refer to the local host.
+ if (address->IsLocalIP() && !allow_local_ips_)
+ return BadRequest(stanza, "candidate has local IP address", NULL);
+
+ // Disallow all ports below 1024, except for 80 and 443 on public addresses.
+ if (port < 1024) {
+ if ((port != 80) && (port != 443))
+ return BadRequest(stanza,
+ "candidate has port below 1024, but not 80 or 443",
+ NULL);
+ if (address->IsPrivateIP()) {
+ return BadRequest(stanza, "candidate has port of 80 or 443 with private "
+ "IP address", NULL);
+ }
+ }
+
+ return true;
+}
+
+} // namespace cricket
diff --git a/Plugins/jingle/libjingle/talk/p2p/base/transport.h b/Plugins/jingle/libjingle/talk/p2p/base/transport.h
new file mode 100644
index 0000000..826fd26
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/p2p/base/transport.h
@@ -0,0 +1,277 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+// A Transport manages a set of named channels of the same type.
+//
+// Subclasses choose the appropriate class to instantiate for each channel;
+// however, this base class keeps track of the channels by name, watches their
+// state changes (in order to update the manager's state), and forwards
+// requests to begin connecting or to reset to each of the channels.
+//
+// On Threading: Transport performs work on both the signaling and worker
+// threads. For subclasses, the rule is that all signaling related calls will
+// be made on the signaling thread and all channel related calls (including
+// signaling for a channel) will be made on the worker thread. When
+// information needs to be sent between the two threads, this class should do
+// the work (e.g., ForwardChannelMessage).
+//
+// Note: Subclasses must call DestroyChannels() in their own constructors.
+// It is not possible to do so here because the subclass constructor will
+// already have run.
+
+#ifndef _CRICKET_P2P_BASE_TRANSPORT_H_
+#define _CRICKET_P2P_BASE_TRANSPORT_H_
+
+#include <string>
+#include <map>
+#include <vector>
+#include "talk/base/criticalsection.h"
+#include "talk/base/messagequeue.h"
+#include "talk/base/sigslot.h"
+
+namespace buzz {
+class QName;
+class XmlElement;
+}
+
+namespace cricket {
+
+class SessionManager;
+class Session;
+class TransportChannel;
+class TransportChannelImpl;
+
+class Transport : public talk_base::MessageHandler, public sigslot::has_slots<> {
+ public:
+ Transport(SessionManager* session_manager, const std::string& name);
+ virtual ~Transport();
+
+ // Returns a pointer to the singleton session manager.
+ SessionManager* session_manager() const { return session_manager_; }
+
+ // Returns the name of this transport.
+ const std::string& name() const { return name_; }
+
+ // Returns the readable and states of this manager. These bits are the ORs
+ // of the corresponding bits on the managed channels. Each time one of these
+ // states changes, a signal is raised.
+ bool readable() const { return readable_; }
+ bool writable() const { return writable_; }
+ sigslot::signal1<Transport*> SignalReadableState;
+ sigslot::signal1<Transport*> SignalWritableState;
+
+ // Returns whether the client has requested the channels to connect.
+ bool connect_requested() const { return connect_requested_; }
+
+ // Create, destroy, and lookup the channels of this type by their names.
+ TransportChannelImpl* CreateChannel(const std::string& name, const std::string &session_type);
+ // Note: GetChannel may lead to race conditions, since the mutex is not held
+ // after the pointer is returned.
+ TransportChannelImpl* GetChannel(const std::string& name);
+ // Note: HasChannel does not lead to race conditions, unlike GetChannel.
+ bool HasChannel(const std::string& name) { return (NULL != GetChannel(name)); }
+ bool HasChannels();
+ void DestroyChannel(const std::string& name);
+
+ // Tells all current and future channels to start connecting. When the first
+ // channel begins connecting, the following signal is raised.
+ void ConnectChannels();
+ sigslot::signal1<Transport*> SignalConnecting;
+
+ // Resets all of the channels back to their initial state. They are no
+ // longer connecting.
+ void ResetChannels();
+
+ // Destroys every channel created so far.
+ void DestroyAllChannels();
+
+ // The session handshake includes negotiation of both the application and the
+ // transport. The initiating transport creates an "offer" describing what
+ // options it supports and the responding transport creates an "answer"
+ // describing which options it has accepted. If OnTransport* returns false,
+ // that indicates that no acceptable options given and this transport cannot
+ // be negotiated.
+ //
+ // The transport negotiation operates as follows. When the initiating client
+ // creates the session, but before they send the initiate, we create the
+ // supported transports. The client may override these, but otherwise they
+ // get a default set. When the initiate is sent, we ask each transport to
+ // produce an offer. When the receiving client gets the initiate, they will
+ // iterate through the transport offers in order of their own preference.
+ // For each one, they create the transport (if they know what it is) and
+ // call OnTransportOffer. If this returns true, then we're good; otherwise,
+ // we continue iterating. If no transport works, then we reject the session.
+ // Otherwise, we have a single transport, and we send back a transport-accept
+ // message that contains the answer. When this arrives at the initiating
+ // client, we destroy all transports but the one in the answer and then pass
+ // the answer to it. If this transport cannot be found or it cannot accept
+ // the answer, then we reject the session. Otherwise, we're in good shape.
+ virtual buzz::XmlElement* CreateTransportOffer() = 0;
+ virtual buzz::XmlElement* CreateTransportAnswer() = 0;
+ virtual bool OnTransportOffer(const buzz::XmlElement* elem) = 0;
+ virtual bool OnTransportAnswer(const buzz::XmlElement* elem) = 0;
+
+ // Before any stanza is sent, the manager will request signaling. Once
+ // signaling is available, the client should call OnSignalingReady. Once
+ // this occurs, the transport (or its channels) can send any waiting stanzas.
+ // OnSignalingReady invokes OnTransportSignalingReady and then forwards this
+ // signal to each channel.
+ sigslot::signal1<Transport*> SignalRequestSignaling;
+ void OnSignalingReady();
+
+ // Handles sending and receiving of stanzas related to negotiating the
+ // connections of the channels. Different transports may have very different
+ // signaling, so the XML is handled by the subclass. The msg variable holds
+ // the element whose name matches this transport, while stanza holds the
+ // entire stanza. The latter is needed when sending an error response.
+ // SignalTransportMessage is given the elements that will become the children
+ // of the transport-info message. Each element must have a name that matches
+ // the transport's name.
+ virtual bool OnTransportMessage(const buzz::XmlElement* msg,
+ const buzz::XmlElement* stanza) = 0;
+ sigslot::signal2<Transport*, const std::vector<buzz::XmlElement*>&>
+ SignalTransportMessage;
+
+ // A transport message has generated an transport-specific error. The
+ // stanza that caused the error is available in session_msg. If false is
+ // returned, the error is considered unrecoverable, and the session is
+ // terminated.
+ virtual bool OnTransportError(const buzz::XmlElement* session_msg,
+ const buzz::XmlElement* error) = 0;
+ sigslot::signal6<Transport*, const buzz::XmlElement*, const buzz::QName&,
+ const std::string&, const std::string&,
+ const buzz::XmlElement*>
+ SignalTransportError;
+
+ sigslot::signal2<Transport*, const std::string&> SignalChannelGone;
+
+ // (For testing purposes only.) This indicates whether we will allow local
+ // IPs (e.g. 127.*) to be used as addresses for P2P.
+ bool allow_local_ips() const { return allow_local_ips_; }
+ void set_allow_local_ips(bool value) { allow_local_ips_ = value; }
+
+ protected:
+ // Helper function to bad-request error for a stanza passed to
+ // OnTransportMessage. Returns false.
+ bool BadRequest(const buzz::XmlElement* stanza, const std::string& text,
+ const buzz::XmlElement* extra_info);
+
+ // Helper function to parse an element describing an address. This retrieves
+ // the IP and port from the given element (using QN_ADDRESS and QN_PORT) and
+ // verifies that they look like plausible values.
+ bool ParseAddress(const buzz::XmlElement* stanza,
+ const buzz::XmlElement* elem,
+ talk_base::SocketAddress* address);
+
+ // These are called by Create/DestroyChannel above in order to create or
+ // destroy the appropriate type of channel.
+ virtual TransportChannelImpl* CreateTransportChannel(
+ const std::string& name, const std::string &session_type) = 0;
+ virtual void DestroyTransportChannel(TransportChannelImpl* channel) = 0;
+
+ // Informs the subclass that we received the signaling ready message.
+ virtual void OnTransportSignalingReady() {}
+
+ // Forwards the given XML element to the channel on the worker thread. This
+ // occurs asynchronously, so we take ownership of the element. Furthermore,
+ // the channel will not be able to return an error if the XML is invalid, so
+ // the transport should have checked its validity already.
+ void ForwardChannelMessage(const std::string& name,
+ buzz::XmlElement* elem);
+
+ // Handles a set of messages sent by the channels. The default
+ // implementation simply forwards each as its own transport message by
+ // wrapping it in an element identifying this transport and then invoking
+ // SignalTransportMessage. Smarter transports may be able to place multiple
+ // channel messages within one transport message.
+ //
+ // Note: The implementor of this method is responsible for deleting the XML
+ // elements passed in, unless they are sent to SignalTransportMessage, where
+ // the receiver will delete them.
+ virtual void OnTransportChannelMessages(
+ const std::vector<buzz::XmlElement*>& msgs);
+
+ private:
+ typedef std::map<std::string, TransportChannelImpl*> ChannelMap;
+ typedef std::vector<buzz::XmlElement*> XmlElementList;
+
+ SessionManager* session_manager_;
+ std::string name_;
+ bool destroyed_;
+ bool readable_;
+ bool writable_;
+ bool connect_requested_;
+ ChannelMap channels_;
+ XmlElementList messages_;
+ talk_base::CriticalSection crit_; // Protects changes to channels and messages
+ bool allow_local_ips_;
+
+ // Called when the state of a channel changes.
+ void OnChannelReadableState(TransportChannel* channel);
+ void OnChannelWritableState(TransportChannel* channel);
+
+ // Called when a channel requests signaling.
+ void OnChannelRequestSignaling();
+
+ // Called when a channel wishes to send a transport message.
+ void OnChannelMessage(TransportChannelImpl* impl, buzz::XmlElement* elem);
+
+ // Dispatches messages to the appropriate handler (below).
+ void OnMessage(talk_base::Message* msg);
+
+ // These are versions of the above methods that are called only on a
+ // particular thread (s = signaling, w = worker). The above methods post or
+ // send a message to invoke this version.
+ TransportChannelImpl* CreateChannel_w(const std::string& name, const std::string& session_type);
+ void DestroyChannel_w(const std::string& name);
+ void ConnectChannels_w();
+ void ResetChannels_w();
+ void DestroyAllChannels_w();
+ void ForwardChannelMessage_w(const std::string& name,
+ buzz::XmlElement* elem);
+ void OnChannelReadableState_s();
+ void OnChannelWritableState_s();
+ void OnChannelRequestSignaling_s();
+ void OnConnecting_s();
+
+ // Helper function that invokes the given function on every channel.
+ typedef void (TransportChannelImpl::* TransportChannelFunc)();
+ void CallChannels_w(TransportChannelFunc func);
+
+ // Computes the OR of the channel's read or write state (argument picks).
+ bool GetTransportState_s(bool read);
+
+ // Invoked when there are messages waiting to send in the messages_ list.
+ // We wait to send any messages until the client asks us to connect.
+ void OnChannelMessage_s();
+
+ DISALLOW_EVIL_CONSTRUCTORS(Transport);
+};
+
+} // namespace cricket
+
+#endif // _CRICKET_P2P_BASE_TRANSPORT_H_
diff --git a/Plugins/jingle/libjingle/talk/p2p/base/transportchannel.cc b/Plugins/jingle/libjingle/talk/p2p/base/transportchannel.cc
new file mode 100644
index 0000000..ba076ac
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/p2p/base/transportchannel.cc
@@ -0,0 +1,56 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sstream>
+#include "talk/p2p/base/transportchannel.h"
+
+namespace cricket {
+
+std::string TransportChannel::ToString() const {
+ const char READABLE_ABBREV[2] = { '_', 'R' };
+ const char WRITABLE_ABBREV[2] = { '_', 'W' };
+ std::stringstream ss;
+ ss << "Channel[" << name_ << "|" << READABLE_ABBREV[readable_]
+ << WRITABLE_ABBREV[writable_] << "]";
+ return ss.str();
+}
+
+void TransportChannel::set_readable(bool readable) {
+ if (readable_ != readable) {
+ readable_ = readable;
+ SignalReadableState(this);
+ }
+}
+
+void TransportChannel::set_writable(bool writable) {
+ if (writable_ != writable) {
+ writable_ = writable;
+ SignalWritableState(this);
+ }
+}
+
+} // namespace cricket
diff --git a/Plugins/jingle/libjingle/talk/p2p/base/transportchannel.h b/Plugins/jingle/libjingle/talk/p2p/base/transportchannel.h
new file mode 100644
index 0000000..68b8fd1
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/p2p/base/transportchannel.h
@@ -0,0 +1,104 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _CRICKET_P2P_BASE_TRANSPORTCHANNEL_H_
+#define _CRICKET_P2P_BASE_TRANSPORTCHANNEL_H_
+
+#include <string>
+#include "talk/base/basictypes.h"
+#include "talk/base/sigslot.h"
+#include "talk/base/socket.h"
+
+namespace cricket {
+
+// A TransportChannel represents one logical stream of packets that are sent
+// between the two sides of a session.
+class TransportChannel: public sigslot::has_slots<> {
+ public:
+ TransportChannel(const std::string& name, const std::string &session_type)
+ : name_(name), session_type_(session_type), readable_(false), writable_(false) {}
+ virtual ~TransportChannel() {}
+
+ // Returns the name of this channel.
+ const std::string& name() const { return name_; }
+ const std::string& session_type() const { return session_type_; }
+
+ // Returns the readable and states of this channel. Each time one of these
+ // states changes, a signal is raised. These states are aggregated by the
+ // TransportManager.
+ bool readable() const { return readable_; }
+ bool writable() const { return writable_; }
+ sigslot::signal1<TransportChannel*> SignalReadableState;
+ sigslot::signal1<TransportChannel*> SignalWritableState;
+
+ // Attempts to send the given packet. The return value is < 0 on failure.
+ virtual int SendPacket(const char *data, size_t len) = 0;
+
+ // Sets a socket option on this channel. Note that not all options are
+ // supported by all transport types.
+ virtual int SetOption(talk_base::Socket::Option opt, int value) = 0;
+
+ // Returns the most recent error that occurred on this channel.
+ virtual int GetError() = 0;
+
+ // Signalled each time a packet is received on this channel.
+ sigslot::signal3<TransportChannel*, const char*, size_t> SignalReadPacket;
+
+ // This signal occurs when there is a change in the way that packets are
+ // being routed. The address indicates the address of the first hop in the
+ // new route, if this is known. If this cannot be determined or is not well-
+ // defined, then the channel may give an address of 0.
+ sigslot::signal2<TransportChannel*, const talk_base::SocketAddress&>
+ SignalRouteChange;
+
+ // Invoked when the channel is being destroyed.
+ sigslot::signal1<TransportChannel*> SignalDestroyed;
+
+ // TODO: Generalize network monitoring.
+
+ // Debugging description of this transport channel.
+ std::string ToString() const;
+
+ protected:
+ // Sets the readable state, signaling if necessary.
+ void set_readable(bool readable);
+
+ // Sets the writable state, signaling if necessary.
+ void set_writable(bool writable);
+
+ private:
+ std::string name_;
+ std::string session_type_;
+ bool readable_;
+ bool writable_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(TransportChannel);
+};
+
+} // namespace cricket
+
+#endif // _CRICKET_P2P_BASE_TRANSPORTCHANNEL_H_
diff --git a/Plugins/jingle/libjingle/talk/p2p/base/transportchannelimpl.h b/Plugins/jingle/libjingle/talk/p2p/base/transportchannelimpl.h
new file mode 100644
index 0000000..742d307
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/p2p/base/transportchannelimpl.h
@@ -0,0 +1,81 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _CRICKET_P2P_BASE_TRANSPORTCHANNELIMPL_H_
+#define _CRICKET_P2P_BASE_TRANSPORTCHANNELIMPL_H_
+
+#include <string>
+#include "talk/p2p/base/transportchannel.h"
+
+namespace buzz { class XmlElement; }
+
+namespace cricket {
+
+class Transport;
+
+// Base class for real implementations of TransportChannel. This includes some
+// methods called only by Transport, which do not need to be exposed to the
+// client.
+class TransportChannelImpl: public TransportChannel {
+ public:
+ TransportChannelImpl(const std::string& name, const std::string& session_type)
+ : TransportChannel(name, session_type) {}
+
+ // Returns the transport that created this channel.
+ virtual Transport* GetTransport() = 0;
+
+ // Begins the process of attempting to make a connection to the other client.
+ virtual void Connect() = 0;
+
+ // Resets this channel back to the initial state (i.e., not connecting).
+ virtual void Reset() = 0;
+
+ // Allows an individual channel to request signaling and be notified when it
+ // is ready. This is useful if the individual named channels have need to
+ // send their own transport-info stanzas.
+ sigslot::signal0<> SignalRequestSignaling;
+ virtual void OnSignalingReady() = 0;
+
+ // Handles sending and receiving of stanzas related to this particular
+ // channel. Any channel may send whatever messages it wants. The Transport
+ // receives all incoming messages and may forward them to the relevant
+ // channel. The transport will delete signaled messages.
+ //
+ // Note: Since these messages are delivered asynchronously to the channel,
+ // they cannot return an error if the message is invalid. It is assumed that
+ // the Transport will have checked validity before forwarding.
+ virtual void OnChannelMessage(const buzz::XmlElement* msg) = 0;
+ sigslot::signal2<TransportChannelImpl*,
+ buzz::XmlElement*> SignalChannelMessage;
+
+ private:
+ DISALLOW_EVIL_CONSTRUCTORS(TransportChannelImpl);
+};
+
+} // namespace cricket
+
+#endif // _CRICKET_P2P_BASE_TRANSPORTCHANNELIMPL_H_
diff --git a/Plugins/jingle/libjingle/talk/p2p/base/transportchannelproxy.cc b/Plugins/jingle/libjingle/talk/p2p/base/transportchannelproxy.cc
new file mode 100644
index 0000000..bc94d67
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/p2p/base/transportchannelproxy.cc
@@ -0,0 +1,99 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "talk/p2p/base/transportchannelproxy.h"
+#include "talk/base/common.h"
+#include "talk/p2p/base/transport.h"
+#include "talk/p2p/base/transportchannelimpl.h"
+
+namespace cricket {
+
+TransportChannelProxy::TransportChannelProxy(const std::string& name, const std::string &session_type)
+ : TransportChannel(name, session_type), impl_(NULL) {
+}
+
+TransportChannelProxy::~TransportChannelProxy() {
+ if (impl_)
+ impl_->GetTransport()->DestroyChannel(impl_->name());
+}
+
+void TransportChannelProxy::SetImplementation(TransportChannelImpl* impl) {
+ impl_ = impl;
+ impl_->SignalReadableState.connect(
+ this, &TransportChannelProxy::OnReadableState);
+ impl_->SignalWritableState.connect(
+ this, &TransportChannelProxy::OnWritableState);
+ impl_->SignalReadPacket.connect(this, &TransportChannelProxy::OnReadPacket);
+ impl_->SignalRouteChange.connect(this, &TransportChannelProxy::OnRouteChange);
+ for (OptionList::iterator it = pending_options_.begin();
+ it != pending_options_.end();
+ ++it) {
+ impl_->SetOption(it->first, it->second);
+ }
+ pending_options_.clear();
+}
+
+int TransportChannelProxy::SendPacket(const char *data, size_t len) {
+ ASSERT(impl_ != NULL); // should not be used until channel is writable
+ return impl_->SendPacket(data, len);
+}
+
+int TransportChannelProxy::SetOption(talk_base::Socket::Option opt, int value) {
+ if (impl_)
+ return impl_->SetOption(opt, value);
+ pending_options_.push_back(OptionPair(opt, value));
+ return 0;
+}
+
+int TransportChannelProxy::GetError() {
+ ASSERT(impl_ != NULL); // should not be used until channel is writable
+ return impl_->GetError();
+}
+
+void TransportChannelProxy::OnReadableState(TransportChannel* channel) {
+ ASSERT(channel == impl_);
+ set_readable(impl_->readable());
+}
+
+void TransportChannelProxy::OnWritableState(TransportChannel* channel) {
+ ASSERT(channel == impl_);
+ set_writable(impl_->writable());
+}
+
+void TransportChannelProxy::OnReadPacket(
+ TransportChannel* channel, const char* data, size_t size) {
+ ASSERT(channel == impl_);
+ SignalReadPacket(this, data, size);
+}
+
+void TransportChannelProxy::OnRouteChange(TransportChannel* channel,
+ const talk_base::SocketAddress& address) {
+ ASSERT(channel == impl_);
+ SignalRouteChange(this, address);
+}
+
+} // namespace cricket
diff --git a/Plugins/jingle/libjingle/talk/p2p/base/transportchannelproxy.h b/Plugins/jingle/libjingle/talk/p2p/base/transportchannelproxy.h
new file mode 100644
index 0000000..46d8a44
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/p2p/base/transportchannelproxy.h
@@ -0,0 +1,77 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _CRICKET_P2P_BASE_TRANSPORTCHANNELPROXY_H_
+#define _CRICKET_P2P_BASE_TRANSPORTCHANNELPROXY_H_
+
+#include <string>
+#include <vector>
+#include "talk/p2p/base/transportchannel.h"
+
+namespace cricket {
+
+class TransportChannelImpl;
+
+// Proxies calls between the client and the transport channel implementation.
+// This is needed because clients are allowed to create channels before the
+// network negotiation is complete. Hence, we create a proxy up front, and
+// when negotiation completes, connect the proxy to the implementaiton.
+class TransportChannelProxy: public TransportChannel {
+ public:
+ TransportChannelProxy(const std::string& name, const std::string &session_type);
+ virtual ~TransportChannelProxy();
+
+ TransportChannelImpl* impl() const { return impl_; }
+
+ // Sets the implementation to which we will proxy.
+ void SetImplementation(TransportChannelImpl* impl);
+
+ // Implementation of the TransportChannel interface. These simply forward to
+ // the implementation.
+ virtual int SendPacket(const char *data, size_t len);
+ virtual int SetOption(talk_base::Socket::Option opt, int value);
+ virtual int GetError();
+
+ private:
+ typedef std::pair<talk_base::Socket::Option, int> OptionPair;
+ typedef std::vector<OptionPair> OptionList;
+ TransportChannelImpl* impl_;
+ OptionList pending_options_;
+
+ // Catch signals from the implementation channel. These just forward to the
+ // client (after updating our state to match).
+ void OnReadableState(TransportChannel* channel);
+ void OnWritableState(TransportChannel* channel);
+ void OnReadPacket(TransportChannel* channel, const char* data, size_t size);
+ void OnRouteChange(TransportChannel* channel, const talk_base::SocketAddress& address);
+
+ DISALLOW_EVIL_CONSTRUCTORS(TransportChannelProxy);
+};
+
+} // namespace cricket
+
+#endif // _CRICKET_P2P_BASE_TRANSPORTCHANNELPROXY_H_
diff --git a/Plugins/jingle/libjingle/talk/p2p/base/udpport.cc b/Plugins/jingle/libjingle/talk/p2p/base/udpport.cc
new file mode 100644
index 0000000..fa47eba
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/p2p/base/udpport.cc
@@ -0,0 +1,121 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#if defined(_MSC_VER) && _MSC_VER < 1300
+#pragma warning(disable:4786)
+#endif
+#include "talk/base/logging.h"
+#include "talk/p2p/base/udpport.h"
+#include <iostream>
+#include <cassert>
+
+#if defined(_MSC_VER) && _MSC_VER < 1300
+namespace std {
+ using ::strerror;
+}
+#endif
+
+#ifdef POSIX
+extern "C" {
+#include <errno.h>
+}
+#endif // POSIX
+
+namespace cricket {
+
+const std::string LOCAL_PORT_TYPE("local");
+
+UDPPort::UDPPort(talk_base::Thread* thread, talk_base::SocketFactory* factory,
+ talk_base::Network* network,
+ const talk_base::SocketAddress& address)
+ : Port(thread, LOCAL_PORT_TYPE, factory, network), error_(0) {
+ socket_ = CreatePacketSocket(PROTO_UDP);
+ socket_->SignalReadPacket.connect(this, &UDPPort::OnReadPacketSlot);
+ if (socket_->Bind(address) < 0)
+ PLOG(LERROR, socket_->GetError()) << "bind";
+}
+
+UDPPort::UDPPort(talk_base::Thread* thread, const std::string &type,
+ talk_base::SocketFactory* factory, talk_base::Network* network)
+ : Port(thread, type, factory, network), socket_(0), error_(0) {
+}
+
+UDPPort::~UDPPort() {
+ delete socket_;
+}
+
+void UDPPort::PrepareAddress() {
+ assert(socket_);
+ AddAddress(socket_->GetLocalAddress(), "udp", true);
+}
+
+Connection* UDPPort::CreateConnection(const Candidate& address,
+ CandidateOrigin origin) {
+ if (address.protocol() != "udp")
+ return 0;
+
+ Connection * conn = new ProxyConnection(this, 0, address);
+ AddConnection(conn);
+ return conn;
+}
+
+int UDPPort::SendTo(const void* data, size_t size,
+ const talk_base::SocketAddress& addr, bool payload) {
+ assert(socket_);
+ int sent = socket_->SendTo(data, size, addr);
+ if (sent < 0)
+ error_ = socket_->GetError();
+ return sent;
+}
+
+int UDPPort::SetOption(talk_base::Socket::Option opt, int value) {
+ return socket_->SetOption(opt, value);
+}
+
+int UDPPort::GetError() {
+ assert(socket_);
+ return error_;
+}
+
+void UDPPort::OnReadPacketSlot(
+ const char* data, size_t size, const talk_base::SocketAddress& remote_addr,
+ talk_base::AsyncPacketSocket* socket) {
+ assert(socket == socket_);
+ OnReadPacket(data, size, remote_addr);
+}
+
+void UDPPort::OnReadPacket(
+ const char* data, size_t size,
+ const talk_base::SocketAddress& remote_addr) {
+ if (Connection* conn = GetConnection(remote_addr)) {
+ conn->OnReadPacket(data, size);
+ } else {
+ Port::OnReadPacket(data, size, remote_addr);
+ }
+}
+
+} // namespace cricket
diff --git a/Plugins/jingle/libjingle/talk/p2p/base/udpport.h b/Plugins/jingle/libjingle/talk/p2p/base/udpport.h
new file mode 100644
index 0000000..9fcfa69
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/p2p/base/udpport.h
@@ -0,0 +1,90 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __UDPPORT_H__
+#define __UDPPORT_H__
+
+#include "talk/base/asyncudpsocket.h"
+#include "talk/p2p/base/port.h"
+
+namespace talk_base {
+ class Thread;
+ class Network;
+ class SocketAddress;
+} // namespace talk_base
+
+namespace cricket {
+
+extern const std::string LOCAL_PORT_TYPE; // type of UDP ports
+
+// Communicates using a local UDP port.
+//
+// This class is designed to allow subclasses to take advantage of the
+// connection management provided by this class. A subclass should take of all
+// packet sending and preparation, but when a packet is received, it should
+// call this UDPPort::OnReadPacket (3 arg) to dispatch to a connection.
+class UDPPort : public Port {
+public:
+ UDPPort(talk_base::Thread* thread, talk_base::SocketFactory* factory,
+ talk_base::Network* network, const talk_base::SocketAddress& address);
+ virtual ~UDPPort();
+
+ virtual void PrepareAddress();
+ virtual Connection* CreateConnection(const Candidate& address, CandidateOrigin origin);
+
+ virtual int SetOption(talk_base::Socket::Option opt, int value);
+ virtual int GetError();
+
+protected:
+ UDPPort(talk_base::Thread* thread, const std::string &type,
+ talk_base::SocketFactory* factory, talk_base::Network* network);
+
+ // Handles sending using the local UDP socket.
+ virtual int SendTo(const void* data, size_t size,
+ const talk_base::SocketAddress& addr, bool payload);
+
+ // Dispatches the given packet to the port or connection as appropriate.
+ void OnReadPacket(
+ const char* data, size_t size,
+ const talk_base::SocketAddress& remote_addr);
+
+ talk_base::AsyncPacketSocket* socket() { return socket_; }
+
+private:
+ talk_base::AsyncPacketSocket* socket_;
+ int error_;
+
+ // Receives packet signal from the local UDP Socket.
+ void OnReadPacketSlot(
+ const char* data, size_t size,
+ const talk_base::SocketAddress& remote_addr,
+ talk_base::AsyncPacketSocket* socket);
+};
+
+} // namespace cricket
+
+#endif // __UDPPORT_H__
diff --git a/Plugins/jingle/libjingle/talk/p2p/client/basicportallocator.cc b/Plugins/jingle/libjingle/talk/p2p/client/basicportallocator.cc
new file mode 100644
index 0000000..b7960f4
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/p2p/client/basicportallocator.cc
@@ -0,0 +1,690 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#if defined(_MSC_VER) && _MSC_VER < 1300
+#pragma warning(disable:4786)
+#endif
+
+#include "talk/base/common.h"
+#include "talk/base/helpers.h"
+#include "talk/base/host.h"
+#include "talk/base/logging.h"
+#include "talk/p2p/client/basicportallocator.h"
+#include "talk/p2p/base/common.h"
+#include "talk/p2p/base/port.h"
+#include "talk/p2p/base/relayport.h"
+#include "talk/p2p/base/stunport.h"
+#include "talk/p2p/base/tcpport.h"
+#include "talk/p2p/base/udpport.h"
+
+namespace {
+
+const uint32 MSG_CONFIG_START = 1;
+const uint32 MSG_CONFIG_READY = 2;
+const uint32 MSG_ALLOCATE = 3;
+const uint32 MSG_ALLOCATION_PHASE = 4;
+const uint32 MSG_SHAKE = 5;
+
+const uint32 ALLOCATE_DELAY = 250;
+const uint32 ALLOCATION_STEP_DELAY = 1 * 1000;
+
+const int PHASE_UDP = 0;
+const int PHASE_RELAY = 1;
+const int PHASE_TCP = 2;
+const int PHASE_SSLTCP = 3;
+const int kNumPhases = 4;
+
+const float PREF_LOCAL_UDP = 1.0f;
+const float PREF_LOCAL_STUN = 0.9f;
+const float PREF_LOCAL_TCP = 0.8f;
+const float PREF_RELAY = 0.5f;
+
+const float RELAY_PRIMARY_PREF_MODIFIER = 0.0f; // modifiers of the above constants
+const float RELAY_BACKUP_PREF_MODIFIER = -0.2f;
+
+
+// Returns the phase in which a given local candidate (or rather, the port that
+// gave rise to that local candidate) would have been created.
+int LocalCandidateToPhase(const cricket::Candidate& candidate) {
+ cricket::ProtocolType proto;
+ bool result = cricket::StringToProto(candidate.protocol().c_str(), proto);
+ if (result) {
+ if (candidate.type() == cricket::LOCAL_PORT_TYPE) {
+ switch (proto) {
+ case cricket::PROTO_UDP: return PHASE_UDP;
+ case cricket::PROTO_TCP: return PHASE_TCP;
+ default: assert(false);
+ }
+ } else if (candidate.type() == cricket::STUN_PORT_TYPE) {
+ return PHASE_UDP;
+ } else if (candidate.type() == cricket::RELAY_PORT_TYPE) {
+ switch (proto) {
+ case cricket::PROTO_UDP: return PHASE_RELAY;
+ case cricket::PROTO_TCP: return PHASE_TCP;
+ case cricket::PROTO_SSLTCP: return PHASE_SSLTCP;
+ default: assert(false);
+ }
+ } else {
+ assert(false);
+ }
+ } else {
+ assert(false);
+ }
+ return PHASE_UDP; // reached only with assert failure
+}
+
+const int SHAKE_MIN_DELAY = 45 * 1000; // 45 seconds
+const int SHAKE_MAX_DELAY = 90 * 1000; // 90 seconds
+
+int ShakeDelay() {
+ int range = SHAKE_MAX_DELAY - SHAKE_MIN_DELAY + 1;
+ return SHAKE_MIN_DELAY + cricket::CreateRandomId() % range;
+}
+
+}
+
+namespace cricket {
+
+// Performs the allocation of ports, in a sequenced (timed) manner, for a given
+// network and IP address.
+class AllocationSequence : public talk_base::MessageHandler {
+public:
+ AllocationSequence(BasicPortAllocatorSession* session,
+ talk_base::Network* network,
+ PortConfiguration* config);
+ ~AllocationSequence();
+
+ // Determines whether this sequence is operating on an equivalent network
+ // setup to the one given.
+ bool IsEquivalent(talk_base::Network* network);
+
+ // Starts and stops the sequence. When started, it will continue allocating
+ // new ports on its own timed schedule.
+ void Start();
+ void Stop();
+
+ // MessageHandler:
+ void OnMessage(talk_base::Message* msg);
+
+ void EnableProtocol(ProtocolType proto);
+ bool ProtocolEnabled(ProtocolType proto) const;
+
+private:
+ BasicPortAllocatorSession* session_;
+ talk_base::Network* network_;
+ uint32 ip_;
+ PortConfiguration* config_;
+ bool running_;
+ int step_;
+ int step_of_phase_[kNumPhases];
+
+ typedef std::vector<ProtocolType> ProtocolList;
+ ProtocolList protocols_;
+
+ void CreateUDPPorts();
+ void CreateTCPPorts();
+ void CreateStunPorts();
+ void CreateRelayPorts();
+};
+
+
+// BasicPortAllocator
+
+BasicPortAllocator::BasicPortAllocator(
+ talk_base::NetworkManager* network_manager)
+ : network_manager_(network_manager), best_writable_phase_(-1),
+ stun_address_(NULL), relay_address_(NULL) {
+}
+
+BasicPortAllocator::BasicPortAllocator(
+ talk_base::NetworkManager* network_manager,
+ talk_base::SocketAddress* stun_address,
+ talk_base::SocketAddress *relay_address)
+ : network_manager_(network_manager), best_writable_phase_(-1),
+ stun_address_(stun_address), relay_address_(relay_address) {
+}
+
+BasicPortAllocator::~BasicPortAllocator() {
+}
+
+int BasicPortAllocator::best_writable_phase() const {
+ // If we are configured with an HTTP proxy, the best bet is to use the relay
+ if ((best_writable_phase_ == -1)
+ && ((proxy().type == talk_base::PROXY_HTTPS)
+ || (proxy().type == talk_base::PROXY_UNKNOWN))) {
+ return PHASE_RELAY;
+ }
+ return best_writable_phase_;
+}
+
+PortAllocatorSession *BasicPortAllocator::CreateSession(
+ const std::string &name, const std::string &session_type) {
+ return new BasicPortAllocatorSession(this, name, session_type, stun_address_,
+ relay_address_);
+}
+
+void BasicPortAllocator::AddWritablePhase(int phase) {
+ if ((best_writable_phase_ == -1) || (phase < best_writable_phase_))
+ best_writable_phase_ = phase;
+}
+
+// BasicPortAllocatorSession
+
+BasicPortAllocatorSession::BasicPortAllocatorSession(
+ BasicPortAllocator *allocator,
+ const std::string &name,
+ const std::string &session_type)
+ : PortAllocatorSession(allocator->flags()), allocator_(allocator),
+ name_(name), network_thread_(NULL), session_type_(session_type),
+ allocation_started_(false), running_(false), stun_address_(NULL),
+ relay_address_(NULL) {
+}
+
+BasicPortAllocatorSession::BasicPortAllocatorSession(
+ BasicPortAllocator *allocator,
+ const std::string &name,
+ const std::string &session_type,
+ talk_base::SocketAddress *stun_address,
+ talk_base::SocketAddress *relay_address)
+ : PortAllocatorSession(allocator->flags()), allocator_(allocator),
+ name_(name), session_type_(session_type), network_thread_(NULL),
+ allocation_started_(false), running_(false), stun_address_(stun_address),
+ relay_address_(relay_address) {
+}
+
+BasicPortAllocatorSession::~BasicPortAllocatorSession() {
+ if (network_thread_ != NULL)
+ network_thread_->Clear(this);
+
+ std::vector<PortData>::iterator it;
+ for (it = ports_.begin(); it != ports_.end(); it++)
+ delete it->port;
+
+ for (uint32 i = 0; i < configs_.size(); ++i)
+ delete configs_[i];
+
+ for (uint32 i = 0; i < sequences_.size(); ++i)
+ delete sequences_[i];
+}
+
+void BasicPortAllocatorSession::GetInitialPorts() {
+ network_thread_ = talk_base::Thread::Current();
+
+ network_thread_->Post(this, MSG_CONFIG_START);
+
+ if (flags() & PORTALLOCATOR_ENABLE_SHAKER)
+ network_thread_->PostDelayed(ShakeDelay(), this, MSG_SHAKE);
+}
+
+void BasicPortAllocatorSession::StartGetAllPorts() {
+ assert(talk_base::Thread::Current() == network_thread_);
+ running_ = true;
+ if (allocation_started_)
+ network_thread_->PostDelayed(ALLOCATE_DELAY, this, MSG_ALLOCATE);
+ for (uint32 i = 0; i < sequences_.size(); ++i)
+ sequences_[i]->Start();
+ for (size_t i = 0; i < ports_.size(); ++i)
+ ports_[i].port->Start();
+}
+
+void BasicPortAllocatorSession::StopGetAllPorts() {
+ assert(talk_base::Thread::Current() == network_thread_);
+ running_ = false;
+ network_thread_->Clear(this, MSG_ALLOCATE);
+ for (uint32 i = 0; i < sequences_.size(); ++i)
+ sequences_[i]->Stop();
+}
+
+void BasicPortAllocatorSession::OnMessage(talk_base::Message *message) {
+ switch (message->message_id) {
+ case MSG_CONFIG_START:
+ assert(talk_base::Thread::Current() == network_thread_);
+ GetPortConfigurations();
+ break;
+
+ case MSG_CONFIG_READY:
+ assert(talk_base::Thread::Current() == network_thread_);
+ OnConfigReady(static_cast<PortConfiguration*>(message->pdata));
+ break;
+
+ case MSG_ALLOCATE:
+ assert(talk_base::Thread::Current() == network_thread_);
+ OnAllocate();
+ break;
+
+ case MSG_SHAKE:
+ assert(talk_base::Thread::Current() == network_thread_);
+ OnShake();
+ break;
+
+ default:
+ assert(false);
+ }
+}
+
+void BasicPortAllocatorSession::GetPortConfigurations() {
+ PortConfiguration* config = NULL;
+ if (stun_address_ != NULL)
+ config = new PortConfiguration(*stun_address_,
+ CreateRandomString(16),
+ CreateRandomString(16),
+ "");
+ PortConfiguration::PortList ports;
+ if (relay_address_ != NULL) {
+ ports.push_back(ProtocolAddress(*relay_address_, PROTO_UDP));
+ config->AddRelay(ports, RELAY_PRIMARY_PREF_MODIFIER);
+ }
+
+ ConfigReady(config);
+}
+
+void BasicPortAllocatorSession::ConfigReady(PortConfiguration* config) {
+ network_thread_->Post(this, MSG_CONFIG_READY, config);
+}
+
+// Adds a configuration to the list.
+void BasicPortAllocatorSession::OnConfigReady(PortConfiguration* config) {
+ if (config)
+ configs_.push_back(config);
+
+ AllocatePorts();
+}
+
+void BasicPortAllocatorSession::AllocatePorts() {
+ assert(talk_base::Thread::Current() == network_thread_);
+
+ if (allocator_->proxy().type != talk_base::PROXY_NONE)
+ Port::set_proxy(allocator_->user_agent(), allocator_->proxy());
+
+ network_thread_->Post(this, MSG_ALLOCATE);
+}
+
+// For each network, see if we have a sequence that covers it already. If not,
+// create a new sequence to create the appropriate ports.
+void BasicPortAllocatorSession::OnAllocate() {
+ std::vector<talk_base::Network*> networks;
+ allocator_->network_manager()->GetNetworks(networks);
+
+ for (uint32 i = 0; i < networks.size(); ++i) {
+ if (HasEquivalentSequence(networks[i]))
+ continue;
+
+ PortConfiguration* config = NULL;
+ if (configs_.size() > 0)
+ config = configs_.back();
+
+ AllocationSequence* sequence =
+ new AllocationSequence(this, networks[i], config);
+ if (running_)
+ sequence->Start();
+
+ sequences_.push_back(sequence);
+ }
+
+ allocation_started_ = true;
+ if (running_)
+ network_thread_->PostDelayed(ALLOCATE_DELAY, this, MSG_ALLOCATE);
+}
+
+bool BasicPortAllocatorSession::HasEquivalentSequence(
+ talk_base::Network* network) {
+ for (uint32 i = 0; i < sequences_.size(); ++i)
+ if (sequences_[i]->IsEquivalent(network))
+ return true;
+ return false;
+}
+
+void BasicPortAllocatorSession::AddAllocatedPort(Port* port,
+ AllocationSequence * seq,
+ float pref,
+ bool prepare_address) {
+ if (!port)
+ return;
+
+ port->set_name(name_);
+ port->set_preference(pref);
+ port->set_generation(generation());
+ PortData data;
+ data.port = port;
+ data.sequence = seq;
+ data.ready = false;
+ ports_.push_back(data);
+ port->SignalAddressReady.connect(this, &BasicPortAllocatorSession::OnAddressReady);
+ port->SignalConnectionCreated.connect(this, &BasicPortAllocatorSession::OnConnectionCreated);
+ port->SignalDestroyed.connect(this, &BasicPortAllocatorSession::OnPortDestroyed);
+ LOG_J(LS_INFO, port) << "Added port to allocator";
+ if (prepare_address)
+ port->PrepareAddress();
+ if (running_)
+ port->Start();
+}
+
+void BasicPortAllocatorSession::OnAddressReady(Port *port) {
+ assert(talk_base::Thread::Current() == network_thread_);
+ std::vector<PortData>::iterator it
+ = std::find(ports_.begin(), ports_.end(), port);
+ assert(it != ports_.end());
+ if (it->ready)
+ return;
+ it->ready = true;
+ SignalPortReady(this, port);
+
+ // Only accumulate the candidates whose protocol has been enabled
+ std::vector<Candidate> candidates;
+ const std::vector<Candidate>& potentials = port->candidates();
+ for (size_t i=0; i<potentials.size(); ++i) {
+ ProtocolType pvalue;
+ if (!StringToProto(potentials[i].protocol().c_str(), pvalue))
+ continue;
+ if (it->sequence->ProtocolEnabled(pvalue)) {
+ candidates.push_back(potentials[i]);
+ }
+ }
+ if (!candidates.empty()) {
+ SignalCandidatesReady(this, candidates);
+ }
+}
+
+void BasicPortAllocatorSession::OnProtocolEnabled(AllocationSequence * seq,
+ ProtocolType proto) {
+ std::vector<Candidate> candidates;
+ for (std::vector<PortData>::iterator it = ports_.begin(); it != ports_.end(); ++it) {
+ if (!it->ready || (it->sequence != seq))
+ continue;
+
+ const std::vector<Candidate>& potentials = it->port->candidates();
+ for (size_t i=0; i<potentials.size(); ++i) {
+ ProtocolType pvalue;
+ if (!StringToProto(potentials[i].protocol().c_str(), pvalue))
+ continue;
+ if (pvalue == proto) {
+ candidates.push_back(potentials[i]);
+ }
+ }
+ }
+ if (!candidates.empty()) {
+ SignalCandidatesReady(this, candidates);
+ }
+}
+
+void BasicPortAllocatorSession::OnPortDestroyed(Port* port) {
+ assert(talk_base::Thread::Current() == network_thread_);
+ std::vector<PortData>::iterator iter =
+ find(ports_.begin(), ports_.end(), port);
+ assert(iter != ports_.end());
+ ports_.erase(iter);
+
+ LOG_J(LS_INFO, port) << "Removed port from allocator ("
+ << static_cast<int>(ports_.size()) << " remaining)";
+}
+
+void BasicPortAllocatorSession::OnConnectionCreated(Port* port,
+ Connection* conn) {
+ conn->SignalStateChange.connect(this,
+ &BasicPortAllocatorSession::OnConnectionStateChange);
+}
+
+void BasicPortAllocatorSession::OnConnectionStateChange(Connection* conn) {
+ if (conn->write_state() == Connection::STATE_WRITABLE)
+ allocator_->AddWritablePhase(
+ LocalCandidateToPhase(conn->local_candidate()));
+}
+
+void BasicPortAllocatorSession::OnShake() {
+ LOG(INFO) << ">>>>> SHAKE <<<<< >>>>> SHAKE <<<<< >>>>> SHAKE <<<<<";
+
+ std::vector<Port*> ports;
+ std::vector<Connection*> connections;
+
+ for (size_t i = 0; i < ports_.size(); ++i) {
+ if (ports_[i].ready)
+ ports.push_back(ports_[i].port);
+ }
+
+ for (size_t i = 0; i < ports.size(); ++i) {
+ Port::AddressMap::const_iterator iter;
+ for (iter = ports[i]->connections().begin();
+ iter != ports[i]->connections().end();
+ ++iter) {
+ connections.push_back(iter->second);
+ }
+ }
+
+ LOG(INFO) << ">>>>> Destroying " << (int)ports.size() << " ports and "
+ << (int)connections.size() << " connections";
+
+ for (size_t i = 0; i < connections.size(); ++i)
+ connections[i]->Destroy();
+
+ if (running_ || (ports.size() > 0) || (connections.size() > 0))
+ network_thread_->PostDelayed(ShakeDelay(), this, MSG_SHAKE);
+}
+
+// AllocationSequence
+
+AllocationSequence::AllocationSequence(BasicPortAllocatorSession* session,
+ talk_base::Network* network,
+ PortConfiguration* config)
+ : session_(session), network_(network), ip_(network->ip()), config_(config),
+ running_(false), step_(0) {
+
+ // All of the phases up until the best-writable phase so far run in step 0.
+ // The other phases follow sequentially in the steps after that. If there is
+ // no best-writable so far, then only phase 0 occurs in step 0.
+ int last_phase_in_step_zero =
+ talk_base::_max(0, session->allocator()->best_writable_phase());
+ for (int phase = 0; phase < kNumPhases; ++phase)
+ step_of_phase_[phase] = talk_base::_max(0, phase - last_phase_in_step_zero);
+
+ // Immediately perform phase 0.
+ OnMessage(NULL);
+}
+
+AllocationSequence::~AllocationSequence() {
+ session_->network_thread()->Clear(this);
+}
+
+bool AllocationSequence::IsEquivalent(talk_base::Network* network) {
+ return (network == network_) && (ip_ == network->ip());
+}
+
+void AllocationSequence::Start() {
+ running_ = true;
+ session_->network_thread()->PostDelayed(ALLOCATION_STEP_DELAY,
+ this,
+ MSG_ALLOCATION_PHASE);
+}
+
+void AllocationSequence::Stop() {
+ running_ = false;
+ session_->network_thread()->Clear(this, MSG_ALLOCATION_PHASE);
+}
+
+void AllocationSequence::OnMessage(talk_base::Message* msg) {
+ assert(talk_base::Thread::Current() == session_->network_thread());
+ if (msg)
+ assert(msg->message_id == MSG_ALLOCATION_PHASE);
+
+ const char* const PHASE_NAMES[kNumPhases] = {
+ "Udp", "Relay", "Tcp", "SslTcp"
+ };
+
+ // Perform all of the phases in the current step.
+ for (int phase = 0; phase < kNumPhases; phase++) {
+ if (step_of_phase_[phase] != step_)
+ continue;
+
+ LOG_J(LS_INFO, network_) << "Allocation Phase=" << PHASE_NAMES[phase]
+ << " (Step=" << step_ << ")";
+
+ switch (phase) {
+ case PHASE_UDP:
+ CreateUDPPorts();
+ CreateStunPorts();
+ EnableProtocol(PROTO_UDP);
+ break;
+
+ case PHASE_RELAY:
+ CreateRelayPorts();
+ break;
+
+ case PHASE_TCP:
+ CreateTCPPorts();
+ EnableProtocol(PROTO_TCP);
+ break;
+
+ case PHASE_SSLTCP:
+ EnableProtocol(PROTO_SSLTCP);
+ break;
+
+ default:
+ ASSERT(false);
+ }
+ }
+
+ // TODO: use different delays for each stage
+ step_ += 1;
+ if (running_) {
+ session_->network_thread()->PostDelayed(ALLOCATION_STEP_DELAY,
+ this,
+ MSG_ALLOCATION_PHASE);
+ }
+}
+
+void AllocationSequence::EnableProtocol(ProtocolType proto) {
+ if (!ProtocolEnabled(proto)) {
+ protocols_.push_back(proto);
+ session_->OnProtocolEnabled(this, proto);
+ }
+}
+
+bool AllocationSequence::ProtocolEnabled(ProtocolType proto) const {
+ for (ProtocolList::const_iterator it = protocols_.begin(); it != protocols_.end(); ++it) {
+ if (*it == proto)
+ return true;
+ }
+ return false;
+}
+
+void AllocationSequence::CreateUDPPorts() {
+ if (session_->flags() & PORTALLOCATOR_DISABLE_UDP)
+ return;
+
+ Port* port = new UDPPort(session_->network_thread(), NULL, network_,
+ talk_base::SocketAddress(ip_, 0));
+ session_->AddAllocatedPort(port, this, PREF_LOCAL_UDP);
+}
+
+void AllocationSequence::CreateTCPPorts() {
+ if (session_->flags() & PORTALLOCATOR_DISABLE_TCP)
+ return;
+
+ Port* port = new TCPPort(session_->network_thread(), NULL, network_,
+ talk_base::SocketAddress(ip_, 0));
+ session_->AddAllocatedPort(port, this, PREF_LOCAL_TCP);
+}
+
+void AllocationSequence::CreateStunPorts() {
+ if (session_->flags() & PORTALLOCATOR_DISABLE_STUN)
+ return;
+
+ if (!config_ || config_->stun_address.IsAny())
+ return;
+
+ Port* port = new StunPort(session_->network_thread(), NULL, network_,
+ talk_base::SocketAddress(ip_, 0),
+ config_->stun_address);
+ session_->AddAllocatedPort(port, this, PREF_LOCAL_STUN);
+}
+
+void AllocationSequence::CreateRelayPorts() {
+ if (session_->flags() & PORTALLOCATOR_DISABLE_RELAY)
+ return;
+
+ if (!config_)
+ return;
+
+ PortConfiguration::RelayList::const_iterator relay;
+ for (relay = config_->relays.begin();
+ relay != config_->relays.end();
+ ++relay) {
+
+ RelayPort *port = new RelayPort(session_->network_thread(), NULL, network_,
+ talk_base::SocketAddress(ip_, 0),
+ config_->username, config_->password,
+ config_->magic_cookie);
+ // Note: We must add the allocated port before we add addresses because
+ // the latter will create candidates that need name and preference
+ // settings. However, we also can't prepare the address (normally
+ // done by AddAllocatedPort) until we have these addresses. So we
+ // wait to do that until below.
+ session_->AddAllocatedPort(port, this, PREF_RELAY + relay->pref_modifier,
+ false);
+
+ // Add the addresses of this protocol.
+ PortConfiguration::PortList::const_iterator relay_port;
+ for (relay_port = relay->ports.begin();
+ relay_port != relay->ports.end();
+ ++relay_port) {
+ port->AddServerAddress(*relay_port);
+ port->AddExternalAddress(*relay_port);
+ }
+
+ // Start fetching an address for this port.
+ port->PrepareAddress();
+ }
+}
+
+// PortConfiguration
+
+PortConfiguration::PortConfiguration(const talk_base::SocketAddress& sa,
+ const std::string& un,
+ const std::string& pw,
+ const std::string& mc)
+ : stun_address(sa), username(un), password(pw), magic_cookie(mc) {
+}
+
+void PortConfiguration::AddRelay(const PortList& ports, float pref_modifier) {
+ RelayServer relay;
+ relay.ports = ports;
+ relay.pref_modifier = pref_modifier;
+ relays.push_back(relay);
+}
+
+bool PortConfiguration::SupportsProtocol(
+ const PortConfiguration::RelayServer& relay, ProtocolType type) {
+ PortConfiguration::PortList::const_iterator relay_port;
+ for (relay_port = relay.ports.begin();
+ relay_port != relay.ports.end();
+ ++relay_port) {
+ if (relay_port->proto == type)
+ return true;
+ }
+ return false;
+}
+
+} // namespace cricket
diff --git a/Plugins/jingle/libjingle/talk/p2p/client/basicportallocator.h b/Plugins/jingle/libjingle/talk/p2p/client/basicportallocator.h
new file mode 100644
index 0000000..0e3d313
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/p2p/client/basicportallocator.h
@@ -0,0 +1,175 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _BASICPORTALLOCATOR_H_
+#define _BASICPORTALLOCATOR_H_
+
+#include "talk/base/thread.h"
+#include "talk/base/messagequeue.h"
+#include "talk/base/network.h"
+#include "talk/p2p/base/portallocator.h"
+#include <string>
+#include <vector>
+
+namespace cricket {
+
+class BasicPortAllocator : public PortAllocator {
+public:
+ BasicPortAllocator(talk_base::NetworkManager* network_manager);
+ BasicPortAllocator(talk_base::NetworkManager* network_manager,
+ talk_base::SocketAddress *stun_server, talk_base::SocketAddress *relay_server);
+ virtual ~BasicPortAllocator();
+
+ talk_base::NetworkManager* network_manager() { return network_manager_; }
+
+ // Returns the best (highest preference) phase that has produced a port that
+ // produced a writable connection. If no writable connections have been
+ // produced, this returns -1.
+ int best_writable_phase() const;
+
+ virtual PortAllocatorSession *CreateSession(const std::string &name, const std::string &session_type);
+
+ // Called whenever a connection becomes writable with the argument being the
+ // phase that the corresponding port was created in.
+ void AddWritablePhase(int phase);
+
+private:
+ talk_base::NetworkManager* network_manager_;
+ talk_base::SocketAddress* stun_address_;
+ talk_base::SocketAddress* relay_address_;
+ int best_writable_phase_;
+};
+
+struct PortConfiguration;
+class AllocationSequence;
+
+class BasicPortAllocatorSession: public PortAllocatorSession,
+ public talk_base::MessageHandler {
+public:
+ BasicPortAllocatorSession(BasicPortAllocator *allocator,
+ const std::string &name,
+ const std::string &session_type);
+ BasicPortAllocatorSession(BasicPortAllocator *allocator,
+ const std::string &name,
+ const std::string &session_type,
+ talk_base::SocketAddress *stun_address,
+ talk_base::SocketAddress *relay_address);
+ ~BasicPortAllocatorSession();
+
+ BasicPortAllocator* allocator() { return allocator_; }
+ const std::string& name() const { return name_; }
+ const std::string& session_type() const { return session_type_; }
+ talk_base::Thread* network_thread() { return network_thread_; }
+
+ virtual void GetInitialPorts();
+ virtual void StartGetAllPorts();
+ virtual void StopGetAllPorts();
+ virtual bool IsGettingAllPorts() { return running_; }
+
+protected:
+ // Starts the process of getting the port configurations.
+ virtual void GetPortConfigurations();
+
+ // Adds a port configuration that is now ready. Once we have one for each
+ // network (or a timeout occurs), we will start allocating ports.
+ void ConfigReady(PortConfiguration* config);
+
+ // MessageHandler. Can be overriden if message IDs do not conflict.
+ virtual void OnMessage(talk_base::Message *message);
+
+private:
+ void OnConfigReady(PortConfiguration* config);
+ void OnConfigTimeout();
+ void AllocatePorts();
+ void OnAllocate();
+ bool HasEquivalentSequence(talk_base::Network* network);
+ void AddAllocatedPort(Port* port, AllocationSequence * seq, float pref,
+ bool prepare_address = true);
+ void OnAddressReady(Port *port);
+ void OnProtocolEnabled(AllocationSequence * seq, ProtocolType proto);
+ void OnPortDestroyed(Port* port);
+ void OnConnectionCreated(Port* port, Connection* conn);
+ void OnConnectionStateChange(Connection* conn);
+ void OnShake();
+
+ BasicPortAllocator *allocator_;
+ std::string name_;
+ std::string session_type_;
+ talk_base::Thread* network_thread_;
+ bool configuration_done_;
+ bool allocation_started_;
+ bool running_; // set when StartGetAllPorts is called
+ std::vector<PortConfiguration*> configs_;
+ std::vector<AllocationSequence*> sequences_;
+ talk_base::SocketAddress *stun_address_;
+ talk_base::SocketAddress *relay_address_;
+
+ struct PortData {
+ Port * port;
+ AllocationSequence * sequence;
+ bool ready;
+
+ bool operator==(Port * rhs) const { return (port == rhs); }
+ };
+ std::vector<PortData> ports_;
+
+ friend class AllocationSequence;
+};
+
+// Records configuration information useful in creating ports.
+struct PortConfiguration : public talk_base::MessageData {
+ talk_base::SocketAddress stun_address;
+ std::string username;
+ std::string password;
+ std::string magic_cookie;
+
+ typedef std::vector<ProtocolAddress> PortList;
+ struct RelayServer {
+ PortList ports;
+ float pref_modifier; // added to the protocol modifier to get the
+ // preference for this particular server
+ };
+
+ typedef std::vector<RelayServer> RelayList;
+ RelayList relays;
+
+ PortConfiguration(const talk_base::SocketAddress& stun_address,
+ const std::string& username,
+ const std::string& password,
+ const std::string& magic_cookie);
+
+ // Adds another relay server, with the given ports and modifier, to the list.
+ void AddRelay(const PortList& ports, float pref_modifier);
+
+ // Determines whether the given relay server supports the given protocol.
+ static bool SupportsProtocol(const PortConfiguration::RelayServer& relay,
+ ProtocolType type);
+};
+
+} // namespace cricket
+
+#endif // _BASICPORTALLOCATOR_H_
diff --git a/Plugins/jingle/libjingle/talk/p2p/client/httpportallocator.cc b/Plugins/jingle/libjingle/talk/p2p/client/httpportallocator.cc
new file mode 100644
index 0000000..9837478
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/p2p/client/httpportallocator.cc
@@ -0,0 +1,190 @@
+#if defined(_MSC_VER) && _MSC_VER < 1300
+#pragma warning(disable:4786)
+#endif
+#include "talk/base/asynchttprequest.h"
+#include "talk/base/basicdefs.h"
+#include "talk/base/common.h"
+#include "talk/base/helpers.h"
+#include "talk/base/logging.h"
+#include "talk/base/signalthread.h"
+#include "talk/p2p/client/httpportallocator.h"
+#include <cassert>
+#include <ctime>
+
+namespace {
+
+// Records the port on the hosts that will receive HTTP requests.
+const uint16 kHostPort = 80;
+
+// Records the URL that we will GET in order to create a session.
+const std::string kCreateSessionURL = "/create_session";
+
+// The number of HTTP requests we should attempt before giving up.
+const size_t kNumRetries = 5;
+
+// The delay before we give up on an HTTP request;
+const int TIMEOUT = 5 * 1000; // 5 seconds
+
+const uint32 MSG_TIMEOUT = 100; // must not conflict with BasicPortAllocator.cpp
+
+// Helper routine to remove whitespace from the ends of a string.
+void Trim(std::string& str) {
+ size_t first = str.find_first_not_of(" \t\r\n");
+ if (first == std::string::npos) {
+ str.clear();
+ return;
+ }
+
+ size_t last = str.find_last_not_of(" \t\r\n");
+ ASSERT(last != std::string::npos);
+}
+
+// Parses the lines in the result of the HTTP request that are of the form
+// 'a=b' and returns them in a map.
+typedef std::map<std::string,std::string> StringMap;
+void ParseMap(const std::string& string, StringMap& map) {
+ size_t start_of_line = 0;
+ size_t end_of_line = 0;
+
+ for (;;) { // for each line
+ start_of_line = string.find_first_not_of("\r\n", end_of_line);
+ if (start_of_line == std::string::npos)
+ break;
+
+ end_of_line = string.find_first_of("\r\n", start_of_line);
+ if (end_of_line == std::string::npos) {
+ end_of_line = string.length();
+ }
+
+ size_t equals = string.find('=', start_of_line);
+ if ((equals >= end_of_line) || (equals == std::string::npos))
+ continue;
+
+ std::string key(string, start_of_line, equals - start_of_line);
+ std::string value(string, equals + 1, end_of_line - equals - 1);
+
+ Trim(key);
+ Trim(value);
+
+ if ((key.size() > 0) && (value.size() > 0))
+ map[key] = value;
+ }
+}
+
+}
+
+namespace cricket {
+
+// HttpPortAllocator
+
+HttpPortAllocator::HttpPortAllocator(talk_base::NetworkManager* network_manager, const std::string &user_agent)
+ : BasicPortAllocator(network_manager), agent_(user_agent) {
+ relay_hosts_.push_back("relay.l.google.com");
+ stun_hosts_.push_back(talk_base::SocketAddress("stun.l.google.com",19302));
+}
+
+HttpPortAllocator::~HttpPortAllocator() {
+}
+
+PortAllocatorSession *HttpPortAllocator::CreateSession(const std::string &name, const std::string &session_type) {
+ return new HttpPortAllocatorSession(this, name, session_type, stun_hosts_, relay_hosts_, relay_token_, agent_);
+}
+
+// HttpPortAllocatorSession
+
+HttpPortAllocatorSession::HttpPortAllocatorSession(HttpPortAllocator* allocator, const std::string &name,
+ const std::string &session_type,
+ const std::vector<talk_base::SocketAddress> &stun_hosts,
+ const std::vector<std::string> &relay_hosts,
+ const std::string &relay_token,
+ const std::string &user_agent)
+ : BasicPortAllocatorSession(allocator, name, session_type),
+ attempts_(0), relay_hosts_(relay_hosts), stun_hosts_(stun_hosts), relay_token_(relay_token), agent_(user_agent) {
+}
+
+void HttpPortAllocatorSession::GetPortConfigurations() {
+
+ if (attempts_ == kNumRetries) {
+ LOG(WARNING) << "HttpPortAllocator: maximum number of requests reached";
+ return;
+ }
+
+ if (relay_hosts_.size() <= 0) {
+ LOG(WARNING) << "HttpPortAllocator: no relay hosts found";
+ return;
+ }
+
+ // Choose the next host to try.
+ std::string host = relay_hosts_[attempts_ % relay_hosts_.size()];
+ attempts_++;
+ LOG(INFO) << "HTTPPortAllocator: sending to host " << host;
+
+ // Initiate an HTTP request to create a session through the chosen host.
+
+ talk_base::AsyncHttpRequest* request = new talk_base::AsyncHttpRequest(agent_);
+ request->SignalWorkDone.connect(this, &HttpPortAllocatorSession::OnRequestDone);
+
+ request->set_proxy(allocator()->proxy());
+ request->response().document.reset(new talk_base::MemoryStream);
+ request->request().verb = talk_base::HV_GET;
+ request->request().path = kCreateSessionURL;
+ request->request().addHeader("X-Talk-Google-Relay-Auth", relay_token_, true);
+ request->request().addHeader("X-Google-Relay-Auth", relay_token_, true);
+ request->request().addHeader("X-Session-Type", session_type(), true);
+ request->set_host(host);
+ request->set_port(kHostPort);
+ request->Start();
+ request->Release();
+}
+
+void HttpPortAllocatorSession::OnRequestDone(talk_base::SignalThread* data) {
+ talk_base::AsyncHttpRequest *request =
+ static_cast<talk_base::AsyncHttpRequest*> (data);
+ if (request->response().scode != 200) {
+ LOG(WARNING) << "HTTPPortAllocator: request "
+ << " received error " << request->response().scode;
+ GetPortConfigurations();
+ return;
+ }
+ LOG(INFO) << "HTTPPortAllocator: request succeeded";
+
+ StringMap map;
+ talk_base::MemoryStream *stream = static_cast<talk_base::MemoryStream*>(request->response().document.get());
+ stream->Rewind();
+ size_t length;
+ stream->GetSize(&length);
+ std::string resp = std::string(stream->GetBuffer(), length);
+ ParseMap(resp, map);
+
+ std::string username = map["username"];
+ std::string password = map["password"];
+ std::string magic_cookie = map["magic_cookie"];
+
+ std::string relay_ip = map["relay.ip"];
+ std::string relay_udp_port = map["relay.udp_port"];
+ std::string relay_tcp_port = map["relay.tcp_port"];
+ std::string relay_ssltcp_port = map["relay.ssltcp_port"];
+
+ PortConfiguration* config = new PortConfiguration(stun_hosts_[0],
+ username,
+ password,
+ magic_cookie);
+
+ PortConfiguration::PortList ports;
+ if (!relay_udp_port.empty()) {
+ talk_base::SocketAddress address(relay_ip, atoi(relay_udp_port.c_str()));
+ ports.push_back(ProtocolAddress(address, PROTO_UDP));
+ }
+ if (!relay_tcp_port.empty()) {
+ talk_base::SocketAddress address(relay_ip, atoi(relay_tcp_port.c_str()));
+ ports.push_back(ProtocolAddress(address, PROTO_TCP));
+ }
+ if (!relay_ssltcp_port.empty()) {
+ talk_base::SocketAddress address(relay_ip, atoi(relay_ssltcp_port.c_str()));
+ ports.push_back(ProtocolAddress(address, PROTO_SSLTCP));
+ }
+ config->AddRelay(ports, 0.0f);
+ ConfigReady(config);
+}
+
+} // namespace cricket
diff --git a/Plugins/jingle/libjingle/talk/p2p/client/httpportallocator.h b/Plugins/jingle/libjingle/talk/p2p/client/httpportallocator.h
new file mode 100644
index 0000000..96f3e72
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/p2p/client/httpportallocator.h
@@ -0,0 +1,61 @@
+#ifndef _HTTPPORTALLOCATOR_H_
+#define _HTTPPORTALLOCATOR_H_
+
+#include "talk/p2p/client/basicportallocator.h"
+
+namespace talk_base {
+ class SignalThread;
+}
+
+namespace cricket {
+
+class HttpPortAllocator : public BasicPortAllocator {
+public:
+ HttpPortAllocator(talk_base::NetworkManager* network_manager, const std::string &user_agent);
+ virtual ~HttpPortAllocator();
+
+ virtual PortAllocatorSession *CreateSession(const std::string &name,
+ const std::string &session_type);
+ void SetStunHosts(const std::vector<talk_base::SocketAddress> &hosts) {stun_hosts_ = hosts;}
+ void SetRelayHosts(const std::vector<std::string> &hosts) {relay_hosts_ = hosts;}
+ void SetRelayToken(const std::string &relay) {relay_token_ = relay;}
+ std::string relay_token() const { return relay_token_; }
+private:
+ std::vector<talk_base::SocketAddress> stun_hosts_;
+ std::vector<std::string> relay_hosts_;
+ std::string relay_token_;
+ std::string agent_;
+};
+
+class RequestData;
+
+class HttpPortAllocatorSession : public BasicPortAllocatorSession {
+ public:
+ HttpPortAllocatorSession(HttpPortAllocator *allocator,
+ const std::string &name,
+ const std::string &session_type,
+ const std::vector<talk_base::SocketAddress> &stun_hosts,
+ const std::vector<std::string> &relay_hosts,
+ const std::string &relay,
+ const std::string &agent);
+ ~HttpPortAllocatorSession() {};
+
+protected:
+ virtual void GetPortConfigurations();
+
+private:
+ std::vector<std::string> relay_hosts_;
+ std::vector<talk_base::SocketAddress> stun_hosts_;
+ std::string relay_token_;
+ std::string agent_;
+
+ void OnRequestDone(talk_base::SignalThread* request);
+ HttpPortAllocator* http_allocator() {
+ return static_cast<HttpPortAllocator*>(allocator());
+ }
+ int attempts_;
+};
+
+} // namespace cricket
+
+#endif // _XMPPPORTALLOCATOR_H_
diff --git a/Plugins/jingle/libjingle/talk/p2p/client/socketmonitor.cc b/Plugins/jingle/libjingle/talk/p2p/client/socketmonitor.cc
new file mode 100644
index 0000000..88d37ce
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/p2p/client/socketmonitor.cc
@@ -0,0 +1,164 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "talk/p2p/client/socketmonitor.h"
+#include "talk/base/common.h"
+
+namespace cricket {
+
+const uint32 MSG_MONITOR_POLL = 1;
+const uint32 MSG_MONITOR_START = 2;
+const uint32 MSG_MONITOR_STOP = 3;
+const uint32 MSG_MONITOR_SIGNAL = 4;
+
+SocketMonitor::SocketMonitor(Session* session,
+ TransportChannel* channel,
+ talk_base::Thread *monitor_thread) {
+ session_ = session;
+ channel_ = channel;
+ channel_thread_ = session->session_manager()->worker_thread();
+ monitoring_thread_ = monitor_thread;
+ monitoring_ = false;
+}
+
+SocketMonitor::~SocketMonitor() {
+ channel_thread_->Clear(this);
+ monitoring_thread_->Clear(this);
+}
+
+void SocketMonitor::Start(int milliseconds) {
+ rate_ = milliseconds;
+ if (rate_ < 250)
+ rate_ = 250;
+ channel_thread_->Post(this, MSG_MONITOR_START);
+}
+
+void SocketMonitor::Stop() {
+ channel_thread_->Post(this, MSG_MONITOR_STOP);
+}
+
+void SocketMonitor::OnMessage(talk_base::Message *message) {
+ talk_base::CritScope cs(&crit_);
+
+ switch (message->message_id) {
+ case MSG_MONITOR_START:
+ ASSERT(talk_base::Thread::Current() == channel_thread_);
+ if (!monitoring_) {
+ monitoring_ = true;
+ if (GetP2PChannel() != NULL) {
+ GetP2PChannel()->SignalConnectionMonitor.connect(
+ this, &SocketMonitor::OnConnectionMonitor);
+ }
+ PollSocket(true);
+ }
+ break;
+
+ case MSG_MONITOR_STOP:
+ ASSERT(talk_base::Thread::Current() == channel_thread_);
+ if (monitoring_) {
+ monitoring_ = false;
+ if (GetP2PChannel() != NULL)
+ GetP2PChannel()->SignalConnectionMonitor.disconnect(this);
+ channel_thread_->Clear(this);
+ }
+ break;
+
+ case MSG_MONITOR_POLL:
+ ASSERT(talk_base::Thread::Current() == channel_thread_);
+ PollSocket(true);
+ break;
+
+ case MSG_MONITOR_SIGNAL:
+ {
+ ASSERT(talk_base::Thread::Current() == monitoring_thread_);
+ std::vector<ConnectionInfo> infos = connection_infos_;
+ crit_.Leave();
+ SignalUpdate(this, infos);
+ crit_.Enter();
+ }
+ break;
+ }
+}
+
+void SocketMonitor::OnConnectionMonitor(P2PTransportChannel* channel) {
+ talk_base::CritScope cs(&crit_);
+ if (monitoring_)
+ PollSocket(false);
+}
+
+void SocketMonitor::PollSocket(bool poll) {
+ ASSERT(talk_base::Thread::Current() == channel_thread_);
+ talk_base::CritScope cs(&crit_);
+
+ // Gather connection infos
+
+ P2PTransportChannel* p2p_channel = GetP2PChannel();
+ if (p2p_channel != NULL) {
+ connection_infos_.clear();
+ const std::vector<Connection *> &connections = p2p_channel->connections();
+ std::vector<Connection *>::const_iterator it;
+ for (it = connections.begin(); it != connections.end(); it++) {
+ Connection *connection = *it;
+ ConnectionInfo info;
+ info.best_connection = p2p_channel->best_connection() == connection;
+ info.readable = connection->read_state() == Connection::STATE_READABLE;
+ info.writable = connection->write_state() == Connection::STATE_WRITABLE;
+ info.timeout = connection->write_state() == Connection::STATE_WRITE_TIMEOUT;
+ info.new_connection = !connection->reported();
+ connection->set_reported(true);
+ info.rtt = connection->rtt();
+ info.sent_total_bytes = connection->sent_total_bytes();
+ info.sent_bytes_second = connection->sent_bytes_second();
+ info.recv_total_bytes = connection->recv_total_bytes();
+ info.recv_bytes_second = connection->recv_bytes_second();
+ info.local_candidate = connection->local_candidate();
+ info.remote_candidate = connection->remote_candidate();
+ info.est_quality = connection->port()->network()->quality();
+ info.key = reinterpret_cast<void *>(connection);
+ connection_infos_.push_back(info);
+ }
+ }
+
+ // Signal the monitoring thread, start another poll timer
+
+ monitoring_thread_->Post(this, MSG_MONITOR_SIGNAL);
+ if (poll)
+ channel_thread_->PostDelayed(rate_, this, MSG_MONITOR_POLL);
+}
+
+P2PTransportChannel* SocketMonitor::GetP2PChannel() {
+ if (session_->transport() == NULL)
+ return NULL;
+ if (session_->transport()->name() != kNsP2pTransport)
+ return NULL;
+ TransportChannelImpl* impl = session_->GetImplementation(channel_);
+ if (impl == NULL)
+ return NULL;
+ return static_cast<P2PTransportChannel*>(impl);
+}
+
+}
diff --git a/Plugins/jingle/libjingle/talk/p2p/client/socketmonitor.h b/Plugins/jingle/libjingle/talk/p2p/client/socketmonitor.h
new file mode 100644
index 0000000..ced86b8
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/p2p/client/socketmonitor.h
@@ -0,0 +1,91 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _SOCKETMONITOR_H_
+#define _SOCKETMONITOR_H_
+
+#include "talk/base/thread.h"
+#include "talk/base/sigslot.h"
+#include "talk/base/criticalsection.h"
+#include "talk/p2p/base/session.h"
+#include "talk/p2p/base/p2ptransportchannel.h"
+#include "talk/p2p/base/port.h"
+#include <vector>
+
+namespace cricket {
+
+struct ConnectionInfo {
+ bool best_connection;
+ bool writable;
+ bool readable;
+ bool timeout;
+ bool new_connection;
+ size_t rtt;
+ size_t sent_total_bytes;
+ size_t sent_bytes_second;
+ size_t recv_total_bytes;
+ size_t recv_bytes_second;
+ Candidate local_candidate;
+ Candidate remote_candidate;
+ double est_quality;
+ void *key;
+};
+
+class SocketMonitor : public talk_base::MessageHandler,
+ public sigslot::has_slots<> {
+public:
+ SocketMonitor(Session* session, TransportChannel* channel,
+ talk_base::Thread *monitor_thread);
+ ~SocketMonitor();
+
+ void Start(int cms);
+ void Stop();
+
+ talk_base::Thread *monitor_thread() { return monitoring_thread_; }
+
+ sigslot::signal2<SocketMonitor *,
+ const std::vector<ConnectionInfo> &> SignalUpdate;
+
+protected:
+ void OnMessage(talk_base::Message *message);
+ void OnConnectionMonitor(P2PTransportChannel* channel);
+ void PollSocket(bool poll);
+ P2PTransportChannel* GetP2PChannel();
+
+ std::vector<ConnectionInfo> connection_infos_;
+ Session* session_;
+ TransportChannel* channel_;
+ talk_base::Thread* channel_thread_;
+ talk_base::Thread* monitoring_thread_;
+ talk_base::CriticalSection crit_;
+ uint32 rate_;
+ bool monitoring_;
+};
+
+}
+
+#endif // _SOCKETMONITOR_H_
diff --git a/Plugins/jingle/libjingle/talk/session/fileshare/Makefile.am b/Plugins/jingle/libjingle/talk/session/fileshare/Makefile.am
new file mode 100644
index 0000000..a6eebf6
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/session/fileshare/Makefile.am
@@ -0,0 +1,7 @@
+libcricketsessionfileshare_la_SOURCES = fileshare.cc
+
+noinst_HEADERS = fileshare.h
+
+AM_CPPFLAGS := -DPOSIX
+noinst_LTLIBRARIES = libcricketsessionfileshare.la
+
diff --git a/Plugins/jingle/libjingle/talk/session/fileshare/Makefile.in b/Plugins/jingle/libjingle/talk/session/fileshare/Makefile.in
new file mode 100644
index 0000000..3c9cd69
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/session/fileshare/Makefile.in
@@ -0,0 +1,446 @@
+# Makefile.in generated by automake 1.9.6 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005 Free Software Foundation, Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+top_builddir = ../../..
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+INSTALL = @INSTALL@
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+target_triplet = @target@
+subdir = talk/session/fileshare
+DIST_COMMON = $(noinst_HEADERS) $(srcdir)/Makefile.am \
+ $(srcdir)/Makefile.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/talk/pkg.m4 \
+ $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+LTLIBRARIES = $(noinst_LTLIBRARIES)
+libcricketsessionfileshare_la_LIBADD =
+am_libcricketsessionfileshare_la_OBJECTS = fileshare.lo
+libcricketsessionfileshare_la_OBJECTS = \
+ $(am_libcricketsessionfileshare_la_OBJECTS)
+DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+ $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS)
+LTCXXCOMPILE = $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CXXFLAGS) $(CXXFLAGS)
+CXXLD = $(CXX)
+CXXLINK = $(LIBTOOL) --tag=CXX --mode=link $(CXXLD) $(AM_CXXFLAGS) \
+ $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
+SOURCES = $(libcricketsessionfileshare_la_SOURCES)
+DIST_SOURCES = $(libcricketsessionfileshare_la_SOURCES)
+HEADERS = $(noinst_HEADERS)
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+ALSA_LIBS = @ALSA_LIBS@
+AMDEP_FALSE = @AMDEP_FALSE@
+AMDEP_TRUE = @AMDEP_TRUE@
+AMTAR = @AMTAR@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+ECHO = @ECHO@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+EXPAT_LIBS = @EXPAT_LIBS@
+F77 = @F77@
+FFLAGS = @FFLAGS@
+GIPS_FALSE = @GIPS_FALSE@
+GIPS_TRUE = @GIPS_TRUE@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_LIBS = @GLIB_LIBS@
+ILBC_CFLAGS = @ILBC_CFLAGS@
+ILBC_LIBS = @ILBC_LIBS@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LDFLAGS = @LDFLAGS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+MAKEINFO = @MAKEINFO@
+MEDIA_LIBS = @MEDIA_LIBS@
+OBJEXT = @OBJEXT@
+ORTP_CFLAGS = @ORTP_CFLAGS@
+ORTP_LIBS = @ORTP_LIBS@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PHONE_FALSE = @PHONE_FALSE@
+PHONE_TRUE = @PHONE_TRUE@
+PKG_CONFIG = @PKG_CONFIG@
+RANLIB = @RANLIB@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SPEEX_CFLAGS = @SPEEX_CFLAGS@
+SPEEX_LIBS = @SPEEX_LIBS@
+STRIP = @STRIP@
+VERSION = @VERSION@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_F77 = @ac_ct_F77@
+ac_ct_RANLIB = @ac_ct_RANLIB@
+ac_ct_STRIP = @ac_ct_STRIP@
+am__fastdepCC_FALSE = @am__fastdepCC_FALSE@
+am__fastdepCC_TRUE = @am__fastdepCC_TRUE@
+am__fastdepCXX_FALSE = @am__fastdepCXX_FALSE@
+am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+datadir = @datadir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+sysconfdir = @sysconfdir@
+target = @target@
+target_alias = @target_alias@
+target_cpu = @target_cpu@
+target_os = @target_os@
+target_vendor = @target_vendor@
+libcricketsessionfileshare_la_SOURCES = fileshare.cc
+noinst_HEADERS = fileshare.h
+AM_CPPFLAGS := -DPOSIX
+noinst_LTLIBRARIES = libcricketsessionfileshare.la
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .cc .lo .o .obj
+$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \
+ && exit 0; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu talk/session/fileshare/Makefile'; \
+ cd $(top_srcdir) && \
+ $(AUTOMAKE) --gnu talk/session/fileshare/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+clean-noinstLTLIBRARIES:
+ -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES)
+ @list='$(noinst_LTLIBRARIES)'; for p in $$list; do \
+ dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
+ test "$$dir" != "$$p" || dir=.; \
+ echo "rm -f \"$${dir}/so_locations\""; \
+ rm -f "$${dir}/so_locations"; \
+ done
+libcricketsessionfileshare.la: $(libcricketsessionfileshare_la_OBJECTS) $(libcricketsessionfileshare_la_DEPENDENCIES)
+ $(CXXLINK) $(libcricketsessionfileshare_la_LDFLAGS) $(libcricketsessionfileshare_la_OBJECTS) $(libcricketsessionfileshare_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fileshare.Plo@am__quote@
+
+.cc.o:
+@am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \
+@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ $<
+
+.cc.obj:
+@am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \
+@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.cc.lo:
+@am__fastdepCXX_TRUE@ if $(LTCXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \
+@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Plo"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(LTCXXCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+distclean-libtool:
+ -rm -f libtool
+uninstall-info-am:
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ mkid -fID $$unique
+tags: TAGS
+
+TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ tags=; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$tags $$unique; \
+ fi
+ctags: CTAGS
+CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ tags=; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ test -z "$(CTAGS_ARGS)$$tags$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$tags $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && cd $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) $$here
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \
+ list='$(DISTFILES)'; for file in $$list; do \
+ case $$file in \
+ $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \
+ $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \
+ esac; \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test "$$dir" != "$$file" && test "$$dir" != "."; then \
+ dir="/$$dir"; \
+ $(mkdir_p) "$(distdir)$$dir"; \
+ else \
+ dir=''; \
+ fi; \
+ if test -d $$d/$$file; then \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \
+ fi; \
+ cp -pR $$d/$$file $(distdir)$$dir || exit 1; \
+ else \
+ test -f $(distdir)/$$file \
+ || cp -p $$d/$$file $(distdir)/$$file \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(LTLIBRARIES) $(HEADERS)
+installdirs:
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ `test -z '$(STRIP)' || \
+ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-libtool distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-exec-am:
+
+install-info: install-info-am
+
+install-man:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-info-am
+
+.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \
+ clean-libtool clean-noinstLTLIBRARIES ctags distclean \
+ distclean-compile distclean-generic distclean-libtool \
+ distclean-tags distdir dvi dvi-am html html-am info info-am \
+ install install-am install-data install-data-am install-exec \
+ install-exec-am install-info install-info-am install-man \
+ install-strip installcheck installcheck-am installdirs \
+ maintainer-clean maintainer-clean-generic mostlyclean \
+ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \
+ pdf pdf-am ps ps-am tags uninstall uninstall-am \
+ uninstall-info-am
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/Plugins/jingle/libjingle/talk/session/fileshare/fileshare.cc b/Plugins/jingle/libjingle/talk/session/fileshare/fileshare.cc
new file mode 100644
index 0000000..863c352
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/session/fileshare/fileshare.cc
@@ -0,0 +1,1313 @@
+/*
+ * libjingle
+ * Copyright 2004--2006, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "talk/session/fileshare/fileshare.h"
+
+#include "talk/base/httpcommon-inl.h"
+
+#include "talk/base/fileutils.h"
+#include "talk/base/streamutils.h"
+#include "talk/base/event.h"
+#include "talk/base/helpers.h"
+#include "talk/base/httpclient.h"
+#include "talk/base/httpserver.h"
+#include "talk/base/pathutils.h"
+#include "talk/base/socketstream.h"
+#include "talk/base/stringdigest.h"
+#include "talk/base/stringencode.h"
+#include "talk/base/stringutils.h"
+#include "talk/base/tarstream.h"
+#include "talk/base/thread.h"
+#include "talk/session/tunnel/pseudotcpchannel.h"
+#include "talk/session/tunnel/tunnelsessionclient.h"
+
+///////////////////////////////////////////////////////////////////////////////
+// <description xmlns="http://www.google.com/session/share">
+// <manifest>
+// <file size='341'>
+// <name>foo.txt</name>
+// </file>
+// <file size='51321'>
+// <name>foo.jpg</name>
+// <image width='480' height='320'/>
+// </file>
+// <folder>
+// <name>stuff</name>
+// </folder>
+// </manifest>
+// <protocol>
+// <http>
+// <url name='source-path'>/temporary/23A53F01/</url>
+// <url name='preview-path'>/temporary/90266EA1/</url>
+// </http>
+// <raw/>
+// </protocol>
+// </description>
+// <p:transport xmns:p="p2p"/>
+///////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////
+// Constants and private functions
+///////////////////////////////////////////////////////////////////////////////
+
+const std::string NS_GOOGLE_SHARE("http://www.google.com/session/share");
+
+namespace {
+
+const buzz::QName QN_SHARE_DESCRIPTION(true, NS_GOOGLE_SHARE, "description");
+const buzz::QName QN_SHARE_MANIFEST(true, NS_GOOGLE_SHARE, "manifest");
+const buzz::QName QN_SHARE_FOLDER(true, NS_GOOGLE_SHARE, "folder");
+const buzz::QName QN_SHARE_FILE(true, NS_GOOGLE_SHARE, "file");
+const buzz::QName QN_SHARE_NAME(true, NS_GOOGLE_SHARE, "name");
+const buzz::QName QN_SHARE_IMAGE(true, NS_GOOGLE_SHARE, "image");
+const buzz::QName QN_SHARE_PROTOCOL(true, NS_GOOGLE_SHARE, "protocol");
+const buzz::QName QN_SHARE_HTTP(true, NS_GOOGLE_SHARE, "http");
+const buzz::QName QN_SHARE_URL(true, NS_GOOGLE_SHARE, "url");
+const buzz::QName QN_SHARE_CHANNEL(true, NS_GOOGLE_SHARE, "channel");
+const buzz::QName QN_SHARE_COMPLETE(true, NS_GOOGLE_SHARE, "complete");
+
+const buzz::QName QN_SIZE(true, buzz::STR_EMPTY, "size");
+const buzz::QName QN_WIDTH(true, buzz::STR_EMPTY, "width");
+const buzz::QName QN_HEIGHT(true, buzz::STR_EMPTY, "height");
+
+const std::string kHttpSourcePath("source-path");
+const std::string kHttpPreviewPath("preview-path");
+
+const size_t kMinImageSize = 16U;
+const size_t kMaxImageSize = 0x8000U; // (32k)
+const size_t kMaxPreviewSize = 1024;
+// Wait 10 seconds to see if any new proxies get established
+const uint32 kProxyWait = 10000;
+
+const int MSG_RETRY = 1;
+const uint32 kFileTransferEnableRetryMs = 1000 * 60 * 4; // 4 minutes
+
+const std::string MIME_OCTET_STREAM("application/octet-stream");
+
+enum {
+ MSG_PROXY_WAIT,
+};
+
+bool AllowedImageDimensions(size_t width, size_t height) {
+ return (width >= kMinImageSize) && (width <= kMaxImageSize)
+ && (height >= kMinImageSize) && (height <= kMaxImageSize);
+}
+
+} // anon namespace
+
+namespace cricket {
+
+///////////////////////////////////////////////////////////////////////////////
+// FileShareManifest
+///////////////////////////////////////////////////////////////////////////////
+
+void
+FileShareManifest::AddFile(const std::string& name, size_t size) {
+ Item i = { T_FILE, name, size };
+ items_.push_back(i);
+}
+
+void
+FileShareManifest::AddImage(const std::string& name, size_t size,
+ size_t width, size_t height) {
+ Item i = { T_IMAGE, name, size, width, height };
+ items_.push_back(i);
+}
+
+void
+FileShareManifest::AddFolder(const std::string& name, size_t size) {
+ Item i = { T_FOLDER, name, size };
+ items_.push_back(i);
+}
+
+size_t
+FileShareManifest::GetItemCount(Type t) const {
+ size_t count = 0;
+ for (size_t i=0; i<items_.size(); ++i) {
+ if (items_[i].type == t)
+ ++count;
+ }
+ return count;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// FileShareSession
+///////////////////////////////////////////////////////////////////////////////
+
+FileShareSession::FileShareSession(cricket::Session* session, const std::string &user_agent)
+ : session_(session), state_(FS_NONE),
+ is_closed_(false),
+ is_sender_(false), manifest_(NULL), pool_(this), http_client_(NULL),
+ http_server_(NULL),
+ transfer_connection_id_(talk_base::HTTP_INVALID_CONNECTION_ID),
+ counter_(NULL), item_transferring_(0), bytes_transferred_(0),
+ local_cancel_(false), local_listener_(NULL), remote_listener_(NULL),
+ next_channel_id_(1), user_agent_(user_agent) {
+ session_->SignalState.connect(this, &FileShareSession::OnSessionState);
+ session_->SignalInfoMessage.connect(this,
+ &FileShareSession::OnSessionInfoMessage);
+ session_->SignalChannelGone.connect(this,
+ &FileShareSession::OnSessionChannelGone);
+}
+
+FileShareSession::~FileShareSession() {
+ ASSERT(FS_NONE != state_);
+ // If we haven't closed, do cleanup now.
+ if (!IsClosed()) {
+ if (!IsComplete()) {
+ state_ = FS_FAILURE;
+ }
+ DoClose(true);
+ }
+ if (session_) {
+ // Make sure we don't get future state changes on this session.
+ session_->SignalState.disconnect(this);
+ session_->SignalInfoMessage.disconnect(this);
+ session_ = NULL;
+ }
+
+ for (TransactionList::const_iterator trans_it = transactions_.begin();
+ trans_it != transactions_.end(); ++trans_it) {
+ (*trans_it)->response()->set_error(talk_base::HC_NOT_FOUND);
+ http_server_->Respond(*trans_it);
+ }
+
+ delete http_client_;
+ delete http_server_;
+ delete manifest_;
+ delete local_listener_;
+ delete remote_listener_;
+}
+
+bool
+FileShareSession::IsComplete() const {
+ return (state_ >= FS_COMPLETE);
+}
+
+bool
+FileShareSession::IsClosed() const {
+ return is_closed_;
+}
+
+FileShareState
+FileShareSession::state() const {
+ return state_;
+}
+
+bool
+FileShareSession::is_sender() const {
+ ASSERT(FS_NONE != state_);
+ return is_sender_;
+}
+
+const buzz::Jid&
+FileShareSession::jid() const {
+ ASSERT(FS_NONE != state_);
+ return jid_;
+}
+
+const FileShareManifest*
+FileShareSession::manifest() const {
+ ASSERT(FS_NONE != state_);
+ return manifest_;
+}
+
+const std::string&
+FileShareSession::local_folder() const {
+ ASSERT(!local_folder_.empty());
+ return local_folder_;
+}
+
+void
+FileShareSession::Share(const buzz::Jid& jid, FileShareManifest* manifest) {
+ ASSERT(FS_NONE == state_);
+ ASSERT(NULL != session_);
+
+ http_server_ = new talk_base::HttpServer;
+ http_server_->SignalHttpRequest.connect(this,
+ &FileShareSession::OnHttpRequest);
+ http_server_->SignalHttpRequestComplete.connect(this,
+ &FileShareSession::OnHttpRequestComplete);
+ http_server_->SignalConnectionClosed.connect(this,
+ &FileShareSession::OnHttpConnectionClosed);
+
+ FileShareDescription* desc = new FileShareDescription;
+ desc->supports_http = true;
+ desc->manifest = *manifest;
+ GenerateTemporaryPrefix(&desc->source_path);
+ GenerateTemporaryPrefix(&desc->preview_path);
+ session_->Initiate(jid.Str(), NULL, desc);
+
+ delete manifest;
+}
+
+void
+FileShareSession::Accept() {
+ ASSERT(FS_OFFER == state_);
+ ASSERT(NULL != session_);
+ ASSERT(NULL != manifest_);
+
+ ASSERT(!http_client_);
+ ASSERT(item_transferring_ == 0);
+ http_client_ = new talk_base::HttpClient(user_agent_,
+ &pool_);
+ http_client_->SignalHttpClientComplete.connect(this,
+ &FileShareSession::OnHttpClientComplete);
+ http_client_->SignalHttpClientClosed.connect(this,
+ &FileShareSession::OnHttpClientClosed);
+
+ // The receiver now has a need for the http_server_, when previewing already
+ // downloaded content.
+ http_server_ = new talk_base::HttpServer;
+ http_server_->SignalHttpRequest.connect(this,
+ &FileShareSession::OnHttpRequest);
+ http_server_->SignalHttpRequestComplete.connect(this,
+ &FileShareSession::OnHttpRequestComplete);
+ http_server_->SignalConnectionClosed.connect(this,
+ &FileShareSession::OnHttpConnectionClosed);
+
+ FileShareDescription* desc = new FileShareDescription;
+ desc->supports_http = description()->supports_http;
+ session_->Accept(desc);
+
+ SetState(FS_TRANSFER, false);
+ NextDownload();
+}
+
+void
+FileShareSession::Decline() {
+ ASSERT(FS_OFFER == state_);
+ ASSERT(NULL != session_);
+ local_cancel_ = true;
+ session_->Reject();
+}
+
+void
+FileShareSession::Cancel() {
+ ASSERT(!IsComplete());
+ ASSERT(NULL != session_);
+ local_cancel_ = true;
+ session_->Terminate();
+}
+
+bool
+FileShareSession::GetItemUrl(size_t index, std::string* url) {
+ return GetItemBaseUrl(index, false, url);
+}
+
+bool FileShareSession::GetImagePreviewUrl(size_t index, size_t width,
+ size_t height, std::string* url) {
+ if (!GetItemBaseUrl(index, true, url))
+ return false;
+
+ if (FileShareManifest::T_IMAGE != manifest_->item(index).type) {
+ ASSERT(false);
+ return false;
+ }
+
+ char query[256];
+ talk_base::sprintfn(query, ARRAY_SIZE(query), "?width=%u&height=%u",
+ width, height);
+ url->append(query);
+ return true;
+}
+
+void FileShareSession::ResampleComplete(talk_base::StreamInterface *i, talk_base::HttpTransaction *trans, bool success) {
+ bool found = false;
+ for (TransactionList::const_iterator trans_it = transactions_.begin();
+ trans_it != transactions_.end(); ++trans_it) {
+ if (*trans_it == trans) {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found)
+ return;
+
+ transactions_.remove(trans);
+
+ if (success) {
+ trans->response()->set_success(MIME_OCTET_STREAM, i);
+ http_server_->Respond(trans);
+
+ }
+ trans->response()->set_error(talk_base::HC_NOT_FOUND);
+ http_server_->Respond(trans);
+}
+
+bool FileShareSession::GetProgress(size_t& bytes) const {
+ bool known = true;
+ bytes = bytes_transferred_;
+ if (counter_) {
+ size_t current_size = manifest_->item(item_transferring_).size;
+ size_t current_pos = counter_->GetByteCount();
+ if (current_size == FileShareManifest::SIZE_UNKNOWN) {
+ known = false;
+ } else if (current_pos > current_size) {
+ // Don't allow the size of a 'known' item to be reported as larger than
+ // it claimed to be.
+ ASSERT(false);
+ current_pos = current_size;
+ }
+ bytes += current_pos;
+ }
+ return known;
+}
+
+bool FileShareSession::GetTotalSize(size_t& bytes) const {
+ bool known = true;
+ bytes = 0;
+ for (size_t i=0; i<manifest_->size(); ++i) {
+ if (manifest_->item(i).size == FileShareManifest::SIZE_UNKNOWN) {
+ // We make files of unknown length worth a single byte.
+ known = false;
+ bytes += 1;
+ } else {
+ bytes += manifest_->item(i).size;
+ }
+ }
+ return known;
+}
+
+bool FileShareSession::GetCurrentItemName(std::string* name) {
+ if (FS_TRANSFER != state_) {
+ name->clear();
+ return false;
+ }
+ ASSERT(item_transferring_ < manifest_->size());
+ if (transfer_name_.empty()) {
+ const FileShareManifest::Item& item = manifest_->item(item_transferring_);
+ *name = item.name;
+ } else {
+ *name = transfer_name_;
+ }
+ return !name->empty();
+}
+
+// StreamPool Implementation
+
+talk_base::StreamInterface* FileShareSession::RequestConnectedStream(
+ const talk_base::SocketAddress& remote, int* err) {
+ ASSERT(remote.IPAsString() == jid_.Str());
+ ASSERT(!IsClosed());
+ ASSERT(NULL != session_);
+ if (!session_) {
+ if (err)
+ *err = -1;
+ return NULL;
+ }
+
+ char channel_name[64];
+ talk_base::sprintfn(channel_name, ARRAY_SIZE(channel_name),
+ "private-%u", next_channel_id_++);
+ if (err)
+ *err = 0;
+ return CreateChannel(channel_name);
+}
+
+void FileShareSession::ReturnConnectedStream(
+ talk_base::StreamInterface* stream) {
+ talk_base::Thread::Current()->Dispose(stream);
+}
+
+// MessageHandler Implementation
+
+void FileShareSession::OnMessage(talk_base::Message* msg) {
+ if (MSG_PROXY_WAIT == msg->message_id) {
+ LOG_F(LS_INFO) << "MSG_PROXY_WAIT";
+ if (proxies_.empty() && IsComplete() && !IsClosed()) {
+ DoClose(true);
+ }
+ }
+}
+
+// Session Signals
+
+void FileShareSession::OnSessionState(cricket::Session* session,
+ cricket::Session::State state) {
+ // Once we are complete, state changes are meaningless.
+ if (!IsComplete()) {
+ switch (state) {
+ case cricket::Session::STATE_SENTINITIATE:
+ case cricket::Session::STATE_RECEIVEDINITIATE:
+ OnInitiate();
+ break;
+ case cricket::Session::STATE_SENTACCEPT:
+ case cricket::Session::STATE_RECEIVEDACCEPT:
+ case cricket::Session::STATE_INPROGRESS:
+ SetState(FS_TRANSFER, false);
+ break;
+ case cricket::Session::STATE_SENTREJECT:
+ case cricket::Session::STATE_SENTTERMINATE:
+ case cricket::Session::STATE_DEINIT:
+ if (local_cancel_) {
+ SetState(FS_LOCAL_CANCEL, false);
+ } else {
+ SetState(FS_REMOTE_CANCEL, false);
+ }
+ break;
+ case cricket::Session::STATE_RECEIVEDTERMINATE:
+ if (is_sender()) {
+ // If we are the sender, and the receiver downloaded the correct number
+ // of bytes, then we assume the transfer was successful. We've
+ // introduced support for explicit completion notification
+ // (QN_SHARE_COMPLETE), but it's not mandatory at this point, so we need
+ // this as a fallback.
+ size_t total_bytes;
+ GetTotalSize(total_bytes);
+ if (bytes_transferred_ >= total_bytes) {
+ SetState(FS_COMPLETE, false);
+ break;
+ }
+ }
+ // Fall through
+ case cricket::Session::STATE_RECEIVEDREJECT:
+ SetState(FS_REMOTE_CANCEL, false);
+ break;
+ case cricket::Session::STATE_INIT:
+ case cricket::Session::STATE_SENTMODIFY:
+ case cricket::Session::STATE_RECEIVEDMODIFY:
+ case cricket::Session::STATE_SENTREDIRECT:
+ default:
+ // These states should not occur.
+ ASSERT(false);
+ break;
+ }
+ }
+
+ if (state == cricket::Session::STATE_DEINIT) {
+ if (!IsClosed()) {
+ DoClose(false);
+ }
+ session_ = NULL;
+ }
+}
+
+void FileShareSession::OnSessionInfoMessage(cricket::Session* session,
+ const cricket::Session::XmlElements& els) {
+ if (IsClosed())
+ return;
+ ASSERT(NULL != session_);
+ for (size_t i=0; i<els.size(); ++i) {
+ if (is_sender() && (els[i]->Name() == QN_SHARE_CHANNEL)) {
+ if (els[i]->HasAttr(buzz::QN_NAME)) {
+ cricket::PseudoTcpChannel* channel =
+ new cricket::PseudoTcpChannel(talk_base::Thread::Current(), session_);
+ VERIFY(channel->Connect(els[i]->Attr(buzz::QN_NAME)));
+ talk_base::StreamInterface* stream = channel->GetStream();
+ http_server_->HandleConnection(stream);
+ }
+ } else if (is_sender() && (els[i]->Name() == QN_SHARE_COMPLETE)) {
+ // Normal file transfer has completed, but receiver may still be getting
+ // previews.
+ if (!IsComplete()) {
+ SetState(FS_COMPLETE, true);
+ }
+ } else {
+ LOG(LS_WARNING) << "Unknown FileShareSession info message: "
+ << els[i]->Name().Merged();
+ }
+ }
+}
+
+void FileShareSession::OnSessionChannelGone(cricket::Session* session,
+ const std::string& name) {
+ LOG_F(LS_WARNING) << "(" << name << ")";
+ ASSERT(session == session_);
+ if (cricket::TransportChannel* channel = session->GetChannel(name)) {
+ session->DestroyChannel(channel);
+ }
+}
+
+// HttpClient Signals
+
+void FileShareSession::OnHttpClientComplete(talk_base::HttpClient* http,
+ int err) {
+ LOG_F(LS_INFO) << "(" << err << ", " << http->response().scode << ")";
+ ASSERT(http == http_client_);
+ ASSERT(NULL != session_);
+
+ transfer_name_.clear();
+ counter_ = NULL; // counter_ is deleted by HttpClient
+ http->response().document.reset();
+ bool success = (err == 0) && (http->response().scode == talk_base::HC_OK);
+
+ const FileShareManifest::Item& item = manifest_->item(item_transferring_);
+ talk_base::Pathname local_name;
+ local_name.SetFilename(item.name);
+ local_name.SetFolder(local_folder_);
+
+ if (local_name.pathname() != transfer_path_) {
+ const bool is_folder = (item.type == FileShareManifest::T_FOLDER);
+ if (success && !talk_base::CreateUniqueFile(local_name, false)) {
+ LOG(LS_ERROR) << "Couldn't rename downloaded file: "
+ << local_name.pathname();
+ success = false;
+ }
+
+ talk_base::Pathname temp_name(transfer_path_);
+ if (is_folder) {
+ // The folder we want is a subdirectory of the transfer_path_.
+ temp_name.AppendFolder(item.name);
+ }
+
+ if (!talk_base::Filesystem::MoveFile(temp_name.pathname(), local_name.pathname())) {
+ success = false;
+ LOG(LS_ERROR) << "Couldn't move downloaded file from '"
+ << temp_name.pathname() << "' to '"
+ << local_name.pathname();
+ }
+
+ if (success && is_folder) {
+ talk_base::Filesystem::DeleteFile(transfer_path_);
+ }
+ }
+
+ if (!success) {
+ if (!talk_base::Filesystem::DeleteFile(transfer_path_)) {
+ LOG(LS_ERROR) << "Couldn't delete downloaded file: " << transfer_path_;
+ }
+ if (!IsComplete()) {
+ SetState(FS_FAILURE, false);
+ }
+ return;
+ }
+
+ // We may have skipped over some items (if they are directories, or otherwise
+ // failed. resize ensures that we populate the skipped entries with empty
+ // strings.
+ stored_location_.resize(item_transferring_ + 1);
+ stored_location_[item_transferring_] = local_name.pathname();
+
+ // bytes_transferred_ represents the size of items which have completely
+ // transferred, and is added to the progress of the currently transferring
+ // items.
+ if (item.size == FileShareManifest::SIZE_UNKNOWN) {
+ bytes_transferred_ += 1;
+ } else {
+ bytes_transferred_ += item.size;
+ }
+ item_transferring_ += 1;
+ NextDownload();
+}
+
+void FileShareSession::OnHttpClientClosed(talk_base::HttpClient* http,
+ int err) {
+ LOG_F(LS_INFO) << "(" << err << ")";
+}
+
+// HttpServer Signals
+
+void FileShareSession::OnHttpRequest(talk_base::HttpServer* server,
+ talk_base::HttpTransaction* transaction) {
+ LOG_F(LS_INFO) << "(" << transaction->request()->path << ")";
+ ASSERT(server == http_server_);
+
+ std::string path, query;
+ size_t query_start = transaction->request()->path.find('?');
+ if (query_start != std::string::npos) {
+ path = transaction->request()->path.substr(0, query_start);
+ query = transaction->request()->path.substr(query_start + 1);
+ } else {
+ path = transaction->request()->path;
+ }
+
+ talk_base::Pathname remote_name(path);
+ bool preview = (preview_path_ == remote_name.folder());
+ bool original = (source_path_ == remote_name.folder());
+
+ std::string requested_file(remote_name.filename());
+ talk_base::transform(requested_file, requested_file.size(), requested_file,
+ talk_base::url_decode);
+
+ size_t item_index;
+ const FileShareManifest::Item* item = NULL;
+ if (preview || original) {
+ for (size_t i=0; i<manifest_->size(); ++i) {
+ LOG(LS_INFO) << "++++ " << manifest_->item(i).name + " " << requested_file;
+ if (manifest_->item(i).name == requested_file) {
+ item_index = i;
+ item = &manifest_->item(item_index);
+ break;
+ }
+ }
+ }
+
+ talk_base::StreamInterface* stream = NULL;
+ std::string mime_type(MIME_OCTET_STREAM);
+
+ if (!item) {
+ // Fall through
+ } else if (preview) {
+ // Only image previews allowed
+ unsigned int width = 0, height = 0;
+ if ((item->type == FileShareManifest::T_IMAGE)
+ && !query.empty()
+ && (sscanf(query.c_str(), "width=%u&height=%u",
+ &width, &height) == 2)) {
+ width = talk_base::_max<unsigned int>(1, talk_base::_min(width, kMaxPreviewSize));
+ height = talk_base::_max<unsigned int>(1, talk_base::_min(height, kMaxPreviewSize));
+ std::string pathname;
+ if (is_sender_) {
+ talk_base::Pathname local_path;
+ local_path.SetFolder(local_folder_);
+ local_path.SetFilename(item->name);
+ pathname = local_path.pathname();
+ } else if ((item_index < stored_location_.size())
+ && !stored_location_[item_index].empty()) {
+ pathname = stored_location_[item_index];
+ }
+ if (!pathname.empty()) {
+ transactions_.push_back(transaction);
+ SignalResampleImage(pathname, width, height, transaction);
+ }
+ }
+ } else if (item->type == FileShareManifest::T_FOLDER) {
+ talk_base::Pathname local_path;
+ local_path.SetFolder(local_folder_);
+ local_path.AppendFolder(item->name);
+ talk_base::TarStream* tar = new talk_base::TarStream;
+ VERIFY(tar->AddFilter(local_path.folder_name()));
+ if (tar->Open(local_path.parent_folder(), true)) {
+ stream = tar;
+ tar->SignalNextEntry.connect(this, &FileShareSession::OnNextEntry);
+ mime_type = "application/x-tar";
+ } else {
+ delete tar;
+ }
+ } else if ((item->type == FileShareManifest::T_FILE)
+ || (item->type == FileShareManifest::T_IMAGE)) {
+ talk_base::Pathname local_path;
+ local_path.SetFolder(local_folder_);
+ local_path.SetFilename(item->name);
+ talk_base::FileStream* file = new talk_base::FileStream;
+ LOG(LS_INFO) << "opening file " << local_path.pathname();
+ if (file->Open(local_path.pathname().c_str(), "rb")) {
+ LOG(LS_INFO) << "File opened";
+ stream = file;
+ } else {
+ delete file;
+ }
+ }
+
+ if (!stream) {
+ transaction->response()->set_error(talk_base::HC_NOT_FOUND);
+ } else if (original) {
+ // We should never have more than one original request pending at a time
+ ASSERT(NULL == counter_);
+ StreamCounter* counter = new StreamCounter(stream);
+ counter->SignalUpdateByteCount.connect(this, &FileShareSession::OnUpdateBytes);
+ transaction->response()->set_success(mime_type.c_str(), counter);
+ transfer_connection_id_ = transaction->connection_id();
+ item_transferring_ = item_index;
+ counter_ = counter;
+ } else {
+ // Note: in the preview case, we don't set counter_, so the transferred
+ // bytes won't be shown as progress, and won't trigger a state change.
+ transaction->response()->set_success(mime_type.c_str(), stream);
+ }
+
+ LOG_F(LS_INFO) << "Result: " << transaction->response()->scode;
+ http_server_->Respond(transaction);
+}
+
+void FileShareSession::OnHttpRequestComplete(talk_base::HttpServer* server,
+ talk_base::HttpTransaction* transaction, int err) {
+ LOG_F(LS_INFO) << "(" << transaction->request()->path << ", " << err << ")";
+ ASSERT(server == http_server_);
+
+ // We only care about transferred originals
+ if (transfer_connection_id_ != transaction->connection_id())
+ return;
+
+ ASSERT(item_transferring_ < manifest_->size());
+ ASSERT(NULL != counter_);
+
+ transfer_connection_id_ = talk_base::HTTP_INVALID_CONNECTION_ID;
+ transfer_name_.clear();
+ counter_ = NULL;
+
+ if (err == 0) {
+ const FileShareManifest::Item& item = manifest_->item(item_transferring_);
+ if (item.size == FileShareManifest::SIZE_UNKNOWN) {
+ bytes_transferred_ += 1;
+ } else {
+ bytes_transferred_ += item.size;
+ }
+ }
+}
+
+void FileShareSession::OnHttpConnectionClosed(talk_base::HttpServer* server,
+ int err, talk_base::StreamInterface* stream) {
+ LOG_F(LS_INFO) << "(" << err << ")";
+ talk_base::Thread::Current()->Dispose(stream);
+}
+
+// TarStream Signals
+
+void FileShareSession::OnNextEntry(const std::string& name, size_t size) {
+ LOG_F(LS_VERBOSE) << "(" << name << ", " << size << ")";
+ transfer_name_ = name;
+ SignalNextFile(this);
+}
+
+// Socket Signals
+
+void FileShareSession::OnProxyAccept(talk_base::AsyncSocket* socket) {
+ bool is_remote;
+ if (socket == remote_listener_) {
+ is_remote = true;
+ ASSERT(NULL != session_);
+ } else if (socket == local_listener_) {
+ is_remote = false;
+ } else {
+ ASSERT(false);
+ return;
+ }
+
+ while (talk_base::AsyncSocket* accepted =
+ static_cast<talk_base::AsyncSocket*>(socket->Accept(NULL))) {
+
+ // Check if connection is from localhost.
+ if (accepted->GetRemoteAddress().ip() != 0x7F000001) {
+ delete accepted;
+ continue;
+ }
+
+ LOG_F(LS_VERBOSE) << (is_remote ? "[remote]" : "[local]");
+
+ if (is_remote) {
+ char channel_name[64];
+ talk_base::sprintfn(channel_name, ARRAY_SIZE(channel_name),
+ "proxy-%u", next_channel_id_++);
+ talk_base::StreamInterface* remote =
+ (NULL != session_) ? CreateChannel(channel_name) : NULL;
+ if (!remote) {
+ LOG_F(LS_WARNING) << "CreateChannel(" << channel_name << ") failed";
+ delete accepted;
+ continue;
+ }
+
+ talk_base::StreamInterface* local = new talk_base::SocketStream(accepted);
+ StreamRelay* proxy = new StreamRelay(local, remote, 64 * 1024);
+ proxy->SignalClosed.connect(this, &FileShareSession::OnProxyClosed);
+ proxies_.push_back(proxy);
+ proxy->Circulate();
+ talk_base::Thread::Current()->Clear(this, MSG_PROXY_WAIT);
+ } else {
+ talk_base::StreamInterface* local = new talk_base::SocketStream(accepted);
+ http_server_->HandleConnection(local);
+ }
+ }
+}
+
+void FileShareSession::OnProxyClosed(StreamRelay* proxy, int error) {
+ ProxyList::iterator it = std::find(proxies_.begin(), proxies_.end(), proxy);
+ if (it == proxies_.end()) {
+ ASSERT(false);
+ return;
+ }
+
+ LOG_F(LS_VERBOSE) << "(" << error << ")";
+
+ proxies_.erase(it);
+ talk_base::Thread::Current()->Dispose(proxy);
+
+ if (proxies_.empty() && IsComplete() && !IsClosed()) {
+ talk_base::Thread::Current()->PostDelayed(kProxyWait, this, MSG_PROXY_WAIT);
+ }
+}
+
+
+void FileShareSession::OnUpdateBytes(size_t count) {
+ SignalUpdateProgress(this);
+}
+
+// Internal Helpers
+
+void FileShareSession::GenerateTemporaryPrefix(std::string* prefix) {
+ std::string data = cricket::CreateRandomString(32);
+ ASSERT(NULL != prefix);
+ prefix->assign("/temporary/");
+ prefix->append(talk_base::MD5(data));
+ prefix->append("/");
+}
+
+void FileShareSession::GetItemNetworkPath(size_t index, bool preview,
+ std::string* path) {
+ ASSERT(index < manifest_->size());
+ ASSERT(NULL != path);
+
+ // preview_path_ and source_path_ are url path segments, which are composed
+ // with the address of the localhost p2p proxy to provide a url which IE can
+ // use.
+
+ std::string ue_name;
+ const std::string& name = manifest_->item(index).name;
+ talk_base::transform(ue_name, name.length() * 3, name, talk_base::url_encode);
+
+ talk_base::Pathname pathname;
+ pathname.SetFolder(preview ? preview_path_ : source_path_);
+ pathname.SetFilename(ue_name);
+ *path = pathname.pathname();
+}
+
+bool FileShareSession::GetItemBaseUrl(size_t index, bool preview,
+ std::string* url) {
+ // This function composes a URL to the referenced item. It may be a local
+ // file url (file:///...), or a remote peer url relayed through localhost
+ // (http://...)
+
+ ASSERT(NULL != url);
+ if (index >= manifest_->size()) {
+ ASSERT(false);
+ return false;
+ }
+
+ const FileShareManifest::Item& item = manifest_->item(index);
+
+ bool is_remote;
+ if (is_sender_) {
+ if (!preview) {
+ talk_base::Pathname path(local_folder_);
+ path.SetFilename(item.name);
+ *url = path.url();
+ return true;
+ }
+ is_remote = false;
+ } else {
+ if ((index < stored_location_.size()) && !stored_location_[index].empty()) {
+ if (!preview) {
+ *url = talk_base::Pathname(stored_location_[index]).url();
+ return true;
+ }
+ // Note: Using the local downloaded files as a source for previews is
+ // desireable, because it means that previews can be regenerated if IE's
+ // cached versions get flushed for some reason, and the remote side is
+ // not available. However, it has the downside that IE _must_ regenerate
+ // the preview locally, which takes time, memory and CPU. Eventually,
+ // we will unify the remote and local cached copy through some sort of
+ // smart http proxying. In the meantime, always use the remote url, to
+ // eliminate the annoying transition from remote to local caching.
+ //is_remote = false;
+ is_remote = true;
+ } else {
+ is_remote = true;
+ }
+ }
+
+ talk_base::SocketAddress address;
+ if (!GetProxyAddress(address, is_remote))
+ return false;
+
+ std::string path;
+ GetItemNetworkPath(index, preview, &path);
+ talk_base::Url<char> make_url(path.c_str(),
+ address.IPAsString().c_str(),
+ address.port());
+ *url = make_url.url();
+ return true;
+}
+
+bool FileShareSession::GetProxyAddress(talk_base::SocketAddress& address,
+ bool is_remote) {
+ talk_base::AsyncSocket*& proxy_listener =
+ is_remote ? remote_listener_ : local_listener_;
+
+ if (!proxy_listener) {
+ talk_base::AsyncSocket* listener =
+ talk_base::Thread::Current()->socketserver()
+ ->CreateAsyncSocket(SOCK_STREAM);
+ if (!listener)
+ return false;
+
+ talk_base::SocketAddress bind_address("127.0.0.1", 0);
+
+ if ((listener->Bind(bind_address) != 0)
+ || (listener->Listen(5) != 0)) {
+ delete listener;
+ return false;
+ }
+
+ LOG(LS_INFO) << "Proxy listener available @ "
+ << listener->GetLocalAddress().ToString();
+
+ listener->SignalReadEvent.connect(this, &FileShareSession::OnProxyAccept);
+ proxy_listener = listener;
+ }
+
+ if (proxy_listener->GetState() == talk_base::Socket::CS_CLOSED) {
+ if (is_remote) {
+ address = remote_listener_address_;
+ return true;
+ }
+ return false;
+ }
+
+ address = proxy_listener->GetLocalAddress();
+ return !address.IsAny();
+}
+
+talk_base::StreamInterface* FileShareSession::CreateChannel(
+ const std::string& channel_name) {
+ ASSERT(NULL != session_);
+
+ // Send a heads-up for our new channel
+ cricket::Session::XmlElements els;
+ buzz::XmlElement* xel_channel = new buzz::XmlElement(QN_SHARE_CHANNEL, true);
+ xel_channel->AddAttr(buzz::QN_NAME, channel_name);
+ els.push_back(xel_channel);
+ session_->SendInfoMessage(els);
+
+ cricket::PseudoTcpChannel* channel =
+ new cricket::PseudoTcpChannel(talk_base::Thread::Current(), session_);
+ VERIFY(channel->Connect(channel_name));
+ return channel->GetStream();
+}
+
+void FileShareSession::SetState(FileShareState state, bool prevent_close) {
+ if (state == state_)
+ return;
+
+ if (IsComplete()) {
+ // Entering a completion state is permanent.
+ ASSERT(false);
+ return;
+ }
+
+ state_ = state;
+ if (IsComplete()) {
+ // All completion states auto-close except for FS_COMPLETE
+ bool close = (state_ > FS_COMPLETE) || !prevent_close;
+ if (close) {
+ DoClose(true);
+ }
+ }
+
+ SignalState(state_);
+}
+
+void FileShareSession::OnInitiate() {
+ // Cache the variables we will need, in case session_ goes away
+ is_sender_ = session_->initiator();
+ jid_ = buzz::Jid(session_->remote_name());
+ manifest_ = new FileShareManifest(description()->manifest);
+ source_path_ = description()->source_path;
+ preview_path_ = description()->preview_path;
+
+ if (local_folder_.empty()) {
+ LOG(LS_ERROR) << "FileShareSession - no local folder, using temp";
+ talk_base::Pathname temp_folder;
+ talk_base::Filesystem::GetTemporaryFolder(temp_folder, true, NULL);
+ local_folder_ = temp_folder.pathname();
+ }
+ LOG(LS_INFO) << session_->state();
+ SetState(FS_OFFER, false);
+}
+
+void FileShareSession::NextDownload() {
+ if (FS_TRANSFER != state_)
+ return;
+
+ if (item_transferring_ >= manifest_->size()) {
+ // Notify the other side that transfer has completed
+ cricket::Session::XmlElements els;
+ els.push_back(new buzz::XmlElement(QN_SHARE_COMPLETE, true));
+ session_->SendInfoMessage(els);
+ SetState(FS_COMPLETE, !proxies_.empty());
+ return;
+ }
+
+ const FileShareManifest::Item& item = manifest_->item(item_transferring_);
+ if ((item.type != FileShareManifest::T_FILE)
+ && (item.type != FileShareManifest::T_IMAGE)
+ && (item.type != FileShareManifest::T_FOLDER)) {
+ item_transferring_ += 1;
+ NextDownload();
+ return;
+ }
+
+ const bool is_folder = (item.type == FileShareManifest::T_FOLDER);
+ talk_base::Pathname temp_name;
+ temp_name.SetFilename(item.name);
+ if (!talk_base::CreateUniqueFile(temp_name, !is_folder)) {
+ SetState(FS_FAILURE, false);
+ return;
+ }
+
+ talk_base::StreamInterface* stream = NULL;
+ if (is_folder) {
+ // Convert unique filename into unique foldername
+ temp_name.AppendFolder(temp_name.filename());
+ temp_name.SetFilename("");
+ talk_base::TarStream* tar = new talk_base::TarStream;
+ // Note: the 'target' directory will be a subdirectory of the transfer_path_
+ talk_base::Pathname target;
+ target.SetFolder(item.name);
+ tar->AddFilter(target.pathname());
+ if (!tar->Open(temp_name.pathname(), false)) {
+ delete tar;
+ SetState(FS_FAILURE, false);
+ return;
+ }
+ stream = tar;
+ tar->SignalNextEntry.connect(this, &FileShareSession::OnNextEntry);
+ } else {
+ talk_base::FileStream* file = new talk_base::FileStream;
+ if (!file->Open(temp_name.pathname().c_str(), "wb")) {
+ delete file;
+ talk_base::Filesystem::DeleteFile(temp_name);
+ SetState(FS_FAILURE, false);
+ return;
+ }
+ stream = file;
+ }
+
+ ASSERT(NULL != stream);
+ transfer_path_ = temp_name.pathname();
+
+ std::string remote_path;
+ GetItemNetworkPath(item_transferring_, false, &remote_path);
+
+ StreamCounter* counter = new StreamCounter(stream);
+ counter->SignalUpdateByteCount.connect(this, &FileShareSession::OnUpdateBytes);
+ counter_ = counter;
+
+ http_client_->reset();
+ http_client_->set_server(talk_base::SocketAddress(jid_.Str(), 0, false));
+ http_client_->request().verb = talk_base::HV_GET;
+ http_client_->request().path = remote_path;
+ http_client_->response().document.reset(counter);
+ http_client_->start();
+}
+
+
+const FileShareSession::FileShareDescription* FileShareSession::description()
+const {
+ ASSERT(NULL != session_);
+ const cricket::SessionDescription* desc =
+ session_->initiator() ? session_->description()
+ : session_->remote_description();
+ return static_cast<const FileShareDescription*>(desc);
+}
+
+void FileShareSession::DoClose(bool terminate) {
+ ASSERT(!is_closed_);
+ ASSERT(IsComplete());
+ ASSERT(NULL != session_);
+
+ is_closed_ = true;
+
+ if (http_client_) {
+ http_client_->reset();
+ }
+ if (http_server_) {
+ http_server_->CloseAll(true);
+ // Currently, CloseAll doesn't result in OnHttpRequestComplete callback.
+ // If we change that, the following resetting won't be necessary.
+ transfer_connection_id_ = talk_base::HTTP_INVALID_CONNECTION_ID;
+ transfer_name_.clear();
+ counter_ = NULL;
+ }
+ // 'reset' and 'CloseAll' cause counter_ to clear.
+ ASSERT(NULL == counter_);
+
+ if (remote_listener_) {
+ // Cache the address for the remote_listener_, so that we can continue to
+ // present a consistent URL for remote previews, which is necessary for IE
+ // to continue using its cached copy.
+ remote_listener_address_ = remote_listener_->GetLocalAddress();
+ remote_listener_->Close();
+ LOG(LS_INFO) << "Proxy listener closed @ "
+ << remote_listener_address_.ToString();
+ }
+
+ if (terminate) {
+ session_->Terminate();
+ }
+}
+
+//////////////////////////////
+/// FileShareSessionClient //
+////////////////////////////
+
+void FileShareSessionClient::OnSessionCreate(cricket::Session* session,
+ bool received_initiate) {
+ VERIFY(sessions_.insert(session).second);
+ if (received_initiate) {
+ FileShareSession* share = new FileShareSession(session, user_agent_);
+ SignalFileShareSessionCreate(share);
+ UNUSED(share); // FileShareSession registers itself with the UI
+ }
+}
+
+void FileShareSessionClient::OnSessionDestroy(cricket::Session* session) {
+ VERIFY(1 == sessions_.erase(session));
+}
+
+const cricket::SessionDescription* FileShareSessionClient::CreateSessionDescription(
+ const buzz::XmlElement* element) {
+ FileShareSession::FileShareDescription* share_desc =
+ new FileShareSession::FileShareDescription;
+
+ if (element->Name() != QN_SHARE_DESCRIPTION)
+ return share_desc;
+
+ const buzz::XmlElement* manifest = element->FirstNamed(QN_SHARE_MANIFEST);
+ const buzz::XmlElement* protocol = element->FirstNamed(QN_SHARE_PROTOCOL);
+
+ if (!manifest || !protocol)
+ return share_desc;
+
+ for (const buzz::XmlElement* item = manifest->FirstElement();
+ item != NULL; item = item->NextElement()) {
+ bool is_folder;
+ if (item->Name() == QN_SHARE_FOLDER) {
+ is_folder = true;
+ } else if (item->Name() == QN_SHARE_FILE) {
+ is_folder = false;
+ } else {
+ continue;
+ }
+ std::string name;
+ if (const buzz::XmlElement* el_name = item->FirstNamed(QN_SHARE_NAME)) {
+ name = el_name->BodyText();
+ }
+ if (name.empty()) {
+ continue;
+ }
+ size_t size = FileShareManifest::SIZE_UNKNOWN;
+ if (item->HasAttr(QN_SIZE)) {
+ size = strtoul(item->Attr(QN_SIZE).c_str(), NULL, 10);
+ }
+ if (is_folder) {
+ share_desc->manifest.AddFolder(name, size);
+ } else {
+ // Check if there is a valid image description for this file.
+ if (const buzz::XmlElement* image = item->FirstNamed(QN_SHARE_IMAGE)) {
+ if (image->HasAttr(QN_WIDTH) && image->HasAttr(QN_HEIGHT)) {
+ size_t width = strtoul(image->Attr(QN_WIDTH).c_str(), NULL, 10);
+ size_t height = strtoul(image->Attr(QN_HEIGHT).c_str(), NULL, 10);
+ if (AllowedImageDimensions(width, height)) {
+ share_desc->manifest.AddImage(name, size, width, height);
+ continue;
+ }
+ }
+ }
+ share_desc->manifest.AddFile(name, size);
+ }
+ }
+
+ if (const buzz::XmlElement* http = protocol->FirstNamed(QN_SHARE_HTTP)) {
+ share_desc->supports_http = true;
+ for (const buzz::XmlElement* url = http->FirstNamed(QN_SHARE_URL);
+ url != NULL; url = url->NextNamed(QN_SHARE_URL)) {
+ if (url->Attr(buzz::QN_NAME) == kHttpSourcePath) {
+ share_desc->source_path = url->BodyText();
+ } else if (url->Attr(buzz::QN_NAME) == kHttpPreviewPath) {
+ share_desc->preview_path = url->BodyText();
+ }
+ }
+ }
+
+ return share_desc;
+}
+
+buzz::XmlElement* FileShareSessionClient::TranslateSessionDescription(
+ const cricket::SessionDescription* description) {
+
+ const FileShareSession::FileShareDescription* share_desc =
+ static_cast<const FileShareSession::FileShareDescription*>(description);
+
+ scoped_ptr<buzz::XmlElement> el(new buzz::XmlElement(QN_SHARE_DESCRIPTION,
+ true));
+
+ const FileShareManifest& manifest = share_desc->manifest;
+ el->AddElement(new buzz::XmlElement(QN_SHARE_MANIFEST));
+ for (size_t i=0; i<manifest.size(); ++i) {
+ const FileShareManifest::Item& item = manifest.item(i);
+ buzz::QName qname;
+ if (item.type == FileShareManifest::T_FOLDER) {
+ qname = QN_SHARE_FOLDER;
+ } else if ((item.type == FileShareManifest::T_FILE)
+ || (item.type == FileShareManifest::T_IMAGE)) {
+ qname = QN_SHARE_FILE;
+ } else {
+ ASSERT(false);
+ continue;
+ }
+ el->AddElement(new buzz::XmlElement(qname), 1);
+ if (item.size != FileShareManifest::SIZE_UNKNOWN) {
+ char buffer[256];
+ talk_base::sprintfn(buffer, sizeof(buffer), "%lu", item.size);
+ el->AddAttr(QN_SIZE, buffer, 2);
+ }
+ buzz::XmlElement* el_name = new buzz::XmlElement(QN_SHARE_NAME);
+ el_name->SetBodyText(item.name);
+ el->AddElement(el_name, 2);
+ if ((item.type == FileShareManifest::T_IMAGE)
+ && AllowedImageDimensions(item.width, item.height)) {
+ el->AddElement(new buzz::XmlElement(QN_SHARE_IMAGE), 2);
+ char buffer[256];
+ talk_base::sprintfn(buffer, sizeof(buffer), "%lu", item.width);
+ el->AddAttr(QN_WIDTH, buffer, 3);
+ talk_base::sprintfn(buffer, sizeof(buffer), "%lu", item.height);
+ el->AddAttr(QN_HEIGHT, buffer, 3);
+ }
+ }
+
+ el->AddElement(new buzz::XmlElement(QN_SHARE_PROTOCOL));
+ if (share_desc->supports_http) {
+ el->AddElement(new buzz::XmlElement(QN_SHARE_HTTP), 1);
+ if (!share_desc->source_path.empty()) {
+ buzz::XmlElement* url = new buzz::XmlElement(QN_SHARE_URL);
+ url->SetAttr(buzz::QN_NAME, kHttpSourcePath);
+ url->SetBodyText(share_desc->source_path);
+ el->AddElement(url, 2);
+ }
+ if (!share_desc->preview_path.empty()) {
+ buzz::XmlElement* url = new buzz::XmlElement(QN_SHARE_URL);
+ url->SetAttr(buzz::QN_NAME, kHttpPreviewPath);
+ url->SetBodyText(share_desc->preview_path);
+ el->AddElement(url, 2);
+ }
+ }
+
+ return el.release();
+}
+
+FileShareSession *FileShareSessionClient::CreateFileShareSession() {
+ cricket::Session* session = sm_->CreateSession(jid_.Str(),
+ NS_GOOGLE_SHARE);
+ FileShareSession* share = new FileShareSession(session, user_agent_);
+ SignalFileShareSessionCreate(share);
+ return share;
+}
+
+
+} // namespace cricket
diff --git a/Plugins/jingle/libjingle/talk/session/fileshare/fileshare.h b/Plugins/jingle/libjingle/talk/session/fileshare/fileshare.h
new file mode 100644
index 0000000..b0802b4
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/session/fileshare/fileshare.h
@@ -0,0 +1,250 @@
+#ifndef TALK_APP_WIN32_FILESHARE_H__
+#define TALK_APP_WIN32_FILESHARE_H__
+#include "talk/base/messagequeue.h"
+#include "talk/base/socketpool.h"
+#include "talk/base/stringutils.h"
+#include "talk/base/sigslot.h"
+#include "talk/p2p/base/session.h"
+#include "talk/p2p/base/sessiondescription.h"
+#include "talk/xmpp/jid.h"
+
+class StreamCounter;
+class StreamRelay;
+
+namespace talk_base {
+ class HttpClient;
+ class HttpServer;
+ class HttpTransaction;
+}
+
+extern const std::string NS_GOOGLE_SHARE;
+
+
+namespace cricket {
+
+///////////////////////////////////////////////////////////////////////////////
+// FileShareManifest
+///////////////////////////////////////////////////////////////////////////////
+
+class FileShareManifest {
+public:
+ enum Type { T_FILE, T_IMAGE, T_FOLDER };
+ enum { SIZE_UNKNOWN = talk_base::SIZE_UNKNOWN };
+
+ struct Item {
+ Type type;
+ std::string name;
+ size_t size, width, height;
+ };
+ typedef std::vector<Item> ItemList;
+
+ inline bool empty() const { return items_.empty(); }
+ inline size_t size() const { return items_.size(); }
+ inline const Item& item(size_t index) const { return items_[index]; }
+
+ void AddFile(const std::string& name, size_t size);
+ void AddImage(const std::string& name, size_t size,
+ size_t width, size_t height);
+ void AddFolder(const std::string& name, size_t size);
+
+ size_t GetItemCount(Type t) const;
+ inline size_t GetFileCount() const { return GetItemCount(T_FILE); }
+ inline size_t GetImageCount() const { return GetItemCount(T_IMAGE); }
+ inline size_t GetFolderCount() const { return GetItemCount(T_FOLDER); }
+
+private:
+ ItemList items_;
+};
+
+
+enum FileShareState {
+ FS_NONE, // Initialization
+ FS_OFFER, // Offer extended
+ FS_TRANSFER, // In progress
+ FS_COMPLETE, // Completed successfully
+ FS_LOCAL_CANCEL, // Local side cancelled
+ FS_REMOTE_CANCEL, // Remote side cancelled
+ FS_FAILURE // An error occurred during transfer
+};
+
+
+class FileShareSession
+ : public talk_base::StreamPool,
+ public talk_base::MessageHandler,
+ public sigslot::has_slots<> {
+public:
+ struct FileShareDescription : public cricket::SessionDescription {
+ FileShareManifest manifest;
+ bool supports_http;
+ std::string source_path;
+ std::string preview_path;
+ FileShareDescription() : supports_http(false) { }
+ };
+
+ FileShareSession(cricket::Session* session, const std::string &user_agent);
+ virtual ~FileShareSession();
+
+ bool IsComplete() const;
+ bool IsClosed() const;
+ FileShareState state() const;
+ sigslot::signal1<FileShareState> SignalState;
+ sigslot::signal1<FileShareSession*> SignalNextFile;
+ sigslot::signal1<FileShareSession*> SignalUpdateProgress;
+ sigslot::signal4<std::string, int, int, talk_base::HttpTransaction*> SignalResampleImage;
+
+ void ResampleComplete(talk_base::StreamInterface *si, talk_base::HttpTransaction *trans, bool success);
+
+ bool is_sender() const;
+ const buzz::Jid& jid() const;
+ const FileShareManifest* manifest() const;
+ const std::string& local_folder() const;
+
+ void SetLocalFolder(const std::string& folder) { local_folder_ = folder; }
+ void Share(const buzz::Jid& jid, FileShareManifest* manifest);
+
+ void Accept();
+ void Decline();
+ void Cancel();
+
+ bool GetItemUrl(size_t index, std::string* url);
+ bool GetImagePreviewUrl(size_t index, size_t width, size_t height,
+ std::string* url);
+ // Returns true if the transferring item size is known
+ bool GetProgress(size_t& bytes) const;
+ // Returns true if the total size is known
+ bool GetTotalSize(size_t& bytes) const;
+ // Returns true if currently transferring item name is known
+ bool GetCurrentItemName(std::string* name);
+
+ // TODO: Eliminate this eventually?
+ cricket::Session* session() { return session_; }
+
+ // StreamPool Interface
+ virtual talk_base::StreamInterface*
+ RequestConnectedStream(const talk_base::SocketAddress& remote, int* err);
+ virtual void ReturnConnectedStream(talk_base::StreamInterface* stream);
+
+ // MessageHandler Interface
+ virtual void OnMessage(talk_base::Message* msg);
+
+ void GetItemNetworkPath(size_t index, bool preview, std::string* path);
+
+private:
+ typedef std::list<StreamRelay*> ProxyList;
+ typedef std::list<talk_base::HttpTransaction*> TransactionList;
+
+ // Session Signals
+ void OnSessionState(cricket::Session* session, cricket::Session::State state);
+ void OnSessionInfoMessage(cricket::Session* session,
+ const cricket::Session::XmlElements& els);
+ void OnSessionChannelGone(cricket::Session* session,
+ const std::string& name);
+
+ // HttpClient Signals
+ void OnHttpClientComplete(talk_base::HttpClient* http, int err);
+ void OnHttpClientClosed(talk_base::HttpClient* http, int err);
+
+ // HttpServer Signals
+ void OnHttpRequest(talk_base::HttpServer* server,
+ talk_base::HttpTransaction* transaction);
+ void OnHttpRequestComplete(talk_base::HttpServer* server,
+ talk_base::HttpTransaction* transaction,
+ int err);
+ void OnHttpConnectionClosed(talk_base::HttpServer* server,
+ int err,
+ talk_base::StreamInterface* stream);
+
+ // TarStream Signals
+ void OnNextEntry(const std::string& name, size_t size);
+
+ // Socket Signals
+ void OnProxyAccept(talk_base::AsyncSocket* socket);
+ void OnProxyClosed(StreamRelay* proxy, int error);
+
+ // StreamCounterSignals
+ void OnUpdateBytes(size_t count);
+
+ // Internal Helpers
+ void GenerateTemporaryPrefix(std::string* prefix);
+ bool GetItemBaseUrl(size_t index, bool preview, std::string* url);
+ bool GetProxyAddress(talk_base::SocketAddress& address, bool is_remote);
+ talk_base::StreamInterface* CreateChannel(const std::string& channel_name);
+ void SetState(FileShareState state, bool prevent_close);
+ void OnInitiate();
+ void NextDownload();
+ const FileShareDescription* description() const;
+ void DoClose(bool terminate);
+
+ cricket::Session* session_;
+ FileShareState state_;
+ bool is_closed_;
+ bool is_sender_;
+ buzz::Jid jid_;
+ FileShareManifest* manifest_;
+ std::string source_path_;
+ std::string preview_path_;
+ std::string local_folder_;
+
+ // The currently active p2p streams to our peer
+ talk_base::StreamCache pool_;
+ // The http client state (client only)
+ talk_base::HttpClient* http_client_;
+ // The http server state (server only)
+ talk_base::HttpServer* http_server_;
+ // The connection id of the currently transferring file (server)
+ int transfer_connection_id_;
+ // The counter for the currently transferring file
+ const StreamCounter* counter_;
+ // The number of manifest items that have successfully transferred
+ size_t item_transferring_;
+ // The byte count of successfully transferred items
+ size_t bytes_transferred_;
+ // Where the currently transferring item is being (temporarily) saved (client)
+ std::string transfer_path_;
+ // The name of the currently transferring item
+ std::string transfer_name_;
+ // Where the files are saved after transfer (client)
+ std::vector<std::string> stored_location_;
+ // Was it a local cancel? Or a remote cancel?
+ bool local_cancel_;
+ // Proxy socket for local IE http requests
+ talk_base::AsyncSocket* local_listener_;
+ // Proxy socket for remote IE http requests
+ talk_base::AsyncSocket* remote_listener_;
+ // Cached address of remote_listener_
+ talk_base::SocketAddress remote_listener_address_;
+ // Uniqueness for channel names
+ size_t next_channel_id_;
+ // Proxy relays
+ ProxyList proxies_;
+ std::string user_agent_;
+ TransactionList transactions_;
+};
+
+class FileShareSessionClient : public SessionClient
+{
+ public:
+ FileShareSessionClient(SessionManager *sm, buzz::Jid jid, const std::string &user_agent) : sm_(sm), jid_(jid),
+ user_agent_(user_agent) {}
+ virtual void OnSessionCreate(cricket::Session* session,
+ bool received_initiate);
+ virtual void OnSessionDestroy(cricket::Session* session);
+ virtual const cricket::SessionDescription* CreateSessionDescription(const buzz::XmlElement* element);
+ virtual buzz::XmlElement* TranslateSessionDescription(const cricket::SessionDescription* description);
+ FileShareSession *CreateFileShareSession();
+
+ sigslot::signal1<FileShareSession*> SignalFileShareSessionCreate;
+ sigslot::signal1<FileShareSession*> SignalFileShareSessionDestroy;
+
+ private:
+ SessionManager *sm_;
+ buzz::Jid jid_;
+ friend class FileShareSession;
+ typedef std::set<cricket::Session*> SessionSet;
+ SessionSet sessions_;
+ std::string user_agent_;
+};
+
+} // namespace cricket
+
+#endif // TALK_APP_WIN32_FILESHARE_H__
diff --git a/Plugins/jingle/libjingle/talk/session/phone/Makefile.am b/Plugins/jingle/libjingle/talk/session/phone/Makefile.am
new file mode 100644
index 0000000..5d489b6
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/session/phone/Makefile.am
@@ -0,0 +1,27 @@
+EXTRA_DIST=gipslitemediaengine.cc gipslitemediaengine.h
+if GIPS
+nodist_libcricketsessionphone_la_SOURCES=gipsmediaengine.cc \
+ gipsstatsmonitor.cc
+else
+dist_libcricketsessionphone_la_SOURCES=linphonemediaengine.cc
+endif
+
+libcricketsessionphone_la_SOURCES = audiomonitor.cc \
+ channelmanager.cc \
+ voicechannel.cc \
+ call.cc \
+ phonesessionclient.cc
+
+noinst_HEADERS = audiomonitor.h \
+ channelmanager.h \
+ linphonemediaengine.h \
+ mediaengine.h \
+ phonesessionclient.h \
+ voicechannel.h \
+ call.h \
+ mediachannel.h \
+ codec.h
+
+AM_CPPFLAGS := -DPOSIX $(ORTP_CFLAGS) $(ILBC_CFLAGS) -I$(top_srcdir)/talk/third_party/mediastreamer $(GLIB_CFLAGS)
+noinst_LTLIBRARIES = libcricketsessionphone.la
+
diff --git a/Plugins/jingle/libjingle/talk/session/phone/Makefile.in b/Plugins/jingle/libjingle/talk/session/phone/Makefile.in
new file mode 100644
index 0000000..8dfdbc8
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/session/phone/Makefile.in
@@ -0,0 +1,485 @@
+# Makefile.in generated by automake 1.9.6 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005 Free Software Foundation, Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+top_builddir = ../../..
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+INSTALL = @INSTALL@
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+target_triplet = @target@
+subdir = talk/session/phone
+DIST_COMMON = $(noinst_HEADERS) $(srcdir)/Makefile.am \
+ $(srcdir)/Makefile.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/talk/pkg.m4 \
+ $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+LTLIBRARIES = $(noinst_LTLIBRARIES)
+libcricketsessionphone_la_LIBADD =
+am_libcricketsessionphone_la_OBJECTS = audiomonitor.lo \
+ channelmanager.lo voicechannel.lo call.lo \
+ phonesessionclient.lo
+am__dist_libcricketsessionphone_la_SOURCES_DIST = \
+ linphonemediaengine.cc
+@GIPS_FALSE@dist_libcricketsessionphone_la_OBJECTS = \
+@GIPS_FALSE@ linphonemediaengine.lo
+@GIPS_TRUE@nodist_libcricketsessionphone_la_OBJECTS = \
+@GIPS_TRUE@ gipsmediaengine.lo gipsstatsmonitor.lo
+libcricketsessionphone_la_OBJECTS = \
+ $(am_libcricketsessionphone_la_OBJECTS) \
+ $(dist_libcricketsessionphone_la_OBJECTS) \
+ $(nodist_libcricketsessionphone_la_OBJECTS)
+DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+ $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS)
+LTCXXCOMPILE = $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CXXFLAGS) $(CXXFLAGS)
+CXXLD = $(CXX)
+CXXLINK = $(LIBTOOL) --tag=CXX --mode=link $(CXXLD) $(AM_CXXFLAGS) \
+ $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
+SOURCES = $(libcricketsessionphone_la_SOURCES) \
+ $(dist_libcricketsessionphone_la_SOURCES) \
+ $(nodist_libcricketsessionphone_la_SOURCES)
+DIST_SOURCES = $(libcricketsessionphone_la_SOURCES) \
+ $(am__dist_libcricketsessionphone_la_SOURCES_DIST)
+HEADERS = $(noinst_HEADERS)
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+ALSA_LIBS = @ALSA_LIBS@
+AMDEP_FALSE = @AMDEP_FALSE@
+AMDEP_TRUE = @AMDEP_TRUE@
+AMTAR = @AMTAR@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+ECHO = @ECHO@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+EXPAT_LIBS = @EXPAT_LIBS@
+F77 = @F77@
+FFLAGS = @FFLAGS@
+GIPS_FALSE = @GIPS_FALSE@
+GIPS_TRUE = @GIPS_TRUE@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_LIBS = @GLIB_LIBS@
+ILBC_CFLAGS = @ILBC_CFLAGS@
+ILBC_LIBS = @ILBC_LIBS@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LDFLAGS = @LDFLAGS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+MAKEINFO = @MAKEINFO@
+MEDIA_LIBS = @MEDIA_LIBS@
+OBJEXT = @OBJEXT@
+ORTP_CFLAGS = @ORTP_CFLAGS@
+ORTP_LIBS = @ORTP_LIBS@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PHONE_FALSE = @PHONE_FALSE@
+PHONE_TRUE = @PHONE_TRUE@
+PKG_CONFIG = @PKG_CONFIG@
+RANLIB = @RANLIB@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SPEEX_CFLAGS = @SPEEX_CFLAGS@
+SPEEX_LIBS = @SPEEX_LIBS@
+STRIP = @STRIP@
+VERSION = @VERSION@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_F77 = @ac_ct_F77@
+ac_ct_RANLIB = @ac_ct_RANLIB@
+ac_ct_STRIP = @ac_ct_STRIP@
+am__fastdepCC_FALSE = @am__fastdepCC_FALSE@
+am__fastdepCC_TRUE = @am__fastdepCC_TRUE@
+am__fastdepCXX_FALSE = @am__fastdepCXX_FALSE@
+am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+datadir = @datadir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+sysconfdir = @sysconfdir@
+target = @target@
+target_alias = @target_alias@
+target_cpu = @target_cpu@
+target_os = @target_os@
+target_vendor = @target_vendor@
+EXTRA_DIST = gipslitemediaengine.cc gipslitemediaengine.h
+@GIPS_TRUE@nodist_libcricketsessionphone_la_SOURCES = gipsmediaengine.cc \
+@GIPS_TRUE@ gipsstatsmonitor.cc
+
+@GIPS_FALSE@dist_libcricketsessionphone_la_SOURCES = linphonemediaengine.cc
+libcricketsessionphone_la_SOURCES = audiomonitor.cc \
+ channelmanager.cc \
+ voicechannel.cc \
+ call.cc \
+ phonesessionclient.cc
+
+noinst_HEADERS = audiomonitor.h \
+ channelmanager.h \
+ linphonemediaengine.h \
+ mediaengine.h \
+ phonesessionclient.h \
+ voicechannel.h \
+ call.h \
+ mediachannel.h \
+ codec.h
+
+AM_CPPFLAGS := -DPOSIX $(ORTP_CFLAGS) $(ILBC_CFLAGS) -I$(top_srcdir)/talk/third_party/mediastreamer $(GLIB_CFLAGS)
+noinst_LTLIBRARIES = libcricketsessionphone.la
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .cc .lo .o .obj
+$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \
+ && exit 0; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu talk/session/phone/Makefile'; \
+ cd $(top_srcdir) && \
+ $(AUTOMAKE) --gnu talk/session/phone/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+clean-noinstLTLIBRARIES:
+ -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES)
+ @list='$(noinst_LTLIBRARIES)'; for p in $$list; do \
+ dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
+ test "$$dir" != "$$p" || dir=.; \
+ echo "rm -f \"$${dir}/so_locations\""; \
+ rm -f "$${dir}/so_locations"; \
+ done
+libcricketsessionphone.la: $(libcricketsessionphone_la_OBJECTS) $(libcricketsessionphone_la_DEPENDENCIES)
+ $(CXXLINK) $(libcricketsessionphone_la_LDFLAGS) $(libcricketsessionphone_la_OBJECTS) $(libcricketsessionphone_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/audiomonitor.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/call.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/channelmanager.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gipsmediaengine.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gipsstatsmonitor.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/linphonemediaengine.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/phonesessionclient.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/voicechannel.Plo@am__quote@
+
+.cc.o:
+@am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \
+@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ $<
+
+.cc.obj:
+@am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \
+@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.cc.lo:
+@am__fastdepCXX_TRUE@ if $(LTCXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \
+@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Plo"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(LTCXXCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+distclean-libtool:
+ -rm -f libtool
+uninstall-info-am:
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ mkid -fID $$unique
+tags: TAGS
+
+TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ tags=; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$tags $$unique; \
+ fi
+ctags: CTAGS
+CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ tags=; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ test -z "$(CTAGS_ARGS)$$tags$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$tags $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && cd $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) $$here
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \
+ list='$(DISTFILES)'; for file in $$list; do \
+ case $$file in \
+ $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \
+ $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \
+ esac; \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test "$$dir" != "$$file" && test "$$dir" != "."; then \
+ dir="/$$dir"; \
+ $(mkdir_p) "$(distdir)$$dir"; \
+ else \
+ dir=''; \
+ fi; \
+ if test -d $$d/$$file; then \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \
+ fi; \
+ cp -pR $$d/$$file $(distdir)$$dir || exit 1; \
+ else \
+ test -f $(distdir)/$$file \
+ || cp -p $$d/$$file $(distdir)/$$file \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(LTLIBRARIES) $(HEADERS)
+installdirs:
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ `test -z '$(STRIP)' || \
+ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-libtool distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-exec-am:
+
+install-info: install-info-am
+
+install-man:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-info-am
+
+.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \
+ clean-libtool clean-noinstLTLIBRARIES ctags distclean \
+ distclean-compile distclean-generic distclean-libtool \
+ distclean-tags distdir dvi dvi-am html html-am info info-am \
+ install install-am install-data install-data-am install-exec \
+ install-exec-am install-info install-info-am install-man \
+ install-strip installcheck installcheck-am installdirs \
+ maintainer-clean maintainer-clean-generic mostlyclean \
+ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \
+ pdf pdf-am ps ps-am tags uninstall uninstall-am \
+ uninstall-info-am
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/Plugins/jingle/libjingle/talk/session/phone/audiomonitor.cc b/Plugins/jingle/libjingle/talk/session/phone/audiomonitor.cc
new file mode 100644
index 0000000..c1f3614
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/session/phone/audiomonitor.cc
@@ -0,0 +1,120 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "talk/session/phone/audiomonitor.h"
+#include "talk/session/phone/voicechannel.h"
+#include <cassert>
+
+namespace cricket {
+
+const uint32 MSG_MONITOR_POLL = 1;
+const uint32 MSG_MONITOR_START = 2;
+const uint32 MSG_MONITOR_STOP = 3;
+const uint32 MSG_MONITOR_SIGNAL = 4;
+
+AudioMonitor::AudioMonitor(VoiceChannel *voice_channel,
+ talk_base::Thread *monitor_thread) {
+ voice_channel_ = voice_channel;
+ monitoring_thread_ = monitor_thread;
+ monitoring_ = false;
+}
+
+AudioMonitor::~AudioMonitor() {
+ voice_channel_->worker_thread()->Clear(this);
+ monitoring_thread_->Clear(this);
+}
+
+void AudioMonitor::Start(int milliseconds) {
+ rate_ = milliseconds;
+ if (rate_ < 100)
+ rate_ = 100;
+ voice_channel_->worker_thread()->Post(this, MSG_MONITOR_START);
+}
+
+void AudioMonitor::Stop() {
+ voice_channel_->worker_thread()->Post(this, MSG_MONITOR_STOP);
+}
+
+void AudioMonitor::OnMessage(talk_base::Message *message) {
+ talk_base::CritScope cs(&crit_);
+
+ switch (message->message_id) {
+ case MSG_MONITOR_START:
+ assert(talk_base::Thread::Current() == voice_channel_->worker_thread());
+ if (!monitoring_) {
+ monitoring_ = true;
+ PollVoiceChannel();
+ }
+ break;
+
+ case MSG_MONITOR_STOP:
+ assert(talk_base::Thread::Current() == voice_channel_->worker_thread());
+ if (monitoring_) {
+ monitoring_ = false;
+ voice_channel_->worker_thread()->Clear(this);
+ }
+ break;
+
+ case MSG_MONITOR_POLL:
+ assert(talk_base::Thread::Current() == voice_channel_->worker_thread());
+ PollVoiceChannel();
+ break;
+
+ case MSG_MONITOR_SIGNAL:
+ {
+ assert(talk_base::Thread::Current() == monitoring_thread_);
+ AudioInfo info = audio_info_;
+ crit_.Leave();
+ SignalUpdate(this, audio_info_);
+ crit_.Enter();
+ }
+ break;
+ }
+}
+
+void AudioMonitor::PollVoiceChannel() {
+ talk_base::CritScope cs(&crit_);
+ assert(talk_base::Thread::Current() == voice_channel_->worker_thread());
+
+ // Gather connection infos
+ audio_info_.input_level = voice_channel_->GetInputLevel_w();
+ audio_info_.output_level = voice_channel_->GetOutputLevel_w();
+
+ // Signal the monitoring thread, start another poll timer
+ monitoring_thread_->Post(this, MSG_MONITOR_SIGNAL);
+ voice_channel_->worker_thread()->PostDelayed(rate_, this, MSG_MONITOR_POLL);
+}
+
+VoiceChannel *AudioMonitor::voice_channel() {
+ return voice_channel_;
+}
+
+talk_base::Thread *AudioMonitor::monitor_thread() {
+ return monitoring_thread_;
+}
+
+}
diff --git a/Plugins/jingle/libjingle/talk/session/phone/audiomonitor.h b/Plugins/jingle/libjingle/talk/session/phone/audiomonitor.h
new file mode 100644
index 0000000..2bfd784
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/session/phone/audiomonitor.h
@@ -0,0 +1,74 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _CRICKET_PHONE_AUDIOMONITOR_H_
+#define _CRICKET_PHONE_AUDIOMONITOR_H_
+
+#include "talk/base/thread.h"
+#include "talk/base/sigslot.h"
+#include "talk/p2p/base/port.h"
+#include <vector>
+
+namespace cricket {
+
+class VoiceChannel;
+
+
+struct AudioInfo {
+ int input_level;
+ int output_level;
+};
+
+class AudioMonitor : public talk_base::MessageHandler,
+ public sigslot::has_slots<> {
+public:
+ AudioMonitor(VoiceChannel* voice_channel, talk_base::Thread *monitor_thread);
+ ~AudioMonitor();
+
+ void Start(int cms);
+ void Stop();
+
+ VoiceChannel* voice_channel();
+ talk_base::Thread *monitor_thread();
+
+ sigslot::signal2<AudioMonitor*, const AudioInfo&> SignalUpdate;
+
+protected:
+ void OnMessage(talk_base::Message *message);
+ void PollVoiceChannel();
+
+ AudioInfo audio_info_;
+ VoiceChannel* voice_channel_;
+ talk_base::Thread* monitoring_thread_;
+ talk_base::CriticalSection crit_;
+ uint32 rate_;
+ bool monitoring_;
+};
+
+}
+
+#endif // _CRICKET_PHONE_AUDIOMONITOR_H_
diff --git a/Plugins/jingle/libjingle/talk/session/phone/call.cc b/Plugins/jingle/libjingle/talk/session/phone/call.cc
new file mode 100644
index 0000000..1676945
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/session/phone/call.cc
@@ -0,0 +1,336 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "talk/base/helpers.h"
+#include "talk/base/logging.h"
+#include "talk/base/thread.h"
+#include "talk/session/phone/call.h"
+
+namespace cricket {
+
+const uint32 MSG_CHECKAUTODESTROY = 1;
+const uint32 MSG_TERMINATECALL = 2;
+
+namespace {
+const int kSendToVoicemailTimeout = 1000*20;
+const int kNoVoicemailTimeout = 1000*180;
+const int kMediaMonitorInterval = 1000*15;
+}
+
+Call::Call(PhoneSessionClient *session_client)
+ : muted_(false), send_to_voicemail_(true)
+{
+ session_client_ = session_client;
+ id_ = CreateRandomId();
+}
+
+Call::~Call() {
+ while (sessions_.begin() != sessions_.end()) {
+ Session *session = sessions_[0];
+ RemoveSession(session);
+ session_client_->session_manager()->DestroySession(session);
+ }
+ talk_base::Thread::Current()->Clear(this);
+}
+
+Session *Call::InitiateSession(const buzz::Jid &jid,
+ std::vector<buzz::XmlElement*>* extra_xml) {
+ Session *session = session_client_->CreateSession(this);
+ AddSession(session);
+ session->Initiate(jid.Str(), extra_xml,
+ session_client_->CreateOfferSessionDescription());
+
+ // After this timeout, terminate the call because the callee isn't
+ // answering
+ session_client_->session_manager()->signaling_thread()->Clear(this,
+ MSG_TERMINATECALL);
+ session_client_->session_manager()->signaling_thread()->PostDelayed(
+ send_to_voicemail_ ? kSendToVoicemailTimeout : kNoVoicemailTimeout,
+ this, MSG_TERMINATECALL);
+ return session;
+}
+
+void Call::AcceptSession(Session *session) {
+ std::vector<Session *>::iterator it;
+ it = std::find(sessions_.begin(), sessions_.end(), session);
+ assert(it != sessions_.end());
+ if (it != sessions_.end()) {
+ session->Accept(session_client_->CreateAcceptSessionDescription(
+ session->remote_description()));
+ }
+}
+
+void Call::RedirectSession(Session *session, const buzz::Jid &to) {
+ std::vector<Session *>::iterator it;
+ it = std::find(sessions_.begin(), sessions_.end(), session);
+ assert(it != sessions_.end());
+ if (it != sessions_.end())
+ session->Redirect(to.Str());
+
+ session_client_->session_manager()->signaling_thread()->Clear(this,
+ MSG_TERMINATECALL);
+ session_client_->session_manager()->signaling_thread()->PostDelayed(
+ send_to_voicemail_ ? kSendToVoicemailTimeout : kNoVoicemailTimeout,
+ this, MSG_TERMINATECALL);
+}
+
+void Call::RejectSession(Session *session) {
+ std::vector<Session *>::iterator it;
+ it = std::find(sessions_.begin(), sessions_.end(), session);
+ assert(it != sessions_.end());
+ if (it != sessions_.end())
+ session->Reject();
+}
+
+void Call::TerminateSession(Session *session) {
+ assert(std::find(sessions_.begin(), sessions_.end(), session)
+ != sessions_.end());
+ std::vector<Session *>::iterator it;
+ it = std::find(sessions_.begin(), sessions_.end(), session);
+ if (it != sessions_.end())
+ (*it)->Terminate();
+}
+
+void Call::Terminate() {
+ // Copy the list so that we can iterate over it in a stable way
+ std::vector<Session *> sessions = sessions_;
+
+ // There may be more than one session to terminate
+ std::vector<Session *>::iterator it;
+ for (it = sessions.begin(); it != sessions.end(); it++)
+ TerminateSession(*it);
+
+}
+
+void Call::OnMessage(talk_base::Message *message) {
+ switch (message->message_id) {
+ case MSG_CHECKAUTODESTROY:
+ // If no more sessions for this call, delete it
+ if (sessions_.size() == 0)
+ session_client_->DestroyCall(this);
+ break;
+ case MSG_TERMINATECALL:
+ // Signal to the user that a timeout has happened and the call should
+ // be sent to voicemail.
+ if (send_to_voicemail_) {
+ SignalSetupToCallVoicemail();
+ }
+
+ // Callee didn't answer - terminate call
+ Terminate();
+ break;
+ }
+}
+
+const std::vector<Session *> &Call::sessions() {
+ return sessions_;
+}
+
+void Call::AddSession(Session *session) {
+ // Add session to list, create voice channel for this session
+ sessions_.push_back(session);
+ session->SignalState.connect(this, &Call::OnSessionState);
+ session->SignalError.connect(this, &Call::OnSessionError);
+ session->SignalReceivedTerminateReason
+ .connect(this, &Call::OnReceivedTerminateReason);
+
+ VoiceChannel *channel
+ = session_client_->channel_manager()->CreateVoiceChannel(session);
+ channel_map_[session->id()] = channel;
+
+ // Start the media monitor for this voicechannel
+ channel->SignalMediaMonitor.connect(this, &Call::OnMediaMonitor);
+ channel->StartMediaMonitor(kMediaMonitorInterval);
+
+ // If this call has the focus, enable this channel
+ if (session_client_->GetFocus() == this)
+ channel->Enable(true);
+
+ // Signal client
+ SignalAddSession(this, session);
+}
+
+void Call::RemoveSession(Session *session) {
+ // Remove session from list
+ std::vector<Session *>::iterator it_session;
+ it_session = std::find(sessions_.begin(), sessions_.end(), session);
+ if (it_session == sessions_.end())
+ return;
+ sessions_.erase(it_session);
+
+ // Destroy session channel
+ std::map<SessionID, VoiceChannel *>::iterator it_channel;
+ it_channel = channel_map_.find(session->id());
+ if (it_channel != channel_map_.end()) {
+ VoiceChannel *channel = it_channel->second;
+ channel_map_.erase(it_channel);
+ channel->StopMediaMonitor();
+ session_client_->channel_manager()->DestroyVoiceChannel(channel);
+ }
+
+ // Signal client
+ SignalRemoveSession(this, session);
+
+
+
+ // The call auto destroys when the lass session is removed
+ talk_base::Thread::Current()->Post(this, MSG_CHECKAUTODESTROY);
+}
+
+VoiceChannel* Call::GetChannel(Session* session) {
+ std::map<SessionID, VoiceChannel *>::iterator it
+ = channel_map_.find(session->id());
+ assert(it != channel_map_.end());
+ return it->second;
+}
+
+void Call::EnableChannels(bool enable) {
+ std::vector<Session *>::iterator it;
+ for (it = sessions_.begin(); it != sessions_.end(); it++) {
+ VoiceChannel *channel = channel_map_[(*it)->id()];
+ if (channel != NULL)
+ channel->Enable(enable);
+ }
+}
+
+void Call::Mute(bool mute) {
+ muted_ = mute;
+ std::vector<Session *>::iterator it;
+ for (it = sessions_.begin(); it != sessions_.end(); it++) {
+ VoiceChannel *channel = channel_map_[(*it)->id()];
+ if (channel != NULL)
+ channel->Mute(mute);
+ }
+}
+
+
+void Call::Join(Call *call, bool enable) {
+ while (call->sessions_.size() != 0) {
+ // Move session
+ Session *session = call->sessions_[0];
+ call->sessions_.erase(call->sessions_.begin());
+ sessions_.push_back(session);
+ session->SignalState.connect(this, &Call::OnSessionState);
+ session->SignalError.connect(this, &Call::OnSessionError);
+ session->SignalReceivedTerminateReason
+ .connect(this, &Call::OnReceivedTerminateReason);
+
+ // Move channel
+ std::map<SessionID, VoiceChannel *>::iterator it_channel;
+ it_channel = call->channel_map_.find(session->id());
+ if (it_channel != call->channel_map_.end()) {
+ VoiceChannel *channel = (*it_channel).second;
+ call->channel_map_.erase(it_channel);
+ channel_map_[session->id()] = channel;
+ channel->Enable(enable);
+ }
+ }
+}
+
+void Call::StartConnectionMonitor(Session *session, int cms) {
+ std::map<SessionID, VoiceChannel *>::iterator it_channel;
+ it_channel = channel_map_.find(session->id());
+ if (it_channel != channel_map_.end()) {
+ VoiceChannel *channel = (*it_channel).second;
+ channel->SignalConnectionMonitor.connect(this, &Call::OnConnectionMonitor);
+ channel->StartConnectionMonitor(cms);
+ }
+}
+
+void Call::StopConnectionMonitor(Session *session) {
+ std::map<SessionID, VoiceChannel *>::iterator it_channel;
+ it_channel = channel_map_.find(session->id());
+ if (it_channel != channel_map_.end()) {
+ VoiceChannel *channel = (*it_channel).second;
+ channel->StopConnectionMonitor();
+ channel->SignalConnectionMonitor.disconnect(this);
+ }
+}
+
+void Call::StartAudioMonitor(Session *session, int cms) {
+ std::map<SessionID, VoiceChannel *>::iterator it_channel;
+ it_channel = channel_map_.find(session->id());
+ if (it_channel != channel_map_.end()) {
+ VoiceChannel *channel = (*it_channel).second;
+ channel->SignalAudioMonitor.connect(this, &Call::OnAudioMonitor);
+ channel->StartAudioMonitor(cms);
+ }
+}
+
+void Call::StopAudioMonitor(Session *session) {
+ std::map<SessionID, VoiceChannel *>::iterator it_channel;
+ it_channel = channel_map_.find(session->id());
+ if (it_channel != channel_map_.end()) {
+ VoiceChannel *channel = (*it_channel).second;
+ channel->StopAudioMonitor();
+ channel->SignalAudioMonitor.disconnect(this);
+ }
+}
+
+
+void Call::OnConnectionMonitor(VoiceChannel *channel,
+ const std::vector<ConnectionInfo> &infos) {
+ SignalConnectionMonitor(this, channel->session(), infos);
+}
+
+void Call::OnAudioMonitor(VoiceChannel *channel, const AudioInfo& info) {
+ SignalAudioMonitor(this, channel->session(), info);
+}
+
+void Call::OnMediaMonitor(VoiceChannel *channel, const MediaInfo& info) {
+ SignalMediaMonitor(this, channel->session(), info);
+}
+
+uint32 Call::id() {
+ return id_;
+}
+
+void Call::OnSessionState(Session *session, Session::State state) {
+ switch (state) {
+ case Session::STATE_RECEIVEDACCEPT:
+ case Session::STATE_RECEIVEDREJECT:
+ case Session::STATE_RECEIVEDTERMINATE:
+ session_client_->session_manager()->signaling_thread()->Clear(this,
+ MSG_TERMINATECALL);
+ break;
+ }
+ SignalSessionState(this, session, state);
+}
+
+void Call::OnSessionError(Session *session, Session::Error error) {
+ session_client_->session_manager()->signaling_thread()->Clear(this,
+ MSG_TERMINATECALL);
+ SignalSessionError(this, session, error);
+}
+
+void Call::OnReceivedTerminateReason(Session *session, const std::string &reason) {
+ session_client_->session_manager()->signaling_thread()->Clear(this,
+ MSG_TERMINATECALL);
+ SignalReceivedTerminateReason(this, session, reason);
+}
+
+}
diff --git a/Plugins/jingle/libjingle/talk/session/phone/call.h b/Plugins/jingle/libjingle/talk/session/phone/call.h
new file mode 100644
index 0000000..7b8d9b6
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/session/phone/call.h
@@ -0,0 +1,116 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _CALL_H_
+#define _CALL_H_
+
+#include "talk/base/messagequeue.h"
+#include "talk/p2p/base/session.h"
+#include "talk/p2p/client/socketmonitor.h"
+#include "talk/xmpp/jid.h"
+#include "talk/session/phone/phonesessionclient.h"
+#include "talk/session/phone/voicechannel.h"
+#include "talk/session/phone/audiomonitor.h"
+
+#include <map>
+#include <vector>
+#include <deque>
+
+namespace cricket {
+
+class PhoneSessionClient;
+
+class Call : public talk_base::MessageHandler, public sigslot::has_slots<> {
+public:
+ Call(PhoneSessionClient *session_client);
+ ~Call();
+
+ Session *InitiateSession(const buzz::Jid &jid,
+ std::vector<buzz::XmlElement*>* extra_xml);
+ void AcceptSession(Session *session);
+ void RedirectSession(Session *session, const buzz::Jid &to);
+ void RejectSession(Session *session);
+ void TerminateSession(Session *session);
+ void Terminate();
+ void StartConnectionMonitor(Session *session, int cms);
+ void StopConnectionMonitor(Session *session);
+ void StartAudioMonitor(Session *session, int cms);
+ void StopAudioMonitor(Session *session);
+ void Mute(bool mute);
+
+
+ const std::vector<Session *> &sessions();
+ uint32 id();
+ bool muted() const { return muted_; }
+
+ // Setting this to false will cause the call to have a longer timeout and
+ // for the SignalSetupToCallVoicemail to never fire.
+ void set_send_to_voicemail(bool send_to_voicemail) {
+ send_to_voicemail_ = send_to_voicemail;
+ }
+ bool send_to_voicemail() { return send_to_voicemail_; }
+
+ // Sets a flag on the chatapp that will redirect the call to voicemail once
+ // the call has been terminated
+ sigslot::signal0<> SignalSetupToCallVoicemail;
+ sigslot::signal2<Call *, Session *> SignalAddSession;
+ sigslot::signal2<Call *, Session *> SignalRemoveSession;
+ sigslot::signal3<Call *, Session *, Session::State> SignalSessionState;
+ sigslot::signal3<Call *, Session *, Session::Error> SignalSessionError;
+ sigslot::signal3<Call *, Session *, const std::string &> SignalReceivedTerminateReason;
+ sigslot::signal3<Call *, Session *, const std::vector<ConnectionInfo> &> SignalConnectionMonitor;
+ sigslot::signal3<Call *, Session *, const AudioInfo&> SignalAudioMonitor;
+ sigslot::signal3<Call *, Session *, const MediaInfo&> SignalMediaMonitor;
+
+private:
+ void OnMessage(talk_base::Message *message);
+ void OnSessionState(Session *session, Session::State state);
+ void OnSessionError(Session *session, Session::Error error);
+ void OnReceivedTerminateReason(Session *session, const std::string &reason);
+ void AddSession(Session *session);
+ void RemoveSession(Session *session);
+ void EnableChannels(bool enable);
+ void Join(Call *call, bool enable);
+ void OnConnectionMonitor(VoiceChannel *channel, const std::vector<ConnectionInfo> &infos);
+ void OnAudioMonitor(VoiceChannel *channel, const AudioInfo& info);
+ void OnMediaMonitor(VoiceChannel *channel, const MediaInfo& info);
+ VoiceChannel* GetChannel(Session* session);
+
+ uint32 id_;
+ PhoneSessionClient *session_client_;
+ std::vector<Session *> sessions_;
+ std::map<SessionID, VoiceChannel *> channel_map_;
+ bool muted_;
+ bool send_to_voicemail_;
+
+
+ friend class PhoneSessionClient;
+};
+
+}
+
+#endif // _CALL_H_
diff --git a/Plugins/jingle/libjingle/talk/session/phone/channelmanager.cc b/Plugins/jingle/libjingle/talk/session/phone/channelmanager.cc
new file mode 100644
index 0000000..77b7a73
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/session/phone/channelmanager.cc
@@ -0,0 +1,219 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef HAVE_GIPS
+#include "talk/session/phone/gipsmediaengine.h"
+#elif HAVE_GIPSLITE
+#include "talk/session/phone/gipslitemediaengine.h"
+#else
+#include "talk/session/phone/linphonemediaengine.h"
+#endif
+#include "channelmanager.h"
+#include <cassert>
+#include <iostream>
+namespace cricket {
+
+const uint32 MSG_CREATEVOICECHANNEL = 1;
+const uint32 MSG_DESTROYVOICECHANNEL = 2;
+const uint32 MSG_SETAUDIOOPTIONS = 3;
+
+ChannelManager::ChannelManager(talk_base::Thread *worker_thread) {
+#ifdef HAVE_GIPS
+ media_engine_ = new GipsMediaEngine();
+#elif HAVE_GIPSLITE
+ media_engine_ = new GipsLiteMediaEngine();
+#else
+ media_engine_ = new LinphoneMediaEngine();
+#endif
+ worker_thread_ = worker_thread;
+ initialized_ = false;
+ Init();
+}
+
+ChannelManager::~ChannelManager() {
+ Exit();
+}
+
+MediaEngine *ChannelManager::media_engine() {
+ return media_engine_;
+}
+
+bool ChannelManager::Init() {
+ initialized_ = media_engine_->Init();
+ return initialized_;
+}
+
+void ChannelManager::Exit() {
+ if (!initialized_)
+ return;
+
+ // Need to destroy the voice channels
+
+ while (true) {
+ crit_.Enter();
+ VoiceChannel *channel = NULL;
+ if (channels_.begin() != channels_.end())
+ channel = channels_[0];
+ crit_.Leave();
+ if (channel == NULL)
+ break;
+ delete channel;
+ }
+ media_engine_->Terminate();
+}
+
+struct CreateParams {
+ Session *session;
+ VoiceChannel *channel;
+};
+
+VoiceChannel *ChannelManager::CreateVoiceChannel(Session *session) {
+ CreateParams params;
+ params.session = session;
+ params.channel = NULL;
+ talk_base::TypedMessageData<CreateParams *> data(&params);
+ worker_thread_->Send(this, MSG_CREATEVOICECHANNEL, &data);
+ return params.channel;
+}
+
+VoiceChannel *ChannelManager::CreateVoiceChannel_w(Session *session) {
+ talk_base::CritScope cs(&crit_);
+
+ // This is ok to alloc from a thread other than the worker thread
+ assert(initialized_);
+ MediaChannel *channel = media_engine_->CreateChannel();
+ if (channel == NULL)
+ return NULL;
+
+ VoiceChannel *voice_channel = new VoiceChannel(this, session, channel);
+ channels_.push_back(voice_channel);
+ return voice_channel;
+}
+
+void ChannelManager::DestroyVoiceChannel(VoiceChannel *voice_channel) {
+ talk_base::TypedMessageData<VoiceChannel *> data(voice_channel);
+ worker_thread_->Send(this, MSG_DESTROYVOICECHANNEL, &data);
+}
+
+void ChannelManager::DestroyVoiceChannel_w(VoiceChannel *voice_channel) {
+ talk_base::CritScope cs(&crit_);
+ // Destroy voice channel.
+ assert(initialized_);
+ std::vector<VoiceChannel *>::iterator it = std::find(channels_.begin(),
+ channels_.end(), voice_channel);
+ assert(it != channels_.end());
+ if (it == channels_.end())
+ return;
+
+ channels_.erase(it);
+ MediaChannel *channel = voice_channel->channel();
+ delete voice_channel;
+ delete channel;
+}
+
+void ChannelManager::SetAudioOptions(bool auto_gain_control, int wave_in_device,
+ int wave_out_device) {
+ AudioOptions options;
+ options.auto_gain_control = auto_gain_control;
+ options.wave_in_device = wave_in_device;
+ options.wave_out_device = wave_out_device;
+ talk_base::TypedMessageData<AudioOptions> data(options);
+ worker_thread_->Send(this, MSG_SETAUDIOOPTIONS, &data);
+}
+
+void ChannelManager::SetAudioOptions_w(AudioOptions options) {
+ assert(worker_thread_ == talk_base::Thread::Current());
+
+ // Set auto gain control on
+ if (media_engine_->SetAudioOptions(
+ options.auto_gain_control ? MediaEngine::AUTO_GAIN_CONTROL : 0) != 0) {
+ // TODO: We need to log these failures.
+ }
+
+ // Set the audio devices
+ // This will fail if audio is already playing. Stop all of the media
+ // start it up again after changing the setting.
+ {
+ talk_base::CritScope cs(&crit_);
+ for (VoiceChannels::iterator it = channels_.begin();
+ it < channels_.end();
+ ++it) {
+ (*it)->PauseMedia_w();
+ }
+
+ if (media_engine_->SetSoundDevices(options.wave_in_device,
+ options.wave_out_device) == -1) {
+ // TODO: We need to log these failures.
+ }
+
+ for (VoiceChannels::iterator it = channels_.begin();
+ it < channels_.end();
+ ++it) {
+ (*it)->UnpauseMedia_w();
+ }
+ }
+}
+
+talk_base::Thread *ChannelManager::worker_thread() {
+ return worker_thread_;
+}
+
+void ChannelManager::OnMessage(talk_base::Message *message) {
+ switch (message->message_id) {
+ case MSG_CREATEVOICECHANNEL:
+ {
+ talk_base::TypedMessageData<CreateParams *> *data
+ = static_cast<talk_base::TypedMessageData<CreateParams *> *>(
+ message->pdata);
+ data->data()->channel = CreateVoiceChannel_w(data->data()->session);
+ }
+ break;
+
+ case MSG_DESTROYVOICECHANNEL:
+ {
+ talk_base::TypedMessageData<VoiceChannel *> *data
+ = static_cast<talk_base::TypedMessageData<VoiceChannel *> *>(
+ message->pdata);
+ DestroyVoiceChannel_w(data->data());
+ }
+ break;
+ case MSG_SETAUDIOOPTIONS:
+ {
+ talk_base::TypedMessageData<AudioOptions> *data
+ = static_cast<talk_base::TypedMessageData<AudioOptions> *>(
+ message->pdata);
+ SetAudioOptions_w(data->data());
+ }
+ break;
+ }
+}
+
+}
diff --git a/Plugins/jingle/libjingle/talk/session/phone/channelmanager.h b/Plugins/jingle/libjingle/talk/session/phone/channelmanager.h
new file mode 100644
index 0000000..c17d5bb
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/session/phone/channelmanager.h
@@ -0,0 +1,80 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _CHANNELMANAGER_H_
+#define _CHANNELMANAGER_H_
+
+#include "talk/base/thread.h"
+#include "talk/base/criticalsection.h"
+#include "talk/p2p/base/session.h"
+#include "talk/session/phone/voicechannel.h"
+#include "talk/session/phone/mediaengine.h"
+#include <vector>
+
+namespace cricket {
+
+class VoiceChannel;
+
+class ChannelManager : public talk_base::MessageHandler {
+public:
+ ChannelManager(talk_base::Thread *worker_thread);
+ ~ChannelManager();
+
+ VoiceChannel *CreateVoiceChannel(Session *session);
+ void DestroyVoiceChannel(VoiceChannel *voice_channel);
+ void SetAudioOptions(bool auto_gain_control, int wave_in_device,
+ int wave_out_device);
+
+ MediaEngine *media_engine();
+ talk_base::Thread *worker_thread();
+
+private:
+ VoiceChannel *CreateVoiceChannel_w(Session *session);
+ void DestroyVoiceChannel_w(VoiceChannel *voice_channel);
+ void OnMessage(talk_base::Message *message);
+ bool Init();
+ void Exit();
+
+ struct AudioOptions {
+ bool auto_gain_control;
+ int wave_in_device;
+ int wave_out_device;
+ };
+ void SetAudioOptions_w(AudioOptions options);
+
+ talk_base::Thread *worker_thread_;
+ MediaEngine *media_engine_;
+ bool initialized_;
+ talk_base::CriticalSection crit_;
+
+ typedef std::vector<VoiceChannel*> VoiceChannels;
+ VoiceChannels channels_;
+};
+
+}
+
+#endif // _CHANNELMANAGER_H_
diff --git a/Plugins/jingle/libjingle/talk/session/phone/codec.h b/Plugins/jingle/libjingle/talk/session/phone/codec.h
new file mode 100644
index 0000000..ef99c2b
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/session/phone/codec.h
@@ -0,0 +1,47 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+#ifndef _CODEC_H_
+#define _CODEC_H_
+
+struct Codec {
+ int id;
+ std::string name;
+ int clockrate;
+ int bitrate;
+ int channels;
+
+ int preference;
+
+ // Creates a codec with the given parameters.
+ Codec(int pt, const std::string& nm, int cr, int br, int cs, int pr) :
+ id(pt), name(nm), clockrate(cr), preference(pr), bitrate(br), channels(cs) {}
+ // Ranks codecs by their preferences.
+};
+
+#endif // CODEC_H_
diff --git a/Plugins/jingle/libjingle/talk/session/phone/gipslitemediaengine.cc b/Plugins/jingle/libjingle/talk/session/phone/gipslitemediaengine.cc
new file mode 100644
index 0000000..6609790
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/session/phone/gipslitemediaengine.cc
@@ -0,0 +1,239 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+// GipsLiteMediaEngine is a GIPS Voice Engine Lite implementation of MediaEngine
+#include "talk/base/logging.h"
+#include <cassert>
+#include <iostream>
+#include "gipslitemediaengine.h"
+using namespace cricket;
+
+#if 0
+#define TRACK(x) LOG(LS_VERBOSE) << x
+#else
+#define TRACK(x)
+#endif
+
+//#define GIPS_TRACING
+
+namespace {
+struct CodecPref { const char* name; int clockrate; int pref; };
+const CodecPref kGIPSCodecPrefs[] = {
+ { "ISAC", 1600, 7 },
+ { "speex", 1600, 6 },
+ { "IPCMWB", 1600, 6},
+ { "speex", 8000, 4},
+ { "iLBC", 8000, 1 },
+ { "G723", 8000, 4 },
+ { "EG711U", 8000, 3 },
+ { "EG711A", 8000, 3 },
+ { "PCMU", 8000, 2 },
+ { "PCMA", 8000, 2 },
+ { "CN", 8000, 2 },
+ { "red", 8000, -1 },
+ { "telephone-event", 8000, -1 }
+};
+const size_t kNumGIPSCodecs = sizeof(kGIPSCodecPrefs) / sizeof(CodecPref);
+}
+
+
+void GipsLiteMediaChannel::SetCodecs(const std::vector<Codec> &codecs) {
+ GIPS_CodecInst c;
+ std::vector<Codec>::const_iterator i;
+
+
+ bool first = true;
+ for (i = codecs.begin(); i < codecs.end(); i++) {
+ if (engine_->FindGIPSCodec(*i, &c) == false)
+ continue;
+
+ if (c.pltype != i->id) {
+ c.pltype = i->id;
+ engine_->gips().GIPSVE_SetRecPayloadType(gips_channel_, &c);
+ }
+
+ if (first) {
+ LOG(LS_INFO) << "Using " << c.plname << "/" << c.plfreq;
+ engine_->gips().GIPSVE_SetSendCodec(gips_channel_, &c);
+ first = false;
+ }
+ }
+ if (first) {
+ // We're being asked to set an empty list of codecs. This will only happen when
+ // dealing with a buggy client. We'll send them the most common format: PCMU
+ Codec codec(0, "PCMU", 8000, 0, 1, 0);
+ LOG(LS_WARNING) << "Received empty list of codces; using PCMU/8000";
+ engine_->FindGIPSCodec(codec, &c);
+ engine_->gips().GIPSVE_SetSendCodec(gips_channel_, &c);
+ }
+}
+
+
+void GipsLiteMediaChannel::OnPacketReceived(const void *data, int len) {
+ engine_->gips().GIPSVE_ReceivedRTPPacket(gips_channel_, data, (int)len);
+}
+
+void GipsLiteMediaChannel::SetPlayout(bool playout) {
+ if (playout)
+ engine_->gips().GIPSVE_StartPlayout(gips_channel_);
+ else
+ engine_->gips().GIPSVE_StopPlayout(gips_channel_);
+}
+
+void GipsLiteMediaChannel::SetSend(bool send) {
+ if (send)
+ engine_->gips().GIPSVE_StartSend(gips_channel_);
+ else
+ engine_->gips().GIPSVE_StopSend(gips_channel_);
+}
+
+GipsLiteMediaChannel::GipsLiteMediaChannel(GipsLiteMediaEngine *engine) {
+ network_interface_ = NULL;
+ engine_ = engine;
+ gips_channel_ = engine_->gips().GIPSVE_CreateChannel();
+ engine_->gips().GIPSVE_SetSendTransport(gips_channel_, *this);
+}
+
+
+int GipsLiteMediaEngine::GetGIPSCodecPreference(const char *name, int clockrate) {
+ for (size_t i = 0; i < kNumGIPSCodecs; ++i) {
+ if ((strcmp(kGIPSCodecPrefs[i].name, name) == 0) &&
+ (kGIPSCodecPrefs[i].clockrate == clockrate))
+ return kGIPSCodecPrefs[i].pref;
+ }
+ assert(false);
+ return -1;
+}
+
+GipsLiteMediaEngine::GipsLiteMediaEngine() :
+ gips_(GetGipsVoiceEngineLite()) {}
+
+bool GipsLiteMediaEngine::Init() {
+
+ TRACK("GIPSVE_Init");
+ if (gips_.GIPSVE_Init() == -1)
+ return false;
+
+ char buffer[1024];
+ TRACK("GIPSVE_GetVersion");
+ int r = gips_.GIPSVE_GetVersion(buffer, sizeof(buffer));
+ LOG(LS_INFO) << "GIPS Version: " << r << ": " << buffer;
+
+ // Set auto gain control on
+ TRACK("GIPSVE_SetAGCStatus");
+ if (gips_.GIPSVE_SetAGCStatus(1) == -1)
+ return false;
+
+ TRACK("GIPSVE_GetNofCodecs");
+ int ncodecs = gips_.GIPSVE_GetNofCodecs();
+ for (int i = 0; i < ncodecs; ++i) {
+ GIPS_CodecInst gips_codec;
+ if (gips_.GIPSVE_GetCodec(i, &gips_codec) >= 0) {
+ Codec codec(gips_codec.pltype, gips_codec.plname, gips_codec.plfreq, gips_codec.rate,
+ gips_codec.channels, GetGIPSCodecPreference(gips_codec.plname, gips_codec.plfreq));
+ LOG(LS_INFO) << gips_codec.plname << "/" << gips_codec.plfreq << "/" << gips_codec.channels << " " << gips_codec.pltype;
+ codecs_.push_back(codec);
+ }
+ }
+ return true;
+}
+
+void GipsLiteMediaEngine::Terminate() {
+ gips_.GIPSVE_Terminate();
+}
+
+MediaChannel * GipsLiteMediaEngine::CreateChannel() {
+ return new GipsLiteMediaChannel(this);
+}
+
+bool GipsLiteMediaEngine::FindGIPSCodec(Codec codec, GIPS_CodecInst* gips_codec) {
+ int ncodecs = gips_.GIPSVE_GetNofCodecs();
+ for (int i = 0; i < ncodecs; ++i) {
+ GIPS_CodecInst gc;
+ if (gips_.GIPSVE_GetCodec(i, &gc) >= 0) {
+ if (codec.id < 96) {
+ // Compare by id
+ if (codec.id != gc.pltype)
+ continue;
+ } else {
+ // Compare by name
+ if (strcmp(codec.name.c_str(), gc.plname) != 0)
+ continue;
+ }
+
+ // If the clockrate is specified, make sure it matches
+ if (codec.clockrate > 0 && codec.clockrate != gc.plfreq)
+ continue;
+
+ // If the bitrate is specified, make sure it matches
+ if (codec.bitrate > 0 && codec.bitrate != gc.rate)
+ continue;
+
+ // Make sure the channels match
+ if (codec.channels != gc.channels)
+ continue;
+
+ // If we got this far, we match.
+ if (gips_codec)
+ *gips_codec = gc;
+ return true;
+ }
+ }
+ return false;
+}
+
+int GipsLiteMediaEngine::SetAudioOptions(int options) {
+ // Set auto gain control on
+ if (gips_.GIPSVE_SetAGCStatus(options & AUTO_GAIN_CONTROL ? 1 : 0) == -1) {
+ return -1;
+ // TODO: We need to log these failures.
+ }
+ return 0;
+}
+
+int GipsLiteMediaEngine::SetSoundDevices(int wave_in_device, int wave_out_device) {
+ if (gips_.GIPSVE_SetSoundDevices(wave_in_device, wave_out_device) == -1) {
+ int error = gips_.GIPSVE_GetLastError();
+ // TODO: We need to log these failures.
+ return error;
+ }
+ return 0;
+}
+
+bool GipsLiteMediaEngine::FindCodec(const Codec &codec)
+{
+ return FindGIPSCodec(codec, NULL);
+}
+
+std::vector<Codec> GipsLiteMediaEngine::codecs()
+{
+ return codecs_;
+} \ No newline at end of file
diff --git a/Plugins/jingle/libjingle/talk/session/phone/gipslitemediaengine.h b/Plugins/jingle/libjingle/talk/session/phone/gipslitemediaengine.h
new file mode 100644
index 0000000..56e1f2e
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/session/phone/gipslitemediaengine.h
@@ -0,0 +1,93 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+// GipsLiteMediaEngine is a GIPS Voice Engine Lite implementation of MediaEngine
+
+#ifndef TALK_SESSION_PHONE_GIPSLITEMEDIAENGINE_H_
+#define TALK_SESSION_PHONE_GIPSLITEMEDIAENGINE_H_
+
+#include "talk/third_party/gips/Interface/GipsVoiceEngineLite.h"
+#include "talk/third_party/gips/expiration.h"
+#include "talk/session/phone/mediaengine.h"
+
+namespace cricket {
+
+class GipsLiteMediaEngine : public MediaEngine {
+ public:
+ GipsLiteMediaEngine();
+ ~GipsLiteMediaEngine() {}
+ virtual bool Init();
+ virtual void Terminate();
+
+ virtual MediaChannel *CreateChannel();
+
+ virtual int SetAudioOptions(int options);
+ virtual int SetSoundDevices(int wave_in_device, int wave_out_device);
+
+ virtual int GetInputLevel() {return gips_.GIPSVE_GetInputLevel();}
+ virtual bool FindCodec(const Codec &codec);
+ virtual std::vector<Codec> codecs();
+ bool FindGIPSCodec(Codec codec, GIPS_CodecInst* gips_codec);
+ GipsVoiceEngineLite &gips() {return gips_;};
+
+ private:
+ GipsVoiceEngineLite & gips_;
+ int GetGIPSCodecPreference(const char *name, int clockrate);
+ std::vector<Codec> codecs_;
+};
+
+class GipsLiteMediaChannel : public MediaChannel, public GIPS_transport {
+ public:
+ GipsLiteMediaChannel(GipsLiteMediaEngine *me);
+ virtual ~GipsLiteMediaChannel() {
+ StopMediaMonitor();
+ engine_->gips().GIPSVE_DeleteChannel(gips_channel_);
+ }
+ virtual void SetCodecs(const std::vector<Codec> &codecs);
+ virtual void OnPacketReceived(const void *data, int len);
+
+ virtual void SetPlayout(bool playout);
+ virtual void SetSend(bool send);
+
+
+ virtual int GetOutputLevel() {return engine_->gips().GIPSVE_GetOutputLevel(gips_channel_);}
+ GipsLiteMediaEngine *engine();
+
+ virtual void StartMediaMonitor(VoiceChannel *voice_channel, uint32 cms) {}
+ virtual void StopMediaMonitor() {}
+
+ private:
+ GipsLiteMediaEngine *engine_;
+ int gips_channel_;
+
+ virtual int SendPacket(int channel, const void *data, int len) {if (network_interface_) network_interface_->SendPacket(data, len); return 1;}
+ virtual int SendRTCPPacket(int channel, const void *data, int len) {return 1;}
+};
+
+} // namespace cricket
+
+#endif // TALK_SESSION_PHONE_GIPSLITEMEDIAENGINE_H_
diff --git a/Plugins/jingle/libjingle/talk/session/phone/linphonemediaengine.cc b/Plugins/jingle/libjingle/talk/session/phone/linphonemediaengine.cc
new file mode 100644
index 0000000..436e16e
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/session/phone/linphonemediaengine.cc
@@ -0,0 +1,207 @@
+/*
+ * Jingle call example
+ * Copyright 2004--2005, Google Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+// LinphoneMediaEngine is a Linphone implementation of MediaEngine
+#include <mediastreamer2/mediastream.h>
+//#ifdef HAVE_ILBC
+//#include <mediastreamer2/msilbcdec.h>
+//#endif
+//#ifdef HAVE_SPEEX
+//#include <mediastreamer2/msspeexdec.h>
+//#endif
+
+#include <ortp/ortp.h>
+#include <ortp/telephonyevents.h>
+//#include <netdb.h>
+//#include <unistd.h>
+#include <fcntl.h>
+#include <iostream>
+#include "talk/base/logging.h"
+#include "talk/base/thread.h"
+#include "talk/base/helpers.h"
+#include "talk/session/phone/codec.h"
+#include "talk/session/phone/linphonemediaengine.h"
+
+using namespace cricket;
+
+void LinphoneMediaChannel::OnIncomingData(talk_base::AsyncSocket *s)
+{
+ char *buf[2048];
+ int len;
+ len = s->Recv(buf, sizeof(buf));
+ if (network_interface_ && !mute_)
+ network_interface_->SendPacket(buf, len);
+}
+
+LinphoneMediaChannel::LinphoneMediaChannel(LinphoneMediaEngine*eng) :
+ pt_(-1),
+ audio_stream_(0),
+ engine_(eng) {
+
+ talk_base::Thread *thread = talk_base::ThreadManager::CurrentThread();
+ talk_base::SocketServer *ss = thread->socketserver();
+ socket_.reset(ss->CreateAsyncSocket(SOCK_DGRAM));
+
+ port_in_ = 2000 + CreateRandomId() % 1000;
+ port_out_ = 3000 + CreateRandomId() % 1000;
+
+ socket_->Bind(talk_base::SocketAddress("localhost", port_out_));
+ socket_->SignalReadEvent.connect(this, &LinphoneMediaChannel::OnIncomingData);
+}
+
+LinphoneMediaChannel::~LinphoneMediaChannel() {
+ if (audio_stream_ != NULL)
+ audio_stream_stop(audio_stream_);
+}
+
+void LinphoneMediaChannel::SetCodecs(const std::vector<Codec> &codecs) {
+ bool first = true;
+ std::vector<Codec>::const_iterator i;
+
+ for (i = codecs.begin(); i < codecs.end(); i++) {
+
+ if (!engine_->FindCodec(*i))
+ continue;
+#ifdef HAVE_ILBC
+ if (i->name == payload_type_ilbc.mime_type) {
+ rtp_profile_set_payload(&av_profile, i->id, &payload_type_ilbc);
+ }
+#endif
+#ifdef HAVE_SPEEX
+ if (i->name == payload_type_speex_wb.mime_type && i->clockrate == payload_type_speex_wb.clock_rate) {
+ rtp_profile_set_payload(&av_profile, i->id, &payload_type_speex_wb);
+ } else if (i->name == payload_type_speex_nb.mime_type && i->clockrate == payload_type_speex_nb.clock_rate) {
+ rtp_profile_set_payload(&av_profile, i->id, &payload_type_speex_nb);
+ }
+#endif
+
+ if (i->id == 0)
+ rtp_profile_set_payload(&av_profile, 0, &payload_type_pcmu8000);
+
+ if (i->name == payload_type_telephone_event.mime_type) {
+ rtp_profile_set_payload(&av_profile, i->id, &payload_type_telephone_event);
+ }
+
+ if (first) {
+ LOG(LS_INFO) << "Using " << i->name << "/" << i->clockrate;
+ pt_ = i->id;
+ audio_stream_ = audio_stream_start(&av_profile, port_in_, "127.0.0.1", port_out_, i->id, 250, false);
+ first = false;
+ }
+ }
+
+ if (first) {
+ // We're being asked to set an empty list of codecs. This will only happen when
+ // working with a buggy client; let's try PCMU.
+ LOG(LS_WARNING) << "Received empty list of codces; using PCMU/8000";
+ audio_stream_ = audio_stream_start(&av_profile, port_in_, "127.0.0.1", port_out_, 0, 250, false);
+ }
+
+}
+
+bool LinphoneMediaEngine::FindCodec(const Codec &c) {
+ if (c.id == 0)
+ return true;
+ if (c.name == payload_type_telephone_event.mime_type)
+ return true;
+#ifdef HAVE_SPEEX
+ if (c.name == payload_type_speex_wb.mime_type && c.clockrate == payload_type_speex_wb.clock_rate)
+ return true;
+ if (c.name == payload_type_speex_nb.mime_type && c.clockrate == payload_type_speex_nb.clock_rate)
+ return true;
+#endif
+#ifdef HAVE_ILBC
+ if (c.name == payload_type_ilbc.mime_type)
+ return true;
+#endif
+return false;
+}
+
+void LinphoneMediaChannel::OnPacketReceived(const void *data, int len) {
+ uint8 buf[2048];
+ memcpy(buf, data, len);
+
+ /* We may receive packets with payload type 13: comfort noise. Linphone can't
+ * handle them, so let's ignore those packets.
+ */
+ int payloadtype = buf[1] & 0x7f;
+ if (play_ && payloadtype != 13)
+ socket_->SendTo(buf, len, talk_base::SocketAddress("localhost", port_in_));
+}
+
+void LinphoneMediaChannel::SetPlayout(bool playout) {
+ play_ = playout;
+}
+
+void LinphoneMediaChannel::SetSend(bool send) {
+ mute_ = !send;
+}
+
+int LinphoneMediaChannel::GetOutputLevel() { return 0; }
+
+LinphoneMediaEngine::LinphoneMediaEngine() {}
+LinphoneMediaEngine::~LinphoneMediaEngine() {}
+
+/*
+static void null_log_handler(const char *log_domain,
+ GLogLevelFlags log_level,
+ const char *message,
+ gpointer user_data) {
+ LOG(LS_INFO) << log_domain << " " << message;
+}
+*/
+
+bool LinphoneMediaEngine::Init() {
+// g_log_set_handler("MediaStreamer", G_LOG_LEVEL_MASK, null_log_handler, this);
+// g_log_set_handler("oRTP", G_LOG_LEVEL_MASK, null_log_handler, this);
+// g_log_set_handler("oRTP-stats", G_LOG_LEVEL_MASK, null_log_handler, this);
+ ortp_init();
+ ms_init();
+
+#ifdef HAVE_SPEEX
+ //ms_speex_codec_init();
+
+ codecs_.push_back(Codec(110, payload_type_speex_wb.mime_type, payload_type_speex_wb.clock_rate, 0, 1, 8));
+ codecs_.push_back(Codec(111, payload_type_speex_nb.mime_type, payload_type_speex_nb.clock_rate, 0, 1, 7));
+
+#endif
+
+#ifdef HAVE_ILBC
+ //ms_ilbc_codec_init();
+ codecs_.push_back(Codec(102, payload_type_ilbc.mime_type, payload_type_ilbc.clock_rate, 0, 1, 4));
+#endif
+
+ codecs_.push_back(Codec(0, payload_type_pcmu8000.mime_type, payload_type_pcmu8000.clock_rate, 0, 1, 2));
+ codecs_.push_back(Codec(101, payload_type_telephone_event.mime_type, payload_type_telephone_event.clock_rate, 0, 1, 1));
+ return true;
+}
+
+void LinphoneMediaEngine::Terminate() {
+
+}
+
+MediaChannel *LinphoneMediaEngine::CreateChannel() {
+ return new LinphoneMediaChannel(this);
+}
+
+int LinphoneMediaEngine::SetAudioOptions(int options) { return 0; }
+int LinphoneMediaEngine::SetSoundDevices(int wave_in_device, int wave_out_device) { return 0; }
+
+float LinphoneMediaEngine::GetCurrentQuality() { return 0; }
+int LinphoneMediaEngine::GetInputLevel() { return 0; }
diff --git a/Plugins/jingle/libjingle/talk/session/phone/linphonemediaengine.h b/Plugins/jingle/libjingle/talk/session/phone/linphonemediaengine.h
new file mode 100644
index 0000000..8e57d43
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/session/phone/linphonemediaengine.h
@@ -0,0 +1,88 @@
+/*
+ * Jingle call example
+ * Copyright 2004--2005, Google Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+// LinphoneMediaEngine is a Linphone implementation of MediaEngine
+
+#ifndef TALK_SESSION_PHONE_LINPHONEMEDIAENGINE_H_
+#define TALK_SESSION_PHONE_LINPHONEMEDIAENGINE_H_
+
+#include <mediastreamer2/mediastream.h>
+#include "talk/base/asyncsocket.h"
+#include "talk/base/scoped_ptr.h"
+#include "talk/session/phone/mediaengine.h"
+
+namespace cricket {
+
+class LinphoneMediaEngine;
+
+class LinphoneMediaChannel : public MediaChannel {
+ public:
+ LinphoneMediaChannel(LinphoneMediaEngine *eng);
+ virtual ~LinphoneMediaChannel();
+
+ virtual void SetCodecs(const std::vector<Codec> &codecs);
+ virtual void OnPacketReceived(const void *data, int len);
+
+ virtual void SetPlayout(bool playout);
+ virtual void SetSend(bool send);
+
+
+ virtual int GetOutputLevel();
+ bool mute() {return mute_;}
+
+ virtual void StartMediaMonitor(VoiceChannel * voice_channel, uint32 cms) {}
+ virtual void StopMediaMonitor() {}
+
+ private:
+ LinphoneMediaEngine *engine_;
+ AudioStream *audio_stream_;
+ talk_base::scoped_ptr<talk_base::AsyncSocket> socket_;
+ void OnIncomingData(talk_base::AsyncSocket *s);
+ int pt_;
+ bool mute_;
+ bool play_;
+ int port_in_;
+ int port_out_;
+};
+
+class LinphoneMediaEngine : public MediaEngine {
+ public:
+ LinphoneMediaEngine();
+ ~LinphoneMediaEngine();
+ virtual bool Init();
+ virtual void Terminate();
+
+ virtual MediaChannel *CreateChannel();
+
+ virtual int SetAudioOptions(int options);
+ virtual int SetSoundDevices(int wave_in_device, int wave_out_device);
+
+ virtual float GetCurrentQuality();
+ virtual int GetInputLevel();
+
+ virtual std::vector<Codec, std::allocator<Codec> > codecs() {return codecs_;}
+ virtual bool FindCodec(const Codec&);
+
+ private:
+ std::vector<Codec, std::allocator<Codec> > codecs_;
+};
+
+} // namespace cricket
+
+#endif // TALK_SESSION_PHONE_LINPHONEMEDIAENGINE_H_
diff --git a/Plugins/jingle/libjingle/talk/session/phone/mediachannel.h b/Plugins/jingle/libjingle/talk/session/phone/mediachannel.h
new file mode 100644
index 0000000..65463a5
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/session/phone/mediachannel.h
@@ -0,0 +1,78 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TALK_SESSION_PHONE_MEDIACHANNEL_H_
+#define TALK_SESSION_PHONE_MEDIACHANNEL_H_
+
+#include "talk/base/basictypes.h"
+#include "talk/base/sigslot.h"
+#include "talk/session/phone/codec.h"
+
+namespace cricket {
+
+class VoiceChannel;
+
+struct MediaInfo {
+ unsigned short fraction_lost;
+ unsigned long cum_lost;
+ unsigned long ext_max;
+ unsigned long jitter;
+ int RTT;
+ int bytesSent;
+ int packetsSent;
+ int bytesReceived;
+ int packetsReceived;
+};
+
+class MediaChannel : public sigslot::has_slots<> {
+ public:
+ class NetworkInterface {
+ public:
+ virtual void SendPacket(const void *data, size_t len) = 0;
+ };
+ MediaChannel() {network_interface_ = NULL;}
+ virtual ~MediaChannel() {};
+ void SetInterface(NetworkInterface *iface) {network_interface_ = iface;}
+ virtual void SetCodecs(const std::vector<Codec> &codecs) = 0;
+ virtual void OnPacketReceived(const void *data, int len) = 0;
+ virtual void SetPlayout(bool playout) = 0;
+ virtual void SetSend(bool send) = 0;
+ virtual int GetOutputLevel() = 0;
+
+ virtual void StartMediaMonitor(VoiceChannel * voice_channel, uint32 cms) = 0;
+ virtual void StopMediaMonitor() = 0;
+ sigslot::signal2<MediaChannel *, const MediaInfo &> SignalMediaMonitor;
+
+
+ NetworkInterface *network_interface() {return network_interface_;}
+ protected:
+ NetworkInterface *network_interface_;
+};
+
+}; // namespace cricket
+
+#endif // TALK_SESSION_PHONE_MEDIACHANNEL_H_
diff --git a/Plugins/jingle/libjingle/talk/session/phone/mediaengine.h b/Plugins/jingle/libjingle/talk/session/phone/mediaengine.h
new file mode 100644
index 0000000..e8081db
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/session/phone/mediaengine.h
@@ -0,0 +1,70 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+// MediaEngine is an abstraction of a media engine which can be subclassed
+// to support different media componentry backends.
+
+#ifndef TALK_SESSION_PHONE_MEDIAENGINE_H_
+#define TALK_SESSION_PHONE_MEDIAENGINE_H_
+
+#include <string>
+#include <vector>
+
+#include "talk/session/phone/codec.h"
+#include "talk/session/phone/mediachannel.h"
+
+namespace cricket {
+
+class MediaEngine {
+ public:
+
+ MediaEngine() {}
+
+ // Bitmask flags for options that may be supported by the media engine implementation
+ enum MediaEngineOptions {
+ AUTO_GAIN_CONTROL = 1 << 1,
+ };
+
+ // Initialize
+ virtual bool Init() = 0;
+ virtual void Terminate() = 0;
+ virtual MediaChannel *CreateChannel() = 0;
+
+ virtual int SetAudioOptions(int options) = 0;
+ virtual int SetSoundDevices(int wave_in_device, int wave_out_device) = 0;
+ virtual int GetInputLevel() = 0;
+
+ virtual std::vector<Codec> codecs() = 0;
+
+ virtual bool FindCodec(const Codec &codec) = 0;
+
+ int GetCodecPreference (Codec codec);
+};
+
+} // namespace cricket
+
+#endif // TALK_SESSION_PHONE_MEDIAENGINE_H_
diff --git a/Plugins/jingle/libjingle/talk/session/phone/phonesessionclient.cc b/Plugins/jingle/libjingle/talk/session/phone/phonesessionclient.cc
new file mode 100644
index 0000000..60daf18
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/session/phone/phonesessionclient.cc
@@ -0,0 +1,278 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "talk/base/logging.h"
+#include "talk/session/phone/phonesessionclient.h"
+#include "talk/xmpp/constants.h"
+#include "talk/xmllite/qname.h"
+namespace {
+
+const std::string NS_PHONE("http://www.google.com/session/phone");
+const std::string NS_EMPTY("");
+
+const buzz::QName QN_PHONE_DESCRIPTION(true, NS_PHONE, "description");
+const buzz::QName QN_PHONE_PAYLOADTYPE(true, NS_PHONE, "payload-type");
+const buzz::QName QN_PHONE_PAYLOADTYPE_ID(true, NS_EMPTY, "id");
+const buzz::QName QN_PHONE_PAYLOADTYPE_NAME(true, NS_EMPTY, "name");
+const buzz::QName QN_PHONE_PAYLOADTYPE_RATE(true, NS_EMPTY, "clockrate");
+const buzz::QName QN_PHONE_PAYLOADTYPE_BITRATE(true, NS_EMPTY, "bitrate");
+const buzz::QName QN_PHONE_PAYLOADTYPE_CHANNELS(true, NS_EMPTY, "channels");
+}
+
+namespace cricket {
+
+PhoneSessionClient::PhoneSessionClient(
+ const buzz::Jid& jid, SessionManager *manager)
+ : jid_(jid), session_manager_(manager) {
+ // No call to start, and certainly no call with focus
+ focus_call_ = NULL;
+
+ // Start up the channel manager on a worker thread
+ channel_manager_ = new ChannelManager(session_manager_->worker_thread());
+
+ // Register ourselves as the handler of phone sessions.
+ session_manager_->AddClient(NS_PHONE, this);
+}
+
+
+PhoneSessionClient::~PhoneSessionClient() {
+ // Destroy all calls
+ std::map<uint32, Call *>::iterator it;
+ while (calls_.begin() != calls_.end()) {
+ std::map<uint32, Call *>::iterator it = calls_.begin();
+ DestroyCall((*it).second);
+ }
+
+ // Delete channel manager. This will wait for the channels to exit
+ delete channel_manager_;
+
+ // Remove ourselves from the client map.
+ session_manager_->RemoveClient(NS_PHONE);
+}
+
+PhoneSessionDescription* PhoneSessionClient::CreateOfferSessionDescription() {
+ PhoneSessionDescription* session_desc = new PhoneSessionDescription();
+
+
+ MediaEngine *me = channel_manager_->media_engine();
+ std::vector<Codec> codecs = me->codecs();
+ std::vector<Codec>::iterator i;
+ for (i = codecs.begin(); i < codecs.end(); i++)
+ session_desc->AddCodec(*i);
+
+ session_desc->Sort();
+ return session_desc;
+}
+
+PhoneSessionDescription* PhoneSessionClient::CreateAcceptSessionDescription(const SessionDescription* offer) {
+ const PhoneSessionDescription* offer_desc =
+ static_cast<const PhoneSessionDescription*>(offer);
+ PhoneSessionDescription* accept_desc = new PhoneSessionDescription();
+ std::vector<Codec> codecs = channel_manager_->media_engine()->codecs();
+ std::vector<Codec>::iterator iter;
+ for (unsigned int i = 0; i < offer_desc->codecs().size(); ++i) {
+ if (channel_manager_->media_engine()->FindCodec(offer_desc->codecs()[i]))
+ accept_desc->AddCodec(offer_desc->codecs()[i]);
+ }
+
+ accept_desc->Sort();
+ return accept_desc;
+}
+
+const SessionDescription *PhoneSessionClient::CreateSessionDescription(const buzz::XmlElement *element) {
+ PhoneSessionDescription* desc = new PhoneSessionDescription();
+
+ const buzz::XmlElement* payload_type = element->FirstNamed(QN_PHONE_PAYLOADTYPE);
+ int num_payload_types = 0;
+
+ while (payload_type) {
+ if (payload_type->HasAttr(QN_PHONE_PAYLOADTYPE_ID)) {
+ int id = atoi(payload_type->Attr(QN_PHONE_PAYLOADTYPE_ID).c_str());
+
+ std::string name = "";
+ if (payload_type->HasAttr(QN_PHONE_PAYLOADTYPE_NAME))
+ name = payload_type->Attr(QN_PHONE_PAYLOADTYPE_NAME);
+
+ int clockrate = 0;
+ if (payload_type->HasAttr(QN_PHONE_PAYLOADTYPE_RATE))
+ clockrate = atoi(payload_type->Attr(QN_PHONE_PAYLOADTYPE_RATE).c_str());
+
+ int bitrate = 0;
+ if (payload_type->HasAttr(QN_PHONE_PAYLOADTYPE_BITRATE))
+ bitrate = atoi(payload_type->Attr(QN_PHONE_PAYLOADTYPE_BITRATE).c_str());
+
+ int channels = 1;
+ if (payload_type->HasAttr(QN_PHONE_PAYLOADTYPE_CHANNELS))
+ channels = atoi(payload_type->Attr(QN_PHONE_PAYLOADTYPE_CHANNELS).c_str());
+
+ desc->AddCodec(Codec(id, name, clockrate, bitrate, channels, 0));
+ }
+
+ payload_type = payload_type->NextNamed(QN_PHONE_PAYLOADTYPE);
+ num_payload_types += 1;
+ }
+
+ // For backward compatability, we can assume the other client is (an old
+ // version of Talk) if it has no payload types at all.
+ if (num_payload_types == 0) {
+ desc->AddCodec(Codec(103, "ISAC", 16000, -1, 1, 1));
+ desc->AddCodec(Codec(0, "PCMU", 8000, 64000, 1, 0));
+ }
+
+ return desc;
+}
+
+buzz::XmlElement *PhoneSessionClient::TranslateSessionDescription(const SessionDescription *_session_desc) {
+ const PhoneSessionDescription* session_desc =
+ static_cast<const PhoneSessionDescription*>(_session_desc);
+ buzz::XmlElement* description = new buzz::XmlElement(QN_PHONE_DESCRIPTION, true);
+
+
+ for (size_t i = 0; i < session_desc->codecs().size(); ++i) {
+ buzz::XmlElement* payload_type = new buzz::XmlElement(QN_PHONE_PAYLOADTYPE, true);
+
+ char buf[32];
+ sprintf(buf, "%d", session_desc->codecs()[i].id);
+ payload_type->AddAttr(QN_PHONE_PAYLOADTYPE_ID, buf);
+
+ payload_type->AddAttr(QN_PHONE_PAYLOADTYPE_NAME,
+ session_desc->codecs()[i].name.c_str());
+
+ if (session_desc->codecs()[i].clockrate > 0) {
+ sprintf(buf, "%d", session_desc->codecs()[i].clockrate);
+ payload_type->AddAttr(QN_PHONE_PAYLOADTYPE_RATE, buf);
+ }
+
+ if (session_desc->codecs()[i].channels > 1) {
+ sprintf(buf, "%d", session_desc->codecs()[i].channels);
+ payload_type->AddAttr(QN_PHONE_PAYLOADTYPE_CHANNELS, buf);
+ }
+
+ if (session_desc->codecs()[i].bitrate > 0) {
+ sprintf(buf, "%d", session_desc->codecs()[i].bitrate);
+ payload_type->AddAttr(QN_PHONE_PAYLOADTYPE_BITRATE, buf);
+ }
+
+ description->AddElement(payload_type);
+ }
+
+ return description;
+}
+
+Call *PhoneSessionClient::CreateCall() {
+ Call *call = new Call(this);
+ calls_[call->id()] = call;
+ SignalCallCreate(call);
+ return call;
+}
+
+void PhoneSessionClient::OnSessionCreate(Session *session,
+ bool received_initiate) {
+ if (received_initiate) {
+ session->SignalState.connect(this, &PhoneSessionClient::OnSessionState);
+
+ Call *call = CreateCall();
+ session_map_[session->id()] = call;
+ call->AddSession(session);
+ }
+}
+
+void PhoneSessionClient::OnSessionState(Session *session,
+ Session::State state) {
+ if (state == Session::STATE_RECEIVEDINITIATE) {
+ // If our accept would have no codecs, then we must reject this call.
+ PhoneSessionDescription* accept_desc =
+ CreateAcceptSessionDescription(session->remote_description());
+ if (accept_desc->codecs().size() == 0) {
+ // TODO: include an error description with the rejection.
+ session->Reject();
+ }
+ delete accept_desc;
+ }
+}
+
+void PhoneSessionClient::DestroyCall(Call *call) {
+ // Change focus away, signal destruction
+
+ if (call == focus_call_)
+ SetFocus(NULL);
+ SignalCallDestroy(call);
+
+ // Remove it from calls_ map and delete
+
+ std::map<uint32, Call *>::iterator it = calls_.find(call->id());
+ if (it != calls_.end())
+ calls_.erase(it);
+
+ delete call;
+}
+
+void PhoneSessionClient::OnSessionDestroy(Session *session) {
+ // Find the call this session is in, remove it
+
+ std::map<SessionID, Call *>::iterator it = session_map_.find(session->id());
+ assert(it != session_map_.end());
+ if (it != session_map_.end()) {
+ Call *call = (*it).second;
+ session_map_.erase(it);
+ call->RemoveSession(session);
+ }
+}
+
+Call *PhoneSessionClient::GetFocus() {
+ return focus_call_;
+}
+
+void PhoneSessionClient::SetFocus(Call *call) {
+ Call *old_focus_call = focus_call_;
+ if (focus_call_ != call) {
+ if (focus_call_ != NULL)
+ focus_call_->EnableChannels(false);
+ focus_call_ = call;
+ if (focus_call_ != NULL)
+ focus_call_->EnableChannels(true);
+ SignalFocus(focus_call_, old_focus_call);
+ }
+}
+
+void PhoneSessionClient::JoinCalls(Call *call_to_join, Call *call) {
+ // Move all sessions from call to call_to_join, delete call.
+ // If call_to_join has focus, added sessions should have enabled channels.
+
+ if (focus_call_ == call)
+ SetFocus(NULL);
+ call_to_join->Join(call, focus_call_ == call_to_join);
+ DestroyCall(call);
+}
+
+Session *PhoneSessionClient::CreateSession(Call *call) {
+ Session *session = session_manager_->CreateSession(jid().Str(), NS_PHONE);
+ session_map_[session->id()] = call;
+ return session;
+}
+
+}
diff --git a/Plugins/jingle/libjingle/talk/session/phone/phonesessionclient.h b/Plugins/jingle/libjingle/talk/session/phone/phonesessionclient.h
new file mode 100644
index 0000000..35083c5
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/session/phone/phonesessionclient.h
@@ -0,0 +1,132 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _PHONESESSIONCLIENT_H_
+#define _PHONESESSIONCLIENT_H_
+
+#include "talk/session/phone/call.h"
+#include "talk/session/phone/channelmanager.h"
+#include "talk/base/sigslot.h"
+#include "talk/base/messagequeue.h"
+#include "talk/base/thread.h"
+#include "talk/p2p/base/sessionmanager.h"
+#include "talk/p2p/base/session.h"
+#include "talk/p2p/base/sessionclient.h"
+#include "talk/p2p/base/sessiondescription.h"
+#include <map>
+
+namespace cricket {
+
+class Call;
+class PhoneSessionDescription;
+
+class PhoneSessionClient: public SessionClient, public sigslot::has_slots<> {
+public:
+ PhoneSessionClient(const buzz::Jid& jid, SessionManager *manager);
+ ~PhoneSessionClient();
+
+ const buzz::Jid &jid() const { return jid_; }
+ SessionManager* session_manager() const { return session_manager_; }
+ ChannelManager* channel_manager() const { return channel_manager_; }
+
+ Call *CreateCall();
+ void DestroyCall(Call *call);
+
+ Call *GetFocus();
+ void SetFocus(Call *call);
+
+ Call *GetCall(uint32 id) {
+ std::map<uint32, Call *>::iterator it = calls_.find(id);
+ if (it != calls_.end())
+ return it->second;
+ return NULL;
+ }
+
+ void JoinCalls(Call *call_to_join, Call *call);
+
+ void SetAudioOptions(bool auto_gain_control, int wave_in_device,
+ int wave_out_device) {
+ if (channel_manager_)
+ channel_manager_->SetAudioOptions(auto_gain_control, wave_in_device,
+ wave_out_device);
+ }
+
+ sigslot::signal2<Call *, Call *> SignalFocus;
+ sigslot::signal1<Call *> SignalCallCreate;
+ sigslot::signal1<Call *> SignalCallDestroy;
+
+ PhoneSessionDescription* CreateOfferSessionDescription();
+
+ PhoneSessionDescription* CreateAcceptSessionDescription(const SessionDescription* offer);
+
+ // Returns our preference for the given codec.
+ static int GetMediaCodecPreference(Codec codec);
+
+private:
+ void OnSessionCreate(Session *session, bool received_initiate);
+ void OnSessionState(Session *session, Session::State state);
+ void OnSessionDestroy(Session *session);
+ const SessionDescription *CreateSessionDescription(const buzz::XmlElement *element);
+ buzz::XmlElement *TranslateSessionDescription(const SessionDescription *description);
+ Session *CreateSession(Call *call);
+
+ buzz::Jid jid_;
+ SessionManager* session_manager_;
+ Call *focus_call_;
+ ChannelManager *channel_manager_;
+ std::map<uint32, Call *> calls_;
+ std::map<SessionID, Call *> session_map_;
+
+ friend class Call;
+};
+
+class PhoneSessionDescription: public SessionDescription {
+public:
+ // Returns the list of codecs sorted by our preference.
+ const std::vector<Codec>& codecs() const { return codecs_; }
+ // Adds another codec to the list.
+ void AddCodec(const Codec& codec) { codecs_.push_back(codec); }
+ // Sorts the list of codecs by preference.
+ void Sort() { std::sort(codecs_.begin(), codecs_.end(), PreferenceSort()); }
+
+private:
+ std::vector<Codec> codecs_;
+ struct PreferenceSort {
+ bool operator()(Codec a, Codec b) {
+ return a.preference > b.preference;
+ }
+ };
+
+#if defined(FEATURE_ENABLE_VOICEMAIL)
+ std::string lang_;
+#endif
+
+};
+
+}
+
+#endif // _PHONESESSIONCLIENT_H_
diff --git a/Plugins/jingle/libjingle/talk/session/phone/voicechannel.cc b/Plugins/jingle/libjingle/talk/session/phone/voicechannel.cc
new file mode 100644
index 0000000..58b8760
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/session/phone/voicechannel.cc
@@ -0,0 +1,330 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "talk/session/phone/voicechannel.h"
+#include "talk/base/common.h"
+#include "talk/base/logging.h"
+#include "talk/p2p/base/transportchannel.h"
+#include "talk/session/phone/channelmanager.h"
+#include "talk/session/phone/phonesessionclient.h"
+#include <cassert>
+#undef SetPort
+
+namespace cricket {
+
+
+VoiceChannel::VoiceChannel(ChannelManager *manager, Session *session,
+ MediaChannel *channel) {
+ channel_manager_ = manager;
+ assert(channel_manager_->worker_thread() == talk_base::Thread::Current());
+ media_channel_ = channel;
+ session_ = session;
+ socket_monitor_ = NULL;
+ audio_monitor_ = NULL;
+ transport_channel_ = session_->CreateChannel("rtp");
+ transport_channel_->SignalWritableState.connect(
+ this, &VoiceChannel::OnWritableState);
+ transport_channel_->SignalReadPacket.connect(
+ this, &VoiceChannel::OnChannelRead);
+ media_channel_->SetInterface(this);
+ enabled_ = false;
+ paused_ = false;
+ writable_ = false;
+ muted_ = false;
+ LOG(INFO) << "Created voice channel";
+
+ session->SignalState.connect(this, &VoiceChannel::OnSessionState);
+ OnSessionState(session, session->state());
+}
+
+VoiceChannel::~VoiceChannel() {
+ assert(channel_manager_->worker_thread() == talk_base::Thread::Current());
+ enabled_ = false;
+ ChangeState();
+ delete socket_monitor_;
+ delete audio_monitor_;
+ talk_base::Thread::Current()->Clear(this);
+ if (transport_channel_ != NULL)
+ session_->DestroyChannel(transport_channel_);
+ LOG(INFO) << "Destroyed voice channel";
+}
+
+void VoiceChannel::OnMessage(talk_base::Message *pmsg) {
+ switch (pmsg->message_id) {
+ case MSG_ENABLE:
+ EnableMedia_w();
+ break;
+
+ case MSG_DISABLE:
+ DisableMedia_w();
+ break;
+
+ case MSG_MUTE:
+ MuteMedia_w();
+ break;
+
+ case MSG_UNMUTE:
+ UnmuteMedia_w();
+ break;
+
+ case MSG_SETSENDCODEC:
+ SetSendCodec_w();
+ break;
+ }
+}
+
+void VoiceChannel::Enable(bool enable) {
+ // Can be called from thread other than worker thread
+ channel_manager_->worker_thread()->Post(this,
+ enable ? MSG_ENABLE : MSG_DISABLE);
+}
+
+void VoiceChannel::Mute(bool mute) {
+ // Can be called from thread other than worker thread
+ channel_manager_->worker_thread()->Post(this, mute ? MSG_MUTE : MSG_UNMUTE);
+}
+
+
+MediaChannel * VoiceChannel::channel() {
+ return media_channel_;
+}
+
+void VoiceChannel::OnSessionState(Session* session, Session::State state) {
+ if ((state == Session::STATE_RECEIVEDACCEPT) ||
+ (state == Session::STATE_RECEIVEDINITIATE)) {
+ channel_manager_->worker_thread()->Post(this, MSG_SETSENDCODEC);
+ }
+}
+
+void VoiceChannel::SetSendCodec_w() {
+ assert(channel_manager_->worker_thread() == talk_base::Thread::Current());
+
+ const PhoneSessionDescription* desc =
+ static_cast<const PhoneSessionDescription*>(
+ session()->remote_description());
+
+ media_channel_->SetCodecs(desc->codecs());
+}
+
+void VoiceChannel::OnWritableState(TransportChannel* channel) {
+ ASSERT(channel == transport_channel_);
+ if (transport_channel_->writable()) {
+ ChannelWritable_w();
+ } else {
+ ChannelNotWritable_w();
+ }
+}
+
+void VoiceChannel::OnChannelRead(TransportChannel* channel,
+ const char* data,
+ size_t len) {
+ ASSERT(channel_manager_->worker_thread() == talk_base::Thread::Current());
+ // OnChannelRead gets called from P2PSocket; now pass data to MediaEngine
+ media_channel_->OnPacketReceived(data, (int)len);
+}
+
+void VoiceChannel::SendPacket(const void *data, size_t len) {
+ // SendPacket gets called from MediaEngine; send to socket
+ // MediaEngine will call us on a random thread. The Send operation on the
+ // socket is special in that it can handle this.
+ transport_channel_->SendPacket(static_cast<const char *>(data), len);
+}
+
+void VoiceChannel::ChangeState() {
+ if (paused_ || !enabled_ || !writable_) {
+ media_channel_->SetPlayout(false);
+ media_channel_->SetSend(false);
+ } else {
+ if (muted_) {
+ media_channel_->SetSend(false);
+ media_channel_->SetPlayout(true);
+ } else {
+ media_channel_->SetSend(true);
+ media_channel_->SetPlayout(true);
+ }
+ }
+}
+
+void VoiceChannel::PauseMedia_w() {
+ ASSERT(channel_manager_->worker_thread() == talk_base::Thread::Current());
+ ASSERT(!paused_);
+
+ LOG(INFO) << "Voice channel paused";
+ paused_ = true;
+ ChangeState();
+}
+
+void VoiceChannel::UnpauseMedia_w() {
+ ASSERT(channel_manager_->worker_thread() == talk_base::Thread::Current());
+ ASSERT(paused_);
+
+ LOG(INFO) << "Voice channel unpaused";
+ paused_ = false;
+ ChangeState();
+}
+
+void VoiceChannel::EnableMedia_w() {
+ ASSERT(channel_manager_->worker_thread() == talk_base::Thread::Current());
+ if (enabled_)
+ return;
+
+ LOG(INFO) << "Voice channel enabled";
+ enabled_ = true;
+ ChangeState();
+}
+
+void VoiceChannel::DisableMedia_w() {
+ ASSERT(channel_manager_->worker_thread() == talk_base::Thread::Current());
+ if (!enabled_)
+ return;
+
+ LOG(INFO) << "Voice channel disabled";
+ enabled_ = false;
+ ChangeState();
+}
+
+void VoiceChannel::MuteMedia_w() {
+ ASSERT(channel_manager_->worker_thread() == talk_base::Thread::Current());
+ if (muted_)
+ return;
+
+ LOG(INFO) << "Voice channel muted";
+ muted_ = true;
+ ChangeState();
+}
+
+void VoiceChannel::UnmuteMedia_w() {
+ ASSERT(channel_manager_->worker_thread() == talk_base::Thread::Current());
+ if (!muted_)
+ return;
+
+ LOG(INFO) << "Voice channel unmuted";
+ muted_ = false;
+ ChangeState();
+}
+
+void VoiceChannel::ChannelWritable_w() {
+ ASSERT(channel_manager_->worker_thread() == talk_base::Thread::Current());
+ if (writable_)
+ return;
+
+ LOG(INFO) << "Voice channel socket writable";
+ writable_ = true;
+ ChangeState();
+}
+
+void VoiceChannel::ChannelNotWritable_w() {
+ ASSERT(channel_manager_->worker_thread() == talk_base::Thread::Current());
+ if (!writable_)
+ return;
+
+ LOG(INFO) << "Voice channel socket not writable";
+ writable_ = false;
+ ChangeState();
+}
+
+
+void VoiceChannel::StartConnectionMonitor(int cms) {
+ delete socket_monitor_;
+ socket_monitor_ =
+ new SocketMonitor(session_, transport_channel_,
+ talk_base::Thread::Current());
+ socket_monitor_->SignalUpdate.connect(
+ this, &VoiceChannel::OnConnectionMonitorUpdate);
+ socket_monitor_->Start(cms);
+}
+
+void VoiceChannel::StopConnectionMonitor() {
+ if (socket_monitor_ != NULL) {
+ socket_monitor_->Stop();
+ socket_monitor_->SignalUpdate.disconnect(this);
+ delete socket_monitor_;
+ socket_monitor_ = NULL;
+ }
+}
+
+void VoiceChannel::OnConnectionMonitorUpdate(
+ SocketMonitor *monitor, const std::vector<ConnectionInfo> &infos) {
+ SignalConnectionMonitor(this, infos);
+}
+
+void VoiceChannel::StartAudioMonitor(int cms) {
+ delete audio_monitor_;
+ audio_monitor_ = new AudioMonitor(this, talk_base::Thread::Current());
+ audio_monitor_
+ ->SignalUpdate.connect(this, &VoiceChannel::OnAudioMonitorUpdate);
+ audio_monitor_->Start(cms);
+}
+
+void VoiceChannel::StopAudioMonitor() {
+ if (audio_monitor_ != NULL) {
+ audio_monitor_ ->Stop();
+ audio_monitor_ ->SignalUpdate.disconnect(this);
+ delete audio_monitor_ ;
+ audio_monitor_ = NULL;
+ }
+}
+
+void VoiceChannel::OnAudioMonitorUpdate(AudioMonitor *monitor,
+ const AudioInfo& info) {
+ SignalAudioMonitor(this, info);
+}
+
+void VoiceChannel::StartMediaMonitor(int cms) {
+ media_channel_
+ ->SignalMediaMonitor.connect(this, &VoiceChannel::OnMediaMonitorUpdate);
+ media_channel_->StartMediaMonitor(this, cms);
+}
+
+void VoiceChannel::StopMediaMonitor() {
+ media_channel_->SignalMediaMonitor.disconnect(this);
+ media_channel_->StopMediaMonitor();
+}
+
+void VoiceChannel::OnMediaMonitorUpdate(
+ MediaChannel *media_channel, const MediaInfo &info) {
+ ASSERT(media_channel == media_channel_);
+ SignalMediaMonitor(this, info);
+}
+
+Session *VoiceChannel::session() {
+ return session_;
+}
+
+int VoiceChannel::GetInputLevel_w() {
+ return channel_manager_->media_engine()->GetInputLevel();
+}
+
+int VoiceChannel::GetOutputLevel_w() {
+ return media_channel_->GetOutputLevel();
+}
+
+talk_base::Thread* VoiceChannel::worker_thread() {
+ return channel_manager_->worker_thread();
+}
+
+}
diff --git a/Plugins/jingle/libjingle/talk/session/phone/voicechannel.h b/Plugins/jingle/libjingle/talk/session/phone/voicechannel.h
new file mode 100644
index 0000000..3d0be12
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/session/phone/voicechannel.h
@@ -0,0 +1,137 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _VOICECHANNEL_H_
+#define _VOICECHANNEL_H_
+
+#include "talk/base/asyncudpsocket.h"
+#include "talk/base/network.h"
+#include "talk/base/sigslot.h"
+#include "talk/p2p/client/socketmonitor.h"
+#include "talk/p2p/base/session.h"
+#include "talk/session/phone/audiomonitor.h"
+#include "talk/session/phone/mediaengine.h"
+#include "talk/session/phone/mediachannel.h"
+
+namespace cricket {
+
+const uint32 MSG_ENABLE = 1;
+const uint32 MSG_DISABLE = 2;
+const uint32 MSG_MUTE = 3;
+const uint32 MSG_UNMUTE = 4;
+const uint32 MSG_SETSENDCODEC = 5;
+
+
+class ChannelManager;
+
+class VoiceChannel
+ : public talk_base::MessageHandler, public sigslot::has_slots<>,
+ public MediaChannel::NetworkInterface {
+ public:
+ VoiceChannel(ChannelManager *manager, Session *session,
+ MediaChannel *channel);
+ ~VoiceChannel();
+
+ void Enable(bool enable);
+ void Mute(bool mute);
+
+
+ MediaChannel *channel();
+ Session *session();
+
+ // Monitoring
+
+ void StartConnectionMonitor(int cms);
+ void StopConnectionMonitor();
+ sigslot::signal2<VoiceChannel *, const std::vector<ConnectionInfo> &>
+ SignalConnectionMonitor;
+
+ void StartAudioMonitor(int cms);
+ void StopAudioMonitor();
+ sigslot::signal2<VoiceChannel *, const AudioInfo&> SignalAudioMonitor;
+ talk_base::Thread* worker_thread();
+
+ void StartMediaMonitor(int cms);
+ void StopMediaMonitor();
+ sigslot::signal2<VoiceChannel *, const MediaInfo&> SignalMediaMonitor;
+
+ // Pausing so that the ChannelManager can change the audio devices. These
+ // should only be called from the worker thread
+ void PauseMedia_w();
+ void UnpauseMedia_w();
+
+ int GetInputLevel_w();
+ int GetOutputLevel_w();
+
+ // MediaEngine calls this
+ virtual void SendPacket(const void *data, size_t len);
+
+private:
+ void ChangeState();
+ void EnableMedia_w();
+ void DisableMedia_w();
+ void MuteMedia_w();
+ void UnmuteMedia_w();
+ void ChannelWritable_w();
+ void ChannelNotWritable_w();
+
+
+ void OnConnectionMonitorUpdate(SocketMonitor *monitor,
+ const std::vector<ConnectionInfo> &infos);
+ void OnAudioMonitorUpdate(AudioMonitor *monitor, const AudioInfo& info);
+ void OnMediaMonitorUpdate(MediaChannel *media_channel,
+ const MediaInfo& info);
+
+ // From MessageHandler
+
+ void OnMessage(talk_base::Message *pmsg);
+
+ // Setting the send codec based on the remote description.
+ void OnSessionState(Session* session, Session::State state);
+ void SetSendCodec_w();
+
+ // From TransportChannel
+
+ void OnWritableState(TransportChannel* channel);
+ void OnChannelRead(TransportChannel* channel, const char *data, size_t len);
+
+
+ bool enabled_;
+ bool paused_;
+ bool writable_;
+ bool muted_;
+ MediaChannel *media_channel_;
+ Session *session_;
+ TransportChannel *transport_channel_;
+ ChannelManager *channel_manager_;
+ SocketMonitor *socket_monitor_;
+ AudioMonitor *audio_monitor_;
+};
+
+}
+
+#endif // _VOICECHANNEL_H_
diff --git a/Plugins/jingle/libjingle/talk/session/tunnel/Makefile.am b/Plugins/jingle/libjingle/talk/session/tunnel/Makefile.am
new file mode 100644
index 0000000..3a8d244
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/session/tunnel/Makefile.am
@@ -0,0 +1,7 @@
+libcricketsessiontunnel_la_SOURCES = tunnelsessionclient.cc \
+ pseudotcpchannel.cc
+noinst_HEADERS = tunnelsessionclient.h \
+ pseudotcpchannel.h
+noinst_LTLIBRARIES = libcricketsessiontunnel.la
+
+AM_CXXFLAGS = -DPOSIX
diff --git a/Plugins/jingle/libjingle/talk/session/tunnel/Makefile.in b/Plugins/jingle/libjingle/talk/session/tunnel/Makefile.in
new file mode 100644
index 0000000..4df78cc
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/session/tunnel/Makefile.in
@@ -0,0 +1,452 @@
+# Makefile.in generated by automake 1.9.6 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005 Free Software Foundation, Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+top_builddir = ../../..
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+INSTALL = @INSTALL@
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+target_triplet = @target@
+subdir = talk/session/tunnel
+DIST_COMMON = $(noinst_HEADERS) $(srcdir)/Makefile.am \
+ $(srcdir)/Makefile.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/talk/pkg.m4 \
+ $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+LTLIBRARIES = $(noinst_LTLIBRARIES)
+libcricketsessiontunnel_la_LIBADD =
+am_libcricketsessiontunnel_la_OBJECTS = tunnelsessionclient.lo \
+ pseudotcpchannel.lo
+libcricketsessiontunnel_la_OBJECTS = \
+ $(am_libcricketsessiontunnel_la_OBJECTS)
+DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+ $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS)
+LTCXXCOMPILE = $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CXXFLAGS) $(CXXFLAGS)
+CXXLD = $(CXX)
+CXXLINK = $(LIBTOOL) --tag=CXX --mode=link $(CXXLD) $(AM_CXXFLAGS) \
+ $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
+SOURCES = $(libcricketsessiontunnel_la_SOURCES)
+DIST_SOURCES = $(libcricketsessiontunnel_la_SOURCES)
+HEADERS = $(noinst_HEADERS)
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+ALSA_LIBS = @ALSA_LIBS@
+AMDEP_FALSE = @AMDEP_FALSE@
+AMDEP_TRUE = @AMDEP_TRUE@
+AMTAR = @AMTAR@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+ECHO = @ECHO@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+EXPAT_LIBS = @EXPAT_LIBS@
+F77 = @F77@
+FFLAGS = @FFLAGS@
+GIPS_FALSE = @GIPS_FALSE@
+GIPS_TRUE = @GIPS_TRUE@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_LIBS = @GLIB_LIBS@
+ILBC_CFLAGS = @ILBC_CFLAGS@
+ILBC_LIBS = @ILBC_LIBS@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LDFLAGS = @LDFLAGS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+MAKEINFO = @MAKEINFO@
+MEDIA_LIBS = @MEDIA_LIBS@
+OBJEXT = @OBJEXT@
+ORTP_CFLAGS = @ORTP_CFLAGS@
+ORTP_LIBS = @ORTP_LIBS@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PHONE_FALSE = @PHONE_FALSE@
+PHONE_TRUE = @PHONE_TRUE@
+PKG_CONFIG = @PKG_CONFIG@
+RANLIB = @RANLIB@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SPEEX_CFLAGS = @SPEEX_CFLAGS@
+SPEEX_LIBS = @SPEEX_LIBS@
+STRIP = @STRIP@
+VERSION = @VERSION@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_F77 = @ac_ct_F77@
+ac_ct_RANLIB = @ac_ct_RANLIB@
+ac_ct_STRIP = @ac_ct_STRIP@
+am__fastdepCC_FALSE = @am__fastdepCC_FALSE@
+am__fastdepCC_TRUE = @am__fastdepCC_TRUE@
+am__fastdepCXX_FALSE = @am__fastdepCXX_FALSE@
+am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+datadir = @datadir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+sysconfdir = @sysconfdir@
+target = @target@
+target_alias = @target_alias@
+target_cpu = @target_cpu@
+target_os = @target_os@
+target_vendor = @target_vendor@
+libcricketsessiontunnel_la_SOURCES = tunnelsessionclient.cc \
+ pseudotcpchannel.cc
+
+noinst_HEADERS = tunnelsessionclient.h \
+ pseudotcpchannel.h
+
+noinst_LTLIBRARIES = libcricketsessiontunnel.la
+AM_CXXFLAGS = -DPOSIX
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .cc .lo .o .obj
+$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \
+ && exit 0; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu talk/session/tunnel/Makefile'; \
+ cd $(top_srcdir) && \
+ $(AUTOMAKE) --gnu talk/session/tunnel/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+clean-noinstLTLIBRARIES:
+ -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES)
+ @list='$(noinst_LTLIBRARIES)'; for p in $$list; do \
+ dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
+ test "$$dir" != "$$p" || dir=.; \
+ echo "rm -f \"$${dir}/so_locations\""; \
+ rm -f "$${dir}/so_locations"; \
+ done
+libcricketsessiontunnel.la: $(libcricketsessiontunnel_la_OBJECTS) $(libcricketsessiontunnel_la_DEPENDENCIES)
+ $(CXXLINK) $(libcricketsessiontunnel_la_LDFLAGS) $(libcricketsessiontunnel_la_OBJECTS) $(libcricketsessiontunnel_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pseudotcpchannel.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tunnelsessionclient.Plo@am__quote@
+
+.cc.o:
+@am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \
+@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ $<
+
+.cc.obj:
+@am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \
+@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.cc.lo:
+@am__fastdepCXX_TRUE@ if $(LTCXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \
+@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Plo"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(LTCXXCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+distclean-libtool:
+ -rm -f libtool
+uninstall-info-am:
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ mkid -fID $$unique
+tags: TAGS
+
+TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ tags=; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$tags $$unique; \
+ fi
+ctags: CTAGS
+CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ tags=; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ test -z "$(CTAGS_ARGS)$$tags$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$tags $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && cd $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) $$here
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \
+ list='$(DISTFILES)'; for file in $$list; do \
+ case $$file in \
+ $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \
+ $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \
+ esac; \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test "$$dir" != "$$file" && test "$$dir" != "."; then \
+ dir="/$$dir"; \
+ $(mkdir_p) "$(distdir)$$dir"; \
+ else \
+ dir=''; \
+ fi; \
+ if test -d $$d/$$file; then \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \
+ fi; \
+ cp -pR $$d/$$file $(distdir)$$dir || exit 1; \
+ else \
+ test -f $(distdir)/$$file \
+ || cp -p $$d/$$file $(distdir)/$$file \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(LTLIBRARIES) $(HEADERS)
+installdirs:
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ `test -z '$(STRIP)' || \
+ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-libtool distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-exec-am:
+
+install-info: install-info-am
+
+install-man:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-info-am
+
+.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \
+ clean-libtool clean-noinstLTLIBRARIES ctags distclean \
+ distclean-compile distclean-generic distclean-libtool \
+ distclean-tags distdir dvi dvi-am html html-am info info-am \
+ install install-am install-data install-data-am install-exec \
+ install-exec-am install-info install-info-am install-man \
+ install-strip installcheck installcheck-am installdirs \
+ maintainer-clean maintainer-clean-generic mostlyclean \
+ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \
+ pdf pdf-am ps ps-am tags uninstall uninstall-am \
+ uninstall-info-am
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/Plugins/jingle/libjingle/talk/session/tunnel/pseudotcpchannel.cc b/Plugins/jingle/libjingle/talk/session/tunnel/pseudotcpchannel.cc
new file mode 100644
index 0000000..54515c0
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/session/tunnel/pseudotcpchannel.cc
@@ -0,0 +1,554 @@
+/*
+ * libjingle
+ * Copyright 2004--2006, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "talk/base/basictypes.h"
+#include "talk/base/common.h"
+#include "talk/base/logging.h"
+#include "talk/base/stringutils.h"
+#include "talk/p2p/base/transportchannel.h"
+#include "pseudotcpchannel.h"
+
+using namespace talk_base;
+
+namespace cricket {
+
+extern const talk_base::ConstantLabel SESSION_STATES[];
+
+// MSG_WK_* - worker thread messages
+// MSG_ST_* - stream thread messages
+// MSG_SI_* - signal thread messages
+
+enum {
+ MSG_WK_CLOCK = 1,
+ MSG_WK_PURGE,
+ MSG_ST_EVENT,
+ MSG_SI_DESTROYCHANNEL,
+ MSG_SI_DESTROY,
+};
+
+struct EventData : public MessageData {
+ int event, error;
+ EventData(int ev, int err = 0) : event(ev), error(err) { }
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// PseudoTcpChannel::InternalStream
+///////////////////////////////////////////////////////////////////////////////
+
+class PseudoTcpChannel::InternalStream : public StreamInterface {
+public:
+ InternalStream(PseudoTcpChannel* parent);
+ virtual ~InternalStream();
+
+ virtual StreamState GetState() const;
+ virtual StreamResult Read(void* buffer, size_t buffer_len,
+ size_t* read, int* error);
+ virtual StreamResult Write(const void* data, size_t data_len,
+ size_t* written, int* error);
+ virtual void Close();
+
+ virtual bool GetSize(size_t* size) const { return false; }
+ virtual bool ReserveSize(size_t size) { return true; }
+ virtual bool Rewind() { return false; }
+
+private:
+ // parent_ is accessed and modified exclusively on the event thread, to
+ // avoid thread contention. This means that the PseudoTcpChannel cannot go
+ // away until after it receives a Close() from TunnelStream.
+ PseudoTcpChannel* parent_;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// PseudoTcpChannel
+// Member object lifetime summaries:
+// session_ - passed in constructor, cleared when channel_ goes away.
+// channel_ - created in Connect, destroyed when session_ or tcp_ goes away.
+// tcp_ - created in Connect, destroyed when channel_ goes away, or connection
+// closes.
+// worker_thread_ - created when channel_ is created, purged when channel_ is
+// destroyed.
+// stream_ - created in GetStream, destroyed by owner at arbitrary time.
+// this - created in constructor, destroyed when worker_thread_ and stream_
+// are both gone.
+///////////////////////////////////////////////////////////////////////////////
+
+//
+// Signal thread methods
+//
+
+PseudoTcpChannel::PseudoTcpChannel(Thread* stream_thread, Session* session)
+ : signal_thread_(session->session_manager()->signaling_thread()),
+ worker_thread_(NULL),
+ stream_thread_(stream_thread),
+ session_(session), channel_(NULL), tcp_(NULL), stream_(NULL),
+ stream_readable_(false), pending_read_event_(false),
+ ready_to_connect_(false) {
+ ASSERT(signal_thread_->IsCurrent());
+}
+
+PseudoTcpChannel::~PseudoTcpChannel() {
+ ASSERT(signal_thread_->IsCurrent());
+ ASSERT(worker_thread_ == NULL);
+ ASSERT(session_ == NULL);
+ ASSERT(channel_ == NULL);
+ ASSERT(stream_ == NULL);
+ ASSERT(tcp_ == NULL);
+}
+
+bool PseudoTcpChannel::Connect(const std::string& channel_name) {
+ ASSERT(signal_thread_->IsCurrent());
+ CritScope lock(&cs_);
+
+ if (channel_)
+ return false;
+
+ ASSERT(session_ != NULL);
+ worker_thread_ = session_->session_manager()->worker_thread();
+ channel_ = session_->CreateChannel(channel_name);
+ channel_name_ = channel_name;
+ channel_->SetOption(Socket::OPT_DONTFRAGMENT, 1);
+
+ channel_->SignalDestroyed.connect(this,
+ &PseudoTcpChannel::OnChannelDestroyed);
+ channel_->SignalWritableState.connect(this,
+ &PseudoTcpChannel::OnChannelWritableState);
+ channel_->SignalReadPacket.connect(this,
+ &PseudoTcpChannel::OnChannelRead);
+ channel_->SignalRouteChange.connect(this,
+ &PseudoTcpChannel::OnChannelConnectionChanged);
+
+ ASSERT(tcp_ == NULL);
+ tcp_ = new PseudoTcp(this, 0);
+ if (session_->initiator()) {
+ // Since we may try several protocols and network adapters that won't work,
+ // waiting until we get our first writable notification before initiating
+ // TCP negotiation.
+ ready_to_connect_ = true;
+ }
+
+ return true;
+}
+
+StreamInterface* PseudoTcpChannel::GetStream() {
+ ASSERT(signal_thread_->IsCurrent());
+ CritScope lock(&cs_);
+ ASSERT(NULL != session_);
+ if (!stream_)
+ stream_ = new PseudoTcpChannel::InternalStream(this);
+ //TODO("should we disallow creation of new stream at some point?");
+ return stream_;
+}
+
+void PseudoTcpChannel::OnChannelDestroyed(TransportChannel* channel) {
+ LOG_F(LS_INFO) << "(" << channel->name() << ")";
+ ASSERT(signal_thread_->IsCurrent());
+ CritScope lock(&cs_);
+ ASSERT(channel == channel_);
+ signal_thread_->Clear(this, MSG_SI_DESTROYCHANNEL);
+ // When MSG_WK_PURGE is received, we know there will be no more messages from
+ // the worker thread.
+ worker_thread_->Clear(this, MSG_WK_CLOCK);
+ worker_thread_->Post(this, MSG_WK_PURGE);
+ session_ = NULL;
+ channel_ = NULL;
+ if ((stream_ != NULL)
+ && ((tcp_ == NULL) || (tcp_->State() != PseudoTcp::TCP_CLOSED)))
+ stream_thread_->Post(this, MSG_ST_EVENT, new EventData(SE_CLOSE, 0));
+ if (tcp_) {
+ tcp_->Close(true);
+ AdjustClock();
+ }
+}
+
+//
+// Stream thread methods
+//
+
+StreamState PseudoTcpChannel::GetState() const {
+ ASSERT(stream_ != NULL && stream_thread_->IsCurrent());
+ CritScope lock(&cs_);
+ if (!tcp_)
+ return SS_OPENING;
+ switch (tcp_->State()) {
+ case PseudoTcp::TCP_LISTEN:
+ case PseudoTcp::TCP_SYN_SENT:
+ case PseudoTcp::TCP_SYN_RECEIVED:
+ return SS_OPENING;
+ case PseudoTcp::TCP_ESTABLISHED:
+ return SS_OPEN;
+ case PseudoTcp::TCP_CLOSED:
+ default:
+ return SS_CLOSED;
+ }
+}
+
+StreamResult PseudoTcpChannel::Read(void* buffer, size_t buffer_len,
+ size_t* read, int* error) {
+ ASSERT(stream_ != NULL && stream_thread_->IsCurrent());
+ CritScope lock(&cs_);
+ if (!tcp_)
+ return SR_BLOCK;
+
+ stream_readable_ = false;
+ int result = tcp_->Recv(static_cast<char*>(buffer), buffer_len);
+ //LOG_F(LS_VERBOSE) << "Recv returned: " << result;
+ if (result > 0) {
+ if (read)
+ *read = result;
+ // PseudoTcp doesn't currently support repeated Readable signals. Simulate
+ // them here.
+ stream_readable_ = true;
+ if (!pending_read_event_) {
+ pending_read_event_ = true;
+ stream_thread_->Post(this, MSG_ST_EVENT, new EventData(SE_READ), true);
+ }
+ return SR_SUCCESS;
+ } else if (IsBlockingError(tcp_->GetError())) {
+ return SR_BLOCK;
+ } else {
+ if (error)
+ *error = tcp_->GetError();
+ return SR_ERROR;
+ }
+ AdjustClock();
+}
+
+StreamResult PseudoTcpChannel::Write(const void* data, size_t data_len,
+ size_t* written, int* error) {
+ ASSERT(stream_ != NULL && stream_thread_->IsCurrent());
+ CritScope lock(&cs_);
+ if (!tcp_)
+ return SR_BLOCK;
+ int result = tcp_->Send(static_cast<const char*>(data), data_len);
+ //LOG_F(LS_VERBOSE) << "Send returned: " << result;
+ if (result > 0) {
+ if (written)
+ *written = result;
+ return SR_SUCCESS;
+ } else if (IsBlockingError(tcp_->GetError())) {
+ return SR_BLOCK;
+ } else {
+ if (error)
+ *error = tcp_->GetError();
+ return SR_ERROR;
+ }
+ AdjustClock();
+}
+
+void PseudoTcpChannel::Close() {
+ ASSERT(stream_ != NULL && stream_thread_->IsCurrent());
+ CritScope lock(&cs_);
+ stream_ = NULL;
+ // Clear out any pending event notifications
+ stream_thread_->Clear(this, MSG_ST_EVENT);
+ if (tcp_) {
+ tcp_->Close(false);
+ AdjustClock();
+ } else {
+ CheckDestroy();
+ }
+}
+
+//
+// Worker thread methods
+//
+
+void PseudoTcpChannel::OnChannelWritableState(TransportChannel* channel) {
+ LOG_F(LS_VERBOSE) << "[" << channel_name_ << "]";
+ ASSERT(worker_thread_->IsCurrent());
+ CritScope lock(&cs_);
+ if (!channel_) {
+ LOG_F(LS_WARNING) << "NULL channel";
+ return;
+ }
+ ASSERT(channel == channel_);
+ if (!tcp_) {
+ LOG_F(LS_WARNING) << "NULL tcp";
+ return;
+ }
+ if (!ready_to_connect_ || !channel->writable())
+ return;
+
+ ready_to_connect_ = false;
+ tcp_->Connect();
+ AdjustClock();
+}
+
+void PseudoTcpChannel::OnChannelRead(TransportChannel* channel,
+ const char* data, size_t size) {
+ //LOG_F(LS_VERBOSE) << "(" << size << ")";
+ ASSERT(worker_thread_->IsCurrent());
+ CritScope lock(&cs_);
+ if (!channel_) {
+ LOG_F(LS_WARNING) << "NULL channel";
+ return;
+ }
+ ASSERT(channel == channel_);
+ if (!tcp_) {
+ LOG_F(LS_WARNING) << "NULL tcp";
+ return;
+ }
+ tcp_->NotifyPacket(data, size);
+ AdjustClock();
+}
+
+void PseudoTcpChannel::OnChannelConnectionChanged(TransportChannel* channel,
+ const SocketAddress& addr) {
+ LOG_F(LS_VERBOSE) << "[" << channel_name_ << "]";
+ ASSERT(worker_thread_->IsCurrent());
+ CritScope lock(&cs_);
+ if (!channel_) {
+ LOG_F(LS_WARNING) << "NULL channel";
+ return;
+ }
+ ASSERT(channel == channel_);
+ if (!tcp_) {
+ LOG_F(LS_WARNING) << "NULL tcp";
+ return;
+ }
+
+ scoped_ptr<Socket> mtu_socket(
+ worker_thread_->socketserver()
+ ->CreateSocket(SOCK_DGRAM));
+
+ uint16 mtu = 65535;
+ if (mtu_socket->Connect(addr) < 0) {
+ LOG_F(LS_ERROR) << "Socket::Connect: " << mtu_socket->GetError();
+ } else if (mtu_socket->EstimateMTU(&mtu) < 0) {
+ LOG_F(LS_ERROR) << "Socket::EstimateMTU: " << mtu_socket->GetError();
+ } else {
+ tcp_->NotifyMTU(mtu);
+ AdjustClock();
+ }
+}
+
+void PseudoTcpChannel::OnTcpOpen(PseudoTcp* tcp) {
+ LOG_F(LS_VERBOSE) << "[" << channel_name_ << "]";
+ ASSERT(cs_.CurrentThreadIsOwner());
+ ASSERT(worker_thread_->IsCurrent());
+ ASSERT(tcp == tcp_);
+ if (stream_) {
+ stream_readable_ = true;
+ pending_read_event_ = true;
+ stream_thread_->Post(this, MSG_ST_EVENT,
+ new EventData(SE_OPEN | SE_READ | SE_WRITE));
+ }
+}
+
+void PseudoTcpChannel::OnTcpReadable(PseudoTcp* tcp) {
+ //LOG_F(LS_VERBOSE);
+ ASSERT(cs_.CurrentThreadIsOwner());
+ ASSERT(worker_thread_->IsCurrent());
+ ASSERT(tcp == tcp_);
+ if (stream_) {
+ stream_readable_ = true;
+ if (!pending_read_event_) {
+ pending_read_event_ = true;
+ stream_thread_->Post(this, MSG_ST_EVENT, new EventData(SE_READ));
+ }
+ }
+}
+
+void PseudoTcpChannel::OnTcpWriteable(PseudoTcp* tcp) {
+ //LOG_F(LS_VERBOSE);
+ ASSERT(cs_.CurrentThreadIsOwner());
+ ASSERT(worker_thread_->IsCurrent());
+ ASSERT(tcp == tcp_);
+ if (stream_)
+ stream_thread_->Post(this, MSG_ST_EVENT, new EventData(SE_WRITE));
+}
+
+void PseudoTcpChannel::OnTcpClosed(PseudoTcp* tcp, uint32 nError) {
+ LOG_F(LS_VERBOSE) << "[" << channel_name_ << "]";
+ ASSERT(cs_.CurrentThreadIsOwner());
+ ASSERT(worker_thread_->IsCurrent());
+ ASSERT(tcp == tcp_);
+ if (stream_)
+ stream_thread_->Post(this, MSG_ST_EVENT, new EventData(SE_CLOSE, nError));
+}
+
+//
+// Multi-thread methods
+//
+
+void PseudoTcpChannel::OnMessage(Message* pmsg) {
+ if (pmsg->message_id == MSG_WK_CLOCK) {
+
+ ASSERT(worker_thread_->IsCurrent());
+ //LOG(LS_INFO) << "PseudoTcpChannel::OnMessage(MSG_WK_CLOCK)";
+ CritScope lock(&cs_);
+ if (tcp_) {
+ tcp_->NotifyClock(PseudoTcp::Now());
+ AdjustClock(false);
+ }
+
+ } else if (pmsg->message_id == MSG_WK_PURGE) {
+
+ ASSERT(worker_thread_->IsCurrent());
+ LOG_F(LS_INFO) << "(MSG_WK_PURGE)";
+ // At this point, we know there are no additional worker thread messages.
+ CritScope lock(&cs_);
+ ASSERT(NULL == session_);
+ ASSERT(NULL == channel_);
+ worker_thread_ = NULL;
+ CheckDestroy();
+
+ } else if (pmsg->message_id == MSG_ST_EVENT) {
+
+ ASSERT(stream_thread_->IsCurrent());
+ //LOG(LS_INFO) << "PseudoTcpChannel::OnMessage(MSG_ST_EVENT, "
+ // << data->event << ", " << data->error << ")";
+ ASSERT(stream_ != NULL);
+ EventData* data = static_cast<EventData*>(pmsg->pdata);
+ if (data->event & SE_READ) {
+ CritScope lock(&cs_);
+ pending_read_event_ = false;
+ }
+ stream_->SignalEvent(stream_, data->event, data->error);
+ delete data;
+
+ } else if (pmsg->message_id == MSG_SI_DESTROYCHANNEL) {
+
+ ASSERT(signal_thread_->IsCurrent());
+ LOG_F(LS_INFO) << "(MSG_SI_DESTROYCHANNEL)";
+ ASSERT(session_ != NULL);
+ ASSERT(channel_ != NULL);
+ session_->DestroyChannel(channel_);
+
+ } else if (pmsg->message_id == MSG_SI_DESTROY) {
+
+ ASSERT(signal_thread_->IsCurrent());
+ LOG_F(LS_INFO) << "(MSG_SI_DESTROY)";
+ // The message queue is empty, so it is safe to destroy ourselves.
+ delete this;
+
+ } else {
+ ASSERT(false);
+ }
+}
+
+IPseudoTcpNotify::WriteResult PseudoTcpChannel::TcpWritePacket(
+ PseudoTcp* tcp, const char* buffer, size_t len) {
+ ASSERT(cs_.CurrentThreadIsOwner());
+ ASSERT(tcp == tcp_);
+ ASSERT(NULL != channel_);
+ int sent = channel_->SendPacket(buffer, len);
+ if (sent > 0) {
+ //LOG_F(LS_VERBOSE) << "(" << sent << ") Sent";
+ return IPseudoTcpNotify::WR_SUCCESS;
+ } else if (IsBlockingError(channel_->GetError())) {
+ LOG_F(LS_VERBOSE) << "Blocking";
+ return IPseudoTcpNotify::WR_SUCCESS;
+ } else if (channel_->GetError() == EMSGSIZE) {
+ LOG_F(LS_ERROR) << "EMSGSIZE";
+ return IPseudoTcpNotify::WR_TOO_LARGE;
+ } else {
+ PLOG(LS_ERROR, channel_->GetError()) << "PseudoTcpChannel::TcpWritePacket";
+ ASSERT(false);
+ return IPseudoTcpNotify::WR_FAIL;
+ }
+}
+
+void PseudoTcpChannel::AdjustClock(bool clear) {
+ ASSERT(cs_.CurrentThreadIsOwner());
+ ASSERT(NULL != tcp_);
+
+ long timeout = 0;
+ if (tcp_->GetNextClock(PseudoTcp::Now(), timeout)) {
+ ASSERT(NULL != channel_);
+ // Reset the next clock, by clearing the old and setting a new one.
+ if (clear)
+ worker_thread_->Clear(this, MSG_WK_CLOCK);
+ worker_thread_->PostDelayed(_max(timeout, 0L), this, MSG_WK_CLOCK);
+ return;
+ }
+
+ delete tcp_;
+ tcp_ = NULL;
+ ready_to_connect_ = false;
+
+ if (channel_) {
+ // If TCP has failed, no need for channel_ anymore
+ signal_thread_->Post(this, MSG_SI_DESTROYCHANNEL);
+ }
+}
+
+void PseudoTcpChannel::CheckDestroy() {
+ ASSERT(cs_.CurrentThreadIsOwner());
+ if ((worker_thread_ != NULL) || (stream_ != NULL))
+ return;
+ signal_thread_->Post(this, MSG_SI_DESTROY);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// PseudoTcpChannel::InternalStream
+///////////////////////////////////////////////////////////////////////////////
+
+PseudoTcpChannel::InternalStream::InternalStream(PseudoTcpChannel* parent)
+ : parent_(parent) {
+}
+
+PseudoTcpChannel::InternalStream::~InternalStream() {
+ Close();
+}
+
+StreamState PseudoTcpChannel::InternalStream::GetState() const {
+ if (!parent_)
+ return SS_CLOSED;
+ return parent_->GetState();
+}
+
+StreamResult PseudoTcpChannel::InternalStream::Read(
+ void* buffer, size_t buffer_len, size_t* read, int* error) {
+ if (!parent_) {
+ if (error)
+ *error = ENOTCONN;
+ return SR_ERROR;
+ }
+ return parent_->Read(buffer, buffer_len, read, error);
+}
+
+StreamResult PseudoTcpChannel::InternalStream::Write(
+ const void* data, size_t data_len, size_t* written, int* error) {
+ if (!parent_) {
+ if (error)
+ *error = ENOTCONN;
+ return SR_ERROR;
+ }
+ return parent_->Write(data, data_len, written, error);
+}
+
+void PseudoTcpChannel::InternalStream::Close() {
+ if (!parent_)
+ return;
+ parent_->Close();
+ parent_ = NULL;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+} // namespace cricket
diff --git a/Plugins/jingle/libjingle/talk/session/tunnel/pseudotcpchannel.h b/Plugins/jingle/libjingle/talk/session/tunnel/pseudotcpchannel.h
new file mode 100644
index 0000000..27574ea
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/session/tunnel/pseudotcpchannel.h
@@ -0,0 +1,125 @@
+/*
+ * libjingle
+ * Copyright 2004--2006, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __PSEUDOTCPCHANNEL_H__
+#define __PSEUDOTCPCHANNEL_H__
+
+#include "talk/base/criticalsection.h"
+#include "talk/base/messagequeue.h"
+#include "talk/base/stream.h"
+#include "talk/p2p/base/pseudotcp.h"
+#include "talk/p2p/base/session.h"
+
+namespace talk_base {
+class Thread;
+}
+
+namespace cricket {
+
+class TransportChannel;
+
+///////////////////////////////////////////////////////////////////////////////
+// ChannelStream
+// Note: The lifetime of TunnelSession is complicated. It needs to survive
+// until the following three conditions are true:
+// 1) TunnelStream has called Close (tracked via non-null stream_)
+// 2) PseudoTcp has completed (tracked via non-null tcp_)
+// 3) Session has been destroyed (tracked via non-null session_)
+// This is accomplished by calling CheckDestroy after these indicators change.
+///////////////////////////////////////////////////////////////////////////////
+// TunnelStream
+// Note: Because TunnelStream provides a stream interface, it's lifetime is
+// controlled by the owner of the stream pointer. As a result, we must support
+// both the TunnelSession disappearing before TunnelStream, and vice versa.
+///////////////////////////////////////////////////////////////////////////////
+
+class PseudoTcpChannel
+ : public IPseudoTcpNotify,
+ public talk_base::MessageHandler,
+ public sigslot::has_slots<> {
+public:
+ // Signal thread methods
+ PseudoTcpChannel(talk_base::Thread* stream_thread,
+ Session* session);
+
+ bool Connect(const std::string& channel_name);
+ talk_base::StreamInterface* GetStream();
+
+ sigslot::signal1<PseudoTcpChannel*> SignalChannelClosed;
+
+private:
+ class InternalStream;
+ friend class InternalStream;
+
+ virtual ~PseudoTcpChannel();
+
+ // Stream thread methods
+ talk_base::StreamState GetState() const;
+ talk_base::StreamResult Read(void* buffer, size_t buffer_len,
+ size_t* read, int* error);
+ talk_base::StreamResult Write(const void* data, size_t data_len,
+ size_t* written, int* error);
+ void Close();
+
+ // Multi-thread methods
+ void OnMessage(talk_base::Message* pmsg);
+ void AdjustClock(bool clear = true);
+ void CheckDestroy();
+
+ // Signal thread methods
+ void OnChannelDestroyed(TransportChannel* channel);
+
+ // Worker thread methods
+ void OnChannelWritableState(TransportChannel* channel);
+ void OnChannelRead(TransportChannel* channel, const char* data, size_t size);
+ void OnChannelConnectionChanged(TransportChannel* channel,
+ const talk_base::SocketAddress& addr);
+
+ virtual void OnTcpOpen(PseudoTcp* ptcp);
+ virtual void OnTcpReadable(PseudoTcp* ptcp);
+ virtual void OnTcpWriteable(PseudoTcp* ptcp);
+ virtual void OnTcpClosed(PseudoTcp* ptcp, uint32 nError);
+ virtual IPseudoTcpNotify::WriteResult TcpWritePacket(PseudoTcp* tcp,
+ const char* buffer,
+ size_t len);
+
+ talk_base::Thread* signal_thread_, * worker_thread_, * stream_thread_;
+ Session* session_;
+ TransportChannel* channel_;
+ std::string channel_name_;
+ PseudoTcp* tcp_;
+ InternalStream* stream_;
+ bool stream_readable_, pending_read_event_;
+ bool ready_to_connect_;
+ mutable talk_base::CriticalSection cs_;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+} // namespace cricket
+
+#endif // __PSEUDOTCPCHANNEL_H__
diff --git a/Plugins/jingle/libjingle/talk/session/tunnel/tunnelsessionclient.cc b/Plugins/jingle/libjingle/talk/session/tunnel/tunnelsessionclient.cc
new file mode 100644
index 0000000..855f0db
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/session/tunnel/tunnelsessionclient.cc
@@ -0,0 +1,316 @@
+/*
+ * libjingle
+ * Copyright 2004--2006, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "talk/base/basicdefs.h"
+#include "talk/base/basictypes.h"
+#include "talk/base/common.h"
+#include "talk/base/helpers.h"
+#include "talk/base/logging.h"
+#include "talk/base/stringutils.h"
+#include "talk/p2p/base/transportchannel.h"
+#include "talk/xmllite/xmlelement.h"
+#include "pseudotcpchannel.h"
+#include "tunnelsessionclient.h"
+
+namespace cricket {
+
+const std::string NS_TUNNEL("http://www.google.com/talk/tunnel");
+const buzz::QName QN_TUNNEL_DESCRIPTION(NS_TUNNEL, "description");
+const buzz::QName QN_TUNNEL_TYPE(NS_TUNNEL, "type");
+
+enum {
+ MSG_CLOCK = 1,
+ MSG_DESTROY,
+ MSG_TERMINATE,
+ MSG_EVENT,
+ MSG_CREATE_TUNNEL,
+};
+
+struct EventData : public talk_base::MessageData {
+ int event, error;
+ EventData(int ev, int err = 0) : event(ev), error(err) { }
+};
+
+struct CreateTunnelData : public talk_base::MessageData {
+ buzz::Jid jid;
+ std::string description;
+ talk_base::Thread* thread;
+ talk_base::StreamInterface* stream;
+};
+
+extern const talk_base::ConstantLabel SESSION_STATES[];
+
+const talk_base::ConstantLabel SESSION_STATES[] = {
+ KLABEL(Session::STATE_INIT),
+ KLABEL(Session::STATE_SENTINITIATE),
+ KLABEL(Session::STATE_RECEIVEDINITIATE),
+ KLABEL(Session::STATE_SENTACCEPT),
+ KLABEL(Session::STATE_RECEIVEDACCEPT),
+ KLABEL(Session::STATE_SENTMODIFY),
+ KLABEL(Session::STATE_RECEIVEDMODIFY),
+ KLABEL(Session::STATE_SENTREJECT),
+ KLABEL(Session::STATE_RECEIVEDREJECT),
+ KLABEL(Session::STATE_SENTREDIRECT),
+ KLABEL(Session::STATE_SENTTERMINATE),
+ KLABEL(Session::STATE_RECEIVEDTERMINATE),
+ KLABEL(Session::STATE_INPROGRESS),
+ KLABEL(Session::STATE_DEINIT),
+ LASTLABEL
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// TunnelSessionDescription
+///////////////////////////////////////////////////////////////////////////////
+
+struct TunnelSessionDescription : public SessionDescription {
+ std::string description;
+
+ TunnelSessionDescription(const std::string& desc) : description(desc) { }
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// TunnelSessionClient
+///////////////////////////////////////////////////////////////////////////////
+
+TunnelSessionClient::TunnelSessionClient(const buzz::Jid& jid,
+ SessionManager* manager)
+ : jid_(jid), session_manager_(manager), shutdown_(false) {
+ // Register ourselves as the handler of tunnel sessions.
+ session_manager_->AddClient(NS_TUNNEL, this);
+}
+
+TunnelSessionClient::~TunnelSessionClient() {
+ shutdown_ = true;
+ for (std::vector<TunnelSession*>::iterator it = sessions_.begin();
+ it != sessions_.end();
+ ++it) {
+ Session* session = (*it)->ReleaseSession(true);
+ session_manager_->DestroySession(session);
+ }
+ session_manager_->RemoveClient(NS_TUNNEL);
+}
+
+const SessionDescription* TunnelSessionClient::CreateSessionDescription(
+ const buzz::XmlElement* element) {
+ if (const buzz::XmlElement* type_elem = element->FirstNamed(QN_TUNNEL_TYPE)) {
+ return new TunnelSessionDescription(type_elem->BodyText());
+ }
+ ASSERT(false);
+ return 0;
+}
+
+buzz::XmlElement* TunnelSessionClient::TranslateSessionDescription(
+ const SessionDescription* description) {
+ const TunnelSessionDescription* desc =
+ static_cast<const TunnelSessionDescription*>(description);
+
+ buzz::XmlElement* root = new buzz::XmlElement(QN_TUNNEL_DESCRIPTION, true);
+ buzz::XmlElement* type_elem = new buzz::XmlElement(QN_TUNNEL_TYPE);
+ type_elem->SetBodyText(desc->description);
+ root->AddElement(type_elem);
+ return root;
+}
+
+void TunnelSessionClient::OnSessionCreate(Session* session, bool received) {
+ LOG(LS_INFO) << "TunnelSessionClient::OnSessionCreate: received=" << received;
+ ASSERT(session_manager_->signaling_thread()->IsCurrent());
+ if (received)
+ sessions_.push_back(
+ new TunnelSession(this, session, talk_base::Thread::Current()));
+}
+
+void TunnelSessionClient::OnSessionDestroy(Session* session) {
+ LOG(LS_INFO) << "TunnelSessionClient::OnSessionDestroy";
+ ASSERT(session_manager_->signaling_thread()->IsCurrent());
+ if (shutdown_)
+ return;
+ for (std::vector<TunnelSession*>::iterator it = sessions_.begin();
+ it != sessions_.end();
+ ++it) {
+ if ((*it)->HasSession(session)) {
+ VERIFY((*it)->ReleaseSession(false) == session);
+ sessions_.erase(it);
+ return;
+ }
+ }
+}
+
+talk_base::StreamInterface* TunnelSessionClient::CreateTunnel(
+ const buzz::Jid& to, const std::string& description) {
+ // Valid from any thread
+ CreateTunnelData data;
+ data.jid = to;
+ data.description = description;
+ data.thread = talk_base::Thread::Current();
+ session_manager_->signaling_thread()->Send(this, MSG_CREATE_TUNNEL, &data);
+ return data.stream;
+}
+
+talk_base::StreamInterface* TunnelSessionClient::AcceptTunnel(
+ Session* session) {
+ ASSERT(session_manager_->signaling_thread()->IsCurrent());
+ TunnelSession* tunnel = NULL;
+ for (std::vector<TunnelSession*>::iterator it = sessions_.begin();
+ it != sessions_.end();
+ ++it) {
+ if ((*it)->HasSession(session)) {
+ tunnel = *it;
+ break;
+ }
+ }
+ ASSERT(tunnel != NULL);
+
+ const TunnelSessionDescription* in_desc =
+ static_cast<const TunnelSessionDescription*>(
+ session->remote_description());
+ TunnelSessionDescription* out_desc = new TunnelSessionDescription(
+ in_desc->description);
+ session->Accept(out_desc);
+ return tunnel->GetStream();
+}
+
+void TunnelSessionClient::DeclineTunnel(Session* session) {
+ ASSERT(session_manager_->signaling_thread()->IsCurrent());
+ session->Reject();
+}
+
+void TunnelSessionClient::OnMessage(talk_base::Message* pmsg) {
+ if (pmsg->message_id == MSG_CREATE_TUNNEL) {
+ ASSERT(session_manager_->signaling_thread()->IsCurrent());
+ CreateTunnelData* data = static_cast<CreateTunnelData*>(pmsg->pdata);
+ Session* session = session_manager_->CreateSession(jid_.Str(), NS_TUNNEL);
+ TunnelSession* tunnel = new TunnelSession(this, session, data->thread);
+ sessions_.push_back(tunnel);
+ TunnelSessionDescription* desc = new TunnelSessionDescription(data->description);
+ session->Initiate(data->jid.Str(), NULL, desc);
+ data->stream = tunnel->GetStream();
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// TunnelSession
+///////////////////////////////////////////////////////////////////////////////
+
+//
+// Signalling thread methods
+//
+
+TunnelSession::TunnelSession(TunnelSessionClient* client, Session* session,
+ talk_base::Thread* stream_thread)
+ : client_(client), session_(session), channel_(NULL) {
+ ASSERT(client_ != NULL);
+ ASSERT(session_ != NULL);
+ session_->SignalState.connect(this, &TunnelSession::OnSessionState);
+ channel_ = new PseudoTcpChannel(stream_thread, session_);
+ channel_->SignalChannelClosed.connect(this, &TunnelSession::OnChannelClosed);
+}
+
+TunnelSession::~TunnelSession() {
+ ASSERT(client_ != NULL);
+ ASSERT(session_ == NULL);
+ ASSERT(channel_ == NULL);
+}
+
+talk_base::StreamInterface* TunnelSession::GetStream() {
+ ASSERT(channel_ != NULL);
+ return channel_->GetStream();
+}
+
+bool TunnelSession::HasSession(Session* session) {
+ ASSERT(NULL != session_);
+ return (session_ == session);
+}
+
+Session* TunnelSession::ReleaseSession(bool channel_exists) {
+ ASSERT(NULL != session_);
+ ASSERT(NULL != channel_);
+ Session* session = session_;
+ session_->SignalState.disconnect(this);
+ session_ = NULL;
+ if (channel_exists)
+ channel_->SignalChannelClosed.disconnect(this);
+ channel_ = NULL;
+ delete this;
+ return session;
+}
+
+void TunnelSession::OnSessionState(Session* session, Session::State state) {
+ LOG(LS_INFO) << "TunnelSession::OnSessionState("
+ << talk_base::nonnull(
+ talk_base::FindLabel(state, SESSION_STATES), "Unknown")
+ << ")";
+ ASSERT(session == session_);
+
+ switch (state) {
+ case Session::STATE_RECEIVEDINITIATE:
+ OnInitiate();
+ break;
+ case Session::STATE_SENTACCEPT:
+ case Session::STATE_RECEIVEDACCEPT:
+ OnAccept();
+ break;
+ case Session::STATE_SENTTERMINATE:
+ case Session::STATE_RECEIVEDTERMINATE:
+ OnTerminate();
+ break;
+ case Session::STATE_DEINIT:
+ // ReleaseSession should have been called before this.
+ ASSERT(false);
+ break;
+ }
+}
+
+void TunnelSession::OnInitiate() {
+ const TunnelSessionDescription* in_desc =
+ static_cast<const TunnelSessionDescription*>(
+ session_->remote_description());
+
+ ASSERT(client_ != NULL);
+ ASSERT(session_ != NULL);
+ client_->SignalIncomingTunnel(client_,
+ buzz::Jid(session_->remote_name()),
+ in_desc->description,
+ session_);
+}
+
+void TunnelSession::OnAccept() {
+ ASSERT(channel_ != NULL);
+ VERIFY(channel_->Connect("tcp"));
+}
+
+void TunnelSession::OnTerminate() {
+}
+
+void TunnelSession::OnChannelClosed(PseudoTcpChannel* channel) {
+ ASSERT(channel_ == channel);
+ ASSERT(session_ != NULL);
+ session_->Terminate();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+} // namespace cricket
diff --git a/Plugins/jingle/libjingle/talk/session/tunnel/tunnelsessionclient.h b/Plugins/jingle/libjingle/talk/session/tunnel/tunnelsessionclient.h
new file mode 100644
index 0000000..34c05bf
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/session/tunnel/tunnelsessionclient.h
@@ -0,0 +1,135 @@
+/*
+ * libjingle
+ * Copyright 2004--2006, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __TUNNELSESSIONCLIENT_H__
+#define __TUNNELSESSIONCLIENT_H__
+
+#include <vector>
+#include "talk/base/criticalsection.h"
+#include "talk/base/stream.h"
+#include "talk/p2p/base/pseudotcp.h"
+#include "talk/p2p/base/session.h"
+#include "talk/p2p/base/sessiondescription.h"
+#include "talk/p2p/base/sessionmanager.h"
+#include "talk/p2p/base/sessionclient.h"
+#include "talk/xmllite/qname.h"
+#include "talk/xmpp/constants.h"
+
+namespace cricket {
+
+class TunnelSession;
+class TunnelStream;
+
+///////////////////////////////////////////////////////////////////////////////
+// TunnelSessionClient
+///////////////////////////////////////////////////////////////////////////////
+
+class TunnelSessionClient
+ : public SessionClient, public talk_base::MessageHandler,
+ public sigslot::has_slots<> {
+public:
+ TunnelSessionClient(const buzz::Jid& jid, SessionManager* manager);
+ virtual ~TunnelSessionClient();
+
+ const buzz::Jid& jid() const { return jid_; }
+ SessionManager* session_manager() const { return session_manager_; }
+
+ const SessionDescription* CreateSessionDescription(
+ const buzz::XmlElement* element);
+ buzz::XmlElement* TranslateSessionDescription(
+ const SessionDescription* description);
+
+ void OnSessionCreate(Session* session, bool received);
+ void OnSessionDestroy(Session* session);
+
+ // This can be called on any thread. The stream interface is thread-safe, but
+ // notifications must be registered on the creating thread.
+ talk_base::StreamInterface* CreateTunnel(const buzz::Jid& to,
+ const std::string& description);
+
+ // Signal arguments are this, initiator, description, session
+ sigslot::signal4<TunnelSessionClient*, buzz::Jid, std::string, Session*>
+ SignalIncomingTunnel;
+ talk_base::StreamInterface* AcceptTunnel(Session* session);
+ void DeclineTunnel(Session* session);
+
+private:
+ void OnMessage(talk_base::Message* pmsg);
+
+ buzz::Jid jid_;
+ SessionManager* session_manager_;
+ std::vector<TunnelSession*> sessions_;
+ bool shutdown_;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// TunnelSession
+// Note: The lifetime of TunnelSession is complicated. It needs to survive
+// until the following three conditions are true:
+// 1) TunnelStream has called Close (tracked via non-null stream_)
+// 2) PseudoTcp has completed (tracked via non-null tcp_)
+// 3) Session has been destroyed (tracked via non-null session_)
+// This is accomplished by calling CheckDestroy after these indicators change.
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+// TunnelStream
+// Note: Because TunnelStream provides a stream interface, it's lifetime is
+// controlled by the owner of the stream pointer. As a result, we must support
+// both the TunnelSession disappearing before TunnelStream, and vice versa.
+///////////////////////////////////////////////////////////////////////////////
+
+class PseudoTcpChannel;
+
+class TunnelSession : public sigslot::has_slots<> {
+public:
+ // Signalling thread methods
+ TunnelSession(TunnelSessionClient* client, Session* session,
+ talk_base::Thread* stream_thread);
+
+ talk_base::StreamInterface* GetStream();
+ bool HasSession(Session* session);
+ Session* ReleaseSession(bool channel_exists);
+
+private:
+ virtual ~TunnelSession();
+
+ void OnSessionState(Session* session, Session::State state);
+ void OnInitiate();
+ void OnAccept();
+ void OnTerminate();
+ void OnChannelClosed(PseudoTcpChannel* channel);
+
+ TunnelSessionClient* client_;
+ Session* session_;
+ PseudoTcpChannel* channel_;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+} // namespace cricket
+
+#endif // __TUNNELSESSIONCLIENT_H__
diff --git a/Plugins/jingle/libjingle/talk/third_party/dll/mediastreamer2.dll b/Plugins/jingle/libjingle/talk/third_party/dll/mediastreamer2.dll
new file mode 100644
index 0000000..a6fe7da
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/third_party/dll/mediastreamer2.dll
Binary files differ
diff --git a/Plugins/jingle/libjingle/talk/third_party/dll/mediastreamer2.lib b/Plugins/jingle/libjingle/talk/third_party/dll/mediastreamer2.lib
new file mode 100644
index 0000000..07aca57
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/third_party/dll/mediastreamer2.lib
Binary files differ
diff --git a/Plugins/jingle/libjingle/talk/xmllite/qname.cc b/Plugins/jingle/libjingle/talk/xmllite/qname.cc
new file mode 100644
index 0000000..626cfa9
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/xmllite/qname.cc
@@ -0,0 +1,167 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <string>
+#include "talk/base/common.h"
+#include "talk/xmllite/xmlelement.h"
+#include "talk/xmllite/qname.h"
+#include "talk/xmllite/xmlconstants.h"
+
+//#define new TRACK_NEW
+
+namespace buzz {
+
+static int QName_Hash(const std::string & ns, const char * local) {
+ int result = ns.size() * 101;
+ while (*local) {
+ result *= 19;
+ result += *local;
+ local += 1;
+ }
+ return result;
+}
+
+static const int bits = 9;
+static QName::Data * get_qname_table() {
+ static QName::Data qname_table[1 << bits];
+ return qname_table;
+}
+
+static QName::Data *
+AllocateOrFind(const std::string & ns, const char * local) {
+ int index = QName_Hash(ns, local);
+ int increment = index >> (bits - 1) | 1;
+ QName::Data * qname_table = get_qname_table();
+ for (;;) {
+ index &= ((1 << bits) - 1);
+ if (!qname_table[index].Occupied()) {
+ return new QName::Data(ns, local);
+ }
+ if (qname_table[index].localPart_ == local &&
+ qname_table[index].namespace_ == ns) {
+ qname_table[index].AddRef();
+ return qname_table + index;
+ }
+ index += increment;
+ }
+}
+
+static QName::Data *
+Add(const std::string & ns, const char * local) {
+ int index = QName_Hash(ns, local);
+ int increment = index >> (bits - 1) | 1;
+ QName::Data * qname_table = get_qname_table();
+ for (;;) {
+ index &= ((1 << bits) - 1);
+ if (!qname_table[index].Occupied()) {
+ qname_table[index].namespace_ = ns;
+ qname_table[index].localPart_ = local;
+ qname_table[index].AddRef(); // AddRef twice so it's never deleted
+ qname_table[index].AddRef();
+ return qname_table + index;
+ }
+ if (qname_table[index].localPart_ == local &&
+ qname_table[index].namespace_ == ns) {
+ qname_table[index].AddRef();
+ return qname_table + index;
+ }
+ index += increment;
+ }
+}
+
+QName::~QName() {
+ data_->Release();
+}
+
+QName::QName() : data_(QN_EMPTY.data_) {
+ data_->AddRef();
+}
+
+QName::QName(bool add, const std::string & ns, const char * local) :
+ data_(add ? Add(ns, local) : AllocateOrFind(ns, local)) {}
+
+QName::QName(bool add, const std::string & ns, const std::string & local) :
+ data_(add ? Add(ns, local.c_str()) : AllocateOrFind(ns, local.c_str())) {}
+
+QName::QName(const std::string & ns, const char * local) :
+ data_(AllocateOrFind(ns, local)) {}
+
+static std::string
+QName_LocalPart(const std::string & name) {
+ size_t i = name.rfind(':');
+ if (i == std::string::npos)
+ return name;
+ return name.substr(i + 1);
+}
+
+static std::string
+QName_Namespace(const std::string & name) {
+ size_t i = name.rfind(':');
+ if (i == std::string::npos)
+ return STR_EMPTY;
+ return name.substr(0, i);
+}
+
+QName::QName(const std::string & mergedOrLocal) :
+ data_(AllocateOrFind(QName_Namespace(mergedOrLocal),
+ QName_LocalPart(mergedOrLocal).c_str())) {}
+
+std::string
+QName::Merged() const {
+ if (data_->namespace_ == STR_EMPTY)
+ return data_->localPart_;
+
+ std::string result(data_->namespace_);
+ result.reserve(result.length() + 1 + data_->localPart_.length());
+ result += ':';
+ result += data_->localPart_;
+ return result;
+}
+
+bool
+QName::operator==(const QName & other) const {
+ return other.data_ == data_ ||
+ data_->localPart_ == other.data_->localPart_ &&
+ data_->namespace_ == other.data_->namespace_;
+}
+
+int
+QName::Compare(const QName & other) const {
+ if (data_ == other.data_)
+ return 0;
+
+ int result = data_->localPart_.compare(other.data_->localPart_);
+ if (result)
+ return result;
+
+ return data_->namespace_.compare(other.data_->namespace_);
+}
+
+}
+
+
+
diff --git a/Plugins/jingle/libjingle/talk/xmllite/qname.h b/Plugins/jingle/libjingle/talk/xmllite/qname.h
new file mode 100644
index 0000000..b1bcec6
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/xmllite/qname.h
@@ -0,0 +1,87 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _qname_h_
+#define _qname_h_
+
+#include <string>
+
+namespace buzz {
+
+
+class QName
+{
+public:
+ explicit QName();
+ QName(const QName & qname) : data_(qname.data_) { data_->AddRef(); }
+ explicit QName(bool add, const std::string & ns, const char * local);
+ explicit QName(bool add, const std::string & ns, const std::string & local);
+ explicit QName(const std::string & ns, const char * local);
+ explicit QName(const std::string & mergedOrLocal);
+ QName & operator=(const QName & qn) {
+ qn.data_->AddRef();
+ data_->Release();
+ data_ = qn.data_;
+ return *this;
+ }
+ ~QName();
+
+ const std::string & Namespace() const { return data_->namespace_; }
+ const std::string & LocalPart() const { return data_->localPart_; }
+ std::string Merged() const;
+ int Compare(const QName & other) const;
+ bool operator==(const QName & other) const;
+ bool operator!=(const QName & other) const { return !operator==(other); }
+ bool operator<(const QName & other) const { return Compare(other) < 0; }
+
+ class Data {
+ public:
+ Data(const std::string & ns, const std::string & local) :
+ refcount_(1),
+ namespace_(ns),
+ localPart_(local) {}
+
+ Data() : refcount_(0) {}
+
+ std::string namespace_;
+ std::string localPart_;
+ void AddRef() { refcount_++; }
+ void Release() { if (!--refcount_) { delete this; } }
+ bool Occupied() { return !!refcount_; }
+
+ private:
+ int refcount_;
+ };
+
+private:
+ Data * data_;
+};
+
+
+}
+
+#endif
diff --git a/Plugins/jingle/libjingle/talk/xmllite/xmlconstants.cc b/Plugins/jingle/libjingle/talk/xmllite/xmlconstants.cc
new file mode 100644
index 0000000..503f832
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/xmllite/xmlconstants.cc
@@ -0,0 +1,65 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "xmlconstants.h"
+
+using namespace buzz;
+
+const std::string & XmlConstants::str_empty() {
+ static const std::string str_empty_;
+ return str_empty_;
+}
+
+const std::string & XmlConstants::ns_xml() {
+ static const std::string ns_xml_("http://www.w3.org/XML/1998/namespace");
+ return ns_xml_;
+}
+
+const std::string & XmlConstants::ns_xmlns() {
+ static const std::string ns_xmlns_("http://www.w3.org/2000/xmlns/");
+ return ns_xmlns_;
+}
+
+const std::string & XmlConstants::str_xmlns() {
+ static const std::string str_xmlns_("xmlns");
+ return str_xmlns_;
+}
+
+const std::string & XmlConstants::str_xml() {
+ static const std::string str_xml_("xml");
+ return str_xml_;
+}
+
+const std::string & XmlConstants::str_version() {
+ static const std::string str_version_("version");
+ return str_version_;
+}
+
+const std::string & XmlConstants::str_encoding() {
+ static const std::string str_encoding_("encoding");
+ return str_encoding_;
+}
diff --git a/Plugins/jingle/libjingle/talk/xmllite/xmlconstants.h b/Plugins/jingle/libjingle/talk/xmllite/xmlconstants.h
new file mode 100644
index 0000000..8514d6f
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/xmllite/xmlconstants.h
@@ -0,0 +1,61 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+// Because global constant initialization order is undefined
+// globals cannot depend on other objects to be instantiated.
+// This class creates string objects within static methods
+// such that globals may refer to these constants by the
+// accessor function and they are guaranteed to be initialized.
+
+#ifndef TALK_XMLLITE_CONSTANTS_H_
+#define TALK_XMLLITE_CONSTANTS_H_
+
+#include <string>
+
+#define STR_EMPTY XmlConstants::str_empty()
+#define NS_XML XmlConstants::ns_xml()
+#define NS_XMLNS XmlConstants::ns_xmlns()
+#define STR_XMLNS XmlConstants::str_xmlns()
+#define STR_XML XmlConstants::str_xml()
+#define STR_VERSION XmlConstants::str_version()
+#define STR_ENCODING XmlConstants::str_encoding()
+namespace buzz {
+
+class XmlConstants {
+ public:
+ static const std::string & str_empty();
+ static const std::string & ns_xml();
+ static const std::string & ns_xmlns();
+ static const std::string & str_xmlns();
+ static const std::string & str_xml();
+ static const std::string & str_version();
+ static const std::string & str_encoding();
+};
+
+}
+
+#endif // TALK_XMLLITE_CONSTANTS_H_
diff --git a/Plugins/jingle/libjingle/talk/xmllite/xmlelement.cc b/Plugins/jingle/libjingle/talk/xmllite/xmlelement.cc
new file mode 100644
index 0000000..7326332
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/xmllite/xmlelement.cc
@@ -0,0 +1,501 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <string>
+#include <iostream>
+#include <vector>
+#include <sstream>
+
+#include "talk/base/common.h"
+#include "talk/xmllite/xmlelement.h"
+#include "talk/xmllite/qname.h"
+//#include "talk/xmllite/xmlparser.h"
+//#include "talk/xmllite/xmlbuilder.h"
+#include "talk/xmllite/xmlprinter.h"
+#include "talk/xmllite/xmlconstants.h"
+
+namespace buzz {
+
+const QName QN_EMPTY(true, STR_EMPTY, STR_EMPTY);
+const QName QN_XMLNS(true, STR_EMPTY, STR_XMLNS);
+
+
+XmlChild::~XmlChild() {
+}
+
+bool
+XmlText::IsTextImpl() const {
+ return true;
+}
+
+XmlElement *
+XmlText::AsElementImpl() const {
+ return NULL;
+}
+
+XmlText *
+XmlText::AsTextImpl() const {
+ return const_cast<XmlText *>(this);
+}
+
+void
+XmlText::SetText(const std::string & text) {
+ text_ = text;
+}
+
+void
+XmlText::AddParsedText(const char * buf, int len) {
+ text_.append(buf, len);
+}
+
+void
+XmlText::AddText(const std::string & text) {
+ text_ += text;
+}
+
+XmlText::~XmlText() {
+}
+
+XmlElement::XmlElement(const QName & name) :
+ name_(name),
+ pFirstAttr_(NULL),
+ pLastAttr_(NULL),
+ pFirstChild_(NULL),
+ pLastChild_(NULL) {
+}
+
+XmlElement::XmlElement(const XmlElement & elt) :
+ XmlChild(),
+ name_(elt.name_),
+ pFirstAttr_(NULL),
+ pLastAttr_(NULL),
+ pFirstChild_(NULL),
+ pLastChild_(NULL) {
+
+ // copy attributes
+ XmlAttr * pAttr;
+ XmlAttr ** ppLastAttr = &pFirstAttr_;
+ XmlAttr * newAttr = NULL;
+ for (pAttr = elt.pFirstAttr_; pAttr; pAttr = pAttr->NextAttr()) {
+ newAttr = new XmlAttr(*pAttr);
+ *ppLastAttr = newAttr;
+ ppLastAttr = &(newAttr->pNextAttr_);
+ }
+ pLastAttr_ = newAttr;
+
+ // copy children
+ XmlChild * pChild;
+ XmlChild ** ppLast = &pFirstChild_;
+ XmlChild * newChild = NULL;
+
+ for (pChild = elt.pFirstChild_; pChild; pChild = pChild->NextChild()) {
+ if (pChild->IsText()) {
+ newChild = new XmlText(*(pChild->AsText()));
+ } else {
+ newChild = new XmlElement(*(pChild->AsElement()));
+ }
+ *ppLast = newChild;
+ ppLast = &(newChild->pNextChild_);
+ }
+ pLastChild_ = newChild;
+
+}
+
+XmlElement::XmlElement(const QName & name, bool useDefaultNs) :
+ name_(name),
+ pFirstAttr_(useDefaultNs ? new XmlAttr(QN_XMLNS, name.Namespace()) : NULL),
+ pLastAttr_(pFirstAttr_),
+ pFirstChild_(NULL),
+ pLastChild_(NULL) {
+}
+
+bool
+XmlElement::IsTextImpl() const {
+ return false;
+}
+
+XmlElement *
+XmlElement::AsElementImpl() const {
+ return const_cast<XmlElement *>(this);
+}
+
+XmlText *
+XmlElement::AsTextImpl() const {
+ return NULL;
+}
+
+const std::string &
+XmlElement::BodyText() const {
+ if (pFirstChild_ && pFirstChild_->IsText() && pLastChild_ == pFirstChild_) {
+ return pFirstChild_->AsText()->Text();
+ }
+
+ return STR_EMPTY;
+}
+
+void
+XmlElement::SetBodyText(const std::string & text) {
+ if (text == STR_EMPTY) {
+ ClearChildren();
+ } else if (pFirstChild_ == NULL) {
+ AddText(text);
+ } else if (pFirstChild_->IsText() && pLastChild_ == pFirstChild_) {
+ pFirstChild_->AsText()->SetText(text);
+ } else {
+ ClearChildren();
+ AddText(text);
+ }
+}
+
+const QName &
+XmlElement::FirstElementName() const {
+ const XmlElement * element = FirstElement();
+ if (element == NULL)
+ return QN_EMPTY;
+ return element->Name();
+}
+
+XmlAttr *
+XmlElement::FirstAttr() {
+ return pFirstAttr_;
+}
+
+const std::string &
+XmlElement::Attr(const QName & name) const {
+ XmlAttr * pattr;
+ for (pattr = pFirstAttr_; pattr; pattr = pattr->pNextAttr_) {
+ if (pattr->name_ == name)
+ return pattr->value_;
+ }
+ return STR_EMPTY;
+}
+
+bool
+XmlElement::HasAttr(const QName & name) const {
+ XmlAttr * pattr;
+ for (pattr = pFirstAttr_; pattr; pattr = pattr->pNextAttr_) {
+ if (pattr->name_ == name)
+ return true;
+ }
+ return false;
+}
+
+void
+XmlElement::SetAttr(const QName & name, const std::string & value) {
+ XmlAttr * pattr;
+ for (pattr = pFirstAttr_; pattr; pattr = pattr->pNextAttr_) {
+ if (pattr->name_ == name)
+ break;
+ }
+ if (!pattr) {
+ pattr = new XmlAttr(name, value);
+ if (pLastAttr_)
+ pLastAttr_->pNextAttr_ = pattr;
+ else
+ pFirstAttr_ = pattr;
+ pLastAttr_ = pattr;
+ return;
+ }
+ pattr->value_ = value;
+}
+
+void
+XmlElement::ClearAttr(const QName & name) {
+ XmlAttr * pattr;
+ XmlAttr *pLastAttr = NULL;
+ for (pattr = pFirstAttr_; pattr; pattr = pattr->pNextAttr_) {
+ if (pattr->name_ == name)
+ break;
+ pLastAttr = pattr;
+ }
+ if (!pattr)
+ return;
+ if (!pLastAttr)
+ pFirstAttr_ = pattr->pNextAttr_;
+ else
+ pLastAttr->pNextAttr_ = pattr->pNextAttr_;
+ if (pLastAttr_ == pattr)
+ pLastAttr_ = pLastAttr;
+ delete pattr;
+}
+
+XmlChild *
+XmlElement::FirstChild() {
+ return pFirstChild_;
+}
+
+XmlElement *
+XmlElement::FirstElement() {
+ XmlChild * pChild;
+ for (pChild = pFirstChild_; pChild; pChild = pChild->pNextChild_) {
+ if (!pChild->IsText())
+ return pChild->AsElement();
+ }
+ return NULL;
+}
+
+XmlElement *
+XmlElement::NextElement() {
+ XmlChild * pChild;
+ for (pChild = pNextChild_; pChild; pChild = pChild->pNextChild_) {
+ if (!pChild->IsText())
+ return pChild->AsElement();
+ }
+ return NULL;
+}
+
+XmlElement *
+XmlElement::FirstWithNamespace(const std::string & ns) {
+ XmlChild * pChild;
+ for (pChild = pFirstChild_; pChild; pChild = pChild->pNextChild_) {
+ if (!pChild->IsText() && pChild->AsElement()->Name().Namespace() == ns)
+ return pChild->AsElement();
+ }
+ return NULL;
+}
+
+XmlElement *
+XmlElement::NextWithNamespace(const std::string & ns) {
+ XmlChild * pChild;
+ for (pChild = pNextChild_; pChild; pChild = pChild->pNextChild_) {
+ if (!pChild->IsText() && pChild->AsElement()->Name().Namespace() == ns)
+ return pChild->AsElement();
+ }
+ return NULL;
+}
+
+XmlElement *
+XmlElement::FirstNamed(const QName & name) {
+ XmlChild * pChild;
+ for (pChild = pFirstChild_; pChild; pChild = pChild->pNextChild_) {
+ if (!pChild->IsText() && pChild->AsElement()->Name() == name)
+ return pChild->AsElement();
+ }
+ return NULL;
+}
+
+XmlElement *
+XmlElement::NextNamed(const QName & name) {
+ XmlChild * pChild;
+ for (pChild = pNextChild_; pChild; pChild = pChild->pNextChild_) {
+ if (!pChild->IsText() && pChild->AsElement()->Name() == name)
+ return pChild->AsElement();
+ }
+ return NULL;
+}
+
+XmlElement* XmlElement::FindOrAddNamedChild(const QName& name) {
+ XmlElement* child = FirstNamed(name);
+ if (!child) {
+ child = new XmlElement(name);
+ AddElement(child);
+ }
+
+ return child;
+}
+
+const std::string &
+XmlElement::TextNamed(const QName & name) const {
+ XmlChild * pChild;
+ for (pChild = pFirstChild_; pChild; pChild = pChild->pNextChild_) {
+ if (!pChild->IsText() && pChild->AsElement()->Name() == name)
+ return pChild->AsElement()->BodyText();
+ }
+ return STR_EMPTY;
+}
+
+void
+XmlElement::InsertChildAfter(XmlChild * pPredecessor, XmlChild * pNext) {
+ if (pPredecessor == NULL) {
+ pNext->pNextChild_ = pFirstChild_;
+ pFirstChild_ = pNext;
+ }
+ else {
+ pNext->pNextChild_ = pPredecessor->pNextChild_;
+ pPredecessor->pNextChild_ = pNext;
+ }
+}
+
+void
+XmlElement::RemoveChildAfter(XmlChild * pPredecessor) {
+ XmlChild * pNext;
+
+ if (pPredecessor == NULL) {
+ pNext = pFirstChild_;
+ pFirstChild_ = pNext->pNextChild_;
+ }
+ else {
+ pNext = pPredecessor->pNextChild_;
+ pPredecessor->pNextChild_ = pNext->pNextChild_;
+ }
+
+ if (pLastChild_ == pNext)
+ pLastChild_ = pPredecessor;
+
+ delete pNext;
+}
+
+void
+XmlElement::AddAttr(const QName & name, const std::string & value) {
+ ASSERT(!HasAttr(name));
+
+ XmlAttr ** pprev = pLastAttr_ ? &(pLastAttr_->pNextAttr_) : &pFirstAttr_;
+ pLastAttr_ = (*pprev = new XmlAttr(name, value));
+}
+
+void
+XmlElement::AddAttr(const QName & name, const std::string & value,
+ int depth) {
+ XmlElement * element = this;
+ while (depth--) {
+ element = element->pLastChild_->AsElement();
+ }
+ element->AddAttr(name, value);
+}
+
+void
+XmlElement::AddParsedText(const char * cstr, int len) {
+ if (len == 0)
+ return;
+
+ if (pLastChild_ && pLastChild_->IsText()) {
+ pLastChild_->AsText()->AddParsedText(cstr, len);
+ return;
+ }
+ XmlChild ** pprev = pLastChild_ ? &(pLastChild_->pNextChild_) : &pFirstChild_;
+ pLastChild_ = *pprev = new XmlText(cstr, len);
+}
+
+void
+XmlElement::AddText(const std::string & text) {
+ if (text == STR_EMPTY)
+ return;
+
+ if (pLastChild_ && pLastChild_->IsText()) {
+ pLastChild_->AsText()->AddText(text);
+ return;
+ }
+ XmlChild ** pprev = pLastChild_ ? &(pLastChild_->pNextChild_) : &pFirstChild_;
+ pLastChild_ = *pprev = new XmlText(text);
+}
+
+void
+XmlElement::AddText(const std::string & text, int depth) {
+ // note: the first syntax is ambigious for msvc 6
+ // XmlElement * pel(this);
+ XmlElement * element = this;
+ while (depth--) {
+ element = element->pLastChild_->AsElement();
+ }
+ element->AddText(text);
+}
+
+void
+XmlElement::AddElement(XmlElement *pelChild) {
+ if (pelChild == NULL)
+ return;
+
+ XmlChild ** pprev = pLastChild_ ? &(pLastChild_->pNextChild_) : &pFirstChild_;
+ pLastChild_ = *pprev = pelChild;
+ pelChild->pNextChild_ = NULL;
+}
+
+void
+XmlElement::AddElement(XmlElement *pelChild, int depth) {
+ XmlElement * element = this;
+ while (depth--) {
+ element = element->pLastChild_->AsElement();
+ }
+ element->AddElement(pelChild);
+}
+
+void
+XmlElement::ClearNamedChildren(const QName & name) {
+ XmlChild * prev_child = NULL;
+ XmlChild * next_child;
+ XmlChild * child;
+ for (child = FirstChild(); child; child = next_child) {
+ next_child = child->NextChild();
+ if (!child->IsText() && child->AsElement()->Name() == name)
+ {
+ RemoveChildAfter(prev_child);
+ continue;
+ }
+ prev_child = child;
+ }
+}
+
+void
+XmlElement::ClearChildren() {
+ XmlChild * pchild;
+ for (pchild = pFirstChild_; pchild; ) {
+ XmlChild * pToDelete = pchild;
+ pchild = pchild->pNextChild_;
+ delete pToDelete;
+ }
+ pFirstChild_ = pLastChild_ = NULL;
+}
+
+std::string
+XmlElement::Str() const {
+ std::stringstream ss;
+ Print(&ss, NULL, 0);
+ return ss.str();
+}
+
+/*
+XmlElement *
+XmlElement::ForStr(const std::string & str) {
+ XmlBuilder builder;
+ XmlParser::ParseXml(&builder, str);
+ return builder.CreateElement();
+}
+*/
+
+void
+XmlElement::Print(
+ std::ostream * pout, std::string xmlns[], int xmlnsCount) const {
+ XmlPrinter::PrintXml(pout, this, xmlns, xmlnsCount);
+}
+
+XmlElement::~XmlElement() {
+ XmlAttr * pattr;
+ for (pattr = pFirstAttr_; pattr; ) {
+ XmlAttr * pToDelete = pattr;
+ pattr = pattr->pNextAttr_;
+ delete pToDelete;
+ }
+
+ XmlChild * pchild;
+ for (pchild = pFirstChild_; pchild; ) {
+ XmlChild * pToDelete = pchild;
+ pchild = pchild->pNextChild_;
+ delete pToDelete;
+ }
+}
+
+}
diff --git a/Plugins/jingle/libjingle/talk/xmllite/xmlelement.h b/Plugins/jingle/libjingle/talk/xmllite/xmlelement.h
new file mode 100644
index 0000000..21c10a6
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/xmllite/xmlelement.h
@@ -0,0 +1,232 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _xmlelement_h_
+#define _xmlelement_h_
+
+#include <iosfwd>
+#include <string>
+#include "talk/base/scoped_ptr.h"
+#include "talk/xmllite/qname.h"
+
+namespace buzz {
+
+extern const QName QN_EMPTY;
+extern const QName QN_XMLNS;
+
+
+class XmlChild;
+class XmlText;
+class XmlElement;
+class XmlAttr;
+
+class XmlChild {
+friend class XmlElement;
+
+public:
+
+ XmlChild * NextChild() { return pNextChild_; }
+ const XmlChild * NextChild() const { return pNextChild_; }
+
+ bool IsText() const { return IsTextImpl(); }
+
+ XmlElement * AsElement() { return AsElementImpl(); }
+ const XmlElement * AsElement() const { return AsElementImpl(); }
+
+ XmlText * AsText() { return AsTextImpl(); }
+ const XmlText * AsText() const { return AsTextImpl(); }
+
+
+protected:
+
+ XmlChild() :
+ pNextChild_(NULL) {
+ }
+
+ virtual bool IsTextImpl() const = 0;
+ virtual XmlElement * AsElementImpl() const = 0;
+ virtual XmlText * AsTextImpl() const = 0;
+
+
+ virtual ~XmlChild();
+
+private:
+ XmlChild(const XmlChild & noimpl);
+
+ XmlChild * pNextChild_;
+
+};
+
+class XmlText : public XmlChild {
+public:
+ explicit XmlText(const std::string & text) :
+ XmlChild(),
+ text_(text) {
+ }
+ explicit XmlText(const XmlText & t) :
+ XmlChild(),
+ text_(t.text_) {
+ }
+ explicit XmlText(const char * cstr, size_t len) :
+ XmlChild(),
+ text_(cstr, len) {
+ }
+ virtual ~XmlText();
+
+ const std::string & Text() const { return text_; }
+ void SetText(const std::string & text);
+ void AddParsedText(const char * buf, int len);
+ void AddText(const std::string & text);
+
+protected:
+ virtual bool IsTextImpl() const;
+ virtual XmlElement * AsElementImpl() const;
+ virtual XmlText * AsTextImpl() const;
+
+private:
+ std::string text_;
+};
+
+class XmlAttr {
+friend class XmlElement;
+
+public:
+ XmlAttr * NextAttr() const { return pNextAttr_; }
+ const QName & Name() const { return name_; }
+ const std::string & Value() const { return value_; }
+
+private:
+ explicit XmlAttr(const QName & name, const std::string & value) :
+ pNextAttr_(NULL),
+ name_(name),
+ value_(value) {
+ }
+ explicit XmlAttr(const XmlAttr & att) :
+ pNextAttr_(NULL),
+ name_(att.name_),
+ value_(att.value_) {
+ }
+
+ XmlAttr * pNextAttr_;
+ QName name_;
+ std::string value_;
+};
+
+class XmlElement : public XmlChild {
+public:
+ explicit XmlElement(const QName & name);
+ explicit XmlElement(const QName & name, bool useDefaultNs);
+ explicit XmlElement(const XmlElement & elt);
+
+ virtual ~XmlElement();
+
+ const QName& Name() const { return name_; }
+ void SetName(const QName& name) { name_ = name; }
+
+ const std::string & BodyText() const;
+ void SetBodyText(const std::string & text);
+
+ const QName & FirstElementName() const;
+
+ XmlAttr * FirstAttr();
+ const XmlAttr * FirstAttr() const
+ { return const_cast<XmlElement *>(this)->FirstAttr(); }
+
+ //! Attr will return STR_EMPTY if the attribute isn't there:
+ //! use HasAttr to test presence of an attribute.
+ const std::string & Attr(const QName & name) const;
+ bool HasAttr(const QName & name) const;
+ void SetAttr(const QName & name, const std::string & value);
+ void ClearAttr(const QName & name);
+
+ XmlChild * FirstChild();
+ const XmlChild * FirstChild() const
+ { return const_cast<XmlElement *>(this)->FirstChild(); }
+
+ XmlElement * FirstElement();
+ const XmlElement * FirstElement() const
+ { return const_cast<XmlElement *>(this)->FirstElement(); }
+
+ XmlElement * NextElement();
+ const XmlElement * NextElement() const
+ { return const_cast<XmlElement *>(this)->NextElement(); }
+
+ XmlElement * FirstWithNamespace(const std::string & ns);
+ const XmlElement * FirstWithNamespace(const std::string & ns) const
+ { return const_cast<XmlElement *>(this)->FirstWithNamespace(ns); }
+
+ XmlElement * NextWithNamespace(const std::string & ns);
+ const XmlElement * NextWithNamespace(const std::string & ns) const
+ { return const_cast<XmlElement *>(this)->NextWithNamespace(ns); }
+
+ XmlElement * FirstNamed(const QName & name);
+ const XmlElement * FirstNamed(const QName & name) const
+ { return const_cast<XmlElement *>(this)->FirstNamed(name); }
+
+ XmlElement * NextNamed(const QName & name);
+ const XmlElement * NextNamed(const QName & name) const
+ { return const_cast<XmlElement *>(this)->NextNamed(name); }
+
+ // Finds the first element named 'name'. If that element can't be found then
+ // adds one and returns it.
+ XmlElement* FindOrAddNamedChild(const QName& name);
+
+ const std::string & TextNamed(const QName & name) const;
+
+ void InsertChildAfter(XmlChild * pPredecessor, XmlChild * pNewChild);
+ void RemoveChildAfter(XmlChild * pPredecessor);
+
+ void AddParsedText(const char * buf, int len);
+ void AddText(const std::string & text);
+ void AddText(const std::string & text, int depth);
+ void AddElement(XmlElement * pelChild);
+ void AddElement(XmlElement * pelChild, int depth);
+ void AddAttr(const QName & name, const std::string & value);
+ void AddAttr(const QName & name, const std::string & value, int depth);
+ void ClearNamedChildren(const QName & name);
+ void ClearChildren();
+
+// static XmlElement * ForStr(const std::string & str);
+ std::string Str() const;
+
+ void Print(std::ostream * pout, std::string xmlns[], int xmlnsCount) const;
+
+protected:
+ virtual bool IsTextImpl() const;
+ virtual XmlElement * AsElementImpl() const;
+ virtual XmlText * AsTextImpl() const;
+
+private:
+ QName name_;
+ XmlAttr * pFirstAttr_;
+ XmlAttr * pLastAttr_;
+ XmlChild * pFirstChild_;
+ XmlChild * pLastChild_;
+};
+
+}
+#endif
diff --git a/Plugins/jingle/libjingle/talk/xmllite/xmlnsstack.cc b/Plugins/jingle/libjingle/talk/xmllite/xmlnsstack.cc
new file mode 100644
index 0000000..4dcb649
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/xmllite/xmlnsstack.cc
@@ -0,0 +1,205 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "talk/base/stl_decl.h"
+#include <string>
+#include <iostream>
+#include <vector>
+#include <sstream>
+#include "talk/xmllite/xmlelement.h"
+#include "talk/xmllite/xmlnsstack.h"
+#include "talk/xmllite/xmlconstants.h"
+
+namespace buzz {
+
+XmlnsStack::XmlnsStack() :
+ pxmlnsStack_(new std::vector<std::string>),
+ pxmlnsDepthStack_(new std::vector<size_t>) {
+}
+
+XmlnsStack::~XmlnsStack() {}
+
+void
+XmlnsStack::PushFrame() {
+ pxmlnsDepthStack_->push_back(pxmlnsStack_->size());
+}
+
+void
+XmlnsStack::PopFrame() {
+ size_t prev_size = pxmlnsDepthStack_->back();
+ pxmlnsDepthStack_->pop_back();
+ if (prev_size < pxmlnsStack_->size()) {
+ pxmlnsStack_->erase(pxmlnsStack_->begin() + prev_size,
+ pxmlnsStack_->end());
+ }
+}
+const std::pair<std::string, bool> NS_NOT_FOUND(STR_EMPTY, false);
+const std::pair<std::string, bool> EMPTY_NS_FOUND(STR_EMPTY, true);
+const std::pair<std::string, bool> XMLNS_DEFINITION_FOUND(NS_XMLNS, true);
+
+const std::string *
+XmlnsStack::NsForPrefix(const std::string & prefix) {
+ if (prefix.length() >= 3 &&
+ (prefix[0] == 'x' || prefix[0] == 'X') &&
+ (prefix[1] == 'm' || prefix[1] == 'M') &&
+ (prefix[2] == 'l' || prefix[2] == 'L')) {
+ if (prefix == "xml")
+ return &(NS_XML);
+ if (prefix == "xmlns")
+ return &(NS_XMLNS);
+ return NULL;
+ }
+
+ std::vector<std::string>::iterator pos;
+ for (pos = pxmlnsStack_->end(); pos > pxmlnsStack_->begin(); ) {
+ pos -= 2;
+ if (*pos == prefix)
+ return &(*(pos + 1));
+ }
+
+ if (prefix == STR_EMPTY)
+ return &(STR_EMPTY); // default namespace
+
+ return NULL; // none found
+}
+
+bool
+XmlnsStack::PrefixMatchesNs(const std::string & prefix, const std::string & ns) {
+ const std::string * match = NsForPrefix(prefix);
+ if (match == NULL)
+ return false;
+ return (*match == ns);
+}
+
+std::pair<std::string, bool>
+XmlnsStack::PrefixForNs(const std::string & ns, bool isattr) {
+ if (ns == NS_XML)
+ return std::make_pair(std::string("xml"), true);
+ if (ns == NS_XMLNS)
+ return std::make_pair(std::string("xmlns"), true);
+ if (isattr ? ns == STR_EMPTY : PrefixMatchesNs(STR_EMPTY, ns))
+ return std::make_pair(STR_EMPTY, true);
+
+ std::vector<std::string>::iterator pos;
+ for (pos = pxmlnsStack_->end(); pos > pxmlnsStack_->begin(); ) {
+ pos -= 2;
+ if (*(pos + 1) == ns &&
+ (!isattr || !pos->empty()) && PrefixMatchesNs(*pos, ns))
+ return std::make_pair(*pos, true);
+ }
+
+ return std::make_pair(STR_EMPTY, false); // none found
+}
+
+std::string
+XmlnsStack::FormatQName(const QName & name, bool isAttr) {
+ std::string prefix(PrefixForNs(name.Namespace(), isAttr).first);
+ if (prefix == STR_EMPTY)
+ return name.LocalPart();
+ else
+ return prefix + ':' + name.LocalPart();
+}
+
+void
+XmlnsStack::AddXmlns(const std::string & prefix, const std::string & ns) {
+ pxmlnsStack_->push_back(prefix);
+ pxmlnsStack_->push_back(ns);
+}
+
+void
+XmlnsStack::RemoveXmlns() {
+ pxmlnsStack_->pop_back();
+ pxmlnsStack_->pop_back();
+}
+
+static bool IsAsciiLetter(char ch) {
+ return ((ch >= 'a' && ch <= 'z') ||
+ (ch >= 'A' && ch <= 'Z'));
+}
+
+static std::string AsciiLower(const std::string & s) {
+ std::string result(s);
+ size_t i;
+ for (i = 0; i < result.length(); i++) {
+ if (result[i] >= 'A' && result[i] <= 'Z')
+ result[i] += 'a' - 'A';
+ }
+ return result;
+}
+
+static std::string SuggestPrefix(const std::string & ns) {
+ size_t len = ns.length();
+ size_t i = ns.find_last_of('.');
+ if (i != std::string::npos && len - i <= 4 + 1)
+ len = i; // chop off ".html" or ".xsd" or ".?{0,4}"
+ size_t last = len;
+ while (last > 0) {
+ last -= 1;
+ if (IsAsciiLetter(ns[last])) {
+ size_t first = last;
+ last += 1;
+ while (first > 0) {
+ if (!IsAsciiLetter(ns[first - 1]))
+ break;
+ first -= 1;
+ }
+ if (last - first > 4)
+ last = first + 3;
+ std::string candidate(AsciiLower(ns.substr(first, last - first)));
+ if (candidate.find("xml") != 0)
+ return candidate;
+ break;
+ }
+ }
+ return "ns";
+}
+
+
+std::pair<std::string, bool>
+XmlnsStack::AddNewPrefix(const std::string & ns, bool isAttr) {
+ if (PrefixForNs(ns, isAttr).second)
+ return std::make_pair(STR_EMPTY, false);
+
+ std::string base(SuggestPrefix(ns));
+ std::string result(base);
+ int i = 2;
+ while (NsForPrefix(result) != NULL) {
+ std::stringstream ss;
+ ss << base;
+ ss << (i++);
+ ss >> result;
+ }
+ AddXmlns(result, ns);
+ return std::make_pair(result, true);
+}
+
+void XmlnsStack::Reset() {
+ pxmlnsStack_->clear();
+ pxmlnsDepthStack_->clear();
+}
+
+}
diff --git a/Plugins/jingle/libjingle/talk/xmllite/xmlnsstack.h b/Plugins/jingle/libjingle/talk/xmllite/xmlnsstack.h
new file mode 100644
index 0000000..299ec1c
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/xmllite/xmlnsstack.h
@@ -0,0 +1,62 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _xmlnsstack_h_
+#define _xmlnsstack_h_
+
+#include <string>
+#include "talk/base/scoped_ptr.h"
+#include "talk/base/stl_decl.h"
+#include "talk/xmllite/qname.h"
+
+namespace buzz {
+
+class XmlnsStack {
+public:
+ XmlnsStack();
+ ~XmlnsStack();
+
+ void AddXmlns(const std::string & prefix, const std::string & ns);
+ void RemoveXmlns();
+ void PushFrame();
+ void PopFrame();
+ void Reset();
+
+ const std::string * NsForPrefix(const std::string & prefix);
+ bool PrefixMatchesNs(const std::string & prefix, const std::string & ns);
+ std::pair<std::string, bool> PrefixForNs(const std::string & ns, bool isAttr);
+ std::pair<std::string, bool> AddNewPrefix(const std::string & ns, bool isAttr);
+ std::string FormatQName(const QName & name, bool isAttr);
+
+private:
+
+ scoped_ptr<std::vector<std::string, std::allocator<std::string> > > pxmlnsStack_;
+ scoped_ptr<std::vector<size_t, std::allocator<size_t> > > pxmlnsDepthStack_;
+};
+}
+
+#endif
diff --git a/Plugins/jingle/libjingle/talk/xmllite/xmlprinter.cc b/Plugins/jingle/libjingle/talk/xmllite/xmlprinter.cc
new file mode 100644
index 0000000..86f143a
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/xmllite/xmlprinter.cc
@@ -0,0 +1,190 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "talk/base/stl_decl.h"
+#include <string>
+#include <iostream>
+#include <vector>
+#include <sstream>
+#include "talk/xmllite/xmlelement.h"
+#include "talk/xmllite/xmlprinter.h"
+#include "talk/xmllite/xmlnsstack.h"
+#include "talk/xmllite/xmlconstants.h"
+
+namespace buzz {
+
+class XmlPrinterImpl {
+public:
+ XmlPrinterImpl(std::ostream * pout,
+ const std::string * const xmlns, int xmlnsCount);
+ void PrintElement(const XmlElement * element);
+ void PrintQuotedValue(const std::string & text);
+ void PrintBodyText(const std::string & text);
+
+private:
+ std::ostream *pout_;
+ XmlnsStack xmlnsStack_;
+};
+
+void
+XmlPrinter::PrintXml(std::ostream * pout, const XmlElement * element) {
+ PrintXml(pout, element, NULL, 0);
+}
+
+void
+XmlPrinter::PrintXml(std::ostream * pout, const XmlElement * element,
+ const std::string * const xmlns, int xmlnsCount) {
+ XmlPrinterImpl printer(pout, xmlns, xmlnsCount);
+ printer.PrintElement(element);
+}
+
+XmlPrinterImpl::XmlPrinterImpl(std::ostream * pout,
+ const std::string * const xmlns, int xmlnsCount) :
+ pout_(pout),
+ xmlnsStack_() {
+ int i;
+ for (i = 0; i < xmlnsCount; i += 2) {
+ xmlnsStack_.AddXmlns(xmlns[i], xmlns[i + 1]);
+ }
+}
+
+void
+XmlPrinterImpl::PrintElement(const XmlElement * element) {
+ xmlnsStack_.PushFrame();
+
+ // first go through attrs of pel to add xmlns definitions
+ const XmlAttr * pattr;
+ for (pattr = element->FirstAttr(); pattr; pattr = pattr->NextAttr()) {
+ if (pattr->Name() == QN_XMLNS)
+ xmlnsStack_.AddXmlns(STR_EMPTY, pattr->Value());
+ else if (pattr->Name().Namespace() == NS_XMLNS)
+ xmlnsStack_.AddXmlns(pattr->Name().LocalPart(),
+ pattr->Value());
+ }
+
+ // then go through qnames to make sure needed xmlns definitons are added
+ std::vector<std::string> newXmlns;
+ std::pair<std::string, bool> prefix;
+ prefix = xmlnsStack_.AddNewPrefix(element->Name().Namespace(), false);
+ if (prefix.second) {
+ newXmlns.push_back(prefix.first);
+ newXmlns.push_back(element->Name().Namespace());
+ }
+
+ for (pattr = element->FirstAttr(); pattr; pattr = pattr->NextAttr()) {
+ prefix = xmlnsStack_.AddNewPrefix(pattr->Name().Namespace(), true);
+ if (prefix.second) {
+ newXmlns.push_back(prefix.first);
+ newXmlns.push_back(pattr->Name().Namespace());
+ }
+ }
+
+ // print the element name
+ *pout_ << '<' << xmlnsStack_.FormatQName(element->Name(), false);
+
+ // and the attributes
+ for (pattr = element->FirstAttr(); pattr; pattr = pattr->NextAttr()) {
+ *pout_ << ' ' << xmlnsStack_.FormatQName(pattr->Name(), true) << "=\"";
+ PrintQuotedValue(pattr->Value());
+ *pout_ << '"';
+ }
+
+ // and the extra xmlns declarations
+ std::vector<std::string>::iterator i(newXmlns.begin());
+ while (i < newXmlns.end()) {
+ if (*i == STR_EMPTY)
+ *pout_ << " xmlns=\"" << *(i + 1) << '"';
+ else
+ *pout_ << " xmlns:" << *i << "=\"" << *(i + 1) << '"';
+ i += 2;
+ }
+
+ // now the children
+ const XmlChild * pchild = element->FirstChild();
+
+ if (pchild == NULL)
+ *pout_ << "/>";
+ else {
+ *pout_ << '>';
+ while (pchild) {
+ if (pchild->IsText())
+ PrintBodyText(pchild->AsText()->Text());
+ else
+ PrintElement(pchild->AsElement());
+ pchild = pchild->NextChild();
+ }
+ *pout_ << "</" << xmlnsStack_.FormatQName(element->Name(), false) << '>';
+ }
+
+ xmlnsStack_.PopFrame();
+}
+
+void
+XmlPrinterImpl::PrintQuotedValue(const std::string & text) {
+ size_t safe = 0;
+ for (;;) {
+ size_t unsafe = text.find_first_of("<>&\"", safe);
+ if (unsafe == std::string::npos)
+ unsafe = text.length();
+ *pout_ << text.substr(safe, unsafe - safe);
+ if (unsafe == text.length())
+ return;
+ switch (text[unsafe]) {
+ case '<': *pout_ << "&lt;"; break;
+ case '>': *pout_ << "&gt;"; break;
+ case '&': *pout_ << "&amp;"; break;
+ case '"': *pout_ << "&quot;"; break;
+ }
+ safe = unsafe + 1;
+ if (safe == text.length())
+ return;
+ }
+}
+
+void
+XmlPrinterImpl::PrintBodyText(const std::string & text) {
+ size_t safe = 0;
+ for (;;) {
+ size_t unsafe = text.find_first_of("<>&", safe);
+ if (unsafe == std::string::npos)
+ unsafe = text.length();
+ *pout_ << text.substr(safe, unsafe - safe);
+ if (unsafe == text.length())
+ return;
+ switch (text[unsafe]) {
+ case '<': *pout_ << "&lt;"; break;
+ case '>': *pout_ << "&gt;"; break;
+ case '&': *pout_ << "&amp;"; break;
+ }
+ safe = unsafe + 1;
+ if (safe == text.length())
+ return;
+ }
+}
+
+
+}
diff --git a/Plugins/jingle/libjingle/talk/xmllite/xmlprinter.h b/Plugins/jingle/libjingle/talk/xmllite/xmlprinter.h
new file mode 100644
index 0000000..96900d0
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/xmllite/xmlprinter.h
@@ -0,0 +1,49 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _xmlprinter_h_
+#define _xmlprinter_h_
+
+#include <iosfwd>
+#include <string>
+#include "talk/base/scoped_ptr.h"
+
+namespace buzz {
+
+class XmlElement;
+
+class XmlPrinter {
+public:
+ static void PrintXml(std::ostream * pout, const XmlElement * pelt);
+
+ static void PrintXml(std::ostream * pout, const XmlElement * pelt,
+ const std::string * const xmlns, int xmlnsCount);
+};
+
+}
+
+#endif
diff --git a/Plugins/jingle/libjingle/talk/xmpp/asyncsocket.h b/Plugins/jingle/libjingle/talk/xmpp/asyncsocket.h
new file mode 100644
index 0000000..e4bce7f
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/xmpp/asyncsocket.h
@@ -0,0 +1,86 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _ASYNCSOCKET_H_
+#define _ASYNCSOCKET_H_
+
+#include "talk/base/sigslot.h"
+
+namespace talk_base {
+ class SocketAddress;
+}
+
+namespace buzz {
+
+class AsyncSocket {
+public:
+ enum State {
+ STATE_CLOSED = 0, //!< Socket is not open.
+ STATE_CLOSING, //!< Socket is closing but can have buffered data
+ STATE_CONNECTING, //!< In the process of
+ STATE_OPEN, //!< Socket is connected
+#if defined(FEATURE_ENABLE_SSL)
+ STATE_TLS_CONNECTING, //!< Establishing TLS connection
+ STATE_TLS_OPEN, //!< TLS connected
+#endif
+ };
+
+ enum Error {
+ ERROR_NONE = 0, //!< No error
+ ERROR_WINSOCK, //!< Winsock error
+ ERROR_DNS, //!< Couldn't resolve host name
+ ERROR_WRONGSTATE, //!< Call made while socket is in the wrong state
+#if defined(FEATURE_ENABLE_SSL)
+ ERROR_SSL, //!< Something went wrong with OpenSSL
+#endif
+ };
+
+ virtual ~AsyncSocket() {}
+ virtual State state() = 0;
+ virtual Error error() = 0;
+ virtual int GetError() = 0; // winsock error code
+
+ virtual bool Connect(const talk_base::SocketAddress& addr) = 0;
+ virtual bool Read(char * data, size_t len, size_t* len_read) = 0;
+ virtual bool Write(const char * data, size_t len) = 0;
+ virtual bool Close() = 0;
+#if defined(FEATURE_ENABLE_SSL)
+ // We allow matching any passed domain.
+ // If both names are passed as empty, we do not require a match.
+ virtual bool StartTls(const std::string & domainname) = 0;
+#endif
+
+ sigslot::signal0<> SignalConnected;
+ sigslot::signal0<> SignalSSLConnected;
+ sigslot::signal0<> SignalClosed;
+ sigslot::signal0<> SignalRead;
+ sigslot::signal0<> SignalError;
+};
+
+}
+
+#endif
diff --git a/Plugins/jingle/libjingle/talk/xmpp/constants.cc b/Plugins/jingle/libjingle/talk/xmpp/constants.cc
new file mode 100644
index 0000000..9388aae
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/xmpp/constants.cc
@@ -0,0 +1,398 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <string>
+#include "talk/base/basicdefs.h"
+#include "talk/xmllite/xmlconstants.h"
+#include "talk/xmllite/xmlelement.h"
+#include "talk/xmllite/qname.h"
+#include "talk/xmpp/jid.h"
+#include "talk/xmpp/constants.h"
+namespace buzz {
+
+const Jid JID_EMPTY(STR_EMPTY);
+
+const std::string & Constants::ns_client() {
+ static const std::string ns_client_("jabber:client");
+ return ns_client_;
+}
+
+const std::string & Constants::ns_server() {
+ static const std::string ns_server_("jabber:server");
+ return ns_server_;
+}
+
+const std::string & Constants::ns_stream() {
+ static const std::string ns_stream_("http://etherx.jabber.org/streams");
+ return ns_stream_;
+}
+
+const std::string & Constants::ns_xstream() {
+ static const std::string ns_xstream_("urn:ietf:params:xml:ns:xmpp-streams");
+ return ns_xstream_;
+}
+
+const std::string & Constants::ns_tls() {
+ static const std::string ns_tls_("urn:ietf:params:xml:ns:xmpp-tls");
+ return ns_tls_;
+}
+
+const std::string & Constants::ns_sasl() {
+ static const std::string ns_sasl_("urn:ietf:params:xml:ns:xmpp-sasl");
+ return ns_sasl_;
+}
+
+const std::string & Constants::ns_bind() {
+ static const std::string ns_bind_("urn:ietf:params:xml:ns:xmpp-bind");
+ return ns_bind_;
+}
+
+const std::string & Constants::ns_dialback() {
+ static const std::string ns_dialback_("jabber:server:dialback");
+ return ns_dialback_;
+}
+
+const std::string & Constants::ns_session() {
+ static const std::string ns_session_("urn:ietf:params:xml:ns:xmpp-session");
+ return ns_session_;
+}
+
+const std::string & Constants::ns_stanza() {
+ static const std::string ns_stanza_("urn:ietf:params:xml:ns:xmpp-stanzas");
+ return ns_stanza_;
+}
+
+const std::string & Constants::ns_privacy() {
+ static const std::string ns_privacy_("jabber:iq:privacy");
+ return ns_privacy_;
+}
+
+const std::string & Constants::ns_roster() {
+ static const std::string ns_roster_("jabber:iq:roster");
+ return ns_roster_;
+}
+
+const std::string & Constants::ns_vcard() {
+ static const std::string ns_vcard_("vcard-temp");
+ return ns_vcard_;
+}
+
+const std::string & Constants::ns_avatar_hash() {
+ static const std::string ns_avatar_hash_("google:avatar");
+ return ns_avatar_hash_;
+}
+
+const std::string & Constants::ns_vcard_update() {
+ static const std::string ns_vcard_update_("vcard-temp:x:update");
+ return ns_vcard_update_;
+}
+
+const std::string & Constants::str_client() {
+ static const std::string str_client_("client");
+ return str_client_;
+}
+
+const std::string & Constants::str_server() {
+ static const std::string str_server_("server");
+ return str_server_;
+}
+
+const std::string & Constants::str_stream() {
+ static const std::string str_stream_("stream");
+ return str_stream_;
+}
+
+const std::string STR_GET("get");
+const std::string STR_SET("set");
+const std::string STR_RESULT("result");
+const std::string STR_ERROR("error");
+
+
+const std::string STR_FROM("from");
+const std::string STR_TO("to");
+const std::string STR_BOTH("both");
+const std::string STR_REMOVE("remove");
+
+const std::string STR_UNAVAILABLE("unavailable");
+
+const std::string STR_GOOGLE_COM("google.com");
+const std::string STR_GMAIL_COM("gmail.com");
+const std::string STR_GOOGLEMAIL_COM("googlemail.com");
+const std::string STR_DEFAULT_DOMAIN("default.talk.google.com");
+const std::string STR_TALK_GOOGLE_COM("talk.google.com");
+const std::string STR_TALKX_L_GOOGLE_COM("talkx.l.google.com");
+
+const std::string STR_X("x");
+
+#ifdef FEATURE_ENABLE_VOICEMAIL
+const std::string STR_VOICEMAIL("voicemail");
+const std::string STR_OUTGOINGVOICEMAIL("outgoingvoicemail");
+#endif
+
+const QName QN_STREAM_STREAM(true, NS_STREAM, STR_STREAM);
+const QName QN_STREAM_FEATURES(true, NS_STREAM, "features");
+const QName QN_STREAM_ERROR(true, NS_STREAM, "error");
+
+const QName QN_XSTREAM_BAD_FORMAT(true, NS_XSTREAM, "bad-format");
+const QName QN_XSTREAM_BAD_NAMESPACE_PREFIX(true, NS_XSTREAM, "bad-namespace-prefix");
+const QName QN_XSTREAM_CONFLICT(true, NS_XSTREAM, "conflict");
+const QName QN_XSTREAM_CONNECTION_TIMEOUT(true, NS_XSTREAM, "connection-timeout");
+const QName QN_XSTREAM_HOST_GONE(true, NS_XSTREAM, "host-gone");
+const QName QN_XSTREAM_HOST_UNKNOWN(true, NS_XSTREAM, "host-unknown");
+const QName QN_XSTREAM_IMPROPER_ADDRESSIING(true, NS_XSTREAM, "improper-addressing");
+const QName QN_XSTREAM_INTERNAL_SERVER_ERROR(true, NS_XSTREAM, "internal-server-error");
+const QName QN_XSTREAM_INVALID_FROM(true, NS_XSTREAM, "invalid-from");
+const QName QN_XSTREAM_INVALID_ID(true, NS_XSTREAM, "invalid-id");
+const QName QN_XSTREAM_INVALID_NAMESPACE(true, NS_XSTREAM, "invalid-namespace");
+const QName QN_XSTREAM_INVALID_XML(true, NS_XSTREAM, "invalid-xml");
+const QName QN_XSTREAM_NOT_AUTHORIZED(true, NS_XSTREAM, "not-authorized");
+const QName QN_XSTREAM_POLICY_VIOLATION(true, NS_XSTREAM, "policy-violation");
+const QName QN_XSTREAM_REMOTE_CONNECTION_FAILED(true, NS_XSTREAM, "remote-connection-failed");
+const QName QN_XSTREAM_RESOURCE_CONSTRAINT(true, NS_XSTREAM, "resource-constraint");
+const QName QN_XSTREAM_RESTRICTED_XML(true, NS_XSTREAM, "restricted-xml");
+const QName QN_XSTREAM_SEE_OTHER_HOST(true, NS_XSTREAM, "see-other-host");
+const QName QN_XSTREAM_SYSTEM_SHUTDOWN(true, NS_XSTREAM, "system-shutdown");
+const QName QN_XSTREAM_UNDEFINED_CONDITION(true, NS_XSTREAM, "undefined-condition");
+const QName QN_XSTREAM_UNSUPPORTED_ENCODING(true, NS_XSTREAM, "unsupported-encoding");
+const QName QN_XSTREAM_UNSUPPORTED_STANZA_TYPE(true, NS_XSTREAM, "unsupported-stanza-type");
+const QName QN_XSTREAM_UNSUPPORTED_VERSION(true, NS_XSTREAM, "unsupported-version");
+const QName QN_XSTREAM_XML_NOT_WELL_FORMED(true, NS_XSTREAM, "xml-not-well-formed");
+const QName QN_XSTREAM_TEXT(true, NS_XSTREAM, "text");
+
+const QName QN_TLS_STARTTLS(true, NS_TLS, "starttls");
+const QName QN_TLS_REQUIRED(true, NS_TLS, "required");
+const QName QN_TLS_PROCEED(true, NS_TLS, "proceed");
+const QName QN_TLS_FAILURE(true, NS_TLS, "failure");
+
+const QName QN_SASL_MECHANISMS(true, NS_SASL, "mechanisms");
+const QName QN_SASL_MECHANISM(true, NS_SASL, "mechanism");
+const QName QN_SASL_AUTH(true, NS_SASL, "auth");
+const QName QN_SASL_CHALLENGE(true, NS_SASL, "challenge");
+const QName QN_SASL_RESPONSE(true, NS_SASL, "response");
+const QName QN_SASL_ABORT(true, NS_SASL, "abort");
+const QName QN_SASL_SUCCESS(true, NS_SASL, "success");
+const QName QN_SASL_FAILURE(true, NS_SASL, "failure");
+const QName QN_SASL_ABORTED(true, NS_SASL, "aborted");
+const QName QN_SASL_INCORRECT_ENCODING(true, NS_SASL, "incorrect-encoding");
+const QName QN_SASL_INVALID_AUTHZID(true, NS_SASL, "invalid-authzid");
+const QName QN_SASL_INVALID_MECHANISM(true, NS_SASL, "invalid-mechanism");
+const QName QN_SASL_MECHANISM_TOO_WEAK(true, NS_SASL, "mechanism-too-weak");
+const QName QN_SASL_NOT_AUTHORIZED(true, NS_SASL, "not-authorized");
+const QName QN_SASL_TEMPORARY_AUTH_FAILURE(true, NS_SASL, "temporary-auth-failure");
+
+const std::string NS_GOOGLE_AUTH("google:auth");
+const QName QN_MISSING_USERNAME(true, NS_GOOGLE_AUTH, "missing-username");
+
+const QName QN_DIALBACK_RESULT(true, NS_DIALBACK, "result");
+const QName QN_DIALBACK_VERIFY(true, NS_DIALBACK, "verify");
+
+const QName QN_STANZA_BAD_REQUEST(true, NS_STANZA, "bad-request");
+const QName QN_STANZA_CONFLICT(true, NS_STANZA, "conflict");
+const QName QN_STANZA_FEATURE_NOT_IMPLEMENTED(true, NS_STANZA, "feature-not-implemented");
+const QName QN_STANZA_FORBIDDEN(true, NS_STANZA, "forbidden");
+const QName QN_STANZA_GONE(true, NS_STANZA, "gone");
+const QName QN_STANZA_INTERNAL_SERVER_ERROR(true, NS_STANZA, "internal-server-error");
+const QName QN_STANZA_ITEM_NOT_FOUND(true, NS_STANZA, "item-not-found");
+const QName QN_STANZA_JID_MALFORMED(true, NS_STANZA, "jid-malformed");
+const QName QN_STANZA_NOT_ACCEPTABLE(true, NS_STANZA, "not-acceptable");
+const QName QN_STANZA_NOT_ALLOWED(true, NS_STANZA, "not-allowed");
+const QName QN_STANZA_PAYMENT_REQUIRED(true, NS_STANZA, "payment-required");
+const QName QN_STANZA_RECIPIENT_UNAVAILABLE(true, NS_STANZA, "recipient-unavailable");
+const QName QN_STANZA_REDIRECT(true, NS_STANZA, "redirect");
+const QName QN_STANZA_REGISTRATION_REQUIRED(true, NS_STANZA, "registration-required");
+const QName QN_STANZA_REMOTE_SERVER_NOT_FOUND(true, NS_STANZA, "remote-server-not-found");
+const QName QN_STANZA_REMOTE_SERVER_TIMEOUT(true, NS_STANZA, "remote-server-timeout");
+const QName QN_STANZA_RESOURCE_CONSTRAINT(true, NS_STANZA, "resource-constraint");
+const QName QN_STANZA_SERVICE_UNAVAILABLE(true, NS_STANZA, "service-unavailable");
+const QName QN_STANZA_SUBSCRIPTION_REQUIRED(true, NS_STANZA, "subscription-required");
+const QName QN_STANZA_UNDEFINED_CONDITION(true, NS_STANZA, "undefined-condition");
+const QName QN_STANZA_UNEXPECTED_REQUEST(true, NS_STANZA, "unexpected-request");
+const QName QN_STANZA_TEXT(true, NS_STANZA, "text");
+
+const QName QN_BIND_BIND(true, NS_BIND, "bind");
+const QName QN_BIND_RESOURCE(true, NS_BIND, "resource");
+const QName QN_BIND_JID(true, NS_BIND, "jid");
+
+const QName QN_MESSAGE(true, NS_CLIENT, "message");
+const QName QN_BODY(true, NS_CLIENT, "body");
+const QName QN_SUBJECT(true, NS_CLIENT, "subject");
+const QName QN_THREAD(true, NS_CLIENT, "thread");
+const QName QN_PRESENCE(true, NS_CLIENT, "presence");
+const QName QN_SHOW(true, NS_CLIENT, "show");
+const QName QN_STATUS(true, NS_CLIENT, "status");
+const QName QN_LANG(true, NS_CLIENT, "lang");
+const QName QN_PRIORITY(true, NS_CLIENT, "priority");
+const QName QN_IQ(true, NS_CLIENT, "iq");
+const QName QN_ERROR(true, NS_CLIENT, "error");
+
+const QName QN_SERVER_MESSAGE(true, NS_SERVER, "message");
+const QName QN_SERVER_BODY(true, NS_SERVER, "body");
+const QName QN_SERVER_SUBJECT(true, NS_SERVER, "subject");
+const QName QN_SERVER_THREAD(true, NS_SERVER, "thread");
+const QName QN_SERVER_PRESENCE(true, NS_SERVER, "presence");
+const QName QN_SERVER_SHOW(true, NS_SERVER, "show");
+const QName QN_SERVER_STATUS(true, NS_SERVER, "status");
+const QName QN_SERVER_LANG(true, NS_SERVER, "lang");
+const QName QN_SERVER_PRIORITY(true, NS_SERVER, "priority");
+const QName QN_SERVER_IQ(true, NS_SERVER, "iq");
+const QName QN_SERVER_ERROR(true, NS_SERVER, "error");
+
+const QName QN_SESSION_SESSION(true, NS_SESSION, "session");
+
+const QName QN_PRIVACY_QUERY(true, NS_PRIVACY, "query");
+const QName QN_PRIVACY_ACTIVE(true, NS_PRIVACY, "active");
+const QName QN_PRIVACY_DEFAULT(true, NS_PRIVACY, "default");
+const QName QN_PRIVACY_LIST(true, NS_PRIVACY, "list");
+const QName QN_PRIVACY_ITEM(true, NS_PRIVACY, "item");
+const QName QN_PRIVACY_IQ(true, NS_PRIVACY, "iq");
+const QName QN_PRIVACY_MESSAGE(true, NS_PRIVACY, "message");
+const QName QN_PRIVACY_PRESENCE_IN(true, NS_PRIVACY, "presence-in");
+const QName QN_PRIVACY_PRESENCE_OUT(true, NS_PRIVACY, "presence-out");
+
+const QName QN_ROSTER_QUERY(true, NS_ROSTER, "query");
+const QName QN_ROSTER_ITEM(true, NS_ROSTER, "item");
+const QName QN_ROSTER_GROUP(true, NS_ROSTER, "group");
+
+const QName QN_VCARD(true, NS_VCARD, "vCard");
+const QName QN_VCARD_FN(true, NS_VCARD, "FN");
+const QName QN_VCARD_PHOTO(true, NS_VCARD, "PHOTO");
+const QName QN_VCARD_PHOTO_BINVAL(true, NS_VCARD, "BINVAL");
+const QName QN_VCARD_AVATAR_HASH(true, NS_AVATAR_HASH, "hash");
+const QName QN_VCARD_AVATAR_HASH_MODIFIED(true, NS_AVATAR_HASH, "modified");
+
+const buzz::QName QN_NAME(true, STR_EMPTY, "name");
+const QName QN_XML_LANG(true, NS_XML, "lang");
+
+const std::string STR_TYPE("type");
+const std::string STR_ID("id");
+const std::string STR_NAME("name");
+const std::string STR_JID("jid");
+const std::string STR_SUBSCRIPTION("subscription");
+const std::string STR_ASK("ask");
+
+const QName QN_ENCODING(true, STR_EMPTY, STR_ENCODING);
+const QName QN_VERSION(true, STR_EMPTY, STR_VERSION);
+const QName QN_TO(true, STR_EMPTY, "to");
+const QName QN_FROM(true, STR_EMPTY, "from");
+const QName QN_TYPE(true, STR_EMPTY, "type");
+const QName QN_ID(true, STR_EMPTY, "id");
+const QName QN_CODE(true, STR_EMPTY, "code");
+
+const QName QN_VALUE(true, STR_EMPTY, "value");
+const QName QN_ACTION(true, STR_EMPTY, "action");
+const QName QN_ORDER(true, STR_EMPTY, "order");
+const QName QN_MECHANISM(true, STR_EMPTY, "mechanism");
+const QName QN_ASK(true, STR_EMPTY, "ask");
+const QName QN_JID(true, STR_EMPTY, "jid");
+const QName QN_SUBSCRIPTION(true, STR_EMPTY, "subscription");
+const QName QN_TITLE1(true, STR_EMPTY, "title1");
+const QName QN_TITLE2(true, STR_EMPTY, "title2");
+const QName QN_SOURCE(true, STR_EMPTY, "source");
+
+const QName QN_XMLNS_CLIENT(true, NS_XMLNS, STR_CLIENT);
+const QName QN_XMLNS_SERVER(true, NS_XMLNS, STR_SERVER);
+const QName QN_XMLNS_STREAM(true, NS_XMLNS, STR_STREAM);
+
+
+
+// Presence
+const std::string STR_SHOW_AWAY("away");
+const std::string STR_SHOW_CHAT("chat");
+const std::string STR_SHOW_DND("dnd");
+const std::string STR_SHOW_XA("xa");
+const std::string STR_SHOW_OFFLINE("offline");
+
+// Subscription
+const std::string STR_SUBSCRIBE("subscribe");
+const std::string STR_SUBSCRIBED("subscribed");
+const std::string STR_UNSUBSCRIBE("unsubscribe");
+const std::string STR_UNSUBSCRIBED("unsubscribed");
+
+
+// JEP 0030
+const QName QN_NODE(true, STR_EMPTY, "node");
+const QName QN_CATEGORY(true, STR_EMPTY, "category");
+const QName QN_VAR(true, STR_EMPTY, "var");
+const std::string NS_DISCO_INFO("http://jabber.org/protocol/disco#info");
+const std::string NS_DISCO_ITEMS("http://jabber.org/protocol/disco#items");
+const QName QN_DISCO_INFO_QUERY(true, NS_DISCO_INFO, "query");
+const QName QN_DISCO_IDENTITY(true, NS_DISCO_INFO, "identity");
+const QName QN_DISCO_FEATURE(true, NS_DISCO_INFO, "feature");
+
+const QName QN_DISCO_ITEMS_QUERY(true, NS_DISCO_ITEMS, "query");
+const QName QN_DISCO_ITEM(true, NS_DISCO_ITEMS, "item");
+
+
+// JEP 0115
+const std::string NS_CAPS("http://jabber.org/protocol/caps");
+const QName QN_CAPS_C(true, NS_CAPS, "c");
+const QName QN_VER(true, STR_EMPTY, "ver");
+const QName QN_EXT(true, STR_EMPTY, "ext");
+
+// JEP 0153
+const std::string kNSVCard("vcard-temp:x:update");
+const QName kQnVCardX(true, kNSVCard, "x");
+const QName kQnVCardPhoto(true, kNSVCard, "photo");
+
+// JEP 0172 User Nickname
+const std::string kNSNickname("http://jabber.org/protocol/nick");
+const QName kQnNickname(true, kNSNickname, "nick");
+
+
+// JEP 0085 chat state
+const std::string NS_CHATSTATE("http://jabber.org/protocol/chatstates");
+const QName QN_CS_ACTIVE(true, NS_CHATSTATE, "active");
+const QName QN_CS_COMPOSING(true, NS_CHATSTATE, "composing");
+const QName QN_CS_PAUSED(true, NS_CHATSTATE, "paused");
+const QName QN_CS_INACTIVE(true, NS_CHATSTATE, "inactive");
+const QName QN_CS_GONE(true, NS_CHATSTATE, "gone");
+
+// JEP 0091 Delayed Delivery
+const std::string kNSDelay("jabber:x:delay");
+const QName kQnDelayX(true, kNSDelay, "x");
+const QName kQnStamp(true, STR_EMPTY, "stamp");
+
+// Google time stamping (higher resolution)
+const std::string kNSTimestamp("google:timestamp");
+const QName kQnTime(true, kNSTimestamp, "time");
+const QName kQnMilliseconds(true, STR_EMPTY, "ms");
+
+
+
+// Jingle Info
+const std::string NS_JINGLE_INFO("google:jingleinfo");
+const QName QN_JINGLE_INFO_QUERY(true, NS_JINGLE_INFO, "query");
+const QName QN_JINGLE_INFO_STUN(true, NS_JINGLE_INFO, "stun");
+const QName QN_JINGLE_INFO_RELAY(true, NS_JINGLE_INFO, "relay");
+const QName QN_JINGLE_INFO_SERVER(true, NS_JINGLE_INFO, "server");
+const QName QN_JINGLE_INFO_TOKEN(true, NS_JINGLE_INFO, "token");
+const QName QN_JINGLE_INFO_HOST(true, STR_EMPTY, "host");
+const QName QN_JINGLE_INFO_TCP(true, STR_EMPTY, "tcp");
+const QName QN_JINGLE_INFO_UDP(true, STR_EMPTY, "udp");
+const QName QN_JINGLE_INFO_TCPSSL(true, STR_EMPTY, "tcpssl");
+
+}
diff --git a/Plugins/jingle/libjingle/talk/xmpp/constants.h b/Plugins/jingle/libjingle/talk/xmpp/constants.h
new file mode 100644
index 0000000..743e7ee
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/xmpp/constants.h
@@ -0,0 +1,358 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _CRICKET_XMPP_XMPPLIB_BUZZ_CONSTANTS_H_
+#define _CRICKET_XMPP_XMPPLIB_BUZZ_CONSTANTS_H_
+
+#include <string>
+#include "talk/xmllite/qname.h"
+#include "talk/xmpp/jid.h"
+
+
+#define NS_CLIENT STR_EMPTY // XXX Constants::ns_client()
+#define NS_SERVER Constants::ns_server()
+#define NS_STREAM Constants::ns_stream()
+#define NS_XSTREAM Constants::ns_xstream()
+#define NS_TLS Constants::ns_tls()
+#define NS_SASL Constants::ns_sasl()
+#define NS_BIND Constants::ns_bind()
+#define NS_DIALBACK Constants::ns_dialback()
+#define NS_SESSION Constants::ns_session()
+#define NS_STANZA Constants::ns_stanza()
+#define NS_PRIVACY Constants::ns_privacy()
+#define NS_ROSTER Constants::ns_roster()
+#define NS_VCARD Constants::ns_vcard()
+#define NS_AVATAR_HASH Constants::ns_avatar_hash()
+#define NS_VCARD_UPDATE Constants::ns_vcard_update()
+#define STR_CLIENT Constants::str_client()
+#define STR_SERVER Constants::str_server()
+#define STR_STREAM Constants::str_stream()
+
+
+namespace buzz {
+
+extern const Jid JID_EMPTY;
+
+class Constants {
+ public:
+ static const std::string & ns_client();
+ static const std::string & ns_server();
+ static const std::string & ns_stream();
+ static const std::string & ns_xstream();
+ static const std::string & ns_tls();
+ static const std::string & ns_sasl();
+ static const std::string & ns_bind();
+ static const std::string & ns_dialback();
+ static const std::string & ns_session();
+ static const std::string & ns_stanza();
+ static const std::string & ns_privacy();
+ static const std::string & ns_roster();
+ static const std::string & ns_vcard();
+ static const std::string & ns_avatar_hash();
+ static const std::string & ns_vcard_update();
+
+ static const std::string & str_client();
+ static const std::string & str_server();
+ static const std::string & str_stream();
+};
+
+extern const std::string STR_GET;
+extern const std::string STR_SET;
+extern const std::string STR_RESULT;
+extern const std::string STR_ERROR;
+
+
+extern const std::string STR_FROM;
+extern const std::string STR_TO;
+extern const std::string STR_BOTH;
+extern const std::string STR_REMOVE;
+
+extern const std::string STR_MESSAGE;
+extern const std::string STR_BODY;
+extern const std::string STR_PRESENCE;
+extern const std::string STR_STATUS;
+extern const std::string STR_SHOW;
+extern const std::string STR_PRIOIRTY;
+extern const std::string STR_IQ;
+
+extern const std::string STR_TYPE;
+extern const std::string STR_NAME;
+extern const std::string STR_ID;
+extern const std::string STR_JID;
+extern const std::string STR_SUBSCRIPTION;
+extern const std::string STR_ASK;
+extern const std::string STR_X;
+extern const std::string STR_GOOGLE_COM;
+extern const std::string STR_GMAIL_COM;
+extern const std::string STR_GOOGLEMAIL_COM;
+extern const std::string STR_DEFAULT_DOMAIN;
+extern const std::string STR_TALK_GOOGLE_COM;
+extern const std::string STR_TALKX_L_GOOGLE_COM;
+
+#ifdef FEATURE_ENABLE_VOICEMAIL
+extern const std::string STR_VOICEMAIL;
+extern const std::string STR_OUTGOINGVOICEMAIL;
+#endif
+
+extern const std::string STR_UNAVAILABLE;
+
+extern const QName QN_STREAM_STREAM;
+extern const QName QN_STREAM_FEATURES;
+extern const QName QN_STREAM_ERROR;
+
+extern const QName QN_XSTREAM_BAD_FORMAT;
+extern const QName QN_XSTREAM_BAD_NAMESPACE_PREFIX;
+extern const QName QN_XSTREAM_CONFLICT;
+extern const QName QN_XSTREAM_CONNECTION_TIMEOUT;
+extern const QName QN_XSTREAM_HOST_GONE;
+extern const QName QN_XSTREAM_HOST_UNKNOWN;
+extern const QName QN_XSTREAM_IMPROPER_ADDRESSIING;
+extern const QName QN_XSTREAM_INTERNAL_SERVER_ERROR;
+extern const QName QN_XSTREAM_INVALID_FROM;
+extern const QName QN_XSTREAM_INVALID_ID;
+extern const QName QN_XSTREAM_INVALID_NAMESPACE;
+extern const QName QN_XSTREAM_INVALID_XML;
+extern const QName QN_XSTREAM_NOT_AUTHORIZED;
+extern const QName QN_XSTREAM_POLICY_VIOLATION;
+extern const QName QN_XSTREAM_REMOTE_CONNECTION_FAILED;
+extern const QName QN_XSTREAM_RESOURCE_CONSTRAINT;
+extern const QName QN_XSTREAM_RESTRICTED_XML;
+extern const QName QN_XSTREAM_SEE_OTHER_HOST;
+extern const QName QN_XSTREAM_SYSTEM_SHUTDOWN;
+extern const QName QN_XSTREAM_UNDEFINED_CONDITION;
+extern const QName QN_XSTREAM_UNSUPPORTED_ENCODING;
+extern const QName QN_XSTREAM_UNSUPPORTED_STANZA_TYPE;
+extern const QName QN_XSTREAM_UNSUPPORTED_VERSION;
+extern const QName QN_XSTREAM_XML_NOT_WELL_FORMED;
+extern const QName QN_XSTREAM_TEXT;
+
+extern const QName QN_TLS_STARTTLS;
+extern const QName QN_TLS_REQUIRED;
+extern const QName QN_TLS_PROCEED;
+extern const QName QN_TLS_FAILURE;
+
+extern const QName QN_SASL_MECHANISMS;
+extern const QName QN_SASL_MECHANISM;
+extern const QName QN_SASL_AUTH;
+extern const QName QN_SASL_CHALLENGE;
+extern const QName QN_SASL_RESPONSE;
+extern const QName QN_SASL_ABORT;
+extern const QName QN_SASL_SUCCESS;
+extern const QName QN_SASL_FAILURE;
+extern const QName QN_SASL_ABORTED;
+extern const QName QN_SASL_INCORRECT_ENCODING;
+extern const QName QN_SASL_INVALID_AUTHZID;
+extern const QName QN_SASL_INVALID_MECHANISM;
+extern const QName QN_SASL_MECHANISM_TOO_WEAK;
+extern const QName QN_SASL_NOT_AUTHORIZED;
+extern const QName QN_SASL_TEMPORARY_AUTH_FAILURE;
+
+extern const std::string NS_GOOGLE_AUTH;
+extern const QName QN_MISSING_USERNAME;
+
+extern const QName QN_DIALBACK_RESULT;
+extern const QName QN_DIALBACK_VERIFY;
+
+extern const QName QN_STANZA_BAD_REQUEST;
+extern const QName QN_STANZA_CONFLICT;
+extern const QName QN_STANZA_FEATURE_NOT_IMPLEMENTED;
+extern const QName QN_STANZA_FORBIDDEN;
+extern const QName QN_STANZA_GONE;
+extern const QName QN_STANZA_INTERNAL_SERVER_ERROR;
+extern const QName QN_STANZA_ITEM_NOT_FOUND;
+extern const QName QN_STANZA_JID_MALFORMED;
+extern const QName QN_STANZA_NOT_ACCEPTABLE;
+extern const QName QN_STANZA_NOT_ALLOWED;
+extern const QName QN_STANZA_PAYMENT_REQUIRED;
+extern const QName QN_STANZA_RECIPIENT_UNAVAILABLE;
+extern const QName QN_STANZA_REDIRECT;
+extern const QName QN_STANZA_REGISTRATION_REQUIRED;
+extern const QName QN_STANZA_REMOTE_SERVER_NOT_FOUND;
+extern const QName QN_STANZA_REMOTE_SERVER_TIMEOUT;
+extern const QName QN_STANZA_RESOURCE_CONSTRAINT;
+extern const QName QN_STANZA_SERVICE_UNAVAILABLE;
+extern const QName QN_STANZA_SUBSCRIPTION_REQUIRED;
+extern const QName QN_STANZA_UNDEFINED_CONDITION;
+extern const QName QN_STANZA_UNEXPECTED_REQUEST;
+extern const QName QN_STANZA_TEXT;
+
+extern const QName QN_BIND_BIND;
+extern const QName QN_BIND_RESOURCE;
+extern const QName QN_BIND_JID;
+
+extern const QName QN_MESSAGE;
+extern const QName QN_BODY;
+extern const QName QN_SUBJECT;
+extern const QName QN_THREAD;
+extern const QName QN_PRESENCE;
+extern const QName QN_SHOW;
+extern const QName QN_STATUS;
+extern const QName QN_LANG;
+extern const QName QN_PRIORITY;
+extern const QName QN_IQ;
+extern const QName QN_ERROR;
+
+extern const QName QN_SERVER_MESSAGE;
+extern const QName QN_SERVER_BODY;
+extern const QName QN_SERVER_SUBJECT;
+extern const QName QN_SERVER_THREAD;
+extern const QName QN_SERVER_PRESENCE;
+extern const QName QN_SERVER_SHOW;
+extern const QName QN_SERVER_STATUS;
+extern const QName QN_SERVER_LANG;
+extern const QName QN_SERVER_PRIORITY;
+extern const QName QN_SERVER_IQ;
+extern const QName QN_SERVER_ERROR;
+
+extern const QName QN_SESSION_SESSION;
+
+extern const QName QN_PRIVACY_QUERY;
+extern const QName QN_PRIVACY_ACTIVE;
+extern const QName QN_PRIVACY_DEFAULT;
+extern const QName QN_PRIVACY_LIST;
+extern const QName QN_PRIVACY_ITEM;
+extern const QName QN_PRIVACY_IQ;
+extern const QName QN_PRIVACY_MESSAGE;
+extern const QName QN_PRIVACY_PRESENCE_IN;
+extern const QName QN_PRIVACY_PRESENCE_OUT;
+
+extern const QName QN_ROSTER_QUERY;
+extern const QName QN_ROSTER_ITEM;
+extern const QName QN_ROSTER_GROUP;
+
+extern const QName QN_VCARD;
+extern const QName QN_VCARD_FN;
+extern const QName QN_VCARD_PHOTO;
+extern const QName QN_VCARD_PHOTO_BINVAL;
+extern const QName QN_VCARD_AVATAR_HASH;
+extern const QName QN_VCARD_AVATAR_HASH_MODIFIED;
+
+
+extern const QName QN_XML_LANG;
+
+extern const QName QN_ENCODING;
+extern const QName QN_VERSION;
+extern const QName QN_TO;
+extern const QName QN_FROM;
+extern const QName QN_TYPE;
+extern const QName QN_ID;
+extern const QName QN_CODE;
+extern const QName QN_NAME;
+extern const QName QN_VALUE;
+extern const QName QN_ACTION;
+extern const QName QN_ORDER;
+extern const QName QN_MECHANISM;
+extern const QName QN_ASK;
+extern const QName QN_JID;
+extern const QName QN_SUBSCRIPTION;
+extern const QName QN_TITLE1;
+extern const QName QN_TITLE2;
+
+
+extern const QName QN_XMLNS_CLIENT;
+extern const QName QN_XMLNS_SERVER;
+extern const QName QN_XMLNS_STREAM;
+
+// Presence
+extern const std::string STR_SHOW_AWAY;
+extern const std::string STR_SHOW_CHAT;
+extern const std::string STR_SHOW_DND;
+extern const std::string STR_SHOW_XA;
+extern const std::string STR_SHOW_OFFLINE;
+
+// Subscription
+extern const std::string STR_SUBSCRIBE;
+extern const std::string STR_SUBSCRIBED;
+extern const std::string STR_UNSUBSCRIBE;
+extern const std::string STR_UNSUBSCRIBED;
+
+
+// JEP 0030
+extern const QName QN_NODE;
+extern const QName QN_CATEGORY;
+extern const QName QN_VAR;
+extern const std::string NS_DISCO_INFO;
+extern const std::string NS_DISCO_ITEMS;
+
+extern const QName QN_DISCO_INFO_QUERY;
+extern const QName QN_DISCO_IDENTITY;
+extern const QName QN_DISCO_FEATURE;
+
+extern const QName QN_DISCO_ITEMS_QUERY;
+extern const QName QN_DISCO_ITEM;
+
+
+// JEP 0115
+extern const std::string NS_CAPS;
+extern const QName QN_CAPS_C;
+extern const QName QN_VER;
+extern const QName QN_EXT;
+
+
+// Avatar - JEP 0153
+extern const std::string kNSVCard;
+extern const QName kQnVCardX;
+extern const QName kQnVCardPhoto;
+
+// JEP 0172 User Nickname
+extern const std::string kNSNickname;
+extern const QName kQnNickname;
+
+
+// JEP 0085 chat state
+extern const std::string NS_CHATSTATE;
+extern const QName QN_CS_ACTIVE;
+extern const QName QN_CS_COMPOSING;
+extern const QName QN_CS_PAUSED;
+extern const QName QN_CS_INACTIVE;
+extern const QName QN_CS_GONE;
+
+// JEP 0091 Delayed Delivery
+extern const std::string kNSDelay;
+extern const QName kQnDelayX;
+extern const QName kQnStamp;
+
+// Google time stamping (higher resolution)
+extern const std::string kNSTimestamp;
+extern const QName kQnTime;
+extern const QName kQnMilliseconds;
+
+
+extern const std::string NS_JINGLE_INFO;
+extern const QName QN_JINGLE_INFO_QUERY;
+extern const QName QN_JINGLE_INFO_STUN;
+extern const QName QN_JINGLE_INFO_RELAY;
+extern const QName QN_JINGLE_INFO_SERVER;
+extern const QName QN_JINGLE_INFO_TOKEN;
+extern const QName QN_JINGLE_INFO_HOST;
+extern const QName QN_JINGLE_INFO_TCP;
+extern const QName QN_JINGLE_INFO_UDP;
+extern const QName QN_JINGLE_INFO_TCPSSL;
+
+}
+
+#endif // _CRICKET_XMPP_XMPPLIB_BUZZ_CONSTANTS_H_
diff --git a/Plugins/jingle/libjingle/talk/xmpp/jid.cc b/Plugins/jingle/libjingle/talk/xmpp/jid.cc
new file mode 100644
index 0000000..ead2074
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/xmpp/jid.cc
@@ -0,0 +1,506 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+extern "C" {
+#include <ctype.h>
+}
+#include <string>
+#include "talk/xmpp/jid.h"
+#include "talk/xmpp/constants.h"
+#include "talk/base/common.h"
+#include <algorithm>
+#include "talk/base/logging.h"
+
+namespace buzz {
+
+static int AsciiToLower(int x) {
+ return (x <= 'Z' && x >= 'A') ? (x + ('a' - 'A')) : x;
+}
+
+Jid::Jid() : data_(NULL) {
+}
+
+Jid::Jid(bool is_special, const std::string & special) {
+ data_ = is_special ? new Data(special, STR_EMPTY, STR_EMPTY) : NULL;
+}
+
+Jid::Jid(const std::string & jid_string) {
+ if (jid_string == STR_EMPTY) {
+ data_ = NULL;
+ return;
+ }
+
+ // First find the slash and slice of that part
+ size_t slash = jid_string.find('/');
+ std::string resource_name = (slash == std::string::npos ? STR_EMPTY :
+ jid_string.substr(slash + 1));
+
+ // Now look for the node
+ std::string node_name;
+ size_t at = jid_string.find('@');
+ size_t domain_begin;
+ if (at < slash && at != std::string::npos) {
+ node_name = jid_string.substr(0, at);
+ domain_begin = at + 1;
+ } else {
+ domain_begin = 0;
+ }
+
+ // Now take what is left as the domain
+ size_t domain_length =
+ ( slash == std::string::npos
+ ? jid_string.length() - domain_begin
+ : slash - domain_begin);
+
+ // avoid allocating these constants repeatedly
+ std::string domain_name;
+
+ if (domain_length == 9 && jid_string.find("gmail.com", domain_begin) == domain_begin) {
+ domain_name = STR_GMAIL_COM;
+ }
+ else if (domain_length == 14 && jid_string.find("googlemail.com", domain_begin) == domain_begin) {
+ domain_name = STR_GOOGLEMAIL_COM;
+ }
+ else if (domain_length == 10 && jid_string.find("google.com", domain_begin) == domain_begin) {
+ domain_name = STR_GOOGLE_COM;
+ }
+ else {
+ domain_name = jid_string.substr(domain_begin, domain_length);
+ }
+
+ // If the domain is empty we have a non-valid jid and we should empty
+ // everything else out
+ if (domain_name.empty()) {
+ data_ = NULL;
+ return;
+ }
+
+ bool valid_node;
+ std::string validated_node = prepNode(node_name,
+ node_name.begin(), node_name.end(), &valid_node);
+ bool valid_domain;
+ std::string validated_domain = prepDomain(domain_name,
+ domain_name.begin(), domain_name.end(), &valid_domain);
+ bool valid_resource;
+ std::string validated_resource = prepResource(resource_name,
+ resource_name.begin(), resource_name.end(), &valid_resource);
+
+ if (!valid_node || !valid_domain || !valid_resource) {
+ data_ = NULL;
+ return;
+ }
+
+ data_ = new Data(validated_node, validated_domain, validated_resource);
+}
+
+Jid::Jid(const std::string & node_name,
+ const std::string & domain_name,
+ const std::string & resource_name) {
+ if (domain_name.empty()) {
+ data_ = NULL;
+ return;
+ }
+
+ bool valid_node;
+ std::string validated_node = prepNode(node_name,
+ node_name.begin(), node_name.end(), &valid_node);
+ bool valid_domain;
+ std::string validated_domain = prepDomain(domain_name,
+ domain_name.begin(), domain_name.end(), &valid_domain);
+ bool valid_resource;
+ std::string validated_resource = prepResource(resource_name,
+ resource_name.begin(), resource_name.end(), &valid_resource);
+
+ if (!valid_node || !valid_domain || !valid_resource) {
+ data_ = NULL;
+ return;
+ }
+
+ data_ = new Data(validated_node, validated_domain, validated_resource);
+}
+
+std::string Jid::Str() const {
+ if (!IsValid())
+ return STR_EMPTY;
+
+ std::string ret;
+
+ if (!data_->node_name_.empty())
+ ret = data_->node_name_ + "@";
+
+ ASSERT(data_->domain_name_ != STR_EMPTY);
+ ret += data_->domain_name_;
+
+ if (!data_->resource_name_.empty())
+ ret += "/" + data_->resource_name_;
+
+ return ret;
+}
+
+bool
+Jid::IsValid() const {
+ return data_ != NULL && !data_->domain_name_.empty();
+}
+
+bool
+Jid::IsBare() const {
+ if (Compare(JID_EMPTY) == 0) {
+ LOG(LS_VERBOSE) << "Warning: Calling IsBare() on the empty jid";
+ return true;
+ }
+ return IsValid() &&
+ data_->resource_name_.empty();
+}
+
+bool
+Jid::IsFull() const {
+ return IsValid() &&
+ !data_->resource_name_.empty();
+}
+
+Jid
+Jid::BareJid() const {
+ if (!IsValid())
+ return Jid();
+ if (!IsFull())
+ return *this;
+ return Jid(data_->node_name_, data_->domain_name_, STR_EMPTY);
+}
+
+#if 0
+void
+Jid::set_node(const std::string & node_name) {
+ data_->node_name_ = node_name;
+}
+void
+Jid::set_domain(const std::string & domain_name) {
+ data_->domain_name_ = domain_name;
+}
+void
+Jid::set_resource(const std::string & res_name) {
+ data_->resource_name_ = res_name;
+}
+#endif
+
+bool
+Jid::BareEquals(const Jid & other) const {
+ return (other.data_ == data_ ||
+ data_ != NULL &&
+ other.data_ != NULL &&
+ other.data_->node_name_ == data_->node_name_ &&
+ other.data_->domain_name_ == data_->domain_name_);
+}
+
+bool
+Jid::operator==(const Jid & other) const {
+ return (other.data_ == data_ ||
+ data_ != NULL &&
+ other.data_ != NULL &&
+ other.data_->node_name_ == data_->node_name_ &&
+ other.data_->domain_name_ == data_->domain_name_ &&
+ other.data_->resource_name_ == data_->resource_name_);
+}
+
+int
+Jid::Compare(const Jid & other) const {
+ if (other.data_ == data_)
+ return 0;
+ if (data_ == NULL)
+ return -1;
+ if (other.data_ == NULL)
+ return 1;
+
+ int compare_result;
+ compare_result = data_->node_name_.compare(other.data_->node_name_);
+ if (0 != compare_result)
+ return compare_result;
+ compare_result = data_->domain_name_.compare(other.data_->domain_name_);
+ if (0 != compare_result)
+ return compare_result;
+ compare_result = data_->resource_name_.compare(other.data_->resource_name_);
+ return compare_result;
+}
+
+uint32 Jid::ComputeLameHash() const {
+ uint32 hash = 0;
+ // Hash the node portion
+ {
+ const std::string &str = node();
+ for (int i = 0; i < static_cast<int>(str.size()); ++i) {
+ hash = ((hash << 2) + hash) + str[i];
+ }
+ }
+
+ // Hash the domain portion
+ {
+ const std::string &str = domain();
+ for (int i = 0; i < static_cast<int>(str.size()); ++i)
+ hash = ((hash << 2) + hash) + str[i];
+ }
+
+ // Hash the resource portion
+ {
+ const std::string &str = resource();
+ for (int i = 0; i < static_cast<int>(str.size()); ++i)
+ hash = ((hash << 2) + hash) + str[i];
+ }
+
+ return hash;
+}
+
+// --- JID parsing code: ---
+
+// Checks and normalizes the node part of a JID.
+std::string
+Jid::prepNode(const std::string str, std::string::const_iterator start,
+ std::string::const_iterator end, bool *valid) {
+ *valid = false;
+ std::string result;
+
+ for (std::string::const_iterator i = start; i < end; i++) {
+ bool char_valid = true;
+ unsigned char ch = *i;
+ if (ch <= 0x7F) {
+ result += prepNodeAscii(ch, &char_valid);
+ }
+ else {
+ // TODO: implement the correct stringprep protocol for these
+ result += tolower(ch);
+ }
+ if (!char_valid) {
+ return STR_EMPTY;
+ }
+ }
+
+ if (result.length() > 1023) {
+ return STR_EMPTY;
+ }
+ *valid = true;
+ return result;
+}
+
+
+// Returns the appropriate mapping for an ASCII character in a node.
+char
+Jid::prepNodeAscii(char ch, bool *valid) {
+ *valid = true;
+ switch (ch) {
+ case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
+ case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N':
+ case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U':
+ case 'V': case 'W': case 'X': case 'Y': case 'Z':
+ return (char)(ch + ('a' - 'A'));
+
+ case 0x00: case 0x01: case 0x02: case 0x03: case 0x04: case 0x05:
+ case 0x06: case 0x07: case 0x08: case 0x09: case 0x0A: case 0x0B:
+ case 0x0C: case 0x0D: case 0x0E: case 0x0F: case 0x10: case 0x11:
+ case 0x12: case 0x13: case 0x14: case 0x15: case 0x16: case 0x17:
+ case ' ': case '&': case '/': case ':': case '<': case '>': case '@':
+ case '\"': case '\'':
+ case 0x7F:
+ *valid = false;
+ return 0;
+
+ default:
+ return ch;
+ }
+}
+
+
+// Checks and normalizes the resource part of a JID.
+std::string
+Jid::prepResource(const std::string str, std::string::const_iterator start,
+ std::string::const_iterator end, bool *valid) {
+ *valid = false;
+ std::string result;
+
+ for (std::string::const_iterator i = start; i < end; i++) {
+ bool char_valid = true;
+ unsigned char ch = *i;
+ if (ch <= 0x7F) {
+ result += prepResourceAscii(ch, &char_valid);
+ }
+ else {
+ // TODO: implement the correct stringprep protocol for these
+ result += ch;
+ }
+ }
+
+ if (result.length() > 1023) {
+ return STR_EMPTY;
+ }
+ *valid = true;
+ return result;
+}
+
+// Returns the appropriate mapping for an ASCII character in a resource.
+char
+Jid::prepResourceAscii(char ch, bool *valid) {
+ *valid = true;
+ switch (ch) {
+ case 0x00: case 0x01: case 0x02: case 0x03: case 0x04: case 0x05:
+ case 0x06: case 0x07: case 0x08: case 0x09: case 0x0A: case 0x0B:
+ case 0x0C: case 0x0D: case 0x0E: case 0x0F: case 0x10: case 0x11:
+ case 0x12: case 0x13: case 0x14: case 0x15: case 0x16: case 0x17:
+ case 0x7F:
+ *valid = false;
+ return 0;
+
+ default:
+ return ch;
+ }
+}
+
+// Checks and normalizes the domain part of a JID.
+std::string
+Jid::prepDomain(const std::string str, std::string::const_iterator start,
+ std::string::const_iterator end, bool *valid) {
+ *valid = false;
+ std::string result;
+
+ // TODO: if the domain contains a ':', then we should parse it
+ // as an IPv6 address rather than giving an error about illegal domain.
+ prepDomain(str, start, end, &result, valid);
+ if (!*valid) {
+ return STR_EMPTY;
+ }
+
+ if (result.length() > 1023) {
+ return STR_EMPTY;
+ }
+ *valid = true;
+ return result;
+}
+
+
+// Checks and normalizes an IDNA domain.
+void
+Jid::prepDomain(const std::string str, std::string::const_iterator start,
+ std::string::const_iterator end, std::string *buf, bool *valid) {
+ *valid = false;
+ std::string::const_iterator last = start;
+ for (std::string::const_iterator i = start; i < end; i++) {
+ bool label_valid = true;
+ char ch = *i;
+ switch (ch) {
+ case 0x002E:
+#if 0 // FIX: This isn't UTF-8-aware.
+ case 0x3002:
+ case 0xFF0E:
+ case 0xFF61:
+#endif
+ prepDomainLabel(str, last, i, buf, &label_valid);
+ *buf += '.';
+ last = i + 1;
+ break;
+ }
+ if (!label_valid) {
+ return;
+ }
+ }
+ prepDomainLabel(str, last, end, buf, valid);
+}
+
+// Checks and normalizes a domain label.
+void
+Jid::prepDomainLabel(const std::string str, std::string::const_iterator start,
+ std::string::const_iterator end, std::string *buf, bool *valid) {
+ *valid = false;
+
+ int startLen = buf->length();
+ for (std::string::const_iterator i = start; i < end; i++) {
+ bool char_valid = true;
+ unsigned char ch = *i;
+ if (ch <= 0x7F) {
+ *buf += prepDomainLabelAscii(ch, &char_valid);
+ }
+ else {
+ // TODO: implement ToASCII for these
+ *buf += ch;
+ }
+ if (!char_valid) {
+ return;
+ }
+ }
+
+ int count = buf->length() - startLen;
+ if (count == 0) {
+ return;
+ }
+ else if (count > 63) {
+ return;
+ }
+
+ // Is this check needed? See comment in prepDomainLabelAscii.
+ if ((*buf)[startLen] == '-') {
+ return;
+ }
+ if ((*buf)[buf->length() - 1] == '-') {
+ return;
+ }
+ *valid = true;
+}
+
+
+// Returns the appropriate mapping for an ASCII character in a domain label.
+char
+Jid::prepDomainLabelAscii(char ch, bool *valid) {
+ *valid = true;
+ // TODO: A literal reading of the spec seems to say that we do
+ // not need to check for these illegal characters (an "internationalized
+ // domain label" runs ToASCII with UseSTD3... set to false). But that
+ // can't be right. We should at least be checking that there are no '/'
+ // or '@' characters in the domain. Perhaps we should see what others
+ // do in this case.
+
+ switch (ch) {
+ case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
+ case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N':
+ case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U':
+ case 'V': case 'W': case 'X': case 'Y': case 'Z':
+ return (char)(ch + ('a' - 'A'));
+
+ case 0x00: case 0x01: case 0x02: case 0x03: case 0x04: case 0x05:
+ case 0x06: case 0x07: case 0x08: case 0x09: case 0x0A: case 0x0B:
+ case 0x0C: case 0x0D: case 0x0E: case 0x0F: case 0x10: case 0x11:
+ case 0x12: case 0x13: case 0x14: case 0x15: case 0x16: case 0x17:
+ case 0x18: case 0x19: case 0x1A: case 0x1B: case 0x1C: case 0x1D:
+ case 0x1E: case 0x1F: case 0x20: case 0x21: case 0x22: case 0x23:
+ case 0x24: case 0x25: case 0x26: case 0x27: case 0x28: case 0x29:
+ case 0x2A: case 0x2B: case 0x2C: case 0x2E: case 0x2F: case 0x3A:
+ case 0x3B: case 0x3C: case 0x3D: case 0x3E: case 0x3F: case 0x40:
+ case 0x5B: case 0x5C: case 0x5D: case 0x5E: case 0x5F: case 0x60:
+ case 0x7B: case 0x7C: case 0x7D: case 0x7E: case 0x7F:
+ *valid = false;
+ return 0;
+
+ default:
+ return ch;
+ }
+}
+
+}
diff --git a/Plugins/jingle/libjingle/talk/xmpp/jid.h b/Plugins/jingle/libjingle/talk/xmpp/jid.h
new file mode 100644
index 0000000..6831bda
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/xmpp/jid.h
@@ -0,0 +1,148 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef _jid_h_
+#define _jid_h_
+
+#include <string>
+#include "talk/base/basictypes.h"
+#include "talk/xmllite/xmlconstants.h"
+
+namespace buzz {
+
+//! The Jid class encapsulates and provides parsing help for Jids
+//! A Jid consists of three parts. The node, the domain and the resource.
+//!
+//! node@domain/resource
+//!
+//! The node and resource are both optional. A valid jid is defined to have
+//! a domain. A bare jid is defined to not have a resource and a full jid
+//! *does* have a resource.
+class Jid {
+public:
+ explicit Jid();
+ explicit Jid(const std::string & jid_string);
+ explicit Jid(const std::string & node_name,
+ const std::string & domain_name,
+ const std::string & resource_name);
+ explicit Jid(bool special, const std::string & special_string);
+ Jid(const Jid & jid) : data_(jid.data_) {
+ if (data_ != NULL) {
+ data_->AddRef();
+ }
+ }
+ Jid & operator=(const Jid & jid) {
+ if (jid.data_ != NULL) {
+ jid.data_->AddRef();
+ }
+ if (data_ != NULL) {
+ data_->Release();
+ }
+ data_ = jid.data_;
+ return *this;
+ }
+ ~Jid() {
+ if (data_ != NULL) {
+ data_->Release();
+ }
+ }
+
+
+ const std::string & node() const { return !data_ ? STR_EMPTY : data_->node_name_; }
+ // void set_node(const std::string & node_name);
+ const std::string & domain() const { return !data_ ? STR_EMPTY : data_->domain_name_; }
+ // void set_domain(const std::string & domain_name);
+ const std::string & resource() const { return !data_ ? STR_EMPTY : data_->resource_name_; }
+ // void set_resource(const std::string & res_name);
+
+ std::string Str() const;
+ Jid BareJid() const;
+
+ bool IsValid() const;
+ bool IsBare() const;
+ bool IsFull() const;
+
+ bool BareEquals(const Jid & other) const;
+
+ bool operator==(const Jid & other) const;
+ bool operator!=(const Jid & other) const { return !operator==(other); }
+
+ bool operator<(const Jid & other) const { return Compare(other) < 0; };
+ bool operator>(const Jid & other) const { return Compare(other) > 0; };
+
+ int Compare(const Jid & other) const;
+
+ // A quick and dirty hash. Don't count on this producing a great
+ // distribution.
+ uint32 ComputeLameHash() const;
+
+private:
+
+ static std::string prepNode(const std::string str,
+ std::string::const_iterator start, std::string::const_iterator end,
+ bool *valid);
+ static char prepNodeAscii(char ch, bool *valid);
+ static std::string prepResource(const std::string str,
+ std::string::const_iterator start, std::string::const_iterator end,
+ bool *valid);
+ static char prepResourceAscii(char ch, bool *valid);
+ static std::string prepDomain(const std::string str,
+ std::string::const_iterator start, std::string::const_iterator end,
+ bool *valid);
+ static void prepDomain(const std::string str,
+ std::string::const_iterator start, std::string::const_iterator end,
+ std::string *buf, bool *valid);
+ static void prepDomainLabel(const std::string str,
+ std::string::const_iterator start, std::string::const_iterator end,
+ std::string *buf, bool *valid);
+ static char prepDomainLabelAscii(char ch, bool *valid);
+
+ class Data {
+ public:
+ Data() : refcount_(1) {}
+ Data(const std::string & node, const std::string &domain, const std::string & resource) :
+ node_name_(node),
+ domain_name_(domain),
+ resource_name_(resource),
+ refcount_(1) {}
+ const std::string node_name_;
+ const std::string domain_name_;
+ const std::string resource_name_;
+
+ void AddRef() { refcount_++; }
+ void Release() { if (!--refcount_) delete this; }
+ private:
+ int refcount_;
+ };
+
+ Data * data_;
+};
+
+}
+
+
+
+#endif
diff --git a/Plugins/jingle/libjingle/talk/xmpp/plainsaslhandler.h b/Plugins/jingle/libjingle/talk/xmpp/plainsaslhandler.h
new file mode 100644
index 0000000..e7d44b9
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/xmpp/plainsaslhandler.h
@@ -0,0 +1,80 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _PLAINSASLHANDLER_H_
+#define _PLAINSASLHANDLER_H_
+
+#include "talk/xmpp/saslhandler.h"
+#include <algorithm>
+
+namespace buzz {
+
+class PlainSaslHandler : public SaslHandler {
+public:
+ PlainSaslHandler(const Jid & jid, const talk_base::CryptString & password,
+ bool allow_plain) : jid_(jid), password_(password),
+ allow_plain_(allow_plain) {}
+
+ virtual ~PlainSaslHandler() {}
+
+ // Should pick the best method according to this handler
+ // returns the empty string if none are suitable
+ virtual std::string ChooseBestSaslMechanism(const std::vector<std::string> & mechanisms, bool encrypted) {
+
+ if (!encrypted && !allow_plain_) {
+ return "";
+ }
+
+ std::vector<std::string>::const_iterator it = std::find(mechanisms.begin(), mechanisms.end(), "PLAIN");
+ if (it == mechanisms.end()) {
+ return "";
+ }
+ else {
+ return "PLAIN";
+ }
+ }
+
+ // Creates a SaslMechanism for the given mechanism name (you own it
+ // once you get it). If not handled, return NULL.
+ virtual SaslMechanism * CreateSaslMechanism(const std::string & mechanism) {
+ if (mechanism == "PLAIN") {
+ return new SaslPlainMechanism(jid_, password_);
+ }
+ return NULL;
+ }
+
+private:
+ Jid jid_;
+ talk_base::CryptString password_;
+ bool allow_plain_;
+};
+
+
+}
+
+#endif
+
diff --git a/Plugins/jingle/libjingle/talk/xmpp/prexmppauth.h b/Plugins/jingle/libjingle/talk/xmpp/prexmppauth.h
new file mode 100644
index 0000000..f94bd3d
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/xmpp/prexmppauth.h
@@ -0,0 +1,86 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _PREXMPPAUTH_H_
+#define _PREXMPPAUTH_H_
+
+#include "talk/base/cryptstring.h"
+#include "talk/base/sigslot.h"
+#include "talk/xmpp/saslhandler.h"
+
+namespace talk_base {
+ class SocketAddress;
+}
+
+namespace buzz {
+
+class Jid;
+class SaslMechanism;
+
+class CaptchaChallenge {
+ public:
+ CaptchaChallenge() : captcha_needed_(false) {}
+ CaptchaChallenge(const std::string& token, const std::string& url)
+ : captcha_needed_(true), captcha_token_(token), captcha_image_url_(url) {
+ }
+
+ bool captcha_needed() const { return captcha_needed_; }
+ const std::string& captcha_token() const { return captcha_token_; }
+
+ // This url is relative to the gaia server. Once we have better tools
+ // for cracking URLs, we should probably make this a full URL
+ const std::string& captcha_image_url() const { return captcha_image_url_; }
+
+ private:
+ bool captcha_needed_;
+ std::string captcha_token_;
+ std::string captcha_image_url_;
+};
+
+class PreXmppAuth : public SaslHandler {
+public:
+ virtual ~PreXmppAuth() {}
+
+ virtual void StartPreXmppAuth(
+ const Jid & jid,
+ const talk_base::SocketAddress & server,
+ const talk_base::CryptString & pass,
+ const std::string & auth_cookie) = 0;
+
+ sigslot::signal0<> SignalAuthDone;
+
+ virtual bool IsAuthDone() = 0;
+ virtual bool IsAuthorized() = 0;
+ virtual bool HadError() = 0;
+ virtual int GetError() = 0;
+ virtual CaptchaChallenge GetCaptchaChallenge() = 0;
+ virtual std::string GetAuthCookie() = 0;
+};
+
+}
+
+#endif
diff --git a/Plugins/jingle/libjingle/talk/xmpp/ratelimitmanager.cc b/Plugins/jingle/libjingle/talk/xmpp/ratelimitmanager.cc
new file mode 100644
index 0000000..81c55ac
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/xmpp/ratelimitmanager.cc
@@ -0,0 +1,77 @@
+/*
+ * libjingle
+ * Copyright 2004--2006, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "talk/xmpp/ratelimitmanager.h"
+
+namespace buzz {
+
+RateLimitManager::RateLimit* RateLimitManager::GetRateLimit(
+ const std::string event_name) {
+ RateLimitMap::iterator it = rate_limits_.find(event_name);
+ if (it != rate_limits_.end()) {
+ return it->second;
+ }
+ return NULL;
+}
+
+bool RateLimitManager::IsWithinRateLimit(const std::string event_name) {
+ RateLimit* current_rate = GetRateLimit(event_name);
+ if (current_rate) {
+ return current_rate->IsWithinRateLimit();
+ }
+ return true; // If no rate limit is set, then you must be under the limit
+}
+
+void RateLimitManager::UpdateRateLimit(const std::string event_name,
+ int max_count,
+ int per_x_seconds) {
+ RateLimit* current_rate = GetRateLimit(event_name);
+ if (!current_rate) {
+ current_rate = new RateLimit(max_count, per_x_seconds);
+ rate_limits_[event_name] = current_rate;
+ }
+ current_rate->UpdateRateLimit();
+}
+
+bool RateLimitManager::VerifyRateLimit(const std::string event_name,
+ int max_count,
+ int per_x_seconds) {
+ return VerifyRateLimit(event_name, max_count, per_x_seconds, false);
+}
+
+bool RateLimitManager::VerifyRateLimit(const std::string event_name,
+ int max_count,
+ int per_x_seconds,
+ bool always_update) {
+ bool within_rate_limit = IsWithinRateLimit(event_name);
+ if (within_rate_limit || always_update) {
+ UpdateRateLimit(event_name, max_count, per_x_seconds);
+ }
+ return within_rate_limit;
+}
+
+}
diff --git a/Plugins/jingle/libjingle/talk/xmpp/ratelimitmanager.h b/Plugins/jingle/libjingle/talk/xmpp/ratelimitmanager.h
new file mode 100644
index 0000000..79960d8
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/xmpp/ratelimitmanager.h
@@ -0,0 +1,146 @@
+/*
+ * libjingle
+ * Copyright 2004--2006, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _RATELIMITMANAGER_H_
+#define _RATELIMITMANAGER_H_
+
+#include "talk/base/time.h"
+#include "talk/base/taskrunner.h"
+#include <map>
+
+namespace buzz {
+
+/////////////////////////////////////////////////////////////////////
+//
+// RATELIMITMANAGER
+//
+/////////////////////////////////////////////////////////////////////
+//
+// RateLimitManager imposes client-side rate limiting for xmpp tasks and
+// other events. It ensures that no more than i events with a given name
+// can occur within k seconds.
+//
+// A buffer tracks the previous max_count events. Before an event is allowed
+// to occur, it can check its rate limit with a call to VerifyRateLimit.
+// VerifyRateLimit will look up the i-th to last event and if more than
+// k seconds have passed since then, it will return true and update the
+// appropriate rate limits. Else, it will return false.
+//
+/////////////////////////////////////////////////////////////////////
+
+class RateLimitManager {
+ public:
+
+ RateLimitManager() { };
+ ~RateLimitManager() {
+ for (RateLimitMap::iterator it = rate_limits_.begin();
+ it != rate_limits_.end(); ++it) {
+ delete it->second;
+ }
+ };
+
+ // Checks if the event is under the defined rate limit and updates the
+ // rate limit if so. Returns true if it's under the rate limit.
+ bool VerifyRateLimit(const std::string event_name, int max_count,
+ int per_x_seconds);
+
+ // Checks if the event is under the defined rate limit and updates the
+ // rate limit if so *or* if always_update = true.
+ bool VerifyRateLimit(const std::string event_name, int max_count,
+ int per_x_seconds, bool always_update);
+
+ private:
+ class RateLimit {
+ public:
+ RateLimit(int max, int per_x_secs) : counter_(0), max_count_(max),
+ per_x_seconds_(per_x_secs) {
+ event_times_ = new uint32[max_count_];
+ for (int i = 0; i < max_count_; i++) {
+ event_times_[i] = 0;
+ }
+ }
+
+ ~RateLimit() {
+ if (event_times_) {
+ delete[] event_times_;
+ }
+ }
+
+ // True iff the current time >= to the next song allowed time
+ bool IsWithinRateLimit() {
+ uint32 current_time = talk_base::Time();
+ if (talk_base::TimeDiff(current_time, NextTimeAllowedForCounter()) >= 0) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ // Updates time and counter for rate limit
+ void UpdateRateLimit() {
+ event_times_[counter_] = talk_base::Time();
+ counter_ = (counter_ + 1) % max_count_;
+ }
+
+ private:
+
+ // The time at which the i-th (where i = max_count) event occured
+ uint32 PreviousTimeAtCounter() {
+ return event_times_[counter_];
+ }
+
+ // The time that the next event is allowed to occur
+ uint32 NextTimeAllowedForCounter() {
+ return PreviousTimeAtCounter() + per_x_seconds_ * talk_base::kSecToMsec;
+ }
+
+ int counter_; // count modulo max_count of the current event
+ int max_count_; // max number of events that can occur within per_x_seconds
+ int per_x_seconds_; // interval size for rate limit
+ uint32* event_times_; // buffer of previous max_count event
+ };
+
+ typedef std::map<const std::string, RateLimit*> RateLimitMap;
+
+ // Maps from event name to its rate limit
+ RateLimitMap rate_limits_;
+
+ // Returns rate limit for event with specified name
+ RateLimit* GetRateLimit(const std::string event_name);
+
+ // True iff the current time >= to the next song allowed time
+ bool IsWithinRateLimit(const std::string event_name);
+
+ // Updates time and counter for rate limit
+ void UpdateRateLimit(const std::string event_name, int max_count,
+ int per_x_seconds);
+
+};
+
+}
+
+#endif //_RATELIMITMANAGER_H_
diff --git a/Plugins/jingle/libjingle/talk/xmpp/saslcookiemechanism.h b/Plugins/jingle/libjingle/talk/xmpp/saslcookiemechanism.h
new file mode 100644
index 0000000..a6630d9
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/xmpp/saslcookiemechanism.h
@@ -0,0 +1,67 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _SASLCOOKIEMECHANISM_H_
+#define _SASLCOOKIEMECHANISM_H_
+
+#include "talk/xmpp/saslmechanism.h"
+#include "talk/xmllite/xmlelement.h"
+#include "talk/xmpp/constants.h"
+
+namespace buzz {
+
+class SaslCookieMechanism : public SaslMechanism {
+
+public:
+ SaslCookieMechanism(const std::string & mechanism, const std::string & username, const std::string & cookie) :
+ mechanism_(mechanism), username_(username), cookie_(cookie) {}
+
+ virtual std::string GetMechanismName() { return mechanism_; }
+
+ virtual XmlElement * StartSaslAuth() {
+ // send initial request
+ XmlElement * el = new XmlElement(QN_SASL_AUTH, true);
+ el->AddAttr(QN_MECHANISM, mechanism_);
+
+ std::string credential;
+ credential.append("\0", 1);
+ credential.append(username_);
+ credential.append("\0", 1);
+ credential.append(cookie_);
+ el->AddText(Base64Encode(credential));
+ return el;
+ }
+
+private:
+ std::string mechanism_;
+ std::string username_;
+ std::string cookie_;
+};
+
+}
+
+#endif
diff --git a/Plugins/jingle/libjingle/talk/xmpp/saslhandler.h b/Plugins/jingle/libjingle/talk/xmpp/saslhandler.h
new file mode 100644
index 0000000..b57d3ba
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/xmpp/saslhandler.h
@@ -0,0 +1,59 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _SASLHANDLER_H_
+#define _SASLHANDLER_H_
+
+#include <string>
+
+namespace buzz {
+
+class XmlElement;
+class SaslMechanism;
+
+// Creates mechanisms to deal with a given mechanism
+class SaslHandler {
+
+public:
+
+ // Intended to be subclassed
+ virtual ~SaslHandler() {}
+
+ // Should pick the best method according to this handler
+ // returns the empty string if none are suitable
+ virtual std::string ChooseBestSaslMechanism(const std::vector<std::string> & mechanisms, bool encrypted) = 0;
+
+ // Creates a SaslMechanism for the given mechanism name (you own it
+ // once you get it).
+ // If not handled, return NULL.
+ virtual SaslMechanism * CreateSaslMechanism(const std::string & mechanism) = 0;
+};
+
+}
+
+#endif
+
diff --git a/Plugins/jingle/libjingle/talk/xmpp/saslmechanism.cc b/Plugins/jingle/libjingle/talk/xmpp/saslmechanism.cc
new file mode 100644
index 0000000..45c947a
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/xmpp/saslmechanism.cc
@@ -0,0 +1,70 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "talk/base/base64.h"
+#include "talk/xmllite/xmlelement.h"
+#include "talk/xmpp/constants.h"
+#include "talk/xmpp/saslmechanism.h"
+
+using talk_base::Base64;
+
+namespace buzz {
+
+XmlElement *
+SaslMechanism::StartSaslAuth() {
+ return new XmlElement(QN_SASL_AUTH, true);
+}
+
+XmlElement *
+SaslMechanism::HandleSaslChallenge(const XmlElement * challenge) {
+ return new XmlElement(QN_SASL_ABORT, true);
+}
+
+void
+SaslMechanism::HandleSaslSuccess(const XmlElement * success) {
+}
+
+void
+SaslMechanism::HandleSaslFailure(const XmlElement * failure) {
+}
+
+std::string
+SaslMechanism::Base64Encode(const std::string & plain) {
+ return Base64::encode(plain);
+}
+
+std::string
+SaslMechanism::Base64Decode(const std::string & encoded) {
+ return Base64::decode(encoded);
+}
+
+std::string
+SaslMechanism::Base64EncodeFromArray(const char * plain, size_t length) {
+ return Base64::encodeFromArray(plain, length);
+}
+
+}
diff --git a/Plugins/jingle/libjingle/talk/xmpp/saslmechanism.h b/Plugins/jingle/libjingle/talk/xmpp/saslmechanism.h
new file mode 100644
index 0000000..f2e5adc
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/xmpp/saslmechanism.h
@@ -0,0 +1,74 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _SASLMECHANISM_H_
+#define _SASLMECHANISM_H_
+
+#include <string>
+
+namespace buzz {
+
+class XmlElement;
+
+
+// Defines a mechnanism to do SASL authentication.
+// Subclass instances should have a self-contained way to present
+// credentials.
+class SaslMechanism {
+
+public:
+
+ // Intended to be subclassed
+ virtual ~SaslMechanism() {}
+
+ // Should return the name of the SASL mechanism, e.g., "PLAIN"
+ virtual std::string GetMechanismName() = 0;
+
+ // Should generate the initial "auth" request. Default is just <auth/>.
+ virtual XmlElement * StartSaslAuth();
+
+ // Should respond to a SASL "<challenge>" request. Default is
+ // to abort (for mechanisms that do not do challenge-response)
+ virtual XmlElement * HandleSaslChallenge(const XmlElement * challenge);
+
+ // Notification of a SASL "<success>". Sometimes information
+ // is passed on success.
+ virtual void HandleSaslSuccess(const XmlElement * success);
+
+ // Notification of a SASL "<failure>". Sometimes information
+ // for the user is passed on failure.
+ virtual void HandleSaslFailure(const XmlElement * failure);
+
+protected:
+ static std::string Base64Encode(const std::string & plain);
+ static std::string Base64Decode(const std::string & encoded);
+ static std::string Base64EncodeFromArray(const char * plain, size_t length);
+};
+
+}
+
+#endif
diff --git a/Plugins/jingle/libjingle/talk/xmpp/saslplainmechanism.h b/Plugins/jingle/libjingle/talk/xmpp/saslplainmechanism.h
new file mode 100644
index 0000000..72532e6
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/xmpp/saslplainmechanism.h
@@ -0,0 +1,65 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _SASLPLAINMECHANISM_H_
+#define _SASLPLAINMECHANISM_H_
+
+#include "talk/base/cryptstring.h"
+#include "talk/xmpp/saslmechanism.h"
+
+namespace buzz {
+
+class SaslPlainMechanism : public SaslMechanism {
+
+public:
+ SaslPlainMechanism(const buzz::Jid user_jid, const talk_base::CryptString & password) :
+ user_jid_(user_jid), password_(password) {}
+
+ virtual std::string GetMechanismName() { return "PLAIN"; }
+
+ virtual XmlElement * StartSaslAuth() {
+ // send initial request
+ XmlElement * el = new XmlElement(QN_SASL_AUTH, true);
+ el->AddAttr(QN_MECHANISM, "PLAIN");
+
+ talk_base::FormatCryptString credential;
+ credential.Append("\0", 1);
+ credential.Append(user_jid_.node());
+ credential.Append("\0", 1);
+ credential.Append(&password_);
+ el->AddText(Base64EncodeFromArray(credential.GetData(), credential.GetLength()));
+ return el;
+ }
+
+private:
+ Jid user_jid_;
+ talk_base::CryptString password_;
+};
+
+}
+
+#endif
diff --git a/Plugins/jingle/libjingle_callclient.cpp b/Plugins/jingle/libjingle_callclient.cpp
new file mode 100644
index 0000000..fcafd8f
--- /dev/null
+++ b/Plugins/jingle/libjingle_callclient.cpp
@@ -0,0 +1,281 @@
+/*
+ * Jingle call example
+ * Copyright 2004--2005, Google Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+
+#include "commons.h"
+
+
+
+
+CallClient::CallClient(DATA *aData)
+{
+ data = aData;
+ phone_client_ = NULL;
+ signaling = false;
+ needSignal = false;
+}
+
+CallClient::~CallClient()
+{
+}
+
+void CallClient::StartSignaling()
+{
+ if (!signaling && needSignal)
+ data->session_manager_->OnSignalingReady();
+
+ signaling = true;
+}
+
+void CallClient::StopSignaling()
+{
+ signaling = false;
+ needSignal = false;
+}
+
+void CallClient::OnSignalingReady()
+{
+ if (signaling)
+ data->session_manager_->OnSignalingReady();
+ else
+ needSignal = true;
+}
+
+void CallClient::OnConnect()
+{
+ std::string jid = ToString(data->fullJID);
+ cricket::InitRandom(jid.c_str(), jid.size());
+
+ phone_client_ = new cricket::PhoneSessionClient(buzz::Jid(jid), data->session_manager_);
+ phone_client_->SignalCallCreate.connect(this, &CallClient::OnCallCreate);
+}
+
+void CallClient::OnDisconnect()
+{
+ if (phone_client_ != NULL)
+ {
+ delete phone_client_;
+ phone_client_ = NULL;
+ }
+}
+
+void CallClient::OnCallCreate(cricket::Call* call)
+{
+ call->SignalSessionState.connect(this, &CallClient::OnSessionState);
+}
+
+void CallClient::OnCallDestroy(cricket::Call* call)
+{
+}
+
+#ifdef UNICODE
+
+static WCHAR *mir_dupToUnicode(const char *ptr)
+{
+ if (ptr == NULL)
+ return NULL;
+
+ size_t size = strlen(ptr) + 1;
+ WCHAR *tmp = (WCHAR *) mir_alloc(size * sizeof(WCHAR));
+
+ MultiByteToWideChar(CP_ACP, 0, ptr, -1, tmp, size * sizeof(WCHAR));
+
+ return tmp;
+}
+
+#endif
+
+void CallClient::NofifyState(cricket::Call* call,
+ cricket::Session* session,
+ int state)
+{
+ char id[20];
+ VOICE_CALL vc = {0};
+ vc.cbSize = sizeof(vc);
+ vc.szModule = data->jabber->protocolName;
+ vc.id = itoa(call->id(), id, 10);
+ vc.flags = VOICE_CALL_CONTACT;
+ vc.state = state;
+#ifdef UNICODE
+ TCHAR *jid = mir_dupToUnicode(session->remote_name().c_str());
+ vc.hContact = data->jabber->pfHContactFromJID(jid);
+ mir_free(jid);
+#else
+ vc.hContact = data->jabber->pfHContactFromJID(session->remote_name().c_str());
+#endif
+ NotifyEventHooks(data->hVoiceNotify, (WPARAM) &vc, 0);
+}
+
+void CallClient::OnSessionState(cricket::Call* call,
+ cricket::Session* session,
+ cricket::Session::State state)
+{
+ if (state == cricket::Session::STATE_RECEIVEDINITIATE) {
+ NofifyState(call, session, VOICE_STATE_RINGING);
+
+ } else if (state == cricket::Session::STATE_SENTINITIATE) {
+ NofifyState(call, session, VOICE_STATE_CALLING);
+
+ } else if (state == cricket::Session::STATE_SENTACCEPT
+ || state == cricket::Session::STATE_RECEIVEDACCEPT) {
+ StartSignaling();
+
+ } else if (state == cricket::Session::STATE_INPROGRESS) {
+ NofifyState(call, session, VOICE_STATE_TALKING);
+
+ } else if (state == cricket::Session::STATE_DEINIT) {
+ NofifyState(call, session, VOICE_STATE_ENDED);
+ StopSignaling();
+ }
+}
+
+void CallClient::OnMessage(talk_base::Message *pmsg)
+{
+ switch(pmsg->message_id)
+ {
+ case MSG_TIMER:
+ {
+ EnterCriticalSection(&data->csPendingIqMap);
+
+ time_t expire = time(NULL) - 3;
+ for(PendingIqMap::iterator it = data->pendingIqs.begin(); it != data->pendingIqs.end(); )
+ {
+ PendingIq &piq = it->second;
+ if (piq.sent < expire)
+ {
+ buzz::XmlElement *stanza = (buzz::XmlElement *) piq.param;
+ data->session_manager_->OnFailedSend(stanza, NULL);
+ delete stanza;
+
+ it = data->pendingIqs.erase(it);
+ }
+ else
+ ++it;
+ }
+
+ LeaveCriticalSection(&data->csPendingIqMap);
+ break;
+ }
+ case MSG_REPLY_MSG:
+ {
+ ReplyMessageData *rmd = (ReplyMessageData *) pmsg->pdata;
+
+ if (rmd->newStanza->Attr(buzz::QN_TYPE) == buzz::STR_RESULT) {
+ data->session_manager_->OnIncomingResponse(rmd->oldStanza, rmd->newStanza);
+ } else {
+ data->session_manager_->OnFailedSend(rmd->oldStanza, rmd->newStanza);
+ }
+
+ delete rmd->oldStanza;
+ delete rmd->newStanza;
+ delete rmd;
+
+ break;
+ }
+ case MSG_INCOMING_MSG:
+ {
+ buzz::XmlElement *el = (buzz::XmlElement *) pmsg->pdata;
+ if (el == NULL)
+ return;
+
+ if (data->session_manager_->IsSessionMessage(el))
+ data->session_manager_->OnIncomingMessage(el);
+
+ delete el;
+ break;
+ }
+ case MSG_MAKE_CALL_TO:
+ {
+ HANDLE hContact = (HANDLE) pmsg->pdata;
+ if ( hContact == NULL )
+ return;
+
+ DBVARIANT dbv;
+ if (DBGetContactSettingTString(hContact, data->jabber->protocolName, "jid", &dbv))
+ return;
+
+ TCHAR jid[512];
+ _tcsncpy(jid, dbv.ptszVal, MAX_REGS(jid));
+ DBFreeVariant(&dbv);
+
+ if (DBGetContactSettingWord(hContact, data->jabber->protocolName, "Status", ID_STATUS_OFFLINE) <= ID_STATUS_OFFLINE)
+ return;
+
+ TCHAR szJid[512];
+ const TCHAR *bestResName = data->jabber->pfGetBestClientResourceNamePtr(jid);
+ mir_sntprintf(szJid, MAX_REGS(szJid), bestResName ? _T("%s/%s") : _T("%s"), jid, bestResName);
+
+ phone_client_->SignalCallDestroy.connect(this, &CallClient::OnCallDestroy);
+ cricket::Call * call = phone_client_->CreateCall();
+ call->InitiateSession(buzz::Jid(ToString(szJid)), NULL);
+ phone_client_->SetFocus(call);
+ break;
+ }
+ case MSG_ANSWER_CALL:
+ {
+ uint32 id = (uint32) pmsg->pdata;
+ cricket::Call * call = phone_client_->GetCall(id);
+ if (call == NULL)
+ return;
+
+ call->AcceptSession(call->sessions()[0]);
+ phone_client_->SetFocus(call);
+ break;
+ }
+ case MSG_DROP_CALL:
+ {
+ uint32 id = (uint32) pmsg->pdata;
+ cricket::Call * call = phone_client_->GetCall(id);
+ if (call == NULL)
+ return;
+
+ if (call->sessions()[0]->state() == cricket::Session::STATE_RECEIVEDINITIATE
+ || call->sessions()[0]->state() == cricket::Session::STATE_RECEIVEDMODIFY)
+ call->RejectSession(call->sessions()[0]);
+ else
+ call->Terminate();
+ break;
+ }
+ case TERMINATE_ALL:
+ {
+ data->session_manager_->TerminateAll();
+ break;
+ }
+ }
+}
+
+void CallClient::MakeCallTo(HANDLE hContact)
+{
+ if ( hContact == NULL )
+ return;
+
+ data->signaling_thread_->Post(this, MSG_MAKE_CALL_TO, (talk_base::MessageData *) hContact);
+}
+
+void CallClient::AnswerCall(int id)
+{
+ data->signaling_thread_->Post(this, MSG_ANSWER_CALL, (talk_base::MessageData *) id);
+}
+
+void CallClient::DropCall(int id)
+{
+ data->signaling_thread_->Post(this, MSG_DROP_CALL, (talk_base::MessageData *) id);
+}
+
diff --git a/Plugins/jingle/libjingle_callclient.h b/Plugins/jingle/libjingle_callclient.h
new file mode 100644
index 0000000..c85dee0
--- /dev/null
+++ b/Plugins/jingle/libjingle_callclient.h
@@ -0,0 +1,88 @@
+/*
+ * Jingle call example
+ * Copyright 2004--2005, Google Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __CALLCLIENT_H__
+#define __CALLCLIENT_H__
+
+
+namespace talk_base {
+ class Thread;
+ class NetworkManager;
+}
+
+namespace cricket {
+ class PortAllocator;
+ class PhoneSessionClient;
+ class Receiver;
+ class Call;
+ class SessionManagerTask;
+}
+
+#define MSG_MAKE_CALL_TO 11
+#define MSG_ANSWER_CALL 12
+#define MSG_DROP_CALL 13
+#define MSG_INCOMING_MSG 14
+#define MSG_TIMER 15
+#define MSG_REPLY_MSG 16
+#define TERMINATE_ALL 17
+
+
+class ReplyMessageData : public talk_base::MessageData {
+public:
+ buzz::XmlElement *oldStanza;
+ buzz::XmlElement *newStanza;
+};
+
+
+
+class CallClient : public sigslot::has_slots<>, public talk_base::MessageHandler {
+public:
+ CallClient(DATA *aData);
+ virtual ~CallClient();
+
+ void OnConnect();
+ void OnDisconnect();
+
+ void MakeCallTo(HANDLE hContact);
+ void AnswerCall(int id);
+ void DropCall(int id);
+ void OnSignalingReady();
+
+ virtual void OnMessage(talk_base::Message *pmsg);
+
+private:
+ DATA *data;
+
+ bool signaling;
+ bool needSignal;
+
+ void StartSignaling();
+ void StopSignaling();
+
+ cricket::PhoneSessionClient *phone_client_;
+
+ void NofifyState(cricket::Call* call, cricket::Session* session, int state);
+
+ void OnCallCreate(cricket::Call* call);
+ void OnCallDestroy(cricket::Call* call);
+ void OnSessionState(cricket::Call* call, cricket::Session* session, cricket::Session::State state);
+};
+
+
+#endif // __CALLCLIENT_H__
diff --git a/Plugins/jingle/sdk/m_updater.h b/Plugins/jingle/sdk/m_updater.h
new file mode 100644
index 0000000..371b743
--- /dev/null
+++ b/Plugins/jingle/sdk/m_updater.h
@@ -0,0 +1,146 @@
+#ifndef _M_UPDATER_H
+#define _M_UPDATER_H
+
+// NOTES:
+// - For langpack updates, include a string of the following format in the langpack text file:
+// ";FLID: <file listing name> <version>"
+// version must be four numbers seperated by '.', in the range 0-255 inclusive
+// - Updater will disable plugins that are downloaded but were not active prior to the update (this is so that, if an archive contains e.g. ansi and
+// unicode versions, the correct plugin will be the only one active after the new version is installed)...so if you add a support plugin, you may need
+// to install an ini file to make the plugin activate when miranda restarts after the update
+// - Updater will replace all dlls that have the same internal shortName as a downloaded update dll (this is so that msn1.dll and msn2.dll, for example,
+// will both be updated) - so if you have a unicode and a non-unicode version of a plugin in your archive, you should make the internal names different (which will break automatic
+// updates from the file listing if there is only one file listing entry for both versions, unless you use the 'MS_UPDATE_REGISTER' service below)
+// - Updater will install all files in the root of the archive into the plugins folder, except for langpack files that contain the FLID string which go into the root folder (same
+// folder as miranda32.exe)...all folders in the archive will also be copied to miranda's root folder, and their contents transferred into the new folders. The only exception is a
+// special folder called 'root_files' - if there is a folder by that name in the archive, it's contents will also be copied into miranda's root folder - this is intended to be used
+// to install additional dlls etc that a plugin may require)
+
+// if you set Update.szUpdateURL to the following value when registering, as well as setting your beta site and version data,
+// Updater will ignore szVersionURL and pbVersionPrefix, and attempt to find the file listing URL's from the backend XML data.
+// for this to work, the plugin name in pluginInfo.shortName must match the file listing exactly (except for case)
+#define UPDATER_AUTOREGISTER "UpdaterAUTOREGISTER"
+// Updater will also use the backend xml data if you provide URL's that reference the miranda file listing for updates (so you can use that method
+// if e.g. your plugin shortName does not match the file listing) - it will grab the file listing id from the end of these URLs
+
+typedef struct Update_tag {
+ int cbSize;
+ char *szComponentName; // component name as it will appear in the UI (will be translated before displaying)
+
+ char *szVersionURL; // URL where the current version can be found (NULL to disable)
+ BYTE *pbVersionPrefix; // bytes occuring in VersionURL before the version, used to locate the version information within the URL data
+ // (note that this URL could point at a binary file - dunno why, but it could :)
+ int cpbVersionPrefix; // number of bytes pointed to by pbVersionPrefix
+ char *szUpdateURL; // URL where dll/zip is located
+ // set to UPDATER_AUTOREGISTER if you want Updater to find the file listing URLs (ensure plugin shortName matches file listing!)
+
+ char *szBetaVersionURL; // URL where the beta version can be found (NULL to disable betas)
+ BYTE *pbBetaVersionPrefix; // bytes occuring in VersionURL before the version, used to locate the version information within the URL data
+ int cpbBetaVersionPrefix; // number of bytes pointed to by pbVersionPrefix
+ char *szBetaUpdateURL; // URL where dll/zip is located
+
+ BYTE *pbVersion; // bytes of current version, used for comparison with those in VersionURL
+ int cpbVersion; // number of bytes pointed to by pbVersion
+
+ char *szBetaChangelogURL; // url for displaying changelog for beta versions
+} Update;
+
+// register a comonent with Updater
+//
+// wparam = 0
+// lparam = (LPARAM)&Update
+#define MS_UPDATE_REGISTER "Update/Register"
+
+// utility functions to create a version string from a DWORD or from pluginInfo
+// point buf at a buffer at least 16 chars wide - but note the version string returned may be shorter
+//
+__inline static char *CreateVersionString(DWORD version, char *buf) {
+ mir_snprintf(buf, 16, "%d.%d.%d.%d", (version >> 24) & 0xFF, (version >> 16) & 0xFF, (version >> 8) & 0xFF, version & 0xFF);
+ return buf;
+}
+
+__inline static char *CreateVersionStringPlugin(PLUGININFO *pluginInfo, char *buf) {
+ return CreateVersionString(pluginInfo->version, buf);
+}
+
+
+// register the 'easy' way - use this method if you have no beta URL and the plugin is on the miranda file listing
+// NOTE: the plugin version string on the file listing must be the string version of the version in pluginInfo (i.e. 0.0.0.1,
+// four numbers between 0 and 255 inclusivem, so no letters, brackets, etc.)
+//
+// wParam = (int)fileID - this is the file ID from the file listing (i.e. the number at the end of the download link)
+// lParam = (PLUGININFO*)&pluginInfo
+#define MS_UPDATE_REGISTERFL "Update/RegisterFL"
+
+// this function can be used to 'unregister' components - useful for plugins that register non-plugin/langpack components and
+// may need to change those components on the fly
+// lParam = (char *)szComponentName
+#define MS_UPDATE_UNREGISTER "Update/Unregister"
+
+// this event is fired when the startup process is complete, but NOT if a restart is imminent
+// it is designed for status managment plugins to use as a trigger for beggining their own startup process
+// wParam = lParam = 0 (unused)
+// (added in version 0.1.6.0)
+#define ME_UPDATE_STARTUPDONE "Update/StartupDone"
+
+// this service can be used to enable/disable Updater's global status control
+// it can be called from the StartupDone event handler
+// wParam = (BOOL)enable
+// lParam = 0
+// (added in version 0.1.6.0)
+#define MS_UPDATE_ENABLESTATUSCONTROL "Update/EnableStatusControl"
+
+// An description of usage of the above service and event:
+// Say you are a status control plugin that normally sets protocol or global statuses in your ModulesLoaded event handler.
+// In order to make yourself 'Updater compatible', you would move the status control code from ModulesLoaded to another function,
+// say DoStartup. Then, in ModulesLoaded you would check for the existence of the MS_UPDATE_ENABLESTATUSCONTROL service.
+// If it does not exist, call DoStartup. If it does exist, hook the ME_UPDATE_STARTUPDONE event and call DoStartup from there. You may
+// also wish to call MS_UPDATE_ENABLESTATUSCONTROL with wParam == FALSE at this time, to disable Updater's own status control feature.
+
+// this service can be used to determine whether updates are possible for a component with the given name
+// wParam = 0
+// lParam = (char *)szComponentName
+// returns TRUE if updates are supported, FALSE otherwise
+#define MS_UPDATE_ISUPDATESUPPORTED "Update/IsUpdateSupported"
+
+#endif
+
+
+/////////////// Usage Example ///////////////
+
+#ifdef EXAMPLE_CODE
+
+// you need to #include "m_updater.h" and HookEvent(ME_SYSTEM_MODULESLOADED, OnModulesLoaded) in your Load function...
+
+int OnModulesLoaded(WPARAM wParam, LPARAM lParam) {
+
+ Update update = {0}; // for c you'd use memset or ZeroMemory...
+ char szVersion[16];
+
+ update.cbSize = sizeof(Update);
+
+ update.szComponentName = pluginInfo.shortName;
+ update.pbVersion = (BYTE *)CreateVersionString(&pluginInfo, szVersion);
+ update.cpbVersion = strlen((char *)update.pbVersion);
+
+ // these are the three lines that matter - the archive, the page containing the version string, and the text (or data)
+ // before the version that we use to locate it on the page
+ // (note that if the update URL and the version URL point to standard file listing entries, the backend xml
+ // data will be used to check for updates rather than the actual web page - this is not true for beta urls)
+ update.szUpdateURL = "http://scottellis.com.au:81/test/updater.zip";
+ update.szVersionURL = "http://scottellis.com.au:81/test/updater_test.html";
+ update.pbVersionPrefix = (BYTE *)"Updater version ";
+
+ update.cpbVersionPrefix = strlen((char *)update.pbVersionPrefix);
+
+ // do the same for the beta versions of the above struct members if you wish to allow beta updates from another URL
+
+ CallService(MS_UPDATE_REGISTER, 0, (WPARAM)&update);
+
+ // Alternatively, to register a plugin with e.g. file ID 2254 on the file listing...
+ // CallService(MS_UPDATE_REGISTERFL, (WPARAM)2254, (LPARAM)&pluginInfo);
+
+ return 0;
+}
+
+#endif
diff --git a/Plugins/jingle/sdk/m_voice.h b/Plugins/jingle/sdk/m_voice.h
new file mode 100644
index 0000000..a81d866
--- /dev/null
+++ b/Plugins/jingle/sdk/m_voice.h
@@ -0,0 +1,158 @@
+/*
+Copyright (C) 2006 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#ifndef __M_VOICE_H__
+# define __M_VOICE_H__
+
+
+#define EVENTTYPE_VOICE_CALL 8739
+
+
+#define PROTOTYPE_VOICE (PROTOTYPE_ENCRYPTION-9)
+
+
+#define VOICE_UNICODE 0x80000000
+
+#ifdef UNICODE
+# define VOICE_TCHAR VOICE_UNICODE
+#else
+# define VOICE_TCHAR 0
+#endif
+
+#define VOICE_STATE_TALKING 0
+#define VOICE_STATE_RINGING 1
+#define VOICE_STATE_CALLING 2
+#define VOICE_STATE_ON_HOLD 3
+#define VOICE_STATE_ENDED 4
+
+typedef struct {
+ int cbSize; // Struct size
+ const char *szModule; // The name of the protocol module (used only in notifications)
+ char *id; // Protocol especific ID for this call
+ int flags; // Can be VOICE_CALL_CONTACT or VOICE_CALL_STRING (VOICE_UNICODE to say the string is unicode)
+ union { // Who to call
+ HANDLE hContact;
+ TCHAR *ptszContact;
+ char *pszContact;
+ WCHAR *pwszContact;
+ };
+ int state; // VOICE_STATE_*
+
+} VOICE_CALL;
+
+
+/*
+Notifies that a voice call changed state
+
+wParam: const VOICE_CALL *
+lParam: ignored
+return: 0 on success
+*/
+#define PE_VOICE_CALL_STATE "/Voice/State"
+
+
+#define VOICE_SUPPORTED 1 // Set if proto support voice calls. Probabilly will be 1 ;)
+#define VOICE_CALL_CONTACT 2 // Set if a call can be made to a hContact
+#define VOICE_CALL_CONTACT_NEED_TEST 4 // Set if the contact need to be tested with PS_VOICE_CALL_CONTACT_VALID (needs VOICE_CALL_CONTACT set to work)
+#define VOICE_CALL_STRING 8 // Set if a call can be made to some string (PS_VOICE_CALL_STRING_VALID is used to validate the string)
+#define VOICE_CAN_SET_DEVICE 16 // Set if the devices to mic in and sound out can be set (or the protocol will handle it internally)
+#define VOICE_CAN_HOLD 32 // Set if a call can be put on hold
+/*
+Get protocol voice support flags
+
+wParam: ignored
+lParam: ignored
+return: 0 on success
+*/
+#define PS_VOICE_GETINFO "/Voice/GetInfo"
+
+/*
+Request to the protocol a voice call to hContact.
+
+wParam: (HANDLE) hContact
+lParam: ignored
+return: 0 on success
+*/
+#define PS_VOICE_CALL "/Voice/Call"
+
+/*
+Service called to make the protocol answer a call.
+It is an async call. If the call was answered, the PE_VOICE_STARTEDCALL
+notification will be fired.
+
+wParam: (const char *) id
+lParam: ignored
+return: 0 on success
+*/
+#define PS_VOICE_ANSWERCALL "/Voice/AnswerCall"
+
+/*
+Service called to make the protocol answer a call. This can be called if the
+call is ringing or has started. If called any other time it should be ignored.
+It is an async call. If the call was droped, the PE_VOICE_ENDEDCALL
+notification will be fired.
+
+wParam: (const char *) id
+lParam: ignored
+return: 0 on success
+*/
+#define PS_VOICE_DROPCALL "/Voice/DropCall"
+
+/*
+Service called to make the protocol hold a call. This means that the call should not
+be droped, but it should be muted and put in a hold, to allow other call to be answered.
+If the protocol can't hold a cal, it should be droped.
+
+This can be called if the call has started. If called any other time it should be ignored.
+It is an async call. If the call was droped, the PE_VOICE_HOLDEDCALL
+notification will be fired.
+
+wParam: (const char *) id
+lParam: ignored
+return: 0 on success
+*/
+#define PS_VOICE_HOLDCALL "/Voice/HoldCall"
+
+/*
+Used if protocol support VOICE_CALL_STRING. The call string is passed as
+wParam and the proto should validate it.
+
+wParam: (const TCHAR *) call string
+lParam: ignored
+return: 0 if wrong, 1 if correct
+*/
+#define PS_VOICE_CALL_STRING_VALID "/Voice/CallStringValid"
+
+/*
+Used if protocol support VOICE_CALL_CONTACT and VOICE_CALL_CONTACT_NEED_TEST.
+The hContact is passed as wParam and the proto should tell if this contact can be
+called.
+
+wParam: (HANDLE) hContact
+lParam: (BOOL) TRUE if it is a test for 'can call now?', FALSE if is a test for 'will be possible to call someday?'
+return: 0 if can't be called, 1 if can
+*/
+#define PS_VOICE_CALL_CONTACT_VALID "/Voice/CallContactValid"
+
+
+
+
+
+#endif // __M_VOICE_H__
diff --git a/Plugins/jingle/sdk/m_voiceservice.h b/Plugins/jingle/sdk/m_voiceservice.h
new file mode 100644
index 0000000..0751e3f
--- /dev/null
+++ b/Plugins/jingle/sdk/m_voiceservice.h
@@ -0,0 +1,65 @@
+/*
+Copyright (C) 2007 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#ifndef __M_VOICESERVICE_H__
+# define __M_VOICESERVICE_H__
+
+#include "m_voice.h"
+
+/*
+This services are a mirror of the services/notifications in m_voice.h,
+with the difference that that ones are to be used by protocols, and this ones
+are to be used by plugins that can make calls to contacts in multiple protocols.
+*/
+
+
+/*
+Notifies that a voice call changed state
+
+wParam: const VOICE_CALL *
+lParam: ignored
+return: 0 on success
+*/
+#define MS_VOICESERVICE_STATE "VoiceService/State"
+
+
+
+struct VOICE_MODULE
+{
+ int cbSize; // sizeof(VOICE_MODULE)
+ char *name; // The internal name of the plugin. All PS_* serivces (except PS_VOICE_GETINFO)
+ // defined in m_voide.h need to be created based in this name. For example,
+ // PS_VOICE_CALL (/Voice/Call) need to be created as <name>/Voice/Call
+ int flags; // VOICE_* from m_voice.h
+};
+/*
+Register a new plugin that can make/receive voice calls.
+
+wParam: const VOICE_MODULE *
+lParam: ignored
+return: 0 on success
+*/
+#define MS_VOICESERVICE_REGISTER "VoiceService/Register"
+
+
+
+
+
+#endif // __M_VOICESERVICE_H__
diff --git a/Plugins/mydetails/Docs/langpack_MyDetails.txt b/Plugins/mydetails/Docs/langpack_MyDetails.txt
new file mode 100644
index 0000000..01c61de
--- /dev/null
+++ b/Plugins/mydetails/Docs/langpack_MyDetails.txt
@@ -0,0 +1,85 @@
+; My Details
+; Author: Pescuma
+; http://forums.miranda-im.org/showthread.php?t=5643
+
+[My Details]
+
+; Group in options
+[Customize]
+
+; Options
+
+[ General ]
+[Cycle through protocols every:]
+[seconds]
+[RTL]
+[Auto-resize frame]
+[Replace Smileys]
+[Align text to right]
+[Use contact lest smileys]
+[Resize Smileys]
+[Global on avatar]
+[Global on nickname]
+[Global on status]
+[Global on status message]
+
+[ Frame Options ]
+[Top:]
+[Bottom:]
+[Left:]
+[Right:]
+[Background Color:]
+
+[ Avatar ]
+[Custom size:]
+[pixels]
+[Allow it to grow]
+[Draw border on avatar]
+[Border Color:]
+[Round corners of avatars]
+[Custom corner size:]
+[Use free space (under avatar) to other texts]
+
+[ Protocol ]
+[Show protocol name]
+[Show protocol cycle button]
+
+
+; Default values
+[<no nickname>]
+[<no status name>]
+
+
+; Without multiwindows
+[Show My Details]
+[Hide My Details]
+
+
+; Menus
+[Set My Avatar...]
+[Set My Nickname...]
+[Set My Status Message...]
+[Set My Avatar for %s...]
+[Set My Nickname for %s...]
+[Set My Status Message for %s...]
+[Show next protocol]
+[Show previous protocol]
+[Cycle through protocols]
+[Don't cycle through protocols]
+
+
+; Dialogs
+[Set My Nickname]
+[Set My Nickname for %s]
+[Nickname:]
+
+[Set My Status Message for All Status]
+[Set My Status Message for %s]
+[Status Message:]
+
+[OK]
+[Cancel]
+
+; Clist Modern BAckground
+[Main Window/Backgrnd]
+[MyDetails/Backgrnd]
diff --git a/Plugins/mydetails/Docs/mydetails.gif b/Plugins/mydetails/Docs/mydetails.gif
new file mode 100644
index 0000000..8e4d04e
--- /dev/null
+++ b/Plugins/mydetails/Docs/mydetails.gif
Binary files differ
diff --git a/Plugins/mydetails/Docs/mydetails_changelog.txt b/Plugins/mydetails/Docs/mydetails_changelog.txt
new file mode 100644
index 0000000..011a1c6
--- /dev/null
+++ b/Plugins/mydetails/Docs/mydetails_changelog.txt
@@ -0,0 +1,228 @@
+My Details
+
+Changelog:
+
+. 0.0.2.6
+ + Added support for clist modern skin engine
+ + Added pidgin style skin
+ * Fix for last shown protocol
+ * Updated updater to use googlecode
+
+. 0.0.2.5
+ + Use account name and ordering
+ * Better handling status messages
+ * Fix for jabber status names
+ * Bug fixes
+
+. 0.0.2.3
+ * Moved background color to font service settings
+ + Better simple away support
+
+. 0.0.2.2
+ * Fix for email count
+ * Better handling of hover
+ * Better handling of small sizes
+ * If fixed avatar size is set, use it even if no avatar present
+ + If windows uses RTL, it is selected by default
+
+. 0.0.2.1
+ + Show lock icon over status
+ + Show unread mail count
+ * Resize frame is working again
+ * Still work in progress
+
+. 0.0.2.0
+ + Now uses skins plugin to position elements (work in progress)
+
+. 0.0.1.11
+ * Fix for arrows: always draw then at right side
+
+. 0.0.1.10
+ + Added arrows to change protocols (Drugwash patch)
+ * Use icolib to show icons (uses listening to icon from contact list)
+
+. 0.0.1.9
+ + Created 2 services: MyDetails/HideFrame and MyDetails/ShowFrame
+ * MyDetails/ShowHideMyDetails changed to work with frames too (it toggles the frame)
+
+. 0.0.1.8
+ * Fix for crash when using main menu options
+
+. 0.0.1.7
+ * Fix for crash when no protocol found
+ + Added 2 keys to allow themes to show/hide frame: MyDetails\ForceHideFrame and MyDetails\ForceShowFrame (BYTE). Both are deleted after use.
+
+. 0.0.1.6
+ + Added uid for 0.8
+
+. 0.0.1.5:
+ + Added option to set status message per protocol
+
+. 0.0.1.4
+ * Fixed crash on protocol connection
+ + Support for new version of ersatz
+
+. 0.0.1.3
+ + Support for ersatz plugin by TioDuke. When it is finished, status message will be shown correctly always :)
+
+. 0.0.1.2
+ + Added listening to info
+
+. 0.0.1.1
+ * Fix for crash on exit (thanks ghazan)
+ - Removed clist modern mod support
+
+. 0.0.1.0
+ * Fix in call to NAS service
+ + Ready to FL
+
+. 0.0.0.42
+ * Fixed code to open only one dialog
+ + CTRL-Enter on status message dialog
+
+. 0.0.0.41
+ + Added support to new NAS services
+ + Open only one dialog (for dialogs that t handles - does not include NAS / avs)
+ * Bigger buffer for status messages
+ + First release build (dll is 200k smaller)
+
+. 0.0.0.40
+ * Changed text [Top: ] to [Top:]
+ + Added langpack_MyDetails.txt
+
+. 0.0.0.39
+ * Reverted to show menus on key up
+ + Using miranda lists now. This version only works with newer versions of miranda
+ * Fixed call to NAS with parsed variables
+
+. 0.0.0.38
+ * Fix for international languages
+ + Translateble options dialog
+ + Show some menus on key down (but it still not work as desired, i'll have to figure it)
+
+. 0.0.0.37
+ * Bugfix in status menu
+ + Better support for updater
+
+. 0.0.0.36
+ + Change to better support NAS
+ + Change to know when info changed (may flick a little less the screen and the tooltip, but may be losing changes)
+
+. 0.0.0.35
+ + Try to discover the default nick
+ + Global on avatar
+ * Fixed order of fields in options dialog
+
+. 0.0.0.34
+ * Fixed issue with mTooltip
+ + Changed tooltip timeout to 10h (is it long enougth? :P )
+
+. 0.0.0.33
+ + Added timer to refresh status messages. It isnt in options dialog, but can be changed at DB, in key MyDetails\RefreshStatusMessageTimer (0 disables it, default to 12 s)
+ * Fixed input box in set my nickname
+
+. 0.0.0.32
+ * Fix for crash on startup
+ * Fix for drawing function
+
+. 0.0.0.31
+ * Fix for status message with NAS
+
+. 0.0.0.30
+ * Try to fix bug when setting name (Again)
+ * Fix for multi line in status message and nickname
+ + Setting to call global functions on left click
+ + Calling service to get max nickname length from protocol (no proto support it right now)
+
+. 0.0.0.29
+ * Fix in status message code
+
+. 0.0.0.28
+ + Multiline popup
+ + More options in context menu
+ * Try to fix bug when setting name
+
+. 0.0.0.27
+ + Better support for core away system (it set the message in the DB, inside SRAway module)
+
+. 0.0.0.26
+ + Added tooltips
+ + Added background collor
+ + Added XStatus support
+ + Added in the zip: avatar service (unicode and non-unicode) and folder service
+
+. 0.0.0.25
+ + Added support to set avatars (needs modified version of avatar service - is inside zip)
+ + Added in the zip: avatar service and folder service
+
+. 0.0.0.23
+ * Bugfix in new status menu code
+
+. 0.0.0.22
+ + Using clist status menus when possible (this should add support to all away system always)
+ + Show global status menu on right click
+ * Fixed space in status
+
+. 0.0.0.21
+ * Bugfixes
+
+. 0.0.0.20
+ + Compatibility with KeepStatus -> code is too ugly :'(
+ + Show status messages only for supported statuses
+ + Show SimpleAway dialog only to supported protocols
+
+. 0.0.0.19
+ + Better support to SimpleAway
+ + Set what protocol to show by clicking in proto name
+ * Bug fixes
+
+. 0.0.0.18
+ + Set status message after status change (should work with gadu-gadu, but it isn't the best solution at all)
+
+. 0.0.0.17
+ + Popups with more actions
+ + Option to grow avatar
+ + An attempt to set status messages withou NAS
+
+. 0.0.0.16
+ * Fixed leak of GDI objects
+ * Fixed resizing of avatar
+
+. 0.0.0.15
+ * Bug fixes
+
+. 0.0.0.14
+ + Resize frame
+ + Use space bellow avatar
+
+. 0.0.0.13
+ - Change to try to get more updates from status message changes
+ * Fix in avatar refresh
+
+. 0.0.0.12
+ * Updater works
+ * Fix drawing protocol name
+
+. 0.0.0.11
+ * Fixed bug on drawing status name
+ + Added custom avatar size
+ + Will not cicle when changing status
+
+. 0.0.0.9
+ * Fixed crash on Set My Nickname from status menu
+ + Added cache to data (should draw faster, but have to see if the data shown is correct)
+ + Added visual things
+ + Option to set status
+ - Changed to set things with left click of mouse
+
+. 0.0.0.7
+ * Fix in NAS fetching code (again)
+
+. 0.0.0.6
+ * Fix in NAS fetching code
+ + RTL
+ + Smileys
+
+. 0.0.0.5
+ + Added option dialog
+ + Added option to not cicle throught protocols \ No newline at end of file
diff --git a/Plugins/mydetails/Docs/mydetails_readme.txt b/Plugins/mydetails/Docs/mydetails_readme.txt
new file mode 100644
index 0000000..c9e58d0
--- /dev/null
+++ b/Plugins/mydetails/Docs/mydetails_readme.txt
@@ -0,0 +1,41 @@
+My Details plugin
+-----------------
+
+What it does:
+- Show your current configuration, per protocol, for avatar, nickname, status and away message
+- It shows each protocol at a time, cicling throught then
+- Allows to set nickname (per protocol or for all protocols) and away messages (per protocol or for all protocols - need NewAwaySystem, SimpleAway or core module)
+
+Some comments:
+1. SimpleAway does not show a dialog to set a message for all protocols. Someones it does not show the dialog (I requested it in the thread http://forums.miranda-im.org/showthread.php?p=47157).
+2. For core away system, only some protocols works (probabily the same as SimpleAway). But for the ones that it works, the message in the frame is the old message. I know, it sucks... But if you use ersatz plugin this problem doesn't happen: http://pescuma.mirandaim.ru/miranda/ersatz.zip
+
+To request support to other away system: If someone wants to use another away system, please request in its thread to add support for 2 services:
+1. Get current status message for a protocol, given its name
+2. Set current status message for a protocol, given its name and the message
+
+
+To use skin engine of clist modern: the following glyphs are used:
+- MyDetails,ID=Background : background of frame
+- MyDetails,ID=MouseOver : base mouse over background (for all fields)
+- MyDetails,ID=MouseOverNick : base mouse over background for nick (drawn over the base one)
+- MyDetails,ID=MouseOverProto : base mouse over background for protocol (drawn over the base one)
+- MyDetails,ID=MouseOverStatus : base mouse over status name/icon for nick (drawn over the base one)
+- MyDetails,ID=MouseOverStatusMsg : base mouse over background for status message (drawn over the base one)
+- MyDetails,ID=MouseOverListening : base mouse over background for listening info (drawn over the base one)
+
+To report bugs/make suggestions, go to the forum thread: http://forums.miranda-im.org/showthread.php?t=5643
+
+Dependencies:
+- If you want integration with clist, an frame enabled clist, such as clist_modern or clist_nicer+
+- Away systens supported: Core, NewAwaySystem or SimpleAway
+
+Todo:
+- Global page
+- Add custom presets
+- Resize on mouse hover
+- clist_modern_layered integration -> try this: http://forums.miranda-im.org/showthread.php?t=6597
+- New drawing code (please, do not request things like order of items or spacing, it is in TODO list, but will take time to be made)
+- Options to XStatus setup
+- Icons instead of ... on mouse over
+- Options to show/hide itens
diff --git a/Plugins/mydetails/Docs/mydetails_version.txt b/Plugins/mydetails/Docs/mydetails_version.txt
new file mode 100644
index 0000000..e078af1
--- /dev/null
+++ b/Plugins/mydetails/Docs/mydetails_version.txt
@@ -0,0 +1 @@
+My Details 0.0.2.6 \ No newline at end of file
diff --git a/Plugins/mydetails/ZIP/doit.bat b/Plugins/mydetails/ZIP/doit.bat
new file mode 100644
index 0000000..8e606e5
--- /dev/null
+++ b/Plugins/mydetails/ZIP/doit.bat
@@ -0,0 +1,104 @@
+rem @echo off
+
+rem Batch file to build and upload files
+rem
+rem TODO: Integration with FL
+
+set name=mydetails
+
+rem To upload, this var must be set here or in other batch
+rem set ftp=ftp://<user>:<password>@<ftp>/<path>
+
+echo Building %name% ...
+
+msdev ..\%name%.dsp /MAKE "%name% - Win32 Release" /REBUILD
+
+echo Generating files for %name% ...
+
+del *.zip
+del *.dll
+del *.pdb
+
+copy ..\Docs\%name%_changelog.txt
+copy ..\Docs\%name%_version.txt
+mkdir Plugins
+cd Plugins
+copy ..\..\..\..\bin\release\Plugins\%name%.dll
+cd ..
+mkdir Skins
+cd Skins
+mkdir Default
+cd Default
+copy ..\..\..\data\Skins\Default\*.msk
+cd..
+mkdir Pidgin
+cd Pidgin
+copy ..\..\..\data\Skins\Pidgin\*.msk
+cd..
+cd..
+mkdir Docs
+cd Docs
+del /Q *.*
+copy ..\..\Docs\langpack_%name%.txt
+copy ..\..\m_%name%.h
+cd ..
+mkdir src
+cd src
+mkdir mydetails
+cd mydetails
+del /Q *.*
+copy ..\..\..\*.h
+copy ..\..\..\*.cpp
+copy ..\..\..\*.
+copy ..\..\..\*.rc
+copy ..\..\..\*.dsp
+copy ..\..\..\*.dsw
+mkdir Docs
+cd Docs
+del /Q *.*
+copy ..\..\..\..\Docs\*.*
+cd ..
+mkdir sdk
+cd sdk
+del /Q *.*
+copy ..\..\..\..\sdk\*.*
+cd ..
+cd ..
+mkdir utils
+cd utils
+copy ..\..\..\..\utils\*.h
+copy ..\..\..\..\utils\*.cpp
+cd ..
+cd ..
+copy ..\Release\%name%.pdb
+
+"C:\Program Files\Filzip\Filzip.exe" -a -rp %name%.zip Plugins Docs Skins
+"C:\Program Files\Filzip\Filzip.exe" -a -rp %name%_src.zip src\mydetails src\utils
+"C:\Program Files\Filzip\Filzip.exe" -a -rp %name%.pdb.zip %name%.pdb
+
+del *.pdb
+rd /S /Q Skins
+rd /S /Q Plugins
+rd /S /Q Docs
+rd /S /Q src
+
+if "%ftp%"=="" GOTO END
+
+echo Going to upload files...
+pause
+
+"C:\Program Files\FileZilla\FileZilla.exe" -u .\%name%.zip %ftp% -overwrite -close
+"C:\Program Files\FileZilla\FileZilla.exe" -u .\%name%.pdb.zip %ftp% -overwrite -close
+"C:\Program Files\FileZilla\FileZilla.exe" -u .\%name%_changelog.txt %ftp% -overwrite -close
+"C:\Program Files\FileZilla\FileZilla.exe" -u .\%name%_version.txt %ftp% -overwrite -close
+
+if "%ftp2%"=="" GOTO END
+
+"C:\Program Files\FileZilla\FileZilla.exe" -u .\%name%.zip %ftp2% -overwrite -close
+"C:\Program Files\FileZilla\FileZilla.exe" -u .\%name%.pdb.zip %ftp2% -overwrite -close
+"C:\Program Files\FileZilla\FileZilla.exe" -u .\%name%_changelog.txt %ftp2% -overwrite -close
+"C:\Program Files\FileZilla\FileZilla.exe" -u .\%name%_version.txt %ftp2% -overwrite -close
+
+:END
+
+echo Done.
diff --git a/Plugins/mydetails/commons.h b/Plugins/mydetails/commons.h
new file mode 100644
index 0000000..ca1849c
--- /dev/null
+++ b/Plugins/mydetails/commons.h
@@ -0,0 +1,148 @@
+/*
+Copyright (C) 2005 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#ifndef __COMMONS_H__
+# define __COMMONS_H__
+
+
+#define _WIN32_WINNT 0x0501
+#include <windows.h>
+#include <win2k.h>
+#include <commctrl.h>
+#include <stdio.h>
+#include <vector>
+
+#define MIRANDA_VER 0x800
+#include <newpluginapi.h>
+#include <m_clist.h>
+#include <m_skin.h>
+#include <m_system.h>
+#include <m_protocols.h>
+#include <m_protosvc.h>
+#include <m_database.h>
+#include <m_utils.h>
+#include <m_langpack.h>
+#include <m_awaymsg.h>
+#include <m_contacts.h>
+#include <m_options.h>
+#include <m_clui.h>
+#include <m_clc.h>
+#include <m_proto_listeningto.h>
+#include <m_listeningto.h>
+
+#include <m_NewAwaySys.h>
+#include <m_updater.h>
+#include <m_fontservice.h>
+#include <m_variables.h>
+#include <m_avatars.h>
+#include <m_statusplugins.h>
+#include <m_ersatz.h>
+#include <m_icq.h>
+#include <m_icolib.h>
+#include "m_cluiframes.h"
+#include "m_simpleaway.h"
+
+#include <richedit.h>
+#include <m_smileyadd.h>
+
+#include <io.h>
+
+#include "../skins/m_skins_cpp.h"
+
+#include "resource.h"
+
+
+#define MODULE_NAME "MyDetails"
+
+#define SETTING_FRAME_VISIBLE "FrameVisible"
+#define SETTING_DEFAULT_NICK "DefaultNick"
+
+
+extern HINSTANCE hInst;
+extern PLUGINLINK *pluginLink;
+
+extern long nickname_dialog_open;
+extern long status_msg_dialog_open;
+
+extern SkinDialog *dialog;
+
+
+#include "m_mydetails.h"
+#include "data.h"
+#include "options.h"
+#include "frame.h"
+#include "../utils/mir_smileys.h"
+#include "../utils/mir_memory.h"
+#include "../utils/mir_options.h"
+#include "../utils/mir_icons.h"
+#include "../utils/tstring.h"
+
+
+#define PS_SETMYAVATAR "/SetMyAvatar"
+#define PS_GETMYAVATAR "/GetMyAvatar"
+#define PS_GETMYAVATARMAXSIZE "/GetMyAvatarMaxSize"
+#define PS_GETUNREADEMAILCOUNT "/GetUnreadEmailCount"
+
+#define PS_SETMYNICKNAME "/SetNickname"
+
+#define PS_GETMYNICKNAMEMAXLENGTH "/GetMyNicknameMaxLength"
+
+#define WAYD_UNICODE 1 // return Unicode texts
+#if defined( _UNICODE )
+ #define WAYD_TCHAR WAYD_UNICODE
+#else
+ #define WAYD_TCHAR 0
+#endif
+
+// Get the max length that a WAYD message can have
+// wParam=(WPARAM)0
+// lParam=(LPARAM)0
+// Returns the max length
+#define PS_GET_MY_WAYD_MAXLENGTH "/GetMyWAYDMaxLength"
+
+// Get the WAYD message for the user
+// wParam=(WPARAM)WAYD_xxx
+// lParam=(LPARAM)0
+// Returns the text or NULL if there is none. Remember to mir_free the return value.
+#define PS_GET_MY_WAYD "/GetMyWAYD"
+
+// Sets the WAYD message for the user
+// wParam=(WPARAM)WAYD_xxx
+// lParam=(LPARAM)(WCHAR * or char *)The text to set
+// Returns 0 on success, nonzero on failure
+#define PS_SET_MY_WAYD "/SetMyWAYD"
+
+#define MAX_REGS(_A_) ( sizeof(_A_) / sizeof(_A_[0]) )
+
+
+// See if a protocol service exists
+__inline static int ProtoServiceExists(const char *szModule,const char *szService)
+{
+ char str[MAXMODULELABELLENGTH];
+ strcpy(str,szModule);
+ strcat(str,szService);
+ return ServiceExists(str);
+}
+
+
+
+
+
+#endif // __COMMONS_H__
diff --git a/Plugins/mydetails/data.cpp b/Plugins/mydetails/data.cpp
new file mode 100644
index 0000000..eccd72e
--- /dev/null
+++ b/Plugins/mydetails/data.cpp
@@ -0,0 +1,1063 @@
+/*
+Copyright (C) 2005 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#include "commons.h"
+#include "data.h"
+#include <algorithm>
+
+
+static char *StatusModeToDbSetting(int status,const char *suffix);
+
+
+static bool IsValid(const char *proto)
+{
+ if (proto == NULL || proto[0] == 0)
+ return false;
+
+ int caps = CallProtoService(proto, PS_GETCAPS, PFLAGNUM_1, 0);
+ return (caps & PF1_IM) == PF1_IM && strcmp(proto, "MetaContacts") != 0;
+}
+
+static bool AccOrderComp(PROTOACCOUNT *p1, PROTOACCOUNT *p2)
+{
+ return p1->iOrder < p2->iOrder;
+}
+
+static void GetAccounts(std::vector<PROTOACCOUNT *> *result)
+{
+ int count;
+ PROTOACCOUNT **protos;
+ ProtoEnumAccounts(&count, &protos);
+
+ for (int i = 0; i < count; i++)
+ {
+ if (protos[i]->type != PROTOTYPE_PROTOCOL)
+ continue;
+
+ if (!IsAccountEnabled(protos[i]))
+ continue;
+
+ if (!IsValid(protos[i]->szModuleName))
+ continue;
+
+ result->push_back(protos[i]);
+ }
+
+ std::sort(result->begin(), result->begin(), AccOrderComp);
+}
+
+
+void GetProtocols(std::vector<Protocol> *result)
+{
+ std::vector<PROTOACCOUNT *> accs;
+ GetAccounts(&accs);
+
+ unsigned int accsSize = accs.size();
+ for (unsigned int i = 0; i < accsSize ; ++i)
+ result->push_back(Protocol(accs[i]->szModuleName));
+}
+
+
+int GetProtocolIndexByName(const char *moduleName)
+{
+ std::vector<PROTOACCOUNT *> protos;
+ GetAccounts(&protos);
+
+ int protosSize = (int) protos.size();
+ for(int i = 0; i < protosSize; ++i)
+ {
+ if (strcmp(protos[i]->szModuleName, moduleName) == 0)
+ return i;
+ }
+
+ return -1;
+}
+
+
+int GetNumProtocols()
+{
+ std::vector<PROTOACCOUNT *> protos;
+ GetAccounts(&protos);
+ return protos.size();
+}
+
+
+struct ProtoCache
+{
+ Protocol *proto;
+ int index;
+
+ void Free()
+ {
+ delete proto;
+ proto = NULL;
+ index = -1;
+ }
+};
+
+static ProtoCache current = { NULL, -1 };
+
+
+void SetCurrentProtocol(int index)
+{
+ current.Free();
+
+ int protosSize = GetNumProtocols();
+ if (protosSize > 0)
+ current.index = (index % protosSize + protosSize) % protosSize;
+
+ DBWriteContactSettingWord(NULL, "MyDetails", "ProtocolNumber", current.index);
+}
+
+Protocol * GetCurrentProtocol(bool createIfDontExist)
+{
+ if (createIfDontExist && current.index >= 0 && current.proto == NULL)
+ {
+ std::vector<PROTOACCOUNT *> protos;
+ GetAccounts(&protos);
+
+ int protosSize = protos.size();
+ if (current.index >= protosSize)
+ {
+ current.index = -1;
+ return NULL;
+ }
+
+ current.proto = new Protocol(protos[current.index]->szModuleName);
+ }
+
+ return current.proto;
+}
+
+int GetCurrentProtocolIndex()
+{
+ return current.index;
+}
+
+
+Protocol GetProtocolByIndex(int index)
+{
+ std::vector<PROTOACCOUNT *> protos;
+ GetAccounts(&protos);
+ int protosSize = protos.size();
+
+ if (protosSize < 1)
+ return Protocol(NULL);
+
+ index = (index % protosSize + protosSize) % protosSize;
+ return Protocol(protos[index]->szModuleName);
+}
+
+
+Protocol GetProtocolByName(const char *moduleName)
+{
+ std::vector<PROTOACCOUNT *> protos;
+ GetAccounts(&protos);
+
+ int protosSize = (int) protos.size();
+ for(int i = 0; i < protosSize; ++i)
+ {
+ if (strcmp(protos[i]->szModuleName, moduleName) == 0)
+ return Protocol(protos[i]->szModuleName);
+ }
+
+ return Protocol(NULL);
+}
+
+
+ProtocolArray *protocols = NULL;
+
+
+void InitProtocolData()
+{
+ protocols = new ProtocolArray();
+}
+
+
+void DeInitProtocolData()
+{
+ current.Free();
+
+ delete protocols;
+ protocols = NULL;
+}
+
+
+// Protocol Class ///////////////////////////////////////////////////////////////////////////////////////////
+
+
+Protocol::Protocol(const char *aName)
+{
+ if (aName)
+ name = aName;
+
+ avatar_bmp = NULL;
+ status = 0;
+ custom_status = 0;
+ locked = false;
+ emails = 0;
+
+ // Initial value
+ UpdateAll();
+}
+
+Protocol::~Protocol()
+{
+}
+
+
+bool Protocol::IsValid()
+{
+ return !name.empty();
+}
+
+
+Protocol::operator bool ()
+{
+ return IsValid();
+}
+
+
+void Protocol::UpdateAll()
+{
+ status_initialized = false;
+ status_message_initialized = false;
+ nickname_initialized = false;
+ avatar_initialized = false;
+ locked_initialized = false;
+ emails_initialized = false;
+ listening_to_initialized = false;
+}
+
+
+int Protocol::Call(const char *service, WPARAM wParam, LPARAM lParam)
+{
+ return CallProtoService(name.c_str(), service, wParam, lParam);
+}
+
+
+bool Protocol::CanCall(const char *service)
+{
+ return ProtoServiceExists(name.c_str(), service) != 0;
+}
+
+
+std::string Protocol::GetDBSettingString(const char *key, const char *def)
+{
+ std::string result = def;
+
+ DBVARIANT dbv;
+ if (!DBGetContactSettingTString(0, name.c_str(), key, &dbv))
+ {
+ if (dbv.ptszVal != NULL && dbv.ptszVal[0] != 0)
+ result = dbv.ptszVal;
+
+ DBFreeVariant(&dbv);
+ }
+
+ return result;
+}
+
+
+const char * Protocol::GetName()
+{
+ return name.c_str();
+}
+
+
+const char * Protocol::GetDescription()
+{
+ if (description.empty())
+ {
+ PROTOACCOUNT *acc = ProtoGetAccount(name.c_str());
+
+ if (acc == NULL || acc->tszAccountName == NULL || acc->tszAccountName[0] == 0)
+ {
+ char tmp[1024];
+ Call(PS_GETNAME, sizeof(tmp), (LPARAM) tmp);
+ description = tmp;
+ }
+ else
+ {
+ if (mir_is_unicode())
+ {
+ char *tmp = mir_u2a((const wchar_t *) acc->tszAccountName);
+ description = tmp;
+ mir_free(tmp);
+ }
+ else
+ {
+ description = acc->tszAccountName;
+ }
+ }
+ }
+
+ return description.c_str();
+}
+
+void Protocol::UpdateStatus()
+{
+ status_initialized = true;
+
+ status = Call(PS_GETSTATUS);
+
+ if (status > ID_STATUS_OFFLINE && CanCall(PS_ICQ_GETCUSTOMSTATUS))
+ {
+ char *name_key = NULL;
+ char *message_key = NULL;
+
+ custom_status = Call(PS_ICQ_GETCUSTOMSTATUS, (WPARAM) &name_key, (LPARAM) &message_key);
+
+ // Fix fo jabber, that returns 0xbaadf00d here
+ if (custom_status < 0)
+ custom_status = 0;
+
+ custom_status_name_key = (name_key ? name_key : "");
+ custom_status_message_key = (message_key ? message_key : "");
+ }
+ else
+ {
+ custom_status = 0;
+ custom_status_name_key = "";
+ custom_status_message_key = "";
+ }
+
+ if (custom_status == 0)
+ {
+ status_name = (char *) CallService(MS_CLIST_GETSTATUSMODEDESCRIPTION, status, GCMDF_TCHAR);
+ }
+ else
+ {
+ status_name = "";
+
+ if (!custom_status_name_key.empty())
+ status_name = GetDBSettingString(custom_status_name_key.c_str());
+
+ if (!custom_status_message_key.empty())
+ {
+ std::string tmp = GetDBSettingString(custom_status_message_key.c_str());
+ if (!tmp.empty())
+ {
+ status_name += ": ";
+ status_name += tmp;
+ }
+ }
+ }
+
+ if (status_name.empty())
+ status_name = TranslateTS("<no status name>");
+}
+
+const char * Protocol::GetStatusName()
+{
+ if (!status_initialized)
+ UpdateStatus();
+
+ return status_name.c_str();
+}
+
+const char * Protocol::GetCustomStatusNameKey()
+{
+ if (!status_initialized)
+ UpdateStatus();
+
+ return custom_status_name_key.c_str();
+}
+
+const char * Protocol::GetCustomStatusMessageKey()
+{
+ if (!status_initialized)
+ UpdateStatus();
+
+ return custom_status_message_key.c_str();
+}
+
+int Protocol::GetStatus()
+{
+ if (!status_initialized)
+ UpdateStatus();
+
+ return status;
+}
+
+int Protocol::GetCustomStatus()
+{
+ if (!status_initialized)
+ UpdateStatus();
+
+ return custom_status;
+}
+
+
+void Protocol::SetStatus(int aStatus)
+{
+ char status_msg[256];
+
+ if (ServiceExists(MS_CS_SETSTATUSEX))
+ {
+ // :'(
+
+ // BEGIN From commomstatus.cpp (KeepStatus)
+ int i, count, pCount;
+ PROTOCOLDESCRIPTOR **protos;
+
+ pCount = 0;
+ CallService(MS_PROTO_ENUMPROTOCOLS,(WPARAM)&count,(LPARAM)&protos);
+ for(i=0;i<count;i++) {
+ if(protos[i]->type!=PROTOTYPE_PROTOCOL || CallProtoService(protos[i]->szName,PS_GETCAPS,PFLAGNUM_2,0)==0) continue;
+ pCount += 1;
+ }
+ // END From commomstatus.cpp (KeepStatus)
+
+
+ PROTOCOLSETTINGEX **pse = (PROTOCOLSETTINGEX **) mir_alloc0(pCount * sizeof(PROTOCOLSETTINGEX *));
+
+ for(i = 0; i < pCount; i++)
+ {
+ pse[i] = (PROTOCOLSETTINGEX *) mir_alloc0(sizeof(PROTOCOLSETTINGEX));
+ pse[i]->szName = "";
+ }
+
+ pse[0]->cbSize = sizeof(PROTOCOLSETTINGEX);
+ pse[0]->status = aStatus;
+ pse[0]->szName = (char *) name.c_str();
+
+ GetStatusMsg(aStatus, status_msg, sizeof(status_msg));
+ pse[0]->szMsg = status_msg;
+
+ CallService(MS_CS_SETSTATUSEX, (WPARAM) &pse, 0);
+
+ for(i = 0; i < pCount; i++)
+ mir_free(pse[i]);
+ mir_free(pse);
+ }
+ else
+ {
+ Call(PS_SETSTATUS, aStatus);
+
+ if (CanSetStatusMsg(aStatus))
+ {
+ char status_msg[MS_MYDETAILS_GETMYSTATUSMESSAGE_BUFFER_SIZE];
+ GetStatusMsg(aStatus, status_msg, sizeof(status_msg));
+ SetStatusMsg(aStatus, status_msg);
+ }
+ }
+}
+
+
+bool Protocol::CanGetStatusMsg()
+{
+ return CanGetStatusMsg(GetStatus());
+}
+
+bool Protocol::CanGetStatusMsg(int aStatus)
+{
+ return (Call(PS_GETCAPS, PFLAGNUM_1) & PF1_MODEMSGSEND) != 0
+ && (Call(PS_GETCAPS, (WPARAM)PFLAGNUM_3) & Proto_Status2Flag(aStatus));
+}
+
+
+bool Protocol::CanSetStatusMsg()
+{
+ return CanSetStatusMsg(GetStatus()) // <- Simple away handled by this one
+ || ServiceExists(MS_NAS_INVOKESTATUSWINDOW);
+
+}
+
+bool Protocol::CanSetStatusMsg(int aStatus)
+{
+ return CanGetStatusMsg(aStatus);
+}
+
+void Protocol::GetStatusMsg(int aStatus, char *msg, size_t msg_size)
+{
+ if (!CanGetStatusMsg())
+ {
+ lstrcpyn(msg, "", msg_size);
+ return;
+ }
+
+ bool isCurrentStatus = (aStatus == GetStatus());
+
+ if (isCurrentStatus && CanCall(PS_GETMYAWAYMSG))
+ {
+ char *tmp = (char *) Call(PS_GETMYAWAYMSG);
+ lstrcpyn(msg, tmp == NULL ? "" : tmp, msg_size);
+ }
+ else if (isCurrentStatus && ServiceExists(MS_SA_ISSARUNNING) && CallService(MS_SA_ISSARUNNING, 0, 0))
+ {
+ char *tmp = (char *) CallService(MS_AWAYMSG_GETSTATUSMSG, (WPARAM) ID_STATUS_CURRENT, (LPARAM) name.c_str());
+
+ if (tmp != NULL)
+ {
+ lstrcpyn(msg, tmp, msg_size);
+ mir_free(tmp);
+ }
+ else lstrcpyn(msg, "", msg_size);
+
+ }
+ else if (ServiceExists(MS_NAS_GETSTATE))
+ {
+ NAS_PROTOINFO pi;
+
+ ZeroMemory(&pi, sizeof(pi));
+ pi.cbSize = sizeof(NAS_PROTOINFO);
+ pi.szProto = (char *) name.c_str();
+ pi.status = (isCurrentStatus ? 0 : aStatus);
+ pi.szMsg = NULL;
+
+ if (CallService(MS_NAS_GETSTATE, (WPARAM) &pi, 1) == 0)
+ {
+ if (pi.szMsg == NULL)
+ {
+ pi.szProto = NULL;
+
+ if (CallService(MS_NAS_GETSTATE, (WPARAM) &pi, 1) == 0)
+ {
+ if (pi.szMsg != NULL)
+ {
+ lstrcpyn(msg, pi.szMsg, msg_size);
+ mir_free(pi.szMsg);
+ }
+ else lstrcpyn(msg, "", msg_size);
+ }
+ else lstrcpyn(msg, "", msg_size);
+ }
+ else // if (pi.szMsg != NULL)
+ {
+ lstrcpyn(msg, pi.szMsg, msg_size);
+ mir_free(pi.szMsg);
+ }
+ }
+ else lstrcpyn(msg, "", msg_size);
+
+ if (ServiceExists(MS_VARS_FORMATSTRING))
+ {
+ char *tmp = variables_parse(msg, NULL, NULL);
+ lstrcpyn(msg, tmp, msg_size);
+ variables_free(tmp);
+ }
+ }
+ // TODO: Remove when removing old NAS services support
+ else if (ServiceExists("NewAwaySystem/GetState"))
+ {
+ NAS_PROTOINFO pi, *pii;
+
+ ZeroMemory(&pi, sizeof(pi));
+ pi.cbSize = sizeof(NAS_PROTOINFO);
+ pi.szProto = (char *) name.c_str();
+ pi.status = (isCurrentStatus ? 0 : aStatus);
+ pi.szMsg = NULL;
+
+ pii = &pi;
+
+ if (CallService("NewAwaySystem/GetState", (WPARAM) &pii, 1) == 0)
+ {
+ if (pi.szMsg == NULL)
+ {
+ pi.szProto = NULL;
+
+ if (CallService("NewAwaySystem/GetState", (WPARAM) &pii, 1) == 0)
+ {
+ if (pi.szMsg != NULL)
+ {
+ lstrcpyn(msg, pi.szMsg, msg_size);
+ mir_free(pi.szMsg);
+ }
+ else lstrcpyn(msg, "", msg_size);
+ }
+ else lstrcpyn(msg, "", msg_size);
+ }
+ else // if (pi.szMsg != NULL)
+ {
+ lstrcpyn(msg, pi.szMsg, msg_size);
+ mir_free(pi.szMsg);
+ }
+ }
+ else lstrcpyn(msg, "", msg_size);
+
+ if (ServiceExists(MS_VARS_FORMATSTRING))
+ {
+ char *tmp = variables_parse(msg, NULL, NULL);
+ lstrcpyn(msg, tmp, msg_size);
+ variables_free(tmp);
+ }
+ }
+ else if (ServiceExists(MS_AWAYMSG_GETSTATUSMSG))
+ {
+ char *tmp = (char *) CallService(MS_AWAYMSG_GETSTATUSMSG, (WPARAM)aStatus, 0);
+
+ if (tmp != NULL)
+ {
+ lstrcpyn(msg, tmp, msg_size);
+ mir_free(tmp);
+ }
+ else lstrcpyn(msg, "", msg_size);
+ }
+}
+
+void Protocol::UpdateStatusMsg()
+{
+ status_message_initialized = true;
+
+ TCHAR tmp[1024];
+ GetStatusMsg(GetStatus(), tmp, sizeof(tmp));
+
+ status_message = tmp;
+}
+
+const char * Protocol::GetStatusMsg()
+{
+ if (!status_message_initialized)
+ UpdateStatusMsg();
+
+ return status_message.c_str();
+}
+
+void Protocol::SetStatusMsg(const char *message)
+{
+ SetStatusMsg(GetStatus(), message);
+}
+
+void Protocol::SetStatusMsg(int aStatus, const char *message)
+{
+ if (!CanSetStatusMsg(aStatus))
+ return;
+
+ if (ServiceExists(MS_NAS_SETSTATE))
+ {
+ NAS_PROTOINFO pi = {0}, *pii;
+
+ pi.cbSize = sizeof(pi);
+ pi.szProto = (char *) name.c_str();
+ pi.szMsg = mir_strdup(message);
+ pi.status = aStatus;
+
+ pii = &pi;
+
+ CallService(MS_NAS_SETSTATE, (WPARAM) &pii, 1);
+ }
+ else
+ {
+ Call(PS_SETAWAYMSG, (WPARAM) aStatus, (LPARAM) message);
+ }
+}
+
+bool Protocol::HasAvatar()
+{
+ if (!avatar_initialized)
+ UpdateAvatar();
+
+ return avatar_bmp != NULL;
+}
+
+bool Protocol::CanGetAvatar()
+{
+ int caps = Call(PS_GETCAPS, PFLAGNUM_4);
+
+ if ((caps & PF4_AVATARS) == 0)
+ return false;
+
+ if (!ServiceExists(MS_AV_GETMYAVATAR))
+ return false;
+
+ return true;
+}
+
+void Protocol::UpdateAvatar()
+{
+ avatar_initialized = true;
+ avatar_file = "";
+ avatar_bmp = NULL;
+
+ // See if can get one
+ if (!CanGetAvatar())
+ return;
+
+ // Get HBITMAP from cache
+ AVATARCACHEENTRY *ace = (avatarCacheEntry *) CallService(MS_AV_GETMYAVATAR, 0, (LPARAM) name.c_str());
+ if (ace != NULL)
+ {
+ avatar_file = ace->szFilename;
+ avatar_bmp = ace->hbmPic;
+ }
+}
+
+const char * Protocol::GetAvatarFile()
+{
+ if (!avatar_initialized)
+ UpdateAvatar();
+
+ return avatar_file.c_str();
+}
+
+HBITMAP Protocol::GetAvatarImage()
+{
+ if (!avatar_initialized)
+ UpdateAvatar();
+
+ return avatar_bmp;
+}
+
+
+bool Protocol::CanGetNick()
+{
+ return ServiceExists(MS_CONTACT_GETCONTACTINFO) != FALSE;
+}
+
+int Protocol::GetNickMaxLength()
+{
+ if (CanCall(PS_GETMYNICKNAMEMAXLENGTH))
+ {
+ int ret = Call(PS_GETMYNICKNAMEMAXLENGTH);
+ if (ret <= 0)
+ ret = MS_MYDETAILS_GETMYNICKNAME_BUFFER_SIZE;
+ return ret;
+ }
+ else
+ return MS_MYDETAILS_GETMYNICKNAME_BUFFER_SIZE;
+}
+
+void Protocol::UpdateNick()
+{
+ nickname_initialized = true;
+ nickname = "";
+
+ // See if can get one
+ if (!CanGetNick())
+ return;
+
+ // Get it
+ CONTACTINFO ci;
+ ZeroMemory(&ci, sizeof(ci));
+ ci.cbSize = sizeof(ci);
+ ci.hContact = NULL;
+ ci.szProto = (char *) name.c_str();
+ ci.dwFlag = CNF_DISPLAY;
+
+#ifdef UNICODE
+ ci.dwFlag |= CNF_UNICODE;
+#endif
+
+ if (!CallService(MS_CONTACT_GETCONTACTINFO, 0, (LPARAM) & ci))
+ {
+ // CNF_DISPLAY always returns a string type
+ nickname = ci.pszVal;
+ mir_free(ci.pszVal);
+ }
+}
+
+const char * Protocol::GetNick()
+{
+ if (!nickname_initialized)
+ UpdateNick();
+
+ return nickname.c_str();
+}
+
+
+bool Protocol::CanSetNick()
+{
+ return CanCall(PS_SETMYNICKNAME) != 0;
+}
+
+
+void Protocol::SetNick(const char *nick)
+{
+ // See if can get one
+ if (!CanSetNick())
+ return;
+
+ if (nick == NULL)
+ return;
+
+ // Get it
+ Call(PS_SETMYNICKNAME, NULL, (LPARAM) nick);
+}
+
+
+bool Protocol::CanSetAvatar()
+{
+ return ServiceExists(MS_AV_SETMYAVATAR) != FALSE && ServiceExists(MS_AV_CANSETMYAVATAR) != FALSE &&
+ CallService(MS_AV_CANSETMYAVATAR, (WPARAM) name.c_str(), 0);
+}
+
+void Protocol::SetAvatar(const char *file_name)
+{
+ if (!CanSetAvatar())
+ return;
+
+ CallService(MS_AV_SETMYAVATAR, (WPARAM) name.c_str(), (LPARAM) file_name);
+}
+
+bool Protocol::CanGetListeningTo()
+{
+ return CanCall(PS_SET_LISTENINGTO) != 0;
+}
+
+bool Protocol::CanSetListeningTo()
+{
+ return CanGetListeningTo() && ServiceExists(MS_LISTENINGTO_ENABLE);
+}
+
+bool Protocol::ListeningToEnabled()
+{
+ return CanSetListeningTo() && CallService(MS_LISTENINGTO_ENABLED, (WPARAM) name.c_str(), 0) != 0;
+}
+
+void Protocol::UpdateListeningTo()
+{
+ listening_to_initialized = true;
+ listening_to = "";
+
+ if (!CanGetListeningTo())
+ return;
+
+ listening_to = GetDBSettingString("ListeningTo");
+}
+
+const char * Protocol::GetListeningTo()
+{
+ if (!listening_to_initialized)
+ UpdateListeningTo();
+
+ return listening_to.c_str();
+}
+
+void Protocol::UpdateLocked()
+{
+ locked_initialized = true;
+
+ locked = (DBGetContactSettingByte(NULL, name.c_str(), "LockMainStatus", 0) != 0);
+}
+
+bool Protocol::IsLocked()
+{
+ if (!locked_initialized)
+ UpdateLocked();
+
+ return locked;
+}
+
+bool Protocol::CanGetEmailCount()
+{
+ return CanCall(PS_GETUNREADEMAILCOUNT) != 0
+ && GetStatus() > ID_STATUS_OFFLINE;
+}
+
+void Protocol::UpdateEmailCount()
+{
+ emails_initialized = true;
+
+ if (!CanGetEmailCount())
+ emails = 0;
+ else
+ emails = max(0, Call(PS_GETUNREADEMAILCOUNT));
+}
+
+int Protocol::GetEmailCount()
+{
+ if (!emails_initialized)
+ UpdateEmailCount();
+
+ return emails;
+}
+
+
+// ProtocolDataArray Class /////////////////////////////////////////////////////////////////////////////
+
+
+ProtocolArray::ProtocolArray()
+{
+ GetDefaultNick();
+ GetDefaultAvatar();
+}
+
+int ProtocolArray::GetGlobalStatus()
+{
+ int status = CallService(MS_CLIST_GETSTATUSMODE, 0, 0);
+ if (status == ID_STATUS_CONNECTING)
+ status = ID_STATUS_OFFLINE;
+
+ return status;
+}
+
+bool ProtocolArray::CanSetAvatars()
+{
+ return ServiceExists(MS_AV_SETMYAVATAR) != FALSE;
+}
+
+void ProtocolArray::SetAvatars(const char *file_name)
+{
+ if (!CanSetAvatars())
+ return;
+
+ CallService(MS_AV_SETMYAVATAR, NULL, (WPARAM) file_name);
+}
+
+
+void ProtocolArray::SetNicks(const char *nick)
+{
+ if (nick == NULL || nick[0] == '\0')
+ return;
+
+ lstrcpyn(default_nick, nick, sizeof(default_nick));
+
+ DBWriteContactSettingString(0, MODULE_NAME, SETTING_DEFAULT_NICK, nick);
+
+ std::vector<Protocol> protos;
+ GetProtocols(&protos);
+
+ unsigned int protosSize = protos.size();
+ for (int i = 0; i < protosSize; ++i)
+ protos[i].SetNick(default_nick);
+}
+
+
+void ProtocolArray::SetStatus(int aStatus)
+{
+ CallService(MS_CLIST_SETSTATUSMODE, aStatus, 0);
+}
+
+void ProtocolArray::SetStatusMsgs(const char *message)
+{
+ for (int i = ID_STATUS_OFFLINE ; i <= ID_STATUS_IDLE; i++)
+ {
+ SetStatusMsgs(i, message);
+ }
+}
+
+void ProtocolArray::SetStatusMsgs(int status, const char *message)
+{
+ DBWriteContactSettingString(NULL,"SRAway",StatusModeToDbSetting(status,"Msg"),message);
+ if (!DBGetContactSettingByte(NULL,"SRAway",StatusModeToDbSetting(status,"UsePrev"),0))
+ {
+ // Save default also
+ DBWriteContactSettingString(NULL,"SRAway",StatusModeToDbSetting(status,"Default"),message);
+ }
+
+ std::vector<Protocol> protos;
+ GetProtocols(&protos);
+
+ unsigned int protosSize = protos.size();
+ for (int i = 0; i < protosSize; ++i)
+ {
+ if (protos[i].GetStatus() == status)
+ protos[i].SetStatusMsg(status, message);
+ }
+}
+
+
+void ProtocolArray::GetDefaultNick()
+{
+ DBVARIANT dbv;
+
+ if (!DBGetContactSettingTString(0, MODULE_NAME, SETTING_DEFAULT_NICK, &dbv))
+ {
+ lstrcpyn(default_nick, dbv.pszVal, sizeof(default_nick));
+ DBFreeVariant(&dbv);
+ }
+ else
+ {
+ default_nick[0] = '\0';
+ }
+}
+
+void ProtocolArray::GetDefaultAvatar()
+{
+ DBVARIANT dbv;
+
+ if (!DBGetContactSettingTString(0, "ContactPhoto", "File", &dbv))
+ {
+ lstrcpyn(default_avatar_file, dbv.pszVal, sizeof(default_avatar_file));
+ DBFreeVariant(&dbv);
+ }
+ else
+ {
+ default_avatar_file[0] = '\0';
+ }
+}
+
+char * ProtocolArray::GetDefaultStatusMsg()
+{
+ return GetDefaultStatusMsg(CallService(MS_CLIST_GETSTATUSMODE, 0, 0));
+}
+
+char * ProtocolArray::GetDefaultStatusMsg(int status)
+{
+ default_status_message[0] = '\0';
+
+ if (ServiceExists(MS_AWAYMSG_GETSTATUSMSG))
+ {
+ if (status == ID_STATUS_CONNECTING)
+ {
+ status = ID_STATUS_OFFLINE;
+ }
+
+ char *tmp = (char *) CallService(MS_AWAYMSG_GETSTATUSMSG, (WPARAM)status, 0);
+
+ if (tmp != NULL)
+ {
+ lstrcpyn(default_status_message, tmp, sizeof(default_status_message));
+ mir_free(tmp);
+ }
+ }
+
+ return default_status_message;
+}
+
+bool ProtocolArray::CanSetListeningTo()
+{
+ return ServiceExists(MS_LISTENINGTO_ENABLE) != 0;
+}
+
+bool ProtocolArray::ListeningToEnabled()
+{
+ return CanSetListeningTo() && CallService(MS_LISTENINGTO_ENABLED, 0, 0) != 0;
+}
+
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////
+// Helper functions
+
+static char *StatusModeToDbSetting(int status,const char *suffix)
+{
+ char *prefix;
+ static char str[64];
+
+ switch(status) {
+ case ID_STATUS_AWAY: prefix="Away"; break;
+ case ID_STATUS_NA: prefix="Na"; break;
+ case ID_STATUS_DND: prefix="Dnd"; break;
+ case ID_STATUS_OCCUPIED: prefix="Occupied"; break;
+ case ID_STATUS_FREECHAT: prefix="FreeChat"; break;
+ case ID_STATUS_ONLINE: prefix="On"; break;
+ case ID_STATUS_OFFLINE: prefix="Off"; break;
+ case ID_STATUS_INVISIBLE: prefix="Inv"; break;
+ case ID_STATUS_ONTHEPHONE: prefix="Otp"; break;
+ case ID_STATUS_OUTTOLUNCH: prefix="Otl"; break;
+ case ID_STATUS_IDLE: prefix="Idl"; break;
+ default: return NULL;
+ }
+ lstrcpyA(str,prefix); lstrcatA(str,suffix);
+ return str;
+}
+
+
diff --git a/Plugins/mydetails/data.h b/Plugins/mydetails/data.h
new file mode 100644
index 0000000..9c458b5
--- /dev/null
+++ b/Plugins/mydetails/data.h
@@ -0,0 +1,183 @@
+/*
+Copyright (C) 2005 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#ifndef __DATA_H__
+# define __DATA_H__
+
+
+//#include "protocol_config.h"
+
+class Protocol
+{
+ // Attributes ////////////
+ std::string name;
+
+ std::string description;
+
+ bool avatar_initialized;
+ std::string avatar_file;
+ HBITMAP avatar_bmp;
+
+ bool status_message_initialized;
+ std::string status_message;
+
+ bool nickname_initialized;
+ std::string nickname;
+
+ bool locked_initialized;
+ bool locked;
+
+ bool emails_initialized;
+ int emails;
+
+ bool listening_to_initialized;
+ std::string listening_to;
+
+ bool status_initialized;
+ std::string status_name;
+ std::string custom_status_name_key;
+ std::string custom_status_message_key;
+ int status;
+ int custom_status;
+
+public:
+ Protocol(const char *name);
+ ~Protocol();
+
+ bool IsValid();
+ operator bool ();
+
+ void UpdateAll();
+
+ const char * GetName();
+ const char * GetDescription();
+
+ const char * GetStatusName();
+ const char * GetCustomStatusNameKey();
+ const char * GetCustomStatusMessageKey();
+ int GetStatus();
+ int GetCustomStatus();
+ void SetStatus(int aStatus);
+
+ bool HasAvatar();
+ bool CanGetAvatar();
+ const char * GetAvatarFile();
+ HBITMAP GetAvatarImage();
+
+ bool CanSetAvatar();
+ void SetAvatar(const TCHAR *file_name);
+
+ bool CanGetNick();
+ const char * GetNick();
+ int GetNickMaxLength();
+ bool CanSetNick();
+ void SetNick(const TCHAR *nick);
+
+ bool CanGetListeningTo();
+ bool CanSetListeningTo();
+ bool ListeningToEnabled();
+ const char * GetListeningTo();
+
+ bool CanGetStatusMsg();
+ bool CanGetStatusMsg(int aStatus);
+ void GetStatusMsg(int aStatus, TCHAR *msg, size_t msg_size);
+ const char * GetStatusMsg();
+
+ bool CanSetStatusMsg();
+ bool CanSetStatusMsg(int aStatus);
+ void SetStatusMsg(const TCHAR *message);
+ void SetStatusMsg(int aStatus, const TCHAR *message);
+
+ bool IsLocked();
+
+ bool CanGetEmailCount();
+ int GetEmailCount();
+
+ int Call(const char *service, WPARAM wParam = 0, LPARAM lParam = 0);
+ bool CanCall(const char *service);
+
+ std::string GetDBSettingString(const char *key, const char *def = "");
+
+private:
+ void UpdateStatus();
+ void UpdateAvatar();
+ void UpdateNick();
+ void UpdateListeningTo();
+ void UpdateStatusMsg();
+ void UpdateLocked();
+ void UpdateEmailCount();
+};
+
+
+
+class ProtocolArray
+{
+public:
+ TCHAR default_nick[256];
+ TCHAR default_avatar_file[256];
+ TCHAR default_status_message[256];
+
+ // Methods ///////////////
+
+ ProtocolArray();
+
+ bool CanSetAvatars();
+ void SetAvatars(const TCHAR *file);
+
+ void SetNicks(const TCHAR *nick);
+
+ void SetStatus(int aStatus);
+
+ void SetStatusMsgs(const TCHAR *message);
+ void SetStatusMsgs(int status, const TCHAR *message);
+
+ int GetGlobalStatus();
+
+ void GetDefaultNick(); // Copy to cache
+ void GetDefaultAvatar(); // Copy to cache
+ TCHAR * GetDefaultStatusMsg(); // Copy to cache
+ TCHAR * GetDefaultStatusMsg(int status);
+
+ bool CanSetListeningTo();
+ bool ListeningToEnabled();
+};
+
+extern ProtocolArray *protocols;
+
+
+void SetCurrentProtocol(int index);
+Protocol * GetCurrentProtocol(bool createIfDontExist = true);
+int GetCurrentProtocolIndex();
+
+void GetProtocols(std::vector<Protocol> *result);
+int GetProtocolIndexByName(const char *moduleName);
+int GetNumProtocols();
+Protocol GetProtocolByIndex(int index);
+Protocol GetProtocolByName(const char *proto);
+
+void InitProtocolData();
+void DeInitProtocolData();
+
+
+
+
+
+
+#endif // __DATA_H__
diff --git a/Plugins/mydetails/data/Skins/Default/MyDetails.msk b/Plugins/mydetails/data/Skins/Default/MyDetails.msk
new file mode 100644
index 0000000..93c46b4
--- /dev/null
+++ b/Plugins/mydetails/data/Skins/Default/MyDetails.msk
@@ -0,0 +1,399 @@
+function configure()
+{
+ // Options for this skin
+
+ opts.align_right.description = "Align to right"
+ opts.align_right.type = CHECKBOX
+ opts.align_right.value = false
+
+ opts.show_avatar.description = "Show avatar"
+ opts.show_avatar.type = CHECKBOX
+ opts.show_avatar.value = true
+
+ opts.avatar_allow_grow.description = "Allow avatar to grow (be bigger than original image)"
+ opts.avatar_allow_grow.type = CHECKBOX
+ opts.avatar_allow_grow.value = false
+
+ opts.avatar_use_fixed_size.description = "Use fixed size avatar"
+ opts.avatar_use_fixed_size.type = CHECKBOX
+ opts.avatar_use_fixed_size.value = false
+
+ opts.avatar_fixed_size.description = "Avatar fixed size (pixels)"
+ opts.avatar_fixed_size.type = NUMBER
+ opts.avatar_fixed_size.value = 30
+ opts.avatar_fixed_size.min = 1
+ opts.avatar_fixed_size.max = 255
+
+ opts.show_protocol.description = "Show protocol"
+ opts.show_protocol.type = CHECKBOX
+ opts.show_protocol.value = true
+
+ opts.show_email.description = "Show unread mail count"
+ opts.show_email.type = CHECKBOX
+ opts.show_email.value = true
+
+ opts.show_status.description = "Show status"
+ opts.show_status.type = CHECKBOX
+ opts.show_status.value = true
+
+ opts.show_status_msg.description = "Show status message"
+ opts.show_status_msg.type = CHECKBOX
+ opts.show_status_msg.value = true
+
+ opts.show_listening.description = "Show listening to"
+ opts.show_listening.type = CHECKBOX
+ opts.show_listening.value = true
+
+ opts.show_protocol_cycle.description = "Show protocol cycle buttons"
+ opts.show_protocol_cycle.type = CHECKBOX
+ opts.show_protocol_cycle.value = false
+
+ opts.use_under_avatar.description = "Use free space (under avatar) to other texts"
+ opts.use_under_avatar.type = CHECKBOX
+ opts.use_under_avatar.value = true
+
+ opts.border_left.description = "Left border"
+ opts.border_left.type = NUMBER
+ opts.border_left.value = 8
+ opts.border_left.min = 0
+ opts.border_left.max = 100
+
+ opts.border_top.description = "Top border"
+ opts.border_top.type = NUMBER
+ opts.border_top.value = 8
+ opts.border_top.min = 0
+ opts.border_top.max = 100
+
+ opts.border_right.description = "Right border"
+ opts.border_right.type = NUMBER
+ opts.border_right.value = 8
+ opts.border_right.min = 0
+ opts.border_right.max = 100
+
+ opts.border_bottom.description = "Bottom border"
+ opts.border_bottom.type = NUMBER
+ opts.border_bottom.value = 8
+ opts.border_bottom.min = 0
+ opts.border_bottom.max = 100
+
+ // Default fonts
+
+ nickname.font.face = "Tahoma"
+ nickname.font.size = 13
+ nickname.font.bold = true
+ nickname.font.color = RGB(0,0,0)
+
+ protocol.font.face = "Tahoma"
+ protocol.font.size = 8
+ protocol.font.color = RGB(0,0,0)
+
+ email.font.face = "Tahoma"
+ email.font.size = 8
+ email.font.color = RGB(0,0,0)
+
+ status_name.font.face = "Tahoma"
+ status_name.font.size = 8
+ status_name.font.color = RGB(0,0,0)
+
+ status_msg.font.face = "Tahoma"
+ status_msg.font.size = 8
+ status_msg.font.italic = true
+ status_msg.font.color = RGB(150,150,150)
+
+ listening.font.face = "Tahoma"
+ listening.font.size = 8
+ listening.font.italic = true
+ listening.font.color = RGB(150,150,150)
+}
+
+function valign_center(top)
+{
+ var height = 0
+ for(var i = 1; i < arguments.length; i++)
+ height = Math.max(height, arguments[i].height)
+ for(var i = 1; i < arguments.length; i++)
+ arguments[i].top = top + (height - arguments[i].height)/2
+ return top + height
+}
+
+// Resize a field, keeping its aspect ratio
+function resize(field, maxWidth, maxHeight, allowGrow)
+{
+ if (allowGrow == null)
+ allowGrow = true
+
+ var factor = Math.min(maxWidth / field.width, maxHeight / field.height)
+ if (!allowGrow && factor >= 1)
+ return
+
+ field.width *= factor
+ field.height *= factor
+}
+
+function draw()
+{
+ // Default texts
+ if (nickname.enabled && nickname.text == "")
+ nickname.text = "<no nickname>"
+ if (status_msg.enabled && status_msg.text == "")
+ status_msg.text = "<no status message>"
+ if (listening.enabled && listening.text == "")
+ listening.text = "<nothing playing>"
+
+ if (info.protocol.locked)
+ status_name.text += " (locked)"
+
+
+ // ToolTips
+ nickname.toolTip = nickname.text
+ protocol.toolTip = protocol.text
+ status_icon.toolTip = status_name.toolTip = status_name.text
+ status_msg.toolTip = status_msg.text
+ listening_icon.toolTip = listening.toolTip = listening.text
+ next_proto.toolTip = "Show next protocol"
+ prev_proto.toolTip = "Show previous protocol"
+ email_icon.toolTip = email.toolTip = "Unread Email Count: " + email.text
+
+
+ // Borders
+ window.borders.left = opts.border_left
+ window.borders.top = opts.border_top
+ window.borders.right = opts.border_right
+ window.borders.bottom = opts.border_bottom
+
+
+ // Visible
+ nickname.visible = true
+ prev_proto.visible = next_proto.visible = opts.show_protocol_cycle && !IsEmpty(next_proto, prev_proto)
+ avatar.visible = opts.show_avatar && avatar.enabled && !IsEmpty(avatar)
+ protocol.visible = opts.show_protocol && protocol.enabled
+ status_icon.visible = status_name.visible = opts.show_status && status_name.enabled
+ status_msg.visible = opts.show_status_msg && !IsEmpty(status_msg)
+ listening_icon.visible = listening.visible = opts.show_listening && !IsEmpty(listening_icon, listening)
+ email_icon.visible = email.visible = opts.show_email && email.enabled && !IsEmpty(email_icon, email) && email.text > 0
+
+
+ // Space to draw the frame around
+ var BORDER_SPACE = 2
+ nickname.borders = BORDER_SPACE
+ protocol.borders = BORDER_SPACE
+ status_icon.borders = BORDER_SPACE
+ status_name.borders = BORDER_SPACE
+ status_msg.borders = BORDER_SPACE
+ listening_icon.borders = BORDER_SPACE
+ listening.borders = BORDER_SPACE
+ email_icon.borders = BORDER_SPACE
+ email.borders = BORDER_SPACE
+
+
+ if (avatar.visible)
+ {
+ if (opts.avatar_use_fixed_size)
+ resize(avatar, opts.avatar_fixed_size, opts.avatar_fixed_size, opts.avatar_allow_grow)
+ else
+ resize(avatar, window.width/2.5, window.height - (!info.resize_frame && prev_proto.visible ? prev_proto.height : 0), opts.avatar_allow_grow)
+ }
+
+ if (!info.resize_frame && prev_proto.visible)
+ {
+ prev_proto.left = 0
+ prev_proto.bottom = window.height
+
+ next_proto.right = window.width
+ next_proto.bottom = window.height
+ }
+
+ var avatar_bottom = avatar.bottom
+ if (opts.show_avatar && opts.avatar_use_fixed_size)
+ avatar_bottom = opts.avatar_fixed_size
+
+ var top = 0
+
+ if (opts.align_right)
+ {
+ // Align
+ nickname.hAlign = RIGHT
+ protocol.hAlign = RIGHT
+ status_name.hAlign = RIGHT
+ status_msg.hAlign = RIGHT
+ listening.hAlign = RIGHT
+
+
+ var right = window.right
+
+ function updateTopRight(val)
+ {
+ top = val
+ if (opts.use_under_avatar && top > avatar_bottom)
+ right = window.right
+ }
+
+ if (avatar.visible)
+ {
+ avatar.right = window.right
+ avatar.top = 0
+
+ right = avatar.left - 6
+ }
+
+ if (opts.show_avatar && opts.avatar_use_fixed_size)
+ right = window.right - opts.avatar_fixed_size - 6
+
+ nickname.right = right
+ nickname.top = top
+
+ updateTopRight(nickname.bottom)
+
+ if (protocol.visible)
+ {
+ protocol.right = right
+ protocol.top = top
+
+ if (email.visible)
+ {
+ email_icon.right = protocol.left - 10
+ email.right = email_icon.left
+
+ var bottom = valign_center(top, protocol, email_icon, email)
+ updateTopRight(bottom)
+ }
+ else
+ updateTopRight(protocol.bottom)
+ }
+ else if (email.visible)
+ {
+ email_icon.borders.left = 0
+ email_icon.right = right
+
+ email.right = email_icon.left
+
+ var bottom = valign_center(top, email_icon, email)
+ updateTopRight(bottom)
+ }
+
+ if (status_name.visible)
+ {
+ status_icon.right = right
+ status_name.right = status_icon.left
+
+ var bottom = valign_center(top, status_icon, status_name)
+ updateTopRight(bottom)
+ }
+
+ if (status_msg.visible)
+ {
+ status_msg.right = right
+ status_msg.top = top
+
+ updateTopRight(status_msg.bottom)
+ }
+
+ if (listening.visible)
+ {
+ listening_icon.borders.left = 0
+ listening_icon.right = right
+
+ listening.right = listening_icon.left
+
+ var bottom = valign_center(top, listening_icon, listening)
+ updateTopRight(bottom)
+ }
+ }
+ else
+ {
+ var left = 0
+
+ function updateTopLeft(val)
+ {
+ top = val
+ if (opts.use_under_avatar && top > avatar_bottom)
+ left = 0
+ }
+
+ if (avatar.visible)
+ {
+ avatar.left = 0
+ avatar.top = 0
+
+ left = avatar.right + 6
+ }
+
+ if (opts.show_avatar && opts.avatar_use_fixed_size)
+ left = opts.avatar_fixed_size + 6
+
+ nickname.left = left
+ nickname.top = top
+
+ updateTopLeft(nickname.bottom)
+
+ if (protocol.visible)
+ {
+ protocol.left = left
+ protocol.top = top
+
+ if (email.visible)
+ {
+ email_icon.left = protocol.right + 10
+ email.left = email_icon.right
+
+ var bottom = valign_center(top, protocol, email_icon, email)
+ updateTopLeft(bottom)
+ }
+ else
+ updateTopLeft(protocol.bottom)
+ }
+ else if (email.visible)
+ {
+ email_icon.borders.right = 0
+ email_icon.left = left
+
+ email.left = email_icon.right
+
+ var bottom = valign_center(top, email_icon, email)
+ updateTopLeft(bottom)
+ }
+
+ if (status_name.visible)
+ {
+ status_icon.left = left
+ status_name.left = status_icon.right
+
+ var bottom = valign_center(top, status_icon, status_name)
+ updateTopLeft(bottom)
+ }
+
+ if (status_msg.visible)
+ {
+ status_msg.left = left
+ status_msg.top = top
+
+ updateTopLeft(status_msg.bottom)
+ }
+
+ if (listening.visible)
+ {
+ listening_icon.borders.right = 0
+ listening_icon.left = left
+
+ listening.borders.left = 0
+ listening.left = listening_icon.right
+
+ var bottom = valign_center(top, listening_icon, listening)
+ updateTopLeft(bottom)
+ }
+ }
+
+ if (info.resize_frame)
+ {
+ if (prev_proto.visible)
+ {
+ top = Math.max(avatar.bottom, top)
+
+ prev_proto.left = 0
+ prev_proto.top = top
+
+ next_proto.right = window.width
+ next_proto.top = top
+ }
+ }
+} \ No newline at end of file
diff --git a/Plugins/mydetails/data/Skins/Pidgin/MyDetails.msk b/Plugins/mydetails/data/Skins/Pidgin/MyDetails.msk
new file mode 100644
index 0000000..8929589
--- /dev/null
+++ b/Plugins/mydetails/data/Skins/Pidgin/MyDetails.msk
@@ -0,0 +1,59 @@
+function configure()
+{
+ // Default fonts
+
+ status_msg.font.face = "Tahoma"
+ status_msg.font.size = 8
+ status_msg.font.color = RGB(0,0,0)
+}
+
+function draw()
+{
+ if (status_msg.text == "")
+ status_msg.text = status_name.text
+
+ // ToolTips
+ status_icon.toolTip = protocol.text + " : " + status_name.text
+ if (info.protocol.locked)
+ status_icon.toolTip += " (locked)"
+ if (email.text > 0)
+ status_icon.toolTip += " [" + email.text + " emails]"
+
+ status_msg.toolTip = status_msg.text
+
+ // Borders
+ window.borders = 10
+
+ // Visible
+ status_msg.visible = true
+ avatar.visible = true
+ status_icon.visible = true
+
+ status_name.visible = false
+ protocol.visible = false
+ nickname.visible = false
+ prev_proto.visible = next_proto.visible = false
+ listening_icon.visible = listening.visible = false
+ email_icon.visible = email.visible = false
+
+ // Space to draw the frame around
+ var BORDER_SPACE = 2
+ status_icon.borders = BORDER_SPACE
+ status_msg.borders = BORDER_SPACE
+ status_name.borders = BORDER_SPACE
+
+ // Positions
+ var HEIGHT = 45
+
+ avatar.right = window.right
+ avatar.top = 0
+ avatar.width = HEIGHT
+ avatar.height = HEIGHT
+
+ status_icon.left = 0
+ status_icon.top = (HEIGHT - status_icon.height) / 2
+
+ status_msg.left = status_icon.right + 5
+ status_msg.top = (HEIGHT - status_msg.height) / 2
+ status_msg.right = avatar.left - 5
+}
diff --git a/Plugins/mydetails/frame.cpp b/Plugins/mydetails/frame.cpp
new file mode 100644
index 0000000..d5b609a
--- /dev/null
+++ b/Plugins/mydetails/frame.cpp
@@ -0,0 +1,2428 @@
+/*
+Copyright (C) 2005 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#include "commons.h"
+#include "frame.h"
+#include "wingdi.h"
+#include "winuser.h"
+#include <m_skin_eng.h>
+
+
+// Prototypes /////////////////////////////////////////////////////////////////////////////////////
+
+
+#define WINDOW_NAME_PREFIX "mydetails_window"
+#define WINDOW_CLASS_NAME "MyDetailsFrame"
+#define CONTAINER_CLASS_NAME "MyDetailsFrameContainer"
+
+#define ID_FRAME_TIMER 1011
+#define ID_RECALC_TIMER 1012
+#define ID_STATUSMESSAGE_TIMER 1013
+
+#define RECALC_TIME 500
+
+#define IDC_HAND MAKEINTRESOURCE(32649)
+
+
+// Messages
+#define MWM_REFRESH (WM_USER+10)
+#define MWM_REFRESH_DATA (WM_USER+18)
+
+
+HWND hwnd_frame = NULL;
+HWND hwnd_container = NULL;
+
+int frame_id = -1;
+
+HANDLE hMenuShowHideFrame = 0;
+
+int CreateFrame();
+void FixMainMenu();
+void UpdateFrameData();
+void RedrawFrame();
+
+
+// used when no multiwindow functionality available
+BOOL MyDetailsFrameVisible();
+void SetMyDetailsFrameVisible(BOOL visible);
+int ShowHideMenuFunc(WPARAM wParam, LPARAM lParam);
+int ShowFrameFunc(WPARAM wParam, LPARAM lParam);
+int HideFrameFunc(WPARAM wParam, LPARAM lParam);
+int ShowHideFrameFunc(WPARAM wParam, LPARAM lParam);
+
+
+
+LRESULT CALLBACK FrameContainerWindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
+LRESULT CALLBACK FrameWindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
+void SetCycleTime();
+void SetCycleTime(HWND hwnd);
+void SetStatusMessageRefreshTime();
+void SetStatusMessageRefreshTime(HWND hwnd);
+int SettingsChangedHook(WPARAM wParam, LPARAM lParam);
+int AvatarChangedHook(WPARAM wParam, LPARAM lParam);
+int ProtoAckHook(WPARAM wParam, LPARAM lParam);
+int SmileyAddOptionsChangedHook(WPARAM wParam,LPARAM lParam);
+int ListeningtoEnableStateChangedHook(WPARAM wParam,LPARAM lParam);
+int AccListChanged(WPARAM wParam, LPARAM lParam);
+
+
+void ExternalRect(RECT &ret, const RECT r1, const RECT r2);
+bool InsideRect(const POINT &p, const RECT &r);
+
+
+int operator==(const RECT& left, const RECT& right)
+{
+ return left.left == right.left && left.right == right.right
+ && left.top == right.top && left.bottom == right.bottom;
+}
+
+class ToolTipArea
+{
+public:
+ ToolTipArea() : hwndTT(0), hwndParent(0) { memset(&rc, 0, sizeof(rc)); }
+ ~ToolTipArea() { removeTooltip(); }
+
+ void createTooltip(HWND hwnd, const RECT &rc, const TCHAR *text)
+ {
+ if (text == NULL || text[0] == 0)
+ {
+ removeTooltip();
+ return;
+ }
+
+ this->text = text;
+
+ if (this->rc == rc && hwndParent == hwnd && hwndTT != NULL)
+ return;
+
+ removeTooltip();
+
+ this->rc = rc;
+ this->hwndParent = hwnd;
+ this->hwndTT = CreateTooltip(this->hwndParent, this->rc);
+ }
+
+ void removeTooltip()
+ {
+ if (hwndTT == NULL)
+ return;
+
+ DestroyWindow(hwndTT);
+ hwndTT = NULL;
+ hwndParent = NULL;
+ }
+
+ const TCHAR * getTextFor(HWND hwndFrom)
+ {
+ if (hwndTT == NULL || hwndTT != hwndFrom)
+ return NULL;
+ return text.c_str();
+ }
+
+
+private:
+
+ HWND hwndTT;
+ RECT rc;
+ HWND hwndParent;
+ std::tstring text;
+
+ HWND CreateTooltip(HWND hwnd, RECT &rect)
+ {
+ // struct specifying control classes to register
+ INITCOMMONCONTROLSEX iccex;
+ HWND hwndTT; // handle to the ToolTip control
+ // struct specifying info about tool in ToolTip control
+ TOOLINFO ti;
+ unsigned int uid = 0; // for ti initialization
+
+ // Load the ToolTip class from the DLL.
+ iccex.dwSize = sizeof(iccex);
+ iccex.dwICC = ICC_BAR_CLASSES;
+
+ if(!InitCommonControlsEx(&iccex))
+ return NULL;
+
+ /* CREATE A TOOLTIP WINDOW */
+ hwndTT = CreateWindowEx(WS_EX_TOPMOST,
+ TOOLTIPS_CLASS,
+ NULL,
+ WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP,
+ CW_USEDEFAULT,
+ CW_USEDEFAULT,
+ CW_USEDEFAULT,
+ CW_USEDEFAULT,
+ hwnd,
+ NULL,
+ hInst,
+ NULL
+ );
+
+ /* Gives problem with mToolTip
+ SetWindowPos(hwndTT,
+ HWND_TOPMOST,
+ 0,
+ 0,
+ 0,
+ 0,
+ SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
+ */
+
+ /* INITIALIZE MEMBERS OF THE TOOLINFO STRUCTURE */
+ ti.cbSize = sizeof(TOOLINFO);
+ ti.uFlags = TTF_SUBCLASS;
+ ti.hwnd = hwnd;
+ ti.hinst = hInst;
+ ti.uId = uid;
+ ti.lpszText = LPSTR_TEXTCALLBACK;
+ // ToolTip control will cover the whole window
+ ti.rect.left = rect.left;
+ ti.rect.top = rect.top;
+ ti.rect.right = rect.right;
+ ti.rect.bottom = rect.bottom;
+
+ /* SEND AN ADDTOOL MESSAGE TO THE TOOLTIP CONTROL WINDOW */
+ SendMessage(hwndTT, TTM_ADDTOOL, 0, (LPARAM) (LPTOOLINFO) &ti);
+ SendMessage(hwndTT, TTM_SETDELAYTIME, (WPARAM) (DWORD) TTDT_AUTOPOP, (LPARAM) MAKELONG(24 * 60 * 60 * 1000, 0));
+
+ return hwndTT;
+ }
+};
+
+
+struct SimpleItem
+{
+ RECT rc;
+ bool draw;
+ bool mouseOver;
+ ToolTipArea tt;
+ bool alignRight;
+
+ SimpleItem() : draw(FALSE), mouseOver(FALSE), alignRight(FALSE)
+ {
+ memset(&rc, 0, sizeof(rc));
+ }
+ virtual ~SimpleItem() {}
+
+ virtual void hide()
+ {
+ draw = false;
+ mouseOver = false;
+ tt.removeTooltip();
+ }
+
+ virtual void update(HWND hwnd, SkinFieldState *item)
+ {
+ draw = item->isVisible();
+ alignRight = ( item->getHorizontalAlign() == SKN_HALIGN_RIGHT );
+
+ if (draw)
+ {
+ rc = item->getRect();
+ tt.createTooltip(hwnd, rc, item->getToolTip());
+ }
+ else
+ {
+ tt.removeTooltip();
+ }
+ }
+
+ virtual bool hitTest(const POINT &p)
+ {
+ return draw && InsideRect(p, rc);
+ }
+
+ virtual const TCHAR * getToolTipFor(HWND hwndFrom)
+ {
+ return tt.getTextFor(hwndFrom);
+ }
+
+ bool setMouseOver(POINT *mousePos)
+ {
+ bool over = (mousePos != NULL && hitTest(*mousePos));
+
+ if (mouseOver == over)
+ return FALSE;
+
+ mouseOver = over;
+ return TRUE;
+ }
+
+};
+
+struct IconAndItem : public SimpleItem
+{
+ RECT rcIcon;
+ RECT rcItem;
+ BOOL drawIcon;
+ BOOL drawItem;
+ ToolTipArea ttIcon;
+
+ IconAndItem() : drawIcon(FALSE), drawItem(FALSE)
+ {
+ memset(&rcIcon, 0, sizeof(rcIcon));
+ memset(&rcItem, 0, sizeof(rcItem));
+ }
+ virtual ~IconAndItem() {}
+
+ virtual void hide()
+ {
+ SimpleItem::hide();
+ drawIcon = FALSE;
+ drawItem = FALSE;
+ }
+
+ virtual void update(HWND hwnd, SkinIconFieldState *icon, SkinTextFieldState *item)
+ {
+ drawIcon = icon->isVisible();
+ drawItem = item->isVisible();
+ alignRight = ( item->getHorizontalAlign() == SKN_HALIGN_RIGHT );
+
+ draw = drawIcon || drawItem;
+ if (draw)
+ {
+ if (drawIcon)
+ rcIcon = icon->getRect();
+ if (drawItem)
+ rcItem = item->getRect();
+
+ if (drawIcon && drawItem)
+ ExternalRect(rc, rcIcon, rcItem);
+ else if (drawIcon)
+ rc = rcIcon;
+ else // if (drawItem)
+ rc = rcItem;
+ }
+
+ if (drawItem)
+ tt.createTooltip(hwnd, rcItem, item->getToolTip());
+ else
+ tt.removeTooltip();
+
+ if (drawIcon)
+ ttIcon.createTooltip(hwnd, rcIcon, icon->getToolTip());
+ else
+ ttIcon.removeTooltip();
+ }
+
+ virtual const TCHAR * getToolTipFor(HWND hwndFrom)
+ {
+ const TCHAR * ret = tt.getTextFor(hwndFrom);
+
+ if (ret == NULL)
+ ret = ttIcon.getTextFor(hwndFrom);
+
+ return ret;
+ }
+};
+
+
+struct MyDetailsFrameData
+{
+ std::vector<SimpleItem*> items;
+ SimpleItem proto;
+ SimpleItem proto_cycle_next;
+ SimpleItem proto_cycle_prev;
+ SimpleItem avatar;
+ SimpleItem nick;
+ IconAndItem status;
+ SimpleItem away_msg;
+ IconAndItem listening_to;
+ IconAndItem email;
+
+ bool showing_menu;
+
+ bool tracking_exit;
+
+ MyDetailsFrameData()
+ : showing_menu(false)
+ , tracking_exit(false)
+ {
+ items.push_back(&proto);
+ items.push_back(&proto_cycle_next);
+ items.push_back(&proto_cycle_prev);
+ items.push_back(&avatar);
+ items.push_back(&nick);
+ items.push_back(&status);
+ items.push_back(&away_msg);
+ items.push_back(&listening_to);
+ items.push_back(&email);
+ }
+};
+
+
+
+// Functions //////////////////////////////////////////////////////////////////////////////////////
+
+void InitFrames()
+{
+ InitContactListSmileys();
+
+ CreateFrame();
+
+ HookEvent(ME_DB_CONTACT_SETTINGCHANGED, SettingsChangedHook);
+ HookEvent(ME_AV_MYAVATARCHANGED, AvatarChangedHook);
+ HookEvent(ME_PROTO_ACK, ProtoAckHook);
+ HookEvent(ME_SMILEYADD_OPTIONSCHANGED,SmileyAddOptionsChangedHook);
+ HookEvent(ME_LISTENINGTO_ENABLE_STATE_CHANGED,ListeningtoEnableStateChangedHook);
+ HookEvent(ME_PROTO_ACCLISTCHANGED, AccListChanged);
+}
+
+
+void DeInitFrames()
+{
+ if(ServiceExists(MS_CLIST_FRAMES_REMOVEFRAME) && frame_id != -1)
+ {
+ CallService(MS_CLIST_FRAMES_REMOVEFRAME, (WPARAM)frame_id, 0);
+ }
+
+ if (hwnd_frame != NULL) DestroyWindow(hwnd_frame);
+ if (hwnd_container != NULL) DestroyWindow(hwnd_container);
+}
+
+int SmileyAddOptionsChangedHook(WPARAM wParam,LPARAM lParam)
+{
+ UpdateFrameData();
+ return 0;
+}
+
+int SkinEngineDrawCallback(HWND hWnd, HDC hDC, RECT * rcPaint, HRGN rgn, DWORD dFlags, void * CallBackData);
+
+int CreateFrame()
+{
+ WNDCLASS wndclass;
+ wndclass.style = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW; //CS_PARENTDC | CS_HREDRAW | CS_VREDRAW;
+ wndclass.lpfnWndProc = FrameWindowProc;
+ wndclass.cbClsExtra = 0;
+ wndclass.cbWndExtra = 0;
+ wndclass.hInstance = hInst;
+ wndclass.hIcon = NULL;
+ wndclass.hCursor = LoadCursor (NULL, IDC_ARROW);
+ wndclass.hbrBackground = 0; //(HBRUSH)(COLOR_3DFACE+1);
+ wndclass.lpszMenuName = NULL;
+ wndclass.lpszClassName = WINDOW_CLASS_NAME;
+ RegisterClass(&wndclass);
+
+ if (ServiceExists(MS_CLIST_FRAMES_ADDFRAME))
+ {
+ hwnd_frame = CreateWindow(WINDOW_CLASS_NAME, Translate("My Details"),
+ WS_CHILD | WS_VISIBLE,
+ 0,0,10,10, (HWND)CallService(MS_CLUI_GETHWND, 0, 0), NULL, hInst, NULL);
+
+ CLISTFrame Frame = {0};
+
+ Frame.cbSize = sizeof(Frame);
+ Frame.name = "My Details";
+ Frame.TBname = Translate("My Details");
+ Frame.hWnd = hwnd_frame;
+ Frame.align = alTop;
+ Frame.Flags = F_VISIBLE | F_SHOWTB | F_SHOWTBTIP | F_NOBORDER | F_NO_SUBCONTAINER;
+ Frame.height = 100;
+
+ frame_id = CallService(MS_CLIST_FRAMES_ADDFRAME, (WPARAM)&Frame, 0);
+
+ if (ServiceExists(MS_SKINENG_REGISTERPAINTSUB))
+ {
+ CallService(MS_BACKGROUNDCONFIG_REGISTER,(WPARAM)"My Details Background/MyDetails", 0);
+ CallService(MS_SKINENG_REGISTERPAINTSUB, (WPARAM) Frame.hWnd, (LPARAM) SkinEngineDrawCallback);
+ }
+
+ if (DBGetContactSettingByte(NULL, "MyDetails", "ForceHideFrame", 0))
+ {
+ int flags = CallService(MS_CLIST_FRAMES_GETFRAMEOPTIONS, MAKEWPARAM(FO_FLAGS, frame_id), 0);
+ if(flags & F_VISIBLE)
+ CallService(MS_CLIST_FRAMES_SHFRAME, frame_id, 0);
+
+ DBDeleteContactSetting(NULL, "MyDetails", "ForceHideFrame");
+ }
+
+ if (DBGetContactSettingByte(NULL, "MyDetails", "ForceShowFrame", 0))
+ {
+ int flags = CallService(MS_CLIST_FRAMES_GETFRAMEOPTIONS, MAKEWPARAM(FO_FLAGS, frame_id), 0);
+ if(!(flags & F_VISIBLE))
+ CallService(MS_CLIST_FRAMES_SHFRAME, frame_id, 0);
+
+ DBDeleteContactSetting(NULL, "MyDetails", "ForceShowFrame");
+ }
+ }
+ else
+ {
+ wndclass.style = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;//CS_HREDRAW | CS_VREDRAW;
+ wndclass.lpfnWndProc = FrameContainerWindowProc;
+ wndclass.cbClsExtra = 0;
+ wndclass.cbWndExtra = 0;
+ wndclass.hInstance = hInst;
+ wndclass.hIcon = NULL;
+ wndclass.hCursor = LoadCursor (NULL, IDC_ARROW);
+ wndclass.hbrBackground = 0; //(HBRUSH)(COLOR_3DFACE+1);
+ wndclass.lpszMenuName = NULL;
+ wndclass.lpszClassName = CONTAINER_CLASS_NAME;
+ RegisterClass(&wndclass);
+
+ hwnd_container = CreateWindowEx(WS_EX_TOOLWINDOW, CONTAINER_CLASS_NAME, Translate("My Details"),
+ (WS_THICKFRAME | WS_CAPTION | WS_SYSMENU) & ~WS_VISIBLE,
+ 0,0,200,130, (HWND)CallService(MS_CLUI_GETHWND, 0, 0), NULL, hInst, NULL);
+
+ hwnd_frame = CreateWindow(WINDOW_CLASS_NAME, Translate("My Details"),
+ WS_CHILD | WS_VISIBLE,
+ 0,0,10,10, hwnd_container, NULL, hInst, NULL);
+
+ SetWindowLong(hwnd_container, GWL_USERDATA, (LONG)hwnd_frame);
+ SendMessage(hwnd_container, WM_SIZE, 0, 0);
+
+ // Create menu item
+
+ CLISTMENUITEM menu = {0};
+
+ menu.cbSize=sizeof(menu);
+ menu.flags = CMIM_ALL;
+ menu.popupPosition = -0x7FFFFFFF;
+ menu.pszPopupName = Translate("My Details");
+ menu.position = 1; // 500010000
+ menu.hIcon = LoadSkinnedIcon(SKINICON_OTHER_MIRANDA);
+ menu.pszName = Translate("Show My Details");
+ menu.pszService= MODULE_NAME "/ShowHideMyDetails";
+ hMenuShowHideFrame = (HANDLE)CallService(MS_CLIST_ADDMAINMENUITEM, 0, (LPARAM)&menu);
+
+ if(DBGetContactSettingByte(0, MODULE_NAME, SETTING_FRAME_VISIBLE, 1) == 1)
+ {
+ ShowWindow(hwnd_container, SW_SHOW);
+ FixMainMenu();
+ }
+ }
+
+ CreateServiceFunction(MS_MYDETAILS_SHOWFRAME, ShowFrameFunc);
+ CreateServiceFunction(MS_MYDETAILS_HIDEFRAME, HideFrameFunc);
+ CreateServiceFunction(MS_MYDETAILS_SHOWHIDEFRAME, ShowHideFrameFunc);
+
+ return 0;
+}
+
+
+BOOL FrameIsFloating()
+{
+ if (frame_id == -1)
+ {
+ return true; // no frames, always floating
+ }
+
+ return (CallService(MS_CLIST_FRAMES_GETFRAMEOPTIONS, MAKEWPARAM(FO_FLOATING, frame_id), 0) != 0);
+}
+
+
+LRESULT CALLBACK FrameContainerWindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
+ switch(msg)
+ {
+ case WM_SHOWWINDOW:
+ {
+ if ((BOOL)wParam)
+ Utils_RestoreWindowPosition(hwnd, 0, MODULE_NAME, WINDOW_NAME_PREFIX);
+ else
+ Utils_SaveWindowPosition(hwnd, 0, MODULE_NAME, WINDOW_NAME_PREFIX);
+ break;
+ }
+
+ case WM_ERASEBKGND:
+ {
+ HWND child = (HWND)GetWindowLong(hwnd, GWL_USERDATA);
+
+ SendMessage(child, WM_ERASEBKGND, wParam, lParam);
+ break;
+ }
+
+ case WM_SIZE:
+ {
+ HWND child = (HWND)GetWindowLong(hwnd, GWL_USERDATA);
+ RECT r;
+ GetClientRect(hwnd, &r);
+
+ SetWindowPos(child, 0, r.left, r.top, r.right - r.left, r.bottom - r.top, SWP_NOZORDER | SWP_NOACTIVATE);
+ InvalidateRect(child, NULL, TRUE);
+
+ return TRUE;
+ }
+
+ case WM_CLOSE:
+ {
+ DBWriteContactSettingByte(0, MODULE_NAME, SETTING_FRAME_VISIBLE, 0);
+ ShowWindow(hwnd, SW_HIDE);
+ FixMainMenu();
+ return TRUE;
+ }
+ }
+
+ return DefWindowProc(hwnd, msg, wParam, lParam);
+}
+
+
+
+BOOL ScreenToClient(HWND hWnd, LPRECT lpRect)
+{
+ BOOL ret;
+
+ POINT pt;
+
+ pt.x = lpRect->left;
+ pt.y = lpRect->top;
+
+ ret = ScreenToClient(hWnd, &pt);
+
+ if (!ret) return ret;
+
+ lpRect->left = pt.x;
+ lpRect->top = pt.y;
+
+
+ pt.x = lpRect->right;
+ pt.y = lpRect->bottom;
+
+ ret = ScreenToClient(hWnd, &pt);
+
+ lpRect->right = pt.x;
+ lpRect->bottom = pt.y;
+
+ return ret;
+}
+
+
+BOOL MoveWindow(HWND hWnd, const RECT &rect, BOOL bRepaint)
+{
+ return MoveWindow(hWnd, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, bRepaint);
+}
+
+
+RECT GetInnerRect(const RECT &rc, const RECT &clipping)
+{
+ RECT rc_ret = rc;
+
+ rc_ret.left = max(rc.left, clipping.left);
+ rc_ret.top = max(rc.top, clipping.top);
+ rc_ret.right = min(rc.right, clipping.right);
+ rc_ret.bottom = min(rc.bottom, clipping.bottom);
+
+ return rc_ret;
+}
+
+
+
+
+
+void ExternalRect(RECT &ret, const RECT r1, const RECT r2)
+{
+ ret.left = min(r1.left, r2.left);
+ ret.right = max(r1.right, r2.right);
+ ret.top = min(r1.top, r2.top);
+ ret.bottom = max(r1.bottom, r2.bottom);
+}
+
+
+HBITMAP CreateBitmap32(int cx, int cy)
+{
+ BITMAPINFO RGB32BitsBITMAPINFO;
+ UINT * ptPixels;
+ HBITMAP DirectBitmap;
+
+ ZeroMemory(&RGB32BitsBITMAPINFO,sizeof(BITMAPINFO));
+ RGB32BitsBITMAPINFO.bmiHeader.biSize=sizeof(BITMAPINFOHEADER);
+ RGB32BitsBITMAPINFO.bmiHeader.biWidth=cx;//bm.bmWidth;
+ RGB32BitsBITMAPINFO.bmiHeader.biHeight=cy;//bm.bmHeight;
+ RGB32BitsBITMAPINFO.bmiHeader.biPlanes=1;
+ RGB32BitsBITMAPINFO.bmiHeader.biBitCount=32;
+
+ DirectBitmap = CreateDIBSection(NULL,
+ (BITMAPINFO *)&RGB32BitsBITMAPINFO,
+ DIB_RGB_COLORS,
+ (void **)&ptPixels,
+ NULL, 0);
+ return DirectBitmap;
+}
+
+
+BOOL UseLayeredMode()
+{
+ return isLayeredEnabled() && !FrameIsFloating();
+}
+
+
+void EraseBackground(HWND hwnd, HDC hdc)
+{
+ RECT r;
+ GetClientRect(hwnd, &r);
+
+ if (isSkinEngineEnabled())
+ {
+ if (FrameIsFloating())
+ {
+ HBRUSH hB = CreateSolidBrush(opts.bkg_color);
+ FillRect(hdc, &r, hB);
+ DeleteObject(hB);
+ }
+ else
+ {
+ SkinDrawWindowBack(hwnd, hdc, &r, "Main,ID=Background");
+ }
+
+ SkinDrawGlyph(hdc, &r, &r,"MyDetails,ID=Background");
+ }
+ else
+ {
+ HBRUSH hB = CreateSolidBrush(opts.bkg_color);
+ FillRect(hdc, &r, hB);
+ DeleteObject(hB);
+ }
+}
+
+static int Width(const RECT &rc)
+{
+ return rc.right - rc.left;
+}
+
+static int Height(const RECT &rc)
+{
+ return rc.bottom - rc.top;
+}
+
+static HICON CreateOverlayedIcon(HICON icon, HICON overlay)
+{
+ HIMAGELIST il = ImageList_Create(
+ GetSystemMetrics(SM_CXICON),
+ GetSystemMetrics(SM_CYICON),
+ ILC_COLOR32|ILC_MASK, 2, 2);
+ ImageList_AddIcon(il, icon);
+ ImageList_AddIcon(il, overlay);
+ HIMAGELIST newImage = ImageList_Merge(il,0,il,1,0,0);
+ ImageList_Destroy(il);
+ HICON hIcon = ImageList_GetIcon(newImage, 0, 0);
+ ImageList_Destroy(newImage);
+ return hIcon; // the result should be destroyed by DestroyIcon()
+}
+
+void Draw(HDC hdc, SkinIconFieldState &state)
+{
+ if (!state.isVisible())
+ return;
+
+ RECT rc = state.getInsideRect();
+ HRGN rgn = CreateRectRgnIndirect(&rc);
+ SelectClipRgn(hdc, rgn);
+
+ rc = state.getInsideRect(true);
+
+ skin_DrawIconEx(hdc, rc.left, rc.top, state.getIcon(), Width(rc), Height(rc), 0, NULL, DI_NORMAL);
+
+ SelectClipRgn(hdc, NULL);
+ DeleteObject(rgn);
+}
+
+void Draw(HDC hdc, SkinTextFieldState &state, BOOL replace_smileys = FALSE, const char *protocol = NULL)
+{
+ if (!state.isVisible())
+ return;
+
+ RECT rc = state.getInsideRect();
+ HRGN rgn = CreateRectRgnIndirect(&rc);
+ SelectClipRgn(hdc, rgn);
+
+ HGDIOBJ oldFont = SelectObject(hdc, state.getFont());
+ COLORREF oldColor = SetTextColor(hdc, state.getFontColor());
+
+ UINT uFormat = DT_NOPREFIX | DT_END_ELLIPSIS | (opts.draw_text_rtl ? DT_RTLREADING : 0);
+
+ switch(state.getHorizontalAlign())
+ {
+ case SKN_HALIGN_RIGHT:
+ uFormat |= DT_RIGHT;
+ break;
+ case SKN_HALIGN_CENTER:
+ uFormat |= DT_CENTER;
+ break;
+ case SKN_HALIGN_LEFT:
+ uFormat |= DT_LEFT;
+ break;
+ }
+
+ if (replace_smileys && opts.replace_smileys)
+ {
+ uFormat |= DT_SINGLELINE;
+
+ // Draw only first line of text
+ char *tmp = strdup(state.getText());
+ char *pos = strchr(tmp, '\r');
+ if (pos != NULL)
+ pos[0] = '\0';
+ pos = strchr(tmp, '\n');
+ if (pos != NULL)
+ pos[0] = '\0';
+
+ Smileys_DrawText(hdc, tmp, -1, &rc, uFormat | (opts.resize_smileys ? DT_RESIZE_SMILEYS : 0),
+ opts.use_contact_list_smileys ? "clist" : protocol, NULL);
+ }
+ else
+ {
+ skin_DrawText(hdc, state.getText(), -1, &rc, uFormat);
+ }
+
+
+ SelectObject(hdc, oldFont);
+ SetTextColor(hdc, oldColor);
+
+ SelectClipRgn(hdc, NULL);
+ DeleteObject(rgn);
+}
+
+
+void DrawMouseOver(HDC hdc, RECT *lprc, const char *place)
+{
+ if (isSkinEngineEnabled())
+ {
+ SkinDrawGlyph(hdc, lprc, lprc, "MyDetails,ID=MouseOver");
+
+ char glyph[1024];
+ mir_snprintf(glyph, MAX_REGS(glyph), "MyDetails,ID=MouseOver%s", place);
+ SkinDrawGlyph(hdc, lprc, lprc, glyph);
+ }
+ else
+ {
+ FrameRect(hdc, lprc, (HBRUSH) GetStockObject(GRAY_BRUSH));
+ }
+}
+
+
+void Draw(HWND hwnd, HDC hdc_orig)
+{
+ MyDetailsFrameData *data = (MyDetailsFrameData *) GetWindowLong(hwnd, GWL_USERDATA);
+
+ Protocol *proto = GetCurrentProtocol();
+ if (proto == NULL)
+ {
+ EraseBackground(hwnd, hdc_orig);
+ return;
+ }
+
+ if (ServiceExists(MS_CLIST_FRAMES_SETFRAMEOPTIONS) && frame_id != -1)
+ {
+ int flags = CallService(MS_CLIST_FRAMES_GETFRAMEOPTIONS, MAKEWPARAM(FO_FLAGS, frame_id), 0);
+ if(flags & F_UNCOLLAPSED)
+ {
+ RECT rf;
+ GetClientRect(hwnd, &rf);
+
+ if (rf.bottom - rf.top != 0)
+ {
+ if (FrameIsFloating())
+ {
+ HWND parent = GetParent(hwnd);
+
+ if (parent != NULL)
+ {
+ RECT rp_client, rp_window, r_window;
+ GetClientRect(parent, &rp_client);
+ GetWindowRect(parent, &rp_window);
+ GetWindowRect(hwnd, &r_window);
+ int diff = (rp_window.bottom - rp_window.top) - (rp_client.bottom - rp_client.top);
+ if(ServiceExists(MS_CLIST_FRAMES_ADDFRAME))
+ diff += (r_window.top - rp_window.top);
+
+ SetWindowPos(parent, 0, 0, 0, rp_window.right - rp_window.left, diff, SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE);
+ }
+ }
+ }
+
+ for (size_t i = 0; i < data->items.size(); ++i)
+ data->items[i]->hide();
+
+ return;
+ }
+ }
+
+
+ RECT r_full;
+ GetClientRect(hwnd, &r_full);
+
+ HDC hdc;
+ HBITMAP hBmp;
+ BOOL useLayeredMode = UseLayeredMode();
+ if (useLayeredMode)
+ {
+ hdc = hdc_orig;
+ hBmp = NULL;
+ }
+ else
+ {
+ hdc = CreateCompatibleDC(hdc_orig);
+ hBmp = CreateBitmap32(Width(r_full), Height(r_full));
+ SelectObject(hdc, hBmp);
+ }
+
+ int old_bk_mode = SetBkMode(hdc, TRANSPARENT);
+ HFONT old_font = (HFONT) GetCurrentObject(hdc, OBJ_FONT);
+ COLORREF old_color = GetTextColor(hdc);
+ SetStretchBltMode(hdc, HALFTONE);
+
+
+ HICON hStatusIcon;
+ bool freeStatusIcon = false;
+ if (proto->GetCustomStatus() != 0 && proto->CanCall(PS_ICQ_GETCUSTOMSTATUSICON))
+ hStatusIcon = (HICON) proto->Call(PS_ICQ_GETCUSTOMSTATUSICON, proto->GetCustomStatus(), LR_SHARED);
+ else
+ hStatusIcon = LoadSkinnedProtoIcon(proto->GetName(), proto->GetStatus());
+
+ if (proto->IsLocked())
+ {
+ HICON hLockOverlay = LoadSkinnedIcon(SKINICON_OTHER_STATUS_LOCKED);
+ if (hLockOverlay != NULL)
+ {
+ freeStatusIcon = true;
+ hStatusIcon = CreateOverlayedIcon(hStatusIcon, hLockOverlay);
+ }
+ }
+
+
+ HICON hListeningIcon = IcoLib_LoadIcon("LISTENING_TO_ICON");
+ HICON hEmailIcon = IcoLib_LoadIcon("MYDETAILS_EMAIL");
+ HICON hNextIcon = IcoLib_LoadIcon("MYDETAILS_NEXT_PROTOCOL");
+ HICON hPrevIcon = IcoLib_LoadIcon("MYDETAILS_PREV_PROTOCOL");
+
+ {
+ dialog->setInfoBool("resize_frame", opts.resize_frame);
+ dialog->setInfoBool("protocol.locked", proto->IsLocked());
+
+
+ if (opts.resize_frame)
+ dialog->setSize(Width(r_full), 0x1FFFFFFF);
+ else
+ dialog->setSize(Width(r_full), Height(r_full));
+
+
+
+ SkinImageField avatar = dialog->getImageField("avatar");
+ if (proto->CanGetAvatar() && proto->GetAvatarImage() != NULL)
+ {
+ avatar.setEnabled(TRUE);
+ avatar.setImage(proto->GetAvatarImage());
+ }
+ else
+ {
+ avatar.setEnabled(FALSE);
+ avatar.setImage(NULL);
+ }
+
+ SkinTextField nickname = dialog->getTextField("nickname");
+ nickname.setText(proto->GetNick());
+
+ SkinTextField protocol = dialog->getTextField("protocol");
+ protocol.setText(proto->GetDescription());
+
+ SkinIconField status_icon = dialog->getIconField("status_icon");
+ status_icon.setIcon(hStatusIcon);
+
+ SkinTextField status_name = dialog->getTextField("status_name");
+ status_name.setText(proto->GetStatusName());
+
+ SkinTextField status_msg = dialog->getTextField("status_msg");
+ if (proto->CanGetStatusMsg())
+ {
+ status_msg.setEnabled(TRUE);
+ status_msg.setText(proto->GetStatusMsg());
+ }
+ else
+ {
+ status_msg.setEnabled(FALSE);
+ status_msg.setText(_T(""));
+ }
+
+ SkinIconField listening_icon = dialog->getIconField("listening_icon");
+ SkinTextField listening = dialog->getTextField("listening");
+ if (proto->ListeningToEnabled() && proto->GetStatus() > ID_STATUS_OFFLINE
+ && proto->GetListeningTo()[0] != 0)
+ {
+ listening_icon.setEnabled(TRUE);
+ listening.setEnabled(TRUE);
+ listening_icon.setIcon(hListeningIcon);
+ listening.setText(proto->GetListeningTo());
+ }
+ else
+ {
+ listening_icon.setEnabled(FALSE);
+ listening.setEnabled(FALSE);
+ listening_icon.setIcon(NULL);
+ listening.setText(_T(""));
+ }
+
+ SkinIconField email_icon = dialog->getIconField("email_icon");
+ SkinTextField email = dialog->getTextField("email");
+ if (proto->CanGetEmailCount())
+ {
+ email_icon.setEnabled(TRUE);
+ email.setEnabled(TRUE);
+ email_icon.setIcon(hEmailIcon);
+
+ TCHAR tmp[64];
+ _sntprintf(tmp, MAX_REGS(tmp), _T("%d"), proto->GetEmailCount());
+ email.setText(tmp);
+ }
+ else
+ {
+ email_icon.setEnabled(FALSE);
+ email.setEnabled(FALSE);
+ email_icon.setIcon(NULL);
+ email.setText(_T(""));
+ }
+
+ SkinIconField next_proto = dialog->getIconField("next_proto");
+ SkinIconField prev_proto = dialog->getIconField("prev_proto");
+ prev_proto.setIcon(hPrevIcon);
+ next_proto.setIcon(hNextIcon);
+ }
+
+ SkinDialogState state = dialog->run();
+ SkinImageFieldState avatar = state.getImageField("avatar");
+ SkinTextFieldState nickname = state.getTextField("nickname");
+ SkinTextFieldState protocol = state.getTextField("protocol");
+ SkinIconFieldState status_icon = state.getIconField("status_icon");
+ SkinTextFieldState status_name = state.getTextField("status_name");
+ SkinTextFieldState status_msg = state.getTextField("status_msg");
+ SkinIconFieldState listening_icon = state.getIconField("listening_icon");
+ SkinTextFieldState listening = state.getTextField("listening");
+ SkinIconFieldState email_icon = state.getIconField("email_icon");
+ SkinTextFieldState email = state.getTextField("email");
+ SkinIconFieldState next_proto = state.getIconField("next_proto");
+ SkinIconFieldState prev_proto = state.getIconField("prev_proto");
+
+
+ {
+ data->proto.update(hwnd, &protocol);
+ data->proto_cycle_next.update(hwnd, &next_proto);
+ data->proto_cycle_prev.update(hwnd, &prev_proto);
+ data->avatar.update(hwnd, &avatar);
+ data->nick.update(hwnd, &nickname);
+ data->status.update(hwnd, &status_icon, &status_name);
+ data->away_msg.update(hwnd, &status_msg);
+ data->listening_to.update(hwnd, &listening_icon, &listening);
+ data->email.update(hwnd, &email_icon, &email);
+
+
+ POINT p = {0};
+ GetCursorPos(&p);
+ ScreenToClient(hwnd, &p);
+
+ for(size_t i = 0; i < data->items.size(); ++i)
+ data->items[i]->setMouseOver(&p);
+ }
+
+ // Erase
+ EraseBackground(hwnd, hdc);
+
+ // Draw items
+
+ UINT uFormat = DT_SINGLELINE | DT_NOPREFIX | DT_END_ELLIPSIS
+ | (opts.draw_text_rtl ? DT_RTLREADING : 0);
+
+ // Image
+ if (avatar.isVisible() && proto->CanGetAvatar() && proto->GetAvatarImage() != NULL)
+ {
+ RECT rc = avatar.getInsideRect();
+ HRGN rgn = CreateRectRgnIndirect(&rc);
+ SelectClipRgn(hdc, rgn);
+
+ rc = avatar.getInsideRect(true);
+
+ int width = Width(rc);
+ int height = Height(rc);
+
+ int round_radius;
+ if (opts.draw_avatar_round_corner)
+ {
+ if (opts.draw_avatar_use_custom_corner_size)
+ round_radius = opts.draw_avatar_custom_corner_size;
+ else
+ round_radius = min(width, height) / 6;
+ }
+ else
+ {
+ round_radius = 0;
+ }
+
+
+ AVATARDRAWREQUEST adr = {0};
+
+ adr.cbSize = sizeof(AVATARDRAWREQUEST);
+ adr.hTargetDC = hdc;
+ adr.rcDraw = rc;
+
+ adr.dwFlags = AVDRQ_OWNPIC | AVDRQ_HIDEBORDERONTRANSPARENCY |
+ (opts.draw_avatar_border ? AVDRQ_DRAWBORDER : 0 ) |
+ (opts.draw_avatar_round_corner ? AVDRQ_ROUNDEDCORNER : 0 );
+
+ if (useLayeredMode)
+ adr.dwFlags |= AVDRQ_AERO;
+
+ adr.clrBorder = opts.draw_avatar_border_color;
+ adr.radius = round_radius;
+ adr.alpha = 255;
+ adr.szProto = (char *) proto->GetName();
+
+ CallService(MS_AV_DRAWAVATAR, 0, (LPARAM) &adr);
+
+ // Clipping rgn
+ SelectClipRgn(hdc, NULL);
+ DeleteObject(rgn);
+ }
+
+ // Nick
+ if (data->nick.draw && data->nick.mouseOver && proto->CanSetNick())
+ DrawMouseOver(hdc, &nickname.getRect(), "Nick");
+
+ Draw(hdc, nickname, TRUE, proto->GetName());
+
+
+ // Protocol
+ if (data->proto.draw && data->proto.mouseOver)
+ DrawMouseOver(hdc, &data->proto.rc, "Proto");
+
+ Draw(hdc, protocol);
+
+
+ // Status
+ if (data->status.draw && data->status.mouseOver)
+ DrawMouseOver(hdc, &data->status.rc, "Status");
+
+ Draw(hdc, status_icon);
+ Draw(hdc, status_name);
+
+
+ // Away message
+ if (data->away_msg.draw && data->away_msg.mouseOver && proto->CanSetStatusMsg())
+ DrawMouseOver(hdc, &data->away_msg.rc, "StatusMsg");
+
+ Draw(hdc, status_msg, TRUE, proto->GetName());
+
+
+ // Listening to
+ Draw(hdc, listening_icon);
+ Draw(hdc, listening);
+
+ if (data->listening_to.draw && data->listening_to.mouseOver && protocols->CanSetListeningTo())
+ DrawMouseOver(hdc, &data->listening_to.rc, "Listening");
+
+
+ // Unread email count
+ Draw(hdc, email_icon);
+ Draw(hdc, email);
+
+ // Protocol cycle icon
+ Draw(hdc, next_proto);
+ Draw(hdc, prev_proto);
+
+
+ SelectObject(hdc, old_font);
+ SetTextColor(hdc, old_color);
+ SetBkMode(hdc, old_bk_mode);
+
+ if (!useLayeredMode)
+ {
+ BitBlt(hdc_orig, r_full.left, r_full.top, r_full.right - r_full.left,
+ r_full.bottom - r_full.top, hdc, r_full.left, r_full.top, SRCCOPY);
+ DeleteDC(hdc);
+ DeleteObject(hBmp);
+ }
+
+ if (freeStatusIcon)
+ DestroyIcon(hStatusIcon);
+ IcoLib_ReleaseIcon(hListeningIcon);
+ IcoLib_ReleaseIcon(hEmailIcon);
+ IcoLib_ReleaseIcon(hPrevIcon);
+ IcoLib_ReleaseIcon(hNextIcon);
+
+ if (opts.resize_frame && ServiceExists(MS_CLIST_FRAMES_SETFRAMEOPTIONS) && frame_id != -1)
+ {
+ RECT rf;
+ GetClientRect(hwnd, &rf);
+
+ int currentSize = Height(r_full);
+
+ int expectedSize = 0;
+ for(size_t i = 0; i < data->items.size(); ++i)
+ {
+ SimpleItem *item = data->items[i];
+ if (!item->draw)
+ continue;
+
+ expectedSize = max(expectedSize, item->rc.bottom);
+ }
+ expectedSize += state.getBorders().bottom;
+
+ if (expectedSize != currentSize)
+ {
+ if (FrameIsFloating())
+ {
+ HWND parent = GetParent(hwnd);
+
+ if (parent != NULL)
+ {
+ RECT rp_client, rp_window, r_window;
+ GetClientRect(parent, &rp_client);
+ GetWindowRect(parent, &rp_window);
+ GetWindowRect(hwnd, &r_window);
+ int diff = (rp_window.bottom - rp_window.top) - (rp_client.bottom - rp_client.top);
+ if(ServiceExists(MS_CLIST_FRAMES_ADDFRAME))
+ diff += (r_window.top - rp_window.top);
+
+ SetWindowPos(parent, 0, 0, 0, rp_window.right - rp_window.left, expectedSize + diff, SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE);
+ }
+ }
+ else if (IsWindowVisible(hwnd) && ServiceExists(MS_CLIST_FRAMES_ADDFRAME))
+ {
+ int flags = CallService(MS_CLIST_FRAMES_GETFRAMEOPTIONS, MAKEWPARAM(FO_FLAGS, frame_id), 0);
+ if(flags & F_VISIBLE)
+ {
+ CallService(MS_CLIST_FRAMES_SETFRAMEOPTIONS, MAKEWPARAM(FO_HEIGHT, frame_id), (LPARAM) expectedSize);
+ CallService(MS_CLIST_FRAMES_UPDATEFRAME, (WPARAM)frame_id, (LPARAM)(FU_TBREDRAW | FU_FMREDRAW | FU_FMPOS));
+ }
+ }
+ }
+ }
+}
+
+int SkinEngineDrawCallback(HWND hWnd, HDC hDC, RECT * rcPaint, HRGN rgn, DWORD dFlags, void * CallBackData)
+{
+ Draw(hWnd, hDC);
+ return 0;
+}
+
+bool InsideRect(const POINT &p, const RECT &r)
+{
+ return p.x >= r.left && p.x < r.right && p.y >= r.top && p.y < r.bottom;
+}
+
+int ShowPopupMenu(HWND hwnd, HMENU submenu, SimpleItem &item)
+{
+ POINT p;
+ if (item.alignRight)
+ p.x = item.rc.right;
+ else
+ p.x = item.rc.left;
+ p.y = item.rc.bottom+1;
+ ClientToScreen(hwnd, &p);
+
+ return TrackPopupMenu(submenu, TPM_TOPALIGN|TPM_RIGHTBUTTON|TPM_RETURNCMD
+ | (item.alignRight ? TPM_RIGHTALIGN : TPM_LEFTALIGN), p.x, p.y, 0, hwnd, NULL);
+}
+
+
+void ShowGlobalStatusMenu(HWND hwnd, MyDetailsFrameData *data, Protocol *proto, POINT &p)
+{
+ HMENU submenu = (HMENU) CallService(MS_CLIST_MENUGETSTATUS,0,0);
+
+ int ret = ShowPopupMenu(hwnd, submenu, data->status);
+ if(ret)
+ CallService(MS_CLIST_MENUPROCESSCOMMAND, MAKEWPARAM(LOWORD(ret),MPCF_MAINMENU),(LPARAM)NULL);
+}
+
+void ShowProtocolStatusMenu(HWND hwnd, MyDetailsFrameData *data, Protocol *proto, POINT &p)
+{
+ HMENU menu = (HMENU) CallService(MS_CLIST_MENUGETSTATUS,0,0);
+ HMENU submenu = NULL;
+
+ if (menu != NULL)
+ {
+ // Find the correct menu item
+ int count = GetMenuItemCount(menu);
+ for (int i = 0 ; i < count && submenu == NULL; i++)
+ {
+ MENUITEMINFO mii = {0};
+
+ mii.cbSize = sizeof(mii);
+
+ if(!IsWinVer98Plus())
+ {
+ mii.fMask = MIIM_TYPE;
+ }
+ else
+ {
+ mii.fMask = MIIM_STRING;
+ }
+
+ GetMenuItemInfo(menu, i, TRUE, &mii);
+
+ if (mii.cch != 0)
+ {
+ mii.cch++;
+ mii.dwTypeData = (char *)malloc(sizeof(char) * mii.cch);
+ GetMenuItemInfo(menu, i, TRUE, &mii);
+
+ if (strcmp(mii.dwTypeData, proto->GetDescription()) == 0)
+ {
+ submenu = GetSubMenu(menu, i);
+ }
+
+ free(mii.dwTypeData);
+ }
+ }
+
+ if (submenu == NULL && GetNumProtocols() == 1)
+ {
+ submenu = menu;
+ }
+ }
+
+ if (submenu != NULL)
+ {
+ int ret = ShowPopupMenu(hwnd, submenu, data->status);
+ if(ret)
+ CallService(MS_CLIST_MENUPROCESSCOMMAND, MAKEWPARAM(LOWORD(ret),MPCF_MAINMENU),(LPARAM)NULL);
+ }
+ else
+ {
+ // Well, lets do it by hand
+ static int statusModePf2List[]={0xFFFFFFFF,PF2_ONLINE,PF2_SHORTAWAY,PF2_LONGAWAY,PF2_LIGHTDND,PF2_HEAVYDND,PF2_FREECHAT,PF2_INVISIBLE,PF2_ONTHEPHONE,PF2_OUTTOLUNCH};
+
+ menu = LoadMenu(hInst, MAKEINTRESOURCE(IDR_MENU1));
+ submenu = GetSubMenu(menu, 0);
+ CallService(MS_LANGPACK_TRANSLATEMENU,(WPARAM)submenu,0);
+
+ DWORD flags = proto->Call(PS_GETCAPS, PFLAGNUM_2);
+ for ( int i = GetMenuItemCount(submenu) -1 ; i >= 0 ; i-- )
+ {
+ if (!(flags & statusModePf2List[i]))
+ {
+ // Hide menu
+ RemoveMenu(submenu, i, MF_BYPOSITION);
+ }
+ }
+
+ int ret = ShowPopupMenu(hwnd, submenu, data->status);
+ DestroyMenu(menu);
+
+ if(ret)
+ proto->SetStatus(ret);
+ }
+}
+
+void ShowListeningToMenu(HWND hwnd, MyDetailsFrameData *data, Protocol *proto, POINT &p)
+{
+ HMENU menu = LoadMenu(hInst, MAKEINTRESOURCE(IDR_MENU1));
+ HMENU submenu = GetSubMenu(menu, 5);
+ CallService(MS_LANGPACK_TRANSLATEMENU,(WPARAM)submenu,0);
+
+ // Add this proto to menu
+ char tmp[128];
+ mir_snprintf(tmp, sizeof(tmp), Translate("Enable Listening To for %s"), proto->GetDescription());
+
+ MENUITEMINFO mii = {0};
+ mii.cbSize = sizeof(mii);
+ mii.fMask = MIIM_ID | MIIM_TYPE | MIIM_STATE;
+ mii.fType = MFT_STRING;
+ mii.fState = proto->ListeningToEnabled() ? MFS_CHECKED : 0;
+ mii.dwTypeData = tmp;
+ mii.cch = strlen(tmp);
+ mii.wID = 1;
+
+ if (!proto->CanSetListeningTo())
+ {
+ mii.fState |= MFS_DISABLED;
+ }
+
+ InsertMenuItem(submenu, 0, TRUE, &mii);
+
+ ZeroMemory(&mii, sizeof(mii));
+ mii.cbSize = sizeof(mii);
+ mii.fMask = MIIM_STATE;
+ mii.fState = protocols->ListeningToEnabled() ? MFS_CHECKED : 0;
+
+ if (!protocols->CanSetListeningTo())
+ {
+ mii.fState |= MFS_DISABLED;
+ }
+
+ SetMenuItemInfo(submenu, ID_LISTENINGTOPOPUP_SENDLISTENINGTO, FALSE, &mii);
+
+ int ret = ShowPopupMenu(hwnd, submenu, data->listening_to);
+
+ DestroyMenu(menu);
+
+ switch(ret)
+ {
+ case 1:
+ {
+ CallService(MS_LISTENINGTO_ENABLE, (LPARAM) proto->GetName(), !proto->ListeningToEnabled());
+ break;
+ }
+ case ID_LISTENINGTOPOPUP_SENDLISTENINGTO:
+ {
+ CallService(MS_LISTENINGTO_ENABLE, 0, !protocols->ListeningToEnabled());
+ break;
+ }
+ }
+
+}
+
+
+LRESULT CALLBACK FrameWindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch(msg)
+ {
+ case WM_CREATE:
+ {
+ MyDetailsFrameData *data = new MyDetailsFrameData();
+ SetWindowLong(hwnd, GWL_USERDATA, (LONG) data);
+
+ SetCurrentProtocol(DBGetContactSettingWord(NULL, "MyDetails", "ProtocolNumber", 0));
+
+ SetCycleTime(hwnd);
+
+ SetStatusMessageRefreshTime(hwnd);
+
+ return TRUE;
+ }
+
+
+ case WM_ERASEBKGND:
+ {
+ //EraseBackground(hwnd, (HDC)wParam);
+ //Draw(hwnd, (HDC)wParam);
+ return TRUE;
+ }
+
+ /*
+ case WM_PRINTCLIENT:
+ {
+ Draw(hwnd, (HDC)wParam);
+ return TRUE;
+ }
+ */
+
+ case WM_PAINT:
+ {
+ if (UseLayeredMode())
+ {
+ CallService(MS_SKINENG_INVALIDATEFRAMEIMAGE, (WPARAM) hwnd, 0);
+ ValidateRect(hwnd, NULL);
+ }
+ else
+ {
+ RECT r;
+ if(GetUpdateRect(hwnd, &r, FALSE))
+ {
+ PAINTSTRUCT ps;
+
+ HDC hdc = BeginPaint(hwnd, &ps);
+ Draw(hwnd, hdc);
+ EndPaint(hwnd, &ps);
+ }
+ }
+
+ return TRUE;
+ }
+
+ case WM_SIZE:
+ {
+ //InvalidateRect(hwnd, NULL, FALSE);
+ MyDetailsFrameData *data = (MyDetailsFrameData *)GetWindowLong(hwnd, GWL_USERDATA);
+ RedrawFrame();
+ break;
+ }
+
+ case WM_TIMER:
+ {
+ MyDetailsFrameData *data = (MyDetailsFrameData *)GetWindowLong(hwnd, GWL_USERDATA);
+
+ if (wParam == ID_FRAME_TIMER)
+ {
+ if (!data->showing_menu)
+ CallService(MS_MYDETAILS_SHOWNEXTPROTOCOL, 0, 0);
+ }
+ else if (wParam == ID_RECALC_TIMER)
+ {
+ KillTimer(hwnd, ID_RECALC_TIMER);
+
+ PostMessage(hwnd, MWM_REFRESH_DATA, 0, 0);
+ }
+ else if (wParam == ID_STATUSMESSAGE_TIMER)
+ {
+ SetStatusMessageRefreshTime(hwnd);
+
+ PostMessage(hwnd, MWM_REFRESH_DATA, 0, 0);
+ }
+
+ return TRUE;
+ }
+
+ case WM_LBUTTONUP:
+ {
+ MyDetailsFrameData *data = (MyDetailsFrameData *)GetWindowLong(hwnd, GWL_USERDATA);
+ Protocol *proto = GetCurrentProtocol();
+ if (proto == NULL)
+ break;
+
+ POINT p;
+ p.x = LOWORD(lParam);
+ p.y = HIWORD(lParam);
+
+ // In proto cycle button?
+ if (data->proto_cycle_next.hitTest(p))
+ {
+ CallService(MS_MYDETAILS_SHOWNEXTPROTOCOL, 0, 0);
+ }
+ else if (data->proto_cycle_prev.hitTest(p))
+ {
+ CallService(MS_MYDETAILS_SHOWPREVIOUSPROTOCOL, 0, 0);
+ }
+ // In image?
+ else if (data->avatar.hitTest(p) && proto->CanSetAvatar())
+ {
+ if (opts.global_on_avatar)
+ CallService(MS_MYDETAILS_SETMYAVATARUI, 0, 0);
+ else
+ CallService(MS_MYDETAILS_SETMYAVATARUI, 0, (LPARAM) proto->GetName());
+ }
+ // In nick?
+ else if (data->nick.hitTest(p) && proto->CanSetNick())
+ {
+ if (opts.global_on_nickname)
+ CallService(MS_MYDETAILS_SETMYNICKNAMEUI, 0, 0);
+ else
+ CallService(MS_MYDETAILS_SETMYNICKNAMEUI, 0, (LPARAM) proto->GetName());
+ }
+ // In status message?
+ else if (data->away_msg.hitTest(p) && proto->CanSetStatusMsg())
+ {
+ if (opts.global_on_status_message)
+ CallService(MS_MYDETAILS_SETMYSTATUSMESSAGEUI, 0, 0);
+ else
+ CallService(MS_MYDETAILS_SETMYSTATUSMESSAGEUI, 0, (LPARAM) proto->GetName());
+ }
+ // In status?
+ else if (data->status.hitTest(p))
+ {
+ data->showing_menu = true;
+
+ if (opts.global_on_status)
+ ShowGlobalStatusMenu(hwnd, data, proto, p);
+ else
+ ShowProtocolStatusMenu(hwnd, data, proto, p);
+
+ data->showing_menu = false;
+ }
+ // In listening to?
+ else if (data->listening_to.hitTest(p) && protocols->CanSetListeningTo())
+ {
+ ShowListeningToMenu(hwnd, data, proto, p);
+ }
+ // In protocol?
+ else if (data->proto.hitTest(p))
+ {
+ data->showing_menu = true;
+
+ HMENU menu = CreatePopupMenu();
+
+ std::vector<Protocol> protos;
+ GetProtocols(&protos);
+
+ int current = GetCurrentProtocolIndex();
+
+ int protosSize = (int) protos.size();
+ for (int i = protosSize - 1 ; i >= 0 ; i--)
+ {
+ Protocol &proto = protos[i];
+
+ MENUITEMINFO mii = {0};
+ mii.cbSize = sizeof(mii);
+ mii.fMask = MIIM_ID | MIIM_TYPE;
+ mii.fType = MFT_STRING;
+ mii.dwTypeData = (char *) proto.GetDescription();
+ mii.cch = strlen(mii.dwTypeData);
+ mii.wID = i + 1;
+
+ if (i == current)
+ {
+ mii.fMask |= MIIM_STATE;
+ mii.fState = MFS_DISABLED;
+ }
+
+ InsertMenuItem(menu, 0, TRUE, &mii);
+ }
+
+ int ret = ShowPopupMenu(hwnd, menu, data->proto);
+
+ DestroyMenu(menu);
+
+ if (ret != 0)
+ PluginCommand_ShowProtocol(NULL, (WPARAM) GetProtocolByIndex(ret - 1).GetName());
+
+ data->showing_menu = false;
+ }
+
+ break;
+ }
+
+ case WM_MEASUREITEM:
+ {
+ return CallService(MS_CLIST_MENUMEASUREITEM,wParam,lParam);
+ }
+ case WM_DRAWITEM:
+ {
+ return CallService(MS_CLIST_MENUDRAWITEM,wParam,lParam);
+ }
+
+ case WM_CONTEXTMENU:
+ {
+ MyDetailsFrameData *data = (MyDetailsFrameData *)GetWindowLong(hwnd, GWL_USERDATA);
+ Protocol *proto = GetCurrentProtocol();
+ if (proto == NULL)
+ break;
+
+ POINT p;
+ p.x = LOWORD(lParam);
+ p.y = HIWORD(lParam);
+
+ ScreenToClient(hwnd, &p);
+
+ data->showing_menu = true;
+
+ // In proto cycle button?
+ if (data->proto_cycle_next.hitTest(p))
+ {
+ CallService(MS_MYDETAILS_SHOWPREVIOUSPROTOCOL, 0, 0);
+ }
+ else if (data->proto_cycle_prev.hitTest(p))
+ {
+ CallService(MS_MYDETAILS_SHOWNEXTPROTOCOL, 0, 0);
+ }
+ // In image?
+ else if (data->avatar.hitTest(p))
+ {
+ HMENU menu = LoadMenu(hInst, MAKEINTRESOURCE(IDR_MENU1));
+ HMENU submenu = GetSubMenu(menu, 4);
+ CallService(MS_LANGPACK_TRANSLATEMENU,(WPARAM)submenu,0);
+
+ // Add this proto to menu
+ char tmp[128];
+ mir_snprintf(tmp, sizeof(tmp), Translate("Set My Avatar for %s..."), proto->GetDescription());
+
+ MENUITEMINFO mii = {0};
+ mii.cbSize = sizeof(mii);
+ mii.fMask = MIIM_ID | MIIM_TYPE;
+ mii.fType = MFT_STRING;
+ mii.dwTypeData = tmp;
+ mii.cch = strlen(tmp);
+ mii.wID = 1;
+
+ if (!proto->CanSetAvatar())
+ {
+ mii.fMask |= MIIM_STATE;
+ mii.fState = MFS_DISABLED;
+ }
+
+ InsertMenuItem(submenu, 0, TRUE, &mii);
+
+ ClientToScreen(hwnd, &p);
+
+ int ret = TrackPopupMenu(submenu, TPM_TOPALIGN|TPM_LEFTALIGN|TPM_RIGHTBUTTON|TPM_RETURNCMD, p.x, p.y, 0, hwnd, NULL);
+ DestroyMenu(menu);
+
+ switch(ret)
+ {
+ case 1:
+ {
+ CallService(MS_MYDETAILS_SETMYAVATARUI, 0, (LPARAM) proto->GetName());
+ break;
+ }
+ case ID_AVATARPOPUP_SETMYAVATAR:
+ {
+ CallService(MS_MYDETAILS_SETMYAVATARUI, 0, 0);
+ break;
+ }
+ }
+ }
+ // In nick?
+ else if (data->nick.hitTest(p))
+ {
+ HMENU menu = LoadMenu(hInst, MAKEINTRESOURCE(IDR_MENU1));
+ HMENU submenu = GetSubMenu(menu, 2);
+ CallService(MS_LANGPACK_TRANSLATEMENU,(WPARAM)submenu,0);
+
+ // Add this proto to menu
+ char tmp[128];
+ mir_snprintf(tmp, sizeof(tmp), Translate("Set My Nickname for %s..."), proto->GetDescription());
+
+ MENUITEMINFO mii = {0};
+ mii.cbSize = sizeof(mii);
+ mii.fMask = MIIM_ID | MIIM_TYPE;
+ mii.fType = MFT_STRING;
+ mii.dwTypeData = tmp;
+ mii.cch = strlen(tmp);
+ mii.wID = 1;
+
+ if (!proto->CanSetNick())
+ {
+ mii.fMask |= MIIM_STATE;
+ mii.fState = MFS_DISABLED;
+ }
+
+ InsertMenuItem(submenu, 0, TRUE, &mii);
+
+ ClientToScreen(hwnd, &p);
+
+ int ret = TrackPopupMenu(submenu, TPM_TOPALIGN|TPM_LEFTALIGN|TPM_RIGHTBUTTON|TPM_RETURNCMD, p.x, p.y, 0, hwnd, NULL);
+ DestroyMenu(menu);
+
+ switch(ret)
+ {
+ case 1:
+ {
+ CallService(MS_MYDETAILS_SETMYNICKNAMEUI, 0, (LPARAM) proto->GetName());
+ break;
+ }
+ case ID_NICKPOPUP_SETMYNICKNAME:
+ {
+ CallService(MS_MYDETAILS_SETMYNICKNAMEUI, 0, 0);
+ break;
+ }
+ }
+ }
+ // In status message?
+ else if (data->away_msg.hitTest(p))
+ {
+ char tmp[128];
+
+ HMENU menu = LoadMenu(hInst, MAKEINTRESOURCE(IDR_MENU1));
+ HMENU submenu = GetSubMenu(menu, 3);
+ CallService(MS_LANGPACK_TRANSLATEMENU,(WPARAM)submenu,0);
+
+ // Add this proto to menu
+ mir_snprintf(tmp, sizeof(tmp), Translate("Set My Status Message for %s..."),
+ proto->GetDescription());
+
+ MENUITEMINFO mii = {0};
+ mii.cbSize = sizeof(mii);
+ mii.fMask = MIIM_ID | MIIM_TYPE;
+ mii.fType = MFT_STRING;
+ mii.dwTypeData = tmp;
+ mii.cch = strlen(tmp);
+ mii.wID = 1;
+
+ if (!proto->CanSetStatusMsg())
+ {
+ mii.fMask |= MIIM_STATE;
+ mii.fState = MFS_DISABLED;
+ }
+
+ InsertMenuItem(submenu, 0, TRUE, &mii);
+
+ {
+ // Add this to menu
+ mir_snprintf(tmp, sizeof(tmp), Translate("Set My Status Message for %s..."),
+ CallService(MS_CLIST_GETSTATUSMODEDESCRIPTION, proto->GetStatus(), 0));
+
+ MENUITEMINFO mii = {0};
+ mii.cbSize = sizeof(mii);
+ mii.fMask = MIIM_ID | MIIM_TYPE;
+ mii.fType = MFT_STRING;
+ mii.dwTypeData = tmp;
+ mii.cch = strlen(tmp);
+ mii.wID = 2;
+
+ if (proto->GetStatus() == ID_STATUS_OFFLINE)
+ {
+ mii.fMask |= MIIM_STATE;
+ mii.fState = MFS_DISABLED;
+ }
+
+ InsertMenuItem(submenu, 0, TRUE, &mii);
+ }
+
+ ClientToScreen(hwnd, &p);
+
+ int ret = TrackPopupMenu(submenu, TPM_TOPALIGN|TPM_LEFTALIGN|TPM_RIGHTBUTTON|TPM_RETURNCMD, p.x, p.y, 0, hwnd, NULL);
+ DestroyMenu(menu);
+
+ switch(ret)
+ {
+ case 1:
+ {
+ CallService(MS_MYDETAILS_SETMYSTATUSMESSAGEUI, 0, (LPARAM) proto->GetName());
+ break;
+ }
+ case 2:
+ {
+ CallService(MS_MYDETAILS_SETMYSTATUSMESSAGEUI, (WPARAM) proto->GetStatus(), 0);
+ break;
+ }
+ case ID_STATUSMESSAGEPOPUP_SETMYSTATUSMESSAGE:
+ {
+ CallService(MS_MYDETAILS_SETMYSTATUSMESSAGEUI, 0, 0);
+ break;
+ }
+ }
+ }
+ // In status?
+ else if (data->status.hitTest(p))
+ {
+ if (opts.global_on_status)
+ ShowProtocolStatusMenu(hwnd, data, proto, p);
+ else
+ ShowGlobalStatusMenu(hwnd, data, proto, p);
+ }
+ // In listening to?
+ else if (data->listening_to.hitTest(p) && protocols->CanSetListeningTo())
+ {
+ ShowListeningToMenu(hwnd, data, proto, p);
+ }
+ // In protocol?
+ else if (data->proto.hitTest(p))
+ {
+ }
+ // Default context menu
+ else
+ {
+ HMENU menu = LoadMenu(hInst, MAKEINTRESOURCE(IDR_MENU1));
+ HMENU submenu = GetSubMenu(menu, 1);
+ CallService(MS_LANGPACK_TRANSLATEMENU,(WPARAM)submenu,0);
+
+ if (opts.cycle_through_protocols)
+ RemoveMenu(submenu, ID_CYCLE_THROUGH_PROTOS, MF_BYCOMMAND);
+ else
+ RemoveMenu(submenu, ID_DONT_CYCLE_THROUGH_PROTOS, MF_BYCOMMAND);
+
+ // Add this proto to menu
+ char tmp[128];
+ MENUITEMINFO mii = {0};
+
+ mir_snprintf(tmp, sizeof(tmp), Translate("Enable Listening To for %s"), proto->GetDescription());
+
+ ZeroMemory(&mii, sizeof(mii));
+ mii.cbSize = sizeof(mii);
+ mii.fMask = MIIM_ID | MIIM_TYPE | MIIM_STATE;
+ mii.fType = MFT_STRING;
+ mii.fState = proto->ListeningToEnabled() ? MFS_CHECKED : 0;
+ mii.dwTypeData = tmp;
+ mii.cch = strlen(tmp);
+ mii.wID = 5;
+
+ if (!proto->CanSetListeningTo())
+ {
+ mii.fState |= MFS_DISABLED;
+ }
+
+ InsertMenuItem(submenu, 0, TRUE, &mii);
+
+ // Add this to menu
+ mir_snprintf(tmp, sizeof(tmp), Translate("Set My Status Message for %s..."),
+ CallService(MS_CLIST_GETSTATUSMODEDESCRIPTION, proto->GetStatus(), 0));
+
+ ZeroMemory(&mii, sizeof(mii));
+ mii.cbSize = sizeof(mii);
+ mii.fMask = MIIM_ID | MIIM_TYPE;
+ mii.fType = MFT_STRING;
+ mii.dwTypeData = tmp;
+ mii.cch = strlen(tmp);
+ mii.wID = 4;
+
+ if (proto->GetStatus() == ID_STATUS_OFFLINE)
+ {
+ mii.fMask |= MIIM_STATE;
+ mii.fState = MFS_DISABLED;
+ }
+
+ InsertMenuItem(submenu, 0, TRUE, &mii);
+
+ // Add this proto to menu
+ mir_snprintf(tmp, sizeof(tmp), Translate("Set My Status Message for %s..."), proto->GetDescription());
+
+ ZeroMemory(&mii, sizeof(mii));
+ mii.cbSize = sizeof(mii);
+ mii.fMask = MIIM_ID | MIIM_TYPE;
+ mii.fType = MFT_STRING;
+ mii.dwTypeData = tmp;
+ mii.cch = strlen(tmp);
+ mii.wID = 3;
+
+ if (!proto->CanSetStatusMsg())
+ {
+ mii.fMask |= MIIM_STATE;
+ mii.fState = MFS_DISABLED;
+ }
+
+ InsertMenuItem(submenu, 0, TRUE, &mii);
+
+ mir_snprintf(tmp, sizeof(tmp), Translate("Set My Nickname for %s..."), proto->GetDescription());
+
+ ZeroMemory(&mii, sizeof(mii));
+ mii.cbSize = sizeof(mii);
+ mii.fMask = MIIM_ID | MIIM_TYPE;
+ mii.fType = MFT_STRING;
+ mii.dwTypeData = tmp;
+ mii.cch = strlen(tmp);
+ mii.wID = 2;
+
+ if (!proto->CanSetNick())
+ {
+ mii.fMask |= MIIM_STATE;
+ mii.fState = MFS_DISABLED;
+ }
+
+ InsertMenuItem(submenu, 0, TRUE, &mii);
+
+ mir_snprintf(tmp, sizeof(tmp), Translate("Set My Avatar for %s..."), proto->GetDescription());
+
+ ZeroMemory(&mii, sizeof(mii));
+ mii.cbSize = sizeof(mii);
+ mii.fMask = MIIM_ID | MIIM_TYPE;
+ mii.fType = MFT_STRING;
+ mii.dwTypeData = tmp;
+ mii.cch = strlen(tmp);
+ mii.wID = 1;
+
+ if (!proto->CanSetAvatar())
+ {
+ mii.fMask |= MIIM_STATE;
+ mii.fState = MFS_DISABLED;
+ }
+
+ InsertMenuItem(submenu, 0, TRUE, &mii);
+
+ ZeroMemory(&mii, sizeof(mii));
+ mii.cbSize = sizeof(mii);
+ mii.fMask = MIIM_STATE;
+ mii.fState = protocols->ListeningToEnabled() ? MFS_CHECKED : 0;
+
+ if (!protocols->CanSetListeningTo())
+ {
+ mii.fState |= MFS_DISABLED;
+ }
+
+ SetMenuItemInfo(submenu, ID_CONTEXTPOPUP_ENABLELISTENINGTO, FALSE, &mii);
+
+ ClientToScreen(hwnd, &p);
+
+ int ret = TrackPopupMenu(submenu, TPM_TOPALIGN|TPM_LEFTALIGN|TPM_RIGHTBUTTON|TPM_RETURNCMD, p.x, p.y, 0, hwnd, NULL);
+ DestroyMenu(menu);
+
+ switch(ret)
+ {
+ case 1:
+ {
+ CallService(MS_MYDETAILS_SETMYAVATARUI, 0, (LPARAM) proto->GetName());
+ break;
+ }
+ case ID_AVATARPOPUP_SETMYAVATAR:
+ {
+ CallService(MS_MYDETAILS_SETMYAVATARUI, 0, 0);
+ break;
+ }
+ case 2:
+ {
+ CallService(MS_MYDETAILS_SETMYNICKNAMEUI, 0, (LPARAM) proto->GetName());
+ break;
+ }
+ case ID_NICKPOPUP_SETMYNICKNAME:
+ {
+ CallService(MS_MYDETAILS_SETMYNICKNAMEUI, 0, 0);
+ break;
+ }
+ case 3:
+ {
+ CallService(MS_MYDETAILS_SETMYSTATUSMESSAGEUI, 0, (LPARAM) proto->GetName());
+ break;
+ }
+ case 4:
+ {
+ CallService(MS_MYDETAILS_SETMYSTATUSMESSAGEUI, (WPARAM) proto->GetStatus(), 0);
+ break;
+ }
+ case ID_STATUSMESSAGEPOPUP_SETMYSTATUSMESSAGE:
+ {
+ CallService(MS_MYDETAILS_SETMYSTATUSMESSAGEUI, 0, 0);
+ break;
+ }
+ case 5:
+ {
+ CallService(MS_LISTENINGTO_ENABLE, (LPARAM) proto->GetName(), !proto->ListeningToEnabled());
+ break;
+ }
+ case ID_CONTEXTPOPUP_ENABLELISTENINGTO:
+ {
+ CallService(MS_LISTENINGTO_ENABLE, 0, !protocols->ListeningToEnabled());
+ break;
+ }
+ case ID_SHOW_NEXT_PROTO:
+ {
+ CallService(MS_MYDETAILS_SHOWNEXTPROTOCOL, 0, 0);
+ break;
+ }
+ case ID_SHOW_PREV_PROTO:
+ {
+ CallService(MS_MYDETAILS_SHOWPREVIOUSPROTOCOL, 0, 0);
+ break;
+ }
+ case ID_CYCLE_THROUGH_PROTOS:
+ {
+ CallService(MS_MYDETAILS_CYCLE_THROUGH_PROTOCOLS, TRUE, 0);
+ break;
+ }
+ case ID_DONT_CYCLE_THROUGH_PROTOS:
+ {
+ CallService(MS_MYDETAILS_CYCLE_THROUGH_PROTOCOLS, FALSE, 0);
+ break;
+ }
+ }
+ }
+
+ data->showing_menu = false;
+
+
+ break;
+ }
+
+ case WM_NCMOUSELEAVE:
+ case WM_MOUSELEAVE:
+ {
+ MyDetailsFrameData *data = (MyDetailsFrameData *)GetWindowLong(hwnd, GWL_USERDATA);
+ data->tracking_exit = false;
+ }
+ case WM_NCMOUSEMOVE:
+ {
+ MyDetailsFrameData *data = (MyDetailsFrameData *)GetWindowLong(hwnd, GWL_USERDATA);
+
+ bool changed = false;
+ for(size_t i = 0; i < data->items.size(); ++i)
+ changed = changed || data->items[i]->setMouseOver(NULL);
+
+ if (changed)
+ InvalidateRect(hwnd, NULL, FALSE);
+
+ break;
+ }
+
+ case WM_MOUSEMOVE:
+ {
+ MyDetailsFrameData *data = (MyDetailsFrameData *)GetWindowLong(hwnd, GWL_USERDATA);
+ Protocol *proto = GetCurrentProtocol();
+ if (proto == NULL)
+ break;
+
+ if (!data->tracking_exit)
+ {
+ TRACKMOUSEEVENT tme;
+ tme.cbSize = sizeof(TRACKMOUSEEVENT);
+ tme.dwFlags = TME_LEAVE;
+ tme.hwndTrack = hwnd;
+ tme.dwHoverTime = HOVER_DEFAULT;
+ TrackMouseEvent(&tme);
+
+ data->tracking_exit = true;
+ }
+
+ POINT p;
+ p.x = LOWORD(lParam);
+ p.y = HIWORD(lParam);
+
+ bool changed = false;
+ for(size_t i = 0; i < data->items.size(); ++i)
+ changed = changed || data->items[i]->setMouseOver(&p);
+
+ if (changed)
+ InvalidateRect(hwnd, NULL, FALSE);
+
+ break;
+ }
+
+ case WM_NOTIFY:
+ {
+ LPNMHDR lpnmhdr = (LPNMHDR) lParam;
+
+ int i = (int) lpnmhdr->code;
+
+ switch (lpnmhdr->code) {
+ case TTN_GETDISPINFO:
+ {
+ MyDetailsFrameData *data = (MyDetailsFrameData *)GetWindowLong(hwnd, GWL_USERDATA);
+
+ LPNMTTDISPINFO lpttd = (LPNMTTDISPINFO) lpnmhdr;
+ SendMessage(lpnmhdr->hwndFrom, TTM_SETMAXTIPWIDTH, 0, 300);
+
+ for(int i = 0; i < data->items.size(); i++)
+ {
+ lpttd->lpszText = (char *) data->items[i]->getToolTipFor(lpnmhdr->hwndFrom);
+ if (lpttd->lpszText != NULL)
+ break;
+ }
+
+ return 0;
+ }
+ }
+
+ break;
+ }
+
+ case WM_DESTROY:
+ {
+ KillTimer(hwnd, ID_FRAME_TIMER);
+
+ MyDetailsFrameData *tmp = (MyDetailsFrameData *)GetWindowLong(hwnd, GWL_USERDATA);
+ if (tmp != NULL) delete tmp;
+
+ break;
+ }
+
+ // Custom Messages //////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+ case MWM_REFRESH:
+ {
+ KillTimer(hwnd, ID_RECALC_TIMER);
+ SetTimer(hwnd, ID_RECALC_TIMER, RECALC_TIME, NULL);
+ break;
+ }
+
+ case MWM_REFRESH_DATA:
+ {
+ Protocol *proto = GetCurrentProtocol(false);
+ if (proto)
+ {
+ proto->UpdateAll();
+ RedrawFrame();
+ }
+ break;
+ }
+ }
+
+ return DefWindowProc(hwnd, msg, wParam, lParam);
+}
+
+
+int ShowHideFrameFunc(WPARAM wParam, LPARAM lParam)
+{
+ if (ServiceExists(MS_CLIST_FRAMES_ADDFRAME))
+ {
+ CallService(MS_CLIST_FRAMES_SHFRAME, frame_id, 0);
+ }
+ else
+ {
+ if (MyDetailsFrameVisible())
+ {
+ SendMessage(hwnd_container, WM_CLOSE, 0, 0);
+ }
+ else
+ {
+ ShowWindow(hwnd_container, SW_SHOW);
+ DBWriteContactSettingByte(0, MODULE_NAME, SETTING_FRAME_VISIBLE, 1);
+ }
+
+ FixMainMenu();
+ }
+ return 0;
+}
+
+
+int ShowFrameFunc(WPARAM wParam, LPARAM lParam)
+{
+ if (ServiceExists(MS_CLIST_FRAMES_ADDFRAME))
+ {
+ int flags = CallService(MS_CLIST_FRAMES_GETFRAMEOPTIONS, MAKEWPARAM(FO_FLAGS, frame_id), 0);
+ if(!(flags & F_VISIBLE))
+ CallService(MS_CLIST_FRAMES_SHFRAME, frame_id, 0);
+ }
+ else
+ {
+ if (!MyDetailsFrameVisible())
+ {
+ ShowWindow(hwnd_container, SW_SHOW);
+ DBWriteContactSettingByte(0, MODULE_NAME, SETTING_FRAME_VISIBLE, 1);
+
+ FixMainMenu();
+ }
+
+ }
+ return 0;
+}
+
+
+int HideFrameFunc(WPARAM wParam, LPARAM lParam)
+{
+ if (ServiceExists(MS_CLIST_FRAMES_ADDFRAME))
+ {
+ int flags = CallService(MS_CLIST_FRAMES_GETFRAMEOPTIONS, MAKEWPARAM(FO_FLAGS, frame_id), 0);
+ if (flags & F_VISIBLE)
+ CallService(MS_CLIST_FRAMES_SHFRAME, frame_id, 0);
+ }
+ else
+ {
+ if (MyDetailsFrameVisible())
+ {
+ SendMessage(hwnd_container, WM_CLOSE, 0, 0);
+
+ FixMainMenu();
+ }
+ }
+ return 0;
+}
+
+
+void FixMainMenu()
+{
+ CLISTMENUITEM mi = {0};
+ mi.cbSize = sizeof(CLISTMENUITEM);
+ mi.flags = CMIM_NAME;
+
+ if(MyDetailsFrameVisible())
+ mi.pszName = Translate("Hide My Details");
+ else
+ mi.pszName = Translate("Show My Details");
+
+ CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM)hMenuShowHideFrame, (LPARAM)&mi);
+}
+
+#include <math.h>
+
+void RedrawFrame()
+{
+ if (frame_id == -1)
+ {
+ InvalidateRect(hwnd_container, NULL, TRUE);
+ }
+ else
+ {
+ CallService(MS_CLIST_FRAMES_UPDATEFRAME, (WPARAM)frame_id, (LPARAM)FU_TBREDRAW | FU_FMREDRAW);
+ }
+}
+
+void UpdateFrameData()
+{
+ if (hwnd_frame != NULL)
+ PostMessage(hwnd_frame, MWM_REFRESH, 0, 0);
+}
+
+// only used when no multiwindow functionality is available
+BOOL MyDetailsFrameVisible()
+{
+ return IsWindowVisible(hwnd_container) ? true : false;
+}
+
+void SetMyDetailsFrameVisible(BOOL visible)
+{
+ if (frame_id == -1 && hwnd_container != 0)
+ {
+ ShowWindow(hwnd_container, visible ? SW_SHOW : SW_HIDE);
+ }
+}
+
+void SetCycleTime()
+{
+ if (hwnd_frame != NULL)
+ SetCycleTime(hwnd_frame);
+}
+
+void SetCycleTime(HWND hwnd)
+{
+ KillTimer(hwnd, ID_FRAME_TIMER);
+
+ if (opts.cycle_through_protocols)
+ SetTimer(hwnd, ID_FRAME_TIMER, opts.seconds_to_show_protocol * 1000, 0);
+}
+
+void SetStatusMessageRefreshTime()
+{
+ if (hwnd_frame != NULL)
+ SetStatusMessageRefreshTime(hwnd_frame);
+}
+
+void SetStatusMessageRefreshTime(HWND hwnd)
+{
+ KillTimer(hwnd, ID_STATUSMESSAGE_TIMER);
+
+ opts.refresh_status_message_timer = DBGetContactSettingWord(NULL, "MyDetails", "RefreshStatusMessageTimer",5);
+ if (opts.refresh_status_message_timer > 0)
+ {
+ SetTimer(hwnd, ID_STATUSMESSAGE_TIMER, opts.refresh_status_message_timer * 1000, NULL);
+ }
+}
+
+int PluginCommand_ShowNextProtocol(WPARAM wParam,LPARAM lParam)
+{
+ if (hwnd_frame == NULL)
+ return -1;
+
+ SetCurrentProtocol(GetCurrentProtocolIndex() + 1);
+
+ SetCycleTime();
+
+ RedrawFrame();
+
+ return 0;
+}
+
+int PluginCommand_ShowPreviousProtocol(WPARAM wParam,LPARAM lParam)
+{
+ if (hwnd_frame == NULL)
+ return -1;
+
+ SetCurrentProtocol(GetCurrentProtocolIndex() - 1);
+
+ SetCycleTime();
+
+ RedrawFrame();
+
+ return 0;
+}
+
+int PluginCommand_ShowProtocol(WPARAM wParam,LPARAM lParam)
+{
+ char * proto = (char *)lParam;
+
+ if (proto == NULL)
+ return -1;
+
+ int proto_num = GetProtocolIndexByName(proto);
+ if (proto_num == -1)
+ return -2;
+
+ if (hwnd_frame == NULL)
+ return -3;
+
+ SetCurrentProtocol(proto_num);
+
+ SetCycleTime();
+
+ RedrawFrame();
+
+ return 0;
+}
+
+int SettingsChangedHook(WPARAM wParam, LPARAM lParam)
+{
+ if (hwnd_frame == NULL)
+ return 0;
+
+ DBCONTACTWRITESETTING *cws = (DBCONTACTWRITESETTING*)lParam;
+
+ if (wParam != NULL)
+ return 0;
+
+ if (strstr(cws->szModule,"Away"))
+ {
+ // Status message changed
+ UpdateFrameData();
+ return 0;
+ }
+
+ Protocol *proto = GetCurrentProtocol(false);
+ if (proto == NULL || strcmp(proto->GetName(), cws->szModule) != 0)
+ return 0;
+
+ if (!strcmp(cws->szSetting,"Status")
+ || !strcmp(cws->szSetting,"StatusMood")
+ || !strcmp(cws->szSetting,"XStatusName")
+ || !strcmp(cws->szSetting,"XStatusMsg")
+ || !strcmp(cws->szSetting,"XStatusId")
+ || ( proto->GetCustomStatus() != 0 && !strcmp(cws->szSetting, proto->GetCustomStatusNameKey()) )
+ || ( proto->GetCustomStatus() != 0 && !strcmp(cws->szSetting, proto->GetCustomStatusMessageKey()) ))
+ {
+ // Status changed
+ UpdateFrameData();
+ }
+ else if(!strcmp(cws->szSetting,"MyHandle")
+ || !strcmp(cws->szSetting,"UIN")
+ || !strcmp(cws->szSetting,"Nick")
+ || !strcmp(cws->szSetting,"FirstName")
+ || !strcmp(cws->szSetting,"e-mail")
+ || !strcmp(cws->szSetting,"LastName")
+ || !strcmp(cws->szSetting,"JID"))
+ {
+ // Name changed
+ UpdateFrameData();
+ }
+ else if (strcmp(cws->szSetting,"ListeningTo") == 0)
+ {
+ UpdateFrameData();
+ }
+ else if (strcmp(cws->szSetting,"LockMainStatus") == 0)
+ {
+ UpdateFrameData();
+ }
+
+ return 0;
+}
+
+int AvatarChangedHook(WPARAM wParam, LPARAM lParam)
+{
+ if (hwnd_frame == NULL)
+ return 0;
+
+ Protocol *proto = GetCurrentProtocol(false);
+ if (proto == NULL || strcmp(proto->GetName(), (const char *) wParam) != 0)
+ return 0;
+
+ UpdateFrameData();
+
+ return 0;
+}
+
+int ProtoAckHook(WPARAM wParam, LPARAM lParam)
+{
+ if (hwnd_frame == NULL)
+ return 0;
+
+ ACKDATA *ack = (ACKDATA*) lParam;
+ if (ack->hContact != NULL)
+ return 0;
+
+ Protocol *proto = GetCurrentProtocol(false);
+ if (proto == NULL || strcmp(proto->GetName(), ack->szModule) != 0)
+ return 0;
+
+ if (ack->type == ACKTYPE_STATUS)
+ {
+ UpdateFrameData();
+ }
+ else if (ack->type == ACKTYPE_AWAYMSG)
+ {
+ UpdateFrameData();
+ }
+ else if (ack->type == ACKTYPE_EMAIL)
+ {
+ UpdateFrameData();
+ }
+
+ return 0;
+}
+
+int ListeningtoEnableStateChangedHook(WPARAM wParam,LPARAM lParam)
+{
+ if (hwnd_frame == NULL)
+ return 0;
+
+ Protocol *proto = GetCurrentProtocol(false);
+ if (proto == NULL || strcmp(proto->GetName(), (const char *) wParam) != 0)
+ return 0;
+
+ UpdateFrameData();
+
+ return 0;
+}
+
+int AccListChanged(WPARAM wParam, LPARAM lParam)
+{
+ SetCurrentProtocol(0);
+
+ RedrawFrame();
+
+ return 0;
+}
diff --git a/Plugins/mydetails/frame.h b/Plugins/mydetails/frame.h
new file mode 100644
index 0000000..58aa800
--- /dev/null
+++ b/Plugins/mydetails/frame.h
@@ -0,0 +1,38 @@
+/*
+Copyright (C) 2005 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#ifndef __FRAME_H__
+# define __FRAME_H__
+
+
+void InitFrames();
+void DeInitFrames();
+
+void UpdateFrameData();
+void RedrawFrame();
+
+void SetCycleTime();
+
+int PluginCommand_ShowNextProtocol(WPARAM wParam,LPARAM lParam);
+int PluginCommand_ShowPreviousProtocol(WPARAM wParam,LPARAM lParam);
+int PluginCommand_ShowProtocol(WPARAM wParam,LPARAM lParam);
+
+
+#endif // __FRAME_H__ \ No newline at end of file
diff --git a/Plugins/mydetails/m_mydetails.h b/Plugins/mydetails/m_mydetails.h
new file mode 100644
index 0000000..ad60433
--- /dev/null
+++ b/Plugins/mydetails/m_mydetails.h
@@ -0,0 +1,187 @@
+/*
+Copyright (C) 2005 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#ifndef __M_MYDETAILS_H__
+# define __M_MYDETAILS_H__
+
+
+#define MIID_MDETAILS { 0xdba18dbc, 0x5be6, 0x4504, { 0xa7, 0x99, 0x29, 0xec, 0x6c, 0xca, 0x0, 0x65 } }
+
+
+
+/*
+MyDetails/SetMyNickname service
+Set the nickname for all possible protocols
+
+wparam = (const char *) protocol name or NULL for all protocols
+lparam = (const char *) new nickname
+returns: -2 if proto can't set this, -1 on protocol not found, else 0
+*/
+#define MS_MYDETAILS_SETMYNICKNAME "MyDetails/SetMyNickname"
+
+
+/*
+MyDetails/SetMyNicknameUI service
+Shows a dialog to set the nickname for all possible protocols
+
+wparam = 0
+lparam = (const char *) protocol name or NULL for all protocols
+returns: -2 if proto can't set this, -1 on protocol not found, else 0
+*/
+#define MS_MYDETAILS_SETMYNICKNAMEUI "MyDetails/SetMyNicknameUI"
+
+
+/*
+MyDetails/SetMyAvatar service
+Set the avatar for all possible protocols
+
+wparam = (const char *) protocol name or NULL for all protocols
+lparam = (const char *) new avatar file name
+returns: -2 if proto can't set this, -1 on protocol not found, else 0
+*/
+#define MS_MYDETAILS_SETMYAVATAR "MyDetails/SetMyAvatar"
+
+
+/*
+MyDetails/SetMyAvatarUI service
+Shows a dialog to set the avatar for all possible protocols
+
+wparam = 0
+lparam = (const char *) protocol name or NULL for all protocols
+returns: -2 if proto can't set this, -1 on protocol not found, else 0
+*/
+#define MS_MYDETAILS_SETMYAVATARUI "MyDetails/SetMyAvatarUI"
+
+
+/*
+MyDetails/GetMyNickname service
+Get the nickname
+
+wparam = (const char *) protocol name or NULL for default nick
+lparam = (char *) the buffer to save the nickname. Has to have at least 1024 chars
+returns: -1 on protocol not found, else 0
+*/
+#define MS_MYDETAILS_GETMYNICKNAME "MyDetails/GetMyNickname"
+#define MS_MYDETAILS_GETMYNICKNAME_BUFFER_SIZE 1024
+
+
+/*
+MyDetails/GetMyAvatar service
+Get the avatar file name
+
+wparam = (const char *) protocol name or NULL for default avatar
+lparam = (char *) the buffer to save the file name. Has to have at least 1024 chars
+returns: -2 if proto can't get this, -1 on protocol not found, else 0
+*/
+#define MS_MYDETAILS_GETMYAVATAR "MyDetails/GetMyAvatar"
+#define MS_MYDETAILS_GETMYAVATAR_BUFFER_SIZE 1024
+
+
+/*
+MyDetails/SetMyStatusMessageUI service
+Shows a dialog to set the status message for all possible protocols
+Today only works if NAS is installed.
+
+wparam = 0
+lparam = (const char *) protocol name or NULL for all protocols
+returns: -2 if proto can't set this, -1 on protocol not found, else 0
+*/
+#define MS_MYDETAILS_SETMYSTATUSMESSAGEUI "MyDetails/SetMyStatusMessageUI"
+#define MS_MYDETAILS_GETMYSTATUSMESSAGE_BUFFER_SIZE 1024
+
+
+/*
+MyDetails/ShowNextProtocol service
+Shows the next protocol in the frame
+
+wparam = 0
+lparam = 0
+returns: -1 on error, 0 on success
+*/
+#define MS_MYDETAILS_SHOWNEXTPROTOCOL "MyDetails/ShowNextProtocol"
+
+
+/*
+MyDetails/ShowPreviousProtocol service
+Shows the previous protocol in the frame
+
+wparam = 0
+lparam = 0
+returns: -1 on error, 0 on success
+*/
+#define MS_MYDETAILS_SHOWPREVIOUSPROTOCOL "MyDetails/ShowPreviousProtocol"
+
+
+/*
+MyDetails/ShowProtocol service
+Shows a protocol given its name in the frame
+
+wparam = 0
+lparam = protocol name
+returns: -1 on error, 0 on success
+*/
+#define MS_MYDETAILS_SHOWPROTOCOL "MyDetails/ShowProtocol"
+
+
+/*
+MyDetails/CicleThroughtProtocols service
+Start/stops the cicling throught protocols
+
+wparam = FALSE to stop, TRUE to start
+lparam = 0
+returns: -1 on error, 0 on success
+*/
+#define MS_MYDETAILS_CYCLE_THROUGH_PROTOCOLS "MyDetails/CicleThroughtProtocols"
+
+
+/*
+MyDetails/ShowFrame service
+Shows the MyDetails frame/window if it is hidden
+
+wparam = 0
+lparam = 0
+returns: 0
+*/
+#define MS_MYDETAILS_SHOWFRAME "MyDetails/ShowFrame"
+
+
+/*
+MyDetails/HideFrame service
+Hides the MyDetails frame/window if it is shown
+
+wparam = 0
+lparam = 0
+returns: 0
+*/
+#define MS_MYDETAILS_HIDEFRAME "MyDetails/HideFrame"
+
+
+/*
+MyDetails/ShowHideMyDetails service
+Shows the MyDetails frame/window if it is hidden or hides the MyDetails frame/window if it is shown
+
+wparam = 0
+lparam = 0
+returns: 0
+*/
+#define MS_MYDETAILS_SHOWHIDEFRAME "MyDetails/ShowHideMyDetails"
+
+
+#endif
diff --git a/Plugins/mydetails/m_simpleaway.h b/Plugins/mydetails/m_simpleaway.h
new file mode 100644
index 0000000..e451c93
--- /dev/null
+++ b/Plugins/mydetails/m_simpleaway.h
@@ -0,0 +1,84 @@
+/*
+
+SimpleAway plugin for Miranda-IM
+
+Copyright © 2005 Harven, © 2006-2008 Dezeath
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef M_SIMPLEAWAY_H__
+#define M_SIMPLEAWAY_H__ 1
+
+// Represents status that a protocol(s) is/are currently in
+#define ID_STATUS_CURRENT 40082
+
+// Force a change of global status mode/message
+// wParam = (int)new status, from statusmodes.h or ID_STATUS_CURRENT
+// lParam = (char *)status message
+#define MS_SA_SETSTATUSMODE "SimpleAway/SetStatusMode"
+#define MS_AWAYSYS_SETSTATUSMODE MS_SA_SETSTATUSMODE // for compatibility with some plugins
+
+// Brings up the status message dialog
+// wParam = 0
+// lParam = (char *)protocol name, NULL if for all protocols
+#define MS_SA_SHOWSTATUSMSGDIALOG "SimpleAway/ShowStatusMessageDialog"
+
+// Similar to the service above, for internal use only
+#define MS_SA_TTCHANGESTATUSMSG "SimpleAway/TTChangeStatusMessage"
+
+// Force a change of status mode/message. The status message dialog will appear,
+// depending on the configuration of the user
+// wParam = (int)new status, from statusmodes.h
+// lParam = (char *)protocol name, NULL if for all protocols
+// Returns 1 when changed without showing the status message dialog
+#define MS_SA_CHANGESTATUSMSG "SimpleAway/ChangeStatusMessage"
+
+// For checking if SimpleAway is running
+// wParam = lParam = 0
+// Always returns 1
+#define MS_SA_ISSARUNNING "SimpleAway/IsSARunning"
+
+// Copy the away/na/etc message of a contact
+// wParam = (WPARAM)(HANDLE)hContact
+// lParam = 0
+// Returns 0 on success or nonzero on failure
+// Returns immediately, without waiting for the message to retrieve
+#define MS_SA_COPYAWAYMSG "SimpleAway/CopyAwayMsg"
+
+// Returns the default status message for a status in specified protocol module
+// or the current status message for the specified protocol if ID_STATUS_CURRENT is used
+// wParam = (int)status, from statusmodes.h or ID_STATUS_CURRENT
+// lParam = (char *)protocol name, NULL if for all protocols
+// Returns status msg. Remember to free the return value
+#ifndef MS_AWAYMSG_GETSTATUSMSG
+#define MS_AWAYMSG_GETSTATUSMSG "SRAway/GetStatusMessage"
+#endif
+
+// Force a change to specified global status mode/message
+// (calls MS_SA_CHANGESTATUSMSG with proper parameters)
+// wParam = lParam = 0
+#define MS_SA_SETOFFLINESTATUS "SimpleAway/SetOfflineStatus"
+#define MS_SA_SETONLINESTATUS "SimpleAway/SetOnlineStatus"
+#define MS_SA_SETAWAYSTATUS "SimpleAway/SetAwayStatus"
+#define MS_SA_SETDNDSTATUS "SimpleAway/SetDNDStatus"
+#define MS_SA_SETNASTATUS "SimpleAway/SetNAStatus"
+#define MS_SA_SETOCCUPIEDSTATUS "SimpleAway/SetOccupiedStatus"
+#define MS_SA_SETFREECHATSTATUS "SimpleAway/SetFreeChatStatus"
+#define MS_SA_SETINVISIBLESTATUS "SimpleAway/SetInvisibleStatus"
+#define MS_SA_SETONTHEPHONESTATUS "SimpleAway/SetOnThePhoneStatus"
+#define MS_SA_SETOUTTOLUNCHSTATUS "SimpleAway/SetOutToLunchStatus"
+
+#endif // M_SIMPLEAWAY_H__
diff --git a/Plugins/mydetails/mydetails.cpp b/Plugins/mydetails/mydetails.cpp
new file mode 100644
index 0000000..9351765
--- /dev/null
+++ b/Plugins/mydetails/mydetails.cpp
@@ -0,0 +1,992 @@
+/*
+Copyright (C) 2005 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#include "commons.h"
+#include "mydetails.h"
+
+
+// Prototypes /////////////////////////////////////////////////////////////////////////////////////
+
+
+HINSTANCE hInst;
+PLUGINLINK *pluginLink;
+
+PLUGININFOEX pluginInfo={
+ sizeof(PLUGININFOEX),
+ "My Details",
+ PLUGIN_MAKE_VERSION(0,0,2,6),
+ "Show and allows you to edit your details for all protocols.",
+ "Ricardo Pescuma Domenecci, Drugwash",
+ "",
+ "© 2005-2010 Ricardo Pescuma Domenecci, Drugwash",
+ "http://pescuma.org/miranda/mydetails",
+ 0, //not transient
+ 0, //doesn't replace anything built-in
+ { 0xa82baeb3, 0xa33c, 0x4036, { 0xb8, 0x37, 0x78, 0x3, 0xa5, 0xb6, 0xc2, 0xab } } // {A82BAEB3-A33C-4036-B837-7803A5B6C2AB}
+};
+
+
+struct MM_INTERFACE mmi;
+struct UTF8_INTERFACE utfi;
+struct SKIN_INTERFACE mski;
+
+
+HANDLE hTTB = NULL;
+
+// Hooks
+HANDLE hModulesLoadedHook = NULL;
+HANDLE hPreShutdownHook = NULL;
+HANDLE hColorChangedHook = NULL;
+
+long nickname_dialog_open;
+HWND hwndSetNickname;
+
+long status_msg_dialog_open;
+HWND hwndSetStatusMsg;
+
+SkinDialog *dialog;
+
+
+// Hook called after init
+static int MainInit(WPARAM wparam,LPARAM lparam);
+static int MainUninit(WPARAM wParam, LPARAM lParam);
+
+
+// Services
+static int PluginCommand_SetMyNicknameUI(WPARAM wParam,LPARAM lParam);
+static int PluginCommand_SetMyNickname(WPARAM wParam,LPARAM lParam);
+static int PluginCommand_GetMyNickname(WPARAM wParam,LPARAM lParam);
+static int PluginCommand_SetMyAvatarUI(WPARAM wParam,LPARAM lParam);
+static int PluginCommand_SetMyAvatar(WPARAM wParam,LPARAM lParam);
+static int PluginCommand_GetMyAvatar(WPARAM wParam,LPARAM lParam);
+static int PluginCommand_SetMyStatusMessageUI(WPARAM wParam,LPARAM lParam);
+static int PluginCommand_CicleThroughtProtocols(WPARAM wParam,LPARAM lParam);
+
+
+
+
+// Functions //////////////////////////////////////////////////////////////////////////////////////
+
+
+BOOL WINAPI DllMain(HINSTANCE hinstDLL,DWORD fdwReason,LPVOID lpvReserved)
+{
+ hInst = hinstDLL;
+ return TRUE;
+}
+
+
+extern "C" __declspec(dllexport) PLUGININFO* MirandaPluginInfo(DWORD mirandaVersion)
+{
+ pluginInfo.cbSize = sizeof(PLUGININFO);
+ return (PLUGININFO*) &pluginInfo;
+}
+
+
+extern "C" __declspec(dllexport) PLUGININFOEX* MirandaPluginInfoEx(DWORD mirandaVersion)
+{
+ pluginInfo.cbSize = sizeof(PLUGININFOEX);
+ return &pluginInfo;
+}
+
+
+static const MUUID interfaces[] = { MIID_MDETAILS, MIID_LAST };
+extern "C" __declspec(dllexport) const MUUID* MirandaPluginInterfaces(void)
+{
+ return interfaces;
+}
+
+
+int __declspec(dllexport) Load(PLUGINLINK *link)
+{
+ // Copy data
+ pluginLink = link;
+
+ // CHECK_VERSION("My Details")
+
+ mir_getMMI(&mmi);
+ mir_getUTFI(&utfi);
+
+ init_list_interface();
+
+ // Hook event to load messages and show first one
+ hModulesLoadedHook = HookEvent(ME_SYSTEM_MODULESLOADED, MainInit);
+ hPreShutdownHook = HookEvent(ME_SYSTEM_PRESHUTDOWN, MainUninit);
+
+ nickname_dialog_open = 0;
+ status_msg_dialog_open = 0;
+
+ // Options
+ InitOptions();
+
+ // Register services
+ CreateServiceFunction(MS_MYDETAILS_SETMYNICKNAME, PluginCommand_SetMyNickname);
+ CreateServiceFunction(MS_MYDETAILS_SETMYNICKNAMEUI, PluginCommand_SetMyNicknameUI);
+ CreateServiceFunction(MS_MYDETAILS_SETMYAVATAR, PluginCommand_SetMyAvatar);
+ CreateServiceFunction(MS_MYDETAILS_SETMYAVATARUI, PluginCommand_SetMyAvatarUI);
+ CreateServiceFunction(MS_MYDETAILS_GETMYNICKNAME, PluginCommand_GetMyNickname);
+ CreateServiceFunction(MS_MYDETAILS_GETMYAVATAR, PluginCommand_GetMyAvatar);
+ CreateServiceFunction(MS_MYDETAILS_SETMYSTATUSMESSAGEUI, PluginCommand_SetMyStatusMessageUI);
+ CreateServiceFunction(MS_MYDETAILS_SHOWNEXTPROTOCOL, PluginCommand_ShowNextProtocol);
+ CreateServiceFunction(MS_MYDETAILS_SHOWPREVIOUSPROTOCOL, PluginCommand_ShowPreviousProtocol);
+ CreateServiceFunction(MS_MYDETAILS_SHOWPROTOCOL, PluginCommand_ShowProtocol);
+ CreateServiceFunction(MS_MYDETAILS_CYCLE_THROUGH_PROTOCOLS, PluginCommand_CicleThroughtProtocols);
+
+ return 0;
+}
+
+
+int __declspec(dllexport) Unload(void)
+{
+ DestroyServiceFunction(MS_MYDETAILS_SETMYNICKNAME);
+ DestroyServiceFunction(MS_MYDETAILS_SETMYNICKNAMEUI);
+ DestroyServiceFunction(MS_MYDETAILS_SETMYAVATAR);
+ DestroyServiceFunction(MS_MYDETAILS_SETMYAVATARUI);
+ DestroyServiceFunction(MS_MYDETAILS_GETMYNICKNAME);
+ DestroyServiceFunction(MS_MYDETAILS_GETMYAVATAR);
+ DestroyServiceFunction(MS_MYDETAILS_SETMYSTATUSMESSAGEUI);
+ DestroyServiceFunction(MS_MYDETAILS_SHOWNEXTPROTOCOL);
+ DestroyServiceFunction(MS_MYDETAILS_SHOWPREVIOUSPROTOCOL);
+ DestroyServiceFunction(MS_MYDETAILS_SHOWPROTOCOL);
+ DestroyServiceFunction(MS_MYDETAILS_CYCLE_THROUGH_PROTOCOLS);
+
+ if (hModulesLoadedHook) UnhookEvent(hModulesLoadedHook);
+
+ DeInitProtocolData();
+ DeInitOptions();
+
+ return 0;
+}
+
+
+static int Menu_SetMyAvatarUI(WPARAM wParam,LPARAM lParam)
+{
+ return PluginCommand_SetMyAvatarUI(0, 0);
+}
+static int Menu_SetMyNicknameUI(WPARAM wParam,LPARAM lParam)
+{
+ return PluginCommand_SetMyNicknameUI(0, 0);
+}
+static int Menu_SetMyStatusMessageUI(WPARAM wParam,LPARAM lParam)
+{
+ return PluginCommand_SetMyStatusMessageUI(0, 0);
+}
+
+static void SkinChanged(void *param, SKINNED_DIALOG dlg)
+{
+ RedrawFrame();
+}
+
+
+static int ColorChanged(WPARAM wparam, LPARAM lparam)
+{
+ ColourID cid = {0};
+ cid.cbSize = sizeof(ColourID);
+ lstrcpynA(cid.group, "My Details", sizeof(cid.group));
+ lstrcpynA(cid.name, "Background", sizeof(cid.name));
+
+ opts.bkg_color = (COLORREF) CallService(MS_COLOUR_GET, (WPARAM) &cid, 0);
+
+ RedrawFrame();
+
+ return 0;
+}
+
+
+// Hook called after init
+static int MainInit(WPARAM wparam,LPARAM lparam)
+{
+ if ( mir_skins_getInterface(&mski) != 0 )
+ {
+ MessageBox(NULL, _T("MyDetails needs Skins plugin in order to work"), _T("MyDetails"), MB_OK | MB_ICONERROR);
+ return 0;
+ }
+
+ if (CallService(MS_SKIN2_GETICON, 0, (LPARAM) "LISTENING_TO_ICON") == NULL)
+ {
+ SKINICONDESC sid = {0};
+ sid.cbSize = sizeof(SKINICONDESC);
+ sid.ptszSection = "Contact List";
+ sid.ptszDescription = "Listening to";
+ sid.pszName = "LISTENING_TO_ICON";
+ sid.hDefaultIcon = LoadIcon(hInst, MAKEINTRESOURCE(IDI_LISTENINGTO));
+ CallService(MS_SKIN2_ADDICON, 0, (LPARAM)&sid);
+ }
+
+ {
+ SKINICONDESC sid = {0};
+ sid.cbSize = sizeof(SKINICONDESC);
+ sid.ptszSection = "My Details";
+ sid.ptszDescription = "Email";
+ sid.pszName = "MYDETAILS_EMAIL";
+ sid.hDefaultIcon = LoadIcon(hInst, MAKEINTRESOURCE(IDI_EMAIL));
+ CallService(MS_SKIN2_ADDICON, 0, (LPARAM)&sid);
+ }
+
+ {
+ SKINICONDESC sid = {0};
+ sid.cbSize = sizeof(SKINICONDESC);
+ sid.ptszSection = "My Details";
+ sid.ptszDescription = "Previous protocol";
+ sid.pszName = "MYDETAILS_PREV_PROTOCOL";
+ sid.hDefaultIcon = LoadIcon(hInst, MAKEINTRESOURCE(IDI_LEFT_ARROW));
+ CallService(MS_SKIN2_ADDICON, 0, (LPARAM)&sid);
+ }
+
+ {
+ SKINICONDESC sid = {0};
+ sid.cbSize = sizeof(SKINICONDESC);
+ sid.ptszSection = "My Details";
+ sid.ptszDescription = "Next protocol";
+ sid.pszName = "MYDETAILS_NEXT_PROTOCOL";
+ sid.hDefaultIcon = LoadIcon(hInst, MAKEINTRESOURCE(IDI_RIGHT_ARROW));
+ CallService(MS_SKIN2_ADDICON, 0, (LPARAM)&sid);
+ }
+
+ {
+ ColourID cid = {0};
+ cid.cbSize = sizeof(ColourID);
+ lstrcpynA(cid.group, "My Details", sizeof(cid.group));
+ lstrcpynA(cid.name, "Background", sizeof(cid.name));
+ lstrcpynA(cid.dbSettingsGroup, MODULE_NAME, sizeof(cid.dbSettingsGroup));
+ lstrcpynA(cid.setting, "BackgroundColor", sizeof(cid.setting));
+ cid.defcolour = GetSysColor(COLOR_BTNFACE);
+
+ CallService(MS_COLOUR_REGISTER, (WPARAM) &cid, 0);
+
+ ColorChanged(0,0);
+
+ hColorChangedHook = HookEvent(ME_COLOUR_RELOAD, ColorChanged);
+ }
+
+ dialog = new SkinDialog("MyDetails", "My Details", MODULE_NAME);
+ if (!dialog->isValid())
+ {
+ MessageBox(NULL, _T("MyDetails could not create dialog. Check if default skin is installed"), _T("MyDetails"), MB_OK | MB_ICONERROR);
+ return 0;
+ }
+
+ dialog->addImageField("avatar", "Avatar");
+ dialog->addTextField("nickname", "Nickname");
+ dialog->addTextField("protocol", "Protocol");
+ dialog->addIconField("email_icon", "Unread Email Count Icon");
+ dialog->addTextField("email", "Unread Email Count");
+ dialog->addIconField("status_icon", "Status Icon");
+ dialog->addTextField("status_name", "Status");
+ dialog->addTextField("status_msg", "Status Message");
+ dialog->addIconField("listening_icon", "Listening To Icon");
+ dialog->addTextField("listening", "Listening To");
+ dialog->addIconField("next_proto", "Next Protocol");
+ dialog->addIconField("prev_proto", "Previous Protocol");
+ dialog->setSkinChangedCallback(SkinChanged, NULL);
+ dialog->finishedConfiguring();
+
+
+ InitProtocolData();
+
+ // Add options to menu
+ CLISTMENUITEM mi;
+
+ if (protocols->CanSetAvatars())
+ {
+ ZeroMemory(&mi,sizeof(mi));
+ mi.cbSize = sizeof(mi);
+ mi.flags = 0;
+ mi.popupPosition = 500050000;
+ mi.pszPopupName = Translate("My Details");
+ mi.position = 100001;
+ mi.pszName = Translate("Set My Avatar...");
+ CreateServiceFunction("MENU_" MS_MYDETAILS_SETMYAVATARUI, Menu_SetMyAvatarUI);
+ mi.pszService = "MENU_" MS_MYDETAILS_SETMYAVATARUI;
+
+ CallService(MS_CLIST_ADDMAINMENUITEM,0,(LPARAM)&mi);
+ }
+
+ ZeroMemory(&mi,sizeof(mi));
+ mi.cbSize = sizeof(mi);
+ mi.flags = 0;
+ mi.popupPosition = 500050000;
+ mi.pszPopupName = Translate("My Details");
+ mi.position = 100002;
+ mi.pszName = Translate("Set My Nickname...");
+ CreateServiceFunction("MENU_" MS_MYDETAILS_SETMYNICKNAMEUI, Menu_SetMyNicknameUI);
+ mi.pszService = "MENU_" MS_MYDETAILS_SETMYNICKNAMEUI;
+
+ CallService(MS_CLIST_ADDMAINMENUITEM,0,(LPARAM)&mi);
+
+ ZeroMemory(&mi,sizeof(mi));
+ mi.cbSize = sizeof(mi);
+ mi.flags = 0;
+ mi.popupPosition = 500050000;
+ mi.pszPopupName = Translate("My Details");
+ mi.position = 100003;
+ mi.pszName = Translate("Set My Status Message...");
+ CreateServiceFunction("MENU_" MS_MYDETAILS_SETMYSTATUSMESSAGEUI, Menu_SetMyStatusMessageUI);
+ mi.pszService = "MENU_" MS_MYDETAILS_SETMYSTATUSMESSAGEUI;
+
+ CallService(MS_CLIST_ADDMAINMENUITEM,0,(LPARAM)&mi);
+
+ // Set protocols to show frame
+ ZeroMemory(&mi,sizeof(mi));
+ mi.cbSize = sizeof(mi);
+ mi.flags = 0;
+ mi.popupPosition = 500050000;
+ mi.pszPopupName = Translate("My Details");
+ mi.position = 200001;
+ mi.pszName = Translate("Show next protocol");
+ mi.pszService = MS_MYDETAILS_SHOWNEXTPROTOCOL;
+
+ CallService(MS_CLIST_ADDMAINMENUITEM,0,(LPARAM)&mi);
+
+ InitFrames();
+
+ // updater plugin support
+ if(ServiceExists(MS_UPDATE_REGISTER))
+ {
+ Update upd = {0};
+ char szCurrentVersion[30];
+
+ upd.cbSize = sizeof(upd);
+ upd.szComponentName = pluginInfo.shortName;
+
+ upd.szUpdateURL = UPDATER_AUTOREGISTER;
+
+ upd.szBetaVersionURL = "http://svn.berlios.de/svnroot/repos/mgoodies/trunk/mydetails/Docs/mydetails_version.txt";
+ upd.szBetaChangelogURL = "http://svn.berlios.de/svnroot/repos/mgoodies/trunk/mydetails/Docs/mydetails_changelog.txt";
+ upd.pbBetaVersionPrefix = (BYTE *)"My Details ";
+ upd.cpbBetaVersionPrefix = strlen((char *)upd.pbBetaVersionPrefix);
+ upd.szBetaUpdateURL = "http://pescuma.googlecode.com/files/mydetails.%VERSION%.zip";
+
+ upd.pbVersion = (BYTE *)CreateVersionStringPlugin((PLUGININFO*) &pluginInfo, szCurrentVersion);
+ upd.cpbVersion = strlen((char *)upd.pbVersion);
+
+ CallService(MS_UPDATE_REGISTER, 0, (LPARAM)&upd);
+ }
+
+ return 0;
+}
+
+static int MainUninit(WPARAM wParam, LPARAM lParam)
+{
+ DeInitFrames();
+
+ delete dialog;
+
+ return 0;
+}
+
+// Set nickname ///////////////////////////////////////////////////////////////////////////////////
+
+#define WMU_SETDATA (WM_USER+1)
+
+static BOOL CALLBACK DlgProcSetNickname(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch ( msg )
+ {
+ case WM_INITDIALOG:
+ {
+ TranslateDialogDefault(hwndDlg);
+ SendMessage(GetDlgItem(hwndDlg, IDC_NICKNAME), EM_LIMITTEXT,
+ MS_MYDETAILS_GETMYNICKNAME_BUFFER_SIZE - 1, 0);
+
+ return TRUE;
+ }
+
+ case WMU_SETDATA:
+ {
+ int proto_num = (int)wParam;
+
+ SetWindowLong(hwndDlg, GWL_USERDATA, proto_num);
+
+ if (proto_num == -1)
+ {
+ SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)LoadSkinnedIcon(SKINICON_OTHER_MIRANDA));
+
+ // All protos have the same nick?
+
+ std::vector<Protocol> protos;
+ GetProtocols(&protos);
+
+ int protosSize = protos.size();
+ if (protosSize > 0)
+ {
+ std::string nick = protos[0].GetNick();
+
+ bool foundDefNick = true;
+ for(int i = 1; i < protosSize; i++)
+ {
+ if (stricmp(protos[i].GetNick(), nick.c_str()) != 0)
+ {
+ foundDefNick = false;
+ break;
+ }
+ }
+
+ if (foundDefNick)
+ {
+ if (stricmp(protocols->default_nick, nick.c_str()) != 0)
+ lstrcpy(protocols->default_nick, nick.c_str());
+ }
+ }
+
+ SetDlgItemText(hwndDlg, IDC_NICKNAME, protocols->default_nick);
+ SendDlgItemMessage(hwndDlg, IDC_NICKNAME, EM_LIMITTEXT, MS_MYDETAILS_GETMYNICKNAME_BUFFER_SIZE, 0);
+ }
+ else
+ {
+ Protocol proto = GetProtocolByIndex(proto_num);
+
+ char tmp[128];
+ mir_snprintf(tmp, sizeof(tmp), Translate("Set My Nickname for %s"), proto.GetDescription());
+
+ SendMessage(hwndDlg, WM_SETTEXT, 0, (LPARAM)tmp);
+
+ HICON hIcon = (HICON) proto.Call(PS_LOADICON, PLI_PROTOCOL);
+ if (hIcon != NULL)
+ {
+ SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)hIcon);
+ DestroyIcon(hIcon);
+ }
+
+ SetDlgItemText(hwndDlg, IDC_NICKNAME, proto.GetNick());
+ SendDlgItemMessage(hwndDlg, IDC_NICKNAME, EM_LIMITTEXT,
+ min(MS_MYDETAILS_GETMYNICKNAME_BUFFER_SIZE, proto.GetNickMaxLength()), 0);
+ }
+
+ return TRUE;
+ }
+
+ case WM_COMMAND:
+ switch(wParam)
+ {
+ case IDOK:
+ {
+ char tmp[MS_MYDETAILS_GETMYNICKNAME_BUFFER_SIZE];
+ GetDlgItemText(hwndDlg, IDC_NICKNAME, tmp, sizeof(tmp));
+
+ int proto_num = (int) GetWindowLong(hwndDlg, GWL_USERDATA);
+ if (proto_num == -1)
+ {
+ protocols->SetNicks(tmp);
+ }
+ else
+ {
+ GetProtocolByIndex(proto_num).SetNick(tmp);
+ }
+
+ DestroyWindow(hwndDlg);
+ break;
+ }
+ case IDCANCEL:
+ {
+ DestroyWindow(hwndDlg);
+ break;
+ }
+ }
+ break;
+
+ case WM_CLOSE:
+ DestroyWindow(hwndDlg);
+ break;
+
+ case WM_DESTROY:
+ InterlockedExchange(&nickname_dialog_open, 0);
+ break;
+ }
+
+ return FALSE;
+}
+
+static int PluginCommand_SetMyNicknameUI(WPARAM wParam,LPARAM lParam)
+{
+ char * proto = (char *)lParam;
+ int proto_num = -1;
+
+ if (proto != NULL)
+ {
+ proto_num = GetProtocolIndexByName(proto);
+ if (proto_num == -1)
+ return -1;
+
+ if (!GetProtocolByIndex(proto_num).CanSetNick())
+ return -2;
+ }
+
+ if (!nickname_dialog_open)
+ {
+ InterlockedExchange(&nickname_dialog_open, 1);
+
+ hwndSetNickname = CreateDialog(hInst, MAKEINTRESOURCE( IDD_SETNICKNAME ), NULL, DlgProcSetNickname );
+
+ SendMessage(hwndSetNickname, WMU_SETDATA, proto_num, 0);
+ }
+
+ SetForegroundWindow( hwndSetNickname );
+ SetFocus( hwndSetNickname );
+ ShowWindow( hwndSetNickname, SW_SHOW );
+
+ return 0;
+}
+
+
+static int PluginCommand_SetMyNickname(WPARAM wParam,LPARAM lParam)
+{
+ char * proto = (char *)wParam;
+
+ if (proto != NULL)
+ {
+ Protocol protocol = GetProtocolByName(proto);
+ if (!protocol)
+ return -1;
+
+ if (!protocol.CanSetNick())
+ return -2;
+
+ protocol.SetNick((char *)lParam);
+ }
+ else
+ {
+ protocols->SetNicks((char *)lParam);
+ }
+
+ return 0;
+}
+
+
+static int PluginCommand_GetMyNickname(WPARAM wParam,LPARAM lParam)
+{
+ char * ret = (char *)lParam;
+ char * proto = (char *)wParam;
+
+ if (ret == NULL)
+ return -1;
+
+ if (proto == NULL)
+ {
+ if (protocols->default_nick != NULL)
+ lstrcpyn(ret, protocols->default_nick, MS_MYDETAILS_GETMYNICKNAME_BUFFER_SIZE);
+ else
+ ret[0] = '\0';
+ }
+ else
+ {
+ Protocol protocol = GetProtocolByName(proto);
+ if (!protocol)
+ return -1;
+
+ lstrcpyn(ret, protocol.GetNick(), MS_MYDETAILS_GETMYNICKNAME_BUFFER_SIZE);
+ }
+
+ return 0;
+}
+
+
+// Set avatar /////////////////////////////////////////////////////////////////////////////////////
+
+static int PluginCommand_SetMyAvatarUI(WPARAM wParam,LPARAM lParam)
+{
+ char * proto = (char *)lParam;
+ int proto_num = -1;
+
+ if (proto != NULL)
+ {
+ Protocol protocol = GetProtocolByName(proto);
+ if (!protocol)
+ return -1;
+
+ if (!protocol.CanSetAvatar())
+ return -2;
+
+ protocol.SetAvatar(NULL);
+ }
+ else
+ {
+ protocols->SetAvatars(NULL);
+ }
+
+ return 0;
+}
+
+
+static int PluginCommand_SetMyAvatar(WPARAM wParam,LPARAM lParam)
+{
+ char * proto = (char *)wParam;
+
+ if (proto != NULL)
+ {
+ Protocol protocol = GetProtocolByName(proto);
+ if (!protocol)
+ return -1;
+
+ if (!protocol.CanSetAvatar())
+ return -2;
+
+ protocol.SetAvatar((char *)lParam);
+ }
+ else
+ {
+ protocols->SetAvatars((char *)lParam);
+ }
+
+ return 0;
+}
+
+
+int Status2SkinIcon(int status)
+{
+ switch(status) {
+ case ID_STATUS_AWAY: return SKINICON_STATUS_AWAY;
+ case ID_STATUS_NA: return SKINICON_STATUS_NA;
+ case ID_STATUS_DND: return SKINICON_STATUS_DND;
+ case ID_STATUS_OCCUPIED: return SKINICON_STATUS_OCCUPIED;
+ case ID_STATUS_FREECHAT: return SKINICON_STATUS_FREE4CHAT;
+ case ID_STATUS_ONLINE: return SKINICON_STATUS_ONLINE;
+ case ID_STATUS_OFFLINE: return SKINICON_STATUS_OFFLINE;
+ case ID_STATUS_INVISIBLE: return SKINICON_STATUS_INVISIBLE;
+ case ID_STATUS_ONTHEPHONE: return SKINICON_STATUS_ONTHEPHONE;
+ case ID_STATUS_OUTTOLUNCH: return SKINICON_STATUS_OUTTOLUNCH;
+ case ID_STATUS_IDLE: return SKINICON_STATUS_AWAY;
+ }
+ return SKINICON_STATUS_OFFLINE;
+}
+
+
+
+static int PluginCommand_GetMyAvatar(WPARAM wParam,LPARAM lParam)
+{
+ char * ret = (char *)lParam;
+ char * proto = (char *)wParam;
+
+ if (ret == NULL)
+ return -1;
+
+ if (proto == NULL)
+ {
+ if (protocols->default_avatar_file != NULL)
+ lstrcpyn(ret, protocols->default_avatar_file, MS_MYDETAILS_GETMYAVATAR_BUFFER_SIZE);
+ else
+ ret[0] = '\0';
+ }
+ else
+ {
+ Protocol protocol = GetProtocolByName(proto);
+ if (!protocol)
+ return -1;
+
+ if (!protocol.CanGetAvatar())
+ return -2;
+
+ lstrcpyn(ret, protocol.GetAvatarFile(), MS_MYDETAILS_GETMYAVATAR_BUFFER_SIZE);
+ }
+
+ return 0;
+}
+
+static LRESULT CALLBACK StatusMsgEditSubclassProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg) {
+ case WM_CHAR:
+ {
+ if(wParam == 0x0a && (GetKeyState(VK_CONTROL) & 0x8000) != 0) {
+ PostMessage(GetParent(hwnd), WM_COMMAND, IDOK, 0);
+ return 0;
+ }
+
+ break;
+ }
+ }
+
+ return CallWindowProc((WNDPROC) GetWindowLong(hwnd, GWL_USERDATA), hwnd, msg, wParam, lParam);
+}
+
+struct SetStatusMessageData {
+ int status;
+ int proto_num;
+};
+
+static BOOL CALLBACK DlgProcSetStatusMessage(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch ( msg )
+ {
+ case WM_INITDIALOG:
+ {
+ TranslateDialogDefault(hwndDlg);
+ SendMessage(GetDlgItem(hwndDlg, IDC_STATUSMESSAGE), EM_LIMITTEXT,
+ MS_MYDETAILS_GETMYSTATUSMESSAGE_BUFFER_SIZE - 1, 0);
+
+ WNDPROC old_proc = (WNDPROC) SetWindowLong(GetDlgItem(hwndDlg, IDC_STATUSMESSAGE),
+ GWL_WNDPROC, (LONG) StatusMsgEditSubclassProc);
+
+ SetWindowLong(GetDlgItem(hwndDlg, IDC_STATUSMESSAGE), GWL_USERDATA, (long) old_proc);
+
+ return TRUE;
+ }
+
+ case WMU_SETDATA:
+ {
+ SetStatusMessageData *data = (SetStatusMessageData *) malloc(sizeof(SetStatusMessageData));
+ data->status = (int)wParam;
+ data->proto_num = (int)lParam;
+
+ SetWindowLong(hwndDlg, GWL_USERDATA, (LONG) data);
+
+ if (data->proto_num >= 0)
+ {
+ Protocol proto = GetProtocolByIndex(data->proto_num);
+
+ HICON hIcon = (HICON) proto.Call(PS_LOADICON, PLI_PROTOCOL);
+ if (hIcon != NULL)
+ {
+ SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)hIcon);
+ DestroyIcon(hIcon);
+ }
+
+ char title[256];
+ mir_snprintf(title, sizeof(title), Translate("Set My Status Message for %s"),
+ proto.GetDescription());
+ SendMessage(hwndDlg, WM_SETTEXT, 0, (LPARAM)title);
+
+ SetDlgItemText(hwndDlg, IDC_STATUSMESSAGE, proto.GetStatusMsg());
+ }
+ else if (data->status != 0)
+ {
+ SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)LoadSkinnedIcon(Status2SkinIcon(data->status)));
+
+ char title[256];
+ mir_snprintf(title, sizeof(title), Translate("Set My Status Message for %s"),
+ CallService(MS_CLIST_GETSTATUSMODEDESCRIPTION, data->status, 0));
+ SendMessage(hwndDlg, WM_SETTEXT, 0, (LPARAM)title);
+
+ SetDlgItemText(hwndDlg, IDC_STATUSMESSAGE, protocols->GetDefaultStatusMsg(data->status));
+ }
+ else
+ {
+ SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)LoadSkinnedIcon(SKINICON_OTHER_MIRANDA));
+
+ SetDlgItemText(hwndDlg, IDC_STATUSMESSAGE, protocols->GetDefaultStatusMsg());
+ }
+
+ return TRUE;
+ }
+ case WM_COMMAND:
+ switch(wParam)
+ {
+ case IDOK:
+ {
+ char tmp[MS_MYDETAILS_GETMYSTATUSMESSAGE_BUFFER_SIZE];
+ GetDlgItemText(hwndDlg, IDC_STATUSMESSAGE, tmp, sizeof(tmp));
+
+ SetStatusMessageData *data = (SetStatusMessageData *) GetWindowLong(hwndDlg, GWL_USERDATA);
+
+ if (data->proto_num >= 0)
+ GetProtocolByIndex(data->proto_num).SetStatusMsg(tmp);
+ else if (data->status == 0)
+ protocols->SetStatusMsgs(tmp);
+ else
+ protocols->SetStatusMsgs(data->status, tmp);
+
+ // To force a refresh
+ UpdateFrameData();
+
+ DestroyWindow(hwndDlg);
+ break;
+ }
+ case IDCANCEL:
+ {
+ DestroyWindow(hwndDlg);
+ break;
+ }
+ }
+ break;
+
+ case WM_CLOSE:
+ DestroyWindow(hwndDlg);
+ break;
+
+ case WM_DESTROY:
+ SetWindowLong(GetDlgItem(hwndDlg, IDC_STATUSMESSAGE), GWL_WNDPROC,
+ GetWindowLong(GetDlgItem(hwndDlg, IDC_STATUSMESSAGE), GWL_USERDATA));
+ free((SetStatusMessageData *) GetWindowLong(hwndDlg, GWL_USERDATA));
+ InterlockedExchange(&status_msg_dialog_open, 0);
+ break;
+ }
+
+ return FALSE;
+}
+
+static int PluginCommand_SetMyStatusMessageUI(WPARAM wParam,LPARAM lParam)
+{
+ int status = (int)wParam;
+ char * proto_name = (char *)lParam;
+ int proto_num = -1;
+ Protocol proto(NULL);
+ TCHAR status_message[256];
+
+ if (status != 0 && (status < ID_STATUS_OFFLINE || status > ID_STATUS_OUTTOLUNCH))
+ return -10;
+
+ if (proto_name != NULL)
+ {
+ proto_num = GetProtocolIndexByName(proto_name);
+ if (proto_num == -1)
+ return -1;
+
+ proto = GetProtocolByIndex(proto_num);
+ if (!proto.CanSetStatusMsg())
+ return -2;
+ }
+
+ if (ServiceExists(MS_NAS_INVOKESTATUSWINDOW))
+ {
+ NAS_ISWINFO iswi;
+
+ ZeroMemory(&iswi, sizeof(iswi));
+
+ iswi.cbSize = sizeof(NAS_ISWINFO);
+
+ if (proto)
+ {
+ // Has to get the unparsed message
+ NAS_PROTOINFO pi;
+
+ ZeroMemory(&pi, sizeof(pi));
+ pi.cbSize = sizeof(NAS_PROTOINFO);
+ pi.szProto = (char *) proto.GetName();
+ pi.status = status;
+ pi.szMsg = NULL;
+
+ if (ServiceExists(MS_NAS_GETSTATE))
+ {
+ if (CallService(MS_NAS_GETSTATE, (WPARAM) &pi, 1) == 0)
+ {
+ if (pi.szMsg == NULL)
+ {
+ pi.szProto = NULL;
+
+ if (CallService(MS_NAS_GETSTATE, (WPARAM) &pi, 1) == 0)
+ {
+ if (pi.szMsg != NULL)
+ {
+ lstrcpyn(status_message, pi.szMsg, MAX_REGS(status_message));
+ mir_free(pi.szMsg);
+ }
+ }
+ }
+ else // if (pi.szMsg != NULL)
+ {
+ lstrcpyn(status_message, pi.szMsg, MAX_REGS(status_message));
+ mir_free(pi.szMsg);
+ }
+ }
+ }
+ // TODO: Remove when removing old NAS services support
+ else
+ {
+ NAS_PROTOINFO *pii = &pi;
+
+ // Old services
+ if (CallService("NewAwaySystem/GetState", (WPARAM) &pii, 1) == 0)
+ {
+ if (pi.szMsg == NULL)
+ {
+ pi.szProto = NULL;
+
+ if (CallService("NewAwaySystem/GetState", (WPARAM) &pii, 1) == 0)
+ {
+ if (pi.szMsg != NULL)
+ {
+ lstrcpyn(status_message, pi.szMsg, MAX_REGS(status_message));
+ mir_free(pi.szMsg);
+ }
+ }
+ }
+ else // if (pi.szMsg != NULL)
+ {
+ lstrcpyn(status_message, pi.szMsg, MAX_REGS(status_message));
+ mir_free(pi.szMsg);
+ }
+ }
+ }
+
+ iswi.szProto = (char *) proto.GetName();
+ iswi.szMsg = status_message;
+ }
+ else
+ {
+ iswi.szMsg = protocols->GetDefaultStatusMsg();
+ }
+
+ iswi.Flags = ISWF_NOCOUNTDOWN;
+
+ CallService(MS_NAS_INVOKESTATUSWINDOW, (WPARAM) &iswi, 0);
+
+ return 0;
+ }
+ else if (ServiceExists(MS_SA_SHOWSTATUSMSGDIALOG))
+ {
+ CallService(MS_SA_SHOWSTATUSMSGDIALOG, 0, (LPARAM) proto_name);
+ return 0;
+ }
+ else if (ServiceExists(MS_SA_CHANGESTATUSMSG))
+ {
+ if (!proto && status == 0)
+ {
+ CallService(MS_SA_CHANGESTATUSMSG, protocols->GetGlobalStatus(), NULL);
+ }
+ else if (status == 0)
+ {
+ CallService(MS_SA_CHANGESTATUSMSG, proto.GetStatus(), (LPARAM) proto_name);
+ }
+ else
+ {
+ CallService(MS_SA_CHANGESTATUSMSG, status, (LPARAM) proto_name);
+ }
+
+ return 0;
+ }
+ else if (!proto || proto.GetStatus() != ID_STATUS_OFFLINE)
+ {
+ if (!status_msg_dialog_open)
+ {
+ InterlockedExchange(&status_msg_dialog_open, 1);
+
+ hwndSetStatusMsg = CreateDialog(hInst, MAKEINTRESOURCE( IDD_SETSTATUSMESSAGE ), NULL, DlgProcSetStatusMessage );
+
+ SendMessage(hwndSetStatusMsg, WMU_SETDATA, status, proto_num);
+ }
+
+ SetForegroundWindow( hwndSetStatusMsg );
+ SetFocus( hwndSetStatusMsg );
+ ShowWindow( hwndSetStatusMsg, SW_SHOW );
+
+ return 0;
+ }
+
+ return -3;
+}
+
+
+static int PluginCommand_CicleThroughtProtocols(WPARAM wParam,LPARAM lParam)
+{
+ DBWriteContactSettingByte(NULL,"MyDetails","CicleThroughtProtocols", (BYTE) wParam);
+
+ LoadOptions();
+
+ return 0;
+} \ No newline at end of file
diff --git a/Plugins/mydetails/mydetails.dsp b/Plugins/mydetails/mydetails.dsp
new file mode 100644
index 0000000..e3c3bae
--- /dev/null
+++ b/Plugins/mydetails/mydetails.dsp
@@ -0,0 +1,223 @@
+# Microsoft Developer Studio Project File - Name="mydetails" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=mydetails - Win32 Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "mydetails.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "mydetails.mak" CFG="mydetails - Win32 Debug"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "mydetails - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "mydetails - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "mydetails - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "MYDETAILS_EXPORTS" /YX /FD /c
+# ADD CPP /nologo /MT /W3 /GX /O2 /I "../../include" /I "sdk" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "MYDETAILS_EXPORTS" /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x809 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386
+# ADD LINK32 comctl32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /out:"../../bin/release/plugins/mydetails.dll"
+
+!ELSEIF "$(CFG)" == "mydetails - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "MYDETAILS_EXPORTS" /YX /FD /GZ /c
+# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "../../include" /I "sdk" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "MYDETAILS_EXPORTS" /FR /YX /FD /GZ /c
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x809 /d "_DEBUG"
+# ADD RSC /l 0x809 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 comctl32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /base:"0x3EC10000" /dll /map /debug /machine:I386 /out:"../../bin/debug unicode/plugins/mydetails.dll" /pdbtype:sept
+
+!ENDIF
+
+# Begin Target
+
+# Name "mydetails - Win32 Release"
+# Name "mydetails - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=.\data.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\frame.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\utils\mir_dblists.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\utils\mir_icons.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\utils\mir_options.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\utils\mir_smileys.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\mydetails.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\options.cpp
+# End Source File
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
+# Begin Source File
+
+SOURCE=.\res\leftarrow.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\res\listening_to.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\res\mail.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\resource.rc
+# End Source File
+# Begin Source File
+
+SOURCE=.\res\rightarrow.ico
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=.\commons.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\data.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\frame.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\m_mydetails.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\m_simpleaway.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\utils\mir_dblists.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\utils\mir_icons.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\utils\mir_memory.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\utils\mir_options.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\utils\mir_smileys.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\mydetails.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\options.h
+# End Source File
+# End Group
+# Begin Group "Docs"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=.\Docs\langpack_MyDetails.txt
+# End Source File
+# Begin Source File
+
+SOURCE=.\Docs\mydetails_changelog.txt
+# End Source File
+# Begin Source File
+
+SOURCE=.\Docs\mydetails_readme.txt
+# End Source File
+# Begin Source File
+
+SOURCE=.\Docs\mydetails_version.txt
+# End Source File
+# End Group
+# End Target
+# End Project
diff --git a/Plugins/mydetails/mydetails.dsw b/Plugins/mydetails/mydetails.dsw
new file mode 100644
index 0000000..0344f9e
--- /dev/null
+++ b/Plugins/mydetails/mydetails.dsw
@@ -0,0 +1,29 @@
+Microsoft Developer Studio Workspace File, Format Version 6.00
+# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
+
+###############################################################################
+
+Project: "mydetails"=".\mydetails.dsp" - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Global:
+
+Package=<5>
+{{{
+}}}
+
+Package=<3>
+{{{
+}}}
+
+###############################################################################
+
diff --git a/Plugins/mydetails/mydetails.h b/Plugins/mydetails/mydetails.h
new file mode 100644
index 0000000..0780f73
--- /dev/null
+++ b/Plugins/mydetails/mydetails.h
@@ -0,0 +1,42 @@
+/*
+Copyright (C) 2005 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#ifndef __MYDETAILS_H__
+# define __MYDETAILS_H__
+
+
+extern "C"
+{
+
+// Dll init
+BOOL WINAPI DllMain(HINSTANCE hinstDLL,DWORD fdwReason,LPVOID lpvReserved);
+
+// Exports:
+__declspec(dllexport) PLUGININFO* MirandaPluginInfo(DWORD mirandaVersion);
+int __declspec(dllexport) Load(PLUGINLINK *link);
+int __declspec(dllexport) Unload(void);
+
+
+}
+
+
+
+
+#endif // __MYDETAILS_H__ \ No newline at end of file
diff --git a/Plugins/mydetails/mydetails.vcproj b/Plugins/mydetails/mydetails.vcproj
new file mode 100644
index 0000000..db4b463
--- /dev/null
+++ b/Plugins/mydetails/mydetails.vcproj
@@ -0,0 +1,642 @@
+<?xml version="1.0" encoding="windows-1251"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="7.00"
+ Name="mydetails"
+ ProjectGUID="{A4855451-6447-437C-B024-B36C3C7901EA}"
+ SccProjectName="SAK"
+ SccAuxPath="SAK"
+ SccLocalPath="SAK"
+ SccProvider="SAK">
+ <Platforms>
+ <Platform
+ Name="Win32"/>
+ </Platforms>
+ <Configurations>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)/Plugins"
+ IntermediateDirectory="$(SolutionDir)$(ConfigurationName)/Obj/$(ProjectName)"
+ ConfigurationType="2"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="FALSE"
+ CharacterSet="2">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="3"
+ GlobalOptimizations="TRUE"
+ InlineFunctionExpansion="1"
+ FavorSizeOrSpeed="2"
+ AdditionalIncludeDirectories="../../include;sdk"
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;MYDETAILS_EXPORTS"
+ StringPooling="TRUE"
+ RuntimeLibrary="2"
+ EnableFunctionLevelLinking="TRUE"
+ UsePrecompiledHeader="3"
+ PrecompiledHeaderThrough="commons.h"
+ WarningLevel="3"
+ SuppressStartupBanner="TRUE"
+ CompileAs="0"/>
+ <Tool
+ Name="VCCustomBuildTool"/>
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="comctl32.lib odbc32.lib odbccp32.lib"
+ LinkIncremental="1"
+ SuppressStartupBanner="TRUE"
+ TargetMachine="1"/>
+ <Tool
+ Name="VCMIDLTool"
+ PreprocessorDefinitions="NDEBUG"
+ MkTypLibCompatible="TRUE"
+ SuppressStartupBanner="TRUE"
+ TargetEnvironment="1"
+ TypeLibraryName=".\Release/mydetails.tlb"
+ HeaderFileName=""/>
+ <Tool
+ Name="VCPostBuildEventTool"/>
+ <Tool
+ Name="VCPreBuildEventTool"/>
+ <Tool
+ Name="VCPreLinkEventTool"/>
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="NDEBUG"
+ Culture="1033"/>
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"/>
+ <Tool
+ Name="VCXMLDataGeneratorTool"/>
+ <Tool
+ Name="VCWebDeploymentTool"/>
+ <Tool
+ Name="VCManagedWrapperGeneratorTool"/>
+ <Tool
+ Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
+ </Configuration>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)/Plugins"
+ IntermediateDirectory="$(SolutionDir)$(ConfigurationName)/Obj/$(ProjectName)"
+ ConfigurationType="2"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="FALSE"
+ CharacterSet="2">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories="../../include;sdk"
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;MYDETAILS_EXPORTS"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="2"
+ BufferSecurityCheck="TRUE"
+ UsePrecompiledHeader="3"
+ PrecompiledHeaderThrough="commons.h"
+ BrowseInformation="1"
+ WarningLevel="3"
+ SuppressStartupBanner="TRUE"
+ DebugInformationFormat="4"
+ CompileAs="0"/>
+ <Tool
+ Name="VCCustomBuildTool"/>
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="comctl32.lib odbc32.lib odbccp32.lib"
+ LinkIncremental="2"
+ SuppressStartupBanner="TRUE"
+ GenerateDebugInformation="TRUE"
+ GenerateMapFile="TRUE"
+ BaseAddress="0x3EC10000"
+ TargetMachine="1"/>
+ <Tool
+ Name="VCMIDLTool"
+ PreprocessorDefinitions="_DEBUG"
+ MkTypLibCompatible="TRUE"
+ SuppressStartupBanner="TRUE"
+ TargetEnvironment="1"
+ TypeLibraryName=".\Debug/mydetails.tlb"
+ HeaderFileName=""/>
+ <Tool
+ Name="VCPostBuildEventTool"/>
+ <Tool
+ Name="VCPreBuildEventTool"/>
+ <Tool
+ Name="VCPreLinkEventTool"/>
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="_DEBUG"
+ Culture="2057"/>
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"/>
+ <Tool
+ Name="VCXMLDataGeneratorTool"/>
+ <Tool
+ Name="VCWebDeploymentTool"/>
+ <Tool
+ Name="VCManagedWrapperGeneratorTool"/>
+ <Tool
+ Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
+ </Configuration>
+ <Configuration
+ Name="Debug Unicode|Win32"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)/Plugins"
+ IntermediateDirectory="$(SolutionDir)$(ConfigurationName)/Obj/$(ProjectName)"
+ ConfigurationType="2"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="FALSE"
+ CharacterSet="2">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories="../../include;sdk"
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;MYDETAILS_EXPORTS"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ BufferSecurityCheck="TRUE"
+ UsePrecompiledHeader="3"
+ PrecompiledHeaderThrough="commons.h"
+ BrowseInformation="1"
+ WarningLevel="3"
+ SuppressStartupBanner="TRUE"
+ DebugInformationFormat="4"
+ CompileAs="0"/>
+ <Tool
+ Name="VCCustomBuildTool"/>
+ <Tool
+ Name="VCLinkerTool"
+ IgnoreImportLibrary="TRUE"
+ AdditionalDependencies="comctl32.lib odbc32.lib odbccp32.lib"
+ LinkIncremental="2"
+ SuppressStartupBanner="TRUE"
+ GenerateDebugInformation="TRUE"
+ GenerateMapFile="TRUE"
+ BaseAddress="0x3EC10000"
+ TargetMachine="1"/>
+ <Tool
+ Name="VCMIDLTool"
+ PreprocessorDefinitions="_DEBUG"
+ MkTypLibCompatible="TRUE"
+ SuppressStartupBanner="TRUE"
+ TargetEnvironment="1"
+ TypeLibraryName=".\Debug/mydetails.tlb"
+ HeaderFileName=""/>
+ <Tool
+ Name="VCPostBuildEventTool"/>
+ <Tool
+ Name="VCPreBuildEventTool"/>
+ <Tool
+ Name="VCPreLinkEventTool"/>
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="_DEBUG"
+ Culture="2057"/>
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"/>
+ <Tool
+ Name="VCXMLDataGeneratorTool"/>
+ <Tool
+ Name="VCWebDeploymentTool"/>
+ <Tool
+ Name="VCManagedWrapperGeneratorTool"/>
+ <Tool
+ Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
+ </Configuration>
+ <Configuration
+ Name="Release Unicode|Win32"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)/Plugins"
+ IntermediateDirectory="$(SolutionDir)$(ConfigurationName)/Obj/$(ProjectName)"
+ ConfigurationType="2"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="FALSE"
+ CharacterSet="2"
+ WholeProgramOptimization="FALSE">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="3"
+ GlobalOptimizations="TRUE"
+ InlineFunctionExpansion="1"
+ FavorSizeOrSpeed="2"
+ AdditionalIncludeDirectories="../../include;sdk"
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;MYDETAILS_EXPORTS"
+ StringPooling="TRUE"
+ RuntimeLibrary="2"
+ BufferSecurityCheck="TRUE"
+ UsePrecompiledHeader="3"
+ PrecompiledHeaderThrough="commons.h"
+ WarningLevel="3"
+ SuppressStartupBanner="TRUE"
+ CompileAs="0"/>
+ <Tool
+ Name="VCCustomBuildTool"/>
+ <Tool
+ Name="VCLinkerTool"
+ IgnoreImportLibrary="TRUE"
+ AdditionalDependencies="comctl32.lib odbc32.lib odbccp32.lib"
+ GenerateDebugInformation="TRUE"
+ GenerateMapFile="TRUE"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ OptimizeForWindows98="1"
+ TargetMachine="1"/>
+ <Tool
+ Name="VCMIDLTool"
+ PreprocessorDefinitions="NDEBUG"
+ MkTypLibCompatible="TRUE"
+ SuppressStartupBanner="TRUE"
+ TargetEnvironment="1"
+ TypeLibraryName=".\Release/mydetails.tlb"
+ HeaderFileName=""/>
+ <Tool
+ Name="VCPostBuildEventTool"/>
+ <Tool
+ Name="VCPreBuildEventTool"/>
+ <Tool
+ Name="VCPreLinkEventTool"/>
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="NDEBUG"
+ Culture="1033"/>
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"/>
+ <Tool
+ Name="VCXMLDataGeneratorTool"/>
+ <Tool
+ Name="VCWebDeploymentTool"/>
+ <Tool
+ Name="VCManagedWrapperGeneratorTool"/>
+ <Tool
+ Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Source Files"
+ Filter="cpp;c;cxx;rc;def;r;odl;idl;hpj;bat">
+ <File
+ RelativePath="data.cpp">
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_MBCS;_USRDLL;MYDETAILS_EXPORTS;$(NoInherit)"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;MYDETAILS_EXPORTS;$(NoInherit)"
+ BasicRuntimeChecks="3"
+ BrowseInformation="1"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;MYDETAILS_EXPORTS;$(NoInherit)"
+ BasicRuntimeChecks="3"
+ BrowseInformation="1"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release Unicode|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_MBCS;_USRDLL;MYDETAILS_EXPORTS;$(NoInherit)"/>
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="frame.cpp">
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_MBCS;_USRDLL;MYDETAILS_EXPORTS;$(NoInherit)"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;MYDETAILS_EXPORTS;$(NoInherit)"
+ BasicRuntimeChecks="3"
+ BrowseInformation="1"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;MYDETAILS_EXPORTS;$(NoInherit)"
+ BasicRuntimeChecks="3"
+ BrowseInformation="1"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release Unicode|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_MBCS;_USRDLL;MYDETAILS_EXPORTS;$(NoInherit)"/>
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="..\utils\mir_dblists.c">
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release Unicode|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"/>
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="..\utils\mir_memory.cpp">
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_MBCS;_USRDLL;MYDETAILS_EXPORTS;$(NoInherit)"
+ UsePrecompiledHeader="0"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;MYDETAILS_EXPORTS;$(NoInherit)"
+ BasicRuntimeChecks="3"
+ UsePrecompiledHeader="0"
+ BrowseInformation="1"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;MYDETAILS_EXPORTS;$(NoInherit)"
+ BasicRuntimeChecks="3"
+ UsePrecompiledHeader="0"
+ BrowseInformation="1"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release Unicode|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_MBCS;_USRDLL;MYDETAILS_EXPORTS;$(NoInherit)"
+ UsePrecompiledHeader="0"/>
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="..\utils\mir_options.cpp">
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_MBCS;_USRDLL;MYDETAILS_EXPORTS;$(NoInherit)"
+ UsePrecompiledHeader="0"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;MYDETAILS_EXPORTS;$(NoInherit)"
+ BasicRuntimeChecks="3"
+ UsePrecompiledHeader="0"
+ BrowseInformation="1"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;MYDETAILS_EXPORTS;$(NoInherit)"
+ BasicRuntimeChecks="3"
+ UsePrecompiledHeader="0"
+ BrowseInformation="1"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release Unicode|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_MBCS;_USRDLL;MYDETAILS_EXPORTS;$(NoInherit)"
+ UsePrecompiledHeader="0"/>
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="..\utils\mir_smileys.cpp">
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_MBCS;_USRDLL;MYDETAILS_EXPORTS;$(NoInherit)"
+ UsePrecompiledHeader="0"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;MYDETAILS_EXPORTS;$(NoInherit)"
+ BasicRuntimeChecks="3"
+ UsePrecompiledHeader="0"
+ BrowseInformation="1"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;MYDETAILS_EXPORTS;$(NoInherit)"
+ BasicRuntimeChecks="3"
+ UsePrecompiledHeader="0"
+ BrowseInformation="1"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release Unicode|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_MBCS;_USRDLL;MYDETAILS_EXPORTS;$(NoInherit)"
+ UsePrecompiledHeader="0"/>
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="mydetails.cpp">
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_MBCS;_USRDLL;MYDETAILS_EXPORTS;$(NoInherit)"
+ UsePrecompiledHeader="1"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;MYDETAILS_EXPORTS;$(NoInherit)"
+ BasicRuntimeChecks="3"
+ UsePrecompiledHeader="1"
+ BrowseInformation="1"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;MYDETAILS_EXPORTS;$(NoInherit)"
+ BasicRuntimeChecks="3"
+ UsePrecompiledHeader="1"
+ BrowseInformation="1"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release Unicode|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_MBCS;_USRDLL;MYDETAILS_EXPORTS;$(NoInherit)"
+ UsePrecompiledHeader="1"/>
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="options.cpp">
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_MBCS;_USRDLL;MYDETAILS_EXPORTS;$(NoInherit)"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;MYDETAILS_EXPORTS;$(NoInherit)"
+ BasicRuntimeChecks="3"
+ BrowseInformation="1"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;MYDETAILS_EXPORTS;$(NoInherit)"
+ BasicRuntimeChecks="3"
+ BrowseInformation="1"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release Unicode|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_MBCS;_USRDLL;MYDETAILS_EXPORTS;$(NoInherit)"/>
+ </FileConfiguration>
+ </File>
+ </Filter>
+ <Filter
+ Name="Resource Files"
+ Filter="ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe">
+ <File
+ RelativePath="resource.rc">
+ </File>
+ </Filter>
+ <Filter
+ Name="Header Files"
+ Filter="h;hpp;hxx;hm;inl">
+ <File
+ RelativePath="commons.h">
+ </File>
+ <File
+ RelativePath="data.h">
+ </File>
+ <File
+ RelativePath="frame.h">
+ </File>
+ <File
+ RelativePath="m_mydetails.h">
+ </File>
+ <File
+ RelativePath="m_simpleaway.h">
+ </File>
+ <File
+ RelativePath="..\utils\mir_dblists.h">
+ </File>
+ <File
+ RelativePath="..\utils\mir_memory.h">
+ </File>
+ <File
+ RelativePath="..\utils\mir_options.h">
+ </File>
+ <File
+ RelativePath="..\utils\mir_smileys.h">
+ </File>
+ <File
+ RelativePath="mydetails.h">
+ </File>
+ <File
+ RelativePath="options.h">
+ </File>
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
+
diff --git a/Plugins/mydetails/options.cpp b/Plugins/mydetails/options.cpp
new file mode 100644
index 0000000..c5c298c
--- /dev/null
+++ b/Plugins/mydetails/options.cpp
@@ -0,0 +1,189 @@
+/*
+Copyright (C) 2005 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#include "commons.h"
+#include "options.h"
+
+
+
+// Prototypes /////////////////////////////////////////////////////////////////////////////////////
+
+Options opts;
+
+
+static BOOL CALLBACK DlgProcOpts(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam);
+
+
+// Functions //////////////////////////////////////////////////////////////////////////////////////
+
+
+
+static OptPageControl pageControls[] = {
+ { &opts.draw_text_rtl, CONTROL_CHECKBOX, IDC_TEXT_RTL, "TextRTL", (BYTE) 0 },
+ { &opts.cycle_through_protocols, CONTROL_CHECKBOX, IDC_CYCLE_THROUGH_PROTOS, "CicleThroughtProtocols", (BYTE) 1 },
+ { &opts.seconds_to_show_protocol, CONTROL_SPIN, IDC_CYCLE_TIME, "CicleTime", (WORD) 5, IDC_CYCLE_TIME_SPIN, (WORD) 1, (WORD) 255 },
+ { &opts.replace_smileys, CONTROL_CHECKBOX, IDC_REPLACE_SMILEYS, "ReplaceSmileys", (BYTE) 1 },
+ { &opts.resize_smileys, CONTROL_CHECKBOX, IDC_RESIZE_SMILEYS, "ResizeSmileys", (BYTE) 0 },
+ { &opts.use_contact_list_smileys, CONTROL_CHECKBOX, IDC_USE_CONTACT_LIST_SMILEYS, "UseContactListSmileys", (BYTE) 0 },
+ { &opts.global_on_avatar, CONTROL_CHECKBOX, IDC_GLOBAL_ON_AVATAR, "GlobalOnAvatar", (BYTE) 0 },
+ { &opts.global_on_nickname, CONTROL_CHECKBOX, IDC_GLOBAL_ON_NICKNAME, "GlobalOnNickname", (BYTE) 0 },
+ { &opts.global_on_status, CONTROL_CHECKBOX, IDC_GLOBAL_ON_STATUS, "GlobalOnStatus", (BYTE) 0 },
+ { &opts.global_on_status_message, CONTROL_CHECKBOX, IDC_GLOBAL_ON_STATUS_MESSAGE, "GlobalOnStatusMessage", (BYTE) 0 },
+ { &opts.draw_avatar_border, CONTROL_CHECKBOX, IDC_AVATAR_DRAW_BORDER, "AvatarDrawBorders", (BYTE) 0 },
+ { &opts.draw_avatar_border_color, CONTROL_COLOR, IDC_AVATAR_BORDER_COLOR, "AvatarBorderColor", (DWORD) RGB(0,0,0) },
+ { &opts.draw_avatar_round_corner, CONTROL_CHECKBOX, IDC_AVATAR_ROUND_CORNERS, "AvatarRoundCorners", (BYTE) 1 },
+ { &opts.draw_avatar_use_custom_corner_size, CONTROL_CHECKBOX, IDC_AVATAR_CUSTOM_CORNER_SIZE_CHECK, "AvatarUseCustomCornerSize", (BYTE) 0 },
+ { &opts.draw_avatar_custom_corner_size, CONTROL_SPIN, IDC_AVATAR_CUSTOM_CORNER_SIZE, "AvatarCustomCornerSize", (WORD) 4, IDC_AVATAR_CUSTOM_CORNER_SIZE_SPIN, (WORD) 1, (WORD) 255 },
+ { &opts.resize_frame, CONTROL_CHECKBOX, IDC_RESIZE_FRAME, "ResizeFrame", (BYTE) 0 },
+};
+
+
+// Initializations needed by options
+void LoadOptions()
+{
+ if (GetSystemMetrics(SM_MIDEASTENABLED))
+ pageControls[0].dwDefValue = TRUE;
+
+ LoadOpts(pageControls, MAX_REGS(pageControls), MODULE_NAME);
+
+ // This is created here to assert that this key always exists
+ opts.refresh_status_message_timer = DBGetContactSettingWord(NULL,"MyDetails","RefreshStatusMessageTimer",12);
+ DBWriteContactSettingWord(NULL,"MyDetails","RefreshStatusMessageTimer", opts.refresh_status_message_timer);
+
+ SetCycleTime();
+ RedrawFrame();
+}
+
+
+int InitOptionsCallback(WPARAM wParam,LPARAM lParam)
+{
+ OPTIONSDIALOGPAGE odp;
+
+ ZeroMemory(&odp,sizeof(odp));
+ odp.cbSize=sizeof(odp);
+ odp.position=-200000000;
+ odp.hInstance=hInst;
+ odp.pfnDlgProc=DlgProcOpts;
+ odp.pszTemplate=MAKEINTRESOURCE(IDD_OPTS);
+ odp.pszGroup=Translate("Customize");
+ odp.pszTitle=Translate("My Details");
+ odp.flags=ODPF_BOLDGROUPS;
+ CallService(MS_OPT_ADDPAGE,wParam,(LPARAM)&odp);
+
+ return 0;
+}
+
+
+void InitOptions()
+{
+ LoadOptions();
+
+ HookEvent(ME_OPT_INITIALISE, InitOptionsCallback);
+}
+
+// Deinitializations needed by options
+void DeInitOptions()
+{
+}
+
+
+static BOOL CALLBACK DlgProcOpts(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ BOOL ret = SaveOptsDlgProc(pageControls, MAX_REGS(pageControls), MODULE_NAME, hwndDlg, msg, wParam, lParam);
+
+ switch (msg)
+ {
+ case WM_INITDIALOG:
+ {
+ if(!IsDlgButtonChecked(hwndDlg,IDC_AVATAR_DRAW_BORDER))
+ {
+ EnableWindow(GetDlgItem(hwndDlg,IDC_AVATAR_BORDER_COLOR_L),FALSE);
+ EnableWindow(GetDlgItem(hwndDlg,IDC_AVATAR_BORDER_COLOR),FALSE);
+ }
+ if(!IsDlgButtonChecked(hwndDlg,IDC_AVATAR_ROUND_CORNERS))
+ {
+ EnableWindow(GetDlgItem(hwndDlg,IDC_AVATAR_CUSTOM_CORNER_SIZE_CHECK),FALSE);
+ EnableWindow(GetDlgItem(hwndDlg,IDC_AVATAR_CUSTOM_CORNER_SIZE),FALSE);
+ EnableWindow(GetDlgItem(hwndDlg,IDC_AVATAR_CUSTOM_CORNER_SIZE_SPIN),FALSE);
+ }
+ if(!IsDlgButtonChecked(hwndDlg,IDC_SHOW_PROTO_NAME))
+ {
+ EnableWindow(GetDlgItem(hwndDlg,IDC_SHOW_CYCLE_PROTO_BUTTON),FALSE);
+ }
+ if (!ServiceExists(MS_SMILEYADD_BATCHPARSE))
+ {
+ EnableWindow(GetDlgItem(hwndDlg,IDC_REPLACE_SMILEYS),FALSE);
+ EnableWindow(GetDlgItem(hwndDlg,IDC_USE_CONTACT_LIST_SMILEYS),FALSE);
+ EnableWindow(GetDlgItem(hwndDlg,IDC_RESIZE_SMILEYS),FALSE);
+ }
+ if (!ServiceExists(MS_CLIST_FRAMES_SETFRAMEOPTIONS))
+ {
+ EnableWindow(GetDlgItem(hwndDlg,IDC_RESIZE_FRAME),FALSE);
+ }
+
+ break;
+ }
+ case WM_COMMAND:
+ {
+ if (LOWORD(wParam)==IDC_AVATAR_DRAW_BORDER)
+ {
+ BOOL enabled = IsDlgButtonChecked(hwndDlg,IDC_AVATAR_DRAW_BORDER);
+ EnableWindow(GetDlgItem(hwndDlg,IDC_AVATAR_BORDER_COLOR_L),enabled);
+ EnableWindow(GetDlgItem(hwndDlg,IDC_AVATAR_BORDER_COLOR),enabled);
+ }
+ else if (LOWORD(wParam)==IDC_AVATAR_ROUND_CORNERS)
+ {
+ BOOL enabled = IsDlgButtonChecked(hwndDlg,IDC_AVATAR_ROUND_CORNERS);
+ EnableWindow(GetDlgItem(hwndDlg,IDC_AVATAR_CUSTOM_CORNER_SIZE_CHECK),enabled);
+ EnableWindow(GetDlgItem(hwndDlg,IDC_AVATAR_CUSTOM_CORNER_SIZE),enabled);
+ EnableWindow(GetDlgItem(hwndDlg,IDC_AVATAR_CUSTOM_CORNER_SIZE_SPIN),enabled);
+ }
+ else if (LOWORD(wParam)==IDC_SHOW_PROTO_NAME)
+ {
+ BOOL enabled = IsDlgButtonChecked(hwndDlg,IDC_SHOW_PROTO_NAME);
+ EnableWindow(GetDlgItem(hwndDlg,IDC_SHOW_CYCLE_PROTO_BUTTON),enabled);
+ }
+
+ break;
+ }
+ case WM_NOTIFY:
+ {
+ switch (((LPNMHDR)lParam)->idFrom)
+ {
+ case 0:
+ {
+ switch (((LPNMHDR)lParam)->code)
+ {
+ case PSN_APPLY:
+ {
+ LoadOptions();
+
+ return TRUE;
+ }
+ }
+ break;
+ }
+ }
+ break;
+ }
+ }
+
+ return ret;
+}
diff --git a/Plugins/mydetails/options.h b/Plugins/mydetails/options.h
new file mode 100644
index 0000000..71d5f47
--- /dev/null
+++ b/Plugins/mydetails/options.h
@@ -0,0 +1,83 @@
+/*
+Copyright (C) 2005 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#ifndef __OPTIONS_H__
+# define __OPTIONS_H__
+
+
+#define TOP 0
+#define LEFT 1
+#define BOTTOM 2
+#define RIGHT 3
+
+struct Options
+{
+ bool cycle_through_protocols;
+ int seconds_to_show_protocol;
+ bool replace_smileys;
+ bool resize_smileys;
+ bool use_contact_list_smileys;
+
+ bool draw_text_rtl;
+// bool draw_text_align_right;
+
+// bool draw_show_protocol_name;
+// bool show_protocol_cycle_button;
+
+ bool global_on_avatar;
+ bool global_on_nickname;
+ bool global_on_status;
+ bool global_on_status_message;
+
+// bool draw_avatar_custom_size;
+// bool draw_avatar_allow_to_grow;
+// int draw_avatar_custom_size_pixels;
+ bool draw_avatar_border;
+ COLORREF draw_avatar_border_color;
+ bool draw_avatar_round_corner;
+ bool draw_avatar_use_custom_corner_size;
+ int draw_avatar_custom_corner_size;
+
+ COLORREF bkg_color;
+// int borders[4];
+
+// bool use_avatar_space_to_draw_text;
+
+ bool resize_frame;
+
+ int refresh_status_message_timer;
+};
+
+extern Options opts;
+
+
+// Initializations needed by options
+void InitOptions();
+
+// Deinitializations needed by options
+void DeInitOptions();
+
+
+// Loads the options from DB
+// It don't need to be called, except in some rare cases
+void LoadOptions();
+
+
+#endif // __OPTIONS_H__
diff --git a/Plugins/mydetails/res/leftarrow.ico b/Plugins/mydetails/res/leftarrow.ico
new file mode 100644
index 0000000..4953e83
--- /dev/null
+++ b/Plugins/mydetails/res/leftarrow.ico
Binary files differ
diff --git a/Plugins/mydetails/res/listening_to.ico b/Plugins/mydetails/res/listening_to.ico
new file mode 100644
index 0000000..d359ec1
--- /dev/null
+++ b/Plugins/mydetails/res/listening_to.ico
Binary files differ
diff --git a/Plugins/mydetails/res/mail.ico b/Plugins/mydetails/res/mail.ico
new file mode 100644
index 0000000..ba0e7d3
--- /dev/null
+++ b/Plugins/mydetails/res/mail.ico
Binary files differ
diff --git a/Plugins/mydetails/res/rightarrow.ico b/Plugins/mydetails/res/rightarrow.ico
new file mode 100644
index 0000000..2705830
--- /dev/null
+++ b/Plugins/mydetails/res/rightarrow.ico
Binary files differ
diff --git a/Plugins/mydetails/resource.h b/Plugins/mydetails/resource.h
new file mode 100644
index 0000000..9bc277e
--- /dev/null
+++ b/Plugins/mydetails/resource.h
@@ -0,0 +1,72 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Developer Studio generated include file.
+// Used by resource.rc
+//
+#define IDD_SETNICKNAME 101
+#define IDD_OPTS 102
+#define IDD_SETSTATUSMESSAGE 103
+#define IDR_MENU1 104
+#define IDI_LISTENINGTO 105
+#define IDI_RIGHT_ARROW 106
+#define IDI_LEFT_ARROW 107
+#define IDI_EMAIL 111
+#define IDC_NICKNAME 1000
+#define IDC_SHOW_PROTO_NAME 1001
+#define IDC_CYCLE_THROUGH_PROTOS 1002
+#define IDC_TEXT_RTL 1003
+#define IDC_TEXT_ALIGN_RIGHT 1004
+#define IDC_REPLACE_SMILEYS 1005
+#define IDC_RESIZE_SMILEYS 1006
+#define IDC_RESIZE_FRAME 1007
+#define IDC_RESIZE_SMILEYS2 1008
+#define IDC_USE_CONTACT_LIST_SMILEYS 1008
+#define IDC_STATUSMESSAGE 1009
+#define IDC_GLOBAL_ON_NICKNAME 1009
+#define IDC_AVATAR_ALLOW_TO_GROW 1010
+#define IDC_GLOBAL_ON_STATUS_MESSAGE 1011
+#define IDC_GLOBAL_ON_STATUS 1012
+#define IDC_GLOBAL_ON_AVATAR 1013
+#define IDC_SHOW_CYCLE_PROTO_BUTTON 1014
+#define IDC_AVATAR_CUSTOM_CORNER_SIZE 1622
+#define IDC_AVATAR_CUSTOM_CORNER_SIZE_SPIN 1623
+#define IDC_BORDER_TOP 1624
+#define IDC_BORDER_TOP_SPIN 1625
+#define IDC_BORDER_LEFT 1626
+#define IDC_BORDER_LEFT_SPIN 1627
+#define IDC_BORDER_BOTTOM 1628
+#define IDC_BORDER_BOTTOM_SPIN 1629
+#define IDC_BORDER_RIGHT 1630
+#define IDC_BORDER_RIGHT_SPIN 1631
+#define IDC_CYCLE_TIME 1632
+#define IDC_CYCLE_TIME_SPIN 1633
+#define IDC_AVATAR_CUSTOM_SIZE 1634
+#define IDC_AVATAR_CUSTOM_SIZE_SPIN 1635
+#define IDC_AVATAR_CUSTOM_CORNER_SIZE_CHECK 1761
+#define IDC_AVATAR_DRAW_BORDER 1764
+#define IDC_AVATAR_CUSTOM_SIZE_CHK 1765
+#define IDC_AVATAR_ROUND_CORNERS 1800
+#define IDC_AVATAR_USE_FREE_SPACE 1801
+#define IDC_AVATAR_BORDER_COLOR_L 1839
+#define IDC_AVATAR_BORDER_COLOR 1840
+#define IDC_AVATAR_BKG_COLOR_L 1841
+#define IDC_AVATAR_BKG_COLOR 1842
+#define ID_CYCLE_THROUGH_PROTOS 40004
+#define ID_DONT_CYCLE_THROUGH_PROTOS 40005
+#define ID_SHOW_NEXT_PROTO 40006
+#define ID_SHOW_PREV_PROTO 40007
+#define ID_NICKPOPUP_SETMYNICKNAME 40008
+#define ID_STATUSMESSAGEPOPUP_SETMYSTATUSMESSAGE 40009
+#define ID_AVATARPOPUP_SETMYAVATAR 40010
+#define ID_LISTENINGTOPOPUP_SENDLISTENINGTO 40011
+#define ID_CONTEXTPOPUP_ENABLELISTENINGTO 40012
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 112
+#define _APS_NEXT_COMMAND_VALUE 40013
+#define _APS_NEXT_CONTROL_VALUE 1011
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/Plugins/mydetails/resource.rc b/Plugins/mydetails/resource.rc
new file mode 100644
index 0000000..5706422
--- /dev/null
+++ b/Plugins/mydetails/resource.rc
@@ -0,0 +1,326 @@
+//Microsoft Developer Studio generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "afxres.h"
+#include "../../include/statusmodes.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// Neutral resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_NEU)
+#ifdef _WIN32
+LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
+#pragma code_page(1252)
+#endif //_WIN32
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Icon
+//
+
+// Icon with lowest ID value placed first to ensure application icon
+// remains consistent on all systems.
+IDI_LISTENINGTO ICON DISCARDABLE "res\\listening_to.ico"
+IDI_RIGHT_ARROW ICON DISCARDABLE "res\\rightarrow.ico"
+IDI_LEFT_ARROW ICON DISCARDABLE "res\\leftarrow.ico"
+IDI_EMAIL ICON DISCARDABLE "res\\mail.ico"
+#endif // Neutral resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+/////////////////////////////////////////////////////////////////////////////
+// English (U.S.) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page(1252)
+#endif //_WIN32
+
+#ifndef _MAC
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 0,0,1,11
+ PRODUCTVERSION 0,0,1,11
+ FILEFLAGSMASK 0x3fL
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x40004L
+ FILETYPE 0x2L
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "080004b0"
+ BEGIN
+ VALUE "Comments", "\0"
+ VALUE "CompanyName", " \0"
+ VALUE "FileDescription", "MyDetails Miranda Plugin\0"
+ VALUE "FileVersion", "0, 0, 1, 11\0"
+ VALUE "InternalName", "mydetails\0"
+ VALUE "LegalCopyright", "Copyright © 2005-2008 Ricardo Pescuma Domenecci, Drugwash\0"
+ VALUE "LegalTrademarks", "\0"
+ VALUE "OriginalFilename", "mydetails.dll\0"
+ VALUE "PrivateBuild", "\0"
+ VALUE "ProductName", "\0"
+ VALUE "ProductVersion", "0, 0, 1, 11\0"
+ VALUE "SpecialBuild", "\0"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x800, 1200
+ END
+END
+
+#endif // !_MAC
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Dialog
+//
+
+IDD_SETNICKNAME DIALOG DISCARDABLE 0, 0, 283, 65
+STYLE DS_MODALFRAME | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_CAPTION |
+ WS_SYSMENU
+CAPTION "Set My Nickname"
+FONT 8, "MS Shell Dlg"
+BEGIN
+ RTEXT "Nickname:",IDC_STATIC,7,8,51,12
+ EDITTEXT IDC_NICKNAME,67,7,209,31,ES_MULTILINE | ES_AUTOVSCROLL |
+ WS_VSCROLL
+ DEFPUSHBUTTON "OK",IDOK,87,44,50,14
+ PUSHBUTTON "Cancel",IDCANCEL,146,44,50,14
+END
+
+IDD_OPTS DIALOGEX 0, 0, 316, 246
+STYLE DS_FIXEDSYS | WS_CHILD | WS_VISIBLE
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg"
+BEGIN
+ GROUPBOX " General ",IDC_STATIC,7,7,302,70
+ CONTROL "Cycle through protocols every:",
+ IDC_CYCLE_THROUGH_PROTOS,"Button",BS_AUTOCHECKBOX |
+ WS_TABSTOP,13,18,114,14
+ EDITTEXT IDC_CYCLE_TIME,129,18,35,12,ES_NUMBER
+ CONTROL "",IDC_CYCLE_TIME_SPIN,"msctls_updown32",UDS_SETBUDDYINT |
+ UDS_ALIGNRIGHT | UDS_ARROWKEYS | UDS_NOTHOUSANDS |
+ UDS_HOTTRACK,156,18,11,12
+ LTEXT "seconds",IDC_STATIC,171,21,32,11
+ CONTROL "Global on avatar",IDC_GLOBAL_ON_AVATAR,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,207,18,96,14
+ CONTROL "RTL",IDC_TEXT_RTL,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,
+ 13,32,85,14
+ CONTROL "Global on nickname",IDC_GLOBAL_ON_NICKNAME,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,207,32,96,14
+ CONTROL "Auto-resize frame",IDC_RESIZE_FRAME,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,13,46,85,14
+ CONTROL "Use contact list smileys",IDC_USE_CONTACT_LIST_SMILEYS,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,105,46,96,14
+ CONTROL "Global on status",IDC_GLOBAL_ON_STATUS,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,207,46,96,14
+ CONTROL "Replace Smileys",IDC_REPLACE_SMILEYS,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,13,60,85,14
+ CONTROL "Resize Smileys",IDC_RESIZE_SMILEYS,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,105,60,96,14
+ CONTROL "Global on status message",IDC_GLOBAL_ON_STATUS_MESSAGE,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,207,61,96,14
+ GROUPBOX " Avatar ",IDC_STATIC,7,80,302,59
+ CONTROL "Draw border on avatar",IDC_AVATAR_DRAW_BORDER,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,16,95,103,9
+ LTEXT "Border Color:",IDC_AVATAR_BORDER_COLOR_L,123,95,53,10
+ CONTROL "",IDC_AVATAR_BORDER_COLOR,"ColourPicker",WS_TABSTOP,180,
+ 92,17,13
+ CONTROL "Round corners of avatars",IDC_AVATAR_ROUND_CORNERS,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,16,109,280,8
+ CONTROL "Custom corner size:",
+ IDC_AVATAR_CUSTOM_CORNER_SIZE_CHECK,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,27,122,93,11
+ EDITTEXT IDC_AVATAR_CUSTOM_CORNER_SIZE,127,121,35,12,ES_NUMBER
+ CONTROL "",IDC_AVATAR_CUSTOM_CORNER_SIZE_SPIN,"msctls_updown32",
+ UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_ARROWKEYS |
+ UDS_NOTHOUSANDS | UDS_HOTTRACK,165,121,11,12
+ LTEXT "pixels",IDC_STATIC,179,124,41,11
+END
+
+IDD_SETSTATUSMESSAGE DIALOG DISCARDABLE 0, 0, 283, 68
+STYLE DS_MODALFRAME | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_CAPTION |
+ WS_SYSMENU
+CAPTION "Set My Status Message for All Status"
+FONT 8, "MS Shell Dlg"
+BEGIN
+ RTEXT "Status Message:",IDC_STATIC,7,8,69,12
+ EDITTEXT IDC_STATUSMESSAGE,81,7,195,35,ES_MULTILINE |
+ ES_AUTOVSCROLL | ES_WANTRETURN | WS_VSCROLL
+ DEFPUSHBUTTON "OK",IDOK,87,47,50,14
+ PUSHBUTTON "Cancel",IDCANCEL,146,47,50,14
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// DESIGNINFO
+//
+
+#ifdef APSTUDIO_INVOKED
+GUIDELINES DESIGNINFO DISCARDABLE
+BEGIN
+ IDD_SETNICKNAME, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 276
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 58
+ END
+
+ IDD_OPTS, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 309
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 238
+ END
+
+ IDD_SETSTATUSMESSAGE, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 276
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 61
+ END
+END
+#endif // APSTUDIO_INVOKED
+
+#endif // English (U.S.) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+/////////////////////////////////////////////////////////////////////////////
+// Portuguese (Brazil) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_PTB)
+#ifdef _WIN32
+LANGUAGE LANG_PORTUGUESE, SUBLANG_PORTUGUESE_BRAZILIAN
+#pragma code_page(1252)
+#endif //_WIN32
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "resource.h\0"
+END
+
+2 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "#include ""afxres.h""\r\n"
+ "#include ""../../include/statusmodes.h""\r\n"
+ "\0"
+END
+
+3 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "\r\n"
+ "\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Menu
+//
+
+IDR_MENU1 MENU DISCARDABLE
+BEGIN
+ POPUP "Status popup"
+ BEGIN
+ MENUITEM "&Offline", ID_STATUS_OFFLINE
+ MENUITEM "On&line", ID_STATUS_ONLINE
+ MENUITEM "&Away", ID_STATUS_AWAY
+ MENUITEM "&NA", ID_STATUS_NA
+ MENUITEM "Occ&upied", ID_STATUS_OCCUPIED
+ MENUITEM "&DND", ID_STATUS_DND
+ MENUITEM "&Free for chat", ID_STATUS_FREECHAT
+ MENUITEM "&Invisible", ID_STATUS_INVISIBLE
+ MENUITEM "On the &Phone", ID_STATUS_ONTHEPHONE
+ MENUITEM "Out to &Lunch", ID_STATUS_OUTTOLUNCH
+ END
+ POPUP "Context popup"
+ BEGIN
+ MENUITEM SEPARATOR
+ MENUITEM "Set My Avatar...", ID_AVATARPOPUP_SETMYAVATAR
+ MENUITEM "Set My Nickname...", ID_NICKPOPUP_SETMYNICKNAME
+ MENUITEM "Set My Status Message...", ID_STATUSMESSAGEPOPUP_SETMYSTATUSMESSAGE
+
+ MENUITEM "Enable Listening To", ID_CONTEXTPOPUP_ENABLELISTENINGTO
+
+ MENUITEM SEPARATOR
+ MENUITEM "Show next protocol", ID_SHOW_NEXT_PROTO
+ MENUITEM "Show previous protocol", ID_SHOW_PREV_PROTO
+ MENUITEM SEPARATOR
+ MENUITEM "Cycle through protocols", ID_CYCLE_THROUGH_PROTOS
+ MENUITEM "Don't cycle through protocols",
+ ID_DONT_CYCLE_THROUGH_PROTOS
+ END
+ POPUP "Nick popup"
+ BEGIN
+ MENUITEM SEPARATOR
+ MENUITEM "Set My Nickname...", ID_NICKPOPUP_SETMYNICKNAME
+ END
+ POPUP "Status message popup"
+ BEGIN
+ MENUITEM SEPARATOR
+ MENUITEM "Set My Status Message...", ID_STATUSMESSAGEPOPUP_SETMYSTATUSMESSAGE
+
+ END
+ POPUP "Avatar popup"
+ BEGIN
+ MENUITEM SEPARATOR
+ MENUITEM "Set My Avatar...", ID_AVATARPOPUP_SETMYAVATAR
+ END
+ POPUP "ListeningTo popup"
+ BEGIN
+ MENUITEM SEPARATOR
+ MENUITEM "Enable Listening To", ID_LISTENINGTOPOPUP_SENDLISTENINGTO
+
+ END
+END
+
+#endif // Portuguese (Brazil) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
+
diff --git a/Plugins/mydetails/sdk/m_NewAwaySys.h b/Plugins/mydetails/sdk/m_NewAwaySys.h
new file mode 100644
index 0000000..4038fad
--- /dev/null
+++ b/Plugins/mydetails/sdk/m_NewAwaySys.h
@@ -0,0 +1,119 @@
+/*
+ New Away System plugin for Miranda IM
+ Copyright (c) 2005-2006 Chervov Dmitry
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#ifndef __M_NEWAWAYSYS_H
+#define __M_NEWAWAYSYS_H
+
+// NAS_PROTOINFO::Flags constants
+#define PIF_NO_CLIST_SETSTATUSMODE 1 // NAS won't call MS_CLIST_SETSTATUSMODE service on a global status change, if this flag is set. it's useful if you want to change the global message and status in NAS without changing current "real" protocol statuses. NAS ignores this flag if szProto != NULL
+#define PIF_NOTTEMPORARY 2
+// usually you should NOT set this flag
+// for MS_NAS_SETSTATE: NAS will overwrite current user-defined message for szProto if this flag is specified; otherwise (if the flag isn't specified), your szMsg will be stored only until the next szProto status change, and won't overwrite any messages specified by user
+// for MS_NAS_GETSTATE: NAS ignores any temporary messages and returns only non-temporary ones. this flag affects something only when status == 0
+
+typedef struct {
+ int cbSize;
+ char *szProto; // pointer to protocol modulename (NULL means global)
+ union
+ {
+ char *szMsg;
+ WCHAR *wszMsg;
+ TCHAR *tszMsg;
+ }; // pointer to the status message _format_ (i.e. it's an unparsed message containing variables, in any case. NAS takes care of parsing) (may be NULL - means that there's no specific message for this protocol - then the global status message will be used)
+/*
+ Be aware that MS_NAS_GETSTATE allocates memory for szMsg through Miranda's
+ memory management interface (MS_SYSTEM_GET_MMI). And MS_NAS_SETSTATE
+ expects szMsg to be allocated through the same service. MS_NAS_SETSTATE deallocates szMsg.
+*/
+ WORD status; // status mode. 0 means current (NAS will overwrite 0 with the current status mode)
+// for MS_NAS_GETSTATE if the specified status is not 0, MS_NAS_GETSTATE will return the default/last status message (depends on settings) - i.e. the same message that will be shown by default when user changes status to the specified one. please note that, for example, if current status mode is ID_STATUS_AWAY, then status messages returned by MS_NAS_GETSTATE for status=0 and status=ID_STATUS_AWAY may be different! for status=ID_STATUS_AWAY it always returns the default/last status message, and for status=0 it returns _current_ status message.
+ int Flags;
+} NAS_PROTOINFO;
+
+// MS_NAS_GETSTATE
+// Fills specified array of NAS_PROTOINFO items with protocol data.
+// You must construct the array and specify cbSize and szProto fields of
+// all items in the array before calling this service.
+// Remember to free szMsg fields through Miranda's MMI if you don't pass them back to NAS through MS_NAS_SETSTATE later.
+// wParam = (WPARAM)(NAS_PROTOINFO*)pi - pointer to an array of NAS_PROTOINFO items to be filled.
+// lParam = (LPARAM)(int)protoCount - number of items in pi.
+// returns 0 on success
+#define MS_NAS_GETSTATEA "NewAwaySystem/GetStateA"
+#define MS_NAS_GETSTATEW "NewAwaySystem/GetStateW"
+#ifdef _UNICODE
+ #define MS_NAS_GETSTATE MS_NAS_GETSTATEW
+#else
+ #define MS_NAS_GETSTATE MS_NAS_GETSTATEA
+#endif
+
+// MS_NAS_SETSTATE
+// Changes status mode and message of specified protocols.
+// (Note that this service deallocates szMsg field of the specified items through
+// Miranda's MMI, so the array is not valid anymore after MS_NAS_SETSTATE returns!)
+// wParam = (WPARAM)(NAS_PROTOINFO*)pi - pointer to an array of NAS_PROTOINFO items.
+// lParam = (LPARAM)(int)protoCount - number of items in pi.
+// returns 0 on success
+#define MS_NAS_SETSTATEA "NewAwaySystem/SetStateA"
+#define MS_NAS_SETSTATEW "NewAwaySystem/SetStateW"
+#ifdef _UNICODE
+ #define MS_NAS_SETSTATE MS_NAS_SETSTATEW
+#else
+ #define MS_NAS_SETSTATE MS_NAS_SETSTATEA
+#endif
+
+// NAS_ISWINFO::Flags constants
+#define ISWF_NOCOUNTDOWN 1 // don't start the countdown to close the window
+#define ISWF_UNICODE 2 // specifies that NAS_ISWINFO::szMsg is a WCHAR*
+#ifdef _UNICODE
+ #define ISWF_TCHAR ISWF_UNICODE // will use WCHAR* instead of char*
+#else
+ #define ISWF_TCHAR 0 // will use char*, as usual
+#endif
+
+typedef struct {
+ int cbSize;
+ char *szProto; // pointer to initial protocol modulename (NULL means global); ignored when hContact is not NULL.
+ HANDLE hContact; // NAS will select this contact in the window initially, if it's not NULL.
+ union
+ {
+ char *szMsg;
+ WCHAR *wszMsg;
+ TCHAR *tszMsg;
+ }; // pointer to an initial status message (may be NULL, NAS will use the default message then)
+ WORD status; // status mode. 0 means current.
+ int Flags; // a combination of ISWF_ constants
+} NAS_ISWINFO;
+
+// MS_NAS_INVOKESTATUSWINDOW
+// Invokes the status message change window.
+// Though if the window is open already, this service just activates an existing window and changes protocol status (i.e. it ignores szMsg and hContact). This behavior may change in future.
+// wParam = (WPARAM)(NAS_ISWINFO*)iswi - pointer to a NAS_ISWINFO structure.
+// lParam = 0
+// returns HWND of the window on success, or NULL on failure.
+#define MS_NAS_INVOKESTATUSWINDOW "NewAwaySystem/InvokeStatusWindow"
+
+/* An example:
+ NAS_ISWINFO iswi = {0}; // for C you may use ZeroMemory() instead
+ iswi.cbSize = sizeof(iswi);
+ iswi.tszMsg = _T("New global status message.");
+ iswi.Flags = ISWF_TCHAR;
+ CallService(MS_NAS_INVOKESTATUSWINDOW, (WPARAM)&iswi, 0);
+*/
+
+#endif // __M_NEWAWAYSYS_H \ No newline at end of file
diff --git a/Plugins/mydetails/sdk/m_avatars.h b/Plugins/mydetails/sdk/m_avatars.h
new file mode 100644
index 0000000..46c6d2b
--- /dev/null
+++ b/Plugins/mydetails/sdk/m_avatars.h
@@ -0,0 +1,297 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2004 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+Avatar service
+
+- load and maintain a cache of contact avatars.
+- draw avatars to a given target device context
+- maintain per protocol fallback images
+
+The avatar service builds on top of Mirandas core bitmap loading service (MS_UTILS_LOADBITMAP).
+However, if imgdecoder.dll is installed in mirandas main or Plugins directory, it can be used
+to support PNG images. The avatar service loads 32bit PNG images and peforms alpha channel
+premultiplication so that these images can be rendered by using the Win32 AlphaBlend() API.
+
+The cache grows on demand only, that is, no avatars are PREloaded. An avatar is only loaded
+if a plugin requests this by using the MS_AV_GETAVATAR service. Since avatars may update
+asynchronously, the avatar iamge may not be ready when a plugin calls the service. In that
+case, an event (ME_AV_AVATARCHANGED) is fired when a contacts avatar changes. This event
+is also fired, when a contact avatar changes automatically.
+
+The service takes care about protocol capabilites (does not actively fetch avatars for
+protocols which do not report avatar capabilities via PF4_AVATARS or for protocols which
+have been disabled in the option dialog). It also does not actively fetch avatars for
+protocols which are in invisible status mode (may cause privacy issues and some protocols
+like MSN don't allow any outbound client communication when in invisible status mode).
+
+- TODO
+- maintain recent avatars (store the last hashes to avoid re-fetching)
+- cache expiration, based on least recently used algorithm.
+
+(c) 2005 by Nightwish, silvercircle@gmail.com
+
+*/
+
+#ifndef _M_AVATARS_H
+#define _M_AVATARS_H
+
+#define AVS_BITMAP_VALID 1
+#define AVS_BITMAP_EXPIRED 2 // the bitmap has been expired from the cache. (unused, currently.
+#define AVS_HIDEONCLIST 4
+#define AVS_PREMULTIPLIED 8 // set in the dwFlags member of the struct avatarCacheEntry for 32 bit transparent
+ // images when loaded with imgdecoder. These images can be rendered transparently
+ // using the AlphaBlend() API with AC_SRC_ALPHA
+#define AVS_PROTOPIC 16 // picture is a protocol picture
+#define AVS_CUSTOMTRANSPBKG 32 // Bitmap was changed to set the background color transparent
+#define AVS_HASTRANSPARENCY 64 // Bitmap has at least one pixel transparent
+#define AVS_OWNAVATAR 128 // is own avatar entry
+#define AVS_NOTREADY 4096
+
+struct avatarCacheEntry {
+ DWORD cbSize; // set to sizeof(struct)
+ HANDLE hContact; // contacts handle, 0, if it is a protocol avatar
+ HBITMAP hbmPic; // bitmap handle of the picutre itself
+ DWORD dwFlags; // see above for flag values
+ LONG bmHeight, bmWidth; // bitmap dimensions
+ DWORD t_lastAccess; // last access time (currently unused, but plugins should still
+ // use it whenever they access the avatar. may be used in the future
+ // to implement cache expiration
+ LPVOID lpDIBSection; // unused field
+ char szFilename[MAX_PATH]; // filename of the avatar (absolute path)
+};
+
+typedef struct avatarCacheEntry AVATARCACHEENTRY;
+
+struct CacheNode {
+ struct CacheNode *pNextNode;
+ struct avatarCacheEntry ace;
+ //CRITICAL_SECTION cs;
+ BOOL loaded;
+ int mustLoad;
+ DWORD dwFlags;
+ int pa_format;
+};
+
+#define AVDRQ_FALLBACKPROTO 1 // use the protocol picture as fallback (currently not used)
+#define AVDRQ_FAILIFNOTCACHED 2 // don't create a cache entry if it doesn't already exist. (currently not working)
+#define AVDRQ_ROUNDEDCORNER 4 // draw with rounded corners
+#define AVDRQ_DRAWBORDER 8 // draw a border around the picture
+#define AVDRQ_PROTOPICT 16 // draw a protocol picture (if available).
+#define AVDRQ_HIDEBORDERONTRANSPARENCY 32 // hide border if bitmap has transparency
+#define AVDRQ_OWNPIC 64 // draw own avatar (szProto is valid)
+#define AVDRQ_RESPECTHIDDEN 128 // don't draw images marked as hidden
+
+// request to draw a contacts picture. See MS_AV_DRAWAVATAR service description
+
+typedef struct _avatarDrawRequest {
+ DWORD cbSize; // set this to sizeof(AVATARDRAWREQUEST) - mandatory, service will return failure code if
+ // cbSize is wrong
+ HANDLE hContact; // the contact for which the avatar should be drawn. set it to 0 to draw a protocol picture
+ HDC hTargetDC; // target device context
+ RECT rcDraw; // target rectangle. The avatar will be centered within the rectangle and scaled to fit.
+ DWORD dwFlags; // flags (see above for valid bitflags)
+ DWORD dwReserved; // for future use
+ DWORD dwInternal; // don't use it
+ COLORREF clrBorder; // color for the border (used with AVDRQ_DRAWBORDER)
+ UCHAR radius; // radius (used with AVDRQ_ROUNDEDCORNER)
+ UCHAR alpha; // alpha value for semi-transparent avatars (valid values form 1 to 255, if it is set to 0
+ // the avatar won't be transparent.
+ char *szProto; // only used when AVDRQ_PROTOPICT or AVDRQ_OWNPIC is set
+} AVATARDRAWREQUEST;
+
+#define CACHE_BLOCKSIZE 20
+
+#define AVS_MODULE "AVS_Settings" // db settings module path
+#define PPICT_MODULE "AVS_ProtoPics" // protocol pictures are saved here
+
+// obtain the bitmap handle of the avatar for the given contact
+// wParam = (HANDLE)hContact
+// lParam = 0;
+// returns: pointer to a struct avatarCacheEntry *, NULL on failure
+// if it returns a failure, the avatar may be ready later and the caller may receive
+// a notification via ME_AV_AVATARCHANGED
+// DONT modify the contents of the returned data structure
+
+#define MS_AV_GETAVATARBITMAP "SV_Avatars/GetAvatar"
+
+// obtain a avatar cache entry for one of my own avatars
+// wParam = 0
+// lParam = (char *)szProto (protocol for which we need to obtain the own avatar information)
+// returns: pointer to a struct avatarCacheEntry *, NULL on failure
+// DONT modify the contents of the returned data structure
+
+#define MS_AV_GETMYAVATAR "SV_Avatars/GetMyAvatar"
+
+// protect the current contact picture from being overwritten by automatic
+// avatar updates. Actually, it only backups the contact picture filename
+// and will used the backuped version until the contact picture gets unlocked
+// again. So this service does not disable avatar updates, but it "fakes"
+// a locked contact picture to the users of the GetAvatar service.
+//
+// wParam = (HANDLE)hContact
+// lParam = 1 -> lock the avatar, lParam = 0 -> unlock
+
+#define MS_AV_PROTECTAVATAR "SV_Avatars/ProtectAvatar"
+
+// set (and optionally protect) a local contact picture for the given hContact
+//
+// wParam = (HANDLE)hContact
+// lParam = either a full picture filename or NULL. If lParam == NULL, the service
+// will open a file selection dialog.
+
+#define MS_AV_SETAVATAR "SV_Avatars/SetAvatar"
+
+// set a local picture for the given protocol
+//
+// wParam = (char *) protocol name
+// lParam = either a full picture filename or NULL. If lParam == NULL, the service
+// will open a file selection dialog.
+
+#define MS_AV_SETMYAVATAR "SV_Avatars/SetMyAvatar"
+
+// see if is possible to set the avatar for the expecified protocol
+//
+// wParam = (char *) protocol name
+// lParam = 0
+// return = 1 if can set, 0 if can't
+
+#define MS_AV_CANSETMYAVATAR "SV_Avatars/CanSetMyAvatar"
+
+// Call avatar option dialog for contact
+//
+// wParam = (HANDLE)hContact
+
+#define MS_AV_CONTACTOPTIONS "SV_Avatars/ContactOptions"
+
+// draw an avatar picture
+//
+// wParam = 0 (not used)
+// lParam = AVATARDRAWREQUEST *avdr
+// draw a contact picture to a destination device context. see description of
+// the AVATARDRAWREQUEST structure for more information on how to use this
+// service.
+// return value: 0 -> failure, avatar probably not available, or not ready. The drawing
+// service DOES schedule an avatar update so your plugin will be notified by the ME_AV_AVATARCHANGED
+// event when the requested avatar is ready for use.
+// 1 -> success. avatar was found and drawing should be ok.
+
+#define MS_AV_DRAWAVATAR "SV_Avatars/Draw"
+
+// fired when a contacts avatar cached by avs changes
+// it includes changes made by the user
+// wParam = hContact
+// lParam = struct avatarCacheEntry *cacheEntry
+// the event CAN pass a NULL pointer in lParam which means that the avatar has changed,
+// but is no longer valid (happens, when a contact removes his avatar, for example).
+// DONT DESTROY the bitmap handle passed in the struct avatarCacheEntry *
+//
+// It is also possible that this event passes 0 as wParam (hContact), in which case,
+// a protocol picture (pseudo - avatar) has been changed.
+
+#define ME_AV_AVATARCHANGED "SV_Avatars/AvatarChanged"
+
+
+typedef struct _contactAvatarChangedNotification {
+ int cbSize; // sizeof()
+ HANDLE hContact; // this might have to be set by the caller too
+ int format; // PA_FORMAT_*
+ char filename[MAX_PATH]; // full path to filename which contains the avatar
+ char hash[128]; // avatar hash (always an empty string by now)
+} CONTACTAVATARCHANGEDNOTIFICATION;
+
+// fired when the contacts avatar is changed by the contact
+// wParam = hContact
+// lParam = struct CONTACTAVATARCHANGENOTIFICATION *cacn
+// the event CAN pass a NULL pointer in lParam which means that the contact deleted its avatar
+
+#define ME_AV_CONTACTAVATARCHANGED "SV_Avatars/ContactAvatarChanged"
+
+// fired when one of our own avatars was changed
+// wParam = (char *)szProto (protocol for which a new avatar was set)
+// lParam = AVATARCACHEENTRY *ace (new cache entry, NULL if the new avatar is not valid)
+
+#define ME_AV_MYAVATARCHANGED "SV_Avatars/MyAvatarChanged"
+
+// Service to be called by protocols to report an avatar has changed. Some avatar changes
+// can be detected automatically, but some not (by now only Skype ones)
+// wParam = (char *)szProto (protocol for which a new avatar was set)
+// lParam = 0
+
+#define MS_AV_REPORTMYAVATARCHANGED "SV_Avatars/ReportMyAvatarChanged"
+
+
+
+// Bitmap services //////////////////////////////////////////////////////////////////////
+
+// Load an image
+// wParam = NULL
+// lParam = filename
+#define MS_AV_LOADBITMAP32 "SV_Avatars/LoadBitmap32"
+
+// Save an HBITMAP to an image
+// wParam = HBITMAP
+// lParam = full path of filename
+#define MS_AV_SAVEBITMAP "SV_Avatars/SaveBitmap"
+
+// Returns != 0 if can save that type of image, = 0 if cant
+// wParam = 0
+// lParam = PA_FORMAT_* // image format
+#define MS_AV_CANSAVEBITMAP "SV_Avatars/CanSaveBitmap"
+
+
+#define RESIZEBITMAP_STRETCH 0 // Distort bitmap to size in (max_width, max_height)
+#define RESIZEBITMAP_KEEP_PROPORTIONS 1 // Keep bitmap proportions (probabily only one of the
+ // max_width/max_height will be respected, and the other will be
+ // smaller)
+#define RESIZEBITMAP_CROP 2 // Keep bitmap proportions but crop it to fix exactly in (max_width, max_height)
+ // Some image info outside will be lost
+#define RESIZEBITMAP_MAKE_SQUARE 3 // Image will be allways square. Image will be croped and the size
+ // returned will be min(max_width, max_height)
+
+#define RESIZEBITMAP_FLAG_DONT_GROW 0x1000 // If set, the image will not grow. Else, it will grow to fit the max width/height
+
+typedef struct {
+ size_t size; // sizeof(ResizeBitmap);
+
+ HBITMAP hBmp;
+
+ int max_width;
+ int max_height;
+
+ int fit; // One of: RESIZEBITMAP_*
+} ResizeBitmap;
+
+// Returns a copy of the bitmap with the size especified or the original bitmap if nothing has to be changed
+// wParam = ResizeBitmap *
+// lParam = NULL
+#define MS_AV_RESIZEBITMAP "SV_Avatars/ResizeBitmap"
+
+/*
+ * flags for internal use ONLY
+ */
+
+#define MC_ISMASTERCONTACT 0x01
+#define MC_ISSUBCONTACT 0x02
+#define AVH_MUSTNOTIFY 0x04 // node->dwFlags (loader thread must notify avatar history about change/delete event)
+#define AVS_DELETENODEFOREVER 0x08
+
+#endif
diff --git a/Plugins/mydetails/sdk/m_cluiframes.h b/Plugins/mydetails/sdk/m_cluiframes.h
new file mode 100644
index 0000000..067d586
--- /dev/null
+++ b/Plugins/mydetails/sdk/m_cluiframes.h
@@ -0,0 +1,338 @@
+/*
+Miranda ICQ: the free icq client for MS Windows
+Copyright (C) 2000-2 Richard Hughes, Roland Rabien & Tristan Van de Vreede
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+/************************************************************************/
+/* Extra Image Column Support +0.5.0.0 */
+/************************************************************************/
+/*
+ HINT: Common usage of Extra icons by modules
+ Usage sequence is next:
+ The Plugin have to be subscribed to ME_CLIST_EXTRA_LIST_REBUILD and
+ ME_CLIST_EXTRA_IMAGE_APPLY notifications.
+
+ During .._REBUILD Notification handle plugin should register required
+ icons in CList internal list via MS_CLIST_EXTRA_ADD_ICON servise
+ and should to keep returned icon indexes.
+
+ Note: _REBUILD notification means that list was rebuilded and
+ all previously registered icon indexes became invalid and can not be used.
+
+ Note: After calling _ADD_ICON services the icon handle you provided is not
+ need for extra images porpouses and have to be released by you in order
+ to reduce GDI resources consumptions.
+
+ Note: Don't forget that icon handle loaded by LoadIcon GDI function is
+ shared and will be kept in memory till plugin unloading. So it is not
+ better way to load icon. Please use appropriate Iconlib services.
+
+ Note: The icon can be registered in Clist at any time.
+
+ During .._ME_CLIST_EXTRA_IMAGE_APPLY the plugin has to call
+ MS_CLIST_EXTRA_SET_ICON passing appropriate icon index for contact. This
+ service can be called any time in order to change current extra image.
+
+ ATTENTION: Currently Module support only 254 registered extra icons. The returned
+ value 0xFF internally means 'No extra icon' so thry t register only realy required
+ icons. The best solution - register not registered/invalidated icon just before
+ setting of extra image.
+
+ ATTENTION: Due to different module may use same extra icons slot - they will be conflicted.
+ Please provide ability to end-user to change extra image slot to be used to show
+ your plugin information.
+
+*/
+
+//Extra columns type.
+//column arranged in this way
+//
+// [statusicon] ContactName [WEB][ADV1][ADV2][SMS][EMAIL][PROTO][CLIENT]
+//
+#define EXTRA_ICON_EMAIL 1
+#define EXTRA_ICON_PROTO 2
+#define EXTRA_ICON_SMS 3
+#define EXTRA_ICON_ADV1 4
+#define EXTRA_ICON_ADV2 5
+#define EXTRA_ICON_WEB 6
+#define EXTRA_ICON_CLIENT 7
+#define EXTRA_ICON_VISMODE 8
+#define EXTRA_ICON_ADV3 9
+#define EXTRA_ICON_ADV4 10
+
+#define EXTRA_ICON_COUNT 10
+
+typedef struct
+{
+ int cbSize; //must be sizeof(IconExtraColumn)
+ int ColumnType;
+ HANDLE hImage; //return value from MS_CLIST_EXTRA_ADD_ICON
+}IconExtraColumn,*pIconExtraColumn;
+
+//Set icon for contact at needed column
+//wparam=hContact
+//lparam=pIconExtraColumn
+//return 0 on success,-1 on failure
+//
+//See above for supported columns
+#define MS_CLIST_EXTRA_SET_ICON "CListFrames/SetIconForExraColumn"
+
+//Adding icon to extra image list.
+//Call this in ME_CLIST_EXTRA_LIST_REBUILD event
+//
+//wparam=hIcon
+//lparam=0
+//return hImage on success,-1 on failure
+#define MS_CLIST_EXTRA_ADD_ICON "CListFrames/AddIconToExtraImageList"
+
+#define ME_CLIST_EXTRA_LIST_REBUILD "CListFrames/OnExtraListRebuild"
+
+//called with wparam=hContact
+#define ME_CLIST_EXTRA_IMAGE_APPLY "CListFrames/OnExtraImageApply"
+
+//End of extra images header. TODO move it to separate m_extraimages.h file
+//Cause it has not any relationship to cluiframes engine
+
+
+/************************************************************************/
+/* CLUI Frames Support */
+/************************************************************************/
+
+// NOTE: Clui frames engine is in to be reconsructed..
+
+// Constants used bellow
+typedef struct tagCLISTFrame {
+ DWORD cbSize;
+ HWND hWnd ;
+ HICON hIcon;
+ int align; //al flags below
+ union {
+ int height;
+ int minSize; //the actual meaning depends from type of frame
+ };
+ int Flags; //F_flags below
+ union {
+ char *name; //frame window name indentifier (DO NOT TRANSLATE)
+ wchar_t *wname;
+ LPTSTR tname;
+ };
+ union {
+ char *TBname; //titlebar & menu caption
+ wchar_t *TBwname;
+ LPTSTR TBtname;
+ };
+} CLISTFrame;
+
+#define F_VISIBLE 1 //Frame visible
+#define F_SHOWTB 2 //Show TitleBar
+#define F_UNCOLLAPSED 4 //UnCollapse frame
+#define F_LOCKED 8 //Lock Frame
+#define F_NOBORDER 16 //Dont apply WS_BORDER style for window
+#define F_SHOWTBTIP 32 //Show titlebar tooltip
+#define F_CANBEVERTICAL 64 //frames can be vertical
+#define F_CANNOTBEHORIZONTAL 128 //frames can NOT be horizontal F_CANBEVERTICAL have to be set
+#define F_NO_SUBCONTAINER 1024 //Support skining no subcontainer needed
+#define F_UNICODE 32768 //Use unicode text
+#ifdef _UNICODE
+# define F_TCHAR F_UNICODE
+#else
+# define F_TCHAR 0
+#endif
+
+// frame alignment
+#define alTop 0x00000001
+#define alBottom 0x00000002
+#define alClient 0x00000004 //only one alClient frame
+
+// since 0.7.0.20
+#define alLeft 0x00000011 // frame is vertical
+#define alRight 0x00000012
+
+#define alVertFrameMask 0x00000010
+
+#define FU_TBREDRAW 1 //redraw titlebar
+#define FU_FMREDRAW 2 //redraw Frame
+#define FU_FMPOS 4 //update Frame position
+
+#define FO_FLAGS 0x0001 //return set of F_VISIBLE,F_SHOWTB,F_UNCOLLAPSED,F_LOCKED,F_NOBORDER,F_SHOWTBTIP
+#define FO_NAME 0x0002 //Change name
+#define FO_TBNAME 0x0003 //Change TB caption
+#define FO_TBSTYLE 0x0004 //Change TB style
+#define FO_TBEXSTYLE 0x0005 //Change TB exstyle
+#define FO_ICON 0x0006 //Change icon
+#define FO_HEIGHT 0x0007 //Change height
+#define FO_ALIGN 0x0008 //Change align
+#define FO_TBTIPNAME 0x0009 //Change TB tooltip
+#define FO_FLOATING 0x000a //Change floating mode
+
+#define FO_UNICODETEXT 0x8000 // flag for FO_NAME,FO_TBNAME, FO_TBTIPNAME set/get lPAram as unicode wchar_t
+#ifdef _UNICODE
+ #define FO_TCHAR FO_UNICODETEXT
+#else
+ #define FO_TCHAR 0x0000
+#endif
+
+
+//////////////////////////////////////////////////////////////////////////
+//want show tooltip for statusbar
+//wparam=(char *)protocolname
+//lparam=0
+#define ME_CLIST_FRAMES_SB_SHOW_TOOLTIP "CListFrames/StatusBarShowToolTip"
+
+//////////////////////////////////////////////////////////////////////////
+//want hide tooltip for statusbar
+//wparam=lparam=0
+#define ME_CLIST_FRAMES_SB_HIDE_TOOLTIP "CListFrames/StatusBarHideToolTip"
+
+//////////////////////////////////////////////////////////////////////////
+//adds a frame window
+//wParam=(CLISTFrame*)
+//lParam=0
+//returns an integer, the frame id.
+#define MS_CLIST_FRAMES_ADDFRAME "CListFrames/AddFrame"
+
+//////////////////////////////////////////////////////////////////////////
+// remove frame. It destroy your window
+//
+#define MS_CLIST_FRAMES_REMOVEFRAME "CListFrames/RemoveFrame"
+
+//////////////////////////////////////////////////////////////////////////
+//shows all frames
+//wParam=lParam=0
+//returns 0 on success, -1 on failure
+#define MS_CLIST_FRAMES_SHOWALLFRAMES "CListFrames/ShowALLFrames"
+
+//////////////////////////////////////////////////////////////////////////
+//shows the titlebars of all frames
+//wParam=lParam=0
+//returns 0 on success, -1 on failure
+#define MS_CLIST_FRAMES_SHOWALLFRAMESTB "CListFrames/ShowALLFramesTB"
+
+//////////////////////////////////////////////////////////////////////////
+//hides the titlebars of all frames
+//wParam=lParam=0
+//returns 0 on success, -1 on failure
+#define MS_CLIST_FRAMES_HIDEALLFRAMESTB "CListFrames/HideALLFramesTB"
+
+//////////////////////////////////////////////////////////////////////////
+//shows the frame if it is hidden,
+//hides the frame if it is shown
+//wParam=FrameId
+//lParam=0
+//returns 0 on success, -1 on failure
+#define MS_CLIST_FRAMES_SHFRAME "CListFrames/SHFrame"
+
+//////////////////////////////////////////////////////////////////////////
+//shows the frame titlebar if it is hidden,
+//hides the frame titlebar if it is shown
+//wParam=FrameId
+//lParam=0
+//returns 0 on success, -1 on failure
+#define MS_CLIST_FRAMES_SHFRAMETITLEBAR "CListFrame/SHFrameTitleBar"
+
+//////////////////////////////////////////////////////////////////////////
+//locks the frame if it is unlocked,
+//unlock the frame if it is locked
+//wParam=FrameId
+//lParam=0
+//returns 0 on success, -1 on failure
+#define MS_CLIST_FRAMES_ULFRAME "CListFrame/ULFrame"
+
+//////////////////////////////////////////////////////////////////////////
+//collapses the frame if it is uncollapsed,
+//uncollapses the frame if it is collapsed
+//wParam=FrameId
+//lParam=0
+//returns 0 on success, -1 on failure
+#define MS_CLIST_FRAMES_UCOLLFRAME "CListFrame/UCOLLFrame"
+
+//////////////////////////////////////////////////////////////////////////
+//trigger border flags
+//wparam=frameid
+//lparam=0
+#define MS_CLIST_FRAMES_SETUNBORDER "CListFrame/SetUnBorder"
+
+//////////////////////////////////////////////////////////////////////////
+//redraws the frame
+//wParam=FrameId, -1 for all frames
+//lparam=FU_flags
+//returns a pointer to option, -1 on failure
+#define MS_CLIST_FRAMES_UPDATEFRAME "CListFrame/UpdateFrame"
+
+//////////////////////////////////////////////////////////////////////////
+//gets the frame options
+//(HIWORD)wParam=FrameId
+//(LOWORD)wParam=FO_flag
+//lParam=0
+//returns a pointer to option, -1 on failure
+#define MS_CLIST_FRAMES_GETFRAMEOPTIONS "CListFrame/GetFrameOptions"
+
+//sets the frame options
+//(HIWORLD)wParam=FrameId
+//(LOWORD)wParam=FO_flag
+//lParam=value
+//returns 0 on success, -1 on failure
+#define MS_CLIST_FRAMES_SETFRAMEOPTIONS "CListFrame/SetFrameOptions"
+
+//////////////////////////////////////////////////////////////////////////
+//Frames related menu stuff
+//////////////////////////////////////////////////////////////////////////
+
+//////////////////////////////////////////////////////////////////////////
+//add a new item to the context frame menu
+//wParam=0
+//lParam=(LPARAM)(CLISTMENUITEM*)&mi
+//returns a handle to the new item
+//popupposition=frameid
+//contactowner=advanced parameter
+#define MS_CLIST_ADDCONTEXTFRAMEMENUITEM "CList/AddContextFrameMenuItem"
+
+//////////////////////////////////////////////////////////////////////////
+//remove a item from context frame menu
+//wParam=hMenuItem returned by MS_CLIST_ADDCONTACTMENUITEM
+//lParam=0
+//returns 0 on success, nonzero on failure
+#define MS_CLIST_REMOVECONTEXTFRAMEMENUITEM "CList/RemoveContextFrameMenuItem"
+
+//////////////////////////////////////////////////////////////////////////
+//builds the context menu for a frame
+//wparam=frameid
+//lParam=0
+//returns a HMENU on success, or NULL on failure
+#define MS_CLIST_MENUBUILDFRAMECONTEXT "CList/BuildContextFrameMenu"
+
+//////////////////////////////////////////////////////////////////////////
+// the frame menu is about to be built
+// wparam=frameid
+// lparam=
+// -1 for build from titlebar,
+// use
+// MS_CLIST_ADDCONTEXTFRAMEMENUITEM
+// MS_CLIST_REMOVECONTEXTFRAMEMENUITEM
+//
+// >0 for build in main menu,
+// must be popupname=lparam to place your items in right popup of main menu.
+// use
+// MS_CLIST_ADDMAINMENUITEM
+// MS_CLIST_REMOVEMAINMENUITEM
+//
+#define ME_CLIST_PREBUILDFRAMEMENU "CList/PreBuildFrameMenu"
+
+//////////////////////////////////////////////////////////////////////////
+//needed by cluiframes module to add frames menu to main menu.
+//it just calls NotifyEventHooks(hPreBuildFrameMenuEvent,wParam,lParam);
+#define MS_CLIST_FRAMEMENUNOTIFY "CList/ContextFrameMenuNotify"
diff --git a/Plugins/mydetails/sdk/m_ersatz.h b/Plugins/mydetails/sdk/m_ersatz.h
new file mode 100644
index 0000000..8136a8b
--- /dev/null
+++ b/Plugins/mydetails/sdk/m_ersatz.h
@@ -0,0 +1,40 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2006 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#ifndef __M_ERSATZ_H__
+# define __M_ERSATZ_H__
+
+
+//Returns the current status message for yourself
+//wParam=(WPARAM)0
+//lParam=(LPARAM)0
+//returns status msg or NULL if there is none. Remember to mir_free the return value
+#define PS_GETMYAWAYMSG "/GetMyAwayMsg"
+
+//Created if ersatz is installed
+//wParam=(WPARAM)0
+//lParam=(LPARAM)0
+//returns always 1
+#define MS_ERSATZ_ENABLED "ERSATZ/Enabled"
+
+
+#endif // __M_ERSATZ_H__
diff --git a/Plugins/mydetails/sdk/m_listeningto.h b/Plugins/mydetails/sdk/m_listeningto.h
new file mode 100644
index 0000000..0dddde7
--- /dev/null
+++ b/Plugins/mydetails/sdk/m_listeningto.h
@@ -0,0 +1,56 @@
+/*
+Copyright (C) 2006 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#ifndef __M_LISTENINGTO_H__
+# define __M_LISTENINGTO_H__
+
+// To be used by other plugins to send listening info to miranda
+#define MIRANDA_WINDOWCLASS _T("Miranda.ListeningTo")
+#define MIRANDA_DW_PROTECTION 0x8754
+
+
+/*
+Return TRUE if sending listening to is enabled for this protocol
+
+wParam: char * - protocol name or NULL for all protocols
+lParam: ignored
+*/
+#define MS_LISTENINGTO_ENABLED "ListeningTo/Enabled"
+
+
+/*
+Enable/disable sending listening to this protocol
+
+wParam: char * - protocol name or NULL for all protocols
+lParam: BOOL - TRUE to enable, FALSE to disable
+*/
+#define MS_LISTENINGTO_ENABLE "ListeningTo/Enable"
+
+
+/*
+Notification fired when enable state changed
+
+wParam: char * - protocol name or NULL for all protocols
+lParam: BOOL - enabled
+*/
+#define ME_LISTENINGTO_ENABLE_STATE_CHANGED "ListeningTo/EnableStateChanged"
+
+
+#endif // __M_LISTENINGTO_H__
diff --git a/Plugins/mydetails/sdk/m_metacontacts.h b/Plugins/mydetails/sdk/m_metacontacts.h
new file mode 100644
index 0000000..1da12b9
--- /dev/null
+++ b/Plugins/mydetails/sdk/m_metacontacts.h
@@ -0,0 +1,162 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright © 2004 Universite Louis PASTEUR, STRASBOURG.
+Copyright © 2004 Scott Ellis (www.scottellis.com.au mail@scottellis.com.au)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef M_METACONTACTS_H__
+#define M_METACONTACTS_H__ 1
+
+//get the handle for a contact's parent metacontact
+//wParam=(HANDLE)hSubContact
+//lParam=0
+//returns a handle to the parent metacontact, or null if this contact is not a subcontact
+#define MS_MC_GETMETACONTACT "MetaContacts/GetMeta"
+
+//gets the handle for the default contact
+//wParam=(HANDLE)hMetaContact
+//lParam=0
+//returns a handle to the default contact, or null on failure
+#define MS_MC_GETDEFAULTCONTACT "MetaContacts/GetDefault"
+
+//gets the contact number for the default contact
+//wParam=(HANDLE)hMetaContact
+//lParam=0
+//returns a DWORD contact number, or -1 on failure
+#define MS_MC_GETDEFAULTCONTACTNUM "MetaContacts/GetDefaultNum"
+
+//gets the handle for the 'most online' contact
+//wParam=(HANDLE)hMetaContact
+//lParam=0
+//returns a handle to the 'most online' contact
+#define MS_MC_GETMOSTONLINECONTACT "MetaContacts/GetMostOnline"
+
+//gets the number of subcontacts for a metacontact
+//wParam=(HANDLE)hMetaContact
+//lParam=0
+//returns a DWORD representing the number of subcontacts for the given metacontact
+#define MS_MC_GETNUMCONTACTS "MetaContacts/GetNumContacts"
+
+//gets the handle of a subcontact, using the subcontact's number
+//wParam=(HANDLE)hMetaContact
+//lParam=(DWORD)contact number
+//returns a handle to the specified subcontact
+#define MS_MC_GETSUBCONTACT "MetaContacts/GetSubContact"
+
+//sets the default contact, using the subcontact's contact number
+//wParam=(HANDLE)hMetaContact
+//lParam=(DWORD)contact number
+//returns 0 on success
+#define MS_MC_SETDEFAULTCONTACTNUM "MetaContacts/SetDefault"
+
+//sets the default contact, using the subcontact's handle
+//wParam=(HANDLE)hMetaContact
+//lParam=(HANDLE)hSubcontact
+//returns 0 on success
+#define MS_MC_SETDEFAULTCONTACT "MetaContacts/SetDefaultByHandle"
+
+//forces the metacontact to send using a specific subcontact, using the subcontact's contact number
+//wParam=(HANDLE)hMetaContact
+//lParam=(DWORD)contact number
+//returns 0 on success
+#define MS_MC_FORCESENDCONTACTNUM "MetaContacts/ForceSendContact"
+
+//forces the metacontact to send using a specific subcontact, using the subcontact's handle
+//wParam=(HANDLE)hMetaContact
+//lParam=(HANDLE)hSubcontact
+//returns 0 on success (will fail if 'force default' is in effect)
+#define MS_MC_FORCESENDCONTACT "MetaContacts/ForceSendContactByHandle"
+
+//'unforces' the metacontact to send using a specific subcontact
+//wParam=(HANDLE)hMetaContact
+//lParam=0
+//returns 0 on success (will fail if 'force default' is in effect)
+#define MS_MC_UNFORCESENDCONTACT "MetaContacts/UnforceSendContact"
+
+//'forces' or 'unforces' (i.e. toggles) the metacontact to send using it's default contact
+// overrides (and clears) 'force send' above, and will even force use of offline contacts
+// will send ME_MC_FORCESEND or ME_MC_UNFORCESEND event
+//wParam=(HANDLE)hMetaContact
+//lParam=0
+//returns 1(true) or 0(false) representing new state of 'force default'
+#define MS_MC_FORCEDEFAULT "MetaContacts/ForceSendDefault"
+
+// method to get state of 'force' for a metacontact
+// wParam=(HANDLE)hMetaContact
+// lParam= (DWORD)&contact_number or NULL
+//
+// if lparam supplied, the contact_number of the contatct 'in force' will be copied to the address it points to,
+// or if none is in force, the value (DWORD)-1 will be copied
+// (v0.8.0.8+ returns 1 if 'force default' is true with *lParam == default contact number, else returns 0 with *lParam as above)
+#define MS_MC_GETFORCESTATE "MetaContacts/GetForceState"
+
+// fired when a metacontact's default contact changes (fired upon creation of metacontact also, when default is initially set)
+// wParam=(HANDLE)hMetaContact
+// lParam=(HANDLE)hDefaultContact
+#define ME_MC_DEFAULTTCHANGED "MetaContacts/DefaultChanged"
+
+// fired when a metacontact's subcontacts change (fired upon creation of metacontact, when contacts are added or removed, and when
+// contacts are reordered) - a signal to re-read metacontact data
+// wParam=(HANDLE)hMetaContact
+// lParam=0
+#define ME_MC_SUBCONTACTSCHANGED "MetaContacts/SubcontactsChanged"
+
+// fired when a metacontact is forced to send using a specific subcontact
+// wParam=(HANDLE)hMetaContact
+// lParam=(HANDLE)hForceContact
+#define ME_MC_FORCESEND "MetaContacts/ForceSend"
+
+// fired when a metacontact is 'unforced' to send using a specific subcontact
+// wParam=(HANDLE)hMetaContact
+// lParam=0
+#define ME_MC_UNFORCESEND "MetaContacts/UnforceSend"
+
+// method to get protocol name - used to be sure you're dealing with a "real" metacontacts plugin :)
+// wParam=lParam=0
+#define MS_MC_GETPROTOCOLNAME "MetaContacts/GetProtoName"
+
+
+// added 0.9.5.0 (22/3/05)
+// wParam=(HANDLE)hContact
+// lParam=0
+// convert a given contact into a metacontact
+#define MS_MC_CONVERTTOMETA "MetaContacts/ConvertToMetacontact"
+
+// added 0.9.5.0 (22/3/05)
+// wParam=(HANDLE)hContact
+// lParam=(HANDLE)hMeta
+// add an existing contact to a metacontact
+#define MS_MC_ADDTOMETA "MetaContacts/AddToMetacontact"
+
+// added 0.9.5.0 (22/3/05)
+// wParam=0
+// lParam=(HANDLE)hContact
+// remove a contact from a metacontact
+#define MS_MC_REMOVEFROMMETA "MetaContacts/RemoveFromMetacontact"
+
+
+// added 0.9.13.2 (6/10/05)
+// wParam=(BOOL)disable
+// lParam=0
+// enable/disable the 'hidden group hack' - for clists that support subcontact hiding using 'IsSubcontact' setting
+// should be called once in the clist 'onmodulesloaded' event handler (which, since it's loaded after the db, will be called
+// before the metacontact onmodulesloaded handler where the subcontact hiding is usually done)
+#define MS_MC_DISABLEHIDDENGROUP "MetaContacts/DisableHiddenGroup"
+
+#endif
diff --git a/Plugins/mydetails/sdk/m_proto_listeningto.h b/Plugins/mydetails/sdk/m_proto_listeningto.h
new file mode 100644
index 0000000..b3e5f9d
--- /dev/null
+++ b/Plugins/mydetails/sdk/m_proto_listeningto.h
@@ -0,0 +1,137 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2006 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+//this module was created in v0.6.0.0
+
+#ifndef M_PROTO_LISTENINGTO_H__
+#define M_PROTO_LISTENINGTO_H__ 1
+
+
+// Protocol Services /////////////////////////////////////////////////////////////////
+
+// This is the services a protocol have to support to support listening info
+
+typedef struct {
+ int cbSize;
+
+ union {
+ char* pszType; // Media type: Music, Video, etc...
+ TCHAR* ptszType;
+ };
+ union {
+ char* pszArtist; // Artist name
+ TCHAR* ptszArtist;
+ };
+ union {
+ char* pszAlbum; // Algum name
+ TCHAR* ptszAlbum;
+ };
+ union {
+ char* pszTitle; // Song name
+ TCHAR* ptszTitle;
+ };
+ union {
+ char* pszTrack; // Track number
+ TCHAR* ptszTrack;
+ };
+ union {
+ char* pszYear; // Song year
+ TCHAR* ptszYear;
+ };
+ union {
+ char* pszGenre; // Song genre
+ TCHAR* ptszGenre;
+ };
+ union {
+ char* pszLength; // Song length
+ TCHAR* ptszLength;
+ };
+ union {
+ char* pszPlayer; // Player name
+ TCHAR* ptszPlayer;
+ };
+
+ DWORD dwFlags;
+
+} LISTENINGTOINFO;
+
+#define LTI_UNICODE 1
+
+#ifdef UNICODE
+ #define LTI_TCHAR LTI_UNICODE
+#else
+ #define LTI_TCHAR 0
+#endif
+
+// Set the listening info for the protocol.
+// Pass NULL to remove it.
+// wParam = NULL
+// lParam = LISTENINGTOINFO *
+#define PS_SET_LISTENINGTO "/SetListeningTo"
+
+// Get the listening info for the protocol
+// wParam = NULL
+// lParam = LISTENINGTOINFO *
+// The strings inside the struct need to be free using miranda free.
+#define PS_GET_LISTENINGTO "/GetListeningTo"
+
+// Also the protocol have to save a string with the text the other user is (probabily)
+// seeing under the main db key: <protocol>/ListeningTo
+
+// For a contact, the protocol should store the listening info as an string inside
+// the contact db key: <protocol>/ListeningTo
+
+
+// ListeningTo configuration plugin //////////////////////////////////////////////////
+
+// One plugin can be used to set some options relative to the listening to information.
+// But protocols should not assume this plugin exists. If it does not exist, protocols
+// have to decide what default to use.
+// This plugin have to support the following services:
+
+// Get the text format the user wants him / his contacts to see. Some strings represents
+// the text information:
+// %artist%, %album%, %title%, %track%, %year%, %genre%, %length%, %player%, %type%
+// This service is optional
+// wParam = TCHAR* - default text for this protocol
+// lParam = 0
+// Returns a TCHAR* containg the user setting. This need to be free using miranda free.
+#define MS_LISTENINGTO_GETTEXTFORMAT "ListeningTo/GetTextFormat"
+
+// Get the text the user wants him / his contacts to see, parsed with the info sent to
+// this service. Uses the same variables as the above service to the default text.
+// wParam = TCHAR* - default text for this protocol
+// lParam = LISTENINGTOINFO *
+// Returns a TCHAR* containg the parsed text. This need to be free using miranda free.
+#define MS_LISTENINGTO_GETPARSEDTEXT "ListeningTo/GetParsedText"
+
+// Get if the contact options about how to show the music info should be overriten or
+// not.
+// wParam = NULL
+// lParam = hContact
+// Returns a BOOL
+#define MS_LISTENINGTO_OVERRIDECONTACTOPTION "ListeningTo/OverrideContactOption"
+
+
+#endif // M_PROTO_LISTENINGTO_H__
+
diff --git a/Plugins/mydetails/sdk/m_skin_eng.h b/Plugins/mydetails/sdk/m_skin_eng.h
new file mode 100644
index 0000000..c099a28
--- /dev/null
+++ b/Plugins/mydetails/sdk/m_skin_eng.h
@@ -0,0 +1,435 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2008 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef M_ske_H_INC
+#define M_ske_H_INC
+
+
+
+/*defaults*/
+#define DEFAULT_FIT_MODE FM_STRETCH
+#define DEFAULT_STYLE ST_BRUSH
+#define DEFAULT_BKCOLOUR GetSysColor(COLOR_3DFACE)
+#define DEFAULT_SELBKCOLOUR GetSysColor(COLOR_HIGHLIGHT)
+#define SIZING_MARGIN 3
+
+/* Fit mode */
+#define FM_STRETCH 0
+#define FM_TILE_HORZ 1
+#define FM_TILE_VERT 2
+#define FM_TILE_BOTH 3
+
+/*Object types*/
+#define OT_ANY 0
+#define OT_GLYPHOBJECT 1
+#define OT_FONTOBJECT 2
+
+/*STYLE INDEXEX*/
+#define ST_SKIP 0
+#define ST_PARENT 1
+#define ST_BRUSH 2
+#define ST_IMAGE 3
+#define ST_SOLARIZE 4 //Not used yet.
+#define ST_FRAGMENT 5
+#define ST_GRADIENT 6
+
+//formats:
+#define ADT_TOP 0x00000000
+#define ADT_LEFT 0x00000000
+#define ADT_HCENTER 0x00000001
+#define ADT_RIGHT 0x00000002
+#define ADT_VCENTER 0x00000004
+#define ADT_BOTTOM 0x00000008
+//#define ADT_ECLIPSE 64
+
+
+/*SERVICES*/
+
+//toggle the 'hide offline contacts' flag and call CLUI
+//wParam=0
+//lParam=0
+#define MS_CLIST_TOGGLEHIDEOFFLINE "CList/ToggleHideOffline"
+
+#define MS_CLIST_TOGGLEGROUPS "CList/ToggleGroups"
+
+#define MS_CLIST_TOGGLESOUNDS "CList/ToggleSounds"
+
+// Add new object to skin object list.
+// wParam = pointer to SKINOBJECTDESCRIPTOR structure
+// lParam = 0 ( used for internal purposes: pointer to skin object list)
+#define MS_SKIN_REGISTEROBJECT "ModernList/RegisterObject"
+
+// Add new object to skin object list.
+// wParam = pointer to DEF_SKIN_OBJECT_PARAMS structure
+// lParam = 0 ( used for internal purposes: pointer to skin object list)
+#define MS_SKIN_REGISTERDEFOBJECT "ModernList/RegisterDefObject"
+
+typedef struct s_DEF_SKIN_OBJECT_PARAMS
+{
+ char * szObjectID;
+ BYTE defStyle;
+ DWORD defColor;
+ // SKINOBJECTSLIST * Skin;
+} DEF_SKIN_OBJECT_PARAMS;
+
+
+// Request painting glyph object
+// wParam = pointer to SKINDRAWREQUEST structure
+// lParam = 0
+#define MS_SKIN_DRAWGLYPH "ModernList/DrawGlyph"
+
+
+
+/* EVENTS */
+#define ME_SKIN_SERVICESCREATED "ModernList/ServicesCreated"
+
+/* DRAWGLYPH Request structure */
+typedef struct s_SKINDRAWREQUEST
+{
+ char szObjectID[255]; // Unic Object ID (path) to paint
+ RECT rcDestRect; // Rectangle to fit
+ RECT rcClipRect; // Rectangle to paint in.
+ HDC hDC; // Handler to device context to paint in.
+} SKINDRAWREQUEST,*LPSKINDRAWREQUEST;
+
+/* SKINOBJECTDESCRIPTOR opbject descriptor structure */
+typedef struct tagSKINOBJECTDESCRIPTOR
+{
+ BYTE bType; // One of OT_* values.
+ char* szObjectID; // Unic Object ID (path) [255] max
+ LPVOID Data; // Pointer to GLYPHOBJECT strycture if bType==OT_GLYPHOBJECT
+} SKINOBJECTDESCRIPTOR, *LPSKINOBJECTDESCRIPTOR;
+
+/* SKINOBJECTDESCRIPTOR opbject descriptor structure */
+typedef struct s_GLYPHOBJECT
+{
+ BYTE Style; // One of ST_* values
+ HBITMAP hGlyph; // Bitmap handler (for internal use only)
+ DWORD dwTop, dwLeft, dwBottom, dwRight; // Margins
+ char* szFileName; // FileName of image
+ DWORD dwColor; // Fill color
+ BYTE dwAlpha; // Constant alpha-transparency level
+ BYTE FitMode; // One of FM_* values
+ POINT clipArea; // Object image rect on full image
+ SIZE szclipArea; // Object image rect on full image
+ SortedList * plTextList; // List of GLYPHTEXT
+ LONG bmWidth;
+ LONG bmHeight;
+ BYTE bmBitsPixel;
+} GLYPHOBJECT,*LPGLYPHOBJECT;
+
+/* SKINTEXTDESCRIPTOR opbject descriptor structure */
+typedef struct s_GLYPHTEXT
+{
+ char * szGlyphTextID;
+ TCHAR * stText;
+ TCHAR * stValueText;
+ DWORD dwFlags;
+ DWORD dwColor; // Color (InvAA)(RR)(GG)(BB)
+ DWORD dwShadow; //ToDo: Color2/Shaddow
+ int iLeft,iTop,iRight,iBottom;
+ BYTE RelativeFlags;
+ char * szFontID;
+ HFONT hFont;
+ char * szObjectName;
+}GLYPHTEXT,*LPGLYPHTEXT;
+
+/* SKINTEXTDESCRIPTOR opbject descriptor structure */
+typedef struct s_SKINFONT
+{
+ char * szFontID;
+ HFONT hFont;
+}SKINFONT, *LPSKINFONT;
+
+/* HELPER FUNCTIONS */
+
+//Paint ObjectID as parent background for frame hwndIn
+int __inline SkinDrawWindowBack(HWND hwndIn, HDC hdc, RECT * rcClip, char * objectID)
+{
+ SKINDRAWREQUEST rq;
+ POINT pt={0};
+ RECT rc,r1;
+
+ HWND hwnd=(HWND)CallService(MS_CLUI_GETHWND,0,0);
+ if (!objectID) return 0;
+ GetWindowRect(hwndIn,&r1);
+ pt.x=r1.left;
+ pt.y=r1.top;
+ //ClientToScreen(hwndIn,&pt);
+ GetWindowRect(hwnd,&rc);
+ OffsetRect(&rc,-pt.x ,-pt.y);
+ rq.hDC=hdc;
+ rq.rcDestRect=rc;
+ rq.rcClipRect=*rcClip;
+ strncpy(rq.szObjectID,objectID,sizeof(rq.szObjectID));
+ ///ske_Service_DrawGlyph((WPARAM)&rq,0); //$$$
+ return CallService(MS_SKIN_DRAWGLYPH,(WPARAM)&rq,0);
+}
+
+
+//Paint ObjectID
+int __inline SkinDrawGlyph(HDC hdc, RECT * rcSize, RECT * rcClip, char * objectID);
+
+//Register object with predefined style
+int __inline CreateGlyphedObjectDefStyle(char * ObjID,BYTE defStyle);
+int __inline CreateGlyphedObjectDefColor(char * ObjID,DWORD defColor);
+//Register default object
+int __inline CreateGlyphedObject(char * ObjID);
+
+
+
+//// Creating and registering objects
+//int __inline CreateGlyphedObject(char * ObjID)
+//{
+// DEF_SKIN_OBJECT_PARAMS prm={0};
+// prm.defColor=DEFAULT_BKCOLOUR;
+// prm.defStyle=DEFAULT_STYLE;
+// prm.szObjectID=ObjID;
+// return CallService(MS_SKIN_REGISTERDEFOBJECT,(WPARAM)&prm,0);
+//}
+static BOOL __inline ScreenToClientRect(HWND hWnd, LPRECT lpRect)
+{
+ BOOL ret;
+
+ POINT pt;
+
+ pt.x = lpRect->left;
+ pt.y = lpRect->top;
+
+ ret = ScreenToClient(hWnd, &pt);
+
+ if (!ret) return ret;
+
+ lpRect->left = pt.x;
+ lpRect->top = pt.y;
+
+
+ pt.x = lpRect->right;
+ pt.y = lpRect->bottom;
+
+ ret = ScreenToClient(hWnd, &pt);
+
+ lpRect->right = pt.x;
+ lpRect->bottom = pt.y;
+
+ return ret;
+}
+
+//int __inline CreateGlyphedObjectDefStyle(char * ObjID,BYTE defStyle)
+//{
+// DEF_SKIN_OBJECT_PARAMS prm={0};
+// prm.defColor=DEFAULT_BKCOLOUR;
+// prm.defStyle=defStyle;
+// prm.szObjectID=ObjID;
+// return CallService(MS_SKIN_REGISTERDEFOBJECT,(WPARAM)&prm,0);
+//}
+//int __inline CreateGlyphedObjectDefColor(char * ObjID,DWORD defColor)
+//{
+// DEF_SKIN_OBJECT_PARAMS prm={0};
+// prm.defColor=defColor;
+// prm.defStyle=ST_BRUSH;
+// prm.szObjectID=ObjID;
+// return CallService(MS_SKIN_REGISTERDEFOBJECT,(WPARAM)&prm,0);
+//}
+static int __inline SkinDrawGlyph(HDC hdc, RECT * rcSize, RECT * rcClip, char * objectID)
+{
+ SKINDRAWREQUEST rq;
+ if (!objectID) return 0;
+ rq.hDC=hdc;
+ rq.rcDestRect=*rcSize;
+ rq.rcClipRect=*rcClip;
+ strncpy(rq.szObjectID,objectID,sizeof(rq.szObjectID));
+ return CallService(MS_SKIN_DRAWGLYPH,(WPARAM)&rq,0);
+}
+//#include "../hdr/modern_skin_selector.h"
+
+//////////////////////////////////////////////
+// //
+// New Painting sequence servises //
+// //
+//////////////////////////////////////////////
+
+typedef struct sPAINT_REQUEST
+{
+ DWORD dStructSize; //size of structure
+ HWND hWnd; //called by window
+ HDC hDC; //context to draw on
+ RECT rcUpdate; //rectangle to be painted in (relative to Top-Left corner of Main window)
+ DWORD dwFlags; //drawing flags
+ void * CallbackData; //Data for passing to callback procedure
+ char Reserved[16]; //reserved for farther usage;
+} sPaintRequest;
+
+// Request to register sub for callback painting frame area
+// wParam = hWnd of called frame
+// lParam = pointer to tPaintCallBackProc (or NULL to remove)
+// return 1 - succes, 0 - failure
+#define MS_SKINENG_REGISTERPAINTSUB "SkinEngine/ske_Service_RegisterFramePaintCallbackProcedure"
+
+// Request to repaint frame or change/drop callback data immeadeately
+// wParam = hWnd of called frame
+// lParam = pointer to sPaintRequest (or NULL to redraw all)
+#define MS_SKINENG_UPTATEFRAMEIMAGE "SkinEngine/ske_Service_UpdateFrameImage"
+
+// Request to repaint frame or change/drop callback data
+// wParam = hWnd of called frame
+// lParam = pointer to sPaintRequest (or NULL to redraw all)
+// return 2 - already queued, data updated, 1-have been queued, 0 - failure
+#define MS_SKINENG_INVALIDATEFRAMEIMAGE "SkinEngine/ske_Service_InvalidateFrameImage"
+
+// Callback proc type
+typedef int (/*__stdcall*/ *tPaintCallbackProc)(HWND hWnd, HDC hDC, RECT * rcPaint, HRGN rgnUpdate, DWORD dFlags, void * CallBackData);
+//tPaintCallbackProc PaintCallbackProc;
+
+// HELPER TO UPDATEIMAGEFRAME
+
+
+inline BOOL isSkinEngineEnabled()
+{
+ return ServiceExists(MS_SKINENG_REGISTERPAINTSUB) && !DBGetContactSettingByte(NULL, "ModernData", "DisableEngine", FALSE);
+}
+
+
+inline BOOL isLayeredEnabled()
+{
+ return isSkinEngineEnabled() && DBGetContactSettingByte(NULL, "ModernData", "EnableLayering", TRUE);
+}
+
+int __inline SkinEngUpdateImageFrame(HWND hwnd, RECT * rcUpdate, DWORD dwFlags, void * CallBackData)
+{
+ sPaintRequest sr={0};
+ sr.dStructSize=sizeof(sPaintRequest);
+ sr.hWnd=hwnd;
+ if (rcUpdate)
+ sr.rcUpdate=*rcUpdate;
+ sr.dwFlags=dwFlags;
+ sr.CallbackData=CallBackData;
+ return CallService(MS_SKINENG_UPTATEFRAMEIMAGE,(WPARAM)hwnd,(LPARAM)&sr);
+}
+
+int __inline SkinEngInvalidateImageFrame(HWND hwnd, CONST RECT * rcUpdate, DWORD dwFlags, void * CallBackData)
+{
+ sPaintRequest sr={0};
+ if (hwnd && !isLayeredEnabled()) return InvalidateRect(hwnd,rcUpdate,dwFlags);
+ sr.dStructSize=sizeof(sPaintRequest);
+ sr.hWnd=hwnd;
+ if (rcUpdate)
+ sr.rcUpdate=*rcUpdate;
+ sr.dwFlags=dwFlags;
+ sr.CallbackData=CallBackData;
+ return CallService(MS_SKINENG_INVALIDATEFRAMEIMAGE,(WPARAM)hwnd,(LPARAM)&sr);
+}
+
+
+int __inline SkinInvalidateFrame(HWND hWnd, CONST RECT* lpRect,BOOL bErase)
+{
+ return SkinEngInvalidateImageFrame(hWnd,lpRect,0,0);
+}
+// Alpha channel GDI replacements/helpers
+
+//
+// Paints text with correct alpha channel
+// wParam - pointer to AlphaTextOutParams
+#define MS_SKINENG_ALPHATEXTOUT "SkinEngine/ske_AlphaTextOut"
+typedef struct _AlphaTextOutParams
+{
+ HDC hDC;
+ LPCTSTR lpString;
+ int nCount;
+ RECT * lpRect;
+ UINT format;
+ DWORD ARGBcolor;
+ char reserv[16];
+}AlphaTextOutParams;
+
+int __inline AlphaText(HDC hDC, LPCTSTR lpString, int nCount, RECT * lpRect, UINT format, DWORD ARGBcolor)
+{
+ AlphaTextOutParams ap={0};
+ ap.hDC=hDC;
+ ap.lpString=lpString;
+ ap.nCount=nCount;
+ ap.lpRect=lpRect;
+ ap.format=format;
+ ap.ARGBcolor=ARGBcolor;
+ return CallService(MS_SKINENG_ALPHATEXTOUT,(WPARAM)&ap,0);
+}
+
+typedef struct _ImageListFixParam
+{
+ HIMAGELIST himl;
+ int index;
+ HICON hicon;
+}ImageListFixParam;
+
+typedef struct _DrawIconFixParam
+{
+ HDC hdc;
+ int xLeft;
+ int yTop;
+ HICON hIcon;
+ int cxWidth;
+ int cyWidth;
+ UINT istepIfAniCur;
+ HBRUSH hbrFlickerFreeDraw;
+ UINT diFlags;
+} DrawIconFixParam;
+//wParam - pointer to DrawIconFixParam
+#define MS_SKINENG_DRAWICONEXFIX "SkinEngine/DrawIconEx_Fix"
+
+int __inline mod_DrawIconEx_helper(HDC hdc,int xLeft,int yTop,HICON hIcon,int cxWidth,int cyWidth, UINT istepIfAniCur, HBRUSH hbrFlickerFreeDraw, UINT diFlags)
+{
+ DrawIconFixParam p={0};
+ p.hdc=hdc;
+ p.xLeft=xLeft;
+ p.yTop=yTop;
+ p.hIcon=hIcon;
+ p.cxWidth=cxWidth;
+ p.cyWidth=cyWidth;
+ p.istepIfAniCur=istepIfAniCur;
+ p.hbrFlickerFreeDraw=hbrFlickerFreeDraw;
+ p.diFlags=diFlags;
+ return CallService(MS_SKINENG_DRAWICONEXFIX,(WPARAM)&p,0);
+}
+
+
+
+
+// Register of plugin's user
+//
+// wParam = (WPARAM)szSetting - string that describes a user
+// format: Category/ModuleName,
+// eg: "Contact list background/CLUI",
+// "Status bar background/StatusBar"
+// lParam = (LPARAM)dwFlags
+//
+#define MS_BACKGROUNDCONFIG_REGISTER "ModernBkgrCfg/Register"
+
+//
+// Notification about changed background
+// wParam = ModuleName
+// lParam = 0
+#define ME_BACKGROUNDCONFIG_CHANGED "ModernBkgrCfg/Changed"
+
+
+
+#endif
diff --git a/Plugins/mydetails/sdk/m_smileyadd.h b/Plugins/mydetails/sdk/m_smileyadd.h
new file mode 100644
index 0000000..c950202
--- /dev/null
+++ b/Plugins/mydetails/sdk/m_smileyadd.h
@@ -0,0 +1,172 @@
+/*
+Miranda SmileyAdd Plugin
+Plugin support header file
+Copyright (C) 2004-2006 borkra, portions by Rein-Peter de Boer
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+
+#define SAFLRE_INSERTEMF 2 // insert smiley as EMF into RichEdit, otherwise bitmap inserted
+ // this flag allows "true" transparency
+
+typedef struct
+{
+ int cbSize; //size of the structure
+ HWND hwndRichEditControl; //handle to the rich edit control
+ CHARRANGE* rangeToReplace; //same meaning as for normal Richedit use (NULL = replaceall)
+ const char* Protocolname; //protocol to use... if you have defined a protocol, u can
+ //use your own protocol name. SmileyAdd will automatically
+ //select the smileypack that is defined for your protocol.
+ //Or, use "Standard" for standard smiley set. Or "ICQ", "MSN"
+ //if you prefer those icons.
+ //If not found or NULL, "Standard" will be used
+ unsigned flags; //Flags (SAFLRE_*) that define the behaivior
+ BOOL disableRedraw; //Parameter have been depricated, have no effect on operation
+ HANDLE hContact; //Contact handle
+} SMADD_RICHEDIT3;
+
+//Replace smileys in a rich edit control...
+//wParam = (WPARAM) 0; not used
+//lParam = (LPARAM) (SMADD_RICHEDIT3*) &smre; //pointer to SMADD_RICHEDIT3
+//return: TRUE if API succeeded (all parameters were valid) , FALSE if not.
+#define MS_SMILEYADD_REPLACESMILEYS "SmileyAdd/ReplaceSmileys"
+
+
+typedef struct
+{
+ int cbSize; //size of the structure
+ char* Protocolname; //protocol to use... if you have defined a protocol, you can
+ //use your own protocol name. Smiley add will automatically
+ //select the smileypack that is defined for your protocol.
+ //Or, use "Standard" for standard smiley set. Or "ICQ", "MSN"
+ //if you prefer those icons.
+ //If not found or NULL: "Standard" will be used
+ int xPosition; //Postition to place the selectwindow
+ int yPosition; // "
+ int Direction; //Direction (i.e. size upwards/downwards/etc) of the window 0, 1, 2, 3
+
+ HWND hwndTarget; //Window, where to send the message when smiley is selected.
+ UINT targetMessage; //Target message, to be sent.
+ LPARAM targetWParam; //Target WParam to be sent (LParam will be char* to select smiley)
+ //see the example file.
+ HWND hwndParent; //Parent window for smiley dialog
+ HANDLE hContact; //Contact handle
+} SMADD_SHOWSEL3;
+
+//Show smiley selection window
+//wParam = (WPARAM) 0; not used
+//lParam = (LPARAM) (SMADD_SHOWSEL3*) &smre; //pointer to SMADD_SHOWSEL3
+//return: TRUE if API succeeded (all parameters were valid) , FALSE if not.
+#define MS_SMILEYADD_SHOWSELECTION "SmileyAdd/ShowSmileySelection"
+
+
+typedef struct
+{
+ int cbSize; //size of the structure
+ char* Protocolname; // " "
+ HICON ButtonIcon; //RETURN VALUE: this is filled with the icon handle
+ //of the smiley that can be used on the button
+ //if used with GETINFO2 handle must be destroyed by user!
+ //NULL if the buttonicon is not defined...
+ int NumberOfVisibleSmileys; //Number of visible smileys defined.
+ int NumberOfSmileys; //Number of total smileys defined
+ HANDLE hContact; //Contact handle
+} SMADD_INFO2;
+
+//get button smiley icon
+//wParam = (WPARAM) 0; not used
+//lParam = (LPARAM) (SMADD_INFO2*) &smgi; //pointer to SMADD_INFO2
+//return: TRUE if API succeeded (all parameters were valid) , FALSE if not.
+#define MS_SMILEYADD_GETINFO2 "SmileyAdd/GetInfo2"
+
+// Event notifies that SmileyAdd options have changed
+// Message dialogs usually need to redraw it's content on reception of this event
+//wParam = Contact handle which options have changed, NULL if global options changed
+//lParam = (LPARAM) 0; not used
+#define ME_SMILEYADD_OPTIONSCHANGED "SmileyAdd/OptionsChanged"
+
+#define SAFL_PATH 1 // provide smiley file path, icon otherwise
+#define SAFL_UNICODE 2 // string fields in OPTIONSDIALOGPAGE are WCHAR*
+
+#if defined _UNICODE || defined UNICODE
+ #define SAFL_TCHAR SAFL_UNICODE
+#else
+ #define SAFL_TCHAR 0
+#endif
+
+typedef struct
+{
+ int cbSize; //size of the structure
+ const char* Protocolname; //protocol to use... if you have defined a protocol, u can
+ //use your own protocol name. Smiley add wil automatically
+ //select the smileypack that is defined for your protocol.
+ //Or, use "Standard" for standard smiley set. Or "ICQ", "MSN"
+ //if you prefer those icons.
+ //If not found or NULL: "Standard" will be used
+ union {
+ TCHAR* str; //String to parse
+ char* astr;
+ wchar_t* wstr;
+ };
+ unsigned flag; //One of the SAFL_ flags specifies parsing requirements
+ //This parameter should be filled by the user
+
+ unsigned numSmileys; //Number of Smileys found, this parameter filled by SmileyAdd
+ unsigned oflag; //One of the SAFL_ flags specifies content of the parse results
+ //this parameter filled by SmileyAdd
+ HANDLE hContact; //Contact handle
+} SMADD_BATCHPARSE2;
+
+typedef struct
+{
+ unsigned startChar; //Starting smiley character
+ //Because of iterative nature of the API caller should set this
+ //parameter to correct value
+ unsigned size; //Number of characters in smiley (0 if not found)
+ //Because of iterative nature of the API caller should set this
+ //parameter to correct value
+ union {
+ const TCHAR* filepath;
+ const char* afilepath;
+ const wchar_t* wfilepath;
+ HICON hIcon; //User responsible for destroying icon handle
+ };
+} SMADD_BATCHPARSERES;
+
+//find all smileys in text, API parses the provided text and returns all smileys found
+//wParam = (WPARAM) 0; not used
+//lParam = (LPARAM) (SMADD_BATCHPARSE2*) &smgp; //pointer to SMADD_BATCHPARSE2
+//function returns pointer to array SMADD_BATCHPARSERES records for each smiley found
+//if no smileys found NULL is returned
+//if non NULL value returned pointer must be freed with MS_SMILEYADD_BATCHFREE API
+#define MS_SMILEYADD_BATCHPARSE "SmileyAdd/BatchParse"
+
+//Free memory allocated by MS_SMILEYADD_BATCHPARSE
+//wParam = (WPARAM) 0; not used
+//lParam = (LPARAM) (SMADD_BATCHPARSERES*) &smgp; //pointer to SMADD_BATCHPARSERES
+#define MS_SMILEYADD_BATCHFREE "SmileyAdd/BatchFree"
+
+typedef struct
+{
+ int cbSize; //size of the structure
+ char* name; //smiley category name for reference
+ char* dispname; //smiley category name for display
+} SMADD_REGCAT;
+
+//Register smiley category
+//wParam = (WPARAM) 0; not used
+//lParam = (LPARAM) (SMADD_REGCAT*) &smgp; //pointer to SMADD_REGCAT
+#define MS_SMILEYADD_REGISTERCATEGORY "SmileyAdd/RegisterCategory"
diff --git a/Plugins/mydetails/sdk/m_statusplugins.h b/Plugins/mydetails/sdk/m_statusplugins.h
new file mode 100644
index 0000000..6a70f29
--- /dev/null
+++ b/Plugins/mydetails/sdk/m_statusplugins.h
@@ -0,0 +1,156 @@
+/*
+ AdvancedAutoAway Plugin for Miranda-IM (www.miranda-im.org)
+ KeepStatus Plugin for Miranda-IM (www.miranda-im.org)
+ StartupStatus Plugin for Miranda-IM (www.miranda-im.org)
+ Copyright 2003-2006 P. Boon
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+#ifndef __M_STATUSPLUGINS
+#define __M_STATUSPLUGINS
+
+// -- common status -- (all three plugins)
+typedef struct {
+ int cbSize;
+ char *szName; // pointer to protocol modulename
+ char *szMsg; // pointer to the status message (may be NULL)
+ WORD status; // the status
+ WORD lastStatus;// last status
+} PROTOCOLSETTINGEX;
+
+// wParam = PROTOCOLSETTINGEX*** (keep it like this for compatibility)
+// lParam = 0
+// returns 0 on success
+#define MS_CS_SETSTATUSEX "CommonStatus/SetStatusEx"
+
+// wParam = PROTOCOLSETTINGEX*** (keep it like this for compatibility)
+// lParam = timeout
+// returns hwnd
+#define MS_CS_SHOWCONFIRMDLGEX "CommonStatus/ShowConfirmDialogEx"
+
+// wParam = 0
+// lParam = 0
+// returns the number of protocols registerd
+#define MS_CS_GETPROTOCOUNT "CommonStatus/GetProtocolCount" // added dec '04
+
+// wParam = PROTOCOLSETTINGEX*** (keep it like this for compatibility)
+// lParam = 0
+#define ME_CS_STATUSCHANGEEX "CommonStatus/StatusChangeEx"
+
+// wParam = protoCount
+// lParam = 0
+#define ME_CS_CSMODULELOADED "CommonStatus/CommonStatusLoaded"
+
+// -- startup status --
+// wParam = profile number (set to -1 to get default profile)
+// lParam = PROTOCOLSETTINGEX*** (keep for... )(memory must be allocated protoCount*PROTOCOLSETTINGEX* and protoCount*PROTOCOLSETTINGEX)
+// szMsg member does not have to be freed
+// returns 0 on success
+#define MS_SS_GETPROFILE "StartupStatus/GetProfile" // don't use this > jan '05, internal use only
+
+// wParam = profile number
+// lParam = 0
+// return 0 on success
+#define MS_SS_LOADANDSETPROFILE "StartupStatus/LoadAndSetProfile" // you can use this
+
+// wParam = int*, maybe NULL sets this int to the default profile number
+// lParam = 0
+// returns profile count
+#define MS_SS_GETPROFILECOUNT "StartupStatus/GetProfileCount"
+
+// wParam = profile number
+// lParam = char* (must be allocated, size = 128)
+// returns 0 on success
+#define MS_SS_GETPROFILENAME "StartupStatus/GetProfileName"
+
+// -- AdvancedAutoAway --
+typedef enum {
+ ACTIVE, // user is active
+ STATUS1_SET, // first status change happened
+ STATUS2_SET, // second status change happened
+ SET_ORGSTATUS, // user was active again, original status will be restored
+ STATUS1_SET2, // first status change happened, but user may be active ('on inactive only was disabled')
+ STATUS2_SET2 // second status change happened, but user may be active ('on inactive only was disabled')
+} STATES;
+
+typedef struct {
+ PROTOCOLSETTINGEX* protocolSetting;
+ int originalStatusMode; // this is set only when going from ACTIVE to STATUS1_SET (or to STATUS2_SET)
+ // (note: this is therefore not always valid)
+ STATES
+ oldState, // state before the call
+ curState; // current state
+ BOOL statusChanged; // the status of the protocol will actually be changed
+ // (note: unlike the name suggests, the status is changed AFTER this hook is called)
+} AUTOAWAYSETTING;
+// wParam = 0;
+// lParam = AUTOAWAYSETTING*
+// Called when a protocol's state in AAA is changed this does NOT necessary means the status was changed
+// note: this hook is called for each protocol seperately
+#define ME_AAA_STATECHANGED "AdvancedAutoAway/StateChanged"
+
+// -- KeepStatus --
+#define KS_CONN_STATE_LOST 1 // lParam = protocol
+#define KS_CONN_STATE_OTHERLOCATION 2 // lParam = protocol
+#define KS_CONN_STATE_RETRY 3 // lParam = nth retry
+#define KS_CONN_STATE_STOPPEDCHECKING 4 // lParam = TRUE if success, FALSE if failed
+#define KS_CONN_STATE_LOGINERROR 5 // lParam = protocol, only if selected in options
+#define KS_CONN_STATE_RETRYNOCONN 6 // lParam = nth try, a connection attempt will not be made
+// wParam = one of above
+// lParam depends on wParam
+#define ME_KS_CONNECTIONEVENT "KeepStatus/ConnectionEvent"
+
+// wParam = 0
+// lParam = 0
+// returns 0 on succes, nonzero on failure, probably keepstatus wasn't reconnecting
+#define MS_KS_STOPRECONNECTING "KeepStatus/StopReconnecting"
+
+// wParam = TRUE to enable checking a protocol, FALSE to disable checking a protocol
+// lParam = protocol
+// return 0 on success, nonzero on failure, probably the protocol is 'hard' disabled or not found
+// note: you cannot enable a protocol that is disabled in the options screen, you can disable a protocol
+// if it's enabled in the option screen.
+#define MS_KS_ENABLEPROTOCOL "KeepStatus/EnableProtocol"
+
+// wParam = 0
+// lParam = protocol
+// returns TRUE if protocol is enabled for checked, FALSE otherwise
+#define MS_KS_ISPROTOCOLENABLED "KeepStatus/IsProtocolEnabled"
+
+// Indicate the status will be changed which will not be regarded as a connection failure.
+// wParam = 0
+// lParam = PROTOCOLSETTINGEX* of the new situation
+// returns 0
+#define MS_KS_ANNOUNCESTATUSCHANGE "KeepStatus/AnnounceStatusChange"
+
+__inline static int announce_status_change(char *szProto, int newstatus, char *szMsg) {
+
+ PROTOCOLSETTINGEX ps;
+
+ ZeroMemory(&ps, sizeof(PROTOCOLSETTINGEX));
+ ps.cbSize = sizeof(PROTOCOLSETTINGEX);
+ if (szProto != NULL) {
+ ps.lastStatus = CallProtoService(szProto, PS_GETSTATUS, 0, 0);
+ } else {
+ ps.lastStatus = CallService(MS_CLIST_GETSTATUSMODE, 0, 0);
+ }
+ ps.status = newstatus;
+ ps.szMsg = szMsg;
+ ps.szName = szProto;
+
+ return CallService(MS_KS_ANNOUNCESTATUSCHANGE, 0, (LPARAM)&ps);
+}
+
+#endif // __M_STATUSPLUGINS
diff --git a/Plugins/mydetails/sdk/m_updater.h b/Plugins/mydetails/sdk/m_updater.h
new file mode 100644
index 0000000..371b743
--- /dev/null
+++ b/Plugins/mydetails/sdk/m_updater.h
@@ -0,0 +1,146 @@
+#ifndef _M_UPDATER_H
+#define _M_UPDATER_H
+
+// NOTES:
+// - For langpack updates, include a string of the following format in the langpack text file:
+// ";FLID: <file listing name> <version>"
+// version must be four numbers seperated by '.', in the range 0-255 inclusive
+// - Updater will disable plugins that are downloaded but were not active prior to the update (this is so that, if an archive contains e.g. ansi and
+// unicode versions, the correct plugin will be the only one active after the new version is installed)...so if you add a support plugin, you may need
+// to install an ini file to make the plugin activate when miranda restarts after the update
+// - Updater will replace all dlls that have the same internal shortName as a downloaded update dll (this is so that msn1.dll and msn2.dll, for example,
+// will both be updated) - so if you have a unicode and a non-unicode version of a plugin in your archive, you should make the internal names different (which will break automatic
+// updates from the file listing if there is only one file listing entry for both versions, unless you use the 'MS_UPDATE_REGISTER' service below)
+// - Updater will install all files in the root of the archive into the plugins folder, except for langpack files that contain the FLID string which go into the root folder (same
+// folder as miranda32.exe)...all folders in the archive will also be copied to miranda's root folder, and their contents transferred into the new folders. The only exception is a
+// special folder called 'root_files' - if there is a folder by that name in the archive, it's contents will also be copied into miranda's root folder - this is intended to be used
+// to install additional dlls etc that a plugin may require)
+
+// if you set Update.szUpdateURL to the following value when registering, as well as setting your beta site and version data,
+// Updater will ignore szVersionURL and pbVersionPrefix, and attempt to find the file listing URL's from the backend XML data.
+// for this to work, the plugin name in pluginInfo.shortName must match the file listing exactly (except for case)
+#define UPDATER_AUTOREGISTER "UpdaterAUTOREGISTER"
+// Updater will also use the backend xml data if you provide URL's that reference the miranda file listing for updates (so you can use that method
+// if e.g. your plugin shortName does not match the file listing) - it will grab the file listing id from the end of these URLs
+
+typedef struct Update_tag {
+ int cbSize;
+ char *szComponentName; // component name as it will appear in the UI (will be translated before displaying)
+
+ char *szVersionURL; // URL where the current version can be found (NULL to disable)
+ BYTE *pbVersionPrefix; // bytes occuring in VersionURL before the version, used to locate the version information within the URL data
+ // (note that this URL could point at a binary file - dunno why, but it could :)
+ int cpbVersionPrefix; // number of bytes pointed to by pbVersionPrefix
+ char *szUpdateURL; // URL where dll/zip is located
+ // set to UPDATER_AUTOREGISTER if you want Updater to find the file listing URLs (ensure plugin shortName matches file listing!)
+
+ char *szBetaVersionURL; // URL where the beta version can be found (NULL to disable betas)
+ BYTE *pbBetaVersionPrefix; // bytes occuring in VersionURL before the version, used to locate the version information within the URL data
+ int cpbBetaVersionPrefix; // number of bytes pointed to by pbVersionPrefix
+ char *szBetaUpdateURL; // URL where dll/zip is located
+
+ BYTE *pbVersion; // bytes of current version, used for comparison with those in VersionURL
+ int cpbVersion; // number of bytes pointed to by pbVersion
+
+ char *szBetaChangelogURL; // url for displaying changelog for beta versions
+} Update;
+
+// register a comonent with Updater
+//
+// wparam = 0
+// lparam = (LPARAM)&Update
+#define MS_UPDATE_REGISTER "Update/Register"
+
+// utility functions to create a version string from a DWORD or from pluginInfo
+// point buf at a buffer at least 16 chars wide - but note the version string returned may be shorter
+//
+__inline static char *CreateVersionString(DWORD version, char *buf) {
+ mir_snprintf(buf, 16, "%d.%d.%d.%d", (version >> 24) & 0xFF, (version >> 16) & 0xFF, (version >> 8) & 0xFF, version & 0xFF);
+ return buf;
+}
+
+__inline static char *CreateVersionStringPlugin(PLUGININFO *pluginInfo, char *buf) {
+ return CreateVersionString(pluginInfo->version, buf);
+}
+
+
+// register the 'easy' way - use this method if you have no beta URL and the plugin is on the miranda file listing
+// NOTE: the plugin version string on the file listing must be the string version of the version in pluginInfo (i.e. 0.0.0.1,
+// four numbers between 0 and 255 inclusivem, so no letters, brackets, etc.)
+//
+// wParam = (int)fileID - this is the file ID from the file listing (i.e. the number at the end of the download link)
+// lParam = (PLUGININFO*)&pluginInfo
+#define MS_UPDATE_REGISTERFL "Update/RegisterFL"
+
+// this function can be used to 'unregister' components - useful for plugins that register non-plugin/langpack components and
+// may need to change those components on the fly
+// lParam = (char *)szComponentName
+#define MS_UPDATE_UNREGISTER "Update/Unregister"
+
+// this event is fired when the startup process is complete, but NOT if a restart is imminent
+// it is designed for status managment plugins to use as a trigger for beggining their own startup process
+// wParam = lParam = 0 (unused)
+// (added in version 0.1.6.0)
+#define ME_UPDATE_STARTUPDONE "Update/StartupDone"
+
+// this service can be used to enable/disable Updater's global status control
+// it can be called from the StartupDone event handler
+// wParam = (BOOL)enable
+// lParam = 0
+// (added in version 0.1.6.0)
+#define MS_UPDATE_ENABLESTATUSCONTROL "Update/EnableStatusControl"
+
+// An description of usage of the above service and event:
+// Say you are a status control plugin that normally sets protocol or global statuses in your ModulesLoaded event handler.
+// In order to make yourself 'Updater compatible', you would move the status control code from ModulesLoaded to another function,
+// say DoStartup. Then, in ModulesLoaded you would check for the existence of the MS_UPDATE_ENABLESTATUSCONTROL service.
+// If it does not exist, call DoStartup. If it does exist, hook the ME_UPDATE_STARTUPDONE event and call DoStartup from there. You may
+// also wish to call MS_UPDATE_ENABLESTATUSCONTROL with wParam == FALSE at this time, to disable Updater's own status control feature.
+
+// this service can be used to determine whether updates are possible for a component with the given name
+// wParam = 0
+// lParam = (char *)szComponentName
+// returns TRUE if updates are supported, FALSE otherwise
+#define MS_UPDATE_ISUPDATESUPPORTED "Update/IsUpdateSupported"
+
+#endif
+
+
+/////////////// Usage Example ///////////////
+
+#ifdef EXAMPLE_CODE
+
+// you need to #include "m_updater.h" and HookEvent(ME_SYSTEM_MODULESLOADED, OnModulesLoaded) in your Load function...
+
+int OnModulesLoaded(WPARAM wParam, LPARAM lParam) {
+
+ Update update = {0}; // for c you'd use memset or ZeroMemory...
+ char szVersion[16];
+
+ update.cbSize = sizeof(Update);
+
+ update.szComponentName = pluginInfo.shortName;
+ update.pbVersion = (BYTE *)CreateVersionString(&pluginInfo, szVersion);
+ update.cpbVersion = strlen((char *)update.pbVersion);
+
+ // these are the three lines that matter - the archive, the page containing the version string, and the text (or data)
+ // before the version that we use to locate it on the page
+ // (note that if the update URL and the version URL point to standard file listing entries, the backend xml
+ // data will be used to check for updates rather than the actual web page - this is not true for beta urls)
+ update.szUpdateURL = "http://scottellis.com.au:81/test/updater.zip";
+ update.szVersionURL = "http://scottellis.com.au:81/test/updater_test.html";
+ update.pbVersionPrefix = (BYTE *)"Updater version ";
+
+ update.cpbVersionPrefix = strlen((char *)update.pbVersionPrefix);
+
+ // do the same for the beta versions of the above struct members if you wish to allow beta updates from another URL
+
+ CallService(MS_UPDATE_REGISTER, 0, (WPARAM)&update);
+
+ // Alternatively, to register a plugin with e.g. file ID 2254 on the file listing...
+ // CallService(MS_UPDATE_REGISTERFL, (WPARAM)2254, (LPARAM)&pluginInfo);
+
+ return 0;
+}
+
+#endif
diff --git a/Plugins/mydetails/sdk/m_variables.h b/Plugins/mydetails/sdk/m_variables.h
new file mode 100644
index 0000000..152994d
--- /dev/null
+++ b/Plugins/mydetails/sdk/m_variables.h
@@ -0,0 +1,668 @@
+/*
+ Variables Plugin for Miranda-IM (www.miranda-im.org)
+ Copyright 2003-2006 P. Boon
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#ifndef __M_VARS
+#define __M_VARS
+
+#if !defined(_TCHAR_DEFINED)
+#include <tchar.h>
+#endif
+
+#ifndef VARIABLES_NOHELPER
+#include <m_button.h>
+#endif
+
+// --------------------------------------------------------------------------
+// Memory management
+// --------------------------------------------------------------------------
+
+// Release memory that was allocated by the Variables plugin, e.g. returned
+// strings.
+
+#define MS_VARS_FREEMEMORY "Vars/FreeMemory"
+
+// Parameters:
+// ------------------------
+// wParam = (WPARAM)(void *)pntr
+// Pointer to memory that was allocated by the Variables plugin (e.g. a
+// returned string) (can be NULL).
+// lParam = 0
+
+// Return Value:
+// ------------------------
+// Does return 0 on success, nozero otherwise.
+
+// Note: Do only use this service to free memory that was *explicitliy*
+// stated that it should be free with this service.
+
+
+
+#define MS_VARS_GET_MMI "Vars/GetMMI"
+
+// Get Variable's RTL/CRT function poiners to malloc(), free() and
+// realloc().
+
+// Parameters:
+// ------------------------
+// wParam = 0
+// lParam = (LPARAM) &MM_INTERFACE
+// Pointer to a memory manager interface struct (see m_system.h).
+
+// Return Value:
+// ------------------------
+// Returns 0 on success, nozero otherwise
+
+// Note: Works exactly the same as the MS_SYSTEM_GET_MMI service
+// service of m_system.h.
+
+// Helper function for easy using:
+#ifndef VARIABLES_NOHELPER
+__inline static void variables_free(void *pntr) {
+
+ CallService(MS_VARS_FREEMEMORY, (WPARAM)pntr, 0);
+}
+#endif
+
+
+
+// --------------------------------------------------------------------------
+// String formatting
+// --------------------------------------------------------------------------
+
+#define MS_VARS_FORMATSTRING "Vars/FormatString"
+
+// This service can be used to parse tokens in a text. The tokens will be
+// replaced by their resolved values. A token can either be a field or a
+// function. A field takes no arguments and is represented between
+// %-characters, e.g. "%winampsong%". A function can take any number of
+// arguments and is represented by a ? or !-character followed by the name
+// of the function and a list of arguments, e.g. "?add(1,2)".
+
+// Parameters:
+// ------------------------
+// wParam = (WPARAM)(FORMATINFO *)&fi
+// See below.
+// lParam = 0
+
+// Return Value:
+// ------------------------
+// Returns a pointer to the resolved string or NULL in case of an error.
+
+// Note: The returned pointer needs to be freed using MS_VARS_FREEMEMORY.
+
+typedef struct {
+ int cbSize; // Set this to sizeof(FORMATINFO).
+ int flags; // Flags to use (see FIF_* below).
+ union {
+ char *szFormat; // Text in which the tokens will be replaced (can't be
+ // NULL).
+ WCHAR *wszFormat;
+ TCHAR *tszFormat;
+ };
+ union {
+ char *szExtraText; // Extra, context-specific string (can be NULL) ->
+ // The field "extratext" will be replaced by this
+ // string. (Previously szSource).
+ WCHAR *wszExtraText;
+ TCHAR *tszExtraText;
+ };
+ HANDLE hContact; // Handle to contact (can be NULL) -> The field "subject"
+ // represents this contact.
+ int pCount; // (output) Number of succesful parsed tokens, needs to be set
+ // to 0 before the call
+ int eCount; // (output) Number of failed tokens, needs to be set to 0
+ // before the call
+} FORMATINFO;
+
+// Possible flags:
+#define FIF_UNICODE 0x01 // Expects and returns unicode text (WCHAR*).
+
+#if defined(UNICODE) || defined(_UNICODE)
+#define FIF_TCHAR FIF_UNICODE // Strings in structure are TCHAR*.
+#else
+#define FIF_TCHAR 0
+#endif
+
+// Helper functions for easy using:
+
+// Helper #1: variables_parse
+// ------------------------
+// The returned string needs to be freed using MS_VARS_FREEMEMORY.
+
+#ifndef VARIABLES_NOHELPER
+__inline static TCHAR *variables_parse(TCHAR *tszFormat, TCHAR *tszExtraText, HANDLE hContact) {
+
+ FORMATINFO fi;
+
+ ZeroMemory(&fi, sizeof(fi));
+ fi.cbSize = sizeof(fi);
+ fi.tszFormat = tszFormat;
+ fi.tszExtraText = tszExtraText;
+ fi.hContact = hContact;
+ fi.flags = FIF_TCHAR;
+
+ return (TCHAR *)CallService(MS_VARS_FORMATSTRING, (WPARAM)&fi, 0);
+}
+#endif
+
+// Helper #2: variables_parsedup
+// ------------------------
+// Returns a _strdup()'ed copy of the unparsed string when Variables is not
+// installed, returns a strdup()'ed copy of the parsed result otherwise.
+
+// Note: The returned pointer needs to be released using your own free().
+
+#ifndef VARIABLES_NOHELPER
+__inline static TCHAR *variables_parsedup(TCHAR *tszFormat, TCHAR *tszExtraText, HANDLE hContact) {
+
+ if (ServiceExists(MS_VARS_FORMATSTRING)) {
+ FORMATINFO fi;
+ TCHAR *tszParsed, *tszResult;
+
+ ZeroMemory(&fi, sizeof(fi));
+ fi.cbSize = sizeof(fi);
+ fi.tszFormat = tszFormat;
+ fi.tszExtraText = tszExtraText;
+ fi.hContact = hContact;
+ fi.flags |= FIF_TCHAR;
+ tszParsed = (TCHAR *)CallService(MS_VARS_FORMATSTRING, (WPARAM)&fi, 0);
+ if (tszParsed) {
+ tszResult = _tcsdup(tszParsed);
+ CallService(MS_VARS_FREEMEMORY, (WPARAM)tszParsed, 0);
+ return tszResult;
+ }
+ }
+ return tszFormat?_tcsdup(tszFormat):tszFormat;
+}
+#endif
+
+
+
+// --------------------------------------------------------------------------
+// Register tokens
+// --------------------------------------------------------------------------
+
+// Plugins can define tokens which will be parsed by the Variables plugin.
+
+#define MS_VARS_REGISTERTOKEN "Vars/RegisterToken"
+
+// With this service you can define your own token. The newly added tokens
+// using this service are taken into account on every call to
+// MS_VARS_FORMATSTRING.
+
+// Parameters:
+// ------------------------
+// wParam = 0
+// lParam = (LPARAM)(TOKENREGISTER*)&tr
+// See below.
+
+// Return Value:
+// ------------------------
+// Returns 0 on success, nonzero otherwise. Existing tokens will be
+// 'overwritten' if registered twice.
+
+// Needed for szService and parseFunction:
+typedef struct {
+ int cbSize; // You need to check if this is >=sizeof(ARGUMENTSINFO)
+ // (already filled in).
+ FORMATINFO *fi; // Arguments passed to MS_VARS_FORMATSTRING.
+ unsigned int argc; // Number of elements in the argv array.
+ union {
+ char **argv; // Argv[0] will be the token name, the following elements
+ // are the additional arguments.
+ WCHAR **wargv; // If the registered token was registered as a unicode
+ // token, wargv should be accessed.
+ TCHAR **targv;
+ };
+ int flags; // (output) You can set flags here (initially 0), use the
+ // AIF_* flags (see below).
+} ARGUMENTSINFO;
+
+// Available flags for ARGUMENTSINFO:
+// Set the flags of the ARGUMENTSINFO struct to any of these to influence
+// further parsing.
+#define AIF_DONTPARSE 0x01 // Don't parse the result of this function,
+ // usually the result of a token is parsed
+ // again, if the `?` is used as a function
+ // character.
+#define AIF_FALSE 0x02 // The function returned logical false.
+
+// Definition of parse/cleanup functions:
+typedef char* (*VARPARSEFUNCA)(ARGUMENTSINFO *ai);
+typedef WCHAR* (*VARPARSEFUNCW)(ARGUMENTSINFO *ai);
+typedef void (*VARCLEANUPFUNCA)(char *szReturn);
+typedef void (*VARCLEANUPFUNCW)(WCHAR *wszReturn);
+
+#if defined(UNICODE) || defined(_UNICODE)
+#define VARPARSEFUNC VARPARSEFUNCW
+#define VARCLEANUPFUNC VARCLEANUPFUNCW
+#else
+#define VARPARSEFUNC VARPARSEFUNCA
+#define VARCLEANUPFUNC VARCLEANUPFUNCA
+#endif
+
+typedef struct {
+ int cbSize; // Set this to sizeof(TOKENREGISTER).
+ union {
+ char *szTokenString; // Name of the new token to be created, without %,
+ // ?, ! etc. signs (can't be NULL).
+ WCHAR *wszTokenString;
+ TCHAR *tszTokenString;
+ };
+ union {
+ char *szService; // Name of a service that is used to request the
+ // token's value, if no service is used, a function
+ // and TRF_PARSEFUNC must be used.
+ VARPARSEFUNCA parseFunction; // See above, use with TRF_PARSEFUNC.
+ VARPARSEFUNCW parseFunctionW;
+ VARPARSEFUNC parseFunctionT;
+ };
+ union {
+ char *szCleanupService; // Name of a service to be called when the
+ // memory allocated in szService can be freed
+ // (only used when flag VRF_CLEANUP is set,
+ // else set this to NULL).
+ VARCLEANUPFUNCA cleanupFunction; // See above, use with TRF_CLEANUPFUNC.
+ VARCLEANUPFUNCW cleanupFunctionW;
+ VARCLEANUPFUNC cleanupFunctionT;
+ };
+ char *szHelpText; // Help info shown in help dialog (can be NULL). Has to
+ // be in the following format:
+ // "subject\targuments\tdescription"
+ // (Example: "math\t(x, y ,...)\tx + y + ..."), or:
+ // "subject\tdescription"
+ // (Example: "miranda\tPath to the Miranda-IM
+ // executable").
+ // Note: subject and description are translated by
+ // Variables.
+ int memType; // Describes which method Varibale's plugin needs to use to
+ // free the returned buffer, use one of the VR_MEM_* values
+ // (see below). Only valid if the flag VRF_FREEMEM is set,
+ // use TR_MEM_OWNER otherwise).
+ int flags; // Flags to use (see below), one of TRF_* (see below).
+} TOKENREGISTER;
+
+// Available Memory Storage Types:
+// These values describe which method Variables Plugin will use to free the
+// buffer returned by the parse function or service
+#define TR_MEM_VARIABLES 1 // Memory is allocated using the functions
+ // retrieved by MS_VARS_GET_MMI.
+#define TR_MEM_MIRANDA 2 // Memory is allocated using Miranda's Memory
+ // Manager Interface (using the functions
+ // returned by MS_SYSTEM_GET_MMI), if
+ // VRF_FREEMEM is set, the memory will be
+ // freed by Variables.
+#define TR_MEM_OWNER 3 // Memory is owned by the calling plugin
+ // (can't be freed by Variables Plugin
+ // automatically). This should be used if
+ // VRF_FREEMEM is not specified in the flags.
+
+// Available Flags for TOKENREGISTER:
+#define TRF_FREEMEM 0x01 // Variables Plugin will automatically free the
+ // pointer returned by the parse function or
+ // service (which method it will us is
+ // specified in memType -> see above).
+#define TRF_CLEANUP 0x02 // Call cleanup service or function, notifying
+ // that the returned buffer can be freed.
+ // Normally you should use either TRF_FREEMEM
+ // or TRF_CLEANUP.
+#define TRF_PARSEFUNC 0x40 // parseFunction will be used instead of a
+ // service.
+#define TRF_CLEANUPFUNC 0x80 // cleanupFunction will be used instead of a
+ // service.
+#define TRF_USEFUNCS TRF_PARSEFUNC|TRF_CLEANUPFUNC
+#define TRF_UNPARSEDARGS 0x04 // Provide the arguments for the parse
+ // function in their raw (unparsed) form.
+ // By default, arguments are parsed before
+ // presenting them to the parse function.
+#define TRF_FIELD 0x08 // The token can be used as a %field%.
+#define TRF_FUNCTION 0x10 // The token can be used as a ?function().
+ // Normally you should use either TRF_FIELD or
+ // TRF_FUNCTION.
+#define TRF_UNICODE 0x20 // Strings in structure are unicode (WCHAR*).
+ // In this case, the strings pointing to the
+ // arguments in the ARGUMENTS struct are
+ // unicode also. The returned buffer is
+ // expected to be unicode also, and the
+ // unicode parse and cleanup functions are
+ // called.
+
+#if defined(UNICODE) || defined(_UNICODE)
+#define TRF_TCHAR TRF_UNICODE // Strings in structure are TCHAR*.
+#else
+#define TRF_TCHAR 0
+#endif
+
+// Deprecated:
+#define TRF_CALLSVC TRF_CLEANUP
+
+// Callback Service (szService) / parseFunction:
+// ------------------------
+// Service that is called automatically by the Variable's Plugin to resolve a
+// registered variable.
+
+// Parameters:
+// wParam = 0
+// lParam = (LPARAM)(ARGUMENTSINFO *)&ai
+// see above
+
+// Return Value:
+// Needs to return the pointer to a dynamically allocacated string or NULL.
+// A return value of NULL is regarded as an error (eCount will be increaded).
+// Flags in the ARGUMENTSINFO struct can be set (see above).
+
+// Callback Service (szCallbackService) / cleanupFunction:
+// ------------------------
+// This service is called when the memory that was allocated by the parse
+// function or service can be freed. Note: It will only be called when the
+// flag VRF_CLEANUP of TOKENREGISTER is set.
+
+// Parameters:
+// wParam = 0
+// lParam = (LPARAM)(char *)&res
+// Result from parse function or service (pointer to a string).
+
+// Return Value:
+// Should return 0 on success.
+
+
+
+// --------------------------------------------------------------------------
+// Show the help dialog
+// --------------------------------------------------------------------------
+
+// Plugins can invoke Variables' help dialog which can be used for easy input
+// by users.
+
+#define MS_VARS_SHOWHELPEX "Vars/ShowHelpEx"
+
+// This service can be used to open the help dialog of Variables. This dialog
+// provides easy input for the user and/or information about the available
+// tokens.
+
+// Parameters:
+// ------------------------
+// wParam = (WPARAM)(HWND)hwndParent
+// lParam = (LPARAM)(VARHELPINFO)&vhi
+// See below.
+
+// Return Value:
+// ------------------------
+// Returns 0 on succes, any other value on error.
+
+typedef struct {
+ int cbSize; // Set to sizeof(VARHELPINFO).
+ FORMATINFO *fi; // Used for both input and output. If this pointer is not
+ // NULL, the information is used as the initial values for
+ // the dialog.
+ HWND hwndCtrl; // Used for both input and output. The window text of this
+ // window will be read and used as the initial input of the
+ // input dialog. If the user presses the OK button the window
+ // text of this window will be set to the text of the input
+ // field and a EN_CHANGE message via WM_COMMAND is send to
+ // this window. (Can be NULL).
+ char *szSubjectDesc; // The description of the %subject% token will be set
+ // to this text, if not NULL. This is translated
+ // automatically.
+ char *szExtraTextDesc; // The description of the %extratext% token will be
+ // set to this text, if not NULL. This is translated
+ // automatically.
+ int flags; // Flags, see below.
+} VARHELPINFO;
+
+
+// Flags for VARHELPINFO
+#define VHF_TOKENS 0x00000001 // Create a dialog with the list of
+ // tokens
+#define VHF_INPUT 0x00000002 // Create a dialog with an input
+ // field (this contains the list of
+ // tokens as well).
+#define VHF_SUBJECT 0x00000004 // Create a dialog to select a
+ // contact for the %subject% token.
+#define VHF_EXTRATEXT 0x00000008 // Create a dialog to enter a text
+ // for the %extratext% token.
+#define VHF_HELP 0x00000010 // Create a dialog with help info.
+#define VHF_HIDESUBJECTTOKEN 0x00000020 // Hide the %subject% token in the
+ // list of tokens.
+#define VHF_HIDEEXTRATEXTTOKEN 0x00000040 // Hide the %extratext% token in
+ // the list of tokens.
+#define VHF_DONTFILLSTRUCT 0x00000080 // Don't fill the struct with the
+ // new information if OK is pressed
+#define VHF_FULLFILLSTRUCT 0x00000100 // Fill all members of the struct
+ // when OK is pressed. By default
+ // only szFormat is set. With this
+ // flag on, hContact and
+ // szExtraText are also set.
+#define VHF_SETLASTSUBJECT 0x00000200 // Set the last contact that was
+ // used in the %subject% dialog in
+ // case fi.hContact is NULL.
+
+// Predefined flags
+#define VHF_FULLDLG VHF_INPUT|VHF_SUBJECT|VHF_EXTRATEXT|VHF_HELP
+#define VHF_SIMPLEDLG VHF_INPUT|VHF_HELP
+#define VHF_NOINPUTDLG VHF_TOKENS|VHF_HELP
+
+// If the service fills information in the struct for szFormat or szExtraText,
+// these members must be free'd using the free function of Variables.
+// If wParam==NULL, the dialog is created modeless. Only one dialog can be
+// shown at the time.
+// If both hwndCtrl and fi are NULL, the user input will not be retrievable.
+// In this case, the dialog is created with only a "Close" button, instead of
+// the "OK" and "Cancel" buttons.
+// In case of modeless dialog and fi != NULL, please make sure this pointer
+// stays valid while the dialog is open.
+
+// Helper function for easy use in standard case:
+#ifndef VARIABLES_NOHELPER
+__inline static int variables_showhelp(HWND hwndDlg, UINT uIDEdit, int flags, char *szSubjectDesc, char *szExtraDesc) {
+
+ VARHELPINFO vhi;
+
+ ZeroMemory(&vhi, sizeof(VARHELPINFO));
+ vhi.cbSize = sizeof(VARHELPINFO);
+ if (flags == 0) {
+ flags = VHF_SIMPLEDLG;
+ }
+ vhi.flags = flags;
+ vhi.hwndCtrl = GetDlgItem(hwndDlg, uIDEdit);
+ vhi.szSubjectDesc = szSubjectDesc;
+ vhi.szExtraTextDesc = szExtraDesc;
+
+ return CallService(MS_VARS_SHOWHELPEX, (WPARAM)hwndDlg, (LPARAM)&vhi);
+}
+#endif
+
+
+#define MS_VARS_GETSKINITEM "Vars/GetSkinItem"
+
+// This service can be used to get the icon you can use for example on the
+// Variables help button in your options screen. You can also get the tooltip
+// text to use with such a button. If icon library is available the icon will
+// be retrieved from icon library manager, otherwise the default is returned.
+
+// Parameters:
+// ------------------------
+// wParam = (WPARAM)0
+// lParam = (LPARAM)VSI_* (see below)
+
+// Return Value:
+// ------------------------
+// Depends on the information to retrieve (see below).
+
+// VSI_ constants
+#define VSI_HELPICON 1 // Can be used on the button accessing the
+ // Variables help dialog. Returns (HICON)hIcon on
+ // success or NULL on failure;
+#define VSI_HELPTIPTEXT 2 // Returns the tooltip text you can use for the
+ // help button. Returns (char *)szTipText, a
+ // static, translated buffer containing the help
+ // text or NULL on error.
+
+// Helper to set the icon on a button accessing the help dialog.
+// Preferably a 16x14 MButtonClass control, but it works on a standard
+// button control as well. If no icon is availble (because of old version of
+// Variables) the string "V" is shown on the button. If Variables is not
+// available, the button will be hidden.
+#ifndef VARIABLES_NOHELPER
+__inline static int variables_skin_helpbutton(HWND hwndDlg, UINT uIDButton) {
+
+ int res;
+ HICON hIcon;
+ TCHAR tszClass[32];
+
+ hIcon = NULL;
+ res = 0;
+ if (ServiceExists(MS_VARS_GETSKINITEM)) {
+ hIcon = (HICON)CallService(MS_VARS_GETSKINITEM, 0, (LPARAM)VSI_HELPICON);
+ }
+ GetClassName(GetDlgItem(hwndDlg, uIDButton), tszClass, sizeof(tszClass));
+ if (!_tcscmp(tszClass, _T("Button"))) {
+ if (hIcon != NULL) {
+ SetWindowLong(GetDlgItem(hwndDlg, uIDButton), GWL_STYLE, GetWindowLong(GetDlgItem(hwndDlg, uIDButton), GWL_STYLE)|BS_ICON);
+ SendMessage(GetDlgItem(hwndDlg, uIDButton), BM_SETIMAGE, (WPARAM)IMAGE_ICON, (LPARAM)hIcon);
+ }
+ else {
+ SetWindowLong(GetDlgItem(hwndDlg, uIDButton), GWL_STYLE, GetWindowLong(GetDlgItem(hwndDlg, uIDButton), GWL_STYLE)&~BS_ICON);
+ SetDlgItemText(hwndDlg, uIDButton, _T("V"));
+ }
+ }
+ else if (!_tcscmp(tszClass, MIRANDABUTTONCLASS)) {
+ if (hIcon != NULL) {
+ char *szTipInfo;
+
+ SendMessage(GetDlgItem(hwndDlg, uIDButton), BM_SETIMAGE, (WPARAM)IMAGE_ICON, (LPARAM)hIcon);
+ if (ServiceExists(MS_VARS_GETSKINITEM)) {
+ szTipInfo = (char *)CallService(MS_VARS_GETSKINITEM, 0, (LPARAM)VSI_HELPTIPTEXT);
+ }
+ if (szTipInfo == NULL) {
+ szTipInfo = Translate("Open String Formatting Help");
+ }
+ SendMessage(GetDlgItem(hwndDlg, uIDButton), BUTTONADDTOOLTIP, (WPARAM)szTipInfo, 0);
+ SendDlgItemMessage(hwndDlg, uIDButton, BUTTONSETASFLATBTN, 0, 0);
+ }
+ else {
+ SetDlgItemText(hwndDlg, uIDButton, _T("V"));
+ }
+ }
+ else {
+ res = -1;
+ }
+ ShowWindow(GetDlgItem(hwndDlg, uIDButton), ServiceExists(MS_VARS_FORMATSTRING));
+
+ return res;
+}
+#endif
+
+
+#define MS_VARS_SHOWHELP "Vars/ShowHelp"
+
+// WARNING: This service is obsolete, please use MS_VARS_SHOWHELPEX
+
+// Shows a help dialog where all possible tokens are displayed. The tokens
+// are explained on the dialog, too. The user can edit the initial string and
+// insert as many tokens as he likes.
+
+// Parameters:
+// ------------------------
+// wParam = (HWND)hwndEdit
+// Handle to an edit control in which the modified string
+// should be inserted (When the user clicks OK in the dialog the edited
+// string will be set to hwndEdit) (can be NULL).
+// lParam = (char *)pszInitialString
+// String that the user is provided with initially when
+// the dialog gets opened (If this is NULL then the current text in the
+// hwndEdit edit control will be used) (can be NULL).
+
+// Return Value:
+// ------------------------
+// Returns the handle to the help dialog (HWND).
+
+// Note: Only one help dialog can be opened at a time. When the dialog gets
+// closed an EN_CHANGE of the edit controll will be triggered because the
+// contents were updated. (Only when user selected OK).
+
+// Example:
+// CallService(MS_VARS_SHOWHELP, (WPARAM)hwndEdit, (LPARAM)"some initial text");
+
+// --------------------------------------------------------------------------
+// Retrieve a contact's HANDLE given a string
+// --------------------------------------------------------------------------
+
+#define MS_VARS_GETCONTACTFROMSTRING "Vars/GetContactFromString"
+
+// Searching for contacts in the database. You can find contacts in db by
+// searching for their name, e.g first name.
+
+// Parameters:
+// ------------------------
+// wParam = (WPARAM)(CONTACTSINFO *)&ci
+// See below.
+// lParam = 0
+
+// Return Value:
+// ------------------------
+// Returns number of contacts found matching the given string representation.
+// The hContacts array of CONTACTSINFO struct contains these hContacts after
+// the call.
+
+// Note: The hContacts array needs to be freed after use using
+// MS_VARS_FREEMEMORY.
+
+typedef struct {
+ int cbSize; // Set this to sizeof(CONTACTSINFO).
+ union {
+ char *szContact; // String to search for, e.g. last name (can't be NULL).
+ WCHAR * wszContact;
+ TCHAR *tszContact;
+ };
+ HANDLE *hContacts; // (output) Array of contacts found.
+ DWORD flags; // Contact details that will be matched with the search
+ // string (flags can be combined).
+} CONTACTSINFO;
+
+// Possible flags:
+#define CI_PROTOID 0x00000001 // The contact in the string is encoded
+ // in the format <PROTOID:UNIQUEID>, e.g.
+ // <ICQ:12345678>.
+#define CI_NICK 0x00000002 // Search nick names.
+#define CI_LISTNAME 0x00000004 // Search custom names shown in contact
+ // list.
+#define CI_FIRSTNAME 0x00000008 // Search contact's first names (contact
+ // details).
+#define CI_LASTNAME 0x00000010 // Search contact's last names (contact
+ // details).
+#define CI_EMAIL 0x00000020 // Search contact's email adresses
+ // (contact details).
+#define CI_UNIQUEID 0x00000040 // Search unique ids of the contac, e.g.
+ // UIN.
+#define CI_CNFINFO 0x40000000 // Searches one of the CNF_* flags (set
+ // flags to CI_CNFINFO|CNF_X), only one
+ // CNF_ type possible
+#define CI_UNICODE 0x80000000 // tszContact is a unicode string
+ // (WCHAR*).
+
+#if defined(UNICODE) || defined(_UNICODE)
+#define CI_TCHAR CI_UNICODE // Strings in structure are TCHAR*.
+#else
+#define CI_TCHAR 0
+#endif
+
+
+
+#endif //__M_VARS
diff --git a/Plugins/nickhistory/Docs/helppack_nickhistory.txt b/Plugins/nickhistory/Docs/helppack_nickhistory.txt
new file mode 100644
index 0000000..295c827
--- /dev/null
+++ b/Plugins/nickhistory/Docs/helppack_nickhistory.txt
@@ -0,0 +1,33 @@
+Miranda Help Pack Version 1
+Language: English
+Locale: 0809
+Last-Modified-Using: Miranda IM 0.5
+Authors: Pescuma
+Plugins-included: nickhistory
+
+[nickhistory:1061@AyUEAyYEAywEByIEAy0EByMEAy4EDhEE]
+Log changes made by the users to the contact history=
+
+[nickhistory:1062@AyUEAyYEAywEByIEAy0EByMEAy4EDhEE]
+Enable this option to test if the text to log has only ANSI chars and in this case avoid spending some database space=Due to the way miranda store the contact history in unicode the size of an entry can be up to 3 times bigger than the text itself. If the message contains only ANSI chars, then this can be avoided.
+
+[nickhistory:1068@AyUEAyYEAywEByIEAy0EByMEAy4EDhEE]
+Enable this to monitor nickname changes=This means that the nick was empty and has beeing filled or that the text has changed (but not to an empty text).<br>This is used to log to history and to show popups.
+
+[nickhistory:1064@AyUEAyYEAywEByIEAy0EByMEAy4EDhEE]
+[nickhistory:1058@AyUEAyYEAywEByIEAy0EByMEAy4EDhEE]
+Template of the text to be used for nickname changes=The first %s found is replaced by the contact's new nickname.<br>This is used to log to history and to show popups.
+
+[nickhistory:1069@AyUEAyYEAywEByIEAy0EByMEAy4EDhEE]
+Enable this to monitor nickname removal=This means that the user had a nickname and changed it to an empty text.<br>This is used to log to history and to show popups.
+
+[nickhistory:1065@AyUEAyYEAywEByIEAy0EByMEAy4EDhEE]
+[nickhistory:1059@AyUEAyYEAywEByIEAy0EByMEAy4EDhEE]
+Template of the text to be used for nickname removal=This is used to log to history and to show popups.
+
+[nickhistory:1070@AyUEAyYEAywEByIEAy0EByMEAy4EDhEE]
+Enable this to ignore changes/removes when the user is offline=This is used to log to history and to show popups.
+
+[nickhistory:1067@AyUEAyYEAywEByIEAy0EByMEAy4EDhEE]
+[nickhistory:1041@AyUEAyYEAywEByIEAy0EByMEAy4EDhEE]
+List of protocols to be monitored=Nickname changes/removes that happens in contacts from protocols that are not in this list will be ignored. Metacontact is a special case: the nick changes/removes is not tracked for metacontacts, but it is copied to the meta if it happens in one of its subcontacts.<br>This is used to log to history and to show popups.
diff --git a/Plugins/nickhistory/Docs/langpack_nickhistory.txt b/Plugins/nickhistory/Docs/langpack_nickhistory.txt
new file mode 100644
index 0000000..9b4cb47
--- /dev/null
+++ b/Plugins/nickhistory/Docs/langpack_nickhistory.txt
@@ -0,0 +1,64 @@
+; Nick History
+; Author: Pescuma
+
+; Options
+
+[History]
+[Nickname]
+
+[ History ]
+[Log changes to history]
+[Don't save Unicode parts for 7bit ANSI history (saves db space)]
+
+[ Tracking ]
+[Track when contacts change their nicknames]
+[Track when contacts remove their nicknames]
+[Template:]
+[Only when the contact is not offline]
+
+[ Protocols ]
+[Enable tracking for these protocols:]
+
+[Popups]
+[Nickname Change]
+
+[Enable popups]
+
+[ Colours ]
+[Background colour]
+[Text colour]
+[Use Windows colours]
+[Use default colours]
+
+[ Delay ]
+[From popup plugin]
+[Custom]
+[Permanent]
+
+[ Actions ]
+[On right click:]
+[On left click:]
+[Do nothing]
+[Close popup]
+[Show history]
+
+[Preview]
+
+
+; Test popup
+
+[Test Contact]
+[Test description]
+
+
+; Contact Menu
+
+[Log Nickname changes]
+[Don't log Nickname changes]
+
+
+; Template defaults
+
+[changed his/her nickname to %s]
+[removed his/her nickname]
+[<no nickname>]
diff --git a/Plugins/nickhistory/Docs/nickhistory_changelog.txt b/Plugins/nickhistory/Docs/nickhistory_changelog.txt
new file mode 100644
index 0000000..190fbd8
--- /dev/null
+++ b/Plugins/nickhistory/Docs/nickhistory_changelog.txt
@@ -0,0 +1,46 @@
+Nick History
+
+Changelog:
+
+. 0.1.0.3
+ + Added support for Miranda 0.8
+
+. 0.1.0.2
+ * -pv- patch
+
+. 0.1.0.1
+ + Added support for \n in template (represents a line break)
+
+. 0.1.0.0
+ + Added to FL
+ + Added help pack
+
+. 0.0.0.9
+ * Fixes for changes tracking
+ * Fixes for updater url
+
+. 0.0.0.8
+ + Changed to History entry in options
+
+. 0.0.0.7
+ * Fix for crash
+
+. 0.0.0.6
+ + Options to what to track
+ * Fix for popups
+
+. 0.0.0.5
+ * Small fixes
+
+. 0.0.0.4
+ + Options page
+ + Popups
+
+. 0.0.0.3
+ + Better handling of metacontacts
+
+. 0.0.0.2
+ + Handle to nick remove
+
+. 0.0.0.1
+ + Initial version \ No newline at end of file
diff --git a/Plugins/nickhistory/Docs/nickhistory_readme.txt b/Plugins/nickhistory/Docs/nickhistory_readme.txt
new file mode 100644
index 0000000..b0b1959
--- /dev/null
+++ b/Plugins/nickhistory/Docs/nickhistory_readme.txt
@@ -0,0 +1,11 @@
+Nick History plugin
+-------------------
+
+THIS PLUGIN WAS DEPRECATED IN FAVOR OF:
+http://pescuma.mirandaim.ru/miranda/historykeeper
+
+This is a simple plugin that logs nickname changes to the history and show popups.
+
+If you are using tabSRMM you can see this events in the message log too.
+
+To report bugs/make suggestions, go to the forum thread: http://forums.miranda-im.org/showthread.php?t=9607
diff --git a/Plugins/nickhistory/Docs/nickhistory_version.txt b/Plugins/nickhistory/Docs/nickhistory_version.txt
new file mode 100644
index 0000000..f481551
--- /dev/null
+++ b/Plugins/nickhistory/Docs/nickhistory_version.txt
@@ -0,0 +1 @@
+Nick History 0.1.0.3 \ No newline at end of file
diff --git a/Plugins/nickhistory/ZIP/doit.bat b/Plugins/nickhistory/ZIP/doit.bat
new file mode 100644
index 0000000..a093809
--- /dev/null
+++ b/Plugins/nickhistory/ZIP/doit.bat
@@ -0,0 +1,80 @@
+@echo off
+
+rem Batch file to build and upload files
+rem
+rem TODO: Integration with FL
+
+set name=nickhistory
+
+rem To upload, this var must be set here or in other batch
+rem set ftp=ftp://<user>:<password>@<ftp>/<path>
+
+echo Building %name% ...
+
+msdev ..\%name%.dsp /MAKE "%name% - Win32 Release" /REBUILD
+msdev ..\%name%.dsp /MAKE "%name% - Win32 Unicode Release" /REBUILD
+
+echo Generating files for %name% ...
+
+del *.zip
+del *.dll
+copy ..\..\..\bin\release\Plugins\%name%.dll
+copy ..\..\..\bin\release\Plugins\%name%W.dll
+copy ..\Docs\%name%_changelog.txt
+copy ..\Docs\%name%_version.txt
+copy ..\Docs\%name%_readme.txt
+mkdir Docs
+cd Docs
+del /Q *.*
+copy ..\..\Docs\langpack_%name%.txt
+copy ..\..\Docs\helppack_%name%.txt
+copy ..\..\m_%name%.h
+cd ..
+mkdir src
+cd src
+del /Q *.*
+copy ..\..\*.h
+copy ..\..\*.cpp
+copy ..\..\*.
+copy ..\..\*.rc
+copy ..\..\*.dsp
+copy ..\..\*.dsw
+mkdir Docs
+cd Docs
+del /Q *.*
+copy ..\..\..\Docs\*.*
+cd ..
+cd ..
+
+"C:\Program Files\Filzip\Filzip.exe" -a -rp %name%.zip %name%.dll Docs
+"C:\Program Files\Filzip\Filzip.exe" -a -rp %name%W.zip %name%W.dll Docs
+"C:\Program Files\Filzip\Filzip.exe" -a -rp %name%_src.zip src\*.*
+
+del *.dll
+cd Docs
+del /Q *.*
+cd ..
+rmdir Docs
+cd src
+del /Q *.*
+cd Docs
+del /Q *.*
+cd ..
+rmdir Docs
+cd ..
+rmdir src
+
+if "%ftp%"=="" GOTO END
+
+echo Going to upload files...
+pause
+
+"C:\Program Files\FileZilla\FileZilla.exe" -u .\%name%.zip %ftp% -overwrite -close
+"C:\Program Files\FileZilla\FileZilla.exe" -u .\%name%W.zip %ftp% -overwrite -close
+"C:\Program Files\FileZilla\FileZilla.exe" -u .\%name%_changelog.txt %ftp% -overwrite -close
+"C:\Program Files\FileZilla\FileZilla.exe" -u .\%name%_readme.txt %ftp% -overwrite -close
+"C:\Program Files\FileZilla\FileZilla.exe" -u .\%name%_version.txt %ftp% -overwrite -close
+
+:END
+
+echo Done.
diff --git a/Plugins/nickhistory/commons.h b/Plugins/nickhistory/commons.h
new file mode 100644
index 0000000..852ade7
--- /dev/null
+++ b/Plugins/nickhistory/commons.h
@@ -0,0 +1,94 @@
+/*
+Copyright (C) 2006 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#ifndef __COMMONS_H__
+# define __COMMONS_H__
+
+
+#include <windows.h>
+#include <tchar.h>
+#include <stdio.h>
+#include <time.h>
+
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+// Miranda headers
+#include <newpluginapi.h>
+#include <m_system.h>
+#include <m_protocols.h>
+#include <m_clist.h>
+#include <m_contacts.h>
+#include <m_langpack.h>
+#include <m_database.h>
+#include <m_options.h>
+#include <m_utils.h>
+#include <m_updater.h>
+#include <m_metacontacts.h>
+#include <m_popup.h>
+#include <m_history.h>
+
+#include "../utils/mir_memory.h"
+#include "../utils/mir_options.h"
+
+#include "resource.h"
+#include "m_nickhistory.h"
+#include "options.h"
+#include "popup.h"
+
+
+#define MODULE_NAME "NickHistory"
+
+
+// Global Variables
+extern HINSTANCE hInst;
+extern PLUGINLINK *pluginLink;
+
+
+#define MAX_REGS(_A_) ( sizeof(_A_) / sizeof(_A_[0]) )
+
+#define DEFAULT_TEMPLATE_CHANGED "changed his/her nickname to %s"
+#define DEFAULT_TEMPLATE_REMOVED "removed his/her nickname"
+
+
+#ifdef UNICODE
+
+#define TCHAR_TO_CHAR(dest, orig) mir_snprintf(dest, MAX_REGS(dest), "%S", orig)
+#define CHAR_TO_TCHAR(dest, orig) mir_sntprintf(dest, MAX_REGS(dest), "%S", orig)
+
+#else
+
+#define TCHAR_TO_CHAR(dest, orig) lstrcpynA(dest, orig, MAX_REGS(dest))
+#define CHAR_TO_TCHAR(dest, orig) lstrcpynA(dest, orig, MAX_REGS(dest))
+
+#endif
+
+
+BOOL AllowProtocol(const char *proto);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // __COMMONS_H__
diff --git a/Plugins/nickhistory/m_nickhistory.h b/Plugins/nickhistory/m_nickhistory.h
new file mode 100644
index 0000000..32b6cc8
--- /dev/null
+++ b/Plugins/nickhistory/m_nickhistory.h
@@ -0,0 +1,61 @@
+/*
+Copyright (C) 2006 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#ifndef __M_NICKHISTORY_H__
+# define __M_NICKHISTORY_H__
+
+
+#define MIID_NICKNAME_CHANGE_LOGGER { 0x478be45e, 0xd331, 0x4d63, { 0xa6, 0x57, 0x85, 0xda, 0x45, 0xf8, 0xc, 0xe0 } }
+#define MIID_NICKNAME_CHANGE_NOTIFIER { 0xc749d46a, 0x885e, 0x46bf, { 0xaa, 0x4c, 0xe1, 0xae, 0xc5, 0xc9, 0xd0, 0x93 } }
+
+#define EVENTTYPE_NICKNAME_CHANGE 9001
+
+
+/*
+Return TRUE is Nick History is enabled for this contact
+
+wParam: hContact
+lParam: ignored
+*/
+#define MS_NICKHISTORY_ENABLED "NickHistory/Enabled"
+
+
+/*
+Enable Nick History for a contact
+
+wParam: hContact
+lParam: ignored
+*/
+#define MS_NICKHISTORY_ENABLE "NickHistory/Enable"
+
+
+/*
+Disable Nick History for a contact
+
+wParam: hContact
+lParam: ignored
+*/
+#define MS_NICKHISTORY_DISABLE "NickHistory/Disable"
+
+
+
+
+
+#endif // __M_NICKHISTORY_H__
diff --git a/Plugins/nickhistory/nickhistory.cpp b/Plugins/nickhistory/nickhistory.cpp
new file mode 100644
index 0000000..fa1be10
--- /dev/null
+++ b/Plugins/nickhistory/nickhistory.cpp
@@ -0,0 +1,668 @@
+/*
+Copyright (C) 2006 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#include "commons.h"
+
+
+// Prototypes ///////////////////////////////////////////////////////////////////////////
+
+
+PLUGININFOEX pluginInfo={
+ sizeof(PLUGININFOEX),
+#ifdef UNICODE
+ "Nick History (Unicode)",
+#else
+ "Nick History",
+#endif
+ PLUGIN_MAKE_VERSION(0,1,0,3),
+ "Log nickname changes to history",
+ "Ricardo Pescuma Domenecci",
+ "",
+ "© 2006 Ricardo Pescuma Domenecci",
+ "http://pescuma.mirandaim.ru/miranda/nickhistory",
+ UNICODE_AWARE,
+ 0, //doesn't replace anything built-in
+#ifdef UNICODE
+ { 0x1469b29d, 0x8a40, 0x4146, { 0x94, 0xed, 0xe4, 0x26, 0x26, 0xc1, 0x7c, 0x4a } } // {1469B29D-8A40-4146-94ED-E42626C17C4A}
+#else
+ { 0x9b898d10, 0x1f9c, 0x4948, { 0x87, 0xc1, 0xcb, 0xaf, 0x21, 0xaa, 0x11, 0x8a } } // {9B898D10-1F9C-4948-87C1-CBAF21AA118A}
+#endif
+};
+
+
+HINSTANCE hInst;
+PLUGINLINK *pluginLink;
+
+HANDLE hEnableMenu = NULL;
+HANDLE hDisableMenu = NULL;
+HANDLE hModulesLoaded = NULL;
+HANDLE hPreBuildCMenu = NULL;
+HANDLE hSettingChanged = NULL;
+
+char *metacontacts_proto = NULL;
+BOOL loaded = FALSE;
+
+int ModulesLoaded(WPARAM wParam, LPARAM lParam);
+int PreBuildContactMenu(WPARAM wParam,LPARAM lParam);
+int SettingChanged(WPARAM wParam,LPARAM lParam);
+
+int EnableHistory(WPARAM wParam,LPARAM lParam);
+int DisableHistory(WPARAM wParam,LPARAM lParam);
+int HistoryEnabled(WPARAM wParam, LPARAM lParam);
+
+BOOL ContactEnabled(HANDLE hContact);
+BOOL ProtocolEnabled(const char *protocol);
+
+
+// Functions ////////////////////////////////////////////////////////////////////////////
+
+
+extern "C" BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
+{
+ hInst = hinstDLL;
+ return TRUE;
+}
+
+
+extern "C" __declspec(dllexport) PLUGININFO* MirandaPluginInfo(DWORD mirandaVersion)
+{
+ pluginInfo.cbSize = sizeof(PLUGININFO);
+ return (PLUGININFO*) &pluginInfo;
+}
+
+
+extern "C" __declspec(dllexport) PLUGININFOEX* MirandaPluginInfoEx(DWORD mirandaVersion)
+{
+ pluginInfo.cbSize = sizeof(PLUGININFOEX);
+ return &pluginInfo;
+}
+
+
+static const MUUID interfaces[] = { MIID_NICKNAME_CHANGE_LOGGER, MIID_NICKNAME_CHANGE_NOTIFIER, MIID_LAST };
+extern "C" __declspec(dllexport) const MUUID* MirandaPluginInterfaces(void)
+{
+ return interfaces;
+}
+
+
+extern "C" int __declspec(dllexport) Load(PLUGINLINK *link)
+{
+ pluginLink = link;
+
+ init_mir_malloc();
+
+ CreateServiceFunction(MS_NICKHISTORY_DISABLE, DisableHistory);
+ CreateServiceFunction(MS_NICKHISTORY_ENABLE, EnableHistory);
+ CreateServiceFunction(MS_NICKHISTORY_ENABLED, HistoryEnabled);
+
+ // Add menu item to enable/disable status message check
+ CLISTMENUITEM mi = {0};
+ mi.cbSize = sizeof(mi);
+ mi.flags = CMIF_NOTOFFLIST;
+ mi.position = 1000100010;
+
+ mi.pszName = Translate("Don't log Nickname changes");
+ mi.pszService = MS_NICKHISTORY_DISABLE;
+ hDisableMenu = (HANDLE) CallService(MS_CLIST_ADDCONTACTMENUITEM, 0, (LPARAM)&mi);
+
+ mi.pszName = Translate("Log Nickname changes");
+ mi.pszService = MS_NICKHISTORY_ENABLE;
+ hEnableMenu = (HANDLE) CallService(MS_CLIST_ADDCONTACTMENUITEM, 0, (LPARAM)&mi);
+
+ // hooks
+ hModulesLoaded = HookEvent(ME_SYSTEM_MODULESLOADED, ModulesLoaded);
+ hPreBuildCMenu = HookEvent(ME_CLIST_PREBUILDCONTACTMENU, PreBuildContactMenu);
+ hSettingChanged = HookEvent(ME_DB_CONTACT_SETTINGCHANGED, SettingChanged);
+
+ InitOptions();
+ InitPopups();
+
+ return 0;
+}
+
+extern "C" int __declspec(dllexport) Unload(void)
+{
+ DeInitPopups();
+ DeInitOptions();
+
+ UnhookEvent(hModulesLoaded);
+ UnhookEvent(hPreBuildCMenu);
+ UnhookEvent(hSettingChanged);
+ return 0;
+}
+
+
+// Called when all the modules are loaded
+int ModulesLoaded(WPARAM wParam, LPARAM lParam)
+{
+ if (ServiceExists(MS_MC_GETPROTOCOLNAME))
+ metacontacts_proto = (char *) CallService(MS_MC_GETPROTOCOLNAME, 0, 0);
+
+ // add our modules to the KnownModules list
+ CallService("DBEditorpp/RegisterSingleModule", (WPARAM) MODULE_NAME, 0);
+
+ // updater plugin support
+ if(ServiceExists(MS_UPDATE_REGISTER))
+ {
+ Update upd = {0};
+ char szCurrentVersion[30];
+
+ upd.cbSize = sizeof(upd);
+ upd.szComponentName = pluginInfo.shortName;
+
+ upd.szUpdateURL = UPDATER_AUTOREGISTER;
+
+ upd.szBetaVersionURL = "http://pescuma.mirandaim.ru/miranda/nickhistory_version.txt";
+ upd.szBetaChangelogURL = "http://pescuma.mirandaim.ru/miranda/nickhistory#Changelog";
+ upd.pbBetaVersionPrefix = (BYTE *)"Nick History ";
+ upd.cpbBetaVersionPrefix = strlen((char *)upd.pbBetaVersionPrefix);
+#ifdef UNICODE
+ upd.szBetaUpdateURL = "http://pescuma.mirandaim.ru/miranda/nickhistoryW.zip";
+#else
+ upd.szBetaUpdateURL = "http://pescuma.mirandaim.ru/miranda/nickhistory.zip";
+#endif
+
+ upd.pbVersion = (BYTE *)CreateVersionStringPlugin((PLUGININFO*) &pluginInfo, szCurrentVersion);
+ upd.cpbVersion = strlen((char *)upd.pbVersion);
+
+ CallService(MS_UPDATE_REGISTER, 0, (LPARAM)&upd);
+ }
+
+ loaded = TRUE;
+
+ return 0;
+}
+
+
+int PreBuildContactMenu(WPARAM wParam,LPARAM lParam)
+{
+ CLISTMENUITEM clmi = {0};
+ clmi.cbSize = sizeof(clmi);
+
+ char *proto = (char*) CallService(MS_PROTO_GETCONTACTBASEPROTO, wParam, 0);
+ if (!ProtocolEnabled(proto))
+ {
+ clmi.flags = CMIM_FLAGS | CMIF_HIDDEN;
+ CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM) hDisableMenu, (LPARAM) &clmi);
+
+ clmi.flags = CMIM_FLAGS | CMIF_HIDDEN;
+ CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM) hEnableMenu, (LPARAM) &clmi);
+ }
+ else if (HistoryEnabled(wParam, 0))
+ {
+ clmi.flags = CMIM_FLAGS | CMIF_HIDDEN;
+ CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM) hEnableMenu, (LPARAM) &clmi);
+
+ clmi.flags = CMIM_FLAGS | CMIM_ICON;
+ CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM) hDisableMenu, (LPARAM) &clmi);
+ }
+ else
+ {
+ clmi.flags = CMIM_FLAGS | CMIF_HIDDEN;
+ CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM) hDisableMenu, (LPARAM) &clmi);
+
+ clmi.flags = CMIM_FLAGS | CMIM_ICON;
+ CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM) hEnableMenu, (LPARAM) &clmi);
+ }
+
+ return 0;
+}
+
+
+int EnableHistory(WPARAM wParam,LPARAM lParam)
+{
+ HANDLE hContact = (HANDLE) wParam;
+
+ if (hContact != NULL)
+ DBWriteContactSettingByte(hContact, MODULE_NAME, "Enabled", TRUE);
+
+ return 0;
+}
+
+
+int DisableHistory(WPARAM wParam,LPARAM lParam)
+{
+ HANDLE hContact = (HANDLE) wParam;
+
+ if (hContact != NULL)
+ DBWriteContactSettingByte(hContact, MODULE_NAME, "Enabled", FALSE);
+
+ return 0;
+}
+
+
+int HistoryEnabled(WPARAM wParam, LPARAM lParam)
+{
+ return ContactEnabled((HANDLE) wParam);
+}
+
+
+BOOL AllowProtocol(const char *proto)
+{
+ return TRUE;
+}
+
+
+BOOL ProtocolEnabled(const char *proto)
+{
+ if (proto == NULL)
+ return FALSE;
+
+ if (!AllowProtocol(proto))
+ return FALSE;
+
+ char setting[256];
+ mir_snprintf(setting, sizeof(setting), "%sEnabled", proto);
+ return (BOOL) DBGetContactSettingByte(NULL, MODULE_NAME, setting, TRUE);
+}
+
+
+BOOL ContactEnabled(HANDLE hContact)
+{
+ if (hContact == NULL)
+ return FALSE;
+
+ char *proto = (char *) CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM) hContact, 0);
+ if (!ProtocolEnabled(proto))
+ return FALSE;
+
+ BYTE def = TRUE;
+
+ // Is a subcontact?
+ if (ServiceExists(MS_MC_GETMETACONTACT))
+ {
+ HANDLE hMetaContact = (HANDLE) CallService(MS_MC_GETMETACONTACT, (WPARAM)hContact, 0);
+
+ if (hMetaContact != NULL)
+ def = ContactEnabled(hMetaContact);
+ }
+
+ return DBGetContactSettingByte(hContact, MODULE_NAME, "Enabled", def);
+}
+
+
+// Returns true if the unicode buffer only contains 7-bit characters.
+BOOL IsUnicodeAscii(const WCHAR * pBuffer, int nSize)
+{
+ BOOL bResult = TRUE;
+ int nIndex;
+
+ for (nIndex = 0; nIndex < nSize; nIndex++) {
+ if (pBuffer[nIndex] > 0x7F) {
+ bResult = FALSE;
+ break;
+ }
+ }
+ return bResult;
+}
+
+
+HANDLE HistoryLog(HANDLE hContact, TCHAR *log_text)
+{
+ if (log_text != NULL)
+ {
+ DBEVENTINFO event = { 0 };
+ BYTE *tmp = NULL;
+
+ event.cbSize = sizeof(event);
+
+#ifdef UNICODE
+
+ size_t needed = WideCharToMultiByte(CP_ACP, 0, log_text, -1, NULL, 0, NULL, NULL);
+ size_t len = lstrlen(log_text);
+ size_t size;
+
+ if (opts.history_only_ansi_if_possible && IsUnicodeAscii(log_text, len))
+ size = needed;
+ else
+ size = needed + (len + 1) * sizeof(WCHAR);
+
+ tmp = (BYTE *) mir_alloc0(size);
+
+ WideCharToMultiByte(CP_ACP, 0, log_text, -1, (char *) tmp, needed, NULL, NULL);
+
+ if (size > needed)
+ lstrcpyn((WCHAR *) &tmp[needed], log_text, len + 1);
+
+ event.pBlob = tmp;
+ event.cbBlob = size;
+
+#else
+
+ event.pBlob = (PBYTE) log_text;
+ event.cbBlob = strlen(log_text) + 1;
+
+#endif
+
+ event.eventType = EVENTTYPE_NICKNAME_CHANGE;
+ event.flags = DBEF_READ;
+ event.timestamp = (DWORD) time(NULL);
+
+ event.szModule = MODULE_NAME;
+
+ // Is a subcontact?
+ if (ServiceExists(MS_MC_GETMETACONTACT))
+ {
+ HANDLE hMetaContact = (HANDLE) CallService(MS_MC_GETMETACONTACT, (WPARAM)hContact, 0);
+
+ if (hMetaContact != NULL && ContactEnabled(hMetaContact))
+ CallService(MS_DB_EVENT_ADD,(WPARAM)hMetaContact,(LPARAM)&event);
+ }
+
+ HANDLE ret = (HANDLE) CallService(MS_DB_EVENT_ADD,(WPARAM)hContact,(LPARAM)&event);
+
+ mir_free(tmp);
+
+ return ret;
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+void ReplaceChars(TCHAR *text)
+{
+ TCHAR *p;
+ while(p = _tcsstr(text, _T("\\n")))
+ {
+ p[0] = _T('\r');
+ p[1] = _T('\n');
+ }
+}
+
+void Notify(HANDLE hContact, TCHAR *text)
+{
+ if (text != NULL && text[0] == _T('\0'))
+ text = NULL;
+
+ if (!opts.track_changes && text != NULL)
+ return;
+
+ if (!opts.track_removes && text == NULL)
+ return;
+
+ // Replace template with nick
+ TCHAR templ[1024];
+ lstrcpyn(templ, text == NULL ? opts.template_removed : opts.template_changed, MAX_REGS(templ));
+ ReplaceChars(templ);
+
+ TCHAR log[1024];
+ mir_sntprintf(log, sizeof(log), templ,
+ text == NULL ? TranslateT("<no nickname>") : text);
+
+ if (opts.history_enable)
+ HistoryLog(hContact, log);
+
+ if (opts.popup_enable)
+ ShowPopup(hContact, NULL, log);
+}
+
+int inline CheckStr(char *str, int not_empty, int empty)
+{
+ if (str == NULL || str[0] == '\0')
+ return empty;
+ else
+ return not_empty;
+}
+
+#ifdef UNICODE
+
+int inline CheckStr(TCHAR *str, int not_empty, int empty)
+{
+ if (str == NULL || str[0] == L'\0')
+ return empty;
+ else
+ return not_empty;
+}
+
+#endif
+
+// Return 0 if not changed, 1 if changed, 2 if removed
+int TrackChange(HANDLE hContact, DBCONTACTWRITESETTING *cws_new, BOOL ignore_remove)
+{
+ char current_setting[256];
+ mir_snprintf(current_setting, MAX_REGS(current_setting), "%sCurrent", cws_new->szSetting);
+
+ int ret = 0;
+
+ DBVARIANT dbv = {0};
+#ifdef UNICODE
+ BOOL found_current = (DBGetContactSettingW(hContact, cws_new->szModule, current_setting, &dbv) == 0);
+#else
+ BOOL found_current = (DBGetContactSetting(hContact, cws_new->szModule, current_setting, &dbv) == 0);
+#endif
+ if (!found_current)
+ {
+ // Current value does not exist
+
+ if (cws_new->value.type == DBVT_DELETED)
+ {
+ ret = 0;
+ }
+ else if (cws_new->value.type == DBVT_ASCIIZ)
+ {
+ ret = CheckStr(cws_new->value.pszVal, 1, 0);
+ }
+#ifdef UNICODE
+ else if (cws_new->value.type == DBVT_UTF8)
+ {
+ ret = CheckStr(cws_new->value.pszVal, 1, 0);
+ }
+ else if (cws_new->value.type == DBVT_WCHAR)
+ {
+ ret = CheckStr(cws_new->value.pwszVal, 1, 0);
+ }
+#endif
+ else
+ {
+ ret = 1;
+ }
+ }
+ else
+ {
+ // Current value exist
+
+ if (cws_new->value.type == DBVT_DELETED)
+ {
+ if (dbv.type == DBVT_ASCIIZ)
+ {
+ ret = CheckStr(dbv.pszVal, 2, 0);
+ }
+#ifdef UNICODE
+ else if (dbv.type == DBVT_UTF8)
+ {
+ ret = CheckStr(dbv.pszVal, 2, 0);
+ }
+ else if (dbv.type == DBVT_WCHAR)
+ {
+ ret = CheckStr(dbv.pwszVal, 2, 0);
+ }
+#endif
+ else
+ {
+ ret = 2;
+ }
+ }
+ else if (dbv.type != cws_new->value.type)
+ {
+#ifdef UNICODE
+ if ( (cws_new->value.type == DBVT_UTF8 || cws_new->value.type == DBVT_ASCIIZ || cws_new->value.type == DBVT_WCHAR)
+ && (dbv.type == DBVT_UTF8 || dbv.type == DBVT_ASCIIZ || dbv.type == DBVT_WCHAR))
+ {
+ WCHAR tmp_cws_new[1024] = L"";
+ if (cws_new->value.type == DBVT_ASCIIZ)
+ MultiByteToWideChar(CP_ACP, 0, cws_new->value.pszVal, -1, tmp_cws_new, MAX_REGS(tmp_cws_new));
+ else if (cws_new->value.type == DBVT_UTF8)
+ MultiByteToWideChar(CP_UTF8, 0, cws_new->value.pszVal, -1, tmp_cws_new, MAX_REGS(tmp_cws_new));
+ else if (cws_new->value.type == DBVT_WCHAR)
+ lstrcpyn(tmp_cws_new, cws_new->value.pwszVal, MAX_REGS(tmp_cws_new));
+
+ WCHAR tmp_dbv[1024] = L"";
+ if (dbv.type == DBVT_ASCIIZ)
+ MultiByteToWideChar(CP_ACP, 0, dbv.pszVal, -1, tmp_dbv, MAX_REGS(tmp_dbv));
+ else if (dbv.type == DBVT_UTF8)
+ MultiByteToWideChar(CP_UTF8, 0, dbv.pszVal, -1, tmp_dbv, MAX_REGS(tmp_dbv));
+ else if (dbv.type == DBVT_WCHAR)
+ lstrcpyn(tmp_dbv, dbv.pwszVal, MAX_REGS(tmp_dbv));
+
+ ret = (lstrcmpW(tmp_cws_new, tmp_dbv) ? CheckStr(tmp_cws_new, 1, 2) : 0);
+ }
+ else
+#endif
+ {
+ ret = 1;
+ }
+ }
+ else if (dbv.type == DBVT_BYTE)
+ {
+ ret = (cws_new->value.bVal != dbv.bVal ? 1 : 0);
+ }
+ else if (dbv.type == DBVT_WORD)
+ {
+ ret = (cws_new->value.wVal != dbv.wVal ? 1 : 0);
+ }
+ else if (dbv.type == DBVT_DWORD)
+ {
+ ret = (cws_new->value.dVal != dbv.dVal ? 1 : 0);
+ }
+ else if (dbv.type == DBVT_ASCIIZ)
+ {
+ ret = (strcmp(cws_new->value.pszVal, dbv.pszVal) ? CheckStr(cws_new->value.pszVal, 1, 2) : 0);
+ }
+#ifdef UNICODE
+ else if (dbv.type == DBVT_UTF8)
+ {
+ ret = (strcmp(cws_new->value.pszVal, dbv.pszVal) ? CheckStr(cws_new->value.pszVal, 1, 2) : 0);
+ }
+ else if (dbv.type == DBVT_WCHAR)
+ {
+ ret = (lstrcmp(cws_new->value.pwszVal, dbv.pwszVal) ? CheckStr(cws_new->value.pwszVal, 1, 2) : 0);
+ }
+#endif
+ }
+
+ if (ret == 1 || (ret == 2 && !ignore_remove))
+ {
+ // Copy current to old
+ char old_setting[256];
+ mir_snprintf(old_setting, MAX_REGS(old_setting), "%sOld", cws_new->szSetting);
+
+ if (dbv.type == DBVT_DELETED)
+ {
+ DBDeleteContactSetting(hContact, cws_new->szModule, old_setting);
+ }
+ else
+ {
+ DBCONTACTWRITESETTING cws_old;
+ cws_old.szModule = cws_new->szModule;
+ cws_old.szSetting = old_setting;
+ cws_old.value = dbv;
+ CallService(MS_DB_CONTACT_WRITESETTING, (WPARAM)hContact, (LPARAM)&cws_old);
+ }
+
+
+ // Copy new to current
+ if (cws_new->value.type == DBVT_DELETED)
+ {
+ DBDeleteContactSetting(hContact, cws_new->szModule, current_setting);
+ }
+ else
+ {
+ DBCONTACTWRITESETTING cws_old;
+ cws_old.szModule = cws_new->szModule;
+ cws_old.szSetting = current_setting;
+ cws_old.value = cws_new->value;
+ CallService(MS_DB_CONTACT_WRITESETTING, (WPARAM)hContact, (LPARAM)&cws_old);
+ }
+ }
+
+ if (found_current)
+ DBFreeVariant(&dbv);
+
+ return ret;
+}
+
+
+int SettingChanged(WPARAM wParam,LPARAM lParam)
+{
+ if (!loaded)
+ return 0;
+
+ if (!opts.history_enable && !opts.popup_enable)
+ return 0;
+
+ HANDLE hContact = (HANDLE) wParam;
+ if (hContact == NULL)
+ return 0;
+
+ char *proto = (char *) CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM) hContact, 0);
+ if (proto == NULL || (metacontacts_proto != NULL && !strcmp(proto, metacontacts_proto)))
+ return 0;
+
+ DBCONTACTWRITESETTING *cws = (DBCONTACTWRITESETTING*)lParam;
+ if (!strcmp(cws->szModule, proto) && !strcmp(cws->szSetting, "Nick"))
+ {
+ if (opts.track_only_not_offline)
+ {
+ if (DBGetContactSettingWord(hContact, proto, "Status", 0) <= ID_STATUS_OFFLINE)
+ return 0;
+ }
+
+ if (!ContactEnabled(hContact))
+ return 0;
+
+ int changed = TrackChange(hContact, cws, !opts.track_removes);
+ if (changed == 0)
+ return 0;
+
+ if (changed == 2)
+ {
+ Notify(hContact, NULL);
+ }
+ else // changed == 1
+#ifdef UNICODE
+ if (cws->value.type == DBVT_ASCIIZ)
+ {
+ WCHAR tmp[1024] = L"";
+ MultiByteToWideChar(CP_ACP, 0, cws->value.pszVal, -1, tmp, MAX_REGS(tmp));
+ Notify(hContact, tmp);
+ }
+ else if (cws->value.type == DBVT_UTF8)
+ {
+ WCHAR tmp[1024] = L"";
+ MultiByteToWideChar(CP_UTF8, 0, cws->value.pszVal, -1, tmp, MAX_REGS(tmp));
+ Notify(hContact, tmp);
+ }
+ else if (cws->value.type == DBVT_WCHAR)
+ {
+ Notify(hContact, cws->value.pwszVal);
+ }
+#else
+ if (cws->value.type == DBVT_ASCIIZ)
+ {
+ Notify(hContact, cws->value.pszVal);
+ }
+#endif
+ }
+
+ return 0;
+}
+
diff --git a/Plugins/nickhistory/nickhistory.dsp b/Plugins/nickhistory/nickhistory.dsp
new file mode 100644
index 0000000..26ddc5b
--- /dev/null
+++ b/Plugins/nickhistory/nickhistory.dsp
@@ -0,0 +1,247 @@
+# Microsoft Developer Studio Project File - Name="nickhistory" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=nickhistory - Win32 Release
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "nickhistory.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "nickhistory.mak" CFG="nickhistory - Win32 Release"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "nickhistory - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "nickhistory - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "nickhistory - Win32 Unicode Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "nickhistory - Win32 Unicode Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "nickhistory - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MD /W3 /GX /O1 /YX /FD /c
+# SUBTRACT BASE CPP /Fr
+# ADD CPP /nologo /G4 /MT /W3 /GX /O2 /Ob0 /I "../../include" /I "sdk" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /Fr /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x417 /d "NDEBUG"
+# ADD RSC /l 0x417 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 user32.lib shell32.lib wininet.lib gdi32.lib /nologo /base:"0x67100000" /dll /machine:I386 /filealign:0x200
+# SUBTRACT BASE LINK32 /pdb:none /map
+# ADD LINK32 kernel32.lib user32.lib /nologo /base:"0x3EC20000" /dll /map /machine:I386 /out:"..\..\bin\release\Plugins\nickhistory.dll" /filealign:0x200 /ALIGN:4096 /ignore:4108
+# SUBTRACT LINK32 /profile /pdb:none
+
+!ELSEIF "$(CFG)" == "nickhistory - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Ignore_Export_Lib 0
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /G4 /MT /W3 /GX /O2 /Ob0 /I "../../include" /FR /YX /FD /c
+# ADD CPP /nologo /G4 /MTd /W3 /GX /ZI /Od /I "../../include" /I "sdk" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /FR /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x417 /d "NDEBUG"
+# ADD RSC /l 0x417 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 comctl32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 /out:"..\..bin\release\Plugins\nickhistory.dll" /filealign:0x200 /ALIGN:4096 /ignore:4108
+# SUBTRACT BASE LINK32 /profile /pdb:none
+# ADD LINK32 kernel32.lib user32.lib /nologo /base:"0x3EC20000" /dll /incremental:yes /debug /machine:I386 /out:"..\..\bin\debug\Plugins\nickhistory.dll" /filealign:0x200 /ALIGN:4096 /ignore:4108
+# SUBTRACT LINK32 /profile /pdb:none
+
+!ELSEIF "$(CFG)" == "nickhistory - Win32 Unicode Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "nickhistory___Win32_Unicode_Debug"
+# PROP BASE Intermediate_Dir "nickhistory___Win32_Unicode_Debug"
+# PROP BASE Ignore_Export_Lib 0
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Unicode_Debug"
+# PROP Intermediate_Dir "Unicode_Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /G4 /MTd /W3 /GX /ZI /Od /I "../../include" /FR /YX /FD /c
+# ADD CPP /nologo /G4 /MTd /W3 /GX /ZI /Od /I "../../include" /I "sdk" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_UNICODE" /D "UNICODE" /D "_USRDLL" /FR /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x417 /d "NDEBUG"
+# ADD RSC /l 0x417 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 comctl32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /base:"0x32100000" /dll /incremental:yes /debug /machine:I386 /out:"..\..\bin\debug\Plugins\nickhistory.dll" /filealign:0x200 /ALIGN:4096 /ignore:4108
+# SUBTRACT BASE LINK32 /profile /pdb:none
+# ADD LINK32 kernel32.lib user32.lib /nologo /base:"0x3EC20000" /dll /incremental:yes /debug /machine:I386 /out:"..\..\bin\debug unicode\Plugins\nickhistoryW.dll" /filealign:0x200 /ALIGN:4096 /ignore:4108
+# SUBTRACT LINK32 /profile /pdb:none
+
+!ELSEIF "$(CFG)" == "nickhistory - Win32 Unicode Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "nickhistory___Win32_Unicode_Release"
+# PROP BASE Intermediate_Dir "nickhistory___Win32_Unicode_Release"
+# PROP BASE Ignore_Export_Lib 0
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Unicode_Release"
+# PROP Intermediate_Dir "Unicode_Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /G4 /MT /W3 /GX /O2 /Ob0 /I "../../include" /Fr /YX /FD /c
+# ADD CPP /nologo /G4 /MT /W3 /GX /O2 /Ob0 /I "../../include" /I "sdk" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_UNICODE" /D "UNICODE" /D "_USRDLL" /Fr /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x417 /d "NDEBUG"
+# ADD RSC /l 0x417 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 comctl32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /base:"0x32100000" /dll /map /machine:I386 /out:"..\..\bin\release\Plugins\nickhistory.dll" /filealign:0x200 /ALIGN:4096 /ignore:4108
+# SUBTRACT BASE LINK32 /profile /pdb:none
+# ADD LINK32 kernel32.lib user32.lib /nologo /base:"0x3EC20000" /dll /map /machine:I386 /out:"..\..\bin\release\Plugins\nickhistoryW.dll" /filealign:0x200 /ALIGN:4096 /ignore:4108
+# SUBTRACT LINK32 /profile /pdb:none
+
+!ENDIF
+
+# Begin Target
+
+# Name "nickhistory - Win32 Release"
+# Name "nickhistory - Win32 Debug"
+# Name "nickhistory - Win32 Unicode Debug"
+# Name "nickhistory - Win32 Unicode Release"
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=.\commons.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\m_nickhistory.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\utils\mir_memory.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\utils\mir_options.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\options.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\popup.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\resource.h
+# End Source File
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
+# Begin Source File
+
+SOURCE=.\resource.rc
+# End Source File
+# End Group
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=..\utils\mir_memory.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\utils\mir_options.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\nickhistory.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\options.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\popup.cpp
+# End Source File
+# End Group
+# Begin Group "Docs"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=.\Docs\helppack_nickhistory.txt
+# End Source File
+# Begin Source File
+
+SOURCE=.\Docs\langpack_nickhistory.txt
+# End Source File
+# Begin Source File
+
+SOURCE=.\Docs\nickhistory_changelog.txt
+# End Source File
+# Begin Source File
+
+SOURCE=.\Docs\nickhistory_readme.txt
+# End Source File
+# Begin Source File
+
+SOURCE=.\Docs\nickhistory_version.txt
+# End Source File
+# End Group
+# End Target
+# End Project
diff --git a/Plugins/nickhistory/nickhistory.dsw b/Plugins/nickhistory/nickhistory.dsw
new file mode 100644
index 0000000..7dfff24
--- /dev/null
+++ b/Plugins/nickhistory/nickhistory.dsw
@@ -0,0 +1,29 @@
+Microsoft Developer Studio Workspace File, Format Version 6.00
+# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
+
+###############################################################################
+
+Project: "nickhistory"=".\nickhistory.dsp" - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Global:
+
+Package=<5>
+{{{
+}}}
+
+Package=<3>
+{{{
+}}}
+
+###############################################################################
+
diff --git a/Plugins/nickhistory/options.cpp b/Plugins/nickhistory/options.cpp
new file mode 100644
index 0000000..0b28247
--- /dev/null
+++ b/Plugins/nickhistory/options.cpp
@@ -0,0 +1,292 @@
+/*
+Copyright (C) 2006 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#include "commons.h"
+
+
+
+// Prototypes /////////////////////////////////////////////////////////////////////////////////////
+
+HANDLE hOptHook = NULL;
+
+Options opts;
+
+
+static BOOL CALLBACK OptionsDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam);
+static BOOL CALLBACK PopupsDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam);
+
+
+static OptPageControl optionsControls[] = {
+ { &opts.history_enable, CONTROL_CHECKBOX, IDC_HISTORY, "HistoryEnable", TRUE },
+ { &opts.history_only_ansi_if_possible, CONTROL_CHECKBOX, IDC_ANSI, "HistoryOnlyANSIIfPossible", TRUE },
+ { &opts.track_changes, CONTROL_CHECKBOX, IDC_TRACK_CHANGE, "TrackChanges", TRUE },
+ { &opts.template_changed, CONTROL_TEXT, IDC_CHANGED, "TemplateChanged", (DWORD) _T(DEFAULT_TEMPLATE_CHANGED) },
+ { &opts.track_removes, CONTROL_CHECKBOX, IDC_TRACK_REMOVE, "TrackRemoves", TRUE },
+ { &opts.template_removed, CONTROL_TEXT, IDC_REMOVED, "TemplateRemoved", (DWORD) _T(DEFAULT_TEMPLATE_REMOVED) },
+ { &opts.track_only_not_offline, CONTROL_CHECKBOX, IDC_ONLY_NOT_OFFLINE,"TrackOnlyWhenNotOffline", FALSE },
+ { NULL, CONTROL_PROTOCOL_LIST, IDC_PROTOCOLS, "%sEnabled", TRUE, (int) AllowProtocol }
+};
+
+static UINT optionsExpertControls[] = {
+#ifdef UNICODE
+ IDC_ANSI,
+#endif
+ IDC_TRACK_G, IDC_TRACK_CHANGE, IDC_CHANGED_L, IDC_CHANGED, IDC_TRACK_REMOVE, IDC_REMOVED_L, IDC_REMOVED, IDC_ONLY_NOT_OFFLINE,
+ IDC_PROTOCOLS_G, IDC_PROTOCOLS_L, IDC_PROTOCOLS
+};
+
+
+static OptPageControl popupsControls[] = {
+ { &opts.popup_enable, CONTROL_CHECKBOX, IDC_POPUPS, "PopupsEnable", FALSE },
+ { &opts.popup_bkg_color, CONTROL_COLOR, IDC_BGCOLOR, "PopupsBgColor", RGB(255,255,255) },
+ { &opts.popup_text_color, CONTROL_COLOR, IDC_TEXTCOLOR, "PopupsTextColor", RGB(0,0,0) },
+ { &opts.popup_use_win_colors, CONTROL_CHECKBOX, IDC_WINCOLORS, "PopupsWinColors", FALSE },
+ { &opts.popup_use_default_colors, CONTROL_CHECKBOX, IDC_DEFAULTCOLORS, "PopupsDefaultColors", FALSE },
+ { &opts.popup_delay_type, CONTROL_RADIO, IDC_DELAYFROMPU, "PopupsDelayType", POPUP_DELAY_DEFAULT, POPUP_DELAY_DEFAULT },
+ { NULL, CONTROL_RADIO, IDC_DELAYCUSTOM, "PopupsDelayType", POPUP_DELAY_DEFAULT, POPUP_DELAY_CUSTOM },
+ { NULL, CONTROL_RADIO, IDC_DELAYPERMANENT, "PopupsDelayType", POPUP_DELAY_DEFAULT, POPUP_DELAY_PERMANENT },
+ { &opts.popup_timeout, CONTROL_SPIN, IDC_DELAY, "PopupsTimeout", 10, IDC_DELAY_SPIN, (WORD) 1, (WORD) 255 },
+ { &opts.popup_right_click_action, CONTROL_COMBO, IDC_RIGHT_ACTION, "PopupsRightClick", POPUP_ACTION_CLOSEPOPUP },
+ { &opts.popup_left_click_action, CONTROL_COMBO, IDC_LEFT_ACTION, "PopupsLeftClick", POPUP_ACTION_OPENHISTORY }
+};
+
+static UINT popupsExpertControls[] = {
+ IDC_COLOURS_G, IDC_BGCOLOR, IDC_BGCOLOR_L, IDC_TEXTCOLOR, IDC_TEXTCOLOR_L, IDC_WINCOLORS, IDC_DEFAULTCOLORS,
+ IDC_DELAY_G, IDC_DELAYFROMPU, IDC_DELAYCUSTOM, IDC_DELAYPERMANENT, IDC_DELAY, IDC_DELAY_SPIN,
+ IDC_ACTIONS_G, IDC_RIGHT_ACTION_L, IDC_RIGHT_ACTION, IDC_LEFT_ACTION_L, IDC_LEFT_ACTION,
+ IDC_PREV
+};
+
+
+// Functions //////////////////////////////////////////////////////////////////////////////////////
+
+
+int InitOptionsCallback(WPARAM wParam,LPARAM lParam)
+{
+ OPTIONSDIALOGPAGE odp;
+
+ ZeroMemory(&odp,sizeof(odp));
+ odp.cbSize=sizeof(odp);
+ odp.position=0;
+ odp.hInstance=hInst;
+ odp.ptszGroup = TranslateT("History");
+ odp.ptszTitle = TranslateT("Nickname");
+ odp.pfnDlgProc = OptionsDlgProc;
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPTIONS);
+ odp.flags = ODPF_BOLDGROUPS | ODPF_TCHAR;
+ odp.expertOnlyControls = optionsExpertControls;
+ odp.nExpertOnlyControls = MAX_REGS(optionsExpertControls);
+ CallService(MS_OPT_ADDPAGE,wParam,(LPARAM)&odp);
+
+ if(ServiceExists(MS_POPUP_ADDPOPUPEX)
+#ifdef UNICODE
+ || ServiceExists(MS_POPUP_ADDPOPUPW)
+#endif
+ )
+ {
+ ZeroMemory(&odp,sizeof(odp));
+ odp.cbSize=sizeof(odp);
+ odp.position=0;
+ odp.hInstance=hInst;
+ odp.ptszGroup = TranslateT("Popups");
+ odp.ptszTitle = TranslateT("Nickname Change");
+ odp.pfnDlgProc = PopupsDlgProc;
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_POPUPS);
+ odp.flags = ODPF_BOLDGROUPS | ODPF_TCHAR;
+ odp.expertOnlyControls = popupsExpertControls;
+ odp.nExpertOnlyControls = MAX_REGS(popupsExpertControls);
+ CallService(MS_OPT_ADDPAGE,wParam,(LPARAM)&odp);
+ }
+
+ return 0;
+}
+
+
+void InitOptions()
+{
+ LoadOptions();
+
+ hOptHook = HookEvent(ME_OPT_INITIALISE, InitOptionsCallback);
+
+ //InitMirOptions();
+}
+
+
+void DeInitOptions()
+{
+ UnhookEvent(hOptHook);
+
+ //FreeMirOptions();
+}
+
+
+void LoadOptions()
+{
+ LoadOpts(optionsControls, MAX_REGS(optionsControls), MODULE_NAME);
+ LoadOpts(popupsControls, MAX_REGS(popupsControls), MODULE_NAME);
+}
+
+
+static void OptionsEnableDisableCtrls(HWND hwndDlg)
+{
+ EnableWindow(GetDlgItem(hwndDlg, IDC_CHANGED_L), IsDlgButtonChecked(hwndDlg, IDC_TRACK_CHANGE));
+ EnableWindow(GetDlgItem(hwndDlg, IDC_CHANGED), IsDlgButtonChecked(hwndDlg, IDC_TRACK_CHANGE));
+
+ EnableWindow(GetDlgItem(hwndDlg, IDC_REMOVED_L), IsDlgButtonChecked(hwndDlg, IDC_TRACK_REMOVE));
+ EnableWindow(GetDlgItem(hwndDlg, IDC_REMOVED), IsDlgButtonChecked(hwndDlg, IDC_TRACK_REMOVE));
+}
+
+
+static BOOL CALLBACK OptionsDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ BOOL ret = SaveOptsDlgProc(optionsControls, MAX_REGS(optionsControls), MODULE_NAME, hwndDlg, msg, wParam, lParam);
+
+ switch (msg)
+ {
+ case WM_INITDIALOG:
+ {
+ OptionsEnableDisableCtrls(hwndDlg);
+
+#ifndef UNICODE
+ ShowWindow(GetDlgItem(hwndDlg, IDC_ANSI), SW_HIDE);
+#endif
+ break;
+ }
+ case WM_COMMAND:
+ {
+ switch (LOWORD(wParam))
+ {
+ case IDC_TRACK_REMOVE:
+ case IDC_TRACK_CHANGE:
+ case IDC_ONLY_NOT_OFFLINE:
+ {
+ if (HIWORD(wParam) == BN_CLICKED)
+ OptionsEnableDisableCtrls(hwndDlg);
+
+ break;
+ }
+ }
+ break;
+ }
+ }
+
+ return ret;
+}
+
+
+static void PopupsEnableDisableCtrls(HWND hwndDlg)
+{
+ BOOL enabled = IsDlgButtonChecked(hwndDlg, IDC_POPUPS);
+
+ EnableWindow(GetDlgItem(hwndDlg, IDC_COLOURS_G), enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_BGCOLOR_L), enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_TEXTCOLOR_L), enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_DELAY_G), enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_DELAYFROMPU), enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_DELAYCUSTOM), enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_DELAYPERMANENT), enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_ACTIONS_G), enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_RIGHT_ACTION_L), enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_RIGHT_ACTION), enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_LEFT_ACTION_L), enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_LEFT_ACTION), enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_PREV), enabled);
+
+ EnableWindow(GetDlgItem(hwndDlg, IDC_BGCOLOR), enabled &&
+ !IsDlgButtonChecked(hwndDlg, IDC_WINCOLORS) &&
+ !IsDlgButtonChecked(hwndDlg, IDC_DEFAULTCOLORS));
+ EnableWindow(GetDlgItem(hwndDlg, IDC_TEXTCOLOR), enabled &&
+ !IsDlgButtonChecked(hwndDlg, IDC_WINCOLORS) &&
+ !IsDlgButtonChecked(hwndDlg, IDC_DEFAULTCOLORS));
+ EnableWindow(GetDlgItem(hwndDlg, IDC_DEFAULTCOLORS), enabled &&
+ !IsDlgButtonChecked(hwndDlg, IDC_WINCOLORS));
+ EnableWindow(GetDlgItem(hwndDlg, IDC_WINCOLORS), enabled &&
+ !IsDlgButtonChecked(hwndDlg, IDC_DEFAULTCOLORS));
+
+ EnableWindow(GetDlgItem(hwndDlg, IDC_DELAY), enabled &&
+ IsDlgButtonChecked(hwndDlg, IDC_DELAYCUSTOM));
+}
+
+
+static BOOL CALLBACK PopupsDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg)
+ {
+ case WM_INITDIALOG:
+ {
+ SendDlgItemMessage(hwndDlg, IDC_RIGHT_ACTION, CB_ADDSTRING, 0, (LONG) TranslateT("Do nothing"));
+ SendDlgItemMessage(hwndDlg, IDC_RIGHT_ACTION, CB_ADDSTRING, 0, (LONG) TranslateT("Close popup"));
+ SendDlgItemMessage(hwndDlg, IDC_RIGHT_ACTION, CB_ADDSTRING, 0, (LONG) TranslateT("Show history"));
+
+ SendDlgItemMessage(hwndDlg, IDC_LEFT_ACTION, CB_ADDSTRING, 0, (LONG) TranslateT("Do nothing"));
+ SendDlgItemMessage(hwndDlg, IDC_LEFT_ACTION, CB_ADDSTRING, 0, (LONG) TranslateT("Close popup"));
+ SendDlgItemMessage(hwndDlg, IDC_LEFT_ACTION, CB_ADDSTRING, 0, (LONG) TranslateT("Show history"));
+
+ // Needs to be called here in this case
+ BOOL ret = SaveOptsDlgProc(popupsControls, MAX_REGS(popupsControls), MODULE_NAME, hwndDlg, msg, wParam, lParam);
+
+ PopupsEnableDisableCtrls(hwndDlg);
+
+ return ret;
+ }
+ case WM_COMMAND:
+ {
+ switch (LOWORD(wParam))
+ {
+ case IDC_POPUPS:
+ case IDC_WINCOLORS:
+ case IDC_DEFAULTCOLORS:
+ case IDC_DELAYFROMPU:
+ case IDC_DELAYPERMANENT:
+ case IDC_DELAYCUSTOM:
+ {
+ if (HIWORD(wParam) == BN_CLICKED)
+ PopupsEnableDisableCtrls(hwndDlg);
+
+ break;
+ }
+ case IDC_PREV:
+ {
+ Options op = opts;
+
+ if (IsDlgButtonChecked(hwndDlg, IDC_DELAYFROMPU))
+ op.popup_delay_type = POPUP_DELAY_DEFAULT;
+ else if (IsDlgButtonChecked(hwndDlg, IDC_DELAYCUSTOM))
+ op.popup_delay_type = POPUP_DELAY_CUSTOM;
+ else if (IsDlgButtonChecked(hwndDlg, IDC_DELAYPERMANENT))
+ op.popup_delay_type = POPUP_DELAY_PERMANENT;
+
+ op.popup_timeout = GetDlgItemInt(hwndDlg,IDC_DELAY, NULL, FALSE);
+ op.popup_bkg_color = SendDlgItemMessage(hwndDlg,IDC_BGCOLOR,CPM_GETCOLOUR,0,0);
+ op.popup_text_color = SendDlgItemMessage(hwndDlg,IDC_TEXTCOLOR,CPM_GETCOLOUR,0,0);
+ op.popup_use_win_colors = IsDlgButtonChecked(hwndDlg, IDC_WINCOLORS) != 0;
+ op.popup_use_default_colors = IsDlgButtonChecked(hwndDlg, IDC_DEFAULTCOLORS) != 0;
+
+ ShowTestPopup(TranslateT("Test Contact"), TranslateT("Test description"), &op);
+
+ break;
+ }
+ }
+ break;
+ }
+ }
+
+ return SaveOptsDlgProc(popupsControls, MAX_REGS(popupsControls), MODULE_NAME, hwndDlg, msg, wParam, lParam);
+}
diff --git a/Plugins/nickhistory/options.h b/Plugins/nickhistory/options.h
new file mode 100644
index 0000000..da0e1a6
--- /dev/null
+++ b/Plugins/nickhistory/options.h
@@ -0,0 +1,89 @@
+/*
+Copyright (C) 2006 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#ifndef __OPTIONS_H__
+# define __OPTIONS_H__
+
+
+#include "commons.h"
+
+#include <windows.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#define POPUP_ACTION_DONOTHING 0
+#define POPUP_ACTION_CLOSEPOPUP 1
+#define POPUP_ACTION_OPENHISTORY 2
+
+#define POPUP_DELAY_DEFAULT 0
+#define POPUP_DELAY_CUSTOM 1
+#define POPUP_DELAY_PERMANENT 2
+
+
+struct Options {
+ // Templates
+ TCHAR template_changed[1024];
+ TCHAR template_removed[1024];
+
+ // Track
+ BYTE track_changes;
+ BYTE track_removes;
+ BYTE track_only_not_offline;
+
+ // History
+ BYTE history_enable;
+ BYTE history_only_ansi_if_possible;
+
+ // Popup
+ BYTE popup_enable;
+ WORD popup_delay_type;
+ WORD popup_timeout;
+ BYTE popup_use_win_colors;
+ BYTE popup_use_default_colors;
+ COLORREF popup_bkg_color;
+ COLORREF popup_text_color;
+ WORD popup_left_click_action;
+ WORD popup_right_click_action;
+};
+
+extern Options opts;
+
+
+// Initializations needed by options
+void InitOptions();
+
+// Deinitializations needed by options
+void DeInitOptions();
+
+
+// Loads the options from DB
+// It don't need to be called, except in some rare cases
+void LoadOptions();
+
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // __OPTIONS_H__
diff --git a/Plugins/nickhistory/popup.cpp b/Plugins/nickhistory/popup.cpp
new file mode 100644
index 0000000..4e40814
--- /dev/null
+++ b/Plugins/nickhistory/popup.cpp
@@ -0,0 +1,319 @@
+/*
+Copyright (C) 2005 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#include "commons.h"
+
+
+
+// Prototypes /////////////////////////////////////////////////////////////////////////////////////
+
+#define WMU_ACTION (WM_USER + 1)
+
+
+LRESULT CALLBACK PopupWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
+
+HWND hPopupWindow = NULL;
+
+
+static LRESULT CALLBACK PopupDlgProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
+static LRESULT CALLBACK DumbPopupDlgProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
+
+
+
+// Functions //////////////////////////////////////////////////////////////////////////////////////
+
+
+// Initializations needed by popups
+void InitPopups()
+{
+ // window needed for popup commands
+ hPopupWindow = CreateWindowEx(WS_EX_TOOLWINDOW, _T("static"), _T(MODULE_NAME) _T("_PopupWindow"),
+ 0, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, HWND_DESKTOP,
+ NULL, hInst, NULL);
+ SetWindowLong(hPopupWindow, GWL_WNDPROC, (LONG)(WNDPROC)PopupWndProc);
+}
+
+
+// Deinitializations needed by popups
+void DeInitPopups()
+{
+}
+
+
+// Show an error popup
+void ShowErrPopup(const TCHAR *description, const TCHAR *title)
+{
+ ShowPopupEx(NULL, title == NULL ? _T(MODULE_NAME) _T(" Error") : title, description,
+ NULL, POPUP_TYPE_ERROR, NULL);
+}
+
+
+void ShowTestPopup(const TCHAR *title, const TCHAR *description, const Options *op)
+{
+ ShowPopupEx(NULL, title, description, NULL, POPUP_TYPE_TEST, op);
+}
+
+
+void ShowPopup(HANDLE hContact, const TCHAR *title, const TCHAR *description)
+{
+ ShowPopupEx(hContact, title, description, hContact, POPUP_TYPE_NORMAL, &opts);
+}
+
+
+// Show an popup
+void ShowPopupEx(HANDLE hContact, const TCHAR *title, const TCHAR *description,
+ void *plugin_data, int type, const Options *op)
+{
+#ifdef UNICODE
+ if(ServiceExists(MS_POPUP_ADDPOPUPW))
+ {
+ // Make popup
+ POPUPDATAW ppd;
+
+ ZeroMemory(&ppd, sizeof(ppd));
+ ppd.lchContact = hContact;
+ ppd.lchIcon = (HICON) LoadImage(GetModuleHandle(NULL),MAKEINTRESOURCE(174),IMAGE_ICON,16,16,LR_DEFAULTCOLOR);
+
+ if (title != NULL)
+ lstrcpyn(ppd.lpwzContactName, title, MAX_REGS(ppd.lpwzContactName));
+ else if (hContact != NULL)
+ lstrcpyn(ppd.lpwzContactName, (TCHAR *) CallService(MS_CLIST_GETCONTACTDISPLAYNAME, (WPARAM)hContact, GCDNF_TCHAR),
+ MAX_REGS(ppd.lpwzContactName));
+
+ if (description != NULL)
+ lstrcpyn(ppd.lpwzText, description, MAX_REGS(ppd.lpwzText));
+
+ if (type == POPUP_TYPE_NORMAL || type == POPUP_TYPE_TEST)
+ {
+ if (op->popup_use_default_colors)
+ {
+ ppd.colorBack = 0;
+ ppd.colorText = 0;
+ }
+ else if (op->popup_use_win_colors)
+ {
+ ppd.colorBack = GetSysColor(COLOR_BTNFACE);
+ ppd.colorText = GetSysColor(COLOR_WINDOWTEXT);
+ }
+ else
+ {
+ ppd.colorBack = op->popup_bkg_color;
+ ppd.colorText = op->popup_text_color;
+ }
+ }
+ else // if (type == POPUP_TYPE_ERROR)
+ {
+ ppd.colorBack = RGB(200,0,0);
+ ppd.colorText = RGB(255,255,255);
+ }
+
+ if (type == POPUP_TYPE_NORMAL)
+ {
+ ppd.PluginWindowProc = PopupDlgProc;
+ ppd.PluginData = plugin_data;
+ }
+ else // if (type == POPUP_TYPE_TEST || type == POPUP_TYPE_ERROR)
+ {
+ ppd.PluginWindowProc = DumbPopupDlgProc;
+ }
+
+ if (type == POPUP_TYPE_NORMAL || type == POPUP_TYPE_TEST)
+ {
+ switch (op->popup_delay_type)
+ {
+ case POPUP_DELAY_CUSTOM:
+ ppd.iSeconds = opts.popup_timeout;
+ break;
+
+ case POPUP_DELAY_PERMANENT:
+ ppd.iSeconds = -1;
+ break;
+
+ //case POPUP_DELAY_DEFAULT:
+ default:
+ ppd.iSeconds = 0;
+ break;
+ }
+ }
+ else // if (type == POPUP_TYPE_ERROR)
+ {
+ ppd.iSeconds = 0;
+ }
+
+ // Now that every field has been filled, we want to see the popup.
+ CallService(MS_POPUP_ADDPOPUPW, (WPARAM)&ppd,0);
+ }
+ else
+#endif
+ if(ServiceExists(MS_POPUP_ADDPOPUPEX))
+ {
+ // Make popup
+ POPUPDATAEX ppd;
+
+ ZeroMemory(&ppd, sizeof(ppd));
+ ppd.lchContact = hContact;
+ ppd.lchIcon = (HICON) LoadImage(GetModuleHandle(NULL),MAKEINTRESOURCE(174),IMAGE_ICON,16,16,LR_DEFAULTCOLOR);
+
+ if (title != NULL)
+ TCHAR_TO_CHAR(ppd.lpzContactName, title);
+ else
+ lstrcpynA(ppd.lpzContactName, (char *) CallService(MS_CLIST_GETCONTACTDISPLAYNAME, (WPARAM)hContact, 0),
+ MAX_REGS(ppd.lpzContactName));
+
+ if (description != NULL)
+ TCHAR_TO_CHAR(ppd.lpzText, description);
+
+ if (type == POPUP_TYPE_NORMAL || type == POPUP_TYPE_TEST)
+ {
+ if (op->popup_use_default_colors)
+ {
+ ppd.colorBack = 0;
+ ppd.colorText = 0;
+ }
+ else if (op->popup_use_win_colors)
+ {
+ ppd.colorBack = GetSysColor(COLOR_BTNFACE);
+ ppd.colorText = GetSysColor(COLOR_WINDOWTEXT);
+ }
+ else
+ {
+ ppd.colorBack = op->popup_bkg_color;
+ ppd.colorText = op->popup_text_color;
+ }
+ }
+ else // if (type == POPUP_TYPE_ERROR)
+ {
+ ppd.colorBack = RGB(200,0,0);
+ ppd.colorText = RGB(255,255,255);
+ }
+
+ if (type == POPUP_TYPE_NORMAL)
+ {
+ ppd.PluginWindowProc = PopupDlgProc;
+ ppd.PluginData = plugin_data;
+ }
+ else // if (type == POPUP_TYPE_TEST || type == POPUP_TYPE_ERROR)
+ {
+ ppd.PluginWindowProc = DumbPopupDlgProc;
+ }
+
+ if (type == POPUP_TYPE_NORMAL || type == POPUP_TYPE_TEST)
+ {
+ switch (op->popup_delay_type)
+ {
+ case POPUP_DELAY_CUSTOM:
+ ppd.iSeconds = opts.popup_timeout;
+ break;
+
+ case POPUP_DELAY_PERMANENT:
+ ppd.iSeconds = -1;
+ break;
+
+ //case POPUP_DELAY_DEFAULT:
+ default:
+ ppd.iSeconds = 0;
+ break;
+ }
+ }
+ else // if (type == POPUP_TYPE_ERROR)
+ {
+ ppd.iSeconds = 0;
+ }
+
+ // Now that every field has been filled, we want to see the popup.
+ CallService(MS_POPUP_ADDPOPUPEX, (WPARAM)&ppd,0);
+ }
+}
+
+
+// Handle to the hidden windows to handle actions for popup clicks
+// wParam has the number of MOTD in case of WMU_SHOW_MOTD_DETAILS
+LRESULT CALLBACK PopupWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ if (uMsg == WMU_ACTION)
+ {
+ if (lParam == POPUP_ACTION_OPENHISTORY)
+ {
+ CallService(MS_HISTORY_SHOWCONTACTHISTORY, wParam, 0);
+ }
+ }
+ return DefWindowProc(hWnd, uMsg, wParam, lParam);
+}
+
+
+// Handle to popup events
+static LRESULT CALLBACK PopupDlgProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ switch(message) {
+ case WM_COMMAND:
+ {
+ SendMessage(hPopupWindow, WMU_ACTION, (WPARAM)PUGetPluginData(hWnd), opts.popup_left_click_action);
+
+ if (opts.popup_left_click_action != POPUP_ACTION_DONOTHING)
+ PUDeletePopUp(hWnd);
+
+ return TRUE;
+ }
+
+ case WM_CONTEXTMENU:
+ {
+ SendMessage(hPopupWindow, WMU_ACTION, (WPARAM)PUGetPluginData(hWnd), opts.popup_right_click_action);
+
+ if (opts.popup_right_click_action != POPUP_ACTION_DONOTHING)
+ PUDeletePopUp(hWnd);
+
+ return TRUE;
+ }
+
+ case UM_FREEPLUGINDATA:
+ {
+ return TRUE;
+ }
+ }
+
+ return DefWindowProc(hWnd, message, wParam, lParam);
+}
+
+
+// Handle to popup events
+static LRESULT CALLBACK DumbPopupDlgProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ switch(message) {
+ case WM_COMMAND:
+ {
+ PUDeletePopUp(hWnd);
+ return TRUE;
+ }
+
+ case WM_CONTEXTMENU:
+ {
+ PUDeletePopUp(hWnd);
+ return TRUE;
+ }
+
+ case UM_FREEPLUGINDATA:
+ {
+ return TRUE;
+ }
+ }
+
+ return DefWindowProc(hWnd, message, wParam, lParam);
+}
+
diff --git a/Plugins/nickhistory/popup.h b/Plugins/nickhistory/popup.h
new file mode 100644
index 0000000..090657e
--- /dev/null
+++ b/Plugins/nickhistory/popup.h
@@ -0,0 +1,60 @@
+/*
+Copyright (C) 2005 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#ifndef __POPUP_H__
+# define __POPUP_H__
+
+#include "commons.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+// Initializations needed by popups
+void InitPopups();
+
+// Deinitializations needed by popups
+void DeInitPopups();
+
+
+#define POPUP_TYPE_NORMAL 0
+#define POPUP_TYPE_TEST 1
+#define POPUP_TYPE_ERROR 2
+
+// Show an popup
+void ShowPopup(HANDLE hContact, const TCHAR *title, const TCHAR *description);
+
+// Show an test
+void ShowTestPopup(const TCHAR *title, const TCHAR *description, const Options *op);
+
+// Show an error popup
+void ShowErrPopup(const char *description, const char *title = NULL);
+
+void ShowPopupEx(HANDLE hContact, const TCHAR *title, const TCHAR *description,
+ void *plugin_data, int type, const Options *op);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif // __POPUP_H__
diff --git a/Plugins/nickhistory/resource.h b/Plugins/nickhistory/resource.h
new file mode 100644
index 0000000..dda44d5
--- /dev/null
+++ b/Plugins/nickhistory/resource.h
@@ -0,0 +1,55 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Developer Studio generated include file.
+// Used by resource.rc
+//
+#define IDD_OPTIONS 119
+#define IDD_POPUPS 120
+#define IDC_DELAY 1001
+#define IDC_WINCOLORS 1002
+#define IDC_DEFAULTCOLORS 1003
+#define IDC_BGCOLOR 1004
+#define IDC_TEXTCOLOR 1005
+#define IDC_PREV 1006
+#define IDC_DELAYFROMPU 1007
+#define IDC_DELAYCUSTOM 1008
+#define IDC_DELAYPERMANENT 1009
+#define IDC_RIGHT_ACTION 1022
+#define IDC_LEFT_ACTION 1023
+#define IDC_PROTOCOLS 1041
+#define IDC_CHANGED 1058
+#define IDC_REMOVED 1059
+#define IDC_CHECK1 1060
+#define IDC_POPUPS 1060
+#define IDC_CHECK2 1061
+#define IDC_DELAY_SPIN 1061
+#define IDC_HISTORY 1061
+#define IDC_ANSI 1062
+#define IDC_TRACK_G 1063
+#define IDC_CHANGED_L 1064
+#define IDC_REMOVED_L 1065
+#define IDC_PROTOCOLS_G 1066
+#define IDC_PROTOCOLS_L 1067
+#define IDC_COLOURS_G 1068
+#define IDC_TRACK_CHANGE 1068
+#define IDC_BGCOLOR_L 1069
+#define IDC_TRACK_REMOVE 1069
+#define IDC_TEXTCOLOR_L 1070
+#define IDC_ONLY_NOT_OFFLINE 1070
+#define IDC_DELAY_G 1071
+#define IDC_ACTIONS_G 1072
+#define IDC_RIGHT_ACTION_L 1073
+#define IDC_LEFT_ACTION_L 1074
+#define IDC_STATIC -1
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NO_MFC 1
+#define _APS_3D_CONTROLS 1
+#define _APS_NEXT_RESOURCE_VALUE 120
+#define _APS_NEXT_COMMAND_VALUE 40004
+#define _APS_NEXT_CONTROL_VALUE 1075
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/Plugins/nickhistory/resource.rc b/Plugins/nickhistory/resource.rc
new file mode 100644
index 0000000..ec41f93
--- /dev/null
+++ b/Plugins/nickhistory/resource.rc
@@ -0,0 +1,180 @@
+//Microsoft Developer Studio generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "resource.h"
+#include "winresrc.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (U.S.) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page(1252)
+#endif //_WIN32
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Dialog
+//
+
+IDD_OPTIONS DIALOG DISCARDABLE 0, 0, 314, 240
+STYLE DS_SHELLFONT | WS_CHILD | WS_VISIBLE
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg"
+BEGIN
+ GROUPBOX " History ",IDC_STATIC,3,3,308,44
+ CONTROL "Log changes to history",IDC_HISTORY,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,12,16,291,11
+ CONTROL "Don't save Unicode parts for 7bit ANSI history (saves db space)",
+ IDC_ANSI,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,30,291,
+ 11
+ GROUPBOX " Tracking ",IDC_TRACK_G,3,51,308,90
+ CONTROL "Track when contacts change their nicknames",
+ IDC_TRACK_CHANGE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,
+ 12,63,291,11
+ RTEXT "Template:",IDC_CHANGED_L,11,76,55,10
+ EDITTEXT IDC_CHANGED,72,75,233,13,ES_AUTOHSCROLL
+ CONTROL "Track when contacts remove their nicknames",
+ IDC_TRACK_REMOVE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,
+ 12,92,291,11
+ RTEXT "Template:",IDC_REMOVED_L,11,106,55,10
+ EDITTEXT IDC_REMOVED,72,105,233,13,ES_AUTOHSCROLL
+ CONTROL "Only when the contact is not offline",
+ IDC_ONLY_NOT_OFFLINE,"Button",BS_AUTOCHECKBOX |
+ WS_TABSTOP,12,123,291,11
+ GROUPBOX " Protocols ",IDC_PROTOCOLS_G,3,144,308,93
+ LTEXT "Enable tracking for these protocols:",
+ IDC_PROTOCOLS_L,13,156,291,11
+ CONTROL "List1",IDC_PROTOCOLS,"SysListView32",LVS_REPORT |
+ LVS_SINGLESEL | LVS_SORTASCENDING | LVS_NOCOLUMNHEADER |
+ LVS_NOSORTHEADER | WS_BORDER | WS_TABSTOP,12,168,158,62
+END
+
+IDD_POPUPS DIALOG DISCARDABLE 0, 0, 314, 240
+STYLE DS_SHELLFONT | WS_CHILD | WS_VISIBLE
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg"
+BEGIN
+ CONTROL "Enable popups",IDC_POPUPS,"Button",BS_AUTOCHECKBOX |
+ WS_TABSTOP,3,3,308,12
+ GROUPBOX " Colours ",IDC_COLOURS_G,3,25,158,75
+ CONTROL "",IDC_BGCOLOR,"ColourPicker",WS_TABSTOP,11,37,35,14
+ LTEXT "Background colour",IDC_BGCOLOR_L,55,41,66,8
+ CONTROL "",IDC_TEXTCOLOR,"ColourPicker",WS_TABSTOP,11,55,35,14
+ LTEXT "Text colour",IDC_TEXTCOLOR_L,55,59,66,8
+ CONTROL "Use Windows colours",IDC_WINCOLORS,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,11,74,108,10
+ CONTROL "Use default colours",IDC_DEFAULTCOLORS,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,11,84,107,10
+ GROUPBOX " Delay ",IDC_DELAY_G,166,25,145,75
+ CONTROL "From popup plugin",IDC_DELAYFROMPU,"Button",
+ BS_AUTORADIOBUTTON | WS_GROUP,175,38,122,10
+ CONTROL "Custom",IDC_DELAYCUSTOM,"Button",BS_AUTORADIOBUTTON,175,
+ 52,54,10
+ CONTROL "Permanent",IDC_DELAYPERMANENT,"Button",
+ BS_AUTORADIOBUTTON,175,66,122,10
+ EDITTEXT IDC_DELAY,233,50,31,14,ES_AUTOHSCROLL | ES_NUMBER
+ CONTROL "Spin1",IDC_DELAY_SPIN,"msctls_updown32",UDS_SETBUDDYINT |
+ UDS_ALIGNRIGHT | UDS_ARROWKEYS | UDS_NOTHOUSANDS |
+ UDS_HOTTRACK,268,51,11,11
+ GROUPBOX " Actions ",IDC_ACTIONS_G,3,103,308,47
+ RTEXT "On right click:",IDC_RIGHT_ACTION_L,13,118,62,9
+ COMBOBOX IDC_RIGHT_ACTION,83,116,156,60,CBS_DROPDOWNLIST |
+ WS_VSCROLL | WS_TABSTOP
+ RTEXT "On left click:",IDC_LEFT_ACTION_L,13,132,62,9
+ COMBOBOX IDC_LEFT_ACTION,83,132,156,60,CBS_DROPDOWNLIST |
+ WS_VSCROLL | WS_TABSTOP
+ PUSHBUTTON "Preview",IDC_PREV,131,161,50,14
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// DESIGNINFO
+//
+
+#ifdef APSTUDIO_INVOKED
+GUIDELINES DESIGNINFO DISCARDABLE
+BEGIN
+ IDD_OPTIONS, DIALOG
+ BEGIN
+ LEFTMARGIN, 3
+ RIGHTMARGIN, 311
+ TOPMARGIN, 3
+ BOTTOMMARGIN, 237
+ END
+
+ IDD_POPUPS, DIALOG
+ BEGIN
+ LEFTMARGIN, 3
+ RIGHTMARGIN, 311
+ TOPMARGIN, 3
+ BOTTOMMARGIN, 237
+ END
+END
+#endif // APSTUDIO_INVOKED
+
+#endif // English (U.S.) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+/////////////////////////////////////////////////////////////////////////////
+// English (Canada) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENC)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_CAN
+#pragma code_page(1252)
+#endif //_WIN32
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "resource.h\0"
+END
+
+2 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "#include ""resource.h""\r\n"
+ "#include ""winresrc.h""\r\n"
+ "\0"
+END
+
+3 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "\r\n"
+ "\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+#endif // English (Canada) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
+
diff --git a/Plugins/nickhistory/sdk/m_metacontacts.h b/Plugins/nickhistory/sdk/m_metacontacts.h
new file mode 100644
index 0000000..1da12b9
--- /dev/null
+++ b/Plugins/nickhistory/sdk/m_metacontacts.h
@@ -0,0 +1,162 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright © 2004 Universite Louis PASTEUR, STRASBOURG.
+Copyright © 2004 Scott Ellis (www.scottellis.com.au mail@scottellis.com.au)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef M_METACONTACTS_H__
+#define M_METACONTACTS_H__ 1
+
+//get the handle for a contact's parent metacontact
+//wParam=(HANDLE)hSubContact
+//lParam=0
+//returns a handle to the parent metacontact, or null if this contact is not a subcontact
+#define MS_MC_GETMETACONTACT "MetaContacts/GetMeta"
+
+//gets the handle for the default contact
+//wParam=(HANDLE)hMetaContact
+//lParam=0
+//returns a handle to the default contact, or null on failure
+#define MS_MC_GETDEFAULTCONTACT "MetaContacts/GetDefault"
+
+//gets the contact number for the default contact
+//wParam=(HANDLE)hMetaContact
+//lParam=0
+//returns a DWORD contact number, or -1 on failure
+#define MS_MC_GETDEFAULTCONTACTNUM "MetaContacts/GetDefaultNum"
+
+//gets the handle for the 'most online' contact
+//wParam=(HANDLE)hMetaContact
+//lParam=0
+//returns a handle to the 'most online' contact
+#define MS_MC_GETMOSTONLINECONTACT "MetaContacts/GetMostOnline"
+
+//gets the number of subcontacts for a metacontact
+//wParam=(HANDLE)hMetaContact
+//lParam=0
+//returns a DWORD representing the number of subcontacts for the given metacontact
+#define MS_MC_GETNUMCONTACTS "MetaContacts/GetNumContacts"
+
+//gets the handle of a subcontact, using the subcontact's number
+//wParam=(HANDLE)hMetaContact
+//lParam=(DWORD)contact number
+//returns a handle to the specified subcontact
+#define MS_MC_GETSUBCONTACT "MetaContacts/GetSubContact"
+
+//sets the default contact, using the subcontact's contact number
+//wParam=(HANDLE)hMetaContact
+//lParam=(DWORD)contact number
+//returns 0 on success
+#define MS_MC_SETDEFAULTCONTACTNUM "MetaContacts/SetDefault"
+
+//sets the default contact, using the subcontact's handle
+//wParam=(HANDLE)hMetaContact
+//lParam=(HANDLE)hSubcontact
+//returns 0 on success
+#define MS_MC_SETDEFAULTCONTACT "MetaContacts/SetDefaultByHandle"
+
+//forces the metacontact to send using a specific subcontact, using the subcontact's contact number
+//wParam=(HANDLE)hMetaContact
+//lParam=(DWORD)contact number
+//returns 0 on success
+#define MS_MC_FORCESENDCONTACTNUM "MetaContacts/ForceSendContact"
+
+//forces the metacontact to send using a specific subcontact, using the subcontact's handle
+//wParam=(HANDLE)hMetaContact
+//lParam=(HANDLE)hSubcontact
+//returns 0 on success (will fail if 'force default' is in effect)
+#define MS_MC_FORCESENDCONTACT "MetaContacts/ForceSendContactByHandle"
+
+//'unforces' the metacontact to send using a specific subcontact
+//wParam=(HANDLE)hMetaContact
+//lParam=0
+//returns 0 on success (will fail if 'force default' is in effect)
+#define MS_MC_UNFORCESENDCONTACT "MetaContacts/UnforceSendContact"
+
+//'forces' or 'unforces' (i.e. toggles) the metacontact to send using it's default contact
+// overrides (and clears) 'force send' above, and will even force use of offline contacts
+// will send ME_MC_FORCESEND or ME_MC_UNFORCESEND event
+//wParam=(HANDLE)hMetaContact
+//lParam=0
+//returns 1(true) or 0(false) representing new state of 'force default'
+#define MS_MC_FORCEDEFAULT "MetaContacts/ForceSendDefault"
+
+// method to get state of 'force' for a metacontact
+// wParam=(HANDLE)hMetaContact
+// lParam= (DWORD)&contact_number or NULL
+//
+// if lparam supplied, the contact_number of the contatct 'in force' will be copied to the address it points to,
+// or if none is in force, the value (DWORD)-1 will be copied
+// (v0.8.0.8+ returns 1 if 'force default' is true with *lParam == default contact number, else returns 0 with *lParam as above)
+#define MS_MC_GETFORCESTATE "MetaContacts/GetForceState"
+
+// fired when a metacontact's default contact changes (fired upon creation of metacontact also, when default is initially set)
+// wParam=(HANDLE)hMetaContact
+// lParam=(HANDLE)hDefaultContact
+#define ME_MC_DEFAULTTCHANGED "MetaContacts/DefaultChanged"
+
+// fired when a metacontact's subcontacts change (fired upon creation of metacontact, when contacts are added or removed, and when
+// contacts are reordered) - a signal to re-read metacontact data
+// wParam=(HANDLE)hMetaContact
+// lParam=0
+#define ME_MC_SUBCONTACTSCHANGED "MetaContacts/SubcontactsChanged"
+
+// fired when a metacontact is forced to send using a specific subcontact
+// wParam=(HANDLE)hMetaContact
+// lParam=(HANDLE)hForceContact
+#define ME_MC_FORCESEND "MetaContacts/ForceSend"
+
+// fired when a metacontact is 'unforced' to send using a specific subcontact
+// wParam=(HANDLE)hMetaContact
+// lParam=0
+#define ME_MC_UNFORCESEND "MetaContacts/UnforceSend"
+
+// method to get protocol name - used to be sure you're dealing with a "real" metacontacts plugin :)
+// wParam=lParam=0
+#define MS_MC_GETPROTOCOLNAME "MetaContacts/GetProtoName"
+
+
+// added 0.9.5.0 (22/3/05)
+// wParam=(HANDLE)hContact
+// lParam=0
+// convert a given contact into a metacontact
+#define MS_MC_CONVERTTOMETA "MetaContacts/ConvertToMetacontact"
+
+// added 0.9.5.0 (22/3/05)
+// wParam=(HANDLE)hContact
+// lParam=(HANDLE)hMeta
+// add an existing contact to a metacontact
+#define MS_MC_ADDTOMETA "MetaContacts/AddToMetacontact"
+
+// added 0.9.5.0 (22/3/05)
+// wParam=0
+// lParam=(HANDLE)hContact
+// remove a contact from a metacontact
+#define MS_MC_REMOVEFROMMETA "MetaContacts/RemoveFromMetacontact"
+
+
+// added 0.9.13.2 (6/10/05)
+// wParam=(BOOL)disable
+// lParam=0
+// enable/disable the 'hidden group hack' - for clists that support subcontact hiding using 'IsSubcontact' setting
+// should be called once in the clist 'onmodulesloaded' event handler (which, since it's loaded after the db, will be called
+// before the metacontact onmodulesloaded handler where the subcontact hiding is usually done)
+#define MS_MC_DISABLEHIDDENGROUP "MetaContacts/DisableHiddenGroup"
+
+#endif
diff --git a/Plugins/nickhistory/sdk/m_updater.h b/Plugins/nickhistory/sdk/m_updater.h
new file mode 100644
index 0000000..371b743
--- /dev/null
+++ b/Plugins/nickhistory/sdk/m_updater.h
@@ -0,0 +1,146 @@
+#ifndef _M_UPDATER_H
+#define _M_UPDATER_H
+
+// NOTES:
+// - For langpack updates, include a string of the following format in the langpack text file:
+// ";FLID: <file listing name> <version>"
+// version must be four numbers seperated by '.', in the range 0-255 inclusive
+// - Updater will disable plugins that are downloaded but were not active prior to the update (this is so that, if an archive contains e.g. ansi and
+// unicode versions, the correct plugin will be the only one active after the new version is installed)...so if you add a support plugin, you may need
+// to install an ini file to make the plugin activate when miranda restarts after the update
+// - Updater will replace all dlls that have the same internal shortName as a downloaded update dll (this is so that msn1.dll and msn2.dll, for example,
+// will both be updated) - so if you have a unicode and a non-unicode version of a plugin in your archive, you should make the internal names different (which will break automatic
+// updates from the file listing if there is only one file listing entry for both versions, unless you use the 'MS_UPDATE_REGISTER' service below)
+// - Updater will install all files in the root of the archive into the plugins folder, except for langpack files that contain the FLID string which go into the root folder (same
+// folder as miranda32.exe)...all folders in the archive will also be copied to miranda's root folder, and their contents transferred into the new folders. The only exception is a
+// special folder called 'root_files' - if there is a folder by that name in the archive, it's contents will also be copied into miranda's root folder - this is intended to be used
+// to install additional dlls etc that a plugin may require)
+
+// if you set Update.szUpdateURL to the following value when registering, as well as setting your beta site and version data,
+// Updater will ignore szVersionURL and pbVersionPrefix, and attempt to find the file listing URL's from the backend XML data.
+// for this to work, the plugin name in pluginInfo.shortName must match the file listing exactly (except for case)
+#define UPDATER_AUTOREGISTER "UpdaterAUTOREGISTER"
+// Updater will also use the backend xml data if you provide URL's that reference the miranda file listing for updates (so you can use that method
+// if e.g. your plugin shortName does not match the file listing) - it will grab the file listing id from the end of these URLs
+
+typedef struct Update_tag {
+ int cbSize;
+ char *szComponentName; // component name as it will appear in the UI (will be translated before displaying)
+
+ char *szVersionURL; // URL where the current version can be found (NULL to disable)
+ BYTE *pbVersionPrefix; // bytes occuring in VersionURL before the version, used to locate the version information within the URL data
+ // (note that this URL could point at a binary file - dunno why, but it could :)
+ int cpbVersionPrefix; // number of bytes pointed to by pbVersionPrefix
+ char *szUpdateURL; // URL where dll/zip is located
+ // set to UPDATER_AUTOREGISTER if you want Updater to find the file listing URLs (ensure plugin shortName matches file listing!)
+
+ char *szBetaVersionURL; // URL where the beta version can be found (NULL to disable betas)
+ BYTE *pbBetaVersionPrefix; // bytes occuring in VersionURL before the version, used to locate the version information within the URL data
+ int cpbBetaVersionPrefix; // number of bytes pointed to by pbVersionPrefix
+ char *szBetaUpdateURL; // URL where dll/zip is located
+
+ BYTE *pbVersion; // bytes of current version, used for comparison with those in VersionURL
+ int cpbVersion; // number of bytes pointed to by pbVersion
+
+ char *szBetaChangelogURL; // url for displaying changelog for beta versions
+} Update;
+
+// register a comonent with Updater
+//
+// wparam = 0
+// lparam = (LPARAM)&Update
+#define MS_UPDATE_REGISTER "Update/Register"
+
+// utility functions to create a version string from a DWORD or from pluginInfo
+// point buf at a buffer at least 16 chars wide - but note the version string returned may be shorter
+//
+__inline static char *CreateVersionString(DWORD version, char *buf) {
+ mir_snprintf(buf, 16, "%d.%d.%d.%d", (version >> 24) & 0xFF, (version >> 16) & 0xFF, (version >> 8) & 0xFF, version & 0xFF);
+ return buf;
+}
+
+__inline static char *CreateVersionStringPlugin(PLUGININFO *pluginInfo, char *buf) {
+ return CreateVersionString(pluginInfo->version, buf);
+}
+
+
+// register the 'easy' way - use this method if you have no beta URL and the plugin is on the miranda file listing
+// NOTE: the plugin version string on the file listing must be the string version of the version in pluginInfo (i.e. 0.0.0.1,
+// four numbers between 0 and 255 inclusivem, so no letters, brackets, etc.)
+//
+// wParam = (int)fileID - this is the file ID from the file listing (i.e. the number at the end of the download link)
+// lParam = (PLUGININFO*)&pluginInfo
+#define MS_UPDATE_REGISTERFL "Update/RegisterFL"
+
+// this function can be used to 'unregister' components - useful for plugins that register non-plugin/langpack components and
+// may need to change those components on the fly
+// lParam = (char *)szComponentName
+#define MS_UPDATE_UNREGISTER "Update/Unregister"
+
+// this event is fired when the startup process is complete, but NOT if a restart is imminent
+// it is designed for status managment plugins to use as a trigger for beggining their own startup process
+// wParam = lParam = 0 (unused)
+// (added in version 0.1.6.0)
+#define ME_UPDATE_STARTUPDONE "Update/StartupDone"
+
+// this service can be used to enable/disable Updater's global status control
+// it can be called from the StartupDone event handler
+// wParam = (BOOL)enable
+// lParam = 0
+// (added in version 0.1.6.0)
+#define MS_UPDATE_ENABLESTATUSCONTROL "Update/EnableStatusControl"
+
+// An description of usage of the above service and event:
+// Say you are a status control plugin that normally sets protocol or global statuses in your ModulesLoaded event handler.
+// In order to make yourself 'Updater compatible', you would move the status control code from ModulesLoaded to another function,
+// say DoStartup. Then, in ModulesLoaded you would check for the existence of the MS_UPDATE_ENABLESTATUSCONTROL service.
+// If it does not exist, call DoStartup. If it does exist, hook the ME_UPDATE_STARTUPDONE event and call DoStartup from there. You may
+// also wish to call MS_UPDATE_ENABLESTATUSCONTROL with wParam == FALSE at this time, to disable Updater's own status control feature.
+
+// this service can be used to determine whether updates are possible for a component with the given name
+// wParam = 0
+// lParam = (char *)szComponentName
+// returns TRUE if updates are supported, FALSE otherwise
+#define MS_UPDATE_ISUPDATESUPPORTED "Update/IsUpdateSupported"
+
+#endif
+
+
+/////////////// Usage Example ///////////////
+
+#ifdef EXAMPLE_CODE
+
+// you need to #include "m_updater.h" and HookEvent(ME_SYSTEM_MODULESLOADED, OnModulesLoaded) in your Load function...
+
+int OnModulesLoaded(WPARAM wParam, LPARAM lParam) {
+
+ Update update = {0}; // for c you'd use memset or ZeroMemory...
+ char szVersion[16];
+
+ update.cbSize = sizeof(Update);
+
+ update.szComponentName = pluginInfo.shortName;
+ update.pbVersion = (BYTE *)CreateVersionString(&pluginInfo, szVersion);
+ update.cpbVersion = strlen((char *)update.pbVersion);
+
+ // these are the three lines that matter - the archive, the page containing the version string, and the text (or data)
+ // before the version that we use to locate it on the page
+ // (note that if the update URL and the version URL point to standard file listing entries, the backend xml
+ // data will be used to check for updates rather than the actual web page - this is not true for beta urls)
+ update.szUpdateURL = "http://scottellis.com.au:81/test/updater.zip";
+ update.szVersionURL = "http://scottellis.com.au:81/test/updater_test.html";
+ update.pbVersionPrefix = (BYTE *)"Updater version ";
+
+ update.cpbVersionPrefix = strlen((char *)update.pbVersionPrefix);
+
+ // do the same for the beta versions of the above struct members if you wish to allow beta updates from another URL
+
+ CallService(MS_UPDATE_REGISTER, 0, (WPARAM)&update);
+
+ // Alternatively, to register a plugin with e.g. file ID 2254 on the file listing...
+ // CallService(MS_UPDATE_REGISTERFL, (WPARAM)2254, (LPARAM)&pluginInfo);
+
+ return 0;
+}
+
+#endif
diff --git a/Plugins/notification_history/commons.h b/Plugins/notification_history/commons.h
new file mode 100644
index 0000000..ce99922
--- /dev/null
+++ b/Plugins/notification_history/commons.h
@@ -0,0 +1,76 @@
+/*
+Copyright (C) 2005 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#ifndef __COMMONS_H__
+# define __COMMONS_H__
+
+
+#include <windows.h>
+#include <stdio.h>
+#include <time.h>
+#include <win2k.h>
+#include <tchar.h>
+#include <stdio.h>
+
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+
+#include <newpluginapi.h>
+#include <m_system.h>
+#include <m_plugins.h>
+#include <m_options.h>
+#include <m_langpack.h>
+#include <m_database.h>
+#include <m_utils.h>
+#include <m_protocols.h>
+
+#include <m_notify.h>
+#include <m_metacontacts.h>
+
+#include "resource.h"
+
+
+#define MODULE_NAME _T("NotificationHistory")
+
+extern HINSTANCE hInst;
+extern PLUGINLINK *pluginLink;
+extern PLUGININFO pluginInfo;
+
+#include "m_notification_history.h"
+#include "options.h"
+#include "../utils/templates.h"
+#include "../utils/mir_memory.h"
+#include "../utils/mir_options_notify.h"
+
+
+#define MAX_REGS(_A_) ( sizeof(_A_) / sizeof(_A_[0]) )
+
+
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // __COMMONS_H__
diff --git a/Plugins/notification_history/m_notification_history.h b/Plugins/notification_history/m_notification_history.h
new file mode 100644
index 0000000..d694b56
--- /dev/null
+++ b/Plugins/notification_history/m_notification_history.h
@@ -0,0 +1,70 @@
+/*
+Copyright (C) 2006 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#ifndef __M_NOTIFICATION_HISTORY_H__
+# define __M_NOTIFICATION_HISTORY_H__
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+
+#define NFOPT_HISTORY_TEMPLATE_SYSTEM "History/SystemTemplate" // const char *
+#define NFOPT_HISTORY_TEMPLATE_CONTACT "History/ContactTemplate" // const char *
+
+#define NFOPT_HISTORY_TEMPLATE_SYSTEMW "History/SystemTemplateW" // const WCHAR *
+#define NFOPT_HISTORY_TEMPLATE_CONTACTW "History/ContactTemplateW" // const WCHAR *
+
+#define NFOPT_HISTORY_SYSTEM_LOG "History/SystemLog" // BYTE
+#define NFOPT_HISTORY_SYSTEM_MARK_READ "History/SystemMarkRead" // BYTE
+#define NFOPT_HISTORY_CONTACT_LOG "History/ContactLog" // BYTE
+#define NFOPT_HISTORY_CONTACT_MARK_READ "History/ContactMarkRead" // BYTE
+#define NFOPT_HISTORY_EVENTTYPE "History/EventType" // WORD
+
+
+#define MS_HISTORY_SHOW "History/Show"
+
+
+// Default event type
+#define EVENTTYPE_NOTIFICATION 16065
+
+
+
+
+#ifdef _UNICODE
+
+# define NFOPT_HISTORY_TEMPLATE_SYSTEMT NFOPT_HISTORY_TEMPLATE_SYSTEMW
+# define NFOPT_HISTORY_TEMPLATE_CONTACTT NFOPT_HISTORY_TEMPLATE_CONTACTW
+
+#else
+
+# define NFOPT_HISTORY_TEMPLATE_SYSTEMT NFOPT_HISTORY_TEMPLATE_SYSTEM
+# define NFOPT_HISTORY_TEMPLATE_CONTACTT NFOPT_HISTORY_TEMPLATE_CONTACT
+
+#endif
+
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // __M_NOTIFICATION_HISTORY_H__
diff --git a/Plugins/notification_history/notification_history.cpp b/Plugins/notification_history/notification_history.cpp
new file mode 100644
index 0000000..7bc37dd
--- /dev/null
+++ b/Plugins/notification_history/notification_history.cpp
@@ -0,0 +1,219 @@
+/*
+Copyright (C) 2005 Ricardo Pescuma Domenecci
+Based on work by tweety, nullbyte
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#include "notification_history.h"
+
+
+
+// Prototypes /////////////////////////////////////////////////////////////////////////////////////
+
+
+
+HINSTANCE hInst;
+PLUGINLINK *pluginLink;
+MNOTIFYLINK *notifyLink;
+
+PLUGININFO pluginInfo = {
+ sizeof(PLUGININFO),
+ "History Notification",
+ PLUGIN_MAKE_VERSION(0,0,0,1),
+ "Notification type that log notifications to system/contacts history",
+ "Ricardo Pescuma Domenecci",
+ "",
+ "© 2006 Ricardo Pescuma Domenecci",
+ "http://www.miranda-im.org/",
+ 0, //not transient
+ 0 //doesn't replace anything built-in
+};
+
+
+HANDLE hhkNotificationShow = NULL;
+HANDLE hhkModulesLoaded = NULL;
+
+
+int ModulesLoaded(WPARAM wParam,LPARAM lParam);
+void LoadNotifyImp();
+void UnloadNotifyImp();
+
+int HistoryShow(WPARAM wParam, LPARAM lParam);
+
+
+
+// Functions //////////////////////////////////////////////////////////////////////////////////////
+
+
+BOOL WINAPI DllMain(HINSTANCE hinstDLL,DWORD fdwReason,LPVOID lpvReserved)
+{
+ hInst = hinstDLL;
+ return TRUE;
+}
+
+
+__declspec(dllexport) PLUGININFO* MirandaPluginInfo(DWORD mirandaVersion)
+{
+ return &pluginInfo;
+}
+
+
+int __declspec(dllexport) Load(PLUGINLINK *link)
+{
+ // Copy data
+ pluginLink = link;
+
+ init_mir_malloc();
+
+ hhkModulesLoaded = HookEvent(ME_SYSTEM_MODULESLOADED, ModulesLoaded);
+
+ return 0;
+}
+
+
+int __declspec(dllexport) Unload(void)
+{
+ UnloadNotifyImp();
+ UnhookEvent(hhkModulesLoaded);
+ return 0;
+}
+
+
+int ModulesLoaded(WPARAM wParam,LPARAM lParam)
+{
+ MNotifyGetLink();
+ LoadNotifyImp();
+ InitOptions();
+ return 0;
+}
+
+
+
+
+
+void LoadNotifyImp()
+{
+ hhkNotificationShow = HookEvent(ME_NOTIFY_SHOW, HistoryShow);
+
+ CreateServiceFunction(MS_HISTORY_SHOW, HistoryShow);
+}
+
+
+void UnloadNotifyImp()
+{
+ UnhookEvent(hhkNotificationShow);
+}
+
+
+void HistoryLog(HANDLE hNotify, HANDLE hContact, BOOL log, BOOL read)
+{
+ if (log)
+ {
+ TCHAR def[1024];
+ mir_sntprintf(def, MAX_REGS(def), _T("%s\r\n%s"),
+ MNotifyGetTTemplate(hNotify, NFOPT_DEFTEMPL_TITLET, _T("%title%")),
+ MNotifyGetTTemplate(hNotify, NFOPT_DEFTEMPL_TEXTT, _T("%text%")));
+
+ // Get text
+ TCHAR *log_text;
+ if (hContact != NULL)
+ {
+ log_text = MNotifyGetTParsedTemplate(hNotify, NFOPT_HISTORY_TEMPLATE_CONTACTT, def);
+ }
+ else
+ {
+ log_text = MNotifyGetTParsedTemplate(hNotify, NFOPT_HISTORY_TEMPLATE_SYSTEMT, def);
+ }
+
+ if (log_text != NULL)
+ {
+ DBEVENTINFO event = { 0 };;
+
+ event.cbSize = sizeof(event);
+
+#ifdef _UNICODE
+ size_t size = lstrlenW(log_text) + 1;
+ BYTE *tmp = (BYTE *) mir_alloc0(size * 3);
+
+ WideCharToMultiByte(CP_ACP, 0, log_text, -1, (char *) tmp, size, NULL, NULL);
+
+ lstrcpynW((WCHAR *) &tmp[size], log_text, size);
+
+ event.pBlob = tmp;
+ event.cbBlob = size * 3;
+#else
+ event.pBlob = (PBYTE) log_text;
+ event.cbBlob = strlen(log_text) + 1;
+#endif
+
+ event.eventType = MNotifyGetWord(hNotify, NFOPT_HISTORY_EVENTTYPE, EVENTTYPE_NOTIFICATION);
+ event.flags = read ? DBEF_READ : 0;
+ event.timestamp = (DWORD) time(NULL);
+
+ event.szModule = (char *) MNotifyGetTString(hNotify, NFOPT_TYPENAME, MODULE_NAME);
+
+ if (hContact != NULL)
+ {
+ HANDLE hMetaContact = NULL;
+
+ //event.szModule = (char *) CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0);
+
+ if (ServiceExists(MS_MC_GETMETACONTACT)) //try to retrieve the metacontact if some
+ hMetaContact = (HANDLE) CallService(MS_MC_GETMETACONTACT, (WPARAM)hContact, 0);
+
+ if (hMetaContact != NULL) //metacontact
+ {
+ CallService(MS_DB_EVENT_ADD,(WPARAM)hMetaContact,(LPARAM)&event);
+ event.flags = DBEF_READ;
+ }
+ }
+
+ CallService(MS_DB_EVENT_ADD,(WPARAM)hContact,(LPARAM)&event);
+
+#ifdef _UNICODE
+ mir_free(tmp);
+#endif
+ mir_free(log_text);
+ }
+ }
+}
+
+
+int HistoryShow(WPARAM wParam, LPARAM lParam)
+{
+ HANDLE hNotify = (HANDLE)lParam;
+
+ HANDLE hContact = (HANDLE)MNotifyGetDWord(hNotify, NFOPT_CONTACT, 0);
+ if (hContact == NULL)
+ {
+ // Log to System
+ HistoryLog( hNotify, NULL,
+ MNotifyGetByte(hNotify, NFOPT_HISTORY_SYSTEM_LOG, 0),
+ MNotifyGetByte(hNotify, NFOPT_HISTORY_SYSTEM_MARK_READ, 1));
+
+ }
+ else
+ {
+ // Log to Contact
+ HistoryLog( hNotify, hContact,
+ MNotifyGetByte(hNotify, NFOPT_HISTORY_CONTACT_LOG, 0),
+ MNotifyGetByte(hNotify, NFOPT_HISTORY_CONTACT_MARK_READ, 1));
+ }
+
+ return 0;
+}
diff --git a/Plugins/notification_history/notification_history.dsp b/Plugins/notification_history/notification_history.dsp
new file mode 100644
index 0000000..85ae40a
--- /dev/null
+++ b/Plugins/notification_history/notification_history.dsp
@@ -0,0 +1,213 @@
+# Microsoft Developer Studio Project File - Name="notification_history" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=notification_history - Win32 Debug Unicode
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "notification_history.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "notification_history.mak" CFG="notification_history - Win32 Debug Unicode"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "notification_history - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "notification_history - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "notification_history - Win32 Debug Unicode" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "notification_history - Win32 Release Unicode" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "notification_history - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "NOTIFICATION_HISTORY_EXPORTS" /YX /FD /c
+# ADD CPP /nologo /MT /W3 /GX /O2 /I "../../include" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "NOTIFICATION_HISTORY_EXPORTS" /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x809 /d "NDEBUG"
+# ADD RSC /l 0x809 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 /out:"../../bin/release/plugins/notification_history.dll"
+
+!ELSEIF "$(CFG)" == "notification_history - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "NOTIFICATION_HISTORY_EXPORTS" /YX /FD /GZ /c
+# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "../../include" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "NOTIFICATION_HISTORY_EXPORTS" /FR /YX /FD /GZ /c
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x809 /d "_DEBUG"
+# ADD RSC /l 0x809 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /out:"../../bin/debug/plugins/notification_history.dll" /pdbtype:sept
+
+!ELSEIF "$(CFG)" == "notification_history - Win32 Debug Unicode"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "notification_history___Win32_Debug_Unicode"
+# PROP BASE Intermediate_Dir "notification_history___Win32_Debug_Unicode"
+# PROP BASE Ignore_Export_Lib 0
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug_Unicode"
+# PROP Intermediate_Dir "Debug_Unicode"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "../../include" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "NOTIFICATION_HISTORY_EXPORTS" /FR /YX /FD /GZ /c
+# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "../../include" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_UNICODE" /D "UNICODE" /D "_USRDLL" /D "NOTIFICATION_HISTORY_EXPORTS" /FR /YX /FD /GZ /c
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x809 /d "_DEBUG"
+# ADD RSC /l 0x809 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /out:"../../bin/debug/plugins/notification_history.dll" /pdbtype:sept
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /out:"../../bin/debug/plugins/notification_historyW.dll" /pdbtype:sept
+
+!ELSEIF "$(CFG)" == "notification_history - Win32 Release Unicode"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "notification_history___Win32_Release_Unicode"
+# PROP BASE Intermediate_Dir "notification_history___Win32_Release_Unicode"
+# PROP BASE Ignore_Export_Lib 0
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release_Unicode"
+# PROP Intermediate_Dir "Release_Unicode"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MT /W3 /GX /O2 /I "../../include" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "NOTIFICATION_HISTORY_EXPORTS" /YX /FD /c
+# ADD CPP /nologo /MT /W3 /GX /O2 /I "../../include" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_UNICODE" /D "UNICODE" /D "_USRDLL" /D "NOTIFICATION_HISTORY_EXPORTS" /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x809 /d "NDEBUG"
+# ADD RSC /l 0x809 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 /out:"../../bin/release/plugins/notification_history.dll"
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 /out:"../../bin/release/plugins/notification_historyW.dll"
+
+!ENDIF
+
+# Begin Target
+
+# Name "notification_history - Win32 Release"
+# Name "notification_history - Win32 Debug"
+# Name "notification_history - Win32 Debug Unicode"
+# Name "notification_history - Win32 Release Unicode"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=..\utils\mir_memory.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\utils\mir_options_notify.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\notification_history.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\options.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\resource.rc
+# End Source File
+# Begin Source File
+
+SOURCE=..\utils\templates.cpp
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=.\commons.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\m_notification_history.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\utils\mir_memory.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\utils\mir_options_notify.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\notification_history.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\options.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\utils\templates.h
+# End Source File
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
+# End Group
+# End Target
+# End Project
diff --git a/Plugins/notification_history/notification_history.dsw b/Plugins/notification_history/notification_history.dsw
new file mode 100644
index 0000000..9474252
--- /dev/null
+++ b/Plugins/notification_history/notification_history.dsw
@@ -0,0 +1,29 @@
+Microsoft Developer Studio Workspace File, Format Version 6.00
+# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
+
+###############################################################################
+
+Project: "notification_history"=".\notification_history.dsp" - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Global:
+
+Package=<5>
+{{{
+}}}
+
+Package=<3>
+{{{
+}}}
+
+###############################################################################
+
diff --git a/Plugins/notification_history/notification_history.h b/Plugins/notification_history/notification_history.h
new file mode 100644
index 0000000..e654d74
--- /dev/null
+++ b/Plugins/notification_history/notification_history.h
@@ -0,0 +1,50 @@
+/*
+Copyright (C) 2006 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#ifndef __NOTIFICATION_HISTORY_H__
+# define __NOTIFICATION_HISTORY_H__
+
+
+#include "commons.h"
+
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+
+// Dll init
+BOOL WINAPI DllMain(HINSTANCE hinstDLL,DWORD fdwReason,LPVOID lpvReserved);
+
+// Exports:
+__declspec(dllexport) PLUGININFO* MirandaPluginInfo(DWORD mirandaVersion);
+int __declspec(dllexport) Load(PLUGINLINK *link);
+int __declspec(dllexport) Unload(void);
+
+
+
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // __NOTIFICATION_HISTORY_H__ \ No newline at end of file
diff --git a/Plugins/notification_history/options.cpp b/Plugins/notification_history/options.cpp
new file mode 100644
index 0000000..7d28780
--- /dev/null
+++ b/Plugins/notification_history/options.cpp
@@ -0,0 +1,116 @@
+/*
+Copyright (C) 2005 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#include "options.h"
+
+
+
+// Prototypes /////////////////////////////////////////////////////////////////////////////////////
+
+
+int NotifyOptionsInitialize(WPARAM wParam,LPARAM lParam);
+
+BOOL CALLBACK DlgProcHistory(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
+
+
+// Functions //////////////////////////////////////////////////////////////////////////////////////
+
+
+// Initializations needed by options
+void InitOptions()
+{
+ HookEvent(ME_NOTIFY_OPT_INITIALISE, NotifyOptionsInitialize);
+}
+
+
+// Deinitializations needed by options
+void DeInitOptions()
+{
+}
+
+
+int NotifyOptionsInitialize(WPARAM wParam,LPARAM lParam)
+{
+ OPTIONSDIALOGPAGE odp = {0};
+ odp.cbSize = sizeof(odp);
+ odp.hInstance = hInst;
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT_HISTORY);
+ odp.ptszTitle = TranslateT("History");
+ odp.flags = ODPF_BOLDGROUPS | ODPF_TCHAR;
+ odp.pfnDlgProc = DlgProcHistory;
+ odp.position = 10;
+ CallService(MS_NOTIFY_OPT_ADDPAGE, wParam, (LPARAM)&odp);
+ return 0;
+}
+
+
+static OptPageControl pageControls[] = {
+ { CONTROL_CHECKBOX, IDC_SYSTEM_LOG, NFOPT_HISTORY_SYSTEM_LOG, (BYTE) 0 },
+ { CONTROL_CHECKBOX, IDC_SYSTEM_MARK_READ, NFOPT_HISTORY_SYSTEM_MARK_READ, (BYTE) 1 },
+ { CONTROL_CHECKBOX, IDC_CONTACT_LOG, NFOPT_HISTORY_CONTACT_LOG, (BYTE) 0 },
+ { CONTROL_CHECKBOX, IDC_CONTACT_MARK_READ, NFOPT_HISTORY_CONTACT_MARK_READ, (BYTE) 1 },
+ { CONTROL_TEMPLATE, IDC_SYS_HIST, NFOPT_HISTORY_TEMPLATE_SYSTEMT, NULL },
+ { CONTROL_TEMPLATE, IDC_CNT_HIST, NFOPT_HISTORY_TEMPLATE_CONTACTT, NULL }
+};
+
+TCHAR def[1024];
+
+static BOOL CALLBACK DlgProcHistory(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg)
+ {
+ case WM_USER+100:
+ {
+ HANDLE hNotify = (HANDLE)lParam;
+
+ mir_sntprintf(def, MAX_REGS(def), _T("%s\r\n%s"),
+ MNotifyGetTTemplate(hNotify, NFOPT_DEFTEMPL_TITLET, _T("%title%")),
+ MNotifyGetTTemplate(hNotify, NFOPT_DEFTEMPL_TEXTT, _T("%text%")));
+
+ pageControls[4].szDefVale = def;
+ pageControls[5].szDefVale = def;
+
+ MNotifyShowTVariables(hNotify);
+
+ break;
+ }
+ case WM_COMMAND:
+ {
+ if (HIWORD(wParam) == BN_CLICKED)
+ {
+ switch (LOWORD(wParam))
+ {
+ case IDC_BTN_HELP:
+ {
+ HANDLE hNotify = (HANDLE)GetWindowLong(hwndDlg, GWL_USERDATA);
+ MNotifyShowTVariables(hNotify);
+
+ return TRUE;
+ }
+ }
+ }
+
+ break;
+ }
+ }
+
+ return SaveOptsDlgProc(pageControls, MAX_REGS(pageControls), hwndDlg, msg, wParam, lParam);
+}
+
diff --git a/Plugins/notification_history/options.h b/Plugins/notification_history/options.h
new file mode 100644
index 0000000..fae6353
--- /dev/null
+++ b/Plugins/notification_history/options.h
@@ -0,0 +1,36 @@
+/*
+Copyright (C) 2005 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#include "commons.h"
+
+
+#ifndef __OPTIONS_H__
+# define __OPTIONS_H__
+
+
+// Initializations needed by options
+void InitOptions();
+
+// Deinitializations needed by options
+void DeInitOptions();
+
+
+
+#endif // __OPTIONS_H__
diff --git a/Plugins/notification_history/resource.h b/Plugins/notification_history/resource.h
new file mode 100644
index 0000000..7b6a45b
--- /dev/null
+++ b/Plugins/notification_history/resource.h
@@ -0,0 +1,26 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Developer Studio generated include file.
+// Used by resource.rc
+//
+#define IDD_OPT_HISTORY 101
+#define IDC_SYSTEM_LOG 1000
+#define IDC_SYSTEM_MARK_READ 1001
+#define IDC_CONTACT_LOG 1002
+#define IDC_CONTACT_MARK_READ 1003
+#define IDC_TEXT_GROUP 1004
+#define IDC_SYS_HIST 1005
+#define IDC_CNT_HIST 1006
+#define IDC_SYS_HIST_L 1007
+#define IDC_CNT_HIST_L 1008
+#define IDC_BTN_HELP 1009
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 102
+#define _APS_NEXT_COMMAND_VALUE 40001
+#define _APS_NEXT_CONTROL_VALUE 1010
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/Plugins/notification_history/resource.rc b/Plugins/notification_history/resource.rc
new file mode 100644
index 0000000..5e6241f
--- /dev/null
+++ b/Plugins/notification_history/resource.rc
@@ -0,0 +1,114 @@
+//Microsoft Developer Studio generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "afxres.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// Portuguese (Brazil) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_PTB)
+#ifdef _WIN32
+LANGUAGE LANG_PORTUGUESE, SUBLANG_PORTUGUESE_BRAZILIAN
+#pragma code_page(1252)
+#endif //_WIN32
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Dialog
+//
+
+IDD_OPT_HISTORY DIALOGEX 0, 0, 206, 210
+STYLE DS_SYSMODAL | DS_3DLOOK | WS_CHILD | WS_VISIBLE
+EXSTYLE WS_EX_TRANSPARENT | WS_EX_CONTROLPARENT
+FONT 8, "MS Sans Serif"
+BEGIN
+ LTEXT "When the notification has no contact associated:",
+ IDC_STATIC,13,18,178,9
+ CONTROL "Add to system history",IDC_SYSTEM_LOG,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,30,29,159,11
+ CONTROL "Mark as read",IDC_SYSTEM_MARK_READ,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,30,41,159,11
+ LTEXT "When the notification has a contact associated:",
+ IDC_STATIC,13,59,178,9
+ CONTROL "Add to contact history",IDC_CONTACT_LOG,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,30,70,159,11
+ CONTROL "Mark as read",IDC_CONTACT_MARK_READ,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,30,83,159,11
+ GROUPBOX " Log ",IDC_STATIC,4,5,199,97
+ GROUPBOX " Text ",IDC_TEXT_GROUP,4,108,198,94
+ LTEXT "System history text:",IDC_SYS_HIST_L,13,120,156,11
+ EDITTEXT IDC_SYS_HIST,12,132,181,24,ES_MULTILINE | ES_AUTOVSCROLL |
+ ES_WANTRETURN
+ LTEXT "Contact history text:",IDC_CNT_HIST_L,13,161,178,11
+ EDITTEXT IDC_CNT_HIST,12,172,181,24,ES_MULTILINE | ES_AUTOVSCROLL |
+ ES_WANTRETURN
+ PUSHBUTTON "?",IDC_BTN_HELP,174,115,18,14
+END
+
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "resource.h\0"
+END
+
+2 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "#include ""afxres.h""\r\n"
+ "\0"
+END
+
+3 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "\r\n"
+ "\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// DESIGNINFO
+//
+
+#ifdef APSTUDIO_INVOKED
+GUIDELINES DESIGNINFO DISCARDABLE
+BEGIN
+ IDD_OPT_HISTORY, DIALOG
+ BEGIN
+ RIGHTMARGIN, 203
+ BOTTOMMARGIN, 115
+ END
+END
+#endif // APSTUDIO_INVOKED
+
+#endif // Portuguese (Brazil) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
+
diff --git a/Plugins/notification_log/commons.h b/Plugins/notification_log/commons.h
new file mode 100644
index 0000000..23716ab
--- /dev/null
+++ b/Plugins/notification_log/commons.h
@@ -0,0 +1,76 @@
+/*
+Copyright (C) 2005 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#ifndef __COMMONS_H__
+# define __COMMONS_H__
+
+
+#include <windows.h>
+#include <stdio.h>
+#include <time.h>
+#include <win2k.h>
+#include <tchar.h>
+#include <stdio.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+
+#include <newpluginapi.h>
+#include <m_system.h>
+#include <m_plugins.h>
+#include <m_options.h>
+#include <m_langpack.h>
+#include <m_database.h>
+#include <m_utils.h>
+#include <m_protocols.h>
+
+#include <m_notify.h>
+#include <m_metacontacts.h>
+#include <m_folders.h>
+
+#include "resource.h"
+
+
+#define MODULE_NAME _T("NotificationLog")
+
+extern HINSTANCE hInst;
+extern PLUGINLINK *pluginLink;
+extern PLUGININFO pluginInfo;
+
+#include "m_notification_log.h"
+#include "options.h"
+#include "../utils/templates.h"
+#include "../utils/mir_memory.h"
+#include "../utils/mir_options_notify.h"
+
+
+#define MAX_REGS(_A_) ( sizeof(_A_) / sizeof(_A_[0]) )
+
+
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // __COMMONS_H__
diff --git a/Plugins/notification_log/m_notification_log.h b/Plugins/notification_log/m_notification_log.h
new file mode 100644
index 0000000..45acc5c
--- /dev/null
+++ b/Plugins/notification_log/m_notification_log.h
@@ -0,0 +1,63 @@
+/*
+Copyright (C) 2006 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#ifndef __M_NOTIFICATION_LOG_H__
+# define __M_NOTIFICATION_LOG_H__
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+
+#define NFOPT_LOG_FILENAME "Log/FileName" // const char *
+#define NFOPT_LOG_FILENAMEW "Log/FileNameW" // const WCHAR *
+
+#define NFOPT_LOG_TEMPLATE_LINE "Log/LineTemplate" // const char *
+#define NFOPT_LOG_TEMPLATE_LINEW "Log/LineTemplateW" // const WCHAR *
+
+#define NFOPT_LOG_ENABLED "Log/Enabled" // BYTE
+
+
+#define MS_LOG_SHOW "Log/Show"
+
+
+
+
+
+#ifdef _UNICODE
+
+# define NFOPT_LOG_FILENAMET NFOPT_LOG_FILENAMEW
+# define NFOPT_LOG_TEMPLATE_LINET NFOPT_LOG_TEMPLATE_LINEW
+
+#else
+
+# define NFOPT_LOG_FILENAMET NFOPT_LOG_FILENAME
+# define NFOPT_LOG_TEMPLATE_LINET NFOPT_LOG_TEMPLATE_LINE
+
+#endif
+
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // __M_NOTIFICATION_LOG_H__
diff --git a/Plugins/notification_log/notification_log.cpp b/Plugins/notification_log/notification_log.cpp
new file mode 100644
index 0000000..b6e2ab0
--- /dev/null
+++ b/Plugins/notification_log/notification_log.cpp
@@ -0,0 +1,203 @@
+/*
+Copyright (C) 2005 Ricardo Pescuma Domenecci
+Based on work by tweety, nullbyte
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#include "notification_log.h"
+
+
+#define FOLDER_LOGSW PROFILE_PATHW L"\\" CURRENT_PROFILEW L"\\logs"
+
+#ifdef _UNICODE
+# define FOLDER_LOGST FOLDER_LOGSW
+#else
+# define FOLDER_LOGST FOLDER_LOGS
+#endif
+
+
+// Prototypes /////////////////////////////////////////////////////////////////////////////////////
+
+
+
+HINSTANCE hInst;
+PLUGINLINK *pluginLink;
+MNOTIFYLINK *notifyLink;
+
+PLUGININFO pluginInfo = {
+ sizeof(PLUGININFO),
+ "Log Notification",
+ PLUGIN_MAKE_VERSION(0,0,0,1),
+ "Notification type that log to a file",
+ "Ricardo Pescuma Domenecci",
+ "",
+ "© 2006 Ricardo Pescuma Domenecci",
+ "http://www.miranda-im.org/",
+ 0, //not transient
+ 0 //doesn't replace anything built-in
+};
+
+
+HANDLE hhkNotificationShow = NULL;
+HANDLE hhkModulesLoaded = NULL;
+HANDLE hLogFolder = 0;
+TCHAR szLogPath[MAX_PATH]; // database profile path (read at startup only)
+
+
+int ModulesLoaded(WPARAM wParam,LPARAM lParam);
+void LoadNotifyImp();
+void UnloadNotifyImp();
+
+int LogShow(WPARAM wParam, LPARAM lParam);
+
+
+
+// Functions //////////////////////////////////////////////////////////////////////////////////////
+
+
+BOOL WINAPI DllMain(HINSTANCE hinstDLL,DWORD fdwReason,LPVOID lpvReserved)
+{
+ hInst = hinstDLL;
+ return TRUE;
+}
+
+
+__declspec(dllexport) PLUGININFO* MirandaPluginInfo(DWORD mirandaVersion)
+{
+ return &pluginInfo;
+}
+
+
+int __declspec(dllexport) Load(PLUGINLINK *link)
+{
+ // Copy data
+ pluginLink = link;
+
+ init_mir_malloc();
+
+ hhkModulesLoaded = HookEvent(ME_SYSTEM_MODULESLOADED, ModulesLoaded);
+
+ return 0;
+}
+
+
+int __declspec(dllexport) Unload(void)
+{
+ UnloadNotifyImp();
+ UnhookEvent(hhkModulesLoaded);
+ return 0;
+}
+
+
+int ModulesLoaded(WPARAM wParam,LPARAM lParam)
+{
+ MNotifyGetLink();
+ LoadNotifyImp();
+ InitOptions();
+
+ // Folders plugin support
+ if (ServiceExists(MS_FOLDERS_REGISTER_PATH))
+ {
+ hLogFolder = (HANDLE) FoldersRegisterCustomPathT("Logs", "Logs Path", FOLDER_LOGST);
+ }
+ else
+ {
+ char path[MAX_PATH];
+ CallService(MS_DB_GETPROFILEPATH, MAX_PATH, (LPARAM)path);
+
+#ifdef _UNICODE
+ mir_sntprintf(szLogPath, sizeof(szLogPath), L"%S\\Logs", path);
+#else
+ mir_sntprintf(szLogPath, sizeof(szLogPath), "%s\\Logs", path);
+#endif
+ }
+
+
+ return 0;
+}
+
+
+
+void LoadNotifyImp()
+{
+ hhkNotificationShow = HookEvent(ME_NOTIFY_SHOW, LogShow);
+
+ CreateServiceFunction(MS_LOG_SHOW, LogShow);
+}
+
+
+void UnloadNotifyImp()
+{
+ UnhookEvent(hhkNotificationShow);
+}
+
+
+int LogShow(WPARAM wParam, LPARAM lParam)
+{
+ HANDLE hNotify = (HANDLE)lParam;
+
+ if (!MNotifyGetByte(hNotify, NFOPT_LOG_ENABLED, 0))
+ return 0;
+
+ TCHAR defFile[1024];
+ mir_sntprintf(defFile, MAX_REGS(defFile), _T("%s_log.txt"),
+ MNotifyGetTString(hNotify, NFOPT_TYPENAME, _T("notifications")));
+ const TCHAR *filename = MNotifyGetTString(hNotify, NFOPT_LOG_FILENAMET, defFile);
+
+ if (filename == NULL)
+ return 0;
+
+ TCHAR defLine[1024];
+ mir_sntprintf(defLine, MAX_REGS(defLine), _T("%s: %s"),
+ MNotifyGetTTemplate(hNotify, NFOPT_DEFTEMPL_TITLET, _T("%title%")),
+ MNotifyGetTTemplate(hNotify, NFOPT_DEFTEMPL_TEXTT, _T("%text%")));
+
+ // Get text
+ TCHAR *log_text = MNotifyGetTParsedTemplate(hNotify, NFOPT_LOG_TEMPLATE_LINET, defLine);
+
+ if (log_text != NULL)
+ {
+ TCHAR fullFilename[2048];
+
+ if (_tcschr(filename, ':') == NULL)
+ {
+ TCHAR path[MAX_PATH];
+ FoldersGetCustomPathT(hLogFolder, path, MAX_PATH, szLogPath);
+ mir_sntprintf(fullFilename, MAX_REGS(fullFilename), _T("%s\\%s"), path, filename);
+ }
+ else
+ {
+ lstrcpyn(fullFilename, filename, MAX_REGS(fullFilename));
+ }
+
+ unsigned long dwBytesWritten = 0;
+ HANDLE hFile = CreateFile(fullFilename, GENERIC_WRITE, FILE_SHARE_READ, 0, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+
+ if (hFile != NULL)
+ {
+ SetFilePointer(hFile, 0, 0, FILE_END);
+ WriteFile(hFile, log_text, lstrlen(log_text) * sizeof(TCHAR), &dwBytesWritten, NULL);
+ WriteFile(hFile, _T("\r\n"), 2 * sizeof(TCHAR), &dwBytesWritten, NULL);
+ CloseHandle(hFile);
+ }
+
+ mir_free(log_text);
+ }
+
+ return 0;
+}
diff --git a/Plugins/notification_log/notification_log.dsp b/Plugins/notification_log/notification_log.dsp
new file mode 100644
index 0000000..5ba8594
--- /dev/null
+++ b/Plugins/notification_log/notification_log.dsp
@@ -0,0 +1,213 @@
+# Microsoft Developer Studio Project File - Name="notification_log" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=notification_log - Win32 Debug Unicode
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "notification_log.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "notification_log.mak" CFG="notification_log - Win32 Debug Unicode"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "notification_log - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "notification_log - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "notification_log - Win32 Debug Unicode" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "notification_log - Win32 Release Unicode" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "notification_log - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "NOTIFICATION_LOG_EXPORTS" /YX /FD /c
+# ADD CPP /nologo /MT /W3 /GX /O2 /I "../../include" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "NOTIFICATION_LOG_EXPORTS" /FR /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x809 /d "NDEBUG"
+# ADD RSC /l 0x809 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 /out:"../../bin/release/plugins/notification_log.dll"
+
+!ELSEIF "$(CFG)" == "notification_log - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "NOTIFICATION_LOG_EXPORTS" /YX /FD /GZ /c
+# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "../../include" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "NOTIFICATION_LOG_EXPORTS" /FR /YX /FD /GZ /c
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x809 /d "_DEBUG"
+# ADD RSC /l 0x809 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /out:"../../bin/debug/plugins/notification_log.dll" /pdbtype:sept
+
+!ELSEIF "$(CFG)" == "notification_log - Win32 Debug Unicode"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "notification_log___Win32_Debug_Unicode"
+# PROP BASE Intermediate_Dir "notification_log___Win32_Debug_Unicode"
+# PROP BASE Ignore_Export_Lib 0
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug_Unicode"
+# PROP Intermediate_Dir "Debug_Unicode"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "../../include" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "NOTIFICATION_LOG_EXPORTS" /FR /YX /FD /GZ /c
+# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "../../include" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_UNICODE" /D "UNICODE" /D "_USRDLL" /D "NOTIFICATION_LOG_EXPORTS" /FR /YX /FD /GZ /c
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x809 /d "_DEBUG"
+# ADD RSC /l 0x809 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /out:"../../bin/debug/plugins/notification_log.dll" /pdbtype:sept
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /out:"../../bin/debug/plugins/notification_logW.dll" /pdbtype:sept
+
+!ELSEIF "$(CFG)" == "notification_log - Win32 Release Unicode"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "notification_log___Win32_Release_Unicode"
+# PROP BASE Intermediate_Dir "notification_log___Win32_Release_Unicode"
+# PROP BASE Ignore_Export_Lib 0
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release_Unicode"
+# PROP Intermediate_Dir "Release_Unicode"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MT /W3 /GX /O2 /I "../../include" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "NOTIFICATION_LOG_EXPORTS" /YX /FD /c
+# ADD CPP /nologo /MT /W3 /GX /O2 /I "../../include" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_UNICODE" /D "UNICODE" /D "_USRDLL" /D "NOTIFICATION_LOG_EXPORTS" /FR /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x809 /d "NDEBUG"
+# ADD RSC /l 0x809 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 /out:"../../bin/release/plugins/notification_log.dll"
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 /out:"../../bin/release/plugins/notification_logW.dll"
+
+!ENDIF
+
+# Begin Target
+
+# Name "notification_log - Win32 Release"
+# Name "notification_log - Win32 Debug"
+# Name "notification_log - Win32 Debug Unicode"
+# Name "notification_log - Win32 Release Unicode"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=..\utils\mir_memory.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\utils\mir_options_notify.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\notification_log.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\options.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\resource.rc
+# End Source File
+# Begin Source File
+
+SOURCE=..\utils\templates.cpp
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=.\commons.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\m_notification_log.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\utils\mir_memory.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\utils\mir_options_notify.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\notification_log.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\options.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\utils\templates.h
+# End Source File
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
+# End Group
+# End Target
+# End Project
diff --git a/Plugins/notification_log/notification_log.dsw b/Plugins/notification_log/notification_log.dsw
new file mode 100644
index 0000000..34c3d15
--- /dev/null
+++ b/Plugins/notification_log/notification_log.dsw
@@ -0,0 +1,29 @@
+Microsoft Developer Studio Workspace File, Format Version 6.00
+# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
+
+###############################################################################
+
+Project: "notification_log"=".\notification_log.dsp" - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Global:
+
+Package=<5>
+{{{
+}}}
+
+Package=<3>
+{{{
+}}}
+
+###############################################################################
+
diff --git a/Plugins/notification_log/notification_log.h b/Plugins/notification_log/notification_log.h
new file mode 100644
index 0000000..a9ac85c
--- /dev/null
+++ b/Plugins/notification_log/notification_log.h
@@ -0,0 +1,50 @@
+/*
+Copyright (C) 2006 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#ifndef __NOTIFICATION_LOG_H__
+# define __NOTIFICATION_LOG_H__
+
+
+#include "commons.h"
+
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+
+// Dll init
+BOOL WINAPI DllMain(HINSTANCE hinstDLL,DWORD fdwReason,LPVOID lpvReserved);
+
+// Exports:
+__declspec(dllexport) PLUGININFO* MirandaPluginInfo(DWORD mirandaVersion);
+int __declspec(dllexport) Load(PLUGINLINK *link);
+int __declspec(dllexport) Unload(void);
+
+
+
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // __NOTIFICATION_LOG_H__ \ No newline at end of file
diff --git a/Plugins/notification_log/options.cpp b/Plugins/notification_log/options.cpp
new file mode 100644
index 0000000..3428142
--- /dev/null
+++ b/Plugins/notification_log/options.cpp
@@ -0,0 +1,117 @@
+/*
+Copyright (C) 2005 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#include "options.h"
+
+
+
+// Prototypes /////////////////////////////////////////////////////////////////////////////////////
+
+
+int NotifyOptionsInitialize(WPARAM wParam,LPARAM lParam);
+
+BOOL CALLBACK DlgProcLog(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
+
+
+// Functions //////////////////////////////////////////////////////////////////////////////////////
+
+
+// Initializations needed by options
+void InitOptions()
+{
+ HookEvent(ME_NOTIFY_OPT_INITIALISE, NotifyOptionsInitialize);
+}
+
+
+// Deinitializations needed by options
+void DeInitOptions()
+{
+}
+
+
+int NotifyOptionsInitialize(WPARAM wParam,LPARAM lParam)
+{
+ OPTIONSDIALOGPAGE odp = {0};
+ odp.cbSize = sizeof(odp);
+ odp.hInstance = hInst;
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT_LOG);
+ odp.ptszTitle = TranslateT("Log");
+ odp.flags = ODPF_BOLDGROUPS | ODPF_TCHAR;
+ odp.pfnDlgProc = DlgProcLog;
+ odp.position = 10;
+ CallService(MS_NOTIFY_OPT_ADDPAGE, wParam, (LPARAM)&odp);
+ return 0;
+}
+
+
+static OptPageControl pageControls[] = {
+ { CONTROL_CHECKBOX, IDC_LOG, NFOPT_LOG_ENABLED, (BYTE) 0 },
+ { CONTROL_TEXT, IDC_FILENAME, NFOPT_LOG_FILENAMET, NULL },
+ { CONTROL_TEMPLATE, IDC_TEXT, NFOPT_LOG_TEMPLATE_LINET, NULL }
+};
+
+TCHAR defLine[1024];
+TCHAR defFile[1024];
+
+static BOOL CALLBACK DlgProcLog(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg)
+ {
+ case WM_USER+100:
+ {
+ HANDLE hNotify = (HANDLE)lParam;
+
+ mir_sntprintf(defFile, MAX_REGS(defLine), _T("%s_log.txt"),
+ MNotifyGetTString(hNotify, NFOPT_TYPENAME, _T("notification")));
+
+ pageControls[1].szDefVale = defFile;
+
+
+ mir_sntprintf(defLine, MAX_REGS(defLine), _T("%s: %s"),
+ MNotifyGetTTemplate(hNotify, NFOPT_DEFTEMPL_TITLET, _T("%title%")),
+ MNotifyGetTTemplate(hNotify, NFOPT_DEFTEMPL_TEXTT, _T("%text%")));
+
+ pageControls[2].szDefVale = defLine;
+
+ break;
+ }
+ case WM_COMMAND:
+ {
+ if (HIWORD(wParam) == BN_CLICKED)
+ {
+ switch (LOWORD(wParam))
+ {
+ case IDC_BTN_HELP:
+ {
+ HANDLE hNotify = (HANDLE)GetWindowLong(hwndDlg, GWL_USERDATA);
+ MNotifyShowTVariables(hNotify);
+
+ return TRUE;
+ }
+ }
+ }
+
+ break;
+ }
+ }
+
+ return SaveOptsDlgProc(pageControls, MAX_REGS(pageControls), hwndDlg, msg, wParam, lParam);
+}
+
diff --git a/Plugins/notification_log/options.h b/Plugins/notification_log/options.h
new file mode 100644
index 0000000..fae6353
--- /dev/null
+++ b/Plugins/notification_log/options.h
@@ -0,0 +1,36 @@
+/*
+Copyright (C) 2005 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#include "commons.h"
+
+
+#ifndef __OPTIONS_H__
+# define __OPTIONS_H__
+
+
+// Initializations needed by options
+void InitOptions();
+
+// Deinitializations needed by options
+void DeInitOptions();
+
+
+
+#endif // __OPTIONS_H__
diff --git a/Plugins/notification_log/resource.h b/Plugins/notification_log/resource.h
new file mode 100644
index 0000000..0a71f17
--- /dev/null
+++ b/Plugins/notification_log/resource.h
@@ -0,0 +1,30 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Developer Studio generated include file.
+// Used by resource.rc
+//
+#define IDD_OPT_HISTORY 101
+#define IDD_OPT_LOG 101
+#define IDC_SYSTEM_LOG 1000
+#define IDC_LOG 1000
+#define IDC_SYSTEM_MARK_READ 1001
+#define IDC_CONTACT_LOG 1002
+#define IDC_CONTACT_MARK_READ 1003
+#define IDC_TEXT_GROUP 1004
+#define IDC_SYS_HIST 1005
+#define IDC_TEXT 1005
+#define IDC_CNT_HIST 1006
+#define IDC_SYS_HIST_L 1007
+#define IDC_CNT_HIST_L 1008
+#define IDC_BTN_HELP 1009
+#define IDC_FILENAME 1010
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 102
+#define _APS_NEXT_COMMAND_VALUE 40001
+#define _APS_NEXT_CONTROL_VALUE 1011
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/Plugins/notification_log/resource.rc b/Plugins/notification_log/resource.rc
new file mode 100644
index 0000000..b988720
--- /dev/null
+++ b/Plugins/notification_log/resource.rc
@@ -0,0 +1,105 @@
+//Microsoft Developer Studio generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "afxres.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// Portuguese (Brazil) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_PTB)
+#ifdef _WIN32
+LANGUAGE LANG_PORTUGUESE, SUBLANG_PORTUGUESE_BRAZILIAN
+#pragma code_page(1252)
+#endif //_WIN32
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Dialog
+//
+
+IDD_OPT_LOG DIALOGEX 0, 0, 206, 153
+STYLE DS_SYSMODAL | DS_3DLOOK | WS_CHILD | WS_VISIBLE
+EXSTYLE WS_EX_TRANSPARENT | WS_EX_CONTROLPARENT
+FONT 8, "MS Sans Serif"
+BEGIN
+ CONTROL "Log to file",IDC_LOG,"Button",BS_AUTOCHECKBOX |
+ WS_TABSTOP,13,20,178,11
+ GROUPBOX " Log ",IDC_STATIC,4,5,199,82
+ GROUPBOX " Text ",IDC_TEXT_GROUP,4,91,198,56
+ LTEXT "Line of text:",IDC_SYS_HIST_L,13,103,156,11
+ EDITTEXT IDC_TEXT,13,116,181,24,ES_MULTILINE | ES_AUTOVSCROLL |
+ ES_WANTRETURN
+ PUSHBUTTON "?",IDC_BTN_HELP,174,98,18,14
+ LTEXT "File name:",IDC_STATIC,13,38,181,10
+ EDITTEXT IDC_FILENAME,13,51,182,13,ES_AUTOHSCROLL
+ CTEXT "Path is relative to Logs folder",IDC_STATIC,13,68,182,
+ 11
+END
+
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "resource.h\0"
+END
+
+2 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "#include ""afxres.h""\r\n"
+ "\0"
+END
+
+3 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "\r\n"
+ "\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// DESIGNINFO
+//
+
+#ifdef APSTUDIO_INVOKED
+GUIDELINES DESIGNINFO DISCARDABLE
+BEGIN
+ IDD_OPT_LOG, DIALOG
+ BEGIN
+ RIGHTMARGIN, 203
+ BOTTOMMARGIN, 148
+ END
+END
+#endif // APSTUDIO_INVOKED
+
+#endif // Portuguese (Brazil) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
+
diff --git a/Plugins/notification_speak/commons.h b/Plugins/notification_speak/commons.h
new file mode 100644
index 0000000..20176a4
--- /dev/null
+++ b/Plugins/notification_speak/commons.h
@@ -0,0 +1,77 @@
+/*
+Copyright (C) 2005 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#ifndef __COMMONS_H__
+# define __COMMONS_H__
+
+
+#include <windows.h>
+#include <time.h>
+#include <win2k.h>
+#include <tchar.h>
+#include <stdio.h>
+
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+
+#include <newpluginapi.h>
+#include <m_system.h>
+#include <m_plugins.h>
+#include <m_options.h>
+#include <m_langpack.h>
+#include <m_database.h>
+#include <m_utils.h>
+#include <m_protocols.h>
+
+#include <m_notify.h>
+#include <m_metacontacts.h>
+
+#include "resource.h"
+
+
+#define MODULE_NAME "NotificationSpeak"
+
+extern HINSTANCE hInst;
+extern PLUGINLINK *pluginLink;
+extern PLUGININFO pluginInfo;
+
+#include "m_notification_speak.h"
+#include "options.h"
+#include "../utils/mir_memory.h"
+#include "../utils/mir_options_notify.h"
+#include "../utils/templates.h"
+
+
+#define MAX_REGS(_A_) ( sizeof(_A_) / sizeof(_A_[0]) )
+
+
+#define MS_SPEAK_SAY "Speak/Say"
+
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // __COMMONS_H__
diff --git a/Plugins/notification_speak/m_notification_speak.h b/Plugins/notification_speak/m_notification_speak.h
new file mode 100644
index 0000000..6530c66
--- /dev/null
+++ b/Plugins/notification_speak/m_notification_speak.h
@@ -0,0 +1,57 @@
+/*
+Copyright (C) 2006 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#ifndef __M_NOTIFICATION_SPEAK_H__
+# define __M_NOTIFICATION_SPEAK_H__
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+
+#define NFOPT_SPEAK_TEMPLATE_TEXT "Speak/Text" // const char *
+#define NFOPT_SPEAK_TEMPLATE_TEXTW "Speak/TextW" // const WCHAR *
+
+#define NFOPT_SPEAK_SAY "Speak/Say" // BYTE
+
+
+#define MS_SPEAK_SHOW "Speak/Show"
+
+
+
+#ifdef _UNICODE
+
+# define NFOPT_SPEAK_TEMPLATE_TEXTT NFOPT_SPEAK_TEMPLATE_TEXTW
+
+#else
+
+# define NFOPT_SPEAK_TEMPLATE_TEXTT NFOPT_SPEAK_TEMPLATE_TEXT
+
+#endif
+
+
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // __M_NOTIFICATION_SPEAK_H__
diff --git a/Plugins/notification_speak/notification_speak.cpp b/Plugins/notification_speak/notification_speak.cpp
new file mode 100644
index 0000000..a4c6095
--- /dev/null
+++ b/Plugins/notification_speak/notification_speak.cpp
@@ -0,0 +1,164 @@
+/*
+Copyright (C) 2005 Ricardo Pescuma Domenecci
+Based on work by tweety, nullbyte
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#include "notification_speak.h"
+
+
+
+// Prototypes /////////////////////////////////////////////////////////////////////////////////////
+
+
+
+HINSTANCE hInst;
+PLUGINLINK *pluginLink;
+MNOTIFYLINK *notifyLink;
+
+PLUGININFO pluginInfo = {
+ sizeof(PLUGININFO),
+ "Speak Notification",
+ PLUGIN_MAKE_VERSION(0,0,0,1),
+ "Notification type that speak notifications aloud. Depends on Speak plugin.",
+ "Ricardo Pescuma Domenecci",
+ "",
+ "© 2006 Ricardo Pescuma Domenecci",
+ "http://www.miranda-im.org/",
+ 0, //not transient
+ 0 //doesn't replace anything built-in
+};
+
+
+HANDLE hhkNotificationShow = NULL;
+HANDLE hhkModulesLoaded = NULL;
+
+
+int ModulesLoaded(WPARAM wParam,LPARAM lParam);
+void LoadNotifyImp();
+void UnloadNotifyImp();
+
+int SpeakShow(WPARAM wParam, LPARAM lParam);
+
+
+
+// Functions //////////////////////////////////////////////////////////////////////////////////////
+
+
+BOOL WINAPI DllMain(HINSTANCE hinstDLL,DWORD fdwReason,LPVOID lpvReserved)
+{
+ hInst = hinstDLL;
+ return TRUE;
+}
+
+
+__declspec(dllexport) PLUGININFO* MirandaPluginInfo(DWORD mirandaVersion)
+{
+ return &pluginInfo;
+}
+
+
+int __declspec(dllexport) Load(PLUGINLINK *link)
+{
+ // Copy data
+ pluginLink = link;
+
+ init_mir_malloc();
+
+ hhkModulesLoaded = HookEvent(ME_SYSTEM_MODULESLOADED, ModulesLoaded);
+
+ return 0;
+}
+
+
+int __declspec(dllexport) Unload(void)
+{
+ UnloadNotifyImp();
+ UnhookEvent(hhkModulesLoaded);
+ return 0;
+}
+
+
+int ModulesLoaded(WPARAM wParam,LPARAM lParam)
+{
+ if (ServiceExists(MS_SPEAK_SAY))
+ {
+ MNotifyGetLink();
+ LoadNotifyImp();
+ InitOptions();
+ }
+ else
+ {
+ MessageBox(NULL, TranslateT("Speak Notification requires Speak plugin to be installed"),
+ TranslateT("Error"), MB_OK | MB_ICONERROR);
+ }
+
+ return 0;
+}
+
+
+
+
+
+void LoadNotifyImp()
+{
+ hhkNotificationShow = HookEvent(ME_NOTIFY_SHOW, SpeakShow);
+
+ CreateServiceFunction(MS_SPEAK_SHOW, SpeakShow);
+}
+
+
+void UnloadNotifyImp()
+{
+ UnhookEvent(hhkNotificationShow);
+}
+
+
+int SpeakShow(WPARAM wParam, LPARAM lParam)
+{
+ HANDLE hNotify = (HANDLE)lParam;
+
+ if (!MNotifyGetByte(hNotify, NFOPT_SPEAK_SAY, 0))
+ return 0;
+
+ TCHAR def[1024];
+ mir_sntprintf(def, MAX_REGS(def), _T("%s\r\n%s"),
+ MNotifyGetTTemplate(hNotify, NFOPT_DEFTEMPL_TITLET, _T("%title%")),
+ MNotifyGetTTemplate(hNotify, NFOPT_DEFTEMPL_TEXTT, _T("%text%")));
+
+ // Get text
+ TCHAR *log_text = MNotifyGetTParsedTemplate(hNotify, NFOPT_SPEAK_TEMPLATE_TEXTT, def);
+
+ if (log_text != NULL)
+ {
+#ifdef _UNICODE
+ // Speak does not have an unicode version
+ char *tmp = mir_dupToAscii(log_text);
+
+ CallService(MS_SPEAK_SAY, 0, (LPARAM) tmp);
+
+ mir_free(tmp);
+#else
+ CallService(MS_SPEAK_SAY, 0, (LPARAM) log_text);
+#endif
+
+ mir_free(log_text);
+ }
+
+ return 0;
+}
diff --git a/Plugins/notification_speak/notification_speak.dsp b/Plugins/notification_speak/notification_speak.dsp
new file mode 100644
index 0000000..6e5339a
--- /dev/null
+++ b/Plugins/notification_speak/notification_speak.dsp
@@ -0,0 +1,213 @@
+# Microsoft Developer Studio Project File - Name="notification_speak" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=notification_speak - Win32 Debug Unicode
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "notification_speak.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "notification_speak.mak" CFG="notification_speak - Win32 Debug Unicode"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "notification_speak - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "notification_speak - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "notification_speak - Win32 Debug Unicode" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "notification_speak - Win32 Release Unicode" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "notification_speak - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "NOTIFICATION_SPEAK_EXPORTS" /YX /FD /c
+# ADD CPP /nologo /MT /W3 /GX /O2 /I "../../include" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "NOTIFICATION_SPEAK_EXPORTS" /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x809 /d "NDEBUG"
+# ADD RSC /l 0x809 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 /out:"../../bin/release/plugins/notification_speak.dll"
+
+!ELSEIF "$(CFG)" == "notification_speak - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "NOTIFICATION_SPEAK_EXPORTS" /YX /FD /GZ /c
+# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "../../include" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "NOTIFICATION_SPEAK_EXPORTS" /FR /YX /FD /GZ /c
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x809 /d "_DEBUG"
+# ADD RSC /l 0x809 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /out:"../../bin/debug/plugins/notification_speak.dll" /pdbtype:sept
+
+!ELSEIF "$(CFG)" == "notification_speak - Win32 Debug Unicode"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "notification_speak___Win32_Debug_Unicode"
+# PROP BASE Intermediate_Dir "notification_speak___Win32_Debug_Unicode"
+# PROP BASE Ignore_Export_Lib 0
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug_Unicode"
+# PROP Intermediate_Dir "Debug_Unicode"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "../../include" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "NOTIFICATION_SPEAK_EXPORTS" /FR /YX /FD /GZ /c
+# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "../../include" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_UNICODE" /D "UNICODE" /D "_USRDLL" /D "NOTIFICATION_SPEAK_EXPORTS" /FR /YX /FD /GZ /c
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x809 /d "_DEBUG"
+# ADD RSC /l 0x809 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /out:"../../bin/debug/plugins/notification_speak.dll" /pdbtype:sept
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /out:"../../bin/debug/plugins/notification_speakW.dll" /pdbtype:sept
+
+!ELSEIF "$(CFG)" == "notification_speak - Win32 Release Unicode"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "notification_speak___Win32_Release_Unicode"
+# PROP BASE Intermediate_Dir "notification_speak___Win32_Release_Unicode"
+# PROP BASE Ignore_Export_Lib 0
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release_Unicode"
+# PROP Intermediate_Dir "Release_Unicode"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MT /W3 /GX /O2 /I "../../include" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "NOTIFICATION_SPEAK_EXPORTS" /YX /FD /c
+# ADD CPP /nologo /MT /W3 /GX /O2 /I "../../include" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_UNICODE" /D "UNICODE" /D "_USRDLL" /D "NOTIFICATION_SPEAK_EXPORTS" /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x809 /d "NDEBUG"
+# ADD RSC /l 0x809 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 /out:"../../bin/release/plugins/notification_speak.dll"
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 /out:"../../bin/release/plugins/notification_speakW.dll"
+
+!ENDIF
+
+# Begin Target
+
+# Name "notification_speak - Win32 Release"
+# Name "notification_speak - Win32 Debug"
+# Name "notification_speak - Win32 Debug Unicode"
+# Name "notification_speak - Win32 Release Unicode"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=..\utils\mir_memory.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\utils\mir_options_notify.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\notification_speak.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\options.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\resource.rc
+# End Source File
+# Begin Source File
+
+SOURCE=..\utils\templates.cpp
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=.\commons.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\m_notification_speak.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\utils\mir_memory.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\utils\mir_options_notify.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\notification_speak.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\options.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\utils\templates.h
+# End Source File
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
+# End Group
+# End Target
+# End Project
diff --git a/Plugins/notification_speak/notification_speak.dsw b/Plugins/notification_speak/notification_speak.dsw
new file mode 100644
index 0000000..fb0e6b4
--- /dev/null
+++ b/Plugins/notification_speak/notification_speak.dsw
@@ -0,0 +1,29 @@
+Microsoft Developer Studio Workspace File, Format Version 6.00
+# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
+
+###############################################################################
+
+Project: "notification_speak"=".\notification_speak.dsp" - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Global:
+
+Package=<5>
+{{{
+}}}
+
+Package=<3>
+{{{
+}}}
+
+###############################################################################
+
diff --git a/Plugins/notification_speak/notification_speak.h b/Plugins/notification_speak/notification_speak.h
new file mode 100644
index 0000000..6b6c166
--- /dev/null
+++ b/Plugins/notification_speak/notification_speak.h
@@ -0,0 +1,50 @@
+/*
+Copyright (C) 2006 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#ifndef __NOTIFICATION_SPEAK_H__
+# define __NOTIFICATION_SPEAK_H__
+
+
+#include "commons.h"
+
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+
+// Dll init
+BOOL WINAPI DllMain(HINSTANCE hinstDLL,DWORD fdwReason,LPVOID lpvReserved);
+
+// Exports:
+__declspec(dllexport) PLUGININFO* MirandaPluginInfo(DWORD mirandaVersion);
+int __declspec(dllexport) Load(PLUGINLINK *link);
+int __declspec(dllexport) Unload(void);
+
+
+
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // __NOTIFICATION_SPEAK_H__ \ No newline at end of file
diff --git a/Plugins/notification_speak/options.cpp b/Plugins/notification_speak/options.cpp
new file mode 100644
index 0000000..6983389
--- /dev/null
+++ b/Plugins/notification_speak/options.cpp
@@ -0,0 +1,110 @@
+/*
+Copyright (C) 2005 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#include "options.h"
+
+
+
+// Prototypes /////////////////////////////////////////////////////////////////////////////////////
+
+
+int NotifyOptionsInitialize(WPARAM wParam,LPARAM lParam);
+
+BOOL CALLBACK DlgProcSpeak(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
+
+
+// Functions //////////////////////////////////////////////////////////////////////////////////////
+
+
+// Initializations needed by options
+void InitOptions()
+{
+ HookEvent(ME_NOTIFY_OPT_INITIALISE, NotifyOptionsInitialize);
+}
+
+
+// Deinitializations needed by options
+void DeInitOptions()
+{
+}
+
+
+int NotifyOptionsInitialize(WPARAM wParam,LPARAM lParam)
+{
+ OPTIONSDIALOGPAGE odp = {0};
+ odp.cbSize = sizeof(odp);
+ odp.hInstance = hInst;
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT_SPEAK);
+ odp.ptszTitle = TranslateT("Speak");
+ odp.flags = ODPF_BOLDGROUPS | ODPF_TCHAR;
+ odp.pfnDlgProc = DlgProcSpeak;
+ odp.position = 20;
+ CallService(MS_NOTIFY_OPT_ADDPAGE, wParam, (LPARAM)&odp);
+
+ return 0;
+}
+
+
+static OptPageControl pageControls[] = {
+ { CONTROL_CHECKBOX, IDC_SAY, NFOPT_SPEAK_SAY, (BYTE) 0 },
+ { CONTROL_TEMPLATE, IDC_TEXT, NFOPT_SPEAK_TEMPLATE_TEXTT, (BYTE) 0 }
+};
+
+TCHAR def[1024];
+
+static BOOL CALLBACK DlgProcSpeak(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg)
+ {
+ case WM_USER+100:
+ {
+ HANDLE hNotify = (HANDLE)lParam;
+
+ mir_sntprintf(def, MAX_REGS(def), _T("%s\r\n%s"),
+ MNotifyGetTTemplate(hNotify, NFOPT_DEFTEMPL_TITLET, _T("%title%")),
+ MNotifyGetTTemplate(hNotify, NFOPT_DEFTEMPL_TEXTT, _T("%text%")));
+
+ pageControls[1].szDefVale = def;
+
+ break;
+ }
+ case WM_COMMAND:
+ {
+ if (HIWORD(wParam) == BN_CLICKED)
+ {
+ switch (LOWORD(wParam))
+ {
+ case IDC_BTN_HELP:
+ {
+ HANDLE hNotify = (HANDLE)GetWindowLong(hwndDlg, GWL_USERDATA);
+ MNotifyShowTVariables(hNotify);
+
+ return TRUE;
+ }
+ }
+ }
+
+ break;
+ }
+ }
+
+ return SaveOptsDlgProc(pageControls, MAX_REGS(pageControls), hwndDlg, msg, wParam, lParam);
+}
+
diff --git a/Plugins/notification_speak/options.h b/Plugins/notification_speak/options.h
new file mode 100644
index 0000000..fae6353
--- /dev/null
+++ b/Plugins/notification_speak/options.h
@@ -0,0 +1,36 @@
+/*
+Copyright (C) 2005 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#include "commons.h"
+
+
+#ifndef __OPTIONS_H__
+# define __OPTIONS_H__
+
+
+// Initializations needed by options
+void InitOptions();
+
+// Deinitializations needed by options
+void DeInitOptions();
+
+
+
+#endif // __OPTIONS_H__
diff --git a/Plugins/notification_speak/resource.h b/Plugins/notification_speak/resource.h
new file mode 100644
index 0000000..c0d9a00
--- /dev/null
+++ b/Plugins/notification_speak/resource.h
@@ -0,0 +1,21 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Developer Studio generated include file.
+// Used by resource.rc
+//
+#define IDD_OPT_SPEAK 101
+#define IDC_SAY 1000
+#define IDC_TEXT_GROUP 1004
+#define IDC_TEXT 1005
+#define IDC_TEXT_L 1007
+#define IDC_BTN_HELP 1009
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 102
+#define _APS_NEXT_COMMAND_VALUE 40001
+#define _APS_NEXT_CONTROL_VALUE 1001
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/Plugins/notification_speak/resource.rc b/Plugins/notification_speak/resource.rc
new file mode 100644
index 0000000..d489e42
--- /dev/null
+++ b/Plugins/notification_speak/resource.rc
@@ -0,0 +1,101 @@
+//Microsoft Developer Studio generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "afxres.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// Portuguese (Brazil) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_PTB)
+#ifdef _WIN32
+LANGUAGE LANG_PORTUGUESE, SUBLANG_PORTUGUESE_BRAZILIAN
+#pragma code_page(1252)
+#endif //_WIN32
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Dialog
+//
+
+IDD_OPT_SPEAK DIALOGEX 0, 0, 206, 111
+STYLE DS_SYSMODAL | DS_3DLOOK | WS_CHILD | WS_VISIBLE
+EXSTYLE WS_EX_TRANSPARENT | WS_EX_CONTROLPARENT
+FONT 8, "MS Sans Serif"
+BEGIN
+ CONTROL "Say notification aloud",IDC_SAY,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,13,20,180,11
+ GROUPBOX "General",IDC_STATIC,4,5,198,36
+ GROUPBOX " Text ",IDC_TEXT_GROUP,4,46,198,56
+ LTEXT "Text to speak:",IDC_TEXT_L,13,58,156,11
+ EDITTEXT IDC_TEXT,12,70,181,24,ES_MULTILINE | ES_AUTOVSCROLL |
+ ES_WANTRETURN
+ PUSHBUTTON "?",IDC_BTN_HELP,174,53,18,14
+END
+
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "resource.h\0"
+END
+
+2 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "#include ""afxres.h""\r\n"
+ "\0"
+END
+
+3 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "\r\n"
+ "\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// DESIGNINFO
+//
+
+#ifdef APSTUDIO_INVOKED
+GUIDELINES DESIGNINFO DISCARDABLE
+BEGIN
+ IDD_OPT_SPEAK, DIALOG
+ BEGIN
+ RIGHTMARGIN, 203
+ BOTTOMMARGIN, 107
+ END
+END
+#endif // APSTUDIO_INVOKED
+
+#endif // Portuguese (Brazil) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
+
diff --git a/Plugins/quickcontacts/Docs/langpack_quickcontacts.txt b/Plugins/quickcontacts/Docs/langpack_quickcontacts.txt
new file mode 100644
index 0000000..bb3ce09
--- /dev/null
+++ b/Plugins/quickcontacts/Docs/langpack_quickcontacts.txt
@@ -0,0 +1,40 @@
+; Quick Contacts
+; Author: micron-x, Pescuma
+; http://pescuma.org/miranda/quickcontacts
+
+[Quick Contacts]
+
+; Hotkey description
+[Open dialog]
+[Open Quick Contacts dialog]
+
+; Menu item
+[Quick Contacts...]
+
+; Main dialog
+[Enter Username:]
+[Show all contacts]
+
+; Tooltips
+[Send message]
+[Make a voice call]
+[Send file]
+[Send URL]
+[Open userinfo]
+[Open history]
+[Open contact menu]
+
+; Options
+[Last Sent]
+[Enable last-sent-to]
+[recognize all messages sent]
+[recognize messages sent with hotkey only]
+
+[Contacts]
+[Show offline contacts on these protocols:]
+[But hide them if protocol is offline]
+[Append group name to contact name]
+[But show it as a column ...]
+[... on left side of name]
+[Hide subcontacts]
+[But keep subcontacts of protocols in above list if meta is hidden]
diff --git a/Plugins/quickcontacts/Docs/quickcontacts.png b/Plugins/quickcontacts/Docs/quickcontacts.png
new file mode 100644
index 0000000..0dba1b7
--- /dev/null
+++ b/Plugins/quickcontacts/Docs/quickcontacts.png
Binary files differ
diff --git a/Plugins/quickcontacts/Docs/quickcontacts_changelog.txt b/Plugins/quickcontacts/Docs/quickcontacts_changelog.txt
new file mode 100644
index 0000000..bc09470
--- /dev/null
+++ b/Plugins/quickcontacts/Docs/quickcontacts_changelog.txt
@@ -0,0 +1,75 @@
+Quick Contacts
+
+Changelog:
+
+. 1.0.0.0
+ * Fix for group names (closes issue #83)
+ + Handle metacontacts off (closes issue #61)
+ + Show account name
+ * Removed spaces from frame names in options
+
+. 0.0.3.1
+ + Number of contacts is not limited anymore
+
+. 0.0.3.0
+ + Added support for hotkeys module in 0.8 (some texts changed in langpack)
+
+. 0.0.2.9
+ + Added contact icon
+ * Fix when comparing unicode strings
+
+. 0.0.2.8
+ + Added VoiceService support
+
+. 0.0.2.7
+ + Added support for Miranda 0.8
+
+. 0.0.2.6
+ * Fix for crash on exit
+
+. 0.0.2.5
+ + Added contact menu buttom
+
+. 0.0.2.4
+ * Only to fix upload of wrong file to FL
+
+. 0.0.2.3
+ + Added support for Hotkeys+
+ * Now it will register to all hotkey services if found
+
+. 0.0.2.2
+ * Fix for icons
+ * Fix for backspace
+ * -pv- fix
+
+. 0.0.2.1
+ * Try to fix error reported
+ + Support of Hotkey service (if it exists, will be used instead of HotKeys2)
+ + Support for Magnetic windows
+
+. 0.0.2.0
+ - First version in FL
+ * Fix for more than one contact wth same name
+ * Fix to always open the dialog inside a monitor
+
+. 0.0.1.4
+ + Option to show group as a column
+ + Act as double click when pressing enter in the combo
+ + Disable buttons that protocol do not allow
+ * Bug fixes
+
+. 0.0.1.3
+ + Changes in options
+ + Added unicode release
+
+. 0.0.1.2
+ + Option to append group name to contact name
+
+. 0.0.1.1
+ + Checkbox in main dialog works
+ + Option to hide contacts from offline protocols
+ * Fix in getting metacontact status
+
+. 0.0.1.0
+ First verion based on Hotkey plugin
+3.2.1.0 \ No newline at end of file
diff --git a/Plugins/quickcontacts/Docs/quickcontacts_readme.txt b/Plugins/quickcontacts/Docs/quickcontacts_readme.txt
new file mode 100644
index 0000000..b7d8b5b
--- /dev/null
+++ b/Plugins/quickcontacts/Docs/quickcontacts_readme.txt
@@ -0,0 +1,22 @@
+Quick Contacts plugin
+---------------------
+
+On pressing an user defined hotkey an dialog pops up where you can enter a contacts name or select it from a combobox and send it messages/files/urls an look at his details.
+
+This is a mod of the Hotkey plugin by micron-x
+
+To be able to set the hotkey you need clist_modern (or some other that implements Hotkeys2), Hotkeys+ or HotkeyService.
+
+Available hotkeys:
+- Ctrl-V: Make a voice call
+- Ctrl-F: Send file
+- Ctrl-U: Send URL
+- Ctrl-I: Show userinfo
+- Ctrl-H: Open history
+- Ctrl-M: Open contact menu
+- User defined: Open hotkey dialog
+In Miranda 0.8 all this keys can be configured in options.
+
+To report bugs/make suggestions, go to the forum thread: http://forums.miranda-im.org/showthread.php?t=8797
+
+To report bugs, please create a attache dump using the pdb file (you need to copy to pdb to the same place the dll is) \ No newline at end of file
diff --git a/Plugins/quickcontacts/Docs/quickcontacts_version.txt b/Plugins/quickcontacts/Docs/quickcontacts_version.txt
new file mode 100644
index 0000000..15e0482
--- /dev/null
+++ b/Plugins/quickcontacts/Docs/quickcontacts_version.txt
@@ -0,0 +1 @@
+Quick Contacts 1.0.0.0 \ No newline at end of file
diff --git a/Plugins/quickcontacts/ZIP/doit.bat b/Plugins/quickcontacts/ZIP/doit.bat
new file mode 100644
index 0000000..181368f
--- /dev/null
+++ b/Plugins/quickcontacts/ZIP/doit.bat
@@ -0,0 +1,102 @@
+@echo off
+
+rem Batch file to build and upload files
+rem
+rem TODO: Integration with FL
+
+set name=quickcontacts
+
+rem To upload, this var must be set here or in other batch
+rem set ftp=ftp://<user>:<password>@<ftp>/<path>
+
+echo Building %name% ...
+
+msdev ..\%name%.dsp /MAKE "%name% - Win32 Release" /REBUILD
+msdev ..\%name%.dsp /MAKE "%name% - Win32 Unicode Release" /REBUILD
+
+echo Generating files for %name% ...
+
+del *.zip
+del *.dll
+copy ..\Release\%name%.pdb
+copy ..\Unicode_Release\%name%W.pdb
+copy ..\Docs\%name%_changelog.txt
+copy ..\Docs\%name%_version.txt
+copy ..\Docs\%name%_readme.txt
+mkdir Docs
+cd Docs
+del /Q *.*
+copy ..\..\Docs\langpack_%name%.txt
+copy ..\..\Docs\%name%_readme.txt
+copy ..\..\m_%name%.h
+cd ..
+mkdir src
+cd src
+del /Q *.*
+copy ..\..\*.h
+copy ..\..\*.cpp
+copy ..\..\*.
+copy ..\..\*.rc
+copy ..\..\*.dsp
+copy ..\..\*.dsw
+mkdir Docs
+cd Docs
+del /Q *.*
+copy ..\..\..\Docs\*.*
+cd ..
+cd ..
+
+mkdir Plugins
+cd Plugins
+del /Q *.*
+copy ..\..\..\..\bin\release\Plugins\%name%.dll
+cd ..
+
+"C:\Program Files\Filzip\Filzip.exe" -a -rp %name%.zip Plugins Docs
+
+cd Plugins
+del /Q *.*
+copy "..\..\..\..\bin\release unicode\Plugins\%name%W.dll"
+cd ..
+
+"C:\Program Files\Filzip\Filzip.exe" -a -rp %name%W.zip Plugins Docs
+
+"C:\Program Files\Filzip\Filzip.exe" -a -rp %name%.pdb.zip %name%.pdb
+"C:\Program Files\Filzip\Filzip.exe" -a -rp %name%W.pdb.zip %name%W.pdb
+"C:\Program Files\Filzip\Filzip.exe" -a -rp %name%_src.zip src\*.*
+
+del *.dll
+del *.pdb
+cd Plugins
+del /Q *.*
+cd ..
+rmdir Plugins
+cd Docs
+del /Q *.*
+cd ..
+rmdir Docs
+cd src
+del /Q *.*
+cd Docs
+del /Q *.*
+cd ..
+rmdir Docs
+cd ..
+rmdir src
+
+if "%ftp%"=="" GOTO END
+
+echo Going to upload files...
+pause
+
+"C:\Program Files\FileZilla\FileZilla.exe" -u .\%name%.zip %ftp% -overwrite -close
+"C:\Program Files\FileZilla\FileZilla.exe" -u .\%name%W.zip %ftp% -overwrite -close
+"C:\Program Files\FileZilla\FileZilla.exe" -u .\%name%.pdb.zip %ftp% -overwrite -close
+"C:\Program Files\FileZilla\FileZilla.exe" -u .\%name%W.pdb.zip %ftp% -overwrite -close
+"C:\Program Files\FileZilla\FileZilla.exe" -u .\%name%_changelog.txt %ftp% -overwrite -close
+"C:\Program Files\FileZilla\FileZilla.exe" -u .\%name%_readme.txt %ftp% -overwrite -close
+"C:\Program Files\FileZilla\FileZilla.exe" -u .\%name%_version.txt %ftp% -overwrite -close
+
+:END
+
+echo Done.
diff --git a/Plugins/quickcontacts/commons.h b/Plugins/quickcontacts/commons.h
new file mode 100644
index 0000000..2a8c9e4
--- /dev/null
+++ b/Plugins/quickcontacts/commons.h
@@ -0,0 +1,111 @@
+/*
+Copyright (C) 2006 Ricardo Pescuma Domenecci
+Based on work (C) Heiko Schillinger
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#ifndef __COMMONS_H__
+# define __COMMONS_H__
+
+
+#define WINVER 0x0500
+#include <windows.h>
+#include <commctrl.h>
+#include <stdio.h>
+#include <tchar.h>
+
+
+// Miranda headers
+#define MIRANDA_VER 0x0700
+#include <newpluginapi.h>
+#include <m_system.h>
+#include <m_system_cpp.h>
+#include <m_protocols.h>
+#include <m_protosvc.h>
+#include <m_clist.h>
+#include <m_ignore.h>
+#include <m_contacts.h>
+#include <m_message.h>
+#include <m_userinfo.h>
+#include <m_skin.h>
+#include <m_langpack.h>
+#include <m_database.h>
+#include <m_options.h>
+#include <m_utils.h>
+#include <m_button.h>
+#include <m_file.h>
+#include <m_url.h>
+#include <m_history.h>
+#include <m_updater.h>
+#include <m_metacontacts.h>
+#include <m_MagneticWindows.h>
+#include <m_hotkeysservice.h>
+#include <m_hotkeysplus.h>
+#include <m_popup.h>
+#include <m_voice.h>
+#include <m_voiceservice.h>
+#include <m_icolib.h>
+#include <m_hotkeys.h>
+
+#include "../utils/mir_memory.h"
+#include "../utils/mir_options.h"
+#include "../utils/utf8_helpers.h"
+
+#include "resource.h"
+#include "m_quickcontacts.h"
+#include "options.h"
+
+
+#define MODULE_NAME "QuickContacts"
+
+
+// Global Variables
+extern HINSTANCE hInst;
+extern PLUGINLINK *pluginLink;
+extern char *metacontacts_proto;
+
+
+#define MAX_REGS(_A_) ( sizeof(_A_) / sizeof(_A_[0]) )
+
+
+
+
+
+// Copied from "../modernb/clc.h" ///////////////////////////////////////////////////////////////////
+
+//add a new hotkey so it has a default and can be changed in the options dialog
+//wParam=0
+//lParam=(LPARAM)(SKINHOTKEYDESC*)ssd;
+//returns 0 on success, nonzero otherwise
+
+typedef struct {
+ int cbSize;
+ const char *pszName; //name to refer to sound when playing and in db
+ const char *pszDescription; //description for options dialog
+ const char *pszSection; //section name used to group sounds (NULL is acceptable)
+ const char *pszService; //Service to call when HotKey Pressed
+
+ int DefHotKey; //default hot key for action
+} SKINHOTKEYDESCEX;
+
+#define MS_SKIN_ADDHOTKEY "Skin/HotKeys/AddNew"
+#define MS_SKIN_PLAYHOTKEY "Skin/HotKeys/Run"
+
+
+
+#endif // __COMMONS_H__
diff --git a/Plugins/quickcontacts/m_quickcontacts.h b/Plugins/quickcontacts/m_quickcontacts.h
new file mode 100644
index 0000000..5f8acb3
--- /dev/null
+++ b/Plugins/quickcontacts/m_quickcontacts.h
@@ -0,0 +1,39 @@
+/*
+Copyright (C) 2005 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#ifndef __M_QUICKCONTACTS_H__
+# define __M_QUICKCONTACTS_H__
+
+
+#define MIID_QUICKCONTACTS { 0x5e638aa7, 0x4989, 0x4dbf, { 0x94, 0xa, 0x1e, 0xcb, 0x6c, 0x52, 0xcc, 0x52 } }
+
+
+/*
+Show the dialog to select the contact
+
+wParam: ignored
+lParam: ignored
+*/
+#define MS_QC_SHOW_DIALOG "QuickContacts/ShowDialog"
+
+
+
+
+#endif // __M_QUICKCONTACTS_H__
diff --git a/Plugins/quickcontacts/options.cpp b/Plugins/quickcontacts/options.cpp
new file mode 100644
index 0000000..e74bee2
--- /dev/null
+++ b/Plugins/quickcontacts/options.cpp
@@ -0,0 +1,164 @@
+/*
+Copyright (C) 2006 Ricardo Pescuma Domenecci
+Based on work (C) Heiko Schillinger
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#include "options.h"
+
+
+
+// Prototypes /////////////////////////////////////////////////////////////////////////////////////
+
+HANDLE hOptHook = NULL;
+
+
+Options opts;
+
+static BOOL CALLBACK OptionsDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam);
+
+
+
+// Functions //////////////////////////////////////////////////////////////////////////////////////
+
+
+// Initializations needed by options
+void LoadOptions()
+{
+ opts.last_sent_enable = DBGetContactSettingByte(NULL, MODULE_NAME, "EnableLastSentTo", TRUE);
+ opts.last_sent_msg_type = DBGetContactSettingWord(NULL, MODULE_NAME, "MsgTypeRec", TYPE_GLOBAL);
+ opts.hide_from_offline_proto = DBGetContactSettingByte(NULL, MODULE_NAME, "HideOfflineFromOfflineProto", TRUE);
+ opts.group_append = DBGetContactSettingByte(NULL, MODULE_NAME, "AppendGroupName", FALSE);
+ opts.group_column = DBGetContactSettingByte(NULL, MODULE_NAME, "GroupColumn", FALSE);
+ opts.group_column_left = DBGetContactSettingByte(NULL, MODULE_NAME, "GroupColumnLeft", FALSE);
+ opts.hide_subcontacts = DBGetContactSettingByte(NULL, MODULE_NAME, "HideSubcontacts", TRUE);
+ opts.keep_subcontacts_from_offline = DBGetContactSettingByte(NULL, MODULE_NAME, "KeepSubcontactsFromOffline", TRUE);
+}
+
+int InitOptionsCallback(WPARAM wParam,LPARAM lParam)
+{
+ OPTIONSDIALOGPAGE odp;
+
+ ZeroMemory(&odp,sizeof(odp));
+ odp.cbSize=sizeof(odp);
+ odp.position=0;
+ odp.hInstance=hInst;
+ odp.ptszGroup = TranslateT("Plugins");
+ odp.ptszTitle = TranslateT("Quick Contacts");
+ odp.pfnDlgProc = OptionsDlgProc;
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT);
+ odp.flags = ODPF_BOLDGROUPS | ODPF_EXPERTONLY | ODPF_TCHAR;
+ CallService(MS_OPT_ADDPAGE,wParam,(LPARAM)&odp);
+
+ return 0;
+}
+
+
+void InitOptions()
+{
+ LoadOptions();
+
+ hOptHook = HookEvent(ME_OPT_INITIALISE, InitOptionsCallback);
+}
+
+// Deinitializations needed by options
+void DeInitOptions()
+{
+ UnhookEvent(hOptHook);
+}
+
+// Options page
+
+static OptPageControl controls[] = {
+ { NULL, CONTROL_CHECKBOX, IDC_LASTSENTTO, "EnableLastSentTo", (BYTE) TRUE },
+ { NULL, CONTROL_RADIO, IDC_GLOBAL, "MsgTypeRec", (WORD) TYPE_GLOBAL, TYPE_GLOBAL },
+ { NULL, CONTROL_RADIO, IDC_LOCAL, "MsgTypeRec", (WORD) TYPE_GLOBAL, TYPE_LOCAL },
+ { NULL, CONTROL_PROTOCOL_LIST, IDC_PROTOCOLS, "ShowOffline%s", (BYTE) FALSE },
+ { NULL, CONTROL_CHECKBOX, IDC_HIDE_OFFLINE, "HideOfflineFromOfflineProto", (BYTE) TRUE },
+ { NULL, CONTROL_CHECKBOX, IDC_APPEND_GROUP, "AppendGroupName", (BYTE) FALSE },
+ { NULL, CONTROL_CHECKBOX, IDC_GROUP_COLUMN, "GroupColumn", (BYTE) FALSE },
+ { NULL, CONTROL_CHECKBOX, IDC_GROUP_LEFT, "GroupColumnLeft", (BYTE) FALSE },
+ { NULL, CONTROL_CHECKBOX, IDC_SUBCONTACTS, "HideSubcontacts", (BYTE) TRUE },
+ { NULL, CONTROL_CHECKBOX, IDC_KEEP_OFFLINE, "KeepSubcontactsFromOffline", (BYTE) TRUE }
+};
+
+static BOOL CALLBACK OptionsDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ int ret = SaveOptsDlgProc(controls, MAX_REGS(controls), MODULE_NAME, hwndDlg, msg, wParam, lParam);
+
+ switch (msg)
+ {
+ case WM_INITDIALOG:
+ {
+ BOOL enabled = IsDlgButtonChecked(hwndDlg, IDC_LASTSENTTO);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_GLOBAL), enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_LOCAL), enabled);
+
+ enabled = IsDlgButtonChecked(hwndDlg, IDC_SUBCONTACTS);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_KEEP_OFFLINE), enabled);
+
+ if (metacontacts_proto == NULL)
+ {
+ ShowWindow(GetDlgItem(hwndDlg, IDC_SUBCONTACTS), SW_HIDE);
+ ShowWindow(GetDlgItem(hwndDlg, IDC_KEEP_OFFLINE), SW_HIDE);
+ }
+
+ return TRUE;
+ }
+ case WM_COMMAND:
+ {
+ if(LOWORD(wParam) == IDC_LASTSENTTO)
+ {
+ BOOL enabled = IsDlgButtonChecked(hwndDlg, IDC_LASTSENTTO);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_GLOBAL), enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_LOCAL), enabled);
+ }
+
+ if(LOWORD(wParam) == IDC_SUBCONTACTS)
+ {
+ BOOL enabled = IsDlgButtonChecked(hwndDlg, IDC_SUBCONTACTS);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_KEEP_OFFLINE), enabled);
+ }
+
+ break;
+ }
+ case WM_NOTIFY:
+ {
+ switch (((LPNMHDR)lParam)->idFrom)
+ {
+ case 0:
+ {
+ switch (((LPNMHDR)lParam)->code)
+ {
+ case PSN_APPLY:
+ {
+ LoadOptions();
+
+ return TRUE;
+ }
+ }
+ break;
+ }
+ }
+ break;
+ }
+ }
+
+ return ret;
+}
+
diff --git a/Plugins/quickcontacts/options.h b/Plugins/quickcontacts/options.h
new file mode 100644
index 0000000..1036261
--- /dev/null
+++ b/Plugins/quickcontacts/options.h
@@ -0,0 +1,65 @@
+/*
+Copyright (C) 2006 Ricardo Pescuma Domenecci
+Based on work (C) Heiko Schillinger
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#ifndef __OPTIONS_H__
+# define __OPTIONS_H__
+
+
+#include "commons.h"
+
+#include <windows.h>
+
+
+#define TYPE_GLOBAL 0
+#define TYPE_LOCAL 1
+
+typedef struct
+{
+ BOOL last_sent_enable;
+ int last_sent_msg_type;
+ BOOL hide_from_offline_proto;
+ BOOL hide_subcontacts;
+ BOOL keep_subcontacts_from_offline;
+ BOOL group_append;
+ BOOL group_column;
+ BOOL group_column_left;
+
+ int num_protos;
+} Options;
+
+extern Options opts;
+
+
+// Initializations needed by options
+void InitOptions();
+
+// Deinitializations needed by options
+void DeInitOptions();
+
+
+// Loads the options from DB
+// It don't need to be called, except in some rare cases
+void LoadOptions();
+
+
+
+
+#endif // __OPTIONS_H__
diff --git a/Plugins/quickcontacts/quickcontacts.cpp b/Plugins/quickcontacts/quickcontacts.cpp
new file mode 100644
index 0000000..0ad9c76
--- /dev/null
+++ b/Plugins/quickcontacts/quickcontacts.cpp
@@ -0,0 +1,1379 @@
+/*
+Copyright (C) 2006 Ricardo Pescuma Domenecci
+Based on work (C) Heiko Schillinger
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#include "commons.h"
+
+
+// Prototypes ///////////////////////////////////////////////////////////////////////////
+
+
+PLUGININFOEX pluginInfo={
+ sizeof(PLUGININFOEX),
+#ifdef UNICODE
+ "Quick Contacts (Unicode)",
+#else
+ "Quick Contacts",
+#endif
+ PLUGIN_MAKE_VERSION(1,0,0,0),
+ "Open contact-specific windows by hotkey",
+ "Ricardo Pescuma Domenecci, Heiko Schillinger",
+ "pescuma@miranda-im.org",
+ "© 2007-2009 Ricardo Pescuma Domenecci",
+ "http://pescuma.org/miranda/quickcontacts",
+ UNICODE_AWARE,
+ 0, //doesn't replace anything built-in
+#ifdef UNICODE
+ { 0xc679e1c9, 0x7967, 0x40ce, { 0x8a, 0x40, 0x95, 0x5b, 0x51, 0xde, 0x64, 0x3b } } // {C679E1C9-7967-40ce-8A40-955B51DE643B}
+#else
+ { 0xd3cc7943, 0xff2e, 0x4c2a, { 0xb3, 0xac, 0x6c, 0xe9, 0xbc, 0x83, 0x18, 0x78 } } // {D3CC7943-FF2E-4c2a-B3AC-6CE9BC831878}
+#endif
+};
+
+
+HINSTANCE hInst;
+PLUGINLINK *pluginLink;
+HIMAGELIST hIml;
+LIST_INTERFACE li;
+MM_INTERFACE mmi;
+UTF8_INTERFACE utfi;
+
+HANDLE hModulesLoaded = NULL;
+HANDLE hEventAdded = NULL;
+HANDLE hHotkeyPressed = NULL;
+
+long main_dialog_open = 0;
+HWND hwndMain = NULL;
+
+int ModulesLoaded(WPARAM wParam, LPARAM lParam);
+int EventAdded(WPARAM wparam, LPARAM lparam);
+int HotkeyPressed(WPARAM wParam, LPARAM lParam);
+int ShowDialog(WPARAM wParam,LPARAM lParam);
+void FreeContacts();
+
+int hksModule = 0;
+int hksAction = 0;
+
+BOOL hasNewHotkeyModule = FALSE;
+
+char *metacontacts_proto = NULL;
+
+#define IDC_ICO 12344
+
+
+// Functions ////////////////////////////////////////////////////////////////////////////
+
+
+extern "C" BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
+{
+ hInst = hinstDLL;
+ return TRUE;
+}
+
+
+extern "C" __declspec(dllexport) PLUGININFO* MirandaPluginInfo(DWORD mirandaVersion)
+{
+ pluginInfo.cbSize = sizeof(PLUGININFO);
+ return (PLUGININFO*) &pluginInfo;
+}
+
+
+extern "C" __declspec(dllexport) PLUGININFOEX* MirandaPluginInfoEx(DWORD mirandaVersion)
+{
+ pluginInfo.cbSize = sizeof(PLUGININFOEX);
+ return &pluginInfo;
+}
+
+
+static const MUUID interfaces[] = { MIID_QUICKCONTACTS, MIID_LAST };
+extern "C" __declspec(dllexport) const MUUID* MirandaPluginInterfaces(void)
+{
+ return interfaces;
+}
+
+
+extern "C" __declspec(dllexport) int Load(PLUGINLINK *link)
+{
+ pluginLink = link;
+
+ mir_getMMI(&mmi);
+ mir_getUTFI(&utfi);
+ mir_getLI(&li);
+
+ CreateServiceFunction(MS_QC_SHOW_DIALOG, ShowDialog);
+
+ // hooks
+ hModulesLoaded = HookEvent(ME_SYSTEM_MODULESLOADED, ModulesLoaded);
+ hEventAdded = HookEvent(ME_DB_EVENT_ADDED, EventAdded);
+
+ return 0;
+}
+
+extern "C" __declspec(dllexport) int Unload(void)
+{
+ FreeContacts();
+
+ DeInitOptions();
+
+ UnhookEvent(hModulesLoaded);
+ UnhookEvent(hEventAdded);
+
+ return 0;
+}
+
+
+// Called when all the modules are loaded
+int ModulesLoaded(WPARAM wParam, LPARAM lParam)
+{
+ InitOptions();
+
+ // add our modules to the KnownModules list
+ CallService("DBEditorpp/RegisterSingleModule", (WPARAM) MODULE_NAME, 0);
+
+ // updater plugin support
+ if(ServiceExists(MS_UPDATE_REGISTER))
+ {
+ Update upd = {0};
+ char szCurrentVersion[30];
+
+ upd.cbSize = sizeof(upd);
+ upd.szComponentName = pluginInfo.shortName;
+
+ upd.szUpdateURL = UPDATER_AUTOREGISTER;
+
+ upd.szBetaVersionURL = "http://pescuma.mirandaim.ru/miranda/quickcontacts_version.txt";
+ upd.szBetaChangelogURL = "http://pescuma.mirandaim.ru/miranda/quickcontacts#Changelog";
+ upd.pbBetaVersionPrefix = (BYTE *)"Quick Contacts ";
+ upd.cpbBetaVersionPrefix = strlen((char *)upd.pbBetaVersionPrefix);
+#ifdef UNICODE
+ upd.szBetaUpdateURL = "http://pescuma.mirandaim.ru/miranda/quickcontactsW.zip";
+#else
+ upd.szBetaUpdateURL = "http://pescuma.mirandaim.ru/miranda/quickcontacts.zip";
+#endif
+
+ upd.pbVersion = (BYTE *)CreateVersionStringPlugin((PLUGININFO*) &pluginInfo, szCurrentVersion);
+ upd.cpbVersion = strlen((char *)upd.pbVersion);
+
+ CallService(MS_UPDATE_REGISTER, 0, (LPARAM)&upd);
+ }
+
+ // Get number of protocols
+ int pcount;
+ PROTOCOLDESCRIPTOR** pdesc;
+
+ CallService(MS_PROTO_ENUMPROTOCOLS,(WPARAM)(int*)&pcount,(LPARAM)(PROTOCOLDESCRIPTOR***)&pdesc);
+
+ opts.num_protos = 0;
+ for (int loop=0;loop<pcount;loop++)
+ {
+ if (pdesc[loop]->type==PROTOTYPE_PROTOCOL)
+ opts.num_protos++;
+ }
+
+ // Add hotkey to multiple services
+
+ if (ServiceExists(MS_HOTKEY_REGISTER))
+ {
+ hasNewHotkeyModule = TRUE;
+
+ HOTKEYDESC hkd = {0};
+ hkd.cbSize = sizeof(hkd);
+ hkd.pszName = Translate("Quick Contacts/Open dialog");
+ hkd.pszDescription = Translate("Open dialog");
+ hkd.pszSection = Translate("Quick Contacts");
+ hkd.pszService = MS_QC_SHOW_DIALOG;
+ hkd.DefHotKey = HOTKEYCODE(HOTKEYF_CONTROL|HOTKEYF_ALT, 'Q');
+ CallService(MS_HOTKEY_REGISTER, 0, (LPARAM)&hkd);
+
+ hkd.pszService = NULL;
+
+ hkd.lParam = HOTKEY_VOICE;
+ hkd.DefHotKey = HOTKEYCODE(HOTKEYF_CONTROL, 'V');
+ hkd.pszName = Translate("Quick Contacts/Voice");
+ hkd.pszDescription = Translate("Make a voice call");
+ CallService(MS_HOTKEY_REGISTER, 0, (LPARAM)&hkd);
+
+ hkd.lParam = HOTKEY_FILE;
+ hkd.DefHotKey = HOTKEYCODE(HOTKEYF_CONTROL, 'F');
+ hkd.pszName = Translate("Quick Contacts/File");
+ hkd.pszDescription = Translate("Send file");
+ CallService(MS_HOTKEY_REGISTER, 0, (LPARAM)&hkd);
+
+ hkd.lParam = HOTKEY_URL;
+ hkd.DefHotKey = HOTKEYCODE(HOTKEYF_CONTROL, 'U');
+ hkd.pszName = Translate("Quick Contacts/URL");
+ hkd.pszDescription = Translate("Send URL");
+ CallService(MS_HOTKEY_REGISTER, 0, (LPARAM)&hkd);
+
+ hkd.lParam = HOTKEY_INFO;
+ hkd.DefHotKey = HOTKEYCODE(HOTKEYF_CONTROL, 'I');
+ hkd.pszName = Translate("Quick Contacts/Info");
+ hkd.pszDescription = Translate("Open userinfo");
+ CallService(MS_HOTKEY_REGISTER, 0, (LPARAM)&hkd);
+
+ hkd.lParam = HOTKEY_HISTORY;
+ hkd.DefHotKey = HOTKEYCODE(HOTKEYF_CONTROL, 'H');
+ hkd.pszName = Translate("Quick Contacts/History");
+ hkd.pszDescription = Translate("Open history");
+ CallService(MS_HOTKEY_REGISTER, 0, (LPARAM)&hkd);
+
+ hkd.lParam = HOTKEY_MENU;
+ hkd.DefHotKey = HOTKEYCODE(HOTKEYF_CONTROL, 'M');
+ hkd.pszName = Translate("Quick Contacts/Menu");
+ hkd.pszDescription = Translate("Open contact menu");
+ CallService(MS_HOTKEY_REGISTER, 0, (LPARAM)&hkd);
+
+ hkd.lParam = HOTKEY_ALL_CONTACTS;
+ hkd.DefHotKey = HOTKEYCODE(HOTKEYF_CONTROL, 'A');
+ hkd.pszName = Translate("Quick Contacts/All Contacts");
+ hkd.pszDescription = Translate("Show all contacts");
+ CallService(MS_HOTKEY_REGISTER, 0, (LPARAM)&hkd);
+ }
+
+ hksModule = HKS_RegisterModule("Quick Contacts");
+ if (hksModule >= 0)
+ {
+ hksAction = HKS_RegisterAction(hksModule, "Open dialog", MOD_CONTROL | MOD_ALT | MOD_GLOBAL, 'Q', 0);
+
+ hHotkeyPressed = HookEvent(ME_HKS_KEY_PRESSED, HotkeyPressed);
+ }
+
+ if (ServiceExists(MS_SKIN_ADDHOTKEY))
+ {
+ SKINHOTKEYDESCEX hk = {0};
+ hk.cbSize = sizeof(hk);
+ hk.pszSection = Translate("Quick Contacts");
+ hk.pszName = Translate("Open dialog");
+ hk.pszDescription = Translate("Open dialog");
+ hk.pszService = MS_QC_SHOW_DIALOG;
+ hk.DefHotKey = 0;
+ CallService(MS_SKIN_ADDHOTKEY, 0, (LPARAM)&hk);
+ }
+
+ if (ServiceExists(MS_HOTKEYSPLUS_ADDKEY))
+ CallService(MS_HOTKEYSPLUS_ADDKEY, (WPARAM) MS_QC_SHOW_DIALOG, (LPARAM) "Open Quick Contacts dialog");
+
+ // Get the icons for the listbox
+ hIml = (HIMAGELIST)CallService(MS_CLIST_GETICONSIMAGELIST,0,0);
+
+ // Add menu item
+ CLISTMENUITEM mi;
+ ZeroMemory(&mi,sizeof(mi));
+ mi.cbSize = sizeof(mi);
+ mi.position = 500100001;
+ mi.flags = 0;
+ mi.pszName = Translate("Quick Contacts...");
+ mi.pszService = MS_QC_SHOW_DIALOG;
+ CallService(MS_CLIST_ADDMAINMENUITEM,0,(LPARAM)&mi);
+
+ if (ServiceExists(MS_MC_GETPROTOCOLNAME) && ServiceExists(MS_MC_GETMETACONTACT))
+ metacontacts_proto = (char *) CallService(MS_MC_GETPROTOCOLNAME, 0, 0);
+
+ return 0;
+}
+
+
+// called when a message/file/url was sent
+// handle of contact is set as window-userdata
+int EventAdded(WPARAM wparam, LPARAM lparam)
+{
+ DBEVENTINFO dbei;
+
+ ZeroMemory(&dbei,sizeof(dbei));
+ dbei.cbSize=sizeof(dbei);
+ dbei.cbBlob=0;
+
+ CallService(MS_DB_EVENT_GET,lparam,(LPARAM)&dbei);
+
+ if( !(dbei.flags & DBEF_SENT)
+ || dbei.flags & DBEF_READ
+ || !DBGetContactSettingByte(NULL, MODULE_NAME, "EnableLastSentTo", 0)
+ || DBGetContactSettingWord(NULL, MODULE_NAME, "MsgTypeRec", TYPE_GLOBAL) != TYPE_GLOBAL)
+ return 0;
+
+ DBWriteContactSettingDword(NULL, MODULE_NAME, "LastSentTo", (DWORD)(HANDLE)wparam);
+ return 0;
+}
+
+
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+
+#define IDC_ENTER 2000 // Pseudo control to handle enter in the main window
+
+
+// array where the contacts are put into
+struct c_struct {
+ TCHAR szname[120];
+ TCHAR szgroup[50];
+ HANDLE hcontact;
+ TCHAR proto[20];
+
+ c_struct()
+ {
+ szname[0] = 0;
+ szgroup[0] = 0;
+ hcontact = 0;
+ proto[0] = 0;
+ }
+};
+
+LIST<c_struct> contacts(200);
+long max_proto_width;
+
+
+// Get the name the contact has in list
+// This was not made to be called by more than one thread!
+TCHAR tmp_list_name[120];
+
+TCHAR *GetListName(c_struct *cs)
+{
+ if (opts.group_append && cs->szgroup[0] != _T('\0'))
+ {
+ mir_sntprintf(tmp_list_name, MAX_REGS(tmp_list_name), _T("%s (%s)"), cs->szname, cs->szgroup);
+ return tmp_list_name;
+ }
+ else
+ {
+ return cs->szname;
+ }
+}
+
+
+int lstreq(TCHAR *a, TCHAR *b, size_t len = -1)
+{
+#ifdef UNICODE
+ a = CharLower(_tcsdup(a));
+ b = CharLower(_tcsdup(b));
+ int ret;
+ if (len > 0)
+ ret = _tcsncmp(a, b, len);
+ else
+ ret = _tcscmp(a, b);
+ free(a);
+ free(b);
+ return ret;
+#else
+ if (len > 0)
+ return _tcsnicmp(a, b, len);
+ else
+ return _tcsicmp(a, b);
+#endif
+}
+
+
+// simple sorting function to have
+// the contact array in alphabetical order
+void SortArray(void)
+{
+ int loop,doop;
+ c_struct *cs_temp;
+
+ SortedList *sl = (SortedList *) &contacts;
+ for(loop=0;loop<contacts.getCount();loop++)
+ {
+ for(doop=loop+1;doop<contacts.getCount();doop++)
+ {
+ int cmp = lstreq(contacts[loop]->szname,contacts[doop]->szname);
+ if (cmp > 0)
+ {
+ cs_temp=contacts[loop];
+ sl->items[loop]=contacts[doop];
+ sl->items[doop]=cs_temp;
+ }
+ else if (cmp == 0)
+ {
+ if(lstreq(contacts[loop]->proto, contacts[doop]->proto) > 0)
+ {
+ cs_temp=contacts[loop];
+ sl->items[loop]=contacts[doop];
+ sl->items[doop]=cs_temp;
+ }
+ }
+
+ }
+ }
+}
+
+
+int GetStatus(HANDLE hContact, char *proto = NULL)
+{
+ if (proto == NULL)
+ proto = (char *) CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM) hContact, 0);
+
+ if (proto == NULL)
+ return ID_STATUS_OFFLINE;
+
+ return DBGetContactSettingWord(hContact, proto, "Status", ID_STATUS_OFFLINE);
+}
+
+
+void FreeContacts()
+{
+ for (int i = contacts.getCount() - 1; i >= 0; i--)
+ {
+ delete contacts[i];
+ contacts.remove(i);
+ }
+}
+
+
+void LoadContacts(HWND hwndDlg, BOOL show_all)
+{
+ BOOL hasAccounts = ServiceExists(MS_PROTO_GETACCOUNT);
+ BOOL metacontactsEnabled = (metacontacts_proto != NULL
+ && DBGetContactSettingByte(0, metacontacts_proto, "Enabled", 1));
+
+ // Read last-sent-to contact from db and set handle as window-userdata
+ HANDLE hlastsent = (HANDLE)DBGetContactSettingDword(NULL, MODULE_NAME, "LastSentTo", -1);
+ SetWindowLong(hwndMain, GWL_USERDATA, (LONG)hlastsent);
+
+ // enumerate all contacts and write them to the array
+ // item data of listbox-strings is the array position
+ FreeContacts();
+ for(HANDLE hContact = (HANDLE)CallService(MS_DB_CONTACT_FINDFIRST,0,0);
+ hContact != NULL;
+ hContact = (HANDLE)CallService(MS_DB_CONTACT_FINDNEXT,(WPARAM)hContact,0))
+ {
+ char *pszProto = (char *) CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0);
+ if(pszProto != NULL)
+ {
+ // Get meta
+ HANDLE hMeta = NULL;
+ if (metacontactsEnabled)
+ {
+ if ((!show_all && opts.hide_subcontacts) || opts.group_append)
+ hMeta = (HANDLE) CallService(MS_MC_GETMETACONTACT, (WPARAM)hContact, 0);
+ }
+ else
+ {
+ if (metacontacts_proto != NULL && strcmp(metacontacts_proto, pszProto) == 0)
+ continue;
+ }
+
+
+ if (!show_all)
+ {
+ // Check if is offline and have to show
+ if (GetStatus(hContact, pszProto) <= ID_STATUS_OFFLINE)
+ {
+ // See if has to show
+ char setting[128];
+ mir_snprintf(setting, sizeof(setting), "ShowOffline%s", pszProto);
+
+ if (!DBGetContactSettingByte(NULL, MODULE_NAME, setting, FALSE))
+ continue;
+
+ // Check if proto offline
+ else if (opts.hide_from_offline_proto
+ && CallProtoService(pszProto, PS_GETSTATUS, 0, 0) <= ID_STATUS_OFFLINE)
+ continue;
+
+ }
+
+ // Check if is subcontact
+ if (opts.hide_subcontacts && hMeta != NULL)
+ {
+ if (!opts.keep_subcontacts_from_offline)
+ continue;
+
+ if (GetStatus(hMeta, metacontacts_proto) > ID_STATUS_OFFLINE)
+ {
+ continue;
+ }
+ else
+ {
+ char setting[128];
+ mir_snprintf(setting, sizeof(setting), "ShowOffline%s", metacontacts_proto);
+ if (DBGetContactSettingByte(NULL, MODULE_NAME, setting, FALSE))
+ continue;
+ }
+ }
+ }
+
+ // Add to list
+
+ // Get group
+ c_struct *contact = new c_struct();
+
+ if (opts.group_append)
+ {
+ DBVARIANT dbv;
+ if (DBGetContactSettingTString(hMeta == NULL ? hContact : hMeta, "CList", "Group", &dbv) == 0)
+ {
+ if (dbv.ptszVal != NULL)
+ lstrcpyn(contact->szgroup, dbv.ptszVal, MAX_REGS(contact->szgroup));
+
+ DBFreeVariant(&dbv);
+ }
+ }
+
+ // Make contact name
+ TCHAR *tmp = (TCHAR *) CallService(MS_CLIST_GETCONTACTDISPLAYNAME, (WPARAM) hContact, GCDNF_TCHAR);
+ lstrcpyn(contact->szname, tmp, MAX_REGS(contact->szname));
+
+ PROTOACCOUNT *acc = (hasAccounts ? ProtoGetAccount(pszProto) : NULL);
+ if (acc != NULL)
+ {
+ lstrcpyn(contact->proto, acc->tszAccountName, MAX_REGS(contact->proto));
+ }
+ else
+ {
+ char szName[128];
+ CallProtoService(pszProto, PS_GETNAME, sizeof(szName), (LPARAM) szName);
+ lstrcpyn(contact->proto, CharToTchar(szName), MAX_REGS(contact->proto));
+ }
+
+ contact->hcontact = hContact;
+
+ contacts.insert(contact);
+ }
+ }
+
+ SortArray();
+
+ SendDlgItemMessage(hwndDlg, IDC_USERNAME, CB_RESETCONTENT, 0, 0);
+ for(int loop = 0; loop < contacts.getCount(); loop++)
+ {
+ SendDlgItemMessage(hwndDlg, IDC_USERNAME, CB_SETITEMDATA,
+ (WPARAM)SendDlgItemMessage(hwndDlg, IDC_USERNAME,
+ CB_ADDSTRING, 0, (LPARAM) GetListName(contacts[loop])),
+ (LPARAM)loop);
+ }
+}
+
+
+// Enable buttons for the selected contact
+void EnableButtons(HWND hwndDlg, HANDLE hContact)
+{
+ if (hContact == NULL)
+ {
+ EnableWindow(GetDlgItem(hwndDlg, IDC_MESSAGE), FALSE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_VOICE), FALSE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_FILE), FALSE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_URL), FALSE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_USERINFO), FALSE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_HISTORY), FALSE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_MENU), FALSE);
+
+ SendMessage(GetDlgItem(hwndDlg, IDC_ICO), STM_SETICON, 0, 0);
+ }
+ else
+ {
+ // Is a meta?
+ if (ServiceExists(MS_MC_GETMOSTONLINECONTACT))
+ {
+ HANDLE hSub = (HANDLE) CallService(MS_MC_GETMOSTONLINECONTACT, (WPARAM) hContact, 0);
+ if (hSub != NULL)
+ hContact = hSub;
+ }
+
+ // Get caps
+ int caps = 0;
+
+ char *pszProto = (char *) CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0);
+ if (pszProto != NULL)
+ caps = CallProtoService(pszProto, PS_GETCAPS, PFLAGNUM_1, 0);
+
+ BOOL voice = (ServiceExists(MS_VOICESERVICE_CAN_CALL)
+ && CallService(MS_VOICESERVICE_CAN_CALL, (WPARAM)hContact, 0) > 0);
+
+ EnableWindow(GetDlgItem(hwndDlg, IDC_MESSAGE), caps & PF1_IMSEND ? TRUE : FALSE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_VOICE), voice);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_FILE), caps & PF1_FILESEND ? TRUE : FALSE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_URL), caps & PF1_URLSEND ? TRUE : FALSE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_USERINFO), TRUE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_HISTORY), TRUE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_MENU), TRUE);
+
+ HICON ico = ImageList_GetIcon(hIml, CallService(MS_CLIST_GETCONTACTICON, (WPARAM) hContact, 0), ILD_IMAGE);
+ SendMessage(GetDlgItem(hwndDlg, IDC_ICO), STM_SETICON, (WPARAM) ico, 0);
+ }
+}
+
+
+// check if the char(s) entered appears in a contacts name
+int CheckText(HWND hdlg, TCHAR *sztext, BOOL only_enable = FALSE)
+{
+ EnableButtons(hwndMain, NULL);
+
+ if(sztext == NULL || sztext[0] == _T('\0'))
+ return 0;
+
+ int len = lstrlen(sztext);
+
+ if (only_enable)
+ {
+ for(int loop=0;loop<contacts.getCount();loop++)
+ {
+ if(lstreq(sztext, contacts[loop]->szname)==0 || lstreq(sztext, GetListName(contacts[loop]))==0)
+ {
+ EnableButtons(hwndMain, contacts[loop]->hcontact);
+ return 0;
+ }
+ }
+ }
+ else
+ {
+ for(int loop=0;loop<contacts.getCount();loop++)
+ {
+ if (lstreq(sztext, GetListName(contacts[loop]), len) == 0)
+ {
+ SendMessage(hdlg, WM_SETTEXT, 0, (LPARAM) GetListName(contacts[loop]));
+ SendMessage(hdlg, EM_SETSEL, (WPARAM) len, (LPARAM) -1);
+ EnableButtons(hwndMain, contacts[loop]->hcontact);
+ return 0;
+ }
+ }
+ }
+
+ EnableButtons(hwndMain, NULL);
+ return 0;
+}
+
+HANDLE GetSelectedContact(HWND hwndDlg)
+{
+ // First try selection
+ int sel = SendDlgItemMessage(hwndDlg, IDC_USERNAME, CB_GETCURSEL, 0, 0);
+
+ if (sel != CB_ERR)
+ {
+ int pos = SendDlgItemMessage(hwndDlg, IDC_USERNAME, CB_GETITEMDATA, sel, 0);
+ if (pos != CB_ERR)
+ return contacts[pos]->hcontact;
+ }
+
+ // Now try the name
+ TCHAR cname[120] = _T("");
+
+ GetDlgItemText(hwndDlg, IDC_USERNAME, cname, MAX_REGS(cname));
+
+ for(int loop = 0; loop < contacts.getCount(); loop++)
+ {
+ if(!lstrcmpi(cname, GetListName(contacts[loop])))
+ return contacts[loop]->hcontact;
+ }
+
+ return NULL;
+}
+
+// get array position from handle
+int GetItemPos(HANDLE hcontact)
+{
+ int loop;
+
+ for(loop=0;loop<contacts.getCount();loop++)
+ {
+ if(hcontact==contacts[loop]->hcontact)
+ return loop;
+ }
+ return -1;
+}
+
+
+WNDPROC wpEditMainProc;
+
+// callback function for edit-box of the listbox
+// without this the autofill function isn't possible
+// this was done like ie does it..as far as spy++ could tell ;)
+LRESULT CALLBACK EditProc(HWND hdlg,UINT msg,WPARAM wparam,LPARAM lparam)
+{
+ switch(msg)
+ {
+ case WM_CHAR:
+ {
+ if (wparam<32 && wparam != VK_BACK)
+ break;
+
+ TCHAR sztext[120] = _T("");
+ DWORD start;
+ DWORD end;
+
+ int ret = SendMessage(hdlg,EM_GETSEL,(WPARAM)&start,(LPARAM)&end);
+
+ SendMessage(hdlg,WM_GETTEXT,(WPARAM)MAX_REGS(sztext),(LPARAM)sztext);
+
+ BOOL at_end = (lstrlen(sztext) == (int)end);
+
+ if (ret != -1)
+ {
+ if (wparam == VK_BACK)
+ {
+ if (start > 0)
+ SendMessage(hdlg,EM_SETSEL,(WPARAM)start-1,(LPARAM)end);
+
+ sztext[0]=0;
+ }
+ else
+ {
+ sztext[0]=wparam;
+ sztext[1]=0;
+ }
+
+ SendMessage(hdlg,EM_REPLACESEL,(WPARAM)0,(LPARAM)sztext);
+ SendMessage(hdlg,WM_GETTEXT,(WPARAM)MAX_REGS(sztext),(LPARAM)sztext);
+ }
+
+ CheckText(hdlg, sztext, !at_end);
+
+ return 1;
+ }
+ case WM_KEYUP:
+ {
+ TCHAR sztext[120] = _T("");
+
+ if (wparam == VK_RETURN)
+ {
+ switch(SendMessage(GetParent(hdlg),CB_GETDROPPEDSTATE,0,0))
+ {
+ case FALSE:
+ SendMessage(GetParent(GetParent(hdlg)),WM_COMMAND,MAKEWPARAM(IDC_ENTER,STN_CLICKED),0);
+ break;
+
+ case TRUE:
+ SendMessage(GetParent(hdlg),CB_SHOWDROPDOWN,(WPARAM)FALSE,0);
+ break;
+ }
+ }
+ else if (wparam == VK_DELETE)
+ {
+ SendMessage(hdlg,WM_GETTEXT,(WPARAM)MAX_REGS(sztext),(LPARAM)sztext);
+ CheckText(hdlg, sztext, TRUE);
+ }
+
+ return 0;
+ }
+ case WM_GETDLGCODE:
+ return DLGC_WANTCHARS|DLGC_WANTARROWS;
+
+ }
+
+ return CallWindowProc(wpEditMainProc,hdlg,msg,wparam,lparam);
+}
+
+
+HACCEL hAcct;
+HHOOK hHook;
+
+// This function filters the message queue and translates
+// the keyboard accelerators
+LRESULT CALLBACK HookProc(int code, WPARAM wparam, LPARAM lparam)
+{
+ MSG *msg;
+ HWND htemp;
+
+ if (code!=MSGF_DIALOGBOX)
+ return 0;
+
+ msg = (MSG*)lparam;
+
+
+ if (hasNewHotkeyModule)
+ {
+ int action = CallService(MS_HOTKEY_CHECK, (WPARAM) msg, (LPARAM) "Quick Contacts");
+ if (action != 0)
+ {
+ SendMessage(hwndMain, WM_COMMAND, action, 0);
+ return 1;
+ }
+ }
+ else
+ {
+ htemp = msg->hwnd;
+ msg->hwnd = hwndMain;
+
+ if (TranslateAccelerator(msg->hwnd, hAcct, msg))
+ return 1;
+
+ msg->hwnd=htemp;
+ }
+
+ if (msg->message == WM_KEYDOWN && msg->wParam == VK_ESCAPE)
+ {
+ switch(SendMessage(GetDlgItem(hwndMain, IDC_USERNAME), CB_GETDROPPEDSTATE, 0, 0))
+ {
+ case FALSE:
+ SendMessage(hwndMain, WM_CLOSE, 0, 0);
+ break;
+
+ case TRUE:
+ SendMessage(GetDlgItem(hwndMain, IDC_USERNAME), CB_SHOWDROPDOWN, (WPARAM)FALSE, 0);
+ break;
+ }
+ }
+
+ return 0;
+}
+
+BOOL ScreenToClient(HWND hWnd, LPRECT lpRect)
+{
+ BOOL ret;
+
+ POINT pt;
+
+ pt.x = lpRect->left;
+ pt.y = lpRect->top;
+
+ ret = ScreenToClient(hWnd, &pt);
+
+ if (!ret) return ret;
+
+ lpRect->left = pt.x;
+ lpRect->top = pt.y;
+
+
+ pt.x = lpRect->right;
+ pt.y = lpRect->bottom;
+
+ ret = ScreenToClient(hWnd, &pt);
+
+ lpRect->right = pt.x;
+ lpRect->bottom = pt.y;
+
+ return ret;
+}
+
+
+BOOL MoveWindow(HWND hWnd, const RECT &rect, BOOL bRepaint)
+{
+ return MoveWindow(hWnd, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, bRepaint);
+}
+
+
+static void FillButton(HWND hwndDlg, int dlgItem, char *name, char *key, HICON icon)
+{
+ char tmp[256];
+ char *full;
+ if (key == NULL)
+ full = Translate(name);
+ else
+ mir_snprintf(full = tmp, MAX_REGS(tmp), "%s (%s)", Translate(name), key);
+
+ SendMessage(GetDlgItem(hwndDlg, dlgItem), BUTTONSETASFLATBTN, 0, 0);
+ SendMessageA(GetDlgItem(hwndDlg, dlgItem), BUTTONADDTOOLTIP, (LPARAM) full, 0);
+ SendDlgItemMessage(hwndDlg, dlgItem, BM_SETIMAGE, IMAGE_ICON, (LPARAM) icon);
+}
+
+
+static void FillCheckbox(HWND hwndDlg, int dlgItem, char *name, char *key)
+{
+ char tmp[256];
+ char *full;
+ if (key == NULL)
+ full = Translate(name);
+ else
+ mir_snprintf(full = tmp, MAX_REGS(tmp), "%s (%s)", Translate(name), key);
+
+ SendMessageA(GetDlgItem(hwndDlg, dlgItem), WM_SETTEXT, 0, (LPARAM) full);
+}
+
+
+static BOOL CALLBACK MainDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg)
+ {
+ case WM_INITDIALOG:
+ {
+ TranslateDialogDefault(hwndDlg);
+
+ RECT rc;
+ GetWindowRect(GetDlgItem(hwndDlg, IDC_USERNAME), &rc);
+ ScreenToClient(hwndDlg, &rc);
+
+ HWND icon = CreateWindow(_T("STATIC"), _T(""), WS_CHILD | WS_VISIBLE | SS_ICON | SS_CENTERIMAGE,
+ rc.left - 20, rc.top + (rc.bottom - rc.top - 16) / 2, 16, 16, hwndDlg, (HMENU) IDC_ICO,
+ hInst, NULL);
+
+ if (!hasNewHotkeyModule)
+ hAcct = LoadAccelerators(hInst, MAKEINTRESOURCE(ACCEL_TABLE));
+
+ hHook = SetWindowsHookEx(WH_MSGFILTER, HookProc, hInst, GetCurrentThreadId());
+
+ // Combo
+ SendMessage(GetDlgItem(hwndDlg, IDC_USERNAME), EM_LIMITTEXT, (WPARAM)119,0);
+ wpEditMainProc = (WNDPROC) SetWindowLong(GetWindow(GetDlgItem(hwndDlg, IDC_USERNAME),GW_CHILD), GWL_WNDPROC, (LONG)EditProc);
+
+ // Buttons
+ FillCheckbox(hwndDlg, IDC_SHOW_ALL_CONTACTS, "Show all contacts", hasNewHotkeyModule ? NULL : "Ctrl+A");
+ FillButton(hwndDlg, IDC_MESSAGE, "Send message", NULL, LoadSkinnedIcon(SKINICON_EVENT_MESSAGE));
+
+ if (ServiceExists(MS_VOICESERVICE_CAN_CALL))
+ {
+ FillButton(hwndDlg, IDC_VOICE, "Make a voice call", hasNewHotkeyModule ? NULL : "Ctrl+V", (HICON) CallService(MS_SKIN2_GETICON, 0, (LPARAM) "vca_call"));
+ }
+ else
+ {
+ GetWindowRect(GetDlgItem(hwndDlg, IDC_VOICE), &rc);
+ ScreenToClient(hwndDlg, &rc);
+ MoveWindow(GetDlgItem(hwndDlg, IDC_MESSAGE), rc, FALSE);
+ ShowWindow(GetDlgItem(hwndDlg, IDC_VOICE), SW_HIDE);
+ }
+
+ FillButton(hwndDlg, IDC_FILE, "Send file", hasNewHotkeyModule ? NULL : "Ctrl+F", LoadSkinnedIcon(SKINICON_EVENT_FILE));
+ FillButton(hwndDlg, IDC_URL, "Send URL", hasNewHotkeyModule ? NULL : "Ctrl+U", LoadSkinnedIcon(SKINICON_EVENT_URL));
+ FillButton(hwndDlg, IDC_USERINFO, "Open userinfo", hasNewHotkeyModule ? NULL : "Ctrl+I", (HICON) LoadImage(GetModuleHandle(NULL),MAKEINTRESOURCE(160),IMAGE_ICON,16,16,LR_DEFAULTCOLOR));
+ FillButton(hwndDlg, IDC_HISTORY, "Open history", hasNewHotkeyModule ? NULL : "Ctrl+H", (HICON) LoadImage(GetModuleHandle(NULL),MAKEINTRESOURCE(174),IMAGE_ICON,16,16,LR_DEFAULTCOLOR));
+ FillButton(hwndDlg, IDC_MENU, "Open contact menu", hasNewHotkeyModule ? NULL : "Ctrl+M", (HICON) LoadImage(GetModuleHandle(NULL),MAKEINTRESOURCE(264),IMAGE_ICON,16,16,LR_DEFAULTCOLOR));
+
+ SendDlgItemMessage(hwndDlg, IDC_USERNAME, CB_SETEXTENDEDUI, (WPARAM)TRUE, 0);
+
+ MagneticWindows_AddWindow(hwndDlg);
+
+ Utils_RestoreWindowPositionNoSize(hwndDlg, NULL, MODULE_NAME, "window");
+
+ LoadContacts(hwndDlg, FALSE);
+
+ EnableButtons(hwndDlg, NULL);
+ if (DBGetContactSettingByte(NULL, MODULE_NAME, "EnableLastSentTo", 0))
+ {
+ int pos = GetItemPos((HANDLE) DBGetContactSettingDword(NULL, MODULE_NAME, "LastSentTo", -1));
+
+ if (pos != -1)
+ {
+ SendDlgItemMessage(hwndDlg, IDC_USERNAME, CB_SETCURSEL, (WPARAM) pos, 0);
+ EnableButtons(hwndDlg, contacts[pos]->hcontact);
+ }
+ }
+
+ SetFocus(GetDlgItem(hwndDlg, IDC_USERNAME));
+
+ return TRUE;
+ }
+
+ case WM_COMMAND:
+ {
+ switch(LOWORD(wParam))
+ {
+ case IDC_USERNAME:
+ {
+ if (HIWORD(wParam) == CBN_SELCHANGE)
+ {
+ int pos = SendDlgItemMessage(hwndDlg, IDC_USERNAME, CB_GETCURSEL, 0, 0);
+ EnableButtons(hwndDlg, pos < contacts.getCount() ? contacts[pos]->hcontact : NULL);
+ }
+ break;
+ }
+ case IDC_ENTER:
+ {
+ HANDLE hContact = GetSelectedContact(hwndDlg);
+ if (hContact == NULL)
+ break;
+
+ CallService(MS_CLIST_CONTACTDOUBLECLICKED, (WPARAM) hContact, 0);
+
+ DBWriteContactSettingDword(NULL, MODULE_NAME, "LastSentTo", (DWORD) hContact);
+ SendMessage(hwndDlg, WM_CLOSE, 0, 0);
+ break;
+ }
+ case IDC_MESSAGE:
+ {
+ HANDLE hContact = GetSelectedContact(hwndDlg);
+ if (hContact == NULL)
+ {
+ SetDlgItemText(hwndDlg, IDC_USERNAME, _T(""));
+ SetFocus(GetDlgItem(hwndDlg, IDC_USERNAME));
+ break;
+ }
+
+ // Is button enabled?
+ if (!IsWindowEnabled(GetDlgItem(hwndDlg, IDC_MESSAGE)))
+ break;
+
+ // don't know why it doesn't work with MS_MSG_SENDMESSAGE
+ // when convers is enabled
+ if (ServiceExists("SRMsg/LaunchMessageWindow"))
+ CallService("SRMsg/LaunchMessageWindow", (WPARAM) hContact, 0);
+ else
+ CallService(MS_MSG_SENDMESSAGE, (WPARAM) hContact, 0);
+
+ DBWriteContactSettingDword(NULL, MODULE_NAME, "LastSentTo", (DWORD) hContact);
+ SendMessage(hwndDlg, WM_CLOSE, 0, 0);
+ break;
+ }
+ case HOTKEY_VOICE:
+ case IDC_VOICE:
+ {
+ HANDLE hContact = GetSelectedContact(hwndDlg);
+ if (hContact == NULL)
+ {
+ SetDlgItemText(hwndDlg, IDC_USERNAME, _T(""));
+ SetFocus(GetDlgItem(hwndDlg, IDC_USERNAME));
+ break;
+ }
+
+ // Is button enabled?
+ if (!IsWindowEnabled(GetDlgItem(hwndDlg, IDC_VOICE)))
+ break;
+
+ if (!ServiceExists(MS_VOICESERVICE_CALL))
+ break;
+
+ CallService(MS_VOICESERVICE_CALL, (WPARAM) hContact, 0);
+
+ DBWriteContactSettingDword(NULL, MODULE_NAME, "LastSentTo", (DWORD) hContact);
+ SendMessage(hwndDlg, WM_CLOSE, 0, 0);
+ break;
+ }
+ case HOTKEY_FILE:
+ case IDC_FILE:
+ {
+ HANDLE hContact = GetSelectedContact(hwndDlg);
+ if (hContact == NULL)
+ {
+ SetDlgItemText(hwndDlg, IDC_USERNAME, _T(""));
+ SetFocus(GetDlgItem(hwndDlg, IDC_USERNAME));
+ break;
+ }
+
+ // Is button enabled?
+ if (!IsWindowEnabled(GetDlgItem(hwndDlg, IDC_FILE)))
+ break;
+
+ CallService(MS_FILE_SENDFILE, (WPARAM) hContact, 0);
+
+ DBWriteContactSettingDword(NULL, MODULE_NAME, "LastSentTo", (DWORD) hContact);
+ SendMessage(hwndDlg, WM_CLOSE, 0, 0);
+ break;
+ }
+ case HOTKEY_URL:
+ case IDC_URL:
+ {
+ HANDLE hContact = GetSelectedContact(hwndDlg);
+ if (hContact == NULL)
+ {
+ SetDlgItemText(hwndDlg, IDC_USERNAME, _T(""));
+ SetFocus(GetDlgItem(hwndDlg, IDC_USERNAME));
+ break;
+ }
+
+ // Is button enabled?
+ if (!IsWindowEnabled(GetDlgItem(hwndDlg, IDC_URL)))
+ break;
+
+ CallService(MS_URL_SENDURL, (WPARAM) hContact, 0);
+
+ DBWriteContactSettingDword(NULL, MODULE_NAME, "LastSentTo", (DWORD) hContact);
+ SendMessage(hwndDlg, WM_CLOSE, 0, 0);
+ break;
+ }
+ case HOTKEY_INFO:
+ case IDC_USERINFO:
+ {
+ HANDLE hContact = GetSelectedContact(hwndDlg);
+ if (hContact == NULL)
+ {
+ SetDlgItemText(hwndDlg, IDC_USERNAME, _T(""));
+ SetFocus(GetDlgItem(hwndDlg, IDC_USERNAME));
+ break;
+ }
+
+ // Is button enabled?
+ if (!IsWindowEnabled(GetDlgItem(hwndDlg, IDC_USERINFO)))
+ break;
+
+ CallService(MS_USERINFO_SHOWDIALOG, (WPARAM) hContact, 0);
+
+ DBWriteContactSettingDword(NULL, MODULE_NAME, "LastSentTo", (DWORD) hContact);
+ SendMessage(hwndDlg, WM_CLOSE, 0, 0);
+ break;
+ }
+ case HOTKEY_HISTORY:
+ case IDC_HISTORY:
+ {
+ HANDLE hContact = GetSelectedContact(hwndDlg);
+ if (hContact == NULL)
+ {
+ SetDlgItemText(hwndDlg, IDC_USERNAME, _T(""));
+ SetFocus(GetDlgItem(hwndDlg, IDC_USERNAME));
+ break;
+ }
+
+ // Is button enabled?
+ if (!IsWindowEnabled(GetDlgItem(hwndDlg, IDC_HISTORY)))
+ break;
+
+ CallService(MS_HISTORY_SHOWCONTACTHISTORY, (WPARAM) hContact, 0);
+
+ DBWriteContactSettingDword(NULL, MODULE_NAME, "LastSentTo", (DWORD) hContact);
+ SendMessage(hwndDlg, WM_CLOSE, 0, 0);
+ break;
+ }
+ case HOTKEY_MENU:
+ case IDC_MENU:
+ {
+ HANDLE hContact = GetSelectedContact(hwndDlg);
+ if (hContact == NULL)
+ {
+ SetDlgItemText(hwndDlg, IDC_USERNAME, _T(""));
+ SetFocus(GetDlgItem(hwndDlg, IDC_USERNAME));
+ break;
+ }
+
+ // Is button enabled?
+ if (!IsWindowEnabled(GetDlgItem(hwndDlg, IDC_MENU)))
+ break;
+
+ RECT rc;
+ GetWindowRect(GetDlgItem(hwndDlg, IDC_MENU), &rc);
+ HMENU hMenu = (HMENU) CallService(MS_CLIST_MENUBUILDCONTACT, (WPARAM) hContact, 0);
+ int ret = TrackPopupMenu(hMenu, TPM_TOPALIGN|TPM_RIGHTBUTTON|TPM_RETURNCMD, rc.left, rc.bottom, 0, hwndDlg, NULL);
+ DestroyMenu(hMenu);
+
+ if(ret)
+ {
+ SendMessage(hwndDlg, WM_CLOSE, 0, 0);
+ CallService(MS_CLIST_MENUPROCESSCOMMAND, MAKEWPARAM(LOWORD(ret),MPCF_CONTACTMENU),(LPARAM) hContact);
+ }
+
+ DBWriteContactSettingDword(NULL, MODULE_NAME, "LastSentTo", (DWORD) hContact);
+ break;
+ }
+ case HOTKEY_ALL_CONTACTS:
+ case IDC_SHOW_ALL_CONTACTS:
+ {
+ // Get old text
+ HWND hEdit = GetWindow(GetWindow(hwndDlg,GW_CHILD),GW_CHILD);
+ TCHAR sztext[120] = _T("");
+
+ if (SendMessage(hEdit, EM_GETSEL, (WPARAM)NULL, (LPARAM)NULL) != -1)
+ SendMessage(hEdit, EM_REPLACESEL, (WPARAM)0, (LPARAM)_T(""));
+
+ SendMessage(hEdit, WM_GETTEXT, (WPARAM)MAX_REGS(sztext), (LPARAM)sztext);
+
+ // Fill combo
+ BOOL all = IsDlgButtonChecked(hwndDlg, IDC_SHOW_ALL_CONTACTS);
+
+ if (LOWORD(wParam) == HOTKEY_ALL_CONTACTS)
+ {
+ // Toggle checkbox
+ all = !all;
+ CheckDlgButton(hwndDlg, IDC_SHOW_ALL_CONTACTS, all ? BST_CHECKED : BST_UNCHECKED);
+ }
+
+ LoadContacts(hwndDlg, all);
+
+ // Return selection
+ CheckText(hEdit, sztext);
+
+ break;
+ }
+ }
+
+ break;
+ }
+
+ case WM_CLOSE:
+ {
+ Utils_SaveWindowPosition(hwndDlg, NULL, MODULE_NAME, "window");
+ MagneticWindows_RemoveWindow(hwndDlg);
+ DestroyWindow(hwndDlg);
+ break;
+ }
+
+ case WM_DESTROY:
+ {
+ UnhookWindowsHookEx(hHook);
+ hwndMain = NULL;
+ FreeContacts();
+ InterlockedExchange(&main_dialog_open, 0);
+ break;
+ }
+
+ case WM_NCLBUTTONDBLCLK:
+ {
+ MagneticWindows_SnapWindowToList(hwndDlg, MS_MW_STL_List_Left | MS_MW_STL_List_Top
+ | MS_MW_STL_Wnd_Right | MS_MW_STL_Wnd_Top);
+ break;
+ }
+
+ case WM_DRAWITEM:
+ {
+ // add icons and protocol to listbox
+ LPDRAWITEMSTRUCT lpdis = (LPDRAWITEMSTRUCT)lParam;
+
+ // Handle contact menu
+ if(lpdis->CtlID != IDC_USERNAME)
+ {
+ if (lpdis->CtlType == ODT_MENU)
+ return CallService(MS_CLIST_MENUDRAWITEM,wParam,lParam);
+ else
+ break;
+ }
+
+ // Handle combo
+ if(lpdis->itemID == -1)
+ break;
+
+ TEXTMETRIC tm;
+ int icon_width=0, icon_height=0;
+ RECT rc;
+
+ GetTextMetrics(lpdis->hDC, &tm);
+ ImageList_GetIconSize(hIml, &icon_width, &icon_height);
+
+ COLORREF clrfore = SetTextColor(lpdis->hDC,GetSysColor(lpdis->itemState & ODS_SELECTED?COLOR_HIGHLIGHTTEXT:COLOR_WINDOWTEXT));
+ COLORREF clrback = SetBkColor(lpdis->hDC,GetSysColor(lpdis->itemState & ODS_SELECTED?COLOR_HIGHLIGHT:COLOR_WINDOW));
+
+ FillRect(lpdis->hDC, &lpdis->rcItem, GetSysColorBrush(lpdis->itemState & ODS_SELECTED ? COLOR_HIGHLIGHT : COLOR_WINDOW));
+
+ // Draw icon
+ rc.left = lpdis->rcItem.left + 5;
+ rc.top = (lpdis->rcItem.bottom + lpdis->rcItem.top - icon_height) / 2;
+ ImageList_Draw(hIml, CallService(MS_CLIST_GETCONTACTICON, (WPARAM)contacts[lpdis->itemData]->hcontact, 0),
+ lpdis->hDC, rc.left, rc.top, ILD_NORMAL);
+
+ // Make rect for text
+ rc.left += icon_width + 5;
+ rc.right = lpdis->rcItem.right - 1;
+ rc.top = (lpdis->rcItem.bottom + lpdis->rcItem.top - tm.tmHeight) / 2;
+ rc.bottom = rc.top + tm.tmHeight;
+
+ // Draw Protocol
+ if (opts.num_protos > 1)
+ {
+ if (max_proto_width == 0)
+ {
+ // Has to be done, else the DC isnt the right one
+ // Dont ask me why
+ for(int loop = 0; loop < contacts.getCount(); loop++)
+ {
+ RECT rcc = { 0, 0, 0x7FFF, 0x7FFF };
+
+ DrawText(lpdis->hDC, contacts[loop]->proto, lstrlen(contacts[loop]->proto),
+ &rcc, DT_END_ELLIPSIS | DT_NOPREFIX | DT_SINGLELINE | DT_CALCRECT);
+ max_proto_width = max(max_proto_width, rcc.right - rcc.left);
+ }
+
+ // Fix max_proto_width
+ if (opts.group_append && opts.group_column)
+ max_proto_width = min(max_proto_width, (rc.right - rc.left) / 5);
+ else if (opts.group_append)
+ max_proto_width = min(max_proto_width, (rc.right - rc.left) / 4);
+ else
+ max_proto_width = min(max_proto_width, (rc.right - rc.left) / 3);
+ }
+
+ RECT rc_tmp = rc;
+
+ rc_tmp.left = rc_tmp.right - max_proto_width;
+
+ DrawText(lpdis->hDC, contacts[lpdis->itemData]->proto, lstrlen(contacts[lpdis->itemData]->proto),
+ &rc_tmp, DT_END_ELLIPSIS | DT_NOPREFIX | DT_SINGLELINE);
+
+ rc.right = rc_tmp.left - 5;
+ }
+
+ // Draw group
+ if (opts.group_append && opts.group_column)
+ {
+ RECT rc_tmp = rc;
+
+ if (opts.group_column_left)
+ {
+ rc_tmp.right = rc_tmp.left + (rc.right - rc.left) / 3;
+ rc.left = rc_tmp.right + 5;
+ }
+ else
+ {
+ rc_tmp.left = rc_tmp.right - (rc.right - rc.left) / 3;
+ rc.right = rc_tmp.left - 5;
+ }
+
+ DrawText(lpdis->hDC, contacts[lpdis->itemData]->szgroup, lstrlen(contacts[lpdis->itemData]->szgroup),
+ &rc_tmp, DT_END_ELLIPSIS | DT_NOPREFIX | DT_SINGLELINE);
+ }
+
+ // Draw text
+ TCHAR *name;
+ if (opts.group_append && !opts.group_column)
+ name = GetListName(contacts[lpdis->itemData]);
+ else
+ name = contacts[lpdis->itemData]->szname;
+
+ DrawText(lpdis->hDC, name, lstrlen(name), &rc, DT_END_ELLIPSIS | DT_NOPREFIX | DT_SINGLELINE);
+
+ // Restore old colors
+ SetTextColor(lpdis->hDC, clrfore);
+ SetBkColor(lpdis->hDC, clrback);
+
+ return TRUE;
+ }
+
+ case WM_MEASUREITEM:
+ {
+ LPMEASUREITEMSTRUCT lpmis = (LPMEASUREITEMSTRUCT)lParam;
+
+ // Handle contact menu
+ if(lpmis->CtlID != IDC_USERNAME)
+ {
+ if (lpmis->CtlType == ODT_MENU)
+ return CallService(MS_CLIST_MENUMEASUREITEM,wParam,lParam);
+ else
+ break;
+ }
+
+ // Handle combo
+
+ TEXTMETRIC tm;
+ int icon_width = 0, icon_height=0;
+
+ GetTextMetrics(GetDC(hwndDlg), &tm);
+ ImageList_GetIconSize(hIml, &icon_width, &icon_height);
+
+ lpmis->itemHeight = max(icon_height, tm.tmHeight);
+
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+
+int HotkeyPressed(WPARAM wParam, LPARAM lParam)
+{
+ THKSEvent *ev = (THKSEvent *) wParam;
+
+ if (ev->moduleId == hksModule && ev->itemId == hksAction)
+ ShowDialog(0, 0);
+
+ return 0;
+}
+
+
+// Show the main dialog
+int ShowDialog(WPARAM wParam, LPARAM lParam)
+{
+ if (!main_dialog_open)
+ {
+ InterlockedExchange(&main_dialog_open, 1);
+
+ hwndMain = CreateDialog(hInst, MAKEINTRESOURCE(IDD_MAIN), NULL, MainDlgProc);
+ }
+
+ // Show it
+ SetForegroundWindow(hwndMain);
+ SetFocus(hwndMain);
+ ShowWindow(hwndMain, SW_SHOW);
+
+ return 0;
+}
diff --git a/Plugins/quickcontacts/quickcontacts.dsp b/Plugins/quickcontacts/quickcontacts.dsp
new file mode 100644
index 0000000..b739b63
--- /dev/null
+++ b/Plugins/quickcontacts/quickcontacts.dsp
@@ -0,0 +1,234 @@
+# Microsoft Developer Studio Project File - Name="quickcontacts" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=quickcontacts - Win32 Release
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "quickcontacts.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "quickcontacts.mak" CFG="quickcontacts - Win32 Release"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "quickcontacts - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "quickcontacts - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "quickcontacts - Win32 Unicode Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "quickcontacts - Win32 Unicode Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "quickcontacts - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MD /W3 /GX /O1 /YX /FD /c
+# SUBTRACT BASE CPP /Fr
+# ADD CPP /nologo /MD /W3 /GX /Zi /O2 /I "../../include" /I "sdk" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /FAcs /YX /FD /c
+# SUBTRACT CPP /Fr
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x417 /d "NDEBUG"
+# ADD RSC /l 0x417 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 user32.lib shell32.lib wininet.lib gdi32.lib /nologo /base:"0x67100000" /dll /machine:I386 /filealign:0x200
+# SUBTRACT BASE LINK32 /pdb:none /map
+# ADD LINK32 comctl32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /base:"0x32100000" /dll /map /debug /machine:I386 /out:"..\..\bin\release\Plugins\quickcontacts.dll" /filealign:0x200 /ALIGN:4096 /ignore:4108
+# SUBTRACT LINK32 /profile /pdb:none
+
+!ELSEIF "$(CFG)" == "quickcontacts - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Ignore_Export_Lib 0
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /G4 /MT /W3 /GX /O2 /Ob0 /I "../../include" /FR /YX /FD /c
+# ADD CPP /nologo /G4 /MTd /W3 /GX /ZI /Od /I "../../include" /I "sdk" /FR /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x417 /d "NDEBUG"
+# ADD RSC /l 0x417 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 comctl32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 /out:"..\..bin\release\Plugins\quickcontacts.dll" /filealign:0x200 /ALIGN:4096 /ignore:4108
+# SUBTRACT BASE LINK32 /profile /pdb:none
+# ADD LINK32 comctl32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /base:"0x32100000" /dll /incremental:yes /debug /machine:I386 /out:"..\..\bin\debug\Plugins\quickcontacts.dll" /filealign:0x200 /ALIGN:4096 /ignore:4108
+# SUBTRACT LINK32 /profile /pdb:none
+
+!ELSEIF "$(CFG)" == "quickcontacts - Win32 Unicode Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "quickcontacts___Win32_Unicode_Release"
+# PROP BASE Intermediate_Dir "quickcontacts___Win32_Unicode_Release"
+# PROP BASE Ignore_Export_Lib 0
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Unicode_Release"
+# PROP Intermediate_Dir "Unicode_Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /G4 /MT /W3 /GX /O2 /Ob0 /I "../../include" /Fr /YX /FD /c
+# ADD CPP /nologo /MD /W3 /GX /Zi /O2 /I "../../include" /I "sdk" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "UNICODE" /D "_UNICODE" /D "_USRDLL" /FAcs /YX /FD /c
+# SUBTRACT CPP /Fr
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x417 /d "NDEBUG"
+# ADD RSC /l 0x417 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 comctl32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /base:"0x32100000" /dll /map /machine:I386 /out:"..\..\bin\release\Plugins\quickcontacts.dll" /filealign:0x200 /ALIGN:4096 /ignore:4108
+# SUBTRACT BASE LINK32 /profile /pdb:none
+# ADD LINK32 comctl32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /base:"0x32100000" /dll /map /debug /machine:I386 /out:"..\..\bin\release unicode\Plugins\quickcontactsW.dll" /filealign:0x200 /ALIGN:4096 /ignore:4108
+# SUBTRACT LINK32 /profile /pdb:none
+
+!ELSEIF "$(CFG)" == "quickcontacts - Win32 Unicode Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "quickcontacts___Win32_Unicode_Debug"
+# PROP BASE Intermediate_Dir "quickcontacts___Win32_Unicode_Debug"
+# PROP BASE Ignore_Export_Lib 0
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Unicode_Debug"
+# PROP Intermediate_Dir "Unicode_Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /G4 /MTd /W3 /GX /ZI /Od /I "../../include" /FR /YX /FD /c
+# ADD CPP /nologo /G4 /MTd /W3 /Gm /Gi /ZI /Od /I "../../include" /I "sdk" /D "WIN32" /D "W32" /D "_DEBUG" /D "_WINDOWS" /D "_UNICODE" /D "UNICODE" /D "_USRDLL" /FAcs /FR /FD /c
+# SUBTRACT CPP /YX
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x417 /d "NDEBUG"
+# ADD RSC /l 0x417 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 comctl32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /base:"0x32100000" /dll /incremental:yes /debug /machine:I386 /out:"..\..\bin\debug\Plugins\quickcontacts.dll" /filealign:0x200 /ALIGN:4096 /ignore:4108
+# SUBTRACT BASE LINK32 /profile /pdb:none
+# ADD LINK32 comctl32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /base:"0x32110000" /dll /incremental:yes /map /debug /machine:I386 /out:"..\..\bin\Debug Unicode\Plugins\quickcontactsW.dll" /filealign:0x200 /ALIGN:4096 /ignore:4108
+# SUBTRACT LINK32 /profile /pdb:none
+
+!ENDIF
+
+# Begin Target
+
+# Name "quickcontacts - Win32 Release"
+# Name "quickcontacts - Win32 Debug"
+# Name "quickcontacts - Win32 Unicode Release"
+# Name "quickcontacts - Win32 Unicode Debug"
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=.\commons.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\m_quickcontacts.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\utils\mir_memory.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\utils\mir_options.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\options.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\resource.h
+# End Source File
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
+# Begin Source File
+
+SOURCE=.\resource.rc
+# End Source File
+# End Group
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=..\utils\mir_options.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\options.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\quickcontacts.cpp
+# End Source File
+# End Group
+# Begin Group "Docs"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=.\Docs\langpack_quickcontacts.txt
+# End Source File
+# Begin Source File
+
+SOURCE=.\Docs\quickcontacts_changelog.txt
+# End Source File
+# Begin Source File
+
+SOURCE=.\Docs\quickcontacts_readme.txt
+# End Source File
+# Begin Source File
+
+SOURCE=.\Docs\quickcontacts_version.txt
+# End Source File
+# End Group
+# End Target
+# End Project
diff --git a/Plugins/quickcontacts/quickcontacts.dsw b/Plugins/quickcontacts/quickcontacts.dsw
new file mode 100644
index 0000000..de1da4e
--- /dev/null
+++ b/Plugins/quickcontacts/quickcontacts.dsw
@@ -0,0 +1,29 @@
+Microsoft Developer Studio Workspace File, Format Version 6.00
+# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
+
+###############################################################################
+
+Project: "quickcontacts"=".\quickcontacts.dsp" - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Global:
+
+Package=<5>
+{{{
+}}}
+
+Package=<3>
+{{{
+}}}
+
+###############################################################################
+
diff --git a/Plugins/quickcontacts/quickcontacts.sln b/Plugins/quickcontacts/quickcontacts.sln
new file mode 100644
index 0000000..c5de439
--- /dev/null
+++ b/Plugins/quickcontacts/quickcontacts.sln
@@ -0,0 +1,26 @@
+
+Microsoft Visual Studio Solution File, Format Version 9.00
+# Visual Studio 2005
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "quickcontacts", "quickcontacts.vcproj", "{7FC00D59-03D3-4A6C-AEFE-67349C9C9E10}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Win32 = Debug|Win32
+ Release|Win32 = Release|Win32
+ Unicode Debug|Win32 = Unicode Debug|Win32
+ Unicode Release|Win32 = Unicode Release|Win32
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {7FC00D59-03D3-4A6C-AEFE-67349C9C9E10}.Debug|Win32.ActiveCfg = Debug|Win32
+ {7FC00D59-03D3-4A6C-AEFE-67349C9C9E10}.Debug|Win32.Build.0 = Debug|Win32
+ {7FC00D59-03D3-4A6C-AEFE-67349C9C9E10}.Release|Win32.ActiveCfg = Release|Win32
+ {7FC00D59-03D3-4A6C-AEFE-67349C9C9E10}.Release|Win32.Build.0 = Release|Win32
+ {7FC00D59-03D3-4A6C-AEFE-67349C9C9E10}.Unicode Debug|Win32.ActiveCfg = Unicode Debug|Win32
+ {7FC00D59-03D3-4A6C-AEFE-67349C9C9E10}.Unicode Debug|Win32.Build.0 = Unicode Debug|Win32
+ {7FC00D59-03D3-4A6C-AEFE-67349C9C9E10}.Unicode Release|Win32.ActiveCfg = Unicode Release|Win32
+ {7FC00D59-03D3-4A6C-AEFE-67349C9C9E10}.Unicode Release|Win32.Build.0 = Unicode Release|Win32
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/Plugins/quickcontacts/quickcontacts.vcproj b/Plugins/quickcontacts/quickcontacts.vcproj
new file mode 100644
index 0000000..99cc78a
--- /dev/null
+++ b/Plugins/quickcontacts/quickcontacts.vcproj
@@ -0,0 +1,671 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="8,00"
+ Name="quickcontacts"
+ ProjectGUID="{7FC00D59-03D3-4A6C-AEFE-67349C9C9E10}"
+ RootNamespace="quickcontacts"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Unicode Release|Win32"
+ OutputDirectory=".\Unicode_Release"
+ IntermediateDirectory=".\Unicode_Release"
+ ConfigurationType="2"
+ InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC60.vsprops"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ PreprocessorDefinitions="NDEBUG"
+ MkTypLibCompatible="true"
+ SuppressStartupBanner="true"
+ TargetEnvironment="1"
+ TypeLibraryName=".\Unicode_Release/quickcontacts.tlb"
+ HeaderFileName=""
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ InlineFunctionExpansion="1"
+ AdditionalIncludeDirectories="../../include,sdk"
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;UNICODE;_USRDLL"
+ StringPooling="true"
+ RuntimeLibrary="2"
+ EnableFunctionLevelLinking="true"
+ PrecompiledHeaderFile=".\Unicode_Release/quickcontacts.pch"
+ AssemblerOutput="2"
+ AssemblerListingLocation=".\Unicode_Release/"
+ ObjectFile=".\Unicode_Release/"
+ ProgramDataBaseFileName=".\Unicode_Release/"
+ WarningLevel="3"
+ SuppressStartupBanner="true"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="NDEBUG"
+ Culture="1047"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalOptions="/ALIGN:4096 /filealign:0x200 /ignore:4108 "
+ AdditionalDependencies="comctl32.lib odbc32.lib odbccp32.lib"
+ OutputFile="..\..\bin\release unicode\Plugins\quickcontactsW.dll"
+ LinkIncremental="1"
+ SuppressStartupBanner="true"
+ GenerateDebugInformation="true"
+ ProgramDatabaseFile=".\Unicode_Release/quickcontactsW.pdb"
+ GenerateMapFile="true"
+ MapFileName=".\Unicode_Release/quickcontactsW.map"
+ BaseAddress="0x32100000"
+ ImportLibrary=".\Unicode_Release/quickcontactsW.lib"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ SuppressStartupBanner="true"
+ OutputFile=".\Unicode_Release/quickcontacts.bsc"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Unicode Debug|Win32"
+ OutputDirectory=".\Unicode_Debug"
+ IntermediateDirectory=".\Unicode_Debug"
+ ConfigurationType="2"
+ InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC60.vsprops"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ PreprocessorDefinitions="NDEBUG"
+ MkTypLibCompatible="true"
+ SuppressStartupBanner="true"
+ TargetEnvironment="1"
+ TypeLibraryName=".\Unicode_Debug/quickcontacts.tlb"
+ HeaderFileName=""
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories="../../include,sdk"
+ PreprocessorDefinitions="UNICODE;_CRT_SECURE_NO_DEPRECATE"
+ RuntimeLibrary="1"
+ PrecompiledHeaderFile=".\Unicode_Debug/quickcontacts.pch"
+ AssemblerListingLocation=".\Unicode_Debug/"
+ ObjectFile=".\Unicode_Debug/"
+ ProgramDataBaseFileName=".\Unicode_Debug/"
+ BrowseInformation="1"
+ WarningLevel="3"
+ SuppressStartupBanner="true"
+ DebugInformationFormat="4"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="NDEBUG"
+ Culture="1047"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalOptions="/ALIGN:4096 /filealign:0x200 /ignore:4108 "
+ AdditionalDependencies="comctl32.lib odbc32.lib odbccp32.lib"
+ OutputFile="..\..\bin\Debug Unicode\Plugins\quickcontactsW.dll"
+ LinkIncremental="2"
+ SuppressStartupBanner="true"
+ GenerateDebugInformation="true"
+ ProgramDatabaseFile=".\Unicode_Debug/quickcontactsW.pdb"
+ BaseAddress="0x32100000"
+ ImportLibrary=".\Unicode_Debug/quickcontactsW.lib"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ SuppressStartupBanner="true"
+ OutputFile=".\Unicode_Debug/quickcontacts.bsc"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory=".\Release"
+ IntermediateDirectory=".\Release"
+ ConfigurationType="2"
+ InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC60.vsprops"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="2"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ PreprocessorDefinitions="NDEBUG"
+ MkTypLibCompatible="true"
+ SuppressStartupBanner="true"
+ TargetEnvironment="1"
+ TypeLibraryName=".\Release/quickcontacts.tlb"
+ HeaderFileName=""
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ InlineFunctionExpansion="1"
+ AdditionalIncludeDirectories="../../include,sdk"
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL"
+ StringPooling="true"
+ RuntimeLibrary="2"
+ EnableFunctionLevelLinking="true"
+ PrecompiledHeaderFile=".\Release/quickcontacts.pch"
+ AssemblerOutput="2"
+ AssemblerListingLocation=".\Release/"
+ ObjectFile=".\Release/"
+ ProgramDataBaseFileName=".\Release/"
+ WarningLevel="3"
+ SuppressStartupBanner="true"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="NDEBUG"
+ Culture="1047"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalOptions="/ALIGN:4096 /filealign:0x200 /ignore:4108 "
+ AdditionalDependencies="comctl32.lib odbc32.lib odbccp32.lib"
+ OutputFile="..\..\bin\release\Plugins\quickcontacts.dll"
+ LinkIncremental="1"
+ SuppressStartupBanner="true"
+ GenerateDebugInformation="true"
+ ProgramDatabaseFile=".\Release/quickcontacts.pdb"
+ GenerateMapFile="true"
+ MapFileName=".\Release/quickcontacts.map"
+ BaseAddress="0x32100000"
+ ImportLibrary=".\Release/quickcontacts.lib"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ SuppressStartupBanner="true"
+ OutputFile=".\Release/quickcontacts.bsc"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory=".\Debug"
+ IntermediateDirectory=".\Debug"
+ ConfigurationType="2"
+ InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC60.vsprops"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ PreprocessorDefinitions="NDEBUG"
+ MkTypLibCompatible="true"
+ SuppressStartupBanner="true"
+ TargetEnvironment="1"
+ TypeLibraryName=".\Debug/quickcontacts.tlb"
+ HeaderFileName=""
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories="../../include,sdk"
+ RuntimeLibrary="1"
+ PrecompiledHeaderFile=".\Debug/quickcontacts.pch"
+ AssemblerListingLocation=".\Debug/"
+ ObjectFile=".\Debug/"
+ ProgramDataBaseFileName=".\Debug/"
+ BrowseInformation="1"
+ WarningLevel="3"
+ SuppressStartupBanner="true"
+ DebugInformationFormat="4"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="NDEBUG"
+ Culture="1047"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalOptions="/ALIGN:4096 /filealign:0x200 /ignore:4108 "
+ AdditionalDependencies="comctl32.lib odbc32.lib odbccp32.lib"
+ OutputFile="..\..\bin\debug\Plugins\quickcontacts.dll"
+ LinkIncremental="2"
+ SuppressStartupBanner="true"
+ GenerateDebugInformation="true"
+ ProgramDatabaseFile=".\Debug/quickcontacts.pdb"
+ BaseAddress="0x32100000"
+ ImportLibrary=".\Debug/quickcontacts.lib"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ SuppressStartupBanner="true"
+ OutputFile=".\Debug/quickcontacts.bsc"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Header Files"
+ Filter="h;hpp;hxx;hm;inl"
+ >
+ <File
+ RelativePath="commons.h"
+ >
+ </File>
+ <File
+ RelativePath="m_quickcontacts.h"
+ >
+ </File>
+ <File
+ RelativePath="..\utils\mir_memory.h"
+ >
+ </File>
+ <File
+ RelativePath="..\utils\mir_options.h"
+ >
+ </File>
+ <File
+ RelativePath="options.h"
+ >
+ </File>
+ <File
+ RelativePath="resource.h"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Resource Files"
+ Filter="ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
+ >
+ <File
+ RelativePath="resource.rc"
+ >
+ <FileConfiguration
+ Name="Unicode Release|Win32"
+ >
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Unicode Debug|Win32"
+ >
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ </File>
+ </Filter>
+ <Filter
+ Name="Source Files"
+ Filter="cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+ >
+ <File
+ RelativePath="..\utils\mir_memory.cpp"
+ >
+ <FileConfiguration
+ Name="Unicode Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Unicode Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="..\utils\mir_options.cpp"
+ >
+ <FileConfiguration
+ Name="Unicode Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Unicode Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="options.cpp"
+ >
+ <FileConfiguration
+ Name="Unicode Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Unicode Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="quickcontacts.cpp"
+ >
+ <FileConfiguration
+ Name="Unicode Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Unicode Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ />
+ </FileConfiguration>
+ </File>
+ </Filter>
+ <Filter
+ Name="Docs"
+ >
+ <File
+ RelativePath="Docs\langpack_quickcontacts.txt"
+ >
+ </File>
+ <File
+ RelativePath="Docs\quickcontacts_changelog.txt"
+ >
+ </File>
+ <File
+ RelativePath="Docs\quickcontacts_readme.txt"
+ >
+ </File>
+ <File
+ RelativePath="Docs\quickcontacts_version.txt"
+ >
+ </File>
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/Plugins/quickcontacts/resource.h b/Plugins/quickcontacts/resource.h
new file mode 100644
index 0000000..f7f6a01
--- /dev/null
+++ b/Plugins/quickcontacts/resource.h
@@ -0,0 +1,47 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Developer Studio generated include file.
+// Used by resource.rc
+//
+#define ACCEL_TABLE 103
+#define IDD_OPT 110
+#define IDD_MAIN 1000
+#define IDC_USERNAME 1001
+#define IDC_LASTSENTTO 1004
+#define IDC_GLOBAL 1009
+#define IDC_LOCAL 1010
+#define IDC_PROTOCOLS 1041
+#define IDC_SUBCONTACTS 1058
+#define IDC_KEEP_OFFLINE 1059
+#define IDC_HIDE_OFFLINE 1060
+#define IDC_SHOW_ALL_CONTACTS 1061
+#define IDC_APPEND_GROUP 1061
+#define IDC_MESSAGE 1062
+#define IDC_GROUP_COLUMN 1062
+#define IDC_FILE 1063
+#define IDC_GROUP_LEFT 1063
+#define IDC_URL 1064
+#define IDC_USERINFO 1065
+#define IDC_HISTORY 1066
+#define IDC_MENU 1067
+#define IDC_VOICE 1068
+#define HOTKEY_FILE 40001
+#define HOTKEY_URL 40002
+#define HOTKEY_INFO 40003
+#define HOTKEY_HISTORY 40004
+#define HOTKEY_ALL_CONTACTS 40005
+#define HOTKEY_MENU 40006
+#define HOTKEY_VOICE 40007
+#define IDC_STATIC -1
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NO_MFC 1
+#define _APS_3D_CONTROLS 1
+#define _APS_NEXT_RESOURCE_VALUE 119
+#define _APS_NEXT_COMMAND_VALUE 40008
+#define _APS_NEXT_CONTROL_VALUE 1063
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/Plugins/quickcontacts/resource.rc b/Plugins/quickcontacts/resource.rc
new file mode 100644
index 0000000..efdb2e6
--- /dev/null
+++ b/Plugins/quickcontacts/resource.rc
@@ -0,0 +1,179 @@
+//Microsoft Developer Studio generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "resource.h"
+#include "winresrc.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (U.S.) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page(1252)
+#endif //_WIN32
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Dialog
+//
+
+IDD_OPT DIALOGEX 0, 0, 257, 237
+STYLE DS_FIXEDSYS | WS_CHILD | WS_VISIBLE
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg"
+BEGIN
+ GROUPBOX "Last Sent",IDC_STATIC,0,0,257,58
+ CONTROL "Enable last-sent-to",IDC_LASTSENTTO,"Button",
+ BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP,18,14,221,10
+ CONTROL "recognize all messages sent",IDC_GLOBAL,"Button",
+ BS_AUTORADIOBUTTON | WS_DISABLED,30,27,209,10
+ CONTROL "recognize messages sent with hotkey only",IDC_LOCAL,
+ "Button",BS_AUTORADIOBUTTON | WS_DISABLED,30,39,209,10
+ GROUPBOX "Contacts",IDC_STATIC,0,59,257,177
+ LTEXT "Show offline contacts on these protocols:",IDC_STATIC,
+ 16,72,236,9
+ CONTROL "List1",IDC_PROTOCOLS,"SysListView32",LVS_REPORT |
+ LVS_SINGLESEL | LVS_SORTASCENDING | LVS_NOCOLUMNHEADER |
+ LVS_NOSORTHEADER | WS_BORDER | WS_TABSTOP,28,83,140,71
+ CONTROL "But hide them if protocol is offline",IDC_HIDE_OFFLINE,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,27,159,214,8
+ CONTROL "Append group name to contact name",IDC_APPEND_GROUP,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,16,171,225,8
+ CONTROL "But show it as a column ...",IDC_GROUP_COLUMN,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,27,183,214,8
+ CONTROL "... on left side of name",IDC_GROUP_LEFT,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,27,195,214,8
+ CONTROL "Hide subcontacts",IDC_SUBCONTACTS,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,16,207,225,8
+ CONTROL "But keep subcontacts of protocols in above list if meta is hidden",
+ IDC_KEEP_OFFLINE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,
+ 28,219,217,9
+END
+
+IDD_MAIN DIALOGEX 13, 55, 193, 54
+STYLE DS_FIXEDSYS | WS_CAPTION | WS_SYSMENU
+EXSTYLE WS_EX_TOOLWINDOW
+CAPTION "Enter username:"
+FONT 8, "MS Shell Dlg", 0, 0, 0x1
+BEGIN
+ COMBOBOX IDC_USERNAME,20,6,164,130,CBS_DROPDOWN |
+ CBS_OWNERDRAWFIXED | CBS_HASSTRINGS |
+ CBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP
+ CONTROL "Show all contacts (Ctrl+A)",IDC_SHOW_ALL_CONTACTS,
+ "Button",BS_AUTOCHECKBOX | BS_NOTIFY | WS_TABSTOP,8,23,
+ 176,11
+ CONTROL "",IDC_MESSAGE,"MButtonClass",0x0,41,36,17,15,
+ 0x18000000L
+ CONTROL "",IDC_VOICE,"MButtonClass",0x0,62,36,17,15,0x18000000L
+ CONTROL "",IDC_FILE,"MButtonClass",0x0,83,36,17,15,0x18000000L
+ CONTROL "",IDC_URL,"MButtonClass",0x0,104,36,17,15,0x18000000L
+ CONTROL "",IDC_USERINFO,"MButtonClass",0x0,125,36,17,15,
+ 0x18000000L
+ CONTROL "",IDC_HISTORY,"MButtonClass",0x0,146,36,17,15,
+ 0x18000000L
+ CONTROL "",IDC_MENU,"MButtonClass",0x0,167,36,17,15,0x18000000L
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// DESIGNINFO
+//
+
+#ifdef APSTUDIO_INVOKED
+GUIDELINES DESIGNINFO DISCARDABLE
+BEGIN
+ IDD_OPT, DIALOG
+ BEGIN
+ TOPMARGIN, 3
+ END
+
+ IDD_MAIN, DIALOG
+ BEGIN
+ RIGHTMARGIN, 184
+ BOTTOMMARGIN, 51
+ END
+END
+#endif // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Accelerator
+//
+
+ACCEL_TABLE ACCELERATORS DISCARDABLE
+BEGIN
+ "A", HOTKEY_ALL_CONTACTS, VIRTKEY, CONTROL, NOINVERT
+ "F", HOTKEY_FILE, VIRTKEY, CONTROL, NOINVERT
+ "H", HOTKEY_HISTORY, VIRTKEY, CONTROL, NOINVERT
+ "I", HOTKEY_INFO, VIRTKEY, CONTROL, NOINVERT
+ "M", HOTKEY_MENU, VIRTKEY, CONTROL, NOINVERT
+ "U", HOTKEY_URL, VIRTKEY, CONTROL, NOINVERT
+ "V", HOTKEY_VOICE, VIRTKEY, CONTROL, NOINVERT
+END
+
+#endif // English (U.S.) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+/////////////////////////////////////////////////////////////////////////////
+// English (Canada) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENC)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_CAN
+#pragma code_page(1252)
+#endif //_WIN32
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "resource.h\0"
+END
+
+2 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "#include ""resource.h""\r\n"
+ "#include ""winresrc.h""\r\n"
+ "\0"
+END
+
+3 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "\r\n"
+ "\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+#endif // English (Canada) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
+
diff --git a/Plugins/quickcontacts/sdk/m_MagneticWindows.h b/Plugins/quickcontacts/sdk/m_MagneticWindows.h
new file mode 100644
index 0000000..e011c77
--- /dev/null
+++ b/Plugins/quickcontacts/sdk/m_MagneticWindows.h
@@ -0,0 +1,86 @@
+#ifndef __M_MAGNETICWINDOWS_H__
+#define __M_MAGNETICWINDOWS_H__
+
+//#include "../include/newpluginapi.h"
+
+// For other Plugins to start snapping for their windows
+// wparam: hwnd of window
+// lparam: 0
+// return: 0 on success, 1 on error
+#define MS_MW_ADDWINDOW "Utils/MagneticWindows/Add"
+
+// For other Plugins to stop snapping for their windows
+// wparam: hwnd of window
+// lparam: 0
+// return: 0 on success, 1 on error
+#define MS_MW_REMWINDOW "Utils/MagneticWindows/Rem"
+
+//decide where to align on the list:
+#define MS_MW_STL_List_Left 0x00000001 //Snaps the window to the left border of the list
+#define MS_MW_STL_List_Top 0x00000002 //Snaps the window to the top border of the list
+#define MS_MW_STL_List_Right 0x00000004 //Snaps the window to the right border of the list
+#define MS_MW_STL_List_Bottom 0x00000008 //Snaps the window to the bottom border of the list
+//decide with what side (of the window you want to snap) to snap to the list
+#define MS_MW_STL_Wnd_Left 0x00000010 //Snaps the window with the left border to the left/right side of the list
+#define MS_MW_STL_Wnd_Top 0x00000020 //Snaps the window with the top border to the top/bottom side of the list
+#define MS_MW_STL_Wnd_Right 0x00000040 //Snaps the window with the right border to the left/right side of the list
+#define MS_MW_STL_Wnd_Bottom 0x00000080 //Snaps the window with the bottom border to the top/bottom side of the list
+
+#define MS_MW_STL_Wnd_FullWidth (MS_MW_STL_Wnd_Left | MS_MW_STL_Wnd_Right)
+ //Snaps to the top/bottom of the list and spans over the full width
+
+#define MS_MW_STL_Wnd_FullHeight (MS_MW_STL_Wnd_Top | MS_MW_STL_Wnd_Bottom)
+ //Snaps to the left/right of the list and spans over the full height
+
+// to place the window in the list combine f.e. MS_MW_STL_List_Left | MS_MW_STL_Wnd_Right | *vetical alignment*
+
+//For other Plugins to snap a window to the list for other Plugins
+// wparam: hwnd of window
+// lparam: combination of the above constants MS_MW_STL_*
+// return: 0 on success, 1 on error
+#define MS_MW_SNAPTOLIST "Utils/MagneticWindows/SnapToList"
+
+// Helper functions
+#ifndef _MW_NO_HELPPER_FUNCTIONS
+
+
+static inline int MagneticWindows_AddWindow(HWND hWnd)
+{
+ if (ServiceExists(MS_MW_ADDWINDOW))
+ {
+ return CallService(MS_MW_ADDWINDOW, (WPARAM) hWnd, 0);
+ }
+ else
+ {
+ return -1;
+ }
+}
+
+static inline int MagneticWindows_RemoveWindow(HWND hWnd)
+{
+ if (ServiceExists(MS_MW_REMWINDOW))
+ {
+ return CallService(MS_MW_REMWINDOW, (WPARAM) hWnd, 0);
+ }
+ else
+ {
+ return -1;
+ }
+}
+
+static inline int MagneticWindows_SnapWindowToList(HWND hWnd, int MS_MW_STL_Options)
+{
+ if (ServiceExists(MS_MW_SNAPTOLIST))
+ {
+ return CallService(MS_MW_SNAPTOLIST, (WPARAM) hWnd, (LPARAM) MS_MW_STL_Options);
+ }
+ else
+ {
+ return -1;
+ }
+}
+
+#endif
+
+
+#endif // __M_MAGNETICWINDOWS_H__
diff --git a/Plugins/quickcontacts/sdk/m_hotkeysplus.h b/Plugins/quickcontacts/sdk/m_hotkeysplus.h
new file mode 100644
index 0000000..3da357f
--- /dev/null
+++ b/Plugins/quickcontacts/sdk/m_hotkeysplus.h
@@ -0,0 +1,22 @@
+#define MS_HOTKEYSPLUS_ADDKEY "HotkeysPlus/Add"
+
+/*
+This service registers hotkey for
+WPARAM - service to perform
+LPARAM - decription of the service
+Returned values:
+ 0 - success,
+ 1 - hotkey for this function is already existing,
+ 2 - the service, that you try to register the hotkey for, not exists
+*/
+
+#define MS_HOTKEYSPLUS_EXISTKEY "HotkeysPlus/Exist"
+/*
+This service checks whether hotkey for service (WPARAM) exists
+LPARAM - not used
+Returned values:
+ 0 - failed,
+ 1 - the hotkey for this function exists,
+ 2 - the service not exists
+*/
+
diff --git a/Plugins/quickcontacts/sdk/m_hotkeysservice.h b/Plugins/quickcontacts/sdk/m_hotkeysservice.h
new file mode 100644
index 0000000..274ab0e
--- /dev/null
+++ b/Plugins/quickcontacts/sdk/m_hotkeysservice.h
@@ -0,0 +1,207 @@
+#ifndef __M_HOTKEYSSERVICE__
+# define __M_HOTKEYSSERVICE__
+
+
+#define HKS_SERVICE_NAME "HotkeysService"
+
+// Modifiers for hotkeys
+//#define MOD_ALT 0x0001
+//#define MOD_CONTROL 0x0002
+//#define MOD_SHIFT 0x0004
+//#define MOD_WIN 0x0008
+#define MOD_GLOBAL 0x0010
+
+// FLAGS
+// inherit modifiers from group (specified modifiers ignored)
+#define HKS_ACTION_SHAREMODIFIERS 1
+// register item, but disable
+#define HKS_INITIALLY_DISABLED 2
+
+// Structure of hotkey
+typedef union
+{
+ struct
+ {
+ WORD key;
+ WORD modifiers;
+ };
+ DWORD hotkey;
+
+} THKSHotkey, *PHKSHotkey;
+
+
+// ITEM TYPES
+#define HKS_ITEM_MODULE 1
+#define HKS_ITEM_GROUP 2
+#define HKS_ITEM_ACTION 3
+#define HKS_ITEM_MACRO 4
+
+
+// Structure passed to RegisterItem service.
+// Used only for registration pursposes, so free it after call.
+// name, owner and itemType field is mandatory.
+// owner is item ID or must be 0 for 1st level items (module, single action or macro)
+// itemType can be one of ITEM TYPES constants.
+// flags can be combined from FLAGS constants.
+// Group can contain other groups and items and optionally
+// set group modifiers. Only item can be tree leaf.
+// If creating group, hotkey.modifiers will be used
+// as group modifiers (if nonzero)
+// If creating action, hotkey is optional. If hotkey.key is filled, then
+// hotkey will be assigned to item (if unused).
+// If creating macro, hotkey is mandatory.
+
+typedef struct
+{
+ int owner;
+ char *name;
+ int itemType;
+ THKSHotkey hotkey;
+ int flags;
+
+} THKSItem, *PHKSItem;
+
+
+// Register item
+// wParam: item data in PKHSItem format
+// lParam: 0
+// Returns HANDLE called "ID" in loWord.
+// For actions and macros, hiWord returns state: 0 if ok, other if passed hotkey
+// can't be registered (for example already used)
+// In other cases hiWord is useless
+#define MS_HKS_REGISTER_ITEM HKS_SERVICE_NAME "/RegisterItem"
+
+// Unregister item
+// If item is Group, then all subItems will be unregistered.
+// wParam: item ID
+// lParam: 0
+// Returns: 0 on success, other on fail
+#define MS_HKS_UNREGISTER_ITEM HKS_SERVICE_NAME "/UnregisterItem"
+
+// Assign hotkey to item. If item is group, then only
+// modifiers are taken as group modifiers. Do not call on modules.
+// Hotkey consists of modifiers in hiWord and key in loWord.
+// wParam: item ID
+// lParam: hotkey as PHKS_Hotkey to register
+// Returns:
+// on success: hotkey
+// on error: 0
+#define MS_HKS_ASSIGN_HOTKEY HKS_SERVICE_NAME "/AssignHotkey"
+
+// Get hotkey assigned to item. Don't apply to modules.
+// wParam: item ID
+// lParam: 0
+// Returns: hotkey assigned, 0 otherwise.
+#define MS_HKS_GET_HOTKEY HKS_SERVICE_NAME "/GetAssignedHotkey"
+
+// Unassign hotkey from item. Only valid on action and macro items.
+// wParam: item ID
+// lParam: 0
+// Returns: 0 on success, other on fail
+#define MS_HKS_UNASSIGN_HOTKEY HKS_SERVICE_NAME "/UnassignHotkey"
+
+// Enable/Disable item.
+// If item is group or module, then all subItems will be affected.
+// wParam: item ID
+// lParam: 1 to enable, anything else to disable
+// Returns: 0 on success, other on fail
+#define MS_HKS_ENABLE_ITEM HKS_SERVICE_NAME "/EnableItem"
+
+// Hotkey to text
+// wParam: hotkey to textify
+// lParam: address to place string, space must be allocated
+// Returns: 0
+#define MS_HKS_TO_TEXT HKS_SERVICE_NAME "/HotkeyToText"
+
+// Get hotkey from text
+// wParam: text to convert to hotkey
+// lParam: 0
+// Returns: hotkey
+#define MS_HKS_FROM_TEXT HKS_SERVICE_NAME "/HotkeyFromText"
+
+
+typedef struct
+{
+ int itemId;
+ int moduleId;
+ char *name;
+ int itemType;
+ THKSHotkey hotkey;
+
+} THKSEvent, *PHKSEvent;
+
+// Event when hotkey is pressed
+// wParam: PHKSEvent
+// lParam: 0
+#define ME_HKS_KEY_PRESSED HKS_SERVICE_NAME "/HotkeyPressed"
+
+
+
+
+// Util functions ////////////////////////////////////////////////////////////////////////
+
+
+static int HKS_RegisterModule(char *name)
+{
+ THKSItem item = {0};
+
+ if (!ServiceExists(MS_HKS_REGISTER_ITEM))
+ return -1;
+
+ item.name = name;
+ item.itemType = HKS_ITEM_MODULE;
+
+ return LOWORD(CallService(MS_HKS_REGISTER_ITEM, (WPARAM) &item, 0));
+}
+
+
+static int HKS_RegisterAction(int owner, char *name, int modifiers, char key, int flags)
+{
+ THKSItem item = {0};
+
+ if (!ServiceExists(MS_HKS_REGISTER_ITEM))
+ return -1;
+
+ item.owner = owner;
+ item.name = name;
+ item.itemType = HKS_ITEM_ACTION;
+ item.flags = flags;
+
+ if (key != 0)
+ {
+ item.hotkey.key = (WORD) key;
+ item.hotkey.modifiers = modifiers;
+ }
+
+ return LOWORD(CallService(MS_HKS_REGISTER_ITEM, (WPARAM) &item, 0));
+}
+
+
+static int HKS_RegisterGroup(int owner, char *name, int modifiers, int flags)
+{
+ THKSItem item = {0};
+
+ if (!ServiceExists(MS_HKS_REGISTER_ITEM))
+ return -1;
+
+ item.owner = owner;
+ item.name = name;
+ item.itemType = HKS_ITEM_GROUP;
+ item.flags = flags;
+ item.hotkey.modifiers = modifiers;
+
+ return LOWORD(CallService(MS_HKS_REGISTER_ITEM, (WPARAM) &item, 0));
+}
+
+static int HKS_Unregister(int id)
+{
+ THKSItem item = {0};
+
+ if (!ServiceExists(MS_HKS_UNREGISTER_ITEM))
+ return -1;
+
+ return CallService(MS_HKS_UNREGISTER_ITEM, (WPARAM) id, 0);
+}
+
+
+#endif
diff --git a/Plugins/quickcontacts/sdk/m_metacontacts.h b/Plugins/quickcontacts/sdk/m_metacontacts.h
new file mode 100644
index 0000000..9f348bd
--- /dev/null
+++ b/Plugins/quickcontacts/sdk/m_metacontacts.h
@@ -0,0 +1,166 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright © 2004 Universite Louis PASTEUR, STRASBOURG.
+Copyright © 2004 Scott Ellis (www.scottellis.com.au mail@scottellis.com.au)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef M_METACONTACTS_H__
+#define M_METACONTACTS_H__ 1
+
+#ifndef MIID_METACONTACTS
+#define MIID_METACONTACTS {0xc0325019, 0xc1a7, 0x40f5, { 0x83, 0x65, 0x4f, 0x46, 0xbe, 0x21, 0x86, 0x3e}}
+#endif
+
+//get the handle for a contact's parent metacontact
+//wParam=(HANDLE)hSubContact
+//lParam=0
+//returns a handle to the parent metacontact, or null if this contact is not a subcontact
+#define MS_MC_GETMETACONTACT "MetaContacts/GetMeta"
+
+//gets the handle for the default contact
+//wParam=(HANDLE)hMetaContact
+//lParam=0
+//returns a handle to the default contact, or null on failure
+#define MS_MC_GETDEFAULTCONTACT "MetaContacts/GetDefault"
+
+//gets the contact number for the default contact
+//wParam=(HANDLE)hMetaContact
+//lParam=0
+//returns a DWORD contact number, or -1 on failure
+#define MS_MC_GETDEFAULTCONTACTNUM "MetaContacts/GetDefaultNum"
+
+//gets the handle for the 'most online' contact
+//wParam=(HANDLE)hMetaContact
+//lParam=0
+//returns a handle to the 'most online' contact
+#define MS_MC_GETMOSTONLINECONTACT "MetaContacts/GetMostOnline"
+
+//gets the number of subcontacts for a metacontact
+//wParam=(HANDLE)hMetaContact
+//lParam=0
+//returns a DWORD representing the number of subcontacts for the given metacontact
+#define MS_MC_GETNUMCONTACTS "MetaContacts/GetNumContacts"
+
+//gets the handle of a subcontact, using the subcontact's number
+//wParam=(HANDLE)hMetaContact
+//lParam=(DWORD)contact number
+//returns a handle to the specified subcontact
+#define MS_MC_GETSUBCONTACT "MetaContacts/GetSubContact"
+
+//sets the default contact, using the subcontact's contact number
+//wParam=(HANDLE)hMetaContact
+//lParam=(DWORD)contact number
+//returns 0 on success
+#define MS_MC_SETDEFAULTCONTACTNUM "MetaContacts/SetDefault"
+
+//sets the default contact, using the subcontact's handle
+//wParam=(HANDLE)hMetaContact
+//lParam=(HANDLE)hSubcontact
+//returns 0 on success
+#define MS_MC_SETDEFAULTCONTACT "MetaContacts/SetDefaultByHandle"
+
+//forces the metacontact to send using a specific subcontact, using the subcontact's contact number
+//wParam=(HANDLE)hMetaContact
+//lParam=(DWORD)contact number
+//returns 0 on success
+#define MS_MC_FORCESENDCONTACTNUM "MetaContacts/ForceSendContact"
+
+//forces the metacontact to send using a specific subcontact, using the subcontact's handle
+//wParam=(HANDLE)hMetaContact
+//lParam=(HANDLE)hSubcontact
+//returns 0 on success (will fail if 'force default' is in effect)
+#define MS_MC_FORCESENDCONTACT "MetaContacts/ForceSendContactByHandle"
+
+//'unforces' the metacontact to send using a specific subcontact
+//wParam=(HANDLE)hMetaContact
+//lParam=0
+//returns 0 on success (will fail if 'force default' is in effect)
+#define MS_MC_UNFORCESENDCONTACT "MetaContacts/UnforceSendContact"
+
+//'forces' or 'unforces' (i.e. toggles) the metacontact to send using it's default contact
+// overrides (and clears) 'force send' above, and will even force use of offline contacts
+// will send ME_MC_FORCESEND or ME_MC_UNFORCESEND event
+//wParam=(HANDLE)hMetaContact
+//lParam=0
+//returns 1(true) or 0(false) representing new state of 'force default'
+#define MS_MC_FORCEDEFAULT "MetaContacts/ForceSendDefault"
+
+// method to get state of 'force' for a metacontact
+// wParam=(HANDLE)hMetaContact
+// lParam= (DWORD)&contact_number or NULL
+//
+// if lparam supplied, the contact_number of the contatct 'in force' will be copied to the address it points to,
+// or if none is in force, the value (DWORD)-1 will be copied
+// (v0.8.0.8+ returns 1 if 'force default' is true with *lParam == default contact number, else returns 0 with *lParam as above)
+#define MS_MC_GETFORCESTATE "MetaContacts/GetForceState"
+
+// fired when a metacontact's default contact changes (fired upon creation of metacontact also, when default is initially set)
+// wParam=(HANDLE)hMetaContact
+// lParam=(HANDLE)hDefaultContact
+#define ME_MC_DEFAULTTCHANGED "MetaContacts/DefaultChanged"
+
+// fired when a metacontact's subcontacts change (fired upon creation of metacontact, when contacts are added or removed, and when
+// contacts are reordered) - a signal to re-read metacontact data
+// wParam=(HANDLE)hMetaContact
+// lParam=0
+#define ME_MC_SUBCONTACTSCHANGED "MetaContacts/SubcontactsChanged"
+
+// fired when a metacontact is forced to send using a specific subcontact
+// wParam=(HANDLE)hMetaContact
+// lParam=(HANDLE)hForceContact
+#define ME_MC_FORCESEND "MetaContacts/ForceSend"
+
+// fired when a metacontact is 'unforced' to send using a specific subcontact
+// wParam=(HANDLE)hMetaContact
+// lParam=0
+#define ME_MC_UNFORCESEND "MetaContacts/UnforceSend"
+
+// method to get protocol name - used to be sure you're dealing with a "real" metacontacts plugin :)
+// wParam=lParam=0
+#define MS_MC_GETPROTOCOLNAME "MetaContacts/GetProtoName"
+
+
+// added 0.9.5.0 (22/3/05)
+// wParam=(HANDLE)hContact
+// lParam=0
+// convert a given contact into a metacontact
+#define MS_MC_CONVERTTOMETA "MetaContacts/ConvertToMetacontact"
+
+// added 0.9.5.0 (22/3/05)
+// wParam=(HANDLE)hContact
+// lParam=(HANDLE)hMeta
+// add an existing contact to a metacontact
+#define MS_MC_ADDTOMETA "MetaContacts/AddToMetacontact"
+
+// added 0.9.5.0 (22/3/05)
+// wParam=0
+// lParam=(HANDLE)hContact
+// remove a contact from a metacontact
+#define MS_MC_REMOVEFROMMETA "MetaContacts/RemoveFromMetacontact"
+
+
+// added 0.9.13.2 (6/10/05)
+// wParam=(BOOL)disable
+// lParam=0
+// enable/disable the 'hidden group hack' - for clists that support subcontact hiding using 'IsSubcontact' setting
+// should be called once in the clist 'onmodulesloaded' event handler (which, since it's loaded after the db, will be called
+// before the metacontact onmodulesloaded handler where the subcontact hiding is usually done)
+#define MS_MC_DISABLEHIDDENGROUP "MetaContacts/DisableHiddenGroup"
+
+#endif
diff --git a/Plugins/quickcontacts/sdk/m_updater.h b/Plugins/quickcontacts/sdk/m_updater.h
new file mode 100644
index 0000000..371b743
--- /dev/null
+++ b/Plugins/quickcontacts/sdk/m_updater.h
@@ -0,0 +1,146 @@
+#ifndef _M_UPDATER_H
+#define _M_UPDATER_H
+
+// NOTES:
+// - For langpack updates, include a string of the following format in the langpack text file:
+// ";FLID: <file listing name> <version>"
+// version must be four numbers seperated by '.', in the range 0-255 inclusive
+// - Updater will disable plugins that are downloaded but were not active prior to the update (this is so that, if an archive contains e.g. ansi and
+// unicode versions, the correct plugin will be the only one active after the new version is installed)...so if you add a support plugin, you may need
+// to install an ini file to make the plugin activate when miranda restarts after the update
+// - Updater will replace all dlls that have the same internal shortName as a downloaded update dll (this is so that msn1.dll and msn2.dll, for example,
+// will both be updated) - so if you have a unicode and a non-unicode version of a plugin in your archive, you should make the internal names different (which will break automatic
+// updates from the file listing if there is only one file listing entry for both versions, unless you use the 'MS_UPDATE_REGISTER' service below)
+// - Updater will install all files in the root of the archive into the plugins folder, except for langpack files that contain the FLID string which go into the root folder (same
+// folder as miranda32.exe)...all folders in the archive will also be copied to miranda's root folder, and their contents transferred into the new folders. The only exception is a
+// special folder called 'root_files' - if there is a folder by that name in the archive, it's contents will also be copied into miranda's root folder - this is intended to be used
+// to install additional dlls etc that a plugin may require)
+
+// if you set Update.szUpdateURL to the following value when registering, as well as setting your beta site and version data,
+// Updater will ignore szVersionURL and pbVersionPrefix, and attempt to find the file listing URL's from the backend XML data.
+// for this to work, the plugin name in pluginInfo.shortName must match the file listing exactly (except for case)
+#define UPDATER_AUTOREGISTER "UpdaterAUTOREGISTER"
+// Updater will also use the backend xml data if you provide URL's that reference the miranda file listing for updates (so you can use that method
+// if e.g. your plugin shortName does not match the file listing) - it will grab the file listing id from the end of these URLs
+
+typedef struct Update_tag {
+ int cbSize;
+ char *szComponentName; // component name as it will appear in the UI (will be translated before displaying)
+
+ char *szVersionURL; // URL where the current version can be found (NULL to disable)
+ BYTE *pbVersionPrefix; // bytes occuring in VersionURL before the version, used to locate the version information within the URL data
+ // (note that this URL could point at a binary file - dunno why, but it could :)
+ int cpbVersionPrefix; // number of bytes pointed to by pbVersionPrefix
+ char *szUpdateURL; // URL where dll/zip is located
+ // set to UPDATER_AUTOREGISTER if you want Updater to find the file listing URLs (ensure plugin shortName matches file listing!)
+
+ char *szBetaVersionURL; // URL where the beta version can be found (NULL to disable betas)
+ BYTE *pbBetaVersionPrefix; // bytes occuring in VersionURL before the version, used to locate the version information within the URL data
+ int cpbBetaVersionPrefix; // number of bytes pointed to by pbVersionPrefix
+ char *szBetaUpdateURL; // URL where dll/zip is located
+
+ BYTE *pbVersion; // bytes of current version, used for comparison with those in VersionURL
+ int cpbVersion; // number of bytes pointed to by pbVersion
+
+ char *szBetaChangelogURL; // url for displaying changelog for beta versions
+} Update;
+
+// register a comonent with Updater
+//
+// wparam = 0
+// lparam = (LPARAM)&Update
+#define MS_UPDATE_REGISTER "Update/Register"
+
+// utility functions to create a version string from a DWORD or from pluginInfo
+// point buf at a buffer at least 16 chars wide - but note the version string returned may be shorter
+//
+__inline static char *CreateVersionString(DWORD version, char *buf) {
+ mir_snprintf(buf, 16, "%d.%d.%d.%d", (version >> 24) & 0xFF, (version >> 16) & 0xFF, (version >> 8) & 0xFF, version & 0xFF);
+ return buf;
+}
+
+__inline static char *CreateVersionStringPlugin(PLUGININFO *pluginInfo, char *buf) {
+ return CreateVersionString(pluginInfo->version, buf);
+}
+
+
+// register the 'easy' way - use this method if you have no beta URL and the plugin is on the miranda file listing
+// NOTE: the plugin version string on the file listing must be the string version of the version in pluginInfo (i.e. 0.0.0.1,
+// four numbers between 0 and 255 inclusivem, so no letters, brackets, etc.)
+//
+// wParam = (int)fileID - this is the file ID from the file listing (i.e. the number at the end of the download link)
+// lParam = (PLUGININFO*)&pluginInfo
+#define MS_UPDATE_REGISTERFL "Update/RegisterFL"
+
+// this function can be used to 'unregister' components - useful for plugins that register non-plugin/langpack components and
+// may need to change those components on the fly
+// lParam = (char *)szComponentName
+#define MS_UPDATE_UNREGISTER "Update/Unregister"
+
+// this event is fired when the startup process is complete, but NOT if a restart is imminent
+// it is designed for status managment plugins to use as a trigger for beggining their own startup process
+// wParam = lParam = 0 (unused)
+// (added in version 0.1.6.0)
+#define ME_UPDATE_STARTUPDONE "Update/StartupDone"
+
+// this service can be used to enable/disable Updater's global status control
+// it can be called from the StartupDone event handler
+// wParam = (BOOL)enable
+// lParam = 0
+// (added in version 0.1.6.0)
+#define MS_UPDATE_ENABLESTATUSCONTROL "Update/EnableStatusControl"
+
+// An description of usage of the above service and event:
+// Say you are a status control plugin that normally sets protocol or global statuses in your ModulesLoaded event handler.
+// In order to make yourself 'Updater compatible', you would move the status control code from ModulesLoaded to another function,
+// say DoStartup. Then, in ModulesLoaded you would check for the existence of the MS_UPDATE_ENABLESTATUSCONTROL service.
+// If it does not exist, call DoStartup. If it does exist, hook the ME_UPDATE_STARTUPDONE event and call DoStartup from there. You may
+// also wish to call MS_UPDATE_ENABLESTATUSCONTROL with wParam == FALSE at this time, to disable Updater's own status control feature.
+
+// this service can be used to determine whether updates are possible for a component with the given name
+// wParam = 0
+// lParam = (char *)szComponentName
+// returns TRUE if updates are supported, FALSE otherwise
+#define MS_UPDATE_ISUPDATESUPPORTED "Update/IsUpdateSupported"
+
+#endif
+
+
+/////////////// Usage Example ///////////////
+
+#ifdef EXAMPLE_CODE
+
+// you need to #include "m_updater.h" and HookEvent(ME_SYSTEM_MODULESLOADED, OnModulesLoaded) in your Load function...
+
+int OnModulesLoaded(WPARAM wParam, LPARAM lParam) {
+
+ Update update = {0}; // for c you'd use memset or ZeroMemory...
+ char szVersion[16];
+
+ update.cbSize = sizeof(Update);
+
+ update.szComponentName = pluginInfo.shortName;
+ update.pbVersion = (BYTE *)CreateVersionString(&pluginInfo, szVersion);
+ update.cpbVersion = strlen((char *)update.pbVersion);
+
+ // these are the three lines that matter - the archive, the page containing the version string, and the text (or data)
+ // before the version that we use to locate it on the page
+ // (note that if the update URL and the version URL point to standard file listing entries, the backend xml
+ // data will be used to check for updates rather than the actual web page - this is not true for beta urls)
+ update.szUpdateURL = "http://scottellis.com.au:81/test/updater.zip";
+ update.szVersionURL = "http://scottellis.com.au:81/test/updater_test.html";
+ update.pbVersionPrefix = (BYTE *)"Updater version ";
+
+ update.cpbVersionPrefix = strlen((char *)update.pbVersionPrefix);
+
+ // do the same for the beta versions of the above struct members if you wish to allow beta updates from another URL
+
+ CallService(MS_UPDATE_REGISTER, 0, (WPARAM)&update);
+
+ // Alternatively, to register a plugin with e.g. file ID 2254 on the file listing...
+ // CallService(MS_UPDATE_REGISTERFL, (WPARAM)2254, (LPARAM)&pluginInfo);
+
+ return 0;
+}
+
+#endif
diff --git a/Plugins/quickcontacts/sdk/m_voice.h b/Plugins/quickcontacts/sdk/m_voice.h
new file mode 100644
index 0000000..a81d866
--- /dev/null
+++ b/Plugins/quickcontacts/sdk/m_voice.h
@@ -0,0 +1,158 @@
+/*
+Copyright (C) 2006 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#ifndef __M_VOICE_H__
+# define __M_VOICE_H__
+
+
+#define EVENTTYPE_VOICE_CALL 8739
+
+
+#define PROTOTYPE_VOICE (PROTOTYPE_ENCRYPTION-9)
+
+
+#define VOICE_UNICODE 0x80000000
+
+#ifdef UNICODE
+# define VOICE_TCHAR VOICE_UNICODE
+#else
+# define VOICE_TCHAR 0
+#endif
+
+#define VOICE_STATE_TALKING 0
+#define VOICE_STATE_RINGING 1
+#define VOICE_STATE_CALLING 2
+#define VOICE_STATE_ON_HOLD 3
+#define VOICE_STATE_ENDED 4
+
+typedef struct {
+ int cbSize; // Struct size
+ const char *szModule; // The name of the protocol module (used only in notifications)
+ char *id; // Protocol especific ID for this call
+ int flags; // Can be VOICE_CALL_CONTACT or VOICE_CALL_STRING (VOICE_UNICODE to say the string is unicode)
+ union { // Who to call
+ HANDLE hContact;
+ TCHAR *ptszContact;
+ char *pszContact;
+ WCHAR *pwszContact;
+ };
+ int state; // VOICE_STATE_*
+
+} VOICE_CALL;
+
+
+/*
+Notifies that a voice call changed state
+
+wParam: const VOICE_CALL *
+lParam: ignored
+return: 0 on success
+*/
+#define PE_VOICE_CALL_STATE "/Voice/State"
+
+
+#define VOICE_SUPPORTED 1 // Set if proto support voice calls. Probabilly will be 1 ;)
+#define VOICE_CALL_CONTACT 2 // Set if a call can be made to a hContact
+#define VOICE_CALL_CONTACT_NEED_TEST 4 // Set if the contact need to be tested with PS_VOICE_CALL_CONTACT_VALID (needs VOICE_CALL_CONTACT set to work)
+#define VOICE_CALL_STRING 8 // Set if a call can be made to some string (PS_VOICE_CALL_STRING_VALID is used to validate the string)
+#define VOICE_CAN_SET_DEVICE 16 // Set if the devices to mic in and sound out can be set (or the protocol will handle it internally)
+#define VOICE_CAN_HOLD 32 // Set if a call can be put on hold
+/*
+Get protocol voice support flags
+
+wParam: ignored
+lParam: ignored
+return: 0 on success
+*/
+#define PS_VOICE_GETINFO "/Voice/GetInfo"
+
+/*
+Request to the protocol a voice call to hContact.
+
+wParam: (HANDLE) hContact
+lParam: ignored
+return: 0 on success
+*/
+#define PS_VOICE_CALL "/Voice/Call"
+
+/*
+Service called to make the protocol answer a call.
+It is an async call. If the call was answered, the PE_VOICE_STARTEDCALL
+notification will be fired.
+
+wParam: (const char *) id
+lParam: ignored
+return: 0 on success
+*/
+#define PS_VOICE_ANSWERCALL "/Voice/AnswerCall"
+
+/*
+Service called to make the protocol answer a call. This can be called if the
+call is ringing or has started. If called any other time it should be ignored.
+It is an async call. If the call was droped, the PE_VOICE_ENDEDCALL
+notification will be fired.
+
+wParam: (const char *) id
+lParam: ignored
+return: 0 on success
+*/
+#define PS_VOICE_DROPCALL "/Voice/DropCall"
+
+/*
+Service called to make the protocol hold a call. This means that the call should not
+be droped, but it should be muted and put in a hold, to allow other call to be answered.
+If the protocol can't hold a cal, it should be droped.
+
+This can be called if the call has started. If called any other time it should be ignored.
+It is an async call. If the call was droped, the PE_VOICE_HOLDEDCALL
+notification will be fired.
+
+wParam: (const char *) id
+lParam: ignored
+return: 0 on success
+*/
+#define PS_VOICE_HOLDCALL "/Voice/HoldCall"
+
+/*
+Used if protocol support VOICE_CALL_STRING. The call string is passed as
+wParam and the proto should validate it.
+
+wParam: (const TCHAR *) call string
+lParam: ignored
+return: 0 if wrong, 1 if correct
+*/
+#define PS_VOICE_CALL_STRING_VALID "/Voice/CallStringValid"
+
+/*
+Used if protocol support VOICE_CALL_CONTACT and VOICE_CALL_CONTACT_NEED_TEST.
+The hContact is passed as wParam and the proto should tell if this contact can be
+called.
+
+wParam: (HANDLE) hContact
+lParam: (BOOL) TRUE if it is a test for 'can call now?', FALSE if is a test for 'will be possible to call someday?'
+return: 0 if can't be called, 1 if can
+*/
+#define PS_VOICE_CALL_CONTACT_VALID "/Voice/CallContactValid"
+
+
+
+
+
+#endif // __M_VOICE_H__
diff --git a/Plugins/quickcontacts/sdk/m_voiceservice.h b/Plugins/quickcontacts/sdk/m_voiceservice.h
new file mode 100644
index 0000000..98c3580
--- /dev/null
+++ b/Plugins/quickcontacts/sdk/m_voiceservice.h
@@ -0,0 +1,86 @@
+/*
+Copyright (C) 2007 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#ifndef __M_VOICESERVICE_H__
+# define __M_VOICESERVICE_H__
+
+#include "m_voice.h"
+
+
+#define MIID_VOICESERVICE { 0x7d64437, 0xef2e, 0x4f60, { 0xbb, 0x2d, 0x3c, 0x51, 0x8f, 0xe2, 0x4d, 0x63 } }
+
+
+/*
+This services are a mirror of the services/notifications in m_voice.h,
+with the difference that that ones are to be used by protocols, and this ones
+are to be used by plugins that can make calls to contacts in multiple protocols.
+*/
+
+
+/*
+Notifies that a voice call changed state
+
+wParam: const VOICE_CALL *
+lParam: ignored
+return: 0 on success
+*/
+#define MS_VOICESERVICE_STATE "VoiceService/State"
+
+
+
+struct VOICE_MODULE
+{
+ int cbSize; // sizeof(VOICE_MODULE)
+ char *name; // The internal name of the plugin. All PS_* serivces (except PS_VOICE_GETINFO)
+ // defined in m_voide.h need to be created based in this name. For example,
+ // PS_VOICE_CALL (/Voice/Call) need to be created as <name>/Voice/Call
+ int flags; // VOICE_* from m_voice.h
+};
+/*
+Register a new plugin that can make/receive voice calls.
+
+wParam: const VOICE_MODULE *
+lParam: ignored
+return: 0 on success
+*/
+#define MS_VOICESERVICE_REGISTER "VoiceService/Register"
+
+
+/*
+Request a voice call to hContact.
+
+wParam: (HANDLE) hContact
+lParam: ignored
+return: the number of option calls for a contact. If > 0, it can be called
+*/
+#define MS_VOICESERVICE_CAN_CALL "VoiceService/CanCall"
+
+/*
+Request a voice call to hContact.
+
+wParam: (HANDLE) hContact
+lParam: (char *) Protocol or NULL to use any proto avaiable
+return: 0 on success
+*/
+#define MS_VOICESERVICE_CALL "VoiceService/Call"
+
+
+
+#endif // __M_VOICESERVICE_H__
diff --git a/Plugins/qupdater/commons.h b/Plugins/qupdater/commons.h
new file mode 100644
index 0000000..e296f00
--- /dev/null
+++ b/Plugins/qupdater/commons.h
@@ -0,0 +1,84 @@
+/*
+Copyright (C) 2005 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#ifndef __COMMONS_H__
+# define __COMMONS_H__
+
+
+#include <windows.h>
+#include <commctrl.h>
+#include <tchar.h>
+#include <stdio.h>
+#include <time.h>
+
+
+// Disable "...truncated to '255' characters in the debug information" warnings
+#pragma warning(disable: 4786)
+
+#include <vector>
+using namespace std;
+
+
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+// Miranda headers
+#include <newpluginapi.h>
+#include <m_system.h>
+#include <m_langpack.h>
+#include <m_database.h>
+#include <m_options.h>
+#include <m_utils.h>
+#include <m_updater.h>
+
+#include "../utils/mir_memory.h"
+#include "../utils/mir_options.h"
+
+#include "resource.h"
+#include "options.h"
+
+
+#define MODULE_NAME "QUpdater"
+
+
+// Global Variables
+extern HINSTANCE hInst;
+extern PLUGINLINK *pluginLink;
+
+
+#define MAX_REGS(_A_) ( sizeof(_A_) / sizeof(_A_[0]) )
+
+
+extern vector<Update> plugins;
+
+void DumpFile();
+
+
+
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // __COMMONS_H__
diff --git a/Plugins/qupdater/options.cpp b/Plugins/qupdater/options.cpp
new file mode 100644
index 0000000..e26185c
--- /dev/null
+++ b/Plugins/qupdater/options.cpp
@@ -0,0 +1,195 @@
+/*
+Copyright (C) 2006 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#include "commons.h"
+
+
+
+// Prototypes /////////////////////////////////////////////////////////////////////////////////////
+
+HANDLE hOptHook = NULL;
+
+Options opts;
+
+
+static BOOL CALLBACK OptionsDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam);
+
+
+static OptPageControl optionsControls[] = {
+ { &opts.csv_file, CONTROL_TEXT, IDC_CSV, "CSVFile", (DWORD) _T("plugins.csv") },
+ { &opts.dump_on_startup, CONTROL_CHECKBOX, IDC_DUMP_STARTUP, "DumpOnStart", FALSE }
+};
+
+
+
+// Functions //////////////////////////////////////////////////////////////////////////////////////
+
+
+int InitOptionsCallback(WPARAM wParam,LPARAM lParam)
+{
+ OPTIONSDIALOGPAGE odp;
+
+ ZeroMemory(&odp,sizeof(odp));
+ odp.cbSize=sizeof(odp);
+ odp.position=0;
+ odp.hInstance=hInst;
+ odp.ptszGroup = TranslateT("Services");
+ odp.ptszTitle = TranslateT("Q Updater");
+ odp.pfnDlgProc = OptionsDlgProc;
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPTIONS);
+ odp.flags = ODPF_BOLDGROUPS | ODPF_TCHAR | ODPF_EXPERTONLY;
+ CallService(MS_OPT_ADDPAGE,wParam,(LPARAM)&odp);
+
+ return 0;
+}
+
+
+void InitOptions()
+{
+ LoadOptions();
+
+ hOptHook = HookEvent(ME_OPT_INITIALISE, InitOptionsCallback);
+}
+
+
+void DeInitOptions()
+{
+ UnhookEvent(hOptHook);
+}
+
+
+void LoadOptions()
+{
+ LoadOpts(optionsControls, MAX_REGS(optionsControls), MODULE_NAME);
+}
+
+
+void ListView_SetItemTextA( HWND hwndLV, int i, int iSubItem, char* pszText )
+{
+ LV_ITEMA _ms_lvi;
+ _ms_lvi.iSubItem = iSubItem;
+ _ms_lvi.pszText = pszText;
+ SendMessageA( hwndLV, LVM_SETITEMTEXTA, i, (LPARAM)&_ms_lvi);
+}
+
+
+static BOOL CALLBACK OptionsDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg)
+ {
+ case WM_INITDIALOG:
+ {
+ TranslateDialogDefault(hwndDlg);
+
+ HWND hwndList = GetDlgItem(hwndDlg,IDC_PLUGINS);
+
+ LVCOLUMN col = {0};
+ col.mask = LVCF_TEXT | LVCF_WIDTH;
+ col.pszText=TranslateT("Plugin Name");
+ col.cx=100;
+ ListView_InsertColumn(hwndList,0,&col);
+
+ col.pszText=TranslateT("Version");
+ col.cx=60;
+ ListView_InsertColumn(hwndList,1,&col);
+
+
+ col.pszText=TranslateT("Version URL");
+ col.cx=200;
+ ListView_InsertColumn(hwndList,2,&col);
+
+ col.pszText=TranslateT("Version Prefix");
+ col.cx=100;
+ ListView_InsertColumn(hwndList,3,&col);
+
+ col.pszText=TranslateT("Update URL");
+ col.cx=200;
+ ListView_InsertColumn(hwndList,4,&col);
+
+
+ col.pszText=TranslateT("Beta Version URL");
+ col.cx=200;
+ ListView_InsertColumn(hwndList,5,&col);
+
+ col.pszText=TranslateT("Beta Version Prefix");
+ col.cx=100;
+ ListView_InsertColumn(hwndList,6,&col);
+
+ col.pszText=TranslateT("Beta Update URL");
+ col.cx=200;
+ ListView_InsertColumn(hwndList,7,&col);
+
+
+ col.pszText=TranslateT("Beta Changelog URL");
+ ListView_InsertColumn(hwndList,8,&col);
+
+ // XXX: Won't work on windows 95 without IE3+ or 4.70
+ ListView_SetExtendedListViewStyleEx(hwndList, 0, LVS_EX_FULLROWSELECT );
+
+ for(int i = 0; i < plugins.size(); i++)
+ {
+ LVITEMA it = {0};
+ it.mask = LVIF_TEXT;
+ it.pszText = plugins[i].szComponentName;
+ int iRow = SendMessageA( hwndList, LVM_INSERTITEMA, 0, (LPARAM)&it );
+ if (plugins[i].pbVersion != NULL)
+ ListView_SetItemTextA(hwndList, iRow, 1, (char *) plugins[i].pbVersion);
+
+ if (plugins[i].szVersionURL != NULL)
+ ListView_SetItemTextA(hwndList, iRow, 2, plugins[i].szVersionURL);
+ if (plugins[i].pbVersionPrefix != NULL)
+ ListView_SetItemTextA(hwndList, iRow, 3, (char *) plugins[i].pbVersionPrefix);
+ if (plugins[i].szUpdateURL != NULL)
+ {
+ if (strcmp(plugins[i].szUpdateURL, UPDATER_AUTOREGISTER) == 0)
+ ListView_SetItemTextA(hwndList, iRow, 4, "<From FL XML>");
+ else
+ ListView_SetItemTextA(hwndList, iRow, 4, plugins[i].szUpdateURL);
+ }
+
+ if (plugins[i].szBetaVersionURL != NULL)
+ ListView_SetItemTextA(hwndList, iRow, 5, plugins[i].szBetaVersionURL);
+ if (plugins[i].pbBetaVersionPrefix != NULL)
+ ListView_SetItemTextA(hwndList, iRow, 6, (char *) plugins[i].pbBetaVersionPrefix);
+ if (plugins[i].szBetaUpdateURL != NULL)
+ ListView_SetItemTextA(hwndList, iRow, 7, plugins[i].szBetaUpdateURL);
+
+ if (plugins[i].szBetaChangelogURL != NULL)
+ ListView_SetItemTextA(hwndList, iRow, 8, plugins[i].szBetaChangelogURL);
+ }
+
+ // sort out the headers
+ ListView_SetColumnWidth(hwndList, 0, LVSCW_AUTOSIZE);
+
+ break;
+ }
+
+ case WM_COMMAND:
+ {
+ if (LOWORD(wParam) == IDC_DUMP)
+ DumpFile();
+
+ break;
+ }
+ }
+
+ return SaveOptsDlgProc(optionsControls, MAX_REGS(optionsControls), MODULE_NAME, hwndDlg, msg, wParam, lParam);
+}
+
diff --git a/Plugins/qupdater/options.h b/Plugins/qupdater/options.h
new file mode 100644
index 0000000..5712246
--- /dev/null
+++ b/Plugins/qupdater/options.h
@@ -0,0 +1,58 @@
+/*
+Copyright (C) 2006 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#ifndef __OPTIONS_H__
+# define __OPTIONS_H__
+
+
+#include <windows.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+
+struct Options {
+ TCHAR csv_file[10];
+ BOOL dump_on_startup;
+};
+
+extern Options opts;
+
+
+// Initializations needed by options
+void InitOptions();
+
+// Deinitializations needed by options
+void DeInitOptions();
+
+
+// Loads the options from DB
+// It don't need to be called, except in some rare cases
+void LoadOptions();
+
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // __OPTIONS_H__
diff --git a/Plugins/qupdater/qupdater.cpp b/Plugins/qupdater/qupdater.cpp
new file mode 100644
index 0000000..b772e70
--- /dev/null
+++ b/Plugins/qupdater/qupdater.cpp
@@ -0,0 +1,177 @@
+/*
+Copyright (C) 2006 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#include "commons.h"
+
+
+// Prototypes ///////////////////////////////////////////////////////////////////////////
+
+
+PLUGININFO pluginInfo = {
+ sizeof(PLUGININFO),
+ "Q Updater",
+ PLUGIN_MAKE_VERSION(0,0,0,1),
+ "Simulates updater API to gather plugin info to use at Q search site",
+ "Ricardo Pescuma Domenecci",
+ "",
+ "© 2007 Ricardo Pescuma Domenecci",
+ "http://q.mirandaim.ru",
+ 0, //not transient
+ 0 //doesn't replace anything built-in
+};
+
+
+HINSTANCE hInst;
+PLUGINLINK *pluginLink;
+
+HANDLE hModulesLoaded = NULL;
+
+int ModulesLoaded(WPARAM wParam, LPARAM lParam);
+int UpdateRegister(WPARAM wParam, LPARAM lParam);
+
+vector<Update> plugins;
+
+
+// Functions ////////////////////////////////////////////////////////////////////////////
+
+
+extern "C" BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
+{
+ hInst = hinstDLL;
+ return TRUE;
+}
+
+
+extern "C" __declspec(dllexport) PLUGININFO* MirandaPluginInfo(DWORD mirandaVersion)
+{
+ return &pluginInfo;
+}
+
+
+extern "C" int __declspec(dllexport) Load(PLUGINLINK *link)
+{
+ pluginLink = link;
+
+ init_mir_malloc();
+ InitOptions();
+
+ CreateServiceFunction(MS_UPDATE_REGISTER, UpdateRegister);
+
+ // hooks
+ hModulesLoaded = HookEvent(ME_SYSTEM_MODULESLOADED, ModulesLoaded);
+
+ return 0;
+}
+
+extern "C" int __declspec(dllexport) Unload(void)
+{
+ DeInitOptions();
+ UnhookEvent(hModulesLoaded);
+ return 0;
+}
+
+
+// Called when all the modules are loaded
+int ModulesLoaded(WPARAM wParam, LPARAM lParam)
+{
+ // add our modules to the KnownModules list
+ CallService("DBEditorpp/RegisterSingleModule", (WPARAM) MODULE_NAME, 0);
+
+ return 0;
+}
+
+
+// Called when all the modules are loaded
+int UpdateRegister(WPARAM wParam, LPARAM lParam)
+{
+ Update *in = (Update *) lParam;
+ if (in == NULL) // Some plugins don't respect this: || in->cbSize < sizeof(Update))
+ return NULL;
+
+ Update updt = {0};
+ updt.szComponentName = mir_strdup(in->szComponentName);
+ updt.szVersionURL = mir_strdup(in->szVersionURL);
+ updt.pbVersionPrefix = (BYTE *) mir_strdup((char *) in->pbVersionPrefix);
+ updt.szUpdateURL = mir_strdup(in->szUpdateURL);
+
+ updt.szBetaVersionURL = mir_strdup(in->szBetaVersionURL);
+ updt.pbBetaVersionPrefix = (BYTE *) mir_strdup((char *) in->pbBetaVersionPrefix);
+ updt.szBetaUpdateURL = mir_strdup(in->szBetaUpdateURL);
+
+ updt.pbVersion = (BYTE *) mir_strdup((char *) in->pbVersion);
+
+ if (in->cbSize >= sizeof(Update))
+ updt.szBetaChangelogURL = mir_strdup(in->szBetaChangelogURL);
+
+ plugins.insert(plugins.begin(), updt);
+
+ if (opts.dump_on_startup)
+ DumpFile();
+
+ return 0;
+}
+
+void DumpFile()
+{
+ FILE *arq = fopen(opts.csv_file, "w");
+ if (arq == NULL)
+ return;
+
+ for(int i = 0; i < plugins.size(); i++)
+ {
+ fprintf(arq, "\"");
+ fprintf(arq, plugins[i].szComponentName);
+ fprintf(arq, "\",\"");
+ if (plugins[i].pbVersion != NULL)
+ fprintf(arq, (char *) plugins[i].pbVersion);
+ fprintf(arq, "\",\"");
+
+ if (plugins[i].szVersionURL != NULL)
+ fprintf(arq, plugins[i].szVersionURL);
+ fprintf(arq, "\",\"");
+ if (plugins[i].pbVersionPrefix != NULL)
+ fprintf(arq, (char *) plugins[i].pbVersionPrefix);
+ fprintf(arq, "\",\"");
+ if (plugins[i].szUpdateURL != NULL)
+ {
+ if (strcmp(plugins[i].szUpdateURL, UPDATER_AUTOREGISTER) == 0)
+ fprintf(arq, "<From FL XML>");
+ else
+ fprintf(arq, plugins[i].szUpdateURL);
+ }
+ fprintf(arq, "\",\"");
+
+ if (plugins[i].szBetaVersionURL != NULL)
+ fprintf(arq, plugins[i].szBetaVersionURL);
+ fprintf(arq, "\",\"");
+ if (plugins[i].pbBetaVersionPrefix != NULL)
+ fprintf(arq, (char *) plugins[i].pbBetaVersionPrefix);
+ fprintf(arq, "\",\"");
+ if (plugins[i].szBetaUpdateURL != NULL)
+ fprintf(arq, plugins[i].szBetaUpdateURL);
+ fprintf(arq, "\",\"");
+
+ if (plugins[i].szBetaChangelogURL != NULL)
+ fprintf(arq, plugins[i].szBetaChangelogURL);
+ fprintf(arq, "\"\n");
+ }
+
+ fclose(arq);
+} \ No newline at end of file
diff --git a/Plugins/qupdater/qupdater.dsp b/Plugins/qupdater/qupdater.dsp
new file mode 100644
index 0000000..c9c4ae0
--- /dev/null
+++ b/Plugins/qupdater/qupdater.dsp
@@ -0,0 +1,145 @@
+# Microsoft Developer Studio Project File - Name="qupdater" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=qupdater - Win32 Release
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "qupdater.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "qupdater.mak" CFG="qupdater - Win32 Release"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "qupdater - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "qupdater - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "qupdater - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MD /W3 /GX /O1 /YX /FD /c
+# SUBTRACT BASE CPP /Fr
+# ADD CPP /nologo /G4 /MT /W3 /O2 /Ob0 /I "../../include" /I "sdk" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /Fr /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x417 /d "NDEBUG"
+# ADD RSC /l 0x417 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 user32.lib shell32.lib wininet.lib gdi32.lib /nologo /base:"0x67100000" /dll /machine:I386 /filealign:0x200
+# SUBTRACT BASE LINK32 /pdb:none /map
+# ADD LINK32 kernel32.lib user32.lib /nologo /base:"0x3EC20000" /dll /map /machine:I386 /out:"..\..\bin\release\Plugins\qupdater.dll" /filealign:0x200 /ALIGN:4096 /ignore:4108
+# SUBTRACT LINK32 /profile /pdb:none
+
+!ELSEIF "$(CFG)" == "qupdater - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Ignore_Export_Lib 0
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /G4 /MT /W3 /GX /O2 /Ob0 /I "../../include" /FR /YX /FD /c
+# ADD CPP /nologo /G4 /MTd /W3 /GX /ZI /Od /I "../../include" /I "sdk" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /FR /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x417 /d "NDEBUG"
+# ADD RSC /l 0x417 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 comctl32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 /out:"..\..bin\release\Plugins\qupdater.dll" /filealign:0x200 /ALIGN:4096 /ignore:4108
+# SUBTRACT BASE LINK32 /profile /pdb:none
+# ADD LINK32 kernel32.lib user32.lib /nologo /base:"0x3EC20000" /dll /incremental:yes /debug /machine:I386 /out:"..\..\bin\debug unicode\Plugins\qupdater.dll" /filealign:0x200 /ALIGN:4096 /ignore:4108
+# SUBTRACT LINK32 /profile /pdb:none
+
+!ENDIF
+
+# Begin Target
+
+# Name "qupdater - Win32 Release"
+# Name "qupdater - Win32 Debug"
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=.\commons.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\utils\mir_memory.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\utils\mir_options.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\options.h
+# End Source File
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
+# Begin Source File
+
+SOURCE=.\resource.rc
+# End Source File
+# End Group
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=..\utils\mir_memory.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\utils\mir_options.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\options.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\qupdater.cpp
+# End Source File
+# End Group
+# End Target
+# End Project
diff --git a/Plugins/qupdater/qupdater.dsw b/Plugins/qupdater/qupdater.dsw
new file mode 100644
index 0000000..4380739
--- /dev/null
+++ b/Plugins/qupdater/qupdater.dsw
@@ -0,0 +1,29 @@
+Microsoft Developer Studio Workspace File, Format Version 6.00
+# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
+
+###############################################################################
+
+Project: "qupdater"=".\qupdater.dsp" - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Global:
+
+Package=<5>
+{{{
+}}}
+
+Package=<3>
+{{{
+}}}
+
+###############################################################################
+
diff --git a/Plugins/qupdater/resource.h b/Plugins/qupdater/resource.h
new file mode 100644
index 0000000..fad98da
--- /dev/null
+++ b/Plugins/qupdater/resource.h
@@ -0,0 +1,21 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Developer Studio generated include file.
+// Used by resource.rc
+//
+#define IDD_OPTIONS 102
+#define IDC_EDIT1 1000
+#define IDC_CSV 1000
+#define IDC_DUMP 1001
+#define IDC_PLUGINS 1002
+#define IDC_DUMP_STARTUP 1003
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 101
+#define _APS_NEXT_COMMAND_VALUE 40001
+#define _APS_NEXT_CONTROL_VALUE 1004
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/Plugins/qupdater/resource.rc b/Plugins/qupdater/resource.rc
new file mode 100644
index 0000000..9a1e3f6
--- /dev/null
+++ b/Plugins/qupdater/resource.rc
@@ -0,0 +1,113 @@
+//Microsoft Developer Studio generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "afxres.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (U.S.) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page(1252)
+#endif //_WIN32
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Dialog
+//
+
+IDD_OPTIONS DIALOGEX 0, 0, 275, 228
+STYLE DS_FIXEDSYS | WS_CHILD | WS_VISIBLE
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg"
+BEGIN
+ GROUPBOX " Output File ",IDC_STATIC,1,0,274,68
+ LTEXT "CSV File:",IDC_STATIC,11,16,51,10
+ EDITTEXT IDC_CSV,65,14,202,13,ES_AUTOHSCROLL
+ PUSHBUTTON "Dump Now",IDC_DUMP,203,50,64,13
+ GROUPBOX "Plugins ",IDC_STATIC,1,72,274,148
+ CONTROL "List1",IDC_PLUGINS,"SysListView32",LVS_REPORT |
+ LVS_NOSORTHEADER | WS_BORDER | WS_TABSTOP,7,84,261,129
+ CONTROL "Dump on miranda start",IDC_DUMP_STARTUP,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,10,34,257,9
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// DESIGNINFO
+//
+
+#ifdef APSTUDIO_INVOKED
+GUIDELINES DESIGNINFO DISCARDABLE
+BEGIN
+ IDD_OPTIONS, DIALOG
+ BEGIN
+ BOTTOMMARGIN, 210
+ END
+END
+#endif // APSTUDIO_INVOKED
+
+#endif // English (U.S.) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+/////////////////////////////////////////////////////////////////////////////
+// Portuguese (Brazil) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_PTB)
+#ifdef _WIN32
+LANGUAGE LANG_PORTUGUESE, SUBLANG_PORTUGUESE_BRAZILIAN
+#pragma code_page(1252)
+#endif //_WIN32
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "resource.h\0"
+END
+
+2 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "#include ""afxres.h""\r\n"
+ "\0"
+END
+
+3 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "\r\n"
+ "\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+#endif // Portuguese (Brazil) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
+
diff --git a/Plugins/qupdater/sdk/m_updater.h b/Plugins/qupdater/sdk/m_updater.h
new file mode 100644
index 0000000..371b743
--- /dev/null
+++ b/Plugins/qupdater/sdk/m_updater.h
@@ -0,0 +1,146 @@
+#ifndef _M_UPDATER_H
+#define _M_UPDATER_H
+
+// NOTES:
+// - For langpack updates, include a string of the following format in the langpack text file:
+// ";FLID: <file listing name> <version>"
+// version must be four numbers seperated by '.', in the range 0-255 inclusive
+// - Updater will disable plugins that are downloaded but were not active prior to the update (this is so that, if an archive contains e.g. ansi and
+// unicode versions, the correct plugin will be the only one active after the new version is installed)...so if you add a support plugin, you may need
+// to install an ini file to make the plugin activate when miranda restarts after the update
+// - Updater will replace all dlls that have the same internal shortName as a downloaded update dll (this is so that msn1.dll and msn2.dll, for example,
+// will both be updated) - so if you have a unicode and a non-unicode version of a plugin in your archive, you should make the internal names different (which will break automatic
+// updates from the file listing if there is only one file listing entry for both versions, unless you use the 'MS_UPDATE_REGISTER' service below)
+// - Updater will install all files in the root of the archive into the plugins folder, except for langpack files that contain the FLID string which go into the root folder (same
+// folder as miranda32.exe)...all folders in the archive will also be copied to miranda's root folder, and their contents transferred into the new folders. The only exception is a
+// special folder called 'root_files' - if there is a folder by that name in the archive, it's contents will also be copied into miranda's root folder - this is intended to be used
+// to install additional dlls etc that a plugin may require)
+
+// if you set Update.szUpdateURL to the following value when registering, as well as setting your beta site and version data,
+// Updater will ignore szVersionURL and pbVersionPrefix, and attempt to find the file listing URL's from the backend XML data.
+// for this to work, the plugin name in pluginInfo.shortName must match the file listing exactly (except for case)
+#define UPDATER_AUTOREGISTER "UpdaterAUTOREGISTER"
+// Updater will also use the backend xml data if you provide URL's that reference the miranda file listing for updates (so you can use that method
+// if e.g. your plugin shortName does not match the file listing) - it will grab the file listing id from the end of these URLs
+
+typedef struct Update_tag {
+ int cbSize;
+ char *szComponentName; // component name as it will appear in the UI (will be translated before displaying)
+
+ char *szVersionURL; // URL where the current version can be found (NULL to disable)
+ BYTE *pbVersionPrefix; // bytes occuring in VersionURL before the version, used to locate the version information within the URL data
+ // (note that this URL could point at a binary file - dunno why, but it could :)
+ int cpbVersionPrefix; // number of bytes pointed to by pbVersionPrefix
+ char *szUpdateURL; // URL where dll/zip is located
+ // set to UPDATER_AUTOREGISTER if you want Updater to find the file listing URLs (ensure plugin shortName matches file listing!)
+
+ char *szBetaVersionURL; // URL where the beta version can be found (NULL to disable betas)
+ BYTE *pbBetaVersionPrefix; // bytes occuring in VersionURL before the version, used to locate the version information within the URL data
+ int cpbBetaVersionPrefix; // number of bytes pointed to by pbVersionPrefix
+ char *szBetaUpdateURL; // URL where dll/zip is located
+
+ BYTE *pbVersion; // bytes of current version, used for comparison with those in VersionURL
+ int cpbVersion; // number of bytes pointed to by pbVersion
+
+ char *szBetaChangelogURL; // url for displaying changelog for beta versions
+} Update;
+
+// register a comonent with Updater
+//
+// wparam = 0
+// lparam = (LPARAM)&Update
+#define MS_UPDATE_REGISTER "Update/Register"
+
+// utility functions to create a version string from a DWORD or from pluginInfo
+// point buf at a buffer at least 16 chars wide - but note the version string returned may be shorter
+//
+__inline static char *CreateVersionString(DWORD version, char *buf) {
+ mir_snprintf(buf, 16, "%d.%d.%d.%d", (version >> 24) & 0xFF, (version >> 16) & 0xFF, (version >> 8) & 0xFF, version & 0xFF);
+ return buf;
+}
+
+__inline static char *CreateVersionStringPlugin(PLUGININFO *pluginInfo, char *buf) {
+ return CreateVersionString(pluginInfo->version, buf);
+}
+
+
+// register the 'easy' way - use this method if you have no beta URL and the plugin is on the miranda file listing
+// NOTE: the plugin version string on the file listing must be the string version of the version in pluginInfo (i.e. 0.0.0.1,
+// four numbers between 0 and 255 inclusivem, so no letters, brackets, etc.)
+//
+// wParam = (int)fileID - this is the file ID from the file listing (i.e. the number at the end of the download link)
+// lParam = (PLUGININFO*)&pluginInfo
+#define MS_UPDATE_REGISTERFL "Update/RegisterFL"
+
+// this function can be used to 'unregister' components - useful for plugins that register non-plugin/langpack components and
+// may need to change those components on the fly
+// lParam = (char *)szComponentName
+#define MS_UPDATE_UNREGISTER "Update/Unregister"
+
+// this event is fired when the startup process is complete, but NOT if a restart is imminent
+// it is designed for status managment plugins to use as a trigger for beggining their own startup process
+// wParam = lParam = 0 (unused)
+// (added in version 0.1.6.0)
+#define ME_UPDATE_STARTUPDONE "Update/StartupDone"
+
+// this service can be used to enable/disable Updater's global status control
+// it can be called from the StartupDone event handler
+// wParam = (BOOL)enable
+// lParam = 0
+// (added in version 0.1.6.0)
+#define MS_UPDATE_ENABLESTATUSCONTROL "Update/EnableStatusControl"
+
+// An description of usage of the above service and event:
+// Say you are a status control plugin that normally sets protocol or global statuses in your ModulesLoaded event handler.
+// In order to make yourself 'Updater compatible', you would move the status control code from ModulesLoaded to another function,
+// say DoStartup. Then, in ModulesLoaded you would check for the existence of the MS_UPDATE_ENABLESTATUSCONTROL service.
+// If it does not exist, call DoStartup. If it does exist, hook the ME_UPDATE_STARTUPDONE event and call DoStartup from there. You may
+// also wish to call MS_UPDATE_ENABLESTATUSCONTROL with wParam == FALSE at this time, to disable Updater's own status control feature.
+
+// this service can be used to determine whether updates are possible for a component with the given name
+// wParam = 0
+// lParam = (char *)szComponentName
+// returns TRUE if updates are supported, FALSE otherwise
+#define MS_UPDATE_ISUPDATESUPPORTED "Update/IsUpdateSupported"
+
+#endif
+
+
+/////////////// Usage Example ///////////////
+
+#ifdef EXAMPLE_CODE
+
+// you need to #include "m_updater.h" and HookEvent(ME_SYSTEM_MODULESLOADED, OnModulesLoaded) in your Load function...
+
+int OnModulesLoaded(WPARAM wParam, LPARAM lParam) {
+
+ Update update = {0}; // for c you'd use memset or ZeroMemory...
+ char szVersion[16];
+
+ update.cbSize = sizeof(Update);
+
+ update.szComponentName = pluginInfo.shortName;
+ update.pbVersion = (BYTE *)CreateVersionString(&pluginInfo, szVersion);
+ update.cpbVersion = strlen((char *)update.pbVersion);
+
+ // these are the three lines that matter - the archive, the page containing the version string, and the text (or data)
+ // before the version that we use to locate it on the page
+ // (note that if the update URL and the version URL point to standard file listing entries, the backend xml
+ // data will be used to check for updates rather than the actual web page - this is not true for beta urls)
+ update.szUpdateURL = "http://scottellis.com.au:81/test/updater.zip";
+ update.szVersionURL = "http://scottellis.com.au:81/test/updater_test.html";
+ update.pbVersionPrefix = (BYTE *)"Updater version ";
+
+ update.cpbVersionPrefix = strlen((char *)update.pbVersionPrefix);
+
+ // do the same for the beta versions of the above struct members if you wish to allow beta updates from another URL
+
+ CallService(MS_UPDATE_REGISTER, 0, (WPARAM)&update);
+
+ // Alternatively, to register a plugin with e.g. file ID 2254 on the file listing...
+ // CallService(MS_UPDATE_REGISTERFL, (WPARAM)2254, (LPARAM)&pluginInfo);
+
+ return 0;
+}
+
+#endif
diff --git a/Plugins/rps/Docs/rps_changelog.txt b/Plugins/rps/Docs/rps_changelog.txt
new file mode 100644
index 0000000..1b10914
--- /dev/null
+++ b/Plugins/rps/Docs/rps_changelog.txt
@@ -0,0 +1,22 @@
+Remove Personal Settings
+
+Changelog:
+
+. 0.1.0.4
+ + Added * to delete module names
+ + Added miranda 0.8 support
+ + Updated ini file
+
+. 0.1.0.3
+ + Added option to delete modules based on a sufix of the protocol name
+ + Added * to delete settings names
+ + Added config to delete alarms and sent files
+
+. 0.1.0.2
+ + Added SetProtocolsOffline
+ * Bugfixes
+
+. 0.1.0.1
+ + Added RemoveWholeProtocolModule
+ + Added DisabledProtocols to delete settings from these
+ + Added deletion of events from DB \ No newline at end of file
diff --git a/Plugins/rps/Docs/rps_readme.txt b/Plugins/rps/Docs/rps_readme.txt
new file mode 100644
index 0000000..f6aed13
--- /dev/null
+++ b/Plugins/rps/Docs/rps_readme.txt
@@ -0,0 +1,22 @@
+Remove Personal Settings plugin
+-------------------------------
+
+WARNING: THIS PLUGIN DELETE SETTINGS FROM MIRANDA DB AND FROM THE HD. THIS CAN'T BE UNDONE. IT CAN DELETE ALL FILES IN YOUR COMPUTER (ALTOUGHT IT NEVER HAPPENED TO ME :P ). USE IT AT YOUR OWN RISK.
+
+This is a plugin that removes personal settings from database.
+
+It can remove all users, remove some files and directories from miranda dir (but only if these aren't beeing used), call services and disable plugins. To do that it is based in a configuration .ini file. If you need some especial configuration (and probabily you will need, because I just put some settings there, but I bet I forgot some), just edit the file RemovePersonalSettings.ini
+
+The idea for using this plugin is to allow sending a copy of you miranda with your actual configuration, but without your personal settings. To use it, you should:
+1) Copy the hole miranda dir to other location
+2) Enable the plugin (if it isn't enabled)
+4) Set global status to offline
+3) Run the menu option "Remove personal settings..."
+4) Exit miranda
+4) Run DBTOOL.EXE to compact sweeped database file
+5) Pack and send to your friend
+
+I made this based on the discussion in the thread:
+http://forums.miranda-im.org/showthread.php?t=5224. I asked the source from nullbie and made my version of the plugin.
+
+To report bugs/make suggestions, go to the forum thread: http://forums.miranda-im.org/showthread.php?t=5451
diff --git a/Plugins/rps/Docs/rps_version.txt b/Plugins/rps/Docs/rps_version.txt
new file mode 100644
index 0000000..affe4aa
--- /dev/null
+++ b/Plugins/rps/Docs/rps_version.txt
@@ -0,0 +1 @@
+Remove Personal Settings 0.1.0.4 \ No newline at end of file
diff --git a/Plugins/rps/RemovePersonalSettings.ini b/Plugins/rps/RemovePersonalSettings.ini
new file mode 100644
index 0000000..789803d
--- /dev/null
+++ b/Plugins/rps/RemovePersonalSettings.ini
@@ -0,0 +1,247 @@
+[GlobalSettings]
+; Set protocol status to offline before any other action
+; true or false
+; default=true
+SetProtocolsOffline=true
+
+; Remove all folders that have protocol names inside miranda folder
+; true or false
+; default=true
+RemoveProtocolFolders=true
+
+; Remove all miranda users
+; true or false
+; default=true
+RemoveAllUsers=true
+
+; Remove some default settings for all enabled protocols
+; The name of the module has to be the same name as the name of the protocol
+; These are the ones under ProtocolSettings
+; true or false
+; default=true
+RemoveProtocolSettings=true
+
+; Remove all settings inside the protocol module
+; (and not only the ones at ProtocolSettings)
+; true or false
+; default=false
+RemoveWholeProtocolModule=false
+
+[DisabledProtocols]
+; Names of protocols that are disabled, but have to has its settings deleted
+; The case must be correct!
+YAHOO
+YAHOO1
+YAHOO2
+YAHOO3
+YAHOO4
+MSN
+MSN1
+MSN2
+MSN3
+MSN4
+ICQ
+ICQ1
+ICQ2
+ICQ3
+ICQ4
+GG
+GG1
+GG2
+GG3
+GG4
+JABBER
+JABBER1
+JABBER2
+JABBER3
+JABBER4
+JGMAIL
+JGMAIL1
+JGMAIL2
+JGMAIL3
+JGMAIL4
+MEEBO
+MEEBO1
+MEEBO2
+MEEBO3
+MEEBO4
+AIM
+AIM1
+AIM2
+AIM3
+AIM4
+SKYPE
+MYSPACE
+
+[ProtocolSettings]
+; Settings that will be erased for all enabled protocols if RemoveProtocolSettings == true
+; Names with spaces or strange chars must be surrounded by "
+Password
+"Nick"
+LoginName
+DisplayName
+PNick
+SN
+Name
+FirstName
+LastName
+Username
+UserName
+User
+UID
+Timezone
+e-mail
+Email
+email
+UIN
+IdleTS
+InfoTS
+LogonTS
+MemberTS
+LastSyncTime
+PictObject
+YourHost
+AvatarFile
+AvatarFilename
+AvatarHash
+AvatarSaved
+ImageURL
+BirthDay
+BirthMonth
+BirthYear
+MaritalStatus
+Gender
+Age
+CompanyOccupation
+Country
+OriginCountry
+Location
+InfoCP
+PublishPrimaryEmail
+SrvAvatarID
+SrvDefGroupId
+SrvLastUpdate
+SrvRecordCount
+SrvVisibility
+SrvVisibilityID
+XStatusMsg
+XStatusMessage
+AlernativeNick
+Status
+ServerComboSelection
+UserID
+Resource
+FirstRun
+WebAvare
+FullName
+LoginName
+"First Name"
+"Last Name"
+jid
+Profile
+NLProxyPort
+NLProxyServer
+City
+Language1
+Language2
+Language3
+Past0
+IP
+RealIP
+State
+QuitMessage
+UserInfo
+GenderString
+GoogleToken
+Override
+yahoo_id
+AvatarURL
+AvatarInv
+
+[ProtocolModuleSufixes]
+; Strings here will be appended to each protocol name and the module with this name
+; will be deleted (the whole module)
+; Names with spaces or strange chars must be surrounded by "
+Groups
+SrvGroups
+P2P
+:HotmailNotify
+
+[RemoveSettings]
+; Settings that should be removed
+; Only from hContact == NULL
+; To remove a module, use ModuleName
+; To remove a setting, use ModuleName/SettingName
+; To remove all settings that start with a name, use * as last char
+; Ex: Test/Test*
+; To remove all settings that end with a name, use * as first key char
+; Ex: Test/*Test
+; Settings are case sensitive
+; Names with spaces or strange chars must be surrounded by "
+"Gmail Notifier/name"
+"Gmail Notifier/pass"
+POP3
+pattern
+Options
+ContactPhoto/File
+MIMImport
+*/Password
+*/password
+FTPFile/password
+FTPFile/user
+FTPFile/url
+FTPFile/server
+SMSPlugin
+SRFile/MruDir*
+SRFile/RecvFilesDirAdv
+UserOnline
+WebSMS-Plugin/LastNumber*
+WebSMS-Plugin/LastPrefix*
+"WinPopup Protocol/Nick"
+"WinPopup Protocol/User"
+"WinPopup Protocol/Workgroup"
+NET_SEND/loging_name
+Alarm/ActionFlags*
+Alarm/Desc*
+Alarm/Hidden*
+Alarm/Occ*
+Alarm/NoStartup*
+Alarm/Snoozer*
+Alarm/SoundNum*
+Alarm/STDay*
+Alarm/STHour*
+Alarm/STMinute*
+Alarm/STMonth*
+Alarm/STSecond*
+Alarm/STYear*
+Alarm/Suspended*
+Alarm/Title*
+*/NLProxyPort
+*/NLProxyServer
+QuickContacts/LastSentTo
+VersionInfo/UploadPassword
+VersionInfo/UploadUser
+
+[ExecuteServices]
+; Always calls passing 0 as wParam and lParam
+; Names with spaces or strange chars must be surrounded by "
+DB3XS/RemovePassword
+
+[RemoveFilesOrFolders]
+; This should be names inside miranda folder
+; Names with spaces or strange chars must be surrounded by "
+; * and ? can be used
+Avatars
+"Avatars History"
+Logs
+"Received Files"
+tabSRMM
+Updates
+UpdateData
+UpdateBackups
+yamn-accounts.pop3.*.book
+
+[DisablePlugins]
+; Plugins dlls to be disabled
+; Names with spaces or strange chars must be surrounded by "
+RemovePersonalSettings.dll
diff --git a/Plugins/rps/ZIP/doit.bat b/Plugins/rps/ZIP/doit.bat
new file mode 100644
index 0000000..4791122
--- /dev/null
+++ b/Plugins/rps/ZIP/doit.bat
@@ -0,0 +1,58 @@
+@echo off
+
+rem Batch file to build and upload files
+rem
+rem TODO: Integration with FL
+
+set name=rps
+
+rem To upload, this var must be set here or in other batch
+rem set ftp=ftp://<user>:<password>@<ftp>/<path>
+
+echo Building %name% ...
+
+msdev ..\%name%.dsp /MAKE "%name% - Win32 Release" /REBUILD
+
+echo Generating files for %name% ...
+
+del *.zip
+del *.dll
+copy ..\Docs\%name%_changelog.txt
+copy ..\Docs\%name%_version.txt
+copy ..\Docs\%name%_readme.txt
+mkdir Docs
+cd Docs
+del /Q *.*
+copy ..\..\Docs\%name%_readme.txt
+cd ..
+mkdir Plugins
+cd Plugins
+del /Q *.*
+copy ..\..\..\..\bin\release\Plugins\RemovePersonalSettings.dll
+copy ..\..\RemovePersonalSettings.ini
+cd ..
+
+"C:\Program Files\Filzip\Filzip.exe" -a -rp %name%.zip Docs Plugins
+
+cd Docs
+del /Q *.*
+cd ..
+rmdir Docs
+cd Plugins
+del /Q *.*
+cd ..
+rmdir Plugins
+
+if "%ftp%"=="" GOTO END
+
+echo Going to upload files...
+pause
+
+"C:\Program Files\FileZilla\FileZilla.exe" -u .\%name%.zip %ftp% -overwrite -close
+"C:\Program Files\FileZilla\FileZilla.exe" -u .\%name%_changelog.txt %ftp% -overwrite -close
+"C:\Program Files\FileZilla\FileZilla.exe" -u .\%name%_version.txt %ftp% -overwrite -close
+"C:\Program Files\FileZilla\FileZilla.exe" -u .\%name%_readme.txt %ftp% -overwrite -close
+
+:END
+
+echo Done.
diff --git a/Plugins/rps/rps.c b/Plugins/rps/rps.c
new file mode 100644
index 0000000..d358f12
--- /dev/null
+++ b/Plugins/rps/rps.c
@@ -0,0 +1,833 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2005 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+Created by Pescuma
+Based on work by nullbie
+
+*/
+#include <windows.h>
+#include <io.h>
+#include <stdio.h>
+#include <newpluginapi.h>
+#include <m_clist.h>
+#include <m_skin.h>
+#include <m_langpack.h>
+#include <m_protocols.h>
+#include <m_protosvc.h>
+#include <m_utils.h>
+#include <m_database.h>
+
+#define MIID_REMOVEPERSONALSETTINGS { 0x5eaec989, 0x8ff, 0x4820, { 0xb8, 0x6c, 0x2b, 0x6e, 0xf0, 0x8e, 0x33, 0x73 } }
+
+#define INI_FILE_NAME "RemovePersonalSettings.ini"
+
+#define PLUGINDISABLELIST "PluginDisable"
+
+#define METACONTACTS_PROTOCOL_NAME "MetaContacts"
+
+
+HINSTANCE hInst;
+PLUGINLINK *pluginLink;
+char gIniFile[MAX_PATH];
+char gMirandaDir[MAX_PATH];
+
+
+PLUGININFOEX pluginInfo={
+ sizeof(PLUGININFO),
+ "Remove Personal Settings",
+ PLUGIN_MAKE_VERSION(0,1,0,4),
+ "Remove personal settings to allow to send a profile to other user(s) without sending personal data.",
+ "Ricardo Pescuma Domenecci",
+ "",
+ "© 2007-2009 Ricardo Pescuma Domenecci",
+ "http://pescuma.org/miranda/rps",
+ 0, //not transient
+ 0, //doesn't replace anything built-in
+ { 0x60e94b84, 0xa799, 0x4021, { 0x94, 0x49, 0x5b, 0x83, 0x8f, 0xc0, 0x6a, 0x7c } } // {60E94B84-A799-4021-9449-5B838FC06A7C}
+};
+
+
+int RemoveAllService(WPARAM wParam,LPARAM lParam);
+void SetProtocolsOffline();
+void RemoveUsers();
+void RemoveSettings();
+void ExecuteServices();
+void RemoveDirectories();
+void DisablePlugins();
+
+// Ini access functions
+BOOL GetSettingBool(const char *section, const char *key, BOOL defaultValue);
+BOOL GetSettings(const char *section, char *buffer, size_t bufferSize);
+
+
+// Utils
+void DeleteFileOrFolder(const char *name);
+void DeleteSetting(const char *setting);
+void DeleteSettingEx(const char *szModule, const char *szSetting);
+BOOL isMetaContact(HANDLE hContact);
+
+
+
+BOOL WINAPI DllMain(HINSTANCE hinstDLL,DWORD fdwReason,LPVOID lpvReserved)
+{
+ hInst=hinstDLL;
+ return TRUE;
+}
+
+
+__declspec(dllexport) PLUGININFO* MirandaPluginInfo(DWORD mirandaVersion)
+{
+ // Are we running under Unicode Windows version ?
+ if ((GetVersion() & 0x80000000) == 0)
+ pluginInfo.flags = 1; // UNICODE_AWARE
+
+ pluginInfo.cbSize = sizeof(PLUGININFO);
+ return (PLUGININFO*) &pluginInfo;
+}
+
+
+__declspec(dllexport) PLUGININFOEX* MirandaPluginInfoEx(DWORD mirandaVersion)
+{
+ // Are we running under Unicode Windows version ?
+ if ((GetVersion() & 0x80000000) == 0)
+ pluginInfo.flags = 1; // UNICODE_AWARE
+
+ pluginInfo.cbSize = sizeof(PLUGININFOEX);
+ return &pluginInfo;
+}
+
+
+static const MUUID interfaces[] = { MIID_REMOVEPERSONALSETTINGS, MIID_LAST };
+__declspec(dllexport) const MUUID* MirandaPluginInterfaces(void)
+{
+ return interfaces;
+}
+
+
+int __declspec(dllexport) Load(PLUGINLINK *link)
+{
+ CLISTMENUITEM mi;
+ char *strTmp;
+
+ pluginLink=link;
+ CreateServiceFunction("RemovePersonalSettings/RemoveAll",RemoveAllService);
+ ZeroMemory(&mi,sizeof(mi));
+ mi.cbSize=sizeof(mi);
+ mi.position=-0x7FFFFFFF;
+ mi.flags=0;
+ mi.hIcon=LoadSkinnedIcon(SKINICON_OTHER_MIRANDA);
+ mi.pszName="Remove Personal Settings...";
+ mi.pszService="RemovePersonalSettings/RemoveAll";
+ CallService(MS_CLIST_ADDMAINMENUITEM,0,(LPARAM)&mi);
+
+ // Get ini file name
+ gMirandaDir[0] = '\0';
+ GetModuleFileName(GetModuleHandle(NULL),gMirandaDir,sizeof(gMirandaDir));
+
+ // Remove last name
+ strTmp = strrchr(gMirandaDir,'\\');
+ if(strTmp != NULL)
+ *strTmp = '\0';
+
+ // Set vars
+ strcat(gMirandaDir, "\\");
+ strcpy(gIniFile, gMirandaDir);
+
+ // Store last pos
+ strTmp = &gIniFile[strlen(gIniFile)];
+
+ // Lets try fist name
+ strcpy(strTmp, INI_FILE_NAME);
+
+ if (_access(gIniFile, 4) != 0)
+ {
+ // Not found, lets try the other aproach
+ strcpy(strTmp, "plugins\\" INI_FILE_NAME);
+
+ if (_access(gIniFile, 4) != 0)
+ {
+ // Not found :(
+ gIniFile[0] = '\0';
+ }
+ }
+
+ return 0;
+}
+
+
+int __declspec(dllexport) Unload(void)
+{
+ return 0;
+}
+
+
+int RemoveAllService(WPARAM wParam,LPARAM lParam)
+{
+ if (gIniFile[0] == '\0')
+ {
+ MessageBox(NULL, Translate("Configuration file could not be found!"), Translate("Remove Personal Settings"), MB_OK | MB_ICONERROR);
+ return -1;
+ }
+
+ if (MessageBox(NULL,Translate("All your personal settings will be erased!\n"
+ "Make sure you are running this from a copy of your profile (and not over the original one).\n"
+ "Running this will erase files/folders under Miranda main folder.\n"
+ "\n"
+ "Are you sure you want to remove all your personal settings?\n"
+ "\n"
+ "(You cannot say that I don't told you about the risks :P )"), Translate("Remove Personal Settings"),MB_YESNO)
+ == IDYES)
+ {
+ SetProtocolsOffline();
+ RemoveUsers();
+ RemoveSettings();
+ ExecuteServices();
+ RemoveDirectories();
+ DisablePlugins();
+
+ MessageBox(NULL,Translate("Settings are deleted now."), Translate("Remove Personal Settings"),MB_OK | MB_ICONINFORMATION);
+ }
+
+ return 0;
+}
+
+void SetProtocolsOffline()
+{
+ if ( GetSettingBool("GlobalSettings", "SetProtocolsOffline", TRUE) )
+ {
+ PROTOCOLDESCRIPTOR **protos;
+ int i,count;
+
+ CallService(MS_PROTO_ENUMPROTOCOLS, (WPARAM)&count, (LPARAM)&protos);
+
+ for (i = 0; i < count; i++)
+ {
+ if (protos[i]->type != PROTOTYPE_PROTOCOL)
+ continue;
+
+ if (protos[i]->szName == NULL || protos[i]->szName[0] == '\0')
+ continue;
+
+ CallProtoService(protos[i]->szName, PS_SETSTATUS, ID_STATUS_OFFLINE, 0);
+ }
+
+ // Give some time to make it really offline
+ Sleep(2000);
+ }
+}
+
+void RemoveUsers()
+{
+ if ( GetSettingBool("GlobalSettings", "RemoveAllUsers", TRUE) )
+ {
+ HANDLE hContact;
+ HANDLE hDbEvent;
+ HANDLE hContactOld;
+
+ // To be faster, remove first all metacontacts (because it syncs histories)
+ hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDFIRST, 0, 0);
+ while(hContact != NULL)
+ {
+ hContactOld = hContact;
+ hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDNEXT, (WPARAM) hContact, 0);
+
+ if ( isMetaContact(hContactOld) )
+ CallService(MS_DB_CONTACT_DELETE, (WPARAM) hContactOld, 0);
+ }
+
+ // Now delete all left-overs
+ hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDFIRST, 0, 0);
+
+ while(hContact != NULL)
+ {
+ CallService(MS_DB_CONTACT_DELETE, (WPARAM) hContact, 0);
+
+ hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDFIRST, 0, 0);
+ }
+
+ // Delete events for contacts not in list
+ hDbEvent = (HANDLE) CallService(MS_DB_EVENT_FINDFIRST, 0, 0);
+
+ while(hDbEvent != NULL)
+ {
+ int ret = CallService(MS_DB_EVENT_DELETE, 0, (WPARAM) hDbEvent);
+
+ hDbEvent = (HANDLE) CallService(MS_DB_EVENT_FINDFIRST, 0, 0);
+ }
+
+ // Now delete groups
+ DeleteSettingEx("CListGroups", NULL);
+ }
+}
+
+void RemoveProtocolSettings(const char * protocolName)
+{
+ char buffer[10000];
+
+ // Remove protocol module settings
+ if ( GetSettingBool("GlobalSettings", "RemoveWholeProtocolModule", FALSE) )
+ {
+ DeleteSettingEx(protocolName, NULL);
+ }
+ else if ( GetSettings("ProtocolSettings", buffer, sizeof(buffer)) )
+ {
+ char *name;
+ char *value;
+
+ name = buffer;
+ while(name[0] != '\0')
+ {
+ value = strchr(name, '=');
+ if (value == NULL)
+ value = &name[strlen(name)];
+
+ // Has " ?
+ if (*name == '"' && *(value-1) == '"')
+ {
+ name++;
+ *(value-1) = '\0';
+ }
+
+ // Disable it
+ if (name[0] != '\0')
+ DeleteSettingEx(protocolName, name);
+
+ // Get next one
+ name = value + strlen(value) + 1;
+ }
+ }
+
+ // Remove modules by protocol sufixes
+ if ( GetSettings("ProtocolModuleSufixes", buffer, sizeof(buffer)) )
+ {
+ char *name;
+ char *value;
+ char moduleName[256];
+
+ name = buffer;
+ while(name[0] != '\0')
+ {
+ value = strchr(name, '=');
+ if (value == NULL)
+ value = &name[strlen(name)];
+
+ // Has " ?
+ if (*name == '"' && *(value-1) == '"')
+ {
+ name++;
+ *(value-1) = '\0';
+ }
+
+ // Delete it
+ if (name[0] != '\0')
+ {
+ mir_snprintf(moduleName, sizeof(moduleName), "%s%s", protocolName, name);
+ DeleteSettingEx(moduleName, NULL);
+ }
+
+ // Get next one
+ name = value + strlen(value) + 1;
+ }
+ }
+}
+
+void RemoveSettings()
+{
+ char buffer[10000];
+
+ // Delete protocol settings
+ if ( GetSettingBool("GlobalSettings", "RemoveProtocolSettings", TRUE) )
+ {
+ PROTOCOLDESCRIPTOR **protos;
+ int i,count;
+
+ // TODO MS_PROTO_ENUMACCOUNTS
+ CallService(MS_PROTO_ENUMPROTOCOLS, (WPARAM)&count, (LPARAM)&protos);
+
+ for (i = 0; i < count; i++)
+ {
+ if (protos[i]->type != PROTOTYPE_PROTOCOL)
+ continue;
+
+ if (protos[i]->szName == NULL || protos[i]->szName[0] == '\0')
+ continue;
+
+ RemoveProtocolSettings(protos[i]->szName);
+ }
+
+ // Get disabled protocols
+ if ( GetSettings("DisabledProtocols", buffer, sizeof(buffer)) )
+ {
+ char *name;
+ char *value;
+
+ name = buffer;
+ while(name[0] != '\0')
+ {
+ value = strchr(name, '=');
+ if (value == NULL)
+ value = &name[strlen(name)];
+
+ // Has " ?
+ if (*name == '"' && *(value-1) == '"')
+ {
+ name++;
+ *(value-1) = '\0';
+ }
+
+ // Disable it
+ if (name[0] != '\0')
+ RemoveProtocolSettings(name);
+
+ // Get next one
+ name = value + strlen(value) + 1;
+ }
+ }
+ }
+
+
+ // Delete other settings
+ if ( GetSettings("RemoveSettings", buffer, sizeof(buffer)) )
+ {
+ char *name;
+ char *value;
+
+ name = buffer;
+ while(name[0] != '\0')
+ {
+ value = strchr(name, '=');
+ if (value == NULL)
+ value = &name[strlen(name)];
+
+ // Has " ?
+ if (*name == '"' && *(value-1) == '"')
+ {
+ name++;
+ *(value-1) = '\0';
+ }
+
+ // Delete it
+ if (name[0] != '\0')
+ DeleteSetting(name);
+
+ // Get next one
+ name = value + strlen(value) + 1;
+ }
+ }
+}
+
+void ExecuteServices()
+{
+ char buffer[10000];
+
+ if ( GetSettings("ExecuteServices", buffer, sizeof(buffer)) )
+ {
+ char *name;
+ char *value;
+
+ name = buffer;
+ while(name[0] != '\0')
+ {
+ value = strchr(name, '=');
+ if (value == NULL)
+ value = &name[strlen(name)];
+
+ // Has " ?
+ if (*name == '"' && *(value-1) == '"')
+ {
+ name++;
+ *(value-1) = '\0';
+ }
+
+ // Disable it
+ if (name[0] != '\0')
+ if (ServiceExists(name))
+ CallService(name,0,0);
+
+ // Get next one
+ name = value + strlen(value) + 1;
+ }
+ }
+}
+
+void RemoveDirectories()
+{
+ char buffer[10000];
+ char dir[MAX_PATH];
+
+ // Remove protocol folders
+ if (GetSettingBool("GlobalSettings", "RemoveProtocolFolders", TRUE))
+ {
+ PROTOCOLDESCRIPTOR **protos;
+ int i,count;
+
+ CallService(MS_PROTO_ENUMPROTOCOLS, (WPARAM)&count, (LPARAM)&protos);
+
+ for (i = 0; i < count; i++)
+ {
+ if (protos[i]->type != PROTOTYPE_PROTOCOL)
+ continue;
+
+ if (protos[i]->szName == NULL || protos[i]->szName[0] == '\0')
+ continue;
+
+ mir_snprintf(dir, sizeof(dir), "%s%s", gMirandaDir, protos[i]->szName);
+ DeleteFileOrFolder(dir);
+ }
+ }
+
+ // Remove other folders
+ if ( GetSettings("RemoveFilesOrFolders", buffer, sizeof(buffer)) )
+ {
+ char *name;
+ char *value;
+
+ name = buffer;
+ while(name[0] != '\0')
+ {
+ value = strchr(name, '=');
+ if (value == NULL)
+ value = &name[strlen(name)];
+
+ // Has " ?
+ if (*name == '"' && *(value-1) == '"')
+ {
+ name++;
+ *(value-1) = '\0';
+ }
+
+ // Delete it
+ if (name[0] != '\0')
+ {
+ mir_snprintf(dir, sizeof(dir), "%s%s", gMirandaDir, name);
+ DeleteFileOrFolder(dir);
+ }
+
+ // Get next one
+ name = value + strlen(value) + 1;
+ }
+ }
+}
+
+void DisablePlugins()
+{
+ char buffer[10000];
+
+ if ( GetSettings("DisablePlugins", buffer, sizeof(buffer)) )
+ {
+ char *name;
+ char *value;
+
+ name = buffer;
+ while(name[0] != '\0')
+ {
+ value = strchr(name, '=');
+ if (value == NULL)
+ value = &name[strlen(name)];
+
+ // Has " ?
+ if (*name == '"' && *(value-1) == '"')
+ {
+ name++;
+ *(value-1) = '\0';
+ }
+
+ // Disable it
+ if (name[0] != '\0')
+ {
+ CharLower(name);
+ if (DBGetContactSettingByte(NULL, PLUGINDISABLELIST, name, 0) != 1)
+ {
+ DBWriteContactSettingByte(NULL, PLUGINDISABLELIST, name, 1);
+ }
+ }
+
+ // Get next one
+ name = value + strlen(value) + 1;
+ }
+ }
+}
+
+
+
+// Ini access functions
+
+BOOL GetSettingBool(const char *section, const char *key, BOOL defaultValue)
+{
+ char tmp[16];
+
+ if ( GetPrivateProfileString(section, key, defaultValue ? "true" : "false", tmp, sizeof(tmp), gIniFile) == 0 )
+ {
+ return defaultValue;
+ }
+ else
+ {
+ return stricmp(tmp, "true") == 0;
+ }
+}
+
+
+BOOL GetSettings(const char *section, char *buffer, size_t bufferSize)
+{
+ buffer[0] = '\0\0';
+
+ return GetPrivateProfileSection(section, buffer, bufferSize, gIniFile) != 0;
+}
+
+
+
+// Utils
+
+void DeleteFileOrFolder(const char *name)
+{
+ int attibs = GetFileAttributes(name);
+
+ if (attibs == 0xFFFFFFFF) // Not exists
+ {
+ // Try to find it
+ WIN32_FIND_DATA findData;
+ HANDLE hwnd;
+ char tmp[MAX_PATH];
+ char *strTmp;
+
+ // Delete files
+ hwnd = FindFirstFile(name, &findData);
+ if (hwnd != INVALID_HANDLE_VALUE)
+ {
+ strcpy(tmp, name);
+ strTmp = strrchr(tmp,'\\');
+ if(strTmp != NULL)
+ {
+ strTmp++;
+ *strTmp = '\0';
+ }
+ else
+ {
+ strcat(tmp, "\\");
+ strTmp = &tmp[strlen(tmp)];
+ }
+
+ do
+ {
+ if (strcmp(findData.cFileName, ".") && strcmp(findData.cFileName, ".."))
+ {
+ strcpy(strTmp, findData.cFileName);
+ DeleteFileOrFolder(tmp);
+ }
+ }
+ while(FindNextFile(hwnd, &findData) != 0);
+
+ FindClose(hwnd);
+ }
+ }
+ else if (attibs & FILE_ATTRIBUTE_DIRECTORY) // Is a directory
+ {
+ // Get all files and delete then
+ WIN32_FIND_DATA findData;
+ HANDLE hwnd;
+ char tmp[MAX_PATH];
+
+ mir_snprintf(tmp, sizeof(tmp), "%s\\*.*", name);
+
+ // Delete files
+ hwnd = FindFirstFile(tmp, &findData);
+ if (hwnd != INVALID_HANDLE_VALUE)
+ {
+ do
+ {
+ if (strcmp(findData.cFileName, ".") && strcmp(findData.cFileName, ".."))
+ {
+ mir_snprintf(tmp, sizeof(tmp), "%s\\%s", name, findData.cFileName);
+ DeleteFileOrFolder(tmp);
+ }
+ }
+ while(FindNextFile(hwnd, &findData) != 0);
+
+ FindClose(hwnd);
+ }
+
+ // Delete directory
+ RemoveDirectory(name);
+ }
+ else // Is a File
+ {
+ SetFileAttributes(name, FILE_ATTRIBUTE_ARCHIVE);
+ DeleteFile(name);
+ }
+}
+
+
+BOOL isMetaContact(HANDLE hContact)
+{
+ DBVARIANT dbv;
+ DBCONTACTGETSETTING dbcgs;
+ char name[32];
+
+ dbv.type=DBVT_ASCIIZ;
+ dbv.pszVal=name;
+ dbv.cchVal=sizeof(name);
+ dbcgs.pValue=&dbv;
+ dbcgs.szModule="Protocol";
+ dbcgs.szSetting="p";
+
+ if(CallService(MS_DB_CONTACT_GETSETTINGSTATIC,(WPARAM)hContact,(LPARAM)&dbcgs))
+ return FALSE;
+
+ return strcmp(dbcgs.pValue->pszVal, METACONTACTS_PROTOCOL_NAME) == 0;
+}
+
+
+
+typedef struct {
+ char buffer[10000];
+ int pos;
+ const char *filter;
+ size_t lenFilterMinusOne;
+} DeleteModuleStruct;
+
+
+int EnumProc(const char *szName, LPARAM lParam)
+{
+ DeleteModuleStruct *dms = (DeleteModuleStruct *) lParam;
+ size_t len = strlen(szName);
+
+ if (dms->filter != NULL && dms->lenFilterMinusOne > 0)
+ {
+ if (len >= dms->lenFilterMinusOne)
+ {
+ if (dms->filter[0] == '*')
+ {
+ if (strcmp(&dms->filter[1], &szName[len - dms->lenFilterMinusOne]) != 0)
+ return 0;
+ }
+ else // if (dms->filter[dms->lenFilterMinusOne] == '*')
+ {
+ if (strncmp(dms->filter, szName, dms->lenFilterMinusOne) != 0)
+ return 0;
+ }
+ }
+ }
+
+ // Add to the struct
+ if (len > 0 && len < sizeof(dms->buffer) - dms->pos - 2)
+ {
+ strcpy(&dms->buffer[dms->pos], szName);
+ dms->pos += len + 1;
+ }
+
+ return 0;
+}
+
+int ModuleEnumProc(const char *szName, DWORD ofsModuleName, LPARAM lParam)
+{
+ return EnumProc(szName, lParam);
+}
+
+void DeleteSettingEx(const char *szModule, const char *szSetting)
+{
+ size_t lenModule;
+
+ if (szModule == NULL)
+ return;
+
+ lenModule = strlen(szModule);
+ if (szModule[0] == '*' || szModule[lenModule-1] == '*')
+ {
+ DeleteModuleStruct dms;
+ ZeroMemory(&dms, sizeof(dms));
+
+ dms.filter = szModule;
+ dms.lenFilterMinusOne = lenModule-1;
+
+ CallService(MS_DB_MODULES_ENUM, (WPARAM) &dms, (LPARAM) &ModuleEnumProc);
+
+ // Delete then
+ szModule = dms.buffer;
+ while(szModule[0] != '\0')
+ {
+ DeleteSettingEx(szModule, szSetting);
+
+ // Get next one
+ szModule += strlen(szModule) + 1;
+ }
+ }
+ else
+ {
+ size_t lenSetting = szSetting == NULL ? 0 : strlen(szSetting);
+ if (szSetting == NULL || szSetting[0] == '*' || szSetting[lenSetting-1] == '*')
+ {
+ DeleteModuleStruct dms;
+ DBCONTACTENUMSETTINGS dbces;
+
+ ZeroMemory(&dms, sizeof(dms));
+
+ dms.filter = szSetting;
+ dms.lenFilterMinusOne = lenSetting-1;
+
+ dbces.pfnEnumProc = EnumProc;
+ dbces.lParam = (LPARAM) &dms;
+ dbces.szModule = szModule;
+ dbces.ofsSettings = 0;
+
+ CallService(MS_DB_CONTACT_ENUMSETTINGS, 0, (LPARAM) &dbces);
+
+ // Delete then
+ szSetting = dms.buffer;
+ while(szSetting[0] != '\0')
+ {
+ DBDeleteContactSetting(NULL, szModule, szSetting);
+
+ // Get next one
+ szSetting += strlen(szSetting) + 1;
+ }
+ }
+ else
+ {
+ DBDeleteContactSetting(NULL, szModule, szSetting);
+ }
+ }
+}
+
+void DeleteSetting(const char *setting)
+{
+ char *szModule;
+ char *szSetting;
+
+ if (setting == NULL || setting[0] == '\0')
+ {
+ return;
+ }
+
+ // Split setting
+ szModule = strdup(setting);
+ szSetting = strrchr(szModule, '/');
+ if (szSetting != NULL)
+ {
+ *szSetting = '\0';
+ szSetting ++;
+ }
+
+ DeleteSettingEx(szModule, szSetting);
+
+ free(szModule);
+}
+
diff --git a/Plugins/rps/rps.dsp b/Plugins/rps/rps.dsp
new file mode 100644
index 0000000..d529f54
--- /dev/null
+++ b/Plugins/rps/rps.dsp
@@ -0,0 +1,127 @@
+# Microsoft Developer Studio Project File - Name="rps" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=rps - Win32 Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "rps.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "rps.mak" CFG="rps - Win32 Debug"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "rps - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "rps - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName "rps"
+# PROP Scc_LocalPath "."
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "rps - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "RPS_EXPORTS" /YX /FD /c
+# ADD CPP /nologo /MT /W3 /GX /O2 /I "../../include" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "RPS_EXPORTS" /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x809 /d "NDEBUG"
+# ADD RSC /l 0x809 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 /out:"../../bin/release/plugins/RemovePersonalSettings.dll"
+
+!ELSEIF "$(CFG)" == "rps - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "RPS_EXPORTS" /YX /FD /GZ /c
+# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "../../include" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "RPS_EXPORTS" /FR /YX /FD /GZ /c
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x809 /d "_DEBUG"
+# ADD RSC /l 0x809 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /out:"../../bin/debug/plugins/RemovePersonalSettings.dll" /pdbtype:sept
+
+!ENDIF
+
+# Begin Target
+
+# Name "rps - Win32 Release"
+# Name "rps - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=.\rps.c
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
+# End Group
+# Begin Group "Docs"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=.\RemovePersonalSettings.ini
+# End Source File
+# Begin Source File
+
+SOURCE=.\Docs\rps_changelog.txt
+# End Source File
+# Begin Source File
+
+SOURCE=.\Docs\rps_readme.txt
+# End Source File
+# Begin Source File
+
+SOURCE=.\Docs\rps_version.txt
+# End Source File
+# End Group
+# End Target
+# End Project
diff --git a/Plugins/rps/rps.dsw b/Plugins/rps/rps.dsw
new file mode 100644
index 0000000..67773bd
--- /dev/null
+++ b/Plugins/rps/rps.dsw
@@ -0,0 +1,33 @@
+Microsoft Developer Studio Workspace File, Format Version 6.00
+# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
+
+###############################################################################
+
+Project: "rps"=".\rps.dsp" - Package Owner=<4>
+
+Package=<5>
+{{{
+ begin source code control
+ rps
+ .
+ end source code control
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Global:
+
+Package=<5>
+{{{
+}}}
+
+Package=<3>
+{{{
+}}}
+
+###############################################################################
+
diff --git a/Plugins/smcnotify/commonheaders.h b/Plugins/smcnotify/commonheaders.h
new file mode 100644
index 0000000..b3b7de0
--- /dev/null
+++ b/Plugins/smcnotify/commonheaders.h
@@ -0,0 +1,90 @@
+/*
+Status Message Change Notify plugin for Miranda IM.
+
+Copyright © 2004-2005 NoName
+Copyright © 2005-2006 Daniel Vijge, Tomasz S³otwiñski, Ricardo Pescuma Domenecci
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef __SMCNOTIFY_COMMONHEADERS_H
+#define __SMCNOTIFY_COMMONHEADERS_H
+
+#include <windows.h>
+#include <tchar.h>
+#include <commctrl.h>
+#include <stdio.h>
+#include <time.h>
+
+#define MIRANDA_VER 0x0670
+
+//Miranda headers
+#include "newpluginapi.h"
+#include "m_system.h"
+#include "m_protocols.h"
+#include "m_protosvc.h"
+#include "m_clist.h"
+#include "m_ignore.h"
+#include "m_contacts.h"
+#include "m_message.h"
+#include "m_userinfo.h"
+#include "m_skin.h"
+#include "m_icolib.h"
+#include "m_langpack.h"
+#include "m_history.h"
+#include "m_database.h"
+#include "m_options.h"
+#include "m_utils.h"
+#include "m_button.h"
+#include "m_clc.h"
+#include "m_popup.h"
+#include "statusmodes.h"
+
+#include "m_toptoolbar.h"
+#include "m_updater.h"
+
+#include "../utils/mir_memory.h"
+#include "../utils/mir_options.h"
+
+#include "resource.h"
+#include "m_smcnotify.h"
+#include "options.h"
+#include "smc.h"
+#include "menu.h"
+#include "popup.h"
+
+
+#define MODULE_NAME "SMCNotify"
+#define PLUGIN_NAME "Status Message Change Notify"
+
+
+// Global Variables
+HINSTANCE hInst;
+PLUGINLINK *pluginLink;
+
+void ShowList(void);
+HWND hListDlg;
+
+TCHAR* GetStr(STATUSMSGINFO *n, const TCHAR *tmplt);
+char* BuildSetting(WORD index, char *suffix);
+BOOL FreeSmiStr(STATUSMSGINFO *smi);
+WCHAR *mir_dupToUnicodeEx(char *ptr, UINT CodePage);
+TCHAR* MyDBGetContactSettingTString_dup(HANDLE hContact, const char *szModule, const char *szSetting, TCHAR *out);
+
+//#define CUSTOMBUILD_CATCHICQSTATUSMSG
+//#define CUSTOMBUILD_OSDSUPPORT
+//#define CUSTOMBUILD_COLORHISTORY
+
+#endif // __SMCNOTIFY_COMMONHEADERS_H
diff --git a/Plugins/smcnotify/docs/smcnotify_license.txt b/Plugins/smcnotify/docs/smcnotify_license.txt
new file mode 100644
index 0000000..7a8e8ab
--- /dev/null
+++ b/Plugins/smcnotify/docs/smcnotify_license.txt
@@ -0,0 +1,340 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/Plugins/smcnotify/docs/smcnotify_readme.txt b/Plugins/smcnotify/docs/smcnotify_readme.txt
new file mode 100644
index 0000000..8adf1ee
--- /dev/null
+++ b/Plugins/smcnotify/docs/smcnotify_readme.txt
@@ -0,0 +1,177 @@
+Status Message Change Notify plugin for Miranda IM
+==================================================
+
+This plugin checks if any contact on your list changes his/her status message. Then it can
+perform some action:
+- notify you about the change via popup (PopUp plugin required),
+- store old status message to keep history of all the changes ( accesable through User
+ Details dialog),
+- if the message window is open show the change in there by adding an event to database,
+- log changes to a file.
+
+You can customize the text of every notification using some variables (see below).
+
+Other features:
+- a customizable window listing all the contacts that have status message set,
+- an item in contact menu 'Go To URL in Status Message' if the status message contains
+ a link,
+- Unicode support,
+- Updater support,
+- Translation support,
+
+Variables
+=========
+You can use those with any customizable template in SMCNotify options.
+Note that variables are case sensitive.
+
+ %n New status message
+ %o Old status message
+ %c Custom nickname
+ \n line break
+ \t tab stop
+
+ %Y Year
+ %M Month
+ %D Day
+ %H Hour (in 24h format)
+ %h Hour (in 12h format)
+ %a AM/PM
+ %m Minutes
+ %s Seconds
+
+List Contacts with Status Message
+=================================
+You can change the color of the text and background and choose an image to be dsplayed in
+the back of the list. Columns are sortable.
+The list doesn't refresh. You have to close it and open again, sorry.
+The first column (Protocol) is sortable in 4 ways:
+- contacts are sorted by status and then grouped by protocol they use,
+- reversed order of the previous one,
+- contacts are sorted by status only,
+- reversed order of the previous one.
+
+Upgrading from version 0.0.3.5 or below
+=======================================
+Because the code was rewritten and all the setting names in db were changed you have to
+configure the plugin from the begining. If you feel comfortable with using DBEditor plugin
+there are some ways to reuse old data:
+
+[Setting/StatusMsgChangeNotify]
+This module is not used any more. You can note down some setting (like colors, templates)
+and use them while configuring pluging through options page. Than delate this module.
+
+Reuse old per-contact settings
+Choose 'Actions->Search and Replace' from the top menu; in the dialogbox do the following:
+Serch For/Text: put 'smcn'; check only those options: Case Sensitive, Exact Match,
+Setting Name; Replace With/Text: put 'SMCNotify'; hit Replace.
+
+Reuse old per-contact status message history
+Choose 'Actions->Search and Replace' from the top menu; in the dialogbox do the following:
+Serch For/Text: put 'StatusMsgChangeNotify'; check only those options: Case Sensitive,
+Exact Match, Module Name; Replace With/Text: put 'SMCNotify'; hit Replace.
+
+Hidden settings
+===============
+[Settings/SMCNotify/IgnoreAfterStatusChange]
+BYTE set it to 1 if you're using NesStatusMessage plugin with option
+'Read status message' checked. This eliminates some double popups for the same contact.
+Note that this doesn't always work because the notifications come in random order.
+
+[Settings/SMCNotify/IgnoreTlenAway]
+BYTE oroginal Tlen client adds timestamp in the format '[hh:mm DD.MM]' when going Away.
+If you set this key to 1 it will ignore those changes.
+
+[<contact>/SMCNotify/HistoryMax]
+DWORD set per-contact max number of status messages it should keep in history.
+
+Development
+===========
+The project was started by daniel who simply changed some code of NickChangeNotify plugin
+by noname. The it was carried on by slotwin with a lot of help from pescuma.
+
+Source code
+===========
+If the source code wasn't published with the plugin you can find it in SVN repository on
+mgoodies project page:
+http://mgoodies.berlios.de
+
+Copyright and license
+=====================
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+History
+=======
+Version 0.0.3.17 beta
+
+[+] You can use %c with log filename to append nicknames
+[+] New option: Log in Ascii
+
+Version 0.0.3.15 beta
+
+[*] Code rewritten almost from scratch
+[+] Unicode support
+[+] Updater support
+[*] Options rearrange
+
+[I've never kept track of the changes and new features I inplement, sorry.]
+
+Version 0.0.1.7
+
+[+] OSD plugin support
+[+] contact item menu to quick disable/enable PopUps for this contact
+[*] better parsing of URL in status message, it can be surrounded with text
+ i.e. "blah blah www.myhomepage.com blah"
+
+
+Version 0.0.1.6 mod by slotwin
+
+[*] hell lot of changes, here are major ones:
+[+] 2 option windows - in 'PopUps' and 'Status' group
+[+] main menu item and toptoolbar button to list all contacts with status messages
+ in a new window:
+ - customizable (text colour, background colour or image)
+ - you can sort results by nick or proto/status
+ - double-left click to open message window
+ - right click for contact menu
+ - store size, columns width and last sorting after closing
+[+] "Go To URL in Status Message" contact menu item
+
+
+Version 0.0.1.1 mod by slotwin
+
+[+] empty status message displayed as "<empty>" (it's translatable)
+[!] minor options window glitches
+
+Version 0.0.1.0 mod by slotwin
+
+[+] new option: Show PopUps when I connect
+[+] new option: Show status message changes in message window (thanks TheLeech)
+[+] new variable: \t tab stop
+[*] some fixes with formatting log and PopUp text, multiline status messages
+[*] new translatable strings
+
+
+Version 0.0.0.2-0.0.0.4 mod by slotwin
+
+[+] GG (Gadu-Gadu) and Yahoo support
+[+] new variable: \n line break
+[*] no PopUps when disconnecting
+[!] bugfix: wrong data read when contact was clearing his/her status message
+[!] bugfix: sound when PopUps disabled
+
+
+Version 0.0.2.7 2005/05/28
+
+ - First version, completely bases on NickChangeNotify 0.0.2.7
diff --git a/Plugins/smcnotify/docs/smcnotify_translation.txt b/Plugins/smcnotify/docs/smcnotify_translation.txt
new file mode 100644
index 0000000..81b384e
--- /dev/null
+++ b/Plugins/smcnotify/docs/smcnotify_translation.txt
@@ -0,0 +1,113 @@
+; plugin name
+[Status Message Change Notify]
+
+; default templates
+[changed his/her status message to %n]
+[removes his/her status message]
+[changes his/her status message to %n]
+[<no status message>]
+
+; main menu items
+[PopUps]
+[Enable status message change notification]
+[Disable status message change notification]
+[List Contacts with Status Message]
+[Go To URL in Status Message]
+
+; skin sound option
+[Status Notify]
+[User has changed status message]
+
+; popup preview
+[Contact]
+[Old status message]
+[New status message]
+
+; IcoLib icons descriptions
+[PopUps Enabled]
+[PopUps Disabled]
+[List Contacts]
+[Go To URL]
+[Status Message History]
+[Log To File]
+
+; contacts with status message list
+[Protocol]
+[Nick]
+[Status Message]
+
+; status message history
+[Status Message History]
+[Clear history]
+; tooltips
+[Enable/Disable popups for this contact]
+[Enable/Disable history for this contact]
+[Enable/Disable logging to file for this contact]
+
+; options
+[Popups]
+[History]
+[General]
+[Advanced]
+[Ignore]
+
+; options page - popups
+[General]
+[Enable PopUps]
+[Show PopUps when I connect]
+[Only if status message has changed]
+[Ignore empty status messages]
+[Use OSD Plugin instead]
+[Colours]
+[From PopUp plugin]
+[Use Windows colours]
+[Custom]
+[Background]
+[Text]
+[Delay]
+[Permanent]
+[Actions]
+[Left click]
+[Right click]
+[Preview]
+
+; options page - general
+[Do not log empty status messages]
+[List]
+[Refresh list automatically]
+[Text colour]
+[Background colour]
+[Use background image]
+[Protocols]
+[Disable protocols which you don't wish to be notified for]
+
+; options page - advanced
+[History]
+[Keep history of up to]
+[status messages]
+[History template]
+[Clear status messages history for all contacts]
+[Message Window]
+[Show status message changes in message window]
+[Message removed]
+[Message changed]
+[Logging]
+[Log to]
+[Log in Ascii]
+;[Use %c in filename if you want to append Contact Custom Nickname]
+[You can use %c with log filename to append nicknames]
+[Log template]
+[Variables]
+
+; options page - ignore
+[Show PopUps]
+[Keep History]
+[Log To File]
+
+; options page - mouse click action select menu
+[Do nothing]
+[Close popup]
+[Open message window]
+[Open contact menu]
+[Open contact details]
+[View status message history]
diff --git a/Plugins/smcnotify/docs/version.txt b/Plugins/smcnotify/docs/version.txt
new file mode 100644
index 0000000..bfb35c7
--- /dev/null
+++ b/Plugins/smcnotify/docs/version.txt
@@ -0,0 +1 @@
+Status Message Change Notify 0.0.3.17
diff --git a/Plugins/smcnotify/history.c b/Plugins/smcnotify/history.c
new file mode 100644
index 0000000..66e19e0
--- /dev/null
+++ b/Plugins/smcnotify/history.c
@@ -0,0 +1,233 @@
+/*
+Status Message Change Notify plugin for Miranda IM.
+
+Copyright © 2004-2005 NoName
+Copyright © 2005-2006 Daniel Vijge, Tomasz S³otwiñski, Ricardo Pescuma Domenecci
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "commonheaders.h"
+
+
+static void LoadHistory(HANDLE hContact, HWND hwnd, int nList) {
+ WORD historyFirst, historyLast, historyMax;
+ short i;
+ //size_t size;
+ TCHAR *str, *tempstr;
+ DBVARIANT dbv;
+ STATUSMSGINFO smi;
+
+ historyMax = DBGetContactSettingWord(hContact, MODULE_NAME, "HistoryMax", opts.dHistoryMax);
+ if (historyMax <= 0)
+ return;
+ else if (historyMax > 99)
+ historyMax = 99;
+
+ historyFirst = DBGetContactSettingWord(hContact, MODULE_NAME, "HistoryFirst",0);
+ if (historyFirst >= historyMax)
+ historyFirst = 0;
+ historyLast = DBGetContactSettingWord(hContact, MODULE_NAME, "HistoryLast",0);
+ if (historyLast >= historyMax)
+ historyLast = historyMax - 1;
+
+ ZeroMemory(&smi, sizeof(smi));
+
+ //str = (TCHAR*)mir_alloc(3 * sizeof(TCHAR));
+ str = (TCHAR*)mir_alloc(historyMax * 1024 * sizeof(TCHAR));
+ str[0] = _T('\0');
+ tempstr = NULL;
+
+ i = historyLast;
+ while (i != historyFirst)
+ {
+ i = (i - 1 + historyMax) % historyMax;
+
+ if (!DBGetContactSettingTString(hContact, MODULE_NAME, BuildSetting(i, NULL), &dbv))
+ {
+#ifdef UNICODE
+ if (dbv.type == DBVT_ASCIIZ)
+ {
+ smi.newstatusmsg = mir_dupToUnicodeEx(dbv.pszVal, CP_ACP);
+ }
+ else if (dbv.type == DBVT_UTF8)
+ {
+ smi.newstatusmsg = mir_dupToUnicodeEx(dbv.pszVal, CP_UTF8);
+ }
+ else if (dbv.type == DBVT_WCHAR)
+ {
+ smi.newstatusmsg = dbv.pwszVal;
+ }
+#else
+ if (dbv.type == DBVT_ASCIIZ)
+ {
+ smi.newstatusmsg = dbv.pszVal;
+ }
+#endif
+ else
+ {
+ smi.newstatusmsg = NULL;
+ }
+
+ smi.dTimeStamp = DBGetContactSettingDword(hContact, MODULE_NAME, BuildSetting(i, "_ts"), 0);
+ tempstr = GetStr(&smi, opts.history);
+ mir_free(smi.newstatusmsg);
+ DBFreeVariant(&dbv);
+ }
+
+ if ((tempstr != NULL) && (tempstr[0] != _T('\0')))
+ {
+ //size = (lstrlen(str) + lstrlen(tempstr) + 2) * sizeof(TCHAR);
+ //str = (TCHAR*)mir_realloc(str, size);
+ lstrcat(str, tempstr);
+ lstrcat(str, _T("\r\n"));
+ }
+ mir_free(tempstr);
+ }
+ SetDlgItemText(hwnd, nList, str);
+
+ mir_free(str);
+ return;
+}
+
+static void ClearHistory(HANDLE hContact) {
+ int i;
+ BOOL bSettingExists = TRUE;
+ for (i = 0; /*i < (int)options.dHistMax && */bSettingExists; i++)
+ {
+ bSettingExists = !DBDeleteContactSetting(hContact, MODULE_NAME, BuildSetting(i, NULL));
+ DBDeleteContactSetting(hContact, MODULE_NAME, BuildSetting(i, "_ts"));
+ }
+ DBDeleteContactSetting(hContact, MODULE_NAME, "HistoryFirst");
+ DBDeleteContactSetting(hContact, MODULE_NAME, "HistoryLast");
+}
+
+void ClearAllHistory() {
+ HANDLE hContact;
+ hContact = (HANDLE)CallService(MS_DB_CONTACT_FINDFIRST, 0, 0);
+ while (hContact)
+ {
+ ClearHistory(hContact);
+ hContact = (HANDLE)CallService(MS_DB_CONTACT_FINDNEXT, (WPARAM)hContact, 0);
+ }
+}
+
+static BOOL CALLBACK HistoryDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) {
+ switch(msg)
+ {
+ case WM_INITDIALOG:
+ {
+ DWORD ignore;
+ SetWindowLong(hwndDlg, GWL_USERDATA, lParam);
+
+ TranslateDialogDefault(hwndDlg);
+
+ // set icons on buttons
+ SendDlgItemMessage(hwndDlg,IDC_CPOPUP,BM_SETIMAGE,IMAGE_ICON, CallService(MS_SKIN2_GETICONBYHANDLE, 0, (LPARAM)ICO_POPUP_E));
+ SendDlgItemMessage(hwndDlg,IDC_CHISTORY,BM_SETIMAGE,IMAGE_ICON, CallService(MS_SKIN2_GETICONBYHANDLE, 0, (LPARAM)ICO_HISTORY));
+ SendDlgItemMessage(hwndDlg,IDC_CLOG,BM_SETIMAGE,IMAGE_ICON, CallService(MS_SKIN2_GETICONBYHANDLE, 0, (LPARAM)ICO_LOG));
+
+ SendMessage(GetDlgItem(hwndDlg,IDC_CPOPUP), BUTTONSETASFLATBTN, 0, 0);
+ SendMessage(GetDlgItem(hwndDlg,IDC_CPOPUP), BUTTONSETASPUSHBTN, 0, 0);
+
+ SendMessage(GetDlgItem(hwndDlg,IDC_CHISTORY), BUTTONSETASFLATBTN, 0, 0);
+ SendMessage(GetDlgItem(hwndDlg,IDC_CHISTORY), BUTTONSETASPUSHBTN, 0, 0);
+
+ SendMessage(GetDlgItem(hwndDlg,IDC_CLOG), BUTTONSETASFLATBTN, 0, 0);
+ SendMessage(GetDlgItem(hwndDlg,IDC_CLOG), BUTTONSETASPUSHBTN, 0, 0);
+
+ ignore = DBGetContactSettingDword((HANDLE)lParam, "Ignore", MODULE_NAME, 0);
+ CheckDlgButton(hwndDlg, IDC_CPOPUP, !(ignore & SMII_POPUP));
+ CheckDlgButton(hwndDlg, IDC_CHISTORY, !(ignore & SMII_HISTORY));
+ CheckDlgButton(hwndDlg, IDC_CLOG, !(ignore & SMII_LOG));
+
+ SendMessage(GetDlgItem(hwndDlg,IDC_CPOPUP), BUTTONADDTOOLTIP, (WPARAM)Translate("Enable/Disable popups for this contact"), 0);
+ SendMessage(GetDlgItem(hwndDlg,IDC_CHISTORY), BUTTONADDTOOLTIP, (WPARAM)Translate("Enable/Disable history for this contact"), 0);
+ SendMessage(GetDlgItem(hwndDlg,IDC_CLOG), BUTTONADDTOOLTIP, (WPARAM)Translate("Enable/Disable logging to file for this contact"), 0);
+
+ //make dialog bigger if UserInfoEx in use [222x132 dlus/340x170 dlus]
+ if (ServiceExists("UserInfo/Reminder/AggrassiveBackup"))
+ {
+ RECT rc, rc0, rcp;
+ rc0.left = 2;rc0.top = 155;rc0.right = 298;rc0.bottom = 148;
+ MapDialogRect(hwndDlg, &rc0);
+ MoveWindow(GetDlgItem(hwndDlg, IDC_HISTORYLIST), rc0.left, 2 * rc0.left, rc0.right, rc0.bottom, TRUE);
+ GetClientRect(GetDlgItem(hwndDlg, IDC_CHISTORYCLEAR), &rc);
+ MoveWindow(GetDlgItem(hwndDlg, IDC_CHISTORYCLEAR), rc0.left, rc0.top, rc.right - rc.left, rc.bottom - rc.top, TRUE);
+ rcp.left = 170;rcp.top = 188;rcp.right = 18;rcp.bottom = 148;MapDialogRect(hwndDlg, &rcp);
+ GetClientRect(GetDlgItem(hwndDlg, IDC_CPOPUP), &rc);
+ MoveWindow(GetDlgItem(hwndDlg, IDC_CPOPUP), rcp.left, rc0.top, rc.right - rc.left, rc.bottom - rc.top, TRUE);
+ GetClientRect(GetDlgItem(hwndDlg, IDC_CHISTORY), &rc);
+ MoveWindow(GetDlgItem(hwndDlg, IDC_CHISTORY), rcp.left + rcp.right, rc0.top, rc.right - rc.left, rc.bottom - rc.top, TRUE);
+ GetClientRect(GetDlgItem(hwndDlg, IDC_CLOG), &rc);
+ MoveWindow(GetDlgItem(hwndDlg, IDC_CLOG), rcp.left + rcp.right + rcp.right, rc0.top, rc.right - rc.left, rc.bottom - rc.top, TRUE);
+ }
+
+ LoadHistory((HANDLE)lParam, hwndDlg, IDC_HISTORYLIST);
+ break;
+ }
+ case WM_COMMAND:
+ switch(LOWORD(wParam))
+ {
+ case IDC_CHISTORYCLEAR:
+ ClearHistory((HANDLE)GetWindowLong(hwndDlg, GWL_USERDATA));
+ SetDlgItemText(hwndDlg, IDC_HISTORYLIST, _T(""));
+ break;
+ case IDC_CPOPUP:
+ case IDC_CLOG:
+ case IDC_CHISTORY:
+ {
+ DWORD ignore = IsDlgButtonChecked(hwndDlg, IDC_CPOPUP)?0:SMII_POPUP;
+ ignore = IsDlgButtonChecked(hwndDlg, IDC_CHISTORY)?ignore:ignore | SMII_HISTORY;
+ ignore = IsDlgButtonChecked(hwndDlg, IDC_CLOG)?ignore:ignore | SMII_LOG;
+ DBWriteContactSettingDword((HANDLE)GetWindowLong(hwndDlg, GWL_USERDATA), "Ignore", MODULE_NAME, ignore);
+ break;
+ }
+ }
+ break;
+#ifdef CUSTOMBUILD_COLORHISTORY
+ case WM_CTLCOLORSTATIC:
+ if ((HWND)lParam == GetDlgItem(hwndDlg, IDC_HISTORYLIST))
+ {
+ //SetBkMode((HDC)wParam, OPAQUE/*TRANSPARENT*/);
+ SetBkColor((HDC)wParam, 0x00000000/*GetSysColor(COLOR_WINDOW)*/);
+ SetTextColor((HDC)wParam, 0x00FFFFFF/*GetSysColor(COLOR_WINDOWTEXT)*/);
+ //return (BOOL)GetSysColorBrush(COLOR_WINDOW);
+ break;
+ }
+#endif
+ }
+
+ return 0;
+}
+
+extern int UserInfoInit(WPARAM wParam, LPARAM lParam) {
+ OPTIONSDIALOGPAGE odp;
+
+ ZeroMemory(&odp, sizeof(odp));
+
+ if ((HANDLE)lParam == NULL) return 0;
+
+ odp.cbSize = sizeof(odp);
+ odp.position = 100000000;
+ odp.hInstance = hInst;
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_HISTORY);
+ odp.ptszTitle = TranslateT("Status Message History");
+ odp.flags = ODPF_TCHAR;
+ odp.pfnDlgProc = HistoryDlgProc;
+ CallService(MS_USERINFO_ADDPAGE, wParam, (LPARAM)&odp);
+
+ return 0;
+}
diff --git a/Plugins/smcnotify/ignore.c b/Plugins/smcnotify/ignore.c
new file mode 100644
index 0000000..50917ea
--- /dev/null
+++ b/Plugins/smcnotify/ignore.c
@@ -0,0 +1,265 @@
+/*
+Status Message Change Notify plugin for Miranda IM.
+
+Copyright © 2004-2005 NoName
+Copyright © 2005-2006 Daniel Vijge, Tomasz S³otwiñski, Ricardo Pescuma Domenecci
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "commonheaders.h"
+
+
+#define SIZEOF(X) (sizeof(X)/sizeof(X[0]))
+
+
+static void SetListGroupIcons(HWND hwndList,HANDLE hFirstItem,HANDLE hParentItem,int *groupChildCount)
+{
+ int typeOfFirst;
+ int iconOn[3]={1,1,1};
+ int childCount[3]={0,0,0},i;
+ int iImage;
+ HANDLE hItem,hChildItem;
+
+ typeOfFirst=SendMessage(hwndList,CLM_GETITEMTYPE,(WPARAM)hFirstItem,0);
+ //check groups
+ if(typeOfFirst==CLCIT_GROUP) hItem=hFirstItem;
+ else hItem=(HANDLE)SendMessage(hwndList,CLM_GETNEXTITEM,CLGN_NEXTGROUP,(LPARAM)hFirstItem);
+ while(hItem) {
+ hChildItem=(HANDLE)SendMessage(hwndList,CLM_GETNEXTITEM,CLGN_CHILD,(LPARAM)hItem);
+ if(hChildItem) SetListGroupIcons(hwndList,hChildItem,hItem,childCount);
+ for( i=0; i < SIZEOF(iconOn); i++)
+ if(iconOn[i] && SendMessage(hwndList,CLM_GETEXTRAIMAGE,(WPARAM)hItem,i)==0) iconOn[i]=0;
+ hItem=(HANDLE)SendMessage(hwndList,CLM_GETNEXTITEM,CLGN_NEXTGROUP,(LPARAM)hItem);
+ }
+ //check contacts
+ if(typeOfFirst==CLCIT_CONTACT) hItem=hFirstItem;
+ else hItem=(HANDLE)SendMessage(hwndList,CLM_GETNEXTITEM,CLGN_NEXTCONTACT,(LPARAM)hFirstItem);
+ while(hItem) {
+ for ( i=0; i < SIZEOF(iconOn); i++) {
+ iImage=SendMessage(hwndList,CLM_GETEXTRAIMAGE,(WPARAM)hItem,i);
+ if(iconOn[i] && iImage==0) iconOn[i]=0;
+ if(iImage!=0xFF) childCount[i]++;
+ }
+ hItem=(HANDLE)SendMessage(hwndList,CLM_GETNEXTITEM,CLGN_NEXTCONTACT,(LPARAM)hItem);
+ }
+ //set icons
+ for( i=0; i < SIZEOF(iconOn); i++) {
+ SendMessage(hwndList,CLM_SETEXTRAIMAGE,(WPARAM)hParentItem,MAKELPARAM(i,childCount[i]?(iconOn[i]?i+1:0):0xFF));
+ if(groupChildCount) groupChildCount[i]+=childCount[i];
+ }
+}
+
+static void SetAllChildIcons(HWND hwndList,HANDLE hFirstItem,int iColumn,int iImage)
+{
+ int typeOfFirst,iOldIcon;
+ HANDLE hItem,hChildItem;
+
+ typeOfFirst=SendMessage(hwndList,CLM_GETITEMTYPE,(WPARAM)hFirstItem,0);
+ //check groups
+ if(typeOfFirst==CLCIT_GROUP) hItem=hFirstItem;
+ else hItem=(HANDLE)SendMessage(hwndList,CLM_GETNEXTITEM,CLGN_NEXTGROUP,(LPARAM)hFirstItem);
+ while(hItem) {
+ hChildItem=(HANDLE)SendMessage(hwndList,CLM_GETNEXTITEM,CLGN_CHILD,(LPARAM)hItem);
+ if(hChildItem) SetAllChildIcons(hwndList,hChildItem,iColumn,iImage);
+ hItem=(HANDLE)SendMessage(hwndList,CLM_GETNEXTITEM,CLGN_NEXTGROUP,(LPARAM)hItem);
+ }
+ //check contacts
+ if(typeOfFirst==CLCIT_CONTACT) hItem=hFirstItem;
+ else hItem=(HANDLE)SendMessage(hwndList,CLM_GETNEXTITEM,CLGN_NEXTCONTACT,(LPARAM)hFirstItem);
+ while(hItem) {
+ iOldIcon=SendMessage(hwndList,CLM_GETEXTRAIMAGE,(WPARAM)hItem,iColumn);
+ if(iOldIcon!=0xFF && iOldIcon!=iImage) SendMessage(hwndList,CLM_SETEXTRAIMAGE,(WPARAM)hItem,MAKELPARAM(iColumn,iImage));
+ hItem=(HANDLE)SendMessage(hwndList,CLM_GETNEXTITEM,CLGN_NEXTCONTACT,(LPARAM)hItem);
+ }
+}
+
+static void SetAllContactIcons(HWND hwndList)
+{
+ HANDLE hContact,hItem;
+ DWORD ignore;
+
+ hContact=(HANDLE)CallService(MS_DB_CONTACT_FINDFIRST,0,0);
+ do {
+ hItem=(HANDLE)SendMessage(hwndList,CLM_FINDCONTACT,(WPARAM)hContact,0);
+ if(hItem) {
+ ignore=DBGetContactSettingDword(hContact,"Ignore",MODULE_NAME,0);
+ if(SendMessage(hwndList,CLM_GETEXTRAIMAGE,(WPARAM)hItem,MAKELPARAM(0,0))==0xFF)
+ SendMessage(hwndList,CLM_SETEXTRAIMAGE,(WPARAM)hItem,MAKELPARAM(0,(ignore&SMII_POPUP)?0:1));
+ if(SendMessage(hwndList,CLM_GETEXTRAIMAGE,(WPARAM)hItem,MAKELPARAM(1,0))==0xFF)
+ SendMessage(hwndList,CLM_SETEXTRAIMAGE,(WPARAM)hItem,MAKELPARAM(1,(ignore&SMII_HISTORY)?0:2));
+ if(SendMessage(hwndList,CLM_GETEXTRAIMAGE,(WPARAM)hItem,MAKELPARAM(2,0))==0xFF)
+ SendMessage(hwndList,CLM_SETEXTRAIMAGE,(WPARAM)hItem,MAKELPARAM(2,(ignore&SMII_LOG)?0:3));
+ }
+ } while(hContact=(HANDLE)CallService(MS_DB_CONTACT_FINDNEXT,(WPARAM)hContact,0));
+}
+
+static void ResetListOptions(HWND hwndList)
+{
+ int i;
+
+ SendMessage(hwndList,CLM_SETBKBITMAP,0,(LPARAM)(HBITMAP)NULL);
+ SendMessage(hwndList,CLM_SETBKCOLOR,GetSysColor(COLOR_WINDOW),0);
+ SendMessage(hwndList,CLM_SETGREYOUTFLAGS,0,0);
+ SendMessage(hwndList,CLM_SETLEFTMARGIN,2,0);
+ SendMessage(hwndList,CLM_SETINDENT,10,0);
+ for(i=0;i<=FONTID_MAX;i++)
+ SendMessage(hwndList,CLM_SETTEXTCOLOR,i,GetSysColor(COLOR_WINDOWTEXT));
+ SetWindowLong(hwndList,GWL_STYLE,GetWindowLong(hwndList,GWL_STYLE)|CLS_SHOWHIDDEN);
+}
+
+extern BOOL CALLBACK IgnoreDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) {
+ static HICON hIcons[3];
+ static HANDLE hItemAll;
+
+ switch (msg)
+ {
+ case WM_INITDIALOG:
+ TranslateDialogDefault(hwndDlg);
+ { HIMAGELIST hIml;
+ hIml = ImageList_Create(GetSystemMetrics(SM_CXSMICON),GetSystemMetrics(SM_CYSMICON),
+ ((LOBYTE(LOWORD(GetVersion()))>=5 && LOWORD(GetVersion())!=5) ? ILC_COLOR32 : ILC_COLOR16) | ILC_MASK, 4, 4);
+ ImageList_AddIcon(hIml, LoadSkinnedIcon(SKINICON_OTHER_SMALLDOT));
+ ImageList_AddIcon(hIml, (HICON)CallService(MS_SKIN2_GETICONBYHANDLE, 0, (LPARAM)ICO_POPUP_E));
+ ImageList_AddIcon(hIml, (HICON)CallService(MS_SKIN2_GETICONBYHANDLE, 0, (LPARAM)ICO_HISTORY));
+ ImageList_AddIcon(hIml, (HICON)CallService(MS_SKIN2_GETICONBYHANDLE, 0, (LPARAM)ICO_LOG));
+ SendDlgItemMessage(hwndDlg, IDC_IGNORELIST, CLM_SETEXTRAIMAGELIST, 0, (LPARAM)hIml);
+ hIcons[0] = ImageList_GetIcon(hIml, 1, ILD_NORMAL);
+ SendDlgItemMessage(hwndDlg, IDC_IGNOREPOPUP, STM_SETICON, (WPARAM)hIcons[0], 0);
+ hIcons[1] = ImageList_GetIcon(hIml, 2, ILD_NORMAL);
+ SendDlgItemMessage(hwndDlg, IDC_IGNOREHISTORY, STM_SETICON, (WPARAM)hIcons[1],0);
+ hIcons[2] = ImageList_GetIcon(hIml, 3, ILD_NORMAL);
+ SendDlgItemMessage(hwndDlg, IDC_IGNORELOG, STM_SETICON, (WPARAM)hIcons[2],0);
+ }
+
+ ResetListOptions(GetDlgItem(hwndDlg, IDC_IGNORELIST));
+ SendDlgItemMessage(hwndDlg, IDC_IGNORELIST, CLM_SETEXTRACOLUMNS, 3, 0);
+
+ { CLCINFOITEM cii = {0};
+ cii.cbSize = sizeof(cii);
+ cii.flags = CLCIIF_GROUPFONT;
+ cii.pszText = TranslateT("** All contacts **");
+ hItemAll = (HANDLE)SendDlgItemMessage(hwndDlg, IDC_IGNORELIST, CLM_ADDINFOITEM, 0, (LPARAM)&cii);
+ }
+
+ SetAllContactIcons(GetDlgItem(hwndDlg, IDC_IGNORELIST));
+ SetListGroupIcons(GetDlgItem(hwndDlg,IDC_IGNORELIST),
+ (HANDLE)SendDlgItemMessage(hwndDlg, IDC_IGNORELIST, CLM_GETNEXTITEM, CLGN_ROOT, 0), hItemAll, NULL);
+ return TRUE;
+ case WM_SETFOCUS:
+ SetFocus(GetDlgItem(hwndDlg,IDC_IGNORELIST));
+ break;
+ case WM_NOTIFY:
+ switch(((LPNMHDR)lParam)->idFrom) {
+ case IDC_LIST:
+ switch (((LPNMHDR)lParam)->code)
+ {
+ case CLN_NEWCONTACT:
+ case CLN_LISTREBUILT:
+ SetAllContactIcons(GetDlgItem(hwndDlg,IDC_LIST));
+ //fall through
+ case CLN_CONTACTMOVED:
+ SetListGroupIcons(GetDlgItem(hwndDlg,IDC_LIST),(HANDLE)SendDlgItemMessage(hwndDlg,IDC_LIST,CLM_GETNEXTITEM,CLGN_ROOT,0),hItemAll,NULL);
+ break;
+ case CLN_OPTIONSCHANGED:
+ ResetListOptions(GetDlgItem(hwndDlg,IDC_LIST));
+ break;
+ case NM_CLICK:
+ { HANDLE hItem;
+ NMCLISTCONTROL *nm=(NMCLISTCONTROL*)lParam;
+ DWORD hitFlags;
+ int iImage;
+ int itemType;
+
+ // Make sure we have an extra column
+ if (nm->iColumn == -1)
+ break;
+
+ // Find clicked item
+ hItem = (HANDLE)SendDlgItemMessage(hwndDlg, IDC_LIST, CLM_HITTEST, (WPARAM)&hitFlags, MAKELPARAM(nm->pt.x,nm->pt.y));
+ // Nothing was clicked
+ if (hItem == NULL) break;
+ // It was not a visbility icon
+ if (!(hitFlags & CLCHT_ONITEMEXTRA)) break;
+
+ // Get image in clicked column (0=none, 1=popups, 2=history, 3=log to file)
+ iImage = SendDlgItemMessage(hwndDlg, IDC_LIST, CLM_GETEXTRAIMAGE, (WPARAM)hItem, MAKELPARAM(nm->iColumn, 0));
+ if (iImage == 0)
+ iImage=nm->iColumn + 1;
+ else
+ if (iImage == 1 || iImage == 2 || iImage == 3)
+ iImage = 0;
+
+ // Get item type (contact, group, etc...)
+ itemType = SendDlgItemMessage(hwndDlg, IDC_LIST, CLM_GETITEMTYPE, (WPARAM)hItem, 0);
+
+ // Update list
+ if (itemType == CLCIT_CONTACT) { // A contact
+ SendDlgItemMessage(hwndDlg, IDC_LIST, CLM_SETEXTRAIMAGE, (WPARAM)hItem, MAKELPARAM(nm->iColumn, iImage));
+ }
+ else if (itemType == CLCIT_INFO) { // All Contacts
+ SetAllChildIcons(GetDlgItem(hwndDlg, IDC_LIST), hItem, nm->iColumn, iImage);
+ }
+ else if (itemType == CLCIT_GROUP) { // A group
+ hItem = (HANDLE)SendDlgItemMessage(hwndDlg, IDC_LIST, CLM_GETNEXTITEM, CLGN_CHILD, (LPARAM)hItem);
+ if (hItem) {
+ SetAllChildIcons(GetDlgItem(hwndDlg, IDC_LIST), hItem, nm->iColumn, iImage);
+ }
+ }
+ // Update the all/none icons
+ SetListGroupIcons(GetDlgItem(hwndDlg, IDC_LIST), (HANDLE)SendDlgItemMessage(hwndDlg, IDC_LIST, CLM_GETNEXTITEM, CLGN_ROOT, 0), hItemAll, NULL);
+
+ // Activate Apply button
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ break;
+ }
+ }
+ break;
+ case 0:
+ switch (((LPNMHDR)lParam)->code)
+ {
+ case PSN_APPLY:
+ { HANDLE hContact,hItem;
+ int set,i,iImage;
+
+ hContact=(HANDLE)CallService(MS_DB_CONTACT_FINDFIRST,0,0);
+ do {
+ hItem=(HANDLE)SendDlgItemMessage(hwndDlg,IDC_LIST,CLM_FINDCONTACT,(WPARAM)hContact,0);
+ if(hItem) {
+ set=0;
+ for(i=0;i<3;i++) {
+ iImage=SendDlgItemMessage(hwndDlg,IDC_LIST,CLM_GETEXTRAIMAGE,(WPARAM)hItem,MAKELPARAM(i,0));
+ if(iImage==0) set = set | (1<<i);
+ }
+ DBWriteContactSettingDword(hContact,"Ignore",MODULE_NAME,set);
+ }
+ } while(hContact=(HANDLE)CallService(MS_DB_CONTACT_FINDNEXT,(WPARAM)hContact,0));
+ return TRUE;
+ }
+ }
+ break;
+ }
+ break;
+ case WM_DESTROY:
+ DestroyIcon(hIcons[0]);
+ DestroyIcon(hIcons[1]);
+ DestroyIcon(hIcons[2]);
+ { HIMAGELIST hIml=(HIMAGELIST)SendDlgItemMessage(hwndDlg,IDC_LIST,CLM_GETEXTRAIMAGELIST,0,0);
+ ImageList_Destroy(hIml);
+ }
+ break;
+ }
+ return FALSE;
+}
diff --git a/Plugins/smcnotify/list.c b/Plugins/smcnotify/list.c
new file mode 100644
index 0000000..bc40ecc
--- /dev/null
+++ b/Plugins/smcnotify/list.c
@@ -0,0 +1,445 @@
+/*
+Status Message Change Notify plugin for Miranda IM.
+
+Copyright © 2004-2005 NoName
+Copyright © 2005-2006 Daniel Vijge, Tomasz S³otwiñski, Ricardo Pescuma Domenecci
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "commonheaders.h"
+
+
+typedef struct {
+ int bSortAscending;
+ int iProtoSort;
+ int iLastColumnSortIndex;
+} LVDLGDAT;
+
+typedef struct {
+ HANDLE hContact;
+ int iiProto;
+ int iStatus;
+} LVITEMDAT;
+
+
+static int GetStatusOnlineness(int status) {
+ switch (status) {
+ case ID_STATUS_FREECHAT:
+ return 110;
+ case ID_STATUS_ONLINE:
+ return 100;
+ case ID_STATUS_OCCUPIED:
+ return 60;
+ case ID_STATUS_ONTHEPHONE:
+ return 50;
+ case ID_STATUS_DND:
+ return 40;
+ case ID_STATUS_AWAY:
+ return 30;
+ case ID_STATUS_OUTTOLUNCH:
+ return 20;
+ case ID_STATUS_NA:
+ return 10;
+ case ID_STATUS_INVISIBLE:
+ return 5;
+ }
+ return 0;
+}
+
+static BOOL CheckStatusMessage(HANDLE hContact, TCHAR* str) {
+ if (DBGetContactSettingByte(hContact, "CList", "Hidden", 0)) return 0;
+
+ MyDBGetContactSettingTString_dup(hContact, "CList", "StatusMsg", str);
+
+ if (str != NULL && str[0] != _T('\0')) return 1;
+
+ return 0;
+}
+
+static BOOL ListOpenContact(HWND hList, int item) {
+ if (item != -1)
+ {
+ LVITEM lvi;
+ LVITEMDAT *lvidat;
+ ZeroMemory(&lvi, sizeof(lvi));
+ lvi.mask = LVIF_PARAM;
+ lvi.lParam = (LONG)NULL;
+ lvi.iItem = item;
+ lvi.iSubItem = 0;
+ ListView_GetItem(hList, &lvi);
+ lvidat = (LVITEMDAT*)lvi.lParam;
+ if(lvidat->hContact != NULL)
+ {
+ CallService(MS_MSG_SENDMESSAGE, (WPARAM)lvidat->hContact, (LONG)NULL);
+ CallService("SRMsg/LaunchMessageWindow", (WPARAM)lvidat->hContact, (LONG)NULL);
+ return 0;
+ }
+ }
+ return 1;
+}
+
+static BOOL ListOpenContactMenu(HWND hDlg, HWND hList, int item) {
+ if (item != -1)
+ {
+ LVITEM lvi;
+ LVITEMDAT *lvidat;
+ ZeroMemory(&lvi, sizeof(lvi));
+ lvi.mask = LVIF_PARAM;
+ lvi.lParam = (LONG)NULL;
+ lvi.iItem = item;
+ lvi.iSubItem = 0;
+ ListView_GetItem(hList, &lvi);
+ lvidat = (LVITEMDAT*)lvi.lParam;
+ if (lvidat->hContact != NULL)
+ {
+ HMENU hCMenu = (HMENU)CallService(MS_CLIST_MENUBUILDCONTACT, (WPARAM)lvidat->hContact, 0);
+ if (hCMenu != NULL)
+ {
+ POINT p;
+ int ret;
+ GetCursorPos(&p);
+ ret = TrackPopupMenu(hCMenu, TPM_NONOTIFY | TPM_RETURNCMD, p.x, p.y, 0, hDlg, NULL);
+ CallService(MS_CLIST_MENUPROCESSCOMMAND, MAKEWPARAM(ret, MPCF_CONTACTMENU), (LPARAM)lvidat->hContact);
+ DestroyMenu(hCMenu);
+ return 0;
+ }
+ }
+ }
+ return 1;
+}
+
+static int CALLBACK ListSortFunc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort) {
+ LVDLGDAT *dat = (LVDLGDAT*)lParamSort;
+ HWND hList = GetDlgItem(hListDlg, IDC_LIST);
+ int result = 0;
+
+ const int maxSize = 128;
+ TCHAR text1[128];
+ TCHAR text2[128];
+ ListView_GetItemText(hList, (int)lParam1, dat->iLastColumnSortIndex, text1, maxSize);
+ ListView_GetItemText(hList, (int)lParam2, dat->iLastColumnSortIndex, text2, maxSize);
+
+ switch (dat->iLastColumnSortIndex)
+ {
+ case 0:
+ {
+ LVITEM lvi;
+ LVITEMDAT *lvid1, *lvid2;
+ ZeroMemory(&lvi, sizeof(lvi));
+ lvi.mask = LVIF_PARAM;
+ lvi.lParam = (LONG)NULL;
+ lvi.iSubItem = 0;
+
+ lvi.iItem = (int)lParam1;
+ ListView_GetItem(hList, &lvi);
+ lvid1 = (LVITEMDAT*)lvi.lParam;
+
+ lvi.iItem = (int)lParam2;
+ ListView_GetItem(hList, &lvi);
+ lvid2 = (LVITEMDAT*)lvi.lParam;
+
+ if ((lvid1 == NULL) || (lvid2 == NULL)) return 0;
+
+ if (dat->iProtoSort == 1)
+ {
+ if (lvid1->iStatus == lvid2->iStatus) break;
+ else
+ {
+ result = (lvid1->iStatus < lvid2->iStatus) ? 1 : -1;
+ }
+ }
+ else if (dat->iProtoSort == 0)
+ {
+ result = lstrcmp(text1, text2);
+ if (result == 0)
+ {
+ if (lvid1->iStatus == lvid2->iStatus) break;
+ else
+ {
+ result = (lvid1->iStatus < lvid2->iStatus) ? 1 : -1;
+ }
+ }
+ }
+ break;
+ }
+ case 1:
+ case 2:
+ result = lstrcmpi(text1, text2);
+ break;
+ }
+
+ return dat->bSortAscending ? result : -result;
+}
+
+static void AddColumns(HWND hList) {
+ HIMAGELIST hImgList;
+ LVCOLUMN lvc;
+
+ ListView_SetExtendedListViewStyle(hList,
+ LVS_EX_FULLROWSELECT
+ | LVS_EX_INFOTIP
+ | LVS_EX_LABELTIP
+ | LVS_EX_BORDERSELECT
+ | LVS_EX_GRIDLINES
+ );
+
+ hImgList = (HIMAGELIST)CallService(MS_CLIST_GETICONSIMAGELIST, 0, 0);
+ if (hImgList != NULL)
+ ListView_SetImageList(hList, hImgList, LVSIL_SMALL);
+
+ ZeroMemory(&lvc, sizeof(lvc));
+ lvc.mask = LVCF_FMT | LVCF_TEXT | LVCF_WIDTH;
+ lvc.fmt = LVCFMT_IMAGE;
+ lvc.pszText = TranslateT("Protocol");
+ lvc.cx = DBGetContactSettingWord(NULL, MODULE_NAME, "ListColWidth0", 30);
+ ListView_InsertColumn(hList, 0, &lvc);
+ lvc.mask = LVCF_TEXT | LVCF_WIDTH;
+// lvc.fmt = LVCFMT_LEFT;
+ lvc.pszText = TranslateT("Nick");
+ lvc.cx = DBGetContactSettingWord(NULL, MODULE_NAME, "ListColWidth1", 70);
+ ListView_InsertColumn(hList, 1, &lvc);
+ lvc.pszText = TranslateT("Status Message");
+ lvc.cx = DBGetContactSettingWord(NULL, MODULE_NAME, "ListColWidth2", 250);
+ ListView_InsertColumn(hList, 2, &lvc);
+
+ ListView_SetTextColor(hList, opts.colListText);
+ if (opts.bListUseBkImage && lstrcmp(opts.listbkimage, _T("")))
+ {
+ LVBKIMAGE lvbi;
+ ZeroMemory(&lvbi, sizeof(lvbi));
+ lvbi.ulFlags = LVBKIF_SOURCE_URL | LVBKIF_STYLE_TILE;
+ lvbi.pszImage = opts.listbkimage;
+ ListView_SetBkImage(hList, &lvbi);
+ ListView_SetBkColor(hList, GetSysColor(COLOR_HIGHLIGHT));
+ ListView_SetTextBkColor(hList, CLR_NONE);
+ }
+ else
+ {
+ ListView_SetBkColor(hList, opts.colListBack);
+ ListView_SetTextBkColor(hList, opts.colListBack);
+ }
+}
+
+static void LoadContacts(HWND hList) {
+ HANDLE hContact;
+ LVITEM lvi;
+ LVITEMDAT *lvidat;
+ int i = 0;
+ TCHAR *proto = NULL;
+ TCHAR *smsg = NULL;
+
+ ZeroMemory(&lvi, sizeof(lvi));
+ hContact = (HANDLE)CallService(MS_DB_CONTACT_FINDFIRST, 0, 0);
+ while (hContact)
+ {
+// if (DBGetContactSettingByte(hContact, "CList", "Hidden", 0));
+ smsg = MyDBGetContactSettingTString_dup(hContact, "CList", "StatusMsg", smsg);
+ if (smsg != NULL && smsg[0] != _T('\0'))
+ {
+ lvidat = (LVITEMDAT*)mir_alloc(sizeof(LVITEMDAT));
+ lvidat->iiProto = CallService(MS_CLIST_GETCONTACTICON, (WPARAM)hContact, 0);
+ lvidat->iStatus = GetStatusOnlineness(DBGetContactSettingWord(hContact, (char*)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0), "Status", 0));
+ lvidat->hContact = hContact;
+ lvi.mask = LVIF_PARAM | LVIF_IMAGE | LVIF_TEXT;
+ lvi.iItem = i;
+ lvi.iSubItem = 0;
+ lvi.lParam = (LPARAM)lvidat;
+ lvi.iImage = CallService(MS_CLIST_GETCONTACTICON, (WPARAM)hContact, 0);
+ proto = MyDBGetContactSettingTString_dup(hContact, "Protocol", "p", proto);
+ lvi.pszText = proto;
+ ListView_InsertItem(hList, &lvi);
+ ListView_SetItemText(hList, i, 1, (TCHAR*)CallService(MS_CLIST_GETCONTACTDISPLAYNAME, (WPARAM)hContact, GCDNF_TCHAR));
+ ListView_SetItemText(hList, i, 2, smsg);
+ i++;
+ mir_free(smsg);
+ mir_free(proto);
+ }
+ hContact = (HANDLE)CallService(MS_DB_CONTACT_FINDNEXT, (WPARAM)hContact, 0);
+ }
+}
+
+static BOOL CALLBACK ListDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) {
+ HWND hList = GetDlgItem(hwndDlg, IDC_LIST);
+ LVDLGDAT *lvdat;
+ lvdat = (LVDLGDAT*)GetWindowLong(hwndDlg, GWL_USERDATA);
+ switch (msg)
+ {
+ case WM_INITDIALOG:
+ {
+ DWORD sort;
+ TranslateDialogDefault(hwndDlg);
+ SendMessage(hwndDlg, WM_SETICON, (WPARAM)ICON_BIG, CallService(MS_SKIN2_GETICONBYHANDLE, 0, (LPARAM)ICO_LIST));
+ SendMessage(hwndDlg, WM_SETICON, (WPARAM)ICON_SMALL, CallService(MS_SKIN2_GETICONBYHANDLE, 0, (LPARAM)ICO_LIST));
+ Utils_RestoreWindowPosition(hwndDlg, NULL, MODULE_NAME, "List");
+
+ lvdat = (LVDLGDAT*)mir_alloc(sizeof(LVDLGDAT));
+ sort = DBGetContactSettingDword(NULL, MODULE_NAME, "ListColumnSort", 9);
+ lvdat->iLastColumnSortIndex = sort >> 2;
+ lvdat->bSortAscending = (sort & 1) ? 1 : 0;
+ lvdat->iProtoSort = (sort & 2) ? 1 : 0;
+ SetWindowLong(hwndDlg, GWL_USERDATA, (LONG)lvdat);
+
+ AddColumns(hList);
+
+ LoadContacts(hList);
+
+ ListView_SortItemsEx(hList, ListSortFunc, (LPARAM)lvdat);
+// SendMessage(hwndDlg, WM_SIZE, 0, 0);
+// ShowWindow(hwndDlg, SW_SHOW);
+ break;
+ }
+ case WM_NOTIFY:
+ {
+ if (((LPNMHDR)lParam)->idFrom == IDC_LIST)
+ {
+ switch (((LPNMHDR)lParam)->code)
+ {
+ case NM_DBLCLK:
+ {
+ LVHITTESTINFO lvh;
+ POINT p;
+ GetCursorPos(&p);
+ ZeroMemory(&lvh, sizeof(lvh));
+ lvh.pt = p;
+ ScreenToClient(hList, &lvh.pt);
+ ListView_HitTest(hList, &lvh);
+ if ((lvh.flags & (LVHT_ONITEMICON | LVHT_ONITEMLABEL | LVHT_ONITEMSTATEICON)) && lvh.iItem != -1)
+ ListOpenContact(hList, lvh.iItem);
+ break;
+ }
+ case NM_RCLICK:
+ {
+ LVHITTESTINFO lvh;
+ POINT p;
+ GetCursorPos(&p);
+ ZeroMemory(&lvh, sizeof(lvh));
+ lvh.pt = p;
+ ScreenToClient(hList, &lvh.pt);
+ ListView_HitTest(hList, &lvh);
+ if ((lvh.flags & (LVHT_ONITEMICON | LVHT_ONITEMLABEL | LVHT_ONITEMSTATEICON)) && lvh.iItem != -1)
+ ListOpenContactMenu(hwndDlg, hList, lvh.iItem);
+ break;
+ }
+ case LVN_COLUMNCLICK:
+ {
+ LPNMLISTVIEW nmlv = (LPNMLISTVIEW)lParam;
+// if (nmlv->iSubItem == 2) {
+// SendMessage(hwndDlg, WM_USER + 10, 0, 0);
+// break;
+// }
+ if (nmlv->iSubItem != lvdat->iLastColumnSortIndex)
+ {
+ lvdat->bSortAscending = 1;
+ lvdat->iLastColumnSortIndex = nmlv->iSubItem;
+ lvdat->iProtoSort = 0;
+ }
+ else
+ {
+ lvdat->bSortAscending = !lvdat->bSortAscending;
+ if (lvdat->bSortAscending == 1) lvdat->iProtoSort = !lvdat->iProtoSort;
+ }
+ ListView_SortItemsEx(hList, ListSortFunc, (LPARAM)lvdat);
+ break;
+ }
+ }
+ }
+ break;
+ }
+ case WM_MEASUREITEM:
+ return CallService(MS_CLIST_MENUMEASUREITEM, wParam, lParam);
+ case WM_DRAWITEM:
+ return CallService(MS_CLIST_MENUDRAWITEM, wParam, lParam);
+ case WM_COMMAND:
+ {
+ switch (wParam)
+ {
+ case IDOK:
+ ListOpenContact(hList, ListView_GetNextItem(hList, -1, LVIS_SELECTED));
+ break;
+ case IDCANCEL:
+ SendMessage(hwndDlg, WM_CLOSE, 0, 0);
+ break;
+ }
+ break;
+ }
+// case WM_USER + 10: // refresh
+// ListView_DeleteAllItems(hList);
+// LoadContactsStatusMsgList(hList);
+// ListView_SortItems(hList, ListSortFunc, (LPARAM)lvdat);
+// return 0;
+ case WM_SIZE:
+ {
+ RECT rc;
+ POINT p;
+ GetWindowRect(hwndDlg, &rc);
+ p.x = rc.right - GetSystemMetrics(SM_CYDLGFRAME);
+ p.y = rc.bottom - GetSystemMetrics(SM_CXDLGFRAME);
+ ScreenToClient(hwndDlg, &p);
+ MoveWindow(hList, 0, 0, p.x, p.y, TRUE);
+ break;
+ }
+ case WM_CLOSE:
+ {
+ DestroyWindow(hwndDlg);
+// KillTimer(hwndDlg, TIMERID_LISTAUTOREFRESH);
+ break;
+ }
+ case WM_DESTROY:
+ {
+ DWORD sort;
+ int i;
+ Utils_SaveWindowPosition(hwndDlg, NULL, MODULE_NAME, "List");
+ DBWriteContactSettingWord(NULL, MODULE_NAME, "ListColWidth0", (WORD)ListView_GetColumnWidth(hList, 0));
+ DBWriteContactSettingWord(NULL, MODULE_NAME, "ListColWidth1", (WORD)ListView_GetColumnWidth(hList, 1));
+ DBWriteContactSettingWord(NULL, MODULE_NAME, "ListColWidth2", (WORD)ListView_GetColumnWidth(hList, 2));
+
+// sort = lvdat->iLastColumnSortIndex<<2;
+ sort = lvdat->iLastColumnSortIndex * 4;
+ sort = lvdat->bSortAscending ? sort | 1 : sort;
+ sort = lvdat->iProtoSort ? sort | 2 : sort;
+ DBWriteContactSettingDword(NULL, MODULE_NAME, "ListColumnSort", sort);
+
+ for (i = 0; i < ListView_GetItemCount(hList); i++)
+ {
+ LVITEM lvi;
+ ZeroMemory(&lvi, sizeof(lvi));
+ lvi.mask = LVIF_PARAM;
+ lvi.lParam = (LONG)NULL;
+ lvi.iItem = i;
+ lvi.iSubItem = 0;
+ ListView_GetItem(hList, &lvi);
+ mir_free((LVITEMDAT*)lvi.lParam);
+ }
+ mir_free(lvdat);
+ hListDlg = NULL;
+ break;
+ }
+ }
+ return FALSE;
+}
+
+extern void ShowList() {
+ if (hListDlg == NULL)
+ {
+ hListDlg = CreateDialog(hInst, MAKEINTRESOURCE(IDD_LIST), NULL, ListDlgProc);
+ //ShowWindow(hListDlg, SW_SHOW);
+ }
+ else
+ {
+ SetForegroundWindow(hListDlg);
+ SetFocus(hListDlg);
+ }
+}
diff --git a/Plugins/smcnotify/m_smcnotify.h b/Plugins/smcnotify/m_smcnotify.h
new file mode 100644
index 0000000..88a0c18
--- /dev/null
+++ b/Plugins/smcnotify/m_smcnotify.h
@@ -0,0 +1,59 @@
+/*
+Status Message Change Notify plugin for Miranda IM.
+
+Copyright © 2004-2005 NoName
+Copyright © 2005-2007 Daniel Vijge, Tomasz S³otwiñski, Ricardo Pescuma Domenecci
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef __SMCNOTIFY_M_SMCNOTIFY_H
+#define __SMCNOTIFY_M_SMCNOTIFY_H
+
+
+#define EVENTTYPE_STATUSMESSAGE_CHANGE 9002
+
+/*
+Enable/Disable status message change notification
+
+wParam=
+0 - used internaly, toggle popups, lParam is ignored
+1 - enable/disable popups from within other plugins, see lParam
+lParam=0
+0 - disable popups
+1 - enable popups
+*/
+#define MS_SMCNOTIFY_POPUPS "SMCNotify/Popups"
+
+
+/*
+Show List with all the contact that have a status message set
+
+wParam=lParam=ignored
+*/
+#define MS_SMCNOTIFY_LIST "SMCNotify/ShowList"
+
+
+/*
+Go To URL in Status Message
+used just internaly for now
+
+wParam=lParam=ignored
+*/
+#define MS_SMCNOTIFY_GOTOURL "SMCNotify/GoToURL"
+
+
+
+#endif // __SMCNOTIFY_M_SMCNOTIFY_H
diff --git a/Plugins/smcnotify/menu.c b/Plugins/smcnotify/menu.c
new file mode 100644
index 0000000..ae74ea8
--- /dev/null
+++ b/Plugins/smcnotify/menu.c
@@ -0,0 +1,192 @@
+/*
+Status Message Change Notify plugin for Miranda IM.
+
+Copyright © 2004-2005 NoName
+Copyright © 2005-2006 Daniel Vijge, Tomasz S³otwiñski, Ricardo Pescuma Domenecci
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "commonheaders.h"
+
+
+int hmPopups;
+int hmShowList;
+int hmGoToURL;
+
+char *pszStatusMsgURL;
+
+char *pszIconId[ICONCOUNT] = {"smcnotify_pope", "smcnotify_popd", "smcnotify_list", "smcnotify_url", "smcnotify_history", "smcnotify_log"};
+
+extern void LoadIcons(void) {
+ SKINICONDESC sid;
+
+ TCHAR *ptszIconName[ICONCOUNT] = {
+ TranslateT("PopUps Enabled"),
+ TranslateT("PopUps Disabled"),
+ TranslateT("List Contacts"),
+ TranslateT("Go To URL"),
+ TranslateT("Status Message History"),
+ TranslateT("Log To File")
+ };
+ int iIconId[ICONCOUNT] = {IDI_POPUP, IDI_NOPOPUP, IDI_LIST, IDI_URL, IDI_HISTORY, IDI_LOG};
+ int i;
+ ZeroMemory(&sid, sizeof(sid));
+ sid.cbSize = sizeof(sid);
+ sid.flags = SIDF_TCHAR;
+ sid.ptszSection = TranslateT(PLUGIN_NAME);
+ for (i = 0; i < ICONCOUNT; i++)
+ {
+ sid.pszName = pszIconId[i];
+ sid.ptszDescription = ptszIconName[i];
+ sid.hDefaultIcon = LoadIcon(hInst, MAKEINTRESOURCE(iIconId[i]));
+ hLibIcons[i] = (HANDLE)CallService(MS_SKIN2_ADDICON, 0, (LPARAM)&sid);
+ }
+
+ return;
+}
+
+static void UpdateMenuItems(BOOL set) {
+ CLISTMENUITEM mi;
+
+ ZeroMemory(&mi, sizeof(mi));
+ mi.cbSize = sizeof(mi);
+ if (set)
+ {
+ mi.pszName = Translate("Disable status message change notification");
+ mi.hIcon = ICO_POPUP_E;
+ }
+ else
+ {
+ mi.pszName = Translate("Enable status message change notification");
+ mi.hIcon = ICO_POPUP_D;
+ }
+ puopts.bEnable = set;
+ mi.flags = CMIM_NAME | CMIM_ICON;
+ CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM)hmPopups, (LPARAM)&mi);
+
+ return;
+}
+
+extern void InitMenuItems(void) {
+ CLISTMENUITEM mi;
+
+ ZeroMemory(&mi, sizeof(mi));
+ mi.cbSize = sizeof(mi);
+// mi.flags = CMIF_ICONFROMICOLIB;
+
+ if (PopupActive)
+ {
+ //Disable/Enable status message change notification
+ mi.position = 0;
+// mi.icolibItem = ICO_POPUP_E;
+ mi.pszPopupName = Translate("PopUps");
+ mi.pszService = MS_SMCNOTIFY_POPUPS;
+ hmPopups = CallService(MS_CLIST_ADDMAINMENUITEM, 0, (LPARAM)&mi);
+
+ mi.ptszPopupName = NULL;
+ }
+
+ mi.flags = CMIF_TCHAR | CMIF_ICONFROMICOLIB;
+
+ //List Contacts with Status Message - main menu
+ mi.position = 500021000;
+ mi.icolibItem = ICO_LIST;
+ mi.ptszName = TranslateT("List Contacts with Status Message");
+ mi.pszService = MS_SMCNOTIFY_LIST;
+ hmShowList = CallService(MS_CLIST_ADDMAINMENUITEM, 0, (LPARAM)&mi);
+
+ mi.pszContactOwner = NULL;
+
+ //Go To URL in Status Message - contact menu
+ mi.position = -2000004000;
+ mi.icolibItem = ICO_URL;
+ mi.ptszName = TranslateT("Go To URL in Status Message");
+ mi.pszService = MS_SMCNOTIFY_GOTOURL;
+ hmGoToURL = CallService(MS_CLIST_ADDCONTACTMENUITEM, 0, (LPARAM)&mi);
+
+ UpdateMenuItems(puopts.bEnable);
+ return;
+}
+
+int MenuItemCmd_PopUps(WPARAM wParam, LPARAM lParam) {
+ UpdateMenuItems(wParam ? lParam : !puopts.bEnable);
+ return 0;
+}
+
+int MenuItemCmd_ShowList(WPARAM wParam, LPARAM lParam) {
+ ShowList();
+ return 0;
+}
+
+int MenuItemCmd_GoToURL(WPARAM wParam, LPARAM lParam) {
+ CallService(MS_UTILS_OPENURL, 1, (LPARAM)pszStatusMsgURL);
+ MIR_FREE(pszStatusMsgURL);
+
+ return 0;
+}
+
+extern int PreBuildCMenu(WPARAM wParam, LPARAM lParam) {
+ DBVARIANT dbv;
+ CLISTMENUITEM clmi;
+ char *str, *p;
+ int c;
+ str = NULL;
+
+ ZeroMemory(&clmi, sizeof(clmi));
+ clmi.cbSize = sizeof(clmi);
+ clmi.flags = CMIM_FLAGS | CMIF_HIDDEN;
+ if (!DBGetContactSettingTString((HANDLE)wParam, "CList", "StatusMsg", &dbv))
+ {
+#ifdef UNICODE
+ if ((dbv.type == DBVT_ASCIIZ) || (dbv.type == DBVT_UTF8))
+ {
+ str = dbv.pszVal;
+ }
+ else if (dbv.type == DBVT_WCHAR)
+ {
+ str = mir_dupToAscii(dbv.pwszVal);
+ }
+#else
+ if (dbv.type == DBVT_ASCIIZ)
+ {
+ str = dbv.pszVal;
+ }
+#endif
+
+ if (lstrcmpA(str, ""))
+ {
+ p = strstr(str, "www.");
+ if (p == NULL) p = strstr(str, "http://");
+ if (p != NULL)
+ {
+ for (c = 0; p[c] != '\n' && p[c] != '\r' && p[c] != '\t' && p[c] != ' ' && p[c] != '\0'; c++);
+
+ lstrcpynA(str, p, c + 1);
+ mir_free(pszStatusMsgURL);
+ pszStatusMsgURL = mir_strdup(str);
+
+ clmi.flags = CMIM_FLAGS;
+ }
+ }
+ DBFreeVariant(&dbv);
+ }
+ CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM)hmGoToURL, (LPARAM)&clmi);
+
+ mir_free(str);
+ mir_free(p);
+
+ return 0;
+} \ No newline at end of file
diff --git a/Plugins/smcnotify/menu.h b/Plugins/smcnotify/menu.h
new file mode 100644
index 0000000..7b60839
--- /dev/null
+++ b/Plugins/smcnotify/menu.h
@@ -0,0 +1,42 @@
+/*
+Status Message Change Notify plugin for Miranda IM.
+
+Copyright © 2004-2005 NoName
+Copyright © 2005-2006 Daniel Vijge, Tomasz S³otwiñski, Ricardo Pescuma Domenecci
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef __SMCNOTIFY_MENU_H
+#define __SMCNOTIFY_MENU_H
+
+void LoadIcons();
+void InitMenuItems();
+int PreBuildCMenu(WPARAM wParam, LPARAM lParam);
+
+int MenuItemCmd_PopUps(WPARAM wParam, LPARAM lParam);
+int MenuItemCmd_ShowList(WPARAM wParam, LPARAM lParam);
+int MenuItemCmd_GoToURL(WPARAM wParam, LPARAM lParam);
+
+#define ICONCOUNT 6
+HANDLE hLibIcons[ICONCOUNT];
+#define ICO_POPUP_E hLibIcons[0]
+#define ICO_POPUP_D hLibIcons[1]
+#define ICO_LIST hLibIcons[2]
+#define ICO_URL hLibIcons[3]
+#define ICO_HISTORY hLibIcons[4]
+#define ICO_LOG hLibIcons[5]
+
+#endif // __SMCNOTIFY_MENU_H
diff --git a/Plugins/smcnotify/options.c b/Plugins/smcnotify/options.c
new file mode 100644
index 0000000..ef690a8
--- /dev/null
+++ b/Plugins/smcnotify/options.c
@@ -0,0 +1,496 @@
+/*
+Status Message Change Notify plugin for Miranda IM.
+
+Copyright © 2004-2005 NoName
+Copyright © 2005-2006 Daniel Vijge, Tomasz S³otwiñski, Ricardo Pescuma Domenecci
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "commonheaders.h"
+
+
+BOOL AllowProtocol(const char *proto) {
+ if ((CallProtoService(proto, PS_GETCAPS, PFLAGNUM_1, 0) & PF1_MODEMSGRECV) == 0)
+ return FALSE;
+
+ return TRUE;
+}
+
+static OptPageControl optionsControls[] = {
+ {&opts.bIgnoreRemove, CONTROL_CHECKBOX, IDC_IGNOREREMOVE, "IgnoreRemove", FALSE},
+ {&opts.bListUseBkImage, CONTROL_CHECKBOX, IDC_USEBGIMG, "ListUseBkImage", FALSE},
+ {&opts.colListBack, CONTROL_COLOR, IDC_LISTBGCOLOR, "ListBkColor", RGB(255,255,255)},
+ {&opts.colListText, CONTROL_COLOR, IDC_LISTTEXTCOLOR, "ListTextColor", RGB(0,0,0)},
+ {NULL, CONTROL_PROTOCOL_LIST, IDC_PROTOCOLS, "%sEnabled", TRUE, (int)AllowProtocol}
+};
+
+static OptPageControl advancedControls[] = {
+ {&opts.bHistoryEnable, CONTROL_CHECKBOX, IDC_HISTORY, "HistoryEnable", TRUE},
+ {&opts.dHistoryMax, CONTROL_SPIN, IDC_HISTORYMAX, "HistoryMax", 20, IDC_HISTORYMAX_SPIN, (WORD)1, (WORD)100},
+ {&opts.history, CONTROL_TEXT, IDC_HISTORYTEXT, "HistoryTemplate", (DWORD)_T(DEFAULT_TEMPLATE_HISTORY)},
+ {&opts.bDBEnable, CONTROL_CHECKBOX, IDC_MESSAGEWND, "DBEnable", TRUE},
+// {&opts.history_only_ansi_if_possible, CONTROL_CHECKBOX, IDC_ANSI, "HistoryOnlyANSIIfPossible", TRUE},
+ {&opts.msgchanged, CONTROL_TEXT, IDC_MSGCHANGED, "TemplateChanged", (DWORD)_T(DEFAULT_TEMPLATE_CHANGED)},
+ {&opts.msgremoved, CONTROL_TEXT, IDC_MSGREMOVED, "TemplateRemoved", (DWORD)_T(DEFAULT_TEMPLATE_REMOVED)},
+ {&opts.bLogEnable, CONTROL_CHECKBOX, IDC_LOG, "LogEnable", FALSE},
+#ifdef UNICODE
+ {&opts.bLogAscii, CONTROL_CHECKBOX, IDC_LOGASCII, "LogAscii", FALSE},
+#endif
+ {&opts.log, CONTROL_TEXT, IDC_LOGTEXT, "LogTemplate", (DWORD)_T(DEFAULT_TEMPLATE_LOG)}
+};
+
+static OptPageControl popupsControls[] = {
+ {&puopts.bEnable, CONTROL_CHECKBOX, IDC_POPUPS, "PopupsEnable", TRUE},
+ {&puopts.bOnConnect, CONTROL_CHECKBOX, IDC_ONCONNECT, "PopupsOnConnect", FALSE},
+ {&puopts.bIfChanged, CONTROL_CHECKBOX, IDC_IFCHANGED, "PopupsIfChanged", TRUE},
+ {&puopts.bIgnoreRemove, CONTROL_CHECKBOX, IDC_PUIGNOREREMOVE, "PopupsIgnoreRemove", FALSE},
+#ifdef CUSTOMBUILD_OSDSUPPORT
+ {&puopts.bUseOSD, CONTROL_CHECKBOX, IDC_USEOSD, "PopupsUseOSD", TRUE},
+#endif
+ {&puopts.bColorType, CONTROL_RADIO, IDC_COLORFROMPU, "PopupsColorType", POPUP_COLOR_DEFAULT, POPUP_COLOR_DEFAULT},
+ {NULL, CONTROL_RADIO, IDC_COLORWINDOWS, "PopupsColorType", POPUP_COLOR_DEFAULT, POPUP_COLOR_WINDOWS},
+ {NULL, CONTROL_RADIO, IDC_COLORCUSTOM, "PopupsColorType", POPUP_COLOR_DEFAULT, POPUP_COLOR_CUSTOM},
+ {&puopts.colBack, CONTROL_COLOR, IDC_PUBGCOLOR, "PopupsBkColor", RGB(201,125,234)},
+ {&puopts.colText, CONTROL_COLOR, IDC_PUTEXTCOLOR, "PopupsTextColor", RGB(0,0,0)},
+ {&puopts.bDelayType, CONTROL_RADIO, IDC_DELAYFROMPU, "PopupsDelayType", POPUP_DELAY_DEFAULT, POPUP_DELAY_DEFAULT},
+ {NULL, CONTROL_RADIO, IDC_DELAYCUSTOM, "PopupsDelayType", POPUP_DELAY_DEFAULT, POPUP_DELAY_CUSTOM},
+ {NULL, CONTROL_RADIO, IDC_DELAYPERMANENT, "PopupsDelayType", POPUP_DELAY_DEFAULT, POPUP_DELAY_PERMANENT},
+ {&puopts.dDelay, CONTROL_SPIN, IDC_DELAY, "PopupsDelay", 10, IDC_DELAY_SPIN, (WORD)1, (WORD)255},
+ {&puopts.LeftClickAction, CONTROL_COMBO, IDC_LEFTACTION, "PopupsLeftClick", POPUP_ACTION_INFO},
+ {&puopts.RightClickAction, CONTROL_COMBO, IDC_RIGHTACTION, "PopupsRightClick", POPUP_ACTION_CLOSE},
+ {&puopts.text, CONTROL_TEXT, IDC_POPUPTEXT, "PopupsTemplate", (DWORD)_T(DEFAULT_TEMPLATE_POPUP)}
+};
+/*
+static UINT popupsExpertControls[] = {
+ IDC_ONCONNECT, IDC_IFCHANGED, IDC_PUIGNOREREMOVE,
+ IDC_EXPERT, IDC_PUBGCOLOR, IDC_PUTEXTCOLOR, IDC_COLORTYPE,
+ IDC_DELAYFROMPU, IDC_DELAYCUSTOM, IDC_DELAYPERMANENT, IDC_DELAY, IDC_DELAY_SPIN,
+ IDC_RIGHTACTION, IDC_LEFTACTION,
+ IDC_POPUPTEXT, IDC_PREVIEW
+};
+*/
+static OptPageControl fileControls[] = {
+ {&opts.logfile, CONTROL_TEXT, IDC_LOGFILE, "LogFile", (DWORD)_T(DEFAULT_LOG_FILENAME), 0, 0, MAX_PATH},
+ {&opts.listbkimage, CONTROL_TEXT, IDC_BGIMGFILE, "ListBkImage", (DWORD)_T(DEFAULT_BGIMAGE_FILENAME), 0, 0, MAX_PATH}
+};
+
+#define MAX_REGS(_A_) ( sizeof(_A_) / sizeof(_A_[0]) )
+
+void LoadOptions() {
+ TCHAR temp[MAX_PATH];
+
+ LoadOpts(optionsControls, MAX_REGS(optionsControls), MODULE_NAME);
+ LoadOpts(advancedControls, MAX_REGS(advancedControls), MODULE_NAME);
+ LoadOpts(popupsControls, MAX_REGS(popupsControls), MODULE_NAME);
+
+ LoadOpts(fileControls, MAX_REGS(fileControls), MODULE_NAME);
+ if (opts.listbkimage != NULL && opts.listbkimage[0] != _T('\0'))
+ {
+ CallService(MS_UTILS_PATHTOABSOLUTET, (WPARAM)opts.listbkimage, (LPARAM)temp);
+ lstrcpyn(opts.listbkimage, temp, MAX_PATH);
+ }
+ if (opts.logfile != NULL && opts.logfile[0] != _T('\0'))
+ {
+ CallService(MS_UTILS_PATHTOABSOLUTET, (WPARAM)opts.logfile, (LPARAM)temp);
+ lstrcpyn(opts.logfile, temp, MAX_PATH);
+ }
+}
+
+static BOOL CALLBACK OptionsDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) {
+ BOOL ret = SaveOptsDlgProc(optionsControls, MAX_REGS(optionsControls), MODULE_NAME, hwndDlg, msg, wParam, lParam);
+ switch (msg)
+ {
+ case WM_INITDIALOG:
+ SetDlgItemText(hwndDlg, IDC_BGIMGFILE, opts.listbkimage);
+ SendMessage(hwndDlg, WM_USER + 10, 0, 0);
+ break;
+ case WM_USER + 10:
+ {
+ BOOL enabled = IsDlgButtonChecked(hwndDlg, IDC_USEBGIMG);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_LISTBGCOLOR), !enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_BGIMGFILE), enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_BGIMGBROWSE), enabled);
+ break;
+ }
+ case WM_COMMAND:
+ switch (LOWORD(wParam))
+ {
+ case IDC_USEBGIMG:
+ SendMessage(hwndDlg, WM_USER + 10, 0, 0);
+ break;
+ case IDC_BGIMGBROWSE:
+ {
+ OPENFILENAME ofn;
+ TCHAR filepath[MAX_PATH] = _T("");
+ char filter[512] = "";
+#ifdef UNICODE
+ WCHAR filterW[512] = L"";
+ int i;
+#endif
+
+ GetDlgItemText(hwndDlg, IDC_BGIMGFILE, filepath, sizeof(filepath));
+ CallService(MS_UTILS_GETBITMAPFILTERSTRINGS, sizeof(filter), (LPARAM)filter);
+#ifdef UNICODE
+ for (i = 0; i < 512; i++)
+ filterW[i] = filter[i];
+#endif
+
+ ZeroMemory(&ofn, sizeof(ofn));
+ ofn.lStructSize = sizeof(ofn);//OPENFILENAME_SIZE_VERSION_400;
+ ofn.hwndOwner = hwndDlg;
+#ifdef UNICODE
+ ofn.lpstrFilter = filterW;
+#else
+ ofn.lpstrFilter = filter;
+#endif
+ ofn.lpstrFile = filepath;
+ ofn.Flags = OFN_EXPLORER | OFN_FILEMUSTEXIST;
+ ofn.nMaxFile = MAX_PATH;
+ ofn.lpstrDefExt = _T("bmp");
+
+ if (GetOpenFileName(&ofn))
+ {
+ SetDlgItemText(hwndDlg, IDC_BGIMGFILE, filepath);
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ }
+
+ break;
+ }
+ case IDC_BGIMGFILE:
+ if (HIWORD(wParam) != EN_CHANGE || (HWND)lParam != GetFocus())
+ return 0;
+// else
+// SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ break;
+ }
+ break;
+ case WM_NOTIFY:
+ {
+ LPNMHDR lpnmhdr = (LPNMHDR)lParam;
+
+ if (lpnmhdr->idFrom == 0 && lpnmhdr->code == PSN_APPLY)
+ {
+ TCHAR temp[MAX_PATH]; temp[0] = _T('\0');
+ GetDlgItemText(hwndDlg, IDC_BGIMGFILE, opts.listbkimage, MAX_PATH);
+ if (opts.listbkimage != NULL && opts.listbkimage[0] != _T('\0'))
+ CallService(MS_UTILS_PATHTORELATIVET, (WPARAM)opts.listbkimage, (LPARAM)temp);
+ DBWriteContactSettingTString(NULL, MODULE_NAME, "ListBkImage", temp);
+ }
+ break;
+ }
+ }
+ return ret;
+}
+
+static BOOL CALLBACK AdvancedDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) {
+ BOOL ret = SaveOptsDlgProc(advancedControls, MAX_REGS(advancedControls), MODULE_NAME, hwndDlg, msg, wParam, lParam);
+ switch (msg)
+ {
+ case WM_INITDIALOG:
+ {
+ TCHAR str[1024];str[0] = _T('\0');
+
+ SetDlgItemText(hwndDlg, IDC_LOGFILE, opts.logfile);
+#ifdef UNICODE
+ ShowWindow(GetDlgItem(hwndDlg, IDC_LOGASCII), SW_HIDE);
+#endif
+ SendMessage(hwndDlg, WM_USER + 10, 0, 0);
+ SendMessage(hwndDlg, WM_USER + 11, 0, 0);
+ SendMessage(hwndDlg, WM_USER + 12, 0, 0);
+
+ lstrcpy(str, _T("%n\tNew Status Message\r\n%o\tOld Status Message\r\n%c\tCustom Nickname\r\n\\n\tline break\r\n\\t\ttab stop"));
+ SetDlgItemText(hwndDlg, IDC_VARS1, str);
+ lstrcpy(str, _T("%D/%M/%Y\tDay/Month/Year\r\n%H:%m:%s\tTime (in 24h format)\r\n%h:%m:%s %a\t(in 12h format)"));
+ SetDlgItemText(hwndDlg, IDC_VARS2, str);
+ break;
+ }
+ case WM_USER + 10:
+ {
+ BOOL enabled = IsDlgButtonChecked(hwndDlg, IDC_MESSAGEWND);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_MSGCHANGED), enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_MSGREMOVED), enabled);
+ break;
+ }
+ case WM_USER + 11:
+ {
+ BOOL enabled = IsDlgButtonChecked(hwndDlg, IDC_HISTORY);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_HISTORYMAX), enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_HISTORYMAX_SPIN), enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_HISTORYTEXT), enabled);
+ break;
+ }
+ case WM_USER + 12:
+ {
+ BOOL enabled = IsDlgButtonChecked(hwndDlg, IDC_LOG);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_LOGTEXT), enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_LOGFILE), enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_LOGFILEBROWSE), enabled);
+#ifdef UNICODE
+ EnableWindow(GetDlgItem(hwndDlg, IDC_LOGASCII), enabled);
+#endif
+ break;
+ }
+ case WM_COMMAND:
+ if ((LOWORD(wParam) == IDC_LOGFILE
+ || LOWORD(wParam) == IDC_VARS1
+ || LOWORD(wParam) == IDC_VARS2)
+ && (HIWORD(wParam) != EN_CHANGE || (HWND)lParam != GetFocus()))
+ return 0;
+ switch (LOWORD(wParam))
+ {
+ case IDC_MESSAGEWND:
+ SendMessage(hwndDlg, WM_USER + 10, 0, 0);
+ break;
+ case IDC_HISTORY:
+ SendMessage(hwndDlg, WM_USER + 11, 0, 0);
+ break;
+ case IDC_LOG:
+ SendMessage(hwndDlg, WM_USER + 12, 0, 0);
+ break;
+ case IDC_HISTORYCLEAR:
+ ClearAllHistory();
+ return 0;
+ case IDC_LOGFILEBROWSE:
+ {
+ OPENFILENAME ofn;
+ TCHAR filepath[MAX_PATH] = _T("");
+
+ GetDlgItemText(hwndDlg, IDC_LOGFILE, filepath, sizeof(filepath));
+
+ ZeroMemory(&ofn, sizeof(ofn));
+ ofn.lStructSize = sizeof(ofn);//OPENFILENAME_SIZE_VERSION_400;
+ ofn.hwndOwner = hwndDlg;
+ ofn.lpstrFilter = _T("Text Files (*.txt)\0*.txt\0Log Files (*.log)\0*.log\0All Files (*.*)\0*.*\0");
+ ofn.lpstrFile = filepath;
+ ofn.Flags = OFN_EXPLORER | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY;
+ ofn.nMaxFile = MAX_PATH;
+ ofn.lpstrDefExt = _T("txt");
+
+ if (GetSaveFileName(&ofn))
+ {
+ SetDlgItemText(hwndDlg, IDC_LOGFILE, filepath);
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ }
+
+ break;
+ }
+ }
+ break;
+ case WM_NOTIFY:
+ {
+ LPNMHDR lpnmhdr = (LPNMHDR)lParam;
+
+ if (lpnmhdr->idFrom == 0 && lpnmhdr->code == PSN_APPLY)
+ {
+ TCHAR temp[MAX_PATH]; temp[0] = _T('\0');
+ GetDlgItemText(hwndDlg, IDC_LOGFILE, opts.logfile, MAX_PATH);
+ if (opts.logfile != NULL && opts.logfile[0] != _T('\0'))
+ CallService(MS_UTILS_PATHTORELATIVET, (WPARAM)opts.logfile, (LPARAM)temp);
+ DBWriteContactSettingTString(NULL, MODULE_NAME, "LogFile", temp);
+ }
+ break;
+ }
+ }
+
+ return ret;
+}
+
+static BOOL CALLBACK PopupOptionsDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) {
+ switch (msg)
+ {
+ case WM_INITDIALOG:
+ {
+ BOOL ret;
+
+ SendDlgItemMessage(hwndDlg, IDC_RIGHTACTION, CB_ADDSTRING, 0, (LONG) TranslateT("Do nothing"));
+ SendDlgItemMessage(hwndDlg, IDC_RIGHTACTION, CB_ADDSTRING, 0, (LONG) TranslateT("Close popup"));
+ SendDlgItemMessage(hwndDlg, IDC_RIGHTACTION, CB_ADDSTRING, 0, (LONG) TranslateT("Open message window"));
+ SendDlgItemMessage(hwndDlg, IDC_RIGHTACTION, CB_ADDSTRING, 0, (LONG) TranslateT("Open contact menu"));
+ SendDlgItemMessage(hwndDlg, IDC_RIGHTACTION, CB_ADDSTRING, 0, (LONG) TranslateT("Open contact details"));
+ SendDlgItemMessage(hwndDlg, IDC_RIGHTACTION, CB_ADDSTRING, 0, (LONG) TranslateT("View status message history"));
+
+ SendDlgItemMessage(hwndDlg, IDC_LEFTACTION, CB_ADDSTRING, 0, (LONG) TranslateT("Do nothing"));
+ SendDlgItemMessage(hwndDlg, IDC_LEFTACTION, CB_ADDSTRING, 0, (LONG) TranslateT("Close popup"));
+ SendDlgItemMessage(hwndDlg, IDC_LEFTACTION, CB_ADDSTRING, 0, (LONG) TranslateT("Open message window"));
+ SendDlgItemMessage(hwndDlg, IDC_LEFTACTION, CB_ADDSTRING, 0, (LONG) TranslateT("Open contact menu"));
+ SendDlgItemMessage(hwndDlg, IDC_LEFTACTION, CB_ADDSTRING, 0, (LONG) TranslateT("Open contact details"));
+ SendDlgItemMessage(hwndDlg, IDC_LEFTACTION, CB_ADDSTRING, 0, (LONG) TranslateT("View status message history"));
+
+ // Needs to be called here in this case
+ ret = SaveOptsDlgProc(popupsControls, MAX_REGS(popupsControls), MODULE_NAME, hwndDlg, msg, wParam, lParam);
+
+ SendMessage(hwndDlg, WM_USER + 10, 0, 0);
+ SendMessage(hwndDlg, WM_USER + 11, 0, 0);
+ SendMessage(hwndDlg, WM_USER + 12, 0, 0);
+#ifdef CUSTOMBUILD_OSDSUPPORT
+ SendMessage(hwndDlg, WM_USER + 13, 0, 0);
+#endif
+
+ return ret;
+ }
+ case WM_USER + 10:
+ EnableWindow(GetDlgItem(hwndDlg, IDC_PUBGCOLOR), IsDlgButtonChecked(hwndDlg, IDC_COLORCUSTOM));
+ EnableWindow(GetDlgItem(hwndDlg, IDC_PUTEXTCOLOR), IsDlgButtonChecked(hwndDlg, IDC_COLORCUSTOM));
+ break;
+ case WM_USER + 11:
+ EnableWindow(GetDlgItem(hwndDlg, IDC_DELAY), IsDlgButtonChecked(hwndDlg, IDC_DELAYCUSTOM));
+ EnableWindow(GetDlgItem(hwndDlg, IDC_DELAY_SPIN), IsDlgButtonChecked(hwndDlg, IDC_DELAYCUSTOM));
+ break;
+ case WM_USER + 12:
+ EnableWindow(GetDlgItem(hwndDlg, IDC_IFCHANGED), IsDlgButtonChecked(hwndDlg, IDC_ONCONNECT));
+ break;
+#ifdef CUSTOMBUILD_OSDSUPPORT
+ case WM_USER + 13:
+ {
+ BOOL state = !IsDlgButtonChecked(hwndDlg, IDC_USEOSD);
+ if (state) SendMessage(hwndDlg, WM_USER + 10, 0, 0);
+ else
+ {
+ EnableWindow(GetDlgItem(hwndDlg, IDC_PUBGCOLOR), state);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_PUTEXTCOLOR), state);
+ }
+ EnableWindow(GetDlgItem(hwndDlg, IDC_COLORTYPE), state);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_LEFTCLICK), state);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_RIGHTCLICK), state);
+ break;
+ }
+#endif
+ case WM_COMMAND:
+ {
+ if ((LOWORD(wParam) == IDC_POPUPTEXT || LOWORD(wParam) == IDC_DELAY)
+ && (HIWORD(wParam) != EN_CHANGE || (HWND)lParam != GetFocus()))
+ return 0;
+ switch (LOWORD(wParam)) //(HIWORD(wParam) == BN_CLICKED)
+ {
+#ifdef CUSTOMBUILD_OSDSUPPORT
+ case IDC_USEOSD:
+ SendMessage(hwndDlg, WM_USER + 13, 0, 0);
+ break;
+#endif
+ case IDC_COLORFROMPU:
+ case IDC_COLORWINDOWS:
+ case IDC_COLORCUSTOM:
+ SendMessage(hwndDlg, WM_USER + 10, 0, 0);
+ break;
+ case IDC_DELAYFROMPU:
+ case IDC_DELAYPERMANENT:
+ case IDC_DELAYCUSTOM:
+ SendMessage(hwndDlg, WM_USER + 11, 0, 0);
+ break;
+ case IDC_ONCONNECT:
+ SendMessage(hwndDlg, WM_USER + 12, 0, 0);
+ break;
+ case IDC_PREVIEW:
+ {
+ STATUSMSGINFO temp_smi;
+ SMCNOTIFY_PUOPTIONS temp_puo;
+
+ ZeroMemory(&temp_smi, sizeof(temp_smi));
+ temp_smi.hContact = NULL;
+ temp_smi.cust = (TCHAR*)mir_alloc0(8 * sizeof(TCHAR));
+ lstrcpy(temp_smi.cust, TranslateT("Contact"));
+ temp_smi.oldstatusmsg = (TCHAR*)mir_alloc0(19 * sizeof(TCHAR));
+ lstrcpy(temp_smi.oldstatusmsg, TranslateT("Old status message"));
+ temp_smi.newstatusmsg = (TCHAR*)mir_alloc0(19 * sizeof(TCHAR));
+ lstrcpy(temp_smi.newstatusmsg, TranslateT("New status message"));
+
+ ZeroMemory(&temp_puo, sizeof(temp_puo));
+ if (IsDlgButtonChecked(hwndDlg, IDC_DELAYFROMPU))
+ temp_puo.bDelayType = POPUP_DELAY_DEFAULT;
+ else if (IsDlgButtonChecked(hwndDlg, IDC_DELAYCUSTOM))
+ {
+ temp_puo.bDelayType = POPUP_DELAY_CUSTOM;
+ temp_puo.dDelay = GetDlgItemInt(hwndDlg,IDC_DELAY, NULL, FALSE);
+ }
+ else if (IsDlgButtonChecked(hwndDlg, IDC_DELAYPERMANENT))
+ temp_puo.bDelayType = POPUP_DELAY_PERMANENT;
+ if (IsDlgButtonChecked(hwndDlg, IDC_COLORFROMPU))
+ temp_puo.bColorType = POPUP_COLOR_DEFAULT;
+ else if (IsDlgButtonChecked(hwndDlg, IDC_COLORWINDOWS))
+ temp_puo.bColorType = POPUP_COLOR_WINDOWS;
+ else if (IsDlgButtonChecked(hwndDlg, IDC_COLORCUSTOM))
+ {
+ temp_puo.bColorType = POPUP_COLOR_CUSTOM;
+ temp_puo.colBack = SendDlgItemMessage(hwndDlg,IDC_PUBGCOLOR,CPM_GETCOLOUR,0,0);
+ temp_puo.colText = SendDlgItemMessage(hwndDlg,IDC_PUTEXTCOLOR,CPM_GETCOLOUR,0,0);
+ }
+ GetDlgItemText(hwndDlg, IDC_POPUPTEXT, temp_puo.text, TEMPLATEMAXLEN);
+
+ PopupNotify(&temp_smi, &temp_puo);
+ MIR_FREE(temp_smi.cust);
+ MIR_FREE(temp_smi.oldstatusmsg);
+ MIR_FREE(temp_smi.newstatusmsg);
+ return 0;
+ }
+ }
+ break;
+ }
+ }
+
+ return SaveOptsDlgProc(popupsControls, MAX_REGS(popupsControls), MODULE_NAME, hwndDlg, msg, wParam, lParam);
+}
+
+extern int OptionsInit(WPARAM wParam, LPARAM lParam) {
+ OPTIONSDIALOGPAGE odp;
+
+ ZeroMemory(&odp, sizeof(odp));
+ odp.cbSize = sizeof(odp);
+ odp.position = 0;
+ odp.hInstance = hInst;
+ odp.flags = ODPF_BOLDGROUPS | ODPF_TCHAR;
+
+ if(ServiceExists(MS_POPUP_ADDPOPUPEX)
+#ifdef UNICODE
+ || ServiceExists(MS_POPUP_ADDPOPUPW)
+#endif
+ )
+ {
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_POPUP);
+ odp.ptszGroup = TranslateT("Popups");
+ odp.ptszTitle = TranslateT(PLUGIN_NAME);
+ odp.pfnDlgProc = PopupOptionsDlgProc;
+// odp.expertOnlyControls = popupsExpertControls;
+// odp.nExpertOnlyControls = sizeof(popupsExpertControls);
+ CallService(MS_OPT_ADDPAGE, wParam, (LPARAM)&odp);
+ }
+
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPTIONS);
+ odp.ptszGroup = TranslateT("History");
+ odp.ptszTitle = TranslateT(PLUGIN_NAME);
+ odp.ptszTab = TranslateT("General");
+ odp.pfnDlgProc = OptionsDlgProc;
+// odp.expertOnlyControls = optionsExpertControls;
+// odp.nExpertOnlyControls = sizeof(optionsExpertControls);
+ CallService(MS_OPT_ADDPAGE, wParam, (LPARAM)&odp);
+
+ odp.flags = ODPF_BOLDGROUPS | ODPF_TCHAR | ODPF_EXPERTONLY;
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_ADVANCED);
+ odp.ptszTab = TranslateT("Advanced");
+ odp.pfnDlgProc = AdvancedDlgProc;
+// odp.expertOnlyControls = optionsExpertControls;
+// odp.nExpertOnlyControls = sizeof(optionsExpertControls);
+ CallService(MS_OPT_ADDPAGE, wParam, (LPARAM)&odp);
+
+ odp.flags = ODPF_BOLDGROUPS | ODPF_TCHAR;
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_IGNORE);
+ odp.ptszTab = TranslateT("Ignore");
+ odp.pfnDlgProc = IgnoreDlgProc;
+// odp.expertOnlyControls = optionsExpertControls;
+// odp.nExpertOnlyControls = sizeof(optionsExpertControls);
+ CallService(MS_OPT_ADDPAGE, wParam, (LPARAM)&odp);
+
+ return 0;
+}
diff --git a/Plugins/smcnotify/options.h b/Plugins/smcnotify/options.h
new file mode 100644
index 0000000..c08ab68
--- /dev/null
+++ b/Plugins/smcnotify/options.h
@@ -0,0 +1,104 @@
+/*
+Status Message Change Notify plugin for Miranda IM.
+
+Copyright © 2004-2005 NoName
+Copyright © 2005-2006 Daniel Vijge, Tomasz S³otwiñski, Ricardo Pescuma Domenecci
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef __SMCNOTIFY_OPTIONS_H
+#define __SMCNOTIFY_OPTIONS_H
+
+#define TEMPLATEMAXLEN 1024
+
+typedef struct {
+ BYTE bIgnoreRemove;
+ BYTE bLogEnable;
+#ifdef UNICODE
+ BYTE bLogAscii;
+#endif
+ BYTE bHistoryEnable;
+ WORD dHistoryMax;
+ BYTE bDBEnable;
+ BYTE bListUseBkImage;
+ COLORREF colListBack;
+ COLORREF colListText;
+ //strings
+ TCHAR msgremoved[TEMPLATEMAXLEN];
+ TCHAR msgchanged[TEMPLATEMAXLEN];
+ TCHAR history[TEMPLATEMAXLEN];
+ TCHAR log[TEMPLATEMAXLEN];
+ TCHAR logfile[MAX_PATH];
+ TCHAR listbkimage[MAX_PATH];
+} SMCNOTIFY_OPTIONS;
+
+SMCNOTIFY_OPTIONS opts;
+
+typedef struct {
+ BYTE bEnable;
+ BYTE bOnConnect;
+ BYTE bIfChanged;
+ BYTE bIgnoreRemove;
+#ifdef CUSTOMBUILD_OSDSUPPORT
+ BYTE bUseOSD;
+#endif
+ WORD bColorType;
+ COLORREF colBack;
+ COLORREF colText;
+ WORD dDelay;
+ WORD bDelayType;
+ WORD LeftClickAction;
+ WORD RightClickAction;
+ TCHAR text[TEMPLATEMAXLEN];
+} SMCNOTIFY_PUOPTIONS;
+
+SMCNOTIFY_PUOPTIONS puopts;
+
+void ClearAllHistory();
+
+BOOL AllowProtocol(const char *proto);
+
+void LoadOptions(void);
+int OptionsInit(WPARAM wParam, LPARAM lParam);
+int UserInfoInit(WPARAM wParam, LPARAM lParam);
+
+BOOL CALLBACK IgnoreDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam);
+
+#define POPUP_DELAY_DEFAULT 0
+#define POPUP_DELAY_CUSTOM 1
+#define POPUP_DELAY_PERMANENT 2
+
+#define POPUP_COLOR_DEFAULT 0
+#define POPUP_COLOR_WINDOWS 1
+#define POPUP_COLOR_CUSTOM 2
+
+#define POPUP_ACTION_NOTHING 0
+#define POPUP_ACTION_CLOSE 1
+#define POPUP_ACTION_MESSAGE 2
+#define POPUP_ACTION_MENU 3
+#define POPUP_ACTION_INFO 4
+#define POPUP_ACTION_HISTORY 5
+
+#define DEFAULT_TEMPLATE_POPUP "changed his/her status message to %n"
+#define DEFAULT_TEMPLATE_HISTORY "[%Y/%M/%D %h:%m %a]\\n%n"
+#define DEFAULT_TEMPLATE_REMOVED "removes his/her status message"
+#define DEFAULT_TEMPLATE_CHANGED "changes his/her status message to %n"
+#define DEFAULT_TEMPLATE_LOG "[%Y/%M/%D %h:%m %a] %c: %n"
+
+#define DEFAULT_BGIMAGE_FILENAME "SMCNotify.bmp"
+#define DEFAULT_LOG_FILENAME "SMCNotify.txt"
+
+#endif // __SMCNOTIFY_OPTIONS_H
diff --git a/Plugins/smcnotify/popup.c b/Plugins/smcnotify/popup.c
new file mode 100644
index 0000000..d9eef61
--- /dev/null
+++ b/Plugins/smcnotify/popup.c
@@ -0,0 +1,203 @@
+/*
+Status Message Change Notify plugin for Miranda IM.
+
+Copyright © 2004-2005 NoName
+Copyright © 2005-2006 Daniel Vijge, Tomasz S³otwiñski, Ricardo Pescuma Domenecci
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "commonheaders.h"
+
+
+static LRESULT CALLBACK PopupDlgProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
+
+
+void PopupNotify(STATUSMSGINFO *smi, SMCNOTIFY_PUOPTIONS *puo) {
+ POPUPDATAT ppd;
+ TCHAR *str = GetStr(smi, puo->text);
+
+#ifdef CUSTOMBUILD_OSDSUPPORT
+ if (ServiceExists("OSD/Announce") && puo->bUseOSD)
+ {
+ int timerOSD = options.bInfiniteDelay ? 2147483647 : (int)(options.dSec * 1000);
+ //char *szOSDText = (char*)smi->cust;
+ //lstrcat(szOSDText, options.popuptext);
+ CallService("OSD/Announce", (WPARAM)str, timerOSD);
+ MIR_FREE(str);
+ return;
+ }
+#endif
+
+ ZeroMemory(&ppd, sizeof(ppd));
+ ppd.lchContact = smi->hContact;
+ ppd.lchIcon = LoadSkinnedProtoIcon(smi->proto, DBGetContactSettingWord(smi->hContact, smi->proto, "Status", ID_STATUS_ONLINE));
+ lstrcpyn(ppd.lptzContactName, smi->cust, MAX_CONTACTNAME);
+ lstrcpyn(ppd.lptzText, str, MAX_SECONDLINE);
+ if (puo->bColorType == POPUP_COLOR_DEFAULT)
+ {
+ ppd.colorBack = 0;
+ ppd.colorText = 0;
+ }
+ else if (puo->bColorType == POPUP_COLOR_WINDOWS)
+ {
+ ppd.colorBack = GetSysColor(COLOR_BTNFACE);
+ ppd.colorText = GetSysColor(COLOR_WINDOWTEXT);
+ }
+ else if (puo->bColorType == POPUP_COLOR_CUSTOM)
+ {
+ ppd.colorBack = puo->colBack;
+ ppd.colorText = puo->colText;
+ }
+ ppd.PluginWindowProc = (WNDPROC)PopupDlgProc;
+ ppd.PluginData = NULL;
+ if (puo->bDelayType == POPUP_DELAY_DEFAULT)
+ ppd.iSeconds = 0;
+ else if (puo->bDelayType == POPUP_DELAY_CUSTOM)
+ ppd.iSeconds = puo->dDelay;
+ else if (puo->bDelayType == POPUP_DELAY_PERMANENT)
+ ppd.iSeconds = -1;
+ CallService(MS_POPUP_ADDPOPUPT, (WPARAM)&ppd, 0);
+
+ MIR_FREE(str);
+ return;
+}
+
+// return TRUE if timeout is over
+static BOOL TimeoutCheck(HANDLE hContact, const char *module, const char *setting) {
+ if (DBGetContactSettingDword(hContact, module, setting, 0) == 1) return TRUE;
+ if ((GetTickCount() - DBGetContactSettingDword(hContact, module, setting, 0)) > TMR_CONNECTIONTIMEOUT)
+ {
+ DBWriteContactSettingDword(hContact, module, setting, 1);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+void PopupCheck(STATUSMSGINFO *smi) {
+ BOOL ret = TRUE;
+
+ if (puopts.bOnConnect && !puopts.bIfChanged && (smi->compare == 0))
+ smi->ignore = SMII_ALL;
+
+ if (puopts.bIgnoreRemove && (smi->compare == 2))
+ ret = FALSE;
+
+ else if (!TimeoutCheck(NULL, MODULE_NAME, smi->proto))
+ ret = (puopts.bOnConnect && (smi->compare || !puopts.bIfChanged));
+
+ else if ((BOOL)DBGetContactSettingByte(NULL, MODULE_NAME, "IgnoreAfterStatusChange", 0))
+ ret = TimeoutCheck(smi->hContact, "UserOnline", "LastStatusChange");
+
+ if (ret)
+ {
+ PopupNotify(smi, &puopts);
+ SkinPlaySound("smcnotify");
+ }
+
+ return;
+}
+
+static LRESULT CALLBACK PopupDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
+ DWORD ID;
+ HANDLE hContact;
+ hContact = PUGetContact(hWnd);
+ switch (msg)
+ {
+ case WM_COMMAND:
+ case WM_CONTEXTMENU:
+ if (msg == WM_COMMAND) // left click
+ ID = puopts.LeftClickAction;
+ else if (msg == WM_CONTEXTMENU) // right click
+ ID = puopts.RightClickAction;
+
+ if (ID == POPUP_ACTION_CLOSE) PUDeletePopUp(hWnd);
+ SendMessage(hPopupWindow, WM_USER + 10 + ID, (WPARAM)hContact, 0);
+ break;
+ }
+
+ return DefWindowProc(hWnd, msg, wParam, lParam);
+}
+
+static LRESULT CALLBACK PopupWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
+ if ((HANDLE)wParam != NULL)
+ {
+ switch (msg - WM_USER - 10)
+ {
+ case POPUP_ACTION_MESSAGE: // open message window
+ CallService(MS_MSG_SENDMESSAGE, wParam, 0);
+ CallService("SRMsg/LaunchMessageWindow", wParam, (LONG)NULL);
+ break;
+ case POPUP_ACTION_MENU: // display contact menu
+ {
+ int retcmd;
+ POINT pt;
+ HMENU hMenu;
+ hMenu = (HMENU)CallService(MS_CLIST_MENUBUILDCONTACT, wParam, 0);
+ GetCursorPos(&pt);
+ retcmd = TrackPopupMenu(hMenu, TPM_NONOTIFY | TPM_RETURNCMD, pt.x, pt.y, 0, hWnd, NULL);
+ CallService(MS_CLIST_MENUPROCESSCOMMAND, MAKEWPARAM(retcmd, MPCF_CONTACTMENU), (LPARAM)wParam);
+ DestroyMenu(hMenu);
+ break;
+ }
+ case POPUP_ACTION_INFO: // display contact detail
+ CallService(MS_USERINFO_SHOWDIALOG, wParam, 0);
+ break;
+ case POPUP_ACTION_HISTORY: // view status message history
+ if (ServiceExists("UserInfo/Reminder/AggrassiveBackup"))
+ DBWriteContactSettingTString(NULL, "UserInfoEx", "LastItem", TranslateT("Status Message History"));
+ else
+ DBWriteContactSettingTString(NULL, "UserInfo", "LastTab", TranslateT("Status Message History"));
+ CallService(MS_USERINFO_SHOWDIALOG, wParam, 0);
+ break;
+ }
+ }
+ if (msg == WM_MEASUREITEM) // Needed by the contact's context menu
+ return CallService(MS_CLIST_MENUMEASUREITEM, wParam, lParam);
+ else if (msg == WM_DRAWITEM) // Needed by the contact's context menu
+ return CallService(MS_CLIST_MENUDRAWITEM, wParam, lParam);
+
+ return DefWindowProc(hWnd, msg, wParam, lParam);
+}
+
+extern void InitPopups(void) {
+ if(ServiceExists(MS_POPUP_ADDPOPUPEX)
+#ifdef UNICODE
+ || ServiceExists(MS_POPUP_ADDPOPUPW)
+#endif
+ )
+ {
+ PopupActive = TRUE;
+
+ //Window needed for popup commands
+ hPopupWindow = CreateWindowEx(WS_EX_TOOLWINDOW, _T("static"), _T(MODULE_NAME) _T("_PopupWindow"),
+ 0, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, HWND_DESKTOP,
+ NULL, hInst, NULL);
+ SetWindowLong(hPopupWindow, GWL_WNDPROC, (LONG)(WNDPROC)PopupWndProc);
+
+ }
+ else PopupActive = FALSE;
+
+ return;
+}
+
+extern void DeinitPopups(void) {
+ if (hPopupWindow)
+ {
+ SendMessage(hPopupWindow, WM_CLOSE, 0, 0);
+ }
+
+ return;
+}
diff --git a/Plugins/smcnotify/popup.h b/Plugins/smcnotify/popup.h
new file mode 100644
index 0000000..7a7b100
--- /dev/null
+++ b/Plugins/smcnotify/popup.h
@@ -0,0 +1,37 @@
+/*
+Status Message Change Notify plugin for Miranda IM.
+
+Copyright © 2004-2005 NoName
+Copyright © 2005-2006 Daniel Vijge, Tomasz S³otwiñski, Ricardo Pescuma Domenecci
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef __SMCNOTIFY_POPUP_H
+#define __SMCNOTIFY_POPUP_H
+
+
+BOOL PopupActive;
+HANDLE hPopupWindow;
+
+void PopupNotify(STATUSMSGINFO *smi, SMCNOTIFY_PUOPTIONS *puo);
+void PopupCheck(STATUSMSGINFO *smi);
+
+void InitPopups(void);
+void DeinitPopups(void);
+
+#define TMR_CONNECTIONTIMEOUT 6000
+
+#endif // __SMCNOTIFY_POPUP_H
diff --git a/Plugins/smcnotify/res/history.ico b/Plugins/smcnotify/res/history.ico
new file mode 100644
index 0000000..d5be5c7
--- /dev/null
+++ b/Plugins/smcnotify/res/history.ico
Binary files differ
diff --git a/Plugins/smcnotify/res/list.ico b/Plugins/smcnotify/res/list.ico
new file mode 100644
index 0000000..208563a
--- /dev/null
+++ b/Plugins/smcnotify/res/list.ico
Binary files differ
diff --git a/Plugins/smcnotify/res/log.ico b/Plugins/smcnotify/res/log.ico
new file mode 100644
index 0000000..035e779
--- /dev/null
+++ b/Plugins/smcnotify/res/log.ico
Binary files differ
diff --git a/Plugins/smcnotify/res/popup.ico b/Plugins/smcnotify/res/popup.ico
new file mode 100644
index 0000000..57a1a25
--- /dev/null
+++ b/Plugins/smcnotify/res/popup.ico
Binary files differ
diff --git a/Plugins/smcnotify/res/popup_no.ico b/Plugins/smcnotify/res/popup_no.ico
new file mode 100644
index 0000000..16bef45
--- /dev/null
+++ b/Plugins/smcnotify/res/popup_no.ico
Binary files differ
diff --git a/Plugins/smcnotify/res/url.ico b/Plugins/smcnotify/res/url.ico
new file mode 100644
index 0000000..a0fcf7b
--- /dev/null
+++ b/Plugins/smcnotify/res/url.ico
Binary files differ
diff --git a/Plugins/smcnotify/resource.h b/Plugins/smcnotify/resource.h
new file mode 100644
index 0000000..dcbb99f
--- /dev/null
+++ b/Plugins/smcnotify/resource.h
@@ -0,0 +1,91 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Developer Studio generated include file.
+// Used by resource.rc
+//
+#define IDI_POPUP 101
+#define IDI_NOPOPUP 102
+#define IDI_LIST 103
+#define IDI_URL 104
+#define IDI_HISTORY 105
+#define IDI_LOG 106
+
+#define IDD_HISTORY 110
+#define IDD_POPUP 111
+#define IDD_OPTIONS 113
+#define IDD_ADVANCED 114
+#define IDD_IGNORE 115
+#define IDD_LIST 116
+
+#define IDR_PMENU 200
+
+#define IDC_HISTORYLIST 1000
+#define IDC_CHISTORYCLEAR 1001
+#define IDC_CPOPUP 1002
+#define IDC_CLOG 1003
+#define IDC_CHISTORY 1004
+
+#define IDC_POPUPS 1100
+#define IDC_ONCONNECT 1101
+#define IDC_IFCHANGED 1102
+#define IDC_PUIGNOREREMOVE 1103
+//#define IDC_USEOSD 1104
+#define IDC_PUBGCOLOR 1105
+#define IDC_PUTEXTCOLOR 1106
+#define IDC_COLORFROMPU 1107
+#define IDC_COLORWINDOWS 1108
+#define IDC_COLORCUSTOM 1109
+#define IDC_DELAYFROMPU 1110
+#define IDC_DELAYCUSTOM 1111
+#define IDC_DELAY 1112
+#define IDC_DELAY_SPIN 1113
+#define IDC_DELAYPERMANENT 1114
+#define IDC_POPUPTEXT 1115
+#define IDC_LEFTACTION 1116
+#define IDC_RIGHTACTION 1117
+#define IDC_PREVIEW 1118
+
+#define IDC_IGNOREREMOVE 1200
+#define IDC_LISTAUTOREFRESH 1201
+#define IDC_LISTTEXTCOLOR 1202
+#define IDC_LISTBGCOLOR 1203
+#define IDC_USEBGIMG 1204
+#define IDC_BGIMGFILE 1205
+#define IDC_BGIMGBROWSE 1206
+#define IDC_PROTOCOLS 1207
+
+#define IDC_HISTORY 1300
+#define IDC_HISTORYMAX 1301
+#define IDC_HISTORYMAX_SPIN 1302
+#define IDC_HISTORYTEXT 1303
+#define IDC_HISTORYCLEAR 1304
+#define IDC_MESSAGEWND 1305
+#define IDC_MSGREMOVED 1316
+#define IDC_MSGCHANGED 1317
+#define IDC_LOG 1308
+#define IDC_LOGFILE 1309
+#define IDC_LOGFILEBROWSE 1313
+#define IDC_LOGASCII 1314
+#define IDC_LOGTEXT 1310
+#define IDC_VARS1 1311
+#define IDC_VARS2 1312
+
+#define IDC_IGNORELIST IDC_LIST//1400
+#define IDC_IGNOREPOPUP 1401
+#define IDC_IGNOREHISTORY 1402
+#define IDC_IGNORELOG 1403
+
+#define IDC_LIST 1500
+
+#define IDC_STATIC -1
+#define IDC_EXPERT 2000
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 117
+#define _APS_NEXT_COMMAND_VALUE 40004
+#define _APS_NEXT_CONTROL_VALUE 1049
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/Plugins/smcnotify/resource.rc b/Plugins/smcnotify/resource.rc
new file mode 100644
index 0000000..6fa5b74
--- /dev/null
+++ b/Plugins/smcnotify/resource.rc
@@ -0,0 +1,303 @@
+//Microsoft Visual C++ generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "resource.h"
+#include "winresrc.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (U.S.) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page(1252)
+#endif //_WIN32
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Icon
+//
+
+// Icon with lowest ID value placed first to ensure application icon
+// remains consistent on all systems.
+IDI_POPUP ICON DISCARDABLE "res\\popup.ico"
+IDI_NOPOPUP ICON DISCARDABLE "res\\popup_no.ico"
+IDI_LIST ICON DISCARDABLE "res\\list.ico"
+IDI_URL ICON DISCARDABLE "res\\url.ico"
+IDI_HISTORY ICON DISCARDABLE "res\\history.ico"
+IDI_LOG ICON DISCARDABLE "res\\log.ico"
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Dialog
+//
+
+IDD_HISTORY DIALOGEX 0, 0, 224, 154
+STYLE DS_SETFONT | DS_3DLOOK | DS_FIXEDSYS | WS_CHILD
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg", 0, 0, 0x1
+BEGIN
+ PUSHBUTTON "Clear history",IDC_CHISTORYCLEAR,2,138,50,14
+ EDITTEXT IDC_HISTORYLIST,2,4,220,131,ES_MULTILINE | ES_READONLY |
+ WS_VSCROLL | WS_BORDER//,WS_EX_STATICEDGE
+ CONTROL "&P",IDC_CPOPUP,"MButtonClass",WS_TABSTOP,170,138,16,14,
+ 0x18000000L
+ CONTROL "&H",IDC_CHISTORY,"MButtonClass",WS_TABSTOP,188,138,16,14,
+ 0x18000000L
+ CONTROL "&L",IDC_CLOG,"MButtonClass",WS_TABSTOP,206,138,16,14,
+ 0x18000000L
+END
+
+IDD_POPUP DIALOGEX 0, 0, 314, 240
+STYLE DS_SETFONT | DS_3DLOOK | DS_FIXEDSYS | WS_CHILD | WS_VISIBLE
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg", 0, 0, 0x1
+BEGIN
+ GROUPBOX "General",IDC_STATIC,0,0,314,70
+ CONTROL "Enable PopUps",IDC_POPUPS,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,6,12,202,8
+// CONTROL "Use OSD Plugin instead",IDC_USEOSD,"Button",
+// BS_AUTOCHECKBOX | WS_TABSTOP,200,12,100,8
+ CONTROL "Show PopUps when I connect",IDC_ONCONNECT,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,6,26,202,8
+ CONTROL "Only if status message has changed",IDC_IFCHANGED,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,18,40,190,8
+ CONTROL "Ignore empty status messages",IDC_PUIGNOREREMOVE,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,6,54,202,8
+ GROUPBOX "Colours",IDC_STATIC,0,86,152,70
+ CONTROL "From PopUp plugin",IDC_COLORFROMPU,"Button",BS_AUTORADIOBUTTON | WS_GROUP,6,98,140,10
+ CONTROL "Use Windows colours",IDC_COLORWINDOWS,"Button",BS_AUTORADIOBUTTON,6,112,140,10
+ CONTROL "Custom",IDC_COLORCUSTOM,"Button",BS_AUTORADIOBUTTON,6,126,140,10
+ CONTROL "",IDC_PUBGCOLOR,"ColourPicker",WS_TABSTOP,6,140,20,12
+ LTEXT "Background",IDC_STATIC,30,142,45,8
+ CONTROL "",IDC_PUTEXTCOLOR,"ColourPicker",WS_TABSTOP,76,140,20,12
+ LTEXT "Text",IDC_STATIC,100,142,45,8
+ GROUPBOX "Delay",IDC_STATIC,155,86,158,56
+ CONTROL "From PopUp plugin",IDC_DELAYFROMPU,"Button",BS_AUTORADIOBUTTON | WS_GROUP,161,98,71,10
+ CONTROL "Custom",IDC_DELAYCUSTOM,"Button",BS_AUTORADIOBUTTON,161,112,48,10
+ EDITTEXT IDC_DELAY,230,110,30,12,ES_RIGHT | ES_AUTOHSCROLL |
+ ES_NUMBER | NOT WS_BORDER,WS_EX_STATICEDGE
+ CONTROL "",IDC_DELAY_SPIN,"msctls_updown32",UDS_SETBUDDYINT |
+ UDS_ALIGNRIGHT | UDS_ARROWKEYS | UDS_NOTHOUSANDS |
+ UDS_HOTTRACK,260,111,10,11
+ CONTROL "Permanent",IDC_DELAYPERMANENT,"Button",
+ BS_AUTORADIOBUTTON,161,126,127,8
+ GROUPBOX "Text",IDC_STATIC,0,158,152,34
+ EDITTEXT IDC_POPUPTEXT,6,168,140,20,ES_MULTILINE | ES_AUTOHSCROLL |
+ NOT WS_BORDER,WS_EX_STATICEDGE
+ GROUPBOX "Actions",IDC_STATIC,155,146,158,46
+ LTEXT "Left click",IDC_STATIC,161,160,50,8
+ COMBOBOX IDC_LEFTACTION,213,158,95,60,CBS_DROPDOWNLIST |
+ WS_VSCROLL | WS_TABSTOP
+ LTEXT "Right click",IDC_STATIC,161,176,50,8
+ COMBOBOX IDC_RIGHTACTION,213,174,95,60,CBS_DROPDOWNLIST |
+ WS_VSCROLL | WS_TABSTOP
+ PUSHBUTTON "Preview",IDC_PREVIEW,135,210,44,12
+END
+
+IDD_OPTIONS DIALOGEX 0, 0, 311, 231
+STYLE DS_SETFONT | DS_3DLOOK | DS_FIXEDSYS | WS_CHILD | WS_VISIBLE
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg", 0, 0, 0x1
+BEGIN
+ GROUPBOX "Status Message Change Notify",IDC_STATIC,4,4,302,28
+ CONTROL "Do not log empty status messages",
+ IDC_IGNOREREMOVE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,10,18,
+ 200,10
+ GROUPBOX "List",IDC_STATIC,4,33,302,56
+ CONTROL "Refresh list automatically",IDC_LISTAUTOREFRESH,"Button",
+ BS_AUTOCHECKBOX | NOT WS_VISIBLE | WS_TABSTOP,10,44,200,10
+ CONTROL "",IDC_LISTTEXTCOLOR,"ColourPicker",WS_TABSTOP,9,44,18,12
+ CONTROL "",IDC_LISTBGCOLOR,"ColourPicker",WS_TABSTOP,155,44,18,12
+ LTEXT "Text colour",IDC_STATIC,30,46,65,10
+ LTEXT "Background colour",IDC_STATIC,176,46,65,10
+ CONTROL "Use background image",IDC_USEBGIMG,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,10,58,200,10
+ EDITTEXT IDC_BGIMGFILE,10,72,270,12,ES_AUTOHSCROLL | ES_READONLY | WS_BORDER,
+ PUSHBUTTON "...",IDC_BGIMGBROWSE,284,72,16,12,BS_CENTER
+ GROUPBOX "Protocols",IDC_STATIC,4,89,302,94
+ CTEXT "Disable protocols which you don't wish to be notified for",IDC_STATIC,10,100,290,10
+ CONTROL "",IDC_PROTOCOLS,"SysListView32",LVS_REPORT |
+ LVS_SINGLESEL | LVS_SORTASCENDING | LVS_NOCOLUMNHEADER |
+ LVS_NOSORTHEADER | WS_BORDER | WS_TABSTOP,100,114,110,64
+END
+
+IDD_ADVANCED DIALOGEX 0, 0, 311, 231
+STYLE DS_SETFONT | DS_3DLOOK | DS_FIXEDSYS | WS_CHILD | WS_VISIBLE
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg", 0, 0, 0x1
+BEGIN
+ GROUPBOX "History",IDC_STATIC,4,2,302,56
+ CONTROL "Keep history of up to",IDC_HISTORY."Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,10,15,100,10
+ EDITTEXT IDC_HISTORYMAX,112,14,30,12,ES_RIGHT | ES_AUTOHSCROLL |
+ ES_NUMBER | NOT WS_BORDER,WS_EX_STATICEDGE
+ CONTROL "",IDC_HISTORYMAX_SPIN,"msctls_updown32",UDS_SETBUDDYINT |
+ UDS_ALIGNRIGHT | UDS_ARROWKEYS | UDS_NOTHOUSANDS |
+ UDS_HOTTRACK,142,14,10,11
+ LTEXT "status messages",IDC_STATIC,156,15,60,10
+ LTEXT "History template",IDC_STATIC,10,29,50,10
+ EDITTEXT IDC_HISTORYTEXT,75,28,138,24,ES_AUTOHSCROLL | NOT WS_BORDER,
+ WS_EX_STATICEDGE
+ PUSHBUTTON "Clear status messages history for all contacts",IDC_HISTORYCLEAR,
+ 220,14,80,36,BS_MULTILINE
+ GROUPBOX "Message Window",IDC_STATIC,4,58,302,58
+ CONTROL "Show status message changes in message window",
+ IDC_MESSAGEWND,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,10,70,
+ 190,10
+ LTEXT "Message removed",IDC_STATIC,10,85,64,10
+ EDITTEXT IDC_MSGREMOVED,75,84,225,12,ES_AUTOHSCROLL | NOT
+ WS_BORDER,WS_EX_STATICEDGE
+ LTEXT "Message changed",IDC_STATIC,10,99,64,10
+ EDITTEXT IDC_MSGCHANGED,75,98,225,12,ES_AUTOHSCROLL | NOT
+ WS_BORDER,WS_EX_STATICEDGE
+ GROUPBOX "Logging",IDC_STATIC,4,116,302,58
+ CONTROL "Log to",IDC_LOG,"Button",BS_AUTOCHECKBOX |
+ WS_TABSTOP,10,130,64,8
+ EDITTEXT IDC_LOGFILE,75,128,205,12,ES_AUTOHSCROLL | ES_READONLY | WS_BORDER,
+ PUSHBUTTON "...",IDC_LOGFILEBROWSE,284,128,16,12,BS_CENTER
+ CTEXT "You can use %c with log filename to append nicknames",IDC_STATIC,75,144,225,8
+// CTEXT "Use %c in filename if you want to append Contact Custom Nickname",IDC_STATIC,18,144,284,8
+ CONTROL "Log in Ascii",IDC_LOGASCII,"Button",BS_AUTOCHECKBOX | WS_TABSTOP | NOT WS_VISIBLE,18,144,64,8
+ LTEXT "Log template",IDC_STATIC,10,158,50,10
+ EDITTEXT IDC_LOGTEXT,75,156,225,12,ES_AUTOHSCROLL | NOT WS_BORDER,
+ WS_EX_STATICEDGE
+ GROUPBOX "Variables",IDC_STATIC,4,175,302,52
+ EDITTEXT IDC_VARS1,10,184,144,40,ES_MULTILINE | ES_READONLY |
+ NOT WS_BORDER
+ EDITTEXT IDC_VARS2,156,184,144,40,ES_MULTILINE | ES_READONLY |
+ NOT WS_BORDER
+END
+
+IDD_IGNORE DIALOGEX 0, 0, 310, 230
+STYLE DS_SETFONT | DS_3DLOOK | DS_FIXEDSYS | WS_CHILD | WS_VISIBLE
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg", 0, 0, 0x1
+BEGIN
+ CONTROL "",IDC_IGNORELIST,"CListControl",WS_TABSTOP | 0x1d2,4,4,302,170,
+ WS_EX_CLIENTEDGE
+ ICON IDI_POPUP,IDC_IGNOREPOPUP,256,178,12,12,SS_CENTERIMAGE | SS_REALSIZEIMAGE
+ RTEXT "Show PopUps",IDC_STATIC,4,180,238,8
+ ICON IDI_HISTORY,IDC_IGNOREHISTORY,268,192,12,12,SS_CENTERIMAGE | SS_REALSIZEIMAGE
+ RTEXT "Keep History",IDC_STATIC,4,194,238,8
+ ICON IDI_LOG,IDC_IGNORELOG,280,206,12,12,SS_CENTERIMAGE | SS_REALSIZEIMAGE
+ RTEXT "Log To File",IDC_STATIC,4,208,238,8
+END
+
+IDD_LIST DIALOGEX 0, 0, 200, 300
+STYLE DS_SETFONT | DS_SETFOREGROUND | DS_3DLOOK | DS_FIXEDSYS | DS_CENTER | WS_MINIMIZEBOX | WS_MAXIMIZEBOX |
+ WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME
+EXSTYLE WS_EX_CONTROLPARENT
+CAPTION "List Contacts with Status Message"
+FONT 8, "MS Shell Dlg", 0, 0, 0x1
+BEGIN
+ CONTROL "List Contacts",IDC_LIST,"SysListView32",LVS_REPORT |
+ LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_SHAREIMAGELISTS |
+ LVS_ALIGNLEFT | WS_TABSTOP,0,0,200,300
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// DESIGNINFO
+//
+
+#ifdef APSTUDIO_INVOKED
+GUIDELINES DESIGNINFO
+BEGIN
+ IDD_HISTORY, DIALOG
+ BEGIN
+ LEFTMARGIN, 5
+ RIGHTMARGIN, 239
+ TOPMARGIN, 5
+ BOTTOMMARGIN, 146
+ END
+
+ IDD_OPT, DIALOG
+ BEGIN
+ VERTGUIDE, 8
+ VERTGUIDE, 72
+ VERTGUIDE, 79
+ VERTGUIDE, 210
+ END
+
+ IDD_OPT_NTF, DIALOG
+ BEGIN
+ LEFTMARGIN, 2
+ RIGHTMARGIN, 312
+ VERTGUIDE, 8
+ VERTGUIDE, 58
+ VERTGUIDE, 63
+ VERTGUIDE, 72
+ VERTGUIDE, 77
+ VERTGUIDE, 181
+ VERTGUIDE, 198
+ VERTGUIDE, 215
+ VERTGUIDE, 306
+ TOPMARGIN, 1
+ BOTTOMMARGIN, 239
+ END
+
+ IDD_OPT_PROTOCOLS, DIALOG
+ BEGIN
+ LEFTMARGIN, 3
+ RIGHTMARGIN, 216
+ VERTGUIDE, 10
+ VERTGUIDE, 209
+ TOPMARGIN, 3
+ BOTTOMMARGIN, 186
+ END
+END
+#endif // APSTUDIO_INVOKED
+
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "resource.h\0"
+END
+
+2 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "#include ""resource.h""\r\n"
+ "#include ""winresrc.h""\r\n"
+ "\0"
+END
+
+3 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "\r\n"
+ "\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+#endif // English (U.S.) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
+
diff --git a/Plugins/smcnotify/smc.c b/Plugins/smcnotify/smc.c
new file mode 100644
index 0000000..9af94a7
--- /dev/null
+++ b/Plugins/smcnotify/smc.c
@@ -0,0 +1,401 @@
+/*
+Status Message Change Notify plugin for Miranda IM.
+
+Copyright © 2004-2005 NoName
+Copyright © 2005-2006 Daniel Vijge, Tomasz S³otwiñski, Ricardo Pescuma Domenecci
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "commonheaders.h"
+
+
+static void LogToFile(STATUSMSGINFO *smi) {
+ HANDLE hFile;
+ TCHAR filename[MAX_PATH] = _T("");
+ TCHAR *p = NULL;
+
+ p = _tcsstr(opts.logfile, _T("%c"));
+ if (p != NULL)
+ {
+ p[1] = _T('s');
+ mir_sntprintf(filename, MAX_PATH, opts.logfile, smi->cust);
+ p[1] = _T('c');
+ }
+ else
+ lstrcpyn(filename, opts.logfile, MAX_PATH);
+
+ hFile = CreateFile(filename/*opts.logfile*/, GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (hFile != INVALID_HANDLE_VALUE)
+ {
+ TCHAR *buffer = GetStr(smi, opts.log);
+
+ if (buffer != NULL && buffer[0] != _T('\0'))
+ {
+ DWORD dwWritten;
+
+ SetFilePointer(hFile, 0, 0, FILE_END);
+#ifdef UNICODE
+ if (opts.bLogAscii)
+ {
+ char *bufferA = mir_dupToAscii(buffer);
+ WriteFile(hFile, bufferA, lstrlenA(bufferA), &dwWritten, NULL);
+ WriteFile(hFile, "\r\n", 2, &dwWritten, NULL);
+ mir_free(bufferA);
+ }
+ else
+#endif
+ {
+ WriteFile(hFile, buffer, lstrlen(buffer) * sizeof(TCHAR), &dwWritten, NULL);
+ WriteFile(hFile, _T("\r\n"), 2 * sizeof(TCHAR), &dwWritten, NULL);
+ }
+
+ mir_free(buffer);
+ }
+ CloseHandle(hFile);
+ }
+
+ return;
+}
+
+static void AddToHistory(STATUSMSGINFO *smi) {
+ WORD historyFirst, historyLast, historyMax;
+// TCHAR *p;
+
+ historyMax = DBGetContactSettingWord(smi->hContact, MODULE_NAME, "HistoryMax", opts.dHistoryMax);
+ if (historyMax <= 0)
+ return;
+ else if (historyMax > 99)
+ historyMax = 99;
+
+ historyFirst = DBGetContactSettingWord(smi->hContact, MODULE_NAME, "HistoryFirst", 0);
+ if (historyFirst >= historyMax)
+ historyFirst = 0;
+ historyLast = DBGetContactSettingWord(smi->hContact, MODULE_NAME, "HistoryLast", 0);
+ if (historyLast >= historyMax)
+ historyLast = historyMax - 1;
+
+ //fix CR/LF from SimpleAway
+// p = &smi->newstatusmsg[0];
+// while (p = _tcsstr(p, _T("\n")))
+// {
+// if (p == &smi->newstatusmsg[0] || p[-1] != _T('\r'))
+// p[0] = _T('\r');
+// }
+
+ //write old status message and its timestamp seperately
+ DBWriteContactSettingTString(smi->hContact, MODULE_NAME, BuildSetting(historyLast, NULL), smi->newstatusmsg);
+ DBWriteContactSettingDword(smi->hContact, MODULE_NAME, BuildSetting(historyLast, "_ts"), smi->dTimeStamp);
+
+ historyLast = (historyLast + 1) % historyMax;
+ DBWriteContactSettingWord(smi->hContact, MODULE_NAME, "HistoryLast", historyLast);
+ if (historyLast == historyFirst)
+ DBWriteContactSettingWord(smi->hContact, MODULE_NAME, "HistoryFirst", (WORD)((historyFirst + 1) % historyMax));
+
+ return;
+}
+
+//return values:
+// 0 - No window found
+// 1 - Window found
+static BOOL MsgWindowCheck(HANDLE hContact) {
+ MessageWindowData mwd;
+ MessageWindowInputData mwid;
+ mwid.cbSize = sizeof(MessageWindowInputData);
+ mwid.hContact = hContact;
+ mwid.uFlags = MSG_WINDOW_UFLAG_MSG_BOTH;
+ mwd.cbSize = sizeof(MessageWindowData);
+ mwd.hContact = hContact;
+ if (!CallService(MS_MSG_GETWINDOWDATA, (WPARAM)&mwid, (LPARAM)&mwd))
+ {
+ if (mwd.hwndWindow != NULL && (mwd.uState & MSG_WINDOW_STATE_EXISTS)) return 1;
+ }
+ return 0;
+}
+
+// Returns true if the unicode buffer only contains 7-bit characters.
+static BOOL IsUnicodeAscii(const WCHAR * pBuffer, int nSize) {
+ BOOL bResult = TRUE;
+ int nIndex;
+
+ for (nIndex = 0; nIndex < nSize; nIndex++) {
+ if (pBuffer[nIndex] > 0x7F) {
+ bResult = FALSE;
+ break;
+ }
+ }
+ return bResult;
+}
+
+static void AddToDB(STATUSMSGINFO *smi) {
+ TCHAR *buffer = NULL;
+
+ if (smi->compare == 2)
+ buffer = GetStr(smi, opts.msgremoved);
+ else if (smi->compare == 1)
+ buffer = GetStr(smi, opts.msgchanged);
+
+ if (buffer != NULL && buffer[0] != _T('\0'))
+ {
+ DBEVENTINFO dbei = {0};
+#ifdef UNICODE
+ size_t needed, len, size;
+ BYTE *tmp = NULL;
+
+ needed = WideCharToMultiByte(CP_ACP, 0, buffer, -1, NULL, 0, NULL, NULL);
+ len = lstrlen(buffer);
+
+ if (/*opts.history_only_ansi_if_possible && */IsUnicodeAscii(buffer, len))
+ size = needed;
+ else
+ size = needed + (len + 1) * sizeof(WCHAR);
+
+ tmp = (BYTE*)mir_alloc0(size);
+
+ WideCharToMultiByte(CP_ACP, 0, buffer, -1, (char*)tmp, needed, NULL, NULL);
+
+ if (size > needed)
+ lstrcpyn((WCHAR*)&tmp[needed], buffer, len + 1);
+
+ //ZeroMemory(&dbei, sizeof(dbei));
+ dbei.cbSize = sizeof(dbei);
+ dbei.pBlob = tmp;
+ dbei.cbBlob = size;
+#else
+ dbei.pBlob = (PBYTE)buffer;
+ dbei.cbBlob = lstrlen(buffer) + 1;
+#endif
+
+ dbei.eventType = EVENTTYPE_STATUSCHANGE;
+ dbei.flags = 0;
+ dbei.timestamp = smi->dTimeStamp;
+ dbei.szModule = smi->proto;
+ CallService(MS_DB_EVENT_ADD, (WPARAM)smi->hContact, (LPARAM)&dbei);
+#ifdef UNICODE
+ mir_free(tmp);
+#endif
+ }
+ mir_free(buffer);
+
+ return;
+}
+
+static int __inline CheckStr(char *str, int not_empty, int empty) {
+ if (str == NULL || str[0] == '\0')
+ return empty;
+ else
+ return not_empty;
+}
+
+#ifdef UNICODE
+
+static int __inline CheckStrW(WCHAR *str, int not_empty, int empty) {
+ if (str == NULL || str[0] == L'\0')
+ return empty;
+ else
+ return not_empty;
+}
+
+#endif
+
+static int CompareStatusMsg(STATUSMSGINFO *smi, DBCONTACTWRITESETTING *cws_new) {
+ DBVARIANT dbv_old;
+ int ret;
+
+ switch (cws_new->value.type)
+ {
+ case DBVT_DELETED:
+ smi->newstatusmsg = NULL;
+ break;
+ case DBVT_ASCIIZ:
+#ifdef UNICODE
+ smi->newstatusmsg = (CheckStr(cws_new->value.pszVal, 0, 1) ? NULL : mir_dupToUnicodeEx(cws_new->value.pszVal, CP_ACP));
+ break;
+ case DBVT_UTF8:
+ smi->newstatusmsg = (CheckStr(cws_new->value.pszVal, 0, 1) ? NULL : mir_dupToUnicodeEx(cws_new->value.pszVal, CP_UTF8));
+ break;
+ case DBVT_WCHAR:
+ smi->newstatusmsg = (CheckStrW(cws_new->value.pwszVal, 0, 1) ? NULL : mir_wstrdup(cws_new->value.pwszVal));
+#else
+ smi->newstatusmsg = (CheckStr(cws_new->value.pszVal, 0, 1) ? NULL : mir_strdup(cws_new->value.pszVal));
+#endif
+ break;
+ default:
+ smi->newstatusmsg = NULL;
+ break;
+ }
+
+ if (!
+#ifdef UNICODE
+ DBGetContactSettingW(smi->hContact, "UserOnline", "OldStatusMsg", &dbv_old)
+#else
+ DBGetContactSetting(smi->hContact, "UserOnline", "OldStatusMsg", &dbv_old)
+#endif
+ )
+ {
+ switch (dbv_old.type)
+ {
+ case DBVT_ASCIIZ:
+#ifdef UNICODE
+ smi->oldstatusmsg = (CheckStr(dbv_old.pszVal, 0, 1) ? NULL : mir_dupToUnicodeEx(dbv_old.pszVal, CP_ACP));
+ break;
+ case DBVT_UTF8:
+ smi->oldstatusmsg = (CheckStr(dbv_old.pszVal, 0, 1) ? NULL : mir_dupToUnicodeEx(dbv_old.pszVal, CP_UTF8));
+ break;
+ case DBVT_WCHAR:
+ smi->oldstatusmsg = (CheckStrW(dbv_old.pwszVal, 0, 1) ? NULL : mir_wstrdup(dbv_old.pwszVal));
+#else
+ smi->oldstatusmsg = (CheckStr(dbv_old.pszVal, 0, 1) ? NULL : mir_strdup(dbv_old.pszVal));
+#endif
+ break;
+ default:
+ smi->oldstatusmsg = NULL;
+ break;
+ }
+
+ if (cws_new->value.type == DBVT_DELETED)
+ if (
+#ifdef UNICODE
+ dbv_old.type == DBVT_WCHAR)
+ ret = CheckStrW(dbv_old.pwszVal, 2, 0);
+ else if (dbv_old.type == DBVT_UTF8 ||
+#endif
+ dbv_old.type == DBVT_ASCIIZ)
+ ret = CheckStr(dbv_old.pszVal, 2, 0);
+ else
+ ret = 2;
+ else if (dbv_old.type != cws_new->value.type)
+#ifdef UNICODE
+ ret = (lstrcmpW(smi->newstatusmsg, smi->oldstatusmsg) ? CheckStrW(smi->newstatusmsg, 1, 2) : 0);
+#else
+ ret = 1;
+#endif;
+ else if (dbv_old.type == DBVT_ASCIIZ)
+ ret = (lstrcmpA(cws_new->value.pszVal, dbv_old.pszVal) ? CheckStr(cws_new->value.pszVal, 1, 2) : 0);
+#ifdef UNICODE
+ else if (dbv_old.type == DBVT_UTF8)
+ ret = (lstrcmpA(cws_new->value.pszVal, dbv_old.pszVal) ? CheckStr(cws_new->value.pszVal, 1, 2) : 0);
+ else if (dbv_old.type == DBVT_WCHAR)
+ ret = (lstrcmpW(cws_new->value.pwszVal, dbv_old.pwszVal) ? CheckStrW(cws_new->value.pwszVal, 1, 2) : 0);
+#endif
+ DBFreeVariant(&dbv_old);
+ }
+ else
+ {
+ if (cws_new->value.type == DBVT_DELETED)
+ ret = 0;
+ else if (
+#ifdef UNICODE
+ cws_new->value.type == DBVT_WCHAR)
+ ret = CheckStrW(cws_new->value.pwszVal, 1, 0);
+ else if (cws_new->value.type == DBVT_UTF8 ||
+#endif
+ cws_new->value.type == DBVT_ASCIIZ)
+ ret = CheckStr(cws_new->value.pszVal, 1, 0);
+ else
+ ret = 1;
+
+ smi->oldstatusmsg = NULL;
+ }
+
+ return ret;
+}
+
+static BOOL ProtocolEnabled(const char *proto) {
+ char setting[256];
+
+ if (proto == NULL)
+ return FALSE;
+
+ if (!AllowProtocol(proto))
+ return FALSE;
+
+ mir_snprintf(setting, sizeof(setting), "%sEnabled", proto);
+ return (BOOL)DBGetContactSettingByte(NULL, MODULE_NAME, setting, TRUE);
+}
+
+extern int ContactSettingChanged(WPARAM wParam, LPARAM lParam) {
+ DBCONTACTWRITESETTING *cws = (DBCONTACTWRITESETTING*)lParam;
+ STATUSMSGINFO smi;
+
+ if ((HANDLE)wParam == NULL) return 0;
+
+ ZeroMemory(&smi, sizeof(smi));
+ smi.proto = (char*)CallService(MS_PROTO_GETCONTACTBASEPROTO, wParam, 0);
+ if (!ProtocolEnabled(smi.proto)) return 0;
+
+ if (!lstrcmpA(cws->szModule, "CList") && !lstrcmpA(cws->szSetting, "StatusMsg"))
+ {
+ if (smi.proto != NULL && CallProtoService(smi.proto, PS_GETSTATUS, 0, 0) != ID_STATUS_OFFLINE)
+ {
+ smi.hContact = (HANDLE)wParam;
+ smi.ignore = DBGetContactSettingDword(smi.hContact, "Ignore", MODULE_NAME, 0);
+ if (smi.ignore == SMII_ALL) return 0;
+
+ smi.compare = CompareStatusMsg(&smi, cws);
+ if ((smi.compare == 0) || (opts.bIgnoreRemove && puopts.bIgnoreRemove && (smi.compare == 2)))
+ return FreeSmiStr(&smi);
+
+ if (DBGetContactSettingByte(NULL, MODULE_NAME, "IgnoreTlenAway", 0))
+ {
+ int len = lstrlen(smi.newstatusmsg);
+ if (smi.newstatusmsg[len-13] == _T('[') && smi.newstatusmsg[len-10] == _T(':') && smi.newstatusmsg[len-7] == _T(' ') &&
+ smi.newstatusmsg[len-4] == _T('.') && smi.newstatusmsg[len-1] == _T(']'))
+ return FreeSmiStr(&smi);
+ }
+
+ if (cws->value.type == DBVT_DELETED)
+ {
+ DBDeleteContactSetting(smi.hContact, "UserOnline", "OldStatusMsg");
+ }
+ else
+ {
+ DBCONTACTWRITESETTING cws_old;
+ cws_old.szModule = "UserOnline";
+ cws_old.szSetting = "OldStatusMsg";
+ cws_old.value = cws->value;
+ CallService(MS_DB_CONTACT_WRITESETTING, (WPARAM)smi.hContact, (LPARAM)&cws_old);
+ }
+ smi.cust = (TCHAR*)CallService(MS_CLIST_GETCONTACTDISPLAYNAME, wParam, GCDNF_TCHAR);
+ smi.dTimeStamp = (DWORD)time(NULL);
+
+ if (puopts.bEnable && !(smi.ignore & SMII_POPUP))
+ PopupCheck(&smi);
+
+ if (opts.bIgnoreRemove && (smi.ignore == 2))
+ smi.ignore = SMII_ALL;
+
+ if (opts.bDBEnable && MsgWindowCheck(smi.hContact))
+ AddToDB(&smi);
+
+ if (opts.bHistoryEnable && !(smi.ignore & SMII_HISTORY))
+ AddToHistory(&smi);
+
+ if (opts.bLogEnable && !(smi.ignore & SMII_LOG))
+ LogToFile(&smi);
+
+ FreeSmiStr(&smi);
+ }
+ }
+ else
+ {
+ if (!lstrcmpA(cws->szSetting, "Status") && !lstrcmpA(cws->szModule, smi.proto))
+ {
+ DBWriteContactSettingDword((HANDLE)wParam, "UserOnline", "LastStatusChange", GetTickCount());
+ }
+ }
+
+ return 0;
+} \ No newline at end of file
diff --git a/Plugins/smcnotify/smc.h b/Plugins/smcnotify/smc.h
new file mode 100644
index 0000000..e40d717
--- /dev/null
+++ b/Plugins/smcnotify/smc.h
@@ -0,0 +1,46 @@
+/*
+Status Message Change Notify plugin for Miranda IM.
+
+Copyright © 2004-2005 NoName
+Copyright © 2005-2006 Daniel Vijge, Tomasz S³otwiñski, Ricardo Pescuma Domenecci
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef __SMCNOTIFY_SMC_H
+#define __SMCNOTIFY_SMC_H
+
+
+typedef struct {
+ HANDLE hContact;
+ TCHAR *cust;
+ TCHAR *oldstatusmsg;
+ TCHAR *newstatusmsg;
+ char *proto;
+ int compare;
+ DWORD ignore;
+ DWORD dTimeStamp;
+} STATUSMSGINFO;
+
+#define EVENTTYPE_STATUSCHANGE 25368
+
+#define SMII_POPUP 1
+#define SMII_HISTORY 2
+#define SMII_LOG 4
+#define SMII_ALL 7
+
+int ContactSettingChanged(WPARAM wParam, LPARAM lParam);
+
+#endif // __SMCNOTIFY_SMC_H
diff --git a/Plugins/smcnotify/smcn.dsp b/Plugins/smcnotify/smcn.dsp
new file mode 100644
index 0000000..cb6753d
--- /dev/null
+++ b/Plugins/smcnotify/smcn.dsp
@@ -0,0 +1,194 @@
+# Microsoft Developer Studio Project File - Name="smcn" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=smcn - Win32 Release
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "smcn.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "smcn.mak" CFG="smcn - Win32 Release"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "smcn - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "smcn - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "smcn - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MD /W3 /GX /O1 /YX /FD /c
+# SUBTRACT BASE CPP /Fr
+# ADD CPP /nologo /G4 /MT /W3 /GX /O2 /Ob0 /I "../../include" /YX /FD /c
+# SUBTRACT CPP /Fr
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x417 /d "NDEBUG"
+# ADD RSC /l 0x417 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 user32.lib shell32.lib wininet.lib gdi32.lib /nologo /base:"0x67100000" /dll /machine:I386 /filealign:0x200
+# SUBTRACT BASE LINK32 /pdb:none /map
+# ADD LINK32 comctl32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /base:"0x32100000" /dll /map /machine:I386 /out:"..\..\bin\release\Plugins\smcn.dll" /filealign:0x200 /ALIGN:4096 /ignore:4108
+# SUBTRACT LINK32 /profile /pdb:none
+
+!ELSEIF "$(CFG)" == "smcn - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "smcn___Win32_Debug"
+# PROP BASE Intermediate_Dir "smcn___Win32_Debug"
+# PROP BASE Ignore_Export_Lib 0
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /G4 /MT /W3 /GX /O2 /Ob0 /I "../../include" /FR /YX /FD /c
+# ADD CPP /nologo /G4 /MTd /W3 /GX /ZI /Od /I "../../include" /FR /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x417 /d "NDEBUG"
+# ADD RSC /l 0x417 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 comctl32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 /out:"..\..bin\release\Plugins\smcn.dll" /filealign:0x200 /ALIGN:4096 /ignore:4108
+# SUBTRACT BASE LINK32 /profile /pdb:none
+# ADD LINK32 comctl32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /base:"0x32100000" /dll /debug /machine:I386 /out:"..\..\bin\debug\Plugins\smcn.dll" /filealign:0x200 /ALIGN:4096 /ignore:4108
+# SUBTRACT LINK32 /profile /pdb:none
+
+!ENDIF
+
+# Begin Target
+
+# Name "smcn - Win32 Release"
+# Name "smcn - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=.\dblists.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\history.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\icqsmsg.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\list.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\main.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\options.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\popup.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\smc.c
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=.\dblists.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\main.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\resource.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\smcn.h
+# End Source File
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
+# Begin Source File
+
+SOURCE=.\resources\hist.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\resources\list.bmp
+# End Source File
+# Begin Source File
+
+SOURCE=.\resources\list.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\resources\log.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\resources\popup.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\resources\popup_no.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\resources\rename.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\resource.rc
+# End Source File
+# Begin Source File
+
+SOURCE=.\resources\url.ico
+# End Source File
+# End Group
+# End Target
+# End Project
diff --git a/Plugins/smcnotify/smcn.dsw b/Plugins/smcnotify/smcn.dsw
new file mode 100644
index 0000000..49a03e6
--- /dev/null
+++ b/Plugins/smcnotify/smcn.dsw
@@ -0,0 +1,29 @@
+Microsoft Developer Studio Workspace File, Format Version 6.00
+# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
+
+###############################################################################
+
+Project: "nsn"=".\smcn.dsp" - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Global:
+
+Package=<5>
+{{{
+}}}
+
+Package=<3>
+{{{
+}}}
+
+###############################################################################
+
diff --git a/Plugins/smcnotify/smcn.sln b/Plugins/smcnotify/smcn.sln
new file mode 100644
index 0000000..ec2c580
--- /dev/null
+++ b/Plugins/smcnotify/smcn.sln
@@ -0,0 +1,23 @@
+
+Microsoft Visual Studio Solution File, Format Version 9.00
+# Visual C++ Express 2005
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "smcn", "smcn.vcproj", "{04B7DEDD-2BE2-4DA1-8B4A-BDA94CFC74E9}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Win32 = Debug|Win32
+ Release|Win32 = Release|Win32
+ Unicode Release|Win32 = Unicode Release|Win32
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {04B7DEDD-2BE2-4DA1-8B4A-BDA94CFC74E9}.Debug|Win32.ActiveCfg = Debug|Win32
+ {04B7DEDD-2BE2-4DA1-8B4A-BDA94CFC74E9}.Debug|Win32.Build.0 = Debug|Win32
+ {04B7DEDD-2BE2-4DA1-8B4A-BDA94CFC74E9}.Release|Win32.ActiveCfg = Release|Win32
+ {04B7DEDD-2BE2-4DA1-8B4A-BDA94CFC74E9}.Release|Win32.Build.0 = Release|Win32
+ {04B7DEDD-2BE2-4DA1-8B4A-BDA94CFC74E9}.Unicode Release|Win32.ActiveCfg = Unicode Release|Win32
+ {04B7DEDD-2BE2-4DA1-8B4A-BDA94CFC74E9}.Unicode Release|Win32.Build.0 = Unicode Release|Win32
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/Plugins/smcnotify/smcn.vcproj b/Plugins/smcnotify/smcn.vcproj
new file mode 100644
index 0000000..b2e2c9d
--- /dev/null
+++ b/Plugins/smcnotify/smcn.vcproj
@@ -0,0 +1,569 @@
+<?xml version="1.0" encoding="windows-1250"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="8,00"
+ Name="smcn"
+ ProjectGUID="{04B7DEDD-2BE2-4DA1-8B4A-BDA94CFC74E9}"
+ RootNamespace="smcn"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory=".\Release"
+ IntermediateDirectory=".\Release"
+ ConfigurationType="2"
+ InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC60.vsprops"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ PreprocessorDefinitions="NDEBUG"
+ MkTypLibCompatible="true"
+ SuppressStartupBanner="true"
+ TargetEnvironment="1"
+ TypeLibraryName=".\Release/smcn.tlb"
+ HeaderFileName=""
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ InlineFunctionExpansion="0"
+ AdditionalIncludeDirectories="..\..\include;.\include"
+ StringPooling="true"
+ RuntimeLibrary="2"
+ EnableFunctionLevelLinking="true"
+ PrecompiledHeaderFile=".\Unicode_Release/smcn.pch"
+ AssemblerListingLocation=".\Release/"
+ ObjectFile=".\Release/"
+ ProgramDataBaseFileName=".\Release/"
+ WarningLevel="3"
+ SuppressStartupBanner="true"
+ CompileAs="1"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="NDEBUG"
+ Culture="1047"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalOptions="/ALIGN:4096 /filealign:0x200 /ignore:4108 "
+ AdditionalDependencies="comctl32.lib"
+ OutputFile="..\..\bin\release\Plugins\smcn.dll"
+ LinkIncremental="1"
+ SuppressStartupBanner="true"
+ ProgramDatabaseFile=".\Release/smcn.pdb"
+ GenerateMapFile="true"
+ MapFileName=".\Release/smcn.map"
+ BaseAddress="0x32100000"
+ ImportLibrary=".\Release/smcn.lib"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ SuppressStartupBanner="true"
+ OutputFile=".\Release/smcn.bsc"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory=".\Debug"
+ IntermediateDirectory=".\Debug"
+ ConfigurationType="2"
+ InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC60.vsprops"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ PreprocessorDefinitions="NDEBUG"
+ MkTypLibCompatible="true"
+ SuppressStartupBanner="true"
+ TargetEnvironment="1"
+ TypeLibraryName=".\Debug/smcn.tlb"
+ HeaderFileName=""
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories="../../include;include"
+ RuntimeLibrary="1"
+ PrecompiledHeaderFile=".\Debug/smcn.pch"
+ AssemblerListingLocation=".\Debug/"
+ ObjectFile=".\Debug/"
+ ProgramDataBaseFileName=".\Debug/"
+ BrowseInformation="1"
+ WarningLevel="3"
+ SuppressStartupBanner="true"
+ DebugInformationFormat="4"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="NDEBUG"
+ Culture="1047"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalOptions="/ALIGN:4096 /filealign:0x200 /ignore:4108 "
+ AdditionalDependencies="comctl32.lib"
+ OutputFile="c:\tools\Miranda_test\plugins\smcn.dll"
+ LinkIncremental="1"
+ SuppressStartupBanner="true"
+ GenerateDebugInformation="true"
+ ProgramDatabaseFile=".\Debug/smcn.pdb"
+ BaseAddress="0x32100000"
+ ImportLibrary=".\Debug/smcn.lib"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ SuppressStartupBanner="true"
+ OutputFile=".\Debug/smcn.bsc"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Unicode Release|Win32"
+ OutputDirectory=".\Unicode_Release"
+ IntermediateDirectory=".\Unicode_Release"
+ ConfigurationType="2"
+ InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC60.vsprops"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ PreprocessorDefinitions="NDEBUG"
+ MkTypLibCompatible="true"
+ SuppressStartupBanner="true"
+ TargetEnvironment="1"
+ TypeLibraryName=".\Release/smcn.tlb"
+ HeaderFileName=""
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ InlineFunctionExpansion="0"
+ AdditionalIncludeDirectories="..\..\include;.\include"
+ PreprocessorDefinitions="_UNICODE;UNICODE"
+ StringPooling="true"
+ RuntimeLibrary="2"
+ EnableFunctionLevelLinking="true"
+ PrecompiledHeaderFile=".\Unicode_Release/smcn.pch"
+ AssemblerListingLocation=".\Unicode_Release/"
+ ObjectFile=".\Unicode_Release/"
+ ProgramDataBaseFileName=".\Unicode_Release/"
+ WarningLevel="3"
+ SuppressStartupBanner="true"
+ CompileAs="1"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="NDEBUG"
+ Culture="1047"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalOptions="/ALIGN:4096 /filealign:0x200 /ignore:4108 "
+ AdditionalDependencies="comctl32.lib"
+ OutputFile="..\..\bin\release\Plugins\smcnW.dll"
+ LinkIncremental="1"
+ SuppressStartupBanner="true"
+ ProgramDatabaseFile=".\Unicode_Release/smcn.pdb"
+ GenerateMapFile="true"
+ MapFileName=".\Unicode_Release/smcn.map"
+ BaseAddress="0x32100000"
+ ImportLibrary=".\Unicode_Release/smcn.lib"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ SuppressStartupBanner="true"
+ OutputFile=".\Unicode_Release/smcn.bsc"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Source Files"
+ Filter="cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+ >
+ <File
+ RelativePath="history.c"
+ >
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Unicode Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="list.c"
+ >
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Unicode Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="main.c"
+ >
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Unicode Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="options.c"
+ >
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ WarningLevel="3"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Unicode Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ WarningLevel="3"
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="popup.c"
+ >
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Unicode Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="smc.c"
+ >
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Unicode Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath=".\utils.c"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Header Files"
+ Filter="h;hpp;hxx;hm;inl"
+ >
+ <File
+ RelativePath="main.h"
+ >
+ </File>
+ <File
+ RelativePath="resource.h"
+ >
+ </File>
+ <File
+ RelativePath="smcn.h"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Resource Files"
+ Filter="ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
+ >
+ <File
+ RelativePath="resources\hist.ico"
+ >
+ </File>
+ <File
+ RelativePath="resources\list.ico"
+ >
+ </File>
+ <File
+ RelativePath="resources\log.ico"
+ >
+ </File>
+ <File
+ RelativePath="resources\popup.ico"
+ >
+ </File>
+ <File
+ RelativePath="resources\popup_no.ico"
+ >
+ </File>
+ <File
+ RelativePath="resource.rc"
+ >
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Unicode Release|Win32"
+ >
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="resources\url.ico"
+ >
+ </File>
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/Plugins/smcnotify/smcnotify.c b/Plugins/smcnotify/smcnotify.c
new file mode 100644
index 0000000..bb810a6
--- /dev/null
+++ b/Plugins/smcnotify/smcnotify.c
@@ -0,0 +1,165 @@
+/*
+Status Message Change Notify plugin for Miranda IM.
+
+Copyright © 2004-2005 NoName
+Copyright © 2005-2007 Daniel Vijge, Tomasz S³otwiñski, Ricardo Pescuma Domenecci
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "commonheaders.h"
+
+
+// Prototypes ///////////////////////////////////////////////////////////////////////////
+
+PLUGININFO pluginInfo = {
+ sizeof(PLUGININFO),
+ PLUGIN_NAME
+#ifdef CUSTOMBUILD_CATCHICQSTATUSMSG
+ " +ICQ"
+#endif
+#ifdef CUSTOMBUILD_OSDSUPPORT
+ " +OSD"
+#endif
+#ifdef _UNICODE
+ " (Unicode)"
+#endif
+ ,
+ PLUGIN_MAKE_VERSION(0,0,3,17),
+ "Notifies, logs and stores history of your contacts' status messages changes",
+ "Daniel Vijge, Tomasz S³otwiñski, Ricardo Pescuma Domenecci",
+ "slotwin@users.berlios.de",
+ "",
+ "http://developer.berlios.de/projects/mgoodies",
+ UNICODE_AWARE,
+ 0
+};
+
+int ProtoAck(WPARAM wParam, LPARAM lParam);
+
+HANDLE heModulesLoaded;
+HANDLE heOptionsInit;
+HANDLE heProtoAck;
+HANDLE hePreBuildCMenu;
+HANDLE heContactSettingChanged;
+HANDLE heUserInfoInit;
+
+// Functions ////////////////////////////////////////////////////////////////////////////
+
+extern BOOL WINAPI DllMain(HINSTANCE hinstDLL,DWORD fdwReason,LPVOID lpvReserved) {
+ hInst = hinstDLL;
+ return TRUE;
+}
+
+extern __declspec(dllexport) PLUGININFO* MirandaPluginInfo(DWORD mirandaVersion) {
+/* if (mirandaVersion < PLUGIN_MAKE_VERSION(0,6,6,0))
+ {
+ //not translatable
+ MessageBox(NULL, "Plugin requires Miranda 0.6.6.0 or later to run.", PLUGIN_NAME, MB_OK|MB_ICONERROR);
+ return NULL;
+ }
+*/ return &pluginInfo;
+}
+
+static int ModulesLoaded(WPARAM wParam, LPARAM lParam) {
+
+ CreateServiceFunction(MS_SMCNOTIFY_POPUPS, MenuItemCmd_PopUps);
+ CreateServiceFunction(MS_SMCNOTIFY_LIST, MenuItemCmd_ShowList);
+ CreateServiceFunction(MS_SMCNOTIFY_GOTOURL, MenuItemCmd_GoToURL);
+ hListDlg = NULL;
+
+ InitPopups();
+ LoadIcons();
+ InitMenuItems();
+
+ heContactSettingChanged = HookEvent(ME_DB_CONTACT_SETTINGCHANGED, ContactSettingChanged);
+ heUserInfoInit = HookEvent(ME_USERINFO_INITIALISE, UserInfoInit);
+
+ //Register with Updater plugin
+ if (ServiceExists(MS_UPDATE_REGISTER))
+ {
+ Update upd;
+ char szCurrentVersion[30];
+
+ ZeroMemory(&upd, sizeof(upd));
+ upd.cbSize = sizeof(upd);
+ upd.szComponentName = pluginInfo.shortName;
+
+ upd.szUpdateURL = UPDATER_AUTOREGISTER;
+
+ upd.szBetaVersionURL = "http://www.torun.mm.pl/~slotwin/smcnotify/version.txt";
+ upd.szBetaChangelogURL = "http://www.torun.mm.pl/~slotwin/smcnotify/changelog.txt";
+ upd.pbBetaVersionPrefix = (BYTE *)"Status Message Change Notify ";
+ upd.cpbBetaVersionPrefix = strlen((char *)upd.pbBetaVersionPrefix);
+#ifdef UNICODE
+ upd.szBetaUpdateURL = "http://www.torun.mm.pl/~slotwin/smcnotify/smcnotifyW.zip";
+#else
+ upd.szBetaUpdateURL = "http://www.torun.mm.pl/~slotwin/smcnotify/smcnotify.zip";
+#endif
+
+ upd.pbVersion = (BYTE *)CreateVersionStringPlugin(&pluginInfo, szCurrentVersion);
+ upd.cpbVersion = strlen((char *)upd.pbVersion);
+
+ CallService(MS_UPDATE_REGISTER, 0, (LPARAM)&upd);
+ }
+
+ //Add sound event
+ SkinAddNewSoundEx("smcnotify", Translate("Status Notify"), Translate("User has changed status message"));
+
+ //Add our module to the KnownModules list
+ CallService("DBEditorpp/RegisterSingleModule", (WPARAM)MODULE_NAME, 0);
+
+ return 0;
+}
+
+extern int __declspec(dllexport) Load(PLUGINLINK *link) {
+ INITCOMMONCONTROLSEX icex;
+
+ //Ensure that the common control DLL is loaded.
+ ZeroMemory(&icex, sizeof(icex));
+ icex.dwSize = sizeof(icex);
+ icex.dwICC = ICC_LISTVIEW_CLASSES;
+ InitCommonControlsEx(&icex);
+
+ pluginLink = link;
+
+ init_mir_malloc();
+ LoadOptions();
+
+ //Hooks
+ heModulesLoaded = HookEvent(ME_SYSTEM_MODULESLOADED, ModulesLoaded);
+ heOptionsInit = HookEvent(ME_OPT_INITIALISE, OptionsInit);
+ heProtoAck = HookEvent(ME_PROTO_ACK, ProtoAck); // needed for "show popups when i connect"
+ hePreBuildCMenu = HookEvent(ME_CLIST_PREBUILDCONTACTMENU, PreBuildCMenu);
+
+ return 0;
+}
+
+extern int __declspec(dllexport) Unload(void) {
+
+ UnhookEvent(heModulesLoaded);
+ UnhookEvent(heOptionsInit);
+ UnhookEvent(heProtoAck);
+ UnhookEvent(hePreBuildCMenu);
+ UnhookEvent(heUserInfoInit);
+ UnhookEvent(heContactSettingChanged);
+
+ DeinitPopups();
+
+ if (hListDlg != NULL)
+ SendMessage(hListDlg, WM_CLOSE, 0, 0);
+
+ return 0;
+}
diff --git a/Plugins/smcnotify/smcnotify.vcproj b/Plugins/smcnotify/smcnotify.vcproj
new file mode 100644
index 0000000..162d0d0
--- /dev/null
+++ b/Plugins/smcnotify/smcnotify.vcproj
@@ -0,0 +1,582 @@
+<?xml version="1.0" encoding="windows-1250"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="8,00"
+ Name="smcnotify"
+ ProjectGUID="{04B7DEDD-2BE2-4DA1-8B4A-BDA94CFC74E9}"
+ RootNamespace="smcnotify"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)/Plugins"
+ IntermediateDirectory="$(SolutionDir)$(ConfigurationName)/Obj/$(ProjectName)"
+ ConfigurationType="2"
+ InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="2"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ PreprocessorDefinitions="_DEBUG"
+ MkTypLibCompatible="true"
+ SuppressStartupBanner="true"
+ TargetEnvironment="1"
+ TypeLibraryName=".\Debug\smcnotify.tlb"
+ HeaderFileName=""
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories="..\..\include;.\include"
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;SMCNOTIFY_EXPORTS"
+ ExceptionHandling="0"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ UsePrecompiledHeader="2"
+ PrecompiledHeaderThrough="commonheaders.h"
+ BrowseInformationFile="$(IntDir)/"
+ WarningLevel="3"
+ SuppressStartupBanner="true"
+ DebugInformationFormat="4"
+ CompileAs="1"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="_DEBUG"
+ Culture="2057"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="comctl32.lib"
+ LinkIncremental="0"
+ SuppressStartupBanner="true"
+ GenerateDebugInformation="true"
+ GenerateMapFile="true"
+ BaseAddress="0x32100000"
+ ImportLibrary="$(IntDir)/$(TargetName).lib"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)/Plugins"
+ IntermediateDirectory="$(SolutionDir)$(ConfigurationName)/Obj/$(ProjectName)"
+ ConfigurationType="2"
+ InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="2"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ PreprocessorDefinitions="NDEBUG"
+ MkTypLibCompatible="true"
+ SuppressStartupBanner="true"
+ TargetEnvironment="1"
+ TypeLibraryName=".\Release\smcnotify.tlb"
+ HeaderFileName=""
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="3"
+ InlineFunctionExpansion="1"
+ FavorSizeOrSpeed="2"
+ AdditionalIncludeDirectories="..\..\include;.\include"
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;SMCNOTIFY_EXPORTS"
+ StringPooling="true"
+ ExceptionHandling="0"
+ RuntimeLibrary="2"
+ EnableFunctionLevelLinking="true"
+ UsePrecompiledHeader="0"
+ PrecompiledHeaderThrough="commonheaders.h"
+ AssemblerOutput="3"
+ BrowseInformationFile="$(IntDir)/"
+ WarningLevel="3"
+ SuppressStartupBanner="true"
+ DebugInformationFormat="3"
+ CompileAs="1"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="NDEBUG"
+ Culture="1033"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ IgnoreImportLibrary="true"
+ AdditionalDependencies="comctl32.lib"
+ LinkIncremental="1"
+ SuppressStartupBanner="true"
+ GenerateDebugInformation="true"
+ GenerateMapFile="true"
+ MapFileName="$(OutDir)/$(ProjectName).map"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ OptimizeForWindows98="1"
+ BaseAddress="0x32100000"
+ ImportLibrary="$(IntDir)/$(TargetName).lib"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Debug Unicode|Win32"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)/Plugins"
+ IntermediateDirectory="$(SolutionDir)$(ConfigurationName)/Obj/$(ProjectName)"
+ ConfigurationType="2"
+ InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ PreprocessorDefinitions="_DEBUG"
+ MkTypLibCompatible="true"
+ SuppressStartupBanner="true"
+ TargetEnvironment="1"
+ TypeLibraryName=".\Debug Unicode\smcnotify.tlb"
+ HeaderFileName=""
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories="..\..\include;.\include"
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;UNICODE;_UNICODE;SMCNOTIFY_EXPORTS"
+ ExceptionHandling="0"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ UsePrecompiledHeader="2"
+ PrecompiledHeaderThrough="commonheaders.h"
+ WarningLevel="3"
+ SuppressStartupBanner="true"
+ DebugInformationFormat="4"
+ CompileAs="1"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="_DEBUG"
+ Culture="2057"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="comctl32.lib"
+ LinkIncremental="0"
+ SuppressStartupBanner="true"
+ GenerateDebugInformation="true"
+ BaseAddress="0x32100000"
+ ImportLibrary="$(IntDir)/$(TargetName).lib"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release Unicode|Win32"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)/Plugins"
+ IntermediateDirectory="$(SolutionDir)$(ConfigurationName)/Obj/$(ProjectName)"
+ ConfigurationType="2"
+ InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="2"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ PreprocessorDefinitions="NDEBUG"
+ MkTypLibCompatible="true"
+ SuppressStartupBanner="true"
+ TargetEnvironment="1"
+ TypeLibraryName=".\Release Unicode\smcnotify.tlb"
+ HeaderFileName=""
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="3"
+ InlineFunctionExpansion="1"
+ FavorSizeOrSpeed="2"
+ OmitFramePointers="true"
+ WholeProgramOptimization="true"
+ AdditionalIncludeDirectories="..\..\include;.\include"
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;UNICODE;_UNICODE;SMCNOTIFY_EXPORTS"
+ StringPooling="true"
+ ExceptionHandling="0"
+ RuntimeLibrary="2"
+ EnableFunctionLevelLinking="true"
+ UsePrecompiledHeader="0"
+ PrecompiledHeaderThrough="commonheaders.h"
+ PrecompiledHeaderFile=""
+ AssemblerOutput="3"
+ BrowseInformationFile="$(IntDir)/"
+ WarningLevel="3"
+ SuppressStartupBanner="true"
+ DebugInformationFormat="3"
+ CompileAs="1"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="NDEBUG"
+ Culture="1033"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ IgnoreImportLibrary="true"
+ AdditionalDependencies="comctl32.lib"
+ LinkIncremental="0"
+ SuppressStartupBanner="true"
+ GenerateDebugInformation="true"
+ GenerateMapFile="true"
+ MapFileName="$(OutDir)/$(ProjectName).map"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ OptimizeForWindows98="1"
+ BaseAddress="0x32100000"
+ ImportLibrary="$(IntDir)/$(TargetName).lib"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Source Files"
+ Filter="cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+ >
+ <File
+ RelativePath=".\history.c"
+ >
+ </File>
+ <File
+ RelativePath=".\ignore.c"
+ >
+ </File>
+ <File
+ RelativePath=".\list.c"
+ >
+ </File>
+ <File
+ RelativePath=".\menu.c"
+ >
+ </File>
+ <File
+ RelativePath="..\utils\mir_memory.cpp"
+ >
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ CompileAs="2"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ CompileAs="2"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ CompileAs="2"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release Unicode|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ CompileAs="2"
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="..\utils\mir_options.cpp"
+ >
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ CompileAs="2"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ CompileAs="2"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ CompileAs="2"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release Unicode|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ CompileAs="2"
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath=".\options.c"
+ >
+ </File>
+ <File
+ RelativePath=".\popup.c"
+ >
+ </File>
+ <File
+ RelativePath=".\smc.c"
+ >
+ </File>
+ <File
+ RelativePath=".\smcnotify.c"
+ >
+ </File>
+ <File
+ RelativePath=".\utils.c"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Header Files"
+ Filter="h;hpp;hxx;hm;inl"
+ >
+ <File
+ RelativePath=".\commonheaders.h"
+ >
+ </File>
+ <File
+ RelativePath=".\m_smcnotify.h"
+ >
+ </File>
+ <File
+ RelativePath=".\menu.h"
+ >
+ </File>
+ <File
+ RelativePath="..\utils\mir_memory.h"
+ >
+ </File>
+ <File
+ RelativePath="..\utils\mir_options.h"
+ >
+ </File>
+ <File
+ RelativePath=".\options.h"
+ >
+ </File>
+ <File
+ RelativePath=".\popup.h"
+ >
+ </File>
+ <File
+ RelativePath=".\resource.h"
+ >
+ </File>
+ <File
+ RelativePath=".\smc.h"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Resource Files"
+ Filter="ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
+ >
+ <File
+ RelativePath=".\resource.rc"
+ >
+ </File>
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/Plugins/smcnotify/utils.c b/Plugins/smcnotify/utils.c
new file mode 100644
index 0000000..01f8834
--- /dev/null
+++ b/Plugins/smcnotify/utils.c
@@ -0,0 +1,238 @@
+/*
+Status Message Change Notify plugin for Miranda IM.
+
+Copyright © 2004-2005 NoName
+Copyright © 2005-2006 Daniel Vijge, Tomasz S³otwiñski, Ricardo Pescuma Domenecci
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "commonheaders.h"
+
+
+#define TMPMAX 1024
+TCHAR* GetStr(STATUSMSGINFO *n, const TCHAR *tmplt) {
+ TCHAR tmp[TMPMAX];
+ TCHAR *str;
+ int i;
+ int len;
+ time_t timestamp;
+ struct tm smsgtime;
+
+ if (tmplt == NULL || tmplt[0] == _T('\0'))
+ return NULL;
+
+ str = (TCHAR*)mir_alloc0(2048 * sizeof(TCHAR));
+ str[0] = _T('\0');
+ len = lstrlen(tmplt);
+
+ if (n->dTimeStamp != 0)
+ timestamp = n->dTimeStamp;
+ else
+ timestamp = time(NULL);
+ ZeroMemory(&smsgtime, sizeof(smsgtime));
+ localtime_s(&smsgtime, &timestamp);
+
+ for (i = 0; i < len; i++)
+ {
+ tmp[0] = _T('\0');
+
+ if (tmplt[i] == _T('%'))
+ {
+ i++;
+ switch (tmplt[i])
+ {
+ case 'n':
+ if (n->compare == 2) lstrcpyn(tmp, TranslateT("<no status message>"), TMPMAX);
+ else lstrcpyn(tmp, n->newstatusmsg, TMPMAX);
+ break;
+ case 'o':
+ if (n->oldstatusmsg == NULL || n->oldstatusmsg[0] == _T('\0')) lstrcpyn(tmp, TranslateT("<no status message>"), TMPMAX);
+ else lstrcpyn(tmp, n->oldstatusmsg, TMPMAX);
+ break;
+ case 'c':
+ if (n->cust == NULL || n->cust[0] == _T('\0')) lstrcpyn(tmp, TranslateT("Contact"), TMPMAX);
+ else lstrcpyn(tmp, n->cust, TMPMAX);
+ break;
+ case 'D':
+ mir_sntprintf(tmp, 1024, _T("%02i"), smsgtime.tm_mday);
+ break;
+ case 'M':
+ mir_sntprintf(tmp, TMPMAX, _T("%02i"), smsgtime.tm_mon + 1);
+ break;
+ case 'Y':
+ mir_sntprintf(tmp, TMPMAX, _T("%i"), smsgtime.tm_year + 1900);
+ break;
+ case 'H':
+ mir_sntprintf(tmp, TMPMAX, _T("%i"), smsgtime.tm_hour);
+ break;
+ case 'h':
+ mir_sntprintf(tmp, TMPMAX, _T("%i"), smsgtime.tm_hour%12 == 0 ? 12 : smsgtime.tm_hour%12);
+ break;
+ case 'm':
+ mir_sntprintf(tmp, TMPMAX, _T("%02i"), smsgtime.tm_min);
+ break;
+ case 's':
+ mir_sntprintf(tmp, TMPMAX, _T("%02i"), smsgtime.tm_sec);
+ break;
+ case 'a':
+ if (smsgtime.tm_hour > 11) lstrcpyn(tmp, _T("PM"), TMPMAX);
+ if (smsgtime.tm_hour < 12) lstrcpyn(tmp, _T("AM"), TMPMAX);
+ break;
+ default:
+ //lstrcpyn(tmp, _T("%"), TMPMAX);
+ i--;
+ tmp[0] = tmplt[i]; tmp[1] = _T('\0');
+ break;
+ }
+ }
+ else if (tmplt[i] == _T('\\'))
+ {
+ i++;
+ switch (tmplt[i])
+ {
+ case 'n':
+ //_tcscat_s(tmp, TMPMAX, _T("\r\n"));
+ tmp[0] = _T('\r'); tmp[1] = _T('\n'); tmp[2] = _T('\0');
+ break;
+ case 't':
+ //_tcscat_s(tmp, TMPMAX, _T("\t"));
+ tmp[0] = _T('\t'); tmp[1] = _T('\0');
+ break;
+ default:
+ //lstrcpyn(tmp, _T("\\"), TMPMAX);
+ i--;
+ tmp[0] = tmplt[i]; tmp[1] = _T('\0');
+ break;
+ }
+ }
+ else
+ {
+ tmp[0] = tmplt[i]; tmp[1] = _T('\0');
+ }
+
+ if (tmp[0] != _T('\0'))
+ {
+ if (lstrlen(tmp) + lstrlen(str) < 2044)
+ {
+ lstrcat(str, tmp);
+ }
+ else
+ {
+ lstrcat(str, _T("..."));
+ break;
+ }
+ }
+ }
+ return str;
+}
+
+char* BuildSetting(WORD index, char *suffix) {
+ static char setting[16];
+ mir_snprintf(setting, sizeof(setting), "History_%i%s", index, (suffix == NULL)?"":suffix);
+ return setting;
+}
+
+extern BOOL FreeSmiStr(STATUSMSGINFO *smi) {
+ mir_free(smi->newstatusmsg);
+ mir_free(smi->oldstatusmsg);
+ return 0;
+}
+
+extern WCHAR *mir_dupToUnicodeEx(char *ptr, UINT CodePage)
+{
+ size_t size;
+ WCHAR *tmp;
+
+ if (ptr == NULL)
+ return NULL;
+
+ size = strlen(ptr) + 1;
+ tmp = (WCHAR *) mir_alloc0(size * sizeof(WCHAR));
+
+ MultiByteToWideChar(CodePage, 0, ptr, -1, tmp, size * sizeof(WCHAR));
+
+ return tmp;
+}
+
+extern TCHAR* MyDBGetContactSettingTString_dup(HANDLE hContact, const char *szModule, const char *szSetting, TCHAR *out) {
+ DBVARIANT dbv;
+
+ if (!DBGetContactSettingTString(hContact, szModule, szSetting, &dbv))
+ {
+ switch (dbv.type)
+ {
+ case DBVT_ASCIIZ:
+#ifdef UNICODE
+ out = mir_dupToUnicodeEx(dbv.pszVal, CP_ACP);
+ break;
+ case DBVT_UTF8:
+ out = mir_dupToUnicodeEx(dbv.pszVal, CP_UTF8);
+ break;
+ case DBVT_WCHAR:
+ out = mir_wstrdup(dbv.pwszVal);
+#else
+ out = mir_strdup(dbv.pszVal);
+#endif
+ break;
+ default:
+ out = NULL;
+ break;
+ }
+ DBFreeVariant(&dbv);
+ }
+ else
+ {
+ out = NULL;
+ }
+
+ return out;
+}
+
+extern ProtoAck(WPARAM wParam, LPARAM lParam) {
+ ACKDATA *ack = (ACKDATA*)lParam;
+
+ if (ack->type == ACKTYPE_STATUS)
+ {
+ WORD newStatus = (WORD)ack->lParam;
+ WORD oldStatus = (WORD)ack->hProcess;
+ char *proto = (char*)ack->szModule;
+
+ if (oldStatus == newStatus) return 0;
+ if (newStatus == ID_STATUS_OFFLINE)
+ {
+ DBWriteContactSettingDword(NULL, MODULE_NAME, proto, 0);
+ }
+ else if ((oldStatus < ID_STATUS_ONLINE) && (newStatus >= ID_STATUS_ONLINE))
+ {
+ DBWriteContactSettingDword(NULL, MODULE_NAME, proto, GetTickCount());
+ }
+
+ return 0;
+ }
+
+#ifdef CUSTOMBUILD_CATCHICQSTATUSMSG
+ if (ack->type == ACKTYPE_AWAYMSG && !lstrcmpA(ack->szModule, "ICQ"))
+ {
+ if (ack->result == ACKRESULT_SUCCESS && !ServiceExists("SMR/MsgRetrievalEnabledForProtocol"))
+ {
+ DBWriteContactSettingString(ack->hContact, "CList", "StatusMsg", (const char*)ack->lParam);
+ }
+ return 0;
+ }
+#endif
+
+ return 0;
+}
diff --git a/Plugins/smh/Docs/helppack_smh.txt b/Plugins/smh/Docs/helppack_smh.txt
new file mode 100644
index 0000000..600509d
--- /dev/null
+++ b/Plugins/smh/Docs/helppack_smh.txt
@@ -0,0 +1,33 @@
+Miranda Help Pack Version 1
+Language: English
+Locale: 0809
+Last-Modified-Using: Miranda IM 0.5
+Authors: Pescuma
+Plugins-included: smh
+
+[smh:1061@AyUEAyYEAywEByIEAy0EByMEAy4EDhEE]
+Log changes made by the users to the contact history=
+
+[smh:1062@AyUEAyYEAywEByIEAy0EByMEAy4EDhEE]
+Enable this option to test if the text to log has only ANSI chars and in this case avoid spending some database space=Due to the way miranda store the contact history in unicode the size of an entry can be up to 3 times bigger than the text itself. If the message contains only ANSI chars, then this can be avoided.
+
+[smh:1068@AyUEAyYEAywEByIEAy0EByMEAy4EDhEE]
+Enable this to monitor status message changes=This means that the status message was empty and has beeing filled or that the text has changed (but not to an empty text).<br>This is used to log to history and to show popups.
+
+[smh:1064@AyUEAyYEAywEByIEAy0EByMEAy4EDhEE]
+[smh:1058@AyUEAyYEAywEByIEAy0EByMEAy4EDhEE]
+Template of the text to be used for status message changes=The first %s found is replaced by the contact's new status message.<br>This is used to log to history and to show popups.
+
+[smh:1069@AyUEAyYEAywEByIEAy0EByMEAy4EDhEE]
+Enable this to monitor status message removal=This means that the user had a status message and changed it to an empty text.<br>This is used to log to history and to show popups.
+
+[smh:1065@AyUEAyYEAywEByIEAy0EByMEAy4EDhEE]
+[smh:1059@AyUEAyYEAywEByIEAy0EByMEAy4EDhEE]
+Template of the text to be used for status message removal=This is used to log to history and to show popups.
+
+[smh:1070@AyUEAyYEAywEByIEAy0EByMEAy4EDhEE]
+Enable this to ignore changes/removes when the user is offline=This is used to log to history and to show popups.
+
+[smh:1067@AyUEAyYEAywEByIEAy0EByMEAy4EDhEE]
+[smh:1041@AyUEAyYEAywEByIEAy0EByMEAy4EDhEE]
+List of protocols to be monitored=Status message changes/removes that happens in contacts from protocols that are not in this list will be ignored. Metacontact is a special case: the status message changes/removes is not tracked for metacontacts, but it is copied to the meta if it happens in one of its subcontacts.<br>This is used to log to history and to show popups.
diff --git a/Plugins/smh/Docs/langpack_smh.txt b/Plugins/smh/Docs/langpack_smh.txt
new file mode 100644
index 0000000..a82a878
--- /dev/null
+++ b/Plugins/smh/Docs/langpack_smh.txt
@@ -0,0 +1,64 @@
+; Status Message History
+; Author: Pescuma
+
+; Options
+
+[History]
+[Status Message]
+
+[ History ]
+[Log changes to history]
+[Don't save Unicode parts for 7bit ANSI history (saves db space)]
+
+[ Tracking ]
+[Track when contacts change their status messages]
+[Track when contacts remove their status messages]
+[Template:]
+[Only when the contact is not offline]
+
+[ Protocols ]
+[Enable tracking for these protocols:]
+
+[Popups]
+[Status Msg Change]
+
+[Enable popups]
+
+[ Colours ]
+[Background colour]
+[Text colour]
+[Use Windows colours]
+[Use default colours]
+
+[ Delay ]
+[From popup plugin]
+[Custom]
+[Permanent]
+
+[ Actions ]
+[On right click:]
+[On left click:]
+[Do nothing]
+[Close popup]
+[Show history]
+
+[Preview]
+
+
+; Test popup
+
+[Test Contact]
+[Test description]
+
+
+; Contact Menu
+
+[Log Status Message changes]
+[Don't log Status Message changes]
+
+
+; Template defaults
+
+[changed his/her status message to %s]
+[removed his/her status message]
+[<no status message>]
diff --git a/Plugins/smh/Docs/smh_changelog.txt b/Plugins/smh/Docs/smh_changelog.txt
new file mode 100644
index 0000000..f7242ca
--- /dev/null
+++ b/Plugins/smh/Docs/smh_changelog.txt
@@ -0,0 +1,38 @@
+Status Message History 0.1.0.3
+
+Changelog:
+
+. 0.1.0.3
+ + Added support for Miranda 0.8
+
+. 0.1.0.2
+ * -pv- patch
+
+. 0.1.0.1
+ + Added support for \n in template (represents a line break)
+
+. 0.1.0.0
+ + Added to FL
+ + Added help pack
+
+. 0.0.0.6
+ * Fixes for changes tracking
+ * Fixes for updater url
+
+. 0.0.0.5
+ + Changed to History entry in options
+ * Fixes in langpack
+
+. 0.0.0.4
+ * Fix for crash
+
+. 0.0.0.3
+ + Options to what to track
+ * Fix for popups
+
+. 0.0.0.2
+ + Options page
+ + Popups
+
+. 0.0.0.1
+ + Initial version \ No newline at end of file
diff --git a/Plugins/smh/Docs/smh_readme.txt b/Plugins/smh/Docs/smh_readme.txt
new file mode 100644
index 0000000..040c797
--- /dev/null
+++ b/Plugins/smh/Docs/smh_readme.txt
@@ -0,0 +1,11 @@
+Status Message History plugin
+-----------------------------
+
+THIS PLUGIN WAS DEPRECATED IN FAVOR OF:
+http://pescuma.mirandaim.ru/miranda/historykeeper
+
+This is a simple plugin that logs status message changes to the history and show popups.
+
+If you are using tabSRMM you can see this events in the message log too.
+
+To report bugs/make suggestions, go to the forum thread: http://forums.miranda-im.org/showthread.php?t=9912
diff --git a/Plugins/smh/Docs/smh_version.txt b/Plugins/smh/Docs/smh_version.txt
new file mode 100644
index 0000000..80e752c
--- /dev/null
+++ b/Plugins/smh/Docs/smh_version.txt
@@ -0,0 +1 @@
+Status Message History 0.1.0.3 \ No newline at end of file
diff --git a/Plugins/smh/ZIP/doit.bat b/Plugins/smh/ZIP/doit.bat
new file mode 100644
index 0000000..6bbcd99
--- /dev/null
+++ b/Plugins/smh/ZIP/doit.bat
@@ -0,0 +1,80 @@
+@echo off
+
+rem Batch file to build and upload files
+rem
+rem TODO: Integration with FL
+
+set name=smh
+
+rem To upload, this var must be set here or in other batch
+rem set ftp=ftp://<user>:<password>@<ftp>/<path>
+
+echo Building %name% ...
+
+msdev ..\%name%.dsp /MAKE "%name% - Win32 Release" /REBUILD
+msdev ..\%name%.dsp /MAKE "%name% - Win32 Unicode Release" /REBUILD
+
+echo Generating files for %name% ...
+
+del *.zip
+del *.dll
+copy ..\..\..\bin\release\Plugins\%name%.dll
+copy ..\..\..\bin\release\Plugins\%name%W.dll
+copy ..\Docs\%name%_changelog.txt
+copy ..\Docs\%name%_version.txt
+copy ..\Docs\%name%_readme.txt
+mkdir Docs
+cd Docs
+del /Q *.*
+copy ..\..\Docs\langpack_%name%.txt
+copy ..\..\Docs\helppack_%name%.txt
+copy ..\..\m_%name%.h
+cd ..
+mkdir src
+cd src
+del /Q *.*
+copy ..\..\*.h
+copy ..\..\*.cpp
+copy ..\..\*.
+copy ..\..\*.rc
+copy ..\..\*.dsp
+copy ..\..\*.dsw
+mkdir Docs
+cd Docs
+del /Q *.*
+copy ..\..\..\Docs\*.*
+cd ..
+cd ..
+
+"C:\Program Files\Filzip\Filzip.exe" -a -rp %name%.zip %name%.dll Docs
+"C:\Program Files\Filzip\Filzip.exe" -a -rp %name%W.zip %name%W.dll Docs
+"C:\Program Files\Filzip\Filzip.exe" -a -rp %name%_src.zip src\*.*
+
+del *.dll
+cd Docs
+del /Q *.*
+cd ..
+rmdir Docs
+cd src
+del /Q *.*
+cd Docs
+del /Q *.*
+cd ..
+rmdir Docs
+cd ..
+rmdir src
+
+if "%ftp%"=="" GOTO END
+
+echo Going to upload files...
+pause
+
+"C:\Program Files\FileZilla\FileZilla.exe" -u .\%name%.zip %ftp% -overwrite -close
+"C:\Program Files\FileZilla\FileZilla.exe" -u .\%name%W.zip %ftp% -overwrite -close
+"C:\Program Files\FileZilla\FileZilla.exe" -u .\%name%_changelog.txt %ftp% -overwrite -close
+"C:\Program Files\FileZilla\FileZilla.exe" -u .\%name%_readme.txt %ftp% -overwrite -close
+"C:\Program Files\FileZilla\FileZilla.exe" -u .\%name%_version.txt %ftp% -overwrite -close
+
+:END
+
+echo Done.
diff --git a/Plugins/smh/commons.h b/Plugins/smh/commons.h
new file mode 100644
index 0000000..5b1453b
--- /dev/null
+++ b/Plugins/smh/commons.h
@@ -0,0 +1,95 @@
+/*
+Copyright (C) 2006 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#ifndef __COMMONS_H__
+# define __COMMONS_H__
+
+
+#include <windows.h>
+#include <tchar.h>
+#include <stdio.h>
+#include <time.h>
+
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+// Miranda headers
+#include <newpluginapi.h>
+#include <m_system.h>
+#include <m_protocols.h>
+#include <m_protosvc.h>
+#include <m_clist.h>
+#include <m_contacts.h>
+#include <m_langpack.h>
+#include <m_database.h>
+#include <m_options.h>
+#include <m_utils.h>
+#include <m_updater.h>
+#include <m_metacontacts.h>
+#include <m_popup.h>
+#include <m_history.h>
+
+#include "../utils/mir_memory.h"
+#include "../utils/mir_options.h"
+
+#include "resource.h"
+#include "m_smh.h"
+#include "options.h"
+#include "popup.h"
+
+
+#define MODULE_NAME "SMH"
+
+
+// Global Variables
+extern HINSTANCE hInst;
+extern PLUGINLINK *pluginLink;
+
+
+#define MAX_REGS(_A_) ( sizeof(_A_) / sizeof(_A_[0]) )
+
+#define DEFAULT_TEMPLATE_CHANGED "changed his/her status message to %s"
+#define DEFAULT_TEMPLATE_REMOVED "removed his/her status message"
+
+
+#ifdef UNICODE
+
+#define TCHAR_TO_CHAR(dest, orig) mir_snprintf(dest, MAX_REGS(dest), "%S", orig)
+#define CHAR_TO_TCHAR(dest, orig) mir_sntprintf(dest, MAX_REGS(dest), "%S", orig)
+
+#else
+
+#define TCHAR_TO_CHAR(dest, orig) lstrcpynA(dest, orig, MAX_REGS(dest))
+#define CHAR_TO_TCHAR(dest, orig) lstrcpynA(dest, orig, MAX_REGS(dest))
+
+#endif
+
+
+BOOL AllowProtocol(const char *proto);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // __COMMONS_H__
diff --git a/Plugins/smh/m_smh.h b/Plugins/smh/m_smh.h
new file mode 100644
index 0000000..c1dddf3
--- /dev/null
+++ b/Plugins/smh/m_smh.h
@@ -0,0 +1,60 @@
+/*
+Copyright (C) 2006 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#ifndef __M_SMH_H__
+# define __M_SMH_H__
+
+
+#define MIID_STATUS_MESSAGE_CHANGE_LOGGER { 0x821be252, 0xe20b, 0x41e7, { 0xa5, 0x1d, 0x3c, 0x34, 0x2e, 0x38, 0xae, 0x22 } }
+#define MIID_STATUS_MESSAGE_CHANGE_NOTIFIER { 0xb628b23b, 0x47ae, 0x430e, { 0x94, 0x81, 0x15, 0x9f, 0xa7, 0x26, 0xc4, 0x3a } }
+
+#define EVENTTYPE_STATUSMESSAGE_CHANGE 9002
+
+/*
+Return TRUE is Status Message History is enabled for this contact
+
+wParam: hContact
+lParam: ignored
+*/
+#define MS_SMH_ENABLED "SMH/Enabled"
+
+
+/*
+Enable Status Message History for a contact
+
+wParam: hContact
+lParam: ignored
+*/
+#define MS_SMH_ENABLE "SMH/Enable"
+
+
+/*
+Disable Status Message History for a contact
+
+wParam: hContact
+lParam: ignored
+*/
+#define MS_SMH_DISABLE "SMH/Disable"
+
+
+
+
+
+#endif // __M_SMH_H__
diff --git a/Plugins/smh/options.cpp b/Plugins/smh/options.cpp
new file mode 100644
index 0000000..f56b45c
--- /dev/null
+++ b/Plugins/smh/options.cpp
@@ -0,0 +1,292 @@
+/*
+Copyright (C) 2006 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#include "commons.h"
+
+
+
+// Prototypes /////////////////////////////////////////////////////////////////////////////////////
+
+HANDLE hOptHook = NULL;
+
+Options opts;
+
+
+static BOOL CALLBACK OptionsDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam);
+static BOOL CALLBACK PopupsDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam);
+
+
+static OptPageControl optionsControls[] = {
+ { &opts.history_enable, CONTROL_CHECKBOX, IDC_HISTORY, "HistoryEnable", TRUE },
+ { &opts.history_only_ansi_if_possible, CONTROL_CHECKBOX, IDC_ANSI, "HistoryOnlyANSIIfPossible", TRUE },
+ { &opts.track_changes, CONTROL_CHECKBOX, IDC_TRACK_CHANGE, "TrackChanges", TRUE },
+ { &opts.template_changed, CONTROL_TEXT, IDC_CHANGED, "TemplateChanged", (DWORD) _T(DEFAULT_TEMPLATE_CHANGED) },
+ { &opts.track_removes, CONTROL_CHECKBOX, IDC_TRACK_REMOVE, "TrackRemoves", TRUE },
+ { &opts.template_removed, CONTROL_TEXT, IDC_REMOVED, "TemplateRemoved", (DWORD) _T(DEFAULT_TEMPLATE_REMOVED) },
+ { &opts.track_only_not_offline, CONTROL_CHECKBOX, IDC_ONLY_NOT_OFFLINE,"TrackOnlyWhenNotOffline", TRUE },
+ { NULL, CONTROL_PROTOCOL_LIST, IDC_PROTOCOLS, "%sEnabled", TRUE, (int) AllowProtocol }
+};
+
+static UINT optionsExpertControls[] = {
+#ifdef UNICODE
+ IDC_ANSI,
+#endif
+ IDC_TRACK_G, IDC_TRACK_CHANGE, IDC_CHANGED_L, IDC_CHANGED, IDC_TRACK_REMOVE, IDC_REMOVED_L, IDC_REMOVED, IDC_ONLY_NOT_OFFLINE,
+ IDC_PROTOCOLS_G, IDC_PROTOCOLS_L, IDC_PROTOCOLS
+};
+
+
+static OptPageControl popupsControls[] = {
+ { &opts.popup_enable, CONTROL_CHECKBOX, IDC_POPUPS, "PopupsEnable", FALSE },
+ { &opts.popup_bkg_color, CONTROL_COLOR, IDC_BGCOLOR, "PopupsBgColor", RGB(255,255,255) },
+ { &opts.popup_text_color, CONTROL_COLOR, IDC_TEXTCOLOR, "PopupsTextColor", RGB(0,0,0) },
+ { &opts.popup_use_win_colors, CONTROL_CHECKBOX, IDC_WINCOLORS, "PopupsWinColors", FALSE },
+ { &opts.popup_use_default_colors, CONTROL_CHECKBOX, IDC_DEFAULTCOLORS, "PopupsDefaultColors", FALSE },
+ { &opts.popup_delay_type, CONTROL_RADIO, IDC_DELAYFROMPU, "PopupsDelayType", POPUP_DELAY_DEFAULT, POPUP_DELAY_DEFAULT },
+ { NULL, CONTROL_RADIO, IDC_DELAYCUSTOM, "PopupsDelayType", POPUP_DELAY_DEFAULT, POPUP_DELAY_CUSTOM },
+ { NULL, CONTROL_RADIO, IDC_DELAYPERMANENT, "PopupsDelayType", POPUP_DELAY_DEFAULT, POPUP_DELAY_PERMANENT },
+ { &opts.popup_timeout, CONTROL_SPIN, IDC_DELAY, "PopupsTimeout", 10, IDC_DELAY_SPIN, (WORD) 1, (WORD) 255 },
+ { &opts.popup_right_click_action, CONTROL_COMBO, IDC_RIGHT_ACTION, "PopupsRightClick", POPUP_ACTION_CLOSEPOPUP },
+ { &opts.popup_left_click_action, CONTROL_COMBO, IDC_LEFT_ACTION, "PopupsLeftClick", POPUP_ACTION_OPENHISTORY }
+};
+
+static UINT popupsExpertControls[] = {
+ IDC_COLOURS_G, IDC_BGCOLOR, IDC_BGCOLOR_L, IDC_TEXTCOLOR, IDC_TEXTCOLOR_L, IDC_WINCOLORS, IDC_DEFAULTCOLORS,
+ IDC_DELAY_G, IDC_DELAYFROMPU, IDC_DELAYCUSTOM, IDC_DELAYPERMANENT, IDC_DELAY, IDC_DELAY_SPIN,
+ IDC_ACTIONS_G, IDC_RIGHT_ACTION_L, IDC_RIGHT_ACTION, IDC_LEFT_ACTION_L, IDC_LEFT_ACTION,
+ IDC_PREV
+};
+
+
+// Functions //////////////////////////////////////////////////////////////////////////////////////
+
+
+int InitOptionsCallback(WPARAM wParam,LPARAM lParam)
+{
+ OPTIONSDIALOGPAGE odp;
+
+ ZeroMemory(&odp,sizeof(odp));
+ odp.cbSize=sizeof(odp);
+ odp.position=0;
+ odp.hInstance=hInst;
+ odp.ptszGroup = TranslateT("History");
+ odp.ptszTitle = TranslateT("Status Message");
+ odp.pfnDlgProc = OptionsDlgProc;
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPTIONS);
+ odp.flags = ODPF_BOLDGROUPS | ODPF_TCHAR;
+ odp.expertOnlyControls = optionsExpertControls;
+ odp.nExpertOnlyControls = MAX_REGS(optionsExpertControls);
+ CallService(MS_OPT_ADDPAGE,wParam,(LPARAM)&odp);
+
+ if(ServiceExists(MS_POPUP_ADDPOPUPEX)
+#ifdef UNICODE
+ || ServiceExists(MS_POPUP_ADDPOPUPW)
+#endif
+ )
+ {
+ ZeroMemory(&odp,sizeof(odp));
+ odp.cbSize=sizeof(odp);
+ odp.position=0;
+ odp.hInstance=hInst;
+ odp.ptszGroup = TranslateT("Popups");
+ odp.ptszTitle = TranslateT("Status Msg Change");
+ odp.pfnDlgProc = PopupsDlgProc;
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_POPUPS);
+ odp.flags = ODPF_BOLDGROUPS | ODPF_TCHAR;
+ odp.expertOnlyControls = popupsExpertControls;
+ odp.nExpertOnlyControls = MAX_REGS(popupsExpertControls);
+ CallService(MS_OPT_ADDPAGE,wParam,(LPARAM)&odp);
+ }
+
+ return 0;
+}
+
+
+void InitOptions()
+{
+ LoadOptions();
+
+ hOptHook = HookEvent(ME_OPT_INITIALISE, InitOptionsCallback);
+
+ //InitMirOptions();
+}
+
+
+void DeInitOptions()
+{
+ UnhookEvent(hOptHook);
+
+ //FreeMirOptions();
+}
+
+
+void LoadOptions()
+{
+ LoadOpts(optionsControls, MAX_REGS(optionsControls), MODULE_NAME);
+ LoadOpts(popupsControls, MAX_REGS(popupsControls), MODULE_NAME);
+}
+
+
+static void OptionsEnableDisableCtrls(HWND hwndDlg)
+{
+ EnableWindow(GetDlgItem(hwndDlg, IDC_CHANGED_L), IsDlgButtonChecked(hwndDlg, IDC_TRACK_CHANGE));
+ EnableWindow(GetDlgItem(hwndDlg, IDC_CHANGED), IsDlgButtonChecked(hwndDlg, IDC_TRACK_CHANGE));
+
+ EnableWindow(GetDlgItem(hwndDlg, IDC_REMOVED_L), IsDlgButtonChecked(hwndDlg, IDC_TRACK_REMOVE));
+ EnableWindow(GetDlgItem(hwndDlg, IDC_REMOVED), IsDlgButtonChecked(hwndDlg, IDC_TRACK_REMOVE));
+}
+
+
+static BOOL CALLBACK OptionsDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ BOOL ret = SaveOptsDlgProc(optionsControls, MAX_REGS(optionsControls), MODULE_NAME, hwndDlg, msg, wParam, lParam);
+
+ switch (msg)
+ {
+ case WM_INITDIALOG:
+ {
+ OptionsEnableDisableCtrls(hwndDlg);
+
+#ifndef UNICODE
+ ShowWindow(GetDlgItem(hwndDlg, IDC_ANSI), SW_HIDE);
+#endif
+ break;
+ }
+ case WM_COMMAND:
+ {
+ switch (LOWORD(wParam))
+ {
+ case IDC_TRACK_REMOVE:
+ case IDC_TRACK_CHANGE:
+ case IDC_ONLY_NOT_OFFLINE:
+ {
+ if (HIWORD(wParam) == BN_CLICKED)
+ OptionsEnableDisableCtrls(hwndDlg);
+
+ break;
+ }
+ }
+ break;
+ }
+ }
+
+ return ret;
+}
+
+
+static void PopupsEnableDisableCtrls(HWND hwndDlg)
+{
+ BOOL enabled = IsDlgButtonChecked(hwndDlg, IDC_POPUPS);
+
+ EnableWindow(GetDlgItem(hwndDlg, IDC_COLOURS_G), enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_BGCOLOR_L), enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_TEXTCOLOR_L), enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_DELAY_G), enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_DELAYFROMPU), enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_DELAYCUSTOM), enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_DELAYPERMANENT), enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_ACTIONS_G), enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_RIGHT_ACTION_L), enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_RIGHT_ACTION), enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_LEFT_ACTION_L), enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_LEFT_ACTION), enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_PREV), enabled);
+
+ EnableWindow(GetDlgItem(hwndDlg, IDC_BGCOLOR), enabled &&
+ !IsDlgButtonChecked(hwndDlg, IDC_WINCOLORS) &&
+ !IsDlgButtonChecked(hwndDlg, IDC_DEFAULTCOLORS));
+ EnableWindow(GetDlgItem(hwndDlg, IDC_TEXTCOLOR), enabled &&
+ !IsDlgButtonChecked(hwndDlg, IDC_WINCOLORS) &&
+ !IsDlgButtonChecked(hwndDlg, IDC_DEFAULTCOLORS));
+ EnableWindow(GetDlgItem(hwndDlg, IDC_DEFAULTCOLORS), enabled &&
+ !IsDlgButtonChecked(hwndDlg, IDC_WINCOLORS));
+ EnableWindow(GetDlgItem(hwndDlg, IDC_WINCOLORS), enabled &&
+ !IsDlgButtonChecked(hwndDlg, IDC_DEFAULTCOLORS));
+
+ EnableWindow(GetDlgItem(hwndDlg, IDC_DELAY), enabled &&
+ IsDlgButtonChecked(hwndDlg, IDC_DELAYCUSTOM));
+}
+
+
+static BOOL CALLBACK PopupsDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg)
+ {
+ case WM_INITDIALOG:
+ {
+ SendDlgItemMessage(hwndDlg, IDC_RIGHT_ACTION, CB_ADDSTRING, 0, (LONG) TranslateT("Do nothing"));
+ SendDlgItemMessage(hwndDlg, IDC_RIGHT_ACTION, CB_ADDSTRING, 0, (LONG) TranslateT("Close popup"));
+ SendDlgItemMessage(hwndDlg, IDC_RIGHT_ACTION, CB_ADDSTRING, 0, (LONG) TranslateT("Show history"));
+
+ SendDlgItemMessage(hwndDlg, IDC_LEFT_ACTION, CB_ADDSTRING, 0, (LONG) TranslateT("Do nothing"));
+ SendDlgItemMessage(hwndDlg, IDC_LEFT_ACTION, CB_ADDSTRING, 0, (LONG) TranslateT("Close popup"));
+ SendDlgItemMessage(hwndDlg, IDC_LEFT_ACTION, CB_ADDSTRING, 0, (LONG) TranslateT("Show history"));
+
+ // Needs to be called here in this case
+ BOOL ret = SaveOptsDlgProc(popupsControls, MAX_REGS(popupsControls), MODULE_NAME, hwndDlg, msg, wParam, lParam);
+
+ PopupsEnableDisableCtrls(hwndDlg);
+
+ return ret;
+ }
+ case WM_COMMAND:
+ {
+ switch (LOWORD(wParam))
+ {
+ case IDC_POPUPS:
+ case IDC_WINCOLORS:
+ case IDC_DEFAULTCOLORS:
+ case IDC_DELAYFROMPU:
+ case IDC_DELAYPERMANENT:
+ case IDC_DELAYCUSTOM:
+ {
+ if (HIWORD(wParam) == BN_CLICKED)
+ PopupsEnableDisableCtrls(hwndDlg);
+
+ break;
+ }
+ case IDC_PREV:
+ {
+ Options op = opts;
+
+ if (IsDlgButtonChecked(hwndDlg, IDC_DELAYFROMPU))
+ op.popup_delay_type = POPUP_DELAY_DEFAULT;
+ else if (IsDlgButtonChecked(hwndDlg, IDC_DELAYCUSTOM))
+ op.popup_delay_type = POPUP_DELAY_CUSTOM;
+ else if (IsDlgButtonChecked(hwndDlg, IDC_DELAYPERMANENT))
+ op.popup_delay_type = POPUP_DELAY_PERMANENT;
+
+ op.popup_timeout = GetDlgItemInt(hwndDlg,IDC_DELAY, NULL, FALSE);
+ op.popup_bkg_color = SendDlgItemMessage(hwndDlg,IDC_BGCOLOR,CPM_GETCOLOUR,0,0);
+ op.popup_text_color = SendDlgItemMessage(hwndDlg,IDC_TEXTCOLOR,CPM_GETCOLOUR,0,0);
+ op.popup_use_win_colors = IsDlgButtonChecked(hwndDlg, IDC_WINCOLORS) != 0;
+ op.popup_use_default_colors = IsDlgButtonChecked(hwndDlg, IDC_DEFAULTCOLORS) != 0;
+
+ ShowTestPopup(TranslateT("Test Contact"), TranslateT("Test description"), &op);
+
+ break;
+ }
+ }
+ break;
+ }
+ }
+
+ return SaveOptsDlgProc(popupsControls, MAX_REGS(popupsControls), MODULE_NAME, hwndDlg, msg, wParam, lParam);
+}
diff --git a/Plugins/smh/options.h b/Plugins/smh/options.h
new file mode 100644
index 0000000..da0e1a6
--- /dev/null
+++ b/Plugins/smh/options.h
@@ -0,0 +1,89 @@
+/*
+Copyright (C) 2006 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#ifndef __OPTIONS_H__
+# define __OPTIONS_H__
+
+
+#include "commons.h"
+
+#include <windows.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#define POPUP_ACTION_DONOTHING 0
+#define POPUP_ACTION_CLOSEPOPUP 1
+#define POPUP_ACTION_OPENHISTORY 2
+
+#define POPUP_DELAY_DEFAULT 0
+#define POPUP_DELAY_CUSTOM 1
+#define POPUP_DELAY_PERMANENT 2
+
+
+struct Options {
+ // Templates
+ TCHAR template_changed[1024];
+ TCHAR template_removed[1024];
+
+ // Track
+ BYTE track_changes;
+ BYTE track_removes;
+ BYTE track_only_not_offline;
+
+ // History
+ BYTE history_enable;
+ BYTE history_only_ansi_if_possible;
+
+ // Popup
+ BYTE popup_enable;
+ WORD popup_delay_type;
+ WORD popup_timeout;
+ BYTE popup_use_win_colors;
+ BYTE popup_use_default_colors;
+ COLORREF popup_bkg_color;
+ COLORREF popup_text_color;
+ WORD popup_left_click_action;
+ WORD popup_right_click_action;
+};
+
+extern Options opts;
+
+
+// Initializations needed by options
+void InitOptions();
+
+// Deinitializations needed by options
+void DeInitOptions();
+
+
+// Loads the options from DB
+// It don't need to be called, except in some rare cases
+void LoadOptions();
+
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // __OPTIONS_H__
diff --git a/Plugins/smh/popup.cpp b/Plugins/smh/popup.cpp
new file mode 100644
index 0000000..4e40814
--- /dev/null
+++ b/Plugins/smh/popup.cpp
@@ -0,0 +1,319 @@
+/*
+Copyright (C) 2005 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#include "commons.h"
+
+
+
+// Prototypes /////////////////////////////////////////////////////////////////////////////////////
+
+#define WMU_ACTION (WM_USER + 1)
+
+
+LRESULT CALLBACK PopupWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
+
+HWND hPopupWindow = NULL;
+
+
+static LRESULT CALLBACK PopupDlgProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
+static LRESULT CALLBACK DumbPopupDlgProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
+
+
+
+// Functions //////////////////////////////////////////////////////////////////////////////////////
+
+
+// Initializations needed by popups
+void InitPopups()
+{
+ // window needed for popup commands
+ hPopupWindow = CreateWindowEx(WS_EX_TOOLWINDOW, _T("static"), _T(MODULE_NAME) _T("_PopupWindow"),
+ 0, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, HWND_DESKTOP,
+ NULL, hInst, NULL);
+ SetWindowLong(hPopupWindow, GWL_WNDPROC, (LONG)(WNDPROC)PopupWndProc);
+}
+
+
+// Deinitializations needed by popups
+void DeInitPopups()
+{
+}
+
+
+// Show an error popup
+void ShowErrPopup(const TCHAR *description, const TCHAR *title)
+{
+ ShowPopupEx(NULL, title == NULL ? _T(MODULE_NAME) _T(" Error") : title, description,
+ NULL, POPUP_TYPE_ERROR, NULL);
+}
+
+
+void ShowTestPopup(const TCHAR *title, const TCHAR *description, const Options *op)
+{
+ ShowPopupEx(NULL, title, description, NULL, POPUP_TYPE_TEST, op);
+}
+
+
+void ShowPopup(HANDLE hContact, const TCHAR *title, const TCHAR *description)
+{
+ ShowPopupEx(hContact, title, description, hContact, POPUP_TYPE_NORMAL, &opts);
+}
+
+
+// Show an popup
+void ShowPopupEx(HANDLE hContact, const TCHAR *title, const TCHAR *description,
+ void *plugin_data, int type, const Options *op)
+{
+#ifdef UNICODE
+ if(ServiceExists(MS_POPUP_ADDPOPUPW))
+ {
+ // Make popup
+ POPUPDATAW ppd;
+
+ ZeroMemory(&ppd, sizeof(ppd));
+ ppd.lchContact = hContact;
+ ppd.lchIcon = (HICON) LoadImage(GetModuleHandle(NULL),MAKEINTRESOURCE(174),IMAGE_ICON,16,16,LR_DEFAULTCOLOR);
+
+ if (title != NULL)
+ lstrcpyn(ppd.lpwzContactName, title, MAX_REGS(ppd.lpwzContactName));
+ else if (hContact != NULL)
+ lstrcpyn(ppd.lpwzContactName, (TCHAR *) CallService(MS_CLIST_GETCONTACTDISPLAYNAME, (WPARAM)hContact, GCDNF_TCHAR),
+ MAX_REGS(ppd.lpwzContactName));
+
+ if (description != NULL)
+ lstrcpyn(ppd.lpwzText, description, MAX_REGS(ppd.lpwzText));
+
+ if (type == POPUP_TYPE_NORMAL || type == POPUP_TYPE_TEST)
+ {
+ if (op->popup_use_default_colors)
+ {
+ ppd.colorBack = 0;
+ ppd.colorText = 0;
+ }
+ else if (op->popup_use_win_colors)
+ {
+ ppd.colorBack = GetSysColor(COLOR_BTNFACE);
+ ppd.colorText = GetSysColor(COLOR_WINDOWTEXT);
+ }
+ else
+ {
+ ppd.colorBack = op->popup_bkg_color;
+ ppd.colorText = op->popup_text_color;
+ }
+ }
+ else // if (type == POPUP_TYPE_ERROR)
+ {
+ ppd.colorBack = RGB(200,0,0);
+ ppd.colorText = RGB(255,255,255);
+ }
+
+ if (type == POPUP_TYPE_NORMAL)
+ {
+ ppd.PluginWindowProc = PopupDlgProc;
+ ppd.PluginData = plugin_data;
+ }
+ else // if (type == POPUP_TYPE_TEST || type == POPUP_TYPE_ERROR)
+ {
+ ppd.PluginWindowProc = DumbPopupDlgProc;
+ }
+
+ if (type == POPUP_TYPE_NORMAL || type == POPUP_TYPE_TEST)
+ {
+ switch (op->popup_delay_type)
+ {
+ case POPUP_DELAY_CUSTOM:
+ ppd.iSeconds = opts.popup_timeout;
+ break;
+
+ case POPUP_DELAY_PERMANENT:
+ ppd.iSeconds = -1;
+ break;
+
+ //case POPUP_DELAY_DEFAULT:
+ default:
+ ppd.iSeconds = 0;
+ break;
+ }
+ }
+ else // if (type == POPUP_TYPE_ERROR)
+ {
+ ppd.iSeconds = 0;
+ }
+
+ // Now that every field has been filled, we want to see the popup.
+ CallService(MS_POPUP_ADDPOPUPW, (WPARAM)&ppd,0);
+ }
+ else
+#endif
+ if(ServiceExists(MS_POPUP_ADDPOPUPEX))
+ {
+ // Make popup
+ POPUPDATAEX ppd;
+
+ ZeroMemory(&ppd, sizeof(ppd));
+ ppd.lchContact = hContact;
+ ppd.lchIcon = (HICON) LoadImage(GetModuleHandle(NULL),MAKEINTRESOURCE(174),IMAGE_ICON,16,16,LR_DEFAULTCOLOR);
+
+ if (title != NULL)
+ TCHAR_TO_CHAR(ppd.lpzContactName, title);
+ else
+ lstrcpynA(ppd.lpzContactName, (char *) CallService(MS_CLIST_GETCONTACTDISPLAYNAME, (WPARAM)hContact, 0),
+ MAX_REGS(ppd.lpzContactName));
+
+ if (description != NULL)
+ TCHAR_TO_CHAR(ppd.lpzText, description);
+
+ if (type == POPUP_TYPE_NORMAL || type == POPUP_TYPE_TEST)
+ {
+ if (op->popup_use_default_colors)
+ {
+ ppd.colorBack = 0;
+ ppd.colorText = 0;
+ }
+ else if (op->popup_use_win_colors)
+ {
+ ppd.colorBack = GetSysColor(COLOR_BTNFACE);
+ ppd.colorText = GetSysColor(COLOR_WINDOWTEXT);
+ }
+ else
+ {
+ ppd.colorBack = op->popup_bkg_color;
+ ppd.colorText = op->popup_text_color;
+ }
+ }
+ else // if (type == POPUP_TYPE_ERROR)
+ {
+ ppd.colorBack = RGB(200,0,0);
+ ppd.colorText = RGB(255,255,255);
+ }
+
+ if (type == POPUP_TYPE_NORMAL)
+ {
+ ppd.PluginWindowProc = PopupDlgProc;
+ ppd.PluginData = plugin_data;
+ }
+ else // if (type == POPUP_TYPE_TEST || type == POPUP_TYPE_ERROR)
+ {
+ ppd.PluginWindowProc = DumbPopupDlgProc;
+ }
+
+ if (type == POPUP_TYPE_NORMAL || type == POPUP_TYPE_TEST)
+ {
+ switch (op->popup_delay_type)
+ {
+ case POPUP_DELAY_CUSTOM:
+ ppd.iSeconds = opts.popup_timeout;
+ break;
+
+ case POPUP_DELAY_PERMANENT:
+ ppd.iSeconds = -1;
+ break;
+
+ //case POPUP_DELAY_DEFAULT:
+ default:
+ ppd.iSeconds = 0;
+ break;
+ }
+ }
+ else // if (type == POPUP_TYPE_ERROR)
+ {
+ ppd.iSeconds = 0;
+ }
+
+ // Now that every field has been filled, we want to see the popup.
+ CallService(MS_POPUP_ADDPOPUPEX, (WPARAM)&ppd,0);
+ }
+}
+
+
+// Handle to the hidden windows to handle actions for popup clicks
+// wParam has the number of MOTD in case of WMU_SHOW_MOTD_DETAILS
+LRESULT CALLBACK PopupWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ if (uMsg == WMU_ACTION)
+ {
+ if (lParam == POPUP_ACTION_OPENHISTORY)
+ {
+ CallService(MS_HISTORY_SHOWCONTACTHISTORY, wParam, 0);
+ }
+ }
+ return DefWindowProc(hWnd, uMsg, wParam, lParam);
+}
+
+
+// Handle to popup events
+static LRESULT CALLBACK PopupDlgProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ switch(message) {
+ case WM_COMMAND:
+ {
+ SendMessage(hPopupWindow, WMU_ACTION, (WPARAM)PUGetPluginData(hWnd), opts.popup_left_click_action);
+
+ if (opts.popup_left_click_action != POPUP_ACTION_DONOTHING)
+ PUDeletePopUp(hWnd);
+
+ return TRUE;
+ }
+
+ case WM_CONTEXTMENU:
+ {
+ SendMessage(hPopupWindow, WMU_ACTION, (WPARAM)PUGetPluginData(hWnd), opts.popup_right_click_action);
+
+ if (opts.popup_right_click_action != POPUP_ACTION_DONOTHING)
+ PUDeletePopUp(hWnd);
+
+ return TRUE;
+ }
+
+ case UM_FREEPLUGINDATA:
+ {
+ return TRUE;
+ }
+ }
+
+ return DefWindowProc(hWnd, message, wParam, lParam);
+}
+
+
+// Handle to popup events
+static LRESULT CALLBACK DumbPopupDlgProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ switch(message) {
+ case WM_COMMAND:
+ {
+ PUDeletePopUp(hWnd);
+ return TRUE;
+ }
+
+ case WM_CONTEXTMENU:
+ {
+ PUDeletePopUp(hWnd);
+ return TRUE;
+ }
+
+ case UM_FREEPLUGINDATA:
+ {
+ return TRUE;
+ }
+ }
+
+ return DefWindowProc(hWnd, message, wParam, lParam);
+}
+
diff --git a/Plugins/smh/popup.h b/Plugins/smh/popup.h
new file mode 100644
index 0000000..090657e
--- /dev/null
+++ b/Plugins/smh/popup.h
@@ -0,0 +1,60 @@
+/*
+Copyright (C) 2005 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#ifndef __POPUP_H__
+# define __POPUP_H__
+
+#include "commons.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+// Initializations needed by popups
+void InitPopups();
+
+// Deinitializations needed by popups
+void DeInitPopups();
+
+
+#define POPUP_TYPE_NORMAL 0
+#define POPUP_TYPE_TEST 1
+#define POPUP_TYPE_ERROR 2
+
+// Show an popup
+void ShowPopup(HANDLE hContact, const TCHAR *title, const TCHAR *description);
+
+// Show an test
+void ShowTestPopup(const TCHAR *title, const TCHAR *description, const Options *op);
+
+// Show an error popup
+void ShowErrPopup(const char *description, const char *title = NULL);
+
+void ShowPopupEx(HANDLE hContact, const TCHAR *title, const TCHAR *description,
+ void *plugin_data, int type, const Options *op);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif // __POPUP_H__
diff --git a/Plugins/smh/resource.h b/Plugins/smh/resource.h
new file mode 100644
index 0000000..dda44d5
--- /dev/null
+++ b/Plugins/smh/resource.h
@@ -0,0 +1,55 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Developer Studio generated include file.
+// Used by resource.rc
+//
+#define IDD_OPTIONS 119
+#define IDD_POPUPS 120
+#define IDC_DELAY 1001
+#define IDC_WINCOLORS 1002
+#define IDC_DEFAULTCOLORS 1003
+#define IDC_BGCOLOR 1004
+#define IDC_TEXTCOLOR 1005
+#define IDC_PREV 1006
+#define IDC_DELAYFROMPU 1007
+#define IDC_DELAYCUSTOM 1008
+#define IDC_DELAYPERMANENT 1009
+#define IDC_RIGHT_ACTION 1022
+#define IDC_LEFT_ACTION 1023
+#define IDC_PROTOCOLS 1041
+#define IDC_CHANGED 1058
+#define IDC_REMOVED 1059
+#define IDC_CHECK1 1060
+#define IDC_POPUPS 1060
+#define IDC_CHECK2 1061
+#define IDC_DELAY_SPIN 1061
+#define IDC_HISTORY 1061
+#define IDC_ANSI 1062
+#define IDC_TRACK_G 1063
+#define IDC_CHANGED_L 1064
+#define IDC_REMOVED_L 1065
+#define IDC_PROTOCOLS_G 1066
+#define IDC_PROTOCOLS_L 1067
+#define IDC_COLOURS_G 1068
+#define IDC_TRACK_CHANGE 1068
+#define IDC_BGCOLOR_L 1069
+#define IDC_TRACK_REMOVE 1069
+#define IDC_TEXTCOLOR_L 1070
+#define IDC_ONLY_NOT_OFFLINE 1070
+#define IDC_DELAY_G 1071
+#define IDC_ACTIONS_G 1072
+#define IDC_RIGHT_ACTION_L 1073
+#define IDC_LEFT_ACTION_L 1074
+#define IDC_STATIC -1
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NO_MFC 1
+#define _APS_3D_CONTROLS 1
+#define _APS_NEXT_RESOURCE_VALUE 120
+#define _APS_NEXT_COMMAND_VALUE 40004
+#define _APS_NEXT_CONTROL_VALUE 1075
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/Plugins/smh/resource.rc b/Plugins/smh/resource.rc
new file mode 100644
index 0000000..2b55fbb
--- /dev/null
+++ b/Plugins/smh/resource.rc
@@ -0,0 +1,180 @@
+//Microsoft Developer Studio generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "resource.h"
+#include "winresrc.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (U.S.) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page(1252)
+#endif //_WIN32
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Dialog
+//
+
+IDD_OPTIONS DIALOG DISCARDABLE 0, 0, 314, 240
+STYLE DS_SHELLFONT | WS_CHILD | WS_VISIBLE
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg"
+BEGIN
+ GROUPBOX " History ",IDC_STATIC,3,3,308,44
+ CONTROL "Log changes to history",IDC_HISTORY,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,12,16,291,11
+ CONTROL "Don't save Unicode parts for 7bit ANSI history (saves db space)",
+ IDC_ANSI,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,30,291,
+ 11
+ GROUPBOX " Tracking ",IDC_TRACK_G,3,51,308,90
+ CONTROL "Track when contacts change their status messages",
+ IDC_TRACK_CHANGE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,
+ 12,63,291,11
+ RTEXT "Template:",IDC_CHANGED_L,11,76,55,10
+ EDITTEXT IDC_CHANGED,72,75,233,13,ES_AUTOHSCROLL
+ CONTROL "Track when contacts remove their status messages",
+ IDC_TRACK_REMOVE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,
+ 12,92,291,11
+ RTEXT "Template:",IDC_REMOVED_L,11,106,55,10
+ EDITTEXT IDC_REMOVED,72,105,233,13,ES_AUTOHSCROLL
+ CONTROL "Only when the contact is not offline",
+ IDC_ONLY_NOT_OFFLINE,"Button",BS_AUTOCHECKBOX |
+ WS_TABSTOP,12,123,291,11
+ GROUPBOX " Protocols ",IDC_PROTOCOLS_G,3,144,308,93
+ LTEXT "Enable tracking for these protocols:",
+ IDC_PROTOCOLS_L,13,156,291,11
+ CONTROL "List1",IDC_PROTOCOLS,"SysListView32",LVS_REPORT |
+ LVS_SINGLESEL | LVS_SORTASCENDING | LVS_NOCOLUMNHEADER |
+ LVS_NOSORTHEADER | WS_BORDER | WS_TABSTOP,12,168,158,62
+END
+
+IDD_POPUPS DIALOG DISCARDABLE 0, 0, 314, 240
+STYLE DS_SHELLFONT | WS_CHILD | WS_VISIBLE
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg"
+BEGIN
+ CONTROL "Enable popups",IDC_POPUPS,"Button",BS_AUTOCHECKBOX |
+ WS_TABSTOP,3,3,308,12
+ GROUPBOX " Colours ",IDC_COLOURS_G,3,25,158,75
+ CONTROL "",IDC_BGCOLOR,"ColourPicker",WS_TABSTOP,11,37,35,14
+ LTEXT "Background colour",IDC_BGCOLOR_L,55,41,66,8
+ CONTROL "",IDC_TEXTCOLOR,"ColourPicker",WS_TABSTOP,11,55,35,14
+ LTEXT "Text colour",IDC_TEXTCOLOR_L,55,59,66,8
+ CONTROL "Use Windows colours",IDC_WINCOLORS,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,11,74,108,10
+ CONTROL "Use default colours",IDC_DEFAULTCOLORS,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,11,84,107,10
+ GROUPBOX " Delay ",IDC_DELAY_G,166,25,145,75
+ CONTROL "From popup plugin",IDC_DELAYFROMPU,"Button",
+ BS_AUTORADIOBUTTON | WS_GROUP,175,38,122,10
+ CONTROL "Custom",IDC_DELAYCUSTOM,"Button",BS_AUTORADIOBUTTON,175,
+ 52,54,10
+ CONTROL "Permanent",IDC_DELAYPERMANENT,"Button",
+ BS_AUTORADIOBUTTON,175,66,122,10
+ EDITTEXT IDC_DELAY,233,50,31,14,ES_AUTOHSCROLL | ES_NUMBER
+ CONTROL "Spin1",IDC_DELAY_SPIN,"msctls_updown32",UDS_SETBUDDYINT |
+ UDS_ALIGNRIGHT | UDS_ARROWKEYS | UDS_NOTHOUSANDS |
+ UDS_HOTTRACK,268,51,11,11
+ GROUPBOX " Actions ",IDC_ACTIONS_G,3,103,308,47
+ RTEXT "On right click:",IDC_RIGHT_ACTION_L,13,118,62,9
+ COMBOBOX IDC_RIGHT_ACTION,83,116,156,60,CBS_DROPDOWNLIST |
+ WS_VSCROLL | WS_TABSTOP
+ RTEXT "On left click:",IDC_LEFT_ACTION_L,13,132,62,9
+ COMBOBOX IDC_LEFT_ACTION,83,132,156,60,CBS_DROPDOWNLIST |
+ WS_VSCROLL | WS_TABSTOP
+ PUSHBUTTON "Preview",IDC_PREV,131,161,50,14
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// DESIGNINFO
+//
+
+#ifdef APSTUDIO_INVOKED
+GUIDELINES DESIGNINFO DISCARDABLE
+BEGIN
+ IDD_OPTIONS, DIALOG
+ BEGIN
+ LEFTMARGIN, 3
+ RIGHTMARGIN, 311
+ TOPMARGIN, 3
+ BOTTOMMARGIN, 237
+ END
+
+ IDD_POPUPS, DIALOG
+ BEGIN
+ LEFTMARGIN, 3
+ RIGHTMARGIN, 311
+ TOPMARGIN, 3
+ BOTTOMMARGIN, 237
+ END
+END
+#endif // APSTUDIO_INVOKED
+
+#endif // English (U.S.) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+/////////////////////////////////////////////////////////////////////////////
+// English (Canada) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENC)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_CAN
+#pragma code_page(1252)
+#endif //_WIN32
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "resource.h\0"
+END
+
+2 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "#include ""resource.h""\r\n"
+ "#include ""winresrc.h""\r\n"
+ "\0"
+END
+
+3 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "\r\n"
+ "\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+#endif // English (Canada) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
+
diff --git a/Plugins/smh/sdk/m_metacontacts.h b/Plugins/smh/sdk/m_metacontacts.h
new file mode 100644
index 0000000..1da12b9
--- /dev/null
+++ b/Plugins/smh/sdk/m_metacontacts.h
@@ -0,0 +1,162 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright © 2004 Universite Louis PASTEUR, STRASBOURG.
+Copyright © 2004 Scott Ellis (www.scottellis.com.au mail@scottellis.com.au)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef M_METACONTACTS_H__
+#define M_METACONTACTS_H__ 1
+
+//get the handle for a contact's parent metacontact
+//wParam=(HANDLE)hSubContact
+//lParam=0
+//returns a handle to the parent metacontact, or null if this contact is not a subcontact
+#define MS_MC_GETMETACONTACT "MetaContacts/GetMeta"
+
+//gets the handle for the default contact
+//wParam=(HANDLE)hMetaContact
+//lParam=0
+//returns a handle to the default contact, or null on failure
+#define MS_MC_GETDEFAULTCONTACT "MetaContacts/GetDefault"
+
+//gets the contact number for the default contact
+//wParam=(HANDLE)hMetaContact
+//lParam=0
+//returns a DWORD contact number, or -1 on failure
+#define MS_MC_GETDEFAULTCONTACTNUM "MetaContacts/GetDefaultNum"
+
+//gets the handle for the 'most online' contact
+//wParam=(HANDLE)hMetaContact
+//lParam=0
+//returns a handle to the 'most online' contact
+#define MS_MC_GETMOSTONLINECONTACT "MetaContacts/GetMostOnline"
+
+//gets the number of subcontacts for a metacontact
+//wParam=(HANDLE)hMetaContact
+//lParam=0
+//returns a DWORD representing the number of subcontacts for the given metacontact
+#define MS_MC_GETNUMCONTACTS "MetaContacts/GetNumContacts"
+
+//gets the handle of a subcontact, using the subcontact's number
+//wParam=(HANDLE)hMetaContact
+//lParam=(DWORD)contact number
+//returns a handle to the specified subcontact
+#define MS_MC_GETSUBCONTACT "MetaContacts/GetSubContact"
+
+//sets the default contact, using the subcontact's contact number
+//wParam=(HANDLE)hMetaContact
+//lParam=(DWORD)contact number
+//returns 0 on success
+#define MS_MC_SETDEFAULTCONTACTNUM "MetaContacts/SetDefault"
+
+//sets the default contact, using the subcontact's handle
+//wParam=(HANDLE)hMetaContact
+//lParam=(HANDLE)hSubcontact
+//returns 0 on success
+#define MS_MC_SETDEFAULTCONTACT "MetaContacts/SetDefaultByHandle"
+
+//forces the metacontact to send using a specific subcontact, using the subcontact's contact number
+//wParam=(HANDLE)hMetaContact
+//lParam=(DWORD)contact number
+//returns 0 on success
+#define MS_MC_FORCESENDCONTACTNUM "MetaContacts/ForceSendContact"
+
+//forces the metacontact to send using a specific subcontact, using the subcontact's handle
+//wParam=(HANDLE)hMetaContact
+//lParam=(HANDLE)hSubcontact
+//returns 0 on success (will fail if 'force default' is in effect)
+#define MS_MC_FORCESENDCONTACT "MetaContacts/ForceSendContactByHandle"
+
+//'unforces' the metacontact to send using a specific subcontact
+//wParam=(HANDLE)hMetaContact
+//lParam=0
+//returns 0 on success (will fail if 'force default' is in effect)
+#define MS_MC_UNFORCESENDCONTACT "MetaContacts/UnforceSendContact"
+
+//'forces' or 'unforces' (i.e. toggles) the metacontact to send using it's default contact
+// overrides (and clears) 'force send' above, and will even force use of offline contacts
+// will send ME_MC_FORCESEND or ME_MC_UNFORCESEND event
+//wParam=(HANDLE)hMetaContact
+//lParam=0
+//returns 1(true) or 0(false) representing new state of 'force default'
+#define MS_MC_FORCEDEFAULT "MetaContacts/ForceSendDefault"
+
+// method to get state of 'force' for a metacontact
+// wParam=(HANDLE)hMetaContact
+// lParam= (DWORD)&contact_number or NULL
+//
+// if lparam supplied, the contact_number of the contatct 'in force' will be copied to the address it points to,
+// or if none is in force, the value (DWORD)-1 will be copied
+// (v0.8.0.8+ returns 1 if 'force default' is true with *lParam == default contact number, else returns 0 with *lParam as above)
+#define MS_MC_GETFORCESTATE "MetaContacts/GetForceState"
+
+// fired when a metacontact's default contact changes (fired upon creation of metacontact also, when default is initially set)
+// wParam=(HANDLE)hMetaContact
+// lParam=(HANDLE)hDefaultContact
+#define ME_MC_DEFAULTTCHANGED "MetaContacts/DefaultChanged"
+
+// fired when a metacontact's subcontacts change (fired upon creation of metacontact, when contacts are added or removed, and when
+// contacts are reordered) - a signal to re-read metacontact data
+// wParam=(HANDLE)hMetaContact
+// lParam=0
+#define ME_MC_SUBCONTACTSCHANGED "MetaContacts/SubcontactsChanged"
+
+// fired when a metacontact is forced to send using a specific subcontact
+// wParam=(HANDLE)hMetaContact
+// lParam=(HANDLE)hForceContact
+#define ME_MC_FORCESEND "MetaContacts/ForceSend"
+
+// fired when a metacontact is 'unforced' to send using a specific subcontact
+// wParam=(HANDLE)hMetaContact
+// lParam=0
+#define ME_MC_UNFORCESEND "MetaContacts/UnforceSend"
+
+// method to get protocol name - used to be sure you're dealing with a "real" metacontacts plugin :)
+// wParam=lParam=0
+#define MS_MC_GETPROTOCOLNAME "MetaContacts/GetProtoName"
+
+
+// added 0.9.5.0 (22/3/05)
+// wParam=(HANDLE)hContact
+// lParam=0
+// convert a given contact into a metacontact
+#define MS_MC_CONVERTTOMETA "MetaContacts/ConvertToMetacontact"
+
+// added 0.9.5.0 (22/3/05)
+// wParam=(HANDLE)hContact
+// lParam=(HANDLE)hMeta
+// add an existing contact to a metacontact
+#define MS_MC_ADDTOMETA "MetaContacts/AddToMetacontact"
+
+// added 0.9.5.0 (22/3/05)
+// wParam=0
+// lParam=(HANDLE)hContact
+// remove a contact from a metacontact
+#define MS_MC_REMOVEFROMMETA "MetaContacts/RemoveFromMetacontact"
+
+
+// added 0.9.13.2 (6/10/05)
+// wParam=(BOOL)disable
+// lParam=0
+// enable/disable the 'hidden group hack' - for clists that support subcontact hiding using 'IsSubcontact' setting
+// should be called once in the clist 'onmodulesloaded' event handler (which, since it's loaded after the db, will be called
+// before the metacontact onmodulesloaded handler where the subcontact hiding is usually done)
+#define MS_MC_DISABLEHIDDENGROUP "MetaContacts/DisableHiddenGroup"
+
+#endif
diff --git a/Plugins/smh/sdk/m_updater.h b/Plugins/smh/sdk/m_updater.h
new file mode 100644
index 0000000..371b743
--- /dev/null
+++ b/Plugins/smh/sdk/m_updater.h
@@ -0,0 +1,146 @@
+#ifndef _M_UPDATER_H
+#define _M_UPDATER_H
+
+// NOTES:
+// - For langpack updates, include a string of the following format in the langpack text file:
+// ";FLID: <file listing name> <version>"
+// version must be four numbers seperated by '.', in the range 0-255 inclusive
+// - Updater will disable plugins that are downloaded but were not active prior to the update (this is so that, if an archive contains e.g. ansi and
+// unicode versions, the correct plugin will be the only one active after the new version is installed)...so if you add a support plugin, you may need
+// to install an ini file to make the plugin activate when miranda restarts after the update
+// - Updater will replace all dlls that have the same internal shortName as a downloaded update dll (this is so that msn1.dll and msn2.dll, for example,
+// will both be updated) - so if you have a unicode and a non-unicode version of a plugin in your archive, you should make the internal names different (which will break automatic
+// updates from the file listing if there is only one file listing entry for both versions, unless you use the 'MS_UPDATE_REGISTER' service below)
+// - Updater will install all files in the root of the archive into the plugins folder, except for langpack files that contain the FLID string which go into the root folder (same
+// folder as miranda32.exe)...all folders in the archive will also be copied to miranda's root folder, and their contents transferred into the new folders. The only exception is a
+// special folder called 'root_files' - if there is a folder by that name in the archive, it's contents will also be copied into miranda's root folder - this is intended to be used
+// to install additional dlls etc that a plugin may require)
+
+// if you set Update.szUpdateURL to the following value when registering, as well as setting your beta site and version data,
+// Updater will ignore szVersionURL and pbVersionPrefix, and attempt to find the file listing URL's from the backend XML data.
+// for this to work, the plugin name in pluginInfo.shortName must match the file listing exactly (except for case)
+#define UPDATER_AUTOREGISTER "UpdaterAUTOREGISTER"
+// Updater will also use the backend xml data if you provide URL's that reference the miranda file listing for updates (so you can use that method
+// if e.g. your plugin shortName does not match the file listing) - it will grab the file listing id from the end of these URLs
+
+typedef struct Update_tag {
+ int cbSize;
+ char *szComponentName; // component name as it will appear in the UI (will be translated before displaying)
+
+ char *szVersionURL; // URL where the current version can be found (NULL to disable)
+ BYTE *pbVersionPrefix; // bytes occuring in VersionURL before the version, used to locate the version information within the URL data
+ // (note that this URL could point at a binary file - dunno why, but it could :)
+ int cpbVersionPrefix; // number of bytes pointed to by pbVersionPrefix
+ char *szUpdateURL; // URL where dll/zip is located
+ // set to UPDATER_AUTOREGISTER if you want Updater to find the file listing URLs (ensure plugin shortName matches file listing!)
+
+ char *szBetaVersionURL; // URL where the beta version can be found (NULL to disable betas)
+ BYTE *pbBetaVersionPrefix; // bytes occuring in VersionURL before the version, used to locate the version information within the URL data
+ int cpbBetaVersionPrefix; // number of bytes pointed to by pbVersionPrefix
+ char *szBetaUpdateURL; // URL where dll/zip is located
+
+ BYTE *pbVersion; // bytes of current version, used for comparison with those in VersionURL
+ int cpbVersion; // number of bytes pointed to by pbVersion
+
+ char *szBetaChangelogURL; // url for displaying changelog for beta versions
+} Update;
+
+// register a comonent with Updater
+//
+// wparam = 0
+// lparam = (LPARAM)&Update
+#define MS_UPDATE_REGISTER "Update/Register"
+
+// utility functions to create a version string from a DWORD or from pluginInfo
+// point buf at a buffer at least 16 chars wide - but note the version string returned may be shorter
+//
+__inline static char *CreateVersionString(DWORD version, char *buf) {
+ mir_snprintf(buf, 16, "%d.%d.%d.%d", (version >> 24) & 0xFF, (version >> 16) & 0xFF, (version >> 8) & 0xFF, version & 0xFF);
+ return buf;
+}
+
+__inline static char *CreateVersionStringPlugin(PLUGININFO *pluginInfo, char *buf) {
+ return CreateVersionString(pluginInfo->version, buf);
+}
+
+
+// register the 'easy' way - use this method if you have no beta URL and the plugin is on the miranda file listing
+// NOTE: the plugin version string on the file listing must be the string version of the version in pluginInfo (i.e. 0.0.0.1,
+// four numbers between 0 and 255 inclusivem, so no letters, brackets, etc.)
+//
+// wParam = (int)fileID - this is the file ID from the file listing (i.e. the number at the end of the download link)
+// lParam = (PLUGININFO*)&pluginInfo
+#define MS_UPDATE_REGISTERFL "Update/RegisterFL"
+
+// this function can be used to 'unregister' components - useful for plugins that register non-plugin/langpack components and
+// may need to change those components on the fly
+// lParam = (char *)szComponentName
+#define MS_UPDATE_UNREGISTER "Update/Unregister"
+
+// this event is fired when the startup process is complete, but NOT if a restart is imminent
+// it is designed for status managment plugins to use as a trigger for beggining their own startup process
+// wParam = lParam = 0 (unused)
+// (added in version 0.1.6.0)
+#define ME_UPDATE_STARTUPDONE "Update/StartupDone"
+
+// this service can be used to enable/disable Updater's global status control
+// it can be called from the StartupDone event handler
+// wParam = (BOOL)enable
+// lParam = 0
+// (added in version 0.1.6.0)
+#define MS_UPDATE_ENABLESTATUSCONTROL "Update/EnableStatusControl"
+
+// An description of usage of the above service and event:
+// Say you are a status control plugin that normally sets protocol or global statuses in your ModulesLoaded event handler.
+// In order to make yourself 'Updater compatible', you would move the status control code from ModulesLoaded to another function,
+// say DoStartup. Then, in ModulesLoaded you would check for the existence of the MS_UPDATE_ENABLESTATUSCONTROL service.
+// If it does not exist, call DoStartup. If it does exist, hook the ME_UPDATE_STARTUPDONE event and call DoStartup from there. You may
+// also wish to call MS_UPDATE_ENABLESTATUSCONTROL with wParam == FALSE at this time, to disable Updater's own status control feature.
+
+// this service can be used to determine whether updates are possible for a component with the given name
+// wParam = 0
+// lParam = (char *)szComponentName
+// returns TRUE if updates are supported, FALSE otherwise
+#define MS_UPDATE_ISUPDATESUPPORTED "Update/IsUpdateSupported"
+
+#endif
+
+
+/////////////// Usage Example ///////////////
+
+#ifdef EXAMPLE_CODE
+
+// you need to #include "m_updater.h" and HookEvent(ME_SYSTEM_MODULESLOADED, OnModulesLoaded) in your Load function...
+
+int OnModulesLoaded(WPARAM wParam, LPARAM lParam) {
+
+ Update update = {0}; // for c you'd use memset or ZeroMemory...
+ char szVersion[16];
+
+ update.cbSize = sizeof(Update);
+
+ update.szComponentName = pluginInfo.shortName;
+ update.pbVersion = (BYTE *)CreateVersionString(&pluginInfo, szVersion);
+ update.cpbVersion = strlen((char *)update.pbVersion);
+
+ // these are the three lines that matter - the archive, the page containing the version string, and the text (or data)
+ // before the version that we use to locate it on the page
+ // (note that if the update URL and the version URL point to standard file listing entries, the backend xml
+ // data will be used to check for updates rather than the actual web page - this is not true for beta urls)
+ update.szUpdateURL = "http://scottellis.com.au:81/test/updater.zip";
+ update.szVersionURL = "http://scottellis.com.au:81/test/updater_test.html";
+ update.pbVersionPrefix = (BYTE *)"Updater version ";
+
+ update.cpbVersionPrefix = strlen((char *)update.pbVersionPrefix);
+
+ // do the same for the beta versions of the above struct members if you wish to allow beta updates from another URL
+
+ CallService(MS_UPDATE_REGISTER, 0, (WPARAM)&update);
+
+ // Alternatively, to register a plugin with e.g. file ID 2254 on the file listing...
+ // CallService(MS_UPDATE_REGISTERFL, (WPARAM)2254, (LPARAM)&pluginInfo);
+
+ return 0;
+}
+
+#endif
diff --git a/Plugins/smh/smh.cpp b/Plugins/smh/smh.cpp
new file mode 100644
index 0000000..6d54e97
--- /dev/null
+++ b/Plugins/smh/smh.cpp
@@ -0,0 +1,671 @@
+/*
+Copyright (C) 2006 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#include "commons.h"
+
+
+// Prototypes ///////////////////////////////////////////////////////////////////////////
+
+
+PLUGININFOEX pluginInfo={
+ sizeof(PLUGININFOEX),
+#ifdef UNICODE
+ "Status Message History (Unicode)",
+#else
+ "Status Message History",
+#endif
+ PLUGIN_MAKE_VERSION(0,1,0,3),
+ "Log status message changes to history",
+ "Ricardo Pescuma Domenecci",
+ "",
+ "© 2006 Ricardo Pescuma Domenecci",
+ "http://pescuma.mirandaim.ru/miranda/smh",
+ UNICODE_AWARE,
+ 0, //doesn't replace anything built-in
+#ifdef UNICODE
+ { 0x4f2b331e, 0xd894, 0x4fdc, { 0x8c, 0xb2, 0xd, 0x11, 0x54, 0xe5, 0xf3, 0x4c } } // {4F2B331E-D894-4fdc-8CB2-0D1154E5F34C}
+#else
+ { 0x9a911bc1, 0xf3f1, 0x4c47, { 0xb2, 0xfd, 0x31, 0x7d, 0xaa, 0x8e, 0x3b, 0xb } } // {9A911BC1-F3F1-4c47-B2FD-317DAA8E3B0B}
+#endif
+};
+
+
+HINSTANCE hInst;
+PLUGINLINK *pluginLink;
+
+HANDLE hEnableMenu = NULL;
+HANDLE hDisableMenu = NULL;
+HANDLE hModulesLoaded = NULL;
+HANDLE hPreBuildCMenu = NULL;
+HANDLE hSettingChanged = NULL;
+
+char *metacontacts_proto = NULL;
+BOOL loaded = FALSE;
+
+int ModulesLoaded(WPARAM wParam, LPARAM lParam);
+int PreBuildContactMenu(WPARAM wParam,LPARAM lParam);
+int SettingChanged(WPARAM wParam,LPARAM lParam);
+
+int EnableHistory(WPARAM wParam,LPARAM lParam);
+int DisableHistory(WPARAM wParam,LPARAM lParam);
+int HistoryEnabled(WPARAM wParam, LPARAM lParam);
+
+BOOL ContactEnabled(HANDLE hContact);
+BOOL ProtocolEnabled(const char *protocol);
+
+
+// Functions ////////////////////////////////////////////////////////////////////////////
+
+
+extern "C" BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
+{
+ hInst = hinstDLL;
+ return TRUE;
+}
+
+
+extern "C" __declspec(dllexport) PLUGININFO* MirandaPluginInfo(DWORD mirandaVersion)
+{
+ pluginInfo.cbSize = sizeof(PLUGININFO);
+ return (PLUGININFO*) &pluginInfo;
+}
+
+
+extern "C" __declspec(dllexport) PLUGININFOEX* MirandaPluginInfoEx(DWORD mirandaVersion)
+{
+ pluginInfo.cbSize = sizeof(PLUGININFOEX);
+ return &pluginInfo;
+}
+
+
+static const MUUID interfaces[] = { MIID_STATUS_MESSAGE_CHANGE_LOGGER, MIID_STATUS_MESSAGE_CHANGE_NOTIFIER, MIID_LAST };
+extern "C" __declspec(dllexport) const MUUID* MirandaPluginInterfaces(void)
+{
+ return interfaces;
+}
+
+
+extern "C" int __declspec(dllexport) Load(PLUGINLINK *link)
+{
+ pluginLink = link;
+
+ init_mir_malloc();
+
+ CreateServiceFunction(MS_SMH_DISABLE, DisableHistory);
+ CreateServiceFunction(MS_SMH_ENABLE, EnableHistory);
+ CreateServiceFunction(MS_SMH_ENABLED, HistoryEnabled);
+
+ // Add menu item to enable/disable status message check
+ CLISTMENUITEM mi = {0};
+ mi.cbSize = sizeof(mi);
+ mi.flags = CMIF_NOTOFFLIST;
+ mi.position = 1000100010;
+
+ mi.pszName = Translate("Don't log Status Message changes");
+ mi.pszService = MS_SMH_DISABLE;
+ hDisableMenu = (HANDLE) CallService(MS_CLIST_ADDCONTACTMENUITEM, 0, (LPARAM)&mi);
+
+ mi.pszName = Translate("Log Status Message changes");
+ mi.pszService = MS_SMH_ENABLE;
+ hEnableMenu = (HANDLE) CallService(MS_CLIST_ADDCONTACTMENUITEM, 0, (LPARAM)&mi);
+
+ // hooks
+ hModulesLoaded = HookEvent(ME_SYSTEM_MODULESLOADED, ModulesLoaded);
+ hPreBuildCMenu = HookEvent(ME_CLIST_PREBUILDCONTACTMENU, PreBuildContactMenu);
+ hSettingChanged = HookEvent(ME_DB_CONTACT_SETTINGCHANGED, SettingChanged);
+
+ InitOptions();
+ InitPopups();
+
+ return 0;
+}
+
+extern "C" int __declspec(dllexport) Unload(void)
+{
+ DeInitPopups();
+ DeInitOptions();
+
+ UnhookEvent(hModulesLoaded);
+ UnhookEvent(hPreBuildCMenu);
+ UnhookEvent(hSettingChanged);
+ return 0;
+}
+
+
+// Called when all the modules are loaded
+int ModulesLoaded(WPARAM wParam, LPARAM lParam)
+{
+ if (ServiceExists(MS_MC_GETPROTOCOLNAME))
+ metacontacts_proto = (char *) CallService(MS_MC_GETPROTOCOLNAME, 0, 0);
+
+ // add our modules to the KnownModules list
+ CallService("DBEditorpp/RegisterSingleModule", (WPARAM) MODULE_NAME, 0);
+
+ // updater plugin support
+ if(ServiceExists(MS_UPDATE_REGISTER))
+ {
+ Update upd = {0};
+ char szCurrentVersion[30];
+
+ upd.cbSize = sizeof(upd);
+ upd.szComponentName = pluginInfo.shortName;
+
+ upd.szUpdateURL = UPDATER_AUTOREGISTER;
+
+ upd.szBetaVersionURL = "http://pescuma.mirandaim.ru/miranda/smh_version.txt";
+ upd.szBetaChangelogURL = "http://pescuma.mirandaim.ru/miranda/smh#Changelog";
+ upd.pbBetaVersionPrefix = (BYTE *)"Status Message History ";
+ upd.cpbBetaVersionPrefix = strlen((char *)upd.pbBetaVersionPrefix);
+#ifdef UNICODE
+ upd.szBetaUpdateURL = "http://pescuma.mirandaim.ru/miranda/smhW.zip";
+#else
+ upd.szBetaUpdateURL = "http://pescuma.mirandaim.ru/miranda/smh.zip";
+#endif
+
+ upd.pbVersion = (BYTE *)CreateVersionStringPlugin((PLUGININFO*) &pluginInfo, szCurrentVersion);
+ upd.cpbVersion = strlen((char *)upd.pbVersion);
+
+ CallService(MS_UPDATE_REGISTER, 0, (LPARAM)&upd);
+ }
+
+ loaded = TRUE;
+
+ return 0;
+}
+
+
+int PreBuildContactMenu(WPARAM wParam,LPARAM lParam)
+{
+ CLISTMENUITEM clmi = {0};
+ clmi.cbSize = sizeof(clmi);
+
+ char *proto = (char*) CallService(MS_PROTO_GETCONTACTBASEPROTO, wParam, 0);
+ if (!ProtocolEnabled(proto))
+ {
+ clmi.flags = CMIM_FLAGS | CMIF_HIDDEN;
+ CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM) hDisableMenu, (LPARAM) &clmi);
+
+ clmi.flags = CMIM_FLAGS | CMIF_HIDDEN;
+ CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM) hEnableMenu, (LPARAM) &clmi);
+ }
+ else if (HistoryEnabled(wParam, 0))
+ {
+ clmi.flags = CMIM_FLAGS | CMIF_HIDDEN;
+ CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM) hEnableMenu, (LPARAM) &clmi);
+
+ clmi.flags = CMIM_FLAGS | CMIM_ICON;
+ CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM) hDisableMenu, (LPARAM) &clmi);
+ }
+ else
+ {
+ clmi.flags = CMIM_FLAGS | CMIF_HIDDEN;
+ CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM) hDisableMenu, (LPARAM) &clmi);
+
+ clmi.flags = CMIM_FLAGS | CMIM_ICON;
+ CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM) hEnableMenu, (LPARAM) &clmi);
+ }
+
+ return 0;
+}
+
+
+int EnableHistory(WPARAM wParam,LPARAM lParam)
+{
+ HANDLE hContact = (HANDLE) wParam;
+
+ if (hContact != NULL)
+ DBWriteContactSettingByte(hContact, MODULE_NAME, "Enabled", TRUE);
+
+ return 0;
+}
+
+
+int DisableHistory(WPARAM wParam,LPARAM lParam)
+{
+ HANDLE hContact = (HANDLE) wParam;
+
+ if (hContact != NULL)
+ DBWriteContactSettingByte(hContact, MODULE_NAME, "Enabled", FALSE);
+
+ return 0;
+}
+
+
+int HistoryEnabled(WPARAM wParam, LPARAM lParam)
+{
+ return ContactEnabled((HANDLE) wParam);
+}
+
+
+BOOL AllowProtocol(const char *proto)
+{
+ if ((CallProtoService(proto, PS_GETCAPS, PFLAGNUM_1, 0) & PF1_MODEMSGRECV) == 0)
+ return FALSE;
+
+ return TRUE;
+}
+
+
+BOOL ProtocolEnabled(const char *proto)
+{
+ if (proto == NULL)
+ return FALSE;
+
+ if (!AllowProtocol(proto))
+ return FALSE;
+
+ char setting[256];
+ mir_snprintf(setting, sizeof(setting), "%sEnabled", proto);
+ return (BOOL) DBGetContactSettingByte(NULL, MODULE_NAME, setting, TRUE);
+}
+
+
+BOOL ContactEnabled(HANDLE hContact)
+{
+ if (hContact == NULL)
+ return FALSE;
+
+ char *proto = (char *) CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM) hContact, 0);
+ if (!ProtocolEnabled(proto))
+ return FALSE;
+
+ BYTE def = TRUE;
+
+ // Is a subcontact?
+ if (ServiceExists(MS_MC_GETMETACONTACT))
+ {
+ HANDLE hMetaContact = (HANDLE) CallService(MS_MC_GETMETACONTACT, (WPARAM)hContact, 0);
+
+ if (hMetaContact != NULL)
+ def = ContactEnabled(hMetaContact);
+ }
+
+ return DBGetContactSettingByte(hContact, MODULE_NAME, "Enabled", def);
+}
+
+
+// Returns true if the unicode buffer only contains 7-bit characters.
+BOOL IsUnicodeAscii(const WCHAR * pBuffer, int nSize)
+{
+ BOOL bResult = TRUE;
+ int nIndex;
+
+ for (nIndex = 0; nIndex < nSize; nIndex++) {
+ if (pBuffer[nIndex] > 0x7F) {
+ bResult = FALSE;
+ break;
+ }
+ }
+ return bResult;
+}
+
+
+HANDLE HistoryLog(HANDLE hContact, TCHAR *log_text)
+{
+ if (log_text != NULL)
+ {
+ DBEVENTINFO event = { 0 };
+ BYTE *tmp = NULL;
+
+ event.cbSize = sizeof(event);
+
+#ifdef UNICODE
+
+ size_t needed = WideCharToMultiByte(CP_ACP, 0, log_text, -1, NULL, 0, NULL, NULL);
+ size_t len = lstrlen(log_text);
+ size_t size;
+
+ if (opts.history_only_ansi_if_possible && IsUnicodeAscii(log_text, len))
+ size = needed;
+ else
+ size = needed + (len + 1) * sizeof(WCHAR);
+
+ tmp = (BYTE *) mir_alloc0(size);
+
+ WideCharToMultiByte(CP_ACP, 0, log_text, -1, (char *) tmp, needed, NULL, NULL);
+
+ if (size > needed)
+ lstrcpyn((WCHAR *) &tmp[needed], log_text, len + 1);
+
+ event.pBlob = tmp;
+ event.cbBlob = size;
+
+#else
+
+ event.pBlob = (PBYTE) log_text;
+ event.cbBlob = strlen(log_text) + 1;
+
+#endif
+
+ event.eventType = EVENTTYPE_STATUSMESSAGE_CHANGE;
+ event.flags = DBEF_READ;
+ event.timestamp = (DWORD) time(NULL);
+
+ event.szModule = MODULE_NAME;
+
+ // Is a subcontact?
+ if (ServiceExists(MS_MC_GETMETACONTACT))
+ {
+ HANDLE hMetaContact = (HANDLE) CallService(MS_MC_GETMETACONTACT, (WPARAM)hContact, 0);
+
+ if (hMetaContact != NULL && ContactEnabled(hMetaContact))
+ CallService(MS_DB_EVENT_ADD,(WPARAM)hMetaContact,(LPARAM)&event);
+ }
+
+ HANDLE ret = (HANDLE) CallService(MS_DB_EVENT_ADD,(WPARAM)hContact,(LPARAM)&event);
+
+ mir_free(tmp);
+
+ return ret;
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+void ReplaceChars(TCHAR *text)
+{
+ TCHAR *p;
+ while(p = _tcsstr(text, _T("\\n")))
+ {
+ p[0] = _T('\r');
+ p[1] = _T('\n');
+ }
+}
+
+void Notify(HANDLE hContact, TCHAR *text)
+{
+ if (text != NULL && text[0] == _T('\0'))
+ text = NULL;
+
+ if (!opts.track_changes && text != NULL)
+ return;
+
+ if (!opts.track_removes && text == NULL)
+ return;
+
+ // Replace template with status_message
+ TCHAR templ[1024];
+ lstrcpyn(templ, text == NULL ? opts.template_removed : opts.template_changed, MAX_REGS(templ));
+ ReplaceChars(templ);
+
+ TCHAR log[1024];
+ mir_sntprintf(log, sizeof(log), templ,
+ text == NULL ? TranslateT("<no status message>") : text);
+
+ if (opts.history_enable)
+ HistoryLog(hContact, log);
+
+ if (opts.popup_enable)
+ ShowPopup(hContact, NULL, log);
+}
+
+int inline CheckStr(char *str, int not_empty, int empty)
+{
+ if (str == NULL || str[0] == '\0')
+ return empty;
+ else
+ return not_empty;
+}
+
+#ifdef UNICODE
+
+int inline CheckStr(TCHAR *str, int not_empty, int empty)
+{
+ if (str == NULL || str[0] == L'\0')
+ return empty;
+ else
+ return not_empty;
+}
+
+#endif
+
+// Return 0 if not changed, 1 if changed, 2 if removed
+int TrackChange(HANDLE hContact, DBCONTACTWRITESETTING *cws_new, BOOL ignore_remove)
+{
+ char current_setting[256];
+ mir_snprintf(current_setting, MAX_REGS(current_setting), "%sCurrent", cws_new->szSetting);
+
+ int ret = 0;
+
+ DBVARIANT dbv = {0};
+#ifdef UNICODE
+ BOOL found_current = (DBGetContactSettingW(hContact, cws_new->szModule, current_setting, &dbv) == 0);
+#else
+ BOOL found_current = (DBGetContactSetting(hContact, cws_new->szModule, current_setting, &dbv) == 0);
+#endif
+ if (!found_current)
+ {
+ // Current value does not exist
+
+ if (cws_new->value.type == DBVT_DELETED)
+ {
+ ret = 0;
+ }
+ else if (cws_new->value.type == DBVT_ASCIIZ)
+ {
+ ret = CheckStr(cws_new->value.pszVal, 1, 0);
+ }
+#ifdef UNICODE
+ else if (cws_new->value.type == DBVT_UTF8)
+ {
+ ret = CheckStr(cws_new->value.pszVal, 1, 0);
+ }
+ else if (cws_new->value.type == DBVT_WCHAR)
+ {
+ ret = CheckStr(cws_new->value.pwszVal, 1, 0);
+ }
+#endif
+ else
+ {
+ ret = 1;
+ }
+ }
+ else
+ {
+ // Current value exist
+
+ if (cws_new->value.type == DBVT_DELETED)
+ {
+ if (dbv.type == DBVT_ASCIIZ)
+ {
+ ret = CheckStr(dbv.pszVal, 2, 0);
+ }
+#ifdef UNICODE
+ else if (dbv.type == DBVT_UTF8)
+ {
+ ret = CheckStr(dbv.pszVal, 2, 0);
+ }
+ else if (dbv.type == DBVT_WCHAR)
+ {
+ ret = CheckStr(dbv.pwszVal, 2, 0);
+ }
+#endif
+ else
+ {
+ ret = 2;
+ }
+ }
+ else if (dbv.type != cws_new->value.type)
+ {
+#ifdef UNICODE
+ if ( (cws_new->value.type == DBVT_UTF8 || cws_new->value.type == DBVT_ASCIIZ || cws_new->value.type == DBVT_WCHAR)
+ && (dbv.type == DBVT_UTF8 || dbv.type == DBVT_ASCIIZ || dbv.type == DBVT_WCHAR))
+ {
+ WCHAR tmp_cws_new[1024] = L"";
+ if (cws_new->value.type == DBVT_ASCIIZ)
+ MultiByteToWideChar(CP_ACP, 0, cws_new->value.pszVal, -1, tmp_cws_new, MAX_REGS(tmp_cws_new));
+ else if (cws_new->value.type == DBVT_UTF8)
+ MultiByteToWideChar(CP_UTF8, 0, cws_new->value.pszVal, -1, tmp_cws_new, MAX_REGS(tmp_cws_new));
+ else if (cws_new->value.type == DBVT_WCHAR)
+ lstrcpyn(tmp_cws_new, cws_new->value.pwszVal, MAX_REGS(tmp_cws_new));
+
+ WCHAR tmp_dbv[1024] = L"";
+ if (dbv.type == DBVT_ASCIIZ)
+ MultiByteToWideChar(CP_ACP, 0, dbv.pszVal, -1, tmp_dbv, MAX_REGS(tmp_dbv));
+ else if (dbv.type == DBVT_UTF8)
+ MultiByteToWideChar(CP_UTF8, 0, dbv.pszVal, -1, tmp_dbv, MAX_REGS(tmp_dbv));
+ else if (dbv.type == DBVT_WCHAR)
+ lstrcpyn(tmp_dbv, dbv.pwszVal, MAX_REGS(tmp_dbv));
+
+ ret = (lstrcmpW(tmp_cws_new, tmp_dbv) ? CheckStr(tmp_cws_new, 1, 2) : 0);
+ }
+ else
+#endif
+ {
+ ret = 1;
+ }
+ }
+ else if (dbv.type == DBVT_BYTE)
+ {
+ ret = (cws_new->value.bVal != dbv.bVal ? 1 : 0);
+ }
+ else if (dbv.type == DBVT_WORD)
+ {
+ ret = (cws_new->value.wVal != dbv.wVal ? 1 : 0);
+ }
+ else if (dbv.type == DBVT_DWORD)
+ {
+ ret = (cws_new->value.dVal != dbv.dVal ? 1 : 0);
+ }
+ else if (dbv.type == DBVT_ASCIIZ)
+ {
+ ret = (strcmp(cws_new->value.pszVal, dbv.pszVal) ? CheckStr(cws_new->value.pszVal, 1, 2) : 0);
+ }
+#ifdef UNICODE
+ else if (dbv.type == DBVT_UTF8)
+ {
+ ret = (strcmp(cws_new->value.pszVal, dbv.pszVal) ? CheckStr(cws_new->value.pszVal, 1, 2) : 0);
+ }
+ else if (dbv.type == DBVT_WCHAR)
+ {
+ ret = (lstrcmp(cws_new->value.pwszVal, dbv.pwszVal) ? CheckStr(cws_new->value.pwszVal, 1, 2) : 0);
+ }
+#endif
+ }
+
+ if (ret == 1 || (ret == 2 && !ignore_remove))
+ {
+ // Copy current to old
+ char old_setting[256];
+ mir_snprintf(old_setting, MAX_REGS(old_setting), "%sOld", cws_new->szSetting);
+
+ if (dbv.type == DBVT_DELETED)
+ {
+ DBDeleteContactSetting(hContact, cws_new->szModule, old_setting);
+ }
+ else
+ {
+ DBCONTACTWRITESETTING cws_old;
+ cws_old.szModule = cws_new->szModule;
+ cws_old.szSetting = old_setting;
+ cws_old.value = dbv;
+ CallService(MS_DB_CONTACT_WRITESETTING, (WPARAM)hContact, (LPARAM)&cws_old);
+ }
+
+
+ // Copy new to current
+ if (cws_new->value.type == DBVT_DELETED)
+ {
+ DBDeleteContactSetting(hContact, cws_new->szModule, current_setting);
+ }
+ else
+ {
+ DBCONTACTWRITESETTING cws_old;
+ cws_old.szModule = cws_new->szModule;
+ cws_old.szSetting = current_setting;
+ cws_old.value = cws_new->value;
+ CallService(MS_DB_CONTACT_WRITESETTING, (WPARAM)hContact, (LPARAM)&cws_old);
+ }
+ }
+
+ if (found_current)
+ DBFreeVariant(&dbv);
+
+ return ret;
+}
+
+
+int SettingChanged(WPARAM wParam,LPARAM lParam)
+{
+ if (!loaded)
+ return 0;
+
+ if (!opts.history_enable && !opts.popup_enable)
+ return 0;
+
+ HANDLE hContact = (HANDLE) wParam;
+ if (hContact == NULL)
+ return 0;
+
+ DBCONTACTWRITESETTING *cws = (DBCONTACTWRITESETTING*)lParam;
+ if (!strcmp(cws->szModule, "CList") && !strcmp(cws->szSetting, "StatusMsg"))
+ {
+ char *proto = (char *) CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM) hContact, 0);
+ if (proto == NULL || (metacontacts_proto != NULL && !strcmp(proto, metacontacts_proto)))
+ return 0;
+
+ if (opts.track_only_not_offline)
+ {
+ if (DBGetContactSettingWord(hContact, proto, "Status", 0) <= ID_STATUS_OFFLINE)
+ return 0;
+ }
+
+ if (!ContactEnabled(hContact))
+ return 0;
+
+ int changed = TrackChange(hContact, cws, !opts.track_removes);
+ if (changed == 0)
+ return 0;
+
+ if (changed == 2)
+ {
+ Notify(hContact, NULL);
+ }
+ else // changed == 1
+#ifdef UNICODE
+ if (cws->value.type == DBVT_ASCIIZ)
+ {
+ WCHAR tmp[1024] = L"";
+ MultiByteToWideChar(CP_ACP, 0, cws->value.pszVal, -1, tmp, MAX_REGS(tmp));
+ Notify(hContact, tmp);
+ }
+ else if (cws->value.type == DBVT_UTF8)
+ {
+ WCHAR tmp[1024] = L"";
+ MultiByteToWideChar(CP_UTF8, 0, cws->value.pszVal, -1, tmp, MAX_REGS(tmp));
+ Notify(hContact, tmp);
+ }
+ else if (cws->value.type == DBVT_WCHAR)
+ {
+ Notify(hContact, cws->value.pwszVal);
+ }
+#else
+ if (cws->value.type == DBVT_ASCIIZ)
+ {
+ Notify(hContact, cws->value.pszVal);
+ }
+#endif
+ }
+
+ return 0;
+}
+
diff --git a/Plugins/smh/smh.dsp b/Plugins/smh/smh.dsp
new file mode 100644
index 0000000..e490159
--- /dev/null
+++ b/Plugins/smh/smh.dsp
@@ -0,0 +1,247 @@
+# Microsoft Developer Studio Project File - Name="smh" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=smh - Win32 Release
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "smh.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "smh.mak" CFG="smh - Win32 Release"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "smh - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "smh - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "smh - Win32 Unicode Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "smh - Win32 Unicode Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "smh - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MD /W3 /GX /O1 /YX /FD /c
+# SUBTRACT BASE CPP /Fr
+# ADD CPP /nologo /G4 /MT /W3 /GX /O2 /Ob0 /I "../../include" /I "sdk" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /Fr /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x417 /d "NDEBUG"
+# ADD RSC /l 0x417 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 user32.lib shell32.lib wininet.lib gdi32.lib /nologo /base:"0x67100000" /dll /machine:I386 /filealign:0x200
+# SUBTRACT BASE LINK32 /pdb:none /map
+# ADD LINK32 kernel32.lib user32.lib /nologo /base:"0x3EC20000" /dll /map /machine:I386 /out:"..\..\bin\release\Plugins\smh.dll" /filealign:0x200 /ALIGN:4096 /ignore:4108
+# SUBTRACT LINK32 /profile /pdb:none
+
+!ELSEIF "$(CFG)" == "smh - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Ignore_Export_Lib 0
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /G4 /MT /W3 /GX /O2 /Ob0 /I "../../include" /FR /YX /FD /c
+# ADD CPP /nologo /G4 /MTd /W3 /GX /ZI /Od /I "../../include" /I "sdk" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /FR /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x417 /d "NDEBUG"
+# ADD RSC /l 0x417 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 comctl32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 /out:"..\..bin\release\Plugins\smh.dll" /filealign:0x200 /ALIGN:4096 /ignore:4108
+# SUBTRACT BASE LINK32 /profile /pdb:none
+# ADD LINK32 kernel32.lib user32.lib /nologo /base:"0x3EC20000" /dll /incremental:yes /debug /machine:I386 /out:"..\..\bin\debug\Plugins\smh.dll" /filealign:0x200 /ALIGN:4096 /ignore:4108
+# SUBTRACT LINK32 /profile /pdb:none
+
+!ELSEIF "$(CFG)" == "smh - Win32 Unicode Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "smh___Win32_Unicode_Debug"
+# PROP BASE Intermediate_Dir "smh___Win32_Unicode_Debug"
+# PROP BASE Ignore_Export_Lib 0
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Unicode_Debug"
+# PROP Intermediate_Dir "Unicode_Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /G4 /MTd /W3 /GX /ZI /Od /I "../../include" /FR /YX /FD /c
+# ADD CPP /nologo /G4 /MTd /W3 /GX /ZI /Od /I "../../include" /I "sdk" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_UNICODE" /D "UNICODE" /D "_USRDLL" /FR /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x417 /d "NDEBUG"
+# ADD RSC /l 0x417 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 comctl32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /base:"0x32100000" /dll /incremental:yes /debug /machine:I386 /out:"..\..\bin\debug\Plugins\smh.dll" /filealign:0x200 /ALIGN:4096 /ignore:4108
+# SUBTRACT BASE LINK32 /profile /pdb:none
+# ADD LINK32 kernel32.lib user32.lib /nologo /base:"0x3EC20000" /dll /incremental:yes /debug /machine:I386 /out:"..\..\bin\debug unicode\Plugins\smhW.dll" /filealign:0x200 /ALIGN:4096 /ignore:4108
+# SUBTRACT LINK32 /profile /pdb:none
+
+!ELSEIF "$(CFG)" == "smh - Win32 Unicode Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "smh___Win32_Unicode_Release"
+# PROP BASE Intermediate_Dir "smh___Win32_Unicode_Release"
+# PROP BASE Ignore_Export_Lib 0
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Unicode_Release"
+# PROP Intermediate_Dir "Unicode_Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /G4 /MT /W3 /GX /O2 /Ob0 /I "../../include" /Fr /YX /FD /c
+# ADD CPP /nologo /G4 /MT /W3 /GX /O2 /Ob0 /I "../../include" /I "sdk" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_UNICODE" /D "UNICODE" /D "_USRDLL" /Fr /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x417 /d "NDEBUG"
+# ADD RSC /l 0x417 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 comctl32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /base:"0x32100000" /dll /map /machine:I386 /out:"..\..\bin\release\Plugins\smh.dll" /filealign:0x200 /ALIGN:4096 /ignore:4108
+# SUBTRACT BASE LINK32 /profile /pdb:none
+# ADD LINK32 kernel32.lib user32.lib /nologo /base:"0x3EC20000" /dll /map /machine:I386 /out:"..\..\bin\release\Plugins\smhW.dll" /filealign:0x200 /ALIGN:4096 /ignore:4108
+# SUBTRACT LINK32 /profile /pdb:none
+
+!ENDIF
+
+# Begin Target
+
+# Name "smh - Win32 Release"
+# Name "smh - Win32 Debug"
+# Name "smh - Win32 Unicode Debug"
+# Name "smh - Win32 Unicode Release"
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=.\commons.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\m_smh.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\utils\mir_memory.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\utils\mir_options.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\options.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\popup.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\resource.h
+# End Source File
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
+# Begin Source File
+
+SOURCE=.\resource.rc
+# End Source File
+# End Group
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=..\utils\mir_memory.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\utils\mir_options.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\options.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\popup.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\smh.cpp
+# End Source File
+# End Group
+# Begin Group "Docs"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=.\Docs\helppack_smh.txt
+# End Source File
+# Begin Source File
+
+SOURCE=.\Docs\langpack_smh.txt
+# End Source File
+# Begin Source File
+
+SOURCE=.\Docs\smh_changelog.txt
+# End Source File
+# Begin Source File
+
+SOURCE=.\Docs\smh_readme.txt
+# End Source File
+# Begin Source File
+
+SOURCE=.\Docs\smh_version.txt
+# End Source File
+# End Group
+# End Target
+# End Project
diff --git a/Plugins/smh/smh.dsw b/Plugins/smh/smh.dsw
new file mode 100644
index 0000000..ed7906d
--- /dev/null
+++ b/Plugins/smh/smh.dsw
@@ -0,0 +1,29 @@
+Microsoft Developer Studio Workspace File, Format Version 6.00
+# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
+
+###############################################################################
+
+Project: "smh"=".\smh.dsp" - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Global:
+
+Package=<5>
+{{{
+}}}
+
+Package=<3>
+{{{
+}}}
+
+###############################################################################
+
diff --git a/Plugins/smr/Docs/langpack_smr.txt b/Plugins/smr/Docs/langpack_smr.txt
new file mode 100644
index 0000000..0a0dc4f
--- /dev/null
+++ b/Plugins/smr/Docs/langpack_smr.txt
@@ -0,0 +1,45 @@
+; Status Message Retriever
+; Author: Pescuma
+
+; Contact Menu
+
+[Enable Status Message Check]
+[Disable Status Message Check]
+
+
+; Group in options
+
+[Status Msg Retrieve]
+
+
+; Options
+
+[General]
+
+[ Retrieve ]
+[Retrieve status messages every]
+[minutes]
+[Retrieve on status change]
+[Also retrieve after]
+[seconds after status change]
+
+[ Clear ]
+[Clear message on status change]
+[Always clear message if status does not support messages]
+[(even for contacts with msg check disabled)]
+
+[ XStatus ]
+[If contact has XStatus set:]
+[Retrieve as usual]
+[Clear]
+[Clear only if XStatus message is set]
+[Set to XStatus Name]
+[Set to XStatus Message]
+[Set to XStatus Name: XStatus Message]
+
+
+[Protocols]
+
+[Get status messages for these protocols:]
+[This feature is mainly for use with protocols that store status messages on server rather then in database (like ICQ). Don't enable it for MSN or Jabber - they already save the status message in database and won't work correctly if you enable this.]
+
diff --git a/Plugins/smr/Docs/smr_changelog.txt b/Plugins/smr/Docs/smr_changelog.txt
new file mode 100644
index 0000000..d7da3da
--- /dev/null
+++ b/Plugins/smr/Docs/smr_changelog.txt
@@ -0,0 +1,61 @@
+Status Message Retriever
+
+Changelog:
+
+. 1.0.0.7
+ * Fix for crash
+
+. 1.0.0.6
+ * Fix for crash at startup
+
+. 1.0.0.5
+ * Now uses tabs from core in options
+ * Options are now expert only
+
+. 1.0.0.4
+ + Added support for Miranda 0.8
+
+. 1.0.0.3
+ * Fix for services
+ + Added more info text
+
+. 1.0.0.2
+ + If XStatus name and message are the same, show only one (and not Name: message)
+
+. 1.0.0.1
+ * Try to fix bug on thread finish
+ + Don't check contacts on invisible list
+ + Options in XP style
+
+. 1.0.0.0
+ * Only save retrieved status message if needed
+ + Langpack file
+ + Changed version number to reflect that the plugin is ready
+
+. 0.0.1.6
+ * Bugfix to freezing
+
+. 0.0.1.5
+ * Bugfix in poll
+ * Bugfix in xstatus code
+
+. 0.0.1.4
+ + Deal with XStatus
+ * Tabbed options dialog
+
+. 0.0.1.3
+ + Option to always clear status message
+ * Fixed error detection
+ * Online check if check on timer enabled also
+
+. 0.0.1.2
+ + New logic to handle requests. Should work better for ICQ, but messages will take longer to arrive.
+
+. 0.0.1.1
+ + Updater support
+ + More options
+ + A lot of changes in polling code
+ * Bugfix in contact menu option
+
+. 0.0.1.0
+ + Initial version \ No newline at end of file
diff --git a/Plugins/smr/Docs/smr_readme.txt b/Plugins/smr/Docs/smr_readme.txt
new file mode 100644
index 0000000..bb6d8c6
--- /dev/null
+++ b/Plugins/smr/Docs/smr_readme.txt
@@ -0,0 +1,8 @@
+Status Message Retriever plugin
+-------------------------------
+
+This is a plugin that retrieves status messages based on timer and on status change. It is made for protocols that store the status message at the client, like ICQ does.
+
+Please, do not enable this for MSN nor Jabber, because it can cause bad side effects (these protocols handle status message internally and smr can break things).
+
+To report bugs/make suggestions, go to the forum thread: http://forums.miranda-im.org/showthread.php?t=7560
diff --git a/Plugins/smr/Docs/smr_version.txt b/Plugins/smr/Docs/smr_version.txt
new file mode 100644
index 0000000..5db2c73
--- /dev/null
+++ b/Plugins/smr/Docs/smr_version.txt
@@ -0,0 +1 @@
+Status Message Retriever 1.0.0.7 \ No newline at end of file
diff --git a/Plugins/smr/ZIP/doit.bat b/Plugins/smr/ZIP/doit.bat
new file mode 100644
index 0000000..95860cd
--- /dev/null
+++ b/Plugins/smr/ZIP/doit.bat
@@ -0,0 +1,84 @@
+@echo off
+
+rem Batch file to build and upload files
+rem
+rem TODO: Integration with FL
+
+set name=smr
+
+rem To upload, this var must be set here or in other batch
+rem set ftp=ftp://<user>:<password>@<ftp>/<path>
+
+echo Building %name% ...
+
+msdev ..\%name%.dsp /MAKE "%name% - Win32 Release" /REBUILD
+
+echo Generating files for %name% ...
+
+del *.zip
+del *.dll
+del *.txt
+copy ..\Docs\%name%_changelog.txt
+copy ..\Docs\%name%_version.txt
+copy ..\Docs\%name%_readme.txt
+mkdir Plugins
+cd Plugins
+del /Q *.*
+copy ..\..\..\..\bin\release\Plugins\%name%.dll
+cd ..
+mkdir Docs
+cd Docs
+del /Q *.*
+copy ..\..\Docs\%name%_readme.txt
+copy ..\..\Docs\langpack_%name%.txt
+rem copy ..\..\Docs\helppack_%name%.txt
+copy ..\..\m_%name%.h
+cd ..
+mkdir src
+cd src
+del /Q *.*
+copy ..\..\*.h
+copy ..\..\*.cpp
+copy ..\..\*.rc
+copy ..\..\*.dsp
+copy ..\..\*.dsw
+mkdir Docs
+cd Docs
+del /Q *.*
+copy ..\..\..\Docs\*.*
+cd ..
+cd ..
+
+"C:\Program Files\Filzip\Filzip.exe" -a -rp %name%.zip Plugins Docs
+"C:\Program Files\Filzip\Filzip.exe" -a -rp %name%_src.zip src\*.*
+
+cd Plugins
+del /Q *.*
+cd ..
+rmdir Plugins
+cd Docs
+del /Q *.*
+cd ..
+rmdir Docs
+cd src
+del /Q *.*
+cd Docs
+del /Q *.*
+cd ..
+rmdir Docs
+cd ..
+rmdir src
+
+if "%ftp%"=="" GOTO END
+
+echo Going to upload files...
+pause
+
+"C:\Program Files\FileZilla\FileZilla.exe" -u .\%name%.zip %ftp% -overwrite -close
+"C:\Program Files\FileZilla\FileZilla.exe" -u .\%name%_changelog.txt %ftp% -overwrite -close
+"C:\Program Files\FileZilla\FileZilla.exe" -u .\%name%_version.txt %ftp% -overwrite -close
+"C:\Program Files\FileZilla\FileZilla.exe" -u .\%name%_readme.txt %ftp% -overwrite -close
+
+:END
+
+echo Done.
diff --git a/Plugins/smr/commons.h b/Plugins/smr/commons.h
new file mode 100644
index 0000000..d23afa6
--- /dev/null
+++ b/Plugins/smr/commons.h
@@ -0,0 +1,93 @@
+/*
+Copyright (C) 2005 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#ifndef __COMMONS_H__
+# define __COMMONS_H__
+
+
+#include <windows.h>
+#include <commctrl.h>
+#include <stdio.h>
+
+
+
+// Miranda headers
+#define MIRANDA_VER 0x0700
+#include <newpluginapi.h>
+#include <m_system.h>
+#include <m_protocols.h>
+#include <m_protosvc.h>
+#include <m_clist.h>
+#include <m_ignore.h>
+#include <m_contacts.h>
+#include <m_message.h>
+#include <m_userinfo.h>
+#include <m_skin.h>
+#include <m_langpack.h>
+#include <m_database.h>
+#include <m_options.h>
+#include <m_utils.h>
+#include <m_updater.h>
+
+#include "../utils/mir_dblists.h"
+#include "../utils/mir_memory.h"
+#include "../utils/mir_options.h"
+#include "../utils/mir_log.h"
+
+#include "resource.h"
+#include "m_smr.h"
+#include "poll.h"
+#include "status_msg.h"
+#include "status.h"
+#include "options.h"
+
+
+#define MODULE_NAME "StatusMsgRetriver"
+
+
+// Global Variables
+extern HINSTANCE hInst;
+extern PLUGINLINK *pluginLink;
+
+
+#define INITIAL_TIMER 5000
+#define ONLINE_TIMER 5000
+#define UPDATE_DELAY 4000
+#define ERROR_DELAY 15000
+#define POOL_DELAY 1000
+#define WAIT_TIME 5000
+
+
+#define MAX_REGS(_A_) ( sizeof(_A_) / sizeof(_A_[0]) )
+
+
+// See if a protocol service exists
+__inline static int ProtoServiceExists(const char *szModule,const char *szService)
+{
+ char str[MAXMODULELABELLENGTH];
+ strcpy(str,szModule);
+ strcat(str,szService);
+ return ServiceExists(str);
+}
+
+
+
+
+#endif // __COMMONS_H__
diff --git a/Plugins/smr/m_smr.h b/Plugins/smr/m_smr.h
new file mode 100644
index 0000000..5cee0d7
--- /dev/null
+++ b/Plugins/smr/m_smr.h
@@ -0,0 +1,70 @@
+/*
+Copyright (C) 2005 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#ifndef __M_SMR_H__
+# define __M_SMR_H__
+
+
+#define MIID_STATUS_MESSAGE_RETRIEVER { 0xbdfc508a, 0x3c73, 0x40d4, { 0x9f, 0x15, 0xf0, 0xbe, 0xb5, 0xab, 0x72, 0x78 } }
+
+
+/*
+Return TRUE is smr is enabled for this protocol
+If is enabled, status message is kept under CList\StatusMsg db key in user data
+
+wParam: protocol name
+lParam: ignored
+*/
+#define MS_SMR_ENABLED_FOR_PROTOCOL "SMR/MsgRetrievalEnabledForProtocol"
+
+
+/*
+Return TRUE is smr is enabled for this contact and its protocol (smr can be disabled per user,
+if protocol is enabled)
+If is enabled, status message is kept under CList\StatusMsg db key in user data
+
+wParam: hContact
+lParam: ignored
+*/
+#define MS_SMR_ENABLED_FOR_CONTACT "SMR/MsgRetrievalEnabledForUser"
+
+
+/*
+Enable status message retrieval for a contact
+
+wParam: hContact
+lParam: ignored
+*/
+#define MS_SMR_ENABLE_CONTACT "SMR/EnableContactMsgRetrieval"
+
+
+/*
+Disable status message retrieval for a contact
+
+wParam: hContact
+lParam: ignored
+*/
+#define MS_SMR_DISABLE_CONTACT "SMR/DisableContactMsgRetrieval"
+
+
+
+
+
+#endif // __M_SMR_H__
diff --git a/Plugins/smr/options.cpp b/Plugins/smr/options.cpp
new file mode 100644
index 0000000..958c8b0
--- /dev/null
+++ b/Plugins/smr/options.cpp
@@ -0,0 +1,155 @@
+/*
+Copyright (C) 2006 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#include "options.h"
+
+
+
+// Prototypes /////////////////////////////////////////////////////////////////////////////////////
+
+HANDLE hOptHook = NULL;
+
+
+Options opts;
+
+static BOOL CALLBACK OptionsDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam);
+static BOOL CALLBACK GeneralOptionsDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam);
+static BOOL CALLBACK ProtocolsOptionsDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam);
+
+
+
+// Functions //////////////////////////////////////////////////////////////////////////////////////
+
+
+// Initializations needed by options
+void LoadOptions()
+{
+ opts.poll_check_on_timer = DBGetContactSettingByte(NULL, MODULE_NAME, OPT_CHECK_ONTIMER, TRUE);
+ opts.poll_check_on_status_change = DBGetContactSettingByte(NULL, MODULE_NAME, OPT_CHECK_ONSTATUSCHANGE, TRUE);
+ opts.poll_check_on_status_change_timer = DBGetContactSettingByte(NULL, MODULE_NAME, OPT_CHECK_ONSTATUSCHANGETIMER, TRUE);
+ opts.poll_timer_check = DBGetContactSettingWord(NULL, MODULE_NAME, OPT_CHECK_ONTIMER_TIMER, 10);
+ opts.poll_timer_status = DBGetContactSettingWord(NULL, MODULE_NAME, OPT_CHECK_ONSTATUSTIMER_TIMER, 15);
+ opts.poll_clear_on_status_change = DBGetContactSettingByte(NULL, MODULE_NAME, OPT_CLEAR_ONSTATUSCHANGE, TRUE);
+ opts.always_clear = DBGetContactSettingByte(NULL, MODULE_NAME, OPT_ALWAYS_CLEAR, TRUE);
+ opts.when_xstatus = (XStatusAction) DBGetContactSettingWord(NULL, MODULE_NAME, OPT_WHEN_XSTATUS, Clear);
+
+ PollSetTimer();
+}
+
+int InitOptionsCallback(WPARAM wParam,LPARAM lParam)
+{
+ OPTIONSDIALOGPAGE odp;
+
+ ZeroMemory(&odp,sizeof(odp));
+ odp.cbSize=sizeof(odp);
+ odp.position=0;
+ odp.hInstance=hInst;
+ odp.ptszGroup = TranslateT("Status");
+ odp.ptszTitle = TranslateT("Status Msg Retrieve");
+ odp.flags = ODPF_BOLDGROUPS | ODPF_EXPERTONLY;
+
+ odp.ptszTab = TranslateT("General");
+ odp.pfnDlgProc = GeneralOptionsDlgProc;
+ odp.pszTemplate = MAKEINTRESOURCE(IDD_OPT_GENERAL);
+ CallService(MS_OPT_ADDPAGE,wParam,(LPARAM)&odp);
+
+ odp.ptszTab = TranslateT("Protocols");
+ odp.pfnDlgProc = ProtocolsOptionsDlgProc;
+ odp.pszTemplate = MAKEINTRESOURCE(IDD_OPT_PROTOCOLS);
+ CallService(MS_OPT_ADDPAGE,wParam,(LPARAM)&odp);
+
+ return 0;
+}
+
+
+void InitOptions()
+{
+ LoadOptions();
+
+ hOptHook = HookEvent(ME_OPT_INITIALISE, InitOptionsCallback);
+}
+
+// Deinitializations needed by options
+void DeInitOptions()
+{
+ UnhookEvent(hOptHook);
+}
+
+
+// General page
+
+static OptPageControl generalControls[] = {
+ { NULL, CONTROL_CHECKBOX, IDC_CHECK_ONTIMER, OPT_CHECK_ONTIMER, (BYTE) TRUE },
+ { NULL, CONTROL_CHECKBOX, IDC_CHECK_ONSTATUS, OPT_CHECK_ONSTATUSCHANGE, (BYTE) TRUE },
+ { NULL, CONTROL_CHECKBOX, IDC_CHECK_ONSTATUSTIMER, OPT_CHECK_ONSTATUSCHANGETIMER, (BYTE) TRUE },
+ { NULL, CONTROL_CHECKBOX, IDC_CLEAR_ON_STATUS, OPT_CLEAR_ONSTATUSCHANGE, (BYTE) TRUE },
+ { NULL, CONTROL_CHECKBOX, IDC_ALWAYS_CLEAR, OPT_ALWAYS_CLEAR, (BYTE) TRUE },
+ { NULL, CONTROL_SPIN, IDC_CHECK_ONTIMER_TIMER, OPT_CHECK_ONTIMER_TIMER, (WORD) 10, IDC_CHECK_ONTIMER_TIMER_SPIN, (WORD) 1, (WORD) 255 },
+ { NULL, CONTROL_SPIN, IDC_CHECK_ONSTATUSTIMER_TIMER, OPT_CHECK_ONSTATUSTIMER_TIMER, (WORD) 15, IDC_CHECK_ONSTATUSTIMER_TIMER_SPIN, (WORD) 1, (WORD) 255 },
+ { NULL, CONTROL_COMBO, IDC_XSTATUS, OPT_WHEN_XSTATUS, (WORD) Clear }
+};
+
+
+static BOOL CALLBACK GeneralOptionsDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg)
+ {
+ case WM_INITDIALOG:
+ {
+ SendDlgItemMessage(hwndDlg, IDC_XSTATUS, CB_ADDSTRING, 0, (LONG) TranslateT("Retrieve as usual"));
+ SendDlgItemMessage(hwndDlg, IDC_XSTATUS, CB_ADDSTRING, 0, (LONG) TranslateT("Clear"));
+ SendDlgItemMessage(hwndDlg, IDC_XSTATUS, CB_ADDSTRING, 0, (LONG) TranslateT("Clear only if XStatus message is set"));
+ SendDlgItemMessage(hwndDlg, IDC_XSTATUS, CB_ADDSTRING, 0, (LONG) TranslateT("Set to XStatus Name"));
+ SendDlgItemMessage(hwndDlg, IDC_XSTATUS, CB_ADDSTRING, 0, (LONG) TranslateT("Set to XStatus Message"));
+ SendDlgItemMessage(hwndDlg, IDC_XSTATUS, CB_ADDSTRING, 0, (LONG) TranslateT("Set to XStatus Name: XStatus Message"));
+ break;
+ }
+ }
+
+ return SaveOptsDlgProc(generalControls, MAX_REGS(generalControls), MODULE_NAME, hwndDlg, msg, wParam, lParam);
+}
+
+
+// Protocols page
+
+BOOL AllowProtocol(const char *proto)
+{
+ if (!ProtoServiceExists(proto, PS_GETSTATUS))
+ return FALSE;
+
+ if (CallProtoService(proto, PS_GETCAPS, PFLAGNUM_2, 0) == 0)
+ return FALSE;
+
+ if ((CallProtoService(proto, PS_GETCAPS, PFLAGNUM_1, 0) & PF1_MODEMSGRECV) == 0)
+ return FALSE;
+
+ return TRUE;
+}
+
+static OptPageControl protocolControls[] = {
+ { NULL, CONTROL_PROTOCOL_LIST, IDC_PROTOCOLS, OPT_PROTOCOL_GETMSG, FALSE, (int)AllowProtocol }
+};
+
+
+static BOOL CALLBACK ProtocolsOptionsDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ return SaveOptsDlgProc(protocolControls, MAX_REGS(protocolControls), MODULE_NAME, hwndDlg, msg, wParam, lParam);
+}
+
diff --git a/Plugins/smr/options.h b/Plugins/smr/options.h
new file mode 100644
index 0000000..a853700
--- /dev/null
+++ b/Plugins/smr/options.h
@@ -0,0 +1,77 @@
+/*
+Copyright (C) 2006 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#ifndef __OPTIONS_H__
+# define __OPTIONS_H__
+
+
+#include "commons.h"
+
+#include <windows.h>
+
+#define OPT_CHECK_ONTIMER "CheckOnTimer"
+#define OPT_CHECK_ONSTATUSCHANGE "CheckOnStatusChange"
+#define OPT_CHECK_ONSTATUSCHANGETIMER "CheckOnStatusChangeTimer"
+#define OPT_CLEAR_ONSTATUSCHANGE "ClearOnStatusChange"
+#define OPT_CHECK_ONTIMER_TIMER "CheckOnTimerTimer"
+#define OPT_CHECK_ONSTATUSTIMER_TIMER "CheckOnStatusTimer"
+#define OPT_ALWAYS_CLEAR "AlwaysClear"
+#define OPT_WHEN_XSTATUS "WhenXStatus"
+#define OPT_CONTACT_GETMSG "MsgCheck"
+#define OPT_PROTOCOL_GETMSG "%sMsgCheck"
+
+typedef enum {
+ Normal = 0,
+ Clear,
+ ClearOnMessage,
+ SetToXStatusName,
+ SetToXStatusMessage,
+ SetToXStatusNameXStatusMessage,
+} XStatusAction;
+
+typedef struct
+{
+ BOOL poll_check_on_timer;
+ BOOL poll_check_on_status_change;
+ BOOL poll_check_on_status_change_timer;
+ BOOL poll_clear_on_status_change;
+ WORD poll_timer_check;
+ WORD poll_timer_status;
+ BOOL always_clear;
+ XStatusAction when_xstatus;
+} Options;
+
+extern Options opts;
+
+
+// Initializations needed by options
+void InitOptions();
+
+// Deinitializations needed by options
+void DeInitOptions();
+
+
+// Loads the options from DB
+// It don't need to be called, except in some rare cases
+void LoadOptions();
+
+
+
+#endif // __OPTIONS_H__
diff --git a/Plugins/smr/poll.cpp b/Plugins/smr/poll.cpp
new file mode 100644
index 0000000..f2fee48
--- /dev/null
+++ b/Plugins/smr/poll.cpp
@@ -0,0 +1,868 @@
+/*
+Copyright (C) 2006 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#include "poll.h"
+
+
+// Prototypes ///////////////////////////////////////////////////////////////////////////
+
+static CRITICAL_SECTION update_cs;
+static CRITICAL_SECTION pause_cs;
+
+typedef struct
+{
+ SortedList *queue;
+ HANDLE hThread;
+ DWORD dwThreadID;
+ BOOL bThreadRunning;
+
+} ThreadQueue;
+
+ThreadQueue statusQueue;
+
+
+struct QueueItem
+{
+ HANDLE hContact;
+ DWORD check_time;
+ int type;
+};
+
+// Types
+#define STATUS_CHANGE 1
+#define STATUS_CHANGE_TIMER 2
+#define PROTOCOL_ONLINE 4
+#define POOL 8
+#define XSTATUS_CHANGE 16
+
+
+UINT_PTR hTimer = 0;
+
+void QueueAdd(HANDLE hContact, DWORD check_time, int type);
+void QueueRemove(HANDLE hContact);
+int QueueSortItems(void *i1, void *i2);
+
+
+VOID CALLBACK PollTimerAddContacts(HWND hwnd, UINT uMsg, UINT idEvent, DWORD dwTime);
+void PollAddAllContacts(int timer, const char *protocol, int type);
+
+DWORD WINAPI UpdateThread(LPVOID vParam);
+
+
+DWORD next_request_at = 0;
+void SetNextRequestAt(DWORD t);
+DWORD GetNextRequestAt(void);
+
+QueueItem *requested_item;
+
+
+// Functions ////////////////////////////////////////////////////////////////////////////
+
+
+void InitPoll()
+{
+ int queuesize = CallService(MS_DB_CONTACT_GETCOUNT, 0, 0);
+
+ // Init queue
+ ZeroMemory(&statusQueue, sizeof(statusQueue));
+ statusQueue.queue = List_Create(0, queuesize + 10);
+ statusQueue.queue->sortFunc = QueueSortItems;
+ statusQueue.bThreadRunning = TRUE;
+ statusQueue.hThread = CreateThread(NULL, 16000, UpdateThread, NULL, 0, &statusQueue.dwThreadID);
+
+ InitializeCriticalSection(&update_cs);
+ InitializeCriticalSection(&pause_cs);
+
+ requested_item = NULL;
+}
+
+
+void FreePoll()
+{
+ DWORD dwExitcode;
+ int steps;
+
+ // Stop queue
+ steps = 0;
+ statusQueue.bThreadRunning = FALSE;
+ ResumeThread(statusQueue.hThread);
+ do {
+ Sleep(100);
+ GetExitCodeThread(statusQueue.hThread, &dwExitcode);
+ steps++;
+ } while ( dwExitcode == STILL_ACTIVE && steps < 20 );
+ if (statusQueue.hThread)
+ CloseHandle(statusQueue.hThread);
+
+ // Delete cs
+ DeleteCriticalSection(&update_cs);
+ DeleteCriticalSection(&pause_cs);
+
+ // Free lists
+ List_DestroyFreeContents(statusQueue.queue);
+ mir_free(statusQueue.queue);
+}
+
+int UpdateDelay()
+{
+ int delay = max(50, DBGetContactSettingWord(NULL, MODULE_NAME, "UpdateDelay", UPDATE_DELAY));
+
+ return delay;
+}
+
+int ErrorDelay()
+{
+ int delay = max(50, DBGetContactSettingWord(NULL, MODULE_NAME, "ErrorDelay", ERROR_DELAY));
+
+ return delay;
+}
+
+int WaitTime()
+{
+ int delay = max(50, DBGetContactSettingWord(NULL, MODULE_NAME, "WaitTime", WAIT_TIME));
+
+ return delay;
+}
+
+// Return true if this protocol has to be checked
+BOOL PollCheckProtocol(const char *protocol)
+{
+ if (protocol == NULL)
+ return FALSE;
+
+ if (!ProtoServiceExists(protocol, PS_GETSTATUS))
+ return FALSE;
+
+ if (CallProtoService(protocol, PS_GETCAPS, PFLAGNUM_2, 0) == 0)
+ return FALSE;
+
+ if ((CallProtoService(protocol, PS_GETCAPS, PFLAGNUM_1, 0) & PF1_MODEMSGRECV) == 0)
+ return FALSE;
+
+ char setting[256];
+ mir_snprintf(setting, sizeof(setting), OPT_PROTOCOL_GETMSG, protocol);
+
+ return (BOOL) DBGetContactSettingByte(NULL, MODULE_NAME, setting, FALSE);
+}
+
+
+// Return true if this contact has to be checked
+BOOL PollCheckContact(HANDLE hContact)
+{
+ return !DBGetContactSettingByte(hContact,"CList","Hidden",0) &&
+ !DBGetContactSettingByte(hContact,"CList","NotOnList",0) &&
+ DBGetContactSettingByte(hContact,"CList","ApparentMode",0) != ID_STATUS_OFFLINE &&
+ DBGetContactSettingByte(hContact, MODULE_NAME, OPT_CONTACT_GETMSG, TRUE);
+}
+
+
+// Add a contact to the poll when the status of the contact has changed
+void PollStatusChangeAddContact(HANDLE hContact)
+{
+ char *proto = (char*) CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM) hContact, 0);
+
+ if (proto != NULL && PollCheckProtocol(proto))
+ {
+ // Delete some messages now (don't add to list...)
+ Check what = ProtocolStatusCheckMsg(hContact, proto);
+
+ if (what == Retrieve)
+ {
+ if (opts.poll_clear_on_status_change)
+ ClearStatusMessage(hContact);
+
+ if (opts.poll_check_on_status_change)
+ QueueAdd(hContact, GetTickCount(), STATUS_CHANGE);
+
+ if (opts.poll_check_on_status_change_timer)
+ QueueAdd(hContact, GetTickCount() + opts.poll_timer_status * 1000, STATUS_CHANGE_TIMER);
+ }
+ else if (what == UseXStatus || what == ClearXStatus)
+ {
+ PollXStatusChangeAddContact(hContact);
+ }
+ else
+ {
+ ProcessCheckNotToServer(what, hContact, proto);
+ }
+ }
+}
+
+// Check after some time, to allow proto to set all data about XStatus
+void PollXStatusChangeAddContact(HANDLE hContact)
+{
+ char *proto = (char*) CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM) hContact, 0);
+
+ if (proto != NULL && PollCheckProtocol(proto) && opts.when_xstatus != Normal)
+ {
+ QueueAdd(hContact, GetTickCount() + 200, XSTATUS_CHANGE);
+ }
+}
+
+void PollAddAllContactsTimer(int timer)
+{
+ PollAddAllContacts(timer, NULL, POOL);
+}
+
+void PollAddAllContactsProtoOnline(int timer, const char *protocol)
+{
+ PollAddAllContacts(timer, protocol, PROTOCOL_ONLINE);
+}
+
+void PollAddAllContacts(int timer, const char *protocol, int type)
+{
+ // Has to check?
+ if (protocol != NULL && !PollCheckProtocol(protocol))
+ return;
+
+ // Make list for next timer ...
+ HANDLE hContact = (HANDLE)CallService(MS_DB_CONTACT_FINDFIRST, 0, 0);
+ while (hContact)
+ {
+ char *proto = (char*)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0);
+
+ if (proto != NULL && (protocol == NULL || strcmp(proto, protocol) == 0)
+ && (protocol != NULL || PollCheckProtocol(proto)))
+ {
+ if (type == PROTOCOL_ONLINE)
+ {
+ Check what = ProtocolStatusCheckMsg(hContact, proto);
+ ProcessCheckNotToServer(what, hContact, proto);
+ }
+
+ QueueAdd(hContact, GetTickCount() + timer, type);
+ }
+
+ hContact = (HANDLE)CallService(MS_DB_CONTACT_FINDNEXT, (WPARAM)hContact, 0);
+ }
+}
+
+VOID CALLBACK PollTimerAddContacts(HWND hwnd, UINT uMsg, UINT idEvent, DWORD dwTime)
+{
+ if (hTimer != NULL)
+ {
+ KillTimer(NULL, hTimer);
+ hTimer = NULL;
+ }
+
+ // Adds with half the timer because the real timer is the calling of this function.
+ // The timer passed here is the amout of time that it will wait to see if the user do not
+ // request the message by itself
+ PollAddAllContactsTimer(opts.poll_timer_check * 60000 / 3);
+
+ PollSetTimer();
+}
+
+
+void PollSetTimer(void)
+{
+ if (hTimer != NULL)
+ {
+ KillTimer(NULL, hTimer);
+ hTimer = NULL;
+ }
+
+ if (opts.poll_check_on_timer)
+ hTimer = SetTimer(NULL, 0, opts.poll_timer_check * 60000, PollTimerAddContacts);
+}
+
+
+// Remove a contact from the current poll
+void PollReceivedContactMessage(HANDLE hContact, BOOL from_network)
+{
+ QueueRemove(hContact);
+
+ if (from_network)
+ {
+ EnterCriticalSection(&update_cs);
+
+ if (requested_item != NULL && requested_item->hContact == hContact)
+ {
+ mir_free(requested_item);
+ requested_item = NULL;
+ }
+
+ LeaveCriticalSection(&update_cs);
+
+ SetNextRequestAt(GetTickCount() + UpdateDelay());
+ }
+}
+
+
+void QueueRemove(HANDLE hContact)
+{
+ EnterCriticalSection(&update_cs);
+
+ if (statusQueue.queue->items != NULL)
+ {
+ DWORD now = GetTickCount() + 5000; // 5secs error allowed
+
+ for (int i = statusQueue.queue->realCount - 1 ; i >= 0 ; i-- )
+ {
+ QueueItem *item = (QueueItem*) statusQueue.queue->items[i];
+
+ if (item->hContact == hContact)
+ {
+ // Remove old items and status changes
+ if ((item->type & ~STATUS_CHANGE_TIMER) || item->check_time <= now)
+ {
+ mir_free(item);
+ List_Remove(statusQueue.queue, i);
+ }
+ }
+ }
+ }
+
+ LeaveCriticalSection(&update_cs);
+}
+
+
+// Test if has to add an item
+BOOL QueueTestAdd(QueueItem *item)
+{
+ BOOL add = TRUE;
+
+ switch(item->type)
+ {
+ case XSTATUS_CHANGE:
+ {
+ // Remove ALL
+ int i;
+ for ( i = statusQueue.queue->realCount - 1 ; i >= 0 ; i-- )
+ {
+ QueueItem *tmp = (QueueItem *) statusQueue.queue->items[i];
+ if (tmp->hContact == item->hContact)
+ {
+ mir_free(tmp);
+ List_Remove(statusQueue.queue, i);
+ }
+ }
+ break;
+ }
+ case STATUS_CHANGE:
+ {
+ // Remove all, exept PROTOCOL_ONLINE
+ int i;
+ int test = ~PROTOCOL_ONLINE;
+ for ( i = statusQueue.queue->realCount - 1 ; i >= 0 ; i-- )
+ {
+ QueueItem *tmp = (QueueItem *) statusQueue.queue->items[i];
+ if (tmp->hContact == item->hContact)
+ {
+ if (tmp->type & test)
+ {
+ mir_free(tmp);
+ List_Remove(statusQueue.queue, i);
+ }
+ else
+ {
+ add = FALSE;
+ }
+ }
+ }
+
+ if (requested_item != NULL && requested_item->hContact == item->hContact)
+ {
+ add = FALSE;
+ }
+
+ break;
+ }
+ case STATUS_CHANGE_TIMER:
+ {
+ // Remove STATUS_CHANGE_TIMER and POOL
+ int i;
+ int test = STATUS_CHANGE_TIMER | POOL;
+ for ( i = statusQueue.queue->realCount - 1 ; i >= 0 ; i-- )
+ {
+ QueueItem *tmp = (QueueItem *) statusQueue.queue->items[i];
+ if (tmp->hContact == item->hContact)
+ {
+ if (tmp->type & test)
+ {
+ mir_free(tmp);
+ List_Remove(statusQueue.queue, i);
+ }
+ else if (tmp->type & PROTOCOL_ONLINE)
+ {
+ add = FALSE;
+ }
+ }
+ }
+ break;
+ }
+ case PROTOCOL_ONLINE:
+ {
+ // Remove ALL
+ int i;
+ for ( i = statusQueue.queue->realCount - 1 ; i >= 0 ; i-- )
+ {
+ QueueItem *tmp = (QueueItem *) statusQueue.queue->items[i];
+ if (tmp->hContact == item->hContact)
+ {
+ mir_free(tmp);
+ List_Remove(statusQueue.queue, i);
+ }
+ }
+ break;
+ }
+ //case POOL:
+ //{
+ // // Dont remove, allways add
+ // break;
+ //}
+ }
+
+ return add;
+}
+
+// Add an contact to the poll queue
+void QueueAdd(HANDLE hContact, DWORD check_time, int type)
+{
+ // Add this to thread...
+ QueueItem *item = (QueueItem *) mir_alloc0(sizeof(QueueItem));
+
+ if (item == NULL)
+ return;
+
+ item->hContact = hContact;
+ item->check_time = check_time;
+ item->type = type;
+
+ EnterCriticalSection(&update_cs);
+
+ if (QueueTestAdd(item))
+ {
+ List_InsertOrdered(statusQueue.queue, item);
+ }
+ else
+ {
+ mir_free(item);
+ }
+
+ LeaveCriticalSection(&update_cs);
+
+ ResumeThread(statusQueue.hThread);
+}
+
+
+// Itens with higher priority at end
+int QueueSortItems(void *i1, void *i2)
+{
+ return ((QueueItem*)i2)->check_time - ((QueueItem*)i1)->check_time;
+}
+
+// Returns if has to check that status
+Check ProtocolStatusCheckMsg(HANDLE hContact, const char *protocol)
+{
+ BOOL check = PollCheckContact(hContact);
+ int status = CallProtoService(protocol, PS_GETSTATUS, 0, 0);
+
+ // Exclude offline, connecting and invisible
+ if (status >= ID_STATUS_ONLINE && status <= ID_STATUS_OUTTOLUNCH && status != ID_STATUS_INVISIBLE)
+ {
+ // Status
+ int contact_status = DBGetContactSettingWord(hContact, protocol, "Status", 0);
+ int contact_xstatus = DBGetContactSettingByte(hContact, protocol, "XStatusId", 0);
+
+ // Check
+ if (opts.when_xstatus != Normal && contact_xstatus != 0)
+ {
+ // Use XStatus
+
+ bool has_xstatus_name = false;
+ bool has_xstatus_message = false;
+
+ DBVARIANT dbv;
+ if (!DBGetContactSettingString(hContact, protocol, "XStatusName", &dbv))
+ {
+ if (dbv.pszVal != NULL && dbv.pszVal[0] != '\0')
+ {
+ has_xstatus_name = true;
+ }
+ DBFreeVariant(&dbv);
+ }
+ if (!DBGetContactSettingString(hContact, protocol, "XStatusMsg", &dbv))
+ {
+ if (dbv.pszVal != NULL && dbv.pszVal[0] != '\0')
+ {
+ has_xstatus_message = true;
+ }
+ DBFreeVariant(&dbv);
+ }
+
+ if (opts.when_xstatus == Clear
+ || (opts.when_xstatus == ClearOnMessage && has_xstatus_message) )
+ {
+ return (check || opts.always_clear) ? ClearMessage : DoNothing;
+ }
+ else if (opts.when_xstatus == SetToXStatusName
+ || opts.when_xstatus == SetToXStatusMessage
+ || opts.when_xstatus == SetToXStatusNameXStatusMessage)
+ {
+ return check ? UseXStatus : DoNothing;
+ }
+ }
+
+ // If get until this point, Use normal status message
+ DWORD protoStatusFlags = CallProtoService(protocol, PS_GETCAPS, PFLAGNUM_3, 0);
+ if (protoStatusFlags & Proto_Status2Flag(contact_status))
+ {
+ return check ? Retrieve : DoNothing;// here you know the proto named protocol supports status i
+ }
+ else
+ {
+ return (check || opts.always_clear) ? ClearMessage : DoNothing;
+ }
+ }
+ else // Protocol in a status that do not have to check msgs
+ {
+ return (check || opts.always_clear) ? ClearMessage : DoNothing;
+ }
+}
+
+// This should be called from outside the critical session
+void ProcessCheckNotToServer(Check what, HANDLE hContact, const char *protocol)
+{
+ switch(what)
+ {
+ case ClearMessage:
+ {
+ ClearStatusMessage(hContact);
+ PollReceivedContactMessage(hContact, FALSE);
+ break;
+ }
+ case UseXStatus:
+ {
+ bool has_xstatus_name = false;
+ bool has_xstatus_message = false;
+ char name[256];
+ char msg[256];
+ name[0] = '\0';
+ msg[0] = '\0';
+
+ DBVARIANT dbv;
+ if (!DBGetContactSettingString(hContact, protocol, "XStatusName", &dbv))
+ {
+ if (dbv.pszVal != NULL && dbv.pszVal[0] != '\0')
+ {
+ strncpy(name, dbv.pszVal, sizeof(name));
+ name[sizeof(name)-1] = '\0';
+ has_xstatus_name = true;
+ }
+ DBFreeVariant(&dbv);
+ }
+ if (!DBGetContactSettingString(hContact, protocol, "XStatusMsg", &dbv))
+ {
+ if (dbv.pszVal != NULL && dbv.pszVal[0] != '\0')
+ {
+ strncpy(msg, dbv.pszVal, sizeof(msg));
+ msg[sizeof(msg)-1] = '\0';
+ has_xstatus_message = true;
+ }
+ DBFreeVariant(&dbv);
+ }
+
+ // SetToXStatusName
+ if (opts.when_xstatus == SetToXStatusName)
+ {
+ SetStatusMessage(hContact, name);
+ PollReceivedContactMessage(hContact, FALSE);
+ }
+
+ // SetToXStatusMessage
+ else if (opts.when_xstatus == SetToXStatusMessage)
+ {
+ SetStatusMessage(hContact, msg);
+ PollReceivedContactMessage(hContact, FALSE);
+ }
+
+ // SetToXStatusNameXStatusValue
+ else if (opts.when_xstatus == SetToXStatusNameXStatusMessage)
+ {
+ if (has_xstatus_name && has_xstatus_message)
+ {
+ if (strcmp(name, msg) == 0)
+ {
+ // Both are the same, use only one
+ SetStatusMessage(hContact, name);
+ }
+ else
+ {
+ char message[512];
+ mir_snprintf(message, sizeof(message), "%s: %s", name, msg);
+ SetStatusMessage(hContact, message);
+ }
+ }
+ else if (has_xstatus_name)
+ {
+ SetStatusMessage(hContact, name);
+ }
+ else if (has_xstatus_message)
+ {
+ SetStatusMessage(hContact, msg);
+ }
+ PollReceivedContactMessage(hContact, FALSE);
+ }
+ break;
+ }
+ }
+}
+
+
+DWORD WINAPI UpdateThread(LPVOID vParam)
+{
+ // Initial timer
+ Sleep(INITIAL_TIMER);
+
+ while (statusQueue.bThreadRunning)
+ {
+ // First remove all that are due and do not have to be pooled
+ EnterCriticalSection(&update_cs);
+
+ if (!List_HasItens(statusQueue.queue))
+ {
+ LeaveCriticalSection(&update_cs);
+ }
+ else
+ {
+ // Create a copy of the queue
+ SortedList *tmpQueue;
+ tmpQueue = List_Create(0, statusQueue.queue->realCount);
+ tmpQueue->sortFunc = QueueSortItems;
+
+ for (int i = statusQueue.queue->realCount - 1; i >= 0; i--)
+ {
+ QueueItem *qi = (QueueItem *) List_Pop(statusQueue.queue);
+
+ if (qi->check_time > GetTickCount())
+ {
+ List_Push(statusQueue.queue, qi);
+ break;
+ }
+ else
+ {
+ List_InsertOrdered(tmpQueue, qi);
+ }
+ }
+
+ LeaveCriticalSection(&update_cs);
+
+ if (!statusQueue.bThreadRunning)
+ break;
+
+ // See the itens that was copied
+ if (List_HasItens(tmpQueue))
+ {
+ for (int i = tmpQueue->realCount - 1; i >= 0; i--)
+ {
+ if (!statusQueue.bThreadRunning)
+ break;
+
+ QueueItem *qi = (QueueItem *) tmpQueue->items[i];
+
+ char *proto = (char *)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)qi->hContact, 0);
+
+ if (proto == NULL || !PollCheckProtocol(proto))
+ {
+ List_Remove(tmpQueue, i);
+ mir_free(qi);
+ }
+ else
+ {
+ Check what = ProtocolStatusCheckMsg(qi->hContact, proto);
+
+ if (what != DoNothing && what != Retrieve)
+ {
+ ProcessCheckNotToServer(what, qi->hContact, proto);
+
+ List_Remove(tmpQueue, i);
+ mir_free(qi);
+ }
+ else if (what == DoNothing)
+ {
+ List_Remove(tmpQueue, i);
+ mir_free(qi);
+ }
+ }
+ }
+
+ if (!statusQueue.bThreadRunning)
+ break;
+
+ // See if has itens to be copied back
+ if (List_HasItens(tmpQueue))
+ {
+ EnterCriticalSection(&update_cs);
+ for (int i = tmpQueue->realCount - 1; i >= 0; i--)
+ {
+ QueueItem *qi = (QueueItem *) List_Pop(tmpQueue);
+ List_InsertOrdered(statusQueue.queue, qi);
+ }
+ LeaveCriticalSection(&update_cs);
+ }
+ }
+ }
+
+ if (!statusQueue.bThreadRunning)
+ break;
+
+ // Was to run yet?
+ DWORD test_timer = GetNextRequestAt();
+ if (test_timer > GetTickCount())
+ {
+ if (statusQueue.bThreadRunning)
+ Sleep(POOL_DELAY);
+ }
+ else
+ {
+ // Ok, lets check the message
+ EnterCriticalSection(&update_cs);
+
+ if (requested_item != NULL)
+ {
+ if (GetTickCount() < requested_item->check_time + WaitTime())
+ {
+ LeaveCriticalSection(&update_cs);
+
+ if (statusQueue.bThreadRunning)
+ Sleep(POOL_DELAY);
+ }
+ else
+ {
+ mir_free(requested_item);
+ requested_item = NULL;
+
+ LeaveCriticalSection(&update_cs);
+ }
+ }
+ else if (!List_HasItens(statusQueue.queue))
+ {
+ LeaveCriticalSection(&update_cs);
+
+ // Stop this one...
+ if (statusQueue.bThreadRunning)
+ SuspendThread(statusQueue.hThread);
+ }
+ else
+ {
+ // Get next job...
+
+ /*
+ * the thread is awake, processing the update queue one by one entry with a given delay
+ * of UPDATE_DELAY seconds. The delay ensures that no protocol will kick us because of
+ * flood protection(s)
+ */
+ QueueItem *qi = (QueueItem *) List_Peek(statusQueue.queue);
+
+ if (qi->check_time > GetTickCount())
+ {
+ LeaveCriticalSection(&update_cs);
+
+ if (statusQueue.bThreadRunning)
+ Sleep(POOL_DELAY);
+ }
+ else
+ {
+ qi = (QueueItem *) List_Pop(statusQueue.queue);
+
+ LeaveCriticalSection(&update_cs);
+
+ if (!statusQueue.bThreadRunning)
+ break;
+
+ char *proto = (char *)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)qi->hContact, 0);
+
+ if (proto == NULL || !PollCheckProtocol(proto))
+ {
+ mir_free(qi);
+ }
+ else
+ {
+ Check what = ProtocolStatusCheckMsg(qi->hContact, proto);
+
+ if (what == DoNothing)
+ {
+ mir_free(qi);
+ }
+ else if (what != Retrieve)
+ {
+ ProcessCheckNotToServer(what, qi->hContact, proto);
+ mir_free(qi);
+ }
+ else // if (what == Retrieve)
+ {
+ if (!statusQueue.bThreadRunning)
+ break;
+
+ int ret = CallContactService(qi->hContact,PSS_GETAWAYMSG,0,0);
+
+ if (ret != 0)
+ {
+ EnterCriticalSection(&update_cs);
+
+ requested_item = qi;
+ requested_item->check_time = GetTickCount();
+
+ LeaveCriticalSection(&update_cs);
+
+ if (statusQueue.bThreadRunning)
+ Sleep(POOL_DELAY);
+ }
+ else
+ {
+ EnterCriticalSection(&update_cs);
+
+ // Error, pause for a while
+ List_Push(statusQueue.queue, qi);
+
+ LeaveCriticalSection(&update_cs);
+
+ int delay = ErrorDelay();
+ SetNextRequestAt(GetTickCount() + delay);
+ if (statusQueue.bThreadRunning)
+ Sleep(delay);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
+
+void SetNextRequestAt(DWORD t)
+{
+ EnterCriticalSection(&pause_cs);
+ next_request_at = t;
+ LeaveCriticalSection(&pause_cs);
+}
+
+DWORD GetNextRequestAt(void)
+{
+ EnterCriticalSection(&pause_cs);
+ DWORD ret = next_request_at;
+ LeaveCriticalSection(&pause_cs);
+
+ return ret;
+}
diff --git a/Plugins/smr/poll.h b/Plugins/smr/poll.h
new file mode 100644
index 0000000..3a29e6d
--- /dev/null
+++ b/Plugins/smr/poll.h
@@ -0,0 +1,62 @@
+/*
+Copyright (C) 2006 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#ifndef __POLL_H__
+# define __POLL_H__
+
+#include <windows.h>
+
+#include "commons.h"
+
+
+void InitPoll();
+void FreePoll();
+
+void PollSetTimer(void);
+
+BOOL PollCheckProtocol(const char *protocol);
+BOOL PollCheckContact(HANDLE hContact);
+
+typedef enum {
+ Retrieve,
+ DoNothing,
+ ClearMessage,
+ UseXStatus,
+ ClearXStatus
+} Check;
+Check ProtocolStatusCheckMsg(HANDLE hContact, const char *protocol);
+void ProcessCheckNotToServer(Check what, HANDLE hContact, const char *protocol);
+
+void PollReceivedContactMessage(HANDLE hContact, BOOL from_network);
+
+void PollStatusChangeAddContact(HANDLE hContact);
+void PollXStatusChangeAddContact(HANDLE hContact);
+void PollAddAllContactsTimer(int timer);
+void PollAddAllContactsProtoOnline(int timer, const char *protocol);
+
+
+
+
+
+
+
+
+
+#endif // __POLL_H__
diff --git a/Plugins/smr/resource.h b/Plugins/smr/resource.h
new file mode 100644
index 0000000..5cb6092
--- /dev/null
+++ b/Plugins/smr/resource.h
@@ -0,0 +1,74 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Developer Studio generated include file.
+// Used by resource.rc
+//
+#define IDI_HIST 101
+#define IDI_POPUP 102
+#define IDI_NOPOPUP 103
+#define IDI_EXT 104
+#define IDI_INT 105
+#define IDI_LIST 106
+#define IDI_URL 107
+#define IDB_LIST 108
+#define IDD_HISTORY 109
+#define IDD_OPT 110
+#define IDD_LIST 111
+#define IDD_OPT_NTF 112
+#define IDD_OPT_PROTOCOLS 114
+#define IDD_OPT_GENERAL 117
+#define IDD_OPTS 118
+#define IDR_PMENU 200
+#define IDC_PREVIEW 1000
+#define IDC_CHKDEFAULTCOL 1001
+#define IDC_DELAY 1002
+#define IDC_HISMAX 1003
+#define IDC_CHECK_ONTIMER_TIMER 1004
+#define IDC_CHECK_ONSTATUSTIMER_TIMER 1005
+#define IDC_FHIS 1011
+#define IDC_FLOG 1012
+#define IDC_LIST 1013
+#define IDC_IGNOREPOP 1014
+#define IDC_HISTORYLIST 1015
+#define IDC_IGNOREALL 1016
+#define IDC_COLLISTBACK 1017
+#define IDC_COLLISTTEXT 1018
+#define IDC_DP 1019
+#define IDC_HIDECMENUITEMS 1037
+#define IDC_CHECK_ONTIMER 1038
+#define IDC_CHECK_ONSTATUSTIMER 1039
+#define IDC_PROTOCOLS 1041
+#define IDC_DELAYINFINITE 1042
+#define IDC_LOGTOFILE 1043
+#define IDC_AUTOREFRESH 1044
+#define IDC_CLEARHIST 1045
+#define IDC_CLEARALLHIS 1048
+#define IDC_CHECK_MSG_TIMER_SPIN 1049
+#define IDC_CHECK_ONTIMER_TIMER_SPIN 1049
+#define IDC_CHECK_STATUS_TIMER_SPIN 1050
+#define IDC_CHECK_ONSTATUS_TIMER_SPIN 1050
+#define IDC_CHECK_ONSTATUSTIMER_TIMER_SPIN 1050
+#define IDC_CLEAR_ON_STATUS 1051
+#define IDC_RETRIEVE_ON_STATUS 1052
+#define IDC_CHECK_ONSTATUS 1052
+#define IDC_ALWAYS_CLEAR 1053
+#define IDC_XSTATUS 1054
+#define IDC_TAB 1056
+#define IDM_M1 40001
+#define IDM_M2 40003
+#define IDM_M3 40005
+#define IDM_M5 40007
+#define IDM_M4 40008
+#define IDC_STATIC -1
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NO_MFC 1
+#define _APS_3D_CONTROLS 1
+#define _APS_NEXT_RESOURCE_VALUE 119
+#define _APS_NEXT_COMMAND_VALUE 40004
+#define _APS_NEXT_CONTROL_VALUE 1058
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/Plugins/smr/resource.rc b/Plugins/smr/resource.rc
new file mode 100644
index 0000000..a972e73
--- /dev/null
+++ b/Plugins/smr/resource.rc
@@ -0,0 +1,163 @@
+//Microsoft Developer Studio generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "resource.h"
+#include "winresrc.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (U.S.) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page(1252)
+#endif //_WIN32
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Dialog
+//
+
+IDD_OPT_PROTOCOLS DIALOG DISCARDABLE 0, 0, 257, 164
+STYLE WS_CHILD
+FONT 8, "MS Shell Dlg"
+BEGIN
+ LTEXT "Get status messages for these protocols:",IDC_STATIC,10,
+ 9,236,9
+ CONTROL "List1",IDC_PROTOCOLS,"SysListView32",LVS_REPORT |
+ LVS_SINGLESEL | LVS_SORTASCENDING | LVS_NOCOLUMNHEADER |
+ LVS_NOSORTHEADER | WS_BORDER | WS_TABSTOP,10,21,236,90
+ LTEXT "This feature is mainly for use with protocols that store status messages on server rather then in database (like ICQ). Don't enable it for MSN or Jabber - they already save the status message in database and won't work correctly if you enable this.",
+ IDC_STATIC,10,114,236,47
+END
+
+IDD_OPT_GENERAL DIALOGEX 0, 0, 271, 167
+STYLE WS_CHILD
+FONT 8, "MS Shell Dlg"
+BEGIN
+ GROUPBOX " Retrieve ",IDC_STATIC,7,7,257,60
+ CONTROL "Retrieve status messages every",IDC_CHECK_ONTIMER,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,15,20,119,10
+ EDITTEXT IDC_CHECK_ONTIMER_TIMER,137,20,31,12,ES_NUMBER | NOT
+ WS_BORDER,WS_EX_STATICEDGE
+ CONTROL "Spin1",IDC_CHECK_ONTIMER_TIMER_SPIN,"msctls_updown32",
+ UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_ARROWKEYS |
+ UDS_NOTHOUSANDS | UDS_HOTTRACK,157,18,11,11
+ LTEXT "minutes",IDC_STATIC,173,21,37,10
+ CONTROL "Retrieve on status change",IDC_CHECK_ONSTATUS,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,15,34,216,10
+ CONTROL "Also retrieve after",IDC_CHECK_ONSTATUSTIMER,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,15,48,76,8
+ EDITTEXT IDC_CHECK_ONSTATUSTIMER_TIMER,93,47,31,12,ES_NUMBER |
+ NOT WS_BORDER,WS_EX_STATICEDGE
+ CONTROL "Spin2",IDC_CHECK_ONSTATUSTIMER_TIMER_SPIN,
+ "msctls_updown32",UDS_SETBUDDYINT | UDS_ALIGNRIGHT |
+ UDS_ARROWKEYS | UDS_NOTHOUSANDS | UDS_HOTTRACK,119,42,11,
+ 10
+ LTEXT "seconds after status change",IDC_STATIC,130,48,99,10
+ GROUPBOX " Clear ",IDC_STATIC,7,70,257,53
+ CONTROL "Clear message on status change",IDC_CLEAR_ON_STATUS,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,15,83,216,10
+ CONTROL "Always clear message if status does not support messages",
+ IDC_ALWAYS_CLEAR,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,
+ 15,97,216,10
+ LTEXT "(even for contacts with msg check disabled)",IDC_STATIC,
+ 28,108,204,10
+ GROUPBOX " XStatus ",IDC_STATIC,7,126,257,34
+ LTEXT "If contact has XStatus set:",IDC_STATIC,15,141,92,13
+ COMBOBOX IDC_XSTATUS,109,139,148,72,CBS_DROPDOWNLIST | WS_VSCROLL |
+ WS_TABSTOP
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// DESIGNINFO
+//
+
+#ifdef APSTUDIO_INVOKED
+GUIDELINES DESIGNINFO DISCARDABLE
+BEGIN
+ IDD_OPT_PROTOCOLS, DIALOG
+ BEGIN
+ LEFTMARGIN, 3
+ RIGHTMARGIN, 254
+ VERTGUIDE, 10
+ VERTGUIDE, 246
+ TOPMARGIN, 3
+ BOTTOMMARGIN, 161
+ END
+
+ IDD_OPT_GENERAL, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 264
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 160
+ END
+END
+#endif // APSTUDIO_INVOKED
+
+#endif // English (U.S.) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+/////////////////////////////////////////////////////////////////////////////
+// English (Canada) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENC)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_CAN
+#pragma code_page(1252)
+#endif //_WIN32
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "resource.h\0"
+END
+
+2 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "#include ""resource.h""\r\n"
+ "#include ""winresrc.h""\r\n"
+ "\0"
+END
+
+3 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "\r\n"
+ "\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+#endif // English (Canada) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
+
diff --git a/Plugins/smr/sdk/m_updater.h b/Plugins/smr/sdk/m_updater.h
new file mode 100644
index 0000000..371b743
--- /dev/null
+++ b/Plugins/smr/sdk/m_updater.h
@@ -0,0 +1,146 @@
+#ifndef _M_UPDATER_H
+#define _M_UPDATER_H
+
+// NOTES:
+// - For langpack updates, include a string of the following format in the langpack text file:
+// ";FLID: <file listing name> <version>"
+// version must be four numbers seperated by '.', in the range 0-255 inclusive
+// - Updater will disable plugins that are downloaded but were not active prior to the update (this is so that, if an archive contains e.g. ansi and
+// unicode versions, the correct plugin will be the only one active after the new version is installed)...so if you add a support plugin, you may need
+// to install an ini file to make the plugin activate when miranda restarts after the update
+// - Updater will replace all dlls that have the same internal shortName as a downloaded update dll (this is so that msn1.dll and msn2.dll, for example,
+// will both be updated) - so if you have a unicode and a non-unicode version of a plugin in your archive, you should make the internal names different (which will break automatic
+// updates from the file listing if there is only one file listing entry for both versions, unless you use the 'MS_UPDATE_REGISTER' service below)
+// - Updater will install all files in the root of the archive into the plugins folder, except for langpack files that contain the FLID string which go into the root folder (same
+// folder as miranda32.exe)...all folders in the archive will also be copied to miranda's root folder, and their contents transferred into the new folders. The only exception is a
+// special folder called 'root_files' - if there is a folder by that name in the archive, it's contents will also be copied into miranda's root folder - this is intended to be used
+// to install additional dlls etc that a plugin may require)
+
+// if you set Update.szUpdateURL to the following value when registering, as well as setting your beta site and version data,
+// Updater will ignore szVersionURL and pbVersionPrefix, and attempt to find the file listing URL's from the backend XML data.
+// for this to work, the plugin name in pluginInfo.shortName must match the file listing exactly (except for case)
+#define UPDATER_AUTOREGISTER "UpdaterAUTOREGISTER"
+// Updater will also use the backend xml data if you provide URL's that reference the miranda file listing for updates (so you can use that method
+// if e.g. your plugin shortName does not match the file listing) - it will grab the file listing id from the end of these URLs
+
+typedef struct Update_tag {
+ int cbSize;
+ char *szComponentName; // component name as it will appear in the UI (will be translated before displaying)
+
+ char *szVersionURL; // URL where the current version can be found (NULL to disable)
+ BYTE *pbVersionPrefix; // bytes occuring in VersionURL before the version, used to locate the version information within the URL data
+ // (note that this URL could point at a binary file - dunno why, but it could :)
+ int cpbVersionPrefix; // number of bytes pointed to by pbVersionPrefix
+ char *szUpdateURL; // URL where dll/zip is located
+ // set to UPDATER_AUTOREGISTER if you want Updater to find the file listing URLs (ensure plugin shortName matches file listing!)
+
+ char *szBetaVersionURL; // URL where the beta version can be found (NULL to disable betas)
+ BYTE *pbBetaVersionPrefix; // bytes occuring in VersionURL before the version, used to locate the version information within the URL data
+ int cpbBetaVersionPrefix; // number of bytes pointed to by pbVersionPrefix
+ char *szBetaUpdateURL; // URL where dll/zip is located
+
+ BYTE *pbVersion; // bytes of current version, used for comparison with those in VersionURL
+ int cpbVersion; // number of bytes pointed to by pbVersion
+
+ char *szBetaChangelogURL; // url for displaying changelog for beta versions
+} Update;
+
+// register a comonent with Updater
+//
+// wparam = 0
+// lparam = (LPARAM)&Update
+#define MS_UPDATE_REGISTER "Update/Register"
+
+// utility functions to create a version string from a DWORD or from pluginInfo
+// point buf at a buffer at least 16 chars wide - but note the version string returned may be shorter
+//
+__inline static char *CreateVersionString(DWORD version, char *buf) {
+ mir_snprintf(buf, 16, "%d.%d.%d.%d", (version >> 24) & 0xFF, (version >> 16) & 0xFF, (version >> 8) & 0xFF, version & 0xFF);
+ return buf;
+}
+
+__inline static char *CreateVersionStringPlugin(PLUGININFO *pluginInfo, char *buf) {
+ return CreateVersionString(pluginInfo->version, buf);
+}
+
+
+// register the 'easy' way - use this method if you have no beta URL and the plugin is on the miranda file listing
+// NOTE: the plugin version string on the file listing must be the string version of the version in pluginInfo (i.e. 0.0.0.1,
+// four numbers between 0 and 255 inclusivem, so no letters, brackets, etc.)
+//
+// wParam = (int)fileID - this is the file ID from the file listing (i.e. the number at the end of the download link)
+// lParam = (PLUGININFO*)&pluginInfo
+#define MS_UPDATE_REGISTERFL "Update/RegisterFL"
+
+// this function can be used to 'unregister' components - useful for plugins that register non-plugin/langpack components and
+// may need to change those components on the fly
+// lParam = (char *)szComponentName
+#define MS_UPDATE_UNREGISTER "Update/Unregister"
+
+// this event is fired when the startup process is complete, but NOT if a restart is imminent
+// it is designed for status managment plugins to use as a trigger for beggining their own startup process
+// wParam = lParam = 0 (unused)
+// (added in version 0.1.6.0)
+#define ME_UPDATE_STARTUPDONE "Update/StartupDone"
+
+// this service can be used to enable/disable Updater's global status control
+// it can be called from the StartupDone event handler
+// wParam = (BOOL)enable
+// lParam = 0
+// (added in version 0.1.6.0)
+#define MS_UPDATE_ENABLESTATUSCONTROL "Update/EnableStatusControl"
+
+// An description of usage of the above service and event:
+// Say you are a status control plugin that normally sets protocol or global statuses in your ModulesLoaded event handler.
+// In order to make yourself 'Updater compatible', you would move the status control code from ModulesLoaded to another function,
+// say DoStartup. Then, in ModulesLoaded you would check for the existence of the MS_UPDATE_ENABLESTATUSCONTROL service.
+// If it does not exist, call DoStartup. If it does exist, hook the ME_UPDATE_STARTUPDONE event and call DoStartup from there. You may
+// also wish to call MS_UPDATE_ENABLESTATUSCONTROL with wParam == FALSE at this time, to disable Updater's own status control feature.
+
+// this service can be used to determine whether updates are possible for a component with the given name
+// wParam = 0
+// lParam = (char *)szComponentName
+// returns TRUE if updates are supported, FALSE otherwise
+#define MS_UPDATE_ISUPDATESUPPORTED "Update/IsUpdateSupported"
+
+#endif
+
+
+/////////////// Usage Example ///////////////
+
+#ifdef EXAMPLE_CODE
+
+// you need to #include "m_updater.h" and HookEvent(ME_SYSTEM_MODULESLOADED, OnModulesLoaded) in your Load function...
+
+int OnModulesLoaded(WPARAM wParam, LPARAM lParam) {
+
+ Update update = {0}; // for c you'd use memset or ZeroMemory...
+ char szVersion[16];
+
+ update.cbSize = sizeof(Update);
+
+ update.szComponentName = pluginInfo.shortName;
+ update.pbVersion = (BYTE *)CreateVersionString(&pluginInfo, szVersion);
+ update.cpbVersion = strlen((char *)update.pbVersion);
+
+ // these are the three lines that matter - the archive, the page containing the version string, and the text (or data)
+ // before the version that we use to locate it on the page
+ // (note that if the update URL and the version URL point to standard file listing entries, the backend xml
+ // data will be used to check for updates rather than the actual web page - this is not true for beta urls)
+ update.szUpdateURL = "http://scottellis.com.au:81/test/updater.zip";
+ update.szVersionURL = "http://scottellis.com.au:81/test/updater_test.html";
+ update.pbVersionPrefix = (BYTE *)"Updater version ";
+
+ update.cpbVersionPrefix = strlen((char *)update.pbVersionPrefix);
+
+ // do the same for the beta versions of the above struct members if you wish to allow beta updates from another URL
+
+ CallService(MS_UPDATE_REGISTER, 0, (WPARAM)&update);
+
+ // Alternatively, to register a plugin with e.g. file ID 2254 on the file listing...
+ // CallService(MS_UPDATE_REGISTERFL, (WPARAM)2254, (LPARAM)&pluginInfo);
+
+ return 0;
+}
+
+#endif
diff --git a/Plugins/smr/smr.cpp b/Plugins/smr/smr.cpp
new file mode 100644
index 0000000..94f4a9e
--- /dev/null
+++ b/Plugins/smr/smr.cpp
@@ -0,0 +1,276 @@
+/*
+Copyright (C) 2006 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#include "commons.h"
+
+
+// Prototypes ///////////////////////////////////////////////////////////////////////////
+
+
+PLUGININFOEX pluginInfo={
+ sizeof(PLUGININFOEX),
+ "Status Message Retriever",
+ PLUGIN_MAKE_VERSION(1,0,0,7),
+ "Retrieve status message based on timer / status change",
+ "Ricardo Pescuma Domenecci, Tomasz S³otwiñski",
+ "",
+ "© 2007 Ricardo Pescuma Domenecci, Tomasz S³otwiñski",
+ "http://pescuma.mirandaim.ru/miranda/smr",
+ 0,
+ 0, //doesn't replace anything built-in
+ { 0x800a5c24, 0x737b, 0x499f, { 0x96, 0xa2, 0x40, 0x46, 0xda, 0xa8, 0x41, 0xb1 } } // {800A5C24-737B-499f-96A2-4046DAA841B1}
+};
+
+
+HINSTANCE hInst;
+PLUGINLINK *pluginLink;
+
+HANDLE hEnableMenu = NULL;
+HANDLE hDisableMenu = NULL;
+HANDLE hModulesLoaded = NULL;
+HANDLE hPreBuildCMenu = NULL;
+
+int ModulesLoaded(WPARAM wParam, LPARAM lParam);
+
+int PreBuildContactMenu(WPARAM wParam,LPARAM lParam);
+
+int EnableContactMsgRetrieval(WPARAM wParam,LPARAM lParam);
+int DisableContactMsgRetrieval(WPARAM wParam,LPARAM lParam);
+int MsgRetrievalEnabledForUser(WPARAM wParam, LPARAM lParam);
+int MsgRetrievalEnabledForProtocol(WPARAM wParam, LPARAM lParam);
+
+
+// Functions ////////////////////////////////////////////////////////////////////////////
+
+
+extern "C" BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
+{
+ hInst = hinstDLL;
+ return TRUE;
+}
+
+
+extern "C" __declspec(dllexport) PLUGININFO* MirandaPluginInfo(DWORD mirandaVersion)
+{
+ pluginInfo.cbSize = sizeof(PLUGININFO);
+ return (PLUGININFO*) &pluginInfo;
+}
+
+
+extern "C" __declspec(dllexport) PLUGININFOEX* MirandaPluginInfoEx(DWORD mirandaVersion)
+{
+ pluginInfo.cbSize = sizeof(PLUGININFOEX);
+ return &pluginInfo;
+}
+
+
+static const MUUID interfaces[] = { MIID_STATUS_MESSAGE_RETRIEVER, MIID_LAST };
+extern "C" __declspec(dllexport) const MUUID* MirandaPluginInterfaces(void)
+{
+ return interfaces;
+}
+
+
+extern "C" int __declspec(dllexport) Load(PLUGINLINK *link) {
+ pluginLink = link;
+
+ init_mir_malloc();
+ init_list_interface();
+
+ CreateServiceFunction(MS_SMR_DISABLE_CONTACT, DisableContactMsgRetrieval);
+ CreateServiceFunction(MS_SMR_ENABLE_CONTACT, EnableContactMsgRetrieval);
+ CreateServiceFunction(MS_SMR_ENABLED_FOR_PROTOCOL, MsgRetrievalEnabledForProtocol);
+ CreateServiceFunction(MS_SMR_ENABLED_FOR_CONTACT, MsgRetrievalEnabledForUser);
+
+ // Add menu item to enable/disable status message check
+ CLISTMENUITEM mi = {0};
+ mi.cbSize = sizeof(mi);
+ mi.flags = 0;
+ mi.pszPopupName = NULL;
+
+ mi.position = 1000100020;
+ mi.ptszName = TranslateT("Disable Status Message Check");
+ mi.pszService = MS_SMR_DISABLE_CONTACT;
+ hDisableMenu = (HANDLE) CallService(MS_CLIST_ADDCONTACTMENUITEM, 0, (LPARAM)&mi);
+
+ mi.position = 1000100020;
+ mi.ptszName = TranslateT("Enable Status Message Check");
+ mi.pszService = MS_SMR_ENABLE_CONTACT;
+ hEnableMenu = (HANDLE) CallService(MS_CLIST_ADDCONTACTMENUITEM, 0, (LPARAM)&mi);
+
+ // hooks
+ hModulesLoaded = HookEvent(ME_SYSTEM_MODULESLOADED, ModulesLoaded);
+
+ // prebuild contact menu
+ hPreBuildCMenu = HookEvent(ME_CLIST_PREBUILDCONTACTMENU, PreBuildContactMenu);
+
+ return 0;
+}
+
+extern "C" int __declspec(dllexport) Unload(void)
+{
+ FreeStatus();
+ FreeStatusMsgs();
+ FreePoll();
+ DeInitOptions();
+
+ UnhookEvent(hModulesLoaded);
+ UnhookEvent(hPreBuildCMenu);
+ return 0;
+}
+
+
+// Called when all the modules are loaded
+int ModulesLoaded(WPARAM wParam, LPARAM lParam)
+{
+ InitPoll();
+ InitStatusMsgs();
+ InitStatus();
+ InitOptions();
+
+ // add our modules to the KnownModules list
+ CallService("DBEditorpp/RegisterSingleModule", (WPARAM) MODULE_NAME, 0);
+
+ // updater plugin support
+ if(ServiceExists(MS_UPDATE_REGISTER))
+ {
+ Update upd = {0};
+ char szCurrentVersion[30];
+
+ upd.cbSize = sizeof(upd);
+ upd.szComponentName = pluginInfo.shortName;
+
+ upd.szUpdateURL = UPDATER_AUTOREGISTER;
+
+ upd.szBetaVersionURL = "http://pescuma.org/miranda/smr_version.txt";
+ upd.szBetaChangelogURL = "http://pescuma.org/miranda/smr#Changelog";
+ upd.pbBetaVersionPrefix = (BYTE *)"Status Message Retriever ";
+ upd.cpbBetaVersionPrefix = strlen((char *)upd.pbBetaVersionPrefix);
+ upd.szBetaUpdateURL = "http://pescuma.org/miranda/smr.zip";
+
+ upd.pbVersion = (BYTE *)CreateVersionStringPlugin((PLUGININFO*) &pluginInfo, szCurrentVersion);
+ upd.cpbVersion = strlen((char *)upd.pbVersion);
+
+ CallService(MS_UPDATE_REGISTER, 0, (LPARAM)&upd);
+ }
+
+ return 0;
+}
+
+
+int EnableContactMsgRetrieval(WPARAM wParam,LPARAM lParam)
+{
+ HANDLE hContact = (HANDLE) wParam;
+
+ if (hContact != NULL)
+ DBWriteContactSettingByte(hContact, MODULE_NAME, OPT_CONTACT_GETMSG, TRUE);
+
+ return 0;
+}
+
+
+int DisableContactMsgRetrieval(WPARAM wParam,LPARAM lParam)
+{
+ HANDLE hContact = (HANDLE) wParam;
+
+ if (hContact != NULL)
+ DBWriteContactSettingByte(hContact, MODULE_NAME, OPT_CONTACT_GETMSG, FALSE);
+
+ return 0;
+}
+
+
+int PreBuildContactMenu(WPARAM wParam,LPARAM lParam)
+{
+ HANDLE hContact = (HANDLE) wParam;
+ char *proto = (char*)CallService(MS_PROTO_GETCONTACTBASEPROTO, wParam, 0);
+
+ if (proto == NULL || !PollCheckProtocol(proto))
+ {
+ // Hide both
+
+ CLISTMENUITEM clmi = {0};
+ clmi.cbSize = sizeof(clmi);
+ clmi.flags = CMIM_FLAGS | CMIF_HIDDEN;
+
+ CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM) hEnableMenu, (LPARAM)&clmi);
+ CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM) hDisableMenu, (LPARAM) &clmi);
+ }
+ else
+ {
+ // See what to show
+
+ CLISTMENUITEM clmi = {0};
+ clmi.cbSize = sizeof(clmi);
+
+ if (DBGetContactSettingByte(hContact, MODULE_NAME, OPT_CONTACT_GETMSG, TRUE))
+ {
+ clmi.flags = CMIM_FLAGS | CMIF_HIDDEN;
+ CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM) hEnableMenu, (LPARAM) &clmi);
+
+ clmi.flags = CMIM_FLAGS | CMIM_ICON;
+ clmi.hIcon = LoadSkinnedProtoIcon(proto, ID_STATUS_OFFLINE);
+ CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM) hDisableMenu, (LPARAM) &clmi);
+ }
+ else
+ {
+ clmi.flags = CMIM_FLAGS | CMIF_HIDDEN;
+ CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM) hDisableMenu, (LPARAM) &clmi);
+
+ clmi.flags = CMIM_FLAGS | CMIM_ICON;
+ clmi.hIcon = LoadSkinnedProtoIcon(proto, ID_STATUS_ONLINE);
+ CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM) hEnableMenu, (LPARAM) &clmi);
+ }
+ }
+
+ return 0;
+}
+
+
+/*
+Return TRUE is smr is enabled for this contact and its protocol (smr can be disabled per user,
+if protocol is enabled)
+If is enabled, status message is kept under CList\StatusMsg db key in user data
+
+wParam: hContact
+lParam: ignored
+*/
+int MsgRetrievalEnabledForUser(WPARAM wParam, LPARAM lParam)
+{
+ char *proto = (char*) CallService(MS_PROTO_GETCONTACTBASEPROTO, wParam, 0);
+
+ return proto != NULL && PollCheckProtocol(proto) && PollCheckContact((HANDLE) wParam);
+}
+
+
+/*
+Return TRUE is smr is enabled for this protocol
+If is enabled, status message is kept under CList\StatusMsg db key in user data
+
+wParam: protocol name
+lParam: ignored
+*/
+int MsgRetrievalEnabledForProtocol(WPARAM wParam, LPARAM lParam)
+{
+ char *proto = (char *) wParam;
+
+ return proto != NULL && PollCheckProtocol(proto);
+}
+
diff --git a/Plugins/smr/smr.dsp b/Plugins/smr/smr.dsp
new file mode 100644
index 0000000..8f1eab8
--- /dev/null
+++ b/Plugins/smr/smr.dsp
@@ -0,0 +1,186 @@
+# Microsoft Developer Studio Project File - Name="smr" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=smr - Win32 Release
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "smr.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "smr.mak" CFG="smr - Win32 Release"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "smr - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "smr - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "smr - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MD /W3 /GX /O1 /YX /FD /c
+# SUBTRACT BASE CPP /Fr
+# ADD CPP /nologo /G4 /MT /W3 /GX /O2 /Ob0 /I "../../include" /I "sdk" /D "WIN32" /D "W32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /Fr /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x417 /d "NDEBUG"
+# ADD RSC /l 0x417 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 user32.lib shell32.lib wininet.lib gdi32.lib /nologo /base:"0x67100000" /dll /machine:I386 /filealign:0x200
+# SUBTRACT BASE LINK32 /pdb:none /map
+# ADD LINK32 comctl32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /base:"0x32100000" /dll /map /machine:I386 /out:"..\..\bin\release\Plugins\smr.dll" /filealign:0x200 /ALIGN:4096 /ignore:4108
+# SUBTRACT LINK32 /profile /pdb:none
+
+!ELSEIF "$(CFG)" == "smr - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "smr___Win32_Debug"
+# PROP BASE Intermediate_Dir "smr___Win32_Debug"
+# PROP BASE Ignore_Export_Lib 0
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /G4 /MT /W3 /GX /O2 /Ob0 /I "../../include" /FR /YX /FD /c
+# ADD CPP /nologo /G4 /MTd /W3 /Gm /Gi /ZI /Od /I "../../include" /I "sdk" /D "WIN32" /D "W32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /FAcs /FR /FD /c
+# SUBTRACT CPP /YX
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x417 /d "NDEBUG"
+# ADD RSC /l 0x417 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 comctl32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 /out:"..\..bin\release\Plugins\smr.dll" /filealign:0x200 /ALIGN:4096 /ignore:4108
+# SUBTRACT BASE LINK32 /profile /pdb:none
+# ADD LINK32 comctl32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /base:"0x32100000" /dll /incremental:yes /map /debug /machine:I386 /out:"..\..\bin\debug\Plugins\smr.dll" /filealign:0x200 /ALIGN:4096 /ignore:4108
+# SUBTRACT LINK32 /profile /pdb:none
+
+!ENDIF
+
+# Begin Target
+
+# Name "smr - Win32 Release"
+# Name "smr - Win32 Debug"
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=.\commons.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\m_smr.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\utils\mir_dblists.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\utils\mir_memory.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\utils\mir_options.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\options.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\poll.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\resource.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\status.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\status_msg.h
+# End Source File
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
+# Begin Source File
+
+SOURCE=.\resource.rc
+# End Source File
+# End Group
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=..\utils\mir_dblists.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\utils\mir_memory.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\utils\mir_options.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\options.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\poll.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\smr.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\status.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\status_msg.cpp
+# End Source File
+# End Group
+# End Target
+# End Project
diff --git a/Plugins/smr/smr.dsw b/Plugins/smr/smr.dsw
new file mode 100644
index 0000000..3d9f8dd
--- /dev/null
+++ b/Plugins/smr/smr.dsw
@@ -0,0 +1,29 @@
+Microsoft Developer Studio Workspace File, Format Version 6.00
+# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
+
+###############################################################################
+
+Project: "smr"=".\smr.dsp" - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Global:
+
+Package=<5>
+{{{
+}}}
+
+Package=<3>
+{{{
+}}}
+
+###############################################################################
+
diff --git a/Plugins/smr/status.cpp b/Plugins/smr/status.cpp
new file mode 100644
index 0000000..cb0c51e
--- /dev/null
+++ b/Plugins/smr/status.cpp
@@ -0,0 +1,63 @@
+/*
+Copyright (C) 2006 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#include "status.h"
+
+HANDLE hContactSettingChanged;
+
+static int ContactSettingChanged(WPARAM wParam, LPARAM lParam);
+
+
+
+
+void InitStatus()
+{
+ hContactSettingChanged = HookEvent(ME_DB_CONTACT_SETTINGCHANGED, ContactSettingChanged);
+}
+
+
+void FreeStatus()
+{
+ UnhookEvent(hContactSettingChanged);
+}
+
+
+static int ContactSettingChanged(WPARAM wParam, LPARAM lParam) {
+ DBCONTACTWRITESETTING *cws = (DBCONTACTWRITESETTING*)lParam;
+ HANDLE hContact = (HANDLE)wParam;
+ const char *proto = cws->szModule;
+
+ // Check contact status changed
+ if (hContact != NULL && proto != NULL)
+ {
+ if (strcmp(cws->szSetting, "Status") == 0)
+ {
+ PollStatusChangeAddContact(hContact);
+ }
+ else if (strcmp(cws->szSetting, "XStatusName") == 0
+ || strcmp(cws->szSetting, "XStatusMsg") == 0
+ || strcmp(cws->szSetting, "XStatusId") == 0)
+ {
+ PollXStatusChangeAddContact(hContact);
+ }
+ }
+
+ return 0;
+}
diff --git a/Plugins/smr/status.h b/Plugins/smr/status.h
new file mode 100644
index 0000000..1a62292
--- /dev/null
+++ b/Plugins/smr/status.h
@@ -0,0 +1,42 @@
+/*
+Copyright (C) 2006 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#ifndef __STATUS_H__
+# define __STATUS_H__
+
+#include <windows.h>
+
+#include "commons.h"
+
+
+void InitStatus();
+void FreeStatus();
+
+
+
+
+
+
+
+
+
+
+
+#endif // __STATUS_H__
diff --git a/Plugins/smr/status_msg.cpp b/Plugins/smr/status_msg.cpp
new file mode 100644
index 0000000..d67c8df
--- /dev/null
+++ b/Plugins/smr/status_msg.cpp
@@ -0,0 +1,89 @@
+/*
+Copyright (C) 2006 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#include "status_msg.h"
+
+
+HANDLE hProtoAck = NULL;
+
+int ProtoAck(WPARAM wParam, LPARAM lParam);
+
+
+
+
+void InitStatusMsgs()
+{
+ hProtoAck = HookEvent(ME_PROTO_ACK, ProtoAck);
+}
+
+
+void FreeStatusMsgs()
+{
+ UnhookEvent(hProtoAck);
+}
+
+
+void ClearStatusMessage(HANDLE hContact)
+{
+ DBWriteContactSettingString(hContact, "CList", "StatusMsg", "");
+}
+
+
+void SetStatusMessage(HANDLE hContact, const TCHAR *msg)
+{
+ DBWriteContactSettingString(hContact, "CList", "StatusMsg", msg);
+}
+
+
+
+int ProtoAck(WPARAM wParam, LPARAM lParam)
+{
+ ACKDATA *ack = (ACKDATA*)lParam;
+
+ if (ack->type == ACKTYPE_STATUS)
+ {
+ WORD newStatus = (WORD)ack->lParam;
+ WORD oldStatus = (WORD)ack->hProcess;
+ char *proto = (char*)ack->szModule;
+
+ if ((oldStatus <= ID_STATUS_OFFLINE || oldStatus == ID_STATUS_INVISIBLE)
+ && newStatus > ID_STATUS_OFFLINE && newStatus != ID_STATUS_INVISIBLE
+ && PollCheckProtocol(proto))
+ {
+ if (opts.poll_check_on_status_change || opts.poll_check_on_status_change_timer)
+ PollAddAllContactsProtoOnline(ONLINE_TIMER, proto);
+ }
+ }
+ else if (ack->type == ACKTYPE_AWAYMSG)
+ {
+ char *proto = (char*)ack->szModule;
+
+ if (ack->result == ACKRESULT_SUCCESS && PollCheckProtocol(proto))
+ {
+ PollReceivedContactMessage(ack->hContact, TRUE);
+
+ // Only set if neede
+ if (ProtocolStatusCheckMsg(ack->hContact, proto) == Retrieve)
+ SetStatusMessage(ack->hContact, (const TCHAR *) ack->lParam);
+ }
+ }
+
+ return 0; //The protocol changed in a way we don't care.
+}
diff --git a/Plugins/smr/status_msg.h b/Plugins/smr/status_msg.h
new file mode 100644
index 0000000..ca30968
--- /dev/null
+++ b/Plugins/smr/status_msg.h
@@ -0,0 +1,46 @@
+/*
+Copyright (C) 2006 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#ifndef __STATUS_MSG_H__
+# define __STATUS_MSG_H__
+
+#include <windows.h>
+
+#include "commons.h"
+
+
+void InitStatusMsgs();
+void FreeStatusMsgs();
+
+
+void ClearStatusMessage(HANDLE hContact);
+void SetStatusMessage(HANDLE hContact, const TCHAR *msg);
+
+
+
+
+
+
+
+
+
+
+
+#endif // __STATUS_MSG_H__
diff --git a/Plugins/spellchecker/Docs/langpack_spellchecker.txt b/Plugins/spellchecker/Docs/langpack_spellchecker.txt
new file mode 100644
index 0000000..6bd580b
--- /dev/null
+++ b/Plugins/spellchecker/Docs/langpack_spellchecker.txt
@@ -0,0 +1,74 @@
+; Spell Checker
+; Author: Pescuma
+
+; Options
+
+[Message Sessions]
+[Spell Checker]
+[Auto-replacements]
+
+[Dictionaries]
+[Custom Dictionaries]
+[Flags DLL]
+
+[Enabled]
+[Disabled]
+
+[Spell Checker]
+[Default language:]
+[Auto-replace words with dictionary suggestions]
+[Ignore words with numbers]
+[Ignore words in UPPER CASE]
+[Download more dictionaries]
+
+[Advanced]
+[Underline type:]
+[Line]
+[Dotted]
+[Dash]
+[Dash dot]
+[Dash dot dot]
+[Wave]
+[Thick]
+[Show corrections in submenu]
+[Show all corrections in context menu (takes more time to show)]
+[Show wrong word]
+[Use flags]
+[Use input language to select dictionary]
+
+[Replacements]
+[Enable auto-replacements]
+[Language:]
+[Wrong word]
+[Correction]
+
+
+; Menu
+
+[Language]
+[Enable spell checking]
+[Always replace with]
+[Ignore all]
+[Add to dictionary]
+[Wrong word: %s]
+[Other...]
+
+
+; Add auto-replace word
+
+[Add auto-replace word]
+[Wrong word:]
+[Correction:]
+[No separators and all lowercase chars]
+[Use variables in correction]
+[The wrong word can't be empty!]
+[The correction can't be empty!]
+[The correction can't be equal to the wrong word!]
+[Wrong Correction]
+[The wrong word typed by the user]
+
+
+; Message Box
+
+[Something is wrong with the message window.\n\nThis usually means one of two things:\n- In tabSRMM the checkbox 'Enable event API' is disabled or\n- You are using SRMM (which don't support Spell Checker).\n In this case, please install tabSRMM or Scriver.]
+[There are spelling errors. Are you sure you want to send this message?]
diff --git a/Plugins/spellchecker/Docs/spellchecker.png b/Plugins/spellchecker/Docs/spellchecker.png
new file mode 100644
index 0000000..14812f5
--- /dev/null
+++ b/Plugins/spellchecker/Docs/spellchecker.png
Binary files differ
diff --git a/Plugins/spellchecker/Docs/spellchecker_changelog.txt b/Plugins/spellchecker/Docs/spellchecker_changelog.txt
new file mode 100644
index 0000000..d34330f
--- /dev/null
+++ b/Plugins/spellchecker/Docs/spellchecker_changelog.txt
@@ -0,0 +1,235 @@
+Spell Checker
+
+Changelog:
+
+. 0.2.6.0
+ + improved support for UserInfoEx Plugin
+ + added Updater support for Addons Page (pluginInfo.shortName didnt match Addons name due to separate Unicode/ANSI version)
+ You will have to enable beta Versions in Updater again if you want beta versions
+ + added error count to confirmation message
+ + added support for Miranda IM path API
+ the new default path for custom dictionaries is now %miranda_userdata%\Dictionaries
+ This change allows per-profile-dictionaries
+ IMPORTANT: You may have to move your custom dictionaries, as these are not moved automatically!!
+ IMPORTANT: You also need at least Folders plugin 0.1.5.1 or newer!!
+ * Updated some header files
+ ! Fixed a bug with translated hotkeys (Fixes issue 115)
+ - removed PLUGININFO support, since it is obsolete
+ * internal Unicode transition
+ * increased options dialog size a bit for translators
+ * Fixed lookup of Language in UserInfo module of metacontacts
+ * updated Hunspell library to version 1.3.1
+
+. 0.2.5.0
+ * Removed space in frame names in options
+ * Added notification if using SRMM (closes issue #92)
+ * Fix for autoreplacement of URL like texts (closes issue #127)
+ * Better speed (but it is not "underscore-friendly" by default anymore)
+ + Added hotkey to disable spell checker in dialog window (closes issue #132)
+ * Fix icons in Vista
+
+. 0.2.4.0
+ + Applied "patch" from miranda-avd with x64 code (but no build using it yet)
+ * Use default input language instead of keyboard layout to get language name (fixes issue #124)
+ * Ctr-Z is now working again (fixes issue #125)
+
+. 0.2.3.0
+ + Option to don't send message with error
+ * Better getting language name for non-english windows
+
+. 0.2.2.0
+ + Allow replacements that only change case
+ + Ignore words with numbers
+ * Fix for last char in a wrapped word
+ * Enable replacements is now on replacement dialog
+
+. 0.2.1.0
+ * Fixes for translation
+ * Better performance for backspace and delete
+ * Keep without redrawing when replacing text
+ * Fix for some cyrilic letters
+
+. 0.2.0.0
+ + Allow editing of auto-replacements
+ + Allow variables in auto-replacements
+ + Better handling of URLs
+ * Fix for a lot of replacements at same line
+ PS: The auto-replacement files (*.ar) are now stored as UTF-8, so you may need to fix some old replacements
+
+. 0.1.2.0
+ + Play nicer with underlines in IRC (it's not perfect)
+ * Creates custom dicts folder if don't exist
+ * Fix for tabSRMM timeout ballon
+
+. 0.1.1.0
+ + Remove underline on send to avoid sending it to the contact (Fixes issue 101)
+ + Use language set with UInfoEx
+ * Force one last check before send to replace errors (Fixes issue 67)
+
+. 0.1.0.5
+ * Fix for ICQ contacts with languages
+
+. 0.1.0.4
+ * Fix for codepage
+
+. 0.1.0.3
+ + Updated to Hunspell 1.2.8
+ * Fix for wordchar in utf8 (fixes French dict)
+
+. 0.1.0.2
+ + Updated to Hunspell 1.2.2b (closes issue 16)
+ + Use also TRY as word chars (closes issue 13)
+
+. 0.1.0.1
+ + Will use also language files avaiable for other apps (Thunderbird and Firefox by now) (thanks yb)
+ * Fix for showing popup menu in group chat
+ + Now it can patch languages that are only equal in prefix (thanks yb)
+
+. 0.1.0.0
+ + First release in FL
+
+. 0.0.4.0
+ * Updated to Hunspell 1.2.1
+ * Changes for compatibility with meSpeak
+
+. 0.0.3.2
+ * Renamed to Use input language to select dictionary
+
+. 0.0.3.1
+ + Option to use keyboard locale to select dictionary (disabled by default)
+
+. 0.0.3.0
+ + Show dict flag on *srmm status bar
+
+. 0.0.2.9
+ * Fix for big dict names
+ + Put back folders support for flags.dll
+ + Added de_frami_neu to list of known dict names
+
+. 0.0.2.8
+ * Fix for wordchars (you can revert the WORDCHARS to old values)
+ + Option to name the dict changing the db
+
+. 0.0.2.7
+ * Fix to allow editing .ar by hand
+ * Fix for crash in options
+ - Changed how icons are read - now only throught IcoLib or Icons\flags.dll
+ + Option to only replace user-defined words
+ + New dialog to add a word to always auto-replace
+ + Updated to latest version of Hunspell (side-effect: WORDCHARS now have to have all chars)
+
+. 0.0.2.6
+ * Try to fix freeze when making an suggestion
+
+. 0.0.2.5
+ + Updated Hunspell to version 1.1.5
+ * Fix to avoid sending typing notification when correcting words
+ * Fix to store custom dics as encoded strings
+
+. 0.0.2.4
+ + Added support for Miranda 0.8
+ * Fixed big bad bug in text reading from richedit
+
+. 0.0.2.3
+ * Fix for resource leak (thanks borkra)
+
+. 0.0.2.2
+ * Better handling of icons (thanks borkra)
+ * Don't crash anymore if default SRMM is installed (thanks borkra)
+
+. 0.0.2.1
+ + Better handling of URLs
+
+. 0.0.2.0
+ + Now ignores URLs and email addresses
+ + Added option to ignore words in UPPER CASE
+ + Added custom folder for flags.dll
+ * Use number as part of words too
+ * Changed text of show all corrections
+ * Fix for interation with srmm icon API
+
+. 0.0.1.9
+ * New icons for status bar (thanks Angeli-Ka)
+ * Changed links to new site
+ + Option to show wrong word
+ + Load flags from flags.dll (thanks Faith Healer)
+
+. 0.0.1.8
+ * Fix for crash
+
+. 0.0.1.7
+ * Better handling of poppup menus
+
+. 0.0.1.6
+ + Option to use flags
+ + Unknown flag
+ + Flags in other menus too
+ * Fix in typo in ShowAllCorrections setting. Sorry but the value of this setting will need to be reset
+
+. 0.0.1.5
+ * Fix for checking while selecting
+ * Fix for CTRL-Z
+ * Fix for add words to dict
+ + Added icon to status bar (needs improviment)
+ + Start adding flags (thanks to famfamfam.com for the icons)
+
+. 0.0.1.4
+ + Option to show all wrong words in menu
+ * Fix for check while selecting
+ + Added one more codepage to list of known ones
+
+. 0.0.1.3
+ * Fix for disable in context menu
+ * Fix to not change fnt settings in tabSRMM (only underline is changed - can't do anything about it)
+ * Fix to ignore char when holding CTRL
+
+. 0.0.1.2
+ * Fix in options
+ + Added hwndOwner param into SPELLCHECKER_POPUPMENU struct
+
+. 0.0.1.1
+ + Don't check if field is read-only
+ * Fix for services
+
+. 0.0.1.0
+ + Auto replace
+
+. 0.0.0.9
+ * Fix for auto-replace
+ + Parse max 3 lines when typing (to speed a little)
+
+. 0.0.0.8
+ * Fix for strange things
+
+. 0.0.0.7
+ + Unicode version (should fix encoding problems)
+ + Better way to handle typing
+ + Underline type
+ + langpack txt
+ * Fixes
+
+. 0.0.0.6
+ + Try to guess contact dict language from spoken language in contact setting
+ * fix for srmm bkg color
+
+. 0.0.0.5
+ + Per contact dictionaries
+ + Option to get dict from tabSRMM keyboard locale
+
+. 0.0.0.4
+ + More changes in srmm interface
+ + Better marking of words
+ * Changed tabSRMM to new interface and made it handle WM_CONTEXTMENU
+
+. 0.0.0.3
+ + Added generic interface for SRMMs
+ * Changed srmm patch to this interface
+ + Created scriver patch using this interface
+
+. 0.0.0.2
+ + Added support for SRMM
+ * Changes in options dialog (thanks sefo and Chudilo)
+ - Removed auto support for SRMMs (it didn't worked)
+
+. 0.0.0.1
+ + Initial version \ No newline at end of file
diff --git a/Plugins/spellchecker/Docs/spellchecker_readme.txt b/Plugins/spellchecker/Docs/spellchecker_readme.txt
new file mode 100644
index 0000000..e0ae5a0
--- /dev/null
+++ b/Plugins/spellchecker/Docs/spellchecker_readme.txt
@@ -0,0 +1,36 @@
+Spell Checker plugin
+--------------------
+
+This is a spell checker for message windows. It also allow to configure replacements to auto-correct words.
+
+It uses Hunspell to do the dirty work. Hunspell is the spell checker used by OpenOffice, so it should have a good range of dictionaries. The dictionaries: each is a couple of files with the name beeing the language and the extensions .dic and .aff. Both need to be inside the dir <Miranda Path>\Dictionaries (of a custom folder if folders plugin is installed). You can download them at: http://wiki.services.openoffice.org/wiki/Dictionaries .
+PS: This path is read only at startup, so changing it needs a restart of miranda.
+
+Note that this plugin does NOT work with SRMM, because it doesn't have a rich edit in the input field. I've done a moded version, but it is old and I'm not sure if it still works:
+Ansi: http://pescuma.org/miranda/srmm.zip
+Unicode: http://pescuma.org/miranda/srmmW.zip
+Patch: http://pescuma.org/miranda/srmm.spellchecker.patch
+
+It has an options page to set the default dictionary and some other options. It is at Message Sessions/Spell Checker .
+
+For other plugins, it works throught providing 3 serices for them to interact:
+- one to add handling of a rich edit control
+- one to remove it
+- one to append options needed to a popup menu and show it
+These plugins have to call this services in other for the magic to happen. So, things like when the popup menu is show are handled by the calling plugin.
+
+The flags: it supports showing flags to represent the dicts. This is done to have an visual way to represent the dict. To load the flags, the following steps are done:
+1. Try to load from file Icons\flags.dll - in this case an icon resource must exist with same name as language (for ex: pt_BR). If this is found, the icon is also added to IconLib to allow further customizing.
+2. Load unknown flag. It can be customized thorught IconLib
+Currently there are 2 sets of flags, one from Angeli-Ka and one from the famfamfam.com site:
+- famfamfam's icons as .ico: http://pescuma.org/miranda/flags-famfamfam.zip (note that there are a lot of files inside this zip with wrong names. It happens because I don't know which languages they represent - and if they represent a language or not. So, if you think some file name must change, please tell me)
+- famfamfam's icons as .dll: http://pescuma.org/miranda/flags-dll-famfamfam.zip
+- Angeli-Ka's icons as .ico: http://pescuma.org/miranda/flags-angelika.zip
+- Angeli-Ka's icons as .ico with language names: http://pescuma.org/miranda/flags-angelika-name.zip
+- Angeli-Ka's icons as .dll: http://pescuma.org/miranda/flags-dll-angelika.zip
+
+Many thanks to the Hunspell team and to Vladimir Vainer that made an initial version of the plugin. And thanks to the famfamfam.com site for the icons I'm using for the flags.
+
+Needs Miranda 0.8
+
+To report bugs/make suggestions, go to the forum thread: http://forums.miranda-im.org/showthread.php?t=11555 \ No newline at end of file
diff --git a/Plugins/spellchecker/Docs/spellchecker_version.txt b/Plugins/spellchecker/Docs/spellchecker_version.txt
new file mode 100644
index 0000000..bbd059d
--- /dev/null
+++ b/Plugins/spellchecker/Docs/spellchecker_version.txt
@@ -0,0 +1 @@
+Spell Checker 0.2.6.0 \ No newline at end of file
diff --git a/Plugins/spellchecker/Flags-Angelika/Unknown.ico b/Plugins/spellchecker/Flags-Angelika/Unknown.ico
new file mode 100644
index 0000000..11bfae5
--- /dev/null
+++ b/Plugins/spellchecker/Flags-Angelika/Unknown.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags-Angelika/af_ZA.ico b/Plugins/spellchecker/Flags-Angelika/af_ZA.ico
new file mode 100644
index 0000000..1031738
--- /dev/null
+++ b/Plugins/spellchecker/Flags-Angelika/af_ZA.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags-Angelika/ar_AR.ico b/Plugins/spellchecker/Flags-Angelika/ar_AR.ico
new file mode 100644
index 0000000..52e72a6
--- /dev/null
+++ b/Plugins/spellchecker/Flags-Angelika/ar_AR.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags-Angelika/az_AZ.ico b/Plugins/spellchecker/Flags-Angelika/az_AZ.ico
new file mode 100644
index 0000000..444d85e
--- /dev/null
+++ b/Plugins/spellchecker/Flags-Angelika/az_AZ.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags-Angelika/bg_BG.ico b/Plugins/spellchecker/Flags-Angelika/bg_BG.ico
new file mode 100644
index 0000000..2bac2a3
--- /dev/null
+++ b/Plugins/spellchecker/Flags-Angelika/bg_BG.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags-Angelika/bn_IN.ico b/Plugins/spellchecker/Flags-Angelika/bn_IN.ico
new file mode 100644
index 0000000..1ac90d7
--- /dev/null
+++ b/Plugins/spellchecker/Flags-Angelika/bn_IN.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags-Angelika/ca_ES.ico b/Plugins/spellchecker/Flags-Angelika/ca_ES.ico
new file mode 100644
index 0000000..dd16723
--- /dev/null
+++ b/Plugins/spellchecker/Flags-Angelika/ca_ES.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags-Angelika/cs_CZ.ico b/Plugins/spellchecker/Flags-Angelika/cs_CZ.ico
new file mode 100644
index 0000000..de6f2e3
--- /dev/null
+++ b/Plugins/spellchecker/Flags-Angelika/cs_CZ.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags-Angelika/csb_PO.ico b/Plugins/spellchecker/Flags-Angelika/csb_PO.ico
new file mode 100644
index 0000000..5727503
--- /dev/null
+++ b/Plugins/spellchecker/Flags-Angelika/csb_PO.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags-Angelika/cy_GB.ico b/Plugins/spellchecker/Flags-Angelika/cy_GB.ico
new file mode 100644
index 0000000..f0df97b
--- /dev/null
+++ b/Plugins/spellchecker/Flags-Angelika/cy_GB.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags-Angelika/da_DK.ico b/Plugins/spellchecker/Flags-Angelika/da_DK.ico
new file mode 100644
index 0000000..321eb69
--- /dev/null
+++ b/Plugins/spellchecker/Flags-Angelika/da_DK.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags-Angelika/de_AT.ico b/Plugins/spellchecker/Flags-Angelika/de_AT.ico
new file mode 100644
index 0000000..c603271
--- /dev/null
+++ b/Plugins/spellchecker/Flags-Angelika/de_AT.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags-Angelika/de_CH.ico b/Plugins/spellchecker/Flags-Angelika/de_CH.ico
new file mode 100644
index 0000000..d6cf941
--- /dev/null
+++ b/Plugins/spellchecker/Flags-Angelika/de_CH.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags-Angelika/de_DE.ico b/Plugins/spellchecker/Flags-Angelika/de_DE.ico
new file mode 100644
index 0000000..eb32452
--- /dev/null
+++ b/Plugins/spellchecker/Flags-Angelika/de_DE.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags-Angelika/el_GR.ico b/Plugins/spellchecker/Flags-Angelika/el_GR.ico
new file mode 100644
index 0000000..64e3b4b
--- /dev/null
+++ b/Plugins/spellchecker/Flags-Angelika/el_GR.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags-Angelika/em_ET.ico b/Plugins/spellchecker/Flags-Angelika/em_ET.ico
new file mode 100644
index 0000000..e836d74
--- /dev/null
+++ b/Plugins/spellchecker/Flags-Angelika/em_ET.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags-Angelika/en_AU.ico b/Plugins/spellchecker/Flags-Angelika/en_AU.ico
new file mode 100644
index 0000000..1580a68
--- /dev/null
+++ b/Plugins/spellchecker/Flags-Angelika/en_AU.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags-Angelika/en_CA.ico b/Plugins/spellchecker/Flags-Angelika/en_CA.ico
new file mode 100644
index 0000000..4e61a0b
--- /dev/null
+++ b/Plugins/spellchecker/Flags-Angelika/en_CA.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags-Angelika/en_GB.ico b/Plugins/spellchecker/Flags-Angelika/en_GB.ico
new file mode 100644
index 0000000..0a34613
--- /dev/null
+++ b/Plugins/spellchecker/Flags-Angelika/en_GB.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags-Angelika/en_NZ.ico b/Plugins/spellchecker/Flags-Angelika/en_NZ.ico
new file mode 100644
index 0000000..10ed20f
--- /dev/null
+++ b/Plugins/spellchecker/Flags-Angelika/en_NZ.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags-Angelika/en_US.ico b/Plugins/spellchecker/Flags-Angelika/en_US.ico
new file mode 100644
index 0000000..4274317
--- /dev/null
+++ b/Plugins/spellchecker/Flags-Angelika/en_US.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags-Angelika/en_ZA.ico b/Plugins/spellchecker/Flags-Angelika/en_ZA.ico
new file mode 100644
index 0000000..1031738
--- /dev/null
+++ b/Plugins/spellchecker/Flags-Angelika/en_ZA.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags-Angelika/es_ES.ico b/Plugins/spellchecker/Flags-Angelika/es_ES.ico
new file mode 100644
index 0000000..3e1d263
--- /dev/null
+++ b/Plugins/spellchecker/Flags-Angelika/es_ES.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags-Angelika/es_MX.ico b/Plugins/spellchecker/Flags-Angelika/es_MX.ico
new file mode 100644
index 0000000..f13b9a1
--- /dev/null
+++ b/Plugins/spellchecker/Flags-Angelika/es_MX.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags-Angelika/et_EE.ico b/Plugins/spellchecker/Flags-Angelika/et_EE.ico
new file mode 100644
index 0000000..bb844d3
--- /dev/null
+++ b/Plugins/spellchecker/Flags-Angelika/et_EE.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags-Angelika/fa_IR.ico b/Plugins/spellchecker/Flags-Angelika/fa_IR.ico
new file mode 100644
index 0000000..22ad9ff
--- /dev/null
+++ b/Plugins/spellchecker/Flags-Angelika/fa_IR.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags-Angelika/fi_FI.ico b/Plugins/spellchecker/Flags-Angelika/fi_FI.ico
new file mode 100644
index 0000000..c3a3299
--- /dev/null
+++ b/Plugins/spellchecker/Flags-Angelika/fi_FI.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags-Angelika/fj_FJ.ico b/Plugins/spellchecker/Flags-Angelika/fj_FJ.ico
new file mode 100644
index 0000000..93ccf58
--- /dev/null
+++ b/Plugins/spellchecker/Flags-Angelika/fj_FJ.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags-Angelika/fo_FO.ico b/Plugins/spellchecker/Flags-Angelika/fo_FO.ico
new file mode 100644
index 0000000..a5e0e76
--- /dev/null
+++ b/Plugins/spellchecker/Flags-Angelika/fo_FO.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags-Angelika/fr_BE.ico b/Plugins/spellchecker/Flags-Angelika/fr_BE.ico
new file mode 100644
index 0000000..62d4fb6
--- /dev/null
+++ b/Plugins/spellchecker/Flags-Angelika/fr_BE.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags-Angelika/fr_FR.ico b/Plugins/spellchecker/Flags-Angelika/fr_FR.ico
new file mode 100644
index 0000000..4010cf5
--- /dev/null
+++ b/Plugins/spellchecker/Flags-Angelika/fr_FR.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags-Angelika/ga_IE.ico b/Plugins/spellchecker/Flags-Angelika/ga_IE.ico
new file mode 100644
index 0000000..18eac04
--- /dev/null
+++ b/Plugins/spellchecker/Flags-Angelika/ga_IE.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags-Angelika/gd_GB.ico b/Plugins/spellchecker/Flags-Angelika/gd_GB.ico
new file mode 100644
index 0000000..9c1953a
--- /dev/null
+++ b/Plugins/spellchecker/Flags-Angelika/gd_GB.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags-Angelika/gl_ES.ico b/Plugins/spellchecker/Flags-Angelika/gl_ES.ico
new file mode 100644
index 0000000..d40ef55
--- /dev/null
+++ b/Plugins/spellchecker/Flags-Angelika/gl_ES.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags-Angelika/he_IL.ico b/Plugins/spellchecker/Flags-Angelika/he_IL.ico
new file mode 100644
index 0000000..5153d87
--- /dev/null
+++ b/Plugins/spellchecker/Flags-Angelika/he_IL.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags-Angelika/hi_IN.ico b/Plugins/spellchecker/Flags-Angelika/hi_IN.ico
new file mode 100644
index 0000000..44d2239
--- /dev/null
+++ b/Plugins/spellchecker/Flags-Angelika/hi_IN.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags-Angelika/hr_Hr.ico b/Plugins/spellchecker/Flags-Angelika/hr_Hr.ico
new file mode 100644
index 0000000..ce46932
--- /dev/null
+++ b/Plugins/spellchecker/Flags-Angelika/hr_Hr.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags-Angelika/hu_HU.ico b/Plugins/spellchecker/Flags-Angelika/hu_HU.ico
new file mode 100644
index 0000000..ce95ae6
--- /dev/null
+++ b/Plugins/spellchecker/Flags-Angelika/hu_HU.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags-Angelika/id_ID.ico b/Plugins/spellchecker/Flags-Angelika/id_ID.ico
new file mode 100644
index 0000000..f454edc
--- /dev/null
+++ b/Plugins/spellchecker/Flags-Angelika/id_ID.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags-Angelika/is_IS.ico b/Plugins/spellchecker/Flags-Angelika/is_IS.ico
new file mode 100644
index 0000000..6c4110e
--- /dev/null
+++ b/Plugins/spellchecker/Flags-Angelika/is_IS.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags-Angelika/it_IT.ico b/Plugins/spellchecker/Flags-Angelika/it_IT.ico
new file mode 100644
index 0000000..850ce77
--- /dev/null
+++ b/Plugins/spellchecker/Flags-Angelika/it_IT.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags-Angelika/km_KH.ico b/Plugins/spellchecker/Flags-Angelika/km_KH.ico
new file mode 100644
index 0000000..ae92b1a
--- /dev/null
+++ b/Plugins/spellchecker/Flags-Angelika/km_KH.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags-Angelika/ku_TR.ico b/Plugins/spellchecker/Flags-Angelika/ku_TR.ico
new file mode 100644
index 0000000..d6d9ab4
--- /dev/null
+++ b/Plugins/spellchecker/Flags-Angelika/ku_TR.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags-Angelika/lt_LT.ico b/Plugins/spellchecker/Flags-Angelika/lt_LT.ico
new file mode 100644
index 0000000..4d4a6c6
--- /dev/null
+++ b/Plugins/spellchecker/Flags-Angelika/lt_LT.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags-Angelika/lu_LU.ico b/Plugins/spellchecker/Flags-Angelika/lu_LU.ico
new file mode 100644
index 0000000..c701560
--- /dev/null
+++ b/Plugins/spellchecker/Flags-Angelika/lu_LU.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags-Angelika/lv_LV.ico b/Plugins/spellchecker/Flags-Angelika/lv_LV.ico
new file mode 100644
index 0000000..ca51d2e
--- /dev/null
+++ b/Plugins/spellchecker/Flags-Angelika/lv_LV.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags-Angelika/mg_MG.ico b/Plugins/spellchecker/Flags-Angelika/mg_MG.ico
new file mode 100644
index 0000000..2b334e6
--- /dev/null
+++ b/Plugins/spellchecker/Flags-Angelika/mg_MG.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags-Angelika/mi_NZ.ico b/Plugins/spellchecker/Flags-Angelika/mi_NZ.ico
new file mode 100644
index 0000000..10ed20f
--- /dev/null
+++ b/Plugins/spellchecker/Flags-Angelika/mi_NZ.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags-Angelika/mn_MN.ico b/Plugins/spellchecker/Flags-Angelika/mn_MN.ico
new file mode 100644
index 0000000..6479314
--- /dev/null
+++ b/Plugins/spellchecker/Flags-Angelika/mn_MN.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags-Angelika/mo_BF.ico b/Plugins/spellchecker/Flags-Angelika/mo_BF.ico
new file mode 100644
index 0000000..49ce981
--- /dev/null
+++ b/Plugins/spellchecker/Flags-Angelika/mo_BF.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags-Angelika/mr_IN.ico b/Plugins/spellchecker/Flags-Angelika/mr_IN.ico
new file mode 100644
index 0000000..44d2239
--- /dev/null
+++ b/Plugins/spellchecker/Flags-Angelika/mr_IN.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags-Angelika/ms_MY.ico b/Plugins/spellchecker/Flags-Angelika/ms_MY.ico
new file mode 100644
index 0000000..a0a4be2
--- /dev/null
+++ b/Plugins/spellchecker/Flags-Angelika/ms_MY.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags-Angelika/nb_NO.ico b/Plugins/spellchecker/Flags-Angelika/nb_NO.ico
new file mode 100644
index 0000000..66ca4ea
--- /dev/null
+++ b/Plugins/spellchecker/Flags-Angelika/nb_NO.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags-Angelika/ne_NP.ico b/Plugins/spellchecker/Flags-Angelika/ne_NP.ico
new file mode 100644
index 0000000..2180137
--- /dev/null
+++ b/Plugins/spellchecker/Flags-Angelika/ne_NP.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags-Angelika/nl_NL.ico b/Plugins/spellchecker/Flags-Angelika/nl_NL.ico
new file mode 100644
index 0000000..f3280bb
--- /dev/null
+++ b/Plugins/spellchecker/Flags-Angelika/nl_NL.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags-Angelika/nn_NO.ico b/Plugins/spellchecker/Flags-Angelika/nn_NO.ico
new file mode 100644
index 0000000..66ca4ea
--- /dev/null
+++ b/Plugins/spellchecker/Flags-Angelika/nn_NO.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags-Angelika/nr_ZA.ico b/Plugins/spellchecker/Flags-Angelika/nr_ZA.ico
new file mode 100644
index 0000000..1031738
--- /dev/null
+++ b/Plugins/spellchecker/Flags-Angelika/nr_ZA.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags-Angelika/ns_ZA.ico b/Plugins/spellchecker/Flags-Angelika/ns_ZA.ico
new file mode 100644
index 0000000..1031738
--- /dev/null
+++ b/Plugins/spellchecker/Flags-Angelika/ns_ZA.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags-Angelika/or_IN.ico b/Plugins/spellchecker/Flags-Angelika/or_IN.ico
new file mode 100644
index 0000000..44d2239
--- /dev/null
+++ b/Plugins/spellchecker/Flags-Angelika/or_IN.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags-Angelika/pl_PL.ico b/Plugins/spellchecker/Flags-Angelika/pl_PL.ico
new file mode 100644
index 0000000..d85aa5b
--- /dev/null
+++ b/Plugins/spellchecker/Flags-Angelika/pl_PL.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags-Angelika/pt_BR.ico b/Plugins/spellchecker/Flags-Angelika/pt_BR.ico
new file mode 100644
index 0000000..c7d9338
--- /dev/null
+++ b/Plugins/spellchecker/Flags-Angelika/pt_BR.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags-Angelika/pt_PT.ico b/Plugins/spellchecker/Flags-Angelika/pt_PT.ico
new file mode 100644
index 0000000..af073da
--- /dev/null
+++ b/Plugins/spellchecker/Flags-Angelika/pt_PT.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags-Angelika/qu_BO.ico b/Plugins/spellchecker/Flags-Angelika/qu_BO.ico
new file mode 100644
index 0000000..663586e
--- /dev/null
+++ b/Plugins/spellchecker/Flags-Angelika/qu_BO.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags-Angelika/ro_RO.ico b/Plugins/spellchecker/Flags-Angelika/ro_RO.ico
new file mode 100644
index 0000000..9623b07
--- /dev/null
+++ b/Plugins/spellchecker/Flags-Angelika/ro_RO.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags-Angelika/ru_RU.ico b/Plugins/spellchecker/Flags-Angelika/ru_RU.ico
new file mode 100644
index 0000000..60d08c1
--- /dev/null
+++ b/Plugins/spellchecker/Flags-Angelika/ru_RU.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags-Angelika/rw_RW.ico b/Plugins/spellchecker/Flags-Angelika/rw_RW.ico
new file mode 100644
index 0000000..a424d1c
--- /dev/null
+++ b/Plugins/spellchecker/Flags-Angelika/rw_RW.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags-Angelika/sk_SK.ico b/Plugins/spellchecker/Flags-Angelika/sk_SK.ico
new file mode 100644
index 0000000..05e16ef
--- /dev/null
+++ b/Plugins/spellchecker/Flags-Angelika/sk_SK.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags-Angelika/sl_SI.ico b/Plugins/spellchecker/Flags-Angelika/sl_SI.ico
new file mode 100644
index 0000000..c23a66e
--- /dev/null
+++ b/Plugins/spellchecker/Flags-Angelika/sl_SI.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags-Angelika/ss_ZA.ico b/Plugins/spellchecker/Flags-Angelika/ss_ZA.ico
new file mode 100644
index 0000000..1031738
--- /dev/null
+++ b/Plugins/spellchecker/Flags-Angelika/ss_ZA.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags-Angelika/st_ZA.ico b/Plugins/spellchecker/Flags-Angelika/st_ZA.ico
new file mode 100644
index 0000000..1031738
--- /dev/null
+++ b/Plugins/spellchecker/Flags-Angelika/st_ZA.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags-Angelika/sv_SE.ico b/Plugins/spellchecker/Flags-Angelika/sv_SE.ico
new file mode 100644
index 0000000..c6e1964
--- /dev/null
+++ b/Plugins/spellchecker/Flags-Angelika/sv_SE.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags-Angelika/ta_IN.ico b/Plugins/spellchecker/Flags-Angelika/ta_IN.ico
new file mode 100644
index 0000000..44d2239
--- /dev/null
+++ b/Plugins/spellchecker/Flags-Angelika/ta_IN.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags-Angelika/tet_ID.ico b/Plugins/spellchecker/Flags-Angelika/tet_ID.ico
new file mode 100644
index 0000000..f454edc
--- /dev/null
+++ b/Plugins/spellchecker/Flags-Angelika/tet_ID.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags-Angelika/tl_PH.ico b/Plugins/spellchecker/Flags-Angelika/tl_PH.ico
new file mode 100644
index 0000000..30772bf
--- /dev/null
+++ b/Plugins/spellchecker/Flags-Angelika/tl_PH.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags-Angelika/tn_ZA.ico b/Plugins/spellchecker/Flags-Angelika/tn_ZA.ico
new file mode 100644
index 0000000..1031738
--- /dev/null
+++ b/Plugins/spellchecker/Flags-Angelika/tn_ZA.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags-Angelika/ts_ZA.ico b/Plugins/spellchecker/Flags-Angelika/ts_ZA.ico
new file mode 100644
index 0000000..1031738
--- /dev/null
+++ b/Plugins/spellchecker/Flags-Angelika/ts_ZA.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags-Angelika/uk_UA.ico b/Plugins/spellchecker/Flags-Angelika/uk_UA.ico
new file mode 100644
index 0000000..7656810
--- /dev/null
+++ b/Plugins/spellchecker/Flags-Angelika/uk_UA.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags-Angelika/uz_UZ.ico b/Plugins/spellchecker/Flags-Angelika/uz_UZ.ico
new file mode 100644
index 0000000..573c4ae
--- /dev/null
+++ b/Plugins/spellchecker/Flags-Angelika/uz_UZ.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags-Angelika/ve_ZA.ico b/Plugins/spellchecker/Flags-Angelika/ve_ZA.ico
new file mode 100644
index 0000000..1031738
--- /dev/null
+++ b/Plugins/spellchecker/Flags-Angelika/ve_ZA.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags-Angelika/vi_VI.ico b/Plugins/spellchecker/Flags-Angelika/vi_VI.ico
new file mode 100644
index 0000000..999d062
--- /dev/null
+++ b/Plugins/spellchecker/Flags-Angelika/vi_VI.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags-Angelika/xh_ZA.ico b/Plugins/spellchecker/Flags-Angelika/xh_ZA.ico
new file mode 100644
index 0000000..1031738
--- /dev/null
+++ b/Plugins/spellchecker/Flags-Angelika/xh_ZA.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags-Angelika/zu_ZA.ico b/Plugins/spellchecker/Flags-Angelika/zu_ZA.ico
new file mode 100644
index 0000000..1031738
--- /dev/null
+++ b/Plugins/spellchecker/Flags-Angelika/zu_ZA.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/ad.ico b/Plugins/spellchecker/Flags/ad.ico
new file mode 100644
index 0000000..041939a
--- /dev/null
+++ b/Plugins/spellchecker/Flags/ad.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/ae.ico b/Plugins/spellchecker/Flags/ae.ico
new file mode 100644
index 0000000..dbf70ee
--- /dev/null
+++ b/Plugins/spellchecker/Flags/ae.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/af_ZA.ico b/Plugins/spellchecker/Flags/af_ZA.ico
new file mode 100644
index 0000000..80019f3
--- /dev/null
+++ b/Plugins/spellchecker/Flags/af_ZA.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/ag.ico b/Plugins/spellchecker/Flags/ag.ico
new file mode 100644
index 0000000..aa362f8
--- /dev/null
+++ b/Plugins/spellchecker/Flags/ag.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/ai.ico b/Plugins/spellchecker/Flags/ai.ico
new file mode 100644
index 0000000..a066e8d
--- /dev/null
+++ b/Plugins/spellchecker/Flags/ai.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/al.ico b/Plugins/spellchecker/Flags/al.ico
new file mode 100644
index 0000000..33c2aee
--- /dev/null
+++ b/Plugins/spellchecker/Flags/al.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/am.ico b/Plugins/spellchecker/Flags/am.ico
new file mode 100644
index 0000000..c9caba1
--- /dev/null
+++ b/Plugins/spellchecker/Flags/am.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/an.ico b/Plugins/spellchecker/Flags/an.ico
new file mode 100644
index 0000000..865d4bb
--- /dev/null
+++ b/Plugins/spellchecker/Flags/an.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/ao.ico b/Plugins/spellchecker/Flags/ao.ico
new file mode 100644
index 0000000..12f6fc5
--- /dev/null
+++ b/Plugins/spellchecker/Flags/ao.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/ar.ico b/Plugins/spellchecker/Flags/ar.ico
new file mode 100644
index 0000000..c62bddf
--- /dev/null
+++ b/Plugins/spellchecker/Flags/ar.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/as.ico b/Plugins/spellchecker/Flags/as.ico
new file mode 100644
index 0000000..e721fca
--- /dev/null
+++ b/Plugins/spellchecker/Flags/as.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/aw.ico b/Plugins/spellchecker/Flags/aw.ico
new file mode 100644
index 0000000..6ffae09
--- /dev/null
+++ b/Plugins/spellchecker/Flags/aw.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/ax.ico b/Plugins/spellchecker/Flags/ax.ico
new file mode 100644
index 0000000..201d72d
--- /dev/null
+++ b/Plugins/spellchecker/Flags/ax.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/az_AZ.ico b/Plugins/spellchecker/Flags/az_AZ.ico
new file mode 100644
index 0000000..5a89d0a
--- /dev/null
+++ b/Plugins/spellchecker/Flags/az_AZ.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/ba.ico b/Plugins/spellchecker/Flags/ba.ico
new file mode 100644
index 0000000..c8e1c91
--- /dev/null
+++ b/Plugins/spellchecker/Flags/ba.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/bb.ico b/Plugins/spellchecker/Flags/bb.ico
new file mode 100644
index 0000000..e726c72
--- /dev/null
+++ b/Plugins/spellchecker/Flags/bb.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/bg_BG.ico b/Plugins/spellchecker/Flags/bg_BG.ico
new file mode 100644
index 0000000..64b732f
--- /dev/null
+++ b/Plugins/spellchecker/Flags/bg_BG.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/bh.ico b/Plugins/spellchecker/Flags/bh.ico
new file mode 100644
index 0000000..c9e20c8
--- /dev/null
+++ b/Plugins/spellchecker/Flags/bh.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/bi.ico b/Plugins/spellchecker/Flags/bi.ico
new file mode 100644
index 0000000..a383e61
--- /dev/null
+++ b/Plugins/spellchecker/Flags/bi.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/bj.ico b/Plugins/spellchecker/Flags/bj.ico
new file mode 100644
index 0000000..25392db
--- /dev/null
+++ b/Plugins/spellchecker/Flags/bj.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/bm.ico b/Plugins/spellchecker/Flags/bm.ico
new file mode 100644
index 0000000..5eb9f7d
--- /dev/null
+++ b/Plugins/spellchecker/Flags/bm.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/bn.ico b/Plugins/spellchecker/Flags/bn.ico
new file mode 100644
index 0000000..42117c3
--- /dev/null
+++ b/Plugins/spellchecker/Flags/bn.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/bn_IN.ico b/Plugins/spellchecker/Flags/bn_IN.ico
new file mode 100644
index 0000000..ed15c62
--- /dev/null
+++ b/Plugins/spellchecker/Flags/bn_IN.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/bs.ico b/Plugins/spellchecker/Flags/bs.ico
new file mode 100644
index 0000000..e26f72a
--- /dev/null
+++ b/Plugins/spellchecker/Flags/bs.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/bt.ico b/Plugins/spellchecker/Flags/bt.ico
new file mode 100644
index 0000000..a319583
--- /dev/null
+++ b/Plugins/spellchecker/Flags/bt.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/bv.ico b/Plugins/spellchecker/Flags/bv.ico
new file mode 100644
index 0000000..f0b648d
--- /dev/null
+++ b/Plugins/spellchecker/Flags/bv.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/by.ico b/Plugins/spellchecker/Flags/by.ico
new file mode 100644
index 0000000..5c90057
--- /dev/null
+++ b/Plugins/spellchecker/Flags/by.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/bz.ico b/Plugins/spellchecker/Flags/bz.ico
new file mode 100644
index 0000000..08a2217
--- /dev/null
+++ b/Plugins/spellchecker/Flags/bz.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/cc.ico b/Plugins/spellchecker/Flags/cc.ico
new file mode 100644
index 0000000..f03d802
--- /dev/null
+++ b/Plugins/spellchecker/Flags/cc.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/cd.ico b/Plugins/spellchecker/Flags/cd.ico
new file mode 100644
index 0000000..440f4eb
--- /dev/null
+++ b/Plugins/spellchecker/Flags/cd.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/cf.ico b/Plugins/spellchecker/Flags/cf.ico
new file mode 100644
index 0000000..4b7d5f7
--- /dev/null
+++ b/Plugins/spellchecker/Flags/cf.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/cg.ico b/Plugins/spellchecker/Flags/cg.ico
new file mode 100644
index 0000000..66f15bb
--- /dev/null
+++ b/Plugins/spellchecker/Flags/cg.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/ci.ico b/Plugins/spellchecker/Flags/ci.ico
new file mode 100644
index 0000000..76fde54
--- /dev/null
+++ b/Plugins/spellchecker/Flags/ci.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/ck.ico b/Plugins/spellchecker/Flags/ck.ico
new file mode 100644
index 0000000..6585124
--- /dev/null
+++ b/Plugins/spellchecker/Flags/ck.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/cl.ico b/Plugins/spellchecker/Flags/cl.ico
new file mode 100644
index 0000000..fb695e4
--- /dev/null
+++ b/Plugins/spellchecker/Flags/cl.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/cm.ico b/Plugins/spellchecker/Flags/cm.ico
new file mode 100644
index 0000000..cb79701
--- /dev/null
+++ b/Plugins/spellchecker/Flags/cm.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/cn.ico b/Plugins/spellchecker/Flags/cn.ico
new file mode 100644
index 0000000..0ceb086
--- /dev/null
+++ b/Plugins/spellchecker/Flags/cn.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/co.ico b/Plugins/spellchecker/Flags/co.ico
new file mode 100644
index 0000000..1cc69ea
--- /dev/null
+++ b/Plugins/spellchecker/Flags/co.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/cr.ico b/Plugins/spellchecker/Flags/cr.ico
new file mode 100644
index 0000000..193b961
--- /dev/null
+++ b/Plugins/spellchecker/Flags/cr.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/cs_CZ.ico b/Plugins/spellchecker/Flags/cs_CZ.ico
new file mode 100644
index 0000000..f0c14bc
--- /dev/null
+++ b/Plugins/spellchecker/Flags/cs_CZ.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/cu.ico b/Plugins/spellchecker/Flags/cu.ico
new file mode 100644
index 0000000..7f1ad5b
--- /dev/null
+++ b/Plugins/spellchecker/Flags/cu.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/cv.ico b/Plugins/spellchecker/Flags/cv.ico
new file mode 100644
index 0000000..349d4e9
--- /dev/null
+++ b/Plugins/spellchecker/Flags/cv.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/cx.ico b/Plugins/spellchecker/Flags/cx.ico
new file mode 100644
index 0000000..363cef2
--- /dev/null
+++ b/Plugins/spellchecker/Flags/cx.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/cy.ico b/Plugins/spellchecker/Flags/cy.ico
new file mode 100644
index 0000000..546f1a7
--- /dev/null
+++ b/Plugins/spellchecker/Flags/cy.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/cy_GB.ico b/Plugins/spellchecker/Flags/cy_GB.ico
new file mode 100644
index 0000000..021c4cb
--- /dev/null
+++ b/Plugins/spellchecker/Flags/cy_GB.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/da_DK.ico b/Plugins/spellchecker/Flags/da_DK.ico
new file mode 100644
index 0000000..eebf7f9
--- /dev/null
+++ b/Plugins/spellchecker/Flags/da_DK.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/de_AT.ico b/Plugins/spellchecker/Flags/de_AT.ico
new file mode 100644
index 0000000..aa0ebc2
--- /dev/null
+++ b/Plugins/spellchecker/Flags/de_AT.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/de_CH.ico b/Plugins/spellchecker/Flags/de_CH.ico
new file mode 100644
index 0000000..c272204
--- /dev/null
+++ b/Plugins/spellchecker/Flags/de_CH.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/de_DE.ico b/Plugins/spellchecker/Flags/de_DE.ico
new file mode 100644
index 0000000..ed1ce66
--- /dev/null
+++ b/Plugins/spellchecker/Flags/de_DE.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/dj.ico b/Plugins/spellchecker/Flags/dj.ico
new file mode 100644
index 0000000..146e62a
--- /dev/null
+++ b/Plugins/spellchecker/Flags/dj.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/dm.ico b/Plugins/spellchecker/Flags/dm.ico
new file mode 100644
index 0000000..585ad1d
--- /dev/null
+++ b/Plugins/spellchecker/Flags/dm.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/do.ico b/Plugins/spellchecker/Flags/do.ico
new file mode 100644
index 0000000..0602e9f
--- /dev/null
+++ b/Plugins/spellchecker/Flags/do.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/dz.ico b/Plugins/spellchecker/Flags/dz.ico
new file mode 100644
index 0000000..23fe39b
--- /dev/null
+++ b/Plugins/spellchecker/Flags/dz.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/ec.ico b/Plugins/spellchecker/Flags/ec.ico
new file mode 100644
index 0000000..93689ca
--- /dev/null
+++ b/Plugins/spellchecker/Flags/ec.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/eg.ico b/Plugins/spellchecker/Flags/eg.ico
new file mode 100644
index 0000000..ce231de
--- /dev/null
+++ b/Plugins/spellchecker/Flags/eg.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/eh.ico b/Plugins/spellchecker/Flags/eh.ico
new file mode 100644
index 0000000..1ac030f
--- /dev/null
+++ b/Plugins/spellchecker/Flags/eh.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/el_GR.ico b/Plugins/spellchecker/Flags/el_GR.ico
new file mode 100644
index 0000000..b7d70e7
--- /dev/null
+++ b/Plugins/spellchecker/Flags/el_GR.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/em_ET.ico b/Plugins/spellchecker/Flags/em_ET.ico
new file mode 100644
index 0000000..aef5354
--- /dev/null
+++ b/Plugins/spellchecker/Flags/em_ET.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/en_AU.ico b/Plugins/spellchecker/Flags/en_AU.ico
new file mode 100644
index 0000000..4c04292
--- /dev/null
+++ b/Plugins/spellchecker/Flags/en_AU.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/en_CA.ico b/Plugins/spellchecker/Flags/en_CA.ico
new file mode 100644
index 0000000..ab30abb
--- /dev/null
+++ b/Plugins/spellchecker/Flags/en_CA.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/en_GB.ico b/Plugins/spellchecker/Flags/en_GB.ico
new file mode 100644
index 0000000..14fcae2
--- /dev/null
+++ b/Plugins/spellchecker/Flags/en_GB.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/en_NZ.ico b/Plugins/spellchecker/Flags/en_NZ.ico
new file mode 100644
index 0000000..9c4cac8
--- /dev/null
+++ b/Plugins/spellchecker/Flags/en_NZ.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/en_US.ico b/Plugins/spellchecker/Flags/en_US.ico
new file mode 100644
index 0000000..793e926
--- /dev/null
+++ b/Plugins/spellchecker/Flags/en_US.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/en_ZA.ico b/Plugins/spellchecker/Flags/en_ZA.ico
new file mode 100644
index 0000000..a786459
--- /dev/null
+++ b/Plugins/spellchecker/Flags/en_ZA.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/england.ico b/Plugins/spellchecker/Flags/england.ico
new file mode 100644
index 0000000..94cd4a7
--- /dev/null
+++ b/Plugins/spellchecker/Flags/england.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/er.ico b/Plugins/spellchecker/Flags/er.ico
new file mode 100644
index 0000000..baff122
--- /dev/null
+++ b/Plugins/spellchecker/Flags/er.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/es_ES.ico b/Plugins/spellchecker/Flags/es_ES.ico
new file mode 100644
index 0000000..007ca25
--- /dev/null
+++ b/Plugins/spellchecker/Flags/es_ES.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/es_MX.ico b/Plugins/spellchecker/Flags/es_MX.ico
new file mode 100644
index 0000000..086ed39
--- /dev/null
+++ b/Plugins/spellchecker/Flags/es_MX.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/et_EE.ico b/Plugins/spellchecker/Flags/et_EE.ico
new file mode 100644
index 0000000..5e2a142
--- /dev/null
+++ b/Plugins/spellchecker/Flags/et_EE.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/fa_IR.ico b/Plugins/spellchecker/Flags/fa_IR.ico
new file mode 100644
index 0000000..35d952a
--- /dev/null
+++ b/Plugins/spellchecker/Flags/fa_IR.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/fi_FI.ico b/Plugins/spellchecker/Flags/fi_FI.ico
new file mode 100644
index 0000000..f333f5a
--- /dev/null
+++ b/Plugins/spellchecker/Flags/fi_FI.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/fj_FJ.ico b/Plugins/spellchecker/Flags/fj_FJ.ico
new file mode 100644
index 0000000..8f1b309
--- /dev/null
+++ b/Plugins/spellchecker/Flags/fj_FJ.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/fk.ico b/Plugins/spellchecker/Flags/fk.ico
new file mode 100644
index 0000000..bbb3689
--- /dev/null
+++ b/Plugins/spellchecker/Flags/fk.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/fm.ico b/Plugins/spellchecker/Flags/fm.ico
new file mode 100644
index 0000000..d9f5cd1
--- /dev/null
+++ b/Plugins/spellchecker/Flags/fm.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/fo_FO.ico b/Plugins/spellchecker/Flags/fo_FO.ico
new file mode 100644
index 0000000..dc4835e
--- /dev/null
+++ b/Plugins/spellchecker/Flags/fo_FO.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/fr_BE.ico b/Plugins/spellchecker/Flags/fr_BE.ico
new file mode 100644
index 0000000..e5d92e1
--- /dev/null
+++ b/Plugins/spellchecker/Flags/fr_BE.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/fr_FR.ico b/Plugins/spellchecker/Flags/fr_FR.ico
new file mode 100644
index 0000000..503ab00
--- /dev/null
+++ b/Plugins/spellchecker/Flags/fr_FR.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/ga.ico b/Plugins/spellchecker/Flags/ga.ico
new file mode 100644
index 0000000..46ba42d
--- /dev/null
+++ b/Plugins/spellchecker/Flags/ga.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/ga_IE.ico b/Plugins/spellchecker/Flags/ga_IE.ico
new file mode 100644
index 0000000..6cd8a7d
--- /dev/null
+++ b/Plugins/spellchecker/Flags/ga_IE.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/gd.ico b/Plugins/spellchecker/Flags/gd.ico
new file mode 100644
index 0000000..72bd001
--- /dev/null
+++ b/Plugins/spellchecker/Flags/gd.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/gd_GB.ico b/Plugins/spellchecker/Flags/gd_GB.ico
new file mode 100644
index 0000000..f29d98c
--- /dev/null
+++ b/Plugins/spellchecker/Flags/gd_GB.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/ge.ico b/Plugins/spellchecker/Flags/ge.ico
new file mode 100644
index 0000000..7d6897c
--- /dev/null
+++ b/Plugins/spellchecker/Flags/ge.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/gi.ico b/Plugins/spellchecker/Flags/gi.ico
new file mode 100644
index 0000000..b4455ec
--- /dev/null
+++ b/Plugins/spellchecker/Flags/gi.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/gl.ico b/Plugins/spellchecker/Flags/gl.ico
new file mode 100644
index 0000000..e60f0d6
--- /dev/null
+++ b/Plugins/spellchecker/Flags/gl.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/gm.ico b/Plugins/spellchecker/Flags/gm.ico
new file mode 100644
index 0000000..355587c
--- /dev/null
+++ b/Plugins/spellchecker/Flags/gm.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/gn.ico b/Plugins/spellchecker/Flags/gn.ico
new file mode 100644
index 0000000..2af4a63
--- /dev/null
+++ b/Plugins/spellchecker/Flags/gn.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/gp.ico b/Plugins/spellchecker/Flags/gp.ico
new file mode 100644
index 0000000..25ed546
--- /dev/null
+++ b/Plugins/spellchecker/Flags/gp.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/gq.ico b/Plugins/spellchecker/Flags/gq.ico
new file mode 100644
index 0000000..1ee063a
--- /dev/null
+++ b/Plugins/spellchecker/Flags/gq.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/gs.ico b/Plugins/spellchecker/Flags/gs.ico
new file mode 100644
index 0000000..15ec1b2
--- /dev/null
+++ b/Plugins/spellchecker/Flags/gs.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/gt.ico b/Plugins/spellchecker/Flags/gt.ico
new file mode 100644
index 0000000..64530c8
--- /dev/null
+++ b/Plugins/spellchecker/Flags/gt.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/gu.ico b/Plugins/spellchecker/Flags/gu.ico
new file mode 100644
index 0000000..2b235cf
--- /dev/null
+++ b/Plugins/spellchecker/Flags/gu.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/gw.ico b/Plugins/spellchecker/Flags/gw.ico
new file mode 100644
index 0000000..ffb0605
--- /dev/null
+++ b/Plugins/spellchecker/Flags/gw.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/gy.ico b/Plugins/spellchecker/Flags/gy.ico
new file mode 100644
index 0000000..a20415d
--- /dev/null
+++ b/Plugins/spellchecker/Flags/gy.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/he_IL.ico b/Plugins/spellchecker/Flags/he_IL.ico
new file mode 100644
index 0000000..fdd3ea1
--- /dev/null
+++ b/Plugins/spellchecker/Flags/he_IL.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/hi_IN.ico b/Plugins/spellchecker/Flags/hi_IN.ico
new file mode 100644
index 0000000..b1a927d
--- /dev/null
+++ b/Plugins/spellchecker/Flags/hi_IN.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/hk.ico b/Plugins/spellchecker/Flags/hk.ico
new file mode 100644
index 0000000..ad16f06
--- /dev/null
+++ b/Plugins/spellchecker/Flags/hk.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/hn.ico b/Plugins/spellchecker/Flags/hn.ico
new file mode 100644
index 0000000..12e2ab0
--- /dev/null
+++ b/Plugins/spellchecker/Flags/hn.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/hr_HR.ico b/Plugins/spellchecker/Flags/hr_HR.ico
new file mode 100644
index 0000000..c6efff9
--- /dev/null
+++ b/Plugins/spellchecker/Flags/hr_HR.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/ht.ico b/Plugins/spellchecker/Flags/ht.ico
new file mode 100644
index 0000000..51fb8a6
--- /dev/null
+++ b/Plugins/spellchecker/Flags/ht.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/hu_HU.ico b/Plugins/spellchecker/Flags/hu_HU.ico
new file mode 100644
index 0000000..73ddd2d
--- /dev/null
+++ b/Plugins/spellchecker/Flags/hu_HU.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/id_ID.ico b/Plugins/spellchecker/Flags/id_ID.ico
new file mode 100644
index 0000000..9432e87
--- /dev/null
+++ b/Plugins/spellchecker/Flags/id_ID.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/io.ico b/Plugins/spellchecker/Flags/io.ico
new file mode 100644
index 0000000..a2eaba7
--- /dev/null
+++ b/Plugins/spellchecker/Flags/io.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/iq.ico b/Plugins/spellchecker/Flags/iq.ico
new file mode 100644
index 0000000..8501e25
--- /dev/null
+++ b/Plugins/spellchecker/Flags/iq.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/is_IS.ico b/Plugins/spellchecker/Flags/is_IS.ico
new file mode 100644
index 0000000..6dbbcef
--- /dev/null
+++ b/Plugins/spellchecker/Flags/is_IS.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/it_IT.ico b/Plugins/spellchecker/Flags/it_IT.ico
new file mode 100644
index 0000000..11d08ed
--- /dev/null
+++ b/Plugins/spellchecker/Flags/it_IT.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/jm.ico b/Plugins/spellchecker/Flags/jm.ico
new file mode 100644
index 0000000..c4d76a5
--- /dev/null
+++ b/Plugins/spellchecker/Flags/jm.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/jo.ico b/Plugins/spellchecker/Flags/jo.ico
new file mode 100644
index 0000000..a80478f
--- /dev/null
+++ b/Plugins/spellchecker/Flags/jo.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/jp.ico b/Plugins/spellchecker/Flags/jp.ico
new file mode 100644
index 0000000..a415342
--- /dev/null
+++ b/Plugins/spellchecker/Flags/jp.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/ke.ico b/Plugins/spellchecker/Flags/ke.ico
new file mode 100644
index 0000000..554ecbb
--- /dev/null
+++ b/Plugins/spellchecker/Flags/ke.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/kg.ico b/Plugins/spellchecker/Flags/kg.ico
new file mode 100644
index 0000000..81b33aa
--- /dev/null
+++ b/Plugins/spellchecker/Flags/kg.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/ki.ico b/Plugins/spellchecker/Flags/ki.ico
new file mode 100644
index 0000000..93b225d
--- /dev/null
+++ b/Plugins/spellchecker/Flags/ki.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/km.ico b/Plugins/spellchecker/Flags/km.ico
new file mode 100644
index 0000000..3bf5683
--- /dev/null
+++ b/Plugins/spellchecker/Flags/km.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/km_KH.ico b/Plugins/spellchecker/Flags/km_KH.ico
new file mode 100644
index 0000000..69d08fb
--- /dev/null
+++ b/Plugins/spellchecker/Flags/km_KH.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/kn.ico b/Plugins/spellchecker/Flags/kn.ico
new file mode 100644
index 0000000..c959493
--- /dev/null
+++ b/Plugins/spellchecker/Flags/kn.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/kp.ico b/Plugins/spellchecker/Flags/kp.ico
new file mode 100644
index 0000000..4dc460f
--- /dev/null
+++ b/Plugins/spellchecker/Flags/kp.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/kr.ico b/Plugins/spellchecker/Flags/kr.ico
new file mode 100644
index 0000000..b2c8f19
--- /dev/null
+++ b/Plugins/spellchecker/Flags/kr.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/ku_TR.ico b/Plugins/spellchecker/Flags/ku_TR.ico
new file mode 100644
index 0000000..024d383
--- /dev/null
+++ b/Plugins/spellchecker/Flags/ku_TR.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/kw.ico b/Plugins/spellchecker/Flags/kw.ico
new file mode 100644
index 0000000..a7382b2
--- /dev/null
+++ b/Plugins/spellchecker/Flags/kw.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/ky.ico b/Plugins/spellchecker/Flags/ky.ico
new file mode 100644
index 0000000..a0f44c1
--- /dev/null
+++ b/Plugins/spellchecker/Flags/ky.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/kz.ico b/Plugins/spellchecker/Flags/kz.ico
new file mode 100644
index 0000000..e18579c
--- /dev/null
+++ b/Plugins/spellchecker/Flags/kz.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/la.ico b/Plugins/spellchecker/Flags/la.ico
new file mode 100644
index 0000000..1b57627
--- /dev/null
+++ b/Plugins/spellchecker/Flags/la.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/lb.ico b/Plugins/spellchecker/Flags/lb.ico
new file mode 100644
index 0000000..7221f19
--- /dev/null
+++ b/Plugins/spellchecker/Flags/lb.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/lc.ico b/Plugins/spellchecker/Flags/lc.ico
new file mode 100644
index 0000000..d3e57d6
--- /dev/null
+++ b/Plugins/spellchecker/Flags/lc.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/li.ico b/Plugins/spellchecker/Flags/li.ico
new file mode 100644
index 0000000..2431ff8
--- /dev/null
+++ b/Plugins/spellchecker/Flags/li.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/lk.ico b/Plugins/spellchecker/Flags/lk.ico
new file mode 100644
index 0000000..b39b1fd
--- /dev/null
+++ b/Plugins/spellchecker/Flags/lk.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/lr.ico b/Plugins/spellchecker/Flags/lr.ico
new file mode 100644
index 0000000..cfab480
--- /dev/null
+++ b/Plugins/spellchecker/Flags/lr.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/ls.ico b/Plugins/spellchecker/Flags/ls.ico
new file mode 100644
index 0000000..874b162
--- /dev/null
+++ b/Plugins/spellchecker/Flags/ls.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/lt_LT.ico b/Plugins/spellchecker/Flags/lt_LT.ico
new file mode 100644
index 0000000..a115a7d
--- /dev/null
+++ b/Plugins/spellchecker/Flags/lt_LT.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/lu_LU.ico b/Plugins/spellchecker/Flags/lu_LU.ico
new file mode 100644
index 0000000..c509c39
--- /dev/null
+++ b/Plugins/spellchecker/Flags/lu_LU.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/lv_LV.ico b/Plugins/spellchecker/Flags/lv_LV.ico
new file mode 100644
index 0000000..eafbc01
--- /dev/null
+++ b/Plugins/spellchecker/Flags/lv_LV.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/ly.ico b/Plugins/spellchecker/Flags/ly.ico
new file mode 100644
index 0000000..00bdab1
--- /dev/null
+++ b/Plugins/spellchecker/Flags/ly.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/ma.ico b/Plugins/spellchecker/Flags/ma.ico
new file mode 100644
index 0000000..7ac31f2
--- /dev/null
+++ b/Plugins/spellchecker/Flags/ma.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/mc.ico b/Plugins/spellchecker/Flags/mc.ico
new file mode 100644
index 0000000..14ab0d4
--- /dev/null
+++ b/Plugins/spellchecker/Flags/mc.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/md.ico b/Plugins/spellchecker/Flags/md.ico
new file mode 100644
index 0000000..238890d
--- /dev/null
+++ b/Plugins/spellchecker/Flags/md.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/mg_MG.ico b/Plugins/spellchecker/Flags/mg_MG.ico
new file mode 100644
index 0000000..c4bd13e
--- /dev/null
+++ b/Plugins/spellchecker/Flags/mg_MG.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/mh.ico b/Plugins/spellchecker/Flags/mh.ico
new file mode 100644
index 0000000..d82482f
--- /dev/null
+++ b/Plugins/spellchecker/Flags/mh.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/mi_NZ.ico b/Plugins/spellchecker/Flags/mi_NZ.ico
new file mode 100644
index 0000000..9c4cac8
--- /dev/null
+++ b/Plugins/spellchecker/Flags/mi_NZ.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/mk.ico b/Plugins/spellchecker/Flags/mk.ico
new file mode 100644
index 0000000..1c032d9
--- /dev/null
+++ b/Plugins/spellchecker/Flags/mk.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/ml.ico b/Plugins/spellchecker/Flags/ml.ico
new file mode 100644
index 0000000..d05c5be
--- /dev/null
+++ b/Plugins/spellchecker/Flags/ml.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/mm.ico b/Plugins/spellchecker/Flags/mm.ico
new file mode 100644
index 0000000..f8257e6
--- /dev/null
+++ b/Plugins/spellchecker/Flags/mm.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/mn_MN.ico b/Plugins/spellchecker/Flags/mn_MN.ico
new file mode 100644
index 0000000..2ad366d
--- /dev/null
+++ b/Plugins/spellchecker/Flags/mn_MN.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/mo.ico b/Plugins/spellchecker/Flags/mo.ico
new file mode 100644
index 0000000..0d1d78a
--- /dev/null
+++ b/Plugins/spellchecker/Flags/mo.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/mo_BF.ico b/Plugins/spellchecker/Flags/mo_BF.ico
new file mode 100644
index 0000000..cadd8be
--- /dev/null
+++ b/Plugins/spellchecker/Flags/mo_BF.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/mp.ico b/Plugins/spellchecker/Flags/mp.ico
new file mode 100644
index 0000000..6df1f65
--- /dev/null
+++ b/Plugins/spellchecker/Flags/mp.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/mq.ico b/Plugins/spellchecker/Flags/mq.ico
new file mode 100644
index 0000000..f42c7d1
--- /dev/null
+++ b/Plugins/spellchecker/Flags/mq.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/mr.ico b/Plugins/spellchecker/Flags/mr.ico
new file mode 100644
index 0000000..e2617fd
--- /dev/null
+++ b/Plugins/spellchecker/Flags/mr.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/mr_IN.ico b/Plugins/spellchecker/Flags/mr_IN.ico
new file mode 100644
index 0000000..b1a927d
--- /dev/null
+++ b/Plugins/spellchecker/Flags/mr_IN.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/ms.ico b/Plugins/spellchecker/Flags/ms.ico
new file mode 100644
index 0000000..700e4f5
--- /dev/null
+++ b/Plugins/spellchecker/Flags/ms.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/ms_MY.ico b/Plugins/spellchecker/Flags/ms_MY.ico
new file mode 100644
index 0000000..b6ddded
--- /dev/null
+++ b/Plugins/spellchecker/Flags/ms_MY.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/mt.ico b/Plugins/spellchecker/Flags/mt.ico
new file mode 100644
index 0000000..d8b5ad2
--- /dev/null
+++ b/Plugins/spellchecker/Flags/mt.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/mu.ico b/Plugins/spellchecker/Flags/mu.ico
new file mode 100644
index 0000000..09b4103
--- /dev/null
+++ b/Plugins/spellchecker/Flags/mu.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/mv.ico b/Plugins/spellchecker/Flags/mv.ico
new file mode 100644
index 0000000..ca77f76
--- /dev/null
+++ b/Plugins/spellchecker/Flags/mv.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/mz.ico b/Plugins/spellchecker/Flags/mz.ico
new file mode 100644
index 0000000..803e62c
--- /dev/null
+++ b/Plugins/spellchecker/Flags/mz.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/na.ico b/Plugins/spellchecker/Flags/na.ico
new file mode 100644
index 0000000..09ae908
--- /dev/null
+++ b/Plugins/spellchecker/Flags/na.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/nb_NO.ico b/Plugins/spellchecker/Flags/nb_NO.ico
new file mode 100644
index 0000000..f0b648d
--- /dev/null
+++ b/Plugins/spellchecker/Flags/nb_NO.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/nc.ico b/Plugins/spellchecker/Flags/nc.ico
new file mode 100644
index 0000000..f8c8695
--- /dev/null
+++ b/Plugins/spellchecker/Flags/nc.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/ne.ico b/Plugins/spellchecker/Flags/ne.ico
new file mode 100644
index 0000000..45b6112
--- /dev/null
+++ b/Plugins/spellchecker/Flags/ne.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/ne_NP.ico b/Plugins/spellchecker/Flags/ne_NP.ico
new file mode 100644
index 0000000..cc7954f
--- /dev/null
+++ b/Plugins/spellchecker/Flags/ne_NP.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/nf.ico b/Plugins/spellchecker/Flags/nf.ico
new file mode 100644
index 0000000..49e50f9
--- /dev/null
+++ b/Plugins/spellchecker/Flags/nf.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/ng.ico b/Plugins/spellchecker/Flags/ng.ico
new file mode 100644
index 0000000..250fc73
--- /dev/null
+++ b/Plugins/spellchecker/Flags/ng.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/ni.ico b/Plugins/spellchecker/Flags/ni.ico
new file mode 100644
index 0000000..f0795ce
--- /dev/null
+++ b/Plugins/spellchecker/Flags/ni.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/nl_NL.ico b/Plugins/spellchecker/Flags/nl_NL.ico
new file mode 100644
index 0000000..ea2d4c6
--- /dev/null
+++ b/Plugins/spellchecker/Flags/nl_NL.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/nn_NO.ico b/Plugins/spellchecker/Flags/nn_NO.ico
new file mode 100644
index 0000000..f0b648d
--- /dev/null
+++ b/Plugins/spellchecker/Flags/nn_NO.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/nr.ico b/Plugins/spellchecker/Flags/nr.ico
new file mode 100644
index 0000000..995a00a
--- /dev/null
+++ b/Plugins/spellchecker/Flags/nr.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/nr_ZA.ico b/Plugins/spellchecker/Flags/nr_ZA.ico
new file mode 100644
index 0000000..a786459
--- /dev/null
+++ b/Plugins/spellchecker/Flags/nr_ZA.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/ns_ZA.ico b/Plugins/spellchecker/Flags/ns_ZA.ico
new file mode 100644
index 0000000..a786459
--- /dev/null
+++ b/Plugins/spellchecker/Flags/ns_ZA.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/nu.ico b/Plugins/spellchecker/Flags/nu.ico
new file mode 100644
index 0000000..9867519
--- /dev/null
+++ b/Plugins/spellchecker/Flags/nu.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/ny_MW.ico b/Plugins/spellchecker/Flags/ny_MW.ico
new file mode 100644
index 0000000..dce539f
--- /dev/null
+++ b/Plugins/spellchecker/Flags/ny_MW.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/om.ico b/Plugins/spellchecker/Flags/om.ico
new file mode 100644
index 0000000..98d735b
--- /dev/null
+++ b/Plugins/spellchecker/Flags/om.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/or_IN.ico b/Plugins/spellchecker/Flags/or_IN.ico
new file mode 100644
index 0000000..b1a927d
--- /dev/null
+++ b/Plugins/spellchecker/Flags/or_IN.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/pa.ico b/Plugins/spellchecker/Flags/pa.ico
new file mode 100644
index 0000000..a0b4d34
--- /dev/null
+++ b/Plugins/spellchecker/Flags/pa.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/pe.ico b/Plugins/spellchecker/Flags/pe.ico
new file mode 100644
index 0000000..1e3d1a7
--- /dev/null
+++ b/Plugins/spellchecker/Flags/pe.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/pf.ico b/Plugins/spellchecker/Flags/pf.ico
new file mode 100644
index 0000000..2b2acef
--- /dev/null
+++ b/Plugins/spellchecker/Flags/pf.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/pg.ico b/Plugins/spellchecker/Flags/pg.ico
new file mode 100644
index 0000000..324ba8e
--- /dev/null
+++ b/Plugins/spellchecker/Flags/pg.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/pk.ico b/Plugins/spellchecker/Flags/pk.ico
new file mode 100644
index 0000000..d979e9d
--- /dev/null
+++ b/Plugins/spellchecker/Flags/pk.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/pl_PL.ico b/Plugins/spellchecker/Flags/pl_PL.ico
new file mode 100644
index 0000000..9b3bfd1
--- /dev/null
+++ b/Plugins/spellchecker/Flags/pl_PL.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/pm.ico b/Plugins/spellchecker/Flags/pm.ico
new file mode 100644
index 0000000..0de10e2
--- /dev/null
+++ b/Plugins/spellchecker/Flags/pm.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/pn.ico b/Plugins/spellchecker/Flags/pn.ico
new file mode 100644
index 0000000..566b7fa
--- /dev/null
+++ b/Plugins/spellchecker/Flags/pn.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/pr.ico b/Plugins/spellchecker/Flags/pr.ico
new file mode 100644
index 0000000..890e131
--- /dev/null
+++ b/Plugins/spellchecker/Flags/pr.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/ps.ico b/Plugins/spellchecker/Flags/ps.ico
new file mode 100644
index 0000000..773c066
--- /dev/null
+++ b/Plugins/spellchecker/Flags/ps.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/pt_BR.ico b/Plugins/spellchecker/Flags/pt_BR.ico
new file mode 100644
index 0000000..9d015e3
--- /dev/null
+++ b/Plugins/spellchecker/Flags/pt_BR.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/pt_PT.ico b/Plugins/spellchecker/Flags/pt_PT.ico
new file mode 100644
index 0000000..d322f99
--- /dev/null
+++ b/Plugins/spellchecker/Flags/pt_PT.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/pw.ico b/Plugins/spellchecker/Flags/pw.ico
new file mode 100644
index 0000000..cd5b828
--- /dev/null
+++ b/Plugins/spellchecker/Flags/pw.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/py.ico b/Plugins/spellchecker/Flags/py.ico
new file mode 100644
index 0000000..a0cc19e
--- /dev/null
+++ b/Plugins/spellchecker/Flags/py.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/qa.ico b/Plugins/spellchecker/Flags/qa.ico
new file mode 100644
index 0000000..dbd700f
--- /dev/null
+++ b/Plugins/spellchecker/Flags/qa.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/qu_BO.ico b/Plugins/spellchecker/Flags/qu_BO.ico
new file mode 100644
index 0000000..315825c
--- /dev/null
+++ b/Plugins/spellchecker/Flags/qu_BO.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/readme.txt b/Plugins/spellchecker/Flags/readme.txt
new file mode 100644
index 0000000..d20182b
--- /dev/null
+++ b/Plugins/spellchecker/Flags/readme.txt
@@ -0,0 +1,8 @@
+flag icons - famfamfam.com
+
+Free for any use. If you use these flags in your software or
+on your website, an email with a link or a screenshot would be nice. :)
+
+- Thanks to Christian Cook for splitting it all up into separate images
+
+Contact: mjames@gmail.com \ No newline at end of file
diff --git a/Plugins/spellchecker/Flags/ro_BO.ico b/Plugins/spellchecker/Flags/ro_BO.ico
new file mode 100644
index 0000000..149a031
--- /dev/null
+++ b/Plugins/spellchecker/Flags/ro_BO.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/ro_RO.ico b/Plugins/spellchecker/Flags/ro_RO.ico
new file mode 100644
index 0000000..1345d51
--- /dev/null
+++ b/Plugins/spellchecker/Flags/ro_RO.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/ru_RU.ico b/Plugins/spellchecker/Flags/ru_RU.ico
new file mode 100644
index 0000000..ecbe640
--- /dev/null
+++ b/Plugins/spellchecker/Flags/ru_RU.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/rw_RW.ico b/Plugins/spellchecker/Flags/rw_RW.ico
new file mode 100644
index 0000000..77d8c8b
--- /dev/null
+++ b/Plugins/spellchecker/Flags/rw_RW.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/sa.ico b/Plugins/spellchecker/Flags/sa.ico
new file mode 100644
index 0000000..9d10c25
--- /dev/null
+++ b/Plugins/spellchecker/Flags/sa.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/sb.ico b/Plugins/spellchecker/Flags/sb.ico
new file mode 100644
index 0000000..5937333
--- /dev/null
+++ b/Plugins/spellchecker/Flags/sb.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/sc.ico b/Plugins/spellchecker/Flags/sc.ico
new file mode 100644
index 0000000..770967b
--- /dev/null
+++ b/Plugins/spellchecker/Flags/sc.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/sd.ico b/Plugins/spellchecker/Flags/sd.ico
new file mode 100644
index 0000000..6545b0e
--- /dev/null
+++ b/Plugins/spellchecker/Flags/sd.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/sg.ico b/Plugins/spellchecker/Flags/sg.ico
new file mode 100644
index 0000000..f6ec180
--- /dev/null
+++ b/Plugins/spellchecker/Flags/sg.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/sh.ico b/Plugins/spellchecker/Flags/sh.ico
new file mode 100644
index 0000000..b6acb40
--- /dev/null
+++ b/Plugins/spellchecker/Flags/sh.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/sk_SK.ico b/Plugins/spellchecker/Flags/sk_SK.ico
new file mode 100644
index 0000000..b1fac30
--- /dev/null
+++ b/Plugins/spellchecker/Flags/sk_SK.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/sl.ico b/Plugins/spellchecker/Flags/sl.ico
new file mode 100644
index 0000000..e9159c1
--- /dev/null
+++ b/Plugins/spellchecker/Flags/sl.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/sl_SI.ico b/Plugins/spellchecker/Flags/sl_SI.ico
new file mode 100644
index 0000000..833fbec
--- /dev/null
+++ b/Plugins/spellchecker/Flags/sl_SI.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/sm.ico b/Plugins/spellchecker/Flags/sm.ico
new file mode 100644
index 0000000..5a7423c
--- /dev/null
+++ b/Plugins/spellchecker/Flags/sm.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/sn.ico b/Plugins/spellchecker/Flags/sn.ico
new file mode 100644
index 0000000..f8501d7
--- /dev/null
+++ b/Plugins/spellchecker/Flags/sn.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/so.ico b/Plugins/spellchecker/Flags/so.ico
new file mode 100644
index 0000000..27ea026
--- /dev/null
+++ b/Plugins/spellchecker/Flags/so.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/sr.ico b/Plugins/spellchecker/Flags/sr.ico
new file mode 100644
index 0000000..e32ebd7
--- /dev/null
+++ b/Plugins/spellchecker/Flags/sr.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/sr_CS.ico b/Plugins/spellchecker/Flags/sr_CS.ico
new file mode 100644
index 0000000..82e104a
--- /dev/null
+++ b/Plugins/spellchecker/Flags/sr_CS.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/ss_ZA.ico b/Plugins/spellchecker/Flags/ss_ZA.ico
new file mode 100644
index 0000000..c5dcd88
--- /dev/null
+++ b/Plugins/spellchecker/Flags/ss_ZA.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/st.ico b/Plugins/spellchecker/Flags/st.ico
new file mode 100644
index 0000000..d4dda8a
--- /dev/null
+++ b/Plugins/spellchecker/Flags/st.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/st_ZA.ico b/Plugins/spellchecker/Flags/st_ZA.ico
new file mode 100644
index 0000000..a786459
--- /dev/null
+++ b/Plugins/spellchecker/Flags/st_ZA.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/sv.ico b/Plugins/spellchecker/Flags/sv.ico
new file mode 100644
index 0000000..00bb860
--- /dev/null
+++ b/Plugins/spellchecker/Flags/sv.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/sv_SE.ico b/Plugins/spellchecker/Flags/sv_SE.ico
new file mode 100644
index 0000000..7a58adf
--- /dev/null
+++ b/Plugins/spellchecker/Flags/sv_SE.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/sy.ico b/Plugins/spellchecker/Flags/sy.ico
new file mode 100644
index 0000000..d022ebc
--- /dev/null
+++ b/Plugins/spellchecker/Flags/sy.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/tc.ico b/Plugins/spellchecker/Flags/tc.ico
new file mode 100644
index 0000000..750323d
--- /dev/null
+++ b/Plugins/spellchecker/Flags/tc.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/td.ico b/Plugins/spellchecker/Flags/td.ico
new file mode 100644
index 0000000..c02ef93
--- /dev/null
+++ b/Plugins/spellchecker/Flags/td.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/tet_ID.ico b/Plugins/spellchecker/Flags/tet_ID.ico
new file mode 100644
index 0000000..9432e87
--- /dev/null
+++ b/Plugins/spellchecker/Flags/tet_ID.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/tf.ico b/Plugins/spellchecker/Flags/tf.ico
new file mode 100644
index 0000000..334b7f0
--- /dev/null
+++ b/Plugins/spellchecker/Flags/tf.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/tg.ico b/Plugins/spellchecker/Flags/tg.ico
new file mode 100644
index 0000000..2ab28c0
--- /dev/null
+++ b/Plugins/spellchecker/Flags/tg.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/th.ico b/Plugins/spellchecker/Flags/th.ico
new file mode 100644
index 0000000..522dd9e
--- /dev/null
+++ b/Plugins/spellchecker/Flags/th.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/tj.ico b/Plugins/spellchecker/Flags/tj.ico
new file mode 100644
index 0000000..fd17d8a
--- /dev/null
+++ b/Plugins/spellchecker/Flags/tj.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/tk.ico b/Plugins/spellchecker/Flags/tk.ico
new file mode 100644
index 0000000..f76b197
--- /dev/null
+++ b/Plugins/spellchecker/Flags/tk.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/tl.ico b/Plugins/spellchecker/Flags/tl.ico
new file mode 100644
index 0000000..e9956a8
--- /dev/null
+++ b/Plugins/spellchecker/Flags/tl.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/tl_PH.ico b/Plugins/spellchecker/Flags/tl_PH.ico
new file mode 100644
index 0000000..d436947
--- /dev/null
+++ b/Plugins/spellchecker/Flags/tl_PH.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/tm.ico b/Plugins/spellchecker/Flags/tm.ico
new file mode 100644
index 0000000..676fa75
--- /dev/null
+++ b/Plugins/spellchecker/Flags/tm.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/tn.ico b/Plugins/spellchecker/Flags/tn.ico
new file mode 100644
index 0000000..4427d36
--- /dev/null
+++ b/Plugins/spellchecker/Flags/tn.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/tn_ZA.ico b/Plugins/spellchecker/Flags/tn_ZA.ico
new file mode 100644
index 0000000..efa797a
--- /dev/null
+++ b/Plugins/spellchecker/Flags/tn_ZA.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/to.ico b/Plugins/spellchecker/Flags/to.ico
new file mode 100644
index 0000000..ce677d9
--- /dev/null
+++ b/Plugins/spellchecker/Flags/to.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/ts_ZA.ico b/Plugins/spellchecker/Flags/ts_ZA.ico
new file mode 100644
index 0000000..a786459
--- /dev/null
+++ b/Plugins/spellchecker/Flags/ts_ZA.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/tt.ico b/Plugins/spellchecker/Flags/tt.ico
new file mode 100644
index 0000000..a737128
--- /dev/null
+++ b/Plugins/spellchecker/Flags/tt.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/tv.ico b/Plugins/spellchecker/Flags/tv.ico
new file mode 100644
index 0000000..9778da1
--- /dev/null
+++ b/Plugins/spellchecker/Flags/tv.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/tw.ico b/Plugins/spellchecker/Flags/tw.ico
new file mode 100644
index 0000000..4f44eea
--- /dev/null
+++ b/Plugins/spellchecker/Flags/tw.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/tz.ico b/Plugins/spellchecker/Flags/tz.ico
new file mode 100644
index 0000000..961587b
--- /dev/null
+++ b/Plugins/spellchecker/Flags/tz.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/ug.ico b/Plugins/spellchecker/Flags/ug.ico
new file mode 100644
index 0000000..91b92fd
--- /dev/null
+++ b/Plugins/spellchecker/Flags/ug.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/uk_UA.ico b/Plugins/spellchecker/Flags/uk_UA.ico
new file mode 100644
index 0000000..be54102
--- /dev/null
+++ b/Plugins/spellchecker/Flags/uk_UA.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/um.ico b/Plugins/spellchecker/Flags/um.ico
new file mode 100644
index 0000000..0979678
--- /dev/null
+++ b/Plugins/spellchecker/Flags/um.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/uy.ico b/Plugins/spellchecker/Flags/uy.ico
new file mode 100644
index 0000000..9b82030
--- /dev/null
+++ b/Plugins/spellchecker/Flags/uy.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/uz_UZ.ico b/Plugins/spellchecker/Flags/uz_UZ.ico
new file mode 100644
index 0000000..1665b26
--- /dev/null
+++ b/Plugins/spellchecker/Flags/uz_UZ.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/va.ico b/Plugins/spellchecker/Flags/va.ico
new file mode 100644
index 0000000..0f7e081
--- /dev/null
+++ b/Plugins/spellchecker/Flags/va.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/vc.ico b/Plugins/spellchecker/Flags/vc.ico
new file mode 100644
index 0000000..65e2626
--- /dev/null
+++ b/Plugins/spellchecker/Flags/vc.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/ve.ico b/Plugins/spellchecker/Flags/ve.ico
new file mode 100644
index 0000000..45ffac8
--- /dev/null
+++ b/Plugins/spellchecker/Flags/ve.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/ve_ZA.ico b/Plugins/spellchecker/Flags/ve_ZA.ico
new file mode 100644
index 0000000..a786459
--- /dev/null
+++ b/Plugins/spellchecker/Flags/ve_ZA.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/vg.ico b/Plugins/spellchecker/Flags/vg.ico
new file mode 100644
index 0000000..e41c589
--- /dev/null
+++ b/Plugins/spellchecker/Flags/vg.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/vi.ico b/Plugins/spellchecker/Flags/vi.ico
new file mode 100644
index 0000000..df3a8c1
--- /dev/null
+++ b/Plugins/spellchecker/Flags/vi.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/vi_VI.ico b/Plugins/spellchecker/Flags/vi_VI.ico
new file mode 100644
index 0000000..eb767d0
--- /dev/null
+++ b/Plugins/spellchecker/Flags/vi_VI.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/vu.ico b/Plugins/spellchecker/Flags/vu.ico
new file mode 100644
index 0000000..71d877f
--- /dev/null
+++ b/Plugins/spellchecker/Flags/vu.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/wf.ico b/Plugins/spellchecker/Flags/wf.ico
new file mode 100644
index 0000000..4836434
--- /dev/null
+++ b/Plugins/spellchecker/Flags/wf.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/ws.ico b/Plugins/spellchecker/Flags/ws.ico
new file mode 100644
index 0000000..468ec94
--- /dev/null
+++ b/Plugins/spellchecker/Flags/ws.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/xh_ZA.ico b/Plugins/spellchecker/Flags/xh_ZA.ico
new file mode 100644
index 0000000..a786459
--- /dev/null
+++ b/Plugins/spellchecker/Flags/xh_ZA.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/ye.ico b/Plugins/spellchecker/Flags/ye.ico
new file mode 100644
index 0000000..049c38c
--- /dev/null
+++ b/Plugins/spellchecker/Flags/ye.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/yt.ico b/Plugins/spellchecker/Flags/yt.ico
new file mode 100644
index 0000000..7bec05f
--- /dev/null
+++ b/Plugins/spellchecker/Flags/yt.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/zm.ico b/Plugins/spellchecker/Flags/zm.ico
new file mode 100644
index 0000000..d8ea987
--- /dev/null
+++ b/Plugins/spellchecker/Flags/zm.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/zu_ZA.ico b/Plugins/spellchecker/Flags/zu_ZA.ico
new file mode 100644
index 0000000..a786459
--- /dev/null
+++ b/Plugins/spellchecker/Flags/zu_ZA.ico
Binary files differ
diff --git a/Plugins/spellchecker/Flags/zw.ico b/Plugins/spellchecker/Flags/zw.ico
new file mode 100644
index 0000000..aa7b042
--- /dev/null
+++ b/Plugins/spellchecker/Flags/zw.ico
Binary files differ
diff --git a/Plugins/spellchecker/RichEdit.cpp b/Plugins/spellchecker/RichEdit.cpp
new file mode 100644
index 0000000..96faed5
--- /dev/null
+++ b/Plugins/spellchecker/RichEdit.cpp
@@ -0,0 +1,332 @@
+#include "commons.h"
+
+
+RichEdit::RichEdit(HWND hwnd)
+ : hwnd(NULL), ole(NULL), textDocument(NULL), stopped(0), undoEnabled(TRUE)
+{
+ SetHWND(hwnd);
+}
+
+RichEdit::~RichEdit()
+{
+ SetHWND(NULL);
+}
+
+bool RichEdit::IsValid() const
+{
+ return ole != NULL;
+}
+
+HWND RichEdit::GetHWND() const
+{
+ return hwnd;
+}
+
+void RichEdit::SetHWND(HWND hwnd)
+{
+ if (textDocument != NULL)
+ {
+ textDocument->Release();
+ textDocument = NULL;
+ }
+ if (ole != NULL)
+ {
+ ole->Release();
+ ole = NULL;
+ }
+
+ this->hwnd = hwnd;
+
+ if (hwnd == NULL)
+ return;
+
+ SendMessage(EM_GETOLEINTERFACE, 0, (LPARAM) &ole);
+ if (ole == NULL)
+ return;
+
+ if (ole->QueryInterface(IID_ITextDocument, (void**) &textDocument) != S_OK)
+ textDocument = NULL;
+}
+
+LRESULT RichEdit::SendMessage(UINT Msg, WPARAM wParam, LPARAM lParam) const
+{
+ return ::SendMessage(hwnd, Msg, wParam, lParam);
+}
+
+bool RichEdit::IsReadOnly() const
+{
+ return (GetWindowLong(hwnd, GWL_STYLE) & ES_READONLY) == ES_READONLY;
+}
+
+void RichEdit::SuspendUndo()
+{
+ if (textDocument != NULL)
+ {
+ textDocument->Undo(tomSuspend, NULL);
+ undoEnabled = FALSE;
+ }
+}
+
+void RichEdit::ResumeUndo()
+{
+ if (textDocument != NULL)
+ {
+ textDocument->Undo(tomResume, NULL);
+ undoEnabled = TRUE;
+ }
+}
+
+void RichEdit::Stop()
+{
+ stopped++;
+ if (stopped != 1)
+ return;
+
+ SuspendUndo();
+
+// HideCaret(hwnd);
+ SendMessage(WM_SETREDRAW, FALSE, 0);
+
+ SendMessage(EM_GETSCROLLPOS, 0, (LPARAM) &old_scroll_pos);
+ SendMessage(EM_EXGETSEL, 0, (LPARAM) &old_sel);
+ GetCaretPos(&caretPos);
+
+ old_mask = SendMessage(EM_GETEVENTMASK, 0, 0);
+ SendMessage(EM_SETEVENTMASK, 0, old_mask & ~ENM_CHANGE);
+
+ inverse = (old_sel.cpMin >= LOWORD(SendMessage(EM_CHARFROMPOS, 0, (LPARAM) &caretPos)));
+}
+
+void RichEdit::Start()
+{
+ stopped--;
+
+ if (stopped < 0)
+ {
+ stopped = 0;
+ return;
+ }
+ else if (stopped > 0)
+ return;
+
+ if (inverse)
+ {
+ LONG tmp = old_sel.cpMin;
+ old_sel.cpMin = old_sel.cpMax;
+ old_sel.cpMax = tmp;
+ }
+
+ SendMessage(EM_SETEVENTMASK, 0, old_mask);
+ SendMessage(EM_EXSETSEL, 0, (LPARAM) &old_sel);
+ SendMessage(EM_SETSCROLLPOS, 0, (LPARAM) &old_scroll_pos);
+
+ SendMessage(WM_SETREDRAW, TRUE, 0);
+ InvalidateRect(hwnd, NULL, FALSE);
+// ShowCaret(hwnd);
+
+ ResumeUndo();
+}
+
+BOOL RichEdit::IsStopped()
+{
+ return stopped > 0;
+}
+
+int RichEdit::GetCharFromPos(const POINT &pt)
+{
+ return LOWORD(SendMessage(EM_CHARFROMPOS, 0, (LPARAM) &pt));
+}
+
+int RichEdit::GetLineCount() const
+{
+ return SendMessage(EM_GETLINECOUNT, 0, 0);
+}
+
+void RichEdit::GetLine(int line, TCHAR *text, size_t text_len) const
+{
+ *((WORD*)text) = text_len - 1;
+ unsigned size = (unsigned) SendMessage(EM_GETLINE, (WPARAM) line, (LPARAM) text);
+ // Sometimes it likes to return size = lineLen+1, adding an \n at the end, so we remove it here
+ // to make both implementations return same size
+ int lineLen = GetLineLength(line);
+ size = (unsigned) max(0, min((int)text_len - 1, min((int) size, lineLen)));
+ text[size] = _T('\0');
+
+/*
+ int len = GetLineLength(line);
+ if (len < 1)
+ {
+ text[0] = 0;
+ return;
+ }
+
+ int first = GetFirstCharOfLine(line);
+ TCHAR *tmp = GetText(first, first + GetLineLength(line));
+ lstrcpyn(text, tmp, text_len);
+ mir_free(tmp);
+*/
+}
+
+int RichEdit::GetLineLength(int line) const
+{
+ return SendMessage(EM_LINELENGTH, GetFirstCharOfLine(line), 0);
+}
+
+int RichEdit::GetFirstCharOfLine(int line) const
+{
+ return SendMessage(EM_LINEINDEX, (WPARAM) line, 0);
+}
+
+int RichEdit::GetLineFromChar(int charPos) const
+{
+ return SendMessage(EM_LINEFROMCHAR, charPos, 0);
+}
+
+CHARRANGE RichEdit::GetSel() const
+{
+ CHARRANGE sel;
+ SendMessage(EM_EXGETSEL, 0, (LPARAM) &sel);
+ return sel;
+}
+
+void RichEdit::SetSel(int start, int end)
+{
+ CHARRANGE sel = { start, end };
+ SetSel(sel);
+}
+
+void RichEdit::SetSel(const CHARRANGE &sel)
+{
+ SendMessage(EM_EXSETSEL, 0, (LPARAM) &sel);
+}
+
+int RichEdit::GetTextLength() const
+{
+ return GetWindowTextLength(hwnd);
+}
+
+TCHAR *RichEdit::GetText(int start, int end) const
+{
+ if (end <= start)
+ end = GetTextLength();
+
+ if (textDocument != NULL)
+ {
+ ITextRange *range;
+ if (textDocument->Range(start, end, &range) != S_OK)
+ return mir_tstrdup(_T(""));
+
+ BSTR text = NULL;
+ if (range->GetText(&text) != S_OK || text == NULL)
+ {
+ range->Release();
+ return mir_tstrdup(_T(""));
+ }
+
+ TCHAR *ret = mir_u2t(text);
+
+ SysFreeString(text);
+
+ range->Release();
+
+ return ret;
+ }
+ else
+ {
+ int len = GetTextLength();
+ TCHAR *tmp = (TCHAR *) mir_alloc(len * sizeof(TCHAR));
+ GetWindowText(hwnd, tmp, len);
+ tmp[len] = 0;
+
+ TCHAR *ret = (TCHAR *) mir_alloc((end - start + 1) * sizeof(TCHAR));
+ memmove(ret, &tmp[start], (end - start) * sizeof(TCHAR));
+ ret[end - start] = 0;
+
+ mir_free(tmp);
+ return ret;
+ }
+}
+
+void RichEdit::ReplaceSel(const TCHAR *new_text)
+{
+ if (stopped)
+ {
+ CHARRANGE sel = GetSel();
+
+ ResumeUndo();
+
+ SendMessage(EM_REPLACESEL, undoEnabled, (LPARAM) new_text);
+
+ SuspendUndo();
+
+ FixSel(&old_sel, sel, lstrlen(new_text));
+
+ SendMessage(WM_SETREDRAW, FALSE, 0);
+ SendMessage(EM_SETEVENTMASK, 0, old_mask & ~ENM_CHANGE);
+ }
+ else
+ {
+ SendMessage(EM_REPLACESEL, undoEnabled, (LPARAM) new_text);
+ }
+}
+
+int RichEdit::Replace(int start, int end, const TCHAR *new_text)
+{
+ CHARRANGE sel = GetSel();
+ CHARRANGE replace_sel = { start, end };
+ SetSel(replace_sel);
+
+ ReplaceSel(new_text);
+
+ int dif = FixSel(&sel, replace_sel, lstrlen(new_text));
+ SetSel(sel);
+ return dif;
+}
+
+int RichEdit::Insert(int pos, const TCHAR *text)
+{
+ CHARRANGE sel = GetSel();
+ CHARRANGE replace_sel = { pos, pos };
+ SetSel(replace_sel);
+
+ ReplaceSel(text);
+
+ int dif = FixSel(&sel, replace_sel, lstrlen(text));
+ SetSel(sel);
+ return dif;
+}
+
+int RichEdit::Delete(int start, int end)
+{
+ CHARRANGE sel = GetSel();
+ CHARRANGE replace_sel = { start, end };
+ SetSel(replace_sel);
+
+ ReplaceSel(_T(""));
+
+ int dif = FixSel(&sel, replace_sel, 0);
+ SetSel(sel);
+ return dif;
+}
+
+int RichEdit::FixSel(CHARRANGE *to_fix, CHARRANGE sel_changed, int new_len)
+{
+ int dif = new_len - (sel_changed.cpMax - sel_changed.cpMin);
+
+ if (to_fix->cpMax <= sel_changed.cpMin)
+ return dif;
+
+ int newMax = sel_changed.cpMax + dif;
+
+ if (to_fix->cpMin >= sel_changed.cpMax)
+ to_fix->cpMin += dif;
+ else if (to_fix->cpMin >= newMax) // For dif < 0, pos beetween sel_changed.cpMax + dif and sel_changed.cpMax
+ to_fix->cpMin = newMax;
+
+ if (to_fix->cpMax >= sel_changed.cpMax)
+ to_fix->cpMax += dif;
+ else if (to_fix->cpMax >= newMax) // For dif < 0, pos beetween sel_changed.cpMax + dif and sel_changed.cpMax
+ to_fix->cpMax = newMax;
+
+ return dif;
+}
diff --git a/Plugins/spellchecker/RichEdit.h b/Plugins/spellchecker/RichEdit.h
new file mode 100644
index 0000000..e8b4101
--- /dev/null
+++ b/Plugins/spellchecker/RichEdit.h
@@ -0,0 +1,71 @@
+#ifndef __RICHEDIT_H__
+#define __RICHEDIT_H__
+
+#include <windows.h>
+#include <richedit.h>
+#include <tom.h>
+#include <richole.h>
+
+
+class RichEdit
+{
+ HWND hwnd;
+ IRichEditOle *ole;
+ ITextDocument *textDocument;
+
+ int stopped;
+ BOOL undoEnabled;
+ POINT old_scroll_pos;
+ CHARRANGE old_sel;
+ POINT caretPos;
+ DWORD old_mask;
+ BOOL inverse;
+
+public:
+ RichEdit(HWND hwnd);
+ ~RichEdit();
+
+ bool IsValid() const;
+ HWND GetHWND() const;
+
+ LRESULT SendMessage(UINT Msg, WPARAM wParam, LPARAM lParam) const;
+
+ bool IsReadOnly() const;
+
+ void SuspendUndo();
+ void ResumeUndo();
+ void Stop();
+ void Start();
+ BOOL IsStopped();
+
+ int GetCharFromPos(const POINT &pt);
+
+ int GetLineCount() const;
+ void GetLine(int line, TCHAR *text, size_t text_len) const;
+ int GetLineLength(int line) const;
+ int GetFirstCharOfLine(int line) const;
+ int GetLineFromChar(int charPos) const;
+
+ CHARRANGE GetSel() const;
+ void SetSel(int start, int end);
+ void SetSel(const CHARRANGE &sel);
+
+ TCHAR *GetText(int start, int end) const;
+ int GetTextLength() const;
+
+ void ReplaceSel(const TCHAR *new_text);
+ int Replace(int start, int end, const TCHAR *new_text);
+ int Insert(int pos, const TCHAR *text);
+ int Delete(int start, int end);
+
+private:
+ void SetHWND(HWND hwnd);
+
+ int FixSel(CHARRANGE *to_fix, CHARRANGE sel_changed, int new_len);
+};
+
+
+
+
+
+#endif // __RICHEDIT_H__
diff --git a/Plugins/spellchecker/ZIP/Dictionaries/en_US.aff b/Plugins/spellchecker/ZIP/Dictionaries/en_US.aff
new file mode 100644
index 0000000..34ac09b
--- /dev/null
+++ b/Plugins/spellchecker/ZIP/Dictionaries/en_US.aff
@@ -0,0 +1,189 @@
+SET ISO8859-1
+TRY esianrtolcdugmphbyfvkwzESIANRTOLCDUGMPHBYFVKWZ
+WORDCHARS '-qwertzuiopasdfghjklyxcvbnmQWERTZUIOPASDFGHJKLYXCVBNM
+
+PFX A Y 1
+PFX A 0 re .
+
+PFX I Y 1
+PFX I 0 in .
+
+PFX U Y 1
+PFX U 0 un .
+
+PFX C Y 1
+PFX C 0 de .
+
+PFX E Y 1
+PFX E 0 dis .
+
+PFX F Y 1
+PFX F 0 con .
+
+PFX K Y 1
+PFX K 0 pro .
+
+SFX V N 2
+SFX V e ive e
+SFX V 0 ive [^e]
+
+SFX N Y 3
+SFX N e ion e
+SFX N y ication y
+SFX N 0 en [^ey]
+
+SFX X Y 3
+SFX X e ions e
+SFX X y ications y
+SFX X 0 ens [^ey]
+
+SFX H N 2
+SFX H y ieth y
+SFX H 0 th [^y]
+
+SFX Y Y 1
+SFX Y 0 ly .
+
+SFX G Y 2
+SFX G e ing e
+SFX G 0 ing [^e]
+
+SFX J Y 2
+SFX J e ings e
+SFX J 0 ings [^e]
+
+SFX D Y 4
+SFX D 0 d e
+SFX D y ied [^aeiou]y
+SFX D 0 ed [^ey]
+SFX D 0 ed [aeiou]y
+
+SFX T N 4
+SFX T 0 st e
+SFX T y iest [^aeiou]y
+SFX T 0 est [aeiou]y
+SFX T 0 est [^ey]
+
+SFX R Y 4
+SFX R 0 r e
+SFX R y ier [^aeiou]y
+SFX R 0 er [aeiou]y
+SFX R 0 er [^ey]
+
+SFX Z Y 4
+SFX Z 0 rs e
+SFX Z y iers [^aeiou]y
+SFX Z 0 ers [aeiou]y
+SFX Z 0 ers [^ey]
+
+SFX S Y 4
+SFX S y ies [^aeiou]y
+SFX S 0 s [aeiou]y
+SFX S 0 es [sxzh]
+SFX S 0 s [^sxzhy]
+
+SFX P Y 3
+SFX P y iness [^aeiou]y
+SFX P 0 ness [aeiou]y
+SFX P 0 ness [^y]
+
+SFX M Y 1
+SFX M 0 's .
+
+SFX B Y 3
+SFX B 0 able [^aeiou]
+SFX B 0 able ee
+SFX B e able [^aeiou]e
+
+SFX L Y 1
+SFX L 0 ment .
+
+REP 88
+REP a ei
+REP ei a
+REP a ey
+REP ey a
+REP ai ie
+REP ie ai
+REP are air
+REP are ear
+REP are eir
+REP air are
+REP air ere
+REP ere air
+REP ere ear
+REP ere eir
+REP ear are
+REP ear air
+REP ear ere
+REP eir are
+REP eir ere
+REP ch te
+REP te ch
+REP ch ti
+REP ti ch
+REP ch tu
+REP tu ch
+REP ch s
+REP s ch
+REP ch k
+REP k ch
+REP f ph
+REP ph f
+REP gh f
+REP f gh
+REP i igh
+REP igh i
+REP i uy
+REP uy i
+REP i ee
+REP ee i
+REP j di
+REP di j
+REP j gg
+REP gg j
+REP j ge
+REP ge j
+REP s ti
+REP ti s
+REP s ci
+REP ci s
+REP k cc
+REP cc k
+REP k qu
+REP qu k
+REP kw qu
+REP o eau
+REP eau o
+REP o ew
+REP ew o
+REP oo ew
+REP ew oo
+REP ew ui
+REP ui ew
+REP oo ui
+REP ui oo
+REP ew u
+REP u ew
+REP oo u
+REP u oo
+REP u oe
+REP oe u
+REP u ieu
+REP ieu u
+REP ue ew
+REP ew ue
+REP uff ough
+REP oo ieu
+REP ieu oo
+REP ier ear
+REP ear ier
+REP ear air
+REP air ear
+REP w qu
+REP qu w
+REP z ss
+REP ss z
+REP shun tion
+REP shun sion
+REP shun cion
diff --git a/Plugins/spellchecker/ZIP/Dictionaries/en_US.dic b/Plugins/spellchecker/ZIP/Dictionaries/en_US.dic
new file mode 100644
index 0000000..fc55225
--- /dev/null
+++ b/Plugins/spellchecker/ZIP/Dictionaries/en_US.dic
@@ -0,0 +1,62077 @@
+62076
+a
+A
+AA
+AAA
+Aachen/M
+aardvark/SM
+Aaren/M
+Aarhus/M
+Aarika/M
+Aaron/M
+AB
+aback
+abacus/SM
+abaft
+Abagael/M
+Abagail/M
+abalone/SM
+abandoner/M
+abandon/LGDRS
+abandonment/SM
+abase/LGDSR
+abasement/S
+abaser/M
+abashed/UY
+abashment/MS
+abash/SDLG
+abate/DSRLG
+abated/U
+abatement/MS
+abater/M
+abattoir/SM
+Abba/M
+Abbe/M
+abbé/S
+abbess/SM
+Abbey/M
+abbey/MS
+Abbie/M
+Abbi/M
+Abbot/M
+abbot/MS
+Abbott/M
+abbr
+abbrev
+abbreviated/UA
+abbreviates/A
+abbreviate/XDSNG
+abbreviating/A
+abbreviation/M
+Abbye/M
+Abby/M
+ABC/M
+Abdel/M
+abdicate/NGDSX
+abdication/M
+abdomen/SM
+abdominal/YS
+abduct/DGS
+abduction/SM
+abductor/SM
+Abdul/M
+ab/DY
+abeam
+Abelard/M
+Abel/M
+Abelson/M
+Abe/M
+Aberdeen/M
+Abernathy/M
+aberrant/YS
+aberrational
+aberration/SM
+abet/S
+abetted
+abetting
+abettor/SM
+Abeu/M
+abeyance/MS
+abeyant
+Abey/M
+abhorred
+abhorrence/MS
+abhorrent/Y
+abhorrer/M
+abhorring
+abhor/S
+abidance/MS
+abide/JGSR
+abider/M
+abiding/Y
+Abidjan/M
+Abie/M
+Abigael/M
+Abigail/M
+Abigale/M
+Abilene/M
+ability/IMES
+abjection/MS
+abjectness/SM
+abject/SGPDY
+abjuration/SM
+abjuratory
+abjurer/M
+abjure/ZGSRD
+ablate/VGNSDX
+ablation/M
+ablative/SY
+ablaze
+abler/E
+ables/E
+ablest
+able/U
+abloom
+ablution/MS
+Ab/M
+ABM/S
+abnegate/NGSDX
+abnegation/M
+Abner/M
+abnormality/SM
+abnormal/SY
+aboard
+abode/GMDS
+abolisher/M
+abolish/LZRSDG
+abolishment/MS
+abolitionism/SM
+abolitionist/SM
+abolition/SM
+abominable
+abominably
+abominate/XSDGN
+abomination/M
+aboriginal/YS
+aborigine/SM
+Aborigine/SM
+aborning
+abortionist/MS
+abortion/MS
+abortiveness/M
+abortive/PY
+abort/SRDVG
+Abo/SM
+abound/GDS
+about/S
+aboveboard
+aboveground
+above/S
+abracadabra/S
+abrader/M
+abrade/SRDG
+Abraham/M
+Abrahan/M
+Abra/M
+Abramo/M
+Abram/SM
+Abramson/M
+Abran/M
+abrasion/MS
+abrasiveness/S
+abrasive/SYMP
+abreaction/MS
+abreast
+abridge/DSRG
+abridged/U
+abridger/M
+abridgment/SM
+abroad
+abrogate/XDSNG
+abrogation/M
+abrogator/SM
+abruptness/SM
+abrupt/TRYP
+ABS
+abscess/GDSM
+abscissa/SM
+abscission/SM
+absconder/M
+abscond/SDRZG
+abseil/SGDR
+absence/SM
+absenteeism/SM
+absentee/MS
+absentia/M
+absentmindedness/S
+absentminded/PY
+absent/SGDRY
+absinthe/SM
+abs/M
+absoluteness/SM
+absolute/NPRSYTX
+absolution/M
+absolutism/MS
+absolutist/SM
+absolve/GDSR
+absolver/M
+absorb/ASGD
+absorbed/U
+absorbency/MS
+absorbent/MS
+absorber/SM
+absorbing/Y
+absorption/MS
+absorptive
+absorptivity/M
+abstainer/M
+abstain/GSDRZ
+abstemiousness/MS
+abstemious/YP
+abstention/SM
+abstinence/MS
+abstinent/Y
+abstractedness/SM
+abstracted/YP
+abstracter/M
+abstractionism/M
+abstractionist/SM
+abstraction/SM
+abstractness/SM
+abstractor/MS
+abstract/PTVGRDYS
+abstruseness/SM
+abstruse/PRYT
+absurdity/SM
+absurdness/SM
+absurd/PRYST
+Abuja
+abundance/SM
+abundant/Y
+abused/E
+abuse/GVZDSRB
+abuser/M
+abuses/E
+abusing/E
+abusiveness/SM
+abusive/YP
+abut/LS
+abutment/SM
+abutted
+abutter/MS
+abutting
+abuzz
+abysmal/Y
+abyssal
+Abyssinia/M
+Abyssinian
+abyss/SM
+AC
+acacia/SM
+academe/MS
+academia/SM
+academical/Y
+academicianship
+academician/SM
+academic/S
+academy/SM
+Acadia/M
+acanthus/MS
+Acapulco/M
+accede/SDG
+accelerated/U
+accelerate/NGSDXV
+accelerating/Y
+acceleration/M
+accelerator/SM
+accelerometer/SM
+accented/U
+accent/SGMD
+accentual/Y
+accentuate/XNGSD
+accentuation/M
+acceptability/SM
+acceptability's/U
+acceptableness/SM
+acceptable/P
+acceptably/U
+acceptance/SM
+acceptant
+acceptation/SM
+accepted/Y
+accepter/M
+accepting/PY
+acceptor/MS
+accept/RDBSZVG
+accessed/A
+accessibility/IMS
+accessible/IU
+accessibly/I
+accession/SMDG
+accessors
+accessory/SM
+access/SDMG
+accidence/M
+accidentalness/M
+accidental/SPY
+accident/MS
+acclaimer/M
+acclaim/SDRG
+acclamation/MS
+acclimate/XSDGN
+acclimation/M
+acclimatisation
+acclimatise/DG
+acclimatization/AMS
+acclimatized/U
+acclimatize/RSDGZ
+acclimatizes/A
+acclivity/SM
+accolade/GDSM
+accommodated/U
+accommodate/XVNGSD
+accommodating/Y
+accommodation/M
+accommodativeness/M
+accommodative/P
+accompanied/U
+accompanier/M
+accompaniment/MS
+accompanist/SM
+accompany/DRSG
+accomplice/MS
+accomplished/U
+accomplisher/M
+accomplishment/SM
+accomplish/SRDLZG
+accordance/SM
+accordant/Y
+accorder/M
+according/Y
+accordionist/SM
+accordion/MS
+accord/SZGMRD
+accost/SGD
+accountability/MS
+accountability's/U
+accountableness/M
+accountable/U
+accountably/U
+accountancy/SM
+accountant/MS
+account/BMDSGJ
+accounted/U
+accounting/M
+accouter/GSD
+accouterments
+accouterment's
+accoutrement/M
+Accra/M
+accreditation/SM
+accredited/U
+accredit/SGD
+accretion/SM
+accrual/MS
+accrue/SDG
+acct
+acculturate/XSDVNG
+acculturation/M
+accumulate/VNGSDX
+accumulation/M
+accumulativeness/M
+accumulative/YP
+accumulator/MS
+accuracy/IMS
+accurate/IY
+accurateness/SM
+accursedness/SM
+accursed/YP
+accusal/M
+accusation/SM
+accusative/S
+accusatory
+accused/M
+accuser/M
+accuse/SRDZG
+accusing/Y
+accustomedness/M
+accustomed/P
+accustom/SGD
+ac/DRG
+aced/M
+acerbate/DSG
+acerbic
+acerbically
+acerbity/MS
+ace/SM
+acetaminophen/S
+acetate/MS
+acetic
+acetone/SM
+acetonic
+acetylene/MS
+Acevedo/M
+Achaean/M
+Achebe/M
+ached/A
+ache/DSG
+achene/SM
+Achernar/M
+aches/A
+Acheson/M
+achievable/U
+achieved/UA
+achieve/LZGRSDB
+achievement/SM
+achiever/M
+Achilles
+aching/Y
+achoo
+achromatic
+achy/TR
+acidic
+acidification/M
+acidify/NSDG
+acidity/SM
+acidness/M
+acidoses
+acidosis/M
+acid/SMYP
+acidulous
+acing/M
+Ackerman/M
+acknowledgeable
+acknowledgedly
+acknowledged/U
+acknowledge/GZDRS
+acknowledger/M
+acknowledgment/SAM
+ACLU
+Ac/M
+ACM
+acme/SM
+acne/MDS
+acolyte/MS
+Aconcagua/M
+aconite/MS
+acorn/SM
+Acosta/M
+acoustical/Y
+acoustician/M
+acoustic/S
+acoustics/M
+acquaintance/MS
+acquaintanceship/S
+acquainted/U
+acquaint/GASD
+acquiesce/GSD
+acquiescence/SM
+acquiescent/Y
+acquirable
+acquire/ASDG
+acquirement/SM
+acquisition's/A
+acquisition/SM
+acquisitiveness/MS
+acquisitive/PY
+acquit/S
+acquittal/MS
+acquittance/M
+acquitted
+acquitter/M
+acquitting
+acreage/MS
+acre/MS
+acridity/MS
+acridness/SM
+acrid/TPRY
+acrimoniousness/MS
+acrimonious/YP
+acrimony/MS
+acrobatically
+acrobatic/S
+acrobatics/M
+acrobat/SM
+acronym/SM
+acrophobia/SM
+Acropolis/M
+acropolis/SM
+across
+acrostic/SM
+Acrux/M
+acrylate/M
+acrylic/S
+ACT
+Actaeon/M
+Acta/M
+ACTH
+acting/S
+actinic
+actinide/SM
+actinium/MS
+actinometer/MS
+action/DMSGB
+actions/AI
+action's/IA
+activate/AXCDSNGI
+activated/U
+activation/AMCI
+activator/SM
+active/APY
+actively/I
+activeness/MS
+actives
+activism/MS
+activist/MS
+activities/A
+activity/MSI
+Acton/M
+actor/MAS
+actress/SM
+act's
+Acts
+act/SADVG
+actuality/SM
+actualization/MAS
+actualize/GSD
+actualizes/A
+actual/SY
+actuarial/Y
+actuary/MS
+actuate/GNXSD
+actuation/M
+actuator/SM
+acuity/MS
+acumen/SM
+acupressure/S
+acupuncture/SM
+acupuncturist/S
+acuteness/MS
+acute/YTSRP
+acyclic
+acyclically
+acyclovir/S
+AD
+adage/MS
+adagio/S
+Adah/M
+Adair/M
+Adaline/M
+Ada/M
+adamant/SY
+Adamo/M
+Adam/SM
+Adamson/M
+Adana/M
+Adan/M
+adaptability/MS
+adaptable/U
+adaptation/MS
+adaptedness/M
+adapted/P
+adapter/M
+adapting/A
+adaption
+adaptively
+adaptiveness/M
+adaptive/U
+adaptivity
+adapt/SRDBZVG
+Adara/M
+ad/AS
+ADC
+Adda/M
+Addams
+addenda
+addend/SM
+addendum/M
+adder/M
+Addia/M
+addiction/MS
+addictive/P
+addict/SGVD
+Addie/M
+Addi/M
+Addison/M
+additional/Y
+addition/MS
+additive/YMS
+additivity
+addle/GDS
+addressability
+addressable/U
+addressed/A
+addressee/SM
+addresser/M
+addresses/A
+address/MDRSZGB
+Addressograph/M
+adduce/GRSD
+adducer/M
+adduct/DGVS
+adduction/M
+adductor/M
+Addy/M
+add/ZGBSDR
+Adelaida/M
+Adelaide/M
+Adela/M
+Adelbert/M
+Adele/M
+Adelheid/M
+Adelice/M
+Adelina/M
+Adelind/M
+Adeline/M
+Adella/M
+Adelle/M
+Adel/M
+Ade/M
+Adena/M
+Adenauer/M
+adenine/SM
+Aden/M
+adenoidal
+adenoid/S
+adeptness/MS
+adept/RYPTS
+adequacy/IMS
+adequate/IPY
+adequateness's/I
+adequateness/SM
+Adey/M
+Adham/M
+Adhara/M
+adherence/SM
+adherent/YMS
+adherer/M
+adhere/ZGRSD
+adhesion/MS
+adhesiveness/MS
+adhesive/PYMS
+adiabatic
+adiabatically
+Adiana/M
+Adidas/M
+adieu/S
+Adi/M
+Adina/M
+adiós
+adipose/S
+Adirondack/SM
+adj
+adjacency/MS
+adjacent/Y
+adjectival/Y
+adjective/MYS
+adjoin/SDG
+adjoint/M
+adjourn/DGLS
+adjournment/SM
+adjudge/DSG
+adjudicate/VNGXSD
+adjudication/M
+adjudicator/SM
+adjudicatory
+adjunct/VSYM
+adjuration/SM
+adjure/GSD
+adjustable/U
+adjustably
+adjust/DRALGSB
+adjusted/U
+adjuster's/A
+adjuster/SM
+adjustive
+adjustment/MAS
+adjustor's
+adjutant/SM
+Adkins/M
+Adlai/M
+Adler/M
+adman/M
+admen
+administer/GDJS
+administrable
+administrate/XSDVNG
+administration/M
+administrative/Y
+administrator/MS
+administratrix/M
+admirableness/M
+admirable/P
+admirably
+admiral/SM
+admiralty/MS
+Admiralty/S
+admiration/MS
+admirer/M
+admire/RSDZBG
+admiring/Y
+admissibility/ISM
+admissible/I
+admissibly
+admission/AMS
+admit/AS
+admittance/MS
+admitted/A
+admittedly
+admitting/A
+admix/SDG
+admixture/SM
+Adm/M
+Ad/MN
+admonisher/M
+admonish/GLSRD
+admonishing/Y
+admonishment/SM
+admonition/MS
+admonitory
+adobe/MS
+adolescence/MS
+adolescent/SYM
+Adolf/M
+Adolfo/M
+Adolphe/M
+Adolph/M
+Adolpho/M
+Adolphus/M
+Ado/M
+ado/MS
+Adonis/SM
+adopted/AU
+adopter/M
+adoption/MS
+adoptive/Y
+adopt/RDSBZVG
+adopts/A
+adorableness/SM
+adorable/P
+adorably
+Adora/M
+adoration/SM
+adore/DSRGZB
+Adoree/M
+Adore/M
+adorer/M
+adoring/Y
+adorned/U
+Adorne/M
+adornment/SM
+adorn/SGLD
+ADP
+Adrea/M
+adrenalin
+adrenaline/MS
+Adrenalin/MS
+adrenal/YS
+Adria/MX
+Adriana/M
+Adriane/M
+Adrian/M
+Adrianna/M
+Adrianne/M
+Adriano/M
+Adriatic
+Adriena/M
+Adrien/M
+Adrienne/M
+adrift
+adroitness/MS
+adroit/RTYP
+ads
+ad's
+adsorbate/M
+adsorbent/S
+adsorb/GSD
+adsorption/MS
+adsorptive/Y
+adulate/GNDSX
+adulation/M
+adulator/SM
+adulatory
+adulterant/SM
+adulterated/U
+adulterate/NGSDX
+adulteration/M
+adulterer/SM
+adulteress/MS
+adulterous/Y
+adultery/SM
+adulthood/MS
+adult/MYPS
+adultness/M
+adumbrate/XSDVGN
+adumbration/M
+adumbrative/Y
+adv
+advance/DSRLZG
+advancement/MS
+advancer/M
+advantage/GMEDS
+advantageous/EY
+advantageousness/M
+Adventist/M
+adventist/S
+adventitiousness/M
+adventitious/PY
+adventive/Y
+Advent/SM
+advent/SVM
+adventurer/M
+adventuresome
+adventure/SRDGMZ
+adventuress/SM
+adventurousness/SM
+adventurous/YP
+adverbial/MYS
+adverb/SM
+adversarial
+adversary/SM
+adverse/DSRPYTG
+adverseness/MS
+adversity/SM
+advert/GSD
+advertised/U
+advertise/JGZSRDL
+advertisement/SM
+advertiser/M
+advertising/M
+advertorial/S
+advice/SM
+Advil/M
+advisability/SIM
+advisable/I
+advisableness/M
+advisably
+advisedly/I
+advised/YU
+advisee/MS
+advisement/MS
+adviser/M
+advise/ZRSDGLB
+advisor's
+advisory/S
+advocacy/SM
+advocate/NGVDS
+advocation/M
+advt
+adze's
+adz/MDSG
+Aegean
+aegis/SM
+Aelfric/M
+Aeneas
+Aeneid/M
+aeolian
+Aeolus/M
+aeon's
+aerate/XNGSD
+aeration/M
+aerator/MS
+aerialist/MS
+aerial/SMY
+Aeriela/M
+Aeriell/M
+Aeriel/M
+aerie/SRMT
+aeroacoustic
+aerobatic/S
+aerobically
+aerobic/S
+aerodrome/SM
+aerodynamically
+aerodynamic/S
+aerodynamics/M
+aeronautical/Y
+aeronautic/S
+aeronautics/M
+aerosolize/D
+aerosol/MS
+aerospace/SM
+Aeschylus/M
+Aesculapius/M
+Aesop/M
+aesthete/S
+aesthetically
+aestheticism/MS
+aesthetics/M
+aesthetic/U
+aether/M
+aetiology/M
+AF
+AFAIK
+afar/S
+AFB
+AFC
+AFDC
+affability/MS
+affable/TR
+affably
+affair/SM
+affectation/MS
+affectedness/EM
+affected/UEYP
+affect/EGSD
+affecter/M
+affecting/Y
+affectionate/UY
+affectioned
+affection/EMS
+affectioning
+affective/MY
+afferent/YS
+affiance/GDS
+affidavit/SM
+affiliated/U
+affiliate/EXSDNG
+affiliation/EM
+affine
+affinity/SM
+affirm/ASDG
+affirmation/SAM
+affirmative/SY
+affix/SDG
+afflatus/MS
+afflict/GVDS
+affliction/SM
+afflictive/Y
+affluence/SM
+affluent/YS
+afford/DSBG
+afforest/A
+afforestation/SM
+afforested
+afforesting
+afforests
+affray/MDSG
+affricate/VNMS
+affrication/M
+affricative/M
+affright
+affront/GSDM
+Afghani/SM
+Afghanistan/M
+afghan/MS
+Afghan/SM
+aficionado/MS
+afield
+afire
+aflame
+afloat
+aflutter
+afoot
+afore
+aforementioned
+aforesaid
+aforethought/S
+afoul
+Afr
+afraid/U
+afresh
+Africa/M
+African/MS
+Afrikaans/M
+Afrikaner/SM
+afro
+Afrocentric
+Afrocentrism/S
+Afro/MS
+afterbirth/M
+afterbirths
+afterburner/MS
+aftercare/SM
+aftereffect/MS
+afterglow/MS
+afterimage/MS
+afterlife/M
+afterlives
+aftermath/M
+aftermaths
+aftermost
+afternoon/SM
+aftershave/S
+aftershock/SM
+afters/M
+aftertaste/SM
+afterthought/MS
+afterward/S
+afterworld/MS
+Afton/M
+aft/ZR
+Agace/M
+again
+against
+Agamemnon/M
+agapae
+agape/S
+agar/MS
+Agassiz/M
+Agata/M
+agate/SM
+Agatha/M
+Agathe/M
+agave/SM
+agedness/M
+aged/PY
+age/GJDRSMZ
+ageism/S
+ageist/S
+agelessness/MS
+ageless/YP
+agency/SM
+agenda/MS
+agent/AMS
+agented
+agenting
+agentive
+ageratum/M
+Aggie/M
+Aggi/M
+agglomerate/XNGVDS
+agglomeration/M
+agglutinate/VNGXSD
+agglutination/M
+agglutinin/MS
+aggrandize/LDSG
+aggrandizement/SM
+aggravate/SDNGX
+aggravating/Y
+aggravation/M
+aggregated/U
+aggregate/EGNVD
+aggregately
+aggregateness/M
+aggregates
+aggregation/SM
+aggregative/Y
+aggression/SM
+aggressively
+aggressiveness/S
+aggressive/U
+aggressor/MS
+aggrieved/Y
+aggrieve/GDS
+Aggy/SM
+aghast
+agile/YTR
+agility/MS
+agitated/Y
+agitate/XVNGSD
+agitation/M
+agitator/SM
+agitprop/MS
+Aglaia/M
+agleam
+aglitter
+aglow
+Ag/M
+Agna/M
+Agnella/M
+Agnese/M
+Agnes/M
+Agnesse/M
+Agneta/M
+Agnew/M
+Agni/M
+Agnola/M
+agnosticism/MS
+agnostic/SM
+ago
+agog
+agonizedly/S
+agonized/Y
+agonize/ZGRSD
+agonizing/Y
+agony/SM
+agoraphobia/MS
+agoraphobic/S
+Agosto/M
+Agra/M
+agrarianism/MS
+agrarian/S
+agreeable/EP
+agreeableness/SME
+agreeably/E
+agreeing/E
+agree/LEBDS
+agreement/ESM
+agreer/S
+Agretha/M
+agribusiness/SM
+Agricola/M
+agriculturalist/S
+agricultural/Y
+agriculture/MS
+agriculturist/SM
+Agrippa/M
+Agrippina/M
+agrochemicals
+agronomic/S
+agronomist/SM
+agronomy/MS
+aground
+Aguascalientes/M
+ague/MS
+Aguie/M
+Aguilar/M
+Aguinaldo/M
+Aguirre/M
+Aguistin/M
+Aguste/M
+Agustin/M
+ah
+Ahab/M
+Aharon/M
+aha/S
+ahead
+ahem/S
+Ahmadabad
+Ahmad/M
+Ahmed/M
+ahoy/S
+Ahriman/M
+AI
+Aida/M
+Aidan/M
+aided/U
+aide/MS
+aider/M
+AIDS
+aid/ZGDRS
+Aigneis/M
+aigrette/SM
+Aiken/M
+Aila/M
+Ailbert/M
+Ailee/M
+Aileen/M
+Aile/M
+Ailene/M
+aileron/MS
+Ailey/M
+Ailina/M
+Aili/SM
+ail/LSDG
+ailment/SM
+Ailsun/M
+Ailyn/M
+Aimee/M
+Aime/M
+aimer/M
+Aimil/M
+aimlessness/MS
+aimless/YP
+aim/ZSGDR
+Aindrea/M
+Ainslee/M
+Ainsley/M
+Ainslie/M
+ain't
+Ainu/M
+airbag/MS
+airbase/S
+airborne
+airbrush/SDMG
+Airbus/M
+airbus/SM
+aircraft/MS
+aircrew/M
+airdrop/MS
+airdropped
+airdropping
+Airedale/SM
+Aires
+airfare/S
+airfield/MS
+airflow/SM
+airfoil/MS
+airframe/MS
+airfreight/SGD
+airhead/MS
+airily
+airiness/MS
+airing/M
+airlessness/S
+airless/P
+airlift/MDSG
+airliner/M
+airline/SRMZ
+airlock/MS
+airmail/DSG
+airman/M
+airmass
+air/MDRTZGJS
+airmen
+airpark
+airplane/SM
+airplay/S
+airport/MS
+airship/MS
+airsickness/SM
+airsick/P
+airspace/SM
+airspeed/SM
+airstrip/MS
+airtightness/M
+airtight/P
+airtime
+airwaves
+airway/SM
+airworthiness/SM
+airworthy/PTR
+airy/PRT
+Aisha/M
+aisle/DSGM
+aitch/MS
+ajar
+Ajax/M
+Ajay/M
+AK
+aka
+Akbar/M
+Akihito/M
+akimbo
+Akim/M
+akin
+Akita/M
+Akkad/M
+Akron/M
+Aksel/M
+AL
+Alabama/M
+Alabaman/S
+Alabamian/MS
+alabaster/MS
+alack/S
+alacrity/SM
+Aladdin/M
+Alaine/M
+Alain/M
+Alair/M
+Alameda/M
+Alamogordo/M
+Alamo/SM
+ala/MS
+Ala/MS
+Alanah/M
+Alana/M
+Aland/M
+Alane/M
+alanine/M
+Alan/M
+Alanna/M
+Alano/M
+Alanson/M
+Alard/M
+Alaric/M
+Alar/M
+alarming/Y
+alarmist/MS
+alarm/SDG
+Alasdair/M
+Alaska/M
+Alaskan/S
+alas/S
+Alastair/M
+Alasteir/M
+Alaster/M
+Alayne/M
+albacore/SM
+alba/M
+Alba/M
+Albania/M
+Albanian/SM
+Albany/M
+albatross/SM
+albedo/M
+Albee/M
+albeit
+Alberich/M
+Alberik/M
+Alberio/M
+Alberta/M
+Albertan/S
+Albertina/M
+Albertine/M
+Albert/M
+Alberto/M
+Albie/M
+Albigensian
+Albina/M
+albinism/SM
+albino/MS
+Albion/M
+Albireo/M
+alb/MS
+Albrecht/M
+albumen/M
+albumin/MS
+albuminous
+album/MNXS
+Albuquerque/M
+Alcatraz/M
+Alcestis/M
+alchemical
+alchemist/SM
+alchemy/MS
+Alcibiades/M
+Alcmena/M
+Alcoa/M
+alcoholically
+alcoholic/MS
+alcoholism/SM
+alcohol/MS
+Alcott/M
+alcove/MSD
+Alcuin/M
+Alcyone/M
+Aldan/M
+Aldebaran/M
+aldehyde/M
+Alden/M
+Alderamin/M
+alderman/M
+aldermen
+alder/SM
+alderwoman
+alderwomen
+Aldin/M
+Aldis/M
+Aldo/M
+Aldon/M
+Aldous/M
+Aldrich/M
+Aldric/M
+Aldridge/M
+Aldrin/M
+Aldus/M
+Aldwin/M
+aleatory
+Alecia/M
+Aleck/M
+Alec/M
+Aleda/M
+alee
+Aleece/M
+Aleen/M
+alehouse/MS
+Aleichem/M
+Alejandra/M
+Alejandrina/M
+Alejandro/M
+Alejoa/M
+Aleksandr/M
+Alembert/M
+alembic/SM
+ale/MVS
+Alena/M
+Alene/M
+aleph/M
+Aleppo/M
+Aler/M
+alerted/Y
+alertness/MS
+alert/STZGPRDY
+Alessandra/M
+Alessandro/M
+Aleta/M
+Alethea/M
+Aleutian/S
+Aleut/SM
+alewife/M
+alewives
+Alexa/M
+Alexander/SM
+Alexandra/M
+Alexandre/M
+Alexandria/M
+Alexandrian/S
+Alexandrina/M
+Alexandr/M
+Alexandro/MS
+Alexei/M
+Alexia/M
+Alexina/M
+Alexine/M
+Alexio/M
+Alexi/SM
+Alex/M
+alfalfa/MS
+Alfa/M
+Alfie/M
+Alfi/M
+Alf/M
+Alfonse/M
+Alfons/M
+Alfonso/M
+Alfonzo/M
+Alford/M
+Alfreda/M
+Alfred/M
+Alfredo/M
+alfresco
+Alfy/M
+algae
+algaecide
+algal
+alga/M
+algebraic
+algebraical/Y
+algebraist/M
+algebra/MS
+Algenib/M
+Algeria/M
+Algerian/MS
+Alger/M
+Algernon/M
+Algieba/M
+Algiers/M
+alginate/SM
+ALGOL
+Algol/M
+Algonquian/SM
+Algonquin/SM
+algorithmic
+algorithmically
+algorithm/MS
+Alhambra/M
+Alhena/M
+Alia/M
+alias/GSD
+alibi/MDSG
+Alica/M
+Alicea/M
+Alice/M
+Alicia/M
+Alick/M
+Alic/M
+Alida/M
+Alidia/M
+Alie/M
+alienable/IU
+alienate/SDNGX
+alienation/M
+alienist/MS
+alien/RDGMBS
+Alighieri/M
+alight/DSG
+aligned/U
+aligner/SM
+align/LASDG
+alignment/SAM
+Alika/M
+Alikee/M
+alikeness/M
+alike/U
+alimentary
+aliment/SDMG
+alimony/MS
+Ali/MS
+Alina/M
+Aline/M
+alinement's
+Alioth/M
+aliquot/S
+Alisa/M
+Alisander/M
+Alisha/M
+Alison/M
+Alissa/M
+Alistair/M
+Alister/M
+Alisun/M
+aliveness/MS
+alive/P
+Alix/M
+aliyah/M
+aliyahs
+Aliza/M
+Alkaid/M
+alkalies
+alkali/M
+alkaline
+alkalinity/MS
+alkalize/SDG
+alkaloid/MS
+alkyd/S
+alkyl/M
+Allahabad/M
+Allah/M
+Alla/M
+Allan/M
+Allard/M
+allay/GDS
+Allayne/M
+Alleen/M
+allegation/SM
+alleged/Y
+allege/SDG
+Allegheny/MS
+allegiance/SM
+allegiant
+allegoric
+allegoricalness/M
+allegorical/YP
+allegorist/MS
+allegory/SM
+Allegra/M
+allegretto/MS
+allegri
+allegro/MS
+allele/SM
+alleluia/S
+allemande/M
+Allendale/M
+Allende/M
+Allene/M
+Allen/M
+Allentown/M
+allergenic
+allergen/MS
+allergic
+allergically
+allergist/MS
+allergy/MS
+alleviate/SDVGNX
+alleviation/M
+alleviator/MS
+Alley/M
+alley/MS
+Alleyn/M
+alleyway/MS
+Allhallows
+alliance/MS
+Allianora/M
+Allie/M
+allier
+allies/M
+alligator/DMGS
+Alli/MS
+Allina/M
+Allin/M
+Allison/M
+Allissa/M
+Allister/M
+Allistir/M
+alliterate/XVNGSD
+alliteration/M
+alliterative/Y
+Allix/M
+allocable/U
+allocatable
+allocate/ACSDNGX
+allocated/U
+allocation/AMC
+allocative
+allocator/AMS
+allophone/MS
+allophonic
+allotment/MS
+allotments/A
+allotrope/M
+allotropic
+allots/A
+allot/SDL
+allotted/A
+allotter/M
+allotting/A
+allover/S
+allowableness/M
+allowable/P
+allowably
+allowance/GSDM
+allowed/Y
+allowing/E
+allow/SBGD
+allows/E
+alloyed/U
+alloy/SGMD
+all/S
+allspice/MS
+Allstate/M
+Allsun/M
+allude/GSD
+allure/GLSD
+allurement/SM
+alluring/Y
+allusion/MS
+allusiveness/MS
+allusive/PY
+alluvial/S
+alluvions
+alluvium/MS
+Allx/M
+ally/ASDG
+Allyce/M
+Ally/MS
+Allyn/M
+Allys
+Allyson/M
+alma
+Almach/M
+Almaden/M
+almagest
+Alma/M
+almanac/MS
+Almaty/M
+Almeda/M
+Almeria/M
+Almeta/M
+almightiness/M
+Almighty/M
+almighty/P
+Almira/M
+Almire/M
+almond/SM
+almoner/MS
+almost
+Al/MRY
+alms/A
+almshouse/SM
+almsman/M
+alnico
+Alnilam/M
+Alnitak/M
+aloe/MS
+aloft
+aloha/SM
+Aloin/M
+Aloise/M
+Aloisia/M
+aloneness/M
+alone/P
+along
+alongshore
+alongside
+Alon/M
+Alonso/M
+Alonzo/M
+aloofness/MS
+aloof/YP
+aloud
+Aloysia/M
+Aloysius/M
+alpaca/SM
+Alpert/M
+alphabetical/Y
+alphabetic/S
+alphabetization/SM
+alphabetizer/M
+alphabetize/SRDGZ
+alphabet/SGDM
+alpha/MS
+alphanumerical/Y
+alphanumeric/S
+Alphard/M
+Alphecca/M
+Alpheratz/M
+Alphonse/M
+Alphonso/M
+Alpine
+alpine/S
+alp/MS
+Alps
+already
+Alric/M
+alright
+Alsace/M
+Alsatian/MS
+also
+Alsop/M
+Alston/M
+Altaic/M
+Altai/M
+Altair/M
+Alta/M
+altar/MS
+altarpiece/SM
+alterable/UI
+alteration/MS
+altercate/NX
+altercation/M
+altered/U
+alternate/SDVGNYX
+alternation/M
+alternativeness/M
+alternative/YMSP
+alternator/MS
+alter/RDZBG
+Althea/M
+although
+altimeter/SM
+Altiplano/M
+altitude/SM
+altogether/S
+Alton/M
+alto/SM
+Altos/M
+altruism/SM
+altruistic
+altruistically
+altruist/SM
+alt/RZS
+ALU
+Aludra/M
+Aluin/M
+Aluino/M
+alumina/SM
+aluminum/MS
+alumnae
+alumna/M
+alumni
+alumnus/MS
+alum/SM
+alundum
+Alva/M
+Alvan/M
+Alvarado/M
+Alvarez/M
+Alvaro/M
+alveolar/Y
+alveoli
+alveolus/M
+Alvera/M
+Alverta/M
+Alvie/M
+Alvina/M
+Alvinia/M
+Alvin/M
+Alvira/M
+Alvis/M
+Alvy/M
+alway/S
+Alwin/M
+Alwyn/M
+Alyce/M
+Alyda/M
+Alyosha/M
+Alysa/M
+Alyse/M
+Alysia/M
+Alys/M
+Alyson/M
+Alyss
+Alyssa/M
+Alzheimer/M
+AM
+AMA
+Amabelle/M
+Amabel/M
+Amadeus/M
+Amado/M
+amain
+Amalea/M
+Amalee/M
+Amaleta/M
+amalgamate/VNGXSD
+amalgamation/M
+amalgam/MS
+Amalia/M
+Amalie/M
+Amalita/M
+Amalle/M
+Amanda/M
+Amandie/M
+Amandi/M
+Amandy/M
+amanuenses
+amanuensis/M
+Amara/M
+amaranth/M
+amaranths
+amaretto/S
+Amargo/M
+Amarillo/M
+amaryllis/MS
+am/AS
+amasser/M
+amass/GRSD
+Amata/M
+amateurishness/MS
+amateurish/YP
+amateurism/MS
+amateur/SM
+Amati/M
+amatory
+amazed/Y
+amaze/LDSRGZ
+amazement/MS
+amazing/Y
+amazonian
+Amazonian
+amazon/MS
+Amazon/SM
+ambassadorial
+ambassador/MS
+ambassadorship/MS
+ambassadress/SM
+ambergris/SM
+Amberly/M
+amber/MS
+Amber/YM
+ambiance/MS
+ambidexterity/MS
+ambidextrous/Y
+ambience's
+ambient/S
+ambiguity/MS
+ambiguously/U
+ambiguousness/M
+ambiguous/YP
+ambition/GMDS
+ambitiousness/MS
+ambitious/PY
+ambit/M
+ambivalence/SM
+ambivalent/Y
+amble/GZDSR
+Amble/M
+ambler/M
+ambrose
+Ambrose/M
+ambrosial/Y
+ambrosia/SM
+Ambrosi/M
+Ambrosio/M
+Ambrosius/M
+Ambros/M
+ambulance/MS
+ambulant/S
+ambulate/DSNGX
+ambulation/M
+ambulatory/S
+Ambur/M
+ambuscade/MGSRD
+ambuscader/M
+ambusher/M
+ambush/MZRSDG
+Amby/M
+Amdahl/M
+ameba's
+Amelia/M
+Amelie/M
+Amelina/M
+Ameline/M
+ameliorate/XVGNSD
+amelioration/M
+Amelita/M
+amenability/SM
+amenably
+amended/U
+amender/M
+amendment/SM
+amen/DRGTSB
+amend/SBRDGL
+amends/M
+Amenhotep/M
+amenity/MS
+amenorrhea/M
+Amerada/M
+Amerasian/S
+amercement/MS
+amerce/SDLG
+Americana/M
+Americanism/SM
+Americanization/SM
+americanized
+Americanize/SDG
+American/MS
+America/SM
+americium/MS
+Amerigo/M
+Amerindian/MS
+Amerind/MS
+Amer/M
+Amery/M
+Ameslan/M
+Ame/SM
+amethystine
+amethyst/MS
+Amharic/M
+Amherst/M
+amiability/MS
+amiableness/M
+amiable/RPT
+amiably
+amicability/SM
+amicableness/M
+amicable/P
+amicably
+amide/SM
+amid/S
+amidships
+amidst
+Amie/M
+Amiga/M
+amigo/MS
+Amii/M
+Amil/M
+Ami/M
+amines
+aminobenzoic
+amino/M
+amir's
+Amish
+amiss
+Amitie/M
+Amity/M
+amity/SM
+Ammamaria/M
+Amman/M
+Ammerman/M
+ammeter/MS
+ammo/MS
+ammoniac
+ammonia/MS
+ammonium/M
+Am/MR
+ammunition/MS
+amnesiac/MS
+amnesia/SM
+amnesic/S
+amnesty/GMSD
+amniocenteses
+amniocentesis/M
+amnion/SM
+amniotic
+Amoco/M
+amoeba/SM
+amoebic
+amoeboid
+amok/MS
+among
+amongst
+Amontillado/M
+amontillado/MS
+amorality/MS
+amoral/Y
+amorousness/SM
+amorous/PY
+amorphousness/MS
+amorphous/PY
+amortization/SUM
+amortized/U
+amortize/SDG
+Amory/M
+Amos
+amount/SMRDZG
+amour/MS
+Amparo/M
+amperage/SM
+Ampere/M
+ampere/MS
+ampersand/MS
+Ampex/M
+amphetamine/MS
+amphibian/SM
+amphibiousness/M
+amphibious/PY
+amphibology/M
+amphitheater/SM
+amphorae
+amphora/M
+ampleness/M
+ample/PTR
+amplification/M
+amplifier/M
+amplify/DRSXGNZ
+amplitude/MS
+ampoule's
+amp/SGMDY
+ampule/SM
+amputate/DSNGX
+amputation/M
+amputee/SM
+Amritsar/M
+ams
+Amsterdam/M
+amt
+Amtrak/M
+amuck's
+amulet/SM
+Amundsen/M
+Amur/M
+amused/Y
+amuse/LDSRGVZ
+amusement/SM
+amuser/M
+amusingness/M
+amusing/YP
+Amway/M
+Amye/M
+amylase/MS
+amyl/M
+Amy/M
+Anabal/M
+Anabaptist/SM
+Anabella/M
+Anabelle/M
+Anabel/M
+anabolic
+anabolism/MS
+anachronism/SM
+anachronistic
+anachronistically
+Anacin/M
+anaconda/MS
+Anacreon/M
+anaerobe/SM
+anaerobic
+anaerobically
+anaglyph/M
+anagrammatic
+anagrammatically
+anagrammed
+anagramming
+anagram/MS
+Anaheim/M
+Analects/M
+analgesia/MS
+analgesic/S
+Analiese/M
+Analise/M
+Anallese/M
+Anallise/M
+analogical/Y
+analogize/SDG
+analogousness/MS
+analogous/YP
+analog/SM
+analogue/SM
+analogy/MS
+anal/Y
+analysand/MS
+analysis/AM
+analyst/SM
+analytical/Y
+analyticity/S
+analytic/S
+analytics/M
+analyzable/U
+analyze/DRSZGA
+analyzed/U
+analyzer/M
+Ana/M
+anamorphic
+Ananias/M
+anapaest's
+anapestic/S
+anapest/SM
+anaphora/M
+anaphoric
+anaphorically
+anaplasmosis/M
+anarchic
+anarchical/Y
+anarchism/MS
+anarchistic
+anarchist/MS
+anarchy/MS
+Anastasia/M
+Anastasie/M
+Anastassia/M
+anastigmatic
+anastomoses
+anastomosis/M
+anastomotic
+anathema/MS
+anathematize/GSD
+Anatola/M
+Anatole/M
+Anatolia/M
+Anatolian
+Anatollo/M
+Anatol/M
+anatomic
+anatomical/YS
+anatomist/MS
+anatomize/GSD
+anatomy/MS
+Anaxagoras/M
+Ancell/M
+ancestor/SMDG
+ancestral/Y
+ancestress/SM
+ancestry/SM
+Anchorage/M
+anchorage/SM
+anchored/U
+anchorite/MS
+anchoritism/M
+anchorman/M
+anchormen
+anchorpeople
+anchorperson/S
+anchor/SGDM
+anchorwoman
+anchorwomen
+anchovy/MS
+ancientness/MS
+ancient/SRYTP
+ancillary/S
+an/CS
+Andalusia/M
+Andalusian
+Andaman
+andante/S
+and/DZGS
+Andean/M
+Andeee/M
+Andee/M
+Anderea/M
+Andersen/M
+Anders/N
+Anderson/M
+Andes
+Andie/M
+Andi/M
+andiron/MS
+Andonis/M
+Andorra/M
+Andover/M
+Andra/SM
+Andrea/MS
+Andreana/M
+Andree/M
+Andrei/M
+Andrej/M
+Andre/SM
+Andrew/MS
+Andrey/M
+Andria/M
+Andriana/M
+Andriette/M
+Andris
+androgenic
+androgen/SM
+androgynous
+androgyny/SM
+android/MS
+Andromache/M
+Andromeda/M
+Andropov/M
+Andros/M
+Andrus/M
+Andy/M
+anecdotal/Y
+anecdote/SM
+anechoic
+anemia/SM
+anemically
+anemic/S
+anemometer/MS
+anemometry/M
+anemone/SM
+anent
+aneroid
+Anestassia/M
+anesthesia/MS
+anesthesiologist/MS
+anesthesiology/SM
+anesthetically
+anesthetic/SM
+anesthetist/MS
+anesthetization/SM
+anesthetizer/M
+anesthetize/ZSRDG
+Anet/M
+Anetta/M
+Anette/M
+Anett/M
+aneurysm/MS
+anew
+Angara/M
+Angela/M
+Angeleno/SM
+Angele/SM
+angelfish/SM
+Angelia/M
+angelic
+angelical/Y
+Angelica/M
+angelica/MS
+Angelico/M
+Angelika/M
+Angeli/M
+Angelina/M
+Angeline/M
+Angelique/M
+Angelita/M
+Angelle/M
+Angel/M
+angel/MDSG
+Angelo/M
+Angelou/M
+Ange/M
+anger/GDMS
+Angevin/M
+Angie/M
+Angil/M
+angina/MS
+angiography
+angioplasty/S
+angiosperm/MS
+Angkor/M
+angle/GMZDSRJ
+angler/M
+Angles
+angleworm/MS
+Anglia/M
+Anglicanism/MS
+Anglican/MS
+Anglicism/SM
+Anglicization/MS
+anglicize/SDG
+Anglicize/SDG
+angling/M
+Anglo/MS
+Anglophile/SM
+Anglophilia/M
+Anglophobe/MS
+Anglophobia/M
+Angola/M
+Angolan/S
+angora/MS
+Angora/MS
+angrily
+angriness/M
+angry/RTP
+angst/MS
+Ångström/M
+angstrom/MS
+Anguilla/M
+anguish/DSMG
+angularity/MS
+angular/Y
+Angus/M
+Angy/M
+Anheuser/M
+anhydride/M
+anhydrite/M
+anhydrous/Y
+Aniakchak/M
+Ania/M
+Anibal/M
+Anica/M
+aniline/SM
+animadversion/SM
+animadvert/DSG
+animalcule/MS
+animal/MYPS
+animated/A
+animatedly
+animately/I
+animateness/MI
+animates/A
+animate/YNGXDSP
+animating/A
+animation/AMS
+animator/SM
+animism/SM
+animistic
+animist/S
+animized
+animosity/MS
+animus/SM
+anionic/S
+anion/MS
+aniseed/MS
+aniseikonic
+anise/MS
+anisette/SM
+anisotropic
+anisotropy/MS
+Anissa/M
+Anita/M
+Anitra/M
+Anjanette/M
+Anjela/M
+Ankara/M
+ankh/M
+ankhs
+anklebone/SM
+ankle/GMDS
+anklet/MS
+Annabal/M
+Annabela/M
+Annabella/M
+Annabelle/M
+Annabell/M
+Annabel/M
+Annadiana/M
+Annadiane/M
+Annalee/M
+Annaliese/M
+Annalise/M
+annalist/MS
+annal/MNS
+Anna/M
+Annamaria/M
+Annamarie/M
+Annapolis/M
+Annapurna/M
+anneal/DRSZG
+annealer/M
+Annecorinne/M
+annelid/MS
+Anneliese/M
+Annelise/M
+Anne/M
+Annemarie/M
+Annetta/M
+Annette/M
+annexation/SM
+annexe/M
+annex/GSD
+Annice/M
+Annie/M
+annihilate/XSDVGN
+annihilation/M
+annihilator/MS
+Anni/MS
+Annissa/M
+anniversary/MS
+Ann/M
+Annmaria/M
+Annmarie/M
+Annnora/M
+Annora/M
+annotated/U
+annotate/VNGXSD
+annotation/M
+annotator/MS
+announced/U
+announcement/SM
+announcer/M
+announce/ZGLRSD
+annoyance/MS
+annoyer/M
+annoying/Y
+annoy/ZGSRD
+annualized
+annual/YS
+annuitant/MS
+annuity/MS
+annular/YS
+annuli
+annulled
+annulling
+annulment/MS
+annul/SL
+annulus/M
+annum
+annunciate/XNGSD
+annunciation/M
+Annunciation/S
+annunciator/SM
+Anny/M
+anode/SM
+anodic
+anodize/GDS
+anodyne/SM
+anoint/DRLGS
+anointer/M
+anointment/SM
+anomalousness/M
+anomalous/YP
+anomaly/MS
+anomic
+anomie/M
+anon/S
+anonymity/MS
+anonymousness/M
+anonymous/YP
+anopheles/M
+anorak/SM
+anorectic/S
+anorexia/SM
+anorexic/S
+another/M
+Anouilh/M
+Ansell/M
+Ansel/M
+Anselma/M
+Anselm/M
+Anselmo/M
+Anshan/M
+ANSI/M
+Ansley/M
+ans/M
+Anson/M
+Anstice/M
+answerable/U
+answered/U
+answerer/M
+answer/MZGBSDR
+antacid/MS
+Antaeus/M
+antagonism/MS
+antagonistic
+antagonistically
+antagonist/MS
+antagonized/U
+antagonize/GZRSD
+antagonizing/U
+Antananarivo/M
+antarctic
+Antarctica/M
+Antarctic/M
+Antares
+anteater/MS
+antebellum
+antecedence/MS
+antecedent/SMY
+antechamber/SM
+antedate/GDS
+antediluvian/S
+anteing
+antelope/MS
+ante/MS
+antenatal
+antennae
+antenna/MS
+anterior/SY
+anteroom/SM
+ant/GSMD
+Anthea/M
+Anthe/M
+anthem/MGDS
+anther/MS
+Anthia/M
+Anthiathia/M
+anthill/S
+anthologist/MS
+anthologize/GDS
+anthology/SM
+Anthony/M
+anthraces
+anthracite/MS
+anthrax/M
+anthropic
+anthropocentric
+anthropogenic
+anthropoid/S
+anthropological/Y
+anthropologist/MS
+anthropology/SM
+anthropometric/S
+anthropometry/M
+anthropomorphic
+anthropomorphically
+anthropomorphism/SM
+anthropomorphizing
+anthropomorphous
+antiabortion
+antiabortionist/S
+antiaircraft
+antibacterial/S
+antibiotic/SM
+antibody/MS
+anticancer
+Antichrist/MS
+anticipated/U
+anticipate/XVGNSD
+anticipation/M
+anticipative/Y
+anticipatory
+anticked
+anticking
+anticlerical/S
+anticlimactic
+anticlimactically
+anticlimax/SM
+anticline/SM
+anticlockwise
+antic/MS
+anticoagulant/S
+anticoagulation/M
+anticommunism/SM
+anticommunist/SM
+anticompetitive
+anticyclone/MS
+anticyclonic
+antidemocratic
+antidepressant/SM
+antidisestablishmentarianism/M
+antidote/DSMG
+Antietam/M
+antifascist/SM
+antiformant
+antifreeze/SM
+antifundamentalist/M
+antigenic
+antigenicity/SM
+antigen/MS
+antigone
+Antigone/M
+Antigua/M
+antiheroes
+antihero/M
+antihistamine/MS
+antihistorical
+antiknock/MS
+antilabor
+Antillean
+Antilles
+antilogarithm/SM
+antilogs
+antimacassar/SM
+antimalarial/S
+antimatter/SM
+antimicrobial/S
+antimissile/S
+antimony/SM
+anting/M
+Antin/M
+antinomian
+antinomy/M
+antinuclear
+Antioch/M
+antioxidant/MS
+antiparticle/SM
+Antipas/M
+antipasti
+antipasto/MS
+antipathetic
+antipathy/SM
+antipersonnel
+antiperspirant/MS
+antiphonal/SY
+antiphon/SM
+antipodal/S
+antipodean/S
+antipode/MS
+Antipodes
+antipollution/S
+antipoverty
+antiquarianism/MS
+antiquarian/MS
+antiquary/SM
+antiquate/NGSD
+antiquation/M
+antique/MGDS
+antiquity/SM
+antiredeposition
+antiresonance/M
+antiresonator
+anti/S
+antisemitic
+antisemitism/M
+antisepses
+antisepsis/M
+antiseptically
+antiseptic/S
+antiserum/SM
+antislavery/S
+antisocial/Y
+antispasmodic/S
+antisubmarine
+antisymmetric
+antisymmetry
+antitank
+antitheses
+antithesis/M
+antithetic
+antithetical/Y
+antithyroid
+antitoxin/MS
+antitrust/MR
+antivenin/MS
+antiviral/S
+antivivisectionist/S
+antiwar
+antler/SDM
+Antofagasta/M
+Antoine/M
+Antoinette/M
+Antonella/M
+Antone/M
+Antonetta/M
+Antonia/M
+Antonie/M
+Antonietta/M
+Antoni/M
+Antonina/M
+Antonin/M
+Antonino/M
+Antoninus/M
+Antonio/M
+Antonius/M
+Anton/MS
+Antonovics/M
+Antony/M
+antonymous
+antonym/SM
+antral
+antsy/RT
+Antwan/M
+Antwerp/M
+Anubis/M
+anus/SM
+anvil/MDSG
+anxiety/MS
+anxiousness/SM
+anxious/PY
+any
+Anya/M
+anybody/S
+anyhow
+Any/M
+anymore
+anyone/MS
+anyplace
+anything/S
+anytime
+anyway/S
+anywhere/S
+anywise
+AOL/M
+aorta/MS
+aortic
+AP
+apace
+apache/MS
+Apache/MS
+Apalachicola/M
+apartheid/SM
+apart/LP
+apartment/MS
+apartness/M
+apathetic
+apathetically
+apathy/SM
+apatite/MS
+APB
+aped/A
+apelike
+ape/MDRSG
+Apennines
+aper/A
+aperiodic
+aperiodically
+aperiodicity/M
+aperitif/S
+aperture/MDS
+apex/MS
+aphasia/SM
+aphasic/S
+aphelia
+aphelion/SM
+aphid/MS
+aphonic
+aphorism/MS
+aphoristic
+aphoristically
+aphrodisiac/SM
+Aphrodite/M
+Apia/M
+apiarist/SM
+apiary/SM
+apical/YS
+apices's
+apiece
+apishness/M
+apish/YP
+aplenty
+aplomb/SM
+APO
+Apocalypse/M
+apocalypse/MS
+apocalyptic
+apocryphalness/M
+apocryphal/YP
+apocrypha/M
+Apocrypha/M
+apogee/MS
+apolar
+apolitical/Y
+Apollinaire/M
+Apollonian
+Apollo/SM
+apologetically/U
+apologetic/S
+apologetics/M
+apologia/SM
+apologist/MS
+apologize/GZSRD
+apologizer/M
+apologizes/A
+apologizing/U
+apology/MS
+apoplectic
+apoplexy/SM
+apostasy/SM
+apostate/SM
+apostatize/DSG
+apostleship/SM
+apostle/SM
+apostolic
+apostrophe/SM
+apostrophized
+apothecary/MS
+apothegm/MS
+apotheoses
+apotheosis/M
+apotheosized
+apotheosizes
+apotheosizing
+Appalachia/M
+Appalachian/MS
+appalling/Y
+appall/SDG
+Appaloosa/MS
+appaloosa/S
+appanage/M
+apparatus/SM
+apparel/SGMD
+apparency
+apparently/I
+apparentness/M
+apparent/U
+apparition/SM
+appealer/M
+appealing/UY
+appeal/SGMDRZ
+appear/AEGDS
+appearance/AMES
+appearer/S
+appease/DSRGZL
+appeased/U
+appeasement/MS
+appeaser/M
+appellant/MS
+appellate/VNX
+appellation/M
+appellative/MY
+appendage/MS
+appendectomy/SM
+appendices
+appendicitis/SM
+appendix/SM
+append/SGZDR
+appertain/DSG
+appetite/MVS
+appetizer/SM
+appetizing/YU
+Appia/M
+Appian/M
+applauder/M
+applaud/ZGSDR
+applause/MS
+applecart/M
+applejack/MS
+Apple/M
+apple/MS
+applesauce/SM
+Appleseed/M
+Appleton/M
+applet/S
+appliance/SM
+applicabilities
+applicability/IM
+applicable/I
+applicably
+applicant/MS
+applicate/V
+application/MA
+applicative/Y
+applicator/MS
+applier/SM
+appliquéd
+appliqué/MSG
+apply/AGSDXN
+appointee/SM
+appoint/ELSADG
+appointer/MS
+appointive
+appointment/ASEM
+Appolonia/M
+Appomattox/M
+apportion/GADLS
+apportionment/SAM
+appose/SDG
+appositeness/MS
+apposite/XYNVP
+apposition/M
+appositive/SY
+appraisal/SAM
+appraised/A
+appraisees
+appraiser/M
+appraises/A
+appraise/ZGDRS
+appraising/Y
+appreciable/I
+appreciably/I
+appreciated/U
+appreciate/XDSNGV
+appreciation/M
+appreciativeness/MI
+appreciative/PIY
+appreciator/MS
+appreciatory
+apprehend/DRSG
+apprehender/M
+apprehensible
+apprehension/SM
+apprehensiveness/SM
+apprehensive/YP
+apprentice/DSGM
+apprenticeship/SM
+apprise/DSG
+apprizer/SM
+apprizingly
+apprizings
+approachability/UM
+approachable/UI
+approach/BRSDZG
+approacher/M
+approbate/NX
+approbation/EMS
+appropriable
+appropriated/U
+appropriately/I
+appropriateness/SMI
+appropriate/XDSGNVYTP
+appropriation/M
+appropriator/SM
+approval/ESM
+approve/DSREG
+approved/U
+approver's/E
+approver/SM
+approving/YE
+approx
+approximate/XGNVYDS
+approximation/M
+approximative/Y
+appurtenance/MS
+appurtenant/S
+APR
+apricot/MS
+Aprilette/M
+April/MS
+Apr/M
+apron/SDMG
+apropos
+apse/MS
+apsis/M
+apter
+aptest
+aptitude/SM
+aptness/SMI
+aptness's/U
+apt/UPYI
+Apuleius/M
+aquaculture/MS
+aqualung/SM
+aquamarine/SM
+aquanaut/SM
+aquaplane/GSDM
+aquarium/MS
+Aquarius/MS
+aqua/SM
+aquatically
+aquatic/S
+aquavit/SM
+aqueduct/MS
+aqueous/Y
+aquiculture's
+aquifer/SM
+Aquila/M
+aquiline
+Aquinas/M
+Aquino/M
+Aquitaine/M
+AR
+Arabela/M
+Arabele/M
+Arabella/M
+Arabelle/M
+Arabel/M
+arabesque/SM
+Arabia/M
+Arabian/MS
+Arabic/M
+arability/MS
+Arabist/MS
+arable/S
+Arab/MS
+Araby/M
+Araceli/M
+arachnid/MS
+arachnoid/M
+arachnophobia
+Arafat/M
+Araguaya/M
+Araldo/M
+Aral/M
+Ara/M
+Aramaic/M
+Aramco/M
+Arapahoes
+Arapahoe's
+Arapaho/MS
+Ararat/M
+Araucanian/M
+Arawakan/M
+Arawak/M
+arbiter/MS
+arbitrage/GMZRSD
+arbitrager/M
+arbitrageur/S
+arbitrament/MS
+arbitrarily
+arbitrariness/MS
+arbitrary/P
+arbitrate/SDXVNG
+arbitration/M
+arbitrator/SM
+arbor/DMS
+arboreal/Y
+arbores
+arboretum/MS
+arborvitae/MS
+arbutus/SM
+ARC
+arcade/SDMG
+Arcadia/M
+Arcadian
+arcana/M
+arcane/P
+arc/DSGM
+archaeological/Y
+archaeologist/SM
+archaically
+archaic/P
+Archaimbaud/M
+archaism/SM
+archaist/MS
+archaize/GDRSZ
+archaizer/M
+Archambault/M
+archangel/SM
+archbishopric/SM
+archbishop/SM
+archdeacon/MS
+archdiocesan
+archdiocese/SM
+archduchess/MS
+archduke/MS
+Archean
+archenemy/SM
+archeologist's
+archeology/MS
+archer/M
+Archer/M
+archery/MS
+archetypal
+archetype/SM
+archfiend/SM
+archfool
+Archibald/M
+Archibaldo/M
+Archibold/M
+Archie/M
+archiepiscopal
+Archimedes/M
+arching/M
+archipelago/SM
+architect/MS
+architectonic/S
+architectonics/M
+architectural/Y
+architecture/SM
+architrave/MS
+archival
+archive/DRSGMZ
+archived/U
+archivist/MS
+Arch/MR
+archness/MS
+arch/PGVZTMYDSR
+archway/SM
+Archy/M
+arclike
+ARCO/M
+arcsine
+arctangent
+Arctic/M
+arctic/S
+Arcturus/M
+Ardabil
+Arda/MH
+Ardath/M
+Ardeen/M
+Ardelia/M
+Ardelis/M
+Ardella/M
+Ardelle/M
+ardency/M
+Ardene/M
+Ardenia/M
+Arden/M
+ardent/Y
+Ardine/M
+Ardisj/M
+Ardis/M
+Ardith/M
+ardor/SM
+Ardra/M
+arduousness/SM
+arduous/YP
+Ardyce/M
+Ardys
+Ardyth/M
+areal
+area/SM
+areawide
+are/BS
+Arel/M
+arenaceous
+arena/SM
+aren't
+Arequipa/M
+Ares
+Aretha/M
+Argentina/M
+Argentinean/S
+Argentine/SM
+Argentinian/S
+argent/MS
+Argonaut/MS
+argonaut/S
+argon/MS
+Argonne/M
+Argo/SM
+argosy/SM
+argot/SM
+arguable/IU
+arguably/IU
+argue/DSRGZ
+arguer/M
+argumentation/SM
+argumentativeness/MS
+argumentative/YP
+argument/SM
+Argus/M
+argyle/S
+Ariadne/M
+Ariana/M
+Arianism/M
+Arianist/SM
+aria/SM
+Aridatha/M
+aridity/SM
+aridness/M
+arid/TYRP
+Ariela/M
+Ariella/M
+Arielle/M
+Ariel/M
+Arie/SM
+Aries/S
+aright
+Ari/M
+Arin/M
+Ario/M
+Ariosto/M
+arise/GJSR
+arisen
+Aristarchus/M
+Aristides
+aristocracy/SM
+aristocratic
+aristocratically
+aristocrat/MS
+Aristophanes/M
+Aristotelean
+Aristotelian/M
+Aristotle/M
+arithmetical/Y
+arithmetician/SM
+arithmetic/MS
+arithmetize/SD
+Arius/M
+Ariz/M
+Arizona/M
+Arizonan/S
+Arizonian/S
+Arjuna/M
+Arkansan/MS
+Arkansas/M
+Arkhangelsk/M
+Ark/M
+ark/MS
+Arkwright/M
+Arlana/M
+Arlan/M
+Arlee/M
+Arleen/M
+Arlena/M
+Arlene/M
+Arlen/M
+Arleta/M
+Arlette/M
+Arley/M
+Arleyne/M
+Arlie/M
+Arliene/M
+Arlina/M
+Arlinda/M
+Arline/M
+Arlington/M
+Arlin/M
+Arluene/M
+Arly/M
+Arlyne/M
+Arlyn/M
+Armada/M
+armada/SM
+armadillo/MS
+Armageddon/SM
+Armagnac/M
+armament/EAS
+armament's/E
+Armand/M
+Armando/M
+Arman/M
+arm/ASEDG
+Armata/M
+armature/MGSD
+armband/SM
+armchair/MS
+Armco/M
+armed/U
+Armenia/M
+Armenian/MS
+armer/MES
+armful/SM
+armhole/MS
+arming/M
+Arminius/M
+Armin/M
+armistice/MS
+armless
+armlet/SM
+armload/M
+Armonk/M
+armored/U
+armorer/M
+armorial/S
+armory/DSM
+armor/ZRDMGS
+Armour/M
+armpit/MS
+armrest/MS
+arm's
+Armstrong/M
+Ar/MY
+army/SM
+Arnaldo/M
+Arneb/M
+Arne/M
+Arney/M
+Arnhem/M
+Arnie/M
+Arni/M
+Arnold/M
+Arnoldo/M
+Arno/M
+Arnuad/M
+Arnulfo/M
+Arny/M
+aroma/SM
+aromatherapist/S
+aromatherapy/S
+aromatically
+aromaticity/M
+aromaticness/M
+aromatic/SP
+Aron/M
+arose
+around
+arousal/MS
+aroused/U
+arouse/GSD
+ARPA/M
+Arpanet/M
+ARPANET/M
+arpeggio/SM
+arrack/M
+Arragon/M
+arraignment/MS
+arraign/SDGL
+arrangeable/A
+arranged/EA
+arrangement/AMSE
+arranger/M
+arranges/EA
+arrange/ZDSRLG
+arranging/EA
+arrant/Y
+arras/SM
+arrayer
+array/ESGMD
+arrear/SM
+arrest/ADSG
+arrestee/MS
+arrester/MS
+arresting/Y
+arrestor/MS
+Arrhenius/M
+arrhythmia/SM
+arrhythmic
+arrhythmical
+Arri/M
+arrival/MS
+arriver/M
+arrive/SRDG
+arrogance/MS
+arrogant/Y
+arrogate/XNGDS
+arrogation/M
+Arron/M
+arrowhead/SM
+arrowroot/MS
+arrow/SDMG
+arroyo/MS
+arr/TV
+arsenal/MS
+arsenate/M
+arsenic/MS
+arsenide/M
+arsine/MS
+arsonist/MS
+arson/SM
+Artair/M
+Artaxerxes/M
+artefact's
+Arte/M
+Artemas
+Artemis/M
+Artemus/M
+arterial/SY
+arteriolar
+arteriole/SM
+arterioscleroses
+arteriosclerosis/M
+artery/SM
+artesian
+artfulness/SM
+artful/YP
+Arther/M
+arthritic/S
+arthritides
+arthritis/M
+arthrogram/MS
+arthropod/SM
+arthroscope/S
+arthroscopic
+Arthurian
+Arthur/M
+artichoke/SM
+article/GMDS
+articulable/I
+articular
+articulated/EU
+articulately/I
+articulateness/IMS
+articulates/I
+articulate/VGNYXPSD
+articulation/M
+articulator/SM
+articulatory
+Artie/M
+artifact/MS
+artificer/M
+artifice/ZRSM
+artificiality/MS
+artificialness/M
+artificial/PY
+artillerist
+artilleryman/M
+artillerymen
+artillery/SM
+artiness/MS
+artisan/SM
+artiste/SM
+artistically/I
+artistic/I
+artist/MS
+artistry/SM
+artlessness/MS
+artless/YP
+Art/M
+art/SM
+artsy/RT
+Artur/M
+Arturo/M
+Artus/M
+artwork/MS
+Arty/M
+arty/TPR
+Aruba/M
+arum/MS
+Arvie/M
+Arvin/M
+Arv/M
+Arvy/M
+Aryan/MS
+Aryn/M
+as
+As
+A's
+Asa/M
+Asama/M
+asap
+ASAP
+asbestos/MS
+Ascella/M
+ascend/ADGS
+ascendancy/MS
+ascendant/SY
+ascender/SM
+Ascension/M
+ascension/SM
+ascent/SM
+ascertain/DSBLG
+ascertainment/MS
+ascetically
+asceticism/MS
+ascetic/SM
+ASCII
+ascot/MS
+ascribe/GSDB
+ascription/MS
+ascriptive
+Ase/M
+aseptically
+aseptic/S
+asexuality/MS
+asexual/Y
+Asgard/M
+ashame/D
+ashamed/UY
+Ashanti/M
+Ashbey/M
+Ashby/M
+ashcan/SM
+Ashely/M
+Asher/M
+Asheville/M
+Ashia/M
+Ashien/M
+Ashil/M
+Ashkenazim
+Ashkhabad/M
+Ashla/M
+Ashland/M
+Ashlan/M
+ashlar/GSDM
+Ashlee/M
+Ashleigh/M
+Ashlen/M
+Ashley/M
+Ashlie/M
+Ashli/M
+Ashlin/M
+Ashly/M
+ashman/M
+ash/MNDRSG
+Ashmolean/M
+Ash/MRY
+ashore
+ashram/SM
+Ashton/M
+ashtray/MS
+Ashurbanipal/M
+ashy/RT
+Asia/M
+Asian/MS
+Asiatic/SM
+aside/S
+Asilomar/M
+Asimov
+asinine/Y
+asininity/MS
+askance
+ask/DRZGS
+asked/U
+asker/M
+askew/P
+ASL
+aslant
+asleep
+Asmara/M
+asocial/S
+Asoka/M
+asparagus/MS
+aspartame/S
+ASPCA
+aspect/SM
+Aspell/M
+aspen/M
+Aspen/M
+asperity/SM
+asper/M
+aspersion/SM
+asphalt/MDRSG
+asphodel/MS
+asphyxia/MS
+asphyxiate/GNXSD
+asphyxiation/M
+aspic/MS
+Aspidiske/M
+aspidistra/MS
+aspirant/MS
+aspirate/NGDSX
+aspirational
+aspiration/M
+aspirator/SM
+aspire/GSRD
+aspirer/M
+aspirin/SM
+asplenium
+asp/MNRXS
+Asquith/M
+Assad/M
+assailable/U
+assailant/SM
+assail/BGDS
+Assamese/M
+Assam/M
+assassinate/DSGNX
+assassination/M
+assassin/MS
+assaulter/M
+assaultive/YP
+assault/SGVMDR
+assayer/M
+assay/SZGRD
+assemblage/MS
+assemble/ADSREG
+assembled/U
+assembler/EMS
+assemblies/A
+assembly/EAM
+assemblyman/M
+assemblymen
+Assembly/MS
+assemblywoman
+assemblywomen
+assent/SGMRD
+assert/ADGS
+asserter/MS
+assertional
+assertion/AMS
+assertiveness/SM
+assertive/PY
+assess/BLSDG
+assessed/A
+assesses/A
+assessment/SAM
+assessor/MS
+asset/SM
+asseverate/XSDNG
+asseveration/M
+asshole/MS
+assiduity/SM
+assiduousness/SM
+assiduous/PY
+assign/ALBSGD
+assignation/MS
+assigned/U
+assignee/MS
+assigner/MS
+assignment/MAS
+assignor/MS
+assigns/CU
+assimilate/VNGXSD
+assimilationist/M
+assimilation/M
+Assisi/M
+assistance/SM
+assistantship/SM
+assistant/SM
+assisted/U
+assister/M
+assist/RDGS
+assize/MGSD
+ass/MNS
+assn
+assoc
+associable
+associated/U
+associate/SDEXNG
+associateship
+associational
+association/ME
+associative/Y
+associativity/S
+associator/MS
+assonance/SM
+assonant/S
+assorter/M
+assort/LRDSG
+assortment/SM
+asst
+assuaged/U
+assuage/SDG
+assumability
+assumer/M
+assume/SRDBJG
+assuming/UA
+assumption/SM
+assumptive
+assurance/AMS
+assure/AGSD
+assuredness/M
+assured/PYS
+assurer/SM
+assuring/YA
+Assyria/M
+Assyrian/SM
+Assyriology/M
+Astaire/SM
+Astarte/M
+astatine/MS
+aster/ESM
+asteria
+asterisked/U
+asterisk/SGMD
+astern
+asteroidal
+asteroid/SM
+asthma/MS
+asthmatic/S
+astigmatic/S
+astigmatism/SM
+astir
+astonish/GSDL
+astonishing/Y
+astonishment/SM
+Aston/M
+Astoria/M
+Astor/M
+astounding/Y
+astound/SDG
+astraddle
+Astrakhan/M
+astrakhan/SM
+astral/SY
+Astra/M
+astray
+astride
+Astrid/M
+astringency/SM
+astringent/YS
+Astrix/M
+astrolabe/MS
+astrologer/MS
+astrological/Y
+astrologist/M
+astrology/SM
+astronautical
+astronautic/S
+astronautics/M
+astronaut/SM
+astronomer/MS
+astronomic
+astronomical/Y
+astronomy/SM
+astrophysical
+astrophysicist/SM
+astrophysics/M
+Astroturf/M
+AstroTurf/S
+Asturias/M
+astuteness/MS
+astute/RTYP
+Asunción/M
+asunder
+Aswan/M
+asylum/MS
+asymmetric
+asymmetrical/Y
+asymmetry/MS
+asymptomatic
+asymptomatically
+asymptote/MS
+asymptotically
+asymptotic/Y
+asynchronism/M
+asynchronous/Y
+asynchrony
+at
+Atacama/M
+Atahualpa/M
+Atalanta/M
+Atari/M
+Atatürk/M
+atavism/MS
+atavistic
+atavist/MS
+ataxia/MS
+ataxic/S
+atelier/SM
+atemporal
+ate/S
+Athabasca/M
+Athabascan's
+Athabaskan/MS
+Athabaska's
+atheism/SM
+atheistic
+atheist/SM
+Athena/M
+Athene/M
+Athenian/SM
+Athens/M
+atheroscleroses
+atherosclerosis/M
+athirst
+athlete/MS
+athletically
+athleticism/M
+athletic/S
+athletics/M
+athwart
+atilt
+Atkins/M
+Atkinson/M
+Atlanta/M
+Atlante/MS
+atlantes
+Atlantic/M
+Atlantis/M
+atlas/SM
+Atlas/SM
+At/M
+Atman
+ATM/M
+atmosphere/DSM
+atmospherically
+atmospheric/S
+atoll/MS
+atomically
+atomicity/M
+atomic/S
+atomics/M
+atomistic
+atomization/SM
+atomize/GZDRS
+atomizer/M
+atom/SM
+atonality/MS
+atonal/Y
+atone/LDSG
+atonement/SM
+atop
+ATP
+Atreus/M
+atria
+atrial
+Atria/M
+atrium/M
+atrociousness/SM
+atrocious/YP
+atrocity/SM
+atrophic
+atrophy/DSGM
+atropine/SM
+Atropos/M
+Ats
+attach/BLGZMDRS
+attached/UA
+attacher/M
+attaché/S
+attachment/ASM
+attacker/M
+attack/GBZSDR
+attainabilities
+attainability/UM
+attainableness/M
+attainable/U
+attainably/U
+attain/AGSD
+attainder/MS
+attained/U
+attainer/MS
+attainment/MS
+attar/MS
+attempt/ADSG
+attempter/MS
+attendance/MS
+attendant/SM
+attended/U
+attendee/SM
+attender/M
+attend/SGZDR
+attentional
+attentionality
+attention/IMS
+attentiveness/IMS
+attentive/YIP
+attenuated/U
+attenuate/SDXGN
+attenuation/M
+attenuator/MS
+attestation/SM
+attested/U
+attester/M
+attest/GSDR
+Attic
+Attica/M
+attic/MS
+Attila/M
+attire/SDG
+attitude/MS
+attitudinal/Y
+attitudinize/SDG
+Attlee/M
+attn
+Attn
+attorney/SM
+attractant/SM
+attract/BSDGV
+attraction/MS
+attractivenesses
+attractiveness/UM
+attractive/UYP
+attractor/MS
+attributable/U
+attribute/BVNGRSDX
+attributed/U
+attributer/M
+attributional
+attribution/M
+attributive/SY
+attrition/MS
+Attucks
+attune/SDG
+atty
+ATV/S
+atwitter
+Atwood/M
+atypical/Y
+Aube/M
+Auberge/M
+aubergine/MS
+Auberon/M
+Auberta/M
+Aubert/M
+Aubine/M
+Aubree/M
+Aubrette/M
+Aubrey/M
+Aubrie/M
+Aubry/M
+auburn/SM
+Auckland/M
+auctioneer/SDMG
+auction/MDSG
+audaciousness/SM
+audacious/PY
+audacity/MS
+Auden/M
+audibility/MSI
+audible/I
+audibles
+audibly/I
+Audie/M
+audience/MS
+Audi/M
+audiogram/SM
+audiological
+audiologist/MS
+audiology/SM
+audiometer/MS
+audiometric
+audiometry/M
+audiophile/SM
+audio/SM
+audiotape/S
+audiovisual/S
+audited/U
+audition/MDSG
+auditorium/MS
+auditor/MS
+auditory/S
+audit/SMDVG
+Audra/M
+Audre/M
+Audrey/M
+Audrie/M
+Audrye/M
+Audry/M
+Audubon/M
+Audy/M
+Auerbach/M
+Augean
+auger/SM
+aught/S
+Augie/M
+Aug/M
+augmentation/SM
+augmentative/S
+augment/DRZGS
+augmenter/M
+augur/GDMS
+augury/SM
+Augusta/M
+Augustan/S
+Auguste/M
+Augustina/M
+Augustine/M
+Augustinian/S
+Augustin/M
+augustness/SM
+Augusto/M
+August/SM
+august/STPYR
+Augustus/M
+Augy/M
+auk/MS
+Au/M
+Aundrea/M
+auntie/MS
+aunt/MYS
+aunty's
+aural/Y
+Aura/M
+aura/SM
+Aurea/M
+Aurelea/M
+Aurelia/M
+Aurelie/M
+Aurelio/M
+Aurelius/M
+Aurel/M
+aureole/GMSD
+aureomycin
+Aureomycin/M
+Auria/M
+auric
+auricle/SM
+auricular
+Aurie/M
+Auriga/M
+Aurilia/M
+Aurlie/M
+Auroora/M
+auroral
+Aurora/M
+aurora/SM
+Aurore/M
+Aurthur/M
+Auschwitz/M
+auscultate/XDSNG
+auscultation/M
+auspice/SM
+auspicious/IPY
+auspiciousnesses
+auspiciousness/IM
+Aussie/MS
+Austen/M
+austereness/M
+austere/TYRP
+austerity/SM
+Austina/M
+Austine/M
+Austin/SM
+austral
+Australasia/M
+Australasian/S
+australes
+Australia/M
+Australian/MS
+Australis/M
+australites
+Australoid
+Australopithecus/M
+Austria/M
+Austrian/SM
+Austronesian
+authentically
+authenticated/U
+authenticate/GNDSX
+authentication/M
+authenticator/MS
+authenticity/MS
+authentic/UI
+author/DMGS
+authoress/S
+authorial
+authoritarianism/MS
+authoritarian/S
+authoritativeness/SM
+authoritative/PY
+authority/SM
+authorization/MAS
+authorize/AGDS
+authorized/U
+authorizer/SM
+authorizes/U
+authorship/MS
+autism/MS
+autistic/S
+autobahn/MS
+autobiographer/MS
+autobiographic
+autobiographical/Y
+autobiography/MS
+autoclave/SDGM
+autocollimator/M
+autocorrelate/GNSDX
+autocorrelation/M
+autocracy/SM
+autocratic
+autocratically
+autocrat/SM
+autodial/R
+autodidact/MS
+autofluorescence
+autograph/MDG
+autographs
+autoignition/M
+autoimmune
+autoimmunity/S
+autoloader
+automaker/S
+automata's
+automate/NGDSX
+automatically
+automatic/S
+automation/M
+automatism/SM
+automatize/DSG
+automaton/SM
+automobile/GDSM
+automorphism/SM
+automotive
+autonavigator/SM
+autonomic/S
+autonomous/Y
+autonomy/MS
+autopilot/SM
+autopsy/MDSG
+autoregressive
+autorepeat/GS
+auto/SDMG
+autostart
+autosuggestibility/M
+autotransformer/M
+autoworker/S
+autumnal/Y
+Autumn/M
+autumn/MS
+aux
+auxiliary/S
+auxin/MS
+AV
+availability/USM
+availableness/M
+available/U
+availably
+avail/BSZGRD
+availing/U
+avalanche/MGSD
+Avalon/M
+Ava/M
+avant
+avarice/SM
+avariciousness/M
+avaricious/PY
+avast/S
+avatar/MS
+avaunt/S
+avdp
+Aveline/M
+Ave/MS
+avenged/U
+avenger/M
+avenge/ZGSRD
+Aventine/M
+Aventino/M
+avenue/MS
+average/DSPGYM
+Averell/M
+Averill/M
+Averil/M
+Avernus/M
+averred
+averrer
+averring
+Averroes/M
+averseness/M
+averse/YNXP
+aversion/M
+avers/V
+avert/GSD
+Averyl/M
+Avery/M
+ave/S
+aves/C
+Avesta/M
+avg
+avian/S
+aviary/SM
+aviate/NX
+aviation/M
+aviator/SM
+aviatrices
+aviatrix/SM
+Avicenna/M
+Avictor/M
+avidity/MS
+avid/TPYR
+Avie/M
+Avigdor/M
+Avignon/M
+Avila/M
+avionic/S
+avionics/M
+Avior/M
+Avis
+avitaminoses
+avitaminosis/M
+Avivah/M
+Aviva/M
+Aviv/M
+avocado/MS
+avocational
+avocation/SM
+Avogadro/M
+avoidable/U
+avoidably/U
+avoidance/SM
+avoider/M
+avoid/ZRDBGS
+avoirdupois/MS
+Avon/M
+avouch/GDS
+avowal/EMS
+avowed/Y
+avower/M
+avow/GEDS
+Avram/M
+Avril/M
+Avrit/M
+Avrom/M
+avuncular
+av/ZR
+AWACS
+await/SDG
+awake/GS
+awakened/U
+awakener/M
+awakening/S
+awaken/SADG
+awarder/M
+award/RDSZG
+awareness/MSU
+aware/TRP
+awash
+away/PS
+aweigh
+awe/SM
+awesomeness/SM
+awesome/PY
+awestruck
+awfuller
+awfullest
+awfulness/SM
+awful/YP
+aw/GD
+awhile/S
+awkwardness/MS
+awkward/PRYT
+awl/MS
+awning/DM
+awn/MDJGS
+awoke
+awoken
+AWOL
+awry/RT
+ax/DRSZGM
+axehead/S
+Axel/M
+Axe/M
+axeman
+axial/Y
+axillary
+axiological/Y
+axiology/M
+axiomatically
+axiomatic/S
+axiomatization/MS
+axiomatize/GDS
+axiom/SM
+axion/SM
+axis/SM
+axle/MS
+axletree/MS
+Ax/M
+axolotl/SM
+axon/SM
+ayah/M
+ayahs
+Ayala/M
+ayatollah
+ayatollahs
+aye/MZRS
+Ayers
+Aylmar/M
+Aylmer/M
+Aymara/M
+Aymer/M
+Ayn/M
+AZ
+azalea/SM
+Azania/M
+Azazel/M
+Azerbaijan/M
+azimuthal/Y
+azimuth/M
+azimuths
+Azores
+Azov/M
+AZT
+Aztecan
+Aztec/MS
+azure/MS
+BA
+Baal/SM
+baa/SDG
+Babara/M
+Babar's
+Babbage/M
+Babbette/M
+Babbie/M
+babbitt/GDS
+Babbitt/M
+babbler/M
+babble/RSDGZ
+Babb/M
+Babcock/M
+Babel/MS
+babel/S
+babe/SM
+Babette/M
+Babita/M
+Babka/M
+baboon/MS
+Bab/SM
+babushka/MS
+babyhood/MS
+babyish
+Babylonia/M
+Babylonian/SM
+Babylon/MS
+babysat
+babysit/S
+babysitter/S
+babysitting
+baby/TDSRMG
+Bacall/M
+Bacardi/M
+baccalaureate/MS
+baccarat/SM
+bacchanalia
+Bacchanalia/M
+bacchanalian/S
+bacchanal/SM
+Bacchic
+Bacchus/M
+bachelorhood/SM
+bachelor/SM
+Bach/M
+bacillary
+bacilli
+bacillus/MS
+backache/SM
+backarrow
+backbencher/M
+backbench/ZR
+backbiter/M
+backbite/S
+backbitten
+backbit/ZGJR
+backboard/SM
+backbone/SM
+backbreaking
+backchaining
+backcloth/M
+backdate/GDS
+backdrop/MS
+backdropped
+backdropping
+backed/U
+backer/M
+backfield/SM
+backfill/SDG
+backfire/GDS
+backgammon/MS
+background/SDRMZG
+back/GZDRMSJ
+backhanded/Y
+backhander/M
+backhand/RDMSZG
+backhoe/S
+backing/M
+backlash/GRSDM
+backless
+backlogged
+backlogging
+backlog/MS
+backorder
+backpacker/M
+backpack/ZGSMRD
+backpedal/DGS
+backplane/MS
+backplate/SM
+backrest/MS
+backscatter/SMDG
+backseat/S
+backside/SM
+backslapper/MS
+backslapping/M
+backslash/DSG
+backslider/M
+backslide/S
+backslid/RZG
+backspace/GSD
+backspin/SM
+backstabber/M
+backstabbing
+backstage
+backstair/S
+backstitch/GDSM
+backstop/MS
+backstopped
+backstopping
+backstreet/M
+backstretch/SM
+backstroke/GMDS
+backtalk/S
+backtrack/SDRGZ
+backup/SM
+Backus/M
+backwardness/MS
+backward/YSP
+backwash/SDMG
+backwater/SM
+backwood/S
+backwoodsman/M
+backwoodsmen
+backyard/MS
+baconer/M
+Bacon/M
+bacon/SRM
+bacterial/Y
+bacteria/MS
+bactericidal
+bactericide/SM
+bacteriologic
+bacteriological
+bacteriologist/MS
+bacteriology/SM
+bacterium/M
+Bactria/M
+badder
+baddest
+baddie/MS
+bade
+Baden/M
+badge/DSRGMZ
+badger/DMG
+badinage/DSMG
+badland/S
+Badlands/M
+badman/M
+badmen
+badminton/MS
+badmouth/DG
+badmouths
+badness/SM
+bad/PSNY
+Baedeker/SM
+Baez/M
+Baffin/M
+bafflement/MS
+baffler/M
+baffle/RSDGZL
+baffling/Y
+bagatelle/MS
+bagel/SM
+bagful/MS
+baggageman
+baggagemen
+baggage/SM
+bagged/M
+bagger/SM
+baggily
+bagginess/MS
+bagging/M
+baggy/PRST
+Baghdad/M
+bagpiper/M
+bagpipe/RSMZ
+Bagrodia/MS
+bag/SM
+baguette/SM
+Baguio/M
+bah
+Baha'i
+Bahama/MS
+Bahamanian/S
+Bahamian/MS
+Baha'ullah
+Bahia/M
+Bahrain/M
+bahs
+Baikal/M
+Bailey/SM
+bail/GSMYDRB
+Bailie/M
+bailiff/SM
+bailiwick/MS
+Baillie/M
+Bail/M
+bailout/MS
+bailsman/M
+bailsmen
+Baily/M
+Baird/M
+bairn/SM
+baiter/M
+bait/GSMDR
+baize/GMDS
+Baja/M
+baked/U
+bakehouse/M
+Bakelite/M
+baker/M
+Baker/M
+Bakersfield/M
+bakery/SM
+bakeshop/S
+bake/ZGJDRS
+baking/M
+baklava/M
+baksheesh/SM
+Baku/M
+Bakunin/M
+balaclava/MS
+balalaika/MS
+balanced/A
+balancedness
+balancer/MS
+balance's
+balance/USDG
+Balanchine/M
+Balboa/M
+balboa/SM
+balcony/MSD
+balderdash/MS
+Balder/M
+baldfaced
+Bald/MR
+baldness/MS
+bald/PYDRGST
+baldric/SM
+Balduin/M
+Baldwin/M
+baldy
+Balearic/M
+baleen/MS
+balefuller
+balefullest
+balefulness/MS
+baleful/YP
+Bale/M
+bale/MZGDRS
+baler/M
+Balfour/M
+Bali/M
+Balinese
+balkanization
+balkanize/DG
+Balkan/SM
+balker/M
+balk/GDRS
+Balkhash/M
+balkiness/M
+balky/PRT
+balladeer/MS
+ballade/MS
+balladry/MS
+ballad/SM
+Ballard/SM
+ballast/SGMD
+ballcock/S
+ballerina/MS
+baller/M
+balletic
+ballet/MS
+ballfields
+ballgame/S
+ball/GZMSDR
+ballistic/S
+ballistics/M
+Ball/M
+balloonist/S
+balloon/RDMZGS
+balloter/M
+ballot/MRDGS
+ballpark/SM
+ballplayer/SM
+ballpoint/SM
+ballroom/SM
+ballsy/TR
+ballyhoo/SGMD
+balminess/SM
+balm/MS
+balmy/PRT
+baloney/SM
+balsam/GMDS
+balsamic
+balsa/MS
+Balthazar/M
+Baltic/M
+Baltimore/M
+Baluchistan/M
+baluster/MS
+balustrade/SM
+Balzac/M
+Ba/M
+Bamako/M
+Bamberger/M
+Bambie/M
+Bambi/M
+bamboo/SM
+bamboozle/GSD
+Bamby/M
+Banach/M
+banality/MS
+banal/TYR
+banana/SM
+Bancroft/M
+bandager/M
+bandage/RSDMG
+bandanna/SM
+bandbox/MS
+bandeau/M
+bandeaux
+band/EDGS
+bander/M
+banding/M
+bandit/MS
+banditry/MS
+bandmaster/MS
+bandoleer/SM
+bandpass
+band's
+bandsman/M
+bandsmen
+bandstand/SM
+bandstop
+Bandung/M
+bandwagon/MS
+bandwidth/M
+bandwidths
+bandy/TGRSD
+banefuller
+banefullest
+baneful/Y
+bane/MS
+Bangalore/M
+banger/M
+bang/GDRZMS
+bangkok
+Bangkok/M
+Bangladeshi/S
+Bangladesh/M
+bangle/MS
+Bangor/M
+Bangui/M
+bani
+banisher/M
+banishment/MS
+banish/RSDGL
+banister/MS
+Banjarmasin/M
+banjoist/SM
+banjo/MS
+Banjul/M
+bankbook/SM
+bankcard/S
+banker/M
+bank/GZJDRMBS
+banking/M
+Bank/MS
+banknote/S
+bankroll/DMSG
+bankruptcy/MS
+bankrupt/DMGS
+Banky/M
+Ban/M
+banned/U
+Banneker/M
+banner/SDMG
+banning/U
+Bannister/M
+bannister's
+bannock/SM
+banns
+banqueter/M
+banquet/SZGJMRD
+banquette/MS
+ban/SGMD
+banshee/MS
+bans/U
+bantam/MS
+bantamweight/MS
+banterer/M
+bantering/Y
+banter/RDSG
+Banting/M
+Bantu/SM
+banyan/MS
+banzai/S
+baobab/SM
+Baotou/M
+baptismal/Y
+baptism/SM
+Baptiste/M
+baptistery/MS
+baptist/MS
+Baptist/MS
+baptistry's
+baptized/U
+baptizer/M
+baptize/SRDZG
+baptizes/U
+Barabbas/M
+Barbabas/M
+Barbabra/M
+Barbadian/S
+Barbados/M
+Barbaraanne/M
+Barbara/M
+Barbarella/M
+barbarianism/MS
+barbarian/MS
+barbaric
+barbarically
+barbarism/MS
+barbarity/SM
+barbarize/SDG
+Barbarossa/M
+barbarousness/M
+barbarous/PY
+Barbary/M
+barb/DRMSGZ
+barbecue/DRSMG
+barbed/P
+Barbee/M
+barbell/SM
+barbel/MS
+Barbe/M
+barbeque's
+barber/DMG
+barbered/U
+Barber/M
+barberry/MS
+barbershop/MS
+Barbette/M
+Barbey/M
+Barbie/M
+Barbi/M
+barbital/M
+barbiturate/MS
+Barbour/M
+Barbra/M
+Barb/RM
+Barbuda/M
+barbwire/SM
+Barby/M
+barcarole/SM
+Barcelona/M
+Barclay/M
+Bardeen/M
+Barde/M
+bardic
+Bard/M
+bard/MDSG
+bareback/D
+barefacedness/M
+barefaced/YP
+barefoot/D
+barehanded
+bareheaded
+barelegged
+bareness/MS
+Barents/M
+bare/YSP
+barfly/SM
+barf/YDSG
+bargainer/M
+bargain/ZGSDRM
+barge/DSGM
+bargeman/M
+bargemen
+bargepole/M
+barhopped
+barhopping
+barhop/S
+Bari/M
+baritone/MS
+barium/MS
+barked/C
+barkeeper/M
+barkeep/SRZ
+barker/M
+Barker/M
+bark/GZDRMS
+Barkley/M
+barks/C
+barleycorn/MS
+barley/MS
+Barlow/M
+barmaid/SM
+barman/M
+barmen
+Bar/MH
+Barnabas
+Barnabe/M
+Barnaby/M
+barnacle/MDS
+Barnard/M
+Barnaul/M
+Barnebas/M
+Barnes
+Barnett/M
+Barney/M
+barnful
+barn/GDSM
+Barnhard/M
+Barnie/M
+Barn/M
+barnsful
+barnstorm/DRGZS
+barnstormer/M
+Barnum/M
+barnyard/MS
+Barny/M
+Baroda/M
+barometer/MS
+barometric
+barometrically
+baronage/MS
+baroness/MS
+baronetcy/SM
+baronet/MS
+baronial
+Baron/M
+baron/SM
+barony/SM
+baroque/SPMY
+barque's
+Barquisimeto/M
+barracker/M
+barrack/SDRG
+barracuda/MS
+barrage/MGSD
+Barranquilla/M
+barred/ECU
+barre/GMDSJ
+barrel/SGMD
+barrenness/SM
+barren/SPRT
+Barrera/M
+Barret/M
+barrette/SM
+Barrett/M
+barricade/SDMG
+Barrie/M
+barrier/MS
+barring/R
+barrio/SM
+Barri/SM
+barrister/MS
+Barr/M
+Barron/M
+barroom/SM
+barrow/MS
+Barry/M
+Barrymore/MS
+bars/ECU
+barstool/SM
+Barstow/M
+Bartel/M
+bartender/M
+bartend/ZR
+barterer/M
+barter/SRDZG
+bar/TGMDRS
+Barthel/M
+Barth/M
+Bartholdi/M
+Bartholemy/M
+Bartholomeo/M
+Bartholomeus/M
+Bartholomew/M
+Bartie/M
+Bartlet/M
+Bartlett/M
+Bart/M
+Bartók/M
+Bartolemo/M
+Bartolomeo/M
+Barton/M
+Bartram/M
+Barty/M
+barycenter
+barycentre's
+barycentric
+Bary/M
+baryon/SM
+Baryram/M
+Baryshnikov/M
+basaltic
+basalt/SM
+basal/Y
+Bascom/M
+bas/DRSTG
+baseball/MS
+baseband
+baseboard/MS
+base/CGRSDL
+baseless
+baseline/SM
+Basel/M
+basely
+Base/M
+baseman/M
+basemen
+basement/CSM
+baseness/MS
+baseplate/M
+base's
+basetting
+bashfulness/MS
+bashful/PY
+bash/JGDSR
+Basho/M
+Basia/M
+BASIC
+basically
+basic/S
+Basie/M
+basilar
+Basile/M
+basilica/SM
+Basilio/M
+basilisk/SM
+Basilius/M
+Basil/M
+basil/MS
+basin/DMS
+basinful/S
+basis/M
+basketball/MS
+basketry/MS
+basket/SM
+basketwork/SM
+bask/GSD
+basophilic
+Basque/SM
+Basra/M
+Basseterre/M
+basset/GMDS
+Bassett/M
+bassinet/SM
+bassist/MS
+Bass/M
+basso/MS
+bassoonist/MS
+bassoon/MS
+bass/SM
+basswood/SM
+bastardization/MS
+bastardized/U
+bastardize/SDG
+bastard/MYS
+bastardy/MS
+baste/NXS
+baster/M
+Bastian/M
+Bastien/M
+Bastille/M
+basting/M
+bastion/DM
+bast/SGZMDR
+Basutoland/M
+Bataan/M
+Batavia/M
+batch/MRSDG
+bated/U
+bate/KGSADC
+bater/AC
+Bates
+bathe
+bather/M
+bathetic
+bathhouse/SM
+bath/JMDSRGZ
+bathmat/S
+Batholomew/M
+bathos/SM
+bathrobe/MS
+bathroom/SDM
+baths
+Bathsheba/M
+bathtub/MS
+bathwater
+bathyscaphe's
+bathysphere/MS
+batik/DMSG
+Batista/M
+batiste/SM
+Bat/M
+batman/M
+Batman/M
+batmen
+baton/SM
+Batsheva/M
+batsman/M
+bat/SMDRG
+batsmen
+battalion/MS
+batted
+batten/SDMG
+batter/SRDZG
+battery/MS
+batting/MS
+battledore/MS
+battledress
+battlefield/SM
+battlefront/SM
+battle/GMZRSDL
+battleground/SM
+Battle/M
+battlement/SMD
+battler/M
+battleship/MS
+batty/RT
+Batu/M
+batwings
+bauble/SM
+Baudelaire/M
+baud/M
+Baudoin/M
+Baudouin/M
+Bauer/M
+Bauhaus/M
+baulk/GSDM
+Bausch/M
+bauxite/SM
+Bavaria/M
+Bavarian/S
+bawdily
+bawdiness/MS
+bawd/SM
+bawdy/PRST
+bawler/M
+bawl/SGDR
+Baxie/M
+Bax/M
+Baxter/M
+Baxy/M
+Bayamon
+Bayard/M
+bayberry/MS
+Bayda/M
+Bayer/M
+Bayes
+Bayesian
+bay/GSMDY
+Baylor/M
+Bay/MR
+bayonet/SGMD
+Bayonne/M
+bayou/MS
+Bayreuth/M
+bazaar/MS
+bazillion/S
+bazooka/MS
+BB
+BBB
+BBC
+bbl
+BBQ
+BBS
+BC
+BCD
+bdrm
+beachcomber/SM
+beachhead/SM
+Beach/M
+beach/MSDG
+beachwear/M
+beacon/DMSG
+beading/M
+Beadle/M
+beadle/SM
+bead/SJGMD
+beadsman/M
+beadworker
+beady/TR
+beagle/SDGM
+beaker/M
+beak/ZSDRM
+Beale/M
+Bealle/M
+Bea/M
+beam/MDRSGZ
+beanbag/SM
+bean/DRMGZS
+beanie/SM
+Bean/M
+beanpole/MS
+beanstalk/SM
+bearable/U
+bearably/U
+beard/DSGM
+bearded/P
+beardless
+Beard/M
+Beardmore/M
+Beardsley/M
+bearer/M
+bearing/M
+bearishness/SM
+bearish/PY
+bearlike
+Bear/M
+Bearnaise/M
+Bearnard/M
+bearskin/MS
+bear/ZBRSJG
+Beasley/M
+beasties
+beastings/M
+beastliness/MS
+beastly/PTR
+beast/SJMY
+beatable/U
+beatably/U
+beaten/U
+beater/M
+beatific
+beatifically
+beatification/M
+beatify/GNXDS
+beating/M
+beatitude/MS
+Beatlemania/M
+Beatles/M
+beatnik/SM
+beat/NRGSBZJ
+Beatrice/M
+Beatrisa/M
+Beatrix/M
+Beatriz/M
+Beauchamps
+Beaufort/M
+Beaujolais/M
+Beau/M
+Beaumarchais/M
+Beaumont/M
+beau/MS
+Beauregard/M
+beauteousness/M
+beauteous/YP
+beautician/MS
+beautification/M
+beautifier/M
+beautifully/U
+beautifulness/M
+beautiful/PTYR
+beautify/SRDNGXZ
+beaut/SM
+beauty/SM
+Beauvoir/M
+beaux's
+beaver/DMSG
+Beaverton/M
+Bebe/M
+bebop/MS
+becalm/GDS
+became
+because
+Becca/M
+Bechtel/M
+Becka/M
+Becker/M
+Becket/M
+Beckett/M
+beck/GSDM
+Beckie/M
+Becki/M
+beckon/SDG
+Beck/RM
+Becky/M
+becloud/SGD
+become/GJS
+becoming/UY
+Becquerel/M
+bedaub/GDS
+bedazzle/GLDS
+bedazzlement/SM
+bedbug/SM
+bedchamber/M
+bedclothes
+bedded
+bedder/MS
+bedding/MS
+bedeck/DGS
+Bede/M
+bedevil/DGLS
+bedevilment/SM
+bedfast
+bedfellow/MS
+Bedford/M
+bedimmed
+bedimming
+bedim/S
+bedizen/DGS
+bedlam/MS
+bedlinen
+bedmaker/SM
+bedmate/MS
+bed/MS
+Bedouin/SM
+bedpan/SM
+bedpost/SM
+bedraggle/GSD
+bedridden
+bedrock/SM
+bedroll/SM
+bedroom/DMS
+bedsheets
+bedside/MS
+bedsit
+bedsitter/M
+bedsore/MS
+bedspread/SM
+bedspring/SM
+bedstead/SM
+bedstraw/M
+bedtime/SM
+Beebe/M
+beebread/MS
+Beecher/M
+beech/MRSN
+beechnut/MS
+beechwood
+beefburger/SM
+beefcake/MS
+beef/GZSDRM
+beefiness/MS
+beefsteak/MS
+beefy/TRP
+beehive/MS
+beekeeper/MS
+beekeeping/SM
+beeline/MGSD
+Beelzebub/M
+Bee/M
+bee/MZGJRS
+been/S
+beeper/M
+beep/GZSMDR
+Beerbohm/M
+beer/M
+beermat/S
+beery/TR
+beeswax/DSMG
+Beethoven/M
+beetle/GMRSD
+Beeton/M
+beetroot/M
+beet/SM
+beeves/M
+befall/SGN
+befell
+befit/SM
+befitted
+befitting/Y
+befogged
+befogging
+befog/S
+before
+beforehand
+befoul/GSD
+befriend/DGS
+befuddle/GLDS
+befuddlement/SM
+began
+beget/S
+begetting
+beggar/DYMSG
+beggarliness/M
+beggarly/P
+beggary/MS
+begged
+begging
+Begin/M
+beginner/MS
+beginning/MS
+begin/S
+begone/S
+begonia/SM
+begot
+begotten
+begrime/SDG
+begrudge/GDRS
+begrudging/Y
+beg/S
+beguilement/SM
+beguiler/M
+beguile/RSDLZG
+beguiling/Y
+beguine/SM
+begum/MS
+begun
+behalf/M
+behalves
+Behan/M
+behave/GRSD
+behavioral/Y
+behaviorism/MS
+behavioristic/S
+behaviorist/S
+behavior/SMD
+behead/GSD
+beheld
+behemoth/M
+behemoths
+behest/SM
+behindhand
+behind/S
+beholder/M
+behold/ZGRNS
+behoofs
+behoove/SDJMG
+behooving/YM
+Behring/M
+Beiderbecke/M
+beige/MS
+Beijing
+Beilul/M
+being/M
+Beirut/M
+Beitris/M
+bejewel/SDG
+Bekesy/M
+Bekki/M
+be/KS
+belabor/MDSG
+Bela/M
+Belarus
+belate/D
+belatedness/M
+belated/PY
+Belau/M
+belay/GSD
+belch/GSD
+beleaguer/GDS
+Belem/M
+Belfast/M
+belfry/SM
+Belgian/MS
+Belgium/M
+Belg/M
+Belgrade/M
+Belia/M
+Belicia/M
+belie
+belief/ESUM
+belier/M
+believability's
+believability/U
+believable/U
+believably/U
+believed/U
+believe/EZGDRS
+believer/MUSE
+believing/U
+Belinda/M
+Belita/M
+belittlement/MS
+belittler/M
+belittle/RSDGL
+Belize/M
+belladonna/MS
+Bella/M
+Bellamy/M
+Bellanca/M
+Bellatrix/M
+bellboy/MS
+belled/A
+Belle/M
+belle/MS
+belletristic
+belletrist/SM
+Belleville/M
+bellflower/M
+bell/GSMD
+bellhop/MS
+bellicoseness/M
+bellicose/YP
+bellicosity/MS
+belligerence/SM
+belligerency/MS
+belligerent/SMY
+Bellina/M
+belling/A
+Bellini/M
+Bell/M
+bellman/M
+bellmen
+Bellovin/M
+bellow/DGS
+Bellow/M
+bellows/M
+bells/A
+bellwether/MS
+Bellwood/M
+bellyacher/M
+bellyache/SRDGM
+bellybutton/MS
+bellyfull
+bellyful/MS
+belly/SDGM
+Bel/M
+Belmont/M
+Belmopan/M
+Beloit/M
+belong/DGJS
+belonging/MP
+Belorussian/S
+Belorussia's
+belove/D
+beloved/S
+below/S
+Belshazzar/M
+belted/U
+belt/GSMD
+belting/M
+Belton/M
+Beltran/M
+Beltsville/M
+beltway/SM
+beluga/SM
+Belushi/M
+Belva/M
+belvedere/M
+Belvia/M
+bely/DSRG
+beman
+Be/MH
+bemire/SDG
+bemoan/GDS
+bemused/Y
+bemuse/GSDL
+bemusement/SM
+Benacerraf/M
+Benares's
+bencher/M
+benchmark/GDMS
+bench/MRSDG
+bend/BUSG
+bended
+Bender/M
+bender/MS
+Bendick/M
+Bendicty/M
+Bendite/M
+Bendix/M
+beneath
+Benedetta/M
+Benedetto/M
+Benedick/M
+Benedicta/M
+Benedictine/MS
+benediction/MS
+Benedict/M
+Benedicto/M
+benedictory
+Benedikta/M
+Benedikt/M
+benefaction/MS
+benefactor/MS
+benefactress/S
+benefice/MGSD
+beneficence/SM
+beneficent/Y
+beneficialness/M
+beneficial/PY
+beneficiary/MS
+benefiter/M
+benefit/SRDMZG
+Benelux/M
+Benet/M
+Benetta/M
+Benetton/M
+benevolence/SM
+benevolentness/M
+benevolent/YP
+Bengali/M
+Bengal/SM
+Benghazi/M
+Bengt/M
+Beniamino/M
+benightedness/M
+benighted/YP
+benignant
+benignity/MS
+benign/Y
+Beninese
+Benin/M
+Benita/M
+Benito/M
+Benjamen/M
+Benjamin/M
+Benjie/M
+Benji/M
+Benjy/M
+Ben/M
+Bennett/M
+Bennie/M
+Benni/M
+Bennington/M
+Benn/M
+Benny/M
+Benoite/M
+Benoit/M
+Benson/M
+Bentham/M
+Bentlee/M
+Bentley/MS
+Bent/M
+Benton/M
+bents
+bent/U
+bentwood/SM
+benumb/SGD
+Benyamin/M
+Benzedrine/M
+benzene/MS
+benzine/SM
+Benz/M
+Beograd's
+Beowulf/M
+bequeath/GSD
+bequeaths
+bequest/MS
+berate/GSD
+Berber/MS
+bereave/GLSD
+bereavement/MS
+bereft
+Berenice/M
+Beret/M
+beret/SM
+Bergen/M
+Bergerac/M
+Berger/M
+Berget/M
+Berglund/M
+Bergman/M
+Berg/NRM
+berg/NRSM
+Bergson/M
+Bergsten/M
+Bergstrom/M
+beribbon/D
+beriberi/SM
+Beringer/M
+Bering/RM
+Berkeley/M
+berkelium/SM
+Berke/M
+Berkie/M
+Berkley/M
+Berkly/M
+Berkowitz/M
+Berkshire/SM
+Berky/M
+Berk/YM
+Berle/M
+Berliner/M
+Berlin/SZRM
+Berlioz/M
+Berlitz/M
+Berman/M
+Ber/MG
+berm/SM
+Bermuda/MS
+Bermudan/S
+Bermudian/S
+Bernadene/M
+Bernadette/M
+Bernadina/M
+Bernadine/M
+Berna/M
+Bernardina/M
+Bernardine/M
+Bernardino/M
+Bernard/M
+Bernardo/M
+Bernarr/M
+Bernays/M
+Bernbach/M
+Bernelle/M
+Berne's
+Bernese
+Bernete/M
+Bernetta/M
+Bernette/M
+Bernhard/M
+Bernhardt/M
+Bernice/M
+Berniece/M
+Bernie/M
+Berni/M
+Bernini/M
+Bernita/M
+Bern/M
+Bernoulli/M
+Bernstein/M
+Berny/M
+Berra/M
+Berrie/M
+Berri/M
+berrylike
+Berry/M
+berry/SDMG
+berserker/M
+berserk/SR
+Berta/M
+Berte/M
+Bertha/M
+Berthe/M
+berth/MDGJ
+berths
+Bertie/M
+Bertillon/M
+Berti/M
+Bertina/M
+Bertine/M
+Bert/M
+Berton/M
+Bertram/M
+Bertrand/M
+Bertrando/M
+Berty/M
+Beryle/M
+beryllium/MS
+Beryl/M
+beryl/SM
+Berzelius/M
+bes
+beseecher/M
+beseeching/Y
+beseech/RSJZG
+beseem/GDS
+beset/S
+besetting
+beside/S
+besieger/M
+besiege/SRDZG
+besmear/GSD
+besmirch/GSD
+besom/GMDS
+besot/S
+besotted
+besotting
+besought
+bespangle/GSD
+bespatter/SGD
+bespeak/SG
+bespectacled
+bespoke
+bespoken
+Bess
+Bessel/M
+Bessemer/M
+Bessie/M
+Bessy/M
+best/DRSG
+bestiality/MS
+bestial/Y
+bestiary/MS
+bestirred
+bestirring
+bestir/S
+Best/M
+bestowal/SM
+bestow/SGD
+bestrew/DGS
+bestrewn
+bestridden
+bestride/SG
+bestrode
+bestseller/MS
+bestselling
+bestubble/D
+betaken
+betake/SG
+beta/SM
+betatron/M
+betcha
+Betelgeuse/M
+betel/MS
+Bethanne/M
+Bethany/M
+bethel/M
+Bethe/M
+Bethena/M
+Bethesda/M
+Bethina/M
+bethink/GS
+Bethlehem/M
+beth/M
+Beth/M
+bethought
+Bethune
+betide/GSD
+betimes
+bet/MS
+betoken/GSD
+betook
+betrayal/SM
+betrayer/M
+betray/SRDZG
+betrothal/SM
+betrothed/U
+betroth/GD
+betroths
+Betsey/M
+Betsy/M
+Betta/M
+Betteanne/M
+Betteann/M
+Bette/M
+betterment/MS
+better/SDLG
+Bettie/M
+Betti/M
+Bettina/M
+Bettine/M
+betting
+bettor/SM
+Bettye/M
+Betty/SM
+betweenness/M
+between/SP
+betwixt
+Beulah/M
+Bevan/M
+bevel/SJGMRD
+beverage/MS
+Beverie/M
+Beverlee/M
+Beverley/M
+Beverlie/M
+Beverly/M
+Bevin/M
+Bevon/M
+Bev's
+Bevvy/M
+bevy/SM
+bewail/GDS
+beware/GSD
+bewhisker/D
+bewigged
+bewildered/PY
+bewildering/Y
+bewilder/LDSG
+bewilderment/SM
+bewitching/Y
+bewitch/LGDS
+bewitchment/SM
+bey/MS
+beyond/S
+bezel/MS
+bf
+B/GT
+Bhopal/M
+Bhutanese
+Bhutan/M
+Bhutto/M
+Bialystok/M
+Bianca/M
+Bianco/M
+Bianka/M
+biannual/Y
+bias/DSMPG
+biased/U
+biathlon/MS
+biaxial/Y
+bibbed
+Bibbie/M
+bibbing
+Bibbye/M
+Bibby/M
+Bibi/M
+bible/MS
+Bible/MS
+biblical/Y
+biblicists
+bibliographer/MS
+bibliographical/Y
+bibliographic/S
+bibliography/MS
+bibliophile/MS
+Bib/M
+bib/MS
+bibulous
+bicameral
+bicameralism/MS
+bicarb/MS
+bicarbonate/MS
+bicentenary/S
+bicentennial/S
+bicep/S
+biceps/M
+bichromate/DM
+bickerer/M
+bickering/M
+bicker/SRDZG
+biconcave
+biconnected
+biconvex
+bicuspid/S
+bicycler/M
+bicycle/RSDMZG
+bicyclist/SM
+biddable
+bidden/U
+bidder/MS
+Biddie/M
+bidding/MS
+Biddle/M
+Biddy/M
+biddy/SM
+bider/M
+bide/S
+bidet/SM
+Bidget/M
+bid/GMRS
+bidiagonal
+bidirectional/Y
+bids/A
+biennial/SY
+biennium/SM
+Bienville/M
+Bierce/M
+bier/M
+bifocal/S
+bifurcate/SDXGNY
+bifurcation/M
+bigamist/SM
+bigamous
+bigamy/SM
+Bigelow/M
+Bigfoot
+bigged
+bigger
+biggest
+biggie/SM
+bigging
+biggish
+bighead/MS
+bigheartedness/S
+bighearted/P
+bighorn/MS
+bight/SMDG
+bigmouth/M
+bigmouths
+bigness/SM
+bigoted/Y
+bigot/MDSG
+bigotry/MS
+big/PYS
+bigwig/MS
+biharmonic
+bijection/MS
+bijective/Y
+bijou/M
+bijoux
+bike/MZGDRS
+biker/M
+bikini/SMD
+Biko/M
+bilabial/S
+bilateralness/M
+bilateral/PY
+bilayer/S
+Bilbao/M
+bilberry/MS
+Bilbo/M
+bile/SM
+bilge/GMDS
+biliary
+Bili/M
+bilinear
+bilingualism/SM
+bilingual/SY
+biliousness/SM
+bilious/P
+bilker/M
+bilk/GZSDR
+billboard/MDGS
+biller/M
+billet/MDGS
+billfold/MS
+billiard/SM
+Billie/M
+Billi/M
+billing/M
+billingsgate/SM
+Billings/M
+billionaire/MS
+billion/SHM
+billionths
+bill/JGZSBMDR
+Bill/JM
+billow/DMGS
+billowy/RT
+billposters
+Billye/M
+Billy/M
+billy/SM
+Bil/MY
+bi/M
+Bi/M
+bimbo/MS
+bimetallic/S
+bimetallism/MS
+Bimini/M
+bimodal
+bimolecular/Y
+bimonthly/S
+binary/S
+binaural/Y
+binder/M
+bindery/MS
+binding/MPY
+bindingness/M
+bind/JDRGZS
+bindle/M
+binds/AU
+bindweed/MS
+binge/MS
+bing/GNDM
+Bingham/M
+Binghamton/M
+Bing/M
+bingo/MS
+Bini/M
+Bink/M
+Binky/M
+binnacle/MS
+binned
+Binnie/M
+Binni/M
+binning
+Binny/M
+binocular/SY
+binodal
+binomial/SYM
+bin/SM
+binuclear
+biochemical/SY
+biochemist/MS
+biochemistry/MS
+biodegradability/S
+biodegradable
+biodiversity/S
+bioengineering/M
+bioethics
+biofeedback/SM
+biographer/M
+biographic
+biographical/Y
+biograph/RZ
+biography/MS
+biog/S
+Bioko/M
+biol
+biological/SY
+biologic/S
+biologist/SM
+biology/MS
+biomass/SM
+biomedical
+biomedicine/M
+biometric/S
+biometrics/M
+biometry/M
+biomolecule/S
+biomorph
+bionically
+bionic/S
+bionics/M
+biophysical/Y
+biophysicist/SM
+biophysic/S
+biophysics/M
+biopic/S
+biopsy/SDGM
+biorhythm/S
+BIOS
+bioscience/S
+biosphere/MS
+biostatistic/S
+biosynthesized
+biotechnological
+biotechnologist
+biotechnology/SM
+biotic
+biotin/SM
+bipartisan
+bipartisanship/MS
+bipartite/YN
+bipartition/M
+bipedal
+biped/MS
+biplane/MS
+bipolar
+bipolarity/MS
+biracial
+Birch/M
+birch/MRSDNG
+birdbath/M
+birdbaths
+birdbrain/SDM
+birdcage/SM
+birder/M
+birdhouse/MS
+birdieing
+Birdie/M
+birdie/MSD
+birdlike
+birdlime/MGDS
+Bird/M
+birdseed/MS
+Birdseye/M
+bird/SMDRGZ
+birdsong
+birdtables
+birdwatch/GZR
+birefringence/M
+birefringent
+biretta/SM
+Birgit/M
+Birgitta/M
+Birkenstock/M
+Birk/M
+Birmingham/M
+Biro/M
+Biron/M
+birthday/SM
+birthmark/MS
+birth/MDG
+birthplace/SM
+birthrate/MS
+birthright/MS
+birth's/A
+births/A
+birthstone/SM
+bis
+Biscay/M
+Biscayne/M
+biscuit/MS
+bisect/DSG
+bisection/MS
+bisector/MS
+biserial
+bisexuality/MS
+bisexual/YMS
+Bishkek
+bishop/DGSM
+Bishop/M
+bishopric/SM
+Bismarck/M
+Bismark/M
+bismuth/M
+bismuths
+bison/M
+bisque/SM
+Bissau/M
+bistable
+bistate
+bistro/SM
+bisyllabic
+bitblt/S
+bitchily
+bitchiness/MS
+bitch/MSDG
+bitchy/PTR
+biter/M
+bite/S
+biting/Y
+bitmap/SM
+bit/MRJSZG
+BITNET/M
+bit's/C
+bits/C
+bitser/M
+bitted
+bitten
+bitterness/SM
+bittern/SM
+bitternut/M
+bitter/PSRDYTG
+bitterroot/M
+bittersweet/YMSP
+bitting
+bitty/PRT
+bitumen/MS
+bituminous
+bitwise
+bivalent/S
+bivalve/MSD
+bivariate
+bivouacked
+bivouacking
+bivouac/MS
+biweekly/S
+biyearly
+bizarreness/M
+bizarre/YSP
+Bizet/M
+biz/M
+bizzes
+Bjorn/M
+bk
+b/KGD
+Bk/M
+blabbed
+blabber/GMDS
+blabbermouth/M
+blabbermouths
+blabbing
+blab/S
+blackamoor/SM
+blackball/SDMG
+blackberry/GMS
+blackbirder/M
+blackbird/SGDRM
+blackboard/SM
+blackbody/S
+Blackburn/M
+blackcurrant/M
+blackener/M
+blacken/GDR
+Blackfeet
+Blackfoot/M
+blackguard/MDSG
+blackhead/SM
+blacking/M
+blackish
+blackjack/SGMD
+blackleg/M
+blacklist/DRMSG
+blackmail/DRMGZS
+blackmailer/M
+Blackman/M
+Blackmer/M
+blackness/MS
+blackout/SM
+Blackpool/M
+Black's
+black/SJTXPYRDNG
+blacksmith/MG
+blacksmiths
+blacksnake/MS
+blackspot
+Blackstone/M
+blackthorn/MS
+blacktop/MS
+blacktopped
+blacktopping
+Blackwell/MS
+bladder/MS
+bladdernut/M
+bladderwort/M
+blade/DSGM
+blah/MDG
+blahs
+Blaine/M
+Blaire/M
+Blair/M
+Blakelee/M
+Blakeley/M
+Blake/M
+Blakey/M
+blame/DSRBGMZ
+blamelessness/SM
+blameless/YP
+blamer/M
+blameworthiness/SM
+blameworthy/P
+Blanca/M
+Blancha/M
+Blanchard/M
+blanch/DRSG
+Blanche/M
+blancher/M
+Blanch/M
+blanc/M
+blancmange/SM
+blandishment/MS
+blandish/SDGL
+blandness/MS
+bland/PYRT
+Blane/M
+Blankenship/M
+blanketing/M
+blanket/SDRMZG
+blankness/MS
+blank/SPGTYRD
+Blanton/M
+Blantyre/M
+blare/DSG
+blarney/DMGS
+blasé
+blasphemer/M
+blaspheme/RSDZG
+blasphemousness/M
+blasphemous/PY
+blasphemy/SM
+blaster/M
+blasting/M
+blastoff/SM
+blast/SMRDGZ
+blatancy/SM
+blatant/YP
+blather/DRGS
+blatting
+Blatz/M
+Blavatsky/M
+Blayne/M
+blaze/DSRGMZ
+blazer/M
+blazing/Y
+blazoner/M
+blazon/SGDR
+bl/D
+bldg
+bleach/DRSZG
+bleached/U
+bleacher/M
+bleakness/MS
+bleak/TPYRS
+blear/GDS
+blearily
+bleariness/SM
+bleary/PRT
+bleater/M
+bleat/RDGS
+bleeder/M
+bleed/ZRJSG
+Bleeker/M
+bleep/GMRDZS
+blemish/DSMG
+blemished/U
+blench/DSG
+blender/M
+blend/GZRDS
+Blenheim/M
+blessedness/MS
+blessed/PRYT
+blessing/M
+bless/JGSD
+Blevins/M
+blew
+Bligh/M
+blighter/M
+blight/GSMDR
+blimey/S
+blimp/MS
+blinded/U
+blinder/M
+blindfold/SDG
+blinding/MY
+blind/JGTZPYRDS
+blindness/MS
+blindside/SDG
+blinker/MDG
+blinking/U
+blink/RDGSZ
+blinks/M
+Blinnie/M
+Blinni/M
+Blinny/M
+blintze/M
+blintz/SM
+blip/MS
+blipped
+blipping
+Blisse/M
+blissfulness/MS
+blissful/PY
+Bliss/M
+bliss/SDMG
+blistering/Y
+blister/SMDG
+blistery
+Blithe/M
+blitheness/SM
+blither/G
+blithesome
+blithe/TYPR
+blitz/GSDM
+blitzkrieg/SM
+blizzard/MS
+bloater/M
+bloat/SRDGZ
+blobbed
+blobbing
+blob/MS
+Bloch/M
+blockader/M
+blockade/ZMGRSD
+blockage/MS
+blockbuster/SM
+blockbusting/MS
+blocker/MS
+blockhead/MS
+blockhouse/SM
+block's
+block/USDG
+blocky/R
+bloc/MS
+Bloemfontein/M
+bloke/SM
+Blomberg/M
+Blomquist/M
+Blondelle/M
+Blondell/M
+blonde's
+Blondie/M
+blondish
+blondness/MS
+blond/SPMRT
+Blondy/M
+bloodbath
+bloodbaths
+bloodcurdling
+bloodhound/SM
+bloodied/U
+bloodiness/MS
+bloodlessness/SM
+bloodless/PY
+bloodletting/MS
+bloodline/SM
+bloodmobile/MS
+bloodroot/M
+bloodshed/SM
+bloodshot
+blood/SMDG
+bloodsport/S
+bloodstain/MDS
+bloodstock/SM
+bloodstone/M
+bloodstream/SM
+bloodsucker/SM
+bloodsucking/S
+bloodthirstily
+bloodthirstiness/MS
+bloodthirsty/RTP
+bloodworm/M
+bloodymindedness
+bloody/TPGDRS
+bloomer/M
+Bloomer/M
+Bloomfield/M
+Bloomington/M
+Bloom/MR
+bloom/SMRDGZ
+blooper/M
+bloop/GSZRD
+blossom/DMGS
+blossomy
+blotch/GMDS
+blotchy/RT
+blot/MS
+blotted
+blotter/MS
+blotting
+blotto
+blouse/GMSD
+blower/M
+blowfish/M
+blowfly/MS
+blowgun/SM
+blow/GZRS
+blowing/M
+blown/U
+blowout/MS
+blowpipe/SM
+blowtorch/SM
+blowup/MS
+blowy/RST
+blowzy/RT
+BLT
+blubber/GSDR
+blubbery
+Blucher/M
+bludgeon/GSMD
+blueback
+Bluebeard/M
+bluebell/MS
+blueberry/SM
+bluebill/M
+bluebird/MS
+bluebonnet/SM
+bluebook/M
+bluebottle/MS
+bluebush
+bluefish/SM
+bluegill/SM
+bluegrass/MS
+blueing's
+blueish
+bluejacket/MS
+bluejeans
+blue/JMYTGDRSP
+blueness/MS
+bluenose/MS
+bluepoint/SM
+blueprint/GDMS
+bluer/M
+bluest/M
+bluestocking/SM
+bluesy/TR
+bluet/MS
+bluffer/M
+bluffness/MS
+bluff/SPGTZYRD
+bluing/M
+bluishness/M
+bluish/P
+Blumenthal/M
+Blum/M
+blunderbuss/MS
+blunderer/M
+blunder/GSMDRJZ
+blundering/Y
+bluntness/MS
+blunt/PSGTYRD
+blurb/GSDM
+blur/MS
+blurred/Y
+blurriness/S
+blurring/Y
+blurry/RPT
+blurt/GSRD
+blusher/M
+blushing/UY
+blush/RSDGZ
+blusterer/M
+blustering/Y
+blusterous
+bluster/SDRZG
+blustery
+blvd
+Blvd
+Blythe/M
+BM
+BMW/M
+BO
+boarded
+boarder/SM
+boardgames
+boardinghouse/SM
+boarding/SM
+board/IS
+boardroom/MS
+board's
+boardwalk/SM
+boar/MS
+boa/SM
+boaster/M
+boastfulness/MS
+boastful/YP
+boast/SJRDGZ
+boatclubs
+boater/M
+boathouse/SM
+boating/M
+boatload/SM
+boatman/M
+boat/MDRGZJS
+boatmen
+boatswain/SM
+boatyard/SM
+bobbed
+Bobbee/M
+Bobbe/M
+Bobbette/M
+Bobbie/M
+Bobbi/M
+bobbing/M
+bobbin/MS
+Bobbitt/M
+bobble/SDGM
+Bobbsey/M
+Bobbye/M
+Bobby/M
+bobby/SM
+bobbysoxer's
+bobcat/MS
+Bobette/M
+Bobina/M
+Bobine/M
+Bobinette/M
+Bob/M
+bobolink/SM
+Bobrow/M
+bobsledded
+bobsledder/MS
+bobsledding/M
+bobsled/MS
+bobsleigh/M
+bobsleighs
+bobs/M
+bob/SM
+bobtail/SGDM
+bobwhite/SM
+Boca/M
+Boccaccio/M
+boccie/SM
+bock/GDS
+bockwurst
+bodega/MS
+Bodenheim/M
+bode/S
+Bodhidharma/M
+bodhisattva
+Bodhisattva/M
+bodice/SM
+bodied/M
+bodiless
+bodily
+boding/M
+bodkin/SM
+bod/SGMD
+bodybuilder/SM
+bodybuilding/S
+body/DSMG
+bodyguard/MS
+bodying/M
+bodysuit/S
+bodyweight
+bodywork/SM
+Boeing/M
+Boeotia/M
+Boeotian
+Boer/M
+Bogartian/M
+Bogart/M
+Bogey/M
+bogeyman/M
+bogeymen
+bogey/SGMD
+bogged
+bogging
+boggle/SDG
+boggling/Y
+boggy/RT
+bogie's
+bog/MS
+Bogotá/M
+bogus
+bogyman
+bogymen
+bogy's
+Boheme/M
+bohemianism/S
+bohemian/S
+Bohemian/SM
+Bohemia/SM
+Bohr/M
+Boigie/M
+boiled/AU
+boiler/M
+boilermaker/MS
+boilerplate/SM
+boil/JSGZDR
+boils/A
+Boise/M
+Bois/M
+boisterousness/MS
+boisterous/YP
+bola/SM
+boldface/SDMG
+boldness/MS
+bold/YRPST
+bole/MS
+bolero/MS
+Boleyn/M
+bolivares
+Bolivar/M
+bolivar/MS
+Bolivia/M
+Bolivian/S
+bollard/SM
+bollix/GSD
+boll/MDSG
+Bologna/M
+bologna/MS
+bolometer/MS
+bolo/MS
+boloney's
+Bolshevik/MS
+Bolshevism/MS
+Bolshevistic/M
+Bolshevist/MS
+Bolshoi/M
+bolsterer/M
+bolster/SRDG
+bolted/U
+bolter/M
+bolt/MDRGS
+Bolton/M
+bolts/U
+Boltzmann/M
+bolus/SM
+bombardier/MS
+bombard/LDSG
+bombardment/SM
+bombastic
+bombastically
+bombast/RMS
+Bombay/M
+bomber/M
+bombproof
+bomb/SGZDRJ
+bombshell/SM
+Bo/MRZ
+bona
+bonanza/MS
+Bonaparte/M
+Bonaventure/M
+bonbon/SM
+bondage/SM
+bonder/M
+bondholder/SM
+Bondie/M
+bond/JMDRSGZ
+Bond/M
+bondman/M
+bondmen
+Bondon/M
+bonds/A
+bondsman/M
+bondsmen
+bondwoman/M
+bondwomen
+Bondy/M
+boned/U
+bonehead/SDM
+boneless
+Bone/M
+bone/MZDRSG
+boner/M
+bonfire/MS
+bong/GDMS
+bongo/MS
+Bonham/M
+bonhomie/MS
+Boniface/M
+boniness/MS
+Bonita/M
+bonito/MS
+bonjour
+bonkers
+Bonnee/M
+Bonner/M
+bonneted/U
+bonnet/SGMD
+Bonneville/M
+Bonnibelle/M
+bonnie
+Bonnie/M
+Bonni/M
+Bonn/RM
+Bonny/M
+bonny/RT
+bonsai/SM
+Bontempo/M
+bonus/SM
+bony/RTP
+bonzes
+boob/DMSG
+booby/SM
+boodle/GMSD
+boogeyman's
+boogieing
+boogie/SD
+boo/GSDH
+boohoo/GDS
+bookbinder/M
+bookbindery/SM
+bookbinding/M
+bookbind/JRGZ
+bookcase/MS
+booked/U
+bookend/SGD
+Booker/M
+book/GZDRMJSB
+bookie/SM
+booking/M
+bookishness/M
+bookish/PY
+bookkeeper/M
+bookkeep/GZJR
+bookkeeping/M
+booklet/MS
+bookmaker/MS
+bookmaking/MS
+bookmark/MDGS
+bookmobile/MS
+bookplate/SM
+bookseller/SM
+bookshelf/M
+bookshelves
+bookshop/MS
+bookstall/MS
+bookstore/SM
+bookwork/M
+bookworm/MS
+Boolean
+boolean/S
+Boole/M
+boom/DRGJS
+boomerang/MDSG
+boomer/M
+boomtown/S
+boondocks
+boondoggle/DRSGZ
+boondoggler/M
+Boone/M
+Boonie/M
+boonies
+boon/MS
+Boony/M
+boorishness/SM
+boorish/PY
+boor/MS
+boosterism
+booster/M
+boost/SGZMRD
+boot/AGDS
+bootblack/MS
+bootee/MS
+Boote/M
+Boötes
+Boothe/M
+booth/M
+Booth/M
+booths
+bootie's
+bootlaces
+bootlegged/M
+bootlegger/SM
+bootlegging/M
+bootleg/S
+Bootle/M
+bootless
+Boot/M
+bootprints
+boot's
+bootstrapped
+bootstrapping
+bootstrap/SM
+booty/SM
+booze/DSRGMZ
+boozer/M
+boozy/TR
+bopped
+bopping
+bop/S
+borate/MSD
+borax/MS
+Bordeaux/M
+bordello/MS
+Borden/M
+borderer/M
+border/JRDMGS
+borderland/SM
+borderline/MS
+Bordie/M
+Bord/MN
+Bordon/M
+Bordy/M
+Borealis/M
+Boreas/M
+boredom/MS
+boreholes
+borer/M
+bore/ZGJDRS
+Borges
+Borgia/M
+Borg/M
+boric
+boring/YMP
+Boris
+Bork/M
+born/AIU
+Borneo/M
+borne/U
+Born/M
+Borodin/M
+boron/SM
+borosilicate/M
+borough/M
+boroughs
+Borroughs/M
+borrower/M
+borrowing/M
+borrow/JZRDGBS
+borscht/SM
+borstal/MS
+Boru/M
+borzoi/MS
+Bosch/M
+Bose/M
+bosh/MS
+Bosnia/M
+Bosnian/S
+bosom's
+bosom/SGUD
+bosomy/RT
+boson/SM
+Bosporus/M
+boss/DSRMG
+bossily
+bossiness/MS
+bossism/MS
+bossy/PTSR
+Bostitch/M
+Bostonian/SM
+Boston/MS
+bosun's
+Boswell/MS
+botanical/SY
+botanic/S
+botanist/SM
+botany/SM
+botcher/M
+botch/SRDGZ
+botfly/M
+bother/DG
+bothersome
+bothy/M
+both/ZR
+bot/S
+Botswana/M
+Botticelli/M
+bottle/GMZSRD
+bottleneck/GSDM
+bottler/M
+bottomlessness/M
+bottomless/YP
+bottommost
+bottom/SMRDG
+botulin/M
+botulinus/M
+botulism/SM
+Boucher/M
+boudoir/MS
+bouffant/S
+bougainvillea/SM
+bough/MD
+boughs
+bought/N
+bouillabaisse/MS
+bouillon/MS
+boulder/GMDS
+Boulder/M
+boulevard/MS
+bouncer/M
+bounce/SRDGZ
+bouncily
+bouncing/Y
+bouncy/TRP
+boundary/MS
+bound/AUDI
+boundedness/MU
+bounded/UP
+bounden
+bounder/AM
+bounders
+bounding
+boundlessness/SM
+boundless/YP
+bounds/IA
+bounteousness/MS
+bounteous/PY
+bountifulness/SM
+bountiful/PY
+bounty/SDM
+bouquet/SM
+Bourbaki/M
+bourbon/SM
+Bourbon/SM
+bourgeoisie/SM
+bourgeois/M
+Bourke/M
+Bourne/M
+Bournemouth/M
+boutique/MS
+bout/MS
+boutonnière/MS
+Bouvier
+Bovary/M
+bovine/YS
+Bowditch/M
+bowdlerization/MS
+bowdlerize/GRSD
+bowed/U
+bowel/GMDS
+Bowell/M
+Bowen/M
+bower/DMG
+Bowers
+Bowery/M
+Bowes
+bowie
+Bowie/M
+bowing/M
+bowlder's
+bowlegged
+bowleg/SM
+bowler/M
+bowlful/S
+bowl/GZSMDR
+bowline/MS
+bowling/M
+bowman/M
+Bowman/M
+bowmen
+bowser/M
+bowsprit/SM
+bows/R
+bowstring/GSMD
+bow/SZGNDR
+bowwow/DMGS
+boxcar/SM
+box/DRSJZGM
+boxer/M
+boxful/M
+boxing/M
+boxlike
+boxtops
+boxwood/SM
+boxy/TPR
+Boyce/M
+Boycey/M
+Boycie/M
+boycotter/M
+boycott/RDGS
+Boyd/M
+Boyer/M
+boyfriend/MS
+boyhood/SM
+boyishness/MS
+boyish/PY
+Boyle/M
+Boy/MR
+boy/MRS
+boyscout
+boysenberry/SM
+bozo/SM
+bpi
+bps
+BR
+brace/DSRJGM
+braced/U
+bracelet/MS
+bracer/M
+brachia
+brachium/M
+bracken/SM
+bracketed/U
+bracketing/M
+bracket/SGMD
+brackishness/SM
+brackish/P
+bract/SM
+Bradan/M
+bradawl/M
+Bradbury/M
+Bradburys
+bradded
+bradding
+Braddock/M
+Brade/M
+Braden/M
+Bradford/M
+Bradley/M
+Bradly/M
+Brad/MYN
+Bradney/M
+Bradshaw/M
+brad/SM
+Bradstreet/M
+Brady/M
+brae/SM
+braggadocio/SM
+braggart/SM
+bragged
+bragger/MS
+braggest
+bragging
+Bragg/M
+brag/S
+Brahe/M
+Brahma/MS
+Brahmanism/MS
+Brahman/SM
+Brahmaputra/M
+Brahmin's
+Brahms
+braider/M
+braiding/M
+braid/RDSJG
+braille/DSG
+Braille/GDSM
+Brainard/SM
+braincell/S
+brainchild/M
+brainchildren
+brain/GSDM
+braininess/MS
+brainlessness/M
+brainless/YP
+Brain/M
+brainpower/M
+brainstorm/DRMGJS
+brainstorming/M
+brainteaser/S
+brainteasing
+brainwasher/M
+brainwashing/M
+brainwash/JGRSD
+brainwave/S
+brainy/RPT
+braise/SDG
+brake/DSGM
+brakeman/M
+brakemen/M
+bramble/DSGM
+brambling/M
+brambly/RT
+Bram/M
+Brampton/M
+bra/MS
+Brana/M
+branched/U
+branching/M
+branchlike
+Branch/M
+branch/MDSJG
+Branchville/M
+Brandais/M
+Brandea/M
+branded/U
+Brandeis/M
+Brandel/M
+Brande/M
+Brandenburg/M
+Branden/M
+brander/GDM
+Brander/M
+Brandice/M
+Brandie/M
+Brandi/M
+Brandise/M
+brandish/GSD
+Brand/MRN
+Brando/M
+Brandon/M
+brand/SMRDGZ
+Brandt/M
+Brandtr/M
+brandy/GDSM
+Brandy/M
+Brandyn/M
+brandywine
+Braniff/M
+Bran/M
+branned
+branning
+Brannon/M
+bran/SM
+Brantley/M
+Brant/M
+Braque/M
+brashness/MS
+brash/PYSRT
+Brasilia
+brasserie/SM
+brass/GSDM
+brassiere/MS
+brassily
+brassiness/SM
+brassy/RSPT
+Bratislava/M
+brat/SM
+Brattain/M
+bratty/RT
+bratwurst/MS
+Braun/M
+bravadoes
+bravado/M
+brave/DSRGYTP
+braveness/MS
+bravery/MS
+bravest/M
+bravo/SDG
+bravura/SM
+brawler/M
+brawl/MRDSGZ
+brawniness/SM
+brawn/MS
+brawny/TRP
+brayer/M
+Bray/M
+bray/SDRG
+braze/GZDSR
+brazenness/MS
+brazen/PYDSG
+brazer/M
+brazier/SM
+Brazilian/MS
+Brazil/M
+Brazos/M
+Brazzaville/M
+breacher/M
+breach/MDRSGZ
+breadbasket/SM
+breadboard/SMDG
+breadbox/S
+breadcrumb/S
+breadfruit/MS
+breadline/MS
+bread/SMDHG
+breadth/M
+breadths
+breadwinner/MS
+breakables
+breakable/U
+breakage/MS
+breakaway/MS
+breakdown/MS
+breaker/M
+breakfaster/M
+breakfast/RDMGZS
+breakfront/S
+breaking/M
+breakneck
+breakout/MS
+breakpoint/SMDG
+break/SZRBG
+breakthroughs
+breakthrough/SM
+breakup/SM
+breakwater/SM
+bream/SDG
+Breanne/M
+Brear/M
+breastbone/MS
+breastfed
+breastfeed/G
+breasting/M
+breast/MDSG
+breastplate/SM
+breaststroke/SM
+breastwork/MS
+breathable/U
+breathalyser/S
+Breathalyzer/SM
+breathe
+breather/M
+breathing/M
+breathlessness/SM
+breathless/PY
+breaths
+breathtaking/Y
+breathy/TR
+breath/ZBJMDRSG
+Brecht/M
+Breckenridge/M
+bred/DG
+bredes
+breeching/M
+breech/MDSG
+breeder/I
+breeder's
+breeding/IM
+breeds/I
+breed/SZJRG
+Bree/M
+Breena/M
+breeze/GMSD
+breezeway/SM
+breezily
+breeziness/SM
+breezy/RPT
+Bremen/M
+bremsstrahlung/M
+Brena/M
+Brenda/M
+Brendan/M
+Brenden/M
+Brendin/M
+Brendis/M
+Brendon/M
+Bren/M
+Brenna/M
+Brennan/M
+Brennen/M
+Brenner/M
+Brenn/RNM
+Brent/M
+Brenton/M
+Bresenham/M
+Brest/M
+brethren
+Bret/M
+Breton
+Brett/M
+breve/SM
+brevet/MS
+brevetted
+brevetting
+breviary/SM
+brevity/MS
+brew/DRGZS
+brewer/M
+Brewer/M
+brewery/MS
+brewing/M
+brewpub/S
+Brew/RM
+Brewster/M
+Brezhnev/M
+Bria/M
+Briana/M
+Brian/M
+Brianna/M
+Brianne/M
+Briano/M
+Briant/M
+briar's
+bribe/GZDSR
+briber/M
+bribery/MS
+Brice/M
+brickbat/SM
+brick/GRDSM
+bricklayer/MS
+bricklaying/SM
+brickmason/S
+brickwork/SM
+brickyard/M
+bridal/S
+Bridalveil/M
+bridegroom/MS
+Bride/M
+bride/MS
+bridesmaid/MS
+Bridewell/M
+bridgeable/U
+bridged/U
+bridgehead/MS
+Bridgeport/M
+Bridger/M
+Bridges
+bridge/SDGM
+Bridget/M
+Bridgetown/M
+Bridgette/M
+Bridgett/M
+Bridgewater/M
+bridgework/MS
+bridging/M
+Bridgman/M
+Bridie/M
+bridled/U
+bridle/SDGM
+bridleway/S
+briefcase/SM
+briefed/C
+briefing/M
+briefness/MS
+briefs/C
+brief/YRDJPGTS
+Brien/M
+Brier/M
+brier/MS
+Brie/RSM
+Brietta/M
+brigade/GDSM
+brigadier/MS
+Brigadoon
+brigandage/MS
+brigand/MS
+brigantine/MS
+Brigg/MS
+Brigham/M
+brightener/M
+brighten/RDZG
+bright/GXTPSYNR
+Bright/M
+brightness/SM
+Brighton/M
+Brigida/M
+Brigid/M
+Brigit/M
+Brigitta/M
+Brigitte/M
+Brig/M
+brig/SM
+brilliance/MS
+brilliancy/MS
+brilliantine/MS
+brilliantness/M
+brilliant/PSY
+Brillo
+Brillouin/M
+brimful
+brimless
+brimmed
+brimming
+brim/SM
+brimstone/MS
+Brina/M
+Brindisi/M
+brindle/DSM
+brine/GMDSR
+briner/M
+Briney/M
+bringer/M
+bring/RGZS
+brininess/MS
+Brinkley/M
+brinkmanship/SM
+brink/MS
+Brinna/M
+Brinn/M
+Briny/M
+briny/PTSR
+brioche/SM
+Brion/M
+briquet's
+briquette/MGSD
+Brisbane/M
+brisket/SM
+briskness/MS
+brisk/YRDPGTS
+bristle/DSGM
+bristly/TR
+Bristol/M
+bristol/S
+Britain/M
+Brita/M
+Britannia/M
+Britannic
+Britannica/M
+britches
+Briticism/MS
+Britisher/M
+Britishly/M
+British/RYZ
+Brit/MS
+Britney/M
+Britni/M
+Briton/MS
+Britta/M
+Brittaney/M
+Brittani/M
+Brittan/M
+Brittany/MS
+Britte/M
+Britten/M
+Britteny/M
+brittleness/MS
+brittle/YTPDRSG
+Britt/MN
+Brittne/M
+Brittney/M
+Brittni/M
+Brnaba/M
+Brnaby/M
+Brno/M
+broach/DRSG
+broacher/M
+broadband
+broadcaster/M
+broadcast/RSGZJ
+broadcasts/A
+broadcloth/M
+broadcloths
+broaden/JGRDZ
+broadleaved
+broadloom/SM
+broadminded/P
+broadness/S
+broadsheet/MS
+broadside/SDGM
+broadsword/MS
+broad/TXSYRNP
+Broadway/SM
+Brobdingnagian
+Brobdingnag/M
+brocade/DSGM
+broccoli/MS
+brochette/SM
+brochure/SM
+Brockie/M
+Brock/M
+Brocky/M
+Broddie/M
+Broddy/M
+Broderick/M
+Broderic/M
+Brodie/M
+Brod/M
+Brody/M
+brogan/MS
+Broglie/M
+brogue/MS
+broiler/M
+broil/RDSGZ
+brokenhearted/Y
+brokenness/MS
+broken/YP
+brokerage/MS
+broker/DMG
+broke/RGZ
+Brok/M
+bromide/MS
+bromidic
+bromine/MS
+bronchial
+bronchi/M
+bronchiolar
+bronchiole/MS
+bronchiolitis
+bronchitic/S
+bronchitis/MS
+broncho's
+bronchus/M
+broncobuster/SM
+bronco/SM
+bronc/S
+Bron/M
+Bronnie/M
+Bronny/M
+Bronson/M
+Bronte
+brontosaur/SM
+brontosaurus/SM
+Bronx/M
+bronzed/M
+bronze/SRDGM
+bronzing/M
+brooch/MS
+brooder/M
+broodiness/M
+brooding/Y
+broodmare/SM
+brood/SMRDGZ
+broody/PTR
+Brookdale/M
+Brooke/M
+Brookfield/M
+Brookhaven/M
+brooklet/MS
+Brooklyn/M
+Brookmont/M
+brook/SGDM
+brookside
+Brook/SM
+broom/SMDG
+broomstick/MS
+Bros
+Brose/M
+bro/SH
+bros/S
+brothel/MS
+brother/DYMG
+brotherhood/SM
+brotherliness/MS
+brotherly/P
+broths
+broth/ZMR
+brougham/MS
+brought
+brouhaha/MS
+browbeat/NSG
+brow/MS
+Brownell/M
+Browne/M
+Brownian/M
+Brownie/MS
+brownie/MTRS
+browning/M
+Browning/M
+brownish
+Brown/MG
+brownness/MS
+brownout/MS
+brownstone/MS
+Brownsville/M
+brown/YRDMSJGTP
+browse
+browser/M
+brows/SRDGZ
+brr
+Br/TMN
+Brubeck/M
+brucellosis/M
+Bruce/M
+Brucie/M
+Bruckner/M
+Bruegel/M
+Brueghel's
+bruin/MS
+bruised/U
+bruise/JGSRDZ
+bruiser/M
+Bruis/M
+bruit/DSG
+Brumidi/M
+Brummel/M
+brunch/MDSG
+Brunei/M
+Brunelleschi/M
+brunet/S
+brunette/SM
+Brunhilda/M
+Brunhilde/M
+Bruno/M
+Brunswick/M
+brunt/GSMD
+brusher/M
+brushfire/MS
+brushlike
+brush/MSRDG
+brushoff/S
+brushwood/SM
+brushwork/MS
+brushy/R
+brusqueness/MS
+brusque/PYTR
+Brussels
+brutality/SM
+brutalization/SM
+brutalized/U
+brutalizes/AU
+brutalize/SDG
+brutal/Y
+brute/DSRGM
+brutishness/SM
+brutish/YP
+Brutus/M
+Bruxelles/M
+Bryana/M
+Bryan/M
+Bryant/M
+Bryanty/M
+Bryce/M
+Bryna/M
+Bryn/M
+Brynna/M
+Brynne/M
+Brynner/M
+Brynn/RM
+Bryon/M
+Brzezinski/M
+B's
+BS
+BSA
+BSD
+Btu
+BTU
+BTW
+bu
+bubblegum/S
+bubbler/M
+bubble/RSDGM
+bubbly/TRS
+Buber/M
+bub/MS
+buboes
+bubo/M
+bubonic
+buccaneer/GMDS
+Buchanan/M
+Bucharest/M
+Buchenwald/M
+Buchwald/M
+buckaroo/SM
+buckboard/SM
+bucker/M
+bucketful/MS
+bucket/SGMD
+buckeye/SM
+buck/GSDRM
+buckhorn/M
+Buckie/M
+Buckingham/M
+buckled/U
+buckler/MDG
+buckle/RSDGMZ
+buckles/U
+Buckley/M
+buckling's
+buckling/U
+Buck/M
+Buckner/M
+buckram/GSDM
+bucksaw/SM
+buckshot/MS
+buckskin/SM
+buckteeth
+bucktooth/DM
+buckwheat/SM
+Bucky/M
+bucolically
+bucolic/S
+Budapest/M
+budded
+Buddha/MS
+Buddhism/SM
+Buddhist/SM
+Buddie/M
+budding/S
+Budd/M
+buddy/GSDM
+Buddy/M
+budge/GDS
+budgerigar/MS
+budgetary
+budgeter/M
+budget/GMRDZS
+budgie/MS
+budging/U
+Bud/M
+bud/MS
+Budweiser/MS
+Buehring/M
+Buena/M
+buffaloes
+Buffalo/M
+buffalo/MDG
+buff/ASGD
+buffered/U
+bufferer/M
+buffer/RDMSGZ
+buffet/GMDJS
+bufflehead/M
+buffoonery/MS
+buffoonish
+buffoon/SM
+buff's
+Buffy/M
+Buford/M
+bugaboo/SM
+Bugatti/M
+bugbear/SM
+bug/CS
+bugeyed
+bugged/C
+buggered
+buggering
+bugger/SCM
+buggery/M
+bugging/C
+buggy/RSMT
+bugle/GMDSRZ
+bugler/M
+bug's
+Buick/M
+builder/SM
+building/SM
+build/SAG
+buildup/MS
+built/AUI
+Buiron/M
+Bujumbura/M
+Bukhara/M
+Bukharin/M
+Bulawayo/M
+Bulba/M
+bulb/DMGS
+bulblet
+bulbous
+Bulfinch/M
+Bulganin/M
+Bulgaria/M
+Bulgarian/S
+bulge/DSGM
+bulgy/RT
+bulimarexia/S
+bulimia/MS
+bulimic/S
+bulk/GDRMS
+bulkhead/SDM
+bulkiness/SM
+bulky/RPT
+bulldogged
+bulldogger
+bulldogging
+bulldog/SM
+bulldoze/GRSDZ
+bulldozer/M
+bullet/GMDS
+bulletin/SGMD
+bulletproof/SGD
+bullfighter/M
+bullfighting/M
+bullfight/SJGZMR
+bullfinch/MS
+bullfrog/SM
+bullhead/DMS
+bullheadedness/SM
+bullheaded/YP
+bullhide
+bullhorn/SM
+bullied/M
+bullion/SM
+bullishness/SM
+bullish/PY
+bull/MDGS
+Bullock/M
+bullock/MS
+bullpen/MS
+bullring/SM
+bullseye
+bullshit/MS
+bullshitted
+bullshitter/S
+bullshitting
+bullwhackers
+Bullwinkle/M
+bullyboy/MS
+bullying/M
+bully/TRSDGM
+bulrush/SM
+Bultmann/M
+bulwark/GMDS
+bumblebee/MS
+bumble/JGZRSD
+bumbler/M
+bumbling/Y
+Bumbry/M
+bummed/M
+bummer/MS
+bummest
+bumming/M
+bumper/DMG
+bump/GZDRS
+bumpiness/MS
+bumpkin/MS
+Bumppo/M
+bumptiousness/SM
+bumptious/PY
+bumpy/PRT
+bum/SM
+Bunche/M
+bunch/MSDG
+bunchy/RT
+buncombe's
+bunco's
+Bundestag/M
+bundled/U
+bundle/GMRSD
+bundler/M
+Bundy/M
+bungalow/MS
+bungee/SM
+bung/GDMS
+bunghole/MS
+bungle/GZRSD
+bungler/M
+bungling/Y
+Bunin/M
+bunion/SM
+bunk/CSGDR
+Bunker/M
+bunker's/C
+bunker/SDMG
+bunkhouse/SM
+bunkmate/MS
+bunko's
+bunk's
+bunkum/SM
+Bunnie/M
+Bunni/M
+Bunny/M
+bunny/SM
+Bunsen/SM
+bun/SM
+bunt/GJZDRS
+bunting/M
+Buñuel/M
+Bunyan/M
+buoyancy/MS
+buoyant/Y
+buoy/SMDG
+Burbank/M
+burbler/M
+burble/RSDG
+burbs
+Burch/M
+burden's
+burdensomeness/M
+burdensome/PY
+burden/UGDS
+burdock/SM
+bureaucracy/MS
+bureaucratically
+bureaucratic/U
+bureaucratization/MS
+bureaucratize/SDG
+bureaucrat/MS
+bureau/MS
+burgeon/GDS
+burger/M
+Burger/M
+Burgess/M
+burgess/MS
+burgher/M
+burgh/MRZ
+burghs
+burglarize/GDS
+burglarproof/DGS
+burglar/SM
+burglary/MS
+burgle/SDG
+burgomaster/SM
+Burgoyne/M
+Burg/RM
+burg/SZRM
+Burgundian/S
+Burgundy/MS
+burgundy/S
+burial/ASM
+buried/U
+burier/M
+Burke/M
+Burk/SM
+burlap/MS
+burler/M
+burlesquer/M
+burlesque/SRDMYG
+burley/M
+Burlie/M
+burliness/SM
+Burlingame/M
+Burlington/M
+Burl/M
+burl/SMDRG
+burly/PRT
+Burma/M
+Burmese
+bur/MYS
+burnable/S
+Burnaby/M
+Burnard/M
+burned/U
+Burne/MS
+burner/M
+Burnett/M
+burn/GZSDRBJ
+burning/Y
+burnisher/M
+burnish/GDRSZ
+burnoose/MS
+burnout/MS
+Burns
+Burnside/MS
+burnt/YP
+burp/SGMD
+burr/GSDRM
+Burris/M
+burrito/S
+Burr/M
+burro/SM
+Burroughs/M
+burrower/M
+burrow/GRDMZS
+bursae
+bursa/M
+Bursa/M
+bursar/MS
+bursary/MS
+bursitis/MS
+burster/M
+burst/SRG
+Burtie/M
+Burt/M
+Burton/M
+Burty/M
+Burundian/S
+Burundi/M
+bury/ASDG
+busboy/MS
+busby/SM
+Busch/M
+buses/A
+busgirl/S
+bus/GMDSJ
+bushel/MDJSG
+Bushido/M
+bushiness/MS
+bushing/M
+bush/JMDSRG
+bushland
+Bush/M
+bushman/M
+bushmaster/SM
+bushmen
+Bushnell/M
+bushwhacker/M
+bushwhacking/M
+bushwhack/RDGSZ
+bushy/PTR
+busily
+businesslike
+businessman/M
+businessmen
+business/MS
+businesspeople
+businessperson/S
+businesswoman/M
+businesswomen
+busker/M
+busk/GRM
+buskin/SM
+bus's/A
+buss/D
+bustard/MS
+buster/M
+bustle/GSD
+bustling/Y
+bust/MSDRGZ
+busty/RT
+busybody/MS
+busy/DSRPTG
+busyness/MS
+busywork/SM
+but/ACS
+butane/MS
+butcherer/M
+butcher/MDRYG
+butchery/MS
+Butch/M
+butch/RSZ
+butene/M
+Butler/M
+butler/SDMG
+butted/A
+butte/MS
+butterball/MS
+buttercup/SM
+buttered/U
+butterfat/MS
+Butterfield/M
+butterfingered
+butterfingers/M
+butterfly/MGSD
+buttermilk/MS
+butternut/MS
+butter/RDMGZ
+butterscotch/SM
+buttery/TRS
+butting/M
+buttock/SGMD
+buttoner/M
+buttonhole/GMRSD
+buttonholer/M
+button's
+button/SUDG
+buttonweed
+buttonwood/SM
+buttress/MSDG
+butt/SGZMDR
+butyl/M
+butyrate/M
+buxomness/M
+buxom/TPYR
+Buxtehude/M
+buyback/S
+buyer/M
+buyout/S
+buy/ZGRS
+buzzard/MS
+buzz/DSRMGZ
+buzzer/M
+buzzword/SM
+buzzy
+bx
+bxs
+byelaw's
+Byelorussia's
+bye/MZS
+Byers/M
+bygone/S
+bylaw/SM
+byliner/M
+byline/RSDGM
+BYOB
+bypass/GSDM
+bypath/M
+bypaths
+byplay/S
+byproduct/SM
+Byram/M
+Byran/M
+Byrann/M
+Byrd/M
+byre/SM
+Byrle/M
+Byrne/M
+byroad/MS
+Byrom/M
+Byronic
+Byronism/M
+Byron/M
+bystander/SM
+byte/SM
+byway/SM
+byword/SM
+byzantine
+Byzantine/S
+Byzantium/M
+by/ZR
+C
+ca
+CA
+cabala/MS
+caballed
+caballero/SM
+caballing
+cabal/SM
+cabana/MS
+cabaret/SM
+cabbage/MGSD
+cabbed
+cabbing
+cabby's
+cabdriver/SM
+caber/M
+Cabernet/M
+cabinetmaker/SM
+cabinetmaking/MS
+cabinet/MS
+cabinetry/SM
+cabinetwork/MS
+cabin/GDMS
+cablecast/SG
+cable/GMDS
+cablegram/SM
+cabochon/MS
+caboodle/SM
+caboose/MS
+Cabot/M
+Cabrera/M
+Cabrini/M
+cabriolet/MS
+cab/SMR
+cabstand/MS
+cacao/SM
+cacciatore
+cache/DSRGM
+cachepot/MS
+cachet/MDGS
+Cacilia/M
+Cacilie/M
+cackler/M
+cackle/RSDGZ
+cackly
+CACM
+cacophonist
+cacophonous
+cacophony/SM
+cacti
+cactus/M
+CAD
+cadaverous/Y
+cadaver/SM
+caddishness/SM
+caddish/PY
+Caddric/M
+caddy/GSDM
+cadence/CSM
+cadenced
+cadencing
+cadent/C
+cadenza/MS
+cadet/SM
+Cadette/S
+cadge/DSRGZ
+cadger/M
+Cadillac/MS
+Cadiz/M
+Cad/M
+cadmium/MS
+cadre/SM
+cad/SM
+caducei
+caduceus/M
+Caedmon/M
+Caesar/MS
+caesura/SM
+café/MS
+cafeteria/SM
+caffeine/SM
+caftan/SM
+caged/U
+Cage/M
+cage/MZGDRS
+cager/M
+cagey/P
+cagier
+cagiest
+cagily
+caginess/MS
+Cagney/M
+Cahokia/M
+cahoot/MS
+Cahra/M
+CAI
+Caiaphas/M
+caiman's
+Caine/M
+Cain/MS
+Cairistiona/M
+cairn/SDM
+Cairo/M
+caisson/SM
+caitiff/MS
+Caitlin/M
+Caitrin/M
+cajole/LGZRSD
+cajolement/MS
+cajoler/M
+cajolery/SM
+Cajun/MS
+cake/MGDS
+cakewalk/SMDG
+calabash/SM
+calaboose/MS
+Calais/M
+calamari/S
+calamine/GSDM
+calamitousness/M
+calamitous/YP
+calamity/MS
+cal/C
+calcareousness/M
+calcareous/PY
+calciferous
+calcification/M
+calcify/XGNSD
+calcimine/GMSD
+calcine/SDG
+calcite/SM
+calcium/SM
+Calcomp/M
+CalComp/M
+CALCOMP/M
+calculability/IM
+calculable/IP
+calculate/AXNGDS
+calculated/PY
+calculatingly
+calculating/U
+calculation/AM
+calculative
+calculator/SM
+calculi
+calculus/M
+Calcutta/M
+caldera/SM
+Calder/M
+Calderon/M
+caldron's
+Caldwell/M
+Caleb/M
+Caledonia/M
+Cale/M
+calendar/MDGS
+calender/MDGS
+calf/M
+calfskin/SM
+Calgary/M
+Calhoun/M
+Caliban/M
+caliber/SM
+calibrated/U
+calibrater's
+calibrate/XNGSD
+calibrating/A
+calibration/M
+calibrator/MS
+calicoes
+calico/M
+Calida/M
+Calif/M
+California/M
+Californian/MS
+californium/SM
+calif's
+Caligula/M
+Cali/M
+caliper/SDMG
+caliphate/SM
+caliph/M
+caliphs
+calisthenic/S
+calisthenics/M
+Callaghan/M
+call/AGRDBS
+Callahan/M
+calla/MS
+Calla/MS
+Callao/M
+callback/S
+Callean/M
+called/U
+callee/M
+caller/MS
+Calley/M
+Callida/M
+Callie/M
+calligrapher/M
+calligraphic
+calligraphist/MS
+calligraph/RZ
+calligraphy/MS
+Calli/M
+calling/SM
+Calliope/M
+calliope/SM
+callisthenics's
+Callisto/M
+callosity/MS
+callousness/SM
+callous/PGSDY
+callowness/MS
+callow/RTSP
+callus/SDMG
+Cally/M
+calming/Y
+calmness/MS
+calm/PGTYDRS
+Cal/MY
+Caloocan/M
+caloric/S
+calorie/SM
+calorific
+calorimeter/MS
+calorimetric
+calorimetry/M
+Caltech/M
+Calumet/M
+calumet/MS
+calumniate/NGSDX
+calumniation/M
+calumniator/SM
+calumnious
+calumny/MS
+calvary/M
+Calvary/M
+calve/GDS
+Calvert/M
+calves/M
+Calvinism/MS
+Calvinistic
+Calvinist/MS
+Calvin/M
+Calv/M
+calyces's
+Calypso/M
+calypso/SM
+calyx/MS
+Ca/M
+CAM
+Camacho/M
+Camala/M
+camaraderie/SM
+camber/DMSG
+cambial
+cambium/SM
+Cambodia/M
+Cambodian/S
+Cambrian/S
+cambric/MS
+Cambridge/M
+camcorder/S
+Camden/M
+camelhair's
+Camella/M
+Camellia/M
+camellia/MS
+Camel/M
+Camelopardalis/M
+Camelot/M
+camel/SM
+Camembert/MS
+cameo/GSDM
+camerae
+cameraman/M
+cameramen
+camera/MS
+camerawoman
+camerawomen
+Cameron/M
+Cameroonian/S
+Cameroon/SM
+came/SN
+Camey/M
+Camila/M
+Camile/M
+Camilla/M
+Camille/M
+Cami/M
+Camino/M
+camion/M
+camisole/MS
+Cam/M
+cammed
+Cammie/M
+Cammi/M
+cam/MS
+Cammy/M
+Camoens/M
+camomile's
+camouflage/DRSGZM
+camouflager/M
+campaigner/M
+campaign/ZMRDSG
+campanile/SM
+campanological
+campanologist/SM
+campanology/MS
+Campbell/M
+Campbellsport/M
+camper/SM
+campesinos
+campest
+campfire/SM
+campground/MS
+camphor/MS
+Campinas/M
+camping/S
+Campos
+camp's
+camp/SCGD
+campsite/MS
+campus/GSDM
+campy/RT
+Camry/M
+camshaft/SM
+Camus/M
+Canaanite/SM
+Canaan/M
+Canada/M
+Canadianism/SM
+Canadian/S
+Canad/M
+Canaletto/M
+canalization/MS
+canalize/GSD
+canal/SGMD
+canapé/S
+canard/MS
+Canaries
+canary/SM
+canasta/SM
+Canaveral/M
+Canberra/M
+cancan/SM
+cancelate/D
+canceled/U
+canceler/M
+cancellation/MS
+cancel/RDZGS
+cancer/MS
+Cancer/MS
+cancerous/Y
+Cancun/M
+Candace/M
+candelabra/S
+candelabrum/M
+Candice/M
+candidacy/MS
+Candida/M
+candidate/SM
+candidature/S
+Candide/M
+candidly/U
+candidness/SM
+candid/TRYPS
+Candie/M
+Candi/SM
+candle/GMZRSD
+candlelight/SMR
+candlelit
+candlepower/SM
+candler/M
+candlestick/SM
+Candlewick/M
+candlewick/MS
+candor/MS
+Candra/M
+candy/GSDM
+Candy/M
+canebrake/SM
+caner/M
+cane/SM
+canine/S
+caning/M
+Canis/M
+canister/SGMD
+cankerous
+canker/SDMG
+Can/M
+can/MDRSZGJ
+cannabis/MS
+canned
+cannelloni
+canner/SM
+cannery/MS
+Cannes
+cannibalism/MS
+cannibalistic
+cannibalization/SM
+cannibalize/GSD
+cannibal/SM
+cannily/U
+canninesses
+canniness/UM
+canning/M
+cannister/SM
+cannonade/SDGM
+cannonball/SGDM
+Cannon/M
+cannon/SDMG
+cannot
+canny/RPUT
+canoe/DSGM
+canoeist/SM
+Canoga/M
+canonic
+canonicalization
+canonicalize/GSD
+canonical/SY
+canonist/M
+canonization/MS
+canonized/U
+canonize/SDG
+canon/SM
+Canopus/M
+canopy/GSDM
+canst
+can't
+cantabile/S
+Cantabrigian
+cantaloupe/MS
+cantankerousness/SM
+cantankerous/PY
+cantata/SM
+cant/CZGSRD
+canted/IA
+canteen/MS
+Canterbury/M
+canter/CM
+cantered
+cantering
+canticle/SM
+cantilever/SDMG
+canto/MS
+cantonal
+Cantonese/M
+Canton/M
+cantonment/SM
+canton/MGSLD
+Cantor/M
+cantor/MS
+Cantrell/M
+cant's
+cants/A
+Cantu/M
+Canute/M
+canvasback/MS
+canvas/RSDMG
+canvasser/M
+canvass/RSDZG
+canyon/MS
+CAP
+capability/ISM
+capableness/IM
+capable/PI
+capabler
+capablest
+capably/I
+capaciousness/MS
+capacious/PY
+capacitance/SM
+capacitate/V
+capacitive/Y
+capacitor/MS
+capacity/IMS
+caparison/SDMG
+Capek/M
+Capella/M
+caper/GDM
+capeskin/SM
+cape/SM
+Capet/M
+Capetown/M
+Caph/M
+capillarity/MS
+capillary/S
+Capistrano/M
+capitalism/SM
+capitalistic
+capitalistically
+capitalist/SM
+capitalization/SMA
+capitalized/AU
+capitalizer/M
+capitalize/RSDGZ
+capitalizes/A
+capital/SMY
+capita/M
+Capitan/M
+capitation/CSM
+Capitoline/M
+Capitol/MS
+capitol/SM
+capitulate/AXNGSD
+capitulation/MA
+caplet/S
+cap/MDRSZB
+Capone/M
+capon/SM
+capo/SM
+Capote/M
+capped/UA
+capping/M
+cappuccino/MS
+Cappy/M
+Capra/M
+Caprice/M
+caprice/MS
+capriciousness/MS
+capricious/PY
+Capricorn/MS
+Capri/M
+caps/AU
+capsicum/MS
+capsize/SDG
+capstan/MS
+capstone/MS
+capsular
+capsule/MGSD
+capsulize/GSD
+captaincy/MS
+captain/SGDM
+caption/GSDRM
+captiousness/SM
+captious/PY
+captivate/XGNSD
+captivation/M
+captivator/SM
+captive/MS
+captivity/SM
+Capt/M
+captor/SM
+capture/AGSD
+capturer/MS
+capt/V
+Capulet/M
+Caputo/M
+Caracalla/M
+Caracas/M
+caracul's
+carafe/SM
+Caralie/M
+Cara/M
+caramelize/SDG
+caramel/MS
+carapace/SM
+carapaxes
+carat/SM
+Caravaggio/M
+caravan/DRMGS
+caravaner/M
+caravansary/MS
+caravanserai's
+caravel/MS
+caraway/MS
+carbide/MS
+carbine/MS
+carbohydrate/MS
+carbolic
+Carboloy/M
+carbonaceous
+carbonate/SDXMNG
+carbonation/M
+Carbondale/M
+Carbone/MS
+carbonic
+carboniferous
+Carboniferous
+carbonization/SAM
+carbonizer/AS
+carbonizer's
+carbonizes/A
+carbonize/ZGRSD
+carbon/MS
+carbonyl/M
+carborundum
+Carborundum/MS
+carboy/MS
+carbuncle/SDM
+carbuncular
+carburetor/MS
+carburetter/S
+carburettor/SM
+carcase/MS
+carcass/SM
+Carce/M
+carcinogenic
+carcinogenicity/MS
+carcinogen/SM
+carcinoma/SM
+cardamom/MS
+cardboard/MS
+card/EDRSG
+Cardenas/M
+carder/MS
+carder's/E
+cardholders
+cardiac/S
+Cardiff/M
+cardigan/SM
+cardinality/SM
+cardinal/SYM
+carding/M
+Cardin/M
+Cardiod/M
+cardiogram/MS
+cardiograph/M
+cardiographs
+cardioid/M
+cardiologist/SM
+cardiology/MS
+cardiomegaly/M
+cardiopulmonary
+cardiovascular
+card's
+cardsharp/ZSMR
+CARE
+cared/U
+careen/DSG
+careerism/M
+careerist/MS
+career/SGRDM
+carefree
+carefuller
+carefullest
+carefulness/MS
+careful/PY
+caregiver/S
+carelessness/MS
+careless/YP
+Care/M
+Carena/M
+Caren/M
+carer/M
+care/S
+Caresa/M
+Caressa/M
+Caresse/M
+caresser/M
+caressing/Y
+caressive/Y
+caress/SRDMVG
+caretaker/SM
+caret/SM
+careworn
+Carey/M
+carfare/MS
+cargoes
+cargo/M
+carhopped
+carhopping
+carhop/SM
+Caria/M
+Caribbean/S
+Carib/M
+caribou/MS
+caricature/GMSD
+caricaturisation
+caricaturist/MS
+caricaturization
+Carie/M
+caries/M
+carillonned
+carillonning
+carillon/SM
+Caril/M
+Carilyn/M
+Cari/M
+Carina/M
+Carine/M
+caring/U
+Carin/M
+Cariotta/M
+carious
+Carissa/M
+Carita/M
+Caritta/M
+carjack/GSJDRZ
+Carla/M
+Carlee/M
+Carleen/M
+Carlene/M
+Carlen/M
+Carletonian/M
+Carleton/M
+Carley/M
+Carlie/M
+Carlina/M
+Carline/M
+Carling/M
+Carlin/M
+Carlita/M
+Carl/MNG
+carload/MSG
+Carlo/SM
+Carlota/M
+Carlotta/M
+Carlsbad/M
+Carlson/M
+Carlton/M
+Carlye/M
+Carlyle/M
+Carly/M
+Carlyn/M
+Carlynne/M
+Carlynn/M
+Carma/M
+Carmela/M
+Carmelia/M
+Carmelina/M
+Carmelita/M
+Carmella/M
+Carmelle/M
+Carmel/M
+Carmelo/M
+Carmencita/M
+Carmen/M
+Carmichael/M
+Carmina/M
+Carmine/M
+carmine/MS
+Carmita/M
+Car/MNY
+Carmon/M
+carnage/MS
+carnality/SM
+carnal/Y
+Carnap/M
+carnation/IMS
+Carnegie/M
+carnelian/SM
+Carney/M
+carney's
+carnival/MS
+carnivore/SM
+carnivorousness/MS
+carnivorous/YP
+Carnot/M
+Carny/M
+carny/SDG
+carob/SM
+Carola/M
+Carolan/M
+Carolann/M
+Carolee/M
+Carole/M
+caroler/M
+Carolina/MS
+Caroline/M
+Carolingian
+Carolinian/S
+Carolin/M
+Caroljean/M
+Carol/M
+carol/SGZMRD
+Carolus/M
+Carolyne/M
+Carolyn/M
+Carolynn/M
+Caro/M
+carom/GSMD
+Caron/M
+carotene/MS
+carotid/MS
+carousal/MS
+carousel/MS
+carouser/M
+carouse/SRDZG
+carpal/SM
+Carpathian/MS
+carpel/SM
+carpenter/DSMG
+carpentering/M
+Carpenter/M
+carpentry/MS
+carper/M
+carpetbagged
+carpetbagger/MS
+carpetbagging
+carpetbag/MS
+carpeting/M
+carpet/MDJGS
+carpi/M
+carping/Y
+carp/MDRSGZ
+carpool/DGS
+carport/MS
+carpus/M
+carrageen/M
+Carree/M
+carrel/SM
+carriage/SM
+carriageway/SM
+Carrie/M
+carrier/M
+Carrier/M
+Carrillo/M
+Carri/M
+carrion/SM
+Carrissa/M
+Carr/M
+Carroll/M
+Carrol/M
+carrot/MS
+carroty/RT
+carrousel's
+carryall/MS
+Carry/MR
+carryout/S
+carryover/S
+carry/RSDZG
+carsickness/SM
+carsick/P
+Carson/M
+cartage/MS
+cartel/SM
+carte/M
+carter/M
+Carter/M
+Cartesian
+Carthage/M
+Carthaginian/S
+carthorse/MS
+Cartier/M
+cartilage/MS
+cartilaginous
+cartload/MS
+cart/MDRGSZ
+Cart/MR
+cartographer/MS
+cartographic
+cartography/MS
+carton/GSDM
+cartoon/GSDM
+cartoonist/MS
+cartridge/SM
+cartwheel/MRDGS
+Cartwright/M
+Carty/RM
+Caruso/M
+carve/DSRJGZ
+carven
+carver/M
+Carver/M
+carving/M
+caryatid/MS
+Caryl/M
+Cary/M
+Caryn/M
+car/ZGSMDR
+casaba/SM
+Casablanca/M
+Casals/M
+Casandra/M
+Casanova/SM
+Casar/M
+casbah/M
+cascade/MSDG
+Cascades/M
+cascara/MS
+casebook/SM
+case/DSJMGL
+cased/U
+caseharden/SGD
+casein/SM
+caseload/MS
+Case/M
+casement/SM
+caseworker/M
+casework/ZMRS
+Casey/M
+cashbook/SM
+cashew/MS
+cash/GZMDSR
+cashier/SDMG
+cashless
+Cash/M
+cashmere/MS
+Casie/M
+Casi/M
+casing/M
+casino/MS
+casket/SGMD
+cask/GSDM
+Caspar/M
+Casper/M
+Caspian
+Cass
+Cassandra/SM
+Cassandre/M
+Cassandry/M
+Cassatt/M
+Cassaundra/M
+cassava/MS
+casserole/MGSD
+cassette/SM
+Cassey/M
+cassia/MS
+Cassie/M
+Cassi/M
+cassino's
+Cassiopeia/M
+Cassite/M
+Cassius/M
+cassock/SDM
+Cassondra/M
+cassowary/SM
+Cassy/M
+Castaneda/M
+castanet/SM
+castaway/SM
+castellated
+caste/MHS
+caster/M
+cast/GZSJMDR
+castigate/XGNSD
+castigation/M
+castigator/SM
+Castile's
+Castillo/M
+casting/M
+castle/GMSD
+castoff/S
+Castor/M
+castor's
+castrate/DSNGX
+castration/M
+Castries/M
+Castro/M
+casts/A
+casualness/SM
+casual/SYP
+casualty/SM
+casuistic
+casuist/MS
+casuistry/SM
+cataclysmal
+cataclysmic
+cataclysm/MS
+catacomb/MS
+catafalque/SM
+Catalan/MS
+catalepsy/MS
+cataleptic/S
+Catalina/M
+cataloger/M
+catalog/SDRMZG
+Catalonia/M
+catalpa/SM
+catalysis/M
+catalyst/SM
+catalytic
+catalytically
+catalyze/DSG
+catamaran/MS
+catapult/MGSD
+cataract/MS
+Catarina/M
+catarrh/M
+catarrhs
+catastrophe/SM
+catastrophic
+catastrophically
+catatonia/MS
+catatonic/S
+Catawba/M
+catbird/MS
+catboat/SM
+catcall/SMDG
+catchable/U
+catchall/MS
+catch/BRSJLGZ
+catcher/M
+catchment/SM
+catchpenny/S
+catchphrase/S
+catchup/MS
+catchword/MS
+catchy/TR
+catechism/MS
+catechist/SM
+catechize/SDG
+catecholamine/MS
+categoric
+categorical/Y
+categorization/MS
+categorized/AU
+categorize/RSDGZ
+category/MS
+Cate/M
+catenate/NF
+catenation/MF
+catercorner
+caterer/M
+cater/GRDZ
+Caterina/M
+catering/M
+Caterpillar
+caterpillar/SM
+caterwaul/DSG
+catfish/MS
+catgut/SM
+Catha/M
+Catharina/M
+Catharine/M
+catharses
+catharsis/M
+cathartic/S
+Cathay/M
+cathedral/SM
+Cathee/M
+Catherina/M
+Catherine/M
+Catherin/M
+Cather/M
+Cathe/RM
+catheterize/GSD
+catheter/SM
+Cathie/M
+Cathi/M
+Cathleen/M
+Cathlene/M
+cathode/MS
+cathodic
+catholicism
+Catholicism/SM
+catholicity/MS
+catholic/MS
+Catholic/S
+Cathrine/M
+Cathrin/M
+Cathryn/M
+Cathyleen/M
+Cathy/M
+Catie/M
+Catiline/M
+Cati/M
+Catina/M
+cationic
+cation/MS
+catkin/SM
+Catlaina/M
+Catlee/M
+catlike
+Catlin/M
+catnapped
+catnapping
+catnap/SM
+catnip/MS
+Cato/M
+Catrina/M
+Catriona/M
+Catskill/SM
+cat/SMRZ
+catsup's
+cattail/SM
+catted
+cattery/M
+cattily
+cattiness/SM
+catting
+cattle/M
+cattleman/M
+cattlemen
+Catt/M
+catty/PRST
+Catullus/M
+CATV
+catwalk/MS
+Caty/M
+Caucasian/S
+Caucasoid/S
+Caucasus/M
+Cauchy/M
+caucus/SDMG
+caudal/Y
+caught/U
+cauldron/MS
+cauliflower/MS
+caulker/M
+caulk/JSGZRD
+causality/SM
+causal/YS
+causate/XVN
+causation/M
+causative/SY
+cause/DSRGMZ
+caused/U
+causeless
+causerie/MS
+causer/M
+causeway/SGDM
+caustically
+causticity/MS
+caustic/YS
+cauterization/SM
+cauterized/U
+cauterize/GSD
+cautionary
+cautioner/M
+caution/GJDRMSZ
+cautiousness's/I
+cautiousness/SM
+cautious/PIY
+cavalcade/MS
+cavalierness/M
+cavalier/SGYDP
+cavalryman/M
+cavalrymen
+cavalry/MS
+caveat/SM
+caveatted
+caveatting
+cave/GFRSD
+caveman/M
+cavemen
+Cavendish/M
+caver/M
+cavern/GSDM
+cavernous/Y
+cave's
+caviar/MS
+caviler/M
+cavil/SJRDGZ
+caving/MS
+cavity/MFS
+cavort/SDG
+Cavour/M
+caw/SMDG
+Caxton/M
+Caye/M
+Cayenne/M
+cayenne/SM
+Cayla/M
+Cayman/M
+cayman/SM
+cay's
+cay/SC
+Cayuga/M
+cayuse/SM
+Caz/M
+Cazzie/M
+c/B
+CB
+CBC
+Cb/M
+CBS
+cc
+Cchaddie/M
+CCTV
+CCU
+CD
+CDC/M
+Cd/M
+CDT
+Ce
+cease/DSCG
+ceasefire/S
+ceaselessness/SM
+ceaseless/YP
+ceasing/U
+Ceausescu/M
+Cebuano/M
+Cebu/M
+ceca
+cecal
+Cecelia/M
+Cece/M
+Cecile/M
+Ceciley/M
+Cecilia/M
+Cecilio/M
+Cecilius/M
+Cecilla/M
+Cecil/M
+Cecily/M
+cecum/M
+cedar/SM
+ceded/A
+cede/FRSDG
+ceder's/F
+ceder/SM
+cedes/A
+cedilla/SM
+ceding/A
+Ced/M
+Cedric/M
+ceilidh/M
+ceiling/MDS
+Ceil/M
+celandine/MS
+Celanese/M
+Celebes's
+celebrant/MS
+celebratedness/M
+celebrated/P
+celebrate/XSDGN
+celebration/M
+celebrator/MS
+celebratory
+celebrity/MS
+Cele/M
+Celene/M
+celerity/SM
+celery/SM
+Celesta/M
+celesta/SM
+Celeste/M
+celestial/YS
+Celestia/M
+Celestina/M
+Celestine/M
+Celestyna/M
+Celestyn/M
+Celia/M
+celibacy/MS
+celibate/SM
+Celie/M
+Celina/M
+Celinda/M
+Celine/M
+Celinka/M
+Celisse/M
+Celka/M
+cellarer/M
+cellar/RDMGS
+Celle/M
+cell/GMDS
+Cellini/M
+cellist/SM
+Cello/M
+cello/MS
+cellophane/SM
+cellphone/S
+cellular/SY
+cellulite/S
+celluloid/SM
+cellulose/SM
+Celsius/S
+Celtic/SM
+Celt/MS
+cementa
+cementer/M
+cementum/SM
+cement/ZGMRDS
+cemetery/MS
+cenobite/MS
+cenobitic
+cenotaph/M
+cenotaphs
+Cenozoic
+censer/MS
+censored/U
+censor/GDMS
+censorial
+censoriousness/MS
+censorious/YP
+censorship/MS
+censure/BRSDZMG
+censurer/M
+census/SDMG
+centaur/SM
+Centaurus/M
+centavo/SM
+centenarian/MS
+centenary/S
+centennial/YS
+center/AC
+centerboard/SM
+centered
+centerer/S
+centerfold/S
+centering/SM
+centerline/SM
+centerpiece/SM
+center's
+Centigrade
+centigrade/S
+centigram/SM
+centiliter/MS
+centime/SM
+centimeter/SM
+centipede/MS
+Centralia/M
+centralism/M
+centralist/M
+centrality/MS
+centralization/CAMS
+centralize/CGSD
+centralizer/SM
+centralizes/A
+central/STRY
+centrefold's
+Centrex
+CENTREX/M
+centric/F
+centrifugal/SY
+centrifugate/NM
+centrifugation/M
+centrifuge/GMSD
+centripetal/Y
+centrist/MS
+centroid/MS
+cent/SZMR
+centurion/MS
+century/MS
+CEO
+cephalic/S
+Cepheid
+Cepheus/M
+ceramicist/S
+ceramic/MS
+ceramist/MS
+cerate/MD
+Cerberus/M
+cereal/MS
+cerebellar
+cerebellum/MS
+cerebra
+cerebral/SY
+cerebrate/XSDGN
+cerebration/M
+cerebrum/MS
+cerement/SM
+ceremonial/YSP
+ceremoniousness/MS
+ceremoniousness's/U
+ceremonious/YUP
+ceremony/MS
+Cerenkov/M
+Ceres/M
+Cerf/M
+cerise/SM
+cerium/MS
+cermet/SM
+CERN/M
+certainer
+certainest
+certainty/UMS
+certain/UY
+cert/FS
+certifiable
+certifiably
+certificate/SDGM
+certification/AMC
+certified/U
+certifier/M
+certify/DRSZGNX
+certiorari/M
+certitude/ISM
+cerulean/MS
+Cervantes/M
+cervical
+cervices/M
+cervix/M
+Cesarean
+cesarean/S
+Cesare/M
+Cesar/M
+Cesaro/M
+cesium/MS
+cessation/SM
+cession/FAMSK
+Cessna/M
+cesspit/M
+cesspool/SM
+Cesya/M
+cetacean/S
+cetera/S
+Cetus/M
+Ceylonese
+Ceylon/M
+Cezanne/S
+cf
+CF
+CFC
+Cf/M
+CFO
+cg
+Chablis/SM
+Chaddie/M
+Chadd/M
+Chaddy/M
+Chadian/S
+Chad/M
+Chadwick/M
+chafe/GDSR
+chafer/M
+chaffer/DRG
+chafferer/M
+Chaffey/M
+chaff/GRDMS
+chaffinch/SM
+Chagall/M
+chagrin/DGMS
+Chaim/M
+chainlike
+chain's
+chainsaw/SGD
+chain/SGUD
+chairlady/M
+chairlift/MS
+chairman/MDGS
+chairmanship/MS
+chairmen
+chairperson/MS
+chair/SGDM
+chairwoman/M
+chairwomen
+chaise/SM
+chalcedony/MS
+Chaldea/M
+Chaldean/M
+chalet/SM
+chalice/DSM
+chalkboard/SM
+chalk/DSMG
+chalkiness/S
+chalkline
+chalky/RPT
+challenged/U
+challenger/M
+challenge/ZGSRD
+challenging/Y
+challis/SM
+Chalmers
+chamberer/M
+Chamberlain/M
+chamberlain/MS
+chambermaid/MS
+chamberpot/S
+Chambers/M
+chamber/SZGDRM
+chambray/MS
+chameleon/SM
+chamfer/DMGS
+chammy's
+chamois/DSMG
+chamomile/MS
+champagne/MS
+champaign/M
+champ/DGSZ
+champion/MDGS
+championship/MS
+Champlain/M
+chanced/M
+chance/GMRSD
+chancellery/SM
+chancellorship/SM
+chancellor/SM
+Chancellorsville/M
+chancel/SM
+Chance/M
+chancery/SM
+Chancey/M
+chanciness/S
+chancing/M
+chancre/SM
+chancy/RPT
+Chandal/M
+Chanda/M
+chandelier/SM
+Chandigarh/M
+Chandler/M
+chandler/MS
+Chandragupta/M
+Chandra/M
+Chandrasekhar/M
+Chandy/M
+Chanel/M
+Chane/M
+Chaney/M
+Changchun/M
+changeabilities
+changeability/UM
+changeableness/SM
+changeable/U
+changeably/U
+changed/U
+change/GZRSD
+changeless
+changeling/M
+changeover/SM
+changer/M
+changing/U
+Chang/M
+Changsha/M
+Chan/M
+Channa/M
+channeler/M
+channeling/M
+channelization/SM
+channelize/GDS
+channellings
+channel/MDRZSG
+Channing/M
+chanson/SM
+Chantalle/M
+Chantal/M
+chanter/M
+chanteuse/MS
+chantey/SM
+chanticleer/SM
+Chantilly/M
+chantry/MS
+chant/SJGZMRD
+chanty's
+Chanukah's
+Chao/M
+chaos/SM
+chaotic
+chaotically
+chaparral/MS
+chapbook/SM
+chapeau/MS
+chapel/MS
+chaperonage/MS
+chaperoned/U
+chaperone's
+chaperon/GMDS
+chaplaincy/MS
+chaplain/MS
+chaplet/SM
+Chaplin/M
+Chapman/M
+chap/MS
+Chappaquiddick/M
+chapped
+chapping
+chapter/SGDM
+Chara
+charabanc/MS
+characterful
+characteristically/U
+characteristic/SM
+characterizable/MS
+characterization/MS
+characterize/DRSBZG
+characterized/U
+characterizer/M
+characterless
+character/MDSG
+charade/SM
+charbroil/SDG
+charcoal/MGSD
+Chardonnay
+chardonnay/S
+chard/SM
+chargeableness/M
+chargeable/P
+charged/U
+charge/EGRSDA
+charger/AME
+chargers
+char/GS
+Charil/M
+charily
+chariness/MS
+Charin/M
+charioteer/GSDM
+Chariot/M
+chariot/SMDG
+Charis
+charisma/M
+charismata
+charismatically
+charismatic/S
+Charissa/M
+Charisse/M
+charitablenesses
+charitableness/UM
+charitable/UP
+charitably/U
+Charita/M
+Charity/M
+charity/MS
+charlady/M
+Charla/M
+charlatanism/MS
+charlatanry/SM
+charlatan/SM
+Charlean/M
+Charleen/M
+Charlemagne/M
+Charlena/M
+Charlene/M
+Charles/M
+Charleston/SM
+Charley/M
+Charlie/M
+Charline/M
+Charlot/M
+Charlotta/M
+Charlotte/M
+Charlottesville/M
+Charlottetown/M
+Charlton/M
+Charmaine/M
+Charmain/M
+Charmane/M
+charmer/M
+Charmian/M
+Charmine/M
+charming/RYT
+Charmin/M
+Charmion/M
+charmless
+charm/SGMZRD
+Charolais
+Charo/M
+Charon/M
+charred
+charring
+charted/U
+charter/AGDS
+chartered/U
+charterer/SM
+charter's
+chartist/SM
+Chartres/M
+chartreuse/MS
+chartroom/S
+chart/SJMRDGBZ
+charwoman/M
+charwomen
+Charybdis/M
+Charyl/M
+chary/PTR
+Chas
+chase/DSRGZ
+Chase/M
+chaser/M
+chasing/M
+Chasity/M
+chasm/SM
+chassis/M
+chastely
+chasteness/SM
+chasten/GSD
+chaste/UTR
+chastisement/SM
+chastiser/M
+chastise/ZGLDRS
+Chastity/M
+chastity/SM
+chastity's/U
+chasuble/SM
+Chateaubriand
+château/M
+chateaus
+châteaux
+châtelaine/SM
+chat/MS
+Chattahoochee/M
+Chattanooga/M
+chatted
+chattel/MS
+chatterbox/MS
+chatterer/M
+Chatterley/M
+chatter/SZGDRY
+Chatterton/M
+chattily
+chattiness/SM
+chatting
+chatty/RTP
+Chaucer/M
+chauffeur/GSMD
+Chaunce/M
+Chauncey/M
+Chautauqua/M
+chauvinism/MS
+chauvinistic
+chauvinistically
+chauvinist/MS
+Chavez/M
+chaw
+Chayefsky/M
+cheapen/DG
+cheapish
+cheapness/MS
+cheapskate/MS
+cheap/YRNTXSP
+cheater/M
+cheat/RDSGZ
+Chechen/M
+Chechnya/M
+checkable/U
+checkbook/MS
+checked/UA
+checkerboard/MS
+checker/DMG
+check/GZBSRDM
+checklist/S
+checkmate/MSDG
+checkoff/SM
+checkout/S
+checkpoint/MS
+checkroom/MS
+check's/A
+checks/A
+checksummed
+checksumming
+checksum/SM
+checkup/MS
+Cheddar/MS
+cheddar/S
+cheekbone/SM
+cheek/DMGS
+cheekily
+cheekiness/SM
+cheeky/PRT
+cheep/GMDS
+cheerer/M
+cheerfuller
+cheerfullest
+cheerfulness/MS
+cheerful/YP
+cheerily
+cheeriness/SM
+cheerio/S
+Cheerios/M
+cheerleader/SM
+cheerlessness/SM
+cheerless/PY
+cheers/S
+cheery/PTR
+cheer/YRDGZS
+cheeseburger/SM
+cheesecake/SM
+cheesecloth/M
+cheesecloths
+cheeseparing/S
+cheese/SDGM
+cheesiness/SM
+cheesy/PRT
+cheetah/M
+cheetahs
+Cheeto/M
+Cheever/M
+cheffed
+cheffing
+chef/SM
+Chekhov/M
+chelate/XDMNG
+chelation/M
+Chelsae/M
+Chelsea/M
+Chelsey/M
+Chelsie/M
+Chelsy/M
+Chelyabinsk/M
+chem
+Che/M
+chemic
+chemical/SYM
+chemiluminescence/M
+chemiluminescent
+chemise/SM
+chemistry/SM
+chemist/SM
+chemotherapeutic/S
+chemotherapy/SM
+chemurgy/SM
+Chengdu
+Cheng/M
+chenille/SM
+Chen/M
+Cheops/M
+Chere/M
+Cherey/M
+Cherianne/M
+Cherice/M
+Cherida/M
+Cherie/M
+Cherilyn/M
+Cherilynn/M
+Cheri/M
+Cherin/M
+Cherise/M
+cherisher/M
+cherish/GDRS
+Cherish/M
+Cheriton/M
+Cherlyn/M
+Cher/M
+Chernenko/M
+Chernobyl/M
+Cherokee/MS
+cheroot/MS
+Cherri/M
+Cherrita/M
+Cherry/M
+cherry/SM
+chert/MS
+cherubic
+cherubim/S
+cherub/SM
+chervil/MS
+Cherye/M
+Cheryl/M
+Chery/M
+Chesapeake/M
+Cheshire/M
+Cheslie/M
+chessboard/SM
+chessman/M
+chessmen
+chess/SM
+Chesterfield/M
+chesterfield/MS
+Chester/M
+Chesterton/M
+chestful/S
+chest/MRDS
+chestnut/SM
+Cheston/M
+chesty/TR
+Chet/M
+Chevalier/M
+chevalier/SM
+Cheviot/M
+cheviot/S
+Chev/M
+Chevrolet/M
+chevron/DMS
+Chevy/M
+chewer/M
+chew/GZSDR
+chewiness/S
+chewy/RTP
+Cheyenne/SM
+chg
+chge
+Chiang/M
+chianti/M
+Chianti/S
+chiaroscuro/SM
+Chiarra/M
+Chiba/M
+Chicagoan/SM
+Chicago/M
+Chicana/MS
+chicane/MGDS
+chicanery/MS
+Chicano/MS
+chichi/RTS
+chickadee/SM
+Chickasaw/SM
+chickenfeed
+chicken/GDM
+chickenhearted
+chickenpox/MS
+Chickie/M
+Chick/M
+chickpea/MS
+chickweed/MS
+chick/XSNM
+Chicky/M
+chicle/MS
+Chic/M
+chicness/S
+Chico/M
+chicory/MS
+chic/SYRPT
+chide/GDS
+chiding/Y
+chiefdom/MS
+chieftain/SM
+chief/YRMST
+chiffonier/MS
+chiffon/MS
+chigger/MS
+chignon/MS
+Chihuahua/MS
+chihuahua/S
+chilblain/MS
+childbearing/MS
+childbirth/M
+childbirths
+childcare/S
+childes
+child/GMYD
+childhood/MS
+childishness/SM
+childish/YP
+childlessness/SM
+childless/P
+childlikeness/M
+childlike/P
+childminders
+childproof/GSD
+childrearing
+children/M
+Chilean/S
+Chile/MS
+chile's
+chilies
+chili/M
+chiller/M
+chilliness/MS
+chilling/Y
+chilli's
+chill/MRDJGTZPS
+chillness/MS
+chilly/TPRS
+Chilton/M
+Chi/M
+chimaera's
+chimaerical
+Chimborazo/M
+chime/DSRGMZ
+Chimera/S
+chimera/SM
+chimeric
+chimerical
+chimer/M
+Chimiques
+chimney/SMD
+chimpanzee/SM
+chimp/MS
+chi/MS
+Chimu/M
+Ch'in
+China/M
+Chinaman/M
+Chinamen
+china/MS
+Chinatown/SM
+chinchilla/SM
+chine/MS
+Chinese/M
+Ching/M
+chink/DMSG
+chinless
+Chin/M
+chinned
+chinner/S
+chinning
+chino/MS
+Chinook/MS
+chin/SGDM
+chinstrap/S
+chintz/SM
+chintzy/TR
+chipboard/M
+Chipewyan/M
+Chip/M
+chipmunk/SM
+chipped
+Chippendale/M
+chipper/DGS
+Chippewa/MS
+chipping/MS
+chip/SM
+Chiquia/M
+Chiquita/M
+chiral
+Chirico/M
+chirography/SM
+chiropodist/SM
+chiropody/MS
+chiropractic/MS
+chiropractor/SM
+chirp/GDS
+chirpy/RT
+chirrup/DGS
+chiseler/M
+chisel/ZGSJMDR
+Chisholm/M
+Chisinau/M
+chitchat/SM
+chitchatted
+chitchatting
+chitinous
+chitin/SM
+chit/SM
+Chittagong/M
+chitterlings
+chivalric
+chivalrously/U
+chivalrousness/MS
+chivalrous/YP
+chivalry/SM
+chive/GMDS
+chivvy/D
+chivying
+chlamydiae
+chlamydia/S
+Chloe/M
+Chloette/M
+Chlo/M
+chloral/MS
+chlorate/M
+chlordane/MS
+chloride/MS
+chlorinated/C
+chlorinates/C
+chlorinate/XDSGN
+chlorination/M
+chlorine/MS
+Chloris
+chlorofluorocarbon/S
+chloroform/DMSG
+chlorophyll/SM
+chloroplast/MS
+chloroquine/M
+chm
+Ch/MGNRS
+chockablock
+chock/SGRDM
+chocoholic/S
+chocolate/MS
+chocolaty
+Choctaw/MS
+choiceness/M
+choice/RSMTYP
+choirboy/MS
+choirmaster/SM
+choir/SDMG
+chokeberry/M
+chokecherry/SM
+choke/DSRGZ
+choker/M
+chokes/M
+choking/Y
+cholera/SM
+choleric
+choler/SM
+cholesterol/SM
+choline/M
+cholinesterase/M
+chomp/DSG
+Chomsky/M
+Chongqing
+choose/GZRS
+chooser/M
+choosiness/S
+choosy/RPT
+chophouse/SM
+Chopin/M
+chopped
+chopper/SDMG
+choppily
+choppiness/MS
+chopping
+choppy/RPT
+chop/S
+chopstick/SM
+chorale/MS
+choral/SY
+chordal
+chordata
+chordate/MS
+chording/M
+chord/SGMD
+chorea/MS
+chore/DSGNM
+choreographer/M
+choreographic
+choreographically
+choreographs
+choreography/MS
+choreograph/ZGDR
+chorines
+chorion/M
+chorister/SM
+choroid/S
+chortler/M
+chortle/ZGDRS
+chorus/GDSM
+chosen/U
+chose/S
+Chou/M
+chowder/SGDM
+chow/DGMS
+Chretien/M
+Chris/M
+chrism/SM
+chrissake
+Chrisse/M
+Chrissie/M
+Chrissy/M
+Christabella/M
+Christabel/M
+Christalle/M
+Christal/M
+Christa/M
+Christan/M
+Christchurch/M
+Christean/M
+Christel/M
+Christendom/MS
+christened/U
+christening/SM
+Christen/M
+christen/SAGD
+Christensen/M
+Christenson/M
+Christiana/M
+Christiane/M
+Christianity/SM
+Christianize/GSD
+Christian/MS
+Christiano/M
+Christiansen/M
+Christians/N
+Christie/SM
+Christi/M
+Christina/M
+Christine/M
+Christin/M
+Christlike
+Christmas/SM
+Christmastide/SM
+Christmastime/S
+Christoffel/M
+Christoffer/M
+Christoforo/M
+Christoper/M
+Christophe/M
+Christopher/M
+Christoph/MR
+Christophorus/M
+Christos/M
+Christ/SMN
+Christye/M
+Christyna/M
+Christy's
+Chrisy/M
+chroma/M
+chromate/M
+chromatically
+chromaticism/M
+chromaticness/M
+chromatic/PS
+chromatics/M
+chromatin/MS
+chromatogram/MS
+chromatograph
+chromatographic
+chromatography/M
+chrome/GMSD
+chromic
+chromite/M
+chromium/SM
+chromosomal
+chromosome/MS
+chromosphere/M
+chronically
+chronicled/U
+chronicler/M
+chronicle/SRDMZG
+chronic/S
+chronograph/M
+chronographs
+chronography
+chronological/Y
+chronologist/MS
+chronology/MS
+chronometer/MS
+chronometric
+Chrotoem/M
+chrysalids
+chrysalis/SM
+Chrysa/M
+chrysanthemum/MS
+Chrysler/M
+Chrysostom/M
+Chrystal/M
+Chrystel/M
+Chryste/M
+chubbiness/SM
+chubby/RTP
+chub/MS
+Chucho/M
+chuck/GSDM
+chuckhole/SM
+chuckle/DSG
+chuckling/Y
+Chuck/M
+chuff/DM
+chugged
+chugging
+chug/MS
+Chukchi/M
+chukka/S
+Chumash/M
+chummed
+chummily
+chumminess/MS
+chumming
+chum/MS
+chummy/SRTP
+chumping/M
+chump/MDGS
+Chungking's
+Chung/M
+chunkiness/MS
+chunk/SGDM
+chunky/RPT
+chuntering
+churchgoer/SM
+churchgoing/SM
+Churchillian
+Churchill/M
+churchliness/M
+churchly/P
+churchman/M
+church/MDSYG
+churchmen
+Church/MS
+churchwarden/SM
+churchwoman/M
+churchwomen
+churchyard/SM
+churlishness/SM
+churlish/YP
+churl/SM
+churner/M
+churning/M
+churn/SGZRDM
+chute/DSGM
+chutney/MS
+chutzpah/M
+chutzpahs
+chutzpa/SM
+Chuvash/M
+ch/VT
+chyme/SM
+Ci
+CIA
+ciao/S
+cicada/MS
+cicatrice/S
+cicatrix's
+Cicely/M
+Cicero/M
+cicerone/MS
+ciceroni
+Ciceronian
+Cicily/M
+CID
+cider's/C
+cider/SM
+Cid/M
+Ciel/M
+cigarette/MS
+cigarillo/MS
+cigar/SM
+cilantro/S
+cilia/M
+ciliate/FDS
+ciliately
+cilium/M
+Cilka/M
+cinch/MSDG
+cinchona/SM
+Cincinnati/M
+cincture/MGSD
+Cinda/M
+Cindee/M
+Cindelyn/M
+cinder/DMGS
+Cinderella/MS
+Cindie/M
+Cindi/M
+Cindra/M
+Cindy/M
+cine/M
+cinema/SM
+cinematic
+cinematographer/MS
+cinematographic
+cinematography/MS
+Cinerama/M
+cinnabar/MS
+Cinnamon/M
+cinnamon/MS
+ciphered/C
+cipher/MSGD
+ciphers/C
+cir
+circa
+circadian
+Circe/M
+circler/M
+circle/RSDGM
+circlet/MS
+circuital
+circuit/GSMD
+circuitousness/MS
+circuitous/YP
+circuitry/SM
+circuity/MS
+circulant
+circularity/SM
+circularize/GSD
+circularness/M
+circular/PSMY
+circulate/ASDNG
+circulation/MA
+circulations
+circulative
+circulatory
+circumcise/DRSXNG
+circumcised/U
+circumciser/M
+circumcision/M
+circumference/SM
+circumferential/Y
+circumflex/MSDG
+circumlocution/MS
+circumlocutory
+circumnavigate/DSNGX
+circumnavigational
+circumnavigation/M
+circumpolar
+circumscribe/GSD
+circumscription/SM
+circumspection/SM
+circumspect/Y
+circumsphere
+circumstance/SDMG
+circumstantial/YS
+circumvention/MS
+circumvent/SBGD
+circus/SM
+Cirillo/M
+Cirilo/M
+Ciro/M
+cirque/SM
+cirrhoses
+cirrhosis/M
+cirrhotic/S
+cirri/M
+cirrus/M
+Cissiee/M
+Cissy/M
+cistern/SM
+citadel/SM
+citations/I
+citation/SMA
+cit/DSG
+cite/ISDAG
+Citibank/M
+citified
+citizenry/SM
+citizenship/MS
+citizen/SYM
+citrate/DM
+citric
+Citroen/M
+citronella/MS
+citron/MS
+citrus/SM
+city/DSM
+cityscape/MS
+citywide
+civet/SM
+civic/S
+civics/M
+civilian/SM
+civility/IMS
+civilizational/MS
+civilization/AMS
+civilizedness/M
+civilized/PU
+civilize/DRSZG
+civilizer/M
+civilizes/AU
+civil/UY
+civvies
+ck/C
+clack/SDG
+cladding/SM
+clads
+clad/U
+Claiborne/M
+Claiborn/M
+claimable
+claimant/MS
+claim/CDRSKAEGZ
+claimed/U
+claimer/KMACE
+Claire/M
+Clair/M
+Clairol/M
+clairvoyance/MS
+clairvoyant/YS
+clambake/MS
+clamberer/M
+clamber/SDRZG
+clammed
+clammily
+clamminess/MS
+clamming
+clam/MS
+clammy/TPR
+clamorer/M
+clamor/GDRMSZ
+clamorousness/UM
+clamorous/PUY
+clampdown/SM
+clamper/M
+clamp/MRDGS
+clamshell/MS
+Clancy/M
+clandestineness/M
+clandestine/YP
+clanger/M
+clangor/MDSG
+clangorous/Y
+clang/SGZRD
+clanking/Y
+clank/SGDM
+clan/MS
+clannishness/SM
+clannish/PY
+clansman/M
+clansmen
+clapboard/SDGM
+Clapeyron/M
+clapped
+clapper/GMDS
+clapping
+clap/S
+Clapton/M
+claptrap/SM
+claque/MS
+Clarabelle/M
+Clara/M
+Clarance/M
+Clare/M
+Claremont/M
+Clarence/M
+Clarendon/M
+Claresta/M
+Clareta/M
+claret/MDGS
+Claretta/M
+Clarette/M
+Clarey/M
+Claribel/M
+Clarice/M
+Clarie/M
+clarification/M
+clarifier/M
+clarify/NGXDRS
+Clari/M
+Clarinda/M
+Clarine/M
+clarinetist/SM
+clarinet/SM
+clarinettist's
+clarion/GSMD
+Clarissa/M
+Clarisse/M
+Clarita/M
+clarities
+clarity/UM
+Clarke/M
+Clark/M
+Clarridge/M
+Clary/M
+clasher/M
+clash/RSDG
+clasped/M
+clasper/M
+clasp's
+clasp/UGSD
+classer/M
+class/GRSDM
+classical/Y
+classicism/SM
+classicist/SM
+classic/S
+classics/M
+classifiable/U
+classification/AMC
+classificatory
+classified/S
+classifier/SM
+classify/CNXASDG
+classiness/SM
+classless/P
+classmate/MS
+classroom/MS
+classwork/M
+classy/PRT
+clatterer/M
+clattering/Y
+clatter/SGDR
+clattery
+Claudelle/M
+Claudell/M
+Claude/M
+Claudetta/M
+Claudette/M
+Claudia/M
+Claudian/M
+Claudianus/M
+Claudie/M
+Claudina/M
+Claudine/M
+Claudio/M
+Claudius/M
+clausal
+clause/MS
+Clausen/M
+Clausewitz/M
+Clausius/M
+Claus/NM
+claustrophobia/SM
+claustrophobic
+clave/RM
+clave's/F
+clavichord/SM
+clavicle/MS
+clavier/MS
+clawer/M
+claw/GDRMS
+Clayborne/M
+Clayborn/M
+Claybourne/M
+clayey
+clayier
+clayiest
+Clay/M
+clay/MDGS
+claymore/MS
+Clayson/M
+Clayton/M
+Clea/M
+cleanable
+cleaner/MS
+cleaning/SM
+cleanliness/UMS
+cleanly/PRTU
+cleanness/MSU
+cleanse
+cleanser/M
+cleans/GDRSZ
+cleanup/MS
+clean/UYRDPT
+clearance/MS
+clearcut
+clearer/M
+clearheadedness/M
+clearheaded/PY
+clearinghouse/S
+clearing/MS
+clearly
+clearness/MS
+clears
+clear/UTRD
+Clearwater/M
+clearway/M
+cleat/MDSG
+cleavage/MS
+cleaver/M
+cleave/RSDGZ
+Cleavland/M
+clef/SM
+cleft/MDGS
+clematis/MS
+clemence
+Clemenceau/M
+Clemence/M
+clemency/ISM
+Clemente/M
+Clementia/M
+Clementina/M
+Clementine/M
+Clementius/M
+clement/IY
+Clement/MS
+clements
+Clemmie/M
+Clemmy/M
+Clemons
+Clemson/M
+Clem/XM
+clenches
+clenching
+clench/UD
+Cleo/M
+Cleon/M
+Cleopatra/M
+Clerc/M
+clerestory/MS
+clergyman/M
+clergymen
+clergy/MS
+clergywoman
+clergywomen
+clericalism/SM
+clerical/YS
+cleric/SM
+Clerissa/M
+clerk/SGYDM
+clerkship/MS
+Cletis
+Cletus/M
+Cleveland/M
+Cleve/M
+cleverness/SM
+clever/RYPT
+Clevey/M
+Clevie/M
+clevis/SM
+clew/DMGS
+cl/GJ
+Cliburn/M
+clichéd
+cliché/SM
+clicker/M
+click/GZSRDM
+clientèle/SM
+client/SM
+cliffhanger/MS
+cliffhanging
+Cliff/M
+Clifford/M
+cliff/SM
+Clifton/M
+climacteric/SM
+climactic
+climate/MS
+climatic
+climatically
+climatological/Y
+climatologist/SM
+climatology/MS
+climax/MDSG
+climbable/U
+climb/BGZSJRD
+climbdown
+climbed/U
+climber/M
+clime/SM
+Clim/M
+clinch/DRSZG
+clincher/M
+clinching/Y
+Cline/M
+clinger/MS
+clinging
+cling/U
+clingy/TR
+clinical/Y
+clinician/MS
+clinic/MS
+clinker/GMD
+clink/RDGSZ
+clinometer/MIS
+Clint/M
+Clinton/M
+Clio/M
+cliometrician/S
+cliometric/S
+clipboard/SM
+clipped/U
+clipper/MS
+clipping/SM
+clip/SM
+clique/SDGM
+cliquey
+cliquier
+cliquiest
+cliquishness/SM
+cliquish/YP
+clitoral
+clitorides
+clitoris/MS
+Clive/M
+cloacae
+cloaca/M
+cloakroom/MS
+cloak's
+cloak/USDG
+clobber/DGS
+cloche/MS
+clocker/M
+clockmaker/M
+clock/SGZRDMJ
+clockwatcher
+clockwise
+clockwork/MS
+clodded
+clodding
+cloddishness/M
+cloddish/P
+clodhopper/SM
+clod/MS
+Cloe/M
+clogged/U
+clogging/U
+clog's
+clog/US
+cloisonné
+cloisonnes
+cloister/MDGS
+cloistral
+Clo/M
+clomp/MDSG
+clonal
+clone/DSRGMZ
+clonk/SGD
+clopped
+clopping
+clop/S
+Cloris/M
+closed/U
+close/EDSRG
+closefisted
+closely
+closemouthed
+closeness/MS
+closeout/MS
+closer/EM
+closers
+closest
+closet/MDSG
+closeup/S
+closing/S
+closured
+closure/EMS
+closure's/I
+closuring
+clothbound
+clothesbrush
+clotheshorse/MS
+clothesline/SDGM
+clothesman
+clothesmen
+clothespin/MS
+clothe/UDSG
+cloth/GJMSD
+clothier/MS
+clothing/M
+Clotho/M
+cloths
+Clotilda/M
+clot/MS
+clotted
+clotting
+cloture/MDSG
+cloudburst/MS
+clouded/U
+cloudiness/SM
+cloudlessness/M
+cloudless/YP
+cloudscape/SM
+cloud/SGMD
+cloudy/TPR
+clout/GSMD
+cloven
+cloverleaf/MS
+clover/M
+clove/SRMZ
+Clovis/M
+clown/DMSG
+clownishness/SM
+clownish/PY
+cloy/DSG
+cloying/Y
+clubbed/M
+clubbing/M
+clubfeet
+clubfoot/DM
+clubhouse/SM
+club/MS
+clubroom/SM
+cluck/GSDM
+clueless
+clue/MGDS
+Cluj/M
+clump/MDGS
+clumpy/RT
+clumsily
+clumsiness/MS
+clumsy/PRT
+clung
+clunk/SGZRDM
+clunky/PRYT
+clustered/AU
+clusters/A
+cluster/SGJMD
+clutch/DSG
+cluttered/U
+clutter/GSD
+Cl/VM
+Clyde/M
+Clydesdale/M
+Cly/M
+Clytemnestra/M
+Clyve/M
+Clywd/M
+cm
+Cm/M
+CMOS
+cnidarian/MS
+CNN
+CNS
+CO
+coacher/M
+coachman/M
+coachmen
+coach/MSRDG
+coachwork/M
+coadjutor/MS
+coagulable
+coagulant/SM
+coagulate/GNXSD
+coagulation/M
+coagulator/S
+coaler/M
+coalesce/GDS
+coalescence/SM
+coalescent
+coalface/SM
+coalfield/MS
+coalitionist/SM
+coalition/MS
+coal/MDRGS
+coalminers
+coarseness/SM
+coarsen/SGD
+coarse/TYRP
+coastal
+coaster/M
+coastguard/MS
+coastline/SM
+coast/SMRDGZ
+coated/U
+Coates/M
+coating/M
+coat/MDRGZJS
+coattail/S
+coattest
+coauthor/MDGS
+coaxer/M
+coax/GZDSR
+coaxial/Y
+coaxing/Y
+Cobain/M
+cobalt/MS
+cobbed
+Cobbie/M
+cobbing
+cobbler/M
+cobble/SRDGMZ
+cobblestone/MSD
+Cobb/M
+Cobby/M
+coble/M
+Cob/M
+COBOL
+Cobol/M
+cobra/MS
+cob/SM
+cobwebbed
+cobwebbing
+cobwebby/RT
+cobweb/SM
+cocaine/MS
+coca/MS
+cocci/MS
+coccus/M
+coccyges
+coccyx/M
+Cochabamba/M
+cochineal/SM
+Cochin/M
+Cochise/M
+cochleae
+cochlear
+cochlea/SM
+Cochran/M
+cockade/SM
+cockamamie
+cockatoo/SM
+cockatrice/MS
+cockcrow/MS
+cockerel/MS
+cocker/M
+cockeye/DM
+cockeyed/PY
+cockfighting/M
+cockfight/MJSG
+cock/GDRMS
+cockily
+cockiness/MS
+cocklebur/M
+cockle/SDGM
+cockleshell/SM
+Cockney
+cockney/MS
+cockpit/MS
+cockroach/SM
+cockscomb/SM
+cockshies
+cocksucker/S
+cocksure
+cocktail/GDMS
+cocky/RPT
+cocoa/SM
+coco/MS
+coconut/SM
+cocoon/GDMS
+Cocteau/M
+COD
+coda/SM
+codded
+codding
+coddle/GSRD
+coddler/M
+codebook/S
+codebreak/R
+coded/UA
+Codee/M
+codeine/MS
+codename/D
+codependency/S
+codependent/S
+coder/CM
+code's
+co/DES
+codes/A
+code/SCZGJRD
+codetermine/S
+codeword/SM
+codex/M
+codfish/SM
+codger/MS
+codices/M
+codicil/SM
+Codie/M
+codification/M
+codifier/M
+codify/NZXGRSD
+Codi/M
+coding/M
+codling/M
+Cod/M
+cod/MDRSZGJ
+codpiece/MS
+Cody/M
+coedited
+coediting
+coeditor/MS
+coedits
+coed/SM
+coeducational
+coeducation/SM
+coefficient/SYM
+coelenterate/MS
+coequal/SY
+coercer/M
+coerce/SRDXVGNZ
+coercible/I
+coercion/M
+coerciveness/M
+coercive/PY
+coeval/YS
+coexistence/MS
+coexistent
+coexist/GDS
+coextensive/Y
+cofactor/MS
+coffeecake/SM
+coffeecup
+coffeehouse/SM
+coffeemaker/S
+coffeepot/MS
+coffee/SM
+cofferdam/SM
+coffer/DMSG
+Coffey/M
+coffin/DMGS
+Coffman/M
+cogency/MS
+cogent/Y
+cogged
+cogging
+cogitate/DSXNGV
+cogitation/M
+cogitator/MS
+cog/MS
+Cognac/M
+cognac/SM
+cognate/SXYN
+cognation/M
+cognitional
+cognition/SAM
+cognitive/SY
+cognizable
+cognizance/MAI
+cognizances/A
+cognizant/I
+cognomen/SM
+cognoscente
+cognoscenti
+cogwheel/SM
+cohabitant/MS
+cohabitational
+cohabitation/SM
+cohabit/SDG
+Cohan/M
+coheir/MS
+Cohen/M
+cohere/GSRD
+coherence/SIM
+coherencies
+coherency/I
+coherent/IY
+coherer/M
+cohesion/MS
+cohesiveness/SM
+cohesive/PY
+Cohn/M
+cohoes
+coho/MS
+cohort/SM
+coiffed
+coiffing
+coiffure/MGSD
+coif/SM
+coil/UGSAD
+Coimbatore/M
+coinage's/A
+coinage/SM
+coincide/GSD
+coincidence/MS
+coincidental/Y
+coincident/Y
+coined/U
+coiner/M
+coin/GZSDRM
+coinsurance/SM
+Cointon/M
+cointreau
+coital/Y
+coitus/SM
+coke/MGDS
+Coke/MS
+COL
+COLA
+colander/SM
+Colan/M
+Colas
+cola/SM
+colatitude/MS
+Colbert/M
+Colby/M
+coldblooded
+coldish
+coldness/MS
+cold/YRPST
+Coleen/M
+Cole/M
+Coleman/M
+Colene/M
+Coleridge/M
+coleslaw/SM
+Colet/M
+Coletta/M
+Colette/M
+coleus/SM
+Colfax/M
+Colgate/M
+colicky
+colic/SM
+coliform
+Colin/M
+coliseum/SM
+colitis/MS
+collaborate/VGNXSD
+collaboration/M
+collaborative/SY
+collaborator/SM
+collage/MGSD
+collagen/M
+collapse/SDG
+collapsibility/M
+collapsible
+collarbone/MS
+collar/DMGS
+collard/SM
+collarless
+collated/U
+collateral/SYM
+collate/SDVNGX
+collation/M
+collator/MS
+colleague/SDGM
+collectedness/M
+collected/PY
+collectible/S
+collection/AMS
+collective/SY
+collectivism/SM
+collectivist/MS
+collectivity/MS
+collectivization/MS
+collectivize/DSG
+collector/MS
+collect/SAGD
+Colleen/M
+colleen/SM
+college/SM
+collegiality/S
+collegian/SM
+collegiate/Y
+Collen/M
+Collete/M
+Collette/M
+coll/G
+collide/SDG
+Collie/M
+collie/MZSRD
+collier/M
+Collier/M
+colliery/MS
+collimate/C
+collimated/U
+collimates
+collimating
+collimation/M
+collimator/M
+collinear
+collinearity/M
+Colline/M
+Collin/MS
+collisional
+collision/SM
+collocate/XSDGN
+collocation/M
+colloidal/Y
+colloid/MS
+colloq
+colloquialism/MS
+colloquial/SY
+colloquies
+colloquium/SM
+colloquy/M
+collude/SDG
+collusion/SM
+collusive
+collying
+Colly/RM
+Colman/M
+Col/MY
+Cologne/M
+cologne/MSD
+Colo/M
+Colombia/M
+Colombian/S
+Colombo/M
+colonelcy/MS
+colonel/MS
+colonialism/MS
+colonialist/MS
+colonial/SPY
+colonist/SM
+colonization/ACSM
+colonize/ACSDG
+colonized/U
+colonizer/MS
+colonizes/U
+Colon/M
+colonnade/MSD
+colon/SM
+colony/SM
+colophon/SM
+Coloradan/S
+Coloradoan/S
+Colorado/M
+colorant/SM
+coloration/EMS
+coloratura/SM
+colorblindness/S
+colorblind/P
+colored/USE
+colorer/M
+colorfastness/SM
+colorfast/P
+colorfulness/MS
+colorful/PY
+colorimeter/SM
+colorimetry
+coloring/M
+colorization/S
+colorize/GSD
+colorizing/C
+colorlessness/SM
+colorless/PY
+colors/EA
+color/SRDMGZJ
+colossal/Y
+Colosseum/M
+colossi
+colossus/M
+colostomy/SM
+colostrum/SM
+col/SD
+colter/M
+coltishness/M
+coltish/PY
+Colt/M
+colt/MRS
+Coltrane/M
+Columbia/M
+Columbian
+Columbine/M
+columbine/SM
+Columbus/M
+columnar
+columnist/MS
+columnize/GSD
+column/SDM
+Colver/M
+Co/M
+comae
+comaker/SM
+Comanche/MS
+coma/SM
+comatose
+combatant/SM
+combativeness/MS
+combative/PY
+combat/SVGMD
+combed/U
+comber/M
+combinational/A
+combination/ASM
+combinatorial/Y
+combinatoric/S
+combinator/SM
+combined/AU
+combiner/M
+combines/A
+combine/ZGBRSD
+combining/A
+combo/MS
+comb/SGZDRMJ
+Combs/M
+combusted
+combustibility/SM
+combustible/SI
+combustion/MS
+combustive
+Comdex/M
+Comdr/M
+comeback/SM
+comedian/SM
+comedic
+comedienne/SM
+comedown/MS
+comedy/SM
+come/IZSRGJ
+comeliness/SM
+comely/TPR
+comer/IM
+comes/M
+comestible/MS
+cometary
+cometh
+comet/SM
+comeuppance/SM
+comfit's
+comfit/SE
+comfortability/S
+comfortableness/MS
+comfortable/U
+comfortably/U
+comforted/U
+comforter/MS
+comfort/ESMDG
+comforting/YE
+comfy/RT
+comicality/MS
+comical/Y
+comic/MS
+Cominform/M
+comity/SM
+com/LJRTZG
+comm
+Com/M
+comma/MS
+commandant/MS
+commandeer/SDG
+commander/M
+commanding/Y
+commandment/SM
+commando/SM
+command/SZRDMGL
+commemorate/SDVNGX
+commemoration/M
+commemorative/YS
+commemorator/S
+commence/ALDSG
+commencement/AMS
+commencer/M
+commendably
+commendation/ASM
+commendatory/A
+commender/AM
+commend/GSADRB
+commensurable/I
+commensurate/IY
+commensurates
+commensuration/SM
+commentary/MS
+commentate/GSD
+commentator/SM
+commenter/M
+comment's
+comment/SUGD
+commerce/MGSD
+commercialism/MS
+commercialization/SM
+commercialize/GSD
+commercial/PYS
+Commie
+commie/SM
+commingle/GSD
+commiserate/VGNXSD
+commiseration/M
+commissariat/MS
+commissar/MS
+commissary/MS
+commission/ASCGD
+commissioner/SM
+commission's/A
+commitment/SM
+commit/SA
+committable
+committal/MA
+committals
+committed/UA
+committeeman/M
+committeemen
+committee/MS
+committeewoman/M
+committeewomen
+committing/A
+commode/MS
+commodes/IE
+commodiousness/MI
+commodious/YIP
+commodity/MS
+commodore/SM
+commonality/MS
+commonalty/MS
+commoner/MS
+commonness/MSU
+commonplaceness/M
+commonplace/SP
+common/RYUPT
+commonsense
+commons/M
+Commons/M
+commonweal/SHM
+commonwealth/M
+Commonwealth/M
+commonwealths
+Commonwealths
+commotion/MS
+communality/M
+communal/Y
+commune/XSDNG
+communicability/MS
+communicable/IU
+communicably
+communicant/MS
+communicate/VNGXSD
+communicational
+communication/M
+communicativeness/M
+communicative/PY
+communicator/SM
+communion/M
+Communion/SM
+communique/S
+communism/MS
+Communism/S
+communistic
+communist/MS
+Communist/S
+communitarian/M
+community/MS
+communize/SDG
+commutable/I
+commutate/XVGNSD
+commutation/M
+commutative/Y
+commutativity
+commutator/MS
+commute/BZGRSD
+commuter/M
+Comoros
+compaction/M
+compactness/MS
+compactor/MS
+compact/TZGSPRDY
+companionableness/M
+companionable/P
+companionably
+companion/GBSMD
+companionship/MS
+companionway/MS
+company/MSDG
+Compaq/M
+comparabilities
+comparability/IM
+comparableness/M
+comparable/P
+comparably/I
+comparativeness/M
+comparative/PYS
+comparator/SM
+compare/GRSDB
+comparer/M
+comparison/MS
+compartmental
+compartmentalization/SM
+compartmentalize/DSG
+compartment/SDMG
+compassionateness/M
+compassionate/PSDGY
+compassion/MS
+compass/MSDG
+compatibility/IMS
+compatibleness/M
+compatible/SI
+compatibly/I
+compatriot/SM
+compeer/DSGM
+compellable
+compelled
+compelling/YM
+compel/S
+compendious
+compendium/MS
+compensable
+compensated/U
+compensate/XVNGSD
+compensation/M
+compensator/M
+compensatory
+compete/GSD
+competence/ISM
+competency/IS
+competency's
+competent/IY
+competition/SM
+competitiveness/SM
+competitive/YP
+competitor/MS
+comp/GSYD
+compilable/U
+compilation/SAM
+compile/ASDCG
+compiler/CS
+compiler's
+complacence/S
+complacency/SM
+complacent/Y
+complainant/MS
+complainer/M
+complain/GZRDS
+complaining/YU
+complaint/MS
+complaisance/SM
+complaisant/Y
+complected
+complementariness/M
+complementarity
+complementary/SP
+complementation/M
+complementer/M
+complement/ZSMRDG
+complete/BTYVNGPRSDX
+completed/U
+completely/I
+completeness/ISM
+completer/M
+completion/MI
+complexional
+complexion/DMS
+complexity/MS
+complexness/M
+complex/TGPRSDY
+compliance/SM
+compliant/Y
+complicatedness/M
+complicated/YP
+complicate/SDG
+complication/M
+complicator/SM
+complicit
+complicity/MS
+complier/M
+complimentary/U
+complimenter/M
+compliment/ZSMRDG
+comply/ZXRSDNG
+component/SM
+comport/GLSD
+comportment/SM
+compose/CGASDE
+composedness/M
+composed/PY
+composer/CM
+composers
+composite/YSDXNG
+compositional/Y
+composition/CMA
+compositions/C
+compositor/MS
+compost/DMGS
+composure/ESM
+compote/MS
+compounded/U
+compounder/M
+compound/RDMBGS
+comprehend/DGS
+comprehending/U
+comprehensibility/SIM
+comprehensibleness/IM
+comprehensible/PI
+comprehensibly/I
+comprehension/IMS
+comprehensiveness/SM
+comprehensive/YPS
+compressed/Y
+compressibility/IM
+compressible/I
+compressional
+compression/CSM
+compressive/Y
+compressor/MS
+compress/SDUGC
+comprise/GSD
+compromiser/M
+compromise/SRDGMZ
+compromising/UY
+Compton/M
+comptroller/SM
+compulsion/SM
+compulsiveness/MS
+compulsive/PYS
+compulsivity
+compulsorily
+compulsory/S
+compunction/MS
+Compuserve/M
+CompuServe/M
+computability/M
+computable/UI
+computably
+computational/Y
+computation/SM
+computed/A
+computerese
+computerization/MS
+computerize/SDG
+computer/M
+compute/RSDZBG
+computes/A
+computing/A
+comradely/P
+comradeship/MS
+comrade/YMS
+Comte/M
+Conakry/M
+Conan/M
+Conant/M
+concatenate/XSDG
+concaveness/MS
+concave/YP
+conceal/BSZGRDL
+concealed/U
+concealer/M
+concealing/Y
+concealment/MS
+conceded/Y
+conceitedness/SM
+conceited/YP
+conceit/SGDM
+conceivable/IU
+conceivably/I
+conceive/BGRSD
+conceiver/M
+concentrate/VNGSDX
+concentration/M
+concentrator/MS
+concentrically
+Concepción/M
+conceptional
+conception/MS
+concept/SVM
+conceptuality/M
+conceptualization/A
+conceptualizations
+conceptualization's
+conceptualize/DRSG
+conceptualizing/A
+conceptual/Y
+concerned/YU
+concern/USGD
+concerted/PY
+concert/EDSG
+concertina/MDGS
+concertize/GDS
+concertmaster/MS
+concerto/SM
+concert's
+concessionaire/SM
+concessional
+concessionary
+concession/R
+Concetta/M
+Concettina/M
+Conchita/M
+conch/MDG
+conchs
+concierge/SM
+conciliar
+conciliate/GNVX
+conciliation/ASM
+conciliator/MS
+conciliatory/A
+conciseness/SM
+concise/TYRNPX
+concision/M
+conclave/S
+concluder/M
+conclude/RSDG
+conclusion/SM
+conclusive/IPY
+conclusiveness/ISM
+concocter/M
+concoction/SM
+concoct/RDVGS
+concomitant/YS
+concordance/MS
+concordant/Y
+concordat/SM
+Concorde/M
+Concordia/M
+Concord/MS
+concourse
+concreteness/MS
+concrete/NGXRSDPYM
+concretion/M
+concubinage/SM
+concubine/SM
+concupiscence/SM
+concupiscent
+concurrence/MS
+concur/S
+concussion/MS
+concuss/VD
+condemnate/XN
+condemnation/M
+condemnatory
+condemner/M
+condemn/ZSGRDB
+condensate/NMXS
+condensation/M
+condenser/M
+condense/ZGSD
+condensible
+condescend
+condescending/Y
+condescension/MS
+condign
+condiment/SM
+condition/AGSJD
+conditionals
+conditional/UY
+conditioned/U
+conditioner/MS
+conditioning/M
+condition's
+condole
+condolence/MS
+condominium/MS
+condom/SM
+condone/GRSD
+condoner/M
+Condorcet/M
+condor/MS
+condo/SM
+conduce/VGSD
+conduciveness/M
+conducive/P
+conductance/SM
+conductibility/SM
+conductible
+conduction/MS
+conductive/Y
+conductivity/MS
+conductor/MS
+conductress/MS
+conduct/V
+conduit/MS
+coneflower/M
+Conestoga
+coney's
+confabbed
+confabbing
+confab/MS
+confabulate/XSDGN
+confabulation/M
+confectioner/M
+confectionery/SM
+confectionist
+confection/RDMGZS
+confect/S
+Confederacy/M
+confederacy/MS
+confederate/M
+Confederate/S
+conferee/MS
+conference/DSGM
+conferrable
+conferral/SM
+conferred
+conferrer/SM
+conferring
+confer/SB
+confessed/Y
+confessional/SY
+confession/MS
+confessor/SM
+confetti/M
+confidante/SM
+confidant/SM
+confidence/SM
+confidentiality/MS
+confidentialness/M
+confidential/PY
+confident/Y
+confider/M
+confide/ZGRSD
+confiding/PY
+configuration/ASM
+configure/AGSDB
+confined/U
+confine/L
+confinement/MS
+confiner/M
+confirm/AGDS
+confirmation/ASM
+confirmatory
+confirmedness/M
+confirmed/YP
+confiscate/DSGNX
+confiscation/M
+confiscator/MS
+confiscatory
+conflagration/MS
+conflate/NGSDX
+conflation/M
+conflicting/Y
+conflict/SVGDM
+confluence/MS
+conformable/U
+conformal
+conformance/SM
+conformational/Y
+conform/B
+conformer/M
+conformism/SM
+conformist/SM
+conformities
+conformity/MUI
+confounded/Y
+confound/R
+confrère/MS
+confrontational
+confrontation/SM
+confronter/M
+confront/Z
+Confucianism/SM
+Confucian/S
+Confucius/M
+confusedness/M
+confused/PY
+confuse/RBZ
+confusing/Y
+confutation/MS
+confute/GRSD
+confuter/M
+conga/MDG
+congeal/GSDL
+congealment/MS
+congeniality/UM
+congenial/U
+congeries/M
+conger/SM
+congestion/MS
+congest/VGSD
+conglomerate/XDSNGVM
+conglomeration/M
+Cong/M
+Congolese
+Congo/M
+congrats
+congratulate/NGXSD
+congratulation/M
+congratulatory
+congregate/DSXGN
+congregational
+Congregational
+congregationalism/MS
+congregationalist/MS
+Congregationalist/S
+congregation/M
+congressional/Y
+congressman/M
+congressmen
+Congress/MS
+congress/MSDG
+congresspeople
+congressperson/S
+congresswoman/M
+congresswomen
+Congreve/M
+congruence/IM
+congruences
+congruency/M
+congruential
+congruent/YI
+congruity/MSI
+congruousness/IM
+congruous/YIP
+conicalness/M
+conical/PSY
+conic/S
+conics/M
+conifer/MS
+coniferous
+conjectural/Y
+conjecture/GMDRS
+conjecturer/M
+conjoint
+conjugacy
+conjugal/Y
+conjugate/XVNGYSDP
+conjugation/M
+conjunct/DSV
+conjunctiva/MS
+conjunctive/YS
+conjunctivitis/SM
+conjuration/MS
+conjurer/M
+conjure/RSDZG
+conjuring/M
+conker/M
+conk/ZDR
+Conley/M
+Con/M
+conman
+connect/ADGES
+connectedly/E
+connectedness/ME
+connected/U
+connectible
+Connecticut/M
+connection/AME
+connectionless
+connections/E
+connective/SYM
+connectivity/MS
+connector/MS
+Connelly/M
+Conner/M
+Connery/M
+connexion/MS
+Conney/M
+conn/GVDR
+Connie/M
+Conni/M
+conniption/MS
+connivance/MS
+conniver/M
+connive/ZGRSD
+connoisseur/MS
+Connor/SM
+connotative/Y
+Conn/RM
+connubial/Y
+Conny/M
+conquerable/U
+conquered/AU
+conqueror/MS
+conquer/RDSBZG
+conquers/A
+conquest/ASM
+conquistador/MS
+Conrade/M
+Conrad/M
+Conrado/M
+Conrail/M
+Conroy/M
+Consalve/M
+consanguineous/Y
+consanguinity/SM
+conscienceless
+conscientiousness/MS
+conscientious/YP
+conscionable/U
+consciousness/MUS
+conscious/UYSP
+conscription/SM
+consecrated/AU
+consecrates/A
+consecrate/XDSNGV
+consecrating/A
+consecration/AMS
+consecutiveness/M
+consecutive/YP
+consensus/SM
+consenter/M
+consenting/Y
+consent/SZGRD
+consequence
+consequentiality/S
+consequential/IY
+consequentialness/M
+consequently/I
+consequent/PSY
+conservancy/SM
+conservationism
+conservationist/SM
+conservation/SM
+conservatism/SM
+conservativeness/M
+Conservative/S
+conservative/SYP
+conservator/MS
+conservatory/MS
+con/SGM
+considerable/I
+considerables
+considerably/I
+considerateness/MSI
+considerate/XIPNY
+consideration/ASMI
+considered/U
+considerer/M
+consider/GASD
+considering/S
+consign/ASGD
+consignee/SM
+consignment/SM
+consist/DSG
+consistence/S
+consistency/IMS
+consistent/IY
+consistory/MS
+consolable/I
+Consolata/M
+consolation/MS
+consolation's/E
+consolatory
+consoled/U
+consoler/M
+console/ZBG
+consolidated/AU
+consolidate/NGDSX
+consolidates/A
+consolidation/M
+consolidator/SM
+consoling/Y
+consommé/S
+consonance/IM
+consonances
+consonantal
+consonant/MYS
+consortia
+consortium/M
+conspectus/MS
+conspicuousness/IMS
+conspicuous/YIP
+conspiracy/MS
+conspiratorial/Y
+conspirator/SM
+constable
+Constable/M
+constabulary/MS
+constance
+Constance/M
+Constancia/M
+constancy/IMS
+Constancy/M
+Constanta/M
+Constantia/M
+Constantina/M
+Constantine/M
+Constantin/M
+Constantino/M
+Constantinople/M
+constant/IY
+constants
+constellation/SM
+consternate/XNGSD
+consternation/M
+constipate/XDSNG
+constipation/M
+constituency/MS
+constituent/SYM
+constituted/A
+constitute/NGVXDS
+constitutes/A
+constituting/A
+Constitution
+constitutionality's
+constitutionality/US
+constitutionally/U
+constitutional/SY
+constitution/AMS
+constitutive/Y
+constrain
+constrainedly
+constrained/U
+constraint/MS
+constriction/MS
+constrictor/MS
+constrict/SDGV
+construable
+construct/ASDGV
+constructibility
+constructible/A
+constructional/Y
+constructionist/MS
+construction/MAS
+constructions/C
+constructiveness/SM
+constructive/YP
+constructor/MS
+construe/GSD
+Consuela/M
+Consuelo/M
+consular/S
+consulate/MS
+consul/KMS
+consulship/MS
+consultancy/S
+consultant/MS
+consultation/SM
+consultative
+consulted/A
+consulter/M
+consult/RDVGS
+consumable/S
+consumed/Y
+consume/JZGSDB
+consumerism/MS
+consumerist/S
+consumer/M
+consuming/Y
+consummate/DSGVY
+consummated/U
+consumption/SM
+consumptive/YS
+cont
+contact/BGD
+contacted/A
+contact's/A
+contacts/A
+contagion/SM
+contagiousness/MS
+contagious/YP
+containerization/SM
+containerize/GSD
+container/M
+containment/SM
+contain/SLZGBRD
+contaminant/SM
+contaminated/AU
+contaminates/A
+contaminate/SDCXNG
+contaminating/A
+contamination/CM
+contaminative
+contaminator/MS
+contd
+cont'd
+contemn/SGD
+contemplate/DVNGX
+contemplation/M
+contemplativeness/M
+contemplative/PSY
+contemporaneity/MS
+contemporaneousness/M
+contemporaneous/PY
+contemptibleness/M
+contemptible/P
+contemptibly
+contempt/M
+contemptuousness/SM
+contemptuous/PY
+contentedly/E
+contentedness/SM
+contented/YP
+content/EMDLSG
+contention/MS
+contentiousness/SM
+contentious/PY
+contently
+contentment/ES
+contentment's
+conterminous/Y
+contestable/I
+contestant/SM
+contested/U
+contextualize/GDS
+contiguity/MS
+contiguousness/M
+contiguous/YP
+continence/ISM
+Continental/S
+continental/SY
+continent/IY
+Continent/M
+continents
+continent's
+contingency/SM
+contingent/SMY
+continua
+continuable
+continual/Y
+continuance/ESM
+continuant/M
+continuation/ESM
+continue/ESDG
+continuer/M
+continuity/SEM
+continuousness/M
+continuous/YE
+continuum/M
+contortionist/SM
+contortion/MS
+contort/VGD
+contour
+contraband/SM
+contrabass/M
+contraception/SM
+contraceptive/S
+contract/DG
+contractible
+contractile
+contractual/Y
+contradict/GDS
+contradiction/MS
+contradictorily
+contradictoriness/M
+contradictory/PS
+contradistinction/MS
+contraflow/S
+contrail/M
+contraindicate/SDVNGX
+contraindication/M
+contralto/SM
+contrapositive/S
+contraption/MS
+contrapuntal/Y
+contrariety/MS
+contrarily
+contrariness/MS
+contrariwise
+contrary/PS
+contra/S
+contrasting/Y
+contrastive/Y
+contrast/SRDVGZ
+contravene/GSRD
+contravener/M
+contravention/MS
+Contreras/M
+contretemps/M
+contribute/XVNZRD
+contribution/M
+contributive/Y
+contributorily
+contributor/SM
+contributory/S
+contriteness/M
+contrite/NXP
+contrition/M
+contrivance/SM
+contriver/M
+contrive/ZGRSD
+control/CS
+controllability/M
+controllable/IU
+controllably/U
+controlled/CU
+controller/SM
+controlling/C
+control's
+controversialists
+controversial/UY
+controversy/MS
+controvert/DGS
+controvertible/I
+contumacious/Y
+contumacy/MS
+contumelious
+contumely/MS
+contuse/NGXSD
+contusion/M
+conundrum/SM
+conurbation/MS
+convalesce/GDS
+convalescence/SM
+convalescent/S
+convect/DSVG
+convectional
+convection/MS
+convector
+convene/ASDG
+convener/MS
+convenience/ISM
+convenient/IY
+conventicle/SM
+conventionalism/M
+conventionalist/M
+conventionality/SUM
+conventionalize/GDS
+conventional/UY
+convention/MA
+conventions
+convergence/MS
+convergent
+conversant/Y
+conversationalist/SM
+conversational/Y
+conversation/SM
+conversazione/M
+converse/Y
+conversion/AM
+conversioning
+converted/U
+converter/MS
+convert/GADS
+convertibility's/I
+convertibility/SM
+convertibleness/M
+convertible/PS
+convexity/MS
+convex/Y
+conveyance/DRSGMZ
+conveyancer/M
+conveyancing/M
+convey/BDGS
+conveyor/MS
+conviction/MS
+convict/SVGD
+convinced/U
+convincer/M
+convince/RSDZG
+convincingness/M
+convincing/PUY
+conviviality/MS
+convivial/Y
+convoke/GSD
+convolute/XDNY
+convolution/M
+convolve/C
+convolved
+convolves
+convolving
+convoy/GMDS
+convulse/SDXVNG
+convulsion/M
+convulsiveness/M
+convulsive/YP
+Conway/M
+cony/SM
+coo/GSD
+cookbook/SM
+cooked/AU
+Cooke/M
+cooker/M
+cookery/MS
+cook/GZDRMJS
+Cookie/M
+cookie/SM
+cooking/M
+Cook/M
+cookout/SM
+cooks/A
+cookware/SM
+cooky's
+coolant/SM
+cooled/U
+cooler/M
+Cooley/M
+coolheaded
+Coolidge/M
+coolie/MS
+coolness/MS
+cool/YDRPJGZTS
+coon/MS
+coonskin/MS
+cooperage/MS
+cooperate/VNGXSD
+cooperation/M
+cooperativeness/SM
+cooperative/PSY
+cooperator/MS
+cooper/GDM
+Cooper/M
+coop/MDRGZS
+Coop/MR
+coordinated/U
+coordinateness/M
+coordinate/XNGVYPDS
+coordination/M
+coordinator/MS
+Coors/M
+cootie/SM
+coot/MS
+copay/S
+Copeland/M
+Copenhagen/M
+coper/M
+Copernican
+Copernicus/M
+cope/S
+copied/A
+copier/M
+copies/A
+copilot/SM
+coping/M
+copiousness/SM
+copious/YP
+coplanar
+Copland/M
+Copley/M
+copolymer/MS
+copora
+copped
+Copperfield/M
+copperhead/MS
+copper/MSGD
+copperplate/MS
+coppersmith/M
+coppersmiths
+coppery
+coppice's
+copping
+Coppola/M
+copra/MS
+coprolite/M
+coprophagous
+copse/M
+cops/GDS
+cop/SJMDRG
+copter/SM
+Coptic/M
+copula/MS
+copulate/XDSNGV
+copulation/M
+copulative/S
+copybook/MS
+copycat/SM
+copycatted
+copycatting
+copyist/SM
+copy/MZBDSRG
+copyrighter/M
+copyright/MSRDGZ
+copywriter/MS
+coquetry/MS
+coquette/DSMG
+coquettish/Y
+Corabella/M
+Corabelle/M
+Corabel/M
+coracle/SM
+Coralie/M
+Coraline/M
+coralline
+Coral/M
+coral/SM
+Coralyn/M
+Cora/M
+corbel/GMDJS
+Corbet/M
+Corbett/M
+Corbie/M
+Corbin/M
+Corby/M
+cordage/MS
+corded/AE
+Cordelia/M
+Cordelie/M
+Cordell/M
+corder/AM
+Cordey/M
+cord/FSAEM
+cordiality/MS
+cordialness/M
+cordial/PYS
+Cordie/M
+cordillera/MS
+Cordilleras
+Cordi/M
+cording/MA
+cordite/MS
+cordless
+Cord/M
+Cordoba
+cordon/DMSG
+cordovan/SM
+Cordula/M
+corduroy/GDMS
+Cordy/M
+cored/A
+Coreen/M
+Corella/M
+core/MZGDRS
+Corenda/M
+Corene/M
+corer/M
+corespondent/MS
+Coretta/M
+Corette/M
+Corey/M
+Corfu/M
+corgi/MS
+coriander/SM
+Corie/M
+Corilla/M
+Cori/M
+Corina/M
+Corine/M
+coring/M
+Corinna/M
+Corinne/M
+Corinthian/S
+Corinthians/M
+Corinth/M
+Coriolanus/M
+Coriolis/M
+Corissa/M
+Coriss/M
+corked/U
+corker/M
+cork/GZDRMS
+Cork/M
+corkscrew/DMGS
+corks/U
+Corliss/M
+Corly/M
+Cormack/M
+corm/MS
+cormorant/MS
+Cornall/M
+cornball/SM
+cornbread/S
+corncob/SM
+corncrake/M
+corneal
+cornea/SM
+Corneille/M
+Cornela/M
+Cornelia/M
+Cornelius/M
+Cornelle/M
+Cornell/M
+corner/GDM
+cornerstone/MS
+cornet/SM
+Corney/M
+cornfield/SM
+cornflake/S
+cornflour/M
+cornflower/SM
+corn/GZDRMS
+cornice/GSDM
+Cornie/M
+cornily
+corniness/S
+Cornish/S
+cornmeal/S
+cornrow/GDS
+cornstalk/MS
+cornstarch/SM
+cornucopia/MS
+Cornwallis/M
+Cornwall/M
+Corny/M
+corny/RPT
+corolla/MS
+corollary/SM
+Coronado/M
+coronal/MS
+coronary/S
+corona/SM
+coronate/NX
+coronation/M
+coroner/MS
+coronet/DMS
+Corot/M
+coroutine/SM
+Corp
+corporal/SYM
+corpora/MS
+corporate/INVXS
+corporately
+corporation/MI
+corporatism/M
+corporatist
+corporeality/MS
+corporeal/IY
+corporealness/M
+corp/S
+corpse/M
+corpsman/M
+corpsmen
+corps/SM
+corpulence/MS
+corpulentness/S
+corpulent/YP
+corpuscle/SM
+corpuscular
+corpus/M
+corr
+corralled
+corralling
+corral/MS
+correctable/U
+correct/BPSDRYTGV
+corrected/U
+correctional
+correction/MS
+corrective/YPS
+correctly/I
+correctness/MSI
+corrector/MS
+Correggio/M
+correlated/U
+correlate/SDXVNG
+correlation/M
+correlative/YS
+Correna/M
+correspond/DSG
+correspondence/MS
+correspondent/SM
+corresponding/Y
+Correy/M
+Corrianne/M
+corridor/SM
+Corrie/M
+corrigenda
+corrigendum/M
+corrigible/I
+Corri/M
+Corrina/M
+Corrine/M
+Corrinne/M
+corroborated/U
+corroborate/GNVXDS
+corroboration/M
+corroborative/Y
+corroborator/MS
+corroboratory
+corrode/SDG
+corrodible
+corrosion/SM
+corrosiveness/M
+corrosive/YPS
+corrugate/NGXSD
+corrugation/M
+corrupt/DRYPTSGV
+corrupted/U
+corrupter/M
+corruptibility/SMI
+corruptible/I
+corruption/IM
+corruptions
+corruptive/Y
+corruptness/MS
+Corry/M
+corsage/MS
+corsair/SM
+corset/GMDS
+Corsica/M
+Corsican/S
+cortège/MS
+Cortes/S
+cortex/M
+Cortez's
+cortical/Y
+cortices
+corticosteroid/SM
+Cortie/M
+cortisone/SM
+Cortland/M
+Cort/M
+Cortney/M
+Corty/M
+corundum/MS
+coruscate/XSDGN
+coruscation/M
+Corvallis/M
+corvette/MS
+Corvus/M
+Cory/M
+Cos
+Cosby/M
+Cosetta/M
+Cosette/M
+cos/GDS
+cosignatory/MS
+cosign/SRDZG
+cosily
+Cosimo/M
+cosine/MS
+cosiness/MS
+Cosme/M
+cosmetically
+cosmetician/MS
+cosmetic/SM
+cosmetologist/MS
+cosmetology/MS
+cosmic
+cosmical/Y
+cosmogonist/MS
+cosmogony/SM
+cosmological/Y
+cosmologist/MS
+cosmology/SM
+Cosmo/M
+cosmonaut/MS
+cosmopolitanism/MS
+cosmopolitan/SM
+cosmos/SM
+cosponsor/DSG
+cossack/S
+Cossack/SM
+cosset/GDS
+Costa/M
+Costanza/M
+costarred
+costarring
+costar/S
+Costello/M
+costiveness/M
+costive/PY
+costless
+costliness/SM
+costly/RTP
+cost/MYGVJS
+Costner/M
+costumer/M
+costume/ZMGSRD
+cotangent/SM
+Cote/M
+cote/MS
+coterie/MS
+coterminous/Y
+cotillion/SM
+Cotonou/M
+Cotopaxi/M
+cot/SGMD
+cottager/M
+cottage/ZMGSRD
+cottar's
+cotted
+cotter/SDM
+cotton/GSDM
+Cotton/M
+cottonmouth/M
+cottonmouths
+cottonseed/MS
+cottontail/SM
+cottonwood/SM
+cottony
+cotyledon/MS
+couching/M
+couch/MSDG
+cougar/MS
+cougher/M
+cough/RDG
+coughs
+couldn't
+could/T
+could've
+coulée/MS
+Coulomb/M
+coulomb/SM
+councilman/M
+councilmen
+councilor/MS
+councilperson/S
+council/SM
+councilwoman/M
+councilwomen
+counsel/GSDM
+counsellings
+counselor/MS
+countability/E
+countable/U
+countably/U
+countdown/SM
+counted/U
+count/EGARDS
+countenance/EGDS
+countenancer/M
+countenance's
+counteract/DSVG
+counteraction/SM
+counterargument/SM
+counterattack/DRMGS
+counterbalance/MSDG
+counterclaim/GSDM
+counterclockwise
+counterculture/MS
+countercyclical
+counterespionage/MS
+counterexample/S
+counterfeiter/M
+counterfeit/ZSGRD
+counterflow
+counterfoil/MS
+counterforce/M
+counter/GSMD
+counterinsurgency/MS
+counterintelligence/MS
+counterintuitive
+countermand/DSG
+counterman/M
+countermeasure/SM
+countermen
+counteroffensive/SM
+counteroffer/SM
+counterpane/SM
+counterpart/SM
+counterpoint/GSDM
+counterpoise/GMSD
+counterproductive
+counterproposal/M
+counterrevolutionary/MS
+counterrevolution/MS
+counter's/E
+counters/E
+countersignature/MS
+countersign/SDG
+countersink/SG
+counterspy/MS
+counterstrike
+countersunk
+countertenor/SM
+countervail/DSG
+counterweight/GMDS
+countess/MS
+countless/Y
+countrify/D
+countryman/M
+countrymen
+country/MS
+countryside/MS
+countrywide
+countrywoman/M
+countrywomen
+county/SM
+coup/ASDG
+coupe/MS
+Couperin/M
+couple/ACU
+coupled/CU
+coupler/C
+couplers
+coupler's
+couple's
+couples/CU
+couplet/SM
+coupling's/C
+coupling/SM
+coupon/SM
+coup's
+courage/MS
+courageously
+courageousness/MS
+courageous/U
+courages/E
+Courbet/M
+courgette/MS
+courier/GMDS
+course/EGSRDM
+courser's/E
+courser/SM
+course's/AF
+courses/FA
+coursework
+coursing/M
+Courtenay/M
+courteousness/EM
+courteousnesses
+courteous/PEY
+courtesan/MS
+courtesied
+courtesy/ESM
+courtesying
+court/GZMYRDS
+courthouse/MS
+courtier/SM
+courtliness/MS
+courtly/RTP
+Court/M
+Courtnay/M
+Courtney/M
+courtroom/MS
+courtship/SM
+courtyard/SM
+couscous/MS
+cousinly/U
+cousin/YMS
+Cousteau/M
+couture/SM
+couturier/SM
+covalent/Y
+covariance/SM
+covariant/S
+covariate/SN
+covary
+cove/DRSMZG
+covenanted/U
+covenanter/M
+covenant/SGRDM
+coven/SM
+Covent/M
+Coventry/MS
+coverable/E
+cover/AEGUDS
+coverage/MS
+coverall/DMS
+coverer/AME
+covering/MS
+coverlet/MS
+coversheet
+covers/M
+covertness/SM
+covert/YPS
+coveter/M
+coveting/Y
+covetousness/SM
+covetous/PY
+covet/SGRD
+covey/SM
+covington
+cowardice/MS
+cowardliness/MS
+cowardly/P
+Coward/M
+coward/MYS
+cowbell/MS
+cowbird/MS
+cowboy/MS
+cowcatcher/SM
+cowed/Y
+cowering/Y
+cower/RDGZ
+cowgirl/MS
+cowhand/S
+cowherd/SM
+cowhide/MGSD
+Cowley/M
+cowlick/MS
+cowling/M
+cowl/SGMD
+cowman/M
+cow/MDRSZG
+cowmen
+coworker/MS
+Cowper/M
+cowpoke/MS
+cowpony
+cowpox/MS
+cowpuncher/M
+cowpunch/RZ
+cowrie/SM
+cowshed/SM
+cowslip/MS
+coxcomb/MS
+Cox/M
+cox/MDSG
+coxswain/GSMD
+coy/CDSG
+coyer
+coyest
+coyly
+Coy/M
+coyness/MS
+coyote/SM
+coypu/SM
+cozenage/MS
+cozen/SGD
+cozily
+coziness/MS
+Cozmo/M
+Cozumel/M
+cozy/DSRTPG
+CPA
+cpd
+CPI
+cpl
+Cpl
+CPO
+CPR
+cps
+CPU/SM
+crabapple
+crabbedness/M
+crabbed/YP
+Crabbe/M
+crabber/MS
+crabbily
+crabbiness/S
+crabbing/M
+crabby/PRT
+crabgrass/S
+crablike
+crab/MS
+crackable/U
+crackdown/MS
+crackerjack/S
+cracker/M
+crackle/GJDS
+crackling/M
+crackly/RT
+crackpot/SM
+crackup/S
+crack/ZSBYRDG
+cradler/M
+cradle/SRDGM
+cradling/M
+craftily
+craftiness/SM
+Craft/M
+craft/MRDSG
+craftsman/M
+craftsmanship/SM
+craftsmen
+craftspeople
+craftspersons
+craftswoman
+craftswomen
+crafty/TRP
+Craggie/M
+cragginess/SM
+Craggy/M
+craggy/RTP
+crag/SM
+Craig/M
+Cramer/M
+crammed
+crammer/M
+cramming
+cramper/M
+cramp/MRDGS
+crampon/SM
+cram/S
+Cranach/M
+cranberry/SM
+Crandall/M
+crane/DSGM
+cranelike
+Crane/M
+Cranford/M
+cranial
+cranium/MS
+crankcase/MS
+crankily
+crankiness/MS
+crank/SGTRDM
+crankshaft/MS
+cranky/TRP
+Cranmer/M
+cranny/DSGM
+Cranston/M
+crape/SM
+crapped
+crappie/M
+crapping
+crappy/RST
+crapshooter/SM
+crap/SMDG
+crasher/M
+crashing/Y
+crash/SRDGZ
+crassness/MS
+crass/TYRP
+crate/DSRGMZ
+crater/DMG
+Crater/M
+cravat/SM
+cravatted
+cravatting
+crave/DSRGJ
+cravenness/SM
+craven/SPYDG
+craver/M
+craving/M
+crawdad/S
+crawfish's
+Crawford/M
+crawler/M
+crawl/RDSGZ
+crawlspace/S
+crawlway
+crawly/TRS
+craw/SYM
+crayfish/GSDM
+Crayola/M
+crayon/GSDM
+Cray/SM
+craze/GMDS
+crazily
+craziness/MS
+crazy/SRTP
+creakily
+creakiness/SM
+creak/SDG
+creaky/PTR
+creamer/M
+creamery/MS
+creamily
+creaminess/SM
+cream/SMRDGZ
+creamy/TRP
+creased/CU
+crease/IDRSG
+crease's
+creases/C
+creasing/C
+created/U
+create/XKVNGADS
+creationism/MS
+creationist/MS
+Creation/M
+creation/MAK
+creativeness/SM
+creative/YP
+creativities
+creativity/K
+creativity's
+Creator/M
+creator/MS
+creatureliness/M
+creaturely/P
+creature/YMS
+crèche/SM
+credence/MS
+credent
+credential/SGMD
+credenza/SM
+credibility/IMS
+credible/I
+credibly/I
+creditability/M
+creditableness/M
+creditable/P
+creditably/E
+credited/U
+credit/EGBSD
+creditor/MS
+credit's
+creditworthiness
+credo/SM
+credulity/ISM
+credulous/IY
+credulousness/SM
+creedal
+creed/C
+creeds
+creed's
+creekside
+creek/SM
+Creek/SM
+creel/SMDG
+Cree/MDS
+creeper/M
+creepily
+creepiness/SM
+creep/SGZR
+creepy/PRST
+Creigh/M
+Creight/M
+Creighton/M
+cremate/XDSNG
+cremation/M
+crematoria
+crematorium/MS
+crematory/S
+creme/S
+crenelate/XGNSD
+crenelation/M
+Creole/MS
+creole/SM
+Creon/M
+creosote/MGDS
+crepe/DSGM
+crept
+crescendoed
+crescendoing
+crescendo/SCM
+crescent/MS
+cress/S
+crestfallenness/M
+crestfallen/PY
+cresting/M
+crestless
+crest/SGMD
+Crestview/M
+cretaceous
+Cretaceously/M
+Cretaceous/Y
+Cretan/S
+Crete/M
+cretinism/MS
+cretin/MS
+cretinous
+cretonne/SM
+crevasse/DSMG
+crevice/SM
+crew/DMGS
+crewel/SM
+crewelwork/SM
+crewman/M
+crewmen
+cribbage/SM
+cribbed
+cribber/SM
+cribbing/M
+crib/SM
+Crichton/M
+cricketer/M
+cricket/SMZRDG
+crick/GDSM
+Crick/M
+cried/C
+crier/CM
+cries/C
+Crimea/M
+Crimean
+crime/GMDS
+criminality/MS
+criminalization/C
+criminalize/GC
+criminal/SYM
+criminologist/SM
+criminology/MS
+crimper/M
+crimp/RDGS
+crimson/DMSG
+cringer/M
+cringe/SRDG
+crinkle/DSG
+crinkly/TRS
+Crin/M
+crinoline/SM
+cripple/GMZDRS
+crippler/M
+crippling/Y
+Crisco/M
+crises
+crisis/M
+Cris/M
+crisper/M
+crispiness/SM
+crispness/MS
+crisp/PGTYRDS
+crispy/RPT
+criss
+crisscross/GDS
+Crissie/M
+Crissy/M
+Cristabel/M
+Cristal/M
+Crista/M
+Cristen/M
+Cristian/M
+Cristiano/M
+Cristie/M
+Cristi/M
+Cristina/M
+Cristine/M
+Cristin/M
+Cristionna/M
+Cristobal/M
+Cristy/M
+criteria
+criterion/M
+criticality
+critically/U
+criticalness/M
+critical/YP
+criticism/MS
+criticized/U
+criticize/GSRDZ
+criticizer/M
+criticizes/A
+criticizingly/S
+criticizing/UY
+critic/MS
+critique/MGSD
+critter/SM
+Cr/M
+croaker/M
+croak/SRDGZ
+croaky/RT
+Croatia/M
+Croatian/S
+Croat/SM
+Croce/M
+crocheter/M
+crochet/RDSZJG
+crockery/SM
+Crockett/M
+Crockpot/M
+crock/SGRDM
+crocodile/MS
+crocus/SM
+Croesus/SM
+crofter/M
+croft/MRGZS
+croissant/MS
+Croix/M
+Cromwellian
+Cromwell/M
+crone/SM
+Cronin/M
+Cronkite/M
+Cronus/M
+crony/SM
+crookedness/SM
+crooked/TPRY
+Crookes/M
+crookneck/MS
+crook/SGDM
+crooner/M
+croon/SRDGZ
+cropland/MS
+crop/MS
+cropped
+cropper/SM
+cropping
+croquet/MDSG
+croquette/SM
+Crosby/M
+crosier/SM
+crossarm
+crossbarred
+crossbarring
+crossbar/SM
+crossbeam/MS
+crossbones
+crossbowman/M
+crossbowmen
+crossbow/SM
+crossbred/S
+crossbreed/SG
+crosscheck/SGD
+crosscurrent/SM
+crosscut/SM
+crosscutting
+crossed/UA
+crosses/UA
+crossfire/SM
+crosshatch/GDS
+crossing/M
+Cross/M
+crossness/MS
+crossover/MS
+crosspatch/MS
+crosspiece/SM
+crosspoint
+crossproduct/S
+crossroad/GSM
+crossroads/M
+crosstalk/M
+crosstown
+crosswalk/MS
+crossway/M
+crosswind/SM
+crosswise
+crossword/MS
+cross/ZTYSRDMPBJG
+crotchetiness/M
+crotchet/MS
+crotchety/P
+crotchless
+crotch/MDS
+crouch/DSG
+croupier/M
+croup/SMDG
+croupy/TZR
+croûton/MS
+crowbait
+crowbarred
+crowbarring
+crowbar/SM
+crowdedness/M
+crowded/P
+crowd/MRDSG
+crowfeet
+crowfoot/M
+crow/GDMS
+Crowley/M
+crowned/U
+crowner/M
+crown/RDMSJG
+crozier's
+CRT/S
+crucial/Y
+crucible/MS
+crucifiable
+crucifixion/MS
+Crucifixion/MS
+crucifix/SM
+cruciform/S
+crucify/NGDS
+crudded
+crudding
+cruddy/TR
+crudeness/MS
+crude/YSP
+crudités
+crudity/MS
+crud/STMR
+cruelness/MS
+cruelty/SM
+cruel/YRTSP
+cruet/MS
+cruft
+crufty
+Cruikshank/M
+cruise/GZSRD
+cruiser/M
+cruller/SM
+crumb/GSYDM
+crumble/DSJG
+crumbliness/MS
+crumbly/PTRS
+crumby/RT
+crumminess/S
+crummy/SRTP
+crump
+crumpet/SM
+crumple/DSG
+crunch/DSRGZ
+crunchiness/MS
+crunchy/TRP
+crupper/MS
+crusade/GDSRMZ
+crusader/M
+cruse/MS
+crushable/U
+crusher/M
+crushing/Y
+crushproof
+crush/SRDBGZ
+Crusoe/M
+crustacean/MS
+crustal
+crust/GMDS
+crustily
+crustiness/SM
+crusty/SRTP
+crutch/MDSG
+Crux/M
+crux/MS
+Cruz/M
+crybaby/MS
+cry/JGDRSZ
+cryogenic/S
+cryogenics/M
+cryostat/M
+cryosurgery/SM
+cryptanalysis/M
+cryptanalyst/M
+cryptanalytic
+crypt/CS
+cryptic
+cryptically
+cryptogram/MS
+cryptographer/MS
+cryptographic
+cryptographically
+cryptography/MS
+cryptologic
+cryptological
+cryptologist/M
+cryptology/M
+Cryptozoic/M
+crypt's
+crystalline/S
+crystallite/SM
+crystallization/AMS
+crystallized/UA
+crystallizes/A
+crystallize/SRDZG
+crystallizing/A
+crystallographer/MS
+crystallographic
+crystallography/M
+Crystal/M
+crystal/SM
+Crysta/M
+Crystie/M
+Cs
+C's
+cs/EA
+cs's
+CST
+ct
+CT
+Cthrine/M
+Ct/M
+ctn
+ctr
+Cuba/M
+Cuban/S
+cubbed
+cubbing
+cubbyhole/MS
+cuber/M
+cube/SM
+cubical/Y
+cubicle/SM
+cubic/YS
+cubism/SM
+cubist/MS
+cubit/MS
+cub/MDRSZG
+cuboid
+Cuchulain/M
+cuckold/GSDM
+cuckoldry/MS
+cuckoo/SGDM
+cucumber/MS
+cuddle/GSD
+cuddly/TRP
+cu/DG
+cudgel/GSJMD
+cud/MS
+cue/MS
+cuff/GSDM
+Cuisinart/M
+cuisine/MS
+Culbertson/M
+culinary
+Cullan/M
+cull/DRGS
+cullender's
+Cullen/M
+culler/M
+Culley/M
+Cullie/M
+Cullin/M
+Cull/MN
+Cully/M
+culminate/XSDGN
+culmination/M
+culotte/S
+culpability/MS
+culpable/I
+culpableness/M
+culpably
+culpa/SM
+culprit/SM
+cultism/SM
+cultist/SM
+cultivable
+cultivated/U
+cultivate/XBSDGN
+cultivation/M
+cultivator/SM
+cult/MS
+cultural/Y
+cultured/U
+culture/SDGM
+Culver/MS
+culvert/SM
+Cu/M
+cumber/DSG
+Cumberland/M
+cumbersomeness/MS
+cumbersome/YP
+cumbrous
+cumin/MS
+cummerbund/MS
+Cummings
+cumquat's
+cum/S
+cumulate/XVNGSD
+cumulation/M
+cumulative/Y
+cumuli
+cumulonimbi
+cumulonimbus/M
+cumulus/M
+Cunard/M
+cuneiform/S
+cunnilingus/SM
+Cunningham/M
+cunningness/M
+cunning/RYSPT
+cunt/SM
+cupboard/SM
+cupcake/SM
+Cupertino/M
+cupful/SM
+cupidinously
+cupidity/MS
+Cupid/M
+cupid/S
+cup/MS
+cupola/MDGS
+cupped
+cupping/M
+cupric
+cuprous
+curability/MS
+curable/IP
+curableness/MI
+curably/I
+Curacao/M
+curacy/SM
+curare/MS
+curate/VGMSD
+curative/YS
+curatorial
+curator/KMS
+curbing/M
+curbside
+curb/SJDMG
+curbstone/MS
+Curcio/M
+curdle/SDG
+curd/SMDG
+cured/U
+cure/KBDRSGZ
+curer/MK
+curettage/SM
+curfew/SM
+curfs
+curiae
+curia/M
+cur/IBS
+Curie/M
+curie/SM
+curiosity/SM
+curio/SM
+curiousness/SM
+curious/TPRY
+Curitiba/M
+curium/MS
+curler/SM
+curlew/MS
+curlicue/MGDS
+curliness/SM
+curling/M
+curl/UDSG
+curlycue's
+curly/PRT
+curmudgeon/MYS
+Curran/M
+currant/SM
+curred/AFI
+currency's
+currency/SF
+current/FSY
+currently/A
+currentness/M
+Currey/M
+curricle/M
+curricula
+curricular
+curriculum/M
+Currie/M
+currier/M
+Currier/M
+curring/FAI
+Curr/M
+currycomb/DMGS
+Curry/MR
+curry/RSDMG
+cur's
+curs/ASDVG
+curse/A
+cursedness/M
+cursed/YRPT
+curse's
+cursive/EPYA
+cursiveness/EM
+cursives
+cursor/DMSG
+cursorily
+cursoriness/SM
+cursory/P
+curtailer/M
+curtail/LSGDR
+curtailment/SM
+curtain/GSMD
+Curtice/M
+Curtis/M
+Curt/M
+curtness/MS
+curtsey's
+curtsy/SDMG
+curt/TYRP
+curvaceousness/S
+curvaceous/YP
+curvature/MS
+curved/A
+curved's
+curve/DSGM
+curvilinearity/M
+curvilinear/Y
+curving/M
+curvy/RT
+cushion/SMDG
+Cushman/M
+cushy/TR
+cuspid/MS
+cuspidor/MS
+cusp/MS
+cussedness/M
+cussed/YP
+cuss/EGDSR
+cusses/F
+cussing/F
+cuss's
+custard/MS
+Custer/M
+custodial
+custodianship/MS
+custodian/SM
+custody/MS
+customarily
+customariness/M
+customary/PS
+customer/M
+customhouse/S
+customization/SM
+customize/ZGBSRD
+custom/SMRZ
+cutaneous/Y
+cutaway/SM
+cutback/SM
+cuteness/MS
+cute/SPY
+cutesy/RT
+cuticle/SM
+cutlass/MS
+cutler/SM
+cutlery/MS
+cutlet/SM
+cut/MRST
+cutoff/MS
+cutout/SM
+cutter/SM
+cutthroat/SM
+cutting/MYS
+cuttlebone/SM
+cuttlefish/MS
+cuttle/M
+cutup/MS
+cutworm/MS
+Cuvier/M
+Cuzco/M
+CV
+cw
+cwt
+Cyanamid/M
+cyanate/M
+cyanic
+cyanide/GMSD
+cyan/MS
+cyanogen/M
+Cybele/M
+cybernetic/S
+cybernetics/M
+cyberpunk/S
+cyberspace/S
+Cybill/M
+Cybil/M
+Cyb/M
+cyborg/S
+Cyclades
+cyclamen/MS
+cycle/ASDG
+cycler
+cycle's
+cycleway/S
+cyclic
+cyclical/SY
+cycling/M
+cyclist/MS
+cyclohexanol
+cycloidal
+cycloid/SM
+cyclometer/MS
+cyclone/SM
+cyclonic
+cyclopean
+cyclopedia/MS
+cyclopes
+Cyclopes
+cyclops
+Cyclops/M
+cyclotron/MS
+cyder/SM
+cygnet/MS
+Cygnus/M
+cylinder/GMDS
+cylindric
+cylindrical/Y
+Cy/M
+cymbalist/MS
+cymbal/SM
+Cymbre/M
+Cynde/M
+Cyndia/M
+Cyndie/M
+Cyndi/M
+Cyndy/M
+cynical/UY
+cynicism/MS
+cynic/MS
+cynosure/SM
+Cynthea/M
+Cynthia/M
+Cynthie/M
+Cynthy/M
+cypher/MGSD
+cypreses
+cypress/SM
+Cyprian
+Cypriot/SM
+Cyprus/M
+Cyrano/M
+Cyrille/M
+Cyrillic
+Cyrill/M
+Cyrillus/M
+Cyril/M
+Cyrus/M
+cystic
+cyst/MS
+cytochemistry/M
+cytochrome/M
+cytologist/MS
+cytology/MS
+cytolysis/M
+cytoplasmic
+cytoplasm/SM
+cytosine/MS
+cytotoxic
+CZ
+czarevitch/M
+czarina/SM
+czarism/M
+czarist/S
+czarship
+czar/SM
+Czech
+Czechoslovakia/M
+Czechoslovakian/S
+Czechoslovak/S
+Czechs
+Czerniak/M
+Czerny/M
+D
+DA
+dabbed
+dabber/MS
+dabbing
+dabbler/M
+dabble/RSDZG
+dab/S
+Dacca's
+dace/MS
+Dacey/M
+dacha/SM
+Dachau/M
+dachshund/SM
+Dacia/M
+Dacie/M
+Dacron/MS
+dactylic/S
+dactyl/MS
+Dacy/M
+Dadaism/M
+dadaism/S
+Dadaist/M
+dadaist/S
+Dada/M
+daddy/SM
+Dade/M
+dado/DMG
+dadoes
+dad/SM
+Daedalus/M
+Dael/M
+daemonic
+daemon/SM
+Daffie/M
+Daffi/M
+daffiness/S
+daffodil/MS
+Daffy/M
+daffy/PTR
+daftness/MS
+daft/TYRP
+DAG
+dagger/DMSG
+Dag/M
+Dagmar/M
+Dagny/M
+Daguerre/M
+daguerreotype/MGDS
+Dagwood/M
+Dahlia/M
+dahlia/MS
+Dahl/M
+Dahomey/M
+Daile/M
+dailiness/MS
+daily/PS
+Daimler/M
+daintily
+daintiness/MS
+dainty/TPRS
+daiquiri/SM
+dairying/M
+dairyland
+dairymaid/SM
+dairyman/M
+dairymen
+dairy/MJGS
+dairywoman/M
+dairywomen
+Daisey/M
+Daisie/M
+Daisi/M
+dais/SM
+Daisy/M
+daisy/SM
+Dakar/M
+Dakotan
+Dakota/SM
+Dale/M
+Dalenna/M
+dale/SMH
+daleth/M
+Daley/M
+Dalhousie/M
+Dalia/M
+Dalian/M
+Dalila/M
+Dali/SM
+Dallas/M
+dalliance/SM
+dallier/M
+Dalli/MS
+Dall/M
+Dallon/M
+dally/ZRSDG
+Dal/M
+Dalmatia/M
+dalmatian/S
+Dalmatian/SM
+Daloris/M
+Dalston/M
+Dalt/M
+Dalton/M
+Daly/M
+damageable
+damaged/U
+damage/MZGRSD
+damager/M
+damaging/Y
+Damara/M
+Damaris/M
+Damascus/M
+damask/DMGS
+dame/SM
+Dame/SMN
+Damian/M
+Damiano/M
+Damien/M
+Damion/M
+Damita/M
+dam/MDS
+dammed
+damming
+dammit/S
+damnably
+damnation/MS
+damnedest/MS
+damned/TR
+damn/GSBRD
+damning/Y
+Damocles/M
+Damon/M
+damped/U
+dampener/M
+dampen/RDZG
+damper/M
+dampness/MS
+damp/SGZTXYRDNP
+damselfly/MS
+damsel/MS
+damson/MS
+Danaë
+Dana/M
+Danbury/M
+dancelike
+dancer/M
+dance/SRDJGZ
+dandelion/MS
+dander/DMGS
+dandify/SDG
+dandily
+dandle/GSD
+dandruff/MS
+dandy/TRSM
+Danelaw/M
+Danella/M
+Danell/M
+Dane/SM
+Danette/M
+danger/DMG
+Dangerfield/M
+dangerousness/M
+dangerous/YP
+dangler/M
+dangle/ZGRSD
+dangling/Y
+dang/SGZRD
+Danial/M
+Dania/M
+Danica/M
+Danice/M
+Daniela/M
+Daniele/M
+Daniella/M
+Danielle/M
+Daniel/SM
+Danielson/M
+Danie/M
+Danika/M
+Danila/M
+Dani/M
+Danish
+danish/S
+Danita/M
+Danit/M
+dankness/MS
+dank/TPYR
+Danna/M
+Dannel/M
+Dannie/M
+Danni/M
+Dannye/M
+Danny/M
+danseuse/SM
+Dan/SM
+Dante/M
+Danton/M
+Danube/M
+Danubian
+Danville/M
+Danya/M
+Danyelle/M
+Danyette/M
+Danzig/M
+Daphene/M
+Daphna/M
+Daphne/M
+dapperness/M
+dapper/PSTRY
+dapple/SDG
+Dara/M
+Darbee/M
+Darbie/M
+Darb/M
+Darby/M
+Darcee/M
+Darcey/M
+Darcie/M
+Darci/M
+D'Arcy
+Darcy/M
+Darda/M
+Dardanelles
+daredevil/MS
+daredevilry/S
+Dareen/M
+Darelle/M
+Darell/M
+Dare/M
+Daren/M
+darer/M
+daresay
+dare/ZGDRSJ
+d'Arezzo
+Daria/M
+Darice/M
+Darill/M
+Dari/M
+daringness/M
+daring/PY
+Darin/M
+Dario/M
+Darius/M
+Darjeeling/M
+darkener/M
+darken/RDZG
+dark/GTXYRDNSP
+darkish
+darkly/TR
+darkness/MS
+darkroom/SM
+Darla/M
+Darleen/M
+Darlene/M
+Darline/M
+Darling/M
+darlingness/M
+Darlington/M
+darling/YMSP
+Darlleen/M
+Dar/MNH
+Darnall/M
+darned/TR
+Darnell/M
+darner/M
+darn/GRDZS
+darning/M
+Darn/M
+Daron/M
+DARPA/M
+Darrelle/M
+Darrell/M
+Darrel/M
+Darren/M
+Darrick/M
+Darrin/M
+Darrow/M
+Darryl/M
+Darsey/M
+Darsie/M
+d'art
+dartboard/SM
+darter/M
+Darth/M
+Dartmouth/M
+dart/MRDGZS
+Darvon/M
+Darwinian/S
+Darwinism/MS
+Darwinist/MS
+Darwin/M
+Darya/M
+Daryle/M
+Daryl/M
+Daryn/M
+Dasha/M
+dashboard/SM
+dasher/M
+dash/GZSRD
+dashiki/SM
+dashing/Y
+Dasie/M
+Dasi/M
+dastardliness/SM
+dastardly/P
+dastard/MYS
+Dasya/M
+DAT
+database/DSMG
+datafile
+datagram/MS
+data/M
+Datamation/M
+Datamedia/M
+dataset/S
+datedly
+datedness
+date/DRSMZGV
+dated/U
+dateless
+dateline/DSMG
+dater/M
+Datha/M
+dative/S
+Datsun/M
+datum/MS
+dauber/M
+daub/RDSGZ
+Daugherty/M
+daughter/MYS
+Daumier/M
+Daune/M
+daunt/DSG
+daunted/U
+daunting/Y
+dauntlessness/SM
+dauntless/PY
+dauphin/SM
+Davao/M
+Daveen/M
+Dave/M
+Daven/M
+Davenport/M
+davenport/MS
+Daveta/M
+Davey/M
+Davida/M
+Davidde/M
+Davide/M
+David/SM
+Davidson/M
+Davie/M
+Davina/M
+Davine/M
+Davinich/M
+Davin/M
+Davis/M
+Davita/M
+davit/SM
+Dav/MN
+Davon/M
+Davy/SM
+dawdler/M
+dawdle/ZGRSD
+Dawes/M
+Dawna/M
+dawn/GSDM
+Dawn/M
+Dawson/M
+daybed/S
+daybreak/SM
+daycare/S
+daydreamer/M
+daydream/RDMSZG
+Dayle/M
+daylight/GSDM
+Day/M
+Dayna/M
+daysack
+day/SM
+daytime/SM
+Dayton/M
+dazed/PY
+daze/DSG
+dazzler/M
+dazzle/ZGJRSD
+dazzling/Y
+db
+DB
+dbl
+dB/M
+DBMS
+DC
+DD
+Ddene/M
+DDS
+DDT
+DE
+deacon/DSMG
+deaconess/MS
+deadbeat/SM
+deadbolt/S
+deadener/M
+deadening/MY
+deaden/RDG
+deadhead/MS
+deadline/MGDS
+deadliness/SM
+deadlock/MGDS
+deadly/RPT
+deadness/M
+deadpanned
+deadpanner
+deadpanning
+deadpan/S
+dead/PTXYRN
+deadwood/SM
+deafening/MY
+deafen/JGD
+deafness/MS
+deaf/TXPYRN
+dealer/M
+dealership/MS
+dealing/M
+deallocator
+deal/RSGZJ
+dealt
+Deana/M
+dean/DMG
+Deandre/M
+Deane/M
+deanery/MS
+Dean/M
+Deanna/M
+Deanne/M
+Deann/M
+deanship/SM
+Dearborn/M
+dearness/MS
+dearth/M
+dearths
+dear/TYRHPS
+deary/MS
+deassign
+deathbed/MS
+deathblow/SM
+deathless/Y
+deathlike
+deathly/TR
+death/MY
+deaths
+deathtrap/SM
+deathward
+deathwatch/MS
+debacle/SM
+debarkation/SM
+debark/G
+debar/L
+debarment/SM
+debarring
+debaser/M
+debatable/U
+debate/BMZ
+debater/M
+debauchedness/M
+debauched/PY
+debauchee/SM
+debaucher/M
+debauchery/SM
+debauch/GDRS
+Debbie/M
+Debbi/M
+Debby/M
+Debee/M
+debenture/MS
+Debera/M
+debilitate/NGXSD
+debilitation/M
+debility/MS
+Debi/M
+debit/DG
+deb/MS
+Deb/MS
+debonairness/SM
+debonair/PY
+Deborah/M
+Debora/M
+Debor/M
+debouch/DSG
+Debra/M
+debrief/GJ
+debris/M
+debtor/SM
+debt/SM
+Debussy/M
+débutante/SM
+debut/MDG
+decade/MS
+decadency/S
+decadent/YS
+decaffeinate/DSG
+decaf/S
+decagon/MS
+Decalogue/M
+decal/SM
+decamp/L
+decampment/MS
+decapitate/GSD
+decapitator/SM
+decathlon/SM
+Decatur/M
+decay/GRD
+Decca/M
+Deccan/M
+decease/M
+decedent/MS
+deceitfulness/SM
+deceitful/PY
+deceit/SM
+deceived/U
+deceiver/M
+deceives/U
+deceive/ZGRSD
+deceivingly
+deceiving/U
+decelerate/XNGSD
+deceleration/M
+decelerator/SM
+December/SM
+decency/ISM
+decennial/SY
+decent/TIYR
+deception/SM
+deceptiveness/SM
+deceptive/YP
+decertify/N
+dechlorinate/N
+decibel/MS
+decidability/U
+decidable/U
+decidedness/M
+decided/PY
+decide/GRSDB
+deciduousness/M
+deciduous/YP
+decile/SM
+deciliter/SM
+decimal/SYM
+decimate/XNGDS
+decimation/M
+decimeter/MS
+decipherable/IU
+decipher/BRZG
+decipherer/M
+decisional
+decisioned
+decisioning
+decision/ISM
+decisive/IPY
+decisiveness/MSI
+deckchair
+decker/M
+Decker/M
+deck/GRDMSJ
+deckhand/S
+decking/M
+Deck/RM
+declamation/SM
+declamatory
+declarable
+declaration/MS
+declaration's/A
+declarative/SY
+declarator/MS
+declaratory
+declare/AGSD
+declared/U
+declarer/MS
+declension/SM
+declination/MS
+decliner/M
+decline/ZGRSD
+declivity/SM
+Dec/M
+DEC/M
+DECNET
+DECnet/M
+deco
+décolletage/S
+décolleté
+decolletes
+decolorising
+decomposability/M
+decomposable/IU
+decompose/B
+decompress/R
+decongestant/S
+deconstruction
+deconvolution
+decorated/AU
+decorate/NGVDSX
+decorates/A
+decorating/A
+decoration/ASM
+decorativeness/M
+decorative/YP
+decorator/SM
+decorousness/MS
+decorousness's/I
+decorous/PIY
+decor/S
+decorticate/GNDS
+decortication/M
+decorum/MS
+decoupage/MGSD
+decouple/G
+decoy/M
+decrease
+decreasing/Y
+decreeing
+decree/RSM
+decremental
+decrement/DMGS
+decrepit
+decrepitude/SM
+decriminalization/S
+decriminalize/DS
+decry/G
+decrypt/GD
+decryption
+DECstation/M
+DECsystem/M
+DECtape/M
+decustomised
+Dedekind/M
+Dede/M
+dedicate/AGDS
+dedicated/Y
+dedication/MS
+dedicative
+dedicator/MS
+dedicatory
+Dedie/M
+Dedra/M
+deduce/RSDG
+deducible
+deductibility/M
+deductible/S
+deduction/SM
+deductive/Y
+deduct/VG
+Deeanne/M
+Deeann/M
+deeded
+Deedee/M
+deeding
+deed/IS
+deed's
+deejay/MDSG
+Dee/M
+deem/ADGS
+deemphasis
+Deena/M
+deepen/DG
+deepish
+deepness/MS
+deep/PTXSYRN
+Deerdre/M
+Deere/M
+deerskin/MS
+deer/SM
+deerstalker/SM
+deerstalking/M
+Deeyn/M
+deface/LZ
+defacement/SM
+defaecate
+defalcate/NGXSD
+defalcation/M
+defamation/SM
+defamatory
+defamer/M
+defame/ZR
+defaulter/M
+default/ZR
+defeated/U
+defeater/M
+defeatism/SM
+defeatist/SM
+defeat/ZGD
+defecate/DSNGX
+defecation/M
+defection/SM
+defectiveness/MS
+defective/PYS
+defect/MDSVG
+defector/MS
+defendant/SM
+defended/U
+defenestrate/GSD
+defenselessness/MS
+defenseless/PY
+defenses/U
+defense/VGSDM
+defensibility/M
+defensible/I
+defensibly/I
+defensiveness/MS
+defensive/PSY
+deference/MS
+deferential/Y
+deferent/S
+deferrable
+deferral/SM
+deferred
+deferrer/MS
+deferring
+deffer
+defiance/MS
+defiant/Y
+defibrillator/M
+deficiency/MS
+deficient/SY
+deficit/MS
+defier/M
+defile/L
+defilement/MS
+definable/UI
+definably/I
+define/AGDRS
+defined/U
+definer/SM
+definite/IPY
+definiteness/IMS
+definitional
+definition/ASM
+definitiveness/M
+definitive/SYP
+defis
+deflate/XNGRSDB
+deflationary
+deflation/M
+deflect/DSGV
+deflected/U
+deflection/MS
+deflector/MS
+defocus
+defocussing
+Defoe/M
+defog
+defogger/S
+defoliant/SM
+defoliator/SM
+deformational
+deform/B
+deformed/U
+deformity/SM
+defrauder/M
+defraud/ZGDR
+defrayal/SM
+defroster/M
+defrost/RZ
+deftness/MS
+deft/TYRP
+defunct/S
+defying/Y
+defy/RDG
+def/Z
+deg
+Degas/M
+degassing
+degauss/GD
+degeneracy/MS
+degenerateness/M
+degenerate/PY
+degrade/B
+degradedness/M
+degraded/YP
+degrading/Y
+degrease
+degree/SM
+degum
+Dehlia/M
+dehumanize
+dehydrator/MS
+deicer/M
+deice/ZR
+deictic
+Deidre/M
+deification/M
+deify/SDXGN
+deign/DGS
+Deimos/M
+Deina/M
+Deirdre/MS
+deistic
+deist/SM
+Deity/M
+deity/SM
+deja
+deject/DSG
+dejectedness/M
+dejected/PY
+dejection/SM
+Dejesus/M
+DeKalb/M
+DeKastere/M
+Delacroix/M
+Delacruz/M
+Delainey/M
+Dela/M
+Delaney/M
+Delano/M
+Delawarean/SM
+Delaware/MS
+delay/D
+delayer/G
+Delbert/M
+Delcina/M
+Delcine/M
+delectableness/M
+delectable/SP
+delectably
+delectation/MS
+delegable
+Deleon/M
+deleted/U
+deleteriousness/M
+deleterious/PY
+delete/XBRSDNG
+deletion/M
+delfs
+Delft/M
+delft/MS
+delftware/S
+Delgado/M
+Delhi/M
+Delia/M
+deliberateness/SM
+deliberate/PVY
+deliberativeness/M
+deliberative/PY
+Delibes/M
+delicacy/IMS
+delicate/IYP
+delicatenesses
+delicateness/IM
+delicates
+delicatessen/MS
+deliciousness/MS
+delicious/YSP
+delicti
+delightedness/M
+delighted/YP
+delightfulness/M
+delightful/YP
+Delilah/M
+Delilahs
+Delila/M
+Delinda/M
+delineate/SDXVNG
+delineation/M
+delinquency/MS
+delinquent/SYM
+deliquesce/GSD
+deliquescent
+deliriousness/MS
+delirious/PY
+delirium/SM
+deli/SM
+Delius/M
+deliverables
+deliverable/U
+deliver/AGSD
+deliverance/SM
+delivered/U
+deliverer/SM
+delivery/AM
+deliverymen/M
+Della/M
+Dell/M
+dell/SM
+Dellwood/M
+Delly/M
+Delmar/M
+Delmarva/M
+Delmer/M
+Delmonico
+Delmore/M
+Delmor/M
+Del/MY
+Delora/M
+Delores/M
+Deloria/M
+Deloris/M
+Delphic
+Delphi/M
+Delphine/M
+Delphinia/M
+delphinium/SM
+Delphinus/M
+Delta/M
+delta/MS
+deltoid/SM
+deluder/M
+delude/RSDG
+deluding/Y
+deluge/SDG
+delusional
+delusion/SM
+delusiveness/M
+delusive/PY
+deluxe
+delve/GZSRD
+delver/M
+demagnify/N
+demagogic
+demagogue/GSDM
+demagoguery/SM
+demagogy/MS
+demander/M
+demand/GSRD
+demandingly
+demanding/U
+demarcate/SDNGX
+demarcation/M
+Demavend/M
+demean/GDS
+demeanor/SM
+dementedness/M
+demented/YP
+dementia/MS
+Demerol/M
+demesne/SM
+Demeter/M
+Demetra/M
+Demetre/M
+Demetria/M
+Demetri/MS
+Demetrius/M
+demigod/MS
+demijohn/MS
+demimondaine/SM
+demimonde/SM
+demineralization/SM
+Deming/M
+demise/DMG
+demit
+demitasse/MS
+demitted
+demitting
+Dem/MG
+democracy/MS
+Democratic
+democratically/U
+democratic/U
+democratization/MS
+democratize/DRSG
+democratizes/U
+Democrat/MS
+democrat/SM
+Democritus/M
+démodé
+demo/DMPG
+demographer/MS
+demographical/Y
+demographic/S
+demography/MS
+demolisher/M
+demolish/GSRD
+demolition/MS
+demonetization/S
+demoniacal/Y
+demoniac/S
+demonic
+demonology/M
+demon/SM
+demonstrable/I
+demonstrableness/M
+demonstrably/I
+demonstrate/XDSNGV
+demonstration/M
+demonstrativenesses
+demonstrativeness/UM
+demonstratives
+demonstrative/YUP
+demonstrator/MS
+demoralization/M
+demoralizer/M
+demoralizing/Y
+DeMorgan/M
+Demosthenes/M
+demote/DGX
+demotic/S
+Demott/M
+demount/B
+Dempsey/M
+demulcent/S
+demultiplex
+demureness/SM
+demure/YP
+demurral/MS
+demurred
+demurrer/MS
+demurring
+demur/RTS
+demythologization/M
+demythologize/R
+den
+Dena/M
+dendrite/MS
+Deneb/M
+Denebola/M
+Deneen/M
+Dene/M
+Deng/M
+dengue/MS
+deniable/U
+denial/SM
+Denice/M
+denier/M
+denigrate/VNGXSD
+denigration/M
+denim/SM
+Denise/M
+Deni/SM
+denizen/SMDG
+Den/M
+De/NM
+Denmark/M
+Denna/M
+denned
+Dennet/M
+Denney/M
+Dennie/M
+Denni/MS
+denning
+Dennison/M
+Denny/M
+denominate/V
+denominational/Y
+denote/B
+denouement/MS
+denounce/LZRSDG
+denouncement/SM
+denouncer/M
+dense/FR
+densely
+denseness/SM
+densitometer/MS
+densitometric
+densitometry/M
+density/MS
+dens/RT
+dental/YS
+dentifrice/SM
+dentine's
+dentin/SM
+dent/ISGD
+dentistry/MS
+dentist/SM
+dentition/MS
+dent's
+denture/IMS
+denuclearize/GSD
+denudation/SM
+denude/DG
+denuder/M
+denunciate/VNGSDX
+denunciation/M
+Denver/M
+denying/Y
+Deny/M
+Denys
+Denyse/M
+deny/SRDZG
+deodorant/SM
+deodorization/SM
+deodorize/GZSRD
+deodorizer/M
+Deon/M
+Deonne/M
+deoxyribonucleic
+depart/L
+departmentalization/SM
+departmentalize/DSG
+departmental/Y
+department/MS
+departure/MS
+dependability/MS
+dependableness/M
+dependable/P
+dependably
+Dependant/MS
+depend/B
+dependence/ISM
+dependency/MS
+dependent/IYS
+dependent's
+depicted/U
+depicter/M
+depiction/SM
+depict/RDSG
+depilatory/S
+deplete/VGNSDX
+depletion/M
+deplorableness/M
+deplorable/P
+deplorably
+deplorer/M
+deplore/SRDBG
+deploring/Y
+deployable
+deploy/AGDLS
+deployment/SAM
+depolarize
+deponent/S
+deportation/MS
+deportee/SM
+deport/LG
+deportment/MS
+depose
+deposit/ADGS
+depositary/M
+deposition/A
+depositor/SAM
+depository/MS
+depravedness/M
+depraved/PY
+deprave/GSRD
+depraver/M
+depravity/SM
+deprecate/XSDNG
+deprecating/Y
+deprecation/M
+deprecatory
+depreciable
+depreciate/XDSNGV
+depreciating/Y
+depreciation/M
+depreciative/Y
+depressant/S
+depressible
+depression/MS
+depressive/YS
+depressor/MS
+depress/V
+deprive/GSD
+depth/M
+depths
+Dept/M
+deputation/SM
+depute/SDG
+deputize/DSG
+deputy/MS
+dequeue
+derail/L
+dérailleur/MS
+derailment/MS
+derange/L
+derangement/MS
+Derbyshire/M
+derby/SM
+Derby/SM
+dereference/Z
+Derek/M
+dereliction/SM
+derelict/S
+Derick/M
+deride/D
+deriding/Y
+derision/SM
+derisiveness/MS
+derisive/PY
+derisory
+derivable/U
+derivate/XNV
+derivation/M
+derivativeness/M
+derivative/SPYM
+derive/B
+derived/U
+Derk/M
+Der/M
+dermal
+dermatitides
+dermatitis/MS
+dermatological
+dermatologist/MS
+dermatology/MS
+dermis/SM
+Dermot/M
+derogate/XDSNGV
+derogation/M
+derogatorily
+derogatory
+Derrek/M
+Derrick/M
+derrick/SMDG
+Derrida/M
+derrière/S
+Derrik/M
+Derril/M
+derringer/SM
+Derron/M
+Derry/M
+dervish/SM
+Derward/M
+Derwin/M
+Des
+desalinate/NGSDX
+desalination/M
+desalinization/MS
+desalinize/GSD
+desalt/G
+descant/M
+Descartes/M
+descendant/SM
+descended/FU
+descendent's
+descender/M
+descending/F
+descends/F
+descend/ZGSDR
+descent
+describable/I
+describe/ZB
+description/MS
+descriptiveness/MS
+descriptive/SYP
+descriptor/SM
+descry/SDG
+Desdemona/M
+desecrater/M
+desecrate/SRDGNX
+desecration/M
+deserter/M
+desertification
+desertion/MS
+desert/ZGMRDS
+deservedness/M
+deserved/YU
+deserve/J
+deserving/Y
+déshabillé's
+desiccant/S
+desiccate/XNGSD
+desiccation/M
+desiccator/SM
+desiderata
+desideratum/M
+designable
+design/ADGS
+designate/VNGSDX
+designational
+designation/M
+designator/SM
+designed/Y
+designer/M
+designing/U
+Desi/M
+desirabilia
+desirability's
+desirability/US
+desirableness/SM
+desirableness's/U
+desirable/UPS
+desirably/U
+Desirae/M
+desire/BR
+desired/U
+Desiree/M
+desirer/M
+Desiri/M
+desirousness/M
+desirous/PY
+desist/DSG
+desk/SM
+desktop/S
+Desmond/M
+Desmund/M
+desolateness/SM
+desolate/PXDRSYNG
+desolater/M
+desolating/Y
+desolation/M
+desorption/M
+despairer/M
+despairing/Y
+despair/SGDR
+desperadoes
+desperado/M
+desperateness/SM
+desperate/YNXP
+desperation/M
+despicable
+despicably
+despiser/M
+despise/SRDG
+despoil/L
+despoilment/MS
+despond
+despondence/S
+despondency/MS
+despondent/Y
+despotic
+despotically
+despotism/SM
+dessert/SM
+dessicate/DN
+d'Estaing
+destinate/NX
+destination/M
+destine/GSD
+destiny/MS
+destituteness/M
+destitute/NXP
+destitution/M
+destroy/BZGDRS
+destroyer/M
+destructibility/SMI
+destructible/I
+destruction/SM
+destructiveness/MS
+destructive/YP
+destructor/M
+destruct/VGSD
+desuetude/MS
+desultorily
+desultoriness/M
+desultory/P
+detachedness/M
+detached/YP
+detacher/M
+detach/LSRDBG
+detachment/SM
+detailedness/M
+detailed/YP
+detainee/S
+detainer/M
+detain/LGRDS
+detainment/MS
+d'etat
+detectability/U
+detectable/U
+detectably/U
+detect/DBSVG
+detected/U
+detection/SM
+detective/MS
+detector/MS
+détente
+detentes
+detention/SM
+detergency/M
+detergent/SM
+deteriorate/XDSNGV
+deterioration/M
+determent/SM
+determinability/M
+determinable/IP
+determinableness/IM
+determinacy/I
+determinant/MS
+determinateness/IM
+determinate/PYIN
+determination/IM
+determinativeness/M
+determinative/P
+determinedly
+determinedness/M
+determined/U
+determine/GASD
+determiner/SM
+determinism/MS
+determinism's/I
+deterministically
+deterministic/I
+deterred/U
+deterrence/SM
+deterrent/SMY
+deterring
+detersive/S
+deter/SL
+deters/V
+detestableness/M
+detestable/P
+detestably
+detestation/SM
+dethrone/L
+dethronement/SM
+detonable
+detonated/U
+detonate/XDSNGV
+detonation/M
+detonator/MS
+detour/G
+detoxification/M
+detoxify/NXGSD
+detox/SDG
+detract/GVD
+detractive/Y
+d'etre
+detribalize/GSD
+detrimental/SY
+detriment/SM
+detritus/M
+Detroit/M
+deuced/Y
+deuce/SDGM
+deus
+deuterium/MS
+deuteron/M
+Deuteronomy/M
+Deutsch/M
+Deva/M
+Devanagari/M
+Devan/M
+devastate/XVNGSD
+devastating/Y
+devastation/M
+devastator/SM
+develop/ALZSGDR
+developed/U
+developer/MA
+developmental/Y
+development/ASM
+deviance/MS
+deviancy/S
+deviant/YMS
+deviated/U
+deviate/XSDGN
+deviating/U
+deviation/M
+devilishness/MS
+devilish/PY
+devilment/SM
+devilry/MS
+devil/SLMDG
+deviltry/MS
+Devi/M
+Devina/M
+Devin/M
+Devinne/M
+deviousness/SM
+devious/YP
+devise/JR
+deviser/M
+Devland/M
+Devlen/M
+Devlin/M
+Dev/M
+devoice
+devolution/MS
+devolve/GSD
+Devondra/M
+Devonian
+Devon/M
+Devonna/M
+Devonne/M
+Devonshire/M
+Devora/M
+devoted/Y
+devotee/MS
+devote/XN
+devotional/YS
+devotion/M
+devourer/M
+devour/SRDZG
+devoutness/MS
+devout/PRYT
+Devy/M
+Dewain/M
+dewar
+Dewar/M
+Dewayne/M
+dewberry/MS
+dewclaw/SM
+dewdrop/MS
+Dewey/M
+Dewie/M
+dewiness/MS
+Dewitt/M
+dewlap/MS
+Dew/M
+dew/MDGS
+dewy/TPR
+Dexedrine/M
+dexes/I
+Dex/M
+dexter
+dexterity/MS
+Dexter/M
+dexterousness/MS
+dexterous/PY
+dextrose/SM
+DH
+Dhaka
+Dhaulagiri/M
+dhoti/SM
+dhow/MS
+DI
+diabase/M
+diabetes/M
+diabetic/S
+diabolic
+diabolicalness/M
+diabolical/YP
+diabolism/M
+diachronic/P
+diacritical/YS
+diacritic/MS
+diadem/GMDS
+diaereses
+diaeresis/M
+Diaghilev/M
+diagnometer/SM
+diagnosable/U
+diagnose/BGDS
+diagnosed/U
+diagnosis/M
+diagnostically
+diagnostician/SM
+diagnostic/MS
+diagnostics/M
+diagonalize/GDSB
+diagonal/YS
+diagrammable
+diagrammatic
+diagrammaticality
+diagrammatically
+diagrammed
+diagrammer/SM
+diagramming
+diagram/MS
+Diahann/M
+dialectal/Y
+dialectical/Y
+dialectic/MS
+dialect/MS
+dialed/A
+dialer/M
+dialing/M
+dial/MRDSGZJ
+dialogged
+dialogging
+dialog/MS
+dials/A
+dialysis/M
+dialyzed/U
+dialyzes
+diam
+diamagnetic
+diameter/MS
+diametric
+diametrical/Y
+diamondback/SM
+diamond/GSMD
+Diana/M
+Diandra/M
+Diane/M
+Dianemarie/M
+Dian/M
+Dianna/M
+Dianne/M
+Diann/M
+Diannne/M
+diapason/MS
+diaper/SGDM
+diaphanousness/M
+diaphanous/YP
+diaphragmatic
+diaphragm/SM
+diarist/SM
+Diarmid/M
+diarrheal
+diarrhea/MS
+diary/MS
+diaspora
+Diaspora/SM
+diastase/SM
+diastole/MS
+diastolic
+diathermy/SM
+diathesis/M
+diatomic
+diatom/SM
+diatonic
+diatribe/MS
+Diaz's
+dibble/SDMG
+dibs
+DiCaprio/M
+dice/GDRS
+dicer/M
+dicey
+dichloride/M
+dichotomization/M
+dichotomize/DSG
+dichotomous/PY
+dichotomy/SM
+dicier
+diciest
+dicing/M
+Dickensian/S
+dickens/M
+Dickens/M
+dicker/DG
+Dickerson/M
+dickey/SM
+dick/GZXRDMS
+Dickie/M
+dickier
+dickiest
+Dickinson/M
+Dickson/M
+Dick/XM
+Dicky/M
+dicky's
+dicotyledonous
+dicotyledon/SM
+dicta/M
+Dictaphone/SM
+dictate/SDNGX
+dictation/M
+dictatorialness/M
+dictatorial/YP
+dictator/MS
+dictatorship/SM
+dictionary/SM
+diction/MS
+dictum/M
+didactically
+didactic/S
+didactics/M
+did/AU
+diddler/M
+diddle/ZGRSD
+Diderot/M
+Didi/M
+didn't
+didoes
+dido/M
+Dido/M
+didst
+die/DS
+Diefenbaker/M
+Diego/M
+dieing
+dielectric/MS
+diem
+Diem/M
+Diena/M
+Dierdre/M
+diereses
+dieresis/M
+diesel/GMDS
+Diesel's
+dies's
+dies/U
+dietary/S
+dieter/M
+Dieter/M
+dietetic/S
+dietetics/M
+diethylaminoethyl
+diethylstilbestrol/M
+dietitian/MS
+diet/RDGZSM
+Dietrich/M
+Dietz/M
+difference/DSGM
+difference's/I
+differences/I
+differentiability
+differentiable
+differential/SMY
+differentiated/U
+differentiate/XSDNG
+differentiation/M
+differentiator/SM
+differentness
+different/YI
+differ/SZGRD
+difficile
+difficult/Y
+difficulty/SM
+diffidence/MS
+diffident/Y
+diffract/GSD
+diffraction/SM
+diffractometer/SM
+diffuseness/MS
+diffuse/PRSDZYVXNG
+diffuser/M
+diffusible
+diffusional
+diffusion/M
+diffusiveness/M
+diffusive/YP
+diffusivity/M
+digerati
+digested/IU
+digester/M
+digestibility/MS
+digestible/I
+digestifs
+digestion/ISM
+digestive/YSP
+digest/RDVGS
+digger/MS
+digging/S
+digitalis/M
+digitalization/MS
+digitalized
+digitalizes
+digitalizing
+digital/SY
+digitization/M
+digitizer/M
+digitize/ZGDRS
+digit/SM
+dignified/U
+dignify/DSG
+dignitary/SM
+dignity/ISM
+digram
+digraph/M
+digraphs
+digress/GVDS
+digression/SM
+digressiveness/M
+digressive/PY
+dig/TS
+dihedral
+Dijkstra/M
+Dijon/M
+dike/DRSMG
+diker/M
+diktat/SM
+Dilan/M
+dilapidate/XGNSD
+dilapidation/M
+dilatation/SM
+dilated/YP
+dilate/XVNGSD
+dilation/M
+dilatoriness/M
+dilator/SM
+dilatory/P
+Dilbert/M
+dilemma/MS
+dilettante/MS
+dilettantish
+dilettantism/MS
+diligence/SM
+diligentness/M
+diligent/YP
+dilithium
+Dillard/M
+Dillie/M
+Dillinger/M
+dilling/R
+dillis
+Dill/M
+Dillon/M
+dill/SGMD
+dillydally/GSD
+Dilly/M
+dilly/SM
+dilogarithm
+diluent
+diluted/U
+diluteness/M
+dilute/RSDPXYVNG
+dilution/M
+Di/M
+DiMaggio/M
+dimensionality/M
+dimensional/Y
+dimensionless
+dimension/MDGS
+dimer/M
+dime/SM
+dimethylglyoxime
+dimethyl/M
+diminished/U
+diminish/SDGBJ
+diminuendo/SM
+diminution/SM
+diminutiveness/M
+diminutive/SYP
+Dimitri/M
+Dimitry/M
+dimity/MS
+dimmed/U
+dimmer/MS
+dimmest
+dimming
+dimness/SM
+dimorphism/M
+dimple/MGSD
+dimply/RT
+dim/RYPZS
+dimwit/MS
+dimwitted
+Dinah/M
+Dina/M
+dinar/SM
+diner/M
+dine/S
+dinette/MS
+dingbat/MS
+ding/GD
+dinghy/SM
+dingily
+dinginess/SM
+dingle/MS
+dingoes
+dingo/MS
+dingus/SM
+dingy/PRST
+dinky/RST
+din/MDRZGS
+dinned
+dinner/SM
+dinnertime/S
+dinnerware/MS
+Dinnie/M
+dinning
+Dinny/M
+Dino/M
+dinosaur/MS
+dint/SGMD
+diocesan/S
+diocese/SM
+Diocletian/M
+diode/SM
+Diogenes/M
+Dione/M
+Dionisio/M
+Dionis/M
+Dion/M
+Dionne/M
+Dionysian
+Dionysus/M
+Diophantine/M
+diopter/MS
+diorama/SM
+Dior/M
+dioxalate
+dioxide/MS
+dioxin/S
+diphtheria/SM
+diphthong/SM
+diplexers
+diploid/S
+diplomacy/SM
+diploma/SMDG
+diplomata
+diplomatically
+diplomatic/S
+diplomatics/M
+diplomatist/SM
+diplomat/MS
+dipodic
+dipody/M
+dipole/MS
+dipped
+Dipper/M
+dipper/SM
+dipping/S
+dippy/TR
+dip/S
+dipsomaniac/MS
+dipsomania/SM
+dipstick/MS
+dipterous
+diptych/M
+diptychs
+Dir
+Dirac/M
+directed/IUA
+directionality
+directional/SY
+direction/MIS
+directions/A
+directive/SM
+directivity/M
+directly/I
+directness/ISM
+director/AMS
+directorate/SM
+directorial
+directorship/SM
+directory/SM
+direct/RDYPTSVG
+directrix/MS
+directs/IA
+direful/Y
+direness/M
+dire/YTRP
+dirge/GSDM
+Dirichlet/M
+dirigible/S
+dirk/GDMS
+Dirk/M
+dirndl/MS
+dirtily
+dirtiness/SM
+dirt/MS
+dirty/GPRSDT
+Dis
+disable/LZGD
+disablement/MS
+disabler/M
+disabuse
+disadvantaged/P
+disagreeable/S
+disallow/D
+disambiguate/DSGNX
+disappointed/Y
+disappointing/Y
+disarming/Y
+disarrange/L
+disastrous/Y
+disband/L
+disbandment/SM
+disbar/L
+disbarment/MS
+disbarring
+disbelieving/Y
+disbursal/S
+disburse/GDRSL
+disbursement/MS
+disburser/M
+discerner/M
+discernibility
+discernible/I
+discernibly
+discerning/Y
+discernment/MS
+discern/SDRGL
+disc/GDM
+discharged/U
+disciple/DSMG
+discipleship/SM
+disciplinarian/SM
+disciplinary
+disciplined/U
+discipline/IDM
+discipliner/M
+disciplines
+disciplining
+disclosed/U
+discography/MS
+discolored/MP
+discoloreds/U
+discolor/G
+discombobulate/SDGNX
+discomfit/DG
+discomfiture/MS
+disco/MG
+discommode/DG
+disconcerting/Y
+disconnectedness/S
+disconnected/P
+disconnecter/M
+disconnect/R
+disconsolate/YN
+discordance/SM
+discordant/Y
+discord/G
+discorporate/D
+discotheque/MS
+discount/B
+discourage/LGDR
+discouragement/MS
+discouraging/Y
+discoverable/I
+discover/ADGS
+discovered/U
+discoverer/S
+discovery/SAM
+discreetly/I
+discreetness's/I
+discreetness/SM
+discreet/TRYP
+discrepancy/SM
+discrepant/Y
+discreteness/SM
+discrete/YPNX
+discretionary
+discretion/IMS
+discretization
+discretized
+discriminable
+discriminant/MS
+discriminated/U
+discriminate/SDVNGX
+discriminating/YI
+discrimination/MI
+discriminator/MS
+discriminatory
+discursiveness/S
+discussant/MS
+discussed/UA
+discusser/M
+discussion/SM
+discus/SM
+disdainfulness/M
+disdainful/YP
+disdain/MGSD
+disease/G
+disembowelment/SM
+disembowel/SLGD
+disengage/L
+disfigure/L
+disfigurement/MS
+disfranchise/L
+disfranchisement/MS
+disgorge
+disgrace/R
+disgracer/M
+disgruntle/DSLG
+disgruntlement/MS
+disguised/UY
+disguise/R
+disguiser/M
+disgust
+disgusted/Y
+disgustful/Y
+disgusting/Y
+dishabille/SM
+disharmonious
+dishcloth/M
+dishcloths
+dishevel/LDGS
+dishevelment/MS
+dish/GD
+dishonest
+dishonored/U
+dishpan/MS
+dishrag/SM
+dishtowel/SM
+dishwasher/MS
+dishwater/SM
+disillusion/LGD
+disillusionment/SM
+disinfectant/MS
+disinherit
+disinterestedness/SM
+disinterested/P
+disinvest/L
+disjoin
+disjointedness/S
+disjunctive/YS
+disjunct/VS
+disk/D
+diskette/S
+dislike/G
+dislodge/LG
+dislodgement/M
+dismalness/M
+dismal/PSTRY
+dismantle/L
+dismantlement/SM
+dismay/D
+dismayed/U
+dismaying/Y
+dis/MB
+dismember/LG
+dismemberment/MS
+dismissive/Y
+dismiss/RZ
+Disneyland/M
+Disney/M
+disoblige/G
+disorderedness/M
+disordered/YP
+disorderliness/M
+disorderly/P
+disorder/Y
+disorganize
+disorganized/U
+disparagement/MS
+disparager/M
+disparage/RSDLG
+disparaging/Y
+disparateness/M
+disparate/PSY
+dispatch/Z
+dispelled
+dispelling
+dispel/S
+dispensable/I
+dispensary/MS
+dispensate/NX
+dispensation/M
+dispenser/M
+dispense/ZGDRSB
+dispersal/MS
+dispersant/M
+dispersed/Y
+disperser/M
+disperse/XDRSZLNGV
+dispersible
+dispersion/M
+dispersiveness/M
+dispersive/PY
+dispirit/DSG
+displace/L
+display/AGDS
+displayed/U
+displeased/Y
+displease/G
+displeasure
+disport
+disposable/S
+disposal/SM
+dispose/IGSD
+dispositional
+disposition/ISM
+disproportional
+disproportionate/N
+disproportionation/M
+disprove/B
+disputable/I
+disputably/I
+disputant/SM
+disputation/SM
+disputatious/Y
+disputed/U
+disputer/M
+dispute/ZBGSRD
+disquieting/Y
+disquiet/M
+disquisition/SM
+Disraeli/M
+disregardful
+disrepair/M
+disreputableness/M
+disreputable/P
+disrepute/M
+disrespect
+disrupted/U
+disrupter/M
+disrupt/GVDRS
+disruption/MS
+disruptive/YP
+disruptor/M
+dissatisfy
+dissect/DG
+dissed
+dissembler/M
+dissemble/ZGRSD
+disseminate/XGNSD
+dissemination/M
+dissension/SM
+dissenter/M
+dissent/ZGSDR
+dissertation/SM
+disservice
+disses
+dissever
+dissidence/SM
+dissident/MS
+dissimilar/S
+dissing
+dissipatedly
+dissipatedness/M
+dissipated/U
+dissipater/M
+dissipate/XRSDVNG
+dissipation/M
+dissociable/I
+dissociate/DSXNGV
+dissociated/U
+dissociation/M
+dissociative/Y
+dissoluble/I
+dissoluteness/SM
+dissolute/PY
+dissolve/ASDG
+dissolved/U
+dissonance/SM
+dissonant/Y
+dissuade/GDRS
+dissuader/M
+dissuasive
+dist
+distaff/SM
+distal/Y
+distance/DSMG
+distantness/M
+distant/YP
+distaste
+distemper
+distend
+distension
+distention/SM
+distillate/XNMS
+distillation/M
+distillery/MS
+distincter
+distinctest
+distinction/MS
+distinctiveness/MS
+distinctive/YP
+distinct/IYVP
+distinctness/MSI
+distinguishable/I
+distinguishably/I
+distinguish/BDRSG
+distinguished/U
+distinguisher/M
+distort/BGDR
+distorted/U
+distorter/M
+distortion/MS
+distract/DG
+distractedness/M
+distracted/YP
+distracting/Y
+distrait
+distraught/Y
+distress
+distressful
+distressing/Y
+distribute/ADXSVNGB
+distributed/U
+distributer
+distributional
+distribution/AM
+distributiveness/M
+distributive/SPY
+distributivity
+distributorship/M
+distributor/SM
+district/GSAD
+district's
+distrust/G
+disturbance/SM
+disturbed/U
+disturber/M
+disturbing/Y
+disturb/ZGDRS
+disulfide/M
+disuse/M
+disyllable/M
+Dita/M
+ditcher/M
+ditch/MRSDG
+dither/RDZSG
+ditsy/TR
+ditto/DMGS
+ditty/SDGM
+Ditzel/M
+ditz/S
+diuresis/M
+diuretic/S
+diurnal/SY
+divalent/S
+diva/MS
+divan/SM
+dived/M
+divergence/SM
+divergent/Y
+diverge/SDG
+diver/M
+diverseness/MS
+diverse/XYNP
+diversification/M
+diversifier/M
+diversify/GSRDNX
+diversionary
+diversion/M
+diversity/SM
+divert/GSD
+diverticulitis/SM
+divertimento/M
+dive/S
+divestiture/MS
+divest/LDGS
+divestment/S
+dividable
+divide/AGDS
+divided/U
+dividend/MS
+divider/MS
+divination/SM
+diviner/M
+divine/RSDTZYG
+divinity/MS
+divisibility/IMS
+divisible/I
+divisional
+division/SM
+divisiveness/MS
+divisive/PY
+divisor/SM
+divorcée/MS
+divorce/GSDLM
+divorcement/MS
+divot/MS
+div/TZGJDRS
+divulge/GSD
+divvy/GSDM
+Dixiecrat/MS
+dixieland
+Dixieland/MS
+Dixie/M
+Dix/M
+Dixon/M
+dizzily
+dizziness/SM
+dizzying/Y
+dizzy/PGRSDT
+DJ
+Djakarta's
+djellabah's
+djellaba/S
+d/JGVX
+Djibouti/M
+DMD
+Dmitri/M
+DMZ
+DNA
+Dnepropetrovsk/M
+Dnepr's
+Dnieper's
+Dniester/M
+Dniren/M
+DOA
+doable
+DOB
+Dobbin/M
+dobbin/MS
+Doberman
+Dobro/M
+docent/SM
+docile/Y
+docility/MS
+docker/M
+docket/GSMD
+dock/GZSRDM
+dockland/MS
+dockside/M
+dockworker/S
+dockyard/SM
+doc/MS
+Doctor
+doctoral
+doctorate/SM
+doctor/GSDM
+Doctorow/M
+doctrinaire/S
+doctrinal/Y
+doctrine/SM
+docudrama/S
+documentary/MS
+documentation/MS
+documented/U
+document/RDMZGS
+DOD
+dodder/DGS
+dodecahedra
+dodecahedral
+dodecahedron/M
+Dode/M
+dodge/GZSRD
+Dodge/M
+dodgem/S
+dodger/M
+Dodgson/M
+Dodie/M
+Dodi/M
+Dodington/M
+Dodoma/M
+dodo/SM
+Dodson/M
+Dody/M
+DOE
+Doe/M
+doe/MS
+doer/MU
+does/AU
+doeskin/MS
+doesn't
+d'oeuvre
+doff/SGD
+dogcart/SM
+dogcatcher/MS
+dogeared
+Doge/M
+doge/SM
+dogfight/GMS
+dogfish/SM
+dogfought
+doggedness/SM
+dogged/PY
+doggerel/SM
+dogging
+doggone/RSDTG
+doggy/SRMT
+doghouse/SM
+dogie/SM
+doglegged
+doglegging
+dogleg/SM
+dogma/MS
+dogmatically/U
+dogmatic/S
+dogmatics/M
+dogmatism/SM
+dogmatist/SM
+dogsbody/M
+dog/SM
+dogtooth/M
+Dogtown/M
+dogtrot/MS
+dogtrotted
+dogtrotting
+dogwood/SM
+dogy's
+Doha/M
+doh's
+doily/SM
+doing/MU
+Dolby/SM
+doldrum/S
+doldrums/M
+doled/F
+dolefuller
+dolefullest
+dolefulness/MS
+doleful/PY
+Dole/M
+dole/MGDS
+doles/F
+Dolf/M
+doling/F
+dollar/SM
+Dolley/M
+Dollie/M
+Dolli/M
+Doll/M
+doll/MDGS
+dollop/GSMD
+Dolly/M
+dolly/SDMG
+dolmen/MS
+dolomite/SM
+dolomitic
+Dolores/M
+Dolorita/SM
+dolorous/Y
+dolor/SM
+dolphin/SM
+Dolph/M
+doltishness/SM
+doltish/YP
+dolt/MS
+domain/MS
+dome/DSMG
+Domenic/M
+Domenico/M
+Domeniga/M
+Domesday/M
+domestically
+domesticate/DSXGN
+domesticated/U
+domestication/M
+domesticity/MS
+domestic/S
+domicile/SDMG
+domiciliary
+dominance/MS
+dominant/YS
+dominate/VNGXSD
+domination/M
+dominator/M
+dominatrices
+dominatrix
+domineer/DSG
+domineeringness/M
+domineering/YP
+Dominga/M
+Domingo/M
+Dominguez/M
+Dominica/M
+Dominican/MS
+Dominick/M
+Dominic/M
+Dominik/M
+Domini/M
+dominion/MS
+Dominique/M
+dominoes
+domino/M
+Domitian/M
+Dom/M
+Donahue/M
+Donald/M
+Donaldson/M
+Donall/M
+Donal/M
+Donalt/M
+Dona/M
+dona/MS
+Donatello/M
+donate/XVGNSD
+donation/M
+donative/M
+Donaugh/M
+Donavon/M
+done/AUF
+Donella/M
+Donelle/M
+Donetsk/M
+Donetta/M
+dong/GDMS
+dongle/S
+Donia/M
+Donica/M
+Donielle/M
+Donizetti/M
+donkey/MS
+Donna/M
+Donnamarie/M
+donned
+Donnell/M
+Donnelly/M
+Donne/M
+Donner/M
+Donnie/M
+Donni/M
+donning
+donnishness/M
+donnish/YP
+Donn/RM
+donnybrook/MS
+Donny/M
+donor/MS
+Donovan/M
+don/S
+Don/SM
+don't
+donut/MS
+donutted
+donutting
+doodad/MS
+doodlebug/MS
+doodler/M
+doodle/SRDZG
+doohickey/MS
+Dooley/M
+Doolittle/M
+doom/MDGS
+doomsday/SM
+Doonesbury/M
+doorbell/SM
+door/GDMS
+doorhandles
+doorkeeper/M
+doorkeep/RZ
+doorknob/SM
+doorman/M
+doormat/SM
+doormen
+doornail/M
+doorplate/SM
+doors/I
+doorstep/MS
+doorstepped
+doorstepping
+doorstop/MS
+doorway/MS
+dooryard/SM
+dopamine
+dopant/M
+dopa/SM
+dope/DRSMZG
+doper/M
+dopey
+dopier
+dopiest
+dopiness/S
+Doppler/M
+Dorado/M
+Doralia/M
+Doralin/M
+Doralyn/M
+Doralynne/M
+Doralynn/M
+Dora/M
+Dorcas
+Dorchester/M
+Doreen/M
+Dorelia/M
+Dorella/M
+Dorelle/M
+Doré/M
+Dorena/M
+Dorene/M
+Doretta/M
+Dorette/M
+Dorey/M
+Doria/M
+Dorian/M
+Doric
+Dorice/M
+Dorie/M
+Dori/MS
+Dorine/M
+Dorisa/M
+Dorise/M
+Dorita/M
+dork/S
+dorky/RT
+dormancy/MS
+dormant/S
+dormer/M
+dormice
+dormitory/SM
+dorm/MRZS
+dormouse/M
+Dorolice/M
+Dorolisa/M
+Doro/M
+Dorotea/M
+Doroteya/M
+Dorothea/M
+Dorothee/M
+Dorothy/M
+Dorree/M
+Dorrie/M
+Dorri/SM
+Dorry/M
+dorsal/YS
+Dorsey/M
+Dorthea/M
+Dorthy/M
+Dortmund/M
+Dory/M
+dory/SM
+DOS
+dosage/SM
+dose/M
+dos/GDS
+Dosi/M
+dosimeter/MS
+dosimetry/M
+dossier/MS
+dost
+Dostoevsky/M
+DOT
+dotage/SM
+dotard/MS
+doter/M
+dote/S
+Doti/M
+doting/Y
+Dot/M
+dot/MDRSJZG
+Dotson/M
+dotted
+Dottie/M
+Dotti/M
+dottiness/M
+dotting
+Dotty/M
+dotty/PRT
+do/TZRHGJ
+Douala/M
+Douay/M
+Doubleday/M
+doubled/UA
+double/GPSRDZ
+doubleheader/MS
+doubleness/M
+doubler/M
+doubles/M
+doublespeak/S
+doublethink/M
+doublet/MS
+doubleton/M
+doubling/A
+doubloon/MS
+doubly
+doubt/AGSDMB
+doubted/U
+doubter/SM
+doubtfulness/SM
+doubtful/YP
+doubting/Y
+doubtlessness/M
+doubtless/YP
+douche/GSDM
+Dougherty/M
+dough/M
+doughs
+doughty/RT
+doughy/RT
+Dougie/M
+Douglas/M
+Douglass
+Doug/M
+Dougy/M
+dourness/MS
+Douro/M
+dour/TYRP
+douser/M
+douse/SRDG
+dovecote/MS
+Dover/M
+dove/RSM
+dovetail/GSDM
+dovish
+Dov/MR
+dowager/SM
+dowdily
+dowdiness/MS
+dowdy/TPSR
+dowel/GMDS
+dower/GDMS
+Dow/M
+downbeat/SM
+downcast/S
+downdraft/M
+downer/M
+Downey/M
+downfall/NMS
+downgrade/GSD
+down/GZSRD
+downheartedness/MS
+downhearted/PY
+downhill/RS
+downland
+download/DGS
+downpipes
+downplay/GDS
+downpour/MS
+downrange
+downrightness/M
+downright/YP
+downriver
+Downs
+downscale/GSD
+downside/S
+downsize/DSG
+downslope
+downspout/SM
+downstage/S
+downstairs
+downstate/SR
+downstream
+downswing/MS
+downtime/SM
+downtowner/M
+downtown/MRS
+downtrend/M
+downtrodden
+downturn/MS
+downwardness/M
+downward/YPS
+downwind
+downy/RT
+dowry/SM
+dowse/GZSRD
+dowser/M
+doxology/MS
+doyenne/SM
+doyen/SM
+Doyle/M
+Doy/M
+doze
+dozen/GHD
+dozenths
+dozer/M
+doz/XGNDRS
+dozy
+DP
+DPs
+dpt
+DPT
+drabbed
+drabber
+drabbest
+drabbing
+drabness/MS
+drab/YSP
+drachma/MS
+Draco/M
+draconian
+Draconian
+Dracula/M
+draft/AMDGS
+draftee/SM
+drafter/MS
+draftily
+draftiness/SM
+drafting/S
+draftsman/M
+draftsmanship/SM
+draftsmen
+draftsperson
+draftswoman
+draftswomen
+drafty/PTR
+dragged
+dragger/M
+dragging/Y
+draggy/RT
+drag/MS
+dragnet/MS
+dragonfly/SM
+dragonhead/M
+dragon/SM
+dragoon/DMGS
+drainage/MS
+drainboard/SM
+drained/U
+drainer/M
+drainpipe/MS
+drain/SZGRDM
+Drake/M
+drake/SM
+Dramamine/MS
+drama/SM
+dramatically/U
+dramatical/Y
+dramatic/S
+dramatics/M
+dramatist/MS
+dramatization/MS
+dramatized/U
+dramatizer/M
+dramatize/SRDZG
+dramaturgy/M
+Drambuie/M
+drammed
+dramming
+dram/MS
+drank
+Drano/M
+draper/M
+drapery/MS
+drape/SRDGZ
+drastic
+drastically
+drat/S
+dratted
+dratting
+Dravidian/M
+drawable
+draw/ASG
+drawback/MS
+drawbridge/SM
+drawer/SM
+drawing/SM
+drawler/M
+drawling/Y
+drawl/RDSG
+drawly
+drawn/AI
+drawnly
+drawnness
+drawstring/MS
+dray/SMDG
+dreadfulness/SM
+dreadful/YPS
+dreadlocks
+dreadnought/SM
+dread/SRDG
+dreamboat/SM
+dreamed/U
+dreamer/M
+dreamily
+dreaminess/SM
+dreaming/Y
+dreamland/SM
+dreamlessness/M
+dreamless/PY
+dreamlike
+dream/SMRDZG
+dreamworld/S
+dreamy/PTR
+drearily
+dreariness/SM
+drear/S
+dreary/TRSP
+Dreddy/M
+dredge/MZGSRD
+dredger/M
+Dredi/M
+dreg/MS
+Dreiser/M
+Dre/M
+drencher/M
+drench/GDRS
+Dresden/M
+dress/ADRSG
+dressage/MS
+dressed/U
+dresser/MS
+dresser's/A
+dresses/U
+dressiness/SM
+dressing/MS
+dressmaker/MS
+dressmaking/SM
+dressy/PTR
+drew/A
+Drew/M
+Drexel/M
+Dreyfus/M
+Dreyfuss
+dribble/DRSGZ
+dribbler/M
+driblet/SM
+drib/SM
+dried/U
+drier/M
+drifter/M
+drifting/Y
+drift/RDZSG
+driftwood/SM
+driller/M
+drilling/M
+drillmaster/SM
+drill/MRDZGS
+drinkable/S
+drink/BRSZG
+drinker/M
+dripped
+dripping/MS
+drippy/RT
+drip/SM
+driveler/M
+drivel/GZDRS
+driven/P
+driver/M
+drive/SRBGZJ
+driveway/MS
+drizzle/DSGM
+drizzling/Y
+drizzly/TR
+Dr/M
+drogue/MS
+drollery/SM
+drollness/MS
+droll/RDSPTG
+drolly
+dromedary/MS
+Drona/M
+drone/SRDGM
+droning/Y
+drool/GSRD
+droopiness/MS
+drooping/Y
+droop/SGD
+droopy/PRT
+drophead
+dropkick/S
+droplet/SM
+dropout/MS
+dropped
+dropper/SM
+dropping/MS
+dropsical
+drop/SM
+dropsy/MS
+drosophila/M
+dross/SM
+drought/SM
+drover/M
+drove/SRDGZ
+drowner/M
+drown/RDSJG
+drowse/SDG
+drowsily
+drowsiness/SM
+drowsy/PTR
+drubbed
+drubber/MS
+drubbing/SM
+drub/S
+Drucie/M
+Drucill/M
+Druci/M
+Drucy/M
+drudge/MGSRD
+drudger/M
+drudgery/SM
+drudging/Y
+Drud/M
+drugged
+druggie/SRT
+drugging
+druggist/SM
+Drugi/M
+drugless
+drug/SM
+drugstore/SM
+druidism/MS
+druid/MS
+Druid's
+Dru/M
+drumbeat/SGM
+drumhead/M
+drumlin/MS
+drummed
+drummer/SM
+drumming
+Drummond/M
+drum/SM
+drumstick/SM
+drunkard/SM
+drunkenness/SM
+drunken/YP
+drunk/SRNYMT
+drupe/SM
+Drury/M
+Drusie/M
+Drusilla/M
+Drusi/M
+Drusy/M
+druthers
+dryad/MS
+Dryden/M
+dryer/MS
+dry/GYDRSTZ
+dryish
+dryness/SM
+drys
+drystone
+drywall/GSD
+D's
+d's/A
+Dshubba/M
+DST
+DTP
+dualism/MS
+dualistic
+dualist/M
+duality/MS
+dual/YS
+Duane/M
+Dubai/M
+dubbed
+dubber/S
+dubbing/M
+dubbin/MS
+Dubcek/M
+Dubhe/M
+dubiety/MS
+dubiousness/SM
+dubious/YP
+Dublin/M
+Dubrovnik/M
+dub/S
+Dubuque/M
+ducal
+ducat/SM
+duce/CAIKF
+duce's
+Duchamp/M
+duchess/MS
+duchy/SM
+duckbill/SM
+ducker/M
+duck/GSRDM
+duckling/SM
+duckpins
+duckpond
+duckweed/MS
+ducky/RSMT
+ducted/CFI
+ductile/I
+ductility/SM
+ducting/F
+duct/KMSF
+ductless
+duct's/A
+ducts/CI
+ductwork/M
+dudder
+dude/MS
+dudgeon/SM
+dud/GMDS
+Dudley/M
+Dud/M
+duelist/MS
+duel/MRDGZSJ
+dueness/M
+duenna/MS
+due/PMS
+duet/MS
+duetted
+duetting
+duffel/M
+duffer/M
+duff/GZSRDM
+Duffie/M
+Duff/M
+Duffy/M
+Dugald/M
+dugout/SM
+dug/S
+duh
+DUI
+Duisburg/M
+dukedom/SM
+duke/DSMG
+Duke/M
+Dukey/M
+Dukie/M
+Duky/M
+Dulcea/M
+Dulce/M
+dulcet/SY
+Dulcia/M
+Dulciana/M
+Dulcie/M
+dulcify
+Dulci/M
+dulcimer/MS
+Dulcinea/M
+Dulcine/M
+Dulcy/M
+dullard/MS
+Dulles/M
+dullness/MS
+dull/SRDPGT
+dully
+dulness's
+Dulsea/M
+Duluth/M
+duly/U
+Du/M
+Dumas
+dumbbell/MS
+dumbfound/GSDR
+dumbness/MS
+Dumbo/M
+dumb/PSGTYRD
+dumbstruck
+dumbwaiter/SM
+dumdum/MS
+dummy/SDMG
+Dumont/M
+dumper/UM
+dumpiness/MS
+dumpling/MS
+dump/SGZRD
+dumpster/S
+Dumpster/S
+Dumpty/M
+dumpy/PRST
+Dunant/M
+Dunbar/M
+Duncan/M
+dunce/MS
+Dunc/M
+Dundee/M
+dunderhead/MS
+Dunedin/M
+dune/SM
+dungaree/SM
+dungeon/GSMD
+dunghill/MS
+dung/SGDM
+Dunham/M
+dunker/M
+dunk/GSRD
+Dunkirk/M
+Dunlap/M
+Dun/M
+dunned
+Dunne/M
+dunner
+dunnest
+dunning
+Dunn/M
+dunno/M
+dun/S
+Dunstan/M
+duodecimal/S
+duodena
+duodenal
+duodenum/M
+duologue/M
+duo/MS
+duopolist
+duopoly/M
+dupe/NGDRSMZ
+duper/M
+dupion/M
+duple
+duplexer/M
+duplex/MSRDG
+duplicability/M
+duplicable
+duplicate/ADSGNX
+duplication/AM
+duplicative
+duplicator/MS
+duplicitous
+duplicity/SM
+Dupont/MS
+DuPont/MS
+durability/MS
+durableness/M
+durable/PS
+durably
+Duracell/M
+durance/SM
+Durand/M
+Duran/M
+Durante/M
+Durant/M
+durational
+duration/MS
+Durban/M
+Dürer/M
+duress/SM
+Durex/M
+Durham/MS
+during
+Durkee/M
+Durkheim/M
+Dur/M
+Durocher/M
+durst
+durum/MS
+Durward/M
+Duse/M
+Dusenberg/M
+Dusenbury/M
+Dushanbe/M
+dusk/GDMS
+duskiness/MS
+dusky/RPT
+Düsseldorf
+dustbin/MS
+dustcart/M
+dustcover
+duster/M
+dustily
+dustiness/MS
+dusting/M
+Dustin/M
+dustless
+dustman/M
+dustmen
+dust/MRDGZS
+dustpan/SM
+Dusty/M
+dusty/RPT
+Dutch/M
+Dutchman/M
+Dutchmen
+dutch/MS
+Dutchwoman
+Dutchwomen
+duteous/Y
+dutiable
+dutifulness/S
+dutiful/UPY
+duty/SM
+Duvalier/M
+duvet/SM
+duxes
+Dvina/M
+Dvorák/M
+Dwain/M
+dwarfish
+dwarfism/MS
+dwarf/MTGSPRD
+Dwayne/M
+dweeb/S
+dweller/SM
+dwell/IGS
+dwelling/MS
+dwelt/I
+DWI
+Dwight/M
+dwindle/GSD
+dyadic
+dyad/MS
+Dyana/M
+Dyane/M
+Dyan/M
+Dyanna/M
+Dyanne/M
+Dyann/M
+dybbukim
+dybbuk/SM
+dyed/A
+dyeing/M
+dye/JDRSMZG
+dyer/M
+Dyer/M
+dyes/A
+dyestuff/SM
+dying/UA
+Dyke/M
+dyke's
+Dylan/M
+Dy/M
+Dynah/M
+Dyna/M
+dynamical/Y
+dynamic/S
+dynamics/M
+dynamism/SM
+dynamiter/M
+dynamite/RSDZMG
+dynamized
+dynamo/MS
+dynastic
+dynasty/MS
+dyne/M
+dysentery/SM
+dysfunctional
+dysfunction/MS
+dyslectic/S
+dyslexia/MS
+dyslexically
+dyslexic/S
+dyspepsia/MS
+dyspeptic/S
+dysprosium/MS
+dystopia/M
+dystrophy/M
+dz
+Dzerzhinsky/M
+E
+ea
+each
+Eachelle/M
+Eada/M
+Eadie/M
+Eadith/M
+Eadmund/M
+eagerness/MS
+eager/TSPRYM
+eagle/SDGM
+eaglet/SM
+Eakins/M
+Ealasaid/M
+Eal/M
+Eamon/M
+earache/SM
+eardrum/SM
+earful/MS
+ear/GSMDYH
+Earhart/M
+earing/M
+earldom/MS
+Earle/M
+Earlene/M
+Earlie/M
+Earline/M
+earliness/SM
+Earl/M
+earl/MS
+earlobe/S
+Early/M
+early/PRST
+earmark/DGSJ
+earmuff/SM
+earned/U
+earner/M
+Earnestine/M
+Earnest/M
+earnestness/MS
+earnest/PYS
+earn/GRDZTSJ
+earning/M
+earphone/MS
+earpieces
+earplug/MS
+Earp/M
+earring/MS
+earshot/MS
+earsplitting
+Eartha/M
+earthbound
+earthed/U
+earthenware/MS
+earthiness/SM
+earthliness/M
+earthling/MS
+earthly/TPR
+earth/MDNYG
+earthmen
+earthmover/M
+earthmoving
+earthquake/SDGM
+earthshaking
+earths/U
+earthward/S
+earthwork/MS
+earthworm/MS
+earthy/PTR
+Earvin/M
+earwax/MS
+earwigged
+earwigging
+earwig/MS
+eased/E
+ease/LDRSMG
+easel/MS
+easement/MS
+easer/M
+ease's/EU
+eases/UE
+easies
+easily/U
+easiness/MSU
+easing/M
+eastbound
+easterly/S
+Easter/M
+easterner/M
+Easterner/M
+easternmost
+Eastern/RZ
+eastern/ZR
+easter/Y
+east/GSMR
+Easthampton/M
+easting/M
+Eastland/M
+Eastman/M
+eastward/S
+Eastwick/M
+Eastwood/M
+East/ZSMR
+easygoingness/M
+easygoing/P
+easy/PUTR
+eatables
+eatable/U
+eaten/U
+eater/M
+eatery/MS
+eating/M
+Eaton/M
+eat/SJZGNRB
+eavesdropped
+eavesdropper/MS
+eavesdropping
+eavesdrop/S
+eave/SM
+Eba/M
+Ebba/M
+ebb/DSG
+EBCDIC
+Ebeneezer/M
+Ebeneser/M
+Ebenezer/M
+Eben/M
+Eberhard/M
+Eberto/M
+Eb/MN
+Ebola
+Ebonee/M
+Ebonics
+Ebony/M
+ebony/SM
+Ebro/M
+ebullience/SM
+ebullient/Y
+ebullition/SM
+EC
+eccentrically
+eccentricity/SM
+eccentric/MS
+eccl
+Eccles
+Ecclesiastes/M
+ecclesiastical/Y
+ecclesiastic/MS
+ECG
+echelon/SGDM
+echinoderm/SM
+echo/DMG
+echoed/A
+echoes/A
+echoic
+echolocation/SM
+éclair/MS
+éclat/MS
+eclectically
+eclecticism/MS
+eclectic/S
+eclipse/MGSD
+ecliptic/MS
+eclogue/MS
+ecocide/SM
+ecol
+Ecole/M
+ecologic
+ecological/Y
+ecologist/MS
+ecology/MS
+Eco/M
+econ
+Econometrica/M
+econometricians
+econometric/S
+econometrics/M
+economical/YU
+economic/S
+economics/M
+economist/MS
+economization
+economize/GZSRD
+economizer/M
+economizing/U
+economy/MS
+ecosystem/MS
+ecru/SM
+ecstasy/MS
+Ecstasy/S
+ecstatically
+ecstatic/S
+ectoplasm/M
+Ecuadoran/S
+Ecuadorean/S
+Ecuadorian/S
+Ecuador/M
+ecumenical/Y
+ecumenicism/SM
+ecumenicist/MS
+ecumenic/MS
+ecumenics/M
+ecumenism/SM
+ecumenist/MS
+eczema/MS
+Eda/M
+Edam/SM
+Edan/M
+ed/ASC
+Edda/M
+Eddie/M
+Eddi/M
+Edd/M
+Eddy/M
+eddy/SDMG
+Edee/M
+Edeline/M
+edelweiss/MS
+Ede/M
+edema/SM
+edematous
+eden
+Eden/M
+Edgard/M
+Edgardo/M
+Edgar/M
+edge/DRSMZGJ
+edgeless
+edger/M
+Edgerton/M
+Edgewater/M
+edgewise
+Edgewood/M
+edgily
+edginess/MS
+edging/M
+edgy/TRP
+edibility/MS
+edibleness/SM
+edible/SP
+edict/SM
+Edie/M
+edification/M
+edifice/SM
+edifier/M
+edifying/U
+edify/ZNXGRSD
+Edik/M
+Edi/MH
+Edinburgh/M
+Edin/M
+Edison/M
+editable
+Edita/M
+edited/IU
+Editha/M
+Edithe/M
+Edith/M
+edition/SM
+editorialist/M
+editorialize/DRSG
+editorializer/M
+editorial/YS
+editor/MS
+editorship/MS
+edit/SADG
+Ediva/M
+Edlin/M
+Edmond/M
+Edmon/M
+Edmonton/M
+Edmund/M
+Edna/M
+Edouard/M
+EDP
+eds
+Edsel/M
+Edsger/M
+EDT
+Eduard/M
+Eduardo/M
+educability/SM
+educable/S
+educated/YP
+educate/XASDGN
+educationalists
+educational/Y
+education/AM
+educationists
+educative
+educator/MS
+educ/DBG
+educe/S
+eduction/M
+Eduino/M
+edutainment/S
+Edvard/M
+Edwardian
+Edwardo/M
+Edward/SM
+Edwina/M
+Edwin/M
+Ed/XMN
+Edy/M
+Edythe/M
+Edyth/M
+EEC
+EEG
+eek/S
+eelgrass/M
+eel/MS
+e'en
+EEO
+EEOC
+e'er
+eerie/RT
+eerily
+eeriness/MS
+Eeyore/M
+effaceable/I
+effacement/MS
+effacer/M
+efface/SRDLG
+effectiveness/ISM
+effectives
+effective/YIP
+effector/MS
+effect/SMDGV
+effectual/IYP
+effectualness/MI
+effectuate/SDGN
+effectuation/M
+effeminacy/MS
+effeminate/SY
+effendi/MS
+efferent/SY
+effervesce/GSD
+effervescence/SM
+effervescent/Y
+effeteness/SM
+effete/YP
+efficacious/IPY
+efficaciousness/MI
+efficacy/IMS
+efficiency/MIS
+efficient/ISY
+Effie/M
+effigy/SM
+effloresce
+efflorescence/SM
+efflorescent
+effluence/SM
+effluent/MS
+effluvia
+effluvium/M
+effluxion
+efflux/M
+effortlessness/SM
+effortless/PY
+effort/MS
+effrontery/MS
+effulgence/SM
+effulgent
+effuse/XSDVGN
+effusion/M
+effusiveness/MS
+effusive/YP
+EFL
+e/FMDS
+Efrain/M
+Efrem/M
+Efren/M
+EFT
+egad
+egalitarian/I
+egalitarianism/MS
+egalitarians
+EGA/M
+Egan/M
+Egbert/M
+Egerton/M
+eggbeater/SM
+eggcup/MS
+egger/M
+egg/GMDRS
+eggheaded/P
+egghead/SDM
+eggnog/SM
+eggplant/MS
+eggshell/SM
+egis's
+eglantine/MS
+egocentrically
+egocentricity/SM
+egocentric/S
+egoism/SM
+egoistic
+egoistical/Y
+egoist/SM
+egomaniac/MS
+egomania/MS
+Egon/M
+Egor/M
+ego/SM
+egotism/SM
+egotistic
+egotistical/Y
+egotist/MS
+egregiousness/MS
+egregious/PY
+egress/SDMG
+egret/SM
+Egyptian/S
+Egypt/M
+Egyptology/M
+eh
+Ehrlich/M
+Eichmann/M
+eiderdown/SM
+eider/SM
+eidetic
+Eiffel/M
+eigenfunction/MS
+eigenstate/S
+eigenvalue/SM
+eigenvector/MS
+eighteen/MHS
+eighteenths
+eightfold
+eighth/MS
+eighths
+eightieths
+eightpence
+eight/SM
+eighty/SHM
+Eileen/M
+Eilis/M
+Eimile/M
+Einsteinian
+einsteinium/MS
+Einstein/SM
+Eire/M
+Eirena/M
+Eisenhower/M
+Eisenstein/M
+Eisner/M
+eisteddfod/M
+either
+ejaculate/SDXNG
+ejaculation/M
+ejaculatory
+ejecta
+ejection/SM
+ejector/SM
+eject/VGSD
+Ekaterina/M
+Ekberg/M
+eked/A
+eke/DSG
+EKG
+Ekstrom/M
+Ektachrome/M
+elaborateness/SM
+elaborate/SDYPVNGX
+elaboration/M
+elaborators
+Elaina/M
+Elaine/M
+Elana/M
+eland/SM
+Elane/M
+élan/M
+Elanor/M
+elans
+elapse/SDG
+el/AS
+elastically/I
+elasticated
+elasticity/SM
+elasticize/GDS
+elastic/S
+elastodynamics
+elastomer/M
+elatedness/M
+elated/PY
+elater/M
+elate/SRDXGN
+elation/M
+Elayne/M
+Elba/MS
+Elbe/M
+Elberta/M
+Elbertina/M
+Elbertine/M
+Elbert/M
+elbow/GDMS
+elbowroom/SM
+Elbrus/M
+Elden/M
+elderberry/MS
+elderflower
+elderliness/M
+elderly/PS
+elder/SY
+eldest
+Eldin/M
+Eldon/M
+Eldorado's
+Eldredge/M
+Eldridge/M
+Eleanora/M
+Eleanore/M
+Eleanor/M
+Eleazar/M
+electable/U
+elect/ASGD
+elected/U
+electioneer/GSD
+election/SAM
+electiveness/M
+elective/SPY
+electoral/Y
+electorate/SM
+elector/SM
+Electra/M
+electress/M
+electricalness/M
+electrical/PY
+electrician/SM
+electricity/SM
+electric/S
+electrification/M
+electrifier/M
+electrify/ZXGNDRS
+electrocardiogram/MS
+electrocardiograph/M
+electrocardiographs
+electrocardiography/MS
+electrochemical/Y
+electrocute/GNXSD
+electrocution/M
+electrode/SM
+electrodynamics/M
+electrodynamic/YS
+electroencephalogram/SM
+electroencephalographic
+electroencephalograph/M
+electroencephalographs
+electroencephalography/MS
+electrologist/MS
+electroluminescent
+electrolysis/M
+electrolyte/SM
+electrolytic
+electrolytically
+electrolyze/SDG
+electro/M
+electromagnetic
+electromagnetically
+electromagnetism/SM
+electromagnet/SM
+electromechanical
+electromechanics
+electromotive
+electromyograph
+electromyographic
+electromyographically
+electromyography/M
+electronegative
+electronically
+electronic/S
+electronics/M
+electron/MS
+electrophoresis/M
+electrophorus/M
+electroplate/DSG
+electroscope/MS
+electroscopic
+electroshock/GDMS
+electrostatic/S
+electrostatics/M
+electrotherapist/M
+electrotype/GSDZM
+electroweak
+eleemosynary
+Eleen/M
+elegance/ISM
+elegant/YI
+elegiacal
+elegiac/S
+elegy/SM
+elem
+elemental/YS
+elementarily
+elementariness/M
+elementary/P
+element/MS
+Elena/M
+Elene/M
+Eleni/M
+Elenore/M
+Eleonora/M
+Eleonore/M
+elephantiases
+elephantiasis/M
+elephantine
+elephant/SM
+elevated/S
+elevate/XDSNG
+elevation/M
+elevator/SM
+eleven/HM
+elevens/S
+elevenths
+elev/NX
+Elfie/M
+elfin/S
+elfish
+elf/M
+Elfreda/M
+Elfrida/M
+Elfrieda/M
+Elga/M
+Elgar/M
+Elianora/M
+Elianore/M
+Elia/SM
+Elicia/M
+elicitation/MS
+elicit/GSD
+elide/GSD
+Elie/M
+eligibility/ISM
+eligible/SI
+Elihu/M
+Elijah/M
+Eli/M
+eliminate/XSDYVGN
+elimination/M
+eliminator/SM
+Elinore/M
+Elinor/M
+Eliot/M
+Elisabeth/M
+Elisabet/M
+Elisabetta/M
+Elisa/M
+Elise/M
+Eliseo/M
+Elisha/M
+elision/SM
+Elissa/M
+Elita/M
+elite/MPS
+elitism/SM
+elitist/SM
+elixir/MS
+Elizabethan/S
+Elizabeth/M
+Elizabet/M
+Eliza/M
+Elka/M
+Elke/M
+Elkhart/M
+elk/MS
+Elladine/M
+Ella/M
+Ellary/M
+Elle/M
+Ellene/M
+Ellen/M
+Ellerey/M
+Ellery/M
+Ellesmere/M
+Ellette/M
+Ellie/M
+Ellington/M
+Elliot/M
+Elliott/M
+ellipse/MS
+ellipsis/M
+ellipsoidal
+ellipsoid/MS
+ellipsometer/MS
+ellipsometry
+elliptic
+elliptical/YS
+ellipticity/M
+Elli/SM
+Ellison/M
+Ellissa/M
+ell/MS
+Ellswerth/M
+Ellsworth/M
+Ellwood/M
+Elly/M
+Ellyn/M
+Ellynn/M
+Elma/M
+Elmer/M
+Elmhurst/M
+Elmira/M
+elm/MRS
+Elmo/M
+Elmore/M
+Elmsford/M
+El/MY
+Elna/MH
+Elnar/M
+Elnath/M
+Elnora/M
+Elnore/M
+elocutionary
+elocutionist/MS
+elocution/SM
+elodea/S
+Elohim/M
+Eloisa/M
+Eloise/M
+elongate/NGXSD
+elongation/M
+Elonore/M
+elopement/MS
+eloper/M
+elope/SRDLG
+eloquence/SM
+eloquent/IY
+Elora/M
+Eloy/M
+Elroy/M
+els
+Elsa/M
+Elsbeth/M
+else/M
+Else/M
+Elset/M
+elsewhere
+Elsey/M
+Elsie/M
+Elsi/M
+Elsinore/M
+Elspeth/M
+Elston/M
+Elsworth/M
+Elsy/M
+Eltanin/M
+Elton/M
+eluate/SM
+elucidate/SDVNGX
+elucidation/M
+elude/GSD
+elusiveness/SM
+elusive/YP
+elute/DGN
+elution/M
+Elva/M
+elven
+Elvera/M
+elver/SM
+elves/M
+Elvia/M
+Elvina/M
+Elvin/M
+Elvira/M
+elvish
+Elvis/M
+Elvyn/M
+Elwin/M
+Elwira/M
+Elwood/M
+Elwyn/M
+Ely/M
+Elyn/M
+Elysée/M
+Elysees
+Elyse/M
+Elysha/M
+Elysia/M
+elysian
+Elysian
+Elysium/SM
+Elyssa/M
+EM
+emaciate/NGXDS
+emaciation/M
+emacs/M
+Emacs/M
+email/SMDG
+Emalee/M
+Emalia/M
+Ema/M
+emanate/XSDVNG
+emanation/M
+emancipate/DSXGN
+emancipation/M
+emancipator/MS
+Emanuele/M
+Emanuel/M
+emasculate/GNDSX
+emasculation/M
+embalmer/M
+embalm/ZGRDS
+embank/GLDS
+embankment/MS
+embarcadero
+embargoes
+embargo/GMD
+embark/ADESG
+embarkation/EMS
+embarrassedly
+embarrassed/U
+embarrassing/Y
+embarrassment/MS
+embarrass/SDLG
+embassy/MS
+embattle/DSG
+embeddable
+embedded
+embedder
+embedding/MS
+embed/S
+embellished/U
+embellisher/M
+embellish/LGRSD
+embellishment/MS
+ember/MS
+embezzle/LZGDRS
+embezzlement/MS
+embezzler/M
+embitter/LGDS
+embitterment/SM
+emblazon/DLGS
+emblazonment/SM
+emblematic
+emblem/GSMD
+embodier/M
+embodiment/ESM
+embody/ESDGA
+embolden/DSG
+embolism/SM
+embosom
+embosser/M
+emboss/ZGRSD
+embouchure/SM
+embower/GSD
+embraceable
+embracer/M
+embrace/RSDVG
+embracing/Y
+embrasure/MS
+embrittle
+embrocation/SM
+embroiderer/M
+embroider/SGZDR
+embroidery/MS
+embroilment/MS
+embroil/SLDG
+embryologist/SM
+embryology/MS
+embryonic
+embryo/SM
+emceeing
+emcee/SDM
+Emelda/M
+Emelen/M
+Emelia/M
+Emelina/M
+Emeline/M
+Emelita/M
+Emelyne/M
+emendation/MS
+emend/SRDGB
+emerald/SM
+Emera/M
+emerge/ADSG
+emergence/MAS
+emergency/SM
+emergent/S
+emerita
+emeritae
+emeriti
+emeritus
+Emerson/M
+Emery/M
+emery/MGSD
+emetic/S
+emf/S
+emigrant/MS
+emigrate/SDXNG
+emigration/M
+émigré/S
+Emilee/M
+Emile/M
+Emilia/M
+Emilie/M
+Emili/M
+Emiline/M
+Emilio/M
+Emil/M
+Emily/M
+eminence/MS
+Eminence/MS
+eminent/Y
+emirate/SM
+emir/SM
+emissary/SM
+emission/AMS
+emissivity/MS
+emit/S
+emittance/M
+emitted
+emitter/SM
+emitting
+Emlen/M
+Emlyn/M
+Emlynne/M
+Emlynn/M
+em/M
+Em/M
+Emmalee/M
+Emmaline/M
+Emmalyn/M
+Emmalynne/M
+Emmalynn/M
+Emma/M
+Emmanuel/M
+Emmeline/M
+Emmerich/M
+Emmery/M
+Emmet/M
+Emmett/M
+Emmey/M
+Emmie/M
+Emmi/M
+Emmit/M
+Emmott/M
+Emmye/M
+Emmy/SM
+Emogene/M
+emollient/S
+emolument/SM
+Emory/M
+emote/SDVGNX
+emotionalism/MS
+emotionality/M
+emotionalize/GDS
+emotional/UY
+emotionless
+emotion/M
+emotive/Y
+empaneled
+empaneling
+empath
+empathetic
+empathetical/Y
+empathic
+empathize/SDG
+empathy/MS
+emperor/MS
+emphases
+emphasis/M
+emphasize/ZGCRSDA
+emphatically/U
+emphatic/U
+emphysema/SM
+emphysematous
+empire/MS
+empirical/Y
+empiricism/SM
+empiricist/SM
+empiric/SM
+emplace/L
+emplacement/MS
+employability/UM
+employable/US
+employed/U
+employee/SM
+employer/SM
+employ/LAGDS
+employment/UMAS
+emporium/MS
+empower/GLSD
+empowerment/MS
+empress/MS
+emptier/M
+emptily
+emptiness/SM
+empty/GRSDPT
+empyrean/SM
+ems/C
+EMT
+emulate/SDVGNX
+emulation/M
+emulative/Y
+emulator/MS
+emulsification/M
+emulsifier/M
+emulsify/NZSRDXG
+emulsion/SM
+emu/SM
+Emylee/M
+Emyle/M
+enabler/M
+enable/SRDZG
+enactment/ASM
+enact/SGALD
+enameler/M
+enamelware/SM
+enamel/ZGJMDRS
+enamor/DSG
+en/BM
+enc
+encamp/LSDG
+encampment/MS
+encapsulate/SDGNX
+encapsulation/M
+encase/GSDL
+encasement/SM
+encephalitic
+encephalitides
+encephalitis/M
+encephalographic
+encephalopathy/M
+enchain/SGD
+enchanter/MS
+enchant/ESLDG
+enchanting/Y
+enchantment/MSE
+enchantress/MS
+enchilada/SM
+encipherer/M
+encipher/SRDG
+encircle/GLDS
+encirclement/SM
+encl
+enclave/MGDS
+enclosed/U
+enclose/GDS
+enclosure/SM
+encoder/M
+encode/ZJGSRD
+encomium/SM
+encompass/GDS
+encore/GSD
+encounter/GSD
+encouragement/SM
+encourager/M
+encourage/SRDGL
+encouraging/Y
+encroacher/M
+encroach/LGRSD
+encroachment/MS
+encrustation/MS
+encrust/DSG
+encrypt/DGS
+encrypted/U
+encryption/SM
+encumbered/U
+encumber/SEDG
+encumbrancer/M
+encumbrance/SRM
+ency
+encyclical/SM
+encyclopaedia's
+encyclopedia/SM
+encyclopedic
+encyst/GSLD
+encystment/MS
+endanger/DGSL
+endangerment/SM
+endear/GSLD
+endearing/Y
+endearment/MS
+endeavored/U
+endeavorer/M
+endeavor/GZSMRD
+endemically
+endemicity
+endemic/S
+ender/M
+endgame/M
+Endicott/M
+ending/M
+endive/SM
+endlessness/MS
+endless/PY
+endmost
+endnote/MS
+endocrine/S
+endocrinologist/SM
+endocrinology/SM
+endogamous
+endogamy/M
+endogenous/Y
+endomorphism/SM
+endorse/DRSZGL
+endorsement/MS
+endorser/M
+endoscope/MS
+endoscopic
+endoscopy/SM
+endosperm/M
+endothelial
+endothermic
+endow/GSDL
+endowment/SM
+endpoint/MS
+endue/SDG
+endungeoned
+endurable/U
+endurably/U
+endurance/SM
+endure/BSDG
+enduringness/M
+enduring/YP
+endways
+Endymion/M
+end/ZGVMDRSJ
+ENE
+enema/SM
+enemy/SM
+energetically
+energetic/S
+energetics/M
+energized/U
+energizer/M
+energize/ZGDRS
+energy/MS
+enervate/XNGVDS
+enervation/M
+enfeeble/GLDS
+enfeeblement/SM
+enfilade/MGDS
+enfold/SGD
+enforceability/M
+enforceable/U
+enforced/Y
+enforce/LDRSZG
+enforcement/SM
+enforcer/M
+enforcible/U
+enfranchise/ELDRSG
+enfranchisement/EMS
+enfranchiser/M
+engage/ADSGE
+engagement/SEM
+engaging/Y
+Engelbert/M
+Engel/MS
+engender/DGS
+engineer/GSMDJ
+engineering/MY
+engine/MGSD
+England/M
+england/ZR
+Englebert/M
+Englewood/M
+English/GDRSM
+Englishman/M
+Englishmen
+Englishwoman/M
+Englishwomen
+Eng/M
+engorge/LGDS
+engorgement/MS
+Engracia/M
+engram/MS
+engraver/M
+engrave/ZGDRSJ
+engraving/M
+engrossed/Y
+engrosser/M
+engross/GLDRS
+engrossing/Y
+engrossment/SM
+engulf/GDSL
+engulfment/SM
+enhanceable
+enhance/LZGDRS
+enhancement/MS
+enhancer/M
+enharmonic
+Enid/M
+Enif/M
+enigma/MS
+enigmatic
+enigmatically
+Eniwetok/M
+enjambement's
+enjambment/MS
+enjoinder
+enjoin/GSD
+enjoyability
+enjoyableness/M
+enjoyable/P
+enjoyably
+enjoy/GBDSL
+enjoyment/SM
+Enkidu/M
+enlargeable
+enlarge/LDRSZG
+enlargement/MS
+enlarger/M
+enlightened/U
+enlighten/GDSL
+enlightening/U
+enlightenment/SM
+enlistee/MS
+enlister/M
+enlistment/SAM
+enlist/SAGDL
+enliven/LDGS
+enlivenment/SM
+enmesh/DSLG
+enmeshment/SM
+enmity/MS
+Ennis/M
+ennoble/LDRSG
+ennoblement/SM
+ennobler/M
+ennui/SM
+Enoch/M
+enormity/SM
+enormousness/MS
+enormous/YP
+Enos
+enough
+enoughs
+enplane/DSG
+enqueue/DS
+enquirer/S
+enquiringly
+enrage/SDG
+enrapture/GSD
+Enrica/M
+enricher/M
+Enrichetta/M
+enrich/LDSRG
+enrichment/SM
+Enrico/M
+Enrika/M
+Enrique/M
+Enriqueta/M
+enrobed
+enrollee/SM
+enroll/LGSD
+enrollment/SM
+ens
+ensconce/DSG
+ensemble/MS
+enshrine/DSLG
+enshrinement/SM
+enshroud/DGS
+ensign/SM
+ensilage/DSMG
+enslavement/MS
+enslaver/M
+enslave/ZGLDSR
+ensnare/GLDS
+ensnarement/SM
+Ensolite/M
+ensue/SDG
+ensurer/M
+ensure/SRDZG
+entailer/M
+entailment/MS
+entail/SDRLG
+entangle/EGDRSL
+entanglement/ESM
+entangler/EM
+entente/MS
+enter/ASDG
+entered/U
+enterer/M
+enteritides
+enteritis/SM
+enterprise/GMSR
+Enterprise/M
+enterpriser/M
+enterprising/Y
+entertainer/M
+entertaining/Y
+entertainment/SM
+entertain/SGZRDL
+enthalpy/SM
+enthrall/GDSL
+enthrallment/SM
+enthrone/GDSL
+enthronement/MS
+enthuse/DSG
+enthusiasm/SM
+enthusiastically/U
+enthusiastic/U
+enthusiast/MS
+enticement/SM
+entice/SRDJLZG
+enticing/Y
+entire/SY
+entirety/SM
+entitle/GLDS
+entitlement/MS
+entity/SM
+entomb/GDSL
+entombment/MS
+entomological
+entomologist/S
+entomology/MS
+entourage/SM
+entr'acte/S
+entrails
+entrainer/M
+entrain/GSLDR
+entrancement/MS
+entrance/MGDSL
+entranceway/M
+entrancing/Y
+entrant/MS
+entrapment/SM
+entrapped
+entrapping
+entrap/SL
+entreating/Y
+entreat/SGD
+entreaty/SM
+entrée/S
+entrench/LSDG
+entrenchment/MS
+entrepreneurial
+entrepreneur/MS
+entrepreneurship/M
+entropic
+entropy/MS
+entrust/DSG
+entry/ASM
+entryway/SM
+entwine/DSG
+enumerable
+enumerate/AN
+enumerated/U
+enumerates
+enumerating
+enumeration's/A
+enumeration/SM
+enumerative
+enumerator/SM
+enunciable
+enunciated/U
+enunciate/XGNSD
+enunciation/M
+enureses
+enuresis/M
+envelope/MS
+enveloper/M
+envelopment/MS
+envelop/ZGLSDR
+envenom/SDG
+enviableness/M
+enviable/U
+enviably
+envied/U
+envier/M
+enviousness/SM
+envious/PY
+environ/LGSD
+environmentalism/SM
+environmentalist/SM
+environmental/Y
+environment/MS
+envisage/DSG
+envision/GSD
+envoy/SM
+envying/Y
+envy/SRDMG
+enzymatic
+enzymatically
+enzyme/SM
+enzymology/M
+Eocene
+EOE
+eohippus/M
+Eolanda/M
+Eolande/M
+eolian
+eon/SM
+EPA
+epaulet/SM
+épée/S
+ephedrine/MS
+ephemeral/SY
+ephemera/MS
+ephemerids
+ephemeris/M
+Ephesian/S
+Ephesians/M
+Ephesus/M
+Ephraim/M
+Ephrayim/M
+Ephrem/M
+epically
+epicenter/SM
+epic/SM
+Epictetus/M
+Epicurean
+epicurean/S
+epicure/SM
+Epicurus/M
+epicycle/MS
+epicyclic
+epicyclical/Y
+epicycloid/M
+epidemically
+epidemic/MS
+epidemiological/Y
+epidemiologist/MS
+epidemiology/MS
+epidermal
+epidermic
+epidermis/MS
+epidural
+epigenetic
+epiglottis/SM
+epigrammatic
+epigram/MS
+epigrapher/M
+epigraph/RM
+epigraphs
+epigraphy/MS
+epilepsy/SM
+epileptic/S
+epilogue/SDMG
+Epimethius/M
+epinephrine/SM
+epiphany/SM
+Epiphany/SM
+epiphenomena
+episcopacy/MS
+episcopalian
+Episcopalian/S
+Episcopal/S
+episcopal/Y
+episcopate/MS
+episode/SM
+episodic
+episodically
+epistemic
+epistemological/Y
+epistemology/M
+epistle/MRS
+Epistle/SM
+epistolary/S
+epistolatory
+epitaph/GMD
+epitaphs
+epitaxial/Y
+epitaxy/M
+epithelial
+epithelium/MS
+epithet/MS
+epitome/MS
+epitomized/U
+epitomizer/M
+epitomize/SRDZG
+epochal/Y
+epoch/M
+epochs
+eponymous
+epoxy/GSD
+epsilon/SM
+Epsom/M
+Epstein/M
+equability/MS
+equableness/M
+equable/P
+equably
+equaling
+equality/ISM
+equalization/MS
+equalize/DRSGJZ
+equalized/U
+equalizer/M
+equalizes/U
+equal/USDY
+equanimity/MS
+equate/NGXBSD
+equation/M
+equatorial/S
+equator/SM
+equerry/MS
+equestrianism/SM
+equestrian/S
+equestrienne/SM
+equiangular
+equidistant/Y
+equilateral/S
+equilibrate/GNSD
+equilibration/M
+equilibrium/MSE
+equine/S
+equinoctial/S
+equinox/MS
+equipage/SM
+equipartition/M
+equip/AS
+equipment/SM
+equipoise/GMSD
+equipotent
+equipped/AU
+equipping/A
+equiproportional
+equiproportionality
+equiproportionate
+equitable/I
+equitableness/M
+equitably/I
+equitation/SM
+equity/IMS
+equiv
+equivalence/DSMG
+equivalent/SY
+equivocalness/MS
+equivocal/UY
+equivocate/NGSDX
+equivocation/M
+equivocator/SM
+Equuleus/M
+ER
+ERA
+eradicable/I
+eradicate/SDXVGN
+eradication/M
+eradicator/SM
+era/MS
+Eran/M
+erase/N
+eraser/M
+erasion/M
+Erasmus/M
+eras/SRDBGZ
+Erastus/M
+erasure/MS
+Erato/M
+Eratosthenes/M
+erbium/SM
+Erda/M
+ere
+Erebus/M
+erect/GPSRDY
+erectile
+erection/SM
+erectness/MS
+erector/SM
+Erek/M
+erelong
+eremite/MS
+Erena/M
+ergo
+ergodic
+ergodicity/M
+ergonomically
+ergonomics/M
+ergonomic/U
+ergophobia
+ergosterol/SM
+ergot/SM
+erg/SM
+Erhard/M
+Erhart/M
+Erica/M
+Ericha/M
+Erich/M
+Ericka/M
+Erick/M
+Erickson/M
+Eric/M
+Ericson's
+Ericsson's
+Eridanus/M
+Erie/SM
+Erika/M
+Erik/M
+Erikson/M
+Erina/M
+Erin/M
+Erinna/M
+Erinn/M
+eris
+Eris
+Eritrea/M
+Erlang/M
+Erlenmeyer/M
+Erl/M
+Er/M
+Erma/M
+Ermanno/M
+Ermengarde/M
+Ermentrude/M
+Ermina/M
+ermine/MSD
+Erminia/M
+Erminie/M
+Ermin/M
+Ernaline/M
+Erna/M
+Ernesta/M
+Ernestine/M
+Ernest/M
+Ernesto/M
+Ernestus/M
+Ernie/M
+Ernst/M
+Erny/M
+erode/SDG
+erodible
+erogenous
+erosible
+erosional
+erosion/SM
+erosiveness/M
+erosive/P
+Eros/SM
+erotically
+erotica/M
+eroticism/MS
+erotic/S
+errancy/MS
+errand/MS
+errantry/M
+errant/YS
+errata/SM
+erratically
+erratic/S
+erratum/MS
+err/DGS
+Errick/M
+erring/UY
+Erroll/M
+Errol/M
+erroneousness/M
+erroneous/YP
+error/SM
+ersatz/S
+Erse/M
+Erskine/M
+erst
+erstwhile
+Ertha/M
+eructation/MS
+eruct/DGS
+erudite/NYX
+erudition/M
+erupt/DSVG
+eruption/SM
+eruptive/SY
+Ervin/M
+ErvIn/M
+Erv/M
+Erwin/M
+Eryn/M
+erysipelas/SM
+erythrocyte/SM
+es
+e's
+Es
+E's
+Esau/M
+escadrille/M
+escalate/CDSXGN
+escalation/MC
+escalator/SM
+escallop/SGDM
+escapable/I
+escapade/SM
+escapee/MS
+escape/LGSRDB
+escapement/MS
+escaper/M
+escapism/SM
+escapist/S
+escapology
+escarole/MS
+escarpment/MS
+eschatology/M
+Escherichia/M
+Escher/M
+eschew/SGD
+Escondido/M
+escort/SGMD
+escritoire/SM
+escrow/DMGS
+escudo/MS
+escutcheon/SM
+Esdras/M
+ESE
+Eskimo/SM
+ESL
+Esma/M
+Esmaria/M
+Esmark/M
+Esme/M
+Esmeralda/M
+esophageal
+esophagi
+esophagus/M
+esoteric
+esoterica
+esoterically
+esp
+ESP
+espadrille/MS
+Espagnol/M
+espalier/SMDG
+especial/Y
+Esperanto/M
+Esperanza/M
+Espinoza/M
+espionage/SM
+esplanade/SM
+Esp/M
+Esposito/M
+espousal/MS
+espouser/M
+espouse/SRDG
+espresso/SM
+esprit/SM
+espy/GSD
+Esq/M
+esquire/GMSD
+Esquire/S
+Esra/M
+Essa/M
+essayer/M
+essayist/SM
+essay/SZMGRD
+essence/MS
+Essene/SM
+Essen/M
+essentialist/M
+essentially
+essentialness/M
+essential/USI
+Essequibo/M
+Essex/M
+Essie/M
+Essy/M
+EST
+established/U
+establisher/M
+establish/LAEGSD
+establishment/EMAS
+Establishment/MS
+Esta/M
+estate/GSDM
+Esteban/M
+esteem/EGDS
+Estela/M
+Estele/M
+Estella/M
+Estelle/M
+Estell/M
+Estel/M
+Esterházy/M
+ester/M
+Ester/M
+Estes
+Estevan/M
+Esther/M
+esthete's
+esthetically
+esthetic's
+esthetics's
+estimable/I
+estimableness/M
+estimate/XDSNGV
+estimating/A
+estimation/M
+estimator/SM
+Estonia/M
+Estonian/S
+estoppal
+Estrada/M
+estrange/DRSLG
+estrangement/SM
+estranger/M
+Estrella/M
+Estrellita/M
+estrogen/SM
+estrous
+estrus/SM
+est/RZ
+estuarine
+estuary/SM
+et
+ET
+ETA
+Etan/M
+eta/SM
+etc
+etcetera/SM
+etcher/M
+etch/GZJSRD
+etching/M
+ETD
+eternalness/SM
+eternal/PSY
+eternity/SM
+ethane/SM
+Ethan/M
+ethanol/MS
+Ethelbert/M
+Ethelda/M
+Ethelind/M
+Etheline/M
+Ethelin/M
+Ethel/M
+Ethelred/M
+Ethelyn/M
+Ethe/M
+etherealness/M
+ethereal/PY
+etherized
+Ethernet/MS
+ether/SM
+ethically/U
+ethicalness/M
+ethical/PYS
+ethicist/S
+ethic/MS
+Ethiopia/M
+Ethiopian/S
+ethnically
+ethnicity/MS
+ethnic/S
+ethnocentric
+ethnocentrism/MS
+ethnographers
+ethnographic
+ethnography/M
+ethnological
+ethnologist/SM
+ethnology/SM
+ethnomethodology
+ethological
+ethologist/MS
+ethology/SM
+ethos/SM
+ethylene/MS
+Ethyl/M
+ethyl/SM
+Etienne/M
+etiologic
+etiological
+etiology/SM
+etiquette/SM
+Etna/M
+Etruria/M
+Etruscan/MS
+Etta/M
+Ettie/M
+Etti/M
+Ettore/M
+Etty/M
+étude/MS
+etymological/Y
+etymologist/SM
+etymology/MS
+EU
+eucalypti
+eucalyptus/SM
+Eucharistic
+Eucharist/SM
+euchre/MGSD
+euclidean
+Euclid/M
+Eudora/M
+Euell/M
+Eugene/M
+Eugenia/M
+eugenically
+eugenicist/SM
+eugenic/S
+eugenics/M
+Eugenie/M
+Eugenio/M
+Eugenius/M
+Eugen/M
+Eugine/M
+Eulalie/M
+Eula/M
+Eulerian/M
+Euler/M
+eulogistic
+eulogist/MS
+eulogized/U
+eulogize/GRSDZ
+eulogizer/M
+eulogy/MS
+Eu/M
+Eumenides
+Eunice/M
+eunuch/M
+eunuchs
+Euphemia/M
+euphemism/MS
+euphemistic
+euphemistically
+euphemist/M
+euphonious/Y
+euphonium/M
+euphony/SM
+euphoria/SM
+euphoric
+euphorically
+Euphrates/M
+Eurasia/M
+Eurasian/S
+eureka/S
+Euripides/M
+Eur/M
+Eurodollar/SM
+Europa/M
+Europeanization/SM
+Europeanized
+European/MS
+Europe/M
+europium/MS
+Eurydice/M
+Eustace/M
+Eustachian/M
+Eustacia/M
+eutectic
+Euterpe/M
+euthanasia/SM
+euthenics/M
+evacuate/DSXNGV
+evacuation/M
+evacuee/MS
+evader/M
+evade/SRDBGZ
+Evaleen/M
+evaluable
+evaluate/ADSGNX
+evaluated/U
+evaluational
+evaluation/MA
+evaluative
+evaluator/MS
+Eva/M
+evanescence/MS
+evanescent
+Evangelia/M
+evangelic
+evangelicalism/SM
+Evangelical/S
+evangelical/YS
+Evangelina/M
+Evangeline/M
+Evangelin/M
+evangelism/SM
+evangelistic
+evangelist/MS
+Evangelist/MS
+evangelize/GDS
+Evania/M
+Evan/MS
+Evanne/M
+Evanston/M
+Evansville/M
+evaporate/VNGSDX
+evaporation/M
+evaporative/Y
+evaporator/MS
+evasion/SM
+evasiveness/SM
+evasive/PY
+Eveleen/M
+Evelina/M
+Eveline/M
+Evelin/M
+Evelyn/M
+Eve/M
+evened
+evener/M
+evenhanded/YP
+evening/SM
+Evenki/M
+Even/M
+evenness/MSU
+even/PUYRT
+evens
+evensong/MS
+eventfulness/SM
+eventful/YU
+eventide/SM
+event/SGM
+eventuality/MS
+eventual/Y
+eventuate/GSD
+Everard/M
+Eveready/M
+Evered/M
+Everest/M
+Everette/M
+Everett/M
+everglade/MS
+Everglades
+evergreen/S
+Everhart/M
+everlastingness/M
+everlasting/PYS
+everliving
+evermore
+EverReady/M
+eve/RSM
+ever/T
+every
+everybody/M
+everydayness/M
+everyday/P
+everyman
+everyone/MS
+everyplace
+everything
+everywhere
+eve's/A
+eves/A
+Evey/M
+evict/DGS
+eviction/SM
+evidence/MGSD
+evidential/Y
+evident/YS
+Evie/M
+evildoer/SM
+evildoing/MS
+evilness/MS
+evil/YRPTS
+evince/SDG
+Evin/M
+eviscerate/GNXDS
+evisceration/M
+Evita/M
+Ev/MN
+evocable
+evocate/NVX
+evocation/M
+evocativeness/M
+evocative/YP
+evoke/SDG
+evolute/NMXS
+evolutionarily
+evolutionary
+evolutionist/MS
+evolution/M
+evolve/SDG
+Evonne/M
+Evvie/M
+Evvy/M
+Evy/M
+Evyn/M
+Ewan/M
+Eward/M
+Ewart/M
+Ewell/M
+ewe/MZRS
+Ewen/M
+ewer/M
+Ewing/M
+exacerbate/NGXDS
+exacerbation/M
+exacter/M
+exactingness/M
+exacting/YP
+exaction/SM
+exactitude/ISM
+exactly/I
+exactness/MSI
+exact/TGSPRDY
+exaggerate/DSXNGV
+exaggerated/YP
+exaggeration/M
+exaggerative/Y
+exaggerator/MS
+exaltation/SM
+exalted/Y
+exalter/M
+exalt/ZRDGS
+examen/M
+examination/AS
+examination's
+examine/BGZDRS
+examined/AU
+examinees
+examiner/M
+examines/A
+examining/A
+exam/MNS
+example/DSGM
+exampled/U
+exasperate/DSXGN
+exasperated/Y
+exasperating/Y
+exasperation/M
+Excalibur/M
+excavate/NGDSX
+excavation/M
+excavator/SM
+Excedrin/M
+exceeder/M
+exceeding/Y
+exceed/SGDR
+excelled
+excellence/SM
+excellency/MS
+Excellency/MS
+excellent/Y
+excelling
+excel/S
+excelsior/S
+except/DSGV
+exceptionable/U
+exceptionalness/M
+exceptional/YU
+exception/BMS
+excerpter/M
+excerpt/GMDRS
+excess/GVDSM
+excessiveness/M
+excessive/PY
+exchangeable
+exchange/GDRSZ
+exchanger/M
+exchequer/SM
+Exchequer/SM
+excise/XMSDNGB
+excision/M
+excitability/MS
+excitableness/M
+excitable/P
+excitably
+excitation/SM
+excitatory
+excited/Y
+excitement/MS
+exciter/M
+excite/RSDLBZG
+excitingly
+exciting/U
+exciton/M
+exclaimer/M
+exclaim/SZDRG
+exclamation/MS
+exclamatory
+exclude/DRSG
+excluder/M
+exclusionary
+exclusioner/M
+exclusion/SZMR
+exclusiveness/SM
+exclusive/SPY
+exclusivity/MS
+excommunicate/XVNGSD
+excommunication/M
+excoriate/GNXSD
+excoriation/M
+excremental
+excrement/SM
+excrescence/MS
+excrescent
+excreta
+excrete/NGDRSX
+excreter/M
+excretion/M
+excretory/S
+excruciate/NGDS
+excruciating/Y
+excruciation/M
+exculpate/XSDGN
+exculpation/M
+exculpatory
+excursionist/SM
+excursion/MS
+excursiveness/SM
+excursive/PY
+excursus/MS
+excusable/IP
+excusableness/IM
+excusably/I
+excuse/BGRSD
+excused/U
+excuser/M
+exec/MS
+execrableness/M
+execrable/P
+execrably
+execrate/DSXNGV
+execration/M
+executable/MS
+execute/NGVZBXDRS
+executer/M
+executional
+executioner/M
+execution/ZMR
+executive/SM
+executor/SM
+executrices
+executrix/M
+exegeses
+exegesis/M
+exegete/M
+exegetical
+exegetic/S
+exemplariness/M
+exemplar/MS
+exemplary/P
+exemplification/M
+exemplifier/M
+exemplify/ZXNSRDG
+exemption/MS
+exempt/SDG
+exerciser/M
+exercise/ZDRSGB
+exertion/MS
+exert/SGD
+Exeter/M
+exeunt
+exhalation/SM
+exhale/GSD
+exhausted/Y
+exhauster/M
+exhaustible/I
+exhausting/Y
+exhaustion/SM
+exhaustiveness/MS
+exhaustive/YP
+exhaust/VGRDS
+exhibitioner/M
+exhibitionism/MS
+exhibitionist/MS
+exhibition/ZMRS
+exhibitor/SM
+exhibit/VGSD
+exhilarate/XSDVNG
+exhilarating/Y
+exhilaration/M
+exhortation/SM
+exhort/DRSG
+exhorter/M
+exhumation/SM
+exhume/GRSD
+exhumer/M
+exigence/S
+exigency/SM
+exigent/SY
+exiguity/SM
+exiguous
+exile/SDGM
+existence/MS
+existent/I
+existentialism/MS
+existentialistic
+existentialist/MS
+existential/Y
+existents
+exist/SDG
+exit/MDSG
+exobiology/MS
+exocrine
+Exodus/M
+exodus/SM
+exogamous
+exogamy/M
+exogenous/Y
+exonerate/SDVGNX
+exoneration/M
+exorbitance/MS
+exorbitant/Y
+exorcise/SDG
+exorcism/SM
+exorcist/SM
+exorcizer/M
+exoskeleton/MS
+exosphere/SM
+exothermic
+exothermically
+exotica
+exotically
+exoticism/SM
+exoticness/M
+exotic/PS
+exp
+expandability/M
+expand/DRSGZB
+expanded/U
+expander/M
+expanse/DSXGNVM
+expansible
+expansionary
+expansionism/MS
+expansionist/MS
+expansion/M
+expansiveness/S
+expansive/YP
+expatiate/XSDNG
+expatiation/M
+expatriate/SDNGX
+expatriation/M
+expectancy/MS
+expectant/YS
+expectational
+expectation/MS
+expected/UPY
+expecting/Y
+expectorant/S
+expectorate/NGXDS
+expectoration/M
+expect/SBGD
+expedience/IS
+expediency/IMS
+expedients
+expedient/YI
+expediter/M
+expedite/ZDRSNGX
+expeditionary
+expedition/M
+expeditiousness/MS
+expeditious/YP
+expeditor's
+expellable
+expelled
+expelling
+expel/S
+expendable/S
+expended/U
+expender/M
+expenditure/SM
+expend/SDRGB
+expense/DSGVM
+expensive/IYP
+expensiveness/SMI
+experienced/U
+experience/ISDM
+experiencing
+experiential/Y
+experimentalism/M
+experimentalist/SM
+experimental/Y
+experimentation/SM
+experimenter/M
+experiment/GSMDRZ
+experted
+experting
+expertise/SM
+expertize/GD
+expertnesses
+expertness/IM
+expert/PISY
+expert's
+expiable/I
+expiate/XGNDS
+expiation/M
+expiatory
+expiration/MS
+expired/U
+expire/SDG
+expiry/MS
+explainable/UI
+explain/ADSG
+explained/U
+explainer/SM
+explanation/MS
+explanatory
+expletive/SM
+explicable/I
+explicate/VGNSDX
+explication/M
+explicative/Y
+explicitness/SM
+explicit/PSY
+explode/DSRGZ
+exploded/U
+exploder/M
+exploitation/MS
+exploitative
+exploited/U
+exploiter/M
+exploit/ZGVSMDRB
+exploration/MS
+exploratory
+explore/DSRBGZ
+explored/U
+explorer/M
+explosion/MS
+explosiveness/SM
+explosive/YPS
+expo/MS
+exponential/SY
+exponentiate/XSDNG
+exponentiation/M
+exponent/MS
+exportability
+exportable
+export/AGSD
+exportation/SM
+exporter/MS
+export's
+expose
+exposed/U
+exposer/M
+exposit/D
+exposition/SM
+expositor/MS
+expository
+expos/RSDZG
+expostulate/DSXNG
+expostulation/M
+exposure/SM
+expounder/M
+expound/ZGSDR
+expressed/U
+expresser/M
+express/GVDRSY
+expressibility/I
+expressible/I
+expressibly/I
+expressionism/SM
+expressionistic
+expressionist/S
+expressionless/YP
+expression/MS
+expressive/IYP
+expressiveness/MS
+expressiveness's/I
+expressway/SM
+expropriate/XDSGN
+expropriation/M
+expropriator/SM
+expulsion/MS
+expunge/GDSR
+expunger/M
+expurgated/U
+expurgate/SDGNX
+expurgation/M
+exquisiteness/SM
+exquisite/YPS
+ex/S
+ext
+extant
+extemporaneousness/MS
+extemporaneous/YP
+extempore/S
+extemporization/SM
+extemporizer/M
+extemporize/ZGSRD
+extendability/M
+extendedly
+extendedness/M
+extended/U
+extender/M
+extendibility/M
+extendibles
+extend/SGZDR
+extensibility/M
+extensible/I
+extensional/Y
+extension/SM
+extensiveness/SM
+extensive/PY
+extensor/MS
+extent/SM
+extenuate/XSDGN
+extenuation/M
+exterior/MYS
+exterminate/XNGDS
+extermination/M
+exterminator/SM
+externalities
+externalization/SM
+externalize/GDS
+external/YS
+extern/M
+extinct/DGVS
+extinction/MS
+extinguishable/I
+extinguish/BZGDRS
+extinguisher/M
+extirpate/XSDVNG
+extirpation/M
+extolled
+extoller/M
+extolling
+extol/S
+extort/DRSGV
+extorter/M
+extortionate/Y
+extortioner/M
+extortionist/SM
+extortion/ZSRM
+extracellular/Y
+extract/GVSBD
+extraction/SM
+extractive/Y
+extractor/SM
+extracurricular/S
+extradite/XNGSDB
+extradition/M
+extragalactic
+extralegal/Y
+extramarital
+extramural
+extraneousness/M
+extraneous/YP
+extraordinarily
+extraordinariness/M
+extraordinary/PS
+extrapolate/XVGNSD
+extrapolation/M
+extra/S
+extrasensory
+extraterrestrial/S
+extraterritorial
+extraterritoriality/MS
+extravagance/MS
+extravagant/Y
+extravaganza/SM
+extravehicular
+extravert's
+extrema
+extremal
+extreme/DSRYTP
+extremeness/MS
+extremism/SM
+extremist/MS
+extremity/SM
+extricable/I
+extricate/XSDNG
+extrication/M
+extrinsic
+extrinsically
+extroversion/SM
+extrovert/GMDS
+extrude/GDSR
+extruder/M
+extrusion/MS
+extrusive
+exuberance/MS
+exuberant/Y
+exudate/XNM
+exudation/M
+exude/GSD
+exultant/Y
+exultation/SM
+exult/DGS
+exulting/Y
+exurban
+exurbanite/SM
+exurbia/MS
+exurb/MS
+Exxon/M
+Eyck/M
+Eyde/M
+Eydie/M
+eyeball/GSMD
+eyebrow/MS
+eyed/P
+eyedropper/MS
+eyeful/MS
+eye/GDRSMZ
+eyeglass/MS
+eyelash/MS
+eyeless
+eyelet/GSMD
+eyelid/SM
+eyeliner/MS
+eyeopener/MS
+eyeopening
+eyepiece/SM
+eyer/M
+eyeshadow
+eyesight/MS
+eyesore/SM
+eyestrain/MS
+eyeteeth
+eyetooth/M
+eyewash/MS
+eyewitness/SM
+Eyre/M
+eyrie's
+Eysenck/M
+Ezechiel/M
+Ezekiel/M
+Ezequiel/M
+Eziechiele/M
+Ezmeralda/M
+Ezra/M
+Ezri/M
+F
+FAA
+Fabe/MR
+Fabergé/M
+Faber/M
+Fabiano/M
+Fabian/S
+Fabien/M
+Fabio/M
+fable/GMSRD
+fabler/M
+fabricate/SDXNG
+fabrication/M
+fabricator/MS
+fabric/MS
+fabulists
+fabulousness/M
+fabulous/YP
+facade/GMSD
+face/AGCSD
+facecloth
+facecloths
+faceless/P
+faceplate/M
+facer/CM
+face's
+facetiousness/MS
+facetious/YP
+facet/SGMD
+facial/YS
+facileness/M
+facile/YP
+facilitate/VNGXSD
+facilitation/M
+facilitator/SM
+facilitatory
+facility/MS
+facing/MS
+facsimileing
+facsimile/MSD
+factional
+factionalism/SM
+faction/SM
+factiousness/M
+factious/PY
+factitious
+fact/MS
+facto
+factoid/S
+factorial/MS
+factoring/A
+factoring's
+factorisable
+factorization/SM
+factorize/GSD
+factor/SDMJG
+factory/MS
+factotum/MS
+factuality/M
+factualness/M
+factual/PY
+faculty/MS
+faddish
+faddist/SM
+fadedly
+faded/U
+fadeout
+fader/M
+fade/S
+fading's
+fading/U
+fad/ZGSMDR
+Fae/M
+faerie/MS
+Faeroe/M
+faery's
+Fafnir/M
+fagged
+fagging
+faggoting's
+Fagin/M
+fag/MS
+fagoting/M
+fagot/MDSJG
+Fahd/M
+Fahrenheit/S
+faïence/S
+failing's
+failing/UY
+fail/JSGD
+faille/MS
+failsafe
+failure/SM
+Faina/M
+fain/GTSRD
+fainter/M
+fainthearted
+faintness/MS
+faint/YRDSGPT
+Fairbanks
+Fairchild/M
+faired
+Fairfax/M
+Fairfield/M
+fairgoer/S
+fairground/MS
+fairing/MS
+fairish
+Fairleigh/M
+fairless
+Fairlie/M
+Fair/M
+Fairmont/M
+fairness's
+fairness/US
+Fairport/M
+fairs
+fair/TURYP
+Fairview/M
+fairway/MS
+fairyland/MS
+fairy/MS
+fairytale
+Faisalabad
+Faisal/M
+faithed
+faithfulness/MSU
+faithfuls
+faithful/UYP
+faithing
+faithlessness/SM
+faithless/YP
+Faith/M
+faiths
+faith's
+faith/U
+fajitas
+faker/M
+fake/ZGDRS
+fakir/SM
+falafel
+falconer/M
+falconry/MS
+falcon/ZSRM
+Falito/M
+Falkland/MS
+Falk/M
+Falkner/M
+fallaciousness/M
+fallacious/PY
+fallacy/MS
+faller/M
+fallibility/MSI
+fallible/I
+fallibleness/MS
+fallibly/I
+falloff/S
+Fallon/M
+fallopian
+Fallopian/M
+fallout/MS
+fallowness/M
+fallow/PSGD
+fall/SGZMRN
+falsehood/SM
+falseness/SM
+false/PTYR
+falsetto/SM
+falsie/MS
+falsifiability/M
+falsifiable/U
+falsification/M
+falsifier/M
+falsify/ZRSDNXG
+falsity/MS
+Falstaff/M
+falterer/M
+faltering/UY
+falter/RDSGJ
+Falwell/M
+fa/M
+famed/C
+fame/DSMG
+fames/C
+familial
+familiarity/MUS
+familiarization/MS
+familiarized/U
+familiarizer/M
+familiarize/ZGRSD
+familiarizing/Y
+familiarly/U
+familiarness/M
+familiar/YPS
+family/MS
+famine/SM
+faming/C
+famish/GSD
+famously/I
+famousness/M
+famous/PY
+fanaticalness/M
+fanatical/YP
+fanaticism/MS
+fanatic/SM
+Fanchette/M
+Fanchon/M
+fancied
+Fancie/M
+fancier/SM
+fanciest
+fancifulness/MS
+fanciful/YP
+fancily
+fanciness/SM
+fancying
+fancy/IS
+Fancy/M
+fancywork/SM
+fandango/SM
+Fanechka/M
+fanfare/SM
+fanfold/M
+fang/DMS
+fangled
+Fania/M
+fanlight/SM
+Fan/M
+fanned
+Fannie/M
+Fanni/M
+fanning
+fanny/SM
+Fanny/SM
+fanout
+fan/SM
+fantail/SM
+fantasia/SM
+fantasist/M
+fantasize/SRDG
+fantastical/Y
+fantastic/S
+fantasy/GMSD
+Fanya/M
+fanzine/S
+FAQ/SM
+Faraday/M
+farad/SM
+Farah/M
+Fara/M
+Farand/M
+faraway
+Farber/M
+farce/SDGM
+farcical/Y
+fare/MS
+farer/M
+farewell/DGMS
+farfetchedness/M
+far/GDR
+Fargo/M
+Farica/M
+farinaceous
+farina/MS
+Farkas/M
+Farlay/M
+Farlee/M
+Farleigh/M
+Farley/M
+Farlie/M
+Farly/M
+farmer/M
+Farmer/M
+farmhand/S
+farmhouse/SM
+farming/M
+Farmington/M
+farmland/SM
+farm/MRDGZSJ
+farmstead/SM
+farmworker/S
+Far/MY
+farmyard/MS
+faro/MS
+farragoes
+farrago/M
+Farragut/M
+Farrah/M
+Farrakhan/M
+Farra/M
+Farrand/M
+Farrell/M
+Farrel/M
+farrier/SM
+Farris/M
+Farr/M
+farrow/DMGS
+farseeing
+farsightedness/SM
+farsighted/YP
+farther
+farthermost
+farthest
+farthing/SM
+fart/MDGS
+fas
+fascia/SM
+fascicle/DSM
+fasciculate/DNX
+fasciculation/M
+fascinate/SDNGX
+fascinating/Y
+fascination/M
+fascism/MS
+Fascism's
+fascistic
+Fascist's
+fascist/SM
+fashionableness/M
+fashionable/PS
+fashionably/U
+fashion/ADSG
+fashioner/SM
+fashion's
+Fassbinder/M
+fastback/MS
+fastball/S
+fasten/AGUDS
+fastener/MS
+fastening/SM
+fast/GTXSPRND
+fastidiousness/MS
+fastidious/PY
+fastness/MS
+fatalism/MS
+fatalistic
+fatalistically
+fatalist/MS
+fatality/MS
+fatal/SY
+fatback/SM
+fatefulness/MS
+fateful/YP
+fate/MS
+Fates
+fatheaded/P
+fathead/SMD
+father/DYMGS
+fathered/U
+fatherhood/MS
+fatherland/SM
+fatherless
+fatherliness/M
+fatherly/P
+Father/SM
+fathomable/U
+fathomless
+fathom/MDSBG
+fatigued/U
+fatigue/MGSD
+fatiguing/Y
+Fatima/M
+fatness/SM
+fat/PSGMDY
+fatso/M
+fatted
+fattener/M
+fatten/JZGSRD
+fatter
+fattest/M
+fattiness/SM
+fatting
+fatty/RSPT
+fatuity/MS
+fatuousness/SM
+fatuous/YP
+fatwa/SM
+faucet/SM
+Faulknerian
+Faulkner/M
+fault/CGSMD
+faultfinder/MS
+faultfinding/MS
+faultily
+faultiness/MS
+faultlessness/SM
+faultless/PY
+faulty/RTP
+fauna/MS
+Faunie/M
+Faun/M
+faun/MS
+Fauntleroy/M
+Faustian
+Faustina/M
+Faustine/M
+Faustino/M
+Faust/M
+Faustus/M
+fauvism/S
+favorableness/MU
+favorable/UMPS
+favorably/U
+favoredness/M
+favored's/U
+favored/YPSM
+favorer/EM
+favor/ESMRDGZ
+favoring/MYS
+favorings/U
+favorite/SMU
+favoritism/MS
+favors/A
+Fawkes/M
+Fawne/M
+fawner/M
+fawn/GZRDMS
+Fawnia/M
+fawning/Y
+Fawn/M
+fax/GMDS
+Fax/M
+Faydra/M
+Faye/M
+Fayette/M
+Fayetteville/M
+Fayina/M
+Fay/M
+fay/MDRGS
+Fayre/M
+Faythe/M
+Fayth/M
+faze/DSG
+FBI
+FCC
+FD
+FDA
+FDIC
+FDR/M
+fealty/MS
+fearfuller
+fearfullest
+fearfulness/MS
+fearful/YP
+fearlessness/MS
+fearless/PY
+fear/RDMSG
+fearsomeness/M
+fearsome/PY
+feasibility/SM
+feasibleness/M
+feasible/UI
+feasibly/U
+feaster/M
+feast/GSMRD
+feater/C
+featherbed
+featherbedding/SM
+featherbrain/MD
+feathered/U
+feathering/M
+featherless
+featherlight
+Featherman/M
+feathertop
+featherweight/SM
+feathery/TR
+feather/ZMDRGS
+feat/MYRGTS
+feats/C
+featureless
+feature/MGSD
+Feb/M
+febrile
+February/MS
+fecal
+feces
+fecklessness/M
+feckless/PY
+fecundability
+fecundate/XSDGN
+fecundation/M
+fecund/I
+fecundity/SM
+federalism/SM
+Federalist
+federalist/MS
+federalization/MS
+federalize/GSD
+Federal/S
+federal/YS
+federated/U
+federate/FSDXVNG
+federation/FM
+federative/Y
+Federica/M
+Federico/M
+FedEx/M
+Fedora/M
+fedora/SM
+feds
+Fed/SM
+fed/U
+feebleness/SM
+feeble/TPR
+feebly
+feedback/SM
+feedbag/MS
+feeder/M
+feed/GRZJS
+feeding/M
+feedlot/SM
+feedstock
+feedstuffs
+feeing
+feeler/M
+feel/GZJRS
+feelingly/U
+feeling/MYP
+feelingness/M
+Fee/M
+fee/MDS
+feet/M
+feigned/U
+feigner/M
+feign/RDGS
+feint/MDSG
+feisty/RT
+Felder/M
+Feldman/M
+feldspar/MS
+Felecia/M
+Felicdad/M
+Felice/M
+Felicia/M
+Felicio/M
+felicitate/XGNSD
+felicitation/M
+felicitous/IY
+felicitousness/M
+felicity/IMS
+Felicity/M
+Felicle/M
+Felic/M
+Felike/M
+Feliks/M
+feline/SY
+Felipa/M
+Felipe/M
+Felisha/M
+Felita/M
+Felix/M
+Feliza/M
+Felizio/M
+fella/S
+fellatio/SM
+felled/A
+feller/M
+felling/A
+Fellini/M
+fellness/M
+fellowman
+fellowmen
+fellow/SGDYM
+fellowshipped
+fellowshipping
+fellowship/SM
+fell/PSGZTRD
+feloniousness/M
+felonious/PY
+felon/MS
+felony/MS
+felt/GSD
+felting/M
+Fe/M
+female/MPS
+femaleness/SM
+feminineness/M
+feminine/PYS
+femininity/MS
+feminism/MS
+feminist/MS
+femme/MS
+femoral
+fem/S
+femur/MS
+fenced/U
+fencepost/M
+fencer/M
+fence/SRDJGMZ
+fencing/M
+fender/CM
+fend/RDSCZG
+Fenelia/M
+fenestration/CSM
+Fenian/M
+fenland/M
+fen/MS
+fennel/SM
+Fenwick/M
+Feodora/M
+Feodor/M
+feral
+Ferber/M
+Ferdie/M
+Ferdinanda/M
+Ferdinande/M
+Ferdinand/M
+Ferdinando/M
+Ferd/M
+Ferdy/M
+fer/FLC
+Fergus/M
+Ferguson/M
+Ferlinghetti/M
+Fermat/M
+fermentation/MS
+fermented
+fermenter
+ferment/FSCM
+fermenting
+Fermi/M
+fermion/MS
+fermium/MS
+Fernanda/M
+Fernande/M
+Fernandez/M
+Fernandina/M
+Fernando/M
+Ferne/M
+fernery/M
+Fern/M
+fern/MS
+ferny/TR
+ferociousness/MS
+ferocious/YP
+ferocity/MS
+Ferrari/M
+Ferraro/M
+Ferreira/M
+Ferrell/M
+Ferrel/M
+Ferrer/M
+ferreter/M
+ferret/SMRDG
+ferric
+ferris
+Ferris
+ferrite/M
+ferro
+ferroelectric
+ferromagnetic
+ferromagnet/M
+ferrous
+ferrule/MGSD
+ferryboat/MS
+ferryman/M
+ferrymen
+ferry/SDMG
+fertileness/M
+fertile/YP
+fertility/IMS
+fertilization/ASM
+fertilized/U
+fertilizer/M
+fertilizes/A
+fertilize/SRDZG
+ferule/SDGM
+fervency/MS
+fervent/Y
+fervidness/M
+fervid/YP
+fervor/MS
+fess/KGFSD
+Fess/M
+fess's
+festal/S
+fester/GD
+festival/SM
+festiveness/SM
+festive/PY
+festivity/SM
+festoon/SMDG
+fest/RVZ
+fetal
+feta/MS
+fetcher/M
+fetching/Y
+fetch/RSDGZ
+feted
+fête/MS
+fetich's
+fetidness/SM
+fetid/YP
+feting
+fetishism/SM
+fetishistic
+fetishist/SM
+fetish/MS
+fetlock/MS
+fetter's
+fetter/UGSD
+fettle/GSD
+fettling/M
+fettuccine/S
+fetus/SM
+feudalism/MS
+feudalistic
+feudal/Y
+feudatory/M
+feud/MDSG
+feverishness/SM
+feverish/PY
+fever/SDMG
+fewness/MS
+few/PTRS
+Fey/M
+Feynman/M
+fey/RT
+fez/M
+Fez/M
+fezzes
+ff
+FHA
+fiancée/S
+fiancé/MS
+Fianna/M
+Fiann/M
+fiascoes
+fiasco/M
+Fiat/M
+fiat/MS
+fibbed
+fibber/MS
+fibbing
+fiberboard/MS
+fiber/DM
+fiberfill/S
+Fiberglas/M
+fiberglass/DSMG
+Fibonacci/M
+fibrillate/XGNDS
+fibrillation/M
+fibril/MS
+fibrin/MS
+fibroblast/MS
+fibroid/S
+fibroses
+fibrosis/M
+fibrousness/M
+fibrous/YP
+fib/SZMR
+fibulae
+fibula/M
+fibular
+FICA
+fices
+fiche/SM
+Fichte/M
+fichu/SM
+fickleness/MS
+fickle/RTP
+ficos
+fictionalization/MS
+fictionalize/DSG
+fictional/Y
+fiction/SM
+fictitiousness/M
+fictitious/PY
+fictive/Y
+ficus
+fiddle/GMZJRSD
+fiddler/M
+fiddlestick/SM
+fiddly
+fide/F
+Fidela/M
+Fidelia/M
+Fidelio/M
+fidelity/IMS
+Fidelity/M
+Fidel/M
+fidget/DSG
+fidgety
+Fidole/M
+Fido/M
+fiducial/Y
+fiduciary/MS
+fiefdom/S
+fief/MS
+fielded
+fielder/IM
+fielding
+Fielding/M
+Field/MGS
+fieldstone/M
+fieldworker/M
+fieldwork/ZMRS
+field/ZISMR
+fiendishness/M
+fiendish/YP
+fiend/MS
+fierceness/SM
+fierce/RPTY
+fierily
+fieriness/MS
+fiery/PTR
+fie/S
+fies/C
+fiesta/MS
+fife/DRSMZG
+fifer/M
+Fifi/M
+Fifine/M
+FIFO
+fifteen/HRMS
+fifteenths
+fifths
+fifth/Y
+fiftieths
+fifty/HSM
+Figaro/M
+figged
+figging
+fightback
+fighter/MIS
+fighting/IS
+fight/ZSJRG
+figment/MS
+fig/MLS
+Figueroa/M
+figural
+figuration/FSM
+figurativeness/M
+figurative/YP
+figure/GFESD
+figurehead/SM
+figurer/SM
+figure's
+figurine/SM
+figuring/S
+Fijian/SM
+Fiji/M
+filamentary
+filament/MS
+filamentous
+Filberte/M
+Filbert/M
+filbert/MS
+Filberto/M
+filch/SDG
+filed/AC
+file/KDRSGMZ
+filename/SM
+filer/KMCS
+files/AC
+filet's
+filial/UY
+Filia/M
+filibusterer/M
+filibuster/MDRSZG
+Filide/M
+filigreeing
+filigree/MSD
+filing/AC
+filings
+Filipino/SM
+Filip/M
+Filippa/M
+Filippo/M
+fill/BAJGSD
+filled/U
+filler/MS
+filleting/M
+fillet/MDSG
+filling/M
+fillip/MDGS
+Fillmore/M
+filly/SM
+filmdom/M
+Filmer/M
+filminess/SM
+filming/M
+filmmaker/S
+Filmore/M
+film/SGMD
+filmstrip/SM
+filmy/RTP
+Filofax/S
+filtered/U
+filterer/M
+filter/RDMSZGB
+filthily
+filthiness/SM
+filth/M
+filths
+filthy/TRSDGP
+filtrated/I
+filtrate/SDXMNG
+filtrates/I
+filtrating/I
+filtration/IMS
+finagler/M
+finagle/RSDZG
+finale/MS
+finalist/MS
+finality/MS
+finalization/SM
+finalize/GSD
+final/SY
+Fina/M
+financed/A
+finance/MGSDJ
+finances/A
+financial/Y
+financier/DMGS
+financing/A
+Finch/M
+finch/MS
+findable/U
+find/BRJSGZ
+finder/M
+finding/M
+Findlay/M
+Findley/M
+fine/FGSCRDA
+finely
+fineness/MS
+finery/MAS
+fine's
+finespun
+finesse/SDMG
+fingerboard/SM
+fingerer/M
+fingering/M
+fingerless
+fingerling/M
+fingernail/MS
+fingerprint/SGDM
+finger/SGRDMJ
+fingertip/MS
+finial/SM
+finical
+finickiness/S
+finicky/RPT
+fining/M
+finished/UA
+finisher/M
+finishes/A
+finish/JZGRSD
+finis/SM
+finite/ISPY
+finitely/C
+finiteness/MIC
+fink/GDMS
+Finland/M
+Finlay/M
+Finley/M
+Fin/M
+Finnbogadottir/M
+finned
+Finnegan/M
+finner
+finning
+Finnish
+Finn/MS
+finny/RT
+fin/TGMDRS
+Fiona/M
+Fionna/M
+Fionnula/M
+fiord's
+Fiorello/M
+Fiorenze/M
+Fiori/M
+f/IRAC
+firearm/SM
+fireball/SM
+fireboat/M
+firebomb/MDSG
+firebox/MS
+firebrand/MS
+firebreak/SM
+firebrick/SM
+firebug/SM
+firecracker/SM
+firedamp/SM
+fired/U
+firefight/JRGZS
+firefly/MS
+fireguard/M
+firehouse/MS
+firelight/GZSM
+fireman/M
+firemen
+fire/MS
+fireplace/MS
+fireplug/MS
+firepower/SM
+fireproof/SGD
+firer/M
+firesafe
+fireside/SM
+Firestone/M
+firestorm/SM
+firetrap/SM
+firetruck/S
+firewall/S
+firewater/SM
+firewood/MS
+firework/MS
+firing/M
+firkin/M
+firmament/MS
+firmer
+firmest
+firm/ISFDG
+firmly/I
+firmness/MS
+firm's
+firmware/MS
+firring
+firstborn/S
+firsthand
+first/SY
+firth/M
+firths
+fir/ZGJMDRHS
+fiscal/YS
+Fischbein/M
+Fischer/M
+fishbowl/MS
+fishcake/S
+fisher/M
+Fisher/M
+fisherman/M
+fishermen/M
+fishery/MS
+fishhook/MS
+fishily
+fishiness/MS
+fishing/M
+fish/JGZMSRD
+Fishkill/M
+fishmeal
+fishmonger/MS
+fishnet/SM
+fishpond/SM
+fishtail/DMGS
+fishtanks
+fishwife/M
+fishwives
+fishy/TPR
+Fiske/M
+Fisk/M
+fissile
+fissionable/S
+fission/BSDMG
+fissure/MGSD
+fistfight/SM
+fistful/MS
+fisticuff/SM
+fist/MDGS
+fistula/SM
+fistulous
+Fitchburg/M
+Fitch/M
+fitfulness/SM
+fitful/PY
+fitments
+fitness/USM
+fits/AK
+fit's/K
+fitted/UA
+fitter/SM
+fittest
+fitting/AU
+fittingly
+fittingness/M
+fittings
+fit/UYPS
+Fitzgerald/M
+Fitz/M
+Fitzpatrick/M
+Fitzroy/M
+fivefold
+five/MRS
+fiver/M
+fixable
+fixate/VNGXSD
+fixatifs
+fixation/M
+fixative/S
+fixedness/M
+fixed/YP
+fixer/SM
+fixes/I
+fixing/SM
+fixity/MS
+fixture/SM
+fix/USDG
+Fizeau/M
+fizzer/M
+fizzle/GSD
+fizz/SRDG
+fizzy/RT
+fjord/SM
+FL
+flabbergast/GSD
+flabbergasting/Y
+flabbily
+flabbiness/SM
+flabby/TPR
+flab/MS
+flaccidity/MS
+flaccid/Y
+flack/SGDM
+flagella/M
+flagellate/DSNGX
+flagellation/M
+flagellum/M
+flagged
+flaggingly/U
+flagging/SMY
+flagman/M
+flagmen
+flag/MS
+flagon/SM
+flagpole/SM
+flagrance/MS
+flagrancy/SM
+flagrant/Y
+flagship/MS
+flagstaff/MS
+flagstone/SM
+flail/SGMD
+flair/SM
+flaker/M
+flake/SM
+flakiness/MS
+flak/RDMGS
+flaky/PRT
+Fla/M
+flambé/D
+flambeing
+flambes
+flamboyance/MS
+flamboyancy/MS
+flamboyant/YS
+flamenco/SM
+flamen/M
+flameproof/DGS
+flamer/IM
+flame's
+flame/SIGDR
+flamethrower/SM
+flamingo/SM
+flaming/Y
+flammability/ISM
+flammable/SI
+flam/MRNDJGZ
+Flanagan/M
+Flanders/M
+flange/GMSD
+flanker/M
+flank/SGZRDM
+flan/MS
+flannel/DMGS
+flannelet/MS
+flannelette's
+flapjack/SM
+flap/MS
+flapped
+flapper/SM
+flapping
+flaps/M
+flare/SDG
+flareup/S
+flaring/Y
+flashback/SM
+flashbulb/SM
+flashcard/S
+flashcube/MS
+flasher/M
+flashgun/S
+flashily
+flashiness/SM
+flashing/M
+flash/JMRSDGZ
+flashlight/MS
+flashy/TPR
+flask/SM
+flatbed/S
+flatboat/MS
+flatcar/MS
+flatfeet
+flatfish/SM
+flatfoot/SGDM
+flathead/M
+flatiron/SM
+flatland/RS
+flatmate/M
+flat/MYPS
+flatness/MS
+flatted
+flattener/M
+flatten/SDRG
+flatter/DRSZG
+flatterer/M
+flattering/YU
+flattery/SM
+flattest/M
+flatting
+flattish
+Flatt/M
+flattop/MS
+flatulence/SM
+flatulent/Y
+flatus/SM
+flatware/MS
+flatworm/SM
+Flaubert/M
+flaunting/Y
+flaunt/SDG
+flautist/SM
+flavored/U
+flavorer/M
+flavorful
+flavoring/M
+flavorless
+flavor/SJDRMZG
+flavorsome
+flaw/GDMS
+flawlessness/MS
+flawless/PY
+flax/MSN
+flaxseed/M
+flayer/M
+flay/RDGZS
+fleabag/MS
+fleabites
+flea/SM
+fleawort/M
+fleck/GRDMS
+Fledermaus/M
+fledged/U
+fledge/GSD
+fledgling/SM
+fleecer/M
+fleece/RSDGMZ
+fleeciness/SM
+fleecy/RTP
+fleeing
+flee/RS
+fleetingly/M
+fleetingness/SM
+fleeting/YP
+fleet/MYRDGTPS
+fleetness/MS
+Fleischer/M
+Fleischman/M
+Fleisher/M
+Fleming/M
+Flemished/M
+Flemish/GDSM
+Flemishing/M
+Flem/JGM
+Flemming/M
+flesher/M
+fleshiness/M
+flesh/JMYRSDG
+fleshless
+fleshly/TR
+fleshpot/SM
+fleshy/TPR
+fletch/DRSGJ
+fletcher/M
+Fletcher/M
+fletching/M
+Fletch/MR
+Fleurette/M
+Fleur/M
+flew/S
+flews/M
+flexed/I
+flexibility/MSI
+flexible/I
+flexibly/I
+flexitime's
+flex/MSDAG
+flextime/S
+flexural
+flexure/M
+fl/GJD
+flibbertigibbet/MS
+flicker/GD
+flickering/Y
+flickery
+flick/GZSRD
+flier/M
+flight/GMDS
+flightiness/SM
+flightless
+flightpath
+flighty/RTP
+flimflammed
+flimflamming
+flimflam/MS
+flimsily
+flimsiness/MS
+flimsy/PTRS
+flincher/M
+flinch/GDRS
+flinching/U
+flinger/M
+fling/RMG
+Flin/M
+Flinn/M
+flintiness/M
+flintless
+flintlock/MS
+Flint/M
+flint/MDSG
+Flintstones
+flinty/TRP
+flipflop
+flippable
+flippancy/MS
+flippant/Y
+flipped
+flipper/SM
+flippest
+flipping
+flip/S
+flirtation/SM
+flirtatiousness/MS
+flirtatious/PY
+flirt/GRDS
+flit/S
+flitted
+flitting
+floater/M
+float/SRDGJZ
+floaty
+flocculate/GNDS
+flocculation/M
+flock/SJDMG
+floe/MS
+flogged
+flogger/SM
+flogging/SM
+flog/S
+Flo/M
+floodgate/MS
+floodlight/DGMS
+floodlit
+floodplain/S
+flood/SMRDG
+floodwater/SM
+floorboard/MS
+floorer/M
+flooring/M
+floor/SJRDMG
+floorspace
+floorwalker/SM
+floozy/SM
+flophouse/SM
+flop/MS
+flopped
+flopper/M
+floppily
+floppiness/SM
+flopping
+floppy/TMRSP
+floral/SY
+Flora/M
+Florance/M
+flora/SM
+Florella/M
+Florence/M
+Florencia/M
+Florentia/M
+Florentine/S
+Florenza/M
+florescence/MIS
+florescent/I
+Flore/SM
+floret/MS
+Florette/M
+Floria/M
+Florian/M
+Florida/M
+Floridan/S
+Floridian/S
+floridness/SM
+florid/YP
+Florie/M
+Florina/M
+Florinda/M
+Florine/M
+florin/MS
+Flori/SM
+florist/MS
+Flor/M
+Florrie/M
+Florri/M
+Florry/M
+Flory/M
+floss/GSDM
+Flossie/M
+Flossi/M
+Flossy/M
+flossy/RST
+flotation/SM
+flotilla/SM
+flotsam/SM
+flounce/GDS
+flouncing/M
+flouncy/RT
+flounder/SDG
+flourisher/M
+flourish/GSRD
+flourishing/Y
+flour/SGDM
+floury/TR
+flouter/M
+flout/GZSRD
+flowchart/SG
+flowed
+flowerbed/SM
+flower/CSGD
+flowerer/M
+floweriness/SM
+flowerless
+flowerpot/MS
+flower's
+Flowers
+flowery/TRP
+flowing/Y
+flow/ISG
+flown
+flowstone
+Floyd/M
+Flss/M
+flt
+flubbed
+flubbing
+flub/S
+fluctuate/XSDNG
+fluctuation/M
+fluency/MS
+fluently
+fluent/SF
+flue/SM
+fluffiness/SM
+fluff/SGDM
+fluffy/PRT
+fluidity/SM
+fluidized
+fluid/MYSP
+fluidness/M
+fluke/SDGM
+fluky/RT
+flume/SDGM
+flummox/DSG
+flu/MS
+flung
+flunkey's
+flunk/SRDG
+flunky/MS
+fluoresce/GSRD
+fluorescence/MS
+fluorescent/S
+fluoridate/XDSGN
+fluoridation/M
+fluoride/SM
+fluorimetric
+fluorinated
+fluorine/SM
+fluorite/MS
+fluorocarbon/MS
+fluoroscope/MGDS
+fluoroscopic
+flurry/GMDS
+flushness/M
+flush/TRSDPBG
+fluster/DSG
+fluter/M
+flute/SRDGMJ
+fluting/M
+flutist/MS
+flutter/DRSG
+flutterer/M
+fluttery
+fluxed/A
+fluxes/A
+flux/IMS
+fluxing
+flyaway
+flyblown
+flyby/M
+flybys
+flycatcher/MS
+flyer's
+fly/JGBDRSTZ
+flyleaf/M
+flyleaves
+Flynn/M
+flyover/MS
+flypaper/MS
+flysheet/S
+flyspeck/MDGS
+flyswatter/S
+flyway/MS
+flyweight/MS
+flywheel/MS
+FM
+Fm/M
+FNMA/M
+foal/MDSG
+foaminess/MS
+foam/MRDSG
+foamy/RPT
+fobbed
+fobbing
+fob/SM
+focal/F
+focally
+Foch/M
+foci's
+focused/AU
+focuser/M
+focuses/A
+focus/SRDMBG
+fodder/GDMS
+foe/SM
+foetid
+FOFL
+fogbound
+fogged/C
+foggily
+fogginess/MS
+fogging/C
+foggy/RPT
+foghorn/SM
+fogs/C
+fog/SM
+fogyish
+fogy/SM
+foible/MS
+foil/GSD
+foist/GDS
+Fokker/M
+foldaway/S
+folded/AU
+folder/M
+foldout/MS
+fold/RDJSGZ
+folds/UA
+Foley/M
+foliage/MSD
+foliate/CSDXGN
+foliation/CM
+folio/SDMG
+folklike
+folklore/MS
+folkloric
+folklorist/SM
+folk/MS
+folksiness/MS
+folksinger/S
+folksinging/S
+folksong/S
+folksy/TPR
+folktale/S
+folkway/S
+foll
+follicle/SM
+follicular
+follower/M
+follow/JSZBGRD
+followup's
+folly/SM
+Folsom
+fol/Y
+Fomalhaut/M
+fomentation/SM
+fomenter/M
+foment/RDSG
+Fonda/M
+fondant/SM
+fondle/GSRD
+fondler/M
+fondness/MS
+fond/PMYRDGTS
+fondue/MS
+Fons
+Fonsie/M
+Fontainebleau/M
+Fontaine/M
+Fontana/M
+fontanelle's
+fontanel/MS
+font/MS
+Fonzie/M
+Fonz/M
+foodie/S
+food/MS
+foodstuff/MS
+foolery/MS
+foolhardily
+foolhardiness/SM
+foolhardy/PTR
+foolishness/SM
+foolish/PRYT
+fool/MDGS
+foolproof
+foolscap/MS
+footage/SM
+football/SRDMGZ
+footbridge/SM
+Foote/M
+footer/M
+footfall/SM
+foothill/SM
+foothold/MS
+footing/M
+footless
+footlights
+footling
+footlocker/SM
+footloose
+footman/M
+footmarks
+footmen
+footnote/MSDG
+footpad/SM
+footpath/M
+footpaths
+footplate/M
+footprint/MS
+footrace/S
+footrest/MS
+footsie/SM
+foot/SMRDGZJ
+footsore
+footstep/SM
+footstool/SM
+footwear/M
+footwork/SM
+fop/MS
+fopped
+foppery/MS
+fopping
+foppishness/SM
+foppish/YP
+forage/GSRDMZ
+forager/M
+forayer/M
+foray/SGMRD
+forbade
+forbearance/SM
+forbearer/M
+forbear/MRSG
+Forbes/M
+forbidden
+forbiddingness/M
+forbidding/YPS
+forbid/S
+forbore
+forborne
+forced/Y
+forcefield/MS
+forcefulness/MS
+forceful/PY
+forceps/M
+forcer/M
+force/SRDGM
+forcibleness/M
+forcible/P
+forcibly
+fordable/U
+Fordham/M
+Ford/M
+ford/SMDBG
+forearm/GSDM
+forebear/MS
+forebode/GJDS
+forebodingness/M
+foreboding/PYM
+forecaster/M
+forecastle/MS
+forecast/SZGR
+foreclose/GSD
+foreclosure/MS
+forecourt/SM
+foredoom/SDG
+forefather/SM
+forefeet
+forefinger/MS
+forefoot/M
+forefront/SM
+foregoer/M
+foregoing/S
+foregone
+foregos
+foreground/MGDS
+forehand/S
+forehead/MS
+foreigner/M
+foreignness/SM
+foreign/PRYZS
+foreknew
+foreknow/GS
+foreknowledge/MS
+foreknown
+foreleg/MS
+forelimb/MS
+forelock/MDSG
+foreman/M
+Foreman/M
+foremast/SM
+foremen
+foremost
+forename/DSM
+forenoon/SM
+forensically
+forensic/S
+forensics/M
+foreordain/DSG
+forepart/MS
+forepaws
+forepeople
+foreperson/S
+foreplay/MS
+forequarter/SM
+forerunner/MS
+fore/S
+foresail/SM
+foresaw
+foreseeable/U
+foreseeing
+foreseen/U
+foreseer/M
+foresee/ZSRB
+foreshadow/SGD
+foreshore/M
+foreshorten/DSG
+foresightedness/SM
+foresighted/PY
+foresight/SMD
+foreskin/SM
+forestaller/M
+forestall/LGSRD
+forestallment/M
+forestation/MCS
+forestations/A
+forest/CSAGD
+Forester/M
+forester/SM
+forestland/S
+Forest/MR
+forestry/MS
+forest's
+foretaste/MGSD
+foreteller/M
+foretell/RGS
+forethought/MS
+foretold
+forevermore
+forever/PS
+forewarner/M
+forewarn/GSJRD
+forewent
+forewoman/M
+forewomen
+foreword/SM
+forfeiter/M
+forfeiture/MS
+forfeit/ZGDRMS
+forfend/GSD
+forgather/GSD
+forgave
+forged/A
+forge/JVGMZSRD
+forger/M
+forgery/MS
+forges/A
+forgetfulness/SM
+forgetful/PY
+forget/SV
+forgettable/U
+forgettably/U
+forgetting
+forging/M
+forgivable/U
+forgivably/U
+forgiven
+forgiveness/SM
+forgiver/M
+forgive/SRPBZG
+forgivingly
+forgivingness/M
+forgiving/UP
+forgoer/M
+forgoes
+forgone
+forgo/RSGZ
+forgot
+forgotten/U
+for/HT
+forkful/S
+fork/GSRDM
+forklift/DMSG
+forlornness/M
+forlorn/PTRY
+formability/AM
+formaldehyde/SM
+formalin/M
+formalism/SM
+formalistic
+formalist/SM
+formality/SMI
+formal/IY
+formalization/SM
+formalized/U
+formalizer/M
+formalizes/I
+formalize/ZGSRD
+formalness/M
+formals
+formant/MIS
+format/AVS
+formate/MXGNSD
+formation/AFSCIM
+formatively/I
+formativeness/IM
+formative/SYP
+format's
+formatted/UA
+formatter/A
+formatters
+formatter's
+formatting/A
+form/CGSAFDI
+formed/U
+former/FSAI
+formerly
+formfitting
+formic
+Formica/MS
+formidableness/M
+formidable/P
+formidably
+formlessness/MS
+formless/PY
+Formosa/M
+Formosan
+form's
+formulaic
+formula/SM
+formulate/AGNSDX
+formulated/U
+formulation/AM
+formulator/SM
+fornicate/GNXSD
+fornication/M
+fornicator/SM
+Forrester/M
+Forrest/RM
+forsaken
+forsake/SG
+forsook
+forsooth
+Forster/M
+forswear/SG
+forswore
+forsworn
+forsythia/MS
+Fortaleza/M
+forte/MS
+forthcome/JG
+forthcoming/U
+FORTH/M
+forthrightness/SM
+forthright/PYS
+forthwith
+fortieths
+fortification/MS
+fortified/U
+fortifier/SM
+fortify/ADSG
+fortiori
+fortissimo/S
+fortitude/SM
+fortnightly/S
+fortnight/MYS
+FORTRAN
+Fortran/M
+fortress/GMSD
+fort/SM
+fortuitousness/SM
+fortuitous/YP
+fortuity/MS
+fortunateness/M
+fortunate/YUS
+fortune/MGSD
+fortuneteller/SM
+fortunetelling/SM
+forty/SRMH
+forum/MS
+forwarder/M
+forwarding/M
+forwardness/MS
+forward/PTZSGDRY
+forwent
+fossiliferous
+fossilization/MS
+fossilized/U
+fossilize/GSD
+fossil/MS
+Foss/M
+fosterer/M
+Foster/M
+foster/SRDG
+Foucault/M
+fought
+foulard/SM
+foulmouth/D
+foulness/MS
+fouls/M
+foul/SYRDGTP
+foundational
+foundation/SM
+founded/UF
+founder/MDG
+founder's/F
+founding/F
+foundling/MS
+found/RDGZS
+foundry/MS
+founds/KF
+fountainhead/SM
+fountain/SMDG
+fount/MS
+fourfold
+Fourier/M
+fourpence/M
+fourpenny
+fourposter/SM
+fourscore/S
+four/SHM
+foursome/SM
+foursquare
+fourteener/M
+fourteen/SMRH
+fourteenths
+Fourth
+fourths
+Fourths
+fourth/Y
+fovea/M
+fowler/M
+Fowler/M
+fowling/M
+fowl/SGMRD
+foxfire/SM
+foxglove/SM
+Foxhall/M
+foxhole/SM
+foxhound/SM
+foxily
+foxiness/MS
+foxing/M
+fox/MDSG
+Fox/MS
+foxtail/M
+foxtrot/MS
+foxtrotted
+foxtrotting
+foxy/TRP
+foyer/SM
+FPO
+fps
+fr
+fracas/SM
+fractal/SM
+fractional/Y
+fractionate/DNG
+fractionation/M
+fractioned
+fractioning
+fraction/ISMA
+fractiousness/SM
+fractious/PY
+fracture/MGDS
+fragile/Y
+fragility/MS
+fragmentarily
+fragmentariness/M
+fragmentary/P
+fragmentation/MS
+fragment/SDMG
+Fragonard/M
+fragrance/SM
+fragrant/Y
+frailness/MS
+frail/STPYR
+frailty/MS
+framed/U
+framer/M
+frame/SRDJGMZ
+framework/SM
+framing/M
+Francaise/M
+France/MS
+Francene/M
+Francesca/M
+Francesco/M
+franchisee/S
+franchise/ESDG
+franchiser/SM
+franchise's
+Franchot/M
+Francie/M
+Francine/M
+Francis
+Francisca/M
+Franciscan/MS
+Francisco/M
+Franciska/M
+Franciskus/M
+francium/MS
+Francklin/M
+Francklyn/M
+Franck/M
+Francoise/M
+Francois/M
+Franco/M
+francophone/M
+franc/SM
+Francyne/M
+frangibility/SM
+frangible
+Frankel/M
+Frankenstein/MS
+franker/M
+Frankford/M
+Frankfort/M
+Frankfurter/M
+frankfurter/MS
+Frankfurt/RM
+Frankie/M
+frankincense/MS
+Frankish/M
+franklin/M
+Franklin/M
+Franklyn/M
+frankness/MS
+frank/SGTYRDP
+Frank/SM
+Franky/M
+Fran/MS
+Frannie/M
+Franni/M
+Franny/M
+Fransisco/M
+frantically
+franticness/M
+frantic/PY
+Frants/M
+Franzen/M
+Franz/NM
+frappé
+frappeed
+frappeing
+frappes
+Frasco/M
+Fraser/M
+Frasier/M
+Frasquito/M
+fraternal/Y
+fraternity/MSF
+fraternization/SM
+fraternize/GZRSD
+fraternizer/M
+fraternizing/U
+frat/MS
+fratricidal
+fratricide/MS
+fraud/CS
+fraud's
+fraudsters
+fraudulence/S
+fraudulent/YP
+fraught/SGD
+Fraulein/S
+Frau/MN
+fray/CSDG
+Frayda/M
+Frayne/M
+fray's
+Fraze/MR
+Frazer/M
+Frazier/M
+frazzle/GDS
+freakishness/SM
+freakish/YP
+freak/SGDM
+freaky/RT
+freckle/GMDS
+freckly/RT
+Freda/M
+Freddie/M
+Freddi/M
+Freddy/M
+Fredek/M
+Fredelia/M
+Frederica/M
+Frederich/M
+Fredericka/M
+Frederick/MS
+Frederic/M
+Frederico/M
+Fredericton/M
+Frederigo/M
+Frederik/M
+Frederique/M
+Fredholm/M
+Fredia/M
+Fredi/M
+Fred/M
+Fredra/M
+Fredrick/M
+Fredrickson/M
+Fredric/M
+Fredrika/M
+freebase/GDS
+freebie/MS
+freebooter/M
+freeboot/ZR
+freeborn
+freedman/M
+Freedman/M
+freedmen
+freedom/MS
+freehand/D
+freehanded/Y
+freeholder/M
+freehold/ZSRM
+freeing/S
+freelance/SRDGZM
+Freeland/M
+freeloader/M
+freeload/SRDGZ
+Free/M
+freeman/M
+Freeman/M
+freemasonry/M
+Freemasonry/MS
+Freemason/SM
+freemen
+Freemon/M
+freeness/M
+Freeport/M
+freestanding
+freestone/SM
+freestyle/SM
+freethinker/MS
+freethinking/S
+Freetown/M
+freeway/MS
+freewheeler/M
+freewheeling/P
+freewheel/SRDMGZ
+freewill
+free/YTDRSP
+freezable
+freezer/SM
+freeze/UGSA
+freezing/S
+Freida/M
+freighter/M
+freight/ZGMDRS
+Fremont/M
+Frenchman/M
+French/MDSG
+Frenchmen
+Frenchwoman/M
+Frenchwomen
+frenetically
+frenetic/S
+frenzied/Y
+frenzy/MDSG
+freon/S
+Freon/SM
+freq
+frequency/ISM
+frequented/U
+frequenter/MS
+frequentest
+frequenting
+frequent/IY
+frequentness/M
+frequents
+fresco/DMG
+frescoes
+fresh/AZSRNDG
+freshener/M
+freshen/SZGDR
+fresher/MA
+freshest
+freshet/SM
+freshly
+freshman/M
+freshmen
+freshness/MS
+freshwater/SM
+Fresnel/M
+Fresno/M
+fretboard
+fretfulness/MS
+fretful/PY
+fret/S
+fretsaw/S
+fretted
+fretting
+fretwork/MS
+Freudian/S
+Freud/M
+Freya/M
+Frey/M
+friableness/M
+friable/P
+friary/MS
+friar/YMS
+fricasseeing
+fricassee/MSD
+frication/M
+fricative/MS
+Frick/M
+frictional/Y
+frictionless/Y
+friction/MS
+Friday/SM
+fridge/SM
+fried/A
+Frieda/M
+Friedan/M
+friedcake/SM
+Friederike/M
+Friedman/M
+Friedrich/M
+Friedrick/M
+friendlessness/M
+friendless/P
+friendlies
+friendlily
+friendliness/USM
+friendly/PUTR
+friend/SGMYD
+friendship/MS
+frier's
+fries/M
+frieze/SDGM
+frigate/SM
+Frigga/M
+frigged
+frigging/S
+frighten/DG
+frightening/Y
+frightfulness/MS
+frightful/PY
+fright/GXMDNS
+Frigidaire/M
+frigidity/MS
+frigidness/SM
+frigid/YP
+frig/S
+frill/MDGS
+frilly/RST
+Fri/M
+fringe/IGSD
+fringe's
+frippery/SM
+Frisbee/MS
+Frisco/M
+Frisian/SM
+frisker/M
+friskily
+friskiness/SM
+frisk/RDGS
+frisky/RTP
+frisson/M
+Frito/M
+fritterer/M
+fritter/RDSG
+Fritz/M
+fritz/SM
+frivolity/MS
+frivolousness/SM
+frivolous/PY
+frizz/GYSD
+frizzle/DSG
+frizzly/RT
+frizzy/RT
+Fr/MD
+Frobisher/M
+frocking/M
+frock's
+frock/SUDGC
+frogged
+frogging
+frogman/M
+frogmarched
+frogmen
+frog/MS
+fro/HS
+Froissart/M
+frolicked
+frolicker/SM
+frolicking
+frolic/SM
+frolicsome
+from
+Fromm/M
+frond/SM
+frontage/MS
+frontal/SY
+Frontenac/M
+front/GSFRD
+frontier/SM
+frontiersman/M
+frontiersmen
+frontispiece/SM
+frontrunner's
+front's
+frontward/S
+frosh/M
+Frostbelt/M
+frostbite/MS
+frostbit/G
+frostbiting/M
+frostbitten
+frost/CDSG
+frosteds
+frosted/U
+frostily
+frostiness/SM
+frosting/MS
+Frost/M
+frost's
+frosty/PTR
+froth/GMD
+frothiness/SM
+froths
+frothy/TRP
+froufrou/MS
+frowardness/MS
+froward/P
+frowner/M
+frowning/Y
+frown/RDSG
+frowzily
+frowziness/SM
+frowzy/RPT
+frozenness/M
+frozen/YP
+froze/UA
+fructify/GSD
+fructose/MS
+Fruehauf/M
+frugality/SM
+frugal/Y
+fruitcake/SM
+fruiterer/M
+fruiter/RM
+fruitfuller
+fruitfullest
+fruitfulness/MS
+fruitful/UYP
+fruit/GMRDS
+fruitiness/MS
+fruition/SM
+fruitlessness/MS
+fruitless/YP
+fruity/RPT
+frumpish
+frump/MS
+frumpy/TR
+Frunze/M
+frustrater/M
+frustrate/RSDXNG
+frustrating/Y
+frustration/M
+frustum/SM
+Frye/M
+fryer/MS
+Fry/M
+fry/NGDS
+F's
+f's/KA
+FSLIC
+ft/C
+FTC
+FTP
+fuchsia/MS
+Fuchs/M
+fucker/M
+fuck/GZJRDMS
+FUD
+fuddle/GSD
+fudge/GMSD
+fuel/ASDG
+fueler/SM
+fuel's
+Fuentes/M
+fugal
+Fugger/M
+fugitiveness/M
+fugitive/SYMP
+fugue/GMSD
+fuhrer/S
+Fuji/M
+Fujitsu/M
+Fujiyama
+Fukuoka/M
+Fulani/M
+Fulbright/M
+fulcrum/SM
+fulfilled/U
+fulfiller/M
+fulfill/GLSRD
+fulfillment/MS
+fullback/SMG
+fuller/DMG
+Fuller/M
+Fullerton/M
+fullish
+fullness/MS
+full/RDPSGZT
+fullstops
+fullword/SM
+fully
+fulminate/XSDGN
+fulmination/M
+fulness's
+fulsomeness/SM
+fulsome/PY
+Fulton/M
+Fulvia/M
+fumble/GZRSD
+fumbler/M
+fumbling/Y
+fume/DSG
+fumigant/MS
+fumigate/NGSDX
+fumigation/M
+fumigator/SM
+fuming/Y
+fumy/TR
+Funafuti
+functionalism/M
+functionalist/SM
+functionality/S
+functional/YS
+functionary/MS
+function/GSMD
+functor/SM
+fundamentalism/SM
+fundamentalist/SM
+fundamental/SY
+fund/ASMRDZG
+funded/U
+fundholders
+fundholding
+funding/S
+Fundy/M
+funeral/MS
+funerary
+funereal/Y
+funfair/M
+fungal/S
+fungible/M
+fungicidal
+fungicide/SM
+fungi/M
+fungoid/S
+fungous
+fungus/M
+funicular/SM
+funk/GSDM
+funkiness/S
+funky/RTP
+fun/MS
+funned
+funnel/SGMD
+funner
+funnest
+funnily/U
+funniness/SM
+funning
+funny/RSPT
+furbelow/MDSG
+furbisher/M
+furbish/GDRSA
+furiousness/M
+furious/RYP
+furlong/MS
+furlough/DGM
+furloughs
+furl/UDGS
+furn
+furnace/GMSD
+furnished/U
+furnisher/MS
+furnish/GASD
+furnishing/SM
+furniture/SM
+furore/MS
+furor/MS
+fur/PMS
+furred
+furrier/M
+furriness/SM
+furring/SM
+furrow/DMGS
+furry/RTZP
+furtherance/MS
+furtherer/M
+furthermore
+furthermost
+further/TGDRS
+furthest
+furtiveness/SM
+furtive/PY
+fury/SM
+furze/SM
+fusebox/S
+fusee/SM
+fuse/FSDAGCI
+fuselage/SM
+fuse's/A
+Fushun/M
+fusibility/SM
+fusible/I
+fusiform
+fusilier/MS
+fusillade/SDMG
+fusion/KMFSI
+fussbudget/MS
+fusser/M
+fussily
+fussiness/MS
+fusspot/SM
+fuss/SRDMG
+fussy/PTR
+fustian/MS
+fustiness/MS
+fusty/RPT
+fut
+futileness/M
+futile/PY
+futility/MS
+futon/S
+future/SM
+futurism/SM
+futuristic/S
+futurist/S
+futurity/MS
+futurologist/S
+futurology/MS
+futz/GSD
+fuze's
+Fuzhou/M
+Fuzzbuster/M
+fuzzily
+fuzziness/SM
+fuzz/SDMG
+fuzzy/PRT
+fwd
+FWD
+fwy
+FY
+FYI
+GA
+gabardine/SM
+gabbed
+Gabbey/M
+Gabbie/M
+Gabbi/M
+gabbiness/S
+gabbing
+gabble/SDG
+Gabby/M
+gabby/TRP
+Gabe/M
+gaberdine's
+Gabey/M
+gabfest/MS
+Gabie/M
+Gabi/M
+gable/GMSRD
+Gable/M
+Gabonese
+Gabon/M
+Gaborone/M
+Gabriela/M
+Gabriele/M
+Gabriella/M
+Gabrielle/M
+Gabriellia/M
+Gabriell/M
+Gabriello/M
+Gabriel/M
+Gabrila/M
+gab/S
+Gaby/M
+Gacrux/M
+gadabout/MS
+gadded
+gadder/MS
+gadding
+gadfly/MS
+gadgetry/MS
+gadget/SM
+gadolinium/MS
+gad/S
+Gadsden/M
+Gaea/M
+Gaelan/M
+Gaelic/M
+Gael/SM
+Gae/M
+gaffe/MS
+gaffer/M
+gaff/SGZRDM
+gaga
+Gagarin/M
+gag/DRSG
+Gage/M
+gager/M
+gage/SM
+gagged
+gagging
+gaggle/SDG
+gagwriter/S
+gaiety/MS
+Gaile/M
+Gail/M
+gaily
+gain/ADGS
+gainer/SM
+Gaines/M
+Gainesville/M
+gainfulness/M
+gainful/YP
+gaining/S
+gainly/U
+gainsaid
+gainsayer/M
+gainsay/RSZG
+Gainsborough/M
+gaiter/M
+gait/GSZMRD
+Gaithersburg/M
+galactic
+Galahad/MS
+Galapagos/M
+gal/AS
+gala/SM
+Galatea/M
+Galatia/M
+Galatians/M
+Galaxy/M
+galaxy/MS
+Galbraith/M
+Galbreath/M
+gale/AS
+Gale/M
+galen
+galena/MS
+galenite/M
+Galen/M
+gale's
+Galibi/M
+Galilean/MS
+Galilee/M
+Galileo/M
+Galina/M
+Gallagher/M
+gallanted
+gallanting
+gallantry/MS
+gallants
+gallant/UY
+Gallard/M
+gallbladder/MS
+Gallegos/M
+galleon/SM
+galleria/S
+gallery/MSDG
+galley/MS
+Gallic
+Gallicism/SM
+gallimaufry/MS
+galling/Y
+gallium/SM
+gallivant/GDS
+Gall/M
+gallonage/M
+gallon/SM
+galloper/M
+gallop/GSRDZ
+Galloway/M
+gallows/M
+gall/SGMD
+gallstone/MS
+Gallup/M
+Gal/MN
+Galois/M
+galoot/MS
+galore/S
+galosh/GMSD
+gal's
+Galsworthy/M
+galumph/GD
+galumphs
+galvanic
+Galvani/M
+galvanism/MS
+galvanization/SM
+galvanize/SDG
+Galvan/M
+galvanometer/SM
+galvanometric
+Galven/M
+Galveston/M
+Galvin/M
+Ga/M
+Gamaliel/M
+Gama/M
+Gambia/M
+Gambian/S
+gambit/MS
+gamble/GZRSD
+Gamble/M
+gambler/M
+gambol/SGD
+gamecock/SM
+gamekeeper/MS
+gameness/MS
+game/PJDRSMYTZG
+gamesmanship/SM
+gamesmen
+gamester/M
+gamest/RZ
+gamete/MS
+gametic
+gamine/SM
+gaminess/MS
+gaming/M
+gamin/MS
+gamma/MS
+gammon/DMSG
+Gamow/M
+gamut/MS
+gamy/TRP
+gander/DMGS
+Gandhian
+Gandhi/M
+gangbusters
+ganger/M
+Ganges/M
+gang/GRDMS
+gangland/SM
+ganglia/M
+gangling
+ganglionic
+ganglion/M
+gangplank/SM
+gangrene/SDMG
+gangrenous
+gangster/SM
+Gangtok/M
+gangway/MS
+Gan/M
+gannet/SM
+Gannie/M
+Gannon/M
+Ganny/M
+gantlet/GMDS
+Gantry/M
+gantry/MS
+Ganymede/M
+GAO
+gaoler/M
+gaol/MRDGZS
+gaper/M
+gape/S
+gaping/Y
+gapped
+gapping
+gap/SJMDRG
+garage/GMSD
+Garald/M
+garbageman/M
+garbage/SDMG
+garbanzo/MS
+garb/DMGS
+garbler/M
+garble/RSDG
+Garbo/M
+Garcia/M
+garçon/SM
+gardener/M
+Gardener/M
+gardenia/SM
+gardening/M
+garden/ZGRDMS
+Gardie/M
+Gardiner/M
+Gard/M
+Gardner/M
+Gardy/M
+Garek/M
+Gare/MH
+Gareth/M
+Garey/M
+Garfield/M
+garfish/MS
+Garfunkel/M
+Gargantua/M
+gargantuan
+gargle/SDG
+gargoyle/DSM
+Garibaldi/M
+Garik/M
+garishness/MS
+garish/YP
+Garland/M
+garland/SMDG
+garlicked
+garlicking
+garlicky
+garlic/SM
+garment/MDGS
+Gar/MH
+Garner/M
+garner/SGD
+Garnet/M
+garnet/SM
+Garnette/M
+Garnett/M
+garnish/DSLG
+garnisheeing
+garnishee/SDM
+garnishment/MS
+Garold/M
+garote's
+garotte's
+Garrard/M
+garred
+Garrek/M
+Garreth/M
+Garret/M
+garret/SM
+Garrett/M
+Garrick/M
+Garrik/M
+garring
+Garrison/M
+garrison/SGMD
+garroter/M
+garrote/SRDMZG
+Garrot/M
+garrotte's
+Garrott/M
+garrulity/SM
+garrulousness/MS
+garrulous/PY
+Garry/M
+gar/SLM
+garter/SGDM
+Garth/M
+Garvey/M
+Garvin/M
+Garv/M
+Garvy/M
+Garwin/M
+Garwood/M
+Gary/M
+Garza/M
+gasbag/MS
+Gascony/M
+gaseousness/M
+gaseous/YP
+gases/C
+gas/FC
+gash/GTMSRD
+gasification/M
+gasifier/M
+gasify/SRDGXZN
+gasket/SM
+gaslight/DMS
+gasohol/S
+gasoline/MS
+gasometer/M
+Gaspard/M
+Gaspar/M
+Gasparo/M
+gasper/M
+Gasper/M
+gasp/GZSRD
+gasping/Y
+gas's
+gassed/C
+Gasser/M
+gasser/MS
+Gasset/M
+gassiness/M
+gassing/SM
+gassy/PTR
+Gaston/M
+gastric
+gastritides
+gastritis/MS
+gastroenteritides
+gastroenteritis/M
+gastrointestinal
+gastronome/SM
+gastronomic
+gastronomical/Y
+gastronomy/MS
+gastropod/SM
+gasworks/M
+gateau/MS
+gateaux
+gatecrash/GZSRD
+gatehouse/MS
+gatekeeper/SM
+gate/MGDS
+gatepost/SM
+Gates
+gateway/MS
+gathered/IA
+gatherer/M
+gathering/M
+gather/JRDZGS
+gathers/A
+Gatlinburg/M
+Gatling/M
+Gatorade/M
+gator/MS
+Gatsby/M
+Gatun/M
+gaucheness/SM
+gaucherie/SM
+gauche/TYPR
+gaucho/SM
+gaudily
+gaudiness/MS
+gaudy/PRST
+gaugeable
+gauger/M
+Gauguin/M
+Gaulish/M
+Gaulle/M
+Gaul/MS
+Gaultiero/M
+gauntlet/GSDM
+Gauntley/M
+gauntness/MS
+gaunt/PYRDSGT
+gauss/C
+gausses
+Gaussian
+Gauss/M
+gauss's
+Gautama/M
+Gauthier/M
+Gautier/M
+gauze/SDGM
+gauziness/MS
+gauzy/TRP
+Gavan/M
+gave
+gavel/GMDS
+Gaven/M
+Gavin/M
+Gav/MN
+gavotte/MSDG
+Gavra/M
+Gavrielle/M
+Gawain/M
+Gawen/M
+gawkily
+gawkiness/MS
+gawk/SGRDM
+gawky/RSPT
+Gayel/M
+Gayelord/M
+Gaye/M
+gayety's
+Gayla/M
+Gayleen/M
+Gaylene/M
+Gayler/M
+Gayle/RM
+Gaylord/M
+Gaylor/M
+Gay/M
+gayness/SM
+Gaynor/M
+gay/RTPS
+Gaza/M
+gazebo/SM
+gaze/DRSZG
+gazelle/MS
+gazer/M
+gazetteer/SGDM
+gazette/MGSD
+Gaziantep/M
+gazillion/S
+gazpacho/MS
+GB
+G/B
+Gdansk/M
+Gd/M
+GDP
+Gearalt/M
+Gearard/M
+gearbox/SM
+gear/DMJSG
+gearing/M
+gearshift/MS
+gearstick
+gearwheel/SM
+Geary/M
+gecko/MS
+GED
+geegaw's
+geeing
+geek/SM
+geeky/RT
+geese/M
+geest/M
+gee/TDS
+geezer/MS
+Gehenna/M
+Gehrig/M
+Geiger/M
+Geigy/M
+geisha/M
+gelatinousness/M
+gelatinous/PY
+gelatin/SM
+gelcap
+gelding/M
+geld/JSGD
+gelid
+gelignite/MS
+gelled
+gelling
+gel/MBS
+Gelya/M
+Ge/M
+GE/M
+Gemini/SM
+gemlike
+Gemma/M
+gemmed
+gemming
+gem/MS
+gemological
+gemologist/MS
+gemology/MS
+gemstone/SM
+gen
+Gena/M
+Genaro/M
+gendarme/MS
+gender/DMGS
+genderless
+genealogical/Y
+genealogist/SM
+genealogy/MS
+Gene/M
+gene/MS
+generalissimo/SM
+generalist/MS
+generality/MS
+generalizable/SM
+generalization/MS
+generalized/U
+generalize/GZBSRD
+generalizer/M
+general/MSPY
+generalness/M
+generalship/SM
+genera/M
+generate/CXAVNGSD
+generational
+generation/MCA
+generative/AY
+generators/A
+generator/SM
+generically
+generic/PS
+generosity/MS
+generously/U
+generousness/SM
+generous/PY
+Genesco/M
+genesis/M
+Genesis/M
+genes/S
+genetically
+geneticist/MS
+genetic/S
+genetics/M
+Genet/M
+Geneva/M
+Genevieve/M
+Genevra/M
+Genghis/M
+geniality/FMS
+genially/F
+genialness/M
+genial/PY
+Genia/M
+genies/K
+genie/SM
+genii/M
+genitalia
+genitals
+genital/YF
+genitive/SM
+genitourinary
+genius/SM
+Gen/M
+Genna/M
+Gennie/M
+Gennifer/M
+Genni/M
+Genny/M
+Genoa/SM
+genocidal
+genocide/SM
+Geno/M
+genome/SM
+genotype/MS
+Genovera/M
+genre/MS
+gent/AMS
+genteelness/MS
+genteel/PRYT
+gentian/SM
+gentile/S
+Gentile's
+gentility/MS
+gentlefolk/S
+gentlemanliness/M
+gentlemanly/U
+gentleman/YM
+gentlemen
+gentleness/SM
+gentle/PRSDGT
+gentlewoman/M
+gentlewomen/M
+gently
+gentrification/M
+gentrify/NSDGX
+Gentry/M
+gentry/MS
+genuflect/GDS
+genuflection/MS
+genuineness/SM
+genuine/PY
+genus
+Genvieve/M
+geocentric
+geocentrically
+geocentricism
+geochemical/Y
+geochemistry/MS
+geochronology/M
+geodesic/S
+geode/SM
+geodesy/MS
+geodetic/S
+Geoff/M
+Geoffrey/M
+Geoffry/M
+geog
+geographer/MS
+geographic
+geographical/Y
+geography/MS
+geologic
+geological/Y
+geologist/MS
+geology/MS
+geom
+Geo/M
+geomagnetic
+geomagnetically
+geomagnetism/SM
+geometer/MS
+geometrical/Y
+geometrician/M
+geometric/S
+geometry/MS
+geomorphological
+geomorphology/M
+geophysical/Y
+geophysicist/MS
+geophysics/M
+geopolitical/Y
+geopolitic/S
+geopolitics/M
+Georas/M
+Geordie/M
+Georgeanna/M
+Georgeanne/M
+Georgena/M
+George/SM
+Georgeta/M
+Georgetown/M
+Georgetta/M
+Georgette/M
+Georgia/M
+Georgiana/M
+Georgianna/M
+Georgianne/M
+Georgian/S
+Georgie/M
+Georgi/M
+Georgina/M
+Georgine/M
+Georg/M
+Georgy/M
+geostationary
+geosynchronous
+geosyncline/SM
+geothermal
+geothermic
+Geralda/M
+Geraldine/M
+Gerald/M
+geranium/SM
+Gerard/M
+Gerardo/M
+Gerber/M
+gerbil/MS
+Gerda/M
+Gerek/M
+Gerhardine/M
+Gerhard/M
+Gerhardt/M
+Gerianna/M
+Gerianne/M
+geriatric/S
+geriatrics/M
+Gerick/M
+Gerik/M
+Geri/M
+Geritol/M
+Gerladina/M
+Ger/M
+Germaine/M
+Germain/M
+Germana/M
+germane
+Germania/M
+Germanic/M
+germanium/SM
+germanized
+German/SM
+Germantown/M
+Germany/M
+Germayne/M
+germen/M
+germicidal
+germicide/MS
+germinal/Y
+germinated/U
+germinate/XVGNSD
+germination/M
+germinative/Y
+germ/MNS
+Gerome/M
+Geronimo/M
+gerontocracy/M
+gerontological
+gerontologist/SM
+gerontology/SM
+Gerrard/M
+Gerrie/M
+Gerrilee/M
+Gerri/M
+Gerry/M
+gerrymander/SGD
+Gershwin/MS
+Gerta/M
+Gertie/M
+Gerti/M
+Gert/M
+Gertruda/M
+Gertrude/M
+Gertrudis/M
+Gertrud/M
+Gerty/M
+gerundive/M
+gerund/SVM
+Gery/M
+gestalt/M
+gestapo/S
+Gestapo/SM
+gestate/SDGNX
+gestational
+gestation/M
+gesticulate/XSDVGN
+gesticulation/M
+gesticulative/Y
+gestural
+gesture/SDMG
+gesundheit
+getaway/SM
+Gethsemane/M
+get/S
+getter/SDM
+getting
+Getty/M
+Gettysburg/M
+getup/MS
+gewgaw/MS
+Gewürztraminer
+geyser/GDMS
+Ghanaian/MS
+Ghana/M
+Ghanian's
+ghastliness/MS
+ghastly/TPR
+ghat/MS
+Ghats/M
+Ghent/M
+Gherardo/M
+gherkin/SM
+ghetto/DGMS
+ghettoize/SDG
+Ghibelline/M
+ghostlike
+ghostliness/MS
+ghostly/TRP
+ghost/SMYDG
+ghostwrite/RSGZ
+ghostwritten
+ghostwrote
+ghoulishness/SM
+ghoulish/PY
+ghoul/SM
+GHQ
+GI
+Giacinta/M
+Giacobo/M
+Giacometti/M
+Giacomo/M
+Giacopo/M
+Giana/M
+Gianina/M
+Gian/M
+Gianna/M
+Gianni/M
+Giannini/M
+giantess/MS
+giantkiller
+giant/SM
+Giauque/M
+Giavani/M
+gibber/DGS
+gibberish/MS
+gibbet/MDSG
+Gibbie/M
+Gibb/MS
+Gibbon/M
+gibbon/MS
+gibbousness/M
+gibbous/YP
+Gibby/M
+gibe/GDRS
+giber/M
+giblet/MS
+Gib/M
+Gibraltar/MS
+Gibson/M
+giddap
+giddily
+giddiness/SM
+Giddings/M
+giddy/GPRSDT
+Gide/M
+Gideon/MS
+Gielgud/M
+Gienah/M
+Giffard/M
+Giffer/M
+Giffie/M
+Gifford/M
+Giff/RM
+Giffy/M
+giftedness/M
+gifted/PY
+gift/SGMD
+gigabyte/S
+gigacycle/MS
+gigahertz/M
+gigantically
+giganticness/M
+gigantic/P
+gigavolt
+gigawatt/M
+gigged
+gigging
+giggler/M
+giggle/RSDGZ
+giggling/Y
+giggly/TR
+Gigi/M
+gig/MS
+GIGO
+gigolo/MS
+gila
+Gila/M
+Gilberta/M
+Gilberte/M
+Gilbertina/M
+Gilbertine/M
+gilbert/M
+Gilbert/M
+Gilberto/M
+Gilbertson/M
+Gilburt/M
+Gilchrist/M
+Gilda/M
+gilder/M
+gilding/M
+gild/JSGZRD
+Gilead/M
+Gilemette/M
+Giles
+Gilgamesh/M
+Gilkson/M
+Gillan/M
+Gilles
+Gillespie/M
+Gillette/M
+Gilliam/M
+Gillian/M
+Gillie/M
+Gilligan/M
+Gilli/M
+Gill/M
+gill/SGMRD
+Gilly/M
+Gilmore/M
+Gil/MY
+gilt/S
+gimbaled
+gimbals
+Gimbel/M
+gimcrackery/SM
+gimcrack/S
+gimlet/MDSG
+gimme/S
+gimmick/GDMS
+gimmickry/MS
+gimmicky
+gimp/GSMD
+gimpy/RT
+Gina/M
+Ginelle/M
+Ginevra/M
+gingerbread/SM
+gingerliness/M
+gingerly/P
+Ginger/M
+ginger/SGDYM
+gingersnap/SM
+gingery
+gingham/SM
+gingivitis/SM
+Gingrich/M
+ginkgoes
+ginkgo/M
+ginmill
+gin/MS
+ginned
+Ginnie/M
+Ginnifer/M
+Ginni/M
+ginning
+Ginny/M
+Gino/M
+Ginsberg/M
+Ginsburg/M
+ginseng/SM
+Gioconda/M
+Giordano/M
+Giorgia/M
+Giorgi/M
+Giorgio/M
+Giorgione/M
+Giotto/M
+Giovanna/M
+Giovanni/M
+Gipsy's
+giraffe/MS
+Giralda/M
+Giraldo/M
+Giraud/M
+Giraudoux/M
+girded/U
+girder/M
+girdle/GMRSD
+girdler/M
+gird/RDSGZ
+girlfriend/MS
+girlhood/SM
+girlie/M
+girlishness/SM
+girlish/YP
+girl/MS
+giro/M
+girt/GDS
+girth/MDG
+girths
+Gisela/M
+Giselbert/M
+Gisele/M
+Gisella/M
+Giselle/M
+Gish/M
+gist/MS
+git/M
+Giuditta/M
+Giulia/M
+Giuliano/M
+Giulietta/M
+Giulio/M
+Giuseppe/M
+Giustina/M
+Giustino/M
+Giusto/M
+giveaway/SM
+giveback/S
+give/HZGRS
+given/SP
+giver/M
+giving/Y
+Giza/M
+Gizela/M
+gizmo's
+gizzard/SM
+Gk/M
+glacé/DGS
+glacial/Y
+glaciate/XNGDS
+glaciation/M
+glacier/SM
+glaciological
+glaciologist/M
+glaciology/M
+gladded
+gladden/GDS
+gladder
+gladdest
+gladding
+gladdy
+glade/SM
+gladiatorial
+gladiator/SM
+Gladi/M
+gladiola/MS
+gladioli
+gladiolus/M
+gladly/RT
+Glad/M
+gladness/MS
+gladsome/RT
+Gladstone/MS
+Gladys
+glad/YSP
+glamor/DMGS
+glamorization/MS
+glamorizer/M
+glamorize/SRDZG
+glamorousness/M
+glamorous/PY
+glance/GJSD
+glancing/Y
+glanders/M
+glandes
+glandular/Y
+gland/ZSM
+glans/M
+glare/SDG
+glaringness/M
+glaring/YP
+Glaser/M
+Glasgow/M
+glasnost/S
+glassblower/S
+glassblowing/MS
+glassful/MS
+glass/GSDM
+glasshouse/SM
+glassily
+glassiness/SM
+glassless
+Glass/M
+glassware/SM
+glasswort/M
+glassy/PRST
+Glastonbury/M
+Glaswegian/S
+glaucoma/SM
+glaucous
+glazed/U
+glazer/M
+glaze/SRDGZJ
+glazier/SM
+glazing/M
+gleam/MDGS
+gleaner/M
+gleaning/M
+glean/RDGZJS
+Gleason/M
+Gleda/M
+gleed/M
+glee/DSM
+gleefulness/MS
+gleeful/YP
+gleeing
+Glendale/M
+Glenda/M
+Glenden/M
+Glendon/M
+Glenine/M
+Glen/M
+Glenna/M
+Glennie/M
+Glennis/M
+Glenn/M
+glen/SM
+glibber
+glibbest
+glibness/MS
+glib/YP
+glide/JGZSRD
+glider/M
+glim/M
+glimmer/DSJG
+glimmering/M
+glimpse/DRSZMG
+glimpser/M
+glint/DSG
+glissandi
+glissando/M
+glisten/DSG
+glister/DGS
+glitch/MS
+glitter/GDSJ
+glittering/Y
+glittery
+glitz/GSD
+glitzy/TR
+gloaming/MS
+gloater/M
+gloating/Y
+gloat/SRDG
+globalism/S
+globalist/S
+global/SY
+globe/SM
+globetrotter/MS
+glob/GDMS
+globularity/M
+globularness/M
+globular/PY
+globule/MS
+globulin/MS
+glockenspiel/SM
+glommed
+gloom/GSMD
+gloomily
+gloominess/MS
+gloomy/RTP
+glop/MS
+glopped
+glopping
+gloppy/TR
+Gloria/M
+Gloriana/M
+Gloriane/M
+glorification/M
+glorifier/M
+glorify/XZRSDNG
+Glori/M
+glorious/IYP
+gloriousness/IM
+Glory/M
+glory/SDMG
+glossary/MS
+gloss/GSDM
+glossily
+glossiness/SM
+glossolalia/SM
+glossy/RSPT
+glottal
+glottalization/M
+glottis/MS
+Gloucester/M
+gloveless
+glover/M
+Glover/M
+glove/SRDGMZ
+glower/GD
+glow/GZRDMS
+glowing/Y
+glowworm/SM
+glucose/SM
+glue/DRSMZG
+glued/U
+gluer/M
+gluey
+gluier
+gluiest
+glummer
+glummest
+glumness/MS
+glum/SYP
+gluon/M
+glutamate/M
+gluten/M
+glutenous
+glutinousness/M
+glutinous/PY
+glut/SMNX
+glutted
+glutting
+glutton/MS
+gluttonous/Y
+gluttony/SM
+glyceride/M
+glycerinate/MD
+glycerine's
+glycerin/SM
+glycerolized/C
+glycerol/SM
+glycine/M
+glycogen/SM
+glycol/MS
+Glynda/M
+Glynis/M
+Glyn/M
+Glynnis/M
+Glynn/M
+glyph/M
+glyphs
+gm
+GM
+GMT
+gnarl/SMDG
+gnash/SDG
+gnat/MS
+gnawer/M
+gnaw/GRDSJ
+gnawing/M
+gneiss/SM
+Gnni/M
+gnomelike
+gnome/SM
+gnomic
+gnomish
+gnomonic
+gnosticism
+Gnosticism/M
+gnostic/K
+Gnostic/M
+GNP
+gnu/MS
+goad/MDSG
+goalie/SM
+goalkeeper/MS
+goalkeeping/M
+goalless
+goal/MDSG
+goalmouth/M
+goalpost/S
+goalscorer
+goalscoring
+goaltender/SM
+Goa/M
+goatee/SM
+goatherd/MS
+goat/MS
+goatskin/SM
+gobbed
+gobbet/MS
+gobbing
+gobbledegook's
+gobbledygook/S
+gobbler/M
+gobble/SRDGZ
+Gobi/M
+goblet/MS
+goblin/SM
+gob/SM
+Godard/M
+Godart/M
+godchild/M
+godchildren
+goddammit
+goddamn/GS
+Goddard/M
+Goddart/M
+goddaughter/SM
+godded
+goddess/MS
+godding
+Gödel/M
+godfather/GSDM
+godforsaken
+Godfree/M
+Godfrey/M
+Godfry/M
+godhead/S
+godhood/SM
+Godiva/M
+godlessness/MS
+godless/P
+godlikeness/M
+godlike/P
+godliness/UMS
+godly/UTPR
+God/M
+godmother/MS
+Godot/M
+godparent/SM
+godsend/MS
+god/SMY
+godson/MS
+Godspeed/S
+Godthaab/M
+Godunov/M
+Godwin/M
+Godzilla/M
+Goebbels/M
+Goering/M
+goer/MG
+goes
+Goethals/M
+Goethe/M
+gofer/SM
+Goff/M
+goggler/M
+goggle/SRDGZ
+Gogh/M
+Gog/M
+Gogol/M
+Goiania/M
+going/M
+goiter/SM
+Golan/M
+Golconda/M
+Golda/M
+Goldarina/M
+Goldberg/M
+goldbricker/M
+goldbrick/GZRDMS
+Golden/M
+goldenness/M
+goldenrod/SM
+goldenseal/M
+golden/TRYP
+goldfinch/MS
+goldfish/SM
+Goldia/M
+Goldie/M
+Goldilocks/M
+Goldi/M
+Goldina/M
+Golding/M
+Goldman/M
+goldmine/S
+gold/MRNGTS
+goldsmith/M
+Goldsmith/M
+goldsmiths
+Goldstein/M
+Goldwater/M
+Goldwyn/M
+Goldy/M
+Goleta/M
+golfer/M
+golf/RDMGZS
+Golgotha/M
+Goliath/M
+Goliaths
+golly/S
+Gomez/M
+Gomorrah/M
+Gompers/M
+go/MRHZGJ
+gonadal
+gonad/SM
+gondola/SM
+gondolier/MS
+Gondwanaland/M
+goner/M
+gone/RZN
+gong/SGDM
+gonion/M
+gonna
+gonorrheal
+gonorrhea/MS
+Gonzales/M
+Gonzalez/M
+Gonzalo/M
+Goober/M
+goober/MS
+goodbye/MS
+goodhearted
+goodie's
+goodish
+goodly/TR
+Good/M
+Goodman/M
+goodness/MS
+goodnight
+Goodrich/M
+good/SYP
+goodwill/MS
+Goodwin/M
+Goodyear/M
+goody/SM
+gooey
+goofiness/MS
+goof/SDMG
+goofy/RPT
+gooier
+gooiest
+gook/SM
+goo/MS
+goon/SM
+goop/SM
+gooseberry/MS
+goosebumps
+goose/M
+goos/SDG
+GOP
+Gopher
+gopher/SM
+Goran/M
+Goraud/M
+Gorbachev
+Gordan/M
+Gorden/M
+Gordian/M
+Gordie/M
+Gordimer/M
+Gordon/M
+Gordy/M
+gore/DSMG
+Gore/M
+Goren/M
+Gorey/M
+Gorgas
+gorged/E
+gorge/GMSRD
+gorgeousness/SM
+gorgeous/YP
+gorger/EM
+gorges/E
+gorging/E
+Gorgon/M
+gorgon/S
+Gorgonzola/M
+Gorham/M
+gorilla/MS
+gorily
+goriness/MS
+goring/M
+Gorky/M
+gormandizer/M
+gormandize/SRDGZ
+gormless
+gorp/S
+gorse/SM
+gory/PRT
+gos
+goshawk/MS
+gosh/S
+gosling/M
+gospeler/M
+gospel/MRSZ
+Gospel/SM
+gossamer/SM
+gossipy
+gossip/ZGMRDS
+gotcha/SM
+Göteborg/M
+Gotham/M
+Gothart/M
+Gothicism/M
+Gothic/S
+Goth/M
+Goths
+got/IU
+goto
+GOTO/MS
+gotta
+gotten/U
+Gottfried/M
+Goucher/M
+Gouda/SM
+gouge/GZSRD
+gouger/M
+goulash/SM
+Gould/M
+Gounod/M
+gourde/SM
+gourd/MS
+gourmand/MS
+gourmet/MS
+gout/SM
+gouty/RT
+governable/U
+governance/SM
+governed/U
+governess/SM
+govern/LBGSD
+governmental/Y
+government/MS
+Governor
+governor/MS
+governorship/SM
+gov/S
+govt
+gown/GSDM
+Goya/M
+GP
+GPA
+GPO
+GPSS
+gr
+grabbed
+grabber/SM
+grabbing/S
+grab/S
+Gracchus/M
+grace/ESDMG
+graceful/EYPU
+gracefuller
+gracefullest
+gracefulness/ESM
+Graceland/M
+gracelessness/MS
+graceless/PY
+Grace/M
+Gracia/M
+Graciela/M
+Gracie/M
+graciousness/SM
+gracious/UY
+grackle/SM
+gradate/DSNGX
+gradation/MCS
+grade/ACSDG
+graded/U
+Gradeigh/M
+gradely
+grader/MC
+grade's
+Gradey/M
+gradient/RMS
+grad/MRDGZJS
+gradualism/MS
+gradualist/MS
+gradualness/MS
+gradual/SYP
+graduand/SM
+graduate/MNGDSX
+graduation/M
+Grady/M
+Graehme/M
+Graeme/M
+Graffias/M
+graffiti
+graffito/M
+Graff/M
+grafter/M
+grafting/M
+graft/MRDSGZ
+Grafton/M
+Grahame/M
+Graham/M
+graham/SM
+Graig/M
+grail/S
+Grail/SM
+grainer/M
+grain/IGSD
+graininess/MS
+graining/M
+grain's
+grainy/RTP
+gram/KSM
+Gram/M
+grammarian/SM
+grammar/MS
+grammaticality/M
+grammaticalness/M
+grammatical/UY
+grammatic/K
+gramme/SM
+Grammy/S
+gramophone/SM
+Grampians
+grampus/SM
+Granada/M
+granary/MS
+grandam/SM
+grandaunt/MS
+grandchild/M
+grandchildren
+granddaddy/MS
+granddad/SM
+granddaughter/MS
+grandee/SM
+grandeur/MS
+grandfather/MYDSG
+grandiloquence/SM
+grandiloquent/Y
+grandiose/YP
+grandiosity/MS
+grandkid/SM
+grandma/MS
+grandmaster/MS
+grandmother/MYS
+grandnephew/MS
+grandness/MS
+grandniece/SM
+grandpa/MS
+grandparent/MS
+grandson/MS
+grandstander/M
+grandstand/SRDMG
+grand/TPSYR
+granduncle/MS
+Grange/MR
+grange/MSR
+Granger/M
+granite/MS
+granitic
+Gran/M
+Grannie/M
+Granny/M
+granny/MS
+granola/S
+grantee/MS
+granter/M
+Grantham/M
+Granthem/M
+Grantley/M
+Grant/M
+grantor's
+grant/SGZMRD
+grantsmanship/S
+granularity/SM
+granular/Y
+granulate/SDXVGN
+granulation/M
+granule/SM
+granulocytic
+Granville/M
+grapefruit/SM
+grape/SDGM
+grapeshot/M
+grapevine/MS
+grapheme/M
+graph/GMD
+graphical/Y
+graphicness/M
+graphic/PS
+graphics/M
+graphite/SM
+graphologist/SM
+graphology/MS
+graphs
+grapnel/SM
+grapple/DRSG
+grappler/M
+grappling/M
+grasper/M
+graspingness/M
+grasping/PY
+grasp/SRDBG
+grass/GZSDM
+grasshopper/SM
+grassland/MS
+Grass/M
+grassroots
+grassy/RT
+Grata/M
+gratefuller
+gratefullest
+gratefulness/USM
+grateful/YPU
+grater/M
+grates/I
+grate/SRDJGZ
+Gratia/M
+Gratiana/M
+graticule/M
+gratification/M
+gratified/U
+gratifying/Y
+gratify/NDSXG
+grating/YM
+gratis
+gratitude/IMS
+gratuitousness/MS
+gratuitous/PY
+gratuity/SM
+gravamen/SM
+gravedigger/SM
+gravel/SGMYD
+graven
+graveness/MS
+graver/M
+graveside/S
+Graves/M
+grave/SRDPGMZTY
+gravestone/SM
+graveyard/MS
+gravidness/M
+gravid/PY
+gravimeter/SM
+gravimetric
+gravitas
+gravitate/XVGNSD
+gravitational/Y
+gravitation/M
+graviton/SM
+gravity/MS
+gravy/SM
+graybeard/MS
+Grayce/M
+grayish
+Gray/M
+grayness/S
+gray/PYRDGTS
+Grayson/M
+graze/GZSRD
+grazer/M
+Grazia/M
+grazing/M
+grease/GMZSRD
+greasepaint/MS
+greaseproof
+greaser/M
+greasily
+greasiness/SM
+greasy/PRT
+greatcoat/DMS
+greaten/DG
+greathearted
+greatness/MS
+great/SPTYRN
+grebe/MS
+Grecian/S
+Greece/M
+greed/C
+greedily
+greediness/SM
+greeds
+greed's
+greedy/RTP
+Greek/SM
+Greeley/M
+greenback/MS
+greenbelt/S
+Greenberg/M
+Greenblatt/M
+Greenbriar/M
+Greene/M
+greenery/MS
+Greenfeld/M
+greenfield
+Greenfield/M
+greenfly/M
+greengage/SM
+greengrocer/SM
+greengrocery/M
+greenhorn/SM
+greenhouse/SM
+greening/M
+greenish/P
+Greenland/M
+Green/M
+greenmail/GDS
+greenness/MS
+Greenpeace/M
+greenroom/SM
+Greensboro/M
+Greensleeves/M
+Greensville/M
+greensward/SM
+green/SYRDMPGT
+Greentree/M
+Greenville/M
+Greenwich/M
+greenwood/MS
+Greer/M
+greeter/M
+greeting/M
+greets/A
+greet/SRDJGZ
+gregariousness/MS
+gregarious/PY
+Gregg/M
+Greggory/M
+Greg/M
+Gregoire/M
+Gregoor/M
+Gregorian
+Gregorio/M
+Gregorius/M
+Gregor/M
+Gregory/M
+gremlin/SM
+Grenada/M
+grenade/MS
+Grenadian/S
+grenadier/SM
+Grenadines
+grenadine/SM
+Grendel/M
+Grenier/M
+Grenoble/M
+Grenville/M
+Gresham/M
+Gretal/M
+Greta/M
+Gretchen/M
+Gretel/M
+Grete/M
+Grethel/M
+Gretna/M
+Gretta/M
+Gretzky/M
+grew/A
+greybeard/M
+greyhound/MS
+Grey/M
+greyness/M
+gridded
+griddlecake/SM
+griddle/DSGM
+gridiron/GSMD
+gridlock/DSG
+grids/A
+grid/SGM
+grief/MS
+Grieg/M
+Grier/M
+grievance/SM
+griever/M
+grieve/SRDGZ
+grieving/Y
+grievousness/SM
+grievous/PY
+Griffie/M
+Griffin/M
+griffin/SM
+Griffith/M
+Griff/M
+griffon's
+Griffy/M
+griller/M
+grille/SM
+grill/RDGS
+grillwork/M
+grimace/DRSGM
+grimacer/M
+Grimaldi/M
+grime/MS
+Grimes
+griminess/MS
+grimmer
+grimmest
+Grimm/M
+grimness/MS
+grim/PGYD
+grimy/TPR
+Grinch/M
+grind/ASG
+grinder/MS
+grinding/SY
+grindstone/SM
+gringo/SM
+grinned
+grinner/M
+grinning/Y
+grin/S
+griper/M
+gripe/S
+grippe/GMZSRD
+gripper/M
+gripping/Y
+grip/SGZMRD
+Griselda/M
+grisliness/SM
+grisly/RPT
+Gris/M
+Grissel/M
+gristle/SM
+gristliness/M
+gristly/TRP
+gristmill/MS
+grist/MYS
+Griswold/M
+grit/MS
+gritted
+gritter/MS
+grittiness/SM
+gritting
+gritty/PRT
+Griz/M
+grizzle/DSG
+grizzling/M
+grizzly/TRS
+Gr/M
+groaner/M
+groan/GZSRDM
+groat/SM
+grocer/MS
+grocery/MS
+groggily
+grogginess/SM
+groggy/RPT
+grog/MS
+groin/MGSD
+grokked
+grokking
+grok/S
+grommet/GMDS
+Gromyko/M
+groofs
+groomer/M
+groom/GZSMRD
+groomsman/M
+groomsmen
+Groot/M
+groover/M
+groove/SRDGM
+groovy/TR
+groper/M
+grope/SRDJGZ
+Gropius/M
+grosbeak/SM
+grosgrain/MS
+Gross
+Grosset/M
+gross/GTYSRDP
+Grossman/M
+grossness/MS
+Grosvenor/M
+Grosz/M
+grotesqueness/MS
+grotesque/PSY
+Grotius/M
+Groton/M
+grottoes
+grotto/M
+grouch/GDS
+grouchily
+grouchiness/MS
+grouchy/RPT
+groundbreaking/S
+grounded/U
+grounder/M
+groundhog/SM
+ground/JGZMDRS
+groundlessness/M
+groundless/YP
+groundnut/MS
+groundsheet/M
+groundskeepers
+groundsman/M
+groundswell/S
+groundwater/S
+groundwork/SM
+grouped/A
+grouper/M
+groupie/MS
+grouping/M
+groups/A
+group/ZJSMRDG
+grouse/GMZSRD
+grouser/M
+grouter/M
+grout/GSMRD
+groveler/M
+grovelike
+groveling/Y
+grovel/SDRGZ
+Grover/M
+Grove/RM
+grove/SRMZ
+grower/M
+grow/GZYRHS
+growing/I
+growingly
+growler/M
+growling/Y
+growl/RDGZS
+growly/RP
+grown/IA
+grownup/MS
+grows/A
+growth/IMA
+growths/IA
+grubbed
+grubber/SM
+grubbily
+grubbiness/SM
+grubbing
+grubby/RTP
+grub/MS
+grubstake/MSDG
+grudge/GMSRDJ
+grudger/M
+grudging/Y
+grueling/Y
+gruel/MDGJS
+gruesomeness/SM
+gruesome/RYTP
+gruffness/MS
+gruff/PSGTYRD
+grumble/GZJDSR
+grumbler/M
+grumbling/Y
+Grumman/M
+grumpily
+grumpiness/MS
+grump/MDGS
+grumpy/TPR
+Grundy/M
+Grünewald/M
+grunge/S
+grungy/RT
+grunion/SM
+grunter/M
+grunt/SGRD
+Grusky/M
+Grus/M
+Gruyère
+Gruyeres
+gryphon's
+g's
+G's
+gs/A
+GSA
+gt
+GU
+guacamole/MS
+Guadalajara/M
+Guadalcanal/M
+Guadalquivir/M
+Guadalupe/M
+Guadeloupe/M
+Guallatiri/M
+Gualterio/M
+Guamanian/SM
+Guam/M
+Guangzhou
+guanine/MS
+guano/MS
+Guantanamo/M
+Guarani/M
+guarani/SM
+guaranteeing
+guarantee/RSDZM
+guarantor/SM
+guaranty/MSDG
+guardedness/UM
+guarded/UYP
+guarder/M
+guardhouse/SM
+Guardia/M
+guardianship/MS
+guardian/SM
+guardrail/SM
+guard/RDSGZ
+guardroom/SM
+guardsman/M
+guardsmen
+Guarnieri/M
+Guatemala/M
+Guatemalan/S
+guava/SM
+Guayaquil/M
+gubernatorial
+Gucci/M
+gudgeon/M
+Guelph/M
+Guendolen/M
+Guenevere/M
+Guenna/M
+Guenther/M
+guernsey/S
+Guernsey/SM
+Guerra/M
+Guerrero/M
+guerrilla/MS
+guessable/U
+guess/BGZRSD
+guessed/U
+guesser/M
+guesstimate/DSMG
+guesswork/MS
+guest/SGMD
+Guevara/M
+guffaw/GSDM
+guff/SM
+Guggenheim/M
+Guglielma/M
+Guglielmo/M
+Guhleman/M
+GUI
+Guiana/M
+guidance/MS
+guidebook/SM
+guided/U
+guide/GZSRD
+guideline/SM
+guidepost/MS
+guider/M
+Guido/M
+Guilbert/M
+guilder/M
+guildhall/SM
+guild/SZMR
+guileful
+guilelessness/MS
+guileless/YP
+guile/SDGM
+Guillaume/M
+Guillema/M
+Guillemette/M
+guillemot/MS
+Guillermo/M
+guillotine/SDGM
+guiltily
+guiltiness/MS
+guiltlessness/M
+guiltless/YP
+guilt/SM
+guilty/PTR
+Gui/M
+Guinea/M
+Guinean/S
+guinea/SM
+Guinevere/M
+Guinna/M
+Guinness/M
+guise's
+guise/SDEG
+guitarist/SM
+guitar/SM
+Guiyang
+Guizot/M
+Gujarati/M
+Gujarat/M
+Gujranwala/M
+gulag/S
+gulch/MS
+gulden/MS
+gulf/DMGS
+Gullah/M
+gullet/MS
+gulley's
+gullibility/MS
+gullible
+Gulliver/M
+gull/MDSG
+gully/SDMG
+gulp/RDGZS
+gumboil/MS
+gumbo/MS
+gumboots
+gumdrop/SM
+gummed
+gumminess/M
+gumming/C
+gum/MS
+gummy/RTP
+gumption/SM
+gumshoeing
+gumshoe/SDM
+gumtree/MS
+Gunar/M
+gunboat/MS
+Gunderson/M
+gunfighter/M
+gunfight/SRMGZ
+gunfire/SM
+gunflint/M
+gunfought
+Gunilla/M
+gunk/SM
+gunky/RT
+Gun/M
+gunman/M
+gunmen
+gunmetal/MS
+gun/MS
+Gunnar/M
+gunned
+gunnel's
+Gunner/M
+gunner/SM
+gunnery/MS
+gunning/M
+gunnysack/SM
+gunny/SM
+gunpoint/MS
+gunpowder/SM
+gunrunner/MS
+gunrunning/MS
+gunship/S
+gunshot/SM
+gunslinger/M
+gunsling/GZR
+gunsmith/M
+gunsmiths
+Guntar/M
+Gunter/M
+Gunther/M
+gunwale/MS
+Guofeng/M
+guppy/SM
+Gupta/M
+gurgle/SDG
+Gurkha/M
+gurney/S
+guru/MS
+Gusella/M
+gusher/M
+gush/SRDGZ
+gushy/TR
+Gus/M
+Guss
+gusset/MDSG
+Gussie/M
+Gussi/M
+gussy/GSD
+Gussy/M
+Gustaf/M
+Gustafson/M
+Gusta/M
+gustatory
+Gustave/M
+Gustav/M
+Gustavo/M
+Gustavus/M
+gusted/E
+Gustie/M
+gustily
+Gusti/M
+gustiness/M
+gusting/E
+gust/MDGS
+gustoes
+gusto/M
+gusts/E
+Gusty/M
+gusty/RPT
+Gutenberg/M
+Guthrey/M
+Guthrie/M
+Guthry/M
+Gutierrez/M
+gutlessness/S
+gutless/P
+gutser/M
+gutsiness/M
+gut/SM
+guts/R
+gutsy/PTR
+gutted
+gutter/GSDM
+guttering/M
+guttersnipe/M
+gutting
+gutturalness/M
+guttural/SPY
+gutty/RSMT
+Guyana/M
+Guyanese
+Guy/M
+guy/MDRZGS
+Guzman/M
+guzzle/GZRSD
+guzzler/M
+g/VBX
+Gwalior/M
+Gwendolen/M
+Gwendoline/M
+Gwendolin/M
+Gwendolyn/M
+Gweneth/M
+Gwenette/M
+Gwen/M
+Gwenneth/M
+Gwennie/M
+Gwenni/M
+Gwenny/M
+Gwenora/M
+Gwenore/M
+Gwyneth/M
+Gwyn/M
+Gwynne/M
+gymkhana/SM
+gym/MS
+gymnasia's
+gymnasium/SM
+gymnastically
+gymnastic/S
+gymnastics/M
+gymnast/SM
+gymnosperm/SM
+gynecologic
+gynecological/MS
+gynecologist/SM
+gynecology/MS
+gypped
+gypper/S
+gypping
+gyp/S
+gypsite
+gypster/S
+gypsum/MS
+gypsy/SDMG
+Gypsy/SM
+gyrate/XNGSD
+gyration/M
+gyrator/MS
+gyrfalcon/SM
+gyrocompass/M
+gyro/MS
+gyroscope/SM
+gyroscopic
+gyve/GDS
+H
+Haag/M
+Haas/M
+Habakkuk/M
+habeas
+haberdasher/SM
+haberdashery/SM
+Haber/M
+Haberman/M
+Habib/M
+habiliment/SM
+habitability/MS
+habitableness/M
+habitable/P
+habitant/ISM
+habitation/MI
+habitations
+habitat/MS
+habit/IBDGS
+habit's
+habitualness/SM
+habitual/SYP
+habituate/SDNGX
+habituation/M
+habitué/MS
+hacienda/MS
+hacker/M
+Hackett/M
+hack/GZSDRBJ
+hackler/M
+hackle/RSDMG
+hackney/SMDG
+hacksaw/SDMG
+hackwork/S
+Hadamard/M
+Hadar/M
+Haddad/M
+haddock/MS
+hades
+Hades
+had/GD
+hadji's
+hadj's
+Hadlee/M
+Hadleigh/M
+Hadley/M
+Had/M
+hadn't
+Hadria/M
+Hadrian/M
+hadron/MS
+hadst
+haemoglobin's
+haemophilia's
+haemorrhage's
+Hafiz/M
+hafnium/MS
+haft/GSMD
+Hagan/M
+Hagar/M
+Hagen/M
+Hager/M
+Haggai/M
+haggardness/MS
+haggard/SYP
+hagged
+hagging
+haggish
+haggis/SM
+haggler/M
+haggle/RSDZG
+Hagiographa/M
+hagiographer/SM
+hagiography/MS
+hag/SMN
+Hagstrom/M
+Hague/M
+ha/H
+hahnium/S
+Hahn/M
+Haifa/M
+haiku/M
+Hailee/M
+hailer/M
+Hailey/M
+hail/SGMDR
+hailstone/SM
+hailstorm/SM
+Haily/M
+Haiphong/M
+hairball/SM
+hairbreadth/M
+hairbreadths
+hairbrush/SM
+haircare
+haircloth/M
+haircloths
+haircut/MS
+haircutting
+hairdo/SM
+hairdresser/SM
+hairdressing/SM
+hairdryer/S
+hairiness/MS
+hairlessness/M
+hairless/P
+hairlike
+hairline/SM
+hairnet/MS
+hairpiece/MS
+hairpin/MS
+hairsbreadth
+hairsbreadths
+hair/SDM
+hairsplitter/SM
+hairsplitting/MS
+hairspray
+hairspring/SM
+hairstyle/SMG
+hairstylist/S
+hairy/PTR
+Haitian/S
+Haiti/M
+hajjes
+hajji/MS
+hajj/M
+Hakeem/M
+hake/MS
+Hakim/M
+Hakka/M
+Hakluyt/M
+halalled
+halalling
+halal/S
+halberd/SM
+halcyon/S
+Haldane/M
+Haleakala/M
+Haleigh/M
+hale/ISRDG
+Hale/M
+haler/IM
+halest
+Halette/M
+Haley/M
+halfback/SM
+halfbreed
+halfheartedness/MS
+halfhearted/PY
+halfpence/S
+halfpenny/MS
+halfpennyworth
+half/PM
+halftime/S
+halftone/MS
+halfway
+halfword/MS
+halibut/SM
+halide/SM
+Halie/M
+Halifax/M
+Hali/M
+Halimeda/M
+halite/MS
+halitoses
+halitosis/M
+hallelujah
+hallelujahs
+Halley/M
+halliard's
+Hallie/M
+Halli/M
+Hallinan/M
+Hall/M
+Hallmark/M
+hallmark/SGMD
+hallo/GDS
+halloo's
+Halloween/MS
+hallowing
+hallows
+hallow/UD
+hall/SMR
+Hallsy/M
+hallucinate/VNGSDX
+hallucination/M
+hallucinatory
+hallucinogenic/S
+hallucinogen/SM
+hallway/SM
+Hally/M
+halocarbon
+halogenated
+halogen/SM
+halon
+halo/SDMG
+Halpern/M
+Halsey/M
+Hal/SMY
+Halsy/M
+halter/GDM
+halt/GZJSMDR
+halting/Y
+halve/GZDS
+halves/M
+halyard/MS
+Ha/M
+Hamal/M
+Haman/M
+hamburger/M
+Hamburg/MS
+hamburg/SZRM
+Hamel/M
+Hamey/M
+Hamhung/M
+Hamid/M
+Hamilcar/M
+Hamil/M
+Hamiltonian/MS
+Hamilton/M
+Hamish/M
+Hamitic/M
+Hamlen/M
+Hamlet/M
+hamlet/MS
+Hamlin/M
+Ham/M
+Hammad/M
+Hammarskjold/M
+hammed
+hammerer/M
+hammerhead/SM
+hammering/M
+hammerless
+hammerlock/MS
+Hammerstein/M
+hammertoe/SM
+hammer/ZGSRDM
+Hammett/M
+hamming
+hammock/MS
+Hammond/M
+Hammurabi/M
+hammy/RT
+Hamnet/M
+hampered/U
+hamper/GSD
+Hampshire/M
+Hampton/M
+ham/SM
+hamster/MS
+hamstring/MGS
+hamstrung
+Hamsun/M
+Hana/M
+Hanan/M
+Hancock/M
+handbagged
+handbagging
+handbag/MS
+handball/SM
+handbarrow/MS
+handbasin
+handbill/MS
+handbook/SM
+handbrake/M
+handcar/SM
+handcart/MS
+handclasp/MS
+handcraft/GMDS
+handcuff/GSD
+handcuffs/M
+handedness/M
+handed/PY
+Handel/M
+hander/S
+handful/SM
+handgun/SM
+handhold/M
+handicapped
+handicapper/SM
+handicapping
+handicap/SM
+handicraftsman/M
+handicraftsmen
+handicraft/SMR
+handily/U
+handiness/SM
+handiwork/MS
+handkerchief/MS
+handleable
+handlebar/SM
+handle/MZGRSD
+handler/M
+handless
+handling/M
+handmade
+handmaiden/M
+handmaid/NMSX
+handout/SM
+handover
+handpick/GDS
+handrail/SM
+hand's
+handsaw/SM
+handset/SM
+handshake/GMSR
+handshaker/M
+handshaking/M
+handsomely/U
+handsomeness/MS
+handsome/RPTY
+handspike/SM
+handspring/SM
+handstand/MS
+hand/UDSG
+handwork/SM
+handwoven
+handwrite/GSJ
+handwriting/M
+handwritten
+Handy/M
+handyman/M
+handymen
+handy/URT
+Haney/M
+hangar/SGDM
+hangdog/S
+hanged/A
+hanger/M
+hang/GDRZBSJ
+hanging/M
+hangman/M
+hangmen
+hangnail/MS
+hangout/MS
+hangover/SM
+hangs/A
+Hangul/M
+hangup/S
+Hangzhou
+Hankel/M
+hankerer/M
+hanker/GRDJ
+hankering/M
+hank/GZDRMS
+hankie/SM
+Hank/M
+hanky's
+Hannah/M
+Hanna/M
+Hannibal/M
+Hannie/M
+Hanni/MS
+Hanny/M
+Hanoi/M
+Hanoverian
+Hanover/M
+Hansel/M
+Hansen/M
+Hansiain/M
+Han/SM
+Hans/N
+hansom/MS
+Hanson/M
+Hanuka/S
+Hanukkah/M
+Hanukkahs
+Hapgood/M
+haphazardness/SM
+haphazard/SPY
+haplessness/MS
+hapless/YP
+haploid/S
+happed
+happening/M
+happen/JDGS
+happenstance/SM
+happily/U
+happiness/UMS
+happing
+Happy/M
+happy/UTPR
+Hapsburg/M
+hap/SMY
+Harald/M
+harangue/GDRS
+haranguer/M
+Harare
+harasser/M
+harass/LSRDZG
+harassment/SM
+Harbert/M
+harbinger/DMSG
+Harbin/M
+harborer/M
+harbor/ZGRDMS
+Harcourt/M
+hardback/SM
+hardball/SM
+hardboard/SM
+hardboiled
+hardbound
+hardcore/MS
+hardcover/SM
+hardened/U
+hardener/M
+hardening/M
+harden/ZGRD
+hardhat/S
+hardheadedness/SM
+hardheaded/YP
+hardheartedness/SM
+hardhearted/YP
+hardihood/MS
+hardily
+hardiness/SM
+Harding/M
+Hardin/M
+hardliner/S
+hardness/MS
+hardscrabble
+hardshell
+hardship/MS
+hardstand/S
+hardtack/MS
+hardtop/MS
+hardware/SM
+hardwire/DSG
+hardwood/MS
+hardworking
+Hardy/M
+hard/YNRPJGXTS
+hardy/PTRS
+harebell/MS
+harebrained
+harelip/MS
+harelipped
+hare/MGDS
+harem/SM
+Hargreaves/M
+hark/GDS
+Harland/M
+Harlan/M
+Harlem/M
+Harlene/M
+Harlen/M
+Harlequin
+harlequin/MS
+Harley/M
+Harlie/M
+Harli/M
+Harlin/M
+harlotry/MS
+harlot/SM
+Harlow/M
+Harman/M
+harmed/U
+harmer/M
+harmfulness/MS
+harmful/PY
+harmlessness/SM
+harmless/YP
+harm/MDRGS
+Harmonia/M
+harmonically
+harmonica/MS
+harmonic/S
+harmonics/M
+Harmonie/M
+harmonious/IPY
+harmoniousness/MS
+harmoniousness's/I
+harmonium/MS
+harmonization/A
+harmonizations
+harmonization's
+harmonized/U
+harmonizer/M
+harmonizes/UA
+harmonize/ZGSRD
+Harmon/M
+harmony/EMS
+Harmony/M
+harness/DRSMG
+harnessed/U
+harnesser/M
+harnesses/U
+Harold/M
+Haroun/M
+harper/M
+Harper/M
+harping/M
+harpist/SM
+harp/MDRJGZS
+Harp/MR
+harpooner/M
+harpoon/SZGDRM
+harpsichordist/MS
+harpsichord/SM
+harpy/SM
+Harpy/SM
+Harrell/M
+harridan/SM
+Harrie/M
+harrier/M
+Harriet/M
+Harrietta/M
+Harriette/M
+Harriett/M
+Harrington/M
+Harriot/M
+Harriott/M
+Harrisburg/M
+Harri/SM
+Harrisonburg/M
+Harrison/M
+harrower/M
+harrow/RDMGS
+harrumph/SDG
+Harry/M
+harry/RSDGZ
+harshen/GD
+harshness/SM
+harsh/TRNYP
+Harte/M
+Hartford/M
+Hartley/M
+Hartline/M
+Hart/M
+Hartman/M
+hart/MS
+Hartwell/M
+Harvard/M
+harvested/U
+harvester/M
+harvestman/M
+harvest/MDRZGS
+Harvey/MS
+Harv/M
+Harwell/M
+Harwilll/M
+has
+Hasbro/M
+hash/AGSD
+Hasheem/M
+hasher/M
+Hashim/M
+hashing/M
+hashish/MS
+hash's
+Hasidim
+Haskell/M
+Haskel/M
+Haskins/M
+Haslett/M
+hasn't
+hasp/GMDS
+hassle/MGRSD
+hassock/MS
+haste/MS
+hastener/M
+hasten/GRD
+hast/GXJDN
+Hastie/M
+hastily
+hastiness/MS
+Hastings/M
+Hasty/M
+hasty/RPT
+hatchback/SM
+hatcheck/S
+hatched/U
+hatcher/M
+hatchery/MS
+hatchet/MDSG
+hatching/M
+hatch/RSDJG
+Hatchure/M
+hatchway/MS
+hatefulness/MS
+hateful/YP
+hater/M
+hate/S
+Hatfield/M
+Hathaway/M
+hatless
+hat/MDRSZG
+hatred/SM
+hatstands
+hatted
+Hatteras/M
+hatter/SM
+Hattie/M
+Hatti/M
+hatting
+Hatty/M
+hauberk/SM
+Haugen/M
+haughtily
+haughtiness/SM
+haughty/TPR
+haulage/MS
+hauler/M
+haul/SDRGZ
+haunch/GMSD
+haunter/M
+haunting/Y
+haunt/JRDSZG
+Hauptmann/M
+Hausa/M
+Hausdorff/M
+Hauser/M
+hauteur/MS
+Havana/SM
+Havarti
+Havel/M
+haven/DMGS
+Haven/M
+haven't
+haver/G
+haversack/SM
+have/ZGSR
+havocked
+havocking
+havoc/SM
+Haw
+Hawaiian/S
+Hawaii/M
+hawker/M
+hawk/GZSDRM
+Hawking
+hawking/M
+Hawkins/M
+hawkishness/S
+hawkish/P
+Hawley/M
+haw/MDSG
+hawser/M
+haws/RZ
+Hawthorne/M
+hawthorn/MS
+haycock/SM
+Hayden/M
+Haydn/M
+Haydon/M
+Hayes
+hayfield/MS
+hay/GSMDR
+Hayley/M
+hayloft/MS
+haymow/MS
+Haynes
+hayrick/MS
+hayride/MS
+hayseed/MS
+Hay/SM
+haystack/SM
+haywain
+Hayward/M
+haywire/MS
+Haywood/M
+Hayyim/M
+hazard/MDGS
+hazardousness/M
+hazardous/PY
+haze/DSRJMZG
+Hazel/M
+hazel/MS
+hazelnut/SM
+Haze/M
+hazer/M
+hazily
+haziness/MS
+hazing/M
+Hazlett/M
+Hazlitt/M
+hazy/PTR
+HBO/M
+hdqrs
+HDTV
+headache/MS
+headband/SM
+headboard/MS
+headcount
+headdress/MS
+header/M
+headfirst
+headgear/SM
+headhunter/M
+headhunting/M
+headhunt/ZGSRDMJ
+headily
+headiness/S
+heading/M
+headlamp/S
+headland/MS
+headlessness/M
+headless/P
+headlight/MS
+headline/DRSZMG
+headliner/M
+headlock/MS
+headlong
+Head/M
+headman/M
+headmaster/MS
+headmastership/M
+headmen
+headmistress/MS
+headphone/SM
+headpiece/SM
+headpin/MS
+headquarter/GDS
+headrest/MS
+headroom/SM
+headscarf/M
+headset/SM
+headship/SM
+headshrinker/MS
+head/SJGZMDR
+headsman/M
+headsmen
+headstall/SM
+headstand/MS
+headstock/M
+headstone/MS
+headstrong
+headwaiter/SM
+headwall/S
+headwater/S
+headway/MS
+headwind/SM
+headword/MS
+heady/PTR
+heal/DRHSGZ
+healed/U
+healer/M
+Heall/M
+healthfully
+healthfulness/SM
+healthful/U
+healthily/U
+healthiness/MSU
+health/M
+healths
+healthy/URPT
+heap/SMDG
+heard/UA
+hearer/M
+hearing/AM
+hearken/SGD
+hearsay/SM
+hearse/M
+hears/SDAG
+Hearst/M
+heartache/SM
+heartbeat/MS
+heartbreak/GMS
+heartbreaking/Y
+heartbroke
+heartbroken
+heartburning/M
+heartburn/SGM
+hearted/Y
+hearten/EGDS
+heartening/EY
+heartfelt
+hearth/M
+hearthrug
+hearths
+hearthstone/MS
+heartily
+heartiness/SM
+heartland/SM
+heartlessness/SM
+heartless/YP
+heartrending/Y
+heartsickness/MS
+heartsick/P
+heart/SMDNXG
+heartstrings
+heartthrob/MS
+heartwarming
+Heartwood/M
+heartwood/SM
+hearty/TRSP
+hear/ZTSRHJG
+heatedly
+heated/UA
+heater/M
+heathendom/SM
+heathenish/Y
+heathenism/MS
+heathen/M
+heather/M
+Heather/M
+heathery
+Heathkit/M
+heathland
+Heathman/M
+Heath/MR
+heath/MRNZX
+heaths
+heatproof
+heats/A
+heat/SMDRGZBJ
+heatstroke/MS
+heatwave
+heave/DSRGZ
+heavenliness/M
+heavenly/PTR
+heaven/SYM
+heavenward/S
+heaver/M
+heaves/M
+heavily
+heaviness/MS
+Heaviside/M
+heavyhearted
+heavyset
+heavy/TPRS
+heavyweight/SM
+Hebe/M
+hebephrenic
+Hebert/M
+Heb/M
+Hebraic
+Hebraism/MS
+Hebrew/SM
+Hebrides/M
+Hecate/M
+hecatomb/M
+heckler/M
+heckle/RSDZG
+heck/S
+hectare/MS
+hectically
+hectic/S
+hectogram/MS
+hectometer/SM
+Hector/M
+hector/SGD
+Hecuba/M
+he'd
+Heda/M
+Hedda/M
+Heddie/M
+Heddi/M
+hedge/DSRGMZ
+hedgehog/MS
+hedgehopped
+hedgehopping
+hedgehop/S
+hedger/M
+hedgerow/SM
+hedging/Y
+Hedi/M
+hedonism/SM
+hedonistic
+hedonist/MS
+Hedvige/M
+Hedvig/M
+Hedwiga/M
+Hedwig/M
+Hedy/M
+heeded/U
+heedfulness/M
+heedful/PY
+heeding/U
+heedlessness/SM
+heedless/YP
+heed/SMGD
+heehaw/DGS
+heeler/M
+heeling/M
+heelless
+heel/SGZMDR
+Heep/M
+Hefner/M
+heft/GSD
+heftily
+heftiness/SM
+hefty/TRP
+Hegelian
+Hegel/M
+hegemonic
+hegemony/MS
+Hegira/M
+hegira/S
+Heida/M
+Heidegger/M
+Heidelberg/M
+Heidie/M
+Heidi/M
+heifer/MS
+Heifetz/M
+heighten/GD
+height/SMNX
+Heimlich/M
+Heindrick/M
+Heineken/M
+Heine/M
+Heinlein/M
+heinousness/SM
+heinous/PY
+Heinrich/M
+Heinrick/M
+Heinrik/M
+Heinze/M
+Heinz/M
+heiress/MS
+heirloom/MS
+heir/SDMG
+Heisenberg/M
+Heiser/M
+heister/M
+heist/GSMRD
+Hejira's
+Helaina/M
+Helaine/M
+held
+Helena/M
+Helene/M
+Helenka/M
+Helen/M
+Helga/M
+Helge/M
+helical/Y
+helices/M
+helicon/M
+Helicon/M
+helicopter/GSMD
+heliocentric
+heliography/M
+Heliopolis/M
+Helios/M
+heliosphere
+heliotrope/SM
+heliport/MS
+helium/MS
+helix/M
+he'll
+hellbender/M
+hellbent
+hellcat/SM
+hellebore/SM
+Hellene/SM
+Hellenic
+Hellenism/MS
+Hellenistic
+Hellenist/MS
+Hellenization/M
+Hellenize
+heller/M
+Heller/M
+Hellespont/M
+hellfire/M
+hell/GSMDR
+hellhole/SM
+Helli/M
+hellion/SM
+hellishness/SM
+hellish/PY
+Hellman/M
+hello/GMS
+Hell's
+helluva
+helmed
+helmet/GSMD
+Helmholtz/M
+helming
+helms
+helm's
+helmsman/M
+helmsmen
+helm/U
+Helmut/M
+Héloise/M
+helot/S
+helper/M
+helpfulness/MS
+helpful/UY
+help/GZSJDR
+helping/M
+helplessness/SM
+helpless/YP
+helpline/S
+helpmate/SM
+helpmeet's
+Helsa/M
+Helsinki/M
+helve/GMDS
+Helvetian/S
+Helvetius/M
+Helyn/M
+He/M
+hematite/MS
+hematologic
+hematological
+hematologist/SM
+hematology/MS
+heme/MS
+Hemingway/M
+hemisphere/MSD
+hemispheric
+hemispherical
+hemline/SM
+hemlock/MS
+hemmed
+hemmer/SM
+hemming
+hem/MS
+hemoglobin/MS
+hemolytic
+hemophiliac/SM
+hemophilia/SM
+hemorrhage/GMDS
+hemorrhagic
+hemorrhoid/MS
+hemostat/SM
+hemp/MNS
+h/EMS
+hemstitch/DSMG
+henceforth
+henceforward
+hence/S
+Hench/M
+henchman/M
+henchmen
+Henderson/M
+Hendrick/SM
+Hendrickson/M
+Hendrika/M
+Hendrik/M
+Hendrix/M
+henge/M
+Henka/M
+Henley/M
+hen/MS
+henna/MDSG
+Hennessey/M
+henning
+henpeck/GSD
+Henrie/M
+Henrieta/M
+Henrietta/M
+Henriette/M
+Henrik/M
+Henri/M
+Henryetta/M
+henry/M
+Henry/M
+Hensley/M
+Henson/M
+heparin/MS
+hepatic/S
+hepatitides
+hepatitis/M
+Hepburn/M
+Hephaestus/M
+Hephzibah/M
+hepper
+heppest
+Hepplewhite
+hep/S
+heptagonal
+heptagon/SM
+heptane/M
+heptathlon/S
+her
+Heracles/M
+Heraclitus/M
+heralded/U
+heraldic
+herald/MDSG
+heraldry/MS
+Hera/M
+herbaceous
+herbage/MS
+herbalism
+herbalist/MS
+herbal/S
+Herbart/M
+Herbert/M
+herbicidal
+herbicide/MS
+Herbie/M
+herbivore/SM
+herbivorous/Y
+Herb/M
+herb/MS
+Herby/M
+Herc/M
+Herculaneum/M
+herculean
+Herculean
+Hercule/MS
+Herculie/M
+herder/M
+Herder/M
+herd/MDRGZS
+herdsman/M
+herdsmen
+hereabout/S
+hereafter/S
+hereby
+hereditary
+heredity/MS
+Hereford/SM
+herein
+hereinafter
+here/IS
+hereof
+hereon
+here's
+heres/M
+heresy/SM
+heretical
+heretic/SM
+hereto
+heretofore
+hereunder
+hereunto
+hereupon
+herewith
+Heriberto/M
+heritable
+heritage/MS
+heritor/IM
+Herkimer/M
+Herman/M
+Hermann/M
+hermaphrodite/SM
+hermaphroditic
+Hermaphroditus/M
+hermeneutic/S
+hermeneutics/M
+Hermes
+hermetical/Y
+hermetic/S
+Hermia/M
+Hermie/M
+Hermina/M
+Hermine/M
+Herminia/M
+Hermione/M
+hermitage/SM
+Hermite/M
+hermitian
+hermit/MS
+Hermon/M
+Hermosa/M
+Hermosillo/M
+Hermy/M
+Hernandez/M
+Hernando/M
+hernial
+hernia/MS
+herniate/NGXDS
+Herod/M
+Herodotus/M
+heroes
+heroically
+heroics
+heroic/U
+heroine/SM
+heroin/MS
+heroism/SM
+Herold/M
+hero/M
+heron/SM
+herpes/M
+herpetologist/SM
+herpetology/MS
+Herrera/M
+Herrick/M
+herringbone/SDGM
+Herring/M
+herring/SM
+Herrington/M
+Herr/MG
+Herschel/M
+Hersch/M
+herself
+Hersey/M
+Hershel/M
+Hershey/M
+Hersh/M
+Herta/M
+Hertha/M
+hertz/M
+Hertz/M
+Hertzog/M
+Hertzsprung/M
+Herve/M
+Hervey/M
+Herzegovina/M
+Herzl/M
+hes
+Hesiod/M
+hesitance/S
+hesitancy/SM
+hesitantly
+hesitant/U
+hesitater/M
+hesitate/XDRSNG
+hesitating/UY
+hesitation/M
+Hesperus/M
+Hesse/M
+Hessian/MS
+Hess/M
+Hester/M
+Hesther/M
+Hestia/M
+Heston/M
+heterodox
+heterodoxy/MS
+heterodyne
+heterogamous
+heterogamy/M
+heterogeneity/SM
+heterogeneousness/M
+heterogeneous/PY
+heterosexuality/SM
+heterosexual/YMS
+heterostructure
+heterozygous
+Hettie/M
+Hetti/M
+Hetty/M
+Heublein/M
+heuristically
+heuristic/SM
+Heusen/M
+Heuser/M
+he/VMZ
+hew/DRZGS
+Hewe/M
+hewer/M
+Hewet/M
+Hewett/M
+Hewie/M
+Hewitt/M
+Hewlett/M
+Hew/M
+hexachloride/M
+hexadecimal/YS
+hexafluoride/M
+hexagonal/Y
+hexagon/SM
+hexagram/SM
+hexameter/SM
+hex/DSRG
+hexer/M
+hey
+heyday/MS
+Heyerdahl/M
+Heywood/M
+Hezekiah/M
+hf
+HF
+Hf/M
+Hg/M
+hgt
+hgwy
+HHS
+HI
+Hialeah/M
+hiatus/SM
+Hiawatha/M
+hibachi/MS
+hibernate/XGNSD
+hibernation/M
+hibernator/SM
+Hibernia/M
+Hibernian/S
+hibiscus/MS
+hiccup/MDGS
+hickey/SM
+Hickey/SM
+Hickman/M
+Hickok/M
+hickory/MS
+hick/SM
+Hicks/M
+hi/D
+hidden/U
+hideaway/SM
+hidebound
+hideousness/SM
+hideous/YP
+hideout/MS
+hider/M
+hide/S
+hiding/M
+hid/ZDRGJ
+hieing
+hierarchal
+hierarchic
+hierarchical/Y
+hierarchy/SM
+hieratic
+hieroglyph
+hieroglyphic/S
+hieroglyphics/M
+hieroglyphs
+Hieronymus/M
+hie/S
+hifalutin
+Higashiosaka
+Higgins/M
+highball/GSDM
+highborn
+highboy/MS
+highbrow/SM
+highchair/SM
+highfalutin
+Highfield/M
+highhandedness/SM
+highhanded/PY
+highish
+Highlander/SM
+Highlands
+highland/ZSRM
+highlight/GZRDMS
+Highness/M
+highness/MS
+highpoint
+high/PYRT
+highroad/MS
+highs
+hight
+hightail/DGS
+highwayman/M
+highwaymen
+highway/MS
+hijacker/M
+hijack/JZRDGS
+hiker/M
+hike/ZGDSR
+Hilario/M
+hilariousness/MS
+hilarious/YP
+hilarity/MS
+Hilarius/M
+Hilary/M
+Hilbert/M
+Hildagarde/M
+Hildagard/M
+Hilda/M
+Hildebrand/M
+Hildegaard/M
+Hildegarde/M
+Hilde/M
+Hildy/M
+Hillard/M
+Hillary/M
+hillbilly/MS
+Hillcrest/M
+Hillel/M
+hiller/M
+Hillery/M
+hill/GSMDR
+Hilliard/M
+Hilliary/M
+Hillie/M
+Hillier/M
+hilliness/SM
+Hill/M
+hillman
+hillmen
+hillock/SM
+Hillsboro/M
+Hillsdale/M
+hillside/SM
+hilltop/MS
+hillwalking
+Hillyer/M
+Hilly/RM
+hilly/TRP
+hilt/MDGS
+Hilton/M
+Hi/M
+Himalaya/MS
+Himalayan/S
+Himmler/M
+him/S
+himself
+Hinayana/M
+Hinda/M
+Hindemith/M
+Hindenburg/M
+hindered/U
+hinderer/M
+hinder/GRD
+Hindi/M
+hindmost
+hindquarter/SM
+hindrance/SM
+hind/RSZ
+hindsight/SM
+Hinduism/SM
+Hindu/MS
+Hindustani/MS
+Hindustan/M
+Hines/M
+hinger
+hinge's
+hinge/UDSG
+Hinkle/M
+Hinsdale/M
+hinterland/MS
+hinter/M
+hint/GZMDRS
+Hinton/M
+Hinze/M
+hipbone/SM
+hipness/S
+Hipparchus/M
+hipped
+hipper
+hippest
+hippie/MTRS
+hipping/M
+Hippocrates/M
+Hippocratic
+hippodrome/MS
+hippo/MS
+hippopotamus/SM
+hip/PSM
+hippy's
+hipster/MS
+Hiram/M
+hire/AGSD
+hireling/SM
+hirer/SM
+Hirey/M
+hiring/S
+Hirohito/M
+Hiroshi/M
+Hiroshima/M
+Hirsch/M
+hirsuteness/MS
+hirsute/P
+his
+Hispanic/SM
+Hispaniola/M
+hiss/DSRMJG
+hisser/M
+hissing/M
+Hiss/M
+histamine/SM
+histochemic
+histochemical
+histochemistry/M
+histogram/MS
+histological
+histologist/MS
+histology/SM
+historian/MS
+historic
+historicalness/M
+historical/PY
+historicism/M
+historicist/M
+historicity/MS
+historiographer/SM
+historiography/MS
+history/MS
+histrionically
+histrionic/S
+histrionics/M
+hist/SDG
+Hitachi/M
+Hitchcock/M
+hitcher/MS
+hitchhike/RSDGZ
+hitch/UGSD
+hither
+hitherto
+Hitler/SM
+hitless
+hit/MS
+hittable
+hitter/SM
+hitting
+Hittite/SM
+HIV
+hive/MGDS
+h'm
+HM
+HMO
+Hmong
+HMS
+hoarder/M
+hoarding/M
+hoard/RDJZSGM
+hoarfrost/SM
+hoariness/MS
+hoar/M
+hoarseness/SM
+hoarse/RTYP
+hoary/TPR
+hoaxer/M
+hoax/GZMDSR
+Hobard/M
+Hobart/M
+hobbed
+Hobbes/M
+hobbing
+hobbit
+hobbler/M
+hobble/ZSRDG
+Hobbs/M
+hobbyhorse/SM
+hobbyist/SM
+hobby/SM
+Hobday/M
+Hobey/M
+hobgoblin/MS
+Hobie/M
+hobnail/GDMS
+hobnobbed
+hobnobbing
+hobnob/S
+Hoboken/M
+hobo/SDMG
+hob/SM
+hoc
+hocker/M
+hockey/SM
+hock/GDRMS
+Hockney/M
+hockshop/SM
+hodge/MS
+Hodge/MS
+hodgepodge/SM
+Hodgkin/M
+ho/DRYZ
+hod/SM
+Hoebart/M
+hoecake/SM
+hoedown/MS
+hoeing
+hoer/M
+hoe/SM
+Hoffa/M
+Hoff/M
+Hoffman/M
+Hofstadter/M
+Hogan/M
+hogan/SM
+Hogarth/M
+hogback/MS
+hogged
+hogger
+hogging
+hoggish/Y
+hogshead/SM
+hog/SM
+hogtie/SD
+hogtying
+hogwash/SM
+Hohenlohe/M
+Hohenstaufen/M
+Hohenzollern/M
+Hohhot/M
+hoister/M
+hoist/GRDS
+hoke/DSG
+hokey/PRT
+hokier
+hokiest
+Hokkaido/M
+hokum/MS
+Hokusai/M
+Holbein/M
+Holbrook/M
+Holcomb/M
+holdall/MS
+Holden/M
+holder/M
+Holder/M
+holding/IS
+holding's
+hold/NRBSJGZ
+holdout/SM
+holdover/SM
+holdup/MS
+hole/MGDS
+holey
+holiday/GRDMS
+Holiday/M
+holidaymaker/S
+holier/U
+Holiness/MS
+holiness/MSU
+holistic
+holistically
+hollandaise
+Hollandaise/M
+Hollander/M
+Holland/RMSZ
+holler/GDS
+Hollerith/M
+Holley/M
+Hollie/M
+Holli/SM
+Hollister/M
+Holloway/M
+hollowness/MS
+hollow/RDYTGSP
+hollowware/M
+Hollyanne/M
+hollyhock/MS
+Holly/M
+holly/SM
+Hollywood/M
+Holman/M
+Holmes
+holmium/MS
+Holm/M
+Holocaust
+holocaust/MS
+Holocene
+hologram/SM
+holograph/GMD
+holographic
+holographs
+holography/MS
+Holstein/MS
+holster/MDSG
+Holst/M
+Holt/M
+Holyoke/M
+holy/SRTP
+holystone/MS
+Holzman/M
+Ho/M
+homage/MGSRD
+homager/M
+hombre/SM
+homburg/SM
+homebody/MS
+homebound
+homeboy/S
+homebuilder/S
+homebuilding
+homebuilt
+homecoming/MS
+home/DSRMYZG
+homegrown
+homeland/SM
+homelessness/SM
+homeless/P
+homelike
+homeliness/SM
+homely/RPT
+homemade
+homemake/JRZG
+homemaker/M
+homemaking/M
+homeomorphic
+homeomorphism/MS
+homeomorph/M
+homeopath
+homeopathic
+homeopaths
+homeopathy/MS
+homeostases
+homeostasis/M
+homeostatic
+homeowner/S
+homeownership
+homepage
+Homere/M
+homer/GDM
+Homeric
+homerists
+Homer/M
+homeroom/MS
+Homerus/M
+homeschooling/S
+homesickness/MS
+homesick/P
+homespun/S
+homesteader/M
+homestead/GZSRDM
+homestretch/SM
+hometown/SM
+homeward
+homeworker/M
+homework/ZSMR
+homeyness/MS
+homey/PS
+homicidal/Y
+homicide/SM
+homier
+homiest
+homiletic/S
+homily/SM
+hominess's
+homing/M
+hominid/MS
+hominy/SM
+Hom/MR
+homogamy/M
+homogenate/MS
+homogeneity/ISM
+homogeneous/PY
+homogenization/MS
+homogenize/DRSGZ
+homogenizer/M
+homograph/M
+homographs
+homological
+homologous
+homologue/M
+homology/MS
+homomorphic
+homomorphism/SM
+homonym/SM
+homophobia/S
+homophobic
+homophone/MS
+homopolymers
+homosexuality/SM
+homosexual/YMS
+homo/SM
+homotopy
+homozygous/Y
+honcho/DSG
+Honda/M
+Hondo/M
+Honduran/S
+Honduras/M
+Honecker/M
+hone/SM
+honestly/E
+honest/RYT
+honesty/ESM
+honeybee/SM
+honeycomb/SDMG
+honeydew/SM
+honey/GSMD
+honeylocust
+Honey/M
+honeymooner/M
+honeymoon/RDMGZS
+honeysuckle/MS
+Honeywell/M
+hong/M
+Honiara/M
+honker/M
+honk/GZSDRM
+honky/SM
+Hon/M
+hon/MDRSZTG
+Honolulu/M
+honorableness/SM
+honorable/PSM
+honorables/U
+honorablies/U
+honorably/UE
+honorarily
+honorarium/SM
+honorary/S
+honored/U
+honoree/S
+honor/ERDBZGS
+honorer/EM
+Honoria/M
+honorific/S
+Honor/M
+honor's
+honors/A
+Honshu/M
+hooch/MS
+hoodedness/M
+hooded/P
+hoodlum/SM
+Hood/M
+hood/MDSG
+hoodoo/DMGS
+hoodwinker/M
+hoodwink/SRDG
+hooey/SM
+hoof/DRMSG
+hoofer/M
+hoofmark/S
+hookah/M
+hookahs
+hookedness/M
+hooked/P
+Hooke/MR
+hooker/M
+Hooker/M
+hookey's
+hook/GZDRMS
+hooks/U
+hookup/SM
+hookworm/MS
+hooky/SRMT
+hooliganism/SM
+hooligan/SM
+hooper/M
+Hooper/M
+hoopla/SM
+hoop/MDRSG
+hooray/SMDG
+hoosegow/MS
+Hoosier/SM
+hootch's
+hootenanny/SM
+hooter/M
+hoot/MDRSGZ
+Hoover/MS
+hooves/M
+hoped/U
+hopefulness/MS
+hopeful/SPY
+hopelessness/SM
+hopeless/YP
+Hope/M
+hoper/M
+hope/SM
+Hopewell/M
+Hopi/SM
+Hopkinsian/M
+Hopkins/M
+hopped
+Hopper/M
+hopper/MS
+hopping/M
+hoppled
+hopples
+hopscotch/MDSG
+hop/SMDRG
+Horace/M
+Horacio/M
+Horatia/M
+Horatio/M
+Horatius/M
+horde/DSGM
+horehound/MS
+horizon/MS
+horizontal/YS
+Hormel/M
+hormonal/Y
+hormone/MS
+Hormuz/M
+hornbeam/M
+hornblende/MS
+Hornblower/M
+hornedness/M
+horned/P
+Horne/M
+hornet/MS
+horn/GDRMS
+horniness/M
+hornless
+hornlike
+Horn/M
+hornpipe/MS
+horny/TRP
+horologic
+horological
+horologist/MS
+horology/MS
+horoscope/MS
+Horowitz/M
+horrendous/Y
+horribleness/SM
+horrible/SP
+horribly
+horridness/M
+horrid/PY
+horrific
+horrifically
+horrify/DSG
+horrifying/Y
+horror/MS
+hors/DSGX
+horseback/MS
+horsedom
+horseflesh/M
+horsefly/MS
+horsehair/SM
+horsehide/SM
+horselaugh/M
+horselaughs
+horseless
+horselike
+horsely
+horseman/M
+horsemanship/MS
+horsemen
+horseplayer/M
+horseplay/SMR
+horsepower/SM
+horseradish/SM
+horse's
+horseshoeing
+horseshoe/MRSD
+horseshoer/M
+horsetail/SM
+horse/UGDS
+horsewhipped
+horsewhipping
+horsewhip/SM
+horsewoman/M
+horsewomen
+horsey
+horsier
+horsiest
+horsing/M
+Horst/M
+hortatory
+Horten/M
+Hortense/M
+Hortensia/M
+horticultural
+horticulture/SM
+horticulturist/SM
+Hort/MN
+Horton/M
+Horus/M
+hosanna/SDG
+Hosea/M
+hose/M
+hosepipe
+hos/GDS
+hosier/MS
+hosiery/SM
+hosp
+hospice/MS
+hospitable/I
+hospitably/I
+hospitality/MS
+hospitality's/I
+hospitalization/MS
+hospitalize/GSD
+hospital/MS
+hostage/MS
+hosteler/M
+hostelry/MS
+hostel/SZGMRD
+hostess/MDSG
+hostile/YS
+hostility/SM
+hostler/MS
+Host/MS
+host/MYDGS
+hotbed/MS
+hotblooded
+hotbox/MS
+hotcake/S
+hotchpotch/M
+hotelier/MS
+hotelman/M
+hotel/MS
+hotfoot/DGS
+hothead/DMS
+hotheadedness/SM
+hotheaded/PY
+hothouse/MGDS
+hotness/MS
+hotplate/SM
+hotpot/M
+hot/PSY
+hotrod
+hotshot/S
+hotted
+Hottentot/SM
+hotter
+hottest
+hotting
+Houdaille/M
+Houdini/M
+hough/M
+hounder/M
+hounding/M
+hound/MRDSG
+hourglass/MS
+houri/MS
+hourly/S
+hour/YMS
+house/ASDG
+houseboat/SM
+housebound
+houseboy/SM
+housebreaker/M
+housebreaking/M
+housebreak/JSRZG
+housebroke
+housebroken
+housebuilding
+housecleaning/M
+houseclean/JDSG
+housecoat/MS
+housefly/MS
+houseful/SM
+householder/M
+household/ZRMS
+househusband/S
+housekeeper/M
+housekeeping/M
+housekeep/JRGZ
+houselights
+House/M
+housemaid/MS
+houseman/M
+housemen
+housemother/MS
+housemoving
+houseparent/SM
+houseplant/S
+houser
+house's
+housetop/MS
+housewares
+housewarming/MS
+housewifeliness/M
+housewifely/P
+housewife/YM
+housewives
+houseworker/M
+housework/ZSMR
+housing/MS
+Housman/M
+Houston/M
+Houyhnhnm/M
+HOV
+hovel/GSMD
+hovercraft/M
+hoverer/M
+hover/GRD
+hove/ZR
+Howard/M
+howbeit
+howdah/M
+howdahs
+howdy/GSD
+Howell/MS
+Howe/M
+however
+Howey/M
+Howie/M
+howitzer/MS
+howler/M
+howl/GZSMDR
+Howrah/M
+how/SM
+howsoever
+hoyden/DMGS
+hoydenish
+Hoyle/SM
+hoy/M
+Hoyt/M
+hp
+HP
+HQ
+hr
+HR
+HRH
+Hrothgar/M
+hrs
+h's
+H's
+HS
+HST
+ht
+HTML
+Hts/M
+HTTP
+Huang/M
+huarache/SM
+hubba
+Hubbard/M
+Hubble/M
+hubbub/SM
+hubby/SM
+hubcap/SM
+Huber/M
+Hube/RM
+Hubert/M
+Huberto/M
+Hubey/M
+Hubie/M
+hub/MS
+hubris/SM
+huckleberry/SM
+Huck/M
+huckster/SGMD
+HUD
+Huddersfield/M
+huddler/M
+huddle/RSDMG
+Hudson/M
+hue/MDS
+Huerta/M
+Huey/M
+huffily
+huffiness/SM
+Huff/M
+Huffman/M
+huff/SGDM
+huffy/TRP
+hugeness/MS
+huge/YP
+hugged
+hugger
+hugging/S
+Huggins
+Hughie/M
+Hugh/MS
+Hugibert/M
+Hugo/M
+hug/RTS
+Huguenot/SM
+Hugues/M
+huh
+huhs
+Hui/M
+Huitzilopitchli/M
+hula/MDSG
+Hulda/M
+hulk/GDMS
+hullabaloo/SM
+huller/M
+hulling/M
+Hull/M
+hull/MDRGZS
+hullo/GSDM
+humane/IY
+humaneness/SM
+humaner
+humanest
+human/IPY
+humanism/SM
+humanistic
+humanist/SM
+humanitarianism/SM
+humanitarian/S
+humanity/ISM
+humanization/CSM
+humanized/C
+humanizer/M
+humanize/RSDZG
+humanizes/IAC
+humanizing/C
+humankind/M
+humannesses
+humanness/IM
+humanoid/S
+humans
+Humbert/M
+Humberto/M
+humbleness/SM
+humble/TZGPRSDJ
+humbly
+Humboldt/M
+humbugged
+humbugging
+humbug/MS
+humdinger/MS
+humdrum/S
+Hume/M
+humeral/S
+humeri
+humerus/M
+Humfrey/M
+Humfrid/M
+Humfried/M
+humidification/MC
+humidifier/CM
+humidify/RSDCXGNZ
+humidistat/M
+humidity/MS
+humidor/MS
+humid/Y
+humiliate/SDXNG
+humiliating/Y
+humiliation/M
+humility/MS
+hummed
+Hummel/M
+hummer/SM
+humming
+hummingbird/SM
+hummock/MDSG
+hummocky
+hummus/S
+humongous
+humored/U
+humorist/MS
+humorlessness/MS
+humorless/PY
+humorousness/MS
+humorous/YP
+humor/RDMZGS
+humpback/SMD
+hump/GSMD
+humph/DG
+Humphrey/SM
+humphs
+Humpty/M
+hum/S
+humus/SM
+Humvee
+hunchback/DSM
+hunch/GMSD
+hundredfold/S
+hundred/SHRM
+hundredths
+hundredweight/SM
+Hunfredo/M
+hung/A
+Hungarian/MS
+Hungary/M
+hunger/SDMG
+Hung/M
+hungover
+hungrily
+hungriness/SM
+hungry/RTP
+hunker/DG
+hunky/RST
+hunk/ZRMS
+Hun/MS
+hunter/M
+Hunter/M
+hunt/GZJDRS
+hunting/M
+Huntington/M
+Huntlee/M
+Huntley/M
+Hunt/MR
+huntress/MS
+huntsman/M
+huntsmen
+Huntsville/M
+hurdle/JMZGRSD
+hurdler/M
+hurl/DRGZJS
+Hurlee/M
+Hurleigh/M
+hurler/M
+Hurley/M
+hurling/M
+Huron/SM
+hurray/SDG
+hurricane/MS
+hurriedness/M
+hurried/UY
+hurry/RSDG
+Hurst/M
+hurter/M
+hurtfulness/MS
+hurtful/PY
+hurting/Y
+hurtle/SDG
+hurts
+hurt/U
+Hurwitz/M
+Hus
+Husain's
+husbander/M
+husband/GSDRYM
+husbandman/M
+husbandmen
+husbandry/SM
+Husein/M
+hush/DSG
+husker/M
+huskily
+huskiness/MS
+husking/M
+husk/SGZDRM
+husky/RSPT
+hussar/MS
+Hussein/M
+Husserl/M
+hussy/SM
+hustings/M
+hustler/M
+hustle/RSDZG
+Huston/M
+Hutchins/M
+Hutchinson/M
+Hutchison/M
+hutch/MSDG
+hut/MS
+hutted
+hutting
+Hutton/M
+Hutu/M
+Huxley/M
+Huygens/M
+huzzah/GD
+huzzahs
+hwy
+Hyacintha/M
+Hyacinthe/M
+Hyacinthia/M
+Hyacinthie/M
+hyacinth/M
+Hyacinth/M
+hyacinths
+Hyades
+hyaena's
+Hyannis/M
+Hyatt/M
+hybridism/SM
+hybridization/S
+hybridize/GSD
+hybrid/MS
+Hyde/M
+Hyderabad/M
+Hydra/M
+hydra/MS
+hydrangea/SM
+hydrant/SM
+hydrate/CSDNGX
+hydrate's
+hydration/MC
+hydraulically
+hydraulicked
+hydraulicking
+hydraulic/S
+hydraulics/M
+hydrazine/M
+hydride/MS
+hydrocarbon/SM
+hydrocephali
+hydrocephalus/MS
+hydrochemistry
+hydrochloric
+hydrochloride/M
+hydrodynamical
+hydrodynamic/S
+hydrodynamics/M
+hydroelectric
+hydroelectrically
+hydroelectricity/SM
+hydrofluoric
+hydrofoil/MS
+hydrogenate/CDSGN
+hydrogenate's
+hydrogenation/MC
+hydrogenations
+hydrogen/MS
+hydrogenous
+hydrological/Y
+hydrologist/MS
+hydrology/SM
+hydrolysis/M
+hydrolyzed/U
+hydrolyze/GSD
+hydromagnetic
+hydromechanics/M
+hydrometer/SM
+hydrometry/MS
+hydrophilic
+hydrophobia/SM
+hydrophobic
+hydrophone/SM
+hydroplane/DSGM
+hydroponic/S
+hydroponics/M
+hydro/SM
+hydrosphere/MS
+hydrostatic/S
+hydrostatics/M
+hydrotherapy/SM
+hydrothermal/Y
+hydrous
+hydroxide/MS
+hydroxy
+hydroxylate/N
+hydroxyl/SM
+hydroxyzine/M
+hyena/MS
+hygiene/MS
+hygienically
+hygienic/S
+hygienics/M
+hygienist/MS
+hygrometer/SM
+hygroscopic
+hying
+Hy/M
+Hyman/M
+hymeneal/S
+Hymen/M
+hymen/MS
+Hymie/M
+hymnal/SM
+hymnbook/S
+hymn/GSDM
+Hynda/M
+hype/MZGDSR
+hyperactive/S
+hyperactivity/SM
+hyperbola/MS
+hyperbole/MS
+hyperbolic
+hyperbolically
+hyperboloidal
+hyperboloid/SM
+hypercellularity
+hypercritical/Y
+hypercube/MS
+hyperemia/M
+hyperemic
+hyperfine
+hypergamous/Y
+hypergamy/M
+hyperglycemia/MS
+hyperinflation
+Hyperion/M
+hypermarket/SM
+hypermedia/S
+hyperplane/SM
+hyperplasia/M
+hypersensitiveness/MS
+hypersensitive/P
+hypersensitivity/MS
+hypersonic
+hyperspace/M
+hypersphere/M
+hypertension/MS
+hypertensive/S
+hypertext/SM
+hyperthyroid
+hyperthyroidism/MS
+hypertrophy/MSDG
+hypervelocity
+hyperventilate/XSDGN
+hyperventilation/M
+hyphenated/U
+hyphenate/NGXSD
+hyphenation/M
+hyphen/DMGS
+hypnoses
+hypnosis/M
+hypnotherapy/SM
+hypnotically
+hypnotic/S
+hypnotism/MS
+hypnotist/SM
+hypnotize/SDG
+hypoactive
+hypoallergenic
+hypocellularity
+hypochondriac/SM
+hypochondria/MS
+hypocrisy/SM
+hypocrite/MS
+hypocritical/Y
+hypodermic/S
+hypo/DMSG
+hypoglycemia/SM
+hypoglycemic/S
+hypophyseal
+hypophysectomized
+hypotenuse/MS
+hypothalami
+hypothalamic
+hypothalamically
+hypothalamus/M
+hypothermia/SM
+hypotheses
+hypothesis/M
+hypothesizer/M
+hypothesize/ZGRSD
+hypothetic
+hypothetical/Y
+hypothyroid
+hypothyroidism/SM
+hypoxia/M
+hyssop/MS
+hysterectomy/MS
+hysteresis/M
+hysteria/SM
+hysterical/YU
+hysteric/SM
+Hyundai/M
+Hz
+i
+I
+IA
+Iaccoca/M
+Iago/M
+Iain/M
+Ia/M
+iambi
+iambic/S
+iamb/MS
+iambus/SM
+Ian/M
+Ianthe/M
+Ibadan/M
+Ibbie/M
+Ibby/M
+Iberia/M
+Iberian/MS
+Ibero/M
+ibex/MS
+ibid
+ibidem
+ibis/SM
+IBM/M
+Ibo/M
+Ibrahim/M
+Ibsen/M
+ibuprofen/S
+Icarus/M
+ICBM/S
+ICC
+iceberg/SM
+iceboat/MS
+icebound
+icebox/MS
+icebreaker/SM
+icecap/SM
+ice/GDSC
+Icelander/M
+Icelandic
+Iceland/MRZ
+Ice/M
+iceman/M
+icemen
+icepack
+icepick/S
+ice's
+Ichabod/M
+ichneumon/M
+ichthyologist/MS
+ichthyology/MS
+icicle/SM
+icily
+iciness/SM
+icing/MS
+icky/RT
+iconic
+icon/MS
+iconoclasm/MS
+iconoclastic
+iconoclast/MS
+iconography/MS
+icosahedra
+icosahedral
+icosahedron/M
+ictus/SM
+ICU
+icy/RPT
+I'd
+ID
+Idahoan/S
+Idahoes
+Idaho/MS
+Idalia/M
+Idalina/M
+Idaline/M
+Ida/M
+idealism/MS
+idealistic
+idealistically
+idealist/MS
+idealization/MS
+idealized/U
+idealize/GDRSZ
+idealizer/M
+ideal/MYS
+idealogical
+idea/SM
+ideate/SN
+ideation/M
+Idelle/M
+Idell/M
+idem
+idempotent/S
+identicalness/M
+identical/YP
+identifiability
+identifiable/U
+identifiably
+identification/M
+identified/U
+identifier/M
+identify/XZNSRDG
+identity/SM
+ideogram/MS
+ideographic
+ideograph/M
+ideographs
+ideological/Y
+ideologist/SM
+ideologue/S
+ideology/SM
+ides
+Idette/M
+idiocy/MS
+idiolect/M
+idiomatically
+idiomatic/P
+idiom/MS
+idiopathic
+idiosyncrasy/SM
+idiosyncratic
+idiosyncratically
+idiotic
+idiotically
+idiot/MS
+idleness/MS
+idle/PZTGDSR
+idler/M
+id/MY
+idolater/MS
+idolatress/S
+idolatrous
+idolatry/SM
+idolization/SM
+idolized/U
+idolizer/M
+idolize/ZGDRS
+idol/MS
+ids
+IDs
+idyllic
+idyllically
+idyll/MS
+IE
+IEEE
+Ieyasu/M
+if
+iffiness/S
+iffy/TPR
+Ifni/M
+ifs
+Iggie/M
+Iggy/M
+igloo/MS
+Ignace/M
+Ignacio/M
+Ignacius/M
+Ignatius/M
+Ignazio/M
+Ignaz/M
+igneous
+ignitable
+ignite/ASDG
+igniter/M
+ignition/MS
+ignobleness/M
+ignoble/P
+ignobly
+ignominious/Y
+ignominy/MS
+ignoramus/SM
+ignorance/MS
+ignorantness/M
+ignorant/SPY
+ignorer/M
+ignore/SRDGB
+Igor/M
+iguana/MS
+Iguassu/M
+ii
+iii
+Ijsselmeer/M
+Ike/M
+Ikey/M
+Ikhnaton/M
+ikon's
+IL
+Ilaire/M
+Ila/M
+Ilario/M
+ilea
+Ileana/M
+Ileane/M
+ileitides
+ileitis/M
+Ilene/M
+ileum/M
+ilia
+iliac
+Iliad/MS
+Ilise/M
+ilium/M
+Ilka/M
+ilk/MS
+I'll
+Illa/M
+illegality/MS
+illegal/YS
+illegibility/MS
+illegible
+illegibly
+illegitimacy/SM
+illegitimate/SDGY
+illiberality/SM
+illiberal/Y
+illicitness/MS
+illicit/YP
+illimitableness/M
+illimitable/P
+Illinoisan/MS
+Illinois/M
+illiquid
+illiteracy/MS
+illiterateness/M
+illiterate/PSY
+Ill/M
+illness/MS
+illogicality/SM
+illogicalness/M
+illogical/PY
+illogic/M
+ill/PS
+illume/DG
+illuminate/XSDVNG
+Illuminati
+illuminatingly
+illuminating/U
+illumination/M
+illumine/BGSD
+illusionary
+illusion/ES
+illusionist/MS
+illusion's
+illusiveness/M
+illusive/PY
+illusoriness/M
+illusory/P
+illustrated/U
+illustrate/VGNSDX
+illustration/M
+illustrative/Y
+illustrator/SM
+illustriousness/SM
+illustrious/PY
+illus/V
+illy
+Ilona/M
+Ilsa/M
+Ilse/M
+Ilysa/M
+Ilyse/M
+Ilyssa/M
+Ilyushin/M
+I'm
+image/DSGM
+Imagen/M
+imagery/MS
+imaginableness
+imaginable/U
+imaginably/U
+imaginariness/M
+imaginary/PS
+imagination/MS
+imaginativeness/M
+imaginative/UY
+imagined/U
+imaginer/M
+imagine/RSDJBG
+imagoes
+imago/M
+imam/MS
+imbalance/SDM
+imbecile/YMS
+imbecilic
+imbecility/MS
+imbiber/M
+imbibe/ZRSDG
+imbrication/SM
+Imbrium/M
+imbroglio/MS
+imbruing
+imbue/GDS
+Imelda/M
+IMF
+IMHO
+imitable/I
+imitate/SDVNGX
+imitation/M
+imitativeness/MS
+imitative/YP
+imitator/SM
+immaculateness/SM
+immaculate/YP
+immanence/S
+immanency/MS
+immanent/Y
+Immanuel/M
+immateriality/MS
+immaterialness/MS
+immaterial/PY
+immatureness/M
+immature/SPY
+immaturity/MS
+immeasurableness/M
+immeasurable/P
+immeasurably
+immediacy/MS
+immediateness/SM
+immediate/YP
+immemorial/Y
+immenseness/M
+immense/PRTY
+immensity/MS
+immerse/RSDXNG
+immersible
+immersion/M
+immigrant/SM
+immigrate/NGSDX
+immigration/M
+imminence/SM
+imminentness/M
+imminent/YP
+immobile
+immobility/MS
+immobilization/MS
+immobilize/DSRG
+immoderateness/M
+immoderate/NYP
+immoderation/M
+immodest/Y
+immodesty/SM
+immolate/SDNGX
+immolation/M
+immorality/MS
+immoral/Y
+immortality/SM
+immortalized/U
+immortalize/GDS
+immortal/SY
+immovability/SM
+immovableness/M
+immovable/PS
+immovably
+immune/S
+immunity/SM
+immunization/MS
+immunize/GSD
+immunoassay/M
+immunodeficiency/S
+immunodeficient
+immunologic
+immunological/Y
+immunologist/SM
+immunology/MS
+immure/GSD
+immutability/MS
+immutableness/M
+immutable/P
+immutably
+IMNSHO
+IMO
+Imogene/M
+Imogen/M
+Imojean/M
+impaction/SM
+impactor/SM
+impact/VGMRDS
+impaired/U
+impairer/M
+impair/LGRDS
+impairment/SM
+impala/MS
+impale/GLRSD
+impalement/SM
+impaler/M
+impalpable
+impalpably
+impanel/DGS
+impartation/M
+impart/GDS
+impartiality/SM
+impartial/Y
+impassableness/M
+impassable/P
+impassably
+impasse/SXBMVN
+impassibility/SM
+impassible
+impassibly
+impassion/DG
+impassioned/U
+impassiveness/MS
+impassive/YP
+impassivity/MS
+impasto/SM
+impatience/SM
+impatiens/M
+impatient/Y
+impeachable/U
+impeach/DRSZGLB
+impeacher/M
+impeachment/MS
+impeccability/SM
+impeccable/S
+impeccably
+impecuniousness/MS
+impecunious/PY
+impedance/MS
+impeded/U
+impeder/M
+impede/S
+imped/GRD
+impedimenta
+impediment/SM
+impelled
+impeller/MS
+impelling
+impel/S
+impend/DGS
+impenetrability/MS
+impenetrableness/M
+impenetrable/P
+impenetrably
+impenitence/MS
+impenitent/YS
+imperativeness/M
+imperative/PSY
+imperceivable
+imperceptibility/MS
+imperceptible
+imperceptibly
+imperceptive
+imperf
+imperfectability
+imperfection/MS
+imperfectness/SM
+imperfect/YSVP
+imperialism/MS
+imperialistic
+imperialistically
+imperialist/SM
+imperial/YS
+imperil/GSLD
+imperilment/SM
+imperiousness/MS
+imperious/YP
+imperishableness/M
+imperishable/SP
+imperishably
+impermanence/MS
+impermanent/Y
+impermeability/SM
+impermeableness/M
+impermeable/P
+impermeably
+impermissible
+impersonality/M
+impersonalized
+impersonal/Y
+impersonate/XGNDS
+impersonation/M
+impersonator/SM
+impertinence/SM
+impertinent/YS
+imperturbability/SM
+imperturbable
+imperturbably
+imperviousness/M
+impervious/PY
+impetigo/MS
+impetuosity/MS
+impetuousness/MS
+impetuous/YP
+impetus/MS
+impiety/MS
+impinge/LS
+impingement/MS
+imping/GD
+impiousness/SM
+impious/PY
+impishness/MS
+impish/YP
+implacability/SM
+implacableness/M
+implacable/P
+implacably
+implantation/SM
+implant/BGSDR
+implanter/M
+implausibility/MS
+implausible
+implausibly
+implementability
+implementable/U
+implementation/A
+implementations
+implementation's
+implemented/AU
+implementer/M
+implementing/A
+implementor/MS
+implement/SMRDGZB
+implicant/SM
+implicate/VGSD
+implication/M
+implicative/PY
+implicitness/SM
+implicit/YP
+implied/Y
+implode/GSD
+implore/GSD
+imploring/Y
+implosion/SM
+implosive/S
+imply/GNSDX
+impoliteness/MS
+impolite/YP
+impoliticness/M
+impolitic/PY
+imponderableness/M
+imponderable/PS
+importance/SM
+important/Y
+importation/MS
+importer/M
+importing/A
+import/SZGBRD
+importunateness/M
+importunate/PYGDS
+importuner/M
+importune/SRDZYG
+importunity/SM
+imposable
+impose/ASDG
+imposer/SM
+imposingly
+imposing/U
+imposition/SM
+impossibility/SM
+impossibleness/M
+impossible/PS
+impossibly
+imposter's
+impostor/SM
+impost/SGMD
+imposture/SM
+impotence/MS
+impotency/S
+impotent/SY
+impound/GDS
+impoundments
+impoverisher/M
+impoverish/LGDRS
+impoverishment/SM
+impracticableness/M
+impracticable/P
+impracticably
+impracticality/SM
+impracticalness/M
+impractical/PY
+imprecate/NGXSD
+imprecation/M
+impreciseness/MS
+imprecise/PYXN
+imprecision/M
+impregnability/MS
+impregnableness/M
+impregnable/P
+impregnably
+impregnate/DSXNG
+impregnation/M
+impresario/SM
+impress/DRSGVL
+impressed/U
+impresser/M
+impressibility/MS
+impressible
+impressionability/SM
+impressionableness/M
+impressionable/P
+impression/BMS
+impressionism/SM
+impressionistic
+impressionist/MS
+impressiveness/MS
+impressive/YP
+impressment/M
+imprimatur/SM
+imprinter/M
+imprinting/M
+imprint/SZDRGM
+imprison/GLDS
+imprisonment/MS
+improbability/MS
+improbableness/M
+improbable/P
+improbably
+impromptu/S
+improperness/M
+improper/PY
+impropitious
+impropriety/SM
+improved/U
+improvement/MS
+improver/M
+improve/SRDGBL
+improvidence/SM
+improvident/Y
+improvisational
+improvisation/MS
+improvisatory
+improviser/M
+improvise/RSDZG
+imprudence/SM
+imprudent/Y
+imp/SGMDRY
+impudence/MS
+impudent/Y
+impugner/M
+impugn/SRDZGB
+impulse/XMVGNSD
+impulsion/M
+impulsiveness/MS
+impulsive/YP
+impunity/SM
+impureness/M
+impure/RPTY
+impurity/MS
+imputation/SM
+impute/SDBG
+Imus/M
+IN
+inaction
+inactive
+inadequate/S
+inadvertence/MS
+inadvertent/Y
+inalienability/MS
+inalienably
+inalterableness/M
+inalterable/P
+Ina/M
+inamorata/MS
+inane/SRPYT
+inanimateness/S
+inanimate/P
+inanity/MS
+inappeasable
+inappropriate/P
+inarticulate/P
+in/AS
+inasmuch
+inaugural/S
+inaugurate/XSDNG
+inauguration/M
+inauthenticity
+inbound/G
+inbred/S
+inbreed/JG
+incalculableness/M
+incalculably
+incandescence/SM
+incandescent/YS
+incant
+incantation/SM
+incantatory
+incapable/S
+incapacitate/GNSD
+incapacitation/M
+incarcerate/XGNDS
+incarceration/M
+incarnadine/GDS
+incarnate/AGSDNX
+incarnation/AM
+Inca/SM
+incendiary/S
+incense/MGDS
+incentive/ESM
+incentively
+incept/DGVS
+inception/MS
+inceptive/Y
+inceptor/M
+incessant/Y
+incest/SM
+incestuousness/MS
+incestuous/PY
+inch/GMDS
+inchoate/DSG
+Inchon/M
+inchworm/MS
+incidence/MS
+incidental/YS
+incident/SM
+incinerate/XNGSD
+incineration/M
+incinerator/SM
+incipience/SM
+incipiency/M
+incipient/Y
+incise/SDVGNX
+incision/M
+incisiveness/MS
+incisive/YP
+incisor/MS
+incitement/MS
+inciter/M
+incite/RZL
+incl
+inclination/ESM
+incline/EGSD
+incliner/M
+inclining/M
+include/GDS
+inclusion/MS
+inclusiveness/MS
+inclusive/PY
+Inc/M
+incognito/S
+incoherency/M
+income/M
+incommode/DG
+incommunicado
+incomparable
+incompetent/MS
+incomplete/P
+inconceivability/MS
+inconceivableness/M
+inconceivable/P
+incondensable
+incongruousness/S
+inconsiderableness/M
+inconsiderable/P
+inconsistence
+inconsolableness/M
+inconsolable/P
+inconsolably
+incontestability/SM
+incontestably
+incontrovertibly
+inconvenience/DG
+inconvertibility
+inconvertible
+incorporable
+incorporated/UE
+incorporate/GASDXN
+incorrect/P
+incorrigibility/MS
+incorrigibleness/M
+incorrigible/SP
+incorrigibly
+incorruptible/S
+incorruptibly
+increase/JB
+increaser/M
+increasing/Y
+incredibleness/M
+incredible/P
+incremental/Y
+incrementation
+increment/DMGS
+incriminate/XNGSD
+incrimination/M
+incriminatory
+incrustation/SM
+inc/T
+incubate/XNGVDS
+incubation/M
+incubator/MS
+incubus/MS
+inculcate/SDGNX
+inculcation/M
+inculpate/SDG
+incumbency/MS
+incumbent/S
+incunabula
+incunabulum
+incurable/S
+incurious
+incursion/SM
+ind
+indebtedness/SM
+indebted/P
+indefatigableness/M
+indefatigable/P
+indefatigably
+indefeasible
+indefeasibly
+indefinableness/M
+indefinable/PS
+indefinite/S
+indelible
+indelibly
+indemnification/M
+indemnify/NXSDG
+indemnity/SM
+indentation/SM
+indented/U
+indenter/M
+indention/SM
+indent/R
+indenture/DG
+Independence/M
+indescribableness/M
+indescribable/PS
+indescribably
+indestructibleness/M
+indestructible/P
+indestructibly
+indeterminably
+indeterminacy/MS
+indeterminism
+indexation/S
+indexer/M
+index/MRDZGB
+India/M
+Indiana/M
+Indianan/S
+Indianapolis/M
+Indianian/S
+Indian/SM
+indicant/MS
+indicate/DSNGVX
+indication/M
+indicative/SY
+indicator/MS
+indices's
+indicter/M
+indictment/SM
+indict/SGLBDR
+indifference
+indigence/MS
+indigenousness/M
+indigenous/YP
+indigent/SY
+indigestible/S
+indignant/Y
+indignation/MS
+indigo/SM
+Indira/M
+indirect/PG
+indiscreet/P
+indiscriminateness/M
+indiscriminate/PY
+indispensability/MS
+indispensableness/M
+indispensable/SP
+indispensably
+indisputableness/M
+indisputable/P
+indissolubleness/M
+indissoluble/P
+indissolubly
+indistinguishableness/M
+indistinguishable/P
+indite/SDG
+indium/SM
+individualism/MS
+individualistic
+individualistically
+individualist/MS
+individuality/MS
+individualization/SM
+individualize/DRSGZ
+individualized/U
+individualizer/M
+individualizes/U
+individualizing/Y
+individual/YMS
+individuate/DSXGN
+individuation/M
+indivisibleness/M
+indivisible/SP
+indivisibly
+Ind/M
+Indochina/M
+Indochinese
+indoctrinate/GNXSD
+indoctrination/M
+indoctrinator/SM
+indolence/SM
+indolent/Y
+indomitableness/M
+indomitable/P
+indomitably
+Indonesia/M
+Indonesian/S
+indoor
+Indore/M
+Indra/M
+indubitableness/M
+indubitable/P
+indubitably
+inducement/MS
+inducer/M
+induce/ZGLSRD
+inducible
+inductance/MS
+inductee/SM
+induct/GV
+induction/SM
+inductiveness/M
+inductive/PY
+inductor/MS
+indulge/GDRS
+indulgence/SDGM
+indulgent/Y
+indulger/M
+Indus/M
+industrialism/MS
+industrialist/MS
+industrialization/MS
+industrialized/U
+industrialize/SDG
+industrial/SY
+industriousness/SM
+industrious/YP
+industry/SM
+Indy/SM
+inebriate/NGSDX
+inebriation/M
+inedible
+ineducable
+ineffability/MS
+ineffableness/M
+ineffable/P
+ineffably
+inelastic
+ineligibly
+ineluctable
+ineluctably
+ineptitude/SM
+ineptness/MS
+inept/YP
+inequivalent
+inerrant
+inertial/Y
+inertia/SM
+inertness/MS
+inert/SPY
+Ines
+inescapably
+Inesita/M
+Inessa/M
+inestimably
+inevitability/MS
+inevitableness/M
+inevitable/P
+inevitably
+inexact/P
+inexhaustibleness/M
+inexhaustible/P
+inexhaustibly
+inexorability/M
+inexorableness/M
+inexorable/P
+inexorably
+inexpedience/M
+inexplicableness/M
+inexplicable/P
+inexplicably
+inexplicit
+inexpressibility/M
+inexpressibleness/M
+inexpressible/PS
+inextricably
+Inez/M
+infamous
+infamy/SM
+infancy/M
+infanticide/MS
+infantile
+infant/MS
+infantryman/M
+infantrymen
+infantry/SM
+infarction/SM
+infarct/SM
+infatuate/XNGSD
+infatuation/M
+infauna
+infected/U
+infecter
+infect/ESGDA
+infection/EASM
+infectiousness/MS
+infectious/PY
+infective
+infer/B
+inference/GMSR
+inferential/Y
+inferiority/MS
+inferior/SMY
+infernal/Y
+inferno/MS
+inferred
+inferring
+infertile
+infestation/MS
+infester/M
+infest/GSDR
+infidel/SM
+infighting/M
+infill/MG
+infiltrate/V
+infiltrator/MS
+infinitesimal/SY
+infinite/V
+infinitival
+infinitive/YMS
+infinitude/MS
+infinitum
+infinity/SM
+infirmary/SM
+infirmity/SM
+infix/M
+inflammableness/M
+inflammable/P
+inflammation/MS
+inflammatory
+inflatable/MS
+inflate/NGBDRSX
+inflater/M
+inflationary
+inflation/ESM
+inflect/GVDS
+inflectional/Y
+inflection/SM
+inflexibleness/M
+inflexible/P
+inflexion/SM
+inflict/DRSGV
+inflicter/M
+infliction/SM
+inflow/M
+influenced/U
+influencer/M
+influence/SRDGM
+influent
+influential/SY
+influenza/MS
+infomercial/S
+Informatica/M
+informatics
+informational
+information/ES
+informativeness/S
+informative/UY
+informatory
+informed/U
+informer/M
+info/SM
+infotainment/S
+infra
+infrared/SM
+infrasonic
+infrastructural
+infrastructure/MS
+infrequence/S
+infringe/LR
+infringement/SM
+infringer/M
+infuriate/GNYSD
+infuriating/Y
+infuriation/M
+infuser/M
+infuse/RZ
+infusibleness/M
+infusible/P
+inf/ZT
+Ingaberg/M
+Ingaborg/M
+Inga/M
+Ingamar/M
+Ingar/M
+Ingeberg/M
+Ingeborg/M
+Ingelbert/M
+Ingemar/M
+ingeniousness/MS
+ingenious/YP
+ingénue/S
+ingenuity/SM
+ingenuous/EY
+ingenuousness/MS
+Inger/M
+Inge/RM
+Ingersoll/M
+ingest/DGVS
+ingestible
+ingestion/SM
+Inglebert/M
+inglenook/MS
+Inglewood/M
+Inglis/M
+Ingmar/M
+ingoing
+ingot/SMDG
+ingrained/Y
+Ingra/M
+Ingram/M
+ingrate/M
+ingratiate/DSGNX
+ingratiating/Y
+ingratiation/M
+ingredient/SM
+Ingres/M
+ingression/M
+ingress/MS
+Ingrid/M
+Ingrim/M
+ingrown/P
+inguinal
+Ingunna/M
+inhabitable/U
+inhabitance
+inhabited/U
+inhabiter/M
+inhabit/R
+inhalant/S
+inhalation/SM
+inhalator/SM
+inhale/Z
+inhere/DG
+inherent/Y
+inheritableness/M
+inheritable/P
+inheritance/EMS
+inherit/BDSG
+inherited/E
+inheriting/E
+inheritor/S
+inheritress/MS
+inheritrix/MS
+inherits/E
+inhibit/DVGS
+inhibited/U
+inhibiter's
+inhibition/MS
+inhibitor/MS
+inhibitory
+inhomogeneous
+inhospitableness/M
+inhospitable/P
+inhospitality
+Inigo/M
+inimical/Y
+inimitableness/M
+inimitable/P
+inimitably
+inion
+iniquitousness/M
+iniquitous/PY
+iniquity/MS
+initialer/M
+initial/GSPRDY
+initialization/A
+initializations
+initialization's
+initialize/ASDG
+initialized/U
+initializer/S
+initiates
+initiate/UD
+initiating
+initiation/SM
+initiative/SM
+initiator/MS
+initiatory
+injectable/U
+inject/GVSDB
+injection/MS
+injector/SM
+injunctive
+injured/U
+injurer/M
+injure/SRDZG
+injuriousness/M
+injurious/YP
+inkblot/SM
+inker/M
+inkiness/MS
+inkling/SM
+inkstand/SM
+inkwell/SM
+inky/TP
+ink/ZDRJ
+inland
+inlander/M
+inlay/RG
+inletting
+inly/G
+inmost
+Inna/M
+innards
+innateness/SM
+innate/YP
+innermost/S
+innersole/S
+innerspring
+innervate/GNSDX
+innervation/M
+inner/Y
+inning/M
+Innis/M
+innkeeper/MS
+innocence/SM
+Innocent/M
+innocent/SYRT
+innocuousness/MS
+innocuous/PY
+innovate/SDVNGX
+innovation/M
+innovative/P
+innovator/MS
+innovatory
+Innsbruck/M
+innuendo/MDGS
+innumerability/M
+innumerableness/M
+innumerable/P
+innumerably
+innumerate
+inn/ZGDRSJ
+inoculate/ASDG
+inoculation/MS
+inoculative
+inoffensive/P
+Inonu/M
+inopportuneness/M
+inopportune/P
+inordinateness/M
+inordinate/PY
+inorganic
+inpatient
+In/PM
+input/MRDG
+inquirer/M
+inquire/ZR
+inquiring/Y
+inquiry/MS
+inquisitional
+inquisition/MS
+Inquisition/MS
+inquisitiveness/MS
+inquisitive/YP
+inquisitorial/Y
+inquisitor/MS
+INRI
+inrush/M
+ins
+INS
+insalubrious
+insanitary
+insatiability/MS
+insatiableness/M
+insatiable/P
+insatiably
+inscribe/Z
+inscription/SM
+inscrutability/SM
+inscrutableness/SM
+inscrutable/P
+inscrutably
+inseam
+insecticidal
+insecticide/MS
+insectivore/SM
+insectivorous
+insecureness/M
+insecure/P
+inseminate/NGXSD
+insemination/M
+insensateness/M
+insensate/P
+insensible/P
+insentient
+inseparable/S
+insert/ADSG
+inserter/M
+insertion/AMS
+insetting
+inshore
+insider/M
+inside/Z
+insidiousness/MS
+insidious/YP
+insightful/Y
+insigne's
+insignia/SM
+insignificant
+insinuate/VNGXSD
+insinuating/Y
+insinuation/M
+insinuator/SM
+insipidity/MS
+insipid/Y
+insistence/SM
+insistent/Y
+insisting/Y
+insist/SGD
+insociable
+insofar
+insole/M
+insolence/SM
+insolent/YS
+insolubleness/M
+insoluble/P
+insolubly
+insomniac/S
+insomnia/MS
+insomuch
+insouciance/SM
+insouciant/Y
+inspect/AGSD
+inspection/SM
+inspective
+inspectorate/MS
+inspector/SM
+inspirational/Y
+inspiration/MS
+inspired/U
+inspire/R
+inspirer/M
+inspiring/U
+inspirit/DG
+Inst
+installable
+install/ADRSG
+installation/SM
+installer/MS
+installment/MS
+instance/GD
+instantaneousness/M
+instantaneous/PY
+instantiated/U
+instantiate/SDXNG
+instantiation/M
+instant/SRYMP
+instate/AGSD
+inst/B
+instead
+instigate/XSDVGN
+instigation/M
+instigator/SM
+instillation/SM
+instinctive/Y
+instinctual
+instinct/VMS
+instituter/M
+institutes/M
+institute/ZXVGNSRD
+institutionalism/M
+institutionalist/M
+institutionalization/SM
+institutionalize/GDS
+institutional/Y
+institution/AM
+institutor's
+instr
+instruct/DSVG
+instructed/U
+instructional
+instruction/MS
+instructiveness/M
+instructive/PY
+instructor/MS
+instrumentalist/MS
+instrumentality/SM
+instrumental/SY
+instrumentation/SM
+instrument/GMDS
+insubordinate
+insubstantial
+insufferable
+insufferably
+insularity/MS
+insular/YS
+insulate/DSXNG
+insulated/U
+insulation/M
+insulator/MS
+insulin/MS
+insult/DRSG
+insulter/M
+insulting/Y
+insuperable
+insuperably
+insupportableness/M
+insupportable/P
+insurance/MS
+insurance's/A
+insure/BZGS
+insured/S
+insurer/M
+insurgence/SM
+insurgency/MS
+insurgent/MS
+insurmountably
+insurrectionist/SM
+insurrection/SM
+intactness/M
+intact/P
+intaglio/GMDS
+intake/M
+intangible/M
+integer/MS
+integrability/M
+integrable
+integral/SYM
+integrand/MS
+integrate/AGNXEDS
+integration/EMA
+integrative/E
+integrator/MS
+integrity/SM
+integument/SM
+intellective/Y
+intellect/MVS
+intellectualism/MS
+intellectuality/M
+intellectualize/GSD
+intellectualness/M
+intellectual/YPS
+intelligence/MSR
+intelligencer/M
+intelligentsia/MS
+intelligent/UY
+intelligibilities
+intelligibility/UM
+intelligibleness/MU
+intelligible/PU
+intelligibly/U
+Intel/M
+Intelsat/M
+intemperate/P
+intendant/MS
+intendedness/M
+intended/SYP
+intender/M
+intensification/M
+intensifier/M
+intensify/GXNZRSD
+intensional/Y
+intensiveness/MS
+intensive/PSY
+intentionality/M
+intentional/UY
+intention/SDM
+intentness/SM
+intent/YP
+interaction/MS
+interactive/PY
+interactivity
+interact/VGDS
+interaxial
+interbank
+interbred
+interbreed/GS
+intercalate/GNVDS
+intercalation/M
+intercase
+intercaste
+interceder/M
+intercede/SRDG
+intercensal
+intercept/DGS
+interception/MS
+interceptor/MS
+intercession/MS
+intercessor/SM
+intercessory
+interchangeability/M
+interchangeableness/M
+interchangeable/P
+interchangeably
+interchange/DSRGJ
+interchanger/M
+intercity
+interclass
+intercohort
+intercollegiate
+intercommunicate/SDXNG
+intercommunication/M
+intercom/SM
+interconnectedness/M
+interconnected/P
+interconnect/GDS
+interconnection/SM
+interconnectivity
+intercontinental
+interconversion/M
+intercorrelated
+intercourse/SM
+Interdata/M
+interdenominational
+interdepartmental/Y
+interdependence/MS
+interdependency/SM
+interdependent/Y
+interdiction/MS
+interdict/MDVGS
+interdisciplinary
+interested/UYE
+interest/GEMDS
+interestingly/U
+interestingness/M
+interesting/YP
+inter/ESTL
+interface/SRDGM
+interfacing/M
+interfaith
+interference/MS
+interferer/M
+interfere/SRDG
+interfering/Y
+interferometer/SM
+interferometric
+interferometry/M
+interferon/MS
+interfile/GSD
+intergalactic
+intergenerational
+intergeneration/M
+interglacial
+intergovernmental
+intergroup
+interim/S
+interindex
+interindustry
+interior/SMY
+interj
+interject/GDS
+interjectional
+interjection/MS
+interlace/GSD
+interlard/SGD
+interlayer/G
+interleave/SDG
+interleukin/S
+interlibrary
+interlinear/S
+interline/JGSD
+interlingual
+interlingua/M
+interlining/M
+interlink/GDS
+interlisp/M
+interlobular
+interlocker/M
+interlock/RDSG
+interlocutor/MS
+interlocutory
+interlope/GZSRD
+interloper/M
+interlude/MSDG
+intermarriage/MS
+intermarry/GDS
+intermediary/MS
+intermediateness/M
+intermediate/YMNGSDP
+intermediation/M
+interment/SME
+intermeshed
+intermetrics
+intermezzi
+intermezzo/SM
+interminably
+intermingle/DSG
+intermission/MS
+intermittent/Y
+intermix/GSRD
+intermodule
+intermolecular/Y
+internalization/SM
+internalize/GDS
+internal/SY
+Internationale/M
+internationalism/SM
+internationalist/SM
+internationality/M
+internationalization/MS
+internationalize/DSG
+international/YS
+internecine
+internee/SM
+interne's
+Internet/M
+INTERNET/M
+internetwork
+internist/SM
+intern/L
+internment/SM
+internship/MS
+internuclear
+interocular
+interoffice
+interoperability
+interpenetrates
+interpersonal/Y
+interplanetary
+interplay/GSMD
+interpol
+interpolate/XGNVBDS
+interpolation/M
+Interpol/M
+interpose/GSRD
+interposer/M
+interposition/MS
+interpretable/U
+interpret/AGSD
+interpretation/MSA
+interpretative/Y
+interpreted/U
+interpreter/SM
+interpretive/Y
+interpretor/S
+interprocess
+interprocessor
+interquartile
+interracial
+interred/E
+interregional
+interregnum/MS
+interrelatedness/M
+interrelated/PY
+interrelate/GNDSX
+interrelation/M
+interrelationship/SM
+interring/E
+interrogate/DSXGNV
+interrogation/M
+interrogative/SY
+interrogator/SM
+interrogatory/S
+interrupted/U
+interrupter/M
+interruptibility
+interruptible
+interruption/MS
+interrupt/VGZRDS
+interscholastic
+intersect/GDS
+intersection/MS
+intersession/MS
+interspecies
+intersperse/GNDSX
+interspersion/M
+interstage
+interstate/S
+interstellar
+interstice/SM
+interstitial/SY
+intersurvey
+intertask
+intertwine/GSD
+interurban/S
+interval/MS
+intervene/GSRD
+intervener/M
+intervenor/M
+interventionism/MS
+interventionist/S
+intervention/MS
+interview/AMD
+interviewed/U
+interviewee/SM
+interviewer/SM
+interviewing
+interviews
+intervocalic
+interweave/GS
+interwove
+interwoven
+intestacy/SM
+intestinal/Y
+intestine/SM
+inti
+intifada
+intimacy/SM
+intimal
+intimateness/M
+intimater/M
+intimate/XYNGPDRS
+intimation/M
+intimidate/SDXNG
+intimidating/Y
+intimidation/M
+into
+intolerableness/M
+intolerable/P
+intolerant/PS
+intonate/NX
+intonation/M
+intoxicant/MS
+intoxicate/DSGNX
+intoxicated/Y
+intoxication/M
+intra
+intracellular
+intracity
+intraclass
+intracohort
+intractability/M
+intractableness/M
+intractable/P
+intradepartmental
+intrafamily
+intragenerational
+intraindustry
+intraline
+intrametropolitan
+intramural/Y
+intramuscular/Y
+intranasal
+intransigence/MS
+intransigent/YS
+intransitive/S
+intraoffice
+intraprocess
+intrapulmonary
+intraregional
+intrasectoral
+intrastate
+intratissue
+intrauterine
+intravenous/YS
+intrepidity/SM
+intrepidness/M
+intrepid/YP
+intricacy/SM
+intricateness/M
+intricate/PY
+intrigue/DRSZG
+intriguer/M
+intriguing/Y
+intrinsically
+intrinsic/S
+introduce/ADSG
+introducer/M
+introduction/ASM
+introductory
+introit/SM
+introject/SD
+intro/S
+introspection/MS
+introspectiveness/M
+introspective/YP
+introspect/SGVD
+introversion/SM
+introvert/SMDG
+intruder/M
+intrude/ZGDSR
+intrusion/SM
+intrusiveness/MS
+intrusive/SYP
+intubate/NGDS
+intubation/M
+intuit/GVDSB
+intuitionist/M
+intuitiveness/MS
+intuitive/YP
+int/ZR
+Inuit/MS
+inundate/SXNG
+inundation/M
+inure/GDS
+invader/M
+invade/ZSRDG
+invalid/GSDM
+invalidism/MS
+invariable/P
+invariant/M
+invasion/SM
+invasive/P
+invectiveness/M
+invective/PSMY
+inveigh/DRG
+inveigher/M
+inveighs
+inveigle/DRSZG
+inveigler/M
+invent/ADGS
+invented/U
+invention/ASM
+inventiveness/MS
+inventive/YP
+inventor/MS
+inventory/SDMG
+Inverness/M
+inverse/YV
+inverter/M
+invertible
+invert/ZSGDR
+invest/ADSLG
+investigate/XDSNGV
+investigation/MA
+investigator/MS
+investigatory
+investiture/SM
+investment/ESA
+investment's/A
+investor/SM
+inveteracy/MS
+inveterate/Y
+inviability
+invidiousness/MS
+invidious/YP
+invigilate/GD
+invigilator/SM
+invigorate/ANGSD
+invigorating/Y
+invigoration/AM
+invigorations
+invincibility/SM
+invincibleness/M
+invincible/P
+invincibly
+inviolability/MS
+inviolably
+inviolateness/M
+inviolate/YP
+inviscid
+invisibleness/M
+invisible/S
+invitational/S
+invitation/MS
+invited/U
+invitee/S
+inviter/M
+invite/SRDG
+inviting/Y
+invocable
+invocate
+invoked/A
+invoke/GSRDBZ
+invoker/M
+invokes/A
+involuntariness/S
+involuntary/P
+involute/XYN
+involution/M
+involutorial
+involvedly
+involved/U
+involve/GDSRL
+involvement/SM
+involver/M
+invulnerability/M
+invulnerableness/M
+inwardness/M
+inward/PY
+ioctl
+iodate/MGND
+iodation/M
+iodide/MS
+iodinate/DNG
+iodine/MS
+iodize/GSD
+Iolande/M
+Iolanthe/M
+Io/M
+Iona/M
+Ionesco/M
+Ionian/M
+ionic/S
+Ionic/S
+ionization's
+ionization/SU
+ionized/UC
+ionize/GNSRDJXZ
+ionizer's
+ionizer/US
+ionizes/U
+ionizing/U
+ionosphere/SM
+ionospheric
+ion's/I
+ion/SMU
+Iorgo/MS
+Iormina/M
+Iosep/M
+iota/SM
+IOU
+Iowan/S
+Iowa/SM
+IPA
+ipecac/MS
+Iphigenia/M
+ipso
+Ipswich/M
+IQ
+Iqbal/M
+Iquitos/M
+Ira/M
+Iranian/MS
+Iran/M
+Iraqi/SM
+Iraq/M
+IRA/S
+irascibility/SM
+irascible
+irascibly
+irateness/S
+irate/RPYT
+ireful
+Ireland/M
+ire/MGDS
+Irena/M
+Irene/M
+irenic/S
+iridescence/SM
+iridescent/Y
+irides/M
+iridium/MS
+irids
+Irina/M
+Iris
+iris/GDSM
+Irishman/M
+Irishmen
+Irish/R
+Irishwoman/M
+Irishwomen
+Irita/M
+irk/GDS
+irksomeness/SM
+irksome/YP
+Irkutsk/M
+Ir/M
+Irma/M
+ironclad/S
+iron/DRMPSGJ
+ironer/M
+ironic
+ironicalness/M
+ironical/YP
+ironing/M
+ironmonger/M
+ironmongery/M
+ironside/MS
+ironstone/MS
+ironware/SM
+ironwood/SM
+ironworker/M
+ironwork/MRS
+irony/SM
+Iroquoian/MS
+Iroquois/M
+irradiate/XSDVNG
+irradiation/M
+irrationality/MS
+irrationalness/M
+irrational/YSP
+Irrawaddy/M
+irreclaimable
+irreconcilability/MS
+irreconcilableness/M
+irreconcilable/PS
+irreconcilably
+irrecoverableness/M
+irrecoverable/P
+irrecoverably
+irredeemable/S
+irredeemably
+irredentism/M
+irredentist/M
+irreducibility/M
+irreducible
+irreducibly
+irreflexive
+irrefutable
+irrefutably
+irregardless
+irregularity/SM
+irregular/YS
+irrelevance/SM
+irrelevancy/MS
+irrelevant/Y
+irreligious
+irremediableness/M
+irremediable/P
+irremediably
+irremovable
+irreparableness/M
+irreparable/P
+irreparably
+irreplaceable/P
+irrepressible
+irrepressibly
+irreproachableness/M
+irreproachable/P
+irreproachably
+irreproducibility
+irreproducible
+irresistibility/M
+irresistibleness/M
+irresistible/P
+irresistibly
+irresoluteness/SM
+irresolute/PNXY
+irresolution/M
+irresolvable
+irrespective/Y
+irresponsibility/SM
+irresponsibleness/M
+irresponsible/PS
+irresponsibly
+irretrievable
+irretrievably
+irreverence/MS
+irreverent/Y
+irreversible
+irreversibly
+irrevocableness/M
+irrevocable/P
+irrevocably
+irrigable
+irrigate/DSXNG
+irrigation/M
+irritability/MS
+irritableness/M
+irritable/P
+irritably
+irritant/S
+irritate/DSXNGV
+irritated/Y
+irritating/Y
+irritation/M
+irrupt/GVSD
+irruption/SM
+IRS
+Irtish/M
+Irvine/M
+Irving/M
+Irvin/M
+Irv/MG
+Irwin/M
+Irwinn/M
+is
+i's
+Isaac/SM
+Isaak/M
+Isabelita/M
+Isabella/M
+Isabelle/M
+Isabel/M
+Isacco/M
+Isac/M
+Isadora/M
+Isadore/M
+Isador/M
+Isahella/M
+Isaiah/M
+Isak/M
+Isa/M
+ISBN
+Iscariot/M
+Iseabal/M
+Isfahan/M
+Isherwood/M
+Ishim/M
+Ishmael/M
+Ishtar/M
+Isiahi/M
+Isiah/M
+Isidora/M
+Isidore/M
+Isidor/M
+Isidoro/M
+Isidro/M
+isinglass/MS
+Isis/M
+Islamabad/M
+Islamic/S
+Islam/SM
+islander/M
+island/GZMRDS
+Islandia/M
+isle/MS
+islet/SM
+isl/GD
+Ismael/M
+ism/MCS
+isn't
+ISO
+isobaric
+isobar/MS
+Isobel/M
+isochronal/Y
+isochronous/Y
+isocline/M
+isocyanate/M
+isodine
+isolate/SDXNG
+isolationism/SM
+isolationistic
+isolationist/SM
+isolation/M
+isolator/MS
+Isolde/M
+isomeric
+isomerism/SM
+isomer/SM
+isometrically
+isometric/S
+isometrics/M
+isomorphic
+isomorphically
+isomorphism/MS
+isomorph/M
+isoperimetrical
+isopleth/M
+isopleths
+isosceles
+isostatic
+isothermal/Y
+isotherm/MS
+isotonic
+isotope/SM
+isotopic
+isotropic
+isotropically
+isotropy/M
+Ispahan's
+ispell/M
+Ispell/M
+Israeli/MS
+Israelite/SM
+Israel/MS
+Issac/M
+Issiah/M
+Issie/M
+Issi/M
+issuable
+issuance/MS
+issuant
+issued/A
+issue/GMZDSR
+issuer/AMS
+issues/A
+issuing/A
+Issy/M
+Istanbul/M
+isthmian/S
+isthmus/SM
+Istvan/M
+Isuzu/M
+It
+IT
+Itaipu/M
+ital
+Italianate/GSD
+Italian/MS
+italicization/MS
+italicized/U
+italicize/GSD
+italic/S
+Ital/M
+Italy/M
+Itasca/M
+itch/GMDS
+itchiness/MS
+Itch/M
+itchy/RTP
+ITcorp/M
+ITCorp/M
+it'd
+Itel/M
+itemization/SM
+itemized/U
+itemize/GZDRS
+itemizer/M
+itemizes/A
+item/MDSG
+iterate/ASDXVGN
+iteration/M
+iterative/YA
+iterator/MS
+Ithaca/M
+Ithacan
+itinerant/SY
+itinerary/MS
+it'll
+it/MUS
+Ito/M
+its
+itself
+ITT
+IUD/S
+IV
+Iva/M
+Ivanhoe/M
+Ivan/M
+Ivar/M
+I've
+Ive/MRS
+Iver/M
+Ivette/M
+Ivett/M
+Ivie/M
+iv/M
+Ivonne/M
+Ivor/M
+Ivory/M
+ivory/SM
+IVs
+Ivy/M
+ivy/MDS
+ix
+Izaak/M
+Izabel/M
+Izak/M
+Izanagi/M
+Izanami/M
+Izhevsk/M
+Izmir/M
+Izvestia/M
+Izzy/M
+jabbed
+jabberer/M
+jabber/JRDSZG
+jabbing
+Jabez/M
+Jablonsky/M
+jabot/MS
+jab/SM
+jacaranda/MS
+Jacenta/M
+Jacinda/M
+Jacinta/M
+Jacintha/M
+Jacinthe/M
+jackal/SM
+jackass/SM
+jackboot/DMS
+jackdaw/SM
+Jackelyn/M
+jacketed/U
+jacket/GSMD
+jack/GDRMS
+jackhammer/MDGS
+Jackie/M
+Jacki/M
+jackknife/MGSD
+jackknives
+Jacklin/M
+Jacklyn/M
+Jack/M
+Jackman/M
+jackpot/MS
+Jackqueline/M
+Jackquelin/M
+jackrabbit/DGS
+Jacksonian
+Jackson/SM
+Jacksonville/M
+jackstraw/MS
+Jacky/M
+Jaclin/M
+Jaclyn/M
+Jacobean
+Jacobian/M
+Jacobi/M
+Jacobin/M
+Jacobite/M
+Jacobo/M
+Jacobsen/M
+Jacob/SM
+Jacobs/N
+Jacobson/M
+Jacobus
+Jacoby/M
+jacquard/MS
+Jacquard/SM
+Jacqueline/M
+Jacquelin/M
+Jacquelyn/M
+Jacquelynn/M
+Jacquenetta/M
+Jacquenette/M
+Jacques/M
+Jacquetta/M
+Jacquette/M
+Jacquie/M
+Jacqui/M
+jacuzzi
+Jacuzzi/S
+Jacynth/M
+Jada/M
+jadedness/SM
+jaded/PY
+jadeite/SM
+Jade/M
+jade/MGDS
+Jaeger/M
+Jae/M
+jaggedness/SM
+jagged/RYTP
+Jagger/M
+jaggers
+jagging
+jag/S
+jaguar/MS
+jailbird/MS
+jailbreak/SM
+jailer/M
+jail/GZSMDR
+Jaime/M
+Jaimie/M
+Jaine/M
+Jainism/M
+Jain/M
+Jaipur/M
+Jakarta/M
+Jake/MS
+Jakie/M
+Jakob/M
+jalapeño/S
+jalopy/SM
+jalousie/MS
+Jamaal/M
+Jamaica/M
+Jamaican/S
+Jamal/M
+Jamar/M
+jambalaya/MS
+jamb/DMGS
+jamboree/MS
+Jamel/M
+Jame/MS
+Jameson/M
+Jamestown/M
+Jamesy/M
+Jamey/M
+Jamie/M
+Jamill/M
+Jamil/M
+Jami/M
+Jamima/M
+Jamison/M
+Jammal/M
+jammed/U
+Jammie/M
+jamming/U
+jam/SM
+Janacek/M
+Jana/M
+Janaya/M
+Janaye/M
+Jandy/M
+Janean/M
+Janeczka/M
+Janeen/M
+Janeiro/M
+Janek/M
+Janela/M
+Janella/M
+Janelle/M
+Janell/M
+Janel/M
+Jane/M
+Janene/M
+Janenna/M
+Janessa/M
+Janesville/M
+Janeta/M
+Janet/M
+Janetta/M
+Janette/M
+Janeva/M
+Janey/M
+jangler/M
+jangle/RSDGZ
+jangly
+Jania/M
+Janice/M
+Janie/M
+Janifer/M
+Janina/M
+Janine/M
+Janis/M
+janissary/MS
+Janith/M
+janitorial
+janitor/SM
+Janka/M
+Jan/M
+Janna/M
+Jannelle/M
+Jannel/M
+Jannie/M
+Janos/M
+Janot/M
+Jansenist/M
+Jansen/M
+January/MS
+Janus/M
+Jany/M
+Japanese/SM
+Japan/M
+japanned
+japanner
+japanning
+japan/SM
+jape/DSMG
+Japura/M
+Jaquelin/M
+Jaquelyn/M
+Jaquenetta/M
+Jaquenette/M
+Jaquith/M
+Jarad/M
+jardinière/MS
+Jard/M
+Jareb/M
+Jared/M
+jarful/S
+jargon/SGDM
+Jarib/M
+Jarid/M
+Jarlsberg
+jar/MS
+Jarrad/M
+jarred
+Jarred/M
+Jarret/M
+Jarrett/M
+Jarrid/M
+jarring/SY
+Jarrod/M
+Jarvis/M
+Jase/M
+Jasen/M
+Jasmina/M
+Jasmine/M
+jasmine/MS
+Jasmin/M
+Jason/M
+Jasper/M
+jasper/MS
+Jastrow/M
+Jasun/M
+jato/SM
+jaundice/DSMG
+jaundiced/U
+jauntily
+jauntiness/MS
+jaunt/MDGS
+jaunty/SRTP
+Javanese
+Java/SM
+javelin/SDMG
+Javier/M
+jawbone/SDMG
+jawbreaker/SM
+jawline
+jaw/SMDG
+Jaxartes/M
+Jayapura/M
+jaybird/SM
+Jaycee/SM
+Jaye/M
+Jay/M
+Jaymee/M
+Jayme/M
+Jaymie/M
+Jaynell/M
+Jayne/M
+jay/SM
+Jayson/M
+jaywalker/M
+jaywalk/JSRDZG
+Jazmin/M
+jazziness/M
+jazzmen
+jazz/MGDS
+jazzy/PTR
+JCS
+jct
+JD
+Jdavie/M
+jealousness/M
+jealous/PY
+jealousy/MS
+Jeana/M
+Jeanelle/M
+Jeane/M
+Jeanette/M
+Jeanie/M
+Jeanine/M
+Jean/M
+jean/MS
+Jeanna/M
+Jeanne/M
+Jeannette/M
+Jeannie/M
+Jeannine/M
+Jecho/M
+Jedd/M
+Jeddy/M
+Jedediah/M
+Jedidiah/M
+Jedi/M
+Jed/M
+jeep/GZSMD
+Jeep/S
+jeerer/M
+jeering/Y
+jeer/SJDRMG
+Jeeves/M
+jeez
+Jefferey/M
+Jeffersonian/S
+Jefferson/M
+Jeffery/M
+Jeffie/M
+Jeff/M
+Jeffrey/SM
+Jeffry/M
+Jeffy/M
+jehad's
+Jehanna/M
+Jehoshaphat/M
+Jehovah/M
+Jehu/M
+jejuna
+jejuneness/M
+jejune/PY
+jejunum/M
+Jekyll/M
+Jelene/M
+jell/GSD
+Jello/M
+jello's
+jellybean/SM
+jellyfish/MS
+jellying/M
+jellylike
+jellyroll/S
+jelly/SDMG
+Jemie/M
+Jemimah/M
+Jemima/M
+Jemmie/M
+jemmy/M
+Jemmy/M
+Jena/M
+Jenda/M
+Jenelle/M
+Jenica/M
+Jeniece/M
+Jenifer/M
+Jeniffer/M
+Jenilee/M
+Jeni/M
+Jenine/M
+Jenkins/M
+Jen/M
+Jenna/M
+Jennee/M
+Jenner/M
+jennet/SM
+Jennette/M
+Jennica/M
+Jennie/M
+Jennifer/M
+Jennilee/M
+Jenni/M
+Jennine/M
+Jennings/M
+Jenn/RMJ
+Jenny/M
+jenny/SM
+Jeno/M
+Jensen/M
+Jens/N
+jeopard
+jeopardize/GSD
+jeopardy/MS
+Jephthah/M
+Jerad/M
+Jerald/M
+Jeralee/M
+Jeramey/M
+Jeramie/M
+Jere/M
+Jereme/M
+jeremiad/SM
+Jeremiah/M
+Jeremiahs
+Jeremias/M
+Jeremie/M
+Jeremy/M
+Jericho/M
+Jeri/M
+jerker/M
+jerk/GSDRJ
+jerkily
+jerkiness/SM
+jerkin/SM
+jerkwater/S
+jerky/RSTP
+Jermaine/M
+Jermain/M
+Jermayne/M
+Jeroboam/M
+Jerold/M
+Jerome/M
+Jeromy/M
+Jerrie/M
+Jerrilee/M
+Jerrilyn/M
+Jerri/M
+Jerrine/M
+Jerrod/M
+Jerrold/M
+Jerrome/M
+jerrybuilt
+Jerrylee/M
+jerry/M
+Jerry/M
+jersey/MS
+Jersey/MS
+Jerusalem/M
+Jervis/M
+Jes
+Jessalin/M
+Jessalyn/M
+Jessa/M
+Jessamine/M
+jessamine's
+Jessamyn/M
+Jessee/M
+Jesselyn/M
+Jesse/M
+Jessey/M
+Jessica/M
+Jessie/M
+Jessika/M
+Jessi/M
+jess/M
+Jess/M
+Jessy/M
+jest/DRSGZM
+jester/M
+jesting/Y
+Jesuit/SM
+Jesus
+Jeth/M
+Jethro/M
+jetliner/MS
+jet/MS
+jetport/SM
+jetsam/MS
+jetted/M
+jetting/M
+jettison/DSG
+jetty/RSDGMT
+jeweler/M
+jewelery/S
+jewel/GZMRDS
+Jewelled/M
+Jewelle/M
+jewellery's
+Jewell/MD
+Jewel/M
+jewelry/MS
+Jewess/SM
+Jewishness/MS
+Jewish/P
+Jew/MS
+Jewry/MS
+Jezebel/MS
+j/F
+JFK/M
+jg/M
+jibbed
+jibbing
+jibe/S
+jib/MDSG
+Jidda/M
+jiff/S
+jiffy/SM
+jigged
+jigger/SDMG
+jigging/M
+jiggle/SDG
+jiggly/TR
+jig/MS
+jigsaw/GSDM
+jihad/SM
+Jilin
+Jillana/M
+Jillane/M
+Jillayne/M
+Jilleen/M
+Jillene/M
+Jillian/M
+Jillie/M
+Jilli/M
+Jill/M
+Jilly/M
+jilt/DRGS
+jilter/M
+Jimenez/M
+Jim/M
+Jimmie/M
+jimmy/GSDM
+Jimmy/M
+jimsonweed/S
+Jinan
+jingler/M
+jingle/RSDG
+jingly/TR
+jingoism/SM
+jingoistic
+jingoist/SM
+jingo/M
+Jinnah/M
+jinni's
+jinn/MS
+Jinny/M
+jinrikisha/SM
+jinx/GMDS
+jitney/MS
+jitterbugged
+jitterbugger
+jitterbugging
+jitterbug/SM
+jitter/S
+jittery/TR
+jiujitsu's
+Jivaro/M
+jive/MGDS
+Joachim/M
+Joana/M
+Joane/M
+Joanie/M
+Joan/M
+Joanna/M
+Joanne/SM
+Joann/M
+Joaquin/M
+jobbed
+jobber/MS
+jobbery/M
+jobbing/M
+Jobey/M
+jobholder/SM
+Jobie/M
+Jobi/M
+Jobina/M
+joblessness/MS
+jobless/P
+Jobrel/M
+job/SM
+Job/SM
+Jobye/M
+Joby/M
+Jobyna/M
+Jocasta/M
+Joceline/M
+Jocelin/M
+Jocelyne/M
+Jocelyn/M
+jockey/SGMD
+jock/GDMS
+Jock/M
+Jocko/M
+jockstrap/MS
+jocoseness/MS
+jocose/YP
+jocosity/SM
+jocularity/SM
+jocular/Y
+jocundity/SM
+jocund/Y
+Jodee/M
+jodhpurs
+Jodie/M
+Jodi/M
+Jody/M
+Joeann/M
+Joela/M
+Joelie/M
+Joella/M
+Joelle/M
+Joellen/M
+Joell/MN
+Joelly/M
+Joellyn/M
+Joel/MY
+Joelynn/M
+Joe/M
+Joesph/M
+Joete/M
+joey/M
+Joey/M
+jogged
+jogger/SM
+jogging/S
+joggler/M
+joggle/SRDG
+Jogjakarta/M
+jog/S
+Johan/M
+Johannah/M
+Johanna/M
+Johannes
+Johannesburg/M
+Johann/M
+Johansen/M
+Johanson/M
+Johna/MH
+Johnathan/M
+Johnath/M
+Johnathon/M
+Johnette/M
+Johnie/M
+Johnna/M
+Johnnie/M
+johnnycake/SM
+Johnny/M
+johnny/SM
+Johnsen/M
+john/SM
+John/SM
+Johns/N
+Johnson/M
+Johnston/M
+Johnstown/M
+Johny/M
+Joice/M
+join/ADGFS
+joined/U
+joiner/FSM
+joinery/MS
+jointed/EYP
+jointedness/ME
+joint/EGDYPS
+jointer/M
+jointly/F
+joint's
+jointures
+joist/GMDS
+Jojo/M
+joke/MZDSRG
+joker/M
+jokey
+jokier
+jokiest
+jokily
+joking/Y
+Jolee/M
+Joleen/M
+Jolene/M
+Joletta/M
+Jolie/M
+Joliet's
+Joli/M
+Joline/M
+Jolla/M
+jollification/MS
+jollily
+jolliness/SM
+jollity/MS
+jolly/TSRDGP
+Jolson/M
+jolt/DRGZS
+jolter/M
+Joly/M
+Jolyn/M
+Jolynn/M
+Jo/MY
+Jonah/M
+Jonahs
+Jonas
+Jonathan/M
+Jonathon/M
+Jonell/M
+Jone/MS
+Jones/S
+Jonie/M
+Joni/MS
+Jon/M
+jonquil/MS
+Jonson/M
+Joplin/M
+Jordain/M
+Jordana/M
+Jordanian/S
+Jordan/M
+Jordanna/M
+Jordon/M
+Jorey/M
+Jorgan/M
+Jorge/M
+Jorgensen/M
+Jorgenson/M
+Jorie/M
+Jori/M
+Jorrie/M
+Jorry/M
+Jory/M
+Joscelin/M
+Josee/M
+Josefa/M
+Josefina/M
+Josef/M
+Joseito/M
+Jose/M
+Josepha/M
+Josephina/M
+Josephine/M
+Joseph/M
+Josephs
+Josephson/M
+Josephus/M
+Josey/M
+josh/DSRGZ
+josher/M
+Joshia/M
+Josh/M
+Joshuah/M
+Joshua/M
+Josiah/M
+Josias/M
+Josie/M
+Josi/M
+Josselyn/M
+joss/M
+jostle/SDG
+Josue/M
+Josy/M
+jot/S
+jotted
+jotter/SM
+jotting/SM
+Joule/M
+joule/SM
+jounce/SDG
+jouncy/RT
+Jourdain/M
+Jourdan/M
+journalese/MS
+journal/GSDM
+journalism/SM
+journalistic
+journalist/SM
+journalize/DRSGZ
+journalized/U
+journalizer/M
+journey/DRMZSGJ
+journeyer/M
+journeyman/M
+journeymen
+jouster/M
+joust/ZSMRDG
+Jovanovich/M
+Jove/M
+joviality/SM
+jovial/Y
+Jovian
+jowl/SMD
+jowly/TR
+Joya/M
+Joyan/M
+Joyann/M
+Joycean
+Joycelin/M
+Joyce/M
+Joye/M
+joyfuller
+joyfullest
+joyfulness/SM
+joyful/PY
+joylessness/MS
+joyless/PY
+Joy/M
+joy/MDSG
+Joyner/M
+joyousness/MS
+joyous/YP
+joyridden
+joyride/SRZMGJ
+joyrode
+joystick/S
+Jozef/M
+JP
+Jpn
+Jr/M
+j's
+J's
+Jsandye/M
+Juana/M
+Juanita/M
+Juan/M
+Juarez
+Jubal/M
+jubilant/Y
+jubilate/XNGDS
+jubilation/M
+jubilee/SM
+Judah/M
+Judaic
+Judaical
+Judaism/SM
+Judas/S
+juddered
+juddering
+Judd/M
+Judea/M
+Jude/M
+judge/AGDS
+judger/M
+judge's
+judgeship/SM
+judgmental/Y
+judgment/MS
+judicable
+judicatory/S
+judicature/MS
+judicial/Y
+judiciary/S
+judicious/IYP
+judiciousness/SMI
+Judie/M
+Judi/MH
+Juditha/M
+Judith/M
+Jud/M
+judo/MS
+Judon/M
+Judson/M
+Judye/M
+Judy/M
+jugate/F
+jugful/SM
+jugged
+Juggernaut/M
+juggernaut/SM
+jugging
+juggler/M
+juggle/RSDGZ
+jugglery/MS
+jug/MS
+jugular/S
+juice/GMZDSR
+juicer/M
+juicily
+juiciness/MS
+juicy/TRP
+Juieta/M
+jujitsu/MS
+jujube/SM
+juju/M
+jujutsu's
+jukebox/SM
+juke/GS
+Julee/M
+Jule/MS
+julep/SM
+Julia/M
+Juliana/M
+Juliane/M
+Julian/M
+Julianna/M
+Julianne/M
+Juliann/M
+Julie/M
+julienne/GSD
+Julienne/M
+Julieta/M
+Juliet/M
+Julietta/M
+Juliette/M
+Juli/M
+Julina/M
+Juline/M
+Julio/M
+Julissa/M
+Julita/M
+Julius/M
+Jul/M
+Julys
+July/SM
+jumble/GSD
+jumbo/MS
+jumper/M
+jump/GZDRS
+jumpily
+jumpiness/MS
+jumpsuit/S
+jumpy/PTR
+jun
+junco/MS
+junction/IMESF
+juncture/SFM
+Juneau/M
+June/MS
+Junette/M
+Jungfrau/M
+Jungian
+jungle/SDM
+Jung/M
+Junia/M
+Junie/M
+Junina/M
+juniority/M
+junior/MS
+Junior/S
+juniper/SM
+junkerdom
+Junker/SM
+junketeer/SGDM
+junket/SMDG
+junk/GZDRMS
+junkie/RSMT
+junkyard/MS
+Jun/M
+Juno/M
+junta/MS
+Jupiter/M
+Jurassic
+juridic
+juridical/Y
+juried
+jurisdictional/Y
+jurisdiction/SM
+jurisprudence/SM
+jurisprudent
+jurisprudential/Y
+juristic
+jurist/MS
+juror/MS
+Jurua/M
+jury/IMS
+jurying
+juryman/M
+jurymen
+jurywoman/M
+jurywomen
+justed
+Justen/M
+juster/M
+justest
+Justice/M
+justice/MIS
+justiciable
+justifiability/M
+justifiable/U
+justifiably/U
+justification/M
+justified/UA
+justifier/M
+justify/GDRSXZN
+Justina/M
+Justine/M
+justing
+Justinian/M
+Justin/M
+Justinn/M
+Justino/M
+Justis/M
+justness/MS
+justness's/U
+justs
+just/UPY
+Justus/M
+jute/SM
+Jutish
+Jutland/M
+jut/S
+jutted
+jutting
+Juvenal/M
+juvenile/SM
+juxtapose/SDG
+juxtaposition/SM
+JV
+J/X
+Jyoti/M
+Kaaba/M
+kabob/SM
+kaboom
+Kabuki
+kabuki/SM
+Kabul/M
+Kacey/M
+Kacie/M
+Kacy/M
+Kaddish/M
+kaddish/S
+Kaela/M
+kaffeeklatch
+kaffeeklatsch/S
+Kafkaesque
+Kafka/M
+kaftan's
+Kagoshima/M
+Kahaleel/M
+Kahlil/M
+Kahlua/M
+Kahn/M
+Kaia/M
+Kaifeng/M
+Kaila/M
+Kaile/M
+Kailey/M
+Kai/M
+Kaine/M
+Kain/M
+kaiser/MS
+Kaiser/SM
+Kaitlin/M
+Kaitlyn/M
+Kaitlynn/M
+Kaja/M
+Kajar/M
+Kakalina/M
+Kalahari/M
+Kala/M
+Kalamazoo/M
+Kalashnikov/M
+Kalb/M
+Kaleb/M
+Kaleena/M
+kaleidescope
+kaleidoscope/SM
+kaleidoscopic
+kaleidoscopically
+Kale/M
+kale/MS
+Kalgoorlie/M
+Kalie/M
+Kalila/M
+Kalil/M
+Kali/M
+Kalina/M
+Kalinda/M
+Kalindi/M
+Kalle/M
+Kalli/M
+Kally/M
+Kalmyk
+Kalvin/M
+Kama/M
+Kamchatka/M
+Kamehameha/M
+Kameko/M
+Kamikaze/MS
+kamikaze/SM
+Kamilah/M
+Kamila/M
+Kamillah/M
+Kampala/M
+Kampuchea/M
+Kanchenjunga/M
+Kandace/M
+Kandahar/M
+Kandinsky/M
+Kandy/M
+Kane/M
+kangaroo/SGMD
+Kania/M
+Kankakee/M
+Kan/MS
+Kannada/M
+Kano/M
+Kanpur/M
+Kansan/S
+Kansas
+Kantian
+Kant/M
+Kanya/M
+Kaohsiung/M
+kaolinite/M
+kaolin/MS
+Kaplan/M
+kapok/SM
+Kaposi/M
+kappa/MS
+kaput/M
+Karachi/M
+Karaganda/M
+Karakorum/M
+karakul/MS
+Karalee/M
+Karalynn/M
+Kara/M
+Karamazov/M
+karaoke/S
+karate/MS
+karat/SM
+Karee/M
+Kareem/M
+Karel/M
+Kare/M
+Karena/M
+Karenina/M
+Karen/M
+Karia/M
+Karie/M
+Karil/M
+Karilynn/M
+Kari/M
+Karim/M
+Karina/M
+Karine/M
+Karin/M
+Kariotta/M
+Karisa/M
+Karissa/M
+Karita/M
+Karla/M
+Karlan/M
+Karlee/M
+Karleen/M
+Karlene/M
+Karlen/M
+Karlie/M
+Karlik/M
+Karlis
+Karl/MNX
+Karloff/M
+Karlotta/M
+Karlotte/M
+Karly/M
+Karlyn/M
+karma/SM
+Karmen/M
+karmic
+Karna/M
+Karney/M
+Karola/M
+Karole/M
+Karolina/M
+Karoline/M
+Karol/M
+Karoly/M
+Karon/M
+Karo/YM
+Karp/M
+Karrah/M
+Karrie/M
+Karroo/M
+Karry/M
+kart/MS
+Karylin/M
+Karyl/M
+Kary/M
+Karyn/M
+Kasai/M
+Kasey/M
+Kashmir/SM
+Kaspar/M
+Kasparov/M
+Kasper/M
+Kass
+Kassandra/M
+Kassey/M
+Kassia/M
+Kassie/M
+Kassi/M
+Katalin/M
+Kata/M
+Katee/M
+Katelyn/M
+Kate/M
+Katerina/M
+Katerine/M
+Katey/M
+Katha/M
+Katharina/M
+Katharine/M
+Katharyn/M
+Kathe/M
+Katherina/M
+Katherine/M
+Katheryn/M
+Kathiawar/M
+Kathie/M
+Kathi/M
+Kathleen/M
+Kathlin/M
+Kath/M
+Kathmandu
+Kathrine/M
+Kathryne/M
+Kathryn/M
+Kathye/M
+Kathy/M
+Katie/M
+Kati/M
+Katina/M
+Katine/M
+Katinka/M
+Katleen/M
+Katlin/M
+Kat/M
+Katmai/M
+Katmandu's
+Katowice/M
+Katrina/M
+Katrine/M
+Katrinka/M
+Kattie/M
+Katti/M
+Katuscha/M
+Katusha/M
+Katya/M
+katydid/SM
+Katy/M
+Katz/M
+Kauai/M
+Kauffman/M
+Kaufman/M
+Kaunas/M
+Kaunda/M
+Kawabata/M
+Kawasaki/M
+kayak/SGDM
+Kaycee/M
+Kaye/M
+Kayla/M
+Kaylee/M
+Kayle/M
+Kayley/M
+Kaylil/M
+Kaylyn/M
+Kay/M
+Kayne/M
+kayo/DMSG
+Kazakh/M
+Kazakhstan
+Kazan/M
+Kazantzakis/M
+kazoo/SM
+Kb
+KB
+KC
+kcal/M
+kc/M
+Keane/M
+Kean/M
+Kearney/M
+Keary/M
+Keaton/M
+Keats/M
+kebab/SM
+Keck/M
+Keefe/MR
+Keefer/M
+Keegan/M
+Keelby/M
+Keeley/M
+keel/GSMDR
+keelhaul/SGD
+Keelia/M
+Keely/M
+Keenan/M
+Keene/M
+keener/M
+keen/GTSPYDR
+keening/M
+Keen/M
+keenness/MS
+keeper/M
+keep/GZJSR
+keeping/M
+keepsake/SM
+Keewatin/M
+kegged
+kegging
+keg/MS
+Keillor/M
+Keir/M
+Keisha/M
+Keith/M
+Kelbee/M
+Kelby/M
+Kelcey/M
+Kelcie/M
+Kelci/M
+Kelcy/M
+Kele/M
+Kelila/M
+Kellby/M
+Kellen/M
+Keller/M
+Kelley/M
+Kellia/M
+Kellie/M
+Kelli/M
+Kellina/M
+Kellogg/M
+Kellsie/M
+Kellyann/M
+Kelly/M
+kelp/GZMDS
+Kelsey/M
+Kelsi/M
+Kelsy/M
+Kelt's
+Kelvin/M
+kelvin/MS
+Kelwin/M
+Kemerovo/M
+Kempis/M
+Kemp/M
+Kendall/M
+Kendal/M
+Kendell/M
+Kendra/M
+Kendre/M
+Kendrick/MS
+Kenilworth/M
+Ken/M
+Kenmore/M
+ken/MS
+Kenna/M
+Kennan/M
+Kennecott/M
+kenned
+Kennedy/M
+kennel/GSMD
+Kenneth/M
+Kennett/M
+Kennie/M
+kenning
+Kennith/M
+Kenn/M
+Kenny/M
+keno/M
+Kenon/M
+Kenosha/M
+Kensington/M
+Kent/M
+Kenton/M
+Kentuckian/S
+Kentucky/M
+Kenya/M
+Kenyan/S
+Kenyatta/M
+Kenyon/M
+Keogh/M
+Keokuk/M
+kepi/SM
+Kepler/M
+kept
+keratin/MS
+kerbside
+Kerby/M
+kerchief/MDSG
+Kerensky/M
+Kerianne/M
+Keriann/M
+Keri/M
+Kerk/M
+Ker/M
+Kermie/M
+Kermit/M
+Kermy/M
+kerned
+kernel/GSMD
+kerning
+Kern/M
+kerosene/MS
+Kerouac/M
+Kerrie/M
+Kerrill/M
+Kerri/M
+Kerrin/M
+Kerr/M
+Kerry/M
+Kerstin/M
+Kerwin/M
+Kerwinn/M
+Kesley/M
+Keslie/M
+Kessiah/M
+Kessia/M
+Kessler/M
+kestrel/SM
+ketch/MS
+ketchup/SM
+ketone/M
+ketosis/M
+Kettering/M
+Kettie/M
+Ketti/M
+kettledrum/SM
+kettleful
+kettle/SM
+Ketty/M
+Kevan/M
+Keven/M
+Kevina/M
+Kevin/M
+Kevlar
+Kev/MN
+Kevon/M
+Kevorkian/M
+Kevyn/M
+Kewaskum/M
+Kewaunee/M
+Kewpie/M
+keyboardist/S
+keyboard/RDMZGS
+keyclick/SM
+keyhole/MS
+Key/M
+Keynesian/M
+Keynes/M
+keynoter/M
+keynote/SRDZMG
+keypad/MS
+keypuncher/M
+keypunch/ZGRSD
+keyring
+key/SGMD
+keystone/SM
+keystroke/SDMG
+keyword/SM
+k/FGEIS
+kg
+K/G
+KGB
+Khabarovsk/M
+Khachaturian/M
+khaki/SM
+Khalid/M
+Khalil/M
+Khan/M
+khan/MS
+Kharkov/M
+Khartoum/M
+Khayyam/M
+Khmer/M
+Khoisan/M
+Khomeini/M
+Khorana/M
+Khrushchev/SM
+Khufu/M
+Khulna/M
+Khwarizmi/M
+Khyber/M
+kHz/M
+KIA
+Kiah/M
+Kial/M
+kibble/GMSD
+kibbutzim
+kibbutz/M
+kibitzer/M
+kibitz/GRSDZ
+kibosh/GMSD
+Kickapoo/M
+kickback/SM
+kickball/MS
+kicker/M
+kick/GZDRS
+kickoff/SM
+kickstand/MS
+kicky/RT
+kidded
+kidder/SM
+kiddie/SD
+kidding/YM
+kiddish
+Kidd/M
+kiddo/SM
+kiddying
+kiddy's
+kidless
+kid/MS
+kidnaper's
+kidnaping's
+kidnap/MSJ
+kidnapped
+kidnapper/SM
+kidnapping/S
+kidney/MS
+kidskin/SM
+Kieffer/M
+kielbasa/SM
+kielbasi
+Kiele/M
+Kiel/M
+Kienan/M
+kier/I
+Kierkegaard/M
+Kiersten/M
+Kieth/M
+Kiev/M
+Kigali/M
+Kikelia/M
+Kikuyu/M
+Kilauea/M
+Kile/M
+Kiley/M
+Kilian/M
+Kilimanjaro/M
+kill/BJGZSDR
+killdeer/SM
+Killebrew/M
+killer/M
+Killian/M
+Killie/M
+killing/Y
+killjoy/S
+Killy/M
+kiln/GDSM
+kilobaud/M
+kilobit/S
+kilobuck
+kilobyte/S
+kilocycle/MS
+kilogauss/M
+kilogram/MS
+kilohertz/M
+kilohm/M
+kilojoule/MS
+kiloliter/MS
+kilometer/SM
+kilo/SM
+kiloton/SM
+kilovolt/SM
+kilowatt/SM
+kiloword
+kilter/M
+kilt/MDRGZS
+Ki/M
+Kimball/M
+Kimbell/M
+Kimberlee/M
+Kimberley/M
+Kimberli/M
+Kimberly/M
+Kimberlyn/M
+Kimble/M
+Kimbra/M
+Kim/M
+Kimmie/M
+Kimmi/M
+Kimmy/M
+kimono/MS
+Kincaid/M
+kinda
+kindergarten/MS
+kindergärtner/SM
+kinder/U
+kindheartedness/MS
+kindhearted/YP
+kindle/AGRSD
+kindler/M
+kindliness/SM
+kindliness's/U
+kindling/M
+kindly/TUPR
+kindness's
+kindness/US
+kind/PSYRT
+kindred/S
+kinematic/S
+kinematics/M
+kinesics/M
+kine/SM
+kinesthesis
+kinesthetically
+kinesthetic/S
+kinetically
+kinetic/S
+kinetics/M
+kinfolk/S
+kingbird/M
+kingdom/SM
+kingfisher/MS
+kinglet/M
+kingliness/M
+kingly/TPR
+King/M
+kingpin/MS
+Kingsbury/M
+king/SGYDM
+kingship/SM
+Kingsley/M
+Kingsly/M
+Kingston/M
+Kingstown/M
+Kingwood/M
+kink/GSDM
+kinkily
+kinkiness/SM
+kinky/PRT
+Kin/M
+kin/MS
+Kinna/M
+Kinney/M
+Kinnickinnic/M
+Kinnie/M
+Kinny/M
+Kinsey/M
+kinsfolk/S
+Kinshasa/M
+Kinshasha/M
+kinship/SM
+Kinsley/M
+kinsman/M
+kinsmen/M
+kinswoman/M
+kinswomen
+kiosk/SM
+Kiowa/SM
+Kipling/M
+Kip/M
+kip/MS
+Kippar/M
+kipped
+kipper/DMSG
+Kipper/M
+Kippie/M
+kipping
+Kipp/MR
+Kippy/M
+Kira/M
+Kirbee/M
+Kirbie/M
+Kirby/M
+Kirchhoff/M
+Kirchner/M
+Kirchoff/M
+Kirghistan/M
+Kirghizia/M
+Kirghiz/M
+Kiribati
+Kiri/M
+Kirinyaga/M
+kirk/GDMS
+Kirkland/M
+Kirk/M
+Kirkpatrick/M
+Kirkwood/M
+Kirov/M
+kirsch/S
+Kirsteni/M
+Kirsten/M
+Kirsti/M
+Kirstin/M
+Kirstyn/M
+Kisangani/M
+Kishinev/M
+kismet/SM
+kiss/DSRBJGZ
+Kissee/M
+kisser/M
+Kissiah/M
+Kissie/M
+Kissinger/M
+Kitakyushu/M
+kitbag's
+kitchener/M
+Kitchener/M
+kitchenette/SM
+kitchen/GDRMS
+kitchenware/SM
+kiter/M
+kite/SM
+kith/MDG
+kiths
+Kit/M
+kit/MDRGS
+kitsch/MS
+kitschy
+kitted
+kittenishness/M
+kittenish/YP
+kitten/SGDM
+Kittie/M
+Kitti/M
+kitting
+kittiwakes
+Kitty/M
+kitty/SM
+Kiwanis/M
+kiwifruit/S
+kiwi/SM
+Kizzee/M
+Kizzie/M
+KKK
+kl
+Klan/M
+Klansman/M
+Klara/M
+Klarika/M
+Klarrisa/M
+Klaus/M
+klaxon/M
+Klee/M
+Kleenex/SM
+Klein/M
+Kleinrock/M
+Klemens/M
+Klement/M
+Kleon/M
+kleptomaniac/SM
+kleptomania/MS
+Kliment/M
+Kline/M
+Klingon/M
+Klondike/SDMG
+kludger/M
+kludge/RSDGMZ
+kludgey
+klutziness/S
+klutz/SM
+klutzy/TRP
+Klux/M
+klystron/MS
+km
+kn
+knacker/M
+knack/SGZRDM
+knackwurst/MS
+Knapp/M
+knapsack/MS
+Knauer/M
+knavery/MS
+knave/SM
+knavish/Y
+kneader/M
+knead/GZRDS
+kneecap/MS
+kneecapped
+kneecapping
+knee/DSM
+kneeing
+kneeler/M
+kneel/GRS
+kneepad/SM
+knell/SMDG
+knelt
+Knesset/M
+knew
+Kngwarreye/M
+Knickerbocker/MS
+knickerbocker/S
+knickknack/SM
+knick/ZR
+Knievel/M
+knife/DSGM
+knighthood/MS
+knightliness/MS
+knightly/P
+Knight/M
+knight/MDYSG
+knish/MS
+knit/AU
+knits
+knitted
+knitter/MS
+knitting/SM
+knitwear/M
+knives/M
+knobbly
+knobby/RT
+Knobeloch/M
+knob/MS
+knockabout/M
+knockdown/S
+knocker/M
+knock/GZSJRD
+knockoff/S
+knockout/MS
+knockwurst's
+knoll/MDSG
+Knopf/M
+Knossos/M
+knothole/SM
+knot/MS
+knotted
+knottiness/M
+knotting/M
+knotty/TPR
+knowable/U
+knower/M
+know/GRBSJ
+knowhow
+knowingly/U
+knowing/RYT
+knowings/U
+knowledgeableness/M
+knowledgeable/P
+knowledgeably
+knowledge/SM
+Knowles
+known/SU
+Knox/M
+Knoxville/M
+knuckleball/R
+knuckle/DSMG
+knuckleduster
+knucklehead/MS
+Knudsen/M
+Knudson/M
+knurl/DSG
+Knuth/M
+Knutsen/M
+Knutson/M
+KO
+koala/SM
+Kobayashi/M
+Kobe/M
+Kochab/M
+Koch/M
+Kodachrome/M
+Kodak/SM
+Kodaly/M
+Kodiak/M
+Koenig/M
+Koenigsberg/M
+Koenraad/M
+Koestler/M
+Kohinoor/M
+Kohler/M
+Kohl/MR
+kohlrabies
+kohlrabi/M
+kola/SM
+Kolyma/M
+Kommunizma/M
+Kong/M
+Kongo/M
+Konrad/M
+Konstance/M
+Konstantine/M
+Konstantin/M
+Konstanze/M
+kookaburra/SM
+kook/GDMS
+kookiness/S
+kooky/PRT
+Koo/M
+Koontz/M
+kopeck/MS
+Koppers/M
+Koralle/M
+Koral/M
+Kora/M
+Koranic
+Koran/SM
+Kordula/M
+Korea/M
+Korean/S
+Korella/M
+Kore/M
+Koren/M
+Koressa/M
+Korey/M
+Korie/M
+Kori/M
+Kornberg/M
+Korney/M
+Korrie/M
+Korry/M
+Kort/M
+Kory/M
+Korzybski/M
+Kosciusko/M
+kosher/DGS
+Kossuth/M
+Kosygin/M
+Kovacs/M
+Kowalewski/M
+Kowalski/M
+Kowloon/M
+kowtow/SGD
+KP
+kph
+kraal/SMDG
+Kraemer/M
+kraft/M
+Kraft/M
+Krakatau's
+Krakatoa/M
+Krakow/M
+Kramer/M
+Krasnodar/M
+Krasnoyarsk/M
+Krause/M
+kraut/S
+Krebs/M
+Kremlin/M
+Kremlinologist/MS
+Kremlinology/MS
+Kresge/M
+Krieger/M
+kriegspiel/M
+krill/MS
+Kringle/M
+Krisha/M
+Krishnah/M
+Krishna/M
+Kris/M
+Krispin/M
+Krissie/M
+Krissy/M
+Kristal/M
+Krista/M
+Kristan/M
+Kristel/M
+Kriste/M
+Kristen/M
+Kristian/M
+Kristie/M
+Kristien/M
+Kristi/MN
+Kristina/M
+Kristine/M
+Kristin/M
+Kristofer/M
+Kristoffer/M
+Kristofor/M
+Kristoforo/M
+Kristo/MS
+Kristopher/M
+Kristy/M
+Kristyn/M
+Kr/M
+Kroc/M
+Kroger/M
+króna/M
+Kronecker/M
+krone/RM
+kronor
+krónur
+Kropotkin/M
+Krueger/M
+Kruger/M
+Krugerrand/S
+Krupp/M
+Kruse/M
+krypton/SM
+Krystalle/M
+Krystal/M
+Krysta/M
+Krystle/M
+Krystyna/M
+ks
+K's
+KS
+k's/IE
+kt
+Kublai/M
+Kubrick/M
+kuchen/MS
+kudos/M
+kudzu/SM
+Kuenning/M
+Kuhn/M
+Kuibyshev/M
+Ku/M
+Kumar/M
+kumquat/SM
+Kunming/M
+Kuomintang/M
+Kurdish/M
+Kurdistan/SM
+Kurd/SM
+Kurosawa/M
+Kurtis/M
+Kurt/M
+kurtosis/M
+Kusch/M
+Kuwaiti/SM
+Kuwait/M
+Kuznetsk/M
+Kuznets/M
+kvetch/DSG
+kw
+kW
+Kwakiutl/M
+Kwangchow's
+Kwangju/M
+Kwanzaa/S
+kWh
+KY
+Kyla/M
+kyle/M
+Kyle/M
+Kylen/M
+Kylie/M
+Kylila/M
+Kylynn/M
+Ky/MH
+Kym/M
+Kynthia/M
+Kyoto/M
+Kyrgyzstan
+Kyrstin/M
+Kyushu/M
+L
+LA
+Laban/M
+labeled/U
+labeler/M
+label/GAZRDS
+labellings/A
+label's
+labial/YS
+labia/M
+labile
+labiodental
+labium/M
+laboratory/MS
+laboredness/M
+labored/PMY
+labored's/U
+laborer/M
+laboring/MY
+laborings/U
+laboriousness/MS
+laborious/PY
+labor/RDMJSZG
+laborsaving
+Labradorean/S
+Labrador/SM
+lab/SM
+Lab/SM
+laburnum/SM
+labyrinthine
+labyrinth/M
+labyrinths
+laced/U
+Lacee/M
+lace/MS
+lacerate/NGVXDS
+laceration/M
+lacer/M
+laces/U
+lacewing/MS
+Lacey/M
+Lachesis/M
+lachrymal/S
+lachrymose
+Lacie/M
+lacing/M
+lackadaisic
+lackadaisical/Y
+Lackawanna/M
+lacker/M
+lackey/SMDG
+lack/GRDMS
+lackluster/S
+Lac/M
+laconic
+laconically
+lacquerer/M
+lacquer/ZGDRMS
+lacrosse/MS
+lac/SGMDR
+lactate/MNGSDX
+lactational/Y
+lactation/M
+lacteal
+lactic
+lactose/MS
+lacunae
+lacuna/M
+Lacy/M
+lacy/RT
+ladder/GDMS
+laddie/MS
+laded/U
+ladened
+ladening
+laden/U
+lade/S
+lading/M
+ladle/SDGM
+Ladoga/M
+Ladonna/M
+lad/XGSJMND
+ladybird/SM
+ladybug/MS
+ladyfinger/SM
+ladylike/U
+ladylove/MS
+Ladyship/MS
+ladyship/SM
+lady/SM
+Lady/SM
+Laetitia/M
+laetrile/S
+Lafayette/M
+Lafitte/M
+lager/DMG
+laggard/MYSP
+laggardness/M
+lagged
+lagging/MS
+lagniappe/SM
+lagoon/MS
+Lagos/M
+Lagrange/M
+Lagrangian/M
+Laguerre/M
+Laguna/M
+lag/ZSR
+Lahore/M
+laid/AI
+Laidlaw/M
+lain
+Laina/M
+Lainey/M
+Laird/M
+laird/MS
+lair/GDMS
+laissez
+laity/SM
+Laius/M
+lake/DSRMG
+Lakehurst/M
+Lakeisha/M
+laker/M
+lakeside
+Lakewood/M
+Lakisha/M
+Lakshmi/M
+lallygagged
+lallygagging
+lallygag/S
+Lalo/M
+La/M
+Lamaism/SM
+Lamarck/M
+Lamar/M
+lamasery/MS
+lama/SM
+Lamaze
+lambada/S
+lambaste/SDG
+lambda/SM
+lambency/MS
+lambent/Y
+Lambert/M
+lambkin/MS
+Lamb/M
+Lamborghini/M
+lambskin/MS
+lamb/SRDMG
+lambswool
+lamebrain/SM
+lamed/M
+lameness/MS
+lamentableness/M
+lamentable/P
+lamentably
+lamentation/SM
+lament/DGSB
+lamented/U
+lame/SPY
+la/MHLG
+laminae
+lamina/M
+laminar
+laminate/XNGSD
+lamination/M
+lam/MDRSTG
+lammed
+lammer
+lamming
+Lammond/M
+Lamond/M
+Lamont/M
+L'Amour
+lampblack/SM
+lamplighter/M
+lamplight/ZRMS
+lampooner/M
+lampoon/RDMGS
+Lamport/M
+lamppost/SM
+lamprey/MS
+lamp/SGMRD
+lampshade/MS
+LAN
+Lanae/M
+Lanai/M
+lanai/SM
+Lana/M
+Lancashire/M
+Lancaster/M
+Lancelot/M
+Lance/M
+lancer/M
+lance/SRDGMZ
+lancet/MS
+landau/MS
+lander/I
+landfall/SM
+landfill/DSG
+landforms
+landholder/M
+landhold/JGZR
+landing/M
+Landis/M
+landlady/MS
+landless
+landlines
+landlocked
+landlord/MS
+landlubber/SM
+Land/M
+landmark/GSMD
+landmass/MS
+Landon/M
+landowner/MS
+landownership/M
+landowning/SM
+Landry/M
+Landsat
+landscape/GMZSRD
+landscaper/M
+lands/I
+landslide/MS
+landslid/G
+landslip
+landsman/M
+landsmen
+land/SMRDJGZ
+Landsteiner/M
+landward/S
+Landwehr/M
+Lane/M
+lane/SM
+Lanette/M
+Laney/M
+Langeland/M
+Lange/M
+Langerhans/M
+Langford/M
+Langland/M
+Langley/M
+Lang/M
+Langmuir/M
+Langsdon/M
+Langston/M
+language/MS
+languidness/MS
+languid/PY
+languisher/M
+languishing/Y
+languish/SRDG
+languorous/Y
+languor/SM
+Lanie/M
+Lani/M
+Lanita/M
+lankiness/SM
+lankness/MS
+lank/PTYR
+lanky/PRT
+Lanna/M
+Lannie/M
+Lanni/M
+Lanny/M
+lanolin/MS
+Lansing/M
+lantern/GSDM
+lanthanide/M
+lanthanum/MS
+lanyard/MS
+Lanzhou
+Laocoon/M
+Lao/SM
+Laotian/MS
+lapboard/MS
+lapdog/S
+lapel/MS
+lapidary/MS
+lapin/MS
+Laplace/M
+Lapland/ZMR
+lapped
+lappet/MS
+lapping
+Lapp/SM
+lapsed/A
+lapse/KSDMG
+lapser/MA
+lapses/A
+lapsing/A
+lap/SM
+laps/SRDG
+laptop/SM
+lapwing/MS
+Laraine/M
+Lara/M
+Laramie/M
+larboard/MS
+larcenist/S
+larcenous
+larceny/MS
+larch/MS
+larder/M
+lard/MRDSGZ
+Lardner/M
+lardy/RT
+Laredo/M
+largehearted
+largemouth
+largeness/SM
+large/SRTYP
+largess/SM
+largish
+largo/S
+lariat/MDGS
+Lari/M
+Larina/M
+Larine/M
+Larisa/M
+Larissa/M
+larker/M
+lark/GRDMS
+Lark/M
+larkspur/MS
+Larousse/M
+Larry/M
+Larsen/M
+Lars/NM
+Larson/M
+larvae
+larval
+larva/M
+laryngeal/YS
+larynges
+laryngitides
+laryngitis/M
+larynx/M
+Laryssa/M
+lasagna/S
+lasagne's
+Lascaux/M
+lasciviousness/MS
+lascivious/YP
+lase
+laser/M
+lashed/U
+lasher/M
+lashing/M
+lash/JGMSRD
+Lassa/M
+Lassen/M
+Lassie/M
+lassie/SM
+lassitude/MS
+lassoer/M
+lasso/GRDMS
+las/SRZG
+lass/SM
+laster/M
+lastingness/M
+lasting/PY
+last/JGSYRD
+Laszlo/M
+Latasha/M
+Latashia/M
+latching/M
+latchkey/SM
+latch's
+latch/UGSD
+latecomer/SM
+lated/A
+late/KA
+lately
+latency/MS
+lateness/MS
+latent/YS
+later/A
+lateral/GDYS
+lateralization
+Lateran/M
+latest/S
+LaTeX/M
+latex/MS
+lathe/M
+latherer/M
+lather/RDMG
+lathery
+lathing/M
+lath/MSRDGZ
+Lathrop/M
+laths
+Latia/M
+latices/M
+Latina/SM
+Latinate
+Latino/S
+Latin/RMS
+latish
+Latisha/M
+latitude/SM
+latitudinal/Y
+latitudinarian/S
+latitudinary
+Lat/M
+Latonya/M
+Latoya/M
+Latrena/M
+Latrina/M
+latrine/MS
+Latrobe/M
+lat/SDRT
+latter/YM
+latte/SR
+lattice/SDMG
+latticework/MS
+latticing/M
+Lattimer/M
+Latvia/M
+Latvian/S
+laudably
+laudanum/MS
+laudatory
+Lauderdale/M
+lauder/M
+Lauder/M
+Laud/MR
+laud/RDSBG
+lauds/M
+Laue/M
+laughableness/M
+laughable/P
+laughably
+laugh/BRDZGJ
+laugher/M
+laughing/MY
+laughingstock/SM
+laughs
+laughter/MS
+Laughton/M
+Launce/M
+launch/AGSD
+launcher/MS
+launching/S
+launchpad/S
+laundered/U
+launderer/M
+launderette/MS
+launder/SDRZJG
+laundress/MS
+laundrette/S
+laundromat/S
+Laundromat/SM
+laundryman/M
+laundrymen
+laundry/MS
+laundrywoman/M
+laundrywomen
+Lauraine/M
+Lauralee/M
+Laural/M
+laura/M
+Laura/M
+Laurasia/M
+laureate/DSNG
+laureateship/SM
+Lauree/M
+Laureen/M
+Laurella/M
+Laurel/M
+laurel/SGMD
+Laure/M
+Laurena/M
+Laurence/M
+Laurene/M
+Lauren/SM
+Laurentian
+Laurent/M
+Lauretta/M
+Laurette/M
+Laurianne/M
+Laurice/M
+Laurie/M
+Lauri/M
+Lauritz/M
+Lauryn/M
+Lausanne/M
+lavage/MS
+lavaliere/MS
+Laval/M
+lava/SM
+lavatory/MS
+lave/GDS
+Lavena/M
+lavender/MDSG
+Laverna/M
+Laverne/M
+Lavern/M
+Lavina/M
+Lavinia/M
+Lavinie/M
+lavishness/MS
+lavish/SRDYPTG
+Lavoisier/M
+Lavonne/M
+Lawanda/M
+lawbreaker/SM
+lawbreaking/MS
+Lawford/M
+lawfulness/SMU
+lawful/PUY
+lawgiver/MS
+lawgiving/M
+lawlessness/MS
+lawless/PY
+Law/M
+lawmaker/MS
+lawmaking/SM
+lawman/M
+lawmen
+lawnmower/S
+lawn/SM
+Lawrence/M
+Lawrenceville/M
+lawrencium/SM
+Lawry/M
+law/SMDG
+Lawson/M
+lawsuit/MS
+Lawton/M
+lawyer/DYMGS
+laxativeness/M
+laxative/PSYM
+laxer/A
+laxes/A
+laxity/SM
+laxness/SM
+lax/PTSRY
+layabout/MS
+Layamon/M
+layaway/S
+lay/CZGSR
+layered/C
+layer/GJDM
+layering/M
+layer's/IC
+layette/SM
+Layla/M
+Lay/M
+layman/M
+laymen
+Layne/M
+Layney/M
+layoff/MS
+layout/SM
+layover/SM
+laypeople
+layperson/S
+lays/AI
+Layton/M
+layup/MS
+laywoman/M
+laywomen
+Lazare/M
+Lazar/M
+Lazaro/M
+Lazarus/M
+laze/DSG
+lazily
+laziness/MS
+lazuli/M
+lazybones/M
+lazy/PTSRDG
+lb
+LBJ/M
+lbs
+LC
+LCD
+LCM
+LDC
+leachate
+Leach/M
+leach/SDG
+Leadbelly/M
+leaded/U
+leadenness/M
+leaden/PGDY
+leaderless
+leader/M
+leadership/MS
+lead/SGZXJRDN
+leadsman/M
+leadsmen
+leafage/MS
+leaf/GSDM
+leafhopper/M
+leafiness/M
+leafless
+leaflet/SDMG
+leafstalk/SM
+leafy/PTR
+leaguer/M
+league/RSDMZG
+Leah/M
+leakage/SM
+leaker/M
+Leakey/M
+leak/GSRDM
+leakiness/MS
+leaky/PRT
+Lea/M
+lea/MS
+Leander/M
+Leandra/M
+leaner/M
+leaning/M
+Lean/M
+Leanna/M
+Leanne/M
+leanness/MS
+Leann/M
+Leanora/M
+Leanor/M
+lean/YRDGTJSP
+leaper/M
+leapfrogged
+leapfrogging
+leapfrog/SM
+leap/RDGZS
+Lear/M
+learnedly
+learnedness/M
+learned/UA
+learner/M
+learning/M
+learns/UA
+learn/SZGJRD
+Leary/M
+lease/ARSDG
+leaseback/MS
+leaseholder/M
+leasehold/SRMZ
+leaser/MA
+lease's
+leash's
+leash/UGSD
+leasing/M
+leas/SRDGZ
+least/S
+leastwise
+leatherette/S
+leather/MDSG
+leathern
+leatherneck/SM
+leathery
+leaven/DMJGS
+leavened/U
+leavening/M
+Leavenworth/M
+leaver/M
+leaves/M
+leave/SRDJGZ
+leaving/M
+Lebanese
+Lebanon/M
+Lebbie/M
+lebensraum
+Lebesgue/M
+Leblanc/M
+lecher/DMGS
+lecherousness/MS
+lecherous/YP
+lechery/MS
+lecithin/SM
+lectern/SM
+lecturer/M
+lecture/RSDZMG
+lectureship/SM
+led
+Leda/M
+Lederberg/M
+ledger/DMG
+ledge/SRMZ
+LED/SM
+Leeanne/M
+Leeann/M
+leech/MSDG
+Leeds/M
+leek/SM
+Leelah/M
+Leela/M
+Leeland/M
+Lee/M
+lee/MZRS
+Leena/M
+leer/DG
+leeriness/MS
+leering/Y
+leery/PTR
+Leesa/M
+Leese/M
+Leeuwenhoek/M
+Leeward/M
+leeward/S
+leeway/MS
+leftism/SM
+leftist/SM
+leftmost
+leftover/MS
+Left/S
+left/TRS
+leftward/S
+Lefty/M
+lefty/SM
+legacy/MS
+legalese/MS
+legalism/SM
+legalistic
+legality/MS
+legalization/MS
+legalize/DSG
+legalized/U
+legal/SY
+legate/AXCNGSD
+legatee/MS
+legate's/C
+legation/AMC
+legato/SM
+legendarily
+legendary/S
+Legendre/M
+legend/SM
+legerdemain/SM
+Leger/SM
+legged
+legginess/MS
+legging/MS
+leggy/PRT
+leghorn/SM
+Leghorn/SM
+legibility/MS
+legible
+legibly
+legionary/S
+legionnaire/SM
+legion/SM
+legislate/SDXVNG
+legislation/M
+legislative/SY
+legislator/SM
+legislature/MS
+legitimacy/MS
+legitimate/SDNGY
+legitimation/M
+legitimatize/SDG
+legitimization/MS
+legitimize/RSDG
+legit/S
+legless
+legman/M
+legmen
+leg/MS
+Lego/M
+Legra/M
+Legree/M
+legroom/MS
+legstraps
+legume/SM
+leguminous
+legwork/SM
+Lehigh/M
+Lehman/M
+Leia/M
+Leibniz/M
+Leicester/SM
+Leiden/M
+Leif/M
+Leigha/M
+Leigh/M
+Leighton/M
+Leilah/M
+Leila/M
+lei/MS
+Leipzig/M
+Leisha/M
+leisureliness/MS
+leisurely/P
+leisure/SDYM
+leisurewear
+leitmotif/SM
+leitmotiv/MS
+Lek/M
+Lelah/M
+Lela/M
+Leland/M
+Lelia/M
+Lemaitre/M
+Lemar/M
+Lemke/M
+Lem/M
+lemma/MS
+lemme/GJ
+Lemmie/M
+lemming/M
+Lemmy/M
+lemonade/SM
+lemon/GSDM
+lemony
+Lemuel/M
+Lemuria/M
+lemur/MS
+Lena/M
+Lenard/M
+Lenci/M
+lender/M
+lend/SRGZ
+Lenee/M
+Lenette/M
+lengthener/M
+lengthen/GRD
+lengthily
+lengthiness/MS
+length/MNYX
+lengths
+lengthwise
+lengthy/TRP
+lenience/S
+leniency/MS
+lenient/SY
+Leningrad/M
+Leninism/M
+Leninist
+Lenin/M
+lenitive/S
+Lenka/M
+Len/M
+Le/NM
+Lenna/M
+Lennard/M
+Lennie/M
+Lennon/M
+Lenny/M
+Lenoir/M
+Leno/M
+Lenora/M
+Lenore/M
+lens/SRDMJGZ
+lent/A
+lenticular
+lentil/SM
+lento/S
+Lent/SMN
+Leodora/M
+Leoine/M
+Leola/M
+Leoline/M
+Leo/MS
+Leona/M
+Leonanie/M
+Leonard/M
+Leonardo/M
+Leoncavallo/M
+Leonelle/M
+Leonel/M
+Leone/M
+Leonerd/M
+Leonhard/M
+Leonidas/M
+Leonid/M
+Leonie/M
+leonine
+Leon/M
+Leonora/M
+Leonore/M
+Leonor/M
+Leontine/M
+Leontyne/M
+leopardess/SM
+leopard/MS
+leopardskin
+Leopold/M
+Leopoldo/M
+Leopoldville/M
+Leora/M
+leotard/MS
+leper/SM
+Lepidus/M
+Lepke/M
+leprechaun/SM
+leprosy/MS
+leprous
+lepta
+lepton/SM
+Lepus/M
+Lerner/M
+Leroi/M
+Leroy/M
+Lesa/M
+lesbianism/MS
+lesbian/MS
+Leshia/M
+lesion/DMSG
+Lesley/M
+Leslie/M
+Lesli/M
+Lesly/M
+Lesotho/M
+lessee/MS
+lessen/GDS
+Lesseps/M
+lesser
+lesses
+Lessie/M
+lessing
+lesson/DMSG
+lessor/MS
+less/U
+Lester/M
+lest/R
+Les/Y
+Lesya/M
+Leta/M
+letdown/SM
+lethality/M
+lethal/YS
+Letha/M
+lethargic
+lethargically
+lethargy/MS
+Lethe/M
+Lethia/M
+Leticia/M
+Letisha/M
+let/ISM
+Letitia/M
+Letizia/M
+Letta/M
+letterbox/S
+lettered/U
+letterer/M
+letterhead/SM
+lettering/M
+letter/JSZGRDM
+letterman/M
+Letterman/M
+lettermen
+letterpress/MS
+Lettie/M
+Letti/M
+letting/S
+lettuce/SM
+Letty/M
+letup/MS
+leukemia/SM
+leukemic/S
+leukocyte/MS
+Leupold/M
+Levant/M
+leveeing
+levee/SDM
+leveled/U
+leveler/M
+levelheadedness/S
+levelheaded/P
+leveling/U
+levelness/SM
+level/STZGRDYP
+leverage/MGDS
+lever/SDMG
+Levesque/M
+Levey/M
+Leviathan
+leviathan/MS
+levier/M
+Levi/MS
+Levine/M
+Levin/M
+levitate/XNGDS
+levitation/M
+Leviticus/M
+Levitt/M
+levity/MS
+Lev/M
+Levon/M
+Levy/M
+levy/SRDZG
+lewdness/MS
+lewd/PYRT
+Lewellyn/M
+Lewes
+Lewie/M
+Lewinsky/M
+lewis/M
+Lewis/M
+Lewiss
+Lew/M
+lex
+lexeme/MS
+lexical/Y
+lexicographer/MS
+lexicographic
+lexicographical/Y
+lexicography/SM
+lexicon/SM
+Lexie/M
+Lexi/MS
+Lexine/M
+Lexington/M
+Lexus/M
+Lexy/M
+Leyden/M
+Leyla/M
+Lezley/M
+Lezlie/M
+lg
+Lhasa/SM
+Lhotse/M
+liability/SAM
+liable/AP
+liaise/GSD
+liaison/SM
+Lia/M
+Liam/M
+Liana/M
+Liane/M
+Lian/M
+Lianna/M
+Lianne/M
+liar/MS
+libation/SM
+libbed
+Libbey/M
+Libbie/M
+Libbi/M
+libbing
+Libby/M
+libeler/M
+libel/GMRDSZ
+libelous/Y
+Liberace/M
+liberalism/MS
+liberality/MS
+liberalization/SM
+liberalized/U
+liberalize/GZSRD
+liberalizer/M
+liberalness/MS
+liberal/YSP
+liberate/NGDSCX
+liberationists
+liberation/MC
+liberator/SCM
+Liberia/M
+Liberian/S
+libertarianism/M
+libertarian/MS
+libertine/MS
+liberty/MS
+libidinal
+libidinousness/M
+libidinous/PY
+libido/MS
+Lib/M
+lib/MS
+librarian/MS
+library/MS
+Libra/SM
+libretoes
+libretos
+librettist/MS
+libretto/MS
+Libreville/M
+Librium/M
+Libya/M
+Libyan/S
+lice/M
+licensed/AU
+licensee/SM
+license/MGBRSD
+licenser/M
+licenses/A
+licensing/A
+licensor/M
+licentiate/MS
+licentiousness/MS
+licentious/PY
+Licha/M
+lichee's
+lichen/DMGS
+Lichtenstein/M
+Lichter/M
+licit/Y
+licked/U
+lickerish
+licker/M
+lick/GRDSJ
+licking/M
+licorice/SM
+Lida/M
+lidded
+lidding
+Lidia/M
+lidless
+lid/MS
+lido/MS
+Lieberman/M
+Liebfraumilch/M
+Liechtenstein/RMZ
+lied/MR
+lie/DRS
+Lief/M
+liefs/A
+lief/TSR
+Liege/M
+liege/SR
+Lie/M
+lien/SM
+lier/IMA
+lies/A
+Liesa/M
+lieu/SM
+lieut
+lieutenancy/MS
+lieutenant/SM
+Lieut/M
+lifeblood/SM
+lifeboat/SM
+lifebuoy/S
+lifeforms
+lifeguard/MDSG
+lifelessness/SM
+lifeless/PY
+lifelikeness/M
+lifelike/P
+lifeline/SM
+lifelong
+life/MZR
+lifer/M
+lifesaver/SM
+lifesaving/S
+lifespan/S
+lifestyle/S
+lifetaking
+lifetime/MS
+lifework/MS
+LIFO
+lifter/M
+lift/GZMRDS
+liftoff/MS
+ligament/MS
+ligand/MS
+ligate/XSDNG
+ligation/M
+ligature/DSGM
+light/ADSCG
+lighted/U
+lightener/M
+lightening/M
+lighten/ZGDRS
+lighter/CM
+lightered
+lightering
+lighters
+lightest
+lightface/SDM
+lightheaded
+lightheartedness/MS
+lighthearted/PY
+lighthouse/MS
+lighting/MS
+lightly
+lightness/MS
+lightning/SMD
+lightproof
+light's
+lightship/SM
+lightweight/S
+ligneous
+lignite/MS
+lignum
+likability/MS
+likableness/MS
+likable/P
+likeability's
+liked/E
+likelihood/MSU
+likely/UPRT
+likeness/MSU
+liken/GSD
+liker/E
+liker's
+likes/E
+likest
+like/USPBY
+likewise
+liking/SM
+lilac/MS
+Lilah/M
+Lila/SM
+Lilia/MS
+Liliana/M
+Liliane/M
+Lilian/M
+Lilith/M
+Liliuokalani/M
+Lilla/M
+Lille/M
+Lillian/M
+Lillie/M
+Lilli/MS
+lilliputian/S
+Lilliputian/SM
+Lilliput/M
+Lilllie/M
+Lilly/M
+Lil/MY
+Lilongwe/M
+lilting/YP
+lilt/MDSG
+Lilyan/M
+Lily/M
+lily/MSD
+Lima/M
+Limbaugh/M
+limbered/U
+limberness/SM
+limber/RDYTGP
+limbers/U
+limbic
+limbless
+Limbo
+limbo/GDMS
+limb/SGZRDM
+Limburger/SM
+limeade/SM
+lime/DSMG
+limekiln/M
+limelight/DMGS
+limerick/SM
+limestone/SM
+limitability
+limitably
+limitation/MCS
+limit/CSZGRD
+limitedly/U
+limitedness/M
+limited/PSY
+limiter/M
+limiting/S
+limitlessness/SM
+limitless/PY
+limit's
+limn/GSD
+Limoges/M
+limo/S
+limousine/SM
+limper/M
+limpet/SM
+limpidity/MS
+limpidness/SM
+limpid/YP
+limpness/MS
+Limpopo/M
+limp/SGTPYRD
+Li/MY
+limy/TR
+linage/MS
+Lina/M
+linchpin/MS
+Linc/M
+Lincoln/SM
+Linda/M
+Lindbergh/M
+Lindberg/M
+linden/MS
+Lindholm/M
+Lindie/M
+Lindi/M
+Lind/M
+Lindon/M
+Lindquist/M
+Lindsay/M
+Lindsey/M
+Lindstrom/M
+Lindsy/M
+Lindy/M
+line/AGDS
+lineage/SM
+lineal/Y
+Linea/M
+lineament/MS
+linearity/MS
+linearize/SDGNB
+linear/Y
+linebacker/SM
+lined/U
+linefeed
+Linell/M
+lineman/M
+linemen
+linen/SM
+liner/SM
+line's
+linesman/M
+linesmen
+Linet/M
+Linette/M
+lineup/S
+lingerer/M
+lingerie/SM
+lingering/Y
+linger/ZGJRD
+lingoes
+lingo/M
+lingual/SY
+lingua/M
+linguine
+linguini's
+linguistically
+linguistic/S
+linguistics/M
+linguist/SM
+ling/ZR
+liniment/MS
+lining/SM
+linkable
+linkage/SM
+linked/A
+linker/S
+linking/S
+Link/M
+link's
+linkup/S
+link/USGD
+Lin/M
+Linnaeus/M
+Linnea/M
+Linnell/M
+Linnet/M
+linnet/SM
+Linnie/M
+Linn/M
+Linoel/M
+linoleum/SM
+lino/M
+Linotype/M
+linseed/SM
+lintel/SM
+linter/M
+Linton/M
+lint/SMR
+linty/RST
+Linus/M
+Linux/M
+Linwood/M
+Linzy/M
+Lionello/M
+Lionel/M
+lioness/SM
+lionhearted
+lionization/SM
+lionizer/M
+lionize/ZRSDG
+Lion/M
+lion/MS
+lipase/M
+lipid/MS
+lip/MS
+liposuction/S
+lipped
+lipper
+Lippi/M
+lipping
+Lippmann/M
+lippy/TR
+lipread/GSRJ
+Lipschitz/M
+Lipscomb/M
+lipstick/MDSG
+Lipton/M
+liq
+liquefaction/SM
+liquefier/M
+liquefy/DRSGZ
+liqueur/DMSG
+liquidate/GNXSD
+liquidation/M
+liquidator/SM
+liquidity/SM
+liquidizer/M
+liquidize/ZGSRD
+liquidness/M
+liquid/SPMY
+liquorice/SM
+liquorish
+liquor/SDMG
+lira/M
+Lira/M
+lire
+Lisabeth/M
+Lisa/M
+Lisbeth/M
+Lisbon/M
+Lise/M
+Lisetta/M
+Lisette/M
+Lisha/M
+Lishe/M
+Lisle/M
+lisle/SM
+lisper/M
+lisp/MRDGZS
+Lissajous/M
+Lissa/M
+Lissie/M
+Lissi/M
+Liss/M
+lissomeness/M
+lissome/P
+lissomness/M
+Lissy/M
+listed/U
+listener/M
+listen/ZGRD
+Listerine/M
+lister/M
+Lister/M
+listing/M
+list/JMRDNGZXS
+listlessness/SM
+listless/PY
+Liston/M
+Liszt/M
+Lita/M
+litany/MS
+litchi/SM
+literacy/MS
+literalism/M
+literalistic
+literalness/MS
+literal/PYS
+literariness/SM
+literary/P
+literate/YNSP
+literati
+literation/M
+literature/SM
+liter/M
+lite/S
+litheness/SM
+lithe/PRTY
+lithesome
+lithium/SM
+lithograph/DRMGZ
+lithographer/M
+lithographic
+lithographically
+lithographs
+lithography/MS
+lithology/M
+lithosphere/MS
+lithospheric
+Lithuania/M
+Lithuanian/S
+litigant/MS
+litigate/NGXDS
+litigation/M
+litigator/SM
+litigiousness/MS
+litigious/PY
+litmus/SM
+litotes/M
+lit/RZS
+littérateur/S
+litterbug/SM
+litter/SZGRDM
+Little/M
+littleneck/M
+littleness/SM
+little/RSPT
+Littleton/M
+Litton/M
+littoral/S
+liturgical/Y
+liturgic/S
+liturgics/M
+liturgist/MS
+liturgy/SM
+Liuka/M
+livability/MS
+livableness/M
+livable/U
+livably
+Liva/M
+lived/A
+livelihood/SM
+liveliness/SM
+livelong/S
+lively/RTP
+liveness/M
+liven/SDG
+liver/CSGD
+liveried
+liverish
+Livermore/M
+Liverpool/M
+Liverpudlian/MS
+liver's
+liverwort/SM
+liverwurst/SM
+livery/CMS
+liveryman/MC
+liverymen/C
+lives/A
+lives's
+livestock/SM
+live/YHZTGJDSRPB
+Livia/M
+lividness/M
+livid/YP
+livingness/M
+Livingstone/M
+Livingston/M
+living/YP
+Liv/M
+Livonia/M
+Livvie/M
+Livvy/M
+Livvyy/M
+Livy/M
+Lizabeth/M
+Liza/M
+lizard/MS
+Lizbeth/M
+Lizette/M
+Liz/M
+Lizzie/M
+Lizzy/M
+l/JGVXT
+Ljubljana/M
+LL
+llama/SM
+llano/SM
+LLB
+ll/C
+LLD
+Llewellyn/M
+Lloyd/M
+Llywellyn/M
+LNG
+lo
+loadable
+loaded/A
+loader/MU
+loading/MS
+load's/A
+loads/A
+loadstar's
+loadstone's
+load/SURDZG
+loafer/M
+Loafer/S
+loaf/SRDMGZ
+loam/SMDG
+loamy/RT
+loaner/M
+loaning/M
+loan/SGZRDMB
+loansharking/S
+loanword/S
+loathe
+loather/M
+loathing/M
+loath/JPSRDYZG
+loathness/M
+loathsomeness/MS
+loathsome/PY
+loaves/M
+Lobachevsky/M
+lobar
+lobbed
+lobber/MS
+lobbing
+lobby/GSDM
+lobbyist/MS
+lobe/SM
+lob/MDSG
+lobotomist
+lobotomize/GDS
+lobotomy/MS
+lobster/MDGS
+lobularity
+lobular/Y
+lobule/SM
+locale/MS
+localisms
+locality/MS
+localization/MS
+localized/U
+localizer/M
+localizes/U
+localize/ZGDRS
+local/SGDY
+locatable
+locate/AXESDGN
+locater/M
+locational/Y
+location/EMA
+locative/S
+locator's
+Lochinvar/M
+loch/M
+lochs
+loci/M
+lockable
+Lockean/M
+locked/A
+Locke/M
+locker/SM
+locket/SM
+Lockhart/M
+Lockheed/M
+Lockian/M
+locking/S
+lockjaw/SM
+Lock/M
+locknut/M
+lockout/MS
+lock's
+locksmithing/M
+locksmith/MG
+locksmiths
+lockstep/S
+lock/UGSD
+lockup/MS
+Lockwood/M
+locomotion/SM
+locomotive/YMS
+locomotor
+locomotory
+loco/SDMG
+locoweed/MS
+locus/M
+locust/SM
+locution/MS
+lode/SM
+lodestar/MS
+lodestone/MS
+lodged/E
+lodge/GMZSRDJ
+Lodge/M
+lodgepole
+lodger/M
+lodges/E
+lodging/M
+lodgment/M
+Lodovico/M
+Lodowick/M
+Lodz
+Loeb/M
+Loella/M
+Loewe/M
+Loewi/M
+lofter/M
+loftily
+loftiness/SM
+loft/SGMRD
+lofty/PTR
+loganberry/SM
+Logan/M
+logarithmic
+logarithmically
+logarithm/MS
+logbook/MS
+loge/SMNX
+logged/U
+loggerhead/SM
+logger/SM
+loggia/SM
+logging/MS
+logicality/MS
+logicalness/M
+logical/SPY
+logician/SM
+logic/SM
+login/S
+logion/M
+logistical/Y
+logistic/MS
+logjam/SM
+LOGO
+logo/SM
+logotype/MS
+logout
+logrolling/SM
+log's/K
+log/SM
+logy/RT
+Lohengrin/M
+loincloth/M
+loincloths
+loin/SM
+Loire/M
+Loise/M
+Lois/M
+loiterer/M
+loiter/RDJSZG
+Loki/M
+Lola/M
+Loleta/M
+Lolita/M
+loller/M
+lollipop/MS
+loll/RDGS
+Lolly/M
+lolly/SM
+Lombardi/M
+Lombard/M
+Lombardy/M
+Lomb/M
+Lome
+Lona/M
+Londonderry/M
+Londoner/M
+London/RMZ
+Lonee/M
+loneliness/SM
+lonely/TRP
+loneness/M
+lone/PYZR
+loner/M
+lonesomeness/MS
+lonesome/PSY
+longboat/MS
+longbow/SM
+longed/K
+longeing
+longer/K
+longevity/MS
+Longfellow/M
+longhair/SM
+longhand/SM
+longhorn/SM
+longing/MY
+longish
+longitude/MS
+longitudinal/Y
+long/JGTYRDPS
+Long/M
+longness/M
+longshoreman/M
+longshoremen
+longsighted
+longs/K
+longstanding
+Longstreet/M
+longsword
+longterm
+longtime
+Longueuil/M
+longueur/SM
+longways
+longword/SM
+Loni/M
+Lon/M
+Lonna/M
+Lonnard/M
+Lonnie/M
+Lonni/M
+Lonny/M
+loofah/M
+loofahs
+lookahead
+lookalike/S
+looker/M
+look/GZRDS
+lookout/MS
+lookup/SM
+looming/M
+Loomis/M
+loom/MDGS
+loon/MS
+loony/SRT
+looper/M
+loophole/MGSD
+loop/MRDGS
+loopy/TR
+loosed/U
+looseleaf
+loosener/M
+looseness/MS
+loosen/UDGS
+loose/SRDPGTY
+looses/U
+loosing/M
+looter/M
+loot/MRDGZS
+loper/M
+lope/S
+Lopez/M
+lopped
+lopper/MS
+lopping
+lop/SDRG
+lopsidedness/SM
+lopsided/YP
+loquaciousness/MS
+loquacious/YP
+loquacity/SM
+Loraine/M
+Lorain/M
+Loralee/M
+Loralie/M
+Loralyn/M
+Lora/M
+Lorant/M
+lording/M
+lordliness/SM
+lordly/PTR
+Lord/MS
+lord/MYDGS
+lordship/SM
+Lordship/SM
+Loree/M
+Loreen/M
+Lorelei/M
+Lorelle/M
+lore/MS
+Lorena/M
+Lorene/M
+Loren/SM
+Lorentzian/M
+Lorentz/M
+Lorenza/M
+Lorenz/M
+Lorenzo/M
+Loretta/M
+Lorette/M
+lorgnette/SM
+Loria/M
+Lorianna/M
+Lorianne/M
+Lorie/M
+Lorilee/M
+Lorilyn/M
+Lori/M
+Lorinda/M
+Lorine/M
+Lorin/M
+loris/SM
+Lorita/M
+lorn
+Lorna/M
+Lorne/M
+Lorraine/M
+Lorrayne/M
+Lorre/M
+Lorrie/M
+Lorri/M
+Lorrin/M
+lorryload/S
+Lorry/M
+lorry/SM
+Lory/M
+Los
+loser/M
+lose/ZGJBSR
+lossage
+lossless
+loss/SM
+lossy/RT
+lost/P
+Lothaire/M
+Lothario/MS
+lotion/MS
+Lot/M
+lot/MS
+Lotta/M
+lotted
+Lotte/M
+lotter
+lottery/MS
+Lottie/M
+Lotti/M
+lotting
+Lott/M
+lotto/MS
+Lotty/M
+lotus/SM
+louden/DG
+loudhailer/S
+loudly/RT
+loudmouth/DM
+loudmouths
+loudness/MS
+loudspeaker/SM
+loudspeaking
+loud/YRNPT
+Louella/M
+Louie/M
+Louisa/M
+Louise/M
+Louisette/M
+Louisiana/M
+Louisianan/S
+Louisianian/S
+Louis/M
+Louisville/M
+Lou/M
+lounger/M
+lounge/SRDZG
+Lourdes/M
+lour/GSD
+louse/CSDG
+louse's
+lousewort/M
+lousily
+lousiness/MS
+lousy/PRT
+loutishness/M
+loutish/YP
+Loutitia/M
+lout/SGMD
+louver/DMS
+L'Ouverture
+Louvre/M
+lovableness/MS
+lovable/U
+lovably
+lovebird/SM
+lovechild
+Lovecraft/M
+love/DSRMYZGJB
+loved/U
+Lovejoy/M
+Lovelace/M
+Loveland/M
+lovelessness/M
+loveless/YP
+lovelies
+lovelinesses
+loveliness/UM
+Lovell/M
+lovelornness/M
+lovelorn/P
+lovely/URPT
+Love/M
+lovemaking/SM
+lover/YMG
+lovesick
+lovestruck
+lovingly
+lovingness/M
+loving/U
+lowborn
+lowboy/SM
+lowbrow/MS
+lowdown/S
+Lowell/M
+Lowe/M
+lowercase/GSD
+lower/DG
+lowermost
+Lowery/M
+lowish
+lowland/RMZS
+Lowlands/M
+lowlife/SM
+lowlight/MS
+lowliness/MS
+lowly/PTR
+lowness/MS
+low/PDRYSZTG
+Lowrance/M
+lox/MDSG
+loyaler
+loyalest
+loyal/EY
+loyalism/SM
+loyalist/SM
+loyalty/EMS
+Loyang/M
+Loydie/M
+Loyd/M
+Loy/M
+Loyola/M
+lozenge/SDM
+LP
+LPG
+LPN/S
+Lr
+ls
+l's
+L's
+LSD
+ltd
+Ltd/M
+Lt/M
+Luanda/M
+Luann/M
+luau/MS
+lubber/YMS
+Lubbock/M
+lube/DSMG
+lubricant/SM
+lubricate/VNGSDX
+lubrication/M
+lubricator/MS
+lubricious/Y
+lubricity/SM
+Lubumbashi/M
+Lucais/M
+Luca/MS
+Luce/M
+lucent/Y
+Lucerne/M
+Lucho/M
+Lucia/MS
+Luciana/M
+Lucian/M
+Luciano/M
+lucidity/MS
+lucidness/MS
+lucid/YP
+Lucie/M
+Lucien/M
+Lucienne/M
+Lucifer/M
+Lucila/M
+Lucile/M
+Lucilia/M
+Lucille/M
+Luci/MN
+Lucina/M
+Lucinda/M
+Lucine/M
+Lucio/M
+Lucita/M
+Lucite/MS
+Lucius/M
+luck/GSDM
+luckier/U
+luckily/U
+luckiness/UMS
+luckless
+Lucknow/M
+Lucky/M
+lucky/RSPT
+lucrativeness/SM
+lucrative/YP
+lucre/MS
+Lucretia/M
+Lucretius/M
+lucubrate/GNSDX
+lucubration/M
+Lucy/M
+Luddite/SM
+Ludhiana/M
+ludicrousness/SM
+ludicrous/PY
+Ludlow/M
+Ludmilla/M
+ludo/M
+Ludovico/M
+Ludovika/M
+Ludvig/M
+Ludwig/M
+Luella/M
+Luelle/M
+luff/GSDM
+Lufthansa/M
+Luftwaffe/M
+luge/MC
+Luger/M
+luggage/SM
+lugged
+lugger/SM
+lugging
+Lugosi/M
+lug/RS
+lugsail/SM
+lugubriousness/MS
+lugubrious/YP
+Luigi/M
+Luisa/M
+Luise/M
+Luis/M
+Lukas/M
+Luke/M
+lukewarmness/SM
+lukewarm/PY
+Lula/M
+Lulita/M
+lullaby/GMSD
+lull/SDG
+lulu/M
+Lulu/M
+Lu/M
+lumbago/SM
+lumbar/S
+lumberer/M
+lumbering/M
+lumberjack/MS
+lumberman/M
+lumbermen
+lumber/RDMGZSJ
+lumberyard/MS
+lumen/M
+Lumière/M
+luminance/M
+luminary/MS
+luminescence/SM
+luminescent
+luminosity/MS
+luminousness/M
+luminous/YP
+lummox/MS
+lumper/M
+lumpiness/MS
+lumpishness/M
+lumpish/YP
+lump/SGMRDN
+lumpy/TPR
+lunacy/MS
+Luna/M
+lunar/S
+lunary
+lunate/YND
+lunatic/S
+lunation/M
+luncheonette/SM
+luncheon/SMDG
+luncher/M
+lunch/GMRSD
+lunchpack
+lunchroom/MS
+lunchtime/MS
+Lundberg/M
+Lund/M
+Lundquist/M
+lune/M
+lunge/MS
+lunger/M
+lungfish/SM
+lungful
+lung/SGRDM
+lunkhead/SM
+Lupe/M
+lupine/SM
+Lupus/M
+lupus/SM
+Lura/M
+lurcher/M
+lurch/RSDG
+lure/DSRG
+lurer/M
+Lurette/M
+lurex
+Luria/M
+luridness/SM
+lurid/YP
+lurker/M
+lurk/GZSRD
+Lurleen/M
+Lurlene/M
+Lurline/M
+Lusaka/M
+Lusa/M
+lusciousness/MS
+luscious/PY
+lushness/MS
+lush/YSRDGTP
+Lusitania/M
+luster/GDM
+lustering/M
+lusterless
+lustfulness/M
+lustful/PY
+lustily
+lustiness/MS
+lust/MRDGZS
+lustrousness/M
+lustrous/PY
+lusty/PRT
+lutanist/MS
+lute/DSMG
+lutenist/MS
+Lutero/M
+lutetium/MS
+Lutheranism/MS
+Lutheran/SM
+Luther/M
+luting/M
+Lutz
+Luxembourgian
+Luxembourg/RMZ
+Luxemburg's
+luxe/MS
+luxuriance/MS
+luxuriant/Y
+luxuriate/GNSDX
+luxuriation/M
+luxuriousness/SM
+luxurious/PY
+luxury/MS
+Luz/M
+Luzon/M
+L'vov
+Lyallpur/M
+lyceum/MS
+lychee's
+lycopodium/M
+Lycra/S
+Lycurgus/M
+Lyda/M
+Lydia/M
+Lydian/S
+Lydie/M
+Lydon/M
+lye/JSMG
+Lyell/M
+lying/Y
+Lyle/M
+Lyly/M
+Lyman/M
+Lyme/M
+lymphatic/S
+lymph/M
+lymphocyte/SM
+lymphoid
+lymphoma/MS
+lymphs
+Ly/MY
+Lynchburg/M
+lyncher/M
+lynching/M
+Lynch/M
+lynch/ZGRSDJ
+Lynda/M
+Lyndell/M
+Lyndel/M
+Lynde/M
+Lyndon/M
+Lyndsay/M
+Lyndsey/M
+Lyndsie/M
+Lyndy/M
+Lynea/M
+Lynelle/M
+Lynette/M
+Lynett/M
+Lyn/M
+Lynna/M
+Lynnea/M
+Lynnelle/M
+Lynnell/M
+Lynne/M
+Lynnet/M
+Lynnette/M
+Lynnett/M
+Lynn/M
+Lynsey/M
+lynx/MS
+Lyon/SM
+Lyra/M
+lyrebird/MS
+lyre/SM
+lyricalness/M
+lyrical/YP
+lyricism/SM
+lyricist/SM
+lyric/S
+Lysenko/M
+lysine/M
+Lysistrata/M
+Lysol/M
+Lyssa/M
+LyX/M
+MA
+Maalox/M
+ma'am
+Mabelle/M
+Mabel/M
+Mable/M
+Mab/M
+macabre/Y
+macadamize/SDG
+macadam/SM
+Macao/M
+macaque/SM
+macaroni/SM
+macaroon/MS
+Macarthur/M
+MacArthur/M
+Macaulay/M
+macaw/SM
+Macbeth/M
+Maccabees/M
+Maccabeus/M
+Macdonald/M
+MacDonald/M
+MacDraw/M
+Macedonia/M
+Macedonian/S
+Macedon/M
+mace/MS
+Mace/MS
+macerate/DSXNG
+maceration/M
+macer/M
+Macgregor/M
+MacGregor/M
+machete/SM
+Machiavellian/S
+Machiavelli/M
+machinate/SDXNG
+machination/M
+machinelike
+machine/MGSDB
+machinery/SM
+machinist/MS
+machismo/SM
+Mach/M
+macho/S
+Machs
+Macias/M
+Macintosh/M
+MacIntosh/M
+macintosh's
+Mackenzie/M
+MacKenzie/M
+mackerel/SM
+Mackinac/M
+Mackinaw
+mackinaw/SM
+mackintosh/SM
+mack/M
+Mack/M
+MacLeish/M
+Macmillan/M
+MacMillan/M
+Macon/SM
+MacPaint/M
+macramé/S
+macrobiotic/S
+macrobiotics/M
+macrocosm/MS
+macrodynamic
+macroeconomic/S
+macroeconomics/M
+macromolecular
+macromolecule/SM
+macron/MS
+macrophage/SM
+macroscopic
+macroscopically
+macrosimulation
+macro/SM
+macrosocioeconomic
+Mac/SGMD
+mac/SGMDR
+Macy/M
+Madagascan/SM
+Madagascar/M
+Madalena/M
+Madalyn/M
+Mada/M
+madame/M
+Madame/MS
+madam/SM
+madcap/S
+Maddalena/M
+madded
+madden/GSD
+maddening/Y
+Madden/M
+madder/MS
+maddest
+Maddie/M
+Maddi/M
+madding
+Maddox/M
+Maddy/M
+made/AU
+Madeira/SM
+Madelaine/M
+Madeleine/M
+Madelena/M
+Madelene/M
+Madelina/M
+Madeline/M
+Madelin/M
+Madella/M
+Madelle/M
+Madel/M
+Madelon/M
+Madelyn/M
+mademoiselle/MS
+Madge/M
+madhouse/SM
+Madhya/M
+Madison/M
+Madlen/M
+Madlin/M
+madman/M
+madmen
+madness/SM
+Madonna/MS
+mad/PSY
+Madras
+madras/SM
+Madrid/M
+madrigal/MSG
+Madsen/M
+Madurai/M
+madwoman/M
+madwomen
+Mady/M
+Maegan/M
+Maelstrom/M
+maelstrom/SM
+Mae/M
+maestro/MS
+Maeterlinck/M
+Mafia/MS
+mafia/S
+mafiosi
+mafioso/M
+Mafioso/S
+MAG
+magazine/DSMG
+Magdaia/M
+Magdalena/M
+Magdalene/M
+Magdalen/M
+Magda/M
+Magellanic
+Magellan/M
+magenta/MS
+magged
+Maggee/M
+Maggie/M
+Maggi/M
+magging
+maggot/MS
+maggoty/RT
+Maggy/M
+magi
+magical/Y
+magician/MS
+magicked
+magicking
+magic/SM
+Magill/M
+Magi/M
+Maginot/M
+magisterial/Y
+magistracy/MS
+magistrate/MS
+Mag/M
+magma/SM
+magnanimity/SM
+magnanimosity
+magnanimous/PY
+magnate/SM
+magnesia/MS
+magnesite/M
+magnesium/SM
+magnetically
+magnetic/S
+magnetics/M
+magnetism/SM
+magnetite/SM
+magnetizable
+magnetization/ASCM
+magnetize/CGDS
+magnetized/U
+magnetodynamics
+magnetohydrodynamical
+magnetohydrodynamics/M
+magnetometer/MS
+magneto/MS
+magnetosphere/M
+magnetron/M
+magnet/SM
+magnification/M
+magnificence/SM
+magnificent/Y
+magnified/U
+magnify/DRSGNXZ
+magniloquence/MS
+magniloquent
+Magnitogorsk/M
+magnitude/SM
+magnolia/SM
+Magnum
+magnum/SM
+Magnuson/M
+Magog/M
+Magoo/M
+magpie/SM
+Magritte/M
+Magruder/M
+mag/S
+Magsaysay/M
+Maguire/SM
+Magus/M
+Magyar/MS
+Mahabharata
+Mahala/M
+Mahalia/M
+maharajah/M
+maharajahs
+maharanee's
+maharani/MS
+Maharashtra/M
+maharishi/SM
+mahatma/SM
+Mahavira/M
+Mahayana/M
+Mahayanist
+Mahdi/M
+Mahfouz/M
+Mahican/SM
+mahjong's
+Mahler/M
+Mahmoud/M
+Mahmud/M
+mahogany/MS
+Mahomet's
+mahout/SM
+Maia/M
+Maible/M
+maidenhair/MS
+maidenhead/SM
+maidenhood/SM
+maidenly/P
+maiden/YM
+maidservant/MS
+maid/SMNX
+maier
+Maier/M
+Maiga/M
+Maighdiln/M
+Maigret/M
+mailbag/MS
+mailbox/MS
+mail/BSJGZMRD
+mailer/M
+Mailer/M
+Maillol/M
+maillot/SM
+mailman/M
+mailmen
+Maiman/M
+maimedness/M
+maimed/P
+maimer/M
+Maimonides/M
+Mai/MR
+maim/SGZRD
+mainbrace/M
+Maine/MZR
+Mainer/M
+mainframe/MS
+mainlander/M
+mainland/SRMZ
+mainliner/M
+mainline/RSDZG
+mainly
+mainmast/SM
+main/SA
+mainsail/SM
+mains/M
+mainspring/SM
+mainstay/MS
+mainstream/DRMSG
+maintainability
+maintainable/U
+maintain/BRDZGS
+maintained/U
+maintainer/M
+maintenance/SM
+maintop/SM
+maiolica's
+Maire/M
+Mair/M
+Maisey/M
+Maisie/M
+maisonette/MS
+Maison/M
+Maitilde/M
+maize/MS
+Maj
+Maje/M
+majestic
+majestically
+majesty/MS
+Majesty/MS
+majolica/SM
+Majorca/M
+major/DMGS
+majordomo/S
+majorette/SM
+majority/SM
+Major/M
+Majuro/M
+makable
+Makarios/M
+makefile/S
+makeover/S
+Maker/M
+maker/SM
+makeshift/S
+make/UGSA
+makeup/MS
+making/SM
+Malabar/M
+Malabo/M
+Malacca/M
+Malachi/M
+malachite/SM
+maladapt/DV
+maladjust/DLV
+maladjustment/MS
+maladministration
+maladroitness/MS
+maladroit/YP
+malady/MS
+Malagasy/M
+malaise/SM
+Mala/M
+Malamud/M
+malamute/SM
+Malanie/M
+malaprop
+malapropism/SM
+Malaprop/M
+malarial
+malaria/MS
+malarious
+malarkey/SM
+malathion/S
+Malawian/S
+Malawi/M
+Malayalam/M
+Malaya/M
+Malayan/MS
+Malaysia/M
+Malaysian/S
+Malay/SM
+Malchy/M
+Malcolm/M
+malcontentedness/M
+malcontented/PY
+malcontent/SMD
+Maldive/SM
+Maldivian/S
+Maldonado/M
+maledict
+malediction/MS
+malefaction/MS
+malefactor/MS
+malefic
+maleficence/MS
+maleficent
+Male/M
+Malena/M
+maleness/MS
+male/PSM
+malevolence/S
+malevolencies
+malevolent/Y
+malfeasance/SM
+malfeasant
+malformation/MS
+malformed
+malfunction/SDG
+Malia/M
+Malian/S
+Malibu/M
+malice/MGSD
+maliciousness/MS
+malicious/YU
+malignancy/SM
+malignant/YS
+malign/GSRDYZ
+malignity/MS
+Mali/M
+Malina/M
+Malinda/M
+Malinde/M
+malingerer/M
+malinger/GZRDS
+Malinowski/M
+Malissa/M
+Malissia/M
+mallard/SM
+Mallarmé/M
+malleability/SM
+malleableness/M
+malleable/P
+mallet/MS
+Mallissa/M
+Mallorie/M
+Mallory/M
+mallow/MS
+mall/SGMD
+Mal/M
+malnourished
+malnutrition/SM
+malocclusion/MS
+malodorous
+Malone/M
+Malorie/M
+Malory/M
+malposed
+malpractice/SM
+Malraux/M
+Malta/M
+malted/S
+Maltese
+Malthusian/S
+Malthus/M
+malting/M
+maltose/SM
+maltreat/GDSL
+maltreatment/S
+malt/SGMD
+malty/RT
+Malva/M
+Malvina/M
+Malvin/M
+Malynda/M
+mama/SM
+mamba/SM
+mambo/GSDM
+Mame/M
+Mamet/M
+ma/MH
+Mamie/M
+mammalian/SM
+mammal/SM
+mammary
+mamma's
+mammogram/S
+mammography/S
+Mammon's
+mammon/SM
+mammoth/M
+mammoths
+mammy/SM
+Mamore/M
+manacle/SDMG
+manageability/S
+manageableness
+manageable/U
+managed/U
+management/SM
+manageress/M
+managerial/Y
+manager/M
+managership/M
+manage/ZLGRSD
+Managua/M
+Manama/M
+mañana/M
+mananas
+Manasseh/M
+manatee/SM
+Manaus's
+Manchester/M
+Manchu/MS
+Manchuria/M
+Manchurian/S
+Mancini/M
+manciple/M
+Mancunian/MS
+mandala/SM
+Mandalay/M
+Manda/M
+mandamus/GMSD
+Mandarin
+mandarin/MS
+mandate/SDMG
+mandatory/S
+Mandela
+Mandelbrot/M
+Mandel/M
+mandible/MS
+mandibular
+Mandie/M
+Mandi/M
+Mandingo/M
+mandolin/MS
+mandrake/MS
+mandrel/SM
+mandrill/SM
+Mandy/M
+manège/GSD
+mane/MDS
+Manet/M
+maneuverability/MS
+maneuverer/M
+maneuver/MRDSGB
+Manfred/M
+manful/Y
+manganese/MS
+mange/GMSRDZ
+manger/M
+manginess/S
+mangler/M
+mangle/RSDG
+mangoes
+mango/M
+mangrove/MS
+mangy/PRT
+manhandle/GSD
+Manhattan/SM
+manhole/MS
+manhood/MS
+manhunt/SM
+maniacal/Y
+maniac/SM
+mania/SM
+manically
+Manichean/M
+manic/S
+manicure/MGSD
+manicurist/SM
+manifestation/SM
+manifesto/GSDM
+manifest/YDPGS
+manifolder/M
+manifold/GPYRDMS
+manifoldness/M
+manikin/MS
+Manila/MS
+manila/S
+manilla's
+Mani/M
+manioc/SM
+manipulability
+manipulable
+manipulate/SDXBVGN
+manipulative/PM
+manipulator/MS
+manipulatory
+Manitoba/M
+Manitoulin/M
+Manitowoc/M
+mankind/M
+Mankowski/M
+Manley/M
+manlike
+manliness/SM
+manliness's/U
+manly/URPT
+manna/MS
+manned/U
+mannequin/MS
+mannered/U
+mannerism/SM
+mannerist/M
+mannerliness/MU
+mannerly/UP
+manner/SDYM
+Mann/GM
+Mannheim/M
+Mannie/M
+mannikin's
+Manning/M
+manning/U
+mannishness/SM
+mannish/YP
+Manny/M
+Manolo/M
+Mano/M
+manometer/SM
+Manon/M
+manorial
+manor/MS
+manpower/SM
+manqué/M
+man's
+mansard/SM
+manservant/M
+manse/XNM
+Mansfield/M
+mansion/M
+manslaughter/SM
+Man/SM
+Manson/M
+mans/S
+manta/MS
+Mantegna/M
+mantelpiece/MS
+mantel/SM
+mantes
+mantilla/MS
+mantissa/SM
+mantis/SM
+mantle/ESDG
+Mantle/M
+mantle's
+mantling/M
+mantra/MS
+mantrap/SM
+manual/SMY
+Manuela/M
+Manuel/M
+manufacture/JZGDSR
+manufacturer/M
+manumission/MS
+manumit/S
+manumitted
+manumitting
+manure/RSDMZG
+manuscript/MS
+man/USY
+Manville/M
+Manx
+many
+Manya/M
+Maoism/MS
+Maoist/S
+Mao/M
+Maori/SM
+Maplecrest/M
+maple/MS
+mapmaker/S
+mappable
+mapped/UA
+mapper/S
+mapping/MS
+Mapplethorpe/M
+maps/AU
+map/SM
+Maputo/M
+Marabel/M
+marabou/MS
+marabout's
+Maracaibo/M
+maraca/MS
+Mara/M
+maraschino/SM
+Marathi
+marathoner/M
+Marathon/M
+marathon/MRSZ
+Marat/M
+marauder/M
+maraud/ZGRDS
+marbleize/GSD
+marble/JRSDMG
+marbler/M
+marbling/M
+Marceau/M
+Marcela/M
+Marcelia/M
+Marcelino/M
+Marcella/M
+Marcelle/M
+Marcellina/M
+Marcelline/M
+Marcello/M
+Marcellus/M
+Marcel/M
+Marcelo/M
+Marchall/M
+Marchelle/M
+marcher/M
+marchioness/SM
+March/MS
+march/RSDZG
+Marcia/M
+Marciano/M
+Marcie/M
+Marcile/M
+Marcille/M
+Marci/M
+Marc/M
+Marconi/M
+Marco/SM
+Marcotte/M
+Marcus/M
+Marcy/M
+Mardi/SM
+Marduk/M
+Mareah/M
+mare/MS
+Marena/M
+Maren/M
+Maressa/M
+Margalit/M
+Margalo/M
+Marga/M
+Margareta/M
+Margarete/M
+Margaretha/M
+Margarethe/M
+Margaret/M
+Margaretta/M
+Margarette/M
+margarine/MS
+Margarita/M
+margarita/SM
+Margarito/M
+Margaux/M
+Margeaux/M
+Marge/M
+Margery/M
+Marget/M
+Margette/M
+Margie/M
+Margi/M
+marginalia
+marginality
+marginalization
+marginalize/SDG
+marginal/YS
+margin/GSDM
+Margit/M
+Margo/M
+Margot/M
+Margrethe/M
+Margret/M
+Marguerite/M
+Margy/M
+mariachi/SM
+maria/M
+Maria/M
+Mariam/M
+Mariana/SM
+Marian/MS
+Marianna/M
+Marianne/M
+Mariann/M
+Mariano/M
+Maribelle/M
+Maribel/M
+Maribeth/M
+Maricela/M
+Marice/M
+Maridel/M
+Marieann/M
+Mariejeanne/M
+Mariele/M
+Marielle/M
+Mariellen/M
+Mariel/M
+Marie/M
+Marietta/M
+Mariette/M
+Marigold/M
+marigold/MS
+Marijn/M
+Marijo/M
+marijuana/SM
+Marika/M
+Marilee/M
+Marilin/M
+Marillin/M
+Marilyn/M
+marimba/SM
+Mari/MS
+marinade/MGDS
+Marina/M
+marina/MS
+marinara/SM
+marinate/NGXDS
+marination/M
+mariner/M
+Marine/S
+marine/ZRS
+Marin/M
+Marinna/M
+Marino/M
+Mario/M
+marionette/MS
+Marion/M
+Mariquilla/M
+Marisa/M
+Mariska/M
+Marisol/M
+Marissa/M
+Maritain/M
+marital/Y
+Marita/M
+maritime/R
+Maritsa/M
+Maritza/M
+Mariupol/M
+Marius/M
+Mariya/M
+Marja/M
+Marje/M
+Marjie/M
+Marji/M
+Marj/M
+marjoram/SM
+Marjorie/M
+Marjory/M
+Marjy/M
+Markab/M
+markdown/SM
+marked/AU
+markedly
+marker/M
+marketability/SM
+marketable/U
+Marketa/M
+marketeer/S
+marketer/M
+market/GSMRDJBZ
+marketing/M
+marketplace/MS
+mark/GZRDMBSJ
+Markham/M
+marking/M
+Markism/M
+markkaa
+markka/M
+Mark/MS
+Markos
+Markov
+Markovian
+Markovitz/M
+marks/A
+marksman/M
+marksmanship/S
+marksmen
+markup/SM
+Markus/M
+Marla/M
+Marlane/M
+Marlboro/M
+Marlborough/M
+Marleah/M
+Marlee/M
+Marleen/M
+Marlena/M
+Marlene/M
+Marley/M
+Marlie/M
+Marline/M
+marlinespike/SM
+Marlin/M
+marlin/SM
+marl/MDSG
+Marlo/M
+Marlon/M
+Marlowe/M
+Marlow/M
+Marlyn/M
+Marmaduke/M
+marmalade/MS
+Marmara/M
+marmoreal
+marmoset/MS
+marmot/SM
+Marna/M
+Marne/M
+Marney/M
+Marnia/M
+Marnie/M
+Marni/M
+maroon/GRDS
+marquee/MS
+Marquesas/M
+marque/SM
+marquess/MS
+marquetry/SM
+Marquette/M
+Marquez/M
+marquise/M
+marquisette/MS
+Marquis/M
+marquis/SM
+Marquita/M
+Marrakesh/M
+marred/U
+marriageability/SM
+marriageable
+marriage/ASM
+married/US
+Marrilee/M
+marring
+Marriott/M
+Marris/M
+Marrissa/M
+marrowbone/MS
+marrow/GDMS
+marry/SDGA
+mar/S
+Marseillaise/SM
+Marseilles
+Marseille's
+marshal/GMDRSZ
+Marshalled/M
+marshaller
+Marshall/GDM
+Marshalling/M
+marshallings
+Marshal/M
+Marsha/M
+marshiness/M
+marshland/MS
+Marsh/M
+marshmallow/SM
+marsh/MS
+marshy/PRT
+Marsiella/M
+Mar/SMN
+marsupial/MS
+Martainn/M
+Marta/M
+Martelle/M
+Martel/M
+marten/M
+Marten/M
+Martguerita/M
+Martha/M
+Marthe/M
+Marthena/M
+Martial
+martial/Y
+Martian/S
+Martica/M
+Martie/M
+Marti/M
+Martina/M
+martinet/SM
+Martinez/M
+martingale/MS
+martini/MS
+Martinique/M
+Martin/M
+Martino/M
+martin/SM
+Martinson/M
+Martita/M
+mart/MDNGXS
+Mart/MN
+Marty/M
+Martyn/M
+Martynne/M
+martyrdom/SM
+martyr/GDMS
+Marva/M
+marvel/DGS
+Marvell/M
+marvelous/PY
+Marve/M
+Marven/M
+Marvin/M
+Marv/NM
+Marwin/M
+Marxian/S
+Marxism/SM
+Marxist/SM
+Marx/M
+Marya/M
+Maryanna/M
+Maryanne/M
+Maryann/M
+Marybelle/M
+Marybeth/M
+Maryellen/M
+Maryjane/M
+Maryjo/M
+Maryland/MZR
+Marylee/M
+Marylinda/M
+Marylin/M
+Maryl/M
+Marylou/M
+Marylynne/M
+Mary/M
+Maryrose/M
+Marys
+Marysa/M
+marzipan/SM
+Masada/M
+Masai/M
+Masaryk/M
+masc
+Mascagni/M
+mascara/SGMD
+mascot/SM
+masculineness/M
+masculine/PYS
+masculinity/SM
+Masefield/M
+maser/M
+Maseru/M
+MASH
+Masha/M
+Mashhad/M
+mash/JGZMSRD
+m/ASK
+masked/U
+masker/M
+mask/GZSRDMJ
+masks/U
+masochism/MS
+masochistic
+masochistically
+masochist/MS
+masonic
+Masonic
+Masonite/M
+masonry/MS
+mason/SDMG
+Mason/SM
+masquerader/M
+masquerade/RSDGMZ
+masquer/M
+masque/RSMZ
+Massachusetts/M
+massacre/DRSMG
+massager/M
+massage/SRDMG
+Massasoit/M
+Massenet/M
+masseur/MS
+masseuse/SM
+Massey/M
+massif/SM
+Massimiliano/M
+Massimo/M
+massing/R
+massiveness/SM
+massive/YP
+massless
+mas/SRZ
+Mass/S
+mass/VGSD
+mastectomy/MS
+masterclass
+mastered/A
+masterfulness/M
+masterful/YP
+master/JGDYM
+masterliness/M
+masterly/P
+mastermind/GDS
+masterpiece/MS
+mastership/M
+Master/SM
+masterstroke/MS
+masterwork/S
+mastery/MS
+mast/GZSMRD
+masthead/SDMG
+masticate/SDXGN
+mastication/M
+mastic/SM
+mastiff/MS
+mastodon/MS
+mastoid/S
+masturbate/SDNGX
+masturbation/M
+masturbatory
+matador/SM
+Mata/M
+matchable/U
+match/BMRSDZGJ
+matchbook/SM
+matchbox/SM
+matched/UA
+matcher/M
+matches/A
+matchless/Y
+matchlock/MS
+matchmake/GZJR
+matchmaker/M
+matchmaking/M
+matchplay
+match's/A
+matchstick/MS
+matchwood/SM
+mated/U
+mate/IMS
+Matelda/M
+Mateo/M
+materialism/SM
+materialistic
+materialistically
+materialist/SM
+materiality/M
+materialization/SM
+materialize/CDS
+materialized/A
+materializer/SM
+materializes/A
+materializing
+materialness/M
+material/SPYM
+matériel/MS
+mater/M
+maternal/Y
+maternity/MS
+mates/U
+mathematical/Y
+Mathematica/M
+mathematician/SM
+mathematic/S
+mathematics/M
+Mathematik/M
+Mather/M
+Mathe/RM
+Mathew/MS
+Mathewson/M
+Mathian/M
+Mathias
+Mathieu/M
+Mathilda/M
+Mathilde/M
+Mathis
+math/M
+maths
+Matias/M
+Matilda/M
+Matilde/M
+matinée/S
+mating/M
+matins/M
+Matisse/SM
+matriarchal
+matriarch/M
+matriarchs
+matriarchy/MS
+matrices
+matricidal
+matricide/MS
+matriculate/XSDGN
+matriculation/M
+matrimonial/Y
+matrimony/SM
+matrix/M
+matron/YMS
+mat/SJGMDR
+Matsumoto/M
+matte/JGMZSRD
+Mattel/M
+Matteo/M
+matter/GDM
+Matterhorn/M
+Matthaeus/M
+Mattheus/M
+Matthew/MS
+Matthias
+Matthieu/M
+Matthiew/M
+Matthus/M
+Mattias/M
+Mattie/M
+Matti/M
+matting/M
+mattins's
+Matt/M
+mattock/MS
+mattress/MS
+matt's
+Matty/M
+maturate/DSNGVX
+maturational
+maturation/M
+matureness/M
+maturer/M
+mature/RSDTPYG
+maturity/MS
+matzo/SHM
+matzot
+Maude/M
+Maudie/M
+maudlin/Y
+Maud/M
+Maugham/M
+Maui/M
+mauler/M
+maul/RDGZS
+maunder/GDS
+Maupassant/M
+Maura/M
+Maureene/M
+Maureen/M
+Maure/M
+Maurene/M
+Mauriac/M
+Maurice/M
+Mauricio/M
+Maurie/M
+Maurine/M
+Maurise/M
+Maurita/M
+Mauritania/M
+Mauritanian/S
+Mauritian/S
+Mauritius/M
+Maurits/M
+Maurizia/M
+Maurizio/M
+Maurois/M
+Mauro/M
+Maury/M
+Mauser/M
+mausoleum/SM
+mauve/SM
+maven/S
+maverick/SMDG
+mavin's
+Mavis/M
+Mavra/M
+mawkishness/SM
+mawkish/PY
+Mawr/M
+maw/SGMD
+max/GDS
+Maxie/M
+maxillae
+maxilla/M
+maxillary/S
+Maxi/M
+maximality
+maximal/SY
+maxima's
+Maximilian/M
+Maximilianus/M
+Maximilien/M
+maximization/SM
+maximizer/M
+maximize/RSDZG
+Maxim/M
+Maximo/M
+maxim/SM
+maximum/MYS
+Maxine/M
+maxi/S
+Max/M
+Maxtor/M
+Maxwellian
+maxwell/M
+Maxwell/M
+Maxy/M
+Maya/MS
+Mayan/S
+Maybelle/M
+maybe/S
+mayday/S
+may/EGS
+Maye/M
+mayer
+Mayer/M
+mayest
+Mayfair/M
+Mayflower/M
+mayflower/SM
+mayfly/MS
+mayhap
+mayhem/MS
+Maynard/M
+Mayne/M
+Maynord/M
+mayn't
+Mayo/M
+mayonnaise/MS
+mayoral
+mayoralty/MS
+mayoress/MS
+Mayor/M
+mayor/MS
+mayorship/M
+mayo/S
+maypole/MS
+Maypole/SM
+Mayra/M
+May/SMR
+mayst
+Mazama/M
+Mazarin/M
+Mazatlan/M
+Mazda/M
+mazedness/SM
+mazed/YP
+maze/MGDSR
+mazurka/SM
+Mazzini/M
+Mb
+MB
+MBA
+Mbabane/M
+Mbini/M
+MC
+McAdam/MS
+McAllister/M
+McBride/M
+McCabe/M
+McCain/M
+McCall/M
+McCarthyism/M
+McCarthy/M
+McCartney/M
+McCarty/M
+McCauley/M
+McClain/M
+McClellan/M
+McClure/M
+McCluskey/M
+McConnell/M
+McCormick/M
+McCoy/SM
+McCracken/M
+McCray/M
+McCullough/M
+McDaniel/M
+McDermott/M
+McDonald/M
+McDonnell/M
+McDougall/M
+McDowell/M
+McElhaney/M
+McEnroe/M
+McFadden/M
+McFarland/M
+McGee/M
+McGill/M
+McGovern/M
+McGowan/M
+McGrath/M
+McGraw/M
+McGregor/M
+McGuffey/M
+McGuire/M
+MCI/M
+McIntosh/M
+McIntyre/M
+McKay/M
+McKee/M
+McKenzie/M
+McKesson/M
+McKinley/M
+McKinney/M
+McKnight/M
+McLanahan/M
+McLaughlin/M
+McLean/M
+McLeod/M
+McLuhan/M
+McMahon/M
+McMartin/M
+McMillan/M
+McNamara/M
+McNaughton/M
+McNeil/M
+McPherson/M
+MD
+Md/M
+mdse
+MDT
+ME
+Meade/M
+Mead/M
+meadowland
+meadowlark/SM
+meadow/MS
+Meadows
+meadowsweet/M
+mead/SM
+Meagan/M
+meagerness/SM
+meager/PY
+Meaghan/M
+meagres
+mealiness/MS
+meal/MDGS
+mealtime/MS
+mealybug/S
+mealymouthed
+mealy/PRST
+meander/JDSG
+meaneing
+meanie/MS
+meaningfulness/SM
+meaningful/YP
+meaninglessness/SM
+meaningless/PY
+meaning/M
+meanness/S
+means/M
+meantime/SM
+meant/U
+meanwhile/S
+Meany/M
+mean/YRGJTPS
+meany's
+Meara/M
+measle/SD
+measles/M
+measly/TR
+measurable/U
+measurably
+measure/BLMGRSD
+measured/Y
+measureless
+measurement/SM
+measurer/M
+measures/A
+measuring/A
+meas/Y
+meataxe
+meatball/MS
+meatiness/MS
+meatless
+meatloaf
+meatloaves
+meat/MS
+meatpacking/S
+meaty/RPT
+Mecca/MS
+mecca/S
+mechanical/YS
+mechanic/MS
+mechanism/SM
+mechanistic
+mechanistically
+mechanist/M
+mechanization/SM
+mechanized/U
+mechanizer/M
+mechanize/RSDZGB
+mechanizes/U
+mechanochemically
+Mechelle/M
+med
+medalist/MS
+medallion/MS
+medal/SGMD
+Medan/M
+meddle/GRSDZ
+meddlesome
+Medea/M
+Medellin
+Medfield/M
+mediaeval's
+medial/AY
+medials
+median/YMS
+media/SM
+mediateness/M
+mediate/PSDYVNGX
+mediation/ASM
+mediator/SM
+Medicaid/SM
+medical/YS
+medicament/MS
+Medicare/MS
+medicate/DSXNGV
+medication/M
+Medici/MS
+medicinal/SY
+medicine/DSMG
+medico/SM
+medic/SM
+medievalist/MS
+medieval/YMS
+Medina/M
+mediocre
+mediocrity/MS
+meditate/NGVXDS
+meditation/M
+meditativeness/M
+meditative/PY
+Mediterranean/MS
+mediumistic
+medium/SM
+medley/SM
+medulla/SM
+Medusa/M
+meed/MS
+meekness/MS
+meek/TPYR
+meerschaum/MS
+meeter/M
+meetinghouse/S
+meeting/M
+meet/JGSYR
+me/G
+mega
+megabit/MS
+megabuck/S
+megabyte/S
+megacycle/MS
+megadeath/M
+megadeaths
+megahertz/M
+megalithic
+megalith/M
+megaliths
+megalomaniac/SM
+megalomania/SM
+megalopolis/SM
+Megan/M
+megaphone/SDGM
+megaton/MS
+megavolt/M
+megawatt/SM
+megaword/S
+Megen/M
+Meggie/M
+Meggi/M
+Meggy/M
+Meghan/M
+Meghann/M
+Meg/MN
+megohm/MS
+Mehetabel/M
+Meier/M
+Meighen/M
+Meiji/M
+Mei/MR
+meioses
+meiosis/M
+meiotic
+Meir/M
+Meister/M
+Meistersinger/M
+Mejia/M
+Mekong/M
+Mela/M
+Melamie/M
+melamine/SM
+melancholia/SM
+melancholic/S
+melancholy/MS
+Melanesia/M
+Melanesian/S
+melange/S
+Melania/M
+Melanie/M
+melanin/MS
+melanoma/SM
+Melantha/M
+Melany/M
+Melba/M
+Melbourne/M
+Melcher/M
+Melchior/M
+meld/SGD
+mêlée/MS
+Melendez/M
+Melesa/M
+Melessa/M
+Melicent/M
+Melina/M
+Melinda/M
+Melinde/M
+meliorate/XSDVNG
+melioration/M
+Melisa/M
+Melisande/M
+Melisandra/M
+Melisenda/M
+Melisent/M
+Melissa/M
+Melisse/M
+Melita/M
+Melitta/M
+Mella/M
+Mellicent/M
+Mellie/M
+mellifluousness/SM
+mellifluous/YP
+Melli/M
+Mellisa/M
+Mellisent/M
+Melloney/M
+Mellon/M
+mellowness/MS
+mellow/TGRDYPS
+Melly/M
+Mel/MY
+Melodee/M
+melodically
+melodic/S
+Melodie/M
+melodiousness/S
+melodious/YP
+melodrama/SM
+melodramatically
+melodramatic/S
+Melody/M
+melody/MS
+Melonie/M
+melon/MS
+Melony/M
+Melosa/M
+Melpomene/M
+meltdown/S
+melter/M
+melting/Y
+Melton/M
+melt/SAGD
+Melva/M
+Melville/M
+Melvin/M
+Melvyn/M
+Me/M
+member/DMS
+membered/AE
+members/EA
+membership/SM
+membrane/MSD
+membranous
+memento/SM
+Memling/M
+memoir/MS
+memorabilia
+memorability/SM
+memorableness/M
+memorable/P
+memorably
+memorandum/SM
+memorialize/DSG
+memorialized/U
+memorial/SY
+memoriam
+memorization/MS
+memorized/U
+memorizer/M
+memorize/RSDZG
+memorizes/A
+memoryless
+memory/MS
+memo/SM
+Memphis/M
+menace/GSD
+menacing/Y
+menagerie/SM
+menage/S
+Menander/M
+menarche/MS
+Menard/M
+Mencius/M
+Mencken/M
+mendaciousness/M
+mendacious/PY
+mendacity/MS
+Mendeleev/M
+mendelevium/SM
+Mendelian
+Mendel/M
+Mendelssohn/M
+mender/M
+Mendez/M
+mendicancy/MS
+mendicant/S
+Mendie/M
+mending/M
+Mendocino/M
+Mendoza/M
+mend/RDSJGZ
+Mendy/M
+Menelaus/M
+Menes/M
+menfolk/S
+menhaden/M
+menial/YS
+meningeal
+meninges
+meningitides
+meningitis/M
+meninx
+menisci
+meniscus/M
+Menkalinan/M
+Menkar/M
+Menkent/M
+Menlo/M
+men/MS
+Mennonite/SM
+Menominee
+menopausal
+menopause/SM
+menorah/M
+menorahs
+Menotti/M
+Mensa/M
+Mensch/M
+mensch/S
+menservants/M
+mens/SDG
+menstrual
+menstruate/NGDSX
+menstruation/M
+mensurable/P
+mensuration/MS
+menswear/M
+mentalist/MS
+mentality/MS
+mental/Y
+mentholated
+menthol/SM
+mentionable/U
+mentioned/U
+mentioner/M
+mention/ZGBRDS
+mentor/DMSG
+Menuhin/M
+menu/SM
+Menzies/M
+meow/DSG
+Mephistopheles/M
+Merak/M
+Mercado/M
+mercantile
+Mercator/M
+Mercedes
+mercenariness/M
+mercenary/SMP
+mercerize/SDG
+Mercer/M
+mercer/SM
+merchandiser/M
+merchandise/SRDJMZG
+merchantability
+merchantman/M
+merchantmen
+merchant/SBDMG
+Mercie/M
+mercifully/U
+mercifulness/M
+merciful/YP
+mercilessness/SM
+merciless/YP
+Merci/M
+Merck/M
+mercurial/SPY
+mercuric
+Mercurochrome/M
+mercury/MS
+Mercury/MS
+Mercy/M
+mercy/SM
+Meredeth/M
+Meredithe/M
+Meredith/M
+Merell/M
+meretriciousness/SM
+meretricious/YP
+mere/YS
+merganser/MS
+merger/M
+merge/SRDGZ
+Meridel/M
+meridian/MS
+meridional
+Meridith/M
+Meriel/M
+Merilee/M
+Merill/M
+Merilyn/M
+meringue/MS
+merino/MS
+Meris
+Merissa/M
+merited/U
+meritocracy/MS
+meritocratic
+meritocrats
+meritoriousness/MS
+meritorious/PY
+merit/SCGMD
+Meriwether/M
+Merla/M
+Merle/M
+Merlina/M
+Merline/M
+merlin/M
+Merlin/M
+Merl/M
+mermaid/MS
+merman/M
+mermen
+Merna/M
+Merola/M
+meromorphic
+Merralee/M
+Merrel/M
+Merriam/M
+Merrick/M
+Merridie/M
+Merrielle/M
+Merrie/M
+Merrilee/M
+Merrile/M
+Merrili/M
+Merrill/M
+merrily
+Merrily/M
+Merrimack/M
+Merrimac/M
+merriment/MS
+merriness/S
+Merritt/M
+Merry/M
+merrymaker/MS
+merrymaking/SM
+merry/RPT
+Mersey/M
+mer/TGDR
+Merton/M
+Mervin/M
+Merv/M
+Merwin/M
+Merwyn/M
+Meryl/M
+Mesa
+Mesabi/M
+mesa/SM
+mescaline/SM
+mescal/SM
+mesdames/M
+mesdemoiselles/M
+Meshed's
+meshed/U
+mesh/GMSD
+mesmeric
+mesmerism/SM
+mesmerized/U
+mesmerizer/M
+mesmerize/SRDZG
+Mesolithic/M
+mesomorph/M
+mesomorphs
+meson/MS
+Mesopotamia/M
+Mesopotamian/S
+mesosphere/MS
+mesozoic
+Mesozoic
+mesquite/MS
+mes/S
+message/SDMG
+messeigneurs
+messenger/GSMD
+Messerschmidt/M
+mess/GSDM
+Messiaen/M
+messiah
+Messiah/M
+messiahs
+Messiahs
+messianic
+Messianic
+messieurs/M
+messily
+messiness/MS
+messmate/MS
+Messrs/M
+messy/PRT
+mestizo/MS
+meta
+metabolic
+metabolically
+metabolism/MS
+metabolite/SM
+metabolize/GSD
+metacarpal/S
+metacarpi
+metacarpus/M
+metacircular
+metacircularity
+metalanguage/MS
+metalization/SM
+metalized
+metallic/S
+metalliferous
+metallings
+metallography/M
+metalloid/M
+metallurgic
+metallurgical/Y
+metallurgist/S
+metallurgy/MS
+metal/SGMD
+metalsmith/MS
+metalworking/M
+metalwork/RMJGSZ
+Meta/M
+metamathematical
+metamorphic
+metamorphism/SM
+metamorphose/GDS
+metamorphosis/M
+metaphoric
+metaphorical/Y
+metaphor/MS
+metaphosphate/M
+metaphysical/Y
+metaphysic/SM
+metastability/M
+metastable
+metastases
+metastasis/M
+metastasize/DSG
+metastatic
+metatarsal/S
+metatarsi
+metatarsus/M
+metatheses
+metathesis/M
+metathesized
+metathesizes
+metathesizing
+metavariable
+metempsychoses
+metempsychosis/M
+meteoric
+meteorically
+meteorite/SM
+meteoritic/S
+meteoritics/M
+meteoroid/SM
+meteorologic
+meteorological
+meteorologist/S
+meteorology/MS
+meteor/SM
+meter/GDM
+mete/ZDGSR
+methadone/SM
+methane/MS
+methanol/SM
+methinks
+methionine/M
+methodicalness/SM
+methodical/YP
+methodism
+Methodism/SM
+methodist/MS
+Methodist/MS
+method/MS
+methodological/Y
+methodologists
+methodology/MS
+methought
+Methuen/M
+Methuselah/M
+Methuselahs
+methylated
+methylene/M
+methyl/SM
+meticulousness/MS
+meticulous/YP
+métier/S
+metonymy/M
+Metrecal/M
+metrical/Y
+metricate/SDNGX
+metricize/GSD
+metrics/M
+metric/SM
+metronome/MS
+metropolis/SM
+metropolitanization
+metropolitan/S
+metro/SM
+mets
+Metternich/M
+mettle/SDM
+mettlesome
+met/U
+Metzler/M
+Meuse/M
+mewl/GSD
+mew/SGD
+mews/SM
+Mex
+Mexicali/M
+Mexican/S
+Mexico/M
+Meyerbeer/M
+Meyer/SM
+mezzanine/MS
+mezzo/S
+MFA
+mfg
+mfr/S
+mg
+M/GB
+Mg/M
+MGM/M
+mgr
+Mgr
+MHz
+MI
+MIA
+Mia/M
+Miami/SM
+Miaplacidus/M
+miasmal
+miasma/SM
+Micaela/M
+Micah/M
+mica/MS
+micelles
+mice/M
+Michaela/M
+Michaelangelo/M
+Michaelina/M
+Michaeline/M
+Michaella/M
+Michaelmas/MS
+Michael/SM
+Michaelson/M
+Michail/M
+Michale/M
+Michal/M
+Micheal/M
+Micheil/M
+Michelangelo/M
+Michele/M
+Michelina/M
+Micheline/M
+Michelin/M
+Michelle/M
+Michell/M
+Michel/M
+Michelson/M
+Michigander/S
+Michiganite/S
+Michigan/M
+Mich/M
+Mickelson/M
+Mickey/M
+mickey/SM
+Mickie/M
+Micki/M
+Mick/M
+Micky/M
+Mic/M
+Micmac/M
+micra's
+microamp
+microanalysis/M
+microanalytic
+microbe/MS
+microbial
+microbicidal
+microbicide/M
+microbiological
+microbiologist/MS
+microbiology/SM
+microbrewery/S
+microchemistry/M
+microchip/S
+microcircuit/MS
+microcode/GSD
+microcomputer/MS
+microcosmic
+microcosm/MS
+microdensitometer
+microdot/MS
+microeconomic/S
+microeconomics/M
+microelectronic/S
+microelectronics/M
+microfiber/S
+microfiche/M
+microfilm/DRMSG
+microfossils
+micrography/M
+microgroove/MS
+microhydrodynamics
+microinstruction/SM
+microjoule
+microlevel
+microlight/S
+micromanage/GDSL
+micromanagement/S
+micrometeorite/MS
+micrometeoritic
+micrometer/SM
+Micronesia/M
+Micronesian/S
+micron/MS
+microorganism/SM
+microphone/SGM
+Microport/M
+microprocessing
+microprocessor/SM
+microprogrammed
+microprogramming
+microprogram/SM
+micro/S
+microscope/SM
+microscopic
+microscopical/Y
+microscopy/MS
+microsecond/MS
+microsimulation/S
+micros/M
+Microsoft/M
+microsomal
+microstore
+microsurgery/SM
+MicroVAXes
+MicroVAX/M
+microvolt/SM
+microwaveable
+microwave/BMGSD
+microword/S
+midair/MS
+midas
+Midas/M
+midband/M
+midday/MS
+midden/SM
+middest
+middlebrow/SM
+Middlebury/M
+middle/GJRSD
+middleman/M
+middlemen
+middlemost
+Middlesex/M
+Middleton/M
+Middletown/M
+middleweight/SM
+middling/Y
+middy/SM
+Mideastern
+Mideast/M
+midfield/RM
+Midge/M
+midge/SM
+midget/MS
+midi/S
+midland/MRS
+Midland/MS
+midlife
+midlives
+midmorn/G
+midmost/S
+midnight/SYM
+midpoint/MS
+midrange
+midrib/MS
+midriff/MS
+mid/S
+midscale
+midsection/M
+midshipman/M
+midshipmen
+midship/S
+midspan
+midstream/MS
+midst/SM
+midsummer/MS
+midterm/MS
+midtown/MS
+Midway/M
+midway/S
+midweek/SYM
+Midwesterner/M
+Midwestern/ZR
+Midwest/M
+midwicket
+midwifery/SM
+midwife/SDMG
+midwinter/YMS
+midwives
+midyear/MS
+mien/M
+miff/GDS
+mightily
+mightiness/MS
+mightn't
+might/S
+mighty/TPR
+mignon
+mignonette/SM
+Mignon/M
+Mignonne/M
+migraine/SM
+migrant/MS
+migrate/ASDG
+migration/MS
+migrative
+migratory/S
+MIG/S
+Miguela/M
+Miguelita/M
+Miguel/M
+mikado/MS
+Mikaela/M
+Mikael/M
+mike/DSMG
+Mikel/M
+Mike/M
+Mikey/M
+Mikhail/M
+Mikkel/M
+Mikol/M
+Mikoyan/M
+milady/MS
+Milagros/M
+Milanese
+Milan/M
+milch/M
+mildew/DMGS
+mildness/MS
+Mildred/M
+Mildrid/M
+mild/STYRNP
+mileage/SM
+Milena/M
+milepost/SM
+miler/M
+mile/SM
+Mile/SM
+milestone/MS
+Milford/M
+Milicent/M
+milieu/SM
+Milissent/M
+militancy/MS
+militantness/M
+militant/YPS
+militarily
+militarism/SM
+militaristic
+militarist/MS
+militarization/SCM
+militarize/SDCG
+military
+militate/SDG
+militiaman/M
+militiamen
+militia/SM
+Milka/M
+Milken/M
+milker/M
+milk/GZSRDM
+milkiness/MS
+milkmaid/SM
+milkman/M
+milkmen
+milkshake/S
+milksop/SM
+milkweed/MS
+milky/RPT
+millage/S
+Millard/M
+Millay/M
+millenarian
+millenarianism/M
+millennial
+millennialism
+millennium/MS
+millepede's
+miller/M
+Miller/M
+Millet/M
+millet/MS
+milliamp
+milliampere/S
+milliard/MS
+millibar/MS
+Millicent/M
+millidegree/S
+Millie/M
+milligram/MS
+millijoule/S
+Millikan/M
+milliliter/MS
+Milli/M
+millimeter/SM
+milliner/SM
+millinery/MS
+milling/M
+millionaire/MS
+million/HDMS
+millionth/M
+millionths
+millipede/SM
+millisecond/MS
+Millisent/M
+millivoltmeter/SM
+millivolt/SM
+milliwatt/S
+millpond/MS
+millrace/SM
+mill/SGZMRD
+Mill/SMR
+millstone/SM
+millstream/SM
+millwright/MS
+Milly/M
+mil/MRSZ
+Mil/MY
+Milne/M
+Milo/M
+Milquetoast/S
+milquetoast/SM
+Miltiades/M
+Miltie/M
+Milt/M
+milt/MDSG
+Miltonic
+Milton/M
+Miltown/M
+Milty/M
+Milwaukee/M
+Milzie/M
+MIMD
+mime/DSRMG
+mimeograph/GMDS
+mimeographs
+mimer/M
+mimesis/M
+mimetic
+mimetically
+mimicked
+mimicker/SM
+mimicking
+mimicry/MS
+mimic/S
+Mimi/M
+mi/MNX
+Mimosa/M
+mimosa/SM
+Mina/M
+minaret/MS
+minatory
+mincemeat/MS
+mincer/M
+mince/SRDGZJ
+mincing/Y
+Minda/M
+Mindanao/M
+mind/ARDSZG
+mindbogglingly
+minded/P
+minder/M
+mindfully
+mindfulness/MS
+mindful/U
+mindlessness/SM
+mindless/YP
+Mindoro/M
+min/DRZGJ
+mind's
+mindset/S
+Mindy/M
+minefield/MS
+mineralization/C
+mineralized/U
+mineralogical
+mineralogist/SM
+mineralogy/MS
+mineral/SM
+miner/M
+Miner/M
+Minerva/M
+mineshaft
+mine/SNX
+minestrone/MS
+minesweeper/MS
+Minetta/M
+Minette/M
+mineworkers
+mingle/SDG
+Ming/M
+Mingus/M
+miniature/GMSD
+miniaturist/SM
+miniaturization/MS
+miniaturize/SDG
+minibike/S
+minibus/SM
+minicab/M
+minicam/MS
+minicomputer/SM
+minidress/SM
+minify/GSD
+minimalism/S
+minimalistic
+minimalist/MS
+minimality
+minimal/SY
+minima's
+minimax/M
+minimization/MS
+minimized/U
+minimizer/M
+minimize/RSDZG
+minim/SM
+minimum/MS
+mining/M
+minion/M
+mini/S
+miniseries
+miniskirt/MS
+ministerial/Y
+minister/MDGS
+ministrant/S
+ministration/SM
+ministry/MS
+minivan/S
+miniver/M
+minke
+mink/SM
+Min/MR
+Minna/M
+Minnaminnie/M
+Minneapolis/M
+Minne/M
+minnesinger/MS
+Minnesota/M
+Minnesotan/S
+Minnie/M
+Minni/M
+Minn/M
+Minnnie/M
+minnow/SM
+Minny/M
+Minoan/S
+Minolta/M
+minor/DMSG
+minority/MS
+Minor/M
+Minos
+Minotaur/M
+minotaur/S
+Minot/M
+minoxidil/S
+Minsk/M
+Minsky/M
+minster/SM
+minstrel/SM
+minstrelsy/MS
+mintage/SM
+Mintaka/M
+Minta/M
+minter/M
+mint/GZSMRD
+minty/RT
+minuend/SM
+minuet/SM
+Minuit/M
+minuscule/SM
+minus/S
+minuteman
+Minuteman/M
+minutemen
+minuteness/SM
+minute/RSDPMTYG
+minutiae
+minutia/M
+minx/MS
+Miocene
+MIPS
+Miquela/M
+Mirabeau/M
+Mirabella/M
+Mirabelle/M
+Mirabel/M
+Mirach/M
+miracle/MS
+miraculousness/M
+miraculous/PY
+mirage/GSDM
+Mira/M
+Miranda/M
+Miran/M
+Mireielle/M
+Mireille/M
+Mirella/M
+Mirelle/M
+mire/MGDS
+Mirfak/M
+Miriam/M
+Mirilla/M
+Mir/M
+Mirna/M
+Miro
+mirror/DMGS
+mirthfulness/SM
+mirthful/PY
+mirthlessness/M
+mirthless/YP
+mirth/M
+mirths
+MIRV/DSG
+miry/RT
+Mirzam/M
+misaddress/SDG
+misadventure/SM
+misalign/DSGL
+misalignment/MS
+misalliance/MS
+misanalysed
+misanthrope/MS
+misanthropic
+misanthropically
+misanthropist/S
+misanthropy/SM
+misapplier/M
+misapply/GNXRSD
+misapprehend/GDS
+misapprehension/MS
+misappropriate/GNXSD
+misbegotten
+misbehaver/M
+misbehave/RSDG
+misbehavior/SM
+misbrand/DSG
+misc
+miscalculate/XGNSD
+miscalculation/M
+miscall/SDG
+miscarriage/MS
+miscarry/SDG
+miscast/GS
+miscegenation/SM
+miscellanea
+miscellaneous/PY
+miscellany/MS
+Mischa/M
+mischance/MGSD
+mischief/MDGS
+mischievousness/MS
+mischievous/PY
+miscibility/S
+miscible/C
+misclassification/M
+misclassified
+misclassifying
+miscode/SDG
+miscommunicate/NDS
+miscomprehended
+misconceive/GDS
+misconception/MS
+misconduct/GSMD
+misconfiguration
+misconstruction/MS
+misconstrue/DSG
+miscopying
+miscount/DGS
+miscreant/MS
+miscue/MGSD
+misdeal/SG
+misdealt
+misdeed/MS
+misdemeanant/SM
+misdemeanor/SM
+misdiagnose/GSD
+misdid
+misdirect/GSD
+misdirection/MS
+misdirector/S
+misdoes
+misdo/JG
+misdone
+miserableness/SM
+miserable/SP
+miserably
+miser/KM
+miserliness/MS
+miserly/P
+misery/MS
+mises/KC
+misfeasance/MS
+misfeature/M
+misfield
+misfile/SDG
+misfire/SDG
+misfit/MS
+misfitted
+misfitting
+misfortune/SM
+misgauge/GDS
+misgiving/MYS
+misgovern/LDGS
+misgovernment/S
+misguidance/SM
+misguidedness/M
+misguided/PY
+misguide/DRSG
+misguider/M
+Misha/M
+mishandle/SDG
+mishap/MS
+mishapped
+mishapping
+misheard
+mishear/GS
+mishitting
+mishmash/SM
+misidentification/M
+misidentify/GNSD
+misinformation/SM
+misinform/GDS
+misinterpretation/MS
+misinterpreter/M
+misinterpret/RDSZG
+misjudge/DSG
+misjudging/Y
+misjudgment/MS
+Miskito
+mislabel/DSG
+mislaid
+mislay/GS
+misleader/M
+mislead/GRJS
+misleading/Y
+misled
+mismanage/LGSD
+mismanagement/MS
+mismatch/GSD
+misname/GSD
+misnomer/GSMD
+misogamist/MS
+misogamy/MS
+misogynistic
+misogynist/MS
+misogynous
+misogyny/MS
+misperceive/SD
+misplace/GLDS
+misplacement/MS
+misplay/GSD
+mispositioned
+misprint/SGDM
+misprision/SM
+mispronounce/DSG
+mispronunciation/MS
+misquotation/MS
+misquote/GDS
+misreader/M
+misread/RSGJ
+misrelated
+misremember/DG
+misreport/DGS
+misrepresentation/MS
+misrepresenter/M
+misrepresent/SDRG
+misroute/DS
+misrule/SDG
+missal/ESM
+misshape/DSG
+misshapenness/SM
+misshapen/PY
+Missie/M
+missile/MS
+missilery/SM
+mission/AMS
+missionary/MS
+missioned
+missioner/SM
+missioning
+missis's
+Mississauga/M
+Mississippian/S
+Mississippi/M
+missive/MS
+Missoula/M
+Missourian/S
+Missouri/M
+misspeak/SG
+misspecification
+misspecified
+misspelling/M
+misspell/SGJD
+misspend/GS
+misspent
+misspoke
+misspoken
+mis/SRZ
+miss/SDEGV
+Miss/SM
+misstate/GLDRS
+misstatement/MS
+misstater/M
+misstep/MS
+misstepped
+misstepping
+missus/SM
+Missy/M
+mistakable/U
+mistake/BMGSR
+mistaken/Y
+mistaker/M
+mistaking/Y
+Mistassini/M
+mister/GDM
+Mister/SM
+mistily
+Misti/M
+mistime/GSD
+mistiness/S
+mistletoe/MS
+mist/MRDGZS
+mistook
+mistral/MS
+mistranslated
+mistranslates
+mistranslating
+mistranslation/SM
+mistreat/DGSL
+mistreatment/SM
+Mistress/MS
+mistress/MSY
+mistrial/SM
+mistruster/M
+mistrustful/Y
+mistrust/SRDG
+Misty/M
+mistype/SDGJ
+misty/PRT
+misunderstander/M
+misunderstanding/M
+misunderstand/JSRZG
+misunderstood
+misuser/M
+misuse/RSDMG
+miswritten
+Mitchael/M
+Mitchell/M
+Mitchel/M
+Mitch/M
+miterer/M
+miter/GRDM
+mite/SRMZ
+Mitford/M
+Mithra/M
+Mithridates/M
+mitigated/U
+mitigate/XNGVDS
+mitigation/M
+MIT/M
+mitoses
+mitosis/M
+mitotic
+MITRE/SM
+Mitsubishi/M
+mitten/M
+Mitterrand/M
+mitt/XSMN
+Mitty/M
+Mitzi/M
+mitzvahs
+mixable
+mix/AGSD
+mixed/U
+mixer/SM
+mixture/SM
+Mizar/M
+mizzenmast/SM
+mizzen/MS
+Mk
+mks
+ml
+Mlle/M
+mm
+MM
+MMe
+Mme/SM
+MN
+mnemonically
+mnemonics/M
+mnemonic/SM
+Mnemosyne/M
+Mn/M
+MO
+moan/GSZRDM
+moat/SMDG
+mobbed
+mobber
+mobbing
+mobcap/SM
+Mobile/M
+mobile/S
+mobility/MS
+mobilizable
+mobilization/AMCS
+mobilize/CGDS
+mobilized/U
+mobilizer/MS
+mobilizes/A
+Mobil/M
+mob/MS
+mobster/MS
+Mobutu/M
+moccasin/SM
+mocha/SM
+mockers/M
+mockery/MS
+mock/GZSRD
+mockingbird/MS
+mocking/Y
+mo/CSK
+modality/MS
+modal/Y
+modeled/A
+modeler/M
+modeling/M
+models/A
+model/ZGSJMRD
+mode/MS
+modem/SM
+moderated/U
+moderateness/SM
+moderate/PNGDSXY
+moderation/M
+moderator/MS
+modernism/MS
+modernistic
+modernist/S
+modernity/SM
+modernization/MS
+modernized/U
+modernizer/M
+modernize/SRDGZ
+modernizes/U
+modernness/SM
+modern/PTRYS
+Modesta/M
+Modestia/M
+Modestine/M
+Modesto/M
+modest/TRY
+Modesty/M
+modesty/MS
+modicum/SM
+modifiability/M
+modifiableness/M
+modifiable/U
+modification/M
+modified/U
+modifier/M
+modify/NGZXRSD
+Modigliani/M
+modishness/MS
+modish/YP
+mod/TSR
+Modula/M
+modularity/SM
+modularization
+modularize/SDG
+modular/SY
+modulate/ADSNCG
+modulation/CMS
+modulator/ACSM
+module/SM
+moduli
+modulo
+modulus/M
+modus
+Moe/M
+Moen/M
+Mogadiscio's
+Mogadishu
+mogul/MS
+Mogul/MS
+mohair/SM
+Mohamed/M
+Mohammad/M
+Mohammedanism/MS
+Mohammedan/SM
+Mohammed's
+Mohandas/M
+Mohandis/M
+Mohawk/MS
+Mohegan/S
+Mohican's
+Moho/M
+Mohorovicic/M
+Mohr/M
+moiety/MS
+moil/SGD
+Moina/M
+Moines/M
+Moira/M
+moire/MS
+Moise/MS
+Moiseyev/M
+Moishe/M
+moistener/M
+moisten/ZGRD
+moistness/MS
+moist/TXPRNY
+moisture/MS
+moisturize/GZDRS
+Mojave/M
+molal
+molarity/SM
+molar/MS
+molasses/MS
+Moldavia/M
+Moldavian/S
+moldboard/SM
+molder/DG
+moldiness/SM
+molding/M
+mold/MRDJSGZ
+Moldova
+moldy/PTR
+molecularity/SM
+molecular/Y
+molecule/MS
+molehill/SM
+mole/MTS
+moleskin/MS
+molestation/SM
+molested/U
+molester/M
+molest/RDZGS
+Moliere
+Molina/M
+Moline/M
+Mollee/M
+Mollie/M
+mollification/M
+mollify/XSDGN
+Molli/M
+Moll/M
+moll/MS
+mollusc's
+mollusk/S
+mollycoddler/M
+mollycoddle/SRDG
+Molly/M
+molly/SM
+Molnar/M
+Moloch/M
+Molokai/M
+Molotov/M
+molter/M
+molt/RDNGZS
+Moluccas
+molybdenite/M
+molybdenum/MS
+Mombasa/M
+momenta
+momentarily
+momentariness/SM
+momentary/P
+moment/MYS
+momentousness/MS
+momentous/YP
+momentum/SM
+momma/S
+Mommy/M
+mommy/SM
+Mo/MN
+mom/SM
+Monaco/M
+monadic
+monad/SM
+Monah/M
+Mona/M
+monarchic
+monarchical
+monarchism/MS
+monarchistic
+monarchist/MS
+monarch/M
+monarchs
+monarchy/MS
+Monash/M
+monastery/MS
+monastical/Y
+monasticism/MS
+monastic/S
+monaural/Y
+Mondale/M
+Monday/MS
+Mondrian/M
+Monegasque/SM
+Monera/M
+monetarily
+monetarism/S
+monetarist/MS
+monetary
+monetization/CMA
+monetize/CGADS
+Monet/M
+moneybag/SM
+moneychangers
+moneyer/M
+moneylender/SM
+moneymaker/MS
+moneymaking/MS
+money/SMRD
+Monfort/M
+monger/SGDM
+Mongolia/M
+Mongolian/S
+Mongolic/M
+mongolism/SM
+mongoloid/S
+Mongoloid/S
+Mongol/SM
+mongoose/SM
+mongrel/SM
+Monica/M
+monies/M
+Monika/M
+moniker/MS
+Monique/M
+monism/MS
+monist/SM
+monition/SM
+monitored/U
+monitor/GSMD
+monitory/S
+monkeyshine/S
+monkey/SMDG
+monkish
+Monk/M
+monk/MS
+monkshood/SM
+Monmouth/M
+monochromatic
+monochromator
+monochrome/MS
+monocle/SDM
+monoclinic
+monoclonal/S
+monocotyledonous
+monocotyledon/SM
+monocular/SY
+monodic
+monodist/S
+monody/MS
+monogamist/MS
+monogamous/PY
+monogamy/MS
+monogrammed
+monogramming
+monogram/MS
+monograph/GMDS
+monographs
+monolingualism
+monolingual/S
+monolithic
+monolithically
+monolith/M
+monoliths
+monologist/S
+monologue/GMSD
+monomaniacal
+monomaniac/MS
+monomania/MS
+monomeric
+monomer/SM
+monomial/SM
+mono/MS
+Monongahela/M
+mononuclear
+mononucleoses
+mononucleosis/M
+monophonic
+monoplane/MS
+monopole/S
+monopolistic
+monopolist/MS
+monopolization/MS
+monopolized/U
+monopolize/GZDSR
+monopolizes/U
+monopoly/MS
+monorail/SM
+monostable
+monosyllabic
+monosyllable/MS
+monotheism/SM
+monotheistic
+monotheist/S
+monotone/SDMG
+monotonic
+monotonically
+monotonicity
+monotonousness/MS
+monotonous/YP
+monotony/MS
+monoxide/SM
+Monroe/M
+Monro/M
+Monrovia/M
+Monsanto/M
+monseigneur
+monsieur/M
+Monsignori
+Monsignor/MS
+monsignor/S
+Mon/SM
+monsoonal
+monsoon/MS
+monster/SM
+monstrance/ASM
+monstrosity/SM
+monstrousness/M
+monstrous/YP
+montage/SDMG
+Montague/M
+Montaigne/M
+Montana/M
+Montanan/MS
+Montcalm/M
+Montclair/M
+Monte/M
+Montenegrin
+Montenegro/M
+Monterey/M
+Monterrey/M
+Montesquieu/M
+Montessori/M
+Monteverdi/M
+Montevideo/M
+Montezuma
+Montgomery/M
+monthly/S
+month/MY
+months
+Monticello/M
+Monti/M
+Mont/M
+Montmartre/M
+Montoya/M
+Montpelier/M
+Montrachet/M
+Montreal/M
+Montserrat/M
+Monty/M
+monumentality/M
+monumental/Y
+monument/DMSG
+mooch/ZSRDG
+moodily
+moodiness/MS
+mood/MS
+Moody/M
+moody/PTR
+Moog
+moo/GSD
+moonbeam/SM
+Mooney/M
+moon/GDMS
+moonless
+moonlight/GZDRMS
+moonlighting/M
+moonlit
+Moon/M
+moonscape/MS
+moonshiner/M
+moonshine/SRZM
+moonshot/MS
+moonstone/SM
+moonstruck
+moonwalk/SDG
+Moore/M
+moor/GDMJS
+mooring/M
+Moorish
+moorland/MS
+Moor/MS
+moose/M
+moot/RDGS
+moped/MS
+moper/M
+mope/S
+mopey
+mopier
+mopiest
+mopish
+mopped
+moppet/MS
+mopping
+mop/SZGMDR
+moraine/MS
+morale/MS
+Morales/M
+moralistic
+moralistically
+moralist/MS
+morality/UMS
+moralization/CS
+moralize/CGDRSZ
+moralled
+moraller
+moralling
+moral/SMY
+Mora/M
+Moran/M
+morass/SM
+moratorium/SM
+Moravia/M
+Moravian
+moray/SM
+morbidity/SM
+morbidness/S
+morbid/YP
+mordancy/MS
+mordant/GDYS
+Mordecai/M
+Mord/M
+Mordred/M
+Mordy/M
+more/DSN
+Moreen/M
+Morehouse/M
+Moreland/M
+morel/SM
+More/M
+Morena/M
+Moreno/M
+moreover
+Morey/M
+Morgana/M
+Morganica/M
+Morgan/MS
+Morganne/M
+morgen/M
+Morgen/M
+morgue/SM
+Morgun/M
+Moria/M
+Moriarty/M
+moribundity/M
+moribund/Y
+Morie/M
+Morin/M
+morion/M
+Morison/M
+Morissa/M
+Morita/M
+Moritz/M
+Morlee/M
+Morley/M
+Morly/M
+Mormonism/MS
+Mormon/SM
+Morna/M
+morning/MY
+morn/SGJDM
+Moroccan/S
+Morocco/M
+morocco/SM
+Moro/M
+moronic
+moronically
+Moroni/M
+moron/SM
+moroseness/MS
+morose/YP
+morpheme/DSMG
+morphemic/S
+Morpheus/M
+morph/GDJ
+morphia/S
+morphine/MS
+morphism/MS
+morphologic
+morphological/Y
+morphology/MS
+morphophonemic/S
+morphophonemics/M
+morphs
+Morrie/M
+morris
+Morris/M
+Morrison/M
+Morristown/M
+Morrow/M
+morrow/MS
+Morry/M
+morsel/GMDS
+Morse/M
+mortality/SM
+mortal/SY
+mortarboard/SM
+mortar/GSDM
+Morten/M
+mortgageable
+mortgagee/SM
+mortgage/MGDS
+mortgagor/SM
+mortice's
+mortician/SM
+Mortie/M
+mortification/M
+mortified/Y
+mortifier/M
+mortify/DRSXGN
+Mortimer/M
+mortise/MGSD
+Mort/MN
+Morton/M
+mortuary/MS
+Morty/M
+Mosaic
+mosaicked
+mosaicking
+mosaic/MS
+Moscone/M
+Moscow/M
+Moseley/M
+Moselle/M
+Mose/MSR
+Moser/M
+mosey/SGD
+Moshe/M
+Moslem's
+Mosley/M
+mosque/SM
+mosquitoes
+mosquito/M
+mos/S
+mossback/MS
+Mossberg/M
+Moss/M
+moss/SDMG
+mossy/SRT
+most/SY
+Mosul/M
+mote/ASCNK
+motel/MS
+mote's
+motet/SM
+mothball/DMGS
+motherboard/MS
+motherfucker/MS
+motherfucking
+motherhood/SM
+mothering/M
+motherland/SM
+motherless
+motherliness/MS
+motherly/P
+mother/RDYMZG
+moths
+moth/ZMR
+motif/MS
+motile/S
+motility/MS
+motional/K
+motioner/M
+motion/GRDMS
+motionlessness/S
+motionless/YP
+motion's/ACK
+motions/K
+motivated/U
+motivate/XDSNGV
+motivational/Y
+motivation/M
+motivator/S
+motiveless
+motive/MGSD
+motley/S
+motlier
+motliest
+mot/MSV
+motocross/SM
+motorbike/SDGM
+motorboat/MS
+motorcade/MSDG
+motorcar/MS
+motorcycle/GMDS
+motorcyclist/SM
+motor/DMSG
+motoring/M
+motorist/SM
+motorization/SM
+motorize/DSG
+motorized/U
+motorman/M
+motormen
+motormouth
+motormouths
+Motorola/M
+motorway/SM
+Motown/M
+mottle/GSRD
+mottler/M
+Mott/M
+mottoes
+motto/M
+moue/DSMG
+moulder/DSG
+moult/GSD
+mound/GMDS
+mountable
+mountaineering/M
+mountaineer/JMDSG
+mountainousness/M
+mountainous/PY
+mountainside/MS
+mountain/SM
+mountaintop/SM
+Mountbatten/M
+mountebank/SGMD
+mounted/U
+mount/EGACD
+mounter/SM
+mounties
+Mountie/SM
+mounting/MS
+Mount/M
+mounts/AE
+mourner/M
+mournfuller
+mournfullest
+mournfulness/S
+mournful/YP
+mourning/M
+mourn/ZGSJRD
+mouser/M
+mouse/SRDGMZ
+mousetrapped
+mousetrapping
+mousetrap/SM
+mousiness/MS
+mousing/M
+mousse/MGSD
+Moussorgsky/M
+mousy/PRT
+Mouthe/M
+mouthful/MS
+mouthiness/SM
+mouth/MSRDG
+mouthorgan
+mouthpiece/SM
+mouths
+mouthwash/SM
+mouthwatering
+mouthy/PTR
+Mouton/M
+mouton/SM
+movable/ASP
+movableness/AM
+move/ARSDGZB
+moved/U
+movement/SM
+mover/AM
+moviegoer/S
+movie/SM
+moving/YS
+mower/M
+Mowgli/M
+mowing/M
+mow/SDRZG
+moxie/MS
+Moyer/M
+Moyna/M
+Moyra/M
+Mozambican/S
+Mozambique/M
+Mozart/M
+Mozelle/M
+Mozes/M
+mozzarella/MS
+mp
+MP
+mpg
+mph
+MPH
+MRI
+Mr/M
+Mrs
+ms
+M's
+MS
+MSG
+Msgr/M
+m's/K
+Ms/S
+MST
+MSW
+mt
+MT
+mtg
+mtge
+Mt/M
+MTS
+MTV
+Muawiya/M
+Mubarak/M
+muchness/M
+much/SP
+mucilage/MS
+mucilaginous
+mucker/M
+muck/GRDMS
+muckraker/M
+muckrake/ZMDRSG
+mucky/RT
+mucosa/M
+mucous
+mucus/SM
+mudded
+muddily
+muddiness/SM
+mudding
+muddle/GRSDZ
+muddleheaded/P
+muddlehead/SMD
+muddler/M
+muddy/TPGRSD
+mudflat/S
+mudguard/SM
+mudlarks
+mud/MS
+mudroom/S
+mudslide/S
+mudslinger/M
+mudslinging/M
+mudsling/JRGZ
+Mueller/M
+Muenster
+muenster/MS
+muesli/M
+muezzin/MS
+muff/GDMS
+Muffin/M
+muffin/SM
+muffler/M
+muffle/ZRSDG
+Mufi/M
+Mufinella/M
+mufti/MS
+Mugabe/M
+mugged
+mugger/SM
+mugginess/S
+mugging/S
+muggy/RPT
+mugshot/S
+mug/SM
+mugwump/MS
+Muhammadanism/S
+Muhammadan/SM
+Muhammad/M
+Muire/M
+Muir/M
+Mukden/M
+mukluk/SM
+mulattoes
+mulatto/M
+mulberry/MS
+mulch/GMSD
+mulct/SDG
+Mulder/M
+mule/MGDS
+muleskinner/S
+muleteer/MS
+mulishness/MS
+mulish/YP
+mullah/M
+mullahs
+mullein/MS
+Mullen/M
+muller/M
+Muller/M
+mullet/MS
+Mulligan/M
+mulligan/SM
+mulligatawny/SM
+Mullikan/M
+Mullins
+mullion/MDSG
+mull/RDSG
+Multan/M
+multi
+Multibus/M
+multicellular
+multichannel/M
+multicollinearity/M
+multicolor/SDM
+multicolumn
+multicomponent
+multicomputer/MS
+Multics/M
+MULTICS/M
+multicultural
+multiculturalism/S
+multidimensional
+multidimensionality
+multidisciplinary
+multifaceted
+multifamily
+multifariousness/SM
+multifarious/YP
+multifigure
+multiform
+multifunction/D
+multilateral/Y
+multilayer
+multilevel/D
+multilingual
+multilingualism/S
+multimedia/S
+multimegaton/M
+multimeter/M
+multimillionaire/SM
+multinational/S
+multinomial/M
+multiphase
+multiple/SM
+multiplet/SM
+multiplex/GZMSRD
+multiplexor's
+multipliable
+multiplicand/SM
+multiplication/M
+multiplicative/YS
+multiplicity/MS
+multiplier/M
+multiply/ZNSRDXG
+multiprocess/G
+multiprocessor/MS
+multiprogram
+multiprogrammed
+multiprogramming/MS
+multipurpose
+multiracial
+multistage
+multistory/S
+multisyllabic
+multitasking/S
+multitude/MS
+multitudinousness/M
+multitudinous/YP
+multiuser
+multivalent
+multivalued
+multivariate
+multiversity/M
+multivitamin/S
+mu/M
+mumbler/M
+mumbletypeg/S
+mumble/ZJGRSD
+Mumford/M
+mummed
+mummer/SM
+mummery/MS
+mummification/M
+mummify/XSDGN
+mumming
+mum/MS
+mummy/GSDM
+mumps/M
+muncher/M
+Münchhausen/M
+munchies
+Munch/M
+munch/ZRSDG
+Muncie/M
+mundane/YSP
+Mundt/M
+munge/JGZSRD
+Munich/M
+municipality/SM
+municipal/YS
+munificence/MS
+munificent/Y
+munition/SDG
+Munmro/M
+Munoz/M
+Munroe/M
+Munro/M
+mun/S
+Munsey/M
+Munson/M
+Munster/MS
+Muong/M
+muon/M
+Muppet/M
+muralist/SM
+mural/SM
+Murasaki/M
+Murat/M
+Murchison/M
+Murcia/M
+murderer/M
+murderess/S
+murder/GZRDMS
+murderousness/M
+murderous/YP
+Murdoch/M
+Murdock/M
+Mureil/M
+Murial/M
+muriatic
+Murielle/M
+Muriel/M
+Murillo/M
+murkily
+murkiness/S
+murk/TRMS
+murky/RPT
+Murmansk/M
+murmurer/M
+murmuring/U
+murmurous
+murmur/RDMGZSJ
+Murphy/M
+murrain/SM
+Murray/M
+Murrow/M
+Murrumbidgee/M
+Murry/M
+Murvyn/M
+muscatel/MS
+Muscat/M
+muscat/SM
+musclebound
+muscle/SDMG
+Muscovite/M
+muscovite/MS
+Muscovy/M
+muscularity/SM
+muscular/Y
+musculature/SM
+muse
+Muse/M
+muser/M
+musette/SM
+museum/MS
+mus/GJDSR
+musher/M
+mushiness/MS
+mush/MSRDG
+mushroom/DMSG
+mushy/PTR
+Musial/M
+musicale/SM
+musicality/SM
+musicals
+musical/YU
+musician/MYS
+musicianship/MS
+musicked
+musicking
+musicological
+musicologist/MS
+musicology/MS
+music/SM
+musing/Y
+Muskegon/M
+muskeg/SM
+muskellunge/SM
+musketeer/MS
+musketry/MS
+musket/SM
+musk/GDMS
+muskie/M
+muskiness/MS
+muskmelon/MS
+muskox/N
+muskrat/MS
+musky/RSPT
+Muslim/MS
+muslin/MS
+mussel/MS
+Mussolini/MS
+Mussorgsky/M
+muss/SDG
+mussy/RT
+mustache/DSM
+mustachio/MDS
+mustang/MS
+mustard/MS
+muster/GD
+mustily
+mustiness/MS
+mustn't
+must/RDGZS
+must've
+musty/RPT
+mutability/SM
+mutableness/M
+mutable/P
+mutably
+mutagen/SM
+mutant/MS
+mutate/XVNGSD
+mutational/Y
+mutation/M
+mutator/S
+muted/Y
+muteness/S
+mute/PDSRBYTG
+mutilate/XDSNG
+mutilation/M
+mutilator/MS
+mutineer/SMDG
+mutinous/Y
+mutiny/MGSD
+Mutsuhito/M
+mutterer/M
+mutter/GZRDJ
+muttonchops
+mutton/SM
+mutt/ZSMR
+mutuality/S
+mutual/SY
+muumuu/MS
+muzak
+Muzak/SM
+Muzo/M
+muzzled/U
+muzzle/MGRSD
+muzzler/M
+MVP
+MW
+Myanmar
+Mycah/M
+Myca/M
+Mycenaean
+Mycenae/M
+Mychal/M
+mycologist/MS
+mycology/MS
+myelitides
+myelitis/M
+Myer/MS
+myers
+mylar
+Mylar/S
+Myles/M
+Mylo/M
+My/M
+myna/SM
+Mynheer/M
+myocardial
+myocardium/M
+myopia/MS
+myopically
+myopic/S
+Myrah/M
+Myra/M
+Myranda/M
+Myrdal/M
+myriad/S
+Myriam/M
+Myrilla/M
+Myrle/M
+Myrlene/M
+myrmidon/S
+Myrna/M
+Myron/M
+myrrh/M
+myrrhs
+Myrta/M
+Myrtia/M
+Myrtice/M
+Myrtie/M
+Myrtle/M
+myrtle/SM
+Myrvyn/M
+Myrwyn/M
+mys
+my/S
+myself
+Mysore/M
+mysteriousness/MS
+mysterious/YP
+mystery/MDSG
+mystical/Y
+mysticism/MS
+mystic/SM
+mystification/M
+mystifier/M
+mystify/CSDGNX
+mystifying/Y
+mystique/MS
+Myst/M
+mythic
+mythical/Y
+myth/MS
+mythographer/SM
+mythography/M
+mythological/Y
+mythologist/MS
+mythologize/CSDG
+mythology/SM
+myths
+N
+NAACP
+nabbed
+nabbing
+Nabisco/M
+nabob/SM
+Nabokov/M
+nab/S
+nacelle/SM
+nacho/S
+NaCl/M
+nacre/MS
+nacreous
+Nada/M
+Nadean/M
+Nadeen/M
+Nader/M
+Nadia/M
+Nadine/M
+nadir/SM
+Nadiya/M
+Nadya/M
+Nady/M
+nae/VM
+Nagasaki/M
+nagged
+nagger/S
+nagging/Y
+nag/MS
+Nagoya/M
+Nagpur/M
+Nagy/M
+Nahuatl/SM
+Nahum/M
+naiad/SM
+naifs
+nailbrush/SM
+nailer/M
+nail/SGMRD
+Naipaul/M
+Nair/M
+Nairobi/M
+Naismith/M
+naive/SRTYP
+naiveté/SM
+naivety/MS
+Nakamura/M
+Nakayama/M
+nakedness/MS
+naked/TYRP
+Nakoma/M
+Nalani/M
+Na/M
+Namath/M
+nameable/U
+name/ADSG
+namedrop
+namedropping
+named's
+named/U
+nameless/PY
+namely
+nameplate/MS
+namer/SM
+name's
+namesake/SM
+Namibia/M
+Namibian/S
+naming/M
+Nam/M
+Nanak/M
+Nana/M
+Nananne/M
+Nancee/M
+Nance/M
+Nancey/M
+Nanchang/M
+Nancie/M
+Nanci/M
+Nancy/M
+Nanete/M
+Nanette/M
+Nanice/M
+Nani/M
+Nanine/M
+Nanjing
+Nanking's
+Nan/M
+Nannette/M
+Nannie/M
+Nanni/M
+Nanny/M
+nanny/SDMG
+nanometer/MS
+Nanon/M
+Nanook/M
+nanosecond/SM
+Nansen/M
+Nantes/M
+Nantucket/M
+Naoma/M
+Naomi/M
+napalm/MDGS
+nape/SM
+Naphtali/M
+naphthalene/MS
+naphtha/SM
+Napier/M
+napkin/SM
+Naples/M
+napless
+Nap/M
+Napoleonic
+napoleon/MS
+Napoleon/MS
+napped
+napper/MS
+Nappie/M
+napping
+Nappy/M
+nappy/TRSM
+nap/SM
+Nara/M
+Narbonne/M
+narc/DGS
+narcissism/MS
+narcissistic
+narcissist/MS
+narcissus/M
+Narcissus/M
+narcoleptic
+narcoses
+narcosis/M
+narcotic/SM
+narcotization/S
+narcotize/GSD
+Nariko/M
+Nari/M
+nark's
+Narmada/M
+Narragansett/M
+narrate/VGNSDX
+narration/M
+narrative/MYS
+narratology
+narrator/SM
+narrowing/P
+narrowness/SM
+narrow/RDYTGPS
+narwhal/MS
+nary
+nasality/MS
+nasalization/MS
+nasalize/GDS
+nasal/YS
+NASA/MS
+nascence/ASM
+nascent/A
+NASDAQ
+Nash/M
+Nashua/M
+Nashville/M
+Nassau/M
+Nasser/M
+nastily
+nastiness/MS
+nasturtium/SM
+nasty/TRSP
+natal
+Natala/M
+Natalee/M
+Natale/M
+Natalia/M
+Natalie/M
+Natalina/M
+Nataline/M
+natalist
+natality/M
+Natal/M
+Natalya/M
+Nata/M
+Nataniel/M
+Natasha/M
+Natassia/M
+Natchez
+natch/S
+Nate/XMN
+Nathalia/M
+Nathalie/M
+Nathanael/M
+Nathanial/M
+Nathaniel/M
+Nathanil/M
+Nathan/MS
+nationalism/SM
+nationalistic
+nationalistically
+nationalist/MS
+nationality/MS
+nationalization/MS
+nationalize/CSDG
+nationalized/AU
+nationalizer/SM
+national/YS
+nationhood/SM
+nation/MS
+nationwide
+nativeness/M
+native/PYS
+Natividad/M
+Nativity/M
+nativity/MS
+Natka/M
+natl
+Nat/M
+NATO/SM
+natter/SGD
+nattily
+nattiness/SM
+Natty/M
+natty/TRP
+naturalism/MS
+naturalistic
+naturalist/MS
+naturalization/SM
+naturalized/U
+naturalize/GSD
+naturalness/US
+natural/PUY
+naturals
+nature/ASDCG
+nature's
+naturist
+Naugahyde/S
+naughtily
+naughtiness/SM
+naught/MS
+naughty/TPRS
+Naur/M
+Nauru/M
+nausea/SM
+nauseate/DSG
+nauseating/Y
+nauseousness/SM
+nauseous/P
+nautical/Y
+nautilus/MS
+Navaho's
+Navajoes
+Navajo/S
+naval/Y
+Navarro/M
+navel/MS
+nave/SM
+navigability/SM
+navigableness/M
+navigable/P
+navigate/DSXNG
+navigational
+navigation/M
+navigator/MS
+Navona/M
+Navratilova/M
+navvy/M
+Navy/S
+navy/SM
+nay/MS
+naysayer/S
+Nazarene/MS
+Nazareth/M
+Nazi/SM
+Nazism/S
+NB
+NBA
+NBC
+Nb/M
+NBS
+NC
+NCAA
+NCC
+NCO
+NCR
+ND
+N'Djamena
+Ndjamena/M
+Nd/M
+Ne
+NE
+Neala/M
+Neale/M
+Neall/M
+Neal/M
+Nealon/M
+Nealson/M
+Nealy/M
+Neanderthal/S
+neap/DGS
+Neapolitan/SM
+nearby
+nearly/RT
+nearness/MS
+nearside/M
+nearsightedness/S
+nearsighted/YP
+near/TYRDPSG
+neaten/DG
+neath
+neatness/MS
+neat/YRNTXPS
+Neb/M
+Nebraska/M
+Nebraskan/MS
+Nebr/M
+Nebuchadnezzar/MS
+nebulae
+nebula/M
+nebular
+nebulousness/SM
+nebulous/PY
+necessaries
+necessarily/U
+necessary/U
+necessitate/DSNGX
+necessitation/M
+necessitous
+necessity/SM
+neckband/M
+neckerchief/MS
+neck/GRDMJS
+necking/M
+necklace/DSMG
+neckline/MS
+necktie/MS
+necrology/SM
+necromancer/MS
+necromancy/MS
+necromantic
+necrophiliac/S
+necrophilia/M
+necropolis/SM
+necropsy/M
+necroses
+necrosis/M
+necrotic
+nectarine/SM
+nectarous
+nectar/SM
+nectary/MS
+Neda/M
+Nedda/M
+Neddie/M
+Neddy/M
+Nedi/M
+Ned/M
+née
+needed/U
+needer/M
+needful/YSP
+Needham/M
+neediness/MS
+needlecraft/M
+needle/GMZRSD
+needlepoint/SM
+needlessness/S
+needless/YP
+needlewoman/M
+needlewomen
+needlework/RMS
+needn't
+need/YRDGS
+needy/TPR
+Neel/M
+Neely/M
+ne'er
+nefariousness/MS
+nefarious/YP
+Nefen/M
+Nefertiti/M
+negated/U
+negater/M
+negate/XRSDVNG
+negation/M
+negativeness/SM
+negative/PDSYG
+negativism/MS
+negativity/MS
+negator/MS
+Negev/M
+neglecter/M
+neglectfulness/SM
+neglectful/YP
+neglect/SDRG
+negligee/SM
+negligence/MS
+negligent/Y
+negligibility/M
+negligible
+negligibly
+negotiability/MS
+negotiable/A
+negotiant/M
+negotiate/ASDXGN
+negotiation/MA
+negotiator/MS
+Negress/MS
+negritude/MS
+Negritude/S
+Negroes
+negroid
+Negroid/S
+Negro/M
+neg/S
+Nehemiah/M
+Nehru/M
+neighbored/U
+neighborer/M
+neighborhood/SM
+neighborlinesses
+neighborliness/UM
+neighborly/UP
+neighbor/SMRDYZGJ
+neigh/MDG
+neighs
+Neila/M
+Neile/M
+Neilla/M
+Neille/M
+Neill/M
+Neil/SM
+neither
+Nelda/M
+Nelia/M
+Nelie/M
+Nelle/M
+Nellie/M
+Nelli/M
+Nell/M
+Nelly/M
+Nelsen/M
+Nels/N
+Nelson/M
+nelson/MS
+nematic
+nematode/SM
+Nembutal/M
+nemeses
+nemesis
+Nemesis/M
+neoclassical
+neoclassicism/MS
+neoclassic/M
+neocolonialism/MS
+neocortex/M
+neodymium/MS
+Neogene
+neolithic
+Neolithic/M
+neologism/SM
+neomycin/M
+neonatal/Y
+neonate/MS
+neon/DMS
+neophyte/MS
+neoplasm/SM
+neoplastic
+neoprene/SM
+Nepalese
+Nepali/MS
+Nepal/M
+nepenthe/MS
+nephew/MS
+nephrite/SM
+nephritic
+nephritides
+nephritis/M
+nepotism/MS
+nepotist/S
+Neptune/M
+neptunium/MS
+nerd/S
+nerdy/RT
+Nereid/M
+Nerf/M
+Nerissa/M
+Nerita/M
+Nero/M
+Neron/M
+Nerta/M
+Nerte/M
+Nertie/M
+Nerti/M
+Nert/M
+Nerty/M
+Neruda/M
+nervelessness/SM
+nerveless/YP
+nerve's
+nerve/UGSD
+nerviness/SM
+nerving/M
+nervousness/SM
+nervous/PY
+nervy/TPR
+Nessa/M
+Nessie/M
+Nessi/M
+Nessy/M
+Nesta/M
+nester/M
+Nester/M
+Nestle/M
+nestler/M
+nestle/RSDG
+nestling/M
+Nestorius/M
+Nestor/M
+nest/RDGSBM
+netball/M
+nether
+Netherlander/SM
+Netherlands/M
+nethermost
+netherworld/S
+Netscape/M
+net/SM
+Netta/M
+Nettie/M
+Netti/M
+netting/M
+nett/JGRDS
+Nettle/M
+nettle/MSDG
+nettlesome
+Netty/M
+network/SJMDG
+Netzahualcoyotl/M
+Neumann/M
+neuralgia/MS
+neuralgic
+neural/Y
+neurasthenia/MS
+neurasthenic/S
+neuritic/S
+neuritides
+neuritis/M
+neuroanatomy
+neurobiology/M
+neurological/Y
+neurologist/MS
+neurology/SM
+neuromuscular
+neuronal
+neurone/S
+neuron/MS
+neuropathology/M
+neurophysiology/M
+neuropsychiatric
+neuroses
+neurosis/M
+neurosurgeon/MS
+neurosurgery/SM
+neurotically
+neurotic/S
+neurotransmitter/S
+neuter/JZGRD
+neutralise's
+neutralism/MS
+neutralist/S
+neutrality/MS
+neutralization/MS
+neutralized/U
+neutralize/GZSRD
+neutral/PYS
+neutrino/MS
+neutron/MS
+neut/ZR
+Nevada/M
+Nevadan/S
+Nevadian/S
+Neva/M
+never
+nevermore
+nevertheless
+nevi
+Nevile/M
+Neville/M
+Nevil/M
+Nevin/SM
+Nevis/M
+Nev/M
+Nevsa/M
+Nevsky/M
+nevus/M
+Newark/M
+newbie/S
+newborn/S
+Newbury/M
+Newburyport/M
+Newcastle/M
+newcomer/MS
+newed/A
+Newell/M
+newel/MS
+newer/A
+newfangled
+newfound
+newfoundland
+Newfoundlander/M
+Newfoundland/SRMZ
+newish
+newline/SM
+newlywed/MS
+Newman/M
+newness/MS
+Newport/M
+news/A
+newsagent/MS
+newsboy/SM
+newscaster/M
+newscasting/M
+newscast/SRMGZ
+newsdealer/MS
+newsed
+newses
+newsflash/S
+newsgirl/S
+newsgroup/SM
+newsing
+newsletter/SM
+NeWS/M
+newsman/M
+newsmen
+newspaperman/M
+newspapermen
+newspaper/SMGD
+newspaperwoman/M
+newspaperwomen
+newsprint/MS
+new/SPTGDRY
+newsreader/MS
+newsreel/SM
+newsroom/S
+news's
+newsstand/MS
+Newsweekly/M
+newsweekly/S
+Newsweek/MY
+newswire
+newswoman/M
+newswomen
+newsworthiness/SM
+newsworthy/RPT
+newsy/TRS
+newt/MS
+Newtonian
+Newton/M
+newton/SM
+Nexis/M
+next
+nexus/SM
+Neysa/M
+NF
+NFC
+NFL
+NFS
+Ngaliema/M
+Nguyen/M
+NH
+NHL
+niacin/SM
+Niagara/M
+Niall/M
+Nial/M
+Niamey/M
+nibbed
+nibbing
+nibbler/M
+nibble/RSDGZ
+Nibelung/M
+nib/SM
+Nicaean
+Nicaragua/M
+Nicaraguan/S
+Niccolo/M
+Nice/M
+Nicene
+niceness/MS
+nicety/MS
+nice/YTPR
+niche/SDGM
+Nicholas
+Nichole/M
+Nicholle/M
+Nichol/MS
+Nicholson/M
+nichrome
+nickelodeon/SM
+nickel/SGMD
+nicker/GD
+Nickey/M
+nick/GZRDMS
+Nickie/M
+Nicki/M
+Nicklaus/M
+Nick/M
+nicknack's
+nickname/MGDRS
+nicknamer/M
+Nickolai/M
+Nickola/MS
+Nickolaus/M
+Nicko/M
+Nicky/M
+Nicobar/M
+Nicodemus/M
+Nicolai/MS
+Nicola/MS
+Nicolea/M
+Nicole/M
+Nicolette/M
+Nicoli/MS
+Nicolina/M
+Nicoline/M
+Nicolle/M
+Nicol/M
+Nico/M
+Nicosia/M
+nicotine/MS
+Niebuhr/M
+niece/MS
+Niel/MS
+Nielsen/M
+Niels/N
+Nielson/M
+Nietzsche/M
+Nieves/M
+nifty/TRS
+Nigel/M
+Nigeria/M
+Nigerian/S
+Nigerien
+Niger/M
+niggardliness/SM
+niggardly/P
+niggard/SGMDY
+nigger/SGDM
+niggler/M
+niggle/RSDGZJ
+niggling/Y
+nigh/RDGT
+nighs
+nightcap/SM
+nightclothes
+nightclubbed
+nightclubbing
+nightclub/MS
+nightdress/MS
+nightfall/SM
+nightgown/MS
+nighthawk/MS
+nightie/MS
+Nightingale/M
+nightingale/SM
+nightlife/MS
+nightlong
+nightmare/MS
+nightmarish/Y
+nightshade/SM
+nightshirt/MS
+night/SMYDZ
+nightspot/MS
+nightstand/SM
+nightstick/S
+nighttime/S
+nightwear/M
+nighty's
+NIH
+nihilism/MS
+nihilistic
+nihilist/MS
+Nijinsky/M
+Nikaniki/M
+Nike/M
+Niki/M
+Nikita/M
+Nikkie/M
+Nikki/M
+Nikko/M
+Nikolai/M
+Nikola/MS
+Nikolaos/M
+Nikolaus/M
+Nikolayev's
+Nikoletta/M
+Nikolia/M
+Nikolos/M
+Niko/MS
+Nikon/M
+Nile/SM
+nilled
+nilling
+Nil/MS
+nil/MYS
+nilpotent
+Nilsen/M
+Nils/N
+Nilson/M
+Nilsson/M
+Ni/M
+nimbi
+nimbleness/SM
+nimble/TRP
+nimbly
+nimbus/DM
+NIMBY
+Nimitz/M
+Nimrod/MS
+Nina/M
+nincompoop/MS
+ninefold
+nine/MS
+ninepence/M
+ninepin/S
+ninepins/M
+nineteen/SMH
+nineteenths
+ninetieths
+Ninetta/M
+Ninette/M
+ninety/MHS
+Nineveh/M
+ninja/S
+Ninnetta/M
+Ninnette/M
+ninny/SM
+Ninon/M
+Nintendo/M
+ninth
+ninths
+Niobe/M
+niobium/MS
+nipped
+nipper/DMGS
+nippiness/S
+nipping/Y
+nipple/GMSD
+Nipponese
+Nippon/M
+nippy/TPR
+nip/S
+Nirenberg/M
+nirvana/MS
+Nirvana/S
+nisei
+Nisei/MS
+Nissa/M
+Nissan/M
+Nisse/M
+Nissie/M
+Nissy/M
+Nita/M
+niter/M
+nitpick/DRSJZG
+nitrate/MGNXSD
+nitration/M
+nitric
+nitride/MGS
+nitriding/M
+nitrification/SM
+nitrite/MS
+nitrocellulose/MS
+nitrogenous
+nitrogen/SM
+nitroglycerin/MS
+nitrous
+nitwit/MS
+nit/ZSMR
+Niven/M
+nixer/M
+nix/GDSR
+Nixie/M
+Nixon/M
+NJ
+Nkrumah/M
+NLRB
+nm
+NM
+no/A
+NOAA
+Noach/M
+Noah/M
+Noak/M
+Noami/M
+Noam/M
+Nobelist/SM
+nobelium/MS
+Nobel/M
+Nobe/M
+Nobie/M
+nobility/MS
+Noble/M
+nobleman/M
+noblemen
+nobleness/SM
+noblesse/M
+noble/TPSR
+noblewoman
+noblewomen
+nob/MY
+nobody/MS
+Noby/M
+nocturnal/SY
+nocturne/SM
+nodal/Y
+nodded
+nodding
+noddle/MSDG
+noddy/M
+node/MS
+NoDoz/M
+nod/SM
+nodular
+nodule/SM
+Noelani/M
+Noella/M
+Noelle/M
+Noell/M
+Noellyn/M
+Noel/MS
+noel/S
+Noelyn/M
+Noe/M
+Noemi/M
+noes/S
+noggin/SM
+nohow
+noise/GMSD
+noiselessness/SM
+noiseless/YP
+noisemaker/M
+noisemake/ZGR
+noisily
+noisiness/MS
+noisome
+noisy/TPR
+Nola/M
+Nolana/M
+Noland/M
+Nolan/M
+Nolie/M
+Nollie/M
+Noll/M
+Nolly/M
+No/M
+nomadic
+nomad/SM
+Nome/M
+nomenclature/MS
+Nomi/M
+nominalized
+nominal/K
+nominally
+nominals
+nominate/CDSAXNG
+nomination/MAC
+nominative/SY
+nominator/CSM
+nominee/MS
+non
+nonabrasive
+nonabsorbent/S
+nonacademic/S
+nonacceptance/MS
+nonacid/MS
+nonactive
+nonadaptive
+nonaddictive
+nonadhesive
+nonadjacent
+nonadjustable
+nonadministrative
+nonage/MS
+nonagenarian/MS
+nonaggression/SM
+nonagricultural
+Nonah/M
+nonalcoholic/S
+nonaligned
+nonalignment/SM
+nonallergic
+Nona/M
+nonappearance/MS
+nonassignable
+nonathletic
+nonattendance/SM
+nonautomotive
+nonavailability/SM
+nonbasic
+nonbeliever/SM
+nonbelligerent/S
+nonblocking
+nonbreakable
+nonburnable
+nonbusiness
+noncaloric
+noncancerous
+noncarbohydrate/M
+nonce/MS
+nonchalance/SM
+nonchalant/YP
+nonchargeable
+nonclerical/S
+nonclinical
+noncollectable
+noncombatant/MS
+noncombustible/S
+noncommercial/S
+noncommissioned
+noncommittal/Y
+noncom/MS
+noncommunicable
+noncompeting
+noncompetitive
+noncompliance/MS
+noncomplying/S
+noncomprehending
+nonconducting
+nonconductor/MS
+nonconforming
+nonconformist/SM
+nonconformity/SM
+nonconsecutive
+nonconservative
+nonconstructive
+noncontagious
+noncontiguous
+noncontinuous
+noncontributing
+noncontributory
+noncontroversial
+nonconvertible
+noncooperation/SM
+noncorroding/S
+noncorrosive
+noncredit
+noncriminal/S
+noncritical
+noncrystalline
+noncumulative
+noncustodial
+noncyclic
+nondairy
+nondecreasing
+nondeductible
+nondelivery/MS
+nondemocratic
+nondenominational
+nondepartmental
+nondepreciating
+nondescript/YS
+nondestructive/Y
+nondetachable
+nondeterminacy
+nondeterminate/Y
+nondeterminism
+nondeterministic
+nondeterministically
+nondisciplinary
+nondisclosure/SM
+nondiscrimination/SM
+nondiscriminatory
+nondramatic
+nondrinker/SM
+nondrying
+nondurable
+noneconomic
+noneducational
+noneffective/S
+nonelastic
+nonelectrical
+nonelectric/S
+nonemergency
+nonempty
+nonenforceable
+nonentity/MS
+nonequivalence/M
+nonequivalent/S
+none/S
+nones/M
+nonessential/S
+nonesuch/SM
+nonetheless
+nonevent/MS
+nonexchangeable
+nonexclusive
+nonexempt
+nonexistence/MS
+nonexistent
+nonexplosive/S
+nonextensible
+nonfactual
+nonfading
+nonfat
+nonfatal
+nonfattening
+nonferrous
+nonfictional
+nonfiction/SM
+nonflammable
+nonflowering
+nonfluctuating
+nonflying
+nonfood/M
+nonfreezing
+nonfunctional
+nongovernmental
+nongranular
+nonhazardous
+nonhereditary
+nonhuman
+nonidentical
+Nonie/M
+Noni/M
+noninclusive
+nonindependent
+nonindustrial
+noninfectious
+noninflammatory
+noninflationary
+noninflected
+nonintellectual/S
+noninteracting
+noninterchangeable
+noninterference/MS
+nonintervention/SM
+nonintoxicating
+nonintuitive
+noninvasive
+nonionic
+nonirritating
+nonjudgmental
+nonjudicial
+nonlegal
+nonlethal
+nonlinearity/MS
+nonlinear/Y
+nonlinguistic
+nonliterary
+nonliving
+nonlocal
+nonmagical
+nonmagnetic
+nonmalignant
+nonmember/SM
+nonmetallic
+nonmetal/MS
+nonmigratory
+nonmilitant/S
+nonmilitary
+Nonnah/M
+Nonna/M
+nonnarcotic/S
+nonnative/S
+nonnegative
+nonnegotiable
+nonnuclear
+nonnumerical/S
+nonobjective
+nonobligatory
+nonobservance/MS
+nonobservant
+nonoccupational
+nonoccurence
+nonofficial
+nonogenarian
+nonoperational
+nonoperative
+nonorthogonal
+nonorthogonality
+nonparallel/S
+nonparametric
+nonpareil/SM
+nonparticipant/SM
+nonparticipating
+nonpartisan/S
+nonpaying
+nonpayment/SM
+nonperformance/SM
+nonperforming
+nonperishable/S
+nonperson/S
+nonperturbing
+nonphysical/Y
+nonplus/S
+nonplussed
+nonplussing
+nonpoisonous
+nonpolitical
+nonpolluting
+nonporous
+nonpracticing
+nonprejudicial
+nonprescription
+nonprocedural/Y
+nonproductive
+nonprofessional/S
+nonprofit/SB
+nonprogrammable
+nonprogrammer
+nonproliferation/SM
+nonpublic
+nonpunishable
+nonracial
+nonradioactive
+nonrandom
+nonreactive
+nonreciprocal/S
+nonreciprocating
+nonrecognition/SM
+nonrecoverable
+nonrecurring
+nonredeemable
+nonreducing
+nonrefillable
+nonrefundable
+nonreligious
+nonrenewable
+nonrepresentational
+nonresidential
+nonresident/SM
+nonresidual
+nonresistance/SM
+nonresistant/S
+nonrespondent/S
+nonresponse
+nonrestrictive
+nonreturnable/S
+nonrhythmic
+nonrigid
+nonsalaried
+nonscheduled
+nonscientific
+nonscoring
+nonseasonal
+nonsectarian
+nonsecular
+nonsegregated
+nonsense/MS
+nonsensicalness/M
+nonsensical/PY
+nonsensitive
+nonsexist
+nonsexual
+nonsingular
+nonskid
+nonslip
+nonsmoker/SM
+nonsmoking
+nonsocial
+nonspeaking
+nonspecialist/MS
+nonspecializing
+nonspecific
+nonspiritual/S
+nonstaining
+nonstandard
+nonstarter/SM
+nonstick
+nonstop
+nonstrategic
+nonstriking
+nonstructural
+nonsuccessive
+nonsupervisory
+nonsupport/GS
+nonsurgical
+nonsustaining
+nonsympathizer/M
+nontarnishable
+nontaxable/S
+nontechnical/Y
+nontenured
+nonterminal/MS
+nonterminating
+nontermination/M
+nontheatrical
+nonthinking/S
+nonthreatening
+nontoxic
+nontraditional
+nontransferable
+nontransparent
+nontrivial
+nontropical
+nonuniform
+nonunion/S
+nonuser/SM
+nonvenomous
+nonverbal/Y
+nonveteran/MS
+nonviable
+nonviolence/SM
+nonviolent/Y
+nonvirulent
+nonvocal
+nonvocational
+nonvolatile
+nonvolunteer/S
+nonvoter/MS
+nonvoting
+nonwhite/SM
+nonworking
+nonyielding
+nonzero
+noodle/GMSD
+nook/MS
+noonday/MS
+noon/GDMS
+nooning/M
+noontide/MS
+noontime/MS
+noose/SDGM
+nope/S
+NORAD/M
+noradrenalin
+noradrenaline/M
+Norah/M
+Nora/M
+Norbert/M
+Norberto/M
+Norbie/M
+Norby/M
+Nordhoff/M
+Nordic/S
+Nordstrom/M
+Norean/M
+Noreen/M
+Norene/M
+Norfolk/M
+nor/H
+Norina/M
+Norine/M
+normalcy/MS
+normality/SM
+normalization/A
+normalizations
+normalization's
+normalized/AU
+normalizes/AU
+normalize/SRDZGB
+normal/SY
+Norma/M
+Normand/M
+Normandy/M
+Norman/SM
+normativeness/M
+normative/YP
+Normie/M
+norm/SMGD
+Normy/M
+Norplant
+Norrie/M
+Norri/SM
+Norristown/M
+Norry/M
+Norse
+Norseman/M
+Norsemen
+Northampton/M
+northbound
+northeastern
+northeaster/YM
+Northeast/SM
+northeastward/S
+northeast/ZSMR
+northerly/S
+norther/MY
+Northerner/M
+northernmost
+northern/RYZS
+Northfield/M
+northing/M
+northland
+North/M
+northmen
+north/MRGZ
+Northrop/M
+Northrup/M
+norths
+Norths
+Northumberland/M
+northward/S
+northwestern
+northwester/YM
+northwest/MRZS
+Northwest/MS
+northwestward/S
+Norton/M
+Norwalk/M
+Norway/M
+Norwegian/S
+Norwich/M
+Norw/M
+nosebag/M
+nosebleed/SM
+nosecone/S
+nosedive/DSG
+nosed/V
+nosegay/MS
+nose/M
+Nosferatu/M
+nos/GDS
+nosh/MSDG
+nosily
+nosiness/MS
+nosing/M
+nostalgia/SM
+nostalgically
+nostalgic/S
+Nostradamus/M
+Nostrand/M
+nostril/SM
+nostrum/SM
+nosy/SRPMT
+notability/SM
+notableness/M
+notable/PS
+notably
+notarial
+notarization/S
+notarize/DSG
+notary/MS
+notate/VGNXSD
+notational/CY
+notation/CMSF
+notative/CF
+notch/MSDG
+not/DRGB
+notebook/MS
+note/CSDFG
+notedness/M
+noted/YP
+notepad/S
+notepaper/MS
+note's
+noteworthiness/SM
+noteworthy/P
+nothingness/SM
+nothing/PS
+noticeable/U
+noticeably
+noticeboard/S
+noticed/U
+notice/MSDG
+notifiable
+notification/M
+notifier/M
+notify/NGXSRDZ
+notional/Y
+notion/MS
+notoriety/S
+notoriousness/M
+notorious/YP
+Notre/M
+Nottingham/M
+notwithstanding
+Nouakchott/M
+nougat/MS
+Noumea/M
+noun/SMK
+nourish/DRSGL
+nourished/U
+nourisher/M
+nourishment/SM
+nous/M
+nouveau
+nouvelle
+novae
+Novak/M
+Nova/M
+nova/MS
+novelette/SM
+Novelia/M
+novelist/SM
+novelization/S
+novelize/GDS
+Novell/SM
+novella/SM
+novel/SM
+novelty/MS
+November/SM
+novena/SM
+novene
+Novgorod/M
+novice/MS
+novitiate/MS
+Nov/M
+Novocaine/M
+Novocain/S
+Novokuznetsk/M
+Novosibirsk/M
+NOW
+nowadays
+noway/S
+Nowell/M
+nowhere/S
+nowise
+now/S
+noxiousness/M
+noxious/PY
+Noyce/M
+Noyes/M
+nozzle/MS
+Np
+NP
+NRA
+nroff/M
+N's
+NS
+n's/CI
+NSF
+n/T
+NT
+nth
+nuance/SDM
+nubbin/SM
+nubby/RT
+Nubia/M
+Nubian/M
+nubile
+nub/MS
+nuclear/K
+nuclease/M
+nucleated/A
+nucleate/DSXNG
+nucleation/M
+nucleic
+nuclei/M
+nucleoli
+nucleolus/M
+nucleon/MS
+nucleotide/MS
+nucleus/M
+nuclide/M
+nude/CRS
+nudely
+nudeness/M
+nudest
+nudge/GSRD
+nudger/M
+nudism/MS
+nudist/MS
+nudity/MS
+nugatory
+Nugent/M
+nugget/SM
+nuisance/MS
+nuke/DSMG
+Nukualofa
+null/DSG
+nullification/M
+nullifier/M
+nullify/RSDXGNZ
+nullity/SM
+nu/M
+numbered/UA
+numberer/M
+numberless
+numberplate/M
+number/RDMGJ
+numbers/A
+Numbers/M
+numbing/Y
+numbness/MS
+numb/SGZTYRDP
+numbskull's
+numerable/IC
+numeracy/SI
+numeral/YMS
+numerate/SDNGX
+numerates/I
+numeration/M
+numerator/MS
+numerical/Y
+numeric/S
+numerological
+numerologist/S
+numerology/MS
+numerousness/M
+numerous/YP
+numinous/S
+numismatic/S
+numismatics/M
+numismatist/MS
+numskull/SM
+Nunavut/M
+nuncio/SM
+Nunez/M
+Nunki/M
+nun/MS
+nunnery/MS
+nuptial/S
+Nuremberg/M
+Nureyev/M
+nursemaid/MS
+nurser/M
+nurseryman/M
+nurserymen
+nursery/MS
+nurse/SRDJGMZ
+nursling/M
+nurturer/M
+nurture/SRDGZM
+nus
+nutate/NGSD
+nutation/M
+nutcracker/M
+nutcrack/RZ
+nuthatch/SM
+nutmeat/SM
+nutmegged
+nutmegging
+nutmeg/MS
+nut/MS
+nutpick/MS
+Nutrasweet/M
+nutria/SM
+nutrient/MS
+nutriment/MS
+nutritional/Y
+nutritionist/MS
+nutrition/SM
+nutritiousness/MS
+nutritious/PY
+nutritive/Y
+nutshell/MS
+nutted
+nuttiness/SM
+nutting
+nutty/TRP
+nuzzle/GZRSD
+NV
+NW
+NWT
+NY
+Nyasa/M
+NYC
+Nydia/M
+Nye/M
+Nyerere/M
+nylon/SM
+nymphet/MS
+nymph/M
+nympholepsy/M
+nymphomaniac/S
+nymphomania/MS
+nymphs
+Nyquist/M
+NYSE
+Nyssa/M
+NZ
+o
+O
+oafishness/S
+oafish/PY
+oaf/MS
+Oahu/M
+Oakland/M
+Oakley/M
+Oakmont/M
+oak/SMN
+oakum/MS
+oakwood
+oar/GSMD
+oarlock/MS
+oarsman/M
+oarsmen
+oarswoman
+oarswomen
+OAS
+oases
+oasis/M
+oatcake/MS
+oater/M
+Oates/M
+oath/M
+oaths
+oatmeal/SM
+oat/SMNR
+Oaxaca/M
+ob
+OB
+Obadiah/M
+Obadias/M
+obbligato/S
+obduracy/S
+obdurateness/S
+obdurate/PDSYG
+Obediah/M
+obedience/EMS
+obedient/EY
+Obed/M
+obeisance/MS
+obeisant/Y
+obelisk/SM
+Oberlin/M
+Oberon/M
+obese
+obesity/MS
+obey/EDRGS
+obeyer/EM
+obfuscate/SRDXGN
+obfuscation/M
+obfuscatory
+Obidiah/M
+Obie/M
+obi/MDGS
+obit/SMR
+obituary/SM
+obj
+objectify/GSDXN
+objectionableness/M
+objectionable/U
+objectionably
+objection/SMB
+objectiveness/MS
+objective/PYS
+objectivity/MS
+objector/SM
+object/SGVMD
+objurgate/GNSDX
+objurgation/M
+oblate/NYPSX
+oblation/M
+obligate/NGSDXY
+obligational
+obligation/M
+obligatorily
+obligatory
+obliged/E
+obliger/M
+obliges/E
+oblige/SRDG
+obligingness/M
+obliging/PY
+oblique/DSYGP
+obliqueness/S
+obliquity/MS
+obliterate/VNGSDX
+obliteration/M
+obliterative/Y
+oblivion/MS
+obliviousness/MS
+oblivious/YP
+oblongness/M
+oblong/SYP
+obloquies
+obloquy/M
+Ob/MD
+obnoxiousness/MS
+obnoxious/YP
+oboe/SM
+oboist/S
+obos
+O'Brien/M
+obs
+obscene/RYT
+obscenity/MS
+obscurantism/MS
+obscurantist/MS
+obscuration
+obscureness/M
+obscure/YTPDSRGL
+obscurity/MS
+obsequies
+obsequiousness/S
+obsequious/YP
+obsequy
+observability/M
+observable/SU
+observably
+observance/MS
+observantly
+observants
+observant/U
+observational/Y
+observation/MS
+observatory/MS
+observed/U
+observer/M
+observe/ZGDSRB
+observing/Y
+obsess/GVDS
+obsessional
+obsession/MS
+obsessiveness/S
+obsessive/PYS
+obsidian/SM
+obsolesce/GSD
+obsolescence/S
+obsolescent/Y
+obsolete/GPDSY
+obsoleteness/M
+obstacle/SM
+obstetrical
+obstetrician/SM
+obstetric/S
+obstetrics/M
+obstinacy/SM
+obstinateness/M
+obstinate/PY
+obstreperousness/SM
+obstreperous/PY
+obstructed/U
+obstructer/M
+obstructionism/SM
+obstructionist/MS
+obstruction/SM
+obstructiveness/MS
+obstructive/PSY
+obstruct/RDVGS
+obtainable/U
+obtainably
+obtain/LSGDRB
+obtainment/S
+obtrude/DSRG
+obtruder/M
+obtrusion/S
+obtrusiveness/MSU
+obtrusive/UPY
+obtuseness/S
+obtuse/PRTY
+obverse/YS
+obviate/XGNDS
+obviousness/SM
+obvious/YP
+Oby/M
+ocarina/MS
+O'Casey
+Occam/M
+occasional/Y
+occasion/MDSJG
+Occidental/S
+occidental/SY
+occident/M
+Occident/SM
+occipital/Y
+occlude/GSD
+occlusion/MS
+occlusive/S
+occulter/M
+occultism/SM
+occult/SRDYG
+occupancy/SM
+occupant/MS
+occupational/Y
+occupation/SAM
+occupied/AU
+occupier/M
+occupies/A
+occupy/RSDZG
+occur/AS
+occurred/A
+occurrence/SM
+occurring/A
+oceanfront/MS
+oceangoing
+Oceania/M
+oceanic
+ocean/MS
+oceanographer/SM
+oceanographic
+oceanography/SM
+oceanology/MS
+oceanside
+Oceanside/M
+Oceanus/M
+ocelot/SM
+ocher/DMGS
+Ochoa/M
+o'clock
+O'Clock
+O'Connell/M
+O'Connor/M
+Oconomowoc/M
+OCR
+octagonal/Y
+octagon/SM
+octahedral
+octahedron/M
+octal/S
+octane/MS
+octant/M
+octave/MS
+Octavia/M
+Octavian/M
+Octavio/M
+Octavius/M
+octavo/MS
+octennial
+octet/SM
+octile
+octillion/M
+Oct/M
+October/MS
+octogenarian/MS
+octopus/SM
+octoroon/M
+ocular/S
+oculist/SM
+OD
+odalisque/SM
+oddball/SM
+oddity/MS
+oddment/MS
+oddness/MS
+odd/TRYSPL
+Odele/M
+Odelia/M
+Odelinda/M
+Odella/M
+Odelle/M
+Odell/M
+O'Dell/M
+ode/MDRS
+Ode/MR
+Oderberg/MS
+Oder/M
+Odessa/M
+Odets/M
+Odetta/M
+Odette/M
+Odey/M
+Odie/M
+Odilia/M
+Odille/M
+Odin/M
+odiousness/MS
+odious/PY
+Odis/M
+odium/MS
+Odo/M
+odometer/SM
+Odom/M
+O'Donnell/M
+odor/DMS
+odoriferous
+odorless
+odorous/YP
+ODs
+O'Dwyer/M
+Ody/M
+Odysseus/M
+Odyssey/M
+odyssey/S
+OE
+OED
+oedipal
+Oedipal/Y
+Oedipus/M
+OEM/M
+OEMS
+oenology/MS
+oenophile/S
+o'er
+O'Er
+Oersted/M
+oesophagi
+oeuvre/SM
+Ofelia/M
+Ofella/M
+offal/MS
+offbeat/MS
+offcuts
+Offenbach/M
+offender/M
+offend/SZGDR
+offense/MSV
+offensively/I
+offensiveness/MSI
+offensive/YSP
+offerer/M
+offering/M
+offer/RDJGZ
+offertory/SM
+offhand/D
+offhandedness/S
+offhanded/YP
+officeholder/SM
+officemate/S
+officer/GMD
+officership/S
+office/SRMZ
+officialdom/SM
+officialism/SM
+officially/U
+official/PSYM
+officiant/SM
+officiate/XSDNG
+officiation/M
+officiator/MS
+officio
+officiousness/MS
+officious/YP
+offing/M
+offish
+offload/GDS
+offprint/GSDM
+offramp
+offset/SM
+offsetting
+offshoot/MS
+offshore
+offside/RS
+offspring/M
+offstage/S
+off/SZGDRJ
+offtrack
+Ofilia/M
+of/K
+often/RT
+oftentimes
+oft/NRT
+ofttimes
+Ogbomosho/M
+Ogdan/M
+Ogden/M
+Ogdon/M
+Ogilvy/M
+ogive/M
+Oglethorpe/M
+ogle/ZGDSR
+ogreish
+ogre/MS
+ogress/S
+oh
+OH
+O'Hara
+O'Hare/M
+O'Higgins
+Ohioan/S
+Ohio/M
+ohmic
+ohmmeter/MS
+ohm/SM
+oho/S
+ohs
+OHSA/M
+oilcloth/M
+oilcloths
+oiler/M
+oilfield/MS
+oiliness/SM
+oilman/M
+oil/MDRSZG
+oilmen
+oilseed/SM
+oilskin/MS
+oily/TPR
+oink/GDS
+ointment/SM
+Oise/M
+OJ
+Ojibwa/SM
+Okamoto/M
+okapi/SM
+Okayama/M
+okay's
+Okeechobee/M
+O'Keeffe
+Okefenokee
+Okhotsk/M
+Okinawa/M
+Okinawan/S
+Oklahoma/M
+Oklahoman/SM
+Okla/M
+OK/MDG
+okra/MS
+OKs
+Oktoberfest
+Olaf/M
+Olag/M
+Ola/M
+Olav/M
+Oldenburg/M
+olden/DG
+Oldfield/M
+oldie/MS
+oldish
+oldness/S
+Oldsmobile/M
+oldster/SM
+Olduvai/M
+old/XTNRPS
+olé
+oleaginous
+oleander/SM
+O'Leary/M
+olefin/M
+Oleg/M
+Ole/MV
+Olenek/M
+Olenka/M
+Olen/M
+Olenolin/M
+oleomargarine/SM
+oleo/S
+oles
+olfactory
+Olga/M
+Olia/M
+oligarchic
+oligarchical
+oligarch/M
+oligarchs
+oligarchy/SM
+Oligocene
+oligopolistic
+oligopoly/MS
+Olimpia/M
+Olin/M
+olive/MSR
+Olive/MZR
+Oliver/M
+Olivero/M
+Olivette/M
+Olivetti/M
+Olivia/M
+Olivier/M
+Olivie/RM
+Oliviero/M
+Oliy/M
+Ollie/M
+Olly/M
+Olmec
+Olmsted/M
+Olsen/M
+Olson/M
+Olva/M
+Olvan/M
+Olwen/M
+Olympe/M
+Olympiad/MS
+Olympian/S
+Olympia/SM
+Olympic/S
+Olympie/M
+Olympus/M
+Omaha/SM
+Oman/M
+Omar/M
+ombudsman/M
+ombudsmen
+Omdurman/M
+omega/MS
+omelet/SM
+omelette's
+omen/DMG
+Omero/M
+omicron/MS
+ominousness/SM
+ominous/YP
+omission/MS
+omit/S
+omitted
+omitting
+omnibus/MS
+omni/M
+omnipotence/SM
+Omnipotent
+omnipotent/SY
+omnipresence/MS
+omnipresent/Y
+omniscience/SM
+omniscient/YS
+omnivore/MS
+omnivorousness/MS
+omnivorous/PY
+oms
+Omsk/M
+om/XN
+ON
+onanism/M
+Onassis/M
+oncer/M
+once/SR
+oncogene/S
+oncologist/S
+oncology/SM
+oncoming/S
+Ondrea/M
+Oneal/M
+Onega/M
+Onegin/M
+Oneida/SM
+O'Neil
+O'Neill
+oneness/MS
+one/NPMSX
+oner/M
+onerousness/SM
+onerous/YP
+oneself
+onetime
+oneupmanship
+Onfre/M
+Onfroi/M
+ongoing/S
+Onida/M
+onion/GDM
+onionskin/MS
+onlooker/MS
+onlooking
+only/TP
+Onofredo/M
+Ono/M
+onomatopoeia/SM
+onomatopoeic
+onomatopoetic
+Onondaga/MS
+onrush/GMS
+on/RY
+ons
+Onsager/M
+onset/SM
+onsetting
+onshore
+onside
+onslaught/MS
+Ontarian/S
+Ontario/M
+Ont/M
+onto
+ontogeny/SM
+ontological/Y
+ontology/SM
+onus/SM
+onward/S
+onyx/MS
+oodles
+ooh/GD
+oohs
+oolitic
+Oona/M
+oops/S
+Oort/M
+ooze/GDS
+oozy/RT
+opacity/SM
+opalescence/S
+opalescent/Y
+Opalina/M
+Opaline/M
+Opal/M
+opal/SM
+opaque/GTPYRSD
+opaqueness/SM
+opcode/MS
+OPEC
+Opel/M
+opencast
+opened/AU
+opener/M
+openhandedness/SM
+openhanded/P
+openhearted
+opening/M
+openness/S
+opens/A
+openwork/MS
+open/YRDJGZTP
+operable/I
+operandi
+operand/SM
+operant/YS
+opera/SM
+operate/XNGVDS
+operatically
+operatic/S
+operationalization/S
+operationalize/D
+operational/Y
+operation/M
+operative/IP
+operatively
+operativeness/MI
+operatives
+operator/SM
+operetta/MS
+ope/S
+Ophelia/M
+Ophelie/M
+Ophiuchus/M
+ophthalmic/S
+ophthalmologist/SM
+ophthalmology/MS
+opiate/GMSD
+opine/XGNSD
+opinionatedness/M
+opinionated/PY
+opinion/M
+opioid
+opium/MS
+opossum/SM
+opp
+Oppenheimer/M
+opponent/MS
+opportune/IY
+opportunism/SM
+opportunistic
+opportunistically
+opportunist/SM
+opportunity/MS
+oppose/BRSDG
+opposed/U
+opposer/M
+oppositeness/M
+opposite/SXYNP
+oppositional
+opposition/M
+oppress/DSGV
+oppression/MS
+oppressiveness/MS
+oppressive/YP
+oppressor/MS
+opprobrious/Y
+opprobrium/SM
+Oprah/M
+ops
+opt/DSG
+opthalmic
+opthalmologic
+opthalmology
+optical/Y
+optician/SM
+optic/S
+optics/M
+optima
+optimality
+optimal/Y
+optimise's
+optimism/SM
+optimistic
+optimistically
+optimist/SM
+optimization/SM
+optimize/DRSZG
+optimized/U
+optimizer/M
+optimizes/U
+optimum/SM
+optionality/M
+optional/YS
+option/GDMS
+optoelectronic
+optometric
+optometrist/MS
+optometry/SM
+opulence/SM
+opulent/Y
+opus/SM
+op/XGDN
+OR
+oracle/GMSD
+oracular
+Oralee/M
+Oralia/M
+Oralie/M
+Oralla/M
+Oralle/M
+oral/YS
+Ora/M
+orangeade/MS
+Orange/M
+orange/MS
+orangery/SM
+orangutan/MS
+Oranjestad/M
+Oran/M
+orate/SDGNX
+oration/M
+oratorical/Y
+oratorio/MS
+orator/MS
+oratory/MS
+Orazio/M
+Orbadiah/M
+orbicular
+orbiculares
+orbital/MYS
+orbit/MRDGZS
+orb/SMDG
+orchard/SM
+orchestral/Y
+orchestra/MS
+orchestrate/GNSDX
+orchestrater's
+orchestration/M
+orchestrator/M
+orchid/SM
+ordainer/M
+ordainment/MS
+ordain/SGLDR
+ordeal/SM
+order/AESGD
+ordered/U
+orderer
+ordering/S
+orderless
+orderliness/SE
+orderly/PS
+order's/E
+ordinal/S
+ordinance/MS
+ordinarily
+ordinariness/S
+ordinary/RSPT
+ordinated
+ordinate/I
+ordinates
+ordinate's
+ordinating
+ordination/SM
+ordnance/SM
+Ordovician
+ordure/MS
+oregano/SM
+Oreg/M
+Oregonian/S
+Oregon/M
+Orelee/M
+Orelia/M
+Orelie/M
+Orella/M
+Orelle/M
+Orel/M
+Oren/M
+Ore/NM
+ore/NSM
+Oreo
+Orestes
+organdie's
+organdy/MS
+organelle/MS
+organically/I
+organic/S
+organismic
+organism/MS
+organist/MS
+organizable/UMS
+organizational/MYS
+organization/MEAS
+organize/AGZDRS
+organized/UE
+organizer/MA
+organizes/E
+organizing/E
+organ/MS
+organometallic
+organza/SM
+orgasm/GSMD
+orgasmic
+orgiastic
+orgy/SM
+Oriana/M
+oriel/MS
+orientable
+Oriental/S
+oriental/SY
+orientated/A
+orientate/ESDXGN
+orientates/A
+orientation/AMES
+orienteering/M
+orienter
+orient/GADES
+orient's
+Orient/SM
+orifice/MS
+orig
+origami/MS
+originality/SM
+originally
+original/US
+originate/VGNXSD
+origination/M
+originative/Y
+originator/SM
+origin/MS
+Orin/M
+Orinoco/M
+oriole/SM
+Orion/M
+orison/SM
+Oriya/M
+Orizaba/M
+Orkney/M
+Orland/M
+Orlando/M
+Orlan/M
+Orleans
+Orlick/M
+Orlon/SM
+Orly/M
+ormolu/SM
+or/MY
+ornamental/SY
+ornamentation/SM
+ornament/GSDM
+ornateness/SM
+ornate/YP
+orneriness/SM
+ornery/PRT
+ornithological
+ornithologist/SM
+ornithology/MS
+orographic/M
+orography/M
+Orono/M
+orotund
+orotundity/MS
+orphanage/MS
+orphanhood/M
+orphan/SGDM
+Orpheus/M
+Orphic
+Orran/M
+Orren/M
+Orrin/M
+orris/SM
+Orr/MN
+ors
+Orsa/M
+Orsola/M
+Orson/M
+Ortega/M
+Ortensia/M
+orthodontia/S
+orthodontic/S
+orthodontics/M
+orthodontist/MS
+orthodoxies
+orthodoxly/U
+Orthodox/S
+orthodoxy's
+orthodox/YS
+orthodoxy/U
+orthogonality/M
+orthogonalization/M
+orthogonalized
+orthogonal/Y
+orthographic
+orthographically
+orthography/MS
+orthonormal
+orthopedic/S
+orthopedics/M
+orthopedist/SM
+orthophosphate/MS
+orthorhombic
+Ortiz/M
+Orton/M
+Orval/M
+Orville/M
+Orv/M
+Orwellian
+Orwell/M
+o's
+Osage/SM
+Osaka/M
+Osbert/M
+Osborne/M
+Osborn/M
+Osbourne/M
+Osbourn/M
+Oscar/SM
+Osceola/M
+oscillate/SDXNG
+oscillation/M
+oscillator/SM
+oscillatory
+oscilloscope/SM
+osculate/XDSNG
+osculation/M
+Osgood/M
+OSHA
+Oshawa/M
+O'Shea/M
+Oshkosh/M
+osier/MS
+Osiris/M
+Oslo/M
+Os/M
+OS/M
+Osman/M
+osmium/MS
+Osmond/M
+osmoses
+osmosis/M
+osmotic
+Osmund/M
+osprey/SM
+osseous/Y
+Ossie/M
+ossification/M
+ossify/NGSDX
+ostensible
+ostensibly
+ostentation/MS
+ostentatiousness/M
+ostentatious/PY
+osteoarthritides
+osteoarthritis/M
+osteology/M
+osteopathic
+osteopath/M
+osteopaths
+osteopathy/MS
+osteoporoses
+osteoporosis/M
+ostracise's
+ostracism/MS
+ostracize/GSD
+Ostrander/M
+ostrich/MS
+Ostrogoth/M
+Ostwald/M
+O'Sullivan/M
+Osvaldo/M
+Oswald/M
+Oswell/M
+OT
+OTB
+OTC
+Otes
+Otha/M
+Othelia/M
+Othella/M
+Othello/M
+otherness/M
+other/SMP
+otherwise
+otherworldly/P
+otherworld/Y
+Othilia/M
+Othilie/M
+Otho/M
+otiose
+Otis/M
+OTOH
+Ottawa/MS
+otter/DMGS
+Ottilie/M
+Otto/M
+Ottoman
+ottoman/MS
+Ouagadougou/M
+oubliette/SM
+ouch/SDG
+oughtn't
+ought/SGD
+Ouija/MS
+ounce/MS
+our/S
+ourself
+ourselves
+ouster/M
+oust/RDGZS
+outage/MS
+outargue/GDS
+outback/MRS
+outbalance/GDS
+outbidding
+outbid/S
+outboard/S
+outboast/GSD
+outbound/S
+outbreak/SMG
+outbroke
+outbroken
+outbuilding/SM
+outburst/MGS
+outcast/GSM
+outclass/SDG
+outcome/SM
+outcropped
+outcropping/S
+outcrop/SM
+outcry/MSDG
+outdated/P
+outdid
+outdistance/GSD
+outdoes
+outdo/G
+outdone
+outdoor/S
+outdoorsy
+outdraw/GS
+outdrawn
+outdrew
+outermost
+outerwear/M
+outface/SDG
+outfall/MS
+outfielder/M
+outfield/RMSZ
+outfight/SG
+outfit/MS
+outfitted
+outfitter/MS
+outfitting
+outflank/SGD
+outflow/SMDG
+outfought
+outfox/GSD
+outgeneraled
+outgoes
+outgo/GJ
+outgoing/P
+outgrew
+outgrip
+outgrow/GSH
+outgrown
+outgrowth/M
+outgrowths
+outguess/SDG
+outhit/S
+outhitting
+outhouse/SM
+outing/M
+outlaid
+outlander/M
+outlandishness/MS
+outlandish/PY
+outland/ZR
+outlast/GSD
+outlawry/M
+outlaw/SDMG
+outlay/GSM
+outlet/SM
+outliers
+outline/SDGM
+outlive/GSD
+outlook/MDGS
+outlying
+outmaneuver/GSD
+outmatch/SDG
+outmigration
+outmoded
+outness/M
+outnumber/GDS
+outpaced
+outpatient/SM
+outperform/DGS
+out/PJZGSDR
+outplacement/S
+outplay/GDS
+outpoint/GDS
+outpost/SM
+outpouring/M
+outpour/MJG
+outproduce/GSD
+output/SM
+outputted
+outputting
+outrace/GSD
+outrage/GSDM
+outrageousness/M
+outrageous/YP
+outran
+outrank/GSD
+outré
+outreach/SDG
+outrider/MS
+outrigger/SM
+outright/Y
+outrunning
+outrun/S
+outscore/GDS
+outsell/GS
+outset/MS
+outsetting
+outshine/SG
+outshone
+outshout/GDS
+outsider/PM
+outside/ZSR
+outsize/S
+outskirt/SM
+outsmart/SDG
+outsold
+outsource/SDJG
+outspend/SG
+outspent
+outspoke
+outspokenness/SM
+outspoken/YP
+outspread/SG
+outstanding/Y
+outstate/NX
+outstation/M
+outstay/SDG
+outstretch/GSD
+outstripped
+outstripping
+outstrip/S
+outtake/S
+outvote/GSD
+outwardness/M
+outward/SYP
+outwear/SG
+outweigh/GD
+outweighs
+outwit/S
+outwitted
+outwitting
+outwore
+outwork/SMDG
+outworn
+ouzo/SM
+oval/MYPS
+ovalness/M
+ova/M
+ovarian
+ovary/SM
+ovate/SDGNX
+ovation/GMD
+ovenbird/SM
+oven/MS
+overabundance/MS
+overabundant
+overachieve/SRDGZ
+overact/DGVS
+overage/S
+overaggressive
+overallocation
+overall/SM
+overambitious
+overanxious
+overarching
+overarm/GSD
+overate
+overattentive
+overawe/GDS
+overbalance/DSG
+overbear/GS
+overbearingness/M
+overbearing/YP
+overbidding
+overbid/S
+overbite/MS
+overblown
+overboard
+overbold
+overbook/SDG
+overbore
+overborne
+overbought
+overbuild/GS
+overbuilt
+overburdening/Y
+overburden/SDG
+overbuy/GS
+overcame
+overcapacity/M
+overcapitalize/DSG
+overcareful
+overcast/GS
+overcasting/M
+overcautious
+overcerebral
+overcharge/DSG
+overcloud/DSG
+overcoating/M
+overcoat/SMG
+overcomer/M
+overcome/RSG
+overcommitment/S
+overcompensate/XGNDS
+overcompensation/M
+overcomplexity/M
+overcomplicated
+overconfidence/MS
+overconfident/Y
+overconscientious
+overconsumption/M
+overcook/SDG
+overcooled
+overcorrection
+overcritical
+overcrowd/DGS
+overcurious
+overdecorate/SDG
+overdependent
+overdetermined
+overdevelop/SDG
+overdid
+overdoes
+overdo/G
+overdone
+overdose/DSMG
+overdraft/SM
+overdraw/GS
+overdrawn
+overdress/GDS
+overdrew
+overdrive/GSM
+overdriven
+overdrove
+overdubbed
+overdubbing
+overdub/S
+overdue
+overeagerness/M
+overeager/PY
+overeater/M
+overeat/GNRS
+overeducated
+overemotional
+overemphases
+overemphasis/M
+overemphasize/GZDSR
+overenthusiastic
+overestimate/DSXGN
+overestimation/M
+overexcite/DSG
+overexercise/SDG
+overexert/GDS
+overexertion/SM
+overexploitation
+overexploited
+overexpose/GDS
+overexposure/SM
+overextend/DSG
+overextension
+overfall/M
+overfed
+overfeed/GS
+overfill/GDS
+overfishing
+overflew
+overflight/SM
+overflow/DGS
+overflown
+overfly/GS
+overfond
+overfull
+overgeneralize/GDS
+overgenerous
+overgraze/SDG
+overgrew
+overground
+overgrow/GSH
+overgrown
+overgrowth/M
+overgrowths
+overhand/DGS
+overhang/GS
+overhasty
+overhaul/GRDJS
+overhead/S
+overheard
+overhearer/M
+overhear/SRG
+overheat/SGD
+overhung
+overincredulous
+overindulgence/SM
+overindulgent
+overindulge/SDG
+overinflated
+overjoy/SGD
+overkill/SDMG
+overladed
+overladen
+overlaid
+overlain
+overland/S
+overlap/MS
+overlapped
+overlapping
+overlarge
+overlay/GS
+overleaf
+overlie
+overload/SDG
+overlong
+overlook/DSG
+overlord/DMSG
+overloud
+overly/GRS
+overmanning
+overmaster/GSD
+overmatching
+overmodest
+overmuch/S
+overnice
+overnight/SDRGZ
+overoptimism/SM
+overoptimistic
+overpaid
+overparticular
+overpass/GMSD
+overpay/LSG
+overpayment/M
+overplay/SGD
+overpopulate/DSNGX
+overpopulation/M
+overpopulous
+overpower/GSD
+overpowering/Y
+overpraise/DSG
+overprecise
+overpressure
+overprice/SDG
+overprint/DGS
+overproduce/SDG
+overproduction/S
+overprotect/GVDS
+overprotection/M
+overqualified
+overran
+overrate/DSG
+overreach/DSRG
+overreaction/SM
+overreact/SGD
+overred
+overrefined
+overrepresented
+overridden
+overrider/M
+override/RSG
+overripe
+overrode
+overrule/GDS
+overrunning
+overrun/S
+oversample/DG
+oversaturate
+oversaw
+oversea/S
+overseeing
+overseen
+overseer/M
+oversee/ZRS
+oversell/SG
+oversensitiveness/S
+oversensitive/P
+oversensitivity
+oversexed
+overshadow/GSD
+overshoe/SM
+overshoot/SG
+overshot/S
+oversight/SM
+oversimple
+oversimplification/M
+oversimplify/GXNDS
+oversize/GS
+oversleep/GS
+overslept
+oversoftness/M
+oversoft/P
+oversold
+overspecialization/MS
+overspecialize/GSD
+overspend/SG
+overspent
+overspill/DMSG
+overspread/SG
+overstaffed
+overstatement/SM
+overstate/SDLG
+overstay/GSD
+overstepped
+overstepping
+overstep/S
+overstimulate/DSG
+overstock/SGD
+overstraining
+overstressed
+overstretch/D
+overstrict
+overstrike/GS
+overstrung
+overstuffed
+oversubscribe/SDG
+oversubtle
+oversupply/MDSG
+oversuspicious
+overtaken
+overtake/RSZG
+overtax/DSG
+overthrew
+overthrow/GS
+overthrown
+overtightened
+overtime/MGDS
+overtire/DSG
+overtone/MS
+overtook
+overt/PY
+overture/DSMG
+overturn/SDG
+overuse/DSG
+overvalue/GSD
+overview/MS
+overweening
+overweight/GSD
+overwhelm/GDS
+overwhelming/Y
+overwinter/SDG
+overwork/GSD
+overwrap
+overwrite/SG
+overwritten
+overwrote
+overwrought
+over/YGS
+overzealousness/M
+overzealous/P
+Ovid/M
+oviduct/SM
+oviform
+oviparous
+ovoid/S
+ovular
+ovulate/GNXDS
+ovulatory
+ovule/MS
+ovum/MS
+ow/DYG
+Owen/MS
+owe/S
+owlet/SM
+owl/GSMDR
+owlishness/M
+owlish/PY
+owned/U
+own/EGDS
+ownership/MS
+owner/SM
+oxalate/M
+oxalic
+oxaloacetic
+oxblood/S
+oxbow/SM
+oxcart/MS
+oxen/M
+oxford/MS
+Oxford/MS
+oxidant/SM
+oxidate/NVX
+oxidation/M
+oxidative/Y
+oxide/SM
+oxidization/MS
+oxidized/U
+oxidize/JDRSGZ
+oxidizer/M
+oxidizes/A
+ox/MNS
+Oxnard
+Oxonian
+oxtail/M
+Oxus/M
+oxyacetylene/MS
+oxygenate/XSDMGN
+oxygenation/M
+oxygen/MS
+oxyhydroxides
+oxymora
+oxymoron/M
+oyster/GSDM
+oystering/M
+oz
+Ozark/SM
+Oz/M
+ozone/SM
+Ozymandias/M
+Ozzie/M
+Ozzy/M
+P
+PA
+Pablo/M
+Pablum/M
+pablum/S
+Pabst/M
+pabulum/SM
+PAC
+pace/DRSMZG
+Pace/M
+pacemaker/SM
+pacer/M
+pacesetter/MS
+pacesetting
+Pacheco/M
+pachyderm/MS
+pachysandra/MS
+pacific
+pacifically
+pacification/M
+Pacific/M
+pacifier/M
+pacifism/MS
+pacifistic
+pacifist/MS
+pacify/NRSDGXZ
+package/ARSDG
+packaged/U
+packager/S
+package's
+packages/U
+packaging/SM
+Packard/SM
+packed/AU
+packer/MUS
+packet/MSDG
+pack/GZSJDRMB
+packhorse/M
+packinghouse/S
+packing/M
+packsaddle/SM
+Packston/M
+packs/UA
+Packwood/M
+Paco/M
+Pacorro/M
+pact/SM
+Padang/M
+padded/U
+Paddie/M
+padding/SM
+paddle/MZGRSD
+paddler/M
+paddock/SDMG
+Paddy/M
+paddy/SM
+Padget/M
+Padgett/M
+Padilla/M
+padlock/SGDM
+pad/MS
+Padraic/M
+Padraig/M
+padre/MS
+Padrewski/M
+Padriac/M
+paean/MS
+paediatrician/MS
+paediatrics/M
+paedophilia's
+paella/SM
+paeony/M
+Paganini/M
+paganism/MS
+pagan/SM
+pageantry/SM
+pageant/SM
+pageboy/SM
+paged/U
+pageful
+Page/M
+page/MZGDRS
+pager/M
+paginate/DSNGX
+Paglia/M
+pagoda/MS
+Pahlavi/M
+paid/AU
+Paige/M
+pailful/SM
+Pail/M
+pail/SM
+Paine/M
+painfuller
+painfullest
+painfulness/MS
+painful/YP
+pain/GSDM
+painkiller/MS
+painkilling
+painlessness/S
+painless/YP
+painstaking/SY
+paint/ADRZGS
+paintbox/M
+paintbrush/SM
+painted/U
+painterly/P
+painter/YM
+painting/SM
+paint's
+paintwork
+paired/UA
+pair/JSDMG
+pairs/A
+pairwise
+paisley/MS
+pajama/MDS
+Pakistani/S
+Pakistan/M
+palace/MS
+paladin/MS
+palaeolithic
+palaeontologists
+palaeontology/M
+palanquin/MS
+palatability/M
+palatableness/M
+palatable/P
+palatalization/MS
+palatalize/SDG
+palatal/YS
+palate/BMS
+palatial/Y
+palatinate/SM
+Palatine
+palatine/S
+palaver/GSDM
+paleface/SM
+Palembang/M
+paleness/S
+Paleocene
+Paleogene
+paleographer/SM
+paleography/SM
+paleolithic
+Paleolithic
+paleontologist/S
+paleontology/MS
+Paleozoic
+Palermo/M
+pale/SPY
+Palestine/M
+Palestinian/S
+Palestrina/M
+palette/MS
+Paley/M
+palfrey/MS
+palimony/S
+palimpsest/MS
+palindrome/MS
+palindromic
+paling/M
+palisade/MGSD
+Palisades/M
+palish
+Palladio/M
+palladium/SM
+pallbearer/SM
+palletized
+pallet/SMGD
+pall/GSMD
+palliate/SDVNGX
+palliation/M
+palliative/SY
+pallidness/MS
+pallid/PY
+Pall/M
+pallor/MS
+palmate
+palmer/M
+Palmer/M
+Palmerston/M
+palmetto/MS
+palm/GSMDR
+palmist/MS
+palmistry/MS
+Palm/MR
+Palmolive/M
+palmtop/S
+Palmyra/M
+palmy/RT
+Palo/M
+Paloma/M
+Palomar/M
+palomino/MS
+palpable
+palpably
+palpate/SDNGX
+palpation/M
+palpitate/NGXSD
+palpitation/M
+pal/SJMDRYTG
+palsy/GSDM
+paltriness/SM
+paltry/TRP
+paludal
+Pa/M
+Pamela/M
+Pamelina/M
+Pamella/M
+pa/MH
+Pamirs
+Pam/M
+Pammie/M
+Pammi/M
+Pammy/M
+pampas/M
+pamperer/M
+pamper/RDSG
+Pampers
+pamphleteer/DMSG
+pamphlet/SM
+panacea/MS
+panache/MS
+Panama/MS
+Panamanian/S
+panama/S
+pancake/MGSD
+Panchito/M
+Pancho/M
+panchromatic
+pancreas/MS
+pancreatic
+panda/SM
+pandemic/S
+pandemonium/SM
+pander/ZGRDS
+Pandora/M
+panegyric/SM
+pane/KMS
+paneling/M
+panelist/MS
+panelization
+panelized
+panel/JSGDM
+Pangaea/M
+pang/GDMS
+pangolin/M
+panhandle/RSDGMZ
+panicked
+panicking
+panicky/RT
+panic/SM
+panier's
+panjandrum/M
+Pankhurst/M
+Pan/M
+Panmunjom/M
+panned
+pannier/SM
+panning
+panoply/MSD
+panorama/MS
+panoramic
+panpipes
+Pansie/M
+pan/SMD
+Pansy/M
+pansy/SM
+Pantagruel/M
+Pantaloon/M
+pantaloons
+pant/GDS
+pantheism/MS
+pantheistic
+pantheist/S
+pantheon/MS
+panther/SM
+pantie/SM
+pantiled
+pantograph/M
+pantomime/SDGM
+pantomimic
+pantomimist/SM
+pantry/SM
+pantsuit/SM
+pantyhose
+pantyliner
+pantywaist/SM
+Panza/M
+Paola/M
+Paoli/M
+Paolina/M
+Paolo/M
+papacy/SM
+Papagena/M
+Papageno/M
+papal/Y
+papa/MS
+paparazzi
+papaw/SM
+papaya/MS
+paperback/GDMS
+paperboard/MS
+paperboy/SM
+paperer/M
+papergirl/SM
+paper/GJMRDZ
+paperhanger/SM
+paperhanging/SM
+paperiness/M
+paperless
+paperweight/MS
+paperwork/SM
+papery/P
+papillae
+papilla/M
+papillary
+papist/MS
+papoose/SM
+Pappas/M
+papped
+papping
+pappy/RST
+paprika/MS
+pap/SZMNR
+papyri
+papyrus/M
+Paquito/M
+parable/MGSD
+parabola/MS
+parabolic
+paraboloidal/M
+paraboloid/MS
+Paracelsus/M
+paracetamol/M
+parachuter/M
+parachute/RSDMG
+parachutist/MS
+Paraclete/M
+parader/M
+parade/RSDMZG
+paradigmatic
+paradigm/SM
+paradisaic
+paradisaical
+Paradise/M
+paradise/MS
+paradoxic
+paradoxicalness/M
+paradoxical/YP
+paradox/MS
+paraffin/GSMD
+paragon/SGDM
+paragrapher/M
+paragraph/MRDG
+paragraphs
+Paraguayan/S
+Paraguay/M
+parakeet/MS
+paralegal/S
+paralinguistic
+parallax/SM
+parallel/DSG
+paralleled/U
+parallelepiped/MS
+parallelism/SM
+parallelization/MS
+parallelize/ZGDSR
+parallelogram/MS
+paralysis/M
+paralytically
+paralytic/S
+paralyzedly/S
+paralyzed/Y
+paralyzer/M
+paralyze/ZGDRS
+paralyzingly/S
+paralyzing/Y
+paramagnetic
+paramagnet/M
+Paramaribo/M
+paramecia
+paramecium/M
+paramedical/S
+paramedic/MS
+parameterization/SM
+parameterize/BSDG
+parameterized/U
+parameterless
+parameter/SM
+parametric
+parametrically
+parametrization
+parametrize/DS
+paramilitary/S
+paramount/S
+paramour/MS
+para/MS
+Paramus/M
+Paraná
+paranoiac/S
+paranoia/SM
+paranoid/S
+paranormal/SY
+parapet/SMD
+paraphernalia
+paraphrase/GMSRD
+paraphraser/M
+paraplegia/MS
+paraplegic/S
+paraprofessional/SM
+parapsychologist/S
+parapsychology/MS
+paraquat/S
+parasite/SM
+parasitically
+parasitic/S
+parasitism/SM
+parasitologist/M
+parasitology/M
+parasol/SM
+parasympathetic/S
+parathion/SM
+parathyroid/S
+paratrooper/M
+paratroop/RSZ
+paratyphoid/S
+parboil/DSG
+parceled/U
+parceling/M
+parcel/SGMD
+Parcheesi/M
+parch/GSDL
+parchment/SM
+PARC/M
+pardonableness/M
+pardonable/U
+pardonably/U
+pardoner/M
+pardon/ZBGRDS
+paregoric/SM
+parentage/MS
+parental/Y
+parenteral
+parentheses
+parenthesis/M
+parenthesize/GSD
+parenthetic
+parenthetical/Y
+parenthood/MS
+parent/MDGJS
+pare/S
+paresis/M
+pares/S
+Pareto/M
+parfait/SM
+pariah/M
+pariahs
+parietal/S
+parimutuel/S
+paring/M
+parishioner/SM
+parish/MS
+Parisian/SM
+Paris/M
+parity/ESM
+parka/MS
+Parke/M
+Parker/M
+Parkersburg/M
+park/GJZDRMS
+Parkhouse/M
+parking/M
+Parkinson/M
+parkish
+parkland/M
+parklike
+Parkman
+Park/RMS
+parkway/MS
+parlance/SM
+parlay/DGS
+parley/MDSG
+parliamentarian/SM
+parliamentary/U
+parliament/MS
+Parliament/MS
+parlor/SM
+parlous
+Parmesan/S
+parmigiana
+Parnassus/SM
+Parnell/M
+parochialism/SM
+parochiality
+parochial/Y
+parodied/U
+parodist/SM
+parody/SDGM
+parolee/MS
+parole/MSDG
+paroxysmal
+paroxysm/MS
+parquetry/SM
+parquet/SMDG
+parrakeet's
+parred
+parricidal
+parricide/MS
+parring
+Parrish/M
+Parr/M
+Parrnell/M
+parrot/GMDS
+parrotlike
+parry/GSD
+Parry/M
+parse
+parsec/SM
+parsed/U
+Parsee's
+parser/M
+Parsifal/M
+parsimonious/Y
+parsimony/SM
+pars/JDSRGZ
+parsley/MS
+parsnip/MS
+parsonage/MS
+parson/MS
+Parsons/M
+partaken
+partaker/M
+partake/ZGSR
+part/CDGS
+parterre/MS
+parter/S
+parthenogeneses
+parthenogenesis/M
+Parthenon/M
+Parthia/M
+partiality/MS
+partial/SY
+participant/MS
+participate/NGVDSX
+participation/M
+participator/S
+participatory
+participial/Y
+participle/MS
+particleboard/S
+particle/MS
+particolored
+particularistic
+particularity/SM
+particularization/MS
+particularize/GSD
+particular/SY
+particulate/S
+parting/MS
+partisanship/SM
+partisan/SM
+partition/AMRDGS
+partitioned/U
+partitioner/M
+partitive/S
+partizan's
+partly
+partner/DMGS
+partnership/SM
+partook
+partridge/MS
+part's
+parturition/SM
+partway
+party/RSDMG
+parvenu/SM
+par/ZGSJBMDR
+Pasadena/M
+PASCAL
+Pascale/M
+Pascal/M
+pascal/SM
+paschal/S
+pasha/MS
+Paso/M
+Pasquale/M
+pas/S
+passably
+passage/MGSD
+passageway/MS
+Passaic/M
+passband
+passbook/MS
+passel/MS
+passé/M
+passenger/MYS
+passerby
+passer/M
+passersby
+passim
+passing/Y
+passionated
+passionate/EYP
+passionateness/EM
+passionates
+passionating
+passioned
+passionflower/MS
+passioning
+passionless
+passion/SEM
+Passion/SM
+passivated
+passiveness/S
+passive/SYP
+passivity/S
+pass/JGVBZDSR
+passkey/SM
+passmark
+passover
+Passover/MS
+passport/SM
+password/SDM
+pasta/MS
+pasteboard/SM
+pasted/UA
+pastel/MS
+paste/MS
+Pasternak/M
+pastern/SM
+pasteup
+pasteurization/MS
+pasteurized/U
+pasteurizer/M
+pasteurize/RSDGZ
+Pasteur/M
+pastiche/MS
+pastille/SM
+pastime/SM
+pastiness/SM
+pastoralization/M
+pastoral/SPY
+pastorate/MS
+pastor/GSDM
+past/PGMDRS
+pastrami/MS
+pastry/SM
+past's/A
+pasts/A
+pasturage/SM
+pasture/MGSRD
+pasturer/M
+pasty/PTRS
+Patagonia/M
+Patagonian/S
+patch/EGRSD
+patcher/EM
+patchily
+patchiness/S
+patch's
+patchwork/RMSZ
+patchy/PRT
+patellae
+patella/MS
+Patel/M
+Pate/M
+paten/M
+Paten/M
+patentee/SM
+patent/ZGMRDYSB
+paterfamilias/SM
+pater/M
+paternalism/MS
+paternalist
+paternalistic
+paternal/Y
+paternity/SM
+paternoster/SM
+Paterson/M
+pate/SM
+pathetic
+pathetically
+pathfinder/MS
+pathless/P
+path/M
+pathname/SM
+pathogenesis/M
+pathogenic
+pathogen/SM
+pathologic
+pathological/Y
+pathologist/MS
+pathology/SM
+pathos/SM
+paths
+pathway/MS
+Patience/M
+patience/SM
+patient/MRYTS
+patient's/I
+patients/I
+patina/SM
+patine
+Patin/M
+patio/MS
+Pat/MN
+pat/MNDRS
+Patna/M
+patois/M
+Paton/M
+patresfamilias
+patriarchal
+patriarchate/MS
+patriarch/M
+patriarchs
+patriarchy/MS
+Patrica/M
+Patrice/M
+Patricia/M
+patrician/MS
+patricide/MS
+Patricio/M
+Patrick/M
+Patric/M
+patrimonial
+patrimony/SM
+patriotically
+patriotic/U
+patriotism/SM
+patriot/SM
+patristic/S
+Patrizia/M
+Patrizio/M
+Patrizius/M
+patrolled
+patrolling
+patrolman/M
+patrolmen
+patrol/MS
+patrolwoman
+patrolwomen
+patronage/MS
+patroness/S
+patronization
+patronized/U
+patronize/GZRSDJ
+patronizer/M
+patronizes/A
+patronizing's/U
+patronizing/YM
+patronymically
+patronymic/S
+patron/YMS
+patroon/MS
+patsy/SM
+Patsy/SM
+patted
+Patten/M
+patten/MS
+patterer/M
+pattern/GSDM
+patternless
+patter/RDSGJ
+Patterson/M
+Pattie/M
+Patti/M
+patting
+Pattin/M
+Patton/M
+Patty/M
+patty/SM
+paucity/SM
+Paula/M
+Paule/M
+Pauletta/M
+Paulette/M
+Paulie/M
+Pauli/M
+Paulina/M
+Pauline
+Pauling/M
+Paulita/M
+Paul/MG
+Paulo/M
+Paulsen/M
+Paulson/M
+Paulus/M
+Pauly/M
+paunch/GMSD
+paunchiness/M
+paunchy/RTP
+pauperism/SM
+pauperize/SDG
+pauper/SGDM
+pause/DSG
+Pavarotti
+paved/UA
+pave/GDRSJL
+Pavel/M
+pavement/SGDM
+paver/M
+paves/A
+Pavia/M
+pavilion/SMDG
+paving/A
+paving's
+Pavla/M
+Pavlova/MS
+Pavlovian
+Pavlov/M
+pawl/SM
+paw/MDSG
+pawnbroker/SM
+pawnbroking/S
+Pawnee/SM
+pawner/M
+pawn/GSDRM
+pawnshop/MS
+pawpaw's
+Pawtucket/M
+paxes
+Paxon/M
+Paxton/M
+payable/S
+pay/AGSLB
+payback/S
+paycheck/SM
+payday/MS
+payed
+payee/SM
+payer/SM
+payload/SM
+paymaster/SM
+payment/ASM
+Payne/SM
+payoff/MS
+payola/MS
+payout/S
+payroll/MS
+payslip/S
+Payson/M
+Payton/M
+Paz/M
+Pb/M
+PBS
+PBX
+PCB
+PC/M
+PCP
+PCs
+pct
+pd
+PD
+Pd/M
+PDP
+PDQ
+PDT
+PE
+Peabody/M
+peaceableness/M
+peaceable/P
+peaceably
+peacefuller
+peacefullest
+peacefulness/S
+peaceful/PY
+peace/GMDS
+peacekeeping/S
+Peace/M
+peacemaker/MS
+peacemaking/MS
+peacetime/MS
+peach/GSDM
+Peachtree/M
+peachy/RT
+peacock/SGMD
+Peadar/M
+peafowl/SM
+peahen/MS
+peaked/P
+peakiness/M
+peak/SGDM
+peaky/P
+pealed/A
+Peale/M
+peal/MDSG
+peals/A
+pea/MS
+peanut/SM
+Pearce/M
+Pearla/M
+Pearle/M
+pearler/M
+Pearlie/M
+Pearline/M
+Pearl/M
+pearl/SGRDM
+pearly/TRS
+Pearson/M
+pear/SYM
+peartrees
+Peary/M
+peasanthood
+peasantry/SM
+peasant/SM
+peashooter/MS
+peats/A
+peat/SM
+peaty/TR
+pebble/MGSD
+pebbling/M
+pebbly/TR
+Pebrook/M
+pecan/SM
+peccadilloes
+peccadillo/M
+peccary/MS
+Pechora/M
+pecker/M
+peck/GZSDRM
+Peckinpah/M
+Peck/M
+Pecos/M
+pectic
+pectin/SM
+pectoral/S
+peculate/NGDSX
+peculator/S
+peculiarity/MS
+peculiar/SY
+pecuniary
+pedagogical/Y
+pedagogic/S
+pedagogics/M
+pedagogue/SDGM
+pedagogy/MS
+pedal/SGRDM
+pedantic
+pedantically
+pedantry/MS
+pedant/SM
+peddler/M
+peddle/ZGRSD
+pederast/SM
+pederasty/SM
+Peder/M
+pedestal/GDMS
+pedestrianization
+pedestrianize/GSD
+pedestrian/MS
+pediatrician/SM
+pediatric/S
+pedicab/SM
+pedicure/DSMG
+pedicurist/SM
+pedigree/DSM
+pediment/DMS
+pedlar's
+pedometer/MS
+pedophile/S
+pedophilia
+Pedro/M
+peduncle/MS
+peeing
+peekaboo/SM
+peek/GSD
+peeler/M
+peeling/M
+Peel/M
+peel/SJGZDR
+peen/GSDM
+peeper/M
+peephole/SM
+peep/SGZDR
+peepshow/MS
+peepy
+peerage/MS
+peer/DMG
+peeress/MS
+peerlessness/M
+peerless/PY
+peeve/GZMDS
+peevers/M
+peevishness/SM
+peevish/YP
+peewee/S
+pee/ZDRS
+Pegasus/MS
+pegboard/SM
+Pegeen/M
+pegged
+Peggie/M
+Peggi/M
+pegging
+Peggy/M
+Peg/M
+peg/MS
+peignoir/SM
+Pei/M
+Peiping/M
+Peirce/M
+pejoration/SM
+pejorative/SY
+peke/MS
+Pekinese's
+pekingese
+Pekingese/SM
+Peking/SM
+pekoe/SM
+pelagic
+Pelee/M
+Pele/M
+pelf/SM
+Pelham/M
+pelican/SM
+pellagra/SM
+pellet/SGMD
+pellucid
+Peloponnese/M
+pelter/M
+pelt/GSDR
+pelvic/S
+pelvis/SM
+Pembroke/M
+pemmican/SM
+penalization/SM
+penalized/U
+penalize/SDG
+penalty/MS
+penal/Y
+Pena/M
+penance/SDMG
+pence/M
+penchant/MS
+pencil/SGJMD
+pendant/SM
+pend/DCGS
+pendent/CS
+Penderecki/M
+Pendleton/M
+pendulous
+pendulum/MS
+Penelopa/M
+Penelope/M
+penetrability/SM
+penetrable
+penetrate/SDVGNX
+penetrating/Y
+penetration/M
+penetrativeness/M
+penetrative/PY
+penetrator/MS
+penguin/MS
+penicillin/SM
+penile
+peninsular
+peninsula/SM
+penis/MS
+penitence/MS
+penitential/YS
+penitentiary/MS
+penitent/SY
+penknife/M
+penknives
+penlight/MS
+pen/M
+Pen/M
+penman/M
+penmanship/MS
+penmen
+Penna
+pennant/SM
+penned
+Penney/M
+Pennie/M
+penniless
+Penni/M
+penning
+Pennington/M
+pennis
+Penn/M
+pennon/SM
+Pennsylvania/M
+Pennsylvanian/S
+Penny/M
+penny/SM
+pennyweight/SM
+pennyworth/M
+penologist/MS
+penology/MS
+Penrod/M
+Pensacola/M
+pensioner/M
+pension/ZGMRDBS
+pensiveness/S
+pensive/PY
+pens/V
+pentacle/MS
+pentagonal/SY
+Pentagon/M
+pentagon/SM
+pentagram/MS
+pentameter/SM
+pent/AS
+Pentateuch/M
+pentathlete/S
+pentathlon/MS
+pentatonic
+pentecostal
+Pentecostalism/S
+Pentecostal/S
+Pentecost/SM
+penthouse/SDGM
+Pentium/M
+penuche/SM
+penultimate/SY
+penumbrae
+penumbra/MS
+penuriousness/MS
+penurious/YP
+penury/SM
+peonage/MS
+peon/MS
+peony/SM
+people/SDMG
+Peoria/M
+Pepe/M
+Pepillo/M
+Pepi/M
+Pepin/M
+Pepita/M
+Pepito/M
+pepped
+peppercorn/MS
+pepperer/M
+peppergrass/M
+peppermint/MS
+pepperoni/S
+pepper/SGRDM
+peppery
+peppiness/SM
+pepping
+peppy/PRT
+Pepsico/M
+PepsiCo/M
+Pepsi/M
+pepsin/SM
+pep/SM
+peptic/S
+peptidase/SM
+peptide/SM
+peptizing
+Pepys/M
+Pequot/M
+peradventure/S
+perambulate/DSNGX
+perambulation/M
+perambulator/MS
+percale/MS
+perceivably
+perceive/DRSZGB
+perceived/U
+perceiver/M
+percentage/MS
+percentile/SM
+percent/MS
+perceptible
+perceptibly
+perceptional
+perception/MS
+perceptiveness/MS
+perceptive/YP
+perceptual/Y
+percept/VMS
+Perceval/M
+perchance
+perch/GSDM
+perchlorate/M
+perchlorination
+percipience/MS
+percipient/S
+Percival/M
+percolate/NGSDX
+percolation/M
+percolator/MS
+percuss/DSGV
+percussionist/MS
+percussion/SAM
+percussiveness/M
+percussive/PY
+percutaneous/Y
+Percy/M
+perdition/MS
+perdurable
+peregrinate/XSDNG
+peregrination/M
+peregrine/S
+Perelman/M
+peremptorily
+peremptory/P
+perennial/SY
+pères
+perestroika/S
+Perez/M
+perfecta/S
+perfect/DRYSTGVP
+perfecter/M
+perfectibility/MS
+perfectible
+perfectionism/MS
+perfectionist/MS
+perfection/MS
+perfectiveness/M
+perfective/PY
+perfectness/MS
+perfidiousness/M
+perfidious/YP
+perfidy/MS
+perforated/U
+perforate/XSDGN
+perforation/M
+perforce
+performance/MS
+performed/U
+performer/M
+perform/SDRZGB
+perfumer/M
+perfumery/SM
+perfume/ZMGSRD
+perfunctorily
+perfunctoriness/M
+perfunctory/P
+perfused
+perfusion/M
+Pergamon/M
+pergola/SM
+perhaps/S
+Peria/M
+pericardia
+pericardium/M
+Perice/M
+Periclean
+Pericles/M
+perigee/SM
+perihelia
+perihelion/M
+peril/GSDM
+Perilla/M
+perilousness/M
+perilous/PY
+Peri/M
+perimeter/MS
+perinatal
+perinea
+perineum/M
+periodic
+periodical/YMS
+periodicity/MS
+period/MS
+periodontal/Y
+periodontics/M
+periodontist/S
+peripatetic/S
+peripheral/SY
+periphery/SM
+periphrases
+periphrasis/M
+periphrastic
+periscope/SDMG
+perishable/SM
+perish/BZGSRD
+perishing/Y
+peristalses
+peristalsis/M
+peristaltic
+peristyle/MS
+peritoneal
+peritoneum/SM
+peritonitis/MS
+periwigged
+periwigging
+periwig/MS
+periwinkle/SM
+perjurer/M
+perjure/SRDZG
+perjury/MS
+per/K
+perk/GDS
+perkily
+perkiness/S
+Perkin/SM
+perky/TRP
+Perla/M
+Perle/M
+Perl/M
+permafrost/MS
+permalloy/M
+Permalloy/M
+permanence/SM
+permanency/MS
+permanentness/M
+permanent/YSP
+permeability/SM
+permeableness/M
+permeable/P
+permeate/NGVDSX
+Permian
+permissibility/M
+permissibleness/M
+permissible/P
+permissibly
+permission/SM
+permissiveness/MS
+permissive/YP
+permit/SM
+permitted
+permitting
+Perm/M
+perm/MDGS
+permutation/MS
+permute/SDG
+Pernell/M
+perniciousness/MS
+pernicious/PY
+Pernod/M
+Peron/M
+peroration/SM
+Perot/M
+peroxidase/M
+peroxide/MGDS
+perpend/DG
+perpendicularity/SM
+perpendicular/SY
+perpetrate/NGXSD
+perpetration/M
+perpetrator/SM
+perpetual/SY
+perpetuate/NGSDX
+perpetuation/M
+perpetuity/MS
+perplex/DSG
+perplexed/Y
+perplexity/MS
+perquisite/SM
+Perren/M
+Perri/M
+Perrine/M
+Perry/MR
+persecute/XVNGSD
+persecution/M
+persecutor/MS
+persecutory
+Perseid/M
+Persephone/M
+Perseus/M
+perseverance/MS
+persevere/GSD
+persevering/Y
+Pershing/M
+Persia/M
+Persian/S
+persiflage/MS
+persimmon/SM
+Persis/M
+persist/DRSG
+persistence/SM
+persistent/Y
+persnickety
+personableness/M
+personable/P
+personae
+personage/SM
+personality/SM
+personalization/CMS
+personalize/CSDG
+personalized/U
+personalty/MS
+personal/YS
+persona/M
+person/BMS
+personification/M
+personifier/M
+personify/XNGDRS
+personnel/SM
+person's/U
+persons/U
+perspective/YMS
+perspex
+perspicaciousness/M
+perspicacious/PY
+perspicacity/S
+perspicuity/SM
+perspicuousness/M
+perspicuous/YP
+perspiration/MS
+perspire/DSG
+persuaded/U
+persuader/M
+persuade/ZGDRSB
+persuasion/SM
+persuasively
+persuasiveness/MS
+persuasive/U
+pertain/GSD
+Perth/M
+pertinaciousness/M
+pertinacious/YP
+pertinacity/MS
+pertinence/S
+pertinent/YS
+pertness/MS
+perturbation/MS
+perturbed/U
+perturb/GDS
+pertussis/SM
+pert/YRTSP
+peruke/SM
+Peru/M
+perusal/SM
+peruser/M
+peruse/RSDZG
+Peruvian/S
+pervade/SDG
+pervasion/M
+pervasiveness/MS
+pervasive/PY
+perverseness/SM
+perverse/PXYNV
+perversion/M
+perversity/MS
+pervert/DRSG
+perverted/YP
+perverter/M
+perviousness
+peseta/SM
+Peshawar/M
+peskily
+peskiness/S
+pesky/RTP
+peso/MS
+pessimal/Y
+pessimism/SM
+pessimistic
+pessimistically
+pessimist/SM
+pester/DG
+pesticide/MS
+pestiferous
+pestilence/SM
+pestilential/Y
+pestilent/Y
+pestle/SDMG
+pesto/S
+pest/RZSM
+PET
+Pétain/M
+petal/SDM
+Peta/M
+petard/MS
+petcock/SM
+Pete/M
+peter/GD
+Peter/M
+Petersburg/M
+Petersen/M
+Peters/N
+Peterson/M
+Peterus/M
+Petey/M
+pethidine/M
+petiole/SM
+petiteness/M
+petite/XNPS
+petitioner/M
+petition/GZMRD
+petition's/A
+petitions/A
+petits
+Petkiewicz/M
+Pet/MRZ
+Petra/M
+Petrarch/M
+petrel/SM
+petri
+petrifaction/SM
+petrify/NDSG
+Petrina/M
+Petr/M
+petrochemical/SM
+petrodollar/MS
+petroglyph/M
+petrolatum/MS
+petroleum/MS
+petrolled
+petrolling
+petrol/MS
+petrologist/MS
+petrology/MS
+Petronella/M
+Petronia/M
+Petronilla/M
+Petronille/M
+pet/SMRZ
+petted
+petter/MS
+Pettibone/M
+petticoat/SMD
+pettifogged
+pettifogger/SM
+pettifogging
+pettifog/S
+pettily
+pettiness/S
+petting
+pettis
+pettishness/M
+pettish/YP
+Petty/M
+petty/PRST
+petulance/MS
+petulant/Y
+Petunia/M
+petunia/SM
+Peugeot/M
+Pewaukee/M
+pewee/MS
+pewit/MS
+pew/SM
+pewter/SRM
+peyote/SM
+Peyter/M
+Peyton/M
+pf
+Pfc
+PFC
+pfennig/SM
+Pfizer/M
+pg
+PG
+Phaedra/M
+Phaethon/M
+phaeton/MS
+phage/M
+phagocyte/SM
+Phaidra/M
+phalanger/MS
+phalanges
+phalanx/SM
+phalli
+phallic
+phallus/M
+Phanerozoic
+phantasmagoria/SM
+phantasmal
+phantasm/SM
+phantasy's
+phantom/MS
+pharaoh
+Pharaoh/M
+pharaohs
+Pharaohs
+pharisaic
+Pharisaic
+Pharisaical
+pharisee/S
+Pharisee/SM
+pharmaceutical/SY
+pharmaceutic/S
+pharmaceutics/M
+pharmacist/SM
+pharmacological/Y
+pharmacologist/SM
+pharmacology/SM
+pharmacopoeia/SM
+pharmacy/SM
+pharyngeal/S
+pharynges
+pharyngitides
+pharyngitis/M
+pharynx/M
+phase/DSRGZM
+phaseout/S
+PhD
+pheasant/SM
+Phebe/M
+Phedra/M
+Phekda/M
+Phelia/M
+Phelps/M
+phenacetin/MS
+phenobarbital/SM
+phenolic
+phenol/MS
+phenolphthalein/M
+phenomenal/Y
+phenomena/SM
+phenomenological/Y
+phenomenology/MS
+phenomenon/SM
+phenotype/MS
+phenylalanine/M
+phenyl/M
+pheromone/MS
+phew/S
+phialled
+phialling
+phial/MS
+Phidias/M
+Philadelphia/M
+philanderer/M
+philander/SRDGZ
+philanthropic
+philanthropically
+philanthropist/MS
+philanthropy/SM
+philatelic
+philatelist/MS
+philately/SM
+Philbert/M
+Philco/M
+philharmonic/S
+Philipa/M
+Philip/M
+Philippa/M
+Philippe/M
+Philippians/M
+philippic/SM
+Philippine/SM
+Philis/M
+philistine/S
+Philistine/SM
+philistinism/S
+Phillida/M
+Phillie/M
+Phillipa/M
+Phillipe/M
+Phillip/MS
+Phillipp/M
+Phillis/M
+Philly/SM
+Phil/MY
+philodendron/MS
+philological/Y
+philologist/MS
+philology/MS
+Philomena/M
+philosopher/MS
+philosophic
+philosophical/Y
+philosophized/U
+philosophizer/M
+philosophizes/U
+philosophize/ZDRSG
+philosophy/MS
+philter/SGDM
+philtre/DSMG
+Phineas/M
+Phip/M
+Phipps/M
+phi/SM
+phlebitides
+phlebitis/M
+phlegmatic
+phlegmatically
+phlegm/SM
+phloem/MS
+phlox/M
+pH/M
+Ph/M
+phobia/SM
+phobic/S
+Phobos/M
+Phoebe/M
+phoebe/SM
+Phoenicia/M
+Phoenician/SM
+Phoenix/M
+phoenix/MS
+phone/DSGM
+phoneme/SM
+phonemically
+phonemic/S
+phonemics/M
+phonetically
+phonetician/SM
+phonetic/S
+phonetics/M
+phonically
+phonic/S
+phonics/M
+phoniness/MS
+phonographer/M
+phonographic
+phonograph/RM
+phonographs
+phonologic
+phonological/Y
+phonologist/MS
+phonology/MS
+phonon/M
+phony/PTRSDG
+phooey/S
+phosphatase/M
+phosphate/MS
+phosphide/M
+phosphine/MS
+phosphoresce
+phosphorescence/SM
+phosphorescent/Y
+phosphoric
+phosphor/MS
+phosphorous
+phosphorus/SM
+photocell/MS
+photochemical/Y
+photochemistry/M
+photocopier/M
+photocopy/MRSDZG
+photoelectric
+photoelectrically
+photoelectronic
+photoelectrons
+photoengraver/M
+photoengrave/RSDJZG
+photoengraving/M
+photofinishing/MS
+photogenic
+photogenically
+photograph/AGD
+photographer/SM
+photographic
+photographically
+photograph's
+photographs/A
+photography/MS
+photojournalism/SM
+photojournalist/SM
+photoluminescence/M
+photolysis/M
+photolytic
+photometer/SM
+photometric
+photometrically
+photometry/M
+photomicrograph/M
+photomicrography/M
+photomultiplier/M
+photon/MS
+photorealism
+photosensitive
+photo/SGMD
+photosphere/M
+photostatic
+Photostat/MS
+Photostatted
+Photostatting
+photosyntheses
+photosynthesis/M
+photosynthesize/DSG
+photosynthetic
+phototypesetter
+phototypesetting/M
+phrasal
+phrase/AGDS
+phrasebook
+phrasemaking
+phraseology/MS
+phrase's
+phrasing/SM
+phrenological/Y
+phrenologist/MS
+phrenology/MS
+phylactery/MS
+phylae
+phyla/M
+Phylis/M
+Phyllida/M
+Phyllis/M
+Phyllys/M
+phylogeny/MS
+phylum/M
+Phylys/M
+phys
+physicality/M
+physical/PYS
+physician/SM
+physicist/MS
+physicked
+physicking
+physic/SM
+physiochemical
+physiognomy/SM
+physiography/MS
+physiologic
+physiological/Y
+physiologist/SM
+physiology/MS
+physiotherapist/MS
+physiotherapy/SM
+physique/MSD
+phytoplankton/M
+Piaf/M
+Piaget/M
+Pia/M
+pianism/M
+pianissimo/S
+pianistic
+pianist/SM
+pianoforte/MS
+pianola
+Pianola/M
+piano/SM
+piaster/MS
+piazza/SM
+pibroch/M
+pibrochs
+picador/MS
+picaresque/S
+pica/SM
+Picasso/M
+picayune/S
+Piccadilly/M
+piccalilli/MS
+piccolo/MS
+pickaback's
+pickaxe's
+pickax/GMSD
+pickerel/MS
+Pickering/M
+picker/MG
+picketer/M
+picket/MSRDZG
+Pickett/M
+Pickford/M
+pick/GZSJDR
+pickle/SDMG
+Pickman/M
+pickoff/S
+pickpocket/GSM
+pickup/SM
+Pickwick/M
+picky/RT
+picnicked
+picnicker/MS
+picnicking
+picnic/SM
+picofarad/MS
+picojoule
+picoseconds
+picot/DMGS
+Pict/M
+pictograph/M
+pictographs
+pictorialness/M
+pictorial/PYS
+picture/MGSD
+picturesqueness/SM
+picturesque/PY
+piddle/GSD
+piddly
+pidgin/SM
+piebald/S
+piece/GMDSR
+piecemeal
+piecer/M
+piecewise
+pieceworker/M
+piecework/ZSMR
+piedmont
+Piedmont/M
+pieing
+pie/MS
+Pierce/M
+piercer/M
+pierce/RSDZGJ
+piercing/Y
+Pierette/M
+pier/M
+Pier/M
+Pierre/M
+Pierrette/M
+Pierrot/M
+Pierson/M
+Pieter/M
+Pietra/M
+Pietrek/M
+Pietro/M
+piety/SM
+piezoelectric
+piezoelectricity/M
+piffle/MGSD
+pigeon/DMGS
+pigeonhole/SDGM
+pigged
+piggery/M
+pigging
+piggishness/SM
+piggish/YP
+piggyback/MSDG
+Piggy/M
+piggy/RSMT
+pigheadedness/S
+pigheaded/YP
+piglet/MS
+pigmentation/MS
+pigment/MDSG
+pig/MLS
+Pigmy's
+pigpen/SM
+pigroot
+pigskin/MS
+pigsty/SM
+pigswill/M
+pigtail/SMD
+Pike/M
+pike/MZGDRS
+piker/M
+pikestaff/MS
+pilaf/MS
+pilaster/SM
+Pilate/M
+pilau's
+pilchard/SM
+Pilcomayo/M
+pile/JDSMZG
+pileup/MS
+pilferage/SM
+pilferer/M
+pilfer/ZGSRD
+Pilgrim
+pilgrimage/DSGM
+pilgrim/MS
+piling/M
+pillage/RSDZG
+pillar/DMSG
+pillbox/MS
+pill/GSMD
+pillion/DMGS
+pillory/MSDG
+pillowcase/SM
+pillow/GDMS
+pillowslip/S
+Pillsbury/M
+pilot/DMGS
+pilothouse/SM
+piloting/M
+pimento/MS
+pimiento/SM
+pimpernel/SM
+pimp/GSMYD
+pimple/SDM
+pimplike
+pimply/TRM
+PIN
+pinafore/MS
+piñata/S
+Pinatubo/M
+pinball/MS
+Pincas/M
+pincer/GSD
+Pinchas/M
+pincher/M
+pinch/GRSD
+pincushion/SM
+Pincus/M
+Pindar/M
+pineapple/MS
+pined/A
+Pinehurst/M
+pine/MNGXDS
+pines/A
+pinfeather/SM
+ping/GDRM
+pinheaded/P
+pinhead/SMD
+pinhole/SM
+pining/A
+pinion/DMG
+Pinkerton/M
+pinkeye/MS
+pink/GTYDRMPS
+pinkie/SM
+pinkish/P
+pinkness/S
+pinko/MS
+pinky's
+pinnacle/MGSD
+pinnate
+pinned/U
+pinning/S
+Pinocchio/M
+Pinochet/M
+pinochle/SM
+piñon/S
+pinpoint/SDG
+pinprick/MDSG
+pin's
+pinsetter/SM
+Pinsky/M
+pinstripe/SDM
+pintail/SM
+Pinter/M
+pint/MRS
+pinto/S
+pinup/MS
+pin/US
+pinwheel/DMGS
+pinyin
+Pinyin
+piny/RT
+pioneer/SDMG
+pion/M
+Piotr/M
+piousness/MS
+pious/YP
+pipeline/DSMG
+pipe/MS
+piper/M
+Piper/M
+Pipestone/M
+pipet's
+pipette/MGSD
+pipework
+piping/YM
+pipit/MS
+pip/JSZMGDR
+Pip/MR
+Pippa/M
+pipped
+pipping
+pippin/SM
+Pippo/M
+Pippy/M
+pipsqueak/SM
+piquancy/MS
+piquantness/M
+piquant/PY
+pique/GMDS
+piracy/MS
+Piraeus/M
+Pirandello/M
+piranha/SM
+pirate/MGSD
+piratical/Y
+pirogi
+pirogies
+pirouette/MGSD
+pis
+Pisa/M
+piscatorial
+Pisces/M
+Pisistratus/M
+pismire/SM
+Pissaro/M
+piss/DSRG
+pistachio/MS
+piste/SM
+pistillate
+pistil/MS
+pistoleers
+pistole/M
+pistol/SMGD
+piston/SM
+pitapat/S
+pitapatted
+pitapatting
+pita/SM
+Pitcairn/M
+pitchblende/SM
+pitcher/M
+pitchfork/GDMS
+pitching/M
+pitchman/M
+pitchmen
+pitch/RSDZG
+pitchstone/M
+piteousness/SM
+piteous/YP
+pitfall/SM
+pithily
+pithiness/SM
+pith/MGDS
+piths
+pithy/RTP
+pitiableness/M
+pitiable/P
+pitiably
+pitier/M
+pitifuller
+pitifullest
+pitifulness/M
+pitiful/PY
+pitilessness/SM
+pitiless/PY
+pitman/M
+pit/MS
+Pitney/M
+piton/SM
+pittance/SM
+pitted
+pitting
+Pittman/M
+Pittsburgh/ZM
+Pittsfield/M
+Pitt/SM
+Pittston/M
+pituitary/SM
+pitying/Y
+pity/ZDSRMG
+Pius/M
+pivotal/Y
+pivot/DMSG
+pivoting/M
+pix/DSG
+pixel/SM
+pixie/MS
+pixiness
+pixmap/SM
+Pizarro/M
+pizazz/S
+pi/ZGDRH
+pizza/SM
+pizzeria/SM
+pizzicati
+pizzicato
+pj's
+PJ's
+pk
+pkg
+pkt
+pkwy
+Pkwy
+pl
+placard/DSMG
+placate/NGVXDRS
+placatory
+placeable/A
+placebo/SM
+placed/EAU
+place/DSRJLGZM
+placeholder/S
+placekick/DGS
+placeless/Y
+placement/AMES
+placental/S
+placenta/SM
+placer/EM
+places/EA
+placidity/SM
+placidness/M
+placid/PY
+placing/AE
+placket/SM
+plagiarism/MS
+plagiarist/MS
+plagiarize/GZDSR
+plagiary/SM
+plagued/U
+plague/MGRSD
+plaguer/M
+plaice/M
+plaid/DMSG
+plainclothes
+plainclothesman
+plainclothesmen
+Plainfield/M
+plainness/MS
+plainsman/M
+plainsmen
+plainsong/SM
+plainspoken
+plain/SPTGRDY
+plaintiff/MS
+plaintiveness/M
+plaintive/YP
+plaint/VMS
+Plainview/M
+plaiting/M
+plait/SRDMG
+planar
+planarity
+Planck/M
+plan/DRMSGZ
+planeload
+planer/M
+plane's
+plane/SCGD
+planetarium/MS
+planetary
+planetesimal/M
+planet/MS
+planetoid/SM
+plangency/S
+plangent
+planking/M
+plank/SJMDG
+plankton/MS
+planned/U
+planner/SM
+planning
+Plano
+planoconcave
+planoconvex
+Plantagenet/M
+plantain/MS
+plantar
+plantation/MS
+planter/MS
+planting/S
+plantlike
+plant's
+plant/SADG
+plaque/MS
+plash/GSDM
+plasma/MS
+plasmid/S
+plasm/M
+plasterboard/MS
+plasterer/M
+plastering/M
+plaster/MDRSZG
+plasterwork/M
+plastically
+plasticine
+Plasticine/M
+plasticity/SM
+plasticize/GDS
+plastic/MYS
+plateau/GDMS
+plateful/S
+platelet/SM
+platen/M
+plater/M
+plate/SM
+platform/SGDM
+Plath/M
+plating/M
+platinize/GSD
+platinum/MS
+platitude/SM
+platitudinous/Y
+plat/JDNRSGXZ
+Plato/M
+platonic
+Platonic
+Platonism/M
+Platonist
+platoon/MDSG
+platted
+Platte/M
+platter/MS
+Platteville/M
+platting
+platypus/MS
+platys
+platy/TR
+plaudit/MS
+plausibility/S
+plausible/P
+plausibly
+Plautus/M
+playability/U
+playable/U
+playacting/M
+playact/SJDG
+playback/MS
+playbill/SM
+Playboy/M
+playboy/SM
+play/DRSEBG
+played/A
+player's/E
+player/SM
+playfellow/S
+playfulness/MS
+playful/PY
+playgirl/SM
+playgoer/MS
+playground/MS
+playgroup/S
+playhouse/SM
+playing/S
+playmate/MS
+playoff/S
+playpen/SM
+playroom/SM
+plays/A
+Playtex/M
+plaything/MS
+playtime/SM
+playwright/SM
+playwriting/M
+plaza/SM
+pleader/MA
+pleading/MY
+plead/ZGJRDS
+pleasanter
+pleasantest
+pleasantness/SMU
+pleasantry/MS
+pleasant/UYP
+pleased/EU
+pleaser/M
+pleases/E
+please/Y
+pleasingness/M
+pleasing/YP
+plea/SM
+pleas/RSDJG
+pleasurableness/M
+pleasurable/P
+pleasurably
+pleasureful
+pleasure/MGBDS
+pleasure's/E
+pleasures/E
+pleater/M
+pleat/RDMGS
+plebeian/SY
+plebe/MS
+plebiscite/SM
+plectra
+plectrum/SM
+pledger/M
+pledge/RSDMG
+Pleiads
+Pleistocene
+plenary/S
+plenipotentiary/S
+plenitude/MS
+plenteousness/M
+plenteous/PY
+plentifulness/M
+plentiful/YP
+plenty/SM
+plenum/M
+pleonasm/MS
+plethora/SM
+pleurae
+pleural
+pleura/M
+pleurisy/SM
+Plexiglas/MS
+plexus/SM
+pliability/MS
+pliableness/M
+pliable/P
+pliancy/MS
+pliantness/M
+pliant/YP
+plication/MA
+plier/MA
+plight/GMDRS
+plimsolls
+plinker/M
+plink/GRDS
+plinth/M
+plinths
+Pliny/M
+Pliocene/S
+PLO
+plodded
+plodder/SM
+plodding/SY
+plod/S
+plopped
+plopping
+plop/SM
+plosive
+plot/SM
+plotted/A
+plotter/MDSG
+plotting
+plover/MS
+plowed/U
+plower/M
+plowman/M
+plowmen
+plow/SGZDRM
+plowshare/MS
+ploy's
+ploy/SCDG
+plucker/M
+pluckily
+pluckiness/SM
+pluck/SGRD
+plucky/TPR
+pluggable
+plugged/UA
+plugging/AU
+plughole
+plug's
+plug/US
+plumage/DSM
+plumbago/M
+plumbed/U
+plumber/M
+plumbing/M
+plumb/JSZGMRD
+plume/SM
+plummer
+plummest
+plummet/DSG
+plummy
+plumper/M
+plumpness/S
+plump/RDNYSTGP
+plum/SMDG
+plumy/TR
+plunder/GDRSZ
+plunger/M
+plunge/RSDZG
+plunker/M
+plunk/ZGSRD
+pluperfect/S
+pluralism/MS
+pluralistic
+pluralist/S
+plurality/SM
+pluralization/MS
+pluralize/GZRSD
+pluralizer/M
+plural/SY
+plushness/MS
+plush/RSYMTP
+plushy/RPT
+plus/S
+plussed
+plussing
+Plutarch/M
+plutocracy/MS
+plutocratic
+plutocrat/SM
+Pluto/M
+plutonium/SM
+pluvial/S
+ply/AZNGRSD
+Plymouth/M
+plywood/MS
+pm
+PM
+Pm/M
+PMS
+pneumatically
+pneumatic/S
+pneumatics/M
+pneumonia/MS
+PO
+poacher/M
+poach/ZGSRD
+Pocahontas/M
+pocketbook/SM
+pocketful/SM
+pocketing/M
+pocketknife/M
+pocketknives
+pocket/MSRDG
+pock/GDMS
+pockmark/MDSG
+Pocono/MS
+podded
+podding
+podge/ZR
+Podgorica/M
+podiatrist/MS
+podiatry/MS
+podium/MS
+pod/SM
+Podunk/M
+Poe/M
+poem/MS
+poesy/GSDM
+poetaster/MS
+poetess/MS
+poetically
+poeticalness
+poetical/U
+poetic/S
+poetics/M
+poet/MS
+poetry/SM
+pogo
+Pogo/M
+pogrom/GMDS
+poignancy/MS
+poignant/Y
+Poincaré/M
+poinciana/SM
+Poindexter/M
+poinsettia/SM
+pointblank
+pointedness/M
+pointed/PY
+pointer/M
+pointillism/SM
+pointillist/SM
+pointing/M
+pointlessness/SM
+pointless/YP
+point/RDMZGS
+pointy/TR
+poise/M
+pois/GDS
+poi/SM
+poisoner/M
+poisoning/M
+poisonous/PY
+poison/RDMZGSJ
+Poisson/M
+poke/DRSZG
+Pokemon/M
+pokerface/D
+poker/M
+poky/SRT
+Poland/M
+Polanski/M
+polarimeter/SM
+polarimetry
+polariscope/M
+Polaris/M
+polarity/MS
+polarization/CMS
+polarized/UC
+polarize/RSDZG
+polarizes/C
+polarizing/C
+polarogram/SM
+polarograph
+polarography/M
+Polaroid/SM
+polar/S
+polecat/SM
+polemical/Y
+polemicist/S
+polemic/S
+polemics/M
+pole/MS
+Pole/MS
+poler/M
+polestar/S
+poleward/S
+pol/GMDRS
+policeman/M
+policemen/M
+police/MSDG
+policewoman/M
+policewomen
+policyholder/MS
+policymaker/S
+policymaking
+policy/SM
+poliomyelitides
+poliomyelitis/M
+polio/SM
+Polish
+polished/U
+polisher/M
+polish/RSDZGJ
+polis/M
+Politburo/M
+politburo/S
+politeness/MS
+polite/PRTY
+politesse/SM
+politically
+political/U
+politician/MS
+politicization/S
+politicize/CSDG
+politicked
+politicking/SM
+politico/SM
+politic/S
+politics/M
+polity/MS
+polka/SDMG
+Polk/M
+pollack/SM
+Pollard/M
+polled/U
+pollen/GDM
+pollinate/XSDGN
+pollination/M
+pollinator/MS
+polliwog/SM
+poll/MDNRSGX
+pollock's
+Pollock/SM
+pollster/MS
+pollutant/MS
+polluted/U
+polluter/M
+pollute/RSDXZVNG
+pollution/M
+Pollux/M
+Pollyanna/M
+Polly/M
+pollywog's
+Pol/MY
+Polo/M
+polo/MS
+polonaise/MS
+polonium/MS
+poltergeist/SM
+poltroon/MS
+polyandrous
+polyandry/MS
+polyatomic
+polybutene/MS
+polycarbonate
+polychemicals
+polychrome
+polyclinic/MS
+polycrystalline
+polyelectrolytes
+polyester/SM
+polyether/S
+polyethylene/SM
+polygamist/MS
+polygamous/Y
+polygamy/MS
+polyglot/S
+polygonal/Y
+polygon/MS
+polygraph/MDG
+polygraphs
+polygynous
+polyhedral
+polyhedron/MS
+Polyhymnia/M
+polyisobutylene
+polyisocyanates
+polymath/M
+polymaths
+polymerase/S
+polymeric
+polymerization/SM
+polymerize/SDG
+polymer/MS
+polymorphic
+polymorphisms
+polymorph/M
+polymyositis
+Polynesia/M
+Polynesian/S
+polynomial/YMS
+Polyphemus/M
+polyphonic
+polyphony/MS
+polyphosphate/S
+polyp/MS
+polypropylene/MS
+polystyrene/SM
+polysyllabic
+polysyllable/SM
+polytechnic/MS
+polytheism/SM
+polytheistic
+polytheist/SM
+polythene/M
+polytonal/Y
+polytopes
+polyunsaturated
+polyurethane/SM
+polyvinyl/MS
+Po/M
+pomade/MGSD
+pomander/MS
+pomegranate/SM
+Pomerania/M
+Pomeranian
+pommel/GSMD
+Pomona/M
+Pompadour/M
+pompadour/MDS
+pompano/SM
+Pompeian/S
+Pompeii/M
+Pompey/M
+pompom/SM
+pompon's
+pomposity/MS
+pompousness/S
+pompous/YP
+pomp/SM
+ponce/M
+Ponce/M
+Ponchartrain/M
+poncho/MS
+ponderer/M
+ponderousness/MS
+ponderous/PY
+ponder/ZGRD
+pond/SMDRGZ
+pone/SM
+pongee/MS
+poniard/GSDM
+pons/M
+Pontchartrain/M
+Pontiac/M
+Pontianak/M
+pontiff/MS
+pontifical/YS
+pontificate/XGNDS
+pontoon/SMDG
+pony/DSMG
+ponytail/SM
+pooch/GSDM
+poodle/MS
+poof/MS
+pooh/DG
+Pooh/M
+poohs
+Poole/M
+pool/MDSG
+poolroom/MS
+poolside
+Poona/M
+poop/MDSG
+poorboy
+poorhouse/MS
+poorness/MS
+poor/TYRP
+popcorn/MS
+Popek/MS
+pope/SM
+Pope/SM
+Popeye/M
+popgun/SM
+popinjay/MS
+poplar/SM
+poplin/MS
+Popocatepetl/M
+popover/SM
+poppa/MS
+popped
+Popper/M
+popper/SM
+poppet/M
+popping
+Poppins/M
+poppycock/MS
+Poppy/M
+poppy/SDM
+poppyseed
+Popsicle/MS
+pop/SM
+populace/MS
+popularism
+popularity/UMS
+popularization/SM
+popularize/A
+popularized
+popularizer/MS
+popularizes/U
+popularizing
+popular/YS
+populate/CXNGDS
+populated/UA
+populates/A
+populating/A
+population/MC
+populism/S
+populist/SM
+populousness/MS
+populous/YP
+porcelain/SM
+porch/SM
+porcine
+porcupine/MS
+pore/ZGDRS
+Porfirio/M
+porgy/SM
+poring/Y
+porker/M
+porky/TSR
+pork/ZRMS
+pornographer/SM
+pornographic
+pornographically
+pornography/SM
+porno/S
+porn/S
+porosity/SM
+porousness/MS
+porous/PY
+porphyritic
+porphyry/MS
+porpoise/DSGM
+porridge/MS
+Porrima/M
+porringer/MS
+Porsche/M
+portability/S
+portables
+portable/U
+portably
+port/ABSGZMRD
+portage/ASM
+portaged
+portaging
+portal/SM
+portamento/M
+portcullis/MS
+ported/CE
+Porte/M
+portend/SDG
+portentousness/M
+portentous/PY
+portent/SM
+porterage/M
+porter/DMG
+porterhouse/SM
+Porter/M
+porter's/A
+portfolio/MS
+porthole/SM
+Portia/M
+porticoes
+portico/M
+Portie/M
+portière/SM
+porting/E
+portion/KGSMD
+Portland/M
+portliness/SM
+portly/PTR
+portmanteau/SM
+Port/MR
+Pôrto/M
+portraitist/SM
+portrait/MS
+portraiture/MS
+portrayal/SM
+portrayer/M
+portray/GDRS
+ports/CE
+Portsmouth/M
+Portugal/M
+Portuguese/M
+portulaca/MS
+Porty/M
+posed/CA
+Poseidon/M
+poser/KME
+poses/CA
+poseur/MS
+pose/ZGKDRSE
+posh/DSRGT
+posing/CA
+positifs
+positionable
+positional/KY
+position/KGASMD
+position's/EC
+positions/EC
+positiveness/S
+positive/RSPYT
+positivism/M
+positivist/S
+positivity
+positron/SM
+posit/SCGD
+Posner/M
+posse/M
+possess/AGEDS
+possessed/PY
+possession/AEMS
+possessional
+possessiveness/MS
+possessive/PSMY
+possessor/MS
+possibility/SM
+possible/TRS
+possibly
+poss/S
+possum/MS
+postage/MS
+postal/S
+post/ASDRJG
+postbag/M
+postbox/SM
+postcard/SM
+postcode/SM
+postcondition/S
+postconsonantal
+postdate/DSG
+postdoctoral
+posteriori
+posterior/SY
+posterity/SM
+poster/MS
+postfix/GDS
+postgraduate/SM
+posthaste/S
+posthumousness/M
+posthumous/YP
+posthypnotic
+postilion/MS
+postindustrial
+posting/M
+postlude/MS
+Post/M
+postman/M
+postmarital
+postmark/GSMD
+postmaster/SM
+postmen
+postmeridian
+postmistress/MS
+postmodern
+postmodernist
+postmortem/S
+postnasal
+postnatal
+postoperative/Y
+postorder
+postpaid
+postpartum
+postpone/GLDRS
+postponement/S
+postpositions
+postprandial
+post's
+postscript/SM
+postsecondary
+postulate/XGNSD
+postulation/M
+postural
+posture/MGSRD
+posturer/M
+postvocalic
+postwar
+posy/SM
+potability/SM
+potableness/M
+potable/SP
+potage/M
+potash/MS
+potassium/MS
+potatoes
+potato/M
+potbelly/MSD
+potboiler/M
+potboil/ZR
+pot/CMS
+Potemkin/M
+potency/MS
+potentate/SM
+potentiality/MS
+potential/SY
+potentiating
+potentiometer/SM
+potent/YS
+potful/SM
+pothead/MS
+potherb/MS
+pother/GDMS
+potholder/MS
+pothole/SDMG
+potholing/M
+pothook/SM
+potion/SM
+potlatch/SM
+potluck/MS
+Potomac/M
+potpie/SM
+potpourri/SM
+Potsdam/M
+potsherd/MS
+potshot/S
+pottage/SM
+Pottawatomie/M
+potted
+Potter/M
+potter/RDMSG
+pottery/MS
+potting
+Potts/M
+potty/SRT
+pouch/SDMG
+Poughkeepsie/M
+Poul/M
+poulterer/MS
+poultice/DSMG
+poultry/MS
+pounce/SDG
+poundage/MS
+pounder/MS
+pound/KRDGS
+Pound/M
+pour/DSG
+pourer's
+Poussin/MS
+pouter/M
+pout/GZDRS
+poverty/MS
+POW
+powderpuff
+powder/RDGMS
+powdery
+Powell/M
+powerboat/MS
+powerfulness/M
+powerful/YP
+power/GMD
+powerhouse/MS
+powerlessness/SM
+powerless/YP
+Powers
+Powhatan/M
+pow/RZ
+powwow/GDMS
+pox/GMDS
+Poznan/M
+pp
+PP
+ppm
+ppr
+PPS
+pr
+PR
+practicability/S
+practicable/P
+practicably
+practicality/SM
+practicalness/M
+practical/YPS
+practice/BDRSMG
+practiced/U
+practicer/M
+practicum/SM
+practitioner/SM
+Pradesh/M
+Prado/M
+Praetorian
+praetorian/S
+praetor/MS
+pragmatical/Y
+pragmatic/S
+pragmatics/M
+pragmatism/MS
+pragmatist/MS
+Prague/M
+Praia
+prairie/MS
+praise/ESDG
+praiser/S
+praise's
+praiseworthiness/MS
+praiseworthy/P
+praising/Y
+Prakrit/M
+praline/MS
+pram/MS
+prancer/M
+prance/ZGSRD
+prancing/Y
+prank/SMDG
+prankster/SM
+praseodymium/SM
+Pratchett/M
+prate/DSRGZ
+prater/M
+pratfall/MS
+prating/Y
+prattle/DRSGZ
+prattler/M
+prattling/Y
+Pratt/M
+Prattville/M
+Pravda/M
+prawn/MDSG
+praxes
+praxis/M
+Praxiteles/M
+pray/DRGZS
+prayerbook
+prayerfulness/M
+prayerful/YP
+prayer/M
+PRC
+preach/DRSGLZJ
+preacher/M
+preaching/Y
+preachment/MS
+preachy/RT
+preadolescence/S
+Preakness/M
+preallocate/XGNDS
+preallocation/M
+preallocator/S
+preamble/MGDS
+preamp
+preamplifier/M
+prearrange/LSDG
+prearrangement/SM
+preassign/SDG
+preauthorize
+prebendary/M
+Precambrian
+precancel/DGS
+precancerous
+precariousness/MS
+precarious/PY
+precautionary
+precaution/SGDM
+precede/DSG
+precedence/SM
+precedented/U
+precedent/SDM
+preceptive/Y
+preceptor/MS
+precept/SMV
+precess/DSG
+precession/M
+precinct/MS
+preciosity/MS
+preciousness/S
+precious/PYS
+precipice/MS
+precipitable
+precipitant/S
+precipitateness/M
+precipitate/YNGVPDSX
+precipitation/M
+precipitousness/M
+precipitous/YP
+preciseness/SM
+precise/XYTRSPN
+precision/M
+précis/MDG
+preclude/GDS
+preclusion/S
+precociousness/MS
+precocious/YP
+precocity/SM
+precode/D
+precognition/SM
+precognitive
+precollege/M
+precolonial
+precomputed
+preconceive/GSD
+preconception/SM
+precondition/GMDS
+preconscious
+precook/GDS
+precursor/SM
+precursory
+precut
+predate/NGDSX
+predation/CMS
+predator/SM
+predatory
+predecease/SDG
+predecessor/MS
+predeclared
+predecline
+predefine/GSD
+predefinition/SM
+predesignate/GDS
+predestination/SM
+predestine/SDG
+predetermination/MS
+predeterminer/M
+predetermine/ZGSRD
+predicable/S
+predicament/SM
+predicate/VGNXSD
+predication/M
+predicator
+predictability/UMS
+predictable/U
+predictably/U
+predict/BSDGV
+predicted/U
+prediction/MS
+predictive/Y
+predictor/MS
+predigest/GDS
+predilect
+predilection/SM
+predispose/SDG
+predisposition/MS
+predoctoral
+predominance/SM
+predominant/Y
+predominate/YSDGN
+predomination/M
+preemie/MS
+preeminence/SM
+preeminent/Y
+preemployment/M
+preempt/GVSD
+preemption/SM
+preemptive/Y
+preemptor/M
+preener/M
+preen/SRDG
+preexist/DSG
+preexistence/SM
+preexistent
+prefabbed
+prefabbing
+prefab/MS
+prefabricate/XNGDS
+prefabrication/M
+preface/DRSGM
+prefacer/M
+prefatory
+prefect/MS
+prefecture/MS
+preferableness/M
+preferable/P
+preferably
+prefer/BL
+preference/MS
+preferential/Y
+preferment/SM
+preferred
+preferring
+prefiguration/M
+prefigure/SDG
+prefix/MDSG
+preflight/SGDM
+preform/DSG
+pref/RZ
+pregnancy/SM
+pregnant/Y
+preheat/GDS
+prehensile
+prehistoric
+prehistorical/Y
+prehistory/SM
+preindustrial
+preinitialize/SDG
+preinterview/M
+preisolated
+prejudge/DRSG
+prejudger/M
+prejudgment/SM
+prejudiced/U
+prejudice/MSDG
+prejudicial/PY
+prekindergarten/MS
+prelacy/MS
+prelate/SM
+preliminarily
+preliminary/S
+preliterate/S
+preloaded
+prelude/GMDRS
+preluder/M
+premarital/Y
+premarket
+prematureness/M
+premature/SPY
+prematurity/M
+premedical
+premeditated/Y
+premeditate/XDSGNV
+premeditation/M
+premed/S
+premenstrual
+premiere/MS
+premier/GSDM
+premiership/SM
+Preminger/M
+premise/GMDS
+premiss's
+premium/MS
+premix/GDS
+premolar/S
+premonition/SM
+premonitory
+prenatal/Y
+Pren/M
+Prenticed/M
+Prentice/MGD
+Prenticing/M
+Prentiss/M
+Prent/M
+prenuptial
+preoccupation/MS
+preoccupy/DSG
+preoperative
+preordain/DSLG
+prepackage/GSD
+prepaid
+preparation/SM
+preparative/SYM
+preparatory
+preparedly
+preparedness/USM
+prepared/UP
+prepare/ZDRSG
+prepay/GLS
+prepayment/SM
+prepender/S
+prepends
+preplanned
+preponderance/SM
+preponderant/Y
+preponderate/DSYGN
+prepositional/Y
+preposition/SDMG
+prepossess/GSD
+prepossessing/U
+prepossession/MS
+preposterousness/M
+preposterous/PY
+prepped
+prepping
+preppy/RST
+preprepared
+preprint/SGDM
+preprocessed
+preprocessing
+preprocessor/S
+preproduction
+preprogrammed
+prep/SM
+prepubescence/S
+prepubescent/S
+prepublication/M
+prepuce/SM
+prequel/S
+preradiation
+prerecord/DGS
+preregister/DSG
+preregistration/MS
+prerequisite/SM
+prerogative/SDM
+Pres
+presage/GMDRS
+presager/M
+presbyopia/MS
+presbyterian
+Presbyterianism/S
+Presbyterian/S
+presbyter/MS
+presbytery/MS
+preschool/RSZ
+prescience/SM
+prescient/Y
+Prescott/M
+prescribed/U
+prescriber/M
+prescribe/RSDG
+prescription/SM
+prescriptive/Y
+prescript/SVM
+preselect/SGD
+presence/SM
+presentableness/M
+presentable/P
+presentably/A
+presentational/A
+presentation/AMS
+presented/A
+presenter/A
+presentiment/MS
+presentment/SM
+presents/A
+present/SLBDRYZGP
+preservationist/S
+preservation/SM
+preservative/SM
+preserve/DRSBZG
+preserved/U
+preserver/M
+preset/S
+presetting
+preshrank
+preshrink/SG
+preshrunk
+preside/DRSG
+presidency/MS
+presidential/Y
+president/SM
+presider/M
+presidia
+presidium/M
+Presley/M
+presoaks
+presort/GDS
+pres/S
+press/ACDSG
+pressed/U
+presser/MS
+pressingly/C
+pressing/YS
+pressman/M
+pressmen
+pressure/DSMG
+pressurization/MS
+pressurize/DSRGZ
+pressurized/U
+prestidigitate/NX
+prestidigitation/M
+prestidigitatorial
+prestidigitator/M
+prestige/MS
+prestigious/PY
+Preston/M
+presto/S
+presumably
+presume/BGDRS
+presumer/M
+presuming/Y
+presumption/MS
+presumptive/Y
+presumptuousness/SM
+presumptuous/YP
+presuppose/GDS
+presupposition/S
+pretax
+preteen/S
+pretended/Y
+pretender/M
+pretending/U
+pretend/SDRZG
+pretense/MNVSX
+pretension/GDM
+pretentiousness/S
+pretentious/UYP
+preterite's
+preterit/SM
+preternatural/Y
+pretest/SDG
+pretext/SMDG
+Pretoria/M
+pretreated
+pretreatment/S
+pretrial
+prettify/SDG
+prettily
+prettiness/SM
+pretty/TGPDRS
+pretzel/SM
+prevailing/Y
+prevail/SGD
+prevalence/MS
+prevalent/SY
+prevaricate/DSXNG
+prevaricator/MS
+preventable/U
+preventably
+preventative/S
+prevent/BSDRGV
+preventer/M
+prevention/MS
+preventiveness/M
+preventive/SPY
+preview/ZGSDRM
+previous/Y
+prevision/SGMD
+prewar
+prexes
+preyer's
+prey/SMDG
+Priam/M
+priapic
+Pribilof/M
+price/AGSD
+priced/U
+priceless
+Price/M
+pricer/MS
+price's
+pricey
+pricier
+priciest
+pricker/M
+pricking/M
+prickle/GMDS
+prickliness/S
+prickly/RTP
+prick/RDSYZG
+prideful/Y
+pride/GMDS
+prier/M
+priestess/MS
+priesthood/SM
+Priestley/M
+priestliness/SM
+priestly/PTR
+priest/SMYDG
+prigged
+prigging
+priggishness/S
+priggish/PYM
+prig/SM
+primacy/MS
+primal
+primarily
+primary/MS
+primate/MS
+primed/U
+primely/M
+primeness/M
+prime/PYS
+primer/M
+Prime's
+primeval/Y
+priming/M
+primitiveness/SM
+primitive/YPS
+primitivism/M
+primmed
+primmer
+primmest
+primming
+primness/MS
+primogenitor/MS
+primogeniture/MS
+primordial/YS
+primp/DGS
+primrose/MGSD
+prim/SPJGZYDR
+princedom/MS
+princeliness/SM
+princely/PRT
+Prince/M
+prince/SMY
+princess/MS
+Princeton/M
+principality/MS
+principal/SY
+Principe/M
+Principia/M
+principled/U
+principle/SDMG
+printable/U
+printably
+print/AGDRS
+printed/U
+printer/AM
+printers
+printing/SM
+printmaker/M
+printmake/ZGR
+printmaking/M
+printout/S
+Prinz/M
+prioress/MS
+priori
+prioritize/DSRGZJ
+priority/MS
+prior/YS
+priory/SM
+Pris
+Prisca/M
+Priscella/M
+Priscilla/M
+prised
+prise/GMAS
+prismatic
+prism/MS
+prison/DRMSGZ
+prisoner/M
+Prissie/M
+prissily
+prissiness/SM
+prissy/RSPT
+pristine/Y
+prithee/S
+privacy/MS
+privateer/SMDG
+privateness/M
+private/NVYTRSXP
+privation/MCS
+privative/Y
+privatization/S
+privatize/GSD
+privet/SM
+privileged/U
+privilege/SDMG
+privily
+privy/SRMT
+prized/A
+prize/DSRGZM
+prizefighter/M
+prizefighting/M
+prizefight/SRMGJZ
+prizewinner/S
+prizewinning
+Pr/MN
+PRO
+proactive
+probabilist
+probabilistic
+probabilistically
+probability/SM
+probable/S
+probably
+probated/A
+probate/NVMX
+probates/A
+probating/A
+probational
+probationary/S
+probationer/M
+probation/MRZ
+probation's/A
+probative/A
+prober/M
+probity/SM
+problematical/UY
+problematic/S
+problem/SM
+proboscis/MS
+prob/RBJ
+procaine/MS
+procedural/SY
+procedure/MS
+proceeder/M
+proceeding/M
+proceed/JRDSG
+process/BSDMG
+processed/UA
+processes/A
+processional/YS
+procession/GD
+processor/MS
+proclamation/MS
+proclivity/MS
+proconsular
+procrastinate/XNGDS
+procrastination/M
+procrastinator/MS
+procreational
+procreatory
+procrustean
+Procrustean
+Procrustes/M
+proctor/GSDM
+proctorial
+procurable/U
+procure/L
+procurement/MS
+Procyon/M
+prodded
+prodding
+prodigality/S
+prodigal/SY
+prodigiousness/M
+prodigious/PY
+prodigy/MS
+prod/S
+produce/AZGDRS
+producer/AM
+producible/A
+production/ASM
+productively/UA
+productiveness/MS
+productive/PY
+productivities
+productivity/A
+productivity's
+productize/GZRSD
+product/V
+Prof
+profanation/S
+profaneness/MS
+profane/YPDRSG
+profanity/MS
+professed/Y
+professionalism/SM
+professionalize/GSD
+professional/USY
+profession/SM
+professorial/Y
+professorship/SM
+professor/SM
+proffer/GSD
+proficiency/SM
+proficient/YS
+profitability/MS
+profitableness/MU
+profitable/UP
+profitably/U
+profiteer/GSMD
+profiterole/MS
+profit/GZDRB
+profitless
+profligacy/S
+profligate/YS
+proforma/S
+profoundity
+profoundness/SM
+profound/PTYR
+prof/S
+profundity/MS
+profuseness/MS
+profuse/YP
+progenitor/SM
+progeny/M
+progesterone/SM
+prognathous
+prognoses
+prognosis/M
+prognosticate/NGVXDS
+prognostication/M
+prognosticator/S
+prognostic/S
+program/CSA
+programed
+programing
+programmability
+programmable/S
+programmed/CA
+programmer/ASM
+programming/CA
+programmings
+progression/SM
+progressiveness/SM
+progressive/SPY
+progressivism
+progress/MSDVG
+prohibiter/M
+prohibitionist/MS
+prohibition/MS
+Prohibition/MS
+prohibitiveness/M
+prohibitive/PY
+prohibitory
+prohibit/VGSRD
+projected/AU
+projectile/MS
+projectionist/MS
+projection/MS
+projective/Y
+project/MDVGS
+projector/SM
+Prokofieff/M
+Prokofiev/M
+prolegomena
+proletarianization/M
+proletarianized
+proletarian/S
+proletariat/SM
+proliferate/GNVDSX
+proliferation/M
+prolifically
+prolific/P
+prolixity/MS
+prolix/Y
+prologize
+prologue/MGSD
+prologuize
+prolongate/NGSDX
+prolongation/M
+prolonger/M
+prolong/G
+promenade/GZMSRD
+promenader/M
+Promethean
+Prometheus/M
+promethium/SM
+prominence/MS
+prominent/Y
+promiscuity/MS
+promiscuousness/M
+promiscuous/PY
+promise/GD
+promising/UY
+promissory
+promontory/MS
+promote/GVZBDR
+promoter/M
+promotiveness/M
+promotive/P
+prompted/U
+prompter/M
+promptitude/SM
+promptness/MS
+prompt/SGJTZPYDR
+pro/MS
+promulgate/NGSDX
+promulgation/M
+promulgator/MS
+pron
+proneness/MS
+prone/PY
+pronghorn/SM
+prong/SGMD
+pronominalization
+pronominalize
+pronounceable/U
+pronouncedly
+pronounced/U
+pronounce/GLSRD
+pronouncement/SM
+pronouncer/M
+pronto
+pronunciation/SM
+proofed/A
+proofer
+proofing/M
+proofreader/M
+proofread/GZSR
+proof/SEAM
+propaganda/SM
+propagandistic
+propagandist/SM
+propagandize/DSG
+propagated/U
+propagate/SDVNGX
+propagation/M
+propagator/MS
+propellant/MS
+propelled
+propeller/MS
+propelling
+propel/S
+propensity/MS
+properness/M
+proper/PYRT
+propertied/U
+property/SDM
+prophecy/SM
+prophesier/M
+prophesy/GRSDZ
+prophetess/S
+prophetic
+prophetical/Y
+prophet/SM
+prophylactic/S
+prophylaxes
+prophylaxis/M
+propinquity/MS
+propionate/M
+propitiate/GNXSD
+propitiatory
+propitiousness/M
+propitious/YP
+proponent/MS
+proportionality/M
+proportional/SY
+proportionate/YGESD
+proportioner/M
+proportion/ESGDM
+proportionment/M
+proposal/SM
+propped
+propping
+proprietary/S
+proprietorial
+proprietorship/SM
+proprietor/SM
+proprietress/MS
+propriety/MS
+proprioception
+proprioceptive
+prop/SZ
+propulsion/MS
+propulsive
+propylene/M
+prorogation/SM
+prorogue
+prosaic
+prosaically
+proscenium/MS
+prosciutti
+prosciutto/SM
+proscription/SM
+proscriptive
+pros/DSRG
+prosecute/SDBXNG
+prosecution/M
+prosecutor/MS
+proselyte/SDGM
+proselytism/MS
+proselytize/ZGDSR
+prose/M
+proser/M
+Proserpine/M
+prosodic/S
+prosody/MS
+prospect/DMSVG
+prospection/SM
+prospectiveness/M
+prospective/SYP
+prospector/MS
+prospectus/SM
+prosper/GSD
+prosperity/MS
+prosperousness/M
+prosperous/PY
+prostate
+prostheses
+prosthesis/M
+prosthetic/S
+prosthetics/M
+prostitute/DSXNGM
+prostitution/M
+prostrate/SDXNG
+prostration/M
+prosy/RT
+protactinium/MS
+protagonist/SM
+Protagoras/M
+protean/S
+protease/M
+protect/DVGS
+protected/UY
+protectionism/MS
+protectionist/MS
+protection/MS
+protectiveness/S
+protective/YPS
+protectorate/SM
+protector/MS
+protégées
+protégé/SM
+protein/MS
+proteolysis/M
+proteolytic
+Proterozoic/M
+protestantism
+Protestantism/MS
+protestant/S
+Protestant/SM
+protestation/MS
+protest/G
+protesting/Y
+Proteus/M
+protocol/DMGS
+protoplasmic
+protoplasm/MS
+prototype/SDGM
+prototypic
+prototypical/Y
+protozoa
+protozoan/MS
+protozoic
+protozoon's
+protract/DG
+protrude/SDG
+protrusile
+protrusion/MS
+protrusive/PY
+protuberance/S
+protuberant
+Proudhon/M
+proud/TRY
+Proust/M
+provabilities
+provability's
+provability/U
+provableness/M
+provable/P
+provably
+prov/DRGZB
+proved/U
+prove/ESDAG
+provenance/SM
+Provençal
+Provencals
+Provence/M
+provender/SDG
+provenience/SM
+provenly
+proverb/DG
+proverbial/Y
+Proverbs/M
+prover/M
+provide/DRSBGZ
+provided/U
+providence/SM
+Providence/SM
+providential/Y
+provident/Y
+provider/M
+province/SM
+provincialism/SM
+provincial/SY
+provisional/YS
+provisioner/M
+provision/R
+proviso/MS
+provocateur/S
+provocativeness/SM
+provocative/P
+provoked/U
+provoke/GZDRS
+provoking/Y
+provolone/SM
+Provo/M
+provost/MS
+prowess/SM
+prowler/M
+prowl/RDSZG
+prow/TRMS
+proximal/Y
+proximateness/M
+proximate/PY
+proximity/MS
+Proxmire/M
+proxy/SM
+Prozac
+prude/MS
+Prudence/M
+prudence/SM
+Prudential/M
+prudential/SY
+prudent/Y
+prudery/MS
+Prudi/M
+prudishness/SM
+prudish/YP
+Prudy/M
+Prue/M
+Pruitt/M
+Pru/M
+prune/DSRGZM
+pruner/M
+prurience/MS
+prurient/Y
+Prussia/M
+Prussian/S
+prussic
+Prut/M
+Pryce/M
+pry/DRSGTZ
+pryer's
+prying/Y
+P's
+PS
+p's/A
+psalmist/SM
+psalm/SGDM
+Psalms/M
+psalter
+Psalter/SM
+psaltery/MS
+psephologist/M
+pseudonymous
+pseudonym/SM
+pseudopod
+pseudo/S
+pseudoscience/S
+pshaw/SDG
+psi/S
+psittacoses
+psittacosis/M
+psoriases
+psoriasis/M
+psst/S
+PST
+psychedelically
+psychedelic/S
+psyche/M
+Psyche/M
+psychiatric
+psychiatrist/SM
+psychiatry/MS
+psychical/Y
+psychic/MS
+psychoacoustic/S
+psychoacoustics/M
+psychoactive
+psychoanalysis/M
+psychoanalyst/S
+psychoanalytic
+psychoanalytical
+psychoanalyze/SDG
+psychobabble/S
+psychobiology/M
+psychocultural
+psychodrama/MS
+psychogenic
+psychokinesis/M
+psycholinguistic/S
+psycholinguistics/M
+psycholinguists
+psychological/Y
+psychologist/MS
+psychology/MS
+psychometric/S
+psychometrics/M
+psychometry/M
+psychoneuroses
+psychoneurosis/M
+psychopathic/S
+psychopath/M
+psychopathology/M
+psychopaths
+psychopathy/SM
+psychophysical/Y
+psychophysic/S
+psychophysics/M
+psychophysiology/M
+psychosis/M
+psycho/SM
+psychosocial/Y
+psychosomatic/S
+psychosomatics/M
+psychos/S
+psychotherapeutic/S
+psychotherapist/MS
+psychotherapy/SM
+psychotically
+psychotic/S
+psychotropic/S
+psychs
+psych/SDG
+PT
+PTA
+Ptah/M
+ptarmigan/MS
+pt/C
+pterodactyl/SM
+Pt/M
+PTO
+Ptolemaic
+Ptolemaists
+Ptolemy/MS
+ptomaine/MS
+Pu
+pubbed
+pubbing
+pubertal
+puberty/MS
+pubes
+pubescence/S
+pubescent
+pubic
+pubis/M
+publican/AMS
+publication/AMS
+publicist/SM
+publicity/SM
+publicized/U
+publicize/SDG
+publicness/M
+publics/A
+public/YSP
+publishable/U
+published/UA
+publisher/ASM
+publishes/A
+publishing/M
+publish/JDRSBZG
+pub/MS
+Puccini/M
+puce/SM
+pucker/DG
+Puckett/M
+puck/GZSDRM
+puckishness/S
+puckish/YP
+Puck/M
+pudding/MS
+puddle/JMGRSD
+puddler/M
+puddling/M
+puddly
+pudenda
+pudendum/M
+pudginess/SM
+pudgy/PRT
+Puebla/M
+Pueblo/MS
+pueblo/SM
+puerile/Y
+puerility/SM
+puerperal
+puers
+Puerto/M
+puffball/SM
+puffer/M
+puffery/M
+puffiness/S
+puffin/SM
+Puff/M
+puff/SGZDRM
+puffy/PRT
+Puget/M
+pugged
+pugging
+Pugh/M
+pugilism/SM
+pugilistic
+pugilist/S
+pug/MS
+pugnaciousness/MS
+pugnacious/YP
+pugnacity/SM
+puissant/Y
+puke/GDS
+pukka
+Pulaski/SM
+pulchritude/SM
+pulchritudinous/M
+pule/GDS
+Pulitzer/SM
+pullback/S
+pull/DRGZSJ
+pullet/SM
+pulley/SM
+Pullman/MS
+pullout/S
+pullover/SM
+pulmonary
+pulpiness/S
+pulpit/MS
+pulp/MDRGS
+pulpwood/MS
+pulpy/PTR
+pulsar/MS
+pulsate/NGSDX
+pulsation/M
+pulse/ADSG
+pulser
+pulse's
+pulverable
+pulverization/MS
+pulverized/U
+pulverize/GZSRD
+pulverizer/M
+pulverizes/UA
+puma/SM
+pumice/SDMG
+pummel/SDG
+pumpernickel/SM
+pump/GZSMDR
+pumping/M
+pumpkin/MS
+punchbowl/M
+punched/U
+puncheon/MS
+puncher/M
+punch/GRSDJBZ
+punchline/S
+Punch/M
+punchy/RT
+punctilio/SM
+punctiliousness/SM
+punctilious/PY
+punctualities
+punctuality/UM
+punctualness/M
+punctual/PY
+punctuate/SDXNG
+punctuational
+punctuation/M
+puncture/SDMG
+punditry/S
+pundit/SM
+pungency/MS
+pungent/Y
+Punic
+puniness/MS
+punished/U
+punisher/M
+punishment/MS
+punish/RSDGBL
+punitiveness/M
+punitive/YP
+Punjabi/M
+Punjab/M
+punk/TRMS
+punky/PRS
+pun/MS
+punned
+punning
+punster/SM
+punter/M
+punt/GZMDRS
+puny/PTR
+pupae
+pupal
+pupa/M
+pupate/NGSD
+pupillage/M
+pupil/SM
+pup/MS
+pupped
+puppeteer/SM
+puppetry/MS
+puppet/SM
+pupping
+puppy/GSDM
+puppyish
+purblind
+Purcell/M
+purchasable
+purchase/GASD
+purchaser/MS
+purdah/M
+purdahs
+Purdue/M
+purebred/S
+puree/DSM
+pureeing
+pureness/MS
+pure/PYTGDR
+purgation/M
+purgative/MS
+purgatorial
+purgatory/SM
+purge/GZDSR
+purger/M
+purify/GSRDNXZ
+Purim/SM
+Purina/M
+purine/SM
+purism/MS
+puristic
+purist/MS
+puritanic
+puritanical/Y
+Puritanism/MS
+puritanism/S
+puritan/SM
+Puritan/SM
+purity/SM
+purlieu/SM
+purl/MDGS
+purloin/DRGS
+purloiner/M
+purple/MTGRSD
+purplish
+purport/DRSZG
+purported/Y
+purposefulness/S
+purposeful/YP
+purposelessness/M
+purposeless/PY
+purpose/SDVGYM
+purposiveness/M
+purposive/YP
+purr/DSG
+purring/Y
+purse/DSRGZM
+purser/M
+pursuance/MS
+pursuant
+pursuer/M
+pursue/ZGRSD
+pursuit/MS
+purulence/MS
+purulent
+Purus
+purveyance/MS
+purvey/DGS
+purveyor/MS
+purview/SM
+Pusan/M
+Pusey/M
+pushbutton/S
+pushcart/SM
+pushchair/SM
+pushdown
+push/DSRBGZ
+pusher/M
+pushily
+pushiness/MS
+Pushkin/M
+pushover/SM
+Pushtu/M
+pushy/PRT
+pusillanimity/MS
+pusillanimous/Y
+pus/SM
+puss/S
+pussycat/S
+pussyfoot/DSG
+pussy/TRSM
+pustular
+pustule/MS
+putative/Y
+Putin/M
+put/IS
+Putnam/M
+Putnem/M
+putout/S
+putrefaction/SM
+putrefactive
+putrefy/DSG
+putrescence/MS
+putrescent
+putridity/M
+putridness/M
+putrid/YP
+putsch/S
+putted/I
+puttee/MS
+putter/RDMGZ
+putting/I
+putt/SGZMDR
+puttying/M
+putty/SDMG
+puzzle/JRSDZLG
+puzzlement/MS
+puzzler/M
+PVC
+pvt
+Pvt/M
+PW
+PX
+p/XTGJ
+Pygmalion/M
+pygmy/SM
+Pygmy/SM
+Pyhrric/M
+pyknotic
+Pyle/M
+pylon/SM
+pylori
+pyloric
+pylorus/M
+Pym/M
+Pynchon/M
+Pyongyang/M
+pyorrhea/SM
+Pyotr/M
+pyramidal/Y
+pyramid/GMDS
+pyre/MS
+Pyrenees
+Pyrex/SM
+pyridine/M
+pyrimidine/SM
+pyrite/MS
+pyrolysis/M
+pyrolyze/RSM
+pyromaniac/SM
+pyromania/MS
+pyrometer/MS
+pyrometry/M
+pyrophosphate/M
+pyrotechnical
+pyrotechnic/S
+pyrotechnics/M
+pyroxene/M
+pyroxenite/M
+Pyrrhic
+Pythagoras/M
+Pythagorean/S
+Pythias
+Python/M
+python/MS
+pyx/MDSG
+q
+Q
+QA
+Qaddafi/M
+Qantas/M
+Qatar/M
+QB
+QC
+QED
+Qingdao
+Qiqihar/M
+QM
+Qom/M
+qr
+q's
+Q's
+qt
+qty
+qua
+Quaalude/M
+quackery/MS
+quackish
+quack/SDG
+quadded
+quadding
+quadrangle/MS
+quadrangular/M
+quadrant/MS
+quadraphonic/S
+quadrapole
+quadratical/Y
+quadratic/SM
+quadrature/MS
+quadrennial/SY
+quadrennium/MS
+quadric
+quadriceps/SM
+quadrilateral/S
+quadrille/XMGNSD
+quadrillion/MH
+quadripartite/NY
+quadriplegia/SM
+quadriplegic/SM
+quadrivia
+quadrivium/M
+quadrupedal
+quadruped/MS
+quadruple/GSD
+quadruplet/SM
+quadruplicate/GDS
+quadruply/NX
+quadrupole
+quad/SM
+quadword/MS
+quaffer/M
+quaff/SRDG
+quagmire/DSMG
+quahog/MS
+quail/GSDM
+quaintness/MS
+quaint/PTYR
+quake/GZDSR
+Quakeress/M
+Quakerism/S
+Quaker/SM
+quaky/RT
+qualification/ME
+qualified/UY
+qualifier/SM
+qualify/EGXSDN
+qualitative/Y
+quality/MS
+qualmish
+qualm/SM
+quandary/MS
+quangos
+quanta/M
+Quantico/M
+quantifiable/U
+quantified/U
+quantifier/M
+quantify/GNSRDZX
+quantile/S
+quantitativeness/M
+quantitative/PY
+quantity/MS
+quantization/MS
+quantizer/M
+quantize/ZGDRS
+quantum/M
+quarantine/DSGM
+quark/SM
+quarreler/M
+quarrellings
+quarrelsomeness/MS
+quarrelsome/PY
+quarrel/SZDRMG
+quarrier/M
+quarryman/M
+quarrymen
+quarry/RSDGM
+quarterback/SGMD
+quarterdeck/MS
+quarterer/M
+quarterfinal/MS
+quartering/M
+quarterly/S
+quartermaster/MS
+quarter/MDRYG
+quarterstaff/M
+quarterstaves
+quartet/SM
+quartic/S
+quartile/SM
+quarto/SM
+quart/RMSZ
+quartzite/M
+quartz/SM
+quasar/SM
+quash/GSD
+quasi
+quasilinear
+Quasimodo/M
+Quaternary
+quaternary/S
+quaternion/SM
+quatrain/SM
+quaver/GDS
+quavering/Y
+quavery
+Quayle/M
+quayside/M
+quay/SM
+queasily
+queasiness/SM
+queasy/TRP
+Quebec/M
+Quechua/M
+Queenie/M
+queenly/RT
+queen/SGMDY
+Queensland/M
+Queen/SM
+queerness/S
+queer/STGRDYP
+queller/M
+quell/SRDG
+Que/M
+quenchable/U
+quenched/U
+quencher/M
+quench/GZRSDB
+quenchless
+Quentin/M
+Quent/M
+Querida/M
+quern/M
+querulousness/S
+querulous/YP
+query/MGRSD
+quested/A
+quester/AS
+quester's
+quest/FSIM
+questing
+questionableness/M
+questionable/P
+questionably/U
+questioned/UA
+questioner/M
+questioning/UY
+questionnaire/MS
+question/SMRDGBZJ
+quests/A
+Quetzalcoatl/M
+queued/C
+queue/GZMDSR
+queuer/M
+queues/C
+queuing/C
+Quezon/M
+quibble/GZRSD
+quibbler/M
+quiche/SM
+quicken/RDG
+quickie/MS
+quicklime/SM
+quickness/MS
+quick/RNYTXPS
+quicksand/MS
+quicksilver/GDMS
+quickstep/SM
+quid/SM
+quiesce/D
+quiescence/MS
+quiescent/YP
+quieted/E
+quieten/SGD
+quieter/E
+quieter's
+quieting/E
+quietly/E
+quietness/MS
+quiets/E
+quietude/IEMS
+quietus/MS
+quiet/UTGPSDRY
+Quillan/M
+quill/GSDM
+Quill/M
+quilter/M
+quilting/M
+quilt/SZJGRDM
+quincentenary/M
+quince/SM
+Quincey/M
+quincy/M
+Quincy/M
+quinine/MS
+Quinlan/M
+Quinn/M
+quinquennial/Y
+quinsy/SM
+Quinta/M
+Quintana/M
+quintessence/SM
+quintessential/Y
+quintet/SM
+quintic
+quintile/SM
+Quintilian/M
+Quintilla/M
+quintillion/MH
+quintillionth/M
+Quintina/M
+Quintin/M
+Quint/M
+quint/MS
+Quinton/M
+quintuple/SDG
+quintuplet/MS
+Quintus/M
+quip/MS
+quipped
+quipper
+quipping
+quipster/SM
+quired/AI
+quire/MDSG
+quires/AI
+Quirinal/M
+quiring/IA
+quirkiness/SM
+quirk/SGMD
+quirky/PTR
+quirt/SDMG
+Quisling/M
+quisling/SM
+quitclaim/GDMS
+quit/DGS
+quite/SADG
+Quito/M
+quittance/SM
+quitter/SM
+quitting
+quiver/GDS
+quivering/Y
+quivery
+Quixote/M
+quixotic
+quixotically
+Quixotism/M
+quiz/M
+quizzed
+quizzer/SM
+quizzes
+quizzical/Y
+quizzing
+quo/H
+quoin/SGMD
+quoit/GSDM
+quondam
+quonset
+Quonset
+quorate/I
+quorum/MS
+quotability/S
+quota/MS
+quotation/SM
+quoter/M
+quote/UGSD
+quot/GDRB
+quotidian/S
+quotient/SM
+qwerty
+qwertys
+Rabat/M
+rabbet/GSMD
+Rabbi/M
+rabbi/MS
+rabbinate/MS
+rabbinic
+rabbinical/Y
+rabbiter/M
+rabbit/MRDSG
+rabble/GMRSD
+rabbler/M
+Rabelaisian
+Rabelais/M
+rabidness/SM
+rabid/YP
+rabies
+Rabi/M
+Rabin/M
+rabis
+Rab/M
+raccoon/SM
+racecourse/MS
+racegoers
+racehorse/SM
+raceme/MS
+race/MZGDRSJ
+racer/M
+racetrack/SMR
+raceway/SM
+Rachael/M
+Rachele/M
+Rachelle/M
+Rachel/M
+Rachmaninoff/M
+racialism/MS
+racialist/MS
+racial/Y
+racily
+Racine/M
+raciness/MS
+racism/S
+racist/MS
+racketeer/MDSJG
+racket/SMDG
+rackety
+rack/GDRMS
+raconteur/SM
+racoon's
+racquetball/S
+racquet's
+racy/RTP
+radarscope/MS
+radar/SM
+Radcliffe/M
+radded
+radder
+raddest
+Raddie/M
+radding
+Raddy/M
+radial/SY
+radiance/SM
+radian/SM
+radiant/YS
+radiate/XSDYVNG
+radiation/M
+radiative/Y
+radiator/MS
+radicalism/MS
+radicalization/S
+radicalize/GSD
+radicalness/M
+radical/SPY
+radices's
+radii/M
+radioactive/Y
+radioactivity/MS
+radioastronomical
+radioastronomy
+radiocarbon/MS
+radiochemical/Y
+radiochemistry/M
+radiogalaxy/S
+radiogram/SM
+radiographer/MS
+radiographic
+radiography/MS
+radioisotope/SM
+radiologic
+radiological/Y
+radiologist/MS
+radiology/MS
+radioman/M
+radiomen
+radiometer/SM
+radiometric
+radiometry/MS
+radionics
+radionuclide/M
+radiopasteurization
+radiophone/MS
+radiophysics
+radioscopy/SM
+radio/SMDG
+radiosonde/SM
+radiosterilization
+radiosterilized
+radiotelegraph
+radiotelegraphs
+radiotelegraphy/MS
+radiotelephone/SM
+radiotherapist/SM
+radiotherapy/SM
+radish/MS
+radium/MS
+radius/M
+radix/SM
+Rad/M
+radon/SM
+rad/S
+Raeann/M
+Rae/M
+RAF
+Rafaela/M
+Rafaelia/M
+Rafaelita/M
+Rafaellle/M
+Rafaello/M
+Rafael/M
+Rafa/M
+Rafe/M
+Raffaello/M
+Raffarty/M
+Rafferty/M
+raffia/SM
+raffishness/SM
+raffish/PY
+raffle/MSDG
+Raff/M
+Rafi/M
+Raf/M
+rafter/DM
+raft/GZSMDR
+raga/MS
+ragamuffin/MS
+ragbag/SM
+rage/MS
+raggedness/SM
+ragged/PRYT
+raggedy/TR
+ragging
+rag/GSMD
+raging/Y
+raglan/MS
+Ragnar/M
+Ragnarök
+ragout/SMDG
+ragtag/MS
+ragtime/MS
+ragweed/MS
+ragwort/M
+Rahal/M
+rah/DG
+Rahel/M
+rahs
+raider/M
+raid/MDRSGZ
+railbird/S
+rail/CDGS
+railer/SM
+railhead/SM
+railing/MS
+raillery/MS
+railroader/M
+railroading/M
+railroad/SZRDMGJ
+rail's
+railwaymen
+railway/MS
+raiment/SM
+Raimondo/M
+Raimund/M
+Raimundo/M
+Raina/M
+rainbow/MS
+raincloud/S
+raincoat/SM
+raindrop/SM
+Raine/MR
+Rainer/M
+rainfall/SM
+rainforest's
+rain/GSDM
+Rainier/M
+rainless
+rainmaker/SM
+rainmaking/MS
+rainproof/GSD
+rainstorm/SM
+rainwater/MS
+rainy/RT
+raise/DSRGZ
+raiser/M
+raising/M
+raisin/MS
+rajah/M
+rajahs
+Rajive/M
+raj/M
+Rakel/M
+rake/MGDRS
+raker/M
+rakishness/MS
+rakish/PY
+Raleigh/M
+Ralf/M
+Ralina/M
+rally/GSD
+Ralph/M
+Ralston/M
+Ra/M
+Ramada/M
+Ramadan/SM
+Ramakrishna/M
+Rama/M
+Raman/M
+Ramayana/M
+ramble/JRSDGZ
+rambler/M
+rambling/Y
+Rambo/M
+rambunctiousness/S
+rambunctious/PY
+ramekin/SM
+ramie/MS
+ramification/M
+ramify/XNGSD
+Ramirez/M
+Ramiro/M
+ramjet/SM
+Ram/M
+rammed
+ramming
+Ramo/MS
+Ramona/M
+Ramonda/M
+Ramon/M
+rampage/SDG
+rampancy/S
+rampant/Y
+rampart/SGMD
+ramp/GMDS
+ramrodded
+ramrodding
+ramrod/MS
+RAM/S
+Ramsay/M
+Ramses/M
+Ramsey/M
+ramshackle
+ram/SM
+rams/S
+ran/A
+Rana/M
+Rancell/M
+Rance/M
+rancher/M
+rancho/SM
+ranch/ZRSDMJG
+rancidity/MS
+rancidness/SM
+rancid/P
+rancorous/Y
+rancor/SM
+Randall/M
+Randal/M
+Randa/M
+Randee/M
+Randell/M
+Randene/M
+Randie/M
+Randi/M
+randiness/S
+Rand/M
+rand/MDGS
+Randolf/M
+Randolph/M
+randomization/SM
+randomize/SRDG
+randomness/SM
+random/PYS
+Randy/M
+randy/PRST
+Ranee/M
+ranee/SM
+ranged/C
+rangeland/S
+ranger/M
+ranges/C
+range/SM
+rang/GZDR
+ranginess/S
+ranging/C
+Rangoon/M
+rangy/RPT
+Rania/M
+Ranice/M
+Ranier/M
+Rani/MR
+Ranique/M
+rani's
+ranked/U
+ranker/M
+rank/GZTYDRMPJS
+Rankine/M
+ranking/M
+Rankin/M
+rankle/SDG
+rankness/MS
+Ranna/M
+ransacker/M
+ransack/GRDS
+Ransell/M
+ransomer/M
+Ransom/M
+ransom/ZGMRDS
+ranter/M
+rant/GZDRJS
+ranting/Y
+Raoul/M
+rapaciousness/MS
+rapacious/YP
+rapacity/MS
+rapeseed/M
+rape/SM
+Raphaela/M
+Raphael/M
+rapidity/MS
+rapidness/S
+rapid/YRPST
+rapier/SM
+rapine/SM
+rapist/MS
+rap/MDRSZG
+rapped
+rappelled
+rappelling
+rappel/S
+rapper/SM
+rapping/M
+rapporteur/SM
+rapport/SM
+rapprochement/SM
+rapscallion/MS
+raptness/S
+rapture/MGSD
+rapturousness/M
+rapturous/YP
+rapt/YP
+Rapunzel/M
+Raquela/M
+Raquel/M
+rarebit/MS
+rarefaction/MS
+rarefy/GSD
+rareness/MS
+rare/YTPGDRS
+rarity/SM
+Rasalgethi/M
+Rasalhague/M
+rascal/SMY
+rasher/M
+rashness/S
+rash/PZTYSR
+Rasia/M
+Rasla/M
+Rasmussen/M
+raspberry/SM
+rasper/M
+rasping/Y
+rasp/SGJMDR
+Rasputin/M
+raspy/RT
+Rastaban/M
+Rastafarian/M
+raster/MS
+Rastus/M
+ratchet/MDSG
+rateable
+rated/U
+rate/KNGSD
+ratepayer/SM
+rater/M
+rate's
+Ratfor/M
+rather
+Rather/M
+rathskeller/SM
+ratifier/M
+ratify/ZSRDGXN
+rating/M
+ratiocinate/VNGSDX
+ratiocination/M
+ratio/MS
+rationale/SM
+rationalism/SM
+rationalistic
+rationalist/S
+rationality/MS
+rationalization/SM
+rationalizer/M
+rationalize/ZGSRD
+rationalness/M
+rational/YPS
+ration/DSMG
+Ratliff/M
+ratlike
+ratline/SM
+rat/MDRSJZGB
+rattail
+rattan/MS
+ratted
+ratter/MS
+ratting
+rattlebrain/DMS
+rattle/RSDJGZ
+rattlesnake/MS
+rattletrap/MS
+rattling/Y
+rattly/TR
+rattrap/SM
+ratty/RT
+raucousness/SM
+raucous/YP
+Raul/M
+raunchily
+raunchiness/S
+raunchy/RTP
+ravage/GZRSD
+ravager/M
+raveling/S
+Ravel/M
+ravel/UGDS
+raven/JGMRDS
+Raven/M
+ravenous/YP
+raver/M
+rave/ZGDRSJ
+Ravid/M
+Ravi/M
+ravine/SDGM
+ravioli/SM
+ravisher/M
+ravishing/Y
+ravish/LSRDZG
+ravishment/SM
+Raviv/M
+Rawalpindi/M
+rawboned
+rawhide/SDMG
+Rawley/M
+Rawlings/M
+Rawlins/M
+Rawlinson/M
+rawness/SM
+raw/PSRYT
+Rawson/M
+Rayburn/M
+Raychel/M
+Raye/M
+ray/GSMD
+Rayleigh/M
+Ray/M
+Raymond/M
+Raymondville/M
+Raymund/M
+Raymundo/M
+Rayna/M
+Raynard/M
+Raynell/M
+Rayner/M
+Raynor/M
+rayon/SM
+Rayshell/M
+Raytheon/M
+raze/DRSG
+razer/M
+razorback/SM
+razorblades
+razor/MDGS
+razz/GDS
+razzmatazz/S
+Rb
+RBI/S
+RC
+RCA
+rcpt
+RCS
+rd
+RD
+RDA
+Rd/M
+reabbreviate
+reachability
+reachable/U
+reachably
+reached/U
+reacher/M
+reach/GRB
+reacquisition
+reactant/SM
+reacted/U
+reaction
+reactionary/SM
+reactivity
+readability/MS
+readable/P
+readably
+readdress/G
+Reade/M
+reader/M
+readership/MS
+Read/GM
+readied
+readies
+readily
+readinesses
+readiness/UM
+reading/M
+Reading/M
+read/JGZBR
+readopt/G
+readout/MS
+reads/A
+readying
+ready/TUPR
+Reagan/M
+Reagen/M
+realisms
+realism's
+realism/U
+realistically/U
+realistic/U
+realist/SM
+reality/USM
+realizability/MS
+realizableness/M
+realizable/SMP
+realizably/S
+realization/MS
+realized/U
+realize/JRSDBZG
+realizer/M
+realizes/U
+realizing/MY
+realm/M
+realness/S
+realpolitik/SM
+real/RSTP
+realtor's
+Realtor/S
+realty/SM
+Rea/M
+reamer/M
+ream/MDRGZ
+Reamonn/M
+reanimate
+reaper/M
+reappraise/G
+reap/SGZ
+rear/DRMSG
+rearguard/MS
+rearmost
+rearrange/L
+rearward/S
+reasonableness/SMU
+reasonable/UP
+reasonably/U
+Reasoner/M
+reasoner/SM
+reasoning/MS
+reasonless
+reasons
+reason/UBDMG
+reassess/GL
+reassuringly/U
+reattach/GSL
+reawakening/M
+Reba/M
+rebate/M
+Rebbecca/M
+Rebeca/M
+Rebecca's
+Rebecka/M
+Rebekah/M
+Rebeka/M
+Rebekkah/M
+rebeller
+rebellion/SM
+rebelliousness/MS
+rebellious/YP
+rebel/MS
+Rebe/M
+rebid
+rebidding
+rebind/G
+rebirth
+reboil/G
+rebook
+reboot/ZR
+rebound/G
+rebroadcast/MG
+rebuke/RSDG
+rebuking/Y
+rebus
+rebuttal/SM
+rebutting
+rec
+recalcitrance/SM
+recalcitrant/S
+recalibrate/N
+recantation/S
+recant/G
+recap
+recappable
+recapping
+recast/G
+recd
+rec'd
+recede
+receipt/SGDM
+receivable/S
+received/U
+receiver/M
+receivership/SM
+receive/ZGRSDB
+recency/M
+recension/M
+recentness/SM
+recent/YPT
+receptacle/SM
+receptionist/MS
+reception/MS
+receptiveness/S
+receptive/YP
+receptivity/S
+receptor/MS
+recessional/S
+recessionary
+recessiveness/M
+recessive/YPS
+recess/SDMVG
+rechargeable
+recheck/G
+recherché
+recherches
+recidivism/MS
+recidivist/MS
+Recife/M
+recipe/MS
+recipiency
+recipient/MS
+reciprocal/SY
+reciprocate/NGXVDS
+reciprocation/M
+reciprocity/MS
+recitalist/S
+recital/MS
+recitative/MS
+reciter/M
+recite/ZR
+recked
+recking
+recklessness/S
+reckless/PY
+reckoner/M
+reckoning/M
+reckon/SGRDJ
+reclaim/B
+reclamation/SM
+recliner/M
+recline/RSDZG
+recluse/MVNS
+reclusion/M
+recode/G
+recognizability
+recognizable/U
+recognizably
+recognize/BZGSRD
+recognizedly/S
+recognized/U
+recognizer/M
+recognizingly/S
+recognizing/UY
+recoilless
+recoinage
+recolor/GD
+recombinant
+recombine
+recommended/U
+recompense/GDS
+recompute/B
+reconciled/U
+reconciler/M
+reconcile/SRDGB
+reconditeness/M
+recondite/YP
+reconfigurability
+reconfigure/R
+reconnaissance/MS
+reconnect/R
+reconnoiter/GSD
+reconquer/G
+reconsecrate
+reconstitute
+reconstructed/U
+Reconstruction/M
+reconsult/G
+recontact/G
+recontaminate/N
+recontribute
+recook/G
+recopy/G
+recorded/AU
+records/A
+record/ZGJ
+recourse
+recoverability
+recoverable/U
+recover/B
+recovery/MS
+recreant/S
+recreational
+recriminate/GNVXDS
+recrimination/M
+recriminatory
+recross/G
+recrudesce/GDS
+recrudescence/MS
+recrudescent
+recruiter/M
+recruitment/MS
+recruit/ZSGDRML
+recrystallize
+rectal/Y
+rectangle/SM
+rectangular/Y
+recta's
+rectifiable
+rectification/M
+rectifier/M
+rectify/DRSGXZN
+rectilinear/Y
+rectitude/MS
+recto/MS
+rector/SM
+rectory/MS
+rectum/SM
+recumbent/Y
+recuperate/VGNSDX
+recuperation/M
+recur
+recurrence/MS
+recurrent
+recurse/NX
+recursion/M
+recusant/M
+recuse
+recyclable/S
+recycle/BZ
+redact/DGS
+redaction/SM
+redactor/MS
+redbird/SM
+redbreast/SM
+redbrick/M
+redbud/M
+redcap/MS
+redcoat/SM
+redcurrant/M
+redden/DGS
+redder
+reddest
+redding
+reddish/P
+Redd/M
+redeclaration
+redecorate
+redeemable/U
+redeem/BRZ
+redeemed/U
+redeemer/M
+Redeemer/M
+redemptioner/M
+redemption/RMS
+redemptive
+redeposit/M
+redetermination
+Redford/M
+Redgrave/M
+redhead/DRMS
+Redhook/M
+redial/G
+redirect/G
+redirection
+redlining/S
+Redmond/M
+redneck/SMD
+redness/MS
+redo/G
+redolence/MS
+redolent
+Redondo/M
+redouble/S
+redoubtably
+redound/GDS
+red/PYS
+redshift/S
+redskin/SM
+Redstone/M
+reduced/U
+reducer/M
+reduce/RSDGZ
+reducibility/M
+reducible
+reducibly
+reductionism/M
+reductionist/S
+reduction/SM
+reduct/V
+redundancy/SM
+redundant/Y
+redwood/SM
+redye
+redyeing
+Reeba/M
+Reebok/M
+Reece/M
+reecho/G
+reed/GMDR
+reediness/SM
+reeding/M
+Reed/M
+Reedville/M
+reedy/PTR
+reefer/M
+reef/GZSDRM
+reeker/M
+reek/GSR
+reeler/M
+reel's
+reel/USDG
+Ree/MDS
+Reena/M
+reenforcement
+reentrant
+Reese/M
+reestimate/M
+Reeta/M
+Reeva/M
+reeve/G
+Reeves
+reexamine
+refection/SM
+refectory/SM
+refer/B
+refereed/U
+refereeing
+referee/MSD
+reference/CGSRD
+referenced/U
+reference's
+referencing/U
+referendum/MS
+referentiality
+referential/YM
+referent/SM
+referral/SM
+referred
+referrer/S
+referring
+reffed
+reffing
+refile
+refinance
+refined/U
+refine/LZ
+refinement/MS
+refinish/G
+refit
+reflectance/M
+reflected/U
+reflectional
+reflection/SM
+reflectiveness/M
+reflective/YP
+reflectivity/M
+reflector/MS
+reflect/SDGV
+reflexion/MS
+reflexiveness/M
+reflexive/PSY
+reflexivity/M
+reflex/YV
+reflooring
+refluent
+reflux/G
+refocus/G
+refold/G
+reforestation
+reforge/G
+reformatory/SM
+reform/B
+reformed/U
+reformer/M
+reformism/M
+reformist/S
+refract/DGVS
+refractiveness/M
+refractive/PY
+refractometer/MS
+refractoriness/M
+refractory/PS
+refrain/DGS
+refreshed/U
+refreshing/Y
+refresh/LB
+refreshment/MS
+refrigerant/MS
+refrigerated/U
+refrigerate/XDSGN
+refrigeration/M
+refrigerator/MS
+refrozen
+refry/GS
+refugee/MS
+refuge/SDGM
+Refugio/M
+refulgence/SM
+refulgent
+refund/B
+refunder/M
+refurbish/L
+refurbishment/S
+refusal/SM
+refuse/R
+refuser/M
+refutation/MS
+refute/GZRSDB
+refuter/M
+ref/ZS
+reg
+regale/L
+regalement/S
+regal/GYRD
+regalia/M
+Regan/M
+regard/EGDS
+regardless/PY
+regather/G
+regatta/MS
+regency/MS
+regeneracy/MS
+regenerately
+regenerateness/M
+regenerate/U
+Regen/M
+reggae/SM
+Reggie/M
+Reggi/MS
+Reggy/M
+regicide/SM
+regime/MS
+regimen/MS
+regimental/S
+regimentation/MS
+regiment/SDMG
+Reginae
+Reginald/M
+Regina/M
+Reginauld/M
+Regine/M
+regionalism/MS
+regional/SY
+region/SM
+Regis/M
+register's
+register/UDSG
+registrable
+registrant/SM
+registrar/SM
+registration/AM
+registrations
+registry/MS
+Reg/MN
+regnant
+Regor/M
+regress/DSGV
+regression/MS
+regressiveness/M
+regressive/PY
+regressors
+regretfulness/M
+regretful/PY
+regret/S
+regrettable
+regrettably
+regretted
+regretting
+reground
+regroup/G
+regrow/G
+regularity/MS
+regularization/MS
+regularize/SDG
+regular/YS
+regulate/CSDXNG
+regulated/U
+regulation/M
+regulative
+regulator/SM
+regulatory
+Regulus/M
+regurgitate/XGNSD
+regurgitation/M
+rehabbed
+rehabbing
+rehabilitate/SDXVGN
+rehabilitation/M
+rehab/S
+rehang/G
+rehear/GJ
+rehearsal/SM
+rehearse
+rehearsed/U
+rehearser/M
+rehears/R
+reheat/G
+reheating/M
+Rehnquist
+rehydrate
+Reichenberg/M
+Reich/M
+Reichstags
+Reichstag's
+Reidar/M
+Reider/M
+Reid/MR
+reign/MDSG
+Reiko/M
+Reilly/M
+reimburse/GSDBL
+reimbursement/MS
+Reinald/M
+Reinaldo/MS
+Reina/M
+reindeer/M
+Reine/M
+reinforced/U
+reinforce/GSRDL
+reinforcement/MS
+reinforcer/M
+rein/GDM
+Reinhard/M
+Reinhardt/M
+Reinhold/M
+Reinold/M
+reinstate/L
+reinstatement/MS
+reinsurance
+Reinwald/M
+reissue
+REIT
+reiterative/SP
+rejecter/M
+rejecting/Y
+rejection/SM
+rejector/MS
+reject/RDVGS
+rejigger
+rejoice/RSDJG
+rejoicing/Y
+rejoinder/SM
+rejuvenate/NGSDX
+rejuvenatory
+relapse
+relatedly
+relatedness/MS
+related/U
+relater/M
+relate/XVNGSZ
+relational/Y
+relation/M
+relationship/MS
+relativeness/M
+relative/SPY
+relativism/M
+relativistic
+relativistically
+relativist/MS
+relativity/MS
+relator's
+relaxant/SM
+relaxation/MS
+relaxedness/M
+relaxed/YP
+relax/GZD
+relaxing/Y
+relay/GDM
+relearn/G
+releasable/U
+release/B
+released/U
+relenting/U
+relentlessness/SM
+relentless/PY
+relent/SDG
+relevance/SM
+relevancy/MS
+relevant/Y
+reliability/UMS
+reliables
+reliable/U
+reliably/U
+reliance/MS
+reliant/Y
+relicense/R
+relic/MS
+relict/C
+relict's
+relief/M
+relievedly
+relieved/U
+reliever/M
+relieve/RSDZG
+religionists
+religion/SM
+religiosity/M
+religiousness/MS
+religious/PY
+relink/G
+relinquish/GSDL
+relinquishment/SM
+reliquary/MS
+relish/GSD
+relive/GB
+reload/GR
+relocate/B
+reluctance/MS
+reluctant/Y
+rel/V
+rely/DG
+rem
+Re/M
+remade/S
+remainder/SGMD
+remain/GD
+remake/M
+remand/DGS
+remap
+remapping
+remarkableness/S
+remarkable/U
+remarkably
+remark/BG
+remarked/U
+Remarque/M
+rematch/G
+Rembrandt/M
+remeasure/D
+remediableness/M
+remediable/P
+remedy/SDMG
+remembered/U
+rememberer/M
+remember/GR
+remembrance/MRS
+remembrancer/M
+Remington/M
+reminisce/GSD
+reminiscence/SM
+reminiscent/Y
+remissness/MS
+remiss/YP
+remit/S
+remittance/MS
+remitted
+remitting/U
+Rem/M
+remnant/MS
+remodel/G
+remolding
+remonstrant/MS
+remonstrate/SDXVNG
+remonstration/M
+remonstrative/Y
+remorsefulness/M
+remorseful/PY
+remorselessness/MS
+remorseless/YP
+remorse/SM
+remoteness/MS
+remote/RPTY
+remoulds
+removal/MS
+REM/S
+remunerated/U
+remunerate/VNGXSD
+remuneration/M
+remunerativeness/M
+remunerative/YP
+Remus/M
+Remy/M
+Renado/M
+Renae/M
+renaissance/S
+Renaissance/SM
+renal
+Renaldo/M
+Rena/M
+Renard/M
+Renascence/SM
+Renata/M
+Renate/M
+Renato/M
+renaturation
+Renaud/M
+Renault/MS
+rend
+renderer/M
+render/GJRD
+rendering/M
+rendezvous/DSMG
+rendition/GSDM
+rend/RGZS
+Renee/M
+renegade/SDMG
+renege/GZRSD
+reneger/M
+Renelle/M
+Renell/M
+Rene/M
+renewal/MS
+renew/BG
+renewer/M
+Renie/M
+rennet/MS
+Rennie/M
+rennin/SM
+Renoir/M
+Reno/M
+renounce/LGRSD
+renouncement/MS
+renouncer/M
+renovate/NGXSD
+renovation/M
+renovator/SM
+renown/SGDM
+Rensselaer/M
+rentaller
+rental/SM
+renter/M
+rent/GZMDRS
+renumber/G
+renumeration
+renunciate/VNX
+renunciation/M
+Renville/M
+reoccupy/G
+reopen/G
+reorganized/U
+repack/G
+repairable/U
+repair/BZGR
+repairer/M
+repairman/M
+repairmen
+repairs/E
+repaper
+reparable
+reparation/SM
+reparteeing
+repartee/MDS
+repartition/Z
+repast/G
+repatriate/SDXNG
+repave
+repealer/M
+repeal/GR
+repeatability/M
+repeatable/U
+repeatably
+repeated/Y
+repeater/M
+repeat/RDJBZG
+repelled
+repellent/SY
+repelling/Y
+repel/S
+repentance/SM
+repentant/SY
+repent/RDG
+repertoire/SM
+repertory/SM
+repetition
+repetitiousness/S
+repetitious/YP
+repetitiveness/MS
+repetitive/PY
+repine/R
+repiner/M
+replace/RL
+replay/GM
+replenish/LRSDG
+replenishment/S
+repleteness/MS
+replete/SDPXGN
+repletion/M
+replica/SM
+replicate/SDVG
+replicator/S
+replug
+reply/X
+Rep/M
+repopulate
+reported/Y
+reportorial/Y
+reposeful
+repose/M
+repository/MS
+reprehend/GDS
+reprehensibility/MS
+reprehensibleness/M
+reprehensible/P
+reprehensibly
+reprehension/MS
+representable/U
+representational/Y
+representativeness/M
+Representative/S
+representative/SYMP
+representativity
+represented/U
+represent/GB
+repression/SM
+repressiveness/M
+repressive/YP
+repress/V
+reprieve/GDS
+reprimand/SGMD
+reprint/M
+reprisal/MS
+reproacher/M
+reproachfulness/M
+reproachful/YP
+reproach/GRSDB
+reproaching/Y
+reprobate/N
+reprocess/G
+reproducibility/MS
+reproducible/S
+reproducibly
+reproductive/S
+reproof/G
+reprove/R
+reproving/Y
+rep/S
+reptile/SM
+reptilian/S
+Republicanism/S
+republicanism/SM
+Republican/S
+republic/M
+republish/G
+repudiate/XGNSD
+repudiation/M
+repudiator/S
+repugnance/MS
+repugnant/Y
+repulse/VNX
+repulsion/M
+repulsiveness/MS
+repulsive/PY
+reputability/SM
+reputably/E
+reputation/SM
+reputed/Y
+repute/ESB
+reputing
+requested/U
+request/G
+Requiem/MS
+requiem/SM
+require/LR
+requirement/MS
+requisiteness/M
+requisite/PNXS
+requisitioner/M
+requisition/GDRM
+requital/MS
+requited/U
+requiter/M
+requite/RZ
+reread/G
+rerecord/G
+rerouteing
+rerunning
+res/C
+rescale
+rescind/SDRG
+rescission/SM
+rescue/GZRSD
+reseal/BG
+research/MB
+reselect/G
+resemblant
+resemble/DSG
+resend/G
+resent/DSLG
+resentfulness/SM
+resentful/PY
+resentment/MS
+reserpine/MS
+reservation/MS
+reservednesses
+reservedness/UM
+reserved/UYP
+reservist/SM
+reservoir/MS
+reset/RDG
+resettle/L
+reshipping
+reshow/G
+reshuffle/M
+reside/G
+residence/MS
+residency/SM
+residential/Y
+resident/SM
+resider/M
+residua
+residual/YS
+residuary
+residue/SM
+residuum/M
+resignation/MS
+resigned/YP
+resilience/MS
+resiliency/S
+resilient/Y
+resin/D
+resinlike
+resinous
+resiny
+resistance/SM
+Resistance/SM
+resistantly
+resistants
+resistant/U
+resisted/U
+resistible
+resistibly
+resisting/U
+resistiveness/M
+resistive/PY
+resistivity/M
+resistless
+resistor/MS
+resist/RDZVGS
+resize/G
+resold
+resole/G
+resoluble
+resoluteness/MS
+resolute/PYTRV
+resolvability/M
+resolvable/U
+resolved/U
+resolvent
+resonance/SM
+resonant/YS
+resonate/DSG
+resonator/MS
+resorption/MS
+resort/R
+resound/G
+resourcefulness/SM
+resourceful/PY
+resp
+respectability/SM
+respectable/SP
+respectably
+respect/BSDRMZGV
+respected/E
+respectful/EY
+respectfulness/SM
+respecting/E
+respectiveness/M
+respective/PY
+respect's/E
+respects/E
+respell/G
+respiration/MS
+respirator/SM
+respiratory/M
+resplendence/MS
+resplendent/Y
+respondent/MS
+respond/SDRZG
+responser/M
+response/RSXMV
+responsibility/MS
+responsibleness/M
+responsible/P
+responsibly
+responsiveness/MSU
+responsive/YPU
+respray/G
+restart/B
+restate/L
+restaurant/SM
+restaurateur/SM
+rest/DRSGVM
+rested/U
+rester/M
+restfuller
+restfullest
+restfulness/MS
+restful/YP
+restitution/SM
+restiveness/SM
+restive/PY
+restlessness/MS
+restless/YP
+restorability
+Restoration/M
+restoration/MS
+restorative/PYS
+restorer/M
+restore/Z
+restrained/UY
+restraint/MS
+restrict/DVGS
+restricted/YU
+restriction/SM
+restrictively
+restrictiveness/MS
+restrictives
+restrictive/U
+restroom/SM
+restructurability
+restructure
+rest's/U
+rests/U
+restudy/M
+restyle
+resubstitute
+resultant/YS
+result/SGMD
+resume/SDBG
+resumption/MS
+resurface
+resurgence/MS
+resurgent
+resurrect/GSD
+resurrection/SM
+resurvey/G
+resuscitate/XSDVNG
+resuscitation/M
+resuscitator/MS
+retail/Z
+retainer/M
+retain/LZGSRD
+retake
+retaliate/VNGXSD
+retaliation/M
+retaliatory
+Reta/M
+retardant/SM
+retardation/SM
+retarder/M
+retard/ZGRDS
+retch/SDG
+retention/SM
+retentiveness/S
+retentive/YP
+retentivity/M
+retest/G
+Retha/M
+rethought
+reticence/S
+reticent/Y
+reticle/SM
+reticular
+reticulate/GNYXSD
+reticulation/M
+reticule/MS
+reticulum/M
+retinal/S
+retina/SM
+retinue/MS
+retiredness/M
+retiree/MS
+retire/L
+retirement/SM
+retiring/YP
+retort/GD
+retract/DG
+retractile
+retrench/L
+retrenchment/MS
+retributed
+retribution/MS
+retributive
+retrieval/SM
+retriever/M
+retrieve/ZGDRSB
+retroactive/Y
+retrofire/GMSD
+retrofit/S
+retrofitted
+retrofitting
+retroflection
+retroflex/D
+retroflexion/M
+retrogradations
+retrograde/GYDS
+retrogression/MS
+retrogressive/Y
+retrogress/SDVG
+retrorocket/MS
+retro/SM
+retrospection/MS
+retrospective/SY
+retrospect/SVGMD
+retrovirus/S
+retrovision
+retry/G
+retsina/SM
+returnable/S
+returned/U
+returnee/SM
+retype
+Reube/M
+Reuben/M
+Reub/NM
+Reunion/M
+reuse/B
+Reuters
+Reuther/M
+reutilization
+Reuven/M
+Reva/M
+revanchist
+revealed/U
+revealingly
+revealing/U
+reveal/JBG
+reveille/MS
+revelation/MS
+Revelation/MS
+revelatory
+revelry/MS
+revel/SJRDGZ
+revenge/MGSRD
+revenger/M
+revenuer/M
+revenue/ZR
+reverberant
+reverberate/XVNGSD
+reverberation/M
+revere/GSD
+Revere/M
+reverencer/M
+reverence/SRDGM
+Reverend
+reverend/SM
+reverential/Y
+reverent/Y
+reverie/SM
+reversal/MS
+reverser/M
+reverse/Y
+reversibility/M
+reversible/S
+reversibly
+reversioner/M
+reversion/R
+revers/M
+reverter/M
+revertible
+revert/RDVGS
+revet/L
+revetment/SM
+review/G
+revile/GZSDL
+revilement/MS
+reviler/M
+revise/BRZ
+revised/U
+revisionary
+revisionism/SM
+revisionist/SM
+revitalize/ZR
+revivalism/MS
+revivalist/MS
+revival/SM
+reviver/M
+revive/RSDG
+revivification/M
+revivify/X
+Revkah/M
+Revlon/M
+Rev/M
+revocable
+revoke/GZRSD
+revolter/M
+revolt/GRD
+revolting/Y
+revolutionariness/M
+revolutionary/MSP
+revolutionist/MS
+revolutionize/GDSRZ
+revolutionizer/M
+revolution/SM
+revolve/BSRDZJG
+revolver/M
+revue/MS
+revulsion/MS
+revved
+revving
+rev/ZM
+rewarded/U
+rewarding/Y
+rewarm/G
+reweave
+rewedding
+reweigh/G
+rewind/BGR
+rewire/G
+rework/G
+rexes
+Rex/M
+Reyes
+Reykjavik/M
+re/YM
+Rey/M
+Reynaldo/M
+Reyna/M
+Reynard/M
+Reynold/SM
+rezone
+Rf
+RF
+RFC
+RFD
+R/G
+rhapsodic
+rhapsodical
+rhapsodize/GSD
+rhapsody/SM
+Rhea/M
+rhea/SM
+Rheba/M
+Rhee/M
+Rheims/M
+Rheinholdt/M
+Rhenish
+rhenium/MS
+rheology/M
+rheostat/MS
+rhesus/S
+Rheta/M
+rhetorical/YP
+rhetorician/MS
+rhetoric/MS
+Rhetta/M
+Rhett/M
+rheumatically
+rheumatic/S
+rheumatics/M
+rheumatism/SM
+rheumatoid
+rheum/MS
+rheumy/RT
+Rhiamon/M
+Rhianna/M
+Rhiannon/M
+Rhianon/M
+Rhinelander/M
+Rhineland/RM
+Rhine/M
+rhinestone/SM
+rhinitides
+rhinitis/M
+rhinoceros/MS
+rhino/MS
+rhinotracheitis
+rhizome/MS
+Rh/M
+Rhoda/M
+Rhodes
+Rhodesia/M
+Rhodesian/S
+Rhodia/M
+Rhodie/M
+rhodium/MS
+rhododendron/SM
+rhodolite/M
+rhodonite/M
+Rhody/M
+rhombic
+rhomboidal
+rhomboid/SM
+rhombus/SM
+rho/MS
+Rhona/M
+Rhonda/M
+Rhone
+rhubarb/MS
+rhyme/DSRGZM
+rhymester/MS
+Rhys/M
+rhythmical/Y
+rhythmic/S
+rhythmics/M
+rhythm/MS
+RI
+rial/MS
+Riane/M
+Riannon/M
+Rianon/M
+ribaldry/MS
+ribald/S
+ribbed
+Ribbentrop/M
+ribber/S
+ribbing/M
+ribbon/DMSG
+ribcage
+rib/MS
+riboflavin/MS
+ribonucleic
+ribosomal
+ribosome/MS
+Rica/M
+Rican/SM
+Ricard/M
+Ricardo/M
+Ricca/M
+Riccardo/M
+rice/DRSMZG
+Rice/M
+ricer/M
+Richard/MS
+Richardo/M
+Richardson/M
+Richart/M
+Richelieu/M
+richen/DG
+Richey/M
+Richfield/M
+Richie/M
+Richland/M
+Rich/M
+Richmond/M
+Richmound/M
+richness/MS
+Richter/M
+Richthofen/M
+Richy/M
+rich/YNSRPT
+Rici/M
+Rickard/M
+Rickenbacker/M
+Rickenbaugh/M
+Rickert/M
+rickets/M
+rickety/RT
+Rickey/M
+rick/GSDM
+Rickie/M
+Ricki/M
+Rick/M
+Rickover/M
+rickrack/MS
+rickshaw/SM
+Ricky/M
+Ric/M
+ricochet/GSD
+Rico/M
+Ricoriki/M
+ricotta/MS
+riddance/SM
+ridden
+ridding
+riddle/GMRSD
+Riddle/M
+ride/CZSGR
+Ride/M
+rider/CM
+riderless
+ridership/S
+ridge/DSGM
+Ridgefield/M
+ridgepole/SM
+Ridgway/M
+ridgy/RT
+ridicule/MGDRS
+ridiculer/M
+ridiculousness/MS
+ridiculous/PY
+riding/M
+rid/ZGRJSB
+Riemann/M
+Riesling/SM
+rife/RT
+riff/GSDM
+riffle/SDG
+riffraff/SM
+rifled/U
+rifle/GZMDSR
+rifleman/M
+riflemen
+rifler/M
+rifling/M
+rift/GSMD
+Riga/M
+rigamarole's
+rigatoni/M
+Rigel/M
+rigged
+rigger/SM
+rigging/MS
+Riggs/M
+righteousnesses/U
+righteousness/MS
+righteous/PYU
+rightfulness/MS
+rightful/PY
+rightism/SM
+rightist/S
+rightmost
+rightness/MS
+Right/S
+right/SGTPYRDN
+rightsize/SDG
+rights/M
+rightward/S
+rigidify/S
+rigidity/S
+rigidness/S
+rigid/YP
+rigmarole/MS
+rig/MS
+Rigoberto/M
+Rigoletto/M
+rigor/MS
+rigorousness/S
+rigorous/YP
+Riki/M
+Rikki/M
+Rik/M
+rile/DSG
+Riley/M
+Rilke/M
+rill/GSMD
+Rimbaud/M
+rime/MS
+rimer/M
+rim/GSMDR
+rimless
+rimmed
+rimming
+Rinaldo/M
+Rina/M
+rind/MDGS
+Rinehart/M
+ringer/M
+ring/GZJDRM
+ringing/Y
+ringleader/MS
+ringlet/SM
+ringlike
+Ringling/M
+Ring/M
+ringmaster/MS
+Ringo/M
+ringside/ZMRS
+ringworm/SM
+rink/GDRMS
+rinse/DSRG
+Riobard/M
+Rio/MS
+Riordan/M
+rioter/M
+riotousness/M
+riotous/PY
+riot/SMDRGZJ
+RIP
+riparian/S
+ripcord/SM
+ripened/U
+ripenesses
+ripeness/UM
+ripen/RDG
+ripe/PSY
+riper/U
+ripest/U
+Ripley/M
+Rip/M
+rip/NDRSXTG
+ripoff/S
+riposte/SDMG
+ripped
+ripper/SM
+ripping
+rippler/M
+ripple/RSDGM
+ripply/TR
+ripsaw/GDMS
+riptide/SM
+Risa/M
+RISC
+risen
+riser/M
+rise/RSJZG
+risibility/SM
+risible/S
+rising/M
+risker/M
+risk/GSDRM
+riskily
+riskiness/MS
+risky/RTP
+risotto/SM
+risqué
+rissole/M
+Ritalin
+Rita/M
+Ritchie/M
+rite/DSM
+Ritter/M
+ritualism/SM
+ritualistic
+ritualistically
+ritualized
+ritual/MSY
+Ritz/M
+ritzy/TR
+rivaled/U
+Rivalee/M
+rivalry/MS
+rival/SGDM
+Riva/MS
+rive/CSGRD
+Rivera/M
+riverbank/SM
+riverbed/S
+riverboat/S
+river/CM
+riverfront
+riverine
+Rivers
+Riverside/M
+riverside/S
+Riverview/M
+riveter/M
+rivet/GZSRDM
+riveting/Y
+Riviera/MS
+Rivi/M
+Rivkah/M
+rivulet/SM
+Rivy/M
+riv/ZGNDR
+Riyadh/M
+riyal/SM
+rm
+RMS
+RN
+RNA
+Rn/M
+roach/GSDM
+Roach/M
+roadbed/MS
+roadblock/SMDG
+roadhouse/SM
+roadie/S
+roadkill/S
+road/MIS
+roadrunner/MS
+roadshow/S
+roadside/S
+roadsigns
+roadster/SM
+roadsweepers
+roadway/SM
+roadwork/SM
+roadworthy
+roam/DRGZS
+Roana/M
+Roanna/M
+Roanne/M
+Roanoke/M
+roan/S
+roar/DRSJGZ
+roarer/M
+roaring/T
+Roarke/M
+roaster/M
+roast/SGJZRD
+robbed
+robber/SM
+Robbert/M
+robbery/SM
+Robbie/M
+Robbi/M
+robbing
+Robbin/MS
+Robb/M
+Robby/M
+Robbyn/M
+robe/ESDG
+Robena/M
+Robenia/M
+Robers/M
+Roberson/M
+Roberta/M
+Robert/MS
+Roberto/M
+Robertson/SM
+robe's
+Robeson/M
+Robespierre/M
+Robina/M
+Robinet/M
+Robinetta/M
+Robinette/M
+Robinett/M
+Robinia/M
+Robin/M
+robin/MS
+Robinson/M
+Robinsonville/M
+Robles/M
+Rob/MZ
+robotic/S
+robotism
+robotize/GDS
+robot/MS
+rob/SDG
+Robson/M
+Robt/M
+robustness/SM
+robust/RYPT
+Roby/M
+Robyn/M
+Rocco/M
+Rocha/M
+Rochambeau/M
+Rochella/M
+Rochelle/M
+Rochell/M
+Roche/M
+Rochester/M
+Rochette/M
+Roch/M
+rockabilly/MS
+rockabye
+Rockaway/MS
+rockbound
+Rockefeller/M
+rocker/M
+rocketry/MS
+rocket/SMDG
+Rockey/M
+rockfall/S
+Rockford/M
+rock/GZDRMS
+Rockie/M
+rockiness/MS
+Rockland/M
+Rock/M
+Rockne/M
+Rockville/M
+Rockwell/M
+Rocky/SM
+rocky/SRTP
+rococo/MS
+Roda/M
+rodded
+Roddenberry/M
+rodder
+Roddie/M
+rodding
+Rodd/M
+Roddy/M
+rodent/MS
+rodeo/SMDG
+Roderich/M
+Roderick/M
+Roderic/M
+Roderigo/M
+rode/S
+Rodger/M
+Rodge/ZMR
+Rodie/M
+Rodi/M
+Rodina/M
+Rodin/M
+Rod/M
+Rodney/M
+Rodolfo/M
+Rodolphe/M
+Rodolph/M
+Rodrick/M
+Rodrigo/M
+Rodriguez/M
+Rodrique/M
+Rodriquez/M
+rod/SGMD
+roebuck/SM
+Roentgen's
+roentgen/SM
+roe/SM
+ROFL
+Rogelio/M
+roger/GSD
+Rogerio/M
+Roger/M
+Roget/M
+Rog/MRZ
+rogued/K
+rogue/GMDS
+roguery/MS
+rogues/K
+roguing/K
+roguishness/SM
+roguish/PY
+roil/SGD
+Roi/SM
+roisterer/M
+roister/SZGRD
+Rojas/M
+Roland/M
+Rolando/M
+Roldan/M
+role/MS
+Roley/M
+Rolfe/M
+Rolf/M
+Rolland/M
+rollback/SM
+rolled/A
+Rollerblade/S
+rollerskating
+roller/SM
+rollick/DGS
+rollicking/Y
+Rollie/M
+rolling/S
+Rollin/SM
+Rollo/M
+rollover/S
+roll/UDSG
+Rolodex
+Rolph/M
+Rolvaag/M
+ROM
+romaine/MS
+Romain/M
+Roma/M
+romancer/M
+romance/RSDZMG
+Romanesque/S
+Romania/M
+Romanian/SM
+Romano/MS
+Romanov/M
+roman/S
+Romansh/M
+Romans/M
+Roman/SM
+romantically/U
+romanticism/MS
+Romanticism/S
+romanticist/S
+romanticize/SDG
+romantic/MS
+Romany/SM
+Romeo/MS
+romeo/S
+Romero/M
+Rome/SM
+Rommel/M
+Romney/M
+Romola/M
+Romona/M
+Romonda/M
+romper/M
+romp/GSZDR
+Rom/SM
+Romulus/M
+Romy/M
+Ronalda/M
+Ronald/M
+Rona/M
+Ronda/M
+rondo/SM
+Ronica/M
+Ron/M
+Ronna/M
+Ronnica/M
+Ronnie/M
+Ronni/M
+Ronny/M
+Ronstadt/M
+Rontgen
+Roobbie/M
+rood/MS
+roof/DRMJGZS
+roofer/M
+roofgarden
+roofing/M
+roofless
+rooftop/S
+rookery/MS
+rook/GDMS
+rookie/SRMT
+roomer/M
+roomette/SM
+roomful/MS
+roominess/MS
+roommate/SM
+room/MDRGZS
+roomy/TPSR
+Rooney/M
+Rooseveltian
+Roosevelt/M
+rooster/M
+roost/SGZRDM
+rooted/P
+rooter/M
+rootlessness/M
+rootless/P
+rootlet/SM
+Root/M
+root/MGDRZS
+rootstock/M
+rope/DRSMZG
+roper/M
+roping/M
+Roquefort/MS
+Roquemore/M
+Rora/M
+Rorie/M
+Rori/M
+Rorke/M
+Rorschach
+Rory/M
+Rosabella/M
+Rosabelle/M
+Rosabel/M
+Rosaleen/M
+Rosales/M
+Rosalia/M
+Rosalie/M
+Rosalinda/M
+Rosalinde/M
+Rosalind/M
+Rosaline/M
+Rosalynd/M
+Rosalyn/M
+Rosa/M
+Rosamond/M
+Rosamund/M
+Rosana/M
+Rosanna/M
+Rosanne/M
+Rosario/M
+rosary/SM
+Roscoe/M
+Rosco/M
+Roseanna/M
+Roseanne/M
+Roseann/M
+roseate/Y
+Roseau
+rosebud/MS
+rosebush/SM
+Rosecrans/M
+Roseland/M
+Roselia/M
+Roseline/M
+Roselin/M
+Rosella/M
+Roselle/M
+Rose/M
+Rosemaria/M
+Rosemarie/M
+Rosemary/M
+rosemary/MS
+rose/MGDS
+Rosemonde/M
+Rosenberg/M
+Rosenblum/M
+Rosendo/M
+Rosene/M
+Rosen/M
+Rosenthal/M
+Rosenzweig/M
+Rosetta/M
+Rosette/M
+rosette/SDMG
+rosewater
+rosewood/SM
+Roshelle/M
+Rosicrucian/M
+Rosie/M
+rosily
+Rosina/M
+rosiness/MS
+rosin/SMDG
+Rosita/M
+Roslyn/M
+Rosmunda/M
+Ros/N
+Ross
+Rossetti/M
+Rossie/M
+Rossi/M
+Rossini/M
+Rossy/M
+Rostand/M
+roster/DMGS
+Rostov/M
+rostra's
+rostrum/SM
+Roswell/M
+Rosy/M
+rosy/RTP
+rota/MS
+Rotarian/SM
+rotary/S
+rotated/U
+rotate/VGNXSD
+rotational/Y
+rotation/M
+rotative/Y
+rotator/SM
+rotatory
+ROTC
+rote/MS
+rotgut/MS
+Roth/M
+Rothschild/M
+rotisserie/MS
+rotogravure/SM
+rotor/MS
+rototill/RZ
+rot/SDG
+rotted
+rottenness/S
+rotten/RYSTP
+Rotterdam/M
+rotter/M
+rotting
+rotunda/SM
+rotundity/S
+rotundness/S
+rotund/SDYPG
+Rouault/M
+roué/MS
+rouge/GMDS
+roughage/SM
+roughen/DG
+rougher/M
+roughhouse/GDSM
+roughish
+roughneck/MDSG
+roughness/MS
+roughs
+roughshod
+rough/XPYRDNGT
+roulette/MGDS
+roundabout/PSM
+roundedness/M
+rounded/P
+roundelay/SM
+roundels
+rounder/M
+roundhead/D
+roundheadedness/M
+roundheaded/P
+roundhouse/SM
+roundish
+roundness/MS
+roundoff
+roundup/MS
+roundworm/MS
+round/YRDSGPZT
+Rourke/M
+rouse/DSRG
+rouser/M
+Rousseau/M
+roustabout/SM
+roust/SGD
+route/ASRDZGJ
+router/M
+route's
+rout/GZJMDRS
+routine/SYM
+routing/M
+routinize/GSD
+Rouvin/M
+rover/M
+Rover/M
+rove/ZGJDRS
+roving/M
+Rowan/M
+rowboat/SM
+rowdily
+rowdiness/MS
+rowdyism/MS
+rowdy/PTSR
+rowel/DMSG
+Rowe/M
+Rowena/M
+rowen/M
+Rowen/M
+rower/M
+Rowland/M
+Rowley/M
+Row/MN
+Rowney/M
+row/SJZMGNDR
+Roxana/M
+Roxane/M
+Roxanna/M
+Roxanne/M
+Roxie/M
+Roxi/M
+Roxine/M
+Roxy/M
+royalist/SM
+Royall/M
+Royal/M
+royal/SY
+royalty/MS
+Royce/M
+Roy/M
+Rozalie/M
+Rozalin/M
+Rozamond/M
+Rozanna/M
+Rozanne/M
+Rozele/M
+Rozella/M
+Rozelle/M
+Roze/M
+Rozina/M
+Roz/M
+RP
+rpm
+RPM
+rps
+RR
+Rriocard/M
+rs
+r's
+R's
+RSFSR
+RSI
+RSV
+RSVP
+RSX
+rt
+rte
+Rte
+RTFM
+r/TGVJ
+Rubaiyat/M
+rubato/MS
+rubbed
+rubberize/GSD
+rubberneck/DRMGSZ
+rubber/SDMG
+rubbery/TR
+rubbing/M
+rubbish/DSMG
+rubbishy
+rubble/GMSD
+rubdown/MS
+rubella/MS
+Rube/M
+Ruben/MS
+rube/SM
+Rubetta/M
+Rubia/M
+Rubicon/SM
+rubicund
+rubidium/SM
+Rubie/M
+Rubik/M
+Rubi/M
+Rubina/M
+Rubin/M
+Rubinstein/M
+ruble/MS
+rubout
+rubric/MS
+rub/S
+Ruby/M
+ruby/MTGDSR
+Ruchbah/M
+ruck/M
+rucksack/SM
+ruckus/SM
+ruction/SM
+rudderless
+rudder/MS
+Ruddie/M
+ruddiness/MS
+Rudd/M
+Ruddy/M
+ruddy/PTGRSD
+rudeness/MS
+rude/PYTR
+Rudie/M
+Rudiger/M
+rudimentariness/M
+rudimentary/P
+rudiment/SM
+Rudolf/M
+Rudolfo/M
+Rudolph/M
+Rudyard/M
+Rudy/M
+ruefulness/S
+rueful/PY
+rue/GDS
+Rufe/M
+ruff/GSYDM
+ruffian/GSMDY
+ruffled/U
+ruffler/M
+ruffle/RSDG
+ruffly/TR
+Rufus/M
+Rugby's
+rugby/SM
+ruggedness/S
+rugged/PYRT
+Ruggiero/M
+rugging
+rug/MS
+Ruhr/M
+ruination/MS
+ruiner/M
+ruin/MGSDR
+ruinousness/M
+ruinous/YP
+Ruiz/M
+rulebook/S
+ruled/U
+rule/MZGJDRS
+ruler/GMD
+ruling/M
+Rumanian's
+Rumania's
+rumba/GDMS
+rumble/JRSDG
+rumbler/M
+rumbustious
+rumen/M
+Rumford/M
+Ru/MH
+ruminant/YMS
+ruminate/VNGXSD
+ruminative/Y
+rummage/GRSD
+rummager/M
+Rummel/M
+rummer
+rummest
+rummy/TRSM
+rumored/U
+rumorer/M
+rumormonger/SGMD
+rumor/ZMRDSG
+Rumpelstiltskin/M
+rump/GMYDS
+rumple/SDG
+rumply/TR
+rumpus/SM
+rum/XSMN
+runabout/SM
+runaround/S
+run/AS
+runaway/S
+rundown/SM
+rune/MS
+Runge/M
+rung/MS
+runic
+runlet/SM
+runnable
+runnel/SM
+runner/MS
+running/S
+Runnymede/M
+runny/RT
+runoff/MS
+runtime
+runtiness/M
+runt/MS
+runty/RPT
+runway/MS
+Runyon/M
+rupee/MS
+Ruperta/M
+Rupert/M
+Ruperto/M
+rupiah/M
+rupiahs
+Ruppert/M
+Ruprecht/M
+rupture/GMSD
+rurality/M
+rural/Y
+Rurik/M
+ruse/MS
+Rushdie/M
+rush/DSRGZ
+rusher/M
+rushes/I
+rushing/M
+Rush/M
+Rushmore/M
+rushy/RT
+Ruskin/M
+rusk/MS
+Russell/M
+Russel/M
+russet/MDS
+russetting
+Russia/M
+Russian/SM
+Russo/M
+Russ/S
+Rustbelt/M
+rustically
+rusticate/GSD
+rustication/M
+rusticity/S
+rustic/S
+Rustie/M
+rustiness/MS
+Rustin/M
+rustler/M
+rustle/RSDGZ
+rust/MSDG
+rustproof/DGS
+Rusty/M
+rusty/XNRTP
+rutabaga/SM
+Rutger/SM
+Ruthanne/M
+Ruthann/M
+Ruthe/M
+ruthenium/MS
+rutherfordium/SM
+Rutherford/M
+Ruthie/M
+Ruthi/M
+ruthlessness/MS
+ruthless/YP
+Ruth/M
+Ruthy/M
+Rutland/M
+Rutledge/M
+rut/MS
+rutted
+Rutter/M
+Ruttger/M
+rutting
+rutty/RT
+Ruy/M
+RV
+RVs
+Rwandan/S
+Rwanda/SM
+Rwy/M
+Rx/M
+Ryan/M
+Ryann/M
+Rycca/M
+Rydberg/M
+Ryder/M
+rye/MS
+Ryley/M
+Ry/M
+Ryon/M
+Ryukyu/M
+Ryun/M
+S
+SA
+Saab/M
+Saar/M
+Saba/M
+sabbath
+Sabbath/M
+Sabbaths
+sabbatical/S
+sabered/U
+saber/GSMD
+Sabik/M
+Sabina/M
+Sabine/M
+Sabin/M
+sable/GMDS
+sabotage/DSMG
+saboteur/SM
+sabot/MS
+Sabra/M
+sabra/MS
+Sabrina/M
+SAC
+Sacajawea/M
+saccharides
+saccharine
+saccharin/MS
+Sacco/M
+sacerdotal
+Sacha/M
+sachem/MS
+sachet/SM
+Sachs/M
+sackcloth/M
+sackcloths
+sacker/M
+sackful/MS
+sack/GJDRMS
+sacking/M
+sacral
+sacra/L
+sacramental/S
+sacrament/DMGS
+Sacramento/M
+sacredness/S
+sacred/PY
+sacrificer/M
+sacrifice/RSDZMG
+sacrificial/Y
+sacrilege/MS
+sacrilegious/Y
+sacristan/SM
+sacristy/MS
+sacroiliac/S
+sacrosanctness/MS
+sacrosanct/P
+sacrum/M
+sac/SM
+Sada/M
+Sadat/M
+Saddam/M
+sadden/DSG
+sadder
+saddest
+saddlebag/SM
+saddler/M
+saddle's
+saddle/UGDS
+Sadducee/M
+Sadella/M
+Sade/M
+sades
+Sadie/M
+sadism/MS
+sadistic
+sadistically
+sadist/MS
+sadness/SM
+sadomasochism/MS
+sadomasochistic
+sadomasochist/S
+sad/PY
+Sadr/M
+Sadye/M
+safari/GMDS
+safeguard/MDSG
+safekeeping/MS
+safeness/MS
+safeness's/U
+safes
+safety/SDMG
+safe/URPTY
+safflower/SM
+saffron/MS
+sagaciousness/M
+sagacious/YP
+sagacity/MS
+saga/MS
+Sagan/M
+sagebrush/SM
+sage/MYPS
+sagged
+sagger
+sagging
+saggy/RT
+Saginaw/M
+Sagittarius/MS
+sago/MS
+sag/TSR
+saguaro/SM
+Sahara/M
+Saharan/M
+Sahel
+sahib/MS
+Saidee/M
+saids
+said/U
+Saigon/M
+sailboard/DGS
+sailboat/SRMZG
+sailcloth/M
+sailcloths
+sailer/M
+sailfish/SM
+sail/GJMDRS
+sailing/M
+sailor/YMS
+sailplane/SDMG
+sainthood/MS
+saintlike
+saintliness/MS
+saintly/RTP
+saint/YDMGS
+Saiph/M
+saith
+saiths
+Sakai/M
+sake/MRS
+saker/M
+Sakhalin/M
+Sakharov/M
+Saki/M
+saki's
+salaam/GMDS
+salable/U
+salaciousness/MS
+salacious/YP
+salacity/MS
+Saladin/M
+Salado/M
+salad/SM
+Salaidh/M
+salamander/MS
+salami/MS
+salary/SDMG
+Salas/M
+Salazar/M
+saleability/M
+sale/ABMS
+Saleem/M
+Salem/M
+Salerno/M
+salesclerk/SM
+salesgirl/SM
+saleslady/S
+salesman/M
+salesmanship/SM
+salesmen
+salespeople/M
+salesperson/MS
+salesroom/M
+saleswoman
+saleswomen
+salience/MS
+saliency
+salient/SY
+Salim/M
+Salina/MS
+saline/S
+salinger
+Salinger/M
+salinity/MS
+Salisbury/M
+Salish/M
+saliva/MS
+salivary
+salivate/XNGSD
+salivation/M
+Salk/M
+Sallee/M
+Salle/M
+Sallie/M
+Salli/M
+sallowness/MS
+sallow/TGRDSP
+Sallust/M
+Sallyanne/M
+Sallyann/M
+sally/GSDM
+Sally/M
+salmonellae
+salmonella/M
+Salmon/M
+salmon/SM
+Sal/MY
+Saloma/M
+Salome/M
+Salomi/M
+Salomo/M
+Salomone/M
+Salomon/M
+Salonika/M
+salon/SM
+saloonkeeper
+saloon/MS
+salsa/MS
+salsify/M
+SALT
+saltcellar/SM
+salted/UC
+salter/M
+salt/GZTPMDRS
+saltine/MS
+saltiness/SM
+saltness/M
+Salton/M
+saltpeter/SM
+salts/C
+saltshaker/S
+saltwater
+salty/RSPT
+salubriousness/M
+salubrious/YP
+salubrity/M
+salutariness/M
+salutary/P
+salutation/SM
+salutatory/S
+saluter/M
+salute/RSDG
+Salvadoran/S
+Salvadorian/S
+Salvador/M
+salvageable
+salvage/MGRSD
+salvager/M
+salvation/MS
+Salvatore/M
+salve/GZMDSR
+salver/M
+Salvidor/M
+salvo/GMDS
+Salween/M
+Salyut/M
+Salz/M
+SAM
+Samantha/M
+Samara/M
+Samaria/M
+Samaritan/MS
+samarium/MS
+Samarkand/M
+samba/GSDM
+sameness/MS
+same/SP
+Sam/M
+Sammie/M
+Sammy/M
+Samoa
+Samoan/S
+Samoset/M
+samovar/SM
+Samoyed/M
+sampan/MS
+sampler/M
+sample/RSDJGMZ
+sampling/M
+Sampson/M
+Samsonite/M
+Samson/M
+Samuele/M
+Samuel/SM
+Samuelson/M
+samurai/M
+San'a
+Sana/M
+sanatorium/MS
+Sanborn/M
+Sanchez/M
+Sancho/M
+sanctification/M
+sanctifier/M
+sanctify/RSDGNX
+sanctimoniousness/MS
+sanctimonious/PY
+sanctimony/MS
+sanctioned/U
+sanction/SMDG
+sanctity/SM
+sanctuary/MS
+sanctum/SM
+sandal/MDGS
+sandalwood/SM
+sandbagged
+sandbagging
+sandbag/MS
+sandbank/SM
+sandbar/S
+sandblaster/M
+sandblast/GZSMRD
+sandbox/MS
+Sandburg/M
+sandcastle/S
+Sande/M
+Sanderling/M
+sander/M
+Sander/M
+Sanderson/M
+sandhill
+sandhog/SM
+Sandia/M
+Sandie/M
+Sandi/M
+sandiness/S
+Sandinista
+sandlot/SM
+sandlotter/S
+sandman/M
+sandmen
+Sand/MRZ
+Sandor/M
+Sandoval/M
+sandpaper/DMGS
+sandpile
+sandpiper/MS
+sandpit/M
+Sandra/M
+Sandro/M
+sand/SMDRGZ
+sandstone/MS
+sandstorm/SM
+Sandusky/M
+sandwich/SDMG
+Sandye/M
+Sandy/M
+sandy/PRT
+saned
+sane/IRYTP
+saneness/MS
+saneness's/I
+sanes
+Sanford/M
+Sanforized
+Sanger/M
+sangfroid/S
+sangria/SM
+Sang/RM
+sang/S
+sanguinary
+sanguined
+sanguine/F
+sanguinely
+sanguineness/M
+sanguineous/F
+sanguines
+sanguining
+Sanhedrin/M
+saning
+sanitarian/S
+sanitarium/SM
+sanitary/S
+sanitate/NX
+sanitation/M
+sanitizer/M
+sanitize/RSDZG
+sanity/SIM
+sank
+Sankara/M
+San/M
+sans
+sanserif
+Sanskritic
+Sanskritize/M
+Sanskrit/M
+Sansone/M
+Sanson/M
+Santa/M
+Santana/M
+Santayana/M
+Santeria
+Santiago/M
+Santo/MS
+sapience/MS
+sapient
+sapless
+sapling/SM
+sap/MS
+sapped
+sapper/SM
+Sapphira/M
+Sapphire/M
+sapphire/MS
+Sappho/M
+sappiness/SM
+sapping
+Sapporo/M
+sappy/RPT
+saprophyte/MS
+saprophytic
+sapsucker/SM
+sapwood/SM
+Saraann/M
+Saracen/MS
+Saragossa/M
+Sarah/M
+Sarajane/M
+Sarajevo/M
+Sara/M
+Saran/M
+saran/SM
+sarape's
+Sarasota/M
+Saratoga/M
+Saratov/M
+Sarawak/M
+sarcasm/MS
+sarcastic
+sarcastically
+sarcoma/MS
+sarcophagi
+sarcophagus/M
+sardine/SDMG
+Sardinia/M
+sardonic
+sardonically
+Saree/M
+Sarena/M
+Sarene/M
+Sarette/M
+Sargasso/M
+Sarge/M
+Sargent/M
+sarge/SM
+Sargon/M
+Sari/M
+sari/MS
+Sarina/M
+Sarine/M
+Sarita/M
+Sarnoff/M
+sarong/MS
+Saroyan/M
+sarsaparilla/MS
+Sarto/M
+sartorial/Y
+sartorius/M
+Sartre/M
+Sascha/M
+SASE
+Sasha/M
+sashay/GDS
+Sashenka/M
+sash/GMDS
+Saskatchewan/M
+Saskatoon/M
+Sask/M
+sassafras/MS
+sass/GDSM
+Sassoon/M
+sassy/TRS
+SAT
+satanic
+satanical/Y
+Satanism/M
+satanism/S
+Satanist/M
+satanist/S
+Satan/M
+satchel/SM
+sat/DG
+sateen/MS
+satellite/GMSD
+sate/S
+satiable/I
+satiate/GNXSD
+satiation/M
+satiety/MS
+satin/MDSG
+satinwood/MS
+satiny
+satire/SM
+satiric
+satirical/Y
+satirist/SM
+satirize/DSG
+satirizes/U
+satisfaction/ESM
+satisfactorily/U
+satisfactoriness/MU
+satisfactory/UP
+satisfiability/U
+satisfiable/U
+satisfied/UE
+satisfier/M
+satisfies/E
+satisfy/GZDRS
+satisfying/EU
+satisfyingly
+Sat/M
+satori/SM
+satrap/SM
+saturated/CUA
+saturater/M
+saturates/A
+saturate/XDRSNG
+saturation/M
+Saturday/MS
+saturnalia
+Saturnalia/M
+saturnine/Y
+Saturn/M
+Satyanarayanan/M
+satyriases
+satyriasis/M
+satyric
+satyr/MS
+sauce/DSRGZM
+saucepan/SM
+saucer/M
+saucily
+sauciness/S
+saucy/TRP
+Saudi/S
+Saud/M
+Saudra/M
+sauerkraut/SM
+Saukville/M
+Saul/M
+Sault/M
+sauna/DMSG
+Sauncho/M
+Saunder/SM
+Saunderson/M
+Saundra/M
+saunter/DRSG
+saurian/S
+sauropod/SM
+sausage/MS
+Saussure/M
+sauté/DGS
+Sauternes/M
+Sauveur/M
+savage/GTZYPRSD
+Savage/M
+savageness/SM
+savagery/MS
+Savannah/M
+savanna/MS
+savant/SM
+saved/U
+saveloy/M
+saver/M
+save/ZGJDRSB
+Savina/M
+Savior/M
+savior/SM
+Saviour/M
+Savonarola/M
+savored/U
+savorer/M
+savorier
+savoriest
+savoriness/S
+savoringly/S
+savoring/Y
+savor/SMRDGZ
+savory/UMPS
+Savoyard/M
+Savoy/M
+savoy/SM
+savvy/GTRSD
+sawbones/M
+sawbuck/SM
+sawdust/MDSG
+sawer/M
+sawfly/SM
+sawhorse/MS
+Saw/M
+sawmill/SM
+saw/SMDRG
+sawtooth
+Sawyere/M
+Sawyer/M
+sawyer/MS
+Saxe/M
+saxifrage/SM
+Sax/M
+sax/MS
+Saxon/SM
+Saxony/M
+saxophone/MS
+saxophonist/SM
+Saxton/M
+Sayer/M
+sayer/SM
+sayest
+saying/MS
+Sayre/MS
+says/M
+say/USG
+Say/ZMR
+SBA
+Sb/M
+SC
+scabbard/SGDM
+scabbed
+scabbiness/SM
+scabbing
+scabby/RTP
+scabies/M
+scabrousness/M
+scabrous/YP
+scab/SM
+scad/SM
+scaffolding/M
+scaffold/JGDMS
+scalability
+Scala/M
+scalar/SM
+scalawag/SM
+scald/GJRDS
+scaled/AU
+scale/JGZMBDSR
+scaleless
+scalene
+scaler/M
+scales/A
+scaliness/MS
+scaling/A
+scallion/MS
+scalloper/M
+scallop/GSMDR
+scalloping/M
+scalpel/SM
+scalper/M
+scalp/GZRDMS
+scalping/M
+scaly/TPR
+scammed
+scamming
+scamper/GD
+scampi/M
+scamp/RDMGZS
+scam/SM
+Scan
+scan/AS
+scandal/GMDS
+scandalized/U
+scandalize/GDS
+scandalmonger/SM
+scandalousness/M
+scandalous/YP
+Scandinavia/M
+Scandinavian/S
+scandium/MS
+scanned/A
+scanner/SM
+scanning/A
+scansion/SM
+scant/CDRSG
+scantest
+scantily
+scantiness/MS
+scantly
+scantness/MS
+scanty/TPRS
+scapegoat/SGDM
+scapegrace/MS
+scape/M
+scapulae
+scapula/M
+scapular/S
+scarab/SM
+Scaramouch/M
+Scarborough/M
+scarceness/SM
+scarce/RTYP
+scarcity/MS
+scar/DRMSG
+scarecrow/MS
+scaremongering/M
+scaremonger/SGM
+scarer/M
+scare/S
+scarface
+Scarface/M
+scarf/SDGM
+scarification/M
+scarify/DRSNGX
+scarily
+scariness/S
+scarlatina/MS
+Scarlatti/M
+Scarlet/M
+scarlet/MDSG
+Scarlett/M
+scarp/SDMG
+scarred
+scarring
+scarves/M
+scary/PTR
+scathe/DG
+scathed/U
+scathing/Y
+scatological
+scatology/SM
+scat/S
+scatted
+scatterbrain/MDS
+scatter/DRJZSG
+scatterer/M
+scattergun
+scattering/YM
+scatting
+scavenge/GDRSZ
+scavenger/M
+SCCS
+scenario/SM
+scenarist/MS
+scene/GMDS
+scenery/SM
+scenically
+scenic/S
+scented/U
+scent/GDMS
+scentless
+scent's/C
+scents/C
+scepter/DMSG
+scepters/U
+sceptically
+sch
+Schaefer/M
+Schaeffer/M
+Schafer/M
+Schaffner/M
+Schantz/M
+Schapiro/M
+Scheat/M
+Schedar/M
+schedule/ADSRG
+scheduled/U
+scheduler/MS
+schedule's
+Scheherazade/M
+Scheherezade/M
+Schelling/M
+schema/M
+schemata
+schematically
+schematic/S
+scheme/JSRDGMZ
+schemer/M
+schemta
+Schenectady/M
+scherzo/MS
+Schick/M
+Schiller/M
+schilling/SM
+schismatic/S
+schism/SM
+schist/SM
+schizoid/S
+schizomycetes
+schizophrenia/SM
+schizophrenically
+schizophrenic/S
+schizo/S
+schlemiel/MS
+schlepped
+schlepping
+schlep/S
+Schlesinger/M
+Schliemann/M
+Schlitz/M
+schlock/SM
+schlocky/TR
+Schloss/M
+schmaltz/MS
+schmaltzy/TR
+Schmidt/M
+Schmitt/M
+schmoes
+schmo/M
+schmooze/GSD
+schmuck/MS
+Schnabel/M
+schnapps/M
+schnauzer/MS
+Schneider/M
+schnitzel/MS
+schnook/SM
+schnoz/S
+schnozzle/MS
+Schoenberg/M
+Schofield/M
+scholarship/MS
+scholar/SYM
+scholastically
+scholastic/S
+schoolbag/SM
+schoolbook/SM
+schoolboy/MS
+schoolchild/M
+schoolchildren
+schooldays
+schooled/U
+schoolfellow/S
+schoolfriend
+schoolgirlish
+schoolgirl/MS
+schoolhouse/MS
+schooling/M
+schoolmarmish
+schoolmarm/MS
+schoolmaster/SGDM
+schoolmate/MS
+schoolmistress/MS
+schoolroom/SM
+schoolteacher/MS
+schoolwork/SM
+schoolyard/SM
+school/ZGMRDJS
+schooner/SM
+Schopenhauer/M
+Schottky/M
+Schrieffer/M
+Schrödinger/M
+Schroeder/M
+Schroedinger/M
+Schubert/M
+Schultz/M
+Schulz/M
+Schumacher/M
+Schuman/M
+Schumann/M
+schussboomer/S
+schuss/SDMG
+Schuster/M
+Schuyler/M
+Schuylkill/M
+Schwab/M
+Schwartzkopf/M
+Schwartz/M
+Schwarzenegger/M
+schwa/SM
+Schweitzer/M
+Schweppes/M
+Schwinger/M
+Schwinn/M
+sci
+sciatica/SM
+sciatic/S
+science/FMS
+scientifically/U
+scientific/U
+scientist/SM
+Scientology/M
+scimitar/SM
+scintilla/MS
+scintillate/GNDSX
+scintillation/M
+scintillator/SM
+scion/SM
+Scipio/M
+scissor/SGD
+scleroses
+sclerosis/M
+sclerotic/S
+Sc/M
+scoffer/M
+scofflaw/MS
+scoff/RDGZS
+scolder/M
+scold/GSJRD
+scolioses
+scoliosis/M
+scollop's
+sconce/SDGM
+scone/SM
+scooper/M
+scoop/SRDMG
+scooter/M
+scoot/SRDGZ
+scope/DSGM
+Scopes/M
+scops
+scorbutic
+scorcher/M
+scorching/Y
+scorch/ZGRSD
+scoreboard/MS
+scorecard/MS
+scored/M
+scorekeeper/SM
+scoreless
+scoreline
+score/ZMDSRJG
+scorner/M
+scornfulness/M
+scornful/PY
+scorn/SGZMRD
+scorpion/SM
+Scorpio/SM
+Scorpius/M
+Scorsese/M
+Scotchgard/M
+Scotchman/M
+Scotchmen
+scotch/MSDG
+scotchs
+Scotch/S
+Scotchwoman
+Scotchwomen
+Scotia/M
+Scotian/M
+Scotland/M
+Scot/MS
+Scotsman/M
+Scotsmen
+Scotswoman
+Scotswomen
+Scottie/SM
+Scotti/M
+Scottish
+Scott/M
+Scottsdale/M
+Scotty's
+scoundrel/YMS
+scourer/M
+scourge/MGRSD
+scourger/M
+scouring/M
+scour/SRDGZ
+scouter/M
+scouting/M
+scoutmaster/SM
+Scout's
+scout/SRDMJG
+scow/DMGS
+scowler/M
+scowl/SRDG
+scrabble/DRSZG
+scrabbler/M
+Scrabble/SM
+scragged
+scragging
+scraggly/TR
+scraggy/TR
+scrag/SM
+scrambler/MS
+scrambler's/U
+scramble/UDSRG
+scrammed
+scramming
+scram/S
+Scranton/M
+scrapbook/SM
+scraper/M
+scrape/S
+scrapheap/SM
+scrapped
+scrapper/SM
+scrapping
+scrappy/RT
+scrap/SGZJRDM
+scrapyard/S
+scratched/U
+scratcher/M
+scratches/M
+scratchily
+scratchiness/S
+scratch/JDRSZG
+scratchy/TRP
+scrawler/M
+scrawl/GRDS
+scrawly/RT
+scrawniness/MS
+scrawny/TRP
+screamer/M
+screaming/Y
+scream/ZGSRD
+screecher/M
+screech/GMDRS
+screechy/TR
+screed/MS
+scree/DSM
+screened/U
+screening/M
+screenplay/MS
+screen/RDMJSG
+screenwriter/MS
+screwball/SM
+screwdriver/SM
+screwer/M
+screw/GUSD
+screwiness/S
+screw's
+screwup
+screwworm/MS
+screwy/RTP
+Scriabin/M
+scribal
+scribble/JZDRSG
+scribbler/M
+scribe/CDRSGIK
+scriber/MKIC
+scribe's
+Scribner/MS
+scrimmager/M
+scrimmage/RSDMG
+scrimp/DGS
+scrimshaw/GSDM
+scrim/SM
+Scripps/M
+scrip/SM
+scripted/U
+script/FGMDS
+scriptural/Y
+scripture/MS
+Scripture/MS
+scriptwriter/SM
+scriptwriting/M
+scrivener/M
+scriven/ZR
+scrod/M
+scrofula/MS
+scrofulous
+scrollbar/SM
+scroll/GMDSB
+Scrooge/MS
+scrooge/SDMG
+scrota
+scrotal
+scrotum/M
+scrounge/ZGDRS
+scroungy/TR
+scrubbed
+scrubber/MS
+scrubbing
+scrubby/TR
+scrub/S
+scruffily
+scruffiness/S
+scruff/SM
+scruffy/PRT
+Scruggs/M
+scrummage/MG
+scrum/MS
+scrumptious/Y
+scrunch/DSG
+scrunchy/S
+scruple/SDMG
+scrupulosity/SM
+scrupulousness's
+scrupulousness/US
+scrupulous/UPY
+scrutable/I
+scrutinized/U
+scrutinizer/M
+scrutinize/RSDGZ
+scrutinizingly/S
+scrutinizing/UY
+scrutiny/MS
+SCSI
+scuba/SDMG
+scudded
+scudding
+Scud/M
+scud/S
+scuff/GSD
+scuffle/SDG
+sculler/M
+scullery/MS
+Sculley/M
+scullion/MS
+scull/SRDMGZ
+sculptor/MS
+sculptress/MS
+sculpt/SDG
+sculptural/Y
+sculpture/SDGM
+scumbag/S
+scummed
+scumming
+scum/MS
+scummy/TR
+scupper/SDMG
+scurf/MS
+scurfy/TR
+scurrility/MS
+scurrilousness/MS
+scurrilous/PY
+scurry/GJSD
+scurvily
+scurviness/M
+scurvy/SRTP
+scutcheon/SM
+scuttlebutt/MS
+scuttle/MGSD
+scuzzy/RT
+Scylla/M
+scythe/SDGM
+Scythia/M
+SD
+SDI
+SE
+seabed/S
+seabird/S
+seaboard/MS
+Seaborg/M
+seaborne
+Seabrook/M
+seacoast/MS
+seafare/JRZG
+seafarer/M
+seafood/MS
+seafront/MS
+Seagate/M
+seagoing
+Seagram/M
+seagull/S
+seahorse/S
+sealant/MS
+sealed/AU
+sealer/M
+seal/MDRSGZ
+sealskin/SM
+seals/UA
+seamail
+seamanship/SM
+seaman/YM
+seamer/M
+seaminess/M
+seamlessness/M
+seamless/PY
+seam/MNDRGS
+seams/I
+seamstress/MS
+Seamus/M
+sea/MYS
+seamy/TRP
+Seana/M
+séance/SM
+Sean/M
+seaplane/SM
+seaport/SM
+seaquake/M
+Seaquarium/M
+searcher/AM
+searching/YS
+searchlight/SM
+search/RSDAGZ
+sear/DRSJGT
+searing/Y
+Sears/M
+seascape/SM
+seashell/MS
+seashore/SM
+seasickness/SM
+seasick/P
+seaside/SM
+seasonableness/M
+seasonable/UP
+seasonably/U
+seasonality
+seasonal/Y
+seasoned/U
+seasoner/M
+seasoning/M
+season/JRDYMBZSG
+seatbelt
+seated/A
+seater/M
+seating/SM
+SEATO
+seat's
+Seattle/M
+seat/UDSG
+seawall/S
+seaward/S
+seawater/S
+seaway/MS
+seaweed/SM
+seaworthinesses
+seaworthiness/MU
+seaworthy/TRP
+sebaceous
+Sebastian/M
+Sebastiano/M
+Sebastien/M
+seborrhea/SM
+SEC
+secant/SM
+secede/GRSD
+secessionist/MS
+secession/MS
+secludedness/M
+secluded/YP
+seclude/GSD
+seclusion/SM
+seclusive
+Seconal
+secondarily
+secondary/PS
+seconder/M
+secondhand
+second/RDYZGSL
+secrecy/MS
+secretarial
+secretariat/MS
+secretaryship/MS
+secretary/SM
+secrete/XNS
+secretion/M
+secretiveness/S
+secretive/PY
+secretory
+secret/TVGRDYS
+sec/S
+sectarianism/MS
+sectarian/S
+sectary/MS
+sectionalism/MS
+sectionalized
+sectional/SY
+section/ASEM
+sectioned
+sectioning
+sect/ISM
+sectoral
+sectored
+sector/EMS
+sectoring
+sects/E
+secularism/MS
+secularist/MS
+secularity/M
+secularization/MS
+secularized/U
+secularize/GSD
+secular/SY
+secured/U
+securely/I
+secure/PGTYRSDJ
+security/MSI
+secy
+sec'y
+sedan/SM
+sedateness/SM
+sedate/PXVNGTYRSD
+sedation/M
+sedative/S
+sedentary
+Seder/SM
+sedge/SM
+Sedgwick/M
+sedgy/RT
+sedimentary
+sedimentation/SM
+sediment/SGDM
+sedition/SM
+seditiousness/M
+seditious/PY
+seducer/M
+seduce/RSDGZ
+seduction/MS
+seductiveness/MS
+seductive/YP
+seductress/SM
+sedulous/Y
+Seebeck/M
+seed/ADSG
+seedbed/MS
+seedcase/SM
+seeded/U
+seeder/MS
+seediness/MS
+seeding/S
+seedless
+seedling/SM
+seedpod/S
+seed's
+seedy/TPR
+seeings
+seeing's
+seeing/U
+seeker/M
+seek/GZSR
+seeking/Y
+Seeley/M
+See/M
+seem/GJSYD
+seeming/Y
+seemliness's
+seemliness/US
+seemly/UTPR
+seen/U
+seepage/MS
+seep/GSD
+seer/SM
+seersucker/MS
+sees
+seesaw/DMSG
+seethe/SDGJ
+see/U
+segmental/Y
+segmentation/SM
+segmented/U
+segment/SGDM
+Segovia/M
+segregant
+segregated/U
+segregate/XCNGSD
+segregation/CM
+segregationist/SM
+segregative
+Segre/M
+segue/DS
+segueing
+Segundo/M
+Se/H
+Seidel/M
+seigneur/MS
+seignior/SM
+Seiko/M
+seine/GZMDSR
+Seine/M
+seiner/M
+Seinfeld/M
+seismic
+seismically
+seismographer/M
+seismographic
+seismographs
+seismography/SM
+seismograph/ZMR
+seismologic
+seismological
+seismologist/MS
+seismology/SM
+seismometer/S
+seize/BJGZDSR
+seizer/M
+seizing/M
+seizin/MS
+seizor/MS
+seizure/MS
+Seka/M
+Sela/M
+Selassie/M
+Selby/M
+seldom
+selected/UAC
+selectional
+selection/MS
+selectiveness/M
+selective/YP
+selectivity/MS
+selectman/M
+selectmen
+selectness/SM
+selector/SM
+select/PDSVGB
+Selectric/M
+selects/A
+Selena/M
+selenate/M
+Selene/M
+selenite/M
+selenium/MS
+selenographer/SM
+selenography/MS
+Selestina/M
+Seleucid/M
+Seleucus/M
+self/GPDMS
+selfishness/SU
+selfish/PUY
+selflessness/MS
+selfless/YP
+selfness/M
+Selfridge/M
+selfsameness/M
+selfsame/P
+Selia/M
+Selie/M
+Selig/M
+Selim/M
+Selina/M
+Selinda/M
+Seline/M
+Seljuk/M
+Selkirk/M
+Sella/M
+sell/AZGSR
+seller/AM
+Sellers/M
+Selle/ZM
+sellout/MS
+Selma/M
+seltzer/S
+selvage/MGSD
+selves/M
+Selznick/M
+semantical/Y
+semanticist/SM
+semantic/S
+semantics/M
+semaphore/GMSD
+Semarang/M
+semblance/ASME
+semen/SM
+semester/SM
+semiannual/Y
+semiarid
+semiautomated
+semiautomatic/S
+semicircle/SM
+semicircular
+semicolon/MS
+semiconductor/SM
+semiconscious
+semidefinite
+semidetached
+semidrying/M
+semifinalist/MS
+semifinal/MS
+semilogarithmic
+semimonthly/S
+seminal/Y
+seminarian/MS
+seminar/SM
+seminary/MS
+Seminole/SM
+semiofficial
+semioticians
+semiotic/S
+semiotics/M
+semipermanent/Y
+semipermeable
+semiprecious
+semiprivate
+semiprofessional/YS
+semipublic
+semiquantitative/Y
+Semiramis/M
+semiretired
+semisecret
+semiskilled
+semi/SM
+semisolid/S
+semistructured
+semisweet
+Semite/SM
+Semitic/MS
+semitic/S
+semitone/SM
+semitrailer/SM
+semitrance
+semitransparent
+semitropical
+semivowel/MS
+semiweekly/S
+semiyearly
+semolina/SM
+sempiternal
+sempstress/SM
+Semtex
+sen
+Sen
+Sena/M
+senate/MS
+Senate/MS
+senatorial
+senator/MS
+Sendai/M
+sender/M
+sends/A
+send/SRGZ
+Seneca/MS
+Senegalese
+Senegal/M
+senescence/SM
+senescent
+senile/SY
+senility/MS
+seniority/SM
+senior/MS
+Senior/S
+Sennacherib/M
+senna/MS
+Sennett/M
+Señora/M
+senora/S
+senorita/S
+senor/MS
+sensately/I
+sensate/YNX
+sensationalism/MS
+sensationalist/S
+sensationalize/GSD
+sensational/Y
+sensation/M
+sens/DSG
+senselessness/SM
+senseless/PY
+sense/M
+sensibility/ISM
+sensibleness/MS
+sensible/PRST
+sensibly/I
+sensitiveness/MS
+sensitiveness's/I
+sensitives
+sensitive/YIP
+sensitivity/ISM
+sensitization/CSM
+sensitized/U
+sensitizers
+sensitize/SDCG
+sensor/MS
+sensory
+sensualist/MS
+sensuality/MS
+sensual/YF
+sensuousness/S
+sensuous/PY
+Sensurround/M
+sentence/SDMG
+sentential/Y
+sententious/Y
+sentience/ISM
+sentient/YS
+sentimentalism/SM
+sentimentalist/SM
+sentimentality/SM
+sentimentalization/SM
+sentimentalize/RSDZG
+sentimentalizes/U
+sentimental/Y
+sentiment/MS
+sentinel/GDMS
+sentry/SM
+sent/UFEA
+Seoul/M
+sepal/SM
+separability/MSI
+separableness/MI
+separable/PI
+separably/I
+separateness/MS
+separates/M
+separate/YNGVDSXP
+separation/M
+separatism/SM
+separatist/SM
+separator/SM
+Sephardi/M
+Sephira/M
+sepia/MS
+Sepoy/M
+sepses
+sepsis/M
+septa/M
+septate/N
+September/MS
+septennial/Y
+septet/MS
+septicemia/SM
+septicemic
+septic/S
+septillion/M
+sept/M
+Sept/M
+septuagenarian/MS
+Septuagint/MS
+septum/M
+sepulcher/MGSD
+sepulchers/UA
+sepulchral/Y
+seq
+sequel/MS
+sequenced/A
+sequence/DRSJZMG
+sequencer/M
+sequence's/F
+sequences/F
+sequent/F
+sequentiality/FM
+sequentialize/DSG
+sequential/YF
+sequester/SDG
+sequestrate/XGNDS
+sequestration/M
+sequin/SDMG
+Sequoia/M
+sequoia/MS
+Sequoya/M
+Serafin/M
+seraglio/SM
+serape/S
+seraphic
+seraphically
+seraphim's
+seraph/M
+seraphs
+sera's
+Serbia/M
+Serbian/S
+Serb/MS
+Serbo/M
+serenade/MGDRS
+serenader/M
+Serena/M
+serendipitous/Y
+serendipity/MS
+serene/GTYRSDP
+Serene/M
+sereneness/SM
+Serengeti/M
+serenity/MS
+sere/TGDRS
+serfdom/MS
+serf/MS
+Sergeant/M
+sergeant/SM
+serge/DSGM
+Sergei/M
+Serge/M
+Sergent/M
+Sergio/M
+serialization/MS
+serialize/GSD
+serial/MYS
+series/M
+serif/SMD
+serigraph/M
+serigraphs
+seriousness/SM
+serious/PY
+sermonize/GSD
+sermon/SGDM
+serological/Y
+serology/MS
+serons
+serous
+Serpens/M
+serpent/GSDM
+serpentine/GYS
+Serra/M
+Serrano/M
+serrate/GNXSD
+serration/M
+serried
+serum/MS
+servant/SDMG
+serve/AGCFDSR
+served/U
+server/MCF
+servers
+serviceability/SM
+serviceableness/M
+serviceable/P
+serviced/U
+serviceman/M
+servicemen
+service/MGSRD
+service's/E
+services/E
+servicewoman
+servicewomen
+serviette/MS
+servilely
+servileness/M
+serviles
+servile/U
+servility/SM
+serving/SM
+servitor/SM
+servitude/MS
+servomechanism/MS
+servomotor/MS
+servo/S
+sesame/MS
+sesquicentennial/S
+sessile
+session/SM
+setback/S
+Seth/M
+Set/M
+Seton/M
+set's
+setscrew/SM
+set/SIA
+settable/A
+sett/BJGZSMR
+settee/MS
+setter/M
+setting/AS
+setting's
+settle/AUDSG
+settlement/ASM
+settler/MS
+settling/S
+setup/MS
+Seumas/M
+Seurat/M
+Seuss/M
+Sevastopol/M
+sevenfold
+sevenpence
+seven/SMH
+seventeen/HMS
+seventeenths
+sevenths
+seventieths
+seventy/MSH
+severalfold
+severalty/M
+several/YS
+severance/SM
+severed/E
+severeness/SM
+severe/PY
+severing/E
+severity/MS
+Severn/M
+severs/E
+sever/SGTRD
+Severus/M
+Seville/M
+sewage/MS
+Seward/M
+sewerage/SM
+sewer/GSMD
+sewing/SM
+sewn
+sew/SAGD
+sexagenarian/MS
+sex/GMDS
+sexily
+sexiness/MS
+sexism/SM
+sexist/SM
+sexless
+sexologist/SM
+sexology/MS
+sexpot/SM
+Sextans/M
+sextant/SM
+sextet/SM
+sextillion/M
+Sexton/M
+sexton/MS
+sextuple/MDG
+sextuplet/MS
+sexuality/MS
+sexualized
+sexual/Y
+sexy/RTP
+Seychelles
+Seyfert
+Seymour/M
+sf
+SF
+Sgt
+shabbily
+shabbiness/SM
+shabby/RTP
+shack/GMDS
+shackler/M
+shackle's
+Shackleton/M
+shackle/UGDS
+shad/DRJGSM
+shaded/U
+shadeless
+shade/SM
+shadily
+shadiness/MS
+shading/M
+shadowbox/SDG
+shadower/M
+shadow/GSDRM
+shadowiness/M
+Shadow/M
+shadowy/TRP
+shady/TRP
+Shae/M
+Shafer/M
+Shaffer/M
+shafting/M
+shaft/SDMG
+shagged
+shagginess/SM
+shagging
+shaggy/TPR
+shag/MS
+shah/M
+shahs
+Shaina/M
+Shaine/M
+shakable/U
+shakably/U
+shakeable
+shakedown/S
+shaken/U
+shakeout/SM
+shaker/M
+Shaker/S
+Shakespearean/S
+Shakespeare/M
+Shakespearian
+shake/SRGZB
+shakeup/S
+shakily
+shakiness/S
+shaking/M
+shaky/TPR
+shale/SM
+shall
+shallot/SM
+shallowness/SM
+shallow/STPGDRY
+Shalna/M
+Shalne/M
+shalom
+Shalom/M
+shalt
+shamanic
+shaman/SM
+shamble/DSG
+shambles/M
+shamefaced/Y
+shamefulness/S
+shameful/YP
+shamelessness/SM
+shameless/PY
+shame/SM
+sham/MDSG
+shammed
+shammer
+shamming
+shammy's
+shampoo/DRSMZG
+shampooer/M
+shamrock/SM
+Shamus/M
+Shana/M
+Shanan/M
+Shanda/M
+Shandee/M
+Shandeigh/M
+Shandie/M
+Shandra/M
+shandy/M
+Shandy/M
+Shane/M
+Shanghai/GM
+Shanghaiing/M
+shanghai/SDG
+Shanie/M
+Shani/M
+shank/SMDG
+Shannah/M
+Shanna/M
+Shannan/M
+Shannen/M
+Shannon/M
+Shanon/M
+shan't
+Shanta/M
+Shantee/M
+shantis
+Shantung/M
+shantung/MS
+shanty/SM
+shantytown/SM
+shape/AGDSR
+shaped/U
+shapelessness/SM
+shapeless/PY
+shapeliness/S
+shapely/RPT
+shaper/S
+shape's
+Shapiro/M
+sharable/U
+Sharai/M
+Shara/M
+shard/SM
+shareable
+sharecropped
+sharecropper/MS
+sharecropping
+sharecrop/S
+share/DSRGZMB
+shared/U
+shareholder/MS
+shareholding/S
+sharer/M
+shareware/S
+Shari'a
+Sharia/M
+sharia/SM
+Shari/M
+Sharity/M
+shark/SGMD
+sharkskin/SM
+Sharla/M
+Sharleen/M
+Sharlene/M
+Sharline/M
+Sharl/M
+Sharona/M
+Sharon/M
+Sharpe/M
+sharpen/ASGD
+sharpened/U
+sharpener/S
+sharper/M
+sharpie/SM
+Sharp/M
+sharpness/MS
+sharp/SGTZXPYRDN
+sharpshooter/M
+sharpshooting/M
+sharpshoot/JRGZ
+sharpy's
+Sharron/M
+Sharyl/M
+Shasta/M
+shat
+shatter/DSG
+shattering/Y
+shatterproof
+Shaughn/M
+Shaula/M
+Shauna/M
+Shaun/M
+shave/DSRJGZ
+shaved/U
+shaver/M
+Shavian
+shaving/M
+Shavuot/M
+Shawano/M
+shawl/SDMG
+shaw/M
+Shaw/M
+Shawna/M
+Shawnee/SM
+Shawn/M
+Shaylah/M
+Shayla/M
+Shaylyn/M
+Shaylynn/M
+Shay/M
+shay/MS
+Shayna/M
+Shayne/M
+Shcharansky/M
+sh/DRS
+sheaf/MDGS
+Shea/M
+shearer/M
+shear/RDGZS
+sheather/M
+sheathe/UGSD
+sheath/GJMDRS
+sheathing/M
+sheaths
+sheave/SDG
+sheaves/M
+Sheba/M
+shebang/MS
+Shebeli/M
+Sheboygan/M
+she'd
+shedding
+Shedir/M
+sheds
+shed's
+shed/U
+Sheelagh/M
+Sheelah/M
+Sheela/M
+Sheena/M
+sheen/MDGS
+sheeny/TRSM
+sheepdog/SM
+sheepfold/MS
+sheepherder/MS
+sheepishness/SM
+sheepish/YP
+sheep/M
+sheepskin/SM
+Sheeree/M
+sheerness/S
+sheer/PGTYRDS
+sheeting/M
+sheetlike
+sheet/RDMJSG
+Sheetrock
+Sheffielder/M
+Sheffield/RMZ
+Sheffie/M
+Sheff/M
+Sheffy/M
+sheikdom/SM
+sheikh's
+sheik/SM
+Sheilah/M
+Sheila/M
+shekel/MS
+Shelagh/M
+Shela/M
+Shelba/M
+Shelbi/M
+Shelby/M
+Shelden/M
+Sheldon/M
+shelf/MDGS
+Shelia/M
+she'll
+shellacked
+shellacking/MS
+shellac/S
+shelled/U
+Shelley/M
+shellfire/SM
+shellfish/SM
+Shellie/M
+Shelli/M
+Shell/M
+shell/RDMGS
+Shelly/M
+Shel/MY
+shelter/DRMGS
+sheltered/U
+shelterer/M
+Shelton/M
+shelve/JRSDG
+shelver/M
+shelves/M
+shelving/M
+she/M
+Shem/M
+Shena/M
+Shenandoah/M
+shenanigan/SM
+Shenyang/M
+Sheol/M
+Shepard/M
+shepherd/DMSG
+shepherdess/S
+Shepherd/M
+Shep/M
+Sheppard/M
+Shepperd/M
+Sheratan/M
+Sheraton/M
+sherbet/MS
+sherd's
+Sheree/M
+Sheridan/M
+Sherie/M
+sheriff/SM
+Sherill/M
+Sherilyn/M
+Sheri/M
+Sherline/M
+Sherlocke/M
+sherlock/M
+Sherlock/M
+Sher/M
+Sherman/M
+Shermie/M
+Sherm/M
+Shermy/M
+Sherpa/SM
+Sherrie/M
+Sherri/M
+Sherry/M
+sherry/MS
+Sherwin/M
+Sherwood/M
+Sherwynd/M
+Sherye/M
+Sheryl/M
+Shetland/S
+Shevardnadze/M
+shew/GSD
+shewn
+shh
+shiatsu/S
+shibboleth/M
+shibboleths
+shielded/U
+shielder/M
+shield/MDRSG
+Shields/M
+shiftily
+shiftiness/SM
+shiftlessness/S
+shiftless/PY
+shift/RDGZS
+shifty/TRP
+Shi'ite
+Shiite/SM
+Shijiazhuang
+Shikoku/M
+shill/DJSG
+shillelagh/M
+shillelaghs
+shilling/M
+Shillong/M
+Shiloh/M
+shimmed
+shimmer/DGS
+shimmery
+shimming
+shimmy/DSMG
+shim/SM
+Shina/M
+shinbone/SM
+shindig/MS
+shiner/M
+shine/S
+shingle/MDRSG
+shingler/M
+shinguard
+shininess/MS
+shining/Y
+shinned
+shinning
+shinny/GDSM
+shin/SGZDRM
+shinsplints
+Shintoism/S
+Shintoist/MS
+Shinto/MS
+shiny/PRT
+shipboard/MS
+shipborne
+shipbuilder/M
+shipbuild/RGZJ
+shipload/SM
+shipman/M
+shipmate/SM
+shipmen
+shipment/AMS
+shipowner/MS
+shippable
+shipped/A
+shipper/SM
+shipping/MS
+ship's
+shipshape
+ship/SLA
+shipwreck/GSMD
+shipwright/MS
+shipyard/MS
+Shiraz/M
+shire/MS
+shirker/M
+shirk/RDGZS
+Shirlee/M
+Shirleen/M
+Shirlene/M
+Shirley/M
+Shirline/M
+Shirl/M
+Shir/M
+shirr/GJDS
+shirtfront/S
+shirting/M
+shirt/JDMSG
+shirtless
+shirtmake/R
+shirtmaker/M
+shirtsleeve/MS
+shirttail/S
+shirtwaist/SM
+shit/S
+shitting
+shitty/RT
+Shiva/M
+shiverer/M
+shiver/GDR
+shivery
+shiv/SZRM
+shivved
+shivving
+shlemiel's
+Shmuel/M
+shoal/SRDMGT
+shoat/SM
+shocker/M
+shocking/Y
+Shockley/M
+shockproof
+shock/SGZRD
+shoddily
+shoddiness/SM
+shoddy/RSTP
+shod/U
+shoehorn/GSMD
+shoeing
+shoelace/MS
+shoemaker/M
+shoemake/RZ
+shoe/MS
+shoer's
+shoeshine/MS
+shoestring/MS
+shoetree/MS
+shogunate/SM
+shogun/MS
+Shoji/M
+Sholom/M
+shone
+shoo/DSG
+shoofly
+shook/SM
+shooter/M
+shootout/MS
+shoot/SJRGZ
+shopkeeper/M
+shopkeep/RGZ
+shoplifter/M
+shoplifting/M
+shoplift/SRDGZ
+shop/MS
+shopped/M
+shopper/M
+shoppe/RSDGZJ
+shopping/M
+shoptalk/SM
+shopworn
+shorebird/S
+shore/DSRGMJ
+shoreline/SM
+Shorewood/M
+shoring/M
+shortage/MS
+shortbread/MS
+shortcake/SM
+shortchange/DSG
+shortcoming/MS
+shortcrust
+shortcut/MS
+shortcutting
+shortener/M
+shortening/M
+shorten/RDGJ
+shortfall/SM
+shorthand/DMS
+Shorthorn/M
+shorthorn/MS
+shortie's
+shortish
+shortlist/GD
+Short/M
+shortness/MS
+short/SGTXYRDNP
+shortsightedness/S
+shortsighted/YP
+shortstop/MS
+shortwave/SM
+shorty/SM
+Shoshana/M
+Shoshanna/M
+Shoshone/SM
+Shostakovitch/M
+shotgunned
+shotgunner
+shotgunning
+shotgun/SM
+shot/MS
+shotted
+shotting
+shoulder/GMD
+shouldn't
+should/TZR
+shout/SGZRDM
+shove/DSRG
+shoveler/M
+shovelful/MS
+shovel/MDRSZG
+shover/M
+showbiz
+showbizzes
+showboat/SGDM
+showcase/MGSD
+showdown/MS
+shower/GDM
+showery/TR
+show/GDRZJS
+showgirl/SM
+showily
+showiness/MS
+showing/M
+showman/M
+showmanship/SM
+showmen
+shown
+showoff/S
+showpiece/SM
+showplace/SM
+showroom/MS
+showy/RTP
+shpt
+shrank
+shrapnel/SM
+shredded
+shredder/MS
+shredding
+shred/MS
+Shreveport/M
+shrewdness/SM
+shrewd/RYTP
+shrew/GSMD
+shrewishness/M
+shrewish/PY
+shrieker/M
+shriek/SGDRMZ
+shrift/SM
+shrike/SM
+shrill/DRTGPS
+shrillness/MS
+shrilly
+shrimp/MDGS
+shrine/SDGM
+shrinkage/SM
+shrinker/M
+shrinking/U
+shrink/SRBG
+shrivel/GSD
+shriven
+shrive/RSDG
+Shropshire/M
+shroud/GSMD
+shrubbed
+shrubbery/SM
+shrubbing
+shrubby/TR
+shrub/SM
+shrugged
+shrugging
+shrug/S
+shrunk/N
+shtick/S
+shucker/M
+shuck/SGMRD
+shucks/S
+shudder/DSG
+shuddery
+shuffleboard/MS
+shuffled/A
+shuffle/GDSRZ
+shuffles/A
+shuffling/A
+Shulman/M
+Shu/M
+shunned
+shunning
+shun/S
+shunter/M
+shunt/GSRD
+Shurlocke/M
+Shurlock/M
+Shurwood/M
+shush/SDG
+shutdown/MS
+shuteye/SM
+shutoff/M
+shutout/SM
+shut/S
+shutterbug/S
+shutter/DMGS
+shuttering/M
+shutting
+shuttlecock/MDSG
+shuttle/MGDS
+shy/DRSGTZY
+shyer
+shyest
+Shylockian/M
+Shylock/M
+shyness/SM
+shyster/SM
+Siamese/M
+Siam/M
+Siana/M
+Sianna/M
+Sian's
+Sibbie/M
+Sibby/M
+Sibeal/M
+Sibelius/M
+Sibella/M
+Sibelle/M
+Sibel/M
+Siberia/M
+Siberian/S
+sibilance/M
+sibilancy/M
+sibilant/SY
+Sibilla/M
+Sibley/M
+sibling/SM
+Sib/M
+Sibylla/M
+Sibylle/M
+sibylline
+Sibyl/M
+sibyl/SM
+Siciliana/M
+Sicilian/S
+Sicily/M
+sickbay/M
+sickbed/S
+sickener/M
+sickening/Y
+sicken/JRDG
+sicker/Y
+sick/GXTYNDRSP
+sickie/SM
+sickish/PY
+sickle/SDGM
+sickliness/M
+sickly/TRSDPG
+sickness/MS
+sicko/S
+sickout/S
+sickroom/SM
+sic/S
+sidearm/S
+sideband/MS
+sidebar/MS
+sideboard/SM
+sideburns
+sidecar/MS
+sided/A
+sidedness
+side/ISRM
+sidekick/MS
+sidelight/SM
+sideline/MGDRS
+sidelong
+sideman/M
+sidemen
+sidepiece/S
+sidereal
+sider/FA
+sides/A
+sidesaddle/MS
+sideshow/MS
+sidesplitting
+sidestepped
+sidestepping
+sidestep/S
+sidestroke/GMSD
+sideswipe/GSDM
+sidetrack/SDG
+sidewalk/MS
+sidewall/MS
+sidewards
+sideway/SM
+sidewinder/SM
+siding/SM
+sidle/DSG
+Sid/M
+Sidnee/M
+Sidney/M
+Sidoney/M
+Sidonia/M
+Sidonnie/M
+SIDS
+siege/GMDS
+Siegel/M
+Siegfried/M
+Sieglinda/M
+Siegmund/M
+Siemens/M
+Siena/M
+sienna/SM
+Sierpinski/M
+sierra/SM
+siesta/MS
+sieve/GZMDS
+Siffre/M
+sifted/UA
+sifter/M
+sift/GZJSDR
+Sigfrid/M
+Sigfried/M
+SIGGRAPH/M
+sigh/DRG
+sigher/M
+sighs
+sighted/P
+sighter/M
+sighting/S
+sight/ISM
+sightless/Y
+sightliness/UM
+sightly/TURP
+sightread
+sightseeing/S
+sightsee/RZ
+Sigismond/M
+Sigismondo/M
+Sigismund/M
+Sigismundo/M
+Sig/M
+sigma/SM
+sigmoid
+Sigmund/M
+signal/A
+signaled
+signaler/S
+signaling
+signalization/S
+signalize/GSD
+signally
+signalman/M
+signalmen
+signals
+signal's
+signatory/SM
+signature/MS
+signboard/MS
+signed/FU
+signer/SC
+signet/SGMD
+sign/GARDCS
+significance/IMS
+significantly/I
+significant/YS
+signification/M
+signify/DRSGNX
+signing/S
+Signora/M
+signora/SM
+signore/M
+signori
+signories
+signorina/SM
+signorine
+Signor/M
+signor/SFM
+signpost/DMSG
+sign's
+signs/F
+Sigrid/M
+Sigurd/M
+Sigvard/M
+Sihanouk/M
+Sikhism/MS
+Sikh/MS
+Sikhs
+Sikkimese
+Sikkim/M
+Sikorsky/M
+silage/GMSD
+Silas/M
+Sileas/M
+siled
+Sile/M
+silence/MZGRSD
+silencer/M
+silentness/M
+silent/TSPRY
+Silesia/M
+silhouette/GMSD
+silica/SM
+silicate/SM
+siliceous
+silicide/M
+silicone/SM
+silicon/MS
+silicoses
+silicosis/M
+silken/DG
+silk/GXNDMS
+silkily
+silkiness/SM
+silkscreen/SM
+silkworm/MS
+silky/RSPT
+silliness/SM
+sill/MS
+silly/PRST
+silo/GSM
+siltation/M
+silt/MDGS
+siltstone/M
+silty/RT
+Silurian/S
+Silvain/M
+Silva/M
+Silvana/M
+Silvan/M
+Silvano/M
+Silvanus/M
+silverer/M
+silverfish/MS
+Silverman/M
+silver/RDYMGS
+silversmith/M
+silversmiths
+Silverstein/M
+silverware/SM
+silvery/RTP
+Silvester/M
+Silvia/M
+Silvie/M
+Silvio/M
+Si/M
+SIMD
+Simenon/M
+Simeon/M
+simian/S
+similar/EY
+similarity/EMS
+simile/SM
+similitude/SME
+Simla/M
+simmer/GSD
+Simmonds/M
+Simmons/M
+Simmonsville/M
+Sim/MS
+Simms/M
+Simona/M
+Simone/M
+Simonette/M
+simonize/SDG
+Simon/M
+Simonne/M
+simony/MS
+simpatico
+simper/GDS
+simpleminded/YP
+simpleness/S
+simple/RSDGTP
+simpleton/SM
+simplex/S
+simplicity/MS
+simplified/U
+simplify/ZXRSDNG
+simplistic
+simplistically
+simply
+Simpson/M
+simulacrum/M
+Simula/M
+SIMULA/M
+simulate/XENGSD
+simulation/ME
+simulative
+simulator/SEM
+simulcast/GSD
+simultaneity/SM
+simultaneousness/M
+simultaneous/YP
+Sinai/M
+Sinatra/M
+since
+sincere/IY
+sincereness/M
+sincerer
+sincerest
+sincerity/MIS
+Sinclair/M
+Sinclare/M
+Sindbad/M
+Sindee/M
+Sindhi/M
+sinecure/MS
+sinecurist/M
+sine/SM
+sinew/SGMD
+sinewy
+sinfulness/SM
+sinful/YP
+Singaporean/S
+Singapore/M
+sing/BGJZYDR
+Singborg/M
+singeing
+singer/M
+Singer/M
+singe/S
+singing/Y
+singlehanded/Y
+singleness/SM
+single/PSDG
+Singleton/M
+singleton/SM
+singletree/SM
+singlet/SM
+singsong/GSMD
+singularity/SM
+singularization/M
+singular/SY
+Sinhalese/M
+sinisterness/M
+sinister/YP
+sinistral/Y
+sinkable/U
+sinker/M
+sink/GZSDRB
+sinkhole/SM
+Sinkiang/M
+sinking/M
+sinlessness/M
+sinless/YP
+sin/MAGS
+sinned
+sinner/MS
+sinning
+sinter/DM
+sinuosity/MS
+sinuousities
+sinuousness/M
+sinuous/PY
+sinusitis/SM
+sinus/MS
+sinusoidal/Y
+sinusoid/MS
+Siobhan/M
+Siouxie/M
+Sioux/M
+siphon/DMSG
+siphons/U
+sipped
+sipper/SM
+sipping
+sip/S
+sired/C
+sire/MS
+siren/M
+sires/C
+siring/C
+Sirius/M
+sirloin/MS
+Sir/MS
+sirocco/MS
+sirred
+sirring
+sirup's
+sir/XGMNDS
+sisal/MS
+Sisely/M
+Sisile/M
+sis/S
+Sissie/M
+sissified
+Sissy/M
+sissy/TRSM
+sister/GDYMS
+sisterhood/MS
+sisterliness/MS
+sisterly/P
+sister's/A
+Sistine
+Sisyphean
+Sisyphus/M
+sit/AG
+sitarist/SM
+sitar/SM
+sitcom/SM
+site/DSJM
+sits
+sitter/MS
+sitting/SM
+situate/GNSDX
+situational/Y
+situationist
+situation/M
+situ/S
+situs/M
+Siusan/M
+Siva/M
+Siward/M
+sixfold
+sixgun
+six/MRSH
+sixpence/MS
+sixpenny
+sixshooter
+sixteen/HRSM
+sixteenths
+sixths
+sixth/Y
+sixtieths
+sixty/SMH
+sizableness/M
+sizable/P
+sized/UA
+size/GJDRSBMZ
+sizer/M
+sizes/A
+sizing/M
+sizzler/M
+sizzle/RSDG
+SJ
+Sjaelland/M
+SK
+ska/S
+skateboard/SJGZMDR
+skater/M
+skate/SM
+skat/JMDRGZ
+skedaddle/GSD
+skeet/RMS
+skein/MDGS
+skeletal/Y
+skeleton/MS
+Skell/M
+Skelly/M
+skeptical/Y
+skepticism/MS
+skeptic/SM
+sketchbook/SM
+sketcher/M
+sketchily
+sketchiness/MS
+sketch/MRSDZG
+sketchpad
+sketchy/PRT
+skew/DRSPGZ
+skewer/GDM
+skewing/M
+skewness/M
+skidded
+skidding
+skid/S
+skiff/GMDS
+skiing/M
+skilfully
+skill/DMSG
+skilled/U
+skillet/MS
+skillfulnesses
+skillfulness/MU
+skillful/YUP
+skilling/M
+skimmed
+skimmer/MS
+skimming/SM
+ski/MNJSG
+skimp/GDS
+skimpily
+skimpiness/MS
+skimpy/PRT
+skim/SM
+skincare
+skindive/G
+skinflint/MS
+skinhead/SM
+skinless
+skinned
+Skinner/M
+skinner/SM
+skinniness/MS
+skinning
+skinny/TRSP
+skin/SM
+skintight
+Skip/M
+skipped
+Skipper/M
+skipper/SGDM
+Skippie/M
+skipping
+Skipp/RM
+Skippy/M
+skip/S
+Skipton/M
+skirmisher/M
+skirmish/RSDMZG
+skirter/M
+skirting/M
+skirt/RDMGS
+skit/GSMD
+skitter/SDG
+skittishness/SM
+skittish/YP
+skittle/SM
+skivvy/GSDM
+skoal/SDG
+Skopje/M
+skulduggery/MS
+skulker/M
+skulk/SRDGZ
+skullcap/MS
+skullduggery's
+skull/SDM
+skunk/GMDS
+skycap/MS
+skydiver/SM
+skydiving/MS
+Skye/M
+skyhook
+skyjacker/M
+skyjack/ZSGRDJ
+Skylab/M
+skylarker/M
+skylark/SRDMG
+Skylar/M
+Skyler/M
+skylight/MS
+skyline/MS
+Sky/M
+sky/MDRSGZ
+skyrocket/GDMS
+skyscraper/M
+skyscrape/RZ
+skyward/S
+skywave
+skyway/M
+skywriter/MS
+skywriting/MS
+slabbed
+slabbing
+slab/MS
+slacken/DG
+slacker/M
+slackness/MS
+slack/SPGTZXYRDN
+Slade/M
+slagged
+slagging
+slag/MS
+slain
+slake/DSG
+slaked/U
+slalom/SGMD
+slammed
+slammer/S
+slamming
+slam/S
+slander/MDRZSG
+slanderousness/M
+slanderous/PY
+slang/SMGD
+slangy/TR
+slanting/Y
+slant/SDG
+slantwise
+slapdash/S
+slaphappy/TR
+slap/MS
+slapped
+slapper
+slapping
+slapstick/MS
+slash/GZRSD
+slashing/Y
+slater/M
+Slater/M
+slate/SM
+slather/SMDG
+slating/M
+slat/MDRSGZ
+slatted
+slattern/MYS
+slatting
+slaughterer/M
+slaughterhouse/SM
+slaughter/SJMRDGZ
+slave/DSRGZM
+slaveholder/SM
+slaver/GDM
+slavery/SM
+Slavic/M
+slavishness/SM
+slavish/YP
+Slav/MS
+Slavonic/M
+slaw/MS
+slay/RGZS
+sleaze/S
+sleazily
+sleaziness/SM
+sleazy/RTP
+sledded
+sledder/S
+sledding
+sledgehammer/MDGS
+sledge/SDGM
+sled/SM
+sleekness/S
+sleek/PYRDGTS
+sleeper/M
+sleepily
+sleepiness/SM
+sleeping/M
+sleeplessness/SM
+sleepless/YP
+sleepover/S
+sleep/RMGZS
+sleepwalker/M
+sleepwalk/JGRDZS
+sleepwear/M
+sleepyhead/MS
+sleepy/PTR
+sleet/DMSG
+sleety/TR
+sleeveless
+sleeve/SDGM
+sleeving/M
+sleigh/GMD
+sleighs
+sleight/SM
+sleken/DG
+slenderize/DSG
+slenderness/MS
+slender/RYTP
+slept
+Slesinger/M
+sleuth/GMD
+sleuths
+slew/DGS
+slice/DSRGZM
+sliced/U
+slicer/M
+slicker/M
+slickness/MS
+slick/PSYRDGTZ
+slider/M
+slide/S
+slid/GZDR
+slight/DRYPSTG
+slighter/M
+slighting/Y
+slightness/S
+slime/SM
+sliminess/S
+slimline
+slimmed
+slimmer/S
+slimmest
+slimming/S
+slimness/S
+slim/SPGYD
+slimy/PTR
+sling/GMRS
+slingshot/MS
+slings/U
+slink/GS
+slinky/RT
+slipcase/MS
+slipcover/GMDS
+slipknot/SM
+slippage/SM
+slipped
+slipper/GSMD
+slipperiness/S
+slippery/PRT
+slipping
+slipshod
+slip/SM
+slipstream/MDGS
+slipway/SM
+slither/DSG
+slithery
+slit/SM
+slitted
+slitter/S
+slitting
+sliver/GSDM
+slivery
+Sloane/M
+Sloan/M
+slobber/SDG
+slobbery
+slob/MS
+Slocum/M
+sloe/MS
+sloganeer/MG
+slogan/MS
+slogged
+slogging
+slog/S
+sloop/SM
+slop/DRSGZ
+sloped/U
+slope/S
+slopped
+sloppily
+sloppiness/SM
+slopping
+sloppy/RTP
+slosh/GSDM
+slothfulness/MS
+slothful/PY
+sloth/GDM
+sloths
+slot/MS
+slotted
+slotting
+slouch/DRSZG
+sloucher/M
+slouchy/RT
+slough/GMD
+sloughs
+Slovakia/M
+Slovakian/S
+Slovak/S
+Slovene/S
+Slovenia/M
+Slovenian/S
+slovenliness/SM
+slovenly/TRP
+sloven/YMS
+slowcoaches
+slowdown/MS
+slowish
+slowness/MS
+slow/PGTYDRS
+slowpoke/MS
+SLR
+sludge/SDGM
+sludgy/TR
+slue/MGDS
+sluggard/MS
+slugged
+slugger/SM
+slugging
+sluggishness/SM
+sluggish/YP
+slug/MS
+sluice/SDGM
+slumberer/M
+slumber/MDRGS
+slumberous
+slumlord/MS
+slummed
+slummer
+slumming
+slum/MS
+slummy/TR
+slump/DSG
+slung/U
+slunk
+slur/MS
+slurp/GSD
+slurred
+slurried/M
+slurring
+slurrying/M
+slurry/MGDS
+slushiness/SM
+slush/SDMG
+slushy/RTP
+slut/MS
+sluttish
+slutty/TR
+Sly/M
+slyness/MS
+sly/RTY
+smacker/M
+smack/SMRDGZ
+smallholders
+smallholding/MS
+smallish
+Small/M
+smallness/S
+smallpox/SM
+small/SGTRDP
+smalltalk
+smalltime
+Smallwood/M
+smarmy/RT
+smarten/GD
+smartness/S
+smartypants
+smart/YRDNSGTXP
+smasher/M
+smash/GZRSD
+smashing/Y
+smashup/S
+smattering/SM
+smearer/M
+smear/GRDS
+smeary/TR
+smeller/M
+smelliness/MS
+smell/SBRDG
+smelly/TRP
+smelter/M
+smelt/SRDGZ
+Smetana/M
+smidgen/MS
+smilax/MS
+smile/GMDSR
+smiley/M
+smilies
+smiling/UY
+smirch/SDG
+smirk/GSMD
+Smirnoff/M
+smite/GSR
+smiter/M
+smith/DMG
+smithereens
+Smithfield/M
+Smith/M
+smiths
+Smithsonian/M
+Smithson/M
+Smithtown/M
+smithy/SM
+smitten
+Smitty/M
+Sm/M
+smocking/M
+smock/SGMDJ
+smoggy/TR
+smog/SM
+smoke/GZMDSRBJ
+smokehouse/MS
+smokeless
+smoker/M
+smokescreen/S
+smokestack/MS
+Smokey/M
+smokiness/S
+smoking/M
+smoky/RSPT
+smoldering/Y
+smolder/SGD
+Smolensk/M
+Smollett/M
+smooch/SDG
+smoothen/DG
+smoother/M
+smoothie/SM
+smoothness/MS
+smooths
+smooth/TZGPRDNY
+smörgåsbord/SM
+smote
+smother/GSD
+SMSA/MS
+SMTP
+Smucker/M
+smudge/GSD
+smudginess/M
+smudgy/TRP
+smugged
+smugger
+smuggest
+smugging
+smuggle/JZGSRD
+smuggler/M
+smugness/MS
+smug/YSP
+smut/SM
+Smuts/M
+smutted
+smuttiness/SM
+smutting
+smutty/TRP
+Smyrna/M
+snack/SGMD
+snaffle/GDSM
+snafu/DMSG
+snagged
+snagging
+snag/MS
+snail/GSDM
+Snake
+snakebird/M
+snakebite/MS
+snake/DSGM
+snakelike
+snakeroot/M
+snaky/TR
+snapback/M
+snapdragon/MS
+snapped/U
+snapper/SM
+snappily
+snappiness/SM
+snapping/U
+snappishness/SM
+snappish/PY
+snappy/PTR
+snapshot/MS
+snapshotted
+snapshotting
+snap/US
+snare/DSRGM
+snarer/M
+snarf/JSGD
+snarler/M
+snarling/Y
+snarl/UGSD
+snarly/RT
+snatch/DRSZG
+snatcher/M
+snazzily
+snazzy/TR
+Snead/M
+sneaker/MD
+sneakily
+sneakiness/SM
+sneaking/Y
+sneak/RDGZS
+sneaky/PRT
+Sneed/M
+sneerer/M
+sneer/GMRDJS
+sneering/Y
+sneeze/SRDG
+Snell/M
+snicker/GMRD
+snick/MRZ
+snideness/M
+Snider/M
+snide/YTSRP
+sniffer/M
+sniff/GZSRD
+sniffle/GDRS
+sniffler/M
+sniffles/M
+snifter/MDSG
+snigger's
+sniper/M
+snipe/SM
+snipped
+snipper/SM
+snippet/SM
+snipping
+snippy/RT
+snip/SGDRZ
+snitch/GDS
+snit/SM
+sniveler/M
+snivel/JSZGDR
+Sn/M
+snobbery/SM
+snobbishness/S
+snobbish/YP
+snobby/RT
+snob/MS
+Snodgrass/M
+snood/SGDM
+snooker/GMD
+snook/SMRZ
+snooper/M
+snoop/SRDGZ
+Snoopy/M
+snoopy/RT
+snootily
+snootiness/MS
+snoot/SDMG
+snooty/TRP
+snooze/GSD
+snore/DSRGZ
+snorkel/ZGSRDM
+snorter/M
+snort/GSZRD
+snot/MS
+snotted
+snottily
+snottiness/SM
+snotting
+snotty/TRP
+snout/SGDM
+snowball/SDMG
+snowbank/SM
+Snowbelt/SM
+snowbird/SM
+snowblower/S
+snowboard/GZDRJS
+snowbound
+snowcapped
+snowdrift/MS
+snowdrop/MS
+snowfall/MS
+snowfield/MS
+snowflake/MS
+snow/GDMS
+snowily
+snowiness/MS
+Snow/M
+snowman/M
+snowmen
+snowmobile/GMDRS
+snowplough/M
+snowploughs
+snowplow/SMGD
+snowshed
+snowshoeing
+snowshoe/MRS
+snowshoer/M
+snowstorm/MS
+snowsuit/S
+snowy/RTP
+snubbed
+snubber
+snubbing
+snub/SP
+snuffbox/SM
+snuffer/M
+snuff/GZSYRD
+snuffle/GDSR
+snuffler/M
+snuffly/RT
+snugged
+snugger
+snuggest
+snugging
+snuggle/GDS
+snuggly
+snugness/MS
+snug/SYP
+Snyder/M
+so
+SO
+soaker/M
+soak/GDRSJ
+soapbox/DSMG
+soapiness/S
+soap/MDRGS
+soapstone/MS
+soapsud/S
+soapy/RPT
+soar/DRJSG
+soarer/M
+soaring/Y
+sobbed
+sobbing/Y
+soberer/M
+soberness/SM
+sober/PGTYRD
+sobriety/SIM
+sobriquet/MS
+sob/SZR
+Soc
+soccer/MS
+sociabilities
+sociability/IM
+sociable/S
+sociably/IU
+socialism/SM
+socialistic
+socialist/SM
+socialite/SM
+sociality/M
+socialization/SM
+socialized/U
+socializer/M
+socialize/RSDG
+socially/U
+social/SY
+societal/Y
+society/MS
+socio
+sociobiology/M
+sociocultural/Y
+sociodemographic
+socioeconomically
+socioeconomic/S
+sociolinguistics/M
+sociological/MY
+sociologist/SM
+sociology/SM
+sociometric
+sociometry/M
+sociopath/M
+sociopaths
+socket/SMDG
+sock/GDMS
+Socorro/M
+Socrates/M
+Socratic/S
+soc/S
+soda/SM
+sodded
+sodden/DYPSG
+soddenness/M
+sodding
+Soddy/M
+sodium/MS
+sod/MS
+sodomite/MS
+sodomize/GDS
+Sodom/M
+sodomy/SM
+soever
+sofa/SM
+Sofia/M
+Sofie/M
+softball/MS
+softbound
+softener/M
+soften/ZGRD
+softhearted
+softie's
+softness/MS
+soft/SPXTYNR
+software/MS
+softwood/SM
+softy/SM
+soggily
+sogginess/S
+soggy/RPT
+Soho/M
+soigné
+soiled/U
+soil/SGMD
+soirée/SM
+sojourn/RDZGSM
+solace/GMSRD
+solacer/M
+solaria
+solarium/M
+solar/S
+solder/RDMSZG
+soldier/MDYSG
+soldiery/MS
+sold/RU
+solecism/MS
+soled/FA
+solemness
+solemnify/GSD
+solemnity/MS
+solemnization/SM
+solemnize/GSD
+solemnness/SM
+solemn/PTRY
+solenoid/MS
+soler/F
+soles/IFA
+sole/YSP
+sol/GSMDR
+solicitation/S
+solicited/U
+solicitor/MS
+solicitousness/S
+solicitous/YP
+solicit/SDG
+solicitude/MS
+solidarity/MS
+solidi
+solidification/M
+solidify/NXSDG
+solidity/S
+solidness/SM
+solid/STYRP
+solidus/M
+soliloquies
+soliloquize/DSG
+soliloquy/M
+soling/NM
+solipsism/MS
+solipsist/S
+Solis/M
+solitaire/SM
+solitary/SP
+solitude/SM
+Sollie/M
+Solly/M
+Sol/MY
+solo/DMSG
+soloist/SM
+Solomon/SM
+Solon/M
+Soloviev/M
+solstice/SM
+solubility/IMS
+soluble/SI
+solute/ENAXS
+solute's
+solution/AME
+solvable/UI
+solvating
+solve/ABSRDZG
+solved/EU
+solvency/IMS
+solvent/IS
+solvently
+solvent's
+solver/MEA
+solves/E
+solving/E
+Solzhenitsyn/M
+Somalia/M
+Somalian/S
+Somali/MS
+soma/M
+somatic
+somberness/SM
+somber/PY
+sombre
+sombrero/SM
+somebody'll
+somebody/SM
+someday
+somehow
+someone'll
+someone/SM
+someplace/M
+somersault/DSGM
+Somerset/M
+somerset/S
+somersetted
+somersetting
+Somerville/M
+something/S
+sometime/S
+someway/S
+somewhat/S
+somewhere/S
+some/Z
+sommelier/SM
+Somme/M
+somnambulism/SM
+somnambulist/SM
+somnolence/MS
+somnolent/Y
+Somoza/M
+sonar/SM
+sonata/MS
+sonatina/SM
+Sondheim/M
+Sondra/M
+Sonenberg/M
+songbag
+songbird/SM
+songbook/S
+songfest/MS
+songfulness/M
+songful/YP
+Songhai/M
+Songhua/M
+song/MS
+songster/MS
+songstress/SM
+songwriter/SM
+songwriting
+Sonia/M
+sonic/S
+Sonja/M
+Son/M
+sonnet/MDSG
+Sonnie/M
+Sonni/M
+Sonnnie/M
+Sonny/M
+sonny/SM
+Sonoma/M
+Sonora/M
+sonority/S
+sonorousness/SM
+sonorous/PY
+son/SMY
+Sontag/M
+sonuvabitch
+Sonya/M
+Sony/M
+soonish
+soon/TR
+soothe
+soother/M
+sooth/GZTYSRDMJ
+soothingness/M
+soothing/YP
+sooths
+soothsayer/M
+soothsay/JGZR
+soot/MGDS
+sooty/RT
+SOP
+Sophey/M
+Sophia/SM
+Sophie/M
+Sophi/M
+sophism/SM
+sophister/M
+sophistical
+sophisticatedly
+sophisticated/U
+sophisticate/XNGDS
+sophistication/MU
+sophistic/S
+sophist/RMS
+sophistry/SM
+Sophoclean
+Sophocles/M
+sophomore/SM
+sophomoric
+Sophronia/M
+soporifically
+soporific/SM
+sopped
+sopping/S
+soppy/RT
+soprano/SM
+sop/SM
+Sopwith/M
+sorbet/SM
+Sorbonne/M
+sorcerer/MS
+sorceress/S
+sorcery/MS
+Sorcha/M
+sordidness/SM
+sordid/PY
+sorehead/SM
+soreness/S
+Sorensen/M
+Sorenson/M
+sore/PYTGDRS
+sorghum/MS
+sorority/MS
+sorrel/SM
+Sorrentine/M
+sorrily
+sorriness/SM
+sorrower/M
+sorrowfulness/SM
+sorrowful/YP
+sorrow/GRDMS
+sorry/PTSR
+sorta
+sortable
+sorted/U
+sorter/MS
+sort/FSAGD
+sortieing
+sortie/MSD
+sort's
+sos
+SOS
+Sosa/M
+Sosanna/M
+Soto/M
+sot/SM
+sottish
+soubriquet's
+soufflé/MS
+sough/DG
+soughs
+sought/U
+soulfulness/MS
+soulful/YP
+soulless/Y
+soul/MDS
+sound/AUD
+soundboard/MS
+sounders
+sounder's
+sounder/U
+soundest
+sounding/AY
+soundings
+sounding's
+soundless/Y
+soundly/U
+soundness/UMS
+soundproof/GSD
+soundproofing/M
+sound's
+sounds/A
+soundtrack/MS
+soupçon/SM
+soup/GMDS
+Souphanouvong/M
+soupy/RT
+source/ASDMG
+sourceless
+sourdough
+sourdoughs
+sourish
+sourness/MS
+sourpuss/MS
+sour/TYDRPSG
+Sousa/M
+sousaphone/SM
+sous/DSG
+souse
+sou/SMH
+Southampton/M
+southbound
+southeastern
+southeaster/YM
+Southeast/MS
+southeast/RZMS
+southeastward/S
+southerly/S
+souther/MY
+southerner/M
+Southerner/MS
+southernisms
+southernmost
+southern/PZSYR
+Southey/M
+Southfield/M
+southing/M
+southland/M
+South/M
+southpaw/MS
+south/RDMG
+souths
+Souths
+southward/S
+southwestern
+southwester/YM
+Southwest/MS
+southwest/RMSZ
+southwestward/S
+souvenir/SM
+sou'wester
+sovereignty/MS
+sovereign/YMS
+soviet/MS
+Soviet/S
+sow/ADGS
+sowbelly/M
+sowens/M
+sower/DS
+Soweto/M
+sown/A
+sox's
+soybean/MS
+Soyinka/M
+soy/MS
+Soyuz/M
+Spaatz/M
+spacecraft/MS
+space/DSRGZMJ
+spaceflight/S
+spaceman/M
+spacemen
+spaceport/SM
+spacer/M
+spaceship/MS
+spacesuit/MS
+spacewalk/GSMD
+Spacewar/M
+spacewoman
+spacewomen
+spacey
+spacial
+spacier
+spaciest
+spaciness
+spacing/M
+spaciousness/SM
+spacious/PY
+Spackle
+spade/DSRGM
+spadeful/SM
+spader/M
+spadework/SM
+spadices
+spadix/M
+Spafford/M
+spaghetti/SM
+Spahn/M
+Spain/M
+spake
+Spalding/M
+Spam/M
+spa/MS
+Span
+spandex/MS
+spandrels
+spangle/GMDS
+Spanglish/S
+Spaniard/SM
+spanielled
+spanielling
+spaniel/SM
+Spanish/M
+spanker/M
+spanking/M
+spank/SRDJG
+span/MS
+spanned/U
+spanner/SM
+spanning
+SPARC/M
+SPARCstation/M
+spar/DRMGTS
+spareness/MS
+spare/PSY
+spareribs
+sparer/M
+sparing/UY
+sparker/M
+sparkle/DRSGZ
+sparkler/M
+Sparkman/M
+Sparks
+spark/SGMRD
+sparky/RT
+sparling/SM
+sparred
+sparrer
+sparring/U
+sparrow/MS
+sparseness/S
+sparse/YP
+sparsity/S
+spars/TR
+Spartacus/M
+Sparta/M
+spartan
+Spartan/S
+spasm/GSDM
+spasmodic
+spasmodically
+spastic/S
+spate/SM
+spathe/MS
+spatiality/M
+spatial/Y
+spat/MS
+spatted
+spatter/DGS
+spatterdock/M
+spatting
+spatula/SM
+spavin/DMS
+spawner/M
+spawn/MRDSG
+spay/DGS
+SPCA
+speakable/U
+speakeasy/SM
+speaker/M
+Speaker's
+speakership/M
+speaking/U
+speak/RBGZJS
+spearer/M
+spearfish/SDMG
+spearhead/GSDM
+spearmint/MS
+spear/MRDGS
+Spears
+spec'd
+specialism/MS
+specialist/MS
+specialization/SM
+specialized/U
+specialize/GZDSR
+specializing/U
+special/SRYP
+specialty/MS
+specie/MS
+specif
+specifiability
+specifiable
+specifiably
+specifically
+specification/SM
+specificity/S
+specific/SP
+specified/U
+specifier/SM
+specifies
+specify/AD
+specifying
+specimen/SM
+spec'ing
+speciousness/SM
+specious/YP
+speck/GMDS
+speckle/GMDS
+spec/SM
+spectacle/MSD
+spectacular/SY
+spectator/SM
+specter/DMS
+specter's/A
+spectralness/M
+spectral/YP
+spectra/M
+spectrogram/MS
+spectrographically
+spectrograph/M
+spectrography/M
+spectrometer/MS
+spectrometric
+spectrometry/M
+spectrophotometer/SM
+spectrophotometric
+spectrophotometry/M
+spectroscope/SM
+spectroscopic
+spectroscopically
+spectroscopy/SM
+spectrum/M
+specularity
+specular/Y
+speculate/VNGSDX
+speculation/M
+speculative/Y
+speculator/SM
+sped
+speech/GMDS
+speechlessness/SM
+speechless/YP
+speedboat/GSRM
+speedboating/M
+speeder/M
+speedily
+speediness/SM
+speedometer/MS
+speed/RMJGZS
+speedster/SM
+speedup/MS
+speedway/SM
+speedwell/MS
+speedy/PTR
+speer/M
+speleological
+speleologist/S
+speleology/MS
+spellbinder/M
+spellbind/SRGZ
+spellbound
+spelldown/MS
+spelled/A
+speller/M
+spelling/M
+spell/RDSJGZ
+spells/A
+spelunker/MS
+spelunking/S
+Spencerian
+Spencer/M
+Spence/RM
+spender/M
+spend/SBJRGZ
+spendthrift/MS
+Spenglerian
+Spengler/M
+Spense/MR
+Spenserian
+Spenser/M
+spent/U
+spermatophyte/M
+spermatozoa
+spermatozoon/M
+spermicidal
+spermicide/MS
+sperm/SM
+Sperry/M
+spew/DRGZJS
+spewer/M
+SPF
+sphagnum/SM
+sphere/SDGM
+spherical/Y
+spheric/S
+spherics/M
+spheroidal/Y
+spheroid/SM
+spherule/MS
+sphincter/SM
+Sphinx/M
+sphinx/MS
+Spica/M
+spic/DGM
+spicebush/M
+spice/SM
+spicily
+spiciness/SM
+spicule/MS
+spicy/PTR
+spider/SM
+spiderweb/S
+spiderwort/M
+spidery/TR
+Spiegel/M
+Spielberg/M
+spiel/GDMS
+spier/M
+spiffy/TDRSG
+spigot/MS
+spike/GMDSR
+Spike/M
+spiker/M
+spikiness/SM
+spiky/PTR
+spillage/SM
+Spillane/M
+spillover/SM
+spill/RDSG
+spillway/SM
+spinach/MS
+spinal/YS
+spindle/JGMDRS
+spindly/RT
+spinelessness/M
+spineless/YP
+spine/MS
+spinet/SM
+spininess/M
+spinnability/M
+spinnaker/SM
+spinneret/MS
+spinner/SM
+spinning/SM
+Spinoza/M
+spin/S
+spinsterhood/SM
+spinsterish
+spinster/MS
+spiny/PRT
+spiracle/SM
+spiraea's
+spiral/YDSG
+spire/AIDSGF
+spirea/MS
+spire's
+spiritedness/M
+spirited/PY
+spirit/GMDS
+spiritless
+spirits/I
+spiritualism/SM
+spiritualistic
+spiritualist/SM
+spirituality/SM
+spiritual/SYP
+spirituous
+spirochete/SM
+Spiro/M
+spiry/TR
+spitball/SM
+spite/CSDAG
+spitefuller
+spitefullest
+spitefulness/MS
+spiteful/PY
+spite's/A
+spitfire/SM
+spit/SGD
+spitted
+spitting
+spittle/SM
+spittoon/SM
+Spitz/M
+splashdown/MS
+splasher/M
+splash/GZDRS
+splashily
+splashiness/MS
+splashy/RTP
+splat/SM
+splatted
+splatter/DSG
+splatting
+splayfeet
+splayfoot/MD
+splay/SDG
+spleen/SM
+splendidness/M
+splendid/YRPT
+splendorous
+splendor/SM
+splenetic/S
+splicer/M
+splice/RSDGZJ
+spline/MSD
+splinter/GMD
+splintery
+splint/SGZMDR
+splits/M
+split/SM
+splittable
+splitter/MS
+splitting/S
+splodge/SM
+splotch/MSDG
+splotchy/RT
+splurge/GMDS
+splutterer/M
+splutter/RDSG
+Sp/M
+Spock/M
+spoilables
+spoilage/SM
+spoil/CSZGDR
+spoiled/U
+spoiler/MC
+spoilsport/SM
+Spokane/M
+spoke/DSG
+spoken/U
+spokeshave/MS
+spokesman/M
+spokesmen
+spokespeople
+spokesperson/S
+spokeswoman/M
+spokeswomen
+spoliation/MCS
+spongecake
+sponge/GMZRSD
+sponger/M
+sponginess/S
+spongy/TRP
+sponsor/DGMS
+sponsorship/S
+spontaneity/SM
+spontaneousness/M
+spontaneous/PY
+spoof/SMDG
+spookiness/MS
+spook/SMDG
+spooky/PRT
+spool/SRDMGZ
+spoonbill/SM
+spoonerism/SM
+spoonful/MS
+spoon/GSMD
+spoor/GSMD
+sporadically
+sporadic/Y
+spore/DSGM
+sporran/MS
+sportiness/SM
+sporting/Y
+sportiveness/M
+sportive/PY
+sportscast/RSGZM
+sportsmanlike/U
+sportsman/MY
+sportsmanship/MS
+sportsmen
+sportswear/M
+sportswoman/M
+sportswomen
+sportswriter/S
+sport/VGSRDM
+sporty/PRT
+Sposato/M
+spotlessness/MS
+spotless/YP
+spotlight/GDMS
+spotlit
+spot/MSC
+spotted/U
+spotter/MS
+spottily
+spottiness/SM
+spotting/M
+spotty/RTP
+spousal/MS
+spouse/GMSD
+spouter/M
+spout/SGRD
+sprain/SGD
+sprang/S
+sprat/SM
+sprawl/GSD
+sprayed/UA
+sprayer/M
+spray/GZSRDM
+sprays/A
+spreadeagled
+spreader/M
+spread/RSJGZB
+spreadsheet/S
+spreeing
+spree/MDS
+sprigged
+sprigging
+sprightliness/MS
+sprightly/PRT
+sprig/MS
+springboard/MS
+springbok/MS
+springeing
+springer/M
+Springfield/M
+springily
+springiness/SM
+springing/M
+springlike
+spring/SGZR
+Springsteen/M
+springtime/MS
+springy/TRP
+sprinkle/DRSJZG
+sprinkler/DM
+sprinkling/M
+Sprint/M
+sprint/SGZMDR
+sprite/SM
+spritz/GZDSR
+sprocket/DMGS
+sprocketed/U
+Sproul/M
+sprout/GSD
+spruce/GMTYRSDP
+spruceness/SM
+sprue/M
+sprung/U
+spryness/S
+spry/TRY
+SPSS
+spudded
+spudding
+spud/MS
+Spuds/M
+spume/DSGM
+spumone's
+spumoni/S
+spumy/TR
+spun
+spunk/GSMD
+spunky/SRT
+spurge/MS
+spuriousness/SM
+spurious/PY
+spur/MS
+spurn/RDSG
+spurred
+spurring
+spurt/SGD
+sputa
+Sputnik
+sputnik/MS
+sputter/DRGS
+sputum/M
+spy/DRSGM
+spyglass/MS
+sq
+sqq
+sqrt
+squabbed
+squabber
+squabbest
+squabbing
+squabbler/M
+squabble/ZGDRS
+squab/SM
+squadded
+squadding
+squadron/MDGS
+squad/SM
+squalidness/SM
+squalid/PRYT
+squaller/M
+squall/GMRDS
+squally/RT
+squalor/SM
+squamous/Y
+squander/GSRD
+Squanto
+square/GMTYRSDP
+squareness/SM
+squarer/M
+Squaresville/M
+squarish
+squash/GSRD
+squashiness/M
+squashy/RTP
+squatness/MS
+squat/SPY
+squatted
+squatter/SMDG
+squattest
+squatting
+squawker/M
+squawk/GRDMZS
+squaw/SM
+squeaker/M
+squeakily
+squeakiness/S
+squeak/RDMGZS
+squeaky/RPT
+squealer/M
+squeal/MRDSGZ
+squeamishness/SM
+squeamish/YP
+squeegee/DSM
+squeegeeing
+squeeze/GZSRDB
+squeezer/M
+squelcher/M
+squelch/GDRS
+squelchy/RT
+squibbed
+Squibb/GM
+squibbing
+Squibbing/M
+squib/SM
+squidded
+squidding
+squid/SM
+squiggle/MGDS
+squiggly/RT
+squinter/M
+squint/GTSRD
+squinting/Y
+squirehood
+squire/SDGM
+squirm/SGD
+squirmy/TR
+squirrel/SGYDM
+squirter/M
+squirt/GSRD
+squish/GSD
+squishy/RTP
+Sr
+Srinagar/M
+SRO
+S's
+SS
+SSA
+SSE
+ssh
+s's/KI
+SSS
+SST
+SSW
+ST
+stabbed
+stabber/S
+stabbing/S
+stability/ISM
+stabilizability
+stabilization/CS
+stabilization's
+stabilize/CGSD
+stabilizer/MS
+stableman/M
+stablemate
+stablemen
+stableness/UM
+stable/RSDGMTP
+stabler/U
+stable's/F
+stables/F
+stablest/U
+stabling/M
+stably/U
+stab/YS
+staccato/S
+Stacee/M
+Stace/M
+Stacey/M
+Stacia/M
+Stacie/M
+Staci/M
+stackable
+stacker/M
+stack's
+stack/USDG
+Stacy/M
+stadias
+stadia's
+stadium/MS
+Stael/M
+Stafani/M
+staff/ADSG
+Staffard/M
+staffer/MS
+Stafford/M
+Staffordshire/M
+staffroom
+staff's
+Staford/M
+stag/DRMJSGZ
+stagecoach/MS
+stagecraft/MS
+stagehand/MS
+stager/M
+stage/SM
+stagestruck
+stagflation/SM
+stagged
+staggerer/M
+stagger/GSJDR
+staggering/Y
+staggers/M
+stagging
+staginess/M
+staging/M
+stagnancy/SM
+stagnant/Y
+stagnate/NGDSX
+stagnation/M
+stagy/PTR
+Stahl/M
+staidness/MS
+staid/YRTP
+stained/U
+stainer/M
+stainless/YS
+stain/SGRD
+staircase/SM
+stair/MS
+stairway/SM
+stairwell/MS
+stake/DSGM
+stakeholder/S
+stakeout/SM
+stalactite/SM
+stalag/M
+stalagmite/SM
+stalemate/SDMG
+staleness/MS
+stale/PGYTDSR
+Staley/M
+Stalingrad/M
+Stalinist
+Stalin/SM
+stalker/M
+stalk/MRDSGZJ
+stall/DMSJG
+stalled/I
+stallholders
+stallion/SM
+Stallone/M
+stalls/I
+stalwartness/M
+stalwart/PYS
+Sta/M
+stamen/MS
+Stamford/M
+stamina/SM
+staminate
+stammer/DRSZG
+stammerer/M
+stammering/Y
+stampede/MGDRS
+stampeder/M
+stamped/U
+stamper/M
+stamp/RDSGZJ
+stance/MIS
+stancher/M
+stanch/GDRST
+stanchion/SGMD
+standalone
+standardization/AMS
+standardized/U
+standardize/GZDSR
+standardizer/M
+standardizes/A
+standard/YMS
+standby
+standbys
+standee/MS
+Standford/M
+standing/M
+Standish/M
+standoffish
+standoff/SM
+standout/MS
+standpipe/MS
+standpoint/SM
+stand/SJGZR
+standstill/SM
+Stanfield/M
+Stanford/M
+Stanislas/M
+Stanislaus/M
+Stanislavsky/M
+Stanislaw/M
+stank/S
+Stanleigh/M
+Stanley/M
+Stanly/M
+stannic
+stannous
+Stanton/M
+Stanwood/M
+Stan/YMS
+stanza/MS
+staph/M
+staphs
+staphylococcal
+staphylococci
+staphylococcus/M
+stapled/U
+stapler/M
+Stapleton/M
+staple/ZRSDGM
+starboard/SDMG
+starchily
+starchiness/MS
+starch/MDSG
+starchy/TRP
+stardom/MS
+star/DRMGZS
+stardust/MS
+stare/S
+starfish/SM
+Stargate/M
+stargaze/ZGDRS
+staring/U
+Starkey/M
+Stark/M
+starkness/MS
+stark/SPGTYRD
+Starla/M
+Starlene/M
+starless
+starlet/MS
+starlight/MS
+starling/MS
+Starlin/M
+starlit
+Star/M
+starred
+starring
+Starr/M
+starry/TR
+starship
+starstruck
+start/ASGDR
+starter/MS
+startle/GDS
+startling/PY
+startup/SM
+starvation/MS
+starveling/M
+starver/M
+starve/RSDG
+stash/GSD
+stasis/M
+stat/DRSGV
+statecraft/MS
+stated/U
+statehood/MS
+statehouse/S
+Statehouse's
+state/IGASD
+statelessness/MS
+stateless/P
+stateliness/MS
+stately/PRT
+statement/MSA
+Staten/M
+stater/M
+stateroom/SM
+stateside
+state's/K
+states/K
+statesmanlike
+statesman/MY
+statesmanship/SM
+statesmen
+stateswoman
+stateswomen
+statewide
+statical/Y
+static/S
+statics/M
+stationarity
+stationary/S
+stationer/M
+stationery/MS
+stationmaster/M
+station/SZGMDR
+statistical/Y
+statistician/MS
+statistic/MS
+Statler/M
+stator/SM
+statuary/SM
+statue/MSD
+statuesque/YP
+statuette/MS
+stature/MS
+status/SM
+statute/SM
+statutorily
+statutory/P
+Stauffer/M
+staunchness/S
+staunch/PDRSYTG
+stave/DGM
+Stavro/MS
+stay/DRGZS
+stayer/M
+std
+STD
+stdio
+steadfastness/MS
+steadfast/PY
+steadily/U
+steadiness's
+steadiness/US
+steading/M
+stead/SGDM
+steady/DRSUTGP
+steakhouse/SM
+steak/SM
+stealer/M
+stealing/M
+steal/SRHG
+stealthily
+stealthiness/MS
+stealth/M
+stealths
+stealthy/PTR
+steamboat/MS
+steamer/MDG
+steamfitter/S
+steamfitting/S
+steamily
+steaminess/SM
+steamroller/DMG
+steamroll/GZRDS
+steam/SGZRDMJ
+steamship/SM
+steamy/RSTP
+Stearne/M
+Stearn/SM
+steed/SM
+Steele/M
+steeliness/SM
+steelmaker/M
+steel/SDMGZ
+steelworker/M
+steelwork/ZSMR
+steelyard/MS
+steely/TPRS
+Steen/M
+steepen/GD
+steeper/M
+steeplebush/M
+steeplechase/GMSD
+steeplejack/MS
+steeple/MS
+steepness/S
+steep/SYRNDPGTX
+steerage/MS
+steerer/M
+steer/SGBRDJ
+steersman/M
+steersmen
+steeves
+Stefa/M
+Stefania/M
+Stefanie/M
+Stefan/M
+Stefano/M
+Steffane/M
+Steffen/M
+Steffie/M
+Steffi/M
+stegosauri
+stegosaurus/S
+Steinbeck/SM
+Steinberg/M
+Steinem/M
+Steiner/M
+Steinmetz/M
+Stein/RM
+stein/SGZMRD
+Steinway/M
+Stella/M
+stellar
+stellated
+Ste/M
+stemless
+stemmed/U
+stemming
+stem/MS
+stemware/MS
+stench/GMDS
+stenciler/M
+stencil/GDRMSZ
+stencillings
+Stendhal/M
+Stendler/M
+Stengel/M
+stenographer/SM
+stenographic
+stenography/SM
+steno/SM
+stenotype/M
+stentorian
+stepbrother/MS
+stepchild/M
+stepchildren
+stepdaughter/MS
+stepfather/SM
+Stepha/M
+Stephana/M
+Stephanie/M
+Stephani/M
+Stephan/M
+Stephannie/M
+Stephanus/M
+Stephenie/M
+Stephen/MS
+Stephenson/M
+Stephie/M
+Stephi/M
+Stephine/M
+stepladder/SM
+step/MIS
+stepmother/SM
+stepparent/SM
+stepper/M
+steppe/RSDGMZ
+steppingstone/S
+stepsister/SM
+stepson/SM
+stepwise
+stereographic
+stereography/M
+stereo/GSDM
+stereophonic
+stereoscope/MS
+stereoscopic
+stereoscopically
+stereoscopy/M
+stereotype/GMZDRS
+stereotypic
+stereotypical/Y
+sterile
+sterility/SM
+sterilization/SM
+sterilized/U
+sterilize/RSDGZ
+sterilizes/A
+Sterling/M
+sterling/MPYS
+sterlingness/M
+sternal
+Sternberg/M
+Sterne/M
+Stern/M
+sternness/S
+Sterno
+stern/SYRDPGT
+sternum/SM
+steroidal
+steroid/MS
+stertorous
+Stesha/M
+stethoscope/SM
+stet/MS
+stetson/MS
+Stetson/SM
+stetted
+stetting
+Steuben/M
+Stevana/M
+stevedore/GMSD
+Steve/M
+Stevena/M
+Steven/MS
+Stevenson/M
+Stevie/M
+Stevy/M
+steward/DMSG
+stewardess/SM
+Steward/M
+stewardship/MS
+Stewart/M
+stew/GDMS
+st/GBJ
+sticker/M
+stickily
+stickiness/SM
+stickleback/MS
+stickle/GZDR
+stickler/M
+stick/MRDSGZ
+stickpin/SM
+stickup/SM
+sticky/GPTDRS
+Stieglitz/M
+stiffen/JZRDG
+stiff/GTXPSYRND
+stiffness/MS
+stifle/GJRSD
+stifler/M
+stifling/Y
+stigma/MS
+stigmata
+stigmatic/S
+stigmatization/C
+stigmatizations
+stigmatization's
+stigmatize/DSG
+stigmatized/U
+stile/GMDS
+stiletto/MDSG
+stillbirth/M
+stillbirths
+stillborn/S
+stiller/MI
+stillest
+Stillman/M
+Stillmann/M
+stillness/MS
+still/RDIGS
+Stillwell/M
+stilted/PY
+stilt/GDMS
+Stilton/MS
+Stimson/M
+stimulant/MS
+stimulated/U
+stimulate/SDVGNX
+stimulation/M
+stimulative/S
+stimulator/M
+stimulatory
+stimuli/M
+stimulus/MS
+Stine/M
+stinger/M
+sting/GZR
+stingily
+stinginess/MS
+stinging/Y
+stingray/MS
+stingy/RTP
+stinkbug/S
+stinker/M
+stink/GZRJS
+stinking/Y
+stinkpot/M
+Stinky/M
+stinky/RT
+stinter/M
+stinting/U
+stint/JGRDMS
+stipendiary
+stipend/MS
+stipple/JDRSG
+stippler/M
+stipulate/XNGSD
+stipulation/M
+Stirling/M
+stirred/U
+stirrer/SM
+stirring/YS
+stirrup/SM
+stir/S
+stitch/ASDG
+stitcher/M
+stitchery/S
+stitching/MS
+stitch's
+St/M
+stoat/SM
+stochastic
+stochastically
+stochasticity
+stockade/SDMG
+stockbreeder/SM
+stockbroker/MS
+stockbroking/S
+stocker/SM
+Stockhausen/M
+stockholder/SM
+Stockholm/M
+stockily
+stockiness/SM
+stockinet's
+stockinette/S
+stocking/MDS
+stockist/MS
+stockpile/GRSD
+stockpiler/M
+stockpot/MS
+stockroom/MS
+stock's
+stock/SGAD
+stocktaking/MS
+Stockton/M
+stockyard/SM
+stocky/PRT
+Stoddard/M
+stodge/M
+stodgily
+stodginess/S
+stodgy/TRP
+stogy/SM
+stoical/Y
+stoichiometric
+stoichiometry/M
+stoicism/SM
+Stoicism/SM
+stoic/MS
+Stoic/MS
+stoke/DSRGZ
+stoker/M
+stokes/M
+Stokes/M
+STOL
+stole/MDS
+stolen
+stolidity/S
+stolidness/S
+stolid/PTYR
+stolon/SM
+stomachache/MS
+stomacher/M
+stomach/RSDMZG
+stomachs
+stomp/DSG
+stonecutter/SM
+stone/DSRGM
+Stonehenge/M
+stoneless
+Stone/M
+stonemason/MS
+stoner/M
+stonewall/GDS
+stoneware/MS
+stonewashed
+stonework/SM
+stonewort/M
+stonily
+stoniness/MS
+stony/TPR
+stood
+stooge/SDGM
+stool/SDMG
+stoop/SDG
+stopcock/MS
+stopgap/SM
+stoplight/SM
+stopover/MS
+stoppable/U
+stoppage/MS
+Stoppard/M
+stopped/U
+stopper/GMDS
+stopping/M
+stopple/GDSM
+stop's
+stops/M
+stop/US
+stopwatch/SM
+storage/SM
+store/ADSRG
+storefront/SM
+storehouse/MS
+storekeeper/M
+storekeep/ZR
+storeroom/SM
+store's
+stork/SM
+stormbound
+stormer/M
+Stormie/M
+stormily
+Stormi/M
+storminess/S
+Storm/M
+storm/SRDMGZ
+stormtroopers
+Stormy/M
+stormy/PTR
+storyboard/MDSG
+storybook/MS
+story/GSDM
+storyline
+storyteller/SM
+storytelling/MS
+Stouffer/M
+stoup/SM
+stouten/DG
+stouthearted
+Stout/M
+stoutness/MS
+stout/STYRNP
+stove/DSRGM
+stovepipe/SM
+stover/M
+stowage/SM
+stowaway/MS
+Stowe/M
+stow/GDS
+Strabo/M
+straddler/M
+straddle/ZDRSG
+Stradivari/SM
+Stradivarius/M
+strafe/GRSD
+strafer/M
+straggle/GDRSZ
+straggly/RT
+straightaway/S
+straightedge/MS
+straightener/M
+straighten/ZGDR
+straightforwardness/MS
+straightforward/SYP
+straightjacket's
+straightness/MS
+straight/RNDYSTXGP
+straightway/S
+strain/ASGZDR
+strained/UF
+strainer/MA
+straining/F
+strains/F
+straiten/DG
+straitjacket/GDMS
+straitlaced
+straitness/M
+strait/XTPSMGYDNR
+stranded/P
+strand/SDRG
+strangeness/SM
+strange/PYZTR
+stranger/GMD
+stranglehold/MS
+strangle/JDRSZG
+strangles/M
+strangulate/NGSDX
+strangulation/M
+strapless/S
+strapped/U
+strapping/S
+strap's
+strap/US
+Strasbourg/M
+stratagem/SM
+strata/MS
+strategical/Y
+strategic/S
+strategics/M
+strategist/SM
+strategy/SM
+Stratford/M
+strati
+stratification/M
+stratified/U
+stratify/NSDGX
+stratigraphic
+stratigraphical
+stratigraphy/M
+stratosphere/SM
+stratospheric
+stratospherically
+stratum/M
+stratus/M
+Strauss
+Stravinsky/M
+strawberry/SM
+strawflower/SM
+straw/SMDG
+strayer/M
+stray/GSRDM
+streak/DRMSGZ
+streaker/M
+streaky/TR
+streamed/U
+streamer/M
+stream/GZSMDR
+streaming/M
+streamline/SRDGM
+streetcar/MS
+streetlight/SM
+street/SMZ
+streetwalker/MS
+streetwise
+Streisand/M
+strengthen/AGDS
+strengthener/MS
+strength/NMX
+strengths
+strenuousness/SM
+strenuous/PY
+strep/MS
+streptococcal
+streptococci
+streptococcus/M
+streptomycin/SM
+stress/DSMG
+stressed/U
+stressful/YP
+stretchability/M
+stretchable/U
+stretch/BDRSZG
+stretcher/DMG
+stretchy/TRP
+strew/GDHS
+strewn
+striae
+stria/M
+striate/DSXGN
+striated/U
+striation/M
+stricken
+Strickland/M
+strict/AF
+stricter
+strictest
+strictly
+strictness/S
+stricture/SM
+stridden
+stridency/S
+strident/Y
+strider/M
+stride/RSGM
+strife/SM
+strikebreaker/M
+strikebreaking/M
+strikebreak/ZGR
+strikeout/S
+striker/M
+strike/RSGZJ
+striking/Y
+Strindberg/M
+stringed
+stringency/S
+stringent/Y
+stringer/MS
+stringiness/SM
+stringing/M
+string's
+string/SAG
+stringy/RTP
+striper/M
+stripe/SM
+strip/GRDMS
+stripling/M
+stripped/U
+stripper/MS
+stripping
+stripteaser/M
+striptease/SRDGZM
+stripy/RT
+strive/JRSG
+striven
+striver/M
+strobe/SDGM
+stroboscope/SM
+stroboscopic
+strode
+stroke/ZRSDGM
+stroking/M
+stroller/M
+stroll/GZSDR
+Stromberg/M
+Stromboli/M
+Strom/M
+strongbow
+strongbox/MS
+Strongheart/M
+stronghold/SM
+strongish
+Strong/M
+strongman/M
+strongmen
+strongroom/MS
+strong/YRT
+strontium/SM
+strophe/MS
+strophic
+stropped
+stropping
+strop/SM
+strove
+struck
+structuralism/M
+structuralist/SM
+structural/Y
+structured/AU
+structureless
+structures/A
+structure/SRDMG
+structuring/A
+strudel/MS
+struggle/GDRS
+struggler/M
+strummed
+strumming
+strumpet/GSDM
+strum/S
+strung/UA
+strut/S
+strutted
+strutter/M
+strutting
+strychnine/MS
+Stuart/MS
+stubbed/M
+stubbing
+Stubblefield/MS
+stubble/SM
+stubbly/RT
+stubbornness/SM
+stubborn/SGTYRDP
+stubby/SRT
+stub/MS
+stuccoes
+stucco/GDM
+stuck/U
+studbook/SM
+studded
+studding/SM
+Studebaker/M
+studentship/MS
+student/SM
+studiedness/M
+studied/PY
+studier/SM
+studio/MS
+studiousness/SM
+studious/PY
+stud/MS
+study/AGDS
+stuffily
+stuffiness/SM
+stuffing/M
+stuff/JGSRD
+stuffy/TRP
+stultify/NXGSD
+Stu/M
+stumble/GZDSR
+stumbling/Y
+stumpage/M
+stumper/M
+stump/RDMSG
+stumpy/RT
+stung
+stunk
+stunned
+stunner/M
+stunning/Y
+stun/S
+stunted/P
+stunt/GSDM
+stupefaction/SM
+stupefy/DSG
+stupendousness/M
+stupendous/PY
+stupidity/SM
+stupidness/M
+stupid/PTYRS
+stupor/MS
+sturdily
+sturdiness/SM
+sturdy/SRPT
+sturgeon/SM
+Sturm/M
+stutter/DRSZG
+Stuttgart/M
+Stuyvesant/M
+sty/DSGM
+Stygian
+styled/A
+style/GZMDSR
+styles/A
+styli
+styling/A
+stylishness/S
+stylish/PY
+stylistically
+stylistic/S
+stylist/MS
+stylites
+stylization/MS
+stylize/DSG
+stylos
+stylus/SM
+stymieing
+stymie/SD
+stymy's
+styptic/S
+styrene/MS
+Styrofoam/S
+Styx/M
+suable
+Suarez/M
+suasion/EMS
+suaveness/S
+suave/PRYT
+suavity/SM
+subaltern/SM
+subarctic/S
+subareas
+Subaru/M
+subassembly/M
+subatomic/S
+subbasement/SM
+subbed
+subbing
+subbranch/S
+subcaste/M
+subcategorizing
+subcategory/SM
+subchain
+subclassifications
+subclass/MS
+subclauses
+subcommand/S
+subcommittee/SM
+subcompact/S
+subcomponent/MS
+subcomputation/MS
+subconcept
+subconsciousness/SM
+subconscious/PSY
+subconstituent
+subcontinental
+subcontinent/MS
+subcontractor/SM
+subcontract/SMDG
+subcultural
+subculture/GMDS
+subcutaneous/Y
+subdirectory/S
+subdistrict/M
+subdivide/SRDG
+subdivision/SM
+subdued/Y
+subdue/GRSD
+subduer/M
+subexpression/MS
+subfamily/SM
+subfield/MS
+subfile/SM
+subfreezing
+subgoal/SM
+subgraph
+subgraphs
+subgroup/SGM
+subharmonic/S
+subheading/M
+subhead/MGJS
+subhuman/S
+subindex/M
+subinterval/MS
+subj
+subject/GVDMS
+subjection/SM
+subjectiveness/M
+subjective/PSY
+subjectivist/S
+subjectivity/SM
+subjoin/DSG
+subjugate/NGXSD
+subjugation/M
+subjunctive/S
+sublayer
+sublease/DSMG
+sublet/S
+subletting
+sublimate/GNSDX
+sublimation/M
+sublime/GRSDTYP
+sublimeness/M
+sublimer/M
+subliminal/Y
+sublimity/SM
+sublist/SM
+subliterary
+sublunary
+submachine
+submarginal
+submarine/MZGSRD
+submariner/M
+submerge/DSG
+submergence/SM
+submerse/XNGDS
+submersible/S
+submersion/M
+submicroscopic
+submission/SAM
+submissiveness/MS
+submissive/PY
+submit/SA
+submittable
+submittal
+submitted/A
+submitter/S
+submitting/A
+submode/S
+submodule/MS
+sub/MS
+subnational
+subnet/SM
+subnetwork/SM
+subnormal/SY
+suboptimal
+suborbital
+suborder/MS
+subordinately/I
+subordinates/I
+subordinate/YVNGXPSD
+subordination/IMS
+subordinator
+subornation/SM
+suborn/GSD
+subpage
+subparagraph/M
+subpart/MS
+subplot/MS
+subpoena/GSDM
+subpopulation/MS
+subproblem/SM
+subprocess/SM
+subprofessional/S
+subprogram/SM
+subproject
+subproof/SM
+subquestion/MS
+subrange/SM
+subregional/Y
+subregion/MS
+subrogation/M
+subroutine/SM
+subsample/MS
+subschema/MS
+subscribe/ASDG
+subscriber/SM
+subscripted/U
+subscription/MS
+subscript/SGD
+subsection/SM
+subsegment/SM
+subsentence
+subsequence/MS
+subsequent/SYP
+subservience/SM
+subservient/SY
+subset/MS
+subsidence/MS
+subside/SDG
+subsidiarity
+subsidiary/MS
+subsidization/MS
+subsidized/U
+subsidizer/M
+subsidize/ZRSDG
+subsidy/MS
+subsistence/MS
+subsistent
+subsist/SGD
+subsocietal
+subsoil/DRMSG
+subsonic
+subspace/MS
+subspecies/M
+substance/MS
+substandard
+substantially/IU
+substantialness/M
+substantial/PYS
+substantiated/U
+substantiate/VGNSDX
+substantiation/MFS
+substantiveness/M
+substantive/PSYM
+substantivity
+substation/MS
+substerilization
+substitutability
+substituted/U
+substitute/NGVBXDRS
+substitutionary
+substitution/M
+substitutive/Y
+substrata
+substrate/MS
+substratum/M
+substring/S
+substructure/SM
+subsume/SDG
+subsurface/S
+subsystem/MS
+subtable/S
+subtask/SM
+subteen/SM
+subtenancy/MS
+subtenant/SM
+subtend/DS
+subterfuge/SM
+subterranean/SY
+subtest
+subtext/SM
+subtitle/DSMG
+subtleness/M
+subtle/RPT
+subtlety/MS
+subtly/U
+subtopic/SM
+subtotal/GSDM
+subtracter/M
+subtraction/MS
+subtract/SRDZVG
+subtrahend/SM
+subtree/SM
+subtropical
+subtropic/S
+subtype/MS
+subunit/SM
+suburbanite/MS
+suburbanization/MS
+suburbanized
+suburbanizing
+suburban/S
+suburbia/SM
+suburb/MS
+subvention/MS
+subversion/SM
+subversiveness/MS
+subversive/SPY
+subverter/M
+subvert/SGDR
+subway/MDGS
+subzero
+succeeder/M
+succeed/GDRS
+successfulness/M
+successful/UY
+succession/SM
+successiveness/M
+successive/YP
+success/MSV
+successor/MS
+successorship
+succinctness/SM
+succinct/RYPT
+succored/U
+succorer/M
+succor/SGZRDM
+succotash/SM
+succubus/M
+succulence/SM
+succulency/MS
+succulent/S
+succumb/SDG
+such
+suchlike
+sucker/DMG
+suck/GZSDRB
+suckle/SDJG
+suckling/M
+Sucre/M
+sucrose/MS
+suction/SMGD
+Sudanese/M
+Sudanic/M
+Sudan/M
+suddenness/SM
+sudden/YPS
+Sudetenland/M
+sud/S
+suds/DSRG
+sudsy/TR
+sued/DG
+suede/SM
+Suellen/M
+Sue/M
+suer/M
+suet/MS
+Suetonius/M
+suety
+sue/ZGDRS
+Suez/M
+sufferance/SM
+sufferer/M
+suffering/M
+suffer/SJRDGZ
+suffice/GRSD
+sufficiency/SIM
+sufficient/IY
+suffixation/S
+suffixed/U
+suffix/GMRSD
+suffocate/XSDVGN
+suffocating/Y
+Suffolk/M
+suffragan/S
+suffrage/MS
+suffragette/MS
+suffragist/SM
+suffuse/VNGSDX
+suffusion/M
+Sufi/M
+Sufism/M
+sugarcane/S
+sugarcoat/GDS
+sugarless
+sugarplum/MS
+sugar/SJGMD
+sugary/TR
+suggest/DRZGVS
+suggester/M
+suggestibility/SM
+suggestible
+suggestion/MS
+suggestiveness/MS
+suggestive/PY
+sugillate
+Suharto/M
+suicidal/Y
+suicide/GSDM
+Sui/M
+suitability/SU
+suitableness/S
+suitable/P
+suitably/U
+suitcase/MS
+suited/U
+suite/SM
+suiting/M
+suit/MDGZBJS
+suitor/SM
+Sukarno/M
+Sukey/M
+Suki/M
+sukiyaki/SM
+Sukkoth's
+Sukkot/S
+Sula/M
+Sulawesi/M
+Suleiman/M
+sulfaquinoxaline
+sulfa/S
+sulfate/MSDG
+sulfide/S
+sulfite/M
+sulfonamide/SM
+sulfur/DMSG
+sulfuric
+sulfurousness/M
+sulfurous/YP
+sulk/GDS
+sulkily
+sulkiness/S
+sulky/RSPT
+Sulla/M
+sullenness/MS
+sullen/TYRP
+sullied/U
+Sullivan/M
+sully/GSD
+Sully/M
+sulphate/SM
+sulphide/MS
+sulphuric
+sultana/SM
+sultanate/MS
+sultan/SM
+sultrily
+sultriness/SM
+sultry/PRT
+Sulzberger/M
+sumach's
+sumac/SM
+Sumatra/M
+Sumatran/S
+sumer/F
+Sumeria/M
+Sumerian/M
+summability/M
+summable
+summand/MS
+summarily
+summarization/MS
+summarized/U
+summarize/GSRDZ
+summarizer/M
+summary/MS
+summation/FMS
+summed
+Summerdale/M
+summerhouse/MS
+summer/SGDM
+Summer/SM
+summertime/MS
+summery/TR
+summing
+summit/GMDS
+summitry/MS
+summoner/M
+summon/JSRDGZ
+summons/MSDG
+sum/MRS
+Sumner/M
+sumo/SM
+sump/SM
+sumptuousness/SM
+sumptuous/PY
+Sumter/M
+Sun
+sunbaked
+sunbathe
+sunbather/M
+sunbathing/M
+sunbaths
+sunbath/ZRSDG
+sunbeam/MS
+Sunbelt/M
+sunblock/S
+sunbonnet/MS
+sunburn/GSMD
+sunburst/MS
+suncream
+sundae/MS
+Sundanese/M
+Sundas
+Sunday/MS
+sunder/SDG
+sundial/MS
+sundowner/M
+sundown/MRDSZG
+sundris
+sundry/S
+sunfish/SM
+sunflower/MS
+sunglass/MS
+Sung/M
+sung/U
+sunk/SN
+sunlamp/S
+sunless
+sunlight/MS
+sunlit
+sun/MS
+sunned
+Sunni/MS
+sunniness/SM
+sunning
+Sunnite/SM
+Sunny/M
+sunny/RSTP
+Sunnyvale/M
+sunrise/GMS
+sunroof/S
+sunscreen/S
+sunset/MS
+sunsetting
+sunshade/MS
+Sunshine/M
+sunshine/MS
+sunshiny
+sunspot/SM
+sunstroke/MS
+suntanned
+suntanning
+suntan/SM
+sunup/MS
+superabundance/MS
+superabundant
+superannuate/GNXSD
+superannuation/M
+superbness/M
+superb/YRPT
+supercargoes
+supercargo/M
+supercharger/M
+supercharge/SRDZG
+superciliousness/SM
+supercilious/PY
+supercity/S
+superclass/M
+supercomputer/MS
+supercomputing
+superconcept
+superconducting
+superconductivity/SM
+superconductor/SM
+supercooled
+supercooling
+supercritical
+superdense
+super/DG
+superego/SM
+supererogation/MS
+supererogatory
+superficiality/S
+superficial/SPY
+superfine
+superfix/M
+superfluity/MS
+superfluousness/S
+superfluous/YP
+superheat/D
+superheroes
+superhero/SM
+superhighway/MS
+superhumanness/M
+superhuman/YP
+superimpose/SDG
+superimposition/MS
+superintendence/S
+superintendency/SM
+superintendent/SM
+superintend/GSD
+superiority/MS
+Superior/M
+superior/SMY
+superlativeness/M
+superlative/PYS
+superlunary
+supermachine
+superman/M
+Superman/M
+supermarket/SM
+supermen
+supermodel
+supermom/S
+supernal
+supernatant
+supernaturalism/M
+supernaturalness/M
+supernatural/SPY
+supernormal/Y
+supernovae
+supernova/MS
+supernumerary/S
+superordinate
+superpose/BSDG
+superposition/MS
+superpower/MS
+superpredicate
+supersaturate/XNGDS
+supersaturation/M
+superscribe/GSD
+superscript/DGS
+superscription/SM
+superseder/M
+supersede/SRDG
+supersensitiveness/M
+supersensitive/P
+superset/MS
+supersonically
+supersonic/S
+supersonics/M
+superstar/SM
+superstition/SM
+superstitious/YP
+superstore/S
+superstructural
+superstructure/SM
+supertanker/SM
+supertitle/MSDG
+superuser/MS
+supervene/GSD
+supervention/S
+supervised/U
+supervise/SDGNX
+supervision/M
+supervisor/SM
+supervisory
+superwoman/M
+superwomen
+supineness/M
+supine/PSY
+supper/DMG
+supplanter/M
+supplant/SGRD
+supplemental/S
+supplementary/S
+supplementation/S
+supplementer/M
+supplement/SMDRG
+suppleness/SM
+supple/SPLY
+suppliant/S
+supplicant/MS
+supplicate/NGXSD
+supplication/M
+supplier/AM
+suppl/RDGT
+supply/MAZGSRD
+supportability/M
+supportable/UI
+supported/U
+supporter/M
+supporting/Y
+supportive/Y
+support/ZGVSBDR
+supposed/Y
+suppose/SRDBJG
+supposition/MS
+suppository/MS
+suppressant/S
+suppressed/U
+suppressible/I
+suppression/SM
+suppressive/P
+suppressor/S
+suppress/VGSD
+suppurate/NGXSD
+suppuration/M
+supp/YDRGZ
+supra
+supranational
+supranationalism/M
+suprasegmental
+supremacist/SM
+supremacy/SM
+supremal
+supremeness/M
+supreme/PSRTY
+supremo/M
+sup/RSZ
+supt
+Supt/M
+Surabaya/M
+Surat/M
+surcease/DSMG
+surcharge/MGSD
+surcingle/MGSD
+surd/M
+sured/I
+surefire
+surefooted
+surely
+sureness/MS
+sureness's/U
+sure/PU
+surer/I
+surest
+surety/SM
+surfaced/UA
+surface/GSRDPZM
+surfacer/AMS
+surfaces/A
+surfacing/A
+surfactant/SM
+surfboard/MDSG
+surfeit/SDRMG
+surfer/M
+surfing/M
+surf/SJDRGMZ
+surged/A
+surge/GYMDS
+surgeon/MS
+surgery/MS
+surges/A
+surgical/Y
+Suriname
+Surinamese
+Surinam's
+surliness/SM
+surly/TPR
+surmiser/M
+surmise/SRDG
+surmountable/IU
+surmount/DBSG
+surname/GSDM
+surpassed/U
+surpass/GDS
+surpassing/Y
+surplice/SM
+surplus/MS
+surplussed
+surplussing
+surprised/U
+surprise/MGDRSJ
+surpriser/M
+surprising/YU
+surrealism/MS
+surrealistic
+surrealistically
+surrealist/S
+surreality
+surreal/S
+surrender/DRSG
+surrenderer/M
+surreptitiousness/S
+surreptitious/PY
+surrey/SM
+surrogacy/S
+surrogate/SDMNG
+surrogation/M
+surrounding/M
+surround/JGSD
+surtax/SDGM
+surveillance/SM
+surveillant
+surveyed/A
+surveying/M
+survey/JDSG
+surveyor/MS
+surveys/A
+survivability/M
+survivable/U
+survivalist/S
+survival/MS
+survive/SRDBG
+survivor/MS
+survivorship/M
+Surya/M
+Sus
+Susana/M
+Susanetta/M
+Susan/M
+Susannah/M
+Susanna/M
+Susanne/M
+Susann/M
+susceptibilities
+susceptibility/IM
+susceptible/I
+Susette/M
+sushi/SM
+Susie/M
+Susi/M
+suspected/U
+suspecter/M
+suspect/GSDR
+suspecting/U
+suspend/DRZGS
+suspended/UA
+suspender/M
+suspenseful
+suspense/MXNVS
+suspension/AM
+suspensive/Y
+suspensor/M
+suspicion/GSMD
+suspiciousness/M
+suspicious/YP
+Susquehanna/M
+Sussex/M
+sustainability
+sustainable/U
+sustain/DRGLBS
+sustainer/M
+sustainment/M
+sustenance/MS
+Susy/M
+Sutherland/M
+Sutherlan/M
+sutler/MS
+Sutton/M
+suture/GMSD
+SUV
+Suva/M
+Suwanee/M
+Suzanna/M
+Suzanne/M
+Suzann/M
+suzerain/SM
+suzerainty/MS
+Suzette/M
+Suzhou/M
+Suzie/M
+Suzi/M
+Suzuki/M
+Suzy/M
+Svalbard/M
+svelte/RPTY
+Svend/M
+Svengali
+Sven/M
+Sverdlovsk/M
+Svetlana/M
+SW
+swabbed
+swabbing
+swabby/S
+Swabian/SM
+swab/MS
+swaddle/SDG
+swagged
+swagger/GSDR
+swagging
+swag/GMS
+Swahili/MS
+swain/SM
+SWAK
+swallower/M
+swallow/GDRS
+swallowtail/SM
+swam
+swami/SM
+swamper/M
+swampland/MS
+swamp/SRDMG
+swampy/RPT
+Swanee/M
+swankily
+swankiness/MS
+swank/RDSGT
+swanky/PTRS
+swanlike
+swan/MS
+swanned
+swanning
+Swansea/M
+Swanson/M
+swappable/U
+swapped
+swapper/SM
+swapping
+swap/S
+sward/MSGD
+swarmer/M
+swarm/GSRDM
+swarthiness/M
+Swarthmore/M
+swarthy/RTP
+swart/P
+Swartz/M
+swashbuckler/SM
+swashbuckling/S
+swash/GSRD
+swastika/SM
+SWAT
+swatch/MS
+swathe
+swather/M
+swaths
+swath/SRDMGJ
+swat/S
+swatted
+swatter/MDSG
+swatting
+swayback/SD
+sway/DRGS
+swayer/M
+Swaziland/M
+Swazi/SM
+swearer/M
+swear/SGZR
+swearword/SM
+sweatband/MS
+sweater/M
+sweatily
+sweatiness/M
+sweatpants
+sweat/SGZRM
+sweatshirt/S
+sweatshop/MS
+sweaty/TRP
+Swedenborg/M
+Sweden/M
+swede/SM
+Swede/SM
+Swedish
+Swed/MN
+Sweeney/SM
+sweeper/M
+sweepingness/M
+sweeping/PY
+sweep/SBRJGZ
+sweeps/M
+sweepstakes
+sweepstake's
+sweetbread/SM
+sweetbrier/SM
+sweetcorn
+sweetened/U
+sweetener/M
+sweetening/M
+sweeten/ZDRGJ
+sweetheart/MS
+sweetie/MS
+sweeting/M
+sweetish/Y
+Sweet/M
+sweetmeat/MS
+sweetness/MS
+sweetshop
+sweet/TXSYRNPG
+swellhead/DS
+swelling/M
+swell/SJRDGT
+swelter/DJGS
+sweltering/Y
+Swen/M
+Swenson/M
+swept
+sweptback
+swerve/GSD
+swerving/U
+swifter/M
+swift/GTYRDPS
+Swift/M
+swiftness/MS
+swigged
+swigging
+swig/SM
+swill/SDG
+swimmer/MS
+swimming/MYS
+swim/S
+swimsuit/MS
+Swinburne/M
+swindle/GZRSD
+swindler/M
+swineherd/MS
+swine/SM
+swingeing
+swinger/M
+swinging/Y
+swing/SGRZJB
+swingy/R
+swinishness/M
+swinish/PY
+Swink/M
+swipe/DSG
+swirling/Y
+swirl/SGRD
+swirly/TR
+swish/GSRD
+swishy/R
+swiss
+Swiss/S
+switchback/GDMS
+switchblade/SM
+switchboard/MS
+switcher/M
+switch/GBZMRSDJ
+switchgear
+switchman/M
+switchmen/M
+switchover/M
+Switzerland/M
+Switzer/M
+Switz/MR
+swivel/GMDS
+swizzle/RDGM
+swob's
+swollen
+swoon/GSRD
+swooning/Y
+swoop/RDSG
+swoosh/GSD
+swop's
+sword/DMSG
+swordfish/SM
+swordplayer/M
+swordplay/RMS
+swordsman/M
+swordsmanship/SM
+swordsmen
+swordtail/M
+swore
+sworn
+swot/S
+swum
+swung
+s/XJBG
+sybarite/MS
+sybaritic
+Sybila/M
+Sybilla/M
+Sybille/M
+Sybil/M
+Sybyl/M
+sycamore/SM
+sycophancy/S
+sycophantic
+sycophantically
+sycophant/SYM
+Sydelle/M
+Sydel/M
+Syd/M
+Sydney/M
+Sykes/M
+Sylas/M
+syllabicate/GNDSX
+syllabication/M
+syllabicity
+syllabic/S
+syllabification/M
+syllabify/GSDXN
+syllabi's
+syllable/SDMG
+syllabub/M
+syllabus/MS
+syllabusss
+syllogism/MS
+syllogistic
+Sylow/M
+sylphic
+sylphlike
+sylph/M
+sylphs
+Sylvania/M
+Sylvan/M
+sylvan/S
+Sylvester/M
+Sylvia/M
+Sylvie/M
+Syman/M
+symbiont/M
+symbioses
+symbiosis/M
+symbiotic
+symbol/GMDS
+symbolical/Y
+symbolics/M
+symbolic/SM
+symbolism/MS
+symbolist/MS
+symbolization/MAS
+symbolized/U
+symbolize/GZRSD
+symbolizes/A
+Symington/M
+symmetric
+symmetrically/U
+symmetricalness/M
+symmetrical/PY
+symmetrization/M
+symmetrizing
+symmetry/MS
+Symon/M
+sympathetically/U
+sympathetic/S
+sympathized/U
+sympathizer/M
+sympathize/SRDJGZ
+sympathizing/MYUS
+sympathy/MS
+symphonic
+symphonists
+symphony/MS
+symposium/MS
+symptomatic
+symptomatically
+symptomatology/M
+symptom/MS
+syn
+synagogal
+synagogue/SM
+synapse/SDGM
+synaptic
+synchronism/M
+synchronization's
+synchronization/SA
+synchronize/AGCDS
+synchronized/U
+synchronizer/MS
+synchronousness/M
+synchronous/YP
+synchrony
+synchrotron/M
+syncopate/VNGXSD
+syncopation/M
+syncope/MS
+sync/SGD
+syndicalist
+syndicate/XSDGNM
+syndic/SM
+syndrome/SM
+synergism/SM
+synergistic
+synergy/MS
+synfuel/S
+Synge/M
+synod/SM
+synonymic
+synonymous/Y
+synonym/SM
+synonymy/MS
+synopses
+synopsis/M
+synopsized
+synopsizes
+synopsizing
+synoptic/S
+syntactical/Y
+syntactics/M
+syntactic/SY
+syntax/MS
+syntheses
+synthesis/M
+synthesized/U
+synthesize/GZSRD
+synthesizer/M
+synthesizes/A
+synthetically
+synthetic/S
+syphilis/MS
+syphilitic/S
+syphilized
+syphilizing
+Syracuse/M
+Syriac/M
+Syria/M
+Syrian/SM
+syringe/GMSD
+syrup/DMSG
+syrupy
+sys
+systematical/Y
+systematics/M
+systematic/SP
+systematization/SM
+systematized/U
+systematizer/M
+systematize/ZDRSG
+systematizing/U
+systemically
+systemic/S
+systemization/SM
+system/MS
+systole/MS
+systolic
+Szilard/M
+Szymborska/M
+TA
+Tabasco/MS
+Tabatha/M
+Tabbatha/M
+tabbed
+Tabbie/M
+Tabbi/M
+tabbing
+Tabbitha/M
+Tabb/M
+tabbouleh
+tabboulehs
+tabby/GSD
+Tabby/M
+Taber/M
+Tabernacle/S
+tabernacle/SDGM
+Tabina/M
+Tabitha/M
+tabla/MS
+tableau/M
+tableaux
+tablecloth/M
+tablecloths
+table/GMSD
+tableland/SM
+tablespoonful/MS
+tablespoon/SM
+tablet/MDGS
+tabletop/MS
+tableware/SM
+tabling/M
+tabloid/MS
+Tab/MR
+taboo/GSMD
+Tabor/M
+tabor/MDGS
+Tabriz/SM
+tab/SM
+tabula
+tabular/Y
+tabulate/XNGDS
+tabulation/M
+tabulator/MS
+tachometer/SM
+tachometry
+tachycardia/MS
+tachyon/SM
+tacitness/MS
+taciturnity/MS
+taciturn/Y
+Tacitus/M
+tacit/YP
+tacker/M
+tack/GZRDMS
+tackiness/MS
+tackler/M
+tackle/RSDMZG
+tackling/M
+tacky/RSTP
+Tacoma/M
+taco/MS
+tact/FSM
+tactfulness/S
+tactful/YP
+tactical/Y
+tactician/MS
+tactic/SM
+tactile/Y
+tactility/S
+tactlessness/SM
+tactless/PY
+tactual/Y
+Taddeo/M
+Taddeusz/M
+Tadd/M
+Tadeas/M
+Tadeo/M
+Tades
+Tadio/M
+Tad/M
+tadpole/MS
+tad/SM
+Tadzhikistan's
+Tadzhikstan/M
+Taegu/M
+Taejon/M
+taffeta/MS
+taffrail/SM
+Taffy/M
+taffy/SM
+Taft/M
+Tagalog/SM
+tagged/U
+tagger/S
+tagging
+Tagore/M
+tag/SM
+Tagus/M
+Tahitian/S
+Tahiti/M
+Tahoe/M
+Taichung/M
+taiga/MS
+tailback/MS
+tail/CMRDGAS
+tailcoat/S
+tailer/AM
+tailgate/MGRSD
+tailgater/M
+tailing/MS
+taillessness/M
+tailless/P
+taillight/MS
+tailor/DMJSGB
+Tailor/M
+tailpipe/SM
+tailspin/MS
+tailwind/SM
+Tainan/M
+Taine/M
+taint/DGS
+tainted/U
+Taipei/M
+Taite/M
+Tait/M
+Taiwanese
+Taiwan/M
+Taiyuan/M
+Tajikistan
+takeaway/S
+taken/A
+takeoff/SM
+takeout/S
+takeover/SM
+taker/M
+take/RSHZGJ
+takes/IA
+taking/IA
+Taklamakan/M
+Talbert/M
+Talbot/M
+talcked
+talcking
+talc/SM
+talcum/S
+talebearer/SM
+talented/M
+talentless
+talent/SMD
+taler/M
+tale/RSMN
+tali
+Talia/M
+Taliesin/M
+talion/M
+talismanic
+talisman/SM
+talkativeness/MS
+talkative/YP
+talker/M
+talk/GZSRD
+talkie/M
+talky/RST
+Talladega/M
+Tallahassee/M
+Tallahatchie/M
+Tallahoosa/M
+tallboy/MS
+Tallchief/M
+Talley/M
+Talleyrand/M
+Tallia/M
+Tallie/M
+Tallinn/M
+tallish
+tallness/MS
+Tallou/M
+tallow/DMSG
+tallowy
+tall/TPR
+Tallulah/M
+tally/GRSDZ
+tallyho/DMSG
+Tally/M
+Talmudic
+Talmudist/MS
+Talmud/MS
+talon/SMD
+talus/MS
+Talyah/M
+Talya/M
+Ta/M
+tamable/M
+tamale/SM
+tamarack/SM
+Tamarah/M
+Tamara/M
+tamarind/MS
+Tamar/M
+Tamarra/M
+Tamas
+tambourine/MS
+tamed/U
+Tameka/M
+tameness/S
+Tamera/M
+Tamerlane/M
+tame/SYP
+Tamika/M
+Tamiko/M
+Tamil/MS
+Tami/M
+Tam/M
+Tamma/M
+Tammany/M
+Tammara/M
+tam/MDRSTZGB
+Tammie/M
+Tammi/M
+Tammy/M
+Tampa/M
+Tampax/M
+tampered/U
+tamperer/M
+tamper/ZGRD
+tampon/DMSG
+tamp/SGZRD
+Tamqrah/M
+Tamra/M
+tanager/MS
+Tanaka/M
+Tana/M
+Tananarive/M
+tanbark/SM
+Tancred/M
+tandem/SM
+Tandie/M
+Tandi/M
+tandoori/S
+Tandy/M
+Taney/M
+T'ang
+Tanganyika/M
+tangelo/SM
+tangency/M
+tangential/Y
+tangent/SM
+tangerine/MS
+tang/GSYDM
+tangibility/MIS
+tangible/IPS
+tangibleness's/I
+tangibleness/SM
+tangibly/I
+Tangier/M
+tangle's
+tangle/UDSG
+tango/MDSG
+Tangshan/M
+tangy/RST
+Tanhya/M
+Tania/M
+Tani/M
+Tanisha/M
+Tanitansy/M
+tankard/MS
+tanker/M
+tankful/MS
+tank/GZSRDM
+Tan/M
+tan/MS
+tanned/U
+Tannenbaum/M
+Tanner/M
+tanner/SM
+tannery/MS
+tannest
+Tanney/M
+Tannhäuser/M
+Tannie/M
+tanning/SM
+tannin/SM
+Tann/RM
+Tanny/M
+Tansy/M
+tansy/SM
+tantalization/SM
+tantalized/U
+tantalize/GZSRD
+tantalizingly/S
+tantalizingness/S
+tantalizing/YP
+tantalum/MS
+Tantalus/M
+tantamount
+tantra/S
+tantrum/SM
+Tanya/M
+Tanzania/M
+Tanzanian/S
+taoism
+Taoism/MS
+Taoist/MS
+taoist/S
+Tao/M
+tao/S
+Tapdance/M
+taped/U
+tapeline/S
+taperer/M
+taper/GRD
+tape/SM
+tapestry/GMSD
+tapeworm/MS
+tapioca/MS
+tapir/MS
+tap/MSDRJZG
+tapped/U
+tapper/MS
+tappet/MS
+tapping/M
+taproom/MS
+taproot/SM
+taps/M
+Tarah/M
+Tara/M
+tarantella/MS
+tarantula/MS
+Tarawa/M
+Tarazed/M
+Tarbell/M
+tardily
+tardiness/S
+tardy/TPRS
+tare/MS
+target/GSMD
+tar/GSMD
+tariff/DMSG
+Tarim/M
+Tarkington/M
+tarmacked
+tarmacking
+tarmac/S
+tarnished/U
+tarnish/GDS
+tarn/MS
+taro/MS
+tarot/MS
+tarpapered
+tarpaulin/MS
+tarp/MS
+tarpon/MS
+tarragon/SM
+Tarrah/M
+Tarra/M
+Tarrance/M
+tarred/M
+tarring/M
+tarry/TGRSD
+Tarrytown/M
+tarsal/S
+tarsi
+tarsus/M
+tartan/MS
+tartaric
+Tartar's
+tartar/SM
+Tartary/M
+tartness/MS
+tart/PMYRDGTS
+Tartuffe/M
+Taryn/M
+Tarzan/M
+Tasha/M
+Tashkent/M
+Tasia/M
+task/GSDM
+taskmaster/SM
+taskmistress/MS
+Tasmania/M
+Tasmanian/S
+tassellings
+tassel/MDGS
+Tass/M
+tasted/EU
+tastefulness/SME
+tasteful/PEY
+taste/GZMJSRD
+tastelessness/SM
+tasteless/YP
+taster/M
+taste's/E
+tastes/E
+tastily
+tastiness/MS
+tasting/E
+tasty/RTP
+tatami/MS
+Tatar/SM
+Tate/M
+tater/M
+Tatiana/M
+Tatiania/M
+tat/SRZ
+tatted
+tatterdemalion/SM
+tattered/M
+tatter/GDS
+tatting/SM
+tattler/M
+tattle/RSDZG
+tattletale/SM
+tattooer/M
+tattooist/MS
+tattoo/ZRDMGS
+tatty/R
+Tatum/M
+taught/AU
+taunter/M
+taunting/Y
+taunt/ZGRDS
+taupe/SM
+Taurus/SM
+tau/SM
+tauten/GD
+tautness/S
+tautological/Y
+tautologous
+tautology/SM
+taut/PGTXYRDNS
+taverner/M
+tavern/RMS
+tawdrily
+tawdriness/SM
+tawdry/SRTP
+Tawney/M
+Tawnya/M
+tawny/RSMPT
+Tawsha/M
+taxable/S
+taxably
+taxation/MS
+taxed/U
+taxicab/MS
+taxidermist/SM
+taxidermy/MS
+taxi/MDGS
+taximeter/SM
+taxing/Y
+taxiway/MS
+taxonomic
+taxonomically
+taxonomist/SM
+taxonomy/SM
+taxpayer/MS
+taxpaying/M
+tax/ZGJMDRSB
+Taylor/SM
+Tb
+TB
+TBA
+Tbilisi/M
+tbs
+tbsp
+Tchaikovsky/M
+Tc/M
+TCP
+TD
+TDD
+Te
+teabag/S
+teacake/MS
+teacart/M
+teachable/P
+teach/AGS
+teacher/MS
+teaching/SM
+teacloth
+teacupful/MS
+teacup/MS
+Teador/M
+teahouse/SM
+teakettle/SM
+teak/SM
+teakwood/M
+tealeaves
+teal/MS
+tea/MDGS
+teammate/MS
+team/MRDGS
+teamster/MS
+teamwork/SM
+teapot/MS
+tearaway
+teardrop/MS
+tearer/M
+tearfulness/M
+tearful/YP
+teargas/S
+teargassed
+teargassing
+tearjerker/S
+tearoom/MS
+tear/RDMSG
+teary/RT
+Teasdale/M
+tease/KS
+teasel/DGSM
+teaser/M
+teashop/SM
+teasing/Y
+teaspoonful/MS
+teaspoon/MS
+teas/SRDGZ
+teatime/MS
+teat/MDS
+tech/D
+technetium/SM
+technicality/MS
+technicalness/M
+technical/YSP
+technician/MS
+Technicolor/MS
+Technion/M
+technique/SM
+technocracy/MS
+technocratic
+technocrat/S
+technological/Y
+technologist/MS
+technology/MS
+technophobia
+technophobic
+techs
+tectonically
+tectonic/S
+tectonics/M
+Tecumseh/M
+Tedda/M
+Teddie/M
+Teddi/M
+Tedd/M
+Teddy/M
+teddy/SM
+Tedie/M
+Tedi/M
+tediousness/SM
+tedious/YP
+tedium/MS
+Ted/M
+Tedman/M
+Tedmund/M
+Tedra/M
+tee/DRSMH
+teeing
+teem/GSD
+teemingness/M
+teeming/PY
+teenager/M
+teenage/RZ
+Teena/M
+teen/SR
+teenybopper/SM
+teeny/RT
+teepee's
+teeshirt/S
+teeter/GDS
+teethe
+teether/M
+teething/M
+teethmarks
+teeth/RSDJMG
+teetotaler/M
+teetotalism/MS
+teetotal/SRDGZ
+TEFL
+Teflon/MS
+Tegucigalpa/M
+Teheran's
+Tehran
+TEirtza/M
+tektite/SM
+Tektronix/M
+telecast/SRGZ
+telecommunicate/NX
+telecommunication/M
+telecommute/SRDZGJ
+telecoms
+teleconference/GMJSD
+Teledyne/M
+Telefunken/M
+telegenic
+telegrammed
+telegramming
+telegram/MS
+telegraphic
+telegraphically
+telegraphist/MS
+telegraph/MRDGZ
+telegraphs
+telegraphy/MS
+telekineses
+telekinesis/M
+telekinetic
+Telemachus/M
+Telemann/M
+telemarketer/S
+telemarketing/S
+telemeter/DMSG
+telemetric
+telemetry/MS
+teleological/Y
+teleology/M
+telepathic
+telepathically
+telepathy/SM
+telephone/SRDGMZ
+telephonic
+telephonist/SM
+telephony/MS
+telephotography/MS
+telephoto/S
+teleprinter/MS
+teleprocessing/S
+teleprompter
+TelePrompter/M
+TelePrompTer/S
+telescope/GSDM
+telescopic
+telescopically
+teletext/S
+telethon/MS
+teletype/SM
+Teletype/SM
+teletypewriter/SM
+televangelism/S
+televangelist/S
+televise/SDXNG
+television/M
+televisor/MS
+televisual
+telex/GSDM
+Telex/M
+tell/AGS
+Teller/M
+teller/SDMG
+telling/YS
+Tell/MR
+telltale/MS
+tellurium/SM
+telly/SM
+Telnet/M
+TELNET/M
+telnet/S
+telomeric
+tel/SY
+Telugu/M
+temblor/SM
+temerity/MS
+Tempe/M
+temperamental/Y
+temperament/SM
+temperance/IMS
+tempera/SLM
+temperately/I
+temperateness's/I
+temperateness/SM
+temperate/SDGPY
+temperature/MS
+tempered/UE
+temper/GRDM
+tempering/E
+temper's/E
+tempers/E
+tempest/DMSG
+tempestuousness/SM
+tempestuous/PY
+template/FS
+template's
+Temple/M
+Templeman/M
+temple/SDM
+Templeton/M
+Temp/M
+tempoes
+tempo/MS
+temporal/YS
+temporarily
+temporarinesses
+temporariness/FM
+temporary/SFP
+temporize/GJZRSD
+temporizer/M
+temporizings/U
+temporizing/YM
+temp/SGZTMRD
+temptation/MS
+tempted
+tempter/S
+tempt/FS
+tempting/YS
+temptress/MS
+tempura/SM
+tenabilities
+tenability/UM
+tenableness/M
+tenable/P
+tenably
+tenaciousness/S
+tenacious/YP
+tenacity/S
+tenancy/MS
+tenanted/U
+tenant/MDSG
+tenantry/MS
+tench/M
+tended/UE
+tendency/MS
+tendentiousness/SM
+tendentious/PY
+tendered
+tenderer
+tenderest
+tenderfoot/MS
+tender/FS
+tenderheartedness/MS
+tenderhearted/YP
+tendering
+tenderizer/M
+tenderize/SRDGZ
+tenderloin/SM
+tenderly
+tenderness/SM
+tending/E
+tendinitis/S
+tend/ISFRDG
+tendon/MS
+tendril/SM
+tends/E
+tenebrous
+tenement/MS
+tenet/SM
+Tenex/M
+TENEX/M
+tenfold/S
+ten/MHB
+Tenneco/M
+tenner
+Tennessean/S
+Tennessee/M
+Tenney/M
+tennis/SM
+Tenn/M
+Tennyson/M
+Tenochtitlan/M
+tenon/GSMD
+tenor/MS
+tenpin/SM
+tense/IPYTNVR
+tenseness's/I
+tenseness/SM
+tensile
+tensional/I
+tension/GMRDS
+tensionless
+tensions/E
+tension's/I
+tensity/IMS
+tensorial
+tensor/MS
+tenspot
+tens/SRDVGT
+tentacle/MSD
+tentativeness/S
+tentative/SPY
+tented/UF
+tenterhook/MS
+tenter/M
+tent/FSIM
+tenths
+tenth/SY
+tenting/F
+tenuity/S
+tenuousness/SM
+tenuous/YP
+tenure/SDM
+Teodoor/M
+Teodora/M
+Teodorico/M
+Teodor/M
+Teodoro/M
+tepee/MS
+tepidity/S
+tepidness/S
+tepid/YP
+tequila/SM
+Tera/M
+teratogenic
+teratology/MS
+terbium/SM
+tercel/M
+tercentenary/S
+tercentennial/S
+Terence/M
+Terencio/M
+Teresa/M
+Terese/M
+Tereshkova/M
+Teresina/M
+Teresita/M
+Teressa/M
+Teriann/M
+Teri/M
+Terkel/M
+termagant/SM
+termcap
+termer/M
+terminable/CPI
+terminableness/IMC
+terminal/SYM
+terminate/CXNV
+terminated/U
+terminates
+terminating
+termination/MC
+terminative/YC
+terminator/SM
+termini
+terminological/Y
+terminology/MS
+terminus/M
+termite/SM
+term/MYRDGS
+ternary/S
+tern/GIDS
+tern's
+terpsichorean
+Terpsichore/M
+terrace/MGSD
+terracing/M
+terracotta
+terrain/MS
+Terra/M
+terramycin
+Terrance/M
+Terran/M
+terrapin/MS
+terrarium/MS
+terrazzo/SM
+Terrell/M
+Terrel/M
+Terre/M
+Terrence/M
+terrestrial/YMS
+terribleness/SM
+terrible/P
+terribly
+Terrie/M
+terrier/M
+terrifically
+terrific/Y
+terrify/GDS
+terrifying/Y
+Terrijo/M
+Terrill/M
+Terri/M
+terrine/M
+territoriality/M
+Territorial/SM
+territorial/SY
+Territory's
+territory/SM
+terrorism/MS
+terroristic
+terrorist/MS
+terrorized/U
+terrorizer/M
+terrorize/RSDZG
+terror/MS
+terr/S
+terrycloth
+Terrye/M
+Terry/M
+terry/ZMRS
+terseness/SM
+terse/RTYP
+Tersina/M
+tertian
+Tertiary
+tertiary/S
+Terza/M
+TESL
+Tesla/M
+TESOL
+Tessa/M
+tessellate/XDSNG
+tessellation/M
+tesseral
+Tessie/M
+Tessi/M
+Tess/M
+Tessy/M
+testability/M
+testable/U
+testamentary
+testament/SM
+testate/IS
+testator/MS
+testatrices
+testatrix
+testbed/S
+testcard
+tested/AKU
+tester/MFCKS
+testes/M
+testicle/SM
+testicular
+testifier/M
+testify/GZDRS
+testily
+testimonial/SM
+testimony/SM
+testiness/S
+testing/S
+testis/M
+testosterone/SM
+test/RDBFZGSC
+tests/AK
+test's/AKF
+testy/RTP
+tetanus/MS
+tetchy/TR
+tether/DMSG
+tethered/U
+Tethys/M
+Tetons
+tetrachloride/M
+tetracycline/SM
+tetrafluoride
+tetragonal/Y
+tetrahalides
+tetrahedral/Y
+tetrahedron/SM
+tetrameron
+tetrameter/SM
+tetra/MS
+tetrasodium
+tetravalent
+Teutonic
+Teuton/SM
+Texaco/M
+Texan/S
+Texas/MS
+Tex/M
+TeX/M
+textbook/SM
+text/FSM
+textile/SM
+Textron/M
+textual/FY
+textural/Y
+textured/U
+texture/MGSD
+T/G
+Thacher/M
+Thackeray/M
+Thaddeus/M
+Thaddus/M
+Thadeus/M
+Thad/M
+Thailand/M
+Thaine/M
+Thain/M
+Thai/S
+thalami
+thalamus/M
+Thales/M
+Thalia/M
+thalidomide/MS
+thallium/SM
+thallophyte/M
+Thames
+than
+Thane/M
+thane/SM
+Thanh/M
+thanker/M
+thankfuller
+thankfullest
+thankfulness/SM
+thankful/YP
+thanklessness/SM
+thankless/PY
+thanksgiving/MS
+Thanksgiving/S
+thank/SRDG
+Thant/M
+Thar/M
+Thatcher/M
+thatching/M
+thatch/JMDRSZG
+Thatch/MR
+that'd
+that'll
+that/MS
+thaumaturge/M
+thaw/DGS
+Thaxter/M
+Thayer/M
+Thayne/M
+THC
+the
+Theadora/M
+Thea/M
+theatergoer/MS
+theatergoing/MS
+theater/SM
+theatricality/SM
+theatrical/YS
+theatric/S
+theatrics/M
+Thebault/M
+Thebes
+Theda/M
+Thedrick/M
+Thedric/M
+thee/DS
+theeing
+theft/MS
+Theiler/M
+their/MS
+theism/SM
+theistic
+theist/SM
+Thekla/M
+Thelma/M
+themas
+thematically
+thematics
+thematic/U
+theme/MS
+them/GD
+Themistocles/M
+themselves
+thence
+thenceforth
+thenceforward/S
+Theobald/M
+theocracy/SM
+theocratic
+Theocritus/M
+theodolite/MS
+Theodora/M
+Theodore/M
+Theodoric/M
+Theodor/M
+Theodosia/M
+Theodosian
+Theodosius/M
+theologian/SM
+theological/Y
+theologists
+theology/MS
+Theo/M
+theorem/MS
+theoretical/Y
+theoretician/MS
+theoretic/S
+theoretics/M
+theorist/SM
+theorization/SM
+theorize/ZGDRS
+theory/MS
+theosophic
+theosophical
+theosophist/MS
+Theosophy
+theosophy/SM
+therapeutically
+therapeutic/S
+therapeutics/M
+therapist/MS
+therapy/MS
+Theravada/M
+thereabout/S
+thereafter
+thereat
+thereby
+there'd
+therefor
+therefore
+therefrom
+therein
+there'll
+there/MS
+thereof
+thereon
+Theresa/M
+Therese/M
+Theresina/M
+Theresita/M
+Theressa/M
+thereto
+theretofore
+thereunder
+thereunto
+thereupon
+therewith
+Therine/M
+thermal/YS
+thermionic/S
+thermionics/M
+thermistor/MS
+therm/MS
+thermocouple/MS
+thermodynamical/Y
+thermodynamic/S
+thermodynamics/M
+thermoelastic
+thermoelectric
+thermoformed
+thermoforming
+thermogravimetric
+thermoluminescence/M
+thermometer/MS
+thermometric
+thermometry/M
+thermonuclear
+thermopile/M
+thermoplastic/S
+thermopower
+thermo/S
+thermosetting
+thermos/S
+Thermos/SM
+thermostable
+thermostatically
+thermostatic/S
+thermostatics/M
+thermostat/SM
+thermostatted
+thermostatting
+Theron/M
+thesauri
+thesaurus/MS
+these/S
+Theseus/M
+thesis/M
+thespian/S
+Thespian/S
+Thespis/M
+Thessalonian
+Thessaloníki/M
+Thessaly/M
+theta/MS
+thew/SM
+they
+they'd
+they'll
+they're
+they've
+th/GNJX
+Thia/M
+thiamine/MS
+Thibaud/M
+Thibaut/M
+thickener/M
+thickening/M
+thicken/RDJZG
+thicket/SMD
+thickheaded/M
+thickish
+thickness/MS
+thickset/S
+thick/TXPSRNY
+thief/M
+Thiensville/M
+Thieu/M
+thievery/MS
+thieve/SDJG
+thievishness/M
+thievish/P
+thighbone/SM
+thigh/DM
+thighs
+thimble/DSMG
+thimbleful/MS
+Thimbu/M
+Thimphu
+thine
+thingamabob/MS
+thingamajig/SM
+thing/MP
+thinkableness/M
+thinkable/U
+thinkably/U
+think/AGRS
+thinker/MS
+thinkingly/U
+thinking/SMYP
+thinned
+thinner/MS
+thinness/MS
+thinnest
+thinning
+thinnish
+thin/STPYR
+thiocyanate/M
+thiouracil/M
+third/DYGS
+thirster/M
+thirst/GSMDR
+thirstily
+thirstiness/S
+thirsty/TPR
+thirteen/MHS
+thirteenths
+thirtieths
+thirty/HMS
+this
+this'll
+thistledown/MS
+thistle/SM
+thither
+Th/M
+tho
+thole/GMSD
+Thomasa/M
+Thomasina/M
+Thomasine/M
+Thomasin/M
+Thoma/SM
+Thomism/M
+Thomistic
+Thom/M
+Thompson/M
+Thomson/M
+thong/SMD
+thoracic
+thorax/MS
+Thorazine
+Thoreau/M
+thoriate/D
+Thorin/M
+thorium/MS
+Thor/M
+Thornburg/M
+Thorndike/M
+Thornie/M
+thorniness/S
+Thorn/M
+thorn/SMDG
+Thornton/M
+Thorny/M
+thorny/PTR
+thoroughbred/S
+thoroughfare/MS
+thoroughgoing
+thoroughness/SM
+thorough/PTYR
+Thorpe/M
+Thorstein/M
+Thorsten/M
+Thorvald/M
+those
+Thoth/M
+thou/DSG
+though
+thoughtfully
+thoughtfulness/S
+thoughtful/U
+thoughtlessness/MS
+thoughtless/YP
+thought/MS
+thousandfold
+thousand/SHM
+thousandths
+Thrace/M
+Thracian/M
+thralldom/S
+thrall/GSMD
+thrash/DSRZGJ
+thrasher/M
+thrashing/M
+threadbare/P
+threader/M
+threading/A
+threadlike
+thread/MZDRGS
+thready/RT
+threatener/M
+threaten/GJRD
+threatening/Y
+threat/MDNSXG
+threefold
+three/MS
+threepence/M
+threepenny
+threescore/S
+threesome/SM
+threnody/SM
+thresh/DSRZG
+thresher/M
+threshold/MDGS
+threw
+thrice
+thriftily
+thriftiness/S
+thriftless
+thrift/SM
+thrifty/PTR
+thriller/M
+thrilling/Y
+thrill/ZMGDRS
+thriver/M
+thrive/RSDJG
+thriving/Y
+throatily
+throatiness/MS
+throat/MDSG
+throaty/PRT
+throbbed
+throbbing
+throb/S
+throeing
+throe/SDM
+thrombi
+thromboses
+thrombosis/M
+thrombotic
+thrombus/M
+Throneberry/M
+throne/CGSD
+throne's
+throng/GDSM
+throttle/DRSZMG
+throttler/M
+throughout
+throughput/SM
+throughway's
+through/Y
+throwaway/SM
+throwback/MS
+thrower/M
+thrown
+throwout
+throw/SZGR
+thrummed
+thrumming
+thrum/S
+thrush/MS
+thruster/M
+thrust/ZGSR
+Thruway/MS
+thruway/SM
+Thu
+Thucydides/M
+thudded
+thudding
+thud/MS
+thuggee/M
+thuggery/SM
+thuggish
+thug/MS
+Thule/M
+thulium/SM
+thumbnail/MS
+thumbscrew/SM
+thumb/SMDG
+thumbtack/GMDS
+thump/RDMSG
+thunderbolt/MS
+thunderclap/SM
+thundercloud/SM
+thunderer/M
+thunderhead/SM
+thundering/Y
+thunderous/Y
+thundershower/MS
+thunderstorm/MS
+thunderstruck
+thundery
+thunder/ZGJDRMS
+thunk
+Thurber/M
+Thurman/M
+Thur/MS
+Thursday/SM
+Thurstan/M
+Thurston/M
+thus/Y
+thwack/DRSZG
+thwacker/M
+thwarter/M
+thwart/GSDRY
+thy
+thyme/SM
+thymine/MS
+thymus/SM
+thyratron/M
+thyristor/MS
+thyroglobulin
+thyroidal
+thyroid/S
+thyronine
+thyrotoxic
+thyrotrophic
+thyrotrophin
+thyrotropic
+thyrotropin/M
+thyroxine/M
+thyself
+Tia/M
+Tianjin
+tiara/MS
+Tiberius/M
+Tiber/M
+Tibetan/S
+Tibet/M
+tibiae
+tibial
+tibia/M
+Tibold/M
+Tiburon/M
+ticker/M
+ticket/SGMD
+tick/GZJRDMS
+ticking/M
+tickler/M
+tickle/RSDZG
+ticklishness/MS
+ticklish/PY
+ticktacktoe/S
+ticktock/SMDG
+tic/MS
+Ticonderoga/M
+tidal/Y
+tidbit/MS
+tiddlywinks/M
+tide/GJDS
+tideland/MS
+tidewater/SM
+tideway/SM
+tidily/U
+tidiness/USM
+tidying/M
+tidy/UGDSRPT
+tie/AUDS
+tieback/MS
+Tiebold/M
+Tiebout/M
+tiebreaker/SM
+Tieck/M
+Tiena/M
+Tienanmen/M
+Tientsin's
+tier/DGM
+Tierney/M
+Tiertza/M
+Tiffanie/M
+Tiffani/M
+tiffany/M
+Tiffany/M
+tiff/GDMS
+Tiffie/M
+Tiffi/M
+Tiff/M
+Tiffy/M
+tigerish
+tiger/SM
+tightener/M
+tighten/JZGDR
+tightfisted
+tightness/MS
+tightrope/SM
+tight/STXPRNY
+tightwad/MS
+tigress/SM
+Tigris/M
+Tijuana/M
+tike's
+Tilda/M
+tilde/MS
+Tildie/M
+Tildi/M
+Tildy/M
+tile/DRSJMZG
+tiled/UE
+Tiler/M
+tiles/U
+tiling/M
+tillable
+tillage/SM
+till/EGSZDR
+tiller/GDM
+tiller's/E
+Tillich/M
+Tillie/M
+Tillman/M
+Tilly/M
+tilth/M
+tilt/RDSGZ
+Ti/M
+timber/DMSG
+timbering/M
+timberland/SM
+timberline/S
+timbrel/SM
+timbre/MS
+Timbuktu/M
+ti/MDRZ
+timebase
+time/DRSJMYZG
+timekeeper/MS
+timekeeping/SM
+timelessness/S
+timeless/PY
+timeliness/SMU
+timely/UTRP
+timeout/S
+timepiece/MS
+timer/M
+timescale/S
+timeserver/MS
+timeserving/S
+timeshare/SDG
+timespan
+timestamped
+timestamps
+timetable/GMSD
+timeworn
+Timex/M
+timezone/S
+timidity/SM
+timidness/MS
+timid/RYTP
+Timi/M
+timing/M
+Timmie/M
+Timmi/M
+Tim/MS
+Timmy/M
+Timofei/M
+Timon/M
+timorousness/MS
+timorous/YP
+Timoteo/M
+Timothea/M
+Timothee/M
+Timotheus/M
+Timothy/M
+timothy/MS
+timpani
+timpanist/S
+Timur/M
+Tina/M
+tincture/SDMG
+tinderbox/MS
+tinder/MS
+Tine/M
+tine/SM
+tinfoil/MS
+tingeing
+tinge/S
+ting/GYDM
+tingle/SDG
+tingling/Y
+tingly/TR
+Ting/M
+tinily
+tininess/MS
+tinker/SRDMZG
+Tinkertoy
+tinkle/SDG
+tinkling/M
+tinkly
+tin/MDGS
+tinned
+tinner/M
+tinnily
+tinniness/SM
+tinning/M
+tinnitus/MS
+tinny/RSTP
+tinplate/S
+tinsel/GMDYS
+Tinseltown/M
+tinsmith/M
+tinsmiths
+tinter/M
+tintinnabulation/MS
+Tintoretto/M
+tint/SGMRDB
+tintype/SM
+tinware/MS
+tiny/RPT
+Tioga/M
+Tiphanie/M
+Tiphani/M
+Tiphany/M
+tipi's
+tip/MS
+tipoff
+Tippecanoe/M
+tipped
+Tipperary/M
+tipper/MS
+tippet/MS
+tipping
+tippler/M
+tipple/ZGRSD
+tippy/R
+tipsily
+tipsiness/SM
+tipster/SM
+tipsy/TPR
+tiptoeing
+tiptoe/SD
+tiptop/S
+tirade/SM
+Tirana's
+Tirane
+tired/AYP
+tireder
+tiredest
+tiredness/S
+tirelessness/SM
+tireless/PY
+tire/MGDSJ
+tires/A
+Tiresias/M
+tiresomeness/S
+tiresome/PY
+tiring/AU
+Tirolean/S
+Tirol/M
+tiro's
+Tirrell/M
+tis
+Tisha/M
+Tish/M
+tissue/MGSD
+titanate/M
+Titania/M
+titanic
+titanically
+Titanic/M
+titanium/SM
+titan/SM
+Titan/SM
+titbit's
+titer/M
+tither/M
+tithe/SRDGZM
+tithing/M
+Titian/M
+titian/S
+Titicaca/M
+titillate/XSDVNG
+titillating/Y
+titillation/M
+titivate/NGDSX
+titivation/M
+titled/AU
+title/GMSRD
+titleholder/SM
+titling/A
+titmice
+titmouse/M
+tit/MRZS
+Tito/SM
+titrate/SDGN
+titration/M
+titted
+titter/GDS
+titting
+tittle/SDMG
+titular/SY
+Titus/M
+tizzy/SM
+TKO
+Tlaloc/M
+TLC
+Tlingit/M
+Tl/M
+TM
+Tm/M
+tn
+TN
+tnpk
+TNT
+toad/SM
+toadstool/SM
+toady/GSDM
+toadyism/M
+toaster/M
+toastmaster/MS
+toastmistress/S
+toast/SZGRDM
+toasty/TRS
+tobacconist/SM
+tobacco/SM
+tobaggon/SM
+Tobago/M
+Tobe/M
+Tobey/M
+Tobiah/M
+Tobias/M
+Tobie/M
+Tobi/M
+Tobin/M
+Tobit/M
+toboggan/MRDSZG
+Tobye/M
+Toby/M
+Tocantins/M
+toccata/M
+Tocqueville
+tocsin/MS
+to/D
+today'll
+today/SM
+Toddie/M
+toddler/M
+toddle/ZGSRD
+Todd/M
+Toddy/M
+toddy/SM
+Tod/M
+toecap/SM
+toeclip/S
+TOEFL
+toehold/MS
+toeing
+toe/MS
+toenail/DMGS
+toffee/SM
+tofu/S
+toga/SMD
+toge
+togetherness/MS
+together/P
+togged
+togging
+toggle/SDMG
+Togolese/M
+Togo/M
+tog/SMG
+Toiboid/M
+toilet/GMDS
+toiletry/MS
+toilette/SM
+toil/SGZMRD
+toilsomeness/M
+toilsome/PY
+Toinette/M
+Tojo/M
+tokamak
+Tokay/M
+toke/GDS
+tokenism/SM
+tokenized
+token/SMDG
+Tokugawa/M
+Tokyoite/MS
+Tokyo/M
+Toland/M
+told/AU
+Toledo/SM
+tole/MGDS
+tolerability/IM
+tolerable/I
+tolerably/I
+tolerance/SIM
+tolerant/IY
+tolerate/XVNGSD
+toleration/M
+Tolkien
+tollbooth/M
+tollbooths
+toll/DGS
+Tolley/M
+tollgate/MS
+tollhouse/M
+tollway/S
+Tolstoy/M
+toluene/MS
+Tolyatti/M
+tomahawk/SGMD
+Tomasina/M
+Tomasine/M
+Toma/SM
+Tomaso/M
+tomatoes
+tomato/M
+Tombaugh/M
+tomb/GSDM
+Tombigbee/M
+tomblike
+tombola/M
+tomboyish
+tomboy/MS
+tombstone/MS
+tomcat/SM
+tomcatted
+tomcatting
+Tome/M
+tome/SM
+tomfoolery/MS
+tomfool/M
+Tomi/M
+Tomkin/M
+Tomlin/M
+Tom/M
+tommed
+Tommie/M
+Tommi/M
+tomming
+tommy/M
+Tommy/M
+tomographic
+tomography/MS
+tomorrow/MS
+Tompkins/M
+Tomsk/M
+tom/SM
+tomtit/SM
+tonality/MS
+tonal/Y
+tonearm/S
+tone/ISRDZG
+tonelessness/M
+toneless/YP
+toner/IM
+tone's
+Tonga/M
+Tongan/SM
+tong/GRDS
+tongueless
+tongue/SDMG
+tonguing/M
+Tonia/M
+tonic/SM
+Tonie/M
+tonight/MS
+Toni/M
+Tonio/M
+tonk/MS
+tonnage/SM
+tonne/MS
+Tonnie/M
+tonsillectomy/MS
+tonsillitis/SM
+tonsil/SM
+ton/SKM
+tonsorial
+tonsure/SDGM
+Tonto/M
+Tonya/M
+Tonye/M
+Tony/M
+tony/RT
+toodle
+too/H
+took/A
+tool/AGDS
+toolbox/SM
+tooler/SM
+tooling/M
+toolkit/SM
+toolmaker/M
+toolmake/ZRG
+toolmaking/M
+tool's
+toolsmith
+Toomey/M
+tooter/M
+toot/GRDZS
+toothache/SM
+toothbrush/MSG
+tooth/DMG
+toothily
+toothless
+toothmarks
+toothpaste/SM
+toothpick/MS
+tooths
+toothsome
+toothy/TR
+tootle/SRDG
+tootsie
+Tootsie/M
+toots/M
+tootsy/MS
+topaz/MS
+topcoat/MS
+topdressing/S
+Topeka/M
+toper/M
+topflight
+topgallant/M
+topiary/S
+topicality/MS
+topical/Y
+topic/MS
+topknot/MS
+topless
+topmast/MS
+topmost
+topnotch/R
+topocentric
+topographer/SM
+topographic
+topographical/Y
+topography/MS
+topological/Y
+topologist/MS
+topology/MS
+topped
+topper/MS
+topping/MS
+topple/GSD
+topsail/MS
+topside/SRM
+top/SMDRG
+topsoil/GDMS
+topspin/MS
+Topsy/M
+toque/MS
+Torah/M
+Torahs
+torchbearer/SM
+torchlight/S
+torch/SDMG
+toreador/SM
+Tore/M
+tore/S
+Torey/M
+Torie/M
+tori/M
+Tori/M
+Torin/M
+torment/GSD
+tormenting/Y
+tormentor/MS
+torn
+tornadoes
+tornado/M
+toroidal/Y
+toroid/MS
+Toronto/M
+torpedoes
+torpedo/GMD
+torpidity/S
+torpid/SY
+torpor/MS
+Torquemada/M
+torque/MZGSRD
+Torrance/M
+Torre/MS
+torrence
+Torrence/M
+Torrens/M
+torrential
+torrent/MS
+Torrey/M
+Torricelli/M
+torridity/SM
+torridness/SM
+torrid/RYTP
+Torrie/M
+Torrin/M
+Torr/XM
+Torry/M
+torsional/Y
+torsion/IAM
+torsions
+torsi's
+tor/SLM
+torso/SM
+tors/S
+tort/ASFE
+tortellini/MS
+torte/MS
+torten
+tortilla/MS
+tortoiseshell/SM
+tortoise/SM
+Tortola/M
+tortoni/MS
+tort's
+Tortuga/M
+tortuousness/MS
+tortuous/PY
+torture/ZGSRD
+torturous
+torus/MS
+Tory/SM
+Tosca/M
+Toscanini/M
+Toshiba/M
+toss/SRDGZ
+tossup/MS
+totaler/M
+totalistic
+totalitarianism/SM
+totalitarian/S
+totality/MS
+totalizator/S
+totalizing
+total/ZGSRDYM
+totemic
+totem/MS
+toter/M
+tote/S
+toting/M
+tot/MDRSG
+Toto/M
+totted
+totterer/M
+tottering/Y
+totter/ZGRDS
+totting
+toucan/MS
+touchable/U
+touch/ASDG
+touchdown/SM
+touché
+touched/U
+toucher/M
+touchily
+touchiness/SM
+touching/SY
+touchline/M
+touchscreen
+touchstone/SM
+touchy/TPR
+toughen/DRZG
+toughener/M
+toughness/SM
+toughs
+tough/TXGRDNYP
+Toulouse/M
+toupee/SM
+toured/CF
+tourer/M
+tour/GZSRDM
+touring/F
+tourism/SM
+touristic
+tourist/SM
+touristy
+tourmaline/SM
+tournament/MS
+tourney/GDMS
+tourniquet/MS
+tour's/CF
+tours/CF
+tousle/GSD
+touter/M
+tout/SGRD
+Tova/M
+Tove/M
+towardliness/M
+towardly/P
+towards
+toward/YU
+towboat/MS
+tow/DRSZG
+towelette/S
+towel/GJDMS
+toweling/M
+tower/GMD
+towering/Y
+towhead/MSD
+towhee/SM
+towline/MS
+towner/M
+Townes
+Towney/M
+townhouse/S
+Townie/M
+townie/S
+Townley/M
+Town/M
+Townsend/M
+townsfolk
+township/MS
+townsman/M
+townsmen
+townspeople/M
+town/SRM
+townswoman/M
+townswomen
+Towny/M
+towpath/M
+towpaths
+towrope/MS
+Towsley/M
+toxemia/MS
+toxicity/MS
+toxicological
+toxicologist/SM
+toxicology/MS
+toxic/S
+toxin/MS
+toyer/M
+toymaker
+toy/MDRSG
+Toynbee/M
+Toyoda/M
+Toyota/M
+toyshop
+tr
+traceability/M
+traceableness/M
+traceable/P
+trace/ASDG
+traceback/MS
+traced/U
+Tracee/M
+traceless/Y
+Trace/M
+tracepoint/SM
+tracer/MS
+tracery/MDS
+trace's
+Tracey/M
+tracheae
+tracheal/M
+trachea/M
+tracheotomy/SM
+Tracie/M
+Traci/M
+tracing/SM
+trackage
+trackball/S
+trackbed
+tracked/U
+tracker/M
+trackless
+tracksuit/SM
+track/SZGMRD
+tractability/SI
+tractable/I
+tractably/I
+tract/ABS
+Tractarians
+traction/KSCEMAF
+tractive/KFE
+tractor/FKMASC
+tract's
+tracts/CEFK
+Tracy/M
+trademark/GSMD
+trader/M
+tradesman/M
+tradesmen
+tradespeople
+tradespersons
+trade/SRDGZM
+tradeswoman/M
+tradeswomen
+traditionalism/MS
+traditionalistic
+traditionalist/MS
+traditionalized
+traditionally
+traditional/U
+tradition/SM
+traduce/DRSGZ
+Trafalgar/M
+trafficked
+trafficker/MS
+trafficking/S
+traffic/SM
+tragedian/SM
+tragedienne/MS
+tragedy/MS
+tragically
+tragicomedy/SM
+tragicomic
+tragic/S
+trailblazer/MS
+trailblazing/S
+trailer/GDM
+trails/F
+trailside
+trail/SZGJRD
+trainable
+train/ASDG
+trained/U
+trainee/MS
+traineeships
+trainer/MS
+training/SM
+trainman/M
+trainmen
+trainspotter/S
+traipse/DSG
+trait/MS
+traitorous/Y
+traitor/SM
+Trajan/M
+trajectory/MS
+trammed
+trammeled/U
+trammel/GSD
+tramming
+tram/MS
+trample/DGRSZ
+trampler/M
+trampoline/GMSD
+tramp/RDSZG
+tramway/M
+trance/MGSD
+tranche/SM
+Tran/M
+tranquility/S
+tranquilized/U
+tranquilize/JGZDSR
+tranquilizer/M
+tranquilizes/A
+tranquilizing/YM
+tranquillize/GRSDZ
+tranquillizer/M
+tranquilness/M
+tranquil/PTRY
+transact/GSD
+transactional
+transaction/MS
+transactor/SM
+transalpine
+transaminase
+transatlantic
+Transcaucasia/M
+transceiver/SM
+transcendence/MS
+transcendentalism/SM
+transcendentalist/SM
+transcendental/YS
+transcendent/Y
+transcend/SDG
+transconductance
+transcontinental
+transcribe/DSRGZ
+transcriber/M
+transcription/SM
+transcript/SM
+transcultural
+transducer/SM
+transduction/M
+transect/DSG
+transept/SM
+transferability/M
+transferal/MS
+transfer/BSMD
+transferee/M
+transference/SM
+transferor/MS
+transferral/SM
+transferred
+transferrer/SM
+transferring
+transfiguration/SM
+transfigure/SDG
+transfinite/Y
+transfix/SDG
+transformational
+transformation/MS
+transform/DRZBSG
+transformed/U
+transformer/M
+transfuse/XSDGNB
+transfusion/M
+transgression/SM
+transgressor/S
+transgress/VGSD
+trans/I
+transience/SM
+transiency/S
+transient/YS
+transistorize/GDS
+transistor/SM
+Transite/M
+transitional/Y
+transition/MDGS
+transitivenesses
+transitiveness/IM
+transitive/PIY
+transitivity/MS
+transitoriness/M
+transitory/P
+transit/SGVMD
+transl
+translatability/M
+translatable/U
+translated/AU
+translate/VGNXSDB
+translational
+translation/M
+translator/SM
+transliterate/XNGSD
+translucence/SM
+translucency/MS
+translucent/Y
+transmigrate/XNGSD
+transmissible
+transmission/MSA
+transmissive
+transmit/AS
+transmittable
+transmittal/SM
+transmittance/MS
+transmitted/A
+transmitter/SM
+transmitting/A
+transmogrification/M
+transmogrify/GXDSN
+transmutation/SM
+transmute/GBSD
+transnational/S
+transoceanic
+transom/SM
+transonic
+transpacific
+transparency/MS
+transparentness/M
+transparent/YP
+transpiration/SM
+transpire/GSD
+transplantation/S
+transplant/GRDBS
+transpolar
+transponder/MS
+transportability
+transportable/U
+transportation/SM
+transport/BGZSDR
+transpose/BGSD
+transposed/U
+transposition/SM
+Transputer/M
+transsexualism/MS
+transsexual/SM
+transship/LS
+transshipment/SM
+transshipped
+transshipping
+transubstantiation/MS
+Transvaal/M
+transversal/YM
+transverse/GYDS
+transvestism/SM
+transvestite/SM
+transvestitism
+Transylvania/M
+trapdoor/S
+trapeze/DSGM
+trapezium/MS
+trapezoidal
+trapezoid/MS
+trap/MS
+trappable/U
+trapped
+trapper/SM
+trapping/S
+Trappist/MS
+trapshooting/SM
+trashcan/SM
+trashiness/SM
+trash/SRDMG
+trashy/TRP
+Trastevere/M
+trauma/MS
+traumatic
+traumatically
+traumatize/SDG
+travail/SMDG
+traveled/U
+traveler/M
+travelog's
+travelogue/S
+travel/SDRGZJ
+Traver/MS
+traversal/SM
+traverse/GBDRS
+traverser/M
+travertine/M
+travesty/SDGM
+Travis/M
+Travus/M
+trawler/M
+trawl/RDMSZG
+tray/SM
+treacherousness/SM
+treacherous/PY
+treachery/SM
+treacle/DSGM
+treacly
+treader/M
+treadle/GDSM
+treadmill/MS
+tread/SAGD
+Treadwell/M
+treas
+treason/BMS
+treasonous
+treasure/DRSZMG
+treasurer/M
+treasurership
+treasury/SM
+Treasury/SM
+treatable
+treated/U
+treater/S
+treatise/MS
+treatment/MS
+treat's
+treat/SAGDR
+treaty/MS
+treble/SDG
+Treblinka/M
+treeing
+treeless
+treelike
+tree/MDS
+treetop/SM
+trefoil/SM
+Trefor/M
+trekked
+trekker/MS
+Trekkie/M
+trekking
+trek/MS
+trellis/GDSM
+Tremaine/M
+Tremain/M
+trematode/SM
+Tremayne/M
+tremble/JDRSG
+trembler/M
+trembles/M
+trembly
+tremendousness/M
+tremendous/YP
+tremolo/MS
+tremor/MS
+tremulousness/SM
+tremulous/YP
+trenchancy/MS
+trenchant/Y
+trencherman/M
+trenchermen
+trencher/SM
+trench/GASD
+trench's
+trendily
+trendiness/S
+trend/SDMG
+trendy/PTRS
+Trenna/M
+Trent/M
+Trenton/M
+trepanned
+trepidation/MS
+Tresa/M
+Trescha/M
+trespasser/M
+trespass/ZRSDG
+Tressa/M
+tressed/E
+tresses/E
+tressing/E
+tress/MSDG
+trestle/MS
+Trevar/M
+Trevelyan/M
+Trever/M
+Trevino/M
+Trevor/M
+Trev/RM
+Trey/M
+trey/MS
+triableness/M
+triable/P
+triadic
+triad/MS
+triage/SDMG
+trial/ASM
+trialization
+trialled
+trialling
+triamcinolone
+triangle/SM
+triangulable
+triangularization/S
+triangular/Y
+triangulate/YGNXSD
+triangulation/M
+Triangulum/M
+Trianon/M
+Triassic
+triathlon/S
+triatomic
+tribalism/MS
+tribal/Y
+tribe/MS
+tribesman/M
+tribesmen
+tribeswoman
+tribeswomen
+tribulate/NX
+tribulation/M
+tribunal/MS
+tribune/SM
+tributary/MS
+tribute/EGSF
+tribute's
+trice/GSDM
+tricentennial/S
+triceps/SM
+triceratops/M
+trichinae
+trichina/M
+trichinoses
+trichinosis/M
+trichloroacetic
+trichloroethane
+trichotomy/M
+trichromatic
+Tricia/M
+trickery/MS
+trick/GMSRD
+trickily
+trickiness/SM
+trickle/DSG
+trickster/MS
+tricky/RPT
+tricolor/SMD
+tricycle/SDMG
+trident/SM
+tridiagonal
+tried/UA
+triennial/SY
+trier/AS
+trier's
+tries/A
+Trieste/M
+triffid/S
+trifle/MZGJSRD
+trifler/M
+trifluoride/M
+trifocals
+trigged
+trigger/GSDM
+triggest
+trigging
+triglyceride/MS
+trigonal/Y
+trigonometric
+trigonometrical
+trigonometry/MS
+trigram/S
+trig/S
+trihedral
+trike/GMSD
+trilateral/S
+trilby/SM
+trilingual
+trillion/SMH
+trillionth/M
+trillionths
+trillium/SM
+trill/RDMGS
+trilobite/MS
+trilogy/MS
+trimaran/MS
+Trimble/M
+trimer/M
+trimester/MS
+trimmed/U
+trimmer/MS
+trimmest
+trimming/MS
+trimness/S
+trimodal
+trimonthly
+trim/PSYR
+Trimurti/M
+Trina/M
+Trinidad/M
+trinitarian/S
+trinitrotoluene/SM
+trinity/MS
+Trinity/MS
+trinketer/M
+trinket/MRDSG
+triode/MS
+trio/SM
+trioxide/M
+tripartite/N
+tripartition/M
+tripe/MS
+triphenylarsine
+triphenylphosphine
+triphenylstibine
+triphosphopyridine
+triple/GSD
+triplet/SM
+triplex/S
+triplicate/SDG
+triplication/M
+triply/GDSN
+Trip/M
+tripodal
+tripod/MS
+tripoli/M
+Tripoli/M
+tripolyphosphate
+tripos/SM
+tripped
+Trippe/M
+tripper/MS
+tripping/Y
+Tripp/M
+trip/SMY
+triptych/M
+triptychs
+tripwire/MS
+trireme/SM
+Tris
+trisect/GSD
+trisection/S
+trisector
+Trisha/M
+Trish/M
+trisodium
+Trista/M
+Tristam/M
+Tristan/M
+tristate
+trisyllable/M
+tritely/F
+triteness/SF
+trite/SRPTY
+tritium/MS
+triton/M
+Triton/M
+triumphal
+triumphalism
+triumphant/Y
+triumph/GMD
+triumphs
+triumvirate/MS
+triumvir/MS
+triune
+trivalent
+trivet/SM
+trivia
+triviality/MS
+trivialization/MS
+trivialize/DSG
+trivial/Y
+trivium/M
+Trixie/M
+Trixi/M
+Trix/M
+Trixy/M
+Trobriand/M
+trochaic/S
+trochee/SM
+trod/AU
+trodden/UA
+trodes
+troff/MR
+troglodyte/MS
+troika/SM
+Trojan/MS
+troll/DMSG
+trolled/F
+trolleybus/S
+trolley/SGMD
+trolling/F
+trollish
+Trollope/M
+trollop/GSMD
+trolly's
+trombone/MS
+trombonist/SM
+tromp/DSG
+Trondheim/M
+trooper/M
+troopship/SM
+troop/SRDMZG
+trope/SM
+Tropez/M
+trophic
+trophy/MGDS
+tropical/SY
+tropic/MS
+tropism/SM
+tropocollagen
+troposphere/MS
+tropospheric
+troth/GDM
+troths
+trot/S
+Trotsky/M
+trotted
+trotter/SM
+trotting
+troubadour/SM
+troubled/U
+trouble/GDRSM
+troublemaker/MS
+troubler/M
+troubleshooter/M
+troubleshoot/SRDZG
+troubleshot
+troublesomeness/M
+troublesome/YP
+trough/M
+troughs
+trounce/GZDRS
+trouncer/M
+troupe/MZGSRD
+trouper/M
+trouser/DMGS
+trousseau/M
+trousseaux
+Troutman/M
+trout/SM
+trove/SM
+troweler/M
+trowel/SMDRGZ
+trow/SGD
+Troyes
+Troy/M
+troy/S
+Trstram/M
+truancy/MS
+truant/SMDG
+truce/SDGM
+Truckee/M
+trucker/M
+trucking/M
+truckle/GDS
+truckload/MS
+truck/SZGMRDJ
+truculence/SM
+truculent/Y
+Truda/M
+Trudeau/M
+Trude/M
+Trudey/M
+trudge/SRDG
+Trudie/M
+Trudi/M
+Trudy/M
+true/DRSPTG
+truelove/MS
+Trueman/M
+trueness/M
+truer/U
+truest/U
+truffle/MS
+truism/SM
+Trujillo/M
+Trula/M
+truly/U
+Trumaine/M
+Truman/M
+Trumann/M
+Trumbull/M
+trump/DMSG
+trumpery/SM
+trumpeter/M
+trumpet/MDRZGS
+Trump/M
+truncate/NGDSX
+truncation/M
+truncheon/MDSG
+trundle/GZDSR
+trundler/M
+trunk/GSMD
+trunnion/SM
+trusser/M
+trussing/M
+truss/SRDG
+trusted/EU
+trusteeing
+trustee/MDS
+trusteeship/SM
+truster/M
+trustful/EY
+trustfulness/SM
+trustiness/M
+trusting/Y
+trust/RDMSG
+trusts/E
+trustworthier
+trustworthiest
+trustworthiness/MS
+trustworthy/UP
+trusty/PTMSR
+Truth
+truthfulness/US
+truthful/UYP
+truths/U
+truth/UM
+TRW
+trying/Y
+try/JGDRSZ
+tryout/MS
+trypsin/M
+tryst/GDMS
+ts
+T's
+tsarevich
+tsarina's
+tsarism/M
+tsarist
+tsetse/S
+Tsimshian/M
+Tsiolkovsky/M
+Tsitsihar/M
+tsp
+tsunami/MS
+Tsunematsu/M
+Tswana/M
+TTL
+tty/M
+ttys
+Tuamotu/M
+Tuareg/M
+tubae
+tubal
+tuba/SM
+tubbed
+tubbing
+tubby/TR
+tubeless
+tubercle/MS
+tubercular/S
+tuberculin/MS
+tuberculoses
+tuberculosis/M
+tuberculous
+tuber/M
+tuberose/SM
+tuberous
+tube/SM
+tubing/M
+tub/JMDRSZG
+Tubman/M
+tubular/Y
+tubule/SM
+tucker/GDM
+Tucker/M
+tuck/GZSRD
+Tuckie/M
+Tuck/RM
+Tucky/M
+Tucson/M
+Tucuman/M
+Tudor/MS
+Tue/S
+Tuesday/SM
+tufter/M
+tuft/GZSMRD
+tufting/M
+tugboat/MS
+tugged
+tugging
+tug/S
+tuition/ISM
+Tulane/M
+tularemia/S
+tulip/SM
+tulle/SM
+Tulley/M
+Tull/M
+Tully/M
+Tulsa/M
+tum
+tumbledown
+tumbler/M
+tumbleweed/MS
+tumble/ZGRSDJ
+tumbrel/SM
+tumescence/S
+tumescent
+tumidity/MS
+tumid/Y
+tummy/SM
+tumor/MDS
+tumorous
+Tums/M
+tumult/SGMD
+tumultuousness/M
+tumultuous/PY
+tumulus/M
+tunableness/M
+tunable/P
+tuna/SM
+tundra/SM
+tun/DRJZGBS
+tune/CSDG
+tunefulness/MS
+tuneful/YP
+tuneless/Y
+tuner/M
+tune's
+tuneup/S
+tung
+tungstate/M
+tungsten/SM
+Tunguska/M
+Tungus/M
+tunic/MS
+tuning/A
+tuning's
+Tunisia/M
+Tunisian/S
+Tunis/M
+tunned
+tunneler/M
+tunnel/MRDSJGZ
+tunning
+tunny/SM
+tupelo/M
+Tupi/M
+tuple/SM
+tuppence/M
+Tupperware
+Tupungato/M
+turban/SDM
+turbid
+turbidity/SM
+turbinate/SD
+turbine/SM
+turbocharged
+turbocharger/SM
+turbofan/MS
+turbojet/MS
+turboprop/MS
+turbo/SM
+turbot/MS
+turbulence/SM
+turbulent/Y
+turd/MS
+tureen/MS
+turf/DGSM
+turfy/RT
+Turgenev/M
+turgidity/SM
+turgidness/M
+turgid/PY
+Turing/M
+Turin/M
+Turkestan/M
+Turkey/M
+turkey/SM
+Turkic/SM
+Turkish
+Turkmenistan/M
+turk/S
+Turk/SM
+turmeric/MS
+turmoil/SDMG
+turnabout/SM
+turnaround/MS
+turn/AZGRDBS
+turnbuckle/SM
+turncoat/SM
+turned/U
+turner/M
+Turner/M
+turning/MS
+turnip/SMDG
+turnkey/MS
+turnoff/MS
+turnout/MS
+turnover/SM
+turnpike/MS
+turnround/MS
+turnstile/SM
+turnstone/M
+turntable/SM
+turpentine/GMSD
+Turpin/M
+turpitude/SM
+turquoise/SM
+turret/SMD
+turtleback/MS
+turtledove/MS
+turtleneck/SDM
+turtle/SDMG
+turves's
+turvy
+Tuscaloosa/M
+Tuscan
+Tuscany/M
+Tuscarora/M
+Tuscon/M
+tush/SDG
+Tuskegee/M
+tusker/M
+tusk/GZRDMS
+tussle/GSD
+tussock/MS
+tussocky
+Tussuad/M
+Tutankhamen/M
+tutelage/MS
+tutelary/S
+Tut/M
+tutored/U
+tutorial/MS
+tutor/MDGS
+tutorship/S
+tut/S
+Tutsi
+tutted
+tutting
+tutti/S
+Tuttle/M
+tutu/SM
+Tuvalu
+tuxedo/SDM
+tux/S
+TVA
+TV/M
+TVs
+twaddle/GZMRSD
+twaddler/M
+Twain/M
+twain/S
+TWA/M
+twang/MDSG
+twangy/TR
+twas
+tweak/SGRD
+tweediness/M
+Tweedledee/M
+Tweedledum/M
+Tweed/M
+twee/DP
+tweed/SM
+tweedy/PTR
+tween
+tweeter/M
+tweet/ZSGRD
+tweezer/M
+tweeze/ZGRD
+twelfth
+twelfths
+twelvemonth/M
+twelvemonths
+twelve/MS
+twentieths
+twenty/MSH
+twerp/MS
+twice/R
+twiddle/GRSD
+twiddler/M
+twiddly/RT
+twigged
+twigging
+twiggy/RT
+twig/SM
+Twila/M
+twilight/MS
+twilit
+twill/SGD
+twiner/M
+twine/SM
+twinge/SDMG
+Twinkie
+twinkler/M
+twinkle/RSDG
+twinkling/M
+twinkly
+twinned
+twinning
+twin/RDMGZS
+twirler/M
+twirling/Y
+twirl/SZGRD
+twirly/TR
+twisted/U
+twister/M
+twists/U
+twist/SZGRD
+twisty
+twitch/GRSD
+twitchy/TR
+twit/S
+twitted
+twitterer/M
+twitter/SGRD
+twittery
+twitting
+twixt
+twofer/MS
+twofold/S
+two/MS
+twopence/SM
+twopenny/S
+twosome/MS
+twp
+Twp
+TWX
+Twyla/M
+TX
+t/XTJBG
+Tybalt/M
+Tybie/M
+Tybi/M
+tycoon/MS
+tyeing
+Tye/M
+tying/UA
+tyke/SM
+Tylenol/M
+Tyler/M
+Ty/M
+Tymon/M
+Tymothy/M
+tympani
+tympanist/SM
+tympanum/SM
+Tynan/M
+Tyndale/M
+Tyndall/M
+Tyne/M
+typeahead
+typecast/SG
+typed/AU
+typedef/S
+typeface/MS
+typeless
+type/MGDRSJ
+types/A
+typescript/SM
+typeset/S
+typesetter/MS
+typesetting/SM
+typewriter/M
+typewrite/SRJZG
+typewriting/M
+typewritten
+typewrote
+typhoid/SM
+Typhon/M
+typhoon/SM
+typhus/SM
+typicality/MS
+typically
+typicalness/M
+typical/U
+typification/M
+typify/SDNXG
+typing/A
+typist/MS
+typographer/SM
+typographic
+typographical/Y
+typography/MS
+typological/Y
+typology/MS
+typo/MS
+tyrannic
+tyrannicalness/M
+tyrannical/PY
+tyrannicide/M
+tyrannizer/M
+tyrannize/ZGJRSD
+tyrannizing/YM
+tyrannosaur/MS
+tyrannosaurus/S
+tyrannous
+tyranny/MS
+tyrant/MS
+Tyree/M
+tyreo
+Tyrolean/S
+Tyrol's
+Tyrone/M
+tyrosine/M
+tyro/SM
+Tyrus/M
+Tyson/M
+tzarina's
+tzar's
+Tzeltal/M
+u
+U
+UAR
+UART
+UAW
+Ubangi/M
+ubiquitous/YP
+ubiquity/S
+Ucayali/M
+Uccello/M
+UCLA/M
+Udale/M
+Udall/M
+udder/SM
+Udell/M
+Ufa/M
+ufologist/S
+ufology/MS
+UFO/S
+Uganda/M
+Ugandan/S
+ugh
+ughs
+uglification
+ugliness/MS
+uglis
+ugly/PTGSRD
+Ugo/M
+uh
+UHF
+Uighur
+Ujungpandang/M
+UK
+ukase/SM
+Ukraine/M
+Ukrainian/S
+ukulele/SM
+UL
+Ula/M
+Ulberto/M
+ulcerate/NGVXDS
+ulceration/M
+ulcer/MDGS
+ulcerous
+Ulick/M
+Ulises/M
+Ulla/M
+Ullman/M
+ulnae
+ulna/M
+ulnar
+Ulrica/M
+Ulrich/M
+Ulrick/M
+Ulric/M
+Ulrika/M
+Ulrikaumeko/M
+Ulrike/M
+Ulster/M
+ulster/MS
+ult
+ulterior/Y
+ultimas
+ultimate/DSYPG
+ultimateness/M
+ultimatum/MS
+ultimo
+ultracentrifugally
+ultracentrifugation
+ultracentrifuge/M
+ultraconservative/S
+ultrafast
+ultrahigh
+ultralight/S
+ultramarine/SM
+ultramodern
+ultramontane
+ultra/S
+ultrashort
+ultrasonically
+ultrasonic/S
+ultrasonics/M
+ultrasound/SM
+ultrastructure/M
+Ultrasuede
+ultraviolet/SM
+Ultrix/M
+ULTRIX/M
+ululate/DSXGN
+ululation/M
+Ulyanovsk/M
+Ulysses/M
+um
+umbel/MS
+umber/GMDS
+Umberto/M
+umbilical/S
+umbilici
+umbilicus/M
+umbrage/MGSD
+umbrageous
+umbra/MS
+umbrella/GDMS
+Umbriel/M
+Umeko/M
+umiak/MS
+umlaut/GMDS
+umpire/MGSD
+ump/MDSG
+umpteen/H
+UN
+unabated/Y
+unabridged/S
+unacceptability
+unacceptable
+unaccepted
+unaccommodating
+unaccountability
+unaccustomed/Y
+unadapted
+unadulterated/Y
+unadventurous
+unalienability
+unalterableness/M
+unalterable/P
+unalterably
+Una/M
+unambiguity
+unambiguous
+unambitious
+unamused
+unanimity/SM
+unanimous/Y
+unanticipated/Y
+unapologetic
+unapologizing/M
+unappeasable
+unappeasably
+unappreciative
+unary
+unassailableness/M
+unassailable/P
+unassertive
+unassumingness/M
+unassuming/PY
+unauthorized/PY
+unavailing/PY
+unaware/SPY
+unbalanced/P
+unbar
+unbarring
+unbecoming/P
+unbeknown
+unbelieving/Y
+unbiased/P
+unbid
+unbind/G
+unblessed
+unblinking/Y
+unbodied
+unbolt/G
+unbreakability
+unbred
+unbroken
+unbuckle
+unbudging/Y
+unburnt
+uncap
+uncapping
+uncatalogued
+uncauterized/MS
+unceasing/Y
+uncelebrated
+uncertain/P
+unchallengeable
+unchangingness/M
+unchanging/PY
+uncharacteristic
+uncharismatic
+unchastity
+unchristian
+uncial/S
+uncivilized/Y
+unclassified
+uncle/MSD
+unclouded/Y
+uncodable
+uncollected
+uncoloredness/M
+uncolored/PY
+uncombable
+uncommunicative
+uncompetitive
+uncomplicated
+uncomprehending/Y
+uncompromisable
+unconcerned/P
+unconcern/M
+unconfirmed
+unconfused
+unconscionableness/M
+unconscionable/P
+unconscionably
+unconstitutional
+unconsumed
+uncontentious
+uncontrollability
+unconvertible
+uncool
+uncooperative
+uncork/G
+uncouple/G
+uncouthness/M
+uncouth/YP
+uncreate/V
+uncritical
+uncross/GB
+uncrowded
+unction/IM
+unctions
+unctuousness/MS
+unctuous/PY
+uncustomary
+uncut
+undated/I
+undaunted/Y
+undeceive
+undecided/S
+undedicated
+undefinability
+undefinedness/M
+undefined/P
+undelete
+undeliverability
+undeniableness/M
+undeniable/P
+undeniably
+undependable
+underachiever/M
+underachieve/SRDGZ
+underact/GDS
+underadjusting
+underage/S
+underarm/DGS
+underbedding
+underbelly/MS
+underbidding
+underbid/S
+underbracing
+underbrush/MSDG
+undercarriage/MS
+undercharge/GSD
+underclassman
+underclassmen
+underclass/S
+underclothes
+underclothing/MS
+undercoating/M
+undercoat/JMDGS
+underconsumption/M
+undercooked
+undercount/S
+undercover
+undercurrent/SM
+undercut/S
+undercutting
+underdeveloped
+underdevelopment/MS
+underdog/MS
+underdone
+undereducated
+underemphasis
+underemployed
+underemployment/SM
+underenumerated
+underenumeration
+underestimate/NGXSD
+underexploited
+underexpose/SDG
+underexposure/SM
+underfed
+underfeed/SG
+underfloor
+underflow/GDMS
+underfoot
+underfund/DG
+underfur/MS
+undergarment/SM
+undergirding
+undergoes
+undergo/G
+undergone
+undergrad/MS
+undergraduate/MS
+underground/RMS
+undergrowth/M
+undergrowths
+underhand/D
+underhandedness/MS
+underhanded/YP
+underheat
+underinvestment
+underlaid
+underlain/S
+underlay/GS
+underlie
+underline/GSDJ
+underling/MS
+underlip/SM
+underloaded
+underly/GS
+undermanned
+undermentioned
+undermine/SDG
+undermost
+underneath
+underneaths
+undernourished
+undernourishment/SM
+underpaid
+underpants
+underpart/MS
+underpass/SM
+underpay/GSL
+underpayment/SM
+underperformed
+underpinned
+underpinning/MS
+underpin/S
+underplay/SGD
+underpopulated
+underpopulation/M
+underpowered
+underpricing
+underprivileged
+underproduction/MS
+underrate/GSD
+underregistration/M
+underreported
+underreporting
+underrepresentation/M
+underrepresented
+underscore/SDG
+undersealed
+undersea/S
+undersecretary/SM
+undersell/SG
+undersexed
+undershirt/SM
+undershoot/SG
+undershorts
+undershot
+underside/SM
+undersigned/M
+undersign/SGD
+undersized
+undersizes
+undersizing
+underskirt/MS
+undersold
+underspecification
+underspecified
+underspend/G
+understaffed
+understandability/M
+understandably
+understanding/YM
+understand/RGSJB
+understate/GSDL
+understatement/MS
+understocked
+understood
+understrength
+understructure/SM
+understudy/GMSD
+undertaken
+undertaker/M
+undertake/SRGZJ
+undertaking/M
+underthings
+undertone/SM
+undertook
+undertow/MS
+underused
+underusing
+underutilization/M
+underutilized
+undervaluation/S
+undervalue/SDG
+underwater/S
+underway
+underwear/M
+underweight/S
+underwent
+underwhelm/DGS
+underwood/M
+Underwood/M
+underworld/MS
+underwrite/GZSR
+underwriter/M
+underwritten
+underwrote
+under/Y
+undeserving
+undesigned
+undeviating/Y
+undialyzed/SM
+undiplomatic
+undiscerning
+undiscriminating
+undo/GJ
+undoubted/Y
+undramatic
+undramatized/SM
+undress/G
+undrinkability
+undrinkable
+undroppable
+undue
+undulant
+undulate/XDSNG
+undulation/M
+unearthliness/S
+unearthly/P
+unearth/YG
+unease
+uneconomic
+uneducated
+unemployed/S
+unencroachable
+unending/Y
+unendurable/P
+unenergized/MS
+unenforced
+unenterprising
+UNESCO
+unethical
+uneulogized/SM
+unexacting
+unexceptionably
+unexcited
+unexpectedness/MS
+unfading/Y
+unfailingness/M
+unfailing/P
+unfamiliar
+unfashionable
+unfathomably
+unfavored
+unfeeling
+unfeigned/Y
+unfelt
+unfeminine
+unfertile
+unfetchable
+unflagging
+unflappability/S
+unflappable
+unflappably
+unflinching/Y
+unfold/LG
+unfoldment/M
+unforced
+unforgeable
+unfossilized/MS
+unfraternizing/SM
+unfrozen
+unfulfillable
+unfunny
+unfussy
+ungainliness/MS
+ungainly/PRT
+Ungava/M
+ungenerous
+ungentle
+unglamorous
+ungrammaticality
+ungrudging
+unguent/MS
+ungulate/MS
+unharmonious
+unharness/G
+unhistorical
+unholy/TP
+unhook/DG
+unhydrolyzed/SM
+unhygienic
+Unibus/M
+unicameral
+UNICEF
+unicellular
+Unicode/M
+unicorn/SM
+unicycle/MGSD
+unicyclist/MS
+unideal
+unidimensional
+unidiomatic
+unidirectionality
+unidirectional/Y
+unidolized/MS
+unifiable
+unification/MA
+unifier/MS
+unifilar
+uniformity/MS
+uniformness/M
+uniform/TGSRDYMP
+unify/AXDSNG
+unilateralism/M
+unilateralist
+unilateral/Y
+unimodal
+unimpeachably
+unimportance
+unimportant
+unimpressive
+unindustrialized/MS
+uninhibited/YP
+uninominal
+uninsured
+unintellectual
+unintended
+uninteresting
+uninterruptedness/M
+uninterrupted/YP
+unintuitive
+uninviting
+union/AEMS
+unionism/SM
+unionist/SM
+Unionist/SM
+unionize
+Union/MS
+UniPlus/M
+unipolar
+uniprocessor/SM
+uniqueness/S
+unique/TYSRP
+Uniroyal/M
+unisex/S
+UniSoft/M
+unison/MS
+Unisys/M
+unitarianism/M
+Unitarianism/SM
+unitarian/MS
+Unitarian/MS
+unitary
+unite/AEDSG
+united/Y
+uniter/M
+unitize/GDS
+unit/VGRD
+unity/SEM
+univ
+Univac/M
+univalent/S
+univalve/MS
+univariate
+universalism/M
+universalistic
+universality/SM
+universalize/DSRZG
+universalizer/M
+universal/YSP
+universe/MS
+university/MS
+Unix/M
+UNIX/M
+unjam
+unkempt
+unkind/TP
+unkink
+unknightly
+unknowable/S
+unknowing
+unlabored
+unlace/G
+unlearn/G
+unlikeable
+unlikeliness/S
+unlimber/G
+unlimited
+unlit
+unliterary
+unloose/G
+unlucky/TP
+unmagnetized/MS
+unmanageably
+unmannered/Y
+unmask/G
+unmeaning
+unmeasured
+unmeetable
+unmelodious
+unmemorable
+unmemorialized/MS
+unmentionable/S
+unmerciful
+unmeritorious
+unmethodical
+unmineralized/MS
+unmissable
+unmistakably
+unmitigated/YP
+unmnemonic
+unmobilized/SM
+unmoral
+unmount/B
+unmovable
+unmoving
+unnaturalness/M
+unnavigable
+unnerving/Y
+unobliging
+unoffensive
+unofficial
+unorganized/YP
+unorthodox
+unpack/G
+unpaintable
+unpalatability
+unpalatable
+unpartizan
+unpatronizing
+unpeople
+unperceptive
+unperson
+unperturbed/Y
+unphysical
+unpick/G
+unpicturesque
+unpinning
+unpleasing
+unploughed
+unpolarized/SM
+unpopular
+unpractical
+unprecedented/Y
+unpredictable/S
+unpreemphasized
+unpremeditated
+unpretentiousness/M
+unprincipled/P
+unproblematic
+unproductive
+unpropitious
+unprovable
+unproven
+unprovocative
+unpunctual
+unquestionable
+unraisable
+unravellings
+unreadability
+unread/B
+unreal
+unrealizable
+unreasoning/Y
+unreceptive
+unrecordable
+unreflective
+unrelenting/Y
+unremitting/Y
+unrepeatability
+unrepeated
+unrepentant
+unreported
+unrepresentative
+unreproducible
+unrest/G
+unrestrained/P
+unrewarding
+unriddle
+unripe/P
+unromantic
+unruliness/SM
+unruly/PTR
+unsaleable
+unsanitary
+unsavored/YP
+unsavoriness/M
+unseal/GB
+unsearchable
+unseasonal
+unseeing/Y
+unseen/S
+unselfconsciousness/M
+unselfconscious/P
+unselfishness/M
+unsellable
+unsentimental
+unset
+unsettledness/M
+unsettled/P
+unsettling/Y
+unshapely
+unshaven
+unshorn
+unsighted
+unsightliness/S
+unskilful
+unsociability
+unsociable/P
+unsocial
+unsound/PT
+unspeakably
+unspecific
+unspectacular
+unspoilt
+unspoke
+unsporting
+unstable/P
+unstigmatized/SM
+unstilted
+unstinting/Y
+unstopping
+unstrapping
+unstudied
+unstuffy
+unsubdued
+unsubstantial
+unsubtle
+unsuitable
+unsuspecting/Y
+unswerving/Y
+unsymmetrical
+unsympathetic
+unsystematic
+unsystematized/Y
+untactful
+untalented
+untaxing
+unteach/B
+untellable
+untenable
+unthinking
+until/G
+untiring/Y
+unto
+untouchable/MS
+untowardness/M
+untoward/P
+untraceable
+untrue
+untruthfulness/M
+untwist/G
+Unukalhai/M
+unusualness/M
+unutterable
+unutterably
+unvocalized/MS
+unvulcanized/SM
+unwaivering
+unwarrantable
+unwarrantably
+unwashed/PS
+unwearable
+unwearied/Y
+unwed
+unwedge
+unwelcome
+unwell/M
+unwieldiness/MS
+unwieldy/TPR
+unwind/B
+unwomanly
+unworkable/S
+unworried
+unwrap
+unwrapping
+unyielding/Y
+unyoke
+unzip
+up
+Upanishads
+uparrow
+upbeat/SM
+upbraid/GDRS
+upbringing/M
+upbring/JG
+UPC
+upchuck/SDG
+upcome/G
+upcountry/S
+updatability
+updater/M
+update/RSDG
+Updike/M
+updraft/SM
+upend/SDG
+upfield
+upfront
+upgradeable
+upgrade/DSJG
+upheaval/MS
+upheld
+uphill/S
+upholder/M
+uphold/RSGZ
+upholster/ADGS
+upholsterer/SM
+upholstery/MS
+UPI
+upkeep/SM
+uplander/M
+upland/MRS
+uplifter/M
+uplift/SJDRG
+upload/GSD
+upmarket
+upon
+upped
+uppercase/GSD
+upperclassman/M
+upperclassmen
+uppercut/S
+uppercutting
+uppermost
+upper/S
+upping
+uppish
+uppity
+upraise/GDS
+uprated
+uprating
+uprear/DSG
+upright/DYGSP
+uprightness/S
+uprise/RGJ
+uprising/M
+upriver/S
+uproariousness/M
+uproarious/PY
+uproar/MS
+uproot/DRGS
+uprooter/M
+ups
+UPS
+upscale/GDS
+upset/S
+upsetting/MS
+upshot/SM
+upside/MS
+upsilon/MS
+upslope
+upstage/DSRG
+upstairs
+upstandingness/M
+upstanding/P
+upstart/MDGS
+upstate/SR
+upstream/DSG
+upstroke/MS
+upsurge/DSG
+upswing/GMS
+upswung
+uptake/SM
+upthrust/GMS
+uptight
+uptime
+Upton/M
+uptown/RS
+uptrend/M
+upturn/GDS
+upwardness/M
+upward/SYP
+upwelling
+upwind/S
+uracil/MS
+Ural/MS
+Urania/M
+uranium/MS
+Uranus/M
+uranyl/M
+Urbain/M
+Urbana/M
+urbane/Y
+urbanism/M
+urbanite/SM
+urbanity/SM
+urbanization/MS
+urbanize/DSG
+Urban/M
+urbanologist/S
+urbanology/S
+Urbano/M
+urban/RT
+Urbanus/M
+urchin/SM
+Urdu/M
+urea/SM
+uremia/MS
+uremic
+ureter/MS
+urethane/MS
+urethrae
+urethral
+urethra/M
+urethritis/M
+Urey/M
+urge/GDRSJ
+urgency/SM
+urgent/Y
+urger/M
+Uriah/M
+uric
+Uriel/M
+urinal/MS
+urinalyses
+urinalysis/M
+urinary/MS
+urinate/XDSNG
+urination/M
+urine/MS
+Uri/SM
+URL
+Ur/M
+urning/M
+urn/MDGS
+urogenital
+urological
+urologist/S
+urology/MS
+Urquhart/M
+Ursala/M
+Ursa/M
+ursine
+Ursola/M
+Urson/M
+Ursula/M
+Ursulina/M
+Ursuline/M
+urticaria/MS
+Uruguayan/S
+Uruguay/M
+Urumqi
+US
+USA
+usability/S
+usable/U
+usably/U
+USAF
+usage/SM
+USART
+USCG
+USC/M
+USDA
+us/DRSBZG
+used/U
+use/ESDAG
+usefulness/SM
+useful/YP
+uselessness/MS
+useless/PY
+Usenet/M
+Usenix/M
+user/M
+USG/M
+usherette/SM
+usher/SGMD
+USIA
+USMC
+USN
+USO
+USP
+USPS
+USS
+USSR
+Ustinov/M
+usu
+usuals
+usual/UPY
+usurer/SM
+usuriousness/M
+usurious/PY
+usurpation/MS
+usurper/M
+usurp/RDZSG
+usury/SM
+UT
+Utahan/SM
+Utah/M
+Uta/M
+Ute/M
+utensil/SM
+uteri
+uterine
+uterus/M
+Utica/M
+utile/I
+utilitarianism/MS
+utilitarian/S
+utility/MS
+utilization/MS
+utilization's/A
+utilize/GZDRS
+utilizer/M
+utilizes/A
+utmost/S
+Utopia/MS
+utopianism/M
+utopian's
+Utopian/S
+utopia/S
+Utrecht/M
+Utrillo/M
+utterance/MS
+uttered/U
+utterer/M
+uttermost/S
+utter/TRDYGS
+uucp/M
+UV
+uvula/MS
+uvular/S
+uxorious
+Uzbekistan
+Uzbek/M
+Uzi/M
+V
+VA
+vacancy/MS
+vacantness/M
+vacant/PY
+vacate/NGXSD
+vacationist/SM
+vacationland
+vacation/MRDZG
+vaccinate/NGSDX
+vaccination/M
+vaccine/SM
+vaccinial
+vaccinia/M
+Vachel/M
+vacillate/XNGSD
+vacillating/Y
+vacillation/M
+vacillator/SM
+Vaclav/M
+vacua's
+vacuity/MS
+vacuo
+vacuolated/U
+vacuolate/SDGN
+vacuole/SM
+vacuolization/SM
+vacuousness/MS
+vacuous/PY
+vacuum/GSMD
+Vader/M
+Vaduz/M
+vagabondage/MS
+vagabond/DMSG
+vagarious
+vagary/MS
+vaginae
+vaginal/Y
+vagina/M
+vagrancy/MS
+vagrant/SMY
+vagueing
+vagueness/MS
+vague/TYSRDP
+Vail/M
+vaingloriousness/M
+vainglorious/YP
+vainglory/MS
+vain/TYRP
+val
+valance/SDMG
+Valaree/M
+Valaria/M
+Valarie/M
+Valdemar/M
+Valdez/M
+Valeda/M
+valediction/MS
+valedictorian/MS
+valedictory/MS
+Vale/M
+valence/SM
+Valencia/MS
+valency/MS
+Valene/M
+Valenka/M
+Valentia/M
+Valentijn/M
+Valentina/M
+Valentine/M
+valentine/SM
+Valentin/M
+Valentino/M
+Valenzuela/M
+Valera/M
+Valeria/M
+Valerian/M
+Valerie/M
+Valerye/M
+Valéry/M
+vale/SM
+valet/GDMS
+valetudinarianism/MS
+valetudinarian/MS
+Valhalla/M
+valiance/S
+valiantness/M
+valiant/SPY
+Valida/M
+validated/AU
+validate/INGSDX
+validates/A
+validation/AMI
+validity/IMS
+validnesses
+validness/MI
+valid/PIY
+Valina/M
+valise/MS
+Valium/S
+Valkyrie/SM
+Vallejo
+Valle/M
+Valletta/M
+valley/SM
+Vallie/M
+Valli/M
+Vally/M
+Valma/M
+Val/MY
+Valois/M
+valor/MS
+valorous/Y
+Valparaiso/M
+Valry/M
+valuable/IP
+valuableness/IM
+valuables
+valuably/I
+valuate/NGXSD
+valuation/CSAM
+valuator/SM
+value/CGASD
+valued/U
+valuelessness/M
+valueless/P
+valuer/SM
+value's
+values/E
+valve/GMSD
+valveless
+valvular
+Va/M
+vamoose/GSD
+vamp/ADSG
+vamper
+vampire/MGSD
+vamp's
+vanadium/MS
+Vance/M
+Vancouver/M
+vandalism/MS
+vandalize/GSD
+vandal/MS
+Vandal/MS
+Vanda/M
+Vandenberg/M
+Vanderbilt/M
+Vanderburgh/M
+Vanderpoel/M
+Vandyke/SM
+vane/MS
+Vanessa/M
+Vang/M
+vanguard/MS
+Vania/M
+vanilla/MS
+vanisher/M
+vanish/GRSDJ
+vanishing/Y
+vanity/SM
+Van/M
+Vanna/M
+vanned
+Vannie/M
+Vanni/M
+vanning
+Vanny/M
+vanquisher/M
+vanquish/RSDGZ
+van/SMD
+vantage/MS
+Vanuatu
+Vanya/M
+Vanzetti/M
+vapidity/MS
+vapidness/SM
+vapid/PY
+vaporer/M
+vaporing/MY
+vaporisation
+vaporise/DSG
+vaporization/AMS
+vaporize/DRSZG
+vaporizer/M
+vapor/MRDJGZS
+vaporous
+vapory
+vaquero/SM
+VAR
+Varanasi/M
+Varese/M
+Vargas/M
+variability/IMS
+variableness/IM
+variable/PMS
+variables/I
+variably/I
+variance/I
+variances
+variance's
+Varian/M
+variant/ISY
+variate/MGNSDX
+variational
+variation/M
+varicolored/MS
+varicose/S
+variedly
+varied/U
+variegate/NGXSD
+variegation/M
+varier/M
+varietal/S
+variety/MS
+various/PY
+varistor/M
+Varityping/M
+varlet/MS
+varmint/SM
+varnished/U
+varnisher/M
+varnish/ZGMDRS
+var/S
+varsity/MS
+varying/UY
+vary/SRDJG
+vascular
+vasectomy/SM
+Vaseline/DSMG
+vase/SM
+Vasili/MS
+Vasily/M
+vasomotor
+Vasquez/M
+vassalage/MS
+vassal/GSMD
+Vassar/M
+Vassili/M
+Vassily/M
+vastness/MS
+vast/PTSYR
+v/ASV
+VAT
+Vatican/M
+vat/SM
+vatted
+vatting
+vaudeville/SM
+vaudevillian/SM
+Vaudois
+Vaughan/M
+Vaughn/M
+vaulter/M
+vaulting/M
+vault/ZSRDMGJ
+vaunter/M
+vaunt/GRDS
+VAXes
+Vax/M
+VAX/M
+Vazquez/M
+vb
+VCR
+VD
+VDT
+VDU
+vealed/A
+vealer/MA
+veal/MRDGS
+veals/A
+Veblen/M
+vectorial
+vectorization
+vectorized
+vectorizing
+vector's/F
+vector/SGDM
+Veda/MS
+Vedanta/M
+veejay/S
+veep/S
+veer/DSG
+veering/Y
+vegan/SM
+Vega/SM
+Vegemite/M
+veges
+vegetable/MS
+vegetarianism/MS
+vegetarian/SM
+vegetate/DSNGVX
+vegetation/M
+vegetative/PY
+vegged
+veggie/S
+vegging
+veg/M
+vehemence/MS
+vehemency/S
+vehement/Y
+vehicle/SM
+vehicular
+veiling/MU
+veil's
+veil/UGSD
+vein/GSRDM
+veining/M
+vela/M
+Vela/M
+velarize/SDG
+velar/S
+Velásquez/M
+Velázquez
+Velcro/SM
+veld/SM
+veldt's
+Velez/M
+Vella/M
+vellum/MS
+Velma/M
+velocipede/SM
+velocity/SM
+velor/S
+velour's
+velum/M
+Velveeta/M
+velveteen/MS
+velvet/GSMD
+Velvet/M
+velvety/RT
+venality/MS
+venal/Y
+venation/SM
+vend/DSG
+vender's/K
+vendetta/MS
+vendible/S
+vendor/MS
+veneerer/M
+veneer/GSRDM
+veneering/M
+venerability/S
+venerable/P
+venerate/XNGSD
+veneration/M
+venereal
+venetian
+Venetian/SM
+Venezuela/M
+Venezuelan/S
+vengeance/MS
+vengeful/APY
+vengefulness/AM
+venialness/M
+venial/YP
+Venice/M
+venireman/M
+veniremen
+venison/SM
+Venita/M
+Venn/M
+venomousness/M
+venomous/YP
+venom/SGDM
+venous/Y
+venter/M
+ventilated/U
+ventilate/XSDVGN
+ventilation/M
+ventilator/MS
+vent/ISGFD
+ventral/YS
+ventricle/MS
+ventricular
+ventriloquies
+ventriloquism/MS
+ventriloquist/MS
+ventriloquy
+vent's/F
+Ventura/M
+venture/RSDJZG
+venturesomeness/SM
+venturesome/YP
+venturi/S
+venturousness/MS
+venturous/YP
+venue/MAS
+Venusian/S
+Venus/S
+veraciousness/M
+veracious/YP
+veracities
+veracity/IM
+Veracruz/M
+Veradis
+Vera/M
+verandahed
+veranda/SDM
+verbalization/MS
+verbalized/U
+verbalizer/M
+verbalize/ZGRSD
+verballed
+verballing
+verbal/SY
+verbatim
+verbena/MS
+verbiage/SM
+verb/KSM
+verbose/YP
+verbosity/SM
+verboten
+verdant/Y
+Verde/M
+Verderer/M
+verdict/SM
+verdigris/GSDM
+Verdi/M
+verdure/SDM
+Vere/M
+Verena/M
+Verene/M
+verge/FGSD
+Verge/M
+verger/SM
+verge's
+Vergil's
+veridical/Y
+Veriee/M
+verifiability/M
+verifiableness/M
+verifiable/U
+verification/S
+verified/U
+verifier/MS
+verify/GASD
+Verile/M
+verily
+Verina/M
+Verine/M
+verisimilitude/SM
+veritableness/M
+veritable/P
+veritably
+verity/MS
+Verlag/M
+Verlaine/M
+Verla/M
+Vermeer/M
+vermicelli/MS
+vermiculite/MS
+vermiform
+vermilion/MS
+vermin/M
+verminous
+Vermonter/M
+Vermont/ZRM
+vermouth/M
+vermouths
+vernacular/YS
+vernal/Y
+Verna/M
+Verne/M
+Vernen/M
+Verney/M
+Vernice/M
+vernier/SM
+Vern/NM
+Vernon/M
+Vernor/M
+Verona/M
+Veronese/M
+Veronica/M
+veronica/SM
+Veronika/M
+Veronike/M
+Veronique/M
+verrucae
+verruca/MS
+versa
+Versailles/M
+Versatec/M
+versatileness/M
+versatile/YP
+versatility/SM
+versed/UI
+verse's
+verses/I
+verse/XSRDAGNF
+versicle/M
+versification/M
+versifier/M
+versify/GDRSZXN
+versing/I
+version/MFISA
+verso/SM
+versus
+vertebrae
+vertebral/Y
+vertebra/M
+vertebrate/IMS
+vertebration/M
+vertex/SM
+vertical/YPS
+vertices's
+vertiginous
+vertigoes
+vertigo/M
+verve/SM
+very/RT
+Vesalius/M
+vesicle/SM
+vesicular/Y
+vesiculate/GSD
+Vespasian/M
+vesper/SM
+Vespucci/M
+vessel/MS
+vestal/YS
+Vesta/M
+vest/DIGSL
+vestibular
+vestibule/SDM
+vestige/SM
+vestigial/Y
+vesting/SM
+vestment/ISM
+vestryman/M
+vestrymen
+vestry/MS
+vest's
+vesture/SDMG
+Vesuvius/M
+vetch/SM
+veteran/SM
+veterinarian/MS
+veterinary/S
+veter/M
+veto/DMG
+vetoes
+vet/SMR
+vetted
+vetting/A
+Vevay/M
+vexation/SM
+vexatiousness/M
+vexatious/PY
+vexed/Y
+vex/GFSD
+VF
+VFW
+VG
+VGA
+vhf
+VHF
+VHS
+VI
+via
+viability/SM
+viable/I
+viably
+viaduct/MS
+Viagra/M
+vial/MDGS
+viand/SM
+vibe/S
+vibraharp/MS
+vibrancy/MS
+vibrant/YS
+vibraphone/MS
+vibraphonist/SM
+vibrate/XNGSD
+vibrational/Y
+vibration/M
+vibrato/MS
+vibrator/SM
+vibratory
+vibrio/M
+vibrionic
+viburnum/SM
+vicarage/SM
+vicariousness/MS
+vicarious/YP
+vicar/SM
+vice/CMS
+viced
+vicegerent/MS
+vicennial
+Vicente/M
+viceregal
+viceroy/SM
+Vichy/M
+vichyssoise/MS
+vicing
+vicinity/MS
+viciousness/S
+vicious/YP
+vicissitude/MS
+Vickers/M
+Vickie/M
+Vicki/M
+Vicksburg/M
+Vicky/M
+Vick/ZM
+Vic/M
+victimization/SM
+victimized/U
+victimizer/M
+victimize/SRDZG
+victim/SM
+Victoir/M
+Victoria/M
+Victorianism/S
+Victorian/S
+victoriousness/M
+victorious/YP
+Victor/M
+victor/SM
+victory/MS
+Victrola/SM
+victualer/M
+victual/ZGSDR
+vicuña/S
+Vidal/M
+Vida/M
+videlicet
+videocassette/S
+videoconferencing
+videodisc/S
+videodisk/SM
+video/GSMD
+videophone/SM
+videotape/SDGM
+Vidovic/M
+Vidovik/M
+Vienna/M
+Viennese/M
+Vientiane/M
+vier/M
+vie/S
+Vietcong/M
+Viet/M
+Vietminh/M
+Vietnamese/M
+Vietnam/M
+viewed/A
+viewer/AS
+viewer's
+viewfinder/MS
+viewgraph/SM
+viewing/M
+viewless/Y
+view/MBGZJSRD
+viewpoint/SM
+views/A
+vigesimal
+vigilance/MS
+vigilante/SM
+vigilantism/MS
+vigilantist
+vigilant/Y
+vigil/SM
+vignette/MGDRS
+vignetter/M
+vignetting/M
+vignettist/MS
+vigor/MS
+vigorousness/M
+vigorous/YP
+vii
+viii
+Vijayawada/M
+Viki/M
+Viking/MS
+viking/S
+Vikki/M
+Vikky/M
+Vikram/M
+Vila
+vile/AR
+vilely
+vileness/MS
+vilest
+Vilhelmina/M
+vilification/M
+vilifier/M
+vilify/GNXRSD
+villager/M
+village/RSMZ
+villainousness/M
+villainous/YP
+villain/SM
+villainy/MS
+Villa/M
+villa/MS
+Villarreal/M
+ville
+villeinage/SM
+villein/MS
+villi
+Villon/M
+villus/M
+Vilma/M
+Vilnius/M
+Vilyui/M
+Vi/M
+vi/MDR
+vim/MS
+vinaigrette/MS
+Vina/M
+Vince/M
+Vincent/MS
+Vincenty/M
+Vincenz/M
+vincible/I
+Vinci/M
+Vindemiatrix/M
+vindicate/XSDVGN
+vindication/M
+vindicator/SM
+vindictiveness/MS
+vindictive/PY
+vinegar/DMSG
+vinegary
+vine/MGDS
+vineyard/SM
+Vinita/M
+Vin/M
+Vinnie/M
+Vinni/M
+Vinny/M
+vino/MS
+vinous
+Vinson/M
+vintage/MRSDG
+vintager/M
+vintner/MS
+vinyl/SM
+violable/I
+Viola/M
+Violante/M
+viola/SM
+violate/VNGXSD
+violator/MS
+Viole/M
+violence/SM
+violent/Y
+Violet/M
+violet/SM
+Violetta/M
+Violette/M
+violinist/SM
+violin/MS
+violist/MS
+viol/MSB
+violoncellist/S
+violoncello/MS
+viper/MS
+viperous
+VIP/S
+viragoes
+virago/M
+viral/Y
+vireo/SM
+Virge/M
+Virgie/M
+Virgilio/M
+Virgil/M
+virginal/YS
+Virgina/M
+Virginia/M
+Virginian/S
+Virginie/M
+virginity/SM
+virgin/SM
+Virgo/MS
+virgule/MS
+virile
+virility/MS
+virologist/S
+virology/SM
+virtual/Y
+virtue/SM
+virtuosity/MS
+virtuosoes
+virtuoso/MS
+virtuousness/SM
+virtuous/PY
+virulence/SM
+virulent/Y
+virus/MS
+visage/MSD
+Visakhapatnam's
+Visa/M
+visa/SGMD
+Visayans
+viscera
+visceral/Y
+viscid/Y
+viscoelastic
+viscoelasticity
+viscometer/SM
+viscose/MS
+viscosity/MS
+viscountcy/MS
+viscountess/SM
+viscount/MS
+viscousness/M
+viscous/PY
+viscus/M
+vise/CAXNGSD
+viselike
+vise's
+Vishnu/M
+visibility/ISM
+visible/PI
+visibly/I
+Visigoth/M
+Visigoths
+visionariness/M
+visionary/PS
+vision/KMDGS
+vision's/A
+visitable/U
+visitant/SM
+visitation/SM
+visited/U
+visit/GASD
+visitor/MS
+vis/MDSGV
+visor/SMDG
+VISTA
+vista/GSDM
+Vistula/M
+visualization/AMS
+visualized/U
+visualizer/M
+visualizes/A
+visualize/SRDZG
+visual/SY
+vitae
+vitality/MS
+vitalization/AMS
+vitalize/ASDGC
+vital/SY
+vita/M
+Vita/M
+vitamin/SM
+Vite/M
+Vitia/M
+vitiate/XGNSD
+vitiation/M
+viticulture/SM
+viticulturist/S
+Vitim/M
+Vito/M
+Vitoria/M
+vitreous/YSP
+vitrifaction/S
+vitrification/M
+vitrify/XDSNG
+vitrine/SM
+vitriolic
+vitriol/MDSG
+vitro
+vittles
+Vittoria/M
+Vittorio/M
+vituperate/SDXVGN
+vituperation/M
+vituperative/Y
+Vitus/M
+vivace/S
+vivaciousness/MS
+vivacious/YP
+vivacity/SM
+viva/DGS
+Vivaldi
+Viva/M
+vivaria
+vivarium/MS
+vivaxes
+Vivekananda/M
+vive/Z
+Vivia/M
+Viviana/M
+Vivian/M
+Vivianna/M
+Vivianne/M
+vividness/SM
+vivid/PTYR
+Vivie/M
+Viviene/M
+Vivien/M
+Vivienne/M
+vivifier
+vivify/NGASD
+Vivi/MN
+viviparous
+vivisect/DGS
+vivisectional
+vivisectionist/SM
+vivisection/MS
+Viviyan/M
+Viv/M
+vivo
+Vivyan/M
+Vivyanne/M
+vixenish/Y
+vixen/SM
+viz
+vizier/MS
+vizor's
+VJ
+Vladamir/M
+Vladimir/M
+Vladivostok/M
+Vlad/M
+VLF
+VLSI
+VMS/M
+VOA
+vocable/SM
+vocab/S
+vocabularian
+vocabularianism
+vocabulary/MS
+vocalic/S
+vocalise's
+vocalism/M
+vocalist/MS
+vocalization/SM
+vocalized/U
+vocalizer/M
+vocalize/ZGDRS
+vocal/SY
+vocation/AKMISF
+vocational/Y
+vocative/KYS
+vociferate/NGXSD
+vociferation/M
+vociferousness/MS
+vociferous/YP
+vocoded
+vocoder
+vodka/MS
+voe/S
+Vogel/M
+vogue/GMSRD
+vogueing
+voguish
+voiceband
+voiced/CU
+voice/IMGDS
+voicelessness/SM
+voiceless/YP
+voicer/S
+voices/C
+voicing/C
+voidable
+void/C
+voided
+voider/M
+voiding
+voidness/M
+voids
+voilà
+voile/MS
+volar
+volatileness/M
+volatile/PS
+volatility/MS
+volatilization/MS
+volatilize/SDG
+volcanically
+volcanic/S
+volcanism/M
+volcanoes
+volcano/M
+vole/MS
+Volga/M
+Volgograd/M
+vol/GSD
+volitionality
+volitional/Y
+volition/MS
+Volkswagen/SM
+volleyball/MS
+volleyer/M
+volley/SMRDG
+Vol/M
+Volstead/M
+voltage/SM
+voltaic
+Voltaire/M
+Volta/M
+volt/AMS
+Volterra/M
+voltmeter/MS
+volubility/S
+voluble/P
+volubly
+volume/SDGM
+volumetric
+volumetrically
+voluminousness/MS
+voluminous/PY
+voluntarily/I
+voluntariness/MI
+voluntarism/MS
+voluntary/PS
+volunteer/DMSG
+voluptuary/SM
+voluptuousness/S
+voluptuous/YP
+volute/S
+Volvo/M
+vomit/GRDS
+Vonda/M
+Von/M
+Vonnegut/M
+Vonnie/M
+Vonni/M
+Vonny/M
+voodoo/GDMS
+voodooism/S
+voraciousness/MS
+voracious/YP
+voracity/MS
+Voronezh/M
+Vorster/M
+vortex/SM
+vortices's
+vorticity/M
+votary/MS
+vote/CSDG
+voter/SM
+vote's
+votive/YP
+voucher/GMD
+vouchsafe/SDG
+vouch/SRDGZ
+vowelled
+vowelling
+vowel/MS
+vower/M
+vow/SMDRG
+voyage/GMZJSRD
+voyager/M
+voyageur/SM
+voyeurism/MS
+voyeuristic
+voyeur/MS
+VP
+vs
+V's
+VT
+Vt/M
+VTOL
+vulcanization/SM
+vulcanized/U
+vulcanize/SDG
+Vulcan/M
+vulgarian/MS
+vulgarism/MS
+vulgarity/MS
+vulgarization/S
+vulgarize/GZSRD
+vulgar/TSYR
+Vulgate/SM
+Vulg/M
+vulnerability/SI
+vulnerable/IP
+vulnerably/I
+vulpine
+vulturelike
+vulture/SM
+vulturous
+vulvae
+vulva/M
+vying
+Vyky/M
+WA
+Waals
+Wabash/M
+WAC
+Wacke/M
+wackes
+wackiness/MS
+wacko/MS
+wacky/RTP
+Waco/M
+Wac/S
+wadded
+wadding/SM
+waddle/GRSD
+Wade/M
+wader/M
+wade/S
+wadi/SM
+wad/MDRZGS
+Wadsworth/M
+wafer/GSMD
+waffle/GMZRSD
+Wafs
+wafter/M
+waft/SGRD
+wag/DRZGS
+waged/U
+wager/GZMRD
+wage/SM
+wagged
+waggery/MS
+wagging
+waggishness/SM
+waggish/YP
+waggle/SDG
+waggly
+Wagnerian
+Wagner/M
+wagoner/M
+wagon/SGZMRD
+wagtail/SM
+Wahl/M
+waif/SGDM
+Waikiki/M
+wailer/M
+wail/SGZRD
+wain/GSDM
+Wain/M
+wainscot/SGJD
+Wainwright/M
+wainwright/SM
+waistband/MS
+waistcoat/GDMS
+waister/M
+waist/GSRDM
+waistline/MS
+Waite/M
+waiter/DMG
+Waiter/M
+wait/GSZJRD
+Wait/MR
+waitpeople
+waitperson/S
+waitress/GMSD
+waiver/MB
+waive/SRDGZ
+Wakefield/M
+wakefulness/MS
+wakeful/PY
+Wake/M
+wake/MGDRSJ
+waken/SMRDG
+waker/M
+wakeup
+Waksman/M
+Walbridge/M
+Walcott/M
+Waldemar/M
+Walden/M
+Waldensian
+Waldheim/M
+Wald/MN
+Waldo/M
+Waldon/M
+Waldorf/M
+wale/DRSMG
+Wales
+Walesa/M
+Walford/M
+Walgreen/M
+waling/M
+walkabout/M
+walkaway/SM
+walker/M
+Walker/M
+walk/GZSBJRD
+walkie
+Walkman/S
+walkout/SM
+walkover/SM
+walkway/MS
+wallaby/MS
+Wallace/M
+Wallache/M
+wallah/M
+Wallas/M
+wallboard/MS
+Wallenstein/M
+Waller/M
+wallet/SM
+walleye/MSD
+wallflower/MS
+Wallie/M
+Wallis
+Walliw/M
+Walloon/SM
+walloper/M
+walloping/M
+wallop/RDSJG
+wallower/M
+wallow/RDSG
+wallpaper/DMGS
+wall/SGMRD
+Wall/SMR
+Wally/M
+wally/S
+walnut/SM
+Walpole/M
+Walpurgisnacht
+walrus/SM
+Walsh/M
+Walter/M
+Walther/M
+Walton/M
+waltzer/M
+Walt/ZMR
+waltz/MRSDGZ
+Walworth/M
+Waly/M
+wampum/SM
+Wanamaker/M
+Wanda/M
+wanderer/M
+wander/JZGRD
+wanderlust/SM
+Wandie/M
+Wandis/M
+wand/MRSZ
+wane/S
+Waneta/M
+wangler/M
+wangle/RSDGZ
+Wang/M
+Wanids/M
+Wankel/M
+wanna
+wannabe/S
+wanned
+wanner
+wanness/S
+wannest
+wanning
+wan/PGSDY
+Wansee/M
+Wansley/M
+wanted/U
+wanter/M
+want/GRDSJ
+wantonness/S
+wanton/PGSRDY
+wapiti/MS
+warble/GZRSD
+warbler/M
+warbonnet/S
+ward/AGMRDS
+Warde/M
+warden/DMGS
+Warden/M
+warder/DMGS
+Ward/MN
+wardrobe/MDSG
+wardroom/MS
+wardship/M
+wards/I
+warehouseman/M
+warehouse/MGSRD
+Ware/MG
+ware/MS
+warfare/SM
+Warfield/M
+war/GSMD
+warhead/MS
+Warhol/M
+warhorse/SM
+warily/U
+warinesses/U
+wariness/MS
+Waring/M
+warless
+warlike
+warlock/SM
+warlord/MS
+warmblooded
+warmed/A
+warmer/M
+warmheartedness/SM
+warmhearted/PY
+warmish
+warmness/MS
+warmongering/M
+warmonger/JGSM
+warms/A
+warmth/M
+warmths
+warm/YRDHPGZTS
+warned/U
+warner/M
+Warner/M
+warn/GRDJS
+warning/YM
+Warnock/M
+warpaint
+warpath/M
+warpaths
+warper/M
+warplane/MS
+warp/MRDGS
+warranted/U
+warranter/M
+warrant/GSMDR
+warranty/SDGM
+warred/M
+warrener/M
+Warren/M
+warren/SZRM
+warring/M
+warrior/MS
+Warsaw/M
+wars/C
+warship/MS
+warthog/S
+wartime/SM
+wart/MDS
+warty/RT
+Warwick/M
+wary/URPT
+Wasatch/M
+washable/S
+wash/AGSD
+washbasin/SM
+washboard/SM
+washbowl/SM
+Washburn/M
+washcloth/M
+washcloths
+washday/M
+washed/U
+washer/GDMS
+washerwoman/M
+washerwomen
+washing/SM
+Washingtonian/S
+Washington/M
+Wash/M
+Washoe/M
+washout/SM
+washrag/SM
+washroom/MS
+washstand/SM
+washtub/MS
+washy/RT
+wasn't
+WASP
+waspishness/SM
+waspish/PY
+Wasp's
+wasp/SM
+was/S
+wassail/GMDS
+Wasserman/M
+Wassermann/M
+wastage/SM
+wastebasket/SM
+wastefulness/S
+wasteful/YP
+wasteland/MS
+wastepaper/MS
+waster/DG
+waste/S
+wastewater
+wast/GZSRD
+wasting/Y
+wastrel/MS
+Watanabe/M
+watchable/U
+watchband/SM
+watchdogged
+watchdogging
+watchdog/SM
+watched/U
+watcher/M
+watchfulness/MS
+watchful/PY
+watch/JRSDGZB
+watchmake/JRGZ
+watchmaker/M
+watchman/M
+watchmen
+watchpoints
+watchtower/MS
+watchword/MS
+waterbird/S
+waterborne
+Waterbury/M
+watercolor/DMGS
+watercolorist/SM
+watercourse/SM
+watercraft/M
+watercress/SM
+waterer/M
+waterfall/SM
+waterfowl/M
+waterfront/SM
+Watergate/M
+waterhole/S
+Waterhouse/M
+wateriness/SM
+watering/M
+water/JGSMRD
+waterless
+waterlily/S
+waterline/S
+waterlogged
+waterloo
+Waterloo/SM
+waterman/M
+watermark/GSDM
+watermelon/SM
+watermill/S
+waterproof/PGRDSJ
+watershed/SM
+waterside/MSR
+watersider/M
+Waters/M
+waterspout/MS
+watertightness/M
+watertight/P
+Watertown/M
+waterway/MS
+waterwheel/S
+waterworks/M
+watery/PRT
+Watkins
+WATS
+Watson/M
+wattage/SM
+Watteau/M
+Wattenberg/M
+Watterson/M
+wattle/SDGM
+Watt/MS
+watt/TMRS
+Watusi/M
+Wat/ZM
+Waugh/M
+Waukesha/M
+Waunona/M
+Waupaca/M
+Waupun/M
+Wausau/M
+Wauwatosa/M
+waveband/MS
+waveform/SM
+wavefront/MS
+waveguide/MS
+Waveland/M
+wavelength/M
+wavelengths
+wavelet/SM
+wavelike
+wavenumber
+waver/GZRD
+wavering/YU
+Waverley/M
+Waverly/M
+Wave/S
+wave/ZGDRS
+wavily
+waviness/MS
+wavy/SRTP
+waxer/M
+waxiness/MS
+wax/MNDRSZG
+waxwing/MS
+waxwork/MS
+waxy/PRT
+wayfarer/MS
+wayfaring/S
+waylaid
+Wayland/M
+Waylan/M
+waylayer/M
+waylay/GRSZ
+wayleave/MS
+Waylen/M
+Waylin/M
+Waylon/M
+Way/M
+waymarked
+way/MS
+Wayne/M
+Waynesboro/M
+wayside/MS
+waywardness/S
+wayward/YP
+WC
+we
+weakener/M
+weaken/ZGRD
+weakfish/SM
+weakish
+weakliness/M
+weakling/SM
+weakly/RTP
+weakness/MS
+weak/TXPYRN
+weal/MHS
+wealthiness/MS
+wealth/M
+wealths
+wealthy/PTR
+weaner/M
+weanling/M
+wean/RDGS
+weapon/GDMS
+weaponless
+weaponry/MS
+wearable/S
+wearer/M
+wearied/U
+wearily
+weariness/MS
+wearing/Y
+wearisomeness/M
+wearisome/YP
+wear/RBSJGZ
+wearying/Y
+weary/TGPRSD
+weasel/SGMDY
+weatherbeaten
+weathercock/SDMG
+weatherer/M
+Weatherford/M
+weathering/M
+weatherize/GSD
+weatherman/M
+weather/MDRYJGS
+weathermen
+weatherperson/S
+weatherproof/SGPD
+weatherstripped
+weatherstripping/S
+weatherstrip/S
+weaver/M
+Weaver/M
+weaves/A
+weave/SRDGZ
+weaving/A
+webbed
+Webber/M
+webbing/MS
+Webb/RM
+weber/M
+Weber/M
+Webern/M
+webfeet
+webfoot/M
+Web/MR
+website/S
+web/SMR
+Webster/MS
+Websterville/M
+we'd
+wedded/A
+Weddell/M
+wedder
+wedding/SM
+wedge/SDGM
+wedgie/RST
+Wedgwood/M
+wedlock/SM
+Wed/M
+Wednesday/SM
+wed/SA
+weeder/M
+weediness/M
+weedkiller/M
+weedless
+wee/DRST
+weed/SGMRDZ
+weedy/TRP
+weeing
+weekday/MS
+weekender/M
+weekend/SDRMG
+weekly/S
+weeknight/SM
+Weeks/M
+week/SYM
+weenie/M
+ween/SGD
+weeny/RSMT
+weeper/M
+weep/SGZJRD
+weepy/RST
+weevil/MS
+weft/SGMD
+Wehr/M
+Weibull/M
+Weidar/M
+Weider/M
+Weidman/M
+Weierstrass/M
+weighed/UA
+weigher/M
+weigh/RDJG
+weighs/A
+weighted/U
+weighter/M
+weightily
+weightiness/SM
+weighting/M
+weight/JMSRDG
+weightlessness/SM
+weightless/YP
+weightlifter/S
+weightlifting/MS
+weighty/TPR
+Weill/M
+Wei/M
+Weinberg/M
+Weiner/M
+Weinstein/M
+weirdie/SM
+weirdness/MS
+weirdo/SM
+weird/YRDPGTS
+weir/SDMG
+Weisenheimer/M
+Weiss/M
+Weissman/M
+Weissmuller/M
+Weizmann/M
+Welbie/M
+Welby/M
+Welcher/M
+Welches
+welcomeness/M
+welcome/PRSDYG
+welcoming/U
+welder/M
+Weldon/M
+weld/SBJGZRD
+Weldwood/M
+welfare/SM
+welkin/SM
+we'll
+Welland/M
+wellbeing/M
+Weller/M
+Wellesley/M
+Welles/M
+wellhead/SM
+Wellington/MS
+wellington/S
+Wellman/M
+wellness/MS
+well/SGPD
+Wells/M
+wellspring/SM
+Wellsville/M
+Welmers/M
+Welsh
+welsher/M
+Welshman/M
+Welshmen
+welsh/RSDGZ
+Welshwoman/M
+Welshwomen
+welter/GD
+welterweight/MS
+welt/GZSMRD
+wencher/M
+wench/GRSDM
+Wendall/M
+Wenda/M
+wend/DSG
+Wendeline/M
+Wendell/M
+Wendel/M
+Wendie/M
+Wendi/M
+Wendye/M
+Wendy/M
+wen/M
+Wenonah/M
+Wenona/M
+went
+Wentworth/M
+wept/U
+were
+we're
+weren't
+werewolf/M
+werewolves
+Werner/M
+Wernher/M
+Werther/M
+werwolf's
+Wes
+Wesleyan
+Wesley/M
+Wessex/M
+Wesson/M
+westbound
+Westbrooke/M
+Westbrook/M
+Westchester/M
+wester/DYG
+westerly/S
+westerner/M
+westernization/MS
+westernize/GSD
+westernmost
+Western/ZRS
+western/ZSR
+Westfield/M
+Westhampton/M
+Westinghouse/M
+westing/M
+Westleigh/M
+Westley/M
+Westminster/M
+Westmore/M
+West/MS
+Weston/M
+Westphalia/M
+Westport/M
+west/RDGSM
+westward/S
+Westwood/M
+wetback/MS
+wetland/S
+wetness/MS
+wet/SPY
+wettable
+wetter/S
+wettest
+wetting
+we've
+Weyden/M
+Weyerhauser/M
+Weylin/M
+Wezen/M
+WFF
+whacker/M
+whack/GZRDS
+whaleboat/MS
+whalebone/SM
+whale/GSRDZM
+Whalen/M
+whaler/M
+whaling/M
+whammed
+whamming/M
+wham/MS
+whammy/S
+wharf/SGMD
+Wharton/M
+wharves
+whatchamacallit/MS
+what'd
+whatever
+what/MS
+whatnot/MS
+what're
+whatsoever
+wheal/MS
+wheatgerm
+Wheaties/M
+Wheatland/M
+wheat/NMXS
+Wheaton/M
+Wheatstone/M
+wheedle/ZDRSG
+wheelbarrow/GSDM
+wheelbase/MS
+wheelchair/MS
+wheeler/M
+Wheeler/M
+wheelhouse/SM
+wheelie/MS
+wheeling/M
+Wheeling/M
+Wheelock/M
+wheel/RDMJSGZ
+wheelwright/MS
+whee/S
+wheeze/SDG
+wheezily
+wheeziness/SM
+wheezy/PRT
+Whelan/M
+whelk/MDS
+Wheller/M
+whelm/DGS
+whelp/DMGS
+whence/S
+whenever
+when/S
+whensoever
+whereabout/S
+whereas/S
+whereat
+whereby
+where'd
+wherefore/MS
+wherein
+where/MS
+whereof
+whereon
+where're
+wheresoever
+whereto
+whereupon
+wherever
+wherewith
+wherewithal/SM
+wherry/DSGM
+whether
+whet/S
+whetstone/MS
+whetted
+whetting
+whew/GSD
+whey/MS
+which
+whichever
+whiff/GSMD
+whiffle/DRSG
+whiffler/M
+whiffletree/SM
+whig/S
+Whig/SM
+while/GSD
+whilom
+whilst
+whimmed
+whimming
+whimper/DSG
+whimsey's
+whimsicality/MS
+whimsical/YP
+whim/SM
+whimsy/TMDRS
+whine/GZMSRD
+whining/Y
+whinny/GTDRS
+whiny/RT
+whipcord/SM
+whiplash/SDMG
+Whippany/M
+whipped
+whipper/MS
+whippersnapper/MS
+whippet/MS
+whipping/SM
+Whipple/M
+whippletree/SM
+whippoorwill/SM
+whipsaw/GDMS
+whips/M
+whip/SM
+whirligig/MS
+whirlpool/MS
+whirl/RDGS
+whirlwind/MS
+whirlybird/MS
+whirly/MS
+whirred
+whirring
+whir/SY
+whisker/DM
+whiskery
+whiskey/SM
+whisk/GZRDS
+whisperer/M
+whisper/GRDJZS
+whispering/YM
+whist/GDMS
+whistleable
+whistle/DRSZG
+whistler/M
+Whistler/M
+whistling/M
+Whitaker/M
+Whitby/M
+Whitcomb/M
+whitebait/M
+whitecap/MS
+whiteface/M
+Whitefield/M
+whitefish/SM
+Whitehall/M
+Whitehead/M
+whitehead/S
+Whitehorse/M
+Whiteleaf/M
+Whiteley/M
+White/MS
+whitener/M
+whiteness/MS
+whitening/M
+whiten/JZDRG
+whiteout/S
+white/PYS
+whitespace
+whitetail/S
+whitewall/SM
+whitewash/GRSDM
+whitewater
+Whitewater/M
+whitey/MS
+Whitfield/M
+whither/DGS
+whitier
+whitiest
+whiting/M
+whitish
+Whitley/M
+Whitlock/M
+Whit/M
+Whitman/M
+Whitney/M
+whit/SJGTXMRND
+Whitsunday/MS
+Whittaker/M
+whitter
+Whittier
+whittle/JDRSZG
+whittler/M
+whiz
+whizkid
+whizzbang/S
+whizzed
+whizzes
+whizzing
+WHO
+whoa/S
+who'd
+whodunit/SM
+whoever
+wholegrain
+wholeheartedness/MS
+wholehearted/PY
+wholemeal
+wholeness/S
+wholesale/GZMSRD
+wholesaler/M
+wholesomeness/USM
+wholesome/UYP
+whole/SP
+wholewheat
+who'll
+wholly
+whom
+who/M
+whomever
+whomsoever
+whoopee/S
+whooper/M
+whoop/SRDGZ
+whoosh/DSGM
+whop
+whopper/MS
+whopping/S
+who're
+whorehouse/SM
+whoreish
+whore/SDGM
+whorish
+whorl/SDM
+whose
+whoso
+whosoever
+who've
+why
+whys
+WI
+Wiatt/M
+Wichita/M
+wickedness/MS
+wicked/RYPT
+wicker/M
+wickerwork/MS
+wicketkeeper/SM
+wicket/SM
+wick/GZRDMS
+wicking/M
+widemouthed
+widener/M
+wideness/S
+widen/SGZRD
+wide/RSYTP
+widespread
+widgeon's
+widget/SM
+widower/M
+widowhood/S
+widow/MRDSGZ
+width/M
+widths
+widthwise
+Wieland/M
+wielder/M
+wield/GZRDS
+Wiemar/M
+wiener/SM
+wienie/SM
+Wier/M
+Wiesel/M
+wife/DSMYG
+wifeless
+wifely/RPT
+wigeon/MS
+wigged
+wigging/M
+Wiggins
+wiggler/M
+wiggle/RSDGZ
+wiggly/RT
+wight/SGDM
+wiglet/S
+wigmaker
+wig/MS
+Wigner/M
+wigwagged
+wigwagging
+wigwag/S
+wigwam/MS
+Wilberforce/M
+Wilbert/M
+Wilbur/M
+Wilburn/M
+Wilburt/M
+Wilcox/M
+Wilda/M
+wildcat/SM
+wildcatted
+wildcatter/MS
+wildcatting
+wildebeest/SM
+Wilde/MR
+Wilden/M
+Wilder/M
+wilderness/SM
+wilder/P
+wildfire/MS
+wildflower/S
+wildfowl/M
+wilding/M
+wildlife/M
+wildness/MS
+Wildon/M
+wild/SPGTYRD
+wile/DSMG
+Wileen/M
+Wilek/M
+Wiley/M
+Wilford/M
+Wilfred/M
+Wilfredo/M
+Wilfrid/M
+wilfulness's
+Wilhelmina/M
+Wilhelmine/M
+Wilhelm/M
+Wilie/M
+wilily
+wiliness/MS
+Wilkerson/M
+Wilkes/M
+Wilkins/M
+Wilkinson/M
+Willabella/M
+Willa/M
+Willamette/M
+Willamina/M
+Willard/M
+Willcox/M
+Willdon/M
+willed/U
+Willem/M
+Willemstad/M
+willer/M
+Willetta/M
+Willette/M
+Willey/M
+willfulness/S
+willful/YP
+Williamsburg/M
+William/SM
+Williamson/M
+Willied/M
+Willie/M
+willies
+Willi/MS
+willinger
+willingest
+willingness's
+willingness/US
+willing/UYP
+Willisson/M
+williwaw/MS
+Will/M
+Willoughby/M
+willower/M
+Willow/M
+willow/RDMSG
+willowy/TR
+willpower/MS
+will/SGJRD
+Willy/SDM
+Willyt/M
+Wilma/M
+Wilmar/M
+Wilmer/M
+Wilmette/M
+Wilmington/M
+Wilona/M
+Wilone/M
+Wilow/M
+Wilshire/M
+Wilsonian
+Wilson/M
+wilt/DGS
+Wilt/M
+Wilton/M
+wily/PTR
+Wimbledon/M
+wimp/GSMD
+wimpish
+wimple/SDGM
+wimpy/RT
+wince/SDG
+Winchell/M
+wincher/M
+winchester/M
+Winchester/MS
+winch/GRSDM
+windbag/SM
+windblown
+windbreak/MZSR
+windburn/GSMD
+winded
+winder/UM
+windfall/SM
+windflower/MS
+Windham/M
+Windhoek/M
+windily
+windiness/SM
+winding/MS
+windjammer/SM
+windlass/GMSD
+windless/YP
+windmill/GDMS
+window/DMGS
+windowless
+windowpane/SM
+Windows
+windowsill/SM
+windpipe/SM
+windproof
+windrow/GDMS
+wind's
+winds/A
+windscreen/MS
+windshield/SM
+windsock/MS
+Windsor/MS
+windstorm/MS
+windsurf/GZJSRD
+windswept
+windup/MS
+wind/USRZG
+Windward/M
+windward/SY
+Windy/M
+windy/TPR
+wineglass/SM
+winegrower/SM
+Winehead/M
+winemake
+winemaster
+wine/MS
+winery/MS
+Winesap/M
+wineskin/M
+Winfield/M
+Winfred/M
+Winfrey/M
+wingback/M
+wingding/MS
+wingeing
+winger/M
+wing/GZRDM
+wingless
+winglike
+wingman
+wingmen
+wingspan/SM
+wingspread/MS
+wingtip/S
+Winifield/M
+Winifred/M
+Wini/M
+winker/M
+wink/GZRDS
+winking/U
+Winkle/M
+winkle/SDGM
+winless
+Win/M
+winnable
+Winnah/M
+Winna/M
+Winnebago/M
+Winne/M
+winner/MS
+Winnetka/M
+Winnie/M
+Winnifred/M
+Winni/M
+winning/SY
+Winnipeg/M
+Winn/M
+winnow/SZGRD
+Winny/M
+Winograd/M
+wino/MS
+Winonah/M
+Winona/M
+Winooski/M
+Winsborough/M
+Winsett/M
+Winslow/M
+winsomeness/SM
+winsome/PRTY
+Winston/M
+winterer/M
+wintergreen/SM
+winterize/GSD
+Winters
+winter/SGRDYM
+wintertime/MS
+Winthrop/M
+wintriness/M
+wintry/TPR
+winy/RT
+win/ZGDRS
+wipe/DRSZG
+wiper/M
+wirehair/MS
+wireless/MSDG
+wireman/M
+wiremen
+wirer/M
+wire's
+wires/A
+wiretap/MS
+wiretapped
+wiretapper/SM
+wiretapping
+wire/UDA
+wiriness/S
+wiring/SM
+wiry/RTP
+Wisc
+Wisconsinite/SM
+Wisconsin/M
+wisdoms
+wisdom/UM
+wiseacre/MS
+wisecrack/GMRDS
+wised
+wisely/TR
+Wise/M
+wiseness
+wisenheimer/M
+Wisenheimer/M
+wises
+wise/URTY
+wishbone/MS
+wishfulness/M
+wishful/PY
+wish/GZSRD
+wishy
+wising
+Wis/M
+wisp/MDGS
+wispy/RT
+wist/DGS
+wisteria/SM
+wistfulness/MS
+wistful/PY
+witchcraft/SM
+witchdoctor/S
+witchery/MS
+witch/SDMG
+withal
+withdrawal/MS
+withdrawer/M
+withdrawnness/M
+withdrawn/P
+withdraw/RGS
+withdrew
+withe/M
+wither/GDJ
+withering/Y
+Witherspoon/M
+with/GSRDZ
+withheld
+withholder/M
+withhold/SJGZR
+within/S
+without/S
+withs
+withstand/SG
+withstood
+witlessness/MS
+witless/PY
+Wit/M
+witness/DSMG
+witnessed/U
+wit/PSM
+witted
+witter/G
+Wittgenstein/M
+witticism/MS
+Wittie/M
+wittily
+wittiness/SM
+wittings
+witting/UY
+Witt/M
+Witty/M
+witty/RTP
+Witwatersrand/M
+wive/GDS
+wives/M
+wizard/MYS
+wizardry/MS
+wizen/D
+wiz's
+wk/Y
+Wm/M
+WNW
+woad/MS
+wobble/GSRD
+wobbler/M
+wobbliness/S
+wobbly/PRST
+Wodehouse/M
+woebegone/P
+woefuller
+woefullest
+woefulness/SM
+woeful/PY
+woe/PSM
+woke
+wok/SMN
+Wolcott/M
+wold/MS
+Wolfe/M
+wolfer/M
+Wolff/M
+Wolfgang/M
+wolfhound/MS
+Wolfie/M
+wolfishness/M
+wolfish/YP
+Wolf/M
+wolfram/MS
+wolf/RDMGS
+Wolfy/M
+Wollongong/M
+Wollstonecraft/M
+Wolsey/M
+Wolverhampton/M
+wolverine/SM
+Wolverton/M
+wolves/M
+woman/GSMYD
+womanhood/MS
+womanish
+womanized/U
+womanizer/M
+womanize/RSDZG
+womanizes/U
+womankind/M
+womanlike
+womanliness/SM
+womanly/PRT
+wombat/MS
+womb/SDM
+womenfolk/MS
+women/MS
+wonderer/M
+wonderfulness/SM
+wonderful/PY
+wonder/GLRDMS
+wondering/Y
+wonderland/SM
+wonderment/SM
+wondrousness/M
+wondrous/YP
+Wong/M
+wonk/S
+wonky/RT
+wonned
+wonning
+won/SG
+won't
+wontedness/MU
+wonted/PUY
+wont/SGMD
+Woodard/M
+Woodberry/M
+woodbine/SM
+woodblock/S
+Woodbury/M
+woodcarver/S
+woodcarving/MS
+woodchopper/SM
+woodchuck/MS
+woodcock/MS
+woodcraft/MS
+woodcut/SM
+woodcutter/MS
+woodcutting/MS
+woodenness/SM
+wooden/TPRY
+woodgrain/G
+woodhen
+Woodhull/M
+Woodie/M
+woodiness/MS
+woodland/SRM
+Woodlawn/M
+woodlice
+woodlot/S
+woodlouse/M
+woodman/M
+Woodman/M
+woodmen
+woodpecker/SM
+woodpile/SM
+Woodrow/M
+woodruff/M
+woo/DRZGS
+woodshedded
+woodshedding
+woodshed/SM
+woodside
+Wood/SM
+woodsman/M
+woodsmen
+wood/SMNDG
+woodsmoke
+woods/R
+Woodstock/M
+woodsy/TRP
+Woodward/MS
+woodwind/S
+woodworker/M
+woodworking/M
+woodwork/SMRGZJ
+woodworm/M
+woodyard
+Woody/M
+woody/TPSR
+woofer/M
+woof/SRDMGZ
+Woolf/M
+woolgatherer/M
+woolgathering/M
+woolgather/RGJ
+woolliness/MS
+woolly/RSPT
+Woolongong/M
+wool/SMYNDX
+Woolworth/M
+Woonsocket/M
+Wooster/M
+Wooten/M
+woozily
+wooziness/MS
+woozy/RTP
+wop/MS
+Worcestershire/M
+Worcester/SM
+wordage/SM
+word/AGSJD
+wordbook/MS
+Worden/M
+wordily
+wordiness/SM
+wording/AM
+wordless/Y
+wordplay/SM
+word's
+Wordsworth/M
+wordy/TPR
+wore
+workability's
+workability/U
+workableness/M
+workable/U
+workably
+workaday
+workaholic/S
+workaround/SM
+workbench/MS
+workbook/SM
+workday/SM
+worked/A
+worker/M
+workfare/S
+workforce/S
+work/GZJSRDMB
+workhorse/MS
+workhouse/SM
+working/M
+workingman/M
+workingmen
+workingwoman/M
+workingwomen
+workload/SM
+workmanlike
+Workman/M
+workman/MY
+workmanship/MS
+workmate/S
+workmen/M
+workout/SM
+workpiece/SM
+workplace/SM
+workroom/MS
+works/A
+worksheet/S
+workshop/MS
+workspace/S
+workstation/MS
+worktable/SM
+worktop/S
+workup/S
+workweek/SM
+worldlier
+worldliest
+worldliness/USM
+worldly/UP
+worldwide
+world/ZSYM
+wormer/M
+wormhole/SM
+worm/SGMRD
+Worms/M
+wormwood/SM
+wormy/RT
+worn/U
+worried/Y
+worrier/M
+worriment/MS
+worrisome/YP
+worrying/Y
+worrywart/SM
+worry/ZGSRD
+worsen/GSD
+worse/SR
+worshiper/M
+worshipfulness/M
+worshipful/YP
+worship/ZDRGS
+worsted/MS
+worst/SGD
+worth/DG
+worthily/U
+worthinesses/U
+worthiness/SM
+Worthington/M
+worthlessness/SM
+worthless/PY
+Worth/M
+worths
+worthwhile/P
+Worthy/M
+worthy/UTSRP
+wort/SM
+wost
+wot
+Wotan/M
+wouldn't
+would/S
+wouldst
+would've
+wound/AU
+wounded/U
+wounder
+wounding
+wounds
+wound's
+wove/A
+woven/AU
+wovens
+wow/SDG
+Wozniak/M
+WP
+wpm
+wrack/SGMD
+wraith/M
+wraiths
+Wrangell/M
+wrangle/GZDRS
+wrangler/M
+wraparound/S
+wrap/MS
+wrapped/U
+wrapper/MS
+wrapping/SM
+wraps/U
+wrasse/SM
+wrathful/YP
+wrath/GDM
+wraths
+wreak/SDG
+wreathe
+wreath/GMDS
+wreaths
+wreckage/MS
+wrecker/M
+wreck/GZRDS
+wrenching/Y
+wrench/MDSG
+wren/MS
+Wren/MS
+Wrennie/M
+wrester/M
+wrestle/JGZDRS
+wrestler/M
+wrestling/M
+wrest/SRDG
+wretchedness/SM
+wretched/TPYR
+wretch/MDS
+wriggle/DRSGZ
+wriggler/M
+wriggly/RT
+Wright/M
+wright/MS
+Wrigley/M
+wringer/M
+wring/GZRS
+wrinkled/U
+wrinkle/GMDS
+wrinkly/RST
+wristband/SM
+wrist/MS
+wristwatch/MS
+writable/U
+write/ASBRJG
+writer/MA
+writeup
+writhe/SDG
+writing/M
+writ/MRSBJGZ
+written/UA
+Wroclaw
+wrongdoer/MS
+wrongdoing/MS
+wronger/M
+wrongfulness/MS
+wrongful/PY
+wrongheadedness/MS
+wrongheaded/PY
+wrongness/MS
+wrong/PSGTYRD
+Wronskian/M
+wrote/A
+wroth
+wrought/I
+wrung
+wry/DSGY
+wryer
+wryest
+wryness/SM
+W's
+WSW
+wt
+W/T
+Wuhan/M
+Wu/M
+Wurlitzer/M
+wurst/SM
+wuss/S
+wussy/TRS
+WV
+WW
+WWI
+WWII
+WWW
+w/XTJGV
+WY
+Wyatan/M
+Wyatt/M
+Wycherley/M
+Wycliffe/M
+Wye/MH
+Wyeth/M
+Wylie/M
+Wylma/M
+Wyman/M
+Wyndham/M
+Wyn/M
+Wynne/M
+Wynnie/M
+Wynn/M
+Wynny/M
+Wyo/M
+Wyomingite/SM
+Wyoming/M
+WYSIWYG
+x
+X
+Xanadu
+Xanthippe/M
+Xanthus/M
+Xaviera/M
+Xavier/M
+Xebec/M
+Xe/M
+XEmacs/M
+Xenakis/M
+Xena/M
+Xenia/M
+Xenix/M
+xenon/SM
+xenophobe/MS
+xenophobia/SM
+xenophobic
+Xenophon/M
+Xenos
+xerographic
+xerography/MS
+xerox/GSD
+Xerox/MGSD
+Xerxes/M
+Xever/M
+Xhosa/M
+Xi'an
+Xian/S
+Xiaoping/M
+xii
+xiii
+xi/M
+Ximenes/M
+Ximenez/M
+Ximian/SM
+Xingu/M
+xis
+xiv
+xix
+XL
+Xmas/SM
+XML
+Xochipilli/M
+XOR
+X's
+XS
+xterm/M
+Xuzhou/M
+xv
+xvi
+xvii
+xviii
+xx
+XXL
+xylem/SM
+xylene/M
+Xylia/M
+Xylina/M
+xylophone/MS
+xylophonist/S
+Xymenes/M
+Y
+ya
+yacc/M
+Yacc/M
+yachting/M
+yachtsman
+yachtsmen
+yachtswoman/M
+yachtswomen
+yacht/ZGJSDM
+yack's
+Yagi/M
+yahoo/MS
+Yahweh/M
+Yakima/M
+yakked
+yakking
+yak/SM
+Yakut/M
+Yakutsk/M
+Yale/M
+Yalies/M
+y'all
+Yalonda/M
+Yalow/M
+Yalta/M
+Yalu/M
+Yamaha/M
+yammer/RDZGS
+Yamoussoukro
+yam/SM
+Yanaton/M
+Yance/M
+Yancey/M
+Yancy/M
+Yang/M
+Yangon
+yang/S
+Yangtze/M
+Yankee/SM
+yank/GDS
+Yank/MS
+Yaounde/M
+yapped
+yapping
+yap/S
+Yaqui/M
+yardage/SM
+yardarm/SM
+Yardley/M
+Yard/M
+yardman/M
+yardmaster/S
+yardmen
+yard/SMDG
+yardstick/SM
+yarmulke/SM
+yarn/SGDM
+Yaroslavl/M
+yarrow/MS
+Yasmeen/M
+Yasmin/M
+Yates
+yaw/DSG
+yawl/SGMD
+yawner/M
+yawn/GZSDR
+yawning/Y
+Yb/M
+yd
+Yeager/M
+yeah
+yeahs
+yearbook/SM
+yearling/M
+yearlong
+yearly/S
+yearner/M
+yearning/MY
+yearn/JSGRD
+year/YMS
+yea/S
+yeastiness/M
+yeast/SGDM
+yeasty/PTR
+Yeats/M
+yecch
+yegg/MS
+Yehudi/M
+Yehudit/M
+Yekaterinburg/M
+Yelena/M
+yell/GSDR
+yellowhammers
+yellowish
+Yellowknife/M
+yellowness/MS
+Yellowstone/M
+yellow/TGPSRDM
+yellowy
+yelper/M
+yelp/GSDR
+Yeltsin
+Yemeni/S
+Yemenite/SM
+Yemen/M
+Yenisei/M
+yenned
+yenning
+yen/SM
+Yentl/M
+yeomanry/MS
+yeoman/YM
+yeomen
+yep/S
+Yerevan/M
+Yerkes/M
+Yesenia/M
+yeshiva/SM
+yes/S
+yessed
+yessing
+yesterday/MS
+yesteryear/SM
+yet
+ye/T
+yeti/SM
+Yetta/M
+Yettie/M
+Yetty/M
+Yevette/M
+Yevtushenko/M
+yew/SM
+y/F
+Yggdrasil/M
+Yiddish/M
+yielded/U
+yielding/U
+yield/JGRDS
+yikes
+yin/S
+yipe/S
+yipped
+yippee/S
+yipping
+yip/S
+YMCA
+YMHA
+Ymir/M
+YMMV
+Ynes/M
+Ynez/M
+yo
+Yoda/M
+yodeler/M
+yodel/SZRDG
+Yoder/M
+yoga/MS
+yoghurt's
+yogi/MS
+yogurt/SM
+yoke/DSMG
+yoked/U
+yokel/SM
+yokes/U
+yoking/U
+Yoknapatawpha/M
+Yokohama/M
+Yoko/M
+Yolanda/M
+Yolande/M
+Yolane/M
+Yolanthe/M
+yolk/DMS
+yon
+yonder
+Yong/M
+Yonkers/M
+yore/MS
+Yorgo/MS
+Yorick/M
+Yorke/M
+Yorker/M
+yorker/SM
+Yorkshire/MS
+Yorktown/M
+York/ZRMS
+Yoruba/M
+Yosemite/M
+Yoshiko/M
+Yoshi/M
+Yost/M
+you'd
+you'll
+youngish
+Young/M
+youngster/MS
+Youngstown/M
+young/TRYP
+you're
+your/MS
+yourself
+yourselves
+you/SH
+youthfulness/SM
+youthful/YP
+youths
+youth/SM
+you've
+Yovonnda/M
+yow
+yowl/GSD
+Ypres/M
+Ypsilanti/M
+yr
+yrs
+Y's
+Ysabel/M
+YT
+ytterbium/MS
+yttrium/SM
+yuan/M
+Yuba/M
+Yucatan
+yucca/MS
+yuck/GSD
+yucky/RT
+Yugo/M
+Yugoslavia/M
+Yugoslavian/S
+Yugoslav/M
+Yuh/M
+Yuki/M
+yukked
+yukking
+Yukon/M
+yuk/S
+yule/MS
+Yule/MS
+yuletide/MS
+Yuletide/S
+Yul/M
+Yulma/M
+yum
+Yuma/M
+yummy/TRS
+Yunnan/M
+yuppie/SM
+yup/S
+Yurik/M
+Yuri/M
+yurt/SM
+Yves/M
+Yvette/M
+Yvon/M
+Yvonne/M
+Yvor/M
+YWCA
+YWHA
+Zabrina/M
+Zaccaria/M
+Zachariah/M
+Zacharia/SM
+Zacharie/M
+Zachary/M
+Zacherie/M
+Zachery/M
+Zach/M
+Zackariah/M
+Zack/M
+zagging
+Zagreb/M
+zag/S
+Zahara/M
+Zaire/M
+Zairian/S
+Zak/M
+Zambezi/M
+Zambia/M
+Zambian/S
+Zamboni
+Zamenhof/M
+Zamora/M
+Zandra/M
+Zane/M
+Zaneta/M
+zaniness/MS
+Zan/M
+Zanuck/M
+zany/PDSRTG
+Zanzibar/M
+Zapata/M
+Zaporozhye/M
+Zappa/M
+zapped
+zapper/S
+zapping
+zap/S
+Zarah/M
+Zara/M
+Zared/M
+Zaria/M
+Zarla/M
+Zealand/M
+zeal/MS
+zealot/MS
+zealotry/MS
+zealousness/SM
+zealous/YP
+Zea/M
+Zebadiah/M
+Zebedee/M
+Zeb/M
+zebra/MS
+Zebulen/M
+Zebulon/M
+zebu/SM
+Zechariah/M
+Zedekiah/M
+Zed/M
+Zedong/M
+zed/SM
+Zeffirelli/M
+Zeiss/M
+zeitgeist/S
+Zeke/M
+Zelda/M
+Zelig/M
+Zellerbach/M
+Zelma/M
+Zena/M
+Zenger/M
+Zenia/M
+zenith/M
+zeniths
+Zen/M
+Zennist/M
+Zeno/M
+Zephaniah/M
+zephyr/MS
+Zephyrus/M
+Zeppelin's
+zeppelin/SM
+Zerk/M
+zeroed/M
+zeroing/M
+zero/SDHMG
+zestfulness/MS
+zestful/YP
+zest/MDSG
+zesty/RT
+zeta/SM
+zeugma/M
+Zeus/M
+Zhdanov/M
+Zhengzhou
+Zhivago/M
+Zhukov/M
+Zia/M
+Zibo/M
+Ziegfeld/MS
+Ziegler/M
+zig
+zigged
+zigging
+Ziggy/M
+zigzagged
+zigzagger
+zigzagging
+zigzag/MS
+zilch/S
+zillion/MS
+Zilvia/M
+Zimbabwean/S
+Zimbabwe/M
+Zimmerman/M
+zincked
+zincking
+zinc/MS
+zing/GZDRM
+zingy/RT
+zinnia/SM
+Zionism/MS
+Zionist/MS
+Zion/SM
+zip/MS
+zipped/U
+zipper/GSDM
+zipping/U
+zippy/RT
+zips/U
+zirconium/MS
+zircon/SM
+Zita/M
+Zitella/M
+zither/SM
+zit/S
+zloty/SM
+Zn/M
+zodiacal
+zodiac/SM
+Zoe/M
+Zola/M
+Zollie/M
+Zolly/M
+Zomba/M
+zombie/SM
+zombi's
+zonal/Y
+Zonda/M
+Zondra/M
+zoned/A
+zone/MYDSRJG
+zones/A
+zoning/A
+zonked
+Zonnya/M
+zookeepers
+zoological/Y
+zoologist/SM
+zoology/MS
+zoom/DGS
+zoophyte/SM
+zoophytic
+zoo/SM
+Zorah/M
+Zora/M
+Zorana/M
+Zorina/M
+Zorine/M
+Zorn/M
+Zoroaster/M
+Zoroastrianism/MS
+Zoroastrian/S
+Zorro/M
+Zosma/M
+zounds/S
+Zr/M
+Zs
+Zsazsa/M
+Zsigmondy/M
+z/TGJ
+Zubenelgenubi/M
+Zubeneschamali/M
+zucchini/SM
+Zukor/M
+Zulema/M
+Zululand/M
+Zulu/MS
+Zuni/S
+Zürich/M
+Zuzana/M
+zwieback/MS
+Zwingli/M
+Zworykin/M
+Z/X
+zydeco/S
+zygote/SM
+zygotic
+zymurgy/S
diff --git a/Plugins/spellchecker/ZIP/Icons/flags.dll b/Plugins/spellchecker/ZIP/Icons/flags.dll
new file mode 100644
index 0000000..6c99a02
--- /dev/null
+++ b/Plugins/spellchecker/ZIP/Icons/flags.dll
Binary files differ
diff --git a/Plugins/spellchecker/ZIP/doit.bat b/Plugins/spellchecker/ZIP/doit.bat
new file mode 100644
index 0000000..73e7fd2
--- /dev/null
+++ b/Plugins/spellchecker/ZIP/doit.bat
@@ -0,0 +1,141 @@
+rem @echo off
+
+rem Batch file to build and upload files
+rem
+rem TODO: Integration with FL
+
+set name=spellchecker
+set version=0.2.6.0
+
+rem To upload, this var must be set here or in other batch
+rem set ftp=ftp://<user>:<password>@<ftp>/<path>
+
+echo Building %name% ...
+
+rem msdev ..\%name%.dsp /MAKE "%name% - Win32 Release" /REBUILD
+rem msdev ..\%name%.dsp /MAKE "%name% - Win32 Unicode Release" /REBUILD
+
+echo Generating files for %name% ...
+
+del *.zip
+del *.dll
+del *.pdb
+rd /S /Q Plugins
+rd /S /Q Docs
+rd /S /Q src
+
+copy "..\bin\Win32\Release\%name%.pdb"
+copy "..\bin\Win32\Unicode Release\%name%W.pdb"
+copy "..\bin\x64\Unicode Release\%name%64.pdb"
+copy ..\Docs\%name%_changelog.txt
+copy ..\Docs\%name%_version.txt
+copy ..\Docs\%name%_readme.txt
+mkdir Docs
+cd Docs
+del /Q *.*
+copy ..\..\Docs\%name%_readme.txt
+copy ..\..\Docs\langpack_%name%.txt
+rem copy ..\..\Docs\helppack_%name%.txt
+copy ..\..\m_%name%.h
+cd ..
+mkdir src
+cd src
+mkdir %name%
+cd %name%
+del /Q *.*
+copy ..\..\..\*.h
+copy ..\..\..\*.cpp
+copy ..\..\..\*.rc
+copy ..\..\..\*.dsp
+copy ..\..\..\*.dsw
+mkdir res
+cd res
+del /Q *.*
+copy ..\..\..\..\res\*.*
+cd ..
+mkdir sdk
+cd sdk
+del /Q *.*
+copy ..\..\..\..\sdk\*.*
+cd ..
+mkdir hunspell
+cd hunspell
+del /Q *.*
+copy ..\..\..\..\hunspell\*.*
+cd ..
+mkdir Docs
+cd Docs
+del /Q *.*
+copy ..\..\..\..\Docs\*.*
+cd ..
+cd ..
+mkdir utils
+cd utils
+del /Q *.*
+copy ..\..\..\..\utils\*.*
+cd ..
+cd ..
+
+mkdir Plugins
+cd Plugins
+del /Q *.dll
+copy "..\..\bin\Win32\Release\%name%.dll"
+cd ..
+
+zip -r -q %name%.%version%.zip Plugins Docs
+copy %name%.%version%.zip %name%.zip
+zip -r -q %name%.FL.zip Plugins Docs Dictionaries Icons
+
+cd Plugins
+del /Q *.dll
+copy "..\..\bin\Win32\Unicode Release\%name%W.dll"
+cd ..
+
+zip -r -q %name%W.%version%.zip Plugins Docs
+copy %name%W.%version%.zip %name%W.zip
+zip -r -q %name%W.FL.zip Plugins Docs Dictionaries Icons
+
+
+cd Plugins
+del /Q *.dll
+copy "..\..\bin\x64\Unicode Release\%name%64.dll"
+cd ..
+
+zip -r -q %name%64.%version%.zip Plugins Docs
+copy %name%64.%version%.zip %name%64.zip
+zip -r -q %name%64.FL.zip Plugins Docs Dictionaries Icons
+
+
+zip -r -q %name%.pdb.%version%.zip %name%.pdb
+copy %name%.pdb.%version%.zip %name%.pdb.zip
+zip -r -q %name%W.pdb.%version%.zip %name%W.pdb
+copy %name%W.pdb.%version%.zip %name%W.pdb.zip
+zip -r -q %name%64.pdb.%version%.zip %name%64.pdb
+copy %name%64.pdb.%version%.zip %name%64.pdb.zip
+
+zip -r -q %name%_src.zip src\*.*
+
+del *.dll
+del *.PDB
+
+rd /S /Q Plugins
+rd /S /Q Docs
+rd /S /Q src
+
+if "%ftp%"=="" GOTO END
+
+echo Going to upload files...
+pause
+
+"C:\Program Files\FileZilla\FileZilla.exe" -u .\%name%.zip %ftp% -overwrite -close
+"C:\Program Files\FileZilla\FileZilla.exe" -u .\%name%W.zip %ftp% -overwrite -close
+"C:\Program Files\FileZilla\FileZilla.exe" -u .\%name%.pdb.zip %ftp% -overwrite -close
+"C:\Program Files\FileZilla\FileZilla.exe" -u .\%name%W.pdb.zip %ftp% -overwrite -close
+"C:\Program Files\FileZilla\FileZilla.exe" -u .\%name%_changelog.txt %ftp% -overwrite -close
+"C:\Program Files\FileZilla\FileZilla.exe" -u .\%name%_version.txt %ftp% -overwrite -close
+"C:\Program Files\FileZilla\FileZilla.exe" -u .\%name%_readme.txt %ftp% -overwrite -close
+rem "C:\Program Files\FileZilla\FileZilla.exe" -u .\srmm.spellchecker.patch %ftp% -overwrite -close
+
+:END
+
+echo Done.
diff --git a/Plugins/spellchecker/ZIP/zip.exe b/Plugins/spellchecker/ZIP/zip.exe
new file mode 100644
index 0000000..65de183
--- /dev/null
+++ b/Plugins/spellchecker/ZIP/zip.exe
Binary files differ
diff --git a/Plugins/spellchecker/ardialog.cpp b/Plugins/spellchecker/ardialog.cpp
new file mode 100644
index 0000000..dc8a24c
--- /dev/null
+++ b/Plugins/spellchecker/ardialog.cpp
@@ -0,0 +1,316 @@
+/*
+Copyright (C) 2006-2010 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#include "commons.h"
+
+
+static LRESULT CALLBACK EditProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
+static INT_PTR CALLBACK AddReplacementDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam);
+
+
+struct Data
+{
+ Dictionary *dict;
+ tstring find;
+ tstring replace;
+ BOOL useVariables;
+
+ BOOL modal;
+ BOOL findReadOnly;
+
+ AutoReplaceDialogCallback callback;
+ void *param;
+
+ WNDPROC old_edit_proc;
+};
+
+BOOL ShowAutoReplaceDialog(HWND parent, BOOL modal,
+ Dictionary *dict, const TCHAR *find, const TCHAR *replace, BOOL useVariables,
+ BOOL findReadOnly, AutoReplaceDialogCallback callback, void *param)
+{
+ Data *data = new Data();
+ data->dict = dict;
+ data->useVariables = useVariables;
+ data->modal = modal;
+ data->findReadOnly = findReadOnly;
+ data->callback = callback;
+ data->param = param;
+
+ if (find != NULL)
+ data->find = find;
+
+ if (replace != NULL)
+ data->replace = replace;
+
+ if (modal)
+ {
+ return DialogBoxParam(hInst, MAKEINTRESOURCE(IDD_ADD_REPLACEMENT), parent,
+ AddReplacementDlgProc, (LPARAM) data);
+ }
+ else
+ {
+ HWND hwnd = CreateDialogParam(hInst, MAKEINTRESOURCE(IDD_ADD_REPLACEMENT), parent,
+ AddReplacementDlgProc, (LPARAM) data);
+ SetForegroundWindow(hwnd);
+ SetFocus(hwnd);
+ SetFocus(GetDlgItem(hwnd, IDC_NEW));
+ ShowWindow(hwnd, SW_SHOW);
+ return TRUE;
+ }
+}
+
+
+static LRESULT CALLBACK OnlyCharsEditProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ Data *data = (Data *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
+
+ switch(msg)
+ {
+ case WM_CHAR:
+ {
+ if (wParam == VK_BACK)
+ break;
+ if (lParam & (1 << 28)) // ALT key
+ break;
+ if (GetKeyState(VK_CONTROL) & 0x8000) // CTRL key
+ break;
+
+ TCHAR c = (TCHAR) wParam;
+ if (!data->dict->autoReplace->isWordChar(c))
+ return 1;
+
+ TCHAR tmp[2] = { c, 0 };
+ CharLower(tmp);
+ wParam = tmp[0];
+
+ break;
+ }
+ }
+
+ LRESULT ret = CallWindowProc(data->old_edit_proc, hwnd, msg, wParam, lParam);
+
+ switch(msg)
+ {
+ case EM_PASTESPECIAL:
+ case WM_PASTE:
+ {
+ TCHAR text[256];
+ GetWindowText(hwnd, text, MAX_REGS(text));
+
+ scoped_free<TCHAR> dest = data->dict->autoReplace->filterText(text);
+ SetWindowText(hwnd, dest);
+ break;
+ }
+ }
+
+ return ret;
+
+}
+
+
+static BOOL CenterParent(HWND hwnd)
+{
+ HWND hwndParent = GetParent(hwnd);
+ if (hwndParent == NULL)
+ return FALSE;
+
+ RECT rect, rectP;
+ GetWindowRect(hwnd, &rect);
+ GetWindowRect(hwndParent, &rectP);
+
+ int width = rect.right - rect.left;
+ int height = rect.bottom - rect.top;
+
+ int x = ((rectP.right-rectP.left) - width) / 2 + rectP.left;
+ int y = ((rectP.bottom-rectP.top) - height) / 2 + rectP.top;
+
+ int screenwidth = GetSystemMetrics(SM_CXSCREEN);
+ int screenheight = GetSystemMetrics(SM_CYSCREEN);
+
+ if(x + width > screenwidth) x = screenwidth - width;
+ if(y + height > screenheight) y = screenheight - height;
+ if(x < 0) x = 0;
+ if(y < 0) y = 0;
+
+ MoveWindow(hwnd, x, y, width, height, FALSE);
+
+ return TRUE;
+}
+
+static void Close(HWND hwndDlg, int ret)
+{
+ Data *data = (Data *) GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
+
+ if (!ret)
+ {
+ data->callback(TRUE, data->dict,
+ data->find.c_str(), data->replace.c_str(), data->useVariables,
+ data->find.c_str(), data->param);
+ }
+
+ SetWindowLongPtr(GetDlgItem(hwndDlg, IDC_OLD), GWLP_WNDPROC, (LONG_PTR) data->old_edit_proc);
+ data->old_edit_proc = NULL;
+
+ if (data->modal)
+ EndDialog(hwndDlg, ret);
+ else
+ DestroyWindow(hwndDlg);
+
+ delete data;
+}
+
+static INT_PTR CALLBACK AddReplacementDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg)
+ {
+ case WM_INITDIALOG:
+ {
+ TranslateDialogDefault(hwndDlg);
+
+ Data *data = (Data *) lParam;
+ SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR) data);
+
+ SetWindowLongPtr(GetDlgItem(hwndDlg, IDC_OLD), GWLP_USERDATA, (LONG_PTR) data);
+ data->old_edit_proc = (WNDPROC) SetWindowLongPtr(GetDlgItem(hwndDlg, IDC_OLD), GWLP_WNDPROC,
+ (LONG_PTR) OnlyCharsEditProc);
+
+ HICON hIcon = IcoLib_LoadIcon("spellchecker_enabled");
+ SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM) hIcon);
+ IcoLib_ReleaseIcon(hIcon);
+
+ SendDlgItemMessage(hwndDlg, IDC_OLD, EM_LIMITTEXT, 256, 0);
+ SendDlgItemMessage(hwndDlg, IDC_NEW, EM_LIMITTEXT, 256, 0);
+
+ if (!data->find.empty())
+ {
+ scoped_free<TCHAR> tmp = data->dict->autoReplace->filterText(data->find.c_str());
+ SetDlgItemText(hwndDlg, IDC_OLD, tmp);
+ }
+ if (!data->replace.empty())
+ SetDlgItemText(hwndDlg, IDC_NEW, data->replace.c_str());
+
+ CheckDlgButton(hwndDlg, IDC_VARIABLES, data->useVariables ? BST_CHECKED : BST_UNCHECKED);
+
+ if (data->findReadOnly)
+ {
+ SendDlgItemMessage(hwndDlg, IDC_OLD, EM_SETREADONLY, TRUE, 0);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_OLD_PS), FALSE);
+ }
+
+ if (!variables_enabled)
+ {
+ ShowWindow(GetDlgItem(hwndDlg, IDC_VARIABLES), FALSE);
+ ShowWindow(GetDlgItem(hwndDlg, IDC_VAR_HELP), FALSE);
+
+ RECT rc_old;
+ GetWindowRect(GetDlgItem(hwndDlg, IDC_OLD), &rc_old);
+ RECT rc_new;
+ GetWindowRect(GetDlgItem(hwndDlg, IDC_NEW), &rc_new);
+ rc_new.right = rc_old.right;
+
+ SetWindowPos(GetDlgItem(hwndDlg, IDC_NEW), NULL, 0, 0,
+ rc_new.right - rc_new.left, rc_new.bottom - rc_new.top,
+ SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOREDRAW | SWP_NOZORDER);
+ }
+ else
+ {
+ variables_skin_helpbutton(hwndDlg, IDC_VAR_HELP);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_VAR_HELP), IsDlgButtonChecked(hwndDlg, IDC_VARIABLES));
+ }
+
+ CenterParent(hwndDlg);
+
+ return TRUE;
+ }
+
+ case WM_COMMAND:
+ switch(wParam)
+ {
+ case IDOK:
+ {
+ Data *data = (Data *) GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
+
+ TCHAR find[256];
+ if (data->findReadOnly)
+ {
+ lstrcpyn(find, data->find.c_str(), MAX_REGS(find));
+ }
+ else
+ {
+ GetDlgItemText(hwndDlg, IDC_OLD, find, MAX_REGS(find));
+ lstrtrim(find);
+ }
+
+ TCHAR replace[256];
+ GetDlgItemText(hwndDlg, IDC_NEW, replace, MAX_REGS(replace));
+ lstrtrim(replace);
+
+ if (!data->findReadOnly && find[0] == 0)
+ {
+ MessageBox(hwndDlg, TranslateT("The wrong word can't be empty!"), TranslateT("Wrong Correction"),
+ MB_OK | MB_ICONERROR);
+ }
+ else if (replace[0] == 0)
+ {
+ MessageBox(hwndDlg, TranslateT("The correction can't be empty!"), TranslateT("Wrong Correction"),
+ MB_OK | MB_ICONERROR);
+ }
+ else if (_tcscmp(find, replace) == 0)
+ {
+ MessageBox(hwndDlg, TranslateT("The correction can't be equal to the wrong word!"), TranslateT("Wrong Correction"),
+ MB_OK | MB_ICONERROR);
+ }
+ else
+ {
+ data->callback(FALSE, data->dict,
+ find, replace, IsDlgButtonChecked(hwndDlg, IDC_VARIABLES),
+ data->find.c_str(), data->param);
+ Close(hwndDlg, 1);
+ }
+
+ break;
+ }
+ case IDCANCEL:
+ {
+ Close(hwndDlg, 0);
+ break;
+ }
+ case IDC_VARIABLES:
+ {
+ EnableWindow(GetDlgItem(hwndDlg, IDC_VAR_HELP), IsDlgButtonChecked(hwndDlg, IDC_VARIABLES));
+ break;
+ }
+ case IDC_VAR_HELP:
+ {
+ variables_showhelp(hwndDlg, IDC_NEW, VHF_FULLDLG, NULL, "The wrong word typed by the user");
+ break;
+ }
+ }
+ break;
+
+ case WM_CLOSE:
+ {
+ Close(hwndDlg, 0);
+ break;
+ }
+ }
+
+ return FALSE;
+}
diff --git a/Plugins/spellchecker/ardialog.h b/Plugins/spellchecker/ardialog.h
new file mode 100644
index 0000000..83f97c6
--- /dev/null
+++ b/Plugins/spellchecker/ardialog.h
@@ -0,0 +1,33 @@
+/*
+Copyright (C) 2009-2010 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+#ifndef __ARDIALOG_H__
+# define __ARDIALOG_H__
+
+
+typedef void (*AutoReplaceDialogCallback)(BOOL canceled, Dictionary *dict,
+ const TCHAR *find, const TCHAR *replace, BOOL useVariables,
+ const TCHAR *original_find, void *param);
+
+BOOL ShowAutoReplaceDialog(HWND parent, BOOL modal,
+ Dictionary *dict, const TCHAR *find, const TCHAR *replace, BOOL useVariables,
+ BOOL findReadOnly, AutoReplaceDialogCallback callback, void *param);
+
+
+#endif // __ARDIALOG_H__
diff --git a/Plugins/spellchecker/autoreplace.cpp b/Plugins/spellchecker/autoreplace.cpp
new file mode 100644
index 0000000..bf72f46
--- /dev/null
+++ b/Plugins/spellchecker/autoreplace.cpp
@@ -0,0 +1,229 @@
+/*
+Copyright (C) 2009-2010 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#include "commons.h"
+
+
+AutoReplacement::AutoReplacement()
+ : useVariables(FALSE)
+{
+}
+
+AutoReplacement::AutoReplacement(const TCHAR *replace, BOOL useVariables)
+ : replace(replace), useVariables(useVariables)
+{
+}
+
+
+AutoReplaceMap::AutoReplaceMap(TCHAR *aFilename, Dictionary *dict)
+{
+ this->dict = dict;
+ lstrcpyn(filename, aFilename, MAX_REGS(filename));
+ loadAutoReplaceMap();
+}
+
+void AutoReplaceMap::loadAutoReplaceMap()
+{
+ FILE *file = _tfopen(filename, _T("rb"));
+ if (file == NULL)
+ return;
+
+ char tmp[1024];
+ char c;
+ int pos = 0;
+ while((c = fgetc(file)) != EOF)
+ {
+ if (c == '\n' || c == '\r' || pos >= MAX_REGS(tmp) - 1)
+ {
+ if (pos > 0)
+ {
+ tmp[pos] = '\0';
+
+ // Get from
+ BOOL useVars;
+ char *p;
+ if ((p = strstr(tmp, "->")) != NULL)
+ {
+ *p = '\0';
+ p += 2;
+ useVars = FALSE;
+ }
+ else if ((p = strstr(tmp, "-V>")) != NULL)
+ {
+ *p = '\0';
+ p += 3;
+ useVars = TRUE;
+ }
+
+ if (p != NULL)
+ {
+ Utf8ToTchar find(tmp);
+ Utf8ToTchar replace(p);
+
+ lstrtrim(find);
+ lstrtrim(replace);
+
+ if (find[0] != 0 && replace[0] != 0)
+ replacements[find.get()] = AutoReplacement(replace, useVars);
+ }
+ }
+
+ pos = 0;
+ }
+ else
+ {
+ tmp[pos] = c;
+ pos ++;
+ }
+ }
+ fclose(file);
+}
+
+void AutoReplaceMap::writeAutoReplaceMap()
+{
+ // Create path
+ TCHAR *p = _tcsrchr(filename, _T('\\'));
+ if (p != NULL)
+ {
+ *p = 0;
+ CreatePath(filename);
+ *p = _T('\\');
+ }
+
+ // Write it
+ FILE *file = _tfopen(filename, _T("wb"));
+ if (file != NULL)
+ {
+ map<tstring,AutoReplacement>::iterator it = replacements.begin();
+ for(; it != replacements.end(); it++)
+ {
+ AutoReplacement &ar = it->second;
+
+ TcharToUtf8 find(it->first.c_str());
+ TcharToUtf8 replace(ar.replace.c_str());
+
+ if (ar.useVariables)
+ fprintf(file, "%s-V>%s\n", (const char *)find, (const char *)replace);
+ else
+ fprintf(file, "%s->%s\n", (const char *)find, (const char *)replace);
+ }
+ fclose(file);
+ }
+}
+
+
+BOOL AutoReplaceMap::isWordChar(TCHAR c)
+{
+ if (IsNumber(c))
+ return TRUE;
+
+ if (_tcschr(_T("-_.!@#$%&*()[]{}<>:?/\\=+"), c) != NULL)
+ return TRUE;
+
+ return dict->isWordChar(c);
+}
+
+
+TCHAR * AutoReplaceMap::autoReplace(const TCHAR * word)
+{
+ scoped_free<TCHAR> from = _tcslwr(_tcsdup(word));
+
+ if (replacements.find(from.get()) == replacements.end())
+ return NULL;
+
+ AutoReplacement &ar = replacements[from.get()];
+
+ TCHAR *to;
+ if (ar.useVariables)
+ to = variables_parsedup((TCHAR *) ar.replace.c_str(), (TCHAR *) word, NULL);
+ else
+ to = _tcsdup(ar.replace.c_str());
+
+ // Wich case to use?
+ size_t len = lstrlen(word);
+ size_t i;
+ for (i = 0; i < len; i++)
+ if (IsCharLower(word[i]))
+ break;
+
+ if (i <= 0)
+ {
+ // All lower
+ return to;
+ }
+ else if (i >= len)
+ {
+ // All upper
+ return CharUpper(to);
+ }
+ else
+ {
+ // First upper
+ TCHAR tmp[2];
+ tmp[0] = to[0];
+ tmp[1] = _T('\0');
+ CharUpper(tmp);
+ to[0] = tmp[0];
+ return to;
+ }
+}
+
+TCHAR * AutoReplaceMap::filterText(const TCHAR *find)
+{
+ TCHAR *ret = _tcsdup(find);
+ int len = lstrlen(ret);
+ int pos = 0;
+ for(int i = 0; i < len; i++)
+ if (isWordChar(find[i]))
+ ret[pos++] = ret[i];
+ ret[pos] = 0;
+ return CharLower(ret);
+}
+
+void AutoReplaceMap::add(const TCHAR * aFrom, const TCHAR * to, BOOL useVariables)
+{
+ scoped_free<TCHAR> from = filterText(aFrom);
+
+ replacements[from.get()] = AutoReplacement(to, useVariables);
+
+ writeAutoReplaceMap();
+}
+
+void AutoReplaceMap::copyMap(map<tstring, AutoReplacement> *replacements)
+{
+ *replacements = this->replacements;
+}
+
+void AutoReplaceMap::setMap(const map<tstring, AutoReplacement> &replacements)
+{
+ this->replacements.clear();
+
+ map<tstring, AutoReplacement>::const_iterator it = replacements.begin();
+ for(; it != replacements.end(); it++)
+ {
+ scoped_free<TCHAR> from = filterText(it->first.c_str());
+ this->replacements[from.get()] = it->second;
+ }
+
+ writeAutoReplaceMap();
+}
+
+
+
diff --git a/Plugins/spellchecker/autoreplace.h b/Plugins/spellchecker/autoreplace.h
new file mode 100644
index 0000000..e7e2efb
--- /dev/null
+++ b/Plugins/spellchecker/autoreplace.h
@@ -0,0 +1,70 @@
+/*
+Copyright (C) 2009-2010 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#ifndef __AUTOREPLACE_H__
+# define __AUTOREPLACE_H__
+
+
+struct AutoReplacement
+{
+ tstring replace;
+ BOOL useVariables;
+
+ AutoReplacement();
+ AutoReplacement(const TCHAR *replace, BOOL useVariables);
+};
+
+
+class Dictionary;
+
+
+class AutoReplaceMap
+{
+private:
+ TCHAR filename[1024];
+ Dictionary *dict;
+ map<tstring, AutoReplacement> replacements;
+
+ void loadAutoReplaceMap();
+ void writeAutoReplaceMap();
+
+public:
+ AutoReplaceMap(TCHAR *filename, Dictionary *dict);
+
+ TCHAR *filterText(const TCHAR *find);
+ BOOL isWordChar(TCHAR c);
+
+ /// Return an auto replacement to a word or NULL if none exists.
+ /// You have to free the item.
+ TCHAR * autoReplace(const TCHAR * word);
+
+ /// Add a word to the list of auto-replaced words
+ void add(const TCHAR * from, const TCHAR * to, BOOL useVariables = FALSE);
+
+ /// Make a copy of the auto replace map
+ void copyMap(map<tstring, AutoReplacement> *replacements);
+
+ /// Make a copy of the auto replace map
+ void setMap(const map<tstring, AutoReplacement> &replacements);
+};
+
+
+
+#endif // __AUTOREPLACE_H__
diff --git a/Plugins/spellchecker/codepages.cpp b/Plugins/spellchecker/codepages.cpp
new file mode 100644
index 0000000..6441cda
--- /dev/null
+++ b/Plugins/spellchecker/codepages.cpp
@@ -0,0 +1,307 @@
+
+struct {
+ char *name;
+ UINT codepage;
+} codepages[] = {
+ { "ISO8859-1", 28591 },
+ { "UTF-7", CP_UTF7 },
+ { "UTF-8", CP_UTF8 },
+ { "UTF7", CP_UTF7 },
+ { "UTF8", CP_UTF8 },
+ { "ISO8859-2", 28592 },
+ { "ISO8859-3", 28593 },
+ { "ISO8859-4", 28594 },
+ { "ISO8859-5", 28595 },
+ { "ISO8859-6", 28596 },
+ { "ISO8859-7", 28597 },
+ { "ISO8859-8", 28598 },
+ { "ISO8859-9", 28599 },
+ { "ASMO-708", 708 },
+ { "DOS-720", 720 },
+ { "iso-8859-6", 28596 },
+ { "arabic", 28596 },
+ { "csISOLatinArabic", 28596 },
+ { "ECMA-114", 28596 },
+ { "ISO_8859-6", 28596 },
+ { "ISO_8859-6:1987", 28596 },
+ { "iso-ir-127", 28596 },
+ { "x-mac-arabic", 10004 },
+ { "windows-1256", 1256 },
+ { "cp1256", 1256 },
+ { "ibm775", 775 },
+ { "CP500", 775 },
+ { "iso-8859-4", 28594 },
+ { "csISOLatin4", 28594 },
+ { "ISO_8859-4", 28594 },
+ { "ISO_8859-4:1988", 28594 },
+ { "iso-ir-110", 28594 },
+ { "l4", 28594 },
+ { "latin4", 28594 },
+ { "windows-1257", 1257 },
+ { "ibm852", 852 },
+ { "cp852", 852 },
+ { "iso-8859-2", 28592 },
+ { "csISOLatin2", 28592 },
+ { "iso_8859-2", 28592 },
+ { "iso_8859-2:1987", 28592 },
+ { "iso-ir-101", 28592 },
+ { "l2", 28592 },
+ { "latin2", 28592 },
+ { "x-mac-ce", 10029 },
+ { "windows-1250", 1250 },
+ { "x-cp1250", 1250 },
+ { "EUC-CN", 51936 },
+ { "x-euc-cn", 51936 },
+ { "gb2312", 936 },
+ { "chinese", 936 },
+ { "CN-GB", 936 },
+ { "csGB2312", 936 },
+ { "csGB231280", 936 },
+ { "csISO58GB231280", 936 },
+ { "GB_2312-80", 936 },
+ { "GB231280", 936 },
+ { "GB2312-80", 936 },
+ { "GBK", 936 },
+ { "iso-ir-58", 936 },
+ { "hz-gb-2312", 52936 },
+ { "x-mac-chinesesimp", 10008 },
+ { "big5", 950 },
+ { "cn-big5", 950 },
+ { "csbig5", 950 },
+ { "x-x-big5", 950 },
+ { "x-Chinese-CNS", 20000 },
+ { "x-Chinese-Eten", 20002 },
+ { "x-mac-chinesetrad", 10002 },
+ { "cp866", 866 },
+ { "ibm866", 866 },
+ { "iso-8859-5", 28595 },
+ { "csISOLatin5", 28595 },
+ { "csISOLatinCyrillic", 28595 },
+ { "cyrillic", 28595 },
+ { "ISO_8859-5", 28595 },
+ { "ISO_8859-5:1988", 28595 },
+ { "iso-ir-144", 28595 },
+ { "l5", 28595 },
+ { "KOI8-R", 20866 },
+ { "csKOI8R", 20866 },
+ { "koi", 20866 },
+ { "koi8", 20866 },
+ { "koi8r", 20866 },
+ { "KOI8-U", 21866 },
+ { "koi8-ru", 21866 },
+ { "x-mac-cyrillic", 10007 },
+ { "windows-1251", 1251 },
+ { "Win1251", 1251 },
+ { "x-cp1251", 1251 },
+ { "x-Europa", 29001 },
+ { "x-IA5-German", 20106 },
+ { "ibm737", 737 },
+ { "iso-8859-7", 28597 },
+ { "csISOLatinGreek", 28597 },
+ { "ECMA-118", 28597 },
+ { "ELOT_928", 28597 },
+ { "greek", 28597 },
+ { "greek8", 28597 },
+ { "ISO_8859-7", 28597 },
+ { "ISO_8859-7:1987", 28597 },
+ { "iso-ir-126", 28597 },
+ { "x-mac-greek", 10006 },
+ { "windows-1253", 1253 },
+ { "ibm869", 869 },
+ { "DOS-862", 862 },
+ { "iso-8859-8-i", 38598 },
+ { "logical", 38598 },
+ { "iso-8859-8", 28598 },
+ { "csISOLatinHebrew", 28598 },
+ { "hebrew", 28598 },
+ { "ISO_8859-8", 28598 },
+ { "ISO_8859-8:1988", 28598 },
+ { "ISO-8859-8", 28598 },
+ { "iso-ir-138", 28598 },
+ { "visual", 28598 },
+ { "x-mac-hebrew", 10005 },
+ { "windows-1255", 1255 },
+ { "ISO_8859-8-I", 1255 },
+ { "ISO-8859-8", 1255 },
+ { "x-EBCDIC-Arabic", 20420 },
+ { "x-EBCDIC-CyrillicRussian", 20880 },
+ { "x-EBCDIC-CyrillicSerbianBulgarian", 21025 },
+ { "x-EBCDIC-DenmarkNorway", 20277 },
+ { "x-ebcdic-denmarknorway-euro", 1142 },
+ { "x-EBCDIC-FinlandSweden", 20278 },
+ { "x-ebcdic-finlandsweden-euro", 1143 },
+ { "X-EBCDIC-France", 1143 },
+ { "X-EBCDIC-France", 1143 },
+ { "x-ebcdic-france-euro", 1147 },
+ { "x-EBCDIC-Germany", 20273 },
+ { "x-ebcdic-germany-euro", 1141 },
+ { "x-EBCDIC-GreekModern", 875 },
+ { "x-EBCDIC-Greek", 20423 },
+ { "x-EBCDIC-Hebrew", 20424 },
+ { "x-EBCDIC-Icelandic", 20871 },
+ { "x-ebcdic-icelandic-euro", 1149 },
+ { "x-ebcdic-international-euro", 1148 },
+ { "x-EBCDIC-Italy", 20280 },
+ { "x-ebcdic-italy-euro", 1144 },
+ { "x-EBCDIC-JapaneseAndKana", 50930 },
+ { "x-EBCDIC-JapaneseAndJapaneseLatin", 50939 },
+ { "x-EBCDIC-JapaneseAndUSCanada", 50931 },
+ { "x-EBCDIC-JapaneseKatakana", 20290 },
+ { "x-EBCDIC-KoreanAndKoreanExtended", 50933 },
+ { "x-EBCDIC-KoreanExtended", 20833 },
+ { "CP870", 870 },
+ { "x-EBCDIC-SimplifiedChinese", 50935 },
+ { "X-EBCDIC-Spain", 20284 },
+ { "x-ebcdic-spain-euro", 1145 },
+ { "x-EBCDIC-Thai", 20838 },
+ { "x-EBCDIC-TraditionalChinese", 50937 },
+ { "CP1026", 1026 },
+ { "x-EBCDIC-Turkish", 20905 },
+ { "x-EBCDIC-UK", 20285 },
+ { "x-ebcdic-uk-euro", 1146 },
+ { "ebcdic-cp-us", 37 },
+ { "x-ebcdic-cp-us-euro", 1140 },
+ { "ibm861", 861 },
+ { "x-mac-icelandic", 10079 },
+ { "x-iscii-as", 57006 },
+ { "x-iscii-be", 57003 },
+ { "x-iscii-de", 57002 },
+ { "x-iscii-gu", 57010 },
+ { "x-iscii-ka", 57008 },
+ { "x-iscii-ma", 57009 },
+ { "x-iscii-or", 57007 },
+ { "x-iscii-pa", 57011 },
+ { "x-iscii-ta", 57004 },
+ { "x-iscii-te", 57005 },
+ { "euc-jp", 51932 },
+ { "csEUCPkdFmtJapanese", 51932 },
+ { "Extended_UNIX_Code_Packed_Format_for_Japanese", 51932 },
+ { "x-euc", 51932 },
+ { "x-euc-jp", 51932 },
+ { "iso-2022-jp", 50220 },
+ { "iso-2022-jp", 50222 },
+ { "_iso-2022-jp$SIO", 50222 },
+ { "csISO2022JP", 50221 },
+ { "_iso-2022-jp", 50221 },
+ { "x-mac-japanese", 10001 },
+ { "shift_jis", 932 },
+ { "csShiftJIS", 932 },
+ { "csWindows31J", 932 },
+ { "ms_Kanji", 932 },
+ { "shift-jis", 932 },
+ { "x-ms-cp932", 932 },
+ { "x-sjis", 932 },
+ { "ks_c_5601-1987", 949 },
+ { "csKSC56011987", 949 },
+ { "euc-kr", 949 },
+ { "iso-ir-149", 949 },
+ { "korean", 949 },
+ { "ks_c_5601", 949 },
+ { "ks_c_5601_1987", 949 },
+ { "ks_c_5601-1989", 949 },
+ { "KSC_5601", 949 },
+ { "KSC5601", 949 },
+ { "euc-kr", 51949 },
+ { "csEUCKR", 51949 },
+ { "iso-2022-kr", 50225 },
+ { "csISO2022KR", 50225 },
+ { "Johab", 1361 },
+ { "x-mac-korean", 10003 },
+ { "iso-8859-3", 28593 },
+ { "csISO", 28593 },
+ { "Latin3", 28593 },
+ { "ISO_8859-3", 28593 },
+ { "ISO_8859-3:1988", 28593 },
+ { "iso-ir-109", 28593 },
+ { "l3", 28593 },
+ { "latin3", 28593 },
+ { "iso-8859-15", 28605 },
+ { "csISO", 28605 },
+ { "Latin9", 28605 },
+ { "ISO_8859-15", 28605 },
+ { "l9", 28605 },
+ { "latin9", 28605 },
+ { "x-IA5-Norwegian", 20108 },
+ { "IBM437", 437 },
+ { "437", 437 },
+ { "cp437", 437 },
+ { "csPC8", 437 },
+ { "CodePage437", 437 },
+ { "x-IA5-Swedish", 20107 },
+ { "windows-874", 874 },
+ { "DOS-874", 874 },
+ { "iso-8859-11", 874 },
+ { "TIS-620", 874 },
+ { "ibm857", 857 },
+ { "iso-8859-9", 28599 },
+ { "csISO", 28599 },
+ { "Latin5", 28599 },
+ { "ISO_8859-9", 28599 },
+ { "ISO_8859-9:1989", 28599 },
+ { "iso-ir-148", 28599 },
+ { "l5", 28599 },
+ { "latin5", 28599 },
+ { "x-mac-turkish", 10081 },
+ { "windows-1254", 1254 },
+ { "ISO_8859-9", 1254 },
+ { "ISO_8859-9:1989", 1254 },
+ { "iso-8859-9", 1254 },
+ { "iso-ir-148", 1254 },
+ { "latin5", 1254 },
+ { "unicode", 1200 },
+ { "utf-16", 1200 },
+ { "unicodeFFFE", 1201 },
+ { "utf-7", 65000 },
+ { "csUnicode11UTF7", 65000 },
+ { "unicode-1-1-utf-7", 65000 },
+ { "x-unicode-2-0-utf-7", 65000 },
+ { "utf-8", 65001 },
+ { "unicode-1-1-utf-8", 65001 },
+ { "unicode-2-0-utf-8", 65001 },
+ { "x-unicode-2-0-utf-8", 65001 },
+ { "us-ascii", 20127 },
+ { "ANSI_X3.4-1968", 20127 },
+ { "ANSI_X3.4-1986", 20127 },
+ { "ascii", 20127 },
+ { "cp367", 20127 },
+ { "csASCII", 20127 },
+ { "IBM367", 20127 },
+ { "ISO_646.irv:1991", 20127 },
+ { "ISO646-US", 20127 },
+ { "iso-ir-6us", 20127 },
+ { "windows-1258", 1258 },
+ { "ibm850", 850 },
+ { "x-IA5", 20105 },
+ { "iso-8859-1", 28591 },
+ { "cp819", 28591 },
+ { "csISO", 28591 },
+ { "Latin1", 28591 },
+ { "ibm819", 28591 },
+ { "iso_8859-1", 28591 },
+ { "iso_8859-1:1987", 28591 },
+ { "iso-ir-100", 28591 },
+ { "l1", 28591 },
+ { "latin1", 28591 },
+ { "macintosh", 10000 },
+ { "Windows-1252", 1252 },
+ { "ANSI_X3.4-1968", 1252 },
+ { "ANSI_X3.4-1986", 1252 },
+ { "ascii", 1252 },
+ { "cp367", 1252 },
+ { "cp819", 1252 },
+ { "csASCII", 1252 },
+ { "IBM367", 1252 },
+ { "ibm819", 1252 },
+ { "ISO_646.irv:1991", 1252 },
+ { "iso_8859-1", 1252 },
+ { "iso_8859-1:1987", 1252 },
+ { "ISO646-US", 1252 },
+ { "iso-ir-100", 1252 },
+ { "iso-ir-6", 1252 },
+ { "latin1", 1252 },
+ { "us", 1252 },
+ { "us-ascii", 1252 },
+ { "x-ansi", 1252 },
+ { "microsoft-cp1251", 1251 }
+};
+
diff --git a/Plugins/spellchecker/commons.h b/Plugins/spellchecker/commons.h
new file mode 100644
index 0000000..336c97e
--- /dev/null
+++ b/Plugins/spellchecker/commons.h
@@ -0,0 +1,154 @@
+/*
+Copyright (C) 2006-2010 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#ifndef __COMMONS_H__
+# define __COMMONS_H__
+
+
+#define OEMRESOURCE
+#include <windows.h>
+#include <tchar.h>
+#include <stdio.h>
+#include <time.h>
+#include <richedit.h>
+#include <tom.h>
+#include <richole.h>
+#include <commctrl.h>
+
+// Disable "...truncated to '255' characters in the debug information" warnings
+#pragma warning(disable: 4786)
+
+#include <map>
+#include <vector>
+#include <string>
+using namespace std;
+
+
+// Miranda headers
+#define MIRANDA_VER 0x0900
+#include <newpluginapi.h>
+#include <m_system.h>
+#include <m_system_cpp.h>
+#include <m_protocols.h>
+#include <m_protosvc.h>
+#include <m_clist.h>
+#include <m_contacts.h>
+#include <m_langpack.h>
+#include <m_database.h>
+#include <m_options.h>
+#include <m_utils.h>
+#include <m_popup.h>
+#include <m_history.h>
+#include <m_message.h>
+#include <m_icolib.h>
+#include <m_hotkeys.h>
+
+//own includes
+#include "sdk/m_folders.h"
+#include "sdk/m_updater.h"
+#include "sdk/m_metacontacts.h"
+#include "sdk/m_variables.h"
+#include "sdk/m_userinfoex.h"
+
+#include "../utils/mir_memory.h"
+#include "../utils/mir_options.h"
+#include "../utils/mir_icons.h"
+#include "../utils/tstring.h"
+#include "../utils/utf8_helpers.h"
+#include "../utils/scope.h"
+
+#include "resource.h"
+#include "m_spellchecker.h"
+#include "options.h"
+#include "autoreplace.h"
+#include "dictionary.h"
+#include "ardialog.h"
+#include "RichEdit.h"
+
+
+#define MODULE_NAME "SpellChecker"
+
+#define FLAGS_DLL_FOLDER _T("%miranda_path%\\Icons")
+#define CUSTOM_DICTIONARIES_FOLDER _T("%miranda_userdata%\\Dictionaries")
+#define DICTIONARIES_FOLDER _T("%miranda_path%\\Dictionaries")
+
+// Global Variables
+extern HINSTANCE hInst;
+extern PLUGINLINK *pluginLink;
+extern BOOL uinfoex_enabled;
+extern BOOL variables_enabled;
+
+
+#define MAX_REGS(_A_) ( sizeof(_A_) / sizeof(_A_[0]) )
+#define FREE(_m_) if (_m_ != NULL) { free(_m_); _m_ = NULL; }
+
+
+#define ICON_SIZE 16
+
+
+extern LIST<Dictionary> languages;
+
+struct WrongWordPopupMenuData
+{
+ Suggestions suggestions;
+ TCHAR *word;
+ CHARRANGE pos;
+ HMENU hMeSubMenu;
+ HMENU hCorrectSubMenu;
+ HMENU hReplaceSubMenu;
+};
+
+struct Dialog
+{
+ HWND hwnd;
+ HWND hwnd_owner;
+ HANDLE hContact;
+ char name[64];
+ Dictionary *lang;
+ TCHAR lang_name[32];
+ WNDPROC old_edit_proc;
+ WNDPROC owner_old_edit_proc;
+ BOOL enabled;
+ BOOL srmm;
+
+ RichEdit *re;
+
+ HWND hwnd_menu_owner;
+ WNDPROC old_menu_proc;
+
+ BOOL changed;
+ BOOL markedSomeWord;
+ int old_text_len;
+
+ // Popup data
+ HMENU hLanguageSubMenu;
+ HMENU hWrongWordsSubMenu;
+ vector<WrongWordPopupMenuData> *wrong_words;
+};
+
+HICON IcoLib_LoadIcon(Dictionary *dict, BOOL copy = FALSE);
+
+BOOL CenterParent(HWND hwnd);
+BOOL CreatePath(const TCHAR *path);
+TCHAR *lstrtrim(TCHAR *str);
+BOOL lstreq(TCHAR *a, TCHAR *b, size_t len = -1);
+BOOL IsNumber(TCHAR c);
+
+#endif // __COMMONS_H__
diff --git a/Plugins/spellchecker/dictionary.cpp b/Plugins/spellchecker/dictionary.cpp
new file mode 100644
index 0000000..8d65763
--- /dev/null
+++ b/Plugins/spellchecker/dictionary.cpp
@@ -0,0 +1,735 @@
+/*
+Copyright (C) 2006-2010 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#include "commons.h"
+
+#include "hunspell/config.h"
+#include "hunspell/hunspell.hxx"
+#include "hunspell/csutil.hxx"
+
+
+#include "codepages.cpp"
+
+
+DWORD WINAPI LoadThread(LPVOID hd);
+
+// Additional languages that i could not find in Windows
+TCHAR *aditionalLanguages[] = {
+ _T("tl_PH"), _T("Tagalog (Philippines)"),
+ _T("de_frami_neu"), _T("German (Germany)")
+};
+
+
+
+#define LANGUAGE_NOT_LOADED 1
+#define LANGUAGE_LOADING -1
+#define LANGUAGE_LOADED 0
+
+
+
+class HunspellDictionary : public Dictionary {
+protected:
+ TCHAR fileWithoutExtension[1024];
+ TCHAR userPath[1024];
+ volatile int loaded;
+ Hunspell *hunspell;
+ TCHAR *wordChars;
+ UINT codePage;
+
+ void loadCustomDict()
+ {
+ TCHAR filename[1024];
+ mir_sntprintf(filename, MAX_REGS(filename), _T("%s\\%s.cdic"), userPath, language);
+
+ FILE *file = _tfopen(filename, _T("rb"));
+ if (file != NULL)
+ {
+ char tmp[1024];
+ char c;
+ int pos = 0;
+ while((c = fgetc(file)) != EOF)
+ {
+ if (c == '\n' || c == '\r' || pos >= MAX_REGS(tmp) - 1)
+ {
+ if (pos > 0)
+ {
+ tmp[pos] = '\0';
+ hunspell->add(tmp);
+ }
+
+ pos = 0;
+ }
+ else
+ {
+ tmp[pos] = c;
+ pos ++;
+ }
+ }
+ fclose(file);
+ }
+ }
+
+ void appendToCustomDict(const TCHAR *word)
+ {
+ CreatePath(userPath);
+
+ TCHAR filename[1024];
+ mir_sntprintf(filename, MAX_REGS(filename), _T("%s\\%s.cdic"), userPath, language);
+
+ FILE *file = _tfopen(filename, _T("ab"));
+ if (file != NULL)
+ {
+ char tmp[1024];
+ toHunspell(tmp, word, MAX_REGS(tmp));
+ fprintf(file, "%s\n", tmp);
+ fclose(file);
+ }
+ }
+
+ virtual void addWordInternal(const TCHAR * word)
+ {
+ if (loaded != LANGUAGE_LOADED)
+ return;
+
+ char hunspell_word[1024];
+ toHunspell(hunspell_word, word, MAX_REGS(hunspell_word));
+
+ hunspell->add(hunspell_word);
+ }
+
+ void toHunspell(char *hunspellWord, const TCHAR *word, size_t hunspellWordLen)
+ {
+#ifdef UNICODE
+ WideCharToMultiByte(codePage, 0, word, -1, hunspellWord, hunspellWordLen, NULL, NULL);
+#else
+ // TODO
+ strncpy(hunspellWord, word, hunspellWordLen);
+#endif
+ }
+
+ TCHAR * fromHunspell(const char *hunspellWord)
+ {
+#ifdef UNICODE
+ int len = MultiByteToWideChar(codePage, 0, hunspellWord, -1, NULL, 0);
+ WCHAR *ret = (WCHAR *) malloc((len + 1) * sizeof(WCHAR));
+ MultiByteToWideChar(codePage, 0, hunspellWord, -1, ret, len + 1);
+ return ret;
+#else
+ // TODO
+ return strdup(hunspellWord);
+#endif
+ }
+
+ TCHAR * fromHunspellAndFree(char *hunspellWord)
+ {
+ if (hunspellWord == NULL)
+ return NULL;
+
+ TCHAR *ret = fromHunspell(hunspellWord);
+ free(hunspellWord);
+ return ret;
+ }
+
+public:
+ HunspellDictionary(TCHAR *aLanguage, TCHAR *aFileWithoutExtension, TCHAR *anUserPath, TCHAR *aSource)
+ {
+ lstrcpyn(language, aLanguage, MAX_REGS(language));
+ lstrcpyn(fileWithoutExtension, aFileWithoutExtension, MAX_REGS(fileWithoutExtension));
+ lstrcpyn(userPath, anUserPath, MAX_REGS(userPath));
+ if (aSource == NULL)
+ source[0] = _T('\0');
+ else
+ lstrcpyn(source, aSource, MAX_REGS(source));
+
+ loaded = LANGUAGE_NOT_LOADED;
+ localized_name[0] = _T('\0');
+ english_name[0] = _T('\0');
+ full_name[0] = _T('\0');
+ hunspell = NULL;
+ wordChars = NULL;
+ codePage = CP_ACP;
+ autoReplace = NULL;
+ }
+
+ virtual ~HunspellDictionary()
+ {
+ if (hunspell != NULL)
+ delete hunspell;
+ if (wordChars != NULL)
+ free(wordChars);
+ }
+
+ TCHAR * merge(TCHAR * s1, TCHAR *s2)
+ {
+ int len1 = (s1 == NULL ? 0 : lstrlen(s1));
+ int len2 = (s2 == NULL ? 0 : lstrlen(s2));
+
+ TCHAR *ret;
+ if (len1 > 0 && len2 > 0)
+ {
+ ret = (TCHAR *) malloc(sizeof(TCHAR) * (len1 + len2 + 1));
+ lstrcpyn(ret, s1, len1+1);
+ lstrcpyn(&ret[len1], s2, len2+1);
+
+ FREE(s1);
+ FREE(s2);
+ }
+ else if (len1 > 0)
+ {
+ ret = s1;
+ FREE(s2);
+ }
+ else if (len2 > 0)
+ {
+ ret = s2;
+ FREE(s1);
+ }
+ else
+ {
+ ret = (TCHAR *) malloc(sizeof(TCHAR));
+ ret[0] = 0;
+
+ FREE(s1);
+ FREE(s2);
+ }
+
+ // Remove duplicated chars
+ int last = lstrlen(ret) - 1;
+ for(int i = 0; i <= last; i++)
+ {
+ TCHAR c = ret[i];
+ for(int j = last; j > i; j--)
+ {
+ if (c != ret[j])
+ continue;
+ if (j != last)
+ ret[j] = ret[last];
+ ret[last] = _T('\0');
+ last--;
+ }
+ }
+
+ return ret;
+ }
+
+
+ void loadThread()
+ {
+ char dic[1024];
+ char aff[1024];
+
+#ifdef UNICODE
+ mir_snprintf(dic, MAX_REGS(dic), "%S.dic", fileWithoutExtension);
+ mir_snprintf(aff, MAX_REGS(aff), "%S.aff", fileWithoutExtension);
+#else
+ mir_snprintf(dic, MAX_REGS(dic), "%s.dic", fileWithoutExtension);
+ mir_snprintf(aff, MAX_REGS(aff), "%s.aff", fileWithoutExtension);
+#endif
+
+ hunspell = new Hunspell(aff, dic);
+
+ // Get codepage
+ const char *dic_enc = hunspell->get_dic_encoding();
+
+ TCHAR *hwordchars;
+ if (strcmp(dic_enc, "UTF-8") == 0)
+ {
+ codePage = CP_UTF8;
+
+#ifdef UNICODE
+ int wcs_len;
+ hwordchars = fromHunspell((char *) hunspell->get_wordchars_utf16(&wcs_len));
+#else
+ // No option
+ hwordchars = NULL;
+#endif
+ }
+ else
+ {
+ for (int i = 0; i < MAX_REGS(codepages); i++)
+ {
+ if (_strcmpi(codepages[i].name, dic_enc) == 0)
+ {
+ if (IsValidCodePage(codepages[i].codepage))
+ codePage = codepages[i].codepage;
+ break;
+ }
+ }
+
+ hwordchars = fromHunspell(hunspell->get_wordchars());
+ }
+
+ TCHAR *casechars = fromHunspellAndFree(get_casechars(dic_enc));
+ TCHAR *try_string = fromHunspellAndFree(hunspell->get_try_string());
+
+ wordChars = merge(merge(casechars, hwordchars), try_string);
+
+ // Make a suggestion to load hunspell internalls
+ char ** words = NULL;
+ int count = hunspell->suggest(&words, "asdf");
+ for (int i = 0; i < count; i++)
+ free(words[i]);
+ if (words != NULL)
+ free(words);
+
+ loadCustomDict();
+
+ loaded = LANGUAGE_LOADED;
+ }
+
+ // Return TRUE if the word is correct
+ virtual BOOL spell(const TCHAR *word)
+ {
+ load();
+ if (loaded != LANGUAGE_LOADED)
+ return TRUE;
+
+ // TODO Check if it was generated by auto-replacement
+
+ char hunspell_word[1024];
+ toHunspell(hunspell_word, word, MAX_REGS(hunspell_word));
+
+ return hunspell->spell(hunspell_word);
+ }
+
+ // Return a list of suggestions to a word
+ virtual Suggestions suggest(const TCHAR * word)
+ {
+ Suggestions ret = {0};
+
+ load();
+ if (loaded != LANGUAGE_LOADED)
+ return ret;
+
+ char hunspell_word[1024];
+ toHunspell(hunspell_word, word, MAX_REGS(hunspell_word));
+
+ char ** words = NULL;
+ ret.count = hunspell->suggest(&words, hunspell_word);
+
+ if (ret.count > 0)
+ {
+ // Oki, lets make our array
+ ret.words = (TCHAR **) malloc(ret.count * sizeof(TCHAR *));
+ for (unsigned i = 0; i < ret.count; i++)
+ {
+ ret.words[i] = fromHunspell(words[i]);
+ free(words[i]);
+ }
+ }
+
+ if (words != NULL)
+ free(words);
+
+ return ret;
+ }
+
+ // Return a list of auto suggestions to a word
+ virtual Suggestions autoSuggest(const TCHAR * word)
+ {
+ Suggestions ret = {0};
+
+ load();
+ if (loaded != LANGUAGE_LOADED)
+ return ret;
+
+ char hunspell_word[1024];
+ toHunspell(hunspell_word, word, MAX_REGS(hunspell_word));
+
+ char ** words;
+ int count = hunspell->suggest_auto(&words, hunspell_word);
+
+ if (count <= 0)
+ return ret;
+
+ // Oki, lets make our array
+ ret.count = count;
+ ret.words = (TCHAR **) malloc(ret.count * sizeof(TCHAR *));
+ for (int i = 0; i < count; i++)
+ {
+ ret.words[i] = fromHunspell(words[i]);
+ free(words[i]);
+ }
+ free(words);
+
+ return ret;
+ }
+
+ // Return a list of auto suggestions to a word
+ // You have to free the list AND each item
+ virtual TCHAR * autoSuggestOne(const TCHAR * word)
+ {
+ load();
+ if (loaded != LANGUAGE_LOADED)
+ return NULL;
+
+ char hunspell_word[1024];
+ toHunspell(hunspell_word, word, MAX_REGS(hunspell_word));
+
+ char ** words;
+ int count = hunspell->suggest_auto(&words, hunspell_word);
+
+ if (count <= 0)
+ return NULL;
+
+ TCHAR *ret = fromHunspell(words[0]);
+
+ // Oki, lets make our array
+ for (int i = 0; i < count; i++)
+ free(words[i]);
+ free(words);
+
+ return ret;
+ }
+
+ // Return TRUE if the char is a word char
+ virtual BOOL isWordChar(TCHAR c)
+ {
+ if (c == 0)
+ return FALSE;
+
+ load();
+ if (loaded != LANGUAGE_LOADED)
+ return TRUE;
+
+ return _tcschr(wordChars, (_TINT) c) != NULL;
+ }
+
+ // Assert that all needed data is loaded
+ virtual void load()
+ {
+ if (loaded == LANGUAGE_NOT_LOADED)
+ {
+ loaded = LANGUAGE_LOADING;
+
+ DWORD thread_id;
+ CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) LoadThread,
+ (LPVOID) this, 0, &thread_id);
+ }
+ }
+
+ virtual BOOL isLoaded()
+ {
+ return loaded == LANGUAGE_LOADED;
+ }
+
+
+ // Add a word to the user custom dict
+ virtual void addWord(const TCHAR * word)
+ {
+ addWordInternal(word);
+ appendToCustomDict(word);
+ }
+
+ // Add a word to the list of ignored words
+ virtual void ignoreWord(const TCHAR * word)
+ {
+ addWordInternal(word);
+ }
+};
+
+
+DWORD WINAPI LoadThread(LPVOID hd)
+{
+ HunspellDictionary *dict = (HunspellDictionary *) hd;
+ dict->loadThread();
+ return 0;
+}
+
+
+
+// To use with EnumLocalesProc :(
+LIST<Dictionary> *tmp_dicts;
+
+// To get the names of the languages
+BOOL CALLBACK EnumLocalesProc(LPTSTR lpLocaleString)
+{
+ TCHAR *stopped = NULL;
+ USHORT langID = (USHORT) _tcstol(lpLocaleString, &stopped, 16);
+
+ TCHAR ini[32];
+ TCHAR end[32];
+ GetLocaleInfo(MAKELCID(langID, 0), LOCALE_SISO639LANGNAME, ini, MAX_REGS(ini));
+ GetLocaleInfo(MAKELCID(langID, 0), LOCALE_SISO3166CTRYNAME, end, MAX_REGS(end));
+
+ TCHAR name[64];
+ mir_sntprintf(name, MAX_REGS(name), _T("%s_%s"), ini, end);
+
+ for(int i = 0; i < tmp_dicts->getCount(); i++)
+ {
+ Dictionary *dict = (*tmp_dicts)[i];
+ if (lstrcmpi(dict->language, name) == 0)
+ {
+#define LOCALE_SLOCALIZEDLANGUAGENAME 0x0000006f
+#define LOCALE_SNATIVEDISPLAYNAME 0x00000073
+
+ GetLocaleInfo(MAKELCID(langID, 0), LOCALE_SENGLANGUAGE, dict->english_name, MAX_REGS(dict->english_name));
+
+ GetLocaleInfo(MAKELCID(langID, 0), LOCALE_SLANGUAGE, dict->localized_name, MAX_REGS(dict->localized_name));
+ if (dict->localized_name[0] == 0)
+ GetLocaleInfo(MAKELCID(langID, 0), LOCALE_SLOCALIZEDLANGUAGENAME, dict->localized_name, MAX_REGS(dict->localized_name));
+ if (dict->localized_name[0] == 0)
+ GetLocaleInfo(MAKELCID(langID, 0), LOCALE_SNATIVEDISPLAYNAME, dict->localized_name, MAX_REGS(dict->localized_name));
+ if (dict->localized_name[0] == 0 && dict->english_name[0] != 0)
+ {
+ TCHAR country[1024];
+ GetLocaleInfo(MAKELCID(langID, 0), LOCALE_SENGCOUNTRY, country, MAX_REGS(country));
+
+ TCHAR name[1024];
+ if (country[0] != 0)
+ mir_sntprintf(name, MAX_REGS(name), _T("%s (%s)"), dict->english_name, country);
+ else
+ lstrcpyn(name, dict->english_name, MAX_REGS(name));
+
+ lstrcpyn(dict->localized_name, TranslateTS(name), MAX_REGS(dict->localized_name));
+ }
+
+ if (dict->localized_name[0] != 0)
+ {
+ mir_sntprintf(dict->full_name, MAX_REGS(dict->full_name), _T("%s [%s]"), dict->localized_name, dict->language);
+ }
+ break;
+ }
+ }
+ return TRUE;
+}
+
+
+void GetDictsInfo(LIST<Dictionary> &dicts)
+{
+ tmp_dicts = &dicts;
+ EnumSystemLocales(EnumLocalesProc, LCID_SUPPORTED);
+
+ // Try to get name from DB
+ for(int i = 0; i < dicts.getCount(); i++)
+ {
+ Dictionary *dict = dicts[i];
+
+ if (dict->full_name[0] == _T('\0'))
+ {
+ DBVARIANT dbv;
+#ifdef UNICODE
+ char lang[128];
+ WideCharToMultiByte(CP_ACP, 0, dict->language, -1, lang, sizeof(lang), NULL, NULL);
+ if (!DBGetContactSettingTString(NULL, MODULE_NAME, lang, &dbv))
+#else
+ if (!DBGetContactSettingTString(NULL, MODULE_NAME, dict->language, &dbv))
+#endif
+ {
+ lstrcpyn(dict->localized_name, dbv.ptszVal, MAX_REGS(dict->localized_name));
+ DBFreeVariant(&dbv);
+ }
+
+ if (dict->localized_name[0] == _T('\0'))
+ {
+ for(size_t j = 0; j < MAX_REGS(aditionalLanguages); j+=2)
+ {
+ if (lstrcmp(aditionalLanguages[j], dict->language) == 0)
+ {
+ lstrcpyn(dict->localized_name, aditionalLanguages[j+1], MAX_REGS(dict->localized_name));
+ break;
+ }
+ }
+ }
+
+ if (dict->localized_name[0] != _T('\0'))
+ {
+ mir_sntprintf(dict->full_name, MAX_REGS(dict->full_name), _T("%s [%s]"), dict->localized_name, dict->language);
+ }
+ else
+ {
+ lstrcpyn(dict->full_name, dict->language, MAX_REGS(dict->full_name));
+ }
+ }
+ }
+}
+
+
+void GetHunspellDictionariesFromFolder(LIST<Dictionary> &dicts, TCHAR *path, TCHAR *user_path, TCHAR *source)
+{
+ // Load the language files and create an array with then
+ TCHAR file[1024];
+ mir_sntprintf(file, MAX_REGS(file), _T("%s\\*.dic"), path);
+
+ BOOL found = FALSE;
+
+ WIN32_FIND_DATA ffd = {0};
+ HANDLE hFFD = FindFirstFile(file, &ffd);
+ if (hFFD != INVALID_HANDLE_VALUE)
+ {
+ do
+ {
+ mir_sntprintf(file, MAX_REGS(file), _T("%s\\%s"), path, ffd.cFileName);
+
+ // Check .dic
+ DWORD attrib = GetFileAttributes(file);
+ if (attrib == 0xFFFFFFFF || (attrib & FILE_ATTRIBUTE_DIRECTORY))
+ continue;
+
+ // See if .aff exists too
+ lstrcpy(&file[lstrlen(file) - 4], _T(".aff"));
+ attrib = GetFileAttributes(file);
+ if (attrib == 0xFFFFFFFF || (attrib & FILE_ATTRIBUTE_DIRECTORY))
+ continue;
+
+ ffd.cFileName[lstrlen(ffd.cFileName)-4] = _T('\0');
+
+ TCHAR *lang = ffd.cFileName;
+
+ // Replace - for _
+ int i;
+ for(i = 0; i < lstrlen(lang); i++)
+ if (lang[i] == _T('-'))
+ lang[i] = _T('_');
+
+ // Check if dict is new
+ BOOL exists = FALSE;
+ for(i = 0; i < dicts.getCount() && !exists; i++)
+ if (lstrcmp(dicts[i]->language, lang) == 0)
+ exists = TRUE;
+
+ if (!exists)
+ {
+ found = TRUE;
+ file[lstrlen(file) - 4] = _T('\0');
+ dicts.insert(new HunspellDictionary(lang, file, user_path, source));
+ }
+ }
+ while(FindNextFile(hFFD, &ffd));
+
+ FindClose(hFFD);
+ }
+}
+
+
+// Return a list of avaible languages
+void GetAvaibleDictionaries(LIST<Dictionary> &dicts, TCHAR *path, TCHAR *user_path)
+{
+ // Get miranda folder dicts
+ GetHunspellDictionariesFromFolder(dicts, path, user_path, NULL);
+
+ if (opts.use_other_apps_dicts)
+ {
+ TCHAR *otherHunspellApps[] = { _T("Thunderbird"), _T("thunderbird.exe"),
+ _T("Firefox"), _T("firefox.exe") };
+
+#define APPPATH _T("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\%s")
+#define MUICACHE _T("Software\\Microsoft\\Windows\\ShellNoRoam\\MUICache")
+
+ // Get other apps dicts
+ for (int i = 0; i < MAX_REGS(otherHunspellApps); i += 2)
+ {
+ TCHAR key[1024];
+ mir_sntprintf(key, MAX_REGS(key), APPPATH, otherHunspellApps[i+1]);
+
+ HKEY hKey = 0;
+ LONG lResult = 0;
+ if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, key, 0, KEY_QUERY_VALUE, &hKey))
+ {
+ DWORD size = MAX_REGS(key);
+ lResult = RegQueryValueEx(hKey, _T("Path"), NULL, NULL, (LPBYTE)key, &size);
+ RegCloseKey(hKey);
+ }
+ else
+ {
+ // Not found in installed apps - Try MUICache
+ lResult = RegOpenKeyEx(HKEY_CURRENT_USER, MUICACHE, 0, KEY_QUERY_VALUE, &hKey);
+ if (ERROR_SUCCESS == lResult)
+ {
+ DWORD numValues;
+ if (ERROR_SUCCESS != RegQueryInfoKey(hKey, NULL, NULL, NULL, NULL, NULL, NULL, &numValues, NULL, NULL, NULL, NULL))
+ numValues = 0;
+
+ lResult = ERROR_NO_MORE_ITEMS;
+ for (DWORD local = 0; local < numValues; local++)
+ {
+ DWORD cchValue = MAX_REGS(key);
+ if (ERROR_SUCCESS != RegEnumValue(hKey, local, key, &cchValue, NULL, NULL, NULL, NULL))
+ break;
+ key[cchValue] = 0;
+ TCHAR *pos;
+ if (pos = _tcsrchr(key, _T('\\')))
+ {
+ if (lstrcmpi(&pos[1], otherHunspellApps[i+1]) == 0)
+ {
+ pos[0] = 0;
+ lResult = ERROR_SUCCESS;
+ break;
+ }
+ }
+ }
+ RegCloseKey(hKey);
+ }
+ }
+
+ if (ERROR_SUCCESS == lResult)
+ {
+ TCHAR folder[1024];
+ mir_sntprintf(folder, MAX_REGS(folder), _T("%s\\Dictionaries"), key);
+
+ GetHunspellDictionariesFromFolder(languages, folder, user_path, otherHunspellApps[i]);
+ }
+ }
+ }
+
+ GetDictsInfo(dicts);
+
+ // Yeah, yeah, yeah, I know, but this is the easiest way...
+ SortedList *sl = (SortedList *) &dicts;
+
+ // Sort dicts
+ for(int i = 0; i < dicts.getCount(); i++)
+ {
+ for(int j = i + 1; j < dicts.getCount(); j++)
+ {
+ if (lstrcmp(dicts[i]->full_name, dicts[j]->full_name) > 0)
+ {
+ Dictionary *dict = dicts[i];
+ sl->items[i] = dicts[j];
+ sl->items[j] = dict;
+ }
+ }
+ }
+}
+
+
+// Free the list returned by GetAvaibleDictionaries
+void FreeDictionaries(LIST<Dictionary> &dicts)
+{
+ for (int i = 0; i < dicts.getCount(); i++)
+ {
+ delete dicts[i];
+ }
+ dicts.destroy();
+}
+
+
+// Free the list returned by GetAvaibleDictionaries
+void FreeSuggestions(Suggestions &suggestions)
+{
+ for (size_t i = 0; i < suggestions.count; i++)
+ {
+ free(suggestions.words[i]);
+ }
+ free(suggestions.words);
+
+ suggestions.words = NULL;
+ suggestions.count = 0;
+}
diff --git a/Plugins/spellchecker/dictionary.h b/Plugins/spellchecker/dictionary.h
new file mode 100644
index 0000000..b57aa73
--- /dev/null
+++ b/Plugins/spellchecker/dictionary.h
@@ -0,0 +1,86 @@
+/*
+Copyright (C) 2006-2010 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#ifndef __DICTIONARY_H__
+# define __DICTIONARY_H__
+
+
+struct Suggestions {
+ TCHAR ** words;
+ size_t count;
+};
+
+
+// A Dictionary interface
+// All dictionaries use a lazy interface
+class Dictionary {
+public:
+ TCHAR language[128];
+ TCHAR localized_name[128];
+ TCHAR english_name[128];
+ TCHAR full_name[256];
+ TCHAR source[128];
+ AutoReplaceMap *autoReplace;
+
+ virtual ~Dictionary() {}
+
+ // Return TRUE if the word is correct
+ virtual BOOL spell(const TCHAR *) =0;
+
+ // Return a list of suggestions to a word
+ virtual Suggestions suggest(const TCHAR * word) =0;
+
+ // Return a list of auto suggestions to a word
+ virtual Suggestions autoSuggest(const TCHAR * word) =0;
+
+ // Return a auto suggestions to a word
+ // You have to free the item
+ virtual TCHAR * autoSuggestOne(const TCHAR * word) =0;
+
+ // Return TRUE if the char is a word char
+ virtual BOOL isWordChar(TCHAR c) =0;
+
+ // Add a word to the user custom dict
+ virtual void addWord(const TCHAR * word) =0;
+
+ // Add a word to the list of ignored words
+ virtual void ignoreWord(const TCHAR * word) =0;
+
+ // Assert that all needed data is loaded
+ virtual void load() =0;
+
+ // Return TRUE if the dict is fully loaded
+ virtual BOOL isLoaded() =0;
+};
+
+
+
+// Return a list of avaible languages
+void GetAvaibleDictionaries(LIST<Dictionary> &dicts, TCHAR *path, TCHAR *user_path);
+
+// Free the list returned by GetAvaibleDictionaries
+void FreeDictionaries(LIST<Dictionary> &dicts);
+
+// Free the list returned by GetAvaibleDictionaries
+void FreeSuggestions(Suggestions &suggestions);
+
+
+
+#endif // __DICTIONARY_H__
diff --git a/Plugins/spellchecker/flags-angelika.dsp b/Plugins/spellchecker/flags-angelika.dsp
new file mode 100644
index 0000000..58ef9b5
--- /dev/null
+++ b/Plugins/spellchecker/flags-angelika.dsp
@@ -0,0 +1,74 @@
+# Microsoft Developer Studio Project File - Name="flagsAngelika" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=flagsAngelika - Win32 Release
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "flagsAngelika.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "flagsAngelika.mak" CFG="flagsAngelika - Win32 Release"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "flagsAngelika - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Ignore_Export_Lib 1
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /YX /FD /c
+# ADD CPP /nologo /MT /W3 /O1 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# SUBTRACT MTL /Oicf
+# ADD BASE RSC /l 0x416 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386
+# ADD LINK32 /nologo /dll /pdb:none /machine:I386 /nodefaultlib /out:"Flags-Angelika/flags.dll" /NOENTRY
+# Begin Target
+
+# Name "flagsAngelika - Win32 Release"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
+# Begin Source File
+
+SOURCE=.\flags-angelika.rc
+# End Source File
+# End Group
+# End Target
+# End Project
diff --git a/Plugins/spellchecker/flags-angelika.rc b/Plugins/spellchecker/flags-angelika.rc
new file mode 100644
index 0000000..73d9503
--- /dev/null
+++ b/Plugins/spellchecker/flags-angelika.rc
@@ -0,0 +1,118 @@
+af ICON ".\\Flags-Angelika\\af_ZA.ico"
+af_ZA ICON ".\\Flags-Angelika\\af_ZA.ico"
+ar ICON ".\\Flags-Angelika\\ar_AR.ico"
+ar_AR ICON ".\\Flags-Angelika\\ar_AR.ico"
+az ICON ".\\Flags-Angelika\\az_AZ.ico"
+az_AZ ICON ".\\Flags-Angelika\\az_AZ.ico"
+bg ICON ".\\Flags-Angelika\\bg_BG.ico"
+bg_BG ICON ".\\Flags-Angelika\\bg_BG.ico"
+bn_IN ICON ".\\Flags-Angelika\\bn_IN.ico"
+ca_ES ICON ".\\Flags-Angelika\\ca_ES.ico"
+cs ICON ".\\Flags-Angelika\\cs_CZ.ico"
+cs_CZ ICON ".\\Flags-Angelika\\cs_CZ.ico"
+csb_PO ICON ".\\Flags-Angelika\\csb_PO.ico"
+cy ICON ".\\Flags-Angelika\\cy_GB.ico"
+cy_GB ICON ".\\Flags-Angelika\\cy_GB.ico"
+da_DK ICON ".\\Flags-Angelika\\da_DK.ico"
+de ICON ".\\Flags-Angelika\\de_DE.ico"
+de_AT ICON ".\\Flags-Angelika\\de_AT.ico"
+de_CH ICON ".\\Flags-Angelika\\de_CH.ico"
+de_DE ICON ".\\Flags-Angelika\\de_DE.ico"
+el ICON ".\\Flags-Angelika\\el_GR.ico"
+el_GR ICON ".\\Flags-Angelika\\el_GR.ico"
+em_ET ICON ".\\Flags-Angelika\\em_ET.ico"
+en_AU ICON ".\\Flags-Angelika\\en_AU.ico"
+en_CA ICON ".\\Flags-Angelika\\en_CA.ico"
+en_GB ICON ".\\Flags-Angelika\\en_GB.ico"
+en_UK ICON ".\\Flags-Angelika\\en_GB.ico"
+en_NZ ICON ".\\Flags-Angelika\\en_NZ.ico"
+en_US ICON ".\\Flags-Angelika\\en_US.ico"
+en_ZA ICON ".\\Flags-Angelika\\en_ZA.ico"
+es ICON ".\\Flags-Angelika\\es_ES.ico"
+es_ES ICON ".\\Flags-Angelika\\es_ES.ico"
+es_MX ICON ".\\Flags-Angelika\\es_MX.ico"
+et_EE ICON ".\\Flags-Angelika\\et_EE.ico"
+fa_IR ICON ".\\Flags-Angelika\\fa_IR.ico"
+fi ICON ".\\Flags-Angelika\\fi_FI.ico"
+fi_FI ICON ".\\Flags-Angelika\\fi_FI.ico"
+fj ICON ".\\Flags-Angelika\\fj_FJ.ico"
+fj_FJ ICON ".\\Flags-Angelika\\fj_FJ.ico"
+fo ICON ".\\Flags-Angelika\\fo_FO.ico"
+fo_FO ICON ".\\Flags-Angelika\\fo_FO.ico"
+fr ICON ".\\Flags-Angelika\\fr_FR.ico"
+fr_BE ICON ".\\Flags-Angelika\\fr_BE.ico"
+fr_FR ICON ".\\Flags-Angelika\\fr_FR.ico"
+ga_IE ICON ".\\Flags-Angelika\\ga_IE.ico"
+gd_GB ICON ".\\Flags-Angelika\\gd_GB.ico"
+gl_ES ICON ".\\Flags-Angelika\\gl_ES.ico"
+he_IL ICON ".\\Flags-Angelika\\he_IL.ico"
+hi ICON ".\\Flags-Angelika\\hi_IN.ico"
+hi_IN ICON ".\\Flags-Angelika\\hi_IN.ico"
+hr ICON ".\\Flags-Angelika\\hr_Hr.ico"
+hr_Hr ICON ".\\Flags-Angelika\\hr_Hr.ico"
+hu ICON ".\\Flags-Angelika\\hu_HU.ico"
+hu_HU ICON ".\\Flags-Angelika\\hu_HU.ico"
+id ICON ".\\Flags-Angelika\\id_ID.ico"
+id_ID ICON ".\\Flags-Angelika\\id_ID.ico"
+is ICON ".\\Flags-Angelika\\is_IS.ico"
+is_IS ICON ".\\Flags-Angelika\\is_IS.ico"
+it ICON ".\\Flags-Angelika\\it_IT.ico"
+it_IT ICON ".\\Flags-Angelika\\it_IT.ico"
+km_KH ICON ".\\Flags-Angelika\\km_KH.ico"
+ku_TR ICON ".\\Flags-Angelika\\ku_TR.ico"
+lt ICON ".\\Flags-Angelika\\lt_LT.ico"
+lt_LT ICON ".\\Flags-Angelika\\lt_LT.ico"
+lu ICON ".\\Flags-Angelika\\lu_LU.ico"
+lu_LU ICON ".\\Flags-Angelika\\lu_LU.ico"
+lv ICON ".\\Flags-Angelika\\lv_LV.ico"
+lv_LV ICON ".\\Flags-Angelika\\lv_LV.ico"
+mg ICON ".\\Flags-Angelika\\mg_MG.ico"
+mg_MG ICON ".\\Flags-Angelika\\mg_MG.ico"
+mi_NZ ICON ".\\Flags-Angelika\\mi_NZ.ico"
+mn ICON ".\\Flags-Angelika\\mn_MN.ico"
+mn_MN ICON ".\\Flags-Angelika\\mn_MN.ico"
+mo_BF ICON ".\\Flags-Angelika\\mo_BF.ico"
+mr_IN ICON ".\\Flags-Angelika\\mr_IN.ico"
+ms_MY ICON ".\\Flags-Angelika\\ms_MY.ico"
+nb ICON ".\\Flags-Angelika\\nb_NO.ico"
+nb_NO ICON ".\\Flags-Angelika\\nb_NO.ico"
+ne_NP ICON ".\\Flags-Angelika\\ne_NP.ico"
+nl ICON ".\\Flags-Angelika\\nl_NL.ico"
+nl_NL ICON ".\\Flags-Angelika\\nl_NL.ico"
+nn_NO ICON ".\\Flags-Angelika\\nn_NO.ico"
+no ICON ".\\Flags-Angelika\\nb_NO.ico"
+nr_ZA ICON ".\\Flags-Angelika\\nr_ZA.ico"
+ns_ZA ICON ".\\Flags-Angelika\\ns_ZA.ico"
+or_IN ICON ".\\Flags-Angelika\\or_IN.ico"
+pl ICON ".\\Flags-Angelika\\pl_PL.ico"
+pl_PL ICON ".\\Flags-Angelika\\pl_PL.ico"
+pt ICON ".\\Flags-Angelika\\pt_PT.ico"
+pt_BR ICON ".\\Flags-Angelika\\pt_BR.ico"
+pt_PT ICON ".\\Flags-Angelika\\pt_PT.ico"
+qu_BO ICON ".\\Flags-Angelika\\qu_BO.ico"
+ro ICON ".\\Flags-Angelika\\ro_RO.ico"
+ro_RO ICON ".\\Flags-Angelika\\ro_RO.ico"
+ru ICON ".\\Flags-Angelika\\ru_RU.ico"
+ru_RU ICON ".\\Flags-Angelika\\ru_RU.ico"
+rw ICON ".\\Flags-Angelika\\rw_RW.ico"
+rw_RW ICON ".\\Flags-Angelika\\rw_RW.ico"
+sk ICON ".\\Flags-Angelika\\sk_SK.ico"
+sk_SK ICON ".\\Flags-Angelika\\sk_SK.ico"
+sl_SI ICON ".\\Flags-Angelika\\sl_SI.ico"
+ss_ZA ICON ".\\Flags-Angelika\\ss_ZA.ico"
+st_ZA ICON ".\\Flags-Angelika\\st_ZA.ico"
+sv ICON ".\\Flags-Angelika\\sv_SE.ico"
+sv_SE ICON ".\\Flags-Angelika\\sv_SE.ico"
+ta_IN ICON ".\\Flags-Angelika\\ta_IN.ico"
+tet_ID ICON ".\\Flags-Angelika\\tet_ID.ico"
+tl_PH ICON ".\\Flags-Angelika\\tl_PH.ico"
+tn_ZA ICON ".\\Flags-Angelika\\tn_ZA.ico"
+ts_ZA ICON ".\\Flags-Angelika\\ts_ZA.ico"
+uk_UA ICON ".\\Flags-Angelika\\uk_UA.ico"
+uz ICON ".\\Flags-Angelika\\uz_UZ.ico"
+uz_UZ ICON ".\\Flags-Angelika\\uz_UZ.ico"
+ve_ZA ICON ".\\Flags-Angelika\\ve_ZA.ico"
+vi ICON ".\\Flags-Angelika\\vi_VI.ico"
+vi_VI ICON ".\\Flags-Angelika\\vi_VI.ico"
+xh_ZA ICON ".\\Flags-Angelika\\xh_ZA.ico"
+zu_ZA ICON ".\\Flags-Angelika\\zu_ZA.ico"
diff --git a/Plugins/spellchecker/flags-angelika.sln b/Plugins/spellchecker/flags-angelika.sln
new file mode 100644
index 0000000..b27be33
--- /dev/null
+++ b/Plugins/spellchecker/flags-angelika.sln
@@ -0,0 +1,40 @@
+
+Microsoft Visual Studio Solution File, Format Version 9.00
+# Visual Studio 2005
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "flagsAngelika", "flags-angelika.vcproj", "{3096A0CE-0CC1-42BC-B19C-BE0985B4CDC5}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Release|Win32 = Release|Win32
+ Release|x64 = Release|x64
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {3096A0CE-0CC1-42BC-B19C-BE0985B4CDC5}.Release|Win32.ActiveCfg = Release|Win32
+ {3096A0CE-0CC1-42BC-B19C-BE0985B4CDC5}.Release|Win32.Build.0 = Release|Win32
+ {3096A0CE-0CC1-42BC-B19C-BE0985B4CDC5}.Release|x64.ActiveCfg = Release|x64
+ {3096A0CE-0CC1-42BC-B19C-BE0985B4CDC5}.Release|x64.Build.0 = Release|x64
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
+
+Microsoft Visual Studio Solution File, Format Version 9.00
+# Visual Studio 2005
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "flagsAngelika", "flags-angelika.vcproj", "{3096A0CE-0CC1-42BC-B19C-BE0985B4CDC5}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Release|Win32 = Release|Win32
+ Release|x64 = Release|x64
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {3096A0CE-0CC1-42BC-B19C-BE0985B4CDC5}.Release|Win32.ActiveCfg = Release|Win32
+ {3096A0CE-0CC1-42BC-B19C-BE0985B4CDC5}.Release|Win32.Build.0 = Release|Win32
+ {3096A0CE-0CC1-42BC-B19C-BE0985B4CDC5}.Release|x64.ActiveCfg = Release|x64
+ {3096A0CE-0CC1-42BC-B19C-BE0985B4CDC5}.Release|x64.Build.0 = Release|x64
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/Plugins/spellchecker/flags-angelika.vcproj b/Plugins/spellchecker/flags-angelika.vcproj
new file mode 100644
index 0000000..e6860f5
--- /dev/null
+++ b/Plugins/spellchecker/flags-angelika.vcproj
@@ -0,0 +1,508 @@
+<?xml version="1.0" encoding="windows-1250"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="8,00"
+ Name="flagsAngelika"
+ ProjectGUID="{3096A0CE-0CC1-42BC-B19C-BE0985B4CDC5}"
+ RootNamespace="flagsAngelika"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ <Platform
+ Name="x64"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory=".\Release"
+ IntermediateDirectory=".\Release"
+ ConfigurationType="2"
+ InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC60.vsprops"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="2"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ PreprocessorDefinitions="NDEBUG"
+ MkTypLibCompatible="true"
+ SuppressStartupBanner="true"
+ TargetEnvironment="1"
+ TypeLibraryName=".\Release/flags-angelika.tlb"
+ HeaderFileName=""
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="1"
+ InlineFunctionExpansion="1"
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL"
+ StringPooling="true"
+ RuntimeLibrary="0"
+ EnableFunctionLevelLinking="true"
+ PrecompiledHeaderFile=".\Release/flags-angelika.pch"
+ AssemblerListingLocation=".\Release/"
+ ObjectFile=".\Release/"
+ ProgramDataBaseFileName=".\Release/"
+ WarningLevel="3"
+ SuppressStartupBanner="true"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="NDEBUG"
+ Culture="1033"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ IgnoreImportLibrary="true"
+ AdditionalDependencies="odbc32.lib odbccp32.lib"
+ OutputFile="Flags-Angelika/flags.dll"
+ LinkIncremental="1"
+ SuppressStartupBanner="true"
+ IgnoreAllDefaultLibraries="true"
+ ResourceOnlyDLL="true"
+ ImportLibrary=".\Release/flags.lib"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ SuppressStartupBanner="true"
+ OutputFile=".\Release/flags-angelika.bsc"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|x64"
+ OutputDirectory=".\Release64"
+ IntermediateDirectory=".\Release64"
+ ConfigurationType="2"
+ InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC60.vsprops"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="2"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ PreprocessorDefinitions="NDEBUG"
+ MkTypLibCompatible="true"
+ SuppressStartupBanner="true"
+ TargetEnvironment="3"
+ TypeLibraryName=".\Release/flags-angelika.tlb"
+ HeaderFileName=""
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="1"
+ InlineFunctionExpansion="1"
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL"
+ StringPooling="true"
+ RuntimeLibrary="0"
+ EnableFunctionLevelLinking="true"
+ PrecompiledHeaderFile=".\Release/flags-angelika.pch"
+ AssemblerListingLocation=".\Release/"
+ ObjectFile=".\Release/"
+ ProgramDataBaseFileName=".\Release/"
+ WarningLevel="3"
+ SuppressStartupBanner="true"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="NDEBUG"
+ Culture="1033"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ IgnoreImportLibrary="true"
+ AdditionalDependencies="odbc32.lib odbccp32.lib"
+ OutputFile="Flags-Angelika64/flags.dll"
+ LinkIncremental="1"
+ SuppressStartupBanner="true"
+ IgnoreAllDefaultLibraries="true"
+ ResourceOnlyDLL="true"
+ ImportLibrary=".\Release64/flags.lib"
+ TargetMachine="17"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ SuppressStartupBanner="true"
+ OutputFile=".\Release64/flags-angelika.bsc"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Resource Files"
+ Filter="ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
+ >
+ <File
+ RelativePath="flags-angelika.rc"
+ >
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|x64"
+ >
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ </File>
+ </Filter>
+ <Filter
+ Name="Source Files"
+ Filter="cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+ >
+ </Filter>
+ <Filter
+ Name="Header Files"
+ Filter="h;hpp;hxx;hm;inl"
+ >
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
+<?xml version="1.0" encoding="windows-1250"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="8,00"
+ Name="flagsAngelika"
+ ProjectGUID="{3096A0CE-0CC1-42BC-B19C-BE0985B4CDC5}"
+ RootNamespace="flagsAngelika"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ <Platform
+ Name="x64"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory=".\Release"
+ IntermediateDirectory=".\Release"
+ ConfigurationType="2"
+ InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC60.vsprops"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="2"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ PreprocessorDefinitions="NDEBUG"
+ MkTypLibCompatible="true"
+ SuppressStartupBanner="true"
+ TargetEnvironment="1"
+ TypeLibraryName=".\Release/flags-angelika.tlb"
+ HeaderFileName=""
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="1"
+ InlineFunctionExpansion="1"
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL"
+ StringPooling="true"
+ RuntimeLibrary="0"
+ EnableFunctionLevelLinking="true"
+ PrecompiledHeaderFile=".\Release/flags-angelika.pch"
+ AssemblerListingLocation=".\Release/"
+ ObjectFile=".\Release/"
+ ProgramDataBaseFileName=".\Release/"
+ WarningLevel="3"
+ SuppressStartupBanner="true"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="NDEBUG"
+ Culture="1033"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ IgnoreImportLibrary="true"
+ AdditionalDependencies="odbc32.lib odbccp32.lib"
+ OutputFile="Flags-Angelika/flags.dll"
+ LinkIncremental="1"
+ SuppressStartupBanner="true"
+ IgnoreAllDefaultLibraries="true"
+ ResourceOnlyDLL="true"
+ ImportLibrary=".\Release/flags.lib"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ SuppressStartupBanner="true"
+ OutputFile=".\Release/flags-angelika.bsc"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|x64"
+ OutputDirectory=".\Release64"
+ IntermediateDirectory=".\Release64"
+ ConfigurationType="2"
+ InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC60.vsprops"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="2"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ PreprocessorDefinitions="NDEBUG"
+ MkTypLibCompatible="true"
+ SuppressStartupBanner="true"
+ TargetEnvironment="3"
+ TypeLibraryName=".\Release/flags-angelika.tlb"
+ HeaderFileName=""
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="1"
+ InlineFunctionExpansion="1"
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL"
+ StringPooling="true"
+ RuntimeLibrary="0"
+ EnableFunctionLevelLinking="true"
+ PrecompiledHeaderFile=".\Release/flags-angelika.pch"
+ AssemblerListingLocation=".\Release/"
+ ObjectFile=".\Release/"
+ ProgramDataBaseFileName=".\Release/"
+ WarningLevel="3"
+ SuppressStartupBanner="true"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="NDEBUG"
+ Culture="1033"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ IgnoreImportLibrary="true"
+ AdditionalDependencies="odbc32.lib odbccp32.lib"
+ OutputFile="Flags-Angelika64/flags.dll"
+ LinkIncremental="1"
+ SuppressStartupBanner="true"
+ IgnoreAllDefaultLibraries="true"
+ ResourceOnlyDLL="true"
+ ImportLibrary=".\Release64/flags.lib"
+ TargetMachine="17"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ SuppressStartupBanner="true"
+ OutputFile=".\Release64/flags-angelika.bsc"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Resource Files"
+ Filter="ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
+ >
+ <File
+ RelativePath="flags-angelika.rc"
+ >
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|x64"
+ >
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ </File>
+ </Filter>
+ <Filter
+ Name="Source Files"
+ Filter="cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+ >
+ </Filter>
+ <Filter
+ Name="Header Files"
+ Filter="h;hpp;hxx;hm;inl"
+ >
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/Plugins/spellchecker/flags.dsp b/Plugins/spellchecker/flags.dsp
new file mode 100644
index 0000000..bf7701c
--- /dev/null
+++ b/Plugins/spellchecker/flags.dsp
@@ -0,0 +1,74 @@
+# Microsoft Developer Studio Project File - Name="flags" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=flags - Win32 Release
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "flags.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "flags.mak" CFG="flags - Win32 Release"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "flags - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Ignore_Export_Lib 1
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /YX /FD /c
+# ADD CPP /nologo /MT /W3 /O1 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# SUBTRACT MTL /Oicf
+# ADD BASE RSC /l 0x416 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386
+# ADD LINK32 /nologo /dll /pdb:none /machine:I386 /nodefaultlib /out:"Flags/flags.dll" /NOENTRY
+# Begin Target
+
+# Name "flags - Win32 Release"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
+# Begin Source File
+
+SOURCE=.\flags.rc
+# End Source File
+# End Group
+# End Target
+# End Project
diff --git a/Plugins/spellchecker/flags.rc b/Plugins/spellchecker/flags.rc
new file mode 100644
index 0000000..402f461
--- /dev/null
+++ b/Plugins/spellchecker/flags.rc
@@ -0,0 +1,119 @@
+af ICON ".\\Flags\\af_ZA.ico"
+af_ZA ICON ".\\Flags\\af_ZA.ico"
+az ICON ".\\Flags\\az_AZ.ico"
+az_AZ ICON ".\\Flags\\az_AZ.ico"
+bg ICON ".\\Flags\\bg_BG.ico"
+bg_BG ICON ".\\Flags\\bg_BG.ico"
+bn_IN ICON ".\\Flags\\bn_IN.ico"
+cs ICON ".\\Flags\\cs_CZ.ico"
+cs_CZ ICON ".\\Flags\\cs_CZ.ico"
+cy ICON ".\\Flags\\cy_GB.ico"
+cy_GB ICON ".\\Flags\\cy_GB.ico"
+da_DK ICON ".\\Flags\\da_DK.ico"
+de ICON ".\\Flags\\de_DE.ico"
+de_AT ICON ".\\Flags\\de_AT.ico"
+de_CH ICON ".\\Flags\\de_CH.ico"
+de_DE ICON ".\\Flags\\de_DE.ico"
+el ICON ".\\Flags\\el_GR.ico"
+el_GR ICON ".\\Flags\\el_GR.ico"
+em_ET ICON ".\\Flags\\em_ET.ico"
+en_AU ICON ".\\Flags\\en_AU.ico"
+en_CA ICON ".\\Flags\\en_CA.ico"
+en_GB ICON ".\\Flags\\en_GB.ico"
+en_UK ICON ".\\Flags\\en_GB.ico"
+en_NZ ICON ".\\Flags\\en_NZ.ico"
+en_US ICON ".\\Flags\\en_US.ico"
+en_ZA ICON ".\\Flags\\en_ZA.ico"
+es ICON ".\\Flags\\es_ES.ico"
+es_ES ICON ".\\Flags\\es_ES.ico"
+es_MX ICON ".\\Flags\\es_MX.ico"
+et_EE ICON ".\\Flags\\et_EE.ico"
+fa_IR ICON ".\\Flags\\fa_IR.ico"
+fi ICON ".\\Flags\\fi_FI.ico"
+fi_FI ICON ".\\Flags\\fi_FI.ico"
+fj ICON ".\\Flags\\fj_FJ.ico"
+fj_FJ ICON ".\\Flags\\fj_FJ.ico"
+fo ICON ".\\Flags\\fo_FO.ico"
+fo_FO ICON ".\\Flags\\fo_FO.ico"
+fr ICON ".\\Flags\\fr_FR.ico"
+fr_BE ICON ".\\Flags\\fr_BE.ico"
+fr_FR ICON ".\\Flags\\fr_FR.ico"
+ga_IE ICON ".\\Flags\\ga_IE.ico"
+gd_GB ICON ".\\Flags\\gd_GB.ico"
+he_IL ICON ".\\Flags\\he_IL.ico"
+hi ICON ".\\Flags\\hi_IN.ico"
+hi_IN ICON ".\\Flags\\hi_IN.ico"
+hr ICON ".\\Flags\\hr_HR.ico"
+hr_HR ICON ".\\Flags\\hr_HR.ico"
+hu ICON ".\\Flags\\hu_HU.ico"
+hu_HU ICON ".\\Flags\\hu_HU.ico"
+id ICON ".\\Flags\\id_ID.ico"
+id_ID ICON ".\\Flags\\id_ID.ico"
+is ICON ".\\Flags\\is_IS.ico"
+is_IS ICON ".\\Flags\\is_IS.ico"
+it ICON ".\\Flags\\it_IT.ico"
+it_IT ICON ".\\Flags\\it_IT.ico"
+km_KH ICON ".\\Flags\\km_KH.ico"
+ku_TR ICON ".\\Flags\\ku_TR.ico"
+lt ICON ".\\Flags\\lt_LT.ico"
+lt_LT ICON ".\\Flags\\lt_LT.ico"
+lu ICON ".\\Flags\\lu_LU.ico"
+lu_LU ICON ".\\Flags\\lu_LU.ico"
+lv ICON ".\\Flags\\lv_LV.ico"
+lv_LV ICON ".\\Flags\\lv_LV.ico"
+mg ICON ".\\Flags\\mg_MG.ico"
+mg_MG ICON ".\\Flags\\mg_MG.ico"
+mi_NZ ICON ".\\Flags\\mi_NZ.ico"
+mn ICON ".\\Flags\\mn_MN.ico"
+mn_MN ICON ".\\Flags\\mn_MN.ico"
+mo_BF ICON ".\\Flags\\mo_BF.ico"
+mr_IN ICON ".\\Flags\\mr_IN.ico"
+ms_MY ICON ".\\Flags\\ms_MY.ico"
+nb ICON ".\\Flags\\nb_NO.ico"
+nb_NO ICON ".\\Flags\\nb_NO.ico"
+ne_NP ICON ".\\Flags\\ne_NP.ico"
+nl ICON ".\\Flags\\nl_NL.ico"
+nl_NL ICON ".\\Flags\\nl_NL.ico"
+nn_NO ICON ".\\Flags\\nn_NO.ico"
+no ICON ".\\Flags\\nb_NO.ico"
+nr_ZA ICON ".\\Flags\\nr_ZA.ico"
+ns_ZA ICON ".\\Flags\\ns_ZA.ico"
+ny_MW ICON ".\\Flags\\ny_MW.ico"
+or_IN ICON ".\\Flags\\or_IN.ico"
+pl ICON ".\\Flags\\pl_PL.ico"
+pl_PL ICON ".\\Flags\\pl_PL.ico"
+pt ICON ".\\Flags\\pt_PT.ico"
+pt_BR ICON ".\\Flags\\pt_BR.ico"
+pt_PT ICON ".\\Flags\\pt_PT.ico"
+qu_BO ICON ".\\Flags\\qu_BO.ico"
+ro ICON ".\\Flags\\ro_RO.ico"
+ro_BO ICON ".\\Flags\\ro_BO.ico"
+ro_RO ICON ".\\Flags\\ro_RO.ico"
+ru ICON ".\\Flags\\ru_RU.ico"
+ru_RU ICON ".\\Flags\\ru_RU.ico"
+rw ICON ".\\Flags\\rw_RW.ico"
+rw_RW ICON ".\\Flags\\rw_RW.ico"
+sk ICON ".\\Flags\\sk_SK.ico"
+sk_SK ICON ".\\Flags\\sk_SK.ico"
+sl_SI ICON ".\\Flags\\sl_SI.ico"
+sr_CS ICON ".\\Flags\\sr_CS.ico"
+ss_ZA ICON ".\\Flags\\ss_ZA.ico"
+st_ZA ICON ".\\Flags\\st_ZA.ico"
+sv ICON ".\\Flags\\sv_SE.ico"
+sv_SE ICON ".\\Flags\\sv_SE.ico"
+tet_ID ICON ".\\Flags\\tet_ID.ico"
+tl_PH ICON ".\\Flags\\tl_PH.ico"
+tn_ZA ICON ".\\Flags\\tn_ZA.ico"
+ts_ZA ICON ".\\Flags\\ts_ZA.ico"
+uk_UA ICON ".\\Flags\\uk_UA.ico"
+uz ICON ".\\Flags\\uz_UZ.ico"
+uz_UZ ICON ".\\Flags\\uz_UZ.ico"
+ve_ZA ICON ".\\Flags\\ve_ZA.ico"
+vi ICON ".\\Flags\\vi_VI.ico"
+vi_VI ICON ".\\Flags\\vi_VI.ico"
+xh_ZA ICON ".\\Flags\\xh_ZA.ico"
+zu_ZA ICON ".\\Flags\\zu_ZA.ico"
+mk ICON ".\\Flags\\mk.ico"
+bs ICON ".\\Flags\\ba.ico"
+zh ICON ".\\Flags\\cn.ico"
+en ICON ".\\Flags\\en.ico"
diff --git a/Plugins/spellchecker/flags.sln b/Plugins/spellchecker/flags.sln
new file mode 100644
index 0000000..f9c9712
--- /dev/null
+++ b/Plugins/spellchecker/flags.sln
@@ -0,0 +1,40 @@
+
+Microsoft Visual Studio Solution File, Format Version 9.00
+# Visual Studio 2005
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "flags", "flags.vcproj", "{3B8B2642-716E-437C-B341-279B08FDC2C0}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Release|Win32 = Release|Win32
+ Release|x64 = Release|x64
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {3B8B2642-716E-437C-B341-279B08FDC2C0}.Release|Win32.ActiveCfg = Release|Win32
+ {3B8B2642-716E-437C-B341-279B08FDC2C0}.Release|Win32.Build.0 = Release|Win32
+ {3B8B2642-716E-437C-B341-279B08FDC2C0}.Release|x64.ActiveCfg = Release|x64
+ {3B8B2642-716E-437C-B341-279B08FDC2C0}.Release|x64.Build.0 = Release|x64
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
+
+Microsoft Visual Studio Solution File, Format Version 9.00
+# Visual Studio 2005
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "flags", "flags.vcproj", "{3B8B2642-716E-437C-B341-279B08FDC2C0}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Release|Win32 = Release|Win32
+ Release|x64 = Release|x64
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {3B8B2642-716E-437C-B341-279B08FDC2C0}.Release|Win32.ActiveCfg = Release|Win32
+ {3B8B2642-716E-437C-B341-279B08FDC2C0}.Release|Win32.Build.0 = Release|Win32
+ {3B8B2642-716E-437C-B341-279B08FDC2C0}.Release|x64.ActiveCfg = Release|x64
+ {3B8B2642-716E-437C-B341-279B08FDC2C0}.Release|x64.Build.0 = Release|x64
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/Plugins/spellchecker/flags.vcproj b/Plugins/spellchecker/flags.vcproj
new file mode 100644
index 0000000..630bab9
--- /dev/null
+++ b/Plugins/spellchecker/flags.vcproj
@@ -0,0 +1,508 @@
+<?xml version="1.0" encoding="windows-1250"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="8,00"
+ Name="flags"
+ ProjectGUID="{3B8B2642-716E-437C-B341-279B08FDC2C0}"
+ RootNamespace="flags"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ <Platform
+ Name="x64"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory=".\Release"
+ IntermediateDirectory=".\Release"
+ ConfigurationType="2"
+ InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC60.vsprops"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="2"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ PreprocessorDefinitions="NDEBUG"
+ MkTypLibCompatible="true"
+ SuppressStartupBanner="true"
+ TargetEnvironment="1"
+ TypeLibraryName=".\Release/flags.tlb"
+ HeaderFileName=""
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="1"
+ InlineFunctionExpansion="1"
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL"
+ StringPooling="true"
+ RuntimeLibrary="0"
+ EnableFunctionLevelLinking="true"
+ PrecompiledHeaderFile=".\Release/flags.pch"
+ AssemblerListingLocation=".\Release/"
+ ObjectFile=".\Release/"
+ ProgramDataBaseFileName=".\Release/"
+ WarningLevel="3"
+ SuppressStartupBanner="true"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="NDEBUG"
+ Culture="1033"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ IgnoreImportLibrary="true"
+ AdditionalDependencies="odbc32.lib odbccp32.lib"
+ OutputFile="Flags/flags.dll"
+ LinkIncremental="1"
+ SuppressStartupBanner="true"
+ IgnoreAllDefaultLibraries="true"
+ ResourceOnlyDLL="true"
+ ImportLibrary=".\Release/flags.lib"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ SuppressStartupBanner="true"
+ OutputFile=".\Release/flags.bsc"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|x64"
+ OutputDirectory=".\Release64"
+ IntermediateDirectory=".\Release64"
+ ConfigurationType="2"
+ InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC60.vsprops"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="2"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ PreprocessorDefinitions="NDEBUG"
+ MkTypLibCompatible="true"
+ SuppressStartupBanner="true"
+ TargetEnvironment="3"
+ TypeLibraryName=".\Release/flags.tlb"
+ HeaderFileName=""
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="1"
+ InlineFunctionExpansion="1"
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL"
+ StringPooling="true"
+ RuntimeLibrary="0"
+ EnableFunctionLevelLinking="true"
+ PrecompiledHeaderFile=".\Release/flags.pch"
+ AssemblerListingLocation=".\Release/"
+ ObjectFile=".\Release/"
+ ProgramDataBaseFileName=".\Release/"
+ WarningLevel="3"
+ SuppressStartupBanner="true"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="NDEBUG"
+ Culture="1033"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ IgnoreImportLibrary="true"
+ AdditionalDependencies="odbc32.lib odbccp32.lib"
+ OutputFile="Flags64/flags.dll"
+ LinkIncremental="1"
+ SuppressStartupBanner="true"
+ IgnoreAllDefaultLibraries="true"
+ ResourceOnlyDLL="true"
+ ImportLibrary=".\Release64/flags.lib"
+ TargetMachine="17"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ SuppressStartupBanner="true"
+ OutputFile=".\Release64/flags.bsc"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Resource Files"
+ Filter="ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
+ >
+ <File
+ RelativePath="flags.rc"
+ >
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|x64"
+ >
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ </File>
+ </Filter>
+ <Filter
+ Name="Source Files"
+ Filter="cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+ >
+ </Filter>
+ <Filter
+ Name="Header Files"
+ Filter="h;hpp;hxx;hm;inl"
+ >
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
+<?xml version="1.0" encoding="windows-1250"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="8,00"
+ Name="flags"
+ ProjectGUID="{3B8B2642-716E-437C-B341-279B08FDC2C0}"
+ RootNamespace="flags"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ <Platform
+ Name="x64"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory=".\Release"
+ IntermediateDirectory=".\Release"
+ ConfigurationType="2"
+ InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC60.vsprops"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="2"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ PreprocessorDefinitions="NDEBUG"
+ MkTypLibCompatible="true"
+ SuppressStartupBanner="true"
+ TargetEnvironment="1"
+ TypeLibraryName=".\Release/flags.tlb"
+ HeaderFileName=""
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="1"
+ InlineFunctionExpansion="1"
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL"
+ StringPooling="true"
+ RuntimeLibrary="0"
+ EnableFunctionLevelLinking="true"
+ PrecompiledHeaderFile=".\Release/flags.pch"
+ AssemblerListingLocation=".\Release/"
+ ObjectFile=".\Release/"
+ ProgramDataBaseFileName=".\Release/"
+ WarningLevel="3"
+ SuppressStartupBanner="true"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="NDEBUG"
+ Culture="1033"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ IgnoreImportLibrary="true"
+ AdditionalDependencies="odbc32.lib odbccp32.lib"
+ OutputFile="Flags/flags.dll"
+ LinkIncremental="1"
+ SuppressStartupBanner="true"
+ IgnoreAllDefaultLibraries="true"
+ ResourceOnlyDLL="true"
+ ImportLibrary=".\Release/flags.lib"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ SuppressStartupBanner="true"
+ OutputFile=".\Release/flags.bsc"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|x64"
+ OutputDirectory=".\Release64"
+ IntermediateDirectory=".\Release64"
+ ConfigurationType="2"
+ InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC60.vsprops"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="2"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ PreprocessorDefinitions="NDEBUG"
+ MkTypLibCompatible="true"
+ SuppressStartupBanner="true"
+ TargetEnvironment="3"
+ TypeLibraryName=".\Release/flags.tlb"
+ HeaderFileName=""
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="1"
+ InlineFunctionExpansion="1"
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL"
+ StringPooling="true"
+ RuntimeLibrary="0"
+ EnableFunctionLevelLinking="true"
+ PrecompiledHeaderFile=".\Release/flags.pch"
+ AssemblerListingLocation=".\Release/"
+ ObjectFile=".\Release/"
+ ProgramDataBaseFileName=".\Release/"
+ WarningLevel="3"
+ SuppressStartupBanner="true"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="NDEBUG"
+ Culture="1033"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ IgnoreImportLibrary="true"
+ AdditionalDependencies="odbc32.lib odbccp32.lib"
+ OutputFile="Flags64/flags.dll"
+ LinkIncremental="1"
+ SuppressStartupBanner="true"
+ IgnoreAllDefaultLibraries="true"
+ ResourceOnlyDLL="true"
+ ImportLibrary=".\Release64/flags.lib"
+ TargetMachine="17"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ SuppressStartupBanner="true"
+ OutputFile=".\Release64/flags.bsc"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Resource Files"
+ Filter="ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
+ >
+ <File
+ RelativePath="flags.rc"
+ >
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|x64"
+ >
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ </File>
+ </Filter>
+ <Filter
+ Name="Source Files"
+ Filter="cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+ >
+ </Filter>
+ <Filter
+ Name="Header Files"
+ Filter="h;hpp;hxx;hm;inl"
+ >
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/Plugins/spellchecker/hunspell/README b/Plugins/spellchecker/hunspell/README
new file mode 100644
index 0000000..b452096
--- /dev/null
+++ b/Plugins/spellchecker/hunspell/README
@@ -0,0 +1,21 @@
+Hunspell spell checker and morphological analyser library
+
+Documentation, tests, examples: http://hunspell.sourceforge.net
+
+Author of Hunspell:
+László Németh (nemethl (at) gyorsposta.hu)
+
+Hunspell based on OpenOffice.org's Myspell. MySpell's author:
+Kevin Hendricks (kevin.hendricks (at) sympatico.ca)
+
+License: GPL 2.0/LGPL 2.1/MPL 1.1 tri-license
+
+The contents of this library may be used under the terms of
+the GNU General Public License Version 2 or later (the "GPL"), or
+the GNU Lesser General Public License Version 2.1 or later (the "LGPL",
+see http://gnu.org/copyleft/lesser.html) or the Mozilla Public License
+Version 1.1 or later (the "MPL", see http://mozilla.org/MPL/MPL-1.1.html).
+
+Software distributed under these licenses is distributed on an "AS IS" basis,
+WITHOUT WARRANTY OF ANY KIND, either express or implied. See the licences
+for the specific language governing rights and limitations under the licenses.
diff --git a/Plugins/spellchecker/hunspell/affentry.cxx b/Plugins/spellchecker/hunspell/affentry.cxx
new file mode 100644
index 0000000..fef0cca
--- /dev/null
+++ b/Plugins/spellchecker/hunspell/affentry.cxx
@@ -0,0 +1,962 @@
+#include "license.hunspell"
+#include "license.myspell"
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <ctype.h>
+
+#include "affentry.hxx"
+#include "csutil.hxx"
+
+PfxEntry::PfxEntry(AffixMgr* pmgr, affentry* dp)
+{
+ // register affix manager
+ pmyMgr = pmgr;
+
+ // set up its initial values
+
+ aflag = dp->aflag; // flag
+ strip = dp->strip; // string to strip
+ appnd = dp->appnd; // string to append
+ stripl = dp->stripl; // length of strip string
+ appndl = dp->appndl; // length of append string
+ numconds = dp->numconds; // length of the condition
+ opts = dp->opts; // cross product flag
+ // then copy over all of the conditions
+ if (opts & aeLONGCOND) {
+ memcpy(c.conds, dp->c.l.conds1, MAXCONDLEN_1);
+ c.l.conds2 = dp->c.l.conds2;
+ } else memcpy(c.conds, dp->c.conds, MAXCONDLEN);
+ next = NULL;
+ nextne = NULL;
+ nexteq = NULL;
+ morphcode = dp->morphcode;
+ contclass = dp->contclass;
+ contclasslen = dp->contclasslen;
+}
+
+
+PfxEntry::~PfxEntry()
+{
+ aflag = 0;
+ if (appnd) free(appnd);
+ if (strip) free(strip);
+ pmyMgr = NULL;
+ appnd = NULL;
+ strip = NULL;
+ if (opts & aeLONGCOND) free(c.l.conds2);
+ if (morphcode && !(opts & aeALIASM)) free(morphcode);
+ if (contclass && !(opts & aeALIASF)) free(contclass);
+}
+
+// add prefix to this word assuming conditions hold
+char * PfxEntry::add(const char * word, int len)
+{
+ char tword[MAXWORDUTF8LEN + 4];
+
+ if ((len > stripl || (len == 0 && pmyMgr->get_fullstrip())) &&
+ (len >= numconds) && test_condition(word) &&
+ (!stripl || (strncmp(word, strip, stripl) == 0)) &&
+ ((MAXWORDUTF8LEN + 4) > (len + appndl - stripl))) {
+ /* we have a match so add prefix */
+ char * pp = tword;
+ if (appndl) {
+ strcpy(tword,appnd);
+ pp += appndl;
+ }
+ strcpy(pp, (word + stripl));
+ return mystrdup(tword);
+ }
+ return NULL;
+}
+
+inline char * PfxEntry::nextchar(char * p) {
+ if (p) {
+ p++;
+ if (opts & aeLONGCOND) {
+ // jump to the 2nd part of the condition
+ if (p == c.conds + MAXCONDLEN_1) return c.l.conds2;
+ // end of the MAXCONDLEN length condition
+ } else if (p == c.conds + MAXCONDLEN) return NULL;
+ return *p ? p : NULL;
+ }
+ return NULL;
+}
+
+inline int PfxEntry::test_condition(const char * st)
+{
+ const char * pos = NULL; // group with pos input position
+ bool neg = false; // complementer
+ bool ingroup = false; // character in the group
+ if (numconds == 0) return 1;
+ char * p = c.conds;
+ while (1) {
+ switch (*p) {
+ case '\0': return 1;
+ case '[': {
+ neg = false;
+ ingroup = false;
+ p = nextchar(p);
+ pos = st; break;
+ }
+ case '^': { p = nextchar(p); neg = true; break; }
+ case ']': {
+ if ((neg && ingroup) || (!neg && !ingroup)) return 0;
+ pos = NULL;
+ p = nextchar(p);
+ // skip the next character
+ if (!ingroup && *st) for (st++; (opts & aeUTF8) && (*st & 0xc0) == 0x80; st++);
+ if (*st == '\0' && p) return 0; // word <= condition
+ break;
+ }
+ case '.': if (!pos) { // dots are not metacharacters in groups: [.]
+ p = nextchar(p);
+ // skip the next character
+ for (st++; (opts & aeUTF8) && (*st & 0xc0) == 0x80; st++);
+ if (*st == '\0' && p) return 0; // word <= condition
+ break;
+ }
+ default: {
+ if (*st == *p) {
+ st++;
+ p = nextchar(p);
+ if ((opts & aeUTF8) && (*(st - 1) & 0x80)) { // multibyte
+ while (p && (*p & 0xc0) == 0x80) { // character
+ if (*p != *st) {
+ if (!pos) return 0;
+ st = pos;
+ break;
+ }
+ p = nextchar(p);
+ st++;
+ }
+ if (pos && st != pos) {
+ ingroup = true;
+ while (p && *p != ']' && (p = nextchar(p)));
+ }
+ } else if (pos) {
+ ingroup = true;
+ while (p && *p != ']' && (p = nextchar(p)));
+ }
+ } else if (pos) { // group
+ p = nextchar(p);
+ } else return 0;
+ }
+ }
+ if (!p) return 1;
+ }
+}
+
+// check if this prefix entry matches
+struct hentry * PfxEntry::checkword(const char * word, int len, char in_compound, const FLAG needflag)
+{
+ int tmpl; // length of tmpword
+ struct hentry * he; // hash entry of root word or NULL
+ char tmpword[MAXWORDUTF8LEN + 4];
+
+ // on entry prefix is 0 length or already matches the beginning of the word.
+ // So if the remaining root word has positive length
+ // and if there are enough chars in root word and added back strip chars
+ // to meet the number of characters conditions, then test it
+
+ tmpl = len - appndl;
+
+ if (tmpl > 0 || (tmpl == 0 && pmyMgr->get_fullstrip())) {
+
+ // generate new root word by removing prefix and adding
+ // back any characters that would have been stripped
+
+ if (stripl) strcpy (tmpword, strip);
+ strcpy ((tmpword + stripl), (word + appndl));
+
+ // now make sure all of the conditions on characters
+ // are met. Please see the appendix at the end of
+ // this file for more info on exactly what is being
+ // tested
+
+ // if all conditions are met then check if resulting
+ // root word in the dictionary
+
+ if (test_condition(tmpword)) {
+ tmpl += stripl;
+ if ((he = pmyMgr->lookup(tmpword)) != NULL) {
+ do {
+ if (TESTAFF(he->astr, aflag, he->alen) &&
+ // forbid single prefixes with needaffix flag
+ ! TESTAFF(contclass, pmyMgr->get_needaffix(), contclasslen) &&
+ // needflag
+ ((!needflag) || TESTAFF(he->astr, needflag, he->alen) ||
+ (contclass && TESTAFF(contclass, needflag, contclasslen))))
+ return he;
+ he = he->next_homonym; // check homonyms
+ } while (he);
+ }
+
+ // prefix matched but no root word was found
+ // if aeXPRODUCT is allowed, try again but now
+ // ross checked combined with a suffix
+
+ //if ((opts & aeXPRODUCT) && in_compound) {
+ if ((opts & aeXPRODUCT)) {
+ he = pmyMgr->suffix_check(tmpword, tmpl, aeXPRODUCT, this, NULL,
+ 0, NULL, FLAG_NULL, needflag, in_compound);
+ if (he) return he;
+ }
+ }
+ }
+ return NULL;
+}
+
+// check if this prefix entry matches
+struct hentry * PfxEntry::check_twosfx(const char * word, int len,
+ char in_compound, const FLAG needflag)
+{
+ int tmpl; // length of tmpword
+ struct hentry * he; // hash entry of root word or NULL
+ char tmpword[MAXWORDUTF8LEN + 4];
+
+ // on entry prefix is 0 length or already matches the beginning of the word.
+ // So if the remaining root word has positive length
+ // and if there are enough chars in root word and added back strip chars
+ // to meet the number of characters conditions, then test it
+
+ tmpl = len - appndl;
+
+ if ((tmpl > 0 || (tmpl == 0 && pmyMgr->get_fullstrip())) &&
+ (tmpl + stripl >= numconds)) {
+
+ // generate new root word by removing prefix and adding
+ // back any characters that would have been stripped
+
+ if (stripl) strcpy (tmpword, strip);
+ strcpy ((tmpword + stripl), (word + appndl));
+
+ // now make sure all of the conditions on characters
+ // are met. Please see the appendix at the end of
+ // this file for more info on exactly what is being
+ // tested
+
+ // if all conditions are met then check if resulting
+ // root word in the dictionary
+
+ if (test_condition(tmpword)) {
+ tmpl += stripl;
+
+ // prefix matched but no root word was found
+ // if aeXPRODUCT is allowed, try again but now
+ // cross checked combined with a suffix
+
+ if ((opts & aeXPRODUCT) && (in_compound != IN_CPD_BEGIN)) {
+ he = pmyMgr->suffix_check_twosfx(tmpword, tmpl, aeXPRODUCT, this, needflag);
+ if (he) return he;
+ }
+ }
+ }
+ return NULL;
+}
+
+// check if this prefix entry matches
+char * PfxEntry::check_twosfx_morph(const char * word, int len,
+ char in_compound, const FLAG needflag)
+{
+ int tmpl; // length of tmpword
+ char tmpword[MAXWORDUTF8LEN + 4];
+
+ // on entry prefix is 0 length or already matches the beginning of the word.
+ // So if the remaining root word has positive length
+ // and if there are enough chars in root word and added back strip chars
+ // to meet the number of characters conditions, then test it
+
+ tmpl = len - appndl;
+
+ if ((tmpl > 0 || (tmpl == 0 && pmyMgr->get_fullstrip())) &&
+ (tmpl + stripl >= numconds)) {
+
+ // generate new root word by removing prefix and adding
+ // back any characters that would have been stripped
+
+ if (stripl) strcpy (tmpword, strip);
+ strcpy ((tmpword + stripl), (word + appndl));
+
+ // now make sure all of the conditions on characters
+ // are met. Please see the appendix at the end of
+ // this file for more info on exactly what is being
+ // tested
+
+ // if all conditions are met then check if resulting
+ // root word in the dictionary
+
+ if (test_condition(tmpword)) {
+ tmpl += stripl;
+
+ // prefix matched but no root word was found
+ // if aeXPRODUCT is allowed, try again but now
+ // ross checked combined with a suffix
+
+ if ((opts & aeXPRODUCT) && (in_compound != IN_CPD_BEGIN)) {
+ return pmyMgr->suffix_check_twosfx_morph(tmpword, tmpl,
+ aeXPRODUCT, this, needflag);
+ }
+ }
+ }
+ return NULL;
+}
+
+// check if this prefix entry matches
+char * PfxEntry::check_morph(const char * word, int len, char in_compound, const FLAG needflag)
+{
+ int tmpl; // length of tmpword
+ struct hentry * he; // hash entry of root word or NULL
+ char tmpword[MAXWORDUTF8LEN + 4];
+ char result[MAXLNLEN];
+ char * st;
+
+ *result = '\0';
+
+ // on entry prefix is 0 length or already matches the beginning of the word.
+ // So if the remaining root word has positive length
+ // and if there are enough chars in root word and added back strip chars
+ // to meet the number of characters conditions, then test it
+
+ tmpl = len - appndl;
+
+ if ((tmpl > 0 || (tmpl == 0 && pmyMgr->get_fullstrip())) &&
+ (tmpl + stripl >= numconds)) {
+
+ // generate new root word by removing prefix and adding
+ // back any characters that would have been stripped
+
+ if (stripl) strcpy (tmpword, strip);
+ strcpy ((tmpword + stripl), (word + appndl));
+
+ // now make sure all of the conditions on characters
+ // are met. Please see the appendix at the end of
+ // this file for more info on exactly what is being
+ // tested
+
+ // if all conditions are met then check if resulting
+ // root word in the dictionary
+
+ if (test_condition(tmpword)) {
+ tmpl += stripl;
+ if ((he = pmyMgr->lookup(tmpword)) != NULL) {
+ do {
+ if (TESTAFF(he->astr, aflag, he->alen) &&
+ // forbid single prefixes with needaffix flag
+ ! TESTAFF(contclass, pmyMgr->get_needaffix(), contclasslen) &&
+ // needflag
+ ((!needflag) || TESTAFF(he->astr, needflag, he->alen) ||
+ (contclass && TESTAFF(contclass, needflag, contclasslen)))) {
+ if (morphcode) {
+ mystrcat(result, " ", MAXLNLEN);
+ mystrcat(result, morphcode, MAXLNLEN);
+ } else mystrcat(result,getKey(), MAXLNLEN);
+ if (!HENTRY_FIND(he, MORPH_STEM)) {
+ mystrcat(result, " ", MAXLNLEN);
+ mystrcat(result, MORPH_STEM, MAXLNLEN);
+ mystrcat(result, HENTRY_WORD(he), MAXLNLEN);
+ }
+ // store the pointer of the hash entry
+ if (HENTRY_DATA(he)) {
+ mystrcat(result, " ", MAXLNLEN);
+ mystrcat(result, HENTRY_DATA2(he), MAXLNLEN);
+ } else {
+ // return with debug information
+ char * flag = pmyMgr->encode_flag(getFlag());
+ mystrcat(result, " ", MAXLNLEN);
+ mystrcat(result, MORPH_FLAG, MAXLNLEN);
+ mystrcat(result, flag, MAXLNLEN);
+ free(flag);
+ }
+ mystrcat(result, "\n", MAXLNLEN);
+ }
+ he = he->next_homonym;
+ } while (he);
+ }
+
+ // prefix matched but no root word was found
+ // if aeXPRODUCT is allowed, try again but now
+ // ross checked combined with a suffix
+
+ if ((opts & aeXPRODUCT) && (in_compound != IN_CPD_BEGIN)) {
+ st = pmyMgr->suffix_check_morph(tmpword, tmpl, aeXPRODUCT, this,
+ FLAG_NULL, needflag);
+ if (st) {
+ mystrcat(result, st, MAXLNLEN);
+ free(st);
+ }
+ }
+ }
+ }
+
+ if (*result) return mystrdup(result);
+ return NULL;
+}
+
+SfxEntry::SfxEntry(AffixMgr * pmgr, affentry* dp)
+{
+ // register affix manager
+ pmyMgr = pmgr;
+
+ // set up its initial values
+ aflag = dp->aflag; // char flag
+ strip = dp->strip; // string to strip
+ appnd = dp->appnd; // string to append
+ stripl = dp->stripl; // length of strip string
+ appndl = dp->appndl; // length of append string
+ numconds = dp->numconds; // length of the condition
+ opts = dp->opts; // cross product flag
+
+ // then copy over all of the conditions
+ if (opts & aeLONGCOND) {
+ memcpy(c.l.conds1, dp->c.l.conds1, MAXCONDLEN_1);
+ c.l.conds2 = dp->c.l.conds2;
+ } else memcpy(c.conds, dp->c.conds, MAXCONDLEN);
+
+ rappnd = myrevstrdup(appnd);
+ morphcode = dp->morphcode;
+ contclass = dp->contclass;
+ contclasslen = dp->contclasslen;
+}
+
+
+SfxEntry::~SfxEntry()
+{
+ aflag = 0;
+ if (appnd) free(appnd);
+ if (rappnd) free(rappnd);
+ if (strip) free(strip);
+ pmyMgr = NULL;
+ appnd = NULL;
+ strip = NULL;
+ if (opts & aeLONGCOND) free(c.l.conds2);
+ if (morphcode && !(opts & aeALIASM)) free(morphcode);
+ if (contclass && !(opts & aeALIASF)) free(contclass);
+}
+
+// add suffix to this word assuming conditions hold
+char * SfxEntry::add(const char * word, int len)
+{
+ char tword[MAXWORDUTF8LEN + 4];
+
+ /* make sure all conditions match */
+ if ((len > stripl || (len == 0 && pmyMgr->get_fullstrip())) &&
+ (len >= numconds) && test_condition(word + len, word) &&
+ (!stripl || (strcmp(word + len - stripl, strip) == 0)) &&
+ ((MAXWORDUTF8LEN + 4) > (len + appndl - stripl))) {
+ /* we have a match so add suffix */
+ strcpy(tword,word);
+ if (appndl) {
+ strcpy(tword + len - stripl, appnd);
+ } else {
+ *(tword + len - stripl) = '\0';
+ }
+ return mystrdup(tword);
+ }
+ return NULL;
+}
+
+inline char * SfxEntry::nextchar(char * p) {
+ if (p) {
+ p++;
+ if (opts & aeLONGCOND) {
+ // jump to the 2nd part of the condition
+ if (p == c.l.conds1 + MAXCONDLEN_1) return c.l.conds2;
+ // end of the MAXCONDLEN length condition
+ } else if (p == c.conds + MAXCONDLEN) return NULL;
+ return *p ? p : NULL;
+ }
+ return NULL;
+}
+
+inline int SfxEntry::test_condition(const char * st, const char * beg)
+{
+ const char * pos = NULL; // group with pos input position
+ bool neg = false; // complementer
+ bool ingroup = false; // character in the group
+ if (numconds == 0) return 1;
+ char * p = c.conds;
+ st--;
+ int i = 1;
+ while (1) {
+ switch (*p) {
+ case '\0': return 1;
+ case '[': { p = nextchar(p); pos = st; break; }
+ case '^': { p = nextchar(p); neg = true; break; }
+ case ']': { if (!neg && !ingroup) return 0;
+ i++;
+ // skip the next character
+ if (!ingroup) {
+ for (; (opts & aeUTF8) && (st >= beg) && (*st & 0xc0) == 0x80; st--);
+ st--;
+ }
+ pos = NULL;
+ neg = false;
+ ingroup = false;
+ p = nextchar(p);
+ if (st < beg && p) return 0; // word <= condition
+ break;
+ }
+ case '.': if (!pos) { // dots are not metacharacters in groups: [.]
+ p = nextchar(p);
+ // skip the next character
+ for (st--; (opts & aeUTF8) && (st >= beg) && (*st & 0xc0) == 0x80; st--);
+ if (st < beg) { // word <= condition
+ if (p) return 0; else return 1;
+ }
+ if ((opts & aeUTF8) && (*st & 0x80)) { // head of the UTF-8 character
+ st--;
+ if (st < beg) { // word <= condition
+ if (p) return 0; else return 1;
+ }
+ }
+ break;
+ }
+ default: {
+ if (*st == *p) {
+ p = nextchar(p);
+ if ((opts & aeUTF8) && (*st & 0x80)) {
+ st--;
+ while (p && (st >= beg)) {
+ if (*p != *st) {
+ if (!pos) return 0;
+ st = pos;
+ break;
+ }
+ // first byte of the UTF-8 multibyte character
+ if ((*p & 0xc0) != 0x80) break;
+ p = nextchar(p);
+ st--;
+ }
+ if (pos && st != pos) {
+ if (neg) return 0;
+ else if (i == numconds) return 1;
+ ingroup = true;
+ while (p && *p != ']' && (p = nextchar(p)));
+ st--;
+ }
+ if (p && *p != ']') p = nextchar(p);
+ } else if (pos) {
+ if (neg) return 0;
+ else if (i == numconds) return 1;
+ ingroup = true;
+ while (p && *p != ']' && (p = nextchar(p)));
+// if (p && *p != ']') p = nextchar(p);
+ st--;
+ }
+ if (!pos) {
+ i++;
+ st--;
+ }
+ if (st < beg && p && *p != ']') return 0; // word <= condition
+ } else if (pos) { // group
+ p = nextchar(p);
+ } else return 0;
+ }
+ }
+ if (!p) return 1;
+ }
+}
+
+// see if this suffix is present in the word
+struct hentry * SfxEntry::checkword(const char * word, int len, int optflags,
+ PfxEntry* ppfx, char ** wlst, int maxSug, int * ns, const FLAG cclass, const FLAG needflag,
+ const FLAG badflag)
+{
+ int tmpl; // length of tmpword
+ struct hentry * he; // hash entry pointer
+ unsigned char * cp;
+ char tmpword[MAXWORDUTF8LEN + 4];
+ PfxEntry* ep = ppfx;
+
+ // if this suffix is being cross checked with a prefix
+ // but it does not support cross products skip it
+
+ if (((optflags & aeXPRODUCT) != 0) && ((opts & aeXPRODUCT) == 0))
+ return NULL;
+
+ // upon entry suffix is 0 length or already matches the end of the word.
+ // So if the remaining root word has positive length
+ // and if there are enough chars in root word and added back strip chars
+ // to meet the number of characters conditions, then test it
+
+ tmpl = len - appndl;
+ // the second condition is not enough for UTF-8 strings
+ // it checked in test_condition()
+
+ if ((tmpl > 0 || (tmpl == 0 && pmyMgr->get_fullstrip())) &&
+ (tmpl + stripl >= numconds)) {
+
+ // generate new root word by removing suffix and adding
+ // back any characters that would have been stripped or
+ // or null terminating the shorter string
+
+ strcpy (tmpword, word);
+ cp = (unsigned char *)(tmpword + tmpl);
+ if (stripl) {
+ strcpy ((char *)cp, strip);
+ tmpl += stripl;
+ cp = (unsigned char *)(tmpword + tmpl);
+ } else *cp = '\0';
+
+ // now make sure all of the conditions on characters
+ // are met. Please see the appendix at the end of
+ // this file for more info on exactly what is being
+ // tested
+
+ // if all conditions are met then check if resulting
+ // root word in the dictionary
+
+ if (test_condition((char *) cp, (char *) tmpword)) {
+
+#ifdef SZOSZABLYA_POSSIBLE_ROOTS
+ fprintf(stdout,"%s %s %c\n", word, tmpword, aflag);
+#endif
+ if ((he = pmyMgr->lookup(tmpword)) != NULL) {
+ do {
+ // check conditional suffix (enabled by prefix)
+ if ((TESTAFF(he->astr, aflag, he->alen) || (ep && ep->getCont() &&
+ TESTAFF(ep->getCont(), aflag, ep->getContLen()))) &&
+ (((optflags & aeXPRODUCT) == 0) ||
+ (ep && TESTAFF(he->astr, ep->getFlag(), he->alen)) ||
+ // enabled by prefix
+ ((contclass) && (ep && TESTAFF(contclass, ep->getFlag(), contclasslen)))
+ ) &&
+ // handle cont. class
+ ((!cclass) ||
+ ((contclass) && TESTAFF(contclass, cclass, contclasslen))
+ ) &&
+ // check only in compound homonyms (bad flags)
+ (!badflag || !TESTAFF(he->astr, badflag, he->alen)
+ ) &&
+ // handle required flag
+ ((!needflag) ||
+ (TESTAFF(he->astr, needflag, he->alen) ||
+ ((contclass) && TESTAFF(contclass, needflag, contclasslen)))
+ )
+ ) return he;
+ he = he->next_homonym; // check homonyms
+ } while (he);
+
+ // obsolote stemming code (used only by the
+ // experimental SuffixMgr:suggest_pos_stems)
+ // store resulting root in wlst
+ } else if (wlst && (*ns < maxSug)) {
+ int cwrd = 1;
+ for (int k=0; k < *ns; k++)
+ if (strcmp(tmpword, wlst[k]) == 0) cwrd = 0;
+ if (cwrd) {
+ wlst[*ns] = mystrdup(tmpword);
+ if (wlst[*ns] == NULL) {
+ for (int j=0; j<*ns; j++) free(wlst[j]);
+ *ns = -1;
+ return NULL;
+ }
+ (*ns)++;
+ }
+ }
+ }
+ }
+ return NULL;
+}
+
+// see if two-level suffix is present in the word
+struct hentry * SfxEntry::check_twosfx(const char * word, int len, int optflags,
+ PfxEntry* ppfx, const FLAG needflag)
+{
+ int tmpl; // length of tmpword
+ struct hentry * he; // hash entry pointer
+ unsigned char * cp;
+ char tmpword[MAXWORDUTF8LEN + 4];
+ PfxEntry* ep = ppfx;
+
+
+ // if this suffix is being cross checked with a prefix
+ // but it does not support cross products skip it
+
+ if ((optflags & aeXPRODUCT) != 0 && (opts & aeXPRODUCT) == 0)
+ return NULL;
+
+ // upon entry suffix is 0 length or already matches the end of the word.
+ // So if the remaining root word has positive length
+ // and if there are enough chars in root word and added back strip chars
+ // to meet the number of characters conditions, then test it
+
+ tmpl = len - appndl;
+
+ if ((tmpl > 0 || (tmpl == 0 && pmyMgr->get_fullstrip())) &&
+ (tmpl + stripl >= numconds)) {
+
+ // generate new root word by removing suffix and adding
+ // back any characters that would have been stripped or
+ // or null terminating the shorter string
+
+ strcpy (tmpword, word);
+ cp = (unsigned char *)(tmpword + tmpl);
+ if (stripl) {
+ strcpy ((char *)cp, strip);
+ tmpl += stripl;
+ cp = (unsigned char *)(tmpword + tmpl);
+ } else *cp = '\0';
+
+ // now make sure all of the conditions on characters
+ // are met. Please see the appendix at the end of
+ // this file for more info on exactly what is being
+ // tested
+
+ // if all conditions are met then recall suffix_check
+
+ if (test_condition((char *) cp, (char *) tmpword)) {
+ if (ppfx) {
+ // handle conditional suffix
+ if ((contclass) && TESTAFF(contclass, ep->getFlag(), contclasslen))
+ he = pmyMgr->suffix_check(tmpword, tmpl, 0, NULL, NULL, 0, NULL, (FLAG) aflag, needflag);
+ else
+ he = pmyMgr->suffix_check(tmpword, tmpl, optflags, ppfx, NULL, 0, NULL, (FLAG) aflag, needflag);
+ } else {
+ he = pmyMgr->suffix_check(tmpword, tmpl, 0, NULL, NULL, 0, NULL, (FLAG) aflag, needflag);
+ }
+ if (he) return he;
+ }
+ }
+ return NULL;
+}
+
+// see if two-level suffix is present in the word
+char * SfxEntry::check_twosfx_morph(const char * word, int len, int optflags,
+ PfxEntry* ppfx, const FLAG needflag)
+{
+ int tmpl; // length of tmpword
+ unsigned char * cp;
+ char tmpword[MAXWORDUTF8LEN + 4];
+ PfxEntry* ep = ppfx;
+ char * st;
+
+ char result[MAXLNLEN];
+
+ *result = '\0';
+
+ // if this suffix is being cross checked with a prefix
+ // but it does not support cross products skip it
+
+ if ((optflags & aeXPRODUCT) != 0 && (opts & aeXPRODUCT) == 0)
+ return NULL;
+
+ // upon entry suffix is 0 length or already matches the end of the word.
+ // So if the remaining root word has positive length
+ // and if there are enough chars in root word and added back strip chars
+ // to meet the number of characters conditions, then test it
+
+ tmpl = len - appndl;
+
+ if ((tmpl > 0 || (tmpl == 0 && pmyMgr->get_fullstrip())) &&
+ (tmpl + stripl >= numconds)) {
+
+ // generate new root word by removing suffix and adding
+ // back any characters that would have been stripped or
+ // or null terminating the shorter string
+
+ strcpy (tmpword, word);
+ cp = (unsigned char *)(tmpword + tmpl);
+ if (stripl) {
+ strcpy ((char *)cp, strip);
+ tmpl += stripl;
+ cp = (unsigned char *)(tmpword + tmpl);
+ } else *cp = '\0';
+
+ // now make sure all of the conditions on characters
+ // are met. Please see the appendix at the end of
+ // this file for more info on exactly what is being
+ // tested
+
+ // if all conditions are met then recall suffix_check
+
+ if (test_condition((char *) cp, (char *) tmpword)) {
+ if (ppfx) {
+ // handle conditional suffix
+ if ((contclass) && TESTAFF(contclass, ep->getFlag(), contclasslen)) {
+ st = pmyMgr->suffix_check_morph(tmpword, tmpl, 0, NULL, aflag, needflag);
+ if (st) {
+ if (ppfx->getMorph()) {
+ mystrcat(result, ppfx->getMorph(), MAXLNLEN);
+ mystrcat(result, " ", MAXLNLEN);
+ }
+ mystrcat(result,st, MAXLNLEN);
+ free(st);
+ mychomp(result);
+ }
+ } else {
+ st = pmyMgr->suffix_check_morph(tmpword, tmpl, optflags, ppfx, aflag, needflag);
+ if (st) {
+ mystrcat(result, st, MAXLNLEN);
+ free(st);
+ mychomp(result);
+ }
+ }
+ } else {
+ st = pmyMgr->suffix_check_morph(tmpword, tmpl, 0, NULL, aflag, needflag);
+ if (st) {
+ mystrcat(result, st, MAXLNLEN);
+ free(st);
+ mychomp(result);
+ }
+ }
+ if (*result) return mystrdup(result);
+ }
+ }
+ return NULL;
+}
+
+// get next homonym with same affix
+struct hentry * SfxEntry::get_next_homonym(struct hentry * he, int optflags, PfxEntry* ppfx,
+ const FLAG cclass, const FLAG needflag)
+{
+ PfxEntry* ep = ppfx;
+ FLAG eFlag = ep ? ep->getFlag() : FLAG_NULL;
+
+ while (he->next_homonym) {
+ he = he->next_homonym;
+ if ((TESTAFF(he->astr, aflag, he->alen) || (ep && ep->getCont() && TESTAFF(ep->getCont(), aflag, ep->getContLen()))) &&
+ ((optflags & aeXPRODUCT) == 0 ||
+ TESTAFF(he->astr, eFlag, he->alen) ||
+ // handle conditional suffix
+ ((contclass) && TESTAFF(contclass, eFlag, contclasslen))
+ ) &&
+ // handle cont. class
+ ((!cclass) ||
+ ((contclass) && TESTAFF(contclass, cclass, contclasslen))
+ ) &&
+ // handle required flag
+ ((!needflag) ||
+ (TESTAFF(he->astr, needflag, he->alen) ||
+ ((contclass) && TESTAFF(contclass, needflag, contclasslen)))
+ )
+ ) return he;
+ }
+ return NULL;
+}
+
+
+#if 0
+
+Appendix: Understanding Affix Code
+
+
+An affix is either a prefix or a suffix attached to root words to make
+other words.
+
+Basically a Prefix or a Suffix is set of AffEntry objects
+which store information about the prefix or suffix along
+with supporting routines to check if a word has a particular
+prefix or suffix or a combination.
+
+The structure affentry is defined as follows:
+
+struct affentry
+{
+ unsigned short aflag; // ID used to represent the affix
+ char * strip; // string to strip before adding affix
+ char * appnd; // the affix string to add
+ unsigned char stripl; // length of the strip string
+ unsigned char appndl; // length of the affix string
+ char numconds; // the number of conditions that must be met
+ char opts; // flag: aeXPRODUCT- combine both prefix and suffix
+ char conds[SETSIZE]; // array which encodes the conditions to be met
+};
+
+
+Here is a suffix borrowed from the en_US.aff file. This file
+is whitespace delimited.
+
+SFX D Y 4
+SFX D 0 e d
+SFX D y ied [^aeiou]y
+SFX D 0 ed [^ey]
+SFX D 0 ed [aeiou]y
+
+This information can be interpreted as follows:
+
+In the first line has 4 fields
+
+Field
+-----
+1 SFX - indicates this is a suffix
+2 D - is the name of the character flag which represents this suffix
+3 Y - indicates it can be combined with prefixes (cross product)
+4 4 - indicates that sequence of 4 affentry structures are needed to
+ properly store the affix information
+
+The remaining lines describe the unique information for the 4 SfxEntry
+objects that make up this affix. Each line can be interpreted
+as follows: (note fields 1 and 2 are as a check against line 1 info)
+
+Field
+-----
+1 SFX - indicates this is a suffix
+2 D - is the name of the character flag for this affix
+3 y - the string of chars to strip off before adding affix
+ (a 0 here indicates the NULL string)
+4 ied - the string of affix characters to add
+5 [^aeiou]y - the conditions which must be met before the affix
+ can be applied
+
+Field 5 is interesting. Since this is a suffix, field 5 tells us that
+there are 2 conditions that must be met. The first condition is that
+the next to the last character in the word must *NOT* be any of the
+following "a", "e", "i", "o" or "u". The second condition is that
+the last character of the word must end in "y".
+
+So how can we encode this information concisely and be able to
+test for both conditions in a fast manner? The answer is found
+but studying the wonderful ispell code of Geoff Kuenning, et.al.
+(now available under a normal BSD license).
+
+If we set up a conds array of 256 bytes indexed (0 to 255) and access it
+using a character (cast to an unsigned char) of a string, we have 8 bits
+of information we can store about that character. Specifically we
+could use each bit to say if that character is allowed in any of the
+last (or first for prefixes) 8 characters of the word.
+
+Basically, each character at one end of the word (up to the number
+of conditions) is used to index into the conds array and the resulting
+value found there says whether the that character is valid for a
+specific character position in the word.
+
+For prefixes, it does this by setting bit 0 if that char is valid
+in the first position, bit 1 if valid in the second position, and so on.
+
+If a bit is not set, then that char is not valid for that postion in the
+word.
+
+If working with suffixes bit 0 is used for the character closest
+to the front, bit 1 for the next character towards the end, ...,
+with bit numconds-1 representing the last char at the end of the string.
+
+Note: since entries in the conds[] are 8 bits, only 8 conditions
+(read that only 8 character positions) can be examined at one
+end of a word (the beginning for prefixes and the end for suffixes.
+
+So to make this clearer, lets encode the conds array values for the
+first two affentries for the suffix D described earlier.
+
+
+ For the first affentry:
+ numconds = 1 (only examine the last character)
+
+ conds['e'] = (1 << 0) (the word must end in an E)
+ all others are all 0
+
+ For the second affentry:
+ numconds = 2 (only examine the last two characters)
+
+ conds[X] = conds[X] | (1 << 0) (aeiou are not allowed)
+ where X is all characters *but* a, e, i, o, or u
+
+
+ conds['y'] = (1 << 1) (the last char must be a y)
+ all other bits for all other entries in the conds array are zero
+
+
+#endif
+
diff --git a/Plugins/spellchecker/hunspell/affentry.hxx b/Plugins/spellchecker/hunspell/affentry.hxx
new file mode 100644
index 0000000..eaf361f
--- /dev/null
+++ b/Plugins/spellchecker/hunspell/affentry.hxx
@@ -0,0 +1,136 @@
+#ifndef _AFFIX_HXX_
+#define _AFFIX_HXX_
+
+#include "hunvisapi.h"
+
+#include "atypes.hxx"
+#include "baseaffix.hxx"
+#include "affixmgr.hxx"
+
+/* A Prefix Entry */
+
+class LIBHUNSPELL_DLL_EXPORTED PfxEntry : protected AffEntry
+{
+ AffixMgr* pmyMgr;
+
+ PfxEntry * next;
+ PfxEntry * nexteq;
+ PfxEntry * nextne;
+ PfxEntry * flgnxt;
+
+public:
+
+ PfxEntry(AffixMgr* pmgr, affentry* dp );
+ ~PfxEntry();
+
+ inline bool allowCross() { return ((opts & aeXPRODUCT) != 0); }
+ struct hentry * checkword(const char * word, int len, char in_compound,
+ const FLAG needflag = FLAG_NULL);
+
+ struct hentry * check_twosfx(const char * word, int len, char in_compound, const FLAG needflag = NULL);
+
+ char * check_morph(const char * word, int len, char in_compound,
+ const FLAG needflag = FLAG_NULL);
+
+ char * check_twosfx_morph(const char * word, int len,
+ char in_compound, const FLAG needflag = FLAG_NULL);
+
+ inline FLAG getFlag() { return aflag; }
+ inline const char * getKey() { return appnd; }
+ char * add(const char * word, int len);
+
+ inline short getKeyLen() { return appndl; }
+
+ inline const char * getMorph() { return morphcode; }
+
+ inline const unsigned short * getCont() { return contclass; }
+ inline short getContLen() { return contclasslen; }
+
+ inline PfxEntry * getNext() { return next; }
+ inline PfxEntry * getNextNE() { return nextne; }
+ inline PfxEntry * getNextEQ() { return nexteq; }
+ inline PfxEntry * getFlgNxt() { return flgnxt; }
+
+ inline void setNext(PfxEntry * ptr) { next = ptr; }
+ inline void setNextNE(PfxEntry * ptr) { nextne = ptr; }
+ inline void setNextEQ(PfxEntry * ptr) { nexteq = ptr; }
+ inline void setFlgNxt(PfxEntry * ptr) { flgnxt = ptr; }
+
+ inline char * nextchar(char * p);
+ inline int test_condition(const char * st);
+};
+
+
+
+
+/* A Suffix Entry */
+
+class LIBHUNSPELL_DLL_EXPORTED SfxEntry : protected AffEntry
+{
+ AffixMgr* pmyMgr;
+ char * rappnd;
+
+ SfxEntry * next;
+ SfxEntry * nexteq;
+ SfxEntry * nextne;
+ SfxEntry * flgnxt;
+
+ SfxEntry * l_morph;
+ SfxEntry * r_morph;
+ SfxEntry * eq_morph;
+
+public:
+
+ SfxEntry(AffixMgr* pmgr, affentry* dp );
+ ~SfxEntry();
+
+ inline bool allowCross() { return ((opts & aeXPRODUCT) != 0); }
+ struct hentry * checkword(const char * word, int len, int optflags,
+ PfxEntry* ppfx, char ** wlst, int maxSug, int * ns,
+// const FLAG cclass = FLAG_NULL, const FLAG needflag = FLAG_NULL, char in_compound=IN_CPD_NOT);
+ const FLAG cclass = FLAG_NULL, const FLAG needflag = FLAG_NULL, const FLAG badflag = 0);
+
+ struct hentry * check_twosfx(const char * word, int len, int optflags, PfxEntry* ppfx, const FLAG needflag = NULL);
+
+ char * check_twosfx_morph(const char * word, int len, int optflags,
+ PfxEntry* ppfx, const FLAG needflag = FLAG_NULL);
+ struct hentry * get_next_homonym(struct hentry * he);
+ struct hentry * get_next_homonym(struct hentry * word, int optflags, PfxEntry* ppfx,
+ const FLAG cclass, const FLAG needflag);
+
+
+ inline FLAG getFlag() { return aflag; }
+ inline const char * getKey() { return rappnd; }
+ char * add(const char * word, int len);
+
+
+ inline const char * getMorph() { return morphcode; }
+
+ inline const unsigned short * getCont() { return contclass; }
+ inline short getContLen() { return contclasslen; }
+ inline const char * getAffix() { return appnd; }
+
+ inline short getKeyLen() { return appndl; }
+
+ inline SfxEntry * getNext() { return next; }
+ inline SfxEntry * getNextNE() { return nextne; }
+ inline SfxEntry * getNextEQ() { return nexteq; }
+
+ inline SfxEntry * getLM() { return l_morph; }
+ inline SfxEntry * getRM() { return r_morph; }
+ inline SfxEntry * getEQM() { return eq_morph; }
+ inline SfxEntry * getFlgNxt() { return flgnxt; }
+
+ inline void setNext(SfxEntry * ptr) { next = ptr; }
+ inline void setNextNE(SfxEntry * ptr) { nextne = ptr; }
+ inline void setNextEQ(SfxEntry * ptr) { nexteq = ptr; }
+ inline void setFlgNxt(SfxEntry * ptr) { flgnxt = ptr; }
+
+ inline char * nextchar(char * p);
+ inline int test_condition(const char * st, const char * begin);
+
+};
+
+#endif
+
+
diff --git a/Plugins/spellchecker/hunspell/affixmgr.cxx b/Plugins/spellchecker/hunspell/affixmgr.cxx
new file mode 100644
index 0000000..b9108d4
--- /dev/null
+++ b/Plugins/spellchecker/hunspell/affixmgr.cxx
@@ -0,0 +1,4521 @@
+#include "license.hunspell"
+#include "license.myspell"
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <ctype.h>
+
+#include <vector>
+
+#include "affixmgr.hxx"
+#include "affentry.hxx"
+#include "langnum.hxx"
+
+#include "csutil.hxx"
+
+AffixMgr::AffixMgr(const char * affpath, HashMgr** ptr, int * md, const char * key)
+{
+ // register hash manager and load affix data from aff file
+ pHMgr = ptr[0];
+ alldic = ptr;
+ maxdic = md;
+ keystring = NULL;
+ trystring = NULL;
+ encoding=NULL;
+ csconv=NULL;
+ utf8 = 0;
+ complexprefixes = 0;
+ maptable = NULL;
+ nummap = 0;
+ breaktable = NULL;
+ numbreak = -1;
+ reptable = NULL;
+ numrep = 0;
+ iconvtable = NULL;
+ oconvtable = NULL;
+ checkcpdtable = NULL;
+ // allow simplified compound forms (see 3rd field of CHECKCOMPOUNDPATTERN)
+ simplifiedcpd = 0;
+ numcheckcpd = 0;
+ defcpdtable = NULL;
+ numdefcpd = 0;
+ phone = NULL;
+ compoundflag = FLAG_NULL; // permits word in compound forms
+ compoundbegin = FLAG_NULL; // may be first word in compound forms
+ compoundmiddle = FLAG_NULL; // may be middle word in compound forms
+ compoundend = FLAG_NULL; // may be last word in compound forms
+ compoundroot = FLAG_NULL; // compound word signing flag
+ compoundpermitflag = FLAG_NULL; // compound permitting flag for suffixed word
+ compoundforbidflag = FLAG_NULL; // compound fordidden flag for suffixed word
+ checkcompounddup = 0; // forbid double words in compounds
+ checkcompoundrep = 0; // forbid bad compounds (may be non compound word with a REP substitution)
+ checkcompoundcase = 0; // forbid upper and lowercase combinations at word bounds
+ checkcompoundtriple = 0; // forbid compounds with triple letters
+ simplifiedtriple = 0; // allow simplified triple letters in compounds (Schiff+fahrt -> Schiffahrt)
+ forbiddenword = FORBIDDENWORD; // forbidden word signing flag
+ nosuggest = FLAG_NULL; // don't suggest words signed with NOSUGGEST flag
+ nongramsuggest = FLAG_NULL;
+ lang = NULL; // language
+ langnum = 0; // language code (see http://l10n.openoffice.org/languages.html)
+ needaffix = FLAG_NULL; // forbidden root, allowed only with suffixes
+ cpdwordmax = -1; // default: unlimited wordcount in compound words
+ cpdmin = -1; // undefined
+ cpdmaxsyllable = 0; // default: unlimited syllablecount in compound words
+ cpdvowels=NULL; // vowels (for calculating of Hungarian compounding limit, O(n) search! XXX)
+ cpdvowels_utf16=NULL; // vowels for UTF-8 encoding (bsearch instead of O(n) search)
+ cpdvowels_utf16_len=0; // vowels
+ pfxappnd=NULL; // previous prefix for counting the syllables of prefix BUG
+ sfxappnd=NULL; // previous suffix for counting a special syllables BUG
+ cpdsyllablenum=NULL; // syllable count incrementing flag
+ checknum=0; // checking numbers, and word with numbers
+ wordchars=NULL; // letters + spec. word characters
+ wordchars_utf16=NULL; // letters + spec. word characters
+ wordchars_utf16_len=0; // letters + spec. word characters
+ ignorechars=NULL; // letters + spec. word characters
+ ignorechars_utf16=NULL; // letters + spec. word characters
+ ignorechars_utf16_len=0; // letters + spec. word characters
+ version=NULL; // affix and dictionary file version string
+ havecontclass=0; // flags of possible continuing classes (double affix)
+ // LEMMA_PRESENT: not put root into the morphological output. Lemma presents
+ // in morhological description in dictionary file. It's often combined with PSEUDOROOT.
+ lemma_present = FLAG_NULL;
+ circumfix = FLAG_NULL;
+ onlyincompound = FLAG_NULL;
+ maxngramsugs = -1; // undefined
+ maxdiff = -1; // undefined
+ onlymaxdiff = 0;
+ maxcpdsugs = -1; // undefined
+ nosplitsugs = 0;
+ sugswithdots = 0;
+ keepcase = 0;
+ forceucase = 0;
+ warn = 0;
+ forbidwarn = 0;
+ checksharps = 0;
+ substandard = FLAG_NULL;
+ fullstrip = 0;
+
+ sfx = NULL;
+ pfx = NULL;
+
+ for (int i=0; i < SETSIZE; i++) {
+ pStart[i] = NULL;
+ sStart[i] = NULL;
+ pFlag[i] = NULL;
+ sFlag[i] = NULL;
+ }
+
+ for (int j=0; j < CONTSIZE; j++) {
+ contclasses[j] = 0;
+ }
+
+ if (parse_file(affpath, key)) {
+ HUNSPELL_WARNING(stderr, "Failure loading aff file %s\n",affpath);
+ }
+
+ if (cpdmin == -1) cpdmin = MINCPDLEN;
+
+}
+
+
+AffixMgr::~AffixMgr()
+{
+ // pass through linked prefix entries and clean up
+ for (int i=0; i < SETSIZE ;i++) {
+ pFlag[i] = NULL;
+ PfxEntry * ptr = pStart[i];
+ PfxEntry * nptr = NULL;
+ while (ptr) {
+ nptr = ptr->getNext();
+ delete(ptr);
+ ptr = nptr;
+ nptr = NULL;
+ }
+ }
+
+ // pass through linked suffix entries and clean up
+ for (int j=0; j < SETSIZE ; j++) {
+ sFlag[j] = NULL;
+ SfxEntry * ptr = sStart[j];
+ SfxEntry * nptr = NULL;
+ while (ptr) {
+ nptr = ptr->getNext();
+ delete(ptr);
+ ptr = nptr;
+ nptr = NULL;
+ }
+ sStart[j] = NULL;
+ }
+
+ if (keystring) free(keystring);
+ keystring=NULL;
+ if (trystring) free(trystring);
+ trystring=NULL;
+ if (encoding) free(encoding);
+ encoding=NULL;
+ if (maptable) {
+ for (int j=0; j < nummap; j++) {
+ for (int k=0; k < maptable[j].len; k++) {
+ if (maptable[j].set[k]) free(maptable[j].set[k]);
+ }
+ free(maptable[j].set);
+ maptable[j].set = NULL;
+ maptable[j].len = 0;
+ }
+ free(maptable);
+ maptable = NULL;
+ }
+ nummap = 0;
+ if (breaktable) {
+ for (int j=0; j < numbreak; j++) {
+ if (breaktable[j]) free(breaktable[j]);
+ breaktable[j] = NULL;
+ }
+ free(breaktable);
+ breaktable = NULL;
+ }
+ numbreak = 0;
+ if (reptable) {
+ for (int j=0; j < numrep; j++) {
+ free(reptable[j].pattern);
+ free(reptable[j].pattern2);
+ }
+ free(reptable);
+ reptable = NULL;
+ }
+ if (iconvtable) delete iconvtable;
+ if (oconvtable) delete oconvtable;
+ if (phone && phone->rules) {
+ for (int j=0; j < phone->num + 1; j++) {
+ free(phone->rules[j * 2]);
+ free(phone->rules[j * 2 + 1]);
+ }
+ free(phone->rules);
+ free(phone);
+ phone = NULL;
+ }
+
+ if (defcpdtable) {
+ for (int j=0; j < numdefcpd; j++) {
+ free(defcpdtable[j].def);
+ defcpdtable[j].def = NULL;
+ }
+ free(defcpdtable);
+ defcpdtable = NULL;
+ }
+ numrep = 0;
+ if (checkcpdtable) {
+ for (int j=0; j < numcheckcpd; j++) {
+ free(checkcpdtable[j].pattern);
+ free(checkcpdtable[j].pattern2);
+ free(checkcpdtable[j].pattern3);
+ checkcpdtable[j].pattern = NULL;
+ checkcpdtable[j].pattern2 = NULL;
+ checkcpdtable[j].pattern3 = NULL;
+ }
+ free(checkcpdtable);
+ checkcpdtable = NULL;
+ }
+ numcheckcpd = 0;
+ FREE_FLAG(compoundflag);
+ FREE_FLAG(compoundbegin);
+ FREE_FLAG(compoundmiddle);
+ FREE_FLAG(compoundend);
+ FREE_FLAG(compoundpermitflag);
+ FREE_FLAG(compoundforbidflag);
+ FREE_FLAG(compoundroot);
+ FREE_FLAG(forbiddenword);
+ FREE_FLAG(nosuggest);
+ FREE_FLAG(nongramsuggest);
+ FREE_FLAG(needaffix);
+ FREE_FLAG(lemma_present);
+ FREE_FLAG(circumfix);
+ FREE_FLAG(onlyincompound);
+
+ cpdwordmax = 0;
+ pHMgr = NULL;
+ cpdmin = 0;
+ cpdmaxsyllable = 0;
+ if (cpdvowels) free(cpdvowels);
+ if (cpdvowels_utf16) free(cpdvowels_utf16);
+ if (cpdsyllablenum) free(cpdsyllablenum);
+ free_utf_tbl();
+ if (lang) free(lang);
+ if (wordchars) free(wordchars);
+ if (wordchars_utf16) free(wordchars_utf16);
+ if (ignorechars) free(ignorechars);
+ if (ignorechars_utf16) free(ignorechars_utf16);
+ if (version) free(version);
+ checknum=0;
+#ifdef MOZILLA_CLIENT
+ delete [] csconv;
+#endif
+}
+
+
+// read in aff file and build up prefix and suffix entry objects
+int AffixMgr::parse_file(const char * affpath, const char * key)
+{
+ char * line; // io buffers
+ char ft; // affix type
+
+ // checking flag duplication
+ char dupflags[CONTSIZE];
+ char dupflags_ini = 1;
+
+ // first line indicator for removing byte order mark
+ int firstline = 1;
+
+ // open the affix file
+ FileMgr * afflst = new FileMgr(affpath, key);
+ if (!afflst) {
+ HUNSPELL_WARNING(stderr, "error: could not open affix description file %s\n",affpath);
+ return 1;
+ }
+
+ // step one is to parse the affix file building up the internal
+ // affix data structures
+
+ // read in each line ignoring any that do not
+ // start with a known line type indicator
+ while ((line = afflst->getline())) {
+ mychomp(line);
+
+ /* remove byte order mark */
+ if (firstline) {
+ firstline = 0;
+ // Affix file begins with byte order mark: possible incompatibility with old Hunspell versions
+ if (strncmp(line,"\xEF\xBB\xBF",3) == 0) {
+ memmove(line, line+3, strlen(line+3)+1);
+ }
+ }
+
+ /* parse in the keyboard string */
+ if (strncmp(line,"KEY",3) == 0) {
+ if (parse_string(line, &keystring, afflst->getlinenum())) {
+ delete afflst;
+ return 1;
+ }
+ }
+
+ /* parse in the try string */
+ if (strncmp(line,"TRY",3) == 0) {
+ if (parse_string(line, &trystring, afflst->getlinenum())) {
+ delete afflst;
+ return 1;
+ }
+ }
+
+ /* parse in the name of the character set used by the .dict and .aff */
+ if (strncmp(line,"SET",3) == 0) {
+ if (parse_string(line, &encoding, afflst->getlinenum())) {
+ delete afflst;
+ return 1;
+ }
+ if (strcmp(encoding, "UTF-8") == 0) {
+ utf8 = 1;
+#ifndef OPENOFFICEORG
+#ifndef MOZILLA_CLIENT
+ if (initialize_utf_tbl()) return 1;
+#endif
+#endif
+ }
+ }
+
+ /* parse COMPLEXPREFIXES for agglutinative languages with right-to-left writing system */
+ if (strncmp(line,"COMPLEXPREFIXES",15) == 0)
+ complexprefixes = 1;
+
+ /* parse in the flag used by the controlled compound words */
+ if (strncmp(line,"COMPOUNDFLAG",12) == 0) {
+ if (parse_flag(line, &compoundflag, afflst)) {
+ delete afflst;
+ return 1;
+ }
+ }
+
+ /* parse in the flag used by compound words */
+ if (strncmp(line,"COMPOUNDBEGIN",13) == 0) {
+ if (complexprefixes) {
+ if (parse_flag(line, &compoundend, afflst)) {
+ delete afflst;
+ return 1;
+ }
+ } else {
+ if (parse_flag(line, &compoundbegin, afflst)) {
+ delete afflst;
+ return 1;
+ }
+ }
+ }
+
+ /* parse in the flag used by compound words */
+ if (strncmp(line,"COMPOUNDMIDDLE",14) == 0) {
+ if (parse_flag(line, &compoundmiddle, afflst)) {
+ delete afflst;
+ return 1;
+ }
+ }
+ /* parse in the flag used by compound words */
+ if (strncmp(line,"COMPOUNDEND",11) == 0) {
+ if (complexprefixes) {
+ if (parse_flag(line, &compoundbegin, afflst)) {
+ delete afflst;
+ return 1;
+ }
+ } else {
+ if (parse_flag(line, &compoundend, afflst)) {
+ delete afflst;
+ return 1;
+ }
+ }
+ }
+
+ /* parse in the data used by compound_check() method */
+ if (strncmp(line,"COMPOUNDWORDMAX",15) == 0) {
+ if (parse_num(line, &cpdwordmax, afflst)) {
+ delete afflst;
+ return 1;
+ }
+ }
+
+ /* parse in the flag sign compounds in dictionary */
+ if (strncmp(line,"COMPOUNDROOT",12) == 0) {
+ if (parse_flag(line, &compoundroot, afflst)) {
+ delete afflst;
+ return 1;
+ }
+ }
+
+ /* parse in the flag used by compound_check() method */
+ if (strncmp(line,"COMPOUNDPERMITFLAG",18) == 0) {
+ if (parse_flag(line, &compoundpermitflag, afflst)) {
+ delete afflst;
+ return 1;
+ }
+ }
+
+ /* parse in the flag used by compound_check() method */
+ if (strncmp(line,"COMPOUNDFORBIDFLAG",18) == 0) {
+ if (parse_flag(line, &compoundforbidflag, afflst)) {
+ delete afflst;
+ return 1;
+ }
+ }
+
+ if (strncmp(line,"CHECKCOMPOUNDDUP",16) == 0) {
+ checkcompounddup = 1;
+ }
+
+ if (strncmp(line,"CHECKCOMPOUNDREP",16) == 0) {
+ checkcompoundrep = 1;
+ }
+
+ if (strncmp(line,"CHECKCOMPOUNDTRIPLE",19) == 0) {
+ checkcompoundtriple = 1;
+ }
+
+ if (strncmp(line,"SIMPLIFIEDTRIPLE",16) == 0) {
+ simplifiedtriple = 1;
+ }
+
+ if (strncmp(line,"CHECKCOMPOUNDCASE",17) == 0) {
+ checkcompoundcase = 1;
+ }
+
+ if (strncmp(line,"NOSUGGEST",9) == 0) {
+ if (parse_flag(line, &nosuggest, afflst)) {
+ delete afflst;
+ return 1;
+ }
+ }
+
+ if (strncmp(line,"NONGRAMSUGGEST",14) == 0) {
+ if (parse_flag(line, &nongramsuggest, afflst)) {
+ delete afflst;
+ return 1;
+ }
+ }
+
+ /* parse in the flag used by forbidden words */
+ if (strncmp(line,"FORBIDDENWORD",13) == 0) {
+ if (parse_flag(line, &forbiddenword, afflst)) {
+ delete afflst;
+ return 1;
+ }
+ }
+
+ /* parse in the flag used by forbidden words */
+ if (strncmp(line,"LEMMA_PRESENT",13) == 0) {
+ if (parse_flag(line, &lemma_present, afflst)) {
+ delete afflst;
+ return 1;
+ }
+ }
+
+ /* parse in the flag used by circumfixes */
+ if (strncmp(line,"CIRCUMFIX",9) == 0) {
+ if (parse_flag(line, &circumfix, afflst)) {
+ delete afflst;
+ return 1;
+ }
+ }
+
+ /* parse in the flag used by fogemorphemes */
+ if (strncmp(line,"ONLYINCOMPOUND",14) == 0) {
+ if (parse_flag(line, &onlyincompound, afflst)) {
+ delete afflst;
+ return 1;
+ }
+ }
+
+ /* parse in the flag used by `needaffixs' */
+ if (strncmp(line,"PSEUDOROOT",10) == 0) {
+ if (parse_flag(line, &needaffix, afflst)) {
+ delete afflst;
+ return 1;
+ }
+ }
+
+ /* parse in the flag used by `needaffixs' */
+ if (strncmp(line,"NEEDAFFIX",9) == 0) {
+ if (parse_flag(line, &needaffix, afflst)) {
+ delete afflst;
+ return 1;
+ }
+ }
+
+ /* parse in the minimal length for words in compounds */
+ if (strncmp(line,"COMPOUNDMIN",11) == 0) {
+ if (parse_num(line, &cpdmin, afflst)) {
+ delete afflst;
+ return 1;
+ }
+ if (cpdmin < 1) cpdmin = 1;
+ }
+
+ /* parse in the max. words and syllables in compounds */
+ if (strncmp(line,"COMPOUNDSYLLABLE",16) == 0) {
+ if (parse_cpdsyllable(line, afflst)) {
+ delete afflst;
+ return 1;
+ }
+ }
+
+ /* parse in the flag used by compound_check() method */
+ if (strncmp(line,"SYLLABLENUM",11) == 0) {
+ if (parse_string(line, &cpdsyllablenum, afflst->getlinenum())) {
+ delete afflst;
+ return 1;
+ }
+ }
+
+ /* parse in the flag used by the controlled compound words */
+ if (strncmp(line,"CHECKNUM",8) == 0) {
+ checknum=1;
+ }
+
+ /* parse in the extra word characters */
+ if (strncmp(line,"WORDCHARS",9) == 0) {
+ if (parse_array(line, &wordchars, &wordchars_utf16, &wordchars_utf16_len, utf8, afflst->getlinenum())) {
+ delete afflst;
+ return 1;
+ }
+ }
+
+ /* parse in the ignored characters (for example, Arabic optional diacretics charachters */
+ if (strncmp(line,"IGNORE",6) == 0) {
+ if (parse_array(line, &ignorechars, &ignorechars_utf16, &ignorechars_utf16_len, utf8, afflst->getlinenum())) {
+ delete afflst;
+ return 1;
+ }
+ }
+
+ /* parse in the typical fault correcting table */
+ if (strncmp(line,"REP",3) == 0) {
+ if (parse_reptable(line, afflst)) {
+ delete afflst;
+ return 1;
+ }
+ }
+
+ /* parse in the input conversion table */
+ if (strncmp(line,"ICONV",5) == 0) {
+ if (parse_convtable(line, afflst, &iconvtable, "ICONV")) {
+ delete afflst;
+ return 1;
+ }
+ }
+
+ /* parse in the input conversion table */
+ if (strncmp(line,"OCONV",5) == 0) {
+ if (parse_convtable(line, afflst, &oconvtable, "OCONV")) {
+ delete afflst;
+ return 1;
+ }
+ }
+
+ /* parse in the phonetic translation table */
+ if (strncmp(line,"PHONE",5) == 0) {
+ if (parse_phonetable(line, afflst)) {
+ delete afflst;
+ return 1;
+ }
+ }
+
+ /* parse in the checkcompoundpattern table */
+ if (strncmp(line,"CHECKCOMPOUNDPATTERN",20) == 0) {
+ if (parse_checkcpdtable(line, afflst)) {
+ delete afflst;
+ return 1;
+ }
+ }
+
+ /* parse in the defcompound table */
+ if (strncmp(line,"COMPOUNDRULE",12) == 0) {
+ if (parse_defcpdtable(line, afflst)) {
+ delete afflst;
+ return 1;
+ }
+ }
+
+ /* parse in the related character map table */
+ if (strncmp(line,"MAP",3) == 0) {
+ if (parse_maptable(line, afflst)) {
+ delete afflst;
+ return 1;
+ }
+ }
+
+ /* parse in the word breakpoints table */
+ if (strncmp(line,"BREAK",5) == 0) {
+ if (parse_breaktable(line, afflst)) {
+ delete afflst;
+ return 1;
+ }
+ }
+
+ /* parse in the language for language specific codes */
+ if (strncmp(line,"LANG",4) == 0) {
+ if (parse_string(line, &lang, afflst->getlinenum())) {
+ delete afflst;
+ return 1;
+ }
+ langnum = get_lang_num(lang);
+ }
+
+ if (strncmp(line,"VERSION",7) == 0) {
+ for(line = line + 7; *line == ' ' || *line == '\t'; line++);
+ version = mystrdup(line);
+ }
+
+ if (strncmp(line,"MAXNGRAMSUGS",12) == 0) {
+ if (parse_num(line, &maxngramsugs, afflst)) {
+ delete afflst;
+ return 1;
+ }
+ }
+
+ if (strncmp(line,"ONLYMAXDIFF", 11) == 0)
+ onlymaxdiff = 1;
+
+ if (strncmp(line,"MAXDIFF",7) == 0) {
+ if (parse_num(line, &maxdiff, afflst)) {
+ delete afflst;
+ return 1;
+ }
+ }
+
+ if (strncmp(line,"MAXCPDSUGS",10) == 0) {
+ if (parse_num(line, &maxcpdsugs, afflst)) {
+ delete afflst;
+ return 1;
+ }
+ }
+
+ if (strncmp(line,"NOSPLITSUGS",11) == 0) {
+ nosplitsugs=1;
+ }
+
+ if (strncmp(line,"FULLSTRIP",9) == 0) {
+ fullstrip=1;
+ }
+
+ if (strncmp(line,"SUGSWITHDOTS",12) == 0) {
+ sugswithdots=1;
+ }
+
+ /* parse in the flag used by forbidden words */
+ if (strncmp(line,"KEEPCASE",8) == 0) {
+ if (parse_flag(line, &keepcase, afflst)) {
+ delete afflst;
+ return 1;
+ }
+ }
+
+ /* parse in the flag used by `forceucase' */
+ if (strncmp(line,"FORCEUCASE",10) == 0) {
+ if (parse_flag(line, &forceucase, afflst)) {
+ delete afflst;
+ return 1;
+ }
+ }
+
+ /* parse in the flag used by `warn' */
+ if (strncmp(line,"WARN",4) == 0) {
+ if (parse_flag(line, &warn, afflst)) {
+ delete afflst;
+ return 1;
+ }
+ }
+
+ if (strncmp(line,"FORBIDWARN",10) == 0) {
+ forbidwarn=1;
+ }
+
+ /* parse in the flag used by the affix generator */
+ if (strncmp(line,"SUBSTANDARD",11) == 0) {
+ if (parse_flag(line, &substandard, afflst)) {
+ delete afflst;
+ return 1;
+ }
+ }
+
+ if (strncmp(line,"CHECKSHARPS",11) == 0) {
+ checksharps=1;
+ }
+
+ /* parse this affix: P - prefix, S - suffix */
+ ft = ' ';
+ if (strncmp(line,"PFX",3) == 0) ft = complexprefixes ? 'S' : 'P';
+ if (strncmp(line,"SFX",3) == 0) ft = complexprefixes ? 'P' : 'S';
+ if (ft != ' ') {
+ if (dupflags_ini) {
+ memset(dupflags, 0, sizeof(dupflags));
+ dupflags_ini = 0;
+ }
+ if (parse_affix(line, ft, afflst, dupflags)) {
+ delete afflst;
+ process_pfx_tree_to_list();
+ process_sfx_tree_to_list();
+ return 1;
+ }
+ }
+
+ }
+ delete afflst;
+
+ // convert affix trees to sorted list
+ process_pfx_tree_to_list();
+ process_sfx_tree_to_list();
+
+ // now we can speed up performance greatly taking advantage of the
+ // relationship between the affixes and the idea of "subsets".
+
+ // View each prefix as a potential leading subset of another and view
+ // each suffix (reversed) as a potential trailing subset of another.
+
+ // To illustrate this relationship if we know the prefix "ab" is found in the
+ // word to examine, only prefixes that "ab" is a leading subset of need be examined.
+ // Furthermore is "ab" is not present then none of the prefixes that "ab" is
+ // is a subset need be examined.
+ // The same argument goes for suffix string that are reversed.
+
+ // Then to top this off why not examine the first char of the word to quickly
+ // limit the set of prefixes to examine (i.e. the prefixes to examine must
+ // be leading supersets of the first character of the word (if they exist)
+
+ // To take advantage of this "subset" relationship, we need to add two links
+ // from entry. One to take next if the current prefix is found (call it nexteq)
+ // and one to take next if the current prefix is not found (call it nextne).
+
+ // Since we have built ordered lists, all that remains is to properly initialize
+ // the nextne and nexteq pointers that relate them
+
+ process_pfx_order();
+ process_sfx_order();
+
+ /* get encoding for CHECKCOMPOUNDCASE */
+ if (!utf8) {
+ char * enc = get_encoding();
+ csconv = get_current_cs(enc);
+ free(enc);
+ enc = NULL;
+
+ char expw[MAXLNLEN];
+ if (wordchars) {
+ strcpy(expw, wordchars);
+ free(wordchars);
+ } else *expw = '\0';
+
+ for (int i = 0; i <= 255; i++) {
+ if ( (csconv[i].cupper != csconv[i].clower) &&
+ (! strchr(expw, (char) i))) {
+ *(expw + strlen(expw) + 1) = '\0';
+ *(expw + strlen(expw)) = (char) i;
+ }
+ }
+
+ wordchars = mystrdup(expw);
+ }
+
+ // default BREAK definition
+ if (numbreak == -1) {
+ breaktable = (char **) malloc(sizeof(char *) * 3);
+ if (!breaktable) return 1;
+ breaktable[0] = mystrdup("-");
+ breaktable[1] = mystrdup("^-");
+ breaktable[2] = mystrdup("-$");
+ if (breaktable[0] && breaktable[1] && breaktable[2]) numbreak = 3;
+ }
+ return 0;
+}
+
+
+// we want to be able to quickly access prefix information
+// both by prefix flag, and sorted by prefix string itself
+// so we need to set up two indexes
+
+int AffixMgr::build_pfxtree(PfxEntry* pfxptr)
+{
+ PfxEntry * ptr;
+ PfxEntry * pptr;
+ PfxEntry * ep = pfxptr;
+
+ // get the right starting points
+ const char * key = ep->getKey();
+ const unsigned char flg = (unsigned char) (ep->getFlag() & 0x00FF);
+
+ // first index by flag which must exist
+ ptr = pFlag[flg];
+ ep->setFlgNxt(ptr);
+ pFlag[flg] = ep;
+
+
+ // handle the special case of null affix string
+ if (strlen(key) == 0) {
+ // always inset them at head of list at element 0
+ ptr = pStart[0];
+ ep->setNext(ptr);
+ pStart[0] = ep;
+ return 0;
+ }
+
+ // now handle the normal case
+ ep->setNextEQ(NULL);
+ ep->setNextNE(NULL);
+
+ unsigned char sp = *((const unsigned char *)key);
+ ptr = pStart[sp];
+
+ // handle the first insert
+ if (!ptr) {
+ pStart[sp] = ep;
+ return 0;
+ }
+
+
+ // otherwise use binary tree insertion so that a sorted
+ // list can easily be generated later
+ pptr = NULL;
+ for (;;) {
+ pptr = ptr;
+ if (strcmp(ep->getKey(), ptr->getKey() ) <= 0) {
+ ptr = ptr->getNextEQ();
+ if (!ptr) {
+ pptr->setNextEQ(ep);
+ break;
+ }
+ } else {
+ ptr = ptr->getNextNE();
+ if (!ptr) {
+ pptr->setNextNE(ep);
+ break;
+ }
+ }
+ }
+ return 0;
+}
+
+// we want to be able to quickly access suffix information
+// both by suffix flag, and sorted by the reverse of the
+// suffix string itself; so we need to set up two indexes
+int AffixMgr::build_sfxtree(SfxEntry* sfxptr)
+{
+ SfxEntry * ptr;
+ SfxEntry * pptr;
+ SfxEntry * ep = sfxptr;
+
+ /* get the right starting point */
+ const char * key = ep->getKey();
+ const unsigned char flg = (unsigned char) (ep->getFlag() & 0x00FF);
+
+ // first index by flag which must exist
+ ptr = sFlag[flg];
+ ep->setFlgNxt(ptr);
+ sFlag[flg] = ep;
+
+ // next index by affix string
+
+ // handle the special case of null affix string
+ if (strlen(key) == 0) {
+ // always inset them at head of list at element 0
+ ptr = sStart[0];
+ ep->setNext(ptr);
+ sStart[0] = ep;
+ return 0;
+ }
+
+ // now handle the normal case
+ ep->setNextEQ(NULL);
+ ep->setNextNE(NULL);
+
+ unsigned char sp = *((const unsigned char *)key);
+ ptr = sStart[sp];
+
+ // handle the first insert
+ if (!ptr) {
+ sStart[sp] = ep;
+ return 0;
+ }
+
+ // otherwise use binary tree insertion so that a sorted
+ // list can easily be generated later
+ pptr = NULL;
+ for (;;) {
+ pptr = ptr;
+ if (strcmp(ep->getKey(), ptr->getKey() ) <= 0) {
+ ptr = ptr->getNextEQ();
+ if (!ptr) {
+ pptr->setNextEQ(ep);
+ break;
+ }
+ } else {
+ ptr = ptr->getNextNE();
+ if (!ptr) {
+ pptr->setNextNE(ep);
+ break;
+ }
+ }
+ }
+ return 0;
+}
+
+// convert from binary tree to sorted list
+int AffixMgr::process_pfx_tree_to_list()
+{
+ for (int i=1; i< SETSIZE; i++) {
+ pStart[i] = process_pfx_in_order(pStart[i],NULL);
+ }
+ return 0;
+}
+
+
+PfxEntry* AffixMgr::process_pfx_in_order(PfxEntry* ptr, PfxEntry* nptr)
+{
+ if (ptr) {
+ nptr = process_pfx_in_order(ptr->getNextNE(), nptr);
+ ptr->setNext(nptr);
+ nptr = process_pfx_in_order(ptr->getNextEQ(), ptr);
+ }
+ return nptr;
+}
+
+
+// convert from binary tree to sorted list
+int AffixMgr:: process_sfx_tree_to_list()
+{
+ for (int i=1; i< SETSIZE; i++) {
+ sStart[i] = process_sfx_in_order(sStart[i],NULL);
+ }
+ return 0;
+}
+
+SfxEntry* AffixMgr::process_sfx_in_order(SfxEntry* ptr, SfxEntry* nptr)
+{
+ if (ptr) {
+ nptr = process_sfx_in_order(ptr->getNextNE(), nptr);
+ ptr->setNext(nptr);
+ nptr = process_sfx_in_order(ptr->getNextEQ(), ptr);
+ }
+ return nptr;
+}
+
+
+// reinitialize the PfxEntry links NextEQ and NextNE to speed searching
+// using the idea of leading subsets this time
+int AffixMgr::process_pfx_order()
+{
+ PfxEntry* ptr;
+
+ // loop through each prefix list starting point
+ for (int i=1; i < SETSIZE; i++) {
+
+ ptr = pStart[i];
+
+ // look through the remainder of the list
+ // and find next entry with affix that
+ // the current one is not a subset of
+ // mark that as destination for NextNE
+ // use next in list that you are a subset
+ // of as NextEQ
+
+ for (; ptr != NULL; ptr = ptr->getNext()) {
+
+ PfxEntry * nptr = ptr->getNext();
+ for (; nptr != NULL; nptr = nptr->getNext()) {
+ if (! isSubset( ptr->getKey() , nptr->getKey() )) break;
+ }
+ ptr->setNextNE(nptr);
+ ptr->setNextEQ(NULL);
+ if ((ptr->getNext()) && isSubset(ptr->getKey() , (ptr->getNext())->getKey()))
+ ptr->setNextEQ(ptr->getNext());
+ }
+
+ // now clean up by adding smart search termination strings:
+ // if you are already a superset of the previous prefix
+ // but not a subset of the next, search can end here
+ // so set NextNE properly
+
+ ptr = pStart[i];
+ for (; ptr != NULL; ptr = ptr->getNext()) {
+ PfxEntry * nptr = ptr->getNext();
+ PfxEntry * mptr = NULL;
+ for (; nptr != NULL; nptr = nptr->getNext()) {
+ if (! isSubset(ptr->getKey(),nptr->getKey())) break;
+ mptr = nptr;
+ }
+ if (mptr) mptr->setNextNE(NULL);
+ }
+ }
+ return 0;
+}
+
+// initialize the SfxEntry links NextEQ and NextNE to speed searching
+// using the idea of leading subsets this time
+int AffixMgr::process_sfx_order()
+{
+ SfxEntry* ptr;
+
+ // loop through each prefix list starting point
+ for (int i=1; i < SETSIZE; i++) {
+
+ ptr = sStart[i];
+
+ // look through the remainder of the list
+ // and find next entry with affix that
+ // the current one is not a subset of
+ // mark that as destination for NextNE
+ // use next in list that you are a subset
+ // of as NextEQ
+
+ for (; ptr != NULL; ptr = ptr->getNext()) {
+ SfxEntry * nptr = ptr->getNext();
+ for (; nptr != NULL; nptr = nptr->getNext()) {
+ if (! isSubset(ptr->getKey(),nptr->getKey())) break;
+ }
+ ptr->setNextNE(nptr);
+ ptr->setNextEQ(NULL);
+ if ((ptr->getNext()) && isSubset(ptr->getKey(),(ptr->getNext())->getKey()))
+ ptr->setNextEQ(ptr->getNext());
+ }
+
+
+ // now clean up by adding smart search termination strings:
+ // if you are already a superset of the previous suffix
+ // but not a subset of the next, search can end here
+ // so set NextNE properly
+
+ ptr = sStart[i];
+ for (; ptr != NULL; ptr = ptr->getNext()) {
+ SfxEntry * nptr = ptr->getNext();
+ SfxEntry * mptr = NULL;
+ for (; nptr != NULL; nptr = nptr->getNext()) {
+ if (! isSubset(ptr->getKey(),nptr->getKey())) break;
+ mptr = nptr;
+ }
+ if (mptr) mptr->setNextNE(NULL);
+ }
+ }
+ return 0;
+}
+
+// add flags to the result for dictionary debugging
+void AffixMgr::debugflag(char * result, unsigned short flag) {
+ char * st = encode_flag(flag);
+ mystrcat(result, " ", MAXLNLEN);
+ mystrcat(result, MORPH_FLAG, MAXLNLEN);
+ if (st) {
+ mystrcat(result, st, MAXLNLEN);
+ free(st);
+ }
+}
+
+// calculate the character length of the condition
+int AffixMgr::condlen(char * st)
+{
+ int l = 0;
+ bool group = false;
+ for(; *st; st++) {
+ if (*st == '[') {
+ group = true;
+ l++;
+ } else if (*st == ']') group = false;
+ else if (!group && (!utf8 ||
+ (!(*st & 0x80) || ((*st & 0xc0) == 0x80)))) l++;
+ }
+ return l;
+}
+
+int AffixMgr::encodeit(affentry &entry, char * cs)
+{
+ if (strcmp(cs,".") != 0) {
+ entry.numconds = (char) condlen(cs);
+ strncpy(entry.c.conds, cs, MAXCONDLEN);
+ // long condition (end of conds padded by strncpy)
+ if (entry.c.conds[MAXCONDLEN - 1] && cs[MAXCONDLEN]) {
+ entry.opts += aeLONGCOND;
+ entry.c.l.conds2 = mystrdup(cs + MAXCONDLEN_1);
+ if (!entry.c.l.conds2) return 1;
+ }
+ } else {
+ entry.numconds = 0;
+ entry.c.conds[0] = '\0';
+ }
+ return 0;
+}
+
+// return 1 if s1 is a leading subset of s2 (dots are for infixes)
+inline int AffixMgr::isSubset(const char * s1, const char * s2)
+ {
+ while (((*s1 == *s2) || (*s1 == '.')) && (*s1 != '\0')) {
+ s1++;
+ s2++;
+ }
+ return (*s1 == '\0');
+ }
+
+
+// check word for prefixes
+struct hentry * AffixMgr::prefix_check(const char * word, int len, char in_compound,
+ const FLAG needflag)
+{
+ struct hentry * rv= NULL;
+
+ pfx = NULL;
+ pfxappnd = NULL;
+ sfxappnd = NULL;
+
+ // first handle the special case of 0 length prefixes
+ PfxEntry * pe = pStart[0];
+ while (pe) {
+ if (
+ // fogemorpheme
+ ((in_compound != IN_CPD_NOT) || !(pe->getCont() &&
+ (TESTAFF(pe->getCont(), onlyincompound, pe->getContLen())))) &&
+ // permit prefixes in compounds
+ ((in_compound != IN_CPD_END) || (pe->getCont() &&
+ (TESTAFF(pe->getCont(), compoundpermitflag, pe->getContLen()))))
+ ) {
+ // check prefix
+ rv = pe->checkword(word, len, in_compound, needflag);
+ if (rv) {
+ pfx=pe; // BUG: pfx not stateless
+ return rv;
+ }
+ }
+ pe = pe->getNext();
+ }
+
+ // now handle the general case
+ unsigned char sp = *((const unsigned char *)word);
+ PfxEntry * pptr = pStart[sp];
+
+ while (pptr) {
+ if (isSubset(pptr->getKey(),word)) {
+ if (
+ // fogemorpheme
+ ((in_compound != IN_CPD_NOT) || !(pptr->getCont() &&
+ (TESTAFF(pptr->getCont(), onlyincompound, pptr->getContLen())))) &&
+ // permit prefixes in compounds
+ ((in_compound != IN_CPD_END) || (pptr->getCont() &&
+ (TESTAFF(pptr->getCont(), compoundpermitflag, pptr->getContLen()))))
+ ) {
+ // check prefix
+ rv = pptr->checkword(word, len, in_compound, needflag);
+ if (rv) {
+ pfx=pptr; // BUG: pfx not stateless
+ return rv;
+ }
+ }
+ pptr = pptr->getNextEQ();
+ } else {
+ pptr = pptr->getNextNE();
+ }
+ }
+
+ return NULL;
+}
+
+// check word for prefixes
+struct hentry * AffixMgr::prefix_check_twosfx(const char * word, int len,
+ char in_compound, const FLAG needflag)
+{
+ struct hentry * rv= NULL;
+
+ pfx = NULL;
+ sfxappnd = NULL;
+
+ // first handle the special case of 0 length prefixes
+ PfxEntry * pe = pStart[0];
+
+ while (pe) {
+ rv = pe->check_twosfx(word, len, in_compound, needflag);
+ if (rv) return rv;
+ pe = pe->getNext();
+ }
+
+ // now handle the general case
+ unsigned char sp = *((const unsigned char *)word);
+ PfxEntry * pptr = pStart[sp];
+
+ while (pptr) {
+ if (isSubset(pptr->getKey(),word)) {
+ rv = pptr->check_twosfx(word, len, in_compound, needflag);
+ if (rv) {
+ pfx = pptr;
+ return rv;
+ }
+ pptr = pptr->getNextEQ();
+ } else {
+ pptr = pptr->getNextNE();
+ }
+ }
+
+ return NULL;
+}
+
+// check word for prefixes
+char * AffixMgr::prefix_check_morph(const char * word, int len, char in_compound,
+ const FLAG needflag)
+{
+ char * st;
+
+ char result[MAXLNLEN];
+ result[0] = '\0';
+
+ pfx = NULL;
+ sfxappnd = NULL;
+
+ // first handle the special case of 0 length prefixes
+ PfxEntry * pe = pStart[0];
+ while (pe) {
+ st = pe->check_morph(word,len,in_compound, needflag);
+ if (st) {
+ mystrcat(result, st, MAXLNLEN);
+ free(st);
+ }
+ // if (rv) return rv;
+ pe = pe->getNext();
+ }
+
+ // now handle the general case
+ unsigned char sp = *((const unsigned char *)word);
+ PfxEntry * pptr = pStart[sp];
+
+ while (pptr) {
+ if (isSubset(pptr->getKey(),word)) {
+ st = pptr->check_morph(word,len,in_compound, needflag);
+ if (st) {
+ // fogemorpheme
+ if ((in_compound != IN_CPD_NOT) || !((pptr->getCont() &&
+ (TESTAFF(pptr->getCont(), onlyincompound, pptr->getContLen()))))) {
+ mystrcat(result, st, MAXLNLEN);
+ pfx = pptr;
+ }
+ free(st);
+ }
+ pptr = pptr->getNextEQ();
+ } else {
+ pptr = pptr->getNextNE();
+ }
+ }
+
+ if (*result) return mystrdup(result);
+ return NULL;
+}
+
+
+// check word for prefixes
+char * AffixMgr::prefix_check_twosfx_morph(const char * word, int len,
+ char in_compound, const FLAG needflag)
+{
+ char * st;
+
+ char result[MAXLNLEN];
+ result[0] = '\0';
+
+ pfx = NULL;
+ sfxappnd = NULL;
+
+ // first handle the special case of 0 length prefixes
+ PfxEntry * pe = pStart[0];
+ while (pe) {
+ st = pe->check_twosfx_morph(word,len,in_compound, needflag);
+ if (st) {
+ mystrcat(result, st, MAXLNLEN);
+ free(st);
+ }
+ pe = pe->getNext();
+ }
+
+ // now handle the general case
+ unsigned char sp = *((const unsigned char *)word);
+ PfxEntry * pptr = pStart[sp];
+
+ while (pptr) {
+ if (isSubset(pptr->getKey(),word)) {
+ st = pptr->check_twosfx_morph(word, len, in_compound, needflag);
+ if (st) {
+ mystrcat(result, st, MAXLNLEN);
+ free(st);
+ pfx = pptr;
+ }
+ pptr = pptr->getNextEQ();
+ } else {
+ pptr = pptr->getNextNE();
+ }
+ }
+
+ if (*result) return mystrdup(result);
+ return NULL;
+}
+
+// Is word a non compound with a REP substitution (see checkcompoundrep)?
+int AffixMgr::cpdrep_check(const char * word, int wl)
+{
+ char candidate[MAXLNLEN];
+ const char * r;
+ int lenr, lenp;
+
+ if ((wl < 2) || !numrep) return 0;
+
+ for (int i=0; i < numrep; i++ ) {
+ r = word;
+ lenr = strlen(reptable[i].pattern2);
+ lenp = strlen(reptable[i].pattern);
+ // search every occurence of the pattern in the word
+ while ((r=strstr(r, reptable[i].pattern)) != NULL) {
+ strcpy(candidate, word);
+ if (r-word + lenr + strlen(r+lenp) >= MAXLNLEN) break;
+ strcpy(candidate+(r-word),reptable[i].pattern2);
+ strcpy(candidate+(r-word)+lenr, r+lenp);
+ if (candidate_check(candidate,strlen(candidate))) return 1;
+ r++; // search for the next letter
+ }
+ }
+ return 0;
+}
+
+// forbid compoundings when there are special patterns at word bound
+int AffixMgr::cpdpat_check(const char * word, int pos, hentry * r1, hentry * r2, const char affixed)
+{
+ int len;
+ for (int i = 0; i < numcheckcpd; i++) {
+ if (isSubset(checkcpdtable[i].pattern2, word + pos) &&
+ (!r1 || !checkcpdtable[i].cond ||
+ (r1->astr && TESTAFF(r1->astr, checkcpdtable[i].cond, r1->alen))) &&
+ (!r2 || !checkcpdtable[i].cond2 ||
+ (r2->astr && TESTAFF(r2->astr, checkcpdtable[i].cond2, r2->alen))) &&
+ // zero length pattern => only TESTAFF
+ // zero pattern (0/flag) => unmodified stem (zero affixes allowed)
+ (!*(checkcpdtable[i].pattern) || (
+ (*(checkcpdtable[i].pattern)=='0' && r1->blen <= pos && strncmp(word + pos - r1->blen, r1->word, r1->blen) == 0) ||
+ (*(checkcpdtable[i].pattern)!='0' && (len = strlen(checkcpdtable[i].pattern)) &&
+ strncmp(word + pos - len, checkcpdtable[i].pattern, len) == 0)))) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+// forbid compounding with neighbouring upper and lower case characters at word bounds
+int AffixMgr::cpdcase_check(const char * word, int pos)
+{
+ if (utf8) {
+ w_char u, w;
+ const char * p;
+ u8_u16(&u, 1, word + pos);
+ for (p = word + pos - 1; (*p & 0xc0) == 0x80; p--);
+ u8_u16(&w, 1, p);
+ unsigned short a = (u.h << 8) + u.l;
+ unsigned short b = (w.h << 8) + w.l;
+ if (((unicodetoupper(a, langnum) == a) || (unicodetoupper(b, langnum) == b)) &&
+ (a != '-') && (b != '-')) return 1;
+ } else {
+ unsigned char a = *(word + pos - 1);
+ unsigned char b = *(word + pos);
+ if ((csconv[a].ccase || csconv[b].ccase) && (a != '-') && (b != '-')) return 1;
+ }
+ return 0;
+}
+
+// check compound patterns
+int AffixMgr::defcpd_check(hentry *** words, short wnum, hentry * rv, hentry ** def, char all)
+{
+ signed short btpp[MAXWORDLEN]; // metacharacter (*, ?) positions for backtracking
+ signed short btwp[MAXWORDLEN]; // word positions for metacharacters
+ int btnum[MAXWORDLEN]; // number of matched characters in metacharacter positions
+ short bt = 0;
+ int i, j;
+ int ok;
+ int w = 0;
+
+ if (!*words) {
+ w = 1;
+ *words = def;
+ }
+
+ if (!*words) {
+ return 0;
+ }
+
+ (*words)[wnum] = rv;
+
+ // has the last word COMPOUNDRULE flag?
+ if (rv->alen == 0) {
+ (*words)[wnum] = NULL;
+ if (w) *words = NULL;
+ return 0;
+ }
+ ok = 0;
+ for (i = 0; i < numdefcpd; i++) {
+ for (j = 0; j < defcpdtable[i].len; j++) {
+ if (defcpdtable[i].def[j] != '*' && defcpdtable[i].def[j] != '?' &&
+ TESTAFF(rv->astr, defcpdtable[i].def[j], rv->alen)) ok = 1;
+ }
+ }
+ if (ok == 0) {
+ (*words)[wnum] = NULL;
+ if (w) *words = NULL;
+ return 0;
+ }
+
+ for (i = 0; i < numdefcpd; i++) {
+ signed short pp = 0; // pattern position
+ signed short wp = 0; // "words" position
+ int ok2;
+ ok = 1;
+ ok2 = 1;
+ do {
+ while ((pp < defcpdtable[i].len) && (wp <= wnum)) {
+ if (((pp+1) < defcpdtable[i].len) &&
+ ((defcpdtable[i].def[pp+1] == '*') || (defcpdtable[i].def[pp+1] == '?'))) {
+ int wend = (defcpdtable[i].def[pp+1] == '?') ? wp : wnum;
+ ok2 = 1;
+ pp+=2;
+ btpp[bt] = pp;
+ btwp[bt] = wp;
+ while (wp <= wend) {
+ if (!(*words)[wp]->alen ||
+ !TESTAFF((*words)[wp]->astr, defcpdtable[i].def[pp-2], (*words)[wp]->alen)) {
+ ok2 = 0;
+ break;
+ }
+ wp++;
+ }
+ if (wp <= wnum) ok2 = 0;
+ btnum[bt] = wp - btwp[bt];
+ if (btnum[bt] > 0) bt++;
+ if (ok2) break;
+ } else {
+ ok2 = 1;
+ if (!(*words)[wp] || !(*words)[wp]->alen ||
+ !TESTAFF((*words)[wp]->astr, defcpdtable[i].def[pp], (*words)[wp]->alen)) {
+ ok = 0;
+ break;
+ }
+ pp++;
+ wp++;
+ if ((defcpdtable[i].len == pp) && !(wp > wnum)) ok = 0;
+ }
+ }
+ if (ok && ok2) {
+ int r = pp;
+ while ((defcpdtable[i].len > r) && ((r+1) < defcpdtable[i].len) &&
+ ((defcpdtable[i].def[r+1] == '*') || (defcpdtable[i].def[r+1] == '?'))) r+=2;
+ if (defcpdtable[i].len <= r) return 1;
+ }
+ // backtrack
+ if (bt) do {
+ ok = 1;
+ btnum[bt - 1]--;
+ pp = btpp[bt - 1];
+ wp = btwp[bt - 1] + (signed short) btnum[bt - 1];
+ } while ((btnum[bt - 1] < 0) && --bt);
+ } while (bt);
+
+ if (ok && ok2 && (!all || (defcpdtable[i].len <= pp))) return 1;
+
+ // check zero ending
+ while (ok && ok2 && (defcpdtable[i].len > pp) && ((pp+1) < defcpdtable[i].len) &&
+ ((defcpdtable[i].def[pp+1] == '*') || (defcpdtable[i].def[pp+1] == '?'))) pp+=2;
+ if (ok && ok2 && (defcpdtable[i].len <= pp)) return 1;
+ }
+ (*words)[wnum] = NULL;
+ if (w) *words = NULL;
+ return 0;
+}
+
+inline int AffixMgr::candidate_check(const char * word, int len)
+{
+ struct hentry * rv=NULL;
+
+ rv = lookup(word);
+ if (rv) return 1;
+
+// rv = prefix_check(word,len,1);
+// if (rv) return 1;
+
+ rv = affix_check(word,len);
+ if (rv) return 1;
+ return 0;
+}
+
+// calculate number of syllable for compound-checking
+short AffixMgr::get_syllable(const char * word, int wlen)
+{
+ if (cpdmaxsyllable==0) return 0;
+
+ short num=0;
+
+ if (!utf8) {
+ for (int i=0; i<wlen; i++) {
+ if (strchr(cpdvowels, word[i])) num++;
+ }
+ } else if (cpdvowels_utf16) {
+ w_char w[MAXWORDUTF8LEN];
+ int i = u8_u16(w, MAXWORDUTF8LEN, word);
+ for (; i > 0; i--) {
+ if (flag_bsearch((unsigned short *) cpdvowels_utf16,
+ ((unsigned short *) w)[i - 1], cpdvowels_utf16_len)) num++;
+ }
+ }
+ return num;
+}
+
+void AffixMgr::setcminmax(int * cmin, int * cmax, const char * word, int len) {
+ if (utf8) {
+ int i;
+ for (*cmin = 0, i = 0; (i < cpdmin) && word[*cmin]; i++) {
+ for ((*cmin)++; (word[*cmin] & 0xc0) == 0x80; (*cmin)++);
+ }
+ for (*cmax = len, i = 0; (i < (cpdmin - 1)) && *cmax; i++) {
+ for ((*cmax)--; (word[*cmax] & 0xc0) == 0x80; (*cmax)--);
+ }
+ } else {
+ *cmin = cpdmin;
+ *cmax = len - cpdmin + 1;
+ }
+}
+
+
+// check if compound word is correctly spelled
+// hu_mov_rule = spec. Hungarian rule (XXX)
+struct hentry * AffixMgr::compound_check(const char * word, int len,
+ short wordnum, short numsyllable, short maxwordnum, short wnum, hentry ** words = NULL,
+ char hu_mov_rule = 0, char is_sug = 0, int * info = NULL)
+{
+ int i;
+ short oldnumsyllable, oldnumsyllable2, oldwordnum, oldwordnum2;
+ struct hentry * rv = NULL;
+ struct hentry * rv_first;
+ struct hentry * rwords[MAXWORDLEN]; // buffer for COMPOUND pattern checking
+ char st [MAXWORDUTF8LEN + 4];
+ char ch = '\0';
+ int cmin;
+ int cmax;
+ int striple = 0;
+ int scpd = 0;
+ int soldi = 0;
+ int oldcmin = 0;
+ int oldcmax = 0;
+ int oldlen = 0;
+ int checkedstriple = 0;
+ int onlycpdrule;
+ int affixed = 0;
+ hentry ** oldwords = words;
+
+ int checked_prefix;
+
+ setcminmax(&cmin, &cmax, word, len);
+
+ strcpy(st, word);
+
+ for (i = cmin; i < cmax; i++) {
+ // go to end of the UTF-8 character
+ if (utf8) {
+ for (; (st[i] & 0xc0) == 0x80; i++);
+ if (i >= cmax) return NULL;
+ }
+
+ words = oldwords;
+ onlycpdrule = (words) ? 1 : 0;
+
+ do { // onlycpdrule loop
+
+ oldnumsyllable = numsyllable;
+ oldwordnum = wordnum;
+ checked_prefix = 0;
+
+
+ do { // simplified checkcompoundpattern loop
+
+ if (scpd > 0) {
+ for (; scpd <= numcheckcpd && (!checkcpdtable[scpd-1].pattern3 ||
+ strncmp(word + i, checkcpdtable[scpd-1].pattern3, strlen(checkcpdtable[scpd-1].pattern3)) != 0); scpd++);
+
+ if (scpd > numcheckcpd) break; // break simplified checkcompoundpattern loop
+ strcpy(st + i, checkcpdtable[scpd-1].pattern);
+ soldi = i;
+ i += strlen(checkcpdtable[scpd-1].pattern);
+ strcpy(st + i, checkcpdtable[scpd-1].pattern2);
+ strcpy(st + i + strlen(checkcpdtable[scpd-1].pattern2), word + soldi + strlen(checkcpdtable[scpd-1].pattern3));
+
+ oldlen = len;
+ len += strlen(checkcpdtable[scpd-1].pattern) + strlen(checkcpdtable[scpd-1].pattern2) - strlen(checkcpdtable[scpd-1].pattern3);
+ oldcmin = cmin;
+ oldcmax = cmax;
+ setcminmax(&cmin, &cmax, st, len);
+
+ cmax = len - cpdmin + 1;
+ }
+
+ ch = st[i];
+ st[i] = '\0';
+
+ sfx = NULL;
+ pfx = NULL;
+
+ // FIRST WORD
+
+ affixed = 1;
+ rv = lookup(st); // perhaps without prefix
+
+ // search homonym with compound flag
+ while ((rv) && !hu_mov_rule &&
+ ((needaffix && TESTAFF(rv->astr, needaffix, rv->alen)) ||
+ !((compoundflag && !words && !onlycpdrule && TESTAFF(rv->astr, compoundflag, rv->alen)) ||
+ (compoundbegin && !wordnum && !onlycpdrule &&
+ TESTAFF(rv->astr, compoundbegin, rv->alen)) ||
+ (compoundmiddle && wordnum && !words && !onlycpdrule &&
+ TESTAFF(rv->astr, compoundmiddle, rv->alen)) ||
+ (numdefcpd && onlycpdrule &&
+ ((!words && !wordnum && defcpd_check(&words, wnum, rv, (hentry **) &rwords, 0)) ||
+ (words && defcpd_check(&words, wnum, rv, (hentry **) &rwords, 0))))) ||
+ (scpd != 0 && checkcpdtable[scpd-1].cond != FLAG_NULL &&
+ !TESTAFF(rv->astr, checkcpdtable[scpd-1].cond, rv->alen)))
+ ) {
+ rv = rv->next_homonym;
+ }
+
+ if (rv) affixed = 0;
+
+ if (!rv) {
+ if (onlycpdrule) break;
+ if (compoundflag &&
+ !(rv = prefix_check(st, i, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN, compoundflag))) {
+ if ((rv = suffix_check(st, i, 0, NULL, NULL, 0, NULL,
+ FLAG_NULL, compoundflag, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN)) && !hu_mov_rule &&
+ sfx->getCont() &&
+ ((compoundforbidflag && TESTAFF(sfx->getCont(), compoundforbidflag,
+ sfx->getContLen())) || (compoundend &&
+ TESTAFF(sfx->getCont(), compoundend,
+ sfx->getContLen())))) {
+ rv = NULL;
+ }
+ }
+
+ if (rv ||
+ (((wordnum == 0) && compoundbegin &&
+ ((rv = suffix_check(st, i, 0, NULL, NULL, 0, NULL, FLAG_NULL, compoundbegin, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN)) ||
+ (rv = prefix_check(st, i, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN, compoundbegin)))) ||
+ ((wordnum > 0) && compoundmiddle &&
+ ((rv = suffix_check(st, i, 0, NULL, NULL, 0, NULL, FLAG_NULL, compoundmiddle, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN)) ||
+ (rv = prefix_check(st, i, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN, compoundmiddle)))))
+ ) checked_prefix = 1;
+ // else check forbiddenwords and needaffix
+ } else if (rv->astr && (TESTAFF(rv->astr, forbiddenword, rv->alen) ||
+ TESTAFF(rv->astr, needaffix, rv->alen) ||
+ TESTAFF(rv->astr, ONLYUPCASEFLAG, rv->alen) ||
+ (is_sug && nosuggest && TESTAFF(rv->astr, nosuggest, rv->alen))
+ )) {
+ st[i] = ch;
+ //continue;
+ break;
+ }
+
+ // check non_compound flag in suffix and prefix
+ if ((rv) && !hu_mov_rule &&
+ ((pfx && pfx->getCont() &&
+ TESTAFF(pfx->getCont(), compoundforbidflag,
+ pfx->getContLen())) ||
+ (sfx && sfx->getCont() &&
+ TESTAFF(sfx->getCont(), compoundforbidflag,
+ sfx->getContLen())))) {
+ rv = NULL;
+ }
+
+ // check compoundend flag in suffix and prefix
+ if ((rv) && !checked_prefix && compoundend && !hu_mov_rule &&
+ ((pfx && pfx->getCont() &&
+ TESTAFF(pfx->getCont(), compoundend,
+ pfx->getContLen())) ||
+ (sfx && sfx->getCont() &&
+ TESTAFF(sfx->getCont(), compoundend,
+ sfx->getContLen())))) {
+ rv = NULL;
+ }
+
+ // check compoundmiddle flag in suffix and prefix
+ if ((rv) && !checked_prefix && (wordnum==0) && compoundmiddle && !hu_mov_rule &&
+ ((pfx && pfx->getCont() &&
+ TESTAFF(pfx->getCont(), compoundmiddle,
+ pfx->getContLen())) ||
+ (sfx && sfx->getCont() &&
+ TESTAFF(sfx->getCont(), compoundmiddle,
+ sfx->getContLen())))) {
+ rv = NULL;
+ }
+
+ // check forbiddenwords
+ if ((rv) && (rv->astr) && (TESTAFF(rv->astr, forbiddenword, rv->alen) ||
+ TESTAFF(rv->astr, ONLYUPCASEFLAG, rv->alen) ||
+ (is_sug && nosuggest && TESTAFF(rv->astr, nosuggest, rv->alen)))) {
+ return NULL;
+ }
+
+ // increment word number, if the second root has a compoundroot flag
+ if ((rv) && compoundroot &&
+ (TESTAFF(rv->astr, compoundroot, rv->alen))) {
+ wordnum++;
+ }
+
+ // first word is acceptable in compound words?
+ if (((rv) &&
+ ( checked_prefix || (words && words[wnum]) ||
+ (compoundflag && TESTAFF(rv->astr, compoundflag, rv->alen)) ||
+ ((oldwordnum == 0) && compoundbegin && TESTAFF(rv->astr, compoundbegin, rv->alen)) ||
+ ((oldwordnum > 0) && compoundmiddle && TESTAFF(rv->astr, compoundmiddle, rv->alen))// ||
+// (numdefcpd && )
+
+// LANG_hu section: spec. Hungarian rule
+ || ((langnum == LANG_hu) && hu_mov_rule && (
+ TESTAFF(rv->astr, 'F', rv->alen) || // XXX hardwired Hungarian dictionary codes
+ TESTAFF(rv->astr, 'G', rv->alen) ||
+ TESTAFF(rv->astr, 'H', rv->alen)
+ )
+ )
+// END of LANG_hu section
+ ) &&
+ (
+ // test CHECKCOMPOUNDPATTERN conditions
+ scpd == 0 || checkcpdtable[scpd-1].cond == FLAG_NULL ||
+ TESTAFF(rv->astr, checkcpdtable[scpd-1].cond, rv->alen)
+ )
+ && ! (( checkcompoundtriple && scpd == 0 && !words && // test triple letters
+ (word[i-1]==word[i]) && (
+ ((i>1) && (word[i-1]==word[i-2])) ||
+ ((word[i-1]==word[i+1])) // may be word[i+1] == '\0'
+ )
+ ) ||
+ (
+ checkcompoundcase && scpd == 0 && !words && cpdcase_check(word, i)
+ ))
+ )
+// LANG_hu section: spec. Hungarian rule
+ || ((!rv) && (langnum == LANG_hu) && hu_mov_rule && (rv = affix_check(st,i)) &&
+ (sfx && sfx->getCont() && ( // XXX hardwired Hungarian dic. codes
+ TESTAFF(sfx->getCont(), (unsigned short) 'x', sfx->getContLen()) ||
+ TESTAFF(sfx->getCont(), (unsigned short) '%', sfx->getContLen())
+ )
+ )
+ )
+ ) { // first word is ok condition
+
+// LANG_hu section: spec. Hungarian rule
+ if (langnum == LANG_hu) {
+ // calculate syllable number of the word
+ numsyllable += get_syllable(st, i);
+ // + 1 word, if syllable number of the prefix > 1 (hungarian convention)
+ if (pfx && (get_syllable(pfx->getKey(),strlen(pfx->getKey())) > 1)) wordnum++;
+ }
+// END of LANG_hu section
+
+ // NEXT WORD(S)
+ rv_first = rv;
+ st[i] = ch;
+
+ do { // striple loop
+
+ // check simplifiedtriple
+ if (simplifiedtriple) {
+ if (striple) {
+ checkedstriple = 1;
+ i--; // check "fahrt" instead of "ahrt" in "Schiffahrt"
+ } else if (i > 2 && *(word+i - 1) == *(word + i - 2)) striple = 1;
+ }
+
+ rv = lookup((st+i)); // perhaps without prefix
+
+ // search homonym with compound flag
+ while ((rv) && ((needaffix && TESTAFF(rv->astr, needaffix, rv->alen)) ||
+ !((compoundflag && !words && TESTAFF(rv->astr, compoundflag, rv->alen)) ||
+ (compoundend && !words && TESTAFF(rv->astr, compoundend, rv->alen)) ||
+ (numdefcpd && words && defcpd_check(&words, wnum + 1, rv, NULL,1))) ||
+ (scpd != 0 && checkcpdtable[scpd-1].cond2 != FLAG_NULL &&
+ !TESTAFF(rv->astr, checkcpdtable[scpd-1].cond2, rv->alen))
+ )) {
+ rv = rv->next_homonym;
+ }
+
+ // check FORCEUCASE
+ if (rv && forceucase && (rv) &&
+ (TESTAFF(rv->astr, forceucase, rv->alen)) && !(info && *info & SPELL_ORIGCAP)) rv = NULL;
+
+ if (rv && words && words[wnum + 1]) return rv_first;
+
+ oldnumsyllable2 = numsyllable;
+ oldwordnum2 = wordnum;
+
+
+// LANG_hu section: spec. Hungarian rule, XXX hardwired dictionary code
+ if ((rv) && (langnum == LANG_hu) && (TESTAFF(rv->astr, 'I', rv->alen)) && !(TESTAFF(rv->astr, 'J', rv->alen))) {
+ numsyllable--;
+ }
+// END of LANG_hu section
+
+ // increment word number, if the second root has a compoundroot flag
+ if ((rv) && (compoundroot) &&
+ (TESTAFF(rv->astr, compoundroot, rv->alen))) {
+ wordnum++;
+ }
+
+ // check forbiddenwords
+ if ((rv) && (rv->astr) && (TESTAFF(rv->astr, forbiddenword, rv->alen) ||
+ TESTAFF(rv->astr, ONLYUPCASEFLAG, rv->alen) ||
+ (is_sug && nosuggest && TESTAFF(rv->astr, nosuggest, rv->alen)))) return NULL;
+
+ // second word is acceptable, as a root?
+ // hungarian conventions: compounding is acceptable,
+ // when compound forms consist of 2 words, or if more,
+ // then the syllable number of root words must be 6, or lesser.
+
+ if ((rv) && (
+ (compoundflag && TESTAFF(rv->astr, compoundflag, rv->alen)) ||
+ (compoundend && TESTAFF(rv->astr, compoundend, rv->alen))
+ )
+ && (
+ ((cpdwordmax==-1) || (wordnum+1<cpdwordmax)) ||
+ ((cpdmaxsyllable!=0) &&
+ (numsyllable + get_syllable(HENTRY_WORD(rv), rv->clen)<=cpdmaxsyllable))
+ ) &&
+ (
+ // test CHECKCOMPOUNDPATTERN
+ !numcheckcpd || scpd != 0 || !cpdpat_check(word, i, rv_first, rv, 0)
+ ) &&
+ (
+ (!checkcompounddup || (rv != rv_first))
+ )
+ // test CHECKCOMPOUNDPATTERN conditions
+ && (scpd == 0 || checkcpdtable[scpd-1].cond2 == FLAG_NULL ||
+ TESTAFF(rv->astr, checkcpdtable[scpd-1].cond2, rv->alen))
+ )
+ {
+ // forbid compound word, if it is a non compound word with typical fault
+ if (checkcompoundrep && cpdrep_check(word,len)) return NULL;
+ return rv_first;
+ }
+
+ numsyllable = oldnumsyllable2;
+ wordnum = oldwordnum2;
+
+ // perhaps second word has prefix or/and suffix
+ sfx = NULL;
+ sfxflag = FLAG_NULL;
+ rv = (compoundflag && !onlycpdrule) ? affix_check((word+i),strlen(word+i), compoundflag, IN_CPD_END) : NULL;
+ if (!rv && compoundend && !onlycpdrule) {
+ sfx = NULL;
+ pfx = NULL;
+ rv = affix_check((word+i),strlen(word+i), compoundend, IN_CPD_END);
+ }
+
+ if (!rv && numdefcpd && words) {
+ rv = affix_check((word+i),strlen(word+i), 0, IN_CPD_END);
+ if (rv && defcpd_check(&words, wnum + 1, rv, NULL, 1)) return rv_first;
+ rv = NULL;
+ }
+
+ // test CHECKCOMPOUNDPATTERN conditions (allowed forms)
+ if (rv && !(scpd == 0 || checkcpdtable[scpd-1].cond2 == FLAG_NULL ||
+ TESTAFF(rv->astr, checkcpdtable[scpd-1].cond2, rv->alen))) rv = NULL;
+
+ // test CHECKCOMPOUNDPATTERN conditions (forbidden compounds)
+ if (rv && numcheckcpd && scpd == 0 && cpdpat_check(word, i, rv_first, rv, affixed)) rv = NULL;
+
+ // check non_compound flag in suffix and prefix
+ if ((rv) &&
+ ((pfx && pfx->getCont() &&
+ TESTAFF(pfx->getCont(), compoundforbidflag,
+ pfx->getContLen())) ||
+ (sfx && sfx->getCont() &&
+ TESTAFF(sfx->getCont(), compoundforbidflag,
+ sfx->getContLen())))) {
+ rv = NULL;
+ }
+
+ // check FORCEUCASE
+ if (rv && forceucase && (rv) &&
+ (TESTAFF(rv->astr, forceucase, rv->alen)) && !(info && *info & SPELL_ORIGCAP)) rv = NULL;
+
+ // check forbiddenwords
+ if ((rv) && (rv->astr) && (TESTAFF(rv->astr, forbiddenword, rv->alen) ||
+ TESTAFF(rv->astr, ONLYUPCASEFLAG, rv->alen) ||
+ (is_sug && nosuggest && TESTAFF(rv->astr, nosuggest, rv->alen)))) return NULL;
+
+ // pfxappnd = prefix of word+i, or NULL
+ // calculate syllable number of prefix.
+ // hungarian convention: when syllable number of prefix is more,
+ // than 1, the prefix+word counts as two words.
+
+ if (langnum == LANG_hu) {
+ // calculate syllable number of the word
+ numsyllable += get_syllable(word + i, strlen(word + i));
+
+ // - affix syllable num.
+ // XXX only second suffix (inflections, not derivations)
+ if (sfxappnd) {
+ char * tmp = myrevstrdup(sfxappnd);
+ numsyllable -= get_syllable(tmp, strlen(tmp));
+ free(tmp);
+ }
+
+ // + 1 word, if syllable number of the prefix > 1 (hungarian convention)
+ if (pfx && (get_syllable(pfx->getKey(),strlen(pfx->getKey())) > 1)) wordnum++;
+
+ // increment syllable num, if last word has a SYLLABLENUM flag
+ // and the suffix is beginning `s'
+
+ if (cpdsyllablenum) {
+ switch (sfxflag) {
+ case 'c': { numsyllable+=2; break; }
+ case 'J': { numsyllable += 1; break; }
+ case 'I': { if (rv && TESTAFF(rv->astr, 'J', rv->alen)) numsyllable += 1; break; }
+ }
+ }
+ }
+
+ // increment word number, if the second word has a compoundroot flag
+ if ((rv) && (compoundroot) &&
+ (TESTAFF(rv->astr, compoundroot, rv->alen))) {
+ wordnum++;
+ }
+
+ // second word is acceptable, as a word with prefix or/and suffix?
+ // hungarian conventions: compounding is acceptable,
+ // when compound forms consist 2 word, otherwise
+ // the syllable number of root words is 6, or lesser.
+ if ((rv) &&
+ (
+ ((cpdwordmax == -1) || (wordnum + 1 < cpdwordmax)) ||
+ ((cpdmaxsyllable != 0) &&
+ (numsyllable <= cpdmaxsyllable))
+ )
+ && (
+ (!checkcompounddup || (rv != rv_first))
+ )) {
+ // forbid compound word, if it is a non compound word with typical fault
+ if (checkcompoundrep && cpdrep_check(word, len)) return NULL;
+ return rv_first;
+ }
+
+ numsyllable = oldnumsyllable2;
+ wordnum = oldwordnum2;
+
+ // perhaps second word is a compound word (recursive call)
+ if (wordnum < maxwordnum) {
+ rv = compound_check((st+i),strlen(st+i), wordnum+1,
+ numsyllable, maxwordnum, wnum + 1, words, 0, is_sug, info);
+
+ if (rv && numcheckcpd && ((scpd == 0 && cpdpat_check(word, i, rv_first, rv, affixed)) ||
+ (scpd != 0 && !cpdpat_check(word, i, rv_first, rv, affixed)))) rv = NULL;
+ } else {
+ rv=NULL;
+ }
+ if (rv) {
+ // forbid compound word, if it is a non compound word with typical fault
+ if (checkcompoundrep || forbiddenword) {
+ struct hentry * rv2 = NULL;
+
+ if (checkcompoundrep && cpdrep_check(word, len)) return NULL;
+
+ // check first part
+ if (strncmp(rv->word, word + i, rv->blen) == 0) {
+ char r = *(st + i + rv->blen);
+ *(st + i + rv->blen) = '\0';
+
+ if (checkcompoundrep && cpdrep_check(st, i + rv->blen)) {
+ *(st + i + rv->blen) = r;
+ continue;
+ }
+
+ if (forbiddenword) {
+ rv2 = lookup(word);
+ if (!rv2) rv2 = affix_check(word, len);
+ if (rv2 && rv2->astr && TESTAFF(rv2->astr, forbiddenword, rv2->alen) &&
+ (strncmp(rv2->word, st, i + rv->blen) == 0)) {
+ return NULL;
+ }
+ }
+ *(st + i + rv->blen) = r;
+ }
+ }
+ return rv_first;
+ }
+ } while (striple && !checkedstriple); // end of striple loop
+
+ if (checkedstriple) {
+ i++;
+ checkedstriple = 0;
+ striple = 0;
+ }
+
+ } // first word is ok condition
+
+ if (soldi != 0) {
+ i = soldi;
+ soldi = 0;
+ len = oldlen;
+ cmin = oldcmin;
+ cmax = oldcmax;
+ }
+ scpd++;
+
+
+ } while (!onlycpdrule && simplifiedcpd && scpd <= numcheckcpd); // end of simplifiedcpd loop
+
+ scpd = 0;
+ wordnum = oldwordnum;
+ numsyllable = oldnumsyllable;
+
+ if (soldi != 0) {
+ i = soldi;
+ strcpy(st, word); // XXX add more optim.
+ soldi = 0;
+ } else st[i] = ch;
+
+ } while (numdefcpd && oldwordnum == 0 && !onlycpdrule && (onlycpdrule = 1)); // end of onlycpd loop
+
+ }
+
+ return NULL;
+}
+
+// check if compound word is correctly spelled
+// hu_mov_rule = spec. Hungarian rule (XXX)
+int AffixMgr::compound_check_morph(const char * word, int len,
+ short wordnum, short numsyllable, short maxwordnum, short wnum, hentry ** words,
+ char hu_mov_rule = 0, char ** result = NULL, char * partresult = NULL)
+{
+ int i;
+ short oldnumsyllable, oldnumsyllable2, oldwordnum, oldwordnum2;
+ int ok = 0;
+
+ struct hentry * rv = NULL;
+ struct hentry * rv_first;
+ struct hentry * rwords[MAXWORDLEN]; // buffer for COMPOUND pattern checking
+ char st [MAXWORDUTF8LEN + 4];
+ char ch;
+
+ int checked_prefix;
+ char presult[MAXLNLEN];
+
+ int cmin;
+ int cmax;
+
+ int onlycpdrule;
+ int affixed = 0;
+ hentry ** oldwords = words;
+
+ setcminmax(&cmin, &cmax, word, len);
+
+ strcpy(st, word);
+
+ for (i = cmin; i < cmax; i++) {
+ oldnumsyllable = numsyllable;
+ oldwordnum = wordnum;
+ checked_prefix = 0;
+
+ // go to end of the UTF-8 character
+ if (utf8) {
+ for (; (st[i] & 0xc0) == 0x80; i++);
+ if (i >= cmax) return 0;
+ }
+
+ words = oldwords;
+ onlycpdrule = (words) ? 1 : 0;
+
+ do { // onlycpdrule loop
+
+ oldnumsyllable = numsyllable;
+ oldwordnum = wordnum;
+ checked_prefix = 0;
+
+ ch = st[i];
+ st[i] = '\0';
+ sfx = NULL;
+
+ // FIRST WORD
+
+ affixed = 1;
+
+ *presult = '\0';
+ if (partresult) mystrcat(presult, partresult, MAXLNLEN);
+
+ rv = lookup(st); // perhaps without prefix
+
+ // search homonym with compound flag
+ while ((rv) && !hu_mov_rule &&
+ ((needaffix && TESTAFF(rv->astr, needaffix, rv->alen)) ||
+ !((compoundflag && !words && !onlycpdrule && TESTAFF(rv->astr, compoundflag, rv->alen)) ||
+ (compoundbegin && !wordnum && !onlycpdrule &&
+ TESTAFF(rv->astr, compoundbegin, rv->alen)) ||
+ (compoundmiddle && wordnum && !words && !onlycpdrule &&
+ TESTAFF(rv->astr, compoundmiddle, rv->alen)) ||
+ (numdefcpd && onlycpdrule &&
+ ((!words && !wordnum && defcpd_check(&words, wnum, rv, (hentry **) &rwords, 0)) ||
+ (words && defcpd_check(&words, wnum, rv, (hentry **) &rwords, 0))))
+ ))) {
+ rv = rv->next_homonym;
+ }
+
+ if (rv) affixed = 0;
+
+ if (rv) {
+ sprintf(presult + strlen(presult), "%c%s%s", MSEP_FLD, MORPH_PART, st);
+ if (!HENTRY_FIND(rv, MORPH_STEM)) {
+ sprintf(presult + strlen(presult), "%c%s%s", MSEP_FLD, MORPH_STEM, st);
+ }
+ // store the pointer of the hash entry
+// sprintf(presult + strlen(presult), "%c%s%p", MSEP_FLD, MORPH_HENTRY, rv);
+ if (HENTRY_DATA(rv)) {
+ sprintf(presult + strlen(presult), "%c%s", MSEP_FLD, HENTRY_DATA2(rv));
+ }
+ }
+
+ if (!rv) {
+ if (onlycpdrule) break;
+ if (compoundflag &&
+ !(rv = prefix_check(st, i, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN, compoundflag))) {
+ if ((rv = suffix_check(st, i, 0, NULL, NULL, 0, NULL,
+ FLAG_NULL, compoundflag, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN)) && !hu_mov_rule &&
+ sfx->getCont() &&
+ ((compoundforbidflag && TESTAFF(sfx->getCont(), compoundforbidflag,
+ sfx->getContLen())) || (compoundend &&
+ TESTAFF(sfx->getCont(), compoundend,
+ sfx->getContLen())))) {
+ rv = NULL;
+ }
+ }
+
+ if (rv ||
+ (((wordnum == 0) && compoundbegin &&
+ ((rv = suffix_check(st, i, 0, NULL, NULL, 0, NULL, FLAG_NULL, compoundbegin, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN)) ||
+ (rv = prefix_check(st, i, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN, compoundbegin)))) ||
+ ((wordnum > 0) && compoundmiddle &&
+ ((rv = suffix_check(st, i, 0, NULL, NULL, 0, NULL, FLAG_NULL, compoundmiddle, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN)) ||
+ (rv = prefix_check(st, i, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN, compoundmiddle)))))
+ ) {
+ // char * p = prefix_check_morph(st, i, 0, compound);
+ char * p = NULL;
+ if (compoundflag) p = affix_check_morph(st, i, compoundflag);
+ if (!p || (*p == '\0')) {
+ if (p) free(p);
+ p = NULL;
+ if ((wordnum == 0) && compoundbegin) {
+ p = affix_check_morph(st, i, compoundbegin);
+ } else if ((wordnum > 0) && compoundmiddle) {
+ p = affix_check_morph(st, i, compoundmiddle);
+ }
+ }
+ if (p && (*p != '\0')) {
+ sprintf(presult + strlen(presult), "%c%s%s%s", MSEP_FLD,
+ MORPH_PART, st, line_uniq_app(&p, MSEP_REC));
+ }
+ if (p) free(p);
+ checked_prefix = 1;
+ }
+ // else check forbiddenwords
+ } else if (rv->astr && (TESTAFF(rv->astr, forbiddenword, rv->alen) ||
+ TESTAFF(rv->astr, ONLYUPCASEFLAG, rv->alen) ||
+ TESTAFF(rv->astr, needaffix, rv->alen))) {
+ st[i] = ch;
+ continue;
+ }
+
+ // check non_compound flag in suffix and prefix
+ if ((rv) && !hu_mov_rule &&
+ ((pfx && pfx->getCont() &&
+ TESTAFF(pfx->getCont(), compoundforbidflag,
+ pfx->getContLen())) ||
+ (sfx && sfx->getCont() &&
+ TESTAFF(sfx->getCont(), compoundforbidflag,
+ sfx->getContLen())))) {
+ continue;
+ }
+
+ // check compoundend flag in suffix and prefix
+ if ((rv) && !checked_prefix && compoundend && !hu_mov_rule &&
+ ((pfx && pfx->getCont() &&
+ TESTAFF(pfx->getCont(), compoundend,
+ pfx->getContLen())) ||
+ (sfx && sfx->getCont() &&
+ TESTAFF(sfx->getCont(), compoundend,
+ sfx->getContLen())))) {
+ continue;
+ }
+
+ // check compoundmiddle flag in suffix and prefix
+ if ((rv) && !checked_prefix && (wordnum==0) && compoundmiddle && !hu_mov_rule &&
+ ((pfx && pfx->getCont() &&
+ TESTAFF(pfx->getCont(), compoundmiddle,
+ pfx->getContLen())) ||
+ (sfx && sfx->getCont() &&
+ TESTAFF(sfx->getCont(), compoundmiddle,
+ sfx->getContLen())))) {
+ rv = NULL;
+ }
+
+ // check forbiddenwords
+ if ((rv) && (rv->astr) && (TESTAFF(rv->astr, forbiddenword, rv->alen)
+ || TESTAFF(rv->astr, ONLYUPCASEFLAG, rv->alen))) continue;
+
+ // increment word number, if the second root has a compoundroot flag
+ if ((rv) && (compoundroot) &&
+ (TESTAFF(rv->astr, compoundroot, rv->alen))) {
+ wordnum++;
+ }
+
+ // first word is acceptable in compound words?
+ if (((rv) &&
+ ( checked_prefix || (words && words[wnum]) ||
+ (compoundflag && TESTAFF(rv->astr, compoundflag, rv->alen)) ||
+ ((oldwordnum == 0) && compoundbegin && TESTAFF(rv->astr, compoundbegin, rv->alen)) ||
+ ((oldwordnum > 0) && compoundmiddle && TESTAFF(rv->astr, compoundmiddle, rv->alen))
+// LANG_hu section: spec. Hungarian rule
+ || ((langnum == LANG_hu) && // hu_mov_rule
+ hu_mov_rule && (
+ TESTAFF(rv->astr, 'F', rv->alen) ||
+ TESTAFF(rv->astr, 'G', rv->alen) ||
+ TESTAFF(rv->astr, 'H', rv->alen)
+ )
+ )
+// END of LANG_hu section
+ )
+ && ! (( checkcompoundtriple && !words && // test triple letters
+ (word[i-1]==word[i]) && (
+ ((i>1) && (word[i-1]==word[i-2])) ||
+ ((word[i-1]==word[i+1])) // may be word[i+1] == '\0'
+ )
+ ) ||
+ (
+ // test CHECKCOMPOUNDPATTERN
+ numcheckcpd && !words && cpdpat_check(word, i, rv, NULL, affixed)
+ ) ||
+ (
+ checkcompoundcase && !words && cpdcase_check(word, i)
+ ))
+ )
+// LANG_hu section: spec. Hungarian rule
+ || ((!rv) && (langnum == LANG_hu) && hu_mov_rule && (rv = affix_check(st,i)) &&
+ (sfx && sfx->getCont() && (
+ TESTAFF(sfx->getCont(), (unsigned short) 'x', sfx->getContLen()) ||
+ TESTAFF(sfx->getCont(), (unsigned short) '%', sfx->getContLen())
+ )
+ )
+ )
+// END of LANG_hu section
+ ) {
+
+// LANG_hu section: spec. Hungarian rule
+ if (langnum == LANG_hu) {
+ // calculate syllable number of the word
+ numsyllable += get_syllable(st, i);
+
+ // + 1 word, if syllable number of the prefix > 1 (hungarian convention)
+ if (pfx && (get_syllable(pfx->getKey(),strlen(pfx->getKey())) > 1)) wordnum++;
+ }
+// END of LANG_hu section
+
+ // NEXT WORD(S)
+ rv_first = rv;
+ rv = lookup((word+i)); // perhaps without prefix
+
+ // search homonym with compound flag
+ while ((rv) && ((needaffix && TESTAFF(rv->astr, needaffix, rv->alen)) ||
+ !((compoundflag && !words && TESTAFF(rv->astr, compoundflag, rv->alen)) ||
+ (compoundend && !words && TESTAFF(rv->astr, compoundend, rv->alen)) ||
+ (numdefcpd && words && defcpd_check(&words, wnum + 1, rv, NULL,1))))) {
+ rv = rv->next_homonym;
+ }
+
+ if (rv && words && words[wnum + 1]) {
+ mystrcat(*result, presult, MAXLNLEN);
+ mystrcat(*result, " ", MAXLNLEN);
+ mystrcat(*result, MORPH_PART, MAXLNLEN);
+ mystrcat(*result, word+i, MAXLNLEN);
+ if (complexprefixes && HENTRY_DATA(rv)) mystrcat(*result, HENTRY_DATA2(rv), MAXLNLEN);
+ if (!HENTRY_FIND(rv, MORPH_STEM)) {
+ mystrcat(*result, " ", MAXLNLEN);
+ mystrcat(*result, MORPH_STEM, MAXLNLEN);
+ mystrcat(*result, HENTRY_WORD(rv), MAXLNLEN);
+ }
+ // store the pointer of the hash entry
+// sprintf(*result + strlen(*result), " %s%p", MORPH_HENTRY, rv);
+ if (!complexprefixes && HENTRY_DATA(rv)) {
+ mystrcat(*result, " ", MAXLNLEN);
+ mystrcat(*result, HENTRY_DATA2(rv), MAXLNLEN);
+ }
+ mystrcat(*result, "\n", MAXLNLEN);
+ ok = 1;
+ return 0;
+ }
+
+ oldnumsyllable2 = numsyllable;
+ oldwordnum2 = wordnum;
+
+// LANG_hu section: spec. Hungarian rule
+ if ((rv) && (langnum == LANG_hu) && (TESTAFF(rv->astr, 'I', rv->alen)) && !(TESTAFF(rv->astr, 'J', rv->alen))) {
+ numsyllable--;
+ }
+// END of LANG_hu section
+ // increment word number, if the second root has a compoundroot flag
+ if ((rv) && (compoundroot) &&
+ (TESTAFF(rv->astr, compoundroot, rv->alen))) {
+ wordnum++;
+ }
+
+ // check forbiddenwords
+ if ((rv) && (rv->astr) && (TESTAFF(rv->astr, forbiddenword, rv->alen) ||
+ TESTAFF(rv->astr, ONLYUPCASEFLAG, rv->alen))) {
+ st[i] = ch;
+ continue;
+ }
+
+ // second word is acceptable, as a root?
+ // hungarian conventions: compounding is acceptable,
+ // when compound forms consist of 2 words, or if more,
+ // then the syllable number of root words must be 6, or lesser.
+ if ((rv) && (
+ (compoundflag && TESTAFF(rv->astr, compoundflag, rv->alen)) ||
+ (compoundend && TESTAFF(rv->astr, compoundend, rv->alen))
+ )
+ && (
+ ((cpdwordmax==-1) || (wordnum+1<cpdwordmax)) ||
+ ((cpdmaxsyllable!=0) &&
+ (numsyllable+get_syllable(HENTRY_WORD(rv),rv->blen)<=cpdmaxsyllable))
+ )
+ && (
+ (!checkcompounddup || (rv != rv_first))
+ )
+ )
+ {
+ // bad compound word
+ mystrcat(*result, presult, MAXLNLEN);
+ mystrcat(*result, " ", MAXLNLEN);
+ mystrcat(*result, MORPH_PART, MAXLNLEN);
+ mystrcat(*result, word+i, MAXLNLEN);
+
+ if (HENTRY_DATA(rv)) {
+ if (complexprefixes) mystrcat(*result, HENTRY_DATA2(rv), MAXLNLEN);
+ if (! HENTRY_FIND(rv, MORPH_STEM)) {
+ mystrcat(*result, " ", MAXLNLEN);
+ mystrcat(*result, MORPH_STEM, MAXLNLEN);
+ mystrcat(*result, HENTRY_WORD(rv), MAXLNLEN);
+ }
+ // store the pointer of the hash entry
+// sprintf(*result + strlen(*result), " %s%p", MORPH_HENTRY, rv);
+ if (!complexprefixes) {
+ mystrcat(*result, " ", MAXLNLEN);
+ mystrcat(*result, HENTRY_DATA2(rv), MAXLNLEN);
+ }
+ }
+ mystrcat(*result, "\n", MAXLNLEN);
+ ok = 1;
+ }
+
+ numsyllable = oldnumsyllable2 ;
+ wordnum = oldwordnum2;
+
+ // perhaps second word has prefix or/and suffix
+ sfx = NULL;
+ sfxflag = FLAG_NULL;
+
+ if (compoundflag && !onlycpdrule) rv = affix_check((word+i),strlen(word+i), compoundflag); else rv = NULL;
+
+ if (!rv && compoundend && !onlycpdrule) {
+ sfx = NULL;
+ pfx = NULL;
+ rv = affix_check((word+i),strlen(word+i), compoundend);
+ }
+
+ if (!rv && numdefcpd && words) {
+ rv = affix_check((word+i),strlen(word+i), 0, IN_CPD_END);
+ if (rv && words && defcpd_check(&words, wnum + 1, rv, NULL, 1)) {
+ char * m = NULL;
+ if (compoundflag) m = affix_check_morph((word+i),strlen(word+i), compoundflag);
+ if ((!m || *m == '\0') && compoundend) {
+ if (m) free(m);
+ m = affix_check_morph((word+i),strlen(word+i), compoundend);
+ }
+ mystrcat(*result, presult, MAXLNLEN);
+ if (m || (*m != '\0')) {
+ sprintf(*result + strlen(*result), "%c%s%s%s", MSEP_FLD,
+ MORPH_PART, word + i, line_uniq_app(&m, MSEP_REC));
+ }
+ if (m) free(m);
+ mystrcat(*result, "\n", MAXLNLEN);
+ ok = 1;
+ }
+ }
+
+ // check non_compound flag in suffix and prefix
+ if ((rv) &&
+ ((pfx && pfx->getCont() &&
+ TESTAFF(pfx->getCont(), compoundforbidflag,
+ pfx->getContLen())) ||
+ (sfx && sfx->getCont() &&
+ TESTAFF(sfx->getCont(), compoundforbidflag,
+ sfx->getContLen())))) {
+ rv = NULL;
+ }
+
+ // check forbiddenwords
+ if ((rv) && (rv->astr) && (TESTAFF(rv->astr,forbiddenword,rv->alen) ||
+ TESTAFF(rv->astr, ONLYUPCASEFLAG, rv->alen))
+ && (! TESTAFF(rv->astr, needaffix, rv->alen))) {
+ st[i] = ch;
+ continue;
+ }
+
+ if (langnum == LANG_hu) {
+ // calculate syllable number of the word
+ numsyllable += get_syllable(word + i, strlen(word + i));
+
+ // - affix syllable num.
+ // XXX only second suffix (inflections, not derivations)
+ if (sfxappnd) {
+ char * tmp = myrevstrdup(sfxappnd);
+ numsyllable -= get_syllable(tmp, strlen(tmp));
+ free(tmp);
+ }
+
+ // + 1 word, if syllable number of the prefix > 1 (hungarian convention)
+ if (pfx && (get_syllable(pfx->getKey(),strlen(pfx->getKey())) > 1)) wordnum++;
+
+ // increment syllable num, if last word has a SYLLABLENUM flag
+ // and the suffix is beginning `s'
+
+ if (cpdsyllablenum) {
+ switch (sfxflag) {
+ case 'c': { numsyllable+=2; break; }
+ case 'J': { numsyllable += 1; break; }
+ case 'I': { if (rv && TESTAFF(rv->astr, 'J', rv->alen)) numsyllable += 1; break; }
+ }
+ }
+ }
+
+ // increment word number, if the second word has a compoundroot flag
+ if ((rv) && (compoundroot) &&
+ (TESTAFF(rv->astr, compoundroot, rv->alen))) {
+ wordnum++;
+ }
+ // second word is acceptable, as a word with prefix or/and suffix?
+ // hungarian conventions: compounding is acceptable,
+ // when compound forms consist 2 word, otherwise
+ // the syllable number of root words is 6, or lesser.
+ if ((rv) &&
+ (
+ ((cpdwordmax==-1) || (wordnum+1<cpdwordmax)) ||
+ ((cpdmaxsyllable!=0) &&
+ (numsyllable <= cpdmaxsyllable))
+ )
+ && (
+ (!checkcompounddup || (rv != rv_first))
+ )) {
+ char * m = NULL;
+ if (compoundflag) m = affix_check_morph((word+i),strlen(word+i), compoundflag);
+ if ((!m || *m == '\0') && compoundend) {
+ if (m) free(m);
+ m = affix_check_morph((word+i),strlen(word+i), compoundend);
+ }
+ mystrcat(*result, presult, MAXLNLEN);
+ if (m && (*m != '\0')) {
+ sprintf(*result + strlen(*result), "%c%s%s%s", MSEP_FLD,
+ MORPH_PART, word + i, line_uniq_app(&m, MSEP_REC));
+ }
+ if (m) free(m);
+ sprintf(*result + strlen(*result), "%c", MSEP_REC);
+ ok = 1;
+ }
+
+ numsyllable = oldnumsyllable2;
+ wordnum = oldwordnum2;
+
+ // perhaps second word is a compound word (recursive call)
+ if ((wordnum < maxwordnum) && (ok == 0)) {
+ compound_check_morph((word+i),strlen(word+i), wordnum+1,
+ numsyllable, maxwordnum, wnum + 1, words, 0, result, presult);
+ } else {
+ rv=NULL;
+ }
+ }
+ st[i] = ch;
+ wordnum = oldwordnum;
+ numsyllable = oldnumsyllable;
+
+ } while (numdefcpd && oldwordnum == 0 && !onlycpdrule && (onlycpdrule = 1)); // end of onlycpd loop
+
+ }
+ return 0;
+}
+
+ // return 1 if s1 (reversed) is a leading subset of end of s2
+/* inline int AffixMgr::isRevSubset(const char * s1, const char * end_of_s2, int len)
+ {
+ while ((len > 0) && *s1 && (*s1 == *end_of_s2)) {
+ s1++;
+ end_of_s2--;
+ len--;
+ }
+ return (*s1 == '\0');
+ }
+ */
+
+inline int AffixMgr::isRevSubset(const char * s1, const char * end_of_s2, int len)
+ {
+ while ((len > 0) && (*s1 != '\0') && ((*s1 == *end_of_s2) || (*s1 == '.'))) {
+ s1++;
+ end_of_s2--;
+ len--;
+ }
+ return (*s1 == '\0');
+ }
+
+// check word for suffixes
+
+struct hentry * AffixMgr::suffix_check (const char * word, int len,
+ int sfxopts, PfxEntry * ppfx, char ** wlst, int maxSug, int * ns,
+ const FLAG cclass, const FLAG needflag, char in_compound)
+{
+ struct hentry * rv = NULL;
+ PfxEntry* ep = ppfx;
+
+ // first handle the special case of 0 length suffixes
+ SfxEntry * se = sStart[0];
+
+ while (se) {
+ if (!cclass || se->getCont()) {
+ // suffixes are not allowed in beginning of compounds
+ if ((((in_compound != IN_CPD_BEGIN)) || // && !cclass
+ // except when signed with compoundpermitflag flag
+ (se->getCont() && compoundpermitflag &&
+ TESTAFF(se->getCont(),compoundpermitflag,se->getContLen()))) && (!circumfix ||
+ // no circumfix flag in prefix and suffix
+ ((!ppfx || !(ep->getCont()) || !TESTAFF(ep->getCont(),
+ circumfix, ep->getContLen())) &&
+ (!se->getCont() || !(TESTAFF(se->getCont(),circumfix,se->getContLen())))) ||
+ // circumfix flag in prefix AND suffix
+ ((ppfx && (ep->getCont()) && TESTAFF(ep->getCont(),
+ circumfix, ep->getContLen())) &&
+ (se->getCont() && (TESTAFF(se->getCont(),circumfix,se->getContLen()))))) &&
+ // fogemorpheme
+ (in_compound ||
+ !(se->getCont() && (TESTAFF(se->getCont(), onlyincompound, se->getContLen())))) &&
+ // needaffix on prefix or first suffix
+ (cclass ||
+ !(se->getCont() && TESTAFF(se->getCont(), needaffix, se->getContLen())) ||
+ (ppfx && !((ep->getCont()) &&
+ TESTAFF(ep->getCont(), needaffix,
+ ep->getContLen())))
+ )) {
+ rv = se->checkword(word,len, sfxopts, ppfx, wlst, maxSug, ns, (FLAG) cclass,
+ needflag, (in_compound ? 0 : onlyincompound));
+ if (rv) {
+ sfx=se; // BUG: sfx not stateless
+ return rv;
+ }
+ }
+ }
+ se = se->getNext();
+ }
+
+ // now handle the general case
+ if (len == 0) return NULL; // FULLSTRIP
+ unsigned char sp= *((const unsigned char *)(word + len - 1));
+ SfxEntry * sptr = sStart[sp];
+
+ while (sptr) {
+ if (isRevSubset(sptr->getKey(), word + len - 1, len)
+ ) {
+ // suffixes are not allowed in beginning of compounds
+ if ((((in_compound != IN_CPD_BEGIN)) || // && !cclass
+ // except when signed with compoundpermitflag flag
+ (sptr->getCont() && compoundpermitflag &&
+ TESTAFF(sptr->getCont(),compoundpermitflag,sptr->getContLen()))) && (!circumfix ||
+ // no circumfix flag in prefix and suffix
+ ((!ppfx || !(ep->getCont()) || !TESTAFF(ep->getCont(),
+ circumfix, ep->getContLen())) &&
+ (!sptr->getCont() || !(TESTAFF(sptr->getCont(),circumfix,sptr->getContLen())))) ||
+ // circumfix flag in prefix AND suffix
+ ((ppfx && (ep->getCont()) && TESTAFF(ep->getCont(),
+ circumfix, ep->getContLen())) &&
+ (sptr->getCont() && (TESTAFF(sptr->getCont(),circumfix,sptr->getContLen()))))) &&
+ // fogemorpheme
+ (in_compound ||
+ !((sptr->getCont() && (TESTAFF(sptr->getCont(), onlyincompound, sptr->getContLen()))))) &&
+ // needaffix on prefix or first suffix
+ (cclass ||
+ !(sptr->getCont() && TESTAFF(sptr->getCont(), needaffix, sptr->getContLen())) ||
+ (ppfx && !((ep->getCont()) &&
+ TESTAFF(ep->getCont(), needaffix,
+ ep->getContLen())))
+ )
+ ) if (in_compound != IN_CPD_END || ppfx || !(sptr->getCont() && TESTAFF(sptr->getCont(), onlyincompound, sptr->getContLen()))) {
+ rv = sptr->checkword(word,len, sfxopts, ppfx, wlst,
+ maxSug, ns, cclass, needflag, (in_compound ? 0 : onlyincompound));
+ if (rv) {
+ sfx=sptr; // BUG: sfx not stateless
+ sfxflag = sptr->getFlag(); // BUG: sfxflag not stateless
+ if (!sptr->getCont()) sfxappnd=sptr->getKey(); // BUG: sfxappnd not stateless
+ return rv;
+ }
+ }
+ sptr = sptr->getNextEQ();
+ } else {
+ sptr = sptr->getNextNE();
+ }
+ }
+
+ return NULL;
+}
+
+// check word for two-level suffixes
+
+struct hentry * AffixMgr::suffix_check_twosfx(const char * word, int len,
+ int sfxopts, PfxEntry * ppfx, const FLAG needflag)
+{
+ struct hentry * rv = NULL;
+
+ // first handle the special case of 0 length suffixes
+ SfxEntry * se = sStart[0];
+ while (se) {
+ if (contclasses[se->getFlag()])
+ {
+ rv = se->check_twosfx(word,len, sfxopts, ppfx, needflag);
+ if (rv) return rv;
+ }
+ se = se->getNext();
+ }
+
+ // now handle the general case
+ if (len == 0) return NULL; // FULLSTRIP
+ unsigned char sp = *((const unsigned char *)(word + len - 1));
+ SfxEntry * sptr = sStart[sp];
+
+ while (sptr) {
+ if (isRevSubset(sptr->getKey(), word + len - 1, len)) {
+ if (contclasses[sptr->getFlag()])
+ {
+ rv = sptr->check_twosfx(word,len, sfxopts, ppfx, needflag);
+ if (rv) {
+ sfxflag = sptr->getFlag(); // BUG: sfxflag not stateless
+ if (!sptr->getCont()) sfxappnd=sptr->getKey(); // BUG: sfxappnd not stateless
+ return rv;
+ }
+ }
+ sptr = sptr->getNextEQ();
+ } else {
+ sptr = sptr->getNextNE();
+ }
+ }
+
+ return NULL;
+}
+
+char * AffixMgr::suffix_check_twosfx_morph(const char * word, int len,
+ int sfxopts, PfxEntry * ppfx, const FLAG needflag)
+{
+ char result[MAXLNLEN];
+ char result2[MAXLNLEN];
+ char result3[MAXLNLEN];
+
+ char * st;
+
+ result[0] = '\0';
+ result2[0] = '\0';
+ result3[0] = '\0';
+
+ // first handle the special case of 0 length suffixes
+ SfxEntry * se = sStart[0];
+ while (se) {
+ if (contclasses[se->getFlag()])
+ {
+ st = se->check_twosfx_morph(word,len, sfxopts, ppfx, needflag);
+ if (st) {
+ if (ppfx) {
+ if (ppfx->getMorph()) {
+ mystrcat(result, ppfx->getMorph(), MAXLNLEN);
+ mystrcat(result, " ", MAXLNLEN);
+ } else debugflag(result, ppfx->getFlag());
+ }
+ mystrcat(result, st, MAXLNLEN);
+ free(st);
+ if (se->getMorph()) {
+ mystrcat(result, " ", MAXLNLEN);
+ mystrcat(result, se->getMorph(), MAXLNLEN);
+ } else debugflag(result, se->getFlag());
+ mystrcat(result, "\n", MAXLNLEN);
+ }
+ }
+ se = se->getNext();
+ }
+
+ // now handle the general case
+ if (len == 0) return NULL; // FULLSTRIP
+ unsigned char sp = *((const unsigned char *)(word + len - 1));
+ SfxEntry * sptr = sStart[sp];
+
+ while (sptr) {
+ if (isRevSubset(sptr->getKey(), word + len - 1, len)) {
+ if (contclasses[sptr->getFlag()])
+ {
+ st = sptr->check_twosfx_morph(word,len, sfxopts, ppfx, needflag);
+ if (st) {
+ sfxflag = sptr->getFlag(); // BUG: sfxflag not stateless
+ if (!sptr->getCont()) sfxappnd=sptr->getKey(); // BUG: sfxappnd not stateless
+ strcpy(result2, st);
+ free(st);
+
+ result3[0] = '\0';
+
+ if (sptr->getMorph()) {
+ mystrcat(result3, " ", MAXLNLEN);
+ mystrcat(result3, sptr->getMorph(), MAXLNLEN);
+ } else debugflag(result3, sptr->getFlag());
+ strlinecat(result2, result3);
+ mystrcat(result2, "\n", MAXLNLEN);
+ mystrcat(result, result2, MAXLNLEN);
+ }
+ }
+ sptr = sptr->getNextEQ();
+ } else {
+ sptr = sptr->getNextNE();
+ }
+ }
+ if (*result) return mystrdup(result);
+ return NULL;
+}
+
+char * AffixMgr::suffix_check_morph(const char * word, int len,
+ int sfxopts, PfxEntry * ppfx, const FLAG cclass, const FLAG needflag, char in_compound)
+{
+ char result[MAXLNLEN];
+
+ struct hentry * rv = NULL;
+
+ result[0] = '\0';
+
+ PfxEntry* ep = ppfx;
+
+ // first handle the special case of 0 length suffixes
+ SfxEntry * se = sStart[0];
+ while (se) {
+ if (!cclass || se->getCont()) {
+ // suffixes are not allowed in beginning of compounds
+ if (((((in_compound != IN_CPD_BEGIN)) || // && !cclass
+ // except when signed with compoundpermitflag flag
+ (se->getCont() && compoundpermitflag &&
+ TESTAFF(se->getCont(),compoundpermitflag,se->getContLen()))) && (!circumfix ||
+ // no circumfix flag in prefix and suffix
+ ((!ppfx || !(ep->getCont()) || !TESTAFF(ep->getCont(),
+ circumfix, ep->getContLen())) &&
+ (!se->getCont() || !(TESTAFF(se->getCont(),circumfix,se->getContLen())))) ||
+ // circumfix flag in prefix AND suffix
+ ((ppfx && (ep->getCont()) && TESTAFF(ep->getCont(),
+ circumfix, ep->getContLen())) &&
+ (se->getCont() && (TESTAFF(se->getCont(),circumfix,se->getContLen()))))) &&
+ // fogemorpheme
+ (in_compound ||
+ !((se->getCont() && (TESTAFF(se->getCont(), onlyincompound, se->getContLen()))))) &&
+ // needaffix on prefix or first suffix
+ (cclass ||
+ !(se->getCont() && TESTAFF(se->getCont(), needaffix, se->getContLen())) ||
+ (ppfx && !((ep->getCont()) &&
+ TESTAFF(ep->getCont(), needaffix,
+ ep->getContLen())))
+ )
+ ))
+ rv = se->checkword(word, len, sfxopts, ppfx, NULL, 0, 0, cclass, needflag);
+ while (rv) {
+ if (ppfx) {
+ if (ppfx->getMorph()) {
+ mystrcat(result, ppfx->getMorph(), MAXLNLEN);
+ mystrcat(result, " ", MAXLNLEN);
+ } else debugflag(result, ppfx->getFlag());
+ }
+ if (complexprefixes && HENTRY_DATA(rv)) mystrcat(result, HENTRY_DATA2(rv), MAXLNLEN);
+ if (! HENTRY_FIND(rv, MORPH_STEM)) {
+ mystrcat(result, " ", MAXLNLEN);
+ mystrcat(result, MORPH_STEM, MAXLNLEN);
+ mystrcat(result, HENTRY_WORD(rv), MAXLNLEN);
+ }
+ // store the pointer of the hash entry
+// sprintf(result + strlen(result), " %s%p", MORPH_HENTRY, rv);
+
+ if (!complexprefixes && HENTRY_DATA(rv)) {
+ mystrcat(result, " ", MAXLNLEN);
+ mystrcat(result, HENTRY_DATA2(rv), MAXLNLEN);
+ }
+ if (se->getMorph()) {
+ mystrcat(result, " ", MAXLNLEN);
+ mystrcat(result, se->getMorph(), MAXLNLEN);
+ } else debugflag(result, se->getFlag());
+ mystrcat(result, "\n", MAXLNLEN);
+ rv = se->get_next_homonym(rv, sfxopts, ppfx, cclass, needflag);
+ }
+ }
+ se = se->getNext();
+ }
+
+ // now handle the general case
+ if (len == 0) return NULL; // FULLSTRIP
+ unsigned char sp = *((const unsigned char *)(word + len - 1));
+ SfxEntry * sptr = sStart[sp];
+
+ while (sptr) {
+ if (isRevSubset(sptr->getKey(), word + len - 1, len)
+ ) {
+ // suffixes are not allowed in beginning of compounds
+ if (((((in_compound != IN_CPD_BEGIN)) || // && !cclass
+ // except when signed with compoundpermitflag flag
+ (sptr->getCont() && compoundpermitflag &&
+ TESTAFF(sptr->getCont(),compoundpermitflag,sptr->getContLen()))) && (!circumfix ||
+ // no circumfix flag in prefix and suffix
+ ((!ppfx || !(ep->getCont()) || !TESTAFF(ep->getCont(),
+ circumfix, ep->getContLen())) &&
+ (!sptr->getCont() || !(TESTAFF(sptr->getCont(),circumfix,sptr->getContLen())))) ||
+ // circumfix flag in prefix AND suffix
+ ((ppfx && (ep->getCont()) && TESTAFF(ep->getCont(),
+ circumfix, ep->getContLen())) &&
+ (sptr->getCont() && (TESTAFF(sptr->getCont(),circumfix,sptr->getContLen()))))) &&
+ // fogemorpheme
+ (in_compound ||
+ !((sptr->getCont() && (TESTAFF(sptr->getCont(), onlyincompound, sptr->getContLen()))))) &&
+ // needaffix on first suffix
+ (cclass || !(sptr->getCont() &&
+ TESTAFF(sptr->getCont(), needaffix, sptr->getContLen())))
+ )) rv = sptr->checkword(word,len, sfxopts, ppfx, NULL, 0, 0, cclass, needflag);
+ while (rv) {
+ if (ppfx) {
+ if (ppfx->getMorph()) {
+ mystrcat(result, ppfx->getMorph(), MAXLNLEN);
+ mystrcat(result, " ", MAXLNLEN);
+ } else debugflag(result, ppfx->getFlag());
+ }
+ if (complexprefixes && HENTRY_DATA(rv)) mystrcat(result, HENTRY_DATA2(rv), MAXLNLEN);
+ if (! HENTRY_FIND(rv, MORPH_STEM)) {
+ mystrcat(result, " ", MAXLNLEN);
+ mystrcat(result, MORPH_STEM, MAXLNLEN);
+ mystrcat(result, HENTRY_WORD(rv), MAXLNLEN);
+ }
+ // store the pointer of the hash entry
+// sprintf(result + strlen(result), " %s%p", MORPH_HENTRY, rv);
+
+ if (!complexprefixes && HENTRY_DATA(rv)) {
+ mystrcat(result, " ", MAXLNLEN);
+ mystrcat(result, HENTRY_DATA2(rv), MAXLNLEN);
+ }
+
+ if (sptr->getMorph()) {
+ mystrcat(result, " ", MAXLNLEN);
+ mystrcat(result, sptr->getMorph(), MAXLNLEN);
+ } else debugflag(result, sptr->getFlag());
+ mystrcat(result, "\n", MAXLNLEN);
+ rv = sptr->get_next_homonym(rv, sfxopts, ppfx, cclass, needflag);
+ }
+ sptr = sptr->getNextEQ();
+ } else {
+ sptr = sptr->getNextNE();
+ }
+ }
+
+ if (*result) return mystrdup(result);
+ return NULL;
+}
+
+// check if word with affixes is correctly spelled
+struct hentry * AffixMgr::affix_check (const char * word, int len, const FLAG needflag, char in_compound)
+{
+ struct hentry * rv= NULL;
+
+ // check all prefixes (also crossed with suffixes if allowed)
+ rv = prefix_check(word, len, in_compound, needflag);
+ if (rv) return rv;
+
+ // if still not found check all suffixes
+ rv = suffix_check(word, len, 0, NULL, NULL, 0, NULL, FLAG_NULL, needflag, in_compound);
+
+ if (havecontclass) {
+ sfx = NULL;
+ pfx = NULL;
+
+ if (rv) return rv;
+ // if still not found check all two-level suffixes
+ rv = suffix_check_twosfx(word, len, 0, NULL, needflag);
+
+ if (rv) return rv;
+ // if still not found check all two-level suffixes
+ rv = prefix_check_twosfx(word, len, IN_CPD_NOT, needflag);
+ }
+
+ return rv;
+}
+
+// check if word with affixes is correctly spelled
+char * AffixMgr::affix_check_morph(const char * word, int len, const FLAG needflag, char in_compound)
+{
+ char result[MAXLNLEN];
+ char * st = NULL;
+
+ *result = '\0';
+
+ // check all prefixes (also crossed with suffixes if allowed)
+ st = prefix_check_morph(word, len, in_compound);
+ if (st) {
+ mystrcat(result, st, MAXLNLEN);
+ free(st);
+ }
+
+ // if still not found check all suffixes
+ st = suffix_check_morph(word, len, 0, NULL, '\0', needflag, in_compound);
+ if (st) {
+ mystrcat(result, st, MAXLNLEN);
+ free(st);
+ }
+
+ if (havecontclass) {
+ sfx = NULL;
+ pfx = NULL;
+ // if still not found check all two-level suffixes
+ st = suffix_check_twosfx_morph(word, len, 0, NULL, needflag);
+ if (st) {
+ mystrcat(result, st, MAXLNLEN);
+ free(st);
+ }
+
+ // if still not found check all two-level suffixes
+ st = prefix_check_twosfx_morph(word, len, IN_CPD_NOT, needflag);
+ if (st) {
+ mystrcat(result, st, MAXLNLEN);
+ free(st);
+ }
+ }
+
+ return mystrdup(result);
+}
+
+char * AffixMgr::morphgen(char * ts, int wl, const unsigned short * ap,
+ unsigned short al, char * morph, char * targetmorph, int level)
+{
+ // handle suffixes
+ char * stemmorph;
+ char * stemmorphcatpos;
+ char mymorph[MAXLNLEN];
+
+ if (!morph) return NULL;
+
+ // check substandard flag
+ if (TESTAFF(ap, substandard, al)) return NULL;
+
+ if (morphcmp(morph, targetmorph) == 0) return mystrdup(ts);
+
+// int targetcount = get_sfxcount(targetmorph);
+
+ // use input suffix fields, if exist
+ if (strstr(morph, MORPH_INFL_SFX) || strstr(morph, MORPH_DERI_SFX)) {
+ stemmorph = mymorph;
+ strcpy(stemmorph, morph);
+ mystrcat(stemmorph, " ", MAXLNLEN);
+ stemmorphcatpos = stemmorph + strlen(stemmorph);
+ } else {
+ stemmorph = morph;
+ stemmorphcatpos = NULL;
+ }
+
+ for (int i = 0; i < al; i++) {
+ const unsigned char c = (unsigned char) (ap[i] & 0x00FF);
+ SfxEntry * sptr = sFlag[c];
+ while (sptr) {
+ if (sptr->getFlag() == ap[i] && sptr->getMorph() && ((sptr->getContLen() == 0) ||
+ // don't generate forms with substandard affixes
+ !TESTAFF(sptr->getCont(), substandard, sptr->getContLen()))) {
+
+ if (stemmorphcatpos) strcpy(stemmorphcatpos, sptr->getMorph());
+ else stemmorph = (char *) sptr->getMorph();
+
+ int cmp = morphcmp(stemmorph, targetmorph);
+
+ if (cmp == 0) {
+ char * newword = sptr->add(ts, wl);
+ if (newword) {
+ hentry * check = pHMgr->lookup(newword); // XXX extra dic
+ if (!check || !check->astr ||
+ !(TESTAFF(check->astr, forbiddenword, check->alen) ||
+ TESTAFF(check->astr, ONLYUPCASEFLAG, check->alen))) {
+ return newword;
+ }
+ free(newword);
+ }
+ }
+
+ // recursive call for secondary suffixes
+ if ((level == 0) && (cmp == 1) && (sptr->getContLen() > 0) &&
+// (get_sfxcount(stemmorph) < targetcount) &&
+ !TESTAFF(sptr->getCont(), substandard, sptr->getContLen())) {
+ char * newword = sptr->add(ts, wl);
+ if (newword) {
+ char * newword2 = morphgen(newword, strlen(newword), sptr->getCont(),
+ sptr->getContLen(), stemmorph, targetmorph, 1);
+
+ if (newword2) {
+ free(newword);
+ return newword2;
+ }
+ free(newword);
+ newword = NULL;
+ }
+ }
+ }
+ sptr = sptr->getFlgNxt();
+ }
+ }
+ return NULL;
+}
+
+
+int AffixMgr::expand_rootword(struct guessword * wlst, int maxn, const char * ts,
+ int wl, const unsigned short * ap, unsigned short al, char * bad, int badl,
+ char * phon)
+{
+ int nh=0;
+ // first add root word to list
+ if ((nh < maxn) && !(al && ((needaffix && TESTAFF(ap, needaffix, al)) ||
+ (onlyincompound && TESTAFF(ap, onlyincompound, al))))) {
+ wlst[nh].word = mystrdup(ts);
+ if (!wlst[nh].word) return 0;
+ wlst[nh].allow = (1 == 0);
+ wlst[nh].orig = NULL;
+ nh++;
+ // add special phonetic version
+ if (phon && (nh < maxn)) {
+ wlst[nh].word = mystrdup(phon);
+ if (!wlst[nh].word) return nh - 1;
+ wlst[nh].allow = (1 == 0);
+ wlst[nh].orig = mystrdup(ts);
+ if (!wlst[nh].orig) return nh - 1;
+ nh++;
+ }
+ }
+
+ // handle suffixes
+ for (int i = 0; i < al; i++) {
+ const unsigned char c = (unsigned char) (ap[i] & 0x00FF);
+ SfxEntry * sptr = sFlag[c];
+ while (sptr) {
+ if ((sptr->getFlag() == ap[i]) && (!sptr->getKeyLen() || ((badl > sptr->getKeyLen()) &&
+ (strcmp(sptr->getAffix(), bad + badl - sptr->getKeyLen()) == 0))) &&
+ // check needaffix flag
+ !(sptr->getCont() && ((needaffix &&
+ TESTAFF(sptr->getCont(), needaffix, sptr->getContLen())) ||
+ (circumfix &&
+ TESTAFF(sptr->getCont(), circumfix, sptr->getContLen())) ||
+ (onlyincompound &&
+ TESTAFF(sptr->getCont(), onlyincompound, sptr->getContLen()))))
+ ) {
+ char * newword = sptr->add(ts, wl);
+ if (newword) {
+ if (nh < maxn) {
+ wlst[nh].word = newword;
+ wlst[nh].allow = sptr->allowCross();
+ wlst[nh].orig = NULL;
+ nh++;
+ // add special phonetic version
+ if (phon && (nh < maxn)) {
+ char st[MAXWORDUTF8LEN];
+ strcpy(st, phon);
+ strcat(st, sptr->getKey());
+ reverseword(st + strlen(phon));
+ wlst[nh].word = mystrdup(st);
+ if (!wlst[nh].word) return nh - 1;
+ wlst[nh].allow = (1 == 0);
+ wlst[nh].orig = mystrdup(newword);
+ if (!wlst[nh].orig) return nh - 1;
+ nh++;
+ }
+ } else {
+ free(newword);
+ }
+ }
+ }
+ sptr = sptr->getFlgNxt();
+ }
+ }
+
+ int n = nh;
+
+ // handle cross products of prefixes and suffixes
+ for (int j=1;j<n ;j++)
+ if (wlst[j].allow) {
+ for (int k = 0; k < al; k++) {
+ const unsigned char c = (unsigned char) (ap[k] & 0x00FF);
+ PfxEntry * cptr = pFlag[c];
+ while (cptr) {
+ if ((cptr->getFlag() == ap[k]) && cptr->allowCross() && (!cptr->getKeyLen() || ((badl > cptr->getKeyLen()) &&
+ (strncmp(cptr->getKey(), bad, cptr->getKeyLen()) == 0)))) {
+ int l1 = strlen(wlst[j].word);
+ char * newword = cptr->add(wlst[j].word, l1);
+ if (newword) {
+ if (nh < maxn) {
+ wlst[nh].word = newword;
+ wlst[nh].allow = cptr->allowCross();
+ wlst[nh].orig = NULL;
+ nh++;
+ } else {
+ free(newword);
+ }
+ }
+ }
+ cptr = cptr->getFlgNxt();
+ }
+ }
+ }
+
+
+ // now handle pure prefixes
+ for (int m = 0; m < al; m ++) {
+ const unsigned char c = (unsigned char) (ap[m] & 0x00FF);
+ PfxEntry * ptr = pFlag[c];
+ while (ptr) {
+ if ((ptr->getFlag() == ap[m]) && (!ptr->getKeyLen() || ((badl > ptr->getKeyLen()) &&
+ (strncmp(ptr->getKey(), bad, ptr->getKeyLen()) == 0))) &&
+ // check needaffix flag
+ !(ptr->getCont() && ((needaffix &&
+ TESTAFF(ptr->getCont(), needaffix, ptr->getContLen())) ||
+ (circumfix &&
+ TESTAFF(ptr->getCont(), circumfix, ptr->getContLen())) ||
+ (onlyincompound &&
+ TESTAFF(ptr->getCont(), onlyincompound, ptr->getContLen()))))
+ ) {
+ char * newword = ptr->add(ts, wl);
+ if (newword) {
+ if (nh < maxn) {
+ wlst[nh].word = newword;
+ wlst[nh].allow = ptr->allowCross();
+ wlst[nh].orig = NULL;
+ nh++;
+ } else {
+ free(newword);
+ }
+ }
+ }
+ ptr = ptr->getFlgNxt();
+ }
+ }
+
+ return nh;
+}
+
+// return length of replacing table
+int AffixMgr::get_numrep() const
+{
+ return numrep;
+}
+
+// return replacing table
+struct replentry * AffixMgr::get_reptable() const
+{
+ if (! reptable ) return NULL;
+ return reptable;
+}
+
+// return iconv table
+RepList * AffixMgr::get_iconvtable() const
+{
+ if (! iconvtable ) return NULL;
+ return iconvtable;
+}
+
+// return oconv table
+RepList * AffixMgr::get_oconvtable() const
+{
+ if (! oconvtable ) return NULL;
+ return oconvtable;
+}
+
+// return replacing table
+struct phonetable * AffixMgr::get_phonetable() const
+{
+ if (! phone ) return NULL;
+ return phone;
+}
+
+// return length of character map table
+int AffixMgr::get_nummap() const
+{
+ return nummap;
+}
+
+// return character map table
+struct mapentry * AffixMgr::get_maptable() const
+{
+ if (! maptable ) return NULL;
+ return maptable;
+}
+
+// return length of word break table
+int AffixMgr::get_numbreak() const
+{
+ return numbreak;
+}
+
+// return character map table
+char ** AffixMgr::get_breaktable() const
+{
+ if (! breaktable ) return NULL;
+ return breaktable;
+}
+
+// return text encoding of dictionary
+char * AffixMgr::get_encoding()
+{
+ if (! encoding ) encoding = mystrdup(SPELL_ENCODING);
+ return mystrdup(encoding);
+}
+
+// return text encoding of dictionary
+int AffixMgr::get_langnum() const
+{
+ return langnum;
+}
+
+// return double prefix option
+int AffixMgr::get_complexprefixes() const
+{
+ return complexprefixes;
+}
+
+// return FULLSTRIP option
+int AffixMgr::get_fullstrip() const
+{
+ return fullstrip;
+}
+
+FLAG AffixMgr::get_keepcase() const
+{
+ return keepcase;
+}
+
+FLAG AffixMgr::get_forceucase() const
+{
+ return forceucase;
+}
+
+FLAG AffixMgr::get_warn() const
+{
+ return warn;
+}
+
+int AffixMgr::get_forbidwarn() const
+{
+ return forbidwarn;
+}
+
+int AffixMgr::get_checksharps() const
+{
+ return checksharps;
+}
+
+char * AffixMgr::encode_flag(unsigned short aflag) const
+{
+ return pHMgr->encode_flag(aflag);
+}
+
+
+// return the preferred ignore string for suggestions
+char * AffixMgr::get_ignore() const
+{
+ if (!ignorechars) return NULL;
+ return ignorechars;
+}
+
+// return the preferred ignore string for suggestions
+unsigned short * AffixMgr::get_ignore_utf16(int * len) const
+{
+ *len = ignorechars_utf16_len;
+ return ignorechars_utf16;
+}
+
+// return the keyboard string for suggestions
+char * AffixMgr::get_key_string()
+{
+ if (! keystring ) keystring = mystrdup(SPELL_KEYSTRING);
+ return mystrdup(keystring);
+}
+
+// return the preferred try string for suggestions
+char * AffixMgr::get_try_string() const
+{
+ if (! trystring ) return NULL;
+ return mystrdup(trystring);
+}
+
+// return the preferred try string for suggestions
+const char * AffixMgr::get_wordchars() const
+{
+ return wordchars;
+}
+
+unsigned short * AffixMgr::get_wordchars_utf16(int * len) const
+{
+ *len = wordchars_utf16_len;
+ return wordchars_utf16;
+}
+
+// is there compounding?
+int AffixMgr::get_compound() const
+{
+ return compoundflag || compoundbegin || numdefcpd;
+}
+
+// return the compound words control flag
+FLAG AffixMgr::get_compoundflag() const
+{
+ return compoundflag;
+}
+
+// return the forbidden words control flag
+FLAG AffixMgr::get_forbiddenword() const
+{
+ return forbiddenword;
+}
+
+// return the forbidden words control flag
+FLAG AffixMgr::get_nosuggest() const
+{
+ return nosuggest;
+}
+
+// return the forbidden words control flag
+FLAG AffixMgr::get_nongramsuggest() const
+{
+ return nongramsuggest;
+}
+
+// return the forbidden words flag modify flag
+FLAG AffixMgr::get_needaffix() const
+{
+ return needaffix;
+}
+
+// return the onlyincompound flag
+FLAG AffixMgr::get_onlyincompound() const
+{
+ return onlyincompound;
+}
+
+// return the compound word signal flag
+FLAG AffixMgr::get_compoundroot() const
+{
+ return compoundroot;
+}
+
+// return the compound begin signal flag
+FLAG AffixMgr::get_compoundbegin() const
+{
+ return compoundbegin;
+}
+
+// return the value of checknum
+int AffixMgr::get_checknum() const
+{
+ return checknum;
+}
+
+// return the value of prefix
+const char * AffixMgr::get_prefix() const
+{
+ if (pfx) return pfx->getKey();
+ return NULL;
+}
+
+// return the value of suffix
+const char * AffixMgr::get_suffix() const
+{
+ return sfxappnd;
+}
+
+// return the value of suffix
+const char * AffixMgr::get_version() const
+{
+ return version;
+}
+
+// return lemma_present flag
+FLAG AffixMgr::get_lemma_present() const
+{
+ return lemma_present;
+}
+
+// utility method to look up root words in hash table
+struct hentry * AffixMgr::lookup(const char * word)
+{
+ int i;
+ struct hentry * he = NULL;
+ for (i = 0; i < *maxdic && !he; i++) {
+ he = (alldic[i])->lookup(word);
+ }
+ return he;
+}
+
+// return the value of suffix
+int AffixMgr::have_contclass() const
+{
+ return havecontclass;
+}
+
+// return utf8
+int AffixMgr::get_utf8() const
+{
+ return utf8;
+}
+
+int AffixMgr::get_maxngramsugs(void) const
+{
+ return maxngramsugs;
+}
+
+int AffixMgr::get_maxcpdsugs(void) const
+{
+ return maxcpdsugs;
+}
+
+int AffixMgr::get_maxdiff(void) const
+{
+ return maxdiff;
+}
+
+int AffixMgr::get_onlymaxdiff(void) const
+{
+ return onlymaxdiff;
+}
+
+// return nosplitsugs
+int AffixMgr::get_nosplitsugs(void) const
+{
+ return nosplitsugs;
+}
+
+// return sugswithdots
+int AffixMgr::get_sugswithdots(void) const
+{
+ return sugswithdots;
+}
+
+/* parse flag */
+int AffixMgr::parse_flag(char * line, unsigned short * out, FileMgr * af) {
+ char * s = NULL;
+ if (*out != FLAG_NULL && !(*out >= DEFAULTFLAGS)) {
+ HUNSPELL_WARNING(stderr, "error: line %d: multiple definitions of an affix file parameter\n", af->getlinenum());
+ return 1;
+ }
+ if (parse_string(line, &s, af->getlinenum())) return 1;
+ *out = pHMgr->decode_flag(s);
+ free(s);
+ return 0;
+}
+
+/* parse num */
+int AffixMgr::parse_num(char * line, int * out, FileMgr * af) {
+ char * s = NULL;
+ if (*out != -1) {
+ HUNSPELL_WARNING(stderr, "error: line %d: multiple definitions of an affix file parameter\n", af->getlinenum());
+ return 1;
+ }
+ if (parse_string(line, &s, af->getlinenum())) return 1;
+ *out = atoi(s);
+ free(s);
+ return 0;
+}
+
+/* parse in the max syllablecount of compound words and */
+int AffixMgr::parse_cpdsyllable(char * line, FileMgr * af)
+{
+ char * tp = line;
+ char * piece;
+ int i = 0;
+ int np = 0;
+ w_char w[MAXWORDLEN];
+ piece = mystrsep(&tp, 0);
+ while (piece) {
+ if (*piece != '\0') {
+ switch(i) {
+ case 0: { np++; break; }
+ case 1: { cpdmaxsyllable = atoi(piece); np++; break; }
+ case 2: {
+ if (!utf8) {
+ cpdvowels = mystrdup(piece);
+ } else {
+ int n = u8_u16(w, MAXWORDLEN, piece);
+ if (n > 0) {
+ flag_qsort((unsigned short *) w, 0, n);
+ cpdvowels_utf16 = (w_char *) malloc(n * sizeof(w_char));
+ if (!cpdvowels_utf16) return 1;
+ memcpy(cpdvowels_utf16, w, n * sizeof(w_char));
+ }
+ cpdvowels_utf16_len = n;
+ }
+ np++;
+ break;
+ }
+ default: break;
+ }
+ i++;
+ }
+ piece = mystrsep(&tp, 0);
+ }
+ if (np < 2) {
+ HUNSPELL_WARNING(stderr, "error: line %d: missing compoundsyllable information\n", af->getlinenum());
+ return 1;
+ }
+ if (np == 2) cpdvowels = mystrdup("aeiouAEIOU");
+ return 0;
+}
+
+/* parse in the typical fault correcting table */
+int AffixMgr::parse_reptable(char * line, FileMgr * af)
+{
+ if (numrep != 0) {
+ HUNSPELL_WARNING(stderr, "error: line %d: multiple table definitions\n", af->getlinenum());
+ return 1;
+ }
+ char * tp = line;
+ char * piece;
+ int i = 0;
+ int np = 0;
+ piece = mystrsep(&tp, 0);
+ while (piece) {
+ if (*piece != '\0') {
+ switch(i) {
+ case 0: { np++; break; }
+ case 1: {
+ numrep = atoi(piece);
+ if (numrep < 1) {
+ HUNSPELL_WARNING(stderr, "error: line %d: incorrect entry number\n", af->getlinenum());
+ return 1;
+ }
+ reptable = (replentry *) malloc(numrep * sizeof(struct replentry));
+ if (!reptable) return 1;
+ np++;
+ break;
+ }
+ default: break;
+ }
+ i++;
+ }
+ piece = mystrsep(&tp, 0);
+ }
+ if (np != 2) {
+ HUNSPELL_WARNING(stderr, "error: line %d: missing data\n", af->getlinenum());
+ return 1;
+ }
+
+ /* now parse the numrep lines to read in the remainder of the table */
+ char * nl;
+ for (int j=0; j < numrep; j++) {
+ if (!(nl = af->getline())) return 1;
+ mychomp(nl);
+ tp = nl;
+ i = 0;
+ reptable[j].pattern = NULL;
+ reptable[j].pattern2 = NULL;
+ piece = mystrsep(&tp, 0);
+ while (piece) {
+ if (*piece != '\0') {
+ switch(i) {
+ case 0: {
+ if (strncmp(piece,"REP",3) != 0) {
+ HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum());
+ numrep = 0;
+ return 1;
+ }
+ break;
+ }
+ case 1: {
+ if (*piece == '^') reptable[j].start = true; else reptable[j].start = false;
+ reptable[j].pattern = mystrrep(mystrdup(piece + int(reptable[j].start)),"_"," ");
+ int lr = strlen(reptable[j].pattern) - 1;
+ if (reptable[j].pattern[lr] == '$') {
+ reptable[j].end = true;
+ reptable[j].pattern[lr] = '\0';
+ } else reptable[j].end = false;
+ break;
+ }
+ case 2: { reptable[j].pattern2 = mystrrep(mystrdup(piece),"_"," "); break; }
+ default: break;
+ }
+ i++;
+ }
+ piece = mystrsep(&tp, 0);
+ }
+ if ((!(reptable[j].pattern)) || (!(reptable[j].pattern2))) {
+ HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum());
+ numrep = 0;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/* parse in the typical fault correcting table */
+int AffixMgr::parse_convtable(char * line, FileMgr * af, RepList ** rl, const char * keyword)
+{
+ if (*rl) {
+ HUNSPELL_WARNING(stderr, "error: line %d: multiple table definitions\n", af->getlinenum());
+ return 1;
+ }
+ char * tp = line;
+ char * piece;
+ int i = 0;
+ int np = 0;
+ int numrl = 0;
+ piece = mystrsep(&tp, 0);
+ while (piece) {
+ if (*piece != '\0') {
+ switch(i) {
+ case 0: { np++; break; }
+ case 1: {
+ numrl = atoi(piece);
+ if (numrl < 1) {
+ HUNSPELL_WARNING(stderr, "error: line %d: incorrect entry number\n", af->getlinenum());
+ return 1;
+ }
+ *rl = new RepList(numrl);
+ if (!*rl) return 1;
+ np++;
+ break;
+ }
+ default: break;
+ }
+ i++;
+ }
+ piece = mystrsep(&tp, 0);
+ }
+ if (np != 2) {
+ HUNSPELL_WARNING(stderr, "error: line %d: missing data\n", af->getlinenum());
+ return 1;
+ }
+
+ /* now parse the num lines to read in the remainder of the table */
+ char * nl;
+ for (int j=0; j < numrl; j++) {
+ if (!(nl = af->getline())) return 1;
+ mychomp(nl);
+ tp = nl;
+ i = 0;
+ char * pattern = NULL;
+ char * pattern2 = NULL;
+ piece = mystrsep(&tp, 0);
+ while (piece) {
+ if (*piece != '\0') {
+ switch(i) {
+ case 0: {
+ if (strncmp(piece, keyword, sizeof(keyword)) != 0) {
+ HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum());
+ delete *rl;
+ *rl = NULL;
+ return 1;
+ }
+ break;
+ }
+ case 1: { pattern = mystrrep(mystrdup(piece),"_"," "); break; }
+ case 2: {
+ pattern2 = mystrrep(mystrdup(piece),"_"," ");
+ break;
+ }
+ default: break;
+ }
+ i++;
+ }
+ piece = mystrsep(&tp, 0);
+ }
+ if (!pattern || !pattern2) {
+ if (pattern)
+ free(pattern);
+ if (pattern2)
+ free(pattern2);
+ HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum());
+ return 1;
+ }
+ (*rl)->add(pattern, pattern2);
+ }
+ return 0;
+}
+
+
+/* parse in the typical fault correcting table */
+int AffixMgr::parse_phonetable(char * line, FileMgr * af)
+{
+ if (phone) {
+ HUNSPELL_WARNING(stderr, "error: line %d: multiple table definitions\n", af->getlinenum());
+ return 1;
+ }
+ char * tp = line;
+ char * piece;
+ int i = 0;
+ int np = 0;
+ piece = mystrsep(&tp, 0);
+ while (piece) {
+ if (*piece != '\0') {
+ switch(i) {
+ case 0: { np++; break; }
+ case 1: {
+ phone = (phonetable *) malloc(sizeof(struct phonetable));
+ if (!phone) return 1;
+ phone->num = atoi(piece);
+ phone->rules = NULL;
+ phone->utf8 = (char) utf8;
+ if (phone->num < 1) {
+ HUNSPELL_WARNING(stderr, "error: line %d: bad entry number\n", af->getlinenum());
+ return 1;
+ }
+ phone->rules = (char * *) malloc(2 * (phone->num + 1) * sizeof(char *));
+ if (!phone->rules) {
+ free(phone);
+ phone = NULL;
+ return 1;
+ }
+ np++;
+ break;
+ }
+ default: break;
+ }
+ i++;
+ }
+ piece = mystrsep(&tp, 0);
+ }
+ if (np != 2) {
+ HUNSPELL_WARNING(stderr, "error: line %d: missing data\n", af->getlinenum());
+ return 1;
+ }
+
+ /* now parse the phone->num lines to read in the remainder of the table */
+ char * nl;
+ for (int j=0; j < phone->num; j++) {
+ if (!(nl = af->getline())) return 1;
+ mychomp(nl);
+ tp = nl;
+ i = 0;
+ phone->rules[j * 2] = NULL;
+ phone->rules[j * 2 + 1] = NULL;
+ piece = mystrsep(&tp, 0);
+ while (piece) {
+ if (*piece != '\0') {
+ switch(i) {
+ case 0: {
+ if (strncmp(piece,"PHONE",5) != 0) {
+ HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum());
+ phone->num = 0;
+ return 1;
+ }
+ break;
+ }
+ case 1: { phone->rules[j * 2] = mystrrep(mystrdup(piece),"_",""); break; }
+ case 2: { phone->rules[j * 2 + 1] = mystrrep(mystrdup(piece),"_",""); break; }
+ default: break;
+ }
+ i++;
+ }
+ piece = mystrsep(&tp, 0);
+ }
+ if ((!(phone->rules[j * 2])) || (!(phone->rules[j * 2 + 1]))) {
+ HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum());
+ phone->num = 0;
+ return 1;
+ }
+ }
+ phone->rules[phone->num * 2] = mystrdup("");
+ phone->rules[phone->num * 2 + 1] = mystrdup("");
+ init_phonet_hash(*phone);
+ return 0;
+}
+
+/* parse in the checkcompoundpattern table */
+int AffixMgr::parse_checkcpdtable(char * line, FileMgr * af)
+{
+ if (numcheckcpd != 0) {
+ HUNSPELL_WARNING(stderr, "error: line %d: multiple table definitions\n", af->getlinenum());
+ return 1;
+ }
+ char * tp = line;
+ char * piece;
+ int i = 0;
+ int np = 0;
+ piece = mystrsep(&tp, 0);
+ while (piece) {
+ if (*piece != '\0') {
+ switch(i) {
+ case 0: { np++; break; }
+ case 1: {
+ numcheckcpd = atoi(piece);
+ if (numcheckcpd < 1) {
+ HUNSPELL_WARNING(stderr, "error: line %d: bad entry number\n", af->getlinenum());
+ return 1;
+ }
+ checkcpdtable = (patentry *) malloc(numcheckcpd * sizeof(struct patentry));
+ if (!checkcpdtable) return 1;
+ np++;
+ break;
+ }
+ default: break;
+ }
+ i++;
+ }
+ piece = mystrsep(&tp, 0);
+ }
+ if (np != 2) {
+ HUNSPELL_WARNING(stderr, "error: line %d: missing data\n", af->getlinenum());
+ return 1;
+ }
+
+ /* now parse the numcheckcpd lines to read in the remainder of the table */
+ char * nl;
+ for (int j=0; j < numcheckcpd; j++) {
+ if (!(nl = af->getline())) return 1;
+ mychomp(nl);
+ tp = nl;
+ i = 0;
+ checkcpdtable[j].pattern = NULL;
+ checkcpdtable[j].pattern2 = NULL;
+ checkcpdtable[j].pattern3 = NULL;
+ checkcpdtable[j].cond = FLAG_NULL;
+ checkcpdtable[j].cond2 = FLAG_NULL;
+ piece = mystrsep(&tp, 0);
+ while (piece) {
+ if (*piece != '\0') {
+ switch(i) {
+ case 0: {
+ if (strncmp(piece,"CHECKCOMPOUNDPATTERN",20) != 0) {
+ HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum());
+ numcheckcpd = 0;
+ return 1;
+ }
+ break;
+ }
+ case 1: {
+ checkcpdtable[j].pattern = mystrdup(piece);
+ char * p = strchr(checkcpdtable[j].pattern, '/');
+ if (p) {
+ *p = '\0';
+ checkcpdtable[j].cond = pHMgr->decode_flag(p + 1);
+ }
+ break; }
+ case 2: {
+ checkcpdtable[j].pattern2 = mystrdup(piece);
+ char * p = strchr(checkcpdtable[j].pattern2, '/');
+ if (p) {
+ *p = '\0';
+ checkcpdtable[j].cond2 = pHMgr->decode_flag(p + 1);
+ }
+ break;
+ }
+ case 3: { checkcpdtable[j].pattern3 = mystrdup(piece); simplifiedcpd = 1; break; }
+ default: break;
+ }
+ i++;
+ }
+ piece = mystrsep(&tp, 0);
+ }
+ if ((!(checkcpdtable[j].pattern)) || (!(checkcpdtable[j].pattern2))) {
+ HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum());
+ numcheckcpd = 0;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/* parse in the compound rule table */
+int AffixMgr::parse_defcpdtable(char * line, FileMgr * af)
+{
+ if (numdefcpd != 0) {
+ HUNSPELL_WARNING(stderr, "error: line %d: multiple table definitions\n", af->getlinenum());
+ return 1;
+ }
+ char * tp = line;
+ char * piece;
+ int i = 0;
+ int np = 0;
+ piece = mystrsep(&tp, 0);
+ while (piece) {
+ if (*piece != '\0') {
+ switch(i) {
+ case 0: { np++; break; }
+ case 1: {
+ numdefcpd = atoi(piece);
+ if (numdefcpd < 1) {
+ HUNSPELL_WARNING(stderr, "error: line %d: bad entry number\n", af->getlinenum());
+ return 1;
+ }
+ defcpdtable = (flagentry *) malloc(numdefcpd * sizeof(flagentry));
+ if (!defcpdtable) return 1;
+ np++;
+ break;
+ }
+ default: break;
+ }
+ i++;
+ }
+ piece = mystrsep(&tp, 0);
+ }
+ if (np != 2) {
+ HUNSPELL_WARNING(stderr, "error: line %d: missing data\n", af->getlinenum());
+ return 1;
+ }
+
+ /* now parse the numdefcpd lines to read in the remainder of the table */
+ char * nl;
+ for (int j=0; j < numdefcpd; j++) {
+ if (!(nl = af->getline())) return 1;
+ mychomp(nl);
+ tp = nl;
+ i = 0;
+ defcpdtable[j].def = NULL;
+ piece = mystrsep(&tp, 0);
+ while (piece) {
+ if (*piece != '\0') {
+ switch(i) {
+ case 0: {
+ if (strncmp(piece, "COMPOUNDRULE", 12) != 0) {
+ HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum());
+ numdefcpd = 0;
+ return 1;
+ }
+ break;
+ }
+ case 1: { // handle parenthesized flags
+ if (strchr(piece, '(')) {
+ defcpdtable[j].def = (FLAG *) malloc(strlen(piece) * sizeof(FLAG));
+ defcpdtable[j].len = 0;
+ int end = 0;
+ FLAG * conv;
+ while (!end) {
+ char * par = piece + 1;
+ while (*par != '(' && *par != ')' && *par != '\0') par++;
+ if (*par == '\0') end = 1; else *par = '\0';
+ if (*piece == '(') piece++;
+ if (*piece == '*' || *piece == '?') {
+ defcpdtable[j].def[defcpdtable[j].len++] = (FLAG) *piece;
+ } else if (*piece != '\0') {
+ int l = pHMgr->decode_flags(&conv, piece, af);
+ for (int k = 0; k < l; k++) defcpdtable[j].def[defcpdtable[j].len++] = conv[k];
+ free(conv);
+ }
+ piece = par + 1;
+ }
+ } else {
+ defcpdtable[j].len = pHMgr->decode_flags(&(defcpdtable[j].def), piece, af);
+ }
+ break;
+ }
+ default: break;
+ }
+ i++;
+ }
+ piece = mystrsep(&tp, 0);
+ }
+ if (!defcpdtable[j].len) {
+ HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum());
+ numdefcpd = 0;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+
+/* parse in the character map table */
+int AffixMgr::parse_maptable(char * line, FileMgr * af)
+{
+ if (nummap != 0) {
+ HUNSPELL_WARNING(stderr, "error: line %d: multiple table definitions\n", af->getlinenum());
+ return 1;
+ }
+ char * tp = line;
+ char * piece;
+ int i = 0;
+ int np = 0;
+ piece = mystrsep(&tp, 0);
+ while (piece) {
+ if (*piece != '\0') {
+ switch(i) {
+ case 0: { np++; break; }
+ case 1: {
+ nummap = atoi(piece);
+ if (nummap < 1) {
+ HUNSPELL_WARNING(stderr, "error: line %d: bad entry number\n", af->getlinenum());
+ return 1;
+ }
+ maptable = (mapentry *) malloc(nummap * sizeof(struct mapentry));
+ if (!maptable) return 1;
+ np++;
+ break;
+ }
+ default: break;
+ }
+ i++;
+ }
+ piece = mystrsep(&tp, 0);
+ }
+ if (np != 2) {
+ HUNSPELL_WARNING(stderr, "error: line %d: missing data\n", af->getlinenum());
+ return 1;
+ }
+
+ /* now parse the nummap lines to read in the remainder of the table */
+ char * nl;
+ for (int j=0; j < nummap; j++) {
+ if (!(nl = af->getline())) return 1;
+ mychomp(nl);
+ tp = nl;
+ i = 0;
+ maptable[j].set = NULL;
+ maptable[j].len = 0;
+ piece = mystrsep(&tp, 0);
+ while (piece) {
+ if (*piece != '\0') {
+ switch(i) {
+ case 0: {
+ if (strncmp(piece,"MAP",3) != 0) {
+ HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum());
+ nummap = 0;
+ return 1;
+ }
+ break;
+ }
+ case 1: {
+ int setn = 0;
+ maptable[j].len = strlen(piece);
+ maptable[j].set = (char **) malloc(maptable[j].len * sizeof(char*));
+ if (!maptable[j].set) return 1;
+ for (int k = 0; k < maptable[j].len; k++) {
+ int chl = 1;
+ int chb = k;
+ if (piece[k] == '(') {
+ char * parpos = strchr(piece + k, ')');
+ if (parpos != NULL) {
+ chb = k + 1;
+ chl = (int)(parpos - piece) - k - 1;
+ k = k + chl + 1;
+ }
+ } else {
+ if (utf8 && (piece[k] & 0xc0) == 0xc0) {
+ for (k++; utf8 && (piece[k] & 0xc0) == 0x80; k++);
+ chl = k - chb;
+ k--;
+ }
+ }
+ maptable[j].set[setn] = (char *) malloc(chl + 1);
+ if (!maptable[j].set[setn]) return 1;
+ strncpy(maptable[j].set[setn], piece + chb, chl);
+ maptable[j].set[setn][chl] = '\0';
+ setn++;
+ }
+ maptable[j].len = setn;
+ break; }
+ default: break;
+ }
+ i++;
+ }
+ piece = mystrsep(&tp, 0);
+ }
+ if (!maptable[j].set || !maptable[j].len) {
+ HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum());
+ nummap = 0;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/* parse in the word breakpoint table */
+int AffixMgr::parse_breaktable(char * line, FileMgr * af)
+{
+ if (numbreak > -1) {
+ HUNSPELL_WARNING(stderr, "error: line %d: multiple table definitions\n", af->getlinenum());
+ return 1;
+ }
+ char * tp = line;
+ char * piece;
+ int i = 0;
+ int np = 0;
+ piece = mystrsep(&tp, 0);
+ while (piece) {
+ if (*piece != '\0') {
+ switch(i) {
+ case 0: { np++; break; }
+ case 1: {
+ numbreak = atoi(piece);
+ if (numbreak < 0) {
+ HUNSPELL_WARNING(stderr, "error: line %d: bad entry number\n", af->getlinenum());
+ return 1;
+ }
+ if (numbreak == 0) return 0;
+ breaktable = (char **) malloc(numbreak * sizeof(char *));
+ if (!breaktable) return 1;
+ np++;
+ break;
+ }
+ default: break;
+ }
+ i++;
+ }
+ piece = mystrsep(&tp, 0);
+ }
+ if (np != 2) {
+ HUNSPELL_WARNING(stderr, "error: line %d: missing data\n", af->getlinenum());
+ return 1;
+ }
+
+ /* now parse the numbreak lines to read in the remainder of the table */
+ char * nl;
+ for (int j=0; j < numbreak; j++) {
+ if (!(nl = af->getline())) return 1;
+ mychomp(nl);
+ tp = nl;
+ i = 0;
+ piece = mystrsep(&tp, 0);
+ while (piece) {
+ if (*piece != '\0') {
+ switch(i) {
+ case 0: {
+ if (strncmp(piece,"BREAK",5) != 0) {
+ HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum());
+ numbreak = 0;
+ return 1;
+ }
+ break;
+ }
+ case 1: {
+ breaktable[j] = mystrdup(piece);
+ break;
+ }
+ default: break;
+ }
+ i++;
+ }
+ piece = mystrsep(&tp, 0);
+ }
+ if (!breaktable) {
+ HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum());
+ numbreak = 0;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+void AffixMgr::reverse_condition(char * piece) {
+ int neg = 0;
+ for (char * k = piece + strlen(piece) - 1; k >= piece; k--) {
+ switch(*k) {
+ case '[': {
+ if (neg) *(k+1) = '['; else *k = ']';
+ break;
+ }
+ case ']': {
+ *k = '[';
+ if (neg) *(k+1) = '^';
+ neg = 0;
+ break;
+ }
+ case '^': {
+ if (*(k+1) == ']') neg = 1; else *(k+1) = *k;
+ break;
+ }
+ default: {
+ if (neg) *(k+1) = *k;
+ }
+ }
+ }
+}
+
+int AffixMgr::parse_affix(char * line, const char at, FileMgr * af, char * dupflags)
+{
+ int numents = 0; // number of affentry structures to parse
+
+ unsigned short aflag = 0; // affix char identifier
+
+ char ff=0;
+ std::vector<affentry> affentries;
+
+ char * tp = line;
+ char * nl = line;
+ char * piece;
+ int i = 0;
+
+ // checking lines with bad syntax
+#ifdef DEBUG
+ int basefieldnum = 0;
+#endif
+
+ // split affix header line into pieces
+
+ int np = 0;
+
+ piece = mystrsep(&tp, 0);
+ while (piece) {
+ if (*piece != '\0') {
+ switch(i) {
+ // piece 1 - is type of affix
+ case 0: { np++; break; }
+
+ // piece 2 - is affix char
+ case 1: {
+ np++;
+ aflag = pHMgr->decode_flag(piece);
+ if (((at == 'S') && (dupflags[aflag] & dupSFX)) ||
+ ((at == 'P') && (dupflags[aflag] & dupPFX))) {
+ HUNSPELL_WARNING(stderr, "error: line %d: multiple definitions of an affix flag\n",
+ af->getlinenum());
+ // return 1; XXX permissive mode for bad dictionaries
+ }
+ dupflags[aflag] += (char) ((at == 'S') ? dupSFX : dupPFX);
+ break;
+ }
+ // piece 3 - is cross product indicator
+ case 2: { np++; if (*piece == 'Y') ff = aeXPRODUCT; break; }
+
+ // piece 4 - is number of affentries
+ case 3: {
+ np++;
+ numents = atoi(piece);
+ if (numents == 0) {
+ char * err = pHMgr->encode_flag(aflag);
+ if (err) {
+ HUNSPELL_WARNING(stderr, "error: line %d: bad entry number\n",
+ af->getlinenum());
+ free(err);
+ }
+ return 1;
+ }
+ affentries.resize(numents);
+ affentries[0].opts = ff;
+ if (utf8) affentries[0].opts += aeUTF8;
+ if (pHMgr->is_aliasf()) affentries[0].opts += aeALIASF;
+ if (pHMgr->is_aliasm()) affentries[0].opts += aeALIASM;
+ affentries[0].aflag = aflag;
+ }
+
+ default: break;
+ }
+ i++;
+ }
+ piece = mystrsep(&tp, 0);
+ }
+ // check to make sure we parsed enough pieces
+ if (np != 4) {
+ char * err = pHMgr->encode_flag(aflag);
+ if (err) {
+ HUNSPELL_WARNING(stderr, "error: line %d: missing data\n", af->getlinenum());
+ free(err);
+ }
+ return 1;
+ }
+
+ // now parse numents affentries for this affix
+ std::vector<affentry>::iterator start = affentries.begin();
+ std::vector<affentry>::iterator end = affentries.end();
+ for (std::vector<affentry>::iterator entry = start; entry != end; ++entry) {
+ if (!(nl = af->getline())) return 1;
+ mychomp(nl);
+ tp = nl;
+ i = 0;
+ np = 0;
+
+ // split line into pieces
+ piece = mystrsep(&tp, 0);
+ while (piece) {
+ if (*piece != '\0') {
+ switch(i) {
+ // piece 1 - is type
+ case 0: {
+ np++;
+ if (entry != start) entry->opts = start->opts &
+ (char) (aeXPRODUCT + aeUTF8 + aeALIASF + aeALIASM);
+ break;
+ }
+
+ // piece 2 - is affix char
+ case 1: {
+ np++;
+ if (pHMgr->decode_flag(piece) != aflag) {
+ char * err = pHMgr->encode_flag(aflag);
+ if (err) {
+ HUNSPELL_WARNING(stderr, "error: line %d: affix %s is corrupt\n",
+ af->getlinenum(), err);
+ free(err);
+ }
+ return 1;
+ }
+
+ if (entry != start) entry->aflag = start->aflag;
+ break;
+ }
+
+ // piece 3 - is string to strip or 0 for null
+ case 2: {
+ np++;
+ if (complexprefixes) {
+ if (utf8) reverseword_utf(piece); else reverseword(piece);
+ }
+ entry->strip = mystrdup(piece);
+ entry->stripl = (unsigned char) strlen(entry->strip);
+ if (strcmp(entry->strip,"0") == 0) {
+ free(entry->strip);
+ entry->strip=mystrdup("");
+ entry->stripl = 0;
+ }
+ break;
+ }
+
+ // piece 4 - is affix string or 0 for null
+ case 3: {
+ char * dash;
+ entry->morphcode = NULL;
+ entry->contclass = NULL;
+ entry->contclasslen = 0;
+ np++;
+ dash = strchr(piece, '/');
+ if (dash) {
+ *dash = '\0';
+
+ if (ignorechars) {
+ if (utf8) {
+ remove_ignored_chars_utf(piece, ignorechars_utf16, ignorechars_utf16_len);
+ } else {
+ remove_ignored_chars(piece,ignorechars);
+ }
+ }
+
+ if (complexprefixes) {
+ if (utf8) reverseword_utf(piece); else reverseword(piece);
+ }
+ entry->appnd = mystrdup(piece);
+
+ if (pHMgr->is_aliasf()) {
+ int index = atoi(dash + 1);
+ entry->contclasslen = (unsigned short) pHMgr->get_aliasf(index, &(entry->contclass), af);
+ if (!entry->contclasslen) HUNSPELL_WARNING(stderr, "error: bad affix flag alias: \"%s\"\n", dash+1);
+ } else {
+ entry->contclasslen = (unsigned short) pHMgr->decode_flags(&(entry->contclass), dash + 1, af);
+ flag_qsort(entry->contclass, 0, entry->contclasslen);
+ }
+ *dash = '/';
+
+ havecontclass = 1;
+ for (unsigned short _i = 0; _i < entry->contclasslen; _i++) {
+ contclasses[(entry->contclass)[_i]] = 1;
+ }
+ } else {
+ if (ignorechars) {
+ if (utf8) {
+ remove_ignored_chars_utf(piece, ignorechars_utf16, ignorechars_utf16_len);
+ } else {
+ remove_ignored_chars(piece,ignorechars);
+ }
+ }
+
+ if (complexprefixes) {
+ if (utf8) reverseword_utf(piece); else reverseword(piece);
+ }
+ entry->appnd = mystrdup(piece);
+ }
+
+ entry->appndl = (unsigned char) strlen(entry->appnd);
+ if (strcmp(entry->appnd,"0") == 0) {
+ free(entry->appnd);
+ entry->appnd=mystrdup("");
+ entry->appndl = 0;
+ }
+ break;
+ }
+
+ // piece 5 - is the conditions descriptions
+ case 4: {
+ np++;
+ if (complexprefixes) {
+ if (utf8) reverseword_utf(piece); else reverseword(piece);
+ reverse_condition(piece);
+ }
+ if (entry->stripl && (strcmp(piece, ".") != 0) &&
+ redundant_condition(at, entry->strip, entry->stripl, piece, af->getlinenum()))
+ strcpy(piece, ".");
+ if (at == 'S') {
+ reverseword(piece);
+ reverse_condition(piece);
+ }
+ if (encodeit(*entry, piece)) return 1;
+ break;
+ }
+
+ case 5: {
+ np++;
+ if (pHMgr->is_aliasm()) {
+ int index = atoi(piece);
+ entry->morphcode = pHMgr->get_aliasm(index);
+ } else {
+ if (complexprefixes) { // XXX - fix me for morph. gen.
+ if (utf8) reverseword_utf(piece); else reverseword(piece);
+ }
+ // add the remaining of the line
+ if (*tp) {
+ *(tp - 1) = ' ';
+ tp = tp + strlen(tp);
+ }
+ entry->morphcode = mystrdup(piece);
+ if (!entry->morphcode) return 1;
+ }
+ break;
+ }
+ default: break;
+ }
+ i++;
+ }
+ piece = mystrsep(&tp, 0);
+ }
+ // check to make sure we parsed enough pieces
+ if (np < 4) {
+ char * err = pHMgr->encode_flag(aflag);
+ if (err) {
+ HUNSPELL_WARNING(stderr, "error: line %d: affix %s is corrupt\n",
+ af->getlinenum(), err);
+ free(err);
+ }
+ return 1;
+ }
+
+#ifdef DEBUG
+ // detect unnecessary fields, excepting comments
+ if (basefieldnum) {
+ int fieldnum = !(entry->morphcode) ? 5 : ((*(entry->morphcode)=='#') ? 5 : 6);
+ if (fieldnum != basefieldnum)
+ HUNSPELL_WARNING(stderr, "warning: line %d: bad field number\n", af->getlinenum());
+ } else {
+ basefieldnum = !(entry->morphcode) ? 5 : ((*(entry->morphcode)=='#') ? 5 : 6);
+ }
+#endif
+ }
+
+ // now create SfxEntry or PfxEntry objects and use links to
+ // build an ordered (sorted by affix string) list
+ for (std::vector<affentry>::iterator entry = start; entry != end; ++entry) {
+ if (at == 'P') {
+ PfxEntry * pfxptr = new PfxEntry(this,&(*entry));
+ build_pfxtree(pfxptr);
+ } else {
+ SfxEntry * sfxptr = new SfxEntry(this,&(*entry));
+ build_sfxtree(sfxptr);
+ }
+ }
+ return 0;
+}
+
+int AffixMgr::redundant_condition(char ft, char * strip, int stripl, const char * cond, int linenum) {
+ int condl = strlen(cond);
+ int i;
+ int j;
+ int neg;
+ int in;
+ if (ft == 'P') { // prefix
+ if (strncmp(strip, cond, condl) == 0) return 1;
+ if (utf8) {
+ } else {
+ for (i = 0, j = 0; (i < stripl) && (j < condl); i++, j++) {
+ if (cond[j] != '[') {
+ if (cond[j] != strip[i]) {
+ HUNSPELL_WARNING(stderr, "warning: line %d: incompatible stripping characters and condition\n", linenum);
+ return 0;
+ }
+ } else {
+ neg = (cond[j+1] == '^') ? 1 : 0;
+ in = 0;
+ do {
+ j++;
+ if (strip[i] == cond[j]) in = 1;
+ } while ((j < (condl - 1)) && (cond[j] != ']'));
+ if (j == (condl - 1) && (cond[j] != ']')) {
+ HUNSPELL_WARNING(stderr, "error: line %d: missing ] in condition:\n%s\n", linenum, cond);
+ return 0;
+ }
+ if ((!neg && !in) || (neg && in)) {
+ HUNSPELL_WARNING(stderr, "warning: line %d: incompatible stripping characters and condition\n", linenum);
+ return 0;
+ }
+ }
+ }
+ if (j >= condl) return 1;
+ }
+ } else { // suffix
+ if ((stripl >= condl) && strcmp(strip + stripl - condl, cond) == 0) return 1;
+ if (utf8) {
+ } else {
+ for (i = stripl - 1, j = condl - 1; (i >= 0) && (j >= 0); i--, j--) {
+ if (cond[j] != ']') {
+ if (cond[j] != strip[i]) {
+ HUNSPELL_WARNING(stderr, "warning: line %d: incompatible stripping characters and condition\n", linenum);
+ return 0;
+ }
+ } else {
+ in = 0;
+ do {
+ j--;
+ if (strip[i] == cond[j]) in = 1;
+ } while ((j > 0) && (cond[j] != '['));
+ if ((j == 0) && (cond[j] != '[')) {
+ HUNSPELL_WARNING(stderr, "error: line: %d: missing ] in condition:\n%s\n", linenum, cond);
+ return 0;
+ }
+ neg = (cond[j+1] == '^') ? 1 : 0;
+ if ((!neg && !in) || (neg && in)) {
+ HUNSPELL_WARNING(stderr, "warning: line %d: incompatible stripping characters and condition\n", linenum);
+ return 0;
+ }
+ }
+ }
+ if (j < 0) return 1;
+ }
+ }
+ return 0;
+}
diff --git a/Plugins/spellchecker/hunspell/affixmgr.hxx b/Plugins/spellchecker/hunspell/affixmgr.hxx
new file mode 100644
index 0000000..d9c625a
--- /dev/null
+++ b/Plugins/spellchecker/hunspell/affixmgr.hxx
@@ -0,0 +1,250 @@
+#ifndef _AFFIXMGR_HXX_
+#define _AFFIXMGR_HXX_
+
+#include "hunvisapi.h"
+
+#include <stdio.h>
+
+#include "atypes.hxx"
+#include "baseaffix.hxx"
+#include "hashmgr.hxx"
+#include "phonet.hxx"
+#include "replist.hxx"
+
+// check flag duplication
+#define dupSFX (1 << 0)
+#define dupPFX (1 << 1)
+
+class PfxEntry;
+class SfxEntry;
+
+class LIBHUNSPELL_DLL_EXPORTED AffixMgr
+{
+
+ PfxEntry * pStart[SETSIZE];
+ SfxEntry * sStart[SETSIZE];
+ PfxEntry * pFlag[SETSIZE];
+ SfxEntry * sFlag[SETSIZE];
+ HashMgr * pHMgr;
+ HashMgr ** alldic;
+ int * maxdic;
+ char * keystring;
+ char * trystring;
+ char * encoding;
+ struct cs_info * csconv;
+ int utf8;
+ int complexprefixes;
+ FLAG compoundflag;
+ FLAG compoundbegin;
+ FLAG compoundmiddle;
+ FLAG compoundend;
+ FLAG compoundroot;
+ FLAG compoundforbidflag;
+ FLAG compoundpermitflag;
+ int checkcompounddup;
+ int checkcompoundrep;
+ int checkcompoundcase;
+ int checkcompoundtriple;
+ int simplifiedtriple;
+ FLAG forbiddenword;
+ FLAG nosuggest;
+ FLAG nongramsuggest;
+ FLAG needaffix;
+ int cpdmin;
+ int numrep;
+ replentry * reptable;
+ RepList * iconvtable;
+ RepList * oconvtable;
+ int nummap;
+ mapentry * maptable;
+ int numbreak;
+ char ** breaktable;
+ int numcheckcpd;
+ patentry * checkcpdtable;
+ int simplifiedcpd;
+ int numdefcpd;
+ flagentry * defcpdtable;
+ phonetable * phone;
+ int maxngramsugs;
+ int maxcpdsugs;
+ int maxdiff;
+ int onlymaxdiff;
+ int nosplitsugs;
+ int sugswithdots;
+ int cpdwordmax;
+ int cpdmaxsyllable;
+ char * cpdvowels;
+ w_char * cpdvowels_utf16;
+ int cpdvowels_utf16_len;
+ char * cpdsyllablenum;
+ const char * pfxappnd; // BUG: not stateless
+ const char * sfxappnd; // BUG: not stateless
+ FLAG sfxflag; // BUG: not stateless
+ char * derived; // BUG: not stateless
+ SfxEntry * sfx; // BUG: not stateless
+ PfxEntry * pfx; // BUG: not stateless
+ int checknum;
+ char * wordchars;
+ unsigned short * wordchars_utf16;
+ int wordchars_utf16_len;
+ char * ignorechars;
+ unsigned short * ignorechars_utf16;
+ int ignorechars_utf16_len;
+ char * version;
+ char * lang;
+ int langnum;
+ FLAG lemma_present;
+ FLAG circumfix;
+ FLAG onlyincompound;
+ FLAG keepcase;
+ FLAG forceucase;
+ FLAG warn;
+ int forbidwarn;
+ FLAG substandard;
+ int checksharps;
+ int fullstrip;
+
+ int havecontclass; // boolean variable
+ char contclasses[CONTSIZE]; // flags of possible continuing classes (twofold affix)
+
+public:
+
+ AffixMgr(const char * affpath, HashMgr** ptr, int * md,
+ const char * key = NULL);
+ ~AffixMgr();
+ struct hentry * affix_check(const char * word, int len,
+ const unsigned short needflag = (unsigned short) 0,
+ char in_compound = IN_CPD_NOT);
+ struct hentry * prefix_check(const char * word, int len,
+ char in_compound, const FLAG needflag = FLAG_NULL);
+ inline int isSubset(const char * s1, const char * s2);
+ struct hentry * prefix_check_twosfx(const char * word, int len,
+ char in_compound, const FLAG needflag = FLAG_NULL);
+ inline int isRevSubset(const char * s1, const char * end_of_s2, int len);
+ struct hentry * suffix_check(const char * word, int len, int sfxopts,
+ PfxEntry* ppfx, char ** wlst, int maxSug, int * ns,
+ const FLAG cclass = FLAG_NULL, const FLAG needflag = FLAG_NULL,
+ char in_compound = IN_CPD_NOT);
+ struct hentry * suffix_check_twosfx(const char * word, int len,
+ int sfxopts, PfxEntry* ppfx, const FLAG needflag = FLAG_NULL);
+
+ char * affix_check_morph(const char * word, int len,
+ const FLAG needflag = FLAG_NULL, char in_compound = IN_CPD_NOT);
+ char * prefix_check_morph(const char * word, int len,
+ char in_compound, const FLAG needflag = FLAG_NULL);
+ char * suffix_check_morph (const char * word, int len, int sfxopts,
+ PfxEntry * ppfx, const FLAG cclass = FLAG_NULL,
+ const FLAG needflag = FLAG_NULL, char in_compound = IN_CPD_NOT);
+
+ char * prefix_check_twosfx_morph(const char * word, int len,
+ char in_compound, const FLAG needflag = FLAG_NULL);
+ char * suffix_check_twosfx_morph(const char * word, int len,
+ int sfxopts, PfxEntry * ppfx, const FLAG needflag = FLAG_NULL);
+
+ char * morphgen(char * ts, int wl, const unsigned short * ap,
+ unsigned short al, char * morph, char * targetmorph, int level);
+
+ int expand_rootword(struct guessword * wlst, int maxn, const char * ts,
+ int wl, const unsigned short * ap, unsigned short al, char * bad,
+ int, char *);
+
+ short get_syllable (const char * word, int wlen);
+ int cpdrep_check(const char * word, int len);
+ int cpdpat_check(const char * word, int len, hentry * r1, hentry * r2,
+ const char affixed);
+ int defcpd_check(hentry *** words, short wnum, hentry * rv,
+ hentry ** rwords, char all);
+ int cpdcase_check(const char * word, int len);
+ inline int candidate_check(const char * word, int len);
+ void setcminmax(int * cmin, int * cmax, const char * word, int len);
+ struct hentry * compound_check(const char * word, int len, short wordnum,
+ short numsyllable, short maxwordnum, short wnum, hentry ** words,
+ char hu_mov_rule, char is_sug, int * info);
+
+ int compound_check_morph(const char * word, int len, short wordnum,
+ short numsyllable, short maxwordnum, short wnum, hentry ** words,
+ char hu_mov_rule, char ** result, char * partresult);
+
+ struct hentry * lookup(const char * word);
+ int get_numrep() const;
+ struct replentry * get_reptable() const;
+ RepList * get_iconvtable() const;
+ RepList * get_oconvtable() const;
+ struct phonetable * get_phonetable() const;
+ int get_nummap() const;
+ struct mapentry * get_maptable() const;
+ int get_numbreak() const;
+ char ** get_breaktable() const;
+ char * get_encoding();
+ int get_langnum() const;
+ char * get_key_string();
+ char * get_try_string() const;
+ const char * get_wordchars() const;
+ unsigned short * get_wordchars_utf16(int * len) const;
+ char * get_ignore() const;
+ unsigned short * get_ignore_utf16(int * len) const;
+ int get_compound() const;
+ FLAG get_compoundflag() const;
+ FLAG get_compoundbegin() const;
+ FLAG get_forbiddenword() const;
+ FLAG get_nosuggest() const;
+ FLAG get_nongramsuggest() const;
+ FLAG get_needaffix() const;
+ FLAG get_onlyincompound() const;
+ FLAG get_compoundroot() const;
+ FLAG get_lemma_present() const;
+ int get_checknum() const;
+ const char * get_prefix() const;
+ const char * get_suffix() const;
+ const char * get_derived() const;
+ const char * get_version() const;
+ int have_contclass() const;
+ int get_utf8() const;
+ int get_complexprefixes() const;
+ char * get_suffixed(char ) const;
+ int get_maxngramsugs() const;
+ int get_maxcpdsugs() const;
+ int get_maxdiff() const;
+ int get_onlymaxdiff() const;
+ int get_nosplitsugs() const;
+ int get_sugswithdots(void) const;
+ FLAG get_keepcase(void) const;
+ FLAG get_forceucase(void) const;
+ FLAG get_warn(void) const;
+ int get_forbidwarn(void) const;
+ int get_checksharps(void) const;
+ char * encode_flag(unsigned short aflag) const;
+ int get_fullstrip() const;
+
+private:
+ int parse_file(const char * affpath, const char * key);
+ int parse_flag(char * line, unsigned short * out, FileMgr * af);
+ int parse_num(char * line, int * out, FileMgr * af);
+ int parse_cpdsyllable(char * line, FileMgr * af);
+ int parse_reptable(char * line, FileMgr * af);
+ int parse_convtable(char * line, FileMgr * af, RepList ** rl, const char * keyword);
+ int parse_phonetable(char * line, FileMgr * af);
+ int parse_maptable(char * line, FileMgr * af);
+ int parse_breaktable(char * line, FileMgr * af);
+ int parse_checkcpdtable(char * line, FileMgr * af);
+ int parse_defcpdtable(char * line, FileMgr * af);
+ int parse_affix(char * line, const char at, FileMgr * af, char * dupflags);
+
+ void reverse_condition(char *);
+ void debugflag(char * result, unsigned short flag);
+ int condlen(char *);
+ int encodeit(affentry &entry, char * cs);
+ int build_pfxtree(PfxEntry* pfxptr);
+ int build_sfxtree(SfxEntry* sfxptr);
+ int process_pfx_order();
+ int process_sfx_order();
+ PfxEntry * process_pfx_in_order(PfxEntry * ptr, PfxEntry * nptr);
+ SfxEntry * process_sfx_in_order(SfxEntry * ptr, SfxEntry * nptr);
+ int process_pfx_tree_to_list();
+ int process_sfx_tree_to_list();
+ int redundant_condition(char, char * strip, int stripl,
+ const char * cond, int);
+};
+
+#endif
+
diff --git a/Plugins/spellchecker/hunspell/atypes.hxx b/Plugins/spellchecker/hunspell/atypes.hxx
new file mode 100644
index 0000000..df27c4d
--- /dev/null
+++ b/Plugins/spellchecker/hunspell/atypes.hxx
@@ -0,0 +1,107 @@
+#ifndef _ATYPES_HXX_
+#define _ATYPES_HXX_
+
+#ifndef HUNSPELL_WARNING
+#include <stdio.h>
+#ifdef HUNSPELL_WARNING_ON
+#define HUNSPELL_WARNING fprintf
+#else
+// empty inline function to switch off warnings (instead of the C99 standard variadic macros)
+static inline void HUNSPELL_WARNING(FILE *, const char *, ...) {}
+#endif
+#endif
+
+// HUNSTEM def.
+#define HUNSTEM
+
+#include "hashmgr.hxx"
+#include "w_char.hxx"
+
+#define SETSIZE 256
+#define CONTSIZE 65536
+#define MAXWORDLEN 100
+#define MAXWORDUTF8LEN 256
+
+// affentry options
+#define aeXPRODUCT (1 << 0)
+#define aeUTF8 (1 << 1)
+#define aeALIASF (1 << 2)
+#define aeALIASM (1 << 3)
+#define aeLONGCOND (1 << 4)
+
+// compound options
+#define IN_CPD_NOT 0
+#define IN_CPD_BEGIN 1
+#define IN_CPD_END 2
+#define IN_CPD_OTHER 3
+
+// info options
+#define SPELL_COMPOUND (1 << 0)
+#define SPELL_FORBIDDEN (1 << 1)
+#define SPELL_ALLCAP (1 << 2)
+#define SPELL_NOCAP (1 << 3)
+#define SPELL_INITCAP (1 << 4)
+#define SPELL_ORIGCAP (1 << 5)
+#define SPELL_WARN (1 << 6)
+
+#define MAXLNLEN 8192
+
+#define MINCPDLEN 3
+#define MAXCOMPOUND 10
+#define MAXCONDLEN 20
+#define MAXCONDLEN_1 (MAXCONDLEN - sizeof(char *))
+
+#define MAXACC 1000
+
+#define FLAG unsigned short
+#define FLAG_NULL 0x00
+#define FREE_FLAG(a) a = 0
+
+#define TESTAFF( a, b , c ) flag_bsearch((unsigned short *) a, (unsigned short) b, c)
+
+struct affentry
+{
+ char * strip;
+ char * appnd;
+ unsigned char stripl;
+ unsigned char appndl;
+ char numconds;
+ char opts;
+ unsigned short aflag;
+ unsigned short * contclass;
+ short contclasslen;
+ union {
+ char conds[MAXCONDLEN];
+ struct {
+ char conds1[MAXCONDLEN_1];
+ char * conds2;
+ } l;
+ } c;
+ char * morphcode;
+};
+
+struct guessword {
+ char * word;
+ bool allow;
+ char * orig;
+};
+
+struct mapentry {
+ char ** set;
+ int len;
+};
+
+struct flagentry {
+ FLAG * def;
+ int len;
+};
+
+struct patentry {
+ char * pattern;
+ char * pattern2;
+ char * pattern3;
+ FLAG cond;
+ FLAG cond2;
+};
+
+#endif
diff --git a/Plugins/spellchecker/hunspell/baseaffix.hxx b/Plugins/spellchecker/hunspell/baseaffix.hxx
new file mode 100644
index 0000000..ed64f3d
--- /dev/null
+++ b/Plugins/spellchecker/hunspell/baseaffix.hxx
@@ -0,0 +1,28 @@
+#ifndef _BASEAFF_HXX_
+#define _BASEAFF_HXX_
+
+#include "hunvisapi.h"
+
+class LIBHUNSPELL_DLL_EXPORTED AffEntry
+{
+protected:
+ char * appnd;
+ char * strip;
+ unsigned char appndl;
+ unsigned char stripl;
+ char numconds;
+ char opts;
+ unsigned short aflag;
+ union {
+ char conds[MAXCONDLEN];
+ struct {
+ char conds1[MAXCONDLEN_1];
+ char * conds2;
+ } l;
+ } c;
+ char * morphcode;
+ unsigned short * contclass;
+ short contclasslen;
+};
+
+#endif
diff --git a/Plugins/spellchecker/hunspell/config.h b/Plugins/spellchecker/hunspell/config.h
new file mode 100644
index 0000000..8c84118
--- /dev/null
+++ b/Plugins/spellchecker/hunspell/config.h
@@ -0,0 +1,215 @@
+/* config.h.in. Generated from configure.ac by autoheader. */
+
+/* Define to one of `_getb67', `GETB67', `getb67' for Cray-2 and Cray-YMP
+ systems. This function is required for `alloca.c' support on those systems.
+ */
+#define CRAY_STACKSEG_END 1
+
+/* Define to 1 if using `alloca.c'. */
+#define C_ALLOCA 1
+
+/* Define to 1 if translation of program messages to the user's native
+ language is requested. */
+#undef ENABLE_NLS
+
+/* Define to 1 if you have `alloca', as a function or macro. */
+#define HAVE_ALLOCA 1
+
+/* Define to 1 if you have <alloca.h> and it should be used (not on Ultrix).
+ */
+#define HAVE_ALLOCA_H 1
+
+/* Define to 1 if you have the <argz.h> header file. */
+#define HAVE_ARGZ_H 1
+
+/* "Define if you have the <curses.h> header" */
+#undef HAVE_CURSES_H
+
+/* Define if the GNU dcgettext() function is already present or preinstalled.
+ */
+#define HAVE_DCGETTEXT 1
+
+/* Define to 1 if you have the <dlfcn.h> header file. */
+#define HAVE_DLFCN_H 1
+
+/* Define to 1 if you have the <error.h> header file. */
+#define HAVE_ERROR_H 1
+
+/* Define to 1 if you have the <fcntl.h> header file. */
+#define HAVE_FCNTL_H 1
+
+/* Define to 1 if you have the `feof_unlocked' function. */
+#define HAVE_FEOF_UNLOCKED 1
+
+/* Define to 1 if you have the `fgets_unlocked' function. */
+#define HAVE_FGETS_UNLOCKED 1
+
+/* Define to 1 if you have the `getcwd' function. */
+#define HAVE_GETCWD 1
+
+/* Define to 1 if you have the `getc_unlocked' function. */
+#define HAVE_GETC_UNLOCKED 1
+
+/* Define to 1 if you have the `getegid' function. */
+#define HAVE_GETEGID 1
+
+/* Define to 1 if you have the `geteuid' function. */
+#define HAVE_GETEUID 1
+
+/* Define to 1 if you have the `getgid' function. */
+#define HAVE_GETGID 1
+
+/* Define to 1 if you have the `getpagesize' function. */
+#define HAVE_GETPAGESIZE 1
+
+/* Define if the GNU gettext() function is already present or preinstalled. */
+#define HAVE_GETTEXT 1
+
+/* Define to 1 if you have the `getuid' function. */
+#define HAVE_GETUID 1
+
+/* Define if you have the iconv() function. */
+#undef HAVE_ICONV
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#define HAVE_INTTYPES_H 1
+
+/* Define if you have <langinfo.h> and nl_langinfo(CODESET). */
+#define HAVE_LANGINFO_CODESET 1
+
+/* Define if your <locale.h> file defines LC_MESSAGES. */
+#define HAVE_LC_MESSAGES 1
+
+/* Define to 1 if you have the <libintl.h> header file. */
+#define HAVE_LIBINTL_H 1
+
+/* Define to 1 if you have the <limits.h> header file. */
+#define HAVE_LIMITS_H 1
+
+/* Define to 1 if you have the <locale.h> header file. */
+#define HAVE_LOCALE_H 1
+
+/* Define to 1 if your system has a GNU libc compatible `malloc' function, and
+ to 0 otherwise. */
+#define HAVE_MALLOC 1
+
+/* Define to 1 if you have the <malloc.h> header file. */
+#define HAVE_MALLOC_H 1
+
+/* Define to 1 if you have the `memchr' function. */
+#define HAVE_MEMCHR 1
+
+/* Define to 1 if you have the <memory.h> header file. */
+#define HAVE_MEMORY_H 1
+
+/* Define to 1 if you have the `mempcpy' function. */
+#define HAVE_MEMPCPY 1
+
+/* Define to 1 if you have a working `mmap' system call. */
+#define HAVE_MMAP 1
+
+/* Define to 1 if you have the `munmap' function. */
+#define HAVE_MUNMAP 1
+
+/* "Define if you have the <ncursesw/curses.h> header" */
+#define HAVE_NCURSESW_H 1
+
+/* Define to 1 if you have the <nl_types.h> header file. */
+#define HAVE_NL_TYPES_H 1
+
+/* Define to 1 if you have the `putenv' function. */
+#define HAVE_PUTENV 1
+
+/* "Define if you have fancy command input editing with Readline" */
+#undef HAVE_READLINE
+
+/* Define to 1 if you have the `setenv' function. */
+#define HAVE_SETENV 1
+
+/* Define to 1 if you have the `setlocale' function. */
+#define HAVE_SETLOCALE 1
+
+/* Define to 1 if you have the <stddef.h> header file. */
+#define HAVE_STDDEF_H 1
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#define HAVE_STDINT_H 1
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#define HAVE_STDLIB_H 1
+
+/* Define to 1 if you have the `stpcpy' function. */
+#define HAVE_STPCPY 1
+
+/* Define to 1 if you have the `strcasecmp' function. */
+#define HAVE_STRCASECMP 1
+
+/* Define to 1 if you have the `strchr' function. */
+#define HAVE_STRCHR 1
+
+/* Define to 1 if you have the `strdup' function. */
+#define HAVE_STRDUP 1
+
+/* Define to 1 if you have the <strings.h> header file. */
+#define HAVE_STRINGS_H 1
+
+/* Define to 1 if you have the <string.h> header file. */
+#define HAVE_STRING_H 1
+
+/* Define to 1 if you have the `strstr' function. */
+#define HAVE_STRSTR 1
+
+/* Define to 1 if you have the `strtoul' function. */
+#define HAVE_STRTOUL 1
+
+/* Define to 1 if you have the <sys/param.h> header file. */
+#define HAVE_SYS_PARAM_H 1
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#define HAVE_SYS_STAT_H 1
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#define HAVE_SYS_TYPES_H 1
+
+/* Define to 1 if you have the `tsearch' function. */
+#define HAVE_TSEARCH 1
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#define HAVE_UNISTD_H 1
+
+/* Define to 1 if you have the `__argz_count' function. */
+#define HAVE___ARGZ_COUNT 1
+
+/* Define to 1 if you have the `__argz_next' function. */
+#define HAVE___ARGZ_NEXT 1
+
+/* Define to 1 if you have the `__argz_stringify' function. */
+#define HAVE___ARGZ_STRINGIFY 1
+
+/* "Define if you use exterimental functions" */
+#define HUNSPELL_EXPERIMENTAL 1
+
+/* "Define if you need warning messages" */
+#define HUNSPELL_WARNING_ON
+
+/* Define as const if the declaration of iconv() needs const. */
+#define ICONV_CONST 1
+
+/* Name of package */
+#define PACKAGE
+
+/* Define to the address where bug reports for this package should be sent. */
+#define PACKAGE_BUGREPORT
+
+/* Define to the full name of this package. */
+#define PACKAGE_NAME
+
+/* Define to the full name and version of this package. */
+#define PACKAGE_STRING
+
+/* Define to the one symbol short name of this package. */
+#define PACKAGE_TARNAME
+
+/* Define to the version of this package. */
+#define PACKAGE_VERSION "1.3.1"
+#define VERSION "1.3.1"
diff --git a/Plugins/spellchecker/hunspell/csutil.cxx b/Plugins/spellchecker/hunspell/csutil.cxx
new file mode 100644
index 0000000..dd89c19
--- /dev/null
+++ b/Plugins/spellchecker/hunspell/csutil.cxx
@@ -0,0 +1,5834 @@
+#include "license.hunspell"
+#include "license.myspell"
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <ctype.h>
+
+#include "csutil.hxx"
+#include "atypes.hxx"
+#include "langnum.hxx"
+
+// Unicode character encoding information
+struct unicode_info {
+ unsigned short c;
+ unsigned short cupper;
+ unsigned short clower;
+};
+
+#ifdef OPENOFFICEORG
+# include <unicode/uchar.h>
+#else
+# ifndef MOZILLA_CLIENT
+# include "utf_info.cxx"
+# define UTF_LST_LEN (sizeof(utf_lst) / (sizeof(unicode_info)))
+# endif
+#endif
+
+#ifdef MOZILLA_CLIENT
+#include "nsCOMPtr.h"
+#include "nsServiceManagerUtils.h"
+#include "nsIUnicodeEncoder.h"
+#include "nsIUnicodeDecoder.h"
+#include "nsUnicharUtils.h"
+#include "nsICharsetConverterManager.h"
+
+static NS_DEFINE_CID(kCharsetConverterManagerCID, NS_ICHARSETCONVERTERMANAGER_CID);
+#endif
+
+struct unicode_info2 {
+ char cletter;
+ unsigned short cupper;
+ unsigned short clower;
+};
+
+static struct unicode_info2 * utf_tbl = NULL;
+static int utf_tbl_count = 0; // utf_tbl can be used by multiple Hunspell instances
+
+/* only UTF-16 (BMP) implementation */
+char * u16_u8(char * dest, int size, const w_char * src, int srclen) {
+ signed char * u8 = (signed char *)dest;
+ signed char * u8_max = (signed char *)(u8 + size);
+ const w_char * u2 = src;
+ const w_char * u2_max = src + srclen;
+ while ((u2 < u2_max) && (u8 < u8_max)) {
+ if (u2->h) { // > 0xFF
+ // XXX 4-byte haven't implemented yet.
+ if (u2->h >= 0x08) { // >= 0x800 (3-byte UTF-8 character)
+ *u8 = 0xe0 + (u2->h >> 4);
+ u8++;
+ if (u8 < u8_max) {
+ *u8 = 0x80 + ((u2->h & 0xf) << 2) + (u2->l >> 6);
+ u8++;
+ if (u8 < u8_max) {
+ *u8 = 0x80 + (u2->l & 0x3f);
+ u8++;
+ }
+ }
+ } else { // < 0x800 (2-byte UTF-8 character)
+ *u8 = 0xc0 + (u2->h << 2) + (u2->l >> 6);
+ u8++;
+ if (u8 < u8_max) {
+ *u8 = 0x80 + (u2->l & 0x3f);
+ u8++;
+ }
+ }
+ } else { // <= 0xFF
+ if (u2->l & 0x80) { // >0x80 (2-byte UTF-8 character)
+ *u8 = 0xc0 + (u2->l >> 6);
+ u8++;
+ if (u8 < u8_max) {
+ *u8 = 0x80 + (u2->l & 0x3f);
+ u8++;
+ }
+ } else { // < 0x80 (1-byte UTF-8 character)
+ *u8 = u2->l;
+ u8++;
+ }
+ }
+ u2++;
+ }
+ *u8 = '\0';
+ return dest;
+}
+
+
+/* only UTF-16 (BMP) implementation */
+int u8_u16(w_char * dest, int size, const char * src) {
+ const signed char * u8 = (const signed char *)src;
+ w_char * u2 = dest;
+ w_char * u2_max = u2 + size;
+
+ while ((u2 < u2_max) && *u8) {
+ switch ((*u8) & 0xf0) {
+ case 0x00:
+ case 0x10:
+ case 0x20:
+ case 0x30:
+ case 0x40:
+ case 0x50:
+ case 0x60:
+ case 0x70: {
+ u2->h = 0;
+ u2->l = *u8;
+ break;
+ }
+ case 0x80:
+ case 0x90:
+ case 0xa0:
+ case 0xb0: {
+ HUNSPELL_WARNING(stderr, "UTF-8 encoding error. Unexpected continuation bytes in %ld. character position\n%s\n", static_cast<long>(u8 - (signed char *)src), src);
+ u2->h = 0xff;
+ u2->l = 0xfd;
+ break;
+ }
+ case 0xc0:
+ case 0xd0: { // 2-byte UTF-8 codes
+ if ((*(u8+1) & 0xc0) == 0x80) {
+ u2->h = (*u8 & 0x1f) >> 2;
+ u2->l = (*u8 << 6) + (*(u8+1) & 0x3f);
+ u8++;
+ } else {
+ HUNSPELL_WARNING(stderr, "UTF-8 encoding error. Missing continuation byte in %ld. character position:\n%s\n", static_cast<long>(u8 - (signed char *)src), src);
+ u2->h = 0xff;
+ u2->l = 0xfd;
+ }
+ break;
+ }
+ case 0xe0: { // 3-byte UTF-8 codes
+ if ((*(u8+1) & 0xc0) == 0x80) {
+ u2->h = ((*u8 & 0x0f) << 4) + ((*(u8+1) & 0x3f) >> 2);
+ u8++;
+ if ((*(u8+1) & 0xc0) == 0x80) {
+ u2->l = (*u8 << 6) + (*(u8+1) & 0x3f);
+ u8++;
+ } else {
+ HUNSPELL_WARNING(stderr, "UTF-8 encoding error. Missing continuation byte in %ld. character position:\n%s\n", static_cast<long>(u8 - (signed char *)src), src);
+ u2->h = 0xff;
+ u2->l = 0xfd;
+ }
+ } else {
+ HUNSPELL_WARNING(stderr, "UTF-8 encoding error. Missing continuation byte in %ld. character position:\n%s\n", static_cast<long>(u8 - (signed char *)src), src);
+ u2->h = 0xff;
+ u2->l = 0xfd;
+ }
+ break;
+ }
+ case 0xf0: { // 4 or more byte UTF-8 codes
+ HUNSPELL_WARNING(stderr, "This UTF-8 encoding can't convert to UTF-16:\n%s\n", src);
+ u2->h = 0xff;
+ u2->l = 0xfd;
+ return -1;
+ }
+ }
+ u8++;
+ u2++;
+ }
+ return (int)(u2 - dest);
+}
+
+void flag_qsort(unsigned short flags[], int begin, int end) {
+ unsigned short reg;
+ if (end > begin) {
+ unsigned short pivot = flags[begin];
+ int l = begin + 1;
+ int r = end;
+ while(l < r) {
+ if (flags[l] <= pivot) {
+ l++;
+ } else {
+ r--;
+ reg = flags[l];
+ flags[l] = flags[r];
+ flags[r] = reg;
+ }
+ }
+ l--;
+ reg = flags[begin];
+ flags[begin] = flags[l];
+ flags[l] = reg;
+
+ flag_qsort(flags, begin, l);
+ flag_qsort(flags, r, end);
+ }
+ }
+
+int flag_bsearch(unsigned short flags[], unsigned short flag, int length) {
+ int mid;
+ int left = 0;
+ int right = length - 1;
+ while (left <= right) {
+ mid = (left + right) / 2;
+ if (flags[mid] == flag) return 1;
+ if (flag < flags[mid]) right = mid - 1;
+ else left = mid + 1;
+ }
+ return 0;
+}
+
+ // strip strings into token based on single char delimiter
+ // acts like strsep() but only uses a delim char and not
+ // a delim string
+ // default delimiter: white space characters
+
+ char * mystrsep(char ** stringp, const char delim)
+ {
+ char * mp = *stringp;
+ if (*mp != '\0') {
+ char * dp;
+ if (delim) {
+ dp = strchr(mp, delim);
+ } else {
+ // don't use isspace() here, the string can be in some random charset
+ // that's way different than the locale's
+ for (dp = mp; (*dp && *dp != ' ' && *dp != '\t'); dp++);
+ if (!*dp) dp = NULL;
+ }
+ if (dp) {
+ *stringp = dp+1;
+ *dp = '\0';
+ } else {
+ *stringp = mp + strlen(mp);
+ }
+ return mp;
+ }
+ return NULL;
+ }
+
+ // replaces strdup with ansi version
+ char * mystrdup(const char * s)
+ {
+ char * d = NULL;
+ if (s) {
+ size_t sl = strlen(s)+1;
+ d = (char *) malloc(sl);
+ if (d) {
+ memcpy(d,s,sl);
+ } else {
+ HUNSPELL_WARNING(stderr, "Can't allocate memory.\n");
+ }
+ }
+ return d;
+ }
+
+ // strcat for limited length destination string
+ char * mystrcat(char * dest, const char * st, int max) {
+ int len;
+ int len2;
+ if (dest == NULL || st == NULL) return dest;
+ len = strlen(dest);
+ len2 = strlen(st);
+ if (len + len2 + 1 > max) return dest;
+ strcpy(dest + len, st);
+ return dest;
+ }
+
+ // remove cross-platform text line end characters
+ void mychomp(char * s)
+ {
+ size_t k = strlen(s);
+ if ((k > 0) && ((*(s+k-1)=='\r') || (*(s+k-1)=='\n'))) *(s+k-1) = '\0';
+ if ((k > 1) && (*(s+k-2) == '\r')) *(s+k-2) = '\0';
+ }
+
+
+ // does an ansi strdup of the reverse of a string
+ char * myrevstrdup(const char * s)
+ {
+ char * d = NULL;
+ if (s) {
+ size_t sl = strlen(s);
+ d = (char *) malloc(sl+1);
+ if (d) {
+ const char * p = s + sl - 1;
+ char * q = d;
+ while (p >= s) *q++ = *p--;
+ *q = '\0';
+ } else {
+ HUNSPELL_WARNING(stderr, "Can't allocate memory.\n");
+ }
+ }
+ return d;
+ }
+
+// break text to lines
+// return number of lines
+int line_tok(const char * text, char *** lines, char breakchar) {
+ int linenum = 0;
+ if (!text) {
+ return linenum;
+ }
+ char * dup = mystrdup(text);
+ char * p = strchr(dup, breakchar);
+ while (p) {
+ linenum++;
+ *p = '\0';
+ p++;
+ p = strchr(p, breakchar);
+ }
+ linenum++;
+ *lines = (char **) malloc(linenum * sizeof(char *));
+ if (!(*lines)) {
+ free(dup);
+ return 0;
+ }
+
+ p = dup;
+ int l = 0;
+ for (int i = 0; i < linenum; i++) {
+ if (*p != '\0') {
+ (*lines)[l] = mystrdup(p);
+ if (!(*lines)[l]) {
+ for (i = 0; i < l; i++) free((*lines)[i]);
+ free(dup);
+ return 0;
+ }
+ l++;
+ }
+ p += strlen(p) + 1;
+ }
+ free(dup);
+ if (!l) free(*lines);
+ return l;
+}
+
+// uniq line in place
+char * line_uniq(char * text, char breakchar) {
+ char ** lines;
+ int linenum = line_tok(text, &lines, breakchar);
+ int i;
+ strcpy(text, lines[0]);
+ for ( i = 1; i < linenum; i++ ) {
+ int dup = 0;
+ for (int j = 0; j < i; j++) {
+ if (strcmp(lines[i], lines[j]) == 0) dup = 1;
+ }
+ if (!dup) {
+ if ((i > 1) || (*(lines[0]) != '\0')) {
+ sprintf(text + strlen(text), "%c", breakchar);
+ }
+ strcat(text, lines[i]);
+ }
+ }
+ for ( i = 0; i < linenum; i++ ) {
+ if (lines[i]) free(lines[i]);
+ }
+ if (lines) free(lines);
+ return text;
+}
+
+// uniq and boundary for compound analysis: "1\n\2\n\1" -> " ( \1 | \2 ) "
+char * line_uniq_app(char ** text, char breakchar) {
+ if (!strchr(*text, breakchar)) {
+ return *text;
+ }
+
+ char ** lines;
+ int i;
+ int linenum = line_tok(*text, &lines, breakchar);
+ int dup = 0;
+ for (i = 0; i < linenum; i++) {
+ for (int j = 0; j < (i - 1); j++) {
+ if (strcmp(lines[i], lines[j]) == 0) {
+ *(lines[i]) = '\0';
+ dup++;
+ break;
+ }
+ }
+ }
+ if ((linenum - dup) == 1) {
+ strcpy(*text, lines[0]);
+ freelist(&lines, linenum);
+ return *text;
+ }
+ char * newtext = (char *) malloc(strlen(*text) + 2 * linenum + 3 + 1);
+ if (newtext) {
+ free(*text);
+ *text = newtext;
+ } else {
+ freelist(&lines, linenum);
+ return *text;
+ }
+ strcpy(*text," ( ");
+ for (i = 0; i < linenum; i++) if (*(lines[i])) {
+ sprintf(*text + strlen(*text), "%s%s", lines[i], " | ");
+ }
+ (*text)[strlen(*text) - 2] = ')'; // " ) "
+ freelist(&lines, linenum);
+ return *text;
+}
+
+ // append s to ends of every lines in text
+ void strlinecat(char * dest, const char * s)
+ {
+ char * dup = mystrdup(dest);
+ char * source = dup;
+ int len = strlen(s);
+ if (dup) {
+ while (*source) {
+ if (*source == '\n') {
+ strncpy(dest, s, len);
+ dest += len;
+ }
+ *dest = *source;
+ source++; dest++;
+ }
+ strcpy(dest, s);
+ free(dup);
+ }
+ }
+
+// change \n to char c
+char * tr(char * text, char oldc, char newc) {
+ char * p;
+ for (p = text; *p; p++) if (*p == oldc) *p = newc;
+ return text;
+}
+
+// morphcmp(): compare MORPH_DERI_SFX, MORPH_INFL_SFX and MORPH_TERM_SFX fields
+// in the first line of the inputs
+// return 0, if inputs equal
+// return 1, if inputs may equal with a secondary suffix
+// otherwise return -1
+int morphcmp(const char * s, const char * t)
+{
+ int se = 0;
+ int te = 0;
+ const char * sl;
+ const char * tl;
+ const char * olds;
+ const char * oldt;
+ if (!s || !t) return 1;
+ olds = s;
+ sl = strchr(s, '\n');
+ s = strstr(s, MORPH_DERI_SFX);
+ if (!s || (sl && sl < s)) s = strstr(olds, MORPH_INFL_SFX);
+ if (!s || (sl && sl < s)) {
+ s= strstr(olds, MORPH_TERM_SFX);
+ olds = NULL;
+ }
+ oldt = t;
+ tl = strchr(t, '\n');
+ t = strstr(t, MORPH_DERI_SFX);
+ if (!t || (tl && tl < t)) t = strstr(oldt, MORPH_INFL_SFX);
+ if (!t || (tl && tl < t)) {
+ t = strstr(oldt, MORPH_TERM_SFX);
+ oldt = NULL;
+ }
+ while (s && t && (!sl || sl > s) && (!tl || tl > t)) {
+ s += MORPH_TAG_LEN;
+ t += MORPH_TAG_LEN;
+ se = 0;
+ te = 0;
+ while ((*s == *t) && !se && !te) {
+ s++;
+ t++;
+ switch(*s) {
+ case ' ':
+ case '\n':
+ case '\t':
+ case '\0': se = 1;
+ }
+ switch(*t) {
+ case ' ':
+ case '\n':
+ case '\t':
+ case '\0': te = 1;
+ }
+ }
+ if (!se || !te) {
+ // not terminal suffix difference
+ if (olds) return -1;
+ return 1;
+ }
+ olds = s;
+ s = strstr(s, MORPH_DERI_SFX);
+ if (!s || (sl && sl < s)) s = strstr(olds, MORPH_INFL_SFX);
+ if (!s || (sl && sl < s)) {
+ s = strstr(olds, MORPH_TERM_SFX);
+ olds = NULL;
+ }
+ oldt = t;
+ t = strstr(t, MORPH_DERI_SFX);
+ if (!t || (tl && tl < t)) t = strstr(oldt, MORPH_INFL_SFX);
+ if (!t || (tl && tl < t)) {
+ t = strstr(oldt, MORPH_TERM_SFX);
+ oldt = NULL;
+ }
+ }
+ if (!s && !t && se && te) return 0;
+ return 1;
+}
+
+int get_sfxcount(const char * morph)
+{
+ if (!morph || !*morph) return 0;
+ int n = 0;
+ const char * old = morph;
+ morph = strstr(morph, MORPH_DERI_SFX);
+ if (!morph) morph = strstr(old, MORPH_INFL_SFX);
+ if (!morph) morph = strstr(old, MORPH_TERM_SFX);
+ while (morph) {
+ n++;
+ old = morph;
+ morph = strstr(morph + 1, MORPH_DERI_SFX);
+ if (!morph) morph = strstr(old + 1, MORPH_INFL_SFX);
+ if (!morph) morph = strstr(old + 1, MORPH_TERM_SFX);
+ }
+ return n;
+}
+
+
+int fieldlen(const char * r)
+{
+ int n = 0;
+ while (r && *r != ' ' && *r != '\t' && *r != '\0' && *r != '\n') {
+ r++;
+ n++;
+ }
+ return n;
+}
+
+char * copy_field(char * dest, const char * morph, const char * var)
+{
+ if (!morph) return NULL;
+ const char * beg = strstr(morph, var);
+ if (beg) {
+ char * d = dest;
+ for (beg += MORPH_TAG_LEN; *beg != ' ' && *beg != '\t' &&
+ *beg != '\n' && *beg != '\0'; d++, beg++) {
+ *d = *beg;
+ }
+ *d = '\0';
+ return dest;
+ }
+ return NULL;
+}
+
+char * mystrrep(char * word, const char * pat, const char * rep) {
+ char * pos = strstr(word, pat);
+ if (pos) {
+ int replen = strlen(rep);
+ int patlen = strlen(pat);
+ while (pos) {
+ if (replen < patlen) {
+ char * end = word + strlen(word);
+ char * next = pos + replen;
+ char * prev = pos + strlen(pat);
+ for (; prev < end; *next = *prev, prev++, next++);
+ *next = '\0';
+ } else if (replen > patlen) {
+ char * end = pos + patlen;
+ char * next = word + strlen(word) + replen - patlen;
+ char * prev = next - replen + patlen;
+ for (; prev >= end; *next = *prev, prev--, next--);
+ }
+ strncpy(pos, rep, replen);
+ pos = strstr(word, pat);
+ }
+ }
+ return word;
+}
+
+ // reverse word
+ int reverseword(char * word) {
+ char r;
+ for (char * dest = word + strlen(word) - 1; word < dest; word++, dest--) {
+ r=*word;
+ *word = *dest;
+ *dest = r;
+ }
+ return 0;
+ }
+
+ // reverse word (error: 1)
+ int reverseword_utf(char * word) {
+ w_char w[MAXWORDLEN];
+ w_char * p;
+ w_char r;
+ int l = u8_u16(w, MAXWORDLEN, word);
+ if (l == -1) return 1;
+ p = w;
+ for (w_char * dest = w + l - 1; p < dest; p++, dest--) {
+ r=*p;
+ *p = *dest;
+ *dest = r;
+ }
+ u16_u8(word, MAXWORDUTF8LEN, w, l);
+ return 0;
+ }
+
+ int uniqlist(char ** list, int n) {
+ int i;
+ if (n < 2) return n;
+ for (i = 0; i < n; i++) {
+ for (int j = 0; j < i; j++) {
+ if (list[j] && list[i] && (strcmp(list[j], list[i]) == 0)) {
+ free(list[i]);
+ list[i] = NULL;
+ break;
+ }
+ }
+ }
+ int m = 1;
+ for (i = 1; i < n; i++) if (list[i]) {
+ list[m] = list[i];
+ m++;
+ }
+ return m;
+ }
+
+ void freelist(char *** list, int n) {
+ if (list && *list && n > 0) {
+ for (int i = 0; i < n; i++) if ((*list)[i]) free((*list)[i]);
+ free(*list);
+ *list = NULL;
+ }
+ }
+
+ // convert null terminated string to all caps
+ void mkallcap(char * p, const struct cs_info * csconv)
+ {
+ while (*p != '\0') {
+ *p = csconv[((unsigned char) *p)].cupper;
+ p++;
+ }
+ }
+
+ // convert null terminated string to all little
+ void mkallsmall(char * p, const struct cs_info * csconv)
+ {
+ while (*p != '\0') {
+ *p = csconv[((unsigned char) *p)].clower;
+ p++;
+ }
+ }
+
+void mkallsmall_utf(w_char * u, int nc, int langnum) {
+ for (int i = 0; i < nc; i++) {
+ unsigned short idx = (u[i].h << 8) + u[i].l;
+ if (idx != unicodetolower(idx, langnum)) {
+ u[i].h = (unsigned char) (unicodetolower(idx, langnum) >> 8);
+ u[i].l = (unsigned char) (unicodetolower(idx, langnum) & 0x00FF);
+ }
+ }
+}
+
+void mkallcap_utf(w_char * u, int nc, int langnum) {
+ for (int i = 0; i < nc; i++) {
+ unsigned short idx = (u[i].h << 8) + u[i].l;
+ if (idx != unicodetoupper(idx, langnum)) {
+ u[i].h = (unsigned char) (unicodetoupper(idx, langnum) >> 8);
+ u[i].l = (unsigned char) (unicodetoupper(idx, langnum) & 0x00FF);
+ }
+ }
+}
+
+ // convert null terminated string to have initial capital
+ void mkinitcap(char * p, const struct cs_info * csconv)
+ {
+ if (*p != '\0') *p = csconv[((unsigned char)*p)].cupper;
+ }
+
+ // conversion function for protected memory
+ void store_pointer(char * dest, char * source)
+ {
+ memcpy(dest, &source, sizeof(char *));
+ }
+
+ // conversion function for protected memory
+ char * get_stored_pointer(const char * s)
+ {
+ char * p;
+ memcpy(&p, s, sizeof(char *));
+ return p;
+ }
+
+#ifndef MOZILLA_CLIENT
+ // convert null terminated string to all caps using encoding
+ void enmkallcap(char * d, const char * p, const char * encoding)
+
+ {
+ struct cs_info * csconv = get_current_cs(encoding);
+ while (*p != '\0') {
+ *d++ = csconv[((unsigned char) *p)].cupper;
+ p++;
+ }
+ *d = '\0';
+ }
+
+ // convert null terminated string to all little using encoding
+ void enmkallsmall(char * d, const char * p, const char * encoding)
+ {
+ struct cs_info * csconv = get_current_cs(encoding);
+ while (*p != '\0') {
+ *d++ = csconv[((unsigned char) *p)].clower;
+ p++;
+ }
+ *d = '\0';
+ }
+
+ // convert null terminated string to have initial capital using encoding
+ void enmkinitcap(char * d, const char * p, const char * encoding)
+ {
+ struct cs_info * csconv = get_current_cs(encoding);
+ memcpy(d,p,(strlen(p)+1));
+ if (*p != '\0') *d= csconv[((unsigned char)*p)].cupper;
+ }
+
+// these are simple character mappings for the
+// encodings supported
+// supplying isupper, tolower, and toupper
+
+static struct cs_info iso1_tbl[] = {
+{ 0x00, 0x00, 0x00 },
+{ 0x00, 0x01, 0x01 },
+{ 0x00, 0x02, 0x02 },
+{ 0x00, 0x03, 0x03 },
+{ 0x00, 0x04, 0x04 },
+{ 0x00, 0x05, 0x05 },
+{ 0x00, 0x06, 0x06 },
+{ 0x00, 0x07, 0x07 },
+{ 0x00, 0x08, 0x08 },
+{ 0x00, 0x09, 0x09 },
+{ 0x00, 0x0a, 0x0a },
+{ 0x00, 0x0b, 0x0b },
+{ 0x00, 0x0c, 0x0c },
+{ 0x00, 0x0d, 0x0d },
+{ 0x00, 0x0e, 0x0e },
+{ 0x00, 0x0f, 0x0f },
+{ 0x00, 0x10, 0x10 },
+{ 0x00, 0x11, 0x11 },
+{ 0x00, 0x12, 0x12 },
+{ 0x00, 0x13, 0x13 },
+{ 0x00, 0x14, 0x14 },
+{ 0x00, 0x15, 0x15 },
+{ 0x00, 0x16, 0x16 },
+{ 0x00, 0x17, 0x17 },
+{ 0x00, 0x18, 0x18 },
+{ 0x00, 0x19, 0x19 },
+{ 0x00, 0x1a, 0x1a },
+{ 0x00, 0x1b, 0x1b },
+{ 0x00, 0x1c, 0x1c },
+{ 0x00, 0x1d, 0x1d },
+{ 0x00, 0x1e, 0x1e },
+{ 0x00, 0x1f, 0x1f },
+{ 0x00, 0x20, 0x20 },
+{ 0x00, 0x21, 0x21 },
+{ 0x00, 0x22, 0x22 },
+{ 0x00, 0x23, 0x23 },
+{ 0x00, 0x24, 0x24 },
+{ 0x00, 0x25, 0x25 },
+{ 0x00, 0x26, 0x26 },
+{ 0x00, 0x27, 0x27 },
+{ 0x00, 0x28, 0x28 },
+{ 0x00, 0x29, 0x29 },
+{ 0x00, 0x2a, 0x2a },
+{ 0x00, 0x2b, 0x2b },
+{ 0x00, 0x2c, 0x2c },
+{ 0x00, 0x2d, 0x2d },
+{ 0x00, 0x2e, 0x2e },
+{ 0x00, 0x2f, 0x2f },
+{ 0x00, 0x30, 0x30 },
+{ 0x00, 0x31, 0x31 },
+{ 0x00, 0x32, 0x32 },
+{ 0x00, 0x33, 0x33 },
+{ 0x00, 0x34, 0x34 },
+{ 0x00, 0x35, 0x35 },
+{ 0x00, 0x36, 0x36 },
+{ 0x00, 0x37, 0x37 },
+{ 0x00, 0x38, 0x38 },
+{ 0x00, 0x39, 0x39 },
+{ 0x00, 0x3a, 0x3a },
+{ 0x00, 0x3b, 0x3b },
+{ 0x00, 0x3c, 0x3c },
+{ 0x00, 0x3d, 0x3d },
+{ 0x00, 0x3e, 0x3e },
+{ 0x00, 0x3f, 0x3f },
+{ 0x00, 0x40, 0x40 },
+{ 0x01, 0x61, 0x41 },
+{ 0x01, 0x62, 0x42 },
+{ 0x01, 0x63, 0x43 },
+{ 0x01, 0x64, 0x44 },
+{ 0x01, 0x65, 0x45 },
+{ 0x01, 0x66, 0x46 },
+{ 0x01, 0x67, 0x47 },
+{ 0x01, 0x68, 0x48 },
+{ 0x01, 0x69, 0x49 },
+{ 0x01, 0x6a, 0x4a },
+{ 0x01, 0x6b, 0x4b },
+{ 0x01, 0x6c, 0x4c },
+{ 0x01, 0x6d, 0x4d },
+{ 0x01, 0x6e, 0x4e },
+{ 0x01, 0x6f, 0x4f },
+{ 0x01, 0x70, 0x50 },
+{ 0x01, 0x71, 0x51 },
+{ 0x01, 0x72, 0x52 },
+{ 0x01, 0x73, 0x53 },
+{ 0x01, 0x74, 0x54 },
+{ 0x01, 0x75, 0x55 },
+{ 0x01, 0x76, 0x56 },
+{ 0x01, 0x77, 0x57 },
+{ 0x01, 0x78, 0x58 },
+{ 0x01, 0x79, 0x59 },
+{ 0x01, 0x7a, 0x5a },
+{ 0x00, 0x5b, 0x5b },
+{ 0x00, 0x5c, 0x5c },
+{ 0x00, 0x5d, 0x5d },
+{ 0x00, 0x5e, 0x5e },
+{ 0x00, 0x5f, 0x5f },
+{ 0x00, 0x60, 0x60 },
+{ 0x00, 0x61, 0x41 },
+{ 0x00, 0x62, 0x42 },
+{ 0x00, 0x63, 0x43 },
+{ 0x00, 0x64, 0x44 },
+{ 0x00, 0x65, 0x45 },
+{ 0x00, 0x66, 0x46 },
+{ 0x00, 0x67, 0x47 },
+{ 0x00, 0x68, 0x48 },
+{ 0x00, 0x69, 0x49 },
+{ 0x00, 0x6a, 0x4a },
+{ 0x00, 0x6b, 0x4b },
+{ 0x00, 0x6c, 0x4c },
+{ 0x00, 0x6d, 0x4d },
+{ 0x00, 0x6e, 0x4e },
+{ 0x00, 0x6f, 0x4f },
+{ 0x00, 0x70, 0x50 },
+{ 0x00, 0x71, 0x51 },
+{ 0x00, 0x72, 0x52 },
+{ 0x00, 0x73, 0x53 },
+{ 0x00, 0x74, 0x54 },
+{ 0x00, 0x75, 0x55 },
+{ 0x00, 0x76, 0x56 },
+{ 0x00, 0x77, 0x57 },
+{ 0x00, 0x78, 0x58 },
+{ 0x00, 0x79, 0x59 },
+{ 0x00, 0x7a, 0x5a },
+{ 0x00, 0x7b, 0x7b },
+{ 0x00, 0x7c, 0x7c },
+{ 0x00, 0x7d, 0x7d },
+{ 0x00, 0x7e, 0x7e },
+{ 0x00, 0x7f, 0x7f },
+{ 0x00, 0x80, 0x80 },
+{ 0x00, 0x81, 0x81 },
+{ 0x00, 0x82, 0x82 },
+{ 0x00, 0x83, 0x83 },
+{ 0x00, 0x84, 0x84 },
+{ 0x00, 0x85, 0x85 },
+{ 0x00, 0x86, 0x86 },
+{ 0x00, 0x87, 0x87 },
+{ 0x00, 0x88, 0x88 },
+{ 0x00, 0x89, 0x89 },
+{ 0x00, 0x8a, 0x8a },
+{ 0x00, 0x8b, 0x8b },
+{ 0x00, 0x8c, 0x8c },
+{ 0x00, 0x8d, 0x8d },
+{ 0x00, 0x8e, 0x8e },
+{ 0x00, 0x8f, 0x8f },
+{ 0x00, 0x90, 0x90 },
+{ 0x00, 0x91, 0x91 },
+{ 0x00, 0x92, 0x92 },
+{ 0x00, 0x93, 0x93 },
+{ 0x00, 0x94, 0x94 },
+{ 0x00, 0x95, 0x95 },
+{ 0x00, 0x96, 0x96 },
+{ 0x00, 0x97, 0x97 },
+{ 0x00, 0x98, 0x98 },
+{ 0x00, 0x99, 0x99 },
+{ 0x00, 0x9a, 0x9a },
+{ 0x00, 0x9b, 0x9b },
+{ 0x00, 0x9c, 0x9c },
+{ 0x00, 0x9d, 0x9d },
+{ 0x00, 0x9e, 0x9e },
+{ 0x00, 0x9f, 0x9f },
+{ 0x00, 0xa0, 0xa0 },
+{ 0x00, 0xa1, 0xa1 },
+{ 0x00, 0xa2, 0xa2 },
+{ 0x00, 0xa3, 0xa3 },
+{ 0x00, 0xa4, 0xa4 },
+{ 0x00, 0xa5, 0xa5 },
+{ 0x00, 0xa6, 0xa6 },
+{ 0x00, 0xa7, 0xa7 },
+{ 0x00, 0xa8, 0xa8 },
+{ 0x00, 0xa9, 0xa9 },
+{ 0x00, 0xaa, 0xaa },
+{ 0x00, 0xab, 0xab },
+{ 0x00, 0xac, 0xac },
+{ 0x00, 0xad, 0xad },
+{ 0x00, 0xae, 0xae },
+{ 0x00, 0xaf, 0xaf },
+{ 0x00, 0xb0, 0xb0 },
+{ 0x00, 0xb1, 0xb1 },
+{ 0x00, 0xb2, 0xb2 },
+{ 0x00, 0xb3, 0xb3 },
+{ 0x00, 0xb4, 0xb4 },
+{ 0x00, 0xb5, 0xb5 },
+{ 0x00, 0xb6, 0xb6 },
+{ 0x00, 0xb7, 0xb7 },
+{ 0x00, 0xb8, 0xb8 },
+{ 0x00, 0xb9, 0xb9 },
+{ 0x00, 0xba, 0xba },
+{ 0x00, 0xbb, 0xbb },
+{ 0x00, 0xbc, 0xbc },
+{ 0x00, 0xbd, 0xbd },
+{ 0x00, 0xbe, 0xbe },
+{ 0x00, 0xbf, 0xbf },
+{ 0x01, 0xe0, 0xc0 },
+{ 0x01, 0xe1, 0xc1 },
+{ 0x01, 0xe2, 0xc2 },
+{ 0x01, 0xe3, 0xc3 },
+{ 0x01, 0xe4, 0xc4 },
+{ 0x01, 0xe5, 0xc5 },
+{ 0x01, 0xe6, 0xc6 },
+{ 0x01, 0xe7, 0xc7 },
+{ 0x01, 0xe8, 0xc8 },
+{ 0x01, 0xe9, 0xc9 },
+{ 0x01, 0xea, 0xca },
+{ 0x01, 0xeb, 0xcb },
+{ 0x01, 0xec, 0xcc },
+{ 0x01, 0xed, 0xcd },
+{ 0x01, 0xee, 0xce },
+{ 0x01, 0xef, 0xcf },
+{ 0x01, 0xf0, 0xd0 },
+{ 0x01, 0xf1, 0xd1 },
+{ 0x01, 0xf2, 0xd2 },
+{ 0x01, 0xf3, 0xd3 },
+{ 0x01, 0xf4, 0xd4 },
+{ 0x01, 0xf5, 0xd5 },
+{ 0x01, 0xf6, 0xd6 },
+{ 0x00, 0xd7, 0xd7 },
+{ 0x01, 0xf8, 0xd8 },
+{ 0x01, 0xf9, 0xd9 },
+{ 0x01, 0xfa, 0xda },
+{ 0x01, 0xfb, 0xdb },
+{ 0x01, 0xfc, 0xdc },
+{ 0x01, 0xfd, 0xdd },
+{ 0x01, 0xfe, 0xde },
+{ 0x00, 0xdf, 0xdf },
+{ 0x00, 0xe0, 0xc0 },
+{ 0x00, 0xe1, 0xc1 },
+{ 0x00, 0xe2, 0xc2 },
+{ 0x00, 0xe3, 0xc3 },
+{ 0x00, 0xe4, 0xc4 },
+{ 0x00, 0xe5, 0xc5 },
+{ 0x00, 0xe6, 0xc6 },
+{ 0x00, 0xe7, 0xc7 },
+{ 0x00, 0xe8, 0xc8 },
+{ 0x00, 0xe9, 0xc9 },
+{ 0x00, 0xea, 0xca },
+{ 0x00, 0xeb, 0xcb },
+{ 0x00, 0xec, 0xcc },
+{ 0x00, 0xed, 0xcd },
+{ 0x00, 0xee, 0xce },
+{ 0x00, 0xef, 0xcf },
+{ 0x00, 0xf0, 0xd0 },
+{ 0x00, 0xf1, 0xd1 },
+{ 0x00, 0xf2, 0xd2 },
+{ 0x00, 0xf3, 0xd3 },
+{ 0x00, 0xf4, 0xd4 },
+{ 0x00, 0xf5, 0xd5 },
+{ 0x00, 0xf6, 0xd6 },
+{ 0x00, 0xf7, 0xf7 },
+{ 0x00, 0xf8, 0xd8 },
+{ 0x00, 0xf9, 0xd9 },
+{ 0x00, 0xfa, 0xda },
+{ 0x00, 0xfb, 0xdb },
+{ 0x00, 0xfc, 0xdc },
+{ 0x00, 0xfd, 0xdd },
+{ 0x00, 0xfe, 0xde },
+{ 0x00, 0xff, 0xff }
+};
+
+
+static struct cs_info iso2_tbl[] = {
+{ 0x00, 0x00, 0x00 },
+{ 0x00, 0x01, 0x01 },
+{ 0x00, 0x02, 0x02 },
+{ 0x00, 0x03, 0x03 },
+{ 0x00, 0x04, 0x04 },
+{ 0x00, 0x05, 0x05 },
+{ 0x00, 0x06, 0x06 },
+{ 0x00, 0x07, 0x07 },
+{ 0x00, 0x08, 0x08 },
+{ 0x00, 0x09, 0x09 },
+{ 0x00, 0x0a, 0x0a },
+{ 0x00, 0x0b, 0x0b },
+{ 0x00, 0x0c, 0x0c },
+{ 0x00, 0x0d, 0x0d },
+{ 0x00, 0x0e, 0x0e },
+{ 0x00, 0x0f, 0x0f },
+{ 0x00, 0x10, 0x10 },
+{ 0x00, 0x11, 0x11 },
+{ 0x00, 0x12, 0x12 },
+{ 0x00, 0x13, 0x13 },
+{ 0x00, 0x14, 0x14 },
+{ 0x00, 0x15, 0x15 },
+{ 0x00, 0x16, 0x16 },
+{ 0x00, 0x17, 0x17 },
+{ 0x00, 0x18, 0x18 },
+{ 0x00, 0x19, 0x19 },
+{ 0x00, 0x1a, 0x1a },
+{ 0x00, 0x1b, 0x1b },
+{ 0x00, 0x1c, 0x1c },
+{ 0x00, 0x1d, 0x1d },
+{ 0x00, 0x1e, 0x1e },
+{ 0x00, 0x1f, 0x1f },
+{ 0x00, 0x20, 0x20 },
+{ 0x00, 0x21, 0x21 },
+{ 0x00, 0x22, 0x22 },
+{ 0x00, 0x23, 0x23 },
+{ 0x00, 0x24, 0x24 },
+{ 0x00, 0x25, 0x25 },
+{ 0x00, 0x26, 0x26 },
+{ 0x00, 0x27, 0x27 },
+{ 0x00, 0x28, 0x28 },
+{ 0x00, 0x29, 0x29 },
+{ 0x00, 0x2a, 0x2a },
+{ 0x00, 0x2b, 0x2b },
+{ 0x00, 0x2c, 0x2c },
+{ 0x00, 0x2d, 0x2d },
+{ 0x00, 0x2e, 0x2e },
+{ 0x00, 0x2f, 0x2f },
+{ 0x00, 0x30, 0x30 },
+{ 0x00, 0x31, 0x31 },
+{ 0x00, 0x32, 0x32 },
+{ 0x00, 0x33, 0x33 },
+{ 0x00, 0x34, 0x34 },
+{ 0x00, 0x35, 0x35 },
+{ 0x00, 0x36, 0x36 },
+{ 0x00, 0x37, 0x37 },
+{ 0x00, 0x38, 0x38 },
+{ 0x00, 0x39, 0x39 },
+{ 0x00, 0x3a, 0x3a },
+{ 0x00, 0x3b, 0x3b },
+{ 0x00, 0x3c, 0x3c },
+{ 0x00, 0x3d, 0x3d },
+{ 0x00, 0x3e, 0x3e },
+{ 0x00, 0x3f, 0x3f },
+{ 0x00, 0x40, 0x40 },
+{ 0x01, 0x61, 0x41 },
+{ 0x01, 0x62, 0x42 },
+{ 0x01, 0x63, 0x43 },
+{ 0x01, 0x64, 0x44 },
+{ 0x01, 0x65, 0x45 },
+{ 0x01, 0x66, 0x46 },
+{ 0x01, 0x67, 0x47 },
+{ 0x01, 0x68, 0x48 },
+{ 0x01, 0x69, 0x49 },
+{ 0x01, 0x6a, 0x4a },
+{ 0x01, 0x6b, 0x4b },
+{ 0x01, 0x6c, 0x4c },
+{ 0x01, 0x6d, 0x4d },
+{ 0x01, 0x6e, 0x4e },
+{ 0x01, 0x6f, 0x4f },
+{ 0x01, 0x70, 0x50 },
+{ 0x01, 0x71, 0x51 },
+{ 0x01, 0x72, 0x52 },
+{ 0x01, 0x73, 0x53 },
+{ 0x01, 0x74, 0x54 },
+{ 0x01, 0x75, 0x55 },
+{ 0x01, 0x76, 0x56 },
+{ 0x01, 0x77, 0x57 },
+{ 0x01, 0x78, 0x58 },
+{ 0x01, 0x79, 0x59 },
+{ 0x01, 0x7a, 0x5a },
+{ 0x00, 0x5b, 0x5b },
+{ 0x00, 0x5c, 0x5c },
+{ 0x00, 0x5d, 0x5d },
+{ 0x00, 0x5e, 0x5e },
+{ 0x00, 0x5f, 0x5f },
+{ 0x00, 0x60, 0x60 },
+{ 0x00, 0x61, 0x41 },
+{ 0x00, 0x62, 0x42 },
+{ 0x00, 0x63, 0x43 },
+{ 0x00, 0x64, 0x44 },
+{ 0x00, 0x65, 0x45 },
+{ 0x00, 0x66, 0x46 },
+{ 0x00, 0x67, 0x47 },
+{ 0x00, 0x68, 0x48 },
+{ 0x00, 0x69, 0x49 },
+{ 0x00, 0x6a, 0x4a },
+{ 0x00, 0x6b, 0x4b },
+{ 0x00, 0x6c, 0x4c },
+{ 0x00, 0x6d, 0x4d },
+{ 0x00, 0x6e, 0x4e },
+{ 0x00, 0x6f, 0x4f },
+{ 0x00, 0x70, 0x50 },
+{ 0x00, 0x71, 0x51 },
+{ 0x00, 0x72, 0x52 },
+{ 0x00, 0x73, 0x53 },
+{ 0x00, 0x74, 0x54 },
+{ 0x00, 0x75, 0x55 },
+{ 0x00, 0x76, 0x56 },
+{ 0x00, 0x77, 0x57 },
+{ 0x00, 0x78, 0x58 },
+{ 0x00, 0x79, 0x59 },
+{ 0x00, 0x7a, 0x5a },
+{ 0x00, 0x7b, 0x7b },
+{ 0x00, 0x7c, 0x7c },
+{ 0x00, 0x7d, 0x7d },
+{ 0x00, 0x7e, 0x7e },
+{ 0x00, 0x7f, 0x7f },
+{ 0x00, 0x80, 0x80 },
+{ 0x00, 0x81, 0x81 },
+{ 0x00, 0x82, 0x82 },
+{ 0x00, 0x83, 0x83 },
+{ 0x00, 0x84, 0x84 },
+{ 0x00, 0x85, 0x85 },
+{ 0x00, 0x86, 0x86 },
+{ 0x00, 0x87, 0x87 },
+{ 0x00, 0x88, 0x88 },
+{ 0x00, 0x89, 0x89 },
+{ 0x00, 0x8a, 0x8a },
+{ 0x00, 0x8b, 0x8b },
+{ 0x00, 0x8c, 0x8c },
+{ 0x00, 0x8d, 0x8d },
+{ 0x00, 0x8e, 0x8e },
+{ 0x00, 0x8f, 0x8f },
+{ 0x00, 0x90, 0x90 },
+{ 0x00, 0x91, 0x91 },
+{ 0x00, 0x92, 0x92 },
+{ 0x00, 0x93, 0x93 },
+{ 0x00, 0x94, 0x94 },
+{ 0x00, 0x95, 0x95 },
+{ 0x00, 0x96, 0x96 },
+{ 0x00, 0x97, 0x97 },
+{ 0x00, 0x98, 0x98 },
+{ 0x00, 0x99, 0x99 },
+{ 0x00, 0x9a, 0x9a },
+{ 0x00, 0x9b, 0x9b },
+{ 0x00, 0x9c, 0x9c },
+{ 0x00, 0x9d, 0x9d },
+{ 0x00, 0x9e, 0x9e },
+{ 0x00, 0x9f, 0x9f },
+{ 0x00, 0xa0, 0xa0 },
+{ 0x01, 0xb1, 0xa1 },
+{ 0x00, 0xa2, 0xa2 },
+{ 0x01, 0xb3, 0xa3 },
+{ 0x00, 0xa4, 0xa4 },
+{ 0x01, 0xb5, 0xa5 },
+{ 0x01, 0xb6, 0xa6 },
+{ 0x00, 0xa7, 0xa7 },
+{ 0x00, 0xa8, 0xa8 },
+{ 0x01, 0xb9, 0xa9 },
+{ 0x01, 0xba, 0xaa },
+{ 0x01, 0xbb, 0xab },
+{ 0x01, 0xbc, 0xac },
+{ 0x00, 0xad, 0xad },
+{ 0x01, 0xbe, 0xae },
+{ 0x01, 0xbf, 0xaf },
+{ 0x00, 0xb0, 0xb0 },
+{ 0x00, 0xb1, 0xa1 },
+{ 0x00, 0xb2, 0xb2 },
+{ 0x00, 0xb3, 0xa3 },
+{ 0x00, 0xb4, 0xb4 },
+{ 0x00, 0xb5, 0xa5 },
+{ 0x00, 0xb6, 0xa6 },
+{ 0x00, 0xb7, 0xb7 },
+{ 0x00, 0xb8, 0xb8 },
+{ 0x00, 0xb9, 0xa9 },
+{ 0x00, 0xba, 0xaa },
+{ 0x00, 0xbb, 0xab },
+{ 0x00, 0xbc, 0xac },
+{ 0x00, 0xbd, 0xbd },
+{ 0x00, 0xbe, 0xae },
+{ 0x00, 0xbf, 0xaf },
+{ 0x01, 0xe0, 0xc0 },
+{ 0x01, 0xe1, 0xc1 },
+{ 0x01, 0xe2, 0xc2 },
+{ 0x01, 0xe3, 0xc3 },
+{ 0x01, 0xe4, 0xc4 },
+{ 0x01, 0xe5, 0xc5 },
+{ 0x01, 0xe6, 0xc6 },
+{ 0x01, 0xe7, 0xc7 },
+{ 0x01, 0xe8, 0xc8 },
+{ 0x01, 0xe9, 0xc9 },
+{ 0x01, 0xea, 0xca },
+{ 0x01, 0xeb, 0xcb },
+{ 0x01, 0xec, 0xcc },
+{ 0x01, 0xed, 0xcd },
+{ 0x01, 0xee, 0xce },
+{ 0x01, 0xef, 0xcf },
+{ 0x01, 0xf0, 0xd0 },
+{ 0x01, 0xf1, 0xd1 },
+{ 0x01, 0xf2, 0xd2 },
+{ 0x01, 0xf3, 0xd3 },
+{ 0x01, 0xf4, 0xd4 },
+{ 0x01, 0xf5, 0xd5 },
+{ 0x01, 0xf6, 0xd6 },
+{ 0x00, 0xd7, 0xd7 },
+{ 0x01, 0xf8, 0xd8 },
+{ 0x01, 0xf9, 0xd9 },
+{ 0x01, 0xfa, 0xda },
+{ 0x01, 0xfb, 0xdb },
+{ 0x01, 0xfc, 0xdc },
+{ 0x01, 0xfd, 0xdd },
+{ 0x01, 0xfe, 0xde },
+{ 0x00, 0xdf, 0xdf },
+{ 0x00, 0xe0, 0xc0 },
+{ 0x00, 0xe1, 0xc1 },
+{ 0x00, 0xe2, 0xc2 },
+{ 0x00, 0xe3, 0xc3 },
+{ 0x00, 0xe4, 0xc4 },
+{ 0x00, 0xe5, 0xc5 },
+{ 0x00, 0xe6, 0xc6 },
+{ 0x00, 0xe7, 0xc7 },
+{ 0x00, 0xe8, 0xc8 },
+{ 0x00, 0xe9, 0xc9 },
+{ 0x00, 0xea, 0xca },
+{ 0x00, 0xeb, 0xcb },
+{ 0x00, 0xec, 0xcc },
+{ 0x00, 0xed, 0xcd },
+{ 0x00, 0xee, 0xce },
+{ 0x00, 0xef, 0xcf },
+{ 0x00, 0xf0, 0xd0 },
+{ 0x00, 0xf1, 0xd1 },
+{ 0x00, 0xf2, 0xd2 },
+{ 0x00, 0xf3, 0xd3 },
+{ 0x00, 0xf4, 0xd4 },
+{ 0x00, 0xf5, 0xd5 },
+{ 0x00, 0xf6, 0xd6 },
+{ 0x00, 0xf7, 0xf7 },
+{ 0x00, 0xf8, 0xd8 },
+{ 0x00, 0xf9, 0xd9 },
+{ 0x00, 0xfa, 0xda },
+{ 0x00, 0xfb, 0xdb },
+{ 0x00, 0xfc, 0xdc },
+{ 0x00, 0xfd, 0xdd },
+{ 0x00, 0xfe, 0xde },
+{ 0x00, 0xff, 0xff }
+};
+
+
+static struct cs_info iso3_tbl[] = {
+{ 0x00, 0x00, 0x00 },
+{ 0x00, 0x01, 0x01 },
+{ 0x00, 0x02, 0x02 },
+{ 0x00, 0x03, 0x03 },
+{ 0x00, 0x04, 0x04 },
+{ 0x00, 0x05, 0x05 },
+{ 0x00, 0x06, 0x06 },
+{ 0x00, 0x07, 0x07 },
+{ 0x00, 0x08, 0x08 },
+{ 0x00, 0x09, 0x09 },
+{ 0x00, 0x0a, 0x0a },
+{ 0x00, 0x0b, 0x0b },
+{ 0x00, 0x0c, 0x0c },
+{ 0x00, 0x0d, 0x0d },
+{ 0x00, 0x0e, 0x0e },
+{ 0x00, 0x0f, 0x0f },
+{ 0x00, 0x10, 0x10 },
+{ 0x00, 0x11, 0x11 },
+{ 0x00, 0x12, 0x12 },
+{ 0x00, 0x13, 0x13 },
+{ 0x00, 0x14, 0x14 },
+{ 0x00, 0x15, 0x15 },
+{ 0x00, 0x16, 0x16 },
+{ 0x00, 0x17, 0x17 },
+{ 0x00, 0x18, 0x18 },
+{ 0x00, 0x19, 0x19 },
+{ 0x00, 0x1a, 0x1a },
+{ 0x00, 0x1b, 0x1b },
+{ 0x00, 0x1c, 0x1c },
+{ 0x00, 0x1d, 0x1d },
+{ 0x00, 0x1e, 0x1e },
+{ 0x00, 0x1f, 0x1f },
+{ 0x00, 0x20, 0x20 },
+{ 0x00, 0x21, 0x21 },
+{ 0x00, 0x22, 0x22 },
+{ 0x00, 0x23, 0x23 },
+{ 0x00, 0x24, 0x24 },
+{ 0x00, 0x25, 0x25 },
+{ 0x00, 0x26, 0x26 },
+{ 0x00, 0x27, 0x27 },
+{ 0x00, 0x28, 0x28 },
+{ 0x00, 0x29, 0x29 },
+{ 0x00, 0x2a, 0x2a },
+{ 0x00, 0x2b, 0x2b },
+{ 0x00, 0x2c, 0x2c },
+{ 0x00, 0x2d, 0x2d },
+{ 0x00, 0x2e, 0x2e },
+{ 0x00, 0x2f, 0x2f },
+{ 0x00, 0x30, 0x30 },
+{ 0x00, 0x31, 0x31 },
+{ 0x00, 0x32, 0x32 },
+{ 0x00, 0x33, 0x33 },
+{ 0x00, 0x34, 0x34 },
+{ 0x00, 0x35, 0x35 },
+{ 0x00, 0x36, 0x36 },
+{ 0x00, 0x37, 0x37 },
+{ 0x00, 0x38, 0x38 },
+{ 0x00, 0x39, 0x39 },
+{ 0x00, 0x3a, 0x3a },
+{ 0x00, 0x3b, 0x3b },
+{ 0x00, 0x3c, 0x3c },
+{ 0x00, 0x3d, 0x3d },
+{ 0x00, 0x3e, 0x3e },
+{ 0x00, 0x3f, 0x3f },
+{ 0x00, 0x40, 0x40 },
+{ 0x01, 0x61, 0x41 },
+{ 0x01, 0x62, 0x42 },
+{ 0x01, 0x63, 0x43 },
+{ 0x01, 0x64, 0x44 },
+{ 0x01, 0x65, 0x45 },
+{ 0x01, 0x66, 0x46 },
+{ 0x01, 0x67, 0x47 },
+{ 0x01, 0x68, 0x48 },
+{ 0x01, 0x69, 0x49 },
+{ 0x01, 0x6a, 0x4a },
+{ 0x01, 0x6b, 0x4b },
+{ 0x01, 0x6c, 0x4c },
+{ 0x01, 0x6d, 0x4d },
+{ 0x01, 0x6e, 0x4e },
+{ 0x01, 0x6f, 0x4f },
+{ 0x01, 0x70, 0x50 },
+{ 0x01, 0x71, 0x51 },
+{ 0x01, 0x72, 0x52 },
+{ 0x01, 0x73, 0x53 },
+{ 0x01, 0x74, 0x54 },
+{ 0x01, 0x75, 0x55 },
+{ 0x01, 0x76, 0x56 },
+{ 0x01, 0x77, 0x57 },
+{ 0x01, 0x78, 0x58 },
+{ 0x01, 0x79, 0x59 },
+{ 0x01, 0x7a, 0x5a },
+{ 0x00, 0x5b, 0x5b },
+{ 0x00, 0x5c, 0x5c },
+{ 0x00, 0x5d, 0x5d },
+{ 0x00, 0x5e, 0x5e },
+{ 0x00, 0x5f, 0x5f },
+{ 0x00, 0x60, 0x60 },
+{ 0x00, 0x61, 0x41 },
+{ 0x00, 0x62, 0x42 },
+{ 0x00, 0x63, 0x43 },
+{ 0x00, 0x64, 0x44 },
+{ 0x00, 0x65, 0x45 },
+{ 0x00, 0x66, 0x46 },
+{ 0x00, 0x67, 0x47 },
+{ 0x00, 0x68, 0x48 },
+{ 0x00, 0x69, 0x49 },
+{ 0x00, 0x6a, 0x4a },
+{ 0x00, 0x6b, 0x4b },
+{ 0x00, 0x6c, 0x4c },
+{ 0x00, 0x6d, 0x4d },
+{ 0x00, 0x6e, 0x4e },
+{ 0x00, 0x6f, 0x4f },
+{ 0x00, 0x70, 0x50 },
+{ 0x00, 0x71, 0x51 },
+{ 0x00, 0x72, 0x52 },
+{ 0x00, 0x73, 0x53 },
+{ 0x00, 0x74, 0x54 },
+{ 0x00, 0x75, 0x55 },
+{ 0x00, 0x76, 0x56 },
+{ 0x00, 0x77, 0x57 },
+{ 0x00, 0x78, 0x58 },
+{ 0x00, 0x79, 0x59 },
+{ 0x00, 0x7a, 0x5a },
+{ 0x00, 0x7b, 0x7b },
+{ 0x00, 0x7c, 0x7c },
+{ 0x00, 0x7d, 0x7d },
+{ 0x00, 0x7e, 0x7e },
+{ 0x00, 0x7f, 0x7f },
+{ 0x00, 0x80, 0x80 },
+{ 0x00, 0x81, 0x81 },
+{ 0x00, 0x82, 0x82 },
+{ 0x00, 0x83, 0x83 },
+{ 0x00, 0x84, 0x84 },
+{ 0x00, 0x85, 0x85 },
+{ 0x00, 0x86, 0x86 },
+{ 0x00, 0x87, 0x87 },
+{ 0x00, 0x88, 0x88 },
+{ 0x00, 0x89, 0x89 },
+{ 0x00, 0x8a, 0x8a },
+{ 0x00, 0x8b, 0x8b },
+{ 0x00, 0x8c, 0x8c },
+{ 0x00, 0x8d, 0x8d },
+{ 0x00, 0x8e, 0x8e },
+{ 0x00, 0x8f, 0x8f },
+{ 0x00, 0x90, 0x90 },
+{ 0x00, 0x91, 0x91 },
+{ 0x00, 0x92, 0x92 },
+{ 0x00, 0x93, 0x93 },
+{ 0x00, 0x94, 0x94 },
+{ 0x00, 0x95, 0x95 },
+{ 0x00, 0x96, 0x96 },
+{ 0x00, 0x97, 0x97 },
+{ 0x00, 0x98, 0x98 },
+{ 0x00, 0x99, 0x99 },
+{ 0x00, 0x9a, 0x9a },
+{ 0x00, 0x9b, 0x9b },
+{ 0x00, 0x9c, 0x9c },
+{ 0x00, 0x9d, 0x9d },
+{ 0x00, 0x9e, 0x9e },
+{ 0x00, 0x9f, 0x9f },
+{ 0x00, 0xa0, 0xa0 },
+{ 0x01, 0xb1, 0xa1 },
+{ 0x00, 0xa2, 0xa2 },
+{ 0x00, 0xa3, 0xa3 },
+{ 0x00, 0xa4, 0xa4 },
+{ 0x00, 0xa5, 0xa5 },
+{ 0x01, 0xb6, 0xa6 },
+{ 0x00, 0xa7, 0xa7 },
+{ 0x00, 0xa8, 0xa8 },
+{ 0x01, 0x69, 0xa9 },
+{ 0x01, 0xba, 0xaa },
+{ 0x01, 0xbb, 0xab },
+{ 0x01, 0xbc, 0xac },
+{ 0x00, 0xad, 0xad },
+{ 0x00, 0xae, 0xae },
+{ 0x01, 0xbf, 0xaf },
+{ 0x00, 0xb0, 0xb0 },
+{ 0x00, 0xb1, 0xa1 },
+{ 0x00, 0xb2, 0xb2 },
+{ 0x00, 0xb3, 0xb3 },
+{ 0x00, 0xb4, 0xb4 },
+{ 0x00, 0xb5, 0xb5 },
+{ 0x00, 0xb6, 0xa6 },
+{ 0x00, 0xb7, 0xb7 },
+{ 0x00, 0xb8, 0xb8 },
+{ 0x00, 0xb9, 0x49 },
+{ 0x00, 0xba, 0xaa },
+{ 0x00, 0xbb, 0xab },
+{ 0x00, 0xbc, 0xac },
+{ 0x00, 0xbd, 0xbd },
+{ 0x00, 0xbe, 0xbe },
+{ 0x00, 0xbf, 0xaf },
+{ 0x01, 0xe0, 0xc0 },
+{ 0x01, 0xe1, 0xc1 },
+{ 0x01, 0xe2, 0xc2 },
+{ 0x00, 0xc3, 0xc3 },
+{ 0x01, 0xe4, 0xc4 },
+{ 0x01, 0xe5, 0xc5 },
+{ 0x01, 0xe6, 0xc6 },
+{ 0x01, 0xe7, 0xc7 },
+{ 0x01, 0xe8, 0xc8 },
+{ 0x01, 0xe9, 0xc9 },
+{ 0x01, 0xea, 0xca },
+{ 0x01, 0xeb, 0xcb },
+{ 0x01, 0xec, 0xcc },
+{ 0x01, 0xed, 0xcd },
+{ 0x01, 0xee, 0xce },
+{ 0x01, 0xef, 0xcf },
+{ 0x00, 0xd0, 0xd0 },
+{ 0x01, 0xf1, 0xd1 },
+{ 0x01, 0xf2, 0xd2 },
+{ 0x01, 0xf3, 0xd3 },
+{ 0x01, 0xf4, 0xd4 },
+{ 0x01, 0xf5, 0xd5 },
+{ 0x01, 0xf6, 0xd6 },
+{ 0x00, 0xd7, 0xd7 },
+{ 0x01, 0xf8, 0xd8 },
+{ 0x01, 0xf9, 0xd9 },
+{ 0x01, 0xfa, 0xda },
+{ 0x01, 0xfb, 0xdb },
+{ 0x01, 0xfc, 0xdc },
+{ 0x01, 0xfd, 0xdd },
+{ 0x01, 0xfe, 0xde },
+{ 0x00, 0xdf, 0xdf },
+{ 0x00, 0xe0, 0xc0 },
+{ 0x00, 0xe1, 0xc1 },
+{ 0x00, 0xe2, 0xc2 },
+{ 0x00, 0xe3, 0xe3 },
+{ 0x00, 0xe4, 0xc4 },
+{ 0x00, 0xe5, 0xc5 },
+{ 0x00, 0xe6, 0xc6 },
+{ 0x00, 0xe7, 0xc7 },
+{ 0x00, 0xe8, 0xc8 },
+{ 0x00, 0xe9, 0xc9 },
+{ 0x00, 0xea, 0xca },
+{ 0x00, 0xeb, 0xcb },
+{ 0x00, 0xec, 0xcc },
+{ 0x00, 0xed, 0xcd },
+{ 0x00, 0xee, 0xce },
+{ 0x00, 0xef, 0xcf },
+{ 0x00, 0xf0, 0xf0 },
+{ 0x00, 0xf1, 0xd1 },
+{ 0x00, 0xf2, 0xd2 },
+{ 0x00, 0xf3, 0xd3 },
+{ 0x00, 0xf4, 0xd4 },
+{ 0x00, 0xf5, 0xd5 },
+{ 0x00, 0xf6, 0xd6 },
+{ 0x00, 0xf7, 0xf7 },
+{ 0x00, 0xf8, 0xd8 },
+{ 0x00, 0xf9, 0xd9 },
+{ 0x00, 0xfa, 0xda },
+{ 0x00, 0xfb, 0xdb },
+{ 0x00, 0xfc, 0xdc },
+{ 0x00, 0xfd, 0xdd },
+{ 0x00, 0xfe, 0xde },
+{ 0x00, 0xff, 0xff }
+};
+
+static struct cs_info iso4_tbl[] = {
+{ 0x00, 0x00, 0x00 },
+{ 0x00, 0x01, 0x01 },
+{ 0x00, 0x02, 0x02 },
+{ 0x00, 0x03, 0x03 },
+{ 0x00, 0x04, 0x04 },
+{ 0x00, 0x05, 0x05 },
+{ 0x00, 0x06, 0x06 },
+{ 0x00, 0x07, 0x07 },
+{ 0x00, 0x08, 0x08 },
+{ 0x00, 0x09, 0x09 },
+{ 0x00, 0x0a, 0x0a },
+{ 0x00, 0x0b, 0x0b },
+{ 0x00, 0x0c, 0x0c },
+{ 0x00, 0x0d, 0x0d },
+{ 0x00, 0x0e, 0x0e },
+{ 0x00, 0x0f, 0x0f },
+{ 0x00, 0x10, 0x10 },
+{ 0x00, 0x11, 0x11 },
+{ 0x00, 0x12, 0x12 },
+{ 0x00, 0x13, 0x13 },
+{ 0x00, 0x14, 0x14 },
+{ 0x00, 0x15, 0x15 },
+{ 0x00, 0x16, 0x16 },
+{ 0x00, 0x17, 0x17 },
+{ 0x00, 0x18, 0x18 },
+{ 0x00, 0x19, 0x19 },
+{ 0x00, 0x1a, 0x1a },
+{ 0x00, 0x1b, 0x1b },
+{ 0x00, 0x1c, 0x1c },
+{ 0x00, 0x1d, 0x1d },
+{ 0x00, 0x1e, 0x1e },
+{ 0x00, 0x1f, 0x1f },
+{ 0x00, 0x20, 0x20 },
+{ 0x00, 0x21, 0x21 },
+{ 0x00, 0x22, 0x22 },
+{ 0x00, 0x23, 0x23 },
+{ 0x00, 0x24, 0x24 },
+{ 0x00, 0x25, 0x25 },
+{ 0x00, 0x26, 0x26 },
+{ 0x00, 0x27, 0x27 },
+{ 0x00, 0x28, 0x28 },
+{ 0x00, 0x29, 0x29 },
+{ 0x00, 0x2a, 0x2a },
+{ 0x00, 0x2b, 0x2b },
+{ 0x00, 0x2c, 0x2c },
+{ 0x00, 0x2d, 0x2d },
+{ 0x00, 0x2e, 0x2e },
+{ 0x00, 0x2f, 0x2f },
+{ 0x00, 0x30, 0x30 },
+{ 0x00, 0x31, 0x31 },
+{ 0x00, 0x32, 0x32 },
+{ 0x00, 0x33, 0x33 },
+{ 0x00, 0x34, 0x34 },
+{ 0x00, 0x35, 0x35 },
+{ 0x00, 0x36, 0x36 },
+{ 0x00, 0x37, 0x37 },
+{ 0x00, 0x38, 0x38 },
+{ 0x00, 0x39, 0x39 },
+{ 0x00, 0x3a, 0x3a },
+{ 0x00, 0x3b, 0x3b },
+{ 0x00, 0x3c, 0x3c },
+{ 0x00, 0x3d, 0x3d },
+{ 0x00, 0x3e, 0x3e },
+{ 0x00, 0x3f, 0x3f },
+{ 0x00, 0x40, 0x40 },
+{ 0x01, 0x61, 0x41 },
+{ 0x01, 0x62, 0x42 },
+{ 0x01, 0x63, 0x43 },
+{ 0x01, 0x64, 0x44 },
+{ 0x01, 0x65, 0x45 },
+{ 0x01, 0x66, 0x46 },
+{ 0x01, 0x67, 0x47 },
+{ 0x01, 0x68, 0x48 },
+{ 0x01, 0x69, 0x49 },
+{ 0x01, 0x6a, 0x4a },
+{ 0x01, 0x6b, 0x4b },
+{ 0x01, 0x6c, 0x4c },
+{ 0x01, 0x6d, 0x4d },
+{ 0x01, 0x6e, 0x4e },
+{ 0x01, 0x6f, 0x4f },
+{ 0x01, 0x70, 0x50 },
+{ 0x01, 0x71, 0x51 },
+{ 0x01, 0x72, 0x52 },
+{ 0x01, 0x73, 0x53 },
+{ 0x01, 0x74, 0x54 },
+{ 0x01, 0x75, 0x55 },
+{ 0x01, 0x76, 0x56 },
+{ 0x01, 0x77, 0x57 },
+{ 0x01, 0x78, 0x58 },
+{ 0x01, 0x79, 0x59 },
+{ 0x01, 0x7a, 0x5a },
+{ 0x00, 0x5b, 0x5b },
+{ 0x00, 0x5c, 0x5c },
+{ 0x00, 0x5d, 0x5d },
+{ 0x00, 0x5e, 0x5e },
+{ 0x00, 0x5f, 0x5f },
+{ 0x00, 0x60, 0x60 },
+{ 0x00, 0x61, 0x41 },
+{ 0x00, 0x62, 0x42 },
+{ 0x00, 0x63, 0x43 },
+{ 0x00, 0x64, 0x44 },
+{ 0x00, 0x65, 0x45 },
+{ 0x00, 0x66, 0x46 },
+{ 0x00, 0x67, 0x47 },
+{ 0x00, 0x68, 0x48 },
+{ 0x00, 0x69, 0x49 },
+{ 0x00, 0x6a, 0x4a },
+{ 0x00, 0x6b, 0x4b },
+{ 0x00, 0x6c, 0x4c },
+{ 0x00, 0x6d, 0x4d },
+{ 0x00, 0x6e, 0x4e },
+{ 0x00, 0x6f, 0x4f },
+{ 0x00, 0x70, 0x50 },
+{ 0x00, 0x71, 0x51 },
+{ 0x00, 0x72, 0x52 },
+{ 0x00, 0x73, 0x53 },
+{ 0x00, 0x74, 0x54 },
+{ 0x00, 0x75, 0x55 },
+{ 0x00, 0x76, 0x56 },
+{ 0x00, 0x77, 0x57 },
+{ 0x00, 0x78, 0x58 },
+{ 0x00, 0x79, 0x59 },
+{ 0x00, 0x7a, 0x5a },
+{ 0x00, 0x7b, 0x7b },
+{ 0x00, 0x7c, 0x7c },
+{ 0x00, 0x7d, 0x7d },
+{ 0x00, 0x7e, 0x7e },
+{ 0x00, 0x7f, 0x7f },
+{ 0x00, 0x80, 0x80 },
+{ 0x00, 0x81, 0x81 },
+{ 0x00, 0x82, 0x82 },
+{ 0x00, 0x83, 0x83 },
+{ 0x00, 0x84, 0x84 },
+{ 0x00, 0x85, 0x85 },
+{ 0x00, 0x86, 0x86 },
+{ 0x00, 0x87, 0x87 },
+{ 0x00, 0x88, 0x88 },
+{ 0x00, 0x89, 0x89 },
+{ 0x00, 0x8a, 0x8a },
+{ 0x00, 0x8b, 0x8b },
+{ 0x00, 0x8c, 0x8c },
+{ 0x00, 0x8d, 0x8d },
+{ 0x00, 0x8e, 0x8e },
+{ 0x00, 0x8f, 0x8f },
+{ 0x00, 0x90, 0x90 },
+{ 0x00, 0x91, 0x91 },
+{ 0x00, 0x92, 0x92 },
+{ 0x00, 0x93, 0x93 },
+{ 0x00, 0x94, 0x94 },
+{ 0x00, 0x95, 0x95 },
+{ 0x00, 0x96, 0x96 },
+{ 0x00, 0x97, 0x97 },
+{ 0x00, 0x98, 0x98 },
+{ 0x00, 0x99, 0x99 },
+{ 0x00, 0x9a, 0x9a },
+{ 0x00, 0x9b, 0x9b },
+{ 0x00, 0x9c, 0x9c },
+{ 0x00, 0x9d, 0x9d },
+{ 0x00, 0x9e, 0x9e },
+{ 0x00, 0x9f, 0x9f },
+{ 0x00, 0xa0, 0xa0 },
+{ 0x01, 0xb1, 0xa1 },
+{ 0x00, 0xa2, 0xa2 },
+{ 0x01, 0xb3, 0xa3 },
+{ 0x00, 0xa4, 0xa4 },
+{ 0x01, 0xb5, 0xa5 },
+{ 0x01, 0xb6, 0xa6 },
+{ 0x00, 0xa7, 0xa7 },
+{ 0x00, 0xa8, 0xa8 },
+{ 0x01, 0xb9, 0xa9 },
+{ 0x01, 0xba, 0xaa },
+{ 0x01, 0xbb, 0xab },
+{ 0x01, 0xbc, 0xac },
+{ 0x00, 0xad, 0xad },
+{ 0x01, 0xbe, 0xae },
+{ 0x00, 0xaf, 0xaf },
+{ 0x00, 0xb0, 0xb0 },
+{ 0x00, 0xb1, 0xa1 },
+{ 0x00, 0xb2, 0xb2 },
+{ 0x00, 0xb3, 0xa3 },
+{ 0x00, 0xb4, 0xb4 },
+{ 0x00, 0xb5, 0xa5 },
+{ 0x00, 0xb6, 0xa6 },
+{ 0x00, 0xb7, 0xb7 },
+{ 0x00, 0xb8, 0xb8 },
+{ 0x00, 0xb9, 0xa9 },
+{ 0x00, 0xba, 0xaa },
+{ 0x00, 0xbb, 0xab },
+{ 0x00, 0xbc, 0xac },
+{ 0x00, 0xbd, 0xbd },
+{ 0x00, 0xbe, 0xae },
+{ 0x00, 0xbf, 0xbf },
+{ 0x01, 0xe0, 0xc0 },
+{ 0x01, 0xe1, 0xc1 },
+{ 0x01, 0xe2, 0xc2 },
+{ 0x01, 0xe3, 0xc3 },
+{ 0x01, 0xe4, 0xc4 },
+{ 0x01, 0xe5, 0xc5 },
+{ 0x01, 0xe6, 0xc6 },
+{ 0x01, 0xe7, 0xc7 },
+{ 0x01, 0xe8, 0xc8 },
+{ 0x01, 0xe9, 0xc9 },
+{ 0x01, 0xea, 0xca },
+{ 0x01, 0xeb, 0xcb },
+{ 0x01, 0xec, 0xcc },
+{ 0x01, 0xed, 0xcd },
+{ 0x01, 0xee, 0xce },
+{ 0x01, 0xef, 0xcf },
+{ 0x01, 0xf0, 0xd0 },
+{ 0x01, 0xf1, 0xd1 },
+{ 0x01, 0xf2, 0xd2 },
+{ 0x01, 0xf3, 0xd3 },
+{ 0x01, 0xf4, 0xd4 },
+{ 0x01, 0xf5, 0xd5 },
+{ 0x01, 0xf6, 0xd6 },
+{ 0x00, 0xd7, 0xd7 },
+{ 0x01, 0xf8, 0xd8 },
+{ 0x01, 0xf9, 0xd9 },
+{ 0x01, 0xfa, 0xda },
+{ 0x01, 0xfb, 0xdb },
+{ 0x01, 0xfc, 0xdc },
+{ 0x01, 0xfd, 0xdd },
+{ 0x01, 0xfe, 0xde },
+{ 0x00, 0xdf, 0xdf },
+{ 0x00, 0xe0, 0xc0 },
+{ 0x00, 0xe1, 0xc1 },
+{ 0x00, 0xe2, 0xc2 },
+{ 0x00, 0xe3, 0xc3 },
+{ 0x00, 0xe4, 0xc4 },
+{ 0x00, 0xe5, 0xc5 },
+{ 0x00, 0xe6, 0xc6 },
+{ 0x00, 0xe7, 0xc7 },
+{ 0x00, 0xe8, 0xc8 },
+{ 0x00, 0xe9, 0xc9 },
+{ 0x00, 0xea, 0xca },
+{ 0x00, 0xeb, 0xcb },
+{ 0x00, 0xec, 0xcc },
+{ 0x00, 0xed, 0xcd },
+{ 0x00, 0xee, 0xce },
+{ 0x00, 0xef, 0xcf },
+{ 0x00, 0xf0, 0xd0 },
+{ 0x00, 0xf1, 0xd1 },
+{ 0x00, 0xf2, 0xd2 },
+{ 0x00, 0xf3, 0xd3 },
+{ 0x00, 0xf4, 0xd4 },
+{ 0x00, 0xf5, 0xd5 },
+{ 0x00, 0xf6, 0xd6 },
+{ 0x00, 0xf7, 0xf7 },
+{ 0x00, 0xf8, 0xd8 },
+{ 0x00, 0xf9, 0xd9 },
+{ 0x00, 0xfa, 0xda },
+{ 0x00, 0xfb, 0xdb },
+{ 0x00, 0xfc, 0xdc },
+{ 0x00, 0xfd, 0xdd },
+{ 0x00, 0xfe, 0xde },
+{ 0x00, 0xff, 0xff }
+};
+
+static struct cs_info iso5_tbl[] = {
+{ 0x00, 0x00, 0x00 },
+{ 0x00, 0x01, 0x01 },
+{ 0x00, 0x02, 0x02 },
+{ 0x00, 0x03, 0x03 },
+{ 0x00, 0x04, 0x04 },
+{ 0x00, 0x05, 0x05 },
+{ 0x00, 0x06, 0x06 },
+{ 0x00, 0x07, 0x07 },
+{ 0x00, 0x08, 0x08 },
+{ 0x00, 0x09, 0x09 },
+{ 0x00, 0x0a, 0x0a },
+{ 0x00, 0x0b, 0x0b },
+{ 0x00, 0x0c, 0x0c },
+{ 0x00, 0x0d, 0x0d },
+{ 0x00, 0x0e, 0x0e },
+{ 0x00, 0x0f, 0x0f },
+{ 0x00, 0x10, 0x10 },
+{ 0x00, 0x11, 0x11 },
+{ 0x00, 0x12, 0x12 },
+{ 0x00, 0x13, 0x13 },
+{ 0x00, 0x14, 0x14 },
+{ 0x00, 0x15, 0x15 },
+{ 0x00, 0x16, 0x16 },
+{ 0x00, 0x17, 0x17 },
+{ 0x00, 0x18, 0x18 },
+{ 0x00, 0x19, 0x19 },
+{ 0x00, 0x1a, 0x1a },
+{ 0x00, 0x1b, 0x1b },
+{ 0x00, 0x1c, 0x1c },
+{ 0x00, 0x1d, 0x1d },
+{ 0x00, 0x1e, 0x1e },
+{ 0x00, 0x1f, 0x1f },
+{ 0x00, 0x20, 0x20 },
+{ 0x00, 0x21, 0x21 },
+{ 0x00, 0x22, 0x22 },
+{ 0x00, 0x23, 0x23 },
+{ 0x00, 0x24, 0x24 },
+{ 0x00, 0x25, 0x25 },
+{ 0x00, 0x26, 0x26 },
+{ 0x00, 0x27, 0x27 },
+{ 0x00, 0x28, 0x28 },
+{ 0x00, 0x29, 0x29 },
+{ 0x00, 0x2a, 0x2a },
+{ 0x00, 0x2b, 0x2b },
+{ 0x00, 0x2c, 0x2c },
+{ 0x00, 0x2d, 0x2d },
+{ 0x00, 0x2e, 0x2e },
+{ 0x00, 0x2f, 0x2f },
+{ 0x00, 0x30, 0x30 },
+{ 0x00, 0x31, 0x31 },
+{ 0x00, 0x32, 0x32 },
+{ 0x00, 0x33, 0x33 },
+{ 0x00, 0x34, 0x34 },
+{ 0x00, 0x35, 0x35 },
+{ 0x00, 0x36, 0x36 },
+{ 0x00, 0x37, 0x37 },
+{ 0x00, 0x38, 0x38 },
+{ 0x00, 0x39, 0x39 },
+{ 0x00, 0x3a, 0x3a },
+{ 0x00, 0x3b, 0x3b },
+{ 0x00, 0x3c, 0x3c },
+{ 0x00, 0x3d, 0x3d },
+{ 0x00, 0x3e, 0x3e },
+{ 0x00, 0x3f, 0x3f },
+{ 0x00, 0x40, 0x40 },
+{ 0x01, 0x61, 0x41 },
+{ 0x01, 0x62, 0x42 },
+{ 0x01, 0x63, 0x43 },
+{ 0x01, 0x64, 0x44 },
+{ 0x01, 0x65, 0x45 },
+{ 0x01, 0x66, 0x46 },
+{ 0x01, 0x67, 0x47 },
+{ 0x01, 0x68, 0x48 },
+{ 0x01, 0x69, 0x49 },
+{ 0x01, 0x6a, 0x4a },
+{ 0x01, 0x6b, 0x4b },
+{ 0x01, 0x6c, 0x4c },
+{ 0x01, 0x6d, 0x4d },
+{ 0x01, 0x6e, 0x4e },
+{ 0x01, 0x6f, 0x4f },
+{ 0x01, 0x70, 0x50 },
+{ 0x01, 0x71, 0x51 },
+{ 0x01, 0x72, 0x52 },
+{ 0x01, 0x73, 0x53 },
+{ 0x01, 0x74, 0x54 },
+{ 0x01, 0x75, 0x55 },
+{ 0x01, 0x76, 0x56 },
+{ 0x01, 0x77, 0x57 },
+{ 0x01, 0x78, 0x58 },
+{ 0x01, 0x79, 0x59 },
+{ 0x01, 0x7a, 0x5a },
+{ 0x00, 0x5b, 0x5b },
+{ 0x00, 0x5c, 0x5c },
+{ 0x00, 0x5d, 0x5d },
+{ 0x00, 0x5e, 0x5e },
+{ 0x00, 0x5f, 0x5f },
+{ 0x00, 0x60, 0x60 },
+{ 0x00, 0x61, 0x41 },
+{ 0x00, 0x62, 0x42 },
+{ 0x00, 0x63, 0x43 },
+{ 0x00, 0x64, 0x44 },
+{ 0x00, 0x65, 0x45 },
+{ 0x00, 0x66, 0x46 },
+{ 0x00, 0x67, 0x47 },
+{ 0x00, 0x68, 0x48 },
+{ 0x00, 0x69, 0x49 },
+{ 0x00, 0x6a, 0x4a },
+{ 0x00, 0x6b, 0x4b },
+{ 0x00, 0x6c, 0x4c },
+{ 0x00, 0x6d, 0x4d },
+{ 0x00, 0x6e, 0x4e },
+{ 0x00, 0x6f, 0x4f },
+{ 0x00, 0x70, 0x50 },
+{ 0x00, 0x71, 0x51 },
+{ 0x00, 0x72, 0x52 },
+{ 0x00, 0x73, 0x53 },
+{ 0x00, 0x74, 0x54 },
+{ 0x00, 0x75, 0x55 },
+{ 0x00, 0x76, 0x56 },
+{ 0x00, 0x77, 0x57 },
+{ 0x00, 0x78, 0x58 },
+{ 0x00, 0x79, 0x59 },
+{ 0x00, 0x7a, 0x5a },
+{ 0x00, 0x7b, 0x7b },
+{ 0x00, 0x7c, 0x7c },
+{ 0x00, 0x7d, 0x7d },
+{ 0x00, 0x7e, 0x7e },
+{ 0x00, 0x7f, 0x7f },
+{ 0x00, 0x80, 0x80 },
+{ 0x00, 0x81, 0x81 },
+{ 0x00, 0x82, 0x82 },
+{ 0x00, 0x83, 0x83 },
+{ 0x00, 0x84, 0x84 },
+{ 0x00, 0x85, 0x85 },
+{ 0x00, 0x86, 0x86 },
+{ 0x00, 0x87, 0x87 },
+{ 0x00, 0x88, 0x88 },
+{ 0x00, 0x89, 0x89 },
+{ 0x00, 0x8a, 0x8a },
+{ 0x00, 0x8b, 0x8b },
+{ 0x00, 0x8c, 0x8c },
+{ 0x00, 0x8d, 0x8d },
+{ 0x00, 0x8e, 0x8e },
+{ 0x00, 0x8f, 0x8f },
+{ 0x00, 0x90, 0x90 },
+{ 0x00, 0x91, 0x91 },
+{ 0x00, 0x92, 0x92 },
+{ 0x00, 0x93, 0x93 },
+{ 0x00, 0x94, 0x94 },
+{ 0x00, 0x95, 0x95 },
+{ 0x00, 0x96, 0x96 },
+{ 0x00, 0x97, 0x97 },
+{ 0x00, 0x98, 0x98 },
+{ 0x00, 0x99, 0x99 },
+{ 0x00, 0x9a, 0x9a },
+{ 0x00, 0x9b, 0x9b },
+{ 0x00, 0x9c, 0x9c },
+{ 0x00, 0x9d, 0x9d },
+{ 0x00, 0x9e, 0x9e },
+{ 0x00, 0x9f, 0x9f },
+{ 0x00, 0xa0, 0xa0 },
+{ 0x01, 0xf1, 0xa1 },
+{ 0x01, 0xf2, 0xa2 },
+{ 0x01, 0xf3, 0xa3 },
+{ 0x01, 0xf4, 0xa4 },
+{ 0x01, 0xf5, 0xa5 },
+{ 0x01, 0xf6, 0xa6 },
+{ 0x01, 0xf7, 0xa7 },
+{ 0x01, 0xf8, 0xa8 },
+{ 0x01, 0xf9, 0xa9 },
+{ 0x01, 0xfa, 0xaa },
+{ 0x01, 0xfb, 0xab },
+{ 0x01, 0xfc, 0xac },
+{ 0x00, 0xad, 0xad },
+{ 0x01, 0xfe, 0xae },
+{ 0x01, 0xff, 0xaf },
+{ 0x01, 0xd0, 0xb0 },
+{ 0x01, 0xd1, 0xb1 },
+{ 0x01, 0xd2, 0xb2 },
+{ 0x01, 0xd3, 0xb3 },
+{ 0x01, 0xd4, 0xb4 },
+{ 0x01, 0xd5, 0xb5 },
+{ 0x01, 0xd6, 0xb6 },
+{ 0x01, 0xd7, 0xb7 },
+{ 0x01, 0xd8, 0xb8 },
+{ 0x01, 0xd9, 0xb9 },
+{ 0x01, 0xda, 0xba },
+{ 0x01, 0xdb, 0xbb },
+{ 0x01, 0xdc, 0xbc },
+{ 0x01, 0xdd, 0xbd },
+{ 0x01, 0xde, 0xbe },
+{ 0x01, 0xdf, 0xbf },
+{ 0x01, 0xe0, 0xc0 },
+{ 0x01, 0xe1, 0xc1 },
+{ 0x01, 0xe2, 0xc2 },
+{ 0x01, 0xe3, 0xc3 },
+{ 0x01, 0xe4, 0xc4 },
+{ 0x01, 0xe5, 0xc5 },
+{ 0x01, 0xe6, 0xc6 },
+{ 0x01, 0xe7, 0xc7 },
+{ 0x01, 0xe8, 0xc8 },
+{ 0x01, 0xe9, 0xc9 },
+{ 0x01, 0xea, 0xca },
+{ 0x01, 0xeb, 0xcb },
+{ 0x01, 0xec, 0xcc },
+{ 0x01, 0xed, 0xcd },
+{ 0x01, 0xee, 0xce },
+{ 0x01, 0xef, 0xcf },
+{ 0x00, 0xd0, 0xb0 },
+{ 0x00, 0xd1, 0xb1 },
+{ 0x00, 0xd2, 0xb2 },
+{ 0x00, 0xd3, 0xb3 },
+{ 0x00, 0xd4, 0xb4 },
+{ 0x00, 0xd5, 0xb5 },
+{ 0x00, 0xd6, 0xb6 },
+{ 0x00, 0xd7, 0xb7 },
+{ 0x00, 0xd8, 0xb8 },
+{ 0x00, 0xd9, 0xb9 },
+{ 0x00, 0xda, 0xba },
+{ 0x00, 0xdb, 0xbb },
+{ 0x00, 0xdc, 0xbc },
+{ 0x00, 0xdd, 0xbd },
+{ 0x00, 0xde, 0xbe },
+{ 0x00, 0xdf, 0xbf },
+{ 0x00, 0xe0, 0xc0 },
+{ 0x00, 0xe1, 0xc1 },
+{ 0x00, 0xe2, 0xc2 },
+{ 0x00, 0xe3, 0xc3 },
+{ 0x00, 0xe4, 0xc4 },
+{ 0x00, 0xe5, 0xc5 },
+{ 0x00, 0xe6, 0xc6 },
+{ 0x00, 0xe7, 0xc7 },
+{ 0x00, 0xe8, 0xc8 },
+{ 0x00, 0xe9, 0xc9 },
+{ 0x00, 0xea, 0xca },
+{ 0x00, 0xeb, 0xcb },
+{ 0x00, 0xec, 0xcc },
+{ 0x00, 0xed, 0xcd },
+{ 0x00, 0xee, 0xce },
+{ 0x00, 0xef, 0xcf },
+{ 0x00, 0xf0, 0xf0 },
+{ 0x00, 0xf1, 0xa1 },
+{ 0x00, 0xf2, 0xa2 },
+{ 0x00, 0xf3, 0xa3 },
+{ 0x00, 0xf4, 0xa4 },
+{ 0x00, 0xf5, 0xa5 },
+{ 0x00, 0xf6, 0xa6 },
+{ 0x00, 0xf7, 0xa7 },
+{ 0x00, 0xf8, 0xa8 },
+{ 0x00, 0xf9, 0xa9 },
+{ 0x00, 0xfa, 0xaa },
+{ 0x00, 0xfb, 0xab },
+{ 0x00, 0xfc, 0xac },
+{ 0x00, 0xfd, 0xfd },
+{ 0x00, 0xfe, 0xae },
+{ 0x00, 0xff, 0xaf }
+};
+
+static struct cs_info iso6_tbl[] = {
+{ 0x00, 0x00, 0x00 },
+{ 0x00, 0x01, 0x01 },
+{ 0x00, 0x02, 0x02 },
+{ 0x00, 0x03, 0x03 },
+{ 0x00, 0x04, 0x04 },
+{ 0x00, 0x05, 0x05 },
+{ 0x00, 0x06, 0x06 },
+{ 0x00, 0x07, 0x07 },
+{ 0x00, 0x08, 0x08 },
+{ 0x00, 0x09, 0x09 },
+{ 0x00, 0x0a, 0x0a },
+{ 0x00, 0x0b, 0x0b },
+{ 0x00, 0x0c, 0x0c },
+{ 0x00, 0x0d, 0x0d },
+{ 0x00, 0x0e, 0x0e },
+{ 0x00, 0x0f, 0x0f },
+{ 0x00, 0x10, 0x10 },
+{ 0x00, 0x11, 0x11 },
+{ 0x00, 0x12, 0x12 },
+{ 0x00, 0x13, 0x13 },
+{ 0x00, 0x14, 0x14 },
+{ 0x00, 0x15, 0x15 },
+{ 0x00, 0x16, 0x16 },
+{ 0x00, 0x17, 0x17 },
+{ 0x00, 0x18, 0x18 },
+{ 0x00, 0x19, 0x19 },
+{ 0x00, 0x1a, 0x1a },
+{ 0x00, 0x1b, 0x1b },
+{ 0x00, 0x1c, 0x1c },
+{ 0x00, 0x1d, 0x1d },
+{ 0x00, 0x1e, 0x1e },
+{ 0x00, 0x1f, 0x1f },
+{ 0x00, 0x20, 0x20 },
+{ 0x00, 0x21, 0x21 },
+{ 0x00, 0x22, 0x22 },
+{ 0x00, 0x23, 0x23 },
+{ 0x00, 0x24, 0x24 },
+{ 0x00, 0x25, 0x25 },
+{ 0x00, 0x26, 0x26 },
+{ 0x00, 0x27, 0x27 },
+{ 0x00, 0x28, 0x28 },
+{ 0x00, 0x29, 0x29 },
+{ 0x00, 0x2a, 0x2a },
+{ 0x00, 0x2b, 0x2b },
+{ 0x00, 0x2c, 0x2c },
+{ 0x00, 0x2d, 0x2d },
+{ 0x00, 0x2e, 0x2e },
+{ 0x00, 0x2f, 0x2f },
+{ 0x00, 0x30, 0x30 },
+{ 0x00, 0x31, 0x31 },
+{ 0x00, 0x32, 0x32 },
+{ 0x00, 0x33, 0x33 },
+{ 0x00, 0x34, 0x34 },
+{ 0x00, 0x35, 0x35 },
+{ 0x00, 0x36, 0x36 },
+{ 0x00, 0x37, 0x37 },
+{ 0x00, 0x38, 0x38 },
+{ 0x00, 0x39, 0x39 },
+{ 0x00, 0x3a, 0x3a },
+{ 0x00, 0x3b, 0x3b },
+{ 0x00, 0x3c, 0x3c },
+{ 0x00, 0x3d, 0x3d },
+{ 0x00, 0x3e, 0x3e },
+{ 0x00, 0x3f, 0x3f },
+{ 0x00, 0x40, 0x40 },
+{ 0x01, 0x61, 0x41 },
+{ 0x01, 0x62, 0x42 },
+{ 0x01, 0x63, 0x43 },
+{ 0x01, 0x64, 0x44 },
+{ 0x01, 0x65, 0x45 },
+{ 0x01, 0x66, 0x46 },
+{ 0x01, 0x67, 0x47 },
+{ 0x01, 0x68, 0x48 },
+{ 0x01, 0x69, 0x49 },
+{ 0x01, 0x6a, 0x4a },
+{ 0x01, 0x6b, 0x4b },
+{ 0x01, 0x6c, 0x4c },
+{ 0x01, 0x6d, 0x4d },
+{ 0x01, 0x6e, 0x4e },
+{ 0x01, 0x6f, 0x4f },
+{ 0x01, 0x70, 0x50 },
+{ 0x01, 0x71, 0x51 },
+{ 0x01, 0x72, 0x52 },
+{ 0x01, 0x73, 0x53 },
+{ 0x01, 0x74, 0x54 },
+{ 0x01, 0x75, 0x55 },
+{ 0x01, 0x76, 0x56 },
+{ 0x01, 0x77, 0x57 },
+{ 0x01, 0x78, 0x58 },
+{ 0x01, 0x79, 0x59 },
+{ 0x01, 0x7a, 0x5a },
+{ 0x00, 0x5b, 0x5b },
+{ 0x00, 0x5c, 0x5c },
+{ 0x00, 0x5d, 0x5d },
+{ 0x00, 0x5e, 0x5e },
+{ 0x00, 0x5f, 0x5f },
+{ 0x00, 0x60, 0x60 },
+{ 0x00, 0x61, 0x41 },
+{ 0x00, 0x62, 0x42 },
+{ 0x00, 0x63, 0x43 },
+{ 0x00, 0x64, 0x44 },
+{ 0x00, 0x65, 0x45 },
+{ 0x00, 0x66, 0x46 },
+{ 0x00, 0x67, 0x47 },
+{ 0x00, 0x68, 0x48 },
+{ 0x00, 0x69, 0x49 },
+{ 0x00, 0x6a, 0x4a },
+{ 0x00, 0x6b, 0x4b },
+{ 0x00, 0x6c, 0x4c },
+{ 0x00, 0x6d, 0x4d },
+{ 0x00, 0x6e, 0x4e },
+{ 0x00, 0x6f, 0x4f },
+{ 0x00, 0x70, 0x50 },
+{ 0x00, 0x71, 0x51 },
+{ 0x00, 0x72, 0x52 },
+{ 0x00, 0x73, 0x53 },
+{ 0x00, 0x74, 0x54 },
+{ 0x00, 0x75, 0x55 },
+{ 0x00, 0x76, 0x56 },
+{ 0x00, 0x77, 0x57 },
+{ 0x00, 0x78, 0x58 },
+{ 0x00, 0x79, 0x59 },
+{ 0x00, 0x7a, 0x5a },
+{ 0x00, 0x7b, 0x7b },
+{ 0x00, 0x7c, 0x7c },
+{ 0x00, 0x7d, 0x7d },
+{ 0x00, 0x7e, 0x7e },
+{ 0x00, 0x7f, 0x7f },
+{ 0x00, 0x80, 0x80 },
+{ 0x00, 0x81, 0x81 },
+{ 0x00, 0x82, 0x82 },
+{ 0x00, 0x83, 0x83 },
+{ 0x00, 0x84, 0x84 },
+{ 0x00, 0x85, 0x85 },
+{ 0x00, 0x86, 0x86 },
+{ 0x00, 0x87, 0x87 },
+{ 0x00, 0x88, 0x88 },
+{ 0x00, 0x89, 0x89 },
+{ 0x00, 0x8a, 0x8a },
+{ 0x00, 0x8b, 0x8b },
+{ 0x00, 0x8c, 0x8c },
+{ 0x00, 0x8d, 0x8d },
+{ 0x00, 0x8e, 0x8e },
+{ 0x00, 0x8f, 0x8f },
+{ 0x00, 0x90, 0x90 },
+{ 0x00, 0x91, 0x91 },
+{ 0x00, 0x92, 0x92 },
+{ 0x00, 0x93, 0x93 },
+{ 0x00, 0x94, 0x94 },
+{ 0x00, 0x95, 0x95 },
+{ 0x00, 0x96, 0x96 },
+{ 0x00, 0x97, 0x97 },
+{ 0x00, 0x98, 0x98 },
+{ 0x00, 0x99, 0x99 },
+{ 0x00, 0x9a, 0x9a },
+{ 0x00, 0x9b, 0x9b },
+{ 0x00, 0x9c, 0x9c },
+{ 0x00, 0x9d, 0x9d },
+{ 0x00, 0x9e, 0x9e },
+{ 0x00, 0x9f, 0x9f },
+{ 0x00, 0xa0, 0xa0 },
+{ 0x00, 0xa1, 0xa1 },
+{ 0x00, 0xa2, 0xa2 },
+{ 0x00, 0xa3, 0xa3 },
+{ 0x00, 0xa4, 0xa4 },
+{ 0x00, 0xa5, 0xa5 },
+{ 0x00, 0xa6, 0xa6 },
+{ 0x00, 0xa7, 0xa7 },
+{ 0x00, 0xa8, 0xa8 },
+{ 0x00, 0xa9, 0xa9 },
+{ 0x00, 0xaa, 0xaa },
+{ 0x00, 0xab, 0xab },
+{ 0x00, 0xac, 0xac },
+{ 0x00, 0xad, 0xad },
+{ 0x00, 0xae, 0xae },
+{ 0x00, 0xaf, 0xaf },
+{ 0x00, 0xb0, 0xb0 },
+{ 0x00, 0xb1, 0xb1 },
+{ 0x00, 0xb2, 0xb2 },
+{ 0x00, 0xb3, 0xb3 },
+{ 0x00, 0xb4, 0xb4 },
+{ 0x00, 0xb5, 0xb5 },
+{ 0x00, 0xb6, 0xb6 },
+{ 0x00, 0xb7, 0xb7 },
+{ 0x00, 0xb8, 0xb8 },
+{ 0x00, 0xb9, 0xb9 },
+{ 0x00, 0xba, 0xba },
+{ 0x00, 0xbb, 0xbb },
+{ 0x00, 0xbc, 0xbc },
+{ 0x00, 0xbd, 0xbd },
+{ 0x00, 0xbe, 0xbe },
+{ 0x00, 0xbf, 0xbf },
+{ 0x00, 0xc0, 0xc0 },
+{ 0x00, 0xc1, 0xc1 },
+{ 0x00, 0xc2, 0xc2 },
+{ 0x00, 0xc3, 0xc3 },
+{ 0x00, 0xc4, 0xc4 },
+{ 0x00, 0xc5, 0xc5 },
+{ 0x00, 0xc6, 0xc6 },
+{ 0x00, 0xc7, 0xc7 },
+{ 0x00, 0xc8, 0xc8 },
+{ 0x00, 0xc9, 0xc9 },
+{ 0x00, 0xca, 0xca },
+{ 0x00, 0xcb, 0xcb },
+{ 0x00, 0xcc, 0xcc },
+{ 0x00, 0xcd, 0xcd },
+{ 0x00, 0xce, 0xce },
+{ 0x00, 0xcf, 0xcf },
+{ 0x00, 0xd0, 0xd0 },
+{ 0x00, 0xd1, 0xd1 },
+{ 0x00, 0xd2, 0xd2 },
+{ 0x00, 0xd3, 0xd3 },
+{ 0x00, 0xd4, 0xd4 },
+{ 0x00, 0xd5, 0xd5 },
+{ 0x00, 0xd6, 0xd6 },
+{ 0x00, 0xd7, 0xd7 },
+{ 0x00, 0xd8, 0xd8 },
+{ 0x00, 0xd9, 0xd9 },
+{ 0x00, 0xda, 0xda },
+{ 0x00, 0xdb, 0xdb },
+{ 0x00, 0xdc, 0xdc },
+{ 0x00, 0xdd, 0xdd },
+{ 0x00, 0xde, 0xde },
+{ 0x00, 0xdf, 0xdf },
+{ 0x00, 0xe0, 0xe0 },
+{ 0x00, 0xe1, 0xe1 },
+{ 0x00, 0xe2, 0xe2 },
+{ 0x00, 0xe3, 0xe3 },
+{ 0x00, 0xe4, 0xe4 },
+{ 0x00, 0xe5, 0xe5 },
+{ 0x00, 0xe6, 0xe6 },
+{ 0x00, 0xe7, 0xe7 },
+{ 0x00, 0xe8, 0xe8 },
+{ 0x00, 0xe9, 0xe9 },
+{ 0x00, 0xea, 0xea },
+{ 0x00, 0xeb, 0xeb },
+{ 0x00, 0xec, 0xec },
+{ 0x00, 0xed, 0xed },
+{ 0x00, 0xee, 0xee },
+{ 0x00, 0xef, 0xef },
+{ 0x00, 0xf0, 0xf0 },
+{ 0x00, 0xf1, 0xf1 },
+{ 0x00, 0xf2, 0xf2 },
+{ 0x00, 0xf3, 0xf3 },
+{ 0x00, 0xf4, 0xf4 },
+{ 0x00, 0xf5, 0xf5 },
+{ 0x00, 0xf6, 0xf6 },
+{ 0x00, 0xf7, 0xf7 },
+{ 0x00, 0xf8, 0xf8 },
+{ 0x00, 0xf9, 0xf9 },
+{ 0x00, 0xfa, 0xfa },
+{ 0x00, 0xfb, 0xfb },
+{ 0x00, 0xfc, 0xfc },
+{ 0x00, 0xfd, 0xfd },
+{ 0x00, 0xfe, 0xfe },
+{ 0x00, 0xff, 0xff }
+};
+
+static struct cs_info iso7_tbl[] = {
+{ 0x00, 0x00, 0x00 },
+{ 0x00, 0x01, 0x01 },
+{ 0x00, 0x02, 0x02 },
+{ 0x00, 0x03, 0x03 },
+{ 0x00, 0x04, 0x04 },
+{ 0x00, 0x05, 0x05 },
+{ 0x00, 0x06, 0x06 },
+{ 0x00, 0x07, 0x07 },
+{ 0x00, 0x08, 0x08 },
+{ 0x00, 0x09, 0x09 },
+{ 0x00, 0x0a, 0x0a },
+{ 0x00, 0x0b, 0x0b },
+{ 0x00, 0x0c, 0x0c },
+{ 0x00, 0x0d, 0x0d },
+{ 0x00, 0x0e, 0x0e },
+{ 0x00, 0x0f, 0x0f },
+{ 0x00, 0x10, 0x10 },
+{ 0x00, 0x11, 0x11 },
+{ 0x00, 0x12, 0x12 },
+{ 0x00, 0x13, 0x13 },
+{ 0x00, 0x14, 0x14 },
+{ 0x00, 0x15, 0x15 },
+{ 0x00, 0x16, 0x16 },
+{ 0x00, 0x17, 0x17 },
+{ 0x00, 0x18, 0x18 },
+{ 0x00, 0x19, 0x19 },
+{ 0x00, 0x1a, 0x1a },
+{ 0x00, 0x1b, 0x1b },
+{ 0x00, 0x1c, 0x1c },
+{ 0x00, 0x1d, 0x1d },
+{ 0x00, 0x1e, 0x1e },
+{ 0x00, 0x1f, 0x1f },
+{ 0x00, 0x20, 0x20 },
+{ 0x00, 0x21, 0x21 },
+{ 0x00, 0x22, 0x22 },
+{ 0x00, 0x23, 0x23 },
+{ 0x00, 0x24, 0x24 },
+{ 0x00, 0x25, 0x25 },
+{ 0x00, 0x26, 0x26 },
+{ 0x00, 0x27, 0x27 },
+{ 0x00, 0x28, 0x28 },
+{ 0x00, 0x29, 0x29 },
+{ 0x00, 0x2a, 0x2a },
+{ 0x00, 0x2b, 0x2b },
+{ 0x00, 0x2c, 0x2c },
+{ 0x00, 0x2d, 0x2d },
+{ 0x00, 0x2e, 0x2e },
+{ 0x00, 0x2f, 0x2f },
+{ 0x00, 0x30, 0x30 },
+{ 0x00, 0x31, 0x31 },
+{ 0x00, 0x32, 0x32 },
+{ 0x00, 0x33, 0x33 },
+{ 0x00, 0x34, 0x34 },
+{ 0x00, 0x35, 0x35 },
+{ 0x00, 0x36, 0x36 },
+{ 0x00, 0x37, 0x37 },
+{ 0x00, 0x38, 0x38 },
+{ 0x00, 0x39, 0x39 },
+{ 0x00, 0x3a, 0x3a },
+{ 0x00, 0x3b, 0x3b },
+{ 0x00, 0x3c, 0x3c },
+{ 0x00, 0x3d, 0x3d },
+{ 0x00, 0x3e, 0x3e },
+{ 0x00, 0x3f, 0x3f },
+{ 0x00, 0x40, 0x40 },
+{ 0x01, 0x61, 0x41 },
+{ 0x01, 0x62, 0x42 },
+{ 0x01, 0x63, 0x43 },
+{ 0x01, 0x64, 0x44 },
+{ 0x01, 0x65, 0x45 },
+{ 0x01, 0x66, 0x46 },
+{ 0x01, 0x67, 0x47 },
+{ 0x01, 0x68, 0x48 },
+{ 0x01, 0x69, 0x49 },
+{ 0x01, 0x6a, 0x4a },
+{ 0x01, 0x6b, 0x4b },
+{ 0x01, 0x6c, 0x4c },
+{ 0x01, 0x6d, 0x4d },
+{ 0x01, 0x6e, 0x4e },
+{ 0x01, 0x6f, 0x4f },
+{ 0x01, 0x70, 0x50 },
+{ 0x01, 0x71, 0x51 },
+{ 0x01, 0x72, 0x52 },
+{ 0x01, 0x73, 0x53 },
+{ 0x01, 0x74, 0x54 },
+{ 0x01, 0x75, 0x55 },
+{ 0x01, 0x76, 0x56 },
+{ 0x01, 0x77, 0x57 },
+{ 0x01, 0x78, 0x58 },
+{ 0x01, 0x79, 0x59 },
+{ 0x01, 0x7a, 0x5a },
+{ 0x00, 0x5b, 0x5b },
+{ 0x00, 0x5c, 0x5c },
+{ 0x00, 0x5d, 0x5d },
+{ 0x00, 0x5e, 0x5e },
+{ 0x00, 0x5f, 0x5f },
+{ 0x00, 0x60, 0x60 },
+{ 0x00, 0x61, 0x41 },
+{ 0x00, 0x62, 0x42 },
+{ 0x00, 0x63, 0x43 },
+{ 0x00, 0x64, 0x44 },
+{ 0x00, 0x65, 0x45 },
+{ 0x00, 0x66, 0x46 },
+{ 0x00, 0x67, 0x47 },
+{ 0x00, 0x68, 0x48 },
+{ 0x00, 0x69, 0x49 },
+{ 0x00, 0x6a, 0x4a },
+{ 0x00, 0x6b, 0x4b },
+{ 0x00, 0x6c, 0x4c },
+{ 0x00, 0x6d, 0x4d },
+{ 0x00, 0x6e, 0x4e },
+{ 0x00, 0x6f, 0x4f },
+{ 0x00, 0x70, 0x50 },
+{ 0x00, 0x71, 0x51 },
+{ 0x00, 0x72, 0x52 },
+{ 0x00, 0x73, 0x53 },
+{ 0x00, 0x74, 0x54 },
+{ 0x00, 0x75, 0x55 },
+{ 0x00, 0x76, 0x56 },
+{ 0x00, 0x77, 0x57 },
+{ 0x00, 0x78, 0x58 },
+{ 0x00, 0x79, 0x59 },
+{ 0x00, 0x7a, 0x5a },
+{ 0x00, 0x7b, 0x7b },
+{ 0x00, 0x7c, 0x7c },
+{ 0x00, 0x7d, 0x7d },
+{ 0x00, 0x7e, 0x7e },
+{ 0x00, 0x7f, 0x7f },
+{ 0x00, 0x80, 0x80 },
+{ 0x00, 0x81, 0x81 },
+{ 0x00, 0x82, 0x82 },
+{ 0x00, 0x83, 0x83 },
+{ 0x00, 0x84, 0x84 },
+{ 0x00, 0x85, 0x85 },
+{ 0x00, 0x86, 0x86 },
+{ 0x00, 0x87, 0x87 },
+{ 0x00, 0x88, 0x88 },
+{ 0x00, 0x89, 0x89 },
+{ 0x00, 0x8a, 0x8a },
+{ 0x00, 0x8b, 0x8b },
+{ 0x00, 0x8c, 0x8c },
+{ 0x00, 0x8d, 0x8d },
+{ 0x00, 0x8e, 0x8e },
+{ 0x00, 0x8f, 0x8f },
+{ 0x00, 0x90, 0x90 },
+{ 0x00, 0x91, 0x91 },
+{ 0x00, 0x92, 0x92 },
+{ 0x00, 0x93, 0x93 },
+{ 0x00, 0x94, 0x94 },
+{ 0x00, 0x95, 0x95 },
+{ 0x00, 0x96, 0x96 },
+{ 0x00, 0x97, 0x97 },
+{ 0x00, 0x98, 0x98 },
+{ 0x00, 0x99, 0x99 },
+{ 0x00, 0x9a, 0x9a },
+{ 0x00, 0x9b, 0x9b },
+{ 0x00, 0x9c, 0x9c },
+{ 0x00, 0x9d, 0x9d },
+{ 0x00, 0x9e, 0x9e },
+{ 0x00, 0x9f, 0x9f },
+{ 0x00, 0xa0, 0xa0 },
+{ 0x00, 0xa1, 0xa1 },
+{ 0x00, 0xa2, 0xa2 },
+{ 0x00, 0xa3, 0xa3 },
+{ 0x00, 0xa4, 0xa4 },
+{ 0x00, 0xa5, 0xa5 },
+{ 0x00, 0xa6, 0xa6 },
+{ 0x00, 0xa7, 0xa7 },
+{ 0x00, 0xa8, 0xa8 },
+{ 0x00, 0xa9, 0xa9 },
+{ 0x00, 0xaa, 0xaa },
+{ 0x00, 0xab, 0xab },
+{ 0x00, 0xac, 0xac },
+{ 0x00, 0xad, 0xad },
+{ 0x00, 0xae, 0xae },
+{ 0x00, 0xaf, 0xaf },
+{ 0x00, 0xb0, 0xb0 },
+{ 0x00, 0xb1, 0xb1 },
+{ 0x00, 0xb2, 0xb2 },
+{ 0x00, 0xb3, 0xb3 },
+{ 0x00, 0xb4, 0xb4 },
+{ 0x00, 0xb5, 0xb5 },
+{ 0x01, 0xdc, 0xb6 },
+{ 0x00, 0xb7, 0xb7 },
+{ 0x01, 0xdd, 0xb8 },
+{ 0x01, 0xde, 0xb9 },
+{ 0x01, 0xdf, 0xba },
+{ 0x00, 0xbb, 0xbb },
+{ 0x01, 0xfc, 0xbc },
+{ 0x00, 0xbd, 0xbd },
+{ 0x01, 0xfd, 0xbe },
+{ 0x01, 0xfe, 0xbf },
+{ 0x00, 0xc0, 0xc0 },
+{ 0x01, 0xe1, 0xc1 },
+{ 0x01, 0xe2, 0xc2 },
+{ 0x01, 0xe3, 0xc3 },
+{ 0x01, 0xe4, 0xc4 },
+{ 0x01, 0xe5, 0xc5 },
+{ 0x01, 0xe6, 0xc6 },
+{ 0x01, 0xe7, 0xc7 },
+{ 0x01, 0xe8, 0xc8 },
+{ 0x01, 0xe9, 0xc9 },
+{ 0x01, 0xea, 0xca },
+{ 0x01, 0xeb, 0xcb },
+{ 0x01, 0xec, 0xcc },
+{ 0x01, 0xed, 0xcd },
+{ 0x01, 0xee, 0xce },
+{ 0x01, 0xef, 0xcf },
+{ 0x01, 0xf0, 0xd0 },
+{ 0x01, 0xf1, 0xd1 },
+{ 0x00, 0xd2, 0xd2 },
+{ 0x01, 0xf3, 0xd3 },
+{ 0x01, 0xf4, 0xd4 },
+{ 0x01, 0xf5, 0xd5 },
+{ 0x01, 0xf6, 0xd6 },
+{ 0x01, 0xf7, 0xd7 },
+{ 0x01, 0xf8, 0xd8 },
+{ 0x01, 0xf9, 0xd9 },
+{ 0x01, 0xfa, 0xda },
+{ 0x01, 0xfb, 0xdb },
+{ 0x00, 0xdc, 0xb6 },
+{ 0x00, 0xdd, 0xb8 },
+{ 0x00, 0xde, 0xb9 },
+{ 0x00, 0xdf, 0xba },
+{ 0x00, 0xe0, 0xe0 },
+{ 0x00, 0xe1, 0xc1 },
+{ 0x00, 0xe2, 0xc2 },
+{ 0x00, 0xe3, 0xc3 },
+{ 0x00, 0xe4, 0xc4 },
+{ 0x00, 0xe5, 0xc5 },
+{ 0x00, 0xe6, 0xc6 },
+{ 0x00, 0xe7, 0xc7 },
+{ 0x00, 0xe8, 0xc8 },
+{ 0x00, 0xe9, 0xc9 },
+{ 0x00, 0xea, 0xca },
+{ 0x00, 0xeb, 0xcb },
+{ 0x00, 0xec, 0xcc },
+{ 0x00, 0xed, 0xcd },
+{ 0x00, 0xee, 0xce },
+{ 0x00, 0xef, 0xcf },
+{ 0x00, 0xf0, 0xd0 },
+{ 0x00, 0xf1, 0xd1 },
+{ 0x00, 0xf2, 0xd3 },
+{ 0x00, 0xf3, 0xd3 },
+{ 0x00, 0xf4, 0xd4 },
+{ 0x00, 0xf5, 0xd5 },
+{ 0x00, 0xf6, 0xd6 },
+{ 0x00, 0xf7, 0xd7 },
+{ 0x00, 0xf8, 0xd8 },
+{ 0x00, 0xf9, 0xd9 },
+{ 0x00, 0xfa, 0xda },
+{ 0x00, 0xfb, 0xdb },
+{ 0x00, 0xfc, 0xbc },
+{ 0x00, 0xfd, 0xbe },
+{ 0x00, 0xfe, 0xbf },
+{ 0x00, 0xff, 0xff }
+};
+
+static struct cs_info iso8_tbl[] = {
+{ 0x00, 0x00, 0x00 },
+{ 0x00, 0x01, 0x01 },
+{ 0x00, 0x02, 0x02 },
+{ 0x00, 0x03, 0x03 },
+{ 0x00, 0x04, 0x04 },
+{ 0x00, 0x05, 0x05 },
+{ 0x00, 0x06, 0x06 },
+{ 0x00, 0x07, 0x07 },
+{ 0x00, 0x08, 0x08 },
+{ 0x00, 0x09, 0x09 },
+{ 0x00, 0x0a, 0x0a },
+{ 0x00, 0x0b, 0x0b },
+{ 0x00, 0x0c, 0x0c },
+{ 0x00, 0x0d, 0x0d },
+{ 0x00, 0x0e, 0x0e },
+{ 0x00, 0x0f, 0x0f },
+{ 0x00, 0x10, 0x10 },
+{ 0x00, 0x11, 0x11 },
+{ 0x00, 0x12, 0x12 },
+{ 0x00, 0x13, 0x13 },
+{ 0x00, 0x14, 0x14 },
+{ 0x00, 0x15, 0x15 },
+{ 0x00, 0x16, 0x16 },
+{ 0x00, 0x17, 0x17 },
+{ 0x00, 0x18, 0x18 },
+{ 0x00, 0x19, 0x19 },
+{ 0x00, 0x1a, 0x1a },
+{ 0x00, 0x1b, 0x1b },
+{ 0x00, 0x1c, 0x1c },
+{ 0x00, 0x1d, 0x1d },
+{ 0x00, 0x1e, 0x1e },
+{ 0x00, 0x1f, 0x1f },
+{ 0x00, 0x20, 0x20 },
+{ 0x00, 0x21, 0x21 },
+{ 0x00, 0x22, 0x22 },
+{ 0x00, 0x23, 0x23 },
+{ 0x00, 0x24, 0x24 },
+{ 0x00, 0x25, 0x25 },
+{ 0x00, 0x26, 0x26 },
+{ 0x00, 0x27, 0x27 },
+{ 0x00, 0x28, 0x28 },
+{ 0x00, 0x29, 0x29 },
+{ 0x00, 0x2a, 0x2a },
+{ 0x00, 0x2b, 0x2b },
+{ 0x00, 0x2c, 0x2c },
+{ 0x00, 0x2d, 0x2d },
+{ 0x00, 0x2e, 0x2e },
+{ 0x00, 0x2f, 0x2f },
+{ 0x00, 0x30, 0x30 },
+{ 0x00, 0x31, 0x31 },
+{ 0x00, 0x32, 0x32 },
+{ 0x00, 0x33, 0x33 },
+{ 0x00, 0x34, 0x34 },
+{ 0x00, 0x35, 0x35 },
+{ 0x00, 0x36, 0x36 },
+{ 0x00, 0x37, 0x37 },
+{ 0x00, 0x38, 0x38 },
+{ 0x00, 0x39, 0x39 },
+{ 0x00, 0x3a, 0x3a },
+{ 0x00, 0x3b, 0x3b },
+{ 0x00, 0x3c, 0x3c },
+{ 0x00, 0x3d, 0x3d },
+{ 0x00, 0x3e, 0x3e },
+{ 0x00, 0x3f, 0x3f },
+{ 0x00, 0x40, 0x40 },
+{ 0x01, 0x61, 0x41 },
+{ 0x01, 0x62, 0x42 },
+{ 0x01, 0x63, 0x43 },
+{ 0x01, 0x64, 0x44 },
+{ 0x01, 0x65, 0x45 },
+{ 0x01, 0x66, 0x46 },
+{ 0x01, 0x67, 0x47 },
+{ 0x01, 0x68, 0x48 },
+{ 0x01, 0x69, 0x49 },
+{ 0x01, 0x6a, 0x4a },
+{ 0x01, 0x6b, 0x4b },
+{ 0x01, 0x6c, 0x4c },
+{ 0x01, 0x6d, 0x4d },
+{ 0x01, 0x6e, 0x4e },
+{ 0x01, 0x6f, 0x4f },
+{ 0x01, 0x70, 0x50 },
+{ 0x01, 0x71, 0x51 },
+{ 0x01, 0x72, 0x52 },
+{ 0x01, 0x73, 0x53 },
+{ 0x01, 0x74, 0x54 },
+{ 0x01, 0x75, 0x55 },
+{ 0x01, 0x76, 0x56 },
+{ 0x01, 0x77, 0x57 },
+{ 0x01, 0x78, 0x58 },
+{ 0x01, 0x79, 0x59 },
+{ 0x01, 0x7a, 0x5a },
+{ 0x00, 0x5b, 0x5b },
+{ 0x00, 0x5c, 0x5c },
+{ 0x00, 0x5d, 0x5d },
+{ 0x00, 0x5e, 0x5e },
+{ 0x00, 0x5f, 0x5f },
+{ 0x00, 0x60, 0x60 },
+{ 0x00, 0x61, 0x41 },
+{ 0x00, 0x62, 0x42 },
+{ 0x00, 0x63, 0x43 },
+{ 0x00, 0x64, 0x44 },
+{ 0x00, 0x65, 0x45 },
+{ 0x00, 0x66, 0x46 },
+{ 0x00, 0x67, 0x47 },
+{ 0x00, 0x68, 0x48 },
+{ 0x00, 0x69, 0x49 },
+{ 0x00, 0x6a, 0x4a },
+{ 0x00, 0x6b, 0x4b },
+{ 0x00, 0x6c, 0x4c },
+{ 0x00, 0x6d, 0x4d },
+{ 0x00, 0x6e, 0x4e },
+{ 0x00, 0x6f, 0x4f },
+{ 0x00, 0x70, 0x50 },
+{ 0x00, 0x71, 0x51 },
+{ 0x00, 0x72, 0x52 },
+{ 0x00, 0x73, 0x53 },
+{ 0x00, 0x74, 0x54 },
+{ 0x00, 0x75, 0x55 },
+{ 0x00, 0x76, 0x56 },
+{ 0x00, 0x77, 0x57 },
+{ 0x00, 0x78, 0x58 },
+{ 0x00, 0x79, 0x59 },
+{ 0x00, 0x7a, 0x5a },
+{ 0x00, 0x7b, 0x7b },
+{ 0x00, 0x7c, 0x7c },
+{ 0x00, 0x7d, 0x7d },
+{ 0x00, 0x7e, 0x7e },
+{ 0x00, 0x7f, 0x7f },
+{ 0x00, 0x80, 0x80 },
+{ 0x00, 0x81, 0x81 },
+{ 0x00, 0x82, 0x82 },
+{ 0x00, 0x83, 0x83 },
+{ 0x00, 0x84, 0x84 },
+{ 0x00, 0x85, 0x85 },
+{ 0x00, 0x86, 0x86 },
+{ 0x00, 0x87, 0x87 },
+{ 0x00, 0x88, 0x88 },
+{ 0x00, 0x89, 0x89 },
+{ 0x00, 0x8a, 0x8a },
+{ 0x00, 0x8b, 0x8b },
+{ 0x00, 0x8c, 0x8c },
+{ 0x00, 0x8d, 0x8d },
+{ 0x00, 0x8e, 0x8e },
+{ 0x00, 0x8f, 0x8f },
+{ 0x00, 0x90, 0x90 },
+{ 0x00, 0x91, 0x91 },
+{ 0x00, 0x92, 0x92 },
+{ 0x00, 0x93, 0x93 },
+{ 0x00, 0x94, 0x94 },
+{ 0x00, 0x95, 0x95 },
+{ 0x00, 0x96, 0x96 },
+{ 0x00, 0x97, 0x97 },
+{ 0x00, 0x98, 0x98 },
+{ 0x00, 0x99, 0x99 },
+{ 0x00, 0x9a, 0x9a },
+{ 0x00, 0x9b, 0x9b },
+{ 0x00, 0x9c, 0x9c },
+{ 0x00, 0x9d, 0x9d },
+{ 0x00, 0x9e, 0x9e },
+{ 0x00, 0x9f, 0x9f },
+{ 0x00, 0xa0, 0xa0 },
+{ 0x00, 0xa1, 0xa1 },
+{ 0x00, 0xa2, 0xa2 },
+{ 0x00, 0xa3, 0xa3 },
+{ 0x00, 0xa4, 0xa4 },
+{ 0x00, 0xa5, 0xa5 },
+{ 0x00, 0xa6, 0xa6 },
+{ 0x00, 0xa7, 0xa7 },
+{ 0x00, 0xa8, 0xa8 },
+{ 0x00, 0xa9, 0xa9 },
+{ 0x00, 0xaa, 0xaa },
+{ 0x00, 0xab, 0xab },
+{ 0x00, 0xac, 0xac },
+{ 0x00, 0xad, 0xad },
+{ 0x00, 0xae, 0xae },
+{ 0x00, 0xaf, 0xaf },
+{ 0x00, 0xb0, 0xb0 },
+{ 0x00, 0xb1, 0xb1 },
+{ 0x00, 0xb2, 0xb2 },
+{ 0x00, 0xb3, 0xb3 },
+{ 0x00, 0xb4, 0xb4 },
+{ 0x00, 0xb5, 0xb5 },
+{ 0x00, 0xb6, 0xb6 },
+{ 0x00, 0xb7, 0xb7 },
+{ 0x00, 0xb8, 0xb8 },
+{ 0x00, 0xb9, 0xb9 },
+{ 0x00, 0xba, 0xba },
+{ 0x00, 0xbb, 0xbb },
+{ 0x00, 0xbc, 0xbc },
+{ 0x00, 0xbd, 0xbd },
+{ 0x00, 0xbe, 0xbe },
+{ 0x00, 0xbf, 0xbf },
+{ 0x00, 0xc0, 0xc0 },
+{ 0x00, 0xc1, 0xc1 },
+{ 0x00, 0xc2, 0xc2 },
+{ 0x00, 0xc3, 0xc3 },
+{ 0x00, 0xc4, 0xc4 },
+{ 0x00, 0xc5, 0xc5 },
+{ 0x00, 0xc6, 0xc6 },
+{ 0x00, 0xc7, 0xc7 },
+{ 0x00, 0xc8, 0xc8 },
+{ 0x00, 0xc9, 0xc9 },
+{ 0x00, 0xca, 0xca },
+{ 0x00, 0xcb, 0xcb },
+{ 0x00, 0xcc, 0xcc },
+{ 0x00, 0xcd, 0xcd },
+{ 0x00, 0xce, 0xce },
+{ 0x00, 0xcf, 0xcf },
+{ 0x00, 0xd0, 0xd0 },
+{ 0x00, 0xd1, 0xd1 },
+{ 0x00, 0xd2, 0xd2 },
+{ 0x00, 0xd3, 0xd3 },
+{ 0x00, 0xd4, 0xd4 },
+{ 0x00, 0xd5, 0xd5 },
+{ 0x00, 0xd6, 0xd6 },
+{ 0x00, 0xd7, 0xd7 },
+{ 0x00, 0xd8, 0xd8 },
+{ 0x00, 0xd9, 0xd9 },
+{ 0x00, 0xda, 0xda },
+{ 0x00, 0xdb, 0xdb },
+{ 0x00, 0xdc, 0xdc },
+{ 0x00, 0xdd, 0xdd },
+{ 0x00, 0xde, 0xde },
+{ 0x00, 0xdf, 0xdf },
+{ 0x00, 0xe0, 0xe0 },
+{ 0x00, 0xe1, 0xe1 },
+{ 0x00, 0xe2, 0xe2 },
+{ 0x00, 0xe3, 0xe3 },
+{ 0x00, 0xe4, 0xe4 },
+{ 0x00, 0xe5, 0xe5 },
+{ 0x00, 0xe6, 0xe6 },
+{ 0x00, 0xe7, 0xe7 },
+{ 0x00, 0xe8, 0xe8 },
+{ 0x00, 0xe9, 0xe9 },
+{ 0x00, 0xea, 0xea },
+{ 0x00, 0xeb, 0xeb },
+{ 0x00, 0xec, 0xec },
+{ 0x00, 0xed, 0xed },
+{ 0x00, 0xee, 0xee },
+{ 0x00, 0xef, 0xef },
+{ 0x00, 0xf0, 0xf0 },
+{ 0x00, 0xf1, 0xf1 },
+{ 0x00, 0xf2, 0xf2 },
+{ 0x00, 0xf3, 0xf3 },
+{ 0x00, 0xf4, 0xf4 },
+{ 0x00, 0xf5, 0xf5 },
+{ 0x00, 0xf6, 0xf6 },
+{ 0x00, 0xf7, 0xf7 },
+{ 0x00, 0xf8, 0xf8 },
+{ 0x00, 0xf9, 0xf9 },
+{ 0x00, 0xfa, 0xfa },
+{ 0x00, 0xfb, 0xfb },
+{ 0x00, 0xfc, 0xfc },
+{ 0x00, 0xfd, 0xfd },
+{ 0x00, 0xfe, 0xfe },
+{ 0x00, 0xff, 0xff }
+};
+
+static struct cs_info iso9_tbl[] = {
+{ 0x00, 0x00, 0x00 },
+{ 0x00, 0x01, 0x01 },
+{ 0x00, 0x02, 0x02 },
+{ 0x00, 0x03, 0x03 },
+{ 0x00, 0x04, 0x04 },
+{ 0x00, 0x05, 0x05 },
+{ 0x00, 0x06, 0x06 },
+{ 0x00, 0x07, 0x07 },
+{ 0x00, 0x08, 0x08 },
+{ 0x00, 0x09, 0x09 },
+{ 0x00, 0x0a, 0x0a },
+{ 0x00, 0x0b, 0x0b },
+{ 0x00, 0x0c, 0x0c },
+{ 0x00, 0x0d, 0x0d },
+{ 0x00, 0x0e, 0x0e },
+{ 0x00, 0x0f, 0x0f },
+{ 0x00, 0x10, 0x10 },
+{ 0x00, 0x11, 0x11 },
+{ 0x00, 0x12, 0x12 },
+{ 0x00, 0x13, 0x13 },
+{ 0x00, 0x14, 0x14 },
+{ 0x00, 0x15, 0x15 },
+{ 0x00, 0x16, 0x16 },
+{ 0x00, 0x17, 0x17 },
+{ 0x00, 0x18, 0x18 },
+{ 0x00, 0x19, 0x19 },
+{ 0x00, 0x1a, 0x1a },
+{ 0x00, 0x1b, 0x1b },
+{ 0x00, 0x1c, 0x1c },
+{ 0x00, 0x1d, 0x1d },
+{ 0x00, 0x1e, 0x1e },
+{ 0x00, 0x1f, 0x1f },
+{ 0x00, 0x20, 0x20 },
+{ 0x00, 0x21, 0x21 },
+{ 0x00, 0x22, 0x22 },
+{ 0x00, 0x23, 0x23 },
+{ 0x00, 0x24, 0x24 },
+{ 0x00, 0x25, 0x25 },
+{ 0x00, 0x26, 0x26 },
+{ 0x00, 0x27, 0x27 },
+{ 0x00, 0x28, 0x28 },
+{ 0x00, 0x29, 0x29 },
+{ 0x00, 0x2a, 0x2a },
+{ 0x00, 0x2b, 0x2b },
+{ 0x00, 0x2c, 0x2c },
+{ 0x00, 0x2d, 0x2d },
+{ 0x00, 0x2e, 0x2e },
+{ 0x00, 0x2f, 0x2f },
+{ 0x00, 0x30, 0x30 },
+{ 0x00, 0x31, 0x31 },
+{ 0x00, 0x32, 0x32 },
+{ 0x00, 0x33, 0x33 },
+{ 0x00, 0x34, 0x34 },
+{ 0x00, 0x35, 0x35 },
+{ 0x00, 0x36, 0x36 },
+{ 0x00, 0x37, 0x37 },
+{ 0x00, 0x38, 0x38 },
+{ 0x00, 0x39, 0x39 },
+{ 0x00, 0x3a, 0x3a },
+{ 0x00, 0x3b, 0x3b },
+{ 0x00, 0x3c, 0x3c },
+{ 0x00, 0x3d, 0x3d },
+{ 0x00, 0x3e, 0x3e },
+{ 0x00, 0x3f, 0x3f },
+{ 0x00, 0x40, 0x40 },
+{ 0x01, 0x61, 0x41 },
+{ 0x01, 0x62, 0x42 },
+{ 0x01, 0x63, 0x43 },
+{ 0x01, 0x64, 0x44 },
+{ 0x01, 0x65, 0x45 },
+{ 0x01, 0x66, 0x46 },
+{ 0x01, 0x67, 0x47 },
+{ 0x01, 0x68, 0x48 },
+{ 0x01, 0xfd, 0x49 },
+{ 0x01, 0x6a, 0x4a },
+{ 0x01, 0x6b, 0x4b },
+{ 0x01, 0x6c, 0x4c },
+{ 0x01, 0x6d, 0x4d },
+{ 0x01, 0x6e, 0x4e },
+{ 0x01, 0x6f, 0x4f },
+{ 0x01, 0x70, 0x50 },
+{ 0x01, 0x71, 0x51 },
+{ 0x01, 0x72, 0x52 },
+{ 0x01, 0x73, 0x53 },
+{ 0x01, 0x74, 0x54 },
+{ 0x01, 0x75, 0x55 },
+{ 0x01, 0x76, 0x56 },
+{ 0x01, 0x77, 0x57 },
+{ 0x01, 0x78, 0x58 },
+{ 0x01, 0x79, 0x59 },
+{ 0x01, 0x7a, 0x5a },
+{ 0x00, 0x5b, 0x5b },
+{ 0x00, 0x5c, 0x5c },
+{ 0x00, 0x5d, 0x5d },
+{ 0x00, 0x5e, 0x5e },
+{ 0x00, 0x5f, 0x5f },
+{ 0x00, 0x60, 0x60 },
+{ 0x00, 0x61, 0x41 },
+{ 0x00, 0x62, 0x42 },
+{ 0x00, 0x63, 0x43 },
+{ 0x00, 0x64, 0x44 },
+{ 0x00, 0x65, 0x45 },
+{ 0x00, 0x66, 0x46 },
+{ 0x00, 0x67, 0x47 },
+{ 0x00, 0x68, 0x48 },
+{ 0x00, 0x69, 0xdd },
+{ 0x00, 0x6a, 0x4a },
+{ 0x00, 0x6b, 0x4b },
+{ 0x00, 0x6c, 0x4c },
+{ 0x00, 0x6d, 0x4d },
+{ 0x00, 0x6e, 0x4e },
+{ 0x00, 0x6f, 0x4f },
+{ 0x00, 0x70, 0x50 },
+{ 0x00, 0x71, 0x51 },
+{ 0x00, 0x72, 0x52 },
+{ 0x00, 0x73, 0x53 },
+{ 0x00, 0x74, 0x54 },
+{ 0x00, 0x75, 0x55 },
+{ 0x00, 0x76, 0x56 },
+{ 0x00, 0x77, 0x57 },
+{ 0x00, 0x78, 0x58 },
+{ 0x00, 0x79, 0x59 },
+{ 0x00, 0x7a, 0x5a },
+{ 0x00, 0x7b, 0x7b },
+{ 0x00, 0x7c, 0x7c },
+{ 0x00, 0x7d, 0x7d },
+{ 0x00, 0x7e, 0x7e },
+{ 0x00, 0x7f, 0x7f },
+{ 0x00, 0x80, 0x80 },
+{ 0x00, 0x81, 0x81 },
+{ 0x00, 0x82, 0x82 },
+{ 0x00, 0x83, 0x83 },
+{ 0x00, 0x84, 0x84 },
+{ 0x00, 0x85, 0x85 },
+{ 0x00, 0x86, 0x86 },
+{ 0x00, 0x87, 0x87 },
+{ 0x00, 0x88, 0x88 },
+{ 0x00, 0x89, 0x89 },
+{ 0x00, 0x8a, 0x8a },
+{ 0x00, 0x8b, 0x8b },
+{ 0x00, 0x8c, 0x8c },
+{ 0x00, 0x8d, 0x8d },
+{ 0x00, 0x8e, 0x8e },
+{ 0x00, 0x8f, 0x8f },
+{ 0x00, 0x90, 0x90 },
+{ 0x00, 0x91, 0x91 },
+{ 0x00, 0x92, 0x92 },
+{ 0x00, 0x93, 0x93 },
+{ 0x00, 0x94, 0x94 },
+{ 0x00, 0x95, 0x95 },
+{ 0x00, 0x96, 0x96 },
+{ 0x00, 0x97, 0x97 },
+{ 0x00, 0x98, 0x98 },
+{ 0x00, 0x99, 0x99 },
+{ 0x00, 0x9a, 0x9a },
+{ 0x00, 0x9b, 0x9b },
+{ 0x00, 0x9c, 0x9c },
+{ 0x00, 0x9d, 0x9d },
+{ 0x00, 0x9e, 0x9e },
+{ 0x00, 0x9f, 0x9f },
+{ 0x00, 0xa0, 0xa0 },
+{ 0x00, 0xa1, 0xa1 },
+{ 0x00, 0xa2, 0xa2 },
+{ 0x00, 0xa3, 0xa3 },
+{ 0x00, 0xa4, 0xa4 },
+{ 0x00, 0xa5, 0xa5 },
+{ 0x00, 0xa6, 0xa6 },
+{ 0x00, 0xa7, 0xa7 },
+{ 0x00, 0xa8, 0xa8 },
+{ 0x00, 0xa9, 0xa9 },
+{ 0x00, 0xaa, 0xaa },
+{ 0x00, 0xab, 0xab },
+{ 0x00, 0xac, 0xac },
+{ 0x00, 0xad, 0xad },
+{ 0x00, 0xae, 0xae },
+{ 0x00, 0xaf, 0xaf },
+{ 0x00, 0xb0, 0xb0 },
+{ 0x00, 0xb1, 0xb1 },
+{ 0x00, 0xb2, 0xb2 },
+{ 0x00, 0xb3, 0xb3 },
+{ 0x00, 0xb4, 0xb4 },
+{ 0x00, 0xb5, 0xb5 },
+{ 0x00, 0xb6, 0xb6 },
+{ 0x00, 0xb7, 0xb7 },
+{ 0x00, 0xb8, 0xb8 },
+{ 0x00, 0xb9, 0xb9 },
+{ 0x00, 0xba, 0xba },
+{ 0x00, 0xbb, 0xbb },
+{ 0x00, 0xbc, 0xbc },
+{ 0x00, 0xbd, 0xbd },
+{ 0x00, 0xbe, 0xbe },
+{ 0x00, 0xbf, 0xbf },
+{ 0x01, 0xe0, 0xc0 },
+{ 0x01, 0xe1, 0xc1 },
+{ 0x01, 0xe2, 0xc2 },
+{ 0x01, 0xe3, 0xc3 },
+{ 0x01, 0xe4, 0xc4 },
+{ 0x01, 0xe5, 0xc5 },
+{ 0x01, 0xe6, 0xc6 },
+{ 0x01, 0xe7, 0xc7 },
+{ 0x01, 0xe8, 0xc8 },
+{ 0x01, 0xe9, 0xc9 },
+{ 0x01, 0xea, 0xca },
+{ 0x01, 0xeb, 0xcb },
+{ 0x01, 0xec, 0xcc },
+{ 0x01, 0xed, 0xcd },
+{ 0x01, 0xee, 0xce },
+{ 0x01, 0xef, 0xcf },
+{ 0x01, 0xf0, 0xd0 },
+{ 0x01, 0xf1, 0xd1 },
+{ 0x01, 0xf2, 0xd2 },
+{ 0x01, 0xf3, 0xd3 },
+{ 0x01, 0xf4, 0xd4 },
+{ 0x01, 0xf5, 0xd5 },
+{ 0x01, 0xf6, 0xd6 },
+{ 0x00, 0xd7, 0xd7 },
+{ 0x01, 0xf8, 0xd8 },
+{ 0x01, 0xf9, 0xd9 },
+{ 0x01, 0xfa, 0xda },
+{ 0x01, 0xfb, 0xdb },
+{ 0x01, 0xfc, 0xdc },
+{ 0x01, 0x69, 0xdd },
+{ 0x01, 0xfe, 0xde },
+{ 0x00, 0xdf, 0xdf },
+{ 0x00, 0xe0, 0xc0 },
+{ 0x00, 0xe1, 0xc1 },
+{ 0x00, 0xe2, 0xc2 },
+{ 0x00, 0xe3, 0xc3 },
+{ 0x00, 0xe4, 0xc4 },
+{ 0x00, 0xe5, 0xc5 },
+{ 0x00, 0xe6, 0xc6 },
+{ 0x00, 0xe7, 0xc7 },
+{ 0x00, 0xe8, 0xc8 },
+{ 0x00, 0xe9, 0xc9 },
+{ 0x00, 0xea, 0xca },
+{ 0x00, 0xeb, 0xcb },
+{ 0x00, 0xec, 0xcc },
+{ 0x00, 0xed, 0xcd },
+{ 0x00, 0xee, 0xce },
+{ 0x00, 0xef, 0xcf },
+{ 0x00, 0xf0, 0xd0 },
+{ 0x00, 0xf1, 0xd1 },
+{ 0x00, 0xf2, 0xd2 },
+{ 0x00, 0xf3, 0xd3 },
+{ 0x00, 0xf4, 0xd4 },
+{ 0x00, 0xf5, 0xd5 },
+{ 0x00, 0xf6, 0xd6 },
+{ 0x00, 0xf7, 0xf7 },
+{ 0x00, 0xf8, 0xd8 },
+{ 0x00, 0xf9, 0xd9 },
+{ 0x00, 0xfa, 0xda },
+{ 0x00, 0xfb, 0xdb },
+{ 0x00, 0xfc, 0xdc },
+{ 0x00, 0xfd, 0x49 },
+{ 0x00, 0xfe, 0xde },
+{ 0x00, 0xff, 0xff }
+};
+
+static struct cs_info iso10_tbl[] = {
+{ 0x00, 0x00, 0x00 },
+{ 0x00, 0x01, 0x01 },
+{ 0x00, 0x02, 0x02 },
+{ 0x00, 0x03, 0x03 },
+{ 0x00, 0x04, 0x04 },
+{ 0x00, 0x05, 0x05 },
+{ 0x00, 0x06, 0x06 },
+{ 0x00, 0x07, 0x07 },
+{ 0x00, 0x08, 0x08 },
+{ 0x00, 0x09, 0x09 },
+{ 0x00, 0x0a, 0x0a },
+{ 0x00, 0x0b, 0x0b },
+{ 0x00, 0x0c, 0x0c },
+{ 0x00, 0x0d, 0x0d },
+{ 0x00, 0x0e, 0x0e },
+{ 0x00, 0x0f, 0x0f },
+{ 0x00, 0x10, 0x10 },
+{ 0x00, 0x11, 0x11 },
+{ 0x00, 0x12, 0x12 },
+{ 0x00, 0x13, 0x13 },
+{ 0x00, 0x14, 0x14 },
+{ 0x00, 0x15, 0x15 },
+{ 0x00, 0x16, 0x16 },
+{ 0x00, 0x17, 0x17 },
+{ 0x00, 0x18, 0x18 },
+{ 0x00, 0x19, 0x19 },
+{ 0x00, 0x1a, 0x1a },
+{ 0x00, 0x1b, 0x1b },
+{ 0x00, 0x1c, 0x1c },
+{ 0x00, 0x1d, 0x1d },
+{ 0x00, 0x1e, 0x1e },
+{ 0x00, 0x1f, 0x1f },
+{ 0x00, 0x20, 0x20 },
+{ 0x00, 0x21, 0x21 },
+{ 0x00, 0x22, 0x22 },
+{ 0x00, 0x23, 0x23 },
+{ 0x00, 0x24, 0x24 },
+{ 0x00, 0x25, 0x25 },
+{ 0x00, 0x26, 0x26 },
+{ 0x00, 0x27, 0x27 },
+{ 0x00, 0x28, 0x28 },
+{ 0x00, 0x29, 0x29 },
+{ 0x00, 0x2a, 0x2a },
+{ 0x00, 0x2b, 0x2b },
+{ 0x00, 0x2c, 0x2c },
+{ 0x00, 0x2d, 0x2d },
+{ 0x00, 0x2e, 0x2e },
+{ 0x00, 0x2f, 0x2f },
+{ 0x00, 0x30, 0x30 },
+{ 0x00, 0x31, 0x31 },
+{ 0x00, 0x32, 0x32 },
+{ 0x00, 0x33, 0x33 },
+{ 0x00, 0x34, 0x34 },
+{ 0x00, 0x35, 0x35 },
+{ 0x00, 0x36, 0x36 },
+{ 0x00, 0x37, 0x37 },
+{ 0x00, 0x38, 0x38 },
+{ 0x00, 0x39, 0x39 },
+{ 0x00, 0x3a, 0x3a },
+{ 0x00, 0x3b, 0x3b },
+{ 0x00, 0x3c, 0x3c },
+{ 0x00, 0x3d, 0x3d },
+{ 0x00, 0x3e, 0x3e },
+{ 0x00, 0x3f, 0x3f },
+{ 0x00, 0x40, 0x40 },
+{ 0x01, 0x61, 0x41 },
+{ 0x01, 0x62, 0x42 },
+{ 0x01, 0x63, 0x43 },
+{ 0x01, 0x64, 0x44 },
+{ 0x01, 0x65, 0x45 },
+{ 0x01, 0x66, 0x46 },
+{ 0x01, 0x67, 0x47 },
+{ 0x01, 0x68, 0x48 },
+{ 0x01, 0x69, 0x49 },
+{ 0x01, 0x6a, 0x4a },
+{ 0x01, 0x6b, 0x4b },
+{ 0x01, 0x6c, 0x4c },
+{ 0x01, 0x6d, 0x4d },
+{ 0x01, 0x6e, 0x4e },
+{ 0x01, 0x6f, 0x4f },
+{ 0x01, 0x70, 0x50 },
+{ 0x01, 0x71, 0x51 },
+{ 0x01, 0x72, 0x52 },
+{ 0x01, 0x73, 0x53 },
+{ 0x01, 0x74, 0x54 },
+{ 0x01, 0x75, 0x55 },
+{ 0x01, 0x76, 0x56 },
+{ 0x01, 0x77, 0x57 },
+{ 0x01, 0x78, 0x58 },
+{ 0x01, 0x79, 0x59 },
+{ 0x01, 0x7a, 0x5a },
+{ 0x00, 0x5b, 0x5b },
+{ 0x00, 0x5c, 0x5c },
+{ 0x00, 0x5d, 0x5d },
+{ 0x00, 0x5e, 0x5e },
+{ 0x00, 0x5f, 0x5f },
+{ 0x00, 0x60, 0x60 },
+{ 0x00, 0x61, 0x41 },
+{ 0x00, 0x62, 0x42 },
+{ 0x00, 0x63, 0x43 },
+{ 0x00, 0x64, 0x44 },
+{ 0x00, 0x65, 0x45 },
+{ 0x00, 0x66, 0x46 },
+{ 0x00, 0x67, 0x47 },
+{ 0x00, 0x68, 0x48 },
+{ 0x00, 0x69, 0x49 },
+{ 0x00, 0x6a, 0x4a },
+{ 0x00, 0x6b, 0x4b },
+{ 0x00, 0x6c, 0x4c },
+{ 0x00, 0x6d, 0x4d },
+{ 0x00, 0x6e, 0x4e },
+{ 0x00, 0x6f, 0x4f },
+{ 0x00, 0x70, 0x50 },
+{ 0x00, 0x71, 0x51 },
+{ 0x00, 0x72, 0x52 },
+{ 0x00, 0x73, 0x53 },
+{ 0x00, 0x74, 0x54 },
+{ 0x00, 0x75, 0x55 },
+{ 0x00, 0x76, 0x56 },
+{ 0x00, 0x77, 0x57 },
+{ 0x00, 0x78, 0x58 },
+{ 0x00, 0x79, 0x59 },
+{ 0x00, 0x7a, 0x5a },
+{ 0x00, 0x7b, 0x7b },
+{ 0x00, 0x7c, 0x7c },
+{ 0x00, 0x7d, 0x7d },
+{ 0x00, 0x7e, 0x7e },
+{ 0x00, 0x7f, 0x7f },
+{ 0x00, 0x80, 0x80 },
+{ 0x00, 0x81, 0x81 },
+{ 0x00, 0x82, 0x82 },
+{ 0x00, 0x83, 0x83 },
+{ 0x00, 0x84, 0x84 },
+{ 0x00, 0x85, 0x85 },
+{ 0x00, 0x86, 0x86 },
+{ 0x00, 0x87, 0x87 },
+{ 0x00, 0x88, 0x88 },
+{ 0x00, 0x89, 0x89 },
+{ 0x00, 0x8a, 0x8a },
+{ 0x00, 0x8b, 0x8b },
+{ 0x00, 0x8c, 0x8c },
+{ 0x00, 0x8d, 0x8d },
+{ 0x00, 0x8e, 0x8e },
+{ 0x00, 0x8f, 0x8f },
+{ 0x00, 0x90, 0x90 },
+{ 0x00, 0x91, 0x91 },
+{ 0x00, 0x92, 0x92 },
+{ 0x00, 0x93, 0x93 },
+{ 0x00, 0x94, 0x94 },
+{ 0x00, 0x95, 0x95 },
+{ 0x00, 0x96, 0x96 },
+{ 0x00, 0x97, 0x97 },
+{ 0x00, 0x98, 0x98 },
+{ 0x00, 0x99, 0x99 },
+{ 0x00, 0x9a, 0x9a },
+{ 0x00, 0x9b, 0x9b },
+{ 0x00, 0x9c, 0x9c },
+{ 0x00, 0x9d, 0x9d },
+{ 0x00, 0x9e, 0x9e },
+{ 0x00, 0x9f, 0x9f },
+{ 0x00, 0xa0, 0xa0 },
+{ 0x00, 0xa1, 0xa1 },
+{ 0x00, 0xa2, 0xa2 },
+{ 0x00, 0xa3, 0xa3 },
+{ 0x00, 0xa4, 0xa4 },
+{ 0x00, 0xa5, 0xa5 },
+{ 0x00, 0xa6, 0xa6 },
+{ 0x00, 0xa7, 0xa7 },
+{ 0x00, 0xa8, 0xa8 },
+{ 0x00, 0xa9, 0xa9 },
+{ 0x00, 0xaa, 0xaa },
+{ 0x00, 0xab, 0xab },
+{ 0x00, 0xac, 0xac },
+{ 0x00, 0xad, 0xad },
+{ 0x00, 0xae, 0xae },
+{ 0x00, 0xaf, 0xaf },
+{ 0x00, 0xb0, 0xb0 },
+{ 0x00, 0xb1, 0xb1 },
+{ 0x00, 0xb2, 0xb2 },
+{ 0x00, 0xb3, 0xb3 },
+{ 0x00, 0xb4, 0xb4 },
+{ 0x00, 0xb5, 0xb5 },
+{ 0x00, 0xb6, 0xb6 },
+{ 0x00, 0xb7, 0xb7 },
+{ 0x00, 0xb8, 0xb8 },
+{ 0x00, 0xb9, 0xb9 },
+{ 0x00, 0xba, 0xba },
+{ 0x00, 0xbb, 0xbb },
+{ 0x00, 0xbc, 0xbc },
+{ 0x00, 0xbd, 0xbd },
+{ 0x00, 0xbe, 0xbe },
+{ 0x00, 0xbf, 0xbf },
+{ 0x00, 0xc0, 0xc0 },
+{ 0x00, 0xc1, 0xc1 },
+{ 0x00, 0xc2, 0xc2 },
+{ 0x00, 0xc3, 0xc3 },
+{ 0x00, 0xc4, 0xc4 },
+{ 0x00, 0xc5, 0xc5 },
+{ 0x00, 0xc6, 0xc6 },
+{ 0x00, 0xc7, 0xc7 },
+{ 0x00, 0xc8, 0xc8 },
+{ 0x00, 0xc9, 0xc9 },
+{ 0x00, 0xca, 0xca },
+{ 0x00, 0xcb, 0xcb },
+{ 0x00, 0xcc, 0xcc },
+{ 0x00, 0xcd, 0xcd },
+{ 0x00, 0xce, 0xce },
+{ 0x00, 0xcf, 0xcf },
+{ 0x00, 0xd0, 0xd0 },
+{ 0x00, 0xd1, 0xd1 },
+{ 0x00, 0xd2, 0xd2 },
+{ 0x00, 0xd3, 0xd3 },
+{ 0x00, 0xd4, 0xd4 },
+{ 0x00, 0xd5, 0xd5 },
+{ 0x00, 0xd6, 0xd6 },
+{ 0x00, 0xd7, 0xd7 },
+{ 0x00, 0xd8, 0xd8 },
+{ 0x00, 0xd9, 0xd9 },
+{ 0x00, 0xda, 0xda },
+{ 0x00, 0xdb, 0xdb },
+{ 0x00, 0xdc, 0xdc },
+{ 0x00, 0xdd, 0xdd },
+{ 0x00, 0xde, 0xde },
+{ 0x00, 0xdf, 0xdf },
+{ 0x00, 0xe0, 0xe0 },
+{ 0x00, 0xe1, 0xe1 },
+{ 0x00, 0xe2, 0xe2 },
+{ 0x00, 0xe3, 0xe3 },
+{ 0x00, 0xe4, 0xe4 },
+{ 0x00, 0xe5, 0xe5 },
+{ 0x00, 0xe6, 0xe6 },
+{ 0x00, 0xe7, 0xe7 },
+{ 0x00, 0xe8, 0xe8 },
+{ 0x00, 0xe9, 0xe9 },
+{ 0x00, 0xea, 0xea },
+{ 0x00, 0xeb, 0xeb },
+{ 0x00, 0xec, 0xec },
+{ 0x00, 0xed, 0xed },
+{ 0x00, 0xee, 0xee },
+{ 0x00, 0xef, 0xef },
+{ 0x00, 0xf0, 0xf0 },
+{ 0x00, 0xf1, 0xf1 },
+{ 0x00, 0xf2, 0xf2 },
+{ 0x00, 0xf3, 0xf3 },
+{ 0x00, 0xf4, 0xf4 },
+{ 0x00, 0xf5, 0xf5 },
+{ 0x00, 0xf6, 0xf6 },
+{ 0x00, 0xf7, 0xf7 },
+{ 0x00, 0xf8, 0xf8 },
+{ 0x00, 0xf9, 0xf9 },
+{ 0x00, 0xfa, 0xfa },
+{ 0x00, 0xfb, 0xfb },
+{ 0x00, 0xfc, 0xfc },
+{ 0x00, 0xfd, 0xfd },
+{ 0x00, 0xfe, 0xfe },
+{ 0x00, 0xff, 0xff }
+};
+
+static struct cs_info koi8r_tbl[] = {
+{ 0x00, 0x00, 0x00 },
+{ 0x00, 0x01, 0x01 },
+{ 0x00, 0x02, 0x02 },
+{ 0x00, 0x03, 0x03 },
+{ 0x00, 0x04, 0x04 },
+{ 0x00, 0x05, 0x05 },
+{ 0x00, 0x06, 0x06 },
+{ 0x00, 0x07, 0x07 },
+{ 0x00, 0x08, 0x08 },
+{ 0x00, 0x09, 0x09 },
+{ 0x00, 0x0a, 0x0a },
+{ 0x00, 0x0b, 0x0b },
+{ 0x00, 0x0c, 0x0c },
+{ 0x00, 0x0d, 0x0d },
+{ 0x00, 0x0e, 0x0e },
+{ 0x00, 0x0f, 0x0f },
+{ 0x00, 0x10, 0x10 },
+{ 0x00, 0x11, 0x11 },
+{ 0x00, 0x12, 0x12 },
+{ 0x00, 0x13, 0x13 },
+{ 0x00, 0x14, 0x14 },
+{ 0x00, 0x15, 0x15 },
+{ 0x00, 0x16, 0x16 },
+{ 0x00, 0x17, 0x17 },
+{ 0x00, 0x18, 0x18 },
+{ 0x00, 0x19, 0x19 },
+{ 0x00, 0x1a, 0x1a },
+{ 0x00, 0x1b, 0x1b },
+{ 0x00, 0x1c, 0x1c },
+{ 0x00, 0x1d, 0x1d },
+{ 0x00, 0x1e, 0x1e },
+{ 0x00, 0x1f, 0x1f },
+{ 0x00, 0x20, 0x20 },
+{ 0x00, 0x21, 0x21 },
+{ 0x00, 0x22, 0x22 },
+{ 0x00, 0x23, 0x23 },
+{ 0x00, 0x24, 0x24 },
+{ 0x00, 0x25, 0x25 },
+{ 0x00, 0x26, 0x26 },
+{ 0x00, 0x27, 0x27 },
+{ 0x00, 0x28, 0x28 },
+{ 0x00, 0x29, 0x29 },
+{ 0x00, 0x2a, 0x2a },
+{ 0x00, 0x2b, 0x2b },
+{ 0x00, 0x2c, 0x2c },
+{ 0x00, 0x2d, 0x2d },
+{ 0x00, 0x2e, 0x2e },
+{ 0x00, 0x2f, 0x2f },
+{ 0x00, 0x30, 0x30 },
+{ 0x00, 0x31, 0x31 },
+{ 0x00, 0x32, 0x32 },
+{ 0x00, 0x33, 0x33 },
+{ 0x00, 0x34, 0x34 },
+{ 0x00, 0x35, 0x35 },
+{ 0x00, 0x36, 0x36 },
+{ 0x00, 0x37, 0x37 },
+{ 0x00, 0x38, 0x38 },
+{ 0x00, 0x39, 0x39 },
+{ 0x00, 0x3a, 0x3a },
+{ 0x00, 0x3b, 0x3b },
+{ 0x00, 0x3c, 0x3c },
+{ 0x00, 0x3d, 0x3d },
+{ 0x00, 0x3e, 0x3e },
+{ 0x00, 0x3f, 0x3f },
+{ 0x00, 0x40, 0x40 },
+{ 0x01, 0x61, 0x41 },
+{ 0x01, 0x62, 0x42 },
+{ 0x01, 0x63, 0x43 },
+{ 0x01, 0x64, 0x44 },
+{ 0x01, 0x65, 0x45 },
+{ 0x01, 0x66, 0x46 },
+{ 0x01, 0x67, 0x47 },
+{ 0x01, 0x68, 0x48 },
+{ 0x01, 0x69, 0x49 },
+{ 0x01, 0x6a, 0x4a },
+{ 0x01, 0x6b, 0x4b },
+{ 0x01, 0x6c, 0x4c },
+{ 0x01, 0x6d, 0x4d },
+{ 0x01, 0x6e, 0x4e },
+{ 0x01, 0x6f, 0x4f },
+{ 0x01, 0x70, 0x50 },
+{ 0x01, 0x71, 0x51 },
+{ 0x01, 0x72, 0x52 },
+{ 0x01, 0x73, 0x53 },
+{ 0x01, 0x74, 0x54 },
+{ 0x01, 0x75, 0x55 },
+{ 0x01, 0x76, 0x56 },
+{ 0x01, 0x77, 0x57 },
+{ 0x01, 0x78, 0x58 },
+{ 0x01, 0x79, 0x59 },
+{ 0x01, 0x7a, 0x5a },
+{ 0x00, 0x5b, 0x5b },
+{ 0x00, 0x5c, 0x5c },
+{ 0x00, 0x5d, 0x5d },
+{ 0x00, 0x5e, 0x5e },
+{ 0x00, 0x5f, 0x5f },
+{ 0x00, 0x60, 0x60 },
+{ 0x00, 0x61, 0x41 },
+{ 0x00, 0x62, 0x42 },
+{ 0x00, 0x63, 0x43 },
+{ 0x00, 0x64, 0x44 },
+{ 0x00, 0x65, 0x45 },
+{ 0x00, 0x66, 0x46 },
+{ 0x00, 0x67, 0x47 },
+{ 0x00, 0x68, 0x48 },
+{ 0x00, 0x69, 0x49 },
+{ 0x00, 0x6a, 0x4a },
+{ 0x00, 0x6b, 0x4b },
+{ 0x00, 0x6c, 0x4c },
+{ 0x00, 0x6d, 0x4d },
+{ 0x00, 0x6e, 0x4e },
+{ 0x00, 0x6f, 0x4f },
+{ 0x00, 0x70, 0x50 },
+{ 0x00, 0x71, 0x51 },
+{ 0x00, 0x72, 0x52 },
+{ 0x00, 0x73, 0x53 },
+{ 0x00, 0x74, 0x54 },
+{ 0x00, 0x75, 0x55 },
+{ 0x00, 0x76, 0x56 },
+{ 0x00, 0x77, 0x57 },
+{ 0x00, 0x78, 0x58 },
+{ 0x00, 0x79, 0x59 },
+{ 0x00, 0x7a, 0x5a },
+{ 0x00, 0x7b, 0x7b },
+{ 0x00, 0x7c, 0x7c },
+{ 0x00, 0x7d, 0x7d },
+{ 0x00, 0x7e, 0x7e },
+{ 0x00, 0x7f, 0x7f },
+{ 0x00, 0x80, 0x80 },
+{ 0x00, 0x81, 0x81 },
+{ 0x00, 0x82, 0x82 },
+{ 0x00, 0x83, 0x83 },
+{ 0x00, 0x84, 0x84 },
+{ 0x00, 0x85, 0x85 },
+{ 0x00, 0x86, 0x86 },
+{ 0x00, 0x87, 0x87 },
+{ 0x00, 0x88, 0x88 },
+{ 0x00, 0x89, 0x89 },
+{ 0x00, 0x8a, 0x8a },
+{ 0x00, 0x8b, 0x8b },
+{ 0x00, 0x8c, 0x8c },
+{ 0x00, 0x8d, 0x8d },
+{ 0x00, 0x8e, 0x8e },
+{ 0x00, 0x8f, 0x8f },
+{ 0x00, 0x90, 0x90 },
+{ 0x00, 0x91, 0x91 },
+{ 0x00, 0x92, 0x92 },
+{ 0x00, 0x93, 0x93 },
+{ 0x00, 0x94, 0x94 },
+{ 0x00, 0x95, 0x95 },
+{ 0x00, 0x96, 0x96 },
+{ 0x00, 0x97, 0x97 },
+{ 0x00, 0x98, 0x98 },
+{ 0x00, 0x99, 0x99 },
+{ 0x00, 0x9a, 0x9a },
+{ 0x00, 0x9b, 0x9b },
+{ 0x00, 0x9c, 0x9c },
+{ 0x00, 0x9d, 0x9d },
+{ 0x00, 0x9e, 0x9e },
+{ 0x00, 0x9f, 0x9f },
+{ 0x00, 0xa0, 0xa0 },
+{ 0x00, 0xa1, 0xa1 },
+{ 0x00, 0xa2, 0xa2 },
+{ 0x00, 0xa3, 0xb3 },
+{ 0x00, 0xa4, 0xa4 },
+{ 0x00, 0xa5, 0xa5 },
+{ 0x00, 0xa6, 0xa6 },
+{ 0x00, 0xa7, 0xa7 },
+{ 0x00, 0xa8, 0xa8 },
+{ 0x00, 0xa9, 0xa9 },
+{ 0x00, 0xaa, 0xaa },
+{ 0x00, 0xab, 0xab },
+{ 0x00, 0xac, 0xac },
+{ 0x00, 0xad, 0xad },
+{ 0x00, 0xae, 0xae },
+{ 0x00, 0xaf, 0xaf },
+{ 0x00, 0xb0, 0xb0 },
+{ 0x00, 0xb1, 0xb1 },
+{ 0x00, 0xb2, 0xb2 },
+{ 0x01, 0xa3, 0xb3 },
+{ 0x00, 0xb4, 0xb4 },
+{ 0x00, 0xb5, 0xb5 },
+{ 0x00, 0xb6, 0xb6 },
+{ 0x00, 0xb7, 0xb7 },
+{ 0x00, 0xb8, 0xb8 },
+{ 0x00, 0xb9, 0xb9 },
+{ 0x00, 0xba, 0xba },
+{ 0x00, 0xbb, 0xbb },
+{ 0x00, 0xbc, 0xbc },
+{ 0x00, 0xbd, 0xbd },
+{ 0x00, 0xbe, 0xbe },
+{ 0x00, 0xbf, 0xbf },
+{ 0x00, 0xc0, 0xe0 },
+{ 0x00, 0xc1, 0xe1 },
+{ 0x00, 0xc2, 0xe2 },
+{ 0x00, 0xc3, 0xe3 },
+{ 0x00, 0xc4, 0xe4 },
+{ 0x00, 0xc5, 0xe5 },
+{ 0x00, 0xc6, 0xe6 },
+{ 0x00, 0xc7, 0xe7 },
+{ 0x00, 0xc8, 0xe8 },
+{ 0x00, 0xc9, 0xe9 },
+{ 0x00, 0xca, 0xea },
+{ 0x00, 0xcb, 0xeb },
+{ 0x00, 0xcc, 0xec },
+{ 0x00, 0xcd, 0xed },
+{ 0x00, 0xce, 0xee },
+{ 0x00, 0xcf, 0xef },
+{ 0x00, 0xd0, 0xf0 },
+{ 0x00, 0xd1, 0xf1 },
+{ 0x00, 0xd2, 0xf2 },
+{ 0x00, 0xd3, 0xf3 },
+{ 0x00, 0xd4, 0xf4 },
+{ 0x00, 0xd5, 0xf5 },
+{ 0x00, 0xd6, 0xf6 },
+{ 0x00, 0xd7, 0xf7 },
+{ 0x00, 0xd8, 0xf8 },
+{ 0x00, 0xd9, 0xf9 },
+{ 0x00, 0xda, 0xfa },
+{ 0x00, 0xdb, 0xfb },
+{ 0x00, 0xdc, 0xfc },
+{ 0x00, 0xdd, 0xfd },
+{ 0x00, 0xde, 0xfe },
+{ 0x00, 0xdf, 0xff },
+{ 0x01, 0xc0, 0xe0 },
+{ 0x01, 0xc1, 0xe1 },
+{ 0x01, 0xc2, 0xe2 },
+{ 0x01, 0xc3, 0xe3 },
+{ 0x01, 0xc4, 0xe4 },
+{ 0x01, 0xc5, 0xe5 },
+{ 0x01, 0xc6, 0xe6 },
+{ 0x01, 0xc7, 0xe7 },
+{ 0x01, 0xc8, 0xe8 },
+{ 0x01, 0xc9, 0xe9 },
+{ 0x01, 0xca, 0xea },
+{ 0x01, 0xcb, 0xeb },
+{ 0x01, 0xcc, 0xec },
+{ 0x01, 0xcd, 0xed },
+{ 0x01, 0xce, 0xee },
+{ 0x01, 0xcf, 0xef },
+{ 0x01, 0xd0, 0xf0 },
+{ 0x01, 0xd1, 0xf1 },
+{ 0x01, 0xd2, 0xf2 },
+{ 0x01, 0xd3, 0xf3 },
+{ 0x01, 0xd4, 0xf4 },
+{ 0x01, 0xd5, 0xf5 },
+{ 0x01, 0xd6, 0xf6 },
+{ 0x01, 0xd7, 0xf7 },
+{ 0x01, 0xd8, 0xf8 },
+{ 0x01, 0xd9, 0xf9 },
+{ 0x01, 0xda, 0xfa },
+{ 0x01, 0xdb, 0xfb },
+{ 0x01, 0xdc, 0xfc },
+{ 0x01, 0xdd, 0xfd },
+{ 0x01, 0xde, 0xfe },
+{ 0x01, 0xdf, 0xff }
+};
+
+static struct cs_info koi8u_tbl[] = {
+{ 0x00, 0x00, 0x00 },
+{ 0x00, 0x01, 0x01 },
+{ 0x00, 0x02, 0x02 },
+{ 0x00, 0x03, 0x03 },
+{ 0x00, 0x04, 0x04 },
+{ 0x00, 0x05, 0x05 },
+{ 0x00, 0x06, 0x06 },
+{ 0x00, 0x07, 0x07 },
+{ 0x00, 0x08, 0x08 },
+{ 0x00, 0x09, 0x09 },
+{ 0x00, 0x0a, 0x0a },
+{ 0x00, 0x0b, 0x0b },
+{ 0x00, 0x0c, 0x0c },
+{ 0x00, 0x0d, 0x0d },
+{ 0x00, 0x0e, 0x0e },
+{ 0x00, 0x0f, 0x0f },
+{ 0x00, 0x10, 0x10 },
+{ 0x00, 0x11, 0x11 },
+{ 0x00, 0x12, 0x12 },
+{ 0x00, 0x13, 0x13 },
+{ 0x00, 0x14, 0x14 },
+{ 0x00, 0x15, 0x15 },
+{ 0x00, 0x16, 0x16 },
+{ 0x00, 0x17, 0x17 },
+{ 0x00, 0x18, 0x18 },
+{ 0x00, 0x19, 0x19 },
+{ 0x00, 0x1a, 0x1a },
+{ 0x00, 0x1b, 0x1b },
+{ 0x00, 0x1c, 0x1c },
+{ 0x00, 0x1d, 0x1d },
+{ 0x00, 0x1e, 0x1e },
+{ 0x00, 0x1f, 0x1f },
+{ 0x00, 0x20, 0x20 },
+{ 0x00, 0x21, 0x21 },
+{ 0x00, 0x22, 0x22 },
+{ 0x00, 0x23, 0x23 },
+{ 0x00, 0x24, 0x24 },
+{ 0x00, 0x25, 0x25 },
+{ 0x00, 0x26, 0x26 },
+{ 0x00, 0x27, 0x27 },
+{ 0x00, 0x28, 0x28 },
+{ 0x00, 0x29, 0x29 },
+{ 0x00, 0x2a, 0x2a },
+{ 0x00, 0x2b, 0x2b },
+{ 0x00, 0x2c, 0x2c },
+{ 0x00, 0x2d, 0x2d },
+{ 0x00, 0x2e, 0x2e },
+{ 0x00, 0x2f, 0x2f },
+{ 0x00, 0x30, 0x30 },
+{ 0x00, 0x31, 0x31 },
+{ 0x00, 0x32, 0x32 },
+{ 0x00, 0x33, 0x33 },
+{ 0x00, 0x34, 0x34 },
+{ 0x00, 0x35, 0x35 },
+{ 0x00, 0x36, 0x36 },
+{ 0x00, 0x37, 0x37 },
+{ 0x00, 0x38, 0x38 },
+{ 0x00, 0x39, 0x39 },
+{ 0x00, 0x3a, 0x3a },
+{ 0x00, 0x3b, 0x3b },
+{ 0x00, 0x3c, 0x3c },
+{ 0x00, 0x3d, 0x3d },
+{ 0x00, 0x3e, 0x3e },
+{ 0x00, 0x3f, 0x3f },
+{ 0x00, 0x40, 0x40 },
+{ 0x01, 0x61, 0x41 },
+{ 0x01, 0x62, 0x42 },
+{ 0x01, 0x63, 0x43 },
+{ 0x01, 0x64, 0x44 },
+{ 0x01, 0x65, 0x45 },
+{ 0x01, 0x66, 0x46 },
+{ 0x01, 0x67, 0x47 },
+{ 0x01, 0x68, 0x48 },
+{ 0x01, 0x69, 0x49 },
+{ 0x01, 0x6a, 0x4a },
+{ 0x01, 0x6b, 0x4b },
+{ 0x01, 0x6c, 0x4c },
+{ 0x01, 0x6d, 0x4d },
+{ 0x01, 0x6e, 0x4e },
+{ 0x01, 0x6f, 0x4f },
+{ 0x01, 0x70, 0x50 },
+{ 0x01, 0x71, 0x51 },
+{ 0x01, 0x72, 0x52 },
+{ 0x01, 0x73, 0x53 },
+{ 0x01, 0x74, 0x54 },
+{ 0x01, 0x75, 0x55 },
+{ 0x01, 0x76, 0x56 },
+{ 0x01, 0x77, 0x57 },
+{ 0x01, 0x78, 0x58 },
+{ 0x01, 0x79, 0x59 },
+{ 0x01, 0x7a, 0x5a },
+{ 0x00, 0x5b, 0x5b },
+{ 0x00, 0x5c, 0x5c },
+{ 0x00, 0x5d, 0x5d },
+{ 0x00, 0x5e, 0x5e },
+{ 0x00, 0x5f, 0x5f },
+{ 0x00, 0x60, 0x60 },
+{ 0x00, 0x61, 0x41 },
+{ 0x00, 0x62, 0x42 },
+{ 0x00, 0x63, 0x43 },
+{ 0x00, 0x64, 0x44 },
+{ 0x00, 0x65, 0x45 },
+{ 0x00, 0x66, 0x46 },
+{ 0x00, 0x67, 0x47 },
+{ 0x00, 0x68, 0x48 },
+{ 0x00, 0x69, 0x49 },
+{ 0x00, 0x6a, 0x4a },
+{ 0x00, 0x6b, 0x4b },
+{ 0x00, 0x6c, 0x4c },
+{ 0x00, 0x6d, 0x4d },
+{ 0x00, 0x6e, 0x4e },
+{ 0x00, 0x6f, 0x4f },
+{ 0x00, 0x70, 0x50 },
+{ 0x00, 0x71, 0x51 },
+{ 0x00, 0x72, 0x52 },
+{ 0x00, 0x73, 0x53 },
+{ 0x00, 0x74, 0x54 },
+{ 0x00, 0x75, 0x55 },
+{ 0x00, 0x76, 0x56 },
+{ 0x00, 0x77, 0x57 },
+{ 0x00, 0x78, 0x58 },
+{ 0x00, 0x79, 0x59 },
+{ 0x00, 0x7a, 0x5a },
+{ 0x00, 0x7b, 0x7b },
+{ 0x00, 0x7c, 0x7c },
+{ 0x00, 0x7d, 0x7d },
+{ 0x00, 0x7e, 0x7e },
+{ 0x00, 0x7f, 0x7f },
+{ 0x00, 0x80, 0x80 },
+{ 0x00, 0x81, 0x81 },
+{ 0x00, 0x82, 0x82 },
+{ 0x00, 0x83, 0x83 },
+{ 0x00, 0x84, 0x84 },
+{ 0x00, 0x85, 0x85 },
+{ 0x00, 0x86, 0x86 },
+{ 0x00, 0x87, 0x87 },
+{ 0x00, 0x88, 0x88 },
+{ 0x00, 0x89, 0x89 },
+{ 0x00, 0x8a, 0x8a },
+{ 0x00, 0x8b, 0x8b },
+{ 0x00, 0x8c, 0x8c },
+{ 0x00, 0x8d, 0x8d },
+{ 0x00, 0x8e, 0x8e },
+{ 0x00, 0x8f, 0x8f },
+{ 0x00, 0x90, 0x90 },
+{ 0x00, 0x91, 0x91 },
+{ 0x00, 0x92, 0x92 },
+{ 0x00, 0x93, 0x93 },
+{ 0x00, 0x94, 0x94 },
+{ 0x00, 0x95, 0x95 },
+{ 0x00, 0x96, 0x96 },
+{ 0x00, 0x97, 0x97 },
+{ 0x00, 0x98, 0x98 },
+{ 0x00, 0x99, 0x99 },
+{ 0x00, 0x9a, 0x9a },
+{ 0x00, 0x9b, 0x9b },
+{ 0x00, 0x9c, 0x9c },
+{ 0x00, 0x9d, 0x9d },
+{ 0x00, 0x9e, 0x9e },
+{ 0x00, 0x9f, 0x9f },
+{ 0x00, 0xa0, 0xa0 },
+{ 0x00, 0xa1, 0xa1 },
+{ 0x00, 0xa2, 0xa2 },
+{ 0x00, 0xa3, 0xb3 },
+{ 0x00, 0xa4, 0xb4 }, /* ie */
+{ 0x00, 0xa5, 0xa5 },
+{ 0x00, 0xa6, 0xb6 }, /* i */
+{ 0x00, 0xa7, 0xb7 }, /* ii */
+{ 0x00, 0xa8, 0xa8 },
+{ 0x00, 0xa9, 0xa9 },
+{ 0x00, 0xaa, 0xaa },
+{ 0x00, 0xab, 0xab },
+{ 0x00, 0xac, 0xac },
+{ 0x00, 0xad, 0xbd }, /* g'' */
+{ 0x00, 0xae, 0xae },
+{ 0x00, 0xaf, 0xaf },
+{ 0x00, 0xb0, 0xb0 },
+{ 0x00, 0xb1, 0xb1 },
+{ 0x00, 0xb2, 0xb2 },
+{ 0x01, 0xa3, 0xb3 },
+{ 0x00, 0xb4, 0xb4 }, /* IE */
+{ 0x00, 0xb5, 0xb5 },
+{ 0x00, 0xb6, 0xb6 }, /* I */
+{ 0x00, 0xb7, 0xb7 }, /* II */
+{ 0x00, 0xb8, 0xb8 },
+{ 0x00, 0xb9, 0xb9 },
+{ 0x00, 0xba, 0xba },
+{ 0x00, 0xbb, 0xbb },
+{ 0x00, 0xbc, 0xbc },
+{ 0x00, 0xbd, 0xbd },
+{ 0x00, 0xbe, 0xbe },
+{ 0x00, 0xbf, 0xbf },
+{ 0x00, 0xc0, 0xe0 },
+{ 0x00, 0xc1, 0xe1 },
+{ 0x00, 0xc2, 0xe2 },
+{ 0x00, 0xc3, 0xe3 },
+{ 0x00, 0xc4, 0xe4 },
+{ 0x00, 0xc5, 0xe5 },
+{ 0x00, 0xc6, 0xe6 },
+{ 0x00, 0xc7, 0xe7 },
+{ 0x00, 0xc8, 0xe8 },
+{ 0x00, 0xc9, 0xe9 },
+{ 0x00, 0xca, 0xea },
+{ 0x00, 0xcb, 0xeb },
+{ 0x00, 0xcc, 0xec },
+{ 0x00, 0xcd, 0xed },
+{ 0x00, 0xce, 0xee },
+{ 0x00, 0xcf, 0xef },
+{ 0x00, 0xd0, 0xf0 },
+{ 0x00, 0xd1, 0xf1 },
+{ 0x00, 0xd2, 0xf2 },
+{ 0x00, 0xd3, 0xf3 },
+{ 0x00, 0xd4, 0xf4 },
+{ 0x00, 0xd5, 0xf5 },
+{ 0x00, 0xd6, 0xf6 },
+{ 0x00, 0xd7, 0xf7 },
+{ 0x00, 0xd8, 0xf8 },
+{ 0x00, 0xd9, 0xf9 },
+{ 0x00, 0xda, 0xfa },
+{ 0x00, 0xdb, 0xfb },
+{ 0x00, 0xdc, 0xfc },
+{ 0x00, 0xdd, 0xfd },
+{ 0x00, 0xde, 0xfe },
+{ 0x00, 0xdf, 0xff },
+{ 0x01, 0xc0, 0xe0 },
+{ 0x01, 0xc1, 0xe1 },
+{ 0x01, 0xc2, 0xe2 },
+{ 0x01, 0xc3, 0xe3 },
+{ 0x01, 0xc4, 0xe4 },
+{ 0x01, 0xc5, 0xe5 },
+{ 0x01, 0xc6, 0xe6 },
+{ 0x01, 0xc7, 0xe7 },
+{ 0x01, 0xc8, 0xe8 },
+{ 0x01, 0xc9, 0xe9 },
+{ 0x01, 0xca, 0xea },
+{ 0x01, 0xcb, 0xeb },
+{ 0x01, 0xcc, 0xec },
+{ 0x01, 0xcd, 0xed },
+{ 0x01, 0xce, 0xee },
+{ 0x01, 0xcf, 0xef },
+{ 0x01, 0xd0, 0xf0 },
+{ 0x01, 0xd1, 0xf1 },
+{ 0x01, 0xd2, 0xf2 },
+{ 0x01, 0xd3, 0xf3 },
+{ 0x01, 0xd4, 0xf4 },
+{ 0x01, 0xd5, 0xf5 },
+{ 0x01, 0xd6, 0xf6 },
+{ 0x01, 0xd7, 0xf7 },
+{ 0x01, 0xd8, 0xf8 },
+{ 0x01, 0xd9, 0xf9 },
+{ 0x01, 0xda, 0xfa },
+{ 0x01, 0xdb, 0xfb },
+{ 0x01, 0xdc, 0xfc },
+{ 0x01, 0xdd, 0xfd },
+{ 0x01, 0xde, 0xfe },
+{ 0x01, 0xdf, 0xff }
+};
+
+static struct cs_info cp1251_tbl[] = {
+{ 0x00, 0x00, 0x00 },
+{ 0x00, 0x01, 0x01 },
+{ 0x00, 0x02, 0x02 },
+{ 0x00, 0x03, 0x03 },
+{ 0x00, 0x04, 0x04 },
+{ 0x00, 0x05, 0x05 },
+{ 0x00, 0x06, 0x06 },
+{ 0x00, 0x07, 0x07 },
+{ 0x00, 0x08, 0x08 },
+{ 0x00, 0x09, 0x09 },
+{ 0x00, 0x0a, 0x0a },
+{ 0x00, 0x0b, 0x0b },
+{ 0x00, 0x0c, 0x0c },
+{ 0x00, 0x0d, 0x0d },
+{ 0x00, 0x0e, 0x0e },
+{ 0x00, 0x0f, 0x0f },
+{ 0x00, 0x10, 0x10 },
+{ 0x00, 0x11, 0x11 },
+{ 0x00, 0x12, 0x12 },
+{ 0x00, 0x13, 0x13 },
+{ 0x00, 0x14, 0x14 },
+{ 0x00, 0x15, 0x15 },
+{ 0x00, 0x16, 0x16 },
+{ 0x00, 0x17, 0x17 },
+{ 0x00, 0x18, 0x18 },
+{ 0x00, 0x19, 0x19 },
+{ 0x00, 0x1a, 0x1a },
+{ 0x00, 0x1b, 0x1b },
+{ 0x00, 0x1c, 0x1c },
+{ 0x00, 0x1d, 0x1d },
+{ 0x00, 0x1e, 0x1e },
+{ 0x00, 0x1f, 0x1f },
+{ 0x00, 0x20, 0x20 },
+{ 0x00, 0x21, 0x21 },
+{ 0x00, 0x22, 0x22 },
+{ 0x00, 0x23, 0x23 },
+{ 0x00, 0x24, 0x24 },
+{ 0x00, 0x25, 0x25 },
+{ 0x00, 0x26, 0x26 },
+{ 0x00, 0x27, 0x27 },
+{ 0x00, 0x28, 0x28 },
+{ 0x00, 0x29, 0x29 },
+{ 0x00, 0x2a, 0x2a },
+{ 0x00, 0x2b, 0x2b },
+{ 0x00, 0x2c, 0x2c },
+{ 0x00, 0x2d, 0x2d },
+{ 0x00, 0x2e, 0x2e },
+{ 0x00, 0x2f, 0x2f },
+{ 0x00, 0x30, 0x30 },
+{ 0x00, 0x31, 0x31 },
+{ 0x00, 0x32, 0x32 },
+{ 0x00, 0x33, 0x33 },
+{ 0x00, 0x34, 0x34 },
+{ 0x00, 0x35, 0x35 },
+{ 0x00, 0x36, 0x36 },
+{ 0x00, 0x37, 0x37 },
+{ 0x00, 0x38, 0x38 },
+{ 0x00, 0x39, 0x39 },
+{ 0x00, 0x3a, 0x3a },
+{ 0x00, 0x3b, 0x3b },
+{ 0x00, 0x3c, 0x3c },
+{ 0x00, 0x3d, 0x3d },
+{ 0x00, 0x3e, 0x3e },
+{ 0x00, 0x3f, 0x3f },
+{ 0x00, 0x40, 0x40 },
+{ 0x01, 0x61, 0x41 },
+{ 0x01, 0x62, 0x42 },
+{ 0x01, 0x63, 0x43 },
+{ 0x01, 0x64, 0x44 },
+{ 0x01, 0x65, 0x45 },
+{ 0x01, 0x66, 0x46 },
+{ 0x01, 0x67, 0x47 },
+{ 0x01, 0x68, 0x48 },
+{ 0x01, 0x69, 0x49 },
+{ 0x01, 0x6a, 0x4a },
+{ 0x01, 0x6b, 0x4b },
+{ 0x01, 0x6c, 0x4c },
+{ 0x01, 0x6d, 0x4d },
+{ 0x01, 0x6e, 0x4e },
+{ 0x01, 0x6f, 0x4f },
+{ 0x01, 0x70, 0x50 },
+{ 0x01, 0x71, 0x51 },
+{ 0x01, 0x72, 0x52 },
+{ 0x01, 0x73, 0x53 },
+{ 0x01, 0x74, 0x54 },
+{ 0x01, 0x75, 0x55 },
+{ 0x01, 0x76, 0x56 },
+{ 0x01, 0x77, 0x57 },
+{ 0x01, 0x78, 0x58 },
+{ 0x01, 0x79, 0x59 },
+{ 0x01, 0x7a, 0x5a },
+{ 0x00, 0x5b, 0x5b },
+{ 0x00, 0x5c, 0x5c },
+{ 0x00, 0x5d, 0x5d },
+{ 0x00, 0x5e, 0x5e },
+{ 0x00, 0x5f, 0x5f },
+{ 0x00, 0x60, 0x60 },
+{ 0x00, 0x61, 0x41 },
+{ 0x00, 0x62, 0x42 },
+{ 0x00, 0x63, 0x43 },
+{ 0x00, 0x64, 0x44 },
+{ 0x00, 0x65, 0x45 },
+{ 0x00, 0x66, 0x46 },
+{ 0x00, 0x67, 0x47 },
+{ 0x00, 0x68, 0x48 },
+{ 0x00, 0x69, 0x49 },
+{ 0x00, 0x6a, 0x4a },
+{ 0x00, 0x6b, 0x4b },
+{ 0x00, 0x6c, 0x4c },
+{ 0x00, 0x6d, 0x4d },
+{ 0x00, 0x6e, 0x4e },
+{ 0x00, 0x6f, 0x4f },
+{ 0x00, 0x70, 0x50 },
+{ 0x00, 0x71, 0x51 },
+{ 0x00, 0x72, 0x52 },
+{ 0x00, 0x73, 0x53 },
+{ 0x00, 0x74, 0x54 },
+{ 0x00, 0x75, 0x55 },
+{ 0x00, 0x76, 0x56 },
+{ 0x00, 0x77, 0x57 },
+{ 0x00, 0x78, 0x58 },
+{ 0x00, 0x79, 0x59 },
+{ 0x00, 0x7a, 0x5a },
+{ 0x00, 0x7b, 0x7b },
+{ 0x00, 0x7c, 0x7c },
+{ 0x00, 0x7d, 0x7d },
+{ 0x00, 0x7e, 0x7e },
+{ 0x00, 0x7f, 0x7f },
+{ 0x01, 0x90, 0x80 },
+{ 0x01, 0x83, 0x81 },
+{ 0x00, 0x82, 0x82 },
+{ 0x00, 0x83, 0x81 },
+{ 0x00, 0x84, 0x84 },
+{ 0x00, 0x85, 0x85 },
+{ 0x00, 0x86, 0x86 },
+{ 0x00, 0x87, 0x87 },
+{ 0x00, 0x88, 0x88 },
+{ 0x00, 0x89, 0x89 },
+{ 0x01, 0x9a, 0x8a },
+{ 0x00, 0x8b, 0x8b },
+{ 0x01, 0x9c, 0x8c },
+{ 0x01, 0x9d, 0x8d },
+{ 0x01, 0x9e, 0x8e },
+{ 0x01, 0x9f, 0x8f },
+{ 0x00, 0x90, 0x80 },
+{ 0x00, 0x91, 0x91 },
+{ 0x00, 0x92, 0x92 },
+{ 0x00, 0x93, 0x93 },
+{ 0x00, 0x94, 0x94 },
+{ 0x00, 0x95, 0x95 },
+{ 0x00, 0x96, 0x96 },
+{ 0x00, 0x97, 0x97 },
+{ 0x00, 0x98, 0x98 },
+{ 0x00, 0x99, 0x99 },
+{ 0x00, 0x9a, 0x8a },
+{ 0x00, 0x9b, 0x9b },
+{ 0x00, 0x9c, 0x8c },
+{ 0x00, 0x9d, 0x8d },
+{ 0x00, 0x9e, 0x8e },
+{ 0x00, 0x9f, 0x8f },
+{ 0x00, 0xa0, 0xa0 },
+{ 0x01, 0xa2, 0xa1 },
+{ 0x00, 0xa2, 0xa1 },
+{ 0x01, 0xbc, 0xa3 },
+{ 0x00, 0xa4, 0xa4 },
+{ 0x01, 0xb4, 0xa5 },
+{ 0x00, 0xa6, 0xa6 },
+{ 0x00, 0xa7, 0xa7 },
+{ 0x01, 0xb8, 0xa8 },
+{ 0x00, 0xa9, 0xa9 },
+{ 0x01, 0xba, 0xaa },
+{ 0x00, 0xab, 0xab },
+{ 0x00, 0xac, 0xac },
+{ 0x00, 0xad, 0xad },
+{ 0x00, 0xae, 0xae },
+{ 0x01, 0xbf, 0xaf },
+{ 0x00, 0xb0, 0xb0 },
+{ 0x00, 0xb1, 0xb1 },
+{ 0x01, 0xb3, 0xb2 },
+{ 0x00, 0xb3, 0xb2 },
+{ 0x00, 0xb4, 0xa5 },
+{ 0x00, 0xb5, 0xb5 },
+{ 0x00, 0xb6, 0xb6 },
+{ 0x00, 0xb7, 0xb7 },
+{ 0x00, 0xb8, 0xa8 },
+{ 0x00, 0xb9, 0xb9 },
+{ 0x00, 0xba, 0xaa },
+{ 0x00, 0xbb, 0xbb },
+{ 0x00, 0xbc, 0xa3 },
+{ 0x01, 0xbe, 0xbd },
+{ 0x00, 0xbe, 0xbd },
+{ 0x00, 0xbf, 0xaf },
+{ 0x01, 0xe0, 0xc0 },
+{ 0x01, 0xe1, 0xc1 },
+{ 0x01, 0xe2, 0xc2 },
+{ 0x01, 0xe3, 0xc3 },
+{ 0x01, 0xe4, 0xc4 },
+{ 0x01, 0xe5, 0xc5 },
+{ 0x01, 0xe6, 0xc6 },
+{ 0x01, 0xe7, 0xc7 },
+{ 0x01, 0xe8, 0xc8 },
+{ 0x01, 0xe9, 0xc9 },
+{ 0x01, 0xea, 0xca },
+{ 0x01, 0xeb, 0xcb },
+{ 0x01, 0xec, 0xcc },
+{ 0x01, 0xed, 0xcd },
+{ 0x01, 0xee, 0xce },
+{ 0x01, 0xef, 0xcf },
+{ 0x01, 0xf0, 0xd0 },
+{ 0x01, 0xf1, 0xd1 },
+{ 0x01, 0xf2, 0xd2 },
+{ 0x01, 0xf3, 0xd3 },
+{ 0x01, 0xf4, 0xd4 },
+{ 0x01, 0xf5, 0xd5 },
+{ 0x01, 0xf6, 0xd6 },
+{ 0x01, 0xf7, 0xd7 },
+{ 0x01, 0xf8, 0xd8 },
+{ 0x01, 0xf9, 0xd9 },
+{ 0x01, 0xfa, 0xda },
+{ 0x01, 0xfb, 0xdb },
+{ 0x01, 0xfc, 0xdc },
+{ 0x01, 0xfd, 0xdd },
+{ 0x01, 0xfe, 0xde },
+{ 0x01, 0xff, 0xdf },
+{ 0x00, 0xe0, 0xc0 },
+{ 0x00, 0xe1, 0xc1 },
+{ 0x00, 0xe2, 0xc2 },
+{ 0x00, 0xe3, 0xc3 },
+{ 0x00, 0xe4, 0xc4 },
+{ 0x00, 0xe5, 0xc5 },
+{ 0x00, 0xe6, 0xc6 },
+{ 0x00, 0xe7, 0xc7 },
+{ 0x00, 0xe8, 0xc8 },
+{ 0x00, 0xe9, 0xc9 },
+{ 0x00, 0xea, 0xca },
+{ 0x00, 0xeb, 0xcb },
+{ 0x00, 0xec, 0xcc },
+{ 0x00, 0xed, 0xcd },
+{ 0x00, 0xee, 0xce },
+{ 0x00, 0xef, 0xcf },
+{ 0x00, 0xf0, 0xd0 },
+{ 0x00, 0xf1, 0xd1 },
+{ 0x00, 0xf2, 0xd2 },
+{ 0x00, 0xf3, 0xd3 },
+{ 0x00, 0xf4, 0xd4 },
+{ 0x00, 0xf5, 0xd5 },
+{ 0x00, 0xf6, 0xd6 },
+{ 0x00, 0xf7, 0xd7 },
+{ 0x00, 0xf8, 0xd8 },
+{ 0x00, 0xf9, 0xd9 },
+{ 0x00, 0xfa, 0xda },
+{ 0x00, 0xfb, 0xdb },
+{ 0x00, 0xfc, 0xdc },
+{ 0x00, 0xfd, 0xdd },
+{ 0x00, 0xfe, 0xde },
+{ 0x00, 0xff, 0xdf }
+};
+
+static struct cs_info iso13_tbl[] = {
+{ 0x00, 0x00, 0x00 },
+{ 0x00, 0x01, 0x01 },
+{ 0x00, 0x02, 0x02 },
+{ 0x00, 0x03, 0x03 },
+{ 0x00, 0x04, 0x04 },
+{ 0x00, 0x05, 0x05 },
+{ 0x00, 0x06, 0x06 },
+{ 0x00, 0x07, 0x07 },
+{ 0x00, 0x08, 0x08 },
+{ 0x00, 0x09, 0x09 },
+{ 0x00, 0x0A, 0x0A },
+{ 0x00, 0x0B, 0x0B },
+{ 0x00, 0x0C, 0x0C },
+{ 0x00, 0x0D, 0x0D },
+{ 0x00, 0x0E, 0x0E },
+{ 0x00, 0x0F, 0x0F },
+{ 0x00, 0x10, 0x10 },
+{ 0x00, 0x11, 0x11 },
+{ 0x00, 0x12, 0x12 },
+{ 0x00, 0x13, 0x13 },
+{ 0x00, 0x14, 0x14 },
+{ 0x00, 0x15, 0x15 },
+{ 0x00, 0x16, 0x16 },
+{ 0x00, 0x17, 0x17 },
+{ 0x00, 0x18, 0x18 },
+{ 0x00, 0x19, 0x19 },
+{ 0x00, 0x1A, 0x1A },
+{ 0x00, 0x1B, 0x1B },
+{ 0x00, 0x1C, 0x1C },
+{ 0x00, 0x1D, 0x1D },
+{ 0x00, 0x1E, 0x1E },
+{ 0x00, 0x1F, 0x1F },
+{ 0x00, 0x20, 0x20 },
+{ 0x00, 0x21, 0x21 },
+{ 0x00, 0x22, 0x22 },
+{ 0x00, 0x23, 0x23 },
+{ 0x00, 0x24, 0x24 },
+{ 0x00, 0x25, 0x25 },
+{ 0x00, 0x26, 0x26 },
+{ 0x00, 0x27, 0x27 },
+{ 0x00, 0x28, 0x28 },
+{ 0x00, 0x29, 0x29 },
+{ 0x00, 0x2A, 0x2A },
+{ 0x00, 0x2B, 0x2B },
+{ 0x00, 0x2C, 0x2C },
+{ 0x00, 0x2D, 0x2D },
+{ 0x00, 0x2E, 0x2E },
+{ 0x00, 0x2F, 0x2F },
+{ 0x00, 0x30, 0x30 },
+{ 0x00, 0x31, 0x31 },
+{ 0x00, 0x32, 0x32 },
+{ 0x00, 0x33, 0x33 },
+{ 0x00, 0x34, 0x34 },
+{ 0x00, 0x35, 0x35 },
+{ 0x00, 0x36, 0x36 },
+{ 0x00, 0x37, 0x37 },
+{ 0x00, 0x38, 0x38 },
+{ 0x00, 0x39, 0x39 },
+{ 0x00, 0x3A, 0x3A },
+{ 0x00, 0x3B, 0x3B },
+{ 0x00, 0x3C, 0x3C },
+{ 0x00, 0x3D, 0x3D },
+{ 0x00, 0x3E, 0x3E },
+{ 0x00, 0x3F, 0x3F },
+{ 0x00, 0x40, 0x40 },
+{ 0x01, 0x61, 0x41 },
+{ 0x01, 0x62, 0x42 },
+{ 0x01, 0x63, 0x43 },
+{ 0x01, 0x64, 0x44 },
+{ 0x01, 0x65, 0x45 },
+{ 0x01, 0x66, 0x46 },
+{ 0x01, 0x67, 0x47 },
+{ 0x01, 0x68, 0x48 },
+{ 0x01, 0x69, 0x49 },
+{ 0x01, 0x6A, 0x4A },
+{ 0x01, 0x6B, 0x4B },
+{ 0x01, 0x6C, 0x4C },
+{ 0x01, 0x6D, 0x4D },
+{ 0x01, 0x6E, 0x4E },
+{ 0x01, 0x6F, 0x4F },
+{ 0x01, 0x70, 0x50 },
+{ 0x01, 0x71, 0x51 },
+{ 0x01, 0x72, 0x52 },
+{ 0x01, 0x73, 0x53 },
+{ 0x01, 0x74, 0x54 },
+{ 0x01, 0x75, 0x55 },
+{ 0x01, 0x76, 0x56 },
+{ 0x01, 0x77, 0x57 },
+{ 0x01, 0x78, 0x58 },
+{ 0x01, 0x79, 0x59 },
+{ 0x01, 0x7A, 0x5A },
+{ 0x00, 0x5B, 0x5B },
+{ 0x00, 0x5C, 0x5C },
+{ 0x00, 0x5D, 0x5D },
+{ 0x00, 0x5E, 0x5E },
+{ 0x00, 0x5F, 0x5F },
+{ 0x00, 0x60, 0x60 },
+{ 0x00, 0x61, 0x41 },
+{ 0x00, 0x62, 0x42 },
+{ 0x00, 0x63, 0x43 },
+{ 0x00, 0x64, 0x44 },
+{ 0x00, 0x65, 0x45 },
+{ 0x00, 0x66, 0x46 },
+{ 0x00, 0x67, 0x47 },
+{ 0x00, 0x68, 0x48 },
+{ 0x00, 0x69, 0x49 },
+{ 0x00, 0x6A, 0x4A },
+{ 0x00, 0x6B, 0x4B },
+{ 0x00, 0x6C, 0x4C },
+{ 0x00, 0x6D, 0x4D },
+{ 0x00, 0x6E, 0x4E },
+{ 0x00, 0x6F, 0x4F },
+{ 0x00, 0x70, 0x50 },
+{ 0x00, 0x71, 0x51 },
+{ 0x00, 0x72, 0x52 },
+{ 0x00, 0x73, 0x53 },
+{ 0x00, 0x74, 0x54 },
+{ 0x00, 0x75, 0x55 },
+{ 0x00, 0x76, 0x56 },
+{ 0x00, 0x77, 0x57 },
+{ 0x00, 0x78, 0x58 },
+{ 0x00, 0x79, 0x59 },
+{ 0x00, 0x7A, 0x5A },
+{ 0x00, 0x7B, 0x7B },
+{ 0x00, 0x7C, 0x7C },
+{ 0x00, 0x7D, 0x7D },
+{ 0x00, 0x7E, 0x7E },
+{ 0x00, 0x7F, 0x7F },
+{ 0x00, 0x80, 0x80 },
+{ 0x00, 0x81, 0x81 },
+{ 0x00, 0x82, 0x82 },
+{ 0x00, 0x83, 0x83 },
+{ 0x00, 0x84, 0x84 },
+{ 0x00, 0x85, 0x85 },
+{ 0x00, 0x86, 0x86 },
+{ 0x00, 0x87, 0x87 },
+{ 0x00, 0x88, 0x88 },
+{ 0x00, 0x89, 0x89 },
+{ 0x00, 0x8A, 0x8A },
+{ 0x00, 0x8B, 0x8B },
+{ 0x00, 0x8C, 0x8C },
+{ 0x00, 0x8D, 0x8D },
+{ 0x00, 0x8E, 0x8E },
+{ 0x00, 0x8F, 0x8F },
+{ 0x00, 0x90, 0x90 },
+{ 0x00, 0x91, 0x91 },
+{ 0x00, 0x92, 0x92 },
+{ 0x00, 0x93, 0x93 },
+{ 0x00, 0x94, 0x94 },
+{ 0x00, 0x95, 0x95 },
+{ 0x00, 0x96, 0x96 },
+{ 0x00, 0x97, 0x97 },
+{ 0x00, 0x98, 0x98 },
+{ 0x00, 0x99, 0x99 },
+{ 0x00, 0x9A, 0x9A },
+{ 0x00, 0x9B, 0x9B },
+{ 0x00, 0x9C, 0x9C },
+{ 0x00, 0x9D, 0x9D },
+{ 0x00, 0x9E, 0x9E },
+{ 0x00, 0x9F, 0x9F },
+{ 0x00, 0xA0, 0xA0 },
+{ 0x00, 0xA1, 0xA1 },
+{ 0x00, 0xA2, 0xA2 },
+{ 0x00, 0xA3, 0xA3 },
+{ 0x00, 0xA4, 0xA4 },
+{ 0x00, 0xA5, 0xA5 },
+{ 0x00, 0xA6, 0xA6 },
+{ 0x00, 0xA7, 0xA7 },
+{ 0x01, 0xB8, 0xA8 },
+{ 0x00, 0xA9, 0xA9 },
+{ 0x01, 0xBA, 0xAA },
+{ 0x00, 0xAB, 0xAB },
+{ 0x00, 0xAC, 0xAC },
+{ 0x00, 0xAD, 0xAD },
+{ 0x00, 0xAE, 0xAE },
+{ 0x01, 0xBF, 0xAF },
+{ 0x00, 0xB0, 0xB0 },
+{ 0x00, 0xB1, 0xB1 },
+{ 0x00, 0xB2, 0xB2 },
+{ 0x00, 0xB3, 0xB3 },
+{ 0x00, 0xB4, 0xB4 },
+{ 0x00, 0xB5, 0xB5 },
+{ 0x00, 0xB6, 0xB6 },
+{ 0x00, 0xB7, 0xB7 },
+{ 0x00, 0xB8, 0xA8 },
+{ 0x00, 0xB9, 0xB9 },
+{ 0x00, 0xBA, 0xAA },
+{ 0x00, 0xBB, 0xBB },
+{ 0x00, 0xBC, 0xBC },
+{ 0x00, 0xBD, 0xBD },
+{ 0x00, 0xBE, 0xBE },
+{ 0x00, 0xBF, 0xAF },
+{ 0x01, 0xE0, 0xC0 },
+{ 0x01, 0xE1, 0xC1 },
+{ 0x01, 0xE2, 0xC2 },
+{ 0x01, 0xE3, 0xC3 },
+{ 0x01, 0xE4, 0xC4 },
+{ 0x01, 0xE5, 0xC5 },
+{ 0x01, 0xE6, 0xC6 },
+{ 0x01, 0xE7, 0xC7 },
+{ 0x01, 0xE8, 0xC8 },
+{ 0x01, 0xE9, 0xC9 },
+{ 0x01, 0xEA, 0xCA },
+{ 0x01, 0xEB, 0xCB },
+{ 0x01, 0xEC, 0xCC },
+{ 0x01, 0xED, 0xCD },
+{ 0x01, 0xEE, 0xCE },
+{ 0x01, 0xEF, 0xCF },
+{ 0x01, 0xF0, 0xD0 },
+{ 0x01, 0xF1, 0xD1 },
+{ 0x01, 0xF2, 0xD2 },
+{ 0x01, 0xF3, 0xD3 },
+{ 0x01, 0xF4, 0xD4 },
+{ 0x01, 0xF5, 0xD5 },
+{ 0x01, 0xF6, 0xD6 },
+{ 0x00, 0xD7, 0xD7 },
+{ 0x01, 0xF8, 0xD8 },
+{ 0x01, 0xF9, 0xD9 },
+{ 0x01, 0xFA, 0xDA },
+{ 0x01, 0xFB, 0xDB },
+{ 0x01, 0xFC, 0xDC },
+{ 0x01, 0xFD, 0xDD },
+{ 0x01, 0xFE, 0xDE },
+{ 0x00, 0xDF, 0xDF },
+{ 0x00, 0xE0, 0xC0 },
+{ 0x00, 0xE1, 0xC1 },
+{ 0x00, 0xE2, 0xC2 },
+{ 0x00, 0xE3, 0xC3 },
+{ 0x00, 0xE4, 0xC4 },
+{ 0x00, 0xE5, 0xC5 },
+{ 0x00, 0xE6, 0xC6 },
+{ 0x00, 0xE7, 0xC7 },
+{ 0x00, 0xE8, 0xC8 },
+{ 0x00, 0xE9, 0xC9 },
+{ 0x00, 0xEA, 0xCA },
+{ 0x00, 0xEB, 0xCB },
+{ 0x00, 0xEC, 0xCC },
+{ 0x00, 0xED, 0xCD },
+{ 0x00, 0xEE, 0xCE },
+{ 0x00, 0xEF, 0xCF },
+{ 0x00, 0xF0, 0xD0 },
+{ 0x00, 0xF1, 0xD1 },
+{ 0x00, 0xF2, 0xD2 },
+{ 0x00, 0xF3, 0xD3 },
+{ 0x00, 0xF4, 0xD4 },
+{ 0x00, 0xF5, 0xD5 },
+{ 0x00, 0xF6, 0xD6 },
+{ 0x00, 0xF7, 0xF7 },
+{ 0x00, 0xF8, 0xD8 },
+{ 0x00, 0xF9, 0xD9 },
+{ 0x00, 0xFA, 0xDA },
+{ 0x00, 0xFB, 0xDB },
+{ 0x00, 0xFC, 0xDC },
+{ 0x00, 0xFD, 0xDD },
+{ 0x00, 0xFE, 0xDE },
+{ 0x00, 0xFF, 0xFF }
+};
+
+
+static struct cs_info iso14_tbl[] = {
+{ 0x00, 0x00, 0x00 },
+{ 0x00, 0x01, 0x01 },
+{ 0x00, 0x02, 0x02 },
+{ 0x00, 0x03, 0x03 },
+{ 0x00, 0x04, 0x04 },
+{ 0x00, 0x05, 0x05 },
+{ 0x00, 0x06, 0x06 },
+{ 0x00, 0x07, 0x07 },
+{ 0x00, 0x08, 0x08 },
+{ 0x00, 0x09, 0x09 },
+{ 0x00, 0x0a, 0x0a },
+{ 0x00, 0x0b, 0x0b },
+{ 0x00, 0x0c, 0x0c },
+{ 0x00, 0x0d, 0x0d },
+{ 0x00, 0x0e, 0x0e },
+{ 0x00, 0x0f, 0x0f },
+{ 0x00, 0x10, 0x10 },
+{ 0x00, 0x11, 0x11 },
+{ 0x00, 0x12, 0x12 },
+{ 0x00, 0x13, 0x13 },
+{ 0x00, 0x14, 0x14 },
+{ 0x00, 0x15, 0x15 },
+{ 0x00, 0x16, 0x16 },
+{ 0x00, 0x17, 0x17 },
+{ 0x00, 0x18, 0x18 },
+{ 0x00, 0x19, 0x19 },
+{ 0x00, 0x1a, 0x1a },
+{ 0x00, 0x1b, 0x1b },
+{ 0x00, 0x1c, 0x1c },
+{ 0x00, 0x1d, 0x1d },
+{ 0x00, 0x1e, 0x1e },
+{ 0x00, 0x1f, 0x1f },
+{ 0x00, 0x20, 0x20 },
+{ 0x00, 0x21, 0x21 },
+{ 0x00, 0x22, 0x22 },
+{ 0x00, 0x23, 0x23 },
+{ 0x00, 0x24, 0x24 },
+{ 0x00, 0x25, 0x25 },
+{ 0x00, 0x26, 0x26 },
+{ 0x00, 0x27, 0x27 },
+{ 0x00, 0x28, 0x28 },
+{ 0x00, 0x29, 0x29 },
+{ 0x00, 0x2a, 0x2a },
+{ 0x00, 0x2b, 0x2b },
+{ 0x00, 0x2c, 0x2c },
+{ 0x00, 0x2d, 0x2d },
+{ 0x00, 0x2e, 0x2e },
+{ 0x00, 0x2f, 0x2f },
+{ 0x00, 0x30, 0x30 },
+{ 0x00, 0x31, 0x31 },
+{ 0x00, 0x32, 0x32 },
+{ 0x00, 0x33, 0x33 },
+{ 0x00, 0x34, 0x34 },
+{ 0x00, 0x35, 0x35 },
+{ 0x00, 0x36, 0x36 },
+{ 0x00, 0x37, 0x37 },
+{ 0x00, 0x38, 0x38 },
+{ 0x00, 0x39, 0x39 },
+{ 0x00, 0x3a, 0x3a },
+{ 0x00, 0x3b, 0x3b },
+{ 0x00, 0x3c, 0x3c },
+{ 0x00, 0x3d, 0x3d },
+{ 0x00, 0x3e, 0x3e },
+{ 0x00, 0x3f, 0x3f },
+{ 0x00, 0x40, 0x40 },
+{ 0x01, 0x61, 0x41 },
+{ 0x01, 0x62, 0x42 },
+{ 0x01, 0x63, 0x43 },
+{ 0x01, 0x64, 0x44 },
+{ 0x01, 0x65, 0x45 },
+{ 0x01, 0x66, 0x46 },
+{ 0x01, 0x67, 0x47 },
+{ 0x01, 0x68, 0x48 },
+{ 0x01, 0x69, 0x49 },
+{ 0x01, 0x6a, 0x4a },
+{ 0x01, 0x6b, 0x4b },
+{ 0x01, 0x6c, 0x4c },
+{ 0x01, 0x6d, 0x4d },
+{ 0x01, 0x6e, 0x4e },
+{ 0x01, 0x6f, 0x4f },
+{ 0x01, 0x70, 0x50 },
+{ 0x01, 0x71, 0x51 },
+{ 0x01, 0x72, 0x52 },
+{ 0x01, 0x73, 0x53 },
+{ 0x01, 0x74, 0x54 },
+{ 0x01, 0x75, 0x55 },
+{ 0x01, 0x76, 0x56 },
+{ 0x01, 0x77, 0x57 },
+{ 0x01, 0x78, 0x58 },
+{ 0x01, 0x79, 0x59 },
+{ 0x01, 0x7a, 0x5a },
+{ 0x00, 0x5b, 0x5b },
+{ 0x00, 0x5c, 0x5c },
+{ 0x00, 0x5d, 0x5d },
+{ 0x00, 0x5e, 0x5e },
+{ 0x00, 0x5f, 0x5f },
+{ 0x00, 0x60, 0x60 },
+{ 0x00, 0x61, 0x41 },
+{ 0x00, 0x62, 0x42 },
+{ 0x00, 0x63, 0x43 },
+{ 0x00, 0x64, 0x44 },
+{ 0x00, 0x65, 0x45 },
+{ 0x00, 0x66, 0x46 },
+{ 0x00, 0x67, 0x47 },
+{ 0x00, 0x68, 0x48 },
+{ 0x00, 0x69, 0x49 },
+{ 0x00, 0x6a, 0x4a },
+{ 0x00, 0x6b, 0x4b },
+{ 0x00, 0x6c, 0x4c },
+{ 0x00, 0x6d, 0x4d },
+{ 0x00, 0x6e, 0x4e },
+{ 0x00, 0x6f, 0x4f },
+{ 0x00, 0x70, 0x50 },
+{ 0x00, 0x71, 0x51 },
+{ 0x00, 0x72, 0x52 },
+{ 0x00, 0x73, 0x53 },
+{ 0x00, 0x74, 0x54 },
+{ 0x00, 0x75, 0x55 },
+{ 0x00, 0x76, 0x56 },
+{ 0x00, 0x77, 0x57 },
+{ 0x00, 0x78, 0x58 },
+{ 0x00, 0x79, 0x59 },
+{ 0x00, 0x7a, 0x5a },
+{ 0x00, 0x7b, 0x7b },
+{ 0x00, 0x7c, 0x7c },
+{ 0x00, 0x7d, 0x7d },
+{ 0x00, 0x7e, 0x7e },
+{ 0x00, 0x7f, 0x7f },
+{ 0x00, 0x80, 0x80 },
+{ 0x00, 0x81, 0x81 },
+{ 0x00, 0x82, 0x82 },
+{ 0x00, 0x83, 0x83 },
+{ 0x00, 0x84, 0x84 },
+{ 0x00, 0x85, 0x85 },
+{ 0x00, 0x86, 0x86 },
+{ 0x00, 0x87, 0x87 },
+{ 0x00, 0x88, 0x88 },
+{ 0x00, 0x89, 0x89 },
+{ 0x00, 0x8a, 0x8a },
+{ 0x00, 0x8b, 0x8b },
+{ 0x00, 0x8c, 0x8c },
+{ 0x00, 0x8d, 0x8d },
+{ 0x00, 0x8e, 0x8e },
+{ 0x00, 0x8f, 0x8f },
+{ 0x00, 0x90, 0x90 },
+{ 0x00, 0x91, 0x91 },
+{ 0x00, 0x92, 0x92 },
+{ 0x00, 0x93, 0x93 },
+{ 0x00, 0x94, 0x94 },
+{ 0x00, 0x95, 0x95 },
+{ 0x00, 0x96, 0x96 },
+{ 0x00, 0x97, 0x97 },
+{ 0x00, 0x98, 0x98 },
+{ 0x00, 0x99, 0x99 },
+{ 0x00, 0x9a, 0x9a },
+{ 0x00, 0x9b, 0x9b },
+{ 0x00, 0x9c, 0x9c },
+{ 0x00, 0x9d, 0x9d },
+{ 0x00, 0x9e, 0x9e },
+{ 0x00, 0x9f, 0x9f },
+{ 0x00, 0xa0, 0xa0 },
+{ 0x01, 0xa2, 0xa1 },
+{ 0x00, 0xa2, 0xa1 },
+{ 0x00, 0xa3, 0xa3 },
+{ 0x01, 0xa5, 0xa4 },
+{ 0x00, 0xa5, 0xa4 },
+{ 0x01, 0xa6, 0xab },
+{ 0x00, 0xa7, 0xa7 },
+{ 0x01, 0xb8, 0xa8 },
+{ 0x00, 0xa9, 0xa9 },
+{ 0x01, 0xba, 0xaa },
+{ 0x00, 0xab, 0xa6 },
+{ 0x01, 0xbc, 0xac },
+{ 0x00, 0xad, 0xad },
+{ 0x00, 0xae, 0xae },
+{ 0x01, 0xff, 0xaf },
+{ 0x01, 0xb1, 0xb0 },
+{ 0x00, 0xb1, 0xb0 },
+{ 0x01, 0xb3, 0xb2 },
+{ 0x00, 0xb3, 0xb2 },
+{ 0x01, 0xb5, 0xb4 },
+{ 0x00, 0xb5, 0xb4 },
+{ 0x00, 0xb6, 0xb6 },
+{ 0x01, 0xb9, 0xb7 },
+{ 0x00, 0xb8, 0xa8 },
+{ 0x00, 0xb9, 0xb6 },
+{ 0x00, 0xba, 0xaa },
+{ 0x01, 0xbf, 0xbb },
+{ 0x00, 0xbc, 0xac },
+{ 0x01, 0xbe, 0xbd },
+{ 0x00, 0xbe, 0xbd },
+{ 0x00, 0xbf, 0xbb },
+{ 0x01, 0xe0, 0xc0 },
+{ 0x01, 0xe1, 0xc1 },
+{ 0x01, 0xe2, 0xc2 },
+{ 0x01, 0xe3, 0xc3 },
+{ 0x01, 0xe4, 0xc4 },
+{ 0x01, 0xe5, 0xc5 },
+{ 0x01, 0xe6, 0xc6 },
+{ 0x01, 0xe7, 0xc7 },
+{ 0x01, 0xe8, 0xc8 },
+{ 0x01, 0xe9, 0xc9 },
+{ 0x01, 0xea, 0xca },
+{ 0x01, 0xeb, 0xcb },
+{ 0x01, 0xec, 0xcc },
+{ 0x01, 0xed, 0xcd },
+{ 0x01, 0xee, 0xce },
+{ 0x01, 0xef, 0xcf },
+{ 0x01, 0xf0, 0xd0 },
+{ 0x01, 0xf1, 0xd1 },
+{ 0x01, 0xf2, 0xd2 },
+{ 0x01, 0xf3, 0xd3 },
+{ 0x01, 0xf4, 0xd4 },
+{ 0x01, 0xf5, 0xd5 },
+{ 0x01, 0xf6, 0xd6 },
+{ 0x01, 0xf7, 0xd7 },
+{ 0x01, 0xf8, 0xd8 },
+{ 0x01, 0xf9, 0xd9 },
+{ 0x01, 0xfa, 0xda },
+{ 0x01, 0xfb, 0xdb },
+{ 0x01, 0xfc, 0xdc },
+{ 0x01, 0xfd, 0xdd },
+{ 0x01, 0xfe, 0xde },
+{ 0x00, 0xdf, 0xdf },
+{ 0x00, 0xe0, 0xc0 },
+{ 0x00, 0xe1, 0xc1 },
+{ 0x00, 0xe2, 0xc2 },
+{ 0x00, 0xe3, 0xc3 },
+{ 0x00, 0xe4, 0xc4 },
+{ 0x00, 0xe5, 0xc5 },
+{ 0x00, 0xe6, 0xc6 },
+{ 0x00, 0xe7, 0xc7 },
+{ 0x00, 0xe8, 0xc8 },
+{ 0x00, 0xe9, 0xc9 },
+{ 0x00, 0xea, 0xca },
+{ 0x00, 0xeb, 0xcb },
+{ 0x00, 0xec, 0xcc },
+{ 0x00, 0xed, 0xcd },
+{ 0x00, 0xee, 0xce },
+{ 0x00, 0xef, 0xcf },
+{ 0x00, 0xf0, 0xd0 },
+{ 0x00, 0xf1, 0xd1 },
+{ 0x00, 0xf2, 0xd2 },
+{ 0x00, 0xf3, 0xd3 },
+{ 0x00, 0xf4, 0xd4 },
+{ 0x00, 0xf5, 0xd5 },
+{ 0x00, 0xf6, 0xd6 },
+{ 0x00, 0xf7, 0xd7 },
+{ 0x00, 0xf8, 0xd8 },
+{ 0x00, 0xf9, 0xd9 },
+{ 0x00, 0xfa, 0xda },
+{ 0x00, 0xfb, 0xdb },
+{ 0x00, 0xfc, 0xdc },
+{ 0x00, 0xfd, 0xdd },
+{ 0x00, 0xfe, 0xde },
+{ 0x00, 0xff, 0xff }
+};
+
+static struct cs_info iso15_tbl[] = {
+{ 0x00, 0x00, 0x00 },
+{ 0x00, 0x01, 0x01 },
+{ 0x00, 0x02, 0x02 },
+{ 0x00, 0x03, 0x03 },
+{ 0x00, 0x04, 0x04 },
+{ 0x00, 0x05, 0x05 },
+{ 0x00, 0x06, 0x06 },
+{ 0x00, 0x07, 0x07 },
+{ 0x00, 0x08, 0x08 },
+{ 0x00, 0x09, 0x09 },
+{ 0x00, 0x0a, 0x0a },
+{ 0x00, 0x0b, 0x0b },
+{ 0x00, 0x0c, 0x0c },
+{ 0x00, 0x0d, 0x0d },
+{ 0x00, 0x0e, 0x0e },
+{ 0x00, 0x0f, 0x0f },
+{ 0x00, 0x10, 0x10 },
+{ 0x00, 0x11, 0x11 },
+{ 0x00, 0x12, 0x12 },
+{ 0x00, 0x13, 0x13 },
+{ 0x00, 0x14, 0x14 },
+{ 0x00, 0x15, 0x15 },
+{ 0x00, 0x16, 0x16 },
+{ 0x00, 0x17, 0x17 },
+{ 0x00, 0x18, 0x18 },
+{ 0x00, 0x19, 0x19 },
+{ 0x00, 0x1a, 0x1a },
+{ 0x00, 0x1b, 0x1b },
+{ 0x00, 0x1c, 0x1c },
+{ 0x00, 0x1d, 0x1d },
+{ 0x00, 0x1e, 0x1e },
+{ 0x00, 0x1f, 0x1f },
+{ 0x00, 0x20, 0x20 },
+{ 0x00, 0x21, 0x21 },
+{ 0x00, 0x22, 0x22 },
+{ 0x00, 0x23, 0x23 },
+{ 0x00, 0x24, 0x24 },
+{ 0x00, 0x25, 0x25 },
+{ 0x00, 0x26, 0x26 },
+{ 0x00, 0x27, 0x27 },
+{ 0x00, 0x28, 0x28 },
+{ 0x00, 0x29, 0x29 },
+{ 0x00, 0x2a, 0x2a },
+{ 0x00, 0x2b, 0x2b },
+{ 0x00, 0x2c, 0x2c },
+{ 0x00, 0x2d, 0x2d },
+{ 0x00, 0x2e, 0x2e },
+{ 0x00, 0x2f, 0x2f },
+{ 0x00, 0x30, 0x30 },
+{ 0x00, 0x31, 0x31 },
+{ 0x00, 0x32, 0x32 },
+{ 0x00, 0x33, 0x33 },
+{ 0x00, 0x34, 0x34 },
+{ 0x00, 0x35, 0x35 },
+{ 0x00, 0x36, 0x36 },
+{ 0x00, 0x37, 0x37 },
+{ 0x00, 0x38, 0x38 },
+{ 0x00, 0x39, 0x39 },
+{ 0x00, 0x3a, 0x3a },
+{ 0x00, 0x3b, 0x3b },
+{ 0x00, 0x3c, 0x3c },
+{ 0x00, 0x3d, 0x3d },
+{ 0x00, 0x3e, 0x3e },
+{ 0x00, 0x3f, 0x3f },
+{ 0x00, 0x40, 0x40 },
+{ 0x01, 0x61, 0x41 },
+{ 0x01, 0x62, 0x42 },
+{ 0x01, 0x63, 0x43 },
+{ 0x01, 0x64, 0x44 },
+{ 0x01, 0x65, 0x45 },
+{ 0x01, 0x66, 0x46 },
+{ 0x01, 0x67, 0x47 },
+{ 0x01, 0x68, 0x48 },
+{ 0x01, 0x69, 0x49 },
+{ 0x01, 0x6a, 0x4a },
+{ 0x01, 0x6b, 0x4b },
+{ 0x01, 0x6c, 0x4c },
+{ 0x01, 0x6d, 0x4d },
+{ 0x01, 0x6e, 0x4e },
+{ 0x01, 0x6f, 0x4f },
+{ 0x01, 0x70, 0x50 },
+{ 0x01, 0x71, 0x51 },
+{ 0x01, 0x72, 0x52 },
+{ 0x01, 0x73, 0x53 },
+{ 0x01, 0x74, 0x54 },
+{ 0x01, 0x75, 0x55 },
+{ 0x01, 0x76, 0x56 },
+{ 0x01, 0x77, 0x57 },
+{ 0x01, 0x78, 0x58 },
+{ 0x01, 0x79, 0x59 },
+{ 0x01, 0x7a, 0x5a },
+{ 0x00, 0x5b, 0x5b },
+{ 0x00, 0x5c, 0x5c },
+{ 0x00, 0x5d, 0x5d },
+{ 0x00, 0x5e, 0x5e },
+{ 0x00, 0x5f, 0x5f },
+{ 0x00, 0x60, 0x60 },
+{ 0x00, 0x61, 0x41 },
+{ 0x00, 0x62, 0x42 },
+{ 0x00, 0x63, 0x43 },
+{ 0x00, 0x64, 0x44 },
+{ 0x00, 0x65, 0x45 },
+{ 0x00, 0x66, 0x46 },
+{ 0x00, 0x67, 0x47 },
+{ 0x00, 0x68, 0x48 },
+{ 0x00, 0x69, 0x49 },
+{ 0x00, 0x6a, 0x4a },
+{ 0x00, 0x6b, 0x4b },
+{ 0x00, 0x6c, 0x4c },
+{ 0x00, 0x6d, 0x4d },
+{ 0x00, 0x6e, 0x4e },
+{ 0x00, 0x6f, 0x4f },
+{ 0x00, 0x70, 0x50 },
+{ 0x00, 0x71, 0x51 },
+{ 0x00, 0x72, 0x52 },
+{ 0x00, 0x73, 0x53 },
+{ 0x00, 0x74, 0x54 },
+{ 0x00, 0x75, 0x55 },
+{ 0x00, 0x76, 0x56 },
+{ 0x00, 0x77, 0x57 },
+{ 0x00, 0x78, 0x58 },
+{ 0x00, 0x79, 0x59 },
+{ 0x00, 0x7a, 0x5a },
+{ 0x00, 0x7b, 0x7b },
+{ 0x00, 0x7c, 0x7c },
+{ 0x00, 0x7d, 0x7d },
+{ 0x00, 0x7e, 0x7e },
+{ 0x00, 0x7f, 0x7f },
+{ 0x00, 0x80, 0x80 },
+{ 0x00, 0x81, 0x81 },
+{ 0x00, 0x82, 0x82 },
+{ 0x00, 0x83, 0x83 },
+{ 0x00, 0x84, 0x84 },
+{ 0x00, 0x85, 0x85 },
+{ 0x00, 0x86, 0x86 },
+{ 0x00, 0x87, 0x87 },
+{ 0x00, 0x88, 0x88 },
+{ 0x00, 0x89, 0x89 },
+{ 0x00, 0x8a, 0x8a },
+{ 0x00, 0x8b, 0x8b },
+{ 0x00, 0x8c, 0x8c },
+{ 0x00, 0x8d, 0x8d },
+{ 0x00, 0x8e, 0x8e },
+{ 0x00, 0x8f, 0x8f },
+{ 0x00, 0x90, 0x90 },
+{ 0x00, 0x91, 0x91 },
+{ 0x00, 0x92, 0x92 },
+{ 0x00, 0x93, 0x93 },
+{ 0x00, 0x94, 0x94 },
+{ 0x00, 0x95, 0x95 },
+{ 0x00, 0x96, 0x96 },
+{ 0x00, 0x97, 0x97 },
+{ 0x00, 0x98, 0x98 },
+{ 0x00, 0x99, 0x99 },
+{ 0x00, 0x9a, 0x9a },
+{ 0x00, 0x9b, 0x9b },
+{ 0x00, 0x9c, 0x9c },
+{ 0x00, 0x9d, 0x9d },
+{ 0x00, 0x9e, 0x9e },
+{ 0x00, 0x9f, 0x9f },
+{ 0x00, 0xa0, 0xa0 },
+{ 0x00, 0xa1, 0xa1 },
+{ 0x00, 0xa2, 0xa2 },
+{ 0x00, 0xa3, 0xa3 },
+{ 0x00, 0xa4, 0xa4 },
+{ 0x00, 0xa5, 0xa5 },
+{ 0x01, 0xa8, 0xa6 },
+{ 0x00, 0xa7, 0xa7 },
+{ 0x00, 0xa8, 0xa6 },
+{ 0x00, 0xa9, 0xa9 },
+{ 0x00, 0xaa, 0xaa },
+{ 0x00, 0xab, 0xab },
+{ 0x00, 0xac, 0xac },
+{ 0x00, 0xad, 0xad },
+{ 0x00, 0xae, 0xae },
+{ 0x00, 0xaf, 0xaf },
+{ 0x00, 0xb0, 0xb0 },
+{ 0x00, 0xb1, 0xb1 },
+{ 0x00, 0xb2, 0xb2 },
+{ 0x00, 0xb3, 0xb3 },
+{ 0x01, 0xb8, 0xb4 },
+{ 0x00, 0xb5, 0xb5 },
+{ 0x00, 0xb6, 0xb6 },
+{ 0x00, 0xb7, 0xb7 },
+{ 0x00, 0xb8, 0xb4 },
+{ 0x00, 0xb9, 0xb9 },
+{ 0x00, 0xba, 0xba },
+{ 0x00, 0xbb, 0xbb },
+{ 0x01, 0xbd, 0xbc },
+{ 0x00, 0xbd, 0xbc },
+{ 0x01, 0xff, 0xbe },
+{ 0x00, 0xbf, 0xbf },
+{ 0x01, 0xe0, 0xc0 },
+{ 0x01, 0xe1, 0xc1 },
+{ 0x01, 0xe2, 0xc2 },
+{ 0x01, 0xe3, 0xc3 },
+{ 0x01, 0xe4, 0xc4 },
+{ 0x01, 0xe5, 0xc5 },
+{ 0x01, 0xe6, 0xc6 },
+{ 0x01, 0xe7, 0xc7 },
+{ 0x01, 0xe8, 0xc8 },
+{ 0x01, 0xe9, 0xc9 },
+{ 0x01, 0xea, 0xca },
+{ 0x01, 0xeb, 0xcb },
+{ 0x01, 0xec, 0xcc },
+{ 0x01, 0xed, 0xcd },
+{ 0x01, 0xee, 0xce },
+{ 0x01, 0xef, 0xcf },
+{ 0x01, 0xf0, 0xd0 },
+{ 0x01, 0xf1, 0xd1 },
+{ 0x01, 0xf2, 0xd2 },
+{ 0x01, 0xf3, 0xd3 },
+{ 0x01, 0xf4, 0xd4 },
+{ 0x01, 0xf5, 0xd5 },
+{ 0x01, 0xf6, 0xd6 },
+{ 0x00, 0xd7, 0xd7 },
+{ 0x01, 0xf8, 0xd8 },
+{ 0x01, 0xf9, 0xd9 },
+{ 0x01, 0xfa, 0xda },
+{ 0x01, 0xfb, 0xdb },
+{ 0x01, 0xfc, 0xdc },
+{ 0x01, 0xfd, 0xdd },
+{ 0x01, 0xfe, 0xde },
+{ 0x00, 0xdf, 0xdf },
+{ 0x00, 0xe0, 0xc0 },
+{ 0x00, 0xe1, 0xc1 },
+{ 0x00, 0xe2, 0xc2 },
+{ 0x00, 0xe3, 0xc3 },
+{ 0x00, 0xe4, 0xc4 },
+{ 0x00, 0xe5, 0xc5 },
+{ 0x00, 0xe6, 0xc6 },
+{ 0x00, 0xe7, 0xc7 },
+{ 0x00, 0xe8, 0xc8 },
+{ 0x00, 0xe9, 0xc9 },
+{ 0x00, 0xea, 0xca },
+{ 0x00, 0xeb, 0xcb },
+{ 0x00, 0xec, 0xcc },
+{ 0x00, 0xed, 0xcd },
+{ 0x00, 0xee, 0xce },
+{ 0x00, 0xef, 0xcf },
+{ 0x00, 0xf0, 0xd0 },
+{ 0x00, 0xf1, 0xd1 },
+{ 0x00, 0xf2, 0xd2 },
+{ 0x00, 0xf3, 0xd3 },
+{ 0x00, 0xf4, 0xd4 },
+{ 0x00, 0xf5, 0xd5 },
+{ 0x00, 0xf6, 0xd6 },
+{ 0x00, 0xf7, 0xf7 },
+{ 0x00, 0xf8, 0xd8 },
+{ 0x00, 0xf9, 0xd9 },
+{ 0x00, 0xfa, 0xda },
+{ 0x00, 0xfb, 0xdb },
+{ 0x00, 0xfc, 0xdc },
+{ 0x00, 0xfd, 0xdd },
+{ 0x00, 0xfe, 0xde },
+{ 0x00, 0xff, 0xbe }
+};
+
+static struct cs_info iscii_devanagari_tbl[] = {
+{ 0x00, 0x00, 0x00 },
+{ 0x00, 0x01, 0x01 },
+{ 0x00, 0x02, 0x02 },
+{ 0x00, 0x03, 0x03 },
+{ 0x00, 0x04, 0x04 },
+{ 0x00, 0x05, 0x05 },
+{ 0x00, 0x06, 0x06 },
+{ 0x00, 0x07, 0x07 },
+{ 0x00, 0x08, 0x08 },
+{ 0x00, 0x09, 0x09 },
+{ 0x00, 0x0a, 0x0a },
+{ 0x00, 0x0b, 0x0b },
+{ 0x00, 0x0c, 0x0c },
+{ 0x00, 0x0d, 0x0d },
+{ 0x00, 0x0e, 0x0e },
+{ 0x00, 0x0f, 0x0f },
+{ 0x00, 0x10, 0x10 },
+{ 0x00, 0x11, 0x11 },
+{ 0x00, 0x12, 0x12 },
+{ 0x00, 0x13, 0x13 },
+{ 0x00, 0x14, 0x14 },
+{ 0x00, 0x15, 0x15 },
+{ 0x00, 0x16, 0x16 },
+{ 0x00, 0x17, 0x17 },
+{ 0x00, 0x18, 0x18 },
+{ 0x00, 0x19, 0x19 },
+{ 0x00, 0x1a, 0x1a },
+{ 0x00, 0x1b, 0x1b },
+{ 0x00, 0x1c, 0x1c },
+{ 0x00, 0x1d, 0x1d },
+{ 0x00, 0x1e, 0x1e },
+{ 0x00, 0x1f, 0x1f },
+{ 0x00, 0x20, 0x20 },
+{ 0x00, 0x21, 0x21 },
+{ 0x00, 0x22, 0x22 },
+{ 0x00, 0x23, 0x23 },
+{ 0x00, 0x24, 0x24 },
+{ 0x00, 0x25, 0x25 },
+{ 0x00, 0x26, 0x26 },
+{ 0x00, 0x27, 0x27 },
+{ 0x00, 0x28, 0x28 },
+{ 0x00, 0x29, 0x29 },
+{ 0x00, 0x2a, 0x2a },
+{ 0x00, 0x2b, 0x2b },
+{ 0x00, 0x2c, 0x2c },
+{ 0x00, 0x2d, 0x2d },
+{ 0x00, 0x2e, 0x2e },
+{ 0x00, 0x2f, 0x2f },
+{ 0x00, 0x30, 0x30 },
+{ 0x00, 0x31, 0x31 },
+{ 0x00, 0x32, 0x32 },
+{ 0x00, 0x33, 0x33 },
+{ 0x00, 0x34, 0x34 },
+{ 0x00, 0x35, 0x35 },
+{ 0x00, 0x36, 0x36 },
+{ 0x00, 0x37, 0x37 },
+{ 0x00, 0x38, 0x38 },
+{ 0x00, 0x39, 0x39 },
+{ 0x00, 0x3a, 0x3a },
+{ 0x00, 0x3b, 0x3b },
+{ 0x00, 0x3c, 0x3c },
+{ 0x00, 0x3d, 0x3d },
+{ 0x00, 0x3e, 0x3e },
+{ 0x00, 0x3f, 0x3f },
+{ 0x00, 0x40, 0x40 },
+{ 0x01, 0x61, 0x41 },
+{ 0x01, 0x62, 0x42 },
+{ 0x01, 0x63, 0x43 },
+{ 0x01, 0x64, 0x44 },
+{ 0x01, 0x65, 0x45 },
+{ 0x01, 0x66, 0x46 },
+{ 0x01, 0x67, 0x47 },
+{ 0x01, 0x68, 0x48 },
+{ 0x01, 0x69, 0x49 },
+{ 0x01, 0x6a, 0x4a },
+{ 0x01, 0x6b, 0x4b },
+{ 0x01, 0x6c, 0x4c },
+{ 0x01, 0x6d, 0x4d },
+{ 0x01, 0x6e, 0x4e },
+{ 0x01, 0x6f, 0x4f },
+{ 0x01, 0x70, 0x50 },
+{ 0x01, 0x71, 0x51 },
+{ 0x01, 0x72, 0x52 },
+{ 0x01, 0x73, 0x53 },
+{ 0x01, 0x74, 0x54 },
+{ 0x01, 0x75, 0x55 },
+{ 0x01, 0x76, 0x56 },
+{ 0x01, 0x77, 0x57 },
+{ 0x01, 0x78, 0x58 },
+{ 0x01, 0x79, 0x59 },
+{ 0x01, 0x7a, 0x5a },
+{ 0x00, 0x5b, 0x5b },
+{ 0x00, 0x5c, 0x5c },
+{ 0x00, 0x5d, 0x5d },
+{ 0x00, 0x5e, 0x5e },
+{ 0x00, 0x5f, 0x5f },
+{ 0x00, 0x60, 0x60 },
+{ 0x00, 0x61, 0x41 },
+{ 0x00, 0x62, 0x42 },
+{ 0x00, 0x63, 0x43 },
+{ 0x00, 0x64, 0x44 },
+{ 0x00, 0x65, 0x45 },
+{ 0x00, 0x66, 0x46 },
+{ 0x00, 0x67, 0x47 },
+{ 0x00, 0x68, 0x48 },
+{ 0x00, 0x69, 0x49 },
+{ 0x00, 0x6a, 0x4a },
+{ 0x00, 0x6b, 0x4b },
+{ 0x00, 0x6c, 0x4c },
+{ 0x00, 0x6d, 0x4d },
+{ 0x00, 0x6e, 0x4e },
+{ 0x00, 0x6f, 0x4f },
+{ 0x00, 0x70, 0x50 },
+{ 0x00, 0x71, 0x51 },
+{ 0x00, 0x72, 0x52 },
+{ 0x00, 0x73, 0x53 },
+{ 0x00, 0x74, 0x54 },
+{ 0x00, 0x75, 0x55 },
+{ 0x00, 0x76, 0x56 },
+{ 0x00, 0x77, 0x57 },
+{ 0x00, 0x78, 0x58 },
+{ 0x00, 0x79, 0x59 },
+{ 0x00, 0x7a, 0x5a },
+{ 0x00, 0x7b, 0x7b },
+{ 0x00, 0x7c, 0x7c },
+{ 0x00, 0x7d, 0x7d },
+{ 0x00, 0x7e, 0x7e },
+{ 0x00, 0x7f, 0x7f },
+{ 0x00, 0x80, 0x80 },
+{ 0x00, 0x81, 0x81 },
+{ 0x00, 0x82, 0x82 },
+{ 0x00, 0x83, 0x83 },
+{ 0x00, 0x84, 0x84 },
+{ 0x00, 0x85, 0x85 },
+{ 0x00, 0x86, 0x86 },
+{ 0x00, 0x87, 0x87 },
+{ 0x00, 0x88, 0x88 },
+{ 0x00, 0x89, 0x89 },
+{ 0x00, 0x8a, 0x8a },
+{ 0x00, 0x8b, 0x8b },
+{ 0x00, 0x8c, 0x8c },
+{ 0x00, 0x8d, 0x8d },
+{ 0x00, 0x8e, 0x8e },
+{ 0x00, 0x8f, 0x8f },
+{ 0x00, 0x90, 0x90 },
+{ 0x00, 0x91, 0x91 },
+{ 0x00, 0x92, 0x92 },
+{ 0x00, 0x93, 0x93 },
+{ 0x00, 0x94, 0x94 },
+{ 0x00, 0x95, 0x95 },
+{ 0x00, 0x96, 0x96 },
+{ 0x00, 0x97, 0x97 },
+{ 0x00, 0x98, 0x98 },
+{ 0x00, 0x99, 0x99 },
+{ 0x00, 0x9a, 0x9a },
+{ 0x00, 0x9b, 0x9b },
+{ 0x00, 0x9c, 0x9c },
+{ 0x00, 0x9d, 0x9d },
+{ 0x00, 0x9e, 0x9e },
+{ 0x00, 0x9f, 0x9f },
+{ 0x00, 0xa0, 0xa0 },
+{ 0x00, 0xa1, 0xa1 },
+{ 0x00, 0xa2, 0xa2 },
+{ 0x00, 0xa3, 0xa3 },
+{ 0x00, 0xa4, 0xa4 },
+{ 0x00, 0xa5, 0xa5 },
+{ 0x00, 0xa6, 0xa6 },
+{ 0x00, 0xa7, 0xa7 },
+{ 0x00, 0xa8, 0xa8 },
+{ 0x00, 0xa9, 0xa9 },
+{ 0x00, 0xaa, 0xaa },
+{ 0x00, 0xab, 0xab },
+{ 0x00, 0xac, 0xac },
+{ 0x00, 0xad, 0xad },
+{ 0x00, 0xae, 0xae },
+{ 0x00, 0xaf, 0xaf },
+{ 0x00, 0xb0, 0xb0 },
+{ 0x00, 0xb1, 0xb1 },
+{ 0x00, 0xb2, 0xb2 },
+{ 0x00, 0xb3, 0xb3 },
+{ 0x00, 0xb4, 0xb4 },
+{ 0x00, 0xb5, 0xb5 },
+{ 0x00, 0xb6, 0xb6 },
+{ 0x00, 0xb7, 0xb7 },
+{ 0x00, 0xb8, 0xb8 },
+{ 0x00, 0xb9, 0xb9 },
+{ 0x00, 0xba, 0xba },
+{ 0x00, 0xbb, 0xbb },
+{ 0x00, 0xbc, 0xbc },
+{ 0x00, 0xbd, 0xbd },
+{ 0x00, 0xbe, 0xbe },
+{ 0x00, 0xbf, 0xbf },
+{ 0x00, 0xc0, 0xc0 },
+{ 0x00, 0xc1, 0xc1 },
+{ 0x00, 0xc2, 0xc2 },
+{ 0x00, 0xc3, 0xc3 },
+{ 0x00, 0xc4, 0xc4 },
+{ 0x00, 0xc5, 0xc5 },
+{ 0x00, 0xc6, 0xc6 },
+{ 0x00, 0xc7, 0xc7 },
+{ 0x00, 0xc8, 0xc8 },
+{ 0x00, 0xc9, 0xc9 },
+{ 0x00, 0xca, 0xca },
+{ 0x00, 0xcb, 0xcb },
+{ 0x00, 0xcc, 0xcc },
+{ 0x00, 0xcd, 0xcd },
+{ 0x00, 0xce, 0xce },
+{ 0x00, 0xcf, 0xcf },
+{ 0x00, 0xd0, 0xd0 },
+{ 0x00, 0xd1, 0xd1 },
+{ 0x00, 0xd2, 0xd2 },
+{ 0x00, 0xd3, 0xd3 },
+{ 0x00, 0xd4, 0xd4 },
+{ 0x00, 0xd5, 0xd5 },
+{ 0x00, 0xd6, 0xd6 },
+{ 0x00, 0xd7, 0xd7 },
+{ 0x00, 0xd8, 0xd8 },
+{ 0x00, 0xd9, 0xd9 },
+{ 0x00, 0xda, 0xda },
+{ 0x00, 0xdb, 0xdb },
+{ 0x00, 0xdc, 0xdc },
+{ 0x00, 0xdd, 0xdd },
+{ 0x00, 0xde, 0xde },
+{ 0x00, 0xdf, 0xdf },
+{ 0x00, 0xe0, 0xe0 },
+{ 0x00, 0xe1, 0xe1 },
+{ 0x00, 0xe2, 0xe2 },
+{ 0x00, 0xe3, 0xe3 },
+{ 0x00, 0xe4, 0xe4 },
+{ 0x00, 0xe5, 0xe5 },
+{ 0x00, 0xe6, 0xe6 },
+{ 0x00, 0xe7, 0xe7 },
+{ 0x00, 0xe8, 0xe8 },
+{ 0x00, 0xe9, 0xe9 },
+{ 0x00, 0xea, 0xea },
+{ 0x00, 0xeb, 0xeb },
+{ 0x00, 0xec, 0xec },
+{ 0x00, 0xed, 0xed },
+{ 0x00, 0xee, 0xee },
+{ 0x00, 0xef, 0xef },
+{ 0x00, 0xf0, 0xf0 },
+{ 0x00, 0xf1, 0xf1 },
+{ 0x00, 0xf2, 0xf2 },
+{ 0x00, 0xf3, 0xf3 },
+{ 0x00, 0xf4, 0xf4 },
+{ 0x00, 0xf5, 0xf5 },
+{ 0x00, 0xf6, 0xf6 },
+{ 0x00, 0xf7, 0xf7 },
+{ 0x00, 0xf8, 0xf8 },
+{ 0x00, 0xf9, 0xf9 },
+{ 0x00, 0xfa, 0xfa },
+{ 0x00, 0xfb, 0xfb },
+{ 0x00, 0xfc, 0xfc },
+{ 0x00, 0xfd, 0xfd },
+{ 0x00, 0xfe, 0xfe },
+{ 0x00, 0xff, 0xff }
+};
+
+static struct cs_info tis620_tbl[] = {
+{ 0x00, 0x00, 0x00 },
+{ 0x00, 0x01, 0x01 },
+{ 0x00, 0x02, 0x02 },
+{ 0x00, 0x03, 0x03 },
+{ 0x00, 0x04, 0x04 },
+{ 0x00, 0x05, 0x05 },
+{ 0x00, 0x06, 0x06 },
+{ 0x00, 0x07, 0x07 },
+{ 0x00, 0x08, 0x08 },
+{ 0x00, 0x09, 0x09 },
+{ 0x00, 0x0a, 0x0a },
+{ 0x00, 0x0b, 0x0b },
+{ 0x00, 0x0c, 0x0c },
+{ 0x00, 0x0d, 0x0d },
+{ 0x00, 0x0e, 0x0e },
+{ 0x00, 0x0f, 0x0f },
+{ 0x00, 0x10, 0x10 },
+{ 0x00, 0x11, 0x11 },
+{ 0x00, 0x12, 0x12 },
+{ 0x00, 0x13, 0x13 },
+{ 0x00, 0x14, 0x14 },
+{ 0x00, 0x15, 0x15 },
+{ 0x00, 0x16, 0x16 },
+{ 0x00, 0x17, 0x17 },
+{ 0x00, 0x18, 0x18 },
+{ 0x00, 0x19, 0x19 },
+{ 0x00, 0x1a, 0x1a },
+{ 0x00, 0x1b, 0x1b },
+{ 0x00, 0x1c, 0x1c },
+{ 0x00, 0x1d, 0x1d },
+{ 0x00, 0x1e, 0x1e },
+{ 0x00, 0x1f, 0x1f },
+{ 0x00, 0x20, 0x20 },
+{ 0x00, 0x21, 0x21 },
+{ 0x00, 0x22, 0x22 },
+{ 0x00, 0x23, 0x23 },
+{ 0x00, 0x24, 0x24 },
+{ 0x00, 0x25, 0x25 },
+{ 0x00, 0x26, 0x26 },
+{ 0x00, 0x27, 0x27 },
+{ 0x00, 0x28, 0x28 },
+{ 0x00, 0x29, 0x29 },
+{ 0x00, 0x2a, 0x2a },
+{ 0x00, 0x2b, 0x2b },
+{ 0x00, 0x2c, 0x2c },
+{ 0x00, 0x2d, 0x2d },
+{ 0x00, 0x2e, 0x2e },
+{ 0x00, 0x2f, 0x2f },
+{ 0x00, 0x30, 0x30 },
+{ 0x00, 0x31, 0x31 },
+{ 0x00, 0x32, 0x32 },
+{ 0x00, 0x33, 0x33 },
+{ 0x00, 0x34, 0x34 },
+{ 0x00, 0x35, 0x35 },
+{ 0x00, 0x36, 0x36 },
+{ 0x00, 0x37, 0x37 },
+{ 0x00, 0x38, 0x38 },
+{ 0x00, 0x39, 0x39 },
+{ 0x00, 0x3a, 0x3a },
+{ 0x00, 0x3b, 0x3b },
+{ 0x00, 0x3c, 0x3c },
+{ 0x00, 0x3d, 0x3d },
+{ 0x00, 0x3e, 0x3e },
+{ 0x00, 0x3f, 0x3f },
+{ 0x00, 0x40, 0x40 },
+{ 0x01, 0x61, 0x41 },
+{ 0x01, 0x62, 0x42 },
+{ 0x01, 0x63, 0x43 },
+{ 0x01, 0x64, 0x44 },
+{ 0x01, 0x65, 0x45 },
+{ 0x01, 0x66, 0x46 },
+{ 0x01, 0x67, 0x47 },
+{ 0x01, 0x68, 0x48 },
+{ 0x01, 0x69, 0x49 },
+{ 0x01, 0x6a, 0x4a },
+{ 0x01, 0x6b, 0x4b },
+{ 0x01, 0x6c, 0x4c },
+{ 0x01, 0x6d, 0x4d },
+{ 0x01, 0x6e, 0x4e },
+{ 0x01, 0x6f, 0x4f },
+{ 0x01, 0x70, 0x50 },
+{ 0x01, 0x71, 0x51 },
+{ 0x01, 0x72, 0x52 },
+{ 0x01, 0x73, 0x53 },
+{ 0x01, 0x74, 0x54 },
+{ 0x01, 0x75, 0x55 },
+{ 0x01, 0x76, 0x56 },
+{ 0x01, 0x77, 0x57 },
+{ 0x01, 0x78, 0x58 },
+{ 0x01, 0x79, 0x59 },
+{ 0x01, 0x7a, 0x5a },
+{ 0x00, 0x5b, 0x5b },
+{ 0x00, 0x5c, 0x5c },
+{ 0x00, 0x5d, 0x5d },
+{ 0x00, 0x5e, 0x5e },
+{ 0x00, 0x5f, 0x5f },
+{ 0x00, 0x60, 0x60 },
+{ 0x00, 0x61, 0x41 },
+{ 0x00, 0x62, 0x42 },
+{ 0x00, 0x63, 0x43 },
+{ 0x00, 0x64, 0x44 },
+{ 0x00, 0x65, 0x45 },
+{ 0x00, 0x66, 0x46 },
+{ 0x00, 0x67, 0x47 },
+{ 0x00, 0x68, 0x48 },
+{ 0x00, 0x69, 0x49 },
+{ 0x00, 0x6a, 0x4a },
+{ 0x00, 0x6b, 0x4b },
+{ 0x00, 0x6c, 0x4c },
+{ 0x00, 0x6d, 0x4d },
+{ 0x00, 0x6e, 0x4e },
+{ 0x00, 0x6f, 0x4f },
+{ 0x00, 0x70, 0x50 },
+{ 0x00, 0x71, 0x51 },
+{ 0x00, 0x72, 0x52 },
+{ 0x00, 0x73, 0x53 },
+{ 0x00, 0x74, 0x54 },
+{ 0x00, 0x75, 0x55 },
+{ 0x00, 0x76, 0x56 },
+{ 0x00, 0x77, 0x57 },
+{ 0x00, 0x78, 0x58 },
+{ 0x00, 0x79, 0x59 },
+{ 0x00, 0x7a, 0x5a },
+{ 0x00, 0x7b, 0x7b },
+{ 0x00, 0x7c, 0x7c },
+{ 0x00, 0x7d, 0x7d },
+{ 0x00, 0x7e, 0x7e },
+{ 0x00, 0x7f, 0x7f },
+{ 0x00, 0x80, 0x80 },
+{ 0x00, 0x81, 0x81 },
+{ 0x00, 0x82, 0x82 },
+{ 0x00, 0x83, 0x83 },
+{ 0x00, 0x84, 0x84 },
+{ 0x00, 0x85, 0x85 },
+{ 0x00, 0x86, 0x86 },
+{ 0x00, 0x87, 0x87 },
+{ 0x00, 0x88, 0x88 },
+{ 0x00, 0x89, 0x89 },
+{ 0x00, 0x8a, 0x8a },
+{ 0x00, 0x8b, 0x8b },
+{ 0x00, 0x8c, 0x8c },
+{ 0x00, 0x8d, 0x8d },
+{ 0x00, 0x8e, 0x8e },
+{ 0x00, 0x8f, 0x8f },
+{ 0x00, 0x90, 0x90 },
+{ 0x00, 0x91, 0x91 },
+{ 0x00, 0x92, 0x92 },
+{ 0x00, 0x93, 0x93 },
+{ 0x00, 0x94, 0x94 },
+{ 0x00, 0x95, 0x95 },
+{ 0x00, 0x96, 0x96 },
+{ 0x00, 0x97, 0x97 },
+{ 0x00, 0x98, 0x98 },
+{ 0x00, 0x99, 0x99 },
+{ 0x00, 0x9a, 0x9a },
+{ 0x00, 0x9b, 0x9b },
+{ 0x00, 0x9c, 0x9c },
+{ 0x00, 0x9d, 0x9d },
+{ 0x00, 0x9e, 0x9e },
+{ 0x00, 0x9f, 0x9f },
+{ 0x00, 0xa0, 0xa0 },
+{ 0x00, 0xa1, 0xa1 },
+{ 0x00, 0xa2, 0xa2 },
+{ 0x00, 0xa3, 0xa3 },
+{ 0x00, 0xa4, 0xa4 },
+{ 0x00, 0xa5, 0xa5 },
+{ 0x00, 0xa6, 0xa6 },
+{ 0x00, 0xa7, 0xa7 },
+{ 0x00, 0xa8, 0xa8 },
+{ 0x00, 0xa9, 0xa9 },
+{ 0x00, 0xaa, 0xaa },
+{ 0x00, 0xab, 0xab },
+{ 0x00, 0xac, 0xac },
+{ 0x00, 0xad, 0xad },
+{ 0x00, 0xae, 0xae },
+{ 0x00, 0xaf, 0xaf },
+{ 0x00, 0xb0, 0xb0 },
+{ 0x00, 0xb1, 0xb1 },
+{ 0x00, 0xb2, 0xb2 },
+{ 0x00, 0xb3, 0xb3 },
+{ 0x00, 0xb4, 0xb4 },
+{ 0x00, 0xb5, 0xb5 },
+{ 0x00, 0xb6, 0xb6 },
+{ 0x00, 0xb7, 0xb7 },
+{ 0x00, 0xb8, 0xb8 },
+{ 0x00, 0xb9, 0xb9 },
+{ 0x00, 0xba, 0xba },
+{ 0x00, 0xbb, 0xbb },
+{ 0x00, 0xbc, 0xbc },
+{ 0x00, 0xbd, 0xbd },
+{ 0x00, 0xbe, 0xbe },
+{ 0x00, 0xbf, 0xbf },
+{ 0x00, 0xc0, 0xc0 },
+{ 0x00, 0xc1, 0xc1 },
+{ 0x00, 0xc2, 0xc2 },
+{ 0x00, 0xc3, 0xc3 },
+{ 0x00, 0xc4, 0xc4 },
+{ 0x00, 0xc5, 0xc5 },
+{ 0x00, 0xc6, 0xc6 },
+{ 0x00, 0xc7, 0xc7 },
+{ 0x00, 0xc8, 0xc8 },
+{ 0x00, 0xc9, 0xc9 },
+{ 0x00, 0xca, 0xca },
+{ 0x00, 0xcb, 0xcb },
+{ 0x00, 0xcc, 0xcc },
+{ 0x00, 0xcd, 0xcd },
+{ 0x00, 0xce, 0xce },
+{ 0x00, 0xcf, 0xcf },
+{ 0x00, 0xd0, 0xd0 },
+{ 0x00, 0xd1, 0xd1 },
+{ 0x00, 0xd2, 0xd2 },
+{ 0x00, 0xd3, 0xd3 },
+{ 0x00, 0xd4, 0xd4 },
+{ 0x00, 0xd5, 0xd5 },
+{ 0x00, 0xd6, 0xd6 },
+{ 0x00, 0xd7, 0xd7 },
+{ 0x00, 0xd8, 0xd8 },
+{ 0x00, 0xd9, 0xd9 },
+{ 0x00, 0xda, 0xda },
+{ 0x00, 0xdb, 0xdb },
+{ 0x00, 0xdc, 0xdc },
+{ 0x00, 0xdd, 0xdd },
+{ 0x00, 0xde, 0xde },
+{ 0x00, 0xdf, 0xdf },
+{ 0x00, 0xe0, 0xe0 },
+{ 0x00, 0xe1, 0xe1 },
+{ 0x00, 0xe2, 0xe2 },
+{ 0x00, 0xe3, 0xe3 },
+{ 0x00, 0xe4, 0xe4 },
+{ 0x00, 0xe5, 0xe5 },
+{ 0x00, 0xe6, 0xe6 },
+{ 0x00, 0xe7, 0xe7 },
+{ 0x00, 0xe8, 0xe8 },
+{ 0x00, 0xe9, 0xe9 },
+{ 0x00, 0xea, 0xea },
+{ 0x00, 0xeb, 0xeb },
+{ 0x00, 0xec, 0xec },
+{ 0x00, 0xed, 0xed },
+{ 0x00, 0xee, 0xee },
+{ 0x00, 0xef, 0xef },
+{ 0x00, 0xf0, 0xf0 },
+{ 0x00, 0xf1, 0xf1 },
+{ 0x00, 0xf2, 0xf2 },
+{ 0x00, 0xf3, 0xf3 },
+{ 0x00, 0xf4, 0xf4 },
+{ 0x00, 0xf5, 0xf5 },
+{ 0x00, 0xf6, 0xf6 },
+{ 0x00, 0xf7, 0xf7 },
+{ 0x00, 0xf8, 0xf8 },
+{ 0x00, 0xf9, 0xf9 },
+{ 0x00, 0xfa, 0xfa },
+{ 0x00, 0xfb, 0xfb },
+{ 0x00, 0xfc, 0xfc },
+{ 0x00, 0xfd, 0xfd },
+{ 0x00, 0xfe, 0xfe },
+{ 0x00, 0xff, 0xff }
+};
+
+struct enc_entry {
+ const char * enc_name;
+ struct cs_info * cs_table;
+};
+
+static struct enc_entry encds[] = {
+ {"iso88591",iso1_tbl}, //ISO-8859-1
+ {"iso88592",iso2_tbl}, //ISO-8859-2
+ {"iso88593",iso3_tbl}, //ISO-8859-3
+ {"iso88594",iso4_tbl}, //ISO-8859-4
+ {"iso88595",iso5_tbl}, //ISO-8859-5
+ {"iso88596",iso6_tbl}, //ISO-8859-6
+ {"iso88597",iso7_tbl}, //ISO-8859-7
+ {"iso88598",iso8_tbl}, //ISO-8859-8
+ {"iso88599",iso9_tbl}, //ISO-8859-9
+ {"iso885910",iso10_tbl}, //ISO-8859-10
+ {"tis620",tis620_tbl}, //TIS-620/ISO-8859-11
+ {"tis6202533",tis620_tbl}, //TIS-620/ISO-8859-11
+ {"iso885911",tis620_tbl}, //TIS-620/ISO-8859-11
+ {"iso885913", iso13_tbl}, //ISO-8859-13
+ {"iso885914", iso14_tbl}, //ISO-8859-14
+ {"iso885915", iso15_tbl}, //ISO-8859-15
+ {"koi8r",koi8r_tbl}, //KOI8-R
+ {"koi8u",koi8u_tbl}, //KOI8-U
+ {"cp1251",cp1251_tbl}, //CP-1251
+ {"microsoftcp1251",cp1251_tbl}, //microsoft-cp1251
+ {"xisciias", iscii_devanagari_tbl}, //x-iscii-as
+ {"isciidevanagari", iscii_devanagari_tbl} //ISCII-DEVANAGARI
+};
+
+/* map to lower case and remove non alphanumeric chars */
+static void toAsciiLowerAndRemoveNonAlphanumeric( const char* pName, char* pBuf )
+{
+ while ( *pName )
+ {
+ /* A-Z */
+ if ( (*pName >= 0x41) && (*pName <= 0x5A) )
+ {
+ *pBuf = (*pName)+0x20; /* toAsciiLower */
+ pBuf++;
+ }
+ /* a-z, 0-9 */
+ else if ( ((*pName >= 0x61) && (*pName <= 0x7A)) ||
+ ((*pName >= 0x30) && (*pName <= 0x39)) )
+ {
+ *pBuf = *pName;
+ pBuf++;
+ }
+
+ pName++;
+ }
+
+ *pBuf = '\0';
+}
+
+struct cs_info * get_current_cs(const char * es) {
+ char *normalized_encoding = new char[strlen(es)+1];
+ toAsciiLowerAndRemoveNonAlphanumeric(es, normalized_encoding);
+
+ struct cs_info * ccs = NULL;
+ int n = sizeof(encds) / sizeof(encds[0]);
+ for (int i = 0; i < n; i++) {
+ if (strcmp(normalized_encoding,encds[i].enc_name) == 0) {
+ ccs = encds[i].cs_table;
+ break;
+ }
+ }
+
+ delete[] normalized_encoding;
+
+ if (!ccs) {
+ HUNSPELL_WARNING(stderr, "error: unknown encoding %s: using %s as fallback\n", es, encds[0].enc_name);
+ ccs = encds[0].cs_table;
+ }
+
+ return ccs;
+}
+#else
+// XXX This function was rewritten for mozilla. Instead of storing the
+// conversion tables static in this file, create them when needed
+// with help the mozilla backend.
+struct cs_info * get_current_cs(const char * es) {
+ struct cs_info *ccs;
+
+ nsCOMPtr<nsIUnicodeEncoder> encoder;
+ nsCOMPtr<nsIUnicodeDecoder> decoder;
+
+ nsresult rv;
+ nsCOMPtr<nsICharsetConverterManager> ccm = do_GetService(kCharsetConverterManagerCID, &rv);
+ if (NS_FAILED(rv))
+ return nsnull;
+
+ rv = ccm->GetUnicodeEncoder(es, getter_AddRefs(encoder));
+ if (NS_FAILED(rv))
+ return nsnull;
+ encoder->SetOutputErrorBehavior(encoder->kOnError_Signal, nsnull, '?');
+ rv = ccm->GetUnicodeDecoder(es, getter_AddRefs(decoder));
+ if (NS_FAILED(rv))
+ return nsnull;
+ decoder->SetInputErrorBehavior(decoder->kOnError_Signal);
+
+ if (NS_FAILED(rv))
+ return nsnull;
+
+ ccs = new cs_info[256];
+
+ for (unsigned int i = 0; i <= 0xff; ++i) {
+ PRBool success = PR_FALSE;
+ // We want to find the upper/lowercase equivalents of each byte
+ // in this 1-byte character encoding. Call our encoding/decoding
+ // APIs separately for each byte since they may reject some of the
+ // bytes, and we want to handle errors separately for each byte.
+ char lower, upper;
+ do {
+ if (i == 0)
+ break;
+ const char source = char(i);
+ PRUnichar uni, uniCased;
+ PRInt32 charLength = 1, uniLength = 1;
+
+ rv = decoder->Convert(&source, &charLength, &uni, &uniLength);
+ // Explicitly check NS_OK because we don't want to allow
+ // NS_OK_UDEC_MOREOUTPUT or NS_OK_UDEC_MOREINPUT.
+ if (rv != NS_OK || charLength != 1 || uniLength != 1)
+ break;
+ uniCased = ToLowerCase(uni);
+ rv = encoder->Convert(&uniCased, &uniLength, &lower, &charLength);
+ // Explicitly check NS_OK because we don't want to allow
+ // NS_OK_UDEC_MOREOUTPUT or NS_OK_UDEC_MOREINPUT.
+ if (rv != NS_OK || charLength != 1 || uniLength != 1)
+ break;
+
+ uniCased = ToUpperCase(uni);
+ rv = encoder->Convert(&uniCased, &uniLength, &upper, &charLength);
+ // Explicitly check NS_OK because we don't want to allow
+ // NS_OK_UDEC_MOREOUTPUT or NS_OK_UDEC_MOREINPUT.
+ if (rv != NS_OK || charLength != 1 || uniLength != 1)
+ break;
+
+ success = PR_TRUE;
+ } while (0);
+
+ if (success) {
+ ccs[i].cupper = upper;
+ ccs[i].clower = lower;
+ } else {
+ ccs[i].cupper = i;
+ ccs[i].clower = i;
+ }
+
+ if (ccs[i].clower != (unsigned char)i)
+ ccs[i].ccase = true;
+ else
+ ccs[i].ccase = false;
+ }
+
+ return ccs;
+}
+#endif
+
+// primitive isalpha() replacement for tokenization
+char * get_casechars(const char * enc) {
+ struct cs_info * csconv = get_current_cs(enc);
+ char expw[MAXLNLEN];
+ char * p = expw;
+ for (int i = 0; i <= 255; i++) {
+ if ((csconv[i].cupper != csconv[i].clower)) {
+ *p = (char) i;
+ p++;
+ }
+ }
+ *p = '\0';
+#ifdef MOZILLA_CLIENT
+ delete [] csconv;
+#endif
+ return mystrdup(expw);
+}
+
+// language to encoding default map
+
+struct lang_map {
+ const char * lang;
+ int num;
+};
+
+static struct lang_map lang2enc[] = {
+{"ar", LANG_ar},
+{"az", LANG_az},
+{"az_AZ", LANG_az}, // for back-compatibility
+{"bg", LANG_bg},
+{"ca", LANG_ca},
+{"cs", LANG_cs},
+{"da", LANG_da},
+{"de", LANG_de},
+{"el", LANG_el},
+{"en", LANG_en},
+{"es", LANG_es},
+{"eu", LANG_eu},
+{"gl", LANG_gl},
+{"fr", LANG_fr},
+{"hr", LANG_hr},
+{"hu", LANG_hu},
+{"hu_HU", LANG_hu}, // for back-compatibility
+{"it", LANG_it},
+{"la", LANG_la},
+{"lv", LANG_lv},
+{"nl", LANG_nl},
+{"pl", LANG_pl},
+{"pt", LANG_pt},
+{"sv", LANG_sv},
+{"tr", LANG_tr},
+{"tr_TR", LANG_tr}, // for back-compatibility
+{"ru", LANG_ru},
+{"uk", LANG_uk}
+};
+
+
+int get_lang_num(const char * lang) {
+ int n = sizeof(lang2enc) / sizeof(lang2enc[0]);
+ for (int i = 0; i < n; i++) {
+ if (strcmp(lang, lang2enc[i].lang) == 0) {
+ return lang2enc[i].num;
+ }
+ }
+ return LANG_xx;
+}
+
+#ifndef OPENOFFICEORG
+#ifndef MOZILLA_CLIENT
+int initialize_utf_tbl() {
+ utf_tbl_count++;
+ if (utf_tbl) return 0;
+ utf_tbl = (unicode_info2 *) malloc(CONTSIZE * sizeof(unicode_info2));
+ if (utf_tbl) {
+ size_t j;
+ for (j = 0; j < CONTSIZE; j++) {
+ utf_tbl[j].cletter = 0;
+ utf_tbl[j].clower = (unsigned short) j;
+ utf_tbl[j].cupper = (unsigned short) j;
+ }
+ for (j = 0; j < UTF_LST_LEN; j++) {
+ utf_tbl[utf_lst[j].c].cletter = 1;
+ utf_tbl[utf_lst[j].c].clower = utf_lst[j].clower;
+ utf_tbl[utf_lst[j].c].cupper = utf_lst[j].cupper;
+ }
+ } else return 1;
+ return 0;
+}
+#endif
+#endif
+
+void free_utf_tbl() {
+ if (utf_tbl_count > 0) utf_tbl_count--;
+ if (utf_tbl && (utf_tbl_count == 0)) {
+ free(utf_tbl);
+ utf_tbl = NULL;
+ }
+}
+
+unsigned short unicodetoupper(unsigned short c, int langnum)
+{
+ // In Azeri and Turkish, I and i dictinct letters:
+ // There are a dotless lower case i pair of upper `I',
+ // and an upper I with dot pair of lower `i'.
+ if (c == 0x0069 && ((langnum == LANG_az) || (langnum == LANG_tr)))
+ return 0x0130;
+#ifdef OPENOFFICEORG
+ return u_toupper(c);
+#else
+#ifdef MOZILLA_CLIENT
+ return ToUpperCase((PRUnichar) c);
+#else
+ return (utf_tbl) ? utf_tbl[c].cupper : c;
+#endif
+#endif
+}
+
+unsigned short unicodetolower(unsigned short c, int langnum)
+{
+ // In Azeri and Turkish, I and i dictinct letters:
+ // There are a dotless lower case i pair of upper `I',
+ // and an upper I with dot pair of lower `i'.
+ if (c == 0x0049 && ((langnum == LANG_az) || (langnum == LANG_tr)))
+ return 0x0131;
+#ifdef OPENOFFICEORG
+ return u_tolower(c);
+#else
+#ifdef MOZILLA_CLIENT
+ return ToLowerCase((PRUnichar) c);
+#else
+ return (utf_tbl) ? utf_tbl[c].clower : c;
+#endif
+#endif
+}
+
+int unicodeisalpha(unsigned short c)
+{
+#ifdef OPENOFFICEORG
+ return u_isalpha(c);
+#else
+ return (utf_tbl) ? utf_tbl[c].cletter : 0;
+#endif
+}
+
+/* get type of capitalization */
+int get_captype(char * word, int nl, cs_info * csconv) {
+ // now determine the capitalization type of the first nl letters
+ int ncap = 0;
+ int nneutral = 0;
+ int firstcap = 0;
+ if (csconv == NULL) return NOCAP;
+ for (char * q = word; *q != '\0'; q++) {
+ if (csconv[*((unsigned char *)q)].ccase) ncap++;
+ if (csconv[*((unsigned char *)q)].cupper == csconv[*((unsigned char *)q)].clower) nneutral++;
+ }
+ if (ncap) {
+ firstcap = csconv[*((unsigned char *) word)].ccase;
+ }
+
+ // now finally set the captype
+ if (ncap == 0) {
+ return NOCAP;
+ } else if ((ncap == 1) && firstcap) {
+ return INITCAP;
+ } else if ((ncap == nl) || ((ncap + nneutral) == nl)) {
+ return ALLCAP;
+ } else if ((ncap > 1) && firstcap) {
+ return HUHINITCAP;
+ }
+ return HUHCAP;
+}
+
+int get_captype_utf8(w_char * word, int nl, int langnum) {
+ // now determine the capitalization type of the first nl letters
+ int ncap = 0;
+ int nneutral = 0;
+ int firstcap = 0;
+ unsigned short idx;
+ // don't check too long words
+ if (nl >= MAXWORDLEN) return 0;
+ // big Unicode character (non BMP area)
+ if (nl == -1) return NOCAP;
+ for (int i = 0; i < nl; i++) {
+ idx = (word[i].h << 8) + word[i].l;
+ if (idx != unicodetolower(idx, langnum)) ncap++;
+ if (unicodetoupper(idx, langnum) == unicodetolower(idx, langnum)) nneutral++;
+ }
+ if (ncap) {
+ idx = (word[0].h << 8) + word[0].l;
+ firstcap = (idx != unicodetolower(idx, langnum));
+ }
+
+ // now finally set the captype
+ if (ncap == 0) {
+ return NOCAP;
+ } else if ((ncap == 1) && firstcap) {
+ return INITCAP;
+ } else if ((ncap == nl) || ((ncap + nneutral) == nl)) {
+ return ALLCAP;
+ } else if ((ncap > 1) && firstcap) {
+ return HUHINITCAP;
+ }
+ return HUHCAP;
+}
+
+
+// strip all ignored characters in the string
+void remove_ignored_chars_utf(char * word, unsigned short ignored_chars[], int ignored_len)
+{
+ w_char w[MAXWORDLEN];
+ w_char w2[MAXWORDLEN];
+ int i;
+ int j;
+ int len = u8_u16(w, MAXWORDLEN, word);
+ for (i = 0, j = 0; i < len; i++) {
+ if (!flag_bsearch(ignored_chars, ((unsigned short *) w)[i], ignored_len)) {
+ w2[j] = w[i];
+ j++;
+ }
+ }
+ if (j < i) u16_u8(word, MAXWORDUTF8LEN, w2, j);
+}
+
+// strip all ignored characters in the string
+void remove_ignored_chars(char * word, char * ignored_chars)
+{
+ for (char * p = word; *p != '\0'; p++) {
+ if (!strchr(ignored_chars, *p)) {
+ *word = *p;
+ word++;
+ }
+ }
+ *word = '\0';
+}
+
+int parse_string(char * line, char ** out, int ln)
+{
+ char * tp = line;
+ char * piece;
+ int i = 0;
+ int np = 0;
+ if (*out) {
+ HUNSPELL_WARNING(stderr, "error: line %d: multiple definitions\n", ln);
+ return 1;
+ }
+ piece = mystrsep(&tp, 0);
+ while (piece) {
+ if (*piece != '\0') {
+ switch(i) {
+ case 0: { np++; break; }
+ case 1: {
+ *out = mystrdup(piece);
+ if (!*out) return 1;
+ np++;
+ break;
+ }
+ default: break;
+ }
+ i++;
+ }
+ // free(piece);
+ piece = mystrsep(&tp, 0);
+ }
+ if (np != 2) {
+ HUNSPELL_WARNING(stderr, "error: line %d: missing data\n", ln);
+ return 1;
+ }
+ return 0;
+}
+
+int parse_array(char * line, char ** out, unsigned short ** out_utf16,
+ int * out_utf16_len, int utf8, int ln) {
+ if (parse_string(line, out, ln)) return 1;
+ if (utf8) {
+ w_char w[MAXWORDLEN];
+ int n = u8_u16(w, MAXWORDLEN, *out);
+ if (n > 0) {
+ flag_qsort((unsigned short *) w, 0, n);
+ *out_utf16 = (unsigned short *) malloc(n * sizeof(unsigned short));
+ if (!*out_utf16) return 1;
+ memcpy(*out_utf16, w, n * sizeof(unsigned short));
+ }
+ *out_utf16_len = n;
+ }
+ return 0;
+}
diff --git a/Plugins/spellchecker/hunspell/csutil.hxx b/Plugins/spellchecker/hunspell/csutil.hxx
new file mode 100644
index 0000000..7bd0b91
--- /dev/null
+++ b/Plugins/spellchecker/hunspell/csutil.hxx
@@ -0,0 +1,220 @@
+#ifndef __CSUTILHXX__
+#define __CSUTILHXX__
+
+#include "hunvisapi.h"
+
+// First some base level utility routines
+
+#include <string.h>
+#include "w_char.hxx"
+#include "htypes.hxx"
+
+#ifdef MOZILLA_CLIENT
+#include "nscore.h" // for mozalloc headers
+#endif
+
+// casing
+#define NOCAP 0
+#define INITCAP 1
+#define ALLCAP 2
+#define HUHCAP 3
+#define HUHINITCAP 4
+
+// default encoding and keystring
+#define SPELL_ENCODING "ISO8859-1"
+#define SPELL_KEYSTRING "qwertyuiop|asdfghjkl|zxcvbnm"
+
+// default morphological fields
+#define MORPH_STEM "st:"
+#define MORPH_ALLOMORPH "al:"
+#define MORPH_POS "po:"
+#define MORPH_DERI_PFX "dp:"
+#define MORPH_INFL_PFX "ip:"
+#define MORPH_TERM_PFX "tp:"
+#define MORPH_DERI_SFX "ds:"
+#define MORPH_INFL_SFX "is:"
+#define MORPH_TERM_SFX "ts:"
+#define MORPH_SURF_PFX "sp:"
+#define MORPH_FREQ "fr:"
+#define MORPH_PHON "ph:"
+#define MORPH_HYPH "hy:"
+#define MORPH_PART "pa:"
+#define MORPH_FLAG "fl:"
+#define MORPH_HENTRY "_H:"
+#define MORPH_TAG_LEN strlen(MORPH_STEM)
+
+#define MSEP_FLD ' '
+#define MSEP_REC '\n'
+#define MSEP_ALT '\v'
+
+// default flags
+#define DEFAULTFLAGS 65510
+#define FORBIDDENWORD 65510
+#define ONLYUPCASEFLAG 65511
+
+// convert UTF-16 characters to UTF-8
+LIBHUNSPELL_DLL_EXPORTED char * u16_u8(char * dest, int size, const w_char * src, int srclen);
+
+// convert UTF-8 characters to UTF-16
+LIBHUNSPELL_DLL_EXPORTED int u8_u16(w_char * dest, int size, const char * src);
+
+// sort 2-byte vector
+LIBHUNSPELL_DLL_EXPORTED void flag_qsort(unsigned short flags[], int begin, int end);
+
+// binary search in 2-byte vector
+LIBHUNSPELL_DLL_EXPORTED int flag_bsearch(unsigned short flags[], unsigned short flag, int right);
+
+// remove end of line char(s)
+LIBHUNSPELL_DLL_EXPORTED void mychomp(char * s);
+
+// duplicate string
+LIBHUNSPELL_DLL_EXPORTED char * mystrdup(const char * s);
+
+// strcat for limited length destination string
+LIBHUNSPELL_DLL_EXPORTED char * mystrcat(char * dest, const char * st, int max);
+
+// duplicate reverse of string
+LIBHUNSPELL_DLL_EXPORTED char * myrevstrdup(const char * s);
+
+// parse into tokens with char delimiter
+LIBHUNSPELL_DLL_EXPORTED char * mystrsep(char ** sptr, const char delim);
+// parse into tokens with char delimiter
+LIBHUNSPELL_DLL_EXPORTED char * mystrsep2(char ** sptr, const char delim);
+
+// parse into tokens with char delimiter
+LIBHUNSPELL_DLL_EXPORTED char * mystrrep(char *, const char *, const char *);
+
+// append s to ends of every lines in text
+LIBHUNSPELL_DLL_EXPORTED void strlinecat(char * lines, const char * s);
+
+// tokenize into lines with new line
+LIBHUNSPELL_DLL_EXPORTED int line_tok(const char * text, char *** lines, char breakchar);
+
+// tokenize into lines with new line and uniq in place
+LIBHUNSPELL_DLL_EXPORTED char * line_uniq(char * text, char breakchar);
+LIBHUNSPELL_DLL_EXPORTED char * line_uniq_app(char ** text, char breakchar);
+
+// change oldchar to newchar in place
+LIBHUNSPELL_DLL_EXPORTED char * tr(char * text, char oldc, char newc);
+
+// reverse word
+LIBHUNSPELL_DLL_EXPORTED int reverseword(char *);
+
+// reverse word
+LIBHUNSPELL_DLL_EXPORTED int reverseword_utf(char *);
+
+// remove duplicates
+LIBHUNSPELL_DLL_EXPORTED int uniqlist(char ** list, int n);
+
+// free character array list
+LIBHUNSPELL_DLL_EXPORTED void freelist(char *** list, int n);
+
+// character encoding information
+struct cs_info {
+ unsigned char ccase;
+ unsigned char clower;
+ unsigned char cupper;
+};
+
+LIBHUNSPELL_DLL_EXPORTED int initialize_utf_tbl();
+LIBHUNSPELL_DLL_EXPORTED void free_utf_tbl();
+LIBHUNSPELL_DLL_EXPORTED unsigned short unicodetoupper(unsigned short c, int langnum);
+LIBHUNSPELL_DLL_EXPORTED unsigned short unicodetolower(unsigned short c, int langnum);
+LIBHUNSPELL_DLL_EXPORTED int unicodeisalpha(unsigned short c);
+
+LIBHUNSPELL_DLL_EXPORTED struct cs_info * get_current_cs(const char * es);
+
+// get language identifiers of language codes
+LIBHUNSPELL_DLL_EXPORTED int get_lang_num(const char * lang);
+
+// get characters of the given 8bit encoding with lower- and uppercase forms
+LIBHUNSPELL_DLL_EXPORTED char * get_casechars(const char * enc);
+
+// convert null terminated string to all caps using encoding
+LIBHUNSPELL_DLL_EXPORTED void enmkallcap(char * d, const char * p, const char * encoding);
+
+// convert null terminated string to all little using encoding
+LIBHUNSPELL_DLL_EXPORTED void enmkallsmall(char * d, const char * p, const char * encoding);
+
+// convert null terminated string to have initial capital using encoding
+LIBHUNSPELL_DLL_EXPORTED void enmkinitcap(char * d, const char * p, const char * encoding);
+
+// convert null terminated string to all caps
+LIBHUNSPELL_DLL_EXPORTED void mkallcap(char * p, const struct cs_info * csconv);
+
+// convert null terminated string to all little
+LIBHUNSPELL_DLL_EXPORTED void mkallsmall(char * p, const struct cs_info * csconv);
+
+// convert null terminated string to have initial capital
+LIBHUNSPELL_DLL_EXPORTED void mkinitcap(char * p, const struct cs_info * csconv);
+
+// convert first nc characters of UTF-8 string to little
+LIBHUNSPELL_DLL_EXPORTED void mkallsmall_utf(w_char * u, int nc, int langnum);
+
+// convert first nc characters of UTF-8 string to capital
+LIBHUNSPELL_DLL_EXPORTED void mkallcap_utf(w_char * u, int nc, int langnum);
+
+// get type of capitalization
+LIBHUNSPELL_DLL_EXPORTED int get_captype(char * q, int nl, cs_info *);
+
+// get type of capitalization (UTF-8)
+LIBHUNSPELL_DLL_EXPORTED int get_captype_utf8(w_char * q, int nl, int langnum);
+
+// strip all ignored characters in the string
+LIBHUNSPELL_DLL_EXPORTED void remove_ignored_chars_utf(char * word, unsigned short ignored_chars[], int ignored_len);
+
+// strip all ignored characters in the string
+LIBHUNSPELL_DLL_EXPORTED void remove_ignored_chars(char * word, char * ignored_chars);
+
+LIBHUNSPELL_DLL_EXPORTED int parse_string(char * line, char ** out, int ln);
+
+LIBHUNSPELL_DLL_EXPORTED int parse_array(char * line, char ** out, unsigned short ** out_utf16,
+ int * out_utf16_len, int utf8, int ln);
+
+LIBHUNSPELL_DLL_EXPORTED int fieldlen(const char * r);
+LIBHUNSPELL_DLL_EXPORTED char * copy_field(char * dest, const char * morph, const char * var);
+
+LIBHUNSPELL_DLL_EXPORTED int morphcmp(const char * s, const char * t);
+
+LIBHUNSPELL_DLL_EXPORTED int get_sfxcount(const char * morph);
+
+// conversion function for protected memory
+LIBHUNSPELL_DLL_EXPORTED void store_pointer(char * dest, char * source);
+
+// conversion function for protected memory
+LIBHUNSPELL_DLL_EXPORTED char * get_stored_pointer(const char * s);
+
+// hash entry macros
+LIBHUNSPELL_DLL_EXPORTED inline char* HENTRY_DATA(struct hentry *h)
+{
+ char *ret;
+ if (!h->var)
+ ret = NULL;
+ else if (h->var & H_OPT_ALIASM)
+ ret = get_stored_pointer(HENTRY_WORD(h) + h->blen + 1);
+ else
+ ret = HENTRY_WORD(h) + h->blen + 1;
+ return ret;
+}
+
+// NULL-free version for warning-free OOo build
+LIBHUNSPELL_DLL_EXPORTED inline const char* HENTRY_DATA2(const struct hentry *h)
+{
+ const char *ret;
+ if (!h->var)
+ ret = "";
+ else if (h->var & H_OPT_ALIASM)
+ ret = get_stored_pointer(HENTRY_WORD(h) + h->blen + 1);
+ else
+ ret = HENTRY_WORD(h) + h->blen + 1;
+ return ret;
+}
+
+LIBHUNSPELL_DLL_EXPORTED inline char* HENTRY_FIND(struct hentry *h, const char *p)
+{
+ return (HENTRY_DATA(h) ? strstr(HENTRY_DATA(h), p) : NULL);
+}
+
+#define w_char_eq(a,b) (((a).l == (b).l) && ((a).h == (b).h))
+
+#endif
diff --git a/Plugins/spellchecker/hunspell/dictmgr.cxx b/Plugins/spellchecker/hunspell/dictmgr.cxx
new file mode 100644
index 0000000..b4a15b1
--- /dev/null
+++ b/Plugins/spellchecker/hunspell/dictmgr.cxx
@@ -0,0 +1,180 @@
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdio.h>
+
+#include "dictmgr.hxx"
+
+DictMgr::DictMgr(const char * dictpath, const char * etype) : numdict(0)
+{
+ // load list of etype entries
+ pdentry = (dictentry *)malloc(MAXDICTIONARIES*sizeof(struct dictentry));
+ if (pdentry) {
+ if (parse_file(dictpath, etype)) {
+ numdict = 0;
+ // no dictionary.lst found is okay
+ }
+ }
+}
+
+
+DictMgr::~DictMgr()
+{
+ dictentry * pdict = NULL;
+ if (pdentry) {
+ pdict = pdentry;
+ for (int i=0;i<numdict;i++) {
+ if (pdict->lang) {
+ free(pdict->lang);
+ pdict->lang = NULL;
+ }
+ if (pdict->region) {
+ free(pdict->region);
+ pdict->region=NULL;
+ }
+ if (pdict->filename) {
+ free(pdict->filename);
+ pdict->filename = NULL;
+ }
+ pdict++;
+ }
+ free(pdentry);
+ pdentry = NULL;
+ pdict = NULL;
+ }
+ numdict = 0;
+}
+
+
+// read in list of etype entries and build up structure to describe them
+int DictMgr::parse_file(const char * dictpath, const char * etype)
+{
+
+ int i;
+ char line[MAXDICTENTRYLEN+1];
+ dictentry * pdict = pdentry;
+
+ // open the dictionary list file
+ FILE * dictlst;
+ dictlst = fopen(dictpath,"r");
+ if (!dictlst) {
+ return 1;
+ }
+
+ // step one is to parse the dictionary list building up the
+ // descriptive structures
+
+ // read in each line ignoring any that dont start with etype
+ while (fgets(line,MAXDICTENTRYLEN,dictlst)) {
+ mychomp(line);
+
+ /* parse in a dictionary entry */
+ if (strncmp(line,etype,4) == 0) {
+ if (numdict < MAXDICTIONARIES) {
+ char * tp = line;
+ char * piece;
+ i = 0;
+ while ((piece=mystrsep(&tp,' '))) {
+ if (*piece != '\0') {
+ switch(i) {
+ case 0: break;
+ case 1: pdict->lang = mystrdup(piece); break;
+ case 2: if (strcmp (piece, "ANY") == 0)
+ pdict->region = mystrdup("");
+ else
+ pdict->region = mystrdup(piece);
+ break;
+ case 3: pdict->filename = mystrdup(piece); break;
+ default: break;
+ }
+ i++;
+ }
+ free(piece);
+ }
+ if (i == 4) {
+ numdict++;
+ pdict++;
+ } else {
+ switch (i) {
+ case 3:
+ free(pdict->region);
+ pdict->region=NULL;
+ case 2: //deliberate fallthrough
+ free(pdict->lang);
+ pdict->lang=NULL;
+ default:
+ break;
+ }
+ fprintf(stderr,"dictionary list corruption in line \"%s\"\n",line);
+ fflush(stderr);
+ }
+ }
+ }
+ }
+ fclose(dictlst);
+ return 0;
+}
+
+// return text encoding of dictionary
+int DictMgr::get_list(dictentry ** ppentry)
+{
+ *ppentry = pdentry;
+ return numdict;
+}
+
+
+
+// strip strings into token based on single char delimiter
+// acts like strsep() but only uses a delim char and not
+// a delim string
+
+char * DictMgr::mystrsep(char ** stringp, const char delim)
+{
+ char * rv = NULL;
+ char * mp = *stringp;
+ size_t n = strlen(mp);
+ if (n > 0) {
+ char * dp = (char *)memchr(mp,(int)((unsigned char)delim),n);
+ if (dp) {
+ *stringp = dp+1;
+ size_t nc = dp - mp;
+ rv = (char *) malloc(nc+1);
+ if (rv) {
+ memcpy(rv,mp,nc);
+ *(rv+nc) = '\0';
+ }
+ } else {
+ rv = (char *) malloc(n+1);
+ if (rv) {
+ memcpy(rv, mp, n);
+ *(rv+n) = '\0';
+ *stringp = mp + n;
+ }
+ }
+ }
+ return rv;
+}
+
+
+// replaces strdup with ansi version
+char * DictMgr::mystrdup(const char * s)
+{
+ char * d = NULL;
+ if (s) {
+ int sl = strlen(s)+1;
+ d = (char *) malloc(sl);
+ if (d) memcpy(d,s,sl);
+ }
+ return d;
+}
+
+
+// remove cross-platform text line end characters
+void DictMgr:: mychomp(char * s)
+{
+ int k = strlen(s);
+ if ((k > 0) && ((*(s+k-1)=='\r') || (*(s+k-1)=='\n'))) *(s+k-1) = '\0';
+ if ((k > 1) && (*(s+k-2) == '\r')) *(s+k-2) = '\0';
+}
+
diff --git a/Plugins/spellchecker/hunspell/dictmgr.hxx b/Plugins/spellchecker/hunspell/dictmgr.hxx
new file mode 100644
index 0000000..bb197f8
--- /dev/null
+++ b/Plugins/spellchecker/hunspell/dictmgr.hxx
@@ -0,0 +1,36 @@
+#ifndef _DICTMGR_HXX_
+#define _DICTMGR_HXX_
+
+#include "hunvisapi.h"
+
+#define MAXDICTIONARIES 100
+#define MAXDICTENTRYLEN 1024
+
+struct dictentry {
+ char * filename;
+ char * lang;
+ char * region;
+};
+
+
+class LIBHUNSPELL_DLL_EXPORTED DictMgr
+{
+
+ int numdict;
+ dictentry * pdentry;
+
+public:
+
+ DictMgr(const char * dictpath, const char * etype);
+ ~DictMgr();
+ int get_list(dictentry** ppentry);
+
+private:
+ int parse_file(const char * dictpath, const char * etype);
+ char * mystrsep(char ** stringp, const char delim);
+ char * mystrdup(const char * s);
+ void mychomp(char * s);
+
+};
+
+#endif
diff --git a/Plugins/spellchecker/hunspell/filemgr.cxx b/Plugins/spellchecker/hunspell/filemgr.cxx
new file mode 100644
index 0000000..5fb82bc
--- /dev/null
+++ b/Plugins/spellchecker/hunspell/filemgr.cxx
@@ -0,0 +1,49 @@
+#include "license.hunspell"
+#include "license.myspell"
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "filemgr.hxx"
+
+int FileMgr::fail(const char * err, const char * par) {
+ fprintf(stderr, err, par);
+ return -1;
+}
+
+FileMgr::FileMgr(const char * file, const char * key) {
+ linenum = 0;
+ hin = NULL;
+ fin = fopen(file, "r");
+ if (!fin) {
+ // check hzipped file
+ char * st = (char *) malloc(strlen(file) + strlen(HZIP_EXTENSION) + 1);
+ if (st) {
+ strcpy(st, file);
+ strcat(st, HZIP_EXTENSION);
+ hin = new Hunzip(st, key);
+ free(st);
+ }
+ }
+ if (!fin && !hin) fail(MSG_OPEN, file);
+}
+
+FileMgr::~FileMgr()
+{
+ if (fin) fclose(fin);
+ if (hin) delete hin;
+}
+
+char * FileMgr::getline() {
+ const char * l;
+ linenum++;
+ if (fin) return fgets(in, BUFSIZE - 1, fin);
+ if (hin && (l = hin->getline())) return strcpy(in, l);
+ linenum--;
+ return NULL;
+}
+
+int FileMgr::getlinenum() {
+ return linenum;
+}
diff --git a/Plugins/spellchecker/hunspell/filemgr.hxx b/Plugins/spellchecker/hunspell/filemgr.hxx
new file mode 100644
index 0000000..94cb723
--- /dev/null
+++ b/Plugins/spellchecker/hunspell/filemgr.hxx
@@ -0,0 +1,25 @@
+/* file manager class - read lines of files [filename] OR [filename.hz] */
+#ifndef _FILEMGR_HXX_
+#define _FILEMGR_HXX_
+
+#include "hunvisapi.h"
+
+#include "hunzip.hxx"
+#include <stdio.h>
+
+class LIBHUNSPELL_DLL_EXPORTED FileMgr
+{
+protected:
+ FILE * fin;
+ Hunzip * hin;
+ char in[BUFSIZE + 50]; // input buffer
+ int fail(const char * err, const char * par);
+ int linenum;
+
+public:
+ FileMgr(const char * filename, const char * key = NULL);
+ ~FileMgr();
+ char * getline();
+ int getlinenum();
+};
+#endif
diff --git a/Plugins/spellchecker/hunspell/hashmgr.cxx b/Plugins/spellchecker/hunspell/hashmgr.cxx
new file mode 100644
index 0000000..ea93b87
--- /dev/null
+++ b/Plugins/spellchecker/hunspell/hashmgr.cxx
@@ -0,0 +1,928 @@
+#include "license.hunspell"
+#include "license.myspell"
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <ctype.h>
+
+#include "hashmgr.hxx"
+#include "csutil.hxx"
+#include "atypes.hxx"
+
+// build a hash table from a munched word list
+
+HashMgr::HashMgr(const char * tpath, const char * apath, const char * key)
+{
+ tablesize = 0;
+ tableptr = NULL;
+ flag_mode = FLAG_CHAR;
+ complexprefixes = 0;
+ utf8 = 0;
+ langnum = 0;
+ lang = NULL;
+ enc = NULL;
+ csconv = 0;
+ ignorechars = NULL;
+ ignorechars_utf16 = NULL;
+ ignorechars_utf16_len = 0;
+ numaliasf = 0;
+ aliasf = NULL;
+ numaliasm = 0;
+ aliasm = NULL;
+ forbiddenword = FORBIDDENWORD; // forbidden word signing flag
+ load_config(apath, key);
+ int ec = load_tables(tpath, key);
+ if (ec) {
+ /* error condition - what should we do here */
+ HUNSPELL_WARNING(stderr, "Hash Manager Error : %d\n",ec);
+ if (tableptr) {
+ free(tableptr);
+ tableptr = NULL;
+ }
+ tablesize = 0;
+ }
+}
+
+
+HashMgr::~HashMgr()
+{
+ if (tableptr) {
+ // now pass through hash table freeing up everything
+ // go through column by column of the table
+ for (int i=0; i < tablesize; i++) {
+ struct hentry * pt = tableptr[i];
+ struct hentry * nt = NULL;
+ while(pt) {
+ nt = pt->next;
+ if (pt->astr && (!aliasf || TESTAFF(pt->astr, ONLYUPCASEFLAG, pt->alen))) free(pt->astr);
+ free(pt);
+ pt = nt;
+ }
+ }
+ free(tableptr);
+ }
+ tablesize = 0;
+
+ if (aliasf) {
+ for (int j = 0; j < (numaliasf); j++) free(aliasf[j]);
+ free(aliasf);
+ aliasf = NULL;
+ if (aliasflen) {
+ free(aliasflen);
+ aliasflen = NULL;
+ }
+ }
+ if (aliasm) {
+ for (int j = 0; j < (numaliasm); j++) free(aliasm[j]);
+ free(aliasm);
+ aliasm = NULL;
+ }
+
+#ifndef OPENOFFICEORG
+#ifndef MOZILLA_CLIENT
+ if (utf8) free_utf_tbl();
+#endif
+#endif
+
+ if (enc) free(enc);
+ if (lang) free(lang);
+
+ if (ignorechars) free(ignorechars);
+ if (ignorechars_utf16) free(ignorechars_utf16);
+
+#ifdef MOZILLA_CLIENT
+ delete [] csconv;
+#endif
+}
+
+// lookup a root word in the hashtable
+
+struct hentry * HashMgr::lookup(const char *word) const
+{
+ struct hentry * dp;
+ if (tableptr) {
+ dp = tableptr[hash(word)];
+ if (!dp) return NULL;
+ for ( ; dp != NULL; dp = dp->next) {
+ if (strcmp(word, dp->word) == 0) return dp;
+ }
+ }
+ return NULL;
+}
+
+// add a word to the hash table (private)
+int HashMgr::add_word(const char * word, int wbl, int wcl, unsigned short * aff,
+ int al, const char * desc, bool onlyupcase)
+{
+ bool upcasehomonym = false;
+ int descl = desc ? (aliasm ? sizeof(short) : strlen(desc) + 1) : 0;
+ // variable-length hash record with word and optional fields
+ struct hentry* hp =
+ (struct hentry *) malloc (sizeof(struct hentry) + wbl + descl);
+ if (!hp) return 1;
+ char * hpw = hp->word;
+ strcpy(hpw, word);
+ if (ignorechars != NULL) {
+ if (utf8) {
+ remove_ignored_chars_utf(hpw, ignorechars_utf16, ignorechars_utf16_len);
+ } else {
+ remove_ignored_chars(hpw, ignorechars);
+ }
+ }
+ if (complexprefixes) {
+ if (utf8) reverseword_utf(hpw); else reverseword(hpw);
+ }
+
+ int i = hash(hpw);
+
+ hp->blen = (unsigned char) wbl;
+ hp->clen = (unsigned char) wcl;
+ hp->alen = (short) al;
+ hp->astr = aff;
+ hp->next = NULL;
+ hp->next_homonym = NULL;
+
+ // store the description string or its pointer
+ if (desc) {
+ hp->var = H_OPT;
+ if (aliasm) {
+ hp->var += H_OPT_ALIASM;
+ store_pointer(hpw + wbl + 1, get_aliasm(atoi(desc)));
+ } else {
+ strcpy(hpw + wbl + 1, desc);
+ if (complexprefixes) {
+ if (utf8) reverseword_utf(HENTRY_DATA(hp));
+ else reverseword(HENTRY_DATA(hp));
+ }
+ }
+ if (strstr(HENTRY_DATA(hp), MORPH_PHON)) hp->var += H_OPT_PHON;
+ } else hp->var = 0;
+
+ struct hentry * dp = tableptr[i];
+ if (!dp) {
+ tableptr[i] = hp;
+ return 0;
+ }
+ while (dp->next != NULL) {
+ if ((!dp->next_homonym) && (strcmp(hp->word, dp->word) == 0)) {
+ // remove hidden onlyupcase homonym
+ if (!onlyupcase) {
+ if ((dp->astr) && TESTAFF(dp->astr, ONLYUPCASEFLAG, dp->alen)) {
+ free(dp->astr);
+ dp->astr = hp->astr;
+ dp->alen = hp->alen;
+ free(hp);
+ return 0;
+ } else {
+ dp->next_homonym = hp;
+ }
+ } else {
+ upcasehomonym = true;
+ }
+ }
+ dp=dp->next;
+ }
+ if (strcmp(hp->word, dp->word) == 0) {
+ // remove hidden onlyupcase homonym
+ if (!onlyupcase) {
+ if ((dp->astr) && TESTAFF(dp->astr, ONLYUPCASEFLAG, dp->alen)) {
+ free(dp->astr);
+ dp->astr = hp->astr;
+ dp->alen = hp->alen;
+ free(hp);
+ return 0;
+ } else {
+ dp->next_homonym = hp;
+ }
+ } else {
+ upcasehomonym = true;
+ }
+ }
+ if (!upcasehomonym) {
+ dp->next = hp;
+ } else {
+ // remove hidden onlyupcase homonym
+ if (hp->astr) free(hp->astr);
+ free(hp);
+ }
+ return 0;
+}
+
+int HashMgr::add_hidden_capitalized_word(char * word, int wbl, int wcl,
+ unsigned short * flags, int al, char * dp, int captype)
+{
+ // add inner capitalized forms to handle the following allcap forms:
+ // Mixed caps: OpenOffice.org -> OPENOFFICE.ORG
+ // Allcaps with suffixes: CIA's -> CIA'S
+ if (((captype == HUHCAP) || (captype == HUHINITCAP) ||
+ ((captype == ALLCAP) && (flags != NULL))) &&
+ !((flags != NULL) && TESTAFF(flags, forbiddenword, al))) {
+ unsigned short * flags2 = (unsigned short *) malloc (sizeof(unsigned short) * (al+1));
+ if (!flags2) return 1;
+ if (al) memcpy(flags2, flags, al * sizeof(unsigned short));
+ flags2[al] = ONLYUPCASEFLAG;
+ if (utf8) {
+ char st[BUFSIZE];
+ w_char w[BUFSIZE];
+ int wlen = u8_u16(w, BUFSIZE, word);
+ mkallsmall_utf(w, wlen, langnum);
+ mkallcap_utf(w, 1, langnum);
+ u16_u8(st, BUFSIZE, w, wlen);
+ return add_word(st,wbl,wcl,flags2,al+1,dp, true);
+ } else {
+ mkallsmall(word, csconv);
+ mkinitcap(word, csconv);
+ return add_word(word,wbl,wcl,flags2,al+1,dp, true);
+ }
+ }
+ return 0;
+}
+
+// detect captype and modify word length for UTF-8 encoding
+int HashMgr::get_clen_and_captype(const char * word, int wbl, int * captype) {
+ int len;
+ if (utf8) {
+ w_char dest_utf[BUFSIZE];
+ len = u8_u16(dest_utf, BUFSIZE, word);
+ *captype = get_captype_utf8(dest_utf, len, langnum);
+ } else {
+ len = wbl;
+ *captype = get_captype((char *) word, len, csconv);
+ }
+ return len;
+}
+
+// remove word (personal dictionary function for standalone applications)
+int HashMgr::remove(const char * word)
+{
+ struct hentry * dp = lookup(word);
+ while (dp) {
+ if (dp->alen == 0 || !TESTAFF(dp->astr, forbiddenword, dp->alen)) {
+ unsigned short * flags =
+ (unsigned short *) malloc(sizeof(short) * (dp->alen + 1));
+ if (!flags) return 1;
+ for (int i = 0; i < dp->alen; i++) flags[i] = dp->astr[i];
+ flags[dp->alen] = forbiddenword;
+ dp->astr = flags;
+ dp->alen++;
+ flag_qsort(flags, 0, dp->alen);
+ }
+ dp = dp->next_homonym;
+ }
+ return 0;
+}
+
+/* remove forbidden flag to add a personal word to the hash */
+int HashMgr::remove_forbidden_flag(const char * word) {
+ struct hentry * dp = lookup(word);
+ if (!dp) return 1;
+ while (dp) {
+ if (dp->astr && TESTAFF(dp->astr, forbiddenword, dp->alen)) {
+ if (dp->alen == 1) dp->alen = 0; // XXX forbidden words of personal dic.
+ else {
+ unsigned short * flags2 =
+ (unsigned short *) malloc(sizeof(short) * (dp->alen - 1));
+ if (!flags2) return 1;
+ int i, j = 0;
+ for (i = 0; i < dp->alen; i++) {
+ if (dp->astr[i] != forbiddenword) flags2[j++] = dp->astr[i];
+ }
+ dp->alen--;
+ dp->astr = flags2; // XXX allowed forbidden words
+ }
+ }
+ dp = dp->next_homonym;
+ }
+ return 0;
+}
+
+// add a custom dic. word to the hash table (public)
+int HashMgr::add(const char * word)
+{
+ unsigned short * flags = NULL;
+ int al = 0;
+ if (remove_forbidden_flag(word)) {
+ int captype;
+ int wbl = strlen(word);
+ int wcl = get_clen_and_captype(word, wbl, &captype);
+ add_word(word, wbl, wcl, flags, al, NULL, false);
+ return add_hidden_capitalized_word((char *) word, wbl, wcl, flags, al, NULL, captype);
+ }
+ return 0;
+}
+
+int HashMgr::add_with_affix(const char * word, const char * example)
+{
+ // detect captype and modify word length for UTF-8 encoding
+ struct hentry * dp = lookup(example);
+ remove_forbidden_flag(word);
+ if (dp && dp->astr) {
+ int captype;
+ int wbl = strlen(word);
+ int wcl = get_clen_and_captype(word, wbl, &captype);
+ if (aliasf) {
+ add_word(word, wbl, wcl, dp->astr, dp->alen, NULL, false);
+ } else {
+ unsigned short * flags = (unsigned short *) malloc (dp->alen * sizeof(short));
+ if (flags) {
+ memcpy((void *) flags, (void *) dp->astr, dp->alen * sizeof(short));
+ add_word(word, wbl, wcl, flags, dp->alen, NULL, false);
+ } else return 1;
+ }
+ return add_hidden_capitalized_word((char *) word, wbl, wcl, dp->astr, dp->alen, NULL, captype);
+ }
+ return 1;
+}
+
+// walk the hash table entry by entry - null at end
+// initialize: col=-1; hp = NULL; hp = walk_hashtable(&col, hp);
+struct hentry * HashMgr::walk_hashtable(int &col, struct hentry * hp) const
+{
+ if (hp && hp->next != NULL) return hp->next;
+ for (col++; col < tablesize; col++) {
+ if (tableptr[col]) return tableptr[col];
+ }
+ // null at end and reset to start
+ col = -1;
+ return NULL;
+}
+
+// load a munched word list and build a hash table on the fly
+int HashMgr::load_tables(const char * tpath, const char * key)
+{
+ int al;
+ char * ap;
+ char * dp;
+ char * dp2;
+ unsigned short * flags;
+ char * ts;
+
+ // open dictionary file
+ FileMgr * dict = new FileMgr(tpath, key);
+ if (dict == NULL) return 1;
+
+ // first read the first line of file to get hash table size */
+ if (!(ts = dict->getline())) {
+ HUNSPELL_WARNING(stderr, "error: empty dic file\n");
+ delete dict;
+ return 2;
+ }
+ mychomp(ts);
+
+ /* remove byte order mark */
+ if (strncmp(ts,"\xEF\xBB\xBF",3) == 0) {
+ memmove(ts, ts+3, strlen(ts+3)+1);
+ // warning: dic file begins with byte order mark: possible incompatibility with old Hunspell versions
+ }
+
+ tablesize = atoi(ts);
+ if (tablesize == 0) {
+ HUNSPELL_WARNING(stderr, "error: line 1: missing or bad word count in the dic file\n");
+ delete dict;
+ return 4;
+ }
+ tablesize = tablesize + 5 + USERWORD;
+ if ((tablesize %2) == 0) tablesize++;
+
+ // allocate the hash table
+ tableptr = (struct hentry **) malloc(tablesize * sizeof(struct hentry *));
+ if (! tableptr) {
+ delete dict;
+ return 3;
+ }
+ for (int i=0; i<tablesize; i++) tableptr[i] = NULL;
+
+ // loop through all words on much list and add to hash
+ // table and create word and affix strings
+
+ while ((ts = dict->getline())) {
+ mychomp(ts);
+ // split each line into word and morphological description
+ dp = ts;
+ while ((dp = strchr(dp, ':'))) {
+ if ((dp > ts + 3) && (*(dp - 3) == ' ' || *(dp - 3) == '\t')) {
+ for (dp -= 4; dp >= ts && (*dp == ' ' || *dp == '\t'); dp--);
+ if (dp < ts) { // missing word
+ dp = NULL;
+ } else {
+ *(dp + 1) = '\0';
+ dp = dp + 2;
+ }
+ break;
+ }
+ dp++;
+ }
+
+ // tabulator is the old morphological field separator
+ dp2 = strchr(ts, '\t');
+ if (dp2 && (!dp || dp2 < dp)) {
+ *dp2 = '\0';
+ dp = dp2 + 1;
+ }
+
+ // split each line into word and affix char strings
+ // "\/" signs slash in words (not affix separator)
+ // "/" at beginning of the line is word character (not affix separator)
+ ap = strchr(ts,'/');
+ while (ap) {
+ if (ap == ts) {
+ ap++;
+ continue;
+ } else if (*(ap - 1) != '\\') break;
+ // replace "\/" with "/"
+ for (char * sp = ap - 1; *sp; *sp = *(sp + 1), sp++);
+ ap = strchr(ap,'/');
+ }
+
+ if (ap) {
+ *ap = '\0';
+ if (aliasf) {
+ int index = atoi(ap + 1);
+ al = get_aliasf(index, &flags, dict);
+ if (!al) {
+ HUNSPELL_WARNING(stderr, "error: line %d: bad flag vector alias\n", dict->getlinenum());
+ *ap = '\0';
+ }
+ } else {
+ al = decode_flags(&flags, ap + 1, dict);
+ if (al == -1) {
+ HUNSPELL_WARNING(stderr, "Can't allocate memory.\n");
+ delete dict;
+ return 6;
+ }
+ flag_qsort(flags, 0, al);
+ }
+ } else {
+ al = 0;
+ ap = NULL;
+ flags = NULL;
+ }
+
+ int captype;
+ int wbl = strlen(ts);
+ int wcl = get_clen_and_captype(ts, wbl, &captype);
+ // add the word and its index plus its capitalized form optionally
+ if (add_word(ts,wbl,wcl,flags,al,dp, false) ||
+ add_hidden_capitalized_word(ts, wbl, wcl, flags, al, dp, captype)) {
+ delete dict;
+ return 5;
+ }
+ }
+
+ delete dict;
+ return 0;
+}
+
+// the hash function is a simple load and rotate
+// algorithm borrowed
+
+int HashMgr::hash(const char * word) const
+{
+ long hv = 0;
+ for (int i=0; i < 4 && *word != 0; i++)
+ hv = (hv << 8) | (*word++);
+ while (*word != 0) {
+ ROTATE(hv,ROTATE_LEN);
+ hv ^= (*word++);
+ }
+ return (unsigned long) hv % tablesize;
+}
+
+int HashMgr::decode_flags(unsigned short ** result, char * flags, FileMgr * af) {
+ int len;
+ if (*flags == '\0') {
+ *result = NULL;
+ return 0;
+ }
+ switch (flag_mode) {
+ case FLAG_LONG: { // two-character flags (1x2yZz -> 1x 2y Zz)
+ len = strlen(flags);
+ if (len%2 == 1) HUNSPELL_WARNING(stderr, "error: line %d: bad flagvector\n", af->getlinenum());
+ len /= 2;
+ *result = (unsigned short *) malloc(len * sizeof(short));
+ if (!*result) return -1;
+ for (int i = 0; i < len; i++) {
+ (*result)[i] = (((unsigned short) flags[i * 2]) << 8) + (unsigned short) flags[i * 2 + 1];
+ }
+ break;
+ }
+ case FLAG_NUM: { // decimal numbers separated by comma (4521,23,233 -> 4521 23 233)
+ int i;
+ len = 1;
+ char * src = flags;
+ unsigned short * dest;
+ char * p;
+ for (p = flags; *p; p++) {
+ if (*p == ',') len++;
+ }
+ *result = (unsigned short *) malloc(len * sizeof(short));
+ if (!*result) return -1;
+ dest = *result;
+ for (p = flags; *p; p++) {
+ if (*p == ',') {
+ i = atoi(src);
+ if (i >= DEFAULTFLAGS) HUNSPELL_WARNING(stderr, "error: line %d: flag id %d is too large (max: %d)\n",
+ af->getlinenum(), i, DEFAULTFLAGS - 1);
+ *dest = (unsigned short) i;
+ if (*dest == 0) HUNSPELL_WARNING(stderr, "error: line %d: 0 is wrong flag id\n", af->getlinenum());
+ src = p + 1;
+ dest++;
+ }
+ }
+ i = atoi(src);
+ if (i >= DEFAULTFLAGS) HUNSPELL_WARNING(stderr, "error: line %d: flag id %d is too large (max: %d)\n",
+ af->getlinenum(), i, DEFAULTFLAGS - 1);
+ *dest = (unsigned short) i;
+ if (*dest == 0) HUNSPELL_WARNING(stderr, "error: line %d: 0 is wrong flag id\n", af->getlinenum());
+ break;
+ }
+ case FLAG_UNI: { // UTF-8 characters
+ w_char w[BUFSIZE/2];
+ len = u8_u16(w, BUFSIZE/2, flags);
+ *result = (unsigned short *) malloc(len * sizeof(short));
+ if (!*result) return -1;
+ memcpy(*result, w, len * sizeof(short));
+ break;
+ }
+ default: { // Ispell's one-character flags (erfg -> e r f g)
+ unsigned short * dest;
+ len = strlen(flags);
+ *result = (unsigned short *) malloc(len * sizeof(short));
+ if (!*result) return -1;
+ dest = *result;
+ for (unsigned char * p = (unsigned char *) flags; *p; p++) {
+ *dest = (unsigned short) *p;
+ dest++;
+ }
+ }
+ }
+ return len;
+}
+
+unsigned short HashMgr::decode_flag(const char * f) {
+ unsigned short s = 0;
+ int i;
+ switch (flag_mode) {
+ case FLAG_LONG:
+ s = ((unsigned short) f[0] << 8) + (unsigned short) f[1];
+ break;
+ case FLAG_NUM:
+ i = atoi(f);
+ if (i >= DEFAULTFLAGS) HUNSPELL_WARNING(stderr, "error: flag id %d is too large (max: %d)\n", i, DEFAULTFLAGS - 1);
+ s = (unsigned short) i;
+ break;
+ case FLAG_UNI:
+ u8_u16((w_char *) &s, 1, f);
+ break;
+ default:
+ s = (unsigned short) *((unsigned char *)f);
+ }
+ if (s == 0) HUNSPELL_WARNING(stderr, "error: 0 is wrong flag id\n");
+ return s;
+}
+
+char * HashMgr::encode_flag(unsigned short f) {
+ unsigned char ch[10];
+ if (f==0) return mystrdup("(NULL)");
+ if (flag_mode == FLAG_LONG) {
+ ch[0] = (unsigned char) (f >> 8);
+ ch[1] = (unsigned char) (f - ((f >> 8) << 8));
+ ch[2] = '\0';
+ } else if (flag_mode == FLAG_NUM) {
+ sprintf((char *) ch, "%d", f);
+ } else if (flag_mode == FLAG_UNI) {
+ u16_u8((char *) &ch, 10, (w_char *) &f, 1);
+ } else {
+ ch[0] = (unsigned char) (f);
+ ch[1] = '\0';
+ }
+ return mystrdup((char *) ch);
+}
+
+// read in aff file and set flag mode
+int HashMgr::load_config(const char * affpath, const char * key)
+{
+ char * line; // io buffers
+ int firstline = 1;
+
+ // open the affix file
+ FileMgr * afflst = new FileMgr(affpath, key);
+ if (!afflst) {
+ HUNSPELL_WARNING(stderr, "Error - could not open affix description file %s\n",affpath);
+ return 1;
+ }
+
+ // read in each line ignoring any that do not
+ // start with a known line type indicator
+
+ while ((line = afflst->getline())) {
+ mychomp(line);
+
+ /* remove byte order mark */
+ if (firstline) {
+ firstline = 0;
+ if (strncmp(line,"\xEF\xBB\xBF",3) == 0) memmove(line, line+3, strlen(line+3)+1);
+ }
+
+ /* parse in the try string */
+ if ((strncmp(line,"FLAG",4) == 0) && isspace(line[4])) {
+ if (flag_mode != FLAG_CHAR) {
+ HUNSPELL_WARNING(stderr, "error: line %d: multiple definitions of the FLAG affix file parameter\n", afflst->getlinenum());
+ }
+ if (strstr(line, "long")) flag_mode = FLAG_LONG;
+ if (strstr(line, "num")) flag_mode = FLAG_NUM;
+ if (strstr(line, "UTF-8")) flag_mode = FLAG_UNI;
+ if (flag_mode == FLAG_CHAR) {
+ HUNSPELL_WARNING(stderr, "error: line %d: FLAG needs `num', `long' or `UTF-8' parameter\n", afflst->getlinenum());
+ }
+ }
+ if (strncmp(line,"FORBIDDENWORD",13) == 0) {
+ char * st = NULL;
+ if (parse_string(line, &st, afflst->getlinenum())) {
+ delete afflst;
+ return 1;
+ }
+ forbiddenword = decode_flag(st);
+ free(st);
+ }
+ if (strncmp(line, "SET", 3) == 0) {
+ if (parse_string(line, &enc, afflst->getlinenum())) {
+ delete afflst;
+ return 1;
+ }
+ if (strcmp(enc, "UTF-8") == 0) {
+ utf8 = 1;
+#ifndef OPENOFFICEORG
+#ifndef MOZILLA_CLIENT
+ initialize_utf_tbl();
+#endif
+#endif
+ } else csconv = get_current_cs(enc);
+ }
+ if (strncmp(line, "LANG", 4) == 0) {
+ if (parse_string(line, &lang, afflst->getlinenum())) {
+ delete afflst;
+ return 1;
+ }
+ langnum = get_lang_num(lang);
+ }
+
+ /* parse in the ignored characters (for example, Arabic optional diacritics characters */
+ if (strncmp(line,"IGNORE",6) == 0) {
+ if (parse_array(line, &ignorechars, &ignorechars_utf16,
+ &ignorechars_utf16_len, utf8, afflst->getlinenum())) {
+ delete afflst;
+ return 1;
+ }
+ }
+
+ if ((strncmp(line,"AF",2) == 0) && isspace(line[2])) {
+ if (parse_aliasf(line, afflst)) {
+ delete afflst;
+ return 1;
+ }
+ }
+
+ if ((strncmp(line,"AM",2) == 0) && isspace(line[2])) {
+ if (parse_aliasm(line, afflst)) {
+ delete afflst;
+ return 1;
+ }
+ }
+
+ if (strncmp(line,"COMPLEXPREFIXES",15) == 0) complexprefixes = 1;
+ if (((strncmp(line,"SFX",3) == 0) || (strncmp(line,"PFX",3) == 0)) && isspace(line[3])) break;
+ }
+ if (csconv == NULL) csconv = get_current_cs(SPELL_ENCODING);
+ delete afflst;
+ return 0;
+}
+
+/* parse in the ALIAS table */
+int HashMgr::parse_aliasf(char * line, FileMgr * af)
+{
+ if (numaliasf != 0) {
+ HUNSPELL_WARNING(stderr, "error: line %d: multiple table definitions\n", af->getlinenum());
+ return 1;
+ }
+ char * tp = line;
+ char * piece;
+ int i = 0;
+ int np = 0;
+ piece = mystrsep(&tp, 0);
+ while (piece) {
+ if (*piece != '\0') {
+ switch(i) {
+ case 0: { np++; break; }
+ case 1: {
+ numaliasf = atoi(piece);
+ if (numaliasf < 1) {
+ numaliasf = 0;
+ aliasf = NULL;
+ aliasflen = NULL;
+ HUNSPELL_WARNING(stderr, "error: line %d: bad entry number\n", af->getlinenum());
+ return 1;
+ }
+ aliasf = (unsigned short **) malloc(numaliasf * sizeof(unsigned short *));
+ aliasflen = (unsigned short *) malloc(numaliasf * sizeof(short));
+ if (!aliasf || !aliasflen) {
+ numaliasf = 0;
+ if (aliasf) free(aliasf);
+ if (aliasflen) free(aliasflen);
+ aliasf = NULL;
+ aliasflen = NULL;
+ return 1;
+ }
+ np++;
+ break;
+ }
+ default: break;
+ }
+ i++;
+ }
+ piece = mystrsep(&tp, 0);
+ }
+ if (np != 2) {
+ numaliasf = 0;
+ free(aliasf);
+ free(aliasflen);
+ aliasf = NULL;
+ aliasflen = NULL;
+ HUNSPELL_WARNING(stderr, "error: line %d: missing data\n", af->getlinenum());
+ return 1;
+ }
+
+ /* now parse the numaliasf lines to read in the remainder of the table */
+ char * nl;
+ for (int j=0; j < numaliasf; j++) {
+ if (!(nl = af->getline())) return 1;
+ mychomp(nl);
+ tp = nl;
+ i = 0;
+ aliasf[j] = NULL;
+ aliasflen[j] = 0;
+ piece = mystrsep(&tp, 0);
+ while (piece) {
+ if (*piece != '\0') {
+ switch(i) {
+ case 0: {
+ if (strncmp(piece,"AF",2) != 0) {
+ numaliasf = 0;
+ free(aliasf);
+ free(aliasflen);
+ aliasf = NULL;
+ aliasflen = NULL;
+ HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum());
+ return 1;
+ }
+ break;
+ }
+ case 1: {
+ aliasflen[j] = (unsigned short) decode_flags(&(aliasf[j]), piece, af);
+ flag_qsort(aliasf[j], 0, aliasflen[j]);
+ break;
+ }
+ default: break;
+ }
+ i++;
+ }
+ piece = mystrsep(&tp, 0);
+ }
+ if (!aliasf[j]) {
+ free(aliasf);
+ free(aliasflen);
+ aliasf = NULL;
+ aliasflen = NULL;
+ numaliasf = 0;
+ HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum());
+ return 1;
+ }
+ }
+ return 0;
+}
+
+int HashMgr::is_aliasf() {
+ return (aliasf != NULL);
+}
+
+int HashMgr::get_aliasf(int index, unsigned short ** fvec, FileMgr * af) {
+ if ((index > 0) && (index <= numaliasf)) {
+ *fvec = aliasf[index - 1];
+ return aliasflen[index - 1];
+ }
+ HUNSPELL_WARNING(stderr, "error: line %d: bad flag alias index: %d\n", af->getlinenum(), index);
+ *fvec = NULL;
+ return 0;
+}
+
+/* parse morph alias definitions */
+int HashMgr::parse_aliasm(char * line, FileMgr * af)
+{
+ if (numaliasm != 0) {
+ HUNSPELL_WARNING(stderr, "error: line %d: multiple table definitions\n", af->getlinenum());
+ return 1;
+ }
+ char * tp = line;
+ char * piece;
+ int i = 0;
+ int np = 0;
+ piece = mystrsep(&tp, 0);
+ while (piece) {
+ if (*piece != '\0') {
+ switch(i) {
+ case 0: { np++; break; }
+ case 1: {
+ numaliasm = atoi(piece);
+ if (numaliasm < 1) {
+ HUNSPELL_WARNING(stderr, "error: line %d: bad entry number\n", af->getlinenum());
+ return 1;
+ }
+ aliasm = (char **) malloc(numaliasm * sizeof(char *));
+ if (!aliasm) {
+ numaliasm = 0;
+ return 1;
+ }
+ np++;
+ break;
+ }
+ default: break;
+ }
+ i++;
+ }
+ piece = mystrsep(&tp, 0);
+ }
+ if (np != 2) {
+ numaliasm = 0;
+ free(aliasm);
+ aliasm = NULL;
+ HUNSPELL_WARNING(stderr, "error: line %d: missing data\n", af->getlinenum());
+ return 1;
+ }
+
+ /* now parse the numaliasm lines to read in the remainder of the table */
+ char * nl = line;
+ for (int j=0; j < numaliasm; j++) {
+ if (!(nl = af->getline())) return 1;
+ mychomp(nl);
+ tp = nl;
+ i = 0;
+ aliasm[j] = NULL;
+ piece = mystrsep(&tp, ' ');
+ while (piece) {
+ if (*piece != '\0') {
+ switch(i) {
+ case 0: {
+ if (strncmp(piece,"AM",2) != 0) {
+ HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum());
+ numaliasm = 0;
+ free(aliasm);
+ aliasm = NULL;
+ return 1;
+ }
+ break;
+ }
+ case 1: {
+ // add the remaining of the line
+ if (*tp) {
+ *(tp - 1) = ' ';
+ tp = tp + strlen(tp);
+ }
+ if (complexprefixes) {
+ if (utf8) reverseword_utf(piece);
+ else reverseword(piece);
+ }
+ aliasm[j] = mystrdup(piece);
+ if (!aliasm[j]) {
+ numaliasm = 0;
+ free(aliasm);
+ aliasm = NULL;
+ return 1;
+ }
+ break; }
+ default: break;
+ }
+ i++;
+ }
+ piece = mystrsep(&tp, ' ');
+ }
+ if (!aliasm[j]) {
+ numaliasm = 0;
+ free(aliasm);
+ aliasm = NULL;
+ HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum());
+ return 1;
+ }
+ }
+ return 0;
+}
+
+int HashMgr::is_aliasm() {
+ return (aliasm != NULL);
+}
+
+char * HashMgr::get_aliasm(int index) {
+ if ((index > 0) && (index <= numaliasm)) return aliasm[index - 1];
+ HUNSPELL_WARNING(stderr, "error: bad morph. alias index: %d\n", index);
+ return NULL;
+}
diff --git a/Plugins/spellchecker/hunspell/hashmgr.hxx b/Plugins/spellchecker/hunspell/hashmgr.hxx
new file mode 100644
index 0000000..341b081
--- /dev/null
+++ b/Plugins/spellchecker/hunspell/hashmgr.hxx
@@ -0,0 +1,69 @@
+#ifndef _HASHMGR_HXX_
+#define _HASHMGR_HXX_
+
+#include "hunvisapi.h"
+
+#include <stdio.h>
+
+#include "htypes.hxx"
+#include "filemgr.hxx"
+
+enum flag { FLAG_CHAR, FLAG_LONG, FLAG_NUM, FLAG_UNI };
+
+class LIBHUNSPELL_DLL_EXPORTED HashMgr
+{
+ int tablesize;
+ struct hentry ** tableptr;
+ int userword;
+ flag flag_mode;
+ int complexprefixes;
+ int utf8;
+ unsigned short forbiddenword;
+ int langnum;
+ char * enc;
+ char * lang;
+ struct cs_info * csconv;
+ char * ignorechars;
+ unsigned short * ignorechars_utf16;
+ int ignorechars_utf16_len;
+ int numaliasf; // flag vector `compression' with aliases
+ unsigned short ** aliasf;
+ unsigned short * aliasflen;
+ int numaliasm; // morphological desciption `compression' with aliases
+ char ** aliasm;
+
+
+public:
+ HashMgr(const char * tpath, const char * apath, const char * key = NULL);
+ ~HashMgr();
+
+ struct hentry * lookup(const char *) const;
+ int hash(const char *) const;
+ struct hentry * walk_hashtable(int & col, struct hentry * hp) const;
+
+ int add(const char * word);
+ int add_with_affix(const char * word, const char * pattern);
+ int remove(const char * word);
+ int decode_flags(unsigned short ** result, char * flags, FileMgr * af);
+ unsigned short decode_flag(const char * flag);
+ char * encode_flag(unsigned short flag);
+ int is_aliasf();
+ int get_aliasf(int index, unsigned short ** fvec, FileMgr * af);
+ int is_aliasm();
+ char * get_aliasm(int index);
+
+private:
+ int get_clen_and_captype(const char * word, int wbl, int * captype);
+ int load_tables(const char * tpath, const char * key);
+ int add_word(const char * word, int wbl, int wcl, unsigned short * ap,
+ int al, const char * desc, bool onlyupcase);
+ int load_config(const char * affpath, const char * key);
+ int parse_aliasf(char * line, FileMgr * af);
+ int add_hidden_capitalized_word(char * word, int wbl, int wcl,
+ unsigned short * flags, int al, char * dp, int captype);
+ int parse_aliasm(char * line, FileMgr * af);
+ int remove_forbidden_flag(const char * word);
+
+};
+
+#endif
diff --git a/Plugins/spellchecker/hunspell/htypes.hxx b/Plugins/spellchecker/hunspell/htypes.hxx
new file mode 100644
index 0000000..5b6c909
--- /dev/null
+++ b/Plugins/spellchecker/hunspell/htypes.hxx
@@ -0,0 +1,32 @@
+#ifndef _HTYPES_HXX_
+#define _HTYPES_HXX_
+
+#define ROTATE_LEN 5
+
+#define ROTATE(v,q) \
+ (v) = ((v) << (q)) | (((v) >> (32 - q)) & ((1 << (q))-1));
+
+// hentry options
+#define H_OPT (1 << 0)
+#define H_OPT_ALIASM (1 << 1)
+#define H_OPT_PHON (1 << 2)
+
+// see also csutil.hxx
+#define HENTRY_WORD(h) &(h->word[0])
+
+// approx. number of user defined words
+#define USERWORD 1000
+
+struct hentry
+{
+ unsigned char blen; // word length in bytes
+ unsigned char clen; // word length in characters (different for UTF-8 enc.)
+ short alen; // length of affix flag vector
+ unsigned short * astr; // affix flag vector
+ struct hentry * next; // next word with same hash code
+ struct hentry * next_homonym; // next homonym word (with same hash code)
+ char var; // variable fields (only for special pronounciation yet)
+ char word[1]; // variable-length word (8-bit or UTF-8 encoding)
+};
+
+#endif
diff --git a/Plugins/spellchecker/hunspell/hunspell.cxx b/Plugins/spellchecker/hunspell/hunspell.cxx
new file mode 100644
index 0000000..8e00e1b
--- /dev/null
+++ b/Plugins/spellchecker/hunspell/hunspell.cxx
@@ -0,0 +1,2011 @@
+#include "license.hunspell"
+#include "license.myspell"
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "hunspell.hxx"
+#include "hunspell.h"
+#ifndef MOZILLA_CLIENT
+# include "config.h"
+#endif
+#include "csutil.hxx"
+
+Hunspell::Hunspell(const char * affpath, const char * dpath, const char * key)
+{
+ encoding = NULL;
+ csconv = NULL;
+ utf8 = 0;
+ complexprefixes = 0;
+ affixpath = mystrdup(affpath);
+ maxdic = 0;
+
+ /* first set up the hash manager */
+ pHMgr[0] = new HashMgr(dpath, affpath, key);
+ if (pHMgr[0]) maxdic = 1;
+
+ /* next set up the affix manager */
+ /* it needs access to the hash manager lookup methods */
+ pAMgr = new AffixMgr(affpath, pHMgr, &maxdic, key);
+
+ /* get the preferred try string and the dictionary */
+ /* encoding from the Affix Manager for that dictionary */
+ char * try_string = pAMgr->get_try_string();
+ encoding = pAMgr->get_encoding();
+ langnum = pAMgr->get_langnum();
+ utf8 = pAMgr->get_utf8();
+ if (!utf8)
+ csconv = get_current_cs(encoding);
+ complexprefixes = pAMgr->get_complexprefixes();
+ wordbreak = pAMgr->get_breaktable();
+
+ /* and finally set up the suggestion manager */
+ pSMgr = new SuggestMgr(try_string, MAXSUGGESTION, pAMgr);
+ if (try_string) free(try_string);
+}
+
+Hunspell::~Hunspell()
+{
+ if (pSMgr) delete pSMgr;
+ if (pAMgr) delete pAMgr;
+ for (int i = 0; i < maxdic; i++) delete pHMgr[i];
+ maxdic = 0;
+ pSMgr = NULL;
+ pAMgr = NULL;
+#ifdef MOZILLA_CLIENT
+ delete [] csconv;
+#endif
+ csconv= NULL;
+ if (encoding) free(encoding);
+ encoding = NULL;
+ if (affixpath) free(affixpath);
+ affixpath = NULL;
+}
+
+// load extra dictionaries
+int Hunspell::add_dic(const char * dpath, const char * key) {
+ if (maxdic == MAXDIC || !affixpath) return 1;
+ pHMgr[maxdic] = new HashMgr(dpath, affixpath, key);
+ if (pHMgr[maxdic]) maxdic++; else return 1;
+ return 0;
+}
+
+// make a copy of src at destination while removing all leading
+// blanks and removing any trailing periods after recording
+// their presence with the abbreviation flag
+// also since already going through character by character,
+// set the capitalization type
+// return the length of the "cleaned" (and UTF-8 encoded) word
+
+int Hunspell::cleanword2(char * dest, const char * src,
+ w_char * dest_utf, int * nc, int * pcaptype, int * pabbrev)
+{
+ unsigned char * p = (unsigned char *) dest;
+ const unsigned char * q = (const unsigned char * ) src;
+
+ // first skip over any leading blanks
+ while ((*q != '\0') && (*q == ' ')) q++;
+
+ // now strip off any trailing periods (recording their presence)
+ *pabbrev = 0;
+ int nl = strlen((const char *)q);
+ while ((nl > 0) && (*(q+nl-1)=='.')) {
+ nl--;
+ (*pabbrev)++;
+ }
+
+ // if no characters are left it can't be capitalized
+ if (nl <= 0) {
+ *pcaptype = NOCAP;
+ *p = '\0';
+ return 0;
+ }
+
+ strncpy(dest, (char *) q, nl);
+ *(dest + nl) = '\0';
+ nl = strlen(dest);
+ if (utf8) {
+ *nc = u8_u16(dest_utf, MAXWORDLEN, dest);
+ // don't check too long words
+ if (*nc >= MAXWORDLEN) return 0;
+ if (*nc == -1) { // big Unicode character (non BMP area)
+ *pcaptype = NOCAP;
+ return nl;
+ }
+ *pcaptype = get_captype_utf8(dest_utf, *nc, langnum);
+ } else {
+ *pcaptype = get_captype(dest, nl, csconv);
+ *nc = nl;
+ }
+ return nl;
+}
+
+int Hunspell::cleanword(char * dest, const char * src,
+ int * pcaptype, int * pabbrev)
+{
+ unsigned char * p = (unsigned char *) dest;
+ const unsigned char * q = (const unsigned char * ) src;
+ int firstcap = 0;
+
+ // first skip over any leading blanks
+ while ((*q != '\0') && (*q == ' ')) q++;
+
+ // now strip off any trailing periods (recording their presence)
+ *pabbrev = 0;
+ int nl = strlen((const char *)q);
+ while ((nl > 0) && (*(q+nl-1)=='.')) {
+ nl--;
+ (*pabbrev)++;
+ }
+
+ // if no characters are left it can't be capitalized
+ if (nl <= 0) {
+ *pcaptype = NOCAP;
+ *p = '\0';
+ return 0;
+ }
+
+ // now determine the capitalization type of the first nl letters
+ int ncap = 0;
+ int nneutral = 0;
+ int nc = 0;
+
+ if (!utf8) {
+ while (nl > 0) {
+ nc++;
+ if (csconv[(*q)].ccase) ncap++;
+ if (csconv[(*q)].cupper == csconv[(*q)].clower) nneutral++;
+ *p++ = *q++;
+ nl--;
+ }
+ // remember to terminate the destination string
+ *p = '\0';
+ firstcap = csconv[(unsigned char)(*dest)].ccase;
+ } else {
+ unsigned short idx;
+ w_char t[MAXWORDLEN];
+ nc = u8_u16(t, MAXWORDLEN, src);
+ for (int i = 0; i < nc; i++) {
+ idx = (t[i].h << 8) + t[i].l;
+ unsigned short low = unicodetolower(idx, langnum);
+ if (idx != low) ncap++;
+ if (unicodetoupper(idx, langnum) == low) nneutral++;
+ }
+ u16_u8(dest, MAXWORDUTF8LEN, t, nc);
+ if (ncap) {
+ idx = (t[0].h << 8) + t[0].l;
+ firstcap = (idx != unicodetolower(idx, langnum));
+ }
+ }
+
+ // now finally set the captype
+ if (ncap == 0) {
+ *pcaptype = NOCAP;
+ } else if ((ncap == 1) && firstcap) {
+ *pcaptype = INITCAP;
+ } else if ((ncap == nc) || ((ncap + nneutral) == nc)){
+ *pcaptype = ALLCAP;
+ } else if ((ncap > 1) && firstcap) {
+ *pcaptype = HUHINITCAP;
+ } else {
+ *pcaptype = HUHCAP;
+ }
+ return strlen(dest);
+}
+
+void Hunspell::mkallcap(char * p)
+{
+ if (utf8) {
+ w_char u[MAXWORDLEN];
+ int nc = u8_u16(u, MAXWORDLEN, p);
+ unsigned short idx;
+ for (int i = 0; i < nc; i++) {
+ idx = (u[i].h << 8) + u[i].l;
+ if (idx != unicodetoupper(idx, langnum)) {
+ u[i].h = (unsigned char) (unicodetoupper(idx, langnum) >> 8);
+ u[i].l = (unsigned char) (unicodetoupper(idx, langnum) & 0x00FF);
+ }
+ }
+ u16_u8(p, MAXWORDUTF8LEN, u, nc);
+ } else {
+ while (*p != '\0') {
+ *p = csconv[((unsigned char) *p)].cupper;
+ p++;
+ }
+ }
+}
+
+int Hunspell::mkallcap2(char * p, w_char * u, int nc)
+{
+ if (utf8) {
+ unsigned short idx;
+ for (int i = 0; i < nc; i++) {
+ idx = (u[i].h << 8) + u[i].l;
+ unsigned short up = unicodetoupper(idx, langnum);
+ if (idx != up) {
+ u[i].h = (unsigned char) (up >> 8);
+ u[i].l = (unsigned char) (up & 0x00FF);
+ }
+ }
+ u16_u8(p, MAXWORDUTF8LEN, u, nc);
+ return strlen(p);
+ } else {
+ while (*p != '\0') {
+ *p = csconv[((unsigned char) *p)].cupper;
+ p++;
+ }
+ }
+ return nc;
+}
+
+
+void Hunspell::mkallsmall(char * p)
+{
+ while (*p != '\0') {
+ *p = csconv[((unsigned char) *p)].clower;
+ p++;
+ }
+}
+
+int Hunspell::mkallsmall2(char * p, w_char * u, int nc)
+{
+ if (utf8) {
+ unsigned short idx;
+ for (int i = 0; i < nc; i++) {
+ idx = (u[i].h << 8) + u[i].l;
+ unsigned short low = unicodetolower(idx, langnum);
+ if (idx != low) {
+ u[i].h = (unsigned char) (low >> 8);
+ u[i].l = (unsigned char) (low & 0x00FF);
+ }
+ }
+ u16_u8(p, MAXWORDUTF8LEN, u, nc);
+ return strlen(p);
+ } else {
+ while (*p != '\0') {
+ *p = csconv[((unsigned char) *p)].clower;
+ p++;
+ }
+ }
+ return nc;
+}
+
+// convert UTF-8 sharp S codes to latin 1
+char * Hunspell::sharps_u8_l1(char * dest, char * source) {
+ char * p = dest;
+ *p = *source;
+ for (p++, source++; *(source - 1); p++, source++) {
+ *p = *source;
+ if (*source == '\x9F') *--p = '\xDF';
+ }
+ return dest;
+}
+
+// recursive search for right ss - sharp s permutations
+hentry * Hunspell::spellsharps(char * base, char * pos, int n,
+ int repnum, char * tmp, int * info, char **root) {
+ pos = strstr(pos, "ss");
+ if (pos && (n < MAXSHARPS)) {
+ *pos = '\xC3';
+ *(pos + 1) = '\x9F';
+ hentry * h = spellsharps(base, pos + 2, n + 1, repnum + 1, tmp, info, root);
+ if (h) return h;
+ *pos = 's';
+ *(pos + 1) = 's';
+ h = spellsharps(base, pos + 2, n + 1, repnum, tmp, info, root);
+ if (h) return h;
+ } else if (repnum > 0) {
+ if (utf8) return checkword(base, info, root);
+ return checkword(sharps_u8_l1(tmp, base), info, root);
+ }
+ return NULL;
+}
+
+int Hunspell::is_keepcase(const hentry * rv) {
+ return pAMgr && rv->astr && pAMgr->get_keepcase() &&
+ TESTAFF(rv->astr, pAMgr->get_keepcase(), rv->alen);
+}
+
+/* insert a word to the beginning of the suggestion array and return ns */
+int Hunspell::insert_sug(char ***slst, char * word, int ns) {
+ char * dup = mystrdup(word);
+ if (!dup) return ns;
+ if (ns == MAXSUGGESTION) {
+ ns--;
+ free((*slst)[ns]);
+ }
+ for (int k = ns; k > 0; k--) (*slst)[k] = (*slst)[k - 1];
+ (*slst)[0] = dup;
+ return ns + 1;
+}
+
+int Hunspell::spell(const char * word, int * info, char ** root)
+{
+ struct hentry * rv=NULL;
+ // need larger vector. For example, Turkish capital letter I converted a
+ // 2-byte UTF-8 character (dotless i) by mkallsmall.
+ char cw[MAXWORDUTF8LEN];
+ char wspace[MAXWORDUTF8LEN];
+ w_char unicw[MAXWORDLEN];
+ // Hunspell supports XML input of the simplified API (see manual)
+ if (strcmp(word, SPELL_XML) == 0) return 1;
+ int nc = strlen(word);
+ int wl2 = 0;
+ if (utf8) {
+ if (nc >= MAXWORDUTF8LEN) return 0;
+ } else {
+ if (nc >= MAXWORDLEN) return 0;
+ }
+ int captype = 0;
+ int abbv = 0;
+ int wl = 0;
+
+ // input conversion
+ RepList * rl = (pAMgr) ? pAMgr->get_iconvtable() : NULL;
+ if (rl && rl->conv(word, wspace)) wl = cleanword2(cw, wspace, unicw, &nc, &captype, &abbv);
+ else wl = cleanword2(cw, word, unicw, &nc, &captype, &abbv);
+
+ int info2 = 0;
+ if (wl == 0 || maxdic == 0) return 1;
+ if (root) *root = NULL;
+
+ // allow numbers with dots, dashes and commas (but forbid double separators: "..", "--" etc.)
+ enum { NBEGIN, NNUM, NSEP };
+ int nstate = NBEGIN;
+ int i;
+
+ for (i = 0; (i < wl); i++) {
+ if ((cw[i] <= '9') && (cw[i] >= '0')) {
+ nstate = NNUM;
+ } else if ((cw[i] == ',') || (cw[i] == '.') || (cw[i] == '-')) {
+ if ((nstate == NSEP) || (i == 0)) break;
+ nstate = NSEP;
+ } else break;
+ }
+ if ((i == wl) && (nstate == NNUM)) return 1;
+ if (!info) info = &info2; else *info = 0;
+
+ switch(captype) {
+ case HUHCAP:
+ case HUHINITCAP:
+ *info += SPELL_ORIGCAP;
+ case NOCAP: {
+ rv = checkword(cw, info, root);
+ if ((abbv) && !(rv)) {
+ memcpy(wspace,cw,wl);
+ *(wspace+wl) = '.';
+ *(wspace+wl+1) = '\0';
+ rv = checkword(wspace, info, root);
+ }
+ break;
+ }
+ case ALLCAP: {
+ *info += SPELL_ORIGCAP;
+ rv = checkword(cw, info, root);
+ if (rv) break;
+ if (abbv) {
+ memcpy(wspace,cw,wl);
+ *(wspace+wl) = '.';
+ *(wspace+wl+1) = '\0';
+ rv = checkword(wspace, info, root);
+ if (rv) break;
+ }
+ // Spec. prefix handling for Catalan, French, Italian:
+ // prefixes separated by apostrophe (SANT'ELIA -> Sant'+Elia).
+ if (pAMgr && strchr(cw, '\'')) {
+ wl = mkallsmall2(cw, unicw, nc);
+ //There are no really sane circumstances where this could fail,
+ //but anyway...
+ if (char * apostrophe = strchr(cw, '\'')) {
+ if (utf8) {
+ w_char tmpword[MAXWORDLEN];
+ *apostrophe = '\0';
+ wl2 = u8_u16(tmpword, MAXWORDLEN, cw);
+ *apostrophe = '\'';
+ if (wl2 < nc) {
+ mkinitcap2(apostrophe + 1, unicw + wl2 + 1, nc - wl2 - 1);
+ rv = checkword(cw, info, root);
+ if (rv) break;
+ }
+ } else {
+ mkinitcap2(apostrophe + 1, unicw, nc);
+ rv = checkword(cw, info, root);
+ if (rv) break;
+ }
+ }
+ mkinitcap2(cw, unicw, nc);
+ rv = checkword(cw, info, root);
+ if (rv) break;
+ }
+ if (pAMgr && pAMgr->get_checksharps() && strstr(cw, "SS")) {
+ char tmpword[MAXWORDUTF8LEN];
+ wl = mkallsmall2(cw, unicw, nc);
+ memcpy(wspace,cw,(wl+1));
+ rv = spellsharps(wspace, wspace, 0, 0, tmpword, info, root);
+ if (!rv) {
+ wl2 = mkinitcap2(cw, unicw, nc);
+ rv = spellsharps(cw, cw, 0, 0, tmpword, info, root);
+ }
+ if ((abbv) && !(rv)) {
+ *(wspace+wl) = '.';
+ *(wspace+wl+1) = '\0';
+ rv = spellsharps(wspace, wspace, 0, 0, tmpword, info, root);
+ if (!rv) {
+ memcpy(wspace, cw, wl2);
+ *(wspace+wl2) = '.';
+ *(wspace+wl2+1) = '\0';
+ rv = spellsharps(wspace, wspace, 0, 0, tmpword, info, root);
+ }
+ }
+ if (rv) break;
+ }
+ }
+ case INITCAP: {
+ *info += SPELL_ORIGCAP;
+ wl = mkallsmall2(cw, unicw, nc);
+ memcpy(wspace,cw,(wl+1));
+ wl2 = mkinitcap2(cw, unicw, nc);
+ if (captype == INITCAP) *info += SPELL_INITCAP;
+ rv = checkword(cw, info, root);
+ if (captype == INITCAP) *info -= SPELL_INITCAP;
+ // forbid bad capitalization
+ // (for example, ijs -> Ijs instead of IJs in Dutch)
+ // use explicit forms in dic: Ijs/F (F = FORBIDDENWORD flag)
+ if (*info & SPELL_FORBIDDEN) {
+ rv = NULL;
+ break;
+ }
+ if (rv && is_keepcase(rv) && (captype == ALLCAP)) rv = NULL;
+ if (rv) break;
+
+ rv = checkword(wspace, info, root);
+ if (abbv && !rv) {
+
+ *(wspace+wl) = '.';
+ *(wspace+wl+1) = '\0';
+ rv = checkword(wspace, info, root);
+ if (!rv) {
+ memcpy(wspace, cw, wl2);
+ *(wspace+wl2) = '.';
+ *(wspace+wl2+1) = '\0';
+ if (captype == INITCAP) *info += SPELL_INITCAP;
+ rv = checkword(wspace, info, root);
+ if (captype == INITCAP) *info -= SPELL_INITCAP;
+ if (rv && is_keepcase(rv) && (captype == ALLCAP)) rv = NULL;
+ break;
+ }
+ }
+ if (rv && is_keepcase(rv) &&
+ ((captype == ALLCAP) ||
+ // if CHECKSHARPS: KEEPCASE words with \xDF are allowed
+ // in INITCAP form, too.
+ !(pAMgr->get_checksharps() &&
+ ((utf8 && strstr(wspace, "\xC3\x9F")) ||
+ (!utf8 && strchr(wspace, '\xDF')))))) rv = NULL;
+ break;
+ }
+ }
+
+ if (rv) {
+ if (pAMgr && pAMgr->get_warn() && rv->astr &&
+ TESTAFF(rv->astr, pAMgr->get_warn(), rv->alen)) {
+ *info += SPELL_WARN;
+ if (pAMgr->get_forbidwarn()) return 0;
+ return HUNSPELL_OK_WARN;
+ }
+ return HUNSPELL_OK;
+ }
+
+ // recursive breaking at break points
+ if (wordbreak) {
+ char * s;
+ char r;
+ int nbr = 0;
+ wl = strlen(cw);
+ int numbreak = pAMgr ? pAMgr->get_numbreak() : 0;
+
+ // calculate break points for recursion limit
+ for (int j = 0; j < numbreak; j++) {
+ s = cw;
+ do {
+ s = (char *) strstr(s, wordbreak[j]);
+ if (s) {
+ nbr++;
+ s++;
+ }
+ } while (s);
+ }
+ if (nbr >= 10) return 0;
+
+ // check boundary patterns (^begin and end$)
+ for (int j = 0; j < numbreak; j++) {
+ int plen = strlen(wordbreak[j]);
+ if (plen == 1 || plen > wl) continue;
+ if (wordbreak[j][0] == '^' && strncmp(cw, wordbreak[j] + 1, plen - 1) == 0
+ && spell(cw + plen - 1)) return 1;
+ if (wordbreak[j][plen - 1] == '$' &&
+ strncmp(cw + wl - plen + 1, wordbreak[j], plen - 1) == 0) {
+ r = cw[wl - plen + 1];
+ cw[wl - plen + 1] = '\0';
+ if (spell(cw)) return 1;
+ cw[wl - plen + 1] = r;
+ }
+ }
+
+ // other patterns
+ for (int j = 0; j < numbreak; j++) {
+ int plen = strlen(wordbreak[j]);
+ s=(char *) strstr(cw, wordbreak[j]);
+ if (s && (s > cw) && (s < cw + wl - plen)) {
+ if (!spell(s + plen)) continue;
+ r = *s;
+ *s = '\0';
+ // examine 2 sides of the break point
+ if (spell(cw)) return 1;
+ *s = r;
+
+ // LANG_hu: spec. dash rule
+ if (langnum == LANG_hu && strcmp(wordbreak[j], "-") == 0) {
+ r = s[1];
+ s[1] = '\0';
+ if (spell(cw)) return 1; // check the first part with dash
+ s[1] = r;
+ }
+ // end of LANG speficic region
+
+ }
+ }
+ }
+
+ return 0;
+}
+
+struct hentry * Hunspell::checkword(const char * w, int * info, char ** root)
+{
+ struct hentry * he = NULL;
+ int len, i;
+ char w2[MAXWORDUTF8LEN];
+ const char * word;
+
+ char * ignoredchars = pAMgr->get_ignore();
+ if (ignoredchars != NULL) {
+ strcpy(w2, w);
+ if (utf8) {
+ int ignoredchars_utf16_len;
+ unsigned short * ignoredchars_utf16 = pAMgr->get_ignore_utf16(&ignoredchars_utf16_len);
+ remove_ignored_chars_utf(w2, ignoredchars_utf16, ignoredchars_utf16_len);
+ } else {
+ remove_ignored_chars(w2,ignoredchars);
+ }
+ word = w2;
+ } else word = w;
+
+ len = strlen(word);
+
+ if (!len)
+ return NULL;
+
+ // word reversing wrapper for complex prefixes
+ if (complexprefixes) {
+ if (word != w2) {
+ strcpy(w2, word);
+ word = w2;
+ }
+ if (utf8) reverseword_utf(w2); else reverseword(w2);
+ }
+
+ // look word in hash table
+ for (i = 0; (i < maxdic) && !he; i ++) {
+ he = (pHMgr[i])->lookup(word);
+
+ // check forbidden and onlyincompound words
+ if ((he) && (he->astr) && (pAMgr) && TESTAFF(he->astr, pAMgr->get_forbiddenword(), he->alen)) {
+ if (info) *info += SPELL_FORBIDDEN;
+ // LANG_hu section: set dash information for suggestions
+ if (langnum == LANG_hu) {
+ if (pAMgr->get_compoundflag() &&
+ TESTAFF(he->astr, pAMgr->get_compoundflag(), he->alen)) {
+ if (info) *info += SPELL_COMPOUND;
+ }
+ }
+ return NULL;
+ }
+
+ // he = next not needaffix, onlyincompound homonym or onlyupcase word
+ while (he && (he->astr) &&
+ ((pAMgr->get_needaffix() && TESTAFF(he->astr, pAMgr->get_needaffix(), he->alen)) ||
+ (pAMgr->get_onlyincompound() && TESTAFF(he->astr, pAMgr->get_onlyincompound(), he->alen)) ||
+ (info && (*info & SPELL_INITCAP) && TESTAFF(he->astr, ONLYUPCASEFLAG, he->alen))
+ )) he = he->next_homonym;
+ }
+
+ // check with affixes
+ if (!he && pAMgr) {
+ // try stripping off affixes */
+ he = pAMgr->affix_check(word, len, 0);
+
+ // check compound restriction and onlyupcase
+ if (he && he->astr && (
+ (pAMgr->get_onlyincompound() &&
+ TESTAFF(he->astr, pAMgr->get_onlyincompound(), he->alen)) ||
+ (info && (*info & SPELL_INITCAP) &&
+ TESTAFF(he->astr, ONLYUPCASEFLAG, he->alen)))) {
+ he = NULL;
+ }
+
+ if (he) {
+ if ((he->astr) && (pAMgr) && TESTAFF(he->astr, pAMgr->get_forbiddenword(), he->alen)) {
+ if (info) *info += SPELL_FORBIDDEN;
+ return NULL;
+ }
+ if (root) {
+ *root = mystrdup(he->word);
+ if (*root && complexprefixes) {
+ if (utf8) reverseword_utf(*root); else reverseword(*root);
+ }
+ }
+ // try check compound word
+ } else if (pAMgr->get_compound()) {
+ he = pAMgr->compound_check(word, len, 0, 0, 100, 0, NULL, 0, 0, info);
+ // LANG_hu section: `moving rule' with last dash
+ if ((!he) && (langnum == LANG_hu) && (word[len-1] == '-')) {
+ char * dup = mystrdup(word);
+ if (!dup) return NULL;
+ dup[len-1] = '\0';
+ he = pAMgr->compound_check(dup, len-1, -5, 0, 100, 0, NULL, 1, 0, info);
+ free(dup);
+ }
+ // end of LANG speficic region
+ if (he) {
+ if (root) {
+ *root = mystrdup(he->word);
+ if (*root && complexprefixes) {
+ if (utf8) reverseword_utf(*root); else reverseword(*root);
+ }
+ }
+ if (info) *info += SPELL_COMPOUND;
+ }
+ }
+
+ }
+
+ return he;
+}
+
+int Hunspell::suggest(char*** slst, const char * word)
+{
+ int onlycmpdsug = 0;
+ char cw[MAXWORDUTF8LEN];
+ char wspace[MAXWORDUTF8LEN];
+ if (!pSMgr || maxdic == 0) return 0;
+ w_char unicw[MAXWORDLEN];
+ *slst = NULL;
+ // process XML input of the simplified API (see manual)
+ if (strncmp(word, SPELL_XML, sizeof(SPELL_XML) - 3) == 0) {
+ return spellml(slst, word);
+ }
+ int nc = strlen(word);
+ if (utf8) {
+ if (nc >= MAXWORDUTF8LEN) return 0;
+ } else {
+ if (nc >= MAXWORDLEN) return 0;
+ }
+ int captype = 0;
+ int abbv = 0;
+ int wl = 0;
+
+ // input conversion
+ RepList * rl = (pAMgr) ? pAMgr->get_iconvtable() : NULL;
+ if (rl && rl->conv(word, wspace)) wl = cleanword2(cw, wspace, unicw, &nc, &captype, &abbv);
+ else wl = cleanword2(cw, word, unicw, &nc, &captype, &abbv);
+
+ if (wl == 0) return 0;
+ int ns = 0;
+ int capwords = 0;
+
+ // check capitalized form for FORCEUCASE
+ if (pAMgr && captype == NOCAP && pAMgr->get_forceucase()) {
+ int info = SPELL_ORIGCAP;
+ char ** wlst;
+ if (checkword(cw, &info, NULL)) {
+ if (*slst) {
+ wlst = *slst;
+ } else {
+ wlst = (char **) malloc(MAXSUGGESTION * sizeof(char *));
+ if (wlst == NULL) return -1;
+ *slst = wlst;
+ for (int i = 0; i < MAXSUGGESTION; i++) {
+ wlst[i] = NULL;
+ }
+ }
+ wlst[0] = mystrdup(cw);
+ mkinitcap(wlst[0]);
+ return 1;
+ }
+ }
+
+ switch(captype) {
+ case NOCAP: {
+ ns = pSMgr->suggest(slst, cw, ns, &onlycmpdsug);
+ break;
+ }
+
+ case INITCAP: {
+ capwords = 1;
+ ns = pSMgr->suggest(slst, cw, ns, &onlycmpdsug);
+ if (ns == -1) break;
+ memcpy(wspace,cw,(wl+1));
+ mkallsmall2(wspace, unicw, nc);
+ ns = pSMgr->suggest(slst, wspace, ns, &onlycmpdsug);
+ break;
+ }
+ case HUHINITCAP:
+ capwords = 1;
+ case HUHCAP: {
+ ns = pSMgr->suggest(slst, cw, ns, &onlycmpdsug);
+ if (ns != -1) {
+ int prevns;
+ // something.The -> something. The
+ char * dot = strchr(cw, '.');
+ if (dot && (dot > cw)) {
+ int captype_;
+ if (utf8) {
+ w_char w_[MAXWORDLEN];
+ int wl_ = u8_u16(w_, MAXWORDLEN, dot + 1);
+ captype_ = get_captype_utf8(w_, wl_, langnum);
+ } else captype_ = get_captype(dot+1, strlen(dot+1), csconv);
+ if (captype_ == INITCAP) {
+ char * st = mystrdup(cw);
+ if (st) st = (char *) realloc(st, wl + 2);
+ if (st) {
+ st[(dot - cw) + 1] = ' ';
+ strcpy(st + (dot - cw) + 2, dot + 1);
+ ns = insert_sug(slst, st, ns);
+ free(st);
+ }
+ }
+ }
+ if (captype == HUHINITCAP) {
+ // TheOpenOffice.org -> The OpenOffice.org
+ memcpy(wspace,cw,(wl+1));
+ mkinitsmall2(wspace, unicw, nc);
+ ns = pSMgr->suggest(slst, wspace, ns, &onlycmpdsug);
+ }
+ memcpy(wspace,cw,(wl+1));
+ mkallsmall2(wspace, unicw, nc);
+ if (spell(wspace)) ns = insert_sug(slst, wspace, ns);
+ prevns = ns;
+ ns = pSMgr->suggest(slst, wspace, ns, &onlycmpdsug);
+ if (captype == HUHINITCAP) {
+ mkinitcap2(wspace, unicw, nc);
+ if (spell(wspace)) ns = insert_sug(slst, wspace, ns);
+ ns = pSMgr->suggest(slst, wspace, ns, &onlycmpdsug);
+ }
+ // aNew -> "a New" (instead of "a new")
+ for (int j = prevns; j < ns; j++) {
+ char * space = strchr((*slst)[j],' ');
+ if (space) {
+ int slen = strlen(space + 1);
+ // different case after space (need capitalisation)
+ if ((slen < wl) && strcmp(cw + wl - slen, space + 1)) {
+ w_char w[MAXWORDLEN];
+ int wc = 0;
+ char * r = (*slst)[j];
+ if (utf8) wc = u8_u16(w, MAXWORDLEN, space + 1);
+ mkinitcap2(space + 1, w, wc);
+ // set as first suggestion
+ for (int k = j; k > 0; k--) (*slst)[k] = (*slst)[k - 1];
+ (*slst)[0] = r;
+ }
+ }
+ }
+ }
+ break;
+ }
+
+ case ALLCAP: {
+ memcpy(wspace, cw, (wl+1));
+ mkallsmall2(wspace, unicw, nc);
+ ns = pSMgr->suggest(slst, wspace, ns, &onlycmpdsug);
+ if (ns == -1) break;
+ if (pAMgr && pAMgr->get_keepcase() && spell(wspace))
+ ns = insert_sug(slst, wspace, ns);
+ mkinitcap2(wspace, unicw, nc);
+ ns = pSMgr->suggest(slst, wspace, ns, &onlycmpdsug);
+ for (int j=0; j < ns; j++) {
+ mkallcap((*slst)[j]);
+ if (pAMgr && pAMgr->get_checksharps()) {
+ char * pos;
+ if (utf8) {
+ pos = strstr((*slst)[j], "\xC3\x9F");
+ while (pos) {
+ *pos = 'S';
+ *(pos+1) = 'S';
+ pos = strstr(pos+2, "\xC3\x9F");
+ }
+ } else {
+ pos = strchr((*slst)[j], '\xDF');
+ while (pos) {
+ (*slst)[j] = (char *) realloc((*slst)[j], strlen((*slst)[j]) + 2);
+ mystrrep((*slst)[j], "\xDF", "SS");
+ pos = strchr((*slst)[j], '\xDF');
+ }
+ }
+ }
+ }
+ break;
+ }
+ }
+
+ // LANG_hu section: replace '-' with ' ' in Hungarian
+ if (langnum == LANG_hu) {
+ for (int j=0; j < ns; j++) {
+ char * pos = strchr((*slst)[j],'-');
+ if (pos) {
+ int info;
+ char w[MAXWORDUTF8LEN];
+ *pos = '\0';
+ strcpy(w, (*slst)[j]);
+ strcat(w, pos + 1);
+ spell(w, &info, NULL);
+ if ((info & SPELL_COMPOUND) && (info & SPELL_FORBIDDEN)) {
+ *pos = ' ';
+ } else *pos = '-';
+ }
+ }
+ }
+ // END OF LANG_hu section
+
+ // try ngram approach since found nothing or only compound words
+ if (pAMgr && (ns == 0 || onlycmpdsug) && (pAMgr->get_maxngramsugs() != 0) && (*slst)) {
+ switch(captype) {
+ case NOCAP: {
+ ns = pSMgr->ngsuggest(*slst, cw, ns, pHMgr, maxdic);
+ break;
+ }
+ case HUHINITCAP:
+ capwords = 1;
+ case HUHCAP: {
+ memcpy(wspace,cw,(wl+1));
+ mkallsmall2(wspace, unicw, nc);
+ ns = pSMgr->ngsuggest(*slst, wspace, ns, pHMgr, maxdic);
+ break;
+ }
+ case INITCAP: {
+ capwords = 1;
+ memcpy(wspace,cw,(wl+1));
+ mkallsmall2(wspace, unicw, nc);
+ ns = pSMgr->ngsuggest(*slst, wspace, ns, pHMgr, maxdic);
+ break;
+ }
+ case ALLCAP: {
+ memcpy(wspace,cw,(wl+1));
+ mkallsmall2(wspace, unicw, nc);
+ int oldns = ns;
+ ns = pSMgr->ngsuggest(*slst, wspace, ns, pHMgr, maxdic);
+ for (int j = oldns; j < ns; j++)
+ mkallcap((*slst)[j]);
+ break;
+ }
+ }
+ }
+
+ // try dash suggestion (Afo-American -> Afro-American)
+ if (char * pos = strchr(cw, '-')) {
+ char * ppos = cw;
+ int nodashsug = 1;
+ char ** nlst = NULL;
+ int nn = 0;
+ int last = 0;
+ if (*slst) {
+ for (int j = 0; j < ns && nodashsug == 1; j++) {
+ if (strchr((*slst)[j], '-')) nodashsug = 0;
+ }
+ }
+ while (nodashsug && !last) {
+ if (*pos == '\0') last = 1; else *pos = '\0';
+ if (!spell(ppos)) {
+ nn = suggest(&nlst, ppos);
+ for (int j = nn - 1; j >= 0; j--) {
+ strncpy(wspace, cw, ppos - cw);
+ strcpy(wspace + (ppos - cw), nlst[j]);
+ if (!last) {
+ strcat(wspace, "-");
+ strcat(wspace, pos + 1);
+ }
+ ns = insert_sug(slst, wspace, ns);
+ free(nlst[j]);
+ }
+ if (nlst != NULL) free(nlst);
+ nodashsug = 0;
+ }
+ if (!last) {
+ *pos = '-';
+ ppos = pos + 1;
+ pos = strchr(ppos, '-');
+ }
+ if (!pos) pos = cw + strlen(cw);
+ }
+ }
+
+ // word reversing wrapper for complex prefixes
+ if (complexprefixes) {
+ for (int j = 0; j < ns; j++) {
+ if (utf8) reverseword_utf((*slst)[j]); else reverseword((*slst)[j]);
+ }
+ }
+
+ // capitalize
+ if (capwords) for (int j=0; j < ns; j++) {
+ mkinitcap((*slst)[j]);
+ }
+
+ // expand suggestions with dot(s)
+ if (abbv && pAMgr && pAMgr->get_sugswithdots()) {
+ for (int j = 0; j < ns; j++) {
+ (*slst)[j] = (char *) realloc((*slst)[j], strlen((*slst)[j]) + 1 + abbv);
+ strcat((*slst)[j], word + strlen(word) - abbv);
+ }
+ }
+
+ // remove bad capitalized and forbidden forms
+ if (pAMgr && (pAMgr->get_keepcase() || pAMgr->get_forbiddenword())) {
+ switch (captype) {
+ case INITCAP:
+ case ALLCAP: {
+ int l = 0;
+ for (int j=0; j < ns; j++) {
+ if (!strchr((*slst)[j],' ') && !spell((*slst)[j])) {
+ char s[MAXSWUTF8L];
+ w_char w[MAXSWL];
+ int len;
+ if (utf8) {
+ len = u8_u16(w, MAXSWL, (*slst)[j]);
+ } else {
+ strcpy(s, (*slst)[j]);
+ len = strlen(s);
+ }
+ mkallsmall2(s, w, len);
+ free((*slst)[j]);
+ if (spell(s)) {
+ (*slst)[l] = mystrdup(s);
+ if ((*slst)[l]) l++;
+ } else {
+ mkinitcap2(s, w, len);
+ if (spell(s)) {
+ (*slst)[l] = mystrdup(s);
+ if ((*slst)[l]) l++;
+ }
+ }
+ } else {
+ (*slst)[l] = (*slst)[j];
+ l++;
+ }
+ }
+ ns = l;
+ }
+ }
+ }
+
+ // remove duplications
+ int l = 0;
+ for (int j = 0; j < ns; j++) {
+ (*slst)[l] = (*slst)[j];
+ for (int k = 0; k < l; k++) {
+ if (strcmp((*slst)[k], (*slst)[j]) == 0) {
+ free((*slst)[j]);
+ l--;
+ break;
+ }
+ }
+ l++;
+ }
+ ns = l;
+
+ // output conversion
+ rl = (pAMgr) ? pAMgr->get_oconvtable() : NULL;
+ for (int j = 0; rl && j < ns; j++) {
+ if (rl->conv((*slst)[j], wspace)) {
+ free((*slst)[j]);
+ (*slst)[j] = mystrdup(wspace);
+ }
+ }
+
+ // if suggestions removed by nosuggest, onlyincompound parameters
+ if (l == 0 && *slst) {
+ free(*slst);
+ *slst = NULL;
+ }
+ return l;
+}
+
+void Hunspell::free_list(char *** slst, int n) {
+ freelist(slst, n);
+}
+
+char * Hunspell::get_dic_encoding()
+{
+ return encoding;
+}
+
+#ifdef HUNSPELL_EXPERIMENTAL
+// XXX need UTF-8 support
+int Hunspell::suggest_auto(char*** slst, const char * word)
+{
+ char cw[MAXWORDUTF8LEN];
+ char wspace[MAXWORDUTF8LEN];
+ if (!pSMgr || maxdic == 0) return 0;
+ int wl = strlen(word);
+ if (utf8) {
+ if (wl >= MAXWORDUTF8LEN) return 0;
+ } else {
+ if (wl >= MAXWORDLEN) return 0;
+ }
+ int captype = 0;
+ int abbv = 0;
+ wl = cleanword(cw, word, &captype, &abbv);
+ if (wl == 0) return 0;
+ int ns = 0;
+ *slst = NULL; // HU, nsug in pSMgr->suggest
+
+ switch(captype) {
+ case NOCAP: {
+ ns = pSMgr->suggest_auto(slst, cw, ns);
+ if (ns>0) break;
+ break;
+ }
+
+ case INITCAP: {
+ memcpy(wspace,cw,(wl+1));
+ mkallsmall(wspace);
+ ns = pSMgr->suggest_auto(slst, wspace, ns);
+ for (int j=0; j < ns; j++)
+ mkinitcap((*slst)[j]);
+ ns = pSMgr->suggest_auto(slst, cw, ns);
+ break;
+
+ }
+
+ case HUHINITCAP:
+ case HUHCAP: {
+ ns = pSMgr->suggest_auto(slst, cw, ns);
+ if (ns == 0) {
+ memcpy(wspace,cw,(wl+1));
+ mkallsmall(wspace);
+ ns = pSMgr->suggest_auto(slst, wspace, ns);
+ }
+ break;
+ }
+
+ case ALLCAP: {
+ memcpy(wspace,cw,(wl+1));
+ mkallsmall(wspace);
+ ns = pSMgr->suggest_auto(slst, wspace, ns);
+
+ mkinitcap(wspace);
+ ns = pSMgr->suggest_auto(slst, wspace, ns);
+
+ for (int j=0; j < ns; j++)
+ mkallcap((*slst)[j]);
+ break;
+ }
+ }
+
+ // word reversing wrapper for complex prefixes
+ if (complexprefixes) {
+ for (int j = 0; j < ns; j++) {
+ if (utf8) reverseword_utf((*slst)[j]); else reverseword((*slst)[j]);
+ }
+ }
+
+ // expand suggestions with dot(s)
+ if (abbv && pAMgr && pAMgr->get_sugswithdots()) {
+ for (int j = 0; j < ns; j++) {
+ (*slst)[j] = (char *) realloc((*slst)[j], strlen((*slst)[j]) + 1 + abbv);
+ strcat((*slst)[j], word + strlen(word) - abbv);
+ }
+ }
+
+ // LANG_hu section: replace '-' with ' ' in Hungarian
+ if (langnum == LANG_hu) {
+ for (int j=0; j < ns; j++) {
+ char * pos = strchr((*slst)[j],'-');
+ if (pos) {
+ int info;
+ char w[MAXWORDUTF8LEN];
+ *pos = '\0';
+ strcpy(w, (*slst)[j]);
+ strcat(w, pos + 1);
+ spell(w, &info, NULL);
+ if ((info & SPELL_COMPOUND) && (info & SPELL_FORBIDDEN)) {
+ *pos = ' ';
+ } else *pos = '-';
+ }
+ }
+ }
+ // END OF LANG_hu section
+ return ns;
+}
+#endif
+
+int Hunspell::stem(char*** slst, char ** desc, int n)
+{
+ char result[MAXLNLEN];
+ char result2[MAXLNLEN];
+ *slst = NULL;
+ if (n == 0) return 0;
+ *result2 = '\0';
+ for (int i = 0; i < n; i++) {
+ *result = '\0';
+ // add compound word parts (except the last one)
+ char * s = (char *) desc[i];
+ char * part = strstr(s, MORPH_PART);
+ if (part) {
+ char * nextpart = strstr(part + 1, MORPH_PART);
+ while (nextpart) {
+ copy_field(result + strlen(result), part, MORPH_PART);
+ part = nextpart;
+ nextpart = strstr(part + 1, MORPH_PART);
+ }
+ s = part;
+ }
+
+ char **pl;
+ char tok[MAXLNLEN];
+ strcpy(tok, s);
+ char * alt = strstr(tok, " | ");
+ while (alt) {
+ alt[1] = MSEP_ALT;
+ alt = strstr(alt, " | ");
+ }
+ int pln = line_tok(tok, &pl, MSEP_ALT);
+ for (int k = 0; k < pln; k++) {
+ // add derivational suffixes
+ if (strstr(pl[k], MORPH_DERI_SFX)) {
+ // remove inflectional suffixes
+ char * is = strstr(pl[k], MORPH_INFL_SFX);
+ if (is) *is = '\0';
+ char * sg = pSMgr->suggest_gen(&(pl[k]), 1, pl[k]);
+ if (sg) {
+ char ** gen;
+ int genl = line_tok(sg, &gen, MSEP_REC);
+ free(sg);
+ for (int j = 0; j < genl; j++) {
+ sprintf(result2 + strlen(result2), "%c%s%s",
+ MSEP_REC, result, gen[j]);
+ }
+ freelist(&gen, genl);
+ }
+ } else {
+ sprintf(result2 + strlen(result2), "%c%s", MSEP_REC, result);
+ if (strstr(pl[k], MORPH_SURF_PFX)) {
+ copy_field(result2 + strlen(result2), pl[k], MORPH_SURF_PFX);
+ }
+ copy_field(result2 + strlen(result2), pl[k], MORPH_STEM);
+ }
+ }
+ freelist(&pl, pln);
+ }
+ int sln = line_tok(result2, slst, MSEP_REC);
+ return uniqlist(*slst, sln);
+
+}
+
+int Hunspell::stem(char*** slst, const char * word)
+{
+ char ** pl;
+ int pln = analyze(&pl, word);
+ int pln2 = stem(slst, pl, pln);
+ freelist(&pl, pln);
+ return pln2;
+}
+
+#ifdef HUNSPELL_EXPERIMENTAL
+int Hunspell::suggest_pos_stems(char*** slst, const char * word)
+{
+ char cw[MAXWORDUTF8LEN];
+ char wspace[MAXWORDUTF8LEN];
+ if (! pSMgr || maxdic == 0) return 0;
+ int wl = strlen(word);
+ if (utf8) {
+ if (wl >= MAXWORDUTF8LEN) return 0;
+ } else {
+ if (wl >= MAXWORDLEN) return 0;
+ }
+ int captype = 0;
+ int abbv = 0;
+ wl = cleanword(cw, word, &captype, &abbv);
+ if (wl == 0) return 0;
+
+ int ns = 0; // ns=0 = normalized input
+
+ *slst = NULL; // HU, nsug in pSMgr->suggest
+
+ switch(captype) {
+ case HUHCAP:
+ case NOCAP: {
+ ns = pSMgr->suggest_pos_stems(slst, cw, ns);
+
+ if ((abbv) && (ns == 0)) {
+ memcpy(wspace,cw,wl);
+ *(wspace+wl) = '.';
+ *(wspace+wl+1) = '\0';
+ ns = pSMgr->suggest_pos_stems(slst, wspace, ns);
+ }
+
+ break;
+ }
+
+ case INITCAP: {
+
+ ns = pSMgr->suggest_pos_stems(slst, cw, ns);
+
+ if (ns == 0 || ((*slst)[0][0] == '#')) {
+ memcpy(wspace,cw,(wl+1));
+ mkallsmall(wspace);
+ ns = pSMgr->suggest_pos_stems(slst, wspace, ns);
+ }
+
+ break;
+
+ }
+
+ case ALLCAP: {
+ ns = pSMgr->suggest_pos_stems(slst, cw, ns);
+ if (ns != 0) break;
+
+ memcpy(wspace,cw,(wl+1));
+ mkallsmall(wspace);
+ ns = pSMgr->suggest_pos_stems(slst, wspace, ns);
+
+ if (ns == 0) {
+ mkinitcap(wspace);
+ ns = pSMgr->suggest_pos_stems(slst, wspace, ns);
+ }
+ break;
+ }
+ }
+
+ return ns;
+}
+#endif // END OF HUNSPELL_EXPERIMENTAL CODE
+
+const char * Hunspell::get_wordchars()
+{
+ return pAMgr->get_wordchars();
+}
+
+unsigned short * Hunspell::get_wordchars_utf16(int * len)
+{
+ return pAMgr->get_wordchars_utf16(len);
+}
+
+char * Hunspell::get_try_string()
+{
+ return pAMgr->get_try_string();
+}
+
+void Hunspell::mkinitcap(char * p)
+{
+ if (!utf8) {
+ if (*p != '\0') *p = csconv[((unsigned char)*p)].cupper;
+ } else {
+ int len;
+ w_char u[MAXWORDLEN];
+ len = u8_u16(u, MAXWORDLEN, p);
+ unsigned short i = unicodetoupper((u[0].h << 8) + u[0].l, langnum);
+ u[0].h = (unsigned char) (i >> 8);
+ u[0].l = (unsigned char) (i & 0x00FF);
+ u16_u8(p, MAXWORDUTF8LEN, u, len);
+ }
+}
+
+int Hunspell::mkinitcap2(char * p, w_char * u, int nc)
+{
+ if (!utf8) {
+ if (*p != '\0') *p = csconv[((unsigned char)*p)].cupper;
+ } else if (nc > 0) {
+ unsigned short i = unicodetoupper((u[0].h << 8) + u[0].l, langnum);
+ u[0].h = (unsigned char) (i >> 8);
+ u[0].l = (unsigned char) (i & 0x00FF);
+ u16_u8(p, MAXWORDUTF8LEN, u, nc);
+ return strlen(p);
+ }
+ return nc;
+}
+
+int Hunspell::mkinitsmall2(char * p, w_char * u, int nc)
+{
+ if (!utf8) {
+ if (*p != '\0') *p = csconv[((unsigned char)*p)].clower;
+ } else if (nc > 0) {
+ unsigned short i = unicodetolower((u[0].h << 8) + u[0].l, langnum);
+ u[0].h = (unsigned char) (i >> 8);
+ u[0].l = (unsigned char) (i & 0x00FF);
+ u16_u8(p, MAXWORDUTF8LEN, u, nc);
+ return strlen(p);
+ }
+ return nc;
+}
+
+int Hunspell::add(const char * word)
+{
+ if (pHMgr[0]) return (pHMgr[0])->add(word);
+ return 0;
+}
+
+int Hunspell::add_with_affix(const char * word, const char * example)
+{
+ if (pHMgr[0]) return (pHMgr[0])->add_with_affix(word, example);
+ return 0;
+}
+
+int Hunspell::remove(const char * word)
+{
+ if (pHMgr[0]) return (pHMgr[0])->remove(word);
+ return 0;
+}
+
+const char * Hunspell::get_version()
+{
+ return pAMgr->get_version();
+}
+
+struct cs_info * Hunspell::get_csconv()
+{
+ return csconv;
+}
+
+void Hunspell::cat_result(char * result, char * st)
+{
+ if (st) {
+ if (*result) mystrcat(result, "\n", MAXLNLEN);
+ mystrcat(result, st, MAXLNLEN);
+ free(st);
+ }
+}
+
+int Hunspell::analyze(char*** slst, const char * word)
+{
+ char cw[MAXWORDUTF8LEN];
+ char wspace[MAXWORDUTF8LEN];
+ w_char unicw[MAXWORDLEN];
+ int wl2 = 0;
+ *slst = NULL;
+ if (! pSMgr || maxdic == 0) return 0;
+ int nc = strlen(word);
+ if (utf8) {
+ if (nc >= MAXWORDUTF8LEN) return 0;
+ } else {
+ if (nc >= MAXWORDLEN) return 0;
+ }
+ int captype = 0;
+ int abbv = 0;
+ int wl = 0;
+
+ // input conversion
+ RepList * rl = (pAMgr) ? pAMgr->get_iconvtable() : NULL;
+ if (rl && rl->conv(word, wspace)) wl = cleanword2(cw, wspace, unicw, &nc, &captype, &abbv);
+ else wl = cleanword2(cw, word, unicw, &nc, &captype, &abbv);
+
+ if (wl == 0) {
+ if (abbv) {
+ for (wl = 0; wl < abbv; wl++) cw[wl] = '.';
+ cw[wl] = '\0';
+ abbv = 0;
+ } else return 0;
+ }
+
+ char result[MAXLNLEN];
+ char * st = NULL;
+
+ *result = '\0';
+
+ int n = 0;
+ int n2 = 0;
+ int n3 = 0;
+
+ // test numbers
+ // LANG_hu section: set dash information for suggestions
+ if (langnum == LANG_hu) {
+ while ((n < wl) &&
+ (((cw[n] <= '9') && (cw[n] >= '0')) || (((cw[n] == '.') || (cw[n] == ',')) && (n > 0)))) {
+ n++;
+ if ((cw[n] == '.') || (cw[n] == ',')) {
+ if (((n2 == 0) && (n > 3)) ||
+ ((n2 > 0) && ((cw[n-1] == '.') || (cw[n-1] == ',')))) break;
+ n2++;
+ n3 = n;
+ }
+ }
+
+ if ((n == wl) && (n3 > 0) && (n - n3 > 3)) return 0;
+ if ((n == wl) || ((n>0) && ((cw[n]=='%') || (cw[n]=='\xB0')) && checkword(cw+n, NULL, NULL))) {
+ mystrcat(result, cw, MAXLNLEN);
+ result[n - 1] = '\0';
+ if (n == wl) cat_result(result, pSMgr->suggest_morph(cw + n - 1));
+ else {
+ char sign = cw[n];
+ cw[n] = '\0';
+ cat_result(result, pSMgr->suggest_morph(cw + n - 1));
+ mystrcat(result, "+", MAXLNLEN); // XXX SPEC. MORPHCODE
+ cw[n] = sign;
+ cat_result(result, pSMgr->suggest_morph(cw + n));
+ }
+ return line_tok(result, slst, MSEP_REC);
+ }
+ }
+ // END OF LANG_hu section
+
+ switch(captype) {
+ case HUHCAP:
+ case HUHINITCAP:
+ case NOCAP: {
+ cat_result(result, pSMgr->suggest_morph(cw));
+ if (abbv) {
+ memcpy(wspace,cw,wl);
+ *(wspace+wl) = '.';
+ *(wspace+wl+1) = '\0';
+ cat_result(result, pSMgr->suggest_morph(wspace));
+ }
+ break;
+ }
+ case INITCAP: {
+ wl = mkallsmall2(cw, unicw, nc);
+ memcpy(wspace,cw,(wl+1));
+ wl2 = mkinitcap2(cw, unicw, nc);
+ cat_result(result, pSMgr->suggest_morph(wspace));
+ cat_result(result, pSMgr->suggest_morph(cw));
+ if (abbv) {
+ *(wspace+wl) = '.';
+ *(wspace+wl+1) = '\0';
+ cat_result(result, pSMgr->suggest_morph(wspace));
+
+ memcpy(wspace, cw, wl2);
+ *(wspace+wl2) = '.';
+ *(wspace+wl2+1) = '\0';
+
+ cat_result(result, pSMgr->suggest_morph(wspace));
+ }
+ break;
+ }
+ case ALLCAP: {
+ cat_result(result, pSMgr->suggest_morph(cw));
+ if (abbv) {
+ memcpy(wspace,cw,wl);
+ *(wspace+wl) = '.';
+ *(wspace+wl+1) = '\0';
+ cat_result(result, pSMgr->suggest_morph(cw));
+ }
+ wl = mkallsmall2(cw, unicw, nc);
+ memcpy(wspace,cw,(wl+1));
+ wl2 = mkinitcap2(cw, unicw, nc);
+
+ cat_result(result, pSMgr->suggest_morph(wspace));
+ cat_result(result, pSMgr->suggest_morph(cw));
+ if (abbv) {
+ *(wspace+wl) = '.';
+ *(wspace+wl+1) = '\0';
+ cat_result(result, pSMgr->suggest_morph(wspace));
+
+ memcpy(wspace, cw, wl2);
+ *(wspace+wl2) = '.';
+ *(wspace+wl2+1) = '\0';
+
+ cat_result(result, pSMgr->suggest_morph(wspace));
+ }
+ break;
+ }
+ }
+
+ if (*result) {
+ // word reversing wrapper for complex prefixes
+ if (complexprefixes) {
+ if (utf8) reverseword_utf(result); else reverseword(result);
+ }
+ return line_tok(result, slst, MSEP_REC);
+ }
+
+ // compound word with dash (HU) I18n
+ char * dash = NULL;
+ int nresult = 0;
+ // LANG_hu section: set dash information for suggestions
+ if (langnum == LANG_hu) dash = (char *) strchr(cw,'-');
+ if ((langnum == LANG_hu) && dash) {
+ *dash='\0';
+ // examine 2 sides of the dash
+ if (dash[1] == '\0') { // base word ending with dash
+ if (spell(cw)) {
+ char * p = pSMgr->suggest_morph(cw);
+ if (p) {
+ int ret = line_tok(p, slst, MSEP_REC);
+ free(p);
+ return ret;
+ }
+
+ }
+ } else if ((dash[1] == 'e') && (dash[2] == '\0')) { // XXX (HU) -e hat.
+ if (spell(cw) && (spell("-e"))) {
+ st = pSMgr->suggest_morph(cw);
+ if (st) {
+ mystrcat(result, st, MAXLNLEN);
+ free(st);
+ }
+ mystrcat(result,"+", MAXLNLEN); // XXX spec. separator in MORPHCODE
+ st = pSMgr->suggest_morph("-e");
+ if (st) {
+ mystrcat(result, st, MAXLNLEN);
+ free(st);
+ }
+ return line_tok(result, slst, MSEP_REC);
+ }
+ } else {
+ // first word ending with dash: word- XXX ???
+ char r2 = *(dash + 1);
+ dash[0]='-';
+ dash[1]='\0';
+ nresult = spell(cw);
+ dash[1] = r2;
+ dash[0]='\0';
+ if (nresult && spell(dash+1) && ((strlen(dash+1) > 1) ||
+ ((dash[1] > '0') && (dash[1] < '9')))) {
+ st = pSMgr->suggest_morph(cw);
+ if (st) {
+ mystrcat(result, st, MAXLNLEN);
+ free(st);
+ mystrcat(result,"+", MAXLNLEN); // XXX spec. separator in MORPHCODE
+ }
+ st = pSMgr->suggest_morph(dash+1);
+ if (st) {
+ mystrcat(result, st, MAXLNLEN);
+ free(st);
+ }
+ return line_tok(result, slst, MSEP_REC);
+ }
+ }
+ // affixed number in correct word
+ if (nresult && (dash > cw) && (((*(dash-1)<='9') &&
+ (*(dash-1)>='0')) || (*(dash-1)=='.'))) {
+ *dash='-';
+ n = 1;
+ if (*(dash - n) == '.') n++;
+ // search first not a number character to left from dash
+ while (((dash - n)>=cw) && ((*(dash - n)=='0') || (n < 3)) && (n < 6)) {
+ n++;
+ }
+ if ((dash - n) < cw) n--;
+ // numbers: valami1000000-hoz
+ // examine 100000-hoz, 10000-hoz 1000-hoz, 10-hoz,
+ // 56-hoz, 6-hoz
+ for(; n >= 1; n--) {
+ if ((*(dash - n) >= '0') && (*(dash - n) <= '9') && checkword(dash - n, NULL, NULL)) {
+ mystrcat(result, cw, MAXLNLEN);
+ result[dash - cw - n] = '\0';
+ st = pSMgr->suggest_morph(dash - n);
+ if (st) {
+ mystrcat(result, st, MAXLNLEN);
+ free(st);
+ }
+ return line_tok(result, slst, MSEP_REC);
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+int Hunspell::generate(char*** slst, const char * word, char ** pl, int pln)
+{
+ *slst = NULL;
+ if (!pSMgr || !pln) return 0;
+ char **pl2;
+ int pl2n = analyze(&pl2, word);
+ int captype = 0;
+ int abbv = 0;
+ char cw[MAXWORDUTF8LEN];
+ cleanword(cw, word, &captype, &abbv);
+ char result[MAXLNLEN];
+ *result = '\0';
+
+ for (int i = 0; i < pln; i++) {
+ cat_result(result, pSMgr->suggest_gen(pl2, pl2n, pl[i]));
+ }
+ freelist(&pl2, pl2n);
+
+ if (*result) {
+ // allcap
+ if (captype == ALLCAP) mkallcap(result);
+
+ // line split
+ int linenum = line_tok(result, slst, MSEP_REC);
+
+ // capitalize
+ if (captype == INITCAP || captype == HUHINITCAP) {
+ for (int j=0; j < linenum; j++) mkinitcap((*slst)[j]);
+ }
+
+ // temporary filtering of prefix related errors (eg.
+ // generate("undrinkable", "eats") --> "undrinkables" and "*undrinks")
+
+ int r = 0;
+ for (int j=0; j < linenum; j++) {
+ if (!spell((*slst)[j])) {
+ free((*slst)[j]);
+ (*slst)[j] = NULL;
+ } else {
+ if (r < j) (*slst)[r] = (*slst)[j];
+ r++;
+ }
+ }
+ if (r > 0) return r;
+ free(*slst);
+ *slst = NULL;
+ }
+ return 0;
+}
+
+int Hunspell::generate(char*** slst, const char * word, const char * pattern)
+{
+ char **pl;
+ int pln = analyze(&pl, pattern);
+ int n = generate(slst, word, pl, pln);
+ freelist(&pl, pln);
+ return uniqlist(*slst, n);
+}
+
+// minimal XML parser functions
+int Hunspell::get_xml_par(char * dest, const char * par, int max)
+{
+ char * d = dest;
+ if (!par) return 0;
+ char end = *par;
+ char * dmax = dest + max;
+ if (end == '>') end = '<';
+ else if (end != '\'' && end != '"') return 0; // bad XML
+ for (par++; d < dmax && *par != '\0' && *par != end; par++, d++) *d = *par;
+ *d = '\0';
+ mystrrep(dest, "&lt;", "<");
+ mystrrep(dest, "&amp;", "&");
+ return (int)(d - dest);
+}
+
+int Hunspell::get_langnum() const
+{
+ return langnum;
+}
+
+// return the beginning of the element (attr == NULL) or the attribute
+const char * Hunspell::get_xml_pos(const char * s, const char * attr)
+{
+ const char * end = strchr(s, '>');
+ const char * p = s;
+ if (attr == NULL) return end;
+ do {
+ p = strstr(p, attr);
+ if (!p || p >= end) return 0;
+ } while (*(p-1) != ' ' && *(p-1) != '\n');
+ return p + strlen(attr);
+}
+
+int Hunspell::check_xml_par(const char * q, const char * attr, const char * value) {
+ char cw[MAXWORDUTF8LEN];
+ if (get_xml_par(cw, get_xml_pos(q, attr), MAXWORDUTF8LEN - 1) &&
+ strcmp(cw, value) == 0) return 1;
+ return 0;
+}
+
+int Hunspell::get_xml_list(char ***slst, char * list, const char * tag) {
+ int n = 0;
+ char * p;
+ if (!list) return 0;
+ for (p = list; (p = strstr(p, tag)); p++) n++;
+ if (n == 0) return 0;
+ *slst = (char **) malloc(sizeof(char *) * n);
+ if (!*slst) return 0;
+ for (p = list, n = 0; (p = strstr(p, tag)); p++, n++) {
+ int l = strlen(p);
+ (*slst)[n] = (char *) malloc(l + 1);
+ if (!(*slst)[n]) return n;
+ if (!get_xml_par((*slst)[n], p + strlen(tag) - 1, l)) {
+ free((*slst)[n]);
+ break;
+ }
+ }
+ return n;
+}
+
+int Hunspell::spellml(char*** slst, const char * word)
+{
+ char *q, *q2;
+ char cw[MAXWORDUTF8LEN], cw2[MAXWORDUTF8LEN];
+ q = (char *) strstr(word, "<query");
+ if (!q) return 0; // bad XML input
+ q2 = strchr(q, '>');
+ if (!q2) return 0; // bad XML input
+ q2 = strstr(q2, "<word");
+ if (!q2) return 0; // bad XML input
+ if (check_xml_par(q, "type=", "analyze")) {
+ int n = 0, s = 0;
+ if (get_xml_par(cw, strchr(q2, '>'), MAXWORDUTF8LEN - 10)) n = analyze(slst, cw);
+ if (n == 0) return 0;
+ // convert the result to <code><a>ana1</a><a>ana2</a></code> format
+ for (int i = 0; i < n; i++) s+= strlen((*slst)[i]);
+ char * r = (char *) malloc(6 + 5 * s + 7 * n + 7 + 1); // XXX 5*s->&->&amp;
+ if (!r) return 0;
+ strcpy(r, "<code>");
+ for (int i = 0; i < n; i++) {
+ int l = strlen(r);
+ strcpy(r + l, "<a>");
+ strcpy(r + l + 3, (*slst)[i]);
+ mystrrep(r + l + 3, "\t", " ");
+ mystrrep(r + l + 3, "<", "&lt;");
+ mystrrep(r + l + 3, "&", "&amp;");
+ strcat(r, "</a>");
+ free((*slst)[i]);
+ }
+ strcat(r, "</code>");
+ (*slst)[0] = r;
+ return 1;
+ } else if (check_xml_par(q, "type=", "stem")) {
+ if (get_xml_par(cw, strchr(q2, '>'), MAXWORDUTF8LEN - 1)) return stem(slst, cw);
+ } else if (check_xml_par(q, "type=", "generate")) {
+ int n = get_xml_par(cw, strchr(q2, '>'), MAXWORDUTF8LEN - 1);
+ if (n == 0) return 0;
+ char * q3 = strstr(q2 + 1, "<word");
+ if (q3) {
+ if (get_xml_par(cw2, strchr(q3, '>'), MAXWORDUTF8LEN - 1)) {
+ return generate(slst, cw, cw2);
+ }
+ } else {
+ if ((q2 = strstr(q2 + 1, "<code"))) {
+ char ** slst2;
+ if ((n = get_xml_list(&slst2, strchr(q2, '>'), "<a>"))) {
+ int n2 = generate(slst, cw, slst2, n);
+ freelist(&slst2, n);
+ return uniqlist(*slst, n2);
+ }
+ freelist(&slst2, n);
+ }
+ }
+ }
+ return 0;
+}
+
+
+#ifdef HUNSPELL_EXPERIMENTAL
+// XXX need UTF-8 support
+char * Hunspell::morph_with_correction(const char * word)
+{
+ char cw[MAXWORDUTF8LEN];
+ char wspace[MAXWORDUTF8LEN];
+ if (! pSMgr || maxdic == 0) return NULL;
+ int wl = strlen(word);
+ if (utf8) {
+ if (wl >= MAXWORDUTF8LEN) return NULL;
+ } else {
+ if (wl >= MAXWORDLEN) return NULL;
+ }
+ int captype = 0;
+ int abbv = 0;
+ wl = cleanword(cw, word, &captype, &abbv);
+ if (wl == 0) return NULL;
+
+ char result[MAXLNLEN];
+ char * st = NULL;
+
+ *result = '\0';
+
+
+ switch(captype) {
+ case NOCAP: {
+ st = pSMgr->suggest_morph_for_spelling_error(cw);
+ if (st) {
+ mystrcat(result, st, MAXLNLEN);
+ free(st);
+ }
+ if (abbv) {
+ memcpy(wspace,cw,wl);
+ *(wspace+wl) = '.';
+ *(wspace+wl+1) = '\0';
+ st = pSMgr->suggest_morph_for_spelling_error(wspace);
+ if (st) {
+ if (*result) mystrcat(result, "\n", MAXLNLEN);
+ mystrcat(result, st, MAXLNLEN);
+ free(st);
+ }
+ }
+ break;
+ }
+ case INITCAP: {
+ memcpy(wspace,cw,(wl+1));
+ mkallsmall(wspace);
+ st = pSMgr->suggest_morph_for_spelling_error(wspace);
+ if (st) {
+ mystrcat(result, st, MAXLNLEN);
+ free(st);
+ }
+ st = pSMgr->suggest_morph_for_spelling_error(cw);
+ if (st) {
+ if (*result) mystrcat(result, "\n", MAXLNLEN);
+ mystrcat(result, st, MAXLNLEN);
+ free(st);
+ }
+ if (abbv) {
+ memcpy(wspace,cw,wl);
+ *(wspace+wl) = '.';
+ *(wspace+wl+1) = '\0';
+ mkallsmall(wspace);
+ st = pSMgr->suggest_morph_for_spelling_error(wspace);
+ if (st) {
+ if (*result) mystrcat(result, "\n", MAXLNLEN);
+ mystrcat(result, st, MAXLNLEN);
+ free(st);
+ }
+ mkinitcap(wspace);
+ st = pSMgr->suggest_morph_for_spelling_error(wspace);
+ if (st) {
+ if (*result) mystrcat(result, "\n", MAXLNLEN);
+ mystrcat(result, st, MAXLNLEN);
+ free(st);
+ }
+ }
+ break;
+ }
+ case HUHCAP: {
+ st = pSMgr->suggest_morph_for_spelling_error(cw);
+ if (st) {
+ mystrcat(result, st, MAXLNLEN);
+ free(st);
+ }
+ memcpy(wspace,cw,(wl+1));
+ mkallsmall(wspace);
+ st = pSMgr->suggest_morph_for_spelling_error(wspace);
+ if (st) {
+ if (*result) mystrcat(result, "\n", MAXLNLEN);
+ mystrcat(result, st, MAXLNLEN);
+ free(st);
+ }
+ break;
+ }
+ case ALLCAP: {
+ memcpy(wspace,cw,(wl+1));
+ st = pSMgr->suggest_morph_for_spelling_error(wspace);
+ if (st) {
+ mystrcat(result, st, MAXLNLEN);
+ free(st);
+ }
+ mkallsmall(wspace);
+ st = pSMgr->suggest_morph_for_spelling_error(wspace);
+ if (st) {
+ if (*result) mystrcat(result, "\n", MAXLNLEN);
+ mystrcat(result, st, MAXLNLEN);
+ free(st);
+ }
+ mkinitcap(wspace);
+ st = pSMgr->suggest_morph_for_spelling_error(wspace);
+ if (st) {
+ if (*result) mystrcat(result, "\n", MAXLNLEN);
+ mystrcat(result, st, MAXLNLEN);
+ free(st);
+ }
+ if (abbv) {
+ memcpy(wspace,cw,(wl+1));
+ *(wspace+wl) = '.';
+ *(wspace+wl+1) = '\0';
+ if (*result) mystrcat(result, "\n", MAXLNLEN);
+ st = pSMgr->suggest_morph_for_spelling_error(wspace);
+ if (st) {
+ mystrcat(result, st, MAXLNLEN);
+ free(st);
+ }
+ mkallsmall(wspace);
+ st = pSMgr->suggest_morph_for_spelling_error(wspace);
+ if (st) {
+ if (*result) mystrcat(result, "\n", MAXLNLEN);
+ mystrcat(result, st, MAXLNLEN);
+ free(st);
+ }
+ mkinitcap(wspace);
+ st = pSMgr->suggest_morph_for_spelling_error(wspace);
+ if (st) {
+ if (*result) mystrcat(result, "\n", MAXLNLEN);
+ mystrcat(result, st, MAXLNLEN);
+ free(st);
+ }
+ }
+ break;
+ }
+ }
+
+ if (*result) return mystrdup(result);
+ return NULL;
+}
+
+#endif // END OF HUNSPELL_EXPERIMENTAL CODE
+
+Hunhandle *Hunspell_create(const char * affpath, const char * dpath)
+{
+ return (Hunhandle*)(new Hunspell(affpath, dpath));
+}
+
+Hunhandle *Hunspell_create_key(const char * affpath, const char * dpath,
+ const char * key)
+{
+ return (Hunhandle*)(new Hunspell(affpath, dpath, key));
+}
+
+void Hunspell_destroy(Hunhandle *pHunspell)
+{
+ delete (Hunspell*)(pHunspell);
+}
+
+int Hunspell_spell(Hunhandle *pHunspell, const char *word)
+{
+ return ((Hunspell*)pHunspell)->spell(word);
+}
+
+char *Hunspell_get_dic_encoding(Hunhandle *pHunspell)
+{
+ return ((Hunspell*)pHunspell)->get_dic_encoding();
+}
+
+int Hunspell_suggest(Hunhandle *pHunspell, char*** slst, const char * word)
+{
+ return ((Hunspell*)pHunspell)->suggest(slst, word);
+}
+
+int Hunspell_analyze(Hunhandle *pHunspell, char*** slst, const char * word)
+{
+ return ((Hunspell*)pHunspell)->analyze(slst, word);
+}
+
+int Hunspell_stem(Hunhandle *pHunspell, char*** slst, const char * word)
+{
+ return ((Hunspell*)pHunspell)->stem(slst, word);
+}
+
+int Hunspell_stem2(Hunhandle *pHunspell, char*** slst, char** desc, int n)
+{
+ return ((Hunspell*)pHunspell)->stem(slst, desc, n);
+}
+
+int Hunspell_generate(Hunhandle *pHunspell, char*** slst, const char * word,
+ const char * word2)
+{
+ return ((Hunspell*)pHunspell)->generate(slst, word, word2);
+}
+
+int Hunspell_generate2(Hunhandle *pHunspell, char*** slst, const char * word,
+ char** desc, int n)
+{
+ return ((Hunspell*)pHunspell)->generate(slst, word, desc, n);
+}
+
+ /* functions for run-time modification of the dictionary */
+
+ /* add word to the run-time dictionary */
+
+int Hunspell_add(Hunhandle *pHunspell, const char * word) {
+ return ((Hunspell*)pHunspell)->add(word);
+}
+
+ /* add word to the run-time dictionary with affix flags of
+ * the example (a dictionary word): Hunspell will recognize
+ * affixed forms of the new word, too.
+ */
+
+int Hunspell_add_with_affix(Hunhandle *pHunspell, const char * word,
+ const char * example) {
+ return ((Hunspell*)pHunspell)->add_with_affix(word, example);
+}
+
+ /* remove word from the run-time dictionary */
+
+int Hunspell_remove(Hunhandle *pHunspell, const char * word) {
+ return ((Hunspell*)pHunspell)->remove(word);
+}
+
+void Hunspell_free_list(Hunhandle *, char *** slst, int n) {
+ freelist(slst, n);
+}
diff --git a/Plugins/spellchecker/hunspell/hunspell.dsp b/Plugins/spellchecker/hunspell/hunspell.dsp
new file mode 100644
index 0000000..c182621
--- /dev/null
+++ b/Plugins/spellchecker/hunspell/hunspell.dsp
@@ -0,0 +1,164 @@
+# Microsoft Developer Studio Project File - Name="hunspell" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Static Library" 0x0104
+
+CFG=hunspell - Win32 Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "hunspell.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "hunspell.mak" CFG="hunspell - Win32 Debug"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "hunspell - Win32 Release" (based on "Win32 (x86) Static Library")
+!MESSAGE "hunspell - Win32 Debug" (based on "Win32 (x86) Static Library")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "hunspell - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "W32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c
+# ADD CPP /nologo /W3 /GX /O2 /D "W32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c
+# ADD BASE RSC /l 0x40e /d "NDEBUG"
+# ADD RSC /l 0x40e /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LIB32=link.exe -lib
+# ADD BASE LIB32 /nologo
+# ADD LIB32 /nologo
+
+!ELSEIF "$(CFG)" == "hunspell - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "W32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c
+# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /D "W32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c
+# ADD BASE RSC /l 0x40e /d "_DEBUG"
+# ADD RSC /l 0x40e /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LIB32=link.exe -lib
+# ADD BASE LIB32 /nologo
+# ADD LIB32 /nologo
+
+!ENDIF
+
+# Begin Target
+
+# Name "hunspell - Win32 Release"
+# Name "hunspell - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=.\affentry.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\affixmgr.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\csutil.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\dictmgr.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\hashmgr.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\hunspell.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\suggestmgr.cxx
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=.\affentry.hxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\affixmgr.hxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\atypes.hxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\baseaffix.hxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\csutil.hxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\dictmgr.hxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\hashmgr.hxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\htypes.hxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\langnum.hxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\hunspell.hxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\suggestmgr.hxx
+# End Source File
+# End Group
+# End Target
+# End Project
diff --git a/Plugins/spellchecker/hunspell/hunspell.h b/Plugins/spellchecker/hunspell/hunspell.h
new file mode 100644
index 0000000..627968a
--- /dev/null
+++ b/Plugins/spellchecker/hunspell/hunspell.h
@@ -0,0 +1,95 @@
+#ifndef _MYSPELLMGR_H_
+#define _MYSPELLMGR_H_
+
+#include "hunvisapi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct Hunhandle Hunhandle;
+
+LIBHUNSPELL_DLL_EXPORTED Hunhandle *Hunspell_create(const char * affpath, const char * dpath);
+
+LIBHUNSPELL_DLL_EXPORTED Hunhandle *Hunspell_create_key(const char * affpath, const char * dpath,
+ const char * key);
+
+LIBHUNSPELL_DLL_EXPORTED void Hunspell_destroy(Hunhandle *pHunspell);
+
+/* spell(word) - spellcheck word
+ * output: 0 = bad word, not 0 = good word
+ */
+LIBHUNSPELL_DLL_EXPORTED int Hunspell_spell(Hunhandle *pHunspell, const char *);
+
+LIBHUNSPELL_DLL_EXPORTED char *Hunspell_get_dic_encoding(Hunhandle *pHunspell);
+
+/* suggest(suggestions, word) - search suggestions
+ * input: pointer to an array of strings pointer and the (bad) word
+ * array of strings pointer (here *slst) may not be initialized
+ * output: number of suggestions in string array, and suggestions in
+ * a newly allocated array of strings (*slts will be NULL when number
+ * of suggestion equals 0.)
+ */
+LIBHUNSPELL_DLL_EXPORTED int Hunspell_suggest(Hunhandle *pHunspell, char*** slst, const char * word);
+
+ /* morphological functions */
+
+ /* analyze(result, word) - morphological analysis of the word */
+
+LIBHUNSPELL_DLL_EXPORTED int Hunspell_analyze(Hunhandle *pHunspell, char*** slst, const char * word);
+
+ /* stem(result, word) - stemmer function */
+
+LIBHUNSPELL_DLL_EXPORTED int Hunspell_stem(Hunhandle *pHunspell, char*** slst, const char * word);
+
+ /* stem(result, analysis, n) - get stems from a morph. analysis
+ * example:
+ * char ** result, result2;
+ * int n1 = Hunspell_analyze(result, "words");
+ * int n2 = Hunspell_stem2(result2, result, n1);
+ */
+
+LIBHUNSPELL_DLL_EXPORTED int Hunspell_stem2(Hunhandle *pHunspell, char*** slst, char** desc, int n);
+
+ /* generate(result, word, word2) - morphological generation by example(s) */
+
+LIBHUNSPELL_DLL_EXPORTED int Hunspell_generate(Hunhandle *pHunspell, char*** slst, const char * word,
+ const char * word2);
+
+ /* generate(result, word, desc, n) - generation by morph. description(s)
+ * example:
+ * char ** result;
+ * char * affix = "is:plural"; // description depends from dictionaries, too
+ * int n = Hunspell_generate2(result, "word", &affix, 1);
+ * for (int i = 0; i < n; i++) printf("%s\n", result[i]);
+ */
+
+LIBHUNSPELL_DLL_EXPORTED int Hunspell_generate2(Hunhandle *pHunspell, char*** slst, const char * word,
+ char** desc, int n);
+
+ /* functions for run-time modification of the dictionary */
+
+ /* add word to the run-time dictionary */
+
+LIBHUNSPELL_DLL_EXPORTED int Hunspell_add(Hunhandle *pHunspell, const char * word);
+
+ /* add word to the run-time dictionary with affix flags of
+ * the example (a dictionary word): Hunspell will recognize
+ * affixed forms of the new word, too.
+ */
+
+LIBHUNSPELL_DLL_EXPORTED int Hunspell_add_with_affix(Hunhandle *pHunspell, const char * word, const char * example);
+
+ /* remove word from the run-time dictionary */
+
+LIBHUNSPELL_DLL_EXPORTED int Hunspell_remove(Hunhandle *pHunspell, const char * word);
+
+ /* free suggestion lists */
+
+LIBHUNSPELL_DLL_EXPORTED void Hunspell_free_list(Hunhandle *pHunspell, char *** slst, int n);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/Plugins/spellchecker/hunspell/hunspell.hxx b/Plugins/spellchecker/hunspell/hunspell.hxx
new file mode 100644
index 0000000..a25c637
--- /dev/null
+++ b/Plugins/spellchecker/hunspell/hunspell.hxx
@@ -0,0 +1,174 @@
+#include "hunvisapi.h"
+
+#include "hashmgr.hxx"
+#include "affixmgr.hxx"
+#include "suggestmgr.hxx"
+#include "langnum.hxx"
+
+#define SPELL_XML "<?xml?>"
+
+#define MAXDIC 20
+#define MAXSUGGESTION 15
+#define MAXSHARPS 5
+
+#define HUNSPELL_OK (1 << 0)
+#define HUNSPELL_OK_WARN (1 << 1)
+
+#ifndef _MYSPELLMGR_HXX_
+#define _MYSPELLMGR_HXX_
+
+class LIBHUNSPELL_DLL_EXPORTED Hunspell
+{
+ AffixMgr* pAMgr;
+ HashMgr* pHMgr[MAXDIC];
+ int maxdic;
+ SuggestMgr* pSMgr;
+ char * affixpath;
+ char * encoding;
+ struct cs_info * csconv;
+ int langnum;
+ int utf8;
+ int complexprefixes;
+ char** wordbreak;
+
+public:
+
+ /* Hunspell(aff, dic) - constructor of Hunspell class
+ * input: path of affix file and dictionary file
+ */
+
+ Hunspell(const char * affpath, const char * dpath, const char * key = NULL);
+ ~Hunspell();
+
+ /* load extra dictionaries (only dic files) */
+ int add_dic(const char * dpath, const char * key = NULL);
+
+ /* spell(word) - spellcheck word
+ * output: 0 = bad word, not 0 = good word
+ *
+ * plus output:
+ * info: information bit array, fields:
+ * SPELL_COMPOUND = a compound word
+ * SPELL_FORBIDDEN = an explicit forbidden word
+ * root: root (stem), when input is a word with affix(es)
+ */
+
+ int spell(const char * word, int * info = NULL, char ** root = NULL);
+
+ /* suggest(suggestions, word) - search suggestions
+ * input: pointer to an array of strings pointer and the (bad) word
+ * array of strings pointer (here *slst) may not be initialized
+ * output: number of suggestions in string array, and suggestions in
+ * a newly allocated array of strings (*slts will be NULL when number
+ * of suggestion equals 0.)
+ */
+
+ int suggest(char*** slst, const char * word);
+
+ /* deallocate suggestion lists */
+
+ void free_list(char *** slst, int n);
+
+ char * get_dic_encoding();
+
+ /* morphological functions */
+
+ /* analyze(result, word) - morphological analysis of the word */
+
+ int analyze(char*** slst, const char * word);
+
+ /* stem(result, word) - stemmer function */
+
+ int stem(char*** slst, const char * word);
+
+ /* stem(result, analysis, n) - get stems from a morph. analysis
+ * example:
+ * char ** result, result2;
+ * int n1 = analyze(&result, "words");
+ * int n2 = stem(&result2, result, n1);
+ */
+
+ int stem(char*** slst, char ** morph, int n);
+
+ /* generate(result, word, word2) - morphological generation by example(s) */
+
+ int generate(char*** slst, const char * word, const char * word2);
+
+ /* generate(result, word, desc, n) - generation by morph. description(s)
+ * example:
+ * char ** result;
+ * char * affix = "is:plural"; // description depends from dictionaries, too
+ * int n = generate(&result, "word", &affix, 1);
+ * for (int i = 0; i < n; i++) printf("%s\n", result[i]);
+ */
+
+ int generate(char*** slst, const char * word, char ** desc, int n);
+
+ /* functions for run-time modification of the dictionary */
+
+ /* add word to the run-time dictionary */
+
+ int add(const char * word);
+
+ /* add word to the run-time dictionary with affix flags of
+ * the example (a dictionary word): Hunspell will recognize
+ * affixed forms of the new word, too.
+ */
+
+ int add_with_affix(const char * word, const char * example);
+
+ /* remove word from the run-time dictionary */
+
+ int remove(const char * word);
+
+ /* other */
+
+ /* get extra word characters definied in affix file for tokenization */
+ const char * get_wordchars();
+ unsigned short * get_wordchars_utf16(int * len);
+
+ char * get_try_string();
+
+ struct cs_info * get_csconv();
+ const char * get_version();
+
+ int get_langnum() const;
+
+ /* experimental and deprecated functions */
+
+#ifdef HUNSPELL_EXPERIMENTAL
+ /* suffix is an affix flag string, similarly in dictionary files */
+ int put_word_suffix(const char * word, const char * suffix);
+ char * morph_with_correction(const char * word);
+
+ /* spec. suggestions */
+ int suggest_auto(char*** slst, const char * word);
+ int suggest_pos_stems(char*** slst, const char * word);
+#endif
+
+private:
+ int cleanword(char *, const char *, int * pcaptype, int * pabbrev);
+ int cleanword2(char *, const char *, w_char *, int * w_len, int * pcaptype, int * pabbrev);
+ void mkinitcap(char *);
+ int mkinitcap2(char * p, w_char * u, int nc);
+ int mkinitsmall2(char * p, w_char * u, int nc);
+ void mkallcap(char *);
+ int mkallcap2(char * p, w_char * u, int nc);
+ void mkallsmall(char *);
+ int mkallsmall2(char * p, w_char * u, int nc);
+ struct hentry * checkword(const char *, int * info, char **root);
+ char * sharps_u8_l1(char * dest, char * source);
+ hentry * spellsharps(char * base, char *, int, int, char * tmp, int * info, char **root);
+ int is_keepcase(const hentry * rv);
+ int insert_sug(char ***slst, char * word, int ns);
+ void cat_result(char * result, char * st);
+ char * stem_description(const char * desc);
+ int spellml(char*** slst, const char * word);
+ int get_xml_par(char * dest, const char * par, int maxl);
+ const char * get_xml_pos(const char * s, const char * attr);
+ int get_xml_list(char ***slst, char * list, const char * tag);
+ int check_xml_par(const char * q, const char * attr, const char * value);
+
+};
+
+#endif
diff --git a/Plugins/spellchecker/hunspell/hunvisapi.h b/Plugins/spellchecker/hunspell/hunvisapi.h
new file mode 100644
index 0000000..4712280
--- /dev/null
+++ b/Plugins/spellchecker/hunspell/hunvisapi.h
@@ -0,0 +1,18 @@
+#ifndef _HUNSPELL_VISIBILITY_H_
+#define _HUNSPELL_VISIBILITY_H_
+
+#if defined(HUNSPELL_STATIC)
+# define LIBHUNSPELL_DLL_EXPORTED
+#elif defined(_MSC_VER)
+# if defined(BUILDING_LIBHUNSPELL)
+# define LIBHUNSPELL_DLL_EXPORTED __declspec(dllexport)
+# else
+# define LIBHUNSPELL_DLL_EXPORTED __declspec(dllimport)
+# endif
+#elif BUILDING_LIBHUNSPELL && 1
+# define LIBHUNSPELL_DLL_EXPORTED __attribute__((__visibility__("default")))
+#else
+# define LIBHUNSPELL_DLL_EXPORTED
+#endif
+
+#endif
diff --git a/Plugins/spellchecker/hunspell/hunzip.cxx b/Plugins/spellchecker/hunspell/hunzip.cxx
new file mode 100644
index 0000000..b50599f
--- /dev/null
+++ b/Plugins/spellchecker/hunspell/hunzip.cxx
@@ -0,0 +1,193 @@
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "hunzip.hxx"
+
+#define CODELEN 65536
+#define BASEBITREC 5000
+
+#define UNCOMPRESSED '\002'
+#define MAGIC "hz0"
+#define MAGIC_ENCRYPT "hz1"
+#define MAGICLEN (sizeof(MAGIC) - 1)
+
+int Hunzip::fail(const char * err, const char * par) {
+ fprintf(stderr, err, par);
+ return -1;
+}
+
+Hunzip::Hunzip(const char * file, const char * key) {
+ bufsiz = 0;
+ lastbit = 0;
+ inc = 0;
+ outc = 0;
+ dec = NULL;
+ fin = NULL;
+ filename = (char *) malloc(strlen(file) + 1);
+ if (filename) strcpy(filename, file);
+ if (getcode(key) == -1) bufsiz = -1;
+ else bufsiz = getbuf();
+}
+
+int Hunzip::getcode(const char * key) {
+ unsigned char c[2];
+ int i, j, n, p;
+ int allocatedbit = BASEBITREC;
+ const char * enc = key;
+
+ if (!filename) return -1;
+
+ fin = fopen(filename, "rb");
+ if (!fin) return -1;
+
+ // read magic number
+ if ((fread(in, 1, 3, fin) < MAGICLEN)
+ || !(strncmp(MAGIC, in, MAGICLEN) == 0 ||
+ strncmp(MAGIC_ENCRYPT, in, MAGICLEN) == 0)) {
+ return fail(MSG_FORMAT, filename);
+ }
+
+ // check encryption
+ if (strncmp(MAGIC_ENCRYPT, in, MAGICLEN) == 0) {
+ unsigned char cs;
+ if (!key) return fail(MSG_KEY, filename);
+ if (fread(&c, 1, 1, fin) < 1) return fail(MSG_FORMAT, filename);
+ for (cs = 0; *enc; enc++) cs ^= *enc;
+ if (cs != c[0]) return fail(MSG_KEY, filename);
+ enc = key;
+ } else key = NULL;
+
+ // read record count
+ if (fread(&c, 1, 2, fin) < 2) return fail(MSG_FORMAT, filename);
+
+ if (key) {
+ c[0] ^= *enc;
+ if (*(++enc) == '\0') enc = key;
+ c[1] ^= *enc;
+ }
+
+ n = ((int) c[0] << 8) + c[1];
+ dec = (struct bit *) malloc(BASEBITREC * sizeof(struct bit));
+ if (!dec) return fail(MSG_MEMORY, filename);
+ dec[0].v[0] = 0;
+ dec[0].v[1] = 0;
+
+ // read codes
+ for (i = 0; i < n; i++) {
+ unsigned char l;
+ if (fread(c, 1, 2, fin) < 2) return fail(MSG_FORMAT, filename);
+ if (key) {
+ if (*(++enc) == '\0') enc = key;
+ c[0] ^= *enc;
+ if (*(++enc) == '\0') enc = key;
+ c[1] ^= *enc;
+ }
+ if (fread(&l, 1, 1, fin) < 1) return fail(MSG_FORMAT, filename);
+ if (key) {
+ if (*(++enc) == '\0') enc = key;
+ l ^= *enc;
+ }
+ if (fread(in, 1, l/8+1, fin) < (size_t) l/8+1) return fail(MSG_FORMAT, filename);
+ if (key) for (j = 0; j <= l/8; j++) {
+ if (*(++enc) == '\0') enc = key;
+ in[j] ^= *enc;
+ }
+ p = 0;
+ for (j = 0; j < l; j++) {
+ int b = (in[j/8] & (1 << (7 - (j % 8)))) ? 1 : 0;
+ int oldp = p;
+ p = dec[p].v[b];
+ if (p == 0) {
+ lastbit++;
+ if (lastbit == allocatedbit) {
+ allocatedbit += BASEBITREC;
+ dec = (struct bit *) realloc(dec, allocatedbit * sizeof(struct bit));
+ }
+ dec[lastbit].v[0] = 0;
+ dec[lastbit].v[1] = 0;
+ dec[oldp].v[b] = lastbit;
+ p = lastbit;
+ }
+ }
+ dec[p].c[0] = c[0];
+ dec[p].c[1] = c[1];
+ }
+ return 0;
+}
+
+Hunzip::~Hunzip()
+{
+ if (dec) free(dec);
+ if (fin) fclose(fin);
+ if (filename) free(filename);
+}
+
+int Hunzip::getbuf() {
+ int p = 0;
+ int o = 0;
+ do {
+ if (inc == 0) inbits = fread(in, 1, BUFSIZE, fin) * 8;
+ for (; inc < inbits; inc++) {
+ int b = (in[inc / 8] & (1 << (7 - (inc % 8)))) ? 1 : 0;
+ int oldp = p;
+ p = dec[p].v[b];
+ if (p == 0) {
+ if (oldp == lastbit) {
+ fclose(fin);
+ fin = NULL;
+ // add last odd byte
+ if (dec[lastbit].c[0]) out[o++] = dec[lastbit].c[1];
+ return o;
+ }
+ out[o++] = dec[oldp].c[0];
+ out[o++] = dec[oldp].c[1];
+ if (o == BUFSIZE) return o;
+ p = dec[p].v[b];
+ }
+ }
+ inc = 0;
+ } while (inbits == BUFSIZE * 8);
+ return fail(MSG_FORMAT, filename);
+}
+
+const char * Hunzip::getline() {
+ char linebuf[BUFSIZE];
+ int l = 0, eol = 0, left = 0, right = 0;
+ if (bufsiz == -1) return NULL;
+ while (l < bufsiz && !eol) {
+ linebuf[l++] = out[outc];
+ switch (out[outc]) {
+ case '\t': break;
+ case 31: { // escape
+ if (++outc == bufsiz) {
+ bufsiz = getbuf();
+ outc = 0;
+ }
+ linebuf[l - 1] = out[outc];
+ break;
+ }
+ case ' ': break;
+ default: if (((unsigned char) out[outc]) < 47) {
+ if (out[outc] > 32) {
+ right = out[outc] - 31;
+ if (++outc == bufsiz) {
+ bufsiz = getbuf();
+ outc = 0;
+ }
+ }
+ if (out[outc] == 30) left = 9; else left = out[outc];
+ linebuf[l-1] = '\n';
+ eol = 1;
+ }
+ }
+ if (++outc == bufsiz) {
+ outc = 0;
+ bufsiz = fin ? getbuf(): -1;
+ }
+ }
+ if (right) strcpy(linebuf + l - 1, line + strlen(line) - right - 1);
+ else linebuf[l] = '\0';
+ strcpy(line + left, linebuf);
+ return line;
+}
diff --git a/Plugins/spellchecker/hunspell/hunzip.hxx b/Plugins/spellchecker/hunspell/hunzip.hxx
new file mode 100644
index 0000000..b58e3ab
--- /dev/null
+++ b/Plugins/spellchecker/hunspell/hunzip.hxx
@@ -0,0 +1,45 @@
+/* hunzip: file decompression for sorted dictionaries with optional encryption,
+ * algorithm: prefix-suffix encoding and 16-bit Huffman encoding */
+
+#ifndef _HUNZIP_HXX_
+#define _HUNZIP_HXX_
+
+#include "hunvisapi.h"
+
+#include <stdio.h>
+
+#define BUFSIZE 65536
+#define HZIP_EXTENSION ".hz"
+
+#define MSG_OPEN "error: %s: cannot open\n"
+#define MSG_FORMAT "error: %s: not in hzip format\n"
+#define MSG_MEMORY "error: %s: missing memory\n"
+#define MSG_KEY "error: %s: missing or bad password\n"
+
+struct bit {
+ unsigned char c[2];
+ int v[2];
+};
+
+class LIBHUNSPELL_DLL_EXPORTED Hunzip
+{
+
+protected:
+ char * filename;
+ FILE * fin;
+ int bufsiz, lastbit, inc, inbits, outc;
+ struct bit * dec; // code table
+ char in[BUFSIZE]; // input buffer
+ char out[BUFSIZE + 1]; // Huffman-decoded buffer
+ char line[BUFSIZE + 50]; // decoded line
+ int getcode(const char * key);
+ int getbuf();
+ int fail(const char * err, const char * par);
+
+public:
+ Hunzip(const char * filename, const char * key = NULL);
+ ~Hunzip();
+ const char * getline();
+};
+
+#endif
diff --git a/Plugins/spellchecker/hunspell/langnum.hxx b/Plugins/spellchecker/hunspell/langnum.hxx
new file mode 100644
index 0000000..1d140a7
--- /dev/null
+++ b/Plugins/spellchecker/hunspell/langnum.hxx
@@ -0,0 +1,38 @@
+#ifndef _LANGNUM_HXX_
+#define _LANGNUM_HXX_
+
+/*
+ language numbers for language specific codes
+ see http://l10n.openoffice.org/languages.html
+*/
+
+enum {
+LANG_ar=96,
+LANG_az=100, // custom number
+LANG_bg=41,
+LANG_ca=37,
+LANG_cs=42,
+LANG_da=45,
+LANG_de=49,
+LANG_el=30,
+LANG_en=01,
+LANG_es=34,
+LANG_eu=10,
+LANG_fr=02,
+LANG_gl=38,
+LANG_hr=78,
+LANG_hu=36,
+LANG_it=39,
+LANG_la=99, // custom number
+LANG_lv=101, // custom number
+LANG_nl=31,
+LANG_pl=48,
+LANG_pt=03,
+LANG_ru=07,
+LANG_sv=50,
+LANG_tr=90,
+LANG_uk=80,
+LANG_xx=999
+};
+
+#endif
diff --git a/Plugins/spellchecker/hunspell/license.hunspell b/Plugins/spellchecker/hunspell/license.hunspell
new file mode 100644
index 0000000..490e440
--- /dev/null
+++ b/Plugins/spellchecker/hunspell/license.hunspell
@@ -0,0 +1,59 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Hunspell, based on MySpell.
+ *
+ * The Initial Developers of the Original Code are
+ * Kevin Hendricks (MySpell) and Laszlo Nemeth (Hunspell).
+ * Portions created by the Initial Developers are Copyright (C) 2002-2005
+ * the Initial Developers. All Rights Reserved.
+ *
+ * Contributor(s):
+ * David Einstein
+ * Davide Prina
+ * Giuseppe Modugno
+ * Gianluca Turconi
+ * Simon Brouwer
+ * Noll Janos
+ * Biro Arpad
+ * Goldman Eleonora
+ * Sarlos Tamas
+ * Bencsath Boldizsar
+ * Halacsy Peter
+ * Dvornik Laszlo
+ * Gefferth Andras
+ * Nagy Viktor
+ * Varga Daniel
+ * Chris Halls
+ * Rene Engelhard
+ * Bram Moolenaar
+ * Dafydd Jones
+ * Harri Pitkanen
+ * Andras Timar
+ * Tor Lillqvist
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "config.h"
diff --git a/Plugins/spellchecker/hunspell/license.myspell b/Plugins/spellchecker/hunspell/license.myspell
new file mode 100644
index 0000000..2da5330
--- /dev/null
+++ b/Plugins/spellchecker/hunspell/license.myspell
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2002 Kevin B. Hendricks, Stratford, Ontario, Canada
+ * And Contributors. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. All modifications to the source code must be clearly marked as
+ * such. Binary redistributions based on modified source code
+ * must be clearly marked as modified versions in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY KEVIN B. HENDRICKS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * KEVIN B. HENDRICKS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *
+ * NOTE: A special thanks and credit goes to Geoff Kuenning
+ * the creator of ispell. MySpell's affix algorithms were
+ * based on those of ispell which should be noted is
+ * copyright Geoff Kuenning et.al. and now available
+ * under a BSD style license. For more information on ispell
+ * and affix compression in general, please see:
+ * http://www.cs.ucla.edu/ficus-members/geoff/ispell.html
+ * (the home page for ispell)
+ *
+ * An almost complete rewrite of MySpell for use by
+ * the Mozilla project has been developed by David Einstein
+ * (Deinst@world.std.com). David and I are now
+ * working on parallel development tracks to help
+ * our respective projects (Mozilla and OpenOffice.org
+ * and we will maintain full affix file and dictionary
+ * file compatibility and work on merging our versions
+ * of MySpell back into a single tree. David has been
+ * a significant help in improving MySpell.
+ *
+ * Special thanks also go to La'szlo' Ne'meth
+ * <nemethl@gyorsposta.hu> who is the author of the
+ * Hungarian dictionary and who developed and contributed
+ * the code to support compound words in MySpell
+ * and fixed numerous problems with the encoding
+ * case conversion tables.
+ *
+ */
diff --git a/Plugins/spellchecker/hunspell/phonet.cxx b/Plugins/spellchecker/hunspell/phonet.cxx
new file mode 100644
index 0000000..144bd40
--- /dev/null
+++ b/Plugins/spellchecker/hunspell/phonet.cxx
@@ -0,0 +1,292 @@
+/* phonetic.c - generic replacement aglogithms for phonetic transformation
+ Copyright (C) 2000 Bjoern Jacke
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation;
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; If not, see
+ <http://www.gnu.org/licenses/>.
+
+ Changelog:
+
+ 2000-01-05 Bjoern Jacke <bjoern at j3e.de>
+ Initial Release insprired by the article about phonetic
+ transformations out of c't 25/1999
+
+ 2007-07-26 Bjoern Jacke <bjoern at j3e.de>
+ Released under MPL/GPL/LGPL tri-license for Hunspell
+
+ 2007-08-23 Laszlo Nemeth <nemeth at OOo>
+ Porting from Aspell to Hunspell using C-like structs
+*/
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <ctype.h>
+
+#include "csutil.hxx"
+#include "phonet.hxx"
+
+void init_phonet_hash(phonetable & parms)
+ {
+ int i, k;
+
+ for (i = 0; i < HASHSIZE; i++) {
+ parms.hash[i] = -1;
+ }
+
+ for (i = 0; parms.rules[i][0] != '\0'; i += 2) {
+ /** set hash value **/
+ k = (unsigned char) parms.rules[i][0];
+
+ if (parms.hash[k] < 0) {
+ parms.hash[k] = i;
+ }
+ }
+ }
+
+// like strcpy but safe if the strings overlap
+// but only if dest < src
+static inline void strmove(char * dest, char * src) {
+ while (*src)
+ *dest++ = *src++;
+ *dest = '\0';
+}
+
+static int myisalpha(char ch) {
+ if ((unsigned char) ch < 128) return isalpha(ch);
+ return 1;
+}
+
+/* phonetic transcription algorithm */
+/* see: http://aspell.net/man-html/Phonetic-Code.html */
+/* convert string to uppercase before this call */
+int phonet (const char * inword, char * target,
+ int len,
+ phonetable & parms)
+ {
+ /** Do phonetic transformation. **/
+ /** "len" = length of "inword" incl. '\0'. **/
+
+ /** result: >= 0: length of "target" **/
+ /** otherwise: error **/
+
+ int i,j,k=0,n,p,z;
+ int k0,n0,p0=-333,z0;
+ char c, c0;
+ const char * s;
+ typedef unsigned char uchar;
+ char word[MAXPHONETUTF8LEN + 1];
+ if (len == -1) len = strlen(inword);
+ if (len > MAXPHONETUTF8LEN) return 0;
+ strcpy(word, inword);
+
+ /** check word **/
+ i = j = z = 0;
+ while ((c = word[i]) != '\0') {
+ n = parms.hash[(uchar) c];
+ z0 = 0;
+
+ if (n >= 0) {
+ /** check all rules for the same letter **/
+ while (parms.rules[n][0] == c) {
+
+ /** check whole string **/
+ k = 1; /** number of found letters **/
+ p = 5; /** default priority **/
+ s = parms.rules[n];
+ s++; /** important for (see below) "*(s-1)" **/
+
+ while (*s != '\0' && word[i+k] == *s
+ && !isdigit ((unsigned char) *s) && strchr ("(-<^$", *s) == NULL) {
+ k++;
+ s++;
+ }
+ if (*s == '(') {
+ /** check letters in "(..)" **/
+ if (myisalpha(word[i+k]) // ...could be implied?
+ && strchr(s+1, word[i+k]) != NULL) {
+ k++;
+ while (*s != ')')
+ s++;
+ s++;
+ }
+ }
+ p0 = (int) *s;
+ k0 = k;
+ while (*s == '-' && k > 1) {
+ k--;
+ s++;
+ }
+ if (*s == '<')
+ s++;
+ if (isdigit ((unsigned char) *s)) {
+ /** determine priority **/
+ p = *s - '0';
+ s++;
+ }
+ if (*s == '^' && *(s+1) == '^')
+ s++;
+
+ if (*s == '\0'
+ || (*s == '^'
+ && (i == 0 || ! myisalpha(word[i-1]))
+ && (*(s+1) != '$'
+ || (! myisalpha(word[i+k0]) )))
+ || (*s == '$' && i > 0
+ && myisalpha(word[i-1])
+ && (! myisalpha(word[i+k0]) )))
+ {
+ /** search for followup rules, if: **/
+ /** parms.followup and k > 1 and NO '-' in searchstring **/
+ c0 = word[i+k-1];
+ n0 = parms.hash[(uchar) c0];
+
+// if (parms.followup && k > 1 && n0 >= 0
+ if (k > 1 && n0 >= 0
+ && p0 != (int) '-' && word[i+k] != '\0') {
+ /** test follow-up rule for "word[i+k]" **/
+ while (parms.rules[n0][0] == c0) {
+
+ /** check whole string **/
+ k0 = k;
+ p0 = 5;
+ s = parms.rules[n0];
+ s++;
+ while (*s != '\0' && word[i+k0] == *s
+ && ! isdigit((unsigned char) *s) && strchr("(-<^$",*s) == NULL) {
+ k0++;
+ s++;
+ }
+ if (*s == '(') {
+ /** check letters **/
+ if (myisalpha(word[i+k0])
+ && strchr (s+1, word[i+k0]) != NULL) {
+ k0++;
+ while (*s != ')' && *s != '\0')
+ s++;
+ if (*s == ')')
+ s++;
+ }
+ }
+ while (*s == '-') {
+ /** "k0" gets NOT reduced **/
+ /** because "if (k0 == k)" **/
+ s++;
+ }
+ if (*s == '<')
+ s++;
+ if (isdigit ((unsigned char) *s)) {
+ p0 = *s - '0';
+ s++;
+ }
+
+ if (*s == '\0'
+ /** *s == '^' cuts **/
+ || (*s == '$' && ! myisalpha(word[i+k0])))
+ {
+ if (k0 == k) {
+ /** this is just a piece of the string **/
+ n0 += 2;
+ continue;
+ }
+
+ if (p0 < p) {
+ /** priority too low **/
+ n0 += 2;
+ continue;
+ }
+ /** rule fits; stop search **/
+ break;
+ }
+ n0 += 2;
+ } /** End of "while (parms.rules[n0][0] == c0)" **/
+
+ if (p0 >= p && parms.rules[n0][0] == c0) {
+ n += 2;
+ continue;
+ }
+ } /** end of follow-up stuff **/
+
+ /** replace string **/
+ s = parms.rules[n+1];
+ p0 = (parms.rules[n][0] != '\0'
+ && strchr (parms.rules[n]+1,'<') != NULL) ? 1:0;
+ if (p0 == 1 && z == 0) {
+ /** rule with '<' is used **/
+ if (j > 0 && *s != '\0'
+ && (target[j-1] == c || target[j-1] == *s)) {
+ j--;
+ }
+ z0 = 1;
+ z = 1;
+ k0 = 0;
+ while (*s != '\0' && word[i+k0] != '\0') {
+ word[i+k0] = *s;
+ k0++;
+ s++;
+ }
+ if (k > k0)
+ strmove (&word[0]+i+k0, &word[0]+i+k);
+
+ /** new "actual letter" **/
+ c = word[i];
+ }
+ else { /** no '<' rule used **/
+ i += k - 1;
+ z = 0;
+ while (*s != '\0'
+ && *(s+1) != '\0' && j < len) {
+ if (j == 0 || target[j-1] != *s) {
+ target[j] = *s;
+ j++;
+ }
+ s++;
+ }
+ /** new "actual letter" **/
+ c = *s;
+ if (parms.rules[n][0] != '\0'
+ && strstr (parms.rules[n]+1, "^^") != NULL) {
+ if (c != '\0') {
+ target[j] = c;
+ j++;
+ }
+ strmove (&word[0], &word[0]+i+1);
+ i = 0;
+ z0 = 1;
+ }
+ }
+ break;
+ } /** end of follow-up stuff **/
+ n += 2;
+ } /** end of while (parms.rules[n][0] == c) **/
+ } /** end of if (n >= 0) **/
+ if (z0 == 0) {
+// if (k && (assert(p0!=-333),!p0) && j < len && c != '\0'
+// && (!parms.collapse_result || j == 0 || target[j-1] != c)){
+ if (k && !p0 && j < len && c != '\0'
+ && (1 || j == 0 || target[j-1] != c)){
+ /** condense only double letters **/
+ target[j] = c;
+ ///printf("\n setting \n");
+ j++;
+ }
+
+ i++;
+ z = 0;
+ k=0;
+ }
+ } /** end of while ((c = word[i]) != '\0') **/
+
+ target[j] = '\0';
+ return (j);
+
+ } /** end of function "phonet" **/
diff --git a/Plugins/spellchecker/hunspell/phonet.hxx b/Plugins/spellchecker/hunspell/phonet.hxx
new file mode 100644
index 0000000..f91d3b0
--- /dev/null
+++ b/Plugins/spellchecker/hunspell/phonet.hxx
@@ -0,0 +1,52 @@
+/* phonetic.c - generic replacement aglogithms for phonetic transformation
+ Copyright (C) 2000 Bjoern Jacke
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation;
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; If not, see
+ <http://www.gnu.org/licenses/>.
+
+ Changelog:
+
+ 2000-01-05 Bjoern Jacke <bjoern at j3e.de>
+ Initial Release insprired by the article about phonetic
+ transformations out of c't 25/1999
+
+ 2007-07-26 Bjoern Jacke <bjoern at j3e.de>
+ Released under MPL/GPL/LGPL tri-license for Hunspell
+
+ 2007-08-23 Laszlo Nemeth <nemeth at OOo>
+ Porting from Aspell to Hunspell using C-like structs
+*/
+
+#ifndef __PHONETHXX__
+#define __PHONETHXX__
+
+#define HASHSIZE 256
+#define MAXPHONETLEN 256
+#define MAXPHONETUTF8LEN (MAXPHONETLEN * 4)
+
+#include "hunvisapi.h"
+
+struct phonetable {
+ char utf8;
+ cs_info * lang;
+ int num;
+ char * * rules;
+ int hash[HASHSIZE];
+};
+
+LIBHUNSPELL_DLL_EXPORTED void init_phonet_hash(phonetable & parms);
+
+LIBHUNSPELL_DLL_EXPORTED int phonet (const char * inword, char * target,
+ int len, phonetable & phone);
+
+#endif
diff --git a/Plugins/spellchecker/hunspell/replist.cxx b/Plugins/spellchecker/hunspell/replist.cxx
new file mode 100644
index 0000000..bc15373
--- /dev/null
+++ b/Plugins/spellchecker/hunspell/replist.cxx
@@ -0,0 +1,87 @@
+#include "license.hunspell"
+#include "license.myspell"
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "replist.hxx"
+#include "csutil.hxx"
+
+RepList::RepList(int n) {
+ dat = (replentry **) malloc(sizeof(replentry *) * n);
+ if (dat == 0) size = 0; else size = n;
+ pos = 0;
+}
+
+RepList::~RepList()
+{
+ for (int i = 0; i < pos; i++) {
+ free(dat[i]->pattern);
+ free(dat[i]->pattern2);
+ free(dat[i]);
+ }
+ free(dat);
+}
+
+int RepList::get_pos() {
+ return pos;
+}
+
+replentry * RepList::item(int n) {
+ return dat[n];
+}
+
+int RepList::findnear(const char * word) {
+ int p1 = 0;
+ int p2 = pos;
+ while ((p2 - p1) > 1) {
+ int m = (p1 + p2) / 2;
+ int c = strcmp(word, dat[m]->pattern);
+ if (c <= 0) {
+ if (c < 0) p2 = m; else p1 = p2 = m;
+ } else p1 = m;
+ }
+ return p1;
+}
+
+int RepList::match(const char * word, int n) {
+ if (strncmp(word, dat[n]->pattern, strlen(dat[n]->pattern)) == 0) return strlen(dat[n]->pattern);
+ return 0;
+}
+
+int RepList::add(char * pat1, char * pat2) {
+ if (pos >= size || pat1 == NULL || pat2 == NULL) return 1;
+ replentry * r = (replentry *) malloc(sizeof(replentry));
+ if (r == NULL) return 1;
+ r->pattern = mystrrep(pat1, "_", " ");
+ r->pattern2 = mystrrep(pat2, "_", " ");
+ r->start = false;
+ r->end = false;
+ dat[pos++] = r;
+ for (int i = pos - 1; i > 0; i--) {
+ r = dat[i];
+ if (strcmp(r->pattern, dat[i - 1]->pattern) < 0) {
+ dat[i] = dat[i - 1];
+ dat[i - 1] = r;
+ } else break;
+ }
+ return 0;
+}
+
+int RepList::conv(const char * word, char * dest) {
+ int stl = 0;
+ int change = 0;
+ for (size_t i = 0; i < strlen(word); i++) {
+ int n = findnear(word + i);
+ int l = match(word + i, n);
+ if (l) {
+ strcpy(dest + stl, dat[n]->pattern2);
+ stl += strlen(dat[n]->pattern2);
+ i += l - 1;
+ change = 1;
+ } else dest[stl++] = word[i];
+ }
+ dest[stl] = '\0';
+ return change;
+}
diff --git a/Plugins/spellchecker/hunspell/replist.hxx b/Plugins/spellchecker/hunspell/replist.hxx
new file mode 100644
index 0000000..1cc039e
--- /dev/null
+++ b/Plugins/spellchecker/hunspell/replist.hxx
@@ -0,0 +1,27 @@
+/* string replacement list class */
+#ifndef _REPLIST_HXX_
+#define _REPLIST_HXX_
+
+#include "hunvisapi.h"
+
+#include "w_char.hxx"
+
+class LIBHUNSPELL_DLL_EXPORTED RepList
+{
+protected:
+ replentry ** dat;
+ int size;
+ int pos;
+
+public:
+ RepList(int n);
+ ~RepList();
+
+ int get_pos();
+ int add(char * pat1, char * pat2);
+ replentry * item(int n);
+ int findnear(const char * word);
+ int match(const char * word, int n);
+ int conv(const char * word, char * dest);
+};
+#endif
diff --git a/Plugins/spellchecker/hunspell/suggestmgr.cxx b/Plugins/spellchecker/hunspell/suggestmgr.cxx
new file mode 100644
index 0000000..d08b506
--- /dev/null
+++ b/Plugins/spellchecker/hunspell/suggestmgr.cxx
@@ -0,0 +1,2001 @@
+#include "license.hunspell"
+#include "license.myspell"
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <ctype.h>
+
+#include "suggestmgr.hxx"
+#include "htypes.hxx"
+#include "csutil.hxx"
+
+const w_char W_VLINE = { '\0', '|' };
+
+SuggestMgr::SuggestMgr(const char * tryme, int maxn,
+ AffixMgr * aptr)
+{
+
+ // register affix manager and check in string of chars to
+ // try when building candidate suggestions
+ pAMgr = aptr;
+
+ csconv = NULL;
+
+ ckeyl = 0;
+ ckey = NULL;
+ ckey_utf = NULL;
+
+ ctryl = 0;
+ ctry = NULL;
+ ctry_utf = NULL;
+
+ utf8 = 0;
+ langnum = 0;
+ complexprefixes = 0;
+
+ maxSug = maxn;
+ nosplitsugs = 0;
+ maxngramsugs = MAXNGRAMSUGS;
+ maxcpdsugs = MAXCOMPOUNDSUGS;
+
+ if (pAMgr) {
+ langnum = pAMgr->get_langnum();
+ ckey = pAMgr->get_key_string();
+ nosplitsugs = pAMgr->get_nosplitsugs();
+ if (pAMgr->get_maxngramsugs() >= 0)
+ maxngramsugs = pAMgr->get_maxngramsugs();
+ utf8 = pAMgr->get_utf8();
+ if (pAMgr->get_maxcpdsugs() >= 0)
+ maxcpdsugs = pAMgr->get_maxcpdsugs();
+ if (!utf8)
+ {
+ char * enc = pAMgr->get_encoding();
+ csconv = get_current_cs(enc);
+ free(enc);
+ }
+ complexprefixes = pAMgr->get_complexprefixes();
+ }
+
+ if (ckey) {
+ if (utf8) {
+ w_char t[MAXSWL];
+ ckeyl = u8_u16(t, MAXSWL, ckey);
+ ckey_utf = (w_char *) malloc(ckeyl * sizeof(w_char));
+ if (ckey_utf) memcpy(ckey_utf, t, ckeyl * sizeof(w_char));
+ else ckeyl = 0;
+ } else {
+ ckeyl = strlen(ckey);
+ }
+ }
+
+ if (tryme) {
+ ctry = mystrdup(tryme);
+ if (ctry) ctryl = strlen(ctry);
+ if (ctry && utf8) {
+ w_char t[MAXSWL];
+ ctryl = u8_u16(t, MAXSWL, tryme);
+ ctry_utf = (w_char *) malloc(ctryl * sizeof(w_char));
+ if (ctry_utf) memcpy(ctry_utf, t, ctryl * sizeof(w_char));
+ else ctryl = 0;
+ }
+ }
+}
+
+
+SuggestMgr::~SuggestMgr()
+{
+ pAMgr = NULL;
+ if (ckey) free(ckey);
+ ckey = NULL;
+ if (ckey_utf) free(ckey_utf);
+ ckey_utf = NULL;
+ ckeyl = 0;
+ if (ctry) free(ctry);
+ ctry = NULL;
+ if (ctry_utf) free(ctry_utf);
+ ctry_utf = NULL;
+ ctryl = 0;
+ maxSug = 0;
+#ifdef MOZILLA_CLIENT
+ delete [] csconv;
+#endif
+}
+
+int SuggestMgr::testsug(char** wlst, const char * candidate, int wl, int ns, int cpdsuggest,
+ int * timer, clock_t * timelimit) {
+ int cwrd = 1;
+ if (ns == maxSug) return maxSug;
+ for (int k=0; k < ns; k++) {
+ if (strcmp(candidate,wlst[k]) == 0) cwrd = 0;
+ }
+ if ((cwrd) && checkword(candidate, wl, cpdsuggest, timer, timelimit)) {
+ wlst[ns] = mystrdup(candidate);
+ if (wlst[ns] == NULL) {
+ for (int j=0; j<ns; j++) free(wlst[j]);
+ return -1;
+ }
+ ns++;
+ }
+ return ns;
+}
+
+// generate suggestions for a misspelled word
+// pass in address of array of char * pointers
+// onlycompoundsug: probably bad suggestions (need for ngram sugs, too)
+
+int SuggestMgr::suggest(char*** slst, const char * w, int nsug,
+ int * onlycompoundsug)
+{
+ int nocompoundtwowords = 0;
+ char ** wlst;
+ w_char word_utf[MAXSWL];
+ int wl = 0;
+ int nsugorig = nsug;
+ char w2[MAXWORDUTF8LEN];
+ const char * word = w;
+ int oldSug = 0;
+
+ // word reversing wrapper for complex prefixes
+ if (complexprefixes) {
+ strcpy(w2, w);
+ if (utf8) reverseword_utf(w2); else reverseword(w2);
+ word = w2;
+ }
+
+ if (*slst) {
+ wlst = *slst;
+ } else {
+ wlst = (char **) malloc(maxSug * sizeof(char *));
+ if (wlst == NULL) return -1;
+ for (int i = 0; i < maxSug; i++) {
+ wlst[i] = NULL;
+ }
+ }
+
+ if (utf8) {
+ wl = u8_u16(word_utf, MAXSWL, word);
+ if (wl == -1) {
+ *slst = wlst;
+ return nsug;
+ }
+ }
+
+ for (int cpdsuggest=0; (cpdsuggest<2) && (nocompoundtwowords==0); cpdsuggest++) {
+
+ // limit compound suggestion
+ if (cpdsuggest > 0) oldSug = nsug;
+
+ // suggestions for an uppercase word (html -> HTML)
+ if ((nsug < maxSug) && (nsug > -1)) {
+ nsug = (utf8) ? capchars_utf(wlst, word_utf, wl, nsug, cpdsuggest) :
+ capchars(wlst, word, nsug, cpdsuggest);
+ }
+
+ // perhaps we made a typical fault of spelling
+ if ((nsug < maxSug) && (nsug > -1) && (!cpdsuggest || (nsug < oldSug + maxcpdsugs))) {
+ nsug = replchars(wlst, word, nsug, cpdsuggest);
+ }
+
+ // perhaps we made chose the wrong char from a related set
+ if ((nsug < maxSug) && (nsug > -1) && (!cpdsuggest || (nsug < oldSug + maxcpdsugs))) {
+ nsug = mapchars(wlst, word, nsug, cpdsuggest);
+ }
+
+ // only suggest compound words when no other suggestion
+ if ((cpdsuggest == 0) && (nsug > nsugorig)) nocompoundtwowords=1;
+
+ // did we swap the order of chars by mistake
+ if ((nsug < maxSug) && (nsug > -1) && (!cpdsuggest || (nsug < oldSug + maxcpdsugs))) {
+ nsug = (utf8) ? swapchar_utf(wlst, word_utf, wl, nsug, cpdsuggest) :
+ swapchar(wlst, word, nsug, cpdsuggest);
+ }
+
+ // did we swap the order of non adjacent chars by mistake
+ if ((nsug < maxSug) && (nsug > -1) && (!cpdsuggest || (nsug < oldSug + maxcpdsugs))) {
+ nsug = (utf8) ? longswapchar_utf(wlst, word_utf, wl, nsug, cpdsuggest) :
+ longswapchar(wlst, word, nsug, cpdsuggest);
+ }
+
+ // did we just hit the wrong key in place of a good char (case and keyboard)
+ if ((nsug < maxSug) && (nsug > -1) && (!cpdsuggest || (nsug < oldSug + maxcpdsugs))) {
+ nsug = (utf8) ? badcharkey_utf(wlst, word_utf, wl, nsug, cpdsuggest) :
+ badcharkey(wlst, word, nsug, cpdsuggest);
+ }
+
+ // did we add a char that should not be there
+ if ((nsug < maxSug) && (nsug > -1) && (!cpdsuggest || (nsug < oldSug + maxcpdsugs))) {
+ nsug = (utf8) ? extrachar_utf(wlst, word_utf, wl, nsug, cpdsuggest) :
+ extrachar(wlst, word, nsug, cpdsuggest);
+ }
+
+
+ // did we forgot a char
+ if ((nsug < maxSug) && (nsug > -1) && (!cpdsuggest || (nsug < oldSug + maxcpdsugs))) {
+ nsug = (utf8) ? forgotchar_utf(wlst, word_utf, wl, nsug, cpdsuggest) :
+ forgotchar(wlst, word, nsug, cpdsuggest);
+ }
+
+ // did we move a char
+ if ((nsug < maxSug) && (nsug > -1) && (!cpdsuggest || (nsug < oldSug + maxcpdsugs))) {
+ nsug = (utf8) ? movechar_utf(wlst, word_utf, wl, nsug, cpdsuggest) :
+ movechar(wlst, word, nsug, cpdsuggest);
+ }
+
+ // did we just hit the wrong key in place of a good char
+ if ((nsug < maxSug) && (nsug > -1) && (!cpdsuggest || (nsug < oldSug + maxcpdsugs))) {
+ nsug = (utf8) ? badchar_utf(wlst, word_utf, wl, nsug, cpdsuggest) :
+ badchar(wlst, word, nsug, cpdsuggest);
+ }
+
+ // did we double two characters
+ if ((nsug < maxSug) && (nsug > -1) && (!cpdsuggest || (nsug < oldSug + maxcpdsugs))) {
+ nsug = (utf8) ? doubletwochars_utf(wlst, word_utf, wl, nsug, cpdsuggest) :
+ doubletwochars(wlst, word, nsug, cpdsuggest);
+ }
+
+ // perhaps we forgot to hit space and two words ran together
+ if (!nosplitsugs && (nsug < maxSug) && (nsug > -1) && (!cpdsuggest || (nsug < oldSug + maxcpdsugs))) {
+ nsug = twowords(wlst, word, nsug, cpdsuggest);
+ }
+
+ } // repeating ``for'' statement compounding support
+
+ if (nsug < 0) {
+ // we ran out of memory - we should free up as much as possible
+ for (int i = 0; i < maxSug; i++)
+ if (wlst[i] != NULL) free(wlst[i]);
+ free(wlst);
+ wlst = NULL;
+ }
+
+ if (!nocompoundtwowords && (nsug > 0) && onlycompoundsug) *onlycompoundsug = 1;
+
+ *slst = wlst;
+ return nsug;
+}
+
+// generate suggestions for a word with typical mistake
+// pass in address of array of char * pointers
+#ifdef HUNSPELL_EXPERIMENTAL
+int SuggestMgr::suggest_auto(char*** slst, const char * w, int nsug)
+{
+ int nocompoundtwowords = 0;
+ char ** wlst;
+ int oldSug;
+
+ char w2[MAXWORDUTF8LEN];
+ const char * word = w;
+
+ // word reversing wrapper for complex prefixes
+ if (complexprefixes) {
+ strcpy(w2, w);
+ if (utf8) reverseword_utf(w2); else reverseword(w2);
+ word = w2;
+ }
+
+ if (*slst) {
+ wlst = *slst;
+ } else {
+ wlst = (char **) malloc(maxSug * sizeof(char *));
+ if (wlst == NULL) return -1;
+ }
+
+ for (int cpdsuggest=0; (cpdsuggest<2) && (nocompoundtwowords==0); cpdsuggest++) {
+
+ // limit compound suggestion
+ if (cpdsuggest > 0) oldSug = nsug;
+
+ // perhaps we made a typical fault of spelling
+ if ((nsug < maxSug) && (nsug > -1))
+ nsug = replchars(wlst, word, nsug, cpdsuggest);
+
+ // perhaps we made chose the wrong char from a related set
+ if ((nsug < maxSug) && (nsug > -1) && (!cpdsuggest || (nsug < oldSug + maxcpdsugs)))
+ nsug = mapchars(wlst, word, nsug, cpdsuggest);
+
+ if ((cpdsuggest==0) && (nsug>0)) nocompoundtwowords=1;
+
+ // perhaps we forgot to hit space and two words ran together
+
+ if ((nsug < maxSug) && (nsug > -1) && (!cpdsuggest || (nsug < oldSug + maxcpdsugs)) && check_forbidden(word, strlen(word))) {
+ nsug = twowords(wlst, word, nsug, cpdsuggest);
+ }
+
+ } // repeating ``for'' statement compounding support
+
+ if (nsug < 0) {
+ for (int i=0;i<maxSug; i++)
+ if (wlst[i] != NULL) free(wlst[i]);
+ free(wlst);
+ return -1;
+ }
+
+ *slst = wlst;
+ return nsug;
+}
+#endif // END OF HUNSPELL_EXPERIMENTAL CODE
+
+// suggestions for an uppercase word (html -> HTML)
+int SuggestMgr::capchars_utf(char ** wlst, const w_char * word, int wl, int ns, int cpdsuggest)
+{
+ char candidate[MAXSWUTF8L];
+ w_char candidate_utf[MAXSWL];
+ memcpy(candidate_utf, word, wl * sizeof(w_char));
+ mkallcap_utf(candidate_utf, wl, langnum);
+ u16_u8(candidate, MAXSWUTF8L, candidate_utf, wl);
+ return testsug(wlst, candidate, strlen(candidate), ns, cpdsuggest, NULL, NULL);
+}
+
+// suggestions for an uppercase word (html -> HTML)
+int SuggestMgr::capchars(char** wlst, const char * word, int ns, int cpdsuggest)
+{
+ char candidate[MAXSWUTF8L];
+ strcpy(candidate, word);
+ mkallcap(candidate, csconv);
+ return testsug(wlst, candidate, strlen(candidate), ns, cpdsuggest, NULL, NULL);
+}
+
+// suggestions for when chose the wrong char out of a related set
+int SuggestMgr::mapchars(char** wlst, const char * word, int ns, int cpdsuggest)
+{
+ char candidate[MAXSWUTF8L];
+ clock_t timelimit;
+ int timer;
+ candidate[0] = '\0';
+
+ int wl = strlen(word);
+ if (wl < 2 || ! pAMgr) return ns;
+
+ int nummap = pAMgr->get_nummap();
+ struct mapentry* maptable = pAMgr->get_maptable();
+ if (maptable==NULL) return ns;
+
+ timelimit = clock();
+ timer = MINTIMER;
+ return map_related(word, (char *) &candidate, 0, 0, wlst, cpdsuggest, ns, maptable, nummap, &timer, &timelimit);
+}
+
+int SuggestMgr::map_related(const char * word, char * candidate, int wn, int cn,
+ char** wlst, int cpdsuggest, int ns,
+ const mapentry* maptable, int nummap, int * timer, clock_t * timelimit)
+{
+ if (*(word + wn) == '\0') {
+ int cwrd = 1;
+ *(candidate + cn) = '\0';
+ int wl = strlen(candidate);
+ for (int m=0; m < ns; m++)
+ if (strcmp(candidate, wlst[m]) == 0) cwrd = 0;
+ if ((cwrd) && checkword(candidate, wl, cpdsuggest, timer, timelimit)) {
+ if (ns < maxSug) {
+ wlst[ns] = mystrdup(candidate);
+ if (wlst[ns] == NULL) return -1;
+ ns++;
+ }
+ }
+ return ns;
+ }
+ int in_map = 0;
+ for (int j = 0; j < nummap; j++) {
+ for (int k = 0; k < maptable[j].len; k++) {
+ int len = strlen(maptable[j].set[k]);
+ if (strncmp(maptable[j].set[k], word + wn, len) == 0) {
+ in_map = 1;
+ for (int l = 0; l < maptable[j].len; l++) {
+ strcpy(candidate + cn, maptable[j].set[l]);
+ ns = map_related(word, candidate, wn + len, strlen(candidate), wlst,
+ cpdsuggest, ns, maptable, nummap, timer, timelimit);
+ if (!(*timer)) return ns;
+ }
+ }
+ }
+ }
+ if (!in_map) {
+ *(candidate + cn) = *(word + wn);
+ ns = map_related(word, candidate, wn + 1, cn + 1, wlst, cpdsuggest,
+ ns, maptable, nummap, timer, timelimit);
+ }
+ return ns;
+}
+
+// suggestions for a typical fault of spelling, that
+// differs with more, than 1 letter from the right form.
+int SuggestMgr::replchars(char** wlst, const char * word, int ns, int cpdsuggest)
+{
+ char candidate[MAXSWUTF8L];
+ const char * r;
+ int lenr, lenp;
+ int wl = strlen(word);
+ if (wl < 2 || ! pAMgr) return ns;
+ int numrep = pAMgr->get_numrep();
+ struct replentry* reptable = pAMgr->get_reptable();
+ if (reptable==NULL) return ns;
+ for (int i=0; i < numrep; i++ ) {
+ r = word;
+ lenr = strlen(reptable[i].pattern2);
+ lenp = strlen(reptable[i].pattern);
+ // search every occurence of the pattern in the word
+ while ((r=strstr(r, reptable[i].pattern)) != NULL && (!reptable[i].end || strlen(r) == strlen(reptable[i].pattern)) &&
+ (!reptable[i].start || r == word)) {
+ strcpy(candidate, word);
+ if (r-word + lenr + strlen(r+lenp) >= MAXSWUTF8L) break;
+ strcpy(candidate+(r-word),reptable[i].pattern2);
+ strcpy(candidate+(r-word)+lenr, r+lenp);
+ ns = testsug(wlst, candidate, wl-lenp+lenr, ns, cpdsuggest, NULL, NULL);
+ if (ns == -1) return -1;
+ // check REP suggestions with space
+ char * sp = strchr(candidate, ' ');
+ if (sp) {
+ char * prev = candidate;
+ while (sp) {
+ *sp = '\0';
+ if (checkword(prev, strlen(prev), 0, NULL, NULL)) {
+ int oldns = ns;
+ *sp = ' ';
+ ns = testsug(wlst, sp + 1, strlen(sp + 1), ns, cpdsuggest, NULL, NULL);
+ if (ns == -1) return -1;
+ if (oldns < ns) {
+ free(wlst[ns - 1]);
+ wlst[ns - 1] = mystrdup(candidate);
+ if (!wlst[ns - 1]) return -1;
+ }
+ }
+ *sp = ' ';
+ prev = sp + 1;
+ sp = strchr(prev, ' ');
+ }
+ }
+ r++; // search for the next letter
+ }
+ }
+ return ns;
+}
+
+// perhaps we doubled two characters (pattern aba -> ababa, for example vacation -> vacacation)
+int SuggestMgr::doubletwochars(char** wlst, const char * word, int ns, int cpdsuggest)
+{
+ char candidate[MAXSWUTF8L];
+ int state=0;
+ int wl = strlen(word);
+ if (wl < 5 || ! pAMgr) return ns;
+ for (int i=2; i < wl; i++ ) {
+ if (word[i]==word[i-2]) {
+ state++;
+ if (state==3) {
+ strcpy(candidate,word);
+ strcpy(candidate+i-1,word+i+1);
+ ns = testsug(wlst, candidate, wl-2, ns, cpdsuggest, NULL, NULL);
+ if (ns == -1) return -1;
+ state=0;
+ }
+ } else {
+ state=0;
+ }
+ }
+ return ns;
+}
+
+// perhaps we doubled two characters (pattern aba -> ababa, for example vacation -> vacacation)
+int SuggestMgr::doubletwochars_utf(char ** wlst, const w_char * word, int wl, int ns, int cpdsuggest)
+{
+ w_char candidate_utf[MAXSWL];
+ char candidate[MAXSWUTF8L];
+ int state=0;
+ if (wl < 5 || ! pAMgr) return ns;
+ for (int i=2; i < wl; i++) {
+ if (w_char_eq(word[i], word[i-2])) {
+ state++;
+ if (state==3) {
+ memcpy(candidate_utf, word, (i - 1) * sizeof(w_char));
+ memcpy(candidate_utf+i-1, word+i+1, (wl-i-1) * sizeof(w_char));
+ u16_u8(candidate, MAXSWUTF8L, candidate_utf, wl-2);
+ ns = testsug(wlst, candidate, strlen(candidate), ns, cpdsuggest, NULL, NULL);
+ if (ns == -1) return -1;
+ state=0;
+ }
+ } else {
+ state=0;
+ }
+ }
+ return ns;
+}
+
+// error is wrong char in place of correct one (case and keyboard related version)
+int SuggestMgr::badcharkey(char ** wlst, const char * word, int ns, int cpdsuggest)
+{
+ char tmpc;
+ char candidate[MAXSWUTF8L];
+ int wl = strlen(word);
+ strcpy(candidate, word);
+ // swap out each char one by one and try uppercase and neighbor
+ // keyboard chars in its place to see if that makes a good word
+
+ for (int i=0; i < wl; i++) {
+ tmpc = candidate[i];
+ // check with uppercase letters
+ candidate[i] = csconv[((unsigned char)tmpc)].cupper;
+ if (tmpc != candidate[i]) {
+ ns = testsug(wlst, candidate, wl, ns, cpdsuggest, NULL, NULL);
+ if (ns == -1) return -1;
+ candidate[i] = tmpc;
+ }
+ // check neighbor characters in keyboard string
+ if (!ckey) continue;
+ char * loc = strchr(ckey, tmpc);
+ while (loc) {
+ if ((loc > ckey) && (*(loc - 1) != '|')) {
+ candidate[i] = *(loc - 1);
+ ns = testsug(wlst, candidate, wl, ns, cpdsuggest, NULL, NULL);
+ if (ns == -1) return -1;
+ }
+ if ((*(loc + 1) != '|') && (*(loc + 1) != '\0')) {
+ candidate[i] = *(loc + 1);
+ ns = testsug(wlst, candidate, wl, ns, cpdsuggest, NULL, NULL);
+ if (ns == -1) return -1;
+ }
+ loc = strchr(loc + 1, tmpc);
+ }
+ candidate[i] = tmpc;
+ }
+ return ns;
+}
+
+// error is wrong char in place of correct one (case and keyboard related version)
+int SuggestMgr::badcharkey_utf(char ** wlst, const w_char * word, int wl, int ns, int cpdsuggest)
+{
+ w_char tmpc;
+ w_char candidate_utf[MAXSWL];
+ char candidate[MAXSWUTF8L];
+ memcpy(candidate_utf, word, wl * sizeof(w_char));
+ // swap out each char one by one and try all the tryme
+ // chars in its place to see if that makes a good word
+ for (int i=0; i < wl; i++) {
+ tmpc = candidate_utf[i];
+ // check with uppercase letters
+ mkallcap_utf(candidate_utf + i, 1, langnum);
+ if (!w_char_eq(tmpc, candidate_utf[i])) {
+ u16_u8(candidate, MAXSWUTF8L, candidate_utf, wl);
+ ns = testsug(wlst, candidate, strlen(candidate), ns, cpdsuggest, NULL, NULL);
+ if (ns == -1) return -1;
+ candidate_utf[i] = tmpc;
+ }
+ // check neighbor characters in keyboard string
+ if (!ckey) continue;
+ w_char * loc = ckey_utf;
+ while ((loc < (ckey_utf + ckeyl)) && !w_char_eq(*loc, tmpc)) loc++;
+ while (loc < (ckey_utf + ckeyl)) {
+ if ((loc > ckey_utf) && !w_char_eq(*(loc - 1), W_VLINE)) {
+ candidate_utf[i] = *(loc - 1);
+ u16_u8(candidate, MAXSWUTF8L, candidate_utf, wl);
+ ns = testsug(wlst, candidate, strlen(candidate), ns, cpdsuggest, NULL, NULL);
+ if (ns == -1) return -1;
+ }
+ if (((loc + 1) < (ckey_utf + ckeyl)) && !w_char_eq(*(loc + 1), W_VLINE)) {
+ candidate_utf[i] = *(loc + 1);
+ u16_u8(candidate, MAXSWUTF8L, candidate_utf, wl);
+ ns = testsug(wlst, candidate, strlen(candidate), ns, cpdsuggest, NULL, NULL);
+ if (ns == -1) return -1;
+ }
+ do { loc++; } while ((loc < (ckey_utf + ckeyl)) && !w_char_eq(*loc, tmpc));
+ }
+ candidate_utf[i] = tmpc;
+ }
+ return ns;
+}
+
+// error is wrong char in place of correct one
+int SuggestMgr::badchar(char ** wlst, const char * word, int ns, int cpdsuggest)
+{
+ char tmpc;
+ char candidate[MAXSWUTF8L];
+ clock_t timelimit = clock();
+ int timer = MINTIMER;
+ int wl = strlen(word);
+ strcpy(candidate, word);
+ // swap out each char one by one and try all the tryme
+ // chars in its place to see if that makes a good word
+ for (int j=0; j < ctryl; j++) {
+ for (int i=wl-1; i >= 0; i--) {
+ tmpc = candidate[i];
+ if (ctry[j] == tmpc) continue;
+ candidate[i] = ctry[j];
+ ns = testsug(wlst, candidate, wl, ns, cpdsuggest, &timer, &timelimit);
+ if (ns == -1) return -1;
+ if (!timer) return ns;
+ candidate[i] = tmpc;
+ }
+ }
+ return ns;
+}
+
+// error is wrong char in place of correct one
+int SuggestMgr::badchar_utf(char ** wlst, const w_char * word, int wl, int ns, int cpdsuggest)
+{
+ w_char tmpc;
+ w_char candidate_utf[MAXSWL];
+ char candidate[MAXSWUTF8L];
+ clock_t timelimit = clock();
+ int timer = MINTIMER;
+ memcpy(candidate_utf, word, wl * sizeof(w_char));
+ // swap out each char one by one and try all the tryme
+ // chars in its place to see if that makes a good word
+ for (int j=0; j < ctryl; j++) {
+ for (int i=wl-1; i >= 0; i--) {
+ tmpc = candidate_utf[i];
+ if (w_char_eq(tmpc, ctry_utf[j])) continue;
+ candidate_utf[i] = ctry_utf[j];
+ u16_u8(candidate, MAXSWUTF8L, candidate_utf, wl);
+ ns = testsug(wlst, candidate, strlen(candidate), ns, cpdsuggest, &timer, &timelimit);
+ if (ns == -1) return -1;
+ if (!timer) return ns;
+ candidate_utf[i] = tmpc;
+ }
+ }
+ return ns;
+}
+
+// error is word has an extra letter it does not need
+int SuggestMgr::extrachar_utf(char** wlst, const w_char * word, int wl, int ns, int cpdsuggest)
+{
+ char candidate[MAXSWUTF8L];
+ w_char candidate_utf[MAXSWL];
+ w_char * p;
+ w_char tmpc = W_VLINE; // not used value, only for VCC warning message
+ if (wl < 2) return ns;
+ // try omitting one char of word at a time
+ memcpy(candidate_utf, word, wl * sizeof(w_char));
+ for (p = candidate_utf + wl - 1; p >= candidate_utf; p--) {
+ w_char tmpc2 = *p;
+ if (p < candidate_utf + wl - 1) *p = tmpc;
+ u16_u8(candidate, MAXSWUTF8L, candidate_utf, wl - 1);
+ ns = testsug(wlst, candidate, strlen(candidate), ns, cpdsuggest, NULL, NULL);
+ if (ns == -1) return -1;
+ tmpc = tmpc2;
+ }
+ return ns;
+}
+
+// error is word has an extra letter it does not need
+int SuggestMgr::extrachar(char** wlst, const char * word, int ns, int cpdsuggest)
+{
+ char tmpc = '\0';
+ char candidate[MAXSWUTF8L];
+ char * p;
+ int wl = strlen(word);
+ if (wl < 2) return ns;
+ // try omitting one char of word at a time
+ strcpy (candidate, word);
+ for (p = candidate + wl - 1; p >=candidate; p--) {
+ char tmpc2 = *p;
+ *p = tmpc;
+ ns = testsug(wlst, candidate, wl-1, ns, cpdsuggest, NULL, NULL);
+ if (ns == -1) return -1;
+ tmpc = tmpc2;
+ }
+ return ns;
+}
+
+// error is missing a letter it needs
+int SuggestMgr::forgotchar(char ** wlst, const char * word, int ns, int cpdsuggest)
+{
+ char candidate[MAXSWUTF8L];
+ char * p;
+ clock_t timelimit = clock();
+ int timer = MINTIMER;
+ int wl = strlen(word);
+ // try inserting a tryme character before every letter (and the null terminator)
+ for (int i = 0; i < ctryl; i++) {
+ strcpy(candidate, word);
+ for (p = candidate + wl; p >= candidate; p--) {
+ *(p+1) = *p;
+ *p = ctry[i];
+ ns = testsug(wlst, candidate, wl+1, ns, cpdsuggest, &timer, &timelimit);
+ if (ns == -1) return -1;
+ if (!timer) return ns;
+ }
+ }
+ return ns;
+}
+
+// error is missing a letter it needs
+int SuggestMgr::forgotchar_utf(char ** wlst, const w_char * word, int wl, int ns, int cpdsuggest)
+{
+ w_char candidate_utf[MAXSWL];
+ char candidate[MAXSWUTF8L];
+ w_char * p;
+ clock_t timelimit = clock();
+ int timer = MINTIMER;
+ // try inserting a tryme character at the end of the word and before every letter
+ for (int i = 0; i < ctryl; i++) {
+ memcpy (candidate_utf, word, wl * sizeof(w_char));
+ for (p = candidate_utf + wl; p >= candidate_utf; p--) {
+ *(p + 1) = *p;
+ *p = ctry_utf[i];
+ u16_u8(candidate, MAXSWUTF8L, candidate_utf, wl + 1);
+ ns = testsug(wlst, candidate, strlen(candidate), ns, cpdsuggest, &timer, &timelimit);
+ if (ns == -1) return -1;
+ if (!timer) return ns;
+ }
+ }
+ return ns;
+}
+
+
+/* error is should have been two words */
+int SuggestMgr::twowords(char ** wlst, const char * word, int ns, int cpdsuggest)
+{
+ char candidate[MAXSWUTF8L];
+ char * p;
+ int c1, c2;
+ int forbidden = 0;
+ int cwrd;
+
+ int wl=strlen(word);
+ if (wl < 3) return ns;
+
+ if (langnum == LANG_hu) forbidden = check_forbidden(word, wl);
+
+ strcpy(candidate + 1, word);
+ // split the string into two pieces after every char
+ // if both pieces are good words make them a suggestion
+ for (p = candidate + 1; p[1] != '\0'; p++) {
+ p[-1] = *p;
+ // go to end of the UTF-8 character
+ while (utf8 && ((p[1] & 0xc0) == 0x80)) {
+ *p = p[1];
+ p++;
+ }
+ if (utf8 && p[1] == '\0') break; // last UTF-8 character
+ *p = '\0';
+ c1 = checkword(candidate,strlen(candidate), cpdsuggest, NULL, NULL);
+ if (c1) {
+ c2 = checkword((p+1),strlen(p+1), cpdsuggest, NULL, NULL);
+ if (c2) {
+ *p = ' ';
+
+ // spec. Hungarian code (need a better compound word support)
+ if ((langnum == LANG_hu) && !forbidden &&
+ // if 3 repeating letter, use - instead of space
+ (((p[-1] == p[1]) && (((p>candidate+1) && (p[-1] == p[-2])) || (p[-1] == p[2]))) ||
+ // or multiple compounding, with more, than 6 syllables
+ ((c1 == 3) && (c2 >= 2)))) *p = '-';
+
+ cwrd = 1;
+ for (int k=0; k < ns; k++)
+ if (strcmp(candidate,wlst[k]) == 0) cwrd = 0;
+ if (ns < maxSug) {
+ if (cwrd) {
+ wlst[ns] = mystrdup(candidate);
+ if (wlst[ns] == NULL) return -1;
+ ns++;
+ }
+ } else return ns;
+ // add two word suggestion with dash, if TRY string contains
+ // "a" or "-"
+ // NOTE: cwrd doesn't modified for REP twoword sugg.
+ if (ctry && (strchr(ctry, 'a') || strchr(ctry, '-')) &&
+ mystrlen(p + 1) > 1 &&
+ mystrlen(candidate) - mystrlen(p) > 1) {
+ *p = '-';
+ for (int k=0; k < ns; k++)
+ if (strcmp(candidate,wlst[k]) == 0) cwrd = 0;
+ if (ns < maxSug) {
+ if (cwrd) {
+ wlst[ns] = mystrdup(candidate);
+ if (wlst[ns] == NULL) return -1;
+ ns++;
+ }
+ } else return ns;
+ }
+ }
+ }
+ }
+ return ns;
+}
+
+
+// error is adjacent letter were swapped
+int SuggestMgr::swapchar(char ** wlst, const char * word, int ns, int cpdsuggest)
+{
+ char candidate[MAXSWUTF8L];
+ char * p;
+ char tmpc;
+ int wl=strlen(word);
+ // try swapping adjacent chars one by one
+ strcpy(candidate, word);
+ for (p = candidate; p[1] != 0; p++) {
+ tmpc = *p;
+ *p = p[1];
+ p[1] = tmpc;
+ ns = testsug(wlst, candidate, wl, ns, cpdsuggest, NULL, NULL);
+ if (ns == -1) return -1;
+ p[1] = *p;
+ *p = tmpc;
+ }
+ // try double swaps for short words
+ // ahev -> have, owudl -> would
+ if (wl == 4 || wl == 5) {
+ candidate[0] = word[1];
+ candidate[1] = word[0];
+ candidate[2] = word[2];
+ candidate[wl - 2] = word[wl - 1];
+ candidate[wl - 1] = word[wl - 2];
+ ns = testsug(wlst, candidate, wl, ns, cpdsuggest, NULL, NULL);
+ if (ns == -1) return -1;
+ if (wl == 5) {
+ candidate[0] = word[0];
+ candidate[1] = word[2];
+ candidate[2] = word[1];
+ ns = testsug(wlst, candidate, wl, ns, cpdsuggest, NULL, NULL);
+ if (ns == -1) return -1;
+ }
+ }
+ return ns;
+}
+
+// error is adjacent letter were swapped
+int SuggestMgr::swapchar_utf(char ** wlst, const w_char * word, int wl, int ns, int cpdsuggest)
+{
+ w_char candidate_utf[MAXSWL];
+ char candidate[MAXSWUTF8L];
+ w_char * p;
+ w_char tmpc;
+ int len = 0;
+ // try swapping adjacent chars one by one
+ memcpy (candidate_utf, word, wl * sizeof(w_char));
+ for (p = candidate_utf; p < (candidate_utf + wl - 1); p++) {
+ tmpc = *p;
+ *p = p[1];
+ p[1] = tmpc;
+ u16_u8(candidate, MAXSWUTF8L, candidate_utf, wl);
+ if (len == 0) len = strlen(candidate);
+ ns = testsug(wlst, candidate, len, ns, cpdsuggest, NULL, NULL);
+ if (ns == -1) return -1;
+ p[1] = *p;
+ *p = tmpc;
+ }
+ // try double swaps for short words
+ // ahev -> have, owudl -> would, suodn -> sound
+ if (wl == 4 || wl == 5) {
+ candidate_utf[0] = word[1];
+ candidate_utf[1] = word[0];
+ candidate_utf[2] = word[2];
+ candidate_utf[wl - 2] = word[wl - 1];
+ candidate_utf[wl - 1] = word[wl - 2];
+ u16_u8(candidate, MAXSWUTF8L, candidate_utf, wl);
+ ns = testsug(wlst, candidate, len, ns, cpdsuggest, NULL, NULL);
+ if (ns == -1) return -1;
+ if (wl == 5) {
+ candidate_utf[0] = word[0];
+ candidate_utf[1] = word[2];
+ candidate_utf[2] = word[1];
+ u16_u8(candidate, MAXSWUTF8L, candidate_utf, wl);
+ ns = testsug(wlst, candidate, len, ns, cpdsuggest, NULL, NULL);
+ if (ns == -1) return -1;
+ }
+ }
+ return ns;
+}
+
+// error is not adjacent letter were swapped
+int SuggestMgr::longswapchar(char ** wlst, const char * word, int ns, int cpdsuggest)
+{
+ char candidate[MAXSWUTF8L];
+ char * p;
+ char * q;
+ char tmpc;
+ int wl=strlen(word);
+ // try swapping not adjacent chars one by one
+ strcpy(candidate, word);
+ for (p = candidate; *p != 0; p++) {
+ for (q = candidate; *q != 0; q++) {
+ if (abs((int)(p-q)) > 1) {
+ tmpc = *p;
+ *p = *q;
+ *q = tmpc;
+ ns = testsug(wlst, candidate, wl, ns, cpdsuggest, NULL, NULL);
+ if (ns == -1) return -1;
+ *q = *p;
+ *p = tmpc;
+ }
+ }
+ }
+ return ns;
+}
+
+
+// error is adjacent letter were swapped
+int SuggestMgr::longswapchar_utf(char ** wlst, const w_char * word, int wl, int ns, int cpdsuggest)
+{
+ w_char candidate_utf[MAXSWL];
+ char candidate[MAXSWUTF8L];
+ w_char * p;
+ w_char * q;
+ w_char tmpc;
+ // try swapping not adjacent chars
+ memcpy (candidate_utf, word, wl * sizeof(w_char));
+ for (p = candidate_utf; p < (candidate_utf + wl); p++) {
+ for (q = candidate_utf; q < (candidate_utf + wl); q++) {
+ if (abs((int)(p-q)) > 1) {
+ tmpc = *p;
+ *p = *q;
+ *q = tmpc;
+ u16_u8(candidate, MAXSWUTF8L, candidate_utf, wl);
+ ns = testsug(wlst, candidate, strlen(candidate), ns, cpdsuggest, NULL, NULL);
+ if (ns == -1) return -1;
+ *q = *p;
+ *p = tmpc;
+ }
+ }
+ }
+ return ns;
+}
+
+// error is a letter was moved
+int SuggestMgr::movechar(char ** wlst, const char * word, int ns, int cpdsuggest)
+{
+ char candidate[MAXSWUTF8L];
+ char * p;
+ char * q;
+ char tmpc;
+
+ int wl=strlen(word);
+ // try moving a char
+ strcpy(candidate, word);
+ for (p = candidate; *p != 0; p++) {
+ for (q = p + 1; (*q != 0) && ((q - p) < 10); q++) {
+ tmpc = *(q-1);
+ *(q-1) = *q;
+ *q = tmpc;
+ if ((q-p) < 2) continue; // omit swap char
+ ns = testsug(wlst, candidate, wl, ns, cpdsuggest, NULL, NULL);
+ if (ns == -1) return -1;
+ }
+ strcpy(candidate, word);
+ }
+ for (p = candidate + wl - 1; p > candidate; p--) {
+ for (q = p - 1; (q >= candidate) && ((p - q) < 10); q--) {
+ tmpc = *(q+1);
+ *(q+1) = *q;
+ *q = tmpc;
+ if ((p-q) < 2) continue; // omit swap char
+ ns = testsug(wlst, candidate, wl, ns, cpdsuggest, NULL, NULL);
+ if (ns == -1) return -1;
+ }
+ strcpy(candidate, word);
+ }
+ return ns;
+}
+
+// error is a letter was moved
+int SuggestMgr::movechar_utf(char ** wlst, const w_char * word, int wl, int ns, int cpdsuggest)
+{
+ w_char candidate_utf[MAXSWL];
+ char candidate[MAXSWUTF8L];
+ w_char * p;
+ w_char * q;
+ w_char tmpc;
+ // try moving a char
+ memcpy (candidate_utf, word, wl * sizeof(w_char));
+ for (p = candidate_utf; p < (candidate_utf + wl); p++) {
+ for (q = p + 1; (q < (candidate_utf + wl)) && ((q - p) < 10); q++) {
+ tmpc = *(q-1);
+ *(q-1) = *q;
+ *q = tmpc;
+ if ((q-p) < 2) continue; // omit swap char
+ u16_u8(candidate, MAXSWUTF8L, candidate_utf, wl);
+ ns = testsug(wlst, candidate, strlen(candidate), ns, cpdsuggest, NULL, NULL);
+ if (ns == -1) return -1;
+ }
+ memcpy (candidate_utf, word, wl * sizeof(w_char));
+ }
+ for (p = candidate_utf + wl - 1; p > candidate_utf; p--) {
+ for (q = p - 1; (q >= candidate_utf) && ((p - q) < 10); q--) {
+ tmpc = *(q+1);
+ *(q+1) = *q;
+ *q = tmpc;
+ if ((p-q) < 2) continue; // omit swap char
+ u16_u8(candidate, MAXSWUTF8L, candidate_utf, wl);
+ ns = testsug(wlst, candidate, strlen(candidate), ns, cpdsuggest, NULL, NULL);
+ if (ns == -1) return -1;
+ }
+ memcpy (candidate_utf, word, wl * sizeof(w_char));
+ }
+ return ns;
+}
+
+// generate a set of suggestions for very poorly spelled words
+int SuggestMgr::ngsuggest(char** wlst, char * w, int ns, HashMgr** pHMgr, int md)
+{
+
+ int i, j;
+ int lval;
+ int sc, scphon;
+ int lp, lpphon;
+ int nonbmp = 0;
+
+ // exhaustively search through all root words
+ // keeping track of the MAX_ROOTS most similar root words
+ struct hentry * roots[MAX_ROOTS];
+ char * rootsphon[MAX_ROOTS];
+ int scores[MAX_ROOTS];
+ int scoresphon[MAX_ROOTS];
+ for (i = 0; i < MAX_ROOTS; i++) {
+ roots[i] = NULL;
+ scores[i] = -100 * i;
+ rootsphon[i] = NULL;
+ scoresphon[i] = -100 * i;
+ }
+ lp = MAX_ROOTS - 1;
+ lpphon = MAX_ROOTS - 1;
+ scphon = -20000;
+ int low = NGRAM_LOWERING;
+
+ char w2[MAXWORDUTF8LEN];
+ char f[MAXSWUTF8L];
+ char * word = w;
+
+ // word reversing wrapper for complex prefixes
+ if (complexprefixes) {
+ strcpy(w2, w);
+ if (utf8) reverseword_utf(w2); else reverseword(w2);
+ word = w2;
+ }
+
+ char mw[MAXSWUTF8L];
+ w_char u8[MAXSWL];
+ int nc = strlen(word);
+ int n = (utf8) ? u8_u16(u8, MAXSWL, word) : nc;
+
+ // set character based ngram suggestion for words with non-BMP Unicode characters
+ if (n == -1) {
+ utf8 = 0; // XXX not state-free
+ n = nc;
+ nonbmp = 1;
+ low = 0;
+ }
+
+ struct hentry* hp = NULL;
+ int col = -1;
+ phonetable * ph = (pAMgr) ? pAMgr->get_phonetable() : NULL;
+ char target[MAXSWUTF8L];
+ char candidate[MAXSWUTF8L];
+ if (ph) {
+ if (utf8) {
+ w_char _w[MAXSWL];
+ int _wl = u8_u16(_w, MAXSWL, word);
+ mkallcap_utf(_w, _wl, langnum);
+ u16_u8(candidate, MAXSWUTF8L, _w, _wl);
+ } else {
+ strcpy(candidate, word);
+ if (!nonbmp) mkallcap(candidate, csconv);
+ }
+ phonet(candidate, target, nc, *ph); // XXX phonet() is 8-bit (nc, not n)
+ }
+
+ FLAG forbiddenword = pAMgr ? pAMgr->get_forbiddenword() : FLAG_NULL;
+ FLAG nosuggest = pAMgr ? pAMgr->get_nosuggest() : FLAG_NULL;
+ FLAG nongramsuggest = pAMgr ? pAMgr->get_nongramsuggest() : FLAG_NULL;
+ FLAG onlyincompound = pAMgr ? pAMgr->get_onlyincompound() : FLAG_NULL;
+
+ for (i = 0; i < md; i++) {
+ while (0 != (hp = (pHMgr[i])->walk_hashtable(col, hp))) {
+ if ((hp->astr) && (pAMgr) &&
+ (TESTAFF(hp->astr, forbiddenword, hp->alen) ||
+ TESTAFF(hp->astr, ONLYUPCASEFLAG, hp->alen) ||
+ TESTAFF(hp->astr, nosuggest, hp->alen) ||
+ TESTAFF(hp->astr, nongramsuggest, hp->alen) ||
+ TESTAFF(hp->astr, onlyincompound, hp->alen))) continue;
+
+ sc = ngram(3, word, HENTRY_WORD(hp), NGRAM_LONGER_WORSE + low) +
+ leftcommonsubstring(word, HENTRY_WORD(hp));
+
+ // check special pronounciation
+ if ((hp->var & H_OPT_PHON) && copy_field(f, HENTRY_DATA(hp), MORPH_PHON)) {
+ int sc2 = ngram(3, word, f, NGRAM_LONGER_WORSE + low) +
+ + leftcommonsubstring(word, f);
+ if (sc2 > sc) sc = sc2;
+ }
+
+ scphon = -20000;
+ if (ph && (sc > 2) && (abs(n - (int) hp->clen) <= 3)) {
+ char target2[MAXSWUTF8L];
+ if (utf8) {
+ w_char _w[MAXSWL];
+ int _wl = u8_u16(_w, MAXSWL, HENTRY_WORD(hp));
+ mkallcap_utf(_w, _wl, langnum);
+ u16_u8(candidate, MAXSWUTF8L, _w, _wl);
+ } else {
+ strcpy(candidate, HENTRY_WORD(hp));
+ mkallcap(candidate, csconv);
+ }
+ phonet(candidate, target2, -1, *ph);
+ scphon = 2 * ngram(3, target, target2, NGRAM_LONGER_WORSE);
+ }
+
+ if (sc > scores[lp]) {
+ scores[lp] = sc;
+ roots[lp] = hp;
+ lval = sc;
+ for (j=0; j < MAX_ROOTS; j++)
+ if (scores[j] < lval) {
+ lp = j;
+ lval = scores[j];
+ }
+ }
+
+
+ if (scphon > scoresphon[lpphon]) {
+ scoresphon[lpphon] = scphon;
+ rootsphon[lpphon] = HENTRY_WORD(hp);
+ lval = scphon;
+ for (j=0; j < MAX_ROOTS; j++)
+ if (scoresphon[j] < lval) {
+ lpphon = j;
+ lval = scoresphon[j];
+ }
+ }
+ }}
+
+ // find minimum threshold for a passable suggestion
+ // mangle original word three differnt ways
+ // and score them to generate a minimum acceptable score
+ int thresh = 0;
+ for (int sp = 1; sp < 4; sp++) {
+ if (utf8) {
+ for (int k=sp; k < n; k+=4) *((unsigned short *) u8 + k) = '*';
+ u16_u8(mw, MAXSWUTF8L, u8, n);
+ thresh = thresh + ngram(n, word, mw, NGRAM_ANY_MISMATCH + low);
+ } else {
+ strcpy(mw, word);
+ for (int k=sp; k < n; k+=4) *(mw + k) = '*';
+ thresh = thresh + ngram(n, word, mw, NGRAM_ANY_MISMATCH + low);
+ }
+ }
+ thresh = thresh / 3;
+ thresh--;
+
+ // now expand affixes on each of these root words and
+ // and use length adjusted ngram scores to select
+ // possible suggestions
+ char * guess[MAX_GUESS];
+ char * guessorig[MAX_GUESS];
+ int gscore[MAX_GUESS];
+ for(i=0;i<MAX_GUESS;i++) {
+ guess[i] = NULL;
+ guessorig[i] = NULL;
+ gscore[i] = -100 * i;
+ }
+
+ lp = MAX_GUESS - 1;
+
+ struct guessword * glst;
+ glst = (struct guessword *) calloc(MAX_WORDS,sizeof(struct guessword));
+ if (! glst) {
+ if (nonbmp) utf8 = 1;
+ return ns;
+ }
+
+ for (i = 0; i < MAX_ROOTS; i++) {
+ if (roots[i]) {
+ struct hentry * rp = roots[i];
+ int nw = pAMgr->expand_rootword(glst, MAX_WORDS, HENTRY_WORD(rp), rp->blen,
+ rp->astr, rp->alen, word, nc,
+ ((rp->var & H_OPT_PHON) ? copy_field(f, HENTRY_DATA(rp), MORPH_PHON) : NULL));
+
+ for (int k = 0; k < nw ; k++) {
+ sc = ngram(n, word, glst[k].word, NGRAM_ANY_MISMATCH + low) +
+ leftcommonsubstring(word, glst[k].word);
+
+ if (sc > thresh) {
+ if (sc > gscore[lp]) {
+ if (guess[lp]) {
+ free (guess[lp]);
+ if (guessorig[lp]) {
+ free(guessorig[lp]);
+ guessorig[lp] = NULL;
+ }
+ }
+ gscore[lp] = sc;
+ guess[lp] = glst[k].word;
+ guessorig[lp] = glst[k].orig;
+ lval = sc;
+ for (j=0; j < MAX_GUESS; j++)
+ if (gscore[j] < lval) {
+ lp = j;
+ lval = gscore[j];
+ }
+ } else {
+ free(glst[k].word);
+ if (glst[k].orig) free(glst[k].orig);
+ }
+ } else {
+ free(glst[k].word);
+ if (glst[k].orig) free(glst[k].orig);
+ }
+ }
+ }
+ }
+ free(glst);
+
+ // now we are done generating guesses
+ // sort in order of decreasing score
+
+
+ bubblesort(&guess[0], &guessorig[0], &gscore[0], MAX_GUESS);
+ if (ph) bubblesort(&rootsphon[0], NULL, &scoresphon[0], MAX_ROOTS);
+
+ // weight suggestions with a similarity index, based on
+ // the longest common subsequent algorithm and resort
+
+ int is_swap = 0;
+ int re = 0;
+ double fact = 1.0;
+ if (pAMgr) {
+ int maxd = pAMgr->get_maxdiff();
+ if (maxd >= 0) fact = (10.0 - maxd)/5.0;
+ }
+
+ for (i=0; i < MAX_GUESS; i++) {
+ if (guess[i]) {
+ // lowering guess[i]
+ char gl[MAXSWUTF8L];
+ int len;
+ if (utf8) {
+ w_char _w[MAXSWL];
+ len = u8_u16(_w, MAXSWL, guess[i]);
+ mkallsmall_utf(_w, len, langnum);
+ u16_u8(gl, MAXSWUTF8L, _w, len);
+ } else {
+ strcpy(gl, guess[i]);
+ if (!nonbmp) mkallsmall(gl, csconv);
+ len = strlen(guess[i]);
+ }
+
+ int _lcs = lcslen(word, gl);
+
+ // same characters with different casing
+ if ((n == len) && (n == _lcs)) {
+ gscore[i] += 2000;
+ break;
+ }
+ // using 2-gram instead of 3, and other weightening
+ gscore[i] =
+ // length of longest common subsequent minus length difference
+ 2 * _lcs - abs((int) (n - len)) +
+ // weight length of the left common substring
+ leftcommonsubstring(word, gl) +
+ // weight equal character positions
+ (!nonbmp && commoncharacterpositions(word, gl, &is_swap) ? 1: 0) +
+ // swap character (not neighboring)
+ ((is_swap) ? 10 : 0) +
+ // ngram
+ ngram(4, word, gl, NGRAM_ANY_MISMATCH + low) +
+ // weighted ngrams
+ (re = ngram(2, word, gl, NGRAM_ANY_MISMATCH + low + NGRAM_WEIGHTED)) +
+ (re += ngram(2, gl, word, NGRAM_ANY_MISMATCH + low + NGRAM_WEIGHTED)) +
+ // different limit for dictionaries with PHONE rules
+ (ph ? (re < len * fact ? -1000 : 0) : (re < (n + len)*fact? -1000 : 0));
+ }
+ }
+
+ bubblesort(&guess[0], &guessorig[0], &gscore[0], MAX_GUESS);
+
+// phonetic version
+ if (ph) for (i=0; i < MAX_ROOTS; i++) {
+ if (rootsphon[i]) {
+ // lowering rootphon[i]
+ char gl[MAXSWUTF8L];
+ int len;
+ if (utf8) {
+ w_char _w[MAXSWL];
+ len = u8_u16(_w, MAXSWL, rootsphon[i]);
+ mkallsmall_utf(_w, len, langnum);
+ u16_u8(gl, MAXSWUTF8L, _w, len);
+ } else {
+ strcpy(gl, rootsphon[i]);
+ if (!nonbmp) mkallsmall(gl, csconv);
+ len = strlen(rootsphon[i]);
+ }
+
+ // heuristic weigthing of ngram scores
+ scoresphon[i] += 2 * lcslen(word, gl) - abs((int) (n - len)) +
+ // weight length of the left common substring
+ leftcommonsubstring(word, gl);
+ }
+ }
+
+ if (ph) bubblesort(&rootsphon[0], NULL, &scoresphon[0], MAX_ROOTS);
+
+ // copy over
+ int oldns = ns;
+
+ int same = 0;
+ for (i=0; i < MAX_GUESS; i++) {
+ if (guess[i]) {
+ if ((ns < oldns + maxngramsugs) && (ns < maxSug) && (!same || (gscore[i] > 1000))) {
+ int unique = 1;
+ // leave only excellent suggestions, if exists
+ if (gscore[i] > 1000) same = 1; else if (gscore[i] < -100) {
+ same = 1;
+ // keep the best ngram suggestions, unless in ONLYMAXDIFF mode
+ if (ns > oldns || (pAMgr && pAMgr->get_onlymaxdiff())) {
+ free(guess[i]);
+ if (guessorig[i]) free(guessorig[i]);
+ continue;
+ }
+ }
+ for (j = 0; j < ns; j++) {
+ // don't suggest previous suggestions or a previous suggestion with prefixes or affixes
+ if ((!guessorig[i] && strstr(guess[i], wlst[j])) ||
+ (guessorig[i] && strstr(guessorig[i], wlst[j])) ||
+ // check forbidden words
+ !checkword(guess[i], strlen(guess[i]), 0, NULL, NULL)) unique = 0;
+ }
+ if (unique) {
+ wlst[ns++] = guess[i];
+ if (guessorig[i]) {
+ free(guess[i]);
+ wlst[ns-1] = guessorig[i];
+ }
+ } else {
+ free(guess[i]);
+ if (guessorig[i]) free(guessorig[i]);
+ }
+ } else {
+ free(guess[i]);
+ if (guessorig[i]) free(guessorig[i]);
+ }
+ }
+ }
+
+ oldns = ns;
+ if (ph) for (i=0; i < MAX_ROOTS; i++) {
+ if (rootsphon[i]) {
+ if ((ns < oldns + MAXPHONSUGS) && (ns < maxSug)) {
+ int unique = 1;
+ for (j = 0; j < ns; j++) {
+ // don't suggest previous suggestions or a previous suggestion with prefixes or affixes
+ if (strstr(rootsphon[i], wlst[j]) ||
+ // check forbidden words
+ !checkword(rootsphon[i], strlen(rootsphon[i]), 0, NULL, NULL)) unique = 0;
+ }
+ if (unique) {
+ wlst[ns++] = mystrdup(rootsphon[i]);
+ if (!wlst[ns - 1]) return ns - 1;
+ }
+ }
+ }
+ }
+
+ if (nonbmp) utf8 = 1;
+ return ns;
+}
+
+
+// see if a candidate suggestion is spelled correctly
+// needs to check both root words and words with affixes
+
+// obsolote MySpell-HU modifications:
+// return value 2 and 3 marks compounding with hyphen (-)
+// `3' marks roots without suffix
+int SuggestMgr::checkword(const char * word, int len, int cpdsuggest, int * timer, clock_t * timelimit)
+{
+ struct hentry * rv=NULL;
+ struct hentry * rv2=NULL;
+ int nosuffix = 0;
+
+ // check time limit
+ if (timer) {
+ (*timer)--;
+ if (!(*timer) && timelimit) {
+ if ((clock() - *timelimit) > TIMELIMIT) return 0;
+ *timer = MAXPLUSTIMER;
+ }
+ }
+
+ if (pAMgr) {
+ if (cpdsuggest==1) {
+ if (pAMgr->get_compound()) {
+ rv = pAMgr->compound_check(word, len, 0, 0, 100, 0, NULL, 0, 1, 0); //EXT
+ if (rv && (!(rv2 = pAMgr->lookup(word)) || !rv2->astr ||
+ !(TESTAFF(rv2->astr,pAMgr->get_forbiddenword(),rv2->alen) ||
+ TESTAFF(rv2->astr,pAMgr->get_nosuggest(),rv2->alen)))) return 3; // XXX obsolote categorisation + only ICONV needs affix flag check?
+ }
+ return 0;
+ }
+
+ rv = pAMgr->lookup(word);
+
+ if (rv) {
+ if ((rv->astr) && (TESTAFF(rv->astr,pAMgr->get_forbiddenword(),rv->alen)
+ || TESTAFF(rv->astr,pAMgr->get_nosuggest(),rv->alen))) return 0;
+ while (rv) {
+ if (rv->astr && (TESTAFF(rv->astr,pAMgr->get_needaffix(),rv->alen) ||
+ TESTAFF(rv->astr, ONLYUPCASEFLAG, rv->alen) ||
+ TESTAFF(rv->astr,pAMgr->get_onlyincompound(),rv->alen))) {
+ rv = rv->next_homonym;
+ } else break;
+ }
+ } else rv = pAMgr->prefix_check(word, len, 0); // only prefix, and prefix + suffix XXX
+
+ if (rv) {
+ nosuffix=1;
+ } else {
+ rv = pAMgr->suffix_check(word, len, 0, NULL, NULL, 0, NULL); // only suffix
+ }
+
+ if (!rv && pAMgr->have_contclass()) {
+ rv = pAMgr->suffix_check_twosfx(word, len, 0, NULL, FLAG_NULL);
+ if (!rv) rv = pAMgr->prefix_check_twosfx(word, len, 1, FLAG_NULL);
+ }
+
+ // check forbidden words
+ if ((rv) && (rv->astr) && (TESTAFF(rv->astr,pAMgr->get_forbiddenword(),rv->alen) ||
+ TESTAFF(rv->astr, ONLYUPCASEFLAG, rv->alen) ||
+ TESTAFF(rv->astr,pAMgr->get_nosuggest(),rv->alen) ||
+ TESTAFF(rv->astr,pAMgr->get_onlyincompound(),rv->alen))) return 0;
+
+ if (rv) { // XXX obsolote
+ if ((pAMgr->get_compoundflag()) &&
+ TESTAFF(rv->astr, pAMgr->get_compoundflag(), rv->alen)) return 2 + nosuffix;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+int SuggestMgr::check_forbidden(const char * word, int len)
+{
+ struct hentry * rv = NULL;
+
+ if (pAMgr) {
+ rv = pAMgr->lookup(word);
+ if (rv && rv->astr && (TESTAFF(rv->astr,pAMgr->get_needaffix(),rv->alen) ||
+ TESTAFF(rv->astr,pAMgr->get_onlyincompound(),rv->alen))) rv = NULL;
+ if (!(pAMgr->prefix_check(word,len,1)))
+ rv = pAMgr->suffix_check(word,len, 0, NULL, NULL, 0, NULL); // prefix+suffix, suffix
+ // check forbidden words
+ if ((rv) && (rv->astr) && TESTAFF(rv->astr,pAMgr->get_forbiddenword(),rv->alen)) return 1;
+ }
+ return 0;
+}
+
+#ifdef HUNSPELL_EXPERIMENTAL
+// suggest possible stems
+int SuggestMgr::suggest_pos_stems(char*** slst, const char * w, int nsug)
+{
+ char ** wlst;
+
+ struct hentry * rv = NULL;
+
+ char w2[MAXSWUTF8L];
+ const char * word = w;
+
+ // word reversing wrapper for complex prefixes
+ if (complexprefixes) {
+ strcpy(w2, w);
+ if (utf8) reverseword_utf(w2); else reverseword(w2);
+ word = w2;
+ }
+
+ int wl = strlen(word);
+
+
+ if (*slst) {
+ wlst = *slst;
+ } else {
+ wlst = (char **) calloc(maxSug, sizeof(char *));
+ if (wlst == NULL) return -1;
+ }
+
+ rv = pAMgr->suffix_check(word, wl, 0, NULL, wlst, maxSug, &nsug);
+
+ // delete dash from end of word
+ if (nsug > 0) {
+ for (int j=0; j < nsug; j++) {
+ if (wlst[j][strlen(wlst[j]) - 1] == '-') wlst[j][strlen(wlst[j]) - 1] = '\0';
+ }
+ }
+
+ *slst = wlst;
+ return nsug;
+}
+#endif // END OF HUNSPELL_EXPERIMENTAL CODE
+
+
+char * SuggestMgr::suggest_morph(const char * w)
+{
+ char result[MAXLNLEN];
+ char * r = (char *) result;
+ char * st;
+
+ struct hentry * rv = NULL;
+
+ *result = '\0';
+
+ if (! pAMgr) return NULL;
+
+ char w2[MAXSWUTF8L];
+ const char * word = w;
+
+ // word reversing wrapper for complex prefixes
+ if (complexprefixes) {
+ strcpy(w2, w);
+ if (utf8) reverseword_utf(w2); else reverseword(w2);
+ word = w2;
+ }
+
+ rv = pAMgr->lookup(word);
+
+ while (rv) {
+ if ((!rv->astr) || !(TESTAFF(rv->astr, pAMgr->get_forbiddenword(), rv->alen) ||
+ TESTAFF(rv->astr, pAMgr->get_needaffix(), rv->alen) ||
+ TESTAFF(rv->astr,pAMgr->get_onlyincompound(),rv->alen))) {
+ if (!HENTRY_FIND(rv, MORPH_STEM)) {
+ mystrcat(result, " ", MAXLNLEN);
+ mystrcat(result, MORPH_STEM, MAXLNLEN);
+ mystrcat(result, word, MAXLNLEN);
+ }
+ if (HENTRY_DATA(rv)) {
+ mystrcat(result, " ", MAXLNLEN);
+ mystrcat(result, HENTRY_DATA2(rv), MAXLNLEN);
+ }
+ mystrcat(result, "\n", MAXLNLEN);
+ }
+ rv = rv->next_homonym;
+ }
+
+ st = pAMgr->affix_check_morph(word,strlen(word));
+ if (st) {
+ mystrcat(result, st, MAXLNLEN);
+ free(st);
+ }
+
+ if (pAMgr->get_compound() && (*result == '\0'))
+ pAMgr->compound_check_morph(word, strlen(word),
+ 0, 0, 100, 0,NULL, 0, &r, NULL);
+
+ return (*result) ? mystrdup(line_uniq(result, MSEP_REC)) : NULL;
+}
+
+#ifdef HUNSPELL_EXPERIMENTAL
+char * SuggestMgr::suggest_morph_for_spelling_error(const char * word)
+{
+ char * p = NULL;
+ char ** wlst = (char **) calloc(maxSug, sizeof(char *));
+ if (!**wlst) return NULL;
+ // we will use only the first suggestion
+ for (int i = 0; i < maxSug - 1; i++) wlst[i] = "";
+ int ns = suggest(&wlst, word, maxSug - 1, NULL);
+ if (ns == maxSug) {
+ p = suggest_morph(wlst[maxSug - 1]);
+ free(wlst[maxSug - 1]);
+ }
+ if (wlst) free(wlst);
+ return p;
+}
+#endif // END OF HUNSPELL_EXPERIMENTAL CODE
+
+/* affixation */
+char * SuggestMgr::suggest_hentry_gen(hentry * rv, char * pattern)
+{
+ char result[MAXLNLEN];
+ *result = '\0';
+ int sfxcount = get_sfxcount(pattern);
+
+ if (get_sfxcount(HENTRY_DATA(rv)) > sfxcount) return NULL;
+
+ if (HENTRY_DATA(rv)) {
+ char * aff = pAMgr->morphgen(HENTRY_WORD(rv), rv->blen, rv->astr, rv->alen,
+ HENTRY_DATA(rv), pattern, 0);
+ if (aff) {
+ mystrcat(result, aff, MAXLNLEN);
+ mystrcat(result, "\n", MAXLNLEN);
+ free(aff);
+ }
+ }
+
+ // check all allomorphs
+ char allomorph[MAXLNLEN];
+ char * p = NULL;
+ if (HENTRY_DATA(rv)) p = (char *) strstr(HENTRY_DATA2(rv), MORPH_ALLOMORPH);
+ while (p) {
+ struct hentry * rv2 = NULL;
+ p += MORPH_TAG_LEN;
+ int plen = fieldlen(p);
+ strncpy(allomorph, p, plen);
+ allomorph[plen] = '\0';
+ rv2 = pAMgr->lookup(allomorph);
+ while (rv2) {
+// if (HENTRY_DATA(rv2) && get_sfxcount(HENTRY_DATA(rv2)) <= sfxcount) {
+ if (HENTRY_DATA(rv2)) {
+ char * st = (char *) strstr(HENTRY_DATA2(rv2), MORPH_STEM);
+ if (st && (strncmp(st + MORPH_TAG_LEN,
+ HENTRY_WORD(rv), fieldlen(st + MORPH_TAG_LEN)) == 0)) {
+ char * aff = pAMgr->morphgen(HENTRY_WORD(rv2), rv2->blen, rv2->astr, rv2->alen,
+ HENTRY_DATA(rv2), pattern, 0);
+ if (aff) {
+ mystrcat(result, aff, MAXLNLEN);
+ mystrcat(result, "\n", MAXLNLEN);
+ free(aff);
+ }
+ }
+ }
+ rv2 = rv2->next_homonym;
+ }
+ p = strstr(p + plen, MORPH_ALLOMORPH);
+ }
+
+ return (*result) ? mystrdup(result) : NULL;
+}
+
+char * SuggestMgr::suggest_gen(char ** desc, int n, char * pattern) {
+ char result[MAXLNLEN];
+ char result2[MAXLNLEN];
+ char newpattern[MAXLNLEN];
+ *newpattern = '\0';
+ if (n == 0) return 0;
+ *result2 = '\0';
+ struct hentry * rv = NULL;
+ if (!pAMgr) return NULL;
+
+// search affixed forms with and without derivational suffixes
+ while(1) {
+
+ for (int k = 0; k < n; k++) {
+ *result = '\0';
+ // add compound word parts (except the last one)
+ char * s = (char *) desc[k];
+ char * part = strstr(s, MORPH_PART);
+ if (part) {
+ char * nextpart = strstr(part + 1, MORPH_PART);
+ while (nextpart) {
+ copy_field(result + strlen(result), part, MORPH_PART);
+ part = nextpart;
+ nextpart = strstr(part + 1, MORPH_PART);
+ }
+ s = part;
+ }
+
+ char **pl;
+ char tok[MAXLNLEN];
+ strcpy(tok, s);
+ char * alt = strstr(tok, " | ");
+ while (alt) {
+ alt[1] = MSEP_ALT;
+ alt = strstr(alt, " | ");
+ }
+ int pln = line_tok(tok, &pl, MSEP_ALT);
+ for (int i = 0; i < pln; i++) {
+ // remove inflectional and terminal suffixes
+ char * is = strstr(pl[i], MORPH_INFL_SFX);
+ if (is) *is = '\0';
+ char * ts = strstr(pl[i], MORPH_TERM_SFX);
+ while (ts) {
+ *ts = '_';
+ ts = strstr(pl[i], MORPH_TERM_SFX);
+ }
+ char * st = strstr(s, MORPH_STEM);
+ if (st) {
+ copy_field(tok, st, MORPH_STEM);
+ rv = pAMgr->lookup(tok);
+ while (rv) {
+ char newpat[MAXLNLEN];
+ strcpy(newpat, pl[i]);
+ strcat(newpat, pattern);
+ char * sg = suggest_hentry_gen(rv, newpat);
+ if (!sg) sg = suggest_hentry_gen(rv, pattern);
+ if (sg) {
+ char ** gen;
+ int genl = line_tok(sg, &gen, MSEP_REC);
+ free(sg);
+ sg = NULL;
+ for (int j = 0; j < genl; j++) {
+ if (strstr(pl[i], MORPH_SURF_PFX)) {
+ int r2l = strlen(result2);
+ result2[r2l] = MSEP_REC;
+ strcpy(result2 + r2l + 1, result);
+ copy_field(result2 + strlen(result2), pl[i], MORPH_SURF_PFX);
+ mystrcat(result2, gen[j], MAXLNLEN);
+ } else {
+ sprintf(result2 + strlen(result2), "%c%s%s",
+ MSEP_REC, result, gen[j]);
+ }
+ }
+ freelist(&gen, genl);
+ }
+ rv = rv->next_homonym;
+ }
+ }
+ }
+ freelist(&pl, pln);
+ }
+
+ if (*result2 || !strstr(pattern, MORPH_DERI_SFX)) break;
+ strcpy(newpattern, pattern);
+ pattern = newpattern;
+ char * ds = strstr(pattern, MORPH_DERI_SFX);
+ while (ds) {
+ strncpy(ds, MORPH_TERM_SFX, MORPH_TAG_LEN);
+ ds = strstr(pattern, MORPH_DERI_SFX);
+ }
+ }
+ return (*result2 ? mystrdup(result2) : NULL);
+}
+
+
+// generate an n-gram score comparing s1 and s2
+int SuggestMgr::ngram(int n, char * s1, const char * s2, int opt)
+{
+ int nscore = 0;
+ int ns;
+ int l1;
+ int l2;
+ int test = 0;
+
+ if (utf8) {
+ w_char su1[MAXSWL];
+ w_char su2[MAXSWL];
+ l1 = u8_u16(su1, MAXSWL, s1);
+ l2 = u8_u16(su2, MAXSWL, s2);
+ if ((l2 <= 0) || (l1 == -1)) return 0;
+ // lowering dictionary word
+ if (opt & NGRAM_LOWERING) mkallsmall_utf(su2, l2, langnum);
+ for (int j = 1; j <= n; j++) {
+ ns = 0;
+ for (int i = 0; i <= (l1-j); i++) {
+ int k = 0;
+ for (int l = 0; l <= (l2-j); l++) {
+ for (k = 0; k < j; k++) {
+ w_char * c1 = su1 + i + k;
+ w_char * c2 = su2 + l + k;
+ if ((c1->l != c2->l) || (c1->h != c2->h)) break;
+ }
+ if (k == j) {
+ ns++;
+ break;
+ }
+ }
+ if (k != j && opt & NGRAM_WEIGHTED) {
+ ns--;
+ test++;
+ if (i == 0 || i == l1-j) ns--; // side weight
+ }
+ }
+ nscore = nscore + ns;
+ if (ns < 2 && !(opt & NGRAM_WEIGHTED)) break;
+ }
+ } else {
+ l2 = strlen(s2);
+ if (l2 == 0) return 0;
+ l1 = strlen(s1);
+ char *t = mystrdup(s2);
+ if (opt & NGRAM_LOWERING) mkallsmall(t, csconv);
+ for (int j = 1; j <= n; j++) {
+ ns = 0;
+ for (int i = 0; i <= (l1-j); i++) {
+ char c = *(s1 + i + j);
+ *(s1 + i + j) = '\0';
+ if (strstr(t,(s1+i))) {
+ ns++;
+ } else if (opt & NGRAM_WEIGHTED) {
+ ns--;
+test++;
+ if (i == 0 || i == l1-j) ns--; // side weight
+ }
+ *(s1 + i + j ) = c;
+ }
+ nscore = nscore + ns;
+ if (ns < 2 && !(opt & NGRAM_WEIGHTED)) break;
+ }
+ free(t);
+ }
+
+ ns = 0;
+ if (opt & NGRAM_LONGER_WORSE) ns = (l2-l1)-2;
+ if (opt & NGRAM_ANY_MISMATCH) ns = abs(l2-l1)-2;
+ ns = (nscore - ((ns > 0) ? ns : 0));
+ return ns;
+}
+
+// length of the left common substring of s1 and (decapitalised) s2
+int SuggestMgr::leftcommonsubstring(char * s1, const char * s2) {
+ if (utf8) {
+ w_char su1[MAXSWL];
+ w_char su2[MAXSWL];
+ su1[0].l = su2[0].l = su1[0].h = su2[0].h = 0;
+ // decapitalize dictionary word
+ if (complexprefixes) {
+ int l1 = u8_u16(su1, MAXSWL, s1);
+ int l2 = u8_u16(su2, MAXSWL, s2);
+ if (*((short *)su1+l1-1) == *((short *)su2+l2-1)) return 1;
+ } else {
+ int i;
+ u8_u16(su1, 1, s1);
+ u8_u16(su2, 1, s2);
+ unsigned short idx = (su2->h << 8) + su2->l;
+ unsigned short otheridx = (su1->h << 8) + su1->l;
+ if (otheridx != idx &&
+ (otheridx != unicodetolower(idx, langnum))) return 0;
+ int l1 = u8_u16(su1, MAXSWL, s1);
+ int l2 = u8_u16(su2, MAXSWL, s2);
+ for(i = 1; (i < l1) && (i < l2) &&
+ (su1[i].l == su2[i].l) && (su1[i].h == su2[i].h); i++);
+ return i;
+ }
+ } else {
+ if (complexprefixes) {
+ int l1 = strlen(s1);
+ int l2 = strlen(s2);
+ if (*(s2+l1-1) == *(s2+l2-1)) return 1;
+ } else {
+ char * olds = s1;
+ // decapitalise dictionary word
+ if ((*s1 != *s2) && (*s1 != csconv[((unsigned char)*s2)].clower)) return 0;
+ do {
+ s1++; s2++;
+ } while ((*s1 == *s2) && (*s1 != '\0'));
+ return (int)(s1 - olds);
+ }
+ }
+ return 0;
+}
+
+int SuggestMgr::commoncharacterpositions(char * s1, const char * s2, int * is_swap) {
+ int num = 0;
+ int diff = 0;
+ int diffpos[2];
+ *is_swap = 0;
+ if (utf8) {
+ w_char su1[MAXSWL];
+ w_char su2[MAXSWL];
+ int l1 = u8_u16(su1, MAXSWL, s1);
+ int l2 = u8_u16(su2, MAXSWL, s2);
+ // decapitalize dictionary word
+ if (complexprefixes) {
+ mkallsmall_utf(su2+l2-1, 1, langnum);
+ } else {
+ mkallsmall_utf(su2, 1, langnum);
+ }
+ for (int i = 0; (i < l1) && (i < l2); i++) {
+ if (((short *) su1)[i] == ((short *) su2)[i]) {
+ num++;
+ } else {
+ if (diff < 2) diffpos[diff] = i;
+ diff++;
+ }
+ }
+ if ((diff == 2) && (l1 == l2) &&
+ (((short *) su1)[diffpos[0]] == ((short *) su2)[diffpos[1]]) &&
+ (((short *) su1)[diffpos[1]] == ((short *) su2)[diffpos[0]])) *is_swap = 1;
+ } else {
+ int i;
+ char t[MAXSWUTF8L];
+ strcpy(t, s2);
+ // decapitalize dictionary word
+ if (complexprefixes) {
+ int l2 = strlen(t);
+ *(t+l2-1) = csconv[((unsigned char)*(t+l2-1))].clower;
+ } else {
+ mkallsmall(t, csconv);
+ }
+ for (i = 0; (*(s1+i) != 0) && (*(t+i) != 0); i++) {
+ if (*(s1+i) == *(t+i)) {
+ num++;
+ } else {
+ if (diff < 2) diffpos[diff] = i;
+ diff++;
+ }
+ }
+ if ((diff == 2) && (*(s1+i) == 0) && (*(t+i) == 0) &&
+ (*(s1+diffpos[0]) == *(t+diffpos[1])) &&
+ (*(s1+diffpos[1]) == *(t+diffpos[0]))) *is_swap = 1;
+ }
+ return num;
+}
+
+int SuggestMgr::mystrlen(const char * word) {
+ if (utf8) {
+ w_char w[MAXSWL];
+ return u8_u16(w, MAXSWL, word);
+ } else return strlen(word);
+}
+
+// sort in decreasing order of score
+void SuggestMgr::bubblesort(char** rword, char** rword2, int* rsc, int n )
+{
+ int m = 1;
+ while (m < n) {
+ int j = m;
+ while (j > 0) {
+ if (rsc[j-1] < rsc[j]) {
+ int sctmp = rsc[j-1];
+ char * wdtmp = rword[j-1];
+ rsc[j-1] = rsc[j];
+ rword[j-1] = rword[j];
+ rsc[j] = sctmp;
+ rword[j] = wdtmp;
+ if (rword2) {
+ wdtmp = rword2[j-1];
+ rword2[j-1] = rword2[j];
+ rword2[j] = wdtmp;
+ }
+ j--;
+ } else break;
+ }
+ m++;
+ }
+ return;
+}
+
+// longest common subsequence
+void SuggestMgr::lcs(const char * s, const char * s2, int * l1, int * l2, char ** result) {
+ int n, m;
+ w_char su[MAXSWL];
+ w_char su2[MAXSWL];
+ char * b;
+ char * c;
+ int i;
+ int j;
+ if (utf8) {
+ m = u8_u16(su, MAXSWL, s);
+ n = u8_u16(su2, MAXSWL, s2);
+ } else {
+ m = strlen(s);
+ n = strlen(s2);
+ }
+ c = (char *) malloc((m + 1) * (n + 1));
+ b = (char *) malloc((m + 1) * (n + 1));
+ if (!c || !b) {
+ if (c) free(c);
+ if (b) free(b);
+ *result = NULL;
+ return;
+ }
+ for (i = 1; i <= m; i++) c[i*(n+1)] = 0;
+ for (j = 0; j <= n; j++) c[j] = 0;
+ for (i = 1; i <= m; i++) {
+ for (j = 1; j <= n; j++) {
+ if ( ((utf8) && (*((short *) su+i-1) == *((short *)su2+j-1)))
+ || ((!utf8) && ((*(s+i-1)) == (*(s2+j-1))))) {
+ c[i*(n+1) + j] = c[(i-1)*(n+1) + j-1]+1;
+ b[i*(n+1) + j] = LCS_UPLEFT;
+ } else if (c[(i-1)*(n+1) + j] >= c[i*(n+1) + j-1]) {
+ c[i*(n+1) + j] = c[(i-1)*(n+1) + j];
+ b[i*(n+1) + j] = LCS_UP;
+ } else {
+ c[i*(n+1) + j] = c[i*(n+1) + j-1];
+ b[i*(n+1) + j] = LCS_LEFT;
+ }
+ }
+ }
+ *result = b;
+ free(c);
+ *l1 = m;
+ *l2 = n;
+}
+
+int SuggestMgr::lcslen(const char * s, const char* s2) {
+ int m;
+ int n;
+ int i;
+ int j;
+ char * result;
+ int len = 0;
+ lcs(s, s2, &m, &n, &result);
+ if (!result) return 0;
+ i = m;
+ j = n;
+ while ((i != 0) && (j != 0)) {
+ if (result[i*(n+1) + j] == LCS_UPLEFT) {
+ len++;
+ i--;
+ j--;
+ } else if (result[i*(n+1) + j] == LCS_UP) {
+ i--;
+ } else j--;
+ }
+ free(result);
+ return len;
+}
diff --git a/Plugins/spellchecker/hunspell/suggestmgr.hxx b/Plugins/spellchecker/hunspell/suggestmgr.hxx
new file mode 100644
index 0000000..5f043fd
--- /dev/null
+++ b/Plugins/spellchecker/hunspell/suggestmgr.hxx
@@ -0,0 +1,111 @@
+#ifndef _SUGGESTMGR_HXX_
+#define _SUGGESTMGR_HXX_
+
+#define MAXSWL 100
+#define MAXSWUTF8L (MAXSWL * 4)
+#define MAX_ROOTS 100
+#define MAX_WORDS 100
+#define MAX_GUESS 200
+#define MAXNGRAMSUGS 4
+#define MAXPHONSUGS 2
+#define MAXCOMPOUNDSUGS 3
+
+// timelimit: max ~1/4 sec (process time on Linux) for a time consuming function
+#define TIMELIMIT (CLOCKS_PER_SEC >> 2)
+#define MINTIMER 100
+#define MAXPLUSTIMER 100
+
+#define NGRAM_LONGER_WORSE (1 << 0)
+#define NGRAM_ANY_MISMATCH (1 << 1)
+#define NGRAM_LOWERING (1 << 2)
+#define NGRAM_WEIGHTED (1 << 3)
+
+#include "hunvisapi.h"
+
+#include "atypes.hxx"
+#include "affixmgr.hxx"
+#include "hashmgr.hxx"
+#include "langnum.hxx"
+#include <time.h>
+
+enum { LCS_UP, LCS_LEFT, LCS_UPLEFT };
+
+class LIBHUNSPELL_DLL_EXPORTED SuggestMgr
+{
+ char * ckey;
+ int ckeyl;
+ w_char * ckey_utf;
+
+ char * ctry;
+ int ctryl;
+ w_char * ctry_utf;
+
+ AffixMgr* pAMgr;
+ int maxSug;
+ struct cs_info * csconv;
+ int utf8;
+ int langnum;
+ int nosplitsugs;
+ int maxngramsugs;
+ int maxcpdsugs;
+ int complexprefixes;
+
+
+public:
+ SuggestMgr(const char * tryme, int maxn, AffixMgr *aptr);
+ ~SuggestMgr();
+
+ int suggest(char*** slst, const char * word, int nsug, int * onlycmpdsug);
+ int ngsuggest(char ** wlst, char * word, int ns, HashMgr** pHMgr, int md);
+ int suggest_auto(char*** slst, const char * word, int nsug);
+ int suggest_stems(char*** slst, const char * word, int nsug);
+ int suggest_pos_stems(char*** slst, const char * word, int nsug);
+
+ char * suggest_morph(const char * word);
+ char * suggest_gen(char ** pl, int pln, char * pattern);
+ char * suggest_morph_for_spelling_error(const char * word);
+
+private:
+ int testsug(char** wlst, const char * candidate, int wl, int ns, int cpdsuggest,
+ int * timer, clock_t * timelimit);
+ int checkword(const char *, int, int, int *, clock_t *);
+ int check_forbidden(const char *, int);
+
+ int capchars(char **, const char *, int, int);
+ int replchars(char**, const char *, int, int);
+ int doubletwochars(char**, const char *, int, int);
+ int forgotchar(char **, const char *, int, int);
+ int swapchar(char **, const char *, int, int);
+ int longswapchar(char **, const char *, int, int);
+ int movechar(char **, const char *, int, int);
+ int extrachar(char **, const char *, int, int);
+ int badcharkey(char **, const char *, int, int);
+ int badchar(char **, const char *, int, int);
+ int twowords(char **, const char *, int, int);
+ int fixstems(char **, const char *, int);
+
+ int capchars_utf(char **, const w_char *, int wl, int, int);
+ int doubletwochars_utf(char**, const w_char *, int wl, int, int);
+ int forgotchar_utf(char**, const w_char *, int wl, int, int);
+ int extrachar_utf(char**, const w_char *, int wl, int, int);
+ int badcharkey_utf(char **, const w_char *, int wl, int, int);
+ int badchar_utf(char **, const w_char *, int wl, int, int);
+ int swapchar_utf(char **, const w_char *, int wl, int, int);
+ int longswapchar_utf(char **, const w_char *, int, int, int);
+ int movechar_utf(char **, const w_char *, int, int, int);
+
+ int mapchars(char**, const char *, int, int);
+ int map_related(const char *, char *, int, int, char ** wlst, int, int, const mapentry*, int, int *, clock_t *);
+ int ngram(int n, char * s1, const char * s2, int opt);
+ int mystrlen(const char * word);
+ int leftcommonsubstring(char * s1, const char * s2);
+ int commoncharacterpositions(char * s1, const char * s2, int * is_swap);
+ void bubblesort( char ** rwd, char ** rwd2, int * rsc, int n);
+ void lcs(const char * s, const char * s2, int * l1, int * l2, char ** result);
+ int lcslen(const char * s, const char* s2);
+ char * suggest_hentry_gen(hentry * rv, char * pattern);
+
+};
+
+#endif
+
diff --git a/Plugins/spellchecker/hunspell/utf_info.cxx b/Plugins/spellchecker/hunspell/utf_info.cxx
new file mode 100644
index 0000000..4a8e203
--- /dev/null
+++ b/Plugins/spellchecker/hunspell/utf_info.cxx
@@ -0,0 +1,19676 @@
+#include "csutil.hxx"
+/* fields: Unicode letter, toupper, tolower */
+static struct unicode_info utf_lst[] = {
+{ 0x0041, 0x0041, 0x0061 },
+{ 0x0042, 0x0042, 0x0062 },
+{ 0x0043, 0x0043, 0x0063 },
+{ 0x0044, 0x0044, 0x0064 },
+{ 0x0045, 0x0045, 0x0065 },
+{ 0x0046, 0x0046, 0x0066 },
+{ 0x0047, 0x0047, 0x0067 },
+{ 0x0048, 0x0048, 0x0068 },
+{ 0x0049, 0x0049, 0x0069 },
+{ 0x004A, 0x004A, 0x006A },
+{ 0x004B, 0x004B, 0x006B },
+{ 0x004C, 0x004C, 0x006C },
+{ 0x004D, 0x004D, 0x006D },
+{ 0x004E, 0x004E, 0x006E },
+{ 0x004F, 0x004F, 0x006F },
+{ 0x0050, 0x0050, 0x0070 },
+{ 0x0051, 0x0051, 0x0071 },
+{ 0x0052, 0x0052, 0x0072 },
+{ 0x0053, 0x0053, 0x0073 },
+{ 0x0054, 0x0054, 0x0074 },
+{ 0x0055, 0x0055, 0x0075 },
+{ 0x0056, 0x0056, 0x0076 },
+{ 0x0057, 0x0057, 0x0077 },
+{ 0x0058, 0x0058, 0x0078 },
+{ 0x0059, 0x0059, 0x0079 },
+{ 0x005A, 0x005A, 0x007A },
+{ 0x0061, 0x0041, 0x0061 },
+{ 0x0062, 0x0042, 0x0062 },
+{ 0x0063, 0x0043, 0x0063 },
+{ 0x0064, 0x0044, 0x0064 },
+{ 0x0065, 0x0045, 0x0065 },
+{ 0x0066, 0x0046, 0x0066 },
+{ 0x0067, 0x0047, 0x0067 },
+{ 0x0068, 0x0048, 0x0068 },
+{ 0x0069, 0x0049, 0x0069 },
+{ 0x006A, 0x004A, 0x006A },
+{ 0x006B, 0x004B, 0x006B },
+{ 0x006C, 0x004C, 0x006C },
+{ 0x006D, 0x004D, 0x006D },
+{ 0x006E, 0x004E, 0x006E },
+{ 0x006F, 0x004F, 0x006F },
+{ 0x0070, 0x0050, 0x0070 },
+{ 0x0071, 0x0051, 0x0071 },
+{ 0x0072, 0x0052, 0x0072 },
+{ 0x0073, 0x0053, 0x0073 },
+{ 0x0074, 0x0054, 0x0074 },
+{ 0x0075, 0x0055, 0x0075 },
+{ 0x0076, 0x0056, 0x0076 },
+{ 0x0077, 0x0057, 0x0077 },
+{ 0x0078, 0x0058, 0x0078 },
+{ 0x0079, 0x0059, 0x0079 },
+{ 0x007A, 0x005A, 0x007A },
+{ 0x00AA, 0x00AA, 0x00AA },
+{ 0x00B5, 0x039C, 0x00B5 },
+{ 0x00BA, 0x00BA, 0x00BA },
+{ 0x00C0, 0x00C0, 0x00E0 },
+{ 0x00C1, 0x00C1, 0x00E1 },
+{ 0x00C2, 0x00C2, 0x00E2 },
+{ 0x00C3, 0x00C3, 0x00E3 },
+{ 0x00C4, 0x00C4, 0x00E4 },
+{ 0x00C5, 0x00C5, 0x00E5 },
+{ 0x00C6, 0x00C6, 0x00E6 },
+{ 0x00C7, 0x00C7, 0x00E7 },
+{ 0x00C8, 0x00C8, 0x00E8 },
+{ 0x00C9, 0x00C9, 0x00E9 },
+{ 0x00CA, 0x00CA, 0x00EA },
+{ 0x00CB, 0x00CB, 0x00EB },
+{ 0x00CC, 0x00CC, 0x00EC },
+{ 0x00CD, 0x00CD, 0x00ED },
+{ 0x00CE, 0x00CE, 0x00EE },
+{ 0x00CF, 0x00CF, 0x00EF },
+{ 0x00D0, 0x00D0, 0x00F0 },
+{ 0x00D1, 0x00D1, 0x00F1 },
+{ 0x00D2, 0x00D2, 0x00F2 },
+{ 0x00D3, 0x00D3, 0x00F3 },
+{ 0x00D4, 0x00D4, 0x00F4 },
+{ 0x00D5, 0x00D5, 0x00F5 },
+{ 0x00D6, 0x00D6, 0x00F6 },
+{ 0x00D8, 0x00D8, 0x00F8 },
+{ 0x00D9, 0x00D9, 0x00F9 },
+{ 0x00DA, 0x00DA, 0x00FA },
+{ 0x00DB, 0x00DB, 0x00FB },
+{ 0x00DC, 0x00DC, 0x00FC },
+{ 0x00DD, 0x00DD, 0x00FD },
+{ 0x00DE, 0x00DE, 0x00FE },
+{ 0x00DF, 0x00DF, 0x00DF },
+{ 0x00E0, 0x00C0, 0x00E0 },
+{ 0x00E1, 0x00C1, 0x00E1 },
+{ 0x00E2, 0x00C2, 0x00E2 },
+{ 0x00E3, 0x00C3, 0x00E3 },
+{ 0x00E4, 0x00C4, 0x00E4 },
+{ 0x00E5, 0x00C5, 0x00E5 },
+{ 0x00E6, 0x00C6, 0x00E6 },
+{ 0x00E7, 0x00C7, 0x00E7 },
+{ 0x00E8, 0x00C8, 0x00E8 },
+{ 0x00E9, 0x00C9, 0x00E9 },
+{ 0x00EA, 0x00CA, 0x00EA },
+{ 0x00EB, 0x00CB, 0x00EB },
+{ 0x00EC, 0x00CC, 0x00EC },
+{ 0x00ED, 0x00CD, 0x00ED },
+{ 0x00EE, 0x00CE, 0x00EE },
+{ 0x00EF, 0x00CF, 0x00EF },
+{ 0x00F0, 0x00D0, 0x00F0 },
+{ 0x00F1, 0x00D1, 0x00F1 },
+{ 0x00F2, 0x00D2, 0x00F2 },
+{ 0x00F3, 0x00D3, 0x00F3 },
+{ 0x00F4, 0x00D4, 0x00F4 },
+{ 0x00F5, 0x00D5, 0x00F5 },
+{ 0x00F6, 0x00D6, 0x00F6 },
+{ 0x00F8, 0x00D8, 0x00F8 },
+{ 0x00F9, 0x00D9, 0x00F9 },
+{ 0x00FA, 0x00DA, 0x00FA },
+{ 0x00FB, 0x00DB, 0x00FB },
+{ 0x00FC, 0x00DC, 0x00FC },
+{ 0x00FD, 0x00DD, 0x00FD },
+{ 0x00FE, 0x00DE, 0x00FE },
+{ 0x00FF, 0x0178, 0x00FF },
+{ 0x0100, 0x0100, 0x0101 },
+{ 0x0101, 0x0100, 0x0101 },
+{ 0x0102, 0x0102, 0x0103 },
+{ 0x0103, 0x0102, 0x0103 },
+{ 0x0104, 0x0104, 0x0105 },
+{ 0x0105, 0x0104, 0x0105 },
+{ 0x0106, 0x0106, 0x0107 },
+{ 0x0107, 0x0106, 0x0107 },
+{ 0x0108, 0x0108, 0x0109 },
+{ 0x0109, 0x0108, 0x0109 },
+{ 0x010A, 0x010A, 0x010B },
+{ 0x010B, 0x010A, 0x010B },
+{ 0x010C, 0x010C, 0x010D },
+{ 0x010D, 0x010C, 0x010D },
+{ 0x010E, 0x010E, 0x010F },
+{ 0x010F, 0x010E, 0x010F },
+{ 0x0110, 0x0110, 0x0111 },
+{ 0x0111, 0x0110, 0x0111 },
+{ 0x0112, 0x0112, 0x0113 },
+{ 0x0113, 0x0112, 0x0113 },
+{ 0x0114, 0x0114, 0x0115 },
+{ 0x0115, 0x0114, 0x0115 },
+{ 0x0116, 0x0116, 0x0117 },
+{ 0x0117, 0x0116, 0x0117 },
+{ 0x0118, 0x0118, 0x0119 },
+{ 0x0119, 0x0118, 0x0119 },
+{ 0x011A, 0x011A, 0x011B },
+{ 0x011B, 0x011A, 0x011B },
+{ 0x011C, 0x011C, 0x011D },
+{ 0x011D, 0x011C, 0x011D },
+{ 0x011E, 0x011E, 0x011F },
+{ 0x011F, 0x011E, 0x011F },
+{ 0x0120, 0x0120, 0x0121 },
+{ 0x0121, 0x0120, 0x0121 },
+{ 0x0122, 0x0122, 0x0123 },
+{ 0x0123, 0x0122, 0x0123 },
+{ 0x0124, 0x0124, 0x0125 },
+{ 0x0125, 0x0124, 0x0125 },
+{ 0x0126, 0x0126, 0x0127 },
+{ 0x0127, 0x0126, 0x0127 },
+{ 0x0128, 0x0128, 0x0129 },
+{ 0x0129, 0x0128, 0x0129 },
+{ 0x012A, 0x012A, 0x012B },
+{ 0x012B, 0x012A, 0x012B },
+{ 0x012C, 0x012C, 0x012D },
+{ 0x012D, 0x012C, 0x012D },
+{ 0x012E, 0x012E, 0x012F },
+{ 0x012F, 0x012E, 0x012F },
+{ 0x0130, 0x0130, 0x0069 },
+{ 0x0131, 0x0049, 0x0131 },
+{ 0x0132, 0x0132, 0x0133 },
+{ 0x0133, 0x0132, 0x0133 },
+{ 0x0134, 0x0134, 0x0135 },
+{ 0x0135, 0x0134, 0x0135 },
+{ 0x0136, 0x0136, 0x0137 },
+{ 0x0137, 0x0136, 0x0137 },
+{ 0x0138, 0x0138, 0x0138 },
+{ 0x0139, 0x0139, 0x013A },
+{ 0x013A, 0x0139, 0x013A },
+{ 0x013B, 0x013B, 0x013C },
+{ 0x013C, 0x013B, 0x013C },
+{ 0x013D, 0x013D, 0x013E },
+{ 0x013E, 0x013D, 0x013E },
+{ 0x013F, 0x013F, 0x0140 },
+{ 0x0140, 0x013F, 0x0140 },
+{ 0x0141, 0x0141, 0x0142 },
+{ 0x0142, 0x0141, 0x0142 },
+{ 0x0143, 0x0143, 0x0144 },
+{ 0x0144, 0x0143, 0x0144 },
+{ 0x0145, 0x0145, 0x0146 },
+{ 0x0146, 0x0145, 0x0146 },
+{ 0x0147, 0x0147, 0x0148 },
+{ 0x0148, 0x0147, 0x0148 },
+{ 0x0149, 0x0149, 0x0149 },
+{ 0x014A, 0x014A, 0x014B },
+{ 0x014B, 0x014A, 0x014B },
+{ 0x014C, 0x014C, 0x014D },
+{ 0x014D, 0x014C, 0x014D },
+{ 0x014E, 0x014E, 0x014F },
+{ 0x014F, 0x014E, 0x014F },
+{ 0x0150, 0x0150, 0x0151 },
+{ 0x0151, 0x0150, 0x0151 },
+{ 0x0152, 0x0152, 0x0153 },
+{ 0x0153, 0x0152, 0x0153 },
+{ 0x0154, 0x0154, 0x0155 },
+{ 0x0155, 0x0154, 0x0155 },
+{ 0x0156, 0x0156, 0x0157 },
+{ 0x0157, 0x0156, 0x0157 },
+{ 0x0158, 0x0158, 0x0159 },
+{ 0x0159, 0x0158, 0x0159 },
+{ 0x015A, 0x015A, 0x015B },
+{ 0x015B, 0x015A, 0x015B },
+{ 0x015C, 0x015C, 0x015D },
+{ 0x015D, 0x015C, 0x015D },
+{ 0x015E, 0x015E, 0x015F },
+{ 0x015F, 0x015E, 0x015F },
+{ 0x0160, 0x0160, 0x0161 },
+{ 0x0161, 0x0160, 0x0161 },
+{ 0x0162, 0x0162, 0x0163 },
+{ 0x0163, 0x0162, 0x0163 },
+{ 0x0164, 0x0164, 0x0165 },
+{ 0x0165, 0x0164, 0x0165 },
+{ 0x0166, 0x0166, 0x0167 },
+{ 0x0167, 0x0166, 0x0167 },
+{ 0x0168, 0x0168, 0x0169 },
+{ 0x0169, 0x0168, 0x0169 },
+{ 0x016A, 0x016A, 0x016B },
+{ 0x016B, 0x016A, 0x016B },
+{ 0x016C, 0x016C, 0x016D },
+{ 0x016D, 0x016C, 0x016D },
+{ 0x016E, 0x016E, 0x016F },
+{ 0x016F, 0x016E, 0x016F },
+{ 0x0170, 0x0170, 0x0171 },
+{ 0x0171, 0x0170, 0x0171 },
+{ 0x0172, 0x0172, 0x0173 },
+{ 0x0173, 0x0172, 0x0173 },
+{ 0x0174, 0x0174, 0x0175 },
+{ 0x0175, 0x0174, 0x0175 },
+{ 0x0176, 0x0176, 0x0177 },
+{ 0x0177, 0x0176, 0x0177 },
+{ 0x0178, 0x0178, 0x00FF },
+{ 0x0179, 0x0179, 0x017A },
+{ 0x017A, 0x0179, 0x017A },
+{ 0x017B, 0x017B, 0x017C },
+{ 0x017C, 0x017B, 0x017C },
+{ 0x017D, 0x017D, 0x017E },
+{ 0x017E, 0x017D, 0x017E },
+{ 0x017F, 0x0053, 0x017F },
+{ 0x0180, 0x0180, 0x0180 },
+{ 0x0181, 0x0181, 0x0253 },
+{ 0x0182, 0x0182, 0x0183 },
+{ 0x0183, 0x0182, 0x0183 },
+{ 0x0184, 0x0184, 0x0185 },
+{ 0x0185, 0x0184, 0x0185 },
+{ 0x0186, 0x0186, 0x0254 },
+{ 0x0187, 0x0187, 0x0188 },
+{ 0x0188, 0x0187, 0x0188 },
+{ 0x0189, 0x0189, 0x0256 },
+{ 0x018A, 0x018A, 0x0257 },
+{ 0x018B, 0x018B, 0x018C },
+{ 0x018C, 0x018B, 0x018C },
+{ 0x018D, 0x018D, 0x018D },
+{ 0x018E, 0x018E, 0x01DD },
+{ 0x018F, 0x018F, 0x0259 },
+{ 0x0190, 0x0190, 0x025B },
+{ 0x0191, 0x0191, 0x0192 },
+{ 0x0192, 0x0191, 0x0192 },
+{ 0x0193, 0x0193, 0x0260 },
+{ 0x0194, 0x0194, 0x0263 },
+{ 0x0195, 0x01F6, 0x0195 },
+{ 0x0196, 0x0196, 0x0269 },
+{ 0x0197, 0x0197, 0x0268 },
+{ 0x0198, 0x0198, 0x0199 },
+{ 0x0199, 0x0198, 0x0199 },
+{ 0x019A, 0x023D, 0x019A },
+{ 0x019B, 0x019B, 0x019B },
+{ 0x019C, 0x019C, 0x026F },
+{ 0x019D, 0x019D, 0x0272 },
+{ 0x019E, 0x0220, 0x019E },
+{ 0x019F, 0x019F, 0x0275 },
+{ 0x01A0, 0x01A0, 0x01A1 },
+{ 0x01A1, 0x01A0, 0x01A1 },
+{ 0x01A2, 0x01A2, 0x01A3 },
+{ 0x01A3, 0x01A2, 0x01A3 },
+{ 0x01A4, 0x01A4, 0x01A5 },
+{ 0x01A5, 0x01A4, 0x01A5 },
+{ 0x01A6, 0x01A6, 0x0280 },
+{ 0x01A7, 0x01A7, 0x01A8 },
+{ 0x01A8, 0x01A7, 0x01A8 },
+{ 0x01A9, 0x01A9, 0x0283 },
+{ 0x01AA, 0x01AA, 0x01AA },
+{ 0x01AB, 0x01AB, 0x01AB },
+{ 0x01AC, 0x01AC, 0x01AD },
+{ 0x01AD, 0x01AC, 0x01AD },
+{ 0x01AE, 0x01AE, 0x0288 },
+{ 0x01AF, 0x01AF, 0x01B0 },
+{ 0x01B0, 0x01AF, 0x01B0 },
+{ 0x01B1, 0x01B1, 0x028A },
+{ 0x01B2, 0x01B2, 0x028B },
+{ 0x01B3, 0x01B3, 0x01B4 },
+{ 0x01B4, 0x01B3, 0x01B4 },
+{ 0x01B5, 0x01B5, 0x01B6 },
+{ 0x01B6, 0x01B5, 0x01B6 },
+{ 0x01B7, 0x01B7, 0x0292 },
+{ 0x01B8, 0x01B8, 0x01B9 },
+{ 0x01B9, 0x01B8, 0x01B9 },
+{ 0x01BA, 0x01BA, 0x01BA },
+{ 0x01BB, 0x01BB, 0x01BB },
+{ 0x01BC, 0x01BC, 0x01BD },
+{ 0x01BD, 0x01BC, 0x01BD },
+{ 0x01BE, 0x01BE, 0x01BE },
+{ 0x01BF, 0x01F7, 0x01BF },
+{ 0x01C0, 0x01C0, 0x01C0 },
+{ 0x01C1, 0x01C1, 0x01C1 },
+{ 0x01C2, 0x01C2, 0x01C2 },
+{ 0x01C3, 0x01C3, 0x01C3 },
+{ 0x01C4, 0x01C4, 0x01C6 },
+{ 0x01C5, 0x01C4, 0x01C6 },
+{ 0x01C6, 0x01C4, 0x01C6 },
+{ 0x01C7, 0x01C7, 0x01C9 },
+{ 0x01C8, 0x01C7, 0x01C9 },
+{ 0x01C9, 0x01C7, 0x01C9 },
+{ 0x01CA, 0x01CA, 0x01CC },
+{ 0x01CB, 0x01CA, 0x01CC },
+{ 0x01CC, 0x01CA, 0x01CC },
+{ 0x01CD, 0x01CD, 0x01CE },
+{ 0x01CE, 0x01CD, 0x01CE },
+{ 0x01CF, 0x01CF, 0x01D0 },
+{ 0x01D0, 0x01CF, 0x01D0 },
+{ 0x01D1, 0x01D1, 0x01D2 },
+{ 0x01D2, 0x01D1, 0x01D2 },
+{ 0x01D3, 0x01D3, 0x01D4 },
+{ 0x01D4, 0x01D3, 0x01D4 },
+{ 0x01D5, 0x01D5, 0x01D6 },
+{ 0x01D6, 0x01D5, 0x01D6 },
+{ 0x01D7, 0x01D7, 0x01D8 },
+{ 0x01D8, 0x01D7, 0x01D8 },
+{ 0x01D9, 0x01D9, 0x01DA },
+{ 0x01DA, 0x01D9, 0x01DA },
+{ 0x01DB, 0x01DB, 0x01DC },
+{ 0x01DC, 0x01DB, 0x01DC },
+{ 0x01DD, 0x018E, 0x01DD },
+{ 0x01DE, 0x01DE, 0x01DF },
+{ 0x01DF, 0x01DE, 0x01DF },
+{ 0x01E0, 0x01E0, 0x01E1 },
+{ 0x01E1, 0x01E0, 0x01E1 },
+{ 0x01E2, 0x01E2, 0x01E3 },
+{ 0x01E3, 0x01E2, 0x01E3 },
+{ 0x01E4, 0x01E4, 0x01E5 },
+{ 0x01E5, 0x01E4, 0x01E5 },
+{ 0x01E6, 0x01E6, 0x01E7 },
+{ 0x01E7, 0x01E6, 0x01E7 },
+{ 0x01E8, 0x01E8, 0x01E9 },
+{ 0x01E9, 0x01E8, 0x01E9 },
+{ 0x01EA, 0x01EA, 0x01EB },
+{ 0x01EB, 0x01EA, 0x01EB },
+{ 0x01EC, 0x01EC, 0x01ED },
+{ 0x01ED, 0x01EC, 0x01ED },
+{ 0x01EE, 0x01EE, 0x01EF },
+{ 0x01EF, 0x01EE, 0x01EF },
+{ 0x01F0, 0x01F0, 0x01F0 },
+{ 0x01F1, 0x01F1, 0x01F3 },
+{ 0x01F2, 0x01F1, 0x01F3 },
+{ 0x01F3, 0x01F1, 0x01F3 },
+{ 0x01F4, 0x01F4, 0x01F5 },
+{ 0x01F5, 0x01F4, 0x01F5 },
+{ 0x01F6, 0x01F6, 0x0195 },
+{ 0x01F7, 0x01F7, 0x01BF },
+{ 0x01F8, 0x01F8, 0x01F9 },
+{ 0x01F9, 0x01F8, 0x01F9 },
+{ 0x01FA, 0x01FA, 0x01FB },
+{ 0x01FB, 0x01FA, 0x01FB },
+{ 0x01FC, 0x01FC, 0x01FD },
+{ 0x01FD, 0x01FC, 0x01FD },
+{ 0x01FE, 0x01FE, 0x01FF },
+{ 0x01FF, 0x01FE, 0x01FF },
+{ 0x0200, 0x0200, 0x0201 },
+{ 0x0201, 0x0200, 0x0201 },
+{ 0x0202, 0x0202, 0x0203 },
+{ 0x0203, 0x0202, 0x0203 },
+{ 0x0204, 0x0204, 0x0205 },
+{ 0x0205, 0x0204, 0x0205 },
+{ 0x0206, 0x0206, 0x0207 },
+{ 0x0207, 0x0206, 0x0207 },
+{ 0x0208, 0x0208, 0x0209 },
+{ 0x0209, 0x0208, 0x0209 },
+{ 0x020A, 0x020A, 0x020B },
+{ 0x020B, 0x020A, 0x020B },
+{ 0x020C, 0x020C, 0x020D },
+{ 0x020D, 0x020C, 0x020D },
+{ 0x020E, 0x020E, 0x020F },
+{ 0x020F, 0x020E, 0x020F },
+{ 0x0210, 0x0210, 0x0211 },
+{ 0x0211, 0x0210, 0x0211 },
+{ 0x0212, 0x0212, 0x0213 },
+{ 0x0213, 0x0212, 0x0213 },
+{ 0x0214, 0x0214, 0x0215 },
+{ 0x0215, 0x0214, 0x0215 },
+{ 0x0216, 0x0216, 0x0217 },
+{ 0x0217, 0x0216, 0x0217 },
+{ 0x0218, 0x0218, 0x0219 },
+{ 0x0219, 0x0218, 0x0219 },
+{ 0x021A, 0x021A, 0x021B },
+{ 0x021B, 0x021A, 0x021B },
+{ 0x021C, 0x021C, 0x021D },
+{ 0x021D, 0x021C, 0x021D },
+{ 0x021E, 0x021E, 0x021F },
+{ 0x021F, 0x021E, 0x021F },
+{ 0x0220, 0x0220, 0x019E },
+{ 0x0221, 0x0221, 0x0221 },
+{ 0x0222, 0x0222, 0x0223 },
+{ 0x0223, 0x0222, 0x0223 },
+{ 0x0224, 0x0224, 0x0225 },
+{ 0x0225, 0x0224, 0x0225 },
+{ 0x0226, 0x0226, 0x0227 },
+{ 0x0227, 0x0226, 0x0227 },
+{ 0x0228, 0x0228, 0x0229 },
+{ 0x0229, 0x0228, 0x0229 },
+{ 0x022A, 0x022A, 0x022B },
+{ 0x022B, 0x022A, 0x022B },
+{ 0x022C, 0x022C, 0x022D },
+{ 0x022D, 0x022C, 0x022D },
+{ 0x022E, 0x022E, 0x022F },
+{ 0x022F, 0x022E, 0x022F },
+{ 0x0230, 0x0230, 0x0231 },
+{ 0x0231, 0x0230, 0x0231 },
+{ 0x0232, 0x0232, 0x0233 },
+{ 0x0233, 0x0232, 0x0233 },
+{ 0x0234, 0x0234, 0x0234 },
+{ 0x0235, 0x0235, 0x0235 },
+{ 0x0236, 0x0236, 0x0236 },
+{ 0x0237, 0x0237, 0x0237 },
+{ 0x0238, 0x0238, 0x0238 },
+{ 0x0239, 0x0239, 0x0239 },
+{ 0x023A, 0x023A, 0x023A },
+{ 0x023B, 0x023B, 0x023C },
+{ 0x023C, 0x023B, 0x023C },
+{ 0x023D, 0x023D, 0x019A },
+{ 0x023E, 0x023E, 0x023E },
+{ 0x023F, 0x023F, 0x023F },
+{ 0x0240, 0x0240, 0x0240 },
+{ 0x0241, 0x0241, 0x0294 },
+{ 0x0250, 0x0250, 0x0250 },
+{ 0x0251, 0x0251, 0x0251 },
+{ 0x0252, 0x0252, 0x0252 },
+{ 0x0253, 0x0181, 0x0253 },
+{ 0x0254, 0x0186, 0x0254 },
+{ 0x0255, 0x0255, 0x0255 },
+{ 0x0256, 0x0189, 0x0256 },
+{ 0x0257, 0x018A, 0x0257 },
+{ 0x0258, 0x0258, 0x0258 },
+{ 0x0259, 0x018F, 0x0259 },
+{ 0x025A, 0x025A, 0x025A },
+{ 0x025B, 0x0190, 0x025B },
+{ 0x025C, 0x025C, 0x025C },
+{ 0x025D, 0x025D, 0x025D },
+{ 0x025E, 0x025E, 0x025E },
+{ 0x025F, 0x025F, 0x025F },
+{ 0x0260, 0x0193, 0x0260 },
+{ 0x0261, 0x0261, 0x0261 },
+{ 0x0262, 0x0262, 0x0262 },
+{ 0x0263, 0x0194, 0x0263 },
+{ 0x0264, 0x0264, 0x0264 },
+{ 0x0265, 0x0265, 0x0265 },
+{ 0x0266, 0x0266, 0x0266 },
+{ 0x0267, 0x0267, 0x0267 },
+{ 0x0268, 0x0197, 0x0268 },
+{ 0x0269, 0x0196, 0x0269 },
+{ 0x026A, 0x026A, 0x026A },
+{ 0x026B, 0x026B, 0x026B },
+{ 0x026C, 0x026C, 0x026C },
+{ 0x026D, 0x026D, 0x026D },
+{ 0x026E, 0x026E, 0x026E },
+{ 0x026F, 0x019C, 0x026F },
+{ 0x0270, 0x0270, 0x0270 },
+{ 0x0271, 0x0271, 0x0271 },
+{ 0x0272, 0x019D, 0x0272 },
+{ 0x0273, 0x0273, 0x0273 },
+{ 0x0274, 0x0274, 0x0274 },
+{ 0x0275, 0x019F, 0x0275 },
+{ 0x0276, 0x0276, 0x0276 },
+{ 0x0277, 0x0277, 0x0277 },
+{ 0x0278, 0x0278, 0x0278 },
+{ 0x0279, 0x0279, 0x0279 },
+{ 0x027A, 0x027A, 0x027A },
+{ 0x027B, 0x027B, 0x027B },
+{ 0x027C, 0x027C, 0x027C },
+{ 0x027D, 0x027D, 0x027D },
+{ 0x027E, 0x027E, 0x027E },
+{ 0x027F, 0x027F, 0x027F },
+{ 0x0280, 0x01A6, 0x0280 },
+{ 0x0281, 0x0281, 0x0281 },
+{ 0x0282, 0x0282, 0x0282 },
+{ 0x0283, 0x01A9, 0x0283 },
+{ 0x0284, 0x0284, 0x0284 },
+{ 0x0285, 0x0285, 0x0285 },
+{ 0x0286, 0x0286, 0x0286 },
+{ 0x0287, 0x0287, 0x0287 },
+{ 0x0288, 0x01AE, 0x0288 },
+{ 0x0289, 0x0289, 0x0289 },
+{ 0x028A, 0x01B1, 0x028A },
+{ 0x028B, 0x01B2, 0x028B },
+{ 0x028C, 0x028C, 0x028C },
+{ 0x028D, 0x028D, 0x028D },
+{ 0x028E, 0x028E, 0x028E },
+{ 0x028F, 0x028F, 0x028F },
+{ 0x0290, 0x0290, 0x0290 },
+{ 0x0291, 0x0291, 0x0291 },
+{ 0x0292, 0x01B7, 0x0292 },
+{ 0x0293, 0x0293, 0x0293 },
+{ 0x0294, 0x0241, 0x0294 },
+{ 0x0295, 0x0295, 0x0295 },
+{ 0x0296, 0x0296, 0x0296 },
+{ 0x0297, 0x0297, 0x0297 },
+{ 0x0298, 0x0298, 0x0298 },
+{ 0x0299, 0x0299, 0x0299 },
+{ 0x029A, 0x029A, 0x029A },
+{ 0x029B, 0x029B, 0x029B },
+{ 0x029C, 0x029C, 0x029C },
+{ 0x029D, 0x029D, 0x029D },
+{ 0x029E, 0x029E, 0x029E },
+{ 0x029F, 0x029F, 0x029F },
+{ 0x02A0, 0x02A0, 0x02A0 },
+{ 0x02A1, 0x02A1, 0x02A1 },
+{ 0x02A2, 0x02A2, 0x02A2 },
+{ 0x02A3, 0x02A3, 0x02A3 },
+{ 0x02A4, 0x02A4, 0x02A4 },
+{ 0x02A5, 0x02A5, 0x02A5 },
+{ 0x02A6, 0x02A6, 0x02A6 },
+{ 0x02A7, 0x02A7, 0x02A7 },
+{ 0x02A8, 0x02A8, 0x02A8 },
+{ 0x02A9, 0x02A9, 0x02A9 },
+{ 0x02AA, 0x02AA, 0x02AA },
+{ 0x02AB, 0x02AB, 0x02AB },
+{ 0x02AC, 0x02AC, 0x02AC },
+{ 0x02AD, 0x02AD, 0x02AD },
+{ 0x02AE, 0x02AE, 0x02AE },
+{ 0x02AF, 0x02AF, 0x02AF },
+{ 0x02B0, 0x02B0, 0x02B0 },
+{ 0x02B1, 0x02B1, 0x02B1 },
+{ 0x02B2, 0x02B2, 0x02B2 },
+{ 0x02B3, 0x02B3, 0x02B3 },
+{ 0x02B4, 0x02B4, 0x02B4 },
+{ 0x02B5, 0x02B5, 0x02B5 },
+{ 0x02B6, 0x02B6, 0x02B6 },
+{ 0x02B7, 0x02B7, 0x02B7 },
+{ 0x02B8, 0x02B8, 0x02B8 },
+{ 0x02B9, 0x02B9, 0x02B9 },
+{ 0x02BA, 0x02BA, 0x02BA },
+{ 0x02BB, 0x02BB, 0x02BB },
+{ 0x02BC, 0x02BC, 0x02BC },
+{ 0x02BD, 0x02BD, 0x02BD },
+{ 0x02BE, 0x02BE, 0x02BE },
+{ 0x02BF, 0x02BF, 0x02BF },
+{ 0x02C0, 0x02C0, 0x02C0 },
+{ 0x02C1, 0x02C1, 0x02C1 },
+{ 0x02C6, 0x02C6, 0x02C6 },
+{ 0x02C7, 0x02C7, 0x02C7 },
+{ 0x02C8, 0x02C8, 0x02C8 },
+{ 0x02C9, 0x02C9, 0x02C9 },
+{ 0x02CA, 0x02CA, 0x02CA },
+{ 0x02CB, 0x02CB, 0x02CB },
+{ 0x02CC, 0x02CC, 0x02CC },
+{ 0x02CD, 0x02CD, 0x02CD },
+{ 0x02CE, 0x02CE, 0x02CE },
+{ 0x02CF, 0x02CF, 0x02CF },
+{ 0x02D0, 0x02D0, 0x02D0 },
+{ 0x02D1, 0x02D1, 0x02D1 },
+{ 0x02E0, 0x02E0, 0x02E0 },
+{ 0x02E1, 0x02E1, 0x02E1 },
+{ 0x02E2, 0x02E2, 0x02E2 },
+{ 0x02E3, 0x02E3, 0x02E3 },
+{ 0x02E4, 0x02E4, 0x02E4 },
+{ 0x02EE, 0x02EE, 0x02EE },
+{ 0x0300, 0x0300, 0x0300 },
+{ 0x0301, 0x0301, 0x0301 },
+{ 0x0302, 0x0302, 0x0302 },
+{ 0x0303, 0x0303, 0x0303 },
+{ 0x0304, 0x0304, 0x0304 },
+{ 0x0305, 0x0305, 0x0305 },
+{ 0x0306, 0x0306, 0x0306 },
+{ 0x0307, 0x0307, 0x0307 },
+{ 0x0308, 0x0308, 0x0308 },
+{ 0x0309, 0x0309, 0x0309 },
+{ 0x030A, 0x030A, 0x030A },
+{ 0x030B, 0x030B, 0x030B },
+{ 0x030C, 0x030C, 0x030C },
+{ 0x030D, 0x030D, 0x030D },
+{ 0x030E, 0x030E, 0x030E },
+{ 0x030F, 0x030F, 0x030F },
+{ 0x0310, 0x0310, 0x0310 },
+{ 0x0311, 0x0311, 0x0311 },
+{ 0x0312, 0x0312, 0x0312 },
+{ 0x0313, 0x0313, 0x0313 },
+{ 0x0314, 0x0314, 0x0314 },
+{ 0x0315, 0x0315, 0x0315 },
+{ 0x0316, 0x0316, 0x0316 },
+{ 0x0317, 0x0317, 0x0317 },
+{ 0x0318, 0x0318, 0x0318 },
+{ 0x0319, 0x0319, 0x0319 },
+{ 0x031A, 0x031A, 0x031A },
+{ 0x031B, 0x031B, 0x031B },
+{ 0x031C, 0x031C, 0x031C },
+{ 0x031D, 0x031D, 0x031D },
+{ 0x031E, 0x031E, 0x031E },
+{ 0x031F, 0x031F, 0x031F },
+{ 0x0320, 0x0320, 0x0320 },
+{ 0x0321, 0x0321, 0x0321 },
+{ 0x0322, 0x0322, 0x0322 },
+{ 0x0323, 0x0323, 0x0323 },
+{ 0x0324, 0x0324, 0x0324 },
+{ 0x0325, 0x0325, 0x0325 },
+{ 0x0326, 0x0326, 0x0326 },
+{ 0x0327, 0x0327, 0x0327 },
+{ 0x0328, 0x0328, 0x0328 },
+{ 0x0329, 0x0329, 0x0329 },
+{ 0x032A, 0x032A, 0x032A },
+{ 0x032B, 0x032B, 0x032B },
+{ 0x032C, 0x032C, 0x032C },
+{ 0x032D, 0x032D, 0x032D },
+{ 0x032E, 0x032E, 0x032E },
+{ 0x032F, 0x032F, 0x032F },
+{ 0x0330, 0x0330, 0x0330 },
+{ 0x0331, 0x0331, 0x0331 },
+{ 0x0332, 0x0332, 0x0332 },
+{ 0x0333, 0x0333, 0x0333 },
+{ 0x0334, 0x0334, 0x0334 },
+{ 0x0335, 0x0335, 0x0335 },
+{ 0x0336, 0x0336, 0x0336 },
+{ 0x0337, 0x0337, 0x0337 },
+{ 0x0338, 0x0338, 0x0338 },
+{ 0x0339, 0x0339, 0x0339 },
+{ 0x033A, 0x033A, 0x033A },
+{ 0x033B, 0x033B, 0x033B },
+{ 0x033C, 0x033C, 0x033C },
+{ 0x033D, 0x033D, 0x033D },
+{ 0x033E, 0x033E, 0x033E },
+{ 0x033F, 0x033F, 0x033F },
+{ 0x0340, 0x0340, 0x0340 },
+{ 0x0341, 0x0341, 0x0341 },
+{ 0x0342, 0x0342, 0x0342 },
+{ 0x0343, 0x0343, 0x0343 },
+{ 0x0344, 0x0344, 0x0344 },
+{ 0x0345, 0x0399, 0x0345 },
+{ 0x0346, 0x0346, 0x0346 },
+{ 0x0347, 0x0347, 0x0347 },
+{ 0x0348, 0x0348, 0x0348 },
+{ 0x0349, 0x0349, 0x0349 },
+{ 0x034A, 0x034A, 0x034A },
+{ 0x034B, 0x034B, 0x034B },
+{ 0x034C, 0x034C, 0x034C },
+{ 0x034D, 0x034D, 0x034D },
+{ 0x034E, 0x034E, 0x034E },
+{ 0x034F, 0x034F, 0x034F },
+{ 0x0350, 0x0350, 0x0350 },
+{ 0x0351, 0x0351, 0x0351 },
+{ 0x0352, 0x0352, 0x0352 },
+{ 0x0353, 0x0353, 0x0353 },
+{ 0x0354, 0x0354, 0x0354 },
+{ 0x0355, 0x0355, 0x0355 },
+{ 0x0356, 0x0356, 0x0356 },
+{ 0x0357, 0x0357, 0x0357 },
+{ 0x0358, 0x0358, 0x0358 },
+{ 0x0359, 0x0359, 0x0359 },
+{ 0x035A, 0x035A, 0x035A },
+{ 0x035B, 0x035B, 0x035B },
+{ 0x035C, 0x035C, 0x035C },
+{ 0x035D, 0x035D, 0x035D },
+{ 0x035E, 0x035E, 0x035E },
+{ 0x035F, 0x035F, 0x035F },
+{ 0x0360, 0x0360, 0x0360 },
+{ 0x0361, 0x0361, 0x0361 },
+{ 0x0362, 0x0362, 0x0362 },
+{ 0x0363, 0x0363, 0x0363 },
+{ 0x0364, 0x0364, 0x0364 },
+{ 0x0365, 0x0365, 0x0365 },
+{ 0x0366, 0x0366, 0x0366 },
+{ 0x0367, 0x0367, 0x0367 },
+{ 0x0368, 0x0368, 0x0368 },
+{ 0x0369, 0x0369, 0x0369 },
+{ 0x036A, 0x036A, 0x036A },
+{ 0x036B, 0x036B, 0x036B },
+{ 0x036C, 0x036C, 0x036C },
+{ 0x036D, 0x036D, 0x036D },
+{ 0x036E, 0x036E, 0x036E },
+{ 0x036F, 0x036F, 0x036F },
+{ 0x037A, 0x037A, 0x037A },
+{ 0x0386, 0x0386, 0x03AC },
+{ 0x0388, 0x0388, 0x03AD },
+{ 0x0389, 0x0389, 0x03AE },
+{ 0x038A, 0x038A, 0x03AF },
+{ 0x038C, 0x038C, 0x03CC },
+{ 0x038E, 0x038E, 0x03CD },
+{ 0x038F, 0x038F, 0x03CE },
+{ 0x0390, 0x0390, 0x0390 },
+{ 0x0391, 0x0391, 0x03B1 },
+{ 0x0392, 0x0392, 0x03B2 },
+{ 0x0393, 0x0393, 0x03B3 },
+{ 0x0394, 0x0394, 0x03B4 },
+{ 0x0395, 0x0395, 0x03B5 },
+{ 0x0396, 0x0396, 0x03B6 },
+{ 0x0397, 0x0397, 0x03B7 },
+{ 0x0398, 0x0398, 0x03B8 },
+{ 0x0399, 0x0399, 0x03B9 },
+{ 0x039A, 0x039A, 0x03BA },
+{ 0x039B, 0x039B, 0x03BB },
+{ 0x039C, 0x039C, 0x03BC },
+{ 0x039D, 0x039D, 0x03BD },
+{ 0x039E, 0x039E, 0x03BE },
+{ 0x039F, 0x039F, 0x03BF },
+{ 0x03A0, 0x03A0, 0x03C0 },
+{ 0x03A1, 0x03A1, 0x03C1 },
+{ 0x03A3, 0x03A3, 0x03C3 },
+{ 0x03A4, 0x03A4, 0x03C4 },
+{ 0x03A5, 0x03A5, 0x03C5 },
+{ 0x03A6, 0x03A6, 0x03C6 },
+{ 0x03A7, 0x03A7, 0x03C7 },
+{ 0x03A8, 0x03A8, 0x03C8 },
+{ 0x03A9, 0x03A9, 0x03C9 },
+{ 0x03AA, 0x03AA, 0x03CA },
+{ 0x03AB, 0x03AB, 0x03CB },
+{ 0x03AC, 0x0386, 0x03AC },
+{ 0x03AD, 0x0388, 0x03AD },
+{ 0x03AE, 0x0389, 0x03AE },
+{ 0x03AF, 0x038A, 0x03AF },
+{ 0x03B0, 0x03B0, 0x03B0 },
+{ 0x03B1, 0x0391, 0x03B1 },
+{ 0x03B2, 0x0392, 0x03B2 },
+{ 0x03B3, 0x0393, 0x03B3 },
+{ 0x03B4, 0x0394, 0x03B4 },
+{ 0x03B5, 0x0395, 0x03B5 },
+{ 0x03B6, 0x0396, 0x03B6 },
+{ 0x03B7, 0x0397, 0x03B7 },
+{ 0x03B8, 0x0398, 0x03B8 },
+{ 0x03B9, 0x0399, 0x03B9 },
+{ 0x03BA, 0x039A, 0x03BA },
+{ 0x03BB, 0x039B, 0x03BB },
+{ 0x03BC, 0x039C, 0x03BC },
+{ 0x03BD, 0x039D, 0x03BD },
+{ 0x03BE, 0x039E, 0x03BE },
+{ 0x03BF, 0x039F, 0x03BF },
+{ 0x03C0, 0x03A0, 0x03C0 },
+{ 0x03C1, 0x03A1, 0x03C1 },
+{ 0x03C2, 0x03A3, 0x03C2 },
+{ 0x03C3, 0x03A3, 0x03C3 },
+{ 0x03C4, 0x03A4, 0x03C4 },
+{ 0x03C5, 0x03A5, 0x03C5 },
+{ 0x03C6, 0x03A6, 0x03C6 },
+{ 0x03C7, 0x03A7, 0x03C7 },
+{ 0x03C8, 0x03A8, 0x03C8 },
+{ 0x03C9, 0x03A9, 0x03C9 },
+{ 0x03CA, 0x03AA, 0x03CA },
+{ 0x03CB, 0x03AB, 0x03CB },
+{ 0x03CC, 0x038C, 0x03CC },
+{ 0x03CD, 0x038E, 0x03CD },
+{ 0x03CE, 0x038F, 0x03CE },
+{ 0x03D0, 0x0392, 0x03D0 },
+{ 0x03D1, 0x0398, 0x03D1 },
+{ 0x03D2, 0x03D2, 0x03D2 },
+{ 0x03D3, 0x03D3, 0x03D3 },
+{ 0x03D4, 0x03D4, 0x03D4 },
+{ 0x03D5, 0x03A6, 0x03D5 },
+{ 0x03D6, 0x03A0, 0x03D6 },
+{ 0x03D7, 0x03D7, 0x03D7 },
+{ 0x03D8, 0x03D8, 0x03D9 },
+{ 0x03D9, 0x03D8, 0x03D9 },
+{ 0x03DA, 0x03DA, 0x03DB },
+{ 0x03DB, 0x03DA, 0x03DB },
+{ 0x03DC, 0x03DC, 0x03DD },
+{ 0x03DD, 0x03DC, 0x03DD },
+{ 0x03DE, 0x03DE, 0x03DF },
+{ 0x03DF, 0x03DE, 0x03DF },
+{ 0x03E0, 0x03E0, 0x03E1 },
+{ 0x03E1, 0x03E0, 0x03E1 },
+{ 0x03E2, 0x03E2, 0x03E3 },
+{ 0x03E3, 0x03E2, 0x03E3 },
+{ 0x03E4, 0x03E4, 0x03E5 },
+{ 0x03E5, 0x03E4, 0x03E5 },
+{ 0x03E6, 0x03E6, 0x03E7 },
+{ 0x03E7, 0x03E6, 0x03E7 },
+{ 0x03E8, 0x03E8, 0x03E9 },
+{ 0x03E9, 0x03E8, 0x03E9 },
+{ 0x03EA, 0x03EA, 0x03EB },
+{ 0x03EB, 0x03EA, 0x03EB },
+{ 0x03EC, 0x03EC, 0x03ED },
+{ 0x03ED, 0x03EC, 0x03ED },
+{ 0x03EE, 0x03EE, 0x03EF },
+{ 0x03EF, 0x03EE, 0x03EF },
+{ 0x03F0, 0x039A, 0x03F0 },
+{ 0x03F1, 0x03A1, 0x03F1 },
+{ 0x03F2, 0x03F9, 0x03F2 },
+{ 0x03F3, 0x03F3, 0x03F3 },
+{ 0x03F4, 0x03F4, 0x03B8 },
+{ 0x03F5, 0x0395, 0x03F5 },
+{ 0x03F7, 0x03F7, 0x03F8 },
+{ 0x03F8, 0x03F7, 0x03F8 },
+{ 0x03F9, 0x03F9, 0x03F2 },
+{ 0x03FA, 0x03FA, 0x03FB },
+{ 0x03FB, 0x03FA, 0x03FB },
+{ 0x03FC, 0x03FC, 0x03FC },
+{ 0x03FD, 0x03FD, 0x03FD },
+{ 0x03FE, 0x03FE, 0x03FE },
+{ 0x03FF, 0x03FF, 0x03FF },
+{ 0x0400, 0x0400, 0x0450 },
+{ 0x0401, 0x0401, 0x0451 },
+{ 0x0402, 0x0402, 0x0452 },
+{ 0x0403, 0x0403, 0x0453 },
+{ 0x0404, 0x0404, 0x0454 },
+{ 0x0405, 0x0405, 0x0455 },
+{ 0x0406, 0x0406, 0x0456 },
+{ 0x0407, 0x0407, 0x0457 },
+{ 0x0408, 0x0408, 0x0458 },
+{ 0x0409, 0x0409, 0x0459 },
+{ 0x040A, 0x040A, 0x045A },
+{ 0x040B, 0x040B, 0x045B },
+{ 0x040C, 0x040C, 0x045C },
+{ 0x040D, 0x040D, 0x045D },
+{ 0x040E, 0x040E, 0x045E },
+{ 0x040F, 0x040F, 0x045F },
+{ 0x0410, 0x0410, 0x0430 },
+{ 0x0411, 0x0411, 0x0431 },
+{ 0x0412, 0x0412, 0x0432 },
+{ 0x0413, 0x0413, 0x0433 },
+{ 0x0414, 0x0414, 0x0434 },
+{ 0x0415, 0x0415, 0x0435 },
+{ 0x0416, 0x0416, 0x0436 },
+{ 0x0417, 0x0417, 0x0437 },
+{ 0x0418, 0x0418, 0x0438 },
+{ 0x0419, 0x0419, 0x0439 },
+{ 0x041A, 0x041A, 0x043A },
+{ 0x041B, 0x041B, 0x043B },
+{ 0x041C, 0x041C, 0x043C },
+{ 0x041D, 0x041D, 0x043D },
+{ 0x041E, 0x041E, 0x043E },
+{ 0x041F, 0x041F, 0x043F },
+{ 0x0420, 0x0420, 0x0440 },
+{ 0x0421, 0x0421, 0x0441 },
+{ 0x0422, 0x0422, 0x0442 },
+{ 0x0423, 0x0423, 0x0443 },
+{ 0x0424, 0x0424, 0x0444 },
+{ 0x0425, 0x0425, 0x0445 },
+{ 0x0426, 0x0426, 0x0446 },
+{ 0x0427, 0x0427, 0x0447 },
+{ 0x0428, 0x0428, 0x0448 },
+{ 0x0429, 0x0429, 0x0449 },
+{ 0x042A, 0x042A, 0x044A },
+{ 0x042B, 0x042B, 0x044B },
+{ 0x042C, 0x042C, 0x044C },
+{ 0x042D, 0x042D, 0x044D },
+{ 0x042E, 0x042E, 0x044E },
+{ 0x042F, 0x042F, 0x044F },
+{ 0x0430, 0x0410, 0x0430 },
+{ 0x0431, 0x0411, 0x0431 },
+{ 0x0432, 0x0412, 0x0432 },
+{ 0x0433, 0x0413, 0x0433 },
+{ 0x0434, 0x0414, 0x0434 },
+{ 0x0435, 0x0415, 0x0435 },
+{ 0x0436, 0x0416, 0x0436 },
+{ 0x0437, 0x0417, 0x0437 },
+{ 0x0438, 0x0418, 0x0438 },
+{ 0x0439, 0x0419, 0x0439 },
+{ 0x043A, 0x041A, 0x043A },
+{ 0x043B, 0x041B, 0x043B },
+{ 0x043C, 0x041C, 0x043C },
+{ 0x043D, 0x041D, 0x043D },
+{ 0x043E, 0x041E, 0x043E },
+{ 0x043F, 0x041F, 0x043F },
+{ 0x0440, 0x0420, 0x0440 },
+{ 0x0441, 0x0421, 0x0441 },
+{ 0x0442, 0x0422, 0x0442 },
+{ 0x0443, 0x0423, 0x0443 },
+{ 0x0444, 0x0424, 0x0444 },
+{ 0x0445, 0x0425, 0x0445 },
+{ 0x0446, 0x0426, 0x0446 },
+{ 0x0447, 0x0427, 0x0447 },
+{ 0x0448, 0x0428, 0x0448 },
+{ 0x0449, 0x0429, 0x0449 },
+{ 0x044A, 0x042A, 0x044A },
+{ 0x044B, 0x042B, 0x044B },
+{ 0x044C, 0x042C, 0x044C },
+{ 0x044D, 0x042D, 0x044D },
+{ 0x044E, 0x042E, 0x044E },
+{ 0x044F, 0x042F, 0x044F },
+{ 0x0450, 0x0400, 0x0450 },
+{ 0x0451, 0x0401, 0x0451 },
+{ 0x0452, 0x0402, 0x0452 },
+{ 0x0453, 0x0403, 0x0453 },
+{ 0x0454, 0x0404, 0x0454 },
+{ 0x0455, 0x0405, 0x0455 },
+{ 0x0456, 0x0406, 0x0456 },
+{ 0x0457, 0x0407, 0x0457 },
+{ 0x0458, 0x0408, 0x0458 },
+{ 0x0459, 0x0409, 0x0459 },
+{ 0x045A, 0x040A, 0x045A },
+{ 0x045B, 0x040B, 0x045B },
+{ 0x045C, 0x040C, 0x045C },
+{ 0x045D, 0x040D, 0x045D },
+{ 0x045E, 0x040E, 0x045E },
+{ 0x045F, 0x040F, 0x045F },
+{ 0x0460, 0x0460, 0x0461 },
+{ 0x0461, 0x0460, 0x0461 },
+{ 0x0462, 0x0462, 0x0463 },
+{ 0x0463, 0x0462, 0x0463 },
+{ 0x0464, 0x0464, 0x0465 },
+{ 0x0465, 0x0464, 0x0465 },
+{ 0x0466, 0x0466, 0x0467 },
+{ 0x0467, 0x0466, 0x0467 },
+{ 0x0468, 0x0468, 0x0469 },
+{ 0x0469, 0x0468, 0x0469 },
+{ 0x046A, 0x046A, 0x046B },
+{ 0x046B, 0x046A, 0x046B },
+{ 0x046C, 0x046C, 0x046D },
+{ 0x046D, 0x046C, 0x046D },
+{ 0x046E, 0x046E, 0x046F },
+{ 0x046F, 0x046E, 0x046F },
+{ 0x0470, 0x0470, 0x0471 },
+{ 0x0471, 0x0470, 0x0471 },
+{ 0x0472, 0x0472, 0x0473 },
+{ 0x0473, 0x0472, 0x0473 },
+{ 0x0474, 0x0474, 0x0475 },
+{ 0x0475, 0x0474, 0x0475 },
+{ 0x0476, 0x0476, 0x0477 },
+{ 0x0477, 0x0476, 0x0477 },
+{ 0x0478, 0x0478, 0x0479 },
+{ 0x0479, 0x0478, 0x0479 },
+{ 0x047A, 0x047A, 0x047B },
+{ 0x047B, 0x047A, 0x047B },
+{ 0x047C, 0x047C, 0x047D },
+{ 0x047D, 0x047C, 0x047D },
+{ 0x047E, 0x047E, 0x047F },
+{ 0x047F, 0x047E, 0x047F },
+{ 0x0480, 0x0480, 0x0481 },
+{ 0x0481, 0x0480, 0x0481 },
+{ 0x0483, 0x0483, 0x0483 },
+{ 0x0484, 0x0484, 0x0484 },
+{ 0x0485, 0x0485, 0x0485 },
+{ 0x0486, 0x0486, 0x0486 },
+{ 0x048A, 0x048A, 0x048B },
+{ 0x048B, 0x048A, 0x048B },
+{ 0x048C, 0x048C, 0x048D },
+{ 0x048D, 0x048C, 0x048D },
+{ 0x048E, 0x048E, 0x048F },
+{ 0x048F, 0x048E, 0x048F },
+{ 0x0490, 0x0490, 0x0491 },
+{ 0x0491, 0x0490, 0x0491 },
+{ 0x0492, 0x0492, 0x0493 },
+{ 0x0493, 0x0492, 0x0493 },
+{ 0x0494, 0x0494, 0x0495 },
+{ 0x0495, 0x0494, 0x0495 },
+{ 0x0496, 0x0496, 0x0497 },
+{ 0x0497, 0x0496, 0x0497 },
+{ 0x0498, 0x0498, 0x0499 },
+{ 0x0499, 0x0498, 0x0499 },
+{ 0x049A, 0x049A, 0x049B },
+{ 0x049B, 0x049A, 0x049B },
+{ 0x049C, 0x049C, 0x049D },
+{ 0x049D, 0x049C, 0x049D },
+{ 0x049E, 0x049E, 0x049F },
+{ 0x049F, 0x049E, 0x049F },
+{ 0x04A0, 0x04A0, 0x04A1 },
+{ 0x04A1, 0x04A0, 0x04A1 },
+{ 0x04A2, 0x04A2, 0x04A3 },
+{ 0x04A3, 0x04A2, 0x04A3 },
+{ 0x04A4, 0x04A4, 0x04A5 },
+{ 0x04A5, 0x04A4, 0x04A5 },
+{ 0x04A6, 0x04A6, 0x04A7 },
+{ 0x04A7, 0x04A6, 0x04A7 },
+{ 0x04A8, 0x04A8, 0x04A9 },
+{ 0x04A9, 0x04A8, 0x04A9 },
+{ 0x04AA, 0x04AA, 0x04AB },
+{ 0x04AB, 0x04AA, 0x04AB },
+{ 0x04AC, 0x04AC, 0x04AD },
+{ 0x04AD, 0x04AC, 0x04AD },
+{ 0x04AE, 0x04AE, 0x04AF },
+{ 0x04AF, 0x04AE, 0x04AF },
+{ 0x04B0, 0x04B0, 0x04B1 },
+{ 0x04B1, 0x04B0, 0x04B1 },
+{ 0x04B2, 0x04B2, 0x04B3 },
+{ 0x04B3, 0x04B2, 0x04B3 },
+{ 0x04B4, 0x04B4, 0x04B5 },
+{ 0x04B5, 0x04B4, 0x04B5 },
+{ 0x04B6, 0x04B6, 0x04B7 },
+{ 0x04B7, 0x04B6, 0x04B7 },
+{ 0x04B8, 0x04B8, 0x04B9 },
+{ 0x04B9, 0x04B8, 0x04B9 },
+{ 0x04BA, 0x04BA, 0x04BB },
+{ 0x04BB, 0x04BA, 0x04BB },
+{ 0x04BC, 0x04BC, 0x04BD },
+{ 0x04BD, 0x04BC, 0x04BD },
+{ 0x04BE, 0x04BE, 0x04BF },
+{ 0x04BF, 0x04BE, 0x04BF },
+{ 0x04C0, 0x04C0, 0x04C0 },
+{ 0x04C1, 0x04C1, 0x04C2 },
+{ 0x04C2, 0x04C1, 0x04C2 },
+{ 0x04C3, 0x04C3, 0x04C4 },
+{ 0x04C4, 0x04C3, 0x04C4 },
+{ 0x04C5, 0x04C5, 0x04C6 },
+{ 0x04C6, 0x04C5, 0x04C6 },
+{ 0x04C7, 0x04C7, 0x04C8 },
+{ 0x04C8, 0x04C7, 0x04C8 },
+{ 0x04C9, 0x04C9, 0x04CA },
+{ 0x04CA, 0x04C9, 0x04CA },
+{ 0x04CB, 0x04CB, 0x04CC },
+{ 0x04CC, 0x04CB, 0x04CC },
+{ 0x04CD, 0x04CD, 0x04CE },
+{ 0x04CE, 0x04CD, 0x04CE },
+{ 0x04D0, 0x04D0, 0x04D1 },
+{ 0x04D1, 0x04D0, 0x04D1 },
+{ 0x04D2, 0x04D2, 0x04D3 },
+{ 0x04D3, 0x04D2, 0x04D3 },
+{ 0x04D4, 0x04D4, 0x04D5 },
+{ 0x04D5, 0x04D4, 0x04D5 },
+{ 0x04D6, 0x04D6, 0x04D7 },
+{ 0x04D7, 0x04D6, 0x04D7 },
+{ 0x04D8, 0x04D8, 0x04D9 },
+{ 0x04D9, 0x04D8, 0x04D9 },
+{ 0x04DA, 0x04DA, 0x04DB },
+{ 0x04DB, 0x04DA, 0x04DB },
+{ 0x04DC, 0x04DC, 0x04DD },
+{ 0x04DD, 0x04DC, 0x04DD },
+{ 0x04DE, 0x04DE, 0x04DF },
+{ 0x04DF, 0x04DE, 0x04DF },
+{ 0x04E0, 0x04E0, 0x04E1 },
+{ 0x04E1, 0x04E0, 0x04E1 },
+{ 0x04E2, 0x04E2, 0x04E3 },
+{ 0x04E3, 0x04E2, 0x04E3 },
+{ 0x04E4, 0x04E4, 0x04E5 },
+{ 0x04E5, 0x04E4, 0x04E5 },
+{ 0x04E6, 0x04E6, 0x04E7 },
+{ 0x04E7, 0x04E6, 0x04E7 },
+{ 0x04E8, 0x04E8, 0x04E9 },
+{ 0x04E9, 0x04E8, 0x04E9 },
+{ 0x04EA, 0x04EA, 0x04EB },
+{ 0x04EB, 0x04EA, 0x04EB },
+{ 0x04EC, 0x04EC, 0x04ED },
+{ 0x04ED, 0x04EC, 0x04ED },
+{ 0x04EE, 0x04EE, 0x04EF },
+{ 0x04EF, 0x04EE, 0x04EF },
+{ 0x04F0, 0x04F0, 0x04F1 },
+{ 0x04F1, 0x04F0, 0x04F1 },
+{ 0x04F2, 0x04F2, 0x04F3 },
+{ 0x04F3, 0x04F2, 0x04F3 },
+{ 0x04F4, 0x04F4, 0x04F5 },
+{ 0x04F5, 0x04F4, 0x04F5 },
+{ 0x04F6, 0x04F6, 0x04F7 },
+{ 0x04F7, 0x04F6, 0x04F7 },
+{ 0x04F8, 0x04F8, 0x04F9 },
+{ 0x04F9, 0x04F8, 0x04F9 },
+{ 0x0500, 0x0500, 0x0501 },
+{ 0x0501, 0x0500, 0x0501 },
+{ 0x0502, 0x0502, 0x0503 },
+{ 0x0503, 0x0502, 0x0503 },
+{ 0x0504, 0x0504, 0x0505 },
+{ 0x0505, 0x0504, 0x0505 },
+{ 0x0506, 0x0506, 0x0507 },
+{ 0x0507, 0x0506, 0x0507 },
+{ 0x0508, 0x0508, 0x0509 },
+{ 0x0509, 0x0508, 0x0509 },
+{ 0x050A, 0x050A, 0x050B },
+{ 0x050B, 0x050A, 0x050B },
+{ 0x050C, 0x050C, 0x050D },
+{ 0x050D, 0x050C, 0x050D },
+{ 0x050E, 0x050E, 0x050F },
+{ 0x050F, 0x050E, 0x050F },
+{ 0x0531, 0x0531, 0x0561 },
+{ 0x0532, 0x0532, 0x0562 },
+{ 0x0533, 0x0533, 0x0563 },
+{ 0x0534, 0x0534, 0x0564 },
+{ 0x0535, 0x0535, 0x0565 },
+{ 0x0536, 0x0536, 0x0566 },
+{ 0x0537, 0x0537, 0x0567 },
+{ 0x0538, 0x0538, 0x0568 },
+{ 0x0539, 0x0539, 0x0569 },
+{ 0x053A, 0x053A, 0x056A },
+{ 0x053B, 0x053B, 0x056B },
+{ 0x053C, 0x053C, 0x056C },
+{ 0x053D, 0x053D, 0x056D },
+{ 0x053E, 0x053E, 0x056E },
+{ 0x053F, 0x053F, 0x056F },
+{ 0x0540, 0x0540, 0x0570 },
+{ 0x0541, 0x0541, 0x0571 },
+{ 0x0542, 0x0542, 0x0572 },
+{ 0x0543, 0x0543, 0x0573 },
+{ 0x0544, 0x0544, 0x0574 },
+{ 0x0545, 0x0545, 0x0575 },
+{ 0x0546, 0x0546, 0x0576 },
+{ 0x0547, 0x0547, 0x0577 },
+{ 0x0548, 0x0548, 0x0578 },
+{ 0x0549, 0x0549, 0x0579 },
+{ 0x054A, 0x054A, 0x057A },
+{ 0x054B, 0x054B, 0x057B },
+{ 0x054C, 0x054C, 0x057C },
+{ 0x054D, 0x054D, 0x057D },
+{ 0x054E, 0x054E, 0x057E },
+{ 0x054F, 0x054F, 0x057F },
+{ 0x0550, 0x0550, 0x0580 },
+{ 0x0551, 0x0551, 0x0581 },
+{ 0x0552, 0x0552, 0x0582 },
+{ 0x0553, 0x0553, 0x0583 },
+{ 0x0554, 0x0554, 0x0584 },
+{ 0x0555, 0x0555, 0x0585 },
+{ 0x0556, 0x0556, 0x0586 },
+{ 0x0559, 0x0559, 0x0559 },
+{ 0x0561, 0x0531, 0x0561 },
+{ 0x0562, 0x0532, 0x0562 },
+{ 0x0563, 0x0533, 0x0563 },
+{ 0x0564, 0x0534, 0x0564 },
+{ 0x0565, 0x0535, 0x0565 },
+{ 0x0566, 0x0536, 0x0566 },
+{ 0x0567, 0x0537, 0x0567 },
+{ 0x0568, 0x0538, 0x0568 },
+{ 0x0569, 0x0539, 0x0569 },
+{ 0x056A, 0x053A, 0x056A },
+{ 0x056B, 0x053B, 0x056B },
+{ 0x056C, 0x053C, 0x056C },
+{ 0x056D, 0x053D, 0x056D },
+{ 0x056E, 0x053E, 0x056E },
+{ 0x056F, 0x053F, 0x056F },
+{ 0x0570, 0x0540, 0x0570 },
+{ 0x0571, 0x0541, 0x0571 },
+{ 0x0572, 0x0542, 0x0572 },
+{ 0x0573, 0x0543, 0x0573 },
+{ 0x0574, 0x0544, 0x0574 },
+{ 0x0575, 0x0545, 0x0575 },
+{ 0x0576, 0x0546, 0x0576 },
+{ 0x0577, 0x0547, 0x0577 },
+{ 0x0578, 0x0548, 0x0578 },
+{ 0x0579, 0x0549, 0x0579 },
+{ 0x057A, 0x054A, 0x057A },
+{ 0x057B, 0x054B, 0x057B },
+{ 0x057C, 0x054C, 0x057C },
+{ 0x057D, 0x054D, 0x057D },
+{ 0x057E, 0x054E, 0x057E },
+{ 0x057F, 0x054F, 0x057F },
+{ 0x0580, 0x0550, 0x0580 },
+{ 0x0581, 0x0551, 0x0581 },
+{ 0x0582, 0x0552, 0x0582 },
+{ 0x0583, 0x0553, 0x0583 },
+{ 0x0584, 0x0554, 0x0584 },
+{ 0x0585, 0x0555, 0x0585 },
+{ 0x0586, 0x0556, 0x0586 },
+{ 0x0587, 0x0587, 0x0587 },
+{ 0x0591, 0x0591, 0x0591 },
+{ 0x0592, 0x0592, 0x0592 },
+{ 0x0593, 0x0593, 0x0593 },
+{ 0x0594, 0x0594, 0x0594 },
+{ 0x0595, 0x0595, 0x0595 },
+{ 0x0596, 0x0596, 0x0596 },
+{ 0x0597, 0x0597, 0x0597 },
+{ 0x0598, 0x0598, 0x0598 },
+{ 0x0599, 0x0599, 0x0599 },
+{ 0x059A, 0x059A, 0x059A },
+{ 0x059B, 0x059B, 0x059B },
+{ 0x059C, 0x059C, 0x059C },
+{ 0x059D, 0x059D, 0x059D },
+{ 0x059E, 0x059E, 0x059E },
+{ 0x059F, 0x059F, 0x059F },
+{ 0x05A0, 0x05A0, 0x05A0 },
+{ 0x05A1, 0x05A1, 0x05A1 },
+{ 0x05A2, 0x05A2, 0x05A2 },
+{ 0x05A3, 0x05A3, 0x05A3 },
+{ 0x05A4, 0x05A4, 0x05A4 },
+{ 0x05A5, 0x05A5, 0x05A5 },
+{ 0x05A6, 0x05A6, 0x05A6 },
+{ 0x05A7, 0x05A7, 0x05A7 },
+{ 0x05A8, 0x05A8, 0x05A8 },
+{ 0x05A9, 0x05A9, 0x05A9 },
+{ 0x05AA, 0x05AA, 0x05AA },
+{ 0x05AB, 0x05AB, 0x05AB },
+{ 0x05AC, 0x05AC, 0x05AC },
+{ 0x05AD, 0x05AD, 0x05AD },
+{ 0x05AE, 0x05AE, 0x05AE },
+{ 0x05AF, 0x05AF, 0x05AF },
+{ 0x05B0, 0x05B0, 0x05B0 },
+{ 0x05B1, 0x05B1, 0x05B1 },
+{ 0x05B2, 0x05B2, 0x05B2 },
+{ 0x05B3, 0x05B3, 0x05B3 },
+{ 0x05B4, 0x05B4, 0x05B4 },
+{ 0x05B5, 0x05B5, 0x05B5 },
+{ 0x05B6, 0x05B6, 0x05B6 },
+{ 0x05B7, 0x05B7, 0x05B7 },
+{ 0x05B8, 0x05B8, 0x05B8 },
+{ 0x05B9, 0x05B9, 0x05B9 },
+{ 0x05BB, 0x05BB, 0x05BB },
+{ 0x05BC, 0x05BC, 0x05BC },
+{ 0x05BD, 0x05BD, 0x05BD },
+{ 0x05BF, 0x05BF, 0x05BF },
+{ 0x05C1, 0x05C1, 0x05C1 },
+{ 0x05C2, 0x05C2, 0x05C2 },
+{ 0x05C4, 0x05C4, 0x05C4 },
+{ 0x05C5, 0x05C5, 0x05C5 },
+{ 0x05C7, 0x05C7, 0x05C7 },
+{ 0x05D0, 0x05D0, 0x05D0 },
+{ 0x05D1, 0x05D1, 0x05D1 },
+{ 0x05D2, 0x05D2, 0x05D2 },
+{ 0x05D3, 0x05D3, 0x05D3 },
+{ 0x05D4, 0x05D4, 0x05D4 },
+{ 0x05D5, 0x05D5, 0x05D5 },
+{ 0x05D6, 0x05D6, 0x05D6 },
+{ 0x05D7, 0x05D7, 0x05D7 },
+{ 0x05D8, 0x05D8, 0x05D8 },
+{ 0x05D9, 0x05D9, 0x05D9 },
+{ 0x05DA, 0x05DA, 0x05DA },
+{ 0x05DB, 0x05DB, 0x05DB },
+{ 0x05DC, 0x05DC, 0x05DC },
+{ 0x05DD, 0x05DD, 0x05DD },
+{ 0x05DE, 0x05DE, 0x05DE },
+{ 0x05DF, 0x05DF, 0x05DF },
+{ 0x05E0, 0x05E0, 0x05E0 },
+{ 0x05E1, 0x05E1, 0x05E1 },
+{ 0x05E2, 0x05E2, 0x05E2 },
+{ 0x05E3, 0x05E3, 0x05E3 },
+{ 0x05E4, 0x05E4, 0x05E4 },
+{ 0x05E5, 0x05E5, 0x05E5 },
+{ 0x05E6, 0x05E6, 0x05E6 },
+{ 0x05E7, 0x05E7, 0x05E7 },
+{ 0x05E8, 0x05E8, 0x05E8 },
+{ 0x05E9, 0x05E9, 0x05E9 },
+{ 0x05EA, 0x05EA, 0x05EA },
+{ 0x05F0, 0x05F0, 0x05F0 },
+{ 0x05F1, 0x05F1, 0x05F1 },
+{ 0x05F2, 0x05F2, 0x05F2 },
+{ 0x0610, 0x0610, 0x0610 },
+{ 0x0611, 0x0611, 0x0611 },
+{ 0x0612, 0x0612, 0x0612 },
+{ 0x0613, 0x0613, 0x0613 },
+{ 0x0614, 0x0614, 0x0614 },
+{ 0x0615, 0x0615, 0x0615 },
+{ 0x0621, 0x0621, 0x0621 },
+{ 0x0622, 0x0622, 0x0622 },
+{ 0x0623, 0x0623, 0x0623 },
+{ 0x0624, 0x0624, 0x0624 },
+{ 0x0625, 0x0625, 0x0625 },
+{ 0x0626, 0x0626, 0x0626 },
+{ 0x0627, 0x0627, 0x0627 },
+{ 0x0628, 0x0628, 0x0628 },
+{ 0x0629, 0x0629, 0x0629 },
+{ 0x062A, 0x062A, 0x062A },
+{ 0x062B, 0x062B, 0x062B },
+{ 0x062C, 0x062C, 0x062C },
+{ 0x062D, 0x062D, 0x062D },
+{ 0x062E, 0x062E, 0x062E },
+{ 0x062F, 0x062F, 0x062F },
+{ 0x0630, 0x0630, 0x0630 },
+{ 0x0631, 0x0631, 0x0631 },
+{ 0x0632, 0x0632, 0x0632 },
+{ 0x0633, 0x0633, 0x0633 },
+{ 0x0634, 0x0634, 0x0634 },
+{ 0x0635, 0x0635, 0x0635 },
+{ 0x0636, 0x0636, 0x0636 },
+{ 0x0637, 0x0637, 0x0637 },
+{ 0x0638, 0x0638, 0x0638 },
+{ 0x0639, 0x0639, 0x0639 },
+{ 0x063A, 0x063A, 0x063A },
+{ 0x0640, 0x0640, 0x0640 },
+{ 0x0641, 0x0641, 0x0641 },
+{ 0x0642, 0x0642, 0x0642 },
+{ 0x0643, 0x0643, 0x0643 },
+{ 0x0644, 0x0644, 0x0644 },
+{ 0x0645, 0x0645, 0x0645 },
+{ 0x0646, 0x0646, 0x0646 },
+{ 0x0647, 0x0647, 0x0647 },
+{ 0x0648, 0x0648, 0x0648 },
+{ 0x0649, 0x0649, 0x0649 },
+{ 0x064A, 0x064A, 0x064A },
+{ 0x064B, 0x064B, 0x064B },
+{ 0x064C, 0x064C, 0x064C },
+{ 0x064D, 0x064D, 0x064D },
+{ 0x064E, 0x064E, 0x064E },
+{ 0x064F, 0x064F, 0x064F },
+{ 0x0650, 0x0650, 0x0650 },
+{ 0x0651, 0x0651, 0x0651 },
+{ 0x0652, 0x0652, 0x0652 },
+{ 0x0653, 0x0653, 0x0653 },
+{ 0x0654, 0x0654, 0x0654 },
+{ 0x0655, 0x0655, 0x0655 },
+{ 0x0656, 0x0656, 0x0656 },
+{ 0x0657, 0x0657, 0x0657 },
+{ 0x0658, 0x0658, 0x0658 },
+{ 0x0659, 0x0659, 0x0659 },
+{ 0x065A, 0x065A, 0x065A },
+{ 0x065B, 0x065B, 0x065B },
+{ 0x065C, 0x065C, 0x065C },
+{ 0x065D, 0x065D, 0x065D },
+{ 0x065E, 0x065E, 0x065E },
+{ 0x066E, 0x066E, 0x066E },
+{ 0x066F, 0x066F, 0x066F },
+{ 0x0670, 0x0670, 0x0670 },
+{ 0x0671, 0x0671, 0x0671 },
+{ 0x0672, 0x0672, 0x0672 },
+{ 0x0673, 0x0673, 0x0673 },
+{ 0x0674, 0x0674, 0x0674 },
+{ 0x0675, 0x0675, 0x0675 },
+{ 0x0676, 0x0676, 0x0676 },
+{ 0x0677, 0x0677, 0x0677 },
+{ 0x0678, 0x0678, 0x0678 },
+{ 0x0679, 0x0679, 0x0679 },
+{ 0x067A, 0x067A, 0x067A },
+{ 0x067B, 0x067B, 0x067B },
+{ 0x067C, 0x067C, 0x067C },
+{ 0x067D, 0x067D, 0x067D },
+{ 0x067E, 0x067E, 0x067E },
+{ 0x067F, 0x067F, 0x067F },
+{ 0x0680, 0x0680, 0x0680 },
+{ 0x0681, 0x0681, 0x0681 },
+{ 0x0682, 0x0682, 0x0682 },
+{ 0x0683, 0x0683, 0x0683 },
+{ 0x0684, 0x0684, 0x0684 },
+{ 0x0685, 0x0685, 0x0685 },
+{ 0x0686, 0x0686, 0x0686 },
+{ 0x0687, 0x0687, 0x0687 },
+{ 0x0688, 0x0688, 0x0688 },
+{ 0x0689, 0x0689, 0x0689 },
+{ 0x068A, 0x068A, 0x068A },
+{ 0x068B, 0x068B, 0x068B },
+{ 0x068C, 0x068C, 0x068C },
+{ 0x068D, 0x068D, 0x068D },
+{ 0x068E, 0x068E, 0x068E },
+{ 0x068F, 0x068F, 0x068F },
+{ 0x0690, 0x0690, 0x0690 },
+{ 0x0691, 0x0691, 0x0691 },
+{ 0x0692, 0x0692, 0x0692 },
+{ 0x0693, 0x0693, 0x0693 },
+{ 0x0694, 0x0694, 0x0694 },
+{ 0x0695, 0x0695, 0x0695 },
+{ 0x0696, 0x0696, 0x0696 },
+{ 0x0697, 0x0697, 0x0697 },
+{ 0x0698, 0x0698, 0x0698 },
+{ 0x0699, 0x0699, 0x0699 },
+{ 0x069A, 0x069A, 0x069A },
+{ 0x069B, 0x069B, 0x069B },
+{ 0x069C, 0x069C, 0x069C },
+{ 0x069D, 0x069D, 0x069D },
+{ 0x069E, 0x069E, 0x069E },
+{ 0x069F, 0x069F, 0x069F },
+{ 0x06A0, 0x06A0, 0x06A0 },
+{ 0x06A1, 0x06A1, 0x06A1 },
+{ 0x06A2, 0x06A2, 0x06A2 },
+{ 0x06A3, 0x06A3, 0x06A3 },
+{ 0x06A4, 0x06A4, 0x06A4 },
+{ 0x06A5, 0x06A5, 0x06A5 },
+{ 0x06A6, 0x06A6, 0x06A6 },
+{ 0x06A7, 0x06A7, 0x06A7 },
+{ 0x06A8, 0x06A8, 0x06A8 },
+{ 0x06A9, 0x06A9, 0x06A9 },
+{ 0x06AA, 0x06AA, 0x06AA },
+{ 0x06AB, 0x06AB, 0x06AB },
+{ 0x06AC, 0x06AC, 0x06AC },
+{ 0x06AD, 0x06AD, 0x06AD },
+{ 0x06AE, 0x06AE, 0x06AE },
+{ 0x06AF, 0x06AF, 0x06AF },
+{ 0x06B0, 0x06B0, 0x06B0 },
+{ 0x06B1, 0x06B1, 0x06B1 },
+{ 0x06B2, 0x06B2, 0x06B2 },
+{ 0x06B3, 0x06B3, 0x06B3 },
+{ 0x06B4, 0x06B4, 0x06B4 },
+{ 0x06B5, 0x06B5, 0x06B5 },
+{ 0x06B6, 0x06B6, 0x06B6 },
+{ 0x06B7, 0x06B7, 0x06B7 },
+{ 0x06B8, 0x06B8, 0x06B8 },
+{ 0x06B9, 0x06B9, 0x06B9 },
+{ 0x06BA, 0x06BA, 0x06BA },
+{ 0x06BB, 0x06BB, 0x06BB },
+{ 0x06BC, 0x06BC, 0x06BC },
+{ 0x06BD, 0x06BD, 0x06BD },
+{ 0x06BE, 0x06BE, 0x06BE },
+{ 0x06BF, 0x06BF, 0x06BF },
+{ 0x06C0, 0x06C0, 0x06C0 },
+{ 0x06C1, 0x06C1, 0x06C1 },
+{ 0x06C2, 0x06C2, 0x06C2 },
+{ 0x06C3, 0x06C3, 0x06C3 },
+{ 0x06C4, 0x06C4, 0x06C4 },
+{ 0x06C5, 0x06C5, 0x06C5 },
+{ 0x06C6, 0x06C6, 0x06C6 },
+{ 0x06C7, 0x06C7, 0x06C7 },
+{ 0x06C8, 0x06C8, 0x06C8 },
+{ 0x06C9, 0x06C9, 0x06C9 },
+{ 0x06CA, 0x06CA, 0x06CA },
+{ 0x06CB, 0x06CB, 0x06CB },
+{ 0x06CC, 0x06CC, 0x06CC },
+{ 0x06CD, 0x06CD, 0x06CD },
+{ 0x06CE, 0x06CE, 0x06CE },
+{ 0x06CF, 0x06CF, 0x06CF },
+{ 0x06D0, 0x06D0, 0x06D0 },
+{ 0x06D1, 0x06D1, 0x06D1 },
+{ 0x06D2, 0x06D2, 0x06D2 },
+{ 0x06D3, 0x06D3, 0x06D3 },
+{ 0x06D5, 0x06D5, 0x06D5 },
+{ 0x06D6, 0x06D6, 0x06D6 },
+{ 0x06D7, 0x06D7, 0x06D7 },
+{ 0x06D8, 0x06D8, 0x06D8 },
+{ 0x06D9, 0x06D9, 0x06D9 },
+{ 0x06DA, 0x06DA, 0x06DA },
+{ 0x06DB, 0x06DB, 0x06DB },
+{ 0x06DC, 0x06DC, 0x06DC },
+{ 0x06DF, 0x06DF, 0x06DF },
+{ 0x06E0, 0x06E0, 0x06E0 },
+{ 0x06E1, 0x06E1, 0x06E1 },
+{ 0x06E2, 0x06E2, 0x06E2 },
+{ 0x06E3, 0x06E3, 0x06E3 },
+{ 0x06E4, 0x06E4, 0x06E4 },
+{ 0x06E5, 0x06E5, 0x06E5 },
+{ 0x06E6, 0x06E6, 0x06E6 },
+{ 0x06E7, 0x06E7, 0x06E7 },
+{ 0x06E8, 0x06E8, 0x06E8 },
+{ 0x06EA, 0x06EA, 0x06EA },
+{ 0x06EB, 0x06EB, 0x06EB },
+{ 0x06EC, 0x06EC, 0x06EC },
+{ 0x06ED, 0x06ED, 0x06ED },
+{ 0x06EE, 0x06EE, 0x06EE },
+{ 0x06EF, 0x06EF, 0x06EF },
+{ 0x06FA, 0x06FA, 0x06FA },
+{ 0x06FB, 0x06FB, 0x06FB },
+{ 0x06FC, 0x06FC, 0x06FC },
+{ 0x06FF, 0x06FF, 0x06FF },
+{ 0x0710, 0x0710, 0x0710 },
+{ 0x0711, 0x0711, 0x0711 },
+{ 0x0712, 0x0712, 0x0712 },
+{ 0x0713, 0x0713, 0x0713 },
+{ 0x0714, 0x0714, 0x0714 },
+{ 0x0715, 0x0715, 0x0715 },
+{ 0x0716, 0x0716, 0x0716 },
+{ 0x0717, 0x0717, 0x0717 },
+{ 0x0718, 0x0718, 0x0718 },
+{ 0x0719, 0x0719, 0x0719 },
+{ 0x071A, 0x071A, 0x071A },
+{ 0x071B, 0x071B, 0x071B },
+{ 0x071C, 0x071C, 0x071C },
+{ 0x071D, 0x071D, 0x071D },
+{ 0x071E, 0x071E, 0x071E },
+{ 0x071F, 0x071F, 0x071F },
+{ 0x0720, 0x0720, 0x0720 },
+{ 0x0721, 0x0721, 0x0721 },
+{ 0x0722, 0x0722, 0x0722 },
+{ 0x0723, 0x0723, 0x0723 },
+{ 0x0724, 0x0724, 0x0724 },
+{ 0x0725, 0x0725, 0x0725 },
+{ 0x0726, 0x0726, 0x0726 },
+{ 0x0727, 0x0727, 0x0727 },
+{ 0x0728, 0x0728, 0x0728 },
+{ 0x0729, 0x0729, 0x0729 },
+{ 0x072A, 0x072A, 0x072A },
+{ 0x072B, 0x072B, 0x072B },
+{ 0x072C, 0x072C, 0x072C },
+{ 0x072D, 0x072D, 0x072D },
+{ 0x072E, 0x072E, 0x072E },
+{ 0x072F, 0x072F, 0x072F },
+{ 0x0730, 0x0730, 0x0730 },
+{ 0x0731, 0x0731, 0x0731 },
+{ 0x0732, 0x0732, 0x0732 },
+{ 0x0733, 0x0733, 0x0733 },
+{ 0x0734, 0x0734, 0x0734 },
+{ 0x0735, 0x0735, 0x0735 },
+{ 0x0736, 0x0736, 0x0736 },
+{ 0x0737, 0x0737, 0x0737 },
+{ 0x0738, 0x0738, 0x0738 },
+{ 0x0739, 0x0739, 0x0739 },
+{ 0x073A, 0x073A, 0x073A },
+{ 0x073B, 0x073B, 0x073B },
+{ 0x073C, 0x073C, 0x073C },
+{ 0x073D, 0x073D, 0x073D },
+{ 0x073E, 0x073E, 0x073E },
+{ 0x073F, 0x073F, 0x073F },
+{ 0x0740, 0x0740, 0x0740 },
+{ 0x0741, 0x0741, 0x0741 },
+{ 0x0742, 0x0742, 0x0742 },
+{ 0x0743, 0x0743, 0x0743 },
+{ 0x0744, 0x0744, 0x0744 },
+{ 0x0745, 0x0745, 0x0745 },
+{ 0x0746, 0x0746, 0x0746 },
+{ 0x0747, 0x0747, 0x0747 },
+{ 0x0748, 0x0748, 0x0748 },
+{ 0x0749, 0x0749, 0x0749 },
+{ 0x074A, 0x074A, 0x074A },
+{ 0x074D, 0x074D, 0x074D },
+{ 0x074E, 0x074E, 0x074E },
+{ 0x074F, 0x074F, 0x074F },
+{ 0x0750, 0x0750, 0x0750 },
+{ 0x0751, 0x0751, 0x0751 },
+{ 0x0752, 0x0752, 0x0752 },
+{ 0x0753, 0x0753, 0x0753 },
+{ 0x0754, 0x0754, 0x0754 },
+{ 0x0755, 0x0755, 0x0755 },
+{ 0x0756, 0x0756, 0x0756 },
+{ 0x0757, 0x0757, 0x0757 },
+{ 0x0758, 0x0758, 0x0758 },
+{ 0x0759, 0x0759, 0x0759 },
+{ 0x075A, 0x075A, 0x075A },
+{ 0x075B, 0x075B, 0x075B },
+{ 0x075C, 0x075C, 0x075C },
+{ 0x075D, 0x075D, 0x075D },
+{ 0x075E, 0x075E, 0x075E },
+{ 0x075F, 0x075F, 0x075F },
+{ 0x0760, 0x0760, 0x0760 },
+{ 0x0761, 0x0761, 0x0761 },
+{ 0x0762, 0x0762, 0x0762 },
+{ 0x0763, 0x0763, 0x0763 },
+{ 0x0764, 0x0764, 0x0764 },
+{ 0x0765, 0x0765, 0x0765 },
+{ 0x0766, 0x0766, 0x0766 },
+{ 0x0767, 0x0767, 0x0767 },
+{ 0x0768, 0x0768, 0x0768 },
+{ 0x0769, 0x0769, 0x0769 },
+{ 0x076A, 0x076A, 0x076A },
+{ 0x076B, 0x076B, 0x076B },
+{ 0x076C, 0x076C, 0x076C },
+{ 0x076D, 0x076D, 0x076D },
+{ 0x0780, 0x0780, 0x0780 },
+{ 0x0781, 0x0781, 0x0781 },
+{ 0x0782, 0x0782, 0x0782 },
+{ 0x0783, 0x0783, 0x0783 },
+{ 0x0784, 0x0784, 0x0784 },
+{ 0x0785, 0x0785, 0x0785 },
+{ 0x0786, 0x0786, 0x0786 },
+{ 0x0787, 0x0787, 0x0787 },
+{ 0x0788, 0x0788, 0x0788 },
+{ 0x0789, 0x0789, 0x0789 },
+{ 0x078A, 0x078A, 0x078A },
+{ 0x078B, 0x078B, 0x078B },
+{ 0x078C, 0x078C, 0x078C },
+{ 0x078D, 0x078D, 0x078D },
+{ 0x078E, 0x078E, 0x078E },
+{ 0x078F, 0x078F, 0x078F },
+{ 0x0790, 0x0790, 0x0790 },
+{ 0x0791, 0x0791, 0x0791 },
+{ 0x0792, 0x0792, 0x0792 },
+{ 0x0793, 0x0793, 0x0793 },
+{ 0x0794, 0x0794, 0x0794 },
+{ 0x0795, 0x0795, 0x0795 },
+{ 0x0796, 0x0796, 0x0796 },
+{ 0x0797, 0x0797, 0x0797 },
+{ 0x0798, 0x0798, 0x0798 },
+{ 0x0799, 0x0799, 0x0799 },
+{ 0x079A, 0x079A, 0x079A },
+{ 0x079B, 0x079B, 0x079B },
+{ 0x079C, 0x079C, 0x079C },
+{ 0x079D, 0x079D, 0x079D },
+{ 0x079E, 0x079E, 0x079E },
+{ 0x079F, 0x079F, 0x079F },
+{ 0x07A0, 0x07A0, 0x07A0 },
+{ 0x07A1, 0x07A1, 0x07A1 },
+{ 0x07A2, 0x07A2, 0x07A2 },
+{ 0x07A3, 0x07A3, 0x07A3 },
+{ 0x07A4, 0x07A4, 0x07A4 },
+{ 0x07A5, 0x07A5, 0x07A5 },
+{ 0x07A6, 0x07A6, 0x07A6 },
+{ 0x07A7, 0x07A7, 0x07A7 },
+{ 0x07A8, 0x07A8, 0x07A8 },
+{ 0x07A9, 0x07A9, 0x07A9 },
+{ 0x07AA, 0x07AA, 0x07AA },
+{ 0x07AB, 0x07AB, 0x07AB },
+{ 0x07AC, 0x07AC, 0x07AC },
+{ 0x07AD, 0x07AD, 0x07AD },
+{ 0x07AE, 0x07AE, 0x07AE },
+{ 0x07AF, 0x07AF, 0x07AF },
+{ 0x07B0, 0x07B0, 0x07B0 },
+{ 0x07B1, 0x07B1, 0x07B1 },
+{ 0x0901, 0x0901, 0x0901 },
+{ 0x0902, 0x0902, 0x0902 },
+{ 0x0904, 0x0904, 0x0904 },
+{ 0x0905, 0x0905, 0x0905 },
+{ 0x0906, 0x0906, 0x0906 },
+{ 0x0907, 0x0907, 0x0907 },
+{ 0x0908, 0x0908, 0x0908 },
+{ 0x0909, 0x0909, 0x0909 },
+{ 0x090A, 0x090A, 0x090A },
+{ 0x090B, 0x090B, 0x090B },
+{ 0x090C, 0x090C, 0x090C },
+{ 0x090D, 0x090D, 0x090D },
+{ 0x090E, 0x090E, 0x090E },
+{ 0x090F, 0x090F, 0x090F },
+{ 0x0910, 0x0910, 0x0910 },
+{ 0x0911, 0x0911, 0x0911 },
+{ 0x0912, 0x0912, 0x0912 },
+{ 0x0913, 0x0913, 0x0913 },
+{ 0x0914, 0x0914, 0x0914 },
+{ 0x0915, 0x0915, 0x0915 },
+{ 0x0916, 0x0916, 0x0916 },
+{ 0x0917, 0x0917, 0x0917 },
+{ 0x0918, 0x0918, 0x0918 },
+{ 0x0919, 0x0919, 0x0919 },
+{ 0x091A, 0x091A, 0x091A },
+{ 0x091B, 0x091B, 0x091B },
+{ 0x091C, 0x091C, 0x091C },
+{ 0x091D, 0x091D, 0x091D },
+{ 0x091E, 0x091E, 0x091E },
+{ 0x091F, 0x091F, 0x091F },
+{ 0x0920, 0x0920, 0x0920 },
+{ 0x0921, 0x0921, 0x0921 },
+{ 0x0922, 0x0922, 0x0922 },
+{ 0x0923, 0x0923, 0x0923 },
+{ 0x0924, 0x0924, 0x0924 },
+{ 0x0925, 0x0925, 0x0925 },
+{ 0x0926, 0x0926, 0x0926 },
+{ 0x0927, 0x0927, 0x0927 },
+{ 0x0928, 0x0928, 0x0928 },
+{ 0x0929, 0x0929, 0x0929 },
+{ 0x092A, 0x092A, 0x092A },
+{ 0x092B, 0x092B, 0x092B },
+{ 0x092C, 0x092C, 0x092C },
+{ 0x092D, 0x092D, 0x092D },
+{ 0x092E, 0x092E, 0x092E },
+{ 0x092F, 0x092F, 0x092F },
+{ 0x0930, 0x0930, 0x0930 },
+{ 0x0931, 0x0931, 0x0931 },
+{ 0x0932, 0x0932, 0x0932 },
+{ 0x0933, 0x0933, 0x0933 },
+{ 0x0934, 0x0934, 0x0934 },
+{ 0x0935, 0x0935, 0x0935 },
+{ 0x0936, 0x0936, 0x0936 },
+{ 0x0937, 0x0937, 0x0937 },
+{ 0x0938, 0x0938, 0x0938 },
+{ 0x0939, 0x0939, 0x0939 },
+{ 0x093C, 0x093C, 0x093C },
+{ 0x093D, 0x093D, 0x093D },
+{ 0x0941, 0x0941, 0x0941 },
+{ 0x0942, 0x0942, 0x0942 },
+{ 0x0943, 0x0943, 0x0943 },
+{ 0x0944, 0x0944, 0x0944 },
+{ 0x0945, 0x0945, 0x0945 },
+{ 0x0946, 0x0946, 0x0946 },
+{ 0x0947, 0x0947, 0x0947 },
+{ 0x0948, 0x0948, 0x0948 },
+{ 0x094D, 0x094D, 0x094D },
+{ 0x0950, 0x0950, 0x0950 },
+{ 0x0951, 0x0951, 0x0951 },
+{ 0x0952, 0x0952, 0x0952 },
+{ 0x0953, 0x0953, 0x0953 },
+{ 0x0954, 0x0954, 0x0954 },
+{ 0x0958, 0x0958, 0x0958 },
+{ 0x0959, 0x0959, 0x0959 },
+{ 0x095A, 0x095A, 0x095A },
+{ 0x095B, 0x095B, 0x095B },
+{ 0x095C, 0x095C, 0x095C },
+{ 0x095D, 0x095D, 0x095D },
+{ 0x095E, 0x095E, 0x095E },
+{ 0x095F, 0x095F, 0x095F },
+{ 0x0960, 0x0960, 0x0960 },
+{ 0x0961, 0x0961, 0x0961 },
+{ 0x0962, 0x0962, 0x0962 },
+{ 0x0963, 0x0963, 0x0963 },
+{ 0x097D, 0x097D, 0x097D },
+{ 0x0981, 0x0981, 0x0981 },
+{ 0x0985, 0x0985, 0x0985 },
+{ 0x0986, 0x0986, 0x0986 },
+{ 0x0987, 0x0987, 0x0987 },
+{ 0x0988, 0x0988, 0x0988 },
+{ 0x0989, 0x0989, 0x0989 },
+{ 0x098A, 0x098A, 0x098A },
+{ 0x098B, 0x098B, 0x098B },
+{ 0x098C, 0x098C, 0x098C },
+{ 0x098F, 0x098F, 0x098F },
+{ 0x0990, 0x0990, 0x0990 },
+{ 0x0993, 0x0993, 0x0993 },
+{ 0x0994, 0x0994, 0x0994 },
+{ 0x0995, 0x0995, 0x0995 },
+{ 0x0996, 0x0996, 0x0996 },
+{ 0x0997, 0x0997, 0x0997 },
+{ 0x0998, 0x0998, 0x0998 },
+{ 0x0999, 0x0999, 0x0999 },
+{ 0x099A, 0x099A, 0x099A },
+{ 0x099B, 0x099B, 0x099B },
+{ 0x099C, 0x099C, 0x099C },
+{ 0x099D, 0x099D, 0x099D },
+{ 0x099E, 0x099E, 0x099E },
+{ 0x099F, 0x099F, 0x099F },
+{ 0x09A0, 0x09A0, 0x09A0 },
+{ 0x09A1, 0x09A1, 0x09A1 },
+{ 0x09A2, 0x09A2, 0x09A2 },
+{ 0x09A3, 0x09A3, 0x09A3 },
+{ 0x09A4, 0x09A4, 0x09A4 },
+{ 0x09A5, 0x09A5, 0x09A5 },
+{ 0x09A6, 0x09A6, 0x09A6 },
+{ 0x09A7, 0x09A7, 0x09A7 },
+{ 0x09A8, 0x09A8, 0x09A8 },
+{ 0x09AA, 0x09AA, 0x09AA },
+{ 0x09AB, 0x09AB, 0x09AB },
+{ 0x09AC, 0x09AC, 0x09AC },
+{ 0x09AD, 0x09AD, 0x09AD },
+{ 0x09AE, 0x09AE, 0x09AE },
+{ 0x09AF, 0x09AF, 0x09AF },
+{ 0x09B0, 0x09B0, 0x09B0 },
+{ 0x09B2, 0x09B2, 0x09B2 },
+{ 0x09B6, 0x09B6, 0x09B6 },
+{ 0x09B7, 0x09B7, 0x09B7 },
+{ 0x09B8, 0x09B8, 0x09B8 },
+{ 0x09B9, 0x09B9, 0x09B9 },
+{ 0x09BC, 0x09BC, 0x09BC },
+{ 0x09BD, 0x09BD, 0x09BD },
+{ 0x09C1, 0x09C1, 0x09C1 },
+{ 0x09C2, 0x09C2, 0x09C2 },
+{ 0x09C3, 0x09C3, 0x09C3 },
+{ 0x09C4, 0x09C4, 0x09C4 },
+{ 0x09CD, 0x09CD, 0x09CD },
+{ 0x09CE, 0x09CE, 0x09CE },
+{ 0x09DC, 0x09DC, 0x09DC },
+{ 0x09DD, 0x09DD, 0x09DD },
+{ 0x09DF, 0x09DF, 0x09DF },
+{ 0x09E0, 0x09E0, 0x09E0 },
+{ 0x09E1, 0x09E1, 0x09E1 },
+{ 0x09E2, 0x09E2, 0x09E2 },
+{ 0x09E3, 0x09E3, 0x09E3 },
+{ 0x09F0, 0x09F0, 0x09F0 },
+{ 0x09F1, 0x09F1, 0x09F1 },
+{ 0x0A01, 0x0A01, 0x0A01 },
+{ 0x0A02, 0x0A02, 0x0A02 },
+{ 0x0A05, 0x0A05, 0x0A05 },
+{ 0x0A06, 0x0A06, 0x0A06 },
+{ 0x0A07, 0x0A07, 0x0A07 },
+{ 0x0A08, 0x0A08, 0x0A08 },
+{ 0x0A09, 0x0A09, 0x0A09 },
+{ 0x0A0A, 0x0A0A, 0x0A0A },
+{ 0x0A0F, 0x0A0F, 0x0A0F },
+{ 0x0A10, 0x0A10, 0x0A10 },
+{ 0x0A13, 0x0A13, 0x0A13 },
+{ 0x0A14, 0x0A14, 0x0A14 },
+{ 0x0A15, 0x0A15, 0x0A15 },
+{ 0x0A16, 0x0A16, 0x0A16 },
+{ 0x0A17, 0x0A17, 0x0A17 },
+{ 0x0A18, 0x0A18, 0x0A18 },
+{ 0x0A19, 0x0A19, 0x0A19 },
+{ 0x0A1A, 0x0A1A, 0x0A1A },
+{ 0x0A1B, 0x0A1B, 0x0A1B },
+{ 0x0A1C, 0x0A1C, 0x0A1C },
+{ 0x0A1D, 0x0A1D, 0x0A1D },
+{ 0x0A1E, 0x0A1E, 0x0A1E },
+{ 0x0A1F, 0x0A1F, 0x0A1F },
+{ 0x0A20, 0x0A20, 0x0A20 },
+{ 0x0A21, 0x0A21, 0x0A21 },
+{ 0x0A22, 0x0A22, 0x0A22 },
+{ 0x0A23, 0x0A23, 0x0A23 },
+{ 0x0A24, 0x0A24, 0x0A24 },
+{ 0x0A25, 0x0A25, 0x0A25 },
+{ 0x0A26, 0x0A26, 0x0A26 },
+{ 0x0A27, 0x0A27, 0x0A27 },
+{ 0x0A28, 0x0A28, 0x0A28 },
+{ 0x0A2A, 0x0A2A, 0x0A2A },
+{ 0x0A2B, 0x0A2B, 0x0A2B },
+{ 0x0A2C, 0x0A2C, 0x0A2C },
+{ 0x0A2D, 0x0A2D, 0x0A2D },
+{ 0x0A2E, 0x0A2E, 0x0A2E },
+{ 0x0A2F, 0x0A2F, 0x0A2F },
+{ 0x0A30, 0x0A30, 0x0A30 },
+{ 0x0A32, 0x0A32, 0x0A32 },
+{ 0x0A33, 0x0A33, 0x0A33 },
+{ 0x0A35, 0x0A35, 0x0A35 },
+{ 0x0A36, 0x0A36, 0x0A36 },
+{ 0x0A38, 0x0A38, 0x0A38 },
+{ 0x0A39, 0x0A39, 0x0A39 },
+{ 0x0A3C, 0x0A3C, 0x0A3C },
+{ 0x0A41, 0x0A41, 0x0A41 },
+{ 0x0A42, 0x0A42, 0x0A42 },
+{ 0x0A47, 0x0A47, 0x0A47 },
+{ 0x0A48, 0x0A48, 0x0A48 },
+{ 0x0A4B, 0x0A4B, 0x0A4B },
+{ 0x0A4C, 0x0A4C, 0x0A4C },
+{ 0x0A4D, 0x0A4D, 0x0A4D },
+{ 0x0A59, 0x0A59, 0x0A59 },
+{ 0x0A5A, 0x0A5A, 0x0A5A },
+{ 0x0A5B, 0x0A5B, 0x0A5B },
+{ 0x0A5C, 0x0A5C, 0x0A5C },
+{ 0x0A5E, 0x0A5E, 0x0A5E },
+{ 0x0A70, 0x0A70, 0x0A70 },
+{ 0x0A71, 0x0A71, 0x0A71 },
+{ 0x0A72, 0x0A72, 0x0A72 },
+{ 0x0A73, 0x0A73, 0x0A73 },
+{ 0x0A74, 0x0A74, 0x0A74 },
+{ 0x0A81, 0x0A81, 0x0A81 },
+{ 0x0A82, 0x0A82, 0x0A82 },
+{ 0x0A85, 0x0A85, 0x0A85 },
+{ 0x0A86, 0x0A86, 0x0A86 },
+{ 0x0A87, 0x0A87, 0x0A87 },
+{ 0x0A88, 0x0A88, 0x0A88 },
+{ 0x0A89, 0x0A89, 0x0A89 },
+{ 0x0A8A, 0x0A8A, 0x0A8A },
+{ 0x0A8B, 0x0A8B, 0x0A8B },
+{ 0x0A8C, 0x0A8C, 0x0A8C },
+{ 0x0A8D, 0x0A8D, 0x0A8D },
+{ 0x0A8F, 0x0A8F, 0x0A8F },
+{ 0x0A90, 0x0A90, 0x0A90 },
+{ 0x0A91, 0x0A91, 0x0A91 },
+{ 0x0A93, 0x0A93, 0x0A93 },
+{ 0x0A94, 0x0A94, 0x0A94 },
+{ 0x0A95, 0x0A95, 0x0A95 },
+{ 0x0A96, 0x0A96, 0x0A96 },
+{ 0x0A97, 0x0A97, 0x0A97 },
+{ 0x0A98, 0x0A98, 0x0A98 },
+{ 0x0A99, 0x0A99, 0x0A99 },
+{ 0x0A9A, 0x0A9A, 0x0A9A },
+{ 0x0A9B, 0x0A9B, 0x0A9B },
+{ 0x0A9C, 0x0A9C, 0x0A9C },
+{ 0x0A9D, 0x0A9D, 0x0A9D },
+{ 0x0A9E, 0x0A9E, 0x0A9E },
+{ 0x0A9F, 0x0A9F, 0x0A9F },
+{ 0x0AA0, 0x0AA0, 0x0AA0 },
+{ 0x0AA1, 0x0AA1, 0x0AA1 },
+{ 0x0AA2, 0x0AA2, 0x0AA2 },
+{ 0x0AA3, 0x0AA3, 0x0AA3 },
+{ 0x0AA4, 0x0AA4, 0x0AA4 },
+{ 0x0AA5, 0x0AA5, 0x0AA5 },
+{ 0x0AA6, 0x0AA6, 0x0AA6 },
+{ 0x0AA7, 0x0AA7, 0x0AA7 },
+{ 0x0AA8, 0x0AA8, 0x0AA8 },
+{ 0x0AAA, 0x0AAA, 0x0AAA },
+{ 0x0AAB, 0x0AAB, 0x0AAB },
+{ 0x0AAC, 0x0AAC, 0x0AAC },
+{ 0x0AAD, 0x0AAD, 0x0AAD },
+{ 0x0AAE, 0x0AAE, 0x0AAE },
+{ 0x0AAF, 0x0AAF, 0x0AAF },
+{ 0x0AB0, 0x0AB0, 0x0AB0 },
+{ 0x0AB2, 0x0AB2, 0x0AB2 },
+{ 0x0AB3, 0x0AB3, 0x0AB3 },
+{ 0x0AB5, 0x0AB5, 0x0AB5 },
+{ 0x0AB6, 0x0AB6, 0x0AB6 },
+{ 0x0AB7, 0x0AB7, 0x0AB7 },
+{ 0x0AB8, 0x0AB8, 0x0AB8 },
+{ 0x0AB9, 0x0AB9, 0x0AB9 },
+{ 0x0ABC, 0x0ABC, 0x0ABC },
+{ 0x0ABD, 0x0ABD, 0x0ABD },
+{ 0x0AC1, 0x0AC1, 0x0AC1 },
+{ 0x0AC2, 0x0AC2, 0x0AC2 },
+{ 0x0AC3, 0x0AC3, 0x0AC3 },
+{ 0x0AC4, 0x0AC4, 0x0AC4 },
+{ 0x0AC5, 0x0AC5, 0x0AC5 },
+{ 0x0AC7, 0x0AC7, 0x0AC7 },
+{ 0x0AC8, 0x0AC8, 0x0AC8 },
+{ 0x0ACD, 0x0ACD, 0x0ACD },
+{ 0x0AD0, 0x0AD0, 0x0AD0 },
+{ 0x0AE0, 0x0AE0, 0x0AE0 },
+{ 0x0AE1, 0x0AE1, 0x0AE1 },
+{ 0x0AE2, 0x0AE2, 0x0AE2 },
+{ 0x0AE3, 0x0AE3, 0x0AE3 },
+{ 0x0B01, 0x0B01, 0x0B01 },
+{ 0x0B05, 0x0B05, 0x0B05 },
+{ 0x0B06, 0x0B06, 0x0B06 },
+{ 0x0B07, 0x0B07, 0x0B07 },
+{ 0x0B08, 0x0B08, 0x0B08 },
+{ 0x0B09, 0x0B09, 0x0B09 },
+{ 0x0B0A, 0x0B0A, 0x0B0A },
+{ 0x0B0B, 0x0B0B, 0x0B0B },
+{ 0x0B0C, 0x0B0C, 0x0B0C },
+{ 0x0B0F, 0x0B0F, 0x0B0F },
+{ 0x0B10, 0x0B10, 0x0B10 },
+{ 0x0B13, 0x0B13, 0x0B13 },
+{ 0x0B14, 0x0B14, 0x0B14 },
+{ 0x0B15, 0x0B15, 0x0B15 },
+{ 0x0B16, 0x0B16, 0x0B16 },
+{ 0x0B17, 0x0B17, 0x0B17 },
+{ 0x0B18, 0x0B18, 0x0B18 },
+{ 0x0B19, 0x0B19, 0x0B19 },
+{ 0x0B1A, 0x0B1A, 0x0B1A },
+{ 0x0B1B, 0x0B1B, 0x0B1B },
+{ 0x0B1C, 0x0B1C, 0x0B1C },
+{ 0x0B1D, 0x0B1D, 0x0B1D },
+{ 0x0B1E, 0x0B1E, 0x0B1E },
+{ 0x0B1F, 0x0B1F, 0x0B1F },
+{ 0x0B20, 0x0B20, 0x0B20 },
+{ 0x0B21, 0x0B21, 0x0B21 },
+{ 0x0B22, 0x0B22, 0x0B22 },
+{ 0x0B23, 0x0B23, 0x0B23 },
+{ 0x0B24, 0x0B24, 0x0B24 },
+{ 0x0B25, 0x0B25, 0x0B25 },
+{ 0x0B26, 0x0B26, 0x0B26 },
+{ 0x0B27, 0x0B27, 0x0B27 },
+{ 0x0B28, 0x0B28, 0x0B28 },
+{ 0x0B2A, 0x0B2A, 0x0B2A },
+{ 0x0B2B, 0x0B2B, 0x0B2B },
+{ 0x0B2C, 0x0B2C, 0x0B2C },
+{ 0x0B2D, 0x0B2D, 0x0B2D },
+{ 0x0B2E, 0x0B2E, 0x0B2E },
+{ 0x0B2F, 0x0B2F, 0x0B2F },
+{ 0x0B30, 0x0B30, 0x0B30 },
+{ 0x0B32, 0x0B32, 0x0B32 },
+{ 0x0B33, 0x0B33, 0x0B33 },
+{ 0x0B35, 0x0B35, 0x0B35 },
+{ 0x0B36, 0x0B36, 0x0B36 },
+{ 0x0B37, 0x0B37, 0x0B37 },
+{ 0x0B38, 0x0B38, 0x0B38 },
+{ 0x0B39, 0x0B39, 0x0B39 },
+{ 0x0B3C, 0x0B3C, 0x0B3C },
+{ 0x0B3D, 0x0B3D, 0x0B3D },
+{ 0x0B3F, 0x0B3F, 0x0B3F },
+{ 0x0B41, 0x0B41, 0x0B41 },
+{ 0x0B42, 0x0B42, 0x0B42 },
+{ 0x0B43, 0x0B43, 0x0B43 },
+{ 0x0B4D, 0x0B4D, 0x0B4D },
+{ 0x0B56, 0x0B56, 0x0B56 },
+{ 0x0B5C, 0x0B5C, 0x0B5C },
+{ 0x0B5D, 0x0B5D, 0x0B5D },
+{ 0x0B5F, 0x0B5F, 0x0B5F },
+{ 0x0B60, 0x0B60, 0x0B60 },
+{ 0x0B61, 0x0B61, 0x0B61 },
+{ 0x0B71, 0x0B71, 0x0B71 },
+{ 0x0B82, 0x0B82, 0x0B82 },
+{ 0x0B83, 0x0B83, 0x0B83 },
+{ 0x0B85, 0x0B85, 0x0B85 },
+{ 0x0B86, 0x0B86, 0x0B86 },
+{ 0x0B87, 0x0B87, 0x0B87 },
+{ 0x0B88, 0x0B88, 0x0B88 },
+{ 0x0B89, 0x0B89, 0x0B89 },
+{ 0x0B8A, 0x0B8A, 0x0B8A },
+{ 0x0B8E, 0x0B8E, 0x0B8E },
+{ 0x0B8F, 0x0B8F, 0x0B8F },
+{ 0x0B90, 0x0B90, 0x0B90 },
+{ 0x0B92, 0x0B92, 0x0B92 },
+{ 0x0B93, 0x0B93, 0x0B93 },
+{ 0x0B94, 0x0B94, 0x0B94 },
+{ 0x0B95, 0x0B95, 0x0B95 },
+{ 0x0B99, 0x0B99, 0x0B99 },
+{ 0x0B9A, 0x0B9A, 0x0B9A },
+{ 0x0B9C, 0x0B9C, 0x0B9C },
+{ 0x0B9E, 0x0B9E, 0x0B9E },
+{ 0x0B9F, 0x0B9F, 0x0B9F },
+{ 0x0BA3, 0x0BA3, 0x0BA3 },
+{ 0x0BA4, 0x0BA4, 0x0BA4 },
+{ 0x0BA8, 0x0BA8, 0x0BA8 },
+{ 0x0BA9, 0x0BA9, 0x0BA9 },
+{ 0x0BAA, 0x0BAA, 0x0BAA },
+{ 0x0BAE, 0x0BAE, 0x0BAE },
+{ 0x0BAF, 0x0BAF, 0x0BAF },
+{ 0x0BB0, 0x0BB0, 0x0BB0 },
+{ 0x0BB1, 0x0BB1, 0x0BB1 },
+{ 0x0BB2, 0x0BB2, 0x0BB2 },
+{ 0x0BB3, 0x0BB3, 0x0BB3 },
+{ 0x0BB4, 0x0BB4, 0x0BB4 },
+{ 0x0BB5, 0x0BB5, 0x0BB5 },
+{ 0x0BB6, 0x0BB6, 0x0BB6 },
+{ 0x0BB7, 0x0BB7, 0x0BB7 },
+{ 0x0BB8, 0x0BB8, 0x0BB8 },
+{ 0x0BB9, 0x0BB9, 0x0BB9 },
+{ 0x0BC0, 0x0BC0, 0x0BC0 },
+{ 0x0BCD, 0x0BCD, 0x0BCD },
+{ 0x0C05, 0x0C05, 0x0C05 },
+{ 0x0C06, 0x0C06, 0x0C06 },
+{ 0x0C07, 0x0C07, 0x0C07 },
+{ 0x0C08, 0x0C08, 0x0C08 },
+{ 0x0C09, 0x0C09, 0x0C09 },
+{ 0x0C0A, 0x0C0A, 0x0C0A },
+{ 0x0C0B, 0x0C0B, 0x0C0B },
+{ 0x0C0C, 0x0C0C, 0x0C0C },
+{ 0x0C0E, 0x0C0E, 0x0C0E },
+{ 0x0C0F, 0x0C0F, 0x0C0F },
+{ 0x0C10, 0x0C10, 0x0C10 },
+{ 0x0C12, 0x0C12, 0x0C12 },
+{ 0x0C13, 0x0C13, 0x0C13 },
+{ 0x0C14, 0x0C14, 0x0C14 },
+{ 0x0C15, 0x0C15, 0x0C15 },
+{ 0x0C16, 0x0C16, 0x0C16 },
+{ 0x0C17, 0x0C17, 0x0C17 },
+{ 0x0C18, 0x0C18, 0x0C18 },
+{ 0x0C19, 0x0C19, 0x0C19 },
+{ 0x0C1A, 0x0C1A, 0x0C1A },
+{ 0x0C1B, 0x0C1B, 0x0C1B },
+{ 0x0C1C, 0x0C1C, 0x0C1C },
+{ 0x0C1D, 0x0C1D, 0x0C1D },
+{ 0x0C1E, 0x0C1E, 0x0C1E },
+{ 0x0C1F, 0x0C1F, 0x0C1F },
+{ 0x0C20, 0x0C20, 0x0C20 },
+{ 0x0C21, 0x0C21, 0x0C21 },
+{ 0x0C22, 0x0C22, 0x0C22 },
+{ 0x0C23, 0x0C23, 0x0C23 },
+{ 0x0C24, 0x0C24, 0x0C24 },
+{ 0x0C25, 0x0C25, 0x0C25 },
+{ 0x0C26, 0x0C26, 0x0C26 },
+{ 0x0C27, 0x0C27, 0x0C27 },
+{ 0x0C28, 0x0C28, 0x0C28 },
+{ 0x0C2A, 0x0C2A, 0x0C2A },
+{ 0x0C2B, 0x0C2B, 0x0C2B },
+{ 0x0C2C, 0x0C2C, 0x0C2C },
+{ 0x0C2D, 0x0C2D, 0x0C2D },
+{ 0x0C2E, 0x0C2E, 0x0C2E },
+{ 0x0C2F, 0x0C2F, 0x0C2F },
+{ 0x0C30, 0x0C30, 0x0C30 },
+{ 0x0C31, 0x0C31, 0x0C31 },
+{ 0x0C32, 0x0C32, 0x0C32 },
+{ 0x0C33, 0x0C33, 0x0C33 },
+{ 0x0C35, 0x0C35, 0x0C35 },
+{ 0x0C36, 0x0C36, 0x0C36 },
+{ 0x0C37, 0x0C37, 0x0C37 },
+{ 0x0C38, 0x0C38, 0x0C38 },
+{ 0x0C39, 0x0C39, 0x0C39 },
+{ 0x0C3E, 0x0C3E, 0x0C3E },
+{ 0x0C3F, 0x0C3F, 0x0C3F },
+{ 0x0C40, 0x0C40, 0x0C40 },
+{ 0x0C46, 0x0C46, 0x0C46 },
+{ 0x0C47, 0x0C47, 0x0C47 },
+{ 0x0C48, 0x0C48, 0x0C48 },
+{ 0x0C4A, 0x0C4A, 0x0C4A },
+{ 0x0C4B, 0x0C4B, 0x0C4B },
+{ 0x0C4C, 0x0C4C, 0x0C4C },
+{ 0x0C4D, 0x0C4D, 0x0C4D },
+{ 0x0C55, 0x0C55, 0x0C55 },
+{ 0x0C56, 0x0C56, 0x0C56 },
+{ 0x0C60, 0x0C60, 0x0C60 },
+{ 0x0C61, 0x0C61, 0x0C61 },
+{ 0x0C85, 0x0C85, 0x0C85 },
+{ 0x0C86, 0x0C86, 0x0C86 },
+{ 0x0C87, 0x0C87, 0x0C87 },
+{ 0x0C88, 0x0C88, 0x0C88 },
+{ 0x0C89, 0x0C89, 0x0C89 },
+{ 0x0C8A, 0x0C8A, 0x0C8A },
+{ 0x0C8B, 0x0C8B, 0x0C8B },
+{ 0x0C8C, 0x0C8C, 0x0C8C },
+{ 0x0C8E, 0x0C8E, 0x0C8E },
+{ 0x0C8F, 0x0C8F, 0x0C8F },
+{ 0x0C90, 0x0C90, 0x0C90 },
+{ 0x0C92, 0x0C92, 0x0C92 },
+{ 0x0C93, 0x0C93, 0x0C93 },
+{ 0x0C94, 0x0C94, 0x0C94 },
+{ 0x0C95, 0x0C95, 0x0C95 },
+{ 0x0C96, 0x0C96, 0x0C96 },
+{ 0x0C97, 0x0C97, 0x0C97 },
+{ 0x0C98, 0x0C98, 0x0C98 },
+{ 0x0C99, 0x0C99, 0x0C99 },
+{ 0x0C9A, 0x0C9A, 0x0C9A },
+{ 0x0C9B, 0x0C9B, 0x0C9B },
+{ 0x0C9C, 0x0C9C, 0x0C9C },
+{ 0x0C9D, 0x0C9D, 0x0C9D },
+{ 0x0C9E, 0x0C9E, 0x0C9E },
+{ 0x0C9F, 0x0C9F, 0x0C9F },
+{ 0x0CA0, 0x0CA0, 0x0CA0 },
+{ 0x0CA1, 0x0CA1, 0x0CA1 },
+{ 0x0CA2, 0x0CA2, 0x0CA2 },
+{ 0x0CA3, 0x0CA3, 0x0CA3 },
+{ 0x0CA4, 0x0CA4, 0x0CA4 },
+{ 0x0CA5, 0x0CA5, 0x0CA5 },
+{ 0x0CA6, 0x0CA6, 0x0CA6 },
+{ 0x0CA7, 0x0CA7, 0x0CA7 },
+{ 0x0CA8, 0x0CA8, 0x0CA8 },
+{ 0x0CAA, 0x0CAA, 0x0CAA },
+{ 0x0CAB, 0x0CAB, 0x0CAB },
+{ 0x0CAC, 0x0CAC, 0x0CAC },
+{ 0x0CAD, 0x0CAD, 0x0CAD },
+{ 0x0CAE, 0x0CAE, 0x0CAE },
+{ 0x0CAF, 0x0CAF, 0x0CAF },
+{ 0x0CB0, 0x0CB0, 0x0CB0 },
+{ 0x0CB1, 0x0CB1, 0x0CB1 },
+{ 0x0CB2, 0x0CB2, 0x0CB2 },
+{ 0x0CB3, 0x0CB3, 0x0CB3 },
+{ 0x0CB5, 0x0CB5, 0x0CB5 },
+{ 0x0CB6, 0x0CB6, 0x0CB6 },
+{ 0x0CB7, 0x0CB7, 0x0CB7 },
+{ 0x0CB8, 0x0CB8, 0x0CB8 },
+{ 0x0CB9, 0x0CB9, 0x0CB9 },
+{ 0x0CBC, 0x0CBC, 0x0CBC },
+{ 0x0CBD, 0x0CBD, 0x0CBD },
+{ 0x0CBF, 0x0CBF, 0x0CBF },
+{ 0x0CC6, 0x0CC6, 0x0CC6 },
+{ 0x0CCC, 0x0CCC, 0x0CCC },
+{ 0x0CCD, 0x0CCD, 0x0CCD },
+{ 0x0CDE, 0x0CDE, 0x0CDE },
+{ 0x0CE0, 0x0CE0, 0x0CE0 },
+{ 0x0CE1, 0x0CE1, 0x0CE1 },
+{ 0x0D05, 0x0D05, 0x0D05 },
+{ 0x0D06, 0x0D06, 0x0D06 },
+{ 0x0D07, 0x0D07, 0x0D07 },
+{ 0x0D08, 0x0D08, 0x0D08 },
+{ 0x0D09, 0x0D09, 0x0D09 },
+{ 0x0D0A, 0x0D0A, 0x0D0A },
+{ 0x0D0B, 0x0D0B, 0x0D0B },
+{ 0x0D0C, 0x0D0C, 0x0D0C },
+{ 0x0D0E, 0x0D0E, 0x0D0E },
+{ 0x0D0F, 0x0D0F, 0x0D0F },
+{ 0x0D10, 0x0D10, 0x0D10 },
+{ 0x0D12, 0x0D12, 0x0D12 },
+{ 0x0D13, 0x0D13, 0x0D13 },
+{ 0x0D14, 0x0D14, 0x0D14 },
+{ 0x0D15, 0x0D15, 0x0D15 },
+{ 0x0D16, 0x0D16, 0x0D16 },
+{ 0x0D17, 0x0D17, 0x0D17 },
+{ 0x0D18, 0x0D18, 0x0D18 },
+{ 0x0D19, 0x0D19, 0x0D19 },
+{ 0x0D1A, 0x0D1A, 0x0D1A },
+{ 0x0D1B, 0x0D1B, 0x0D1B },
+{ 0x0D1C, 0x0D1C, 0x0D1C },
+{ 0x0D1D, 0x0D1D, 0x0D1D },
+{ 0x0D1E, 0x0D1E, 0x0D1E },
+{ 0x0D1F, 0x0D1F, 0x0D1F },
+{ 0x0D20, 0x0D20, 0x0D20 },
+{ 0x0D21, 0x0D21, 0x0D21 },
+{ 0x0D22, 0x0D22, 0x0D22 },
+{ 0x0D23, 0x0D23, 0x0D23 },
+{ 0x0D24, 0x0D24, 0x0D24 },
+{ 0x0D25, 0x0D25, 0x0D25 },
+{ 0x0D26, 0x0D26, 0x0D26 },
+{ 0x0D27, 0x0D27, 0x0D27 },
+{ 0x0D28, 0x0D28, 0x0D28 },
+{ 0x0D2A, 0x0D2A, 0x0D2A },
+{ 0x0D2B, 0x0D2B, 0x0D2B },
+{ 0x0D2C, 0x0D2C, 0x0D2C },
+{ 0x0D2D, 0x0D2D, 0x0D2D },
+{ 0x0D2E, 0x0D2E, 0x0D2E },
+{ 0x0D2F, 0x0D2F, 0x0D2F },
+{ 0x0D30, 0x0D30, 0x0D30 },
+{ 0x0D31, 0x0D31, 0x0D31 },
+{ 0x0D32, 0x0D32, 0x0D32 },
+{ 0x0D33, 0x0D33, 0x0D33 },
+{ 0x0D34, 0x0D34, 0x0D34 },
+{ 0x0D35, 0x0D35, 0x0D35 },
+{ 0x0D36, 0x0D36, 0x0D36 },
+{ 0x0D37, 0x0D37, 0x0D37 },
+{ 0x0D38, 0x0D38, 0x0D38 },
+{ 0x0D39, 0x0D39, 0x0D39 },
+{ 0x0D41, 0x0D41, 0x0D41 },
+{ 0x0D42, 0x0D42, 0x0D42 },
+{ 0x0D43, 0x0D43, 0x0D43 },
+{ 0x0D4D, 0x0D4D, 0x0D4D },
+{ 0x0D60, 0x0D60, 0x0D60 },
+{ 0x0D61, 0x0D61, 0x0D61 },
+{ 0x0D85, 0x0D85, 0x0D85 },
+{ 0x0D86, 0x0D86, 0x0D86 },
+{ 0x0D87, 0x0D87, 0x0D87 },
+{ 0x0D88, 0x0D88, 0x0D88 },
+{ 0x0D89, 0x0D89, 0x0D89 },
+{ 0x0D8A, 0x0D8A, 0x0D8A },
+{ 0x0D8B, 0x0D8B, 0x0D8B },
+{ 0x0D8C, 0x0D8C, 0x0D8C },
+{ 0x0D8D, 0x0D8D, 0x0D8D },
+{ 0x0D8E, 0x0D8E, 0x0D8E },
+{ 0x0D8F, 0x0D8F, 0x0D8F },
+{ 0x0D90, 0x0D90, 0x0D90 },
+{ 0x0D91, 0x0D91, 0x0D91 },
+{ 0x0D92, 0x0D92, 0x0D92 },
+{ 0x0D93, 0x0D93, 0x0D93 },
+{ 0x0D94, 0x0D94, 0x0D94 },
+{ 0x0D95, 0x0D95, 0x0D95 },
+{ 0x0D96, 0x0D96, 0x0D96 },
+{ 0x0D9A, 0x0D9A, 0x0D9A },
+{ 0x0D9B, 0x0D9B, 0x0D9B },
+{ 0x0D9C, 0x0D9C, 0x0D9C },
+{ 0x0D9D, 0x0D9D, 0x0D9D },
+{ 0x0D9E, 0x0D9E, 0x0D9E },
+{ 0x0D9F, 0x0D9F, 0x0D9F },
+{ 0x0DA0, 0x0DA0, 0x0DA0 },
+{ 0x0DA1, 0x0DA1, 0x0DA1 },
+{ 0x0DA2, 0x0DA2, 0x0DA2 },
+{ 0x0DA3, 0x0DA3, 0x0DA3 },
+{ 0x0DA4, 0x0DA4, 0x0DA4 },
+{ 0x0DA5, 0x0DA5, 0x0DA5 },
+{ 0x0DA6, 0x0DA6, 0x0DA6 },
+{ 0x0DA7, 0x0DA7, 0x0DA7 },
+{ 0x0DA8, 0x0DA8, 0x0DA8 },
+{ 0x0DA9, 0x0DA9, 0x0DA9 },
+{ 0x0DAA, 0x0DAA, 0x0DAA },
+{ 0x0DAB, 0x0DAB, 0x0DAB },
+{ 0x0DAC, 0x0DAC, 0x0DAC },
+{ 0x0DAD, 0x0DAD, 0x0DAD },
+{ 0x0DAE, 0x0DAE, 0x0DAE },
+{ 0x0DAF, 0x0DAF, 0x0DAF },
+{ 0x0DB0, 0x0DB0, 0x0DB0 },
+{ 0x0DB1, 0x0DB1, 0x0DB1 },
+{ 0x0DB3, 0x0DB3, 0x0DB3 },
+{ 0x0DB4, 0x0DB4, 0x0DB4 },
+{ 0x0DB5, 0x0DB5, 0x0DB5 },
+{ 0x0DB6, 0x0DB6, 0x0DB6 },
+{ 0x0DB7, 0x0DB7, 0x0DB7 },
+{ 0x0DB8, 0x0DB8, 0x0DB8 },
+{ 0x0DB9, 0x0DB9, 0x0DB9 },
+{ 0x0DBA, 0x0DBA, 0x0DBA },
+{ 0x0DBB, 0x0DBB, 0x0DBB },
+{ 0x0DBD, 0x0DBD, 0x0DBD },
+{ 0x0DC0, 0x0DC0, 0x0DC0 },
+{ 0x0DC1, 0x0DC1, 0x0DC1 },
+{ 0x0DC2, 0x0DC2, 0x0DC2 },
+{ 0x0DC3, 0x0DC3, 0x0DC3 },
+{ 0x0DC4, 0x0DC4, 0x0DC4 },
+{ 0x0DC5, 0x0DC5, 0x0DC5 },
+{ 0x0DC6, 0x0DC6, 0x0DC6 },
+{ 0x0DCA, 0x0DCA, 0x0DCA },
+{ 0x0DD2, 0x0DD2, 0x0DD2 },
+{ 0x0DD3, 0x0DD3, 0x0DD3 },
+{ 0x0DD4, 0x0DD4, 0x0DD4 },
+{ 0x0DD6, 0x0DD6, 0x0DD6 },
+{ 0x0E01, 0x0E01, 0x0E01 },
+{ 0x0E02, 0x0E02, 0x0E02 },
+{ 0x0E03, 0x0E03, 0x0E03 },
+{ 0x0E04, 0x0E04, 0x0E04 },
+{ 0x0E05, 0x0E05, 0x0E05 },
+{ 0x0E06, 0x0E06, 0x0E06 },
+{ 0x0E07, 0x0E07, 0x0E07 },
+{ 0x0E08, 0x0E08, 0x0E08 },
+{ 0x0E09, 0x0E09, 0x0E09 },
+{ 0x0E0A, 0x0E0A, 0x0E0A },
+{ 0x0E0B, 0x0E0B, 0x0E0B },
+{ 0x0E0C, 0x0E0C, 0x0E0C },
+{ 0x0E0D, 0x0E0D, 0x0E0D },
+{ 0x0E0E, 0x0E0E, 0x0E0E },
+{ 0x0E0F, 0x0E0F, 0x0E0F },
+{ 0x0E10, 0x0E10, 0x0E10 },
+{ 0x0E11, 0x0E11, 0x0E11 },
+{ 0x0E12, 0x0E12, 0x0E12 },
+{ 0x0E13, 0x0E13, 0x0E13 },
+{ 0x0E14, 0x0E14, 0x0E14 },
+{ 0x0E15, 0x0E15, 0x0E15 },
+{ 0x0E16, 0x0E16, 0x0E16 },
+{ 0x0E17, 0x0E17, 0x0E17 },
+{ 0x0E18, 0x0E18, 0x0E18 },
+{ 0x0E19, 0x0E19, 0x0E19 },
+{ 0x0E1A, 0x0E1A, 0x0E1A },
+{ 0x0E1B, 0x0E1B, 0x0E1B },
+{ 0x0E1C, 0x0E1C, 0x0E1C },
+{ 0x0E1D, 0x0E1D, 0x0E1D },
+{ 0x0E1E, 0x0E1E, 0x0E1E },
+{ 0x0E1F, 0x0E1F, 0x0E1F },
+{ 0x0E20, 0x0E20, 0x0E20 },
+{ 0x0E21, 0x0E21, 0x0E21 },
+{ 0x0E22, 0x0E22, 0x0E22 },
+{ 0x0E23, 0x0E23, 0x0E23 },
+{ 0x0E24, 0x0E24, 0x0E24 },
+{ 0x0E25, 0x0E25, 0x0E25 },
+{ 0x0E26, 0x0E26, 0x0E26 },
+{ 0x0E27, 0x0E27, 0x0E27 },
+{ 0x0E28, 0x0E28, 0x0E28 },
+{ 0x0E29, 0x0E29, 0x0E29 },
+{ 0x0E2A, 0x0E2A, 0x0E2A },
+{ 0x0E2B, 0x0E2B, 0x0E2B },
+{ 0x0E2C, 0x0E2C, 0x0E2C },
+{ 0x0E2D, 0x0E2D, 0x0E2D },
+{ 0x0E2E, 0x0E2E, 0x0E2E },
+{ 0x0E2F, 0x0E2F, 0x0E2F },
+{ 0x0E30, 0x0E30, 0x0E30 },
+{ 0x0E31, 0x0E31, 0x0E31 },
+{ 0x0E32, 0x0E32, 0x0E32 },
+{ 0x0E33, 0x0E33, 0x0E33 },
+{ 0x0E34, 0x0E34, 0x0E34 },
+{ 0x0E35, 0x0E35, 0x0E35 },
+{ 0x0E36, 0x0E36, 0x0E36 },
+{ 0x0E37, 0x0E37, 0x0E37 },
+{ 0x0E38, 0x0E38, 0x0E38 },
+{ 0x0E39, 0x0E39, 0x0E39 },
+{ 0x0E3A, 0x0E3A, 0x0E3A },
+{ 0x0E40, 0x0E40, 0x0E40 },
+{ 0x0E41, 0x0E41, 0x0E41 },
+{ 0x0E42, 0x0E42, 0x0E42 },
+{ 0x0E43, 0x0E43, 0x0E43 },
+{ 0x0E44, 0x0E44, 0x0E44 },
+{ 0x0E45, 0x0E45, 0x0E45 },
+{ 0x0E46, 0x0E46, 0x0E46 },
+{ 0x0E47, 0x0E47, 0x0E47 },
+{ 0x0E48, 0x0E48, 0x0E48 },
+{ 0x0E49, 0x0E49, 0x0E49 },
+{ 0x0E4A, 0x0E4A, 0x0E4A },
+{ 0x0E4B, 0x0E4B, 0x0E4B },
+{ 0x0E4C, 0x0E4C, 0x0E4C },
+{ 0x0E4D, 0x0E4D, 0x0E4D },
+{ 0x0E4E, 0x0E4E, 0x0E4E },
+{ 0x0E81, 0x0E81, 0x0E81 },
+{ 0x0E82, 0x0E82, 0x0E82 },
+{ 0x0E84, 0x0E84, 0x0E84 },
+{ 0x0E87, 0x0E87, 0x0E87 },
+{ 0x0E88, 0x0E88, 0x0E88 },
+{ 0x0E8A, 0x0E8A, 0x0E8A },
+{ 0x0E8D, 0x0E8D, 0x0E8D },
+{ 0x0E94, 0x0E94, 0x0E94 },
+{ 0x0E95, 0x0E95, 0x0E95 },
+{ 0x0E96, 0x0E96, 0x0E96 },
+{ 0x0E97, 0x0E97, 0x0E97 },
+{ 0x0E99, 0x0E99, 0x0E99 },
+{ 0x0E9A, 0x0E9A, 0x0E9A },
+{ 0x0E9B, 0x0E9B, 0x0E9B },
+{ 0x0E9C, 0x0E9C, 0x0E9C },
+{ 0x0E9D, 0x0E9D, 0x0E9D },
+{ 0x0E9E, 0x0E9E, 0x0E9E },
+{ 0x0E9F, 0x0E9F, 0x0E9F },
+{ 0x0EA1, 0x0EA1, 0x0EA1 },
+{ 0x0EA2, 0x0EA2, 0x0EA2 },
+{ 0x0EA3, 0x0EA3, 0x0EA3 },
+{ 0x0EA5, 0x0EA5, 0x0EA5 },
+{ 0x0EA7, 0x0EA7, 0x0EA7 },
+{ 0x0EAA, 0x0EAA, 0x0EAA },
+{ 0x0EAB, 0x0EAB, 0x0EAB },
+{ 0x0EAD, 0x0EAD, 0x0EAD },
+{ 0x0EAE, 0x0EAE, 0x0EAE },
+{ 0x0EAF, 0x0EAF, 0x0EAF },
+{ 0x0EB0, 0x0EB0, 0x0EB0 },
+{ 0x0EB1, 0x0EB1, 0x0EB1 },
+{ 0x0EB2, 0x0EB2, 0x0EB2 },
+{ 0x0EB3, 0x0EB3, 0x0EB3 },
+{ 0x0EB4, 0x0EB4, 0x0EB4 },
+{ 0x0EB5, 0x0EB5, 0x0EB5 },
+{ 0x0EB6, 0x0EB6, 0x0EB6 },
+{ 0x0EB7, 0x0EB7, 0x0EB7 },
+{ 0x0EB8, 0x0EB8, 0x0EB8 },
+{ 0x0EB9, 0x0EB9, 0x0EB9 },
+{ 0x0EBB, 0x0EBB, 0x0EBB },
+{ 0x0EBC, 0x0EBC, 0x0EBC },
+{ 0x0EBD, 0x0EBD, 0x0EBD },
+{ 0x0EC0, 0x0EC0, 0x0EC0 },
+{ 0x0EC1, 0x0EC1, 0x0EC1 },
+{ 0x0EC2, 0x0EC2, 0x0EC2 },
+{ 0x0EC3, 0x0EC3, 0x0EC3 },
+{ 0x0EC4, 0x0EC4, 0x0EC4 },
+{ 0x0EC6, 0x0EC6, 0x0EC6 },
+{ 0x0EC8, 0x0EC8, 0x0EC8 },
+{ 0x0EC9, 0x0EC9, 0x0EC9 },
+{ 0x0ECA, 0x0ECA, 0x0ECA },
+{ 0x0ECB, 0x0ECB, 0x0ECB },
+{ 0x0ECC, 0x0ECC, 0x0ECC },
+{ 0x0ECD, 0x0ECD, 0x0ECD },
+{ 0x0EDC, 0x0EDC, 0x0EDC },
+{ 0x0EDD, 0x0EDD, 0x0EDD },
+{ 0x0F00, 0x0F00, 0x0F00 },
+{ 0x0F18, 0x0F18, 0x0F18 },
+{ 0x0F19, 0x0F19, 0x0F19 },
+{ 0x0F35, 0x0F35, 0x0F35 },
+{ 0x0F37, 0x0F37, 0x0F37 },
+{ 0x0F39, 0x0F39, 0x0F39 },
+{ 0x0F40, 0x0F40, 0x0F40 },
+{ 0x0F41, 0x0F41, 0x0F41 },
+{ 0x0F42, 0x0F42, 0x0F42 },
+{ 0x0F43, 0x0F43, 0x0F43 },
+{ 0x0F44, 0x0F44, 0x0F44 },
+{ 0x0F45, 0x0F45, 0x0F45 },
+{ 0x0F46, 0x0F46, 0x0F46 },
+{ 0x0F47, 0x0F47, 0x0F47 },
+{ 0x0F49, 0x0F49, 0x0F49 },
+{ 0x0F4A, 0x0F4A, 0x0F4A },
+{ 0x0F4B, 0x0F4B, 0x0F4B },
+{ 0x0F4C, 0x0F4C, 0x0F4C },
+{ 0x0F4D, 0x0F4D, 0x0F4D },
+{ 0x0F4E, 0x0F4E, 0x0F4E },
+{ 0x0F4F, 0x0F4F, 0x0F4F },
+{ 0x0F50, 0x0F50, 0x0F50 },
+{ 0x0F51, 0x0F51, 0x0F51 },
+{ 0x0F52, 0x0F52, 0x0F52 },
+{ 0x0F53, 0x0F53, 0x0F53 },
+{ 0x0F54, 0x0F54, 0x0F54 },
+{ 0x0F55, 0x0F55, 0x0F55 },
+{ 0x0F56, 0x0F56, 0x0F56 },
+{ 0x0F57, 0x0F57, 0x0F57 },
+{ 0x0F58, 0x0F58, 0x0F58 },
+{ 0x0F59, 0x0F59, 0x0F59 },
+{ 0x0F5A, 0x0F5A, 0x0F5A },
+{ 0x0F5B, 0x0F5B, 0x0F5B },
+{ 0x0F5C, 0x0F5C, 0x0F5C },
+{ 0x0F5D, 0x0F5D, 0x0F5D },
+{ 0x0F5E, 0x0F5E, 0x0F5E },
+{ 0x0F5F, 0x0F5F, 0x0F5F },
+{ 0x0F60, 0x0F60, 0x0F60 },
+{ 0x0F61, 0x0F61, 0x0F61 },
+{ 0x0F62, 0x0F62, 0x0F62 },
+{ 0x0F63, 0x0F63, 0x0F63 },
+{ 0x0F64, 0x0F64, 0x0F64 },
+{ 0x0F65, 0x0F65, 0x0F65 },
+{ 0x0F66, 0x0F66, 0x0F66 },
+{ 0x0F67, 0x0F67, 0x0F67 },
+{ 0x0F68, 0x0F68, 0x0F68 },
+{ 0x0F69, 0x0F69, 0x0F69 },
+{ 0x0F6A, 0x0F6A, 0x0F6A },
+{ 0x0F71, 0x0F71, 0x0F71 },
+{ 0x0F72, 0x0F72, 0x0F72 },
+{ 0x0F73, 0x0F73, 0x0F73 },
+{ 0x0F74, 0x0F74, 0x0F74 },
+{ 0x0F75, 0x0F75, 0x0F75 },
+{ 0x0F76, 0x0F76, 0x0F76 },
+{ 0x0F77, 0x0F77, 0x0F77 },
+{ 0x0F78, 0x0F78, 0x0F78 },
+{ 0x0F79, 0x0F79, 0x0F79 },
+{ 0x0F7A, 0x0F7A, 0x0F7A },
+{ 0x0F7B, 0x0F7B, 0x0F7B },
+{ 0x0F7C, 0x0F7C, 0x0F7C },
+{ 0x0F7D, 0x0F7D, 0x0F7D },
+{ 0x0F7E, 0x0F7E, 0x0F7E },
+{ 0x0F80, 0x0F80, 0x0F80 },
+{ 0x0F81, 0x0F81, 0x0F81 },
+{ 0x0F82, 0x0F82, 0x0F82 },
+{ 0x0F83, 0x0F83, 0x0F83 },
+{ 0x0F84, 0x0F84, 0x0F84 },
+{ 0x0F86, 0x0F86, 0x0F86 },
+{ 0x0F87, 0x0F87, 0x0F87 },
+{ 0x0F88, 0x0F88, 0x0F88 },
+{ 0x0F89, 0x0F89, 0x0F89 },
+{ 0x0F8A, 0x0F8A, 0x0F8A },
+{ 0x0F8B, 0x0F8B, 0x0F8B },
+{ 0x0F90, 0x0F90, 0x0F90 },
+{ 0x0F91, 0x0F91, 0x0F91 },
+{ 0x0F92, 0x0F92, 0x0F92 },
+{ 0x0F93, 0x0F93, 0x0F93 },
+{ 0x0F94, 0x0F94, 0x0F94 },
+{ 0x0F95, 0x0F95, 0x0F95 },
+{ 0x0F96, 0x0F96, 0x0F96 },
+{ 0x0F97, 0x0F97, 0x0F97 },
+{ 0x0F99, 0x0F99, 0x0F99 },
+{ 0x0F9A, 0x0F9A, 0x0F9A },
+{ 0x0F9B, 0x0F9B, 0x0F9B },
+{ 0x0F9C, 0x0F9C, 0x0F9C },
+{ 0x0F9D, 0x0F9D, 0x0F9D },
+{ 0x0F9E, 0x0F9E, 0x0F9E },
+{ 0x0F9F, 0x0F9F, 0x0F9F },
+{ 0x0FA0, 0x0FA0, 0x0FA0 },
+{ 0x0FA1, 0x0FA1, 0x0FA1 },
+{ 0x0FA2, 0x0FA2, 0x0FA2 },
+{ 0x0FA3, 0x0FA3, 0x0FA3 },
+{ 0x0FA4, 0x0FA4, 0x0FA4 },
+{ 0x0FA5, 0x0FA5, 0x0FA5 },
+{ 0x0FA6, 0x0FA6, 0x0FA6 },
+{ 0x0FA7, 0x0FA7, 0x0FA7 },
+{ 0x0FA8, 0x0FA8, 0x0FA8 },
+{ 0x0FA9, 0x0FA9, 0x0FA9 },
+{ 0x0FAA, 0x0FAA, 0x0FAA },
+{ 0x0FAB, 0x0FAB, 0x0FAB },
+{ 0x0FAC, 0x0FAC, 0x0FAC },
+{ 0x0FAD, 0x0FAD, 0x0FAD },
+{ 0x0FAE, 0x0FAE, 0x0FAE },
+{ 0x0FAF, 0x0FAF, 0x0FAF },
+{ 0x0FB0, 0x0FB0, 0x0FB0 },
+{ 0x0FB1, 0x0FB1, 0x0FB1 },
+{ 0x0FB2, 0x0FB2, 0x0FB2 },
+{ 0x0FB3, 0x0FB3, 0x0FB3 },
+{ 0x0FB4, 0x0FB4, 0x0FB4 },
+{ 0x0FB5, 0x0FB5, 0x0FB5 },
+{ 0x0FB6, 0x0FB6, 0x0FB6 },
+{ 0x0FB7, 0x0FB7, 0x0FB7 },
+{ 0x0FB8, 0x0FB8, 0x0FB8 },
+{ 0x0FB9, 0x0FB9, 0x0FB9 },
+{ 0x0FBA, 0x0FBA, 0x0FBA },
+{ 0x0FBB, 0x0FBB, 0x0FBB },
+{ 0x0FBC, 0x0FBC, 0x0FBC },
+{ 0x0FC6, 0x0FC6, 0x0FC6 },
+{ 0x1000, 0x1000, 0x1000 },
+{ 0x1001, 0x1001, 0x1001 },
+{ 0x1002, 0x1002, 0x1002 },
+{ 0x1003, 0x1003, 0x1003 },
+{ 0x1004, 0x1004, 0x1004 },
+{ 0x1005, 0x1005, 0x1005 },
+{ 0x1006, 0x1006, 0x1006 },
+{ 0x1007, 0x1007, 0x1007 },
+{ 0x1008, 0x1008, 0x1008 },
+{ 0x1009, 0x1009, 0x1009 },
+{ 0x100A, 0x100A, 0x100A },
+{ 0x100B, 0x100B, 0x100B },
+{ 0x100C, 0x100C, 0x100C },
+{ 0x100D, 0x100D, 0x100D },
+{ 0x100E, 0x100E, 0x100E },
+{ 0x100F, 0x100F, 0x100F },
+{ 0x1010, 0x1010, 0x1010 },
+{ 0x1011, 0x1011, 0x1011 },
+{ 0x1012, 0x1012, 0x1012 },
+{ 0x1013, 0x1013, 0x1013 },
+{ 0x1014, 0x1014, 0x1014 },
+{ 0x1015, 0x1015, 0x1015 },
+{ 0x1016, 0x1016, 0x1016 },
+{ 0x1017, 0x1017, 0x1017 },
+{ 0x1018, 0x1018, 0x1018 },
+{ 0x1019, 0x1019, 0x1019 },
+{ 0x101A, 0x101A, 0x101A },
+{ 0x101B, 0x101B, 0x101B },
+{ 0x101C, 0x101C, 0x101C },
+{ 0x101D, 0x101D, 0x101D },
+{ 0x101E, 0x101E, 0x101E },
+{ 0x101F, 0x101F, 0x101F },
+{ 0x1020, 0x1020, 0x1020 },
+{ 0x1021, 0x1021, 0x1021 },
+{ 0x1023, 0x1023, 0x1023 },
+{ 0x1024, 0x1024, 0x1024 },
+{ 0x1025, 0x1025, 0x1025 },
+{ 0x1026, 0x1026, 0x1026 },
+{ 0x1027, 0x1027, 0x1027 },
+{ 0x1029, 0x1029, 0x1029 },
+{ 0x102A, 0x102A, 0x102A },
+{ 0x102D, 0x102D, 0x102D },
+{ 0x102E, 0x102E, 0x102E },
+{ 0x102F, 0x102F, 0x102F },
+{ 0x1030, 0x1030, 0x1030 },
+{ 0x1032, 0x1032, 0x1032 },
+{ 0x1036, 0x1036, 0x1036 },
+{ 0x1037, 0x1037, 0x1037 },
+{ 0x1039, 0x1039, 0x1039 },
+{ 0x1050, 0x1050, 0x1050 },
+{ 0x1051, 0x1051, 0x1051 },
+{ 0x1052, 0x1052, 0x1052 },
+{ 0x1053, 0x1053, 0x1053 },
+{ 0x1054, 0x1054, 0x1054 },
+{ 0x1055, 0x1055, 0x1055 },
+{ 0x1058, 0x1058, 0x1058 },
+{ 0x1059, 0x1059, 0x1059 },
+{ 0x10A0, 0x10A0, 0x2D00 },
+{ 0x10A1, 0x10A1, 0x2D01 },
+{ 0x10A2, 0x10A2, 0x2D02 },
+{ 0x10A3, 0x10A3, 0x2D03 },
+{ 0x10A4, 0x10A4, 0x2D04 },
+{ 0x10A5, 0x10A5, 0x2D05 },
+{ 0x10A6, 0x10A6, 0x2D06 },
+{ 0x10A7, 0x10A7, 0x2D07 },
+{ 0x10A8, 0x10A8, 0x2D08 },
+{ 0x10A9, 0x10A9, 0x2D09 },
+{ 0x10AA, 0x10AA, 0x2D0A },
+{ 0x10AB, 0x10AB, 0x2D0B },
+{ 0x10AC, 0x10AC, 0x2D0C },
+{ 0x10AD, 0x10AD, 0x2D0D },
+{ 0x10AE, 0x10AE, 0x2D0E },
+{ 0x10AF, 0x10AF, 0x2D0F },
+{ 0x10B0, 0x10B0, 0x2D10 },
+{ 0x10B1, 0x10B1, 0x2D11 },
+{ 0x10B2, 0x10B2, 0x2D12 },
+{ 0x10B3, 0x10B3, 0x2D13 },
+{ 0x10B4, 0x10B4, 0x2D14 },
+{ 0x10B5, 0x10B5, 0x2D15 },
+{ 0x10B6, 0x10B6, 0x2D16 },
+{ 0x10B7, 0x10B7, 0x2D17 },
+{ 0x10B8, 0x10B8, 0x2D18 },
+{ 0x10B9, 0x10B9, 0x2D19 },
+{ 0x10BA, 0x10BA, 0x2D1A },
+{ 0x10BB, 0x10BB, 0x2D1B },
+{ 0x10BC, 0x10BC, 0x2D1C },
+{ 0x10BD, 0x10BD, 0x2D1D },
+{ 0x10BE, 0x10BE, 0x2D1E },
+{ 0x10BF, 0x10BF, 0x2D1F },
+{ 0x10C0, 0x10C0, 0x2D20 },
+{ 0x10C1, 0x10C1, 0x2D21 },
+{ 0x10C2, 0x10C2, 0x2D22 },
+{ 0x10C3, 0x10C3, 0x2D23 },
+{ 0x10C4, 0x10C4, 0x2D24 },
+{ 0x10C5, 0x10C5, 0x2D25 },
+{ 0x10D0, 0x10D0, 0x10D0 },
+{ 0x10D1, 0x10D1, 0x10D1 },
+{ 0x10D2, 0x10D2, 0x10D2 },
+{ 0x10D3, 0x10D3, 0x10D3 },
+{ 0x10D4, 0x10D4, 0x10D4 },
+{ 0x10D5, 0x10D5, 0x10D5 },
+{ 0x10D6, 0x10D6, 0x10D6 },
+{ 0x10D7, 0x10D7, 0x10D7 },
+{ 0x10D8, 0x10D8, 0x10D8 },
+{ 0x10D9, 0x10D9, 0x10D9 },
+{ 0x10DA, 0x10DA, 0x10DA },
+{ 0x10DB, 0x10DB, 0x10DB },
+{ 0x10DC, 0x10DC, 0x10DC },
+{ 0x10DD, 0x10DD, 0x10DD },
+{ 0x10DE, 0x10DE, 0x10DE },
+{ 0x10DF, 0x10DF, 0x10DF },
+{ 0x10E0, 0x10E0, 0x10E0 },
+{ 0x10E1, 0x10E1, 0x10E1 },
+{ 0x10E2, 0x10E2, 0x10E2 },
+{ 0x10E3, 0x10E3, 0x10E3 },
+{ 0x10E4, 0x10E4, 0x10E4 },
+{ 0x10E5, 0x10E5, 0x10E5 },
+{ 0x10E6, 0x10E6, 0x10E6 },
+{ 0x10E7, 0x10E7, 0x10E7 },
+{ 0x10E8, 0x10E8, 0x10E8 },
+{ 0x10E9, 0x10E9, 0x10E9 },
+{ 0x10EA, 0x10EA, 0x10EA },
+{ 0x10EB, 0x10EB, 0x10EB },
+{ 0x10EC, 0x10EC, 0x10EC },
+{ 0x10ED, 0x10ED, 0x10ED },
+{ 0x10EE, 0x10EE, 0x10EE },
+{ 0x10EF, 0x10EF, 0x10EF },
+{ 0x10F0, 0x10F0, 0x10F0 },
+{ 0x10F1, 0x10F1, 0x10F1 },
+{ 0x10F2, 0x10F2, 0x10F2 },
+{ 0x10F3, 0x10F3, 0x10F3 },
+{ 0x10F4, 0x10F4, 0x10F4 },
+{ 0x10F5, 0x10F5, 0x10F5 },
+{ 0x10F6, 0x10F6, 0x10F6 },
+{ 0x10F7, 0x10F7, 0x10F7 },
+{ 0x10F8, 0x10F8, 0x10F8 },
+{ 0x10F9, 0x10F9, 0x10F9 },
+{ 0x10FA, 0x10FA, 0x10FA },
+{ 0x10FC, 0x10FC, 0x10FC },
+{ 0x1100, 0x1100, 0x1100 },
+{ 0x1101, 0x1101, 0x1101 },
+{ 0x1102, 0x1102, 0x1102 },
+{ 0x1103, 0x1103, 0x1103 },
+{ 0x1104, 0x1104, 0x1104 },
+{ 0x1105, 0x1105, 0x1105 },
+{ 0x1106, 0x1106, 0x1106 },
+{ 0x1107, 0x1107, 0x1107 },
+{ 0x1108, 0x1108, 0x1108 },
+{ 0x1109, 0x1109, 0x1109 },
+{ 0x110A, 0x110A, 0x110A },
+{ 0x110B, 0x110B, 0x110B },
+{ 0x110C, 0x110C, 0x110C },
+{ 0x110D, 0x110D, 0x110D },
+{ 0x110E, 0x110E, 0x110E },
+{ 0x110F, 0x110F, 0x110F },
+{ 0x1110, 0x1110, 0x1110 },
+{ 0x1111, 0x1111, 0x1111 },
+{ 0x1112, 0x1112, 0x1112 },
+{ 0x1113, 0x1113, 0x1113 },
+{ 0x1114, 0x1114, 0x1114 },
+{ 0x1115, 0x1115, 0x1115 },
+{ 0x1116, 0x1116, 0x1116 },
+{ 0x1117, 0x1117, 0x1117 },
+{ 0x1118, 0x1118, 0x1118 },
+{ 0x1119, 0x1119, 0x1119 },
+{ 0x111A, 0x111A, 0x111A },
+{ 0x111B, 0x111B, 0x111B },
+{ 0x111C, 0x111C, 0x111C },
+{ 0x111D, 0x111D, 0x111D },
+{ 0x111E, 0x111E, 0x111E },
+{ 0x111F, 0x111F, 0x111F },
+{ 0x1120, 0x1120, 0x1120 },
+{ 0x1121, 0x1121, 0x1121 },
+{ 0x1122, 0x1122, 0x1122 },
+{ 0x1123, 0x1123, 0x1123 },
+{ 0x1124, 0x1124, 0x1124 },
+{ 0x1125, 0x1125, 0x1125 },
+{ 0x1126, 0x1126, 0x1126 },
+{ 0x1127, 0x1127, 0x1127 },
+{ 0x1128, 0x1128, 0x1128 },
+{ 0x1129, 0x1129, 0x1129 },
+{ 0x112A, 0x112A, 0x112A },
+{ 0x112B, 0x112B, 0x112B },
+{ 0x112C, 0x112C, 0x112C },
+{ 0x112D, 0x112D, 0x112D },
+{ 0x112E, 0x112E, 0x112E },
+{ 0x112F, 0x112F, 0x112F },
+{ 0x1130, 0x1130, 0x1130 },
+{ 0x1131, 0x1131, 0x1131 },
+{ 0x1132, 0x1132, 0x1132 },
+{ 0x1133, 0x1133, 0x1133 },
+{ 0x1134, 0x1134, 0x1134 },
+{ 0x1135, 0x1135, 0x1135 },
+{ 0x1136, 0x1136, 0x1136 },
+{ 0x1137, 0x1137, 0x1137 },
+{ 0x1138, 0x1138, 0x1138 },
+{ 0x1139, 0x1139, 0x1139 },
+{ 0x113A, 0x113A, 0x113A },
+{ 0x113B, 0x113B, 0x113B },
+{ 0x113C, 0x113C, 0x113C },
+{ 0x113D, 0x113D, 0x113D },
+{ 0x113E, 0x113E, 0x113E },
+{ 0x113F, 0x113F, 0x113F },
+{ 0x1140, 0x1140, 0x1140 },
+{ 0x1141, 0x1141, 0x1141 },
+{ 0x1142, 0x1142, 0x1142 },
+{ 0x1143, 0x1143, 0x1143 },
+{ 0x1144, 0x1144, 0x1144 },
+{ 0x1145, 0x1145, 0x1145 },
+{ 0x1146, 0x1146, 0x1146 },
+{ 0x1147, 0x1147, 0x1147 },
+{ 0x1148, 0x1148, 0x1148 },
+{ 0x1149, 0x1149, 0x1149 },
+{ 0x114A, 0x114A, 0x114A },
+{ 0x114B, 0x114B, 0x114B },
+{ 0x114C, 0x114C, 0x114C },
+{ 0x114D, 0x114D, 0x114D },
+{ 0x114E, 0x114E, 0x114E },
+{ 0x114F, 0x114F, 0x114F },
+{ 0x1150, 0x1150, 0x1150 },
+{ 0x1151, 0x1151, 0x1151 },
+{ 0x1152, 0x1152, 0x1152 },
+{ 0x1153, 0x1153, 0x1153 },
+{ 0x1154, 0x1154, 0x1154 },
+{ 0x1155, 0x1155, 0x1155 },
+{ 0x1156, 0x1156, 0x1156 },
+{ 0x1157, 0x1157, 0x1157 },
+{ 0x1158, 0x1158, 0x1158 },
+{ 0x1159, 0x1159, 0x1159 },
+{ 0x115F, 0x115F, 0x115F },
+{ 0x1160, 0x1160, 0x1160 },
+{ 0x1161, 0x1161, 0x1161 },
+{ 0x1162, 0x1162, 0x1162 },
+{ 0x1163, 0x1163, 0x1163 },
+{ 0x1164, 0x1164, 0x1164 },
+{ 0x1165, 0x1165, 0x1165 },
+{ 0x1166, 0x1166, 0x1166 },
+{ 0x1167, 0x1167, 0x1167 },
+{ 0x1168, 0x1168, 0x1168 },
+{ 0x1169, 0x1169, 0x1169 },
+{ 0x116A, 0x116A, 0x116A },
+{ 0x116B, 0x116B, 0x116B },
+{ 0x116C, 0x116C, 0x116C },
+{ 0x116D, 0x116D, 0x116D },
+{ 0x116E, 0x116E, 0x116E },
+{ 0x116F, 0x116F, 0x116F },
+{ 0x1170, 0x1170, 0x1170 },
+{ 0x1171, 0x1171, 0x1171 },
+{ 0x1172, 0x1172, 0x1172 },
+{ 0x1173, 0x1173, 0x1173 },
+{ 0x1174, 0x1174, 0x1174 },
+{ 0x1175, 0x1175, 0x1175 },
+{ 0x1176, 0x1176, 0x1176 },
+{ 0x1177, 0x1177, 0x1177 },
+{ 0x1178, 0x1178, 0x1178 },
+{ 0x1179, 0x1179, 0x1179 },
+{ 0x117A, 0x117A, 0x117A },
+{ 0x117B, 0x117B, 0x117B },
+{ 0x117C, 0x117C, 0x117C },
+{ 0x117D, 0x117D, 0x117D },
+{ 0x117E, 0x117E, 0x117E },
+{ 0x117F, 0x117F, 0x117F },
+{ 0x1180, 0x1180, 0x1180 },
+{ 0x1181, 0x1181, 0x1181 },
+{ 0x1182, 0x1182, 0x1182 },
+{ 0x1183, 0x1183, 0x1183 },
+{ 0x1184, 0x1184, 0x1184 },
+{ 0x1185, 0x1185, 0x1185 },
+{ 0x1186, 0x1186, 0x1186 },
+{ 0x1187, 0x1187, 0x1187 },
+{ 0x1188, 0x1188, 0x1188 },
+{ 0x1189, 0x1189, 0x1189 },
+{ 0x118A, 0x118A, 0x118A },
+{ 0x118B, 0x118B, 0x118B },
+{ 0x118C, 0x118C, 0x118C },
+{ 0x118D, 0x118D, 0x118D },
+{ 0x118E, 0x118E, 0x118E },
+{ 0x118F, 0x118F, 0x118F },
+{ 0x1190, 0x1190, 0x1190 },
+{ 0x1191, 0x1191, 0x1191 },
+{ 0x1192, 0x1192, 0x1192 },
+{ 0x1193, 0x1193, 0x1193 },
+{ 0x1194, 0x1194, 0x1194 },
+{ 0x1195, 0x1195, 0x1195 },
+{ 0x1196, 0x1196, 0x1196 },
+{ 0x1197, 0x1197, 0x1197 },
+{ 0x1198, 0x1198, 0x1198 },
+{ 0x1199, 0x1199, 0x1199 },
+{ 0x119A, 0x119A, 0x119A },
+{ 0x119B, 0x119B, 0x119B },
+{ 0x119C, 0x119C, 0x119C },
+{ 0x119D, 0x119D, 0x119D },
+{ 0x119E, 0x119E, 0x119E },
+{ 0x119F, 0x119F, 0x119F },
+{ 0x11A0, 0x11A0, 0x11A0 },
+{ 0x11A1, 0x11A1, 0x11A1 },
+{ 0x11A2, 0x11A2, 0x11A2 },
+{ 0x11A8, 0x11A8, 0x11A8 },
+{ 0x11A9, 0x11A9, 0x11A9 },
+{ 0x11AA, 0x11AA, 0x11AA },
+{ 0x11AB, 0x11AB, 0x11AB },
+{ 0x11AC, 0x11AC, 0x11AC },
+{ 0x11AD, 0x11AD, 0x11AD },
+{ 0x11AE, 0x11AE, 0x11AE },
+{ 0x11AF, 0x11AF, 0x11AF },
+{ 0x11B0, 0x11B0, 0x11B0 },
+{ 0x11B1, 0x11B1, 0x11B1 },
+{ 0x11B2, 0x11B2, 0x11B2 },
+{ 0x11B3, 0x11B3, 0x11B3 },
+{ 0x11B4, 0x11B4, 0x11B4 },
+{ 0x11B5, 0x11B5, 0x11B5 },
+{ 0x11B6, 0x11B6, 0x11B6 },
+{ 0x11B7, 0x11B7, 0x11B7 },
+{ 0x11B8, 0x11B8, 0x11B8 },
+{ 0x11B9, 0x11B9, 0x11B9 },
+{ 0x11BA, 0x11BA, 0x11BA },
+{ 0x11BB, 0x11BB, 0x11BB },
+{ 0x11BC, 0x11BC, 0x11BC },
+{ 0x11BD, 0x11BD, 0x11BD },
+{ 0x11BE, 0x11BE, 0x11BE },
+{ 0x11BF, 0x11BF, 0x11BF },
+{ 0x11C0, 0x11C0, 0x11C0 },
+{ 0x11C1, 0x11C1, 0x11C1 },
+{ 0x11C2, 0x11C2, 0x11C2 },
+{ 0x11C3, 0x11C3, 0x11C3 },
+{ 0x11C4, 0x11C4, 0x11C4 },
+{ 0x11C5, 0x11C5, 0x11C5 },
+{ 0x11C6, 0x11C6, 0x11C6 },
+{ 0x11C7, 0x11C7, 0x11C7 },
+{ 0x11C8, 0x11C8, 0x11C8 },
+{ 0x11C9, 0x11C9, 0x11C9 },
+{ 0x11CA, 0x11CA, 0x11CA },
+{ 0x11CB, 0x11CB, 0x11CB },
+{ 0x11CC, 0x11CC, 0x11CC },
+{ 0x11CD, 0x11CD, 0x11CD },
+{ 0x11CE, 0x11CE, 0x11CE },
+{ 0x11CF, 0x11CF, 0x11CF },
+{ 0x11D0, 0x11D0, 0x11D0 },
+{ 0x11D1, 0x11D1, 0x11D1 },
+{ 0x11D2, 0x11D2, 0x11D2 },
+{ 0x11D3, 0x11D3, 0x11D3 },
+{ 0x11D4, 0x11D4, 0x11D4 },
+{ 0x11D5, 0x11D5, 0x11D5 },
+{ 0x11D6, 0x11D6, 0x11D6 },
+{ 0x11D7, 0x11D7, 0x11D7 },
+{ 0x11D8, 0x11D8, 0x11D8 },
+{ 0x11D9, 0x11D9, 0x11D9 },
+{ 0x11DA, 0x11DA, 0x11DA },
+{ 0x11DB, 0x11DB, 0x11DB },
+{ 0x11DC, 0x11DC, 0x11DC },
+{ 0x11DD, 0x11DD, 0x11DD },
+{ 0x11DE, 0x11DE, 0x11DE },
+{ 0x11DF, 0x11DF, 0x11DF },
+{ 0x11E0, 0x11E0, 0x11E0 },
+{ 0x11E1, 0x11E1, 0x11E1 },
+{ 0x11E2, 0x11E2, 0x11E2 },
+{ 0x11E3, 0x11E3, 0x11E3 },
+{ 0x11E4, 0x11E4, 0x11E4 },
+{ 0x11E5, 0x11E5, 0x11E5 },
+{ 0x11E6, 0x11E6, 0x11E6 },
+{ 0x11E7, 0x11E7, 0x11E7 },
+{ 0x11E8, 0x11E8, 0x11E8 },
+{ 0x11E9, 0x11E9, 0x11E9 },
+{ 0x11EA, 0x11EA, 0x11EA },
+{ 0x11EB, 0x11EB, 0x11EB },
+{ 0x11EC, 0x11EC, 0x11EC },
+{ 0x11ED, 0x11ED, 0x11ED },
+{ 0x11EE, 0x11EE, 0x11EE },
+{ 0x11EF, 0x11EF, 0x11EF },
+{ 0x11F0, 0x11F0, 0x11F0 },
+{ 0x11F1, 0x11F1, 0x11F1 },
+{ 0x11F2, 0x11F2, 0x11F2 },
+{ 0x11F3, 0x11F3, 0x11F3 },
+{ 0x11F4, 0x11F4, 0x11F4 },
+{ 0x11F5, 0x11F5, 0x11F5 },
+{ 0x11F6, 0x11F6, 0x11F6 },
+{ 0x11F7, 0x11F7, 0x11F7 },
+{ 0x11F8, 0x11F8, 0x11F8 },
+{ 0x11F9, 0x11F9, 0x11F9 },
+{ 0x1200, 0x1200, 0x1200 },
+{ 0x1201, 0x1201, 0x1201 },
+{ 0x1202, 0x1202, 0x1202 },
+{ 0x1203, 0x1203, 0x1203 },
+{ 0x1204, 0x1204, 0x1204 },
+{ 0x1205, 0x1205, 0x1205 },
+{ 0x1206, 0x1206, 0x1206 },
+{ 0x1207, 0x1207, 0x1207 },
+{ 0x1208, 0x1208, 0x1208 },
+{ 0x1209, 0x1209, 0x1209 },
+{ 0x120A, 0x120A, 0x120A },
+{ 0x120B, 0x120B, 0x120B },
+{ 0x120C, 0x120C, 0x120C },
+{ 0x120D, 0x120D, 0x120D },
+{ 0x120E, 0x120E, 0x120E },
+{ 0x120F, 0x120F, 0x120F },
+{ 0x1210, 0x1210, 0x1210 },
+{ 0x1211, 0x1211, 0x1211 },
+{ 0x1212, 0x1212, 0x1212 },
+{ 0x1213, 0x1213, 0x1213 },
+{ 0x1214, 0x1214, 0x1214 },
+{ 0x1215, 0x1215, 0x1215 },
+{ 0x1216, 0x1216, 0x1216 },
+{ 0x1217, 0x1217, 0x1217 },
+{ 0x1218, 0x1218, 0x1218 },
+{ 0x1219, 0x1219, 0x1219 },
+{ 0x121A, 0x121A, 0x121A },
+{ 0x121B, 0x121B, 0x121B },
+{ 0x121C, 0x121C, 0x121C },
+{ 0x121D, 0x121D, 0x121D },
+{ 0x121E, 0x121E, 0x121E },
+{ 0x121F, 0x121F, 0x121F },
+{ 0x1220, 0x1220, 0x1220 },
+{ 0x1221, 0x1221, 0x1221 },
+{ 0x1222, 0x1222, 0x1222 },
+{ 0x1223, 0x1223, 0x1223 },
+{ 0x1224, 0x1224, 0x1224 },
+{ 0x1225, 0x1225, 0x1225 },
+{ 0x1226, 0x1226, 0x1226 },
+{ 0x1227, 0x1227, 0x1227 },
+{ 0x1228, 0x1228, 0x1228 },
+{ 0x1229, 0x1229, 0x1229 },
+{ 0x122A, 0x122A, 0x122A },
+{ 0x122B, 0x122B, 0x122B },
+{ 0x122C, 0x122C, 0x122C },
+{ 0x122D, 0x122D, 0x122D },
+{ 0x122E, 0x122E, 0x122E },
+{ 0x122F, 0x122F, 0x122F },
+{ 0x1230, 0x1230, 0x1230 },
+{ 0x1231, 0x1231, 0x1231 },
+{ 0x1232, 0x1232, 0x1232 },
+{ 0x1233, 0x1233, 0x1233 },
+{ 0x1234, 0x1234, 0x1234 },
+{ 0x1235, 0x1235, 0x1235 },
+{ 0x1236, 0x1236, 0x1236 },
+{ 0x1237, 0x1237, 0x1237 },
+{ 0x1238, 0x1238, 0x1238 },
+{ 0x1239, 0x1239, 0x1239 },
+{ 0x123A, 0x123A, 0x123A },
+{ 0x123B, 0x123B, 0x123B },
+{ 0x123C, 0x123C, 0x123C },
+{ 0x123D, 0x123D, 0x123D },
+{ 0x123E, 0x123E, 0x123E },
+{ 0x123F, 0x123F, 0x123F },
+{ 0x1240, 0x1240, 0x1240 },
+{ 0x1241, 0x1241, 0x1241 },
+{ 0x1242, 0x1242, 0x1242 },
+{ 0x1243, 0x1243, 0x1243 },
+{ 0x1244, 0x1244, 0x1244 },
+{ 0x1245, 0x1245, 0x1245 },
+{ 0x1246, 0x1246, 0x1246 },
+{ 0x1247, 0x1247, 0x1247 },
+{ 0x1248, 0x1248, 0x1248 },
+{ 0x124A, 0x124A, 0x124A },
+{ 0x124B, 0x124B, 0x124B },
+{ 0x124C, 0x124C, 0x124C },
+{ 0x124D, 0x124D, 0x124D },
+{ 0x1250, 0x1250, 0x1250 },
+{ 0x1251, 0x1251, 0x1251 },
+{ 0x1252, 0x1252, 0x1252 },
+{ 0x1253, 0x1253, 0x1253 },
+{ 0x1254, 0x1254, 0x1254 },
+{ 0x1255, 0x1255, 0x1255 },
+{ 0x1256, 0x1256, 0x1256 },
+{ 0x1258, 0x1258, 0x1258 },
+{ 0x125A, 0x125A, 0x125A },
+{ 0x125B, 0x125B, 0x125B },
+{ 0x125C, 0x125C, 0x125C },
+{ 0x125D, 0x125D, 0x125D },
+{ 0x1260, 0x1260, 0x1260 },
+{ 0x1261, 0x1261, 0x1261 },
+{ 0x1262, 0x1262, 0x1262 },
+{ 0x1263, 0x1263, 0x1263 },
+{ 0x1264, 0x1264, 0x1264 },
+{ 0x1265, 0x1265, 0x1265 },
+{ 0x1266, 0x1266, 0x1266 },
+{ 0x1267, 0x1267, 0x1267 },
+{ 0x1268, 0x1268, 0x1268 },
+{ 0x1269, 0x1269, 0x1269 },
+{ 0x126A, 0x126A, 0x126A },
+{ 0x126B, 0x126B, 0x126B },
+{ 0x126C, 0x126C, 0x126C },
+{ 0x126D, 0x126D, 0x126D },
+{ 0x126E, 0x126E, 0x126E },
+{ 0x126F, 0x126F, 0x126F },
+{ 0x1270, 0x1270, 0x1270 },
+{ 0x1271, 0x1271, 0x1271 },
+{ 0x1272, 0x1272, 0x1272 },
+{ 0x1273, 0x1273, 0x1273 },
+{ 0x1274, 0x1274, 0x1274 },
+{ 0x1275, 0x1275, 0x1275 },
+{ 0x1276, 0x1276, 0x1276 },
+{ 0x1277, 0x1277, 0x1277 },
+{ 0x1278, 0x1278, 0x1278 },
+{ 0x1279, 0x1279, 0x1279 },
+{ 0x127A, 0x127A, 0x127A },
+{ 0x127B, 0x127B, 0x127B },
+{ 0x127C, 0x127C, 0x127C },
+{ 0x127D, 0x127D, 0x127D },
+{ 0x127E, 0x127E, 0x127E },
+{ 0x127F, 0x127F, 0x127F },
+{ 0x1280, 0x1280, 0x1280 },
+{ 0x1281, 0x1281, 0x1281 },
+{ 0x1282, 0x1282, 0x1282 },
+{ 0x1283, 0x1283, 0x1283 },
+{ 0x1284, 0x1284, 0x1284 },
+{ 0x1285, 0x1285, 0x1285 },
+{ 0x1286, 0x1286, 0x1286 },
+{ 0x1287, 0x1287, 0x1287 },
+{ 0x1288, 0x1288, 0x1288 },
+{ 0x128A, 0x128A, 0x128A },
+{ 0x128B, 0x128B, 0x128B },
+{ 0x128C, 0x128C, 0x128C },
+{ 0x128D, 0x128D, 0x128D },
+{ 0x1290, 0x1290, 0x1290 },
+{ 0x1291, 0x1291, 0x1291 },
+{ 0x1292, 0x1292, 0x1292 },
+{ 0x1293, 0x1293, 0x1293 },
+{ 0x1294, 0x1294, 0x1294 },
+{ 0x1295, 0x1295, 0x1295 },
+{ 0x1296, 0x1296, 0x1296 },
+{ 0x1297, 0x1297, 0x1297 },
+{ 0x1298, 0x1298, 0x1298 },
+{ 0x1299, 0x1299, 0x1299 },
+{ 0x129A, 0x129A, 0x129A },
+{ 0x129B, 0x129B, 0x129B },
+{ 0x129C, 0x129C, 0x129C },
+{ 0x129D, 0x129D, 0x129D },
+{ 0x129E, 0x129E, 0x129E },
+{ 0x129F, 0x129F, 0x129F },
+{ 0x12A0, 0x12A0, 0x12A0 },
+{ 0x12A1, 0x12A1, 0x12A1 },
+{ 0x12A2, 0x12A2, 0x12A2 },
+{ 0x12A3, 0x12A3, 0x12A3 },
+{ 0x12A4, 0x12A4, 0x12A4 },
+{ 0x12A5, 0x12A5, 0x12A5 },
+{ 0x12A6, 0x12A6, 0x12A6 },
+{ 0x12A7, 0x12A7, 0x12A7 },
+{ 0x12A8, 0x12A8, 0x12A8 },
+{ 0x12A9, 0x12A9, 0x12A9 },
+{ 0x12AA, 0x12AA, 0x12AA },
+{ 0x12AB, 0x12AB, 0x12AB },
+{ 0x12AC, 0x12AC, 0x12AC },
+{ 0x12AD, 0x12AD, 0x12AD },
+{ 0x12AE, 0x12AE, 0x12AE },
+{ 0x12AF, 0x12AF, 0x12AF },
+{ 0x12B0, 0x12B0, 0x12B0 },
+{ 0x12B2, 0x12B2, 0x12B2 },
+{ 0x12B3, 0x12B3, 0x12B3 },
+{ 0x12B4, 0x12B4, 0x12B4 },
+{ 0x12B5, 0x12B5, 0x12B5 },
+{ 0x12B8, 0x12B8, 0x12B8 },
+{ 0x12B9, 0x12B9, 0x12B9 },
+{ 0x12BA, 0x12BA, 0x12BA },
+{ 0x12BB, 0x12BB, 0x12BB },
+{ 0x12BC, 0x12BC, 0x12BC },
+{ 0x12BD, 0x12BD, 0x12BD },
+{ 0x12BE, 0x12BE, 0x12BE },
+{ 0x12C0, 0x12C0, 0x12C0 },
+{ 0x12C2, 0x12C2, 0x12C2 },
+{ 0x12C3, 0x12C3, 0x12C3 },
+{ 0x12C4, 0x12C4, 0x12C4 },
+{ 0x12C5, 0x12C5, 0x12C5 },
+{ 0x12C8, 0x12C8, 0x12C8 },
+{ 0x12C9, 0x12C9, 0x12C9 },
+{ 0x12CA, 0x12CA, 0x12CA },
+{ 0x12CB, 0x12CB, 0x12CB },
+{ 0x12CC, 0x12CC, 0x12CC },
+{ 0x12CD, 0x12CD, 0x12CD },
+{ 0x12CE, 0x12CE, 0x12CE },
+{ 0x12CF, 0x12CF, 0x12CF },
+{ 0x12D0, 0x12D0, 0x12D0 },
+{ 0x12D1, 0x12D1, 0x12D1 },
+{ 0x12D2, 0x12D2, 0x12D2 },
+{ 0x12D3, 0x12D3, 0x12D3 },
+{ 0x12D4, 0x12D4, 0x12D4 },
+{ 0x12D5, 0x12D5, 0x12D5 },
+{ 0x12D6, 0x12D6, 0x12D6 },
+{ 0x12D8, 0x12D8, 0x12D8 },
+{ 0x12D9, 0x12D9, 0x12D9 },
+{ 0x12DA, 0x12DA, 0x12DA },
+{ 0x12DB, 0x12DB, 0x12DB },
+{ 0x12DC, 0x12DC, 0x12DC },
+{ 0x12DD, 0x12DD, 0x12DD },
+{ 0x12DE, 0x12DE, 0x12DE },
+{ 0x12DF, 0x12DF, 0x12DF },
+{ 0x12E0, 0x12E0, 0x12E0 },
+{ 0x12E1, 0x12E1, 0x12E1 },
+{ 0x12E2, 0x12E2, 0x12E2 },
+{ 0x12E3, 0x12E3, 0x12E3 },
+{ 0x12E4, 0x12E4, 0x12E4 },
+{ 0x12E5, 0x12E5, 0x12E5 },
+{ 0x12E6, 0x12E6, 0x12E6 },
+{ 0x12E7, 0x12E7, 0x12E7 },
+{ 0x12E8, 0x12E8, 0x12E8 },
+{ 0x12E9, 0x12E9, 0x12E9 },
+{ 0x12EA, 0x12EA, 0x12EA },
+{ 0x12EB, 0x12EB, 0x12EB },
+{ 0x12EC, 0x12EC, 0x12EC },
+{ 0x12ED, 0x12ED, 0x12ED },
+{ 0x12EE, 0x12EE, 0x12EE },
+{ 0x12EF, 0x12EF, 0x12EF },
+{ 0x12F0, 0x12F0, 0x12F0 },
+{ 0x12F1, 0x12F1, 0x12F1 },
+{ 0x12F2, 0x12F2, 0x12F2 },
+{ 0x12F3, 0x12F3, 0x12F3 },
+{ 0x12F4, 0x12F4, 0x12F4 },
+{ 0x12F5, 0x12F5, 0x12F5 },
+{ 0x12F6, 0x12F6, 0x12F6 },
+{ 0x12F7, 0x12F7, 0x12F7 },
+{ 0x12F8, 0x12F8, 0x12F8 },
+{ 0x12F9, 0x12F9, 0x12F9 },
+{ 0x12FA, 0x12FA, 0x12FA },
+{ 0x12FB, 0x12FB, 0x12FB },
+{ 0x12FC, 0x12FC, 0x12FC },
+{ 0x12FD, 0x12FD, 0x12FD },
+{ 0x12FE, 0x12FE, 0x12FE },
+{ 0x12FF, 0x12FF, 0x12FF },
+{ 0x1300, 0x1300, 0x1300 },
+{ 0x1301, 0x1301, 0x1301 },
+{ 0x1302, 0x1302, 0x1302 },
+{ 0x1303, 0x1303, 0x1303 },
+{ 0x1304, 0x1304, 0x1304 },
+{ 0x1305, 0x1305, 0x1305 },
+{ 0x1306, 0x1306, 0x1306 },
+{ 0x1307, 0x1307, 0x1307 },
+{ 0x1308, 0x1308, 0x1308 },
+{ 0x1309, 0x1309, 0x1309 },
+{ 0x130A, 0x130A, 0x130A },
+{ 0x130B, 0x130B, 0x130B },
+{ 0x130C, 0x130C, 0x130C },
+{ 0x130D, 0x130D, 0x130D },
+{ 0x130E, 0x130E, 0x130E },
+{ 0x130F, 0x130F, 0x130F },
+{ 0x1310, 0x1310, 0x1310 },
+{ 0x1312, 0x1312, 0x1312 },
+{ 0x1313, 0x1313, 0x1313 },
+{ 0x1314, 0x1314, 0x1314 },
+{ 0x1315, 0x1315, 0x1315 },
+{ 0x1318, 0x1318, 0x1318 },
+{ 0x1319, 0x1319, 0x1319 },
+{ 0x131A, 0x131A, 0x131A },
+{ 0x131B, 0x131B, 0x131B },
+{ 0x131C, 0x131C, 0x131C },
+{ 0x131D, 0x131D, 0x131D },
+{ 0x131E, 0x131E, 0x131E },
+{ 0x131F, 0x131F, 0x131F },
+{ 0x1320, 0x1320, 0x1320 },
+{ 0x1321, 0x1321, 0x1321 },
+{ 0x1322, 0x1322, 0x1322 },
+{ 0x1323, 0x1323, 0x1323 },
+{ 0x1324, 0x1324, 0x1324 },
+{ 0x1325, 0x1325, 0x1325 },
+{ 0x1326, 0x1326, 0x1326 },
+{ 0x1327, 0x1327, 0x1327 },
+{ 0x1328, 0x1328, 0x1328 },
+{ 0x1329, 0x1329, 0x1329 },
+{ 0x132A, 0x132A, 0x132A },
+{ 0x132B, 0x132B, 0x132B },
+{ 0x132C, 0x132C, 0x132C },
+{ 0x132D, 0x132D, 0x132D },
+{ 0x132E, 0x132E, 0x132E },
+{ 0x132F, 0x132F, 0x132F },
+{ 0x1330, 0x1330, 0x1330 },
+{ 0x1331, 0x1331, 0x1331 },
+{ 0x1332, 0x1332, 0x1332 },
+{ 0x1333, 0x1333, 0x1333 },
+{ 0x1334, 0x1334, 0x1334 },
+{ 0x1335, 0x1335, 0x1335 },
+{ 0x1336, 0x1336, 0x1336 },
+{ 0x1337, 0x1337, 0x1337 },
+{ 0x1338, 0x1338, 0x1338 },
+{ 0x1339, 0x1339, 0x1339 },
+{ 0x133A, 0x133A, 0x133A },
+{ 0x133B, 0x133B, 0x133B },
+{ 0x133C, 0x133C, 0x133C },
+{ 0x133D, 0x133D, 0x133D },
+{ 0x133E, 0x133E, 0x133E },
+{ 0x133F, 0x133F, 0x133F },
+{ 0x1340, 0x1340, 0x1340 },
+{ 0x1341, 0x1341, 0x1341 },
+{ 0x1342, 0x1342, 0x1342 },
+{ 0x1343, 0x1343, 0x1343 },
+{ 0x1344, 0x1344, 0x1344 },
+{ 0x1345, 0x1345, 0x1345 },
+{ 0x1346, 0x1346, 0x1346 },
+{ 0x1347, 0x1347, 0x1347 },
+{ 0x1348, 0x1348, 0x1348 },
+{ 0x1349, 0x1349, 0x1349 },
+{ 0x134A, 0x134A, 0x134A },
+{ 0x134B, 0x134B, 0x134B },
+{ 0x134C, 0x134C, 0x134C },
+{ 0x134D, 0x134D, 0x134D },
+{ 0x134E, 0x134E, 0x134E },
+{ 0x134F, 0x134F, 0x134F },
+{ 0x1350, 0x1350, 0x1350 },
+{ 0x1351, 0x1351, 0x1351 },
+{ 0x1352, 0x1352, 0x1352 },
+{ 0x1353, 0x1353, 0x1353 },
+{ 0x1354, 0x1354, 0x1354 },
+{ 0x1355, 0x1355, 0x1355 },
+{ 0x1356, 0x1356, 0x1356 },
+{ 0x1357, 0x1357, 0x1357 },
+{ 0x1358, 0x1358, 0x1358 },
+{ 0x1359, 0x1359, 0x1359 },
+{ 0x135A, 0x135A, 0x135A },
+{ 0x135F, 0x135F, 0x135F },
+{ 0x1380, 0x1380, 0x1380 },
+{ 0x1381, 0x1381, 0x1381 },
+{ 0x1382, 0x1382, 0x1382 },
+{ 0x1383, 0x1383, 0x1383 },
+{ 0x1384, 0x1384, 0x1384 },
+{ 0x1385, 0x1385, 0x1385 },
+{ 0x1386, 0x1386, 0x1386 },
+{ 0x1387, 0x1387, 0x1387 },
+{ 0x1388, 0x1388, 0x1388 },
+{ 0x1389, 0x1389, 0x1389 },
+{ 0x138A, 0x138A, 0x138A },
+{ 0x138B, 0x138B, 0x138B },
+{ 0x138C, 0x138C, 0x138C },
+{ 0x138D, 0x138D, 0x138D },
+{ 0x138E, 0x138E, 0x138E },
+{ 0x138F, 0x138F, 0x138F },
+{ 0x13A0, 0x13A0, 0x13A0 },
+{ 0x13A1, 0x13A1, 0x13A1 },
+{ 0x13A2, 0x13A2, 0x13A2 },
+{ 0x13A3, 0x13A3, 0x13A3 },
+{ 0x13A4, 0x13A4, 0x13A4 },
+{ 0x13A5, 0x13A5, 0x13A5 },
+{ 0x13A6, 0x13A6, 0x13A6 },
+{ 0x13A7, 0x13A7, 0x13A7 },
+{ 0x13A8, 0x13A8, 0x13A8 },
+{ 0x13A9, 0x13A9, 0x13A9 },
+{ 0x13AA, 0x13AA, 0x13AA },
+{ 0x13AB, 0x13AB, 0x13AB },
+{ 0x13AC, 0x13AC, 0x13AC },
+{ 0x13AD, 0x13AD, 0x13AD },
+{ 0x13AE, 0x13AE, 0x13AE },
+{ 0x13AF, 0x13AF, 0x13AF },
+{ 0x13B0, 0x13B0, 0x13B0 },
+{ 0x13B1, 0x13B1, 0x13B1 },
+{ 0x13B2, 0x13B2, 0x13B2 },
+{ 0x13B3, 0x13B3, 0x13B3 },
+{ 0x13B4, 0x13B4, 0x13B4 },
+{ 0x13B5, 0x13B5, 0x13B5 },
+{ 0x13B6, 0x13B6, 0x13B6 },
+{ 0x13B7, 0x13B7, 0x13B7 },
+{ 0x13B8, 0x13B8, 0x13B8 },
+{ 0x13B9, 0x13B9, 0x13B9 },
+{ 0x13BA, 0x13BA, 0x13BA },
+{ 0x13BB, 0x13BB, 0x13BB },
+{ 0x13BC, 0x13BC, 0x13BC },
+{ 0x13BD, 0x13BD, 0x13BD },
+{ 0x13BE, 0x13BE, 0x13BE },
+{ 0x13BF, 0x13BF, 0x13BF },
+{ 0x13C0, 0x13C0, 0x13C0 },
+{ 0x13C1, 0x13C1, 0x13C1 },
+{ 0x13C2, 0x13C2, 0x13C2 },
+{ 0x13C3, 0x13C3, 0x13C3 },
+{ 0x13C4, 0x13C4, 0x13C4 },
+{ 0x13C5, 0x13C5, 0x13C5 },
+{ 0x13C6, 0x13C6, 0x13C6 },
+{ 0x13C7, 0x13C7, 0x13C7 },
+{ 0x13C8, 0x13C8, 0x13C8 },
+{ 0x13C9, 0x13C9, 0x13C9 },
+{ 0x13CA, 0x13CA, 0x13CA },
+{ 0x13CB, 0x13CB, 0x13CB },
+{ 0x13CC, 0x13CC, 0x13CC },
+{ 0x13CD, 0x13CD, 0x13CD },
+{ 0x13CE, 0x13CE, 0x13CE },
+{ 0x13CF, 0x13CF, 0x13CF },
+{ 0x13D0, 0x13D0, 0x13D0 },
+{ 0x13D1, 0x13D1, 0x13D1 },
+{ 0x13D2, 0x13D2, 0x13D2 },
+{ 0x13D3, 0x13D3, 0x13D3 },
+{ 0x13D4, 0x13D4, 0x13D4 },
+{ 0x13D5, 0x13D5, 0x13D5 },
+{ 0x13D6, 0x13D6, 0x13D6 },
+{ 0x13D7, 0x13D7, 0x13D7 },
+{ 0x13D8, 0x13D8, 0x13D8 },
+{ 0x13D9, 0x13D9, 0x13D9 },
+{ 0x13DA, 0x13DA, 0x13DA },
+{ 0x13DB, 0x13DB, 0x13DB },
+{ 0x13DC, 0x13DC, 0x13DC },
+{ 0x13DD, 0x13DD, 0x13DD },
+{ 0x13DE, 0x13DE, 0x13DE },
+{ 0x13DF, 0x13DF, 0x13DF },
+{ 0x13E0, 0x13E0, 0x13E0 },
+{ 0x13E1, 0x13E1, 0x13E1 },
+{ 0x13E2, 0x13E2, 0x13E2 },
+{ 0x13E3, 0x13E3, 0x13E3 },
+{ 0x13E4, 0x13E4, 0x13E4 },
+{ 0x13E5, 0x13E5, 0x13E5 },
+{ 0x13E6, 0x13E6, 0x13E6 },
+{ 0x13E7, 0x13E7, 0x13E7 },
+{ 0x13E8, 0x13E8, 0x13E8 },
+{ 0x13E9, 0x13E9, 0x13E9 },
+{ 0x13EA, 0x13EA, 0x13EA },
+{ 0x13EB, 0x13EB, 0x13EB },
+{ 0x13EC, 0x13EC, 0x13EC },
+{ 0x13ED, 0x13ED, 0x13ED },
+{ 0x13EE, 0x13EE, 0x13EE },
+{ 0x13EF, 0x13EF, 0x13EF },
+{ 0x13F0, 0x13F0, 0x13F0 },
+{ 0x13F1, 0x13F1, 0x13F1 },
+{ 0x13F2, 0x13F2, 0x13F2 },
+{ 0x13F3, 0x13F3, 0x13F3 },
+{ 0x13F4, 0x13F4, 0x13F4 },
+{ 0x1401, 0x1401, 0x1401 },
+{ 0x1402, 0x1402, 0x1402 },
+{ 0x1403, 0x1403, 0x1403 },
+{ 0x1404, 0x1404, 0x1404 },
+{ 0x1405, 0x1405, 0x1405 },
+{ 0x1406, 0x1406, 0x1406 },
+{ 0x1407, 0x1407, 0x1407 },
+{ 0x1408, 0x1408, 0x1408 },
+{ 0x1409, 0x1409, 0x1409 },
+{ 0x140A, 0x140A, 0x140A },
+{ 0x140B, 0x140B, 0x140B },
+{ 0x140C, 0x140C, 0x140C },
+{ 0x140D, 0x140D, 0x140D },
+{ 0x140E, 0x140E, 0x140E },
+{ 0x140F, 0x140F, 0x140F },
+{ 0x1410, 0x1410, 0x1410 },
+{ 0x1411, 0x1411, 0x1411 },
+{ 0x1412, 0x1412, 0x1412 },
+{ 0x1413, 0x1413, 0x1413 },
+{ 0x1414, 0x1414, 0x1414 },
+{ 0x1415, 0x1415, 0x1415 },
+{ 0x1416, 0x1416, 0x1416 },
+{ 0x1417, 0x1417, 0x1417 },
+{ 0x1418, 0x1418, 0x1418 },
+{ 0x1419, 0x1419, 0x1419 },
+{ 0x141A, 0x141A, 0x141A },
+{ 0x141B, 0x141B, 0x141B },
+{ 0x141C, 0x141C, 0x141C },
+{ 0x141D, 0x141D, 0x141D },
+{ 0x141E, 0x141E, 0x141E },
+{ 0x141F, 0x141F, 0x141F },
+{ 0x1420, 0x1420, 0x1420 },
+{ 0x1421, 0x1421, 0x1421 },
+{ 0x1422, 0x1422, 0x1422 },
+{ 0x1423, 0x1423, 0x1423 },
+{ 0x1424, 0x1424, 0x1424 },
+{ 0x1425, 0x1425, 0x1425 },
+{ 0x1426, 0x1426, 0x1426 },
+{ 0x1427, 0x1427, 0x1427 },
+{ 0x1428, 0x1428, 0x1428 },
+{ 0x1429, 0x1429, 0x1429 },
+{ 0x142A, 0x142A, 0x142A },
+{ 0x142B, 0x142B, 0x142B },
+{ 0x142C, 0x142C, 0x142C },
+{ 0x142D, 0x142D, 0x142D },
+{ 0x142E, 0x142E, 0x142E },
+{ 0x142F, 0x142F, 0x142F },
+{ 0x1430, 0x1430, 0x1430 },
+{ 0x1431, 0x1431, 0x1431 },
+{ 0x1432, 0x1432, 0x1432 },
+{ 0x1433, 0x1433, 0x1433 },
+{ 0x1434, 0x1434, 0x1434 },
+{ 0x1435, 0x1435, 0x1435 },
+{ 0x1436, 0x1436, 0x1436 },
+{ 0x1437, 0x1437, 0x1437 },
+{ 0x1438, 0x1438, 0x1438 },
+{ 0x1439, 0x1439, 0x1439 },
+{ 0x143A, 0x143A, 0x143A },
+{ 0x143B, 0x143B, 0x143B },
+{ 0x143C, 0x143C, 0x143C },
+{ 0x143D, 0x143D, 0x143D },
+{ 0x143E, 0x143E, 0x143E },
+{ 0x143F, 0x143F, 0x143F },
+{ 0x1440, 0x1440, 0x1440 },
+{ 0x1441, 0x1441, 0x1441 },
+{ 0x1442, 0x1442, 0x1442 },
+{ 0x1443, 0x1443, 0x1443 },
+{ 0x1444, 0x1444, 0x1444 },
+{ 0x1445, 0x1445, 0x1445 },
+{ 0x1446, 0x1446, 0x1446 },
+{ 0x1447, 0x1447, 0x1447 },
+{ 0x1448, 0x1448, 0x1448 },
+{ 0x1449, 0x1449, 0x1449 },
+{ 0x144A, 0x144A, 0x144A },
+{ 0x144B, 0x144B, 0x144B },
+{ 0x144C, 0x144C, 0x144C },
+{ 0x144D, 0x144D, 0x144D },
+{ 0x144E, 0x144E, 0x144E },
+{ 0x144F, 0x144F, 0x144F },
+{ 0x1450, 0x1450, 0x1450 },
+{ 0x1451, 0x1451, 0x1451 },
+{ 0x1452, 0x1452, 0x1452 },
+{ 0x1453, 0x1453, 0x1453 },
+{ 0x1454, 0x1454, 0x1454 },
+{ 0x1455, 0x1455, 0x1455 },
+{ 0x1456, 0x1456, 0x1456 },
+{ 0x1457, 0x1457, 0x1457 },
+{ 0x1458, 0x1458, 0x1458 },
+{ 0x1459, 0x1459, 0x1459 },
+{ 0x145A, 0x145A, 0x145A },
+{ 0x145B, 0x145B, 0x145B },
+{ 0x145C, 0x145C, 0x145C },
+{ 0x145D, 0x145D, 0x145D },
+{ 0x145E, 0x145E, 0x145E },
+{ 0x145F, 0x145F, 0x145F },
+{ 0x1460, 0x1460, 0x1460 },
+{ 0x1461, 0x1461, 0x1461 },
+{ 0x1462, 0x1462, 0x1462 },
+{ 0x1463, 0x1463, 0x1463 },
+{ 0x1464, 0x1464, 0x1464 },
+{ 0x1465, 0x1465, 0x1465 },
+{ 0x1466, 0x1466, 0x1466 },
+{ 0x1467, 0x1467, 0x1467 },
+{ 0x1468, 0x1468, 0x1468 },
+{ 0x1469, 0x1469, 0x1469 },
+{ 0x146A, 0x146A, 0x146A },
+{ 0x146B, 0x146B, 0x146B },
+{ 0x146C, 0x146C, 0x146C },
+{ 0x146D, 0x146D, 0x146D },
+{ 0x146E, 0x146E, 0x146E },
+{ 0x146F, 0x146F, 0x146F },
+{ 0x1470, 0x1470, 0x1470 },
+{ 0x1471, 0x1471, 0x1471 },
+{ 0x1472, 0x1472, 0x1472 },
+{ 0x1473, 0x1473, 0x1473 },
+{ 0x1474, 0x1474, 0x1474 },
+{ 0x1475, 0x1475, 0x1475 },
+{ 0x1476, 0x1476, 0x1476 },
+{ 0x1477, 0x1477, 0x1477 },
+{ 0x1478, 0x1478, 0x1478 },
+{ 0x1479, 0x1479, 0x1479 },
+{ 0x147A, 0x147A, 0x147A },
+{ 0x147B, 0x147B, 0x147B },
+{ 0x147C, 0x147C, 0x147C },
+{ 0x147D, 0x147D, 0x147D },
+{ 0x147E, 0x147E, 0x147E },
+{ 0x147F, 0x147F, 0x147F },
+{ 0x1480, 0x1480, 0x1480 },
+{ 0x1481, 0x1481, 0x1481 },
+{ 0x1482, 0x1482, 0x1482 },
+{ 0x1483, 0x1483, 0x1483 },
+{ 0x1484, 0x1484, 0x1484 },
+{ 0x1485, 0x1485, 0x1485 },
+{ 0x1486, 0x1486, 0x1486 },
+{ 0x1487, 0x1487, 0x1487 },
+{ 0x1488, 0x1488, 0x1488 },
+{ 0x1489, 0x1489, 0x1489 },
+{ 0x148A, 0x148A, 0x148A },
+{ 0x148B, 0x148B, 0x148B },
+{ 0x148C, 0x148C, 0x148C },
+{ 0x148D, 0x148D, 0x148D },
+{ 0x148E, 0x148E, 0x148E },
+{ 0x148F, 0x148F, 0x148F },
+{ 0x1490, 0x1490, 0x1490 },
+{ 0x1491, 0x1491, 0x1491 },
+{ 0x1492, 0x1492, 0x1492 },
+{ 0x1493, 0x1493, 0x1493 },
+{ 0x1494, 0x1494, 0x1494 },
+{ 0x1495, 0x1495, 0x1495 },
+{ 0x1496, 0x1496, 0x1496 },
+{ 0x1497, 0x1497, 0x1497 },
+{ 0x1498, 0x1498, 0x1498 },
+{ 0x1499, 0x1499, 0x1499 },
+{ 0x149A, 0x149A, 0x149A },
+{ 0x149B, 0x149B, 0x149B },
+{ 0x149C, 0x149C, 0x149C },
+{ 0x149D, 0x149D, 0x149D },
+{ 0x149E, 0x149E, 0x149E },
+{ 0x149F, 0x149F, 0x149F },
+{ 0x14A0, 0x14A0, 0x14A0 },
+{ 0x14A1, 0x14A1, 0x14A1 },
+{ 0x14A2, 0x14A2, 0x14A2 },
+{ 0x14A3, 0x14A3, 0x14A3 },
+{ 0x14A4, 0x14A4, 0x14A4 },
+{ 0x14A5, 0x14A5, 0x14A5 },
+{ 0x14A6, 0x14A6, 0x14A6 },
+{ 0x14A7, 0x14A7, 0x14A7 },
+{ 0x14A8, 0x14A8, 0x14A8 },
+{ 0x14A9, 0x14A9, 0x14A9 },
+{ 0x14AA, 0x14AA, 0x14AA },
+{ 0x14AB, 0x14AB, 0x14AB },
+{ 0x14AC, 0x14AC, 0x14AC },
+{ 0x14AD, 0x14AD, 0x14AD },
+{ 0x14AE, 0x14AE, 0x14AE },
+{ 0x14AF, 0x14AF, 0x14AF },
+{ 0x14B0, 0x14B0, 0x14B0 },
+{ 0x14B1, 0x14B1, 0x14B1 },
+{ 0x14B2, 0x14B2, 0x14B2 },
+{ 0x14B3, 0x14B3, 0x14B3 },
+{ 0x14B4, 0x14B4, 0x14B4 },
+{ 0x14B5, 0x14B5, 0x14B5 },
+{ 0x14B6, 0x14B6, 0x14B6 },
+{ 0x14B7, 0x14B7, 0x14B7 },
+{ 0x14B8, 0x14B8, 0x14B8 },
+{ 0x14B9, 0x14B9, 0x14B9 },
+{ 0x14BA, 0x14BA, 0x14BA },
+{ 0x14BB, 0x14BB, 0x14BB },
+{ 0x14BC, 0x14BC, 0x14BC },
+{ 0x14BD, 0x14BD, 0x14BD },
+{ 0x14BE, 0x14BE, 0x14BE },
+{ 0x14BF, 0x14BF, 0x14BF },
+{ 0x14C0, 0x14C0, 0x14C0 },
+{ 0x14C1, 0x14C1, 0x14C1 },
+{ 0x14C2, 0x14C2, 0x14C2 },
+{ 0x14C3, 0x14C3, 0x14C3 },
+{ 0x14C4, 0x14C4, 0x14C4 },
+{ 0x14C5, 0x14C5, 0x14C5 },
+{ 0x14C6, 0x14C6, 0x14C6 },
+{ 0x14C7, 0x14C7, 0x14C7 },
+{ 0x14C8, 0x14C8, 0x14C8 },
+{ 0x14C9, 0x14C9, 0x14C9 },
+{ 0x14CA, 0x14CA, 0x14CA },
+{ 0x14CB, 0x14CB, 0x14CB },
+{ 0x14CC, 0x14CC, 0x14CC },
+{ 0x14CD, 0x14CD, 0x14CD },
+{ 0x14CE, 0x14CE, 0x14CE },
+{ 0x14CF, 0x14CF, 0x14CF },
+{ 0x14D0, 0x14D0, 0x14D0 },
+{ 0x14D1, 0x14D1, 0x14D1 },
+{ 0x14D2, 0x14D2, 0x14D2 },
+{ 0x14D3, 0x14D3, 0x14D3 },
+{ 0x14D4, 0x14D4, 0x14D4 },
+{ 0x14D5, 0x14D5, 0x14D5 },
+{ 0x14D6, 0x14D6, 0x14D6 },
+{ 0x14D7, 0x14D7, 0x14D7 },
+{ 0x14D8, 0x14D8, 0x14D8 },
+{ 0x14D9, 0x14D9, 0x14D9 },
+{ 0x14DA, 0x14DA, 0x14DA },
+{ 0x14DB, 0x14DB, 0x14DB },
+{ 0x14DC, 0x14DC, 0x14DC },
+{ 0x14DD, 0x14DD, 0x14DD },
+{ 0x14DE, 0x14DE, 0x14DE },
+{ 0x14DF, 0x14DF, 0x14DF },
+{ 0x14E0, 0x14E0, 0x14E0 },
+{ 0x14E1, 0x14E1, 0x14E1 },
+{ 0x14E2, 0x14E2, 0x14E2 },
+{ 0x14E3, 0x14E3, 0x14E3 },
+{ 0x14E4, 0x14E4, 0x14E4 },
+{ 0x14E5, 0x14E5, 0x14E5 },
+{ 0x14E6, 0x14E6, 0x14E6 },
+{ 0x14E7, 0x14E7, 0x14E7 },
+{ 0x14E8, 0x14E8, 0x14E8 },
+{ 0x14E9, 0x14E9, 0x14E9 },
+{ 0x14EA, 0x14EA, 0x14EA },
+{ 0x14EB, 0x14EB, 0x14EB },
+{ 0x14EC, 0x14EC, 0x14EC },
+{ 0x14ED, 0x14ED, 0x14ED },
+{ 0x14EE, 0x14EE, 0x14EE },
+{ 0x14EF, 0x14EF, 0x14EF },
+{ 0x14F0, 0x14F0, 0x14F0 },
+{ 0x14F1, 0x14F1, 0x14F1 },
+{ 0x14F2, 0x14F2, 0x14F2 },
+{ 0x14F3, 0x14F3, 0x14F3 },
+{ 0x14F4, 0x14F4, 0x14F4 },
+{ 0x14F5, 0x14F5, 0x14F5 },
+{ 0x14F6, 0x14F6, 0x14F6 },
+{ 0x14F7, 0x14F7, 0x14F7 },
+{ 0x14F8, 0x14F8, 0x14F8 },
+{ 0x14F9, 0x14F9, 0x14F9 },
+{ 0x14FA, 0x14FA, 0x14FA },
+{ 0x14FB, 0x14FB, 0x14FB },
+{ 0x14FC, 0x14FC, 0x14FC },
+{ 0x14FD, 0x14FD, 0x14FD },
+{ 0x14FE, 0x14FE, 0x14FE },
+{ 0x14FF, 0x14FF, 0x14FF },
+{ 0x1500, 0x1500, 0x1500 },
+{ 0x1501, 0x1501, 0x1501 },
+{ 0x1502, 0x1502, 0x1502 },
+{ 0x1503, 0x1503, 0x1503 },
+{ 0x1504, 0x1504, 0x1504 },
+{ 0x1505, 0x1505, 0x1505 },
+{ 0x1506, 0x1506, 0x1506 },
+{ 0x1507, 0x1507, 0x1507 },
+{ 0x1508, 0x1508, 0x1508 },
+{ 0x1509, 0x1509, 0x1509 },
+{ 0x150A, 0x150A, 0x150A },
+{ 0x150B, 0x150B, 0x150B },
+{ 0x150C, 0x150C, 0x150C },
+{ 0x150D, 0x150D, 0x150D },
+{ 0x150E, 0x150E, 0x150E },
+{ 0x150F, 0x150F, 0x150F },
+{ 0x1510, 0x1510, 0x1510 },
+{ 0x1511, 0x1511, 0x1511 },
+{ 0x1512, 0x1512, 0x1512 },
+{ 0x1513, 0x1513, 0x1513 },
+{ 0x1514, 0x1514, 0x1514 },
+{ 0x1515, 0x1515, 0x1515 },
+{ 0x1516, 0x1516, 0x1516 },
+{ 0x1517, 0x1517, 0x1517 },
+{ 0x1518, 0x1518, 0x1518 },
+{ 0x1519, 0x1519, 0x1519 },
+{ 0x151A, 0x151A, 0x151A },
+{ 0x151B, 0x151B, 0x151B },
+{ 0x151C, 0x151C, 0x151C },
+{ 0x151D, 0x151D, 0x151D },
+{ 0x151E, 0x151E, 0x151E },
+{ 0x151F, 0x151F, 0x151F },
+{ 0x1520, 0x1520, 0x1520 },
+{ 0x1521, 0x1521, 0x1521 },
+{ 0x1522, 0x1522, 0x1522 },
+{ 0x1523, 0x1523, 0x1523 },
+{ 0x1524, 0x1524, 0x1524 },
+{ 0x1525, 0x1525, 0x1525 },
+{ 0x1526, 0x1526, 0x1526 },
+{ 0x1527, 0x1527, 0x1527 },
+{ 0x1528, 0x1528, 0x1528 },
+{ 0x1529, 0x1529, 0x1529 },
+{ 0x152A, 0x152A, 0x152A },
+{ 0x152B, 0x152B, 0x152B },
+{ 0x152C, 0x152C, 0x152C },
+{ 0x152D, 0x152D, 0x152D },
+{ 0x152E, 0x152E, 0x152E },
+{ 0x152F, 0x152F, 0x152F },
+{ 0x1530, 0x1530, 0x1530 },
+{ 0x1531, 0x1531, 0x1531 },
+{ 0x1532, 0x1532, 0x1532 },
+{ 0x1533, 0x1533, 0x1533 },
+{ 0x1534, 0x1534, 0x1534 },
+{ 0x1535, 0x1535, 0x1535 },
+{ 0x1536, 0x1536, 0x1536 },
+{ 0x1537, 0x1537, 0x1537 },
+{ 0x1538, 0x1538, 0x1538 },
+{ 0x1539, 0x1539, 0x1539 },
+{ 0x153A, 0x153A, 0x153A },
+{ 0x153B, 0x153B, 0x153B },
+{ 0x153C, 0x153C, 0x153C },
+{ 0x153D, 0x153D, 0x153D },
+{ 0x153E, 0x153E, 0x153E },
+{ 0x153F, 0x153F, 0x153F },
+{ 0x1540, 0x1540, 0x1540 },
+{ 0x1541, 0x1541, 0x1541 },
+{ 0x1542, 0x1542, 0x1542 },
+{ 0x1543, 0x1543, 0x1543 },
+{ 0x1544, 0x1544, 0x1544 },
+{ 0x1545, 0x1545, 0x1545 },
+{ 0x1546, 0x1546, 0x1546 },
+{ 0x1547, 0x1547, 0x1547 },
+{ 0x1548, 0x1548, 0x1548 },
+{ 0x1549, 0x1549, 0x1549 },
+{ 0x154A, 0x154A, 0x154A },
+{ 0x154B, 0x154B, 0x154B },
+{ 0x154C, 0x154C, 0x154C },
+{ 0x154D, 0x154D, 0x154D },
+{ 0x154E, 0x154E, 0x154E },
+{ 0x154F, 0x154F, 0x154F },
+{ 0x1550, 0x1550, 0x1550 },
+{ 0x1551, 0x1551, 0x1551 },
+{ 0x1552, 0x1552, 0x1552 },
+{ 0x1553, 0x1553, 0x1553 },
+{ 0x1554, 0x1554, 0x1554 },
+{ 0x1555, 0x1555, 0x1555 },
+{ 0x1556, 0x1556, 0x1556 },
+{ 0x1557, 0x1557, 0x1557 },
+{ 0x1558, 0x1558, 0x1558 },
+{ 0x1559, 0x1559, 0x1559 },
+{ 0x155A, 0x155A, 0x155A },
+{ 0x155B, 0x155B, 0x155B },
+{ 0x155C, 0x155C, 0x155C },
+{ 0x155D, 0x155D, 0x155D },
+{ 0x155E, 0x155E, 0x155E },
+{ 0x155F, 0x155F, 0x155F },
+{ 0x1560, 0x1560, 0x1560 },
+{ 0x1561, 0x1561, 0x1561 },
+{ 0x1562, 0x1562, 0x1562 },
+{ 0x1563, 0x1563, 0x1563 },
+{ 0x1564, 0x1564, 0x1564 },
+{ 0x1565, 0x1565, 0x1565 },
+{ 0x1566, 0x1566, 0x1566 },
+{ 0x1567, 0x1567, 0x1567 },
+{ 0x1568, 0x1568, 0x1568 },
+{ 0x1569, 0x1569, 0x1569 },
+{ 0x156A, 0x156A, 0x156A },
+{ 0x156B, 0x156B, 0x156B },
+{ 0x156C, 0x156C, 0x156C },
+{ 0x156D, 0x156D, 0x156D },
+{ 0x156E, 0x156E, 0x156E },
+{ 0x156F, 0x156F, 0x156F },
+{ 0x1570, 0x1570, 0x1570 },
+{ 0x1571, 0x1571, 0x1571 },
+{ 0x1572, 0x1572, 0x1572 },
+{ 0x1573, 0x1573, 0x1573 },
+{ 0x1574, 0x1574, 0x1574 },
+{ 0x1575, 0x1575, 0x1575 },
+{ 0x1576, 0x1576, 0x1576 },
+{ 0x1577, 0x1577, 0x1577 },
+{ 0x1578, 0x1578, 0x1578 },
+{ 0x1579, 0x1579, 0x1579 },
+{ 0x157A, 0x157A, 0x157A },
+{ 0x157B, 0x157B, 0x157B },
+{ 0x157C, 0x157C, 0x157C },
+{ 0x157D, 0x157D, 0x157D },
+{ 0x157E, 0x157E, 0x157E },
+{ 0x157F, 0x157F, 0x157F },
+{ 0x1580, 0x1580, 0x1580 },
+{ 0x1581, 0x1581, 0x1581 },
+{ 0x1582, 0x1582, 0x1582 },
+{ 0x1583, 0x1583, 0x1583 },
+{ 0x1584, 0x1584, 0x1584 },
+{ 0x1585, 0x1585, 0x1585 },
+{ 0x1586, 0x1586, 0x1586 },
+{ 0x1587, 0x1587, 0x1587 },
+{ 0x1588, 0x1588, 0x1588 },
+{ 0x1589, 0x1589, 0x1589 },
+{ 0x158A, 0x158A, 0x158A },
+{ 0x158B, 0x158B, 0x158B },
+{ 0x158C, 0x158C, 0x158C },
+{ 0x158D, 0x158D, 0x158D },
+{ 0x158E, 0x158E, 0x158E },
+{ 0x158F, 0x158F, 0x158F },
+{ 0x1590, 0x1590, 0x1590 },
+{ 0x1591, 0x1591, 0x1591 },
+{ 0x1592, 0x1592, 0x1592 },
+{ 0x1593, 0x1593, 0x1593 },
+{ 0x1594, 0x1594, 0x1594 },
+{ 0x1595, 0x1595, 0x1595 },
+{ 0x1596, 0x1596, 0x1596 },
+{ 0x1597, 0x1597, 0x1597 },
+{ 0x1598, 0x1598, 0x1598 },
+{ 0x1599, 0x1599, 0x1599 },
+{ 0x159A, 0x159A, 0x159A },
+{ 0x159B, 0x159B, 0x159B },
+{ 0x159C, 0x159C, 0x159C },
+{ 0x159D, 0x159D, 0x159D },
+{ 0x159E, 0x159E, 0x159E },
+{ 0x159F, 0x159F, 0x159F },
+{ 0x15A0, 0x15A0, 0x15A0 },
+{ 0x15A1, 0x15A1, 0x15A1 },
+{ 0x15A2, 0x15A2, 0x15A2 },
+{ 0x15A3, 0x15A3, 0x15A3 },
+{ 0x15A4, 0x15A4, 0x15A4 },
+{ 0x15A5, 0x15A5, 0x15A5 },
+{ 0x15A6, 0x15A6, 0x15A6 },
+{ 0x15A7, 0x15A7, 0x15A7 },
+{ 0x15A8, 0x15A8, 0x15A8 },
+{ 0x15A9, 0x15A9, 0x15A9 },
+{ 0x15AA, 0x15AA, 0x15AA },
+{ 0x15AB, 0x15AB, 0x15AB },
+{ 0x15AC, 0x15AC, 0x15AC },
+{ 0x15AD, 0x15AD, 0x15AD },
+{ 0x15AE, 0x15AE, 0x15AE },
+{ 0x15AF, 0x15AF, 0x15AF },
+{ 0x15B0, 0x15B0, 0x15B0 },
+{ 0x15B1, 0x15B1, 0x15B1 },
+{ 0x15B2, 0x15B2, 0x15B2 },
+{ 0x15B3, 0x15B3, 0x15B3 },
+{ 0x15B4, 0x15B4, 0x15B4 },
+{ 0x15B5, 0x15B5, 0x15B5 },
+{ 0x15B6, 0x15B6, 0x15B6 },
+{ 0x15B7, 0x15B7, 0x15B7 },
+{ 0x15B8, 0x15B8, 0x15B8 },
+{ 0x15B9, 0x15B9, 0x15B9 },
+{ 0x15BA, 0x15BA, 0x15BA },
+{ 0x15BB, 0x15BB, 0x15BB },
+{ 0x15BC, 0x15BC, 0x15BC },
+{ 0x15BD, 0x15BD, 0x15BD },
+{ 0x15BE, 0x15BE, 0x15BE },
+{ 0x15BF, 0x15BF, 0x15BF },
+{ 0x15C0, 0x15C0, 0x15C0 },
+{ 0x15C1, 0x15C1, 0x15C1 },
+{ 0x15C2, 0x15C2, 0x15C2 },
+{ 0x15C3, 0x15C3, 0x15C3 },
+{ 0x15C4, 0x15C4, 0x15C4 },
+{ 0x15C5, 0x15C5, 0x15C5 },
+{ 0x15C6, 0x15C6, 0x15C6 },
+{ 0x15C7, 0x15C7, 0x15C7 },
+{ 0x15C8, 0x15C8, 0x15C8 },
+{ 0x15C9, 0x15C9, 0x15C9 },
+{ 0x15CA, 0x15CA, 0x15CA },
+{ 0x15CB, 0x15CB, 0x15CB },
+{ 0x15CC, 0x15CC, 0x15CC },
+{ 0x15CD, 0x15CD, 0x15CD },
+{ 0x15CE, 0x15CE, 0x15CE },
+{ 0x15CF, 0x15CF, 0x15CF },
+{ 0x15D0, 0x15D0, 0x15D0 },
+{ 0x15D1, 0x15D1, 0x15D1 },
+{ 0x15D2, 0x15D2, 0x15D2 },
+{ 0x15D3, 0x15D3, 0x15D3 },
+{ 0x15D4, 0x15D4, 0x15D4 },
+{ 0x15D5, 0x15D5, 0x15D5 },
+{ 0x15D6, 0x15D6, 0x15D6 },
+{ 0x15D7, 0x15D7, 0x15D7 },
+{ 0x15D8, 0x15D8, 0x15D8 },
+{ 0x15D9, 0x15D9, 0x15D9 },
+{ 0x15DA, 0x15DA, 0x15DA },
+{ 0x15DB, 0x15DB, 0x15DB },
+{ 0x15DC, 0x15DC, 0x15DC },
+{ 0x15DD, 0x15DD, 0x15DD },
+{ 0x15DE, 0x15DE, 0x15DE },
+{ 0x15DF, 0x15DF, 0x15DF },
+{ 0x15E0, 0x15E0, 0x15E0 },
+{ 0x15E1, 0x15E1, 0x15E1 },
+{ 0x15E2, 0x15E2, 0x15E2 },
+{ 0x15E3, 0x15E3, 0x15E3 },
+{ 0x15E4, 0x15E4, 0x15E4 },
+{ 0x15E5, 0x15E5, 0x15E5 },
+{ 0x15E6, 0x15E6, 0x15E6 },
+{ 0x15E7, 0x15E7, 0x15E7 },
+{ 0x15E8, 0x15E8, 0x15E8 },
+{ 0x15E9, 0x15E9, 0x15E9 },
+{ 0x15EA, 0x15EA, 0x15EA },
+{ 0x15EB, 0x15EB, 0x15EB },
+{ 0x15EC, 0x15EC, 0x15EC },
+{ 0x15ED, 0x15ED, 0x15ED },
+{ 0x15EE, 0x15EE, 0x15EE },
+{ 0x15EF, 0x15EF, 0x15EF },
+{ 0x15F0, 0x15F0, 0x15F0 },
+{ 0x15F1, 0x15F1, 0x15F1 },
+{ 0x15F2, 0x15F2, 0x15F2 },
+{ 0x15F3, 0x15F3, 0x15F3 },
+{ 0x15F4, 0x15F4, 0x15F4 },
+{ 0x15F5, 0x15F5, 0x15F5 },
+{ 0x15F6, 0x15F6, 0x15F6 },
+{ 0x15F7, 0x15F7, 0x15F7 },
+{ 0x15F8, 0x15F8, 0x15F8 },
+{ 0x15F9, 0x15F9, 0x15F9 },
+{ 0x15FA, 0x15FA, 0x15FA },
+{ 0x15FB, 0x15FB, 0x15FB },
+{ 0x15FC, 0x15FC, 0x15FC },
+{ 0x15FD, 0x15FD, 0x15FD },
+{ 0x15FE, 0x15FE, 0x15FE },
+{ 0x15FF, 0x15FF, 0x15FF },
+{ 0x1600, 0x1600, 0x1600 },
+{ 0x1601, 0x1601, 0x1601 },
+{ 0x1602, 0x1602, 0x1602 },
+{ 0x1603, 0x1603, 0x1603 },
+{ 0x1604, 0x1604, 0x1604 },
+{ 0x1605, 0x1605, 0x1605 },
+{ 0x1606, 0x1606, 0x1606 },
+{ 0x1607, 0x1607, 0x1607 },
+{ 0x1608, 0x1608, 0x1608 },
+{ 0x1609, 0x1609, 0x1609 },
+{ 0x160A, 0x160A, 0x160A },
+{ 0x160B, 0x160B, 0x160B },
+{ 0x160C, 0x160C, 0x160C },
+{ 0x160D, 0x160D, 0x160D },
+{ 0x160E, 0x160E, 0x160E },
+{ 0x160F, 0x160F, 0x160F },
+{ 0x1610, 0x1610, 0x1610 },
+{ 0x1611, 0x1611, 0x1611 },
+{ 0x1612, 0x1612, 0x1612 },
+{ 0x1613, 0x1613, 0x1613 },
+{ 0x1614, 0x1614, 0x1614 },
+{ 0x1615, 0x1615, 0x1615 },
+{ 0x1616, 0x1616, 0x1616 },
+{ 0x1617, 0x1617, 0x1617 },
+{ 0x1618, 0x1618, 0x1618 },
+{ 0x1619, 0x1619, 0x1619 },
+{ 0x161A, 0x161A, 0x161A },
+{ 0x161B, 0x161B, 0x161B },
+{ 0x161C, 0x161C, 0x161C },
+{ 0x161D, 0x161D, 0x161D },
+{ 0x161E, 0x161E, 0x161E },
+{ 0x161F, 0x161F, 0x161F },
+{ 0x1620, 0x1620, 0x1620 },
+{ 0x1621, 0x1621, 0x1621 },
+{ 0x1622, 0x1622, 0x1622 },
+{ 0x1623, 0x1623, 0x1623 },
+{ 0x1624, 0x1624, 0x1624 },
+{ 0x1625, 0x1625, 0x1625 },
+{ 0x1626, 0x1626, 0x1626 },
+{ 0x1627, 0x1627, 0x1627 },
+{ 0x1628, 0x1628, 0x1628 },
+{ 0x1629, 0x1629, 0x1629 },
+{ 0x162A, 0x162A, 0x162A },
+{ 0x162B, 0x162B, 0x162B },
+{ 0x162C, 0x162C, 0x162C },
+{ 0x162D, 0x162D, 0x162D },
+{ 0x162E, 0x162E, 0x162E },
+{ 0x162F, 0x162F, 0x162F },
+{ 0x1630, 0x1630, 0x1630 },
+{ 0x1631, 0x1631, 0x1631 },
+{ 0x1632, 0x1632, 0x1632 },
+{ 0x1633, 0x1633, 0x1633 },
+{ 0x1634, 0x1634, 0x1634 },
+{ 0x1635, 0x1635, 0x1635 },
+{ 0x1636, 0x1636, 0x1636 },
+{ 0x1637, 0x1637, 0x1637 },
+{ 0x1638, 0x1638, 0x1638 },
+{ 0x1639, 0x1639, 0x1639 },
+{ 0x163A, 0x163A, 0x163A },
+{ 0x163B, 0x163B, 0x163B },
+{ 0x163C, 0x163C, 0x163C },
+{ 0x163D, 0x163D, 0x163D },
+{ 0x163E, 0x163E, 0x163E },
+{ 0x163F, 0x163F, 0x163F },
+{ 0x1640, 0x1640, 0x1640 },
+{ 0x1641, 0x1641, 0x1641 },
+{ 0x1642, 0x1642, 0x1642 },
+{ 0x1643, 0x1643, 0x1643 },
+{ 0x1644, 0x1644, 0x1644 },
+{ 0x1645, 0x1645, 0x1645 },
+{ 0x1646, 0x1646, 0x1646 },
+{ 0x1647, 0x1647, 0x1647 },
+{ 0x1648, 0x1648, 0x1648 },
+{ 0x1649, 0x1649, 0x1649 },
+{ 0x164A, 0x164A, 0x164A },
+{ 0x164B, 0x164B, 0x164B },
+{ 0x164C, 0x164C, 0x164C },
+{ 0x164D, 0x164D, 0x164D },
+{ 0x164E, 0x164E, 0x164E },
+{ 0x164F, 0x164F, 0x164F },
+{ 0x1650, 0x1650, 0x1650 },
+{ 0x1651, 0x1651, 0x1651 },
+{ 0x1652, 0x1652, 0x1652 },
+{ 0x1653, 0x1653, 0x1653 },
+{ 0x1654, 0x1654, 0x1654 },
+{ 0x1655, 0x1655, 0x1655 },
+{ 0x1656, 0x1656, 0x1656 },
+{ 0x1657, 0x1657, 0x1657 },
+{ 0x1658, 0x1658, 0x1658 },
+{ 0x1659, 0x1659, 0x1659 },
+{ 0x165A, 0x165A, 0x165A },
+{ 0x165B, 0x165B, 0x165B },
+{ 0x165C, 0x165C, 0x165C },
+{ 0x165D, 0x165D, 0x165D },
+{ 0x165E, 0x165E, 0x165E },
+{ 0x165F, 0x165F, 0x165F },
+{ 0x1660, 0x1660, 0x1660 },
+{ 0x1661, 0x1661, 0x1661 },
+{ 0x1662, 0x1662, 0x1662 },
+{ 0x1663, 0x1663, 0x1663 },
+{ 0x1664, 0x1664, 0x1664 },
+{ 0x1665, 0x1665, 0x1665 },
+{ 0x1666, 0x1666, 0x1666 },
+{ 0x1667, 0x1667, 0x1667 },
+{ 0x1668, 0x1668, 0x1668 },
+{ 0x1669, 0x1669, 0x1669 },
+{ 0x166A, 0x166A, 0x166A },
+{ 0x166B, 0x166B, 0x166B },
+{ 0x166C, 0x166C, 0x166C },
+{ 0x166F, 0x166F, 0x166F },
+{ 0x1670, 0x1670, 0x1670 },
+{ 0x1671, 0x1671, 0x1671 },
+{ 0x1672, 0x1672, 0x1672 },
+{ 0x1673, 0x1673, 0x1673 },
+{ 0x1674, 0x1674, 0x1674 },
+{ 0x1675, 0x1675, 0x1675 },
+{ 0x1676, 0x1676, 0x1676 },
+{ 0x1681, 0x1681, 0x1681 },
+{ 0x1682, 0x1682, 0x1682 },
+{ 0x1683, 0x1683, 0x1683 },
+{ 0x1684, 0x1684, 0x1684 },
+{ 0x1685, 0x1685, 0x1685 },
+{ 0x1686, 0x1686, 0x1686 },
+{ 0x1687, 0x1687, 0x1687 },
+{ 0x1688, 0x1688, 0x1688 },
+{ 0x1689, 0x1689, 0x1689 },
+{ 0x168A, 0x168A, 0x168A },
+{ 0x168B, 0x168B, 0x168B },
+{ 0x168C, 0x168C, 0x168C },
+{ 0x168D, 0x168D, 0x168D },
+{ 0x168E, 0x168E, 0x168E },
+{ 0x168F, 0x168F, 0x168F },
+{ 0x1690, 0x1690, 0x1690 },
+{ 0x1691, 0x1691, 0x1691 },
+{ 0x1692, 0x1692, 0x1692 },
+{ 0x1693, 0x1693, 0x1693 },
+{ 0x1694, 0x1694, 0x1694 },
+{ 0x1695, 0x1695, 0x1695 },
+{ 0x1696, 0x1696, 0x1696 },
+{ 0x1697, 0x1697, 0x1697 },
+{ 0x1698, 0x1698, 0x1698 },
+{ 0x1699, 0x1699, 0x1699 },
+{ 0x169A, 0x169A, 0x169A },
+{ 0x16A0, 0x16A0, 0x16A0 },
+{ 0x16A1, 0x16A1, 0x16A1 },
+{ 0x16A2, 0x16A2, 0x16A2 },
+{ 0x16A3, 0x16A3, 0x16A3 },
+{ 0x16A4, 0x16A4, 0x16A4 },
+{ 0x16A5, 0x16A5, 0x16A5 },
+{ 0x16A6, 0x16A6, 0x16A6 },
+{ 0x16A7, 0x16A7, 0x16A7 },
+{ 0x16A8, 0x16A8, 0x16A8 },
+{ 0x16A9, 0x16A9, 0x16A9 },
+{ 0x16AA, 0x16AA, 0x16AA },
+{ 0x16AB, 0x16AB, 0x16AB },
+{ 0x16AC, 0x16AC, 0x16AC },
+{ 0x16AD, 0x16AD, 0x16AD },
+{ 0x16AE, 0x16AE, 0x16AE },
+{ 0x16AF, 0x16AF, 0x16AF },
+{ 0x16B0, 0x16B0, 0x16B0 },
+{ 0x16B1, 0x16B1, 0x16B1 },
+{ 0x16B2, 0x16B2, 0x16B2 },
+{ 0x16B3, 0x16B3, 0x16B3 },
+{ 0x16B4, 0x16B4, 0x16B4 },
+{ 0x16B5, 0x16B5, 0x16B5 },
+{ 0x16B6, 0x16B6, 0x16B6 },
+{ 0x16B7, 0x16B7, 0x16B7 },
+{ 0x16B8, 0x16B8, 0x16B8 },
+{ 0x16B9, 0x16B9, 0x16B9 },
+{ 0x16BA, 0x16BA, 0x16BA },
+{ 0x16BB, 0x16BB, 0x16BB },
+{ 0x16BC, 0x16BC, 0x16BC },
+{ 0x16BD, 0x16BD, 0x16BD },
+{ 0x16BE, 0x16BE, 0x16BE },
+{ 0x16BF, 0x16BF, 0x16BF },
+{ 0x16C0, 0x16C0, 0x16C0 },
+{ 0x16C1, 0x16C1, 0x16C1 },
+{ 0x16C2, 0x16C2, 0x16C2 },
+{ 0x16C3, 0x16C3, 0x16C3 },
+{ 0x16C4, 0x16C4, 0x16C4 },
+{ 0x16C5, 0x16C5, 0x16C5 },
+{ 0x16C6, 0x16C6, 0x16C6 },
+{ 0x16C7, 0x16C7, 0x16C7 },
+{ 0x16C8, 0x16C8, 0x16C8 },
+{ 0x16C9, 0x16C9, 0x16C9 },
+{ 0x16CA, 0x16CA, 0x16CA },
+{ 0x16CB, 0x16CB, 0x16CB },
+{ 0x16CC, 0x16CC, 0x16CC },
+{ 0x16CD, 0x16CD, 0x16CD },
+{ 0x16CE, 0x16CE, 0x16CE },
+{ 0x16CF, 0x16CF, 0x16CF },
+{ 0x16D0, 0x16D0, 0x16D0 },
+{ 0x16D1, 0x16D1, 0x16D1 },
+{ 0x16D2, 0x16D2, 0x16D2 },
+{ 0x16D3, 0x16D3, 0x16D3 },
+{ 0x16D4, 0x16D4, 0x16D4 },
+{ 0x16D5, 0x16D5, 0x16D5 },
+{ 0x16D6, 0x16D6, 0x16D6 },
+{ 0x16D7, 0x16D7, 0x16D7 },
+{ 0x16D8, 0x16D8, 0x16D8 },
+{ 0x16D9, 0x16D9, 0x16D9 },
+{ 0x16DA, 0x16DA, 0x16DA },
+{ 0x16DB, 0x16DB, 0x16DB },
+{ 0x16DC, 0x16DC, 0x16DC },
+{ 0x16DD, 0x16DD, 0x16DD },
+{ 0x16DE, 0x16DE, 0x16DE },
+{ 0x16DF, 0x16DF, 0x16DF },
+{ 0x16E0, 0x16E0, 0x16E0 },
+{ 0x16E1, 0x16E1, 0x16E1 },
+{ 0x16E2, 0x16E2, 0x16E2 },
+{ 0x16E3, 0x16E3, 0x16E3 },
+{ 0x16E4, 0x16E4, 0x16E4 },
+{ 0x16E5, 0x16E5, 0x16E5 },
+{ 0x16E6, 0x16E6, 0x16E6 },
+{ 0x16E7, 0x16E7, 0x16E7 },
+{ 0x16E8, 0x16E8, 0x16E8 },
+{ 0x16E9, 0x16E9, 0x16E9 },
+{ 0x16EA, 0x16EA, 0x16EA },
+{ 0x1700, 0x1700, 0x1700 },
+{ 0x1701, 0x1701, 0x1701 },
+{ 0x1702, 0x1702, 0x1702 },
+{ 0x1703, 0x1703, 0x1703 },
+{ 0x1704, 0x1704, 0x1704 },
+{ 0x1705, 0x1705, 0x1705 },
+{ 0x1706, 0x1706, 0x1706 },
+{ 0x1707, 0x1707, 0x1707 },
+{ 0x1708, 0x1708, 0x1708 },
+{ 0x1709, 0x1709, 0x1709 },
+{ 0x170A, 0x170A, 0x170A },
+{ 0x170B, 0x170B, 0x170B },
+{ 0x170C, 0x170C, 0x170C },
+{ 0x170E, 0x170E, 0x170E },
+{ 0x170F, 0x170F, 0x170F },
+{ 0x1710, 0x1710, 0x1710 },
+{ 0x1711, 0x1711, 0x1711 },
+{ 0x1712, 0x1712, 0x1712 },
+{ 0x1713, 0x1713, 0x1713 },
+{ 0x1714, 0x1714, 0x1714 },
+{ 0x1720, 0x1720, 0x1720 },
+{ 0x1721, 0x1721, 0x1721 },
+{ 0x1722, 0x1722, 0x1722 },
+{ 0x1723, 0x1723, 0x1723 },
+{ 0x1724, 0x1724, 0x1724 },
+{ 0x1725, 0x1725, 0x1725 },
+{ 0x1726, 0x1726, 0x1726 },
+{ 0x1727, 0x1727, 0x1727 },
+{ 0x1728, 0x1728, 0x1728 },
+{ 0x1729, 0x1729, 0x1729 },
+{ 0x172A, 0x172A, 0x172A },
+{ 0x172B, 0x172B, 0x172B },
+{ 0x172C, 0x172C, 0x172C },
+{ 0x172D, 0x172D, 0x172D },
+{ 0x172E, 0x172E, 0x172E },
+{ 0x172F, 0x172F, 0x172F },
+{ 0x1730, 0x1730, 0x1730 },
+{ 0x1731, 0x1731, 0x1731 },
+{ 0x1732, 0x1732, 0x1732 },
+{ 0x1733, 0x1733, 0x1733 },
+{ 0x1734, 0x1734, 0x1734 },
+{ 0x1740, 0x1740, 0x1740 },
+{ 0x1741, 0x1741, 0x1741 },
+{ 0x1742, 0x1742, 0x1742 },
+{ 0x1743, 0x1743, 0x1743 },
+{ 0x1744, 0x1744, 0x1744 },
+{ 0x1745, 0x1745, 0x1745 },
+{ 0x1746, 0x1746, 0x1746 },
+{ 0x1747, 0x1747, 0x1747 },
+{ 0x1748, 0x1748, 0x1748 },
+{ 0x1749, 0x1749, 0x1749 },
+{ 0x174A, 0x174A, 0x174A },
+{ 0x174B, 0x174B, 0x174B },
+{ 0x174C, 0x174C, 0x174C },
+{ 0x174D, 0x174D, 0x174D },
+{ 0x174E, 0x174E, 0x174E },
+{ 0x174F, 0x174F, 0x174F },
+{ 0x1750, 0x1750, 0x1750 },
+{ 0x1751, 0x1751, 0x1751 },
+{ 0x1752, 0x1752, 0x1752 },
+{ 0x1753, 0x1753, 0x1753 },
+{ 0x1760, 0x1760, 0x1760 },
+{ 0x1761, 0x1761, 0x1761 },
+{ 0x1762, 0x1762, 0x1762 },
+{ 0x1763, 0x1763, 0x1763 },
+{ 0x1764, 0x1764, 0x1764 },
+{ 0x1765, 0x1765, 0x1765 },
+{ 0x1766, 0x1766, 0x1766 },
+{ 0x1767, 0x1767, 0x1767 },
+{ 0x1768, 0x1768, 0x1768 },
+{ 0x1769, 0x1769, 0x1769 },
+{ 0x176A, 0x176A, 0x176A },
+{ 0x176B, 0x176B, 0x176B },
+{ 0x176C, 0x176C, 0x176C },
+{ 0x176E, 0x176E, 0x176E },
+{ 0x176F, 0x176F, 0x176F },
+{ 0x1770, 0x1770, 0x1770 },
+{ 0x1772, 0x1772, 0x1772 },
+{ 0x1773, 0x1773, 0x1773 },
+{ 0x1780, 0x1780, 0x1780 },
+{ 0x1781, 0x1781, 0x1781 },
+{ 0x1782, 0x1782, 0x1782 },
+{ 0x1783, 0x1783, 0x1783 },
+{ 0x1784, 0x1784, 0x1784 },
+{ 0x1785, 0x1785, 0x1785 },
+{ 0x1786, 0x1786, 0x1786 },
+{ 0x1787, 0x1787, 0x1787 },
+{ 0x1788, 0x1788, 0x1788 },
+{ 0x1789, 0x1789, 0x1789 },
+{ 0x178A, 0x178A, 0x178A },
+{ 0x178B, 0x178B, 0x178B },
+{ 0x178C, 0x178C, 0x178C },
+{ 0x178D, 0x178D, 0x178D },
+{ 0x178E, 0x178E, 0x178E },
+{ 0x178F, 0x178F, 0x178F },
+{ 0x1790, 0x1790, 0x1790 },
+{ 0x1791, 0x1791, 0x1791 },
+{ 0x1792, 0x1792, 0x1792 },
+{ 0x1793, 0x1793, 0x1793 },
+{ 0x1794, 0x1794, 0x1794 },
+{ 0x1795, 0x1795, 0x1795 },
+{ 0x1796, 0x1796, 0x1796 },
+{ 0x1797, 0x1797, 0x1797 },
+{ 0x1798, 0x1798, 0x1798 },
+{ 0x1799, 0x1799, 0x1799 },
+{ 0x179A, 0x179A, 0x179A },
+{ 0x179B, 0x179B, 0x179B },
+{ 0x179C, 0x179C, 0x179C },
+{ 0x179D, 0x179D, 0x179D },
+{ 0x179E, 0x179E, 0x179E },
+{ 0x179F, 0x179F, 0x179F },
+{ 0x17A0, 0x17A0, 0x17A0 },
+{ 0x17A1, 0x17A1, 0x17A1 },
+{ 0x17A2, 0x17A2, 0x17A2 },
+{ 0x17A3, 0x17A3, 0x17A3 },
+{ 0x17A4, 0x17A4, 0x17A4 },
+{ 0x17A5, 0x17A5, 0x17A5 },
+{ 0x17A6, 0x17A6, 0x17A6 },
+{ 0x17A7, 0x17A7, 0x17A7 },
+{ 0x17A8, 0x17A8, 0x17A8 },
+{ 0x17A9, 0x17A9, 0x17A9 },
+{ 0x17AA, 0x17AA, 0x17AA },
+{ 0x17AB, 0x17AB, 0x17AB },
+{ 0x17AC, 0x17AC, 0x17AC },
+{ 0x17AD, 0x17AD, 0x17AD },
+{ 0x17AE, 0x17AE, 0x17AE },
+{ 0x17AF, 0x17AF, 0x17AF },
+{ 0x17B0, 0x17B0, 0x17B0 },
+{ 0x17B1, 0x17B1, 0x17B1 },
+{ 0x17B2, 0x17B2, 0x17B2 },
+{ 0x17B3, 0x17B3, 0x17B3 },
+{ 0x17B7, 0x17B7, 0x17B7 },
+{ 0x17B8, 0x17B8, 0x17B8 },
+{ 0x17B9, 0x17B9, 0x17B9 },
+{ 0x17BA, 0x17BA, 0x17BA },
+{ 0x17BB, 0x17BB, 0x17BB },
+{ 0x17BC, 0x17BC, 0x17BC },
+{ 0x17BD, 0x17BD, 0x17BD },
+{ 0x17C6, 0x17C6, 0x17C6 },
+{ 0x17C9, 0x17C9, 0x17C9 },
+{ 0x17CA, 0x17CA, 0x17CA },
+{ 0x17CB, 0x17CB, 0x17CB },
+{ 0x17CC, 0x17CC, 0x17CC },
+{ 0x17CD, 0x17CD, 0x17CD },
+{ 0x17CE, 0x17CE, 0x17CE },
+{ 0x17CF, 0x17CF, 0x17CF },
+{ 0x17D0, 0x17D0, 0x17D0 },
+{ 0x17D1, 0x17D1, 0x17D1 },
+{ 0x17D2, 0x17D2, 0x17D2 },
+{ 0x17D3, 0x17D3, 0x17D3 },
+{ 0x17D7, 0x17D7, 0x17D7 },
+{ 0x17DC, 0x17DC, 0x17DC },
+{ 0x17DD, 0x17DD, 0x17DD },
+{ 0x180B, 0x180B, 0x180B },
+{ 0x180C, 0x180C, 0x180C },
+{ 0x180D, 0x180D, 0x180D },
+{ 0x1820, 0x1820, 0x1820 },
+{ 0x1821, 0x1821, 0x1821 },
+{ 0x1822, 0x1822, 0x1822 },
+{ 0x1823, 0x1823, 0x1823 },
+{ 0x1824, 0x1824, 0x1824 },
+{ 0x1825, 0x1825, 0x1825 },
+{ 0x1826, 0x1826, 0x1826 },
+{ 0x1827, 0x1827, 0x1827 },
+{ 0x1828, 0x1828, 0x1828 },
+{ 0x1829, 0x1829, 0x1829 },
+{ 0x182A, 0x182A, 0x182A },
+{ 0x182B, 0x182B, 0x182B },
+{ 0x182C, 0x182C, 0x182C },
+{ 0x182D, 0x182D, 0x182D },
+{ 0x182E, 0x182E, 0x182E },
+{ 0x182F, 0x182F, 0x182F },
+{ 0x1830, 0x1830, 0x1830 },
+{ 0x1831, 0x1831, 0x1831 },
+{ 0x1832, 0x1832, 0x1832 },
+{ 0x1833, 0x1833, 0x1833 },
+{ 0x1834, 0x1834, 0x1834 },
+{ 0x1835, 0x1835, 0x1835 },
+{ 0x1836, 0x1836, 0x1836 },
+{ 0x1837, 0x1837, 0x1837 },
+{ 0x1838, 0x1838, 0x1838 },
+{ 0x1839, 0x1839, 0x1839 },
+{ 0x183A, 0x183A, 0x183A },
+{ 0x183B, 0x183B, 0x183B },
+{ 0x183C, 0x183C, 0x183C },
+{ 0x183D, 0x183D, 0x183D },
+{ 0x183E, 0x183E, 0x183E },
+{ 0x183F, 0x183F, 0x183F },
+{ 0x1840, 0x1840, 0x1840 },
+{ 0x1841, 0x1841, 0x1841 },
+{ 0x1842, 0x1842, 0x1842 },
+{ 0x1843, 0x1843, 0x1843 },
+{ 0x1844, 0x1844, 0x1844 },
+{ 0x1845, 0x1845, 0x1845 },
+{ 0x1846, 0x1846, 0x1846 },
+{ 0x1847, 0x1847, 0x1847 },
+{ 0x1848, 0x1848, 0x1848 },
+{ 0x1849, 0x1849, 0x1849 },
+{ 0x184A, 0x184A, 0x184A },
+{ 0x184B, 0x184B, 0x184B },
+{ 0x184C, 0x184C, 0x184C },
+{ 0x184D, 0x184D, 0x184D },
+{ 0x184E, 0x184E, 0x184E },
+{ 0x184F, 0x184F, 0x184F },
+{ 0x1850, 0x1850, 0x1850 },
+{ 0x1851, 0x1851, 0x1851 },
+{ 0x1852, 0x1852, 0x1852 },
+{ 0x1853, 0x1853, 0x1853 },
+{ 0x1854, 0x1854, 0x1854 },
+{ 0x1855, 0x1855, 0x1855 },
+{ 0x1856, 0x1856, 0x1856 },
+{ 0x1857, 0x1857, 0x1857 },
+{ 0x1858, 0x1858, 0x1858 },
+{ 0x1859, 0x1859, 0x1859 },
+{ 0x185A, 0x185A, 0x185A },
+{ 0x185B, 0x185B, 0x185B },
+{ 0x185C, 0x185C, 0x185C },
+{ 0x185D, 0x185D, 0x185D },
+{ 0x185E, 0x185E, 0x185E },
+{ 0x185F, 0x185F, 0x185F },
+{ 0x1860, 0x1860, 0x1860 },
+{ 0x1861, 0x1861, 0x1861 },
+{ 0x1862, 0x1862, 0x1862 },
+{ 0x1863, 0x1863, 0x1863 },
+{ 0x1864, 0x1864, 0x1864 },
+{ 0x1865, 0x1865, 0x1865 },
+{ 0x1866, 0x1866, 0x1866 },
+{ 0x1867, 0x1867, 0x1867 },
+{ 0x1868, 0x1868, 0x1868 },
+{ 0x1869, 0x1869, 0x1869 },
+{ 0x186A, 0x186A, 0x186A },
+{ 0x186B, 0x186B, 0x186B },
+{ 0x186C, 0x186C, 0x186C },
+{ 0x186D, 0x186D, 0x186D },
+{ 0x186E, 0x186E, 0x186E },
+{ 0x186F, 0x186F, 0x186F },
+{ 0x1870, 0x1870, 0x1870 },
+{ 0x1871, 0x1871, 0x1871 },
+{ 0x1872, 0x1872, 0x1872 },
+{ 0x1873, 0x1873, 0x1873 },
+{ 0x1874, 0x1874, 0x1874 },
+{ 0x1875, 0x1875, 0x1875 },
+{ 0x1876, 0x1876, 0x1876 },
+{ 0x1877, 0x1877, 0x1877 },
+{ 0x1880, 0x1880, 0x1880 },
+{ 0x1881, 0x1881, 0x1881 },
+{ 0x1882, 0x1882, 0x1882 },
+{ 0x1883, 0x1883, 0x1883 },
+{ 0x1884, 0x1884, 0x1884 },
+{ 0x1885, 0x1885, 0x1885 },
+{ 0x1886, 0x1886, 0x1886 },
+{ 0x1887, 0x1887, 0x1887 },
+{ 0x1888, 0x1888, 0x1888 },
+{ 0x1889, 0x1889, 0x1889 },
+{ 0x188A, 0x188A, 0x188A },
+{ 0x188B, 0x188B, 0x188B },
+{ 0x188C, 0x188C, 0x188C },
+{ 0x188D, 0x188D, 0x188D },
+{ 0x188E, 0x188E, 0x188E },
+{ 0x188F, 0x188F, 0x188F },
+{ 0x1890, 0x1890, 0x1890 },
+{ 0x1891, 0x1891, 0x1891 },
+{ 0x1892, 0x1892, 0x1892 },
+{ 0x1893, 0x1893, 0x1893 },
+{ 0x1894, 0x1894, 0x1894 },
+{ 0x1895, 0x1895, 0x1895 },
+{ 0x1896, 0x1896, 0x1896 },
+{ 0x1897, 0x1897, 0x1897 },
+{ 0x1898, 0x1898, 0x1898 },
+{ 0x1899, 0x1899, 0x1899 },
+{ 0x189A, 0x189A, 0x189A },
+{ 0x189B, 0x189B, 0x189B },
+{ 0x189C, 0x189C, 0x189C },
+{ 0x189D, 0x189D, 0x189D },
+{ 0x189E, 0x189E, 0x189E },
+{ 0x189F, 0x189F, 0x189F },
+{ 0x18A0, 0x18A0, 0x18A0 },
+{ 0x18A1, 0x18A1, 0x18A1 },
+{ 0x18A2, 0x18A2, 0x18A2 },
+{ 0x18A3, 0x18A3, 0x18A3 },
+{ 0x18A4, 0x18A4, 0x18A4 },
+{ 0x18A5, 0x18A5, 0x18A5 },
+{ 0x18A6, 0x18A6, 0x18A6 },
+{ 0x18A7, 0x18A7, 0x18A7 },
+{ 0x18A8, 0x18A8, 0x18A8 },
+{ 0x18A9, 0x18A9, 0x18A9 },
+{ 0x1900, 0x1900, 0x1900 },
+{ 0x1901, 0x1901, 0x1901 },
+{ 0x1902, 0x1902, 0x1902 },
+{ 0x1903, 0x1903, 0x1903 },
+{ 0x1904, 0x1904, 0x1904 },
+{ 0x1905, 0x1905, 0x1905 },
+{ 0x1906, 0x1906, 0x1906 },
+{ 0x1907, 0x1907, 0x1907 },
+{ 0x1908, 0x1908, 0x1908 },
+{ 0x1909, 0x1909, 0x1909 },
+{ 0x190A, 0x190A, 0x190A },
+{ 0x190B, 0x190B, 0x190B },
+{ 0x190C, 0x190C, 0x190C },
+{ 0x190D, 0x190D, 0x190D },
+{ 0x190E, 0x190E, 0x190E },
+{ 0x190F, 0x190F, 0x190F },
+{ 0x1910, 0x1910, 0x1910 },
+{ 0x1911, 0x1911, 0x1911 },
+{ 0x1912, 0x1912, 0x1912 },
+{ 0x1913, 0x1913, 0x1913 },
+{ 0x1914, 0x1914, 0x1914 },
+{ 0x1915, 0x1915, 0x1915 },
+{ 0x1916, 0x1916, 0x1916 },
+{ 0x1917, 0x1917, 0x1917 },
+{ 0x1918, 0x1918, 0x1918 },
+{ 0x1919, 0x1919, 0x1919 },
+{ 0x191A, 0x191A, 0x191A },
+{ 0x191B, 0x191B, 0x191B },
+{ 0x191C, 0x191C, 0x191C },
+{ 0x1920, 0x1920, 0x1920 },
+{ 0x1921, 0x1921, 0x1921 },
+{ 0x1922, 0x1922, 0x1922 },
+{ 0x1927, 0x1927, 0x1927 },
+{ 0x1928, 0x1928, 0x1928 },
+{ 0x1932, 0x1932, 0x1932 },
+{ 0x1939, 0x1939, 0x1939 },
+{ 0x193A, 0x193A, 0x193A },
+{ 0x193B, 0x193B, 0x193B },
+{ 0x1950, 0x1950, 0x1950 },
+{ 0x1951, 0x1951, 0x1951 },
+{ 0x1952, 0x1952, 0x1952 },
+{ 0x1953, 0x1953, 0x1953 },
+{ 0x1954, 0x1954, 0x1954 },
+{ 0x1955, 0x1955, 0x1955 },
+{ 0x1956, 0x1956, 0x1956 },
+{ 0x1957, 0x1957, 0x1957 },
+{ 0x1958, 0x1958, 0x1958 },
+{ 0x1959, 0x1959, 0x1959 },
+{ 0x195A, 0x195A, 0x195A },
+{ 0x195B, 0x195B, 0x195B },
+{ 0x195C, 0x195C, 0x195C },
+{ 0x195D, 0x195D, 0x195D },
+{ 0x195E, 0x195E, 0x195E },
+{ 0x195F, 0x195F, 0x195F },
+{ 0x1960, 0x1960, 0x1960 },
+{ 0x1961, 0x1961, 0x1961 },
+{ 0x1962, 0x1962, 0x1962 },
+{ 0x1963, 0x1963, 0x1963 },
+{ 0x1964, 0x1964, 0x1964 },
+{ 0x1965, 0x1965, 0x1965 },
+{ 0x1966, 0x1966, 0x1966 },
+{ 0x1967, 0x1967, 0x1967 },
+{ 0x1968, 0x1968, 0x1968 },
+{ 0x1969, 0x1969, 0x1969 },
+{ 0x196A, 0x196A, 0x196A },
+{ 0x196B, 0x196B, 0x196B },
+{ 0x196C, 0x196C, 0x196C },
+{ 0x196D, 0x196D, 0x196D },
+{ 0x1970, 0x1970, 0x1970 },
+{ 0x1971, 0x1971, 0x1971 },
+{ 0x1972, 0x1972, 0x1972 },
+{ 0x1973, 0x1973, 0x1973 },
+{ 0x1974, 0x1974, 0x1974 },
+{ 0x1980, 0x1980, 0x1980 },
+{ 0x1981, 0x1981, 0x1981 },
+{ 0x1982, 0x1982, 0x1982 },
+{ 0x1983, 0x1983, 0x1983 },
+{ 0x1984, 0x1984, 0x1984 },
+{ 0x1985, 0x1985, 0x1985 },
+{ 0x1986, 0x1986, 0x1986 },
+{ 0x1987, 0x1987, 0x1987 },
+{ 0x1988, 0x1988, 0x1988 },
+{ 0x1989, 0x1989, 0x1989 },
+{ 0x198A, 0x198A, 0x198A },
+{ 0x198B, 0x198B, 0x198B },
+{ 0x198C, 0x198C, 0x198C },
+{ 0x198D, 0x198D, 0x198D },
+{ 0x198E, 0x198E, 0x198E },
+{ 0x198F, 0x198F, 0x198F },
+{ 0x1990, 0x1990, 0x1990 },
+{ 0x1991, 0x1991, 0x1991 },
+{ 0x1992, 0x1992, 0x1992 },
+{ 0x1993, 0x1993, 0x1993 },
+{ 0x1994, 0x1994, 0x1994 },
+{ 0x1995, 0x1995, 0x1995 },
+{ 0x1996, 0x1996, 0x1996 },
+{ 0x1997, 0x1997, 0x1997 },
+{ 0x1998, 0x1998, 0x1998 },
+{ 0x1999, 0x1999, 0x1999 },
+{ 0x199A, 0x199A, 0x199A },
+{ 0x199B, 0x199B, 0x199B },
+{ 0x199C, 0x199C, 0x199C },
+{ 0x199D, 0x199D, 0x199D },
+{ 0x199E, 0x199E, 0x199E },
+{ 0x199F, 0x199F, 0x199F },
+{ 0x19A0, 0x19A0, 0x19A0 },
+{ 0x19A1, 0x19A1, 0x19A1 },
+{ 0x19A2, 0x19A2, 0x19A2 },
+{ 0x19A3, 0x19A3, 0x19A3 },
+{ 0x19A4, 0x19A4, 0x19A4 },
+{ 0x19A5, 0x19A5, 0x19A5 },
+{ 0x19A6, 0x19A6, 0x19A6 },
+{ 0x19A7, 0x19A7, 0x19A7 },
+{ 0x19A8, 0x19A8, 0x19A8 },
+{ 0x19A9, 0x19A9, 0x19A9 },
+{ 0x19C1, 0x19C1, 0x19C1 },
+{ 0x19C2, 0x19C2, 0x19C2 },
+{ 0x19C3, 0x19C3, 0x19C3 },
+{ 0x19C4, 0x19C4, 0x19C4 },
+{ 0x19C5, 0x19C5, 0x19C5 },
+{ 0x19C6, 0x19C6, 0x19C6 },
+{ 0x19C7, 0x19C7, 0x19C7 },
+{ 0x1A00, 0x1A00, 0x1A00 },
+{ 0x1A01, 0x1A01, 0x1A01 },
+{ 0x1A02, 0x1A02, 0x1A02 },
+{ 0x1A03, 0x1A03, 0x1A03 },
+{ 0x1A04, 0x1A04, 0x1A04 },
+{ 0x1A05, 0x1A05, 0x1A05 },
+{ 0x1A06, 0x1A06, 0x1A06 },
+{ 0x1A07, 0x1A07, 0x1A07 },
+{ 0x1A08, 0x1A08, 0x1A08 },
+{ 0x1A09, 0x1A09, 0x1A09 },
+{ 0x1A0A, 0x1A0A, 0x1A0A },
+{ 0x1A0B, 0x1A0B, 0x1A0B },
+{ 0x1A0C, 0x1A0C, 0x1A0C },
+{ 0x1A0D, 0x1A0D, 0x1A0D },
+{ 0x1A0E, 0x1A0E, 0x1A0E },
+{ 0x1A0F, 0x1A0F, 0x1A0F },
+{ 0x1A10, 0x1A10, 0x1A10 },
+{ 0x1A11, 0x1A11, 0x1A11 },
+{ 0x1A12, 0x1A12, 0x1A12 },
+{ 0x1A13, 0x1A13, 0x1A13 },
+{ 0x1A14, 0x1A14, 0x1A14 },
+{ 0x1A15, 0x1A15, 0x1A15 },
+{ 0x1A16, 0x1A16, 0x1A16 },
+{ 0x1A17, 0x1A17, 0x1A17 },
+{ 0x1A18, 0x1A18, 0x1A18 },
+{ 0x1D00, 0x1D00, 0x1D00 },
+{ 0x1D01, 0x1D01, 0x1D01 },
+{ 0x1D02, 0x1D02, 0x1D02 },
+{ 0x1D03, 0x1D03, 0x1D03 },
+{ 0x1D04, 0x1D04, 0x1D04 },
+{ 0x1D05, 0x1D05, 0x1D05 },
+{ 0x1D06, 0x1D06, 0x1D06 },
+{ 0x1D07, 0x1D07, 0x1D07 },
+{ 0x1D08, 0x1D08, 0x1D08 },
+{ 0x1D09, 0x1D09, 0x1D09 },
+{ 0x1D0A, 0x1D0A, 0x1D0A },
+{ 0x1D0B, 0x1D0B, 0x1D0B },
+{ 0x1D0C, 0x1D0C, 0x1D0C },
+{ 0x1D0D, 0x1D0D, 0x1D0D },
+{ 0x1D0E, 0x1D0E, 0x1D0E },
+{ 0x1D0F, 0x1D0F, 0x1D0F },
+{ 0x1D10, 0x1D10, 0x1D10 },
+{ 0x1D11, 0x1D11, 0x1D11 },
+{ 0x1D12, 0x1D12, 0x1D12 },
+{ 0x1D13, 0x1D13, 0x1D13 },
+{ 0x1D14, 0x1D14, 0x1D14 },
+{ 0x1D15, 0x1D15, 0x1D15 },
+{ 0x1D16, 0x1D16, 0x1D16 },
+{ 0x1D17, 0x1D17, 0x1D17 },
+{ 0x1D18, 0x1D18, 0x1D18 },
+{ 0x1D19, 0x1D19, 0x1D19 },
+{ 0x1D1A, 0x1D1A, 0x1D1A },
+{ 0x1D1B, 0x1D1B, 0x1D1B },
+{ 0x1D1C, 0x1D1C, 0x1D1C },
+{ 0x1D1D, 0x1D1D, 0x1D1D },
+{ 0x1D1E, 0x1D1E, 0x1D1E },
+{ 0x1D1F, 0x1D1F, 0x1D1F },
+{ 0x1D20, 0x1D20, 0x1D20 },
+{ 0x1D21, 0x1D21, 0x1D21 },
+{ 0x1D22, 0x1D22, 0x1D22 },
+{ 0x1D23, 0x1D23, 0x1D23 },
+{ 0x1D24, 0x1D24, 0x1D24 },
+{ 0x1D25, 0x1D25, 0x1D25 },
+{ 0x1D26, 0x1D26, 0x1D26 },
+{ 0x1D27, 0x1D27, 0x1D27 },
+{ 0x1D28, 0x1D28, 0x1D28 },
+{ 0x1D29, 0x1D29, 0x1D29 },
+{ 0x1D2A, 0x1D2A, 0x1D2A },
+{ 0x1D2B, 0x1D2B, 0x1D2B },
+{ 0x1D2C, 0x1D2C, 0x1D2C },
+{ 0x1D2D, 0x1D2D, 0x1D2D },
+{ 0x1D2E, 0x1D2E, 0x1D2E },
+{ 0x1D2F, 0x1D2F, 0x1D2F },
+{ 0x1D30, 0x1D30, 0x1D30 },
+{ 0x1D31, 0x1D31, 0x1D31 },
+{ 0x1D32, 0x1D32, 0x1D32 },
+{ 0x1D33, 0x1D33, 0x1D33 },
+{ 0x1D34, 0x1D34, 0x1D34 },
+{ 0x1D35, 0x1D35, 0x1D35 },
+{ 0x1D36, 0x1D36, 0x1D36 },
+{ 0x1D37, 0x1D37, 0x1D37 },
+{ 0x1D38, 0x1D38, 0x1D38 },
+{ 0x1D39, 0x1D39, 0x1D39 },
+{ 0x1D3A, 0x1D3A, 0x1D3A },
+{ 0x1D3B, 0x1D3B, 0x1D3B },
+{ 0x1D3C, 0x1D3C, 0x1D3C },
+{ 0x1D3D, 0x1D3D, 0x1D3D },
+{ 0x1D3E, 0x1D3E, 0x1D3E },
+{ 0x1D3F, 0x1D3F, 0x1D3F },
+{ 0x1D40, 0x1D40, 0x1D40 },
+{ 0x1D41, 0x1D41, 0x1D41 },
+{ 0x1D42, 0x1D42, 0x1D42 },
+{ 0x1D43, 0x1D43, 0x1D43 },
+{ 0x1D44, 0x1D44, 0x1D44 },
+{ 0x1D45, 0x1D45, 0x1D45 },
+{ 0x1D46, 0x1D46, 0x1D46 },
+{ 0x1D47, 0x1D47, 0x1D47 },
+{ 0x1D48, 0x1D48, 0x1D48 },
+{ 0x1D49, 0x1D49, 0x1D49 },
+{ 0x1D4A, 0x1D4A, 0x1D4A },
+{ 0x1D4B, 0x1D4B, 0x1D4B },
+{ 0x1D4C, 0x1D4C, 0x1D4C },
+{ 0x1D4D, 0x1D4D, 0x1D4D },
+{ 0x1D4E, 0x1D4E, 0x1D4E },
+{ 0x1D4F, 0x1D4F, 0x1D4F },
+{ 0x1D50, 0x1D50, 0x1D50 },
+{ 0x1D51, 0x1D51, 0x1D51 },
+{ 0x1D52, 0x1D52, 0x1D52 },
+{ 0x1D53, 0x1D53, 0x1D53 },
+{ 0x1D54, 0x1D54, 0x1D54 },
+{ 0x1D55, 0x1D55, 0x1D55 },
+{ 0x1D56, 0x1D56, 0x1D56 },
+{ 0x1D57, 0x1D57, 0x1D57 },
+{ 0x1D58, 0x1D58, 0x1D58 },
+{ 0x1D59, 0x1D59, 0x1D59 },
+{ 0x1D5A, 0x1D5A, 0x1D5A },
+{ 0x1D5B, 0x1D5B, 0x1D5B },
+{ 0x1D5C, 0x1D5C, 0x1D5C },
+{ 0x1D5D, 0x1D5D, 0x1D5D },
+{ 0x1D5E, 0x1D5E, 0x1D5E },
+{ 0x1D5F, 0x1D5F, 0x1D5F },
+{ 0x1D60, 0x1D60, 0x1D60 },
+{ 0x1D61, 0x1D61, 0x1D61 },
+{ 0x1D62, 0x1D62, 0x1D62 },
+{ 0x1D63, 0x1D63, 0x1D63 },
+{ 0x1D64, 0x1D64, 0x1D64 },
+{ 0x1D65, 0x1D65, 0x1D65 },
+{ 0x1D66, 0x1D66, 0x1D66 },
+{ 0x1D67, 0x1D67, 0x1D67 },
+{ 0x1D68, 0x1D68, 0x1D68 },
+{ 0x1D69, 0x1D69, 0x1D69 },
+{ 0x1D6A, 0x1D6A, 0x1D6A },
+{ 0x1D6B, 0x1D6B, 0x1D6B },
+{ 0x1D6C, 0x1D6C, 0x1D6C },
+{ 0x1D6D, 0x1D6D, 0x1D6D },
+{ 0x1D6E, 0x1D6E, 0x1D6E },
+{ 0x1D6F, 0x1D6F, 0x1D6F },
+{ 0x1D70, 0x1D70, 0x1D70 },
+{ 0x1D71, 0x1D71, 0x1D71 },
+{ 0x1D72, 0x1D72, 0x1D72 },
+{ 0x1D73, 0x1D73, 0x1D73 },
+{ 0x1D74, 0x1D74, 0x1D74 },
+{ 0x1D75, 0x1D75, 0x1D75 },
+{ 0x1D76, 0x1D76, 0x1D76 },
+{ 0x1D77, 0x1D77, 0x1D77 },
+{ 0x1D78, 0x1D78, 0x1D78 },
+{ 0x1D79, 0x1D79, 0x1D79 },
+{ 0x1D7A, 0x1D7A, 0x1D7A },
+{ 0x1D7B, 0x1D7B, 0x1D7B },
+{ 0x1D7C, 0x1D7C, 0x1D7C },
+{ 0x1D7D, 0x1D7D, 0x1D7D },
+{ 0x1D7E, 0x1D7E, 0x1D7E },
+{ 0x1D7F, 0x1D7F, 0x1D7F },
+{ 0x1D80, 0x1D80, 0x1D80 },
+{ 0x1D81, 0x1D81, 0x1D81 },
+{ 0x1D82, 0x1D82, 0x1D82 },
+{ 0x1D83, 0x1D83, 0x1D83 },
+{ 0x1D84, 0x1D84, 0x1D84 },
+{ 0x1D85, 0x1D85, 0x1D85 },
+{ 0x1D86, 0x1D86, 0x1D86 },
+{ 0x1D87, 0x1D87, 0x1D87 },
+{ 0x1D88, 0x1D88, 0x1D88 },
+{ 0x1D89, 0x1D89, 0x1D89 },
+{ 0x1D8A, 0x1D8A, 0x1D8A },
+{ 0x1D8B, 0x1D8B, 0x1D8B },
+{ 0x1D8C, 0x1D8C, 0x1D8C },
+{ 0x1D8D, 0x1D8D, 0x1D8D },
+{ 0x1D8E, 0x1D8E, 0x1D8E },
+{ 0x1D8F, 0x1D8F, 0x1D8F },
+{ 0x1D90, 0x1D90, 0x1D90 },
+{ 0x1D91, 0x1D91, 0x1D91 },
+{ 0x1D92, 0x1D92, 0x1D92 },
+{ 0x1D93, 0x1D93, 0x1D93 },
+{ 0x1D94, 0x1D94, 0x1D94 },
+{ 0x1D95, 0x1D95, 0x1D95 },
+{ 0x1D96, 0x1D96, 0x1D96 },
+{ 0x1D97, 0x1D97, 0x1D97 },
+{ 0x1D98, 0x1D98, 0x1D98 },
+{ 0x1D99, 0x1D99, 0x1D99 },
+{ 0x1D9A, 0x1D9A, 0x1D9A },
+{ 0x1D9B, 0x1D9B, 0x1D9B },
+{ 0x1D9C, 0x1D9C, 0x1D9C },
+{ 0x1D9D, 0x1D9D, 0x1D9D },
+{ 0x1D9E, 0x1D9E, 0x1D9E },
+{ 0x1D9F, 0x1D9F, 0x1D9F },
+{ 0x1DA0, 0x1DA0, 0x1DA0 },
+{ 0x1DA1, 0x1DA1, 0x1DA1 },
+{ 0x1DA2, 0x1DA2, 0x1DA2 },
+{ 0x1DA3, 0x1DA3, 0x1DA3 },
+{ 0x1DA4, 0x1DA4, 0x1DA4 },
+{ 0x1DA5, 0x1DA5, 0x1DA5 },
+{ 0x1DA6, 0x1DA6, 0x1DA6 },
+{ 0x1DA7, 0x1DA7, 0x1DA7 },
+{ 0x1DA8, 0x1DA8, 0x1DA8 },
+{ 0x1DA9, 0x1DA9, 0x1DA9 },
+{ 0x1DAA, 0x1DAA, 0x1DAA },
+{ 0x1DAB, 0x1DAB, 0x1DAB },
+{ 0x1DAC, 0x1DAC, 0x1DAC },
+{ 0x1DAD, 0x1DAD, 0x1DAD },
+{ 0x1DAE, 0x1DAE, 0x1DAE },
+{ 0x1DAF, 0x1DAF, 0x1DAF },
+{ 0x1DB0, 0x1DB0, 0x1DB0 },
+{ 0x1DB1, 0x1DB1, 0x1DB1 },
+{ 0x1DB2, 0x1DB2, 0x1DB2 },
+{ 0x1DB3, 0x1DB3, 0x1DB3 },
+{ 0x1DB4, 0x1DB4, 0x1DB4 },
+{ 0x1DB5, 0x1DB5, 0x1DB5 },
+{ 0x1DB6, 0x1DB6, 0x1DB6 },
+{ 0x1DB7, 0x1DB7, 0x1DB7 },
+{ 0x1DB8, 0x1DB8, 0x1DB8 },
+{ 0x1DB9, 0x1DB9, 0x1DB9 },
+{ 0x1DBA, 0x1DBA, 0x1DBA },
+{ 0x1DBB, 0x1DBB, 0x1DBB },
+{ 0x1DBC, 0x1DBC, 0x1DBC },
+{ 0x1DBD, 0x1DBD, 0x1DBD },
+{ 0x1DBE, 0x1DBE, 0x1DBE },
+{ 0x1DBF, 0x1DBF, 0x1DBF },
+{ 0x1DC0, 0x1DC0, 0x1DC0 },
+{ 0x1DC1, 0x1DC1, 0x1DC1 },
+{ 0x1DC2, 0x1DC2, 0x1DC2 },
+{ 0x1DC3, 0x1DC3, 0x1DC3 },
+{ 0x1E00, 0x1E00, 0x1E01 },
+{ 0x1E01, 0x1E00, 0x1E01 },
+{ 0x1E02, 0x1E02, 0x1E03 },
+{ 0x1E03, 0x1E02, 0x1E03 },
+{ 0x1E04, 0x1E04, 0x1E05 },
+{ 0x1E05, 0x1E04, 0x1E05 },
+{ 0x1E06, 0x1E06, 0x1E07 },
+{ 0x1E07, 0x1E06, 0x1E07 },
+{ 0x1E08, 0x1E08, 0x1E09 },
+{ 0x1E09, 0x1E08, 0x1E09 },
+{ 0x1E0A, 0x1E0A, 0x1E0B },
+{ 0x1E0B, 0x1E0A, 0x1E0B },
+{ 0x1E0C, 0x1E0C, 0x1E0D },
+{ 0x1E0D, 0x1E0C, 0x1E0D },
+{ 0x1E0E, 0x1E0E, 0x1E0F },
+{ 0x1E0F, 0x1E0E, 0x1E0F },
+{ 0x1E10, 0x1E10, 0x1E11 },
+{ 0x1E11, 0x1E10, 0x1E11 },
+{ 0x1E12, 0x1E12, 0x1E13 },
+{ 0x1E13, 0x1E12, 0x1E13 },
+{ 0x1E14, 0x1E14, 0x1E15 },
+{ 0x1E15, 0x1E14, 0x1E15 },
+{ 0x1E16, 0x1E16, 0x1E17 },
+{ 0x1E17, 0x1E16, 0x1E17 },
+{ 0x1E18, 0x1E18, 0x1E19 },
+{ 0x1E19, 0x1E18, 0x1E19 },
+{ 0x1E1A, 0x1E1A, 0x1E1B },
+{ 0x1E1B, 0x1E1A, 0x1E1B },
+{ 0x1E1C, 0x1E1C, 0x1E1D },
+{ 0x1E1D, 0x1E1C, 0x1E1D },
+{ 0x1E1E, 0x1E1E, 0x1E1F },
+{ 0x1E1F, 0x1E1E, 0x1E1F },
+{ 0x1E20, 0x1E20, 0x1E21 },
+{ 0x1E21, 0x1E20, 0x1E21 },
+{ 0x1E22, 0x1E22, 0x1E23 },
+{ 0x1E23, 0x1E22, 0x1E23 },
+{ 0x1E24, 0x1E24, 0x1E25 },
+{ 0x1E25, 0x1E24, 0x1E25 },
+{ 0x1E26, 0x1E26, 0x1E27 },
+{ 0x1E27, 0x1E26, 0x1E27 },
+{ 0x1E28, 0x1E28, 0x1E29 },
+{ 0x1E29, 0x1E28, 0x1E29 },
+{ 0x1E2A, 0x1E2A, 0x1E2B },
+{ 0x1E2B, 0x1E2A, 0x1E2B },
+{ 0x1E2C, 0x1E2C, 0x1E2D },
+{ 0x1E2D, 0x1E2C, 0x1E2D },
+{ 0x1E2E, 0x1E2E, 0x1E2F },
+{ 0x1E2F, 0x1E2E, 0x1E2F },
+{ 0x1E30, 0x1E30, 0x1E31 },
+{ 0x1E31, 0x1E30, 0x1E31 },
+{ 0x1E32, 0x1E32, 0x1E33 },
+{ 0x1E33, 0x1E32, 0x1E33 },
+{ 0x1E34, 0x1E34, 0x1E35 },
+{ 0x1E35, 0x1E34, 0x1E35 },
+{ 0x1E36, 0x1E36, 0x1E37 },
+{ 0x1E37, 0x1E36, 0x1E37 },
+{ 0x1E38, 0x1E38, 0x1E39 },
+{ 0x1E39, 0x1E38, 0x1E39 },
+{ 0x1E3A, 0x1E3A, 0x1E3B },
+{ 0x1E3B, 0x1E3A, 0x1E3B },
+{ 0x1E3C, 0x1E3C, 0x1E3D },
+{ 0x1E3D, 0x1E3C, 0x1E3D },
+{ 0x1E3E, 0x1E3E, 0x1E3F },
+{ 0x1E3F, 0x1E3E, 0x1E3F },
+{ 0x1E40, 0x1E40, 0x1E41 },
+{ 0x1E41, 0x1E40, 0x1E41 },
+{ 0x1E42, 0x1E42, 0x1E43 },
+{ 0x1E43, 0x1E42, 0x1E43 },
+{ 0x1E44, 0x1E44, 0x1E45 },
+{ 0x1E45, 0x1E44, 0x1E45 },
+{ 0x1E46, 0x1E46, 0x1E47 },
+{ 0x1E47, 0x1E46, 0x1E47 },
+{ 0x1E48, 0x1E48, 0x1E49 },
+{ 0x1E49, 0x1E48, 0x1E49 },
+{ 0x1E4A, 0x1E4A, 0x1E4B },
+{ 0x1E4B, 0x1E4A, 0x1E4B },
+{ 0x1E4C, 0x1E4C, 0x1E4D },
+{ 0x1E4D, 0x1E4C, 0x1E4D },
+{ 0x1E4E, 0x1E4E, 0x1E4F },
+{ 0x1E4F, 0x1E4E, 0x1E4F },
+{ 0x1E50, 0x1E50, 0x1E51 },
+{ 0x1E51, 0x1E50, 0x1E51 },
+{ 0x1E52, 0x1E52, 0x1E53 },
+{ 0x1E53, 0x1E52, 0x1E53 },
+{ 0x1E54, 0x1E54, 0x1E55 },
+{ 0x1E55, 0x1E54, 0x1E55 },
+{ 0x1E56, 0x1E56, 0x1E57 },
+{ 0x1E57, 0x1E56, 0x1E57 },
+{ 0x1E58, 0x1E58, 0x1E59 },
+{ 0x1E59, 0x1E58, 0x1E59 },
+{ 0x1E5A, 0x1E5A, 0x1E5B },
+{ 0x1E5B, 0x1E5A, 0x1E5B },
+{ 0x1E5C, 0x1E5C, 0x1E5D },
+{ 0x1E5D, 0x1E5C, 0x1E5D },
+{ 0x1E5E, 0x1E5E, 0x1E5F },
+{ 0x1E5F, 0x1E5E, 0x1E5F },
+{ 0x1E60, 0x1E60, 0x1E61 },
+{ 0x1E61, 0x1E60, 0x1E61 },
+{ 0x1E62, 0x1E62, 0x1E63 },
+{ 0x1E63, 0x1E62, 0x1E63 },
+{ 0x1E64, 0x1E64, 0x1E65 },
+{ 0x1E65, 0x1E64, 0x1E65 },
+{ 0x1E66, 0x1E66, 0x1E67 },
+{ 0x1E67, 0x1E66, 0x1E67 },
+{ 0x1E68, 0x1E68, 0x1E69 },
+{ 0x1E69, 0x1E68, 0x1E69 },
+{ 0x1E6A, 0x1E6A, 0x1E6B },
+{ 0x1E6B, 0x1E6A, 0x1E6B },
+{ 0x1E6C, 0x1E6C, 0x1E6D },
+{ 0x1E6D, 0x1E6C, 0x1E6D },
+{ 0x1E6E, 0x1E6E, 0x1E6F },
+{ 0x1E6F, 0x1E6E, 0x1E6F },
+{ 0x1E70, 0x1E70, 0x1E71 },
+{ 0x1E71, 0x1E70, 0x1E71 },
+{ 0x1E72, 0x1E72, 0x1E73 },
+{ 0x1E73, 0x1E72, 0x1E73 },
+{ 0x1E74, 0x1E74, 0x1E75 },
+{ 0x1E75, 0x1E74, 0x1E75 },
+{ 0x1E76, 0x1E76, 0x1E77 },
+{ 0x1E77, 0x1E76, 0x1E77 },
+{ 0x1E78, 0x1E78, 0x1E79 },
+{ 0x1E79, 0x1E78, 0x1E79 },
+{ 0x1E7A, 0x1E7A, 0x1E7B },
+{ 0x1E7B, 0x1E7A, 0x1E7B },
+{ 0x1E7C, 0x1E7C, 0x1E7D },
+{ 0x1E7D, 0x1E7C, 0x1E7D },
+{ 0x1E7E, 0x1E7E, 0x1E7F },
+{ 0x1E7F, 0x1E7E, 0x1E7F },
+{ 0x1E80, 0x1E80, 0x1E81 },
+{ 0x1E81, 0x1E80, 0x1E81 },
+{ 0x1E82, 0x1E82, 0x1E83 },
+{ 0x1E83, 0x1E82, 0x1E83 },
+{ 0x1E84, 0x1E84, 0x1E85 },
+{ 0x1E85, 0x1E84, 0x1E85 },
+{ 0x1E86, 0x1E86, 0x1E87 },
+{ 0x1E87, 0x1E86, 0x1E87 },
+{ 0x1E88, 0x1E88, 0x1E89 },
+{ 0x1E89, 0x1E88, 0x1E89 },
+{ 0x1E8A, 0x1E8A, 0x1E8B },
+{ 0x1E8B, 0x1E8A, 0x1E8B },
+{ 0x1E8C, 0x1E8C, 0x1E8D },
+{ 0x1E8D, 0x1E8C, 0x1E8D },
+{ 0x1E8E, 0x1E8E, 0x1E8F },
+{ 0x1E8F, 0x1E8E, 0x1E8F },
+{ 0x1E90, 0x1E90, 0x1E91 },
+{ 0x1E91, 0x1E90, 0x1E91 },
+{ 0x1E92, 0x1E92, 0x1E93 },
+{ 0x1E93, 0x1E92, 0x1E93 },
+{ 0x1E94, 0x1E94, 0x1E95 },
+{ 0x1E95, 0x1E94, 0x1E95 },
+{ 0x1E96, 0x1E96, 0x1E96 },
+{ 0x1E97, 0x1E97, 0x1E97 },
+{ 0x1E98, 0x1E98, 0x1E98 },
+{ 0x1E99, 0x1E99, 0x1E99 },
+{ 0x1E9A, 0x1E9A, 0x1E9A },
+{ 0x1E9B, 0x1E60, 0x1E9B },
+{ 0x1EA0, 0x1EA0, 0x1EA1 },
+{ 0x1EA1, 0x1EA0, 0x1EA1 },
+{ 0x1EA2, 0x1EA2, 0x1EA3 },
+{ 0x1EA3, 0x1EA2, 0x1EA3 },
+{ 0x1EA4, 0x1EA4, 0x1EA5 },
+{ 0x1EA5, 0x1EA4, 0x1EA5 },
+{ 0x1EA6, 0x1EA6, 0x1EA7 },
+{ 0x1EA7, 0x1EA6, 0x1EA7 },
+{ 0x1EA8, 0x1EA8, 0x1EA9 },
+{ 0x1EA9, 0x1EA8, 0x1EA9 },
+{ 0x1EAA, 0x1EAA, 0x1EAB },
+{ 0x1EAB, 0x1EAA, 0x1EAB },
+{ 0x1EAC, 0x1EAC, 0x1EAD },
+{ 0x1EAD, 0x1EAC, 0x1EAD },
+{ 0x1EAE, 0x1EAE, 0x1EAF },
+{ 0x1EAF, 0x1EAE, 0x1EAF },
+{ 0x1EB0, 0x1EB0, 0x1EB1 },
+{ 0x1EB1, 0x1EB0, 0x1EB1 },
+{ 0x1EB2, 0x1EB2, 0x1EB3 },
+{ 0x1EB3, 0x1EB2, 0x1EB3 },
+{ 0x1EB4, 0x1EB4, 0x1EB5 },
+{ 0x1EB5, 0x1EB4, 0x1EB5 },
+{ 0x1EB6, 0x1EB6, 0x1EB7 },
+{ 0x1EB7, 0x1EB6, 0x1EB7 },
+{ 0x1EB8, 0x1EB8, 0x1EB9 },
+{ 0x1EB9, 0x1EB8, 0x1EB9 },
+{ 0x1EBA, 0x1EBA, 0x1EBB },
+{ 0x1EBB, 0x1EBA, 0x1EBB },
+{ 0x1EBC, 0x1EBC, 0x1EBD },
+{ 0x1EBD, 0x1EBC, 0x1EBD },
+{ 0x1EBE, 0x1EBE, 0x1EBF },
+{ 0x1EBF, 0x1EBE, 0x1EBF },
+{ 0x1EC0, 0x1EC0, 0x1EC1 },
+{ 0x1EC1, 0x1EC0, 0x1EC1 },
+{ 0x1EC2, 0x1EC2, 0x1EC3 },
+{ 0x1EC3, 0x1EC2, 0x1EC3 },
+{ 0x1EC4, 0x1EC4, 0x1EC5 },
+{ 0x1EC5, 0x1EC4, 0x1EC5 },
+{ 0x1EC6, 0x1EC6, 0x1EC7 },
+{ 0x1EC7, 0x1EC6, 0x1EC7 },
+{ 0x1EC8, 0x1EC8, 0x1EC9 },
+{ 0x1EC9, 0x1EC8, 0x1EC9 },
+{ 0x1ECA, 0x1ECA, 0x1ECB },
+{ 0x1ECB, 0x1ECA, 0x1ECB },
+{ 0x1ECC, 0x1ECC, 0x1ECD },
+{ 0x1ECD, 0x1ECC, 0x1ECD },
+{ 0x1ECE, 0x1ECE, 0x1ECF },
+{ 0x1ECF, 0x1ECE, 0x1ECF },
+{ 0x1ED0, 0x1ED0, 0x1ED1 },
+{ 0x1ED1, 0x1ED0, 0x1ED1 },
+{ 0x1ED2, 0x1ED2, 0x1ED3 },
+{ 0x1ED3, 0x1ED2, 0x1ED3 },
+{ 0x1ED4, 0x1ED4, 0x1ED5 },
+{ 0x1ED5, 0x1ED4, 0x1ED5 },
+{ 0x1ED6, 0x1ED6, 0x1ED7 },
+{ 0x1ED7, 0x1ED6, 0x1ED7 },
+{ 0x1ED8, 0x1ED8, 0x1ED9 },
+{ 0x1ED9, 0x1ED8, 0x1ED9 },
+{ 0x1EDA, 0x1EDA, 0x1EDB },
+{ 0x1EDB, 0x1EDA, 0x1EDB },
+{ 0x1EDC, 0x1EDC, 0x1EDD },
+{ 0x1EDD, 0x1EDC, 0x1EDD },
+{ 0x1EDE, 0x1EDE, 0x1EDF },
+{ 0x1EDF, 0x1EDE, 0x1EDF },
+{ 0x1EE0, 0x1EE0, 0x1EE1 },
+{ 0x1EE1, 0x1EE0, 0x1EE1 },
+{ 0x1EE2, 0x1EE2, 0x1EE3 },
+{ 0x1EE3, 0x1EE2, 0x1EE3 },
+{ 0x1EE4, 0x1EE4, 0x1EE5 },
+{ 0x1EE5, 0x1EE4, 0x1EE5 },
+{ 0x1EE6, 0x1EE6, 0x1EE7 },
+{ 0x1EE7, 0x1EE6, 0x1EE7 },
+{ 0x1EE8, 0x1EE8, 0x1EE9 },
+{ 0x1EE9, 0x1EE8, 0x1EE9 },
+{ 0x1EEA, 0x1EEA, 0x1EEB },
+{ 0x1EEB, 0x1EEA, 0x1EEB },
+{ 0x1EEC, 0x1EEC, 0x1EED },
+{ 0x1EED, 0x1EEC, 0x1EED },
+{ 0x1EEE, 0x1EEE, 0x1EEF },
+{ 0x1EEF, 0x1EEE, 0x1EEF },
+{ 0x1EF0, 0x1EF0, 0x1EF1 },
+{ 0x1EF1, 0x1EF0, 0x1EF1 },
+{ 0x1EF2, 0x1EF2, 0x1EF3 },
+{ 0x1EF3, 0x1EF2, 0x1EF3 },
+{ 0x1EF4, 0x1EF4, 0x1EF5 },
+{ 0x1EF5, 0x1EF4, 0x1EF5 },
+{ 0x1EF6, 0x1EF6, 0x1EF7 },
+{ 0x1EF7, 0x1EF6, 0x1EF7 },
+{ 0x1EF8, 0x1EF8, 0x1EF9 },
+{ 0x1EF9, 0x1EF8, 0x1EF9 },
+{ 0x1F00, 0x1F08, 0x1F00 },
+{ 0x1F01, 0x1F09, 0x1F01 },
+{ 0x1F02, 0x1F0A, 0x1F02 },
+{ 0x1F03, 0x1F0B, 0x1F03 },
+{ 0x1F04, 0x1F0C, 0x1F04 },
+{ 0x1F05, 0x1F0D, 0x1F05 },
+{ 0x1F06, 0x1F0E, 0x1F06 },
+{ 0x1F07, 0x1F0F, 0x1F07 },
+{ 0x1F08, 0x1F08, 0x1F00 },
+{ 0x1F09, 0x1F09, 0x1F01 },
+{ 0x1F0A, 0x1F0A, 0x1F02 },
+{ 0x1F0B, 0x1F0B, 0x1F03 },
+{ 0x1F0C, 0x1F0C, 0x1F04 },
+{ 0x1F0D, 0x1F0D, 0x1F05 },
+{ 0x1F0E, 0x1F0E, 0x1F06 },
+{ 0x1F0F, 0x1F0F, 0x1F07 },
+{ 0x1F10, 0x1F18, 0x1F10 },
+{ 0x1F11, 0x1F19, 0x1F11 },
+{ 0x1F12, 0x1F1A, 0x1F12 },
+{ 0x1F13, 0x1F1B, 0x1F13 },
+{ 0x1F14, 0x1F1C, 0x1F14 },
+{ 0x1F15, 0x1F1D, 0x1F15 },
+{ 0x1F18, 0x1F18, 0x1F10 },
+{ 0x1F19, 0x1F19, 0x1F11 },
+{ 0x1F1A, 0x1F1A, 0x1F12 },
+{ 0x1F1B, 0x1F1B, 0x1F13 },
+{ 0x1F1C, 0x1F1C, 0x1F14 },
+{ 0x1F1D, 0x1F1D, 0x1F15 },
+{ 0x1F20, 0x1F28, 0x1F20 },
+{ 0x1F21, 0x1F29, 0x1F21 },
+{ 0x1F22, 0x1F2A, 0x1F22 },
+{ 0x1F23, 0x1F2B, 0x1F23 },
+{ 0x1F24, 0x1F2C, 0x1F24 },
+{ 0x1F25, 0x1F2D, 0x1F25 },
+{ 0x1F26, 0x1F2E, 0x1F26 },
+{ 0x1F27, 0x1F2F, 0x1F27 },
+{ 0x1F28, 0x1F28, 0x1F20 },
+{ 0x1F29, 0x1F29, 0x1F21 },
+{ 0x1F2A, 0x1F2A, 0x1F22 },
+{ 0x1F2B, 0x1F2B, 0x1F23 },
+{ 0x1F2C, 0x1F2C, 0x1F24 },
+{ 0x1F2D, 0x1F2D, 0x1F25 },
+{ 0x1F2E, 0x1F2E, 0x1F26 },
+{ 0x1F2F, 0x1F2F, 0x1F27 },
+{ 0x1F30, 0x1F38, 0x1F30 },
+{ 0x1F31, 0x1F39, 0x1F31 },
+{ 0x1F32, 0x1F3A, 0x1F32 },
+{ 0x1F33, 0x1F3B, 0x1F33 },
+{ 0x1F34, 0x1F3C, 0x1F34 },
+{ 0x1F35, 0x1F3D, 0x1F35 },
+{ 0x1F36, 0x1F3E, 0x1F36 },
+{ 0x1F37, 0x1F3F, 0x1F37 },
+{ 0x1F38, 0x1F38, 0x1F30 },
+{ 0x1F39, 0x1F39, 0x1F31 },
+{ 0x1F3A, 0x1F3A, 0x1F32 },
+{ 0x1F3B, 0x1F3B, 0x1F33 },
+{ 0x1F3C, 0x1F3C, 0x1F34 },
+{ 0x1F3D, 0x1F3D, 0x1F35 },
+{ 0x1F3E, 0x1F3E, 0x1F36 },
+{ 0x1F3F, 0x1F3F, 0x1F37 },
+{ 0x1F40, 0x1F48, 0x1F40 },
+{ 0x1F41, 0x1F49, 0x1F41 },
+{ 0x1F42, 0x1F4A, 0x1F42 },
+{ 0x1F43, 0x1F4B, 0x1F43 },
+{ 0x1F44, 0x1F4C, 0x1F44 },
+{ 0x1F45, 0x1F4D, 0x1F45 },
+{ 0x1F48, 0x1F48, 0x1F40 },
+{ 0x1F49, 0x1F49, 0x1F41 },
+{ 0x1F4A, 0x1F4A, 0x1F42 },
+{ 0x1F4B, 0x1F4B, 0x1F43 },
+{ 0x1F4C, 0x1F4C, 0x1F44 },
+{ 0x1F4D, 0x1F4D, 0x1F45 },
+{ 0x1F50, 0x1F50, 0x1F50 },
+{ 0x1F51, 0x1F59, 0x1F51 },
+{ 0x1F52, 0x1F52, 0x1F52 },
+{ 0x1F53, 0x1F5B, 0x1F53 },
+{ 0x1F54, 0x1F54, 0x1F54 },
+{ 0x1F55, 0x1F5D, 0x1F55 },
+{ 0x1F56, 0x1F56, 0x1F56 },
+{ 0x1F57, 0x1F5F, 0x1F57 },
+{ 0x1F59, 0x1F59, 0x1F51 },
+{ 0x1F5B, 0x1F5B, 0x1F53 },
+{ 0x1F5D, 0x1F5D, 0x1F55 },
+{ 0x1F5F, 0x1F5F, 0x1F57 },
+{ 0x1F60, 0x1F68, 0x1F60 },
+{ 0x1F61, 0x1F69, 0x1F61 },
+{ 0x1F62, 0x1F6A, 0x1F62 },
+{ 0x1F63, 0x1F6B, 0x1F63 },
+{ 0x1F64, 0x1F6C, 0x1F64 },
+{ 0x1F65, 0x1F6D, 0x1F65 },
+{ 0x1F66, 0x1F6E, 0x1F66 },
+{ 0x1F67, 0x1F6F, 0x1F67 },
+{ 0x1F68, 0x1F68, 0x1F60 },
+{ 0x1F69, 0x1F69, 0x1F61 },
+{ 0x1F6A, 0x1F6A, 0x1F62 },
+{ 0x1F6B, 0x1F6B, 0x1F63 },
+{ 0x1F6C, 0x1F6C, 0x1F64 },
+{ 0x1F6D, 0x1F6D, 0x1F65 },
+{ 0x1F6E, 0x1F6E, 0x1F66 },
+{ 0x1F6F, 0x1F6F, 0x1F67 },
+{ 0x1F70, 0x1FBA, 0x1F70 },
+{ 0x1F71, 0x1FBB, 0x1F71 },
+{ 0x1F72, 0x1FC8, 0x1F72 },
+{ 0x1F73, 0x1FC9, 0x1F73 },
+{ 0x1F74, 0x1FCA, 0x1F74 },
+{ 0x1F75, 0x1FCB, 0x1F75 },
+{ 0x1F76, 0x1FDA, 0x1F76 },
+{ 0x1F77, 0x1FDB, 0x1F77 },
+{ 0x1F78, 0x1FF8, 0x1F78 },
+{ 0x1F79, 0x1FF9, 0x1F79 },
+{ 0x1F7A, 0x1FEA, 0x1F7A },
+{ 0x1F7B, 0x1FEB, 0x1F7B },
+{ 0x1F7C, 0x1FFA, 0x1F7C },
+{ 0x1F7D, 0x1FFB, 0x1F7D },
+{ 0x1F80, 0x1F88, 0x1F80 },
+{ 0x1F81, 0x1F89, 0x1F81 },
+{ 0x1F82, 0x1F8A, 0x1F82 },
+{ 0x1F83, 0x1F8B, 0x1F83 },
+{ 0x1F84, 0x1F8C, 0x1F84 },
+{ 0x1F85, 0x1F8D, 0x1F85 },
+{ 0x1F86, 0x1F8E, 0x1F86 },
+{ 0x1F87, 0x1F8F, 0x1F87 },
+{ 0x1F88, 0x1F88, 0x1F80 },
+{ 0x1F89, 0x1F89, 0x1F81 },
+{ 0x1F8A, 0x1F8A, 0x1F82 },
+{ 0x1F8B, 0x1F8B, 0x1F83 },
+{ 0x1F8C, 0x1F8C, 0x1F84 },
+{ 0x1F8D, 0x1F8D, 0x1F85 },
+{ 0x1F8E, 0x1F8E, 0x1F86 },
+{ 0x1F8F, 0x1F8F, 0x1F87 },
+{ 0x1F90, 0x1F98, 0x1F90 },
+{ 0x1F91, 0x1F99, 0x1F91 },
+{ 0x1F92, 0x1F9A, 0x1F92 },
+{ 0x1F93, 0x1F9B, 0x1F93 },
+{ 0x1F94, 0x1F9C, 0x1F94 },
+{ 0x1F95, 0x1F9D, 0x1F95 },
+{ 0x1F96, 0x1F9E, 0x1F96 },
+{ 0x1F97, 0x1F9F, 0x1F97 },
+{ 0x1F98, 0x1F98, 0x1F90 },
+{ 0x1F99, 0x1F99, 0x1F91 },
+{ 0x1F9A, 0x1F9A, 0x1F92 },
+{ 0x1F9B, 0x1F9B, 0x1F93 },
+{ 0x1F9C, 0x1F9C, 0x1F94 },
+{ 0x1F9D, 0x1F9D, 0x1F95 },
+{ 0x1F9E, 0x1F9E, 0x1F96 },
+{ 0x1F9F, 0x1F9F, 0x1F97 },
+{ 0x1FA0, 0x1FA8, 0x1FA0 },
+{ 0x1FA1, 0x1FA9, 0x1FA1 },
+{ 0x1FA2, 0x1FAA, 0x1FA2 },
+{ 0x1FA3, 0x1FAB, 0x1FA3 },
+{ 0x1FA4, 0x1FAC, 0x1FA4 },
+{ 0x1FA5, 0x1FAD, 0x1FA5 },
+{ 0x1FA6, 0x1FAE, 0x1FA6 },
+{ 0x1FA7, 0x1FAF, 0x1FA7 },
+{ 0x1FA8, 0x1FA8, 0x1FA0 },
+{ 0x1FA9, 0x1FA9, 0x1FA1 },
+{ 0x1FAA, 0x1FAA, 0x1FA2 },
+{ 0x1FAB, 0x1FAB, 0x1FA3 },
+{ 0x1FAC, 0x1FAC, 0x1FA4 },
+{ 0x1FAD, 0x1FAD, 0x1FA5 },
+{ 0x1FAE, 0x1FAE, 0x1FA6 },
+{ 0x1FAF, 0x1FAF, 0x1FA7 },
+{ 0x1FB0, 0x1FB8, 0x1FB0 },
+{ 0x1FB1, 0x1FB9, 0x1FB1 },
+{ 0x1FB2, 0x1FB2, 0x1FB2 },
+{ 0x1FB3, 0x1FBC, 0x1FB3 },
+{ 0x1FB4, 0x1FB4, 0x1FB4 },
+{ 0x1FB6, 0x1FB6, 0x1FB6 },
+{ 0x1FB7, 0x1FB7, 0x1FB7 },
+{ 0x1FB8, 0x1FB8, 0x1FB0 },
+{ 0x1FB9, 0x1FB9, 0x1FB1 },
+{ 0x1FBA, 0x1FBA, 0x1F70 },
+{ 0x1FBB, 0x1FBB, 0x1F71 },
+{ 0x1FBC, 0x1FBC, 0x1FB3 },
+{ 0x1FBE, 0x0399, 0x1FBE },
+{ 0x1FC2, 0x1FC2, 0x1FC2 },
+{ 0x1FC3, 0x1FCC, 0x1FC3 },
+{ 0x1FC4, 0x1FC4, 0x1FC4 },
+{ 0x1FC6, 0x1FC6, 0x1FC6 },
+{ 0x1FC7, 0x1FC7, 0x1FC7 },
+{ 0x1FC8, 0x1FC8, 0x1F72 },
+{ 0x1FC9, 0x1FC9, 0x1F73 },
+{ 0x1FCA, 0x1FCA, 0x1F74 },
+{ 0x1FCB, 0x1FCB, 0x1F75 },
+{ 0x1FCC, 0x1FCC, 0x1FC3 },
+{ 0x1FD0, 0x1FD8, 0x1FD0 },
+{ 0x1FD1, 0x1FD9, 0x1FD1 },
+{ 0x1FD2, 0x1FD2, 0x1FD2 },
+{ 0x1FD3, 0x1FD3, 0x1FD3 },
+{ 0x1FD6, 0x1FD6, 0x1FD6 },
+{ 0x1FD7, 0x1FD7, 0x1FD7 },
+{ 0x1FD8, 0x1FD8, 0x1FD0 },
+{ 0x1FD9, 0x1FD9, 0x1FD1 },
+{ 0x1FDA, 0x1FDA, 0x1F76 },
+{ 0x1FDB, 0x1FDB, 0x1F77 },
+{ 0x1FE0, 0x1FE8, 0x1FE0 },
+{ 0x1FE1, 0x1FE9, 0x1FE1 },
+{ 0x1FE2, 0x1FE2, 0x1FE2 },
+{ 0x1FE3, 0x1FE3, 0x1FE3 },
+{ 0x1FE4, 0x1FE4, 0x1FE4 },
+{ 0x1FE5, 0x1FEC, 0x1FE5 },
+{ 0x1FE6, 0x1FE6, 0x1FE6 },
+{ 0x1FE7, 0x1FE7, 0x1FE7 },
+{ 0x1FE8, 0x1FE8, 0x1FE0 },
+{ 0x1FE9, 0x1FE9, 0x1FE1 },
+{ 0x1FEA, 0x1FEA, 0x1F7A },
+{ 0x1FEB, 0x1FEB, 0x1F7B },
+{ 0x1FEC, 0x1FEC, 0x1FE5 },
+{ 0x1FF2, 0x1FF2, 0x1FF2 },
+{ 0x1FF3, 0x1FFC, 0x1FF3 },
+{ 0x1FF4, 0x1FF4, 0x1FF4 },
+{ 0x1FF6, 0x1FF6, 0x1FF6 },
+{ 0x1FF7, 0x1FF7, 0x1FF7 },
+{ 0x1FF8, 0x1FF8, 0x1F78 },
+{ 0x1FF9, 0x1FF9, 0x1F79 },
+{ 0x1FFA, 0x1FFA, 0x1F7C },
+{ 0x1FFB, 0x1FFB, 0x1F7D },
+{ 0x1FFC, 0x1FFC, 0x1FF3 },
+{ 0x2071, 0x2071, 0x2071 },
+{ 0x207F, 0x207F, 0x207F },
+{ 0x2090, 0x2090, 0x2090 },
+{ 0x2091, 0x2091, 0x2091 },
+{ 0x2092, 0x2092, 0x2092 },
+{ 0x2093, 0x2093, 0x2093 },
+{ 0x2094, 0x2094, 0x2094 },
+{ 0x20D0, 0x20D0, 0x20D0 },
+{ 0x20D1, 0x20D1, 0x20D1 },
+{ 0x20D2, 0x20D2, 0x20D2 },
+{ 0x20D3, 0x20D3, 0x20D3 },
+{ 0x20D4, 0x20D4, 0x20D4 },
+{ 0x20D5, 0x20D5, 0x20D5 },
+{ 0x20D6, 0x20D6, 0x20D6 },
+{ 0x20D7, 0x20D7, 0x20D7 },
+{ 0x20D8, 0x20D8, 0x20D8 },
+{ 0x20D9, 0x20D9, 0x20D9 },
+{ 0x20DA, 0x20DA, 0x20DA },
+{ 0x20DB, 0x20DB, 0x20DB },
+{ 0x20DC, 0x20DC, 0x20DC },
+{ 0x20E1, 0x20E1, 0x20E1 },
+{ 0x20E5, 0x20E5, 0x20E5 },
+{ 0x20E6, 0x20E6, 0x20E6 },
+{ 0x20E7, 0x20E7, 0x20E7 },
+{ 0x20E8, 0x20E8, 0x20E8 },
+{ 0x20E9, 0x20E9, 0x20E9 },
+{ 0x20EA, 0x20EA, 0x20EA },
+{ 0x20EB, 0x20EB, 0x20EB },
+{ 0x2102, 0x2102, 0x2102 },
+{ 0x2107, 0x2107, 0x2107 },
+{ 0x210A, 0x210A, 0x210A },
+{ 0x210B, 0x210B, 0x210B },
+{ 0x210C, 0x210C, 0x210C },
+{ 0x210D, 0x210D, 0x210D },
+{ 0x210E, 0x210E, 0x210E },
+{ 0x210F, 0x210F, 0x210F },
+{ 0x2110, 0x2110, 0x2110 },
+{ 0x2111, 0x2111, 0x2111 },
+{ 0x2112, 0x2112, 0x2112 },
+{ 0x2113, 0x2113, 0x2113 },
+{ 0x2115, 0x2115, 0x2115 },
+{ 0x2119, 0x2119, 0x2119 },
+{ 0x211A, 0x211A, 0x211A },
+{ 0x211B, 0x211B, 0x211B },
+{ 0x211C, 0x211C, 0x211C },
+{ 0x211D, 0x211D, 0x211D },
+{ 0x2124, 0x2124, 0x2124 },
+{ 0x2126, 0x2126, 0x03C9 },
+{ 0x2128, 0x2128, 0x2128 },
+{ 0x212A, 0x212A, 0x006B },
+{ 0x212B, 0x212B, 0x00E5 },
+{ 0x212C, 0x212C, 0x212C },
+{ 0x212D, 0x212D, 0x212D },
+{ 0x212F, 0x212F, 0x212F },
+{ 0x2130, 0x2130, 0x2130 },
+{ 0x2131, 0x2131, 0x2131 },
+{ 0x2133, 0x2133, 0x2133 },
+{ 0x2134, 0x2134, 0x2134 },
+{ 0x2135, 0x2135, 0x2135 },
+{ 0x2136, 0x2136, 0x2136 },
+{ 0x2137, 0x2137, 0x2137 },
+{ 0x2138, 0x2138, 0x2138 },
+{ 0x2139, 0x2139, 0x2139 },
+{ 0x213C, 0x213C, 0x213C },
+{ 0x213D, 0x213D, 0x213D },
+{ 0x213E, 0x213E, 0x213E },
+{ 0x213F, 0x213F, 0x213F },
+{ 0x2145, 0x2145, 0x2145 },
+{ 0x2146, 0x2146, 0x2146 },
+{ 0x2147, 0x2147, 0x2147 },
+{ 0x2148, 0x2148, 0x2148 },
+{ 0x2149, 0x2149, 0x2149 },
+{ 0x2C00, 0x2C00, 0x2C30 },
+{ 0x2C01, 0x2C01, 0x2C31 },
+{ 0x2C02, 0x2C02, 0x2C32 },
+{ 0x2C03, 0x2C03, 0x2C33 },
+{ 0x2C04, 0x2C04, 0x2C34 },
+{ 0x2C05, 0x2C05, 0x2C35 },
+{ 0x2C06, 0x2C06, 0x2C36 },
+{ 0x2C07, 0x2C07, 0x2C37 },
+{ 0x2C08, 0x2C08, 0x2C38 },
+{ 0x2C09, 0x2C09, 0x2C39 },
+{ 0x2C0A, 0x2C0A, 0x2C3A },
+{ 0x2C0B, 0x2C0B, 0x2C3B },
+{ 0x2C0C, 0x2C0C, 0x2C3C },
+{ 0x2C0D, 0x2C0D, 0x2C3D },
+{ 0x2C0E, 0x2C0E, 0x2C3E },
+{ 0x2C0F, 0x2C0F, 0x2C3F },
+{ 0x2C10, 0x2C10, 0x2C40 },
+{ 0x2C11, 0x2C11, 0x2C41 },
+{ 0x2C12, 0x2C12, 0x2C42 },
+{ 0x2C13, 0x2C13, 0x2C43 },
+{ 0x2C14, 0x2C14, 0x2C44 },
+{ 0x2C15, 0x2C15, 0x2C45 },
+{ 0x2C16, 0x2C16, 0x2C46 },
+{ 0x2C17, 0x2C17, 0x2C47 },
+{ 0x2C18, 0x2C18, 0x2C48 },
+{ 0x2C19, 0x2C19, 0x2C49 },
+{ 0x2C1A, 0x2C1A, 0x2C4A },
+{ 0x2C1B, 0x2C1B, 0x2C4B },
+{ 0x2C1C, 0x2C1C, 0x2C4C },
+{ 0x2C1D, 0x2C1D, 0x2C4D },
+{ 0x2C1E, 0x2C1E, 0x2C4E },
+{ 0x2C1F, 0x2C1F, 0x2C4F },
+{ 0x2C20, 0x2C20, 0x2C50 },
+{ 0x2C21, 0x2C21, 0x2C51 },
+{ 0x2C22, 0x2C22, 0x2C52 },
+{ 0x2C23, 0x2C23, 0x2C53 },
+{ 0x2C24, 0x2C24, 0x2C54 },
+{ 0x2C25, 0x2C25, 0x2C55 },
+{ 0x2C26, 0x2C26, 0x2C56 },
+{ 0x2C27, 0x2C27, 0x2C57 },
+{ 0x2C28, 0x2C28, 0x2C58 },
+{ 0x2C29, 0x2C29, 0x2C59 },
+{ 0x2C2A, 0x2C2A, 0x2C5A },
+{ 0x2C2B, 0x2C2B, 0x2C5B },
+{ 0x2C2C, 0x2C2C, 0x2C5C },
+{ 0x2C2D, 0x2C2D, 0x2C5D },
+{ 0x2C2E, 0x2C2E, 0x2C5E },
+{ 0x2C30, 0x2C00, 0x2C30 },
+{ 0x2C31, 0x2C01, 0x2C31 },
+{ 0x2C32, 0x2C02, 0x2C32 },
+{ 0x2C33, 0x2C03, 0x2C33 },
+{ 0x2C34, 0x2C04, 0x2C34 },
+{ 0x2C35, 0x2C05, 0x2C35 },
+{ 0x2C36, 0x2C06, 0x2C36 },
+{ 0x2C37, 0x2C07, 0x2C37 },
+{ 0x2C38, 0x2C08, 0x2C38 },
+{ 0x2C39, 0x2C09, 0x2C39 },
+{ 0x2C3A, 0x2C0A, 0x2C3A },
+{ 0x2C3B, 0x2C0B, 0x2C3B },
+{ 0x2C3C, 0x2C0C, 0x2C3C },
+{ 0x2C3D, 0x2C0D, 0x2C3D },
+{ 0x2C3E, 0x2C0E, 0x2C3E },
+{ 0x2C3F, 0x2C0F, 0x2C3F },
+{ 0x2C40, 0x2C10, 0x2C40 },
+{ 0x2C41, 0x2C11, 0x2C41 },
+{ 0x2C42, 0x2C12, 0x2C42 },
+{ 0x2C43, 0x2C13, 0x2C43 },
+{ 0x2C44, 0x2C14, 0x2C44 },
+{ 0x2C45, 0x2C15, 0x2C45 },
+{ 0x2C46, 0x2C16, 0x2C46 },
+{ 0x2C47, 0x2C17, 0x2C47 },
+{ 0x2C48, 0x2C18, 0x2C48 },
+{ 0x2C49, 0x2C19, 0x2C49 },
+{ 0x2C4A, 0x2C1A, 0x2C4A },
+{ 0x2C4B, 0x2C1B, 0x2C4B },
+{ 0x2C4C, 0x2C1C, 0x2C4C },
+{ 0x2C4D, 0x2C1D, 0x2C4D },
+{ 0x2C4E, 0x2C1E, 0x2C4E },
+{ 0x2C4F, 0x2C1F, 0x2C4F },
+{ 0x2C50, 0x2C20, 0x2C50 },
+{ 0x2C51, 0x2C21, 0x2C51 },
+{ 0x2C52, 0x2C22, 0x2C52 },
+{ 0x2C53, 0x2C23, 0x2C53 },
+{ 0x2C54, 0x2C24, 0x2C54 },
+{ 0x2C55, 0x2C25, 0x2C55 },
+{ 0x2C56, 0x2C26, 0x2C56 },
+{ 0x2C57, 0x2C27, 0x2C57 },
+{ 0x2C58, 0x2C28, 0x2C58 },
+{ 0x2C59, 0x2C29, 0x2C59 },
+{ 0x2C5A, 0x2C2A, 0x2C5A },
+{ 0x2C5B, 0x2C2B, 0x2C5B },
+{ 0x2C5C, 0x2C2C, 0x2C5C },
+{ 0x2C5D, 0x2C2D, 0x2C5D },
+{ 0x2C5E, 0x2C2E, 0x2C5E },
+{ 0x2C80, 0x2C80, 0x2C81 },
+{ 0x2C81, 0x2C80, 0x2C81 },
+{ 0x2C82, 0x2C82, 0x2C83 },
+{ 0x2C83, 0x2C82, 0x2C83 },
+{ 0x2C84, 0x2C84, 0x2C85 },
+{ 0x2C85, 0x2C84, 0x2C85 },
+{ 0x2C86, 0x2C86, 0x2C87 },
+{ 0x2C87, 0x2C86, 0x2C87 },
+{ 0x2C88, 0x2C88, 0x2C89 },
+{ 0x2C89, 0x2C88, 0x2C89 },
+{ 0x2C8A, 0x2C8A, 0x2C8B },
+{ 0x2C8B, 0x2C8A, 0x2C8B },
+{ 0x2C8C, 0x2C8C, 0x2C8D },
+{ 0x2C8D, 0x2C8C, 0x2C8D },
+{ 0x2C8E, 0x2C8E, 0x2C8F },
+{ 0x2C8F, 0x2C8E, 0x2C8F },
+{ 0x2C90, 0x2C90, 0x2C91 },
+{ 0x2C91, 0x2C90, 0x2C91 },
+{ 0x2C92, 0x2C92, 0x2C93 },
+{ 0x2C93, 0x2C92, 0x2C93 },
+{ 0x2C94, 0x2C94, 0x2C95 },
+{ 0x2C95, 0x2C94, 0x2C95 },
+{ 0x2C96, 0x2C96, 0x2C97 },
+{ 0x2C97, 0x2C96, 0x2C97 },
+{ 0x2C98, 0x2C98, 0x2C99 },
+{ 0x2C99, 0x2C98, 0x2C99 },
+{ 0x2C9A, 0x2C9A, 0x2C9B },
+{ 0x2C9B, 0x2C9A, 0x2C9B },
+{ 0x2C9C, 0x2C9C, 0x2C9D },
+{ 0x2C9D, 0x2C9C, 0x2C9D },
+{ 0x2C9E, 0x2C9E, 0x2C9F },
+{ 0x2C9F, 0x2C9E, 0x2C9F },
+{ 0x2CA0, 0x2CA0, 0x2CA1 },
+{ 0x2CA1, 0x2CA0, 0x2CA1 },
+{ 0x2CA2, 0x2CA2, 0x2CA3 },
+{ 0x2CA3, 0x2CA2, 0x2CA3 },
+{ 0x2CA4, 0x2CA4, 0x2CA5 },
+{ 0x2CA5, 0x2CA4, 0x2CA5 },
+{ 0x2CA6, 0x2CA6, 0x2CA7 },
+{ 0x2CA7, 0x2CA6, 0x2CA7 },
+{ 0x2CA8, 0x2CA8, 0x2CA9 },
+{ 0x2CA9, 0x2CA8, 0x2CA9 },
+{ 0x2CAA, 0x2CAA, 0x2CAB },
+{ 0x2CAB, 0x2CAA, 0x2CAB },
+{ 0x2CAC, 0x2CAC, 0x2CAD },
+{ 0x2CAD, 0x2CAC, 0x2CAD },
+{ 0x2CAE, 0x2CAE, 0x2CAF },
+{ 0x2CAF, 0x2CAE, 0x2CAF },
+{ 0x2CB0, 0x2CB0, 0x2CB1 },
+{ 0x2CB1, 0x2CB0, 0x2CB1 },
+{ 0x2CB2, 0x2CB2, 0x2CB3 },
+{ 0x2CB3, 0x2CB2, 0x2CB3 },
+{ 0x2CB4, 0x2CB4, 0x2CB5 },
+{ 0x2CB5, 0x2CB4, 0x2CB5 },
+{ 0x2CB6, 0x2CB6, 0x2CB7 },
+{ 0x2CB7, 0x2CB6, 0x2CB7 },
+{ 0x2CB8, 0x2CB8, 0x2CB9 },
+{ 0x2CB9, 0x2CB8, 0x2CB9 },
+{ 0x2CBA, 0x2CBA, 0x2CBB },
+{ 0x2CBB, 0x2CBA, 0x2CBB },
+{ 0x2CBC, 0x2CBC, 0x2CBD },
+{ 0x2CBD, 0x2CBC, 0x2CBD },
+{ 0x2CBE, 0x2CBE, 0x2CBF },
+{ 0x2CBF, 0x2CBE, 0x2CBF },
+{ 0x2CC0, 0x2CC0, 0x2CC1 },
+{ 0x2CC1, 0x2CC0, 0x2CC1 },
+{ 0x2CC2, 0x2CC2, 0x2CC3 },
+{ 0x2CC3, 0x2CC2, 0x2CC3 },
+{ 0x2CC4, 0x2CC4, 0x2CC5 },
+{ 0x2CC5, 0x2CC4, 0x2CC5 },
+{ 0x2CC6, 0x2CC6, 0x2CC7 },
+{ 0x2CC7, 0x2CC6, 0x2CC7 },
+{ 0x2CC8, 0x2CC8, 0x2CC9 },
+{ 0x2CC9, 0x2CC8, 0x2CC9 },
+{ 0x2CCA, 0x2CCA, 0x2CCB },
+{ 0x2CCB, 0x2CCA, 0x2CCB },
+{ 0x2CCC, 0x2CCC, 0x2CCD },
+{ 0x2CCD, 0x2CCC, 0x2CCD },
+{ 0x2CCE, 0x2CCE, 0x2CCF },
+{ 0x2CCF, 0x2CCE, 0x2CCF },
+{ 0x2CD0, 0x2CD0, 0x2CD1 },
+{ 0x2CD1, 0x2CD0, 0x2CD1 },
+{ 0x2CD2, 0x2CD2, 0x2CD3 },
+{ 0x2CD3, 0x2CD2, 0x2CD3 },
+{ 0x2CD4, 0x2CD4, 0x2CD5 },
+{ 0x2CD5, 0x2CD4, 0x2CD5 },
+{ 0x2CD6, 0x2CD6, 0x2CD7 },
+{ 0x2CD7, 0x2CD6, 0x2CD7 },
+{ 0x2CD8, 0x2CD8, 0x2CD9 },
+{ 0x2CD9, 0x2CD8, 0x2CD9 },
+{ 0x2CDA, 0x2CDA, 0x2CDB },
+{ 0x2CDB, 0x2CDA, 0x2CDB },
+{ 0x2CDC, 0x2CDC, 0x2CDD },
+{ 0x2CDD, 0x2CDC, 0x2CDD },
+{ 0x2CDE, 0x2CDE, 0x2CDF },
+{ 0x2CDF, 0x2CDE, 0x2CDF },
+{ 0x2CE0, 0x2CE0, 0x2CE1 },
+{ 0x2CE1, 0x2CE0, 0x2CE1 },
+{ 0x2CE2, 0x2CE2, 0x2CE3 },
+{ 0x2CE3, 0x2CE2, 0x2CE3 },
+{ 0x2CE4, 0x2CE4, 0x2CE4 },
+{ 0x2D00, 0x10A0, 0x2D00 },
+{ 0x2D01, 0x10A1, 0x2D01 },
+{ 0x2D02, 0x10A2, 0x2D02 },
+{ 0x2D03, 0x10A3, 0x2D03 },
+{ 0x2D04, 0x10A4, 0x2D04 },
+{ 0x2D05, 0x10A5, 0x2D05 },
+{ 0x2D06, 0x10A6, 0x2D06 },
+{ 0x2D07, 0x10A7, 0x2D07 },
+{ 0x2D08, 0x10A8, 0x2D08 },
+{ 0x2D09, 0x10A9, 0x2D09 },
+{ 0x2D0A, 0x10AA, 0x2D0A },
+{ 0x2D0B, 0x10AB, 0x2D0B },
+{ 0x2D0C, 0x10AC, 0x2D0C },
+{ 0x2D0D, 0x10AD, 0x2D0D },
+{ 0x2D0E, 0x10AE, 0x2D0E },
+{ 0x2D0F, 0x10AF, 0x2D0F },
+{ 0x2D10, 0x10B0, 0x2D10 },
+{ 0x2D11, 0x10B1, 0x2D11 },
+{ 0x2D12, 0x10B2, 0x2D12 },
+{ 0x2D13, 0x10B3, 0x2D13 },
+{ 0x2D14, 0x10B4, 0x2D14 },
+{ 0x2D15, 0x10B5, 0x2D15 },
+{ 0x2D16, 0x10B6, 0x2D16 },
+{ 0x2D17, 0x10B7, 0x2D17 },
+{ 0x2D18, 0x10B8, 0x2D18 },
+{ 0x2D19, 0x10B9, 0x2D19 },
+{ 0x2D1A, 0x10BA, 0x2D1A },
+{ 0x2D1B, 0x10BB, 0x2D1B },
+{ 0x2D1C, 0x10BC, 0x2D1C },
+{ 0x2D1D, 0x10BD, 0x2D1D },
+{ 0x2D1E, 0x10BE, 0x2D1E },
+{ 0x2D1F, 0x10BF, 0x2D1F },
+{ 0x2D20, 0x10C0, 0x2D20 },
+{ 0x2D21, 0x10C1, 0x2D21 },
+{ 0x2D22, 0x10C2, 0x2D22 },
+{ 0x2D23, 0x10C3, 0x2D23 },
+{ 0x2D24, 0x10C4, 0x2D24 },
+{ 0x2D25, 0x10C5, 0x2D25 },
+{ 0x2D30, 0x2D30, 0x2D30 },
+{ 0x2D31, 0x2D31, 0x2D31 },
+{ 0x2D32, 0x2D32, 0x2D32 },
+{ 0x2D33, 0x2D33, 0x2D33 },
+{ 0x2D34, 0x2D34, 0x2D34 },
+{ 0x2D35, 0x2D35, 0x2D35 },
+{ 0x2D36, 0x2D36, 0x2D36 },
+{ 0x2D37, 0x2D37, 0x2D37 },
+{ 0x2D38, 0x2D38, 0x2D38 },
+{ 0x2D39, 0x2D39, 0x2D39 },
+{ 0x2D3A, 0x2D3A, 0x2D3A },
+{ 0x2D3B, 0x2D3B, 0x2D3B },
+{ 0x2D3C, 0x2D3C, 0x2D3C },
+{ 0x2D3D, 0x2D3D, 0x2D3D },
+{ 0x2D3E, 0x2D3E, 0x2D3E },
+{ 0x2D3F, 0x2D3F, 0x2D3F },
+{ 0x2D40, 0x2D40, 0x2D40 },
+{ 0x2D41, 0x2D41, 0x2D41 },
+{ 0x2D42, 0x2D42, 0x2D42 },
+{ 0x2D43, 0x2D43, 0x2D43 },
+{ 0x2D44, 0x2D44, 0x2D44 },
+{ 0x2D45, 0x2D45, 0x2D45 },
+{ 0x2D46, 0x2D46, 0x2D46 },
+{ 0x2D47, 0x2D47, 0x2D47 },
+{ 0x2D48, 0x2D48, 0x2D48 },
+{ 0x2D49, 0x2D49, 0x2D49 },
+{ 0x2D4A, 0x2D4A, 0x2D4A },
+{ 0x2D4B, 0x2D4B, 0x2D4B },
+{ 0x2D4C, 0x2D4C, 0x2D4C },
+{ 0x2D4D, 0x2D4D, 0x2D4D },
+{ 0x2D4E, 0x2D4E, 0x2D4E },
+{ 0x2D4F, 0x2D4F, 0x2D4F },
+{ 0x2D50, 0x2D50, 0x2D50 },
+{ 0x2D51, 0x2D51, 0x2D51 },
+{ 0x2D52, 0x2D52, 0x2D52 },
+{ 0x2D53, 0x2D53, 0x2D53 },
+{ 0x2D54, 0x2D54, 0x2D54 },
+{ 0x2D55, 0x2D55, 0x2D55 },
+{ 0x2D56, 0x2D56, 0x2D56 },
+{ 0x2D57, 0x2D57, 0x2D57 },
+{ 0x2D58, 0x2D58, 0x2D58 },
+{ 0x2D59, 0x2D59, 0x2D59 },
+{ 0x2D5A, 0x2D5A, 0x2D5A },
+{ 0x2D5B, 0x2D5B, 0x2D5B },
+{ 0x2D5C, 0x2D5C, 0x2D5C },
+{ 0x2D5D, 0x2D5D, 0x2D5D },
+{ 0x2D5E, 0x2D5E, 0x2D5E },
+{ 0x2D5F, 0x2D5F, 0x2D5F },
+{ 0x2D60, 0x2D60, 0x2D60 },
+{ 0x2D61, 0x2D61, 0x2D61 },
+{ 0x2D62, 0x2D62, 0x2D62 },
+{ 0x2D63, 0x2D63, 0x2D63 },
+{ 0x2D64, 0x2D64, 0x2D64 },
+{ 0x2D65, 0x2D65, 0x2D65 },
+{ 0x2D6F, 0x2D6F, 0x2D6F },
+{ 0x2D80, 0x2D80, 0x2D80 },
+{ 0x2D81, 0x2D81, 0x2D81 },
+{ 0x2D82, 0x2D82, 0x2D82 },
+{ 0x2D83, 0x2D83, 0x2D83 },
+{ 0x2D84, 0x2D84, 0x2D84 },
+{ 0x2D85, 0x2D85, 0x2D85 },
+{ 0x2D86, 0x2D86, 0x2D86 },
+{ 0x2D87, 0x2D87, 0x2D87 },
+{ 0x2D88, 0x2D88, 0x2D88 },
+{ 0x2D89, 0x2D89, 0x2D89 },
+{ 0x2D8A, 0x2D8A, 0x2D8A },
+{ 0x2D8B, 0x2D8B, 0x2D8B },
+{ 0x2D8C, 0x2D8C, 0x2D8C },
+{ 0x2D8D, 0x2D8D, 0x2D8D },
+{ 0x2D8E, 0x2D8E, 0x2D8E },
+{ 0x2D8F, 0x2D8F, 0x2D8F },
+{ 0x2D90, 0x2D90, 0x2D90 },
+{ 0x2D91, 0x2D91, 0x2D91 },
+{ 0x2D92, 0x2D92, 0x2D92 },
+{ 0x2D93, 0x2D93, 0x2D93 },
+{ 0x2D94, 0x2D94, 0x2D94 },
+{ 0x2D95, 0x2D95, 0x2D95 },
+{ 0x2D96, 0x2D96, 0x2D96 },
+{ 0x2DA0, 0x2DA0, 0x2DA0 },
+{ 0x2DA1, 0x2DA1, 0x2DA1 },
+{ 0x2DA2, 0x2DA2, 0x2DA2 },
+{ 0x2DA3, 0x2DA3, 0x2DA3 },
+{ 0x2DA4, 0x2DA4, 0x2DA4 },
+{ 0x2DA5, 0x2DA5, 0x2DA5 },
+{ 0x2DA6, 0x2DA6, 0x2DA6 },
+{ 0x2DA8, 0x2DA8, 0x2DA8 },
+{ 0x2DA9, 0x2DA9, 0x2DA9 },
+{ 0x2DAA, 0x2DAA, 0x2DAA },
+{ 0x2DAB, 0x2DAB, 0x2DAB },
+{ 0x2DAC, 0x2DAC, 0x2DAC },
+{ 0x2DAD, 0x2DAD, 0x2DAD },
+{ 0x2DAE, 0x2DAE, 0x2DAE },
+{ 0x2DB0, 0x2DB0, 0x2DB0 },
+{ 0x2DB1, 0x2DB1, 0x2DB1 },
+{ 0x2DB2, 0x2DB2, 0x2DB2 },
+{ 0x2DB3, 0x2DB3, 0x2DB3 },
+{ 0x2DB4, 0x2DB4, 0x2DB4 },
+{ 0x2DB5, 0x2DB5, 0x2DB5 },
+{ 0x2DB6, 0x2DB6, 0x2DB6 },
+{ 0x2DB8, 0x2DB8, 0x2DB8 },
+{ 0x2DB9, 0x2DB9, 0x2DB9 },
+{ 0x2DBA, 0x2DBA, 0x2DBA },
+{ 0x2DBB, 0x2DBB, 0x2DBB },
+{ 0x2DBC, 0x2DBC, 0x2DBC },
+{ 0x2DBD, 0x2DBD, 0x2DBD },
+{ 0x2DBE, 0x2DBE, 0x2DBE },
+{ 0x2DC0, 0x2DC0, 0x2DC0 },
+{ 0x2DC1, 0x2DC1, 0x2DC1 },
+{ 0x2DC2, 0x2DC2, 0x2DC2 },
+{ 0x2DC3, 0x2DC3, 0x2DC3 },
+{ 0x2DC4, 0x2DC4, 0x2DC4 },
+{ 0x2DC5, 0x2DC5, 0x2DC5 },
+{ 0x2DC6, 0x2DC6, 0x2DC6 },
+{ 0x2DC8, 0x2DC8, 0x2DC8 },
+{ 0x2DC9, 0x2DC9, 0x2DC9 },
+{ 0x2DCA, 0x2DCA, 0x2DCA },
+{ 0x2DCB, 0x2DCB, 0x2DCB },
+{ 0x2DCC, 0x2DCC, 0x2DCC },
+{ 0x2DCD, 0x2DCD, 0x2DCD },
+{ 0x2DCE, 0x2DCE, 0x2DCE },
+{ 0x2DD0, 0x2DD0, 0x2DD0 },
+{ 0x2DD1, 0x2DD1, 0x2DD1 },
+{ 0x2DD2, 0x2DD2, 0x2DD2 },
+{ 0x2DD3, 0x2DD3, 0x2DD3 },
+{ 0x2DD4, 0x2DD4, 0x2DD4 },
+{ 0x2DD5, 0x2DD5, 0x2DD5 },
+{ 0x2DD6, 0x2DD6, 0x2DD6 },
+{ 0x2DD8, 0x2DD8, 0x2DD8 },
+{ 0x2DD9, 0x2DD9, 0x2DD9 },
+{ 0x2DDA, 0x2DDA, 0x2DDA },
+{ 0x2DDB, 0x2DDB, 0x2DDB },
+{ 0x2DDC, 0x2DDC, 0x2DDC },
+{ 0x2DDD, 0x2DDD, 0x2DDD },
+{ 0x2DDE, 0x2DDE, 0x2DDE },
+{ 0x3005, 0x3005, 0x3005 },
+{ 0x3006, 0x3006, 0x3006 },
+{ 0x302A, 0x302A, 0x302A },
+{ 0x302B, 0x302B, 0x302B },
+{ 0x302C, 0x302C, 0x302C },
+{ 0x302D, 0x302D, 0x302D },
+{ 0x302E, 0x302E, 0x302E },
+{ 0x302F, 0x302F, 0x302F },
+{ 0x3031, 0x3031, 0x3031 },
+{ 0x3032, 0x3032, 0x3032 },
+{ 0x3033, 0x3033, 0x3033 },
+{ 0x3034, 0x3034, 0x3034 },
+{ 0x3035, 0x3035, 0x3035 },
+{ 0x303B, 0x303B, 0x303B },
+{ 0x303C, 0x303C, 0x303C },
+{ 0x3041, 0x3041, 0x3041 },
+{ 0x3042, 0x3042, 0x3042 },
+{ 0x3043, 0x3043, 0x3043 },
+{ 0x3044, 0x3044, 0x3044 },
+{ 0x3045, 0x3045, 0x3045 },
+{ 0x3046, 0x3046, 0x3046 },
+{ 0x3047, 0x3047, 0x3047 },
+{ 0x3048, 0x3048, 0x3048 },
+{ 0x3049, 0x3049, 0x3049 },
+{ 0x304A, 0x304A, 0x304A },
+{ 0x304B, 0x304B, 0x304B },
+{ 0x304C, 0x304C, 0x304C },
+{ 0x304D, 0x304D, 0x304D },
+{ 0x304E, 0x304E, 0x304E },
+{ 0x304F, 0x304F, 0x304F },
+{ 0x3050, 0x3050, 0x3050 },
+{ 0x3051, 0x3051, 0x3051 },
+{ 0x3052, 0x3052, 0x3052 },
+{ 0x3053, 0x3053, 0x3053 },
+{ 0x3054, 0x3054, 0x3054 },
+{ 0x3055, 0x3055, 0x3055 },
+{ 0x3056, 0x3056, 0x3056 },
+{ 0x3057, 0x3057, 0x3057 },
+{ 0x3058, 0x3058, 0x3058 },
+{ 0x3059, 0x3059, 0x3059 },
+{ 0x305A, 0x305A, 0x305A },
+{ 0x305B, 0x305B, 0x305B },
+{ 0x305C, 0x305C, 0x305C },
+{ 0x305D, 0x305D, 0x305D },
+{ 0x305E, 0x305E, 0x305E },
+{ 0x305F, 0x305F, 0x305F },
+{ 0x3060, 0x3060, 0x3060 },
+{ 0x3061, 0x3061, 0x3061 },
+{ 0x3062, 0x3062, 0x3062 },
+{ 0x3063, 0x3063, 0x3063 },
+{ 0x3064, 0x3064, 0x3064 },
+{ 0x3065, 0x3065, 0x3065 },
+{ 0x3066, 0x3066, 0x3066 },
+{ 0x3067, 0x3067, 0x3067 },
+{ 0x3068, 0x3068, 0x3068 },
+{ 0x3069, 0x3069, 0x3069 },
+{ 0x306A, 0x306A, 0x306A },
+{ 0x306B, 0x306B, 0x306B },
+{ 0x306C, 0x306C, 0x306C },
+{ 0x306D, 0x306D, 0x306D },
+{ 0x306E, 0x306E, 0x306E },
+{ 0x306F, 0x306F, 0x306F },
+{ 0x3070, 0x3070, 0x3070 },
+{ 0x3071, 0x3071, 0x3071 },
+{ 0x3072, 0x3072, 0x3072 },
+{ 0x3073, 0x3073, 0x3073 },
+{ 0x3074, 0x3074, 0x3074 },
+{ 0x3075, 0x3075, 0x3075 },
+{ 0x3076, 0x3076, 0x3076 },
+{ 0x3077, 0x3077, 0x3077 },
+{ 0x3078, 0x3078, 0x3078 },
+{ 0x3079, 0x3079, 0x3079 },
+{ 0x307A, 0x307A, 0x307A },
+{ 0x307B, 0x307B, 0x307B },
+{ 0x307C, 0x307C, 0x307C },
+{ 0x307D, 0x307D, 0x307D },
+{ 0x307E, 0x307E, 0x307E },
+{ 0x307F, 0x307F, 0x307F },
+{ 0x3080, 0x3080, 0x3080 },
+{ 0x3081, 0x3081, 0x3081 },
+{ 0x3082, 0x3082, 0x3082 },
+{ 0x3083, 0x3083, 0x3083 },
+{ 0x3084, 0x3084, 0x3084 },
+{ 0x3085, 0x3085, 0x3085 },
+{ 0x3086, 0x3086, 0x3086 },
+{ 0x3087, 0x3087, 0x3087 },
+{ 0x3088, 0x3088, 0x3088 },
+{ 0x3089, 0x3089, 0x3089 },
+{ 0x308A, 0x308A, 0x308A },
+{ 0x308B, 0x308B, 0x308B },
+{ 0x308C, 0x308C, 0x308C },
+{ 0x308D, 0x308D, 0x308D },
+{ 0x308E, 0x308E, 0x308E },
+{ 0x308F, 0x308F, 0x308F },
+{ 0x3090, 0x3090, 0x3090 },
+{ 0x3091, 0x3091, 0x3091 },
+{ 0x3092, 0x3092, 0x3092 },
+{ 0x3093, 0x3093, 0x3093 },
+{ 0x3094, 0x3094, 0x3094 },
+{ 0x3095, 0x3095, 0x3095 },
+{ 0x3096, 0x3096, 0x3096 },
+{ 0x3099, 0x3099, 0x3099 },
+{ 0x309A, 0x309A, 0x309A },
+{ 0x309D, 0x309D, 0x309D },
+{ 0x309E, 0x309E, 0x309E },
+{ 0x309F, 0x309F, 0x309F },
+{ 0x30A1, 0x30A1, 0x30A1 },
+{ 0x30A2, 0x30A2, 0x30A2 },
+{ 0x30A3, 0x30A3, 0x30A3 },
+{ 0x30A4, 0x30A4, 0x30A4 },
+{ 0x30A5, 0x30A5, 0x30A5 },
+{ 0x30A6, 0x30A6, 0x30A6 },
+{ 0x30A7, 0x30A7, 0x30A7 },
+{ 0x30A8, 0x30A8, 0x30A8 },
+{ 0x30A9, 0x30A9, 0x30A9 },
+{ 0x30AA, 0x30AA, 0x30AA },
+{ 0x30AB, 0x30AB, 0x30AB },
+{ 0x30AC, 0x30AC, 0x30AC },
+{ 0x30AD, 0x30AD, 0x30AD },
+{ 0x30AE, 0x30AE, 0x30AE },
+{ 0x30AF, 0x30AF, 0x30AF },
+{ 0x30B0, 0x30B0, 0x30B0 },
+{ 0x30B1, 0x30B1, 0x30B1 },
+{ 0x30B2, 0x30B2, 0x30B2 },
+{ 0x30B3, 0x30B3, 0x30B3 },
+{ 0x30B4, 0x30B4, 0x30B4 },
+{ 0x30B5, 0x30B5, 0x30B5 },
+{ 0x30B6, 0x30B6, 0x30B6 },
+{ 0x30B7, 0x30B7, 0x30B7 },
+{ 0x30B8, 0x30B8, 0x30B8 },
+{ 0x30B9, 0x30B9, 0x30B9 },
+{ 0x30BA, 0x30BA, 0x30BA },
+{ 0x30BB, 0x30BB, 0x30BB },
+{ 0x30BC, 0x30BC, 0x30BC },
+{ 0x30BD, 0x30BD, 0x30BD },
+{ 0x30BE, 0x30BE, 0x30BE },
+{ 0x30BF, 0x30BF, 0x30BF },
+{ 0x30C0, 0x30C0, 0x30C0 },
+{ 0x30C1, 0x30C1, 0x30C1 },
+{ 0x30C2, 0x30C2, 0x30C2 },
+{ 0x30C3, 0x30C3, 0x30C3 },
+{ 0x30C4, 0x30C4, 0x30C4 },
+{ 0x30C5, 0x30C5, 0x30C5 },
+{ 0x30C6, 0x30C6, 0x30C6 },
+{ 0x30C7, 0x30C7, 0x30C7 },
+{ 0x30C8, 0x30C8, 0x30C8 },
+{ 0x30C9, 0x30C9, 0x30C9 },
+{ 0x30CA, 0x30CA, 0x30CA },
+{ 0x30CB, 0x30CB, 0x30CB },
+{ 0x30CC, 0x30CC, 0x30CC },
+{ 0x30CD, 0x30CD, 0x30CD },
+{ 0x30CE, 0x30CE, 0x30CE },
+{ 0x30CF, 0x30CF, 0x30CF },
+{ 0x30D0, 0x30D0, 0x30D0 },
+{ 0x30D1, 0x30D1, 0x30D1 },
+{ 0x30D2, 0x30D2, 0x30D2 },
+{ 0x30D3, 0x30D3, 0x30D3 },
+{ 0x30D4, 0x30D4, 0x30D4 },
+{ 0x30D5, 0x30D5, 0x30D5 },
+{ 0x30D6, 0x30D6, 0x30D6 },
+{ 0x30D7, 0x30D7, 0x30D7 },
+{ 0x30D8, 0x30D8, 0x30D8 },
+{ 0x30D9, 0x30D9, 0x30D9 },
+{ 0x30DA, 0x30DA, 0x30DA },
+{ 0x30DB, 0x30DB, 0x30DB },
+{ 0x30DC, 0x30DC, 0x30DC },
+{ 0x30DD, 0x30DD, 0x30DD },
+{ 0x30DE, 0x30DE, 0x30DE },
+{ 0x30DF, 0x30DF, 0x30DF },
+{ 0x30E0, 0x30E0, 0x30E0 },
+{ 0x30E1, 0x30E1, 0x30E1 },
+{ 0x30E2, 0x30E2, 0x30E2 },
+{ 0x30E3, 0x30E3, 0x30E3 },
+{ 0x30E4, 0x30E4, 0x30E4 },
+{ 0x30E5, 0x30E5, 0x30E5 },
+{ 0x30E6, 0x30E6, 0x30E6 },
+{ 0x30E7, 0x30E7, 0x30E7 },
+{ 0x30E8, 0x30E8, 0x30E8 },
+{ 0x30E9, 0x30E9, 0x30E9 },
+{ 0x30EA, 0x30EA, 0x30EA },
+{ 0x30EB, 0x30EB, 0x30EB },
+{ 0x30EC, 0x30EC, 0x30EC },
+{ 0x30ED, 0x30ED, 0x30ED },
+{ 0x30EE, 0x30EE, 0x30EE },
+{ 0x30EF, 0x30EF, 0x30EF },
+{ 0x30F0, 0x30F0, 0x30F0 },
+{ 0x30F1, 0x30F1, 0x30F1 },
+{ 0x30F2, 0x30F2, 0x30F2 },
+{ 0x30F3, 0x30F3, 0x30F3 },
+{ 0x30F4, 0x30F4, 0x30F4 },
+{ 0x30F5, 0x30F5, 0x30F5 },
+{ 0x30F6, 0x30F6, 0x30F6 },
+{ 0x30F7, 0x30F7, 0x30F7 },
+{ 0x30F8, 0x30F8, 0x30F8 },
+{ 0x30F9, 0x30F9, 0x30F9 },
+{ 0x30FA, 0x30FA, 0x30FA },
+{ 0x30FC, 0x30FC, 0x30FC },
+{ 0x30FD, 0x30FD, 0x30FD },
+{ 0x30FE, 0x30FE, 0x30FE },
+{ 0x30FF, 0x30FF, 0x30FF },
+{ 0x3105, 0x3105, 0x3105 },
+{ 0x3106, 0x3106, 0x3106 },
+{ 0x3107, 0x3107, 0x3107 },
+{ 0x3108, 0x3108, 0x3108 },
+{ 0x3109, 0x3109, 0x3109 },
+{ 0x310A, 0x310A, 0x310A },
+{ 0x310B, 0x310B, 0x310B },
+{ 0x310C, 0x310C, 0x310C },
+{ 0x310D, 0x310D, 0x310D },
+{ 0x310E, 0x310E, 0x310E },
+{ 0x310F, 0x310F, 0x310F },
+{ 0x3110, 0x3110, 0x3110 },
+{ 0x3111, 0x3111, 0x3111 },
+{ 0x3112, 0x3112, 0x3112 },
+{ 0x3113, 0x3113, 0x3113 },
+{ 0x3114, 0x3114, 0x3114 },
+{ 0x3115, 0x3115, 0x3115 },
+{ 0x3116, 0x3116, 0x3116 },
+{ 0x3117, 0x3117, 0x3117 },
+{ 0x3118, 0x3118, 0x3118 },
+{ 0x3119, 0x3119, 0x3119 },
+{ 0x311A, 0x311A, 0x311A },
+{ 0x311B, 0x311B, 0x311B },
+{ 0x311C, 0x311C, 0x311C },
+{ 0x311D, 0x311D, 0x311D },
+{ 0x311E, 0x311E, 0x311E },
+{ 0x311F, 0x311F, 0x311F },
+{ 0x3120, 0x3120, 0x3120 },
+{ 0x3121, 0x3121, 0x3121 },
+{ 0x3122, 0x3122, 0x3122 },
+{ 0x3123, 0x3123, 0x3123 },
+{ 0x3124, 0x3124, 0x3124 },
+{ 0x3125, 0x3125, 0x3125 },
+{ 0x3126, 0x3126, 0x3126 },
+{ 0x3127, 0x3127, 0x3127 },
+{ 0x3128, 0x3128, 0x3128 },
+{ 0x3129, 0x3129, 0x3129 },
+{ 0x312A, 0x312A, 0x312A },
+{ 0x312B, 0x312B, 0x312B },
+{ 0x312C, 0x312C, 0x312C },
+{ 0x3131, 0x3131, 0x3131 },
+{ 0x3132, 0x3132, 0x3132 },
+{ 0x3133, 0x3133, 0x3133 },
+{ 0x3134, 0x3134, 0x3134 },
+{ 0x3135, 0x3135, 0x3135 },
+{ 0x3136, 0x3136, 0x3136 },
+{ 0x3137, 0x3137, 0x3137 },
+{ 0x3138, 0x3138, 0x3138 },
+{ 0x3139, 0x3139, 0x3139 },
+{ 0x313A, 0x313A, 0x313A },
+{ 0x313B, 0x313B, 0x313B },
+{ 0x313C, 0x313C, 0x313C },
+{ 0x313D, 0x313D, 0x313D },
+{ 0x313E, 0x313E, 0x313E },
+{ 0x313F, 0x313F, 0x313F },
+{ 0x3140, 0x3140, 0x3140 },
+{ 0x3141, 0x3141, 0x3141 },
+{ 0x3142, 0x3142, 0x3142 },
+{ 0x3143, 0x3143, 0x3143 },
+{ 0x3144, 0x3144, 0x3144 },
+{ 0x3145, 0x3145, 0x3145 },
+{ 0x3146, 0x3146, 0x3146 },
+{ 0x3147, 0x3147, 0x3147 },
+{ 0x3148, 0x3148, 0x3148 },
+{ 0x3149, 0x3149, 0x3149 },
+{ 0x314A, 0x314A, 0x314A },
+{ 0x314B, 0x314B, 0x314B },
+{ 0x314C, 0x314C, 0x314C },
+{ 0x314D, 0x314D, 0x314D },
+{ 0x314E, 0x314E, 0x314E },
+{ 0x314F, 0x314F, 0x314F },
+{ 0x3150, 0x3150, 0x3150 },
+{ 0x3151, 0x3151, 0x3151 },
+{ 0x3152, 0x3152, 0x3152 },
+{ 0x3153, 0x3153, 0x3153 },
+{ 0x3154, 0x3154, 0x3154 },
+{ 0x3155, 0x3155, 0x3155 },
+{ 0x3156, 0x3156, 0x3156 },
+{ 0x3157, 0x3157, 0x3157 },
+{ 0x3158, 0x3158, 0x3158 },
+{ 0x3159, 0x3159, 0x3159 },
+{ 0x315A, 0x315A, 0x315A },
+{ 0x315B, 0x315B, 0x315B },
+{ 0x315C, 0x315C, 0x315C },
+{ 0x315D, 0x315D, 0x315D },
+{ 0x315E, 0x315E, 0x315E },
+{ 0x315F, 0x315F, 0x315F },
+{ 0x3160, 0x3160, 0x3160 },
+{ 0x3161, 0x3161, 0x3161 },
+{ 0x3162, 0x3162, 0x3162 },
+{ 0x3163, 0x3163, 0x3163 },
+{ 0x3164, 0x3164, 0x3164 },
+{ 0x3165, 0x3165, 0x3165 },
+{ 0x3166, 0x3166, 0x3166 },
+{ 0x3167, 0x3167, 0x3167 },
+{ 0x3168, 0x3168, 0x3168 },
+{ 0x3169, 0x3169, 0x3169 },
+{ 0x316A, 0x316A, 0x316A },
+{ 0x316B, 0x316B, 0x316B },
+{ 0x316C, 0x316C, 0x316C },
+{ 0x316D, 0x316D, 0x316D },
+{ 0x316E, 0x316E, 0x316E },
+{ 0x316F, 0x316F, 0x316F },
+{ 0x3170, 0x3170, 0x3170 },
+{ 0x3171, 0x3171, 0x3171 },
+{ 0x3172, 0x3172, 0x3172 },
+{ 0x3173, 0x3173, 0x3173 },
+{ 0x3174, 0x3174, 0x3174 },
+{ 0x3175, 0x3175, 0x3175 },
+{ 0x3176, 0x3176, 0x3176 },
+{ 0x3177, 0x3177, 0x3177 },
+{ 0x3178, 0x3178, 0x3178 },
+{ 0x3179, 0x3179, 0x3179 },
+{ 0x317A, 0x317A, 0x317A },
+{ 0x317B, 0x317B, 0x317B },
+{ 0x317C, 0x317C, 0x317C },
+{ 0x317D, 0x317D, 0x317D },
+{ 0x317E, 0x317E, 0x317E },
+{ 0x317F, 0x317F, 0x317F },
+{ 0x3180, 0x3180, 0x3180 },
+{ 0x3181, 0x3181, 0x3181 },
+{ 0x3182, 0x3182, 0x3182 },
+{ 0x3183, 0x3183, 0x3183 },
+{ 0x3184, 0x3184, 0x3184 },
+{ 0x3185, 0x3185, 0x3185 },
+{ 0x3186, 0x3186, 0x3186 },
+{ 0x3187, 0x3187, 0x3187 },
+{ 0x3188, 0x3188, 0x3188 },
+{ 0x3189, 0x3189, 0x3189 },
+{ 0x318A, 0x318A, 0x318A },
+{ 0x318B, 0x318B, 0x318B },
+{ 0x318C, 0x318C, 0x318C },
+{ 0x318D, 0x318D, 0x318D },
+{ 0x318E, 0x318E, 0x318E },
+{ 0x31A0, 0x31A0, 0x31A0 },
+{ 0x31A1, 0x31A1, 0x31A1 },
+{ 0x31A2, 0x31A2, 0x31A2 },
+{ 0x31A3, 0x31A3, 0x31A3 },
+{ 0x31A4, 0x31A4, 0x31A4 },
+{ 0x31A5, 0x31A5, 0x31A5 },
+{ 0x31A6, 0x31A6, 0x31A6 },
+{ 0x31A7, 0x31A7, 0x31A7 },
+{ 0x31A8, 0x31A8, 0x31A8 },
+{ 0x31A9, 0x31A9, 0x31A9 },
+{ 0x31AA, 0x31AA, 0x31AA },
+{ 0x31AB, 0x31AB, 0x31AB },
+{ 0x31AC, 0x31AC, 0x31AC },
+{ 0x31AD, 0x31AD, 0x31AD },
+{ 0x31AE, 0x31AE, 0x31AE },
+{ 0x31AF, 0x31AF, 0x31AF },
+{ 0x31B0, 0x31B0, 0x31B0 },
+{ 0x31B1, 0x31B1, 0x31B1 },
+{ 0x31B2, 0x31B2, 0x31B2 },
+{ 0x31B3, 0x31B3, 0x31B3 },
+{ 0x31B4, 0x31B4, 0x31B4 },
+{ 0x31B5, 0x31B5, 0x31B5 },
+{ 0x31B6, 0x31B6, 0x31B6 },
+{ 0x31B7, 0x31B7, 0x31B7 },
+{ 0x31F0, 0x31F0, 0x31F0 },
+{ 0x31F1, 0x31F1, 0x31F1 },
+{ 0x31F2, 0x31F2, 0x31F2 },
+{ 0x31F3, 0x31F3, 0x31F3 },
+{ 0x31F4, 0x31F4, 0x31F4 },
+{ 0x31F5, 0x31F5, 0x31F5 },
+{ 0x31F6, 0x31F6, 0x31F6 },
+{ 0x31F7, 0x31F7, 0x31F7 },
+{ 0x31F8, 0x31F8, 0x31F8 },
+{ 0x31F9, 0x31F9, 0x31F9 },
+{ 0x31FA, 0x31FA, 0x31FA },
+{ 0x31FB, 0x31FB, 0x31FB },
+{ 0x31FC, 0x31FC, 0x31FC },
+{ 0x31FD, 0x31FD, 0x31FD },
+{ 0x31FE, 0x31FE, 0x31FE },
+{ 0x31FF, 0x31FF, 0x31FF },
+{ 0x3400, 0x3400, 0x3400 },
+{ 0x4DB5, 0x4DB5, 0x4DB5 },
+{ 0x4E00, 0x4E00, 0x4E00 },
+{ 0x9FBB, 0x9FBB, 0x9FBB },
+{ 0xA000, 0xA000, 0xA000 },
+{ 0xA001, 0xA001, 0xA001 },
+{ 0xA002, 0xA002, 0xA002 },
+{ 0xA003, 0xA003, 0xA003 },
+{ 0xA004, 0xA004, 0xA004 },
+{ 0xA005, 0xA005, 0xA005 },
+{ 0xA006, 0xA006, 0xA006 },
+{ 0xA007, 0xA007, 0xA007 },
+{ 0xA008, 0xA008, 0xA008 },
+{ 0xA009, 0xA009, 0xA009 },
+{ 0xA00A, 0xA00A, 0xA00A },
+{ 0xA00B, 0xA00B, 0xA00B },
+{ 0xA00C, 0xA00C, 0xA00C },
+{ 0xA00D, 0xA00D, 0xA00D },
+{ 0xA00E, 0xA00E, 0xA00E },
+{ 0xA00F, 0xA00F, 0xA00F },
+{ 0xA010, 0xA010, 0xA010 },
+{ 0xA011, 0xA011, 0xA011 },
+{ 0xA012, 0xA012, 0xA012 },
+{ 0xA013, 0xA013, 0xA013 },
+{ 0xA014, 0xA014, 0xA014 },
+{ 0xA015, 0xA015, 0xA015 },
+{ 0xA016, 0xA016, 0xA016 },
+{ 0xA017, 0xA017, 0xA017 },
+{ 0xA018, 0xA018, 0xA018 },
+{ 0xA019, 0xA019, 0xA019 },
+{ 0xA01A, 0xA01A, 0xA01A },
+{ 0xA01B, 0xA01B, 0xA01B },
+{ 0xA01C, 0xA01C, 0xA01C },
+{ 0xA01D, 0xA01D, 0xA01D },
+{ 0xA01E, 0xA01E, 0xA01E },
+{ 0xA01F, 0xA01F, 0xA01F },
+{ 0xA020, 0xA020, 0xA020 },
+{ 0xA021, 0xA021, 0xA021 },
+{ 0xA022, 0xA022, 0xA022 },
+{ 0xA023, 0xA023, 0xA023 },
+{ 0xA024, 0xA024, 0xA024 },
+{ 0xA025, 0xA025, 0xA025 },
+{ 0xA026, 0xA026, 0xA026 },
+{ 0xA027, 0xA027, 0xA027 },
+{ 0xA028, 0xA028, 0xA028 },
+{ 0xA029, 0xA029, 0xA029 },
+{ 0xA02A, 0xA02A, 0xA02A },
+{ 0xA02B, 0xA02B, 0xA02B },
+{ 0xA02C, 0xA02C, 0xA02C },
+{ 0xA02D, 0xA02D, 0xA02D },
+{ 0xA02E, 0xA02E, 0xA02E },
+{ 0xA02F, 0xA02F, 0xA02F },
+{ 0xA030, 0xA030, 0xA030 },
+{ 0xA031, 0xA031, 0xA031 },
+{ 0xA032, 0xA032, 0xA032 },
+{ 0xA033, 0xA033, 0xA033 },
+{ 0xA034, 0xA034, 0xA034 },
+{ 0xA035, 0xA035, 0xA035 },
+{ 0xA036, 0xA036, 0xA036 },
+{ 0xA037, 0xA037, 0xA037 },
+{ 0xA038, 0xA038, 0xA038 },
+{ 0xA039, 0xA039, 0xA039 },
+{ 0xA03A, 0xA03A, 0xA03A },
+{ 0xA03B, 0xA03B, 0xA03B },
+{ 0xA03C, 0xA03C, 0xA03C },
+{ 0xA03D, 0xA03D, 0xA03D },
+{ 0xA03E, 0xA03E, 0xA03E },
+{ 0xA03F, 0xA03F, 0xA03F },
+{ 0xA040, 0xA040, 0xA040 },
+{ 0xA041, 0xA041, 0xA041 },
+{ 0xA042, 0xA042, 0xA042 },
+{ 0xA043, 0xA043, 0xA043 },
+{ 0xA044, 0xA044, 0xA044 },
+{ 0xA045, 0xA045, 0xA045 },
+{ 0xA046, 0xA046, 0xA046 },
+{ 0xA047, 0xA047, 0xA047 },
+{ 0xA048, 0xA048, 0xA048 },
+{ 0xA049, 0xA049, 0xA049 },
+{ 0xA04A, 0xA04A, 0xA04A },
+{ 0xA04B, 0xA04B, 0xA04B },
+{ 0xA04C, 0xA04C, 0xA04C },
+{ 0xA04D, 0xA04D, 0xA04D },
+{ 0xA04E, 0xA04E, 0xA04E },
+{ 0xA04F, 0xA04F, 0xA04F },
+{ 0xA050, 0xA050, 0xA050 },
+{ 0xA051, 0xA051, 0xA051 },
+{ 0xA052, 0xA052, 0xA052 },
+{ 0xA053, 0xA053, 0xA053 },
+{ 0xA054, 0xA054, 0xA054 },
+{ 0xA055, 0xA055, 0xA055 },
+{ 0xA056, 0xA056, 0xA056 },
+{ 0xA057, 0xA057, 0xA057 },
+{ 0xA058, 0xA058, 0xA058 },
+{ 0xA059, 0xA059, 0xA059 },
+{ 0xA05A, 0xA05A, 0xA05A },
+{ 0xA05B, 0xA05B, 0xA05B },
+{ 0xA05C, 0xA05C, 0xA05C },
+{ 0xA05D, 0xA05D, 0xA05D },
+{ 0xA05E, 0xA05E, 0xA05E },
+{ 0xA05F, 0xA05F, 0xA05F },
+{ 0xA060, 0xA060, 0xA060 },
+{ 0xA061, 0xA061, 0xA061 },
+{ 0xA062, 0xA062, 0xA062 },
+{ 0xA063, 0xA063, 0xA063 },
+{ 0xA064, 0xA064, 0xA064 },
+{ 0xA065, 0xA065, 0xA065 },
+{ 0xA066, 0xA066, 0xA066 },
+{ 0xA067, 0xA067, 0xA067 },
+{ 0xA068, 0xA068, 0xA068 },
+{ 0xA069, 0xA069, 0xA069 },
+{ 0xA06A, 0xA06A, 0xA06A },
+{ 0xA06B, 0xA06B, 0xA06B },
+{ 0xA06C, 0xA06C, 0xA06C },
+{ 0xA06D, 0xA06D, 0xA06D },
+{ 0xA06E, 0xA06E, 0xA06E },
+{ 0xA06F, 0xA06F, 0xA06F },
+{ 0xA070, 0xA070, 0xA070 },
+{ 0xA071, 0xA071, 0xA071 },
+{ 0xA072, 0xA072, 0xA072 },
+{ 0xA073, 0xA073, 0xA073 },
+{ 0xA074, 0xA074, 0xA074 },
+{ 0xA075, 0xA075, 0xA075 },
+{ 0xA076, 0xA076, 0xA076 },
+{ 0xA077, 0xA077, 0xA077 },
+{ 0xA078, 0xA078, 0xA078 },
+{ 0xA079, 0xA079, 0xA079 },
+{ 0xA07A, 0xA07A, 0xA07A },
+{ 0xA07B, 0xA07B, 0xA07B },
+{ 0xA07C, 0xA07C, 0xA07C },
+{ 0xA07D, 0xA07D, 0xA07D },
+{ 0xA07E, 0xA07E, 0xA07E },
+{ 0xA07F, 0xA07F, 0xA07F },
+{ 0xA080, 0xA080, 0xA080 },
+{ 0xA081, 0xA081, 0xA081 },
+{ 0xA082, 0xA082, 0xA082 },
+{ 0xA083, 0xA083, 0xA083 },
+{ 0xA084, 0xA084, 0xA084 },
+{ 0xA085, 0xA085, 0xA085 },
+{ 0xA086, 0xA086, 0xA086 },
+{ 0xA087, 0xA087, 0xA087 },
+{ 0xA088, 0xA088, 0xA088 },
+{ 0xA089, 0xA089, 0xA089 },
+{ 0xA08A, 0xA08A, 0xA08A },
+{ 0xA08B, 0xA08B, 0xA08B },
+{ 0xA08C, 0xA08C, 0xA08C },
+{ 0xA08D, 0xA08D, 0xA08D },
+{ 0xA08E, 0xA08E, 0xA08E },
+{ 0xA08F, 0xA08F, 0xA08F },
+{ 0xA090, 0xA090, 0xA090 },
+{ 0xA091, 0xA091, 0xA091 },
+{ 0xA092, 0xA092, 0xA092 },
+{ 0xA093, 0xA093, 0xA093 },
+{ 0xA094, 0xA094, 0xA094 },
+{ 0xA095, 0xA095, 0xA095 },
+{ 0xA096, 0xA096, 0xA096 },
+{ 0xA097, 0xA097, 0xA097 },
+{ 0xA098, 0xA098, 0xA098 },
+{ 0xA099, 0xA099, 0xA099 },
+{ 0xA09A, 0xA09A, 0xA09A },
+{ 0xA09B, 0xA09B, 0xA09B },
+{ 0xA09C, 0xA09C, 0xA09C },
+{ 0xA09D, 0xA09D, 0xA09D },
+{ 0xA09E, 0xA09E, 0xA09E },
+{ 0xA09F, 0xA09F, 0xA09F },
+{ 0xA0A0, 0xA0A0, 0xA0A0 },
+{ 0xA0A1, 0xA0A1, 0xA0A1 },
+{ 0xA0A2, 0xA0A2, 0xA0A2 },
+{ 0xA0A3, 0xA0A3, 0xA0A3 },
+{ 0xA0A4, 0xA0A4, 0xA0A4 },
+{ 0xA0A5, 0xA0A5, 0xA0A5 },
+{ 0xA0A6, 0xA0A6, 0xA0A6 },
+{ 0xA0A7, 0xA0A7, 0xA0A7 },
+{ 0xA0A8, 0xA0A8, 0xA0A8 },
+{ 0xA0A9, 0xA0A9, 0xA0A9 },
+{ 0xA0AA, 0xA0AA, 0xA0AA },
+{ 0xA0AB, 0xA0AB, 0xA0AB },
+{ 0xA0AC, 0xA0AC, 0xA0AC },
+{ 0xA0AD, 0xA0AD, 0xA0AD },
+{ 0xA0AE, 0xA0AE, 0xA0AE },
+{ 0xA0AF, 0xA0AF, 0xA0AF },
+{ 0xA0B0, 0xA0B0, 0xA0B0 },
+{ 0xA0B1, 0xA0B1, 0xA0B1 },
+{ 0xA0B2, 0xA0B2, 0xA0B2 },
+{ 0xA0B3, 0xA0B3, 0xA0B3 },
+{ 0xA0B4, 0xA0B4, 0xA0B4 },
+{ 0xA0B5, 0xA0B5, 0xA0B5 },
+{ 0xA0B6, 0xA0B6, 0xA0B6 },
+{ 0xA0B7, 0xA0B7, 0xA0B7 },
+{ 0xA0B8, 0xA0B8, 0xA0B8 },
+{ 0xA0B9, 0xA0B9, 0xA0B9 },
+{ 0xA0BA, 0xA0BA, 0xA0BA },
+{ 0xA0BB, 0xA0BB, 0xA0BB },
+{ 0xA0BC, 0xA0BC, 0xA0BC },
+{ 0xA0BD, 0xA0BD, 0xA0BD },
+{ 0xA0BE, 0xA0BE, 0xA0BE },
+{ 0xA0BF, 0xA0BF, 0xA0BF },
+{ 0xA0C0, 0xA0C0, 0xA0C0 },
+{ 0xA0C1, 0xA0C1, 0xA0C1 },
+{ 0xA0C2, 0xA0C2, 0xA0C2 },
+{ 0xA0C3, 0xA0C3, 0xA0C3 },
+{ 0xA0C4, 0xA0C4, 0xA0C4 },
+{ 0xA0C5, 0xA0C5, 0xA0C5 },
+{ 0xA0C6, 0xA0C6, 0xA0C6 },
+{ 0xA0C7, 0xA0C7, 0xA0C7 },
+{ 0xA0C8, 0xA0C8, 0xA0C8 },
+{ 0xA0C9, 0xA0C9, 0xA0C9 },
+{ 0xA0CA, 0xA0CA, 0xA0CA },
+{ 0xA0CB, 0xA0CB, 0xA0CB },
+{ 0xA0CC, 0xA0CC, 0xA0CC },
+{ 0xA0CD, 0xA0CD, 0xA0CD },
+{ 0xA0CE, 0xA0CE, 0xA0CE },
+{ 0xA0CF, 0xA0CF, 0xA0CF },
+{ 0xA0D0, 0xA0D0, 0xA0D0 },
+{ 0xA0D1, 0xA0D1, 0xA0D1 },
+{ 0xA0D2, 0xA0D2, 0xA0D2 },
+{ 0xA0D3, 0xA0D3, 0xA0D3 },
+{ 0xA0D4, 0xA0D4, 0xA0D4 },
+{ 0xA0D5, 0xA0D5, 0xA0D5 },
+{ 0xA0D6, 0xA0D6, 0xA0D6 },
+{ 0xA0D7, 0xA0D7, 0xA0D7 },
+{ 0xA0D8, 0xA0D8, 0xA0D8 },
+{ 0xA0D9, 0xA0D9, 0xA0D9 },
+{ 0xA0DA, 0xA0DA, 0xA0DA },
+{ 0xA0DB, 0xA0DB, 0xA0DB },
+{ 0xA0DC, 0xA0DC, 0xA0DC },
+{ 0xA0DD, 0xA0DD, 0xA0DD },
+{ 0xA0DE, 0xA0DE, 0xA0DE },
+{ 0xA0DF, 0xA0DF, 0xA0DF },
+{ 0xA0E0, 0xA0E0, 0xA0E0 },
+{ 0xA0E1, 0xA0E1, 0xA0E1 },
+{ 0xA0E2, 0xA0E2, 0xA0E2 },
+{ 0xA0E3, 0xA0E3, 0xA0E3 },
+{ 0xA0E4, 0xA0E4, 0xA0E4 },
+{ 0xA0E5, 0xA0E5, 0xA0E5 },
+{ 0xA0E6, 0xA0E6, 0xA0E6 },
+{ 0xA0E7, 0xA0E7, 0xA0E7 },
+{ 0xA0E8, 0xA0E8, 0xA0E8 },
+{ 0xA0E9, 0xA0E9, 0xA0E9 },
+{ 0xA0EA, 0xA0EA, 0xA0EA },
+{ 0xA0EB, 0xA0EB, 0xA0EB },
+{ 0xA0EC, 0xA0EC, 0xA0EC },
+{ 0xA0ED, 0xA0ED, 0xA0ED },
+{ 0xA0EE, 0xA0EE, 0xA0EE },
+{ 0xA0EF, 0xA0EF, 0xA0EF },
+{ 0xA0F0, 0xA0F0, 0xA0F0 },
+{ 0xA0F1, 0xA0F1, 0xA0F1 },
+{ 0xA0F2, 0xA0F2, 0xA0F2 },
+{ 0xA0F3, 0xA0F3, 0xA0F3 },
+{ 0xA0F4, 0xA0F4, 0xA0F4 },
+{ 0xA0F5, 0xA0F5, 0xA0F5 },
+{ 0xA0F6, 0xA0F6, 0xA0F6 },
+{ 0xA0F7, 0xA0F7, 0xA0F7 },
+{ 0xA0F8, 0xA0F8, 0xA0F8 },
+{ 0xA0F9, 0xA0F9, 0xA0F9 },
+{ 0xA0FA, 0xA0FA, 0xA0FA },
+{ 0xA0FB, 0xA0FB, 0xA0FB },
+{ 0xA0FC, 0xA0FC, 0xA0FC },
+{ 0xA0FD, 0xA0FD, 0xA0FD },
+{ 0xA0FE, 0xA0FE, 0xA0FE },
+{ 0xA0FF, 0xA0FF, 0xA0FF },
+{ 0xA100, 0xA100, 0xA100 },
+{ 0xA101, 0xA101, 0xA101 },
+{ 0xA102, 0xA102, 0xA102 },
+{ 0xA103, 0xA103, 0xA103 },
+{ 0xA104, 0xA104, 0xA104 },
+{ 0xA105, 0xA105, 0xA105 },
+{ 0xA106, 0xA106, 0xA106 },
+{ 0xA107, 0xA107, 0xA107 },
+{ 0xA108, 0xA108, 0xA108 },
+{ 0xA109, 0xA109, 0xA109 },
+{ 0xA10A, 0xA10A, 0xA10A },
+{ 0xA10B, 0xA10B, 0xA10B },
+{ 0xA10C, 0xA10C, 0xA10C },
+{ 0xA10D, 0xA10D, 0xA10D },
+{ 0xA10E, 0xA10E, 0xA10E },
+{ 0xA10F, 0xA10F, 0xA10F },
+{ 0xA110, 0xA110, 0xA110 },
+{ 0xA111, 0xA111, 0xA111 },
+{ 0xA112, 0xA112, 0xA112 },
+{ 0xA113, 0xA113, 0xA113 },
+{ 0xA114, 0xA114, 0xA114 },
+{ 0xA115, 0xA115, 0xA115 },
+{ 0xA116, 0xA116, 0xA116 },
+{ 0xA117, 0xA117, 0xA117 },
+{ 0xA118, 0xA118, 0xA118 },
+{ 0xA119, 0xA119, 0xA119 },
+{ 0xA11A, 0xA11A, 0xA11A },
+{ 0xA11B, 0xA11B, 0xA11B },
+{ 0xA11C, 0xA11C, 0xA11C },
+{ 0xA11D, 0xA11D, 0xA11D },
+{ 0xA11E, 0xA11E, 0xA11E },
+{ 0xA11F, 0xA11F, 0xA11F },
+{ 0xA120, 0xA120, 0xA120 },
+{ 0xA121, 0xA121, 0xA121 },
+{ 0xA122, 0xA122, 0xA122 },
+{ 0xA123, 0xA123, 0xA123 },
+{ 0xA124, 0xA124, 0xA124 },
+{ 0xA125, 0xA125, 0xA125 },
+{ 0xA126, 0xA126, 0xA126 },
+{ 0xA127, 0xA127, 0xA127 },
+{ 0xA128, 0xA128, 0xA128 },
+{ 0xA129, 0xA129, 0xA129 },
+{ 0xA12A, 0xA12A, 0xA12A },
+{ 0xA12B, 0xA12B, 0xA12B },
+{ 0xA12C, 0xA12C, 0xA12C },
+{ 0xA12D, 0xA12D, 0xA12D },
+{ 0xA12E, 0xA12E, 0xA12E },
+{ 0xA12F, 0xA12F, 0xA12F },
+{ 0xA130, 0xA130, 0xA130 },
+{ 0xA131, 0xA131, 0xA131 },
+{ 0xA132, 0xA132, 0xA132 },
+{ 0xA133, 0xA133, 0xA133 },
+{ 0xA134, 0xA134, 0xA134 },
+{ 0xA135, 0xA135, 0xA135 },
+{ 0xA136, 0xA136, 0xA136 },
+{ 0xA137, 0xA137, 0xA137 },
+{ 0xA138, 0xA138, 0xA138 },
+{ 0xA139, 0xA139, 0xA139 },
+{ 0xA13A, 0xA13A, 0xA13A },
+{ 0xA13B, 0xA13B, 0xA13B },
+{ 0xA13C, 0xA13C, 0xA13C },
+{ 0xA13D, 0xA13D, 0xA13D },
+{ 0xA13E, 0xA13E, 0xA13E },
+{ 0xA13F, 0xA13F, 0xA13F },
+{ 0xA140, 0xA140, 0xA140 },
+{ 0xA141, 0xA141, 0xA141 },
+{ 0xA142, 0xA142, 0xA142 },
+{ 0xA143, 0xA143, 0xA143 },
+{ 0xA144, 0xA144, 0xA144 },
+{ 0xA145, 0xA145, 0xA145 },
+{ 0xA146, 0xA146, 0xA146 },
+{ 0xA147, 0xA147, 0xA147 },
+{ 0xA148, 0xA148, 0xA148 },
+{ 0xA149, 0xA149, 0xA149 },
+{ 0xA14A, 0xA14A, 0xA14A },
+{ 0xA14B, 0xA14B, 0xA14B },
+{ 0xA14C, 0xA14C, 0xA14C },
+{ 0xA14D, 0xA14D, 0xA14D },
+{ 0xA14E, 0xA14E, 0xA14E },
+{ 0xA14F, 0xA14F, 0xA14F },
+{ 0xA150, 0xA150, 0xA150 },
+{ 0xA151, 0xA151, 0xA151 },
+{ 0xA152, 0xA152, 0xA152 },
+{ 0xA153, 0xA153, 0xA153 },
+{ 0xA154, 0xA154, 0xA154 },
+{ 0xA155, 0xA155, 0xA155 },
+{ 0xA156, 0xA156, 0xA156 },
+{ 0xA157, 0xA157, 0xA157 },
+{ 0xA158, 0xA158, 0xA158 },
+{ 0xA159, 0xA159, 0xA159 },
+{ 0xA15A, 0xA15A, 0xA15A },
+{ 0xA15B, 0xA15B, 0xA15B },
+{ 0xA15C, 0xA15C, 0xA15C },
+{ 0xA15D, 0xA15D, 0xA15D },
+{ 0xA15E, 0xA15E, 0xA15E },
+{ 0xA15F, 0xA15F, 0xA15F },
+{ 0xA160, 0xA160, 0xA160 },
+{ 0xA161, 0xA161, 0xA161 },
+{ 0xA162, 0xA162, 0xA162 },
+{ 0xA163, 0xA163, 0xA163 },
+{ 0xA164, 0xA164, 0xA164 },
+{ 0xA165, 0xA165, 0xA165 },
+{ 0xA166, 0xA166, 0xA166 },
+{ 0xA167, 0xA167, 0xA167 },
+{ 0xA168, 0xA168, 0xA168 },
+{ 0xA169, 0xA169, 0xA169 },
+{ 0xA16A, 0xA16A, 0xA16A },
+{ 0xA16B, 0xA16B, 0xA16B },
+{ 0xA16C, 0xA16C, 0xA16C },
+{ 0xA16D, 0xA16D, 0xA16D },
+{ 0xA16E, 0xA16E, 0xA16E },
+{ 0xA16F, 0xA16F, 0xA16F },
+{ 0xA170, 0xA170, 0xA170 },
+{ 0xA171, 0xA171, 0xA171 },
+{ 0xA172, 0xA172, 0xA172 },
+{ 0xA173, 0xA173, 0xA173 },
+{ 0xA174, 0xA174, 0xA174 },
+{ 0xA175, 0xA175, 0xA175 },
+{ 0xA176, 0xA176, 0xA176 },
+{ 0xA177, 0xA177, 0xA177 },
+{ 0xA178, 0xA178, 0xA178 },
+{ 0xA179, 0xA179, 0xA179 },
+{ 0xA17A, 0xA17A, 0xA17A },
+{ 0xA17B, 0xA17B, 0xA17B },
+{ 0xA17C, 0xA17C, 0xA17C },
+{ 0xA17D, 0xA17D, 0xA17D },
+{ 0xA17E, 0xA17E, 0xA17E },
+{ 0xA17F, 0xA17F, 0xA17F },
+{ 0xA180, 0xA180, 0xA180 },
+{ 0xA181, 0xA181, 0xA181 },
+{ 0xA182, 0xA182, 0xA182 },
+{ 0xA183, 0xA183, 0xA183 },
+{ 0xA184, 0xA184, 0xA184 },
+{ 0xA185, 0xA185, 0xA185 },
+{ 0xA186, 0xA186, 0xA186 },
+{ 0xA187, 0xA187, 0xA187 },
+{ 0xA188, 0xA188, 0xA188 },
+{ 0xA189, 0xA189, 0xA189 },
+{ 0xA18A, 0xA18A, 0xA18A },
+{ 0xA18B, 0xA18B, 0xA18B },
+{ 0xA18C, 0xA18C, 0xA18C },
+{ 0xA18D, 0xA18D, 0xA18D },
+{ 0xA18E, 0xA18E, 0xA18E },
+{ 0xA18F, 0xA18F, 0xA18F },
+{ 0xA190, 0xA190, 0xA190 },
+{ 0xA191, 0xA191, 0xA191 },
+{ 0xA192, 0xA192, 0xA192 },
+{ 0xA193, 0xA193, 0xA193 },
+{ 0xA194, 0xA194, 0xA194 },
+{ 0xA195, 0xA195, 0xA195 },
+{ 0xA196, 0xA196, 0xA196 },
+{ 0xA197, 0xA197, 0xA197 },
+{ 0xA198, 0xA198, 0xA198 },
+{ 0xA199, 0xA199, 0xA199 },
+{ 0xA19A, 0xA19A, 0xA19A },
+{ 0xA19B, 0xA19B, 0xA19B },
+{ 0xA19C, 0xA19C, 0xA19C },
+{ 0xA19D, 0xA19D, 0xA19D },
+{ 0xA19E, 0xA19E, 0xA19E },
+{ 0xA19F, 0xA19F, 0xA19F },
+{ 0xA1A0, 0xA1A0, 0xA1A0 },
+{ 0xA1A1, 0xA1A1, 0xA1A1 },
+{ 0xA1A2, 0xA1A2, 0xA1A2 },
+{ 0xA1A3, 0xA1A3, 0xA1A3 },
+{ 0xA1A4, 0xA1A4, 0xA1A4 },
+{ 0xA1A5, 0xA1A5, 0xA1A5 },
+{ 0xA1A6, 0xA1A6, 0xA1A6 },
+{ 0xA1A7, 0xA1A7, 0xA1A7 },
+{ 0xA1A8, 0xA1A8, 0xA1A8 },
+{ 0xA1A9, 0xA1A9, 0xA1A9 },
+{ 0xA1AA, 0xA1AA, 0xA1AA },
+{ 0xA1AB, 0xA1AB, 0xA1AB },
+{ 0xA1AC, 0xA1AC, 0xA1AC },
+{ 0xA1AD, 0xA1AD, 0xA1AD },
+{ 0xA1AE, 0xA1AE, 0xA1AE },
+{ 0xA1AF, 0xA1AF, 0xA1AF },
+{ 0xA1B0, 0xA1B0, 0xA1B0 },
+{ 0xA1B1, 0xA1B1, 0xA1B1 },
+{ 0xA1B2, 0xA1B2, 0xA1B2 },
+{ 0xA1B3, 0xA1B3, 0xA1B3 },
+{ 0xA1B4, 0xA1B4, 0xA1B4 },
+{ 0xA1B5, 0xA1B5, 0xA1B5 },
+{ 0xA1B6, 0xA1B6, 0xA1B6 },
+{ 0xA1B7, 0xA1B7, 0xA1B7 },
+{ 0xA1B8, 0xA1B8, 0xA1B8 },
+{ 0xA1B9, 0xA1B9, 0xA1B9 },
+{ 0xA1BA, 0xA1BA, 0xA1BA },
+{ 0xA1BB, 0xA1BB, 0xA1BB },
+{ 0xA1BC, 0xA1BC, 0xA1BC },
+{ 0xA1BD, 0xA1BD, 0xA1BD },
+{ 0xA1BE, 0xA1BE, 0xA1BE },
+{ 0xA1BF, 0xA1BF, 0xA1BF },
+{ 0xA1C0, 0xA1C0, 0xA1C0 },
+{ 0xA1C1, 0xA1C1, 0xA1C1 },
+{ 0xA1C2, 0xA1C2, 0xA1C2 },
+{ 0xA1C3, 0xA1C3, 0xA1C3 },
+{ 0xA1C4, 0xA1C4, 0xA1C4 },
+{ 0xA1C5, 0xA1C5, 0xA1C5 },
+{ 0xA1C6, 0xA1C6, 0xA1C6 },
+{ 0xA1C7, 0xA1C7, 0xA1C7 },
+{ 0xA1C8, 0xA1C8, 0xA1C8 },
+{ 0xA1C9, 0xA1C9, 0xA1C9 },
+{ 0xA1CA, 0xA1CA, 0xA1CA },
+{ 0xA1CB, 0xA1CB, 0xA1CB },
+{ 0xA1CC, 0xA1CC, 0xA1CC },
+{ 0xA1CD, 0xA1CD, 0xA1CD },
+{ 0xA1CE, 0xA1CE, 0xA1CE },
+{ 0xA1CF, 0xA1CF, 0xA1CF },
+{ 0xA1D0, 0xA1D0, 0xA1D0 },
+{ 0xA1D1, 0xA1D1, 0xA1D1 },
+{ 0xA1D2, 0xA1D2, 0xA1D2 },
+{ 0xA1D3, 0xA1D3, 0xA1D3 },
+{ 0xA1D4, 0xA1D4, 0xA1D4 },
+{ 0xA1D5, 0xA1D5, 0xA1D5 },
+{ 0xA1D6, 0xA1D6, 0xA1D6 },
+{ 0xA1D7, 0xA1D7, 0xA1D7 },
+{ 0xA1D8, 0xA1D8, 0xA1D8 },
+{ 0xA1D9, 0xA1D9, 0xA1D9 },
+{ 0xA1DA, 0xA1DA, 0xA1DA },
+{ 0xA1DB, 0xA1DB, 0xA1DB },
+{ 0xA1DC, 0xA1DC, 0xA1DC },
+{ 0xA1DD, 0xA1DD, 0xA1DD },
+{ 0xA1DE, 0xA1DE, 0xA1DE },
+{ 0xA1DF, 0xA1DF, 0xA1DF },
+{ 0xA1E0, 0xA1E0, 0xA1E0 },
+{ 0xA1E1, 0xA1E1, 0xA1E1 },
+{ 0xA1E2, 0xA1E2, 0xA1E2 },
+{ 0xA1E3, 0xA1E3, 0xA1E3 },
+{ 0xA1E4, 0xA1E4, 0xA1E4 },
+{ 0xA1E5, 0xA1E5, 0xA1E5 },
+{ 0xA1E6, 0xA1E6, 0xA1E6 },
+{ 0xA1E7, 0xA1E7, 0xA1E7 },
+{ 0xA1E8, 0xA1E8, 0xA1E8 },
+{ 0xA1E9, 0xA1E9, 0xA1E9 },
+{ 0xA1EA, 0xA1EA, 0xA1EA },
+{ 0xA1EB, 0xA1EB, 0xA1EB },
+{ 0xA1EC, 0xA1EC, 0xA1EC },
+{ 0xA1ED, 0xA1ED, 0xA1ED },
+{ 0xA1EE, 0xA1EE, 0xA1EE },
+{ 0xA1EF, 0xA1EF, 0xA1EF },
+{ 0xA1F0, 0xA1F0, 0xA1F0 },
+{ 0xA1F1, 0xA1F1, 0xA1F1 },
+{ 0xA1F2, 0xA1F2, 0xA1F2 },
+{ 0xA1F3, 0xA1F3, 0xA1F3 },
+{ 0xA1F4, 0xA1F4, 0xA1F4 },
+{ 0xA1F5, 0xA1F5, 0xA1F5 },
+{ 0xA1F6, 0xA1F6, 0xA1F6 },
+{ 0xA1F7, 0xA1F7, 0xA1F7 },
+{ 0xA1F8, 0xA1F8, 0xA1F8 },
+{ 0xA1F9, 0xA1F9, 0xA1F9 },
+{ 0xA1FA, 0xA1FA, 0xA1FA },
+{ 0xA1FB, 0xA1FB, 0xA1FB },
+{ 0xA1FC, 0xA1FC, 0xA1FC },
+{ 0xA1FD, 0xA1FD, 0xA1FD },
+{ 0xA1FE, 0xA1FE, 0xA1FE },
+{ 0xA1FF, 0xA1FF, 0xA1FF },
+{ 0xA200, 0xA200, 0xA200 },
+{ 0xA201, 0xA201, 0xA201 },
+{ 0xA202, 0xA202, 0xA202 },
+{ 0xA203, 0xA203, 0xA203 },
+{ 0xA204, 0xA204, 0xA204 },
+{ 0xA205, 0xA205, 0xA205 },
+{ 0xA206, 0xA206, 0xA206 },
+{ 0xA207, 0xA207, 0xA207 },
+{ 0xA208, 0xA208, 0xA208 },
+{ 0xA209, 0xA209, 0xA209 },
+{ 0xA20A, 0xA20A, 0xA20A },
+{ 0xA20B, 0xA20B, 0xA20B },
+{ 0xA20C, 0xA20C, 0xA20C },
+{ 0xA20D, 0xA20D, 0xA20D },
+{ 0xA20E, 0xA20E, 0xA20E },
+{ 0xA20F, 0xA20F, 0xA20F },
+{ 0xA210, 0xA210, 0xA210 },
+{ 0xA211, 0xA211, 0xA211 },
+{ 0xA212, 0xA212, 0xA212 },
+{ 0xA213, 0xA213, 0xA213 },
+{ 0xA214, 0xA214, 0xA214 },
+{ 0xA215, 0xA215, 0xA215 },
+{ 0xA216, 0xA216, 0xA216 },
+{ 0xA217, 0xA217, 0xA217 },
+{ 0xA218, 0xA218, 0xA218 },
+{ 0xA219, 0xA219, 0xA219 },
+{ 0xA21A, 0xA21A, 0xA21A },
+{ 0xA21B, 0xA21B, 0xA21B },
+{ 0xA21C, 0xA21C, 0xA21C },
+{ 0xA21D, 0xA21D, 0xA21D },
+{ 0xA21E, 0xA21E, 0xA21E },
+{ 0xA21F, 0xA21F, 0xA21F },
+{ 0xA220, 0xA220, 0xA220 },
+{ 0xA221, 0xA221, 0xA221 },
+{ 0xA222, 0xA222, 0xA222 },
+{ 0xA223, 0xA223, 0xA223 },
+{ 0xA224, 0xA224, 0xA224 },
+{ 0xA225, 0xA225, 0xA225 },
+{ 0xA226, 0xA226, 0xA226 },
+{ 0xA227, 0xA227, 0xA227 },
+{ 0xA228, 0xA228, 0xA228 },
+{ 0xA229, 0xA229, 0xA229 },
+{ 0xA22A, 0xA22A, 0xA22A },
+{ 0xA22B, 0xA22B, 0xA22B },
+{ 0xA22C, 0xA22C, 0xA22C },
+{ 0xA22D, 0xA22D, 0xA22D },
+{ 0xA22E, 0xA22E, 0xA22E },
+{ 0xA22F, 0xA22F, 0xA22F },
+{ 0xA230, 0xA230, 0xA230 },
+{ 0xA231, 0xA231, 0xA231 },
+{ 0xA232, 0xA232, 0xA232 },
+{ 0xA233, 0xA233, 0xA233 },
+{ 0xA234, 0xA234, 0xA234 },
+{ 0xA235, 0xA235, 0xA235 },
+{ 0xA236, 0xA236, 0xA236 },
+{ 0xA237, 0xA237, 0xA237 },
+{ 0xA238, 0xA238, 0xA238 },
+{ 0xA239, 0xA239, 0xA239 },
+{ 0xA23A, 0xA23A, 0xA23A },
+{ 0xA23B, 0xA23B, 0xA23B },
+{ 0xA23C, 0xA23C, 0xA23C },
+{ 0xA23D, 0xA23D, 0xA23D },
+{ 0xA23E, 0xA23E, 0xA23E },
+{ 0xA23F, 0xA23F, 0xA23F },
+{ 0xA240, 0xA240, 0xA240 },
+{ 0xA241, 0xA241, 0xA241 },
+{ 0xA242, 0xA242, 0xA242 },
+{ 0xA243, 0xA243, 0xA243 },
+{ 0xA244, 0xA244, 0xA244 },
+{ 0xA245, 0xA245, 0xA245 },
+{ 0xA246, 0xA246, 0xA246 },
+{ 0xA247, 0xA247, 0xA247 },
+{ 0xA248, 0xA248, 0xA248 },
+{ 0xA249, 0xA249, 0xA249 },
+{ 0xA24A, 0xA24A, 0xA24A },
+{ 0xA24B, 0xA24B, 0xA24B },
+{ 0xA24C, 0xA24C, 0xA24C },
+{ 0xA24D, 0xA24D, 0xA24D },
+{ 0xA24E, 0xA24E, 0xA24E },
+{ 0xA24F, 0xA24F, 0xA24F },
+{ 0xA250, 0xA250, 0xA250 },
+{ 0xA251, 0xA251, 0xA251 },
+{ 0xA252, 0xA252, 0xA252 },
+{ 0xA253, 0xA253, 0xA253 },
+{ 0xA254, 0xA254, 0xA254 },
+{ 0xA255, 0xA255, 0xA255 },
+{ 0xA256, 0xA256, 0xA256 },
+{ 0xA257, 0xA257, 0xA257 },
+{ 0xA258, 0xA258, 0xA258 },
+{ 0xA259, 0xA259, 0xA259 },
+{ 0xA25A, 0xA25A, 0xA25A },
+{ 0xA25B, 0xA25B, 0xA25B },
+{ 0xA25C, 0xA25C, 0xA25C },
+{ 0xA25D, 0xA25D, 0xA25D },
+{ 0xA25E, 0xA25E, 0xA25E },
+{ 0xA25F, 0xA25F, 0xA25F },
+{ 0xA260, 0xA260, 0xA260 },
+{ 0xA261, 0xA261, 0xA261 },
+{ 0xA262, 0xA262, 0xA262 },
+{ 0xA263, 0xA263, 0xA263 },
+{ 0xA264, 0xA264, 0xA264 },
+{ 0xA265, 0xA265, 0xA265 },
+{ 0xA266, 0xA266, 0xA266 },
+{ 0xA267, 0xA267, 0xA267 },
+{ 0xA268, 0xA268, 0xA268 },
+{ 0xA269, 0xA269, 0xA269 },
+{ 0xA26A, 0xA26A, 0xA26A },
+{ 0xA26B, 0xA26B, 0xA26B },
+{ 0xA26C, 0xA26C, 0xA26C },
+{ 0xA26D, 0xA26D, 0xA26D },
+{ 0xA26E, 0xA26E, 0xA26E },
+{ 0xA26F, 0xA26F, 0xA26F },
+{ 0xA270, 0xA270, 0xA270 },
+{ 0xA271, 0xA271, 0xA271 },
+{ 0xA272, 0xA272, 0xA272 },
+{ 0xA273, 0xA273, 0xA273 },
+{ 0xA274, 0xA274, 0xA274 },
+{ 0xA275, 0xA275, 0xA275 },
+{ 0xA276, 0xA276, 0xA276 },
+{ 0xA277, 0xA277, 0xA277 },
+{ 0xA278, 0xA278, 0xA278 },
+{ 0xA279, 0xA279, 0xA279 },
+{ 0xA27A, 0xA27A, 0xA27A },
+{ 0xA27B, 0xA27B, 0xA27B },
+{ 0xA27C, 0xA27C, 0xA27C },
+{ 0xA27D, 0xA27D, 0xA27D },
+{ 0xA27E, 0xA27E, 0xA27E },
+{ 0xA27F, 0xA27F, 0xA27F },
+{ 0xA280, 0xA280, 0xA280 },
+{ 0xA281, 0xA281, 0xA281 },
+{ 0xA282, 0xA282, 0xA282 },
+{ 0xA283, 0xA283, 0xA283 },
+{ 0xA284, 0xA284, 0xA284 },
+{ 0xA285, 0xA285, 0xA285 },
+{ 0xA286, 0xA286, 0xA286 },
+{ 0xA287, 0xA287, 0xA287 },
+{ 0xA288, 0xA288, 0xA288 },
+{ 0xA289, 0xA289, 0xA289 },
+{ 0xA28A, 0xA28A, 0xA28A },
+{ 0xA28B, 0xA28B, 0xA28B },
+{ 0xA28C, 0xA28C, 0xA28C },
+{ 0xA28D, 0xA28D, 0xA28D },
+{ 0xA28E, 0xA28E, 0xA28E },
+{ 0xA28F, 0xA28F, 0xA28F },
+{ 0xA290, 0xA290, 0xA290 },
+{ 0xA291, 0xA291, 0xA291 },
+{ 0xA292, 0xA292, 0xA292 },
+{ 0xA293, 0xA293, 0xA293 },
+{ 0xA294, 0xA294, 0xA294 },
+{ 0xA295, 0xA295, 0xA295 },
+{ 0xA296, 0xA296, 0xA296 },
+{ 0xA297, 0xA297, 0xA297 },
+{ 0xA298, 0xA298, 0xA298 },
+{ 0xA299, 0xA299, 0xA299 },
+{ 0xA29A, 0xA29A, 0xA29A },
+{ 0xA29B, 0xA29B, 0xA29B },
+{ 0xA29C, 0xA29C, 0xA29C },
+{ 0xA29D, 0xA29D, 0xA29D },
+{ 0xA29E, 0xA29E, 0xA29E },
+{ 0xA29F, 0xA29F, 0xA29F },
+{ 0xA2A0, 0xA2A0, 0xA2A0 },
+{ 0xA2A1, 0xA2A1, 0xA2A1 },
+{ 0xA2A2, 0xA2A2, 0xA2A2 },
+{ 0xA2A3, 0xA2A3, 0xA2A3 },
+{ 0xA2A4, 0xA2A4, 0xA2A4 },
+{ 0xA2A5, 0xA2A5, 0xA2A5 },
+{ 0xA2A6, 0xA2A6, 0xA2A6 },
+{ 0xA2A7, 0xA2A7, 0xA2A7 },
+{ 0xA2A8, 0xA2A8, 0xA2A8 },
+{ 0xA2A9, 0xA2A9, 0xA2A9 },
+{ 0xA2AA, 0xA2AA, 0xA2AA },
+{ 0xA2AB, 0xA2AB, 0xA2AB },
+{ 0xA2AC, 0xA2AC, 0xA2AC },
+{ 0xA2AD, 0xA2AD, 0xA2AD },
+{ 0xA2AE, 0xA2AE, 0xA2AE },
+{ 0xA2AF, 0xA2AF, 0xA2AF },
+{ 0xA2B0, 0xA2B0, 0xA2B0 },
+{ 0xA2B1, 0xA2B1, 0xA2B1 },
+{ 0xA2B2, 0xA2B2, 0xA2B2 },
+{ 0xA2B3, 0xA2B3, 0xA2B3 },
+{ 0xA2B4, 0xA2B4, 0xA2B4 },
+{ 0xA2B5, 0xA2B5, 0xA2B5 },
+{ 0xA2B6, 0xA2B6, 0xA2B6 },
+{ 0xA2B7, 0xA2B7, 0xA2B7 },
+{ 0xA2B8, 0xA2B8, 0xA2B8 },
+{ 0xA2B9, 0xA2B9, 0xA2B9 },
+{ 0xA2BA, 0xA2BA, 0xA2BA },
+{ 0xA2BB, 0xA2BB, 0xA2BB },
+{ 0xA2BC, 0xA2BC, 0xA2BC },
+{ 0xA2BD, 0xA2BD, 0xA2BD },
+{ 0xA2BE, 0xA2BE, 0xA2BE },
+{ 0xA2BF, 0xA2BF, 0xA2BF },
+{ 0xA2C0, 0xA2C0, 0xA2C0 },
+{ 0xA2C1, 0xA2C1, 0xA2C1 },
+{ 0xA2C2, 0xA2C2, 0xA2C2 },
+{ 0xA2C3, 0xA2C3, 0xA2C3 },
+{ 0xA2C4, 0xA2C4, 0xA2C4 },
+{ 0xA2C5, 0xA2C5, 0xA2C5 },
+{ 0xA2C6, 0xA2C6, 0xA2C6 },
+{ 0xA2C7, 0xA2C7, 0xA2C7 },
+{ 0xA2C8, 0xA2C8, 0xA2C8 },
+{ 0xA2C9, 0xA2C9, 0xA2C9 },
+{ 0xA2CA, 0xA2CA, 0xA2CA },
+{ 0xA2CB, 0xA2CB, 0xA2CB },
+{ 0xA2CC, 0xA2CC, 0xA2CC },
+{ 0xA2CD, 0xA2CD, 0xA2CD },
+{ 0xA2CE, 0xA2CE, 0xA2CE },
+{ 0xA2CF, 0xA2CF, 0xA2CF },
+{ 0xA2D0, 0xA2D0, 0xA2D0 },
+{ 0xA2D1, 0xA2D1, 0xA2D1 },
+{ 0xA2D2, 0xA2D2, 0xA2D2 },
+{ 0xA2D3, 0xA2D3, 0xA2D3 },
+{ 0xA2D4, 0xA2D4, 0xA2D4 },
+{ 0xA2D5, 0xA2D5, 0xA2D5 },
+{ 0xA2D6, 0xA2D6, 0xA2D6 },
+{ 0xA2D7, 0xA2D7, 0xA2D7 },
+{ 0xA2D8, 0xA2D8, 0xA2D8 },
+{ 0xA2D9, 0xA2D9, 0xA2D9 },
+{ 0xA2DA, 0xA2DA, 0xA2DA },
+{ 0xA2DB, 0xA2DB, 0xA2DB },
+{ 0xA2DC, 0xA2DC, 0xA2DC },
+{ 0xA2DD, 0xA2DD, 0xA2DD },
+{ 0xA2DE, 0xA2DE, 0xA2DE },
+{ 0xA2DF, 0xA2DF, 0xA2DF },
+{ 0xA2E0, 0xA2E0, 0xA2E0 },
+{ 0xA2E1, 0xA2E1, 0xA2E1 },
+{ 0xA2E2, 0xA2E2, 0xA2E2 },
+{ 0xA2E3, 0xA2E3, 0xA2E3 },
+{ 0xA2E4, 0xA2E4, 0xA2E4 },
+{ 0xA2E5, 0xA2E5, 0xA2E5 },
+{ 0xA2E6, 0xA2E6, 0xA2E6 },
+{ 0xA2E7, 0xA2E7, 0xA2E7 },
+{ 0xA2E8, 0xA2E8, 0xA2E8 },
+{ 0xA2E9, 0xA2E9, 0xA2E9 },
+{ 0xA2EA, 0xA2EA, 0xA2EA },
+{ 0xA2EB, 0xA2EB, 0xA2EB },
+{ 0xA2EC, 0xA2EC, 0xA2EC },
+{ 0xA2ED, 0xA2ED, 0xA2ED },
+{ 0xA2EE, 0xA2EE, 0xA2EE },
+{ 0xA2EF, 0xA2EF, 0xA2EF },
+{ 0xA2F0, 0xA2F0, 0xA2F0 },
+{ 0xA2F1, 0xA2F1, 0xA2F1 },
+{ 0xA2F2, 0xA2F2, 0xA2F2 },
+{ 0xA2F3, 0xA2F3, 0xA2F3 },
+{ 0xA2F4, 0xA2F4, 0xA2F4 },
+{ 0xA2F5, 0xA2F5, 0xA2F5 },
+{ 0xA2F6, 0xA2F6, 0xA2F6 },
+{ 0xA2F7, 0xA2F7, 0xA2F7 },
+{ 0xA2F8, 0xA2F8, 0xA2F8 },
+{ 0xA2F9, 0xA2F9, 0xA2F9 },
+{ 0xA2FA, 0xA2FA, 0xA2FA },
+{ 0xA2FB, 0xA2FB, 0xA2FB },
+{ 0xA2FC, 0xA2FC, 0xA2FC },
+{ 0xA2FD, 0xA2FD, 0xA2FD },
+{ 0xA2FE, 0xA2FE, 0xA2FE },
+{ 0xA2FF, 0xA2FF, 0xA2FF },
+{ 0xA300, 0xA300, 0xA300 },
+{ 0xA301, 0xA301, 0xA301 },
+{ 0xA302, 0xA302, 0xA302 },
+{ 0xA303, 0xA303, 0xA303 },
+{ 0xA304, 0xA304, 0xA304 },
+{ 0xA305, 0xA305, 0xA305 },
+{ 0xA306, 0xA306, 0xA306 },
+{ 0xA307, 0xA307, 0xA307 },
+{ 0xA308, 0xA308, 0xA308 },
+{ 0xA309, 0xA309, 0xA309 },
+{ 0xA30A, 0xA30A, 0xA30A },
+{ 0xA30B, 0xA30B, 0xA30B },
+{ 0xA30C, 0xA30C, 0xA30C },
+{ 0xA30D, 0xA30D, 0xA30D },
+{ 0xA30E, 0xA30E, 0xA30E },
+{ 0xA30F, 0xA30F, 0xA30F },
+{ 0xA310, 0xA310, 0xA310 },
+{ 0xA311, 0xA311, 0xA311 },
+{ 0xA312, 0xA312, 0xA312 },
+{ 0xA313, 0xA313, 0xA313 },
+{ 0xA314, 0xA314, 0xA314 },
+{ 0xA315, 0xA315, 0xA315 },
+{ 0xA316, 0xA316, 0xA316 },
+{ 0xA317, 0xA317, 0xA317 },
+{ 0xA318, 0xA318, 0xA318 },
+{ 0xA319, 0xA319, 0xA319 },
+{ 0xA31A, 0xA31A, 0xA31A },
+{ 0xA31B, 0xA31B, 0xA31B },
+{ 0xA31C, 0xA31C, 0xA31C },
+{ 0xA31D, 0xA31D, 0xA31D },
+{ 0xA31E, 0xA31E, 0xA31E },
+{ 0xA31F, 0xA31F, 0xA31F },
+{ 0xA320, 0xA320, 0xA320 },
+{ 0xA321, 0xA321, 0xA321 },
+{ 0xA322, 0xA322, 0xA322 },
+{ 0xA323, 0xA323, 0xA323 },
+{ 0xA324, 0xA324, 0xA324 },
+{ 0xA325, 0xA325, 0xA325 },
+{ 0xA326, 0xA326, 0xA326 },
+{ 0xA327, 0xA327, 0xA327 },
+{ 0xA328, 0xA328, 0xA328 },
+{ 0xA329, 0xA329, 0xA329 },
+{ 0xA32A, 0xA32A, 0xA32A },
+{ 0xA32B, 0xA32B, 0xA32B },
+{ 0xA32C, 0xA32C, 0xA32C },
+{ 0xA32D, 0xA32D, 0xA32D },
+{ 0xA32E, 0xA32E, 0xA32E },
+{ 0xA32F, 0xA32F, 0xA32F },
+{ 0xA330, 0xA330, 0xA330 },
+{ 0xA331, 0xA331, 0xA331 },
+{ 0xA332, 0xA332, 0xA332 },
+{ 0xA333, 0xA333, 0xA333 },
+{ 0xA334, 0xA334, 0xA334 },
+{ 0xA335, 0xA335, 0xA335 },
+{ 0xA336, 0xA336, 0xA336 },
+{ 0xA337, 0xA337, 0xA337 },
+{ 0xA338, 0xA338, 0xA338 },
+{ 0xA339, 0xA339, 0xA339 },
+{ 0xA33A, 0xA33A, 0xA33A },
+{ 0xA33B, 0xA33B, 0xA33B },
+{ 0xA33C, 0xA33C, 0xA33C },
+{ 0xA33D, 0xA33D, 0xA33D },
+{ 0xA33E, 0xA33E, 0xA33E },
+{ 0xA33F, 0xA33F, 0xA33F },
+{ 0xA340, 0xA340, 0xA340 },
+{ 0xA341, 0xA341, 0xA341 },
+{ 0xA342, 0xA342, 0xA342 },
+{ 0xA343, 0xA343, 0xA343 },
+{ 0xA344, 0xA344, 0xA344 },
+{ 0xA345, 0xA345, 0xA345 },
+{ 0xA346, 0xA346, 0xA346 },
+{ 0xA347, 0xA347, 0xA347 },
+{ 0xA348, 0xA348, 0xA348 },
+{ 0xA349, 0xA349, 0xA349 },
+{ 0xA34A, 0xA34A, 0xA34A },
+{ 0xA34B, 0xA34B, 0xA34B },
+{ 0xA34C, 0xA34C, 0xA34C },
+{ 0xA34D, 0xA34D, 0xA34D },
+{ 0xA34E, 0xA34E, 0xA34E },
+{ 0xA34F, 0xA34F, 0xA34F },
+{ 0xA350, 0xA350, 0xA350 },
+{ 0xA351, 0xA351, 0xA351 },
+{ 0xA352, 0xA352, 0xA352 },
+{ 0xA353, 0xA353, 0xA353 },
+{ 0xA354, 0xA354, 0xA354 },
+{ 0xA355, 0xA355, 0xA355 },
+{ 0xA356, 0xA356, 0xA356 },
+{ 0xA357, 0xA357, 0xA357 },
+{ 0xA358, 0xA358, 0xA358 },
+{ 0xA359, 0xA359, 0xA359 },
+{ 0xA35A, 0xA35A, 0xA35A },
+{ 0xA35B, 0xA35B, 0xA35B },
+{ 0xA35C, 0xA35C, 0xA35C },
+{ 0xA35D, 0xA35D, 0xA35D },
+{ 0xA35E, 0xA35E, 0xA35E },
+{ 0xA35F, 0xA35F, 0xA35F },
+{ 0xA360, 0xA360, 0xA360 },
+{ 0xA361, 0xA361, 0xA361 },
+{ 0xA362, 0xA362, 0xA362 },
+{ 0xA363, 0xA363, 0xA363 },
+{ 0xA364, 0xA364, 0xA364 },
+{ 0xA365, 0xA365, 0xA365 },
+{ 0xA366, 0xA366, 0xA366 },
+{ 0xA367, 0xA367, 0xA367 },
+{ 0xA368, 0xA368, 0xA368 },
+{ 0xA369, 0xA369, 0xA369 },
+{ 0xA36A, 0xA36A, 0xA36A },
+{ 0xA36B, 0xA36B, 0xA36B },
+{ 0xA36C, 0xA36C, 0xA36C },
+{ 0xA36D, 0xA36D, 0xA36D },
+{ 0xA36E, 0xA36E, 0xA36E },
+{ 0xA36F, 0xA36F, 0xA36F },
+{ 0xA370, 0xA370, 0xA370 },
+{ 0xA371, 0xA371, 0xA371 },
+{ 0xA372, 0xA372, 0xA372 },
+{ 0xA373, 0xA373, 0xA373 },
+{ 0xA374, 0xA374, 0xA374 },
+{ 0xA375, 0xA375, 0xA375 },
+{ 0xA376, 0xA376, 0xA376 },
+{ 0xA377, 0xA377, 0xA377 },
+{ 0xA378, 0xA378, 0xA378 },
+{ 0xA379, 0xA379, 0xA379 },
+{ 0xA37A, 0xA37A, 0xA37A },
+{ 0xA37B, 0xA37B, 0xA37B },
+{ 0xA37C, 0xA37C, 0xA37C },
+{ 0xA37D, 0xA37D, 0xA37D },
+{ 0xA37E, 0xA37E, 0xA37E },
+{ 0xA37F, 0xA37F, 0xA37F },
+{ 0xA380, 0xA380, 0xA380 },
+{ 0xA381, 0xA381, 0xA381 },
+{ 0xA382, 0xA382, 0xA382 },
+{ 0xA383, 0xA383, 0xA383 },
+{ 0xA384, 0xA384, 0xA384 },
+{ 0xA385, 0xA385, 0xA385 },
+{ 0xA386, 0xA386, 0xA386 },
+{ 0xA387, 0xA387, 0xA387 },
+{ 0xA388, 0xA388, 0xA388 },
+{ 0xA389, 0xA389, 0xA389 },
+{ 0xA38A, 0xA38A, 0xA38A },
+{ 0xA38B, 0xA38B, 0xA38B },
+{ 0xA38C, 0xA38C, 0xA38C },
+{ 0xA38D, 0xA38D, 0xA38D },
+{ 0xA38E, 0xA38E, 0xA38E },
+{ 0xA38F, 0xA38F, 0xA38F },
+{ 0xA390, 0xA390, 0xA390 },
+{ 0xA391, 0xA391, 0xA391 },
+{ 0xA392, 0xA392, 0xA392 },
+{ 0xA393, 0xA393, 0xA393 },
+{ 0xA394, 0xA394, 0xA394 },
+{ 0xA395, 0xA395, 0xA395 },
+{ 0xA396, 0xA396, 0xA396 },
+{ 0xA397, 0xA397, 0xA397 },
+{ 0xA398, 0xA398, 0xA398 },
+{ 0xA399, 0xA399, 0xA399 },
+{ 0xA39A, 0xA39A, 0xA39A },
+{ 0xA39B, 0xA39B, 0xA39B },
+{ 0xA39C, 0xA39C, 0xA39C },
+{ 0xA39D, 0xA39D, 0xA39D },
+{ 0xA39E, 0xA39E, 0xA39E },
+{ 0xA39F, 0xA39F, 0xA39F },
+{ 0xA3A0, 0xA3A0, 0xA3A0 },
+{ 0xA3A1, 0xA3A1, 0xA3A1 },
+{ 0xA3A2, 0xA3A2, 0xA3A2 },
+{ 0xA3A3, 0xA3A3, 0xA3A3 },
+{ 0xA3A4, 0xA3A4, 0xA3A4 },
+{ 0xA3A5, 0xA3A5, 0xA3A5 },
+{ 0xA3A6, 0xA3A6, 0xA3A6 },
+{ 0xA3A7, 0xA3A7, 0xA3A7 },
+{ 0xA3A8, 0xA3A8, 0xA3A8 },
+{ 0xA3A9, 0xA3A9, 0xA3A9 },
+{ 0xA3AA, 0xA3AA, 0xA3AA },
+{ 0xA3AB, 0xA3AB, 0xA3AB },
+{ 0xA3AC, 0xA3AC, 0xA3AC },
+{ 0xA3AD, 0xA3AD, 0xA3AD },
+{ 0xA3AE, 0xA3AE, 0xA3AE },
+{ 0xA3AF, 0xA3AF, 0xA3AF },
+{ 0xA3B0, 0xA3B0, 0xA3B0 },
+{ 0xA3B1, 0xA3B1, 0xA3B1 },
+{ 0xA3B2, 0xA3B2, 0xA3B2 },
+{ 0xA3B3, 0xA3B3, 0xA3B3 },
+{ 0xA3B4, 0xA3B4, 0xA3B4 },
+{ 0xA3B5, 0xA3B5, 0xA3B5 },
+{ 0xA3B6, 0xA3B6, 0xA3B6 },
+{ 0xA3B7, 0xA3B7, 0xA3B7 },
+{ 0xA3B8, 0xA3B8, 0xA3B8 },
+{ 0xA3B9, 0xA3B9, 0xA3B9 },
+{ 0xA3BA, 0xA3BA, 0xA3BA },
+{ 0xA3BB, 0xA3BB, 0xA3BB },
+{ 0xA3BC, 0xA3BC, 0xA3BC },
+{ 0xA3BD, 0xA3BD, 0xA3BD },
+{ 0xA3BE, 0xA3BE, 0xA3BE },
+{ 0xA3BF, 0xA3BF, 0xA3BF },
+{ 0xA3C0, 0xA3C0, 0xA3C0 },
+{ 0xA3C1, 0xA3C1, 0xA3C1 },
+{ 0xA3C2, 0xA3C2, 0xA3C2 },
+{ 0xA3C3, 0xA3C3, 0xA3C3 },
+{ 0xA3C4, 0xA3C4, 0xA3C4 },
+{ 0xA3C5, 0xA3C5, 0xA3C5 },
+{ 0xA3C6, 0xA3C6, 0xA3C6 },
+{ 0xA3C7, 0xA3C7, 0xA3C7 },
+{ 0xA3C8, 0xA3C8, 0xA3C8 },
+{ 0xA3C9, 0xA3C9, 0xA3C9 },
+{ 0xA3CA, 0xA3CA, 0xA3CA },
+{ 0xA3CB, 0xA3CB, 0xA3CB },
+{ 0xA3CC, 0xA3CC, 0xA3CC },
+{ 0xA3CD, 0xA3CD, 0xA3CD },
+{ 0xA3CE, 0xA3CE, 0xA3CE },
+{ 0xA3CF, 0xA3CF, 0xA3CF },
+{ 0xA3D0, 0xA3D0, 0xA3D0 },
+{ 0xA3D1, 0xA3D1, 0xA3D1 },
+{ 0xA3D2, 0xA3D2, 0xA3D2 },
+{ 0xA3D3, 0xA3D3, 0xA3D3 },
+{ 0xA3D4, 0xA3D4, 0xA3D4 },
+{ 0xA3D5, 0xA3D5, 0xA3D5 },
+{ 0xA3D6, 0xA3D6, 0xA3D6 },
+{ 0xA3D7, 0xA3D7, 0xA3D7 },
+{ 0xA3D8, 0xA3D8, 0xA3D8 },
+{ 0xA3D9, 0xA3D9, 0xA3D9 },
+{ 0xA3DA, 0xA3DA, 0xA3DA },
+{ 0xA3DB, 0xA3DB, 0xA3DB },
+{ 0xA3DC, 0xA3DC, 0xA3DC },
+{ 0xA3DD, 0xA3DD, 0xA3DD },
+{ 0xA3DE, 0xA3DE, 0xA3DE },
+{ 0xA3DF, 0xA3DF, 0xA3DF },
+{ 0xA3E0, 0xA3E0, 0xA3E0 },
+{ 0xA3E1, 0xA3E1, 0xA3E1 },
+{ 0xA3E2, 0xA3E2, 0xA3E2 },
+{ 0xA3E3, 0xA3E3, 0xA3E3 },
+{ 0xA3E4, 0xA3E4, 0xA3E4 },
+{ 0xA3E5, 0xA3E5, 0xA3E5 },
+{ 0xA3E6, 0xA3E6, 0xA3E6 },
+{ 0xA3E7, 0xA3E7, 0xA3E7 },
+{ 0xA3E8, 0xA3E8, 0xA3E8 },
+{ 0xA3E9, 0xA3E9, 0xA3E9 },
+{ 0xA3EA, 0xA3EA, 0xA3EA },
+{ 0xA3EB, 0xA3EB, 0xA3EB },
+{ 0xA3EC, 0xA3EC, 0xA3EC },
+{ 0xA3ED, 0xA3ED, 0xA3ED },
+{ 0xA3EE, 0xA3EE, 0xA3EE },
+{ 0xA3EF, 0xA3EF, 0xA3EF },
+{ 0xA3F0, 0xA3F0, 0xA3F0 },
+{ 0xA3F1, 0xA3F1, 0xA3F1 },
+{ 0xA3F2, 0xA3F2, 0xA3F2 },
+{ 0xA3F3, 0xA3F3, 0xA3F3 },
+{ 0xA3F4, 0xA3F4, 0xA3F4 },
+{ 0xA3F5, 0xA3F5, 0xA3F5 },
+{ 0xA3F6, 0xA3F6, 0xA3F6 },
+{ 0xA3F7, 0xA3F7, 0xA3F7 },
+{ 0xA3F8, 0xA3F8, 0xA3F8 },
+{ 0xA3F9, 0xA3F9, 0xA3F9 },
+{ 0xA3FA, 0xA3FA, 0xA3FA },
+{ 0xA3FB, 0xA3FB, 0xA3FB },
+{ 0xA3FC, 0xA3FC, 0xA3FC },
+{ 0xA3FD, 0xA3FD, 0xA3FD },
+{ 0xA3FE, 0xA3FE, 0xA3FE },
+{ 0xA3FF, 0xA3FF, 0xA3FF },
+{ 0xA400, 0xA400, 0xA400 },
+{ 0xA401, 0xA401, 0xA401 },
+{ 0xA402, 0xA402, 0xA402 },
+{ 0xA403, 0xA403, 0xA403 },
+{ 0xA404, 0xA404, 0xA404 },
+{ 0xA405, 0xA405, 0xA405 },
+{ 0xA406, 0xA406, 0xA406 },
+{ 0xA407, 0xA407, 0xA407 },
+{ 0xA408, 0xA408, 0xA408 },
+{ 0xA409, 0xA409, 0xA409 },
+{ 0xA40A, 0xA40A, 0xA40A },
+{ 0xA40B, 0xA40B, 0xA40B },
+{ 0xA40C, 0xA40C, 0xA40C },
+{ 0xA40D, 0xA40D, 0xA40D },
+{ 0xA40E, 0xA40E, 0xA40E },
+{ 0xA40F, 0xA40F, 0xA40F },
+{ 0xA410, 0xA410, 0xA410 },
+{ 0xA411, 0xA411, 0xA411 },
+{ 0xA412, 0xA412, 0xA412 },
+{ 0xA413, 0xA413, 0xA413 },
+{ 0xA414, 0xA414, 0xA414 },
+{ 0xA415, 0xA415, 0xA415 },
+{ 0xA416, 0xA416, 0xA416 },
+{ 0xA417, 0xA417, 0xA417 },
+{ 0xA418, 0xA418, 0xA418 },
+{ 0xA419, 0xA419, 0xA419 },
+{ 0xA41A, 0xA41A, 0xA41A },
+{ 0xA41B, 0xA41B, 0xA41B },
+{ 0xA41C, 0xA41C, 0xA41C },
+{ 0xA41D, 0xA41D, 0xA41D },
+{ 0xA41E, 0xA41E, 0xA41E },
+{ 0xA41F, 0xA41F, 0xA41F },
+{ 0xA420, 0xA420, 0xA420 },
+{ 0xA421, 0xA421, 0xA421 },
+{ 0xA422, 0xA422, 0xA422 },
+{ 0xA423, 0xA423, 0xA423 },
+{ 0xA424, 0xA424, 0xA424 },
+{ 0xA425, 0xA425, 0xA425 },
+{ 0xA426, 0xA426, 0xA426 },
+{ 0xA427, 0xA427, 0xA427 },
+{ 0xA428, 0xA428, 0xA428 },
+{ 0xA429, 0xA429, 0xA429 },
+{ 0xA42A, 0xA42A, 0xA42A },
+{ 0xA42B, 0xA42B, 0xA42B },
+{ 0xA42C, 0xA42C, 0xA42C },
+{ 0xA42D, 0xA42D, 0xA42D },
+{ 0xA42E, 0xA42E, 0xA42E },
+{ 0xA42F, 0xA42F, 0xA42F },
+{ 0xA430, 0xA430, 0xA430 },
+{ 0xA431, 0xA431, 0xA431 },
+{ 0xA432, 0xA432, 0xA432 },
+{ 0xA433, 0xA433, 0xA433 },
+{ 0xA434, 0xA434, 0xA434 },
+{ 0xA435, 0xA435, 0xA435 },
+{ 0xA436, 0xA436, 0xA436 },
+{ 0xA437, 0xA437, 0xA437 },
+{ 0xA438, 0xA438, 0xA438 },
+{ 0xA439, 0xA439, 0xA439 },
+{ 0xA43A, 0xA43A, 0xA43A },
+{ 0xA43B, 0xA43B, 0xA43B },
+{ 0xA43C, 0xA43C, 0xA43C },
+{ 0xA43D, 0xA43D, 0xA43D },
+{ 0xA43E, 0xA43E, 0xA43E },
+{ 0xA43F, 0xA43F, 0xA43F },
+{ 0xA440, 0xA440, 0xA440 },
+{ 0xA441, 0xA441, 0xA441 },
+{ 0xA442, 0xA442, 0xA442 },
+{ 0xA443, 0xA443, 0xA443 },
+{ 0xA444, 0xA444, 0xA444 },
+{ 0xA445, 0xA445, 0xA445 },
+{ 0xA446, 0xA446, 0xA446 },
+{ 0xA447, 0xA447, 0xA447 },
+{ 0xA448, 0xA448, 0xA448 },
+{ 0xA449, 0xA449, 0xA449 },
+{ 0xA44A, 0xA44A, 0xA44A },
+{ 0xA44B, 0xA44B, 0xA44B },
+{ 0xA44C, 0xA44C, 0xA44C },
+{ 0xA44D, 0xA44D, 0xA44D },
+{ 0xA44E, 0xA44E, 0xA44E },
+{ 0xA44F, 0xA44F, 0xA44F },
+{ 0xA450, 0xA450, 0xA450 },
+{ 0xA451, 0xA451, 0xA451 },
+{ 0xA452, 0xA452, 0xA452 },
+{ 0xA453, 0xA453, 0xA453 },
+{ 0xA454, 0xA454, 0xA454 },
+{ 0xA455, 0xA455, 0xA455 },
+{ 0xA456, 0xA456, 0xA456 },
+{ 0xA457, 0xA457, 0xA457 },
+{ 0xA458, 0xA458, 0xA458 },
+{ 0xA459, 0xA459, 0xA459 },
+{ 0xA45A, 0xA45A, 0xA45A },
+{ 0xA45B, 0xA45B, 0xA45B },
+{ 0xA45C, 0xA45C, 0xA45C },
+{ 0xA45D, 0xA45D, 0xA45D },
+{ 0xA45E, 0xA45E, 0xA45E },
+{ 0xA45F, 0xA45F, 0xA45F },
+{ 0xA460, 0xA460, 0xA460 },
+{ 0xA461, 0xA461, 0xA461 },
+{ 0xA462, 0xA462, 0xA462 },
+{ 0xA463, 0xA463, 0xA463 },
+{ 0xA464, 0xA464, 0xA464 },
+{ 0xA465, 0xA465, 0xA465 },
+{ 0xA466, 0xA466, 0xA466 },
+{ 0xA467, 0xA467, 0xA467 },
+{ 0xA468, 0xA468, 0xA468 },
+{ 0xA469, 0xA469, 0xA469 },
+{ 0xA46A, 0xA46A, 0xA46A },
+{ 0xA46B, 0xA46B, 0xA46B },
+{ 0xA46C, 0xA46C, 0xA46C },
+{ 0xA46D, 0xA46D, 0xA46D },
+{ 0xA46E, 0xA46E, 0xA46E },
+{ 0xA46F, 0xA46F, 0xA46F },
+{ 0xA470, 0xA470, 0xA470 },
+{ 0xA471, 0xA471, 0xA471 },
+{ 0xA472, 0xA472, 0xA472 },
+{ 0xA473, 0xA473, 0xA473 },
+{ 0xA474, 0xA474, 0xA474 },
+{ 0xA475, 0xA475, 0xA475 },
+{ 0xA476, 0xA476, 0xA476 },
+{ 0xA477, 0xA477, 0xA477 },
+{ 0xA478, 0xA478, 0xA478 },
+{ 0xA479, 0xA479, 0xA479 },
+{ 0xA47A, 0xA47A, 0xA47A },
+{ 0xA47B, 0xA47B, 0xA47B },
+{ 0xA47C, 0xA47C, 0xA47C },
+{ 0xA47D, 0xA47D, 0xA47D },
+{ 0xA47E, 0xA47E, 0xA47E },
+{ 0xA47F, 0xA47F, 0xA47F },
+{ 0xA480, 0xA480, 0xA480 },
+{ 0xA481, 0xA481, 0xA481 },
+{ 0xA482, 0xA482, 0xA482 },
+{ 0xA483, 0xA483, 0xA483 },
+{ 0xA484, 0xA484, 0xA484 },
+{ 0xA485, 0xA485, 0xA485 },
+{ 0xA486, 0xA486, 0xA486 },
+{ 0xA487, 0xA487, 0xA487 },
+{ 0xA488, 0xA488, 0xA488 },
+{ 0xA489, 0xA489, 0xA489 },
+{ 0xA48A, 0xA48A, 0xA48A },
+{ 0xA48B, 0xA48B, 0xA48B },
+{ 0xA48C, 0xA48C, 0xA48C },
+{ 0xA800, 0xA800, 0xA800 },
+{ 0xA801, 0xA801, 0xA801 },
+{ 0xA803, 0xA803, 0xA803 },
+{ 0xA804, 0xA804, 0xA804 },
+{ 0xA805, 0xA805, 0xA805 },
+{ 0xA806, 0xA806, 0xA806 },
+{ 0xA807, 0xA807, 0xA807 },
+{ 0xA808, 0xA808, 0xA808 },
+{ 0xA809, 0xA809, 0xA809 },
+{ 0xA80A, 0xA80A, 0xA80A },
+{ 0xA80B, 0xA80B, 0xA80B },
+{ 0xA80C, 0xA80C, 0xA80C },
+{ 0xA80D, 0xA80D, 0xA80D },
+{ 0xA80E, 0xA80E, 0xA80E },
+{ 0xA80F, 0xA80F, 0xA80F },
+{ 0xA810, 0xA810, 0xA810 },
+{ 0xA811, 0xA811, 0xA811 },
+{ 0xA812, 0xA812, 0xA812 },
+{ 0xA813, 0xA813, 0xA813 },
+{ 0xA814, 0xA814, 0xA814 },
+{ 0xA815, 0xA815, 0xA815 },
+{ 0xA816, 0xA816, 0xA816 },
+{ 0xA817, 0xA817, 0xA817 },
+{ 0xA818, 0xA818, 0xA818 },
+{ 0xA819, 0xA819, 0xA819 },
+{ 0xA81A, 0xA81A, 0xA81A },
+{ 0xA81B, 0xA81B, 0xA81B },
+{ 0xA81C, 0xA81C, 0xA81C },
+{ 0xA81D, 0xA81D, 0xA81D },
+{ 0xA81E, 0xA81E, 0xA81E },
+{ 0xA81F, 0xA81F, 0xA81F },
+{ 0xA820, 0xA820, 0xA820 },
+{ 0xA821, 0xA821, 0xA821 },
+{ 0xA822, 0xA822, 0xA822 },
+{ 0xA825, 0xA825, 0xA825 },
+{ 0xA826, 0xA826, 0xA826 },
+{ 0xAC00, 0xAC00, 0xAC00 },
+{ 0xAC01, 0xAC01, 0xAC01 },
+{ 0xAC02, 0xAC02, 0xAC02 },
+{ 0xAC03, 0xAC03, 0xAC03 },
+{ 0xAC04, 0xAC04, 0xAC04 },
+{ 0xAC05, 0xAC05, 0xAC05 },
+{ 0xAC06, 0xAC06, 0xAC06 },
+{ 0xAC07, 0xAC07, 0xAC07 },
+{ 0xAC08, 0xAC08, 0xAC08 },
+{ 0xAC09, 0xAC09, 0xAC09 },
+{ 0xAC0A, 0xAC0A, 0xAC0A },
+{ 0xAC0B, 0xAC0B, 0xAC0B },
+{ 0xAC0C, 0xAC0C, 0xAC0C },
+{ 0xAC0D, 0xAC0D, 0xAC0D },
+{ 0xAC0E, 0xAC0E, 0xAC0E },
+{ 0xAC0F, 0xAC0F, 0xAC0F },
+{ 0xAC10, 0xAC10, 0xAC10 },
+{ 0xAC11, 0xAC11, 0xAC11 },
+{ 0xAC12, 0xAC12, 0xAC12 },
+{ 0xAC13, 0xAC13, 0xAC13 },
+{ 0xAC14, 0xAC14, 0xAC14 },
+{ 0xAC15, 0xAC15, 0xAC15 },
+{ 0xAC16, 0xAC16, 0xAC16 },
+{ 0xAC17, 0xAC17, 0xAC17 },
+{ 0xAC18, 0xAC18, 0xAC18 },
+{ 0xAC19, 0xAC19, 0xAC19 },
+{ 0xAC1A, 0xAC1A, 0xAC1A },
+{ 0xAC1B, 0xAC1B, 0xAC1B },
+{ 0xAC1C, 0xAC1C, 0xAC1C },
+{ 0xAC1D, 0xAC1D, 0xAC1D },
+{ 0xAC1E, 0xAC1E, 0xAC1E },
+{ 0xAC1F, 0xAC1F, 0xAC1F },
+{ 0xAC20, 0xAC20, 0xAC20 },
+{ 0xAC21, 0xAC21, 0xAC21 },
+{ 0xAC22, 0xAC22, 0xAC22 },
+{ 0xAC23, 0xAC23, 0xAC23 },
+{ 0xAC24, 0xAC24, 0xAC24 },
+{ 0xAC25, 0xAC25, 0xAC25 },
+{ 0xAC26, 0xAC26, 0xAC26 },
+{ 0xAC27, 0xAC27, 0xAC27 },
+{ 0xAC28, 0xAC28, 0xAC28 },
+{ 0xAC29, 0xAC29, 0xAC29 },
+{ 0xAC2A, 0xAC2A, 0xAC2A },
+{ 0xAC2B, 0xAC2B, 0xAC2B },
+{ 0xAC2C, 0xAC2C, 0xAC2C },
+{ 0xAC2D, 0xAC2D, 0xAC2D },
+{ 0xAC2E, 0xAC2E, 0xAC2E },
+{ 0xAC2F, 0xAC2F, 0xAC2F },
+{ 0xAC30, 0xAC30, 0xAC30 },
+{ 0xAC31, 0xAC31, 0xAC31 },
+{ 0xAC32, 0xAC32, 0xAC32 },
+{ 0xAC33, 0xAC33, 0xAC33 },
+{ 0xAC34, 0xAC34, 0xAC34 },
+{ 0xAC35, 0xAC35, 0xAC35 },
+{ 0xAC36, 0xAC36, 0xAC36 },
+{ 0xAC37, 0xAC37, 0xAC37 },
+{ 0xAC38, 0xAC38, 0xAC38 },
+{ 0xAC39, 0xAC39, 0xAC39 },
+{ 0xAC3A, 0xAC3A, 0xAC3A },
+{ 0xAC3B, 0xAC3B, 0xAC3B },
+{ 0xAC3C, 0xAC3C, 0xAC3C },
+{ 0xAC3D, 0xAC3D, 0xAC3D },
+{ 0xAC3E, 0xAC3E, 0xAC3E },
+{ 0xAC3F, 0xAC3F, 0xAC3F },
+{ 0xAC40, 0xAC40, 0xAC40 },
+{ 0xAC41, 0xAC41, 0xAC41 },
+{ 0xAC42, 0xAC42, 0xAC42 },
+{ 0xAC43, 0xAC43, 0xAC43 },
+{ 0xAC44, 0xAC44, 0xAC44 },
+{ 0xAC45, 0xAC45, 0xAC45 },
+{ 0xAC46, 0xAC46, 0xAC46 },
+{ 0xAC47, 0xAC47, 0xAC47 },
+{ 0xAC48, 0xAC48, 0xAC48 },
+{ 0xAC49, 0xAC49, 0xAC49 },
+{ 0xAC4A, 0xAC4A, 0xAC4A },
+{ 0xAC4B, 0xAC4B, 0xAC4B },
+{ 0xAC4C, 0xAC4C, 0xAC4C },
+{ 0xAC4D, 0xAC4D, 0xAC4D },
+{ 0xAC4E, 0xAC4E, 0xAC4E },
+{ 0xAC4F, 0xAC4F, 0xAC4F },
+{ 0xAC50, 0xAC50, 0xAC50 },
+{ 0xAC51, 0xAC51, 0xAC51 },
+{ 0xAC52, 0xAC52, 0xAC52 },
+{ 0xAC53, 0xAC53, 0xAC53 },
+{ 0xAC54, 0xAC54, 0xAC54 },
+{ 0xAC55, 0xAC55, 0xAC55 },
+{ 0xAC56, 0xAC56, 0xAC56 },
+{ 0xAC57, 0xAC57, 0xAC57 },
+{ 0xAC58, 0xAC58, 0xAC58 },
+{ 0xAC59, 0xAC59, 0xAC59 },
+{ 0xAC5A, 0xAC5A, 0xAC5A },
+{ 0xAC5B, 0xAC5B, 0xAC5B },
+{ 0xAC5C, 0xAC5C, 0xAC5C },
+{ 0xAC5D, 0xAC5D, 0xAC5D },
+{ 0xAC5E, 0xAC5E, 0xAC5E },
+{ 0xAC5F, 0xAC5F, 0xAC5F },
+{ 0xAC60, 0xAC60, 0xAC60 },
+{ 0xAC61, 0xAC61, 0xAC61 },
+{ 0xAC62, 0xAC62, 0xAC62 },
+{ 0xAC63, 0xAC63, 0xAC63 },
+{ 0xAC64, 0xAC64, 0xAC64 },
+{ 0xAC65, 0xAC65, 0xAC65 },
+{ 0xAC66, 0xAC66, 0xAC66 },
+{ 0xAC67, 0xAC67, 0xAC67 },
+{ 0xAC68, 0xAC68, 0xAC68 },
+{ 0xAC69, 0xAC69, 0xAC69 },
+{ 0xAC6A, 0xAC6A, 0xAC6A },
+{ 0xAC6B, 0xAC6B, 0xAC6B },
+{ 0xAC6C, 0xAC6C, 0xAC6C },
+{ 0xAC6D, 0xAC6D, 0xAC6D },
+{ 0xAC6E, 0xAC6E, 0xAC6E },
+{ 0xAC6F, 0xAC6F, 0xAC6F },
+{ 0xAC70, 0xAC70, 0xAC70 },
+{ 0xAC71, 0xAC71, 0xAC71 },
+{ 0xAC72, 0xAC72, 0xAC72 },
+{ 0xAC73, 0xAC73, 0xAC73 },
+{ 0xAC74, 0xAC74, 0xAC74 },
+{ 0xAC75, 0xAC75, 0xAC75 },
+{ 0xAC76, 0xAC76, 0xAC76 },
+{ 0xAC77, 0xAC77, 0xAC77 },
+{ 0xAC78, 0xAC78, 0xAC78 },
+{ 0xAC79, 0xAC79, 0xAC79 },
+{ 0xAC7A, 0xAC7A, 0xAC7A },
+{ 0xAC7B, 0xAC7B, 0xAC7B },
+{ 0xAC7C, 0xAC7C, 0xAC7C },
+{ 0xAC7D, 0xAC7D, 0xAC7D },
+{ 0xAC7E, 0xAC7E, 0xAC7E },
+{ 0xAC7F, 0xAC7F, 0xAC7F },
+{ 0xAC80, 0xAC80, 0xAC80 },
+{ 0xAC81, 0xAC81, 0xAC81 },
+{ 0xAC82, 0xAC82, 0xAC82 },
+{ 0xAC83, 0xAC83, 0xAC83 },
+{ 0xAC84, 0xAC84, 0xAC84 },
+{ 0xAC85, 0xAC85, 0xAC85 },
+{ 0xAC86, 0xAC86, 0xAC86 },
+{ 0xAC87, 0xAC87, 0xAC87 },
+{ 0xAC88, 0xAC88, 0xAC88 },
+{ 0xAC89, 0xAC89, 0xAC89 },
+{ 0xAC8A, 0xAC8A, 0xAC8A },
+{ 0xAC8B, 0xAC8B, 0xAC8B },
+{ 0xAC8C, 0xAC8C, 0xAC8C },
+{ 0xAC8D, 0xAC8D, 0xAC8D },
+{ 0xAC8E, 0xAC8E, 0xAC8E },
+{ 0xAC8F, 0xAC8F, 0xAC8F },
+{ 0xAC90, 0xAC90, 0xAC90 },
+{ 0xAC91, 0xAC91, 0xAC91 },
+{ 0xAC92, 0xAC92, 0xAC92 },
+{ 0xAC93, 0xAC93, 0xAC93 },
+{ 0xAC94, 0xAC94, 0xAC94 },
+{ 0xAC95, 0xAC95, 0xAC95 },
+{ 0xAC96, 0xAC96, 0xAC96 },
+{ 0xAC97, 0xAC97, 0xAC97 },
+{ 0xAC98, 0xAC98, 0xAC98 },
+{ 0xAC99, 0xAC99, 0xAC99 },
+{ 0xAC9A, 0xAC9A, 0xAC9A },
+{ 0xAC9B, 0xAC9B, 0xAC9B },
+{ 0xAC9C, 0xAC9C, 0xAC9C },
+{ 0xAC9D, 0xAC9D, 0xAC9D },
+{ 0xAC9E, 0xAC9E, 0xAC9E },
+{ 0xAC9F, 0xAC9F, 0xAC9F },
+{ 0xACA0, 0xACA0, 0xACA0 },
+{ 0xACA1, 0xACA1, 0xACA1 },
+{ 0xACA2, 0xACA2, 0xACA2 },
+{ 0xACA3, 0xACA3, 0xACA3 },
+{ 0xACA4, 0xACA4, 0xACA4 },
+{ 0xACA5, 0xACA5, 0xACA5 },
+{ 0xACA6, 0xACA6, 0xACA6 },
+{ 0xACA7, 0xACA7, 0xACA7 },
+{ 0xACA8, 0xACA8, 0xACA8 },
+{ 0xACA9, 0xACA9, 0xACA9 },
+{ 0xACAA, 0xACAA, 0xACAA },
+{ 0xACAB, 0xACAB, 0xACAB },
+{ 0xACAC, 0xACAC, 0xACAC },
+{ 0xACAD, 0xACAD, 0xACAD },
+{ 0xACAE, 0xACAE, 0xACAE },
+{ 0xACAF, 0xACAF, 0xACAF },
+{ 0xACB0, 0xACB0, 0xACB0 },
+{ 0xACB1, 0xACB1, 0xACB1 },
+{ 0xACB2, 0xACB2, 0xACB2 },
+{ 0xACB3, 0xACB3, 0xACB3 },
+{ 0xACB4, 0xACB4, 0xACB4 },
+{ 0xACB5, 0xACB5, 0xACB5 },
+{ 0xACB6, 0xACB6, 0xACB6 },
+{ 0xACB7, 0xACB7, 0xACB7 },
+{ 0xACB8, 0xACB8, 0xACB8 },
+{ 0xACB9, 0xACB9, 0xACB9 },
+{ 0xACBA, 0xACBA, 0xACBA },
+{ 0xACBB, 0xACBB, 0xACBB },
+{ 0xACBC, 0xACBC, 0xACBC },
+{ 0xACBD, 0xACBD, 0xACBD },
+{ 0xACBE, 0xACBE, 0xACBE },
+{ 0xACBF, 0xACBF, 0xACBF },
+{ 0xACC0, 0xACC0, 0xACC0 },
+{ 0xACC1, 0xACC1, 0xACC1 },
+{ 0xACC2, 0xACC2, 0xACC2 },
+{ 0xACC3, 0xACC3, 0xACC3 },
+{ 0xACC4, 0xACC4, 0xACC4 },
+{ 0xACC5, 0xACC5, 0xACC5 },
+{ 0xACC6, 0xACC6, 0xACC6 },
+{ 0xACC7, 0xACC7, 0xACC7 },
+{ 0xACC8, 0xACC8, 0xACC8 },
+{ 0xACC9, 0xACC9, 0xACC9 },
+{ 0xACCA, 0xACCA, 0xACCA },
+{ 0xACCB, 0xACCB, 0xACCB },
+{ 0xACCC, 0xACCC, 0xACCC },
+{ 0xACCD, 0xACCD, 0xACCD },
+{ 0xACCE, 0xACCE, 0xACCE },
+{ 0xACCF, 0xACCF, 0xACCF },
+{ 0xACD0, 0xACD0, 0xACD0 },
+{ 0xACD1, 0xACD1, 0xACD1 },
+{ 0xACD2, 0xACD2, 0xACD2 },
+{ 0xACD3, 0xACD3, 0xACD3 },
+{ 0xACD4, 0xACD4, 0xACD4 },
+{ 0xACD5, 0xACD5, 0xACD5 },
+{ 0xACD6, 0xACD6, 0xACD6 },
+{ 0xACD7, 0xACD7, 0xACD7 },
+{ 0xACD8, 0xACD8, 0xACD8 },
+{ 0xACD9, 0xACD9, 0xACD9 },
+{ 0xACDA, 0xACDA, 0xACDA },
+{ 0xACDB, 0xACDB, 0xACDB },
+{ 0xACDC, 0xACDC, 0xACDC },
+{ 0xACDD, 0xACDD, 0xACDD },
+{ 0xACDE, 0xACDE, 0xACDE },
+{ 0xACDF, 0xACDF, 0xACDF },
+{ 0xACE0, 0xACE0, 0xACE0 },
+{ 0xACE1, 0xACE1, 0xACE1 },
+{ 0xACE2, 0xACE2, 0xACE2 },
+{ 0xACE3, 0xACE3, 0xACE3 },
+{ 0xACE4, 0xACE4, 0xACE4 },
+{ 0xACE5, 0xACE5, 0xACE5 },
+{ 0xACE6, 0xACE6, 0xACE6 },
+{ 0xACE7, 0xACE7, 0xACE7 },
+{ 0xACE8, 0xACE8, 0xACE8 },
+{ 0xACE9, 0xACE9, 0xACE9 },
+{ 0xACEA, 0xACEA, 0xACEA },
+{ 0xACEB, 0xACEB, 0xACEB },
+{ 0xACEC, 0xACEC, 0xACEC },
+{ 0xACED, 0xACED, 0xACED },
+{ 0xACEE, 0xACEE, 0xACEE },
+{ 0xACEF, 0xACEF, 0xACEF },
+{ 0xACF0, 0xACF0, 0xACF0 },
+{ 0xACF1, 0xACF1, 0xACF1 },
+{ 0xACF2, 0xACF2, 0xACF2 },
+{ 0xACF3, 0xACF3, 0xACF3 },
+{ 0xACF4, 0xACF4, 0xACF4 },
+{ 0xACF5, 0xACF5, 0xACF5 },
+{ 0xACF6, 0xACF6, 0xACF6 },
+{ 0xACF7, 0xACF7, 0xACF7 },
+{ 0xACF8, 0xACF8, 0xACF8 },
+{ 0xACF9, 0xACF9, 0xACF9 },
+{ 0xACFA, 0xACFA, 0xACFA },
+{ 0xACFB, 0xACFB, 0xACFB },
+{ 0xACFC, 0xACFC, 0xACFC },
+{ 0xACFD, 0xACFD, 0xACFD },
+{ 0xACFE, 0xACFE, 0xACFE },
+{ 0xACFF, 0xACFF, 0xACFF },
+{ 0xAD00, 0xAD00, 0xAD00 },
+{ 0xAD01, 0xAD01, 0xAD01 },
+{ 0xAD02, 0xAD02, 0xAD02 },
+{ 0xAD03, 0xAD03, 0xAD03 },
+{ 0xAD04, 0xAD04, 0xAD04 },
+{ 0xAD05, 0xAD05, 0xAD05 },
+{ 0xAD06, 0xAD06, 0xAD06 },
+{ 0xAD07, 0xAD07, 0xAD07 },
+{ 0xAD08, 0xAD08, 0xAD08 },
+{ 0xAD09, 0xAD09, 0xAD09 },
+{ 0xAD0A, 0xAD0A, 0xAD0A },
+{ 0xAD0B, 0xAD0B, 0xAD0B },
+{ 0xAD0C, 0xAD0C, 0xAD0C },
+{ 0xAD0D, 0xAD0D, 0xAD0D },
+{ 0xAD0E, 0xAD0E, 0xAD0E },
+{ 0xAD0F, 0xAD0F, 0xAD0F },
+{ 0xAD10, 0xAD10, 0xAD10 },
+{ 0xAD11, 0xAD11, 0xAD11 },
+{ 0xAD12, 0xAD12, 0xAD12 },
+{ 0xAD13, 0xAD13, 0xAD13 },
+{ 0xAD14, 0xAD14, 0xAD14 },
+{ 0xAD15, 0xAD15, 0xAD15 },
+{ 0xAD16, 0xAD16, 0xAD16 },
+{ 0xAD17, 0xAD17, 0xAD17 },
+{ 0xAD18, 0xAD18, 0xAD18 },
+{ 0xAD19, 0xAD19, 0xAD19 },
+{ 0xAD1A, 0xAD1A, 0xAD1A },
+{ 0xAD1B, 0xAD1B, 0xAD1B },
+{ 0xAD1C, 0xAD1C, 0xAD1C },
+{ 0xAD1D, 0xAD1D, 0xAD1D },
+{ 0xAD1E, 0xAD1E, 0xAD1E },
+{ 0xAD1F, 0xAD1F, 0xAD1F },
+{ 0xAD20, 0xAD20, 0xAD20 },
+{ 0xAD21, 0xAD21, 0xAD21 },
+{ 0xAD22, 0xAD22, 0xAD22 },
+{ 0xAD23, 0xAD23, 0xAD23 },
+{ 0xAD24, 0xAD24, 0xAD24 },
+{ 0xAD25, 0xAD25, 0xAD25 },
+{ 0xAD26, 0xAD26, 0xAD26 },
+{ 0xAD27, 0xAD27, 0xAD27 },
+{ 0xAD28, 0xAD28, 0xAD28 },
+{ 0xAD29, 0xAD29, 0xAD29 },
+{ 0xAD2A, 0xAD2A, 0xAD2A },
+{ 0xAD2B, 0xAD2B, 0xAD2B },
+{ 0xAD2C, 0xAD2C, 0xAD2C },
+{ 0xAD2D, 0xAD2D, 0xAD2D },
+{ 0xAD2E, 0xAD2E, 0xAD2E },
+{ 0xAD2F, 0xAD2F, 0xAD2F },
+{ 0xAD30, 0xAD30, 0xAD30 },
+{ 0xAD31, 0xAD31, 0xAD31 },
+{ 0xAD32, 0xAD32, 0xAD32 },
+{ 0xAD33, 0xAD33, 0xAD33 },
+{ 0xAD34, 0xAD34, 0xAD34 },
+{ 0xAD35, 0xAD35, 0xAD35 },
+{ 0xAD36, 0xAD36, 0xAD36 },
+{ 0xAD37, 0xAD37, 0xAD37 },
+{ 0xAD38, 0xAD38, 0xAD38 },
+{ 0xAD39, 0xAD39, 0xAD39 },
+{ 0xAD3A, 0xAD3A, 0xAD3A },
+{ 0xAD3B, 0xAD3B, 0xAD3B },
+{ 0xAD3C, 0xAD3C, 0xAD3C },
+{ 0xAD3D, 0xAD3D, 0xAD3D },
+{ 0xAD3E, 0xAD3E, 0xAD3E },
+{ 0xAD3F, 0xAD3F, 0xAD3F },
+{ 0xAD40, 0xAD40, 0xAD40 },
+{ 0xAD41, 0xAD41, 0xAD41 },
+{ 0xAD42, 0xAD42, 0xAD42 },
+{ 0xAD43, 0xAD43, 0xAD43 },
+{ 0xAD44, 0xAD44, 0xAD44 },
+{ 0xAD45, 0xAD45, 0xAD45 },
+{ 0xAD46, 0xAD46, 0xAD46 },
+{ 0xAD47, 0xAD47, 0xAD47 },
+{ 0xAD48, 0xAD48, 0xAD48 },
+{ 0xAD49, 0xAD49, 0xAD49 },
+{ 0xAD4A, 0xAD4A, 0xAD4A },
+{ 0xAD4B, 0xAD4B, 0xAD4B },
+{ 0xAD4C, 0xAD4C, 0xAD4C },
+{ 0xAD4D, 0xAD4D, 0xAD4D },
+{ 0xAD4E, 0xAD4E, 0xAD4E },
+{ 0xAD4F, 0xAD4F, 0xAD4F },
+{ 0xAD50, 0xAD50, 0xAD50 },
+{ 0xAD51, 0xAD51, 0xAD51 },
+{ 0xAD52, 0xAD52, 0xAD52 },
+{ 0xAD53, 0xAD53, 0xAD53 },
+{ 0xAD54, 0xAD54, 0xAD54 },
+{ 0xAD55, 0xAD55, 0xAD55 },
+{ 0xAD56, 0xAD56, 0xAD56 },
+{ 0xAD57, 0xAD57, 0xAD57 },
+{ 0xAD58, 0xAD58, 0xAD58 },
+{ 0xAD59, 0xAD59, 0xAD59 },
+{ 0xAD5A, 0xAD5A, 0xAD5A },
+{ 0xAD5B, 0xAD5B, 0xAD5B },
+{ 0xAD5C, 0xAD5C, 0xAD5C },
+{ 0xAD5D, 0xAD5D, 0xAD5D },
+{ 0xAD5E, 0xAD5E, 0xAD5E },
+{ 0xAD5F, 0xAD5F, 0xAD5F },
+{ 0xAD60, 0xAD60, 0xAD60 },
+{ 0xAD61, 0xAD61, 0xAD61 },
+{ 0xAD62, 0xAD62, 0xAD62 },
+{ 0xAD63, 0xAD63, 0xAD63 },
+{ 0xAD64, 0xAD64, 0xAD64 },
+{ 0xAD65, 0xAD65, 0xAD65 },
+{ 0xAD66, 0xAD66, 0xAD66 },
+{ 0xAD67, 0xAD67, 0xAD67 },
+{ 0xAD68, 0xAD68, 0xAD68 },
+{ 0xAD69, 0xAD69, 0xAD69 },
+{ 0xAD6A, 0xAD6A, 0xAD6A },
+{ 0xAD6B, 0xAD6B, 0xAD6B },
+{ 0xAD6C, 0xAD6C, 0xAD6C },
+{ 0xAD6D, 0xAD6D, 0xAD6D },
+{ 0xAD6E, 0xAD6E, 0xAD6E },
+{ 0xAD6F, 0xAD6F, 0xAD6F },
+{ 0xAD70, 0xAD70, 0xAD70 },
+{ 0xAD71, 0xAD71, 0xAD71 },
+{ 0xAD72, 0xAD72, 0xAD72 },
+{ 0xAD73, 0xAD73, 0xAD73 },
+{ 0xAD74, 0xAD74, 0xAD74 },
+{ 0xAD75, 0xAD75, 0xAD75 },
+{ 0xAD76, 0xAD76, 0xAD76 },
+{ 0xAD77, 0xAD77, 0xAD77 },
+{ 0xAD78, 0xAD78, 0xAD78 },
+{ 0xAD79, 0xAD79, 0xAD79 },
+{ 0xAD7A, 0xAD7A, 0xAD7A },
+{ 0xAD7B, 0xAD7B, 0xAD7B },
+{ 0xAD7C, 0xAD7C, 0xAD7C },
+{ 0xAD7D, 0xAD7D, 0xAD7D },
+{ 0xAD7E, 0xAD7E, 0xAD7E },
+{ 0xAD7F, 0xAD7F, 0xAD7F },
+{ 0xAD80, 0xAD80, 0xAD80 },
+{ 0xAD81, 0xAD81, 0xAD81 },
+{ 0xAD82, 0xAD82, 0xAD82 },
+{ 0xAD83, 0xAD83, 0xAD83 },
+{ 0xAD84, 0xAD84, 0xAD84 },
+{ 0xAD85, 0xAD85, 0xAD85 },
+{ 0xAD86, 0xAD86, 0xAD86 },
+{ 0xAD87, 0xAD87, 0xAD87 },
+{ 0xAD88, 0xAD88, 0xAD88 },
+{ 0xAD89, 0xAD89, 0xAD89 },
+{ 0xAD8A, 0xAD8A, 0xAD8A },
+{ 0xAD8B, 0xAD8B, 0xAD8B },
+{ 0xAD8C, 0xAD8C, 0xAD8C },
+{ 0xAD8D, 0xAD8D, 0xAD8D },
+{ 0xAD8E, 0xAD8E, 0xAD8E },
+{ 0xAD8F, 0xAD8F, 0xAD8F },
+{ 0xAD90, 0xAD90, 0xAD90 },
+{ 0xAD91, 0xAD91, 0xAD91 },
+{ 0xAD92, 0xAD92, 0xAD92 },
+{ 0xAD93, 0xAD93, 0xAD93 },
+{ 0xAD94, 0xAD94, 0xAD94 },
+{ 0xAD95, 0xAD95, 0xAD95 },
+{ 0xAD96, 0xAD96, 0xAD96 },
+{ 0xAD97, 0xAD97, 0xAD97 },
+{ 0xAD98, 0xAD98, 0xAD98 },
+{ 0xAD99, 0xAD99, 0xAD99 },
+{ 0xAD9A, 0xAD9A, 0xAD9A },
+{ 0xAD9B, 0xAD9B, 0xAD9B },
+{ 0xAD9C, 0xAD9C, 0xAD9C },
+{ 0xAD9D, 0xAD9D, 0xAD9D },
+{ 0xAD9E, 0xAD9E, 0xAD9E },
+{ 0xAD9F, 0xAD9F, 0xAD9F },
+{ 0xADA0, 0xADA0, 0xADA0 },
+{ 0xADA1, 0xADA1, 0xADA1 },
+{ 0xADA2, 0xADA2, 0xADA2 },
+{ 0xADA3, 0xADA3, 0xADA3 },
+{ 0xADA4, 0xADA4, 0xADA4 },
+{ 0xADA5, 0xADA5, 0xADA5 },
+{ 0xADA6, 0xADA6, 0xADA6 },
+{ 0xADA7, 0xADA7, 0xADA7 },
+{ 0xADA8, 0xADA8, 0xADA8 },
+{ 0xADA9, 0xADA9, 0xADA9 },
+{ 0xADAA, 0xADAA, 0xADAA },
+{ 0xADAB, 0xADAB, 0xADAB },
+{ 0xADAC, 0xADAC, 0xADAC },
+{ 0xADAD, 0xADAD, 0xADAD },
+{ 0xADAE, 0xADAE, 0xADAE },
+{ 0xADAF, 0xADAF, 0xADAF },
+{ 0xADB0, 0xADB0, 0xADB0 },
+{ 0xADB1, 0xADB1, 0xADB1 },
+{ 0xADB2, 0xADB2, 0xADB2 },
+{ 0xADB3, 0xADB3, 0xADB3 },
+{ 0xADB4, 0xADB4, 0xADB4 },
+{ 0xADB5, 0xADB5, 0xADB5 },
+{ 0xADB6, 0xADB6, 0xADB6 },
+{ 0xADB7, 0xADB7, 0xADB7 },
+{ 0xADB8, 0xADB8, 0xADB8 },
+{ 0xADB9, 0xADB9, 0xADB9 },
+{ 0xADBA, 0xADBA, 0xADBA },
+{ 0xADBB, 0xADBB, 0xADBB },
+{ 0xADBC, 0xADBC, 0xADBC },
+{ 0xADBD, 0xADBD, 0xADBD },
+{ 0xADBE, 0xADBE, 0xADBE },
+{ 0xADBF, 0xADBF, 0xADBF },
+{ 0xADC0, 0xADC0, 0xADC0 },
+{ 0xADC1, 0xADC1, 0xADC1 },
+{ 0xADC2, 0xADC2, 0xADC2 },
+{ 0xADC3, 0xADC3, 0xADC3 },
+{ 0xADC4, 0xADC4, 0xADC4 },
+{ 0xADC5, 0xADC5, 0xADC5 },
+{ 0xADC6, 0xADC6, 0xADC6 },
+{ 0xADC7, 0xADC7, 0xADC7 },
+{ 0xADC8, 0xADC8, 0xADC8 },
+{ 0xADC9, 0xADC9, 0xADC9 },
+{ 0xADCA, 0xADCA, 0xADCA },
+{ 0xADCB, 0xADCB, 0xADCB },
+{ 0xADCC, 0xADCC, 0xADCC },
+{ 0xADCD, 0xADCD, 0xADCD },
+{ 0xADCE, 0xADCE, 0xADCE },
+{ 0xADCF, 0xADCF, 0xADCF },
+{ 0xADD0, 0xADD0, 0xADD0 },
+{ 0xADD1, 0xADD1, 0xADD1 },
+{ 0xADD2, 0xADD2, 0xADD2 },
+{ 0xADD3, 0xADD3, 0xADD3 },
+{ 0xADD4, 0xADD4, 0xADD4 },
+{ 0xADD5, 0xADD5, 0xADD5 },
+{ 0xADD6, 0xADD6, 0xADD6 },
+{ 0xADD7, 0xADD7, 0xADD7 },
+{ 0xADD8, 0xADD8, 0xADD8 },
+{ 0xADD9, 0xADD9, 0xADD9 },
+{ 0xADDA, 0xADDA, 0xADDA },
+{ 0xADDB, 0xADDB, 0xADDB },
+{ 0xADDC, 0xADDC, 0xADDC },
+{ 0xADDD, 0xADDD, 0xADDD },
+{ 0xADDE, 0xADDE, 0xADDE },
+{ 0xADDF, 0xADDF, 0xADDF },
+{ 0xADE0, 0xADE0, 0xADE0 },
+{ 0xADE1, 0xADE1, 0xADE1 },
+{ 0xADE2, 0xADE2, 0xADE2 },
+{ 0xADE3, 0xADE3, 0xADE3 },
+{ 0xADE4, 0xADE4, 0xADE4 },
+{ 0xADE5, 0xADE5, 0xADE5 },
+{ 0xADE6, 0xADE6, 0xADE6 },
+{ 0xADE7, 0xADE7, 0xADE7 },
+{ 0xADE8, 0xADE8, 0xADE8 },
+{ 0xADE9, 0xADE9, 0xADE9 },
+{ 0xADEA, 0xADEA, 0xADEA },
+{ 0xADEB, 0xADEB, 0xADEB },
+{ 0xADEC, 0xADEC, 0xADEC },
+{ 0xADED, 0xADED, 0xADED },
+{ 0xADEE, 0xADEE, 0xADEE },
+{ 0xADEF, 0xADEF, 0xADEF },
+{ 0xADF0, 0xADF0, 0xADF0 },
+{ 0xADF1, 0xADF1, 0xADF1 },
+{ 0xADF2, 0xADF2, 0xADF2 },
+{ 0xADF3, 0xADF3, 0xADF3 },
+{ 0xADF4, 0xADF4, 0xADF4 },
+{ 0xADF5, 0xADF5, 0xADF5 },
+{ 0xADF6, 0xADF6, 0xADF6 },
+{ 0xADF7, 0xADF7, 0xADF7 },
+{ 0xADF8, 0xADF8, 0xADF8 },
+{ 0xADF9, 0xADF9, 0xADF9 },
+{ 0xADFA, 0xADFA, 0xADFA },
+{ 0xADFB, 0xADFB, 0xADFB },
+{ 0xADFC, 0xADFC, 0xADFC },
+{ 0xADFD, 0xADFD, 0xADFD },
+{ 0xADFE, 0xADFE, 0xADFE },
+{ 0xADFF, 0xADFF, 0xADFF },
+{ 0xAE00, 0xAE00, 0xAE00 },
+{ 0xAE01, 0xAE01, 0xAE01 },
+{ 0xAE02, 0xAE02, 0xAE02 },
+{ 0xAE03, 0xAE03, 0xAE03 },
+{ 0xAE04, 0xAE04, 0xAE04 },
+{ 0xAE05, 0xAE05, 0xAE05 },
+{ 0xAE06, 0xAE06, 0xAE06 },
+{ 0xAE07, 0xAE07, 0xAE07 },
+{ 0xAE08, 0xAE08, 0xAE08 },
+{ 0xAE09, 0xAE09, 0xAE09 },
+{ 0xAE0A, 0xAE0A, 0xAE0A },
+{ 0xAE0B, 0xAE0B, 0xAE0B },
+{ 0xAE0C, 0xAE0C, 0xAE0C },
+{ 0xAE0D, 0xAE0D, 0xAE0D },
+{ 0xAE0E, 0xAE0E, 0xAE0E },
+{ 0xAE0F, 0xAE0F, 0xAE0F },
+{ 0xAE10, 0xAE10, 0xAE10 },
+{ 0xAE11, 0xAE11, 0xAE11 },
+{ 0xAE12, 0xAE12, 0xAE12 },
+{ 0xAE13, 0xAE13, 0xAE13 },
+{ 0xAE14, 0xAE14, 0xAE14 },
+{ 0xAE15, 0xAE15, 0xAE15 },
+{ 0xAE16, 0xAE16, 0xAE16 },
+{ 0xAE17, 0xAE17, 0xAE17 },
+{ 0xAE18, 0xAE18, 0xAE18 },
+{ 0xAE19, 0xAE19, 0xAE19 },
+{ 0xAE1A, 0xAE1A, 0xAE1A },
+{ 0xAE1B, 0xAE1B, 0xAE1B },
+{ 0xAE1C, 0xAE1C, 0xAE1C },
+{ 0xAE1D, 0xAE1D, 0xAE1D },
+{ 0xAE1E, 0xAE1E, 0xAE1E },
+{ 0xAE1F, 0xAE1F, 0xAE1F },
+{ 0xAE20, 0xAE20, 0xAE20 },
+{ 0xAE21, 0xAE21, 0xAE21 },
+{ 0xAE22, 0xAE22, 0xAE22 },
+{ 0xAE23, 0xAE23, 0xAE23 },
+{ 0xAE24, 0xAE24, 0xAE24 },
+{ 0xAE25, 0xAE25, 0xAE25 },
+{ 0xAE26, 0xAE26, 0xAE26 },
+{ 0xAE27, 0xAE27, 0xAE27 },
+{ 0xAE28, 0xAE28, 0xAE28 },
+{ 0xAE29, 0xAE29, 0xAE29 },
+{ 0xAE2A, 0xAE2A, 0xAE2A },
+{ 0xAE2B, 0xAE2B, 0xAE2B },
+{ 0xAE2C, 0xAE2C, 0xAE2C },
+{ 0xAE2D, 0xAE2D, 0xAE2D },
+{ 0xAE2E, 0xAE2E, 0xAE2E },
+{ 0xAE2F, 0xAE2F, 0xAE2F },
+{ 0xAE30, 0xAE30, 0xAE30 },
+{ 0xAE31, 0xAE31, 0xAE31 },
+{ 0xAE32, 0xAE32, 0xAE32 },
+{ 0xAE33, 0xAE33, 0xAE33 },
+{ 0xAE34, 0xAE34, 0xAE34 },
+{ 0xAE35, 0xAE35, 0xAE35 },
+{ 0xAE36, 0xAE36, 0xAE36 },
+{ 0xAE37, 0xAE37, 0xAE37 },
+{ 0xAE38, 0xAE38, 0xAE38 },
+{ 0xAE39, 0xAE39, 0xAE39 },
+{ 0xAE3A, 0xAE3A, 0xAE3A },
+{ 0xAE3B, 0xAE3B, 0xAE3B },
+{ 0xAE3C, 0xAE3C, 0xAE3C },
+{ 0xAE3D, 0xAE3D, 0xAE3D },
+{ 0xAE3E, 0xAE3E, 0xAE3E },
+{ 0xAE3F, 0xAE3F, 0xAE3F },
+{ 0xAE40, 0xAE40, 0xAE40 },
+{ 0xAE41, 0xAE41, 0xAE41 },
+{ 0xAE42, 0xAE42, 0xAE42 },
+{ 0xAE43, 0xAE43, 0xAE43 },
+{ 0xAE44, 0xAE44, 0xAE44 },
+{ 0xAE45, 0xAE45, 0xAE45 },
+{ 0xAE46, 0xAE46, 0xAE46 },
+{ 0xAE47, 0xAE47, 0xAE47 },
+{ 0xAE48, 0xAE48, 0xAE48 },
+{ 0xAE49, 0xAE49, 0xAE49 },
+{ 0xAE4A, 0xAE4A, 0xAE4A },
+{ 0xAE4B, 0xAE4B, 0xAE4B },
+{ 0xAE4C, 0xAE4C, 0xAE4C },
+{ 0xAE4D, 0xAE4D, 0xAE4D },
+{ 0xAE4E, 0xAE4E, 0xAE4E },
+{ 0xAE4F, 0xAE4F, 0xAE4F },
+{ 0xAE50, 0xAE50, 0xAE50 },
+{ 0xAE51, 0xAE51, 0xAE51 },
+{ 0xAE52, 0xAE52, 0xAE52 },
+{ 0xAE53, 0xAE53, 0xAE53 },
+{ 0xAE54, 0xAE54, 0xAE54 },
+{ 0xAE55, 0xAE55, 0xAE55 },
+{ 0xAE56, 0xAE56, 0xAE56 },
+{ 0xAE57, 0xAE57, 0xAE57 },
+{ 0xAE58, 0xAE58, 0xAE58 },
+{ 0xAE59, 0xAE59, 0xAE59 },
+{ 0xAE5A, 0xAE5A, 0xAE5A },
+{ 0xAE5B, 0xAE5B, 0xAE5B },
+{ 0xAE5C, 0xAE5C, 0xAE5C },
+{ 0xAE5D, 0xAE5D, 0xAE5D },
+{ 0xAE5E, 0xAE5E, 0xAE5E },
+{ 0xAE5F, 0xAE5F, 0xAE5F },
+{ 0xAE60, 0xAE60, 0xAE60 },
+{ 0xAE61, 0xAE61, 0xAE61 },
+{ 0xAE62, 0xAE62, 0xAE62 },
+{ 0xAE63, 0xAE63, 0xAE63 },
+{ 0xAE64, 0xAE64, 0xAE64 },
+{ 0xAE65, 0xAE65, 0xAE65 },
+{ 0xAE66, 0xAE66, 0xAE66 },
+{ 0xAE67, 0xAE67, 0xAE67 },
+{ 0xAE68, 0xAE68, 0xAE68 },
+{ 0xAE69, 0xAE69, 0xAE69 },
+{ 0xAE6A, 0xAE6A, 0xAE6A },
+{ 0xAE6B, 0xAE6B, 0xAE6B },
+{ 0xAE6C, 0xAE6C, 0xAE6C },
+{ 0xAE6D, 0xAE6D, 0xAE6D },
+{ 0xAE6E, 0xAE6E, 0xAE6E },
+{ 0xAE6F, 0xAE6F, 0xAE6F },
+{ 0xAE70, 0xAE70, 0xAE70 },
+{ 0xAE71, 0xAE71, 0xAE71 },
+{ 0xAE72, 0xAE72, 0xAE72 },
+{ 0xAE73, 0xAE73, 0xAE73 },
+{ 0xAE74, 0xAE74, 0xAE74 },
+{ 0xAE75, 0xAE75, 0xAE75 },
+{ 0xAE76, 0xAE76, 0xAE76 },
+{ 0xAE77, 0xAE77, 0xAE77 },
+{ 0xAE78, 0xAE78, 0xAE78 },
+{ 0xAE79, 0xAE79, 0xAE79 },
+{ 0xAE7A, 0xAE7A, 0xAE7A },
+{ 0xAE7B, 0xAE7B, 0xAE7B },
+{ 0xAE7C, 0xAE7C, 0xAE7C },
+{ 0xAE7D, 0xAE7D, 0xAE7D },
+{ 0xAE7E, 0xAE7E, 0xAE7E },
+{ 0xAE7F, 0xAE7F, 0xAE7F },
+{ 0xAE80, 0xAE80, 0xAE80 },
+{ 0xAE81, 0xAE81, 0xAE81 },
+{ 0xAE82, 0xAE82, 0xAE82 },
+{ 0xAE83, 0xAE83, 0xAE83 },
+{ 0xAE84, 0xAE84, 0xAE84 },
+{ 0xAE85, 0xAE85, 0xAE85 },
+{ 0xAE86, 0xAE86, 0xAE86 },
+{ 0xAE87, 0xAE87, 0xAE87 },
+{ 0xAE88, 0xAE88, 0xAE88 },
+{ 0xAE89, 0xAE89, 0xAE89 },
+{ 0xAE8A, 0xAE8A, 0xAE8A },
+{ 0xAE8B, 0xAE8B, 0xAE8B },
+{ 0xAE8C, 0xAE8C, 0xAE8C },
+{ 0xAE8D, 0xAE8D, 0xAE8D },
+{ 0xAE8E, 0xAE8E, 0xAE8E },
+{ 0xAE8F, 0xAE8F, 0xAE8F },
+{ 0xAE90, 0xAE90, 0xAE90 },
+{ 0xAE91, 0xAE91, 0xAE91 },
+{ 0xAE92, 0xAE92, 0xAE92 },
+{ 0xAE93, 0xAE93, 0xAE93 },
+{ 0xAE94, 0xAE94, 0xAE94 },
+{ 0xAE95, 0xAE95, 0xAE95 },
+{ 0xAE96, 0xAE96, 0xAE96 },
+{ 0xAE97, 0xAE97, 0xAE97 },
+{ 0xAE98, 0xAE98, 0xAE98 },
+{ 0xAE99, 0xAE99, 0xAE99 },
+{ 0xAE9A, 0xAE9A, 0xAE9A },
+{ 0xAE9B, 0xAE9B, 0xAE9B },
+{ 0xAE9C, 0xAE9C, 0xAE9C },
+{ 0xAE9D, 0xAE9D, 0xAE9D },
+{ 0xAE9E, 0xAE9E, 0xAE9E },
+{ 0xAE9F, 0xAE9F, 0xAE9F },
+{ 0xAEA0, 0xAEA0, 0xAEA0 },
+{ 0xAEA1, 0xAEA1, 0xAEA1 },
+{ 0xAEA2, 0xAEA2, 0xAEA2 },
+{ 0xAEA3, 0xAEA3, 0xAEA3 },
+{ 0xAEA4, 0xAEA4, 0xAEA4 },
+{ 0xAEA5, 0xAEA5, 0xAEA5 },
+{ 0xAEA6, 0xAEA6, 0xAEA6 },
+{ 0xAEA7, 0xAEA7, 0xAEA7 },
+{ 0xAEA8, 0xAEA8, 0xAEA8 },
+{ 0xAEA9, 0xAEA9, 0xAEA9 },
+{ 0xAEAA, 0xAEAA, 0xAEAA },
+{ 0xAEAB, 0xAEAB, 0xAEAB },
+{ 0xAEAC, 0xAEAC, 0xAEAC },
+{ 0xAEAD, 0xAEAD, 0xAEAD },
+{ 0xAEAE, 0xAEAE, 0xAEAE },
+{ 0xAEAF, 0xAEAF, 0xAEAF },
+{ 0xAEB0, 0xAEB0, 0xAEB0 },
+{ 0xAEB1, 0xAEB1, 0xAEB1 },
+{ 0xAEB2, 0xAEB2, 0xAEB2 },
+{ 0xAEB3, 0xAEB3, 0xAEB3 },
+{ 0xAEB4, 0xAEB4, 0xAEB4 },
+{ 0xAEB5, 0xAEB5, 0xAEB5 },
+{ 0xAEB6, 0xAEB6, 0xAEB6 },
+{ 0xAEB7, 0xAEB7, 0xAEB7 },
+{ 0xAEB8, 0xAEB8, 0xAEB8 },
+{ 0xAEB9, 0xAEB9, 0xAEB9 },
+{ 0xAEBA, 0xAEBA, 0xAEBA },
+{ 0xAEBB, 0xAEBB, 0xAEBB },
+{ 0xAEBC, 0xAEBC, 0xAEBC },
+{ 0xAEBD, 0xAEBD, 0xAEBD },
+{ 0xAEBE, 0xAEBE, 0xAEBE },
+{ 0xAEBF, 0xAEBF, 0xAEBF },
+{ 0xAEC0, 0xAEC0, 0xAEC0 },
+{ 0xAEC1, 0xAEC1, 0xAEC1 },
+{ 0xAEC2, 0xAEC2, 0xAEC2 },
+{ 0xAEC3, 0xAEC3, 0xAEC3 },
+{ 0xAEC4, 0xAEC4, 0xAEC4 },
+{ 0xAEC5, 0xAEC5, 0xAEC5 },
+{ 0xAEC6, 0xAEC6, 0xAEC6 },
+{ 0xAEC7, 0xAEC7, 0xAEC7 },
+{ 0xAEC8, 0xAEC8, 0xAEC8 },
+{ 0xAEC9, 0xAEC9, 0xAEC9 },
+{ 0xAECA, 0xAECA, 0xAECA },
+{ 0xAECB, 0xAECB, 0xAECB },
+{ 0xAECC, 0xAECC, 0xAECC },
+{ 0xAECD, 0xAECD, 0xAECD },
+{ 0xAECE, 0xAECE, 0xAECE },
+{ 0xAECF, 0xAECF, 0xAECF },
+{ 0xAED0, 0xAED0, 0xAED0 },
+{ 0xAED1, 0xAED1, 0xAED1 },
+{ 0xAED2, 0xAED2, 0xAED2 },
+{ 0xAED3, 0xAED3, 0xAED3 },
+{ 0xAED4, 0xAED4, 0xAED4 },
+{ 0xAED5, 0xAED5, 0xAED5 },
+{ 0xAED6, 0xAED6, 0xAED6 },
+{ 0xAED7, 0xAED7, 0xAED7 },
+{ 0xAED8, 0xAED8, 0xAED8 },
+{ 0xAED9, 0xAED9, 0xAED9 },
+{ 0xAEDA, 0xAEDA, 0xAEDA },
+{ 0xAEDB, 0xAEDB, 0xAEDB },
+{ 0xAEDC, 0xAEDC, 0xAEDC },
+{ 0xAEDD, 0xAEDD, 0xAEDD },
+{ 0xAEDE, 0xAEDE, 0xAEDE },
+{ 0xAEDF, 0xAEDF, 0xAEDF },
+{ 0xAEE0, 0xAEE0, 0xAEE0 },
+{ 0xAEE1, 0xAEE1, 0xAEE1 },
+{ 0xAEE2, 0xAEE2, 0xAEE2 },
+{ 0xAEE3, 0xAEE3, 0xAEE3 },
+{ 0xAEE4, 0xAEE4, 0xAEE4 },
+{ 0xAEE5, 0xAEE5, 0xAEE5 },
+{ 0xAEE6, 0xAEE6, 0xAEE6 },
+{ 0xAEE7, 0xAEE7, 0xAEE7 },
+{ 0xAEE8, 0xAEE8, 0xAEE8 },
+{ 0xAEE9, 0xAEE9, 0xAEE9 },
+{ 0xAEEA, 0xAEEA, 0xAEEA },
+{ 0xAEEB, 0xAEEB, 0xAEEB },
+{ 0xAEEC, 0xAEEC, 0xAEEC },
+{ 0xAEED, 0xAEED, 0xAEED },
+{ 0xAEEE, 0xAEEE, 0xAEEE },
+{ 0xAEEF, 0xAEEF, 0xAEEF },
+{ 0xAEF0, 0xAEF0, 0xAEF0 },
+{ 0xAEF1, 0xAEF1, 0xAEF1 },
+{ 0xAEF2, 0xAEF2, 0xAEF2 },
+{ 0xAEF3, 0xAEF3, 0xAEF3 },
+{ 0xAEF4, 0xAEF4, 0xAEF4 },
+{ 0xAEF5, 0xAEF5, 0xAEF5 },
+{ 0xAEF6, 0xAEF6, 0xAEF6 },
+{ 0xAEF7, 0xAEF7, 0xAEF7 },
+{ 0xAEF8, 0xAEF8, 0xAEF8 },
+{ 0xAEF9, 0xAEF9, 0xAEF9 },
+{ 0xAEFA, 0xAEFA, 0xAEFA },
+{ 0xAEFB, 0xAEFB, 0xAEFB },
+{ 0xAEFC, 0xAEFC, 0xAEFC },
+{ 0xAEFD, 0xAEFD, 0xAEFD },
+{ 0xAEFE, 0xAEFE, 0xAEFE },
+{ 0xAEFF, 0xAEFF, 0xAEFF },
+{ 0xAF00, 0xAF00, 0xAF00 },
+{ 0xAF01, 0xAF01, 0xAF01 },
+{ 0xAF02, 0xAF02, 0xAF02 },
+{ 0xAF03, 0xAF03, 0xAF03 },
+{ 0xAF04, 0xAF04, 0xAF04 },
+{ 0xAF05, 0xAF05, 0xAF05 },
+{ 0xAF06, 0xAF06, 0xAF06 },
+{ 0xAF07, 0xAF07, 0xAF07 },
+{ 0xAF08, 0xAF08, 0xAF08 },
+{ 0xAF09, 0xAF09, 0xAF09 },
+{ 0xAF0A, 0xAF0A, 0xAF0A },
+{ 0xAF0B, 0xAF0B, 0xAF0B },
+{ 0xAF0C, 0xAF0C, 0xAF0C },
+{ 0xAF0D, 0xAF0D, 0xAF0D },
+{ 0xAF0E, 0xAF0E, 0xAF0E },
+{ 0xAF0F, 0xAF0F, 0xAF0F },
+{ 0xAF10, 0xAF10, 0xAF10 },
+{ 0xAF11, 0xAF11, 0xAF11 },
+{ 0xAF12, 0xAF12, 0xAF12 },
+{ 0xAF13, 0xAF13, 0xAF13 },
+{ 0xAF14, 0xAF14, 0xAF14 },
+{ 0xAF15, 0xAF15, 0xAF15 },
+{ 0xAF16, 0xAF16, 0xAF16 },
+{ 0xAF17, 0xAF17, 0xAF17 },
+{ 0xAF18, 0xAF18, 0xAF18 },
+{ 0xAF19, 0xAF19, 0xAF19 },
+{ 0xAF1A, 0xAF1A, 0xAF1A },
+{ 0xAF1B, 0xAF1B, 0xAF1B },
+{ 0xAF1C, 0xAF1C, 0xAF1C },
+{ 0xAF1D, 0xAF1D, 0xAF1D },
+{ 0xAF1E, 0xAF1E, 0xAF1E },
+{ 0xAF1F, 0xAF1F, 0xAF1F },
+{ 0xAF20, 0xAF20, 0xAF20 },
+{ 0xAF21, 0xAF21, 0xAF21 },
+{ 0xAF22, 0xAF22, 0xAF22 },
+{ 0xAF23, 0xAF23, 0xAF23 },
+{ 0xAF24, 0xAF24, 0xAF24 },
+{ 0xAF25, 0xAF25, 0xAF25 },
+{ 0xAF26, 0xAF26, 0xAF26 },
+{ 0xAF27, 0xAF27, 0xAF27 },
+{ 0xAF28, 0xAF28, 0xAF28 },
+{ 0xAF29, 0xAF29, 0xAF29 },
+{ 0xAF2A, 0xAF2A, 0xAF2A },
+{ 0xAF2B, 0xAF2B, 0xAF2B },
+{ 0xAF2C, 0xAF2C, 0xAF2C },
+{ 0xAF2D, 0xAF2D, 0xAF2D },
+{ 0xAF2E, 0xAF2E, 0xAF2E },
+{ 0xAF2F, 0xAF2F, 0xAF2F },
+{ 0xAF30, 0xAF30, 0xAF30 },
+{ 0xAF31, 0xAF31, 0xAF31 },
+{ 0xAF32, 0xAF32, 0xAF32 },
+{ 0xAF33, 0xAF33, 0xAF33 },
+{ 0xAF34, 0xAF34, 0xAF34 },
+{ 0xAF35, 0xAF35, 0xAF35 },
+{ 0xAF36, 0xAF36, 0xAF36 },
+{ 0xAF37, 0xAF37, 0xAF37 },
+{ 0xAF38, 0xAF38, 0xAF38 },
+{ 0xAF39, 0xAF39, 0xAF39 },
+{ 0xAF3A, 0xAF3A, 0xAF3A },
+{ 0xAF3B, 0xAF3B, 0xAF3B },
+{ 0xAF3C, 0xAF3C, 0xAF3C },
+{ 0xAF3D, 0xAF3D, 0xAF3D },
+{ 0xAF3E, 0xAF3E, 0xAF3E },
+{ 0xAF3F, 0xAF3F, 0xAF3F },
+{ 0xAF40, 0xAF40, 0xAF40 },
+{ 0xAF41, 0xAF41, 0xAF41 },
+{ 0xAF42, 0xAF42, 0xAF42 },
+{ 0xAF43, 0xAF43, 0xAF43 },
+{ 0xAF44, 0xAF44, 0xAF44 },
+{ 0xAF45, 0xAF45, 0xAF45 },
+{ 0xAF46, 0xAF46, 0xAF46 },
+{ 0xAF47, 0xAF47, 0xAF47 },
+{ 0xAF48, 0xAF48, 0xAF48 },
+{ 0xAF49, 0xAF49, 0xAF49 },
+{ 0xAF4A, 0xAF4A, 0xAF4A },
+{ 0xAF4B, 0xAF4B, 0xAF4B },
+{ 0xAF4C, 0xAF4C, 0xAF4C },
+{ 0xAF4D, 0xAF4D, 0xAF4D },
+{ 0xAF4E, 0xAF4E, 0xAF4E },
+{ 0xAF4F, 0xAF4F, 0xAF4F },
+{ 0xAF50, 0xAF50, 0xAF50 },
+{ 0xAF51, 0xAF51, 0xAF51 },
+{ 0xAF52, 0xAF52, 0xAF52 },
+{ 0xAF53, 0xAF53, 0xAF53 },
+{ 0xAF54, 0xAF54, 0xAF54 },
+{ 0xAF55, 0xAF55, 0xAF55 },
+{ 0xAF56, 0xAF56, 0xAF56 },
+{ 0xAF57, 0xAF57, 0xAF57 },
+{ 0xAF58, 0xAF58, 0xAF58 },
+{ 0xAF59, 0xAF59, 0xAF59 },
+{ 0xAF5A, 0xAF5A, 0xAF5A },
+{ 0xAF5B, 0xAF5B, 0xAF5B },
+{ 0xAF5C, 0xAF5C, 0xAF5C },
+{ 0xAF5D, 0xAF5D, 0xAF5D },
+{ 0xAF5E, 0xAF5E, 0xAF5E },
+{ 0xAF5F, 0xAF5F, 0xAF5F },
+{ 0xAF60, 0xAF60, 0xAF60 },
+{ 0xAF61, 0xAF61, 0xAF61 },
+{ 0xAF62, 0xAF62, 0xAF62 },
+{ 0xAF63, 0xAF63, 0xAF63 },
+{ 0xAF64, 0xAF64, 0xAF64 },
+{ 0xAF65, 0xAF65, 0xAF65 },
+{ 0xAF66, 0xAF66, 0xAF66 },
+{ 0xAF67, 0xAF67, 0xAF67 },
+{ 0xAF68, 0xAF68, 0xAF68 },
+{ 0xAF69, 0xAF69, 0xAF69 },
+{ 0xAF6A, 0xAF6A, 0xAF6A },
+{ 0xAF6B, 0xAF6B, 0xAF6B },
+{ 0xAF6C, 0xAF6C, 0xAF6C },
+{ 0xAF6D, 0xAF6D, 0xAF6D },
+{ 0xAF6E, 0xAF6E, 0xAF6E },
+{ 0xAF6F, 0xAF6F, 0xAF6F },
+{ 0xAF70, 0xAF70, 0xAF70 },
+{ 0xAF71, 0xAF71, 0xAF71 },
+{ 0xAF72, 0xAF72, 0xAF72 },
+{ 0xAF73, 0xAF73, 0xAF73 },
+{ 0xAF74, 0xAF74, 0xAF74 },
+{ 0xAF75, 0xAF75, 0xAF75 },
+{ 0xAF76, 0xAF76, 0xAF76 },
+{ 0xAF77, 0xAF77, 0xAF77 },
+{ 0xAF78, 0xAF78, 0xAF78 },
+{ 0xAF79, 0xAF79, 0xAF79 },
+{ 0xAF7A, 0xAF7A, 0xAF7A },
+{ 0xAF7B, 0xAF7B, 0xAF7B },
+{ 0xAF7C, 0xAF7C, 0xAF7C },
+{ 0xAF7D, 0xAF7D, 0xAF7D },
+{ 0xAF7E, 0xAF7E, 0xAF7E },
+{ 0xAF7F, 0xAF7F, 0xAF7F },
+{ 0xAF80, 0xAF80, 0xAF80 },
+{ 0xAF81, 0xAF81, 0xAF81 },
+{ 0xAF82, 0xAF82, 0xAF82 },
+{ 0xAF83, 0xAF83, 0xAF83 },
+{ 0xAF84, 0xAF84, 0xAF84 },
+{ 0xAF85, 0xAF85, 0xAF85 },
+{ 0xAF86, 0xAF86, 0xAF86 },
+{ 0xAF87, 0xAF87, 0xAF87 },
+{ 0xAF88, 0xAF88, 0xAF88 },
+{ 0xAF89, 0xAF89, 0xAF89 },
+{ 0xAF8A, 0xAF8A, 0xAF8A },
+{ 0xAF8B, 0xAF8B, 0xAF8B },
+{ 0xAF8C, 0xAF8C, 0xAF8C },
+{ 0xAF8D, 0xAF8D, 0xAF8D },
+{ 0xAF8E, 0xAF8E, 0xAF8E },
+{ 0xAF8F, 0xAF8F, 0xAF8F },
+{ 0xAF90, 0xAF90, 0xAF90 },
+{ 0xAF91, 0xAF91, 0xAF91 },
+{ 0xAF92, 0xAF92, 0xAF92 },
+{ 0xAF93, 0xAF93, 0xAF93 },
+{ 0xAF94, 0xAF94, 0xAF94 },
+{ 0xAF95, 0xAF95, 0xAF95 },
+{ 0xAF96, 0xAF96, 0xAF96 },
+{ 0xAF97, 0xAF97, 0xAF97 },
+{ 0xAF98, 0xAF98, 0xAF98 },
+{ 0xAF99, 0xAF99, 0xAF99 },
+{ 0xAF9A, 0xAF9A, 0xAF9A },
+{ 0xAF9B, 0xAF9B, 0xAF9B },
+{ 0xAF9C, 0xAF9C, 0xAF9C },
+{ 0xAF9D, 0xAF9D, 0xAF9D },
+{ 0xAF9E, 0xAF9E, 0xAF9E },
+{ 0xAF9F, 0xAF9F, 0xAF9F },
+{ 0xAFA0, 0xAFA0, 0xAFA0 },
+{ 0xAFA1, 0xAFA1, 0xAFA1 },
+{ 0xAFA2, 0xAFA2, 0xAFA2 },
+{ 0xAFA3, 0xAFA3, 0xAFA3 },
+{ 0xAFA4, 0xAFA4, 0xAFA4 },
+{ 0xAFA5, 0xAFA5, 0xAFA5 },
+{ 0xAFA6, 0xAFA6, 0xAFA6 },
+{ 0xAFA7, 0xAFA7, 0xAFA7 },
+{ 0xAFA8, 0xAFA8, 0xAFA8 },
+{ 0xAFA9, 0xAFA9, 0xAFA9 },
+{ 0xAFAA, 0xAFAA, 0xAFAA },
+{ 0xAFAB, 0xAFAB, 0xAFAB },
+{ 0xAFAC, 0xAFAC, 0xAFAC },
+{ 0xAFAD, 0xAFAD, 0xAFAD },
+{ 0xAFAE, 0xAFAE, 0xAFAE },
+{ 0xAFAF, 0xAFAF, 0xAFAF },
+{ 0xAFB0, 0xAFB0, 0xAFB0 },
+{ 0xAFB1, 0xAFB1, 0xAFB1 },
+{ 0xAFB2, 0xAFB2, 0xAFB2 },
+{ 0xAFB3, 0xAFB3, 0xAFB3 },
+{ 0xAFB4, 0xAFB4, 0xAFB4 },
+{ 0xAFB5, 0xAFB5, 0xAFB5 },
+{ 0xAFB6, 0xAFB6, 0xAFB6 },
+{ 0xAFB7, 0xAFB7, 0xAFB7 },
+{ 0xAFB8, 0xAFB8, 0xAFB8 },
+{ 0xAFB9, 0xAFB9, 0xAFB9 },
+{ 0xAFBA, 0xAFBA, 0xAFBA },
+{ 0xAFBB, 0xAFBB, 0xAFBB },
+{ 0xAFBC, 0xAFBC, 0xAFBC },
+{ 0xAFBD, 0xAFBD, 0xAFBD },
+{ 0xAFBE, 0xAFBE, 0xAFBE },
+{ 0xAFBF, 0xAFBF, 0xAFBF },
+{ 0xAFC0, 0xAFC0, 0xAFC0 },
+{ 0xAFC1, 0xAFC1, 0xAFC1 },
+{ 0xAFC2, 0xAFC2, 0xAFC2 },
+{ 0xAFC3, 0xAFC3, 0xAFC3 },
+{ 0xAFC4, 0xAFC4, 0xAFC4 },
+{ 0xAFC5, 0xAFC5, 0xAFC5 },
+{ 0xAFC6, 0xAFC6, 0xAFC6 },
+{ 0xAFC7, 0xAFC7, 0xAFC7 },
+{ 0xAFC8, 0xAFC8, 0xAFC8 },
+{ 0xAFC9, 0xAFC9, 0xAFC9 },
+{ 0xAFCA, 0xAFCA, 0xAFCA },
+{ 0xAFCB, 0xAFCB, 0xAFCB },
+{ 0xAFCC, 0xAFCC, 0xAFCC },
+{ 0xAFCD, 0xAFCD, 0xAFCD },
+{ 0xAFCE, 0xAFCE, 0xAFCE },
+{ 0xAFCF, 0xAFCF, 0xAFCF },
+{ 0xAFD0, 0xAFD0, 0xAFD0 },
+{ 0xAFD1, 0xAFD1, 0xAFD1 },
+{ 0xAFD2, 0xAFD2, 0xAFD2 },
+{ 0xAFD3, 0xAFD3, 0xAFD3 },
+{ 0xAFD4, 0xAFD4, 0xAFD4 },
+{ 0xAFD5, 0xAFD5, 0xAFD5 },
+{ 0xAFD6, 0xAFD6, 0xAFD6 },
+{ 0xAFD7, 0xAFD7, 0xAFD7 },
+{ 0xAFD8, 0xAFD8, 0xAFD8 },
+{ 0xAFD9, 0xAFD9, 0xAFD9 },
+{ 0xAFDA, 0xAFDA, 0xAFDA },
+{ 0xAFDB, 0xAFDB, 0xAFDB },
+{ 0xAFDC, 0xAFDC, 0xAFDC },
+{ 0xAFDD, 0xAFDD, 0xAFDD },
+{ 0xAFDE, 0xAFDE, 0xAFDE },
+{ 0xAFDF, 0xAFDF, 0xAFDF },
+{ 0xAFE0, 0xAFE0, 0xAFE0 },
+{ 0xAFE1, 0xAFE1, 0xAFE1 },
+{ 0xAFE2, 0xAFE2, 0xAFE2 },
+{ 0xAFE3, 0xAFE3, 0xAFE3 },
+{ 0xAFE4, 0xAFE4, 0xAFE4 },
+{ 0xAFE5, 0xAFE5, 0xAFE5 },
+{ 0xAFE6, 0xAFE6, 0xAFE6 },
+{ 0xAFE7, 0xAFE7, 0xAFE7 },
+{ 0xAFE8, 0xAFE8, 0xAFE8 },
+{ 0xAFE9, 0xAFE9, 0xAFE9 },
+{ 0xAFEA, 0xAFEA, 0xAFEA },
+{ 0xAFEB, 0xAFEB, 0xAFEB },
+{ 0xAFEC, 0xAFEC, 0xAFEC },
+{ 0xAFED, 0xAFED, 0xAFED },
+{ 0xAFEE, 0xAFEE, 0xAFEE },
+{ 0xAFEF, 0xAFEF, 0xAFEF },
+{ 0xAFF0, 0xAFF0, 0xAFF0 },
+{ 0xAFF1, 0xAFF1, 0xAFF1 },
+{ 0xAFF2, 0xAFF2, 0xAFF2 },
+{ 0xAFF3, 0xAFF3, 0xAFF3 },
+{ 0xAFF4, 0xAFF4, 0xAFF4 },
+{ 0xAFF5, 0xAFF5, 0xAFF5 },
+{ 0xAFF6, 0xAFF6, 0xAFF6 },
+{ 0xAFF7, 0xAFF7, 0xAFF7 },
+{ 0xAFF8, 0xAFF8, 0xAFF8 },
+{ 0xAFF9, 0xAFF9, 0xAFF9 },
+{ 0xAFFA, 0xAFFA, 0xAFFA },
+{ 0xAFFB, 0xAFFB, 0xAFFB },
+{ 0xAFFC, 0xAFFC, 0xAFFC },
+{ 0xAFFD, 0xAFFD, 0xAFFD },
+{ 0xAFFE, 0xAFFE, 0xAFFE },
+{ 0xAFFF, 0xAFFF, 0xAFFF },
+{ 0xB000, 0xB000, 0xB000 },
+{ 0xB001, 0xB001, 0xB001 },
+{ 0xB002, 0xB002, 0xB002 },
+{ 0xB003, 0xB003, 0xB003 },
+{ 0xB004, 0xB004, 0xB004 },
+{ 0xB005, 0xB005, 0xB005 },
+{ 0xB006, 0xB006, 0xB006 },
+{ 0xB007, 0xB007, 0xB007 },
+{ 0xB008, 0xB008, 0xB008 },
+{ 0xB009, 0xB009, 0xB009 },
+{ 0xB00A, 0xB00A, 0xB00A },
+{ 0xB00B, 0xB00B, 0xB00B },
+{ 0xB00C, 0xB00C, 0xB00C },
+{ 0xB00D, 0xB00D, 0xB00D },
+{ 0xB00E, 0xB00E, 0xB00E },
+{ 0xB00F, 0xB00F, 0xB00F },
+{ 0xB010, 0xB010, 0xB010 },
+{ 0xB011, 0xB011, 0xB011 },
+{ 0xB012, 0xB012, 0xB012 },
+{ 0xB013, 0xB013, 0xB013 },
+{ 0xB014, 0xB014, 0xB014 },
+{ 0xB015, 0xB015, 0xB015 },
+{ 0xB016, 0xB016, 0xB016 },
+{ 0xB017, 0xB017, 0xB017 },
+{ 0xB018, 0xB018, 0xB018 },
+{ 0xB019, 0xB019, 0xB019 },
+{ 0xB01A, 0xB01A, 0xB01A },
+{ 0xB01B, 0xB01B, 0xB01B },
+{ 0xB01C, 0xB01C, 0xB01C },
+{ 0xB01D, 0xB01D, 0xB01D },
+{ 0xB01E, 0xB01E, 0xB01E },
+{ 0xB01F, 0xB01F, 0xB01F },
+{ 0xB020, 0xB020, 0xB020 },
+{ 0xB021, 0xB021, 0xB021 },
+{ 0xB022, 0xB022, 0xB022 },
+{ 0xB023, 0xB023, 0xB023 },
+{ 0xB024, 0xB024, 0xB024 },
+{ 0xB025, 0xB025, 0xB025 },
+{ 0xB026, 0xB026, 0xB026 },
+{ 0xB027, 0xB027, 0xB027 },
+{ 0xB028, 0xB028, 0xB028 },
+{ 0xB029, 0xB029, 0xB029 },
+{ 0xB02A, 0xB02A, 0xB02A },
+{ 0xB02B, 0xB02B, 0xB02B },
+{ 0xB02C, 0xB02C, 0xB02C },
+{ 0xB02D, 0xB02D, 0xB02D },
+{ 0xB02E, 0xB02E, 0xB02E },
+{ 0xB02F, 0xB02F, 0xB02F },
+{ 0xB030, 0xB030, 0xB030 },
+{ 0xB031, 0xB031, 0xB031 },
+{ 0xB032, 0xB032, 0xB032 },
+{ 0xB033, 0xB033, 0xB033 },
+{ 0xB034, 0xB034, 0xB034 },
+{ 0xB035, 0xB035, 0xB035 },
+{ 0xB036, 0xB036, 0xB036 },
+{ 0xB037, 0xB037, 0xB037 },
+{ 0xB038, 0xB038, 0xB038 },
+{ 0xB039, 0xB039, 0xB039 },
+{ 0xB03A, 0xB03A, 0xB03A },
+{ 0xB03B, 0xB03B, 0xB03B },
+{ 0xB03C, 0xB03C, 0xB03C },
+{ 0xB03D, 0xB03D, 0xB03D },
+{ 0xB03E, 0xB03E, 0xB03E },
+{ 0xB03F, 0xB03F, 0xB03F },
+{ 0xB040, 0xB040, 0xB040 },
+{ 0xB041, 0xB041, 0xB041 },
+{ 0xB042, 0xB042, 0xB042 },
+{ 0xB043, 0xB043, 0xB043 },
+{ 0xB044, 0xB044, 0xB044 },
+{ 0xB045, 0xB045, 0xB045 },
+{ 0xB046, 0xB046, 0xB046 },
+{ 0xB047, 0xB047, 0xB047 },
+{ 0xB048, 0xB048, 0xB048 },
+{ 0xB049, 0xB049, 0xB049 },
+{ 0xB04A, 0xB04A, 0xB04A },
+{ 0xB04B, 0xB04B, 0xB04B },
+{ 0xB04C, 0xB04C, 0xB04C },
+{ 0xB04D, 0xB04D, 0xB04D },
+{ 0xB04E, 0xB04E, 0xB04E },
+{ 0xB04F, 0xB04F, 0xB04F },
+{ 0xB050, 0xB050, 0xB050 },
+{ 0xB051, 0xB051, 0xB051 },
+{ 0xB052, 0xB052, 0xB052 },
+{ 0xB053, 0xB053, 0xB053 },
+{ 0xB054, 0xB054, 0xB054 },
+{ 0xB055, 0xB055, 0xB055 },
+{ 0xB056, 0xB056, 0xB056 },
+{ 0xB057, 0xB057, 0xB057 },
+{ 0xB058, 0xB058, 0xB058 },
+{ 0xB059, 0xB059, 0xB059 },
+{ 0xB05A, 0xB05A, 0xB05A },
+{ 0xB05B, 0xB05B, 0xB05B },
+{ 0xB05C, 0xB05C, 0xB05C },
+{ 0xB05D, 0xB05D, 0xB05D },
+{ 0xB05E, 0xB05E, 0xB05E },
+{ 0xB05F, 0xB05F, 0xB05F },
+{ 0xB060, 0xB060, 0xB060 },
+{ 0xB061, 0xB061, 0xB061 },
+{ 0xB062, 0xB062, 0xB062 },
+{ 0xB063, 0xB063, 0xB063 },
+{ 0xB064, 0xB064, 0xB064 },
+{ 0xB065, 0xB065, 0xB065 },
+{ 0xB066, 0xB066, 0xB066 },
+{ 0xB067, 0xB067, 0xB067 },
+{ 0xB068, 0xB068, 0xB068 },
+{ 0xB069, 0xB069, 0xB069 },
+{ 0xB06A, 0xB06A, 0xB06A },
+{ 0xB06B, 0xB06B, 0xB06B },
+{ 0xB06C, 0xB06C, 0xB06C },
+{ 0xB06D, 0xB06D, 0xB06D },
+{ 0xB06E, 0xB06E, 0xB06E },
+{ 0xB06F, 0xB06F, 0xB06F },
+{ 0xB070, 0xB070, 0xB070 },
+{ 0xB071, 0xB071, 0xB071 },
+{ 0xB072, 0xB072, 0xB072 },
+{ 0xB073, 0xB073, 0xB073 },
+{ 0xB074, 0xB074, 0xB074 },
+{ 0xB075, 0xB075, 0xB075 },
+{ 0xB076, 0xB076, 0xB076 },
+{ 0xB077, 0xB077, 0xB077 },
+{ 0xB078, 0xB078, 0xB078 },
+{ 0xB079, 0xB079, 0xB079 },
+{ 0xB07A, 0xB07A, 0xB07A },
+{ 0xB07B, 0xB07B, 0xB07B },
+{ 0xB07C, 0xB07C, 0xB07C },
+{ 0xB07D, 0xB07D, 0xB07D },
+{ 0xB07E, 0xB07E, 0xB07E },
+{ 0xB07F, 0xB07F, 0xB07F },
+{ 0xB080, 0xB080, 0xB080 },
+{ 0xB081, 0xB081, 0xB081 },
+{ 0xB082, 0xB082, 0xB082 },
+{ 0xB083, 0xB083, 0xB083 },
+{ 0xB084, 0xB084, 0xB084 },
+{ 0xB085, 0xB085, 0xB085 },
+{ 0xB086, 0xB086, 0xB086 },
+{ 0xB087, 0xB087, 0xB087 },
+{ 0xB088, 0xB088, 0xB088 },
+{ 0xB089, 0xB089, 0xB089 },
+{ 0xB08A, 0xB08A, 0xB08A },
+{ 0xB08B, 0xB08B, 0xB08B },
+{ 0xB08C, 0xB08C, 0xB08C },
+{ 0xB08D, 0xB08D, 0xB08D },
+{ 0xB08E, 0xB08E, 0xB08E },
+{ 0xB08F, 0xB08F, 0xB08F },
+{ 0xB090, 0xB090, 0xB090 },
+{ 0xB091, 0xB091, 0xB091 },
+{ 0xB092, 0xB092, 0xB092 },
+{ 0xB093, 0xB093, 0xB093 },
+{ 0xB094, 0xB094, 0xB094 },
+{ 0xB095, 0xB095, 0xB095 },
+{ 0xB096, 0xB096, 0xB096 },
+{ 0xB097, 0xB097, 0xB097 },
+{ 0xB098, 0xB098, 0xB098 },
+{ 0xB099, 0xB099, 0xB099 },
+{ 0xB09A, 0xB09A, 0xB09A },
+{ 0xB09B, 0xB09B, 0xB09B },
+{ 0xB09C, 0xB09C, 0xB09C },
+{ 0xB09D, 0xB09D, 0xB09D },
+{ 0xB09E, 0xB09E, 0xB09E },
+{ 0xB09F, 0xB09F, 0xB09F },
+{ 0xB0A0, 0xB0A0, 0xB0A0 },
+{ 0xB0A1, 0xB0A1, 0xB0A1 },
+{ 0xB0A2, 0xB0A2, 0xB0A2 },
+{ 0xB0A3, 0xB0A3, 0xB0A3 },
+{ 0xB0A4, 0xB0A4, 0xB0A4 },
+{ 0xB0A5, 0xB0A5, 0xB0A5 },
+{ 0xB0A6, 0xB0A6, 0xB0A6 },
+{ 0xB0A7, 0xB0A7, 0xB0A7 },
+{ 0xB0A8, 0xB0A8, 0xB0A8 },
+{ 0xB0A9, 0xB0A9, 0xB0A9 },
+{ 0xB0AA, 0xB0AA, 0xB0AA },
+{ 0xB0AB, 0xB0AB, 0xB0AB },
+{ 0xB0AC, 0xB0AC, 0xB0AC },
+{ 0xB0AD, 0xB0AD, 0xB0AD },
+{ 0xB0AE, 0xB0AE, 0xB0AE },
+{ 0xB0AF, 0xB0AF, 0xB0AF },
+{ 0xB0B0, 0xB0B0, 0xB0B0 },
+{ 0xB0B1, 0xB0B1, 0xB0B1 },
+{ 0xB0B2, 0xB0B2, 0xB0B2 },
+{ 0xB0B3, 0xB0B3, 0xB0B3 },
+{ 0xB0B4, 0xB0B4, 0xB0B4 },
+{ 0xB0B5, 0xB0B5, 0xB0B5 },
+{ 0xB0B6, 0xB0B6, 0xB0B6 },
+{ 0xB0B7, 0xB0B7, 0xB0B7 },
+{ 0xB0B8, 0xB0B8, 0xB0B8 },
+{ 0xB0B9, 0xB0B9, 0xB0B9 },
+{ 0xB0BA, 0xB0BA, 0xB0BA },
+{ 0xB0BB, 0xB0BB, 0xB0BB },
+{ 0xB0BC, 0xB0BC, 0xB0BC },
+{ 0xB0BD, 0xB0BD, 0xB0BD },
+{ 0xB0BE, 0xB0BE, 0xB0BE },
+{ 0xB0BF, 0xB0BF, 0xB0BF },
+{ 0xB0C0, 0xB0C0, 0xB0C0 },
+{ 0xB0C1, 0xB0C1, 0xB0C1 },
+{ 0xB0C2, 0xB0C2, 0xB0C2 },
+{ 0xB0C3, 0xB0C3, 0xB0C3 },
+{ 0xB0C4, 0xB0C4, 0xB0C4 },
+{ 0xB0C5, 0xB0C5, 0xB0C5 },
+{ 0xB0C6, 0xB0C6, 0xB0C6 },
+{ 0xB0C7, 0xB0C7, 0xB0C7 },
+{ 0xB0C8, 0xB0C8, 0xB0C8 },
+{ 0xB0C9, 0xB0C9, 0xB0C9 },
+{ 0xB0CA, 0xB0CA, 0xB0CA },
+{ 0xB0CB, 0xB0CB, 0xB0CB },
+{ 0xB0CC, 0xB0CC, 0xB0CC },
+{ 0xB0CD, 0xB0CD, 0xB0CD },
+{ 0xB0CE, 0xB0CE, 0xB0CE },
+{ 0xB0CF, 0xB0CF, 0xB0CF },
+{ 0xB0D0, 0xB0D0, 0xB0D0 },
+{ 0xB0D1, 0xB0D1, 0xB0D1 },
+{ 0xB0D2, 0xB0D2, 0xB0D2 },
+{ 0xB0D3, 0xB0D3, 0xB0D3 },
+{ 0xB0D4, 0xB0D4, 0xB0D4 },
+{ 0xB0D5, 0xB0D5, 0xB0D5 },
+{ 0xB0D6, 0xB0D6, 0xB0D6 },
+{ 0xB0D7, 0xB0D7, 0xB0D7 },
+{ 0xB0D8, 0xB0D8, 0xB0D8 },
+{ 0xB0D9, 0xB0D9, 0xB0D9 },
+{ 0xB0DA, 0xB0DA, 0xB0DA },
+{ 0xB0DB, 0xB0DB, 0xB0DB },
+{ 0xB0DC, 0xB0DC, 0xB0DC },
+{ 0xB0DD, 0xB0DD, 0xB0DD },
+{ 0xB0DE, 0xB0DE, 0xB0DE },
+{ 0xB0DF, 0xB0DF, 0xB0DF },
+{ 0xB0E0, 0xB0E0, 0xB0E0 },
+{ 0xB0E1, 0xB0E1, 0xB0E1 },
+{ 0xB0E2, 0xB0E2, 0xB0E2 },
+{ 0xB0E3, 0xB0E3, 0xB0E3 },
+{ 0xB0E4, 0xB0E4, 0xB0E4 },
+{ 0xB0E5, 0xB0E5, 0xB0E5 },
+{ 0xB0E6, 0xB0E6, 0xB0E6 },
+{ 0xB0E7, 0xB0E7, 0xB0E7 },
+{ 0xB0E8, 0xB0E8, 0xB0E8 },
+{ 0xB0E9, 0xB0E9, 0xB0E9 },
+{ 0xB0EA, 0xB0EA, 0xB0EA },
+{ 0xB0EB, 0xB0EB, 0xB0EB },
+{ 0xB0EC, 0xB0EC, 0xB0EC },
+{ 0xB0ED, 0xB0ED, 0xB0ED },
+{ 0xB0EE, 0xB0EE, 0xB0EE },
+{ 0xB0EF, 0xB0EF, 0xB0EF },
+{ 0xB0F0, 0xB0F0, 0xB0F0 },
+{ 0xB0F1, 0xB0F1, 0xB0F1 },
+{ 0xB0F2, 0xB0F2, 0xB0F2 },
+{ 0xB0F3, 0xB0F3, 0xB0F3 },
+{ 0xB0F4, 0xB0F4, 0xB0F4 },
+{ 0xB0F5, 0xB0F5, 0xB0F5 },
+{ 0xB0F6, 0xB0F6, 0xB0F6 },
+{ 0xB0F7, 0xB0F7, 0xB0F7 },
+{ 0xB0F8, 0xB0F8, 0xB0F8 },
+{ 0xB0F9, 0xB0F9, 0xB0F9 },
+{ 0xB0FA, 0xB0FA, 0xB0FA },
+{ 0xB0FB, 0xB0FB, 0xB0FB },
+{ 0xB0FC, 0xB0FC, 0xB0FC },
+{ 0xB0FD, 0xB0FD, 0xB0FD },
+{ 0xB0FE, 0xB0FE, 0xB0FE },
+{ 0xB0FF, 0xB0FF, 0xB0FF },
+{ 0xB100, 0xB100, 0xB100 },
+{ 0xB101, 0xB101, 0xB101 },
+{ 0xB102, 0xB102, 0xB102 },
+{ 0xB103, 0xB103, 0xB103 },
+{ 0xB104, 0xB104, 0xB104 },
+{ 0xB105, 0xB105, 0xB105 },
+{ 0xB106, 0xB106, 0xB106 },
+{ 0xB107, 0xB107, 0xB107 },
+{ 0xB108, 0xB108, 0xB108 },
+{ 0xB109, 0xB109, 0xB109 },
+{ 0xB10A, 0xB10A, 0xB10A },
+{ 0xB10B, 0xB10B, 0xB10B },
+{ 0xB10C, 0xB10C, 0xB10C },
+{ 0xB10D, 0xB10D, 0xB10D },
+{ 0xB10E, 0xB10E, 0xB10E },
+{ 0xB10F, 0xB10F, 0xB10F },
+{ 0xB110, 0xB110, 0xB110 },
+{ 0xB111, 0xB111, 0xB111 },
+{ 0xB112, 0xB112, 0xB112 },
+{ 0xB113, 0xB113, 0xB113 },
+{ 0xB114, 0xB114, 0xB114 },
+{ 0xB115, 0xB115, 0xB115 },
+{ 0xB116, 0xB116, 0xB116 },
+{ 0xB117, 0xB117, 0xB117 },
+{ 0xB118, 0xB118, 0xB118 },
+{ 0xB119, 0xB119, 0xB119 },
+{ 0xB11A, 0xB11A, 0xB11A },
+{ 0xB11B, 0xB11B, 0xB11B },
+{ 0xB11C, 0xB11C, 0xB11C },
+{ 0xB11D, 0xB11D, 0xB11D },
+{ 0xB11E, 0xB11E, 0xB11E },
+{ 0xB11F, 0xB11F, 0xB11F },
+{ 0xB120, 0xB120, 0xB120 },
+{ 0xB121, 0xB121, 0xB121 },
+{ 0xB122, 0xB122, 0xB122 },
+{ 0xB123, 0xB123, 0xB123 },
+{ 0xB124, 0xB124, 0xB124 },
+{ 0xB125, 0xB125, 0xB125 },
+{ 0xB126, 0xB126, 0xB126 },
+{ 0xB127, 0xB127, 0xB127 },
+{ 0xB128, 0xB128, 0xB128 },
+{ 0xB129, 0xB129, 0xB129 },
+{ 0xB12A, 0xB12A, 0xB12A },
+{ 0xB12B, 0xB12B, 0xB12B },
+{ 0xB12C, 0xB12C, 0xB12C },
+{ 0xB12D, 0xB12D, 0xB12D },
+{ 0xB12E, 0xB12E, 0xB12E },
+{ 0xB12F, 0xB12F, 0xB12F },
+{ 0xB130, 0xB130, 0xB130 },
+{ 0xB131, 0xB131, 0xB131 },
+{ 0xB132, 0xB132, 0xB132 },
+{ 0xB133, 0xB133, 0xB133 },
+{ 0xB134, 0xB134, 0xB134 },
+{ 0xB135, 0xB135, 0xB135 },
+{ 0xB136, 0xB136, 0xB136 },
+{ 0xB137, 0xB137, 0xB137 },
+{ 0xB138, 0xB138, 0xB138 },
+{ 0xB139, 0xB139, 0xB139 },
+{ 0xB13A, 0xB13A, 0xB13A },
+{ 0xB13B, 0xB13B, 0xB13B },
+{ 0xB13C, 0xB13C, 0xB13C },
+{ 0xB13D, 0xB13D, 0xB13D },
+{ 0xB13E, 0xB13E, 0xB13E },
+{ 0xB13F, 0xB13F, 0xB13F },
+{ 0xB140, 0xB140, 0xB140 },
+{ 0xB141, 0xB141, 0xB141 },
+{ 0xB142, 0xB142, 0xB142 },
+{ 0xB143, 0xB143, 0xB143 },
+{ 0xB144, 0xB144, 0xB144 },
+{ 0xB145, 0xB145, 0xB145 },
+{ 0xB146, 0xB146, 0xB146 },
+{ 0xB147, 0xB147, 0xB147 },
+{ 0xB148, 0xB148, 0xB148 },
+{ 0xB149, 0xB149, 0xB149 },
+{ 0xB14A, 0xB14A, 0xB14A },
+{ 0xB14B, 0xB14B, 0xB14B },
+{ 0xB14C, 0xB14C, 0xB14C },
+{ 0xB14D, 0xB14D, 0xB14D },
+{ 0xB14E, 0xB14E, 0xB14E },
+{ 0xB14F, 0xB14F, 0xB14F },
+{ 0xB150, 0xB150, 0xB150 },
+{ 0xB151, 0xB151, 0xB151 },
+{ 0xB152, 0xB152, 0xB152 },
+{ 0xB153, 0xB153, 0xB153 },
+{ 0xB154, 0xB154, 0xB154 },
+{ 0xB155, 0xB155, 0xB155 },
+{ 0xB156, 0xB156, 0xB156 },
+{ 0xB157, 0xB157, 0xB157 },
+{ 0xB158, 0xB158, 0xB158 },
+{ 0xB159, 0xB159, 0xB159 },
+{ 0xB15A, 0xB15A, 0xB15A },
+{ 0xB15B, 0xB15B, 0xB15B },
+{ 0xB15C, 0xB15C, 0xB15C },
+{ 0xB15D, 0xB15D, 0xB15D },
+{ 0xB15E, 0xB15E, 0xB15E },
+{ 0xB15F, 0xB15F, 0xB15F },
+{ 0xB160, 0xB160, 0xB160 },
+{ 0xB161, 0xB161, 0xB161 },
+{ 0xB162, 0xB162, 0xB162 },
+{ 0xB163, 0xB163, 0xB163 },
+{ 0xB164, 0xB164, 0xB164 },
+{ 0xB165, 0xB165, 0xB165 },
+{ 0xB166, 0xB166, 0xB166 },
+{ 0xB167, 0xB167, 0xB167 },
+{ 0xB168, 0xB168, 0xB168 },
+{ 0xB169, 0xB169, 0xB169 },
+{ 0xB16A, 0xB16A, 0xB16A },
+{ 0xB16B, 0xB16B, 0xB16B },
+{ 0xB16C, 0xB16C, 0xB16C },
+{ 0xB16D, 0xB16D, 0xB16D },
+{ 0xB16E, 0xB16E, 0xB16E },
+{ 0xB16F, 0xB16F, 0xB16F },
+{ 0xB170, 0xB170, 0xB170 },
+{ 0xB171, 0xB171, 0xB171 },
+{ 0xB172, 0xB172, 0xB172 },
+{ 0xB173, 0xB173, 0xB173 },
+{ 0xB174, 0xB174, 0xB174 },
+{ 0xB175, 0xB175, 0xB175 },
+{ 0xB176, 0xB176, 0xB176 },
+{ 0xB177, 0xB177, 0xB177 },
+{ 0xB178, 0xB178, 0xB178 },
+{ 0xB179, 0xB179, 0xB179 },
+{ 0xB17A, 0xB17A, 0xB17A },
+{ 0xB17B, 0xB17B, 0xB17B },
+{ 0xB17C, 0xB17C, 0xB17C },
+{ 0xB17D, 0xB17D, 0xB17D },
+{ 0xB17E, 0xB17E, 0xB17E },
+{ 0xB17F, 0xB17F, 0xB17F },
+{ 0xB180, 0xB180, 0xB180 },
+{ 0xB181, 0xB181, 0xB181 },
+{ 0xB182, 0xB182, 0xB182 },
+{ 0xB183, 0xB183, 0xB183 },
+{ 0xB184, 0xB184, 0xB184 },
+{ 0xB185, 0xB185, 0xB185 },
+{ 0xB186, 0xB186, 0xB186 },
+{ 0xB187, 0xB187, 0xB187 },
+{ 0xB188, 0xB188, 0xB188 },
+{ 0xB189, 0xB189, 0xB189 },
+{ 0xB18A, 0xB18A, 0xB18A },
+{ 0xB18B, 0xB18B, 0xB18B },
+{ 0xB18C, 0xB18C, 0xB18C },
+{ 0xB18D, 0xB18D, 0xB18D },
+{ 0xB18E, 0xB18E, 0xB18E },
+{ 0xB18F, 0xB18F, 0xB18F },
+{ 0xB190, 0xB190, 0xB190 },
+{ 0xB191, 0xB191, 0xB191 },
+{ 0xB192, 0xB192, 0xB192 },
+{ 0xB193, 0xB193, 0xB193 },
+{ 0xB194, 0xB194, 0xB194 },
+{ 0xB195, 0xB195, 0xB195 },
+{ 0xB196, 0xB196, 0xB196 },
+{ 0xB197, 0xB197, 0xB197 },
+{ 0xB198, 0xB198, 0xB198 },
+{ 0xB199, 0xB199, 0xB199 },
+{ 0xB19A, 0xB19A, 0xB19A },
+{ 0xB19B, 0xB19B, 0xB19B },
+{ 0xB19C, 0xB19C, 0xB19C },
+{ 0xB19D, 0xB19D, 0xB19D },
+{ 0xB19E, 0xB19E, 0xB19E },
+{ 0xB19F, 0xB19F, 0xB19F },
+{ 0xB1A0, 0xB1A0, 0xB1A0 },
+{ 0xB1A1, 0xB1A1, 0xB1A1 },
+{ 0xB1A2, 0xB1A2, 0xB1A2 },
+{ 0xB1A3, 0xB1A3, 0xB1A3 },
+{ 0xB1A4, 0xB1A4, 0xB1A4 },
+{ 0xB1A5, 0xB1A5, 0xB1A5 },
+{ 0xB1A6, 0xB1A6, 0xB1A6 },
+{ 0xB1A7, 0xB1A7, 0xB1A7 },
+{ 0xB1A8, 0xB1A8, 0xB1A8 },
+{ 0xB1A9, 0xB1A9, 0xB1A9 },
+{ 0xB1AA, 0xB1AA, 0xB1AA },
+{ 0xB1AB, 0xB1AB, 0xB1AB },
+{ 0xB1AC, 0xB1AC, 0xB1AC },
+{ 0xB1AD, 0xB1AD, 0xB1AD },
+{ 0xB1AE, 0xB1AE, 0xB1AE },
+{ 0xB1AF, 0xB1AF, 0xB1AF },
+{ 0xB1B0, 0xB1B0, 0xB1B0 },
+{ 0xB1B1, 0xB1B1, 0xB1B1 },
+{ 0xB1B2, 0xB1B2, 0xB1B2 },
+{ 0xB1B3, 0xB1B3, 0xB1B3 },
+{ 0xB1B4, 0xB1B4, 0xB1B4 },
+{ 0xB1B5, 0xB1B5, 0xB1B5 },
+{ 0xB1B6, 0xB1B6, 0xB1B6 },
+{ 0xB1B7, 0xB1B7, 0xB1B7 },
+{ 0xB1B8, 0xB1B8, 0xB1B8 },
+{ 0xB1B9, 0xB1B9, 0xB1B9 },
+{ 0xB1BA, 0xB1BA, 0xB1BA },
+{ 0xB1BB, 0xB1BB, 0xB1BB },
+{ 0xB1BC, 0xB1BC, 0xB1BC },
+{ 0xB1BD, 0xB1BD, 0xB1BD },
+{ 0xB1BE, 0xB1BE, 0xB1BE },
+{ 0xB1BF, 0xB1BF, 0xB1BF },
+{ 0xB1C0, 0xB1C0, 0xB1C0 },
+{ 0xB1C1, 0xB1C1, 0xB1C1 },
+{ 0xB1C2, 0xB1C2, 0xB1C2 },
+{ 0xB1C3, 0xB1C3, 0xB1C3 },
+{ 0xB1C4, 0xB1C4, 0xB1C4 },
+{ 0xB1C5, 0xB1C5, 0xB1C5 },
+{ 0xB1C6, 0xB1C6, 0xB1C6 },
+{ 0xB1C7, 0xB1C7, 0xB1C7 },
+{ 0xB1C8, 0xB1C8, 0xB1C8 },
+{ 0xB1C9, 0xB1C9, 0xB1C9 },
+{ 0xB1CA, 0xB1CA, 0xB1CA },
+{ 0xB1CB, 0xB1CB, 0xB1CB },
+{ 0xB1CC, 0xB1CC, 0xB1CC },
+{ 0xB1CD, 0xB1CD, 0xB1CD },
+{ 0xB1CE, 0xB1CE, 0xB1CE },
+{ 0xB1CF, 0xB1CF, 0xB1CF },
+{ 0xB1D0, 0xB1D0, 0xB1D0 },
+{ 0xB1D1, 0xB1D1, 0xB1D1 },
+{ 0xB1D2, 0xB1D2, 0xB1D2 },
+{ 0xB1D3, 0xB1D3, 0xB1D3 },
+{ 0xB1D4, 0xB1D4, 0xB1D4 },
+{ 0xB1D5, 0xB1D5, 0xB1D5 },
+{ 0xB1D6, 0xB1D6, 0xB1D6 },
+{ 0xB1D7, 0xB1D7, 0xB1D7 },
+{ 0xB1D8, 0xB1D8, 0xB1D8 },
+{ 0xB1D9, 0xB1D9, 0xB1D9 },
+{ 0xB1DA, 0xB1DA, 0xB1DA },
+{ 0xB1DB, 0xB1DB, 0xB1DB },
+{ 0xB1DC, 0xB1DC, 0xB1DC },
+{ 0xB1DD, 0xB1DD, 0xB1DD },
+{ 0xB1DE, 0xB1DE, 0xB1DE },
+{ 0xB1DF, 0xB1DF, 0xB1DF },
+{ 0xB1E0, 0xB1E0, 0xB1E0 },
+{ 0xB1E1, 0xB1E1, 0xB1E1 },
+{ 0xB1E2, 0xB1E2, 0xB1E2 },
+{ 0xB1E3, 0xB1E3, 0xB1E3 },
+{ 0xB1E4, 0xB1E4, 0xB1E4 },
+{ 0xB1E5, 0xB1E5, 0xB1E5 },
+{ 0xB1E6, 0xB1E6, 0xB1E6 },
+{ 0xB1E7, 0xB1E7, 0xB1E7 },
+{ 0xB1E8, 0xB1E8, 0xB1E8 },
+{ 0xB1E9, 0xB1E9, 0xB1E9 },
+{ 0xB1EA, 0xB1EA, 0xB1EA },
+{ 0xB1EB, 0xB1EB, 0xB1EB },
+{ 0xB1EC, 0xB1EC, 0xB1EC },
+{ 0xB1ED, 0xB1ED, 0xB1ED },
+{ 0xB1EE, 0xB1EE, 0xB1EE },
+{ 0xB1EF, 0xB1EF, 0xB1EF },
+{ 0xB1F0, 0xB1F0, 0xB1F0 },
+{ 0xB1F1, 0xB1F1, 0xB1F1 },
+{ 0xB1F2, 0xB1F2, 0xB1F2 },
+{ 0xB1F3, 0xB1F3, 0xB1F3 },
+{ 0xB1F4, 0xB1F4, 0xB1F4 },
+{ 0xB1F5, 0xB1F5, 0xB1F5 },
+{ 0xB1F6, 0xB1F6, 0xB1F6 },
+{ 0xB1F7, 0xB1F7, 0xB1F7 },
+{ 0xB1F8, 0xB1F8, 0xB1F8 },
+{ 0xB1F9, 0xB1F9, 0xB1F9 },
+{ 0xB1FA, 0xB1FA, 0xB1FA },
+{ 0xB1FB, 0xB1FB, 0xB1FB },
+{ 0xB1FC, 0xB1FC, 0xB1FC },
+{ 0xB1FD, 0xB1FD, 0xB1FD },
+{ 0xB1FE, 0xB1FE, 0xB1FE },
+{ 0xB1FF, 0xB1FF, 0xB1FF },
+{ 0xB200, 0xB200, 0xB200 },
+{ 0xB201, 0xB201, 0xB201 },
+{ 0xB202, 0xB202, 0xB202 },
+{ 0xB203, 0xB203, 0xB203 },
+{ 0xB204, 0xB204, 0xB204 },
+{ 0xB205, 0xB205, 0xB205 },
+{ 0xB206, 0xB206, 0xB206 },
+{ 0xB207, 0xB207, 0xB207 },
+{ 0xB208, 0xB208, 0xB208 },
+{ 0xB209, 0xB209, 0xB209 },
+{ 0xB20A, 0xB20A, 0xB20A },
+{ 0xB20B, 0xB20B, 0xB20B },
+{ 0xB20C, 0xB20C, 0xB20C },
+{ 0xB20D, 0xB20D, 0xB20D },
+{ 0xB20E, 0xB20E, 0xB20E },
+{ 0xB20F, 0xB20F, 0xB20F },
+{ 0xB210, 0xB210, 0xB210 },
+{ 0xB211, 0xB211, 0xB211 },
+{ 0xB212, 0xB212, 0xB212 },
+{ 0xB213, 0xB213, 0xB213 },
+{ 0xB214, 0xB214, 0xB214 },
+{ 0xB215, 0xB215, 0xB215 },
+{ 0xB216, 0xB216, 0xB216 },
+{ 0xB217, 0xB217, 0xB217 },
+{ 0xB218, 0xB218, 0xB218 },
+{ 0xB219, 0xB219, 0xB219 },
+{ 0xB21A, 0xB21A, 0xB21A },
+{ 0xB21B, 0xB21B, 0xB21B },
+{ 0xB21C, 0xB21C, 0xB21C },
+{ 0xB21D, 0xB21D, 0xB21D },
+{ 0xB21E, 0xB21E, 0xB21E },
+{ 0xB21F, 0xB21F, 0xB21F },
+{ 0xB220, 0xB220, 0xB220 },
+{ 0xB221, 0xB221, 0xB221 },
+{ 0xB222, 0xB222, 0xB222 },
+{ 0xB223, 0xB223, 0xB223 },
+{ 0xB224, 0xB224, 0xB224 },
+{ 0xB225, 0xB225, 0xB225 },
+{ 0xB226, 0xB226, 0xB226 },
+{ 0xB227, 0xB227, 0xB227 },
+{ 0xB228, 0xB228, 0xB228 },
+{ 0xB229, 0xB229, 0xB229 },
+{ 0xB22A, 0xB22A, 0xB22A },
+{ 0xB22B, 0xB22B, 0xB22B },
+{ 0xB22C, 0xB22C, 0xB22C },
+{ 0xB22D, 0xB22D, 0xB22D },
+{ 0xB22E, 0xB22E, 0xB22E },
+{ 0xB22F, 0xB22F, 0xB22F },
+{ 0xB230, 0xB230, 0xB230 },
+{ 0xB231, 0xB231, 0xB231 },
+{ 0xB232, 0xB232, 0xB232 },
+{ 0xB233, 0xB233, 0xB233 },
+{ 0xB234, 0xB234, 0xB234 },
+{ 0xB235, 0xB235, 0xB235 },
+{ 0xB236, 0xB236, 0xB236 },
+{ 0xB237, 0xB237, 0xB237 },
+{ 0xB238, 0xB238, 0xB238 },
+{ 0xB239, 0xB239, 0xB239 },
+{ 0xB23A, 0xB23A, 0xB23A },
+{ 0xB23B, 0xB23B, 0xB23B },
+{ 0xB23C, 0xB23C, 0xB23C },
+{ 0xB23D, 0xB23D, 0xB23D },
+{ 0xB23E, 0xB23E, 0xB23E },
+{ 0xB23F, 0xB23F, 0xB23F },
+{ 0xB240, 0xB240, 0xB240 },
+{ 0xB241, 0xB241, 0xB241 },
+{ 0xB242, 0xB242, 0xB242 },
+{ 0xB243, 0xB243, 0xB243 },
+{ 0xB244, 0xB244, 0xB244 },
+{ 0xB245, 0xB245, 0xB245 },
+{ 0xB246, 0xB246, 0xB246 },
+{ 0xB247, 0xB247, 0xB247 },
+{ 0xB248, 0xB248, 0xB248 },
+{ 0xB249, 0xB249, 0xB249 },
+{ 0xB24A, 0xB24A, 0xB24A },
+{ 0xB24B, 0xB24B, 0xB24B },
+{ 0xB24C, 0xB24C, 0xB24C },
+{ 0xB24D, 0xB24D, 0xB24D },
+{ 0xB24E, 0xB24E, 0xB24E },
+{ 0xB24F, 0xB24F, 0xB24F },
+{ 0xB250, 0xB250, 0xB250 },
+{ 0xB251, 0xB251, 0xB251 },
+{ 0xB252, 0xB252, 0xB252 },
+{ 0xB253, 0xB253, 0xB253 },
+{ 0xB254, 0xB254, 0xB254 },
+{ 0xB255, 0xB255, 0xB255 },
+{ 0xB256, 0xB256, 0xB256 },
+{ 0xB257, 0xB257, 0xB257 },
+{ 0xB258, 0xB258, 0xB258 },
+{ 0xB259, 0xB259, 0xB259 },
+{ 0xB25A, 0xB25A, 0xB25A },
+{ 0xB25B, 0xB25B, 0xB25B },
+{ 0xB25C, 0xB25C, 0xB25C },
+{ 0xB25D, 0xB25D, 0xB25D },
+{ 0xB25E, 0xB25E, 0xB25E },
+{ 0xB25F, 0xB25F, 0xB25F },
+{ 0xB260, 0xB260, 0xB260 },
+{ 0xB261, 0xB261, 0xB261 },
+{ 0xB262, 0xB262, 0xB262 },
+{ 0xB263, 0xB263, 0xB263 },
+{ 0xB264, 0xB264, 0xB264 },
+{ 0xB265, 0xB265, 0xB265 },
+{ 0xB266, 0xB266, 0xB266 },
+{ 0xB267, 0xB267, 0xB267 },
+{ 0xB268, 0xB268, 0xB268 },
+{ 0xB269, 0xB269, 0xB269 },
+{ 0xB26A, 0xB26A, 0xB26A },
+{ 0xB26B, 0xB26B, 0xB26B },
+{ 0xB26C, 0xB26C, 0xB26C },
+{ 0xB26D, 0xB26D, 0xB26D },
+{ 0xB26E, 0xB26E, 0xB26E },
+{ 0xB26F, 0xB26F, 0xB26F },
+{ 0xB270, 0xB270, 0xB270 },
+{ 0xB271, 0xB271, 0xB271 },
+{ 0xB272, 0xB272, 0xB272 },
+{ 0xB273, 0xB273, 0xB273 },
+{ 0xB274, 0xB274, 0xB274 },
+{ 0xB275, 0xB275, 0xB275 },
+{ 0xB276, 0xB276, 0xB276 },
+{ 0xB277, 0xB277, 0xB277 },
+{ 0xB278, 0xB278, 0xB278 },
+{ 0xB279, 0xB279, 0xB279 },
+{ 0xB27A, 0xB27A, 0xB27A },
+{ 0xB27B, 0xB27B, 0xB27B },
+{ 0xB27C, 0xB27C, 0xB27C },
+{ 0xB27D, 0xB27D, 0xB27D },
+{ 0xB27E, 0xB27E, 0xB27E },
+{ 0xB27F, 0xB27F, 0xB27F },
+{ 0xB280, 0xB280, 0xB280 },
+{ 0xB281, 0xB281, 0xB281 },
+{ 0xB282, 0xB282, 0xB282 },
+{ 0xB283, 0xB283, 0xB283 },
+{ 0xB284, 0xB284, 0xB284 },
+{ 0xB285, 0xB285, 0xB285 },
+{ 0xB286, 0xB286, 0xB286 },
+{ 0xB287, 0xB287, 0xB287 },
+{ 0xB288, 0xB288, 0xB288 },
+{ 0xB289, 0xB289, 0xB289 },
+{ 0xB28A, 0xB28A, 0xB28A },
+{ 0xB28B, 0xB28B, 0xB28B },
+{ 0xB28C, 0xB28C, 0xB28C },
+{ 0xB28D, 0xB28D, 0xB28D },
+{ 0xB28E, 0xB28E, 0xB28E },
+{ 0xB28F, 0xB28F, 0xB28F },
+{ 0xB290, 0xB290, 0xB290 },
+{ 0xB291, 0xB291, 0xB291 },
+{ 0xB292, 0xB292, 0xB292 },
+{ 0xB293, 0xB293, 0xB293 },
+{ 0xB294, 0xB294, 0xB294 },
+{ 0xB295, 0xB295, 0xB295 },
+{ 0xB296, 0xB296, 0xB296 },
+{ 0xB297, 0xB297, 0xB297 },
+{ 0xB298, 0xB298, 0xB298 },
+{ 0xB299, 0xB299, 0xB299 },
+{ 0xB29A, 0xB29A, 0xB29A },
+{ 0xB29B, 0xB29B, 0xB29B },
+{ 0xB29C, 0xB29C, 0xB29C },
+{ 0xB29D, 0xB29D, 0xB29D },
+{ 0xB29E, 0xB29E, 0xB29E },
+{ 0xB29F, 0xB29F, 0xB29F },
+{ 0xB2A0, 0xB2A0, 0xB2A0 },
+{ 0xB2A1, 0xB2A1, 0xB2A1 },
+{ 0xB2A2, 0xB2A2, 0xB2A2 },
+{ 0xB2A3, 0xB2A3, 0xB2A3 },
+{ 0xB2A4, 0xB2A4, 0xB2A4 },
+{ 0xB2A5, 0xB2A5, 0xB2A5 },
+{ 0xB2A6, 0xB2A6, 0xB2A6 },
+{ 0xB2A7, 0xB2A7, 0xB2A7 },
+{ 0xB2A8, 0xB2A8, 0xB2A8 },
+{ 0xB2A9, 0xB2A9, 0xB2A9 },
+{ 0xB2AA, 0xB2AA, 0xB2AA },
+{ 0xB2AB, 0xB2AB, 0xB2AB },
+{ 0xB2AC, 0xB2AC, 0xB2AC },
+{ 0xB2AD, 0xB2AD, 0xB2AD },
+{ 0xB2AE, 0xB2AE, 0xB2AE },
+{ 0xB2AF, 0xB2AF, 0xB2AF },
+{ 0xB2B0, 0xB2B0, 0xB2B0 },
+{ 0xB2B1, 0xB2B1, 0xB2B1 },
+{ 0xB2B2, 0xB2B2, 0xB2B2 },
+{ 0xB2B3, 0xB2B3, 0xB2B3 },
+{ 0xB2B4, 0xB2B4, 0xB2B4 },
+{ 0xB2B5, 0xB2B5, 0xB2B5 },
+{ 0xB2B6, 0xB2B6, 0xB2B6 },
+{ 0xB2B7, 0xB2B7, 0xB2B7 },
+{ 0xB2B8, 0xB2B8, 0xB2B8 },
+{ 0xB2B9, 0xB2B9, 0xB2B9 },
+{ 0xB2BA, 0xB2BA, 0xB2BA },
+{ 0xB2BB, 0xB2BB, 0xB2BB },
+{ 0xB2BC, 0xB2BC, 0xB2BC },
+{ 0xB2BD, 0xB2BD, 0xB2BD },
+{ 0xB2BE, 0xB2BE, 0xB2BE },
+{ 0xB2BF, 0xB2BF, 0xB2BF },
+{ 0xB2C0, 0xB2C0, 0xB2C0 },
+{ 0xB2C1, 0xB2C1, 0xB2C1 },
+{ 0xB2C2, 0xB2C2, 0xB2C2 },
+{ 0xB2C3, 0xB2C3, 0xB2C3 },
+{ 0xB2C4, 0xB2C4, 0xB2C4 },
+{ 0xB2C5, 0xB2C5, 0xB2C5 },
+{ 0xB2C6, 0xB2C6, 0xB2C6 },
+{ 0xB2C7, 0xB2C7, 0xB2C7 },
+{ 0xB2C8, 0xB2C8, 0xB2C8 },
+{ 0xB2C9, 0xB2C9, 0xB2C9 },
+{ 0xB2CA, 0xB2CA, 0xB2CA },
+{ 0xB2CB, 0xB2CB, 0xB2CB },
+{ 0xB2CC, 0xB2CC, 0xB2CC },
+{ 0xB2CD, 0xB2CD, 0xB2CD },
+{ 0xB2CE, 0xB2CE, 0xB2CE },
+{ 0xB2CF, 0xB2CF, 0xB2CF },
+{ 0xB2D0, 0xB2D0, 0xB2D0 },
+{ 0xB2D1, 0xB2D1, 0xB2D1 },
+{ 0xB2D2, 0xB2D2, 0xB2D2 },
+{ 0xB2D3, 0xB2D3, 0xB2D3 },
+{ 0xB2D4, 0xB2D4, 0xB2D4 },
+{ 0xB2D5, 0xB2D5, 0xB2D5 },
+{ 0xB2D6, 0xB2D6, 0xB2D6 },
+{ 0xB2D7, 0xB2D7, 0xB2D7 },
+{ 0xB2D8, 0xB2D8, 0xB2D8 },
+{ 0xB2D9, 0xB2D9, 0xB2D9 },
+{ 0xB2DA, 0xB2DA, 0xB2DA },
+{ 0xB2DB, 0xB2DB, 0xB2DB },
+{ 0xB2DC, 0xB2DC, 0xB2DC },
+{ 0xB2DD, 0xB2DD, 0xB2DD },
+{ 0xB2DE, 0xB2DE, 0xB2DE },
+{ 0xB2DF, 0xB2DF, 0xB2DF },
+{ 0xB2E0, 0xB2E0, 0xB2E0 },
+{ 0xB2E1, 0xB2E1, 0xB2E1 },
+{ 0xB2E2, 0xB2E2, 0xB2E2 },
+{ 0xB2E3, 0xB2E3, 0xB2E3 },
+{ 0xB2E4, 0xB2E4, 0xB2E4 },
+{ 0xB2E5, 0xB2E5, 0xB2E5 },
+{ 0xB2E6, 0xB2E6, 0xB2E6 },
+{ 0xB2E7, 0xB2E7, 0xB2E7 },
+{ 0xB2E8, 0xB2E8, 0xB2E8 },
+{ 0xB2E9, 0xB2E9, 0xB2E9 },
+{ 0xB2EA, 0xB2EA, 0xB2EA },
+{ 0xB2EB, 0xB2EB, 0xB2EB },
+{ 0xB2EC, 0xB2EC, 0xB2EC },
+{ 0xB2ED, 0xB2ED, 0xB2ED },
+{ 0xB2EE, 0xB2EE, 0xB2EE },
+{ 0xB2EF, 0xB2EF, 0xB2EF },
+{ 0xB2F0, 0xB2F0, 0xB2F0 },
+{ 0xB2F1, 0xB2F1, 0xB2F1 },
+{ 0xB2F2, 0xB2F2, 0xB2F2 },
+{ 0xB2F3, 0xB2F3, 0xB2F3 },
+{ 0xB2F4, 0xB2F4, 0xB2F4 },
+{ 0xB2F5, 0xB2F5, 0xB2F5 },
+{ 0xB2F6, 0xB2F6, 0xB2F6 },
+{ 0xB2F7, 0xB2F7, 0xB2F7 },
+{ 0xB2F8, 0xB2F8, 0xB2F8 },
+{ 0xB2F9, 0xB2F9, 0xB2F9 },
+{ 0xB2FA, 0xB2FA, 0xB2FA },
+{ 0xB2FB, 0xB2FB, 0xB2FB },
+{ 0xB2FC, 0xB2FC, 0xB2FC },
+{ 0xB2FD, 0xB2FD, 0xB2FD },
+{ 0xB2FE, 0xB2FE, 0xB2FE },
+{ 0xB2FF, 0xB2FF, 0xB2FF },
+{ 0xB300, 0xB300, 0xB300 },
+{ 0xB301, 0xB301, 0xB301 },
+{ 0xB302, 0xB302, 0xB302 },
+{ 0xB303, 0xB303, 0xB303 },
+{ 0xB304, 0xB304, 0xB304 },
+{ 0xB305, 0xB305, 0xB305 },
+{ 0xB306, 0xB306, 0xB306 },
+{ 0xB307, 0xB307, 0xB307 },
+{ 0xB308, 0xB308, 0xB308 },
+{ 0xB309, 0xB309, 0xB309 },
+{ 0xB30A, 0xB30A, 0xB30A },
+{ 0xB30B, 0xB30B, 0xB30B },
+{ 0xB30C, 0xB30C, 0xB30C },
+{ 0xB30D, 0xB30D, 0xB30D },
+{ 0xB30E, 0xB30E, 0xB30E },
+{ 0xB30F, 0xB30F, 0xB30F },
+{ 0xB310, 0xB310, 0xB310 },
+{ 0xB311, 0xB311, 0xB311 },
+{ 0xB312, 0xB312, 0xB312 },
+{ 0xB313, 0xB313, 0xB313 },
+{ 0xB314, 0xB314, 0xB314 },
+{ 0xB315, 0xB315, 0xB315 },
+{ 0xB316, 0xB316, 0xB316 },
+{ 0xB317, 0xB317, 0xB317 },
+{ 0xB318, 0xB318, 0xB318 },
+{ 0xB319, 0xB319, 0xB319 },
+{ 0xB31A, 0xB31A, 0xB31A },
+{ 0xB31B, 0xB31B, 0xB31B },
+{ 0xB31C, 0xB31C, 0xB31C },
+{ 0xB31D, 0xB31D, 0xB31D },
+{ 0xB31E, 0xB31E, 0xB31E },
+{ 0xB31F, 0xB31F, 0xB31F },
+{ 0xB320, 0xB320, 0xB320 },
+{ 0xB321, 0xB321, 0xB321 },
+{ 0xB322, 0xB322, 0xB322 },
+{ 0xB323, 0xB323, 0xB323 },
+{ 0xB324, 0xB324, 0xB324 },
+{ 0xB325, 0xB325, 0xB325 },
+{ 0xB326, 0xB326, 0xB326 },
+{ 0xB327, 0xB327, 0xB327 },
+{ 0xB328, 0xB328, 0xB328 },
+{ 0xB329, 0xB329, 0xB329 },
+{ 0xB32A, 0xB32A, 0xB32A },
+{ 0xB32B, 0xB32B, 0xB32B },
+{ 0xB32C, 0xB32C, 0xB32C },
+{ 0xB32D, 0xB32D, 0xB32D },
+{ 0xB32E, 0xB32E, 0xB32E },
+{ 0xB32F, 0xB32F, 0xB32F },
+{ 0xB330, 0xB330, 0xB330 },
+{ 0xB331, 0xB331, 0xB331 },
+{ 0xB332, 0xB332, 0xB332 },
+{ 0xB333, 0xB333, 0xB333 },
+{ 0xB334, 0xB334, 0xB334 },
+{ 0xB335, 0xB335, 0xB335 },
+{ 0xB336, 0xB336, 0xB336 },
+{ 0xB337, 0xB337, 0xB337 },
+{ 0xB338, 0xB338, 0xB338 },
+{ 0xB339, 0xB339, 0xB339 },
+{ 0xB33A, 0xB33A, 0xB33A },
+{ 0xB33B, 0xB33B, 0xB33B },
+{ 0xB33C, 0xB33C, 0xB33C },
+{ 0xB33D, 0xB33D, 0xB33D },
+{ 0xB33E, 0xB33E, 0xB33E },
+{ 0xB33F, 0xB33F, 0xB33F },
+{ 0xB340, 0xB340, 0xB340 },
+{ 0xB341, 0xB341, 0xB341 },
+{ 0xB342, 0xB342, 0xB342 },
+{ 0xB343, 0xB343, 0xB343 },
+{ 0xB344, 0xB344, 0xB344 },
+{ 0xB345, 0xB345, 0xB345 },
+{ 0xB346, 0xB346, 0xB346 },
+{ 0xB347, 0xB347, 0xB347 },
+{ 0xB348, 0xB348, 0xB348 },
+{ 0xB349, 0xB349, 0xB349 },
+{ 0xB34A, 0xB34A, 0xB34A },
+{ 0xB34B, 0xB34B, 0xB34B },
+{ 0xB34C, 0xB34C, 0xB34C },
+{ 0xB34D, 0xB34D, 0xB34D },
+{ 0xB34E, 0xB34E, 0xB34E },
+{ 0xB34F, 0xB34F, 0xB34F },
+{ 0xB350, 0xB350, 0xB350 },
+{ 0xB351, 0xB351, 0xB351 },
+{ 0xB352, 0xB352, 0xB352 },
+{ 0xB353, 0xB353, 0xB353 },
+{ 0xB354, 0xB354, 0xB354 },
+{ 0xB355, 0xB355, 0xB355 },
+{ 0xB356, 0xB356, 0xB356 },
+{ 0xB357, 0xB357, 0xB357 },
+{ 0xB358, 0xB358, 0xB358 },
+{ 0xB359, 0xB359, 0xB359 },
+{ 0xB35A, 0xB35A, 0xB35A },
+{ 0xB35B, 0xB35B, 0xB35B },
+{ 0xB35C, 0xB35C, 0xB35C },
+{ 0xB35D, 0xB35D, 0xB35D },
+{ 0xB35E, 0xB35E, 0xB35E },
+{ 0xB35F, 0xB35F, 0xB35F },
+{ 0xB360, 0xB360, 0xB360 },
+{ 0xB361, 0xB361, 0xB361 },
+{ 0xB362, 0xB362, 0xB362 },
+{ 0xB363, 0xB363, 0xB363 },
+{ 0xB364, 0xB364, 0xB364 },
+{ 0xB365, 0xB365, 0xB365 },
+{ 0xB366, 0xB366, 0xB366 },
+{ 0xB367, 0xB367, 0xB367 },
+{ 0xB368, 0xB368, 0xB368 },
+{ 0xB369, 0xB369, 0xB369 },
+{ 0xB36A, 0xB36A, 0xB36A },
+{ 0xB36B, 0xB36B, 0xB36B },
+{ 0xB36C, 0xB36C, 0xB36C },
+{ 0xB36D, 0xB36D, 0xB36D },
+{ 0xB36E, 0xB36E, 0xB36E },
+{ 0xB36F, 0xB36F, 0xB36F },
+{ 0xB370, 0xB370, 0xB370 },
+{ 0xB371, 0xB371, 0xB371 },
+{ 0xB372, 0xB372, 0xB372 },
+{ 0xB373, 0xB373, 0xB373 },
+{ 0xB374, 0xB374, 0xB374 },
+{ 0xB375, 0xB375, 0xB375 },
+{ 0xB376, 0xB376, 0xB376 },
+{ 0xB377, 0xB377, 0xB377 },
+{ 0xB378, 0xB378, 0xB378 },
+{ 0xB379, 0xB379, 0xB379 },
+{ 0xB37A, 0xB37A, 0xB37A },
+{ 0xB37B, 0xB37B, 0xB37B },
+{ 0xB37C, 0xB37C, 0xB37C },
+{ 0xB37D, 0xB37D, 0xB37D },
+{ 0xB37E, 0xB37E, 0xB37E },
+{ 0xB37F, 0xB37F, 0xB37F },
+{ 0xB380, 0xB380, 0xB380 },
+{ 0xB381, 0xB381, 0xB381 },
+{ 0xB382, 0xB382, 0xB382 },
+{ 0xB383, 0xB383, 0xB383 },
+{ 0xB384, 0xB384, 0xB384 },
+{ 0xB385, 0xB385, 0xB385 },
+{ 0xB386, 0xB386, 0xB386 },
+{ 0xB387, 0xB387, 0xB387 },
+{ 0xB388, 0xB388, 0xB388 },
+{ 0xB389, 0xB389, 0xB389 },
+{ 0xB38A, 0xB38A, 0xB38A },
+{ 0xB38B, 0xB38B, 0xB38B },
+{ 0xB38C, 0xB38C, 0xB38C },
+{ 0xB38D, 0xB38D, 0xB38D },
+{ 0xB38E, 0xB38E, 0xB38E },
+{ 0xB38F, 0xB38F, 0xB38F },
+{ 0xB390, 0xB390, 0xB390 },
+{ 0xB391, 0xB391, 0xB391 },
+{ 0xB392, 0xB392, 0xB392 },
+{ 0xB393, 0xB393, 0xB393 },
+{ 0xB394, 0xB394, 0xB394 },
+{ 0xB395, 0xB395, 0xB395 },
+{ 0xB396, 0xB396, 0xB396 },
+{ 0xB397, 0xB397, 0xB397 },
+{ 0xB398, 0xB398, 0xB398 },
+{ 0xB399, 0xB399, 0xB399 },
+{ 0xB39A, 0xB39A, 0xB39A },
+{ 0xB39B, 0xB39B, 0xB39B },
+{ 0xB39C, 0xB39C, 0xB39C },
+{ 0xB39D, 0xB39D, 0xB39D },
+{ 0xB39E, 0xB39E, 0xB39E },
+{ 0xB39F, 0xB39F, 0xB39F },
+{ 0xB3A0, 0xB3A0, 0xB3A0 },
+{ 0xB3A1, 0xB3A1, 0xB3A1 },
+{ 0xB3A2, 0xB3A2, 0xB3A2 },
+{ 0xB3A3, 0xB3A3, 0xB3A3 },
+{ 0xB3A4, 0xB3A4, 0xB3A4 },
+{ 0xB3A5, 0xB3A5, 0xB3A5 },
+{ 0xB3A6, 0xB3A6, 0xB3A6 },
+{ 0xB3A7, 0xB3A7, 0xB3A7 },
+{ 0xB3A8, 0xB3A8, 0xB3A8 },
+{ 0xB3A9, 0xB3A9, 0xB3A9 },
+{ 0xB3AA, 0xB3AA, 0xB3AA },
+{ 0xB3AB, 0xB3AB, 0xB3AB },
+{ 0xB3AC, 0xB3AC, 0xB3AC },
+{ 0xB3AD, 0xB3AD, 0xB3AD },
+{ 0xB3AE, 0xB3AE, 0xB3AE },
+{ 0xB3AF, 0xB3AF, 0xB3AF },
+{ 0xB3B0, 0xB3B0, 0xB3B0 },
+{ 0xB3B1, 0xB3B1, 0xB3B1 },
+{ 0xB3B2, 0xB3B2, 0xB3B2 },
+{ 0xB3B3, 0xB3B3, 0xB3B3 },
+{ 0xB3B4, 0xB3B4, 0xB3B4 },
+{ 0xB3B5, 0xB3B5, 0xB3B5 },
+{ 0xB3B6, 0xB3B6, 0xB3B6 },
+{ 0xB3B7, 0xB3B7, 0xB3B7 },
+{ 0xB3B8, 0xB3B8, 0xB3B8 },
+{ 0xB3B9, 0xB3B9, 0xB3B9 },
+{ 0xB3BA, 0xB3BA, 0xB3BA },
+{ 0xB3BB, 0xB3BB, 0xB3BB },
+{ 0xB3BC, 0xB3BC, 0xB3BC },
+{ 0xB3BD, 0xB3BD, 0xB3BD },
+{ 0xB3BE, 0xB3BE, 0xB3BE },
+{ 0xB3BF, 0xB3BF, 0xB3BF },
+{ 0xB3C0, 0xB3C0, 0xB3C0 },
+{ 0xB3C1, 0xB3C1, 0xB3C1 },
+{ 0xB3C2, 0xB3C2, 0xB3C2 },
+{ 0xB3C3, 0xB3C3, 0xB3C3 },
+{ 0xB3C4, 0xB3C4, 0xB3C4 },
+{ 0xB3C5, 0xB3C5, 0xB3C5 },
+{ 0xB3C6, 0xB3C6, 0xB3C6 },
+{ 0xB3C7, 0xB3C7, 0xB3C7 },
+{ 0xB3C8, 0xB3C8, 0xB3C8 },
+{ 0xB3C9, 0xB3C9, 0xB3C9 },
+{ 0xB3CA, 0xB3CA, 0xB3CA },
+{ 0xB3CB, 0xB3CB, 0xB3CB },
+{ 0xB3CC, 0xB3CC, 0xB3CC },
+{ 0xB3CD, 0xB3CD, 0xB3CD },
+{ 0xB3CE, 0xB3CE, 0xB3CE },
+{ 0xB3CF, 0xB3CF, 0xB3CF },
+{ 0xB3D0, 0xB3D0, 0xB3D0 },
+{ 0xB3D1, 0xB3D1, 0xB3D1 },
+{ 0xB3D2, 0xB3D2, 0xB3D2 },
+{ 0xB3D3, 0xB3D3, 0xB3D3 },
+{ 0xB3D4, 0xB3D4, 0xB3D4 },
+{ 0xB3D5, 0xB3D5, 0xB3D5 },
+{ 0xB3D6, 0xB3D6, 0xB3D6 },
+{ 0xB3D7, 0xB3D7, 0xB3D7 },
+{ 0xB3D8, 0xB3D8, 0xB3D8 },
+{ 0xB3D9, 0xB3D9, 0xB3D9 },
+{ 0xB3DA, 0xB3DA, 0xB3DA },
+{ 0xB3DB, 0xB3DB, 0xB3DB },
+{ 0xB3DC, 0xB3DC, 0xB3DC },
+{ 0xB3DD, 0xB3DD, 0xB3DD },
+{ 0xB3DE, 0xB3DE, 0xB3DE },
+{ 0xB3DF, 0xB3DF, 0xB3DF },
+{ 0xB3E0, 0xB3E0, 0xB3E0 },
+{ 0xB3E1, 0xB3E1, 0xB3E1 },
+{ 0xB3E2, 0xB3E2, 0xB3E2 },
+{ 0xB3E3, 0xB3E3, 0xB3E3 },
+{ 0xB3E4, 0xB3E4, 0xB3E4 },
+{ 0xB3E5, 0xB3E5, 0xB3E5 },
+{ 0xB3E6, 0xB3E6, 0xB3E6 },
+{ 0xB3E7, 0xB3E7, 0xB3E7 },
+{ 0xB3E8, 0xB3E8, 0xB3E8 },
+{ 0xB3E9, 0xB3E9, 0xB3E9 },
+{ 0xB3EA, 0xB3EA, 0xB3EA },
+{ 0xB3EB, 0xB3EB, 0xB3EB },
+{ 0xB3EC, 0xB3EC, 0xB3EC },
+{ 0xB3ED, 0xB3ED, 0xB3ED },
+{ 0xB3EE, 0xB3EE, 0xB3EE },
+{ 0xB3EF, 0xB3EF, 0xB3EF },
+{ 0xB3F0, 0xB3F0, 0xB3F0 },
+{ 0xB3F1, 0xB3F1, 0xB3F1 },
+{ 0xB3F2, 0xB3F2, 0xB3F2 },
+{ 0xB3F3, 0xB3F3, 0xB3F3 },
+{ 0xB3F4, 0xB3F4, 0xB3F4 },
+{ 0xB3F5, 0xB3F5, 0xB3F5 },
+{ 0xB3F6, 0xB3F6, 0xB3F6 },
+{ 0xB3F7, 0xB3F7, 0xB3F7 },
+{ 0xB3F8, 0xB3F8, 0xB3F8 },
+{ 0xB3F9, 0xB3F9, 0xB3F9 },
+{ 0xB3FA, 0xB3FA, 0xB3FA },
+{ 0xB3FB, 0xB3FB, 0xB3FB },
+{ 0xB3FC, 0xB3FC, 0xB3FC },
+{ 0xB3FD, 0xB3FD, 0xB3FD },
+{ 0xB3FE, 0xB3FE, 0xB3FE },
+{ 0xB3FF, 0xB3FF, 0xB3FF },
+{ 0xB400, 0xB400, 0xB400 },
+{ 0xB401, 0xB401, 0xB401 },
+{ 0xB402, 0xB402, 0xB402 },
+{ 0xB403, 0xB403, 0xB403 },
+{ 0xB404, 0xB404, 0xB404 },
+{ 0xB405, 0xB405, 0xB405 },
+{ 0xB406, 0xB406, 0xB406 },
+{ 0xB407, 0xB407, 0xB407 },
+{ 0xB408, 0xB408, 0xB408 },
+{ 0xB409, 0xB409, 0xB409 },
+{ 0xB40A, 0xB40A, 0xB40A },
+{ 0xB40B, 0xB40B, 0xB40B },
+{ 0xB40C, 0xB40C, 0xB40C },
+{ 0xB40D, 0xB40D, 0xB40D },
+{ 0xB40E, 0xB40E, 0xB40E },
+{ 0xB40F, 0xB40F, 0xB40F },
+{ 0xB410, 0xB410, 0xB410 },
+{ 0xB411, 0xB411, 0xB411 },
+{ 0xB412, 0xB412, 0xB412 },
+{ 0xB413, 0xB413, 0xB413 },
+{ 0xB414, 0xB414, 0xB414 },
+{ 0xB415, 0xB415, 0xB415 },
+{ 0xB416, 0xB416, 0xB416 },
+{ 0xB417, 0xB417, 0xB417 },
+{ 0xB418, 0xB418, 0xB418 },
+{ 0xB419, 0xB419, 0xB419 },
+{ 0xB41A, 0xB41A, 0xB41A },
+{ 0xB41B, 0xB41B, 0xB41B },
+{ 0xB41C, 0xB41C, 0xB41C },
+{ 0xB41D, 0xB41D, 0xB41D },
+{ 0xB41E, 0xB41E, 0xB41E },
+{ 0xB41F, 0xB41F, 0xB41F },
+{ 0xB420, 0xB420, 0xB420 },
+{ 0xB421, 0xB421, 0xB421 },
+{ 0xB422, 0xB422, 0xB422 },
+{ 0xB423, 0xB423, 0xB423 },
+{ 0xB424, 0xB424, 0xB424 },
+{ 0xB425, 0xB425, 0xB425 },
+{ 0xB426, 0xB426, 0xB426 },
+{ 0xB427, 0xB427, 0xB427 },
+{ 0xB428, 0xB428, 0xB428 },
+{ 0xB429, 0xB429, 0xB429 },
+{ 0xB42A, 0xB42A, 0xB42A },
+{ 0xB42B, 0xB42B, 0xB42B },
+{ 0xB42C, 0xB42C, 0xB42C },
+{ 0xB42D, 0xB42D, 0xB42D },
+{ 0xB42E, 0xB42E, 0xB42E },
+{ 0xB42F, 0xB42F, 0xB42F },
+{ 0xB430, 0xB430, 0xB430 },
+{ 0xB431, 0xB431, 0xB431 },
+{ 0xB432, 0xB432, 0xB432 },
+{ 0xB433, 0xB433, 0xB433 },
+{ 0xB434, 0xB434, 0xB434 },
+{ 0xB435, 0xB435, 0xB435 },
+{ 0xB436, 0xB436, 0xB436 },
+{ 0xB437, 0xB437, 0xB437 },
+{ 0xB438, 0xB438, 0xB438 },
+{ 0xB439, 0xB439, 0xB439 },
+{ 0xB43A, 0xB43A, 0xB43A },
+{ 0xB43B, 0xB43B, 0xB43B },
+{ 0xB43C, 0xB43C, 0xB43C },
+{ 0xB43D, 0xB43D, 0xB43D },
+{ 0xB43E, 0xB43E, 0xB43E },
+{ 0xB43F, 0xB43F, 0xB43F },
+{ 0xB440, 0xB440, 0xB440 },
+{ 0xB441, 0xB441, 0xB441 },
+{ 0xB442, 0xB442, 0xB442 },
+{ 0xB443, 0xB443, 0xB443 },
+{ 0xB444, 0xB444, 0xB444 },
+{ 0xB445, 0xB445, 0xB445 },
+{ 0xB446, 0xB446, 0xB446 },
+{ 0xB447, 0xB447, 0xB447 },
+{ 0xB448, 0xB448, 0xB448 },
+{ 0xB449, 0xB449, 0xB449 },
+{ 0xB44A, 0xB44A, 0xB44A },
+{ 0xB44B, 0xB44B, 0xB44B },
+{ 0xB44C, 0xB44C, 0xB44C },
+{ 0xB44D, 0xB44D, 0xB44D },
+{ 0xB44E, 0xB44E, 0xB44E },
+{ 0xB44F, 0xB44F, 0xB44F },
+{ 0xB450, 0xB450, 0xB450 },
+{ 0xB451, 0xB451, 0xB451 },
+{ 0xB452, 0xB452, 0xB452 },
+{ 0xB453, 0xB453, 0xB453 },
+{ 0xB454, 0xB454, 0xB454 },
+{ 0xB455, 0xB455, 0xB455 },
+{ 0xB456, 0xB456, 0xB456 },
+{ 0xB457, 0xB457, 0xB457 },
+{ 0xB458, 0xB458, 0xB458 },
+{ 0xB459, 0xB459, 0xB459 },
+{ 0xB45A, 0xB45A, 0xB45A },
+{ 0xB45B, 0xB45B, 0xB45B },
+{ 0xB45C, 0xB45C, 0xB45C },
+{ 0xB45D, 0xB45D, 0xB45D },
+{ 0xB45E, 0xB45E, 0xB45E },
+{ 0xB45F, 0xB45F, 0xB45F },
+{ 0xB460, 0xB460, 0xB460 },
+{ 0xB461, 0xB461, 0xB461 },
+{ 0xB462, 0xB462, 0xB462 },
+{ 0xB463, 0xB463, 0xB463 },
+{ 0xB464, 0xB464, 0xB464 },
+{ 0xB465, 0xB465, 0xB465 },
+{ 0xB466, 0xB466, 0xB466 },
+{ 0xB467, 0xB467, 0xB467 },
+{ 0xB468, 0xB468, 0xB468 },
+{ 0xB469, 0xB469, 0xB469 },
+{ 0xB46A, 0xB46A, 0xB46A },
+{ 0xB46B, 0xB46B, 0xB46B },
+{ 0xB46C, 0xB46C, 0xB46C },
+{ 0xB46D, 0xB46D, 0xB46D },
+{ 0xB46E, 0xB46E, 0xB46E },
+{ 0xB46F, 0xB46F, 0xB46F },
+{ 0xB470, 0xB470, 0xB470 },
+{ 0xB471, 0xB471, 0xB471 },
+{ 0xB472, 0xB472, 0xB472 },
+{ 0xB473, 0xB473, 0xB473 },
+{ 0xB474, 0xB474, 0xB474 },
+{ 0xB475, 0xB475, 0xB475 },
+{ 0xB476, 0xB476, 0xB476 },
+{ 0xB477, 0xB477, 0xB477 },
+{ 0xB478, 0xB478, 0xB478 },
+{ 0xB479, 0xB479, 0xB479 },
+{ 0xB47A, 0xB47A, 0xB47A },
+{ 0xB47B, 0xB47B, 0xB47B },
+{ 0xB47C, 0xB47C, 0xB47C },
+{ 0xB47D, 0xB47D, 0xB47D },
+{ 0xB47E, 0xB47E, 0xB47E },
+{ 0xB47F, 0xB47F, 0xB47F },
+{ 0xB480, 0xB480, 0xB480 },
+{ 0xB481, 0xB481, 0xB481 },
+{ 0xB482, 0xB482, 0xB482 },
+{ 0xB483, 0xB483, 0xB483 },
+{ 0xB484, 0xB484, 0xB484 },
+{ 0xB485, 0xB485, 0xB485 },
+{ 0xB486, 0xB486, 0xB486 },
+{ 0xB487, 0xB487, 0xB487 },
+{ 0xB488, 0xB488, 0xB488 },
+{ 0xB489, 0xB489, 0xB489 },
+{ 0xB48A, 0xB48A, 0xB48A },
+{ 0xB48B, 0xB48B, 0xB48B },
+{ 0xB48C, 0xB48C, 0xB48C },
+{ 0xB48D, 0xB48D, 0xB48D },
+{ 0xB48E, 0xB48E, 0xB48E },
+{ 0xB48F, 0xB48F, 0xB48F },
+{ 0xB490, 0xB490, 0xB490 },
+{ 0xB491, 0xB491, 0xB491 },
+{ 0xB492, 0xB492, 0xB492 },
+{ 0xB493, 0xB493, 0xB493 },
+{ 0xB494, 0xB494, 0xB494 },
+{ 0xB495, 0xB495, 0xB495 },
+{ 0xB496, 0xB496, 0xB496 },
+{ 0xB497, 0xB497, 0xB497 },
+{ 0xB498, 0xB498, 0xB498 },
+{ 0xB499, 0xB499, 0xB499 },
+{ 0xB49A, 0xB49A, 0xB49A },
+{ 0xB49B, 0xB49B, 0xB49B },
+{ 0xB49C, 0xB49C, 0xB49C },
+{ 0xB49D, 0xB49D, 0xB49D },
+{ 0xB49E, 0xB49E, 0xB49E },
+{ 0xB49F, 0xB49F, 0xB49F },
+{ 0xB4A0, 0xB4A0, 0xB4A0 },
+{ 0xB4A1, 0xB4A1, 0xB4A1 },
+{ 0xB4A2, 0xB4A2, 0xB4A2 },
+{ 0xB4A3, 0xB4A3, 0xB4A3 },
+{ 0xB4A4, 0xB4A4, 0xB4A4 },
+{ 0xB4A5, 0xB4A5, 0xB4A5 },
+{ 0xB4A6, 0xB4A6, 0xB4A6 },
+{ 0xB4A7, 0xB4A7, 0xB4A7 },
+{ 0xB4A8, 0xB4A8, 0xB4A8 },
+{ 0xB4A9, 0xB4A9, 0xB4A9 },
+{ 0xB4AA, 0xB4AA, 0xB4AA },
+{ 0xB4AB, 0xB4AB, 0xB4AB },
+{ 0xB4AC, 0xB4AC, 0xB4AC },
+{ 0xB4AD, 0xB4AD, 0xB4AD },
+{ 0xB4AE, 0xB4AE, 0xB4AE },
+{ 0xB4AF, 0xB4AF, 0xB4AF },
+{ 0xB4B0, 0xB4B0, 0xB4B0 },
+{ 0xB4B1, 0xB4B1, 0xB4B1 },
+{ 0xB4B2, 0xB4B2, 0xB4B2 },
+{ 0xB4B3, 0xB4B3, 0xB4B3 },
+{ 0xB4B4, 0xB4B4, 0xB4B4 },
+{ 0xB4B5, 0xB4B5, 0xB4B5 },
+{ 0xB4B6, 0xB4B6, 0xB4B6 },
+{ 0xB4B7, 0xB4B7, 0xB4B7 },
+{ 0xB4B8, 0xB4B8, 0xB4B8 },
+{ 0xB4B9, 0xB4B9, 0xB4B9 },
+{ 0xB4BA, 0xB4BA, 0xB4BA },
+{ 0xB4BB, 0xB4BB, 0xB4BB },
+{ 0xB4BC, 0xB4BC, 0xB4BC },
+{ 0xB4BD, 0xB4BD, 0xB4BD },
+{ 0xB4BE, 0xB4BE, 0xB4BE },
+{ 0xB4BF, 0xB4BF, 0xB4BF },
+{ 0xB4C0, 0xB4C0, 0xB4C0 },
+{ 0xB4C1, 0xB4C1, 0xB4C1 },
+{ 0xB4C2, 0xB4C2, 0xB4C2 },
+{ 0xB4C3, 0xB4C3, 0xB4C3 },
+{ 0xB4C4, 0xB4C4, 0xB4C4 },
+{ 0xB4C5, 0xB4C5, 0xB4C5 },
+{ 0xB4C6, 0xB4C6, 0xB4C6 },
+{ 0xB4C7, 0xB4C7, 0xB4C7 },
+{ 0xB4C8, 0xB4C8, 0xB4C8 },
+{ 0xB4C9, 0xB4C9, 0xB4C9 },
+{ 0xB4CA, 0xB4CA, 0xB4CA },
+{ 0xB4CB, 0xB4CB, 0xB4CB },
+{ 0xB4CC, 0xB4CC, 0xB4CC },
+{ 0xB4CD, 0xB4CD, 0xB4CD },
+{ 0xB4CE, 0xB4CE, 0xB4CE },
+{ 0xB4CF, 0xB4CF, 0xB4CF },
+{ 0xB4D0, 0xB4D0, 0xB4D0 },
+{ 0xB4D1, 0xB4D1, 0xB4D1 },
+{ 0xB4D2, 0xB4D2, 0xB4D2 },
+{ 0xB4D3, 0xB4D3, 0xB4D3 },
+{ 0xB4D4, 0xB4D4, 0xB4D4 },
+{ 0xB4D5, 0xB4D5, 0xB4D5 },
+{ 0xB4D6, 0xB4D6, 0xB4D6 },
+{ 0xB4D7, 0xB4D7, 0xB4D7 },
+{ 0xB4D8, 0xB4D8, 0xB4D8 },
+{ 0xB4D9, 0xB4D9, 0xB4D9 },
+{ 0xB4DA, 0xB4DA, 0xB4DA },
+{ 0xB4DB, 0xB4DB, 0xB4DB },
+{ 0xB4DC, 0xB4DC, 0xB4DC },
+{ 0xB4DD, 0xB4DD, 0xB4DD },
+{ 0xB4DE, 0xB4DE, 0xB4DE },
+{ 0xB4DF, 0xB4DF, 0xB4DF },
+{ 0xB4E0, 0xB4E0, 0xB4E0 },
+{ 0xB4E1, 0xB4E1, 0xB4E1 },
+{ 0xB4E2, 0xB4E2, 0xB4E2 },
+{ 0xB4E3, 0xB4E3, 0xB4E3 },
+{ 0xB4E4, 0xB4E4, 0xB4E4 },
+{ 0xB4E5, 0xB4E5, 0xB4E5 },
+{ 0xB4E6, 0xB4E6, 0xB4E6 },
+{ 0xB4E7, 0xB4E7, 0xB4E7 },
+{ 0xB4E8, 0xB4E8, 0xB4E8 },
+{ 0xB4E9, 0xB4E9, 0xB4E9 },
+{ 0xB4EA, 0xB4EA, 0xB4EA },
+{ 0xB4EB, 0xB4EB, 0xB4EB },
+{ 0xB4EC, 0xB4EC, 0xB4EC },
+{ 0xB4ED, 0xB4ED, 0xB4ED },
+{ 0xB4EE, 0xB4EE, 0xB4EE },
+{ 0xB4EF, 0xB4EF, 0xB4EF },
+{ 0xB4F0, 0xB4F0, 0xB4F0 },
+{ 0xB4F1, 0xB4F1, 0xB4F1 },
+{ 0xB4F2, 0xB4F2, 0xB4F2 },
+{ 0xB4F3, 0xB4F3, 0xB4F3 },
+{ 0xB4F4, 0xB4F4, 0xB4F4 },
+{ 0xB4F5, 0xB4F5, 0xB4F5 },
+{ 0xB4F6, 0xB4F6, 0xB4F6 },
+{ 0xB4F7, 0xB4F7, 0xB4F7 },
+{ 0xB4F8, 0xB4F8, 0xB4F8 },
+{ 0xB4F9, 0xB4F9, 0xB4F9 },
+{ 0xB4FA, 0xB4FA, 0xB4FA },
+{ 0xB4FB, 0xB4FB, 0xB4FB },
+{ 0xB4FC, 0xB4FC, 0xB4FC },
+{ 0xB4FD, 0xB4FD, 0xB4FD },
+{ 0xB4FE, 0xB4FE, 0xB4FE },
+{ 0xB4FF, 0xB4FF, 0xB4FF },
+{ 0xB500, 0xB500, 0xB500 },
+{ 0xB501, 0xB501, 0xB501 },
+{ 0xB502, 0xB502, 0xB502 },
+{ 0xB503, 0xB503, 0xB503 },
+{ 0xB504, 0xB504, 0xB504 },
+{ 0xB505, 0xB505, 0xB505 },
+{ 0xB506, 0xB506, 0xB506 },
+{ 0xB507, 0xB507, 0xB507 },
+{ 0xB508, 0xB508, 0xB508 },
+{ 0xB509, 0xB509, 0xB509 },
+{ 0xB50A, 0xB50A, 0xB50A },
+{ 0xB50B, 0xB50B, 0xB50B },
+{ 0xB50C, 0xB50C, 0xB50C },
+{ 0xB50D, 0xB50D, 0xB50D },
+{ 0xB50E, 0xB50E, 0xB50E },
+{ 0xB50F, 0xB50F, 0xB50F },
+{ 0xB510, 0xB510, 0xB510 },
+{ 0xB511, 0xB511, 0xB511 },
+{ 0xB512, 0xB512, 0xB512 },
+{ 0xB513, 0xB513, 0xB513 },
+{ 0xB514, 0xB514, 0xB514 },
+{ 0xB515, 0xB515, 0xB515 },
+{ 0xB516, 0xB516, 0xB516 },
+{ 0xB517, 0xB517, 0xB517 },
+{ 0xB518, 0xB518, 0xB518 },
+{ 0xB519, 0xB519, 0xB519 },
+{ 0xB51A, 0xB51A, 0xB51A },
+{ 0xB51B, 0xB51B, 0xB51B },
+{ 0xB51C, 0xB51C, 0xB51C },
+{ 0xB51D, 0xB51D, 0xB51D },
+{ 0xB51E, 0xB51E, 0xB51E },
+{ 0xB51F, 0xB51F, 0xB51F },
+{ 0xB520, 0xB520, 0xB520 },
+{ 0xB521, 0xB521, 0xB521 },
+{ 0xB522, 0xB522, 0xB522 },
+{ 0xB523, 0xB523, 0xB523 },
+{ 0xB524, 0xB524, 0xB524 },
+{ 0xB525, 0xB525, 0xB525 },
+{ 0xB526, 0xB526, 0xB526 },
+{ 0xB527, 0xB527, 0xB527 },
+{ 0xB528, 0xB528, 0xB528 },
+{ 0xB529, 0xB529, 0xB529 },
+{ 0xB52A, 0xB52A, 0xB52A },
+{ 0xB52B, 0xB52B, 0xB52B },
+{ 0xB52C, 0xB52C, 0xB52C },
+{ 0xB52D, 0xB52D, 0xB52D },
+{ 0xB52E, 0xB52E, 0xB52E },
+{ 0xB52F, 0xB52F, 0xB52F },
+{ 0xB530, 0xB530, 0xB530 },
+{ 0xB531, 0xB531, 0xB531 },
+{ 0xB532, 0xB532, 0xB532 },
+{ 0xB533, 0xB533, 0xB533 },
+{ 0xB534, 0xB534, 0xB534 },
+{ 0xB535, 0xB535, 0xB535 },
+{ 0xB536, 0xB536, 0xB536 },
+{ 0xB537, 0xB537, 0xB537 },
+{ 0xB538, 0xB538, 0xB538 },
+{ 0xB539, 0xB539, 0xB539 },
+{ 0xB53A, 0xB53A, 0xB53A },
+{ 0xB53B, 0xB53B, 0xB53B },
+{ 0xB53C, 0xB53C, 0xB53C },
+{ 0xB53D, 0xB53D, 0xB53D },
+{ 0xB53E, 0xB53E, 0xB53E },
+{ 0xB53F, 0xB53F, 0xB53F },
+{ 0xB540, 0xB540, 0xB540 },
+{ 0xB541, 0xB541, 0xB541 },
+{ 0xB542, 0xB542, 0xB542 },
+{ 0xB543, 0xB543, 0xB543 },
+{ 0xB544, 0xB544, 0xB544 },
+{ 0xB545, 0xB545, 0xB545 },
+{ 0xB546, 0xB546, 0xB546 },
+{ 0xB547, 0xB547, 0xB547 },
+{ 0xB548, 0xB548, 0xB548 },
+{ 0xB549, 0xB549, 0xB549 },
+{ 0xB54A, 0xB54A, 0xB54A },
+{ 0xB54B, 0xB54B, 0xB54B },
+{ 0xB54C, 0xB54C, 0xB54C },
+{ 0xB54D, 0xB54D, 0xB54D },
+{ 0xB54E, 0xB54E, 0xB54E },
+{ 0xB54F, 0xB54F, 0xB54F },
+{ 0xB550, 0xB550, 0xB550 },
+{ 0xB551, 0xB551, 0xB551 },
+{ 0xB552, 0xB552, 0xB552 },
+{ 0xB553, 0xB553, 0xB553 },
+{ 0xB554, 0xB554, 0xB554 },
+{ 0xB555, 0xB555, 0xB555 },
+{ 0xB556, 0xB556, 0xB556 },
+{ 0xB557, 0xB557, 0xB557 },
+{ 0xB558, 0xB558, 0xB558 },
+{ 0xB559, 0xB559, 0xB559 },
+{ 0xB55A, 0xB55A, 0xB55A },
+{ 0xB55B, 0xB55B, 0xB55B },
+{ 0xB55C, 0xB55C, 0xB55C },
+{ 0xB55D, 0xB55D, 0xB55D },
+{ 0xB55E, 0xB55E, 0xB55E },
+{ 0xB55F, 0xB55F, 0xB55F },
+{ 0xB560, 0xB560, 0xB560 },
+{ 0xB561, 0xB561, 0xB561 },
+{ 0xB562, 0xB562, 0xB562 },
+{ 0xB563, 0xB563, 0xB563 },
+{ 0xB564, 0xB564, 0xB564 },
+{ 0xB565, 0xB565, 0xB565 },
+{ 0xB566, 0xB566, 0xB566 },
+{ 0xB567, 0xB567, 0xB567 },
+{ 0xB568, 0xB568, 0xB568 },
+{ 0xB569, 0xB569, 0xB569 },
+{ 0xB56A, 0xB56A, 0xB56A },
+{ 0xB56B, 0xB56B, 0xB56B },
+{ 0xB56C, 0xB56C, 0xB56C },
+{ 0xB56D, 0xB56D, 0xB56D },
+{ 0xB56E, 0xB56E, 0xB56E },
+{ 0xB56F, 0xB56F, 0xB56F },
+{ 0xB570, 0xB570, 0xB570 },
+{ 0xB571, 0xB571, 0xB571 },
+{ 0xB572, 0xB572, 0xB572 },
+{ 0xB573, 0xB573, 0xB573 },
+{ 0xB574, 0xB574, 0xB574 },
+{ 0xB575, 0xB575, 0xB575 },
+{ 0xB576, 0xB576, 0xB576 },
+{ 0xB577, 0xB577, 0xB577 },
+{ 0xB578, 0xB578, 0xB578 },
+{ 0xB579, 0xB579, 0xB579 },
+{ 0xB57A, 0xB57A, 0xB57A },
+{ 0xB57B, 0xB57B, 0xB57B },
+{ 0xB57C, 0xB57C, 0xB57C },
+{ 0xB57D, 0xB57D, 0xB57D },
+{ 0xB57E, 0xB57E, 0xB57E },
+{ 0xB57F, 0xB57F, 0xB57F },
+{ 0xB580, 0xB580, 0xB580 },
+{ 0xB581, 0xB581, 0xB581 },
+{ 0xB582, 0xB582, 0xB582 },
+{ 0xB583, 0xB583, 0xB583 },
+{ 0xB584, 0xB584, 0xB584 },
+{ 0xB585, 0xB585, 0xB585 },
+{ 0xB586, 0xB586, 0xB586 },
+{ 0xB587, 0xB587, 0xB587 },
+{ 0xB588, 0xB588, 0xB588 },
+{ 0xB589, 0xB589, 0xB589 },
+{ 0xB58A, 0xB58A, 0xB58A },
+{ 0xB58B, 0xB58B, 0xB58B },
+{ 0xB58C, 0xB58C, 0xB58C },
+{ 0xB58D, 0xB58D, 0xB58D },
+{ 0xB58E, 0xB58E, 0xB58E },
+{ 0xB58F, 0xB58F, 0xB58F },
+{ 0xB590, 0xB590, 0xB590 },
+{ 0xB591, 0xB591, 0xB591 },
+{ 0xB592, 0xB592, 0xB592 },
+{ 0xB593, 0xB593, 0xB593 },
+{ 0xB594, 0xB594, 0xB594 },
+{ 0xB595, 0xB595, 0xB595 },
+{ 0xB596, 0xB596, 0xB596 },
+{ 0xB597, 0xB597, 0xB597 },
+{ 0xB598, 0xB598, 0xB598 },
+{ 0xB599, 0xB599, 0xB599 },
+{ 0xB59A, 0xB59A, 0xB59A },
+{ 0xB59B, 0xB59B, 0xB59B },
+{ 0xB59C, 0xB59C, 0xB59C },
+{ 0xB59D, 0xB59D, 0xB59D },
+{ 0xB59E, 0xB59E, 0xB59E },
+{ 0xB59F, 0xB59F, 0xB59F },
+{ 0xB5A0, 0xB5A0, 0xB5A0 },
+{ 0xB5A1, 0xB5A1, 0xB5A1 },
+{ 0xB5A2, 0xB5A2, 0xB5A2 },
+{ 0xB5A3, 0xB5A3, 0xB5A3 },
+{ 0xB5A4, 0xB5A4, 0xB5A4 },
+{ 0xB5A5, 0xB5A5, 0xB5A5 },
+{ 0xB5A6, 0xB5A6, 0xB5A6 },
+{ 0xB5A7, 0xB5A7, 0xB5A7 },
+{ 0xB5A8, 0xB5A8, 0xB5A8 },
+{ 0xB5A9, 0xB5A9, 0xB5A9 },
+{ 0xB5AA, 0xB5AA, 0xB5AA },
+{ 0xB5AB, 0xB5AB, 0xB5AB },
+{ 0xB5AC, 0xB5AC, 0xB5AC },
+{ 0xB5AD, 0xB5AD, 0xB5AD },
+{ 0xB5AE, 0xB5AE, 0xB5AE },
+{ 0xB5AF, 0xB5AF, 0xB5AF },
+{ 0xB5B0, 0xB5B0, 0xB5B0 },
+{ 0xB5B1, 0xB5B1, 0xB5B1 },
+{ 0xB5B2, 0xB5B2, 0xB5B2 },
+{ 0xB5B3, 0xB5B3, 0xB5B3 },
+{ 0xB5B4, 0xB5B4, 0xB5B4 },
+{ 0xB5B5, 0xB5B5, 0xB5B5 },
+{ 0xB5B6, 0xB5B6, 0xB5B6 },
+{ 0xB5B7, 0xB5B7, 0xB5B7 },
+{ 0xB5B8, 0xB5B8, 0xB5B8 },
+{ 0xB5B9, 0xB5B9, 0xB5B9 },
+{ 0xB5BA, 0xB5BA, 0xB5BA },
+{ 0xB5BB, 0xB5BB, 0xB5BB },
+{ 0xB5BC, 0xB5BC, 0xB5BC },
+{ 0xB5BD, 0xB5BD, 0xB5BD },
+{ 0xB5BE, 0xB5BE, 0xB5BE },
+{ 0xB5BF, 0xB5BF, 0xB5BF },
+{ 0xB5C0, 0xB5C0, 0xB5C0 },
+{ 0xB5C1, 0xB5C1, 0xB5C1 },
+{ 0xB5C2, 0xB5C2, 0xB5C2 },
+{ 0xB5C3, 0xB5C3, 0xB5C3 },
+{ 0xB5C4, 0xB5C4, 0xB5C4 },
+{ 0xB5C5, 0xB5C5, 0xB5C5 },
+{ 0xB5C6, 0xB5C6, 0xB5C6 },
+{ 0xB5C7, 0xB5C7, 0xB5C7 },
+{ 0xB5C8, 0xB5C8, 0xB5C8 },
+{ 0xB5C9, 0xB5C9, 0xB5C9 },
+{ 0xB5CA, 0xB5CA, 0xB5CA },
+{ 0xB5CB, 0xB5CB, 0xB5CB },
+{ 0xB5CC, 0xB5CC, 0xB5CC },
+{ 0xB5CD, 0xB5CD, 0xB5CD },
+{ 0xB5CE, 0xB5CE, 0xB5CE },
+{ 0xB5CF, 0xB5CF, 0xB5CF },
+{ 0xB5D0, 0xB5D0, 0xB5D0 },
+{ 0xB5D1, 0xB5D1, 0xB5D1 },
+{ 0xB5D2, 0xB5D2, 0xB5D2 },
+{ 0xB5D3, 0xB5D3, 0xB5D3 },
+{ 0xB5D4, 0xB5D4, 0xB5D4 },
+{ 0xB5D5, 0xB5D5, 0xB5D5 },
+{ 0xB5D6, 0xB5D6, 0xB5D6 },
+{ 0xB5D7, 0xB5D7, 0xB5D7 },
+{ 0xB5D8, 0xB5D8, 0xB5D8 },
+{ 0xB5D9, 0xB5D9, 0xB5D9 },
+{ 0xB5DA, 0xB5DA, 0xB5DA },
+{ 0xB5DB, 0xB5DB, 0xB5DB },
+{ 0xB5DC, 0xB5DC, 0xB5DC },
+{ 0xB5DD, 0xB5DD, 0xB5DD },
+{ 0xB5DE, 0xB5DE, 0xB5DE },
+{ 0xB5DF, 0xB5DF, 0xB5DF },
+{ 0xB5E0, 0xB5E0, 0xB5E0 },
+{ 0xB5E1, 0xB5E1, 0xB5E1 },
+{ 0xB5E2, 0xB5E2, 0xB5E2 },
+{ 0xB5E3, 0xB5E3, 0xB5E3 },
+{ 0xB5E4, 0xB5E4, 0xB5E4 },
+{ 0xB5E5, 0xB5E5, 0xB5E5 },
+{ 0xB5E6, 0xB5E6, 0xB5E6 },
+{ 0xB5E7, 0xB5E7, 0xB5E7 },
+{ 0xB5E8, 0xB5E8, 0xB5E8 },
+{ 0xB5E9, 0xB5E9, 0xB5E9 },
+{ 0xB5EA, 0xB5EA, 0xB5EA },
+{ 0xB5EB, 0xB5EB, 0xB5EB },
+{ 0xB5EC, 0xB5EC, 0xB5EC },
+{ 0xB5ED, 0xB5ED, 0xB5ED },
+{ 0xB5EE, 0xB5EE, 0xB5EE },
+{ 0xB5EF, 0xB5EF, 0xB5EF },
+{ 0xB5F0, 0xB5F0, 0xB5F0 },
+{ 0xB5F1, 0xB5F1, 0xB5F1 },
+{ 0xB5F2, 0xB5F2, 0xB5F2 },
+{ 0xB5F3, 0xB5F3, 0xB5F3 },
+{ 0xB5F4, 0xB5F4, 0xB5F4 },
+{ 0xB5F5, 0xB5F5, 0xB5F5 },
+{ 0xB5F6, 0xB5F6, 0xB5F6 },
+{ 0xB5F7, 0xB5F7, 0xB5F7 },
+{ 0xB5F8, 0xB5F8, 0xB5F8 },
+{ 0xB5F9, 0xB5F9, 0xB5F9 },
+{ 0xB5FA, 0xB5FA, 0xB5FA },
+{ 0xB5FB, 0xB5FB, 0xB5FB },
+{ 0xB5FC, 0xB5FC, 0xB5FC },
+{ 0xB5FD, 0xB5FD, 0xB5FD },
+{ 0xB5FE, 0xB5FE, 0xB5FE },
+{ 0xB5FF, 0xB5FF, 0xB5FF },
+{ 0xB600, 0xB600, 0xB600 },
+{ 0xB601, 0xB601, 0xB601 },
+{ 0xB602, 0xB602, 0xB602 },
+{ 0xB603, 0xB603, 0xB603 },
+{ 0xB604, 0xB604, 0xB604 },
+{ 0xB605, 0xB605, 0xB605 },
+{ 0xB606, 0xB606, 0xB606 },
+{ 0xB607, 0xB607, 0xB607 },
+{ 0xB608, 0xB608, 0xB608 },
+{ 0xB609, 0xB609, 0xB609 },
+{ 0xB60A, 0xB60A, 0xB60A },
+{ 0xB60B, 0xB60B, 0xB60B },
+{ 0xB60C, 0xB60C, 0xB60C },
+{ 0xB60D, 0xB60D, 0xB60D },
+{ 0xB60E, 0xB60E, 0xB60E },
+{ 0xB60F, 0xB60F, 0xB60F },
+{ 0xB610, 0xB610, 0xB610 },
+{ 0xB611, 0xB611, 0xB611 },
+{ 0xB612, 0xB612, 0xB612 },
+{ 0xB613, 0xB613, 0xB613 },
+{ 0xB614, 0xB614, 0xB614 },
+{ 0xB615, 0xB615, 0xB615 },
+{ 0xB616, 0xB616, 0xB616 },
+{ 0xB617, 0xB617, 0xB617 },
+{ 0xB618, 0xB618, 0xB618 },
+{ 0xB619, 0xB619, 0xB619 },
+{ 0xB61A, 0xB61A, 0xB61A },
+{ 0xB61B, 0xB61B, 0xB61B },
+{ 0xB61C, 0xB61C, 0xB61C },
+{ 0xB61D, 0xB61D, 0xB61D },
+{ 0xB61E, 0xB61E, 0xB61E },
+{ 0xB61F, 0xB61F, 0xB61F },
+{ 0xB620, 0xB620, 0xB620 },
+{ 0xB621, 0xB621, 0xB621 },
+{ 0xB622, 0xB622, 0xB622 },
+{ 0xB623, 0xB623, 0xB623 },
+{ 0xB624, 0xB624, 0xB624 },
+{ 0xB625, 0xB625, 0xB625 },
+{ 0xB626, 0xB626, 0xB626 },
+{ 0xB627, 0xB627, 0xB627 },
+{ 0xB628, 0xB628, 0xB628 },
+{ 0xB629, 0xB629, 0xB629 },
+{ 0xB62A, 0xB62A, 0xB62A },
+{ 0xB62B, 0xB62B, 0xB62B },
+{ 0xB62C, 0xB62C, 0xB62C },
+{ 0xB62D, 0xB62D, 0xB62D },
+{ 0xB62E, 0xB62E, 0xB62E },
+{ 0xB62F, 0xB62F, 0xB62F },
+{ 0xB630, 0xB630, 0xB630 },
+{ 0xB631, 0xB631, 0xB631 },
+{ 0xB632, 0xB632, 0xB632 },
+{ 0xB633, 0xB633, 0xB633 },
+{ 0xB634, 0xB634, 0xB634 },
+{ 0xB635, 0xB635, 0xB635 },
+{ 0xB636, 0xB636, 0xB636 },
+{ 0xB637, 0xB637, 0xB637 },
+{ 0xB638, 0xB638, 0xB638 },
+{ 0xB639, 0xB639, 0xB639 },
+{ 0xB63A, 0xB63A, 0xB63A },
+{ 0xB63B, 0xB63B, 0xB63B },
+{ 0xB63C, 0xB63C, 0xB63C },
+{ 0xB63D, 0xB63D, 0xB63D },
+{ 0xB63E, 0xB63E, 0xB63E },
+{ 0xB63F, 0xB63F, 0xB63F },
+{ 0xB640, 0xB640, 0xB640 },
+{ 0xB641, 0xB641, 0xB641 },
+{ 0xB642, 0xB642, 0xB642 },
+{ 0xB643, 0xB643, 0xB643 },
+{ 0xB644, 0xB644, 0xB644 },
+{ 0xB645, 0xB645, 0xB645 },
+{ 0xB646, 0xB646, 0xB646 },
+{ 0xB647, 0xB647, 0xB647 },
+{ 0xB648, 0xB648, 0xB648 },
+{ 0xB649, 0xB649, 0xB649 },
+{ 0xB64A, 0xB64A, 0xB64A },
+{ 0xB64B, 0xB64B, 0xB64B },
+{ 0xB64C, 0xB64C, 0xB64C },
+{ 0xB64D, 0xB64D, 0xB64D },
+{ 0xB64E, 0xB64E, 0xB64E },
+{ 0xB64F, 0xB64F, 0xB64F },
+{ 0xB650, 0xB650, 0xB650 },
+{ 0xB651, 0xB651, 0xB651 },
+{ 0xB652, 0xB652, 0xB652 },
+{ 0xB653, 0xB653, 0xB653 },
+{ 0xB654, 0xB654, 0xB654 },
+{ 0xB655, 0xB655, 0xB655 },
+{ 0xB656, 0xB656, 0xB656 },
+{ 0xB657, 0xB657, 0xB657 },
+{ 0xB658, 0xB658, 0xB658 },
+{ 0xB659, 0xB659, 0xB659 },
+{ 0xB65A, 0xB65A, 0xB65A },
+{ 0xB65B, 0xB65B, 0xB65B },
+{ 0xB65C, 0xB65C, 0xB65C },
+{ 0xB65D, 0xB65D, 0xB65D },
+{ 0xB65E, 0xB65E, 0xB65E },
+{ 0xB65F, 0xB65F, 0xB65F },
+{ 0xB660, 0xB660, 0xB660 },
+{ 0xB661, 0xB661, 0xB661 },
+{ 0xB662, 0xB662, 0xB662 },
+{ 0xB663, 0xB663, 0xB663 },
+{ 0xB664, 0xB664, 0xB664 },
+{ 0xB665, 0xB665, 0xB665 },
+{ 0xB666, 0xB666, 0xB666 },
+{ 0xB667, 0xB667, 0xB667 },
+{ 0xB668, 0xB668, 0xB668 },
+{ 0xB669, 0xB669, 0xB669 },
+{ 0xB66A, 0xB66A, 0xB66A },
+{ 0xB66B, 0xB66B, 0xB66B },
+{ 0xB66C, 0xB66C, 0xB66C },
+{ 0xB66D, 0xB66D, 0xB66D },
+{ 0xB66E, 0xB66E, 0xB66E },
+{ 0xB66F, 0xB66F, 0xB66F },
+{ 0xB670, 0xB670, 0xB670 },
+{ 0xB671, 0xB671, 0xB671 },
+{ 0xB672, 0xB672, 0xB672 },
+{ 0xB673, 0xB673, 0xB673 },
+{ 0xB674, 0xB674, 0xB674 },
+{ 0xB675, 0xB675, 0xB675 },
+{ 0xB676, 0xB676, 0xB676 },
+{ 0xB677, 0xB677, 0xB677 },
+{ 0xB678, 0xB678, 0xB678 },
+{ 0xB679, 0xB679, 0xB679 },
+{ 0xB67A, 0xB67A, 0xB67A },
+{ 0xB67B, 0xB67B, 0xB67B },
+{ 0xB67C, 0xB67C, 0xB67C },
+{ 0xB67D, 0xB67D, 0xB67D },
+{ 0xB67E, 0xB67E, 0xB67E },
+{ 0xB67F, 0xB67F, 0xB67F },
+{ 0xB680, 0xB680, 0xB680 },
+{ 0xB681, 0xB681, 0xB681 },
+{ 0xB682, 0xB682, 0xB682 },
+{ 0xB683, 0xB683, 0xB683 },
+{ 0xB684, 0xB684, 0xB684 },
+{ 0xB685, 0xB685, 0xB685 },
+{ 0xB686, 0xB686, 0xB686 },
+{ 0xB687, 0xB687, 0xB687 },
+{ 0xB688, 0xB688, 0xB688 },
+{ 0xB689, 0xB689, 0xB689 },
+{ 0xB68A, 0xB68A, 0xB68A },
+{ 0xB68B, 0xB68B, 0xB68B },
+{ 0xB68C, 0xB68C, 0xB68C },
+{ 0xB68D, 0xB68D, 0xB68D },
+{ 0xB68E, 0xB68E, 0xB68E },
+{ 0xB68F, 0xB68F, 0xB68F },
+{ 0xB690, 0xB690, 0xB690 },
+{ 0xB691, 0xB691, 0xB691 },
+{ 0xB692, 0xB692, 0xB692 },
+{ 0xB693, 0xB693, 0xB693 },
+{ 0xB694, 0xB694, 0xB694 },
+{ 0xB695, 0xB695, 0xB695 },
+{ 0xB696, 0xB696, 0xB696 },
+{ 0xB697, 0xB697, 0xB697 },
+{ 0xB698, 0xB698, 0xB698 },
+{ 0xB699, 0xB699, 0xB699 },
+{ 0xB69A, 0xB69A, 0xB69A },
+{ 0xB69B, 0xB69B, 0xB69B },
+{ 0xB69C, 0xB69C, 0xB69C },
+{ 0xB69D, 0xB69D, 0xB69D },
+{ 0xB69E, 0xB69E, 0xB69E },
+{ 0xB69F, 0xB69F, 0xB69F },
+{ 0xB6A0, 0xB6A0, 0xB6A0 },
+{ 0xB6A1, 0xB6A1, 0xB6A1 },
+{ 0xB6A2, 0xB6A2, 0xB6A2 },
+{ 0xB6A3, 0xB6A3, 0xB6A3 },
+{ 0xB6A4, 0xB6A4, 0xB6A4 },
+{ 0xB6A5, 0xB6A5, 0xB6A5 },
+{ 0xB6A6, 0xB6A6, 0xB6A6 },
+{ 0xB6A7, 0xB6A7, 0xB6A7 },
+{ 0xB6A8, 0xB6A8, 0xB6A8 },
+{ 0xB6A9, 0xB6A9, 0xB6A9 },
+{ 0xB6AA, 0xB6AA, 0xB6AA },
+{ 0xB6AB, 0xB6AB, 0xB6AB },
+{ 0xB6AC, 0xB6AC, 0xB6AC },
+{ 0xB6AD, 0xB6AD, 0xB6AD },
+{ 0xB6AE, 0xB6AE, 0xB6AE },
+{ 0xB6AF, 0xB6AF, 0xB6AF },
+{ 0xB6B0, 0xB6B0, 0xB6B0 },
+{ 0xB6B1, 0xB6B1, 0xB6B1 },
+{ 0xB6B2, 0xB6B2, 0xB6B2 },
+{ 0xB6B3, 0xB6B3, 0xB6B3 },
+{ 0xB6B4, 0xB6B4, 0xB6B4 },
+{ 0xB6B5, 0xB6B5, 0xB6B5 },
+{ 0xB6B6, 0xB6B6, 0xB6B6 },
+{ 0xB6B7, 0xB6B7, 0xB6B7 },
+{ 0xB6B8, 0xB6B8, 0xB6B8 },
+{ 0xB6B9, 0xB6B9, 0xB6B9 },
+{ 0xB6BA, 0xB6BA, 0xB6BA },
+{ 0xB6BB, 0xB6BB, 0xB6BB },
+{ 0xB6BC, 0xB6BC, 0xB6BC },
+{ 0xB6BD, 0xB6BD, 0xB6BD },
+{ 0xB6BE, 0xB6BE, 0xB6BE },
+{ 0xB6BF, 0xB6BF, 0xB6BF },
+{ 0xB6C0, 0xB6C0, 0xB6C0 },
+{ 0xB6C1, 0xB6C1, 0xB6C1 },
+{ 0xB6C2, 0xB6C2, 0xB6C2 },
+{ 0xB6C3, 0xB6C3, 0xB6C3 },
+{ 0xB6C4, 0xB6C4, 0xB6C4 },
+{ 0xB6C5, 0xB6C5, 0xB6C5 },
+{ 0xB6C6, 0xB6C6, 0xB6C6 },
+{ 0xB6C7, 0xB6C7, 0xB6C7 },
+{ 0xB6C8, 0xB6C8, 0xB6C8 },
+{ 0xB6C9, 0xB6C9, 0xB6C9 },
+{ 0xB6CA, 0xB6CA, 0xB6CA },
+{ 0xB6CB, 0xB6CB, 0xB6CB },
+{ 0xB6CC, 0xB6CC, 0xB6CC },
+{ 0xB6CD, 0xB6CD, 0xB6CD },
+{ 0xB6CE, 0xB6CE, 0xB6CE },
+{ 0xB6CF, 0xB6CF, 0xB6CF },
+{ 0xB6D0, 0xB6D0, 0xB6D0 },
+{ 0xB6D1, 0xB6D1, 0xB6D1 },
+{ 0xB6D2, 0xB6D2, 0xB6D2 },
+{ 0xB6D3, 0xB6D3, 0xB6D3 },
+{ 0xB6D4, 0xB6D4, 0xB6D4 },
+{ 0xB6D5, 0xB6D5, 0xB6D5 },
+{ 0xB6D6, 0xB6D6, 0xB6D6 },
+{ 0xB6D7, 0xB6D7, 0xB6D7 },
+{ 0xB6D8, 0xB6D8, 0xB6D8 },
+{ 0xB6D9, 0xB6D9, 0xB6D9 },
+{ 0xB6DA, 0xB6DA, 0xB6DA },
+{ 0xB6DB, 0xB6DB, 0xB6DB },
+{ 0xB6DC, 0xB6DC, 0xB6DC },
+{ 0xB6DD, 0xB6DD, 0xB6DD },
+{ 0xB6DE, 0xB6DE, 0xB6DE },
+{ 0xB6DF, 0xB6DF, 0xB6DF },
+{ 0xB6E0, 0xB6E0, 0xB6E0 },
+{ 0xB6E1, 0xB6E1, 0xB6E1 },
+{ 0xB6E2, 0xB6E2, 0xB6E2 },
+{ 0xB6E3, 0xB6E3, 0xB6E3 },
+{ 0xB6E4, 0xB6E4, 0xB6E4 },
+{ 0xB6E5, 0xB6E5, 0xB6E5 },
+{ 0xB6E6, 0xB6E6, 0xB6E6 },
+{ 0xB6E7, 0xB6E7, 0xB6E7 },
+{ 0xB6E8, 0xB6E8, 0xB6E8 },
+{ 0xB6E9, 0xB6E9, 0xB6E9 },
+{ 0xB6EA, 0xB6EA, 0xB6EA },
+{ 0xB6EB, 0xB6EB, 0xB6EB },
+{ 0xB6EC, 0xB6EC, 0xB6EC },
+{ 0xB6ED, 0xB6ED, 0xB6ED },
+{ 0xB6EE, 0xB6EE, 0xB6EE },
+{ 0xB6EF, 0xB6EF, 0xB6EF },
+{ 0xB6F0, 0xB6F0, 0xB6F0 },
+{ 0xB6F1, 0xB6F1, 0xB6F1 },
+{ 0xB6F2, 0xB6F2, 0xB6F2 },
+{ 0xB6F3, 0xB6F3, 0xB6F3 },
+{ 0xB6F4, 0xB6F4, 0xB6F4 },
+{ 0xB6F5, 0xB6F5, 0xB6F5 },
+{ 0xB6F6, 0xB6F6, 0xB6F6 },
+{ 0xB6F7, 0xB6F7, 0xB6F7 },
+{ 0xB6F8, 0xB6F8, 0xB6F8 },
+{ 0xB6F9, 0xB6F9, 0xB6F9 },
+{ 0xB6FA, 0xB6FA, 0xB6FA },
+{ 0xB6FB, 0xB6FB, 0xB6FB },
+{ 0xB6FC, 0xB6FC, 0xB6FC },
+{ 0xB6FD, 0xB6FD, 0xB6FD },
+{ 0xB6FE, 0xB6FE, 0xB6FE },
+{ 0xB6FF, 0xB6FF, 0xB6FF },
+{ 0xB700, 0xB700, 0xB700 },
+{ 0xB701, 0xB701, 0xB701 },
+{ 0xB702, 0xB702, 0xB702 },
+{ 0xB703, 0xB703, 0xB703 },
+{ 0xB704, 0xB704, 0xB704 },
+{ 0xB705, 0xB705, 0xB705 },
+{ 0xB706, 0xB706, 0xB706 },
+{ 0xB707, 0xB707, 0xB707 },
+{ 0xB708, 0xB708, 0xB708 },
+{ 0xB709, 0xB709, 0xB709 },
+{ 0xB70A, 0xB70A, 0xB70A },
+{ 0xB70B, 0xB70B, 0xB70B },
+{ 0xB70C, 0xB70C, 0xB70C },
+{ 0xB70D, 0xB70D, 0xB70D },
+{ 0xB70E, 0xB70E, 0xB70E },
+{ 0xB70F, 0xB70F, 0xB70F },
+{ 0xB710, 0xB710, 0xB710 },
+{ 0xB711, 0xB711, 0xB711 },
+{ 0xB712, 0xB712, 0xB712 },
+{ 0xB713, 0xB713, 0xB713 },
+{ 0xB714, 0xB714, 0xB714 },
+{ 0xB715, 0xB715, 0xB715 },
+{ 0xB716, 0xB716, 0xB716 },
+{ 0xB717, 0xB717, 0xB717 },
+{ 0xB718, 0xB718, 0xB718 },
+{ 0xB719, 0xB719, 0xB719 },
+{ 0xB71A, 0xB71A, 0xB71A },
+{ 0xB71B, 0xB71B, 0xB71B },
+{ 0xB71C, 0xB71C, 0xB71C },
+{ 0xB71D, 0xB71D, 0xB71D },
+{ 0xB71E, 0xB71E, 0xB71E },
+{ 0xB71F, 0xB71F, 0xB71F },
+{ 0xB720, 0xB720, 0xB720 },
+{ 0xB721, 0xB721, 0xB721 },
+{ 0xB722, 0xB722, 0xB722 },
+{ 0xB723, 0xB723, 0xB723 },
+{ 0xB724, 0xB724, 0xB724 },
+{ 0xB725, 0xB725, 0xB725 },
+{ 0xB726, 0xB726, 0xB726 },
+{ 0xB727, 0xB727, 0xB727 },
+{ 0xB728, 0xB728, 0xB728 },
+{ 0xB729, 0xB729, 0xB729 },
+{ 0xB72A, 0xB72A, 0xB72A },
+{ 0xB72B, 0xB72B, 0xB72B },
+{ 0xB72C, 0xB72C, 0xB72C },
+{ 0xB72D, 0xB72D, 0xB72D },
+{ 0xB72E, 0xB72E, 0xB72E },
+{ 0xB72F, 0xB72F, 0xB72F },
+{ 0xB730, 0xB730, 0xB730 },
+{ 0xB731, 0xB731, 0xB731 },
+{ 0xB732, 0xB732, 0xB732 },
+{ 0xB733, 0xB733, 0xB733 },
+{ 0xB734, 0xB734, 0xB734 },
+{ 0xB735, 0xB735, 0xB735 },
+{ 0xB736, 0xB736, 0xB736 },
+{ 0xB737, 0xB737, 0xB737 },
+{ 0xB738, 0xB738, 0xB738 },
+{ 0xB739, 0xB739, 0xB739 },
+{ 0xB73A, 0xB73A, 0xB73A },
+{ 0xB73B, 0xB73B, 0xB73B },
+{ 0xB73C, 0xB73C, 0xB73C },
+{ 0xB73D, 0xB73D, 0xB73D },
+{ 0xB73E, 0xB73E, 0xB73E },
+{ 0xB73F, 0xB73F, 0xB73F },
+{ 0xB740, 0xB740, 0xB740 },
+{ 0xB741, 0xB741, 0xB741 },
+{ 0xB742, 0xB742, 0xB742 },
+{ 0xB743, 0xB743, 0xB743 },
+{ 0xB744, 0xB744, 0xB744 },
+{ 0xB745, 0xB745, 0xB745 },
+{ 0xB746, 0xB746, 0xB746 },
+{ 0xB747, 0xB747, 0xB747 },
+{ 0xB748, 0xB748, 0xB748 },
+{ 0xB749, 0xB749, 0xB749 },
+{ 0xB74A, 0xB74A, 0xB74A },
+{ 0xB74B, 0xB74B, 0xB74B },
+{ 0xB74C, 0xB74C, 0xB74C },
+{ 0xB74D, 0xB74D, 0xB74D },
+{ 0xB74E, 0xB74E, 0xB74E },
+{ 0xB74F, 0xB74F, 0xB74F },
+{ 0xB750, 0xB750, 0xB750 },
+{ 0xB751, 0xB751, 0xB751 },
+{ 0xB752, 0xB752, 0xB752 },
+{ 0xB753, 0xB753, 0xB753 },
+{ 0xB754, 0xB754, 0xB754 },
+{ 0xB755, 0xB755, 0xB755 },
+{ 0xB756, 0xB756, 0xB756 },
+{ 0xB757, 0xB757, 0xB757 },
+{ 0xB758, 0xB758, 0xB758 },
+{ 0xB759, 0xB759, 0xB759 },
+{ 0xB75A, 0xB75A, 0xB75A },
+{ 0xB75B, 0xB75B, 0xB75B },
+{ 0xB75C, 0xB75C, 0xB75C },
+{ 0xB75D, 0xB75D, 0xB75D },
+{ 0xB75E, 0xB75E, 0xB75E },
+{ 0xB75F, 0xB75F, 0xB75F },
+{ 0xB760, 0xB760, 0xB760 },
+{ 0xB761, 0xB761, 0xB761 },
+{ 0xB762, 0xB762, 0xB762 },
+{ 0xB763, 0xB763, 0xB763 },
+{ 0xB764, 0xB764, 0xB764 },
+{ 0xB765, 0xB765, 0xB765 },
+{ 0xB766, 0xB766, 0xB766 },
+{ 0xB767, 0xB767, 0xB767 },
+{ 0xB768, 0xB768, 0xB768 },
+{ 0xB769, 0xB769, 0xB769 },
+{ 0xB76A, 0xB76A, 0xB76A },
+{ 0xB76B, 0xB76B, 0xB76B },
+{ 0xB76C, 0xB76C, 0xB76C },
+{ 0xB76D, 0xB76D, 0xB76D },
+{ 0xB76E, 0xB76E, 0xB76E },
+{ 0xB76F, 0xB76F, 0xB76F },
+{ 0xB770, 0xB770, 0xB770 },
+{ 0xB771, 0xB771, 0xB771 },
+{ 0xB772, 0xB772, 0xB772 },
+{ 0xB773, 0xB773, 0xB773 },
+{ 0xB774, 0xB774, 0xB774 },
+{ 0xB775, 0xB775, 0xB775 },
+{ 0xB776, 0xB776, 0xB776 },
+{ 0xB777, 0xB777, 0xB777 },
+{ 0xB778, 0xB778, 0xB778 },
+{ 0xB779, 0xB779, 0xB779 },
+{ 0xB77A, 0xB77A, 0xB77A },
+{ 0xB77B, 0xB77B, 0xB77B },
+{ 0xB77C, 0xB77C, 0xB77C },
+{ 0xB77D, 0xB77D, 0xB77D },
+{ 0xB77E, 0xB77E, 0xB77E },
+{ 0xB77F, 0xB77F, 0xB77F },
+{ 0xB780, 0xB780, 0xB780 },
+{ 0xB781, 0xB781, 0xB781 },
+{ 0xB782, 0xB782, 0xB782 },
+{ 0xB783, 0xB783, 0xB783 },
+{ 0xB784, 0xB784, 0xB784 },
+{ 0xB785, 0xB785, 0xB785 },
+{ 0xB786, 0xB786, 0xB786 },
+{ 0xB787, 0xB787, 0xB787 },
+{ 0xB788, 0xB788, 0xB788 },
+{ 0xB789, 0xB789, 0xB789 },
+{ 0xB78A, 0xB78A, 0xB78A },
+{ 0xB78B, 0xB78B, 0xB78B },
+{ 0xB78C, 0xB78C, 0xB78C },
+{ 0xB78D, 0xB78D, 0xB78D },
+{ 0xB78E, 0xB78E, 0xB78E },
+{ 0xB78F, 0xB78F, 0xB78F },
+{ 0xB790, 0xB790, 0xB790 },
+{ 0xB791, 0xB791, 0xB791 },
+{ 0xB792, 0xB792, 0xB792 },
+{ 0xB793, 0xB793, 0xB793 },
+{ 0xB794, 0xB794, 0xB794 },
+{ 0xB795, 0xB795, 0xB795 },
+{ 0xB796, 0xB796, 0xB796 },
+{ 0xB797, 0xB797, 0xB797 },
+{ 0xB798, 0xB798, 0xB798 },
+{ 0xB799, 0xB799, 0xB799 },
+{ 0xB79A, 0xB79A, 0xB79A },
+{ 0xB79B, 0xB79B, 0xB79B },
+{ 0xB79C, 0xB79C, 0xB79C },
+{ 0xB79D, 0xB79D, 0xB79D },
+{ 0xB79E, 0xB79E, 0xB79E },
+{ 0xB79F, 0xB79F, 0xB79F },
+{ 0xB7A0, 0xB7A0, 0xB7A0 },
+{ 0xB7A1, 0xB7A1, 0xB7A1 },
+{ 0xB7A2, 0xB7A2, 0xB7A2 },
+{ 0xB7A3, 0xB7A3, 0xB7A3 },
+{ 0xB7A4, 0xB7A4, 0xB7A4 },
+{ 0xB7A5, 0xB7A5, 0xB7A5 },
+{ 0xB7A6, 0xB7A6, 0xB7A6 },
+{ 0xB7A7, 0xB7A7, 0xB7A7 },
+{ 0xB7A8, 0xB7A8, 0xB7A8 },
+{ 0xB7A9, 0xB7A9, 0xB7A9 },
+{ 0xB7AA, 0xB7AA, 0xB7AA },
+{ 0xB7AB, 0xB7AB, 0xB7AB },
+{ 0xB7AC, 0xB7AC, 0xB7AC },
+{ 0xB7AD, 0xB7AD, 0xB7AD },
+{ 0xB7AE, 0xB7AE, 0xB7AE },
+{ 0xB7AF, 0xB7AF, 0xB7AF },
+{ 0xB7B0, 0xB7B0, 0xB7B0 },
+{ 0xB7B1, 0xB7B1, 0xB7B1 },
+{ 0xB7B2, 0xB7B2, 0xB7B2 },
+{ 0xB7B3, 0xB7B3, 0xB7B3 },
+{ 0xB7B4, 0xB7B4, 0xB7B4 },
+{ 0xB7B5, 0xB7B5, 0xB7B5 },
+{ 0xB7B6, 0xB7B6, 0xB7B6 },
+{ 0xB7B7, 0xB7B7, 0xB7B7 },
+{ 0xB7B8, 0xB7B8, 0xB7B8 },
+{ 0xB7B9, 0xB7B9, 0xB7B9 },
+{ 0xB7BA, 0xB7BA, 0xB7BA },
+{ 0xB7BB, 0xB7BB, 0xB7BB },
+{ 0xB7BC, 0xB7BC, 0xB7BC },
+{ 0xB7BD, 0xB7BD, 0xB7BD },
+{ 0xB7BE, 0xB7BE, 0xB7BE },
+{ 0xB7BF, 0xB7BF, 0xB7BF },
+{ 0xB7C0, 0xB7C0, 0xB7C0 },
+{ 0xB7C1, 0xB7C1, 0xB7C1 },
+{ 0xB7C2, 0xB7C2, 0xB7C2 },
+{ 0xB7C3, 0xB7C3, 0xB7C3 },
+{ 0xB7C4, 0xB7C4, 0xB7C4 },
+{ 0xB7C5, 0xB7C5, 0xB7C5 },
+{ 0xB7C6, 0xB7C6, 0xB7C6 },
+{ 0xB7C7, 0xB7C7, 0xB7C7 },
+{ 0xB7C8, 0xB7C8, 0xB7C8 },
+{ 0xB7C9, 0xB7C9, 0xB7C9 },
+{ 0xB7CA, 0xB7CA, 0xB7CA },
+{ 0xB7CB, 0xB7CB, 0xB7CB },
+{ 0xB7CC, 0xB7CC, 0xB7CC },
+{ 0xB7CD, 0xB7CD, 0xB7CD },
+{ 0xB7CE, 0xB7CE, 0xB7CE },
+{ 0xB7CF, 0xB7CF, 0xB7CF },
+{ 0xB7D0, 0xB7D0, 0xB7D0 },
+{ 0xB7D1, 0xB7D1, 0xB7D1 },
+{ 0xB7D2, 0xB7D2, 0xB7D2 },
+{ 0xB7D3, 0xB7D3, 0xB7D3 },
+{ 0xB7D4, 0xB7D4, 0xB7D4 },
+{ 0xB7D5, 0xB7D5, 0xB7D5 },
+{ 0xB7D6, 0xB7D6, 0xB7D6 },
+{ 0xB7D7, 0xB7D7, 0xB7D7 },
+{ 0xB7D8, 0xB7D8, 0xB7D8 },
+{ 0xB7D9, 0xB7D9, 0xB7D9 },
+{ 0xB7DA, 0xB7DA, 0xB7DA },
+{ 0xB7DB, 0xB7DB, 0xB7DB },
+{ 0xB7DC, 0xB7DC, 0xB7DC },
+{ 0xB7DD, 0xB7DD, 0xB7DD },
+{ 0xB7DE, 0xB7DE, 0xB7DE },
+{ 0xB7DF, 0xB7DF, 0xB7DF },
+{ 0xB7E0, 0xB7E0, 0xB7E0 },
+{ 0xB7E1, 0xB7E1, 0xB7E1 },
+{ 0xB7E2, 0xB7E2, 0xB7E2 },
+{ 0xB7E3, 0xB7E3, 0xB7E3 },
+{ 0xB7E4, 0xB7E4, 0xB7E4 },
+{ 0xB7E5, 0xB7E5, 0xB7E5 },
+{ 0xB7E6, 0xB7E6, 0xB7E6 },
+{ 0xB7E7, 0xB7E7, 0xB7E7 },
+{ 0xB7E8, 0xB7E8, 0xB7E8 },
+{ 0xB7E9, 0xB7E9, 0xB7E9 },
+{ 0xB7EA, 0xB7EA, 0xB7EA },
+{ 0xB7EB, 0xB7EB, 0xB7EB },
+{ 0xB7EC, 0xB7EC, 0xB7EC },
+{ 0xB7ED, 0xB7ED, 0xB7ED },
+{ 0xB7EE, 0xB7EE, 0xB7EE },
+{ 0xB7EF, 0xB7EF, 0xB7EF },
+{ 0xB7F0, 0xB7F0, 0xB7F0 },
+{ 0xB7F1, 0xB7F1, 0xB7F1 },
+{ 0xB7F2, 0xB7F2, 0xB7F2 },
+{ 0xB7F3, 0xB7F3, 0xB7F3 },
+{ 0xB7F4, 0xB7F4, 0xB7F4 },
+{ 0xB7F5, 0xB7F5, 0xB7F5 },
+{ 0xB7F6, 0xB7F6, 0xB7F6 },
+{ 0xB7F7, 0xB7F7, 0xB7F7 },
+{ 0xB7F8, 0xB7F8, 0xB7F8 },
+{ 0xB7F9, 0xB7F9, 0xB7F9 },
+{ 0xB7FA, 0xB7FA, 0xB7FA },
+{ 0xB7FB, 0xB7FB, 0xB7FB },
+{ 0xB7FC, 0xB7FC, 0xB7FC },
+{ 0xB7FD, 0xB7FD, 0xB7FD },
+{ 0xB7FE, 0xB7FE, 0xB7FE },
+{ 0xB7FF, 0xB7FF, 0xB7FF },
+{ 0xB800, 0xB800, 0xB800 },
+{ 0xB801, 0xB801, 0xB801 },
+{ 0xB802, 0xB802, 0xB802 },
+{ 0xB803, 0xB803, 0xB803 },
+{ 0xB804, 0xB804, 0xB804 },
+{ 0xB805, 0xB805, 0xB805 },
+{ 0xB806, 0xB806, 0xB806 },
+{ 0xB807, 0xB807, 0xB807 },
+{ 0xB808, 0xB808, 0xB808 },
+{ 0xB809, 0xB809, 0xB809 },
+{ 0xB80A, 0xB80A, 0xB80A },
+{ 0xB80B, 0xB80B, 0xB80B },
+{ 0xB80C, 0xB80C, 0xB80C },
+{ 0xB80D, 0xB80D, 0xB80D },
+{ 0xB80E, 0xB80E, 0xB80E },
+{ 0xB80F, 0xB80F, 0xB80F },
+{ 0xB810, 0xB810, 0xB810 },
+{ 0xB811, 0xB811, 0xB811 },
+{ 0xB812, 0xB812, 0xB812 },
+{ 0xB813, 0xB813, 0xB813 },
+{ 0xB814, 0xB814, 0xB814 },
+{ 0xB815, 0xB815, 0xB815 },
+{ 0xB816, 0xB816, 0xB816 },
+{ 0xB817, 0xB817, 0xB817 },
+{ 0xB818, 0xB818, 0xB818 },
+{ 0xB819, 0xB819, 0xB819 },
+{ 0xB81A, 0xB81A, 0xB81A },
+{ 0xB81B, 0xB81B, 0xB81B },
+{ 0xB81C, 0xB81C, 0xB81C },
+{ 0xB81D, 0xB81D, 0xB81D },
+{ 0xB81E, 0xB81E, 0xB81E },
+{ 0xB81F, 0xB81F, 0xB81F },
+{ 0xB820, 0xB820, 0xB820 },
+{ 0xB821, 0xB821, 0xB821 },
+{ 0xB822, 0xB822, 0xB822 },
+{ 0xB823, 0xB823, 0xB823 },
+{ 0xB824, 0xB824, 0xB824 },
+{ 0xB825, 0xB825, 0xB825 },
+{ 0xB826, 0xB826, 0xB826 },
+{ 0xB827, 0xB827, 0xB827 },
+{ 0xB828, 0xB828, 0xB828 },
+{ 0xB829, 0xB829, 0xB829 },
+{ 0xB82A, 0xB82A, 0xB82A },
+{ 0xB82B, 0xB82B, 0xB82B },
+{ 0xB82C, 0xB82C, 0xB82C },
+{ 0xB82D, 0xB82D, 0xB82D },
+{ 0xB82E, 0xB82E, 0xB82E },
+{ 0xB82F, 0xB82F, 0xB82F },
+{ 0xB830, 0xB830, 0xB830 },
+{ 0xB831, 0xB831, 0xB831 },
+{ 0xB832, 0xB832, 0xB832 },
+{ 0xB833, 0xB833, 0xB833 },
+{ 0xB834, 0xB834, 0xB834 },
+{ 0xB835, 0xB835, 0xB835 },
+{ 0xB836, 0xB836, 0xB836 },
+{ 0xB837, 0xB837, 0xB837 },
+{ 0xB838, 0xB838, 0xB838 },
+{ 0xB839, 0xB839, 0xB839 },
+{ 0xB83A, 0xB83A, 0xB83A },
+{ 0xB83B, 0xB83B, 0xB83B },
+{ 0xB83C, 0xB83C, 0xB83C },
+{ 0xB83D, 0xB83D, 0xB83D },
+{ 0xB83E, 0xB83E, 0xB83E },
+{ 0xB83F, 0xB83F, 0xB83F },
+{ 0xB840, 0xB840, 0xB840 },
+{ 0xB841, 0xB841, 0xB841 },
+{ 0xB842, 0xB842, 0xB842 },
+{ 0xB843, 0xB843, 0xB843 },
+{ 0xB844, 0xB844, 0xB844 },
+{ 0xB845, 0xB845, 0xB845 },
+{ 0xB846, 0xB846, 0xB846 },
+{ 0xB847, 0xB847, 0xB847 },
+{ 0xB848, 0xB848, 0xB848 },
+{ 0xB849, 0xB849, 0xB849 },
+{ 0xB84A, 0xB84A, 0xB84A },
+{ 0xB84B, 0xB84B, 0xB84B },
+{ 0xB84C, 0xB84C, 0xB84C },
+{ 0xB84D, 0xB84D, 0xB84D },
+{ 0xB84E, 0xB84E, 0xB84E },
+{ 0xB84F, 0xB84F, 0xB84F },
+{ 0xB850, 0xB850, 0xB850 },
+{ 0xB851, 0xB851, 0xB851 },
+{ 0xB852, 0xB852, 0xB852 },
+{ 0xB853, 0xB853, 0xB853 },
+{ 0xB854, 0xB854, 0xB854 },
+{ 0xB855, 0xB855, 0xB855 },
+{ 0xB856, 0xB856, 0xB856 },
+{ 0xB857, 0xB857, 0xB857 },
+{ 0xB858, 0xB858, 0xB858 },
+{ 0xB859, 0xB859, 0xB859 },
+{ 0xB85A, 0xB85A, 0xB85A },
+{ 0xB85B, 0xB85B, 0xB85B },
+{ 0xB85C, 0xB85C, 0xB85C },
+{ 0xB85D, 0xB85D, 0xB85D },
+{ 0xB85E, 0xB85E, 0xB85E },
+{ 0xB85F, 0xB85F, 0xB85F },
+{ 0xB860, 0xB860, 0xB860 },
+{ 0xB861, 0xB861, 0xB861 },
+{ 0xB862, 0xB862, 0xB862 },
+{ 0xB863, 0xB863, 0xB863 },
+{ 0xB864, 0xB864, 0xB864 },
+{ 0xB865, 0xB865, 0xB865 },
+{ 0xB866, 0xB866, 0xB866 },
+{ 0xB867, 0xB867, 0xB867 },
+{ 0xB868, 0xB868, 0xB868 },
+{ 0xB869, 0xB869, 0xB869 },
+{ 0xB86A, 0xB86A, 0xB86A },
+{ 0xB86B, 0xB86B, 0xB86B },
+{ 0xB86C, 0xB86C, 0xB86C },
+{ 0xB86D, 0xB86D, 0xB86D },
+{ 0xB86E, 0xB86E, 0xB86E },
+{ 0xB86F, 0xB86F, 0xB86F },
+{ 0xB870, 0xB870, 0xB870 },
+{ 0xB871, 0xB871, 0xB871 },
+{ 0xB872, 0xB872, 0xB872 },
+{ 0xB873, 0xB873, 0xB873 },
+{ 0xB874, 0xB874, 0xB874 },
+{ 0xB875, 0xB875, 0xB875 },
+{ 0xB876, 0xB876, 0xB876 },
+{ 0xB877, 0xB877, 0xB877 },
+{ 0xB878, 0xB878, 0xB878 },
+{ 0xB879, 0xB879, 0xB879 },
+{ 0xB87A, 0xB87A, 0xB87A },
+{ 0xB87B, 0xB87B, 0xB87B },
+{ 0xB87C, 0xB87C, 0xB87C },
+{ 0xB87D, 0xB87D, 0xB87D },
+{ 0xB87E, 0xB87E, 0xB87E },
+{ 0xB87F, 0xB87F, 0xB87F },
+{ 0xB880, 0xB880, 0xB880 },
+{ 0xB881, 0xB881, 0xB881 },
+{ 0xB882, 0xB882, 0xB882 },
+{ 0xB883, 0xB883, 0xB883 },
+{ 0xB884, 0xB884, 0xB884 },
+{ 0xB885, 0xB885, 0xB885 },
+{ 0xB886, 0xB886, 0xB886 },
+{ 0xB887, 0xB887, 0xB887 },
+{ 0xB888, 0xB888, 0xB888 },
+{ 0xB889, 0xB889, 0xB889 },
+{ 0xB88A, 0xB88A, 0xB88A },
+{ 0xB88B, 0xB88B, 0xB88B },
+{ 0xB88C, 0xB88C, 0xB88C },
+{ 0xB88D, 0xB88D, 0xB88D },
+{ 0xB88E, 0xB88E, 0xB88E },
+{ 0xB88F, 0xB88F, 0xB88F },
+{ 0xB890, 0xB890, 0xB890 },
+{ 0xB891, 0xB891, 0xB891 },
+{ 0xB892, 0xB892, 0xB892 },
+{ 0xB893, 0xB893, 0xB893 },
+{ 0xB894, 0xB894, 0xB894 },
+{ 0xB895, 0xB895, 0xB895 },
+{ 0xB896, 0xB896, 0xB896 },
+{ 0xB897, 0xB897, 0xB897 },
+{ 0xB898, 0xB898, 0xB898 },
+{ 0xB899, 0xB899, 0xB899 },
+{ 0xB89A, 0xB89A, 0xB89A },
+{ 0xB89B, 0xB89B, 0xB89B },
+{ 0xB89C, 0xB89C, 0xB89C },
+{ 0xB89D, 0xB89D, 0xB89D },
+{ 0xB89E, 0xB89E, 0xB89E },
+{ 0xB89F, 0xB89F, 0xB89F },
+{ 0xB8A0, 0xB8A0, 0xB8A0 },
+{ 0xB8A1, 0xB8A1, 0xB8A1 },
+{ 0xB8A2, 0xB8A2, 0xB8A2 },
+{ 0xB8A3, 0xB8A3, 0xB8A3 },
+{ 0xB8A4, 0xB8A4, 0xB8A4 },
+{ 0xB8A5, 0xB8A5, 0xB8A5 },
+{ 0xB8A6, 0xB8A6, 0xB8A6 },
+{ 0xB8A7, 0xB8A7, 0xB8A7 },
+{ 0xB8A8, 0xB8A8, 0xB8A8 },
+{ 0xB8A9, 0xB8A9, 0xB8A9 },
+{ 0xB8AA, 0xB8AA, 0xB8AA },
+{ 0xB8AB, 0xB8AB, 0xB8AB },
+{ 0xB8AC, 0xB8AC, 0xB8AC },
+{ 0xB8AD, 0xB8AD, 0xB8AD },
+{ 0xB8AE, 0xB8AE, 0xB8AE },
+{ 0xB8AF, 0xB8AF, 0xB8AF },
+{ 0xB8B0, 0xB8B0, 0xB8B0 },
+{ 0xB8B1, 0xB8B1, 0xB8B1 },
+{ 0xB8B2, 0xB8B2, 0xB8B2 },
+{ 0xB8B3, 0xB8B3, 0xB8B3 },
+{ 0xB8B4, 0xB8B4, 0xB8B4 },
+{ 0xB8B5, 0xB8B5, 0xB8B5 },
+{ 0xB8B6, 0xB8B6, 0xB8B6 },
+{ 0xB8B7, 0xB8B7, 0xB8B7 },
+{ 0xB8B8, 0xB8B8, 0xB8B8 },
+{ 0xB8B9, 0xB8B9, 0xB8B9 },
+{ 0xB8BA, 0xB8BA, 0xB8BA },
+{ 0xB8BB, 0xB8BB, 0xB8BB },
+{ 0xB8BC, 0xB8BC, 0xB8BC },
+{ 0xB8BD, 0xB8BD, 0xB8BD },
+{ 0xB8BE, 0xB8BE, 0xB8BE },
+{ 0xB8BF, 0xB8BF, 0xB8BF },
+{ 0xB8C0, 0xB8C0, 0xB8C0 },
+{ 0xB8C1, 0xB8C1, 0xB8C1 },
+{ 0xB8C2, 0xB8C2, 0xB8C2 },
+{ 0xB8C3, 0xB8C3, 0xB8C3 },
+{ 0xB8C4, 0xB8C4, 0xB8C4 },
+{ 0xB8C5, 0xB8C5, 0xB8C5 },
+{ 0xB8C6, 0xB8C6, 0xB8C6 },
+{ 0xB8C7, 0xB8C7, 0xB8C7 },
+{ 0xB8C8, 0xB8C8, 0xB8C8 },
+{ 0xB8C9, 0xB8C9, 0xB8C9 },
+{ 0xB8CA, 0xB8CA, 0xB8CA },
+{ 0xB8CB, 0xB8CB, 0xB8CB },
+{ 0xB8CC, 0xB8CC, 0xB8CC },
+{ 0xB8CD, 0xB8CD, 0xB8CD },
+{ 0xB8CE, 0xB8CE, 0xB8CE },
+{ 0xB8CF, 0xB8CF, 0xB8CF },
+{ 0xB8D0, 0xB8D0, 0xB8D0 },
+{ 0xB8D1, 0xB8D1, 0xB8D1 },
+{ 0xB8D2, 0xB8D2, 0xB8D2 },
+{ 0xB8D3, 0xB8D3, 0xB8D3 },
+{ 0xB8D4, 0xB8D4, 0xB8D4 },
+{ 0xB8D5, 0xB8D5, 0xB8D5 },
+{ 0xB8D6, 0xB8D6, 0xB8D6 },
+{ 0xB8D7, 0xB8D7, 0xB8D7 },
+{ 0xB8D8, 0xB8D8, 0xB8D8 },
+{ 0xB8D9, 0xB8D9, 0xB8D9 },
+{ 0xB8DA, 0xB8DA, 0xB8DA },
+{ 0xB8DB, 0xB8DB, 0xB8DB },
+{ 0xB8DC, 0xB8DC, 0xB8DC },
+{ 0xB8DD, 0xB8DD, 0xB8DD },
+{ 0xB8DE, 0xB8DE, 0xB8DE },
+{ 0xB8DF, 0xB8DF, 0xB8DF },
+{ 0xB8E0, 0xB8E0, 0xB8E0 },
+{ 0xB8E1, 0xB8E1, 0xB8E1 },
+{ 0xB8E2, 0xB8E2, 0xB8E2 },
+{ 0xB8E3, 0xB8E3, 0xB8E3 },
+{ 0xB8E4, 0xB8E4, 0xB8E4 },
+{ 0xB8E5, 0xB8E5, 0xB8E5 },
+{ 0xB8E6, 0xB8E6, 0xB8E6 },
+{ 0xB8E7, 0xB8E7, 0xB8E7 },
+{ 0xB8E8, 0xB8E8, 0xB8E8 },
+{ 0xB8E9, 0xB8E9, 0xB8E9 },
+{ 0xB8EA, 0xB8EA, 0xB8EA },
+{ 0xB8EB, 0xB8EB, 0xB8EB },
+{ 0xB8EC, 0xB8EC, 0xB8EC },
+{ 0xB8ED, 0xB8ED, 0xB8ED },
+{ 0xB8EE, 0xB8EE, 0xB8EE },
+{ 0xB8EF, 0xB8EF, 0xB8EF },
+{ 0xB8F0, 0xB8F0, 0xB8F0 },
+{ 0xB8F1, 0xB8F1, 0xB8F1 },
+{ 0xB8F2, 0xB8F2, 0xB8F2 },
+{ 0xB8F3, 0xB8F3, 0xB8F3 },
+{ 0xB8F4, 0xB8F4, 0xB8F4 },
+{ 0xB8F5, 0xB8F5, 0xB8F5 },
+{ 0xB8F6, 0xB8F6, 0xB8F6 },
+{ 0xB8F7, 0xB8F7, 0xB8F7 },
+{ 0xB8F8, 0xB8F8, 0xB8F8 },
+{ 0xB8F9, 0xB8F9, 0xB8F9 },
+{ 0xB8FA, 0xB8FA, 0xB8FA },
+{ 0xB8FB, 0xB8FB, 0xB8FB },
+{ 0xB8FC, 0xB8FC, 0xB8FC },
+{ 0xB8FD, 0xB8FD, 0xB8FD },
+{ 0xB8FE, 0xB8FE, 0xB8FE },
+{ 0xB8FF, 0xB8FF, 0xB8FF },
+{ 0xB900, 0xB900, 0xB900 },
+{ 0xB901, 0xB901, 0xB901 },
+{ 0xB902, 0xB902, 0xB902 },
+{ 0xB903, 0xB903, 0xB903 },
+{ 0xB904, 0xB904, 0xB904 },
+{ 0xB905, 0xB905, 0xB905 },
+{ 0xB906, 0xB906, 0xB906 },
+{ 0xB907, 0xB907, 0xB907 },
+{ 0xB908, 0xB908, 0xB908 },
+{ 0xB909, 0xB909, 0xB909 },
+{ 0xB90A, 0xB90A, 0xB90A },
+{ 0xB90B, 0xB90B, 0xB90B },
+{ 0xB90C, 0xB90C, 0xB90C },
+{ 0xB90D, 0xB90D, 0xB90D },
+{ 0xB90E, 0xB90E, 0xB90E },
+{ 0xB90F, 0xB90F, 0xB90F },
+{ 0xB910, 0xB910, 0xB910 },
+{ 0xB911, 0xB911, 0xB911 },
+{ 0xB912, 0xB912, 0xB912 },
+{ 0xB913, 0xB913, 0xB913 },
+{ 0xB914, 0xB914, 0xB914 },
+{ 0xB915, 0xB915, 0xB915 },
+{ 0xB916, 0xB916, 0xB916 },
+{ 0xB917, 0xB917, 0xB917 },
+{ 0xB918, 0xB918, 0xB918 },
+{ 0xB919, 0xB919, 0xB919 },
+{ 0xB91A, 0xB91A, 0xB91A },
+{ 0xB91B, 0xB91B, 0xB91B },
+{ 0xB91C, 0xB91C, 0xB91C },
+{ 0xB91D, 0xB91D, 0xB91D },
+{ 0xB91E, 0xB91E, 0xB91E },
+{ 0xB91F, 0xB91F, 0xB91F },
+{ 0xB920, 0xB920, 0xB920 },
+{ 0xB921, 0xB921, 0xB921 },
+{ 0xB922, 0xB922, 0xB922 },
+{ 0xB923, 0xB923, 0xB923 },
+{ 0xB924, 0xB924, 0xB924 },
+{ 0xB925, 0xB925, 0xB925 },
+{ 0xB926, 0xB926, 0xB926 },
+{ 0xB927, 0xB927, 0xB927 },
+{ 0xB928, 0xB928, 0xB928 },
+{ 0xB929, 0xB929, 0xB929 },
+{ 0xB92A, 0xB92A, 0xB92A },
+{ 0xB92B, 0xB92B, 0xB92B },
+{ 0xB92C, 0xB92C, 0xB92C },
+{ 0xB92D, 0xB92D, 0xB92D },
+{ 0xB92E, 0xB92E, 0xB92E },
+{ 0xB92F, 0xB92F, 0xB92F },
+{ 0xB930, 0xB930, 0xB930 },
+{ 0xB931, 0xB931, 0xB931 },
+{ 0xB932, 0xB932, 0xB932 },
+{ 0xB933, 0xB933, 0xB933 },
+{ 0xB934, 0xB934, 0xB934 },
+{ 0xB935, 0xB935, 0xB935 },
+{ 0xB936, 0xB936, 0xB936 },
+{ 0xB937, 0xB937, 0xB937 },
+{ 0xB938, 0xB938, 0xB938 },
+{ 0xB939, 0xB939, 0xB939 },
+{ 0xB93A, 0xB93A, 0xB93A },
+{ 0xB93B, 0xB93B, 0xB93B },
+{ 0xB93C, 0xB93C, 0xB93C },
+{ 0xB93D, 0xB93D, 0xB93D },
+{ 0xB93E, 0xB93E, 0xB93E },
+{ 0xB93F, 0xB93F, 0xB93F },
+{ 0xB940, 0xB940, 0xB940 },
+{ 0xB941, 0xB941, 0xB941 },
+{ 0xB942, 0xB942, 0xB942 },
+{ 0xB943, 0xB943, 0xB943 },
+{ 0xB944, 0xB944, 0xB944 },
+{ 0xB945, 0xB945, 0xB945 },
+{ 0xB946, 0xB946, 0xB946 },
+{ 0xB947, 0xB947, 0xB947 },
+{ 0xB948, 0xB948, 0xB948 },
+{ 0xB949, 0xB949, 0xB949 },
+{ 0xB94A, 0xB94A, 0xB94A },
+{ 0xB94B, 0xB94B, 0xB94B },
+{ 0xB94C, 0xB94C, 0xB94C },
+{ 0xB94D, 0xB94D, 0xB94D },
+{ 0xB94E, 0xB94E, 0xB94E },
+{ 0xB94F, 0xB94F, 0xB94F },
+{ 0xB950, 0xB950, 0xB950 },
+{ 0xB951, 0xB951, 0xB951 },
+{ 0xB952, 0xB952, 0xB952 },
+{ 0xB953, 0xB953, 0xB953 },
+{ 0xB954, 0xB954, 0xB954 },
+{ 0xB955, 0xB955, 0xB955 },
+{ 0xB956, 0xB956, 0xB956 },
+{ 0xB957, 0xB957, 0xB957 },
+{ 0xB958, 0xB958, 0xB958 },
+{ 0xB959, 0xB959, 0xB959 },
+{ 0xB95A, 0xB95A, 0xB95A },
+{ 0xB95B, 0xB95B, 0xB95B },
+{ 0xB95C, 0xB95C, 0xB95C },
+{ 0xB95D, 0xB95D, 0xB95D },
+{ 0xB95E, 0xB95E, 0xB95E },
+{ 0xB95F, 0xB95F, 0xB95F },
+{ 0xB960, 0xB960, 0xB960 },
+{ 0xB961, 0xB961, 0xB961 },
+{ 0xB962, 0xB962, 0xB962 },
+{ 0xB963, 0xB963, 0xB963 },
+{ 0xB964, 0xB964, 0xB964 },
+{ 0xB965, 0xB965, 0xB965 },
+{ 0xB966, 0xB966, 0xB966 },
+{ 0xB967, 0xB967, 0xB967 },
+{ 0xB968, 0xB968, 0xB968 },
+{ 0xB969, 0xB969, 0xB969 },
+{ 0xB96A, 0xB96A, 0xB96A },
+{ 0xB96B, 0xB96B, 0xB96B },
+{ 0xB96C, 0xB96C, 0xB96C },
+{ 0xB96D, 0xB96D, 0xB96D },
+{ 0xB96E, 0xB96E, 0xB96E },
+{ 0xB96F, 0xB96F, 0xB96F },
+{ 0xB970, 0xB970, 0xB970 },
+{ 0xB971, 0xB971, 0xB971 },
+{ 0xB972, 0xB972, 0xB972 },
+{ 0xB973, 0xB973, 0xB973 },
+{ 0xB974, 0xB974, 0xB974 },
+{ 0xB975, 0xB975, 0xB975 },
+{ 0xB976, 0xB976, 0xB976 },
+{ 0xB977, 0xB977, 0xB977 },
+{ 0xB978, 0xB978, 0xB978 },
+{ 0xB979, 0xB979, 0xB979 },
+{ 0xB97A, 0xB97A, 0xB97A },
+{ 0xB97B, 0xB97B, 0xB97B },
+{ 0xB97C, 0xB97C, 0xB97C },
+{ 0xB97D, 0xB97D, 0xB97D },
+{ 0xB97E, 0xB97E, 0xB97E },
+{ 0xB97F, 0xB97F, 0xB97F },
+{ 0xB980, 0xB980, 0xB980 },
+{ 0xB981, 0xB981, 0xB981 },
+{ 0xB982, 0xB982, 0xB982 },
+{ 0xB983, 0xB983, 0xB983 },
+{ 0xB984, 0xB984, 0xB984 },
+{ 0xB985, 0xB985, 0xB985 },
+{ 0xB986, 0xB986, 0xB986 },
+{ 0xB987, 0xB987, 0xB987 },
+{ 0xB988, 0xB988, 0xB988 },
+{ 0xB989, 0xB989, 0xB989 },
+{ 0xB98A, 0xB98A, 0xB98A },
+{ 0xB98B, 0xB98B, 0xB98B },
+{ 0xB98C, 0xB98C, 0xB98C },
+{ 0xB98D, 0xB98D, 0xB98D },
+{ 0xB98E, 0xB98E, 0xB98E },
+{ 0xB98F, 0xB98F, 0xB98F },
+{ 0xB990, 0xB990, 0xB990 },
+{ 0xB991, 0xB991, 0xB991 },
+{ 0xB992, 0xB992, 0xB992 },
+{ 0xB993, 0xB993, 0xB993 },
+{ 0xB994, 0xB994, 0xB994 },
+{ 0xB995, 0xB995, 0xB995 },
+{ 0xB996, 0xB996, 0xB996 },
+{ 0xB997, 0xB997, 0xB997 },
+{ 0xB998, 0xB998, 0xB998 },
+{ 0xB999, 0xB999, 0xB999 },
+{ 0xB99A, 0xB99A, 0xB99A },
+{ 0xB99B, 0xB99B, 0xB99B },
+{ 0xB99C, 0xB99C, 0xB99C },
+{ 0xB99D, 0xB99D, 0xB99D },
+{ 0xB99E, 0xB99E, 0xB99E },
+{ 0xB99F, 0xB99F, 0xB99F },
+{ 0xB9A0, 0xB9A0, 0xB9A0 },
+{ 0xB9A1, 0xB9A1, 0xB9A1 },
+{ 0xB9A2, 0xB9A2, 0xB9A2 },
+{ 0xB9A3, 0xB9A3, 0xB9A3 },
+{ 0xB9A4, 0xB9A4, 0xB9A4 },
+{ 0xB9A5, 0xB9A5, 0xB9A5 },
+{ 0xB9A6, 0xB9A6, 0xB9A6 },
+{ 0xB9A7, 0xB9A7, 0xB9A7 },
+{ 0xB9A8, 0xB9A8, 0xB9A8 },
+{ 0xB9A9, 0xB9A9, 0xB9A9 },
+{ 0xB9AA, 0xB9AA, 0xB9AA },
+{ 0xB9AB, 0xB9AB, 0xB9AB },
+{ 0xB9AC, 0xB9AC, 0xB9AC },
+{ 0xB9AD, 0xB9AD, 0xB9AD },
+{ 0xB9AE, 0xB9AE, 0xB9AE },
+{ 0xB9AF, 0xB9AF, 0xB9AF },
+{ 0xB9B0, 0xB9B0, 0xB9B0 },
+{ 0xB9B1, 0xB9B1, 0xB9B1 },
+{ 0xB9B2, 0xB9B2, 0xB9B2 },
+{ 0xB9B3, 0xB9B3, 0xB9B3 },
+{ 0xB9B4, 0xB9B4, 0xB9B4 },
+{ 0xB9B5, 0xB9B5, 0xB9B5 },
+{ 0xB9B6, 0xB9B6, 0xB9B6 },
+{ 0xB9B7, 0xB9B7, 0xB9B7 },
+{ 0xB9B8, 0xB9B8, 0xB9B8 },
+{ 0xB9B9, 0xB9B9, 0xB9B9 },
+{ 0xB9BA, 0xB9BA, 0xB9BA },
+{ 0xB9BB, 0xB9BB, 0xB9BB },
+{ 0xB9BC, 0xB9BC, 0xB9BC },
+{ 0xB9BD, 0xB9BD, 0xB9BD },
+{ 0xB9BE, 0xB9BE, 0xB9BE },
+{ 0xB9BF, 0xB9BF, 0xB9BF },
+{ 0xB9C0, 0xB9C0, 0xB9C0 },
+{ 0xB9C1, 0xB9C1, 0xB9C1 },
+{ 0xB9C2, 0xB9C2, 0xB9C2 },
+{ 0xB9C3, 0xB9C3, 0xB9C3 },
+{ 0xB9C4, 0xB9C4, 0xB9C4 },
+{ 0xB9C5, 0xB9C5, 0xB9C5 },
+{ 0xB9C6, 0xB9C6, 0xB9C6 },
+{ 0xB9C7, 0xB9C7, 0xB9C7 },
+{ 0xB9C8, 0xB9C8, 0xB9C8 },
+{ 0xB9C9, 0xB9C9, 0xB9C9 },
+{ 0xB9CA, 0xB9CA, 0xB9CA },
+{ 0xB9CB, 0xB9CB, 0xB9CB },
+{ 0xB9CC, 0xB9CC, 0xB9CC },
+{ 0xB9CD, 0xB9CD, 0xB9CD },
+{ 0xB9CE, 0xB9CE, 0xB9CE },
+{ 0xB9CF, 0xB9CF, 0xB9CF },
+{ 0xB9D0, 0xB9D0, 0xB9D0 },
+{ 0xB9D1, 0xB9D1, 0xB9D1 },
+{ 0xB9D2, 0xB9D2, 0xB9D2 },
+{ 0xB9D3, 0xB9D3, 0xB9D3 },
+{ 0xB9D4, 0xB9D4, 0xB9D4 },
+{ 0xB9D5, 0xB9D5, 0xB9D5 },
+{ 0xB9D6, 0xB9D6, 0xB9D6 },
+{ 0xB9D7, 0xB9D7, 0xB9D7 },
+{ 0xB9D8, 0xB9D8, 0xB9D8 },
+{ 0xB9D9, 0xB9D9, 0xB9D9 },
+{ 0xB9DA, 0xB9DA, 0xB9DA },
+{ 0xB9DB, 0xB9DB, 0xB9DB },
+{ 0xB9DC, 0xB9DC, 0xB9DC },
+{ 0xB9DD, 0xB9DD, 0xB9DD },
+{ 0xB9DE, 0xB9DE, 0xB9DE },
+{ 0xB9DF, 0xB9DF, 0xB9DF },
+{ 0xB9E0, 0xB9E0, 0xB9E0 },
+{ 0xB9E1, 0xB9E1, 0xB9E1 },
+{ 0xB9E2, 0xB9E2, 0xB9E2 },
+{ 0xB9E3, 0xB9E3, 0xB9E3 },
+{ 0xB9E4, 0xB9E4, 0xB9E4 },
+{ 0xB9E5, 0xB9E5, 0xB9E5 },
+{ 0xB9E6, 0xB9E6, 0xB9E6 },
+{ 0xB9E7, 0xB9E7, 0xB9E7 },
+{ 0xB9E8, 0xB9E8, 0xB9E8 },
+{ 0xB9E9, 0xB9E9, 0xB9E9 },
+{ 0xB9EA, 0xB9EA, 0xB9EA },
+{ 0xB9EB, 0xB9EB, 0xB9EB },
+{ 0xB9EC, 0xB9EC, 0xB9EC },
+{ 0xB9ED, 0xB9ED, 0xB9ED },
+{ 0xB9EE, 0xB9EE, 0xB9EE },
+{ 0xB9EF, 0xB9EF, 0xB9EF },
+{ 0xB9F0, 0xB9F0, 0xB9F0 },
+{ 0xB9F1, 0xB9F1, 0xB9F1 },
+{ 0xB9F2, 0xB9F2, 0xB9F2 },
+{ 0xB9F3, 0xB9F3, 0xB9F3 },
+{ 0xB9F4, 0xB9F4, 0xB9F4 },
+{ 0xB9F5, 0xB9F5, 0xB9F5 },
+{ 0xB9F6, 0xB9F6, 0xB9F6 },
+{ 0xB9F7, 0xB9F7, 0xB9F7 },
+{ 0xB9F8, 0xB9F8, 0xB9F8 },
+{ 0xB9F9, 0xB9F9, 0xB9F9 },
+{ 0xB9FA, 0xB9FA, 0xB9FA },
+{ 0xB9FB, 0xB9FB, 0xB9FB },
+{ 0xB9FC, 0xB9FC, 0xB9FC },
+{ 0xB9FD, 0xB9FD, 0xB9FD },
+{ 0xB9FE, 0xB9FE, 0xB9FE },
+{ 0xB9FF, 0xB9FF, 0xB9FF },
+{ 0xBA00, 0xBA00, 0xBA00 },
+{ 0xBA01, 0xBA01, 0xBA01 },
+{ 0xBA02, 0xBA02, 0xBA02 },
+{ 0xBA03, 0xBA03, 0xBA03 },
+{ 0xBA04, 0xBA04, 0xBA04 },
+{ 0xBA05, 0xBA05, 0xBA05 },
+{ 0xBA06, 0xBA06, 0xBA06 },
+{ 0xBA07, 0xBA07, 0xBA07 },
+{ 0xBA08, 0xBA08, 0xBA08 },
+{ 0xBA09, 0xBA09, 0xBA09 },
+{ 0xBA0A, 0xBA0A, 0xBA0A },
+{ 0xBA0B, 0xBA0B, 0xBA0B },
+{ 0xBA0C, 0xBA0C, 0xBA0C },
+{ 0xBA0D, 0xBA0D, 0xBA0D },
+{ 0xBA0E, 0xBA0E, 0xBA0E },
+{ 0xBA0F, 0xBA0F, 0xBA0F },
+{ 0xBA10, 0xBA10, 0xBA10 },
+{ 0xBA11, 0xBA11, 0xBA11 },
+{ 0xBA12, 0xBA12, 0xBA12 },
+{ 0xBA13, 0xBA13, 0xBA13 },
+{ 0xBA14, 0xBA14, 0xBA14 },
+{ 0xBA15, 0xBA15, 0xBA15 },
+{ 0xBA16, 0xBA16, 0xBA16 },
+{ 0xBA17, 0xBA17, 0xBA17 },
+{ 0xBA18, 0xBA18, 0xBA18 },
+{ 0xBA19, 0xBA19, 0xBA19 },
+{ 0xBA1A, 0xBA1A, 0xBA1A },
+{ 0xBA1B, 0xBA1B, 0xBA1B },
+{ 0xBA1C, 0xBA1C, 0xBA1C },
+{ 0xBA1D, 0xBA1D, 0xBA1D },
+{ 0xBA1E, 0xBA1E, 0xBA1E },
+{ 0xBA1F, 0xBA1F, 0xBA1F },
+{ 0xBA20, 0xBA20, 0xBA20 },
+{ 0xBA21, 0xBA21, 0xBA21 },
+{ 0xBA22, 0xBA22, 0xBA22 },
+{ 0xBA23, 0xBA23, 0xBA23 },
+{ 0xBA24, 0xBA24, 0xBA24 },
+{ 0xBA25, 0xBA25, 0xBA25 },
+{ 0xBA26, 0xBA26, 0xBA26 },
+{ 0xBA27, 0xBA27, 0xBA27 },
+{ 0xBA28, 0xBA28, 0xBA28 },
+{ 0xBA29, 0xBA29, 0xBA29 },
+{ 0xBA2A, 0xBA2A, 0xBA2A },
+{ 0xBA2B, 0xBA2B, 0xBA2B },
+{ 0xBA2C, 0xBA2C, 0xBA2C },
+{ 0xBA2D, 0xBA2D, 0xBA2D },
+{ 0xBA2E, 0xBA2E, 0xBA2E },
+{ 0xBA2F, 0xBA2F, 0xBA2F },
+{ 0xBA30, 0xBA30, 0xBA30 },
+{ 0xBA31, 0xBA31, 0xBA31 },
+{ 0xBA32, 0xBA32, 0xBA32 },
+{ 0xBA33, 0xBA33, 0xBA33 },
+{ 0xBA34, 0xBA34, 0xBA34 },
+{ 0xBA35, 0xBA35, 0xBA35 },
+{ 0xBA36, 0xBA36, 0xBA36 },
+{ 0xBA37, 0xBA37, 0xBA37 },
+{ 0xBA38, 0xBA38, 0xBA38 },
+{ 0xBA39, 0xBA39, 0xBA39 },
+{ 0xBA3A, 0xBA3A, 0xBA3A },
+{ 0xBA3B, 0xBA3B, 0xBA3B },
+{ 0xBA3C, 0xBA3C, 0xBA3C },
+{ 0xBA3D, 0xBA3D, 0xBA3D },
+{ 0xBA3E, 0xBA3E, 0xBA3E },
+{ 0xBA3F, 0xBA3F, 0xBA3F },
+{ 0xBA40, 0xBA40, 0xBA40 },
+{ 0xBA41, 0xBA41, 0xBA41 },
+{ 0xBA42, 0xBA42, 0xBA42 },
+{ 0xBA43, 0xBA43, 0xBA43 },
+{ 0xBA44, 0xBA44, 0xBA44 },
+{ 0xBA45, 0xBA45, 0xBA45 },
+{ 0xBA46, 0xBA46, 0xBA46 },
+{ 0xBA47, 0xBA47, 0xBA47 },
+{ 0xBA48, 0xBA48, 0xBA48 },
+{ 0xBA49, 0xBA49, 0xBA49 },
+{ 0xBA4A, 0xBA4A, 0xBA4A },
+{ 0xBA4B, 0xBA4B, 0xBA4B },
+{ 0xBA4C, 0xBA4C, 0xBA4C },
+{ 0xBA4D, 0xBA4D, 0xBA4D },
+{ 0xBA4E, 0xBA4E, 0xBA4E },
+{ 0xBA4F, 0xBA4F, 0xBA4F },
+{ 0xBA50, 0xBA50, 0xBA50 },
+{ 0xBA51, 0xBA51, 0xBA51 },
+{ 0xBA52, 0xBA52, 0xBA52 },
+{ 0xBA53, 0xBA53, 0xBA53 },
+{ 0xBA54, 0xBA54, 0xBA54 },
+{ 0xBA55, 0xBA55, 0xBA55 },
+{ 0xBA56, 0xBA56, 0xBA56 },
+{ 0xBA57, 0xBA57, 0xBA57 },
+{ 0xBA58, 0xBA58, 0xBA58 },
+{ 0xBA59, 0xBA59, 0xBA59 },
+{ 0xBA5A, 0xBA5A, 0xBA5A },
+{ 0xBA5B, 0xBA5B, 0xBA5B },
+{ 0xBA5C, 0xBA5C, 0xBA5C },
+{ 0xBA5D, 0xBA5D, 0xBA5D },
+{ 0xBA5E, 0xBA5E, 0xBA5E },
+{ 0xBA5F, 0xBA5F, 0xBA5F },
+{ 0xBA60, 0xBA60, 0xBA60 },
+{ 0xBA61, 0xBA61, 0xBA61 },
+{ 0xBA62, 0xBA62, 0xBA62 },
+{ 0xBA63, 0xBA63, 0xBA63 },
+{ 0xBA64, 0xBA64, 0xBA64 },
+{ 0xBA65, 0xBA65, 0xBA65 },
+{ 0xBA66, 0xBA66, 0xBA66 },
+{ 0xBA67, 0xBA67, 0xBA67 },
+{ 0xBA68, 0xBA68, 0xBA68 },
+{ 0xBA69, 0xBA69, 0xBA69 },
+{ 0xBA6A, 0xBA6A, 0xBA6A },
+{ 0xBA6B, 0xBA6B, 0xBA6B },
+{ 0xBA6C, 0xBA6C, 0xBA6C },
+{ 0xBA6D, 0xBA6D, 0xBA6D },
+{ 0xBA6E, 0xBA6E, 0xBA6E },
+{ 0xBA6F, 0xBA6F, 0xBA6F },
+{ 0xBA70, 0xBA70, 0xBA70 },
+{ 0xBA71, 0xBA71, 0xBA71 },
+{ 0xBA72, 0xBA72, 0xBA72 },
+{ 0xBA73, 0xBA73, 0xBA73 },
+{ 0xBA74, 0xBA74, 0xBA74 },
+{ 0xBA75, 0xBA75, 0xBA75 },
+{ 0xBA76, 0xBA76, 0xBA76 },
+{ 0xBA77, 0xBA77, 0xBA77 },
+{ 0xBA78, 0xBA78, 0xBA78 },
+{ 0xBA79, 0xBA79, 0xBA79 },
+{ 0xBA7A, 0xBA7A, 0xBA7A },
+{ 0xBA7B, 0xBA7B, 0xBA7B },
+{ 0xBA7C, 0xBA7C, 0xBA7C },
+{ 0xBA7D, 0xBA7D, 0xBA7D },
+{ 0xBA7E, 0xBA7E, 0xBA7E },
+{ 0xBA7F, 0xBA7F, 0xBA7F },
+{ 0xBA80, 0xBA80, 0xBA80 },
+{ 0xBA81, 0xBA81, 0xBA81 },
+{ 0xBA82, 0xBA82, 0xBA82 },
+{ 0xBA83, 0xBA83, 0xBA83 },
+{ 0xBA84, 0xBA84, 0xBA84 },
+{ 0xBA85, 0xBA85, 0xBA85 },
+{ 0xBA86, 0xBA86, 0xBA86 },
+{ 0xBA87, 0xBA87, 0xBA87 },
+{ 0xBA88, 0xBA88, 0xBA88 },
+{ 0xBA89, 0xBA89, 0xBA89 },
+{ 0xBA8A, 0xBA8A, 0xBA8A },
+{ 0xBA8B, 0xBA8B, 0xBA8B },
+{ 0xBA8C, 0xBA8C, 0xBA8C },
+{ 0xBA8D, 0xBA8D, 0xBA8D },
+{ 0xBA8E, 0xBA8E, 0xBA8E },
+{ 0xBA8F, 0xBA8F, 0xBA8F },
+{ 0xBA90, 0xBA90, 0xBA90 },
+{ 0xBA91, 0xBA91, 0xBA91 },
+{ 0xBA92, 0xBA92, 0xBA92 },
+{ 0xBA93, 0xBA93, 0xBA93 },
+{ 0xBA94, 0xBA94, 0xBA94 },
+{ 0xBA95, 0xBA95, 0xBA95 },
+{ 0xBA96, 0xBA96, 0xBA96 },
+{ 0xBA97, 0xBA97, 0xBA97 },
+{ 0xBA98, 0xBA98, 0xBA98 },
+{ 0xBA99, 0xBA99, 0xBA99 },
+{ 0xBA9A, 0xBA9A, 0xBA9A },
+{ 0xBA9B, 0xBA9B, 0xBA9B },
+{ 0xBA9C, 0xBA9C, 0xBA9C },
+{ 0xBA9D, 0xBA9D, 0xBA9D },
+{ 0xBA9E, 0xBA9E, 0xBA9E },
+{ 0xBA9F, 0xBA9F, 0xBA9F },
+{ 0xBAA0, 0xBAA0, 0xBAA0 },
+{ 0xBAA1, 0xBAA1, 0xBAA1 },
+{ 0xBAA2, 0xBAA2, 0xBAA2 },
+{ 0xBAA3, 0xBAA3, 0xBAA3 },
+{ 0xBAA4, 0xBAA4, 0xBAA4 },
+{ 0xBAA5, 0xBAA5, 0xBAA5 },
+{ 0xBAA6, 0xBAA6, 0xBAA6 },
+{ 0xBAA7, 0xBAA7, 0xBAA7 },
+{ 0xBAA8, 0xBAA8, 0xBAA8 },
+{ 0xBAA9, 0xBAA9, 0xBAA9 },
+{ 0xBAAA, 0xBAAA, 0xBAAA },
+{ 0xBAAB, 0xBAAB, 0xBAAB },
+{ 0xBAAC, 0xBAAC, 0xBAAC },
+{ 0xBAAD, 0xBAAD, 0xBAAD },
+{ 0xBAAE, 0xBAAE, 0xBAAE },
+{ 0xBAAF, 0xBAAF, 0xBAAF },
+{ 0xBAB0, 0xBAB0, 0xBAB0 },
+{ 0xBAB1, 0xBAB1, 0xBAB1 },
+{ 0xBAB2, 0xBAB2, 0xBAB2 },
+{ 0xBAB3, 0xBAB3, 0xBAB3 },
+{ 0xBAB4, 0xBAB4, 0xBAB4 },
+{ 0xBAB5, 0xBAB5, 0xBAB5 },
+{ 0xBAB6, 0xBAB6, 0xBAB6 },
+{ 0xBAB7, 0xBAB7, 0xBAB7 },
+{ 0xBAB8, 0xBAB8, 0xBAB8 },
+{ 0xBAB9, 0xBAB9, 0xBAB9 },
+{ 0xBABA, 0xBABA, 0xBABA },
+{ 0xBABB, 0xBABB, 0xBABB },
+{ 0xBABC, 0xBABC, 0xBABC },
+{ 0xBABD, 0xBABD, 0xBABD },
+{ 0xBABE, 0xBABE, 0xBABE },
+{ 0xBABF, 0xBABF, 0xBABF },
+{ 0xBAC0, 0xBAC0, 0xBAC0 },
+{ 0xBAC1, 0xBAC1, 0xBAC1 },
+{ 0xBAC2, 0xBAC2, 0xBAC2 },
+{ 0xBAC3, 0xBAC3, 0xBAC3 },
+{ 0xBAC4, 0xBAC4, 0xBAC4 },
+{ 0xBAC5, 0xBAC5, 0xBAC5 },
+{ 0xBAC6, 0xBAC6, 0xBAC6 },
+{ 0xBAC7, 0xBAC7, 0xBAC7 },
+{ 0xBAC8, 0xBAC8, 0xBAC8 },
+{ 0xBAC9, 0xBAC9, 0xBAC9 },
+{ 0xBACA, 0xBACA, 0xBACA },
+{ 0xBACB, 0xBACB, 0xBACB },
+{ 0xBACC, 0xBACC, 0xBACC },
+{ 0xBACD, 0xBACD, 0xBACD },
+{ 0xBACE, 0xBACE, 0xBACE },
+{ 0xBACF, 0xBACF, 0xBACF },
+{ 0xBAD0, 0xBAD0, 0xBAD0 },
+{ 0xBAD1, 0xBAD1, 0xBAD1 },
+{ 0xBAD2, 0xBAD2, 0xBAD2 },
+{ 0xBAD3, 0xBAD3, 0xBAD3 },
+{ 0xBAD4, 0xBAD4, 0xBAD4 },
+{ 0xBAD5, 0xBAD5, 0xBAD5 },
+{ 0xBAD6, 0xBAD6, 0xBAD6 },
+{ 0xBAD7, 0xBAD7, 0xBAD7 },
+{ 0xBAD8, 0xBAD8, 0xBAD8 },
+{ 0xBAD9, 0xBAD9, 0xBAD9 },
+{ 0xBADA, 0xBADA, 0xBADA },
+{ 0xBADB, 0xBADB, 0xBADB },
+{ 0xBADC, 0xBADC, 0xBADC },
+{ 0xBADD, 0xBADD, 0xBADD },
+{ 0xBADE, 0xBADE, 0xBADE },
+{ 0xBADF, 0xBADF, 0xBADF },
+{ 0xBAE0, 0xBAE0, 0xBAE0 },
+{ 0xBAE1, 0xBAE1, 0xBAE1 },
+{ 0xBAE2, 0xBAE2, 0xBAE2 },
+{ 0xBAE3, 0xBAE3, 0xBAE3 },
+{ 0xBAE4, 0xBAE4, 0xBAE4 },
+{ 0xBAE5, 0xBAE5, 0xBAE5 },
+{ 0xBAE6, 0xBAE6, 0xBAE6 },
+{ 0xBAE7, 0xBAE7, 0xBAE7 },
+{ 0xBAE8, 0xBAE8, 0xBAE8 },
+{ 0xBAE9, 0xBAE9, 0xBAE9 },
+{ 0xBAEA, 0xBAEA, 0xBAEA },
+{ 0xBAEB, 0xBAEB, 0xBAEB },
+{ 0xBAEC, 0xBAEC, 0xBAEC },
+{ 0xBAED, 0xBAED, 0xBAED },
+{ 0xBAEE, 0xBAEE, 0xBAEE },
+{ 0xBAEF, 0xBAEF, 0xBAEF },
+{ 0xBAF0, 0xBAF0, 0xBAF0 },
+{ 0xBAF1, 0xBAF1, 0xBAF1 },
+{ 0xBAF2, 0xBAF2, 0xBAF2 },
+{ 0xBAF3, 0xBAF3, 0xBAF3 },
+{ 0xBAF4, 0xBAF4, 0xBAF4 },
+{ 0xBAF5, 0xBAF5, 0xBAF5 },
+{ 0xBAF6, 0xBAF6, 0xBAF6 },
+{ 0xBAF7, 0xBAF7, 0xBAF7 },
+{ 0xBAF8, 0xBAF8, 0xBAF8 },
+{ 0xBAF9, 0xBAF9, 0xBAF9 },
+{ 0xBAFA, 0xBAFA, 0xBAFA },
+{ 0xBAFB, 0xBAFB, 0xBAFB },
+{ 0xBAFC, 0xBAFC, 0xBAFC },
+{ 0xBAFD, 0xBAFD, 0xBAFD },
+{ 0xBAFE, 0xBAFE, 0xBAFE },
+{ 0xBAFF, 0xBAFF, 0xBAFF },
+{ 0xBB00, 0xBB00, 0xBB00 },
+{ 0xBB01, 0xBB01, 0xBB01 },
+{ 0xBB02, 0xBB02, 0xBB02 },
+{ 0xBB03, 0xBB03, 0xBB03 },
+{ 0xBB04, 0xBB04, 0xBB04 },
+{ 0xBB05, 0xBB05, 0xBB05 },
+{ 0xBB06, 0xBB06, 0xBB06 },
+{ 0xBB07, 0xBB07, 0xBB07 },
+{ 0xBB08, 0xBB08, 0xBB08 },
+{ 0xBB09, 0xBB09, 0xBB09 },
+{ 0xBB0A, 0xBB0A, 0xBB0A },
+{ 0xBB0B, 0xBB0B, 0xBB0B },
+{ 0xBB0C, 0xBB0C, 0xBB0C },
+{ 0xBB0D, 0xBB0D, 0xBB0D },
+{ 0xBB0E, 0xBB0E, 0xBB0E },
+{ 0xBB0F, 0xBB0F, 0xBB0F },
+{ 0xBB10, 0xBB10, 0xBB10 },
+{ 0xBB11, 0xBB11, 0xBB11 },
+{ 0xBB12, 0xBB12, 0xBB12 },
+{ 0xBB13, 0xBB13, 0xBB13 },
+{ 0xBB14, 0xBB14, 0xBB14 },
+{ 0xBB15, 0xBB15, 0xBB15 },
+{ 0xBB16, 0xBB16, 0xBB16 },
+{ 0xBB17, 0xBB17, 0xBB17 },
+{ 0xBB18, 0xBB18, 0xBB18 },
+{ 0xBB19, 0xBB19, 0xBB19 },
+{ 0xBB1A, 0xBB1A, 0xBB1A },
+{ 0xBB1B, 0xBB1B, 0xBB1B },
+{ 0xBB1C, 0xBB1C, 0xBB1C },
+{ 0xBB1D, 0xBB1D, 0xBB1D },
+{ 0xBB1E, 0xBB1E, 0xBB1E },
+{ 0xBB1F, 0xBB1F, 0xBB1F },
+{ 0xBB20, 0xBB20, 0xBB20 },
+{ 0xBB21, 0xBB21, 0xBB21 },
+{ 0xBB22, 0xBB22, 0xBB22 },
+{ 0xBB23, 0xBB23, 0xBB23 },
+{ 0xBB24, 0xBB24, 0xBB24 },
+{ 0xBB25, 0xBB25, 0xBB25 },
+{ 0xBB26, 0xBB26, 0xBB26 },
+{ 0xBB27, 0xBB27, 0xBB27 },
+{ 0xBB28, 0xBB28, 0xBB28 },
+{ 0xBB29, 0xBB29, 0xBB29 },
+{ 0xBB2A, 0xBB2A, 0xBB2A },
+{ 0xBB2B, 0xBB2B, 0xBB2B },
+{ 0xBB2C, 0xBB2C, 0xBB2C },
+{ 0xBB2D, 0xBB2D, 0xBB2D },
+{ 0xBB2E, 0xBB2E, 0xBB2E },
+{ 0xBB2F, 0xBB2F, 0xBB2F },
+{ 0xBB30, 0xBB30, 0xBB30 },
+{ 0xBB31, 0xBB31, 0xBB31 },
+{ 0xBB32, 0xBB32, 0xBB32 },
+{ 0xBB33, 0xBB33, 0xBB33 },
+{ 0xBB34, 0xBB34, 0xBB34 },
+{ 0xBB35, 0xBB35, 0xBB35 },
+{ 0xBB36, 0xBB36, 0xBB36 },
+{ 0xBB37, 0xBB37, 0xBB37 },
+{ 0xBB38, 0xBB38, 0xBB38 },
+{ 0xBB39, 0xBB39, 0xBB39 },
+{ 0xBB3A, 0xBB3A, 0xBB3A },
+{ 0xBB3B, 0xBB3B, 0xBB3B },
+{ 0xBB3C, 0xBB3C, 0xBB3C },
+{ 0xBB3D, 0xBB3D, 0xBB3D },
+{ 0xBB3E, 0xBB3E, 0xBB3E },
+{ 0xBB3F, 0xBB3F, 0xBB3F },
+{ 0xBB40, 0xBB40, 0xBB40 },
+{ 0xBB41, 0xBB41, 0xBB41 },
+{ 0xBB42, 0xBB42, 0xBB42 },
+{ 0xBB43, 0xBB43, 0xBB43 },
+{ 0xBB44, 0xBB44, 0xBB44 },
+{ 0xBB45, 0xBB45, 0xBB45 },
+{ 0xBB46, 0xBB46, 0xBB46 },
+{ 0xBB47, 0xBB47, 0xBB47 },
+{ 0xBB48, 0xBB48, 0xBB48 },
+{ 0xBB49, 0xBB49, 0xBB49 },
+{ 0xBB4A, 0xBB4A, 0xBB4A },
+{ 0xBB4B, 0xBB4B, 0xBB4B },
+{ 0xBB4C, 0xBB4C, 0xBB4C },
+{ 0xBB4D, 0xBB4D, 0xBB4D },
+{ 0xBB4E, 0xBB4E, 0xBB4E },
+{ 0xBB4F, 0xBB4F, 0xBB4F },
+{ 0xBB50, 0xBB50, 0xBB50 },
+{ 0xBB51, 0xBB51, 0xBB51 },
+{ 0xBB52, 0xBB52, 0xBB52 },
+{ 0xBB53, 0xBB53, 0xBB53 },
+{ 0xBB54, 0xBB54, 0xBB54 },
+{ 0xBB55, 0xBB55, 0xBB55 },
+{ 0xBB56, 0xBB56, 0xBB56 },
+{ 0xBB57, 0xBB57, 0xBB57 },
+{ 0xBB58, 0xBB58, 0xBB58 },
+{ 0xBB59, 0xBB59, 0xBB59 },
+{ 0xBB5A, 0xBB5A, 0xBB5A },
+{ 0xBB5B, 0xBB5B, 0xBB5B },
+{ 0xBB5C, 0xBB5C, 0xBB5C },
+{ 0xBB5D, 0xBB5D, 0xBB5D },
+{ 0xBB5E, 0xBB5E, 0xBB5E },
+{ 0xBB5F, 0xBB5F, 0xBB5F },
+{ 0xBB60, 0xBB60, 0xBB60 },
+{ 0xBB61, 0xBB61, 0xBB61 },
+{ 0xBB62, 0xBB62, 0xBB62 },
+{ 0xBB63, 0xBB63, 0xBB63 },
+{ 0xBB64, 0xBB64, 0xBB64 },
+{ 0xBB65, 0xBB65, 0xBB65 },
+{ 0xBB66, 0xBB66, 0xBB66 },
+{ 0xBB67, 0xBB67, 0xBB67 },
+{ 0xBB68, 0xBB68, 0xBB68 },
+{ 0xBB69, 0xBB69, 0xBB69 },
+{ 0xBB6A, 0xBB6A, 0xBB6A },
+{ 0xBB6B, 0xBB6B, 0xBB6B },
+{ 0xBB6C, 0xBB6C, 0xBB6C },
+{ 0xBB6D, 0xBB6D, 0xBB6D },
+{ 0xBB6E, 0xBB6E, 0xBB6E },
+{ 0xBB6F, 0xBB6F, 0xBB6F },
+{ 0xBB70, 0xBB70, 0xBB70 },
+{ 0xBB71, 0xBB71, 0xBB71 },
+{ 0xBB72, 0xBB72, 0xBB72 },
+{ 0xBB73, 0xBB73, 0xBB73 },
+{ 0xBB74, 0xBB74, 0xBB74 },
+{ 0xBB75, 0xBB75, 0xBB75 },
+{ 0xBB76, 0xBB76, 0xBB76 },
+{ 0xBB77, 0xBB77, 0xBB77 },
+{ 0xBB78, 0xBB78, 0xBB78 },
+{ 0xBB79, 0xBB79, 0xBB79 },
+{ 0xBB7A, 0xBB7A, 0xBB7A },
+{ 0xBB7B, 0xBB7B, 0xBB7B },
+{ 0xBB7C, 0xBB7C, 0xBB7C },
+{ 0xBB7D, 0xBB7D, 0xBB7D },
+{ 0xBB7E, 0xBB7E, 0xBB7E },
+{ 0xBB7F, 0xBB7F, 0xBB7F },
+{ 0xBB80, 0xBB80, 0xBB80 },
+{ 0xBB81, 0xBB81, 0xBB81 },
+{ 0xBB82, 0xBB82, 0xBB82 },
+{ 0xBB83, 0xBB83, 0xBB83 },
+{ 0xBB84, 0xBB84, 0xBB84 },
+{ 0xBB85, 0xBB85, 0xBB85 },
+{ 0xBB86, 0xBB86, 0xBB86 },
+{ 0xBB87, 0xBB87, 0xBB87 },
+{ 0xBB88, 0xBB88, 0xBB88 },
+{ 0xBB89, 0xBB89, 0xBB89 },
+{ 0xBB8A, 0xBB8A, 0xBB8A },
+{ 0xBB8B, 0xBB8B, 0xBB8B },
+{ 0xBB8C, 0xBB8C, 0xBB8C },
+{ 0xBB8D, 0xBB8D, 0xBB8D },
+{ 0xBB8E, 0xBB8E, 0xBB8E },
+{ 0xBB8F, 0xBB8F, 0xBB8F },
+{ 0xBB90, 0xBB90, 0xBB90 },
+{ 0xBB91, 0xBB91, 0xBB91 },
+{ 0xBB92, 0xBB92, 0xBB92 },
+{ 0xBB93, 0xBB93, 0xBB93 },
+{ 0xBB94, 0xBB94, 0xBB94 },
+{ 0xBB95, 0xBB95, 0xBB95 },
+{ 0xBB96, 0xBB96, 0xBB96 },
+{ 0xBB97, 0xBB97, 0xBB97 },
+{ 0xBB98, 0xBB98, 0xBB98 },
+{ 0xBB99, 0xBB99, 0xBB99 },
+{ 0xBB9A, 0xBB9A, 0xBB9A },
+{ 0xBB9B, 0xBB9B, 0xBB9B },
+{ 0xBB9C, 0xBB9C, 0xBB9C },
+{ 0xBB9D, 0xBB9D, 0xBB9D },
+{ 0xBB9E, 0xBB9E, 0xBB9E },
+{ 0xBB9F, 0xBB9F, 0xBB9F },
+{ 0xBBA0, 0xBBA0, 0xBBA0 },
+{ 0xBBA1, 0xBBA1, 0xBBA1 },
+{ 0xBBA2, 0xBBA2, 0xBBA2 },
+{ 0xBBA3, 0xBBA3, 0xBBA3 },
+{ 0xBBA4, 0xBBA4, 0xBBA4 },
+{ 0xBBA5, 0xBBA5, 0xBBA5 },
+{ 0xBBA6, 0xBBA6, 0xBBA6 },
+{ 0xBBA7, 0xBBA7, 0xBBA7 },
+{ 0xBBA8, 0xBBA8, 0xBBA8 },
+{ 0xBBA9, 0xBBA9, 0xBBA9 },
+{ 0xBBAA, 0xBBAA, 0xBBAA },
+{ 0xBBAB, 0xBBAB, 0xBBAB },
+{ 0xBBAC, 0xBBAC, 0xBBAC },
+{ 0xBBAD, 0xBBAD, 0xBBAD },
+{ 0xBBAE, 0xBBAE, 0xBBAE },
+{ 0xBBAF, 0xBBAF, 0xBBAF },
+{ 0xBBB0, 0xBBB0, 0xBBB0 },
+{ 0xBBB1, 0xBBB1, 0xBBB1 },
+{ 0xBBB2, 0xBBB2, 0xBBB2 },
+{ 0xBBB3, 0xBBB3, 0xBBB3 },
+{ 0xBBB4, 0xBBB4, 0xBBB4 },
+{ 0xBBB5, 0xBBB5, 0xBBB5 },
+{ 0xBBB6, 0xBBB6, 0xBBB6 },
+{ 0xBBB7, 0xBBB7, 0xBBB7 },
+{ 0xBBB8, 0xBBB8, 0xBBB8 },
+{ 0xBBB9, 0xBBB9, 0xBBB9 },
+{ 0xBBBA, 0xBBBA, 0xBBBA },
+{ 0xBBBB, 0xBBBB, 0xBBBB },
+{ 0xBBBC, 0xBBBC, 0xBBBC },
+{ 0xBBBD, 0xBBBD, 0xBBBD },
+{ 0xBBBE, 0xBBBE, 0xBBBE },
+{ 0xBBBF, 0xBBBF, 0xBBBF },
+{ 0xBBC0, 0xBBC0, 0xBBC0 },
+{ 0xBBC1, 0xBBC1, 0xBBC1 },
+{ 0xBBC2, 0xBBC2, 0xBBC2 },
+{ 0xBBC3, 0xBBC3, 0xBBC3 },
+{ 0xBBC4, 0xBBC4, 0xBBC4 },
+{ 0xBBC5, 0xBBC5, 0xBBC5 },
+{ 0xBBC6, 0xBBC6, 0xBBC6 },
+{ 0xBBC7, 0xBBC7, 0xBBC7 },
+{ 0xBBC8, 0xBBC8, 0xBBC8 },
+{ 0xBBC9, 0xBBC9, 0xBBC9 },
+{ 0xBBCA, 0xBBCA, 0xBBCA },
+{ 0xBBCB, 0xBBCB, 0xBBCB },
+{ 0xBBCC, 0xBBCC, 0xBBCC },
+{ 0xBBCD, 0xBBCD, 0xBBCD },
+{ 0xBBCE, 0xBBCE, 0xBBCE },
+{ 0xBBCF, 0xBBCF, 0xBBCF },
+{ 0xBBD0, 0xBBD0, 0xBBD0 },
+{ 0xBBD1, 0xBBD1, 0xBBD1 },
+{ 0xBBD2, 0xBBD2, 0xBBD2 },
+{ 0xBBD3, 0xBBD3, 0xBBD3 },
+{ 0xBBD4, 0xBBD4, 0xBBD4 },
+{ 0xBBD5, 0xBBD5, 0xBBD5 },
+{ 0xBBD6, 0xBBD6, 0xBBD6 },
+{ 0xBBD7, 0xBBD7, 0xBBD7 },
+{ 0xBBD8, 0xBBD8, 0xBBD8 },
+{ 0xBBD9, 0xBBD9, 0xBBD9 },
+{ 0xBBDA, 0xBBDA, 0xBBDA },
+{ 0xBBDB, 0xBBDB, 0xBBDB },
+{ 0xBBDC, 0xBBDC, 0xBBDC },
+{ 0xBBDD, 0xBBDD, 0xBBDD },
+{ 0xBBDE, 0xBBDE, 0xBBDE },
+{ 0xBBDF, 0xBBDF, 0xBBDF },
+{ 0xBBE0, 0xBBE0, 0xBBE0 },
+{ 0xBBE1, 0xBBE1, 0xBBE1 },
+{ 0xBBE2, 0xBBE2, 0xBBE2 },
+{ 0xBBE3, 0xBBE3, 0xBBE3 },
+{ 0xBBE4, 0xBBE4, 0xBBE4 },
+{ 0xBBE5, 0xBBE5, 0xBBE5 },
+{ 0xBBE6, 0xBBE6, 0xBBE6 },
+{ 0xBBE7, 0xBBE7, 0xBBE7 },
+{ 0xBBE8, 0xBBE8, 0xBBE8 },
+{ 0xBBE9, 0xBBE9, 0xBBE9 },
+{ 0xBBEA, 0xBBEA, 0xBBEA },
+{ 0xBBEB, 0xBBEB, 0xBBEB },
+{ 0xBBEC, 0xBBEC, 0xBBEC },
+{ 0xBBED, 0xBBED, 0xBBED },
+{ 0xBBEE, 0xBBEE, 0xBBEE },
+{ 0xBBEF, 0xBBEF, 0xBBEF },
+{ 0xBBF0, 0xBBF0, 0xBBF0 },
+{ 0xBBF1, 0xBBF1, 0xBBF1 },
+{ 0xBBF2, 0xBBF2, 0xBBF2 },
+{ 0xBBF3, 0xBBF3, 0xBBF3 },
+{ 0xBBF4, 0xBBF4, 0xBBF4 },
+{ 0xBBF5, 0xBBF5, 0xBBF5 },
+{ 0xBBF6, 0xBBF6, 0xBBF6 },
+{ 0xBBF7, 0xBBF7, 0xBBF7 },
+{ 0xBBF8, 0xBBF8, 0xBBF8 },
+{ 0xBBF9, 0xBBF9, 0xBBF9 },
+{ 0xBBFA, 0xBBFA, 0xBBFA },
+{ 0xBBFB, 0xBBFB, 0xBBFB },
+{ 0xBBFC, 0xBBFC, 0xBBFC },
+{ 0xBBFD, 0xBBFD, 0xBBFD },
+{ 0xBBFE, 0xBBFE, 0xBBFE },
+{ 0xBBFF, 0xBBFF, 0xBBFF },
+{ 0xBC00, 0xBC00, 0xBC00 },
+{ 0xBC01, 0xBC01, 0xBC01 },
+{ 0xBC02, 0xBC02, 0xBC02 },
+{ 0xBC03, 0xBC03, 0xBC03 },
+{ 0xBC04, 0xBC04, 0xBC04 },
+{ 0xBC05, 0xBC05, 0xBC05 },
+{ 0xBC06, 0xBC06, 0xBC06 },
+{ 0xBC07, 0xBC07, 0xBC07 },
+{ 0xBC08, 0xBC08, 0xBC08 },
+{ 0xBC09, 0xBC09, 0xBC09 },
+{ 0xBC0A, 0xBC0A, 0xBC0A },
+{ 0xBC0B, 0xBC0B, 0xBC0B },
+{ 0xBC0C, 0xBC0C, 0xBC0C },
+{ 0xBC0D, 0xBC0D, 0xBC0D },
+{ 0xBC0E, 0xBC0E, 0xBC0E },
+{ 0xBC0F, 0xBC0F, 0xBC0F },
+{ 0xBC10, 0xBC10, 0xBC10 },
+{ 0xBC11, 0xBC11, 0xBC11 },
+{ 0xBC12, 0xBC12, 0xBC12 },
+{ 0xBC13, 0xBC13, 0xBC13 },
+{ 0xBC14, 0xBC14, 0xBC14 },
+{ 0xBC15, 0xBC15, 0xBC15 },
+{ 0xBC16, 0xBC16, 0xBC16 },
+{ 0xBC17, 0xBC17, 0xBC17 },
+{ 0xBC18, 0xBC18, 0xBC18 },
+{ 0xBC19, 0xBC19, 0xBC19 },
+{ 0xBC1A, 0xBC1A, 0xBC1A },
+{ 0xBC1B, 0xBC1B, 0xBC1B },
+{ 0xBC1C, 0xBC1C, 0xBC1C },
+{ 0xBC1D, 0xBC1D, 0xBC1D },
+{ 0xBC1E, 0xBC1E, 0xBC1E },
+{ 0xBC1F, 0xBC1F, 0xBC1F },
+{ 0xBC20, 0xBC20, 0xBC20 },
+{ 0xBC21, 0xBC21, 0xBC21 },
+{ 0xBC22, 0xBC22, 0xBC22 },
+{ 0xBC23, 0xBC23, 0xBC23 },
+{ 0xBC24, 0xBC24, 0xBC24 },
+{ 0xBC25, 0xBC25, 0xBC25 },
+{ 0xBC26, 0xBC26, 0xBC26 },
+{ 0xBC27, 0xBC27, 0xBC27 },
+{ 0xBC28, 0xBC28, 0xBC28 },
+{ 0xBC29, 0xBC29, 0xBC29 },
+{ 0xBC2A, 0xBC2A, 0xBC2A },
+{ 0xBC2B, 0xBC2B, 0xBC2B },
+{ 0xBC2C, 0xBC2C, 0xBC2C },
+{ 0xBC2D, 0xBC2D, 0xBC2D },
+{ 0xBC2E, 0xBC2E, 0xBC2E },
+{ 0xBC2F, 0xBC2F, 0xBC2F },
+{ 0xBC30, 0xBC30, 0xBC30 },
+{ 0xBC31, 0xBC31, 0xBC31 },
+{ 0xBC32, 0xBC32, 0xBC32 },
+{ 0xBC33, 0xBC33, 0xBC33 },
+{ 0xBC34, 0xBC34, 0xBC34 },
+{ 0xBC35, 0xBC35, 0xBC35 },
+{ 0xBC36, 0xBC36, 0xBC36 },
+{ 0xBC37, 0xBC37, 0xBC37 },
+{ 0xBC38, 0xBC38, 0xBC38 },
+{ 0xBC39, 0xBC39, 0xBC39 },
+{ 0xBC3A, 0xBC3A, 0xBC3A },
+{ 0xBC3B, 0xBC3B, 0xBC3B },
+{ 0xBC3C, 0xBC3C, 0xBC3C },
+{ 0xBC3D, 0xBC3D, 0xBC3D },
+{ 0xBC3E, 0xBC3E, 0xBC3E },
+{ 0xBC3F, 0xBC3F, 0xBC3F },
+{ 0xBC40, 0xBC40, 0xBC40 },
+{ 0xBC41, 0xBC41, 0xBC41 },
+{ 0xBC42, 0xBC42, 0xBC42 },
+{ 0xBC43, 0xBC43, 0xBC43 },
+{ 0xBC44, 0xBC44, 0xBC44 },
+{ 0xBC45, 0xBC45, 0xBC45 },
+{ 0xBC46, 0xBC46, 0xBC46 },
+{ 0xBC47, 0xBC47, 0xBC47 },
+{ 0xBC48, 0xBC48, 0xBC48 },
+{ 0xBC49, 0xBC49, 0xBC49 },
+{ 0xBC4A, 0xBC4A, 0xBC4A },
+{ 0xBC4B, 0xBC4B, 0xBC4B },
+{ 0xBC4C, 0xBC4C, 0xBC4C },
+{ 0xBC4D, 0xBC4D, 0xBC4D },
+{ 0xBC4E, 0xBC4E, 0xBC4E },
+{ 0xBC4F, 0xBC4F, 0xBC4F },
+{ 0xBC50, 0xBC50, 0xBC50 },
+{ 0xBC51, 0xBC51, 0xBC51 },
+{ 0xBC52, 0xBC52, 0xBC52 },
+{ 0xBC53, 0xBC53, 0xBC53 },
+{ 0xBC54, 0xBC54, 0xBC54 },
+{ 0xBC55, 0xBC55, 0xBC55 },
+{ 0xBC56, 0xBC56, 0xBC56 },
+{ 0xBC57, 0xBC57, 0xBC57 },
+{ 0xBC58, 0xBC58, 0xBC58 },
+{ 0xBC59, 0xBC59, 0xBC59 },
+{ 0xBC5A, 0xBC5A, 0xBC5A },
+{ 0xBC5B, 0xBC5B, 0xBC5B },
+{ 0xBC5C, 0xBC5C, 0xBC5C },
+{ 0xBC5D, 0xBC5D, 0xBC5D },
+{ 0xBC5E, 0xBC5E, 0xBC5E },
+{ 0xBC5F, 0xBC5F, 0xBC5F },
+{ 0xBC60, 0xBC60, 0xBC60 },
+{ 0xBC61, 0xBC61, 0xBC61 },
+{ 0xBC62, 0xBC62, 0xBC62 },
+{ 0xBC63, 0xBC63, 0xBC63 },
+{ 0xBC64, 0xBC64, 0xBC64 },
+{ 0xBC65, 0xBC65, 0xBC65 },
+{ 0xBC66, 0xBC66, 0xBC66 },
+{ 0xBC67, 0xBC67, 0xBC67 },
+{ 0xBC68, 0xBC68, 0xBC68 },
+{ 0xBC69, 0xBC69, 0xBC69 },
+{ 0xBC6A, 0xBC6A, 0xBC6A },
+{ 0xBC6B, 0xBC6B, 0xBC6B },
+{ 0xBC6C, 0xBC6C, 0xBC6C },
+{ 0xBC6D, 0xBC6D, 0xBC6D },
+{ 0xBC6E, 0xBC6E, 0xBC6E },
+{ 0xBC6F, 0xBC6F, 0xBC6F },
+{ 0xBC70, 0xBC70, 0xBC70 },
+{ 0xBC71, 0xBC71, 0xBC71 },
+{ 0xBC72, 0xBC72, 0xBC72 },
+{ 0xBC73, 0xBC73, 0xBC73 },
+{ 0xBC74, 0xBC74, 0xBC74 },
+{ 0xBC75, 0xBC75, 0xBC75 },
+{ 0xBC76, 0xBC76, 0xBC76 },
+{ 0xBC77, 0xBC77, 0xBC77 },
+{ 0xBC78, 0xBC78, 0xBC78 },
+{ 0xBC79, 0xBC79, 0xBC79 },
+{ 0xBC7A, 0xBC7A, 0xBC7A },
+{ 0xBC7B, 0xBC7B, 0xBC7B },
+{ 0xBC7C, 0xBC7C, 0xBC7C },
+{ 0xBC7D, 0xBC7D, 0xBC7D },
+{ 0xBC7E, 0xBC7E, 0xBC7E },
+{ 0xBC7F, 0xBC7F, 0xBC7F },
+{ 0xBC80, 0xBC80, 0xBC80 },
+{ 0xBC81, 0xBC81, 0xBC81 },
+{ 0xBC82, 0xBC82, 0xBC82 },
+{ 0xBC83, 0xBC83, 0xBC83 },
+{ 0xBC84, 0xBC84, 0xBC84 },
+{ 0xBC85, 0xBC85, 0xBC85 },
+{ 0xBC86, 0xBC86, 0xBC86 },
+{ 0xBC87, 0xBC87, 0xBC87 },
+{ 0xBC88, 0xBC88, 0xBC88 },
+{ 0xBC89, 0xBC89, 0xBC89 },
+{ 0xBC8A, 0xBC8A, 0xBC8A },
+{ 0xBC8B, 0xBC8B, 0xBC8B },
+{ 0xBC8C, 0xBC8C, 0xBC8C },
+{ 0xBC8D, 0xBC8D, 0xBC8D },
+{ 0xBC8E, 0xBC8E, 0xBC8E },
+{ 0xBC8F, 0xBC8F, 0xBC8F },
+{ 0xBC90, 0xBC90, 0xBC90 },
+{ 0xBC91, 0xBC91, 0xBC91 },
+{ 0xBC92, 0xBC92, 0xBC92 },
+{ 0xBC93, 0xBC93, 0xBC93 },
+{ 0xBC94, 0xBC94, 0xBC94 },
+{ 0xBC95, 0xBC95, 0xBC95 },
+{ 0xBC96, 0xBC96, 0xBC96 },
+{ 0xBC97, 0xBC97, 0xBC97 },
+{ 0xBC98, 0xBC98, 0xBC98 },
+{ 0xBC99, 0xBC99, 0xBC99 },
+{ 0xBC9A, 0xBC9A, 0xBC9A },
+{ 0xBC9B, 0xBC9B, 0xBC9B },
+{ 0xBC9C, 0xBC9C, 0xBC9C },
+{ 0xBC9D, 0xBC9D, 0xBC9D },
+{ 0xBC9E, 0xBC9E, 0xBC9E },
+{ 0xBC9F, 0xBC9F, 0xBC9F },
+{ 0xBCA0, 0xBCA0, 0xBCA0 },
+{ 0xBCA1, 0xBCA1, 0xBCA1 },
+{ 0xBCA2, 0xBCA2, 0xBCA2 },
+{ 0xBCA3, 0xBCA3, 0xBCA3 },
+{ 0xBCA4, 0xBCA4, 0xBCA4 },
+{ 0xBCA5, 0xBCA5, 0xBCA5 },
+{ 0xBCA6, 0xBCA6, 0xBCA6 },
+{ 0xBCA7, 0xBCA7, 0xBCA7 },
+{ 0xBCA8, 0xBCA8, 0xBCA8 },
+{ 0xBCA9, 0xBCA9, 0xBCA9 },
+{ 0xBCAA, 0xBCAA, 0xBCAA },
+{ 0xBCAB, 0xBCAB, 0xBCAB },
+{ 0xBCAC, 0xBCAC, 0xBCAC },
+{ 0xBCAD, 0xBCAD, 0xBCAD },
+{ 0xBCAE, 0xBCAE, 0xBCAE },
+{ 0xBCAF, 0xBCAF, 0xBCAF },
+{ 0xBCB0, 0xBCB0, 0xBCB0 },
+{ 0xBCB1, 0xBCB1, 0xBCB1 },
+{ 0xBCB2, 0xBCB2, 0xBCB2 },
+{ 0xBCB3, 0xBCB3, 0xBCB3 },
+{ 0xBCB4, 0xBCB4, 0xBCB4 },
+{ 0xBCB5, 0xBCB5, 0xBCB5 },
+{ 0xBCB6, 0xBCB6, 0xBCB6 },
+{ 0xBCB7, 0xBCB7, 0xBCB7 },
+{ 0xBCB8, 0xBCB8, 0xBCB8 },
+{ 0xBCB9, 0xBCB9, 0xBCB9 },
+{ 0xBCBA, 0xBCBA, 0xBCBA },
+{ 0xBCBB, 0xBCBB, 0xBCBB },
+{ 0xBCBC, 0xBCBC, 0xBCBC },
+{ 0xBCBD, 0xBCBD, 0xBCBD },
+{ 0xBCBE, 0xBCBE, 0xBCBE },
+{ 0xBCBF, 0xBCBF, 0xBCBF },
+{ 0xBCC0, 0xBCC0, 0xBCC0 },
+{ 0xBCC1, 0xBCC1, 0xBCC1 },
+{ 0xBCC2, 0xBCC2, 0xBCC2 },
+{ 0xBCC3, 0xBCC3, 0xBCC3 },
+{ 0xBCC4, 0xBCC4, 0xBCC4 },
+{ 0xBCC5, 0xBCC5, 0xBCC5 },
+{ 0xBCC6, 0xBCC6, 0xBCC6 },
+{ 0xBCC7, 0xBCC7, 0xBCC7 },
+{ 0xBCC8, 0xBCC8, 0xBCC8 },
+{ 0xBCC9, 0xBCC9, 0xBCC9 },
+{ 0xBCCA, 0xBCCA, 0xBCCA },
+{ 0xBCCB, 0xBCCB, 0xBCCB },
+{ 0xBCCC, 0xBCCC, 0xBCCC },
+{ 0xBCCD, 0xBCCD, 0xBCCD },
+{ 0xBCCE, 0xBCCE, 0xBCCE },
+{ 0xBCCF, 0xBCCF, 0xBCCF },
+{ 0xBCD0, 0xBCD0, 0xBCD0 },
+{ 0xBCD1, 0xBCD1, 0xBCD1 },
+{ 0xBCD2, 0xBCD2, 0xBCD2 },
+{ 0xBCD3, 0xBCD3, 0xBCD3 },
+{ 0xBCD4, 0xBCD4, 0xBCD4 },
+{ 0xBCD5, 0xBCD5, 0xBCD5 },
+{ 0xBCD6, 0xBCD6, 0xBCD6 },
+{ 0xBCD7, 0xBCD7, 0xBCD7 },
+{ 0xBCD8, 0xBCD8, 0xBCD8 },
+{ 0xBCD9, 0xBCD9, 0xBCD9 },
+{ 0xBCDA, 0xBCDA, 0xBCDA },
+{ 0xBCDB, 0xBCDB, 0xBCDB },
+{ 0xBCDC, 0xBCDC, 0xBCDC },
+{ 0xBCDD, 0xBCDD, 0xBCDD },
+{ 0xBCDE, 0xBCDE, 0xBCDE },
+{ 0xBCDF, 0xBCDF, 0xBCDF },
+{ 0xBCE0, 0xBCE0, 0xBCE0 },
+{ 0xBCE1, 0xBCE1, 0xBCE1 },
+{ 0xBCE2, 0xBCE2, 0xBCE2 },
+{ 0xBCE3, 0xBCE3, 0xBCE3 },
+{ 0xBCE4, 0xBCE4, 0xBCE4 },
+{ 0xBCE5, 0xBCE5, 0xBCE5 },
+{ 0xBCE6, 0xBCE6, 0xBCE6 },
+{ 0xBCE7, 0xBCE7, 0xBCE7 },
+{ 0xBCE8, 0xBCE8, 0xBCE8 },
+{ 0xBCE9, 0xBCE9, 0xBCE9 },
+{ 0xBCEA, 0xBCEA, 0xBCEA },
+{ 0xBCEB, 0xBCEB, 0xBCEB },
+{ 0xBCEC, 0xBCEC, 0xBCEC },
+{ 0xBCED, 0xBCED, 0xBCED },
+{ 0xBCEE, 0xBCEE, 0xBCEE },
+{ 0xBCEF, 0xBCEF, 0xBCEF },
+{ 0xBCF0, 0xBCF0, 0xBCF0 },
+{ 0xBCF1, 0xBCF1, 0xBCF1 },
+{ 0xBCF2, 0xBCF2, 0xBCF2 },
+{ 0xBCF3, 0xBCF3, 0xBCF3 },
+{ 0xBCF4, 0xBCF4, 0xBCF4 },
+{ 0xBCF5, 0xBCF5, 0xBCF5 },
+{ 0xBCF6, 0xBCF6, 0xBCF6 },
+{ 0xBCF7, 0xBCF7, 0xBCF7 },
+{ 0xBCF8, 0xBCF8, 0xBCF8 },
+{ 0xBCF9, 0xBCF9, 0xBCF9 },
+{ 0xBCFA, 0xBCFA, 0xBCFA },
+{ 0xBCFB, 0xBCFB, 0xBCFB },
+{ 0xBCFC, 0xBCFC, 0xBCFC },
+{ 0xBCFD, 0xBCFD, 0xBCFD },
+{ 0xBCFE, 0xBCFE, 0xBCFE },
+{ 0xBCFF, 0xBCFF, 0xBCFF },
+{ 0xBD00, 0xBD00, 0xBD00 },
+{ 0xBD01, 0xBD01, 0xBD01 },
+{ 0xBD02, 0xBD02, 0xBD02 },
+{ 0xBD03, 0xBD03, 0xBD03 },
+{ 0xBD04, 0xBD04, 0xBD04 },
+{ 0xBD05, 0xBD05, 0xBD05 },
+{ 0xBD06, 0xBD06, 0xBD06 },
+{ 0xBD07, 0xBD07, 0xBD07 },
+{ 0xBD08, 0xBD08, 0xBD08 },
+{ 0xBD09, 0xBD09, 0xBD09 },
+{ 0xBD0A, 0xBD0A, 0xBD0A },
+{ 0xBD0B, 0xBD0B, 0xBD0B },
+{ 0xBD0C, 0xBD0C, 0xBD0C },
+{ 0xBD0D, 0xBD0D, 0xBD0D },
+{ 0xBD0E, 0xBD0E, 0xBD0E },
+{ 0xBD0F, 0xBD0F, 0xBD0F },
+{ 0xBD10, 0xBD10, 0xBD10 },
+{ 0xBD11, 0xBD11, 0xBD11 },
+{ 0xBD12, 0xBD12, 0xBD12 },
+{ 0xBD13, 0xBD13, 0xBD13 },
+{ 0xBD14, 0xBD14, 0xBD14 },
+{ 0xBD15, 0xBD15, 0xBD15 },
+{ 0xBD16, 0xBD16, 0xBD16 },
+{ 0xBD17, 0xBD17, 0xBD17 },
+{ 0xBD18, 0xBD18, 0xBD18 },
+{ 0xBD19, 0xBD19, 0xBD19 },
+{ 0xBD1A, 0xBD1A, 0xBD1A },
+{ 0xBD1B, 0xBD1B, 0xBD1B },
+{ 0xBD1C, 0xBD1C, 0xBD1C },
+{ 0xBD1D, 0xBD1D, 0xBD1D },
+{ 0xBD1E, 0xBD1E, 0xBD1E },
+{ 0xBD1F, 0xBD1F, 0xBD1F },
+{ 0xBD20, 0xBD20, 0xBD20 },
+{ 0xBD21, 0xBD21, 0xBD21 },
+{ 0xBD22, 0xBD22, 0xBD22 },
+{ 0xBD23, 0xBD23, 0xBD23 },
+{ 0xBD24, 0xBD24, 0xBD24 },
+{ 0xBD25, 0xBD25, 0xBD25 },
+{ 0xBD26, 0xBD26, 0xBD26 },
+{ 0xBD27, 0xBD27, 0xBD27 },
+{ 0xBD28, 0xBD28, 0xBD28 },
+{ 0xBD29, 0xBD29, 0xBD29 },
+{ 0xBD2A, 0xBD2A, 0xBD2A },
+{ 0xBD2B, 0xBD2B, 0xBD2B },
+{ 0xBD2C, 0xBD2C, 0xBD2C },
+{ 0xBD2D, 0xBD2D, 0xBD2D },
+{ 0xBD2E, 0xBD2E, 0xBD2E },
+{ 0xBD2F, 0xBD2F, 0xBD2F },
+{ 0xBD30, 0xBD30, 0xBD30 },
+{ 0xBD31, 0xBD31, 0xBD31 },
+{ 0xBD32, 0xBD32, 0xBD32 },
+{ 0xBD33, 0xBD33, 0xBD33 },
+{ 0xBD34, 0xBD34, 0xBD34 },
+{ 0xBD35, 0xBD35, 0xBD35 },
+{ 0xBD36, 0xBD36, 0xBD36 },
+{ 0xBD37, 0xBD37, 0xBD37 },
+{ 0xBD38, 0xBD38, 0xBD38 },
+{ 0xBD39, 0xBD39, 0xBD39 },
+{ 0xBD3A, 0xBD3A, 0xBD3A },
+{ 0xBD3B, 0xBD3B, 0xBD3B },
+{ 0xBD3C, 0xBD3C, 0xBD3C },
+{ 0xBD3D, 0xBD3D, 0xBD3D },
+{ 0xBD3E, 0xBD3E, 0xBD3E },
+{ 0xBD3F, 0xBD3F, 0xBD3F },
+{ 0xBD40, 0xBD40, 0xBD40 },
+{ 0xBD41, 0xBD41, 0xBD41 },
+{ 0xBD42, 0xBD42, 0xBD42 },
+{ 0xBD43, 0xBD43, 0xBD43 },
+{ 0xBD44, 0xBD44, 0xBD44 },
+{ 0xBD45, 0xBD45, 0xBD45 },
+{ 0xBD46, 0xBD46, 0xBD46 },
+{ 0xBD47, 0xBD47, 0xBD47 },
+{ 0xBD48, 0xBD48, 0xBD48 },
+{ 0xBD49, 0xBD49, 0xBD49 },
+{ 0xBD4A, 0xBD4A, 0xBD4A },
+{ 0xBD4B, 0xBD4B, 0xBD4B },
+{ 0xBD4C, 0xBD4C, 0xBD4C },
+{ 0xBD4D, 0xBD4D, 0xBD4D },
+{ 0xBD4E, 0xBD4E, 0xBD4E },
+{ 0xBD4F, 0xBD4F, 0xBD4F },
+{ 0xBD50, 0xBD50, 0xBD50 },
+{ 0xBD51, 0xBD51, 0xBD51 },
+{ 0xBD52, 0xBD52, 0xBD52 },
+{ 0xBD53, 0xBD53, 0xBD53 },
+{ 0xBD54, 0xBD54, 0xBD54 },
+{ 0xBD55, 0xBD55, 0xBD55 },
+{ 0xBD56, 0xBD56, 0xBD56 },
+{ 0xBD57, 0xBD57, 0xBD57 },
+{ 0xBD58, 0xBD58, 0xBD58 },
+{ 0xBD59, 0xBD59, 0xBD59 },
+{ 0xBD5A, 0xBD5A, 0xBD5A },
+{ 0xBD5B, 0xBD5B, 0xBD5B },
+{ 0xBD5C, 0xBD5C, 0xBD5C },
+{ 0xBD5D, 0xBD5D, 0xBD5D },
+{ 0xBD5E, 0xBD5E, 0xBD5E },
+{ 0xBD5F, 0xBD5F, 0xBD5F },
+{ 0xBD60, 0xBD60, 0xBD60 },
+{ 0xBD61, 0xBD61, 0xBD61 },
+{ 0xBD62, 0xBD62, 0xBD62 },
+{ 0xBD63, 0xBD63, 0xBD63 },
+{ 0xBD64, 0xBD64, 0xBD64 },
+{ 0xBD65, 0xBD65, 0xBD65 },
+{ 0xBD66, 0xBD66, 0xBD66 },
+{ 0xBD67, 0xBD67, 0xBD67 },
+{ 0xBD68, 0xBD68, 0xBD68 },
+{ 0xBD69, 0xBD69, 0xBD69 },
+{ 0xBD6A, 0xBD6A, 0xBD6A },
+{ 0xBD6B, 0xBD6B, 0xBD6B },
+{ 0xBD6C, 0xBD6C, 0xBD6C },
+{ 0xBD6D, 0xBD6D, 0xBD6D },
+{ 0xBD6E, 0xBD6E, 0xBD6E },
+{ 0xBD6F, 0xBD6F, 0xBD6F },
+{ 0xBD70, 0xBD70, 0xBD70 },
+{ 0xBD71, 0xBD71, 0xBD71 },
+{ 0xBD72, 0xBD72, 0xBD72 },
+{ 0xBD73, 0xBD73, 0xBD73 },
+{ 0xBD74, 0xBD74, 0xBD74 },
+{ 0xBD75, 0xBD75, 0xBD75 },
+{ 0xBD76, 0xBD76, 0xBD76 },
+{ 0xBD77, 0xBD77, 0xBD77 },
+{ 0xBD78, 0xBD78, 0xBD78 },
+{ 0xBD79, 0xBD79, 0xBD79 },
+{ 0xBD7A, 0xBD7A, 0xBD7A },
+{ 0xBD7B, 0xBD7B, 0xBD7B },
+{ 0xBD7C, 0xBD7C, 0xBD7C },
+{ 0xBD7D, 0xBD7D, 0xBD7D },
+{ 0xBD7E, 0xBD7E, 0xBD7E },
+{ 0xBD7F, 0xBD7F, 0xBD7F },
+{ 0xBD80, 0xBD80, 0xBD80 },
+{ 0xBD81, 0xBD81, 0xBD81 },
+{ 0xBD82, 0xBD82, 0xBD82 },
+{ 0xBD83, 0xBD83, 0xBD83 },
+{ 0xBD84, 0xBD84, 0xBD84 },
+{ 0xBD85, 0xBD85, 0xBD85 },
+{ 0xBD86, 0xBD86, 0xBD86 },
+{ 0xBD87, 0xBD87, 0xBD87 },
+{ 0xBD88, 0xBD88, 0xBD88 },
+{ 0xBD89, 0xBD89, 0xBD89 },
+{ 0xBD8A, 0xBD8A, 0xBD8A },
+{ 0xBD8B, 0xBD8B, 0xBD8B },
+{ 0xBD8C, 0xBD8C, 0xBD8C },
+{ 0xBD8D, 0xBD8D, 0xBD8D },
+{ 0xBD8E, 0xBD8E, 0xBD8E },
+{ 0xBD8F, 0xBD8F, 0xBD8F },
+{ 0xBD90, 0xBD90, 0xBD90 },
+{ 0xBD91, 0xBD91, 0xBD91 },
+{ 0xBD92, 0xBD92, 0xBD92 },
+{ 0xBD93, 0xBD93, 0xBD93 },
+{ 0xBD94, 0xBD94, 0xBD94 },
+{ 0xBD95, 0xBD95, 0xBD95 },
+{ 0xBD96, 0xBD96, 0xBD96 },
+{ 0xBD97, 0xBD97, 0xBD97 },
+{ 0xBD98, 0xBD98, 0xBD98 },
+{ 0xBD99, 0xBD99, 0xBD99 },
+{ 0xBD9A, 0xBD9A, 0xBD9A },
+{ 0xBD9B, 0xBD9B, 0xBD9B },
+{ 0xBD9C, 0xBD9C, 0xBD9C },
+{ 0xBD9D, 0xBD9D, 0xBD9D },
+{ 0xBD9E, 0xBD9E, 0xBD9E },
+{ 0xBD9F, 0xBD9F, 0xBD9F },
+{ 0xBDA0, 0xBDA0, 0xBDA0 },
+{ 0xBDA1, 0xBDA1, 0xBDA1 },
+{ 0xBDA2, 0xBDA2, 0xBDA2 },
+{ 0xBDA3, 0xBDA3, 0xBDA3 },
+{ 0xBDA4, 0xBDA4, 0xBDA4 },
+{ 0xBDA5, 0xBDA5, 0xBDA5 },
+{ 0xBDA6, 0xBDA6, 0xBDA6 },
+{ 0xBDA7, 0xBDA7, 0xBDA7 },
+{ 0xBDA8, 0xBDA8, 0xBDA8 },
+{ 0xBDA9, 0xBDA9, 0xBDA9 },
+{ 0xBDAA, 0xBDAA, 0xBDAA },
+{ 0xBDAB, 0xBDAB, 0xBDAB },
+{ 0xBDAC, 0xBDAC, 0xBDAC },
+{ 0xBDAD, 0xBDAD, 0xBDAD },
+{ 0xBDAE, 0xBDAE, 0xBDAE },
+{ 0xBDAF, 0xBDAF, 0xBDAF },
+{ 0xBDB0, 0xBDB0, 0xBDB0 },
+{ 0xBDB1, 0xBDB1, 0xBDB1 },
+{ 0xBDB2, 0xBDB2, 0xBDB2 },
+{ 0xBDB3, 0xBDB3, 0xBDB3 },
+{ 0xBDB4, 0xBDB4, 0xBDB4 },
+{ 0xBDB5, 0xBDB5, 0xBDB5 },
+{ 0xBDB6, 0xBDB6, 0xBDB6 },
+{ 0xBDB7, 0xBDB7, 0xBDB7 },
+{ 0xBDB8, 0xBDB8, 0xBDB8 },
+{ 0xBDB9, 0xBDB9, 0xBDB9 },
+{ 0xBDBA, 0xBDBA, 0xBDBA },
+{ 0xBDBB, 0xBDBB, 0xBDBB },
+{ 0xBDBC, 0xBDBC, 0xBDBC },
+{ 0xBDBD, 0xBDBD, 0xBDBD },
+{ 0xBDBE, 0xBDBE, 0xBDBE },
+{ 0xBDBF, 0xBDBF, 0xBDBF },
+{ 0xBDC0, 0xBDC0, 0xBDC0 },
+{ 0xBDC1, 0xBDC1, 0xBDC1 },
+{ 0xBDC2, 0xBDC2, 0xBDC2 },
+{ 0xBDC3, 0xBDC3, 0xBDC3 },
+{ 0xBDC4, 0xBDC4, 0xBDC4 },
+{ 0xBDC5, 0xBDC5, 0xBDC5 },
+{ 0xBDC6, 0xBDC6, 0xBDC6 },
+{ 0xBDC7, 0xBDC7, 0xBDC7 },
+{ 0xBDC8, 0xBDC8, 0xBDC8 },
+{ 0xBDC9, 0xBDC9, 0xBDC9 },
+{ 0xBDCA, 0xBDCA, 0xBDCA },
+{ 0xBDCB, 0xBDCB, 0xBDCB },
+{ 0xBDCC, 0xBDCC, 0xBDCC },
+{ 0xBDCD, 0xBDCD, 0xBDCD },
+{ 0xBDCE, 0xBDCE, 0xBDCE },
+{ 0xBDCF, 0xBDCF, 0xBDCF },
+{ 0xBDD0, 0xBDD0, 0xBDD0 },
+{ 0xBDD1, 0xBDD1, 0xBDD1 },
+{ 0xBDD2, 0xBDD2, 0xBDD2 },
+{ 0xBDD3, 0xBDD3, 0xBDD3 },
+{ 0xBDD4, 0xBDD4, 0xBDD4 },
+{ 0xBDD5, 0xBDD5, 0xBDD5 },
+{ 0xBDD6, 0xBDD6, 0xBDD6 },
+{ 0xBDD7, 0xBDD7, 0xBDD7 },
+{ 0xBDD8, 0xBDD8, 0xBDD8 },
+{ 0xBDD9, 0xBDD9, 0xBDD9 },
+{ 0xBDDA, 0xBDDA, 0xBDDA },
+{ 0xBDDB, 0xBDDB, 0xBDDB },
+{ 0xBDDC, 0xBDDC, 0xBDDC },
+{ 0xBDDD, 0xBDDD, 0xBDDD },
+{ 0xBDDE, 0xBDDE, 0xBDDE },
+{ 0xBDDF, 0xBDDF, 0xBDDF },
+{ 0xBDE0, 0xBDE0, 0xBDE0 },
+{ 0xBDE1, 0xBDE1, 0xBDE1 },
+{ 0xBDE2, 0xBDE2, 0xBDE2 },
+{ 0xBDE3, 0xBDE3, 0xBDE3 },
+{ 0xBDE4, 0xBDE4, 0xBDE4 },
+{ 0xBDE5, 0xBDE5, 0xBDE5 },
+{ 0xBDE6, 0xBDE6, 0xBDE6 },
+{ 0xBDE7, 0xBDE7, 0xBDE7 },
+{ 0xBDE8, 0xBDE8, 0xBDE8 },
+{ 0xBDE9, 0xBDE9, 0xBDE9 },
+{ 0xBDEA, 0xBDEA, 0xBDEA },
+{ 0xBDEB, 0xBDEB, 0xBDEB },
+{ 0xBDEC, 0xBDEC, 0xBDEC },
+{ 0xBDED, 0xBDED, 0xBDED },
+{ 0xBDEE, 0xBDEE, 0xBDEE },
+{ 0xBDEF, 0xBDEF, 0xBDEF },
+{ 0xBDF0, 0xBDF0, 0xBDF0 },
+{ 0xBDF1, 0xBDF1, 0xBDF1 },
+{ 0xBDF2, 0xBDF2, 0xBDF2 },
+{ 0xBDF3, 0xBDF3, 0xBDF3 },
+{ 0xBDF4, 0xBDF4, 0xBDF4 },
+{ 0xBDF5, 0xBDF5, 0xBDF5 },
+{ 0xBDF6, 0xBDF6, 0xBDF6 },
+{ 0xBDF7, 0xBDF7, 0xBDF7 },
+{ 0xBDF8, 0xBDF8, 0xBDF8 },
+{ 0xBDF9, 0xBDF9, 0xBDF9 },
+{ 0xBDFA, 0xBDFA, 0xBDFA },
+{ 0xBDFB, 0xBDFB, 0xBDFB },
+{ 0xBDFC, 0xBDFC, 0xBDFC },
+{ 0xBDFD, 0xBDFD, 0xBDFD },
+{ 0xBDFE, 0xBDFE, 0xBDFE },
+{ 0xBDFF, 0xBDFF, 0xBDFF },
+{ 0xBE00, 0xBE00, 0xBE00 },
+{ 0xBE01, 0xBE01, 0xBE01 },
+{ 0xBE02, 0xBE02, 0xBE02 },
+{ 0xBE03, 0xBE03, 0xBE03 },
+{ 0xBE04, 0xBE04, 0xBE04 },
+{ 0xBE05, 0xBE05, 0xBE05 },
+{ 0xBE06, 0xBE06, 0xBE06 },
+{ 0xBE07, 0xBE07, 0xBE07 },
+{ 0xBE08, 0xBE08, 0xBE08 },
+{ 0xBE09, 0xBE09, 0xBE09 },
+{ 0xBE0A, 0xBE0A, 0xBE0A },
+{ 0xBE0B, 0xBE0B, 0xBE0B },
+{ 0xBE0C, 0xBE0C, 0xBE0C },
+{ 0xBE0D, 0xBE0D, 0xBE0D },
+{ 0xBE0E, 0xBE0E, 0xBE0E },
+{ 0xBE0F, 0xBE0F, 0xBE0F },
+{ 0xBE10, 0xBE10, 0xBE10 },
+{ 0xBE11, 0xBE11, 0xBE11 },
+{ 0xBE12, 0xBE12, 0xBE12 },
+{ 0xBE13, 0xBE13, 0xBE13 },
+{ 0xBE14, 0xBE14, 0xBE14 },
+{ 0xBE15, 0xBE15, 0xBE15 },
+{ 0xBE16, 0xBE16, 0xBE16 },
+{ 0xBE17, 0xBE17, 0xBE17 },
+{ 0xBE18, 0xBE18, 0xBE18 },
+{ 0xBE19, 0xBE19, 0xBE19 },
+{ 0xBE1A, 0xBE1A, 0xBE1A },
+{ 0xBE1B, 0xBE1B, 0xBE1B },
+{ 0xBE1C, 0xBE1C, 0xBE1C },
+{ 0xBE1D, 0xBE1D, 0xBE1D },
+{ 0xBE1E, 0xBE1E, 0xBE1E },
+{ 0xBE1F, 0xBE1F, 0xBE1F },
+{ 0xBE20, 0xBE20, 0xBE20 },
+{ 0xBE21, 0xBE21, 0xBE21 },
+{ 0xBE22, 0xBE22, 0xBE22 },
+{ 0xBE23, 0xBE23, 0xBE23 },
+{ 0xBE24, 0xBE24, 0xBE24 },
+{ 0xBE25, 0xBE25, 0xBE25 },
+{ 0xBE26, 0xBE26, 0xBE26 },
+{ 0xBE27, 0xBE27, 0xBE27 },
+{ 0xBE28, 0xBE28, 0xBE28 },
+{ 0xBE29, 0xBE29, 0xBE29 },
+{ 0xBE2A, 0xBE2A, 0xBE2A },
+{ 0xBE2B, 0xBE2B, 0xBE2B },
+{ 0xBE2C, 0xBE2C, 0xBE2C },
+{ 0xBE2D, 0xBE2D, 0xBE2D },
+{ 0xBE2E, 0xBE2E, 0xBE2E },
+{ 0xBE2F, 0xBE2F, 0xBE2F },
+{ 0xBE30, 0xBE30, 0xBE30 },
+{ 0xBE31, 0xBE31, 0xBE31 },
+{ 0xBE32, 0xBE32, 0xBE32 },
+{ 0xBE33, 0xBE33, 0xBE33 },
+{ 0xBE34, 0xBE34, 0xBE34 },
+{ 0xBE35, 0xBE35, 0xBE35 },
+{ 0xBE36, 0xBE36, 0xBE36 },
+{ 0xBE37, 0xBE37, 0xBE37 },
+{ 0xBE38, 0xBE38, 0xBE38 },
+{ 0xBE39, 0xBE39, 0xBE39 },
+{ 0xBE3A, 0xBE3A, 0xBE3A },
+{ 0xBE3B, 0xBE3B, 0xBE3B },
+{ 0xBE3C, 0xBE3C, 0xBE3C },
+{ 0xBE3D, 0xBE3D, 0xBE3D },
+{ 0xBE3E, 0xBE3E, 0xBE3E },
+{ 0xBE3F, 0xBE3F, 0xBE3F },
+{ 0xBE40, 0xBE40, 0xBE40 },
+{ 0xBE41, 0xBE41, 0xBE41 },
+{ 0xBE42, 0xBE42, 0xBE42 },
+{ 0xBE43, 0xBE43, 0xBE43 },
+{ 0xBE44, 0xBE44, 0xBE44 },
+{ 0xBE45, 0xBE45, 0xBE45 },
+{ 0xBE46, 0xBE46, 0xBE46 },
+{ 0xBE47, 0xBE47, 0xBE47 },
+{ 0xBE48, 0xBE48, 0xBE48 },
+{ 0xBE49, 0xBE49, 0xBE49 },
+{ 0xBE4A, 0xBE4A, 0xBE4A },
+{ 0xBE4B, 0xBE4B, 0xBE4B },
+{ 0xBE4C, 0xBE4C, 0xBE4C },
+{ 0xBE4D, 0xBE4D, 0xBE4D },
+{ 0xBE4E, 0xBE4E, 0xBE4E },
+{ 0xBE4F, 0xBE4F, 0xBE4F },
+{ 0xBE50, 0xBE50, 0xBE50 },
+{ 0xBE51, 0xBE51, 0xBE51 },
+{ 0xBE52, 0xBE52, 0xBE52 },
+{ 0xBE53, 0xBE53, 0xBE53 },
+{ 0xBE54, 0xBE54, 0xBE54 },
+{ 0xBE55, 0xBE55, 0xBE55 },
+{ 0xBE56, 0xBE56, 0xBE56 },
+{ 0xBE57, 0xBE57, 0xBE57 },
+{ 0xBE58, 0xBE58, 0xBE58 },
+{ 0xBE59, 0xBE59, 0xBE59 },
+{ 0xBE5A, 0xBE5A, 0xBE5A },
+{ 0xBE5B, 0xBE5B, 0xBE5B },
+{ 0xBE5C, 0xBE5C, 0xBE5C },
+{ 0xBE5D, 0xBE5D, 0xBE5D },
+{ 0xBE5E, 0xBE5E, 0xBE5E },
+{ 0xBE5F, 0xBE5F, 0xBE5F },
+{ 0xBE60, 0xBE60, 0xBE60 },
+{ 0xBE61, 0xBE61, 0xBE61 },
+{ 0xBE62, 0xBE62, 0xBE62 },
+{ 0xBE63, 0xBE63, 0xBE63 },
+{ 0xBE64, 0xBE64, 0xBE64 },
+{ 0xBE65, 0xBE65, 0xBE65 },
+{ 0xBE66, 0xBE66, 0xBE66 },
+{ 0xBE67, 0xBE67, 0xBE67 },
+{ 0xBE68, 0xBE68, 0xBE68 },
+{ 0xBE69, 0xBE69, 0xBE69 },
+{ 0xBE6A, 0xBE6A, 0xBE6A },
+{ 0xBE6B, 0xBE6B, 0xBE6B },
+{ 0xBE6C, 0xBE6C, 0xBE6C },
+{ 0xBE6D, 0xBE6D, 0xBE6D },
+{ 0xBE6E, 0xBE6E, 0xBE6E },
+{ 0xBE6F, 0xBE6F, 0xBE6F },
+{ 0xBE70, 0xBE70, 0xBE70 },
+{ 0xBE71, 0xBE71, 0xBE71 },
+{ 0xBE72, 0xBE72, 0xBE72 },
+{ 0xBE73, 0xBE73, 0xBE73 },
+{ 0xBE74, 0xBE74, 0xBE74 },
+{ 0xBE75, 0xBE75, 0xBE75 },
+{ 0xBE76, 0xBE76, 0xBE76 },
+{ 0xBE77, 0xBE77, 0xBE77 },
+{ 0xBE78, 0xBE78, 0xBE78 },
+{ 0xBE79, 0xBE79, 0xBE79 },
+{ 0xBE7A, 0xBE7A, 0xBE7A },
+{ 0xBE7B, 0xBE7B, 0xBE7B },
+{ 0xBE7C, 0xBE7C, 0xBE7C },
+{ 0xBE7D, 0xBE7D, 0xBE7D },
+{ 0xBE7E, 0xBE7E, 0xBE7E },
+{ 0xBE7F, 0xBE7F, 0xBE7F },
+{ 0xBE80, 0xBE80, 0xBE80 },
+{ 0xBE81, 0xBE81, 0xBE81 },
+{ 0xBE82, 0xBE82, 0xBE82 },
+{ 0xBE83, 0xBE83, 0xBE83 },
+{ 0xBE84, 0xBE84, 0xBE84 },
+{ 0xBE85, 0xBE85, 0xBE85 },
+{ 0xBE86, 0xBE86, 0xBE86 },
+{ 0xBE87, 0xBE87, 0xBE87 },
+{ 0xBE88, 0xBE88, 0xBE88 },
+{ 0xBE89, 0xBE89, 0xBE89 },
+{ 0xBE8A, 0xBE8A, 0xBE8A },
+{ 0xBE8B, 0xBE8B, 0xBE8B },
+{ 0xBE8C, 0xBE8C, 0xBE8C },
+{ 0xBE8D, 0xBE8D, 0xBE8D },
+{ 0xBE8E, 0xBE8E, 0xBE8E },
+{ 0xBE8F, 0xBE8F, 0xBE8F },
+{ 0xBE90, 0xBE90, 0xBE90 },
+{ 0xBE91, 0xBE91, 0xBE91 },
+{ 0xBE92, 0xBE92, 0xBE92 },
+{ 0xBE93, 0xBE93, 0xBE93 },
+{ 0xBE94, 0xBE94, 0xBE94 },
+{ 0xBE95, 0xBE95, 0xBE95 },
+{ 0xBE96, 0xBE96, 0xBE96 },
+{ 0xBE97, 0xBE97, 0xBE97 },
+{ 0xBE98, 0xBE98, 0xBE98 },
+{ 0xBE99, 0xBE99, 0xBE99 },
+{ 0xBE9A, 0xBE9A, 0xBE9A },
+{ 0xBE9B, 0xBE9B, 0xBE9B },
+{ 0xBE9C, 0xBE9C, 0xBE9C },
+{ 0xBE9D, 0xBE9D, 0xBE9D },
+{ 0xBE9E, 0xBE9E, 0xBE9E },
+{ 0xBE9F, 0xBE9F, 0xBE9F },
+{ 0xBEA0, 0xBEA0, 0xBEA0 },
+{ 0xBEA1, 0xBEA1, 0xBEA1 },
+{ 0xBEA2, 0xBEA2, 0xBEA2 },
+{ 0xBEA3, 0xBEA3, 0xBEA3 },
+{ 0xBEA4, 0xBEA4, 0xBEA4 },
+{ 0xBEA5, 0xBEA5, 0xBEA5 },
+{ 0xBEA6, 0xBEA6, 0xBEA6 },
+{ 0xBEA7, 0xBEA7, 0xBEA7 },
+{ 0xBEA8, 0xBEA8, 0xBEA8 },
+{ 0xBEA9, 0xBEA9, 0xBEA9 },
+{ 0xBEAA, 0xBEAA, 0xBEAA },
+{ 0xBEAB, 0xBEAB, 0xBEAB },
+{ 0xBEAC, 0xBEAC, 0xBEAC },
+{ 0xBEAD, 0xBEAD, 0xBEAD },
+{ 0xBEAE, 0xBEAE, 0xBEAE },
+{ 0xBEAF, 0xBEAF, 0xBEAF },
+{ 0xBEB0, 0xBEB0, 0xBEB0 },
+{ 0xBEB1, 0xBEB1, 0xBEB1 },
+{ 0xBEB2, 0xBEB2, 0xBEB2 },
+{ 0xBEB3, 0xBEB3, 0xBEB3 },
+{ 0xBEB4, 0xBEB4, 0xBEB4 },
+{ 0xBEB5, 0xBEB5, 0xBEB5 },
+{ 0xBEB6, 0xBEB6, 0xBEB6 },
+{ 0xBEB7, 0xBEB7, 0xBEB7 },
+{ 0xBEB8, 0xBEB8, 0xBEB8 },
+{ 0xBEB9, 0xBEB9, 0xBEB9 },
+{ 0xBEBA, 0xBEBA, 0xBEBA },
+{ 0xBEBB, 0xBEBB, 0xBEBB },
+{ 0xBEBC, 0xBEBC, 0xBEBC },
+{ 0xBEBD, 0xBEBD, 0xBEBD },
+{ 0xBEBE, 0xBEBE, 0xBEBE },
+{ 0xBEBF, 0xBEBF, 0xBEBF },
+{ 0xBEC0, 0xBEC0, 0xBEC0 },
+{ 0xBEC1, 0xBEC1, 0xBEC1 },
+{ 0xBEC2, 0xBEC2, 0xBEC2 },
+{ 0xBEC3, 0xBEC3, 0xBEC3 },
+{ 0xBEC4, 0xBEC4, 0xBEC4 },
+{ 0xBEC5, 0xBEC5, 0xBEC5 },
+{ 0xBEC6, 0xBEC6, 0xBEC6 },
+{ 0xBEC7, 0xBEC7, 0xBEC7 },
+{ 0xBEC8, 0xBEC8, 0xBEC8 },
+{ 0xBEC9, 0xBEC9, 0xBEC9 },
+{ 0xBECA, 0xBECA, 0xBECA },
+{ 0xBECB, 0xBECB, 0xBECB },
+{ 0xBECC, 0xBECC, 0xBECC },
+{ 0xBECD, 0xBECD, 0xBECD },
+{ 0xBECE, 0xBECE, 0xBECE },
+{ 0xBECF, 0xBECF, 0xBECF },
+{ 0xBED0, 0xBED0, 0xBED0 },
+{ 0xBED1, 0xBED1, 0xBED1 },
+{ 0xBED2, 0xBED2, 0xBED2 },
+{ 0xBED3, 0xBED3, 0xBED3 },
+{ 0xBED4, 0xBED4, 0xBED4 },
+{ 0xBED5, 0xBED5, 0xBED5 },
+{ 0xBED6, 0xBED6, 0xBED6 },
+{ 0xBED7, 0xBED7, 0xBED7 },
+{ 0xBED8, 0xBED8, 0xBED8 },
+{ 0xBED9, 0xBED9, 0xBED9 },
+{ 0xBEDA, 0xBEDA, 0xBEDA },
+{ 0xBEDB, 0xBEDB, 0xBEDB },
+{ 0xBEDC, 0xBEDC, 0xBEDC },
+{ 0xBEDD, 0xBEDD, 0xBEDD },
+{ 0xBEDE, 0xBEDE, 0xBEDE },
+{ 0xBEDF, 0xBEDF, 0xBEDF },
+{ 0xBEE0, 0xBEE0, 0xBEE0 },
+{ 0xBEE1, 0xBEE1, 0xBEE1 },
+{ 0xBEE2, 0xBEE2, 0xBEE2 },
+{ 0xBEE3, 0xBEE3, 0xBEE3 },
+{ 0xBEE4, 0xBEE4, 0xBEE4 },
+{ 0xBEE5, 0xBEE5, 0xBEE5 },
+{ 0xBEE6, 0xBEE6, 0xBEE6 },
+{ 0xBEE7, 0xBEE7, 0xBEE7 },
+{ 0xBEE8, 0xBEE8, 0xBEE8 },
+{ 0xBEE9, 0xBEE9, 0xBEE9 },
+{ 0xBEEA, 0xBEEA, 0xBEEA },
+{ 0xBEEB, 0xBEEB, 0xBEEB },
+{ 0xBEEC, 0xBEEC, 0xBEEC },
+{ 0xBEED, 0xBEED, 0xBEED },
+{ 0xBEEE, 0xBEEE, 0xBEEE },
+{ 0xBEEF, 0xBEEF, 0xBEEF },
+{ 0xBEF0, 0xBEF0, 0xBEF0 },
+{ 0xBEF1, 0xBEF1, 0xBEF1 },
+{ 0xBEF2, 0xBEF2, 0xBEF2 },
+{ 0xBEF3, 0xBEF3, 0xBEF3 },
+{ 0xBEF4, 0xBEF4, 0xBEF4 },
+{ 0xBEF5, 0xBEF5, 0xBEF5 },
+{ 0xBEF6, 0xBEF6, 0xBEF6 },
+{ 0xBEF7, 0xBEF7, 0xBEF7 },
+{ 0xBEF8, 0xBEF8, 0xBEF8 },
+{ 0xBEF9, 0xBEF9, 0xBEF9 },
+{ 0xBEFA, 0xBEFA, 0xBEFA },
+{ 0xBEFB, 0xBEFB, 0xBEFB },
+{ 0xBEFC, 0xBEFC, 0xBEFC },
+{ 0xBEFD, 0xBEFD, 0xBEFD },
+{ 0xBEFE, 0xBEFE, 0xBEFE },
+{ 0xBEFF, 0xBEFF, 0xBEFF },
+{ 0xBF00, 0xBF00, 0xBF00 },
+{ 0xBF01, 0xBF01, 0xBF01 },
+{ 0xBF02, 0xBF02, 0xBF02 },
+{ 0xBF03, 0xBF03, 0xBF03 },
+{ 0xBF04, 0xBF04, 0xBF04 },
+{ 0xBF05, 0xBF05, 0xBF05 },
+{ 0xBF06, 0xBF06, 0xBF06 },
+{ 0xBF07, 0xBF07, 0xBF07 },
+{ 0xBF08, 0xBF08, 0xBF08 },
+{ 0xBF09, 0xBF09, 0xBF09 },
+{ 0xBF0A, 0xBF0A, 0xBF0A },
+{ 0xBF0B, 0xBF0B, 0xBF0B },
+{ 0xBF0C, 0xBF0C, 0xBF0C },
+{ 0xBF0D, 0xBF0D, 0xBF0D },
+{ 0xBF0E, 0xBF0E, 0xBF0E },
+{ 0xBF0F, 0xBF0F, 0xBF0F },
+{ 0xBF10, 0xBF10, 0xBF10 },
+{ 0xBF11, 0xBF11, 0xBF11 },
+{ 0xBF12, 0xBF12, 0xBF12 },
+{ 0xBF13, 0xBF13, 0xBF13 },
+{ 0xBF14, 0xBF14, 0xBF14 },
+{ 0xBF15, 0xBF15, 0xBF15 },
+{ 0xBF16, 0xBF16, 0xBF16 },
+{ 0xBF17, 0xBF17, 0xBF17 },
+{ 0xBF18, 0xBF18, 0xBF18 },
+{ 0xBF19, 0xBF19, 0xBF19 },
+{ 0xBF1A, 0xBF1A, 0xBF1A },
+{ 0xBF1B, 0xBF1B, 0xBF1B },
+{ 0xBF1C, 0xBF1C, 0xBF1C },
+{ 0xBF1D, 0xBF1D, 0xBF1D },
+{ 0xBF1E, 0xBF1E, 0xBF1E },
+{ 0xBF1F, 0xBF1F, 0xBF1F },
+{ 0xBF20, 0xBF20, 0xBF20 },
+{ 0xBF21, 0xBF21, 0xBF21 },
+{ 0xBF22, 0xBF22, 0xBF22 },
+{ 0xBF23, 0xBF23, 0xBF23 },
+{ 0xBF24, 0xBF24, 0xBF24 },
+{ 0xBF25, 0xBF25, 0xBF25 },
+{ 0xBF26, 0xBF26, 0xBF26 },
+{ 0xBF27, 0xBF27, 0xBF27 },
+{ 0xBF28, 0xBF28, 0xBF28 },
+{ 0xBF29, 0xBF29, 0xBF29 },
+{ 0xBF2A, 0xBF2A, 0xBF2A },
+{ 0xBF2B, 0xBF2B, 0xBF2B },
+{ 0xBF2C, 0xBF2C, 0xBF2C },
+{ 0xBF2D, 0xBF2D, 0xBF2D },
+{ 0xBF2E, 0xBF2E, 0xBF2E },
+{ 0xBF2F, 0xBF2F, 0xBF2F },
+{ 0xBF30, 0xBF30, 0xBF30 },
+{ 0xBF31, 0xBF31, 0xBF31 },
+{ 0xBF32, 0xBF32, 0xBF32 },
+{ 0xBF33, 0xBF33, 0xBF33 },
+{ 0xBF34, 0xBF34, 0xBF34 },
+{ 0xBF35, 0xBF35, 0xBF35 },
+{ 0xBF36, 0xBF36, 0xBF36 },
+{ 0xBF37, 0xBF37, 0xBF37 },
+{ 0xBF38, 0xBF38, 0xBF38 },
+{ 0xBF39, 0xBF39, 0xBF39 },
+{ 0xBF3A, 0xBF3A, 0xBF3A },
+{ 0xBF3B, 0xBF3B, 0xBF3B },
+{ 0xBF3C, 0xBF3C, 0xBF3C },
+{ 0xBF3D, 0xBF3D, 0xBF3D },
+{ 0xBF3E, 0xBF3E, 0xBF3E },
+{ 0xBF3F, 0xBF3F, 0xBF3F },
+{ 0xBF40, 0xBF40, 0xBF40 },
+{ 0xBF41, 0xBF41, 0xBF41 },
+{ 0xBF42, 0xBF42, 0xBF42 },
+{ 0xBF43, 0xBF43, 0xBF43 },
+{ 0xBF44, 0xBF44, 0xBF44 },
+{ 0xBF45, 0xBF45, 0xBF45 },
+{ 0xBF46, 0xBF46, 0xBF46 },
+{ 0xBF47, 0xBF47, 0xBF47 },
+{ 0xBF48, 0xBF48, 0xBF48 },
+{ 0xBF49, 0xBF49, 0xBF49 },
+{ 0xBF4A, 0xBF4A, 0xBF4A },
+{ 0xBF4B, 0xBF4B, 0xBF4B },
+{ 0xBF4C, 0xBF4C, 0xBF4C },
+{ 0xBF4D, 0xBF4D, 0xBF4D },
+{ 0xBF4E, 0xBF4E, 0xBF4E },
+{ 0xBF4F, 0xBF4F, 0xBF4F },
+{ 0xBF50, 0xBF50, 0xBF50 },
+{ 0xBF51, 0xBF51, 0xBF51 },
+{ 0xBF52, 0xBF52, 0xBF52 },
+{ 0xBF53, 0xBF53, 0xBF53 },
+{ 0xBF54, 0xBF54, 0xBF54 },
+{ 0xBF55, 0xBF55, 0xBF55 },
+{ 0xBF56, 0xBF56, 0xBF56 },
+{ 0xBF57, 0xBF57, 0xBF57 },
+{ 0xBF58, 0xBF58, 0xBF58 },
+{ 0xBF59, 0xBF59, 0xBF59 },
+{ 0xBF5A, 0xBF5A, 0xBF5A },
+{ 0xBF5B, 0xBF5B, 0xBF5B },
+{ 0xBF5C, 0xBF5C, 0xBF5C },
+{ 0xBF5D, 0xBF5D, 0xBF5D },
+{ 0xBF5E, 0xBF5E, 0xBF5E },
+{ 0xBF5F, 0xBF5F, 0xBF5F },
+{ 0xBF60, 0xBF60, 0xBF60 },
+{ 0xBF61, 0xBF61, 0xBF61 },
+{ 0xBF62, 0xBF62, 0xBF62 },
+{ 0xBF63, 0xBF63, 0xBF63 },
+{ 0xBF64, 0xBF64, 0xBF64 },
+{ 0xBF65, 0xBF65, 0xBF65 },
+{ 0xBF66, 0xBF66, 0xBF66 },
+{ 0xBF67, 0xBF67, 0xBF67 },
+{ 0xBF68, 0xBF68, 0xBF68 },
+{ 0xBF69, 0xBF69, 0xBF69 },
+{ 0xBF6A, 0xBF6A, 0xBF6A },
+{ 0xBF6B, 0xBF6B, 0xBF6B },
+{ 0xBF6C, 0xBF6C, 0xBF6C },
+{ 0xBF6D, 0xBF6D, 0xBF6D },
+{ 0xBF6E, 0xBF6E, 0xBF6E },
+{ 0xBF6F, 0xBF6F, 0xBF6F },
+{ 0xBF70, 0xBF70, 0xBF70 },
+{ 0xBF71, 0xBF71, 0xBF71 },
+{ 0xBF72, 0xBF72, 0xBF72 },
+{ 0xBF73, 0xBF73, 0xBF73 },
+{ 0xBF74, 0xBF74, 0xBF74 },
+{ 0xBF75, 0xBF75, 0xBF75 },
+{ 0xBF76, 0xBF76, 0xBF76 },
+{ 0xBF77, 0xBF77, 0xBF77 },
+{ 0xBF78, 0xBF78, 0xBF78 },
+{ 0xBF79, 0xBF79, 0xBF79 },
+{ 0xBF7A, 0xBF7A, 0xBF7A },
+{ 0xBF7B, 0xBF7B, 0xBF7B },
+{ 0xBF7C, 0xBF7C, 0xBF7C },
+{ 0xBF7D, 0xBF7D, 0xBF7D },
+{ 0xBF7E, 0xBF7E, 0xBF7E },
+{ 0xBF7F, 0xBF7F, 0xBF7F },
+{ 0xBF80, 0xBF80, 0xBF80 },
+{ 0xBF81, 0xBF81, 0xBF81 },
+{ 0xBF82, 0xBF82, 0xBF82 },
+{ 0xBF83, 0xBF83, 0xBF83 },
+{ 0xBF84, 0xBF84, 0xBF84 },
+{ 0xBF85, 0xBF85, 0xBF85 },
+{ 0xBF86, 0xBF86, 0xBF86 },
+{ 0xBF87, 0xBF87, 0xBF87 },
+{ 0xBF88, 0xBF88, 0xBF88 },
+{ 0xBF89, 0xBF89, 0xBF89 },
+{ 0xBF8A, 0xBF8A, 0xBF8A },
+{ 0xBF8B, 0xBF8B, 0xBF8B },
+{ 0xBF8C, 0xBF8C, 0xBF8C },
+{ 0xBF8D, 0xBF8D, 0xBF8D },
+{ 0xBF8E, 0xBF8E, 0xBF8E },
+{ 0xBF8F, 0xBF8F, 0xBF8F },
+{ 0xBF90, 0xBF90, 0xBF90 },
+{ 0xBF91, 0xBF91, 0xBF91 },
+{ 0xBF92, 0xBF92, 0xBF92 },
+{ 0xBF93, 0xBF93, 0xBF93 },
+{ 0xBF94, 0xBF94, 0xBF94 },
+{ 0xBF95, 0xBF95, 0xBF95 },
+{ 0xBF96, 0xBF96, 0xBF96 },
+{ 0xBF97, 0xBF97, 0xBF97 },
+{ 0xBF98, 0xBF98, 0xBF98 },
+{ 0xBF99, 0xBF99, 0xBF99 },
+{ 0xBF9A, 0xBF9A, 0xBF9A },
+{ 0xBF9B, 0xBF9B, 0xBF9B },
+{ 0xBF9C, 0xBF9C, 0xBF9C },
+{ 0xBF9D, 0xBF9D, 0xBF9D },
+{ 0xBF9E, 0xBF9E, 0xBF9E },
+{ 0xBF9F, 0xBF9F, 0xBF9F },
+{ 0xBFA0, 0xBFA0, 0xBFA0 },
+{ 0xBFA1, 0xBFA1, 0xBFA1 },
+{ 0xBFA2, 0xBFA2, 0xBFA2 },
+{ 0xBFA3, 0xBFA3, 0xBFA3 },
+{ 0xBFA4, 0xBFA4, 0xBFA4 },
+{ 0xBFA5, 0xBFA5, 0xBFA5 },
+{ 0xBFA6, 0xBFA6, 0xBFA6 },
+{ 0xBFA7, 0xBFA7, 0xBFA7 },
+{ 0xBFA8, 0xBFA8, 0xBFA8 },
+{ 0xBFA9, 0xBFA9, 0xBFA9 },
+{ 0xBFAA, 0xBFAA, 0xBFAA },
+{ 0xBFAB, 0xBFAB, 0xBFAB },
+{ 0xBFAC, 0xBFAC, 0xBFAC },
+{ 0xBFAD, 0xBFAD, 0xBFAD },
+{ 0xBFAE, 0xBFAE, 0xBFAE },
+{ 0xBFAF, 0xBFAF, 0xBFAF },
+{ 0xBFB0, 0xBFB0, 0xBFB0 },
+{ 0xBFB1, 0xBFB1, 0xBFB1 },
+{ 0xBFB2, 0xBFB2, 0xBFB2 },
+{ 0xBFB3, 0xBFB3, 0xBFB3 },
+{ 0xBFB4, 0xBFB4, 0xBFB4 },
+{ 0xBFB5, 0xBFB5, 0xBFB5 },
+{ 0xBFB6, 0xBFB6, 0xBFB6 },
+{ 0xBFB7, 0xBFB7, 0xBFB7 },
+{ 0xBFB8, 0xBFB8, 0xBFB8 },
+{ 0xBFB9, 0xBFB9, 0xBFB9 },
+{ 0xBFBA, 0xBFBA, 0xBFBA },
+{ 0xBFBB, 0xBFBB, 0xBFBB },
+{ 0xBFBC, 0xBFBC, 0xBFBC },
+{ 0xBFBD, 0xBFBD, 0xBFBD },
+{ 0xBFBE, 0xBFBE, 0xBFBE },
+{ 0xBFBF, 0xBFBF, 0xBFBF },
+{ 0xBFC0, 0xBFC0, 0xBFC0 },
+{ 0xBFC1, 0xBFC1, 0xBFC1 },
+{ 0xBFC2, 0xBFC2, 0xBFC2 },
+{ 0xBFC3, 0xBFC3, 0xBFC3 },
+{ 0xBFC4, 0xBFC4, 0xBFC4 },
+{ 0xBFC5, 0xBFC5, 0xBFC5 },
+{ 0xBFC6, 0xBFC6, 0xBFC6 },
+{ 0xBFC7, 0xBFC7, 0xBFC7 },
+{ 0xBFC8, 0xBFC8, 0xBFC8 },
+{ 0xBFC9, 0xBFC9, 0xBFC9 },
+{ 0xBFCA, 0xBFCA, 0xBFCA },
+{ 0xBFCB, 0xBFCB, 0xBFCB },
+{ 0xBFCC, 0xBFCC, 0xBFCC },
+{ 0xBFCD, 0xBFCD, 0xBFCD },
+{ 0xBFCE, 0xBFCE, 0xBFCE },
+{ 0xBFCF, 0xBFCF, 0xBFCF },
+{ 0xBFD0, 0xBFD0, 0xBFD0 },
+{ 0xBFD1, 0xBFD1, 0xBFD1 },
+{ 0xBFD2, 0xBFD2, 0xBFD2 },
+{ 0xBFD3, 0xBFD3, 0xBFD3 },
+{ 0xBFD4, 0xBFD4, 0xBFD4 },
+{ 0xBFD5, 0xBFD5, 0xBFD5 },
+{ 0xBFD6, 0xBFD6, 0xBFD6 },
+{ 0xBFD7, 0xBFD7, 0xBFD7 },
+{ 0xBFD8, 0xBFD8, 0xBFD8 },
+{ 0xBFD9, 0xBFD9, 0xBFD9 },
+{ 0xBFDA, 0xBFDA, 0xBFDA },
+{ 0xBFDB, 0xBFDB, 0xBFDB },
+{ 0xBFDC, 0xBFDC, 0xBFDC },
+{ 0xBFDD, 0xBFDD, 0xBFDD },
+{ 0xBFDE, 0xBFDE, 0xBFDE },
+{ 0xBFDF, 0xBFDF, 0xBFDF },
+{ 0xBFE0, 0xBFE0, 0xBFE0 },
+{ 0xBFE1, 0xBFE1, 0xBFE1 },
+{ 0xBFE2, 0xBFE2, 0xBFE2 },
+{ 0xBFE3, 0xBFE3, 0xBFE3 },
+{ 0xBFE4, 0xBFE4, 0xBFE4 },
+{ 0xBFE5, 0xBFE5, 0xBFE5 },
+{ 0xBFE6, 0xBFE6, 0xBFE6 },
+{ 0xBFE7, 0xBFE7, 0xBFE7 },
+{ 0xBFE8, 0xBFE8, 0xBFE8 },
+{ 0xBFE9, 0xBFE9, 0xBFE9 },
+{ 0xBFEA, 0xBFEA, 0xBFEA },
+{ 0xBFEB, 0xBFEB, 0xBFEB },
+{ 0xBFEC, 0xBFEC, 0xBFEC },
+{ 0xBFED, 0xBFED, 0xBFED },
+{ 0xBFEE, 0xBFEE, 0xBFEE },
+{ 0xBFEF, 0xBFEF, 0xBFEF },
+{ 0xBFF0, 0xBFF0, 0xBFF0 },
+{ 0xBFF1, 0xBFF1, 0xBFF1 },
+{ 0xBFF2, 0xBFF2, 0xBFF2 },
+{ 0xBFF3, 0xBFF3, 0xBFF3 },
+{ 0xBFF4, 0xBFF4, 0xBFF4 },
+{ 0xBFF5, 0xBFF5, 0xBFF5 },
+{ 0xBFF6, 0xBFF6, 0xBFF6 },
+{ 0xBFF7, 0xBFF7, 0xBFF7 },
+{ 0xBFF8, 0xBFF8, 0xBFF8 },
+{ 0xBFF9, 0xBFF9, 0xBFF9 },
+{ 0xBFFA, 0xBFFA, 0xBFFA },
+{ 0xBFFB, 0xBFFB, 0xBFFB },
+{ 0xBFFC, 0xBFFC, 0xBFFC },
+{ 0xBFFD, 0xBFFD, 0xBFFD },
+{ 0xBFFE, 0xBFFE, 0xBFFE },
+{ 0xBFFF, 0xBFFF, 0xBFFF },
+{ 0xC000, 0xC000, 0xC000 },
+{ 0xC001, 0xC001, 0xC001 },
+{ 0xC002, 0xC002, 0xC002 },
+{ 0xC003, 0xC003, 0xC003 },
+{ 0xC004, 0xC004, 0xC004 },
+{ 0xC005, 0xC005, 0xC005 },
+{ 0xC006, 0xC006, 0xC006 },
+{ 0xC007, 0xC007, 0xC007 },
+{ 0xC008, 0xC008, 0xC008 },
+{ 0xC009, 0xC009, 0xC009 },
+{ 0xC00A, 0xC00A, 0xC00A },
+{ 0xC00B, 0xC00B, 0xC00B },
+{ 0xC00C, 0xC00C, 0xC00C },
+{ 0xC00D, 0xC00D, 0xC00D },
+{ 0xC00E, 0xC00E, 0xC00E },
+{ 0xC00F, 0xC00F, 0xC00F },
+{ 0xC010, 0xC010, 0xC010 },
+{ 0xC011, 0xC011, 0xC011 },
+{ 0xC012, 0xC012, 0xC012 },
+{ 0xC013, 0xC013, 0xC013 },
+{ 0xC014, 0xC014, 0xC014 },
+{ 0xC015, 0xC015, 0xC015 },
+{ 0xC016, 0xC016, 0xC016 },
+{ 0xC017, 0xC017, 0xC017 },
+{ 0xC018, 0xC018, 0xC018 },
+{ 0xC019, 0xC019, 0xC019 },
+{ 0xC01A, 0xC01A, 0xC01A },
+{ 0xC01B, 0xC01B, 0xC01B },
+{ 0xC01C, 0xC01C, 0xC01C },
+{ 0xC01D, 0xC01D, 0xC01D },
+{ 0xC01E, 0xC01E, 0xC01E },
+{ 0xC01F, 0xC01F, 0xC01F },
+{ 0xC020, 0xC020, 0xC020 },
+{ 0xC021, 0xC021, 0xC021 },
+{ 0xC022, 0xC022, 0xC022 },
+{ 0xC023, 0xC023, 0xC023 },
+{ 0xC024, 0xC024, 0xC024 },
+{ 0xC025, 0xC025, 0xC025 },
+{ 0xC026, 0xC026, 0xC026 },
+{ 0xC027, 0xC027, 0xC027 },
+{ 0xC028, 0xC028, 0xC028 },
+{ 0xC029, 0xC029, 0xC029 },
+{ 0xC02A, 0xC02A, 0xC02A },
+{ 0xC02B, 0xC02B, 0xC02B },
+{ 0xC02C, 0xC02C, 0xC02C },
+{ 0xC02D, 0xC02D, 0xC02D },
+{ 0xC02E, 0xC02E, 0xC02E },
+{ 0xC02F, 0xC02F, 0xC02F },
+{ 0xC030, 0xC030, 0xC030 },
+{ 0xC031, 0xC031, 0xC031 },
+{ 0xC032, 0xC032, 0xC032 },
+{ 0xC033, 0xC033, 0xC033 },
+{ 0xC034, 0xC034, 0xC034 },
+{ 0xC035, 0xC035, 0xC035 },
+{ 0xC036, 0xC036, 0xC036 },
+{ 0xC037, 0xC037, 0xC037 },
+{ 0xC038, 0xC038, 0xC038 },
+{ 0xC039, 0xC039, 0xC039 },
+{ 0xC03A, 0xC03A, 0xC03A },
+{ 0xC03B, 0xC03B, 0xC03B },
+{ 0xC03C, 0xC03C, 0xC03C },
+{ 0xC03D, 0xC03D, 0xC03D },
+{ 0xC03E, 0xC03E, 0xC03E },
+{ 0xC03F, 0xC03F, 0xC03F },
+{ 0xC040, 0xC040, 0xC040 },
+{ 0xC041, 0xC041, 0xC041 },
+{ 0xC042, 0xC042, 0xC042 },
+{ 0xC043, 0xC043, 0xC043 },
+{ 0xC044, 0xC044, 0xC044 },
+{ 0xC045, 0xC045, 0xC045 },
+{ 0xC046, 0xC046, 0xC046 },
+{ 0xC047, 0xC047, 0xC047 },
+{ 0xC048, 0xC048, 0xC048 },
+{ 0xC049, 0xC049, 0xC049 },
+{ 0xC04A, 0xC04A, 0xC04A },
+{ 0xC04B, 0xC04B, 0xC04B },
+{ 0xC04C, 0xC04C, 0xC04C },
+{ 0xC04D, 0xC04D, 0xC04D },
+{ 0xC04E, 0xC04E, 0xC04E },
+{ 0xC04F, 0xC04F, 0xC04F },
+{ 0xC050, 0xC050, 0xC050 },
+{ 0xC051, 0xC051, 0xC051 },
+{ 0xC052, 0xC052, 0xC052 },
+{ 0xC053, 0xC053, 0xC053 },
+{ 0xC054, 0xC054, 0xC054 },
+{ 0xC055, 0xC055, 0xC055 },
+{ 0xC056, 0xC056, 0xC056 },
+{ 0xC057, 0xC057, 0xC057 },
+{ 0xC058, 0xC058, 0xC058 },
+{ 0xC059, 0xC059, 0xC059 },
+{ 0xC05A, 0xC05A, 0xC05A },
+{ 0xC05B, 0xC05B, 0xC05B },
+{ 0xC05C, 0xC05C, 0xC05C },
+{ 0xC05D, 0xC05D, 0xC05D },
+{ 0xC05E, 0xC05E, 0xC05E },
+{ 0xC05F, 0xC05F, 0xC05F },
+{ 0xC060, 0xC060, 0xC060 },
+{ 0xC061, 0xC061, 0xC061 },
+{ 0xC062, 0xC062, 0xC062 },
+{ 0xC063, 0xC063, 0xC063 },
+{ 0xC064, 0xC064, 0xC064 },
+{ 0xC065, 0xC065, 0xC065 },
+{ 0xC066, 0xC066, 0xC066 },
+{ 0xC067, 0xC067, 0xC067 },
+{ 0xC068, 0xC068, 0xC068 },
+{ 0xC069, 0xC069, 0xC069 },
+{ 0xC06A, 0xC06A, 0xC06A },
+{ 0xC06B, 0xC06B, 0xC06B },
+{ 0xC06C, 0xC06C, 0xC06C },
+{ 0xC06D, 0xC06D, 0xC06D },
+{ 0xC06E, 0xC06E, 0xC06E },
+{ 0xC06F, 0xC06F, 0xC06F },
+{ 0xC070, 0xC070, 0xC070 },
+{ 0xC071, 0xC071, 0xC071 },
+{ 0xC072, 0xC072, 0xC072 },
+{ 0xC073, 0xC073, 0xC073 },
+{ 0xC074, 0xC074, 0xC074 },
+{ 0xC075, 0xC075, 0xC075 },
+{ 0xC076, 0xC076, 0xC076 },
+{ 0xC077, 0xC077, 0xC077 },
+{ 0xC078, 0xC078, 0xC078 },
+{ 0xC079, 0xC079, 0xC079 },
+{ 0xC07A, 0xC07A, 0xC07A },
+{ 0xC07B, 0xC07B, 0xC07B },
+{ 0xC07C, 0xC07C, 0xC07C },
+{ 0xC07D, 0xC07D, 0xC07D },
+{ 0xC07E, 0xC07E, 0xC07E },
+{ 0xC07F, 0xC07F, 0xC07F },
+{ 0xC080, 0xC080, 0xC080 },
+{ 0xC081, 0xC081, 0xC081 },
+{ 0xC082, 0xC082, 0xC082 },
+{ 0xC083, 0xC083, 0xC083 },
+{ 0xC084, 0xC084, 0xC084 },
+{ 0xC085, 0xC085, 0xC085 },
+{ 0xC086, 0xC086, 0xC086 },
+{ 0xC087, 0xC087, 0xC087 },
+{ 0xC088, 0xC088, 0xC088 },
+{ 0xC089, 0xC089, 0xC089 },
+{ 0xC08A, 0xC08A, 0xC08A },
+{ 0xC08B, 0xC08B, 0xC08B },
+{ 0xC08C, 0xC08C, 0xC08C },
+{ 0xC08D, 0xC08D, 0xC08D },
+{ 0xC08E, 0xC08E, 0xC08E },
+{ 0xC08F, 0xC08F, 0xC08F },
+{ 0xC090, 0xC090, 0xC090 },
+{ 0xC091, 0xC091, 0xC091 },
+{ 0xC092, 0xC092, 0xC092 },
+{ 0xC093, 0xC093, 0xC093 },
+{ 0xC094, 0xC094, 0xC094 },
+{ 0xC095, 0xC095, 0xC095 },
+{ 0xC096, 0xC096, 0xC096 },
+{ 0xC097, 0xC097, 0xC097 },
+{ 0xC098, 0xC098, 0xC098 },
+{ 0xC099, 0xC099, 0xC099 },
+{ 0xC09A, 0xC09A, 0xC09A },
+{ 0xC09B, 0xC09B, 0xC09B },
+{ 0xC09C, 0xC09C, 0xC09C },
+{ 0xC09D, 0xC09D, 0xC09D },
+{ 0xC09E, 0xC09E, 0xC09E },
+{ 0xC09F, 0xC09F, 0xC09F },
+{ 0xC0A0, 0xC0A0, 0xC0A0 },
+{ 0xC0A1, 0xC0A1, 0xC0A1 },
+{ 0xC0A2, 0xC0A2, 0xC0A2 },
+{ 0xC0A3, 0xC0A3, 0xC0A3 },
+{ 0xC0A4, 0xC0A4, 0xC0A4 },
+{ 0xC0A5, 0xC0A5, 0xC0A5 },
+{ 0xC0A6, 0xC0A6, 0xC0A6 },
+{ 0xC0A7, 0xC0A7, 0xC0A7 },
+{ 0xC0A8, 0xC0A8, 0xC0A8 },
+{ 0xC0A9, 0xC0A9, 0xC0A9 },
+{ 0xC0AA, 0xC0AA, 0xC0AA },
+{ 0xC0AB, 0xC0AB, 0xC0AB },
+{ 0xC0AC, 0xC0AC, 0xC0AC },
+{ 0xC0AD, 0xC0AD, 0xC0AD },
+{ 0xC0AE, 0xC0AE, 0xC0AE },
+{ 0xC0AF, 0xC0AF, 0xC0AF },
+{ 0xC0B0, 0xC0B0, 0xC0B0 },
+{ 0xC0B1, 0xC0B1, 0xC0B1 },
+{ 0xC0B2, 0xC0B2, 0xC0B2 },
+{ 0xC0B3, 0xC0B3, 0xC0B3 },
+{ 0xC0B4, 0xC0B4, 0xC0B4 },
+{ 0xC0B5, 0xC0B5, 0xC0B5 },
+{ 0xC0B6, 0xC0B6, 0xC0B6 },
+{ 0xC0B7, 0xC0B7, 0xC0B7 },
+{ 0xC0B8, 0xC0B8, 0xC0B8 },
+{ 0xC0B9, 0xC0B9, 0xC0B9 },
+{ 0xC0BA, 0xC0BA, 0xC0BA },
+{ 0xC0BB, 0xC0BB, 0xC0BB },
+{ 0xC0BC, 0xC0BC, 0xC0BC },
+{ 0xC0BD, 0xC0BD, 0xC0BD },
+{ 0xC0BE, 0xC0BE, 0xC0BE },
+{ 0xC0BF, 0xC0BF, 0xC0BF },
+{ 0xC0C0, 0xC0C0, 0xC0C0 },
+{ 0xC0C1, 0xC0C1, 0xC0C1 },
+{ 0xC0C2, 0xC0C2, 0xC0C2 },
+{ 0xC0C3, 0xC0C3, 0xC0C3 },
+{ 0xC0C4, 0xC0C4, 0xC0C4 },
+{ 0xC0C5, 0xC0C5, 0xC0C5 },
+{ 0xC0C6, 0xC0C6, 0xC0C6 },
+{ 0xC0C7, 0xC0C7, 0xC0C7 },
+{ 0xC0C8, 0xC0C8, 0xC0C8 },
+{ 0xC0C9, 0xC0C9, 0xC0C9 },
+{ 0xC0CA, 0xC0CA, 0xC0CA },
+{ 0xC0CB, 0xC0CB, 0xC0CB },
+{ 0xC0CC, 0xC0CC, 0xC0CC },
+{ 0xC0CD, 0xC0CD, 0xC0CD },
+{ 0xC0CE, 0xC0CE, 0xC0CE },
+{ 0xC0CF, 0xC0CF, 0xC0CF },
+{ 0xC0D0, 0xC0D0, 0xC0D0 },
+{ 0xC0D1, 0xC0D1, 0xC0D1 },
+{ 0xC0D2, 0xC0D2, 0xC0D2 },
+{ 0xC0D3, 0xC0D3, 0xC0D3 },
+{ 0xC0D4, 0xC0D4, 0xC0D4 },
+{ 0xC0D5, 0xC0D5, 0xC0D5 },
+{ 0xC0D6, 0xC0D6, 0xC0D6 },
+{ 0xC0D7, 0xC0D7, 0xC0D7 },
+{ 0xC0D8, 0xC0D8, 0xC0D8 },
+{ 0xC0D9, 0xC0D9, 0xC0D9 },
+{ 0xC0DA, 0xC0DA, 0xC0DA },
+{ 0xC0DB, 0xC0DB, 0xC0DB },
+{ 0xC0DC, 0xC0DC, 0xC0DC },
+{ 0xC0DD, 0xC0DD, 0xC0DD },
+{ 0xC0DE, 0xC0DE, 0xC0DE },
+{ 0xC0DF, 0xC0DF, 0xC0DF },
+{ 0xC0E0, 0xC0E0, 0xC0E0 },
+{ 0xC0E1, 0xC0E1, 0xC0E1 },
+{ 0xC0E2, 0xC0E2, 0xC0E2 },
+{ 0xC0E3, 0xC0E3, 0xC0E3 },
+{ 0xC0E4, 0xC0E4, 0xC0E4 },
+{ 0xC0E5, 0xC0E5, 0xC0E5 },
+{ 0xC0E6, 0xC0E6, 0xC0E6 },
+{ 0xC0E7, 0xC0E7, 0xC0E7 },
+{ 0xC0E8, 0xC0E8, 0xC0E8 },
+{ 0xC0E9, 0xC0E9, 0xC0E9 },
+{ 0xC0EA, 0xC0EA, 0xC0EA },
+{ 0xC0EB, 0xC0EB, 0xC0EB },
+{ 0xC0EC, 0xC0EC, 0xC0EC },
+{ 0xC0ED, 0xC0ED, 0xC0ED },
+{ 0xC0EE, 0xC0EE, 0xC0EE },
+{ 0xC0EF, 0xC0EF, 0xC0EF },
+{ 0xC0F0, 0xC0F0, 0xC0F0 },
+{ 0xC0F1, 0xC0F1, 0xC0F1 },
+{ 0xC0F2, 0xC0F2, 0xC0F2 },
+{ 0xC0F3, 0xC0F3, 0xC0F3 },
+{ 0xC0F4, 0xC0F4, 0xC0F4 },
+{ 0xC0F5, 0xC0F5, 0xC0F5 },
+{ 0xC0F6, 0xC0F6, 0xC0F6 },
+{ 0xC0F7, 0xC0F7, 0xC0F7 },
+{ 0xC0F8, 0xC0F8, 0xC0F8 },
+{ 0xC0F9, 0xC0F9, 0xC0F9 },
+{ 0xC0FA, 0xC0FA, 0xC0FA },
+{ 0xC0FB, 0xC0FB, 0xC0FB },
+{ 0xC0FC, 0xC0FC, 0xC0FC },
+{ 0xC0FD, 0xC0FD, 0xC0FD },
+{ 0xC0FE, 0xC0FE, 0xC0FE },
+{ 0xC0FF, 0xC0FF, 0xC0FF },
+{ 0xC100, 0xC100, 0xC100 },
+{ 0xC101, 0xC101, 0xC101 },
+{ 0xC102, 0xC102, 0xC102 },
+{ 0xC103, 0xC103, 0xC103 },
+{ 0xC104, 0xC104, 0xC104 },
+{ 0xC105, 0xC105, 0xC105 },
+{ 0xC106, 0xC106, 0xC106 },
+{ 0xC107, 0xC107, 0xC107 },
+{ 0xC108, 0xC108, 0xC108 },
+{ 0xC109, 0xC109, 0xC109 },
+{ 0xC10A, 0xC10A, 0xC10A },
+{ 0xC10B, 0xC10B, 0xC10B },
+{ 0xC10C, 0xC10C, 0xC10C },
+{ 0xC10D, 0xC10D, 0xC10D },
+{ 0xC10E, 0xC10E, 0xC10E },
+{ 0xC10F, 0xC10F, 0xC10F },
+{ 0xC110, 0xC110, 0xC110 },
+{ 0xC111, 0xC111, 0xC111 },
+{ 0xC112, 0xC112, 0xC112 },
+{ 0xC113, 0xC113, 0xC113 },
+{ 0xC114, 0xC114, 0xC114 },
+{ 0xC115, 0xC115, 0xC115 },
+{ 0xC116, 0xC116, 0xC116 },
+{ 0xC117, 0xC117, 0xC117 },
+{ 0xC118, 0xC118, 0xC118 },
+{ 0xC119, 0xC119, 0xC119 },
+{ 0xC11A, 0xC11A, 0xC11A },
+{ 0xC11B, 0xC11B, 0xC11B },
+{ 0xC11C, 0xC11C, 0xC11C },
+{ 0xC11D, 0xC11D, 0xC11D },
+{ 0xC11E, 0xC11E, 0xC11E },
+{ 0xC11F, 0xC11F, 0xC11F },
+{ 0xC120, 0xC120, 0xC120 },
+{ 0xC121, 0xC121, 0xC121 },
+{ 0xC122, 0xC122, 0xC122 },
+{ 0xC123, 0xC123, 0xC123 },
+{ 0xC124, 0xC124, 0xC124 },
+{ 0xC125, 0xC125, 0xC125 },
+{ 0xC126, 0xC126, 0xC126 },
+{ 0xC127, 0xC127, 0xC127 },
+{ 0xC128, 0xC128, 0xC128 },
+{ 0xC129, 0xC129, 0xC129 },
+{ 0xC12A, 0xC12A, 0xC12A },
+{ 0xC12B, 0xC12B, 0xC12B },
+{ 0xC12C, 0xC12C, 0xC12C },
+{ 0xC12D, 0xC12D, 0xC12D },
+{ 0xC12E, 0xC12E, 0xC12E },
+{ 0xC12F, 0xC12F, 0xC12F },
+{ 0xC130, 0xC130, 0xC130 },
+{ 0xC131, 0xC131, 0xC131 },
+{ 0xC132, 0xC132, 0xC132 },
+{ 0xC133, 0xC133, 0xC133 },
+{ 0xC134, 0xC134, 0xC134 },
+{ 0xC135, 0xC135, 0xC135 },
+{ 0xC136, 0xC136, 0xC136 },
+{ 0xC137, 0xC137, 0xC137 },
+{ 0xC138, 0xC138, 0xC138 },
+{ 0xC139, 0xC139, 0xC139 },
+{ 0xC13A, 0xC13A, 0xC13A },
+{ 0xC13B, 0xC13B, 0xC13B },
+{ 0xC13C, 0xC13C, 0xC13C },
+{ 0xC13D, 0xC13D, 0xC13D },
+{ 0xC13E, 0xC13E, 0xC13E },
+{ 0xC13F, 0xC13F, 0xC13F },
+{ 0xC140, 0xC140, 0xC140 },
+{ 0xC141, 0xC141, 0xC141 },
+{ 0xC142, 0xC142, 0xC142 },
+{ 0xC143, 0xC143, 0xC143 },
+{ 0xC144, 0xC144, 0xC144 },
+{ 0xC145, 0xC145, 0xC145 },
+{ 0xC146, 0xC146, 0xC146 },
+{ 0xC147, 0xC147, 0xC147 },
+{ 0xC148, 0xC148, 0xC148 },
+{ 0xC149, 0xC149, 0xC149 },
+{ 0xC14A, 0xC14A, 0xC14A },
+{ 0xC14B, 0xC14B, 0xC14B },
+{ 0xC14C, 0xC14C, 0xC14C },
+{ 0xC14D, 0xC14D, 0xC14D },
+{ 0xC14E, 0xC14E, 0xC14E },
+{ 0xC14F, 0xC14F, 0xC14F },
+{ 0xC150, 0xC150, 0xC150 },
+{ 0xC151, 0xC151, 0xC151 },
+{ 0xC152, 0xC152, 0xC152 },
+{ 0xC153, 0xC153, 0xC153 },
+{ 0xC154, 0xC154, 0xC154 },
+{ 0xC155, 0xC155, 0xC155 },
+{ 0xC156, 0xC156, 0xC156 },
+{ 0xC157, 0xC157, 0xC157 },
+{ 0xC158, 0xC158, 0xC158 },
+{ 0xC159, 0xC159, 0xC159 },
+{ 0xC15A, 0xC15A, 0xC15A },
+{ 0xC15B, 0xC15B, 0xC15B },
+{ 0xC15C, 0xC15C, 0xC15C },
+{ 0xC15D, 0xC15D, 0xC15D },
+{ 0xC15E, 0xC15E, 0xC15E },
+{ 0xC15F, 0xC15F, 0xC15F },
+{ 0xC160, 0xC160, 0xC160 },
+{ 0xC161, 0xC161, 0xC161 },
+{ 0xC162, 0xC162, 0xC162 },
+{ 0xC163, 0xC163, 0xC163 },
+{ 0xC164, 0xC164, 0xC164 },
+{ 0xC165, 0xC165, 0xC165 },
+{ 0xC166, 0xC166, 0xC166 },
+{ 0xC167, 0xC167, 0xC167 },
+{ 0xC168, 0xC168, 0xC168 },
+{ 0xC169, 0xC169, 0xC169 },
+{ 0xC16A, 0xC16A, 0xC16A },
+{ 0xC16B, 0xC16B, 0xC16B },
+{ 0xC16C, 0xC16C, 0xC16C },
+{ 0xC16D, 0xC16D, 0xC16D },
+{ 0xC16E, 0xC16E, 0xC16E },
+{ 0xC16F, 0xC16F, 0xC16F },
+{ 0xC170, 0xC170, 0xC170 },
+{ 0xC171, 0xC171, 0xC171 },
+{ 0xC172, 0xC172, 0xC172 },
+{ 0xC173, 0xC173, 0xC173 },
+{ 0xC174, 0xC174, 0xC174 },
+{ 0xC175, 0xC175, 0xC175 },
+{ 0xC176, 0xC176, 0xC176 },
+{ 0xC177, 0xC177, 0xC177 },
+{ 0xC178, 0xC178, 0xC178 },
+{ 0xC179, 0xC179, 0xC179 },
+{ 0xC17A, 0xC17A, 0xC17A },
+{ 0xC17B, 0xC17B, 0xC17B },
+{ 0xC17C, 0xC17C, 0xC17C },
+{ 0xC17D, 0xC17D, 0xC17D },
+{ 0xC17E, 0xC17E, 0xC17E },
+{ 0xC17F, 0xC17F, 0xC17F },
+{ 0xC180, 0xC180, 0xC180 },
+{ 0xC181, 0xC181, 0xC181 },
+{ 0xC182, 0xC182, 0xC182 },
+{ 0xC183, 0xC183, 0xC183 },
+{ 0xC184, 0xC184, 0xC184 },
+{ 0xC185, 0xC185, 0xC185 },
+{ 0xC186, 0xC186, 0xC186 },
+{ 0xC187, 0xC187, 0xC187 },
+{ 0xC188, 0xC188, 0xC188 },
+{ 0xC189, 0xC189, 0xC189 },
+{ 0xC18A, 0xC18A, 0xC18A },
+{ 0xC18B, 0xC18B, 0xC18B },
+{ 0xC18C, 0xC18C, 0xC18C },
+{ 0xC18D, 0xC18D, 0xC18D },
+{ 0xC18E, 0xC18E, 0xC18E },
+{ 0xC18F, 0xC18F, 0xC18F },
+{ 0xC190, 0xC190, 0xC190 },
+{ 0xC191, 0xC191, 0xC191 },
+{ 0xC192, 0xC192, 0xC192 },
+{ 0xC193, 0xC193, 0xC193 },
+{ 0xC194, 0xC194, 0xC194 },
+{ 0xC195, 0xC195, 0xC195 },
+{ 0xC196, 0xC196, 0xC196 },
+{ 0xC197, 0xC197, 0xC197 },
+{ 0xC198, 0xC198, 0xC198 },
+{ 0xC199, 0xC199, 0xC199 },
+{ 0xC19A, 0xC19A, 0xC19A },
+{ 0xC19B, 0xC19B, 0xC19B },
+{ 0xC19C, 0xC19C, 0xC19C },
+{ 0xC19D, 0xC19D, 0xC19D },
+{ 0xC19E, 0xC19E, 0xC19E },
+{ 0xC19F, 0xC19F, 0xC19F },
+{ 0xC1A0, 0xC1A0, 0xC1A0 },
+{ 0xC1A1, 0xC1A1, 0xC1A1 },
+{ 0xC1A2, 0xC1A2, 0xC1A2 },
+{ 0xC1A3, 0xC1A3, 0xC1A3 },
+{ 0xC1A4, 0xC1A4, 0xC1A4 },
+{ 0xC1A5, 0xC1A5, 0xC1A5 },
+{ 0xC1A6, 0xC1A6, 0xC1A6 },
+{ 0xC1A7, 0xC1A7, 0xC1A7 },
+{ 0xC1A8, 0xC1A8, 0xC1A8 },
+{ 0xC1A9, 0xC1A9, 0xC1A9 },
+{ 0xC1AA, 0xC1AA, 0xC1AA },
+{ 0xC1AB, 0xC1AB, 0xC1AB },
+{ 0xC1AC, 0xC1AC, 0xC1AC },
+{ 0xC1AD, 0xC1AD, 0xC1AD },
+{ 0xC1AE, 0xC1AE, 0xC1AE },
+{ 0xC1AF, 0xC1AF, 0xC1AF },
+{ 0xC1B0, 0xC1B0, 0xC1B0 },
+{ 0xC1B1, 0xC1B1, 0xC1B1 },
+{ 0xC1B2, 0xC1B2, 0xC1B2 },
+{ 0xC1B3, 0xC1B3, 0xC1B3 },
+{ 0xC1B4, 0xC1B4, 0xC1B4 },
+{ 0xC1B5, 0xC1B5, 0xC1B5 },
+{ 0xC1B6, 0xC1B6, 0xC1B6 },
+{ 0xC1B7, 0xC1B7, 0xC1B7 },
+{ 0xC1B8, 0xC1B8, 0xC1B8 },
+{ 0xC1B9, 0xC1B9, 0xC1B9 },
+{ 0xC1BA, 0xC1BA, 0xC1BA },
+{ 0xC1BB, 0xC1BB, 0xC1BB },
+{ 0xC1BC, 0xC1BC, 0xC1BC },
+{ 0xC1BD, 0xC1BD, 0xC1BD },
+{ 0xC1BE, 0xC1BE, 0xC1BE },
+{ 0xC1BF, 0xC1BF, 0xC1BF },
+{ 0xC1C0, 0xC1C0, 0xC1C0 },
+{ 0xC1C1, 0xC1C1, 0xC1C1 },
+{ 0xC1C2, 0xC1C2, 0xC1C2 },
+{ 0xC1C3, 0xC1C3, 0xC1C3 },
+{ 0xC1C4, 0xC1C4, 0xC1C4 },
+{ 0xC1C5, 0xC1C5, 0xC1C5 },
+{ 0xC1C6, 0xC1C6, 0xC1C6 },
+{ 0xC1C7, 0xC1C7, 0xC1C7 },
+{ 0xC1C8, 0xC1C8, 0xC1C8 },
+{ 0xC1C9, 0xC1C9, 0xC1C9 },
+{ 0xC1CA, 0xC1CA, 0xC1CA },
+{ 0xC1CB, 0xC1CB, 0xC1CB },
+{ 0xC1CC, 0xC1CC, 0xC1CC },
+{ 0xC1CD, 0xC1CD, 0xC1CD },
+{ 0xC1CE, 0xC1CE, 0xC1CE },
+{ 0xC1CF, 0xC1CF, 0xC1CF },
+{ 0xC1D0, 0xC1D0, 0xC1D0 },
+{ 0xC1D1, 0xC1D1, 0xC1D1 },
+{ 0xC1D2, 0xC1D2, 0xC1D2 },
+{ 0xC1D3, 0xC1D3, 0xC1D3 },
+{ 0xC1D4, 0xC1D4, 0xC1D4 },
+{ 0xC1D5, 0xC1D5, 0xC1D5 },
+{ 0xC1D6, 0xC1D6, 0xC1D6 },
+{ 0xC1D7, 0xC1D7, 0xC1D7 },
+{ 0xC1D8, 0xC1D8, 0xC1D8 },
+{ 0xC1D9, 0xC1D9, 0xC1D9 },
+{ 0xC1DA, 0xC1DA, 0xC1DA },
+{ 0xC1DB, 0xC1DB, 0xC1DB },
+{ 0xC1DC, 0xC1DC, 0xC1DC },
+{ 0xC1DD, 0xC1DD, 0xC1DD },
+{ 0xC1DE, 0xC1DE, 0xC1DE },
+{ 0xC1DF, 0xC1DF, 0xC1DF },
+{ 0xC1E0, 0xC1E0, 0xC1E0 },
+{ 0xC1E1, 0xC1E1, 0xC1E1 },
+{ 0xC1E2, 0xC1E2, 0xC1E2 },
+{ 0xC1E3, 0xC1E3, 0xC1E3 },
+{ 0xC1E4, 0xC1E4, 0xC1E4 },
+{ 0xC1E5, 0xC1E5, 0xC1E5 },
+{ 0xC1E6, 0xC1E6, 0xC1E6 },
+{ 0xC1E7, 0xC1E7, 0xC1E7 },
+{ 0xC1E8, 0xC1E8, 0xC1E8 },
+{ 0xC1E9, 0xC1E9, 0xC1E9 },
+{ 0xC1EA, 0xC1EA, 0xC1EA },
+{ 0xC1EB, 0xC1EB, 0xC1EB },
+{ 0xC1EC, 0xC1EC, 0xC1EC },
+{ 0xC1ED, 0xC1ED, 0xC1ED },
+{ 0xC1EE, 0xC1EE, 0xC1EE },
+{ 0xC1EF, 0xC1EF, 0xC1EF },
+{ 0xC1F0, 0xC1F0, 0xC1F0 },
+{ 0xC1F1, 0xC1F1, 0xC1F1 },
+{ 0xC1F2, 0xC1F2, 0xC1F2 },
+{ 0xC1F3, 0xC1F3, 0xC1F3 },
+{ 0xC1F4, 0xC1F4, 0xC1F4 },
+{ 0xC1F5, 0xC1F5, 0xC1F5 },
+{ 0xC1F6, 0xC1F6, 0xC1F6 },
+{ 0xC1F7, 0xC1F7, 0xC1F7 },
+{ 0xC1F8, 0xC1F8, 0xC1F8 },
+{ 0xC1F9, 0xC1F9, 0xC1F9 },
+{ 0xC1FA, 0xC1FA, 0xC1FA },
+{ 0xC1FB, 0xC1FB, 0xC1FB },
+{ 0xC1FC, 0xC1FC, 0xC1FC },
+{ 0xC1FD, 0xC1FD, 0xC1FD },
+{ 0xC1FE, 0xC1FE, 0xC1FE },
+{ 0xC1FF, 0xC1FF, 0xC1FF },
+{ 0xC200, 0xC200, 0xC200 },
+{ 0xC201, 0xC201, 0xC201 },
+{ 0xC202, 0xC202, 0xC202 },
+{ 0xC203, 0xC203, 0xC203 },
+{ 0xC204, 0xC204, 0xC204 },
+{ 0xC205, 0xC205, 0xC205 },
+{ 0xC206, 0xC206, 0xC206 },
+{ 0xC207, 0xC207, 0xC207 },
+{ 0xC208, 0xC208, 0xC208 },
+{ 0xC209, 0xC209, 0xC209 },
+{ 0xC20A, 0xC20A, 0xC20A },
+{ 0xC20B, 0xC20B, 0xC20B },
+{ 0xC20C, 0xC20C, 0xC20C },
+{ 0xC20D, 0xC20D, 0xC20D },
+{ 0xC20E, 0xC20E, 0xC20E },
+{ 0xC20F, 0xC20F, 0xC20F },
+{ 0xC210, 0xC210, 0xC210 },
+{ 0xC211, 0xC211, 0xC211 },
+{ 0xC212, 0xC212, 0xC212 },
+{ 0xC213, 0xC213, 0xC213 },
+{ 0xC214, 0xC214, 0xC214 },
+{ 0xC215, 0xC215, 0xC215 },
+{ 0xC216, 0xC216, 0xC216 },
+{ 0xC217, 0xC217, 0xC217 },
+{ 0xC218, 0xC218, 0xC218 },
+{ 0xC219, 0xC219, 0xC219 },
+{ 0xC21A, 0xC21A, 0xC21A },
+{ 0xC21B, 0xC21B, 0xC21B },
+{ 0xC21C, 0xC21C, 0xC21C },
+{ 0xC21D, 0xC21D, 0xC21D },
+{ 0xC21E, 0xC21E, 0xC21E },
+{ 0xC21F, 0xC21F, 0xC21F },
+{ 0xC220, 0xC220, 0xC220 },
+{ 0xC221, 0xC221, 0xC221 },
+{ 0xC222, 0xC222, 0xC222 },
+{ 0xC223, 0xC223, 0xC223 },
+{ 0xC224, 0xC224, 0xC224 },
+{ 0xC225, 0xC225, 0xC225 },
+{ 0xC226, 0xC226, 0xC226 },
+{ 0xC227, 0xC227, 0xC227 },
+{ 0xC228, 0xC228, 0xC228 },
+{ 0xC229, 0xC229, 0xC229 },
+{ 0xC22A, 0xC22A, 0xC22A },
+{ 0xC22B, 0xC22B, 0xC22B },
+{ 0xC22C, 0xC22C, 0xC22C },
+{ 0xC22D, 0xC22D, 0xC22D },
+{ 0xC22E, 0xC22E, 0xC22E },
+{ 0xC22F, 0xC22F, 0xC22F },
+{ 0xC230, 0xC230, 0xC230 },
+{ 0xC231, 0xC231, 0xC231 },
+{ 0xC232, 0xC232, 0xC232 },
+{ 0xC233, 0xC233, 0xC233 },
+{ 0xC234, 0xC234, 0xC234 },
+{ 0xC235, 0xC235, 0xC235 },
+{ 0xC236, 0xC236, 0xC236 },
+{ 0xC237, 0xC237, 0xC237 },
+{ 0xC238, 0xC238, 0xC238 },
+{ 0xC239, 0xC239, 0xC239 },
+{ 0xC23A, 0xC23A, 0xC23A },
+{ 0xC23B, 0xC23B, 0xC23B },
+{ 0xC23C, 0xC23C, 0xC23C },
+{ 0xC23D, 0xC23D, 0xC23D },
+{ 0xC23E, 0xC23E, 0xC23E },
+{ 0xC23F, 0xC23F, 0xC23F },
+{ 0xC240, 0xC240, 0xC240 },
+{ 0xC241, 0xC241, 0xC241 },
+{ 0xC242, 0xC242, 0xC242 },
+{ 0xC243, 0xC243, 0xC243 },
+{ 0xC244, 0xC244, 0xC244 },
+{ 0xC245, 0xC245, 0xC245 },
+{ 0xC246, 0xC246, 0xC246 },
+{ 0xC247, 0xC247, 0xC247 },
+{ 0xC248, 0xC248, 0xC248 },
+{ 0xC249, 0xC249, 0xC249 },
+{ 0xC24A, 0xC24A, 0xC24A },
+{ 0xC24B, 0xC24B, 0xC24B },
+{ 0xC24C, 0xC24C, 0xC24C },
+{ 0xC24D, 0xC24D, 0xC24D },
+{ 0xC24E, 0xC24E, 0xC24E },
+{ 0xC24F, 0xC24F, 0xC24F },
+{ 0xC250, 0xC250, 0xC250 },
+{ 0xC251, 0xC251, 0xC251 },
+{ 0xC252, 0xC252, 0xC252 },
+{ 0xC253, 0xC253, 0xC253 },
+{ 0xC254, 0xC254, 0xC254 },
+{ 0xC255, 0xC255, 0xC255 },
+{ 0xC256, 0xC256, 0xC256 },
+{ 0xC257, 0xC257, 0xC257 },
+{ 0xC258, 0xC258, 0xC258 },
+{ 0xC259, 0xC259, 0xC259 },
+{ 0xC25A, 0xC25A, 0xC25A },
+{ 0xC25B, 0xC25B, 0xC25B },
+{ 0xC25C, 0xC25C, 0xC25C },
+{ 0xC25D, 0xC25D, 0xC25D },
+{ 0xC25E, 0xC25E, 0xC25E },
+{ 0xC25F, 0xC25F, 0xC25F },
+{ 0xC260, 0xC260, 0xC260 },
+{ 0xC261, 0xC261, 0xC261 },
+{ 0xC262, 0xC262, 0xC262 },
+{ 0xC263, 0xC263, 0xC263 },
+{ 0xC264, 0xC264, 0xC264 },
+{ 0xC265, 0xC265, 0xC265 },
+{ 0xC266, 0xC266, 0xC266 },
+{ 0xC267, 0xC267, 0xC267 },
+{ 0xC268, 0xC268, 0xC268 },
+{ 0xC269, 0xC269, 0xC269 },
+{ 0xC26A, 0xC26A, 0xC26A },
+{ 0xC26B, 0xC26B, 0xC26B },
+{ 0xC26C, 0xC26C, 0xC26C },
+{ 0xC26D, 0xC26D, 0xC26D },
+{ 0xC26E, 0xC26E, 0xC26E },
+{ 0xC26F, 0xC26F, 0xC26F },
+{ 0xC270, 0xC270, 0xC270 },
+{ 0xC271, 0xC271, 0xC271 },
+{ 0xC272, 0xC272, 0xC272 },
+{ 0xC273, 0xC273, 0xC273 },
+{ 0xC274, 0xC274, 0xC274 },
+{ 0xC275, 0xC275, 0xC275 },
+{ 0xC276, 0xC276, 0xC276 },
+{ 0xC277, 0xC277, 0xC277 },
+{ 0xC278, 0xC278, 0xC278 },
+{ 0xC279, 0xC279, 0xC279 },
+{ 0xC27A, 0xC27A, 0xC27A },
+{ 0xC27B, 0xC27B, 0xC27B },
+{ 0xC27C, 0xC27C, 0xC27C },
+{ 0xC27D, 0xC27D, 0xC27D },
+{ 0xC27E, 0xC27E, 0xC27E },
+{ 0xC27F, 0xC27F, 0xC27F },
+{ 0xC280, 0xC280, 0xC280 },
+{ 0xC281, 0xC281, 0xC281 },
+{ 0xC282, 0xC282, 0xC282 },
+{ 0xC283, 0xC283, 0xC283 },
+{ 0xC284, 0xC284, 0xC284 },
+{ 0xC285, 0xC285, 0xC285 },
+{ 0xC286, 0xC286, 0xC286 },
+{ 0xC287, 0xC287, 0xC287 },
+{ 0xC288, 0xC288, 0xC288 },
+{ 0xC289, 0xC289, 0xC289 },
+{ 0xC28A, 0xC28A, 0xC28A },
+{ 0xC28B, 0xC28B, 0xC28B },
+{ 0xC28C, 0xC28C, 0xC28C },
+{ 0xC28D, 0xC28D, 0xC28D },
+{ 0xC28E, 0xC28E, 0xC28E },
+{ 0xC28F, 0xC28F, 0xC28F },
+{ 0xC290, 0xC290, 0xC290 },
+{ 0xC291, 0xC291, 0xC291 },
+{ 0xC292, 0xC292, 0xC292 },
+{ 0xC293, 0xC293, 0xC293 },
+{ 0xC294, 0xC294, 0xC294 },
+{ 0xC295, 0xC295, 0xC295 },
+{ 0xC296, 0xC296, 0xC296 },
+{ 0xC297, 0xC297, 0xC297 },
+{ 0xC298, 0xC298, 0xC298 },
+{ 0xC299, 0xC299, 0xC299 },
+{ 0xC29A, 0xC29A, 0xC29A },
+{ 0xC29B, 0xC29B, 0xC29B },
+{ 0xC29C, 0xC29C, 0xC29C },
+{ 0xC29D, 0xC29D, 0xC29D },
+{ 0xC29E, 0xC29E, 0xC29E },
+{ 0xC29F, 0xC29F, 0xC29F },
+{ 0xC2A0, 0xC2A0, 0xC2A0 },
+{ 0xC2A1, 0xC2A1, 0xC2A1 },
+{ 0xC2A2, 0xC2A2, 0xC2A2 },
+{ 0xC2A3, 0xC2A3, 0xC2A3 },
+{ 0xC2A4, 0xC2A4, 0xC2A4 },
+{ 0xC2A5, 0xC2A5, 0xC2A5 },
+{ 0xC2A6, 0xC2A6, 0xC2A6 },
+{ 0xC2A7, 0xC2A7, 0xC2A7 },
+{ 0xC2A8, 0xC2A8, 0xC2A8 },
+{ 0xC2A9, 0xC2A9, 0xC2A9 },
+{ 0xC2AA, 0xC2AA, 0xC2AA },
+{ 0xC2AB, 0xC2AB, 0xC2AB },
+{ 0xC2AC, 0xC2AC, 0xC2AC },
+{ 0xC2AD, 0xC2AD, 0xC2AD },
+{ 0xC2AE, 0xC2AE, 0xC2AE },
+{ 0xC2AF, 0xC2AF, 0xC2AF },
+{ 0xC2B0, 0xC2B0, 0xC2B0 },
+{ 0xC2B1, 0xC2B1, 0xC2B1 },
+{ 0xC2B2, 0xC2B2, 0xC2B2 },
+{ 0xC2B3, 0xC2B3, 0xC2B3 },
+{ 0xC2B4, 0xC2B4, 0xC2B4 },
+{ 0xC2B5, 0xC2B5, 0xC2B5 },
+{ 0xC2B6, 0xC2B6, 0xC2B6 },
+{ 0xC2B7, 0xC2B7, 0xC2B7 },
+{ 0xC2B8, 0xC2B8, 0xC2B8 },
+{ 0xC2B9, 0xC2B9, 0xC2B9 },
+{ 0xC2BA, 0xC2BA, 0xC2BA },
+{ 0xC2BB, 0xC2BB, 0xC2BB },
+{ 0xC2BC, 0xC2BC, 0xC2BC },
+{ 0xC2BD, 0xC2BD, 0xC2BD },
+{ 0xC2BE, 0xC2BE, 0xC2BE },
+{ 0xC2BF, 0xC2BF, 0xC2BF },
+{ 0xC2C0, 0xC2C0, 0xC2C0 },
+{ 0xC2C1, 0xC2C1, 0xC2C1 },
+{ 0xC2C2, 0xC2C2, 0xC2C2 },
+{ 0xC2C3, 0xC2C3, 0xC2C3 },
+{ 0xC2C4, 0xC2C4, 0xC2C4 },
+{ 0xC2C5, 0xC2C5, 0xC2C5 },
+{ 0xC2C6, 0xC2C6, 0xC2C6 },
+{ 0xC2C7, 0xC2C7, 0xC2C7 },
+{ 0xC2C8, 0xC2C8, 0xC2C8 },
+{ 0xC2C9, 0xC2C9, 0xC2C9 },
+{ 0xC2CA, 0xC2CA, 0xC2CA },
+{ 0xC2CB, 0xC2CB, 0xC2CB },
+{ 0xC2CC, 0xC2CC, 0xC2CC },
+{ 0xC2CD, 0xC2CD, 0xC2CD },
+{ 0xC2CE, 0xC2CE, 0xC2CE },
+{ 0xC2CF, 0xC2CF, 0xC2CF },
+{ 0xC2D0, 0xC2D0, 0xC2D0 },
+{ 0xC2D1, 0xC2D1, 0xC2D1 },
+{ 0xC2D2, 0xC2D2, 0xC2D2 },
+{ 0xC2D3, 0xC2D3, 0xC2D3 },
+{ 0xC2D4, 0xC2D4, 0xC2D4 },
+{ 0xC2D5, 0xC2D5, 0xC2D5 },
+{ 0xC2D6, 0xC2D6, 0xC2D6 },
+{ 0xC2D7, 0xC2D7, 0xC2D7 },
+{ 0xC2D8, 0xC2D8, 0xC2D8 },
+{ 0xC2D9, 0xC2D9, 0xC2D9 },
+{ 0xC2DA, 0xC2DA, 0xC2DA },
+{ 0xC2DB, 0xC2DB, 0xC2DB },
+{ 0xC2DC, 0xC2DC, 0xC2DC },
+{ 0xC2DD, 0xC2DD, 0xC2DD },
+{ 0xC2DE, 0xC2DE, 0xC2DE },
+{ 0xC2DF, 0xC2DF, 0xC2DF },
+{ 0xC2E0, 0xC2E0, 0xC2E0 },
+{ 0xC2E1, 0xC2E1, 0xC2E1 },
+{ 0xC2E2, 0xC2E2, 0xC2E2 },
+{ 0xC2E3, 0xC2E3, 0xC2E3 },
+{ 0xC2E4, 0xC2E4, 0xC2E4 },
+{ 0xC2E5, 0xC2E5, 0xC2E5 },
+{ 0xC2E6, 0xC2E6, 0xC2E6 },
+{ 0xC2E7, 0xC2E7, 0xC2E7 },
+{ 0xC2E8, 0xC2E8, 0xC2E8 },
+{ 0xC2E9, 0xC2E9, 0xC2E9 },
+{ 0xC2EA, 0xC2EA, 0xC2EA },
+{ 0xC2EB, 0xC2EB, 0xC2EB },
+{ 0xC2EC, 0xC2EC, 0xC2EC },
+{ 0xC2ED, 0xC2ED, 0xC2ED },
+{ 0xC2EE, 0xC2EE, 0xC2EE },
+{ 0xC2EF, 0xC2EF, 0xC2EF },
+{ 0xC2F0, 0xC2F0, 0xC2F0 },
+{ 0xC2F1, 0xC2F1, 0xC2F1 },
+{ 0xC2F2, 0xC2F2, 0xC2F2 },
+{ 0xC2F3, 0xC2F3, 0xC2F3 },
+{ 0xC2F4, 0xC2F4, 0xC2F4 },
+{ 0xC2F5, 0xC2F5, 0xC2F5 },
+{ 0xC2F6, 0xC2F6, 0xC2F6 },
+{ 0xC2F7, 0xC2F7, 0xC2F7 },
+{ 0xC2F8, 0xC2F8, 0xC2F8 },
+{ 0xC2F9, 0xC2F9, 0xC2F9 },
+{ 0xC2FA, 0xC2FA, 0xC2FA },
+{ 0xC2FB, 0xC2FB, 0xC2FB },
+{ 0xC2FC, 0xC2FC, 0xC2FC },
+{ 0xC2FD, 0xC2FD, 0xC2FD },
+{ 0xC2FE, 0xC2FE, 0xC2FE },
+{ 0xC2FF, 0xC2FF, 0xC2FF },
+{ 0xC300, 0xC300, 0xC300 },
+{ 0xC301, 0xC301, 0xC301 },
+{ 0xC302, 0xC302, 0xC302 },
+{ 0xC303, 0xC303, 0xC303 },
+{ 0xC304, 0xC304, 0xC304 },
+{ 0xC305, 0xC305, 0xC305 },
+{ 0xC306, 0xC306, 0xC306 },
+{ 0xC307, 0xC307, 0xC307 },
+{ 0xC308, 0xC308, 0xC308 },
+{ 0xC309, 0xC309, 0xC309 },
+{ 0xC30A, 0xC30A, 0xC30A },
+{ 0xC30B, 0xC30B, 0xC30B },
+{ 0xC30C, 0xC30C, 0xC30C },
+{ 0xC30D, 0xC30D, 0xC30D },
+{ 0xC30E, 0xC30E, 0xC30E },
+{ 0xC30F, 0xC30F, 0xC30F },
+{ 0xC310, 0xC310, 0xC310 },
+{ 0xC311, 0xC311, 0xC311 },
+{ 0xC312, 0xC312, 0xC312 },
+{ 0xC313, 0xC313, 0xC313 },
+{ 0xC314, 0xC314, 0xC314 },
+{ 0xC315, 0xC315, 0xC315 },
+{ 0xC316, 0xC316, 0xC316 },
+{ 0xC317, 0xC317, 0xC317 },
+{ 0xC318, 0xC318, 0xC318 },
+{ 0xC319, 0xC319, 0xC319 },
+{ 0xC31A, 0xC31A, 0xC31A },
+{ 0xC31B, 0xC31B, 0xC31B },
+{ 0xC31C, 0xC31C, 0xC31C },
+{ 0xC31D, 0xC31D, 0xC31D },
+{ 0xC31E, 0xC31E, 0xC31E },
+{ 0xC31F, 0xC31F, 0xC31F },
+{ 0xC320, 0xC320, 0xC320 },
+{ 0xC321, 0xC321, 0xC321 },
+{ 0xC322, 0xC322, 0xC322 },
+{ 0xC323, 0xC323, 0xC323 },
+{ 0xC324, 0xC324, 0xC324 },
+{ 0xC325, 0xC325, 0xC325 },
+{ 0xC326, 0xC326, 0xC326 },
+{ 0xC327, 0xC327, 0xC327 },
+{ 0xC328, 0xC328, 0xC328 },
+{ 0xC329, 0xC329, 0xC329 },
+{ 0xC32A, 0xC32A, 0xC32A },
+{ 0xC32B, 0xC32B, 0xC32B },
+{ 0xC32C, 0xC32C, 0xC32C },
+{ 0xC32D, 0xC32D, 0xC32D },
+{ 0xC32E, 0xC32E, 0xC32E },
+{ 0xC32F, 0xC32F, 0xC32F },
+{ 0xC330, 0xC330, 0xC330 },
+{ 0xC331, 0xC331, 0xC331 },
+{ 0xC332, 0xC332, 0xC332 },
+{ 0xC333, 0xC333, 0xC333 },
+{ 0xC334, 0xC334, 0xC334 },
+{ 0xC335, 0xC335, 0xC335 },
+{ 0xC336, 0xC336, 0xC336 },
+{ 0xC337, 0xC337, 0xC337 },
+{ 0xC338, 0xC338, 0xC338 },
+{ 0xC339, 0xC339, 0xC339 },
+{ 0xC33A, 0xC33A, 0xC33A },
+{ 0xC33B, 0xC33B, 0xC33B },
+{ 0xC33C, 0xC33C, 0xC33C },
+{ 0xC33D, 0xC33D, 0xC33D },
+{ 0xC33E, 0xC33E, 0xC33E },
+{ 0xC33F, 0xC33F, 0xC33F },
+{ 0xC340, 0xC340, 0xC340 },
+{ 0xC341, 0xC341, 0xC341 },
+{ 0xC342, 0xC342, 0xC342 },
+{ 0xC343, 0xC343, 0xC343 },
+{ 0xC344, 0xC344, 0xC344 },
+{ 0xC345, 0xC345, 0xC345 },
+{ 0xC346, 0xC346, 0xC346 },
+{ 0xC347, 0xC347, 0xC347 },
+{ 0xC348, 0xC348, 0xC348 },
+{ 0xC349, 0xC349, 0xC349 },
+{ 0xC34A, 0xC34A, 0xC34A },
+{ 0xC34B, 0xC34B, 0xC34B },
+{ 0xC34C, 0xC34C, 0xC34C },
+{ 0xC34D, 0xC34D, 0xC34D },
+{ 0xC34E, 0xC34E, 0xC34E },
+{ 0xC34F, 0xC34F, 0xC34F },
+{ 0xC350, 0xC350, 0xC350 },
+{ 0xC351, 0xC351, 0xC351 },
+{ 0xC352, 0xC352, 0xC352 },
+{ 0xC353, 0xC353, 0xC353 },
+{ 0xC354, 0xC354, 0xC354 },
+{ 0xC355, 0xC355, 0xC355 },
+{ 0xC356, 0xC356, 0xC356 },
+{ 0xC357, 0xC357, 0xC357 },
+{ 0xC358, 0xC358, 0xC358 },
+{ 0xC359, 0xC359, 0xC359 },
+{ 0xC35A, 0xC35A, 0xC35A },
+{ 0xC35B, 0xC35B, 0xC35B },
+{ 0xC35C, 0xC35C, 0xC35C },
+{ 0xC35D, 0xC35D, 0xC35D },
+{ 0xC35E, 0xC35E, 0xC35E },
+{ 0xC35F, 0xC35F, 0xC35F },
+{ 0xC360, 0xC360, 0xC360 },
+{ 0xC361, 0xC361, 0xC361 },
+{ 0xC362, 0xC362, 0xC362 },
+{ 0xC363, 0xC363, 0xC363 },
+{ 0xC364, 0xC364, 0xC364 },
+{ 0xC365, 0xC365, 0xC365 },
+{ 0xC366, 0xC366, 0xC366 },
+{ 0xC367, 0xC367, 0xC367 },
+{ 0xC368, 0xC368, 0xC368 },
+{ 0xC369, 0xC369, 0xC369 },
+{ 0xC36A, 0xC36A, 0xC36A },
+{ 0xC36B, 0xC36B, 0xC36B },
+{ 0xC36C, 0xC36C, 0xC36C },
+{ 0xC36D, 0xC36D, 0xC36D },
+{ 0xC36E, 0xC36E, 0xC36E },
+{ 0xC36F, 0xC36F, 0xC36F },
+{ 0xC370, 0xC370, 0xC370 },
+{ 0xC371, 0xC371, 0xC371 },
+{ 0xC372, 0xC372, 0xC372 },
+{ 0xC373, 0xC373, 0xC373 },
+{ 0xC374, 0xC374, 0xC374 },
+{ 0xC375, 0xC375, 0xC375 },
+{ 0xC376, 0xC376, 0xC376 },
+{ 0xC377, 0xC377, 0xC377 },
+{ 0xC378, 0xC378, 0xC378 },
+{ 0xC379, 0xC379, 0xC379 },
+{ 0xC37A, 0xC37A, 0xC37A },
+{ 0xC37B, 0xC37B, 0xC37B },
+{ 0xC37C, 0xC37C, 0xC37C },
+{ 0xC37D, 0xC37D, 0xC37D },
+{ 0xC37E, 0xC37E, 0xC37E },
+{ 0xC37F, 0xC37F, 0xC37F },
+{ 0xC380, 0xC380, 0xC380 },
+{ 0xC381, 0xC381, 0xC381 },
+{ 0xC382, 0xC382, 0xC382 },
+{ 0xC383, 0xC383, 0xC383 },
+{ 0xC384, 0xC384, 0xC384 },
+{ 0xC385, 0xC385, 0xC385 },
+{ 0xC386, 0xC386, 0xC386 },
+{ 0xC387, 0xC387, 0xC387 },
+{ 0xC388, 0xC388, 0xC388 },
+{ 0xC389, 0xC389, 0xC389 },
+{ 0xC38A, 0xC38A, 0xC38A },
+{ 0xC38B, 0xC38B, 0xC38B },
+{ 0xC38C, 0xC38C, 0xC38C },
+{ 0xC38D, 0xC38D, 0xC38D },
+{ 0xC38E, 0xC38E, 0xC38E },
+{ 0xC38F, 0xC38F, 0xC38F },
+{ 0xC390, 0xC390, 0xC390 },
+{ 0xC391, 0xC391, 0xC391 },
+{ 0xC392, 0xC392, 0xC392 },
+{ 0xC393, 0xC393, 0xC393 },
+{ 0xC394, 0xC394, 0xC394 },
+{ 0xC395, 0xC395, 0xC395 },
+{ 0xC396, 0xC396, 0xC396 },
+{ 0xC397, 0xC397, 0xC397 },
+{ 0xC398, 0xC398, 0xC398 },
+{ 0xC399, 0xC399, 0xC399 },
+{ 0xC39A, 0xC39A, 0xC39A },
+{ 0xC39B, 0xC39B, 0xC39B },
+{ 0xC39C, 0xC39C, 0xC39C },
+{ 0xC39D, 0xC39D, 0xC39D },
+{ 0xC39E, 0xC39E, 0xC39E },
+{ 0xC39F, 0xC39F, 0xC39F },
+{ 0xC3A0, 0xC3A0, 0xC3A0 },
+{ 0xC3A1, 0xC3A1, 0xC3A1 },
+{ 0xC3A2, 0xC3A2, 0xC3A2 },
+{ 0xC3A3, 0xC3A3, 0xC3A3 },
+{ 0xC3A4, 0xC3A4, 0xC3A4 },
+{ 0xC3A5, 0xC3A5, 0xC3A5 },
+{ 0xC3A6, 0xC3A6, 0xC3A6 },
+{ 0xC3A7, 0xC3A7, 0xC3A7 },
+{ 0xC3A8, 0xC3A8, 0xC3A8 },
+{ 0xC3A9, 0xC3A9, 0xC3A9 },
+{ 0xC3AA, 0xC3AA, 0xC3AA },
+{ 0xC3AB, 0xC3AB, 0xC3AB },
+{ 0xC3AC, 0xC3AC, 0xC3AC },
+{ 0xC3AD, 0xC3AD, 0xC3AD },
+{ 0xC3AE, 0xC3AE, 0xC3AE },
+{ 0xC3AF, 0xC3AF, 0xC3AF },
+{ 0xC3B0, 0xC3B0, 0xC3B0 },
+{ 0xC3B1, 0xC3B1, 0xC3B1 },
+{ 0xC3B2, 0xC3B2, 0xC3B2 },
+{ 0xC3B3, 0xC3B3, 0xC3B3 },
+{ 0xC3B4, 0xC3B4, 0xC3B4 },
+{ 0xC3B5, 0xC3B5, 0xC3B5 },
+{ 0xC3B6, 0xC3B6, 0xC3B6 },
+{ 0xC3B7, 0xC3B7, 0xC3B7 },
+{ 0xC3B8, 0xC3B8, 0xC3B8 },
+{ 0xC3B9, 0xC3B9, 0xC3B9 },
+{ 0xC3BA, 0xC3BA, 0xC3BA },
+{ 0xC3BB, 0xC3BB, 0xC3BB },
+{ 0xC3BC, 0xC3BC, 0xC3BC },
+{ 0xC3BD, 0xC3BD, 0xC3BD },
+{ 0xC3BE, 0xC3BE, 0xC3BE },
+{ 0xC3BF, 0xC3BF, 0xC3BF },
+{ 0xC3C0, 0xC3C0, 0xC3C0 },
+{ 0xC3C1, 0xC3C1, 0xC3C1 },
+{ 0xC3C2, 0xC3C2, 0xC3C2 },
+{ 0xC3C3, 0xC3C3, 0xC3C3 },
+{ 0xC3C4, 0xC3C4, 0xC3C4 },
+{ 0xC3C5, 0xC3C5, 0xC3C5 },
+{ 0xC3C6, 0xC3C6, 0xC3C6 },
+{ 0xC3C7, 0xC3C7, 0xC3C7 },
+{ 0xC3C8, 0xC3C8, 0xC3C8 },
+{ 0xC3C9, 0xC3C9, 0xC3C9 },
+{ 0xC3CA, 0xC3CA, 0xC3CA },
+{ 0xC3CB, 0xC3CB, 0xC3CB },
+{ 0xC3CC, 0xC3CC, 0xC3CC },
+{ 0xC3CD, 0xC3CD, 0xC3CD },
+{ 0xC3CE, 0xC3CE, 0xC3CE },
+{ 0xC3CF, 0xC3CF, 0xC3CF },
+{ 0xC3D0, 0xC3D0, 0xC3D0 },
+{ 0xC3D1, 0xC3D1, 0xC3D1 },
+{ 0xC3D2, 0xC3D2, 0xC3D2 },
+{ 0xC3D3, 0xC3D3, 0xC3D3 },
+{ 0xC3D4, 0xC3D4, 0xC3D4 },
+{ 0xC3D5, 0xC3D5, 0xC3D5 },
+{ 0xC3D6, 0xC3D6, 0xC3D6 },
+{ 0xC3D7, 0xC3D7, 0xC3D7 },
+{ 0xC3D8, 0xC3D8, 0xC3D8 },
+{ 0xC3D9, 0xC3D9, 0xC3D9 },
+{ 0xC3DA, 0xC3DA, 0xC3DA },
+{ 0xC3DB, 0xC3DB, 0xC3DB },
+{ 0xC3DC, 0xC3DC, 0xC3DC },
+{ 0xC3DD, 0xC3DD, 0xC3DD },
+{ 0xC3DE, 0xC3DE, 0xC3DE },
+{ 0xC3DF, 0xC3DF, 0xC3DF },
+{ 0xC3E0, 0xC3E0, 0xC3E0 },
+{ 0xC3E1, 0xC3E1, 0xC3E1 },
+{ 0xC3E2, 0xC3E2, 0xC3E2 },
+{ 0xC3E3, 0xC3E3, 0xC3E3 },
+{ 0xC3E4, 0xC3E4, 0xC3E4 },
+{ 0xC3E5, 0xC3E5, 0xC3E5 },
+{ 0xC3E6, 0xC3E6, 0xC3E6 },
+{ 0xC3E7, 0xC3E7, 0xC3E7 },
+{ 0xC3E8, 0xC3E8, 0xC3E8 },
+{ 0xC3E9, 0xC3E9, 0xC3E9 },
+{ 0xC3EA, 0xC3EA, 0xC3EA },
+{ 0xC3EB, 0xC3EB, 0xC3EB },
+{ 0xC3EC, 0xC3EC, 0xC3EC },
+{ 0xC3ED, 0xC3ED, 0xC3ED },
+{ 0xC3EE, 0xC3EE, 0xC3EE },
+{ 0xC3EF, 0xC3EF, 0xC3EF },
+{ 0xC3F0, 0xC3F0, 0xC3F0 },
+{ 0xC3F1, 0xC3F1, 0xC3F1 },
+{ 0xC3F2, 0xC3F2, 0xC3F2 },
+{ 0xC3F3, 0xC3F3, 0xC3F3 },
+{ 0xC3F4, 0xC3F4, 0xC3F4 },
+{ 0xC3F5, 0xC3F5, 0xC3F5 },
+{ 0xC3F6, 0xC3F6, 0xC3F6 },
+{ 0xC3F7, 0xC3F7, 0xC3F7 },
+{ 0xC3F8, 0xC3F8, 0xC3F8 },
+{ 0xC3F9, 0xC3F9, 0xC3F9 },
+{ 0xC3FA, 0xC3FA, 0xC3FA },
+{ 0xC3FB, 0xC3FB, 0xC3FB },
+{ 0xC3FC, 0xC3FC, 0xC3FC },
+{ 0xC3FD, 0xC3FD, 0xC3FD },
+{ 0xC3FE, 0xC3FE, 0xC3FE },
+{ 0xC3FF, 0xC3FF, 0xC3FF },
+{ 0xC400, 0xC400, 0xC400 },
+{ 0xC401, 0xC401, 0xC401 },
+{ 0xC402, 0xC402, 0xC402 },
+{ 0xC403, 0xC403, 0xC403 },
+{ 0xC404, 0xC404, 0xC404 },
+{ 0xC405, 0xC405, 0xC405 },
+{ 0xC406, 0xC406, 0xC406 },
+{ 0xC407, 0xC407, 0xC407 },
+{ 0xC408, 0xC408, 0xC408 },
+{ 0xC409, 0xC409, 0xC409 },
+{ 0xC40A, 0xC40A, 0xC40A },
+{ 0xC40B, 0xC40B, 0xC40B },
+{ 0xC40C, 0xC40C, 0xC40C },
+{ 0xC40D, 0xC40D, 0xC40D },
+{ 0xC40E, 0xC40E, 0xC40E },
+{ 0xC40F, 0xC40F, 0xC40F },
+{ 0xC410, 0xC410, 0xC410 },
+{ 0xC411, 0xC411, 0xC411 },
+{ 0xC412, 0xC412, 0xC412 },
+{ 0xC413, 0xC413, 0xC413 },
+{ 0xC414, 0xC414, 0xC414 },
+{ 0xC415, 0xC415, 0xC415 },
+{ 0xC416, 0xC416, 0xC416 },
+{ 0xC417, 0xC417, 0xC417 },
+{ 0xC418, 0xC418, 0xC418 },
+{ 0xC419, 0xC419, 0xC419 },
+{ 0xC41A, 0xC41A, 0xC41A },
+{ 0xC41B, 0xC41B, 0xC41B },
+{ 0xC41C, 0xC41C, 0xC41C },
+{ 0xC41D, 0xC41D, 0xC41D },
+{ 0xC41E, 0xC41E, 0xC41E },
+{ 0xC41F, 0xC41F, 0xC41F },
+{ 0xC420, 0xC420, 0xC420 },
+{ 0xC421, 0xC421, 0xC421 },
+{ 0xC422, 0xC422, 0xC422 },
+{ 0xC423, 0xC423, 0xC423 },
+{ 0xC424, 0xC424, 0xC424 },
+{ 0xC425, 0xC425, 0xC425 },
+{ 0xC426, 0xC426, 0xC426 },
+{ 0xC427, 0xC427, 0xC427 },
+{ 0xC428, 0xC428, 0xC428 },
+{ 0xC429, 0xC429, 0xC429 },
+{ 0xC42A, 0xC42A, 0xC42A },
+{ 0xC42B, 0xC42B, 0xC42B },
+{ 0xC42C, 0xC42C, 0xC42C },
+{ 0xC42D, 0xC42D, 0xC42D },
+{ 0xC42E, 0xC42E, 0xC42E },
+{ 0xC42F, 0xC42F, 0xC42F },
+{ 0xC430, 0xC430, 0xC430 },
+{ 0xC431, 0xC431, 0xC431 },
+{ 0xC432, 0xC432, 0xC432 },
+{ 0xC433, 0xC433, 0xC433 },
+{ 0xC434, 0xC434, 0xC434 },
+{ 0xC435, 0xC435, 0xC435 },
+{ 0xC436, 0xC436, 0xC436 },
+{ 0xC437, 0xC437, 0xC437 },
+{ 0xC438, 0xC438, 0xC438 },
+{ 0xC439, 0xC439, 0xC439 },
+{ 0xC43A, 0xC43A, 0xC43A },
+{ 0xC43B, 0xC43B, 0xC43B },
+{ 0xC43C, 0xC43C, 0xC43C },
+{ 0xC43D, 0xC43D, 0xC43D },
+{ 0xC43E, 0xC43E, 0xC43E },
+{ 0xC43F, 0xC43F, 0xC43F },
+{ 0xC440, 0xC440, 0xC440 },
+{ 0xC441, 0xC441, 0xC441 },
+{ 0xC442, 0xC442, 0xC442 },
+{ 0xC443, 0xC443, 0xC443 },
+{ 0xC444, 0xC444, 0xC444 },
+{ 0xC445, 0xC445, 0xC445 },
+{ 0xC446, 0xC446, 0xC446 },
+{ 0xC447, 0xC447, 0xC447 },
+{ 0xC448, 0xC448, 0xC448 },
+{ 0xC449, 0xC449, 0xC449 },
+{ 0xC44A, 0xC44A, 0xC44A },
+{ 0xC44B, 0xC44B, 0xC44B },
+{ 0xC44C, 0xC44C, 0xC44C },
+{ 0xC44D, 0xC44D, 0xC44D },
+{ 0xC44E, 0xC44E, 0xC44E },
+{ 0xC44F, 0xC44F, 0xC44F },
+{ 0xC450, 0xC450, 0xC450 },
+{ 0xC451, 0xC451, 0xC451 },
+{ 0xC452, 0xC452, 0xC452 },
+{ 0xC453, 0xC453, 0xC453 },
+{ 0xC454, 0xC454, 0xC454 },
+{ 0xC455, 0xC455, 0xC455 },
+{ 0xC456, 0xC456, 0xC456 },
+{ 0xC457, 0xC457, 0xC457 },
+{ 0xC458, 0xC458, 0xC458 },
+{ 0xC459, 0xC459, 0xC459 },
+{ 0xC45A, 0xC45A, 0xC45A },
+{ 0xC45B, 0xC45B, 0xC45B },
+{ 0xC45C, 0xC45C, 0xC45C },
+{ 0xC45D, 0xC45D, 0xC45D },
+{ 0xC45E, 0xC45E, 0xC45E },
+{ 0xC45F, 0xC45F, 0xC45F },
+{ 0xC460, 0xC460, 0xC460 },
+{ 0xC461, 0xC461, 0xC461 },
+{ 0xC462, 0xC462, 0xC462 },
+{ 0xC463, 0xC463, 0xC463 },
+{ 0xC464, 0xC464, 0xC464 },
+{ 0xC465, 0xC465, 0xC465 },
+{ 0xC466, 0xC466, 0xC466 },
+{ 0xC467, 0xC467, 0xC467 },
+{ 0xC468, 0xC468, 0xC468 },
+{ 0xC469, 0xC469, 0xC469 },
+{ 0xC46A, 0xC46A, 0xC46A },
+{ 0xC46B, 0xC46B, 0xC46B },
+{ 0xC46C, 0xC46C, 0xC46C },
+{ 0xC46D, 0xC46D, 0xC46D },
+{ 0xC46E, 0xC46E, 0xC46E },
+{ 0xC46F, 0xC46F, 0xC46F },
+{ 0xC470, 0xC470, 0xC470 },
+{ 0xC471, 0xC471, 0xC471 },
+{ 0xC472, 0xC472, 0xC472 },
+{ 0xC473, 0xC473, 0xC473 },
+{ 0xC474, 0xC474, 0xC474 },
+{ 0xC475, 0xC475, 0xC475 },
+{ 0xC476, 0xC476, 0xC476 },
+{ 0xC477, 0xC477, 0xC477 },
+{ 0xC478, 0xC478, 0xC478 },
+{ 0xC479, 0xC479, 0xC479 },
+{ 0xC47A, 0xC47A, 0xC47A },
+{ 0xC47B, 0xC47B, 0xC47B },
+{ 0xC47C, 0xC47C, 0xC47C },
+{ 0xC47D, 0xC47D, 0xC47D },
+{ 0xC47E, 0xC47E, 0xC47E },
+{ 0xC47F, 0xC47F, 0xC47F },
+{ 0xC480, 0xC480, 0xC480 },
+{ 0xC481, 0xC481, 0xC481 },
+{ 0xC482, 0xC482, 0xC482 },
+{ 0xC483, 0xC483, 0xC483 },
+{ 0xC484, 0xC484, 0xC484 },
+{ 0xC485, 0xC485, 0xC485 },
+{ 0xC486, 0xC486, 0xC486 },
+{ 0xC487, 0xC487, 0xC487 },
+{ 0xC488, 0xC488, 0xC488 },
+{ 0xC489, 0xC489, 0xC489 },
+{ 0xC48A, 0xC48A, 0xC48A },
+{ 0xC48B, 0xC48B, 0xC48B },
+{ 0xC48C, 0xC48C, 0xC48C },
+{ 0xC48D, 0xC48D, 0xC48D },
+{ 0xC48E, 0xC48E, 0xC48E },
+{ 0xC48F, 0xC48F, 0xC48F },
+{ 0xC490, 0xC490, 0xC490 },
+{ 0xC491, 0xC491, 0xC491 },
+{ 0xC492, 0xC492, 0xC492 },
+{ 0xC493, 0xC493, 0xC493 },
+{ 0xC494, 0xC494, 0xC494 },
+{ 0xC495, 0xC495, 0xC495 },
+{ 0xC496, 0xC496, 0xC496 },
+{ 0xC497, 0xC497, 0xC497 },
+{ 0xC498, 0xC498, 0xC498 },
+{ 0xC499, 0xC499, 0xC499 },
+{ 0xC49A, 0xC49A, 0xC49A },
+{ 0xC49B, 0xC49B, 0xC49B },
+{ 0xC49C, 0xC49C, 0xC49C },
+{ 0xC49D, 0xC49D, 0xC49D },
+{ 0xC49E, 0xC49E, 0xC49E },
+{ 0xC49F, 0xC49F, 0xC49F },
+{ 0xC4A0, 0xC4A0, 0xC4A0 },
+{ 0xC4A1, 0xC4A1, 0xC4A1 },
+{ 0xC4A2, 0xC4A2, 0xC4A2 },
+{ 0xC4A3, 0xC4A3, 0xC4A3 },
+{ 0xC4A4, 0xC4A4, 0xC4A4 },
+{ 0xC4A5, 0xC4A5, 0xC4A5 },
+{ 0xC4A6, 0xC4A6, 0xC4A6 },
+{ 0xC4A7, 0xC4A7, 0xC4A7 },
+{ 0xC4A8, 0xC4A8, 0xC4A8 },
+{ 0xC4A9, 0xC4A9, 0xC4A9 },
+{ 0xC4AA, 0xC4AA, 0xC4AA },
+{ 0xC4AB, 0xC4AB, 0xC4AB },
+{ 0xC4AC, 0xC4AC, 0xC4AC },
+{ 0xC4AD, 0xC4AD, 0xC4AD },
+{ 0xC4AE, 0xC4AE, 0xC4AE },
+{ 0xC4AF, 0xC4AF, 0xC4AF },
+{ 0xC4B0, 0xC4B0, 0xC4B0 },
+{ 0xC4B1, 0xC4B1, 0xC4B1 },
+{ 0xC4B2, 0xC4B2, 0xC4B2 },
+{ 0xC4B3, 0xC4B3, 0xC4B3 },
+{ 0xC4B4, 0xC4B4, 0xC4B4 },
+{ 0xC4B5, 0xC4B5, 0xC4B5 },
+{ 0xC4B6, 0xC4B6, 0xC4B6 },
+{ 0xC4B7, 0xC4B7, 0xC4B7 },
+{ 0xC4B8, 0xC4B8, 0xC4B8 },
+{ 0xC4B9, 0xC4B9, 0xC4B9 },
+{ 0xC4BA, 0xC4BA, 0xC4BA },
+{ 0xC4BB, 0xC4BB, 0xC4BB },
+{ 0xC4BC, 0xC4BC, 0xC4BC },
+{ 0xC4BD, 0xC4BD, 0xC4BD },
+{ 0xC4BE, 0xC4BE, 0xC4BE },
+{ 0xC4BF, 0xC4BF, 0xC4BF },
+{ 0xC4C0, 0xC4C0, 0xC4C0 },
+{ 0xC4C1, 0xC4C1, 0xC4C1 },
+{ 0xC4C2, 0xC4C2, 0xC4C2 },
+{ 0xC4C3, 0xC4C3, 0xC4C3 },
+{ 0xC4C4, 0xC4C4, 0xC4C4 },
+{ 0xC4C5, 0xC4C5, 0xC4C5 },
+{ 0xC4C6, 0xC4C6, 0xC4C6 },
+{ 0xC4C7, 0xC4C7, 0xC4C7 },
+{ 0xC4C8, 0xC4C8, 0xC4C8 },
+{ 0xC4C9, 0xC4C9, 0xC4C9 },
+{ 0xC4CA, 0xC4CA, 0xC4CA },
+{ 0xC4CB, 0xC4CB, 0xC4CB },
+{ 0xC4CC, 0xC4CC, 0xC4CC },
+{ 0xC4CD, 0xC4CD, 0xC4CD },
+{ 0xC4CE, 0xC4CE, 0xC4CE },
+{ 0xC4CF, 0xC4CF, 0xC4CF },
+{ 0xC4D0, 0xC4D0, 0xC4D0 },
+{ 0xC4D1, 0xC4D1, 0xC4D1 },
+{ 0xC4D2, 0xC4D2, 0xC4D2 },
+{ 0xC4D3, 0xC4D3, 0xC4D3 },
+{ 0xC4D4, 0xC4D4, 0xC4D4 },
+{ 0xC4D5, 0xC4D5, 0xC4D5 },
+{ 0xC4D6, 0xC4D6, 0xC4D6 },
+{ 0xC4D7, 0xC4D7, 0xC4D7 },
+{ 0xC4D8, 0xC4D8, 0xC4D8 },
+{ 0xC4D9, 0xC4D9, 0xC4D9 },
+{ 0xC4DA, 0xC4DA, 0xC4DA },
+{ 0xC4DB, 0xC4DB, 0xC4DB },
+{ 0xC4DC, 0xC4DC, 0xC4DC },
+{ 0xC4DD, 0xC4DD, 0xC4DD },
+{ 0xC4DE, 0xC4DE, 0xC4DE },
+{ 0xC4DF, 0xC4DF, 0xC4DF },
+{ 0xC4E0, 0xC4E0, 0xC4E0 },
+{ 0xC4E1, 0xC4E1, 0xC4E1 },
+{ 0xC4E2, 0xC4E2, 0xC4E2 },
+{ 0xC4E3, 0xC4E3, 0xC4E3 },
+{ 0xC4E4, 0xC4E4, 0xC4E4 },
+{ 0xC4E5, 0xC4E5, 0xC4E5 },
+{ 0xC4E6, 0xC4E6, 0xC4E6 },
+{ 0xC4E7, 0xC4E7, 0xC4E7 },
+{ 0xC4E8, 0xC4E8, 0xC4E8 },
+{ 0xC4E9, 0xC4E9, 0xC4E9 },
+{ 0xC4EA, 0xC4EA, 0xC4EA },
+{ 0xC4EB, 0xC4EB, 0xC4EB },
+{ 0xC4EC, 0xC4EC, 0xC4EC },
+{ 0xC4ED, 0xC4ED, 0xC4ED },
+{ 0xC4EE, 0xC4EE, 0xC4EE },
+{ 0xC4EF, 0xC4EF, 0xC4EF },
+{ 0xC4F0, 0xC4F0, 0xC4F0 },
+{ 0xC4F1, 0xC4F1, 0xC4F1 },
+{ 0xC4F2, 0xC4F2, 0xC4F2 },
+{ 0xC4F3, 0xC4F3, 0xC4F3 },
+{ 0xC4F4, 0xC4F4, 0xC4F4 },
+{ 0xC4F5, 0xC4F5, 0xC4F5 },
+{ 0xC4F6, 0xC4F6, 0xC4F6 },
+{ 0xC4F7, 0xC4F7, 0xC4F7 },
+{ 0xC4F8, 0xC4F8, 0xC4F8 },
+{ 0xC4F9, 0xC4F9, 0xC4F9 },
+{ 0xC4FA, 0xC4FA, 0xC4FA },
+{ 0xC4FB, 0xC4FB, 0xC4FB },
+{ 0xC4FC, 0xC4FC, 0xC4FC },
+{ 0xC4FD, 0xC4FD, 0xC4FD },
+{ 0xC4FE, 0xC4FE, 0xC4FE },
+{ 0xC4FF, 0xC4FF, 0xC4FF },
+{ 0xC500, 0xC500, 0xC500 },
+{ 0xC501, 0xC501, 0xC501 },
+{ 0xC502, 0xC502, 0xC502 },
+{ 0xC503, 0xC503, 0xC503 },
+{ 0xC504, 0xC504, 0xC504 },
+{ 0xC505, 0xC505, 0xC505 },
+{ 0xC506, 0xC506, 0xC506 },
+{ 0xC507, 0xC507, 0xC507 },
+{ 0xC508, 0xC508, 0xC508 },
+{ 0xC509, 0xC509, 0xC509 },
+{ 0xC50A, 0xC50A, 0xC50A },
+{ 0xC50B, 0xC50B, 0xC50B },
+{ 0xC50C, 0xC50C, 0xC50C },
+{ 0xC50D, 0xC50D, 0xC50D },
+{ 0xC50E, 0xC50E, 0xC50E },
+{ 0xC50F, 0xC50F, 0xC50F },
+{ 0xC510, 0xC510, 0xC510 },
+{ 0xC511, 0xC511, 0xC511 },
+{ 0xC512, 0xC512, 0xC512 },
+{ 0xC513, 0xC513, 0xC513 },
+{ 0xC514, 0xC514, 0xC514 },
+{ 0xC515, 0xC515, 0xC515 },
+{ 0xC516, 0xC516, 0xC516 },
+{ 0xC517, 0xC517, 0xC517 },
+{ 0xC518, 0xC518, 0xC518 },
+{ 0xC519, 0xC519, 0xC519 },
+{ 0xC51A, 0xC51A, 0xC51A },
+{ 0xC51B, 0xC51B, 0xC51B },
+{ 0xC51C, 0xC51C, 0xC51C },
+{ 0xC51D, 0xC51D, 0xC51D },
+{ 0xC51E, 0xC51E, 0xC51E },
+{ 0xC51F, 0xC51F, 0xC51F },
+{ 0xC520, 0xC520, 0xC520 },
+{ 0xC521, 0xC521, 0xC521 },
+{ 0xC522, 0xC522, 0xC522 },
+{ 0xC523, 0xC523, 0xC523 },
+{ 0xC524, 0xC524, 0xC524 },
+{ 0xC525, 0xC525, 0xC525 },
+{ 0xC526, 0xC526, 0xC526 },
+{ 0xC527, 0xC527, 0xC527 },
+{ 0xC528, 0xC528, 0xC528 },
+{ 0xC529, 0xC529, 0xC529 },
+{ 0xC52A, 0xC52A, 0xC52A },
+{ 0xC52B, 0xC52B, 0xC52B },
+{ 0xC52C, 0xC52C, 0xC52C },
+{ 0xC52D, 0xC52D, 0xC52D },
+{ 0xC52E, 0xC52E, 0xC52E },
+{ 0xC52F, 0xC52F, 0xC52F },
+{ 0xC530, 0xC530, 0xC530 },
+{ 0xC531, 0xC531, 0xC531 },
+{ 0xC532, 0xC532, 0xC532 },
+{ 0xC533, 0xC533, 0xC533 },
+{ 0xC534, 0xC534, 0xC534 },
+{ 0xC535, 0xC535, 0xC535 },
+{ 0xC536, 0xC536, 0xC536 },
+{ 0xC537, 0xC537, 0xC537 },
+{ 0xC538, 0xC538, 0xC538 },
+{ 0xC539, 0xC539, 0xC539 },
+{ 0xC53A, 0xC53A, 0xC53A },
+{ 0xC53B, 0xC53B, 0xC53B },
+{ 0xC53C, 0xC53C, 0xC53C },
+{ 0xC53D, 0xC53D, 0xC53D },
+{ 0xC53E, 0xC53E, 0xC53E },
+{ 0xC53F, 0xC53F, 0xC53F },
+{ 0xC540, 0xC540, 0xC540 },
+{ 0xC541, 0xC541, 0xC541 },
+{ 0xC542, 0xC542, 0xC542 },
+{ 0xC543, 0xC543, 0xC543 },
+{ 0xC544, 0xC544, 0xC544 },
+{ 0xC545, 0xC545, 0xC545 },
+{ 0xC546, 0xC546, 0xC546 },
+{ 0xC547, 0xC547, 0xC547 },
+{ 0xC548, 0xC548, 0xC548 },
+{ 0xC549, 0xC549, 0xC549 },
+{ 0xC54A, 0xC54A, 0xC54A },
+{ 0xC54B, 0xC54B, 0xC54B },
+{ 0xC54C, 0xC54C, 0xC54C },
+{ 0xC54D, 0xC54D, 0xC54D },
+{ 0xC54E, 0xC54E, 0xC54E },
+{ 0xC54F, 0xC54F, 0xC54F },
+{ 0xC550, 0xC550, 0xC550 },
+{ 0xC551, 0xC551, 0xC551 },
+{ 0xC552, 0xC552, 0xC552 },
+{ 0xC553, 0xC553, 0xC553 },
+{ 0xC554, 0xC554, 0xC554 },
+{ 0xC555, 0xC555, 0xC555 },
+{ 0xC556, 0xC556, 0xC556 },
+{ 0xC557, 0xC557, 0xC557 },
+{ 0xC558, 0xC558, 0xC558 },
+{ 0xC559, 0xC559, 0xC559 },
+{ 0xC55A, 0xC55A, 0xC55A },
+{ 0xC55B, 0xC55B, 0xC55B },
+{ 0xC55C, 0xC55C, 0xC55C },
+{ 0xC55D, 0xC55D, 0xC55D },
+{ 0xC55E, 0xC55E, 0xC55E },
+{ 0xC55F, 0xC55F, 0xC55F },
+{ 0xC560, 0xC560, 0xC560 },
+{ 0xC561, 0xC561, 0xC561 },
+{ 0xC562, 0xC562, 0xC562 },
+{ 0xC563, 0xC563, 0xC563 },
+{ 0xC564, 0xC564, 0xC564 },
+{ 0xC565, 0xC565, 0xC565 },
+{ 0xC566, 0xC566, 0xC566 },
+{ 0xC567, 0xC567, 0xC567 },
+{ 0xC568, 0xC568, 0xC568 },
+{ 0xC569, 0xC569, 0xC569 },
+{ 0xC56A, 0xC56A, 0xC56A },
+{ 0xC56B, 0xC56B, 0xC56B },
+{ 0xC56C, 0xC56C, 0xC56C },
+{ 0xC56D, 0xC56D, 0xC56D },
+{ 0xC56E, 0xC56E, 0xC56E },
+{ 0xC56F, 0xC56F, 0xC56F },
+{ 0xC570, 0xC570, 0xC570 },
+{ 0xC571, 0xC571, 0xC571 },
+{ 0xC572, 0xC572, 0xC572 },
+{ 0xC573, 0xC573, 0xC573 },
+{ 0xC574, 0xC574, 0xC574 },
+{ 0xC575, 0xC575, 0xC575 },
+{ 0xC576, 0xC576, 0xC576 },
+{ 0xC577, 0xC577, 0xC577 },
+{ 0xC578, 0xC578, 0xC578 },
+{ 0xC579, 0xC579, 0xC579 },
+{ 0xC57A, 0xC57A, 0xC57A },
+{ 0xC57B, 0xC57B, 0xC57B },
+{ 0xC57C, 0xC57C, 0xC57C },
+{ 0xC57D, 0xC57D, 0xC57D },
+{ 0xC57E, 0xC57E, 0xC57E },
+{ 0xC57F, 0xC57F, 0xC57F },
+{ 0xC580, 0xC580, 0xC580 },
+{ 0xC581, 0xC581, 0xC581 },
+{ 0xC582, 0xC582, 0xC582 },
+{ 0xC583, 0xC583, 0xC583 },
+{ 0xC584, 0xC584, 0xC584 },
+{ 0xC585, 0xC585, 0xC585 },
+{ 0xC586, 0xC586, 0xC586 },
+{ 0xC587, 0xC587, 0xC587 },
+{ 0xC588, 0xC588, 0xC588 },
+{ 0xC589, 0xC589, 0xC589 },
+{ 0xC58A, 0xC58A, 0xC58A },
+{ 0xC58B, 0xC58B, 0xC58B },
+{ 0xC58C, 0xC58C, 0xC58C },
+{ 0xC58D, 0xC58D, 0xC58D },
+{ 0xC58E, 0xC58E, 0xC58E },
+{ 0xC58F, 0xC58F, 0xC58F },
+{ 0xC590, 0xC590, 0xC590 },
+{ 0xC591, 0xC591, 0xC591 },
+{ 0xC592, 0xC592, 0xC592 },
+{ 0xC593, 0xC593, 0xC593 },
+{ 0xC594, 0xC594, 0xC594 },
+{ 0xC595, 0xC595, 0xC595 },
+{ 0xC596, 0xC596, 0xC596 },
+{ 0xC597, 0xC597, 0xC597 },
+{ 0xC598, 0xC598, 0xC598 },
+{ 0xC599, 0xC599, 0xC599 },
+{ 0xC59A, 0xC59A, 0xC59A },
+{ 0xC59B, 0xC59B, 0xC59B },
+{ 0xC59C, 0xC59C, 0xC59C },
+{ 0xC59D, 0xC59D, 0xC59D },
+{ 0xC59E, 0xC59E, 0xC59E },
+{ 0xC59F, 0xC59F, 0xC59F },
+{ 0xC5A0, 0xC5A0, 0xC5A0 },
+{ 0xC5A1, 0xC5A1, 0xC5A1 },
+{ 0xC5A2, 0xC5A2, 0xC5A2 },
+{ 0xC5A3, 0xC5A3, 0xC5A3 },
+{ 0xC5A4, 0xC5A4, 0xC5A4 },
+{ 0xC5A5, 0xC5A5, 0xC5A5 },
+{ 0xC5A6, 0xC5A6, 0xC5A6 },
+{ 0xC5A7, 0xC5A7, 0xC5A7 },
+{ 0xC5A8, 0xC5A8, 0xC5A8 },
+{ 0xC5A9, 0xC5A9, 0xC5A9 },
+{ 0xC5AA, 0xC5AA, 0xC5AA },
+{ 0xC5AB, 0xC5AB, 0xC5AB },
+{ 0xC5AC, 0xC5AC, 0xC5AC },
+{ 0xC5AD, 0xC5AD, 0xC5AD },
+{ 0xC5AE, 0xC5AE, 0xC5AE },
+{ 0xC5AF, 0xC5AF, 0xC5AF },
+{ 0xC5B0, 0xC5B0, 0xC5B0 },
+{ 0xC5B1, 0xC5B1, 0xC5B1 },
+{ 0xC5B2, 0xC5B2, 0xC5B2 },
+{ 0xC5B3, 0xC5B3, 0xC5B3 },
+{ 0xC5B4, 0xC5B4, 0xC5B4 },
+{ 0xC5B5, 0xC5B5, 0xC5B5 },
+{ 0xC5B6, 0xC5B6, 0xC5B6 },
+{ 0xC5B7, 0xC5B7, 0xC5B7 },
+{ 0xC5B8, 0xC5B8, 0xC5B8 },
+{ 0xC5B9, 0xC5B9, 0xC5B9 },
+{ 0xC5BA, 0xC5BA, 0xC5BA },
+{ 0xC5BB, 0xC5BB, 0xC5BB },
+{ 0xC5BC, 0xC5BC, 0xC5BC },
+{ 0xC5BD, 0xC5BD, 0xC5BD },
+{ 0xC5BE, 0xC5BE, 0xC5BE },
+{ 0xC5BF, 0xC5BF, 0xC5BF },
+{ 0xC5C0, 0xC5C0, 0xC5C0 },
+{ 0xC5C1, 0xC5C1, 0xC5C1 },
+{ 0xC5C2, 0xC5C2, 0xC5C2 },
+{ 0xC5C3, 0xC5C3, 0xC5C3 },
+{ 0xC5C4, 0xC5C4, 0xC5C4 },
+{ 0xC5C5, 0xC5C5, 0xC5C5 },
+{ 0xC5C6, 0xC5C6, 0xC5C6 },
+{ 0xC5C7, 0xC5C7, 0xC5C7 },
+{ 0xC5C8, 0xC5C8, 0xC5C8 },
+{ 0xC5C9, 0xC5C9, 0xC5C9 },
+{ 0xC5CA, 0xC5CA, 0xC5CA },
+{ 0xC5CB, 0xC5CB, 0xC5CB },
+{ 0xC5CC, 0xC5CC, 0xC5CC },
+{ 0xC5CD, 0xC5CD, 0xC5CD },
+{ 0xC5CE, 0xC5CE, 0xC5CE },
+{ 0xC5CF, 0xC5CF, 0xC5CF },
+{ 0xC5D0, 0xC5D0, 0xC5D0 },
+{ 0xC5D1, 0xC5D1, 0xC5D1 },
+{ 0xC5D2, 0xC5D2, 0xC5D2 },
+{ 0xC5D3, 0xC5D3, 0xC5D3 },
+{ 0xC5D4, 0xC5D4, 0xC5D4 },
+{ 0xC5D5, 0xC5D5, 0xC5D5 },
+{ 0xC5D6, 0xC5D6, 0xC5D6 },
+{ 0xC5D7, 0xC5D7, 0xC5D7 },
+{ 0xC5D8, 0xC5D8, 0xC5D8 },
+{ 0xC5D9, 0xC5D9, 0xC5D9 },
+{ 0xC5DA, 0xC5DA, 0xC5DA },
+{ 0xC5DB, 0xC5DB, 0xC5DB },
+{ 0xC5DC, 0xC5DC, 0xC5DC },
+{ 0xC5DD, 0xC5DD, 0xC5DD },
+{ 0xC5DE, 0xC5DE, 0xC5DE },
+{ 0xC5DF, 0xC5DF, 0xC5DF },
+{ 0xC5E0, 0xC5E0, 0xC5E0 },
+{ 0xC5E1, 0xC5E1, 0xC5E1 },
+{ 0xC5E2, 0xC5E2, 0xC5E2 },
+{ 0xC5E3, 0xC5E3, 0xC5E3 },
+{ 0xC5E4, 0xC5E4, 0xC5E4 },
+{ 0xC5E5, 0xC5E5, 0xC5E5 },
+{ 0xC5E6, 0xC5E6, 0xC5E6 },
+{ 0xC5E7, 0xC5E7, 0xC5E7 },
+{ 0xC5E8, 0xC5E8, 0xC5E8 },
+{ 0xC5E9, 0xC5E9, 0xC5E9 },
+{ 0xC5EA, 0xC5EA, 0xC5EA },
+{ 0xC5EB, 0xC5EB, 0xC5EB },
+{ 0xC5EC, 0xC5EC, 0xC5EC },
+{ 0xC5ED, 0xC5ED, 0xC5ED },
+{ 0xC5EE, 0xC5EE, 0xC5EE },
+{ 0xC5EF, 0xC5EF, 0xC5EF },
+{ 0xC5F0, 0xC5F0, 0xC5F0 },
+{ 0xC5F1, 0xC5F1, 0xC5F1 },
+{ 0xC5F2, 0xC5F2, 0xC5F2 },
+{ 0xC5F3, 0xC5F3, 0xC5F3 },
+{ 0xC5F4, 0xC5F4, 0xC5F4 },
+{ 0xC5F5, 0xC5F5, 0xC5F5 },
+{ 0xC5F6, 0xC5F6, 0xC5F6 },
+{ 0xC5F7, 0xC5F7, 0xC5F7 },
+{ 0xC5F8, 0xC5F8, 0xC5F8 },
+{ 0xC5F9, 0xC5F9, 0xC5F9 },
+{ 0xC5FA, 0xC5FA, 0xC5FA },
+{ 0xC5FB, 0xC5FB, 0xC5FB },
+{ 0xC5FC, 0xC5FC, 0xC5FC },
+{ 0xC5FD, 0xC5FD, 0xC5FD },
+{ 0xC5FE, 0xC5FE, 0xC5FE },
+{ 0xC5FF, 0xC5FF, 0xC5FF },
+{ 0xC600, 0xC600, 0xC600 },
+{ 0xC601, 0xC601, 0xC601 },
+{ 0xC602, 0xC602, 0xC602 },
+{ 0xC603, 0xC603, 0xC603 },
+{ 0xC604, 0xC604, 0xC604 },
+{ 0xC605, 0xC605, 0xC605 },
+{ 0xC606, 0xC606, 0xC606 },
+{ 0xC607, 0xC607, 0xC607 },
+{ 0xC608, 0xC608, 0xC608 },
+{ 0xC609, 0xC609, 0xC609 },
+{ 0xC60A, 0xC60A, 0xC60A },
+{ 0xC60B, 0xC60B, 0xC60B },
+{ 0xC60C, 0xC60C, 0xC60C },
+{ 0xC60D, 0xC60D, 0xC60D },
+{ 0xC60E, 0xC60E, 0xC60E },
+{ 0xC60F, 0xC60F, 0xC60F },
+{ 0xC610, 0xC610, 0xC610 },
+{ 0xC611, 0xC611, 0xC611 },
+{ 0xC612, 0xC612, 0xC612 },
+{ 0xC613, 0xC613, 0xC613 },
+{ 0xC614, 0xC614, 0xC614 },
+{ 0xC615, 0xC615, 0xC615 },
+{ 0xC616, 0xC616, 0xC616 },
+{ 0xC617, 0xC617, 0xC617 },
+{ 0xC618, 0xC618, 0xC618 },
+{ 0xC619, 0xC619, 0xC619 },
+{ 0xC61A, 0xC61A, 0xC61A },
+{ 0xC61B, 0xC61B, 0xC61B },
+{ 0xC61C, 0xC61C, 0xC61C },
+{ 0xC61D, 0xC61D, 0xC61D },
+{ 0xC61E, 0xC61E, 0xC61E },
+{ 0xC61F, 0xC61F, 0xC61F },
+{ 0xC620, 0xC620, 0xC620 },
+{ 0xC621, 0xC621, 0xC621 },
+{ 0xC622, 0xC622, 0xC622 },
+{ 0xC623, 0xC623, 0xC623 },
+{ 0xC624, 0xC624, 0xC624 },
+{ 0xC625, 0xC625, 0xC625 },
+{ 0xC626, 0xC626, 0xC626 },
+{ 0xC627, 0xC627, 0xC627 },
+{ 0xC628, 0xC628, 0xC628 },
+{ 0xC629, 0xC629, 0xC629 },
+{ 0xC62A, 0xC62A, 0xC62A },
+{ 0xC62B, 0xC62B, 0xC62B },
+{ 0xC62C, 0xC62C, 0xC62C },
+{ 0xC62D, 0xC62D, 0xC62D },
+{ 0xC62E, 0xC62E, 0xC62E },
+{ 0xC62F, 0xC62F, 0xC62F },
+{ 0xC630, 0xC630, 0xC630 },
+{ 0xC631, 0xC631, 0xC631 },
+{ 0xC632, 0xC632, 0xC632 },
+{ 0xC633, 0xC633, 0xC633 },
+{ 0xC634, 0xC634, 0xC634 },
+{ 0xC635, 0xC635, 0xC635 },
+{ 0xC636, 0xC636, 0xC636 },
+{ 0xC637, 0xC637, 0xC637 },
+{ 0xC638, 0xC638, 0xC638 },
+{ 0xC639, 0xC639, 0xC639 },
+{ 0xC63A, 0xC63A, 0xC63A },
+{ 0xC63B, 0xC63B, 0xC63B },
+{ 0xC63C, 0xC63C, 0xC63C },
+{ 0xC63D, 0xC63D, 0xC63D },
+{ 0xC63E, 0xC63E, 0xC63E },
+{ 0xC63F, 0xC63F, 0xC63F },
+{ 0xC640, 0xC640, 0xC640 },
+{ 0xC641, 0xC641, 0xC641 },
+{ 0xC642, 0xC642, 0xC642 },
+{ 0xC643, 0xC643, 0xC643 },
+{ 0xC644, 0xC644, 0xC644 },
+{ 0xC645, 0xC645, 0xC645 },
+{ 0xC646, 0xC646, 0xC646 },
+{ 0xC647, 0xC647, 0xC647 },
+{ 0xC648, 0xC648, 0xC648 },
+{ 0xC649, 0xC649, 0xC649 },
+{ 0xC64A, 0xC64A, 0xC64A },
+{ 0xC64B, 0xC64B, 0xC64B },
+{ 0xC64C, 0xC64C, 0xC64C },
+{ 0xC64D, 0xC64D, 0xC64D },
+{ 0xC64E, 0xC64E, 0xC64E },
+{ 0xC64F, 0xC64F, 0xC64F },
+{ 0xC650, 0xC650, 0xC650 },
+{ 0xC651, 0xC651, 0xC651 },
+{ 0xC652, 0xC652, 0xC652 },
+{ 0xC653, 0xC653, 0xC653 },
+{ 0xC654, 0xC654, 0xC654 },
+{ 0xC655, 0xC655, 0xC655 },
+{ 0xC656, 0xC656, 0xC656 },
+{ 0xC657, 0xC657, 0xC657 },
+{ 0xC658, 0xC658, 0xC658 },
+{ 0xC659, 0xC659, 0xC659 },
+{ 0xC65A, 0xC65A, 0xC65A },
+{ 0xC65B, 0xC65B, 0xC65B },
+{ 0xC65C, 0xC65C, 0xC65C },
+{ 0xC65D, 0xC65D, 0xC65D },
+{ 0xC65E, 0xC65E, 0xC65E },
+{ 0xC65F, 0xC65F, 0xC65F },
+{ 0xC660, 0xC660, 0xC660 },
+{ 0xC661, 0xC661, 0xC661 },
+{ 0xC662, 0xC662, 0xC662 },
+{ 0xC663, 0xC663, 0xC663 },
+{ 0xC664, 0xC664, 0xC664 },
+{ 0xC665, 0xC665, 0xC665 },
+{ 0xC666, 0xC666, 0xC666 },
+{ 0xC667, 0xC667, 0xC667 },
+{ 0xC668, 0xC668, 0xC668 },
+{ 0xC669, 0xC669, 0xC669 },
+{ 0xC66A, 0xC66A, 0xC66A },
+{ 0xC66B, 0xC66B, 0xC66B },
+{ 0xC66C, 0xC66C, 0xC66C },
+{ 0xC66D, 0xC66D, 0xC66D },
+{ 0xC66E, 0xC66E, 0xC66E },
+{ 0xC66F, 0xC66F, 0xC66F },
+{ 0xC670, 0xC670, 0xC670 },
+{ 0xC671, 0xC671, 0xC671 },
+{ 0xC672, 0xC672, 0xC672 },
+{ 0xC673, 0xC673, 0xC673 },
+{ 0xC674, 0xC674, 0xC674 },
+{ 0xC675, 0xC675, 0xC675 },
+{ 0xC676, 0xC676, 0xC676 },
+{ 0xC677, 0xC677, 0xC677 },
+{ 0xC678, 0xC678, 0xC678 },
+{ 0xC679, 0xC679, 0xC679 },
+{ 0xC67A, 0xC67A, 0xC67A },
+{ 0xC67B, 0xC67B, 0xC67B },
+{ 0xC67C, 0xC67C, 0xC67C },
+{ 0xC67D, 0xC67D, 0xC67D },
+{ 0xC67E, 0xC67E, 0xC67E },
+{ 0xC67F, 0xC67F, 0xC67F },
+{ 0xC680, 0xC680, 0xC680 },
+{ 0xC681, 0xC681, 0xC681 },
+{ 0xC682, 0xC682, 0xC682 },
+{ 0xC683, 0xC683, 0xC683 },
+{ 0xC684, 0xC684, 0xC684 },
+{ 0xC685, 0xC685, 0xC685 },
+{ 0xC686, 0xC686, 0xC686 },
+{ 0xC687, 0xC687, 0xC687 },
+{ 0xC688, 0xC688, 0xC688 },
+{ 0xC689, 0xC689, 0xC689 },
+{ 0xC68A, 0xC68A, 0xC68A },
+{ 0xC68B, 0xC68B, 0xC68B },
+{ 0xC68C, 0xC68C, 0xC68C },
+{ 0xC68D, 0xC68D, 0xC68D },
+{ 0xC68E, 0xC68E, 0xC68E },
+{ 0xC68F, 0xC68F, 0xC68F },
+{ 0xC690, 0xC690, 0xC690 },
+{ 0xC691, 0xC691, 0xC691 },
+{ 0xC692, 0xC692, 0xC692 },
+{ 0xC693, 0xC693, 0xC693 },
+{ 0xC694, 0xC694, 0xC694 },
+{ 0xC695, 0xC695, 0xC695 },
+{ 0xC696, 0xC696, 0xC696 },
+{ 0xC697, 0xC697, 0xC697 },
+{ 0xC698, 0xC698, 0xC698 },
+{ 0xC699, 0xC699, 0xC699 },
+{ 0xC69A, 0xC69A, 0xC69A },
+{ 0xC69B, 0xC69B, 0xC69B },
+{ 0xC69C, 0xC69C, 0xC69C },
+{ 0xC69D, 0xC69D, 0xC69D },
+{ 0xC69E, 0xC69E, 0xC69E },
+{ 0xC69F, 0xC69F, 0xC69F },
+{ 0xC6A0, 0xC6A0, 0xC6A0 },
+{ 0xC6A1, 0xC6A1, 0xC6A1 },
+{ 0xC6A2, 0xC6A2, 0xC6A2 },
+{ 0xC6A3, 0xC6A3, 0xC6A3 },
+{ 0xC6A4, 0xC6A4, 0xC6A4 },
+{ 0xC6A5, 0xC6A5, 0xC6A5 },
+{ 0xC6A6, 0xC6A6, 0xC6A6 },
+{ 0xC6A7, 0xC6A7, 0xC6A7 },
+{ 0xC6A8, 0xC6A8, 0xC6A8 },
+{ 0xC6A9, 0xC6A9, 0xC6A9 },
+{ 0xC6AA, 0xC6AA, 0xC6AA },
+{ 0xC6AB, 0xC6AB, 0xC6AB },
+{ 0xC6AC, 0xC6AC, 0xC6AC },
+{ 0xC6AD, 0xC6AD, 0xC6AD },
+{ 0xC6AE, 0xC6AE, 0xC6AE },
+{ 0xC6AF, 0xC6AF, 0xC6AF },
+{ 0xC6B0, 0xC6B0, 0xC6B0 },
+{ 0xC6B1, 0xC6B1, 0xC6B1 },
+{ 0xC6B2, 0xC6B2, 0xC6B2 },
+{ 0xC6B3, 0xC6B3, 0xC6B3 },
+{ 0xC6B4, 0xC6B4, 0xC6B4 },
+{ 0xC6B5, 0xC6B5, 0xC6B5 },
+{ 0xC6B6, 0xC6B6, 0xC6B6 },
+{ 0xC6B7, 0xC6B7, 0xC6B7 },
+{ 0xC6B8, 0xC6B8, 0xC6B8 },
+{ 0xC6B9, 0xC6B9, 0xC6B9 },
+{ 0xC6BA, 0xC6BA, 0xC6BA },
+{ 0xC6BB, 0xC6BB, 0xC6BB },
+{ 0xC6BC, 0xC6BC, 0xC6BC },
+{ 0xC6BD, 0xC6BD, 0xC6BD },
+{ 0xC6BE, 0xC6BE, 0xC6BE },
+{ 0xC6BF, 0xC6BF, 0xC6BF },
+{ 0xC6C0, 0xC6C0, 0xC6C0 },
+{ 0xC6C1, 0xC6C1, 0xC6C1 },
+{ 0xC6C2, 0xC6C2, 0xC6C2 },
+{ 0xC6C3, 0xC6C3, 0xC6C3 },
+{ 0xC6C4, 0xC6C4, 0xC6C4 },
+{ 0xC6C5, 0xC6C5, 0xC6C5 },
+{ 0xC6C6, 0xC6C6, 0xC6C6 },
+{ 0xC6C7, 0xC6C7, 0xC6C7 },
+{ 0xC6C8, 0xC6C8, 0xC6C8 },
+{ 0xC6C9, 0xC6C9, 0xC6C9 },
+{ 0xC6CA, 0xC6CA, 0xC6CA },
+{ 0xC6CB, 0xC6CB, 0xC6CB },
+{ 0xC6CC, 0xC6CC, 0xC6CC },
+{ 0xC6CD, 0xC6CD, 0xC6CD },
+{ 0xC6CE, 0xC6CE, 0xC6CE },
+{ 0xC6CF, 0xC6CF, 0xC6CF },
+{ 0xC6D0, 0xC6D0, 0xC6D0 },
+{ 0xC6D1, 0xC6D1, 0xC6D1 },
+{ 0xC6D2, 0xC6D2, 0xC6D2 },
+{ 0xC6D3, 0xC6D3, 0xC6D3 },
+{ 0xC6D4, 0xC6D4, 0xC6D4 },
+{ 0xC6D5, 0xC6D5, 0xC6D5 },
+{ 0xC6D6, 0xC6D6, 0xC6D6 },
+{ 0xC6D7, 0xC6D7, 0xC6D7 },
+{ 0xC6D8, 0xC6D8, 0xC6D8 },
+{ 0xC6D9, 0xC6D9, 0xC6D9 },
+{ 0xC6DA, 0xC6DA, 0xC6DA },
+{ 0xC6DB, 0xC6DB, 0xC6DB },
+{ 0xC6DC, 0xC6DC, 0xC6DC },
+{ 0xC6DD, 0xC6DD, 0xC6DD },
+{ 0xC6DE, 0xC6DE, 0xC6DE },
+{ 0xC6DF, 0xC6DF, 0xC6DF },
+{ 0xC6E0, 0xC6E0, 0xC6E0 },
+{ 0xC6E1, 0xC6E1, 0xC6E1 },
+{ 0xC6E2, 0xC6E2, 0xC6E2 },
+{ 0xC6E3, 0xC6E3, 0xC6E3 },
+{ 0xC6E4, 0xC6E4, 0xC6E4 },
+{ 0xC6E5, 0xC6E5, 0xC6E5 },
+{ 0xC6E6, 0xC6E6, 0xC6E6 },
+{ 0xC6E7, 0xC6E7, 0xC6E7 },
+{ 0xC6E8, 0xC6E8, 0xC6E8 },
+{ 0xC6E9, 0xC6E9, 0xC6E9 },
+{ 0xC6EA, 0xC6EA, 0xC6EA },
+{ 0xC6EB, 0xC6EB, 0xC6EB },
+{ 0xC6EC, 0xC6EC, 0xC6EC },
+{ 0xC6ED, 0xC6ED, 0xC6ED },
+{ 0xC6EE, 0xC6EE, 0xC6EE },
+{ 0xC6EF, 0xC6EF, 0xC6EF },
+{ 0xC6F0, 0xC6F0, 0xC6F0 },
+{ 0xC6F1, 0xC6F1, 0xC6F1 },
+{ 0xC6F2, 0xC6F2, 0xC6F2 },
+{ 0xC6F3, 0xC6F3, 0xC6F3 },
+{ 0xC6F4, 0xC6F4, 0xC6F4 },
+{ 0xC6F5, 0xC6F5, 0xC6F5 },
+{ 0xC6F6, 0xC6F6, 0xC6F6 },
+{ 0xC6F7, 0xC6F7, 0xC6F7 },
+{ 0xC6F8, 0xC6F8, 0xC6F8 },
+{ 0xC6F9, 0xC6F9, 0xC6F9 },
+{ 0xC6FA, 0xC6FA, 0xC6FA },
+{ 0xC6FB, 0xC6FB, 0xC6FB },
+{ 0xC6FC, 0xC6FC, 0xC6FC },
+{ 0xC6FD, 0xC6FD, 0xC6FD },
+{ 0xC6FE, 0xC6FE, 0xC6FE },
+{ 0xC6FF, 0xC6FF, 0xC6FF },
+{ 0xC700, 0xC700, 0xC700 },
+{ 0xC701, 0xC701, 0xC701 },
+{ 0xC702, 0xC702, 0xC702 },
+{ 0xC703, 0xC703, 0xC703 },
+{ 0xC704, 0xC704, 0xC704 },
+{ 0xC705, 0xC705, 0xC705 },
+{ 0xC706, 0xC706, 0xC706 },
+{ 0xC707, 0xC707, 0xC707 },
+{ 0xC708, 0xC708, 0xC708 },
+{ 0xC709, 0xC709, 0xC709 },
+{ 0xC70A, 0xC70A, 0xC70A },
+{ 0xC70B, 0xC70B, 0xC70B },
+{ 0xC70C, 0xC70C, 0xC70C },
+{ 0xC70D, 0xC70D, 0xC70D },
+{ 0xC70E, 0xC70E, 0xC70E },
+{ 0xC70F, 0xC70F, 0xC70F },
+{ 0xC710, 0xC710, 0xC710 },
+{ 0xC711, 0xC711, 0xC711 },
+{ 0xC712, 0xC712, 0xC712 },
+{ 0xC713, 0xC713, 0xC713 },
+{ 0xC714, 0xC714, 0xC714 },
+{ 0xC715, 0xC715, 0xC715 },
+{ 0xC716, 0xC716, 0xC716 },
+{ 0xC717, 0xC717, 0xC717 },
+{ 0xC718, 0xC718, 0xC718 },
+{ 0xC719, 0xC719, 0xC719 },
+{ 0xC71A, 0xC71A, 0xC71A },
+{ 0xC71B, 0xC71B, 0xC71B },
+{ 0xC71C, 0xC71C, 0xC71C },
+{ 0xC71D, 0xC71D, 0xC71D },
+{ 0xC71E, 0xC71E, 0xC71E },
+{ 0xC71F, 0xC71F, 0xC71F },
+{ 0xC720, 0xC720, 0xC720 },
+{ 0xC721, 0xC721, 0xC721 },
+{ 0xC722, 0xC722, 0xC722 },
+{ 0xC723, 0xC723, 0xC723 },
+{ 0xC724, 0xC724, 0xC724 },
+{ 0xC725, 0xC725, 0xC725 },
+{ 0xC726, 0xC726, 0xC726 },
+{ 0xC727, 0xC727, 0xC727 },
+{ 0xC728, 0xC728, 0xC728 },
+{ 0xC729, 0xC729, 0xC729 },
+{ 0xC72A, 0xC72A, 0xC72A },
+{ 0xC72B, 0xC72B, 0xC72B },
+{ 0xC72C, 0xC72C, 0xC72C },
+{ 0xC72D, 0xC72D, 0xC72D },
+{ 0xC72E, 0xC72E, 0xC72E },
+{ 0xC72F, 0xC72F, 0xC72F },
+{ 0xC730, 0xC730, 0xC730 },
+{ 0xC731, 0xC731, 0xC731 },
+{ 0xC732, 0xC732, 0xC732 },
+{ 0xC733, 0xC733, 0xC733 },
+{ 0xC734, 0xC734, 0xC734 },
+{ 0xC735, 0xC735, 0xC735 },
+{ 0xC736, 0xC736, 0xC736 },
+{ 0xC737, 0xC737, 0xC737 },
+{ 0xC738, 0xC738, 0xC738 },
+{ 0xC739, 0xC739, 0xC739 },
+{ 0xC73A, 0xC73A, 0xC73A },
+{ 0xC73B, 0xC73B, 0xC73B },
+{ 0xC73C, 0xC73C, 0xC73C },
+{ 0xC73D, 0xC73D, 0xC73D },
+{ 0xC73E, 0xC73E, 0xC73E },
+{ 0xC73F, 0xC73F, 0xC73F },
+{ 0xC740, 0xC740, 0xC740 },
+{ 0xC741, 0xC741, 0xC741 },
+{ 0xC742, 0xC742, 0xC742 },
+{ 0xC743, 0xC743, 0xC743 },
+{ 0xC744, 0xC744, 0xC744 },
+{ 0xC745, 0xC745, 0xC745 },
+{ 0xC746, 0xC746, 0xC746 },
+{ 0xC747, 0xC747, 0xC747 },
+{ 0xC748, 0xC748, 0xC748 },
+{ 0xC749, 0xC749, 0xC749 },
+{ 0xC74A, 0xC74A, 0xC74A },
+{ 0xC74B, 0xC74B, 0xC74B },
+{ 0xC74C, 0xC74C, 0xC74C },
+{ 0xC74D, 0xC74D, 0xC74D },
+{ 0xC74E, 0xC74E, 0xC74E },
+{ 0xC74F, 0xC74F, 0xC74F },
+{ 0xC750, 0xC750, 0xC750 },
+{ 0xC751, 0xC751, 0xC751 },
+{ 0xC752, 0xC752, 0xC752 },
+{ 0xC753, 0xC753, 0xC753 },
+{ 0xC754, 0xC754, 0xC754 },
+{ 0xC755, 0xC755, 0xC755 },
+{ 0xC756, 0xC756, 0xC756 },
+{ 0xC757, 0xC757, 0xC757 },
+{ 0xC758, 0xC758, 0xC758 },
+{ 0xC759, 0xC759, 0xC759 },
+{ 0xC75A, 0xC75A, 0xC75A },
+{ 0xC75B, 0xC75B, 0xC75B },
+{ 0xC75C, 0xC75C, 0xC75C },
+{ 0xC75D, 0xC75D, 0xC75D },
+{ 0xC75E, 0xC75E, 0xC75E },
+{ 0xC75F, 0xC75F, 0xC75F },
+{ 0xC760, 0xC760, 0xC760 },
+{ 0xC761, 0xC761, 0xC761 },
+{ 0xC762, 0xC762, 0xC762 },
+{ 0xC763, 0xC763, 0xC763 },
+{ 0xC764, 0xC764, 0xC764 },
+{ 0xC765, 0xC765, 0xC765 },
+{ 0xC766, 0xC766, 0xC766 },
+{ 0xC767, 0xC767, 0xC767 },
+{ 0xC768, 0xC768, 0xC768 },
+{ 0xC769, 0xC769, 0xC769 },
+{ 0xC76A, 0xC76A, 0xC76A },
+{ 0xC76B, 0xC76B, 0xC76B },
+{ 0xC76C, 0xC76C, 0xC76C },
+{ 0xC76D, 0xC76D, 0xC76D },
+{ 0xC76E, 0xC76E, 0xC76E },
+{ 0xC76F, 0xC76F, 0xC76F },
+{ 0xC770, 0xC770, 0xC770 },
+{ 0xC771, 0xC771, 0xC771 },
+{ 0xC772, 0xC772, 0xC772 },
+{ 0xC773, 0xC773, 0xC773 },
+{ 0xC774, 0xC774, 0xC774 },
+{ 0xC775, 0xC775, 0xC775 },
+{ 0xC776, 0xC776, 0xC776 },
+{ 0xC777, 0xC777, 0xC777 },
+{ 0xC778, 0xC778, 0xC778 },
+{ 0xC779, 0xC779, 0xC779 },
+{ 0xC77A, 0xC77A, 0xC77A },
+{ 0xC77B, 0xC77B, 0xC77B },
+{ 0xC77C, 0xC77C, 0xC77C },
+{ 0xC77D, 0xC77D, 0xC77D },
+{ 0xC77E, 0xC77E, 0xC77E },
+{ 0xC77F, 0xC77F, 0xC77F },
+{ 0xC780, 0xC780, 0xC780 },
+{ 0xC781, 0xC781, 0xC781 },
+{ 0xC782, 0xC782, 0xC782 },
+{ 0xC783, 0xC783, 0xC783 },
+{ 0xC784, 0xC784, 0xC784 },
+{ 0xC785, 0xC785, 0xC785 },
+{ 0xC786, 0xC786, 0xC786 },
+{ 0xC787, 0xC787, 0xC787 },
+{ 0xC788, 0xC788, 0xC788 },
+{ 0xC789, 0xC789, 0xC789 },
+{ 0xC78A, 0xC78A, 0xC78A },
+{ 0xC78B, 0xC78B, 0xC78B },
+{ 0xC78C, 0xC78C, 0xC78C },
+{ 0xC78D, 0xC78D, 0xC78D },
+{ 0xC78E, 0xC78E, 0xC78E },
+{ 0xC78F, 0xC78F, 0xC78F },
+{ 0xC790, 0xC790, 0xC790 },
+{ 0xC791, 0xC791, 0xC791 },
+{ 0xC792, 0xC792, 0xC792 },
+{ 0xC793, 0xC793, 0xC793 },
+{ 0xC794, 0xC794, 0xC794 },
+{ 0xC795, 0xC795, 0xC795 },
+{ 0xC796, 0xC796, 0xC796 },
+{ 0xC797, 0xC797, 0xC797 },
+{ 0xC798, 0xC798, 0xC798 },
+{ 0xC799, 0xC799, 0xC799 },
+{ 0xC79A, 0xC79A, 0xC79A },
+{ 0xC79B, 0xC79B, 0xC79B },
+{ 0xC79C, 0xC79C, 0xC79C },
+{ 0xC79D, 0xC79D, 0xC79D },
+{ 0xC79E, 0xC79E, 0xC79E },
+{ 0xC79F, 0xC79F, 0xC79F },
+{ 0xC7A0, 0xC7A0, 0xC7A0 },
+{ 0xC7A1, 0xC7A1, 0xC7A1 },
+{ 0xC7A2, 0xC7A2, 0xC7A2 },
+{ 0xC7A3, 0xC7A3, 0xC7A3 },
+{ 0xC7A4, 0xC7A4, 0xC7A4 },
+{ 0xC7A5, 0xC7A5, 0xC7A5 },
+{ 0xC7A6, 0xC7A6, 0xC7A6 },
+{ 0xC7A7, 0xC7A7, 0xC7A7 },
+{ 0xC7A8, 0xC7A8, 0xC7A8 },
+{ 0xC7A9, 0xC7A9, 0xC7A9 },
+{ 0xC7AA, 0xC7AA, 0xC7AA },
+{ 0xC7AB, 0xC7AB, 0xC7AB },
+{ 0xC7AC, 0xC7AC, 0xC7AC },
+{ 0xC7AD, 0xC7AD, 0xC7AD },
+{ 0xC7AE, 0xC7AE, 0xC7AE },
+{ 0xC7AF, 0xC7AF, 0xC7AF },
+{ 0xC7B0, 0xC7B0, 0xC7B0 },
+{ 0xC7B1, 0xC7B1, 0xC7B1 },
+{ 0xC7B2, 0xC7B2, 0xC7B2 },
+{ 0xC7B3, 0xC7B3, 0xC7B3 },
+{ 0xC7B4, 0xC7B4, 0xC7B4 },
+{ 0xC7B5, 0xC7B5, 0xC7B5 },
+{ 0xC7B6, 0xC7B6, 0xC7B6 },
+{ 0xC7B7, 0xC7B7, 0xC7B7 },
+{ 0xC7B8, 0xC7B8, 0xC7B8 },
+{ 0xC7B9, 0xC7B9, 0xC7B9 },
+{ 0xC7BA, 0xC7BA, 0xC7BA },
+{ 0xC7BB, 0xC7BB, 0xC7BB },
+{ 0xC7BC, 0xC7BC, 0xC7BC },
+{ 0xC7BD, 0xC7BD, 0xC7BD },
+{ 0xC7BE, 0xC7BE, 0xC7BE },
+{ 0xC7BF, 0xC7BF, 0xC7BF },
+{ 0xC7C0, 0xC7C0, 0xC7C0 },
+{ 0xC7C1, 0xC7C1, 0xC7C1 },
+{ 0xC7C2, 0xC7C2, 0xC7C2 },
+{ 0xC7C3, 0xC7C3, 0xC7C3 },
+{ 0xC7C4, 0xC7C4, 0xC7C4 },
+{ 0xC7C5, 0xC7C5, 0xC7C5 },
+{ 0xC7C6, 0xC7C6, 0xC7C6 },
+{ 0xC7C7, 0xC7C7, 0xC7C7 },
+{ 0xC7C8, 0xC7C8, 0xC7C8 },
+{ 0xC7C9, 0xC7C9, 0xC7C9 },
+{ 0xC7CA, 0xC7CA, 0xC7CA },
+{ 0xC7CB, 0xC7CB, 0xC7CB },
+{ 0xC7CC, 0xC7CC, 0xC7CC },
+{ 0xC7CD, 0xC7CD, 0xC7CD },
+{ 0xC7CE, 0xC7CE, 0xC7CE },
+{ 0xC7CF, 0xC7CF, 0xC7CF },
+{ 0xC7D0, 0xC7D0, 0xC7D0 },
+{ 0xC7D1, 0xC7D1, 0xC7D1 },
+{ 0xC7D2, 0xC7D2, 0xC7D2 },
+{ 0xC7D3, 0xC7D3, 0xC7D3 },
+{ 0xC7D4, 0xC7D4, 0xC7D4 },
+{ 0xC7D5, 0xC7D5, 0xC7D5 },
+{ 0xC7D6, 0xC7D6, 0xC7D6 },
+{ 0xC7D7, 0xC7D7, 0xC7D7 },
+{ 0xC7D8, 0xC7D8, 0xC7D8 },
+{ 0xC7D9, 0xC7D9, 0xC7D9 },
+{ 0xC7DA, 0xC7DA, 0xC7DA },
+{ 0xC7DB, 0xC7DB, 0xC7DB },
+{ 0xC7DC, 0xC7DC, 0xC7DC },
+{ 0xC7DD, 0xC7DD, 0xC7DD },
+{ 0xC7DE, 0xC7DE, 0xC7DE },
+{ 0xC7DF, 0xC7DF, 0xC7DF },
+{ 0xC7E0, 0xC7E0, 0xC7E0 },
+{ 0xC7E1, 0xC7E1, 0xC7E1 },
+{ 0xC7E2, 0xC7E2, 0xC7E2 },
+{ 0xC7E3, 0xC7E3, 0xC7E3 },
+{ 0xC7E4, 0xC7E4, 0xC7E4 },
+{ 0xC7E5, 0xC7E5, 0xC7E5 },
+{ 0xC7E6, 0xC7E6, 0xC7E6 },
+{ 0xC7E7, 0xC7E7, 0xC7E7 },
+{ 0xC7E8, 0xC7E8, 0xC7E8 },
+{ 0xC7E9, 0xC7E9, 0xC7E9 },
+{ 0xC7EA, 0xC7EA, 0xC7EA },
+{ 0xC7EB, 0xC7EB, 0xC7EB },
+{ 0xC7EC, 0xC7EC, 0xC7EC },
+{ 0xC7ED, 0xC7ED, 0xC7ED },
+{ 0xC7EE, 0xC7EE, 0xC7EE },
+{ 0xC7EF, 0xC7EF, 0xC7EF },
+{ 0xC7F0, 0xC7F0, 0xC7F0 },
+{ 0xC7F1, 0xC7F1, 0xC7F1 },
+{ 0xC7F2, 0xC7F2, 0xC7F2 },
+{ 0xC7F3, 0xC7F3, 0xC7F3 },
+{ 0xC7F4, 0xC7F4, 0xC7F4 },
+{ 0xC7F5, 0xC7F5, 0xC7F5 },
+{ 0xC7F6, 0xC7F6, 0xC7F6 },
+{ 0xC7F7, 0xC7F7, 0xC7F7 },
+{ 0xC7F8, 0xC7F8, 0xC7F8 },
+{ 0xC7F9, 0xC7F9, 0xC7F9 },
+{ 0xC7FA, 0xC7FA, 0xC7FA },
+{ 0xC7FB, 0xC7FB, 0xC7FB },
+{ 0xC7FC, 0xC7FC, 0xC7FC },
+{ 0xC7FD, 0xC7FD, 0xC7FD },
+{ 0xC7FE, 0xC7FE, 0xC7FE },
+{ 0xC7FF, 0xC7FF, 0xC7FF },
+{ 0xC800, 0xC800, 0xC800 },
+{ 0xC801, 0xC801, 0xC801 },
+{ 0xC802, 0xC802, 0xC802 },
+{ 0xC803, 0xC803, 0xC803 },
+{ 0xC804, 0xC804, 0xC804 },
+{ 0xC805, 0xC805, 0xC805 },
+{ 0xC806, 0xC806, 0xC806 },
+{ 0xC807, 0xC807, 0xC807 },
+{ 0xC808, 0xC808, 0xC808 },
+{ 0xC809, 0xC809, 0xC809 },
+{ 0xC80A, 0xC80A, 0xC80A },
+{ 0xC80B, 0xC80B, 0xC80B },
+{ 0xC80C, 0xC80C, 0xC80C },
+{ 0xC80D, 0xC80D, 0xC80D },
+{ 0xC80E, 0xC80E, 0xC80E },
+{ 0xC80F, 0xC80F, 0xC80F },
+{ 0xC810, 0xC810, 0xC810 },
+{ 0xC811, 0xC811, 0xC811 },
+{ 0xC812, 0xC812, 0xC812 },
+{ 0xC813, 0xC813, 0xC813 },
+{ 0xC814, 0xC814, 0xC814 },
+{ 0xC815, 0xC815, 0xC815 },
+{ 0xC816, 0xC816, 0xC816 },
+{ 0xC817, 0xC817, 0xC817 },
+{ 0xC818, 0xC818, 0xC818 },
+{ 0xC819, 0xC819, 0xC819 },
+{ 0xC81A, 0xC81A, 0xC81A },
+{ 0xC81B, 0xC81B, 0xC81B },
+{ 0xC81C, 0xC81C, 0xC81C },
+{ 0xC81D, 0xC81D, 0xC81D },
+{ 0xC81E, 0xC81E, 0xC81E },
+{ 0xC81F, 0xC81F, 0xC81F },
+{ 0xC820, 0xC820, 0xC820 },
+{ 0xC821, 0xC821, 0xC821 },
+{ 0xC822, 0xC822, 0xC822 },
+{ 0xC823, 0xC823, 0xC823 },
+{ 0xC824, 0xC824, 0xC824 },
+{ 0xC825, 0xC825, 0xC825 },
+{ 0xC826, 0xC826, 0xC826 },
+{ 0xC827, 0xC827, 0xC827 },
+{ 0xC828, 0xC828, 0xC828 },
+{ 0xC829, 0xC829, 0xC829 },
+{ 0xC82A, 0xC82A, 0xC82A },
+{ 0xC82B, 0xC82B, 0xC82B },
+{ 0xC82C, 0xC82C, 0xC82C },
+{ 0xC82D, 0xC82D, 0xC82D },
+{ 0xC82E, 0xC82E, 0xC82E },
+{ 0xC82F, 0xC82F, 0xC82F },
+{ 0xC830, 0xC830, 0xC830 },
+{ 0xC831, 0xC831, 0xC831 },
+{ 0xC832, 0xC832, 0xC832 },
+{ 0xC833, 0xC833, 0xC833 },
+{ 0xC834, 0xC834, 0xC834 },
+{ 0xC835, 0xC835, 0xC835 },
+{ 0xC836, 0xC836, 0xC836 },
+{ 0xC837, 0xC837, 0xC837 },
+{ 0xC838, 0xC838, 0xC838 },
+{ 0xC839, 0xC839, 0xC839 },
+{ 0xC83A, 0xC83A, 0xC83A },
+{ 0xC83B, 0xC83B, 0xC83B },
+{ 0xC83C, 0xC83C, 0xC83C },
+{ 0xC83D, 0xC83D, 0xC83D },
+{ 0xC83E, 0xC83E, 0xC83E },
+{ 0xC83F, 0xC83F, 0xC83F },
+{ 0xC840, 0xC840, 0xC840 },
+{ 0xC841, 0xC841, 0xC841 },
+{ 0xC842, 0xC842, 0xC842 },
+{ 0xC843, 0xC843, 0xC843 },
+{ 0xC844, 0xC844, 0xC844 },
+{ 0xC845, 0xC845, 0xC845 },
+{ 0xC846, 0xC846, 0xC846 },
+{ 0xC847, 0xC847, 0xC847 },
+{ 0xC848, 0xC848, 0xC848 },
+{ 0xC849, 0xC849, 0xC849 },
+{ 0xC84A, 0xC84A, 0xC84A },
+{ 0xC84B, 0xC84B, 0xC84B },
+{ 0xC84C, 0xC84C, 0xC84C },
+{ 0xC84D, 0xC84D, 0xC84D },
+{ 0xC84E, 0xC84E, 0xC84E },
+{ 0xC84F, 0xC84F, 0xC84F },
+{ 0xC850, 0xC850, 0xC850 },
+{ 0xC851, 0xC851, 0xC851 },
+{ 0xC852, 0xC852, 0xC852 },
+{ 0xC853, 0xC853, 0xC853 },
+{ 0xC854, 0xC854, 0xC854 },
+{ 0xC855, 0xC855, 0xC855 },
+{ 0xC856, 0xC856, 0xC856 },
+{ 0xC857, 0xC857, 0xC857 },
+{ 0xC858, 0xC858, 0xC858 },
+{ 0xC859, 0xC859, 0xC859 },
+{ 0xC85A, 0xC85A, 0xC85A },
+{ 0xC85B, 0xC85B, 0xC85B },
+{ 0xC85C, 0xC85C, 0xC85C },
+{ 0xC85D, 0xC85D, 0xC85D },
+{ 0xC85E, 0xC85E, 0xC85E },
+{ 0xC85F, 0xC85F, 0xC85F },
+{ 0xC860, 0xC860, 0xC860 },
+{ 0xC861, 0xC861, 0xC861 },
+{ 0xC862, 0xC862, 0xC862 },
+{ 0xC863, 0xC863, 0xC863 },
+{ 0xC864, 0xC864, 0xC864 },
+{ 0xC865, 0xC865, 0xC865 },
+{ 0xC866, 0xC866, 0xC866 },
+{ 0xC867, 0xC867, 0xC867 },
+{ 0xC868, 0xC868, 0xC868 },
+{ 0xC869, 0xC869, 0xC869 },
+{ 0xC86A, 0xC86A, 0xC86A },
+{ 0xC86B, 0xC86B, 0xC86B },
+{ 0xC86C, 0xC86C, 0xC86C },
+{ 0xC86D, 0xC86D, 0xC86D },
+{ 0xC86E, 0xC86E, 0xC86E },
+{ 0xC86F, 0xC86F, 0xC86F },
+{ 0xC870, 0xC870, 0xC870 },
+{ 0xC871, 0xC871, 0xC871 },
+{ 0xC872, 0xC872, 0xC872 },
+{ 0xC873, 0xC873, 0xC873 },
+{ 0xC874, 0xC874, 0xC874 },
+{ 0xC875, 0xC875, 0xC875 },
+{ 0xC876, 0xC876, 0xC876 },
+{ 0xC877, 0xC877, 0xC877 },
+{ 0xC878, 0xC878, 0xC878 },
+{ 0xC879, 0xC879, 0xC879 },
+{ 0xC87A, 0xC87A, 0xC87A },
+{ 0xC87B, 0xC87B, 0xC87B },
+{ 0xC87C, 0xC87C, 0xC87C },
+{ 0xC87D, 0xC87D, 0xC87D },
+{ 0xC87E, 0xC87E, 0xC87E },
+{ 0xC87F, 0xC87F, 0xC87F },
+{ 0xC880, 0xC880, 0xC880 },
+{ 0xC881, 0xC881, 0xC881 },
+{ 0xC882, 0xC882, 0xC882 },
+{ 0xC883, 0xC883, 0xC883 },
+{ 0xC884, 0xC884, 0xC884 },
+{ 0xC885, 0xC885, 0xC885 },
+{ 0xC886, 0xC886, 0xC886 },
+{ 0xC887, 0xC887, 0xC887 },
+{ 0xC888, 0xC888, 0xC888 },
+{ 0xC889, 0xC889, 0xC889 },
+{ 0xC88A, 0xC88A, 0xC88A },
+{ 0xC88B, 0xC88B, 0xC88B },
+{ 0xC88C, 0xC88C, 0xC88C },
+{ 0xC88D, 0xC88D, 0xC88D },
+{ 0xC88E, 0xC88E, 0xC88E },
+{ 0xC88F, 0xC88F, 0xC88F },
+{ 0xC890, 0xC890, 0xC890 },
+{ 0xC891, 0xC891, 0xC891 },
+{ 0xC892, 0xC892, 0xC892 },
+{ 0xC893, 0xC893, 0xC893 },
+{ 0xC894, 0xC894, 0xC894 },
+{ 0xC895, 0xC895, 0xC895 },
+{ 0xC896, 0xC896, 0xC896 },
+{ 0xC897, 0xC897, 0xC897 },
+{ 0xC898, 0xC898, 0xC898 },
+{ 0xC899, 0xC899, 0xC899 },
+{ 0xC89A, 0xC89A, 0xC89A },
+{ 0xC89B, 0xC89B, 0xC89B },
+{ 0xC89C, 0xC89C, 0xC89C },
+{ 0xC89D, 0xC89D, 0xC89D },
+{ 0xC89E, 0xC89E, 0xC89E },
+{ 0xC89F, 0xC89F, 0xC89F },
+{ 0xC8A0, 0xC8A0, 0xC8A0 },
+{ 0xC8A1, 0xC8A1, 0xC8A1 },
+{ 0xC8A2, 0xC8A2, 0xC8A2 },
+{ 0xC8A3, 0xC8A3, 0xC8A3 },
+{ 0xC8A4, 0xC8A4, 0xC8A4 },
+{ 0xC8A5, 0xC8A5, 0xC8A5 },
+{ 0xC8A6, 0xC8A6, 0xC8A6 },
+{ 0xC8A7, 0xC8A7, 0xC8A7 },
+{ 0xC8A8, 0xC8A8, 0xC8A8 },
+{ 0xC8A9, 0xC8A9, 0xC8A9 },
+{ 0xC8AA, 0xC8AA, 0xC8AA },
+{ 0xC8AB, 0xC8AB, 0xC8AB },
+{ 0xC8AC, 0xC8AC, 0xC8AC },
+{ 0xC8AD, 0xC8AD, 0xC8AD },
+{ 0xC8AE, 0xC8AE, 0xC8AE },
+{ 0xC8AF, 0xC8AF, 0xC8AF },
+{ 0xC8B0, 0xC8B0, 0xC8B0 },
+{ 0xC8B1, 0xC8B1, 0xC8B1 },
+{ 0xC8B2, 0xC8B2, 0xC8B2 },
+{ 0xC8B3, 0xC8B3, 0xC8B3 },
+{ 0xC8B4, 0xC8B4, 0xC8B4 },
+{ 0xC8B5, 0xC8B5, 0xC8B5 },
+{ 0xC8B6, 0xC8B6, 0xC8B6 },
+{ 0xC8B7, 0xC8B7, 0xC8B7 },
+{ 0xC8B8, 0xC8B8, 0xC8B8 },
+{ 0xC8B9, 0xC8B9, 0xC8B9 },
+{ 0xC8BA, 0xC8BA, 0xC8BA },
+{ 0xC8BB, 0xC8BB, 0xC8BB },
+{ 0xC8BC, 0xC8BC, 0xC8BC },
+{ 0xC8BD, 0xC8BD, 0xC8BD },
+{ 0xC8BE, 0xC8BE, 0xC8BE },
+{ 0xC8BF, 0xC8BF, 0xC8BF },
+{ 0xC8C0, 0xC8C0, 0xC8C0 },
+{ 0xC8C1, 0xC8C1, 0xC8C1 },
+{ 0xC8C2, 0xC8C2, 0xC8C2 },
+{ 0xC8C3, 0xC8C3, 0xC8C3 },
+{ 0xC8C4, 0xC8C4, 0xC8C4 },
+{ 0xC8C5, 0xC8C5, 0xC8C5 },
+{ 0xC8C6, 0xC8C6, 0xC8C6 },
+{ 0xC8C7, 0xC8C7, 0xC8C7 },
+{ 0xC8C8, 0xC8C8, 0xC8C8 },
+{ 0xC8C9, 0xC8C9, 0xC8C9 },
+{ 0xC8CA, 0xC8CA, 0xC8CA },
+{ 0xC8CB, 0xC8CB, 0xC8CB },
+{ 0xC8CC, 0xC8CC, 0xC8CC },
+{ 0xC8CD, 0xC8CD, 0xC8CD },
+{ 0xC8CE, 0xC8CE, 0xC8CE },
+{ 0xC8CF, 0xC8CF, 0xC8CF },
+{ 0xC8D0, 0xC8D0, 0xC8D0 },
+{ 0xC8D1, 0xC8D1, 0xC8D1 },
+{ 0xC8D2, 0xC8D2, 0xC8D2 },
+{ 0xC8D3, 0xC8D3, 0xC8D3 },
+{ 0xC8D4, 0xC8D4, 0xC8D4 },
+{ 0xC8D5, 0xC8D5, 0xC8D5 },
+{ 0xC8D6, 0xC8D6, 0xC8D6 },
+{ 0xC8D7, 0xC8D7, 0xC8D7 },
+{ 0xC8D8, 0xC8D8, 0xC8D8 },
+{ 0xC8D9, 0xC8D9, 0xC8D9 },
+{ 0xC8DA, 0xC8DA, 0xC8DA },
+{ 0xC8DB, 0xC8DB, 0xC8DB },
+{ 0xC8DC, 0xC8DC, 0xC8DC },
+{ 0xC8DD, 0xC8DD, 0xC8DD },
+{ 0xC8DE, 0xC8DE, 0xC8DE },
+{ 0xC8DF, 0xC8DF, 0xC8DF },
+{ 0xC8E0, 0xC8E0, 0xC8E0 },
+{ 0xC8E1, 0xC8E1, 0xC8E1 },
+{ 0xC8E2, 0xC8E2, 0xC8E2 },
+{ 0xC8E3, 0xC8E3, 0xC8E3 },
+{ 0xC8E4, 0xC8E4, 0xC8E4 },
+{ 0xC8E5, 0xC8E5, 0xC8E5 },
+{ 0xC8E6, 0xC8E6, 0xC8E6 },
+{ 0xC8E7, 0xC8E7, 0xC8E7 },
+{ 0xC8E8, 0xC8E8, 0xC8E8 },
+{ 0xC8E9, 0xC8E9, 0xC8E9 },
+{ 0xC8EA, 0xC8EA, 0xC8EA },
+{ 0xC8EB, 0xC8EB, 0xC8EB },
+{ 0xC8EC, 0xC8EC, 0xC8EC },
+{ 0xC8ED, 0xC8ED, 0xC8ED },
+{ 0xC8EE, 0xC8EE, 0xC8EE },
+{ 0xC8EF, 0xC8EF, 0xC8EF },
+{ 0xC8F0, 0xC8F0, 0xC8F0 },
+{ 0xC8F1, 0xC8F1, 0xC8F1 },
+{ 0xC8F2, 0xC8F2, 0xC8F2 },
+{ 0xC8F3, 0xC8F3, 0xC8F3 },
+{ 0xC8F4, 0xC8F4, 0xC8F4 },
+{ 0xC8F5, 0xC8F5, 0xC8F5 },
+{ 0xC8F6, 0xC8F6, 0xC8F6 },
+{ 0xC8F7, 0xC8F7, 0xC8F7 },
+{ 0xC8F8, 0xC8F8, 0xC8F8 },
+{ 0xC8F9, 0xC8F9, 0xC8F9 },
+{ 0xC8FA, 0xC8FA, 0xC8FA },
+{ 0xC8FB, 0xC8FB, 0xC8FB },
+{ 0xC8FC, 0xC8FC, 0xC8FC },
+{ 0xC8FD, 0xC8FD, 0xC8FD },
+{ 0xC8FE, 0xC8FE, 0xC8FE },
+{ 0xC8FF, 0xC8FF, 0xC8FF },
+{ 0xC900, 0xC900, 0xC900 },
+{ 0xC901, 0xC901, 0xC901 },
+{ 0xC902, 0xC902, 0xC902 },
+{ 0xC903, 0xC903, 0xC903 },
+{ 0xC904, 0xC904, 0xC904 },
+{ 0xC905, 0xC905, 0xC905 },
+{ 0xC906, 0xC906, 0xC906 },
+{ 0xC907, 0xC907, 0xC907 },
+{ 0xC908, 0xC908, 0xC908 },
+{ 0xC909, 0xC909, 0xC909 },
+{ 0xC90A, 0xC90A, 0xC90A },
+{ 0xC90B, 0xC90B, 0xC90B },
+{ 0xC90C, 0xC90C, 0xC90C },
+{ 0xC90D, 0xC90D, 0xC90D },
+{ 0xC90E, 0xC90E, 0xC90E },
+{ 0xC90F, 0xC90F, 0xC90F },
+{ 0xC910, 0xC910, 0xC910 },
+{ 0xC911, 0xC911, 0xC911 },
+{ 0xC912, 0xC912, 0xC912 },
+{ 0xC913, 0xC913, 0xC913 },
+{ 0xC914, 0xC914, 0xC914 },
+{ 0xC915, 0xC915, 0xC915 },
+{ 0xC916, 0xC916, 0xC916 },
+{ 0xC917, 0xC917, 0xC917 },
+{ 0xC918, 0xC918, 0xC918 },
+{ 0xC919, 0xC919, 0xC919 },
+{ 0xC91A, 0xC91A, 0xC91A },
+{ 0xC91B, 0xC91B, 0xC91B },
+{ 0xC91C, 0xC91C, 0xC91C },
+{ 0xC91D, 0xC91D, 0xC91D },
+{ 0xC91E, 0xC91E, 0xC91E },
+{ 0xC91F, 0xC91F, 0xC91F },
+{ 0xC920, 0xC920, 0xC920 },
+{ 0xC921, 0xC921, 0xC921 },
+{ 0xC922, 0xC922, 0xC922 },
+{ 0xC923, 0xC923, 0xC923 },
+{ 0xC924, 0xC924, 0xC924 },
+{ 0xC925, 0xC925, 0xC925 },
+{ 0xC926, 0xC926, 0xC926 },
+{ 0xC927, 0xC927, 0xC927 },
+{ 0xC928, 0xC928, 0xC928 },
+{ 0xC929, 0xC929, 0xC929 },
+{ 0xC92A, 0xC92A, 0xC92A },
+{ 0xC92B, 0xC92B, 0xC92B },
+{ 0xC92C, 0xC92C, 0xC92C },
+{ 0xC92D, 0xC92D, 0xC92D },
+{ 0xC92E, 0xC92E, 0xC92E },
+{ 0xC92F, 0xC92F, 0xC92F },
+{ 0xC930, 0xC930, 0xC930 },
+{ 0xC931, 0xC931, 0xC931 },
+{ 0xC932, 0xC932, 0xC932 },
+{ 0xC933, 0xC933, 0xC933 },
+{ 0xC934, 0xC934, 0xC934 },
+{ 0xC935, 0xC935, 0xC935 },
+{ 0xC936, 0xC936, 0xC936 },
+{ 0xC937, 0xC937, 0xC937 },
+{ 0xC938, 0xC938, 0xC938 },
+{ 0xC939, 0xC939, 0xC939 },
+{ 0xC93A, 0xC93A, 0xC93A },
+{ 0xC93B, 0xC93B, 0xC93B },
+{ 0xC93C, 0xC93C, 0xC93C },
+{ 0xC93D, 0xC93D, 0xC93D },
+{ 0xC93E, 0xC93E, 0xC93E },
+{ 0xC93F, 0xC93F, 0xC93F },
+{ 0xC940, 0xC940, 0xC940 },
+{ 0xC941, 0xC941, 0xC941 },
+{ 0xC942, 0xC942, 0xC942 },
+{ 0xC943, 0xC943, 0xC943 },
+{ 0xC944, 0xC944, 0xC944 },
+{ 0xC945, 0xC945, 0xC945 },
+{ 0xC946, 0xC946, 0xC946 },
+{ 0xC947, 0xC947, 0xC947 },
+{ 0xC948, 0xC948, 0xC948 },
+{ 0xC949, 0xC949, 0xC949 },
+{ 0xC94A, 0xC94A, 0xC94A },
+{ 0xC94B, 0xC94B, 0xC94B },
+{ 0xC94C, 0xC94C, 0xC94C },
+{ 0xC94D, 0xC94D, 0xC94D },
+{ 0xC94E, 0xC94E, 0xC94E },
+{ 0xC94F, 0xC94F, 0xC94F },
+{ 0xC950, 0xC950, 0xC950 },
+{ 0xC951, 0xC951, 0xC951 },
+{ 0xC952, 0xC952, 0xC952 },
+{ 0xC953, 0xC953, 0xC953 },
+{ 0xC954, 0xC954, 0xC954 },
+{ 0xC955, 0xC955, 0xC955 },
+{ 0xC956, 0xC956, 0xC956 },
+{ 0xC957, 0xC957, 0xC957 },
+{ 0xC958, 0xC958, 0xC958 },
+{ 0xC959, 0xC959, 0xC959 },
+{ 0xC95A, 0xC95A, 0xC95A },
+{ 0xC95B, 0xC95B, 0xC95B },
+{ 0xC95C, 0xC95C, 0xC95C },
+{ 0xC95D, 0xC95D, 0xC95D },
+{ 0xC95E, 0xC95E, 0xC95E },
+{ 0xC95F, 0xC95F, 0xC95F },
+{ 0xC960, 0xC960, 0xC960 },
+{ 0xC961, 0xC961, 0xC961 },
+{ 0xC962, 0xC962, 0xC962 },
+{ 0xC963, 0xC963, 0xC963 },
+{ 0xC964, 0xC964, 0xC964 },
+{ 0xC965, 0xC965, 0xC965 },
+{ 0xC966, 0xC966, 0xC966 },
+{ 0xC967, 0xC967, 0xC967 },
+{ 0xC968, 0xC968, 0xC968 },
+{ 0xC969, 0xC969, 0xC969 },
+{ 0xC96A, 0xC96A, 0xC96A },
+{ 0xC96B, 0xC96B, 0xC96B },
+{ 0xC96C, 0xC96C, 0xC96C },
+{ 0xC96D, 0xC96D, 0xC96D },
+{ 0xC96E, 0xC96E, 0xC96E },
+{ 0xC96F, 0xC96F, 0xC96F },
+{ 0xC970, 0xC970, 0xC970 },
+{ 0xC971, 0xC971, 0xC971 },
+{ 0xC972, 0xC972, 0xC972 },
+{ 0xC973, 0xC973, 0xC973 },
+{ 0xC974, 0xC974, 0xC974 },
+{ 0xC975, 0xC975, 0xC975 },
+{ 0xC976, 0xC976, 0xC976 },
+{ 0xC977, 0xC977, 0xC977 },
+{ 0xC978, 0xC978, 0xC978 },
+{ 0xC979, 0xC979, 0xC979 },
+{ 0xC97A, 0xC97A, 0xC97A },
+{ 0xC97B, 0xC97B, 0xC97B },
+{ 0xC97C, 0xC97C, 0xC97C },
+{ 0xC97D, 0xC97D, 0xC97D },
+{ 0xC97E, 0xC97E, 0xC97E },
+{ 0xC97F, 0xC97F, 0xC97F },
+{ 0xC980, 0xC980, 0xC980 },
+{ 0xC981, 0xC981, 0xC981 },
+{ 0xC982, 0xC982, 0xC982 },
+{ 0xC983, 0xC983, 0xC983 },
+{ 0xC984, 0xC984, 0xC984 },
+{ 0xC985, 0xC985, 0xC985 },
+{ 0xC986, 0xC986, 0xC986 },
+{ 0xC987, 0xC987, 0xC987 },
+{ 0xC988, 0xC988, 0xC988 },
+{ 0xC989, 0xC989, 0xC989 },
+{ 0xC98A, 0xC98A, 0xC98A },
+{ 0xC98B, 0xC98B, 0xC98B },
+{ 0xC98C, 0xC98C, 0xC98C },
+{ 0xC98D, 0xC98D, 0xC98D },
+{ 0xC98E, 0xC98E, 0xC98E },
+{ 0xC98F, 0xC98F, 0xC98F },
+{ 0xC990, 0xC990, 0xC990 },
+{ 0xC991, 0xC991, 0xC991 },
+{ 0xC992, 0xC992, 0xC992 },
+{ 0xC993, 0xC993, 0xC993 },
+{ 0xC994, 0xC994, 0xC994 },
+{ 0xC995, 0xC995, 0xC995 },
+{ 0xC996, 0xC996, 0xC996 },
+{ 0xC997, 0xC997, 0xC997 },
+{ 0xC998, 0xC998, 0xC998 },
+{ 0xC999, 0xC999, 0xC999 },
+{ 0xC99A, 0xC99A, 0xC99A },
+{ 0xC99B, 0xC99B, 0xC99B },
+{ 0xC99C, 0xC99C, 0xC99C },
+{ 0xC99D, 0xC99D, 0xC99D },
+{ 0xC99E, 0xC99E, 0xC99E },
+{ 0xC99F, 0xC99F, 0xC99F },
+{ 0xC9A0, 0xC9A0, 0xC9A0 },
+{ 0xC9A1, 0xC9A1, 0xC9A1 },
+{ 0xC9A2, 0xC9A2, 0xC9A2 },
+{ 0xC9A3, 0xC9A3, 0xC9A3 },
+{ 0xC9A4, 0xC9A4, 0xC9A4 },
+{ 0xC9A5, 0xC9A5, 0xC9A5 },
+{ 0xC9A6, 0xC9A6, 0xC9A6 },
+{ 0xC9A7, 0xC9A7, 0xC9A7 },
+{ 0xC9A8, 0xC9A8, 0xC9A8 },
+{ 0xC9A9, 0xC9A9, 0xC9A9 },
+{ 0xC9AA, 0xC9AA, 0xC9AA },
+{ 0xC9AB, 0xC9AB, 0xC9AB },
+{ 0xC9AC, 0xC9AC, 0xC9AC },
+{ 0xC9AD, 0xC9AD, 0xC9AD },
+{ 0xC9AE, 0xC9AE, 0xC9AE },
+{ 0xC9AF, 0xC9AF, 0xC9AF },
+{ 0xC9B0, 0xC9B0, 0xC9B0 },
+{ 0xC9B1, 0xC9B1, 0xC9B1 },
+{ 0xC9B2, 0xC9B2, 0xC9B2 },
+{ 0xC9B3, 0xC9B3, 0xC9B3 },
+{ 0xC9B4, 0xC9B4, 0xC9B4 },
+{ 0xC9B5, 0xC9B5, 0xC9B5 },
+{ 0xC9B6, 0xC9B6, 0xC9B6 },
+{ 0xC9B7, 0xC9B7, 0xC9B7 },
+{ 0xC9B8, 0xC9B8, 0xC9B8 },
+{ 0xC9B9, 0xC9B9, 0xC9B9 },
+{ 0xC9BA, 0xC9BA, 0xC9BA },
+{ 0xC9BB, 0xC9BB, 0xC9BB },
+{ 0xC9BC, 0xC9BC, 0xC9BC },
+{ 0xC9BD, 0xC9BD, 0xC9BD },
+{ 0xC9BE, 0xC9BE, 0xC9BE },
+{ 0xC9BF, 0xC9BF, 0xC9BF },
+{ 0xC9C0, 0xC9C0, 0xC9C0 },
+{ 0xC9C1, 0xC9C1, 0xC9C1 },
+{ 0xC9C2, 0xC9C2, 0xC9C2 },
+{ 0xC9C3, 0xC9C3, 0xC9C3 },
+{ 0xC9C4, 0xC9C4, 0xC9C4 },
+{ 0xC9C5, 0xC9C5, 0xC9C5 },
+{ 0xC9C6, 0xC9C6, 0xC9C6 },
+{ 0xC9C7, 0xC9C7, 0xC9C7 },
+{ 0xC9C8, 0xC9C8, 0xC9C8 },
+{ 0xC9C9, 0xC9C9, 0xC9C9 },
+{ 0xC9CA, 0xC9CA, 0xC9CA },
+{ 0xC9CB, 0xC9CB, 0xC9CB },
+{ 0xC9CC, 0xC9CC, 0xC9CC },
+{ 0xC9CD, 0xC9CD, 0xC9CD },
+{ 0xC9CE, 0xC9CE, 0xC9CE },
+{ 0xC9CF, 0xC9CF, 0xC9CF },
+{ 0xC9D0, 0xC9D0, 0xC9D0 },
+{ 0xC9D1, 0xC9D1, 0xC9D1 },
+{ 0xC9D2, 0xC9D2, 0xC9D2 },
+{ 0xC9D3, 0xC9D3, 0xC9D3 },
+{ 0xC9D4, 0xC9D4, 0xC9D4 },
+{ 0xC9D5, 0xC9D5, 0xC9D5 },
+{ 0xC9D6, 0xC9D6, 0xC9D6 },
+{ 0xC9D7, 0xC9D7, 0xC9D7 },
+{ 0xC9D8, 0xC9D8, 0xC9D8 },
+{ 0xC9D9, 0xC9D9, 0xC9D9 },
+{ 0xC9DA, 0xC9DA, 0xC9DA },
+{ 0xC9DB, 0xC9DB, 0xC9DB },
+{ 0xC9DC, 0xC9DC, 0xC9DC },
+{ 0xC9DD, 0xC9DD, 0xC9DD },
+{ 0xC9DE, 0xC9DE, 0xC9DE },
+{ 0xC9DF, 0xC9DF, 0xC9DF },
+{ 0xC9E0, 0xC9E0, 0xC9E0 },
+{ 0xC9E1, 0xC9E1, 0xC9E1 },
+{ 0xC9E2, 0xC9E2, 0xC9E2 },
+{ 0xC9E3, 0xC9E3, 0xC9E3 },
+{ 0xC9E4, 0xC9E4, 0xC9E4 },
+{ 0xC9E5, 0xC9E5, 0xC9E5 },
+{ 0xC9E6, 0xC9E6, 0xC9E6 },
+{ 0xC9E7, 0xC9E7, 0xC9E7 },
+{ 0xC9E8, 0xC9E8, 0xC9E8 },
+{ 0xC9E9, 0xC9E9, 0xC9E9 },
+{ 0xC9EA, 0xC9EA, 0xC9EA },
+{ 0xC9EB, 0xC9EB, 0xC9EB },
+{ 0xC9EC, 0xC9EC, 0xC9EC },
+{ 0xC9ED, 0xC9ED, 0xC9ED },
+{ 0xC9EE, 0xC9EE, 0xC9EE },
+{ 0xC9EF, 0xC9EF, 0xC9EF },
+{ 0xC9F0, 0xC9F0, 0xC9F0 },
+{ 0xC9F1, 0xC9F1, 0xC9F1 },
+{ 0xC9F2, 0xC9F2, 0xC9F2 },
+{ 0xC9F3, 0xC9F3, 0xC9F3 },
+{ 0xC9F4, 0xC9F4, 0xC9F4 },
+{ 0xC9F5, 0xC9F5, 0xC9F5 },
+{ 0xC9F6, 0xC9F6, 0xC9F6 },
+{ 0xC9F7, 0xC9F7, 0xC9F7 },
+{ 0xC9F8, 0xC9F8, 0xC9F8 },
+{ 0xC9F9, 0xC9F9, 0xC9F9 },
+{ 0xC9FA, 0xC9FA, 0xC9FA },
+{ 0xC9FB, 0xC9FB, 0xC9FB },
+{ 0xC9FC, 0xC9FC, 0xC9FC },
+{ 0xC9FD, 0xC9FD, 0xC9FD },
+{ 0xC9FE, 0xC9FE, 0xC9FE },
+{ 0xC9FF, 0xC9FF, 0xC9FF },
+{ 0xCA00, 0xCA00, 0xCA00 },
+{ 0xCA01, 0xCA01, 0xCA01 },
+{ 0xCA02, 0xCA02, 0xCA02 },
+{ 0xCA03, 0xCA03, 0xCA03 },
+{ 0xCA04, 0xCA04, 0xCA04 },
+{ 0xCA05, 0xCA05, 0xCA05 },
+{ 0xCA06, 0xCA06, 0xCA06 },
+{ 0xCA07, 0xCA07, 0xCA07 },
+{ 0xCA08, 0xCA08, 0xCA08 },
+{ 0xCA09, 0xCA09, 0xCA09 },
+{ 0xCA0A, 0xCA0A, 0xCA0A },
+{ 0xCA0B, 0xCA0B, 0xCA0B },
+{ 0xCA0C, 0xCA0C, 0xCA0C },
+{ 0xCA0D, 0xCA0D, 0xCA0D },
+{ 0xCA0E, 0xCA0E, 0xCA0E },
+{ 0xCA0F, 0xCA0F, 0xCA0F },
+{ 0xCA10, 0xCA10, 0xCA10 },
+{ 0xCA11, 0xCA11, 0xCA11 },
+{ 0xCA12, 0xCA12, 0xCA12 },
+{ 0xCA13, 0xCA13, 0xCA13 },
+{ 0xCA14, 0xCA14, 0xCA14 },
+{ 0xCA15, 0xCA15, 0xCA15 },
+{ 0xCA16, 0xCA16, 0xCA16 },
+{ 0xCA17, 0xCA17, 0xCA17 },
+{ 0xCA18, 0xCA18, 0xCA18 },
+{ 0xCA19, 0xCA19, 0xCA19 },
+{ 0xCA1A, 0xCA1A, 0xCA1A },
+{ 0xCA1B, 0xCA1B, 0xCA1B },
+{ 0xCA1C, 0xCA1C, 0xCA1C },
+{ 0xCA1D, 0xCA1D, 0xCA1D },
+{ 0xCA1E, 0xCA1E, 0xCA1E },
+{ 0xCA1F, 0xCA1F, 0xCA1F },
+{ 0xCA20, 0xCA20, 0xCA20 },
+{ 0xCA21, 0xCA21, 0xCA21 },
+{ 0xCA22, 0xCA22, 0xCA22 },
+{ 0xCA23, 0xCA23, 0xCA23 },
+{ 0xCA24, 0xCA24, 0xCA24 },
+{ 0xCA25, 0xCA25, 0xCA25 },
+{ 0xCA26, 0xCA26, 0xCA26 },
+{ 0xCA27, 0xCA27, 0xCA27 },
+{ 0xCA28, 0xCA28, 0xCA28 },
+{ 0xCA29, 0xCA29, 0xCA29 },
+{ 0xCA2A, 0xCA2A, 0xCA2A },
+{ 0xCA2B, 0xCA2B, 0xCA2B },
+{ 0xCA2C, 0xCA2C, 0xCA2C },
+{ 0xCA2D, 0xCA2D, 0xCA2D },
+{ 0xCA2E, 0xCA2E, 0xCA2E },
+{ 0xCA2F, 0xCA2F, 0xCA2F },
+{ 0xCA30, 0xCA30, 0xCA30 },
+{ 0xCA31, 0xCA31, 0xCA31 },
+{ 0xCA32, 0xCA32, 0xCA32 },
+{ 0xCA33, 0xCA33, 0xCA33 },
+{ 0xCA34, 0xCA34, 0xCA34 },
+{ 0xCA35, 0xCA35, 0xCA35 },
+{ 0xCA36, 0xCA36, 0xCA36 },
+{ 0xCA37, 0xCA37, 0xCA37 },
+{ 0xCA38, 0xCA38, 0xCA38 },
+{ 0xCA39, 0xCA39, 0xCA39 },
+{ 0xCA3A, 0xCA3A, 0xCA3A },
+{ 0xCA3B, 0xCA3B, 0xCA3B },
+{ 0xCA3C, 0xCA3C, 0xCA3C },
+{ 0xCA3D, 0xCA3D, 0xCA3D },
+{ 0xCA3E, 0xCA3E, 0xCA3E },
+{ 0xCA3F, 0xCA3F, 0xCA3F },
+{ 0xCA40, 0xCA40, 0xCA40 },
+{ 0xCA41, 0xCA41, 0xCA41 },
+{ 0xCA42, 0xCA42, 0xCA42 },
+{ 0xCA43, 0xCA43, 0xCA43 },
+{ 0xCA44, 0xCA44, 0xCA44 },
+{ 0xCA45, 0xCA45, 0xCA45 },
+{ 0xCA46, 0xCA46, 0xCA46 },
+{ 0xCA47, 0xCA47, 0xCA47 },
+{ 0xCA48, 0xCA48, 0xCA48 },
+{ 0xCA49, 0xCA49, 0xCA49 },
+{ 0xCA4A, 0xCA4A, 0xCA4A },
+{ 0xCA4B, 0xCA4B, 0xCA4B },
+{ 0xCA4C, 0xCA4C, 0xCA4C },
+{ 0xCA4D, 0xCA4D, 0xCA4D },
+{ 0xCA4E, 0xCA4E, 0xCA4E },
+{ 0xCA4F, 0xCA4F, 0xCA4F },
+{ 0xCA50, 0xCA50, 0xCA50 },
+{ 0xCA51, 0xCA51, 0xCA51 },
+{ 0xCA52, 0xCA52, 0xCA52 },
+{ 0xCA53, 0xCA53, 0xCA53 },
+{ 0xCA54, 0xCA54, 0xCA54 },
+{ 0xCA55, 0xCA55, 0xCA55 },
+{ 0xCA56, 0xCA56, 0xCA56 },
+{ 0xCA57, 0xCA57, 0xCA57 },
+{ 0xCA58, 0xCA58, 0xCA58 },
+{ 0xCA59, 0xCA59, 0xCA59 },
+{ 0xCA5A, 0xCA5A, 0xCA5A },
+{ 0xCA5B, 0xCA5B, 0xCA5B },
+{ 0xCA5C, 0xCA5C, 0xCA5C },
+{ 0xCA5D, 0xCA5D, 0xCA5D },
+{ 0xCA5E, 0xCA5E, 0xCA5E },
+{ 0xCA5F, 0xCA5F, 0xCA5F },
+{ 0xCA60, 0xCA60, 0xCA60 },
+{ 0xCA61, 0xCA61, 0xCA61 },
+{ 0xCA62, 0xCA62, 0xCA62 },
+{ 0xCA63, 0xCA63, 0xCA63 },
+{ 0xCA64, 0xCA64, 0xCA64 },
+{ 0xCA65, 0xCA65, 0xCA65 },
+{ 0xCA66, 0xCA66, 0xCA66 },
+{ 0xCA67, 0xCA67, 0xCA67 },
+{ 0xCA68, 0xCA68, 0xCA68 },
+{ 0xCA69, 0xCA69, 0xCA69 },
+{ 0xCA6A, 0xCA6A, 0xCA6A },
+{ 0xCA6B, 0xCA6B, 0xCA6B },
+{ 0xCA6C, 0xCA6C, 0xCA6C },
+{ 0xCA6D, 0xCA6D, 0xCA6D },
+{ 0xCA6E, 0xCA6E, 0xCA6E },
+{ 0xCA6F, 0xCA6F, 0xCA6F },
+{ 0xCA70, 0xCA70, 0xCA70 },
+{ 0xCA71, 0xCA71, 0xCA71 },
+{ 0xCA72, 0xCA72, 0xCA72 },
+{ 0xCA73, 0xCA73, 0xCA73 },
+{ 0xCA74, 0xCA74, 0xCA74 },
+{ 0xCA75, 0xCA75, 0xCA75 },
+{ 0xCA76, 0xCA76, 0xCA76 },
+{ 0xCA77, 0xCA77, 0xCA77 },
+{ 0xCA78, 0xCA78, 0xCA78 },
+{ 0xCA79, 0xCA79, 0xCA79 },
+{ 0xCA7A, 0xCA7A, 0xCA7A },
+{ 0xCA7B, 0xCA7B, 0xCA7B },
+{ 0xCA7C, 0xCA7C, 0xCA7C },
+{ 0xCA7D, 0xCA7D, 0xCA7D },
+{ 0xCA7E, 0xCA7E, 0xCA7E },
+{ 0xCA7F, 0xCA7F, 0xCA7F },
+{ 0xCA80, 0xCA80, 0xCA80 },
+{ 0xCA81, 0xCA81, 0xCA81 },
+{ 0xCA82, 0xCA82, 0xCA82 },
+{ 0xCA83, 0xCA83, 0xCA83 },
+{ 0xCA84, 0xCA84, 0xCA84 },
+{ 0xCA85, 0xCA85, 0xCA85 },
+{ 0xCA86, 0xCA86, 0xCA86 },
+{ 0xCA87, 0xCA87, 0xCA87 },
+{ 0xCA88, 0xCA88, 0xCA88 },
+{ 0xCA89, 0xCA89, 0xCA89 },
+{ 0xCA8A, 0xCA8A, 0xCA8A },
+{ 0xCA8B, 0xCA8B, 0xCA8B },
+{ 0xCA8C, 0xCA8C, 0xCA8C },
+{ 0xCA8D, 0xCA8D, 0xCA8D },
+{ 0xCA8E, 0xCA8E, 0xCA8E },
+{ 0xCA8F, 0xCA8F, 0xCA8F },
+{ 0xCA90, 0xCA90, 0xCA90 },
+{ 0xCA91, 0xCA91, 0xCA91 },
+{ 0xCA92, 0xCA92, 0xCA92 },
+{ 0xCA93, 0xCA93, 0xCA93 },
+{ 0xCA94, 0xCA94, 0xCA94 },
+{ 0xCA95, 0xCA95, 0xCA95 },
+{ 0xCA96, 0xCA96, 0xCA96 },
+{ 0xCA97, 0xCA97, 0xCA97 },
+{ 0xCA98, 0xCA98, 0xCA98 },
+{ 0xCA99, 0xCA99, 0xCA99 },
+{ 0xCA9A, 0xCA9A, 0xCA9A },
+{ 0xCA9B, 0xCA9B, 0xCA9B },
+{ 0xCA9C, 0xCA9C, 0xCA9C },
+{ 0xCA9D, 0xCA9D, 0xCA9D },
+{ 0xCA9E, 0xCA9E, 0xCA9E },
+{ 0xCA9F, 0xCA9F, 0xCA9F },
+{ 0xCAA0, 0xCAA0, 0xCAA0 },
+{ 0xCAA1, 0xCAA1, 0xCAA1 },
+{ 0xCAA2, 0xCAA2, 0xCAA2 },
+{ 0xCAA3, 0xCAA3, 0xCAA3 },
+{ 0xCAA4, 0xCAA4, 0xCAA4 },
+{ 0xCAA5, 0xCAA5, 0xCAA5 },
+{ 0xCAA6, 0xCAA6, 0xCAA6 },
+{ 0xCAA7, 0xCAA7, 0xCAA7 },
+{ 0xCAA8, 0xCAA8, 0xCAA8 },
+{ 0xCAA9, 0xCAA9, 0xCAA9 },
+{ 0xCAAA, 0xCAAA, 0xCAAA },
+{ 0xCAAB, 0xCAAB, 0xCAAB },
+{ 0xCAAC, 0xCAAC, 0xCAAC },
+{ 0xCAAD, 0xCAAD, 0xCAAD },
+{ 0xCAAE, 0xCAAE, 0xCAAE },
+{ 0xCAAF, 0xCAAF, 0xCAAF },
+{ 0xCAB0, 0xCAB0, 0xCAB0 },
+{ 0xCAB1, 0xCAB1, 0xCAB1 },
+{ 0xCAB2, 0xCAB2, 0xCAB2 },
+{ 0xCAB3, 0xCAB3, 0xCAB3 },
+{ 0xCAB4, 0xCAB4, 0xCAB4 },
+{ 0xCAB5, 0xCAB5, 0xCAB5 },
+{ 0xCAB6, 0xCAB6, 0xCAB6 },
+{ 0xCAB7, 0xCAB7, 0xCAB7 },
+{ 0xCAB8, 0xCAB8, 0xCAB8 },
+{ 0xCAB9, 0xCAB9, 0xCAB9 },
+{ 0xCABA, 0xCABA, 0xCABA },
+{ 0xCABB, 0xCABB, 0xCABB },
+{ 0xCABC, 0xCABC, 0xCABC },
+{ 0xCABD, 0xCABD, 0xCABD },
+{ 0xCABE, 0xCABE, 0xCABE },
+{ 0xCABF, 0xCABF, 0xCABF },
+{ 0xCAC0, 0xCAC0, 0xCAC0 },
+{ 0xCAC1, 0xCAC1, 0xCAC1 },
+{ 0xCAC2, 0xCAC2, 0xCAC2 },
+{ 0xCAC3, 0xCAC3, 0xCAC3 },
+{ 0xCAC4, 0xCAC4, 0xCAC4 },
+{ 0xCAC5, 0xCAC5, 0xCAC5 },
+{ 0xCAC6, 0xCAC6, 0xCAC6 },
+{ 0xCAC7, 0xCAC7, 0xCAC7 },
+{ 0xCAC8, 0xCAC8, 0xCAC8 },
+{ 0xCAC9, 0xCAC9, 0xCAC9 },
+{ 0xCACA, 0xCACA, 0xCACA },
+{ 0xCACB, 0xCACB, 0xCACB },
+{ 0xCACC, 0xCACC, 0xCACC },
+{ 0xCACD, 0xCACD, 0xCACD },
+{ 0xCACE, 0xCACE, 0xCACE },
+{ 0xCACF, 0xCACF, 0xCACF },
+{ 0xCAD0, 0xCAD0, 0xCAD0 },
+{ 0xCAD1, 0xCAD1, 0xCAD1 },
+{ 0xCAD2, 0xCAD2, 0xCAD2 },
+{ 0xCAD3, 0xCAD3, 0xCAD3 },
+{ 0xCAD4, 0xCAD4, 0xCAD4 },
+{ 0xCAD5, 0xCAD5, 0xCAD5 },
+{ 0xCAD6, 0xCAD6, 0xCAD6 },
+{ 0xCAD7, 0xCAD7, 0xCAD7 },
+{ 0xCAD8, 0xCAD8, 0xCAD8 },
+{ 0xCAD9, 0xCAD9, 0xCAD9 },
+{ 0xCADA, 0xCADA, 0xCADA },
+{ 0xCADB, 0xCADB, 0xCADB },
+{ 0xCADC, 0xCADC, 0xCADC },
+{ 0xCADD, 0xCADD, 0xCADD },
+{ 0xCADE, 0xCADE, 0xCADE },
+{ 0xCADF, 0xCADF, 0xCADF },
+{ 0xCAE0, 0xCAE0, 0xCAE0 },
+{ 0xCAE1, 0xCAE1, 0xCAE1 },
+{ 0xCAE2, 0xCAE2, 0xCAE2 },
+{ 0xCAE3, 0xCAE3, 0xCAE3 },
+{ 0xCAE4, 0xCAE4, 0xCAE4 },
+{ 0xCAE5, 0xCAE5, 0xCAE5 },
+{ 0xCAE6, 0xCAE6, 0xCAE6 },
+{ 0xCAE7, 0xCAE7, 0xCAE7 },
+{ 0xCAE8, 0xCAE8, 0xCAE8 },
+{ 0xCAE9, 0xCAE9, 0xCAE9 },
+{ 0xCAEA, 0xCAEA, 0xCAEA },
+{ 0xCAEB, 0xCAEB, 0xCAEB },
+{ 0xCAEC, 0xCAEC, 0xCAEC },
+{ 0xCAED, 0xCAED, 0xCAED },
+{ 0xCAEE, 0xCAEE, 0xCAEE },
+{ 0xCAEF, 0xCAEF, 0xCAEF },
+{ 0xCAF0, 0xCAF0, 0xCAF0 },
+{ 0xCAF1, 0xCAF1, 0xCAF1 },
+{ 0xCAF2, 0xCAF2, 0xCAF2 },
+{ 0xCAF3, 0xCAF3, 0xCAF3 },
+{ 0xCAF4, 0xCAF4, 0xCAF4 },
+{ 0xCAF5, 0xCAF5, 0xCAF5 },
+{ 0xCAF6, 0xCAF6, 0xCAF6 },
+{ 0xCAF7, 0xCAF7, 0xCAF7 },
+{ 0xCAF8, 0xCAF8, 0xCAF8 },
+{ 0xCAF9, 0xCAF9, 0xCAF9 },
+{ 0xCAFA, 0xCAFA, 0xCAFA },
+{ 0xCAFB, 0xCAFB, 0xCAFB },
+{ 0xCAFC, 0xCAFC, 0xCAFC },
+{ 0xCAFD, 0xCAFD, 0xCAFD },
+{ 0xCAFE, 0xCAFE, 0xCAFE },
+{ 0xCAFF, 0xCAFF, 0xCAFF },
+{ 0xCB00, 0xCB00, 0xCB00 },
+{ 0xCB01, 0xCB01, 0xCB01 },
+{ 0xCB02, 0xCB02, 0xCB02 },
+{ 0xCB03, 0xCB03, 0xCB03 },
+{ 0xCB04, 0xCB04, 0xCB04 },
+{ 0xCB05, 0xCB05, 0xCB05 },
+{ 0xCB06, 0xCB06, 0xCB06 },
+{ 0xCB07, 0xCB07, 0xCB07 },
+{ 0xCB08, 0xCB08, 0xCB08 },
+{ 0xCB09, 0xCB09, 0xCB09 },
+{ 0xCB0A, 0xCB0A, 0xCB0A },
+{ 0xCB0B, 0xCB0B, 0xCB0B },
+{ 0xCB0C, 0xCB0C, 0xCB0C },
+{ 0xCB0D, 0xCB0D, 0xCB0D },
+{ 0xCB0E, 0xCB0E, 0xCB0E },
+{ 0xCB0F, 0xCB0F, 0xCB0F },
+{ 0xCB10, 0xCB10, 0xCB10 },
+{ 0xCB11, 0xCB11, 0xCB11 },
+{ 0xCB12, 0xCB12, 0xCB12 },
+{ 0xCB13, 0xCB13, 0xCB13 },
+{ 0xCB14, 0xCB14, 0xCB14 },
+{ 0xCB15, 0xCB15, 0xCB15 },
+{ 0xCB16, 0xCB16, 0xCB16 },
+{ 0xCB17, 0xCB17, 0xCB17 },
+{ 0xCB18, 0xCB18, 0xCB18 },
+{ 0xCB19, 0xCB19, 0xCB19 },
+{ 0xCB1A, 0xCB1A, 0xCB1A },
+{ 0xCB1B, 0xCB1B, 0xCB1B },
+{ 0xCB1C, 0xCB1C, 0xCB1C },
+{ 0xCB1D, 0xCB1D, 0xCB1D },
+{ 0xCB1E, 0xCB1E, 0xCB1E },
+{ 0xCB1F, 0xCB1F, 0xCB1F },
+{ 0xCB20, 0xCB20, 0xCB20 },
+{ 0xCB21, 0xCB21, 0xCB21 },
+{ 0xCB22, 0xCB22, 0xCB22 },
+{ 0xCB23, 0xCB23, 0xCB23 },
+{ 0xCB24, 0xCB24, 0xCB24 },
+{ 0xCB25, 0xCB25, 0xCB25 },
+{ 0xCB26, 0xCB26, 0xCB26 },
+{ 0xCB27, 0xCB27, 0xCB27 },
+{ 0xCB28, 0xCB28, 0xCB28 },
+{ 0xCB29, 0xCB29, 0xCB29 },
+{ 0xCB2A, 0xCB2A, 0xCB2A },
+{ 0xCB2B, 0xCB2B, 0xCB2B },
+{ 0xCB2C, 0xCB2C, 0xCB2C },
+{ 0xCB2D, 0xCB2D, 0xCB2D },
+{ 0xCB2E, 0xCB2E, 0xCB2E },
+{ 0xCB2F, 0xCB2F, 0xCB2F },
+{ 0xCB30, 0xCB30, 0xCB30 },
+{ 0xCB31, 0xCB31, 0xCB31 },
+{ 0xCB32, 0xCB32, 0xCB32 },
+{ 0xCB33, 0xCB33, 0xCB33 },
+{ 0xCB34, 0xCB34, 0xCB34 },
+{ 0xCB35, 0xCB35, 0xCB35 },
+{ 0xCB36, 0xCB36, 0xCB36 },
+{ 0xCB37, 0xCB37, 0xCB37 },
+{ 0xCB38, 0xCB38, 0xCB38 },
+{ 0xCB39, 0xCB39, 0xCB39 },
+{ 0xCB3A, 0xCB3A, 0xCB3A },
+{ 0xCB3B, 0xCB3B, 0xCB3B },
+{ 0xCB3C, 0xCB3C, 0xCB3C },
+{ 0xCB3D, 0xCB3D, 0xCB3D },
+{ 0xCB3E, 0xCB3E, 0xCB3E },
+{ 0xCB3F, 0xCB3F, 0xCB3F },
+{ 0xCB40, 0xCB40, 0xCB40 },
+{ 0xCB41, 0xCB41, 0xCB41 },
+{ 0xCB42, 0xCB42, 0xCB42 },
+{ 0xCB43, 0xCB43, 0xCB43 },
+{ 0xCB44, 0xCB44, 0xCB44 },
+{ 0xCB45, 0xCB45, 0xCB45 },
+{ 0xCB46, 0xCB46, 0xCB46 },
+{ 0xCB47, 0xCB47, 0xCB47 },
+{ 0xCB48, 0xCB48, 0xCB48 },
+{ 0xCB49, 0xCB49, 0xCB49 },
+{ 0xCB4A, 0xCB4A, 0xCB4A },
+{ 0xCB4B, 0xCB4B, 0xCB4B },
+{ 0xCB4C, 0xCB4C, 0xCB4C },
+{ 0xCB4D, 0xCB4D, 0xCB4D },
+{ 0xCB4E, 0xCB4E, 0xCB4E },
+{ 0xCB4F, 0xCB4F, 0xCB4F },
+{ 0xCB50, 0xCB50, 0xCB50 },
+{ 0xCB51, 0xCB51, 0xCB51 },
+{ 0xCB52, 0xCB52, 0xCB52 },
+{ 0xCB53, 0xCB53, 0xCB53 },
+{ 0xCB54, 0xCB54, 0xCB54 },
+{ 0xCB55, 0xCB55, 0xCB55 },
+{ 0xCB56, 0xCB56, 0xCB56 },
+{ 0xCB57, 0xCB57, 0xCB57 },
+{ 0xCB58, 0xCB58, 0xCB58 },
+{ 0xCB59, 0xCB59, 0xCB59 },
+{ 0xCB5A, 0xCB5A, 0xCB5A },
+{ 0xCB5B, 0xCB5B, 0xCB5B },
+{ 0xCB5C, 0xCB5C, 0xCB5C },
+{ 0xCB5D, 0xCB5D, 0xCB5D },
+{ 0xCB5E, 0xCB5E, 0xCB5E },
+{ 0xCB5F, 0xCB5F, 0xCB5F },
+{ 0xCB60, 0xCB60, 0xCB60 },
+{ 0xCB61, 0xCB61, 0xCB61 },
+{ 0xCB62, 0xCB62, 0xCB62 },
+{ 0xCB63, 0xCB63, 0xCB63 },
+{ 0xCB64, 0xCB64, 0xCB64 },
+{ 0xCB65, 0xCB65, 0xCB65 },
+{ 0xCB66, 0xCB66, 0xCB66 },
+{ 0xCB67, 0xCB67, 0xCB67 },
+{ 0xCB68, 0xCB68, 0xCB68 },
+{ 0xCB69, 0xCB69, 0xCB69 },
+{ 0xCB6A, 0xCB6A, 0xCB6A },
+{ 0xCB6B, 0xCB6B, 0xCB6B },
+{ 0xCB6C, 0xCB6C, 0xCB6C },
+{ 0xCB6D, 0xCB6D, 0xCB6D },
+{ 0xCB6E, 0xCB6E, 0xCB6E },
+{ 0xCB6F, 0xCB6F, 0xCB6F },
+{ 0xCB70, 0xCB70, 0xCB70 },
+{ 0xCB71, 0xCB71, 0xCB71 },
+{ 0xCB72, 0xCB72, 0xCB72 },
+{ 0xCB73, 0xCB73, 0xCB73 },
+{ 0xCB74, 0xCB74, 0xCB74 },
+{ 0xCB75, 0xCB75, 0xCB75 },
+{ 0xCB76, 0xCB76, 0xCB76 },
+{ 0xCB77, 0xCB77, 0xCB77 },
+{ 0xCB78, 0xCB78, 0xCB78 },
+{ 0xCB79, 0xCB79, 0xCB79 },
+{ 0xCB7A, 0xCB7A, 0xCB7A },
+{ 0xCB7B, 0xCB7B, 0xCB7B },
+{ 0xCB7C, 0xCB7C, 0xCB7C },
+{ 0xCB7D, 0xCB7D, 0xCB7D },
+{ 0xCB7E, 0xCB7E, 0xCB7E },
+{ 0xCB7F, 0xCB7F, 0xCB7F },
+{ 0xCB80, 0xCB80, 0xCB80 },
+{ 0xCB81, 0xCB81, 0xCB81 },
+{ 0xCB82, 0xCB82, 0xCB82 },
+{ 0xCB83, 0xCB83, 0xCB83 },
+{ 0xCB84, 0xCB84, 0xCB84 },
+{ 0xCB85, 0xCB85, 0xCB85 },
+{ 0xCB86, 0xCB86, 0xCB86 },
+{ 0xCB87, 0xCB87, 0xCB87 },
+{ 0xCB88, 0xCB88, 0xCB88 },
+{ 0xCB89, 0xCB89, 0xCB89 },
+{ 0xCB8A, 0xCB8A, 0xCB8A },
+{ 0xCB8B, 0xCB8B, 0xCB8B },
+{ 0xCB8C, 0xCB8C, 0xCB8C },
+{ 0xCB8D, 0xCB8D, 0xCB8D },
+{ 0xCB8E, 0xCB8E, 0xCB8E },
+{ 0xCB8F, 0xCB8F, 0xCB8F },
+{ 0xCB90, 0xCB90, 0xCB90 },
+{ 0xCB91, 0xCB91, 0xCB91 },
+{ 0xCB92, 0xCB92, 0xCB92 },
+{ 0xCB93, 0xCB93, 0xCB93 },
+{ 0xCB94, 0xCB94, 0xCB94 },
+{ 0xCB95, 0xCB95, 0xCB95 },
+{ 0xCB96, 0xCB96, 0xCB96 },
+{ 0xCB97, 0xCB97, 0xCB97 },
+{ 0xCB98, 0xCB98, 0xCB98 },
+{ 0xCB99, 0xCB99, 0xCB99 },
+{ 0xCB9A, 0xCB9A, 0xCB9A },
+{ 0xCB9B, 0xCB9B, 0xCB9B },
+{ 0xCB9C, 0xCB9C, 0xCB9C },
+{ 0xCB9D, 0xCB9D, 0xCB9D },
+{ 0xCB9E, 0xCB9E, 0xCB9E },
+{ 0xCB9F, 0xCB9F, 0xCB9F },
+{ 0xCBA0, 0xCBA0, 0xCBA0 },
+{ 0xCBA1, 0xCBA1, 0xCBA1 },
+{ 0xCBA2, 0xCBA2, 0xCBA2 },
+{ 0xCBA3, 0xCBA3, 0xCBA3 },
+{ 0xCBA4, 0xCBA4, 0xCBA4 },
+{ 0xCBA5, 0xCBA5, 0xCBA5 },
+{ 0xCBA6, 0xCBA6, 0xCBA6 },
+{ 0xCBA7, 0xCBA7, 0xCBA7 },
+{ 0xCBA8, 0xCBA8, 0xCBA8 },
+{ 0xCBA9, 0xCBA9, 0xCBA9 },
+{ 0xCBAA, 0xCBAA, 0xCBAA },
+{ 0xCBAB, 0xCBAB, 0xCBAB },
+{ 0xCBAC, 0xCBAC, 0xCBAC },
+{ 0xCBAD, 0xCBAD, 0xCBAD },
+{ 0xCBAE, 0xCBAE, 0xCBAE },
+{ 0xCBAF, 0xCBAF, 0xCBAF },
+{ 0xCBB0, 0xCBB0, 0xCBB0 },
+{ 0xCBB1, 0xCBB1, 0xCBB1 },
+{ 0xCBB2, 0xCBB2, 0xCBB2 },
+{ 0xCBB3, 0xCBB3, 0xCBB3 },
+{ 0xCBB4, 0xCBB4, 0xCBB4 },
+{ 0xCBB5, 0xCBB5, 0xCBB5 },
+{ 0xCBB6, 0xCBB6, 0xCBB6 },
+{ 0xCBB7, 0xCBB7, 0xCBB7 },
+{ 0xCBB8, 0xCBB8, 0xCBB8 },
+{ 0xCBB9, 0xCBB9, 0xCBB9 },
+{ 0xCBBA, 0xCBBA, 0xCBBA },
+{ 0xCBBB, 0xCBBB, 0xCBBB },
+{ 0xCBBC, 0xCBBC, 0xCBBC },
+{ 0xCBBD, 0xCBBD, 0xCBBD },
+{ 0xCBBE, 0xCBBE, 0xCBBE },
+{ 0xCBBF, 0xCBBF, 0xCBBF },
+{ 0xCBC0, 0xCBC0, 0xCBC0 },
+{ 0xCBC1, 0xCBC1, 0xCBC1 },
+{ 0xCBC2, 0xCBC2, 0xCBC2 },
+{ 0xCBC3, 0xCBC3, 0xCBC3 },
+{ 0xCBC4, 0xCBC4, 0xCBC4 },
+{ 0xCBC5, 0xCBC5, 0xCBC5 },
+{ 0xCBC6, 0xCBC6, 0xCBC6 },
+{ 0xCBC7, 0xCBC7, 0xCBC7 },
+{ 0xCBC8, 0xCBC8, 0xCBC8 },
+{ 0xCBC9, 0xCBC9, 0xCBC9 },
+{ 0xCBCA, 0xCBCA, 0xCBCA },
+{ 0xCBCB, 0xCBCB, 0xCBCB },
+{ 0xCBCC, 0xCBCC, 0xCBCC },
+{ 0xCBCD, 0xCBCD, 0xCBCD },
+{ 0xCBCE, 0xCBCE, 0xCBCE },
+{ 0xCBCF, 0xCBCF, 0xCBCF },
+{ 0xCBD0, 0xCBD0, 0xCBD0 },
+{ 0xCBD1, 0xCBD1, 0xCBD1 },
+{ 0xCBD2, 0xCBD2, 0xCBD2 },
+{ 0xCBD3, 0xCBD3, 0xCBD3 },
+{ 0xCBD4, 0xCBD4, 0xCBD4 },
+{ 0xCBD5, 0xCBD5, 0xCBD5 },
+{ 0xCBD6, 0xCBD6, 0xCBD6 },
+{ 0xCBD7, 0xCBD7, 0xCBD7 },
+{ 0xCBD8, 0xCBD8, 0xCBD8 },
+{ 0xCBD9, 0xCBD9, 0xCBD9 },
+{ 0xCBDA, 0xCBDA, 0xCBDA },
+{ 0xCBDB, 0xCBDB, 0xCBDB },
+{ 0xCBDC, 0xCBDC, 0xCBDC },
+{ 0xCBDD, 0xCBDD, 0xCBDD },
+{ 0xCBDE, 0xCBDE, 0xCBDE },
+{ 0xCBDF, 0xCBDF, 0xCBDF },
+{ 0xCBE0, 0xCBE0, 0xCBE0 },
+{ 0xCBE1, 0xCBE1, 0xCBE1 },
+{ 0xCBE2, 0xCBE2, 0xCBE2 },
+{ 0xCBE3, 0xCBE3, 0xCBE3 },
+{ 0xCBE4, 0xCBE4, 0xCBE4 },
+{ 0xCBE5, 0xCBE5, 0xCBE5 },
+{ 0xCBE6, 0xCBE6, 0xCBE6 },
+{ 0xCBE7, 0xCBE7, 0xCBE7 },
+{ 0xCBE8, 0xCBE8, 0xCBE8 },
+{ 0xCBE9, 0xCBE9, 0xCBE9 },
+{ 0xCBEA, 0xCBEA, 0xCBEA },
+{ 0xCBEB, 0xCBEB, 0xCBEB },
+{ 0xCBEC, 0xCBEC, 0xCBEC },
+{ 0xCBED, 0xCBED, 0xCBED },
+{ 0xCBEE, 0xCBEE, 0xCBEE },
+{ 0xCBEF, 0xCBEF, 0xCBEF },
+{ 0xCBF0, 0xCBF0, 0xCBF0 },
+{ 0xCBF1, 0xCBF1, 0xCBF1 },
+{ 0xCBF2, 0xCBF2, 0xCBF2 },
+{ 0xCBF3, 0xCBF3, 0xCBF3 },
+{ 0xCBF4, 0xCBF4, 0xCBF4 },
+{ 0xCBF5, 0xCBF5, 0xCBF5 },
+{ 0xCBF6, 0xCBF6, 0xCBF6 },
+{ 0xCBF7, 0xCBF7, 0xCBF7 },
+{ 0xCBF8, 0xCBF8, 0xCBF8 },
+{ 0xCBF9, 0xCBF9, 0xCBF9 },
+{ 0xCBFA, 0xCBFA, 0xCBFA },
+{ 0xCBFB, 0xCBFB, 0xCBFB },
+{ 0xCBFC, 0xCBFC, 0xCBFC },
+{ 0xCBFD, 0xCBFD, 0xCBFD },
+{ 0xCBFE, 0xCBFE, 0xCBFE },
+{ 0xCBFF, 0xCBFF, 0xCBFF },
+{ 0xCC00, 0xCC00, 0xCC00 },
+{ 0xCC01, 0xCC01, 0xCC01 },
+{ 0xCC02, 0xCC02, 0xCC02 },
+{ 0xCC03, 0xCC03, 0xCC03 },
+{ 0xCC04, 0xCC04, 0xCC04 },
+{ 0xCC05, 0xCC05, 0xCC05 },
+{ 0xCC06, 0xCC06, 0xCC06 },
+{ 0xCC07, 0xCC07, 0xCC07 },
+{ 0xCC08, 0xCC08, 0xCC08 },
+{ 0xCC09, 0xCC09, 0xCC09 },
+{ 0xCC0A, 0xCC0A, 0xCC0A },
+{ 0xCC0B, 0xCC0B, 0xCC0B },
+{ 0xCC0C, 0xCC0C, 0xCC0C },
+{ 0xCC0D, 0xCC0D, 0xCC0D },
+{ 0xCC0E, 0xCC0E, 0xCC0E },
+{ 0xCC0F, 0xCC0F, 0xCC0F },
+{ 0xCC10, 0xCC10, 0xCC10 },
+{ 0xCC11, 0xCC11, 0xCC11 },
+{ 0xCC12, 0xCC12, 0xCC12 },
+{ 0xCC13, 0xCC13, 0xCC13 },
+{ 0xCC14, 0xCC14, 0xCC14 },
+{ 0xCC15, 0xCC15, 0xCC15 },
+{ 0xCC16, 0xCC16, 0xCC16 },
+{ 0xCC17, 0xCC17, 0xCC17 },
+{ 0xCC18, 0xCC18, 0xCC18 },
+{ 0xCC19, 0xCC19, 0xCC19 },
+{ 0xCC1A, 0xCC1A, 0xCC1A },
+{ 0xCC1B, 0xCC1B, 0xCC1B },
+{ 0xCC1C, 0xCC1C, 0xCC1C },
+{ 0xCC1D, 0xCC1D, 0xCC1D },
+{ 0xCC1E, 0xCC1E, 0xCC1E },
+{ 0xCC1F, 0xCC1F, 0xCC1F },
+{ 0xCC20, 0xCC20, 0xCC20 },
+{ 0xCC21, 0xCC21, 0xCC21 },
+{ 0xCC22, 0xCC22, 0xCC22 },
+{ 0xCC23, 0xCC23, 0xCC23 },
+{ 0xCC24, 0xCC24, 0xCC24 },
+{ 0xCC25, 0xCC25, 0xCC25 },
+{ 0xCC26, 0xCC26, 0xCC26 },
+{ 0xCC27, 0xCC27, 0xCC27 },
+{ 0xCC28, 0xCC28, 0xCC28 },
+{ 0xCC29, 0xCC29, 0xCC29 },
+{ 0xCC2A, 0xCC2A, 0xCC2A },
+{ 0xCC2B, 0xCC2B, 0xCC2B },
+{ 0xCC2C, 0xCC2C, 0xCC2C },
+{ 0xCC2D, 0xCC2D, 0xCC2D },
+{ 0xCC2E, 0xCC2E, 0xCC2E },
+{ 0xCC2F, 0xCC2F, 0xCC2F },
+{ 0xCC30, 0xCC30, 0xCC30 },
+{ 0xCC31, 0xCC31, 0xCC31 },
+{ 0xCC32, 0xCC32, 0xCC32 },
+{ 0xCC33, 0xCC33, 0xCC33 },
+{ 0xCC34, 0xCC34, 0xCC34 },
+{ 0xCC35, 0xCC35, 0xCC35 },
+{ 0xCC36, 0xCC36, 0xCC36 },
+{ 0xCC37, 0xCC37, 0xCC37 },
+{ 0xCC38, 0xCC38, 0xCC38 },
+{ 0xCC39, 0xCC39, 0xCC39 },
+{ 0xCC3A, 0xCC3A, 0xCC3A },
+{ 0xCC3B, 0xCC3B, 0xCC3B },
+{ 0xCC3C, 0xCC3C, 0xCC3C },
+{ 0xCC3D, 0xCC3D, 0xCC3D },
+{ 0xCC3E, 0xCC3E, 0xCC3E },
+{ 0xCC3F, 0xCC3F, 0xCC3F },
+{ 0xCC40, 0xCC40, 0xCC40 },
+{ 0xCC41, 0xCC41, 0xCC41 },
+{ 0xCC42, 0xCC42, 0xCC42 },
+{ 0xCC43, 0xCC43, 0xCC43 },
+{ 0xCC44, 0xCC44, 0xCC44 },
+{ 0xCC45, 0xCC45, 0xCC45 },
+{ 0xCC46, 0xCC46, 0xCC46 },
+{ 0xCC47, 0xCC47, 0xCC47 },
+{ 0xCC48, 0xCC48, 0xCC48 },
+{ 0xCC49, 0xCC49, 0xCC49 },
+{ 0xCC4A, 0xCC4A, 0xCC4A },
+{ 0xCC4B, 0xCC4B, 0xCC4B },
+{ 0xCC4C, 0xCC4C, 0xCC4C },
+{ 0xCC4D, 0xCC4D, 0xCC4D },
+{ 0xCC4E, 0xCC4E, 0xCC4E },
+{ 0xCC4F, 0xCC4F, 0xCC4F },
+{ 0xCC50, 0xCC50, 0xCC50 },
+{ 0xCC51, 0xCC51, 0xCC51 },
+{ 0xCC52, 0xCC52, 0xCC52 },
+{ 0xCC53, 0xCC53, 0xCC53 },
+{ 0xCC54, 0xCC54, 0xCC54 },
+{ 0xCC55, 0xCC55, 0xCC55 },
+{ 0xCC56, 0xCC56, 0xCC56 },
+{ 0xCC57, 0xCC57, 0xCC57 },
+{ 0xCC58, 0xCC58, 0xCC58 },
+{ 0xCC59, 0xCC59, 0xCC59 },
+{ 0xCC5A, 0xCC5A, 0xCC5A },
+{ 0xCC5B, 0xCC5B, 0xCC5B },
+{ 0xCC5C, 0xCC5C, 0xCC5C },
+{ 0xCC5D, 0xCC5D, 0xCC5D },
+{ 0xCC5E, 0xCC5E, 0xCC5E },
+{ 0xCC5F, 0xCC5F, 0xCC5F },
+{ 0xCC60, 0xCC60, 0xCC60 },
+{ 0xCC61, 0xCC61, 0xCC61 },
+{ 0xCC62, 0xCC62, 0xCC62 },
+{ 0xCC63, 0xCC63, 0xCC63 },
+{ 0xCC64, 0xCC64, 0xCC64 },
+{ 0xCC65, 0xCC65, 0xCC65 },
+{ 0xCC66, 0xCC66, 0xCC66 },
+{ 0xCC67, 0xCC67, 0xCC67 },
+{ 0xCC68, 0xCC68, 0xCC68 },
+{ 0xCC69, 0xCC69, 0xCC69 },
+{ 0xCC6A, 0xCC6A, 0xCC6A },
+{ 0xCC6B, 0xCC6B, 0xCC6B },
+{ 0xCC6C, 0xCC6C, 0xCC6C },
+{ 0xCC6D, 0xCC6D, 0xCC6D },
+{ 0xCC6E, 0xCC6E, 0xCC6E },
+{ 0xCC6F, 0xCC6F, 0xCC6F },
+{ 0xCC70, 0xCC70, 0xCC70 },
+{ 0xCC71, 0xCC71, 0xCC71 },
+{ 0xCC72, 0xCC72, 0xCC72 },
+{ 0xCC73, 0xCC73, 0xCC73 },
+{ 0xCC74, 0xCC74, 0xCC74 },
+{ 0xCC75, 0xCC75, 0xCC75 },
+{ 0xCC76, 0xCC76, 0xCC76 },
+{ 0xCC77, 0xCC77, 0xCC77 },
+{ 0xCC78, 0xCC78, 0xCC78 },
+{ 0xCC79, 0xCC79, 0xCC79 },
+{ 0xCC7A, 0xCC7A, 0xCC7A },
+{ 0xCC7B, 0xCC7B, 0xCC7B },
+{ 0xCC7C, 0xCC7C, 0xCC7C },
+{ 0xCC7D, 0xCC7D, 0xCC7D },
+{ 0xCC7E, 0xCC7E, 0xCC7E },
+{ 0xCC7F, 0xCC7F, 0xCC7F },
+{ 0xCC80, 0xCC80, 0xCC80 },
+{ 0xCC81, 0xCC81, 0xCC81 },
+{ 0xCC82, 0xCC82, 0xCC82 },
+{ 0xCC83, 0xCC83, 0xCC83 },
+{ 0xCC84, 0xCC84, 0xCC84 },
+{ 0xCC85, 0xCC85, 0xCC85 },
+{ 0xCC86, 0xCC86, 0xCC86 },
+{ 0xCC87, 0xCC87, 0xCC87 },
+{ 0xCC88, 0xCC88, 0xCC88 },
+{ 0xCC89, 0xCC89, 0xCC89 },
+{ 0xCC8A, 0xCC8A, 0xCC8A },
+{ 0xCC8B, 0xCC8B, 0xCC8B },
+{ 0xCC8C, 0xCC8C, 0xCC8C },
+{ 0xCC8D, 0xCC8D, 0xCC8D },
+{ 0xCC8E, 0xCC8E, 0xCC8E },
+{ 0xCC8F, 0xCC8F, 0xCC8F },
+{ 0xCC90, 0xCC90, 0xCC90 },
+{ 0xCC91, 0xCC91, 0xCC91 },
+{ 0xCC92, 0xCC92, 0xCC92 },
+{ 0xCC93, 0xCC93, 0xCC93 },
+{ 0xCC94, 0xCC94, 0xCC94 },
+{ 0xCC95, 0xCC95, 0xCC95 },
+{ 0xCC96, 0xCC96, 0xCC96 },
+{ 0xCC97, 0xCC97, 0xCC97 },
+{ 0xCC98, 0xCC98, 0xCC98 },
+{ 0xCC99, 0xCC99, 0xCC99 },
+{ 0xCC9A, 0xCC9A, 0xCC9A },
+{ 0xCC9B, 0xCC9B, 0xCC9B },
+{ 0xCC9C, 0xCC9C, 0xCC9C },
+{ 0xCC9D, 0xCC9D, 0xCC9D },
+{ 0xCC9E, 0xCC9E, 0xCC9E },
+{ 0xCC9F, 0xCC9F, 0xCC9F },
+{ 0xCCA0, 0xCCA0, 0xCCA0 },
+{ 0xCCA1, 0xCCA1, 0xCCA1 },
+{ 0xCCA2, 0xCCA2, 0xCCA2 },
+{ 0xCCA3, 0xCCA3, 0xCCA3 },
+{ 0xCCA4, 0xCCA4, 0xCCA4 },
+{ 0xCCA5, 0xCCA5, 0xCCA5 },
+{ 0xCCA6, 0xCCA6, 0xCCA6 },
+{ 0xCCA7, 0xCCA7, 0xCCA7 },
+{ 0xCCA8, 0xCCA8, 0xCCA8 },
+{ 0xCCA9, 0xCCA9, 0xCCA9 },
+{ 0xCCAA, 0xCCAA, 0xCCAA },
+{ 0xCCAB, 0xCCAB, 0xCCAB },
+{ 0xCCAC, 0xCCAC, 0xCCAC },
+{ 0xCCAD, 0xCCAD, 0xCCAD },
+{ 0xCCAE, 0xCCAE, 0xCCAE },
+{ 0xCCAF, 0xCCAF, 0xCCAF },
+{ 0xCCB0, 0xCCB0, 0xCCB0 },
+{ 0xCCB1, 0xCCB1, 0xCCB1 },
+{ 0xCCB2, 0xCCB2, 0xCCB2 },
+{ 0xCCB3, 0xCCB3, 0xCCB3 },
+{ 0xCCB4, 0xCCB4, 0xCCB4 },
+{ 0xCCB5, 0xCCB5, 0xCCB5 },
+{ 0xCCB6, 0xCCB6, 0xCCB6 },
+{ 0xCCB7, 0xCCB7, 0xCCB7 },
+{ 0xCCB8, 0xCCB8, 0xCCB8 },
+{ 0xCCB9, 0xCCB9, 0xCCB9 },
+{ 0xCCBA, 0xCCBA, 0xCCBA },
+{ 0xCCBB, 0xCCBB, 0xCCBB },
+{ 0xCCBC, 0xCCBC, 0xCCBC },
+{ 0xCCBD, 0xCCBD, 0xCCBD },
+{ 0xCCBE, 0xCCBE, 0xCCBE },
+{ 0xCCBF, 0xCCBF, 0xCCBF },
+{ 0xCCC0, 0xCCC0, 0xCCC0 },
+{ 0xCCC1, 0xCCC1, 0xCCC1 },
+{ 0xCCC2, 0xCCC2, 0xCCC2 },
+{ 0xCCC3, 0xCCC3, 0xCCC3 },
+{ 0xCCC4, 0xCCC4, 0xCCC4 },
+{ 0xCCC5, 0xCCC5, 0xCCC5 },
+{ 0xCCC6, 0xCCC6, 0xCCC6 },
+{ 0xCCC7, 0xCCC7, 0xCCC7 },
+{ 0xCCC8, 0xCCC8, 0xCCC8 },
+{ 0xCCC9, 0xCCC9, 0xCCC9 },
+{ 0xCCCA, 0xCCCA, 0xCCCA },
+{ 0xCCCB, 0xCCCB, 0xCCCB },
+{ 0xCCCC, 0xCCCC, 0xCCCC },
+{ 0xCCCD, 0xCCCD, 0xCCCD },
+{ 0xCCCE, 0xCCCE, 0xCCCE },
+{ 0xCCCF, 0xCCCF, 0xCCCF },
+{ 0xCCD0, 0xCCD0, 0xCCD0 },
+{ 0xCCD1, 0xCCD1, 0xCCD1 },
+{ 0xCCD2, 0xCCD2, 0xCCD2 },
+{ 0xCCD3, 0xCCD3, 0xCCD3 },
+{ 0xCCD4, 0xCCD4, 0xCCD4 },
+{ 0xCCD5, 0xCCD5, 0xCCD5 },
+{ 0xCCD6, 0xCCD6, 0xCCD6 },
+{ 0xCCD7, 0xCCD7, 0xCCD7 },
+{ 0xCCD8, 0xCCD8, 0xCCD8 },
+{ 0xCCD9, 0xCCD9, 0xCCD9 },
+{ 0xCCDA, 0xCCDA, 0xCCDA },
+{ 0xCCDB, 0xCCDB, 0xCCDB },
+{ 0xCCDC, 0xCCDC, 0xCCDC },
+{ 0xCCDD, 0xCCDD, 0xCCDD },
+{ 0xCCDE, 0xCCDE, 0xCCDE },
+{ 0xCCDF, 0xCCDF, 0xCCDF },
+{ 0xCCE0, 0xCCE0, 0xCCE0 },
+{ 0xCCE1, 0xCCE1, 0xCCE1 },
+{ 0xCCE2, 0xCCE2, 0xCCE2 },
+{ 0xCCE3, 0xCCE3, 0xCCE3 },
+{ 0xCCE4, 0xCCE4, 0xCCE4 },
+{ 0xCCE5, 0xCCE5, 0xCCE5 },
+{ 0xCCE6, 0xCCE6, 0xCCE6 },
+{ 0xCCE7, 0xCCE7, 0xCCE7 },
+{ 0xCCE8, 0xCCE8, 0xCCE8 },
+{ 0xCCE9, 0xCCE9, 0xCCE9 },
+{ 0xCCEA, 0xCCEA, 0xCCEA },
+{ 0xCCEB, 0xCCEB, 0xCCEB },
+{ 0xCCEC, 0xCCEC, 0xCCEC },
+{ 0xCCED, 0xCCED, 0xCCED },
+{ 0xCCEE, 0xCCEE, 0xCCEE },
+{ 0xCCEF, 0xCCEF, 0xCCEF },
+{ 0xCCF0, 0xCCF0, 0xCCF0 },
+{ 0xCCF1, 0xCCF1, 0xCCF1 },
+{ 0xCCF2, 0xCCF2, 0xCCF2 },
+{ 0xCCF3, 0xCCF3, 0xCCF3 },
+{ 0xCCF4, 0xCCF4, 0xCCF4 },
+{ 0xCCF5, 0xCCF5, 0xCCF5 },
+{ 0xCCF6, 0xCCF6, 0xCCF6 },
+{ 0xCCF7, 0xCCF7, 0xCCF7 },
+{ 0xCCF8, 0xCCF8, 0xCCF8 },
+{ 0xCCF9, 0xCCF9, 0xCCF9 },
+{ 0xCCFA, 0xCCFA, 0xCCFA },
+{ 0xCCFB, 0xCCFB, 0xCCFB },
+{ 0xCCFC, 0xCCFC, 0xCCFC },
+{ 0xCCFD, 0xCCFD, 0xCCFD },
+{ 0xCCFE, 0xCCFE, 0xCCFE },
+{ 0xCCFF, 0xCCFF, 0xCCFF },
+{ 0xCD00, 0xCD00, 0xCD00 },
+{ 0xCD01, 0xCD01, 0xCD01 },
+{ 0xCD02, 0xCD02, 0xCD02 },
+{ 0xCD03, 0xCD03, 0xCD03 },
+{ 0xCD04, 0xCD04, 0xCD04 },
+{ 0xCD05, 0xCD05, 0xCD05 },
+{ 0xCD06, 0xCD06, 0xCD06 },
+{ 0xCD07, 0xCD07, 0xCD07 },
+{ 0xCD08, 0xCD08, 0xCD08 },
+{ 0xCD09, 0xCD09, 0xCD09 },
+{ 0xCD0A, 0xCD0A, 0xCD0A },
+{ 0xCD0B, 0xCD0B, 0xCD0B },
+{ 0xCD0C, 0xCD0C, 0xCD0C },
+{ 0xCD0D, 0xCD0D, 0xCD0D },
+{ 0xCD0E, 0xCD0E, 0xCD0E },
+{ 0xCD0F, 0xCD0F, 0xCD0F },
+{ 0xCD10, 0xCD10, 0xCD10 },
+{ 0xCD11, 0xCD11, 0xCD11 },
+{ 0xCD12, 0xCD12, 0xCD12 },
+{ 0xCD13, 0xCD13, 0xCD13 },
+{ 0xCD14, 0xCD14, 0xCD14 },
+{ 0xCD15, 0xCD15, 0xCD15 },
+{ 0xCD16, 0xCD16, 0xCD16 },
+{ 0xCD17, 0xCD17, 0xCD17 },
+{ 0xCD18, 0xCD18, 0xCD18 },
+{ 0xCD19, 0xCD19, 0xCD19 },
+{ 0xCD1A, 0xCD1A, 0xCD1A },
+{ 0xCD1B, 0xCD1B, 0xCD1B },
+{ 0xCD1C, 0xCD1C, 0xCD1C },
+{ 0xCD1D, 0xCD1D, 0xCD1D },
+{ 0xCD1E, 0xCD1E, 0xCD1E },
+{ 0xCD1F, 0xCD1F, 0xCD1F },
+{ 0xCD20, 0xCD20, 0xCD20 },
+{ 0xCD21, 0xCD21, 0xCD21 },
+{ 0xCD22, 0xCD22, 0xCD22 },
+{ 0xCD23, 0xCD23, 0xCD23 },
+{ 0xCD24, 0xCD24, 0xCD24 },
+{ 0xCD25, 0xCD25, 0xCD25 },
+{ 0xCD26, 0xCD26, 0xCD26 },
+{ 0xCD27, 0xCD27, 0xCD27 },
+{ 0xCD28, 0xCD28, 0xCD28 },
+{ 0xCD29, 0xCD29, 0xCD29 },
+{ 0xCD2A, 0xCD2A, 0xCD2A },
+{ 0xCD2B, 0xCD2B, 0xCD2B },
+{ 0xCD2C, 0xCD2C, 0xCD2C },
+{ 0xCD2D, 0xCD2D, 0xCD2D },
+{ 0xCD2E, 0xCD2E, 0xCD2E },
+{ 0xCD2F, 0xCD2F, 0xCD2F },
+{ 0xCD30, 0xCD30, 0xCD30 },
+{ 0xCD31, 0xCD31, 0xCD31 },
+{ 0xCD32, 0xCD32, 0xCD32 },
+{ 0xCD33, 0xCD33, 0xCD33 },
+{ 0xCD34, 0xCD34, 0xCD34 },
+{ 0xCD35, 0xCD35, 0xCD35 },
+{ 0xCD36, 0xCD36, 0xCD36 },
+{ 0xCD37, 0xCD37, 0xCD37 },
+{ 0xCD38, 0xCD38, 0xCD38 },
+{ 0xCD39, 0xCD39, 0xCD39 },
+{ 0xCD3A, 0xCD3A, 0xCD3A },
+{ 0xCD3B, 0xCD3B, 0xCD3B },
+{ 0xCD3C, 0xCD3C, 0xCD3C },
+{ 0xCD3D, 0xCD3D, 0xCD3D },
+{ 0xCD3E, 0xCD3E, 0xCD3E },
+{ 0xCD3F, 0xCD3F, 0xCD3F },
+{ 0xCD40, 0xCD40, 0xCD40 },
+{ 0xCD41, 0xCD41, 0xCD41 },
+{ 0xCD42, 0xCD42, 0xCD42 },
+{ 0xCD43, 0xCD43, 0xCD43 },
+{ 0xCD44, 0xCD44, 0xCD44 },
+{ 0xCD45, 0xCD45, 0xCD45 },
+{ 0xCD46, 0xCD46, 0xCD46 },
+{ 0xCD47, 0xCD47, 0xCD47 },
+{ 0xCD48, 0xCD48, 0xCD48 },
+{ 0xCD49, 0xCD49, 0xCD49 },
+{ 0xCD4A, 0xCD4A, 0xCD4A },
+{ 0xCD4B, 0xCD4B, 0xCD4B },
+{ 0xCD4C, 0xCD4C, 0xCD4C },
+{ 0xCD4D, 0xCD4D, 0xCD4D },
+{ 0xCD4E, 0xCD4E, 0xCD4E },
+{ 0xCD4F, 0xCD4F, 0xCD4F },
+{ 0xCD50, 0xCD50, 0xCD50 },
+{ 0xCD51, 0xCD51, 0xCD51 },
+{ 0xCD52, 0xCD52, 0xCD52 },
+{ 0xCD53, 0xCD53, 0xCD53 },
+{ 0xCD54, 0xCD54, 0xCD54 },
+{ 0xCD55, 0xCD55, 0xCD55 },
+{ 0xCD56, 0xCD56, 0xCD56 },
+{ 0xCD57, 0xCD57, 0xCD57 },
+{ 0xCD58, 0xCD58, 0xCD58 },
+{ 0xCD59, 0xCD59, 0xCD59 },
+{ 0xCD5A, 0xCD5A, 0xCD5A },
+{ 0xCD5B, 0xCD5B, 0xCD5B },
+{ 0xCD5C, 0xCD5C, 0xCD5C },
+{ 0xCD5D, 0xCD5D, 0xCD5D },
+{ 0xCD5E, 0xCD5E, 0xCD5E },
+{ 0xCD5F, 0xCD5F, 0xCD5F },
+{ 0xCD60, 0xCD60, 0xCD60 },
+{ 0xCD61, 0xCD61, 0xCD61 },
+{ 0xCD62, 0xCD62, 0xCD62 },
+{ 0xCD63, 0xCD63, 0xCD63 },
+{ 0xCD64, 0xCD64, 0xCD64 },
+{ 0xCD65, 0xCD65, 0xCD65 },
+{ 0xCD66, 0xCD66, 0xCD66 },
+{ 0xCD67, 0xCD67, 0xCD67 },
+{ 0xCD68, 0xCD68, 0xCD68 },
+{ 0xCD69, 0xCD69, 0xCD69 },
+{ 0xCD6A, 0xCD6A, 0xCD6A },
+{ 0xCD6B, 0xCD6B, 0xCD6B },
+{ 0xCD6C, 0xCD6C, 0xCD6C },
+{ 0xCD6D, 0xCD6D, 0xCD6D },
+{ 0xCD6E, 0xCD6E, 0xCD6E },
+{ 0xCD6F, 0xCD6F, 0xCD6F },
+{ 0xCD70, 0xCD70, 0xCD70 },
+{ 0xCD71, 0xCD71, 0xCD71 },
+{ 0xCD72, 0xCD72, 0xCD72 },
+{ 0xCD73, 0xCD73, 0xCD73 },
+{ 0xCD74, 0xCD74, 0xCD74 },
+{ 0xCD75, 0xCD75, 0xCD75 },
+{ 0xCD76, 0xCD76, 0xCD76 },
+{ 0xCD77, 0xCD77, 0xCD77 },
+{ 0xCD78, 0xCD78, 0xCD78 },
+{ 0xCD79, 0xCD79, 0xCD79 },
+{ 0xCD7A, 0xCD7A, 0xCD7A },
+{ 0xCD7B, 0xCD7B, 0xCD7B },
+{ 0xCD7C, 0xCD7C, 0xCD7C },
+{ 0xCD7D, 0xCD7D, 0xCD7D },
+{ 0xCD7E, 0xCD7E, 0xCD7E },
+{ 0xCD7F, 0xCD7F, 0xCD7F },
+{ 0xCD80, 0xCD80, 0xCD80 },
+{ 0xCD81, 0xCD81, 0xCD81 },
+{ 0xCD82, 0xCD82, 0xCD82 },
+{ 0xCD83, 0xCD83, 0xCD83 },
+{ 0xCD84, 0xCD84, 0xCD84 },
+{ 0xCD85, 0xCD85, 0xCD85 },
+{ 0xCD86, 0xCD86, 0xCD86 },
+{ 0xCD87, 0xCD87, 0xCD87 },
+{ 0xCD88, 0xCD88, 0xCD88 },
+{ 0xCD89, 0xCD89, 0xCD89 },
+{ 0xCD8A, 0xCD8A, 0xCD8A },
+{ 0xCD8B, 0xCD8B, 0xCD8B },
+{ 0xCD8C, 0xCD8C, 0xCD8C },
+{ 0xCD8D, 0xCD8D, 0xCD8D },
+{ 0xCD8E, 0xCD8E, 0xCD8E },
+{ 0xCD8F, 0xCD8F, 0xCD8F },
+{ 0xCD90, 0xCD90, 0xCD90 },
+{ 0xCD91, 0xCD91, 0xCD91 },
+{ 0xCD92, 0xCD92, 0xCD92 },
+{ 0xCD93, 0xCD93, 0xCD93 },
+{ 0xCD94, 0xCD94, 0xCD94 },
+{ 0xCD95, 0xCD95, 0xCD95 },
+{ 0xCD96, 0xCD96, 0xCD96 },
+{ 0xCD97, 0xCD97, 0xCD97 },
+{ 0xCD98, 0xCD98, 0xCD98 },
+{ 0xCD99, 0xCD99, 0xCD99 },
+{ 0xCD9A, 0xCD9A, 0xCD9A },
+{ 0xCD9B, 0xCD9B, 0xCD9B },
+{ 0xCD9C, 0xCD9C, 0xCD9C },
+{ 0xCD9D, 0xCD9D, 0xCD9D },
+{ 0xCD9E, 0xCD9E, 0xCD9E },
+{ 0xCD9F, 0xCD9F, 0xCD9F },
+{ 0xCDA0, 0xCDA0, 0xCDA0 },
+{ 0xCDA1, 0xCDA1, 0xCDA1 },
+{ 0xCDA2, 0xCDA2, 0xCDA2 },
+{ 0xCDA3, 0xCDA3, 0xCDA3 },
+{ 0xCDA4, 0xCDA4, 0xCDA4 },
+{ 0xCDA5, 0xCDA5, 0xCDA5 },
+{ 0xCDA6, 0xCDA6, 0xCDA6 },
+{ 0xCDA7, 0xCDA7, 0xCDA7 },
+{ 0xCDA8, 0xCDA8, 0xCDA8 },
+{ 0xCDA9, 0xCDA9, 0xCDA9 },
+{ 0xCDAA, 0xCDAA, 0xCDAA },
+{ 0xCDAB, 0xCDAB, 0xCDAB },
+{ 0xCDAC, 0xCDAC, 0xCDAC },
+{ 0xCDAD, 0xCDAD, 0xCDAD },
+{ 0xCDAE, 0xCDAE, 0xCDAE },
+{ 0xCDAF, 0xCDAF, 0xCDAF },
+{ 0xCDB0, 0xCDB0, 0xCDB0 },
+{ 0xCDB1, 0xCDB1, 0xCDB1 },
+{ 0xCDB2, 0xCDB2, 0xCDB2 },
+{ 0xCDB3, 0xCDB3, 0xCDB3 },
+{ 0xCDB4, 0xCDB4, 0xCDB4 },
+{ 0xCDB5, 0xCDB5, 0xCDB5 },
+{ 0xCDB6, 0xCDB6, 0xCDB6 },
+{ 0xCDB7, 0xCDB7, 0xCDB7 },
+{ 0xCDB8, 0xCDB8, 0xCDB8 },
+{ 0xCDB9, 0xCDB9, 0xCDB9 },
+{ 0xCDBA, 0xCDBA, 0xCDBA },
+{ 0xCDBB, 0xCDBB, 0xCDBB },
+{ 0xCDBC, 0xCDBC, 0xCDBC },
+{ 0xCDBD, 0xCDBD, 0xCDBD },
+{ 0xCDBE, 0xCDBE, 0xCDBE },
+{ 0xCDBF, 0xCDBF, 0xCDBF },
+{ 0xCDC0, 0xCDC0, 0xCDC0 },
+{ 0xCDC1, 0xCDC1, 0xCDC1 },
+{ 0xCDC2, 0xCDC2, 0xCDC2 },
+{ 0xCDC3, 0xCDC3, 0xCDC3 },
+{ 0xCDC4, 0xCDC4, 0xCDC4 },
+{ 0xCDC5, 0xCDC5, 0xCDC5 },
+{ 0xCDC6, 0xCDC6, 0xCDC6 },
+{ 0xCDC7, 0xCDC7, 0xCDC7 },
+{ 0xCDC8, 0xCDC8, 0xCDC8 },
+{ 0xCDC9, 0xCDC9, 0xCDC9 },
+{ 0xCDCA, 0xCDCA, 0xCDCA },
+{ 0xCDCB, 0xCDCB, 0xCDCB },
+{ 0xCDCC, 0xCDCC, 0xCDCC },
+{ 0xCDCD, 0xCDCD, 0xCDCD },
+{ 0xCDCE, 0xCDCE, 0xCDCE },
+{ 0xCDCF, 0xCDCF, 0xCDCF },
+{ 0xCDD0, 0xCDD0, 0xCDD0 },
+{ 0xCDD1, 0xCDD1, 0xCDD1 },
+{ 0xCDD2, 0xCDD2, 0xCDD2 },
+{ 0xCDD3, 0xCDD3, 0xCDD3 },
+{ 0xCDD4, 0xCDD4, 0xCDD4 },
+{ 0xCDD5, 0xCDD5, 0xCDD5 },
+{ 0xCDD6, 0xCDD6, 0xCDD6 },
+{ 0xCDD7, 0xCDD7, 0xCDD7 },
+{ 0xCDD8, 0xCDD8, 0xCDD8 },
+{ 0xCDD9, 0xCDD9, 0xCDD9 },
+{ 0xCDDA, 0xCDDA, 0xCDDA },
+{ 0xCDDB, 0xCDDB, 0xCDDB },
+{ 0xCDDC, 0xCDDC, 0xCDDC },
+{ 0xCDDD, 0xCDDD, 0xCDDD },
+{ 0xCDDE, 0xCDDE, 0xCDDE },
+{ 0xCDDF, 0xCDDF, 0xCDDF },
+{ 0xCDE0, 0xCDE0, 0xCDE0 },
+{ 0xCDE1, 0xCDE1, 0xCDE1 },
+{ 0xCDE2, 0xCDE2, 0xCDE2 },
+{ 0xCDE3, 0xCDE3, 0xCDE3 },
+{ 0xCDE4, 0xCDE4, 0xCDE4 },
+{ 0xCDE5, 0xCDE5, 0xCDE5 },
+{ 0xCDE6, 0xCDE6, 0xCDE6 },
+{ 0xCDE7, 0xCDE7, 0xCDE7 },
+{ 0xCDE8, 0xCDE8, 0xCDE8 },
+{ 0xCDE9, 0xCDE9, 0xCDE9 },
+{ 0xCDEA, 0xCDEA, 0xCDEA },
+{ 0xCDEB, 0xCDEB, 0xCDEB },
+{ 0xCDEC, 0xCDEC, 0xCDEC },
+{ 0xCDED, 0xCDED, 0xCDED },
+{ 0xCDEE, 0xCDEE, 0xCDEE },
+{ 0xCDEF, 0xCDEF, 0xCDEF },
+{ 0xCDF0, 0xCDF0, 0xCDF0 },
+{ 0xCDF1, 0xCDF1, 0xCDF1 },
+{ 0xCDF2, 0xCDF2, 0xCDF2 },
+{ 0xCDF3, 0xCDF3, 0xCDF3 },
+{ 0xCDF4, 0xCDF4, 0xCDF4 },
+{ 0xCDF5, 0xCDF5, 0xCDF5 },
+{ 0xCDF6, 0xCDF6, 0xCDF6 },
+{ 0xCDF7, 0xCDF7, 0xCDF7 },
+{ 0xCDF8, 0xCDF8, 0xCDF8 },
+{ 0xCDF9, 0xCDF9, 0xCDF9 },
+{ 0xCDFA, 0xCDFA, 0xCDFA },
+{ 0xCDFB, 0xCDFB, 0xCDFB },
+{ 0xCDFC, 0xCDFC, 0xCDFC },
+{ 0xCDFD, 0xCDFD, 0xCDFD },
+{ 0xCDFE, 0xCDFE, 0xCDFE },
+{ 0xCDFF, 0xCDFF, 0xCDFF },
+{ 0xCE00, 0xCE00, 0xCE00 },
+{ 0xCE01, 0xCE01, 0xCE01 },
+{ 0xCE02, 0xCE02, 0xCE02 },
+{ 0xCE03, 0xCE03, 0xCE03 },
+{ 0xCE04, 0xCE04, 0xCE04 },
+{ 0xCE05, 0xCE05, 0xCE05 },
+{ 0xCE06, 0xCE06, 0xCE06 },
+{ 0xCE07, 0xCE07, 0xCE07 },
+{ 0xCE08, 0xCE08, 0xCE08 },
+{ 0xCE09, 0xCE09, 0xCE09 },
+{ 0xCE0A, 0xCE0A, 0xCE0A },
+{ 0xCE0B, 0xCE0B, 0xCE0B },
+{ 0xCE0C, 0xCE0C, 0xCE0C },
+{ 0xCE0D, 0xCE0D, 0xCE0D },
+{ 0xCE0E, 0xCE0E, 0xCE0E },
+{ 0xCE0F, 0xCE0F, 0xCE0F },
+{ 0xCE10, 0xCE10, 0xCE10 },
+{ 0xCE11, 0xCE11, 0xCE11 },
+{ 0xCE12, 0xCE12, 0xCE12 },
+{ 0xCE13, 0xCE13, 0xCE13 },
+{ 0xCE14, 0xCE14, 0xCE14 },
+{ 0xCE15, 0xCE15, 0xCE15 },
+{ 0xCE16, 0xCE16, 0xCE16 },
+{ 0xCE17, 0xCE17, 0xCE17 },
+{ 0xCE18, 0xCE18, 0xCE18 },
+{ 0xCE19, 0xCE19, 0xCE19 },
+{ 0xCE1A, 0xCE1A, 0xCE1A },
+{ 0xCE1B, 0xCE1B, 0xCE1B },
+{ 0xCE1C, 0xCE1C, 0xCE1C },
+{ 0xCE1D, 0xCE1D, 0xCE1D },
+{ 0xCE1E, 0xCE1E, 0xCE1E },
+{ 0xCE1F, 0xCE1F, 0xCE1F },
+{ 0xCE20, 0xCE20, 0xCE20 },
+{ 0xCE21, 0xCE21, 0xCE21 },
+{ 0xCE22, 0xCE22, 0xCE22 },
+{ 0xCE23, 0xCE23, 0xCE23 },
+{ 0xCE24, 0xCE24, 0xCE24 },
+{ 0xCE25, 0xCE25, 0xCE25 },
+{ 0xCE26, 0xCE26, 0xCE26 },
+{ 0xCE27, 0xCE27, 0xCE27 },
+{ 0xCE28, 0xCE28, 0xCE28 },
+{ 0xCE29, 0xCE29, 0xCE29 },
+{ 0xCE2A, 0xCE2A, 0xCE2A },
+{ 0xCE2B, 0xCE2B, 0xCE2B },
+{ 0xCE2C, 0xCE2C, 0xCE2C },
+{ 0xCE2D, 0xCE2D, 0xCE2D },
+{ 0xCE2E, 0xCE2E, 0xCE2E },
+{ 0xCE2F, 0xCE2F, 0xCE2F },
+{ 0xCE30, 0xCE30, 0xCE30 },
+{ 0xCE31, 0xCE31, 0xCE31 },
+{ 0xCE32, 0xCE32, 0xCE32 },
+{ 0xCE33, 0xCE33, 0xCE33 },
+{ 0xCE34, 0xCE34, 0xCE34 },
+{ 0xCE35, 0xCE35, 0xCE35 },
+{ 0xCE36, 0xCE36, 0xCE36 },
+{ 0xCE37, 0xCE37, 0xCE37 },
+{ 0xCE38, 0xCE38, 0xCE38 },
+{ 0xCE39, 0xCE39, 0xCE39 },
+{ 0xCE3A, 0xCE3A, 0xCE3A },
+{ 0xCE3B, 0xCE3B, 0xCE3B },
+{ 0xCE3C, 0xCE3C, 0xCE3C },
+{ 0xCE3D, 0xCE3D, 0xCE3D },
+{ 0xCE3E, 0xCE3E, 0xCE3E },
+{ 0xCE3F, 0xCE3F, 0xCE3F },
+{ 0xCE40, 0xCE40, 0xCE40 },
+{ 0xCE41, 0xCE41, 0xCE41 },
+{ 0xCE42, 0xCE42, 0xCE42 },
+{ 0xCE43, 0xCE43, 0xCE43 },
+{ 0xCE44, 0xCE44, 0xCE44 },
+{ 0xCE45, 0xCE45, 0xCE45 },
+{ 0xCE46, 0xCE46, 0xCE46 },
+{ 0xCE47, 0xCE47, 0xCE47 },
+{ 0xCE48, 0xCE48, 0xCE48 },
+{ 0xCE49, 0xCE49, 0xCE49 },
+{ 0xCE4A, 0xCE4A, 0xCE4A },
+{ 0xCE4B, 0xCE4B, 0xCE4B },
+{ 0xCE4C, 0xCE4C, 0xCE4C },
+{ 0xCE4D, 0xCE4D, 0xCE4D },
+{ 0xCE4E, 0xCE4E, 0xCE4E },
+{ 0xCE4F, 0xCE4F, 0xCE4F },
+{ 0xCE50, 0xCE50, 0xCE50 },
+{ 0xCE51, 0xCE51, 0xCE51 },
+{ 0xCE52, 0xCE52, 0xCE52 },
+{ 0xCE53, 0xCE53, 0xCE53 },
+{ 0xCE54, 0xCE54, 0xCE54 },
+{ 0xCE55, 0xCE55, 0xCE55 },
+{ 0xCE56, 0xCE56, 0xCE56 },
+{ 0xCE57, 0xCE57, 0xCE57 },
+{ 0xCE58, 0xCE58, 0xCE58 },
+{ 0xCE59, 0xCE59, 0xCE59 },
+{ 0xCE5A, 0xCE5A, 0xCE5A },
+{ 0xCE5B, 0xCE5B, 0xCE5B },
+{ 0xCE5C, 0xCE5C, 0xCE5C },
+{ 0xCE5D, 0xCE5D, 0xCE5D },
+{ 0xCE5E, 0xCE5E, 0xCE5E },
+{ 0xCE5F, 0xCE5F, 0xCE5F },
+{ 0xCE60, 0xCE60, 0xCE60 },
+{ 0xCE61, 0xCE61, 0xCE61 },
+{ 0xCE62, 0xCE62, 0xCE62 },
+{ 0xCE63, 0xCE63, 0xCE63 },
+{ 0xCE64, 0xCE64, 0xCE64 },
+{ 0xCE65, 0xCE65, 0xCE65 },
+{ 0xCE66, 0xCE66, 0xCE66 },
+{ 0xCE67, 0xCE67, 0xCE67 },
+{ 0xCE68, 0xCE68, 0xCE68 },
+{ 0xCE69, 0xCE69, 0xCE69 },
+{ 0xCE6A, 0xCE6A, 0xCE6A },
+{ 0xCE6B, 0xCE6B, 0xCE6B },
+{ 0xCE6C, 0xCE6C, 0xCE6C },
+{ 0xCE6D, 0xCE6D, 0xCE6D },
+{ 0xCE6E, 0xCE6E, 0xCE6E },
+{ 0xCE6F, 0xCE6F, 0xCE6F },
+{ 0xCE70, 0xCE70, 0xCE70 },
+{ 0xCE71, 0xCE71, 0xCE71 },
+{ 0xCE72, 0xCE72, 0xCE72 },
+{ 0xCE73, 0xCE73, 0xCE73 },
+{ 0xCE74, 0xCE74, 0xCE74 },
+{ 0xCE75, 0xCE75, 0xCE75 },
+{ 0xCE76, 0xCE76, 0xCE76 },
+{ 0xCE77, 0xCE77, 0xCE77 },
+{ 0xCE78, 0xCE78, 0xCE78 },
+{ 0xCE79, 0xCE79, 0xCE79 },
+{ 0xCE7A, 0xCE7A, 0xCE7A },
+{ 0xCE7B, 0xCE7B, 0xCE7B },
+{ 0xCE7C, 0xCE7C, 0xCE7C },
+{ 0xCE7D, 0xCE7D, 0xCE7D },
+{ 0xCE7E, 0xCE7E, 0xCE7E },
+{ 0xCE7F, 0xCE7F, 0xCE7F },
+{ 0xCE80, 0xCE80, 0xCE80 },
+{ 0xCE81, 0xCE81, 0xCE81 },
+{ 0xCE82, 0xCE82, 0xCE82 },
+{ 0xCE83, 0xCE83, 0xCE83 },
+{ 0xCE84, 0xCE84, 0xCE84 },
+{ 0xCE85, 0xCE85, 0xCE85 },
+{ 0xCE86, 0xCE86, 0xCE86 },
+{ 0xCE87, 0xCE87, 0xCE87 },
+{ 0xCE88, 0xCE88, 0xCE88 },
+{ 0xCE89, 0xCE89, 0xCE89 },
+{ 0xCE8A, 0xCE8A, 0xCE8A },
+{ 0xCE8B, 0xCE8B, 0xCE8B },
+{ 0xCE8C, 0xCE8C, 0xCE8C },
+{ 0xCE8D, 0xCE8D, 0xCE8D },
+{ 0xCE8E, 0xCE8E, 0xCE8E },
+{ 0xCE8F, 0xCE8F, 0xCE8F },
+{ 0xCE90, 0xCE90, 0xCE90 },
+{ 0xCE91, 0xCE91, 0xCE91 },
+{ 0xCE92, 0xCE92, 0xCE92 },
+{ 0xCE93, 0xCE93, 0xCE93 },
+{ 0xCE94, 0xCE94, 0xCE94 },
+{ 0xCE95, 0xCE95, 0xCE95 },
+{ 0xCE96, 0xCE96, 0xCE96 },
+{ 0xCE97, 0xCE97, 0xCE97 },
+{ 0xCE98, 0xCE98, 0xCE98 },
+{ 0xCE99, 0xCE99, 0xCE99 },
+{ 0xCE9A, 0xCE9A, 0xCE9A },
+{ 0xCE9B, 0xCE9B, 0xCE9B },
+{ 0xCE9C, 0xCE9C, 0xCE9C },
+{ 0xCE9D, 0xCE9D, 0xCE9D },
+{ 0xCE9E, 0xCE9E, 0xCE9E },
+{ 0xCE9F, 0xCE9F, 0xCE9F },
+{ 0xCEA0, 0xCEA0, 0xCEA0 },
+{ 0xCEA1, 0xCEA1, 0xCEA1 },
+{ 0xCEA2, 0xCEA2, 0xCEA2 },
+{ 0xCEA3, 0xCEA3, 0xCEA3 },
+{ 0xCEA4, 0xCEA4, 0xCEA4 },
+{ 0xCEA5, 0xCEA5, 0xCEA5 },
+{ 0xCEA6, 0xCEA6, 0xCEA6 },
+{ 0xCEA7, 0xCEA7, 0xCEA7 },
+{ 0xCEA8, 0xCEA8, 0xCEA8 },
+{ 0xCEA9, 0xCEA9, 0xCEA9 },
+{ 0xCEAA, 0xCEAA, 0xCEAA },
+{ 0xCEAB, 0xCEAB, 0xCEAB },
+{ 0xCEAC, 0xCEAC, 0xCEAC },
+{ 0xCEAD, 0xCEAD, 0xCEAD },
+{ 0xCEAE, 0xCEAE, 0xCEAE },
+{ 0xCEAF, 0xCEAF, 0xCEAF },
+{ 0xCEB0, 0xCEB0, 0xCEB0 },
+{ 0xCEB1, 0xCEB1, 0xCEB1 },
+{ 0xCEB2, 0xCEB2, 0xCEB2 },
+{ 0xCEB3, 0xCEB3, 0xCEB3 },
+{ 0xCEB4, 0xCEB4, 0xCEB4 },
+{ 0xCEB5, 0xCEB5, 0xCEB5 },
+{ 0xCEB6, 0xCEB6, 0xCEB6 },
+{ 0xCEB7, 0xCEB7, 0xCEB7 },
+{ 0xCEB8, 0xCEB8, 0xCEB8 },
+{ 0xCEB9, 0xCEB9, 0xCEB9 },
+{ 0xCEBA, 0xCEBA, 0xCEBA },
+{ 0xCEBB, 0xCEBB, 0xCEBB },
+{ 0xCEBC, 0xCEBC, 0xCEBC },
+{ 0xCEBD, 0xCEBD, 0xCEBD },
+{ 0xCEBE, 0xCEBE, 0xCEBE },
+{ 0xCEBF, 0xCEBF, 0xCEBF },
+{ 0xCEC0, 0xCEC0, 0xCEC0 },
+{ 0xCEC1, 0xCEC1, 0xCEC1 },
+{ 0xCEC2, 0xCEC2, 0xCEC2 },
+{ 0xCEC3, 0xCEC3, 0xCEC3 },
+{ 0xCEC4, 0xCEC4, 0xCEC4 },
+{ 0xCEC5, 0xCEC5, 0xCEC5 },
+{ 0xCEC6, 0xCEC6, 0xCEC6 },
+{ 0xCEC7, 0xCEC7, 0xCEC7 },
+{ 0xCEC8, 0xCEC8, 0xCEC8 },
+{ 0xCEC9, 0xCEC9, 0xCEC9 },
+{ 0xCECA, 0xCECA, 0xCECA },
+{ 0xCECB, 0xCECB, 0xCECB },
+{ 0xCECC, 0xCECC, 0xCECC },
+{ 0xCECD, 0xCECD, 0xCECD },
+{ 0xCECE, 0xCECE, 0xCECE },
+{ 0xCECF, 0xCECF, 0xCECF },
+{ 0xCED0, 0xCED0, 0xCED0 },
+{ 0xCED1, 0xCED1, 0xCED1 },
+{ 0xCED2, 0xCED2, 0xCED2 },
+{ 0xCED3, 0xCED3, 0xCED3 },
+{ 0xCED4, 0xCED4, 0xCED4 },
+{ 0xCED5, 0xCED5, 0xCED5 },
+{ 0xCED6, 0xCED6, 0xCED6 },
+{ 0xCED7, 0xCED7, 0xCED7 },
+{ 0xCED8, 0xCED8, 0xCED8 },
+{ 0xCED9, 0xCED9, 0xCED9 },
+{ 0xCEDA, 0xCEDA, 0xCEDA },
+{ 0xCEDB, 0xCEDB, 0xCEDB },
+{ 0xCEDC, 0xCEDC, 0xCEDC },
+{ 0xCEDD, 0xCEDD, 0xCEDD },
+{ 0xCEDE, 0xCEDE, 0xCEDE },
+{ 0xCEDF, 0xCEDF, 0xCEDF },
+{ 0xCEE0, 0xCEE0, 0xCEE0 },
+{ 0xCEE1, 0xCEE1, 0xCEE1 },
+{ 0xCEE2, 0xCEE2, 0xCEE2 },
+{ 0xCEE3, 0xCEE3, 0xCEE3 },
+{ 0xCEE4, 0xCEE4, 0xCEE4 },
+{ 0xCEE5, 0xCEE5, 0xCEE5 },
+{ 0xCEE6, 0xCEE6, 0xCEE6 },
+{ 0xCEE7, 0xCEE7, 0xCEE7 },
+{ 0xCEE8, 0xCEE8, 0xCEE8 },
+{ 0xCEE9, 0xCEE9, 0xCEE9 },
+{ 0xCEEA, 0xCEEA, 0xCEEA },
+{ 0xCEEB, 0xCEEB, 0xCEEB },
+{ 0xCEEC, 0xCEEC, 0xCEEC },
+{ 0xCEED, 0xCEED, 0xCEED },
+{ 0xCEEE, 0xCEEE, 0xCEEE },
+{ 0xCEEF, 0xCEEF, 0xCEEF },
+{ 0xCEF0, 0xCEF0, 0xCEF0 },
+{ 0xCEF1, 0xCEF1, 0xCEF1 },
+{ 0xCEF2, 0xCEF2, 0xCEF2 },
+{ 0xCEF3, 0xCEF3, 0xCEF3 },
+{ 0xCEF4, 0xCEF4, 0xCEF4 },
+{ 0xCEF5, 0xCEF5, 0xCEF5 },
+{ 0xCEF6, 0xCEF6, 0xCEF6 },
+{ 0xCEF7, 0xCEF7, 0xCEF7 },
+{ 0xCEF8, 0xCEF8, 0xCEF8 },
+{ 0xCEF9, 0xCEF9, 0xCEF9 },
+{ 0xCEFA, 0xCEFA, 0xCEFA },
+{ 0xCEFB, 0xCEFB, 0xCEFB },
+{ 0xCEFC, 0xCEFC, 0xCEFC },
+{ 0xCEFD, 0xCEFD, 0xCEFD },
+{ 0xCEFE, 0xCEFE, 0xCEFE },
+{ 0xCEFF, 0xCEFF, 0xCEFF },
+{ 0xCF00, 0xCF00, 0xCF00 },
+{ 0xCF01, 0xCF01, 0xCF01 },
+{ 0xCF02, 0xCF02, 0xCF02 },
+{ 0xCF03, 0xCF03, 0xCF03 },
+{ 0xCF04, 0xCF04, 0xCF04 },
+{ 0xCF05, 0xCF05, 0xCF05 },
+{ 0xCF06, 0xCF06, 0xCF06 },
+{ 0xCF07, 0xCF07, 0xCF07 },
+{ 0xCF08, 0xCF08, 0xCF08 },
+{ 0xCF09, 0xCF09, 0xCF09 },
+{ 0xCF0A, 0xCF0A, 0xCF0A },
+{ 0xCF0B, 0xCF0B, 0xCF0B },
+{ 0xCF0C, 0xCF0C, 0xCF0C },
+{ 0xCF0D, 0xCF0D, 0xCF0D },
+{ 0xCF0E, 0xCF0E, 0xCF0E },
+{ 0xCF0F, 0xCF0F, 0xCF0F },
+{ 0xCF10, 0xCF10, 0xCF10 },
+{ 0xCF11, 0xCF11, 0xCF11 },
+{ 0xCF12, 0xCF12, 0xCF12 },
+{ 0xCF13, 0xCF13, 0xCF13 },
+{ 0xCF14, 0xCF14, 0xCF14 },
+{ 0xCF15, 0xCF15, 0xCF15 },
+{ 0xCF16, 0xCF16, 0xCF16 },
+{ 0xCF17, 0xCF17, 0xCF17 },
+{ 0xCF18, 0xCF18, 0xCF18 },
+{ 0xCF19, 0xCF19, 0xCF19 },
+{ 0xCF1A, 0xCF1A, 0xCF1A },
+{ 0xCF1B, 0xCF1B, 0xCF1B },
+{ 0xCF1C, 0xCF1C, 0xCF1C },
+{ 0xCF1D, 0xCF1D, 0xCF1D },
+{ 0xCF1E, 0xCF1E, 0xCF1E },
+{ 0xCF1F, 0xCF1F, 0xCF1F },
+{ 0xCF20, 0xCF20, 0xCF20 },
+{ 0xCF21, 0xCF21, 0xCF21 },
+{ 0xCF22, 0xCF22, 0xCF22 },
+{ 0xCF23, 0xCF23, 0xCF23 },
+{ 0xCF24, 0xCF24, 0xCF24 },
+{ 0xCF25, 0xCF25, 0xCF25 },
+{ 0xCF26, 0xCF26, 0xCF26 },
+{ 0xCF27, 0xCF27, 0xCF27 },
+{ 0xCF28, 0xCF28, 0xCF28 },
+{ 0xCF29, 0xCF29, 0xCF29 },
+{ 0xCF2A, 0xCF2A, 0xCF2A },
+{ 0xCF2B, 0xCF2B, 0xCF2B },
+{ 0xCF2C, 0xCF2C, 0xCF2C },
+{ 0xCF2D, 0xCF2D, 0xCF2D },
+{ 0xCF2E, 0xCF2E, 0xCF2E },
+{ 0xCF2F, 0xCF2F, 0xCF2F },
+{ 0xCF30, 0xCF30, 0xCF30 },
+{ 0xCF31, 0xCF31, 0xCF31 },
+{ 0xCF32, 0xCF32, 0xCF32 },
+{ 0xCF33, 0xCF33, 0xCF33 },
+{ 0xCF34, 0xCF34, 0xCF34 },
+{ 0xCF35, 0xCF35, 0xCF35 },
+{ 0xCF36, 0xCF36, 0xCF36 },
+{ 0xCF37, 0xCF37, 0xCF37 },
+{ 0xCF38, 0xCF38, 0xCF38 },
+{ 0xCF39, 0xCF39, 0xCF39 },
+{ 0xCF3A, 0xCF3A, 0xCF3A },
+{ 0xCF3B, 0xCF3B, 0xCF3B },
+{ 0xCF3C, 0xCF3C, 0xCF3C },
+{ 0xCF3D, 0xCF3D, 0xCF3D },
+{ 0xCF3E, 0xCF3E, 0xCF3E },
+{ 0xCF3F, 0xCF3F, 0xCF3F },
+{ 0xCF40, 0xCF40, 0xCF40 },
+{ 0xCF41, 0xCF41, 0xCF41 },
+{ 0xCF42, 0xCF42, 0xCF42 },
+{ 0xCF43, 0xCF43, 0xCF43 },
+{ 0xCF44, 0xCF44, 0xCF44 },
+{ 0xCF45, 0xCF45, 0xCF45 },
+{ 0xCF46, 0xCF46, 0xCF46 },
+{ 0xCF47, 0xCF47, 0xCF47 },
+{ 0xCF48, 0xCF48, 0xCF48 },
+{ 0xCF49, 0xCF49, 0xCF49 },
+{ 0xCF4A, 0xCF4A, 0xCF4A },
+{ 0xCF4B, 0xCF4B, 0xCF4B },
+{ 0xCF4C, 0xCF4C, 0xCF4C },
+{ 0xCF4D, 0xCF4D, 0xCF4D },
+{ 0xCF4E, 0xCF4E, 0xCF4E },
+{ 0xCF4F, 0xCF4F, 0xCF4F },
+{ 0xCF50, 0xCF50, 0xCF50 },
+{ 0xCF51, 0xCF51, 0xCF51 },
+{ 0xCF52, 0xCF52, 0xCF52 },
+{ 0xCF53, 0xCF53, 0xCF53 },
+{ 0xCF54, 0xCF54, 0xCF54 },
+{ 0xCF55, 0xCF55, 0xCF55 },
+{ 0xCF56, 0xCF56, 0xCF56 },
+{ 0xCF57, 0xCF57, 0xCF57 },
+{ 0xCF58, 0xCF58, 0xCF58 },
+{ 0xCF59, 0xCF59, 0xCF59 },
+{ 0xCF5A, 0xCF5A, 0xCF5A },
+{ 0xCF5B, 0xCF5B, 0xCF5B },
+{ 0xCF5C, 0xCF5C, 0xCF5C },
+{ 0xCF5D, 0xCF5D, 0xCF5D },
+{ 0xCF5E, 0xCF5E, 0xCF5E },
+{ 0xCF5F, 0xCF5F, 0xCF5F },
+{ 0xCF60, 0xCF60, 0xCF60 },
+{ 0xCF61, 0xCF61, 0xCF61 },
+{ 0xCF62, 0xCF62, 0xCF62 },
+{ 0xCF63, 0xCF63, 0xCF63 },
+{ 0xCF64, 0xCF64, 0xCF64 },
+{ 0xCF65, 0xCF65, 0xCF65 },
+{ 0xCF66, 0xCF66, 0xCF66 },
+{ 0xCF67, 0xCF67, 0xCF67 },
+{ 0xCF68, 0xCF68, 0xCF68 },
+{ 0xCF69, 0xCF69, 0xCF69 },
+{ 0xCF6A, 0xCF6A, 0xCF6A },
+{ 0xCF6B, 0xCF6B, 0xCF6B },
+{ 0xCF6C, 0xCF6C, 0xCF6C },
+{ 0xCF6D, 0xCF6D, 0xCF6D },
+{ 0xCF6E, 0xCF6E, 0xCF6E },
+{ 0xCF6F, 0xCF6F, 0xCF6F },
+{ 0xCF70, 0xCF70, 0xCF70 },
+{ 0xCF71, 0xCF71, 0xCF71 },
+{ 0xCF72, 0xCF72, 0xCF72 },
+{ 0xCF73, 0xCF73, 0xCF73 },
+{ 0xCF74, 0xCF74, 0xCF74 },
+{ 0xCF75, 0xCF75, 0xCF75 },
+{ 0xCF76, 0xCF76, 0xCF76 },
+{ 0xCF77, 0xCF77, 0xCF77 },
+{ 0xCF78, 0xCF78, 0xCF78 },
+{ 0xCF79, 0xCF79, 0xCF79 },
+{ 0xCF7A, 0xCF7A, 0xCF7A },
+{ 0xCF7B, 0xCF7B, 0xCF7B },
+{ 0xCF7C, 0xCF7C, 0xCF7C },
+{ 0xCF7D, 0xCF7D, 0xCF7D },
+{ 0xCF7E, 0xCF7E, 0xCF7E },
+{ 0xCF7F, 0xCF7F, 0xCF7F },
+{ 0xCF80, 0xCF80, 0xCF80 },
+{ 0xCF81, 0xCF81, 0xCF81 },
+{ 0xCF82, 0xCF82, 0xCF82 },
+{ 0xCF83, 0xCF83, 0xCF83 },
+{ 0xCF84, 0xCF84, 0xCF84 },
+{ 0xCF85, 0xCF85, 0xCF85 },
+{ 0xCF86, 0xCF86, 0xCF86 },
+{ 0xCF87, 0xCF87, 0xCF87 },
+{ 0xCF88, 0xCF88, 0xCF88 },
+{ 0xCF89, 0xCF89, 0xCF89 },
+{ 0xCF8A, 0xCF8A, 0xCF8A },
+{ 0xCF8B, 0xCF8B, 0xCF8B },
+{ 0xCF8C, 0xCF8C, 0xCF8C },
+{ 0xCF8D, 0xCF8D, 0xCF8D },
+{ 0xCF8E, 0xCF8E, 0xCF8E },
+{ 0xCF8F, 0xCF8F, 0xCF8F },
+{ 0xCF90, 0xCF90, 0xCF90 },
+{ 0xCF91, 0xCF91, 0xCF91 },
+{ 0xCF92, 0xCF92, 0xCF92 },
+{ 0xCF93, 0xCF93, 0xCF93 },
+{ 0xCF94, 0xCF94, 0xCF94 },
+{ 0xCF95, 0xCF95, 0xCF95 },
+{ 0xCF96, 0xCF96, 0xCF96 },
+{ 0xCF97, 0xCF97, 0xCF97 },
+{ 0xCF98, 0xCF98, 0xCF98 },
+{ 0xCF99, 0xCF99, 0xCF99 },
+{ 0xCF9A, 0xCF9A, 0xCF9A },
+{ 0xCF9B, 0xCF9B, 0xCF9B },
+{ 0xCF9C, 0xCF9C, 0xCF9C },
+{ 0xCF9D, 0xCF9D, 0xCF9D },
+{ 0xCF9E, 0xCF9E, 0xCF9E },
+{ 0xCF9F, 0xCF9F, 0xCF9F },
+{ 0xCFA0, 0xCFA0, 0xCFA0 },
+{ 0xCFA1, 0xCFA1, 0xCFA1 },
+{ 0xCFA2, 0xCFA2, 0xCFA2 },
+{ 0xCFA3, 0xCFA3, 0xCFA3 },
+{ 0xCFA4, 0xCFA4, 0xCFA4 },
+{ 0xCFA5, 0xCFA5, 0xCFA5 },
+{ 0xCFA6, 0xCFA6, 0xCFA6 },
+{ 0xCFA7, 0xCFA7, 0xCFA7 },
+{ 0xCFA8, 0xCFA8, 0xCFA8 },
+{ 0xCFA9, 0xCFA9, 0xCFA9 },
+{ 0xCFAA, 0xCFAA, 0xCFAA },
+{ 0xCFAB, 0xCFAB, 0xCFAB },
+{ 0xCFAC, 0xCFAC, 0xCFAC },
+{ 0xCFAD, 0xCFAD, 0xCFAD },
+{ 0xCFAE, 0xCFAE, 0xCFAE },
+{ 0xCFAF, 0xCFAF, 0xCFAF },
+{ 0xCFB0, 0xCFB0, 0xCFB0 },
+{ 0xCFB1, 0xCFB1, 0xCFB1 },
+{ 0xCFB2, 0xCFB2, 0xCFB2 },
+{ 0xCFB3, 0xCFB3, 0xCFB3 },
+{ 0xCFB4, 0xCFB4, 0xCFB4 },
+{ 0xCFB5, 0xCFB5, 0xCFB5 },
+{ 0xCFB6, 0xCFB6, 0xCFB6 },
+{ 0xCFB7, 0xCFB7, 0xCFB7 },
+{ 0xCFB8, 0xCFB8, 0xCFB8 },
+{ 0xCFB9, 0xCFB9, 0xCFB9 },
+{ 0xCFBA, 0xCFBA, 0xCFBA },
+{ 0xCFBB, 0xCFBB, 0xCFBB },
+{ 0xCFBC, 0xCFBC, 0xCFBC },
+{ 0xCFBD, 0xCFBD, 0xCFBD },
+{ 0xCFBE, 0xCFBE, 0xCFBE },
+{ 0xCFBF, 0xCFBF, 0xCFBF },
+{ 0xCFC0, 0xCFC0, 0xCFC0 },
+{ 0xCFC1, 0xCFC1, 0xCFC1 },
+{ 0xCFC2, 0xCFC2, 0xCFC2 },
+{ 0xCFC3, 0xCFC3, 0xCFC3 },
+{ 0xCFC4, 0xCFC4, 0xCFC4 },
+{ 0xCFC5, 0xCFC5, 0xCFC5 },
+{ 0xCFC6, 0xCFC6, 0xCFC6 },
+{ 0xCFC7, 0xCFC7, 0xCFC7 },
+{ 0xCFC8, 0xCFC8, 0xCFC8 },
+{ 0xCFC9, 0xCFC9, 0xCFC9 },
+{ 0xCFCA, 0xCFCA, 0xCFCA },
+{ 0xCFCB, 0xCFCB, 0xCFCB },
+{ 0xCFCC, 0xCFCC, 0xCFCC },
+{ 0xCFCD, 0xCFCD, 0xCFCD },
+{ 0xCFCE, 0xCFCE, 0xCFCE },
+{ 0xCFCF, 0xCFCF, 0xCFCF },
+{ 0xCFD0, 0xCFD0, 0xCFD0 },
+{ 0xCFD1, 0xCFD1, 0xCFD1 },
+{ 0xCFD2, 0xCFD2, 0xCFD2 },
+{ 0xCFD3, 0xCFD3, 0xCFD3 },
+{ 0xCFD4, 0xCFD4, 0xCFD4 },
+{ 0xCFD5, 0xCFD5, 0xCFD5 },
+{ 0xCFD6, 0xCFD6, 0xCFD6 },
+{ 0xCFD7, 0xCFD7, 0xCFD7 },
+{ 0xCFD8, 0xCFD8, 0xCFD8 },
+{ 0xCFD9, 0xCFD9, 0xCFD9 },
+{ 0xCFDA, 0xCFDA, 0xCFDA },
+{ 0xCFDB, 0xCFDB, 0xCFDB },
+{ 0xCFDC, 0xCFDC, 0xCFDC },
+{ 0xCFDD, 0xCFDD, 0xCFDD },
+{ 0xCFDE, 0xCFDE, 0xCFDE },
+{ 0xCFDF, 0xCFDF, 0xCFDF },
+{ 0xCFE0, 0xCFE0, 0xCFE0 },
+{ 0xCFE1, 0xCFE1, 0xCFE1 },
+{ 0xCFE2, 0xCFE2, 0xCFE2 },
+{ 0xCFE3, 0xCFE3, 0xCFE3 },
+{ 0xCFE4, 0xCFE4, 0xCFE4 },
+{ 0xCFE5, 0xCFE5, 0xCFE5 },
+{ 0xCFE6, 0xCFE6, 0xCFE6 },
+{ 0xCFE7, 0xCFE7, 0xCFE7 },
+{ 0xCFE8, 0xCFE8, 0xCFE8 },
+{ 0xCFE9, 0xCFE9, 0xCFE9 },
+{ 0xCFEA, 0xCFEA, 0xCFEA },
+{ 0xCFEB, 0xCFEB, 0xCFEB },
+{ 0xCFEC, 0xCFEC, 0xCFEC },
+{ 0xCFED, 0xCFED, 0xCFED },
+{ 0xCFEE, 0xCFEE, 0xCFEE },
+{ 0xCFEF, 0xCFEF, 0xCFEF },
+{ 0xCFF0, 0xCFF0, 0xCFF0 },
+{ 0xCFF1, 0xCFF1, 0xCFF1 },
+{ 0xCFF2, 0xCFF2, 0xCFF2 },
+{ 0xCFF3, 0xCFF3, 0xCFF3 },
+{ 0xCFF4, 0xCFF4, 0xCFF4 },
+{ 0xCFF5, 0xCFF5, 0xCFF5 },
+{ 0xCFF6, 0xCFF6, 0xCFF6 },
+{ 0xCFF7, 0xCFF7, 0xCFF7 },
+{ 0xCFF8, 0xCFF8, 0xCFF8 },
+{ 0xCFF9, 0xCFF9, 0xCFF9 },
+{ 0xCFFA, 0xCFFA, 0xCFFA },
+{ 0xCFFB, 0xCFFB, 0xCFFB },
+{ 0xCFFC, 0xCFFC, 0xCFFC },
+{ 0xCFFD, 0xCFFD, 0xCFFD },
+{ 0xCFFE, 0xCFFE, 0xCFFE },
+{ 0xCFFF, 0xCFFF, 0xCFFF },
+{ 0xD000, 0xD000, 0xD000 },
+{ 0xD001, 0xD001, 0xD001 },
+{ 0xD002, 0xD002, 0xD002 },
+{ 0xD003, 0xD003, 0xD003 },
+{ 0xD004, 0xD004, 0xD004 },
+{ 0xD005, 0xD005, 0xD005 },
+{ 0xD006, 0xD006, 0xD006 },
+{ 0xD007, 0xD007, 0xD007 },
+{ 0xD008, 0xD008, 0xD008 },
+{ 0xD009, 0xD009, 0xD009 },
+{ 0xD00A, 0xD00A, 0xD00A },
+{ 0xD00B, 0xD00B, 0xD00B },
+{ 0xD00C, 0xD00C, 0xD00C },
+{ 0xD00D, 0xD00D, 0xD00D },
+{ 0xD00E, 0xD00E, 0xD00E },
+{ 0xD00F, 0xD00F, 0xD00F },
+{ 0xD010, 0xD010, 0xD010 },
+{ 0xD011, 0xD011, 0xD011 },
+{ 0xD012, 0xD012, 0xD012 },
+{ 0xD013, 0xD013, 0xD013 },
+{ 0xD014, 0xD014, 0xD014 },
+{ 0xD015, 0xD015, 0xD015 },
+{ 0xD016, 0xD016, 0xD016 },
+{ 0xD017, 0xD017, 0xD017 },
+{ 0xD018, 0xD018, 0xD018 },
+{ 0xD019, 0xD019, 0xD019 },
+{ 0xD01A, 0xD01A, 0xD01A },
+{ 0xD01B, 0xD01B, 0xD01B },
+{ 0xD01C, 0xD01C, 0xD01C },
+{ 0xD01D, 0xD01D, 0xD01D },
+{ 0xD01E, 0xD01E, 0xD01E },
+{ 0xD01F, 0xD01F, 0xD01F },
+{ 0xD020, 0xD020, 0xD020 },
+{ 0xD021, 0xD021, 0xD021 },
+{ 0xD022, 0xD022, 0xD022 },
+{ 0xD023, 0xD023, 0xD023 },
+{ 0xD024, 0xD024, 0xD024 },
+{ 0xD025, 0xD025, 0xD025 },
+{ 0xD026, 0xD026, 0xD026 },
+{ 0xD027, 0xD027, 0xD027 },
+{ 0xD028, 0xD028, 0xD028 },
+{ 0xD029, 0xD029, 0xD029 },
+{ 0xD02A, 0xD02A, 0xD02A },
+{ 0xD02B, 0xD02B, 0xD02B },
+{ 0xD02C, 0xD02C, 0xD02C },
+{ 0xD02D, 0xD02D, 0xD02D },
+{ 0xD02E, 0xD02E, 0xD02E },
+{ 0xD02F, 0xD02F, 0xD02F },
+{ 0xD030, 0xD030, 0xD030 },
+{ 0xD031, 0xD031, 0xD031 },
+{ 0xD032, 0xD032, 0xD032 },
+{ 0xD033, 0xD033, 0xD033 },
+{ 0xD034, 0xD034, 0xD034 },
+{ 0xD035, 0xD035, 0xD035 },
+{ 0xD036, 0xD036, 0xD036 },
+{ 0xD037, 0xD037, 0xD037 },
+{ 0xD038, 0xD038, 0xD038 },
+{ 0xD039, 0xD039, 0xD039 },
+{ 0xD03A, 0xD03A, 0xD03A },
+{ 0xD03B, 0xD03B, 0xD03B },
+{ 0xD03C, 0xD03C, 0xD03C },
+{ 0xD03D, 0xD03D, 0xD03D },
+{ 0xD03E, 0xD03E, 0xD03E },
+{ 0xD03F, 0xD03F, 0xD03F },
+{ 0xD040, 0xD040, 0xD040 },
+{ 0xD041, 0xD041, 0xD041 },
+{ 0xD042, 0xD042, 0xD042 },
+{ 0xD043, 0xD043, 0xD043 },
+{ 0xD044, 0xD044, 0xD044 },
+{ 0xD045, 0xD045, 0xD045 },
+{ 0xD046, 0xD046, 0xD046 },
+{ 0xD047, 0xD047, 0xD047 },
+{ 0xD048, 0xD048, 0xD048 },
+{ 0xD049, 0xD049, 0xD049 },
+{ 0xD04A, 0xD04A, 0xD04A },
+{ 0xD04B, 0xD04B, 0xD04B },
+{ 0xD04C, 0xD04C, 0xD04C },
+{ 0xD04D, 0xD04D, 0xD04D },
+{ 0xD04E, 0xD04E, 0xD04E },
+{ 0xD04F, 0xD04F, 0xD04F },
+{ 0xD050, 0xD050, 0xD050 },
+{ 0xD051, 0xD051, 0xD051 },
+{ 0xD052, 0xD052, 0xD052 },
+{ 0xD053, 0xD053, 0xD053 },
+{ 0xD054, 0xD054, 0xD054 },
+{ 0xD055, 0xD055, 0xD055 },
+{ 0xD056, 0xD056, 0xD056 },
+{ 0xD057, 0xD057, 0xD057 },
+{ 0xD058, 0xD058, 0xD058 },
+{ 0xD059, 0xD059, 0xD059 },
+{ 0xD05A, 0xD05A, 0xD05A },
+{ 0xD05B, 0xD05B, 0xD05B },
+{ 0xD05C, 0xD05C, 0xD05C },
+{ 0xD05D, 0xD05D, 0xD05D },
+{ 0xD05E, 0xD05E, 0xD05E },
+{ 0xD05F, 0xD05F, 0xD05F },
+{ 0xD060, 0xD060, 0xD060 },
+{ 0xD061, 0xD061, 0xD061 },
+{ 0xD062, 0xD062, 0xD062 },
+{ 0xD063, 0xD063, 0xD063 },
+{ 0xD064, 0xD064, 0xD064 },
+{ 0xD065, 0xD065, 0xD065 },
+{ 0xD066, 0xD066, 0xD066 },
+{ 0xD067, 0xD067, 0xD067 },
+{ 0xD068, 0xD068, 0xD068 },
+{ 0xD069, 0xD069, 0xD069 },
+{ 0xD06A, 0xD06A, 0xD06A },
+{ 0xD06B, 0xD06B, 0xD06B },
+{ 0xD06C, 0xD06C, 0xD06C },
+{ 0xD06D, 0xD06D, 0xD06D },
+{ 0xD06E, 0xD06E, 0xD06E },
+{ 0xD06F, 0xD06F, 0xD06F },
+{ 0xD070, 0xD070, 0xD070 },
+{ 0xD071, 0xD071, 0xD071 },
+{ 0xD072, 0xD072, 0xD072 },
+{ 0xD073, 0xD073, 0xD073 },
+{ 0xD074, 0xD074, 0xD074 },
+{ 0xD075, 0xD075, 0xD075 },
+{ 0xD076, 0xD076, 0xD076 },
+{ 0xD077, 0xD077, 0xD077 },
+{ 0xD078, 0xD078, 0xD078 },
+{ 0xD079, 0xD079, 0xD079 },
+{ 0xD07A, 0xD07A, 0xD07A },
+{ 0xD07B, 0xD07B, 0xD07B },
+{ 0xD07C, 0xD07C, 0xD07C },
+{ 0xD07D, 0xD07D, 0xD07D },
+{ 0xD07E, 0xD07E, 0xD07E },
+{ 0xD07F, 0xD07F, 0xD07F },
+{ 0xD080, 0xD080, 0xD080 },
+{ 0xD081, 0xD081, 0xD081 },
+{ 0xD082, 0xD082, 0xD082 },
+{ 0xD083, 0xD083, 0xD083 },
+{ 0xD084, 0xD084, 0xD084 },
+{ 0xD085, 0xD085, 0xD085 },
+{ 0xD086, 0xD086, 0xD086 },
+{ 0xD087, 0xD087, 0xD087 },
+{ 0xD088, 0xD088, 0xD088 },
+{ 0xD089, 0xD089, 0xD089 },
+{ 0xD08A, 0xD08A, 0xD08A },
+{ 0xD08B, 0xD08B, 0xD08B },
+{ 0xD08C, 0xD08C, 0xD08C },
+{ 0xD08D, 0xD08D, 0xD08D },
+{ 0xD08E, 0xD08E, 0xD08E },
+{ 0xD08F, 0xD08F, 0xD08F },
+{ 0xD090, 0xD090, 0xD090 },
+{ 0xD091, 0xD091, 0xD091 },
+{ 0xD092, 0xD092, 0xD092 },
+{ 0xD093, 0xD093, 0xD093 },
+{ 0xD094, 0xD094, 0xD094 },
+{ 0xD095, 0xD095, 0xD095 },
+{ 0xD096, 0xD096, 0xD096 },
+{ 0xD097, 0xD097, 0xD097 },
+{ 0xD098, 0xD098, 0xD098 },
+{ 0xD099, 0xD099, 0xD099 },
+{ 0xD09A, 0xD09A, 0xD09A },
+{ 0xD09B, 0xD09B, 0xD09B },
+{ 0xD09C, 0xD09C, 0xD09C },
+{ 0xD09D, 0xD09D, 0xD09D },
+{ 0xD09E, 0xD09E, 0xD09E },
+{ 0xD09F, 0xD09F, 0xD09F },
+{ 0xD0A0, 0xD0A0, 0xD0A0 },
+{ 0xD0A1, 0xD0A1, 0xD0A1 },
+{ 0xD0A2, 0xD0A2, 0xD0A2 },
+{ 0xD0A3, 0xD0A3, 0xD0A3 },
+{ 0xD0A4, 0xD0A4, 0xD0A4 },
+{ 0xD0A5, 0xD0A5, 0xD0A5 },
+{ 0xD0A6, 0xD0A6, 0xD0A6 },
+{ 0xD0A7, 0xD0A7, 0xD0A7 },
+{ 0xD0A8, 0xD0A8, 0xD0A8 },
+{ 0xD0A9, 0xD0A9, 0xD0A9 },
+{ 0xD0AA, 0xD0AA, 0xD0AA },
+{ 0xD0AB, 0xD0AB, 0xD0AB },
+{ 0xD0AC, 0xD0AC, 0xD0AC },
+{ 0xD0AD, 0xD0AD, 0xD0AD },
+{ 0xD0AE, 0xD0AE, 0xD0AE },
+{ 0xD0AF, 0xD0AF, 0xD0AF },
+{ 0xD0B0, 0xD0B0, 0xD0B0 },
+{ 0xD0B1, 0xD0B1, 0xD0B1 },
+{ 0xD0B2, 0xD0B2, 0xD0B2 },
+{ 0xD0B3, 0xD0B3, 0xD0B3 },
+{ 0xD0B4, 0xD0B4, 0xD0B4 },
+{ 0xD0B5, 0xD0B5, 0xD0B5 },
+{ 0xD0B6, 0xD0B6, 0xD0B6 },
+{ 0xD0B7, 0xD0B7, 0xD0B7 },
+{ 0xD0B8, 0xD0B8, 0xD0B8 },
+{ 0xD0B9, 0xD0B9, 0xD0B9 },
+{ 0xD0BA, 0xD0BA, 0xD0BA },
+{ 0xD0BB, 0xD0BB, 0xD0BB },
+{ 0xD0BC, 0xD0BC, 0xD0BC },
+{ 0xD0BD, 0xD0BD, 0xD0BD },
+{ 0xD0BE, 0xD0BE, 0xD0BE },
+{ 0xD0BF, 0xD0BF, 0xD0BF },
+{ 0xD0C0, 0xD0C0, 0xD0C0 },
+{ 0xD0C1, 0xD0C1, 0xD0C1 },
+{ 0xD0C2, 0xD0C2, 0xD0C2 },
+{ 0xD0C3, 0xD0C3, 0xD0C3 },
+{ 0xD0C4, 0xD0C4, 0xD0C4 },
+{ 0xD0C5, 0xD0C5, 0xD0C5 },
+{ 0xD0C6, 0xD0C6, 0xD0C6 },
+{ 0xD0C7, 0xD0C7, 0xD0C7 },
+{ 0xD0C8, 0xD0C8, 0xD0C8 },
+{ 0xD0C9, 0xD0C9, 0xD0C9 },
+{ 0xD0CA, 0xD0CA, 0xD0CA },
+{ 0xD0CB, 0xD0CB, 0xD0CB },
+{ 0xD0CC, 0xD0CC, 0xD0CC },
+{ 0xD0CD, 0xD0CD, 0xD0CD },
+{ 0xD0CE, 0xD0CE, 0xD0CE },
+{ 0xD0CF, 0xD0CF, 0xD0CF },
+{ 0xD0D0, 0xD0D0, 0xD0D0 },
+{ 0xD0D1, 0xD0D1, 0xD0D1 },
+{ 0xD0D2, 0xD0D2, 0xD0D2 },
+{ 0xD0D3, 0xD0D3, 0xD0D3 },
+{ 0xD0D4, 0xD0D4, 0xD0D4 },
+{ 0xD0D5, 0xD0D5, 0xD0D5 },
+{ 0xD0D6, 0xD0D6, 0xD0D6 },
+{ 0xD0D7, 0xD0D7, 0xD0D7 },
+{ 0xD0D8, 0xD0D8, 0xD0D8 },
+{ 0xD0D9, 0xD0D9, 0xD0D9 },
+{ 0xD0DA, 0xD0DA, 0xD0DA },
+{ 0xD0DB, 0xD0DB, 0xD0DB },
+{ 0xD0DC, 0xD0DC, 0xD0DC },
+{ 0xD0DD, 0xD0DD, 0xD0DD },
+{ 0xD0DE, 0xD0DE, 0xD0DE },
+{ 0xD0DF, 0xD0DF, 0xD0DF },
+{ 0xD0E0, 0xD0E0, 0xD0E0 },
+{ 0xD0E1, 0xD0E1, 0xD0E1 },
+{ 0xD0E2, 0xD0E2, 0xD0E2 },
+{ 0xD0E3, 0xD0E3, 0xD0E3 },
+{ 0xD0E4, 0xD0E4, 0xD0E4 },
+{ 0xD0E5, 0xD0E5, 0xD0E5 },
+{ 0xD0E6, 0xD0E6, 0xD0E6 },
+{ 0xD0E7, 0xD0E7, 0xD0E7 },
+{ 0xD0E8, 0xD0E8, 0xD0E8 },
+{ 0xD0E9, 0xD0E9, 0xD0E9 },
+{ 0xD0EA, 0xD0EA, 0xD0EA },
+{ 0xD0EB, 0xD0EB, 0xD0EB },
+{ 0xD0EC, 0xD0EC, 0xD0EC },
+{ 0xD0ED, 0xD0ED, 0xD0ED },
+{ 0xD0EE, 0xD0EE, 0xD0EE },
+{ 0xD0EF, 0xD0EF, 0xD0EF },
+{ 0xD0F0, 0xD0F0, 0xD0F0 },
+{ 0xD0F1, 0xD0F1, 0xD0F1 },
+{ 0xD0F2, 0xD0F2, 0xD0F2 },
+{ 0xD0F3, 0xD0F3, 0xD0F3 },
+{ 0xD0F4, 0xD0F4, 0xD0F4 },
+{ 0xD0F5, 0xD0F5, 0xD0F5 },
+{ 0xD0F6, 0xD0F6, 0xD0F6 },
+{ 0xD0F7, 0xD0F7, 0xD0F7 },
+{ 0xD0F8, 0xD0F8, 0xD0F8 },
+{ 0xD0F9, 0xD0F9, 0xD0F9 },
+{ 0xD0FA, 0xD0FA, 0xD0FA },
+{ 0xD0FB, 0xD0FB, 0xD0FB },
+{ 0xD0FC, 0xD0FC, 0xD0FC },
+{ 0xD0FD, 0xD0FD, 0xD0FD },
+{ 0xD0FE, 0xD0FE, 0xD0FE },
+{ 0xD0FF, 0xD0FF, 0xD0FF },
+{ 0xD100, 0xD100, 0xD100 },
+{ 0xD101, 0xD101, 0xD101 },
+{ 0xD102, 0xD102, 0xD102 },
+{ 0xD103, 0xD103, 0xD103 },
+{ 0xD104, 0xD104, 0xD104 },
+{ 0xD105, 0xD105, 0xD105 },
+{ 0xD106, 0xD106, 0xD106 },
+{ 0xD107, 0xD107, 0xD107 },
+{ 0xD108, 0xD108, 0xD108 },
+{ 0xD109, 0xD109, 0xD109 },
+{ 0xD10A, 0xD10A, 0xD10A },
+{ 0xD10B, 0xD10B, 0xD10B },
+{ 0xD10C, 0xD10C, 0xD10C },
+{ 0xD10D, 0xD10D, 0xD10D },
+{ 0xD10E, 0xD10E, 0xD10E },
+{ 0xD10F, 0xD10F, 0xD10F },
+{ 0xD110, 0xD110, 0xD110 },
+{ 0xD111, 0xD111, 0xD111 },
+{ 0xD112, 0xD112, 0xD112 },
+{ 0xD113, 0xD113, 0xD113 },
+{ 0xD114, 0xD114, 0xD114 },
+{ 0xD115, 0xD115, 0xD115 },
+{ 0xD116, 0xD116, 0xD116 },
+{ 0xD117, 0xD117, 0xD117 },
+{ 0xD118, 0xD118, 0xD118 },
+{ 0xD119, 0xD119, 0xD119 },
+{ 0xD11A, 0xD11A, 0xD11A },
+{ 0xD11B, 0xD11B, 0xD11B },
+{ 0xD11C, 0xD11C, 0xD11C },
+{ 0xD11D, 0xD11D, 0xD11D },
+{ 0xD11E, 0xD11E, 0xD11E },
+{ 0xD11F, 0xD11F, 0xD11F },
+{ 0xD120, 0xD120, 0xD120 },
+{ 0xD121, 0xD121, 0xD121 },
+{ 0xD122, 0xD122, 0xD122 },
+{ 0xD123, 0xD123, 0xD123 },
+{ 0xD124, 0xD124, 0xD124 },
+{ 0xD125, 0xD125, 0xD125 },
+{ 0xD126, 0xD126, 0xD126 },
+{ 0xD127, 0xD127, 0xD127 },
+{ 0xD128, 0xD128, 0xD128 },
+{ 0xD129, 0xD129, 0xD129 },
+{ 0xD12A, 0xD12A, 0xD12A },
+{ 0xD12B, 0xD12B, 0xD12B },
+{ 0xD12C, 0xD12C, 0xD12C },
+{ 0xD12D, 0xD12D, 0xD12D },
+{ 0xD12E, 0xD12E, 0xD12E },
+{ 0xD12F, 0xD12F, 0xD12F },
+{ 0xD130, 0xD130, 0xD130 },
+{ 0xD131, 0xD131, 0xD131 },
+{ 0xD132, 0xD132, 0xD132 },
+{ 0xD133, 0xD133, 0xD133 },
+{ 0xD134, 0xD134, 0xD134 },
+{ 0xD135, 0xD135, 0xD135 },
+{ 0xD136, 0xD136, 0xD136 },
+{ 0xD137, 0xD137, 0xD137 },
+{ 0xD138, 0xD138, 0xD138 },
+{ 0xD139, 0xD139, 0xD139 },
+{ 0xD13A, 0xD13A, 0xD13A },
+{ 0xD13B, 0xD13B, 0xD13B },
+{ 0xD13C, 0xD13C, 0xD13C },
+{ 0xD13D, 0xD13D, 0xD13D },
+{ 0xD13E, 0xD13E, 0xD13E },
+{ 0xD13F, 0xD13F, 0xD13F },
+{ 0xD140, 0xD140, 0xD140 },
+{ 0xD141, 0xD141, 0xD141 },
+{ 0xD142, 0xD142, 0xD142 },
+{ 0xD143, 0xD143, 0xD143 },
+{ 0xD144, 0xD144, 0xD144 },
+{ 0xD145, 0xD145, 0xD145 },
+{ 0xD146, 0xD146, 0xD146 },
+{ 0xD147, 0xD147, 0xD147 },
+{ 0xD148, 0xD148, 0xD148 },
+{ 0xD149, 0xD149, 0xD149 },
+{ 0xD14A, 0xD14A, 0xD14A },
+{ 0xD14B, 0xD14B, 0xD14B },
+{ 0xD14C, 0xD14C, 0xD14C },
+{ 0xD14D, 0xD14D, 0xD14D },
+{ 0xD14E, 0xD14E, 0xD14E },
+{ 0xD14F, 0xD14F, 0xD14F },
+{ 0xD150, 0xD150, 0xD150 },
+{ 0xD151, 0xD151, 0xD151 },
+{ 0xD152, 0xD152, 0xD152 },
+{ 0xD153, 0xD153, 0xD153 },
+{ 0xD154, 0xD154, 0xD154 },
+{ 0xD155, 0xD155, 0xD155 },
+{ 0xD156, 0xD156, 0xD156 },
+{ 0xD157, 0xD157, 0xD157 },
+{ 0xD158, 0xD158, 0xD158 },
+{ 0xD159, 0xD159, 0xD159 },
+{ 0xD15A, 0xD15A, 0xD15A },
+{ 0xD15B, 0xD15B, 0xD15B },
+{ 0xD15C, 0xD15C, 0xD15C },
+{ 0xD15D, 0xD15D, 0xD15D },
+{ 0xD15E, 0xD15E, 0xD15E },
+{ 0xD15F, 0xD15F, 0xD15F },
+{ 0xD160, 0xD160, 0xD160 },
+{ 0xD161, 0xD161, 0xD161 },
+{ 0xD162, 0xD162, 0xD162 },
+{ 0xD163, 0xD163, 0xD163 },
+{ 0xD164, 0xD164, 0xD164 },
+{ 0xD165, 0xD165, 0xD165 },
+{ 0xD166, 0xD166, 0xD166 },
+{ 0xD167, 0xD167, 0xD167 },
+{ 0xD168, 0xD168, 0xD168 },
+{ 0xD169, 0xD169, 0xD169 },
+{ 0xD16A, 0xD16A, 0xD16A },
+{ 0xD16B, 0xD16B, 0xD16B },
+{ 0xD16C, 0xD16C, 0xD16C },
+{ 0xD16D, 0xD16D, 0xD16D },
+{ 0xD16E, 0xD16E, 0xD16E },
+{ 0xD16F, 0xD16F, 0xD16F },
+{ 0xD170, 0xD170, 0xD170 },
+{ 0xD171, 0xD171, 0xD171 },
+{ 0xD172, 0xD172, 0xD172 },
+{ 0xD173, 0xD173, 0xD173 },
+{ 0xD174, 0xD174, 0xD174 },
+{ 0xD175, 0xD175, 0xD175 },
+{ 0xD176, 0xD176, 0xD176 },
+{ 0xD177, 0xD177, 0xD177 },
+{ 0xD178, 0xD178, 0xD178 },
+{ 0xD179, 0xD179, 0xD179 },
+{ 0xD17A, 0xD17A, 0xD17A },
+{ 0xD17B, 0xD17B, 0xD17B },
+{ 0xD17C, 0xD17C, 0xD17C },
+{ 0xD17D, 0xD17D, 0xD17D },
+{ 0xD17E, 0xD17E, 0xD17E },
+{ 0xD17F, 0xD17F, 0xD17F },
+{ 0xD180, 0xD180, 0xD180 },
+{ 0xD181, 0xD181, 0xD181 },
+{ 0xD182, 0xD182, 0xD182 },
+{ 0xD183, 0xD183, 0xD183 },
+{ 0xD184, 0xD184, 0xD184 },
+{ 0xD185, 0xD185, 0xD185 },
+{ 0xD186, 0xD186, 0xD186 },
+{ 0xD187, 0xD187, 0xD187 },
+{ 0xD188, 0xD188, 0xD188 },
+{ 0xD189, 0xD189, 0xD189 },
+{ 0xD18A, 0xD18A, 0xD18A },
+{ 0xD18B, 0xD18B, 0xD18B },
+{ 0xD18C, 0xD18C, 0xD18C },
+{ 0xD18D, 0xD18D, 0xD18D },
+{ 0xD18E, 0xD18E, 0xD18E },
+{ 0xD18F, 0xD18F, 0xD18F },
+{ 0xD190, 0xD190, 0xD190 },
+{ 0xD191, 0xD191, 0xD191 },
+{ 0xD192, 0xD192, 0xD192 },
+{ 0xD193, 0xD193, 0xD193 },
+{ 0xD194, 0xD194, 0xD194 },
+{ 0xD195, 0xD195, 0xD195 },
+{ 0xD196, 0xD196, 0xD196 },
+{ 0xD197, 0xD197, 0xD197 },
+{ 0xD198, 0xD198, 0xD198 },
+{ 0xD199, 0xD199, 0xD199 },
+{ 0xD19A, 0xD19A, 0xD19A },
+{ 0xD19B, 0xD19B, 0xD19B },
+{ 0xD19C, 0xD19C, 0xD19C },
+{ 0xD19D, 0xD19D, 0xD19D },
+{ 0xD19E, 0xD19E, 0xD19E },
+{ 0xD19F, 0xD19F, 0xD19F },
+{ 0xD1A0, 0xD1A0, 0xD1A0 },
+{ 0xD1A1, 0xD1A1, 0xD1A1 },
+{ 0xD1A2, 0xD1A2, 0xD1A2 },
+{ 0xD1A3, 0xD1A3, 0xD1A3 },
+{ 0xD1A4, 0xD1A4, 0xD1A4 },
+{ 0xD1A5, 0xD1A5, 0xD1A5 },
+{ 0xD1A6, 0xD1A6, 0xD1A6 },
+{ 0xD1A7, 0xD1A7, 0xD1A7 },
+{ 0xD1A8, 0xD1A8, 0xD1A8 },
+{ 0xD1A9, 0xD1A9, 0xD1A9 },
+{ 0xD1AA, 0xD1AA, 0xD1AA },
+{ 0xD1AB, 0xD1AB, 0xD1AB },
+{ 0xD1AC, 0xD1AC, 0xD1AC },
+{ 0xD1AD, 0xD1AD, 0xD1AD },
+{ 0xD1AE, 0xD1AE, 0xD1AE },
+{ 0xD1AF, 0xD1AF, 0xD1AF },
+{ 0xD1B0, 0xD1B0, 0xD1B0 },
+{ 0xD1B1, 0xD1B1, 0xD1B1 },
+{ 0xD1B2, 0xD1B2, 0xD1B2 },
+{ 0xD1B3, 0xD1B3, 0xD1B3 },
+{ 0xD1B4, 0xD1B4, 0xD1B4 },
+{ 0xD1B5, 0xD1B5, 0xD1B5 },
+{ 0xD1B6, 0xD1B6, 0xD1B6 },
+{ 0xD1B7, 0xD1B7, 0xD1B7 },
+{ 0xD1B8, 0xD1B8, 0xD1B8 },
+{ 0xD1B9, 0xD1B9, 0xD1B9 },
+{ 0xD1BA, 0xD1BA, 0xD1BA },
+{ 0xD1BB, 0xD1BB, 0xD1BB },
+{ 0xD1BC, 0xD1BC, 0xD1BC },
+{ 0xD1BD, 0xD1BD, 0xD1BD },
+{ 0xD1BE, 0xD1BE, 0xD1BE },
+{ 0xD1BF, 0xD1BF, 0xD1BF },
+{ 0xD1C0, 0xD1C0, 0xD1C0 },
+{ 0xD1C1, 0xD1C1, 0xD1C1 },
+{ 0xD1C2, 0xD1C2, 0xD1C2 },
+{ 0xD1C3, 0xD1C3, 0xD1C3 },
+{ 0xD1C4, 0xD1C4, 0xD1C4 },
+{ 0xD1C5, 0xD1C5, 0xD1C5 },
+{ 0xD1C6, 0xD1C6, 0xD1C6 },
+{ 0xD1C7, 0xD1C7, 0xD1C7 },
+{ 0xD1C8, 0xD1C8, 0xD1C8 },
+{ 0xD1C9, 0xD1C9, 0xD1C9 },
+{ 0xD1CA, 0xD1CA, 0xD1CA },
+{ 0xD1CB, 0xD1CB, 0xD1CB },
+{ 0xD1CC, 0xD1CC, 0xD1CC },
+{ 0xD1CD, 0xD1CD, 0xD1CD },
+{ 0xD1CE, 0xD1CE, 0xD1CE },
+{ 0xD1CF, 0xD1CF, 0xD1CF },
+{ 0xD1D0, 0xD1D0, 0xD1D0 },
+{ 0xD1D1, 0xD1D1, 0xD1D1 },
+{ 0xD1D2, 0xD1D2, 0xD1D2 },
+{ 0xD1D3, 0xD1D3, 0xD1D3 },
+{ 0xD1D4, 0xD1D4, 0xD1D4 },
+{ 0xD1D5, 0xD1D5, 0xD1D5 },
+{ 0xD1D6, 0xD1D6, 0xD1D6 },
+{ 0xD1D7, 0xD1D7, 0xD1D7 },
+{ 0xD1D8, 0xD1D8, 0xD1D8 },
+{ 0xD1D9, 0xD1D9, 0xD1D9 },
+{ 0xD1DA, 0xD1DA, 0xD1DA },
+{ 0xD1DB, 0xD1DB, 0xD1DB },
+{ 0xD1DC, 0xD1DC, 0xD1DC },
+{ 0xD1DD, 0xD1DD, 0xD1DD },
+{ 0xD1DE, 0xD1DE, 0xD1DE },
+{ 0xD1DF, 0xD1DF, 0xD1DF },
+{ 0xD1E0, 0xD1E0, 0xD1E0 },
+{ 0xD1E1, 0xD1E1, 0xD1E1 },
+{ 0xD1E2, 0xD1E2, 0xD1E2 },
+{ 0xD1E3, 0xD1E3, 0xD1E3 },
+{ 0xD1E4, 0xD1E4, 0xD1E4 },
+{ 0xD1E5, 0xD1E5, 0xD1E5 },
+{ 0xD1E6, 0xD1E6, 0xD1E6 },
+{ 0xD1E7, 0xD1E7, 0xD1E7 },
+{ 0xD1E8, 0xD1E8, 0xD1E8 },
+{ 0xD1E9, 0xD1E9, 0xD1E9 },
+{ 0xD1EA, 0xD1EA, 0xD1EA },
+{ 0xD1EB, 0xD1EB, 0xD1EB },
+{ 0xD1EC, 0xD1EC, 0xD1EC },
+{ 0xD1ED, 0xD1ED, 0xD1ED },
+{ 0xD1EE, 0xD1EE, 0xD1EE },
+{ 0xD1EF, 0xD1EF, 0xD1EF },
+{ 0xD1F0, 0xD1F0, 0xD1F0 },
+{ 0xD1F1, 0xD1F1, 0xD1F1 },
+{ 0xD1F2, 0xD1F2, 0xD1F2 },
+{ 0xD1F3, 0xD1F3, 0xD1F3 },
+{ 0xD1F4, 0xD1F4, 0xD1F4 },
+{ 0xD1F5, 0xD1F5, 0xD1F5 },
+{ 0xD1F6, 0xD1F6, 0xD1F6 },
+{ 0xD1F7, 0xD1F7, 0xD1F7 },
+{ 0xD1F8, 0xD1F8, 0xD1F8 },
+{ 0xD1F9, 0xD1F9, 0xD1F9 },
+{ 0xD1FA, 0xD1FA, 0xD1FA },
+{ 0xD1FB, 0xD1FB, 0xD1FB },
+{ 0xD1FC, 0xD1FC, 0xD1FC },
+{ 0xD1FD, 0xD1FD, 0xD1FD },
+{ 0xD1FE, 0xD1FE, 0xD1FE },
+{ 0xD1FF, 0xD1FF, 0xD1FF },
+{ 0xD200, 0xD200, 0xD200 },
+{ 0xD201, 0xD201, 0xD201 },
+{ 0xD202, 0xD202, 0xD202 },
+{ 0xD203, 0xD203, 0xD203 },
+{ 0xD204, 0xD204, 0xD204 },
+{ 0xD205, 0xD205, 0xD205 },
+{ 0xD206, 0xD206, 0xD206 },
+{ 0xD207, 0xD207, 0xD207 },
+{ 0xD208, 0xD208, 0xD208 },
+{ 0xD209, 0xD209, 0xD209 },
+{ 0xD20A, 0xD20A, 0xD20A },
+{ 0xD20B, 0xD20B, 0xD20B },
+{ 0xD20C, 0xD20C, 0xD20C },
+{ 0xD20D, 0xD20D, 0xD20D },
+{ 0xD20E, 0xD20E, 0xD20E },
+{ 0xD20F, 0xD20F, 0xD20F },
+{ 0xD210, 0xD210, 0xD210 },
+{ 0xD211, 0xD211, 0xD211 },
+{ 0xD212, 0xD212, 0xD212 },
+{ 0xD213, 0xD213, 0xD213 },
+{ 0xD214, 0xD214, 0xD214 },
+{ 0xD215, 0xD215, 0xD215 },
+{ 0xD216, 0xD216, 0xD216 },
+{ 0xD217, 0xD217, 0xD217 },
+{ 0xD218, 0xD218, 0xD218 },
+{ 0xD219, 0xD219, 0xD219 },
+{ 0xD21A, 0xD21A, 0xD21A },
+{ 0xD21B, 0xD21B, 0xD21B },
+{ 0xD21C, 0xD21C, 0xD21C },
+{ 0xD21D, 0xD21D, 0xD21D },
+{ 0xD21E, 0xD21E, 0xD21E },
+{ 0xD21F, 0xD21F, 0xD21F },
+{ 0xD220, 0xD220, 0xD220 },
+{ 0xD221, 0xD221, 0xD221 },
+{ 0xD222, 0xD222, 0xD222 },
+{ 0xD223, 0xD223, 0xD223 },
+{ 0xD224, 0xD224, 0xD224 },
+{ 0xD225, 0xD225, 0xD225 },
+{ 0xD226, 0xD226, 0xD226 },
+{ 0xD227, 0xD227, 0xD227 },
+{ 0xD228, 0xD228, 0xD228 },
+{ 0xD229, 0xD229, 0xD229 },
+{ 0xD22A, 0xD22A, 0xD22A },
+{ 0xD22B, 0xD22B, 0xD22B },
+{ 0xD22C, 0xD22C, 0xD22C },
+{ 0xD22D, 0xD22D, 0xD22D },
+{ 0xD22E, 0xD22E, 0xD22E },
+{ 0xD22F, 0xD22F, 0xD22F },
+{ 0xD230, 0xD230, 0xD230 },
+{ 0xD231, 0xD231, 0xD231 },
+{ 0xD232, 0xD232, 0xD232 },
+{ 0xD233, 0xD233, 0xD233 },
+{ 0xD234, 0xD234, 0xD234 },
+{ 0xD235, 0xD235, 0xD235 },
+{ 0xD236, 0xD236, 0xD236 },
+{ 0xD237, 0xD237, 0xD237 },
+{ 0xD238, 0xD238, 0xD238 },
+{ 0xD239, 0xD239, 0xD239 },
+{ 0xD23A, 0xD23A, 0xD23A },
+{ 0xD23B, 0xD23B, 0xD23B },
+{ 0xD23C, 0xD23C, 0xD23C },
+{ 0xD23D, 0xD23D, 0xD23D },
+{ 0xD23E, 0xD23E, 0xD23E },
+{ 0xD23F, 0xD23F, 0xD23F },
+{ 0xD240, 0xD240, 0xD240 },
+{ 0xD241, 0xD241, 0xD241 },
+{ 0xD242, 0xD242, 0xD242 },
+{ 0xD243, 0xD243, 0xD243 },
+{ 0xD244, 0xD244, 0xD244 },
+{ 0xD245, 0xD245, 0xD245 },
+{ 0xD246, 0xD246, 0xD246 },
+{ 0xD247, 0xD247, 0xD247 },
+{ 0xD248, 0xD248, 0xD248 },
+{ 0xD249, 0xD249, 0xD249 },
+{ 0xD24A, 0xD24A, 0xD24A },
+{ 0xD24B, 0xD24B, 0xD24B },
+{ 0xD24C, 0xD24C, 0xD24C },
+{ 0xD24D, 0xD24D, 0xD24D },
+{ 0xD24E, 0xD24E, 0xD24E },
+{ 0xD24F, 0xD24F, 0xD24F },
+{ 0xD250, 0xD250, 0xD250 },
+{ 0xD251, 0xD251, 0xD251 },
+{ 0xD252, 0xD252, 0xD252 },
+{ 0xD253, 0xD253, 0xD253 },
+{ 0xD254, 0xD254, 0xD254 },
+{ 0xD255, 0xD255, 0xD255 },
+{ 0xD256, 0xD256, 0xD256 },
+{ 0xD257, 0xD257, 0xD257 },
+{ 0xD258, 0xD258, 0xD258 },
+{ 0xD259, 0xD259, 0xD259 },
+{ 0xD25A, 0xD25A, 0xD25A },
+{ 0xD25B, 0xD25B, 0xD25B },
+{ 0xD25C, 0xD25C, 0xD25C },
+{ 0xD25D, 0xD25D, 0xD25D },
+{ 0xD25E, 0xD25E, 0xD25E },
+{ 0xD25F, 0xD25F, 0xD25F },
+{ 0xD260, 0xD260, 0xD260 },
+{ 0xD261, 0xD261, 0xD261 },
+{ 0xD262, 0xD262, 0xD262 },
+{ 0xD263, 0xD263, 0xD263 },
+{ 0xD264, 0xD264, 0xD264 },
+{ 0xD265, 0xD265, 0xD265 },
+{ 0xD266, 0xD266, 0xD266 },
+{ 0xD267, 0xD267, 0xD267 },
+{ 0xD268, 0xD268, 0xD268 },
+{ 0xD269, 0xD269, 0xD269 },
+{ 0xD26A, 0xD26A, 0xD26A },
+{ 0xD26B, 0xD26B, 0xD26B },
+{ 0xD26C, 0xD26C, 0xD26C },
+{ 0xD26D, 0xD26D, 0xD26D },
+{ 0xD26E, 0xD26E, 0xD26E },
+{ 0xD26F, 0xD26F, 0xD26F },
+{ 0xD270, 0xD270, 0xD270 },
+{ 0xD271, 0xD271, 0xD271 },
+{ 0xD272, 0xD272, 0xD272 },
+{ 0xD273, 0xD273, 0xD273 },
+{ 0xD274, 0xD274, 0xD274 },
+{ 0xD275, 0xD275, 0xD275 },
+{ 0xD276, 0xD276, 0xD276 },
+{ 0xD277, 0xD277, 0xD277 },
+{ 0xD278, 0xD278, 0xD278 },
+{ 0xD279, 0xD279, 0xD279 },
+{ 0xD27A, 0xD27A, 0xD27A },
+{ 0xD27B, 0xD27B, 0xD27B },
+{ 0xD27C, 0xD27C, 0xD27C },
+{ 0xD27D, 0xD27D, 0xD27D },
+{ 0xD27E, 0xD27E, 0xD27E },
+{ 0xD27F, 0xD27F, 0xD27F },
+{ 0xD280, 0xD280, 0xD280 },
+{ 0xD281, 0xD281, 0xD281 },
+{ 0xD282, 0xD282, 0xD282 },
+{ 0xD283, 0xD283, 0xD283 },
+{ 0xD284, 0xD284, 0xD284 },
+{ 0xD285, 0xD285, 0xD285 },
+{ 0xD286, 0xD286, 0xD286 },
+{ 0xD287, 0xD287, 0xD287 },
+{ 0xD288, 0xD288, 0xD288 },
+{ 0xD289, 0xD289, 0xD289 },
+{ 0xD28A, 0xD28A, 0xD28A },
+{ 0xD28B, 0xD28B, 0xD28B },
+{ 0xD28C, 0xD28C, 0xD28C },
+{ 0xD28D, 0xD28D, 0xD28D },
+{ 0xD28E, 0xD28E, 0xD28E },
+{ 0xD28F, 0xD28F, 0xD28F },
+{ 0xD290, 0xD290, 0xD290 },
+{ 0xD291, 0xD291, 0xD291 },
+{ 0xD292, 0xD292, 0xD292 },
+{ 0xD293, 0xD293, 0xD293 },
+{ 0xD294, 0xD294, 0xD294 },
+{ 0xD295, 0xD295, 0xD295 },
+{ 0xD296, 0xD296, 0xD296 },
+{ 0xD297, 0xD297, 0xD297 },
+{ 0xD298, 0xD298, 0xD298 },
+{ 0xD299, 0xD299, 0xD299 },
+{ 0xD29A, 0xD29A, 0xD29A },
+{ 0xD29B, 0xD29B, 0xD29B },
+{ 0xD29C, 0xD29C, 0xD29C },
+{ 0xD29D, 0xD29D, 0xD29D },
+{ 0xD29E, 0xD29E, 0xD29E },
+{ 0xD29F, 0xD29F, 0xD29F },
+{ 0xD2A0, 0xD2A0, 0xD2A0 },
+{ 0xD2A1, 0xD2A1, 0xD2A1 },
+{ 0xD2A2, 0xD2A2, 0xD2A2 },
+{ 0xD2A3, 0xD2A3, 0xD2A3 },
+{ 0xD2A4, 0xD2A4, 0xD2A4 },
+{ 0xD2A5, 0xD2A5, 0xD2A5 },
+{ 0xD2A6, 0xD2A6, 0xD2A6 },
+{ 0xD2A7, 0xD2A7, 0xD2A7 },
+{ 0xD2A8, 0xD2A8, 0xD2A8 },
+{ 0xD2A9, 0xD2A9, 0xD2A9 },
+{ 0xD2AA, 0xD2AA, 0xD2AA },
+{ 0xD2AB, 0xD2AB, 0xD2AB },
+{ 0xD2AC, 0xD2AC, 0xD2AC },
+{ 0xD2AD, 0xD2AD, 0xD2AD },
+{ 0xD2AE, 0xD2AE, 0xD2AE },
+{ 0xD2AF, 0xD2AF, 0xD2AF },
+{ 0xD2B0, 0xD2B0, 0xD2B0 },
+{ 0xD2B1, 0xD2B1, 0xD2B1 },
+{ 0xD2B2, 0xD2B2, 0xD2B2 },
+{ 0xD2B3, 0xD2B3, 0xD2B3 },
+{ 0xD2B4, 0xD2B4, 0xD2B4 },
+{ 0xD2B5, 0xD2B5, 0xD2B5 },
+{ 0xD2B6, 0xD2B6, 0xD2B6 },
+{ 0xD2B7, 0xD2B7, 0xD2B7 },
+{ 0xD2B8, 0xD2B8, 0xD2B8 },
+{ 0xD2B9, 0xD2B9, 0xD2B9 },
+{ 0xD2BA, 0xD2BA, 0xD2BA },
+{ 0xD2BB, 0xD2BB, 0xD2BB },
+{ 0xD2BC, 0xD2BC, 0xD2BC },
+{ 0xD2BD, 0xD2BD, 0xD2BD },
+{ 0xD2BE, 0xD2BE, 0xD2BE },
+{ 0xD2BF, 0xD2BF, 0xD2BF },
+{ 0xD2C0, 0xD2C0, 0xD2C0 },
+{ 0xD2C1, 0xD2C1, 0xD2C1 },
+{ 0xD2C2, 0xD2C2, 0xD2C2 },
+{ 0xD2C3, 0xD2C3, 0xD2C3 },
+{ 0xD2C4, 0xD2C4, 0xD2C4 },
+{ 0xD2C5, 0xD2C5, 0xD2C5 },
+{ 0xD2C6, 0xD2C6, 0xD2C6 },
+{ 0xD2C7, 0xD2C7, 0xD2C7 },
+{ 0xD2C8, 0xD2C8, 0xD2C8 },
+{ 0xD2C9, 0xD2C9, 0xD2C9 },
+{ 0xD2CA, 0xD2CA, 0xD2CA },
+{ 0xD2CB, 0xD2CB, 0xD2CB },
+{ 0xD2CC, 0xD2CC, 0xD2CC },
+{ 0xD2CD, 0xD2CD, 0xD2CD },
+{ 0xD2CE, 0xD2CE, 0xD2CE },
+{ 0xD2CF, 0xD2CF, 0xD2CF },
+{ 0xD2D0, 0xD2D0, 0xD2D0 },
+{ 0xD2D1, 0xD2D1, 0xD2D1 },
+{ 0xD2D2, 0xD2D2, 0xD2D2 },
+{ 0xD2D3, 0xD2D3, 0xD2D3 },
+{ 0xD2D4, 0xD2D4, 0xD2D4 },
+{ 0xD2D5, 0xD2D5, 0xD2D5 },
+{ 0xD2D6, 0xD2D6, 0xD2D6 },
+{ 0xD2D7, 0xD2D7, 0xD2D7 },
+{ 0xD2D8, 0xD2D8, 0xD2D8 },
+{ 0xD2D9, 0xD2D9, 0xD2D9 },
+{ 0xD2DA, 0xD2DA, 0xD2DA },
+{ 0xD2DB, 0xD2DB, 0xD2DB },
+{ 0xD2DC, 0xD2DC, 0xD2DC },
+{ 0xD2DD, 0xD2DD, 0xD2DD },
+{ 0xD2DE, 0xD2DE, 0xD2DE },
+{ 0xD2DF, 0xD2DF, 0xD2DF },
+{ 0xD2E0, 0xD2E0, 0xD2E0 },
+{ 0xD2E1, 0xD2E1, 0xD2E1 },
+{ 0xD2E2, 0xD2E2, 0xD2E2 },
+{ 0xD2E3, 0xD2E3, 0xD2E3 },
+{ 0xD2E4, 0xD2E4, 0xD2E4 },
+{ 0xD2E5, 0xD2E5, 0xD2E5 },
+{ 0xD2E6, 0xD2E6, 0xD2E6 },
+{ 0xD2E7, 0xD2E7, 0xD2E7 },
+{ 0xD2E8, 0xD2E8, 0xD2E8 },
+{ 0xD2E9, 0xD2E9, 0xD2E9 },
+{ 0xD2EA, 0xD2EA, 0xD2EA },
+{ 0xD2EB, 0xD2EB, 0xD2EB },
+{ 0xD2EC, 0xD2EC, 0xD2EC },
+{ 0xD2ED, 0xD2ED, 0xD2ED },
+{ 0xD2EE, 0xD2EE, 0xD2EE },
+{ 0xD2EF, 0xD2EF, 0xD2EF },
+{ 0xD2F0, 0xD2F0, 0xD2F0 },
+{ 0xD2F1, 0xD2F1, 0xD2F1 },
+{ 0xD2F2, 0xD2F2, 0xD2F2 },
+{ 0xD2F3, 0xD2F3, 0xD2F3 },
+{ 0xD2F4, 0xD2F4, 0xD2F4 },
+{ 0xD2F5, 0xD2F5, 0xD2F5 },
+{ 0xD2F6, 0xD2F6, 0xD2F6 },
+{ 0xD2F7, 0xD2F7, 0xD2F7 },
+{ 0xD2F8, 0xD2F8, 0xD2F8 },
+{ 0xD2F9, 0xD2F9, 0xD2F9 },
+{ 0xD2FA, 0xD2FA, 0xD2FA },
+{ 0xD2FB, 0xD2FB, 0xD2FB },
+{ 0xD2FC, 0xD2FC, 0xD2FC },
+{ 0xD2FD, 0xD2FD, 0xD2FD },
+{ 0xD2FE, 0xD2FE, 0xD2FE },
+{ 0xD2FF, 0xD2FF, 0xD2FF },
+{ 0xD300, 0xD300, 0xD300 },
+{ 0xD301, 0xD301, 0xD301 },
+{ 0xD302, 0xD302, 0xD302 },
+{ 0xD303, 0xD303, 0xD303 },
+{ 0xD304, 0xD304, 0xD304 },
+{ 0xD305, 0xD305, 0xD305 },
+{ 0xD306, 0xD306, 0xD306 },
+{ 0xD307, 0xD307, 0xD307 },
+{ 0xD308, 0xD308, 0xD308 },
+{ 0xD309, 0xD309, 0xD309 },
+{ 0xD30A, 0xD30A, 0xD30A },
+{ 0xD30B, 0xD30B, 0xD30B },
+{ 0xD30C, 0xD30C, 0xD30C },
+{ 0xD30D, 0xD30D, 0xD30D },
+{ 0xD30E, 0xD30E, 0xD30E },
+{ 0xD30F, 0xD30F, 0xD30F },
+{ 0xD310, 0xD310, 0xD310 },
+{ 0xD311, 0xD311, 0xD311 },
+{ 0xD312, 0xD312, 0xD312 },
+{ 0xD313, 0xD313, 0xD313 },
+{ 0xD314, 0xD314, 0xD314 },
+{ 0xD315, 0xD315, 0xD315 },
+{ 0xD316, 0xD316, 0xD316 },
+{ 0xD317, 0xD317, 0xD317 },
+{ 0xD318, 0xD318, 0xD318 },
+{ 0xD319, 0xD319, 0xD319 },
+{ 0xD31A, 0xD31A, 0xD31A },
+{ 0xD31B, 0xD31B, 0xD31B },
+{ 0xD31C, 0xD31C, 0xD31C },
+{ 0xD31D, 0xD31D, 0xD31D },
+{ 0xD31E, 0xD31E, 0xD31E },
+{ 0xD31F, 0xD31F, 0xD31F },
+{ 0xD320, 0xD320, 0xD320 },
+{ 0xD321, 0xD321, 0xD321 },
+{ 0xD322, 0xD322, 0xD322 },
+{ 0xD323, 0xD323, 0xD323 },
+{ 0xD324, 0xD324, 0xD324 },
+{ 0xD325, 0xD325, 0xD325 },
+{ 0xD326, 0xD326, 0xD326 },
+{ 0xD327, 0xD327, 0xD327 },
+{ 0xD328, 0xD328, 0xD328 },
+{ 0xD329, 0xD329, 0xD329 },
+{ 0xD32A, 0xD32A, 0xD32A },
+{ 0xD32B, 0xD32B, 0xD32B },
+{ 0xD32C, 0xD32C, 0xD32C },
+{ 0xD32D, 0xD32D, 0xD32D },
+{ 0xD32E, 0xD32E, 0xD32E },
+{ 0xD32F, 0xD32F, 0xD32F },
+{ 0xD330, 0xD330, 0xD330 },
+{ 0xD331, 0xD331, 0xD331 },
+{ 0xD332, 0xD332, 0xD332 },
+{ 0xD333, 0xD333, 0xD333 },
+{ 0xD334, 0xD334, 0xD334 },
+{ 0xD335, 0xD335, 0xD335 },
+{ 0xD336, 0xD336, 0xD336 },
+{ 0xD337, 0xD337, 0xD337 },
+{ 0xD338, 0xD338, 0xD338 },
+{ 0xD339, 0xD339, 0xD339 },
+{ 0xD33A, 0xD33A, 0xD33A },
+{ 0xD33B, 0xD33B, 0xD33B },
+{ 0xD33C, 0xD33C, 0xD33C },
+{ 0xD33D, 0xD33D, 0xD33D },
+{ 0xD33E, 0xD33E, 0xD33E },
+{ 0xD33F, 0xD33F, 0xD33F },
+{ 0xD340, 0xD340, 0xD340 },
+{ 0xD341, 0xD341, 0xD341 },
+{ 0xD342, 0xD342, 0xD342 },
+{ 0xD343, 0xD343, 0xD343 },
+{ 0xD344, 0xD344, 0xD344 },
+{ 0xD345, 0xD345, 0xD345 },
+{ 0xD346, 0xD346, 0xD346 },
+{ 0xD347, 0xD347, 0xD347 },
+{ 0xD348, 0xD348, 0xD348 },
+{ 0xD349, 0xD349, 0xD349 },
+{ 0xD34A, 0xD34A, 0xD34A },
+{ 0xD34B, 0xD34B, 0xD34B },
+{ 0xD34C, 0xD34C, 0xD34C },
+{ 0xD34D, 0xD34D, 0xD34D },
+{ 0xD34E, 0xD34E, 0xD34E },
+{ 0xD34F, 0xD34F, 0xD34F },
+{ 0xD350, 0xD350, 0xD350 },
+{ 0xD351, 0xD351, 0xD351 },
+{ 0xD352, 0xD352, 0xD352 },
+{ 0xD353, 0xD353, 0xD353 },
+{ 0xD354, 0xD354, 0xD354 },
+{ 0xD355, 0xD355, 0xD355 },
+{ 0xD356, 0xD356, 0xD356 },
+{ 0xD357, 0xD357, 0xD357 },
+{ 0xD358, 0xD358, 0xD358 },
+{ 0xD359, 0xD359, 0xD359 },
+{ 0xD35A, 0xD35A, 0xD35A },
+{ 0xD35B, 0xD35B, 0xD35B },
+{ 0xD35C, 0xD35C, 0xD35C },
+{ 0xD35D, 0xD35D, 0xD35D },
+{ 0xD35E, 0xD35E, 0xD35E },
+{ 0xD35F, 0xD35F, 0xD35F },
+{ 0xD360, 0xD360, 0xD360 },
+{ 0xD361, 0xD361, 0xD361 },
+{ 0xD362, 0xD362, 0xD362 },
+{ 0xD363, 0xD363, 0xD363 },
+{ 0xD364, 0xD364, 0xD364 },
+{ 0xD365, 0xD365, 0xD365 },
+{ 0xD366, 0xD366, 0xD366 },
+{ 0xD367, 0xD367, 0xD367 },
+{ 0xD368, 0xD368, 0xD368 },
+{ 0xD369, 0xD369, 0xD369 },
+{ 0xD36A, 0xD36A, 0xD36A },
+{ 0xD36B, 0xD36B, 0xD36B },
+{ 0xD36C, 0xD36C, 0xD36C },
+{ 0xD36D, 0xD36D, 0xD36D },
+{ 0xD36E, 0xD36E, 0xD36E },
+{ 0xD36F, 0xD36F, 0xD36F },
+{ 0xD370, 0xD370, 0xD370 },
+{ 0xD371, 0xD371, 0xD371 },
+{ 0xD372, 0xD372, 0xD372 },
+{ 0xD373, 0xD373, 0xD373 },
+{ 0xD374, 0xD374, 0xD374 },
+{ 0xD375, 0xD375, 0xD375 },
+{ 0xD376, 0xD376, 0xD376 },
+{ 0xD377, 0xD377, 0xD377 },
+{ 0xD378, 0xD378, 0xD378 },
+{ 0xD379, 0xD379, 0xD379 },
+{ 0xD37A, 0xD37A, 0xD37A },
+{ 0xD37B, 0xD37B, 0xD37B },
+{ 0xD37C, 0xD37C, 0xD37C },
+{ 0xD37D, 0xD37D, 0xD37D },
+{ 0xD37E, 0xD37E, 0xD37E },
+{ 0xD37F, 0xD37F, 0xD37F },
+{ 0xD380, 0xD380, 0xD380 },
+{ 0xD381, 0xD381, 0xD381 },
+{ 0xD382, 0xD382, 0xD382 },
+{ 0xD383, 0xD383, 0xD383 },
+{ 0xD384, 0xD384, 0xD384 },
+{ 0xD385, 0xD385, 0xD385 },
+{ 0xD386, 0xD386, 0xD386 },
+{ 0xD387, 0xD387, 0xD387 },
+{ 0xD388, 0xD388, 0xD388 },
+{ 0xD389, 0xD389, 0xD389 },
+{ 0xD38A, 0xD38A, 0xD38A },
+{ 0xD38B, 0xD38B, 0xD38B },
+{ 0xD38C, 0xD38C, 0xD38C },
+{ 0xD38D, 0xD38D, 0xD38D },
+{ 0xD38E, 0xD38E, 0xD38E },
+{ 0xD38F, 0xD38F, 0xD38F },
+{ 0xD390, 0xD390, 0xD390 },
+{ 0xD391, 0xD391, 0xD391 },
+{ 0xD392, 0xD392, 0xD392 },
+{ 0xD393, 0xD393, 0xD393 },
+{ 0xD394, 0xD394, 0xD394 },
+{ 0xD395, 0xD395, 0xD395 },
+{ 0xD396, 0xD396, 0xD396 },
+{ 0xD397, 0xD397, 0xD397 },
+{ 0xD398, 0xD398, 0xD398 },
+{ 0xD399, 0xD399, 0xD399 },
+{ 0xD39A, 0xD39A, 0xD39A },
+{ 0xD39B, 0xD39B, 0xD39B },
+{ 0xD39C, 0xD39C, 0xD39C },
+{ 0xD39D, 0xD39D, 0xD39D },
+{ 0xD39E, 0xD39E, 0xD39E },
+{ 0xD39F, 0xD39F, 0xD39F },
+{ 0xD3A0, 0xD3A0, 0xD3A0 },
+{ 0xD3A1, 0xD3A1, 0xD3A1 },
+{ 0xD3A2, 0xD3A2, 0xD3A2 },
+{ 0xD3A3, 0xD3A3, 0xD3A3 },
+{ 0xD3A4, 0xD3A4, 0xD3A4 },
+{ 0xD3A5, 0xD3A5, 0xD3A5 },
+{ 0xD3A6, 0xD3A6, 0xD3A6 },
+{ 0xD3A7, 0xD3A7, 0xD3A7 },
+{ 0xD3A8, 0xD3A8, 0xD3A8 },
+{ 0xD3A9, 0xD3A9, 0xD3A9 },
+{ 0xD3AA, 0xD3AA, 0xD3AA },
+{ 0xD3AB, 0xD3AB, 0xD3AB },
+{ 0xD3AC, 0xD3AC, 0xD3AC },
+{ 0xD3AD, 0xD3AD, 0xD3AD },
+{ 0xD3AE, 0xD3AE, 0xD3AE },
+{ 0xD3AF, 0xD3AF, 0xD3AF },
+{ 0xD3B0, 0xD3B0, 0xD3B0 },
+{ 0xD3B1, 0xD3B1, 0xD3B1 },
+{ 0xD3B2, 0xD3B2, 0xD3B2 },
+{ 0xD3B3, 0xD3B3, 0xD3B3 },
+{ 0xD3B4, 0xD3B4, 0xD3B4 },
+{ 0xD3B5, 0xD3B5, 0xD3B5 },
+{ 0xD3B6, 0xD3B6, 0xD3B6 },
+{ 0xD3B7, 0xD3B7, 0xD3B7 },
+{ 0xD3B8, 0xD3B8, 0xD3B8 },
+{ 0xD3B9, 0xD3B9, 0xD3B9 },
+{ 0xD3BA, 0xD3BA, 0xD3BA },
+{ 0xD3BB, 0xD3BB, 0xD3BB },
+{ 0xD3BC, 0xD3BC, 0xD3BC },
+{ 0xD3BD, 0xD3BD, 0xD3BD },
+{ 0xD3BE, 0xD3BE, 0xD3BE },
+{ 0xD3BF, 0xD3BF, 0xD3BF },
+{ 0xD3C0, 0xD3C0, 0xD3C0 },
+{ 0xD3C1, 0xD3C1, 0xD3C1 },
+{ 0xD3C2, 0xD3C2, 0xD3C2 },
+{ 0xD3C3, 0xD3C3, 0xD3C3 },
+{ 0xD3C4, 0xD3C4, 0xD3C4 },
+{ 0xD3C5, 0xD3C5, 0xD3C5 },
+{ 0xD3C6, 0xD3C6, 0xD3C6 },
+{ 0xD3C7, 0xD3C7, 0xD3C7 },
+{ 0xD3C8, 0xD3C8, 0xD3C8 },
+{ 0xD3C9, 0xD3C9, 0xD3C9 },
+{ 0xD3CA, 0xD3CA, 0xD3CA },
+{ 0xD3CB, 0xD3CB, 0xD3CB },
+{ 0xD3CC, 0xD3CC, 0xD3CC },
+{ 0xD3CD, 0xD3CD, 0xD3CD },
+{ 0xD3CE, 0xD3CE, 0xD3CE },
+{ 0xD3CF, 0xD3CF, 0xD3CF },
+{ 0xD3D0, 0xD3D0, 0xD3D0 },
+{ 0xD3D1, 0xD3D1, 0xD3D1 },
+{ 0xD3D2, 0xD3D2, 0xD3D2 },
+{ 0xD3D3, 0xD3D3, 0xD3D3 },
+{ 0xD3D4, 0xD3D4, 0xD3D4 },
+{ 0xD3D5, 0xD3D5, 0xD3D5 },
+{ 0xD3D6, 0xD3D6, 0xD3D6 },
+{ 0xD3D7, 0xD3D7, 0xD3D7 },
+{ 0xD3D8, 0xD3D8, 0xD3D8 },
+{ 0xD3D9, 0xD3D9, 0xD3D9 },
+{ 0xD3DA, 0xD3DA, 0xD3DA },
+{ 0xD3DB, 0xD3DB, 0xD3DB },
+{ 0xD3DC, 0xD3DC, 0xD3DC },
+{ 0xD3DD, 0xD3DD, 0xD3DD },
+{ 0xD3DE, 0xD3DE, 0xD3DE },
+{ 0xD3DF, 0xD3DF, 0xD3DF },
+{ 0xD3E0, 0xD3E0, 0xD3E0 },
+{ 0xD3E1, 0xD3E1, 0xD3E1 },
+{ 0xD3E2, 0xD3E2, 0xD3E2 },
+{ 0xD3E3, 0xD3E3, 0xD3E3 },
+{ 0xD3E4, 0xD3E4, 0xD3E4 },
+{ 0xD3E5, 0xD3E5, 0xD3E5 },
+{ 0xD3E6, 0xD3E6, 0xD3E6 },
+{ 0xD3E7, 0xD3E7, 0xD3E7 },
+{ 0xD3E8, 0xD3E8, 0xD3E8 },
+{ 0xD3E9, 0xD3E9, 0xD3E9 },
+{ 0xD3EA, 0xD3EA, 0xD3EA },
+{ 0xD3EB, 0xD3EB, 0xD3EB },
+{ 0xD3EC, 0xD3EC, 0xD3EC },
+{ 0xD3ED, 0xD3ED, 0xD3ED },
+{ 0xD3EE, 0xD3EE, 0xD3EE },
+{ 0xD3EF, 0xD3EF, 0xD3EF },
+{ 0xD3F0, 0xD3F0, 0xD3F0 },
+{ 0xD3F1, 0xD3F1, 0xD3F1 },
+{ 0xD3F2, 0xD3F2, 0xD3F2 },
+{ 0xD3F3, 0xD3F3, 0xD3F3 },
+{ 0xD3F4, 0xD3F4, 0xD3F4 },
+{ 0xD3F5, 0xD3F5, 0xD3F5 },
+{ 0xD3F6, 0xD3F6, 0xD3F6 },
+{ 0xD3F7, 0xD3F7, 0xD3F7 },
+{ 0xD3F8, 0xD3F8, 0xD3F8 },
+{ 0xD3F9, 0xD3F9, 0xD3F9 },
+{ 0xD3FA, 0xD3FA, 0xD3FA },
+{ 0xD3FB, 0xD3FB, 0xD3FB },
+{ 0xD3FC, 0xD3FC, 0xD3FC },
+{ 0xD3FD, 0xD3FD, 0xD3FD },
+{ 0xD3FE, 0xD3FE, 0xD3FE },
+{ 0xD3FF, 0xD3FF, 0xD3FF },
+{ 0xD400, 0xD400, 0xD400 },
+{ 0xD401, 0xD401, 0xD401 },
+{ 0xD402, 0xD402, 0xD402 },
+{ 0xD403, 0xD403, 0xD403 },
+{ 0xD404, 0xD404, 0xD404 },
+{ 0xD405, 0xD405, 0xD405 },
+{ 0xD406, 0xD406, 0xD406 },
+{ 0xD407, 0xD407, 0xD407 },
+{ 0xD408, 0xD408, 0xD408 },
+{ 0xD409, 0xD409, 0xD409 },
+{ 0xD40A, 0xD40A, 0xD40A },
+{ 0xD40B, 0xD40B, 0xD40B },
+{ 0xD40C, 0xD40C, 0xD40C },
+{ 0xD40D, 0xD40D, 0xD40D },
+{ 0xD40E, 0xD40E, 0xD40E },
+{ 0xD40F, 0xD40F, 0xD40F },
+{ 0xD410, 0xD410, 0xD410 },
+{ 0xD411, 0xD411, 0xD411 },
+{ 0xD412, 0xD412, 0xD412 },
+{ 0xD413, 0xD413, 0xD413 },
+{ 0xD414, 0xD414, 0xD414 },
+{ 0xD415, 0xD415, 0xD415 },
+{ 0xD416, 0xD416, 0xD416 },
+{ 0xD417, 0xD417, 0xD417 },
+{ 0xD418, 0xD418, 0xD418 },
+{ 0xD419, 0xD419, 0xD419 },
+{ 0xD41A, 0xD41A, 0xD41A },
+{ 0xD41B, 0xD41B, 0xD41B },
+{ 0xD41C, 0xD41C, 0xD41C },
+{ 0xD41D, 0xD41D, 0xD41D },
+{ 0xD41E, 0xD41E, 0xD41E },
+{ 0xD41F, 0xD41F, 0xD41F },
+{ 0xD420, 0xD420, 0xD420 },
+{ 0xD421, 0xD421, 0xD421 },
+{ 0xD422, 0xD422, 0xD422 },
+{ 0xD423, 0xD423, 0xD423 },
+{ 0xD424, 0xD424, 0xD424 },
+{ 0xD425, 0xD425, 0xD425 },
+{ 0xD426, 0xD426, 0xD426 },
+{ 0xD427, 0xD427, 0xD427 },
+{ 0xD428, 0xD428, 0xD428 },
+{ 0xD429, 0xD429, 0xD429 },
+{ 0xD42A, 0xD42A, 0xD42A },
+{ 0xD42B, 0xD42B, 0xD42B },
+{ 0xD42C, 0xD42C, 0xD42C },
+{ 0xD42D, 0xD42D, 0xD42D },
+{ 0xD42E, 0xD42E, 0xD42E },
+{ 0xD42F, 0xD42F, 0xD42F },
+{ 0xD430, 0xD430, 0xD430 },
+{ 0xD431, 0xD431, 0xD431 },
+{ 0xD432, 0xD432, 0xD432 },
+{ 0xD433, 0xD433, 0xD433 },
+{ 0xD434, 0xD434, 0xD434 },
+{ 0xD435, 0xD435, 0xD435 },
+{ 0xD436, 0xD436, 0xD436 },
+{ 0xD437, 0xD437, 0xD437 },
+{ 0xD438, 0xD438, 0xD438 },
+{ 0xD439, 0xD439, 0xD439 },
+{ 0xD43A, 0xD43A, 0xD43A },
+{ 0xD43B, 0xD43B, 0xD43B },
+{ 0xD43C, 0xD43C, 0xD43C },
+{ 0xD43D, 0xD43D, 0xD43D },
+{ 0xD43E, 0xD43E, 0xD43E },
+{ 0xD43F, 0xD43F, 0xD43F },
+{ 0xD440, 0xD440, 0xD440 },
+{ 0xD441, 0xD441, 0xD441 },
+{ 0xD442, 0xD442, 0xD442 },
+{ 0xD443, 0xD443, 0xD443 },
+{ 0xD444, 0xD444, 0xD444 },
+{ 0xD445, 0xD445, 0xD445 },
+{ 0xD446, 0xD446, 0xD446 },
+{ 0xD447, 0xD447, 0xD447 },
+{ 0xD448, 0xD448, 0xD448 },
+{ 0xD449, 0xD449, 0xD449 },
+{ 0xD44A, 0xD44A, 0xD44A },
+{ 0xD44B, 0xD44B, 0xD44B },
+{ 0xD44C, 0xD44C, 0xD44C },
+{ 0xD44D, 0xD44D, 0xD44D },
+{ 0xD44E, 0xD44E, 0xD44E },
+{ 0xD44F, 0xD44F, 0xD44F },
+{ 0xD450, 0xD450, 0xD450 },
+{ 0xD451, 0xD451, 0xD451 },
+{ 0xD452, 0xD452, 0xD452 },
+{ 0xD453, 0xD453, 0xD453 },
+{ 0xD454, 0xD454, 0xD454 },
+{ 0xD455, 0xD455, 0xD455 },
+{ 0xD456, 0xD456, 0xD456 },
+{ 0xD457, 0xD457, 0xD457 },
+{ 0xD458, 0xD458, 0xD458 },
+{ 0xD459, 0xD459, 0xD459 },
+{ 0xD45A, 0xD45A, 0xD45A },
+{ 0xD45B, 0xD45B, 0xD45B },
+{ 0xD45C, 0xD45C, 0xD45C },
+{ 0xD45D, 0xD45D, 0xD45D },
+{ 0xD45E, 0xD45E, 0xD45E },
+{ 0xD45F, 0xD45F, 0xD45F },
+{ 0xD460, 0xD460, 0xD460 },
+{ 0xD461, 0xD461, 0xD461 },
+{ 0xD462, 0xD462, 0xD462 },
+{ 0xD463, 0xD463, 0xD463 },
+{ 0xD464, 0xD464, 0xD464 },
+{ 0xD465, 0xD465, 0xD465 },
+{ 0xD466, 0xD466, 0xD466 },
+{ 0xD467, 0xD467, 0xD467 },
+{ 0xD468, 0xD468, 0xD468 },
+{ 0xD469, 0xD469, 0xD469 },
+{ 0xD46A, 0xD46A, 0xD46A },
+{ 0xD46B, 0xD46B, 0xD46B },
+{ 0xD46C, 0xD46C, 0xD46C },
+{ 0xD46D, 0xD46D, 0xD46D },
+{ 0xD46E, 0xD46E, 0xD46E },
+{ 0xD46F, 0xD46F, 0xD46F },
+{ 0xD470, 0xD470, 0xD470 },
+{ 0xD471, 0xD471, 0xD471 },
+{ 0xD472, 0xD472, 0xD472 },
+{ 0xD473, 0xD473, 0xD473 },
+{ 0xD474, 0xD474, 0xD474 },
+{ 0xD475, 0xD475, 0xD475 },
+{ 0xD476, 0xD476, 0xD476 },
+{ 0xD477, 0xD477, 0xD477 },
+{ 0xD478, 0xD478, 0xD478 },
+{ 0xD479, 0xD479, 0xD479 },
+{ 0xD47A, 0xD47A, 0xD47A },
+{ 0xD47B, 0xD47B, 0xD47B },
+{ 0xD47C, 0xD47C, 0xD47C },
+{ 0xD47D, 0xD47D, 0xD47D },
+{ 0xD47E, 0xD47E, 0xD47E },
+{ 0xD47F, 0xD47F, 0xD47F },
+{ 0xD480, 0xD480, 0xD480 },
+{ 0xD481, 0xD481, 0xD481 },
+{ 0xD482, 0xD482, 0xD482 },
+{ 0xD483, 0xD483, 0xD483 },
+{ 0xD484, 0xD484, 0xD484 },
+{ 0xD485, 0xD485, 0xD485 },
+{ 0xD486, 0xD486, 0xD486 },
+{ 0xD487, 0xD487, 0xD487 },
+{ 0xD488, 0xD488, 0xD488 },
+{ 0xD489, 0xD489, 0xD489 },
+{ 0xD48A, 0xD48A, 0xD48A },
+{ 0xD48B, 0xD48B, 0xD48B },
+{ 0xD48C, 0xD48C, 0xD48C },
+{ 0xD48D, 0xD48D, 0xD48D },
+{ 0xD48E, 0xD48E, 0xD48E },
+{ 0xD48F, 0xD48F, 0xD48F },
+{ 0xD490, 0xD490, 0xD490 },
+{ 0xD491, 0xD491, 0xD491 },
+{ 0xD492, 0xD492, 0xD492 },
+{ 0xD493, 0xD493, 0xD493 },
+{ 0xD494, 0xD494, 0xD494 },
+{ 0xD495, 0xD495, 0xD495 },
+{ 0xD496, 0xD496, 0xD496 },
+{ 0xD497, 0xD497, 0xD497 },
+{ 0xD498, 0xD498, 0xD498 },
+{ 0xD499, 0xD499, 0xD499 },
+{ 0xD49A, 0xD49A, 0xD49A },
+{ 0xD49B, 0xD49B, 0xD49B },
+{ 0xD49C, 0xD49C, 0xD49C },
+{ 0xD49D, 0xD49D, 0xD49D },
+{ 0xD49E, 0xD49E, 0xD49E },
+{ 0xD49F, 0xD49F, 0xD49F },
+{ 0xD4A0, 0xD4A0, 0xD4A0 },
+{ 0xD4A1, 0xD4A1, 0xD4A1 },
+{ 0xD4A2, 0xD4A2, 0xD4A2 },
+{ 0xD4A3, 0xD4A3, 0xD4A3 },
+{ 0xD4A4, 0xD4A4, 0xD4A4 },
+{ 0xD4A5, 0xD4A5, 0xD4A5 },
+{ 0xD4A6, 0xD4A6, 0xD4A6 },
+{ 0xD4A7, 0xD4A7, 0xD4A7 },
+{ 0xD4A8, 0xD4A8, 0xD4A8 },
+{ 0xD4A9, 0xD4A9, 0xD4A9 },
+{ 0xD4AA, 0xD4AA, 0xD4AA },
+{ 0xD4AB, 0xD4AB, 0xD4AB },
+{ 0xD4AC, 0xD4AC, 0xD4AC },
+{ 0xD4AD, 0xD4AD, 0xD4AD },
+{ 0xD4AE, 0xD4AE, 0xD4AE },
+{ 0xD4AF, 0xD4AF, 0xD4AF },
+{ 0xD4B0, 0xD4B0, 0xD4B0 },
+{ 0xD4B1, 0xD4B1, 0xD4B1 },
+{ 0xD4B2, 0xD4B2, 0xD4B2 },
+{ 0xD4B3, 0xD4B3, 0xD4B3 },
+{ 0xD4B4, 0xD4B4, 0xD4B4 },
+{ 0xD4B5, 0xD4B5, 0xD4B5 },
+{ 0xD4B6, 0xD4B6, 0xD4B6 },
+{ 0xD4B7, 0xD4B7, 0xD4B7 },
+{ 0xD4B8, 0xD4B8, 0xD4B8 },
+{ 0xD4B9, 0xD4B9, 0xD4B9 },
+{ 0xD4BA, 0xD4BA, 0xD4BA },
+{ 0xD4BB, 0xD4BB, 0xD4BB },
+{ 0xD4BC, 0xD4BC, 0xD4BC },
+{ 0xD4BD, 0xD4BD, 0xD4BD },
+{ 0xD4BE, 0xD4BE, 0xD4BE },
+{ 0xD4BF, 0xD4BF, 0xD4BF },
+{ 0xD4C0, 0xD4C0, 0xD4C0 },
+{ 0xD4C1, 0xD4C1, 0xD4C1 },
+{ 0xD4C2, 0xD4C2, 0xD4C2 },
+{ 0xD4C3, 0xD4C3, 0xD4C3 },
+{ 0xD4C4, 0xD4C4, 0xD4C4 },
+{ 0xD4C5, 0xD4C5, 0xD4C5 },
+{ 0xD4C6, 0xD4C6, 0xD4C6 },
+{ 0xD4C7, 0xD4C7, 0xD4C7 },
+{ 0xD4C8, 0xD4C8, 0xD4C8 },
+{ 0xD4C9, 0xD4C9, 0xD4C9 },
+{ 0xD4CA, 0xD4CA, 0xD4CA },
+{ 0xD4CB, 0xD4CB, 0xD4CB },
+{ 0xD4CC, 0xD4CC, 0xD4CC },
+{ 0xD4CD, 0xD4CD, 0xD4CD },
+{ 0xD4CE, 0xD4CE, 0xD4CE },
+{ 0xD4CF, 0xD4CF, 0xD4CF },
+{ 0xD4D0, 0xD4D0, 0xD4D0 },
+{ 0xD4D1, 0xD4D1, 0xD4D1 },
+{ 0xD4D2, 0xD4D2, 0xD4D2 },
+{ 0xD4D3, 0xD4D3, 0xD4D3 },
+{ 0xD4D4, 0xD4D4, 0xD4D4 },
+{ 0xD4D5, 0xD4D5, 0xD4D5 },
+{ 0xD4D6, 0xD4D6, 0xD4D6 },
+{ 0xD4D7, 0xD4D7, 0xD4D7 },
+{ 0xD4D8, 0xD4D8, 0xD4D8 },
+{ 0xD4D9, 0xD4D9, 0xD4D9 },
+{ 0xD4DA, 0xD4DA, 0xD4DA },
+{ 0xD4DB, 0xD4DB, 0xD4DB },
+{ 0xD4DC, 0xD4DC, 0xD4DC },
+{ 0xD4DD, 0xD4DD, 0xD4DD },
+{ 0xD4DE, 0xD4DE, 0xD4DE },
+{ 0xD4DF, 0xD4DF, 0xD4DF },
+{ 0xD4E0, 0xD4E0, 0xD4E0 },
+{ 0xD4E1, 0xD4E1, 0xD4E1 },
+{ 0xD4E2, 0xD4E2, 0xD4E2 },
+{ 0xD4E3, 0xD4E3, 0xD4E3 },
+{ 0xD4E4, 0xD4E4, 0xD4E4 },
+{ 0xD4E5, 0xD4E5, 0xD4E5 },
+{ 0xD4E6, 0xD4E6, 0xD4E6 },
+{ 0xD4E7, 0xD4E7, 0xD4E7 },
+{ 0xD4E8, 0xD4E8, 0xD4E8 },
+{ 0xD4E9, 0xD4E9, 0xD4E9 },
+{ 0xD4EA, 0xD4EA, 0xD4EA },
+{ 0xD4EB, 0xD4EB, 0xD4EB },
+{ 0xD4EC, 0xD4EC, 0xD4EC },
+{ 0xD4ED, 0xD4ED, 0xD4ED },
+{ 0xD4EE, 0xD4EE, 0xD4EE },
+{ 0xD4EF, 0xD4EF, 0xD4EF },
+{ 0xD4F0, 0xD4F0, 0xD4F0 },
+{ 0xD4F1, 0xD4F1, 0xD4F1 },
+{ 0xD4F2, 0xD4F2, 0xD4F2 },
+{ 0xD4F3, 0xD4F3, 0xD4F3 },
+{ 0xD4F4, 0xD4F4, 0xD4F4 },
+{ 0xD4F5, 0xD4F5, 0xD4F5 },
+{ 0xD4F6, 0xD4F6, 0xD4F6 },
+{ 0xD4F7, 0xD4F7, 0xD4F7 },
+{ 0xD4F8, 0xD4F8, 0xD4F8 },
+{ 0xD4F9, 0xD4F9, 0xD4F9 },
+{ 0xD4FA, 0xD4FA, 0xD4FA },
+{ 0xD4FB, 0xD4FB, 0xD4FB },
+{ 0xD4FC, 0xD4FC, 0xD4FC },
+{ 0xD4FD, 0xD4FD, 0xD4FD },
+{ 0xD4FE, 0xD4FE, 0xD4FE },
+{ 0xD4FF, 0xD4FF, 0xD4FF },
+{ 0xD500, 0xD500, 0xD500 },
+{ 0xD501, 0xD501, 0xD501 },
+{ 0xD502, 0xD502, 0xD502 },
+{ 0xD503, 0xD503, 0xD503 },
+{ 0xD504, 0xD504, 0xD504 },
+{ 0xD505, 0xD505, 0xD505 },
+{ 0xD506, 0xD506, 0xD506 },
+{ 0xD507, 0xD507, 0xD507 },
+{ 0xD508, 0xD508, 0xD508 },
+{ 0xD509, 0xD509, 0xD509 },
+{ 0xD50A, 0xD50A, 0xD50A },
+{ 0xD50B, 0xD50B, 0xD50B },
+{ 0xD50C, 0xD50C, 0xD50C },
+{ 0xD50D, 0xD50D, 0xD50D },
+{ 0xD50E, 0xD50E, 0xD50E },
+{ 0xD50F, 0xD50F, 0xD50F },
+{ 0xD510, 0xD510, 0xD510 },
+{ 0xD511, 0xD511, 0xD511 },
+{ 0xD512, 0xD512, 0xD512 },
+{ 0xD513, 0xD513, 0xD513 },
+{ 0xD514, 0xD514, 0xD514 },
+{ 0xD515, 0xD515, 0xD515 },
+{ 0xD516, 0xD516, 0xD516 },
+{ 0xD517, 0xD517, 0xD517 },
+{ 0xD518, 0xD518, 0xD518 },
+{ 0xD519, 0xD519, 0xD519 },
+{ 0xD51A, 0xD51A, 0xD51A },
+{ 0xD51B, 0xD51B, 0xD51B },
+{ 0xD51C, 0xD51C, 0xD51C },
+{ 0xD51D, 0xD51D, 0xD51D },
+{ 0xD51E, 0xD51E, 0xD51E },
+{ 0xD51F, 0xD51F, 0xD51F },
+{ 0xD520, 0xD520, 0xD520 },
+{ 0xD521, 0xD521, 0xD521 },
+{ 0xD522, 0xD522, 0xD522 },
+{ 0xD523, 0xD523, 0xD523 },
+{ 0xD524, 0xD524, 0xD524 },
+{ 0xD525, 0xD525, 0xD525 },
+{ 0xD526, 0xD526, 0xD526 },
+{ 0xD527, 0xD527, 0xD527 },
+{ 0xD528, 0xD528, 0xD528 },
+{ 0xD529, 0xD529, 0xD529 },
+{ 0xD52A, 0xD52A, 0xD52A },
+{ 0xD52B, 0xD52B, 0xD52B },
+{ 0xD52C, 0xD52C, 0xD52C },
+{ 0xD52D, 0xD52D, 0xD52D },
+{ 0xD52E, 0xD52E, 0xD52E },
+{ 0xD52F, 0xD52F, 0xD52F },
+{ 0xD530, 0xD530, 0xD530 },
+{ 0xD531, 0xD531, 0xD531 },
+{ 0xD532, 0xD532, 0xD532 },
+{ 0xD533, 0xD533, 0xD533 },
+{ 0xD534, 0xD534, 0xD534 },
+{ 0xD535, 0xD535, 0xD535 },
+{ 0xD536, 0xD536, 0xD536 },
+{ 0xD537, 0xD537, 0xD537 },
+{ 0xD538, 0xD538, 0xD538 },
+{ 0xD539, 0xD539, 0xD539 },
+{ 0xD53A, 0xD53A, 0xD53A },
+{ 0xD53B, 0xD53B, 0xD53B },
+{ 0xD53C, 0xD53C, 0xD53C },
+{ 0xD53D, 0xD53D, 0xD53D },
+{ 0xD53E, 0xD53E, 0xD53E },
+{ 0xD53F, 0xD53F, 0xD53F },
+{ 0xD540, 0xD540, 0xD540 },
+{ 0xD541, 0xD541, 0xD541 },
+{ 0xD542, 0xD542, 0xD542 },
+{ 0xD543, 0xD543, 0xD543 },
+{ 0xD544, 0xD544, 0xD544 },
+{ 0xD545, 0xD545, 0xD545 },
+{ 0xD546, 0xD546, 0xD546 },
+{ 0xD547, 0xD547, 0xD547 },
+{ 0xD548, 0xD548, 0xD548 },
+{ 0xD549, 0xD549, 0xD549 },
+{ 0xD54A, 0xD54A, 0xD54A },
+{ 0xD54B, 0xD54B, 0xD54B },
+{ 0xD54C, 0xD54C, 0xD54C },
+{ 0xD54D, 0xD54D, 0xD54D },
+{ 0xD54E, 0xD54E, 0xD54E },
+{ 0xD54F, 0xD54F, 0xD54F },
+{ 0xD550, 0xD550, 0xD550 },
+{ 0xD551, 0xD551, 0xD551 },
+{ 0xD552, 0xD552, 0xD552 },
+{ 0xD553, 0xD553, 0xD553 },
+{ 0xD554, 0xD554, 0xD554 },
+{ 0xD555, 0xD555, 0xD555 },
+{ 0xD556, 0xD556, 0xD556 },
+{ 0xD557, 0xD557, 0xD557 },
+{ 0xD558, 0xD558, 0xD558 },
+{ 0xD559, 0xD559, 0xD559 },
+{ 0xD55A, 0xD55A, 0xD55A },
+{ 0xD55B, 0xD55B, 0xD55B },
+{ 0xD55C, 0xD55C, 0xD55C },
+{ 0xD55D, 0xD55D, 0xD55D },
+{ 0xD55E, 0xD55E, 0xD55E },
+{ 0xD55F, 0xD55F, 0xD55F },
+{ 0xD560, 0xD560, 0xD560 },
+{ 0xD561, 0xD561, 0xD561 },
+{ 0xD562, 0xD562, 0xD562 },
+{ 0xD563, 0xD563, 0xD563 },
+{ 0xD564, 0xD564, 0xD564 },
+{ 0xD565, 0xD565, 0xD565 },
+{ 0xD566, 0xD566, 0xD566 },
+{ 0xD567, 0xD567, 0xD567 },
+{ 0xD568, 0xD568, 0xD568 },
+{ 0xD569, 0xD569, 0xD569 },
+{ 0xD56A, 0xD56A, 0xD56A },
+{ 0xD56B, 0xD56B, 0xD56B },
+{ 0xD56C, 0xD56C, 0xD56C },
+{ 0xD56D, 0xD56D, 0xD56D },
+{ 0xD56E, 0xD56E, 0xD56E },
+{ 0xD56F, 0xD56F, 0xD56F },
+{ 0xD570, 0xD570, 0xD570 },
+{ 0xD571, 0xD571, 0xD571 },
+{ 0xD572, 0xD572, 0xD572 },
+{ 0xD573, 0xD573, 0xD573 },
+{ 0xD574, 0xD574, 0xD574 },
+{ 0xD575, 0xD575, 0xD575 },
+{ 0xD576, 0xD576, 0xD576 },
+{ 0xD577, 0xD577, 0xD577 },
+{ 0xD578, 0xD578, 0xD578 },
+{ 0xD579, 0xD579, 0xD579 },
+{ 0xD57A, 0xD57A, 0xD57A },
+{ 0xD57B, 0xD57B, 0xD57B },
+{ 0xD57C, 0xD57C, 0xD57C },
+{ 0xD57D, 0xD57D, 0xD57D },
+{ 0xD57E, 0xD57E, 0xD57E },
+{ 0xD57F, 0xD57F, 0xD57F },
+{ 0xD580, 0xD580, 0xD580 },
+{ 0xD581, 0xD581, 0xD581 },
+{ 0xD582, 0xD582, 0xD582 },
+{ 0xD583, 0xD583, 0xD583 },
+{ 0xD584, 0xD584, 0xD584 },
+{ 0xD585, 0xD585, 0xD585 },
+{ 0xD586, 0xD586, 0xD586 },
+{ 0xD587, 0xD587, 0xD587 },
+{ 0xD588, 0xD588, 0xD588 },
+{ 0xD589, 0xD589, 0xD589 },
+{ 0xD58A, 0xD58A, 0xD58A },
+{ 0xD58B, 0xD58B, 0xD58B },
+{ 0xD58C, 0xD58C, 0xD58C },
+{ 0xD58D, 0xD58D, 0xD58D },
+{ 0xD58E, 0xD58E, 0xD58E },
+{ 0xD58F, 0xD58F, 0xD58F },
+{ 0xD590, 0xD590, 0xD590 },
+{ 0xD591, 0xD591, 0xD591 },
+{ 0xD592, 0xD592, 0xD592 },
+{ 0xD593, 0xD593, 0xD593 },
+{ 0xD594, 0xD594, 0xD594 },
+{ 0xD595, 0xD595, 0xD595 },
+{ 0xD596, 0xD596, 0xD596 },
+{ 0xD597, 0xD597, 0xD597 },
+{ 0xD598, 0xD598, 0xD598 },
+{ 0xD599, 0xD599, 0xD599 },
+{ 0xD59A, 0xD59A, 0xD59A },
+{ 0xD59B, 0xD59B, 0xD59B },
+{ 0xD59C, 0xD59C, 0xD59C },
+{ 0xD59D, 0xD59D, 0xD59D },
+{ 0xD59E, 0xD59E, 0xD59E },
+{ 0xD59F, 0xD59F, 0xD59F },
+{ 0xD5A0, 0xD5A0, 0xD5A0 },
+{ 0xD5A1, 0xD5A1, 0xD5A1 },
+{ 0xD5A2, 0xD5A2, 0xD5A2 },
+{ 0xD5A3, 0xD5A3, 0xD5A3 },
+{ 0xD5A4, 0xD5A4, 0xD5A4 },
+{ 0xD5A5, 0xD5A5, 0xD5A5 },
+{ 0xD5A6, 0xD5A6, 0xD5A6 },
+{ 0xD5A7, 0xD5A7, 0xD5A7 },
+{ 0xD5A8, 0xD5A8, 0xD5A8 },
+{ 0xD5A9, 0xD5A9, 0xD5A9 },
+{ 0xD5AA, 0xD5AA, 0xD5AA },
+{ 0xD5AB, 0xD5AB, 0xD5AB },
+{ 0xD5AC, 0xD5AC, 0xD5AC },
+{ 0xD5AD, 0xD5AD, 0xD5AD },
+{ 0xD5AE, 0xD5AE, 0xD5AE },
+{ 0xD5AF, 0xD5AF, 0xD5AF },
+{ 0xD5B0, 0xD5B0, 0xD5B0 },
+{ 0xD5B1, 0xD5B1, 0xD5B1 },
+{ 0xD5B2, 0xD5B2, 0xD5B2 },
+{ 0xD5B3, 0xD5B3, 0xD5B3 },
+{ 0xD5B4, 0xD5B4, 0xD5B4 },
+{ 0xD5B5, 0xD5B5, 0xD5B5 },
+{ 0xD5B6, 0xD5B6, 0xD5B6 },
+{ 0xD5B7, 0xD5B7, 0xD5B7 },
+{ 0xD5B8, 0xD5B8, 0xD5B8 },
+{ 0xD5B9, 0xD5B9, 0xD5B9 },
+{ 0xD5BA, 0xD5BA, 0xD5BA },
+{ 0xD5BB, 0xD5BB, 0xD5BB },
+{ 0xD5BC, 0xD5BC, 0xD5BC },
+{ 0xD5BD, 0xD5BD, 0xD5BD },
+{ 0xD5BE, 0xD5BE, 0xD5BE },
+{ 0xD5BF, 0xD5BF, 0xD5BF },
+{ 0xD5C0, 0xD5C0, 0xD5C0 },
+{ 0xD5C1, 0xD5C1, 0xD5C1 },
+{ 0xD5C2, 0xD5C2, 0xD5C2 },
+{ 0xD5C3, 0xD5C3, 0xD5C3 },
+{ 0xD5C4, 0xD5C4, 0xD5C4 },
+{ 0xD5C5, 0xD5C5, 0xD5C5 },
+{ 0xD5C6, 0xD5C6, 0xD5C6 },
+{ 0xD5C7, 0xD5C7, 0xD5C7 },
+{ 0xD5C8, 0xD5C8, 0xD5C8 },
+{ 0xD5C9, 0xD5C9, 0xD5C9 },
+{ 0xD5CA, 0xD5CA, 0xD5CA },
+{ 0xD5CB, 0xD5CB, 0xD5CB },
+{ 0xD5CC, 0xD5CC, 0xD5CC },
+{ 0xD5CD, 0xD5CD, 0xD5CD },
+{ 0xD5CE, 0xD5CE, 0xD5CE },
+{ 0xD5CF, 0xD5CF, 0xD5CF },
+{ 0xD5D0, 0xD5D0, 0xD5D0 },
+{ 0xD5D1, 0xD5D1, 0xD5D1 },
+{ 0xD5D2, 0xD5D2, 0xD5D2 },
+{ 0xD5D3, 0xD5D3, 0xD5D3 },
+{ 0xD5D4, 0xD5D4, 0xD5D4 },
+{ 0xD5D5, 0xD5D5, 0xD5D5 },
+{ 0xD5D6, 0xD5D6, 0xD5D6 },
+{ 0xD5D7, 0xD5D7, 0xD5D7 },
+{ 0xD5D8, 0xD5D8, 0xD5D8 },
+{ 0xD5D9, 0xD5D9, 0xD5D9 },
+{ 0xD5DA, 0xD5DA, 0xD5DA },
+{ 0xD5DB, 0xD5DB, 0xD5DB },
+{ 0xD5DC, 0xD5DC, 0xD5DC },
+{ 0xD5DD, 0xD5DD, 0xD5DD },
+{ 0xD5DE, 0xD5DE, 0xD5DE },
+{ 0xD5DF, 0xD5DF, 0xD5DF },
+{ 0xD5E0, 0xD5E0, 0xD5E0 },
+{ 0xD5E1, 0xD5E1, 0xD5E1 },
+{ 0xD5E2, 0xD5E2, 0xD5E2 },
+{ 0xD5E3, 0xD5E3, 0xD5E3 },
+{ 0xD5E4, 0xD5E4, 0xD5E4 },
+{ 0xD5E5, 0xD5E5, 0xD5E5 },
+{ 0xD5E6, 0xD5E6, 0xD5E6 },
+{ 0xD5E7, 0xD5E7, 0xD5E7 },
+{ 0xD5E8, 0xD5E8, 0xD5E8 },
+{ 0xD5E9, 0xD5E9, 0xD5E9 },
+{ 0xD5EA, 0xD5EA, 0xD5EA },
+{ 0xD5EB, 0xD5EB, 0xD5EB },
+{ 0xD5EC, 0xD5EC, 0xD5EC },
+{ 0xD5ED, 0xD5ED, 0xD5ED },
+{ 0xD5EE, 0xD5EE, 0xD5EE },
+{ 0xD5EF, 0xD5EF, 0xD5EF },
+{ 0xD5F0, 0xD5F0, 0xD5F0 },
+{ 0xD5F1, 0xD5F1, 0xD5F1 },
+{ 0xD5F2, 0xD5F2, 0xD5F2 },
+{ 0xD5F3, 0xD5F3, 0xD5F3 },
+{ 0xD5F4, 0xD5F4, 0xD5F4 },
+{ 0xD5F5, 0xD5F5, 0xD5F5 },
+{ 0xD5F6, 0xD5F6, 0xD5F6 },
+{ 0xD5F7, 0xD5F7, 0xD5F7 },
+{ 0xD5F8, 0xD5F8, 0xD5F8 },
+{ 0xD5F9, 0xD5F9, 0xD5F9 },
+{ 0xD5FA, 0xD5FA, 0xD5FA },
+{ 0xD5FB, 0xD5FB, 0xD5FB },
+{ 0xD5FC, 0xD5FC, 0xD5FC },
+{ 0xD5FD, 0xD5FD, 0xD5FD },
+{ 0xD5FE, 0xD5FE, 0xD5FE },
+{ 0xD5FF, 0xD5FF, 0xD5FF },
+{ 0xD600, 0xD600, 0xD600 },
+{ 0xD601, 0xD601, 0xD601 },
+{ 0xD602, 0xD602, 0xD602 },
+{ 0xD603, 0xD603, 0xD603 },
+{ 0xD604, 0xD604, 0xD604 },
+{ 0xD605, 0xD605, 0xD605 },
+{ 0xD606, 0xD606, 0xD606 },
+{ 0xD607, 0xD607, 0xD607 },
+{ 0xD608, 0xD608, 0xD608 },
+{ 0xD609, 0xD609, 0xD609 },
+{ 0xD60A, 0xD60A, 0xD60A },
+{ 0xD60B, 0xD60B, 0xD60B },
+{ 0xD60C, 0xD60C, 0xD60C },
+{ 0xD60D, 0xD60D, 0xD60D },
+{ 0xD60E, 0xD60E, 0xD60E },
+{ 0xD60F, 0xD60F, 0xD60F },
+{ 0xD610, 0xD610, 0xD610 },
+{ 0xD611, 0xD611, 0xD611 },
+{ 0xD612, 0xD612, 0xD612 },
+{ 0xD613, 0xD613, 0xD613 },
+{ 0xD614, 0xD614, 0xD614 },
+{ 0xD615, 0xD615, 0xD615 },
+{ 0xD616, 0xD616, 0xD616 },
+{ 0xD617, 0xD617, 0xD617 },
+{ 0xD618, 0xD618, 0xD618 },
+{ 0xD619, 0xD619, 0xD619 },
+{ 0xD61A, 0xD61A, 0xD61A },
+{ 0xD61B, 0xD61B, 0xD61B },
+{ 0xD61C, 0xD61C, 0xD61C },
+{ 0xD61D, 0xD61D, 0xD61D },
+{ 0xD61E, 0xD61E, 0xD61E },
+{ 0xD61F, 0xD61F, 0xD61F },
+{ 0xD620, 0xD620, 0xD620 },
+{ 0xD621, 0xD621, 0xD621 },
+{ 0xD622, 0xD622, 0xD622 },
+{ 0xD623, 0xD623, 0xD623 },
+{ 0xD624, 0xD624, 0xD624 },
+{ 0xD625, 0xD625, 0xD625 },
+{ 0xD626, 0xD626, 0xD626 },
+{ 0xD627, 0xD627, 0xD627 },
+{ 0xD628, 0xD628, 0xD628 },
+{ 0xD629, 0xD629, 0xD629 },
+{ 0xD62A, 0xD62A, 0xD62A },
+{ 0xD62B, 0xD62B, 0xD62B },
+{ 0xD62C, 0xD62C, 0xD62C },
+{ 0xD62D, 0xD62D, 0xD62D },
+{ 0xD62E, 0xD62E, 0xD62E },
+{ 0xD62F, 0xD62F, 0xD62F },
+{ 0xD630, 0xD630, 0xD630 },
+{ 0xD631, 0xD631, 0xD631 },
+{ 0xD632, 0xD632, 0xD632 },
+{ 0xD633, 0xD633, 0xD633 },
+{ 0xD634, 0xD634, 0xD634 },
+{ 0xD635, 0xD635, 0xD635 },
+{ 0xD636, 0xD636, 0xD636 },
+{ 0xD637, 0xD637, 0xD637 },
+{ 0xD638, 0xD638, 0xD638 },
+{ 0xD639, 0xD639, 0xD639 },
+{ 0xD63A, 0xD63A, 0xD63A },
+{ 0xD63B, 0xD63B, 0xD63B },
+{ 0xD63C, 0xD63C, 0xD63C },
+{ 0xD63D, 0xD63D, 0xD63D },
+{ 0xD63E, 0xD63E, 0xD63E },
+{ 0xD63F, 0xD63F, 0xD63F },
+{ 0xD640, 0xD640, 0xD640 },
+{ 0xD641, 0xD641, 0xD641 },
+{ 0xD642, 0xD642, 0xD642 },
+{ 0xD643, 0xD643, 0xD643 },
+{ 0xD644, 0xD644, 0xD644 },
+{ 0xD645, 0xD645, 0xD645 },
+{ 0xD646, 0xD646, 0xD646 },
+{ 0xD647, 0xD647, 0xD647 },
+{ 0xD648, 0xD648, 0xD648 },
+{ 0xD649, 0xD649, 0xD649 },
+{ 0xD64A, 0xD64A, 0xD64A },
+{ 0xD64B, 0xD64B, 0xD64B },
+{ 0xD64C, 0xD64C, 0xD64C },
+{ 0xD64D, 0xD64D, 0xD64D },
+{ 0xD64E, 0xD64E, 0xD64E },
+{ 0xD64F, 0xD64F, 0xD64F },
+{ 0xD650, 0xD650, 0xD650 },
+{ 0xD651, 0xD651, 0xD651 },
+{ 0xD652, 0xD652, 0xD652 },
+{ 0xD653, 0xD653, 0xD653 },
+{ 0xD654, 0xD654, 0xD654 },
+{ 0xD655, 0xD655, 0xD655 },
+{ 0xD656, 0xD656, 0xD656 },
+{ 0xD657, 0xD657, 0xD657 },
+{ 0xD658, 0xD658, 0xD658 },
+{ 0xD659, 0xD659, 0xD659 },
+{ 0xD65A, 0xD65A, 0xD65A },
+{ 0xD65B, 0xD65B, 0xD65B },
+{ 0xD65C, 0xD65C, 0xD65C },
+{ 0xD65D, 0xD65D, 0xD65D },
+{ 0xD65E, 0xD65E, 0xD65E },
+{ 0xD65F, 0xD65F, 0xD65F },
+{ 0xD660, 0xD660, 0xD660 },
+{ 0xD661, 0xD661, 0xD661 },
+{ 0xD662, 0xD662, 0xD662 },
+{ 0xD663, 0xD663, 0xD663 },
+{ 0xD664, 0xD664, 0xD664 },
+{ 0xD665, 0xD665, 0xD665 },
+{ 0xD666, 0xD666, 0xD666 },
+{ 0xD667, 0xD667, 0xD667 },
+{ 0xD668, 0xD668, 0xD668 },
+{ 0xD669, 0xD669, 0xD669 },
+{ 0xD66A, 0xD66A, 0xD66A },
+{ 0xD66B, 0xD66B, 0xD66B },
+{ 0xD66C, 0xD66C, 0xD66C },
+{ 0xD66D, 0xD66D, 0xD66D },
+{ 0xD66E, 0xD66E, 0xD66E },
+{ 0xD66F, 0xD66F, 0xD66F },
+{ 0xD670, 0xD670, 0xD670 },
+{ 0xD671, 0xD671, 0xD671 },
+{ 0xD672, 0xD672, 0xD672 },
+{ 0xD673, 0xD673, 0xD673 },
+{ 0xD674, 0xD674, 0xD674 },
+{ 0xD675, 0xD675, 0xD675 },
+{ 0xD676, 0xD676, 0xD676 },
+{ 0xD677, 0xD677, 0xD677 },
+{ 0xD678, 0xD678, 0xD678 },
+{ 0xD679, 0xD679, 0xD679 },
+{ 0xD67A, 0xD67A, 0xD67A },
+{ 0xD67B, 0xD67B, 0xD67B },
+{ 0xD67C, 0xD67C, 0xD67C },
+{ 0xD67D, 0xD67D, 0xD67D },
+{ 0xD67E, 0xD67E, 0xD67E },
+{ 0xD67F, 0xD67F, 0xD67F },
+{ 0xD680, 0xD680, 0xD680 },
+{ 0xD681, 0xD681, 0xD681 },
+{ 0xD682, 0xD682, 0xD682 },
+{ 0xD683, 0xD683, 0xD683 },
+{ 0xD684, 0xD684, 0xD684 },
+{ 0xD685, 0xD685, 0xD685 },
+{ 0xD686, 0xD686, 0xD686 },
+{ 0xD687, 0xD687, 0xD687 },
+{ 0xD688, 0xD688, 0xD688 },
+{ 0xD689, 0xD689, 0xD689 },
+{ 0xD68A, 0xD68A, 0xD68A },
+{ 0xD68B, 0xD68B, 0xD68B },
+{ 0xD68C, 0xD68C, 0xD68C },
+{ 0xD68D, 0xD68D, 0xD68D },
+{ 0xD68E, 0xD68E, 0xD68E },
+{ 0xD68F, 0xD68F, 0xD68F },
+{ 0xD690, 0xD690, 0xD690 },
+{ 0xD691, 0xD691, 0xD691 },
+{ 0xD692, 0xD692, 0xD692 },
+{ 0xD693, 0xD693, 0xD693 },
+{ 0xD694, 0xD694, 0xD694 },
+{ 0xD695, 0xD695, 0xD695 },
+{ 0xD696, 0xD696, 0xD696 },
+{ 0xD697, 0xD697, 0xD697 },
+{ 0xD698, 0xD698, 0xD698 },
+{ 0xD699, 0xD699, 0xD699 },
+{ 0xD69A, 0xD69A, 0xD69A },
+{ 0xD69B, 0xD69B, 0xD69B },
+{ 0xD69C, 0xD69C, 0xD69C },
+{ 0xD69D, 0xD69D, 0xD69D },
+{ 0xD69E, 0xD69E, 0xD69E },
+{ 0xD69F, 0xD69F, 0xD69F },
+{ 0xD6A0, 0xD6A0, 0xD6A0 },
+{ 0xD6A1, 0xD6A1, 0xD6A1 },
+{ 0xD6A2, 0xD6A2, 0xD6A2 },
+{ 0xD6A3, 0xD6A3, 0xD6A3 },
+{ 0xD6A4, 0xD6A4, 0xD6A4 },
+{ 0xD6A5, 0xD6A5, 0xD6A5 },
+{ 0xD6A6, 0xD6A6, 0xD6A6 },
+{ 0xD6A7, 0xD6A7, 0xD6A7 },
+{ 0xD6A8, 0xD6A8, 0xD6A8 },
+{ 0xD6A9, 0xD6A9, 0xD6A9 },
+{ 0xD6AA, 0xD6AA, 0xD6AA },
+{ 0xD6AB, 0xD6AB, 0xD6AB },
+{ 0xD6AC, 0xD6AC, 0xD6AC },
+{ 0xD6AD, 0xD6AD, 0xD6AD },
+{ 0xD6AE, 0xD6AE, 0xD6AE },
+{ 0xD6AF, 0xD6AF, 0xD6AF },
+{ 0xD6B0, 0xD6B0, 0xD6B0 },
+{ 0xD6B1, 0xD6B1, 0xD6B1 },
+{ 0xD6B2, 0xD6B2, 0xD6B2 },
+{ 0xD6B3, 0xD6B3, 0xD6B3 },
+{ 0xD6B4, 0xD6B4, 0xD6B4 },
+{ 0xD6B5, 0xD6B5, 0xD6B5 },
+{ 0xD6B6, 0xD6B6, 0xD6B6 },
+{ 0xD6B7, 0xD6B7, 0xD6B7 },
+{ 0xD6B8, 0xD6B8, 0xD6B8 },
+{ 0xD6B9, 0xD6B9, 0xD6B9 },
+{ 0xD6BA, 0xD6BA, 0xD6BA },
+{ 0xD6BB, 0xD6BB, 0xD6BB },
+{ 0xD6BC, 0xD6BC, 0xD6BC },
+{ 0xD6BD, 0xD6BD, 0xD6BD },
+{ 0xD6BE, 0xD6BE, 0xD6BE },
+{ 0xD6BF, 0xD6BF, 0xD6BF },
+{ 0xD6C0, 0xD6C0, 0xD6C0 },
+{ 0xD6C1, 0xD6C1, 0xD6C1 },
+{ 0xD6C2, 0xD6C2, 0xD6C2 },
+{ 0xD6C3, 0xD6C3, 0xD6C3 },
+{ 0xD6C4, 0xD6C4, 0xD6C4 },
+{ 0xD6C5, 0xD6C5, 0xD6C5 },
+{ 0xD6C6, 0xD6C6, 0xD6C6 },
+{ 0xD6C7, 0xD6C7, 0xD6C7 },
+{ 0xD6C8, 0xD6C8, 0xD6C8 },
+{ 0xD6C9, 0xD6C9, 0xD6C9 },
+{ 0xD6CA, 0xD6CA, 0xD6CA },
+{ 0xD6CB, 0xD6CB, 0xD6CB },
+{ 0xD6CC, 0xD6CC, 0xD6CC },
+{ 0xD6CD, 0xD6CD, 0xD6CD },
+{ 0xD6CE, 0xD6CE, 0xD6CE },
+{ 0xD6CF, 0xD6CF, 0xD6CF },
+{ 0xD6D0, 0xD6D0, 0xD6D0 },
+{ 0xD6D1, 0xD6D1, 0xD6D1 },
+{ 0xD6D2, 0xD6D2, 0xD6D2 },
+{ 0xD6D3, 0xD6D3, 0xD6D3 },
+{ 0xD6D4, 0xD6D4, 0xD6D4 },
+{ 0xD6D5, 0xD6D5, 0xD6D5 },
+{ 0xD6D6, 0xD6D6, 0xD6D6 },
+{ 0xD6D7, 0xD6D7, 0xD6D7 },
+{ 0xD6D8, 0xD6D8, 0xD6D8 },
+{ 0xD6D9, 0xD6D9, 0xD6D9 },
+{ 0xD6DA, 0xD6DA, 0xD6DA },
+{ 0xD6DB, 0xD6DB, 0xD6DB },
+{ 0xD6DC, 0xD6DC, 0xD6DC },
+{ 0xD6DD, 0xD6DD, 0xD6DD },
+{ 0xD6DE, 0xD6DE, 0xD6DE },
+{ 0xD6DF, 0xD6DF, 0xD6DF },
+{ 0xD6E0, 0xD6E0, 0xD6E0 },
+{ 0xD6E1, 0xD6E1, 0xD6E1 },
+{ 0xD6E2, 0xD6E2, 0xD6E2 },
+{ 0xD6E3, 0xD6E3, 0xD6E3 },
+{ 0xD6E4, 0xD6E4, 0xD6E4 },
+{ 0xD6E5, 0xD6E5, 0xD6E5 },
+{ 0xD6E6, 0xD6E6, 0xD6E6 },
+{ 0xD6E7, 0xD6E7, 0xD6E7 },
+{ 0xD6E8, 0xD6E8, 0xD6E8 },
+{ 0xD6E9, 0xD6E9, 0xD6E9 },
+{ 0xD6EA, 0xD6EA, 0xD6EA },
+{ 0xD6EB, 0xD6EB, 0xD6EB },
+{ 0xD6EC, 0xD6EC, 0xD6EC },
+{ 0xD6ED, 0xD6ED, 0xD6ED },
+{ 0xD6EE, 0xD6EE, 0xD6EE },
+{ 0xD6EF, 0xD6EF, 0xD6EF },
+{ 0xD6F0, 0xD6F0, 0xD6F0 },
+{ 0xD6F1, 0xD6F1, 0xD6F1 },
+{ 0xD6F2, 0xD6F2, 0xD6F2 },
+{ 0xD6F3, 0xD6F3, 0xD6F3 },
+{ 0xD6F4, 0xD6F4, 0xD6F4 },
+{ 0xD6F5, 0xD6F5, 0xD6F5 },
+{ 0xD6F6, 0xD6F6, 0xD6F6 },
+{ 0xD6F7, 0xD6F7, 0xD6F7 },
+{ 0xD6F8, 0xD6F8, 0xD6F8 },
+{ 0xD6F9, 0xD6F9, 0xD6F9 },
+{ 0xD6FA, 0xD6FA, 0xD6FA },
+{ 0xD6FB, 0xD6FB, 0xD6FB },
+{ 0xD6FC, 0xD6FC, 0xD6FC },
+{ 0xD6FD, 0xD6FD, 0xD6FD },
+{ 0xD6FE, 0xD6FE, 0xD6FE },
+{ 0xD6FF, 0xD6FF, 0xD6FF },
+{ 0xD700, 0xD700, 0xD700 },
+{ 0xD701, 0xD701, 0xD701 },
+{ 0xD702, 0xD702, 0xD702 },
+{ 0xD703, 0xD703, 0xD703 },
+{ 0xD704, 0xD704, 0xD704 },
+{ 0xD705, 0xD705, 0xD705 },
+{ 0xD706, 0xD706, 0xD706 },
+{ 0xD707, 0xD707, 0xD707 },
+{ 0xD708, 0xD708, 0xD708 },
+{ 0xD709, 0xD709, 0xD709 },
+{ 0xD70A, 0xD70A, 0xD70A },
+{ 0xD70B, 0xD70B, 0xD70B },
+{ 0xD70C, 0xD70C, 0xD70C },
+{ 0xD70D, 0xD70D, 0xD70D },
+{ 0xD70E, 0xD70E, 0xD70E },
+{ 0xD70F, 0xD70F, 0xD70F },
+{ 0xD710, 0xD710, 0xD710 },
+{ 0xD711, 0xD711, 0xD711 },
+{ 0xD712, 0xD712, 0xD712 },
+{ 0xD713, 0xD713, 0xD713 },
+{ 0xD714, 0xD714, 0xD714 },
+{ 0xD715, 0xD715, 0xD715 },
+{ 0xD716, 0xD716, 0xD716 },
+{ 0xD717, 0xD717, 0xD717 },
+{ 0xD718, 0xD718, 0xD718 },
+{ 0xD719, 0xD719, 0xD719 },
+{ 0xD71A, 0xD71A, 0xD71A },
+{ 0xD71B, 0xD71B, 0xD71B },
+{ 0xD71C, 0xD71C, 0xD71C },
+{ 0xD71D, 0xD71D, 0xD71D },
+{ 0xD71E, 0xD71E, 0xD71E },
+{ 0xD71F, 0xD71F, 0xD71F },
+{ 0xD720, 0xD720, 0xD720 },
+{ 0xD721, 0xD721, 0xD721 },
+{ 0xD722, 0xD722, 0xD722 },
+{ 0xD723, 0xD723, 0xD723 },
+{ 0xD724, 0xD724, 0xD724 },
+{ 0xD725, 0xD725, 0xD725 },
+{ 0xD726, 0xD726, 0xD726 },
+{ 0xD727, 0xD727, 0xD727 },
+{ 0xD728, 0xD728, 0xD728 },
+{ 0xD729, 0xD729, 0xD729 },
+{ 0xD72A, 0xD72A, 0xD72A },
+{ 0xD72B, 0xD72B, 0xD72B },
+{ 0xD72C, 0xD72C, 0xD72C },
+{ 0xD72D, 0xD72D, 0xD72D },
+{ 0xD72E, 0xD72E, 0xD72E },
+{ 0xD72F, 0xD72F, 0xD72F },
+{ 0xD730, 0xD730, 0xD730 },
+{ 0xD731, 0xD731, 0xD731 },
+{ 0xD732, 0xD732, 0xD732 },
+{ 0xD733, 0xD733, 0xD733 },
+{ 0xD734, 0xD734, 0xD734 },
+{ 0xD735, 0xD735, 0xD735 },
+{ 0xD736, 0xD736, 0xD736 },
+{ 0xD737, 0xD737, 0xD737 },
+{ 0xD738, 0xD738, 0xD738 },
+{ 0xD739, 0xD739, 0xD739 },
+{ 0xD73A, 0xD73A, 0xD73A },
+{ 0xD73B, 0xD73B, 0xD73B },
+{ 0xD73C, 0xD73C, 0xD73C },
+{ 0xD73D, 0xD73D, 0xD73D },
+{ 0xD73E, 0xD73E, 0xD73E },
+{ 0xD73F, 0xD73F, 0xD73F },
+{ 0xD740, 0xD740, 0xD740 },
+{ 0xD741, 0xD741, 0xD741 },
+{ 0xD742, 0xD742, 0xD742 },
+{ 0xD743, 0xD743, 0xD743 },
+{ 0xD744, 0xD744, 0xD744 },
+{ 0xD745, 0xD745, 0xD745 },
+{ 0xD746, 0xD746, 0xD746 },
+{ 0xD747, 0xD747, 0xD747 },
+{ 0xD748, 0xD748, 0xD748 },
+{ 0xD749, 0xD749, 0xD749 },
+{ 0xD74A, 0xD74A, 0xD74A },
+{ 0xD74B, 0xD74B, 0xD74B },
+{ 0xD74C, 0xD74C, 0xD74C },
+{ 0xD74D, 0xD74D, 0xD74D },
+{ 0xD74E, 0xD74E, 0xD74E },
+{ 0xD74F, 0xD74F, 0xD74F },
+{ 0xD750, 0xD750, 0xD750 },
+{ 0xD751, 0xD751, 0xD751 },
+{ 0xD752, 0xD752, 0xD752 },
+{ 0xD753, 0xD753, 0xD753 },
+{ 0xD754, 0xD754, 0xD754 },
+{ 0xD755, 0xD755, 0xD755 },
+{ 0xD756, 0xD756, 0xD756 },
+{ 0xD757, 0xD757, 0xD757 },
+{ 0xD758, 0xD758, 0xD758 },
+{ 0xD759, 0xD759, 0xD759 },
+{ 0xD75A, 0xD75A, 0xD75A },
+{ 0xD75B, 0xD75B, 0xD75B },
+{ 0xD75C, 0xD75C, 0xD75C },
+{ 0xD75D, 0xD75D, 0xD75D },
+{ 0xD75E, 0xD75E, 0xD75E },
+{ 0xD75F, 0xD75F, 0xD75F },
+{ 0xD760, 0xD760, 0xD760 },
+{ 0xD761, 0xD761, 0xD761 },
+{ 0xD762, 0xD762, 0xD762 },
+{ 0xD763, 0xD763, 0xD763 },
+{ 0xD764, 0xD764, 0xD764 },
+{ 0xD765, 0xD765, 0xD765 },
+{ 0xD766, 0xD766, 0xD766 },
+{ 0xD767, 0xD767, 0xD767 },
+{ 0xD768, 0xD768, 0xD768 },
+{ 0xD769, 0xD769, 0xD769 },
+{ 0xD76A, 0xD76A, 0xD76A },
+{ 0xD76B, 0xD76B, 0xD76B },
+{ 0xD76C, 0xD76C, 0xD76C },
+{ 0xD76D, 0xD76D, 0xD76D },
+{ 0xD76E, 0xD76E, 0xD76E },
+{ 0xD76F, 0xD76F, 0xD76F },
+{ 0xD770, 0xD770, 0xD770 },
+{ 0xD771, 0xD771, 0xD771 },
+{ 0xD772, 0xD772, 0xD772 },
+{ 0xD773, 0xD773, 0xD773 },
+{ 0xD774, 0xD774, 0xD774 },
+{ 0xD775, 0xD775, 0xD775 },
+{ 0xD776, 0xD776, 0xD776 },
+{ 0xD777, 0xD777, 0xD777 },
+{ 0xD778, 0xD778, 0xD778 },
+{ 0xD779, 0xD779, 0xD779 },
+{ 0xD77A, 0xD77A, 0xD77A },
+{ 0xD77B, 0xD77B, 0xD77B },
+{ 0xD77C, 0xD77C, 0xD77C },
+{ 0xD77D, 0xD77D, 0xD77D },
+{ 0xD77E, 0xD77E, 0xD77E },
+{ 0xD77F, 0xD77F, 0xD77F },
+{ 0xD780, 0xD780, 0xD780 },
+{ 0xD781, 0xD781, 0xD781 },
+{ 0xD782, 0xD782, 0xD782 },
+{ 0xD783, 0xD783, 0xD783 },
+{ 0xD784, 0xD784, 0xD784 },
+{ 0xD785, 0xD785, 0xD785 },
+{ 0xD786, 0xD786, 0xD786 },
+{ 0xD787, 0xD787, 0xD787 },
+{ 0xD788, 0xD788, 0xD788 },
+{ 0xD789, 0xD789, 0xD789 },
+{ 0xD78A, 0xD78A, 0xD78A },
+{ 0xD78B, 0xD78B, 0xD78B },
+{ 0xD78C, 0xD78C, 0xD78C },
+{ 0xD78D, 0xD78D, 0xD78D },
+{ 0xD78E, 0xD78E, 0xD78E },
+{ 0xD78F, 0xD78F, 0xD78F },
+{ 0xD790, 0xD790, 0xD790 },
+{ 0xD791, 0xD791, 0xD791 },
+{ 0xD792, 0xD792, 0xD792 },
+{ 0xD793, 0xD793, 0xD793 },
+{ 0xD794, 0xD794, 0xD794 },
+{ 0xD795, 0xD795, 0xD795 },
+{ 0xD796, 0xD796, 0xD796 },
+{ 0xD797, 0xD797, 0xD797 },
+{ 0xD798, 0xD798, 0xD798 },
+{ 0xD799, 0xD799, 0xD799 },
+{ 0xD79A, 0xD79A, 0xD79A },
+{ 0xD79B, 0xD79B, 0xD79B },
+{ 0xD79C, 0xD79C, 0xD79C },
+{ 0xD79D, 0xD79D, 0xD79D },
+{ 0xD79E, 0xD79E, 0xD79E },
+{ 0xD79F, 0xD79F, 0xD79F },
+{ 0xD7A0, 0xD7A0, 0xD7A0 },
+{ 0xD7A1, 0xD7A1, 0xD7A1 },
+{ 0xD7A2, 0xD7A2, 0xD7A2 },
+{ 0xD7A3, 0xD7A3, 0xD7A3 },
+{ 0xF900, 0xF900, 0xF900 },
+{ 0xF901, 0xF901, 0xF901 },
+{ 0xF902, 0xF902, 0xF902 },
+{ 0xF903, 0xF903, 0xF903 },
+{ 0xF904, 0xF904, 0xF904 },
+{ 0xF905, 0xF905, 0xF905 },
+{ 0xF906, 0xF906, 0xF906 },
+{ 0xF907, 0xF907, 0xF907 },
+{ 0xF908, 0xF908, 0xF908 },
+{ 0xF909, 0xF909, 0xF909 },
+{ 0xF90A, 0xF90A, 0xF90A },
+{ 0xF90B, 0xF90B, 0xF90B },
+{ 0xF90C, 0xF90C, 0xF90C },
+{ 0xF90D, 0xF90D, 0xF90D },
+{ 0xF90E, 0xF90E, 0xF90E },
+{ 0xF90F, 0xF90F, 0xF90F },
+{ 0xF910, 0xF910, 0xF910 },
+{ 0xF911, 0xF911, 0xF911 },
+{ 0xF912, 0xF912, 0xF912 },
+{ 0xF913, 0xF913, 0xF913 },
+{ 0xF914, 0xF914, 0xF914 },
+{ 0xF915, 0xF915, 0xF915 },
+{ 0xF916, 0xF916, 0xF916 },
+{ 0xF917, 0xF917, 0xF917 },
+{ 0xF918, 0xF918, 0xF918 },
+{ 0xF919, 0xF919, 0xF919 },
+{ 0xF91A, 0xF91A, 0xF91A },
+{ 0xF91B, 0xF91B, 0xF91B },
+{ 0xF91C, 0xF91C, 0xF91C },
+{ 0xF91D, 0xF91D, 0xF91D },
+{ 0xF91E, 0xF91E, 0xF91E },
+{ 0xF91F, 0xF91F, 0xF91F },
+{ 0xF920, 0xF920, 0xF920 },
+{ 0xF921, 0xF921, 0xF921 },
+{ 0xF922, 0xF922, 0xF922 },
+{ 0xF923, 0xF923, 0xF923 },
+{ 0xF924, 0xF924, 0xF924 },
+{ 0xF925, 0xF925, 0xF925 },
+{ 0xF926, 0xF926, 0xF926 },
+{ 0xF927, 0xF927, 0xF927 },
+{ 0xF928, 0xF928, 0xF928 },
+{ 0xF929, 0xF929, 0xF929 },
+{ 0xF92A, 0xF92A, 0xF92A },
+{ 0xF92B, 0xF92B, 0xF92B },
+{ 0xF92C, 0xF92C, 0xF92C },
+{ 0xF92D, 0xF92D, 0xF92D },
+{ 0xF92E, 0xF92E, 0xF92E },
+{ 0xF92F, 0xF92F, 0xF92F },
+{ 0xF930, 0xF930, 0xF930 },
+{ 0xF931, 0xF931, 0xF931 },
+{ 0xF932, 0xF932, 0xF932 },
+{ 0xF933, 0xF933, 0xF933 },
+{ 0xF934, 0xF934, 0xF934 },
+{ 0xF935, 0xF935, 0xF935 },
+{ 0xF936, 0xF936, 0xF936 },
+{ 0xF937, 0xF937, 0xF937 },
+{ 0xF938, 0xF938, 0xF938 },
+{ 0xF939, 0xF939, 0xF939 },
+{ 0xF93A, 0xF93A, 0xF93A },
+{ 0xF93B, 0xF93B, 0xF93B },
+{ 0xF93C, 0xF93C, 0xF93C },
+{ 0xF93D, 0xF93D, 0xF93D },
+{ 0xF93E, 0xF93E, 0xF93E },
+{ 0xF93F, 0xF93F, 0xF93F },
+{ 0xF940, 0xF940, 0xF940 },
+{ 0xF941, 0xF941, 0xF941 },
+{ 0xF942, 0xF942, 0xF942 },
+{ 0xF943, 0xF943, 0xF943 },
+{ 0xF944, 0xF944, 0xF944 },
+{ 0xF945, 0xF945, 0xF945 },
+{ 0xF946, 0xF946, 0xF946 },
+{ 0xF947, 0xF947, 0xF947 },
+{ 0xF948, 0xF948, 0xF948 },
+{ 0xF949, 0xF949, 0xF949 },
+{ 0xF94A, 0xF94A, 0xF94A },
+{ 0xF94B, 0xF94B, 0xF94B },
+{ 0xF94C, 0xF94C, 0xF94C },
+{ 0xF94D, 0xF94D, 0xF94D },
+{ 0xF94E, 0xF94E, 0xF94E },
+{ 0xF94F, 0xF94F, 0xF94F },
+{ 0xF950, 0xF950, 0xF950 },
+{ 0xF951, 0xF951, 0xF951 },
+{ 0xF952, 0xF952, 0xF952 },
+{ 0xF953, 0xF953, 0xF953 },
+{ 0xF954, 0xF954, 0xF954 },
+{ 0xF955, 0xF955, 0xF955 },
+{ 0xF956, 0xF956, 0xF956 },
+{ 0xF957, 0xF957, 0xF957 },
+{ 0xF958, 0xF958, 0xF958 },
+{ 0xF959, 0xF959, 0xF959 },
+{ 0xF95A, 0xF95A, 0xF95A },
+{ 0xF95B, 0xF95B, 0xF95B },
+{ 0xF95C, 0xF95C, 0xF95C },
+{ 0xF95D, 0xF95D, 0xF95D },
+{ 0xF95E, 0xF95E, 0xF95E },
+{ 0xF95F, 0xF95F, 0xF95F },
+{ 0xF960, 0xF960, 0xF960 },
+{ 0xF961, 0xF961, 0xF961 },
+{ 0xF962, 0xF962, 0xF962 },
+{ 0xF963, 0xF963, 0xF963 },
+{ 0xF964, 0xF964, 0xF964 },
+{ 0xF965, 0xF965, 0xF965 },
+{ 0xF966, 0xF966, 0xF966 },
+{ 0xF967, 0xF967, 0xF967 },
+{ 0xF968, 0xF968, 0xF968 },
+{ 0xF969, 0xF969, 0xF969 },
+{ 0xF96A, 0xF96A, 0xF96A },
+{ 0xF96B, 0xF96B, 0xF96B },
+{ 0xF96C, 0xF96C, 0xF96C },
+{ 0xF96D, 0xF96D, 0xF96D },
+{ 0xF96E, 0xF96E, 0xF96E },
+{ 0xF96F, 0xF96F, 0xF96F },
+{ 0xF970, 0xF970, 0xF970 },
+{ 0xF971, 0xF971, 0xF971 },
+{ 0xF972, 0xF972, 0xF972 },
+{ 0xF973, 0xF973, 0xF973 },
+{ 0xF974, 0xF974, 0xF974 },
+{ 0xF975, 0xF975, 0xF975 },
+{ 0xF976, 0xF976, 0xF976 },
+{ 0xF977, 0xF977, 0xF977 },
+{ 0xF978, 0xF978, 0xF978 },
+{ 0xF979, 0xF979, 0xF979 },
+{ 0xF97A, 0xF97A, 0xF97A },
+{ 0xF97B, 0xF97B, 0xF97B },
+{ 0xF97C, 0xF97C, 0xF97C },
+{ 0xF97D, 0xF97D, 0xF97D },
+{ 0xF97E, 0xF97E, 0xF97E },
+{ 0xF97F, 0xF97F, 0xF97F },
+{ 0xF980, 0xF980, 0xF980 },
+{ 0xF981, 0xF981, 0xF981 },
+{ 0xF982, 0xF982, 0xF982 },
+{ 0xF983, 0xF983, 0xF983 },
+{ 0xF984, 0xF984, 0xF984 },
+{ 0xF985, 0xF985, 0xF985 },
+{ 0xF986, 0xF986, 0xF986 },
+{ 0xF987, 0xF987, 0xF987 },
+{ 0xF988, 0xF988, 0xF988 },
+{ 0xF989, 0xF989, 0xF989 },
+{ 0xF98A, 0xF98A, 0xF98A },
+{ 0xF98B, 0xF98B, 0xF98B },
+{ 0xF98C, 0xF98C, 0xF98C },
+{ 0xF98D, 0xF98D, 0xF98D },
+{ 0xF98E, 0xF98E, 0xF98E },
+{ 0xF98F, 0xF98F, 0xF98F },
+{ 0xF990, 0xF990, 0xF990 },
+{ 0xF991, 0xF991, 0xF991 },
+{ 0xF992, 0xF992, 0xF992 },
+{ 0xF993, 0xF993, 0xF993 },
+{ 0xF994, 0xF994, 0xF994 },
+{ 0xF995, 0xF995, 0xF995 },
+{ 0xF996, 0xF996, 0xF996 },
+{ 0xF997, 0xF997, 0xF997 },
+{ 0xF998, 0xF998, 0xF998 },
+{ 0xF999, 0xF999, 0xF999 },
+{ 0xF99A, 0xF99A, 0xF99A },
+{ 0xF99B, 0xF99B, 0xF99B },
+{ 0xF99C, 0xF99C, 0xF99C },
+{ 0xF99D, 0xF99D, 0xF99D },
+{ 0xF99E, 0xF99E, 0xF99E },
+{ 0xF99F, 0xF99F, 0xF99F },
+{ 0xF9A0, 0xF9A0, 0xF9A0 },
+{ 0xF9A1, 0xF9A1, 0xF9A1 },
+{ 0xF9A2, 0xF9A2, 0xF9A2 },
+{ 0xF9A3, 0xF9A3, 0xF9A3 },
+{ 0xF9A4, 0xF9A4, 0xF9A4 },
+{ 0xF9A5, 0xF9A5, 0xF9A5 },
+{ 0xF9A6, 0xF9A6, 0xF9A6 },
+{ 0xF9A7, 0xF9A7, 0xF9A7 },
+{ 0xF9A8, 0xF9A8, 0xF9A8 },
+{ 0xF9A9, 0xF9A9, 0xF9A9 },
+{ 0xF9AA, 0xF9AA, 0xF9AA },
+{ 0xF9AB, 0xF9AB, 0xF9AB },
+{ 0xF9AC, 0xF9AC, 0xF9AC },
+{ 0xF9AD, 0xF9AD, 0xF9AD },
+{ 0xF9AE, 0xF9AE, 0xF9AE },
+{ 0xF9AF, 0xF9AF, 0xF9AF },
+{ 0xF9B0, 0xF9B0, 0xF9B0 },
+{ 0xF9B1, 0xF9B1, 0xF9B1 },
+{ 0xF9B2, 0xF9B2, 0xF9B2 },
+{ 0xF9B3, 0xF9B3, 0xF9B3 },
+{ 0xF9B4, 0xF9B4, 0xF9B4 },
+{ 0xF9B5, 0xF9B5, 0xF9B5 },
+{ 0xF9B6, 0xF9B6, 0xF9B6 },
+{ 0xF9B7, 0xF9B7, 0xF9B7 },
+{ 0xF9B8, 0xF9B8, 0xF9B8 },
+{ 0xF9B9, 0xF9B9, 0xF9B9 },
+{ 0xF9BA, 0xF9BA, 0xF9BA },
+{ 0xF9BB, 0xF9BB, 0xF9BB },
+{ 0xF9BC, 0xF9BC, 0xF9BC },
+{ 0xF9BD, 0xF9BD, 0xF9BD },
+{ 0xF9BE, 0xF9BE, 0xF9BE },
+{ 0xF9BF, 0xF9BF, 0xF9BF },
+{ 0xF9C0, 0xF9C0, 0xF9C0 },
+{ 0xF9C1, 0xF9C1, 0xF9C1 },
+{ 0xF9C2, 0xF9C2, 0xF9C2 },
+{ 0xF9C3, 0xF9C3, 0xF9C3 },
+{ 0xF9C4, 0xF9C4, 0xF9C4 },
+{ 0xF9C5, 0xF9C5, 0xF9C5 },
+{ 0xF9C6, 0xF9C6, 0xF9C6 },
+{ 0xF9C7, 0xF9C7, 0xF9C7 },
+{ 0xF9C8, 0xF9C8, 0xF9C8 },
+{ 0xF9C9, 0xF9C9, 0xF9C9 },
+{ 0xF9CA, 0xF9CA, 0xF9CA },
+{ 0xF9CB, 0xF9CB, 0xF9CB },
+{ 0xF9CC, 0xF9CC, 0xF9CC },
+{ 0xF9CD, 0xF9CD, 0xF9CD },
+{ 0xF9CE, 0xF9CE, 0xF9CE },
+{ 0xF9CF, 0xF9CF, 0xF9CF },
+{ 0xF9D0, 0xF9D0, 0xF9D0 },
+{ 0xF9D1, 0xF9D1, 0xF9D1 },
+{ 0xF9D2, 0xF9D2, 0xF9D2 },
+{ 0xF9D3, 0xF9D3, 0xF9D3 },
+{ 0xF9D4, 0xF9D4, 0xF9D4 },
+{ 0xF9D5, 0xF9D5, 0xF9D5 },
+{ 0xF9D6, 0xF9D6, 0xF9D6 },
+{ 0xF9D7, 0xF9D7, 0xF9D7 },
+{ 0xF9D8, 0xF9D8, 0xF9D8 },
+{ 0xF9D9, 0xF9D9, 0xF9D9 },
+{ 0xF9DA, 0xF9DA, 0xF9DA },
+{ 0xF9DB, 0xF9DB, 0xF9DB },
+{ 0xF9DC, 0xF9DC, 0xF9DC },
+{ 0xF9DD, 0xF9DD, 0xF9DD },
+{ 0xF9DE, 0xF9DE, 0xF9DE },
+{ 0xF9DF, 0xF9DF, 0xF9DF },
+{ 0xF9E0, 0xF9E0, 0xF9E0 },
+{ 0xF9E1, 0xF9E1, 0xF9E1 },
+{ 0xF9E2, 0xF9E2, 0xF9E2 },
+{ 0xF9E3, 0xF9E3, 0xF9E3 },
+{ 0xF9E4, 0xF9E4, 0xF9E4 },
+{ 0xF9E5, 0xF9E5, 0xF9E5 },
+{ 0xF9E6, 0xF9E6, 0xF9E6 },
+{ 0xF9E7, 0xF9E7, 0xF9E7 },
+{ 0xF9E8, 0xF9E8, 0xF9E8 },
+{ 0xF9E9, 0xF9E9, 0xF9E9 },
+{ 0xF9EA, 0xF9EA, 0xF9EA },
+{ 0xF9EB, 0xF9EB, 0xF9EB },
+{ 0xF9EC, 0xF9EC, 0xF9EC },
+{ 0xF9ED, 0xF9ED, 0xF9ED },
+{ 0xF9EE, 0xF9EE, 0xF9EE },
+{ 0xF9EF, 0xF9EF, 0xF9EF },
+{ 0xF9F0, 0xF9F0, 0xF9F0 },
+{ 0xF9F1, 0xF9F1, 0xF9F1 },
+{ 0xF9F2, 0xF9F2, 0xF9F2 },
+{ 0xF9F3, 0xF9F3, 0xF9F3 },
+{ 0xF9F4, 0xF9F4, 0xF9F4 },
+{ 0xF9F5, 0xF9F5, 0xF9F5 },
+{ 0xF9F6, 0xF9F6, 0xF9F6 },
+{ 0xF9F7, 0xF9F7, 0xF9F7 },
+{ 0xF9F8, 0xF9F8, 0xF9F8 },
+{ 0xF9F9, 0xF9F9, 0xF9F9 },
+{ 0xF9FA, 0xF9FA, 0xF9FA },
+{ 0xF9FB, 0xF9FB, 0xF9FB },
+{ 0xF9FC, 0xF9FC, 0xF9FC },
+{ 0xF9FD, 0xF9FD, 0xF9FD },
+{ 0xF9FE, 0xF9FE, 0xF9FE },
+{ 0xF9FF, 0xF9FF, 0xF9FF },
+{ 0xFA00, 0xFA00, 0xFA00 },
+{ 0xFA01, 0xFA01, 0xFA01 },
+{ 0xFA02, 0xFA02, 0xFA02 },
+{ 0xFA03, 0xFA03, 0xFA03 },
+{ 0xFA04, 0xFA04, 0xFA04 },
+{ 0xFA05, 0xFA05, 0xFA05 },
+{ 0xFA06, 0xFA06, 0xFA06 },
+{ 0xFA07, 0xFA07, 0xFA07 },
+{ 0xFA08, 0xFA08, 0xFA08 },
+{ 0xFA09, 0xFA09, 0xFA09 },
+{ 0xFA0A, 0xFA0A, 0xFA0A },
+{ 0xFA0B, 0xFA0B, 0xFA0B },
+{ 0xFA0C, 0xFA0C, 0xFA0C },
+{ 0xFA0D, 0xFA0D, 0xFA0D },
+{ 0xFA0E, 0xFA0E, 0xFA0E },
+{ 0xFA0F, 0xFA0F, 0xFA0F },
+{ 0xFA10, 0xFA10, 0xFA10 },
+{ 0xFA11, 0xFA11, 0xFA11 },
+{ 0xFA12, 0xFA12, 0xFA12 },
+{ 0xFA13, 0xFA13, 0xFA13 },
+{ 0xFA14, 0xFA14, 0xFA14 },
+{ 0xFA15, 0xFA15, 0xFA15 },
+{ 0xFA16, 0xFA16, 0xFA16 },
+{ 0xFA17, 0xFA17, 0xFA17 },
+{ 0xFA18, 0xFA18, 0xFA18 },
+{ 0xFA19, 0xFA19, 0xFA19 },
+{ 0xFA1A, 0xFA1A, 0xFA1A },
+{ 0xFA1B, 0xFA1B, 0xFA1B },
+{ 0xFA1C, 0xFA1C, 0xFA1C },
+{ 0xFA1D, 0xFA1D, 0xFA1D },
+{ 0xFA1E, 0xFA1E, 0xFA1E },
+{ 0xFA1F, 0xFA1F, 0xFA1F },
+{ 0xFA20, 0xFA20, 0xFA20 },
+{ 0xFA21, 0xFA21, 0xFA21 },
+{ 0xFA22, 0xFA22, 0xFA22 },
+{ 0xFA23, 0xFA23, 0xFA23 },
+{ 0xFA24, 0xFA24, 0xFA24 },
+{ 0xFA25, 0xFA25, 0xFA25 },
+{ 0xFA26, 0xFA26, 0xFA26 },
+{ 0xFA27, 0xFA27, 0xFA27 },
+{ 0xFA28, 0xFA28, 0xFA28 },
+{ 0xFA29, 0xFA29, 0xFA29 },
+{ 0xFA2A, 0xFA2A, 0xFA2A },
+{ 0xFA2B, 0xFA2B, 0xFA2B },
+{ 0xFA2C, 0xFA2C, 0xFA2C },
+{ 0xFA2D, 0xFA2D, 0xFA2D },
+{ 0xFA30, 0xFA30, 0xFA30 },
+{ 0xFA31, 0xFA31, 0xFA31 },
+{ 0xFA32, 0xFA32, 0xFA32 },
+{ 0xFA33, 0xFA33, 0xFA33 },
+{ 0xFA34, 0xFA34, 0xFA34 },
+{ 0xFA35, 0xFA35, 0xFA35 },
+{ 0xFA36, 0xFA36, 0xFA36 },
+{ 0xFA37, 0xFA37, 0xFA37 },
+{ 0xFA38, 0xFA38, 0xFA38 },
+{ 0xFA39, 0xFA39, 0xFA39 },
+{ 0xFA3A, 0xFA3A, 0xFA3A },
+{ 0xFA3B, 0xFA3B, 0xFA3B },
+{ 0xFA3C, 0xFA3C, 0xFA3C },
+{ 0xFA3D, 0xFA3D, 0xFA3D },
+{ 0xFA3E, 0xFA3E, 0xFA3E },
+{ 0xFA3F, 0xFA3F, 0xFA3F },
+{ 0xFA40, 0xFA40, 0xFA40 },
+{ 0xFA41, 0xFA41, 0xFA41 },
+{ 0xFA42, 0xFA42, 0xFA42 },
+{ 0xFA43, 0xFA43, 0xFA43 },
+{ 0xFA44, 0xFA44, 0xFA44 },
+{ 0xFA45, 0xFA45, 0xFA45 },
+{ 0xFA46, 0xFA46, 0xFA46 },
+{ 0xFA47, 0xFA47, 0xFA47 },
+{ 0xFA48, 0xFA48, 0xFA48 },
+{ 0xFA49, 0xFA49, 0xFA49 },
+{ 0xFA4A, 0xFA4A, 0xFA4A },
+{ 0xFA4B, 0xFA4B, 0xFA4B },
+{ 0xFA4C, 0xFA4C, 0xFA4C },
+{ 0xFA4D, 0xFA4D, 0xFA4D },
+{ 0xFA4E, 0xFA4E, 0xFA4E },
+{ 0xFA4F, 0xFA4F, 0xFA4F },
+{ 0xFA50, 0xFA50, 0xFA50 },
+{ 0xFA51, 0xFA51, 0xFA51 },
+{ 0xFA52, 0xFA52, 0xFA52 },
+{ 0xFA53, 0xFA53, 0xFA53 },
+{ 0xFA54, 0xFA54, 0xFA54 },
+{ 0xFA55, 0xFA55, 0xFA55 },
+{ 0xFA56, 0xFA56, 0xFA56 },
+{ 0xFA57, 0xFA57, 0xFA57 },
+{ 0xFA58, 0xFA58, 0xFA58 },
+{ 0xFA59, 0xFA59, 0xFA59 },
+{ 0xFA5A, 0xFA5A, 0xFA5A },
+{ 0xFA5B, 0xFA5B, 0xFA5B },
+{ 0xFA5C, 0xFA5C, 0xFA5C },
+{ 0xFA5D, 0xFA5D, 0xFA5D },
+{ 0xFA5E, 0xFA5E, 0xFA5E },
+{ 0xFA5F, 0xFA5F, 0xFA5F },
+{ 0xFA60, 0xFA60, 0xFA60 },
+{ 0xFA61, 0xFA61, 0xFA61 },
+{ 0xFA62, 0xFA62, 0xFA62 },
+{ 0xFA63, 0xFA63, 0xFA63 },
+{ 0xFA64, 0xFA64, 0xFA64 },
+{ 0xFA65, 0xFA65, 0xFA65 },
+{ 0xFA66, 0xFA66, 0xFA66 },
+{ 0xFA67, 0xFA67, 0xFA67 },
+{ 0xFA68, 0xFA68, 0xFA68 },
+{ 0xFA69, 0xFA69, 0xFA69 },
+{ 0xFA6A, 0xFA6A, 0xFA6A },
+{ 0xFA70, 0xFA70, 0xFA70 },
+{ 0xFA71, 0xFA71, 0xFA71 },
+{ 0xFA72, 0xFA72, 0xFA72 },
+{ 0xFA73, 0xFA73, 0xFA73 },
+{ 0xFA74, 0xFA74, 0xFA74 },
+{ 0xFA75, 0xFA75, 0xFA75 },
+{ 0xFA76, 0xFA76, 0xFA76 },
+{ 0xFA77, 0xFA77, 0xFA77 },
+{ 0xFA78, 0xFA78, 0xFA78 },
+{ 0xFA79, 0xFA79, 0xFA79 },
+{ 0xFA7A, 0xFA7A, 0xFA7A },
+{ 0xFA7B, 0xFA7B, 0xFA7B },
+{ 0xFA7C, 0xFA7C, 0xFA7C },
+{ 0xFA7D, 0xFA7D, 0xFA7D },
+{ 0xFA7E, 0xFA7E, 0xFA7E },
+{ 0xFA7F, 0xFA7F, 0xFA7F },
+{ 0xFA80, 0xFA80, 0xFA80 },
+{ 0xFA81, 0xFA81, 0xFA81 },
+{ 0xFA82, 0xFA82, 0xFA82 },
+{ 0xFA83, 0xFA83, 0xFA83 },
+{ 0xFA84, 0xFA84, 0xFA84 },
+{ 0xFA85, 0xFA85, 0xFA85 },
+{ 0xFA86, 0xFA86, 0xFA86 },
+{ 0xFA87, 0xFA87, 0xFA87 },
+{ 0xFA88, 0xFA88, 0xFA88 },
+{ 0xFA89, 0xFA89, 0xFA89 },
+{ 0xFA8A, 0xFA8A, 0xFA8A },
+{ 0xFA8B, 0xFA8B, 0xFA8B },
+{ 0xFA8C, 0xFA8C, 0xFA8C },
+{ 0xFA8D, 0xFA8D, 0xFA8D },
+{ 0xFA8E, 0xFA8E, 0xFA8E },
+{ 0xFA8F, 0xFA8F, 0xFA8F },
+{ 0xFA90, 0xFA90, 0xFA90 },
+{ 0xFA91, 0xFA91, 0xFA91 },
+{ 0xFA92, 0xFA92, 0xFA92 },
+{ 0xFA93, 0xFA93, 0xFA93 },
+{ 0xFA94, 0xFA94, 0xFA94 },
+{ 0xFA95, 0xFA95, 0xFA95 },
+{ 0xFA96, 0xFA96, 0xFA96 },
+{ 0xFA97, 0xFA97, 0xFA97 },
+{ 0xFA98, 0xFA98, 0xFA98 },
+{ 0xFA99, 0xFA99, 0xFA99 },
+{ 0xFA9A, 0xFA9A, 0xFA9A },
+{ 0xFA9B, 0xFA9B, 0xFA9B },
+{ 0xFA9C, 0xFA9C, 0xFA9C },
+{ 0xFA9D, 0xFA9D, 0xFA9D },
+{ 0xFA9E, 0xFA9E, 0xFA9E },
+{ 0xFA9F, 0xFA9F, 0xFA9F },
+{ 0xFAA0, 0xFAA0, 0xFAA0 },
+{ 0xFAA1, 0xFAA1, 0xFAA1 },
+{ 0xFAA2, 0xFAA2, 0xFAA2 },
+{ 0xFAA3, 0xFAA3, 0xFAA3 },
+{ 0xFAA4, 0xFAA4, 0xFAA4 },
+{ 0xFAA5, 0xFAA5, 0xFAA5 },
+{ 0xFAA6, 0xFAA6, 0xFAA6 },
+{ 0xFAA7, 0xFAA7, 0xFAA7 },
+{ 0xFAA8, 0xFAA8, 0xFAA8 },
+{ 0xFAA9, 0xFAA9, 0xFAA9 },
+{ 0xFAAA, 0xFAAA, 0xFAAA },
+{ 0xFAAB, 0xFAAB, 0xFAAB },
+{ 0xFAAC, 0xFAAC, 0xFAAC },
+{ 0xFAAD, 0xFAAD, 0xFAAD },
+{ 0xFAAE, 0xFAAE, 0xFAAE },
+{ 0xFAAF, 0xFAAF, 0xFAAF },
+{ 0xFAB0, 0xFAB0, 0xFAB0 },
+{ 0xFAB1, 0xFAB1, 0xFAB1 },
+{ 0xFAB2, 0xFAB2, 0xFAB2 },
+{ 0xFAB3, 0xFAB3, 0xFAB3 },
+{ 0xFAB4, 0xFAB4, 0xFAB4 },
+{ 0xFAB5, 0xFAB5, 0xFAB5 },
+{ 0xFAB6, 0xFAB6, 0xFAB6 },
+{ 0xFAB7, 0xFAB7, 0xFAB7 },
+{ 0xFAB8, 0xFAB8, 0xFAB8 },
+{ 0xFAB9, 0xFAB9, 0xFAB9 },
+{ 0xFABA, 0xFABA, 0xFABA },
+{ 0xFABB, 0xFABB, 0xFABB },
+{ 0xFABC, 0xFABC, 0xFABC },
+{ 0xFABD, 0xFABD, 0xFABD },
+{ 0xFABE, 0xFABE, 0xFABE },
+{ 0xFABF, 0xFABF, 0xFABF },
+{ 0xFAC0, 0xFAC0, 0xFAC0 },
+{ 0xFAC1, 0xFAC1, 0xFAC1 },
+{ 0xFAC2, 0xFAC2, 0xFAC2 },
+{ 0xFAC3, 0xFAC3, 0xFAC3 },
+{ 0xFAC4, 0xFAC4, 0xFAC4 },
+{ 0xFAC5, 0xFAC5, 0xFAC5 },
+{ 0xFAC6, 0xFAC6, 0xFAC6 },
+{ 0xFAC7, 0xFAC7, 0xFAC7 },
+{ 0xFAC8, 0xFAC8, 0xFAC8 },
+{ 0xFAC9, 0xFAC9, 0xFAC9 },
+{ 0xFACA, 0xFACA, 0xFACA },
+{ 0xFACB, 0xFACB, 0xFACB },
+{ 0xFACC, 0xFACC, 0xFACC },
+{ 0xFACD, 0xFACD, 0xFACD },
+{ 0xFACE, 0xFACE, 0xFACE },
+{ 0xFACF, 0xFACF, 0xFACF },
+{ 0xFAD0, 0xFAD0, 0xFAD0 },
+{ 0xFAD1, 0xFAD1, 0xFAD1 },
+{ 0xFAD2, 0xFAD2, 0xFAD2 },
+{ 0xFAD3, 0xFAD3, 0xFAD3 },
+{ 0xFAD4, 0xFAD4, 0xFAD4 },
+{ 0xFAD5, 0xFAD5, 0xFAD5 },
+{ 0xFAD6, 0xFAD6, 0xFAD6 },
+{ 0xFAD7, 0xFAD7, 0xFAD7 },
+{ 0xFAD8, 0xFAD8, 0xFAD8 },
+{ 0xFAD9, 0xFAD9, 0xFAD9 },
+{ 0xFB00, 0xFB00, 0xFB00 },
+{ 0xFB01, 0xFB01, 0xFB01 },
+{ 0xFB02, 0xFB02, 0xFB02 },
+{ 0xFB03, 0xFB03, 0xFB03 },
+{ 0xFB04, 0xFB04, 0xFB04 },
+{ 0xFB05, 0xFB05, 0xFB05 },
+{ 0xFB06, 0xFB06, 0xFB06 },
+{ 0xFB13, 0xFB13, 0xFB13 },
+{ 0xFB14, 0xFB14, 0xFB14 },
+{ 0xFB15, 0xFB15, 0xFB15 },
+{ 0xFB16, 0xFB16, 0xFB16 },
+{ 0xFB17, 0xFB17, 0xFB17 },
+{ 0xFB1D, 0xFB1D, 0xFB1D },
+{ 0xFB1E, 0xFB1E, 0xFB1E },
+{ 0xFB1F, 0xFB1F, 0xFB1F },
+{ 0xFB20, 0xFB20, 0xFB20 },
+{ 0xFB21, 0xFB21, 0xFB21 },
+{ 0xFB22, 0xFB22, 0xFB22 },
+{ 0xFB23, 0xFB23, 0xFB23 },
+{ 0xFB24, 0xFB24, 0xFB24 },
+{ 0xFB25, 0xFB25, 0xFB25 },
+{ 0xFB26, 0xFB26, 0xFB26 },
+{ 0xFB27, 0xFB27, 0xFB27 },
+{ 0xFB28, 0xFB28, 0xFB28 },
+{ 0xFB2A, 0xFB2A, 0xFB2A },
+{ 0xFB2B, 0xFB2B, 0xFB2B },
+{ 0xFB2C, 0xFB2C, 0xFB2C },
+{ 0xFB2D, 0xFB2D, 0xFB2D },
+{ 0xFB2E, 0xFB2E, 0xFB2E },
+{ 0xFB2F, 0xFB2F, 0xFB2F },
+{ 0xFB30, 0xFB30, 0xFB30 },
+{ 0xFB31, 0xFB31, 0xFB31 },
+{ 0xFB32, 0xFB32, 0xFB32 },
+{ 0xFB33, 0xFB33, 0xFB33 },
+{ 0xFB34, 0xFB34, 0xFB34 },
+{ 0xFB35, 0xFB35, 0xFB35 },
+{ 0xFB36, 0xFB36, 0xFB36 },
+{ 0xFB38, 0xFB38, 0xFB38 },
+{ 0xFB39, 0xFB39, 0xFB39 },
+{ 0xFB3A, 0xFB3A, 0xFB3A },
+{ 0xFB3B, 0xFB3B, 0xFB3B },
+{ 0xFB3C, 0xFB3C, 0xFB3C },
+{ 0xFB3E, 0xFB3E, 0xFB3E },
+{ 0xFB40, 0xFB40, 0xFB40 },
+{ 0xFB41, 0xFB41, 0xFB41 },
+{ 0xFB43, 0xFB43, 0xFB43 },
+{ 0xFB44, 0xFB44, 0xFB44 },
+{ 0xFB46, 0xFB46, 0xFB46 },
+{ 0xFB47, 0xFB47, 0xFB47 },
+{ 0xFB48, 0xFB48, 0xFB48 },
+{ 0xFB49, 0xFB49, 0xFB49 },
+{ 0xFB4A, 0xFB4A, 0xFB4A },
+{ 0xFB4B, 0xFB4B, 0xFB4B },
+{ 0xFB4C, 0xFB4C, 0xFB4C },
+{ 0xFB4D, 0xFB4D, 0xFB4D },
+{ 0xFB4E, 0xFB4E, 0xFB4E },
+{ 0xFB4F, 0xFB4F, 0xFB4F },
+{ 0xFB50, 0xFB50, 0xFB50 },
+{ 0xFB51, 0xFB51, 0xFB51 },
+{ 0xFB52, 0xFB52, 0xFB52 },
+{ 0xFB53, 0xFB53, 0xFB53 },
+{ 0xFB54, 0xFB54, 0xFB54 },
+{ 0xFB55, 0xFB55, 0xFB55 },
+{ 0xFB56, 0xFB56, 0xFB56 },
+{ 0xFB57, 0xFB57, 0xFB57 },
+{ 0xFB58, 0xFB58, 0xFB58 },
+{ 0xFB59, 0xFB59, 0xFB59 },
+{ 0xFB5A, 0xFB5A, 0xFB5A },
+{ 0xFB5B, 0xFB5B, 0xFB5B },
+{ 0xFB5C, 0xFB5C, 0xFB5C },
+{ 0xFB5D, 0xFB5D, 0xFB5D },
+{ 0xFB5E, 0xFB5E, 0xFB5E },
+{ 0xFB5F, 0xFB5F, 0xFB5F },
+{ 0xFB60, 0xFB60, 0xFB60 },
+{ 0xFB61, 0xFB61, 0xFB61 },
+{ 0xFB62, 0xFB62, 0xFB62 },
+{ 0xFB63, 0xFB63, 0xFB63 },
+{ 0xFB64, 0xFB64, 0xFB64 },
+{ 0xFB65, 0xFB65, 0xFB65 },
+{ 0xFB66, 0xFB66, 0xFB66 },
+{ 0xFB67, 0xFB67, 0xFB67 },
+{ 0xFB68, 0xFB68, 0xFB68 },
+{ 0xFB69, 0xFB69, 0xFB69 },
+{ 0xFB6A, 0xFB6A, 0xFB6A },
+{ 0xFB6B, 0xFB6B, 0xFB6B },
+{ 0xFB6C, 0xFB6C, 0xFB6C },
+{ 0xFB6D, 0xFB6D, 0xFB6D },
+{ 0xFB6E, 0xFB6E, 0xFB6E },
+{ 0xFB6F, 0xFB6F, 0xFB6F },
+{ 0xFB70, 0xFB70, 0xFB70 },
+{ 0xFB71, 0xFB71, 0xFB71 },
+{ 0xFB72, 0xFB72, 0xFB72 },
+{ 0xFB73, 0xFB73, 0xFB73 },
+{ 0xFB74, 0xFB74, 0xFB74 },
+{ 0xFB75, 0xFB75, 0xFB75 },
+{ 0xFB76, 0xFB76, 0xFB76 },
+{ 0xFB77, 0xFB77, 0xFB77 },
+{ 0xFB78, 0xFB78, 0xFB78 },
+{ 0xFB79, 0xFB79, 0xFB79 },
+{ 0xFB7A, 0xFB7A, 0xFB7A },
+{ 0xFB7B, 0xFB7B, 0xFB7B },
+{ 0xFB7C, 0xFB7C, 0xFB7C },
+{ 0xFB7D, 0xFB7D, 0xFB7D },
+{ 0xFB7E, 0xFB7E, 0xFB7E },
+{ 0xFB7F, 0xFB7F, 0xFB7F },
+{ 0xFB80, 0xFB80, 0xFB80 },
+{ 0xFB81, 0xFB81, 0xFB81 },
+{ 0xFB82, 0xFB82, 0xFB82 },
+{ 0xFB83, 0xFB83, 0xFB83 },
+{ 0xFB84, 0xFB84, 0xFB84 },
+{ 0xFB85, 0xFB85, 0xFB85 },
+{ 0xFB86, 0xFB86, 0xFB86 },
+{ 0xFB87, 0xFB87, 0xFB87 },
+{ 0xFB88, 0xFB88, 0xFB88 },
+{ 0xFB89, 0xFB89, 0xFB89 },
+{ 0xFB8A, 0xFB8A, 0xFB8A },
+{ 0xFB8B, 0xFB8B, 0xFB8B },
+{ 0xFB8C, 0xFB8C, 0xFB8C },
+{ 0xFB8D, 0xFB8D, 0xFB8D },
+{ 0xFB8E, 0xFB8E, 0xFB8E },
+{ 0xFB8F, 0xFB8F, 0xFB8F },
+{ 0xFB90, 0xFB90, 0xFB90 },
+{ 0xFB91, 0xFB91, 0xFB91 },
+{ 0xFB92, 0xFB92, 0xFB92 },
+{ 0xFB93, 0xFB93, 0xFB93 },
+{ 0xFB94, 0xFB94, 0xFB94 },
+{ 0xFB95, 0xFB95, 0xFB95 },
+{ 0xFB96, 0xFB96, 0xFB96 },
+{ 0xFB97, 0xFB97, 0xFB97 },
+{ 0xFB98, 0xFB98, 0xFB98 },
+{ 0xFB99, 0xFB99, 0xFB99 },
+{ 0xFB9A, 0xFB9A, 0xFB9A },
+{ 0xFB9B, 0xFB9B, 0xFB9B },
+{ 0xFB9C, 0xFB9C, 0xFB9C },
+{ 0xFB9D, 0xFB9D, 0xFB9D },
+{ 0xFB9E, 0xFB9E, 0xFB9E },
+{ 0xFB9F, 0xFB9F, 0xFB9F },
+{ 0xFBA0, 0xFBA0, 0xFBA0 },
+{ 0xFBA1, 0xFBA1, 0xFBA1 },
+{ 0xFBA2, 0xFBA2, 0xFBA2 },
+{ 0xFBA3, 0xFBA3, 0xFBA3 },
+{ 0xFBA4, 0xFBA4, 0xFBA4 },
+{ 0xFBA5, 0xFBA5, 0xFBA5 },
+{ 0xFBA6, 0xFBA6, 0xFBA6 },
+{ 0xFBA7, 0xFBA7, 0xFBA7 },
+{ 0xFBA8, 0xFBA8, 0xFBA8 },
+{ 0xFBA9, 0xFBA9, 0xFBA9 },
+{ 0xFBAA, 0xFBAA, 0xFBAA },
+{ 0xFBAB, 0xFBAB, 0xFBAB },
+{ 0xFBAC, 0xFBAC, 0xFBAC },
+{ 0xFBAD, 0xFBAD, 0xFBAD },
+{ 0xFBAE, 0xFBAE, 0xFBAE },
+{ 0xFBAF, 0xFBAF, 0xFBAF },
+{ 0xFBB0, 0xFBB0, 0xFBB0 },
+{ 0xFBB1, 0xFBB1, 0xFBB1 },
+{ 0xFBD3, 0xFBD3, 0xFBD3 },
+{ 0xFBD4, 0xFBD4, 0xFBD4 },
+{ 0xFBD5, 0xFBD5, 0xFBD5 },
+{ 0xFBD6, 0xFBD6, 0xFBD6 },
+{ 0xFBD7, 0xFBD7, 0xFBD7 },
+{ 0xFBD8, 0xFBD8, 0xFBD8 },
+{ 0xFBD9, 0xFBD9, 0xFBD9 },
+{ 0xFBDA, 0xFBDA, 0xFBDA },
+{ 0xFBDB, 0xFBDB, 0xFBDB },
+{ 0xFBDC, 0xFBDC, 0xFBDC },
+{ 0xFBDD, 0xFBDD, 0xFBDD },
+{ 0xFBDE, 0xFBDE, 0xFBDE },
+{ 0xFBDF, 0xFBDF, 0xFBDF },
+{ 0xFBE0, 0xFBE0, 0xFBE0 },
+{ 0xFBE1, 0xFBE1, 0xFBE1 },
+{ 0xFBE2, 0xFBE2, 0xFBE2 },
+{ 0xFBE3, 0xFBE3, 0xFBE3 },
+{ 0xFBE4, 0xFBE4, 0xFBE4 },
+{ 0xFBE5, 0xFBE5, 0xFBE5 },
+{ 0xFBE6, 0xFBE6, 0xFBE6 },
+{ 0xFBE7, 0xFBE7, 0xFBE7 },
+{ 0xFBE8, 0xFBE8, 0xFBE8 },
+{ 0xFBE9, 0xFBE9, 0xFBE9 },
+{ 0xFBEA, 0xFBEA, 0xFBEA },
+{ 0xFBEB, 0xFBEB, 0xFBEB },
+{ 0xFBEC, 0xFBEC, 0xFBEC },
+{ 0xFBED, 0xFBED, 0xFBED },
+{ 0xFBEE, 0xFBEE, 0xFBEE },
+{ 0xFBEF, 0xFBEF, 0xFBEF },
+{ 0xFBF0, 0xFBF0, 0xFBF0 },
+{ 0xFBF1, 0xFBF1, 0xFBF1 },
+{ 0xFBF2, 0xFBF2, 0xFBF2 },
+{ 0xFBF3, 0xFBF3, 0xFBF3 },
+{ 0xFBF4, 0xFBF4, 0xFBF4 },
+{ 0xFBF5, 0xFBF5, 0xFBF5 },
+{ 0xFBF6, 0xFBF6, 0xFBF6 },
+{ 0xFBF7, 0xFBF7, 0xFBF7 },
+{ 0xFBF8, 0xFBF8, 0xFBF8 },
+{ 0xFBF9, 0xFBF9, 0xFBF9 },
+{ 0xFBFA, 0xFBFA, 0xFBFA },
+{ 0xFBFB, 0xFBFB, 0xFBFB },
+{ 0xFBFC, 0xFBFC, 0xFBFC },
+{ 0xFBFD, 0xFBFD, 0xFBFD },
+{ 0xFBFE, 0xFBFE, 0xFBFE },
+{ 0xFBFF, 0xFBFF, 0xFBFF },
+{ 0xFC00, 0xFC00, 0xFC00 },
+{ 0xFC01, 0xFC01, 0xFC01 },
+{ 0xFC02, 0xFC02, 0xFC02 },
+{ 0xFC03, 0xFC03, 0xFC03 },
+{ 0xFC04, 0xFC04, 0xFC04 },
+{ 0xFC05, 0xFC05, 0xFC05 },
+{ 0xFC06, 0xFC06, 0xFC06 },
+{ 0xFC07, 0xFC07, 0xFC07 },
+{ 0xFC08, 0xFC08, 0xFC08 },
+{ 0xFC09, 0xFC09, 0xFC09 },
+{ 0xFC0A, 0xFC0A, 0xFC0A },
+{ 0xFC0B, 0xFC0B, 0xFC0B },
+{ 0xFC0C, 0xFC0C, 0xFC0C },
+{ 0xFC0D, 0xFC0D, 0xFC0D },
+{ 0xFC0E, 0xFC0E, 0xFC0E },
+{ 0xFC0F, 0xFC0F, 0xFC0F },
+{ 0xFC10, 0xFC10, 0xFC10 },
+{ 0xFC11, 0xFC11, 0xFC11 },
+{ 0xFC12, 0xFC12, 0xFC12 },
+{ 0xFC13, 0xFC13, 0xFC13 },
+{ 0xFC14, 0xFC14, 0xFC14 },
+{ 0xFC15, 0xFC15, 0xFC15 },
+{ 0xFC16, 0xFC16, 0xFC16 },
+{ 0xFC17, 0xFC17, 0xFC17 },
+{ 0xFC18, 0xFC18, 0xFC18 },
+{ 0xFC19, 0xFC19, 0xFC19 },
+{ 0xFC1A, 0xFC1A, 0xFC1A },
+{ 0xFC1B, 0xFC1B, 0xFC1B },
+{ 0xFC1C, 0xFC1C, 0xFC1C },
+{ 0xFC1D, 0xFC1D, 0xFC1D },
+{ 0xFC1E, 0xFC1E, 0xFC1E },
+{ 0xFC1F, 0xFC1F, 0xFC1F },
+{ 0xFC20, 0xFC20, 0xFC20 },
+{ 0xFC21, 0xFC21, 0xFC21 },
+{ 0xFC22, 0xFC22, 0xFC22 },
+{ 0xFC23, 0xFC23, 0xFC23 },
+{ 0xFC24, 0xFC24, 0xFC24 },
+{ 0xFC25, 0xFC25, 0xFC25 },
+{ 0xFC26, 0xFC26, 0xFC26 },
+{ 0xFC27, 0xFC27, 0xFC27 },
+{ 0xFC28, 0xFC28, 0xFC28 },
+{ 0xFC29, 0xFC29, 0xFC29 },
+{ 0xFC2A, 0xFC2A, 0xFC2A },
+{ 0xFC2B, 0xFC2B, 0xFC2B },
+{ 0xFC2C, 0xFC2C, 0xFC2C },
+{ 0xFC2D, 0xFC2D, 0xFC2D },
+{ 0xFC2E, 0xFC2E, 0xFC2E },
+{ 0xFC2F, 0xFC2F, 0xFC2F },
+{ 0xFC30, 0xFC30, 0xFC30 },
+{ 0xFC31, 0xFC31, 0xFC31 },
+{ 0xFC32, 0xFC32, 0xFC32 },
+{ 0xFC33, 0xFC33, 0xFC33 },
+{ 0xFC34, 0xFC34, 0xFC34 },
+{ 0xFC35, 0xFC35, 0xFC35 },
+{ 0xFC36, 0xFC36, 0xFC36 },
+{ 0xFC37, 0xFC37, 0xFC37 },
+{ 0xFC38, 0xFC38, 0xFC38 },
+{ 0xFC39, 0xFC39, 0xFC39 },
+{ 0xFC3A, 0xFC3A, 0xFC3A },
+{ 0xFC3B, 0xFC3B, 0xFC3B },
+{ 0xFC3C, 0xFC3C, 0xFC3C },
+{ 0xFC3D, 0xFC3D, 0xFC3D },
+{ 0xFC3E, 0xFC3E, 0xFC3E },
+{ 0xFC3F, 0xFC3F, 0xFC3F },
+{ 0xFC40, 0xFC40, 0xFC40 },
+{ 0xFC41, 0xFC41, 0xFC41 },
+{ 0xFC42, 0xFC42, 0xFC42 },
+{ 0xFC43, 0xFC43, 0xFC43 },
+{ 0xFC44, 0xFC44, 0xFC44 },
+{ 0xFC45, 0xFC45, 0xFC45 },
+{ 0xFC46, 0xFC46, 0xFC46 },
+{ 0xFC47, 0xFC47, 0xFC47 },
+{ 0xFC48, 0xFC48, 0xFC48 },
+{ 0xFC49, 0xFC49, 0xFC49 },
+{ 0xFC4A, 0xFC4A, 0xFC4A },
+{ 0xFC4B, 0xFC4B, 0xFC4B },
+{ 0xFC4C, 0xFC4C, 0xFC4C },
+{ 0xFC4D, 0xFC4D, 0xFC4D },
+{ 0xFC4E, 0xFC4E, 0xFC4E },
+{ 0xFC4F, 0xFC4F, 0xFC4F },
+{ 0xFC50, 0xFC50, 0xFC50 },
+{ 0xFC51, 0xFC51, 0xFC51 },
+{ 0xFC52, 0xFC52, 0xFC52 },
+{ 0xFC53, 0xFC53, 0xFC53 },
+{ 0xFC54, 0xFC54, 0xFC54 },
+{ 0xFC55, 0xFC55, 0xFC55 },
+{ 0xFC56, 0xFC56, 0xFC56 },
+{ 0xFC57, 0xFC57, 0xFC57 },
+{ 0xFC58, 0xFC58, 0xFC58 },
+{ 0xFC59, 0xFC59, 0xFC59 },
+{ 0xFC5A, 0xFC5A, 0xFC5A },
+{ 0xFC5B, 0xFC5B, 0xFC5B },
+{ 0xFC5C, 0xFC5C, 0xFC5C },
+{ 0xFC5D, 0xFC5D, 0xFC5D },
+{ 0xFC5E, 0xFC5E, 0xFC5E },
+{ 0xFC5F, 0xFC5F, 0xFC5F },
+{ 0xFC60, 0xFC60, 0xFC60 },
+{ 0xFC61, 0xFC61, 0xFC61 },
+{ 0xFC62, 0xFC62, 0xFC62 },
+{ 0xFC63, 0xFC63, 0xFC63 },
+{ 0xFC64, 0xFC64, 0xFC64 },
+{ 0xFC65, 0xFC65, 0xFC65 },
+{ 0xFC66, 0xFC66, 0xFC66 },
+{ 0xFC67, 0xFC67, 0xFC67 },
+{ 0xFC68, 0xFC68, 0xFC68 },
+{ 0xFC69, 0xFC69, 0xFC69 },
+{ 0xFC6A, 0xFC6A, 0xFC6A },
+{ 0xFC6B, 0xFC6B, 0xFC6B },
+{ 0xFC6C, 0xFC6C, 0xFC6C },
+{ 0xFC6D, 0xFC6D, 0xFC6D },
+{ 0xFC6E, 0xFC6E, 0xFC6E },
+{ 0xFC6F, 0xFC6F, 0xFC6F },
+{ 0xFC70, 0xFC70, 0xFC70 },
+{ 0xFC71, 0xFC71, 0xFC71 },
+{ 0xFC72, 0xFC72, 0xFC72 },
+{ 0xFC73, 0xFC73, 0xFC73 },
+{ 0xFC74, 0xFC74, 0xFC74 },
+{ 0xFC75, 0xFC75, 0xFC75 },
+{ 0xFC76, 0xFC76, 0xFC76 },
+{ 0xFC77, 0xFC77, 0xFC77 },
+{ 0xFC78, 0xFC78, 0xFC78 },
+{ 0xFC79, 0xFC79, 0xFC79 },
+{ 0xFC7A, 0xFC7A, 0xFC7A },
+{ 0xFC7B, 0xFC7B, 0xFC7B },
+{ 0xFC7C, 0xFC7C, 0xFC7C },
+{ 0xFC7D, 0xFC7D, 0xFC7D },
+{ 0xFC7E, 0xFC7E, 0xFC7E },
+{ 0xFC7F, 0xFC7F, 0xFC7F },
+{ 0xFC80, 0xFC80, 0xFC80 },
+{ 0xFC81, 0xFC81, 0xFC81 },
+{ 0xFC82, 0xFC82, 0xFC82 },
+{ 0xFC83, 0xFC83, 0xFC83 },
+{ 0xFC84, 0xFC84, 0xFC84 },
+{ 0xFC85, 0xFC85, 0xFC85 },
+{ 0xFC86, 0xFC86, 0xFC86 },
+{ 0xFC87, 0xFC87, 0xFC87 },
+{ 0xFC88, 0xFC88, 0xFC88 },
+{ 0xFC89, 0xFC89, 0xFC89 },
+{ 0xFC8A, 0xFC8A, 0xFC8A },
+{ 0xFC8B, 0xFC8B, 0xFC8B },
+{ 0xFC8C, 0xFC8C, 0xFC8C },
+{ 0xFC8D, 0xFC8D, 0xFC8D },
+{ 0xFC8E, 0xFC8E, 0xFC8E },
+{ 0xFC8F, 0xFC8F, 0xFC8F },
+{ 0xFC90, 0xFC90, 0xFC90 },
+{ 0xFC91, 0xFC91, 0xFC91 },
+{ 0xFC92, 0xFC92, 0xFC92 },
+{ 0xFC93, 0xFC93, 0xFC93 },
+{ 0xFC94, 0xFC94, 0xFC94 },
+{ 0xFC95, 0xFC95, 0xFC95 },
+{ 0xFC96, 0xFC96, 0xFC96 },
+{ 0xFC97, 0xFC97, 0xFC97 },
+{ 0xFC98, 0xFC98, 0xFC98 },
+{ 0xFC99, 0xFC99, 0xFC99 },
+{ 0xFC9A, 0xFC9A, 0xFC9A },
+{ 0xFC9B, 0xFC9B, 0xFC9B },
+{ 0xFC9C, 0xFC9C, 0xFC9C },
+{ 0xFC9D, 0xFC9D, 0xFC9D },
+{ 0xFC9E, 0xFC9E, 0xFC9E },
+{ 0xFC9F, 0xFC9F, 0xFC9F },
+{ 0xFCA0, 0xFCA0, 0xFCA0 },
+{ 0xFCA1, 0xFCA1, 0xFCA1 },
+{ 0xFCA2, 0xFCA2, 0xFCA2 },
+{ 0xFCA3, 0xFCA3, 0xFCA3 },
+{ 0xFCA4, 0xFCA4, 0xFCA4 },
+{ 0xFCA5, 0xFCA5, 0xFCA5 },
+{ 0xFCA6, 0xFCA6, 0xFCA6 },
+{ 0xFCA7, 0xFCA7, 0xFCA7 },
+{ 0xFCA8, 0xFCA8, 0xFCA8 },
+{ 0xFCA9, 0xFCA9, 0xFCA9 },
+{ 0xFCAA, 0xFCAA, 0xFCAA },
+{ 0xFCAB, 0xFCAB, 0xFCAB },
+{ 0xFCAC, 0xFCAC, 0xFCAC },
+{ 0xFCAD, 0xFCAD, 0xFCAD },
+{ 0xFCAE, 0xFCAE, 0xFCAE },
+{ 0xFCAF, 0xFCAF, 0xFCAF },
+{ 0xFCB0, 0xFCB0, 0xFCB0 },
+{ 0xFCB1, 0xFCB1, 0xFCB1 },
+{ 0xFCB2, 0xFCB2, 0xFCB2 },
+{ 0xFCB3, 0xFCB3, 0xFCB3 },
+{ 0xFCB4, 0xFCB4, 0xFCB4 },
+{ 0xFCB5, 0xFCB5, 0xFCB5 },
+{ 0xFCB6, 0xFCB6, 0xFCB6 },
+{ 0xFCB7, 0xFCB7, 0xFCB7 },
+{ 0xFCB8, 0xFCB8, 0xFCB8 },
+{ 0xFCB9, 0xFCB9, 0xFCB9 },
+{ 0xFCBA, 0xFCBA, 0xFCBA },
+{ 0xFCBB, 0xFCBB, 0xFCBB },
+{ 0xFCBC, 0xFCBC, 0xFCBC },
+{ 0xFCBD, 0xFCBD, 0xFCBD },
+{ 0xFCBE, 0xFCBE, 0xFCBE },
+{ 0xFCBF, 0xFCBF, 0xFCBF },
+{ 0xFCC0, 0xFCC0, 0xFCC0 },
+{ 0xFCC1, 0xFCC1, 0xFCC1 },
+{ 0xFCC2, 0xFCC2, 0xFCC2 },
+{ 0xFCC3, 0xFCC3, 0xFCC3 },
+{ 0xFCC4, 0xFCC4, 0xFCC4 },
+{ 0xFCC5, 0xFCC5, 0xFCC5 },
+{ 0xFCC6, 0xFCC6, 0xFCC6 },
+{ 0xFCC7, 0xFCC7, 0xFCC7 },
+{ 0xFCC8, 0xFCC8, 0xFCC8 },
+{ 0xFCC9, 0xFCC9, 0xFCC9 },
+{ 0xFCCA, 0xFCCA, 0xFCCA },
+{ 0xFCCB, 0xFCCB, 0xFCCB },
+{ 0xFCCC, 0xFCCC, 0xFCCC },
+{ 0xFCCD, 0xFCCD, 0xFCCD },
+{ 0xFCCE, 0xFCCE, 0xFCCE },
+{ 0xFCCF, 0xFCCF, 0xFCCF },
+{ 0xFCD0, 0xFCD0, 0xFCD0 },
+{ 0xFCD1, 0xFCD1, 0xFCD1 },
+{ 0xFCD2, 0xFCD2, 0xFCD2 },
+{ 0xFCD3, 0xFCD3, 0xFCD3 },
+{ 0xFCD4, 0xFCD4, 0xFCD4 },
+{ 0xFCD5, 0xFCD5, 0xFCD5 },
+{ 0xFCD6, 0xFCD6, 0xFCD6 },
+{ 0xFCD7, 0xFCD7, 0xFCD7 },
+{ 0xFCD8, 0xFCD8, 0xFCD8 },
+{ 0xFCD9, 0xFCD9, 0xFCD9 },
+{ 0xFCDA, 0xFCDA, 0xFCDA },
+{ 0xFCDB, 0xFCDB, 0xFCDB },
+{ 0xFCDC, 0xFCDC, 0xFCDC },
+{ 0xFCDD, 0xFCDD, 0xFCDD },
+{ 0xFCDE, 0xFCDE, 0xFCDE },
+{ 0xFCDF, 0xFCDF, 0xFCDF },
+{ 0xFCE0, 0xFCE0, 0xFCE0 },
+{ 0xFCE1, 0xFCE1, 0xFCE1 },
+{ 0xFCE2, 0xFCE2, 0xFCE2 },
+{ 0xFCE3, 0xFCE3, 0xFCE3 },
+{ 0xFCE4, 0xFCE4, 0xFCE4 },
+{ 0xFCE5, 0xFCE5, 0xFCE5 },
+{ 0xFCE6, 0xFCE6, 0xFCE6 },
+{ 0xFCE7, 0xFCE7, 0xFCE7 },
+{ 0xFCE8, 0xFCE8, 0xFCE8 },
+{ 0xFCE9, 0xFCE9, 0xFCE9 },
+{ 0xFCEA, 0xFCEA, 0xFCEA },
+{ 0xFCEB, 0xFCEB, 0xFCEB },
+{ 0xFCEC, 0xFCEC, 0xFCEC },
+{ 0xFCED, 0xFCED, 0xFCED },
+{ 0xFCEE, 0xFCEE, 0xFCEE },
+{ 0xFCEF, 0xFCEF, 0xFCEF },
+{ 0xFCF0, 0xFCF0, 0xFCF0 },
+{ 0xFCF1, 0xFCF1, 0xFCF1 },
+{ 0xFCF2, 0xFCF2, 0xFCF2 },
+{ 0xFCF3, 0xFCF3, 0xFCF3 },
+{ 0xFCF4, 0xFCF4, 0xFCF4 },
+{ 0xFCF5, 0xFCF5, 0xFCF5 },
+{ 0xFCF6, 0xFCF6, 0xFCF6 },
+{ 0xFCF7, 0xFCF7, 0xFCF7 },
+{ 0xFCF8, 0xFCF8, 0xFCF8 },
+{ 0xFCF9, 0xFCF9, 0xFCF9 },
+{ 0xFCFA, 0xFCFA, 0xFCFA },
+{ 0xFCFB, 0xFCFB, 0xFCFB },
+{ 0xFCFC, 0xFCFC, 0xFCFC },
+{ 0xFCFD, 0xFCFD, 0xFCFD },
+{ 0xFCFE, 0xFCFE, 0xFCFE },
+{ 0xFCFF, 0xFCFF, 0xFCFF },
+{ 0xFD00, 0xFD00, 0xFD00 },
+{ 0xFD01, 0xFD01, 0xFD01 },
+{ 0xFD02, 0xFD02, 0xFD02 },
+{ 0xFD03, 0xFD03, 0xFD03 },
+{ 0xFD04, 0xFD04, 0xFD04 },
+{ 0xFD05, 0xFD05, 0xFD05 },
+{ 0xFD06, 0xFD06, 0xFD06 },
+{ 0xFD07, 0xFD07, 0xFD07 },
+{ 0xFD08, 0xFD08, 0xFD08 },
+{ 0xFD09, 0xFD09, 0xFD09 },
+{ 0xFD0A, 0xFD0A, 0xFD0A },
+{ 0xFD0B, 0xFD0B, 0xFD0B },
+{ 0xFD0C, 0xFD0C, 0xFD0C },
+{ 0xFD0D, 0xFD0D, 0xFD0D },
+{ 0xFD0E, 0xFD0E, 0xFD0E },
+{ 0xFD0F, 0xFD0F, 0xFD0F },
+{ 0xFD10, 0xFD10, 0xFD10 },
+{ 0xFD11, 0xFD11, 0xFD11 },
+{ 0xFD12, 0xFD12, 0xFD12 },
+{ 0xFD13, 0xFD13, 0xFD13 },
+{ 0xFD14, 0xFD14, 0xFD14 },
+{ 0xFD15, 0xFD15, 0xFD15 },
+{ 0xFD16, 0xFD16, 0xFD16 },
+{ 0xFD17, 0xFD17, 0xFD17 },
+{ 0xFD18, 0xFD18, 0xFD18 },
+{ 0xFD19, 0xFD19, 0xFD19 },
+{ 0xFD1A, 0xFD1A, 0xFD1A },
+{ 0xFD1B, 0xFD1B, 0xFD1B },
+{ 0xFD1C, 0xFD1C, 0xFD1C },
+{ 0xFD1D, 0xFD1D, 0xFD1D },
+{ 0xFD1E, 0xFD1E, 0xFD1E },
+{ 0xFD1F, 0xFD1F, 0xFD1F },
+{ 0xFD20, 0xFD20, 0xFD20 },
+{ 0xFD21, 0xFD21, 0xFD21 },
+{ 0xFD22, 0xFD22, 0xFD22 },
+{ 0xFD23, 0xFD23, 0xFD23 },
+{ 0xFD24, 0xFD24, 0xFD24 },
+{ 0xFD25, 0xFD25, 0xFD25 },
+{ 0xFD26, 0xFD26, 0xFD26 },
+{ 0xFD27, 0xFD27, 0xFD27 },
+{ 0xFD28, 0xFD28, 0xFD28 },
+{ 0xFD29, 0xFD29, 0xFD29 },
+{ 0xFD2A, 0xFD2A, 0xFD2A },
+{ 0xFD2B, 0xFD2B, 0xFD2B },
+{ 0xFD2C, 0xFD2C, 0xFD2C },
+{ 0xFD2D, 0xFD2D, 0xFD2D },
+{ 0xFD2E, 0xFD2E, 0xFD2E },
+{ 0xFD2F, 0xFD2F, 0xFD2F },
+{ 0xFD30, 0xFD30, 0xFD30 },
+{ 0xFD31, 0xFD31, 0xFD31 },
+{ 0xFD32, 0xFD32, 0xFD32 },
+{ 0xFD33, 0xFD33, 0xFD33 },
+{ 0xFD34, 0xFD34, 0xFD34 },
+{ 0xFD35, 0xFD35, 0xFD35 },
+{ 0xFD36, 0xFD36, 0xFD36 },
+{ 0xFD37, 0xFD37, 0xFD37 },
+{ 0xFD38, 0xFD38, 0xFD38 },
+{ 0xFD39, 0xFD39, 0xFD39 },
+{ 0xFD3A, 0xFD3A, 0xFD3A },
+{ 0xFD3B, 0xFD3B, 0xFD3B },
+{ 0xFD3C, 0xFD3C, 0xFD3C },
+{ 0xFD3D, 0xFD3D, 0xFD3D },
+{ 0xFD50, 0xFD50, 0xFD50 },
+{ 0xFD51, 0xFD51, 0xFD51 },
+{ 0xFD52, 0xFD52, 0xFD52 },
+{ 0xFD53, 0xFD53, 0xFD53 },
+{ 0xFD54, 0xFD54, 0xFD54 },
+{ 0xFD55, 0xFD55, 0xFD55 },
+{ 0xFD56, 0xFD56, 0xFD56 },
+{ 0xFD57, 0xFD57, 0xFD57 },
+{ 0xFD58, 0xFD58, 0xFD58 },
+{ 0xFD59, 0xFD59, 0xFD59 },
+{ 0xFD5A, 0xFD5A, 0xFD5A },
+{ 0xFD5B, 0xFD5B, 0xFD5B },
+{ 0xFD5C, 0xFD5C, 0xFD5C },
+{ 0xFD5D, 0xFD5D, 0xFD5D },
+{ 0xFD5E, 0xFD5E, 0xFD5E },
+{ 0xFD5F, 0xFD5F, 0xFD5F },
+{ 0xFD60, 0xFD60, 0xFD60 },
+{ 0xFD61, 0xFD61, 0xFD61 },
+{ 0xFD62, 0xFD62, 0xFD62 },
+{ 0xFD63, 0xFD63, 0xFD63 },
+{ 0xFD64, 0xFD64, 0xFD64 },
+{ 0xFD65, 0xFD65, 0xFD65 },
+{ 0xFD66, 0xFD66, 0xFD66 },
+{ 0xFD67, 0xFD67, 0xFD67 },
+{ 0xFD68, 0xFD68, 0xFD68 },
+{ 0xFD69, 0xFD69, 0xFD69 },
+{ 0xFD6A, 0xFD6A, 0xFD6A },
+{ 0xFD6B, 0xFD6B, 0xFD6B },
+{ 0xFD6C, 0xFD6C, 0xFD6C },
+{ 0xFD6D, 0xFD6D, 0xFD6D },
+{ 0xFD6E, 0xFD6E, 0xFD6E },
+{ 0xFD6F, 0xFD6F, 0xFD6F },
+{ 0xFD70, 0xFD70, 0xFD70 },
+{ 0xFD71, 0xFD71, 0xFD71 },
+{ 0xFD72, 0xFD72, 0xFD72 },
+{ 0xFD73, 0xFD73, 0xFD73 },
+{ 0xFD74, 0xFD74, 0xFD74 },
+{ 0xFD75, 0xFD75, 0xFD75 },
+{ 0xFD76, 0xFD76, 0xFD76 },
+{ 0xFD77, 0xFD77, 0xFD77 },
+{ 0xFD78, 0xFD78, 0xFD78 },
+{ 0xFD79, 0xFD79, 0xFD79 },
+{ 0xFD7A, 0xFD7A, 0xFD7A },
+{ 0xFD7B, 0xFD7B, 0xFD7B },
+{ 0xFD7C, 0xFD7C, 0xFD7C },
+{ 0xFD7D, 0xFD7D, 0xFD7D },
+{ 0xFD7E, 0xFD7E, 0xFD7E },
+{ 0xFD7F, 0xFD7F, 0xFD7F },
+{ 0xFD80, 0xFD80, 0xFD80 },
+{ 0xFD81, 0xFD81, 0xFD81 },
+{ 0xFD82, 0xFD82, 0xFD82 },
+{ 0xFD83, 0xFD83, 0xFD83 },
+{ 0xFD84, 0xFD84, 0xFD84 },
+{ 0xFD85, 0xFD85, 0xFD85 },
+{ 0xFD86, 0xFD86, 0xFD86 },
+{ 0xFD87, 0xFD87, 0xFD87 },
+{ 0xFD88, 0xFD88, 0xFD88 },
+{ 0xFD89, 0xFD89, 0xFD89 },
+{ 0xFD8A, 0xFD8A, 0xFD8A },
+{ 0xFD8B, 0xFD8B, 0xFD8B },
+{ 0xFD8C, 0xFD8C, 0xFD8C },
+{ 0xFD8D, 0xFD8D, 0xFD8D },
+{ 0xFD8E, 0xFD8E, 0xFD8E },
+{ 0xFD8F, 0xFD8F, 0xFD8F },
+{ 0xFD92, 0xFD92, 0xFD92 },
+{ 0xFD93, 0xFD93, 0xFD93 },
+{ 0xFD94, 0xFD94, 0xFD94 },
+{ 0xFD95, 0xFD95, 0xFD95 },
+{ 0xFD96, 0xFD96, 0xFD96 },
+{ 0xFD97, 0xFD97, 0xFD97 },
+{ 0xFD98, 0xFD98, 0xFD98 },
+{ 0xFD99, 0xFD99, 0xFD99 },
+{ 0xFD9A, 0xFD9A, 0xFD9A },
+{ 0xFD9B, 0xFD9B, 0xFD9B },
+{ 0xFD9C, 0xFD9C, 0xFD9C },
+{ 0xFD9D, 0xFD9D, 0xFD9D },
+{ 0xFD9E, 0xFD9E, 0xFD9E },
+{ 0xFD9F, 0xFD9F, 0xFD9F },
+{ 0xFDA0, 0xFDA0, 0xFDA0 },
+{ 0xFDA1, 0xFDA1, 0xFDA1 },
+{ 0xFDA2, 0xFDA2, 0xFDA2 },
+{ 0xFDA3, 0xFDA3, 0xFDA3 },
+{ 0xFDA4, 0xFDA4, 0xFDA4 },
+{ 0xFDA5, 0xFDA5, 0xFDA5 },
+{ 0xFDA6, 0xFDA6, 0xFDA6 },
+{ 0xFDA7, 0xFDA7, 0xFDA7 },
+{ 0xFDA8, 0xFDA8, 0xFDA8 },
+{ 0xFDA9, 0xFDA9, 0xFDA9 },
+{ 0xFDAA, 0xFDAA, 0xFDAA },
+{ 0xFDAB, 0xFDAB, 0xFDAB },
+{ 0xFDAC, 0xFDAC, 0xFDAC },
+{ 0xFDAD, 0xFDAD, 0xFDAD },
+{ 0xFDAE, 0xFDAE, 0xFDAE },
+{ 0xFDAF, 0xFDAF, 0xFDAF },
+{ 0xFDB0, 0xFDB0, 0xFDB0 },
+{ 0xFDB1, 0xFDB1, 0xFDB1 },
+{ 0xFDB2, 0xFDB2, 0xFDB2 },
+{ 0xFDB3, 0xFDB3, 0xFDB3 },
+{ 0xFDB4, 0xFDB4, 0xFDB4 },
+{ 0xFDB5, 0xFDB5, 0xFDB5 },
+{ 0xFDB6, 0xFDB6, 0xFDB6 },
+{ 0xFDB7, 0xFDB7, 0xFDB7 },
+{ 0xFDB8, 0xFDB8, 0xFDB8 },
+{ 0xFDB9, 0xFDB9, 0xFDB9 },
+{ 0xFDBA, 0xFDBA, 0xFDBA },
+{ 0xFDBB, 0xFDBB, 0xFDBB },
+{ 0xFDBC, 0xFDBC, 0xFDBC },
+{ 0xFDBD, 0xFDBD, 0xFDBD },
+{ 0xFDBE, 0xFDBE, 0xFDBE },
+{ 0xFDBF, 0xFDBF, 0xFDBF },
+{ 0xFDC0, 0xFDC0, 0xFDC0 },
+{ 0xFDC1, 0xFDC1, 0xFDC1 },
+{ 0xFDC2, 0xFDC2, 0xFDC2 },
+{ 0xFDC3, 0xFDC3, 0xFDC3 },
+{ 0xFDC4, 0xFDC4, 0xFDC4 },
+{ 0xFDC5, 0xFDC5, 0xFDC5 },
+{ 0xFDC6, 0xFDC6, 0xFDC6 },
+{ 0xFDC7, 0xFDC7, 0xFDC7 },
+{ 0xFDF0, 0xFDF0, 0xFDF0 },
+{ 0xFDF1, 0xFDF1, 0xFDF1 },
+{ 0xFDF2, 0xFDF2, 0xFDF2 },
+{ 0xFDF3, 0xFDF3, 0xFDF3 },
+{ 0xFDF4, 0xFDF4, 0xFDF4 },
+{ 0xFDF5, 0xFDF5, 0xFDF5 },
+{ 0xFDF6, 0xFDF6, 0xFDF6 },
+{ 0xFDF7, 0xFDF7, 0xFDF7 },
+{ 0xFDF8, 0xFDF8, 0xFDF8 },
+{ 0xFDF9, 0xFDF9, 0xFDF9 },
+{ 0xFDFA, 0xFDFA, 0xFDFA },
+{ 0xFDFB, 0xFDFB, 0xFDFB },
+{ 0xFE00, 0xFE00, 0xFE00 },
+{ 0xFE01, 0xFE01, 0xFE01 },
+{ 0xFE02, 0xFE02, 0xFE02 },
+{ 0xFE03, 0xFE03, 0xFE03 },
+{ 0xFE04, 0xFE04, 0xFE04 },
+{ 0xFE05, 0xFE05, 0xFE05 },
+{ 0xFE06, 0xFE06, 0xFE06 },
+{ 0xFE07, 0xFE07, 0xFE07 },
+{ 0xFE08, 0xFE08, 0xFE08 },
+{ 0xFE09, 0xFE09, 0xFE09 },
+{ 0xFE0A, 0xFE0A, 0xFE0A },
+{ 0xFE0B, 0xFE0B, 0xFE0B },
+{ 0xFE0C, 0xFE0C, 0xFE0C },
+{ 0xFE0D, 0xFE0D, 0xFE0D },
+{ 0xFE0E, 0xFE0E, 0xFE0E },
+{ 0xFE0F, 0xFE0F, 0xFE0F },
+{ 0xFE20, 0xFE20, 0xFE20 },
+{ 0xFE21, 0xFE21, 0xFE21 },
+{ 0xFE22, 0xFE22, 0xFE22 },
+{ 0xFE23, 0xFE23, 0xFE23 },
+{ 0xFE70, 0xFE70, 0xFE70 },
+{ 0xFE71, 0xFE71, 0xFE71 },
+{ 0xFE72, 0xFE72, 0xFE72 },
+{ 0xFE73, 0xFE73, 0xFE73 },
+{ 0xFE74, 0xFE74, 0xFE74 },
+{ 0xFE76, 0xFE76, 0xFE76 },
+{ 0xFE77, 0xFE77, 0xFE77 },
+{ 0xFE78, 0xFE78, 0xFE78 },
+{ 0xFE79, 0xFE79, 0xFE79 },
+{ 0xFE7A, 0xFE7A, 0xFE7A },
+{ 0xFE7B, 0xFE7B, 0xFE7B },
+{ 0xFE7C, 0xFE7C, 0xFE7C },
+{ 0xFE7D, 0xFE7D, 0xFE7D },
+{ 0xFE7E, 0xFE7E, 0xFE7E },
+{ 0xFE7F, 0xFE7F, 0xFE7F },
+{ 0xFE80, 0xFE80, 0xFE80 },
+{ 0xFE81, 0xFE81, 0xFE81 },
+{ 0xFE82, 0xFE82, 0xFE82 },
+{ 0xFE83, 0xFE83, 0xFE83 },
+{ 0xFE84, 0xFE84, 0xFE84 },
+{ 0xFE85, 0xFE85, 0xFE85 },
+{ 0xFE86, 0xFE86, 0xFE86 },
+{ 0xFE87, 0xFE87, 0xFE87 },
+{ 0xFE88, 0xFE88, 0xFE88 },
+{ 0xFE89, 0xFE89, 0xFE89 },
+{ 0xFE8A, 0xFE8A, 0xFE8A },
+{ 0xFE8B, 0xFE8B, 0xFE8B },
+{ 0xFE8C, 0xFE8C, 0xFE8C },
+{ 0xFE8D, 0xFE8D, 0xFE8D },
+{ 0xFE8E, 0xFE8E, 0xFE8E },
+{ 0xFE8F, 0xFE8F, 0xFE8F },
+{ 0xFE90, 0xFE90, 0xFE90 },
+{ 0xFE91, 0xFE91, 0xFE91 },
+{ 0xFE92, 0xFE92, 0xFE92 },
+{ 0xFE93, 0xFE93, 0xFE93 },
+{ 0xFE94, 0xFE94, 0xFE94 },
+{ 0xFE95, 0xFE95, 0xFE95 },
+{ 0xFE96, 0xFE96, 0xFE96 },
+{ 0xFE97, 0xFE97, 0xFE97 },
+{ 0xFE98, 0xFE98, 0xFE98 },
+{ 0xFE99, 0xFE99, 0xFE99 },
+{ 0xFE9A, 0xFE9A, 0xFE9A },
+{ 0xFE9B, 0xFE9B, 0xFE9B },
+{ 0xFE9C, 0xFE9C, 0xFE9C },
+{ 0xFE9D, 0xFE9D, 0xFE9D },
+{ 0xFE9E, 0xFE9E, 0xFE9E },
+{ 0xFE9F, 0xFE9F, 0xFE9F },
+{ 0xFEA0, 0xFEA0, 0xFEA0 },
+{ 0xFEA1, 0xFEA1, 0xFEA1 },
+{ 0xFEA2, 0xFEA2, 0xFEA2 },
+{ 0xFEA3, 0xFEA3, 0xFEA3 },
+{ 0xFEA4, 0xFEA4, 0xFEA4 },
+{ 0xFEA5, 0xFEA5, 0xFEA5 },
+{ 0xFEA6, 0xFEA6, 0xFEA6 },
+{ 0xFEA7, 0xFEA7, 0xFEA7 },
+{ 0xFEA8, 0xFEA8, 0xFEA8 },
+{ 0xFEA9, 0xFEA9, 0xFEA9 },
+{ 0xFEAA, 0xFEAA, 0xFEAA },
+{ 0xFEAB, 0xFEAB, 0xFEAB },
+{ 0xFEAC, 0xFEAC, 0xFEAC },
+{ 0xFEAD, 0xFEAD, 0xFEAD },
+{ 0xFEAE, 0xFEAE, 0xFEAE },
+{ 0xFEAF, 0xFEAF, 0xFEAF },
+{ 0xFEB0, 0xFEB0, 0xFEB0 },
+{ 0xFEB1, 0xFEB1, 0xFEB1 },
+{ 0xFEB2, 0xFEB2, 0xFEB2 },
+{ 0xFEB3, 0xFEB3, 0xFEB3 },
+{ 0xFEB4, 0xFEB4, 0xFEB4 },
+{ 0xFEB5, 0xFEB5, 0xFEB5 },
+{ 0xFEB6, 0xFEB6, 0xFEB6 },
+{ 0xFEB7, 0xFEB7, 0xFEB7 },
+{ 0xFEB8, 0xFEB8, 0xFEB8 },
+{ 0xFEB9, 0xFEB9, 0xFEB9 },
+{ 0xFEBA, 0xFEBA, 0xFEBA },
+{ 0xFEBB, 0xFEBB, 0xFEBB },
+{ 0xFEBC, 0xFEBC, 0xFEBC },
+{ 0xFEBD, 0xFEBD, 0xFEBD },
+{ 0xFEBE, 0xFEBE, 0xFEBE },
+{ 0xFEBF, 0xFEBF, 0xFEBF },
+{ 0xFEC0, 0xFEC0, 0xFEC0 },
+{ 0xFEC1, 0xFEC1, 0xFEC1 },
+{ 0xFEC2, 0xFEC2, 0xFEC2 },
+{ 0xFEC3, 0xFEC3, 0xFEC3 },
+{ 0xFEC4, 0xFEC4, 0xFEC4 },
+{ 0xFEC5, 0xFEC5, 0xFEC5 },
+{ 0xFEC6, 0xFEC6, 0xFEC6 },
+{ 0xFEC7, 0xFEC7, 0xFEC7 },
+{ 0xFEC8, 0xFEC8, 0xFEC8 },
+{ 0xFEC9, 0xFEC9, 0xFEC9 },
+{ 0xFECA, 0xFECA, 0xFECA },
+{ 0xFECB, 0xFECB, 0xFECB },
+{ 0xFECC, 0xFECC, 0xFECC },
+{ 0xFECD, 0xFECD, 0xFECD },
+{ 0xFECE, 0xFECE, 0xFECE },
+{ 0xFECF, 0xFECF, 0xFECF },
+{ 0xFED0, 0xFED0, 0xFED0 },
+{ 0xFED1, 0xFED1, 0xFED1 },
+{ 0xFED2, 0xFED2, 0xFED2 },
+{ 0xFED3, 0xFED3, 0xFED3 },
+{ 0xFED4, 0xFED4, 0xFED4 },
+{ 0xFED5, 0xFED5, 0xFED5 },
+{ 0xFED6, 0xFED6, 0xFED6 },
+{ 0xFED7, 0xFED7, 0xFED7 },
+{ 0xFED8, 0xFED8, 0xFED8 },
+{ 0xFED9, 0xFED9, 0xFED9 },
+{ 0xFEDA, 0xFEDA, 0xFEDA },
+{ 0xFEDB, 0xFEDB, 0xFEDB },
+{ 0xFEDC, 0xFEDC, 0xFEDC },
+{ 0xFEDD, 0xFEDD, 0xFEDD },
+{ 0xFEDE, 0xFEDE, 0xFEDE },
+{ 0xFEDF, 0xFEDF, 0xFEDF },
+{ 0xFEE0, 0xFEE0, 0xFEE0 },
+{ 0xFEE1, 0xFEE1, 0xFEE1 },
+{ 0xFEE2, 0xFEE2, 0xFEE2 },
+{ 0xFEE3, 0xFEE3, 0xFEE3 },
+{ 0xFEE4, 0xFEE4, 0xFEE4 },
+{ 0xFEE5, 0xFEE5, 0xFEE5 },
+{ 0xFEE6, 0xFEE6, 0xFEE6 },
+{ 0xFEE7, 0xFEE7, 0xFEE7 },
+{ 0xFEE8, 0xFEE8, 0xFEE8 },
+{ 0xFEE9, 0xFEE9, 0xFEE9 },
+{ 0xFEEA, 0xFEEA, 0xFEEA },
+{ 0xFEEB, 0xFEEB, 0xFEEB },
+{ 0xFEEC, 0xFEEC, 0xFEEC },
+{ 0xFEED, 0xFEED, 0xFEED },
+{ 0xFEEE, 0xFEEE, 0xFEEE },
+{ 0xFEEF, 0xFEEF, 0xFEEF },
+{ 0xFEF0, 0xFEF0, 0xFEF0 },
+{ 0xFEF1, 0xFEF1, 0xFEF1 },
+{ 0xFEF2, 0xFEF2, 0xFEF2 },
+{ 0xFEF3, 0xFEF3, 0xFEF3 },
+{ 0xFEF4, 0xFEF4, 0xFEF4 },
+{ 0xFEF5, 0xFEF5, 0xFEF5 },
+{ 0xFEF6, 0xFEF6, 0xFEF6 },
+{ 0xFEF7, 0xFEF7, 0xFEF7 },
+{ 0xFEF8, 0xFEF8, 0xFEF8 },
+{ 0xFEF9, 0xFEF9, 0xFEF9 },
+{ 0xFEFA, 0xFEFA, 0xFEFA },
+{ 0xFEFB, 0xFEFB, 0xFEFB },
+{ 0xFEFC, 0xFEFC, 0xFEFC },
+{ 0xFF21, 0xFF21, 0xFF41 },
+{ 0xFF22, 0xFF22, 0xFF42 },
+{ 0xFF23, 0xFF23, 0xFF43 },
+{ 0xFF24, 0xFF24, 0xFF44 },
+{ 0xFF25, 0xFF25, 0xFF45 },
+{ 0xFF26, 0xFF26, 0xFF46 },
+{ 0xFF27, 0xFF27, 0xFF47 },
+{ 0xFF28, 0xFF28, 0xFF48 },
+{ 0xFF29, 0xFF29, 0xFF49 },
+{ 0xFF2A, 0xFF2A, 0xFF4A },
+{ 0xFF2B, 0xFF2B, 0xFF4B },
+{ 0xFF2C, 0xFF2C, 0xFF4C },
+{ 0xFF2D, 0xFF2D, 0xFF4D },
+{ 0xFF2E, 0xFF2E, 0xFF4E },
+{ 0xFF2F, 0xFF2F, 0xFF4F },
+{ 0xFF30, 0xFF30, 0xFF50 },
+{ 0xFF31, 0xFF31, 0xFF51 },
+{ 0xFF32, 0xFF32, 0xFF52 },
+{ 0xFF33, 0xFF33, 0xFF53 },
+{ 0xFF34, 0xFF34, 0xFF54 },
+{ 0xFF35, 0xFF35, 0xFF55 },
+{ 0xFF36, 0xFF36, 0xFF56 },
+{ 0xFF37, 0xFF37, 0xFF57 },
+{ 0xFF38, 0xFF38, 0xFF58 },
+{ 0xFF39, 0xFF39, 0xFF59 },
+{ 0xFF3A, 0xFF3A, 0xFF5A },
+{ 0xFF41, 0xFF21, 0xFF41 },
+{ 0xFF42, 0xFF22, 0xFF42 },
+{ 0xFF43, 0xFF23, 0xFF43 },
+{ 0xFF44, 0xFF24, 0xFF44 },
+{ 0xFF45, 0xFF25, 0xFF45 },
+{ 0xFF46, 0xFF26, 0xFF46 },
+{ 0xFF47, 0xFF27, 0xFF47 },
+{ 0xFF48, 0xFF28, 0xFF48 },
+{ 0xFF49, 0xFF29, 0xFF49 },
+{ 0xFF4A, 0xFF2A, 0xFF4A },
+{ 0xFF4B, 0xFF2B, 0xFF4B },
+{ 0xFF4C, 0xFF2C, 0xFF4C },
+{ 0xFF4D, 0xFF2D, 0xFF4D },
+{ 0xFF4E, 0xFF2E, 0xFF4E },
+{ 0xFF4F, 0xFF2F, 0xFF4F },
+{ 0xFF50, 0xFF30, 0xFF50 },
+{ 0xFF51, 0xFF31, 0xFF51 },
+{ 0xFF52, 0xFF32, 0xFF52 },
+{ 0xFF53, 0xFF33, 0xFF53 },
+{ 0xFF54, 0xFF34, 0xFF54 },
+{ 0xFF55, 0xFF35, 0xFF55 },
+{ 0xFF56, 0xFF36, 0xFF56 },
+{ 0xFF57, 0xFF37, 0xFF57 },
+{ 0xFF58, 0xFF38, 0xFF58 },
+{ 0xFF59, 0xFF39, 0xFF59 },
+{ 0xFF5A, 0xFF3A, 0xFF5A },
+{ 0xFF66, 0xFF66, 0xFF66 },
+{ 0xFF67, 0xFF67, 0xFF67 },
+{ 0xFF68, 0xFF68, 0xFF68 },
+{ 0xFF69, 0xFF69, 0xFF69 },
+{ 0xFF6A, 0xFF6A, 0xFF6A },
+{ 0xFF6B, 0xFF6B, 0xFF6B },
+{ 0xFF6C, 0xFF6C, 0xFF6C },
+{ 0xFF6D, 0xFF6D, 0xFF6D },
+{ 0xFF6E, 0xFF6E, 0xFF6E },
+{ 0xFF6F, 0xFF6F, 0xFF6F },
+{ 0xFF70, 0xFF70, 0xFF70 },
+{ 0xFF71, 0xFF71, 0xFF71 },
+{ 0xFF72, 0xFF72, 0xFF72 },
+{ 0xFF73, 0xFF73, 0xFF73 },
+{ 0xFF74, 0xFF74, 0xFF74 },
+{ 0xFF75, 0xFF75, 0xFF75 },
+{ 0xFF76, 0xFF76, 0xFF76 },
+{ 0xFF77, 0xFF77, 0xFF77 },
+{ 0xFF78, 0xFF78, 0xFF78 },
+{ 0xFF79, 0xFF79, 0xFF79 },
+{ 0xFF7A, 0xFF7A, 0xFF7A },
+{ 0xFF7B, 0xFF7B, 0xFF7B },
+{ 0xFF7C, 0xFF7C, 0xFF7C },
+{ 0xFF7D, 0xFF7D, 0xFF7D },
+{ 0xFF7E, 0xFF7E, 0xFF7E },
+{ 0xFF7F, 0xFF7F, 0xFF7F },
+{ 0xFF80, 0xFF80, 0xFF80 },
+{ 0xFF81, 0xFF81, 0xFF81 },
+{ 0xFF82, 0xFF82, 0xFF82 },
+{ 0xFF83, 0xFF83, 0xFF83 },
+{ 0xFF84, 0xFF84, 0xFF84 },
+{ 0xFF85, 0xFF85, 0xFF85 },
+{ 0xFF86, 0xFF86, 0xFF86 },
+{ 0xFF87, 0xFF87, 0xFF87 },
+{ 0xFF88, 0xFF88, 0xFF88 },
+{ 0xFF89, 0xFF89, 0xFF89 },
+{ 0xFF8A, 0xFF8A, 0xFF8A },
+{ 0xFF8B, 0xFF8B, 0xFF8B },
+{ 0xFF8C, 0xFF8C, 0xFF8C },
+{ 0xFF8D, 0xFF8D, 0xFF8D },
+{ 0xFF8E, 0xFF8E, 0xFF8E },
+{ 0xFF8F, 0xFF8F, 0xFF8F },
+{ 0xFF90, 0xFF90, 0xFF90 },
+{ 0xFF91, 0xFF91, 0xFF91 },
+{ 0xFF92, 0xFF92, 0xFF92 },
+{ 0xFF93, 0xFF93, 0xFF93 },
+{ 0xFF94, 0xFF94, 0xFF94 },
+{ 0xFF95, 0xFF95, 0xFF95 },
+{ 0xFF96, 0xFF96, 0xFF96 },
+{ 0xFF97, 0xFF97, 0xFF97 },
+{ 0xFF98, 0xFF98, 0xFF98 },
+{ 0xFF99, 0xFF99, 0xFF99 },
+{ 0xFF9A, 0xFF9A, 0xFF9A },
+{ 0xFF9B, 0xFF9B, 0xFF9B },
+{ 0xFF9C, 0xFF9C, 0xFF9C },
+{ 0xFF9D, 0xFF9D, 0xFF9D },
+{ 0xFF9E, 0xFF9E, 0xFF9E },
+{ 0xFF9F, 0xFF9F, 0xFF9F },
+{ 0xFFA0, 0xFFA0, 0xFFA0 },
+{ 0xFFA1, 0xFFA1, 0xFFA1 },
+{ 0xFFA2, 0xFFA2, 0xFFA2 },
+{ 0xFFA3, 0xFFA3, 0xFFA3 },
+{ 0xFFA4, 0xFFA4, 0xFFA4 },
+{ 0xFFA5, 0xFFA5, 0xFFA5 },
+{ 0xFFA6, 0xFFA6, 0xFFA6 },
+{ 0xFFA7, 0xFFA7, 0xFFA7 },
+{ 0xFFA8, 0xFFA8, 0xFFA8 },
+{ 0xFFA9, 0xFFA9, 0xFFA9 },
+{ 0xFFAA, 0xFFAA, 0xFFAA },
+{ 0xFFAB, 0xFFAB, 0xFFAB },
+{ 0xFFAC, 0xFFAC, 0xFFAC },
+{ 0xFFAD, 0xFFAD, 0xFFAD },
+{ 0xFFAE, 0xFFAE, 0xFFAE },
+{ 0xFFAF, 0xFFAF, 0xFFAF },
+{ 0xFFB0, 0xFFB0, 0xFFB0 },
+{ 0xFFB1, 0xFFB1, 0xFFB1 },
+{ 0xFFB2, 0xFFB2, 0xFFB2 },
+{ 0xFFB3, 0xFFB3, 0xFFB3 },
+{ 0xFFB4, 0xFFB4, 0xFFB4 },
+{ 0xFFB5, 0xFFB5, 0xFFB5 },
+{ 0xFFB6, 0xFFB6, 0xFFB6 },
+{ 0xFFB7, 0xFFB7, 0xFFB7 },
+{ 0xFFB8, 0xFFB8, 0xFFB8 },
+{ 0xFFB9, 0xFFB9, 0xFFB9 },
+{ 0xFFBA, 0xFFBA, 0xFFBA },
+{ 0xFFBB, 0xFFBB, 0xFFBB },
+{ 0xFFBC, 0xFFBC, 0xFFBC },
+{ 0xFFBD, 0xFFBD, 0xFFBD },
+{ 0xFFBE, 0xFFBE, 0xFFBE },
+{ 0xFFC2, 0xFFC2, 0xFFC2 },
+{ 0xFFC3, 0xFFC3, 0xFFC3 },
+{ 0xFFC4, 0xFFC4, 0xFFC4 },
+{ 0xFFC5, 0xFFC5, 0xFFC5 },
+{ 0xFFC6, 0xFFC6, 0xFFC6 },
+{ 0xFFC7, 0xFFC7, 0xFFC7 },
+{ 0xFFCA, 0xFFCA, 0xFFCA },
+{ 0xFFCB, 0xFFCB, 0xFFCB },
+{ 0xFFCC, 0xFFCC, 0xFFCC },
+{ 0xFFCD, 0xFFCD, 0xFFCD },
+{ 0xFFCE, 0xFFCE, 0xFFCE },
+{ 0xFFCF, 0xFFCF, 0xFFCF },
+{ 0xFFD2, 0xFFD2, 0xFFD2 },
+{ 0xFFD3, 0xFFD3, 0xFFD3 },
+{ 0xFFD4, 0xFFD4, 0xFFD4 },
+{ 0xFFD5, 0xFFD5, 0xFFD5 },
+{ 0xFFD6, 0xFFD6, 0xFFD6 },
+{ 0xFFD7, 0xFFD7, 0xFFD7 },
+{ 0xFFDA, 0xFFDA, 0xFFDA },
+{ 0xFFDB, 0xFFDB, 0xFFDB },
+{ 0xFFDC, 0xFFDC, 0xFFDC }
+};
diff --git a/Plugins/spellchecker/hunspell/w_char.hxx b/Plugins/spellchecker/hunspell/w_char.hxx
new file mode 100644
index 0000000..3719dd3
--- /dev/null
+++ b/Plugins/spellchecker/hunspell/w_char.hxx
@@ -0,0 +1,21 @@
+#ifndef __WCHARHXX__
+#define __WCHARHXX__
+
+#ifndef GCC
+typedef struct {
+#else
+typedef struct __attribute__ ((packed)) {
+#endif
+ unsigned char l;
+ unsigned char h;
+} w_char;
+
+// two character arrays
+struct replentry {
+ char * pattern;
+ char * pattern2;
+ bool start;
+ bool end;
+};
+
+#endif
diff --git a/Plugins/spellchecker/m_spellchecker.h b/Plugins/spellchecker/m_spellchecker.h
new file mode 100644
index 0000000..c7b5d3c
--- /dev/null
+++ b/Plugins/spellchecker/m_spellchecker.h
@@ -0,0 +1,77 @@
+/*
+Copyright (C) 2006-2010 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#ifndef __M_SPELLCHECKER_H__
+# define __M_SPELLCHECKER_H__
+
+
+#define MIID_SPELLCHECKER { 0x26eed12a, 0x7016, 0x4d0f, { 0x9b, 0x4a, 0xc, 0xaa, 0x7e, 0x22, 0x29, 0xf3 } }
+
+
+
+typedef struct {
+ int cbSize;
+ HANDLE hContact; // The contact to get the settings from, or NULL
+ HWND hwnd; // The hwnd of the richedit
+ char *window_name; // A name for this richedit
+} SPELLCHECKER_ITEM;
+
+typedef struct {
+ int cbSize;
+ HWND hwnd; // The hwnd of the richedit
+ HMENU hMenu; // The handle to the menu
+ POINT pt; // The point, in screen coords
+ HWND hwndOwner; // The hwnd of owner of the popup menu. If it is null, hwnd is used
+} SPELLCHECKER_POPUPMENU;
+
+
+/*
+Adds a richedit control for the spell checker to check
+
+wParam: SPELLCHECKER_ITEM *
+lParam: ignored
+return: 0 on success
+*/
+#define MS_SPELLCHECKER_ADD_RICHEDIT "SpellChecker/AddRichedit"
+
+
+/*
+Removes a richedit control for the spell checker to check
+
+wParam: HWND
+lParam: ignored
+return: 0 on success
+*/
+#define MS_SPELLCHECKER_REMOVE_RICHEDIT "SpellChecker/RemoveRichedit"
+
+
+/*
+Show context menu
+
+wParam: SPELLCHECKER_POPUPMENU
+lParam: ignored
+return: the control id selected by the user, 0 if no one was selected, < 0 on error
+*/
+#define MS_SPELLCHECKER_SHOW_POPUP_MENU "SpellChecker/ShowPopupMenu"
+
+
+
+
+#endif // __M_SPELLCHECKER_H__
diff --git a/Plugins/spellchecker/options.cpp b/Plugins/spellchecker/options.cpp
new file mode 100644
index 0000000..cf9f6c3
--- /dev/null
+++ b/Plugins/spellchecker/options.cpp
@@ -0,0 +1,610 @@
+/*
+Copyright (C) 2006-2010 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#include "commons.h"
+
+
+
+// Prototypes /////////////////////////////////////////////////////////////////////////////////////
+
+HANDLE hOptHook = NULL;
+
+Options opts;
+
+
+static INT_PTR CALLBACK OptionsDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam);
+static INT_PTR CALLBACK AutoreplaceDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam);
+
+
+static OptPageControl optionsControls[] = {
+ { &opts.auto_replace_dict, CONTROL_CHECKBOX, IDC_AUTO_DICT, "AutoReplaceDict", FALSE },
+ { &opts.ignore_with_numbers, CONTROL_CHECKBOX, IDC_IGNORE_NUMBERS, "IgnoreWithNumbers", FALSE },
+ { &opts.ignore_uppercase, CONTROL_CHECKBOX, IDC_IGNORE_UPPERCASE, "IgnoreUppercase", FALSE },
+ { &opts.ask_when_sending_with_error, CONTROL_CHECKBOX, IDC_ASK_ON_ERROR, "AskWhenSendingWithError", FALSE },
+ { &opts.underline_type, CONTROL_COMBO, IDC_UNDERLINE_TYPE, "UnderlineType", CFU_UNDERLINEWAVE - CFU_UNDERLINEDOUBLE },
+ { &opts.cascade_corrections, CONTROL_CHECKBOX, IDC_CASCADE_CORRECTIONS, "CascadeCorrections", FALSE },
+ { &opts.show_all_corrections, CONTROL_CHECKBOX, IDC_SHOW_ALL_CORRECTIONS, "ShowAllCorrections", FALSE },
+ { &opts.show_wrong_word, CONTROL_CHECKBOX, IDC_SHOW_WRONG_WORD, "ShowWrongWord", TRUE },
+ { &opts.use_flags, CONTROL_CHECKBOX, IDC_USE_FLAGS, "UseFlags", TRUE },
+ { &opts.auto_locale, CONTROL_CHECKBOX, IDC_AUTO_LOCALE, "AutoLocale", FALSE },
+ { &opts.use_other_apps_dicts, CONTROL_CHECKBOX, IDC_OTHER_PROGS, "UseOtherAppsDicts", TRUE },
+ { &opts.handle_underscore, CONTROL_CHECKBOX, IDC_HANDLE_UNDERSCORE, "HandleUnderscore", FALSE },
+};
+
+static UINT optionsExpertControls[] = {
+ IDC_ADVANCED, IDC_UNDERLINE_TYPE_L, IDC_UNDERLINE_TYPE, IDC_CASCADE_CORRECTIONS, IDC_SHOW_ALL_CORRECTIONS,
+ IDC_SHOW_WRONG_WORD, IDC_USE_FLAGS, IDC_AUTO_LOCALE, IDC_OTHER_PROGS, IDC_HANDLE_UNDERSCORE
+};
+
+
+static OptPageControl autoReplaceControls[] = {
+ { &opts.auto_replace_user, CONTROL_CHECKBOX, IDC_AUTO_USER, "AutoReplaceUser", TRUE },
+};
+
+
+// Functions //////////////////////////////////////////////////////////////////////////////////////
+
+
+int InitOptionsCallback(WPARAM wParam,LPARAM lParam)
+{
+ OPTIONSDIALOGPAGE odp;
+
+ ZeroMemory(&odp,sizeof(odp));
+ odp.cbSize=sizeof(odp);
+ odp.position=0;
+ odp.hInstance=hInst;
+ odp.ptszGroup = LPGENT("Message Sessions");
+ odp.ptszTitle = LPGENT("Spell Checker");
+ odp.pfnDlgProc = OptionsDlgProc;
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPTIONS);
+ odp.flags = ODPF_BOLDGROUPS | ODPF_TCHAR;
+ odp.nIDBottomSimpleControl = IDC_SPELL_CHECKER;
+ odp.expertOnlyControls = optionsExpertControls;
+ odp.nExpertOnlyControls = MAX_REGS(optionsExpertControls);
+ CallService(MS_OPT_ADDPAGE,wParam,(LPARAM)&odp);
+
+ ZeroMemory(&odp,sizeof(odp));
+ odp.cbSize=sizeof(odp);
+ odp.position=0;
+ odp.hInstance=hInst;
+ odp.ptszGroup = LPGENT("Message Sessions");
+ odp.ptszTitle = LPGENT("Auto-replacements");
+ odp.pfnDlgProc = AutoreplaceDlgProc;
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_REPLACEMENTS);
+ odp.flags = ODPF_BOLDGROUPS | ODPF_TCHAR;
+ CallService(MS_OPT_ADDPAGE,wParam,(LPARAM)&odp);
+
+ return 0;
+}
+
+
+void InitOptions()
+{
+ LoadOptions();
+
+ hOptHook = HookEvent(ME_OPT_INITIALISE, InitOptionsCallback);
+}
+
+
+void DeInitOptions()
+{
+ UnhookEvent(hOptHook);
+}
+
+
+void LoadOptions()
+{
+ LoadOpts(optionsControls, MAX_REGS(optionsControls), MODULE_NAME);
+ LoadOpts(autoReplaceControls, MAX_REGS(autoReplaceControls), MODULE_NAME);
+
+ if (languages.getCount() <= 0)
+ {
+ opts.default_language[0] = _T('\0');
+ return;
+ }
+
+ DBVARIANT dbv;
+ if (!DBGetContactSettingTString(NULL, MODULE_NAME, "DefaultLanguage", &dbv))
+ {
+ lstrcpyn(opts.default_language, dbv.ptszVal, MAX_REGS(opts.default_language));
+ DBFreeVariant(&dbv);
+ }
+
+ int i;
+ for(i = 0; i < languages.getCount(); i++)
+ if (lstrcmp(languages[i]->language, opts.default_language) == 0)
+ break;
+
+ if (i >= languages.getCount())
+ lstrcpy(opts.default_language, languages[0]->language);
+}
+
+
+static void DrawItem(HWND hwndDlg, LPDRAWITEMSTRUCT lpdis, Dictionary *dict)
+{
+ TEXTMETRIC tm;
+ RECT rc;
+
+ GetTextMetrics(lpdis->hDC, &tm);
+
+
+ int foreIndex, backIndex;
+
+ if (lpdis->itemState & ODS_DISABLED)
+ {
+ foreIndex = COLOR_GRAYTEXT;
+ backIndex = COLOR_BTNFACE;
+ }
+ else if (lpdis->itemState & ODS_SELECTED)
+ {
+ foreIndex = COLOR_HIGHLIGHTTEXT;
+ backIndex = COLOR_HIGHLIGHT;
+ }
+ else
+ {
+ foreIndex = COLOR_WINDOWTEXT;
+ backIndex = COLOR_WINDOW;
+ }
+
+ COLORREF clrfore = SetTextColor(lpdis->hDC,GetSysColor(foreIndex));
+ COLORREF clrback = SetBkColor(lpdis->hDC,GetSysColor(backIndex));
+
+ FillRect(lpdis->hDC, &lpdis->rcItem, GetSysColorBrush(backIndex));
+
+ rc.left = lpdis->rcItem.left + 2;
+
+ // Draw icon
+ if (opts.use_flags)
+ {
+ HICON hFlag = IcoLib_LoadIcon(dict);
+
+ rc.top = (lpdis->rcItem.bottom + lpdis->rcItem.top - ICON_SIZE) / 2;
+ DrawIconEx(lpdis->hDC, rc.left, rc.top, hFlag, 16, 16, 0, NULL, DI_NORMAL);
+
+ rc.left += ICON_SIZE + 4;
+
+ IcoLib_ReleaseIcon(hFlag);
+ }
+
+ // Draw text
+ rc.right = lpdis->rcItem.right - 2;
+ rc.top = (lpdis->rcItem.bottom + lpdis->rcItem.top - tm.tmHeight) / 2;
+ rc.bottom = rc.top + tm.tmHeight;
+ DrawText(lpdis->hDC, dict->full_name, lstrlen(dict->full_name), &rc, DT_END_ELLIPSIS | DT_NOPREFIX | DT_SINGLELINE);
+
+ // Restore old colors
+ SetTextColor(lpdis->hDC, clrfore);
+ SetBkColor(lpdis->hDC, clrback);
+}
+
+
+static void MeasureItem(HWND hwndDlg, LPMEASUREITEMSTRUCT lpmis)
+{
+ TEXTMETRIC tm;
+ GetTextMetrics(GetDC(hwndDlg), &tm);
+
+ if (opts.use_flags)
+ lpmis->itemHeight = max(ICON_SIZE, tm.tmHeight);
+ else
+ lpmis->itemHeight = tm.tmHeight;
+}
+
+
+static INT_PTR CALLBACK OptionsDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg)
+ {
+ case WM_INITDIALOG:
+ {
+ int i, sel = -1;
+ for(i = 0; i < languages.getCount(); i++)
+ {
+ SendDlgItemMessage(hwndDlg, IDC_DEF_LANG, CB_ADDSTRING, 0, (LONG) languages[i]->full_name);
+ SendDlgItemMessage(hwndDlg, IDC_DEF_LANG, CB_SETITEMDATA, i, (DWORD) languages[i]);
+
+ if (lstrcmp(opts.default_language, languages[i]->language) == 0)
+ sel = i;
+ }
+ SendDlgItemMessage(hwndDlg, IDC_DEF_LANG, CB_SETCURSEL, sel, 0);
+
+ SendDlgItemMessage(hwndDlg, IDC_UNDERLINE_TYPE, CB_ADDSTRING, 0, (LONG) TranslateT("Line"));
+ SendDlgItemMessage(hwndDlg, IDC_UNDERLINE_TYPE, CB_ADDSTRING, 0, (LONG) TranslateT("Dotted"));
+ SendDlgItemMessage(hwndDlg, IDC_UNDERLINE_TYPE, CB_ADDSTRING, 0, (LONG) TranslateT("Dash"));
+ SendDlgItemMessage(hwndDlg, IDC_UNDERLINE_TYPE, CB_ADDSTRING, 0, (LONG) TranslateT("Dash dot"));
+ SendDlgItemMessage(hwndDlg, IDC_UNDERLINE_TYPE, CB_ADDSTRING, 0, (LONG) TranslateT("Dash dot dot"));
+ SendDlgItemMessage(hwndDlg, IDC_UNDERLINE_TYPE, CB_ADDSTRING, 0, (LONG) TranslateT("Wave"));
+ SendDlgItemMessage(hwndDlg, IDC_UNDERLINE_TYPE, CB_ADDSTRING, 0, (LONG) TranslateT("Thick"));
+
+ break;
+ }
+
+ case WM_COMMAND:
+ {
+ if(LOWORD(wParam) == IDC_GETMORE)
+ CallService(MS_UTILS_OPENURL, 1, (LPARAM) "http://wiki.services.openoffice.org/wiki/Dictionaries");
+
+ if (LOWORD(wParam) == IDC_DEF_LANG
+ && (HIWORD(wParam) == CBN_SELCHANGE && (HWND)lParam == GetFocus()))
+ {
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ return 0;
+ }
+
+ break;
+ }
+
+ case WM_NOTIFY:
+ {
+ LPNMHDR lpnmhdr = (LPNMHDR)lParam;
+
+ if (lpnmhdr->idFrom == 0 && lpnmhdr->code == PSN_APPLY && languages.getCount() > 0)
+ {
+ int sel = SendDlgItemMessage(hwndDlg, IDC_DEF_LANG, CB_GETCURSEL, 0, 0);
+ if (sel >= languages.getCount())
+ sel = 0;
+ DBWriteContactSettingTString(NULL, MODULE_NAME, "DefaultLanguage",
+ (TCHAR *) languages[sel]->language);
+ lstrcpy(opts.default_language, languages[sel]->language);
+ }
+
+ break;
+ }
+
+ case WM_DRAWITEM:
+ {
+ LPDRAWITEMSTRUCT lpdis = (LPDRAWITEMSTRUCT)lParam;
+ if(lpdis->CtlID != IDC_DEF_LANG)
+ break;
+ if(lpdis->itemID == -1)
+ break;
+
+ Dictionary *dict = (Dictionary *) lpdis->itemData;
+
+ DrawItem(hwndDlg, lpdis, dict);
+
+ return TRUE;
+ }
+
+ case WM_MEASUREITEM:
+ {
+ LPMEASUREITEMSTRUCT lpmis = (LPMEASUREITEMSTRUCT)lParam;
+ if(lpmis->CtlID != IDC_DEF_LANG)
+ break;
+
+ MeasureItem(hwndDlg, lpmis);
+
+
+ return TRUE;
+ }
+ }
+
+ return SaveOptsDlgProc(optionsControls, MAX_REGS(optionsControls), MODULE_NAME, hwndDlg, msg, wParam, lParam);
+}
+
+
+struct AutoreplaceData
+{
+ Dictionary *dict;
+ map<tstring, AutoReplacement> autoReplaceMap;
+ BOOL initialized;
+ BOOL changed;
+
+ AutoreplaceData(Dictionary *dict) : dict(dict), initialized(FALSE), changed(FALSE) {}
+
+ void RemoveWord(const TCHAR *aWord)
+ {
+ map<tstring,AutoReplacement>::iterator it = autoReplaceMap.find(aWord);
+ if (it != autoReplaceMap.end())
+ autoReplaceMap.erase(it);
+ changed = TRUE;
+ }
+
+ void AddWord(const TCHAR *find, const TCHAR *replace, BOOL useVars)
+ {
+ autoReplaceMap[find] = AutoReplacement(replace, useVars);
+ changed = TRUE;
+ }
+};
+
+
+static void EnableDisableCtrls(HWND hwndDlg)
+{
+ BOOL enabled = IsDlgButtonChecked(hwndDlg, IDC_AUTO_USER);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_LANGUAGE_L), enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_LANGUAGE), enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_REPLACEMENTS), enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_ADD), enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_EDIT), enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_REMOVE), enabled);
+ if (!enabled)
+ return;
+
+ HWND hList = GetDlgItem(hwndDlg, IDC_REPLACEMENTS);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_EDIT), ListView_GetSelectedCount(hList) == 1);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_REMOVE), ListView_GetSelectedCount(hList) > 0);
+
+
+}
+
+
+static void LoadReplacements(HWND hwndDlg)
+{
+ HWND hList = GetDlgItem(hwndDlg, IDC_REPLACEMENTS);
+
+ ListView_DeleteAllItems(hList);
+ ListView_SetItemCount(hList, 0);
+
+ int sel = SendDlgItemMessage(hwndDlg, IDC_LANGUAGE, CB_GETCURSEL, 0, 0);
+ if (sel < 0)
+ return;
+
+ AutoreplaceData *data = (AutoreplaceData *) SendDlgItemMessage(hwndDlg, IDC_LANGUAGE, CB_GETITEMDATA, sel, 0);
+ if (!data->initialized)
+ {
+ data->dict->autoReplace->copyMap(&data->autoReplaceMap);
+ data->initialized = TRUE;
+ }
+
+ map<tstring,AutoReplacement>::iterator it = data->autoReplaceMap.begin();
+ for(int i = 0; it != data->autoReplaceMap.end(); it++, i++)
+ {
+ LVITEM item = {0};
+ item.mask = LVIF_TEXT | LVIF_PARAM;
+ item.iItem = i;
+ item.iSubItem = 0;
+ item.pszText = (TCHAR *) it->first.c_str();
+ item.cchTextMax = it->first.length();
+ item.lParam = i;
+
+ ListView_InsertItem(hList, &item);
+
+ ListView_SetItemText(hList, i, 1, (TCHAR *) it->second.replace.c_str());
+ }
+
+ EnableDisableCtrls(hwndDlg);
+}
+
+
+static void SaveNewReplacements(BOOL canceled, Dictionary *dict,
+ const TCHAR *find, const TCHAR *replace, BOOL useVariables,
+ const TCHAR *original_find, void *param)
+{
+ if (canceled)
+ return;
+
+ AutoreplaceData *data = (AutoreplaceData *) param;
+
+ if (lstrlen(original_find) > 0)
+ data->RemoveWord(original_find);
+
+ data->AddWord(find, replace, useVariables);
+}
+
+
+static void ShowAddReplacement(HWND hwndDlg, int item = -1)
+{
+ int sel = SendDlgItemMessage(hwndDlg, IDC_LANGUAGE, CB_GETCURSEL, 0, 0);
+ if (sel < 0)
+ return;
+
+ AutoreplaceData *data = (AutoreplaceData *) SendDlgItemMessage(hwndDlg, IDC_LANGUAGE, CB_GETITEMDATA, sel, 0);
+
+ TCHAR find[256];
+ const TCHAR *replace = NULL;
+ BOOL useVariables = FALSE;
+
+ if (item < 0)
+ {
+ find[0] = 0;
+ }
+ else
+ {
+ ListView_GetItemText(GetDlgItem(hwndDlg, IDC_REPLACEMENTS), item, 0, find, MAX_REGS(find));
+ }
+
+ if (lstrlen(find) > 0)
+ {
+ AutoReplacement &ar = data->autoReplaceMap[find];
+ replace = ar.replace.c_str();
+ useVariables = ar.useVariables;
+ }
+
+ if (ShowAutoReplaceDialog(hwndDlg, TRUE, data->dict, find, replace, useVariables,
+ FALSE, &SaveNewReplacements, data))
+ {
+ LoadReplacements(hwndDlg);
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ }
+
+ EnableDisableCtrls(hwndDlg);
+}
+
+
+static INT_PTR CALLBACK AutoreplaceDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg)
+ {
+ case WM_INITDIALOG:
+ {
+ BOOL ret = SaveOptsDlgProc(autoReplaceControls, MAX_REGS(autoReplaceControls), MODULE_NAME, hwndDlg, msg, wParam, lParam);
+
+ int sel = -1;
+ for(int i = 0; i < languages.getCount(); i++)
+ {
+ SendDlgItemMessage(hwndDlg, IDC_LANGUAGE, CB_ADDSTRING, 0, (LONG) languages[i]->full_name);
+ SendDlgItemMessage(hwndDlg, IDC_LANGUAGE, CB_SETITEMDATA, i, (DWORD) new AutoreplaceData(languages[i]));
+
+ if (lstrcmp(opts.default_language, languages[i]->language) == 0)
+ sel = i;
+ }
+ SendDlgItemMessage(hwndDlg, IDC_LANGUAGE, CB_SETCURSEL, sel, 0);
+
+ HWND hList = GetDlgItem(hwndDlg, IDC_REPLACEMENTS);
+
+ ListView_SetExtendedListViewStyle(hList, ListView_GetExtendedListViewStyle(hList) | LVS_EX_FULLROWSELECT);
+
+ LVCOLUMN col = {0};
+ col.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT ;
+ col.fmt = LVCFMT_LEFT;
+ col.cx = 175;
+ col.pszText = TranslateT("Wrong word");
+ col.cchTextMax = lstrlen(col.pszText);
+ ListView_InsertColumn(hList, 0, &col);
+
+ col.pszText = TranslateT("Correction");
+ col.cchTextMax = lstrlen(col.pszText);
+ ListView_InsertColumn(hList, 1, &col);
+
+ LoadReplacements(hwndDlg);
+
+ return ret;
+ }
+
+ case WM_COMMAND:
+ {
+ if (LOWORD(wParam) == IDC_LANGUAGE && HIWORD(wParam) == CBN_SELCHANGE)
+ {
+ LoadReplacements(hwndDlg);
+ }
+ else if (LOWORD(wParam) == IDC_ADD)
+ {
+ ShowAddReplacement(hwndDlg);
+ }
+ else if (LOWORD(wParam) == IDC_EDIT)
+ {
+ HWND hList = GetDlgItem(hwndDlg, IDC_REPLACEMENTS);
+ if (ListView_GetSelectedCount(hList) != 1)
+ break;
+
+ int sel = SendMessage(hList, LVM_GETNEXTITEM, -1, LVNI_SELECTED);
+ if (sel < 0)
+ break;
+
+ ShowAddReplacement(hwndDlg, sel);
+ }
+ else if (LOWORD(wParam) == IDC_REMOVE)
+ {
+ HWND hList = GetDlgItem(hwndDlg, IDC_REPLACEMENTS);
+
+ int sel = SendDlgItemMessage(hwndDlg, IDC_LANGUAGE, CB_GETCURSEL, 0, 0);
+ if (sel < 0)
+ break;
+
+ AutoreplaceData *data = (AutoreplaceData *) SendDlgItemMessage(hwndDlg, IDC_LANGUAGE, CB_GETITEMDATA, sel, 0);
+
+ BOOL changed = FALSE;
+
+ sel = SendMessage(hList, LVM_GETNEXTITEM, -1, LVNI_SELECTED);
+ while(sel >= 0)
+ {
+ TCHAR tmp[256];
+ ListView_GetItemText(hList, sel, 0, tmp, MAX_REGS(tmp));
+
+ data->RemoveWord(tmp);
+ changed = TRUE;
+
+ sel = SendMessage(hList, LVM_GETNEXTITEM, sel, LVNI_SELECTED);
+ }
+
+ if (changed)
+ {
+ LoadReplacements(hwndDlg);
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ }
+ }
+
+ break;
+ }
+
+ case WM_NOTIFY:
+ {
+ LPNMHDR lpnmhdr = (LPNMHDR)lParam;
+
+ if (lpnmhdr->idFrom == 0 && lpnmhdr->code == PSN_APPLY && languages.getCount() > 0)
+ {
+ for(int i = 0; i < languages.getCount(); i++)
+ {
+ AutoreplaceData *data = (AutoreplaceData *) SendDlgItemMessage(hwndDlg, IDC_LANGUAGE, CB_GETITEMDATA, i, 0);
+ if (data->changed)
+ {
+ data->dict->autoReplace->setMap(data->autoReplaceMap);
+ data->changed = FALSE;
+ }
+ }
+ }
+ else if (lpnmhdr->idFrom == IDC_REPLACEMENTS)
+ {
+ HWND hList = GetDlgItem(hwndDlg, IDC_REPLACEMENTS);
+
+ switch(lpnmhdr->code)
+ {
+ case LVN_ITEMCHANGED:
+ case NM_CLICK:
+ EnableDisableCtrls(hwndDlg);
+ break;
+
+ case NM_DBLCLK:
+ LPNMITEMACTIVATE lpnmitem = (LPNMITEMACTIVATE) lParam;
+ if (lpnmitem->iItem >= 0)
+ ShowAddReplacement(hwndDlg, lpnmitem->iItem);
+ break;
+ }
+ }
+ else if (lpnmhdr->idFrom == IDC_AUTO_USER)
+ {
+ EnableDisableCtrls(hwndDlg);
+ }
+
+ break;
+ }
+
+ case WM_DRAWITEM:
+ {
+ LPDRAWITEMSTRUCT lpdis = (LPDRAWITEMSTRUCT)lParam;
+ if(lpdis->CtlID != IDC_LANGUAGE)
+ break;
+ if(lpdis->itemID == -1)
+ break;
+
+ AutoreplaceData *data = (AutoreplaceData *) lpdis->itemData;
+
+ DrawItem(hwndDlg, lpdis, data->dict);
+
+ return TRUE;
+ }
+
+ case WM_MEASUREITEM:
+ {
+ LPMEASUREITEMSTRUCT lpmis = (LPMEASUREITEMSTRUCT)lParam;
+ if(lpmis->CtlID != IDC_LANGUAGE)
+ break;
+
+ MeasureItem(hwndDlg, lpmis);
+
+
+ return TRUE;
+ }
+ }
+
+
+ return SaveOptsDlgProc(autoReplaceControls, MAX_REGS(autoReplaceControls), MODULE_NAME, hwndDlg, msg, wParam, lParam);
+}
+
+
diff --git a/Plugins/spellchecker/options.h b/Plugins/spellchecker/options.h
new file mode 100644
index 0000000..70710fd
--- /dev/null
+++ b/Plugins/spellchecker/options.h
@@ -0,0 +1,68 @@
+/*
+Copyright (C) 2006-2010 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#ifndef __OPTIONS_H__
+# define __OPTIONS_H__
+
+
+#define POPUP_ACTION_DONOTHING 0
+#define POPUP_ACTION_CLOSEPOPUP 1
+#define POPUP_ACTION_OPENHISTORY 2
+
+#define POPUP_DELAY_DEFAULT 0
+#define POPUP_DELAY_CUSTOM 1
+#define POPUP_DELAY_PERMANENT 2
+
+
+struct Options {
+ TCHAR default_language[32];
+ BOOL auto_replace_dict;
+ BOOL auto_replace_user;
+ BOOL ignore_uppercase;
+ BOOL ignore_with_numbers;
+ BOOL ask_when_sending_with_error;
+
+ WORD underline_type;
+ BOOL cascade_corrections;
+ BOOL show_all_corrections;
+ BOOL show_wrong_word;
+ BOOL use_flags;
+ BOOL auto_locale;
+ BOOL use_other_apps_dicts;
+ BOOL handle_underscore;
+};
+
+extern Options opts;
+
+
+// Initializations needed by options
+void InitOptions();
+
+// Deinitializations needed by options
+void DeInitOptions();
+
+
+// Loads the options from DB
+// It don't need to be called, except in some rare cases
+void LoadOptions();
+
+
+
+#endif // __OPTIONS_H__
diff --git a/Plugins/spellchecker/res/no_spellcheck.ico b/Plugins/spellchecker/res/no_spellcheck.ico
new file mode 100644
index 0000000..a6190bf
--- /dev/null
+++ b/Plugins/spellchecker/res/no_spellcheck.ico
Binary files differ
diff --git a/Plugins/spellchecker/res/spellcheck.ico b/Plugins/spellchecker/res/spellcheck.ico
new file mode 100644
index 0000000..7ab7cce
--- /dev/null
+++ b/Plugins/spellchecker/res/spellcheck.ico
Binary files differ
diff --git a/Plugins/spellchecker/res/unknown.ico b/Plugins/spellchecker/res/unknown.ico
new file mode 100644
index 0000000..709342a
--- /dev/null
+++ b/Plugins/spellchecker/res/unknown.ico
Binary files differ
diff --git a/Plugins/spellchecker/resource.h b/Plugins/spellchecker/resource.h
new file mode 100644
index 0000000..bcb2800
--- /dev/null
+++ b/Plugins/spellchecker/resource.h
@@ -0,0 +1,93 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Developer Studio generated include file.
+// Used by resource.rc
+//
+#define IDD_OPTIONS 119
+#define IDD_POPUPS 120
+#define IDR_CONTEXT 120
+#define IDI_NO_CHECK 122
+#define IDI_CHECK 123
+#define IDI_UNKNOWN_FLAG 124
+#define IDD_ADD_REPLACEMENT 125
+#define IDD_REPLACEMENTS 126
+#define IDC_DELAY 1001
+#define IDC_WINCOLORS 1002
+#define IDC_DEFAULTCOLORS 1003
+#define IDC_BGCOLOR 1004
+#define IDC_TEXTCOLOR 1005
+#define IDC_PREV 1006
+#define IDC_DELAYFROMPU 1007
+#define IDC_DELAYCUSTOM 1008
+#define IDC_DELAYPERMANENT 1009
+#define IDC_RIGHT_ACTION 1022
+#define IDC_LEFT_ACTION 1023
+#define IDC_PROTOCOLS 1041
+#define IDC_CHANGED 1058
+#define IDC_REMOVED 1059
+#define IDC_CHECK1 1060
+#define IDC_POPUPS 1060
+#define IDC_AUTOCORRECT 1060
+#define IDC_AUTO_DICT 1060
+#define IDC_VARIABLES 1060
+#define IDC_CHECK2 1061
+#define IDC_DELAY_SPIN 1061
+#define IDC_HISTORY 1061
+#define IDC_SIMULATE_SUPPORT 1061
+#define IDC_USE_LOCALE 1061
+#define IDC_CASCADE_CORRECTIONS 1061
+#define IDC_ANSI 1062
+#define IDC_SHOW_ALL_CORRECTIONS 1062
+#define IDC_TRACK_G 1063
+#define IDC_USE_FLAGS 1063
+#define IDC_CHANGED_L 1064
+#define IDC_SHOW_WRONG_WORD 1064
+#define IDC_REMOVED_L 1065
+#define IDC_IGNORE_UPPERCASE 1065
+#define IDC_PROTOCOLS_G 1066
+#define IDC_AUTO_USER 1066
+#define IDC_ASK_ON_ERROR 1066
+#define IDC_PROTOCOLS_L 1067
+#define IDC_AUTO_LOCALE 1067
+#define IDC_COLOURS_G 1068
+#define IDC_TRACK_CHANGE 1068
+#define IDC_OTHER_PROGS 1068
+#define IDC_BGCOLOR_L 1069
+#define IDC_TRACK_REMOVE 1069
+#define IDC_HANDLE_UNDERSCORE 1069
+#define IDC_TEXTCOLOR_L 1070
+#define IDC_ONLY_NOT_OFFLINE 1070
+#define IDC_DELAY_G 1071
+#define IDC_ACTIONS_G 1072
+#define IDC_RIGHT_ACTION_L 1073
+#define IDC_LEFT_ACTION_L 1074
+#define IDC_DEF_LANG 1075
+#define IDC_GETMORE 1076
+#define IDC_ADVANCED 1077
+#define IDC_UNDERLINE_TYPE 1078
+#define IDC_UNDERLINE_TYPE_L 1079
+#define IDC_SPELL_CHECKER 1080
+#define IDC_NEW 1082
+#define IDC_OLD 1083
+#define IDC_REPLACEMENTS 1084
+#define IDC_REMOVE 1085
+#define IDC_ADD 1086
+#define IDC_LANGUAGE 1087
+#define IDC_EDIT 1088
+#define IDC_VAR_HELP 1088
+#define IDC_OLD_PS 1089
+#define IDC_LANGUAGE_L 1090
+#define IDC_IGNORE_NUMBERS 1091
+#define IDC_STATIC -1
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NO_MFC 1
+#define _APS_3D_CONTROLS 1
+#define _APS_NEXT_RESOURCE_VALUE 126
+#define _APS_NEXT_COMMAND_VALUE 40005
+#define _APS_NEXT_CONTROL_VALUE 1092
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/Plugins/spellchecker/resource.rc b/Plugins/spellchecker/resource.rc
new file mode 100644
index 0000000..d4c4ab2
--- /dev/null
+++ b/Plugins/spellchecker/resource.rc
@@ -0,0 +1,201 @@
+// Microsoft Visual C++ generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "resource.h"
+#include "winresrc.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// Neutral resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_NEU)
+LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
+#pragma code_page(1252)
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Dialog
+//
+
+IDD_REPLACEMENTS DIALOGEX 0, 0, 276, 229
+STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_VISIBLE
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg", 0, 0, 0x1
+BEGIN
+ GROUPBOX "Replacements",IDC_STATIC,1,5,274,216
+ CONTROL "Enable auto-replacements",IDC_AUTO_USER,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,11,20,256,10
+ LTEXT "Language:",IDC_LANGUAGE_L,11,37,82,8
+ COMBOBOX IDC_LANGUAGE,95,35,173,60,CBS_DROPDOWNLIST | CBS_OWNERDRAWFIXED | CBS_HASSTRINGS | WS_VSCROLL | WS_TABSTOP
+ CONTROL "List1",IDC_REPLACEMENTS,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_NOSORTHEADER | WS_BORDER | WS_TABSTOP,10,55,258,139
+ PUSHBUTTON "Add...",IDC_ADD,68,199,60,15
+ PUSHBUTTON "Edit...",IDC_EDIT,137,199,60,15
+ PUSHBUTTON "Remove",IDC_REMOVE,205,199,60,15
+END
+
+IDD_ADD_REPLACEMENT DIALOG 0, 0, 282, 98
+STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Add auto-replace word"
+FONT 8, "MS Shell Dlg"
+BEGIN
+ RTEXT "Wrong word:",IDC_STATIC,7,9,67,11
+ EDITTEXT IDC_OLD,77,7,198,13,ES_AUTOHSCROLL
+ LTEXT "No separators and all lowercase chars",IDC_OLD_PS,77,22,198,11
+ RTEXT "Correction:",IDC_STATIC,7,40,67,11
+ EDITTEXT IDC_NEW,77,38,180,13,ES_AUTOHSCROLL
+ CONTROL "",IDC_VAR_HELP,"MButtonClass",WS_TABSTOP,259,38,16,14,WS_EX_NOACTIVATE | 0x10000000L
+ CONTROL "Use variables in correction",IDC_VARIABLES,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,77,55,198,10
+ DEFPUSHBUTTON "OK",IDOK,88,77,50,14
+ PUSHBUTTON "Cancel",IDCANCEL,142,77,50,14
+END
+
+IDD_OPTIONS DIALOGEX 0, 0, 284, 247
+STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_VISIBLE
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg", 0, 0, 0x1
+BEGIN
+ GROUPBOX "Spell Checker",IDC_SPELL_CHECKER,1,5,282,109
+ LTEXT "Default language:",IDC_STATIC,11,20,82,8
+ COMBOBOX IDC_DEF_LANG,95,18,173,60,CBS_DROPDOWNLIST | CBS_OWNERDRAWFIXED | CBS_HASSTRINGS | WS_VSCROLL | WS_TABSTOP
+ CONTROL "Auto-replace words with dictionary suggestions",IDC_AUTO_DICT,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,10,40,266,10
+ CONTROL "Ignore words with numbers",IDC_IGNORE_NUMBERS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,10,52,266,10
+ CONTROL "Ignore words in UPPER CASE",IDC_IGNORE_UPPERCASE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,10,64,266,10
+ CONTROL "Ask before sending a message with spelling errors",IDC_ASK_ON_ERROR,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,10,76,266,10
+ CONTROL "Download more dictionaries",IDC_GETMORE,"Hyperlink",WS_TABSTOP | 0x1,10,96,266,12
+ GROUPBOX "Advanced",IDC_ADVANCED,1,118,282,125
+ LTEXT "Underline type:",IDC_UNDERLINE_TYPE_L,10,134,92,8
+ COMBOBOX IDC_UNDERLINE_TYPE,110,132,158,60,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
+ CONTROL "Show corrections in submenu",IDC_CASCADE_CORRECTIONS,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,10,154,266,10
+ CONTROL "Show all corrections in context menu (takes more time to show)",IDC_SHOW_ALL_CORRECTIONS,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,10,166,266,10
+ CONTROL "Show wrong word",IDC_SHOW_WRONG_WORD,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,10,178,266,10
+ CONTROL "Use flags",IDC_USE_FLAGS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,10,190,266,10
+ CONTROL "Use input language to select dictionary",IDC_AUTO_LOCALE,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,10,202,266,10
+ CONTROL "Use dictionaries from other programs",IDC_OTHER_PROGS,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,10,214,266,10
+ CONTROL "Avoid removing underscores (slows down checking of long texts)",IDC_HANDLE_UNDERSCORE,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,10,226,266,10
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// DESIGNINFO
+//
+
+#ifdef APSTUDIO_INVOKED
+GUIDELINES DESIGNINFO
+BEGIN
+ IDD_REPLACEMENTS, DIALOG
+ BEGIN
+ LEFTMARGIN, 1
+ RIGHTMARGIN, 275
+ TOPMARGIN, 1
+ BOTTOMMARGIN, 227
+ HORZGUIDE, 199
+ END
+
+ IDD_ADD_REPLACEMENT, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 275
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 91
+ END
+
+ IDD_OPTIONS, DIALOG
+ BEGIN
+ LEFTMARGIN, 1
+ RIGHTMARGIN, 283
+ VERTGUIDE, 10
+ VERTGUIDE, 276
+ TOPMARGIN, 1
+ BOTTOMMARGIN, 243
+ END
+END
+#endif // APSTUDIO_INVOKED
+
+#endif // Neutral resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+/////////////////////////////////////////////////////////////////////////////
+// English (United States) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page(1252)
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Icon
+//
+
+// Icon with lowest ID value placed first to ensure application icon
+// remains consistent on all systems.
+IDI_NO_CHECK ICON "res/no_spellcheck.ico"
+IDI_CHECK ICON "res/spellcheck.ico"
+IDI_UNKNOWN_FLAG ICON "res/unknown.ico"
+#endif // English (United States) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+/////////////////////////////////////////////////////////////////////////////
+// English (Canada) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENC)
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_CAN
+#pragma code_page(1252)
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE
+BEGIN
+ "resource.h\0"
+END
+
+2 TEXTINCLUDE
+BEGIN
+ "#include ""resource.h""\r\n"
+ "#include ""winresrc.h""\r\n"
+ "\0"
+END
+
+3 TEXTINCLUDE
+BEGIN
+ "\r\n"
+ "\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+#endif // English (Canada) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
+
diff --git a/Plugins/spellchecker/sdk/m_folders.h b/Plugins/spellchecker/sdk/m_folders.h
new file mode 100644
index 0000000..5971cff
--- /dev/null
+++ b/Plugins/spellchecker/sdk/m_folders.h
@@ -0,0 +1,284 @@
+/*
+Custom profile folders plugin for Miranda IM
+
+Copyright © 2005 Cristian Libotean
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef M_CUSTOM_FOLDERS_H
+#define M_CUSTOM_FOLDERS_H
+
+#define FOLDERS_API 501 //dunno why it's here but it is :)
+
+#define PROFILE_PATH "%profile_path%"
+#define CURRENT_PROFILE "%current_profile%"
+#define MIRANDA_PATH "%miranda_path%"
+#define PLUGINS_PATH "%miranda_path%" "\\plugins"
+#define MIRANDA_USERDATA "%miranda_userdata%"
+
+#define TO_WIDE(x) L ## x
+
+#define PROFILE_PATHW L"%profile_path%"
+#define CURRENT_PROFILEW L"%current_profile%"
+#define MIRANDA_PATHW L"%miranda_path%"
+#define MIRANDA_USERDATAW L"%miranda_userdata%"
+
+#define FOLDER_AVATARS PROFILE_PATH "\\" CURRENT_PROFILE "\\avatars"
+#define FOLDER_VCARDS PROFILE_PATH "\\" CURRENT_PROFILE "\\vcards"
+#define FOLDER_LOGS PROFILE_PATH "\\" CURRENT_PROFILE "\\logs"
+#define FOLDER_RECEIVED_FILES PROFILE_PATH "\\" CURRENT_PROFILE "\\received files"
+#define FOLDER_DOCS MIRANDA_PATH "\\" "docs"
+
+#define FOLDER_CONFIG PLUGINS_PATH "\\" "config"
+
+#define FOLDER_SCRIPTS MIRANDA_PATH "\\" "scripts"
+
+#define FOLDER_UPDATES MIRANDA_PATH "\\" "updates"
+
+#define FOLDER_CUSTOMIZE MIRANDA_PATH "\\" "customize"
+#define FOLDER_CUSTOMIZE_SOUNDS FOLDER_CUSTOMIZE "\\sounds"
+#define FOLDER_CUSTOMIZE_ICONS FOLDER_CUSTOMIZE "\\icons"
+#define FOLDER_CUSTOMIZE_SMILEYS FOLDER_CUSTOMIZE "\\smileys"
+#define FOLDER_CUSTOMIZE_SKINS FOLDER_CUSTOMIZE "\\skins"
+#define FOLDER_CUSTOMIZE_THEMES FOLDER_CUSTOMIZE "\\themes"
+
+
+#define FOLDERS_NAME_MAX_SIZE 64 //maximum name and section size
+
+#define FF_UNICODE 0x00000001
+
+#if defined (UNICODE)
+ #define FF_TCHAR FF_UNICODE
+#else
+ #define FF_TCHAR 0
+#endif
+
+typedef struct{
+ int cbSize; //size of struct
+ char szSection[FOLDERS_NAME_MAX_SIZE]; //section name, if it doesn't exist it will be created otherwise it will just add this entry to it
+ char szName[FOLDERS_NAME_MAX_SIZE]; //entry name - will be shown in options
+ union{
+ const char *szFormat; //default string format. Fallback string in case there's no entry in the database for this folder. This should be the initial value for the path, users will be able to change it later.
+ const wchar_t *szFormatW; //String is dup()'d so you can free it later. If you set the unicode string don't forget to set the flag accordingly.
+ const TCHAR *szFormatT;
+ };
+ DWORD flags; //FF_* flags
+} FOLDERSDATA;
+
+/*Folders/Register/Path service
+ wParam - not used, must be 0
+ lParam - (LPARAM) (const FOLDERDATA *) - Data structure filled with
+ the necessary information.
+ Returns a handle to the registered path or 0 on error.
+ You need to use this to call the other services.
+*/
+#define MS_FOLDERS_REGISTER_PATH "Folders/Register/Path"
+
+/*Folders/Get/PathSize service
+ wParam - (WPARAM) (int) - handle to registered path
+ lParam - (LPARAM) (int *) - pointer to the variable that receives the size of the path
+ string (not including the null character). Depending on the flags set when creating the path
+ it will either call strlen() or wcslen() to get the length of the string.
+ Returns the size of the buffer.
+*/
+#define MS_FOLDERS_GET_SIZE "Folders/Get/PathSize"
+
+typedef struct{
+ int cbSize;
+ int nMaxPathSize; //maximum size of buffer. This represents the number of characters that can be copied to it (so for unicode strings you don't send the number of bytes but the length of the string).
+ union{
+ char *szPath; //pointer to the buffer that receives the path without the last "\\"
+ wchar_t *szPathW; //unicode version of the buffer.
+ TCHAR *szPathT;
+ };
+} FOLDERSGETDATA;
+
+/*Folders/Get/Path service
+ wParam - (WPARAM) (int) - handle to registered path
+ lParam - (LPARAM) (FOLDERSGETDATA *) pointer to a FOLDERSGETDATA that has all the relevant fields filled.
+ Should return 0 on success, or nonzero otherwise.
+*/
+#define MS_FOLDERS_GET_PATH "Folders/Get/Path"
+
+typedef struct{
+ int cbSize;
+ union{
+ char **szPath; //address of a string variable (char *) or (wchar_t*) where the path should be stored (the last \ won't be copied).
+ wchar_t **szPathW; //unicode version of string.
+ TCHAR **szPathT;
+ };
+} FOLDERSGETALLOCDATA;
+
+/*Folders/GetRelativePath/Alloc service
+ wParam - (WPARAM) (int) - Handle to registered path
+ lParam - (LPARAM) (FOLDERSALLOCDATA *) data
+ This service is the same as MS_FOLDERS_GET_PATH with the difference that this service
+ allocates the needed space for the buffer. It uses miranda's memory functions for that and you need
+ to use those to free the resulting buffer.
+ Should return 0 on success, or nonzero otherwise. Currently it only returns 0.
+*/
+#define MS_FOLDERS_GET_PATH_ALLOC "Folders/Get/Path/Alloc"
+
+
+/*Folders/On/Path/Changed
+ wParam - (WPARAM) 0
+ lParam - (LPARAM) 0
+ Triggered when the folders change, you should reget the paths you registered.
+*/
+#define ME_FOLDERS_PATH_CHANGED "Folders/On/Path/Changed"
+
+#ifndef FOLDERS_NO_HELPER_FUNCTIONS
+
+#ifndef M_UTILS_H__
+#error The helper functions require that m_utils.h be included in the project. Please include that file if you want to use the helper functions. If you don''t want to use the functions just define FOLDERS_NO_HELPER_FUNCTIONS.
+#endif
+//#include "../../../include/newpluginapi.h"
+
+__inline static HANDLE FoldersRegisterCustomPath(const char *section, const char *name, const char *defaultPath)
+{
+ FOLDERSDATA fd = {0};
+ if (!ServiceExists(MS_FOLDERS_REGISTER_PATH)) return 0;
+ fd.cbSize = sizeof(FOLDERSDATA);
+ strncpy(fd.szSection, section, FOLDERS_NAME_MAX_SIZE);
+ fd.szSection[FOLDERS_NAME_MAX_SIZE - 1] = '\0';
+ strncpy(fd.szName, name, FOLDERS_NAME_MAX_SIZE);
+ fd.szName[FOLDERS_NAME_MAX_SIZE - 1] = '\0';
+ fd.szFormat = defaultPath;
+ return (HANDLE) CallService(MS_FOLDERS_REGISTER_PATH, 0, (LPARAM) &fd);
+}
+
+__inline static HANDLE FoldersRegisterCustomPathW(const char *section, const char *name, const wchar_t *defaultPathW)
+{
+ FOLDERSDATA fd = {0};
+ if (!ServiceExists(MS_FOLDERS_REGISTER_PATH)) return 0;
+ fd.cbSize = sizeof(FOLDERSDATA);
+ strncpy(fd.szSection, section, FOLDERS_NAME_MAX_SIZE);
+ fd.szSection[FOLDERS_NAME_MAX_SIZE - 1] = '\0'; //make sure it's NULL terminated
+ strncpy(fd.szName, name, FOLDERS_NAME_MAX_SIZE);
+ fd.szName[FOLDERS_NAME_MAX_SIZE - 1] = '\0'; //make sure it's NULL terminated
+ fd.szFormatW = defaultPathW;
+ fd.flags = FF_UNICODE;
+ return (HANDLE) CallService(MS_FOLDERS_REGISTER_PATH, 0, (LPARAM) &fd);
+}
+
+__inline static INT_PTR FoldersGetCustomPath(HANDLE hFolderEntry, char *path, const int size, const char *notFound)
+{
+ FOLDERSGETDATA fgd = {0};
+ INT_PTR res;
+ fgd.cbSize = sizeof(FOLDERSGETDATA);
+ fgd.nMaxPathSize = size;
+ fgd.szPath = path;
+ res = CallService(MS_FOLDERS_GET_PATH, (WPARAM) hFolderEntry, (LPARAM) &fgd);
+ if (res)
+ {
+ char buffer[MAX_PATH];
+ CallService(MS_UTILS_PATHTOABSOLUTE, (WPARAM) notFound, (LPARAM) buffer);
+ mir_snprintf(path, size, "%s", buffer);
+ }
+
+ return res;
+}
+
+__inline static INT_PTR FoldersGetCustomPathW(HANDLE hFolderEntry, wchar_t *pathW, const int count, const wchar_t *notFoundW)
+{
+ FOLDERSGETDATA fgd = {0};
+ INT_PTR res;
+ fgd.cbSize = sizeof(FOLDERSGETDATA);
+ fgd.nMaxPathSize = count;
+ fgd.szPathW = pathW;
+ res = CallService(MS_FOLDERS_GET_PATH, (WPARAM) hFolderEntry, (LPARAM) &fgd);
+ if (res)
+ {
+ wcsncpy(pathW, notFoundW, count);
+ pathW[count - 1] = '\0';
+ }
+
+ return res;
+}
+
+__inline static INT_PTR FoldersGetCustomPathEx(HANDLE hFolderEntry, char *path, const int size, char *notFound, char *fileName)
+{
+ FOLDERSGETDATA fgd = {0};
+ INT_PTR res;
+ fgd.cbSize = sizeof(FOLDERSGETDATA);
+ fgd.nMaxPathSize = size;
+ fgd.szPath = path;
+ res = CallService(MS_FOLDERS_GET_PATH, (WPARAM) hFolderEntry, (LPARAM) &fgd);
+ if (res)
+ {
+ char buffer[MAX_PATH];
+ CallService(MS_UTILS_PATHTOABSOLUTE, (WPARAM) notFound, (LPARAM) buffer);
+ mir_snprintf(path, size, "%s", buffer);
+ }
+ if (strlen(path) > 0)
+ {
+ strcat(path, "\\");
+ }
+ else{
+ path[0] = '\0';
+ }
+
+ if (fileName)
+ {
+ strcat(path, fileName);
+ }
+
+ return res;
+}
+
+__inline static INT_PTR FoldersGetCustomPathExW(HANDLE hFolderEntry, wchar_t *pathW, const int count, wchar_t *notFoundW, wchar_t *fileNameW)
+{
+ FOLDERSGETDATA fgd = {0};
+ INT_PTR res;
+ fgd.cbSize = sizeof(FOLDERSGETDATA);
+ fgd.nMaxPathSize = count;
+ fgd.szPathW = pathW;
+ res = CallService(MS_FOLDERS_GET_PATH, (WPARAM) hFolderEntry, (LPARAM) &fgd);
+ if (res)
+ {
+ wcsncpy(pathW, notFoundW, count);
+ pathW[count - 1] = '\0';
+ }
+
+ if (wcslen(pathW) > 0)
+ {
+ wcscat(pathW, L"\\");
+ }
+ else{
+ pathW[0] = L'\0';
+ }
+
+ if (fileNameW)
+ {
+ wcscat(pathW, fileNameW);
+ }
+
+ return res;
+}
+
+# ifdef _UNICODE
+# define FoldersGetCustomPathT FoldersGetCustomPathW
+# define FoldersGetCustomPathExT FoldersGetCustomPathExW
+# define FoldersRegisterCustomPathT FoldersRegisterCustomPathW
+#else
+# define FoldersGetCustomPathT FoldersGetCustomPath
+# define FoldersGetCustomPathExT FoldersGetCustomPath
+# define FoldersRegisterCustomPathT FoldersRegisterCustomPath
+#endif
+
+#endif
+
+#endif //M_CUSTOM_FOLDERS_H \ No newline at end of file
diff --git a/Plugins/spellchecker/sdk/m_metacontacts.h b/Plugins/spellchecker/sdk/m_metacontacts.h
new file mode 100644
index 0000000..9f348bd
--- /dev/null
+++ b/Plugins/spellchecker/sdk/m_metacontacts.h
@@ -0,0 +1,166 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright © 2004 Universite Louis PASTEUR, STRASBOURG.
+Copyright © 2004 Scott Ellis (www.scottellis.com.au mail@scottellis.com.au)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef M_METACONTACTS_H__
+#define M_METACONTACTS_H__ 1
+
+#ifndef MIID_METACONTACTS
+#define MIID_METACONTACTS {0xc0325019, 0xc1a7, 0x40f5, { 0x83, 0x65, 0x4f, 0x46, 0xbe, 0x21, 0x86, 0x3e}}
+#endif
+
+//get the handle for a contact's parent metacontact
+//wParam=(HANDLE)hSubContact
+//lParam=0
+//returns a handle to the parent metacontact, or null if this contact is not a subcontact
+#define MS_MC_GETMETACONTACT "MetaContacts/GetMeta"
+
+//gets the handle for the default contact
+//wParam=(HANDLE)hMetaContact
+//lParam=0
+//returns a handle to the default contact, or null on failure
+#define MS_MC_GETDEFAULTCONTACT "MetaContacts/GetDefault"
+
+//gets the contact number for the default contact
+//wParam=(HANDLE)hMetaContact
+//lParam=0
+//returns a DWORD contact number, or -1 on failure
+#define MS_MC_GETDEFAULTCONTACTNUM "MetaContacts/GetDefaultNum"
+
+//gets the handle for the 'most online' contact
+//wParam=(HANDLE)hMetaContact
+//lParam=0
+//returns a handle to the 'most online' contact
+#define MS_MC_GETMOSTONLINECONTACT "MetaContacts/GetMostOnline"
+
+//gets the number of subcontacts for a metacontact
+//wParam=(HANDLE)hMetaContact
+//lParam=0
+//returns a DWORD representing the number of subcontacts for the given metacontact
+#define MS_MC_GETNUMCONTACTS "MetaContacts/GetNumContacts"
+
+//gets the handle of a subcontact, using the subcontact's number
+//wParam=(HANDLE)hMetaContact
+//lParam=(DWORD)contact number
+//returns a handle to the specified subcontact
+#define MS_MC_GETSUBCONTACT "MetaContacts/GetSubContact"
+
+//sets the default contact, using the subcontact's contact number
+//wParam=(HANDLE)hMetaContact
+//lParam=(DWORD)contact number
+//returns 0 on success
+#define MS_MC_SETDEFAULTCONTACTNUM "MetaContacts/SetDefault"
+
+//sets the default contact, using the subcontact's handle
+//wParam=(HANDLE)hMetaContact
+//lParam=(HANDLE)hSubcontact
+//returns 0 on success
+#define MS_MC_SETDEFAULTCONTACT "MetaContacts/SetDefaultByHandle"
+
+//forces the metacontact to send using a specific subcontact, using the subcontact's contact number
+//wParam=(HANDLE)hMetaContact
+//lParam=(DWORD)contact number
+//returns 0 on success
+#define MS_MC_FORCESENDCONTACTNUM "MetaContacts/ForceSendContact"
+
+//forces the metacontact to send using a specific subcontact, using the subcontact's handle
+//wParam=(HANDLE)hMetaContact
+//lParam=(HANDLE)hSubcontact
+//returns 0 on success (will fail if 'force default' is in effect)
+#define MS_MC_FORCESENDCONTACT "MetaContacts/ForceSendContactByHandle"
+
+//'unforces' the metacontact to send using a specific subcontact
+//wParam=(HANDLE)hMetaContact
+//lParam=0
+//returns 0 on success (will fail if 'force default' is in effect)
+#define MS_MC_UNFORCESENDCONTACT "MetaContacts/UnforceSendContact"
+
+//'forces' or 'unforces' (i.e. toggles) the metacontact to send using it's default contact
+// overrides (and clears) 'force send' above, and will even force use of offline contacts
+// will send ME_MC_FORCESEND or ME_MC_UNFORCESEND event
+//wParam=(HANDLE)hMetaContact
+//lParam=0
+//returns 1(true) or 0(false) representing new state of 'force default'
+#define MS_MC_FORCEDEFAULT "MetaContacts/ForceSendDefault"
+
+// method to get state of 'force' for a metacontact
+// wParam=(HANDLE)hMetaContact
+// lParam= (DWORD)&contact_number or NULL
+//
+// if lparam supplied, the contact_number of the contatct 'in force' will be copied to the address it points to,
+// or if none is in force, the value (DWORD)-1 will be copied
+// (v0.8.0.8+ returns 1 if 'force default' is true with *lParam == default contact number, else returns 0 with *lParam as above)
+#define MS_MC_GETFORCESTATE "MetaContacts/GetForceState"
+
+// fired when a metacontact's default contact changes (fired upon creation of metacontact also, when default is initially set)
+// wParam=(HANDLE)hMetaContact
+// lParam=(HANDLE)hDefaultContact
+#define ME_MC_DEFAULTTCHANGED "MetaContacts/DefaultChanged"
+
+// fired when a metacontact's subcontacts change (fired upon creation of metacontact, when contacts are added or removed, and when
+// contacts are reordered) - a signal to re-read metacontact data
+// wParam=(HANDLE)hMetaContact
+// lParam=0
+#define ME_MC_SUBCONTACTSCHANGED "MetaContacts/SubcontactsChanged"
+
+// fired when a metacontact is forced to send using a specific subcontact
+// wParam=(HANDLE)hMetaContact
+// lParam=(HANDLE)hForceContact
+#define ME_MC_FORCESEND "MetaContacts/ForceSend"
+
+// fired when a metacontact is 'unforced' to send using a specific subcontact
+// wParam=(HANDLE)hMetaContact
+// lParam=0
+#define ME_MC_UNFORCESEND "MetaContacts/UnforceSend"
+
+// method to get protocol name - used to be sure you're dealing with a "real" metacontacts plugin :)
+// wParam=lParam=0
+#define MS_MC_GETPROTOCOLNAME "MetaContacts/GetProtoName"
+
+
+// added 0.9.5.0 (22/3/05)
+// wParam=(HANDLE)hContact
+// lParam=0
+// convert a given contact into a metacontact
+#define MS_MC_CONVERTTOMETA "MetaContacts/ConvertToMetacontact"
+
+// added 0.9.5.0 (22/3/05)
+// wParam=(HANDLE)hContact
+// lParam=(HANDLE)hMeta
+// add an existing contact to a metacontact
+#define MS_MC_ADDTOMETA "MetaContacts/AddToMetacontact"
+
+// added 0.9.5.0 (22/3/05)
+// wParam=0
+// lParam=(HANDLE)hContact
+// remove a contact from a metacontact
+#define MS_MC_REMOVEFROMMETA "MetaContacts/RemoveFromMetacontact"
+
+
+// added 0.9.13.2 (6/10/05)
+// wParam=(BOOL)disable
+// lParam=0
+// enable/disable the 'hidden group hack' - for clists that support subcontact hiding using 'IsSubcontact' setting
+// should be called once in the clist 'onmodulesloaded' event handler (which, since it's loaded after the db, will be called
+// before the metacontact onmodulesloaded handler where the subcontact hiding is usually done)
+#define MS_MC_DISABLEHIDDENGROUP "MetaContacts/DisableHiddenGroup"
+
+#endif
diff --git a/Plugins/spellchecker/sdk/m_updater.h b/Plugins/spellchecker/sdk/m_updater.h
new file mode 100644
index 0000000..488d372
--- /dev/null
+++ b/Plugins/spellchecker/sdk/m_updater.h
@@ -0,0 +1,150 @@
+#ifndef _M_UPDATER_H
+#define _M_UPDATER_H
+
+// NOTES:
+// - For langpack updates, include a string of the following format in the langpack text file:
+// ";FLID: <file listing name> <version>"
+// version must be four numbers seperated by '.', in the range 0-255 inclusive
+// - Updater will disable plugins that are downloaded but were not active prior to the update (this is so that, if an archive contains e.g. ansi and
+// unicode versions, the correct plugin will be the only one active after the new version is installed)...so if you add a support plugin, you may need
+// to install an ini file to make the plugin activate when miranda restarts after the update
+// - Updater will replace all dlls that have the same internal shortName as a downloaded update dll (this is so that msn1.dll and msn2.dll, for example,
+// will both be updated) - so if you have a unicode and a non-unicode version of a plugin in your archive, you should make the internal names different (which will break automatic
+// updates from the file listing if there is only one file listing entry for both versions, unless you use the 'MS_UPDATE_REGISTER' service below)
+// - Updater will install all files in the root of the archive into the plugins folder, except for langpack files that contain the FLID string which go into the root folder (same
+// folder as miranda32.exe)...all folders in the archive will also be copied to miranda's root folder, and their contents transferred into the new folders. The only exception is a
+// special folder called 'root_files' - if there is a folder by that name in the archive, it's contents will also be copied into miranda's root folder - this is intended to be used
+// to install additional dlls etc that a plugin may require)
+
+// if you set Update.szUpdateURL to the following value when registering, as well as setting your beta site and version data,
+// Updater will ignore szVersionURL and pbVersionPrefix, and attempt to find the file listing URL's from the backend XML data.
+// for this to work, the plugin name in pluginInfo.shortName must match the file listing exactly (except for case)
+#define UPDATER_AUTOREGISTER "UpdaterAUTOREGISTER"
+// Updater will also use the backend xml data if you provide URL's that reference the miranda file listing for updates (so you can use that method
+// if e.g. your plugin shortName does not match the file listing) - it will grab the file listing id from the end of these URLs
+
+typedef struct Update_tag {
+ int cbSize;
+ char *szComponentName; // component name as it will appear in the UI (will be translated before displaying)
+
+ char *szVersionURL; // URL where the current version can be found (NULL to disable)
+ BYTE *pbVersionPrefix; // bytes occuring in VersionURL before the version, used to locate the version information within the URL data
+ // (note that this URL could point at a binary file - dunno why, but it could :)
+ int cpbVersionPrefix; // number of bytes pointed to by pbVersionPrefix
+ char *szUpdateURL; // URL where dll/zip is located
+ // set to UPDATER_AUTOREGISTER if you want Updater to find the file listing URLs (ensure plugin shortName matches file listing!)
+
+ char *szBetaVersionURL; // URL where the beta version can be found (NULL to disable betas)
+ BYTE *pbBetaVersionPrefix; // bytes occuring in VersionURL before the version, used to locate the version information within the URL data
+ int cpbBetaVersionPrefix; // number of bytes pointed to by pbVersionPrefix
+ char *szBetaUpdateURL; // URL where dll/zip is located
+
+ BYTE *pbVersion; // bytes of current version, used for comparison with those in VersionURL
+ int cpbVersion; // number of bytes pointed to by pbVersion
+
+ char *szBetaChangelogURL; // url for displaying changelog for beta versions
+} Update;
+
+// register a comonent with Updater
+//
+// wparam = 0
+// lparam = (LPARAM)&Update
+#define MS_UPDATE_REGISTER "Update/Register"
+
+// utility functions to create a version string from a DWORD or from pluginInfo
+// point buf at a buffer at least 16 chars wide - but note the version string returned may be shorter
+//
+__inline static char *CreateVersionString(DWORD version, char *buf) {
+ mir_snprintf(buf, 16, "%d.%d.%d.%d", (version >> 24) & 0xFF, (version >> 16) & 0xFF, (version >> 8) & 0xFF, version & 0xFF);
+ return buf;
+}
+
+__inline static char *CreateVersionStringPlugin(PLUGININFO *pluginInfo, char *buf) {
+ return CreateVersionString(pluginInfo->version, buf);
+}
+
+__inline static char *CreateVersionStringPluginEx(PLUGININFOEX *pluginInfo, char *buf) {
+ return CreateVersionString(pluginInfo->version, buf);
+}
+
+
+// register the 'easy' way - use this method if you have no beta URL and the plugin is on the miranda file listing
+// NOTE: the plugin version string on the file listing must be the string version of the version in pluginInfo (i.e. 0.0.0.1,
+// four numbers between 0 and 255 inclusivem, so no letters, brackets, etc.)
+//
+// wParam = (int)fileID - this is the file ID from the file listing (i.e. the number at the end of the download link)
+// lParam = (PLUGININFO*)&pluginInfo
+#define MS_UPDATE_REGISTERFL "Update/RegisterFL"
+
+// this function can be used to 'unregister' components - useful for plugins that register non-plugin/langpack components and
+// may need to change those components on the fly
+// lParam = (char *)szComponentName
+#define MS_UPDATE_UNREGISTER "Update/Unregister"
+
+// this event is fired when the startup process is complete, but NOT if a restart is imminent
+// it is designed for status managment plugins to use as a trigger for beggining their own startup process
+// wParam = lParam = 0 (unused)
+// (added in version 0.1.6.0)
+#define ME_UPDATE_STARTUPDONE "Update/StartupDone"
+
+// this service can be used to enable/disable Updater's global status control
+// it can be called from the StartupDone event handler
+// wParam = (BOOL)enable
+// lParam = 0
+// (added in version 0.1.6.0)
+#define MS_UPDATE_ENABLESTATUSCONTROL "Update/EnableStatusControl"
+
+// An description of usage of the above service and event:
+// Say you are a status control plugin that normally sets protocol or global statuses in your ModulesLoaded event handler.
+// In order to make yourself 'Updater compatible', you would move the status control code from ModulesLoaded to another function,
+// say DoStartup. Then, in ModulesLoaded you would check for the existence of the MS_UPDATE_ENABLESTATUSCONTROL service.
+// If it does not exist, call DoStartup. If it does exist, hook the ME_UPDATE_STARTUPDONE event and call DoStartup from there. You may
+// also wish to call MS_UPDATE_ENABLESTATUSCONTROL with wParam == FALSE at this time, to disable Updater's own status control feature.
+
+// this service can be used to determine whether updates are possible for a component with the given name
+// wParam = 0
+// lParam = (char *)szComponentName
+// returns TRUE if updates are supported, FALSE otherwise
+#define MS_UPDATE_ISUPDATESUPPORTED "Update/IsUpdateSupported"
+
+#endif
+
+
+/////////////// Usage Example ///////////////
+
+#ifdef EXAMPLE_CODE
+
+// you need to #include "m_updater.h" and HookEvent(ME_SYSTEM_MODULESLOADED, OnModulesLoaded) in your Load function...
+
+int OnModulesLoaded(WPARAM wParam, LPARAM lParam) {
+
+ Update update = {0}; // for c you'd use memset or ZeroMemory...
+ char szVersion[16];
+
+ update.cbSize = sizeof(Update);
+
+ update.szComponentName = pluginInfo.shortName;
+ update.pbVersion = (BYTE *)CreateVersionString(&pluginInfo, szVersion);
+ update.cpbVersion = strlen((char *)update.pbVersion);
+
+ // these are the three lines that matter - the archive, the page containing the version string, and the text (or data)
+ // before the version that we use to locate it on the page
+ // (note that if the update URL and the version URL point to standard file listing entries, the backend xml
+ // data will be used to check for updates rather than the actual web page - this is not true for beta urls)
+ update.szUpdateURL = "http://scottellis.com.au:81/test/updater.zip";
+ update.szVersionURL = "http://scottellis.com.au:81/test/updater_test.html";
+ update.pbVersionPrefix = (BYTE *)"Updater version ";
+
+ update.cpbVersionPrefix = strlen((char *)update.pbVersionPrefix);
+
+ // do the same for the beta versions of the above struct members if you wish to allow beta updates from another URL
+
+ CallService(MS_UPDATE_REGISTER, 0, (WPARAM)&update);
+
+ // Alternatively, to register a plugin with e.g. file ID 2254 on the file listing...
+ // CallService(MS_UPDATE_REGISTERFL, (WPARAM)2254, (LPARAM)&pluginInfo);
+
+ return 0;
+}
+
+#endif
diff --git a/Plugins/spellchecker/sdk/m_userinfoex.h b/Plugins/spellchecker/sdk/m_userinfoex.h
new file mode 100644
index 0000000..393e802
--- /dev/null
+++ b/Plugins/spellchecker/sdk/m_userinfoex.h
@@ -0,0 +1,764 @@
+/*
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2009 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#ifndef _M_USERINFOEX_H_
+#define _M_USERINFOEX_H_
+/*************************************************************
+ * Interfaces Module
+ */
+
+// {9C23A24B-E6AA-43c6-B0B8-D6C36D2F7B57}
+#define MIID_UIUSERINFOEX { 0x9c23a24b, 0xe6aa, 0x43c6, { 0xb0, 0xb8, 0xd6, 0xc3, 0x6d, 0x2f, 0x7b, 0x57 } }
+// {17DBD7C9-450E-4000-BFB4-908A7EF4CE72}
+#define MIID_CONTACTINFO { 0x17dbd7c9, 0x450e, 0x4000, { 0xbf, 0xb4, 0x90, 0x8a, 0x7e, 0xf4, 0xce, 0x72 } }
+// {02E890BD-278D-4890-918D-AB2CF5DC50BD}
+#define MIID_REMINDER { 0x2e890bd, 0x278d, 0x4890, { 0x91, 0x8d, 0xab, 0x2c, 0xf5, 0xdc, 0x50, 0xbd } }
+
+
+/*************************************************************
+ * PropertySheetPage Module
+ */
+
+/* UserInfo/AddPage v0.1.0.0+
+If the hIcon member of te optiondialogpage is valid, the tree show it for nicer look.
+Otherwise the default icon is displayed for this treeitem.
+*/
+#ifndef ODPF_UNICODE
+ #define ODPF_UNICODE 8 // string fields in OPTIONSDIALOGPAGE are WCHAR*
+#endif
+#define ODPF_ICON 64 // the hIcon member of the option dialog page is valid
+
+/* Handling notifications v0.1.0.4+
+A dialogbox should call SetWindowLongPtr(hDlg, DWLP_MSGRESULT, PSP_CHANGED) on a PSN_INFOCHANGED notification if
+there are unsafed changes and apply button should keep enabled. Otherwise the changed status
+of the dialogbox is resetted as well as the changed status of the details dialog box itself if no page
+called this message. Because UserinfoEx now looks for changes in the settings of a user to keep the
+shown inforamtion up to date.
+*/
+#define PSP_CHANGED 2
+#define PSN_ICONCHANGED 2000
+
+/* PSM_GETBOLDFONT v0.1.0.3+
+wParam=NULL
+lParam=(HFONT*)&hFont
+get bold dialog font. wParam is pointer to a handle that retrieves the boldfont.
+You can also call GetWindowLong(hDlg, DWLP_MSGRESULT) to get the font.
+This function returns TRUE on success or FALSE otherwise.
+*/
+#ifndef PSM_GETBOLDFONT
+ #define PSM_GETBOLDFONT (WM_USER+102)
+#endif
+
+/* PSM_ISLOCKED v0.1.0.4+
+Returns state of propertysheet. If it is locked, The PSM_CHANGED messages sent by a propertysheetpage does not
+have any effect. To aVOID editcontrols, ... to check for changes on redrawing at a load of settings from database or
+if another propertysheetpage is selected, a plugin should check this state and skip those checks to reduce stressing
+the database if such a test if control's content changed does so.
+wParam=NULL
+lParam=NULL
+You can also call GetWindowLong(hDlg, DWLP_MSGRESULT) to get the handle.
+This function returns TRUE if the PropertySheet is locked or FALSE otherwise.
+*/
+#define PSM_ISLOCKED (WM_USER+901)
+
+/* PSM_GETCONTACT v0.1.0.4+
+You can get the handle to the contact the propertysheet is associated with by calling PSM_GETCONTACT
+to the parent of your propertysheetpage - the propertysheet.
+wParam=index or -1 for current item
+lParam=(HANDLE*)&hContact
+You can also call GetWindowLong(hDlg, DWLP_MSGRESULT) to get the handle.
+This function returns TRUE on success or FALSE otherwise.
+*/
+#define PSM_GETCONTACT (WM_USER+903)
+
+/* PSM_GETBASEPROTO v0.1.0.4+
+You can get a pointer to the basic protocol module by sending PSM_GETBASEPROTO to the parent of your propertysheetpage.
+wParam=index or -1 for current item
+lParam=(LPCSTR*)&pszProto
+The propertysheet loads the basic contact protocol on creation for a better handling
+of owners (ICQ) protocol used for changing details on the server. Should also reduce database traffic.
+You can also call GetWindowLong(hDlg, DWLP_MSGRESULT) to get the protocol.
+This function returns TRUE on success or FALSE otherwise.
+*/
+#define PSM_GETBASEPROTO (WM_USER+905)
+
+#define INDEX_CURPAGE (-1)
+/* short helper macros
+*/
+#define PSGetBoldFont(hPsp, hFont) SNDMSG(GetParent((HWND)hPsp), PSM_GETBOLDFONT, (WPARAM)INDEX_CURPAGE, (LPARAM)(HFONT*)&hFont)
+#define PSGetContact(hPsp, hContact) SNDMSG(GetParent((HWND)hPsp), PSM_GETCONTACT, (WPARAM)INDEX_CURPAGE, (LPARAM)(HANDLE*)&hContact)
+#define PSGetBaseProto(hPsp, pszProto) SNDMSG(GetParent((HWND)hPsp), PSM_GETBASEPROTO, (WPARAM)INDEX_CURPAGE, (LPARAM)(LPCSTR*)&pszProto)
+
+/* PspIsLocked v0.1.1.0+
+Changed function a bit, because sometimes SNDMSG does not return the right value.
+Don't know why. But this works fine.
+*/
+static FORCEINLINE BOOLEAN PspIsLocked(HWND hPsp)
+{
+ HWND hPs = GetParent(hPsp);
+ return ((BOOLEAN)SendMessage((hPs), PSM_ISLOCKED, 0, 0) || GetWindowLongPtr((hPs), DWLP_MSGRESULT) != 0);
+}
+
+/* PSM_GETPAGEHWND v0.1.1.1+
+retrieve the windowhandle for a propertysheetpage identified by its id
+wParam=idDlg
+lParam=hInstance
+*/
+#define PSM_GETPAGEHWND (WM_USER+906)
+
+#define PSGetPageHandle(hPsp, idDlg, hInst) SNDMSG(GetParent((HWND)hPsp), PSM_GETPAGEHWND, (WPARAM)idDlg, (LPARAM)hInst)
+
+/* PSM_DLGMESSAGE v0.1.1.1+
+Send a message to a specified propertypage of the details dialog.
+This enables communication between propertypages without the need to know
+the window handles of each page.
+*/
+typedef struct TDlgCommand {
+ HINSTANCE hInst;
+ WORD idDlg;
+ WORD idDlgItem;
+ UINT uMsg;
+ WPARAM wParam;
+ LPARAM lParam;
+} DLGCOMMAND, *LPDLGCOMMAND;
+
+#define PSM_DLGMESSAGE (WM_USER+907)
+
+#define PSSendDlgMessage(hPsp, pDlgCmd) SNDMSG(GetParent((HWND)hPsp), PSM_DLGMESSAGE, NULL, (LPARAM)(LPDLGCOMMAND)pDlgCmd)
+
+
+/* PSM_ISAEROMODE v0.8.2.1+
+This message can be sent to the propertysheet (details dialog) to examine,
+whether the aero adaption mode is enabled or not. This message should be used in
+each propertysheet page's dialog procedure as follows:
+
+ ...
+ switch (msg) {
+ ...
+ case WM_CTLCOLORSTATIC:
+ case WM_CTLCOLORDLG:
+ if (PSIsAeroMode(hDlg))
+ return (INT_PTR)GetStockBrush(WHITE_BRUSH);
+ break;
+ ...
+
+This will draw a propertysheet page with white background, if aero adaption is enabled.
+wParam=not used
+lParam=(BOOL*)&bIsAero
+*/
+#define PSM_ISAEROMODE (WM_USER+908)
+static FORCEINLINE BOOLEAN PSIsAeroMode(HWND hPsp)
+{
+ BOOLEAN bIsAero;
+ SendMessage(GetParent(hPsp), PSM_ISAEROMODE, NULL, (LPARAM)&bIsAero);
+ return bIsAero;
+}
+
+/*************************************************************
+ * vCard Module
+ */
+
+/* UserInfo/vCardExport v0.1.0.4+
+*/
+#define MS_USERINFO_VCARD_IMPORT "UserInfo/vCard/Import"
+
+#define MS_USERINFO_VCARD_IMPORTALL "UserInfo/vCard/ImportAll"
+/* UserInfo/vCardImport v0.1.0.4+
+*/
+#define MS_USERINFO_VCARD_EXPORT "UserInfo/vCard/Export"
+
+/* UserInfo/vCardImport v0.1.0.4+
+*/
+#define MS_USERINFO_VCARD_EXPORTALL "UserInfo/vCard/ExportAll"
+
+/*************************************************************
+ * time Module
+ */
+
+/* UserInfo/LocalTime v0.1.0.3+
+Computes the local time for the desired contact and writes it to lpst.
+wParam=(WPARAM)hContact
+lParam=(LPSYSTEMTIME)lpst
+The service gets your windows box's local time, reads your timezoneinformation (Windows setting)
+and hContact's timezone from his user details. With these information contact's local time is computed
+considering daylightsaving time.
+Return values are TRUE for success and FALSE if anything went wrong.
+*/
+#define MS_USERINFO_LOCALTIME "UserInfo/LocalTime"
+
+/* UserInfo/LocalTime v0.7.0.1+
+This service provides the timezone information for a given contact
+as known by windows, too. All but the DaylightName and StandardName members
+of the class are filled out and can be used. The values are read
+from the windows registry and therefore keep up to date if the latest
+windows hotfixes are installed. There is no default API in the windows SDK
+to solve this problem.
+wParam=(WPARAM)hContact
+lParam=(TIME_ZONE_INFORMATION*)tzi
+Return values are 0 for success and 1 if no valid timezone is set for the contact.
+*/
+#define MS_USERINFO_TIMEZONEINFO "UserInfo/TimezoneInfo"
+
+/*************************************************************
+ * Reminder module
+ */
+
+/* UserInfo/Reminder/Check v0.1.0.4+
+This service checks if one of your contacts has birthday in the next few days
+wParam = lParam = not used
+*/
+#define MS_USERINFO_REMINDER_CHECK "UserInfo/Reminder/Check"
+
+
+/* UserInfo/Reminder/Check v0.1.1.1+
+This service creates a dialog, that lists all of the anniversaries
+wParam = lParam = not used
+*/
+#define MS_USERINFO_REMINDER_LIST "UserInfo/Reminder/List"
+
+
+/* UserInfo/Reminder/Check v0.1.2.16+
+This service compares birthday date which is set by the protocol module of each contact
+to the first found custom set birthday date. If a difference is detected, the user is asked
+whether to update the custom set birthday by the one of the protocol or not.
+
+If no custom birthday is set yet and the protocol contains a valid birthday, it is copied to
+primary custom module (e.g.: mBirthday or UserInfo).
+wParam = handle to single contact or NULL to backup all
+lParam = not used
+*/
+#define MS_USERINFO_REMINDER_AGGRASIVEBACKUP "UserInfo/Reminder/AggrassiveBackup"
+
+
+/* UserInfo/Refresh v0.7.0.1+
+This service calls PSS_GETINFO for all contacts in the contact list
+wParam = not used
+lParam = not used
+*/
+#define MS_USERINFO_REFRESH "UserInfo/Refresh"
+
+
+/*************************************************************
+ * Uinfobuttonclass module
+ */
+
+// button styles
+#define MBS_DEFBUTTON 0x00001000L // default button
+#define MBS_PUSHBUTTON 0x00002000L // toggle button
+#define MBS_FLAT 0x00004000L // flat button
+#define MBS_DOWNARROW 0x00008000L // has arrow on the right
+
+#define MBF_UNICODE 1
+#ifdef _UNICODE
+ #define MBF_TCHAR MBF_UNICODE
+#else
+ #define MBF_TCHAR 0
+#endif
+
+// BUTTONADDTOOLTIP
+// use lParam=MBF_UNICODE to set unicode tooltips
+// for lParam=0 the string is interpreted as ansi
+
+// message to explicitly translate the buttons text,
+// as it is not done by default translation routine
+// wParam=lParam=NULL
+#define BUTTONTRANSLATE (WM_USER+6)
+
+/* UserInfo/MsgBox v0.1.0.4+
+Slightly modified version of MButtonClass, to draw both text and icon in a button control
+*/
+#define UINFOBUTTONCLASS _T("UInfoButtonClass")
+
+/*************************************************************
+ * contact info module
+ */
+
+// additional information which can be retrieved with this service
+#define CNF_COPHONE 55 // returns company phone (string)
+#define CNF_COFAX 40 // returns company fax (string)
+#define CNF_COCELLULAR 41 // returns company cellular (string)
+#define CNF_COEMAIL 42 // returns company email address (string)
+
+/* CNF_BIRTHDATE v0.1.2.18+
+returns a formated string with the birthdate in it
+wParam - 1 for long dateformat, 0 for short dateformat
+lParam - CONTACTINFO structure as for all other fields, too
+returns 0 on success and 1 on failure
+*/
+#define CNF_BIRTHDATE 43 // returns date of birth (string)
+
+
+/*************************************************************
+ * extended integration module
+ */
+
+/* UserInfo/Homepage/OpenURL v0.1.2.19+
+This service reads the contact's homepage from UserInfo module or contact's protocol module
+and opens the default browser to show it.
+wParam=hContact - handle to contact whose homepage is to show
+lParam=not used
+*/
+#define MS_USERINFO_HOMEPAGE_OPENURL "UserInfo/Homepage/OpenURL"
+
+/*************************************************************
+ * extended database module
+ */
+
+/* DB/Contact/GetSettingStrEx v0.7.0.1+
+This service function reads a database setting from USERINFO module.
+If the setting does not exist, it is looked up in 'pszProto'.
+If 'hContact' points to a MetaContact, the setting is recursivly
+searched in all sub contacts, too, starting with the default contact,
+if the MetaContact does not directly provide the setting.
+This service can directly replace the default MS_DB_CONTACT_GETSETTING_STR
+for reading contact information from the database.
+There will be no difference for the user but the possible source of information.
+
+This service can be used to retrieve all kinds of settings!
+Some versions of the default MS_DB_CONTACT_GETSETTING_STR service return
+an error for BYTE, WORD and DWORD values if cgs.pValue->type is not 0.
+
+wParam = (WPARAM)(HANDLE)hContact
+lParam = (LPARAM)(DBCONTACTGETSETTING*)&cgs
+
+This service returns one of the results of MS_DB_CONTACT_GETSETTING_STR!
+*/
+#define MS_DB_CONTACT_GETSETTING_STR_EX "DB/Contact/GetSettingStrEx"
+
+static FORCEINLINE INT_PTR
+ DBGetContactSettingEx_Helper(
+ HANDLE hContact,
+ const char* pszProto,
+ const char* pszSetting,
+ DBVARIANT *dbv,
+ BYTE nType
+ )
+{
+ INT_PTR rc;
+ DBCONTACTGETSETTING cgs;
+
+ cgs.szModule = pszProto;
+ cgs.szSetting = pszSetting;
+ cgs.pValue = dbv;
+ dbv->type = nType;
+
+ rc = CallService(MS_DB_CONTACT_GETSETTING_STR_EX, (WPARAM)hContact, (LPARAM)&cgs);
+ if (rc == CALLSERVICE_NOTFOUND)
+ {
+ rc = CallService(MS_DB_CONTACT_GETSETTING_STR, (WPARAM)hContact, (LPARAM)&cgs);
+ }
+ return rc;
+}
+
+#define DBGetContactSettingStringEx(c,p,s,v) DBGetContactSettingEx_Helper(c,p,s,v,DBVT_ASCIIZ)
+#define DBGetContactSettingWStringEx(c,p,s,v) DBGetContactSettingEx_Helper(c,p,s,v,DBVT_WCHAR)
+#define DBGetContactSettingUTF8StringEx(c,p,s,v) DBGetContactSettingEx_Helper(c,p,s,v,DBVT_UTF8)
+
+#ifdef _UNICODE
+#define DBGetContactSettingTStringEx DBGetContactSettingWStringEx
+#else
+#define DBGetContactSettingTStringEx DBGetContactSettingStringEx
+#endif
+
+/*************************************************************/
+#endif /* _M_USERINFOEX_H_ */
+/*
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2009 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#ifndef _M_USERINFOEX_H_
+#define _M_USERINFOEX_H_
+/*************************************************************
+ * Interfaces Module
+ */
+
+// {9C23A24B-E6AA-43c6-B0B8-D6C36D2F7B57}
+#define MIID_UIUSERINFOEX { 0x9c23a24b, 0xe6aa, 0x43c6, { 0xb0, 0xb8, 0xd6, 0xc3, 0x6d, 0x2f, 0x7b, 0x57 } }
+// {17DBD7C9-450E-4000-BFB4-908A7EF4CE72}
+#define MIID_CONTACTINFO { 0x17dbd7c9, 0x450e, 0x4000, { 0xbf, 0xb4, 0x90, 0x8a, 0x7e, 0xf4, 0xce, 0x72 } }
+// {02E890BD-278D-4890-918D-AB2CF5DC50BD}
+#define MIID_REMINDER { 0x2e890bd, 0x278d, 0x4890, { 0x91, 0x8d, 0xab, 0x2c, 0xf5, 0xdc, 0x50, 0xbd } }
+
+
+/*************************************************************
+ * PropertySheetPage Module
+ */
+
+/* UserInfo/AddPage v0.1.0.0+
+If the hIcon member of te optiondialogpage is valid, the tree show it for nicer look.
+Otherwise the default icon is displayed for this treeitem.
+*/
+#ifndef ODPF_UNICODE
+ #define ODPF_UNICODE 8 // string fields in OPTIONSDIALOGPAGE are WCHAR*
+#endif
+#define ODPF_ICON 64 // the hIcon member of the option dialog page is valid
+
+/* Handling notifications v0.1.0.4+
+A dialogbox should call SetWindowLongPtr(hDlg, DWLP_MSGRESULT, PSP_CHANGED) on a PSN_INFOCHANGED notification if
+there are unsafed changes and apply button should keep enabled. Otherwise the changed status
+of the dialogbox is resetted as well as the changed status of the details dialog box itself if no page
+called this message. Because UserinfoEx now looks for changes in the settings of a user to keep the
+shown inforamtion up to date.
+*/
+#define PSP_CHANGED 2
+#define PSN_ICONCHANGED 2000
+
+/* PSM_GETBOLDFONT v0.1.0.3+
+wParam=NULL
+lParam=(HFONT*)&hFont
+get bold dialog font. wParam is pointer to a handle that retrieves the boldfont.
+You can also call GetWindowLong(hDlg, DWLP_MSGRESULT) to get the font.
+This function returns TRUE on success or FALSE otherwise.
+*/
+#ifndef PSM_GETBOLDFONT
+ #define PSM_GETBOLDFONT (WM_USER+102)
+#endif
+
+/* PSM_ISLOCKED v0.1.0.4+
+Returns state of propertysheet. If it is locked, The PSM_CHANGED messages sent by a propertysheetpage does not
+have any effect. To aVOID editcontrols, ... to check for changes on redrawing at a load of settings from database or
+if another propertysheetpage is selected, a plugin should check this state and skip those checks to reduce stressing
+the database if such a test if control's content changed does so.
+wParam=NULL
+lParam=NULL
+You can also call GetWindowLong(hDlg, DWLP_MSGRESULT) to get the handle.
+This function returns TRUE if the PropertySheet is locked or FALSE otherwise.
+*/
+#define PSM_ISLOCKED (WM_USER+901)
+
+/* PSM_GETCONTACT v0.1.0.4+
+You can get the handle to the contact the propertysheet is associated with by calling PSM_GETCONTACT
+to the parent of your propertysheetpage - the propertysheet.
+wParam=index or -1 for current item
+lParam=(HANDLE*)&hContact
+You can also call GetWindowLong(hDlg, DWLP_MSGRESULT) to get the handle.
+This function returns TRUE on success or FALSE otherwise.
+*/
+#define PSM_GETCONTACT (WM_USER+903)
+
+/* PSM_GETBASEPROTO v0.1.0.4+
+You can get a pointer to the basic protocol module by sending PSM_GETBASEPROTO to the parent of your propertysheetpage.
+wParam=index or -1 for current item
+lParam=(LPCSTR*)&pszProto
+The propertysheet loads the basic contact protocol on creation for a better handling
+of owners (ICQ) protocol used for changing details on the server. Should also reduce database traffic.
+You can also call GetWindowLong(hDlg, DWLP_MSGRESULT) to get the protocol.
+This function returns TRUE on success or FALSE otherwise.
+*/
+#define PSM_GETBASEPROTO (WM_USER+905)
+
+#define INDEX_CURPAGE (-1)
+/* short helper macros
+*/
+#define PSGetBoldFont(hPsp, hFont) SNDMSG(GetParent((HWND)hPsp), PSM_GETBOLDFONT, (WPARAM)INDEX_CURPAGE, (LPARAM)(HFONT*)&hFont)
+#define PSGetContact(hPsp, hContact) SNDMSG(GetParent((HWND)hPsp), PSM_GETCONTACT, (WPARAM)INDEX_CURPAGE, (LPARAM)(HANDLE*)&hContact)
+#define PSGetBaseProto(hPsp, pszProto) SNDMSG(GetParent((HWND)hPsp), PSM_GETBASEPROTO, (WPARAM)INDEX_CURPAGE, (LPARAM)(LPCSTR*)&pszProto)
+
+/* PspIsLocked v0.1.1.0+
+Changed function a bit, because sometimes SNDMSG does not return the right value.
+Don't know why. But this works fine.
+*/
+static FORCEINLINE BOOLEAN PspIsLocked(HWND hPsp)
+{
+ HWND hPs = GetParent(hPsp);
+ return ((BOOLEAN)SendMessage((hPs), PSM_ISLOCKED, 0, 0) || GetWindowLongPtr((hPs), DWLP_MSGRESULT) != 0);
+}
+
+/* PSM_GETPAGEHWND v0.1.1.1+
+retrieve the windowhandle for a propertysheetpage identified by its id
+wParam=idDlg
+lParam=hInstance
+*/
+#define PSM_GETPAGEHWND (WM_USER+906)
+
+#define PSGetPageHandle(hPsp, idDlg, hInst) SNDMSG(GetParent((HWND)hPsp), PSM_GETPAGEHWND, (WPARAM)idDlg, (LPARAM)hInst)
+
+/* PSM_DLGMESSAGE v0.1.1.1+
+Send a message to a specified propertypage of the details dialog.
+This enables communication between propertypages without the need to know
+the window handles of each page.
+*/
+typedef struct TDlgCommand {
+ HINSTANCE hInst;
+ WORD idDlg;
+ WORD idDlgItem;
+ UINT uMsg;
+ WPARAM wParam;
+ LPARAM lParam;
+} DLGCOMMAND, *LPDLGCOMMAND;
+
+#define PSM_DLGMESSAGE (WM_USER+907)
+
+#define PSSendDlgMessage(hPsp, pDlgCmd) SNDMSG(GetParent((HWND)hPsp), PSM_DLGMESSAGE, NULL, (LPARAM)(LPDLGCOMMAND)pDlgCmd)
+
+
+/* PSM_ISAEROMODE v0.8.2.1+
+This message can be sent to the propertysheet (details dialog) to examine,
+whether the aero adaption mode is enabled or not. This message should be used in
+each propertysheet page's dialog procedure as follows:
+
+ ...
+ switch (msg) {
+ ...
+ case WM_CTLCOLORSTATIC:
+ case WM_CTLCOLORDLG:
+ if (PSIsAeroMode(hDlg))
+ return (INT_PTR)GetStockBrush(WHITE_BRUSH);
+ break;
+ ...
+
+This will draw a propertysheet page with white background, if aero adaption is enabled.
+wParam=not used
+lParam=(BOOL*)&bIsAero
+*/
+#define PSM_ISAEROMODE (WM_USER+908)
+static FORCEINLINE BOOLEAN PSIsAeroMode(HWND hPsp)
+{
+ BOOLEAN bIsAero;
+ SendMessage(GetParent(hPsp), PSM_ISAEROMODE, NULL, (LPARAM)&bIsAero);
+ return bIsAero;
+}
+
+/*************************************************************
+ * vCard Module
+ */
+
+/* UserInfo/vCardExport v0.1.0.4+
+*/
+#define MS_USERINFO_VCARD_IMPORT "UserInfo/vCard/Import"
+
+#define MS_USERINFO_VCARD_IMPORTALL "UserInfo/vCard/ImportAll"
+/* UserInfo/vCardImport v0.1.0.4+
+*/
+#define MS_USERINFO_VCARD_EXPORT "UserInfo/vCard/Export"
+
+/* UserInfo/vCardImport v0.1.0.4+
+*/
+#define MS_USERINFO_VCARD_EXPORTALL "UserInfo/vCard/ExportAll"
+
+/*************************************************************
+ * time Module
+ */
+
+/* UserInfo/LocalTime v0.1.0.3+
+Computes the local time for the desired contact and writes it to lpst.
+wParam=(WPARAM)hContact
+lParam=(LPSYSTEMTIME)lpst
+The service gets your windows box's local time, reads your timezoneinformation (Windows setting)
+and hContact's timezone from his user details. With these information contact's local time is computed
+considering daylightsaving time.
+Return values are TRUE for success and FALSE if anything went wrong.
+*/
+#define MS_USERINFO_LOCALTIME "UserInfo/LocalTime"
+
+/* UserInfo/LocalTime v0.7.0.1+
+This service provides the timezone information for a given contact
+as known by windows, too. All but the DaylightName and StandardName members
+of the class are filled out and can be used. The values are read
+from the windows registry and therefore keep up to date if the latest
+windows hotfixes are installed. There is no default API in the windows SDK
+to solve this problem.
+wParam=(WPARAM)hContact
+lParam=(TIME_ZONE_INFORMATION*)tzi
+Return values are 0 for success and 1 if no valid timezone is set for the contact.
+*/
+#define MS_USERINFO_TIMEZONEINFO "UserInfo/TimezoneInfo"
+
+/*************************************************************
+ * Reminder module
+ */
+
+/* UserInfo/Reminder/Check v0.1.0.4+
+This service checks if one of your contacts has birthday in the next few days
+wParam = lParam = not used
+*/
+#define MS_USERINFO_REMINDER_CHECK "UserInfo/Reminder/Check"
+
+
+/* UserInfo/Reminder/Check v0.1.1.1+
+This service creates a dialog, that lists all of the anniversaries
+wParam = lParam = not used
+*/
+#define MS_USERINFO_REMINDER_LIST "UserInfo/Reminder/List"
+
+
+/* UserInfo/Reminder/Check v0.1.2.16+
+This service compares birthday date which is set by the protocol module of each contact
+to the first found custom set birthday date. If a difference is detected, the user is asked
+whether to update the custom set birthday by the one of the protocol or not.
+
+If no custom birthday is set yet and the protocol contains a valid birthday, it is copied to
+primary custom module (e.g.: mBirthday or UserInfo).
+wParam = handle to single contact or NULL to backup all
+lParam = not used
+*/
+#define MS_USERINFO_REMINDER_AGGRASIVEBACKUP "UserInfo/Reminder/AggrassiveBackup"
+
+
+/* UserInfo/Refresh v0.7.0.1+
+This service calls PSS_GETINFO for all contacts in the contact list
+wParam = not used
+lParam = not used
+*/
+#define MS_USERINFO_REFRESH "UserInfo/Refresh"
+
+
+/*************************************************************
+ * Uinfobuttonclass module
+ */
+
+// button styles
+#define MBS_DEFBUTTON 0x00001000L // default button
+#define MBS_PUSHBUTTON 0x00002000L // toggle button
+#define MBS_FLAT 0x00004000L // flat button
+#define MBS_DOWNARROW 0x00008000L // has arrow on the right
+
+#define MBF_UNICODE 1
+#ifdef _UNICODE
+ #define MBF_TCHAR MBF_UNICODE
+#else
+ #define MBF_TCHAR 0
+#endif
+
+// BUTTONADDTOOLTIP
+// use lParam=MBF_UNICODE to set unicode tooltips
+// for lParam=0 the string is interpreted as ansi
+
+// message to explicitly translate the buttons text,
+// as it is not done by default translation routine
+// wParam=lParam=NULL
+#define BUTTONTRANSLATE (WM_USER+6)
+
+/* UserInfo/MsgBox v0.1.0.4+
+Slightly modified version of MButtonClass, to draw both text and icon in a button control
+*/
+#define UINFOBUTTONCLASS _T("UInfoButtonClass")
+
+/*************************************************************
+ * contact info module
+ */
+
+// additional information which can be retrieved with this service
+#define CNF_COPHONE 55 // returns company phone (string)
+#define CNF_COFAX 40 // returns company fax (string)
+#define CNF_COCELLULAR 41 // returns company cellular (string)
+#define CNF_COEMAIL 42 // returns company email address (string)
+
+/* CNF_BIRTHDATE v0.1.2.18+
+returns a formated string with the birthdate in it
+wParam - 1 for long dateformat, 0 for short dateformat
+lParam - CONTACTINFO structure as for all other fields, too
+returns 0 on success and 1 on failure
+*/
+#define CNF_BIRTHDATE 43 // returns date of birth (string)
+
+
+/*************************************************************
+ * extended integration module
+ */
+
+/* UserInfo/Homepage/OpenURL v0.1.2.19+
+This service reads the contact's homepage from UserInfo module or contact's protocol module
+and opens the default browser to show it.
+wParam=hContact - handle to contact whose homepage is to show
+lParam=not used
+*/
+#define MS_USERINFO_HOMEPAGE_OPENURL "UserInfo/Homepage/OpenURL"
+
+/*************************************************************
+ * extended database module
+ */
+
+/* DB/Contact/GetSettingStrEx v0.7.0.1+
+This service function reads a database setting from USERINFO module.
+If the setting does not exist, it is looked up in 'pszProto'.
+If 'hContact' points to a MetaContact, the setting is recursivly
+searched in all sub contacts, too, starting with the default contact,
+if the MetaContact does not directly provide the setting.
+This service can directly replace the default MS_DB_CONTACT_GETSETTING_STR
+for reading contact information from the database.
+There will be no difference for the user but the possible source of information.
+
+This service can be used to retrieve all kinds of settings!
+Some versions of the default MS_DB_CONTACT_GETSETTING_STR service return
+an error for BYTE, WORD and DWORD values if cgs.pValue->type is not 0.
+
+wParam = (WPARAM)(HANDLE)hContact
+lParam = (LPARAM)(DBCONTACTGETSETTING*)&cgs
+
+This service returns one of the results of MS_DB_CONTACT_GETSETTING_STR!
+*/
+#define MS_DB_CONTACT_GETSETTING_STR_EX "DB/Contact/GetSettingStrEx"
+
+static FORCEINLINE INT_PTR
+ DBGetContactSettingEx_Helper(
+ HANDLE hContact,
+ const char* pszProto,
+ const char* pszSetting,
+ DBVARIANT *dbv,
+ BYTE nType
+ )
+{
+ INT_PTR rc;
+ DBCONTACTGETSETTING cgs;
+
+ cgs.szModule = pszProto;
+ cgs.szSetting = pszSetting;
+ cgs.pValue = dbv;
+ dbv->type = nType;
+
+ rc = CallService(MS_DB_CONTACT_GETSETTING_STR_EX, (WPARAM)hContact, (LPARAM)&cgs);
+ if (rc == CALLSERVICE_NOTFOUND)
+ {
+ rc = CallService(MS_DB_CONTACT_GETSETTING_STR, (WPARAM)hContact, (LPARAM)&cgs);
+ }
+ return rc;
+}
+
+#define DBGetContactSettingStringEx(c,p,s,v) DBGetContactSettingEx_Helper(c,p,s,v,DBVT_ASCIIZ)
+#define DBGetContactSettingWStringEx(c,p,s,v) DBGetContactSettingEx_Helper(c,p,s,v,DBVT_WCHAR)
+#define DBGetContactSettingUTF8StringEx(c,p,s,v) DBGetContactSettingEx_Helper(c,p,s,v,DBVT_UTF8)
+
+#ifdef _UNICODE
+#define DBGetContactSettingTStringEx DBGetContactSettingWStringEx
+#else
+#define DBGetContactSettingTStringEx DBGetContactSettingStringEx
+#endif
+
+/*************************************************************/
+#endif /* _M_USERINFOEX_H_ */
diff --git a/Plugins/spellchecker/sdk/m_variables.h b/Plugins/spellchecker/sdk/m_variables.h
new file mode 100644
index 0000000..1264643
--- /dev/null
+++ b/Plugins/spellchecker/sdk/m_variables.h
@@ -0,0 +1,719 @@
+/*
+ Variables Plugin for Miranda-IM (www.miranda-im.org)
+ Copyright 2003-2006 P. Boon
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#ifndef __M_VARS
+#define __M_VARS
+
+#if !defined(_TCHAR_DEFINED)
+#include <tchar.h>
+#endif
+
+#ifndef VARIABLES_NOHELPER
+#include <m_button.h>
+#endif
+
+#ifndef SIZEOF
+#include <win2k.h>
+#endif
+
+// --------------------------------------------------------------------------
+// Memory management
+// --------------------------------------------------------------------------
+
+// Release memory that was allocated by the Variables plugin, e.g. returned
+// strings.
+
+#define MS_VARS_FREEMEMORY "Vars/FreeMemory"
+
+// Parameters:
+// ------------------------
+// wParam = (WPARAM)(void *)pntr
+// Pointer to memory that was allocated by the Variables plugin (e.g. a
+// returned string) (can be NULL).
+// lParam = 0
+
+// Return Value:
+// ------------------------
+// Does return 0 on success, nozero otherwise.
+
+// Note: Do only use this service to free memory that was *explicitliy*
+// stated that it should be free with this service.
+
+
+
+#define MS_VARS_GET_MMI "Vars/GetMMI"
+
+// Get Variable's RTL/CRT function poiners to malloc(), free() and
+// realloc().
+
+// Parameters:
+// ------------------------
+// wParam = 0
+// lParam = (LPARAM) &MM_INTERFACE
+// Pointer to a memory manager interface struct (see m_system.h).
+
+// Return Value:
+// ------------------------
+// Returns 0 on success, nozero otherwise
+
+// Note: Works exactly the same as the MS_SYSTEM_GET_MMI service
+// service of m_system.h.
+
+// Helper function for easy using:
+#ifndef VARIABLES_NOHELPER
+__inline static void variables_free(void *pntr) {
+
+ CallService(MS_VARS_FREEMEMORY, (WPARAM)pntr, 0);
+}
+#endif
+
+
+
+// --------------------------------------------------------------------------
+// String formatting
+// --------------------------------------------------------------------------
+
+#define MS_VARS_FORMATSTRING "Vars/FormatString"
+
+// This service can be used to parse tokens in a text. The tokens will be
+// replaced by their resolved values. A token can either be a field or a
+// function. A field takes no arguments and is represented between
+// %-characters, e.g. "%winampsong%". A function can take any number of
+// arguments and is represented by a ? or !-character followed by the name
+// of the function and a list of arguments, e.g. "?add(1,2)".
+
+// Parameters:
+// ------------------------
+// wParam = (WPARAM)(FORMATINFO *)&fi
+// See below.
+// lParam = 0
+
+// Return Value:
+// ------------------------
+// Returns a pointer to the resolved string or NULL in case of an error.
+
+// Note: The returned pointer needs to be freed using MS_VARS_FREEMEMORY.
+
+typedef struct {
+ int cbSize; // Set this to sizeof(FORMATINFO).
+ int flags; // Flags to use (see FIF_* below).
+ union {
+ char *szFormat; // Text in which the tokens will be replaced (can't be
+ // NULL).
+ WCHAR *wszFormat;
+ TCHAR *tszFormat;
+ };
+ union {
+ char *szExtraText; // Extra, context-specific string (can be NULL) ->
+ // The field "extratext" will be replaced by this
+ // string. (Previously szSource).
+ WCHAR *wszExtraText;
+ TCHAR *tszExtraText;
+ };
+ HANDLE hContact; // Handle to contact (can be NULL) -> The field "subject"
+ // represents this contact.
+ int pCount; // (output) Number of succesful parsed tokens, needs to be set
+ // to 0 before the call
+ int eCount; // (output) Number of failed tokens, needs to be set to 0
+ // before the call
+ union {
+ char **szaTemporaryVars; // Temporary variables valid only in the duration of the format call
+ TCHAR **tszaTemporaryVars; // By pos: [i] is var name, [i + 1] is var value
+ WCHAR **wszaTemporaryVars;
+ };
+ int cbTemporaryVarsSize; // Number of elements in szaTemporaryVars array
+
+} FORMATINFO;
+
+#define FORMATINFOV2_SIZE 28
+
+// Possible flags:
+#define FIF_UNICODE 0x01 // Expects and returns unicode text (WCHAR*).
+
+#if defined(UNICODE) || defined(_UNICODE)
+#define FIF_TCHAR FIF_UNICODE // Strings in structure are TCHAR*.
+#else
+#define FIF_TCHAR 0
+#endif
+
+// Helper functions for easy using:
+
+// Helper #1: variables_parse
+// ------------------------
+// The returned string needs to be freed using MS_VARS_FREEMEMORY.
+
+#ifndef VARIABLES_NOHELPER
+__inline static TCHAR *variables_parse(TCHAR *tszFormat, TCHAR *tszExtraText, HANDLE hContact) {
+
+ FORMATINFO fi;
+
+ ZeroMemory(&fi, sizeof(fi));
+ fi.cbSize = sizeof(fi);
+ fi.tszFormat = tszFormat;
+ fi.tszExtraText = tszExtraText;
+ fi.hContact = hContact;
+ fi.flags = FIF_TCHAR;
+
+ return (TCHAR *)CallService(MS_VARS_FORMATSTRING, (WPARAM)&fi, 0);
+}
+#endif
+
+__inline static TCHAR *variables_parse_ex(TCHAR *tszFormat, TCHAR *tszExtraText, HANDLE hContact,
+ TCHAR **tszaTemporaryVars, int cbTemporaryVarsSize) {
+
+ FORMATINFO fi;
+
+ ZeroMemory(&fi, sizeof(fi));
+ fi.cbSize = sizeof(fi);
+ fi.tszFormat = tszFormat;
+ fi.tszExtraText = tszExtraText;
+ fi.hContact = hContact;
+ fi.flags = FIF_TCHAR;
+ fi.tszaTemporaryVars = tszaTemporaryVars;
+ fi.cbTemporaryVarsSize = cbTemporaryVarsSize;
+
+ return (TCHAR *)CallService(MS_VARS_FORMATSTRING, (WPARAM)&fi, 0);
+}
+// Helper #2: variables_parsedup
+// ------------------------
+// Returns a _strdup()'ed copy of the unparsed string when Variables is not
+// installed, returns a strdup()'ed copy of the parsed result otherwise.
+
+// Note: The returned pointer needs to be released using your own free().
+
+#ifndef VARIABLES_NOHELPER
+__inline static TCHAR *variables_parsedup(TCHAR *tszFormat, TCHAR *tszExtraText, HANDLE hContact) {
+
+ if (ServiceExists(MS_VARS_FORMATSTRING)) {
+ FORMATINFO fi;
+ TCHAR *tszParsed, *tszResult;
+
+ ZeroMemory(&fi, sizeof(fi));
+ fi.cbSize = sizeof(fi);
+ fi.tszFormat = tszFormat;
+ fi.tszExtraText = tszExtraText;
+ fi.hContact = hContact;
+ fi.flags |= FIF_TCHAR;
+ tszParsed = (TCHAR *)CallService(MS_VARS_FORMATSTRING, (WPARAM)&fi, 0);
+ if (tszParsed) {
+ tszResult = _tcsdup(tszParsed);
+ CallService(MS_VARS_FREEMEMORY, (WPARAM)tszParsed, 0);
+ return tszResult;
+ }
+ }
+ return tszFormat?_tcsdup(tszFormat):tszFormat;
+}
+
+__inline static TCHAR *variables_parsedup_ex(TCHAR *tszFormat, TCHAR *tszExtraText, HANDLE hContact,
+ TCHAR **tszaTemporaryVars, int cbTemporaryVarsSize) {
+
+ if (ServiceExists(MS_VARS_FORMATSTRING)) {
+ FORMATINFO fi;
+ TCHAR *tszParsed, *tszResult;
+
+ ZeroMemory(&fi, sizeof(fi));
+ fi.cbSize = sizeof(fi);
+ fi.tszFormat = tszFormat;
+ fi.tszExtraText = tszExtraText;
+ fi.hContact = hContact;
+ fi.flags |= FIF_TCHAR;
+ fi.tszaTemporaryVars = tszaTemporaryVars;
+ fi.cbTemporaryVarsSize = cbTemporaryVarsSize;
+ tszParsed = (TCHAR *)CallService(MS_VARS_FORMATSTRING, (WPARAM)&fi, 0);
+ if (tszParsed) {
+ tszResult = _tcsdup(tszParsed);
+ CallService(MS_VARS_FREEMEMORY, (WPARAM)tszParsed, 0);
+ return tszResult;
+ }
+ }
+ return tszFormat?_tcsdup(tszFormat):tszFormat;
+}
+#endif
+
+
+
+// --------------------------------------------------------------------------
+// Register tokens
+// --------------------------------------------------------------------------
+
+// Plugins can define tokens which will be parsed by the Variables plugin.
+
+#define MS_VARS_REGISTERTOKEN "Vars/RegisterToken"
+
+// With this service you can define your own token. The newly added tokens
+// using this service are taken into account on every call to
+// MS_VARS_FORMATSTRING.
+
+// Parameters:
+// ------------------------
+// wParam = 0
+// lParam = (LPARAM)(TOKENREGISTER*)&tr
+// See below.
+
+// Return Value:
+// ------------------------
+// Returns 0 on success, nonzero otherwise. Existing tokens will be
+// 'overwritten' if registered twice.
+
+// Needed for szService and parseFunction:
+typedef struct {
+ int cbSize; // You need to check if this is >=sizeof(ARGUMENTSINFO)
+ // (already filled in).
+ FORMATINFO *fi; // Arguments passed to MS_VARS_FORMATSTRING.
+ unsigned int argc; // Number of elements in the argv array.
+ union {
+ char **argv; // Argv[0] will be the token name, the following elements
+ // are the additional arguments.
+ WCHAR **wargv; // If the registered token was registered as a unicode
+ // token, wargv should be accessed.
+ TCHAR **targv;
+ };
+ int flags; // (output) You can set flags here (initially 0), use the
+ // AIF_* flags (see below).
+} ARGUMENTSINFO;
+
+// Available flags for ARGUMENTSINFO:
+// Set the flags of the ARGUMENTSINFO struct to any of these to influence
+// further parsing.
+#define AIF_DONTPARSE 0x01 // Don't parse the result of this function,
+ // usually the result of a token is parsed
+ // again, if the `?` is used as a function
+ // character.
+#define AIF_FALSE 0x02 // The function returned logical false.
+
+// Definition of parse/cleanup functions:
+typedef char* (*VARPARSEFUNCA)(ARGUMENTSINFO *ai);
+typedef WCHAR* (*VARPARSEFUNCW)(ARGUMENTSINFO *ai);
+typedef void (*VARCLEANUPFUNCA)(char *szReturn);
+typedef void (*VARCLEANUPFUNCW)(WCHAR *wszReturn);
+
+#if defined(UNICODE) || defined(_UNICODE)
+#define VARPARSEFUNC VARPARSEFUNCW
+#define VARCLEANUPFUNC VARCLEANUPFUNCW
+#else
+#define VARPARSEFUNC VARPARSEFUNCA
+#define VARCLEANUPFUNC VARCLEANUPFUNCA
+#endif
+
+typedef struct {
+ int cbSize; // Set this to sizeof(TOKENREGISTER).
+ union {
+ char *szTokenString; // Name of the new token to be created, without %,
+ // ?, ! etc. signs (can't be NULL).
+ WCHAR *wszTokenString;
+ TCHAR *tszTokenString;
+ };
+ union {
+ char *szService; // Name of a service that is used to request the
+ // token's value, if no service is used, a function
+ // and TRF_PARSEFUNC must be used.
+ VARPARSEFUNCA parseFunction; // See above, use with TRF_PARSEFUNC.
+ VARPARSEFUNCW parseFunctionW;
+ VARPARSEFUNC parseFunctionT;
+ };
+ union {
+ char *szCleanupService; // Name of a service to be called when the
+ // memory allocated in szService can be freed
+ // (only used when flag VRF_CLEANUP is set,
+ // else set this to NULL).
+ VARCLEANUPFUNCA cleanupFunction; // See above, use with TRF_CLEANUPFUNC.
+ VARCLEANUPFUNCW cleanupFunctionW;
+ VARCLEANUPFUNC cleanupFunctionT;
+ };
+ char *szHelpText; // Help info shown in help dialog (can be NULL). Has to
+ // be in the following format:
+ // "subject\targuments\tdescription"
+ // (Example: "math\t(x, y ,...)\tx + y + ..."), or:
+ // "subject\tdescription"
+ // (Example: "miranda\tPath to the Miranda-IM
+ // executable").
+ // Note: subject and description are translated by
+ // Variables.
+ int memType; // Describes which method Varibale's plugin needs to use to
+ // free the returned buffer, use one of the VR_MEM_* values
+ // (see below). Only valid if the flag VRF_FREEMEM is set,
+ // use TR_MEM_OWNER otherwise).
+ int flags; // Flags to use (see below), one of TRF_* (see below).
+} TOKENREGISTER;
+
+// Available Memory Storage Types:
+// These values describe which method Variables Plugin will use to free the
+// buffer returned by the parse function or service
+#define TR_MEM_VARIABLES 1 // Memory is allocated using the functions
+ // retrieved by MS_VARS_GET_MMI.
+#define TR_MEM_MIRANDA 2 // Memory is allocated using Miranda's Memory
+ // Manager Interface (using the functions
+ // returned by MS_SYSTEM_GET_MMI), if
+ // VRF_FREEMEM is set, the memory will be
+ // freed by Variables.
+#define TR_MEM_OWNER 3 // Memory is owned by the calling plugin
+ // (can't be freed by Variables Plugin
+ // automatically). This should be used if
+ // VRF_FREEMEM is not specified in the flags.
+
+// Available Flags for TOKENREGISTER:
+#define TRF_FREEMEM 0x01 // Variables Plugin will automatically free the
+ // pointer returned by the parse function or
+ // service (which method it will us is
+ // specified in memType -> see above).
+#define TRF_CLEANUP 0x02 // Call cleanup service or function, notifying
+ // that the returned buffer can be freed.
+ // Normally you should use either TRF_FREEMEM
+ // or TRF_CLEANUP.
+#define TRF_PARSEFUNC 0x40 // parseFunction will be used instead of a
+ // service.
+#define TRF_CLEANUPFUNC 0x80 // cleanupFunction will be used instead of a
+ // service.
+#define TRF_USEFUNCS TRF_PARSEFUNC|TRF_CLEANUPFUNC
+#define TRF_UNPARSEDARGS 0x04 // Provide the arguments for the parse
+ // function in their raw (unparsed) form.
+ // By default, arguments are parsed before
+ // presenting them to the parse function.
+#define TRF_FIELD 0x08 // The token can be used as a %field%.
+#define TRF_FUNCTION 0x10 // The token can be used as a ?function().
+ // Normally you should use either TRF_FIELD or
+ // TRF_FUNCTION.
+#define TRF_UNICODE 0x20 // Strings in structure are unicode (WCHAR*).
+ // In this case, the strings pointing to the
+ // arguments in the ARGUMENTS struct are
+ // unicode also. The returned buffer is
+ // expected to be unicode also, and the
+ // unicode parse and cleanup functions are
+ // called.
+
+#if defined(UNICODE) || defined(_UNICODE)
+#define TRF_TCHAR TRF_UNICODE // Strings in structure are TCHAR*.
+#else
+#define TRF_TCHAR 0
+#endif
+
+// Deprecated:
+#define TRF_CALLSVC TRF_CLEANUP
+
+// Callback Service (szService) / parseFunction:
+// ------------------------
+// Service that is called automatically by the Variable's Plugin to resolve a
+// registered variable.
+
+// Parameters:
+// wParam = 0
+// lParam = (LPARAM)(ARGUMENTSINFO *)&ai
+// see above
+
+// Return Value:
+// Needs to return the pointer to a dynamically allocacated string or NULL.
+// A return value of NULL is regarded as an error (eCount will be increaded).
+// Flags in the ARGUMENTSINFO struct can be set (see above).
+
+// Callback Service (szCallbackService) / cleanupFunction:
+// ------------------------
+// This service is called when the memory that was allocated by the parse
+// function or service can be freed. Note: It will only be called when the
+// flag VRF_CLEANUP of TOKENREGISTER is set.
+
+// Parameters:
+// wParam = 0
+// lParam = (LPARAM)(char *)&res
+// Result from parse function or service (pointer to a string).
+
+// Return Value:
+// Should return 0 on success.
+
+
+
+// --------------------------------------------------------------------------
+// Show the help dialog
+// --------------------------------------------------------------------------
+
+// Plugins can invoke Variables' help dialog which can be used for easy input
+// by users.
+
+#define MS_VARS_SHOWHELPEX "Vars/ShowHelpEx"
+
+// This service can be used to open the help dialog of Variables. This dialog
+// provides easy input for the user and/or information about the available
+// tokens.
+
+// Parameters:
+// ------------------------
+// wParam = (WPARAM)(HWND)hwndParent
+// lParam = (LPARAM)(VARHELPINFO)&vhi
+// See below.
+
+// Return Value:
+// ------------------------
+// Returns 0 on succes, any other value on error.
+
+typedef struct {
+ int cbSize; // Set to sizeof(VARHELPINFO).
+ FORMATINFO *fi; // Used for both input and output. If this pointer is not
+ // NULL, the information is used as the initial values for
+ // the dialog.
+ HWND hwndCtrl; // Used for both input and output. The window text of this
+ // window will be read and used as the initial input of the
+ // input dialog. If the user presses the OK button the window
+ // text of this window will be set to the text of the input
+ // field and a EN_CHANGE message via WM_COMMAND is send to
+ // this window. (Can be NULL).
+ char *szSubjectDesc; // The description of the %subject% token will be set
+ // to this text, if not NULL. This is translated
+ // automatically.
+ char *szExtraTextDesc; // The description of the %extratext% token will be
+ // set to this text, if not NULL. This is translated
+ // automatically.
+ int flags; // Flags, see below.
+} VARHELPINFO;
+
+
+// Flags for VARHELPINFO
+#define VHF_TOKENS 0x00000001 // Create a dialog with the list of
+ // tokens
+#define VHF_INPUT 0x00000002 // Create a dialog with an input
+ // field (this contains the list of
+ // tokens as well).
+#define VHF_SUBJECT 0x00000004 // Create a dialog to select a
+ // contact for the %subject% token.
+#define VHF_EXTRATEXT 0x00000008 // Create a dialog to enter a text
+ // for the %extratext% token.
+#define VHF_HELP 0x00000010 // Create a dialog with help info.
+#define VHF_HIDESUBJECTTOKEN 0x00000020 // Hide the %subject% token in the
+ // list of tokens.
+#define VHF_HIDEEXTRATEXTTOKEN 0x00000040 // Hide the %extratext% token in
+ // the list of tokens.
+#define VHF_DONTFILLSTRUCT 0x00000080 // Don't fill the struct with the
+ // new information if OK is pressed
+#define VHF_FULLFILLSTRUCT 0x00000100 // Fill all members of the struct
+ // when OK is pressed. By default
+ // only szFormat is set. With this
+ // flag on, hContact and
+ // szExtraText are also set.
+#define VHF_SETLASTSUBJECT 0x00000200 // Set the last contact that was
+ // used in the %subject% dialog in
+ // case fi.hContact is NULL.
+
+// Predefined flags
+#define VHF_FULLDLG VHF_INPUT|VHF_SUBJECT|VHF_EXTRATEXT|VHF_HELP
+#define VHF_SIMPLEDLG VHF_INPUT|VHF_HELP
+#define VHF_NOINPUTDLG VHF_TOKENS|VHF_HELP
+
+// If the service fills information in the struct for szFormat or szExtraText,
+// these members must be free'd using the free function of Variables.
+// If wParam==NULL, the dialog is created modeless. Only one dialog can be
+// shown at the time.
+// If both hwndCtrl and fi are NULL, the user input will not be retrievable.
+// In this case, the dialog is created with only a "Close" button, instead of
+// the "OK" and "Cancel" buttons.
+// In case of modeless dialog and fi != NULL, please make sure this pointer
+// stays valid while the dialog is open.
+
+// Helper function for easy use in standard case:
+#ifndef VARIABLES_NOHELPER
+__inline static int variables_showhelp(HWND hwndDlg, UINT uIDEdit, int flags, char *szSubjectDesc, char *szExtraDesc) {
+
+ VARHELPINFO vhi;
+
+ ZeroMemory(&vhi, sizeof(VARHELPINFO));
+ vhi.cbSize = sizeof(VARHELPINFO);
+ if (flags == 0) {
+ flags = VHF_SIMPLEDLG;
+ }
+ vhi.flags = flags;
+ vhi.hwndCtrl = GetDlgItem(hwndDlg, uIDEdit);
+ vhi.szSubjectDesc = szSubjectDesc;
+ vhi.szExtraTextDesc = szExtraDesc;
+
+ return CallService(MS_VARS_SHOWHELPEX, (WPARAM)hwndDlg, (LPARAM)&vhi);
+}
+#endif
+
+
+#define MS_VARS_GETSKINITEM "Vars/GetSkinItem"
+
+// This service can be used to get the icon you can use for example on the
+// Variables help button in your options screen. You can also get the tooltip
+// text to use with such a button. If icon library is available the icon will
+// be retrieved from icon library manager, otherwise the default is returned.
+
+// Parameters:
+// ------------------------
+// wParam = (WPARAM)0
+// lParam = (LPARAM)VSI_* (see below)
+
+// Return Value:
+// ------------------------
+// Depends on the information to retrieve (see below).
+
+// VSI_ constants
+#define VSI_HELPICON 1 // Can be used on the button accessing the
+ // Variables help dialog. Returns (HICON)hIcon on
+ // success or NULL on failure;
+#define VSI_HELPTIPTEXT 2 // Returns the tooltip text you can use for the
+ // help button. Returns (char *)szTipText, a
+ // static, translated buffer containing the help
+ // text or NULL on error.
+
+// Helper to set the icon on a button accessing the help dialog.
+// Preferably a 16x14 MButtonClass control, but it works on a standard
+// button control as well. If no icon is availble (because of old version of
+// Variables) the string "V" is shown on the button. If Variables is not
+// available, the button will be hidden.
+#ifndef VARIABLES_NOHELPER
+__inline static int variables_skin_helpbutton(HWND hwndDlg, UINT uIDButton) {
+
+ int res;
+ HICON hIcon;
+ TCHAR tszClass[32];
+
+ hIcon = NULL;
+ res = 0;
+ if (ServiceExists(MS_VARS_GETSKINITEM))
+ hIcon = (HICON)CallService(MS_VARS_GETSKINITEM, 0, (LPARAM)VSI_HELPICON);
+
+ GetClassName(GetDlgItem(hwndDlg, uIDButton), tszClass, SIZEOF(tszClass));
+ if (!_tcscmp(tszClass, _T("Button"))) {
+ if (hIcon != NULL) {
+ SetWindowLongPtr(GetDlgItem(hwndDlg, uIDButton), GWL_STYLE, GetWindowLongPtr(GetDlgItem(hwndDlg, uIDButton), GWL_STYLE)|BS_ICON);
+ SendMessage(GetDlgItem(hwndDlg, uIDButton), BM_SETIMAGE, (WPARAM)IMAGE_ICON, (LPARAM)hIcon);
+ }
+ else {
+ SetWindowLongPtr(GetDlgItem(hwndDlg, uIDButton), GWL_STYLE, GetWindowLongPtr(GetDlgItem(hwndDlg, uIDButton), GWL_STYLE)&~BS_ICON);
+ SetDlgItemText(hwndDlg, uIDButton, _T("V"));
+ }
+ }
+ else if (!_tcscmp(tszClass, MIRANDABUTTONCLASS)) {
+ if (hIcon != NULL) {
+ char *szTipInfo = NULL;
+
+ SendMessage(GetDlgItem(hwndDlg, uIDButton), BM_SETIMAGE, (WPARAM)IMAGE_ICON, (LPARAM)hIcon);
+ if (ServiceExists(MS_VARS_GETSKINITEM))
+ szTipInfo = (char *)CallService(MS_VARS_GETSKINITEM, 0, (LPARAM)VSI_HELPTIPTEXT);
+
+ if (szTipInfo == NULL)
+ szTipInfo = Translate("Open String Formatting Help");
+
+ SendMessage(GetDlgItem(hwndDlg, uIDButton), BUTTONADDTOOLTIP, (WPARAM)szTipInfo, 0);
+ SendDlgItemMessage(hwndDlg, uIDButton, BUTTONSETASFLATBTN, 0, 0);
+ }
+ else SetDlgItemText(hwndDlg, uIDButton, _T("V"));
+ }
+ else res = -1;
+
+ ShowWindow(GetDlgItem(hwndDlg, uIDButton), ServiceExists(MS_VARS_FORMATSTRING));
+
+ return res;
+}
+#endif
+
+
+#define MS_VARS_SHOWHELP "Vars/ShowHelp"
+
+// WARNING: This service is obsolete, please use MS_VARS_SHOWHELPEX
+
+// Shows a help dialog where all possible tokens are displayed. The tokens
+// are explained on the dialog, too. The user can edit the initial string and
+// insert as many tokens as he likes.
+
+// Parameters:
+// ------------------------
+// wParam = (HWND)hwndEdit
+// Handle to an edit control in which the modified string
+// should be inserted (When the user clicks OK in the dialog the edited
+// string will be set to hwndEdit) (can be NULL).
+// lParam = (char *)pszInitialString
+// String that the user is provided with initially when
+// the dialog gets opened (If this is NULL then the current text in the
+// hwndEdit edit control will be used) (can be NULL).
+
+// Return Value:
+// ------------------------
+// Returns the handle to the help dialog (HWND).
+
+// Note: Only one help dialog can be opened at a time. When the dialog gets
+// closed an EN_CHANGE of the edit controll will be triggered because the
+// contents were updated. (Only when user selected OK).
+
+// Example:
+// CallService(MS_VARS_SHOWHELP, (WPARAM)hwndEdit, (LPARAM)"some initial text");
+
+// --------------------------------------------------------------------------
+// Retrieve a contact's HANDLE given a string
+// --------------------------------------------------------------------------
+
+#define MS_VARS_GETCONTACTFROMSTRING "Vars/GetContactFromString"
+
+// Searching for contacts in the database. You can find contacts in db by
+// searching for their name, e.g first name.
+
+// Parameters:
+// ------------------------
+// wParam = (WPARAM)(CONTACTSINFO *)&ci
+// See below.
+// lParam = 0
+
+// Return Value:
+// ------------------------
+// Returns number of contacts found matching the given string representation.
+// The hContacts array of CONTACTSINFO struct contains these hContacts after
+// the call.
+
+// Note: The hContacts array needs to be freed after use using
+// MS_VARS_FREEMEMORY.
+
+typedef struct {
+ int cbSize; // Set this to sizeof(CONTACTSINFO).
+ union {
+ char *szContact; // String to search for, e.g. last name (can't be NULL).
+ WCHAR *wszContact;
+ TCHAR *tszContact;
+ };
+ HANDLE *hContacts; // (output) Array of contacts found.
+ DWORD flags; // Contact details that will be matched with the search
+ // string (flags can be combined).
+} CONTACTSINFO;
+
+// Possible flags:
+#define CI_PROTOID 0x00000001 // The contact in the string is encoded
+ // in the format <PROTOID:UNIQUEID>, e.g.
+ // <ICQ:12345678>.
+#define CI_NICK 0x00000002 // Search nick names.
+#define CI_LISTNAME 0x00000004 // Search custom names shown in contact
+ // list.
+#define CI_FIRSTNAME 0x00000008 // Search contact's first names (contact
+ // details).
+#define CI_LASTNAME 0x00000010 // Search contact's last names (contact
+ // details).
+#define CI_EMAIL 0x00000020 // Search contact's email adresses
+ // (contact details).
+#define CI_UNIQUEID 0x00000040 // Search unique ids of the contac, e.g.
+ // UIN.
+#define CI_CNFINFO 0x40000000 // Searches one of the CNF_* flags (set
+ // flags to CI_CNFINFO|CNF_X), only one
+ // CNF_ type possible
+#define CI_UNICODE 0x80000000 // tszContact is a unicode string
+ // (WCHAR*).
+
+#if defined(UNICODE) || defined(_UNICODE)
+#define CI_TCHAR CI_UNICODE // Strings in structure are TCHAR*.
+#else
+#define CI_TCHAR 0
+#endif
+
+
+
+#endif //__M_VARS
diff --git a/Plugins/spellchecker/spellchecker.cpp b/Plugins/spellchecker/spellchecker.cpp
new file mode 100644
index 0000000..f77626f
--- /dev/null
+++ b/Plugins/spellchecker/spellchecker.cpp
@@ -0,0 +1,2322 @@
+/*
+Copyright (C) 2006-2010 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+#include "commons.h"
+
+
+// Prototypes ///////////////////////////////////////////////////////////////////////////
+
+
+PLUGININFOEX pluginInfo={
+ sizeof(PLUGININFOEX),
+ "Spell Checker",
+ PLUGIN_MAKE_VERSION(0,2,6,0),
+ "Spell checker for the message windows. Uses Hunspell to do the checking.",
+ "Ricardo Pescuma Domenecci, FREAK_THEMIGHTY",
+ "pescuma@miranda-im.org",
+ "© 2006-2010 Ricardo Pescuma Domenecci",
+ "http://pescuma.org/miranda/spellchecker",
+ UNICODE_AWARE,
+ 0, //doesn't replace anything built-in
+#ifdef WIN64
+ { 0x77c4713f, 0xd544, 0x4b53, { 0xbf, 0x1f, 0xe, 0x47, 0x3e, 0xe9, 0xc4, 0x8c } } // {77C4713F-D544-4b53-BF1F-0E473EE9C48C}
+#elif UNICODE
+ { 0x36753ae3, 0x840b, 0x4797, { 0x94, 0xa5, 0xfd, 0x9f, 0x58, 0x52, 0xb9, 0x42 } } // {36753AE3-840B-4797-94A5-FD9F5852B942}
+#else
+ { 0x3cdbcd92, 0x94d7, 0x466a, { 0x94, 0x7f, 0x4e, 0xe0, 0x1c, 0xca, 0xf4, 0x89 } } // {3CDBCD92-94D7-466a-947F-4EE01CCAF489}
+#endif
+};
+
+typedef struct
+{
+ TCHAR* szDescr;
+ char* szName;
+ int defIconID;
+} IconStruct;
+
+static IconStruct iconList[] =
+{
+ { LPGENT("Enabled"), "spellchecker_enabled", IDI_CHECK },
+ { LPGENT("Disabled"), "spellchecker_disabled", IDI_NO_CHECK },
+// { LPGENT("Unknown Flag"), "spellchecker_unknown_flag", IDI_UNKNOWN_FLAG },
+};
+
+#define TIMER_ID 17982
+#define WMU_DICT_CHANGED (WM_USER+100)
+#define WMU_KBDL_CHANGED (WM_USER+101)
+
+#define HOTKEY_ACTION_TOGGLE 1
+
+HINSTANCE hInst;
+PLUGINLINK *pluginLink;
+LIST_INTERFACE li;
+MM_INTERFACE mmi;
+UTF8_INTERFACE utfi;
+
+HANDLE hHooks[6];
+HANDLE hServices[3];
+
+HANDLE hDictionariesFolder = NULL;
+TCHAR *dictionariesFolder;
+
+HANDLE hCustomDictionariesFolder = NULL;
+TCHAR *customDictionariesFolder;
+
+HANDLE hFlagsDllFolder = NULL;
+TCHAR *flagsDllFolder;
+
+HBITMAP hCheckedBmp;
+BITMAP bmpChecked;
+
+BOOL variables_enabled = FALSE;
+BOOL loaded = FALSE;
+
+LIST<Dictionary> languages(1);
+
+typedef map<HWND, Dialog *> DialogMapType;
+
+DialogMapType dialogs;
+DialogMapType menus;
+
+int ModulesLoaded(WPARAM wParam, LPARAM lParam);
+int PreShutdown(WPARAM wParam, LPARAM lParam);
+int MsgWindowEvent(WPARAM wParam, LPARAM lParam);
+int MsgWindowPopup(WPARAM wParam, LPARAM lParam);
+int IconsChanged(WPARAM wParam, LPARAM lParam);
+int IconPressed(WPARAM wParam, LPARAM lParam);
+
+int AddContactTextBox(HANDLE hContact, HWND hwnd, char *name, BOOL srmm, HWND hwndOwner);
+int RemoveContactTextBox(HWND hwnd);
+int ShowPopupMenu(HWND hwnd, HMENU hMenu, POINT pt, HWND hwndOwner);
+
+INT_PTR AddContactTextBoxService(WPARAM wParam, LPARAM lParam);
+INT_PTR RemoveContactTextBoxService(WPARAM wParam, LPARAM lParam);
+INT_PTR ShowPopupMenuService(WPARAM wParam, LPARAM lParam);
+
+LRESULT CALLBACK MenuWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
+
+void ModifyIcon(Dialog *dlg);
+BOOL GetWordCharRange(Dialog *dlg, CHARRANGE &sel, TCHAR *text, size_t text_len, int &first_char);
+TCHAR *GetWordUnderPoint(Dialog *dlg, POINT pt, CHARRANGE &sel);
+
+int GetClosestLanguage(TCHAR *lang_name);
+
+typedef void (*FoundWrongWordCallback)(TCHAR *word, CHARRANGE pos, void *param);
+
+
+#define DEFINE_GUIDXXX(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \
+ const GUID CDECL name \
+ = { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } }
+
+DEFINE_GUIDXXX(IID_ITextDocument,0x8CC497C0,0xA1DF,0x11CE,0x80,0x98,
+ 0x00,0xAA,0x00,0x47,0xBE,0x5D);
+
+
+
+// Functions ////////////////////////////////////////////////////////////////////////////
+
+
+HICON IcoLib_LoadIcon(Dictionary *dict, BOOL copy)
+{
+#ifdef UNICODE
+ char lang[32];
+ WideCharToMultiByte(CP_ACP, 0, dict->language, -1, lang, sizeof(lang), NULL, NULL);
+ return IcoLib_LoadIcon(lang, copy);
+#else
+ return IcoLib_LoadIcon(dict->language, copy);
+#endif
+}
+
+
+extern "C" BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
+{
+ hInst = hinstDLL;
+ return TRUE;
+}
+
+extern "C" __declspec(dllexport) PLUGININFOEX* MirandaPluginInfoEx(DWORD mirandaVersion)
+{
+ return &pluginInfo;
+}
+
+
+static const MUUID interfaces[] = { MIID_SPELLCHECKER, MIID_LAST };
+extern "C" __declspec(dllexport) const MUUID* MirandaPluginInterfaces(void)
+{
+ return interfaces;
+}
+
+
+extern "C" int __declspec(dllexport) Load(PLUGINLINK *link)
+{
+ pluginLink = link;
+
+ mir_getMMI(&mmi);
+ mir_getUTFI(&utfi);
+ mir_getLI(&li);
+
+ // hooks
+ hHooks[0] = HookEvent(ME_SYSTEM_MODULESLOADED, ModulesLoaded);
+ hHooks[1] = HookEvent(ME_SYSTEM_PRESHUTDOWN, PreShutdown);
+
+ hCheckedBmp = LoadBitmap(NULL, MAKEINTRESOURCE(OBM_CHECK));
+ if (GetObject(hCheckedBmp, sizeof(bmpChecked), &bmpChecked) == 0)
+ bmpChecked.bmHeight = bmpChecked.bmWidth = 10;
+
+ return 0;
+}
+
+extern "C" int __declspec(dllexport) Unload(void)
+{
+ DeleteObject(hCheckedBmp);
+ FreeDictionaries(languages);
+
+ return 0;
+}
+
+// Called when all the modules are loaded
+int ModulesLoaded(WPARAM wParam, LPARAM lParam)
+{
+ variables_enabled = ServiceExists(MS_VARS_FORMATSTRING);
+
+ // add our modules to the KnownModules list
+ CallService("DBEditorpp/RegisterSingleModule", (WPARAM) MODULE_NAME, 0);
+
+ // updater plugin support
+ if(ServiceExists(MS_UPDATE_REGISTER))
+ {
+ Update upd = {0};
+ char szCurrentVersion[30];
+
+ upd.cbSize = sizeof(upd);
+ upd.szComponentName = pluginInfo.shortName;
+
+ upd.szBetaVersionURL = "http://svn.berlios.de/svnroot/repos/mgoodies/trunk/spellchecker/Docs/spellchecker_version.txt";
+ upd.szBetaChangelogURL = "http://pescuma.org/miranda/spellchecker#Changelog";
+ upd.pbBetaVersionPrefix = (BYTE *)"Spell Checker ";
+ upd.cpbBetaVersionPrefix = (int)strlen((char *)upd.pbBetaVersionPrefix);
+#ifdef WIN64
+ upd.szBetaUpdateURL = "http://pescuma.googlecode.com/files/spellchecker64.%VERSION%.zip";
+// upd.szVersionURL = "http://addons.miranda-im.org/details.php?action=viewfile&id=";
+// upd.szUpdateURL = "http://addons.miranda-im.org/download.php?dlfile=";
+ upd.pbVersionPrefix = (BYTE *)"<span class=\"fileNameHeader\">Spell Checker (x64) ";
+#elif UNICODE
+ upd.szBetaUpdateURL = "http://pescuma.googlecode.com/files/spellcheckerW.%VERSION%.zip";
+ upd.szVersionURL = "http://addons.miranda-im.org/details.php?action=viewfile&id=3691";
+ upd.szUpdateURL = "http://addons.miranda-im.org/download.php?dlfile=3691";
+ upd.pbVersionPrefix = (BYTE *)"<span class=\"fileNameHeader\">Spell Checker (Unicode) ";
+#else
+ upd.szBetaUpdateURL = "http://pescuma.googlecode.com/files/spellchecker.%VERSION%.zip";
+ upd.szVersionURL = "http://addons.miranda-im.org/details.php?action=viewfile&id=3690";
+ upd.szUpdateURL = "http://addons.miranda-im.org/download.php?dlfile=3690";
+ upd.pbVersionPrefix = (BYTE *)"<span class=\"fileNameHeader\">Spell Checker (Ansi) ";
+#endif
+ upd.cpbVersionPrefix = (int)strlen((char *)upd.pbVersionPrefix);
+
+ upd.pbVersion = (BYTE *)CreateVersionStringPluginEx(&pluginInfo, szCurrentVersion);
+ upd.cpbVersion = (int)strlen((char *)upd.pbVersion);
+
+ CallService(MS_UPDATE_REGISTER, 0, (LPARAM)&upd);
+ }
+
+ // Folders plugin support
+ if (ServiceExists(MS_FOLDERS_REGISTER_PATH))
+ {
+ hDictionariesFolder = FoldersRegisterCustomPathT(LPGEN("Spell Checker"), LPGEN("Dictionaries"), DICTIONARIES_FOLDER);
+
+ dictionariesFolder = (TCHAR *) mir_alloc(sizeof(TCHAR) * MAX_PATH);
+ FoldersGetCustomPathT(hDictionariesFolder, dictionariesFolder, MAX_PATH, _T("."));
+
+ hCustomDictionariesFolder = FoldersRegisterCustomPathT(LPGEN("Spell Checker"), LPGEN("Custom Dictionaries"), CUSTOM_DICTIONARIES_FOLDER);
+
+ customDictionariesFolder = (TCHAR *) mir_alloc(sizeof(TCHAR) * MAX_PATH);
+ FoldersGetCustomPathT(hCustomDictionariesFolder, customDictionariesFolder, MAX_PATH, _T("."));
+
+ hFlagsDllFolder = FoldersRegisterCustomPathT(LPGEN("Spell Checker"), LPGEN("Flags DLL"), FLAGS_DLL_FOLDER);
+
+ flagsDllFolder = (TCHAR *) mir_alloc(sizeof(TCHAR) * MAX_PATH);
+ FoldersGetCustomPathT(hFlagsDllFolder, flagsDllFolder, MAX_PATH, _T("."));
+ }
+ else
+ {
+ dictionariesFolder = Utils_ReplaceVarsT(DICTIONARIES_FOLDER);
+ customDictionariesFolder = Utils_ReplaceVarsT(CUSTOM_DICTIONARIES_FOLDER);
+ flagsDllFolder = Utils_ReplaceVarsT(FLAGS_DLL_FOLDER);
+ }
+
+ TCHAR path[MAX_PATH];
+ GetModuleFileName(hInst, path, MAX_PATH);
+
+ SKINICONDESC sid = {0};
+ sid.cbSize = sizeof(SKINICONDESC);
+ sid.flags = SIDF_ALL_TCHAR;
+ sid.ptszSection = LPGENT("Spell Checker");
+ sid.ptszDefaultFile = path;
+
+ for (unsigned i = 0; i < MAX_REGS(iconList); ++i)
+ {
+ sid.ptszDescription = iconList[i].szDescr;
+ sid.pszName = iconList[i].szName;
+ sid.iDefaultIndex = -iconList[i].defIconID;
+ CallService(MS_SKIN2_ADDICON, 0, (LPARAM)&sid);
+ }
+
+ InitOptions();
+
+ GetAvaibleDictionaries(languages, dictionariesFolder, customDictionariesFolder);
+
+ LoadOptions();
+
+ if (opts.use_flags)
+ {
+ // Load flags dll
+ TCHAR flag_file[1024];
+ mir_sntprintf(flag_file, MAX_REGS(flag_file), _T("%s\\flags.dll"), flagsDllFolder);
+ HMODULE hFlagsDll = LoadLibraryEx(flag_file, NULL, LOAD_LIBRARY_AS_DATAFILE);
+
+ sid.flags = SIDF_ALL_TCHAR | SIDF_SORTED;
+ sid.ptszSection = _T("Languages/Flags");
+
+ // Get language flags
+ for(int i = 0; i < languages.getCount(); i++)
+ {
+ sid.ptszDescription = languages[i]->full_name;
+#ifdef UNICODE
+ char lang[32];
+ mir_snprintf(lang, MAX_REGS(lang), "%S", languages[i]->language);
+ sid.pszName = lang;
+#else
+ sid.pszName = languages[i]->language;
+#endif
+
+ HICON hFlag = IcoLib_LoadIcon(sid.pszName);
+ if (hFlag != NULL)
+ {
+ // Already registered
+ IcoLib_ReleaseIcon(hFlag);
+ continue;
+ }
+
+ if (hFlagsDll != NULL)
+ hFlag = (HICON) LoadImage(hFlagsDll, languages[i]->language, IMAGE_ICON, 16, 16, 0);
+ else
+ hFlag = NULL;
+
+ if (hFlag != NULL)
+ {
+ sid.hDefaultIcon = hFlag;
+ sid.ptszDefaultFile = NULL;
+ sid.iDefaultIndex = 0;
+ }
+ else
+ {
+ sid.hDefaultIcon = NULL;
+ sid.ptszDefaultFile = path;
+ sid.iDefaultIndex = - IDI_UNKNOWN_FLAG;
+ }
+
+ // Oki, lets add to IcoLib, then
+ CallService(MS_SKIN2_ADDICON, 0, (LPARAM)&sid);
+
+ if (hFlag != NULL)
+ DestroyIcon(hFlag);
+ }
+ FreeLibrary(hFlagsDll);
+ }
+
+ for (int j = 0; j < languages.getCount(); j++)
+ {
+ Dictionary *dict = languages[j];
+
+ TCHAR filename[MAX_PATH];
+ mir_sntprintf(filename, MAX_PATH, _T("%s\\%s.ar"), customDictionariesFolder, dict->language);
+ dict->autoReplace = new AutoReplaceMap(filename, dict);
+
+ if (lstrcmp(dict->language, opts.default_language) == 0)
+ dict->load();
+ }
+
+ hHooks[2] = HookEvent(ME_SKIN2_ICONSCHANGED, &IconsChanged);
+ hHooks[3] = HookEvent(ME_MSG_WINDOWEVENT, &MsgWindowEvent);
+ hHooks[4] = HookEvent(ME_MSG_WINDOWPOPUP, &MsgWindowPopup);
+ hHooks[5] = HookEvent(ME_MSG_ICONPRESSED, &IconPressed);
+
+ hServices[0] = CreateServiceFunction(MS_SPELLCHECKER_ADD_RICHEDIT, AddContactTextBoxService);
+ hServices[1] = CreateServiceFunction(MS_SPELLCHECKER_REMOVE_RICHEDIT, RemoveContactTextBoxService);
+ hServices[2] = CreateServiceFunction(MS_SPELLCHECKER_SHOW_POPUP_MENU, ShowPopupMenuService);
+
+ if (ServiceExists(MS_MSG_ADDICON))
+ {
+ StatusIconData sid = {0};
+ sid.cbSize = sizeof(sid);
+ sid.szModule = MODULE_NAME;
+ sid.hIconDisabled = IcoLib_LoadIcon("spellchecker_disabled", TRUE);
+ sid.flags = MBF_HIDDEN;
+
+ for (int i = 0; i < languages.getCount(); i++)
+ {
+ sid.dwId = i;
+
+ char tmp[128];
+ mir_snprintf(tmp, MAX_REGS(tmp), "%s - " TCHAR_STR_PARAM,
+ Translate("Spell Checker"), languages[i]->full_name);
+ sid.szTooltip = tmp;
+
+ if (opts.use_flags)
+ sid.hIcon = IcoLib_LoadIcon(languages[i], TRUE);
+ else
+ sid.hIcon = IcoLib_LoadIcon("spellchecker_enabled", TRUE);
+
+ CallService(MS_MSG_ADDICON, 0, (LPARAM) &sid);
+ }
+ }
+
+ if (ServiceExists(MS_HOTKEY_REGISTER))
+ {
+ HOTKEYDESC hkd = {0};
+ hkd.cbSize = sizeof(hkd);
+ hkd.pszName = "Spell Checker/Toggle";
+ hkd.ptszSection = LPGENT("Spell Checker");
+ hkd.ptszDescription = LPGENT("Enable/disable spell checker");
+ hkd.dwFlags = HKD_TCHAR;
+// hkd.DefHotKey = HOTKEYCODE(HOTKEYF_SHIFT|HOTKEYF_ALT, 'S');
+ hkd.lParam = HOTKEY_ACTION_TOGGLE;
+ CallService(MS_HOTKEY_REGISTER, 0, (LPARAM) &hkd);
+ }
+
+ loaded = TRUE;
+
+ return 0;
+}
+
+
+int IconsChanged(WPARAM wParam, LPARAM lParam)
+{
+ if (ServiceExists(MS_MSG_MODIFYICON))
+ {
+ StatusIconData sid = {0};
+ sid.cbSize = sizeof(sid);
+ sid.szModule = MODULE_NAME;
+ sid.hIconDisabled = IcoLib_LoadIcon("spellchecker_disabled", TRUE);
+ sid.flags = MBF_HIDDEN;
+
+
+ for (int i = 0; i < languages.getCount(); i++)
+ {
+ sid.dwId = i;
+
+ char tmp[128];
+ mir_snprintf(tmp, MAX_REGS(tmp), "%s - " TCHAR_STR_PARAM,
+ Translate("Spell Checker"), languages[i]->full_name);
+ sid.szTooltip = tmp;
+
+ if (opts.use_flags)
+ sid.hIcon = IcoLib_LoadIcon(languages[i], TRUE);
+ else
+ sid.hIcon = IcoLib_LoadIcon("spellchecker_enabled", TRUE);
+
+ CallService(MS_MSG_MODIFYICON, 0, (LPARAM) &sid);
+ }
+ }
+
+ return 0;
+}
+
+
+int PreShutdown(WPARAM wParam, LPARAM lParam)
+{
+ int i;
+ for(i = 0; i < MAX_REGS(hServices); i++)
+ DestroyServiceFunction(hServices[i]);
+
+ for(i = 0; i < MAX_REGS(hHooks); i++)
+ UnhookEvent(hHooks[i]);
+
+ DeInitOptions();
+
+ if (ServiceExists(MS_MSG_REMOVEICON))
+ {
+ StatusIconData sid = {0};
+ sid.cbSize = sizeof(sid);
+ sid.szModule = MODULE_NAME;
+ CallService(MS_MSG_REMOVEICON, 0, (LPARAM) &sid);
+ }
+
+ mir_free(dictionariesFolder);
+ mir_free(customDictionariesFolder);
+ mir_free(flagsDllFolder);
+
+ return 0;
+}
+
+
+void SetUnderline(Dialog *dlg, int pos_start, int pos_end)
+{
+ dlg->re->SetSel(pos_start, pos_end);
+
+ CHARFORMAT2 cf;
+ cf.cbSize = sizeof(CHARFORMAT2);
+ cf.dwMask = CFM_UNDERLINE | CFM_UNDERLINETYPE;
+ cf.dwEffects = CFE_UNDERLINE;
+ cf.bUnderlineType = ((opts.underline_type + CFU_UNDERLINEDOUBLE) | 0x50);
+ dlg->re->SendMessage(EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM)&cf);
+
+ dlg->markedSomeWord = TRUE;
+}
+
+
+BOOL IsMyUnderline(const CHARFORMAT2 &cf)
+{
+ return (cf.dwEffects & CFE_UNDERLINE)
+ && (cf.bUnderlineType & 0x0F) >= CFU_UNDERLINEDOUBLE
+ && (cf.bUnderlineType & 0x0F) <= CFU_UNDERLINETHICK
+ && (cf.bUnderlineType & ~0x0F) == 0x50;
+}
+
+
+void SetNoUnderline(RichEdit *re, int pos_start, int pos_end)
+{
+ if (opts.handle_underscore)
+ {
+ for(int i = pos_start; i <= pos_end; i++)
+ {
+ re->SetSel(i, min(i+1, pos_end));
+
+ CHARFORMAT2 cf;
+ cf.cbSize = sizeof(CHARFORMAT2);
+ re->SendMessage(EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM)&cf);
+
+ BOOL mine = IsMyUnderline(cf);
+ if (mine)
+ {
+ cf.cbSize = sizeof(CHARFORMAT2);
+ cf.dwMask = CFM_UNDERLINE | CFM_UNDERLINETYPE;
+ cf.dwEffects = 0;
+ cf.bUnderlineType = CFU_UNDERLINE;
+ re->SendMessage(EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM)&cf);
+ }
+ }
+ }
+ else
+ {
+ re->SetSel(pos_start, pos_end);
+
+ CHARFORMAT2 cf;
+ cf.cbSize = sizeof(CHARFORMAT2);
+ cf.dwMask = CFM_UNDERLINE | CFM_UNDERLINETYPE;
+ cf.dwEffects = 0;
+ cf.bUnderlineType = CFU_UNDERLINE;
+ re->SendMessage(EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM)&cf);
+ }
+}
+
+
+void SetNoUnderline(Dialog *dlg)
+{
+ dlg->re->Stop();
+ SetNoUnderline(dlg->re, 0, dlg->re->GetTextLength());
+ dlg->markedSomeWord = FALSE;
+ dlg->re->Start();
+}
+
+
+inline BOOL IsNumber(TCHAR c)
+{
+ return c >= _T('0') && c <= _T('9');
+}
+
+
+inline BOOL IsURL(TCHAR c)
+{
+ return (c >= _T('a') && c <= _T('z'))
+ || (c >= _T('A') && c <= _T('Z'))
+ || IsNumber(c)
+ || c == _T('.') || c == _T('/')
+ || c == _T('\\') || c == _T('?')
+ || c == _T('=') || c == _T('&')
+ || c == _T('%') || c == _T('-')
+ || c == _T('_') || c == _T(':')
+ || c == _T('@') || c == _T('#');
+}
+
+
+int FindURLEnd(Dialog *dlg, TCHAR *text, int start_pos, int *checked_until = NULL)
+{
+ int num_slashes = 0;
+ int num_ats = 0;
+ int num_dots = 0;
+
+ int i = start_pos;
+
+ for(; IsURL(text[i]) || dlg->lang->isWordChar(text[i]); i++)
+ {
+ TCHAR c = text[i];
+
+ if (c == _T('\\') || c == _T('/'))
+ num_slashes++;
+ else if (c == _T('.'))
+ num_dots++;
+ else if (c == _T('@'))
+ num_ats++;
+ }
+
+ if (checked_until != NULL)
+ *checked_until = i;
+
+ if (num_slashes <= 0 && num_ats <= 0 && num_dots <= 0)
+ return -1;
+
+ if (num_slashes == 0 && num_ats == 0 && num_dots < 2)
+ return -1;
+
+ if (i - start_pos < 2)
+ return -1;
+
+ return i;
+}
+
+
+int ReplaceWord(Dialog *dlg, CHARRANGE &sel, TCHAR *new_word)
+{
+ dlg->re->Stop();
+ dlg->re->ResumeUndo();
+
+ int dif = dlg->re->Replace(sel.cpMin, sel.cpMax, new_word);
+
+ dlg->re->SuspendUndo();
+ dlg->re->Start();
+
+ return dif;
+}
+
+
+class TextParser
+{
+public:
+ virtual ~TextParser() {}
+
+ /// @return true when finished an word
+ virtual bool feed(int pos, TCHAR c) =0;
+ virtual int getFirstCharPos() =0;
+ virtual void reset() =0;
+ virtual void deal(const TCHAR *text, bool *mark, bool *replace, TCHAR **replacement) =0;
+};
+
+
+class SpellParser : public TextParser
+{
+ Dictionary *dict;
+ int last_pos;
+ BOOL found_real_char;
+
+public:
+ SpellParser(Dictionary *dict) : dict(dict)
+ {
+ reset();
+ }
+
+ void reset()
+ {
+ last_pos = -1;
+ found_real_char = FALSE;
+ }
+
+ bool feed(int pos, TCHAR c)
+ {
+ // Is inside a word?
+ if (dict->isWordChar(c) || IsNumber(c))
+ {
+ if (last_pos == -1)
+ last_pos = pos;
+
+ if (c != _T('-') && !IsNumber(c))
+ found_real_char = TRUE;
+
+ return false;
+ }
+
+ if (!found_real_char)
+ last_pos = -1;
+
+ return (last_pos != -1);
+ }
+
+ int getFirstCharPos()
+ {
+ if (!found_real_char)
+ return -1;
+ else
+ return last_pos;
+ }
+
+ void deal(const TCHAR *text, bool *mark, bool *replace, TCHAR **replacement)
+ {
+ // Is it correct?
+ if (dict->spell(text))
+ return;
+
+ // Has to auto-correct?
+ if (opts.auto_replace_dict)
+ {
+ *replacement = dict->autoSuggestOne(text);
+ if (*replacement != NULL)
+ {
+ *replace = true;
+ return;
+ }
+ }
+
+ *mark = true;
+ }
+};
+
+
+class AutoReplaceParser : public TextParser
+{
+ AutoReplaceMap *ar;
+ int last_pos;
+
+public:
+ AutoReplaceParser(AutoReplaceMap *ar) : ar(ar)
+ {
+ reset();
+ }
+
+ void reset()
+ {
+ last_pos = -1;
+ }
+
+ bool feed(int pos, TCHAR c)
+ {
+ // Is inside a word?
+ if (ar->isWordChar(c))
+ {
+ if (last_pos == -1)
+ last_pos = pos;
+ return false;
+ }
+
+ return (last_pos != -1);
+ }
+
+ int getFirstCharPos()
+ {
+ return last_pos;
+ }
+
+ void deal(const TCHAR *text, bool *mark, bool *replace, TCHAR **replacement)
+ {
+ *replacement = ar->autoReplace(text);
+ if (*replacement != NULL)
+ *replace = true;
+ }
+};
+
+int CheckTextLine(Dialog *dlg, int line, TextParser *parser,
+ BOOL ignore_upper, BOOL ignore_with_numbers, BOOL test_urls,
+ const CHARRANGE &ignored, FoundWrongWordCallback callback, void *param)
+{
+ int errors = 0;
+ TCHAR text[1024];
+ dlg->re->GetLine(line, text, MAX_REGS(text));
+ int len = lstrlen(text);
+ int first_char = dlg->re->GetFirstCharOfLine(line);
+
+ // Now lets get the words
+ int next_char_for_url = 0;
+ for (int pos = 0; pos < len; pos++)
+ {
+ int url_end = pos;
+ if (pos >= next_char_for_url)
+ {
+ url_end = FindURLEnd(dlg, text, pos, &next_char_for_url);
+ next_char_for_url++;
+ }
+
+ if (url_end > pos)
+ {
+ BOOL ignore_url = FALSE;
+
+ if (test_urls)
+ {
+ // All the url must be handled by the parser
+ parser->reset();
+
+ BOOL feed = FALSE;
+ for(int j = pos; !feed && j <= url_end; j++)
+ feed = parser->feed(j, text[j]);
+
+ if (feed || parser->getFirstCharPos() != pos)
+ ignore_url = TRUE;
+ }
+ else
+ ignore_url = TRUE;
+
+ pos = url_end;
+
+ if (ignore_url)
+ {
+ parser->reset();
+ continue;
+ }
+ }
+ else
+ {
+ TCHAR c = text[pos];
+
+ BOOL feed = parser->feed(pos, c);
+
+ if (!feed)
+ {
+ if (pos >= len-1)
+ pos = len; // To check the last block
+ else
+ continue;
+ }
+ }
+
+ int last_pos = parser->getFirstCharPos();
+ parser->reset();
+
+ if (last_pos < 0)
+ continue;
+
+ // We found a word
+ CHARRANGE sel = { first_char + last_pos, first_char + pos };
+
+ // Is in ignored range?
+ if (sel.cpMin <= ignored.cpMax && sel.cpMax >= ignored.cpMin)
+ continue;
+
+ if (ignore_upper)
+ {
+ BOOL upper = TRUE;
+ for(int i = last_pos; i < pos && upper; i++)
+ upper = !IsCharLower(text[i]);
+ if (upper)
+ continue;
+ }
+
+ if (ignore_with_numbers)
+ {
+ BOOL hasNumbers = FALSE;
+ for(int i = last_pos; i < pos && !hasNumbers; i++)
+ hasNumbers = IsNumber(text[i]);
+ if (hasNumbers)
+ continue;
+ }
+
+ text[pos] = 0;
+
+ bool mark = false;
+ bool replace = false;
+ TCHAR *replacement = NULL;
+ parser->deal(&text[last_pos], &mark, &replace, &replacement);
+
+ if (replace)
+ {
+ // Replace in rich edit
+ int dif = dlg->re->Replace(sel.cpMin, sel.cpMax, replacement);
+ if (dif != 0)
+ {
+ // Read line again
+ dlg->re->GetLine(line, text, MAX_REGS(text));
+ len = lstrlen(text);
+
+ int old_first_char = first_char;
+ first_char = dlg->re->GetFirstCharOfLine(line);
+
+ pos = max(-1, pos + dif + old_first_char - first_char);
+ }
+
+ free(replacement);
+ }
+ else if (mark)
+ {
+ SetUnderline(dlg, sel.cpMin, sel.cpMax);
+
+ if (callback != NULL)
+ callback(&text[last_pos], sel, param);
+
+ errors++;
+ }
+ }
+
+ return errors;
+}
+
+
+// Checks for errors in all text
+int CheckText(Dialog *dlg, BOOL check_all,
+ FoundWrongWordCallback callback = NULL, void *param = NULL)
+{
+ int errors = 0;
+
+ dlg->re->Stop();
+
+ if (dlg->re->GetTextLength() > 0)
+ {
+ int lines = dlg->re->GetLineCount();
+ int line = 0;
+ CHARRANGE cur_sel = { -1, -1 };
+
+ if (!check_all)
+ {
+ // Check only the current line, one up and one down
+ int current_line = dlg->re->GetLineFromChar(dlg->re->GetSel().cpMin);
+ line = max(line, current_line - 1);
+ lines = min(lines, current_line + 2);
+ cur_sel = dlg->re->GetSel();
+ }
+
+ for(; line < lines; line++)
+ {
+ int first_char = dlg->re->GetFirstCharOfLine(line);
+
+ SetNoUnderline(dlg->re, first_char, first_char + dlg->re->GetLineLength(line));
+
+ if (opts.auto_replace_user)
+ {
+ errors += CheckTextLine(dlg, line, &AutoReplaceParser(dlg->lang->autoReplace),
+ FALSE, FALSE, TRUE,
+ cur_sel, callback, param);
+ }
+
+ errors += CheckTextLine(dlg, line, &SpellParser(dlg->lang),
+ opts.ignore_uppercase, opts.ignore_with_numbers, FALSE,
+ cur_sel, callback, param);
+ }
+ }
+
+ // Fix last char
+ int len = dlg->re->GetTextLength();
+ SetNoUnderline(dlg->re, len, len);
+
+ dlg->re->Start();
+
+ return errors;
+}
+
+
+void ToLocaleID(TCHAR *szKLName, size_t size)
+{
+ TCHAR *stopped = NULL;
+ USHORT langID = (USHORT) _tcstol(szKLName, &stopped, 16);
+
+ TCHAR ini[32];
+ TCHAR end[32];
+ GetLocaleInfo(MAKELCID(langID, 0), LOCALE_SISO639LANGNAME, ini, MAX_REGS(ini));
+ GetLocaleInfo(MAKELCID(langID, 0), LOCALE_SISO3166CTRYNAME, end, MAX_REGS(end));
+
+ mir_sntprintf(szKLName, size, _T("%s_%s"), ini, end);
+}
+
+
+void LoadDictFromKbdl(Dialog *dlg)
+{
+ TCHAR szKLName[KL_NAMELENGTH + 1];
+
+ // Use default input language
+ HKL hkl = GetKeyboardLayout(0);
+ mir_sntprintf(szKLName, MAX_REGS(szKLName), _T("%x"), (int) LOWORD(hkl));
+ ToLocaleID(szKLName, MAX_REGS(szKLName));
+
+ // Old code (use keyboard layout)
+// GetKeyboardLayoutName(szKLName);
+// ToLocaleID(szKLName, MAX_REGS(szKLName));
+
+ int d = GetClosestLanguage(szKLName);
+ if (d >= 0)
+ {
+ dlg->lang = languages[d];
+ dlg->lang->load();
+
+ if (dlg->srmm)
+ ModifyIcon(dlg);
+ }
+}
+
+int TimerCheck(Dialog *dlg, BOOL forceCheck = FALSE)
+{
+ KillTimer(dlg->hwnd, TIMER_ID);
+
+ if (!dlg->enabled || dlg->lang == NULL)
+ return -1;
+
+ if (!dlg->lang->isLoaded())
+ {
+ SetTimer(dlg->hwnd, TIMER_ID, 500, NULL);
+ return -1;
+ }
+
+ // Don't check if field is read-only
+ if (dlg->re->IsReadOnly())
+ return -1;
+
+ int len = dlg->re->GetTextLength();
+ if (!forceCheck && len == dlg->old_text_len && !dlg->changed)
+ return -1;
+
+ dlg->old_text_len = len;
+ dlg->changed = FALSE;
+
+ return CheckText(dlg, TRUE);
+}
+
+
+LRESULT CALLBACK OwnerProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ DialogMapType::iterator dlgit = dialogs.find(hwnd);
+ if (dlgit == dialogs.end())
+ return -1;
+
+ Dialog *dlg = dlgit->second;
+
+ if (msg == WM_COMMAND && (LOWORD(wParam) == IDOK || LOWORD(wParam) == 1624))
+ {
+ if (opts.ask_when_sending_with_error)
+ {
+ int errors = TimerCheck(dlg, TRUE);
+ if (errors > 0)
+ {
+ TCHAR text[500];
+ mir_sntprintf(text,MAX_REGS(text),TranslateT("There are %d spelling errors. Are you sure you want to send this message?"),errors);
+ if (MessageBox(hwnd,text,TranslateT("Spell Checker"), MB_ICONQUESTION | MB_YESNO) == IDNO)
+ {
+ return TRUE;
+ }
+ }
+ }
+ else if (opts.auto_replace_dict || opts.auto_replace_user)
+ {
+ // Fix all
+ TimerCheck(dlg);
+ }
+
+ if (dlg->markedSomeWord)
+ // Remove underline
+ SetNoUnderline(dlg);
+
+ // Schedule to re-parse
+ KillTimer(dlg->hwnd, TIMER_ID);
+ SetTimer(dlg->hwnd, TIMER_ID, 100, NULL);
+
+ dlg->changed = TRUE;
+ }
+
+ return CallWindowProc(dlg->owner_old_edit_proc, hwnd, msg, wParam, lParam);
+}
+
+
+void ToggleEnabled(Dialog *dlg)
+{
+ dlg->enabled = !dlg->enabled;
+ DBWriteContactSettingByte(dlg->hContact, MODULE_NAME, dlg->name, dlg->enabled);
+
+ if (!dlg->enabled)
+ {
+ SetNoUnderline(dlg);
+ }
+ else
+ {
+ dlg->changed = TRUE;
+ SetTimer(dlg->hwnd, TIMER_ID, 100, NULL);
+ }
+
+ if (dlg->srmm)
+ ModifyIcon(dlg);
+}
+
+
+LRESULT CALLBACK EditProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ DialogMapType::iterator dlgit = dialogs.find(hwnd);
+ if (dlgit == dialogs.end())
+ return -1;
+
+ Dialog *dlg = dlgit->second;
+ if (dlg == NULL)
+ return -1;
+
+ // Hotkey support
+ {
+ MSG msgData = {0};
+ msgData.hwnd = hwnd;
+ msgData.message = msg;
+ msgData.wParam = wParam;
+ msgData.lParam = lParam;
+
+ int action = CallService(MS_HOTKEY_CHECK, (WPARAM) &msgData, (LPARAM) "Spell Checker");
+ if (action == HOTKEY_ACTION_TOGGLE)
+ {
+ ToggleEnabled(dlg);
+ return 1;
+ }
+ }
+
+ LRESULT ret = CallWindowProc(dlg->old_edit_proc, hwnd, msg, wParam, lParam);
+
+ switch(msg)
+ {
+ case WM_KEYDOWN:
+ {
+ if (wParam != VK_DELETE)
+ break;
+ }
+ case WM_CHAR:
+ {
+ if (dlg->re->IsStopped())
+ break;
+
+ if (lParam & (1 << 28)) // ALT key
+ break;
+
+ if (GetKeyState(VK_CONTROL) & 0x8000) // CTRL key
+ break;
+
+ TCHAR c = (TCHAR) wParam;
+ BOOL deleting = (c == VK_BACK || c == VK_DELETE);
+
+ // Need to do that to avoid changing the word while typing
+ KillTimer(hwnd, TIMER_ID);
+ SetTimer(hwnd, TIMER_ID, 1000, NULL);
+
+ dlg->changed = TRUE;
+
+ if (!deleting && (lParam & 0xFF) > 1) // Repeat rate
+ break;
+
+ if (!dlg->enabled || dlg->lang == NULL || !dlg->lang->isLoaded())
+ break;
+
+ // Don't check if field is read-only
+ if (dlg->re->IsReadOnly())
+ break;
+
+
+ if (!deleting && !dlg->lang->isWordChar(c))
+ {
+ CheckText(dlg, FALSE);
+ }
+ else
+ {
+ // Remove underline of current word
+
+ CHARFORMAT2 cf;
+ cf.cbSize = sizeof(CHARFORMAT2);
+ dlg->re->SendMessage(EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM)&cf);
+
+ if (IsMyUnderline(cf))
+ {
+ dlg->re->Stop();
+
+ CHARRANGE sel = dlg->re->GetSel();
+
+ TCHAR text[1024];
+ int first_char;
+ GetWordCharRange(dlg, sel, text, MAX_REGS(text), first_char);
+
+ SetNoUnderline(dlg->re, sel.cpMin, sel.cpMax);
+
+ dlg->re->Start();
+ }
+ }
+
+ break;
+ }
+ case EM_REPLACESEL:
+ case WM_SETTEXT:
+ case EM_SETTEXTEX:
+ case EM_PASTESPECIAL:
+ case WM_PASTE:
+ {
+ if (dlg->re->IsStopped())
+ break;
+
+ KillTimer(hwnd, TIMER_ID);
+ SetTimer(hwnd, TIMER_ID, 100, NULL);
+
+ dlg->changed = TRUE;
+ break;
+ }
+
+ case WM_TIMER:
+ {
+ if (wParam != TIMER_ID)
+ break;
+
+ TimerCheck(dlg);
+ break;
+ }
+
+ case WMU_DICT_CHANGED:
+ {
+ KillTimer(hwnd, TIMER_ID);
+ SetTimer(hwnd, TIMER_ID, 100, NULL);
+
+ dlg->changed = TRUE;
+ break;
+ }
+
+ case WMU_KBDL_CHANGED:
+ {
+ if (opts.auto_locale)
+ {
+ KillTimer(hwnd, TIMER_ID);
+ SetTimer(hwnd, TIMER_ID, 100, NULL);
+
+ dlg->changed = TRUE;
+
+ LoadDictFromKbdl(dlg);
+ }
+ break;
+ }
+
+ case WM_INPUTLANGCHANGE:
+ {
+ // Allow others to process this message and we get only the result
+ PostMessage(hwnd, WMU_KBDL_CHANGED, 0, 0);
+ break;
+ }
+ }
+
+ return ret;
+}
+
+int GetClosestLanguage(TCHAR *lang_name)
+{
+ int i;
+
+ // Search the language by name
+ for (i = 0; i < languages.getCount(); i++)
+ {
+ if (lstrcmpi(languages[i]->language, lang_name) == 0)
+ {
+ return i;
+ }
+ }
+
+ // Try searching by the prefix only
+ TCHAR lang[128];
+ lstrcpyn(lang, lang_name, MAX_REGS(lang));
+
+ TCHAR *p = _tcschr(lang, _T('_'));
+ if (p != NULL)
+ *p = _T('\0');
+
+ // First check if there is a language that is only the prefix
+ for (i = 0; i < languages.getCount(); i++)
+ {
+ if (lstrcmpi(languages[i]->language, lang) == 0)
+ {
+ return i;
+ }
+ }
+
+ // Now try any suffix
+ size_t len = lstrlen(lang);
+ for (i = 0; i < languages.getCount(); i++)
+ {
+ TCHAR *p = _tcschr(languages[i]->language, _T('_'));
+ if (p == NULL)
+ continue;
+
+ int prefix_len = p - languages[i]->language;
+ if (prefix_len != len)
+ continue;
+
+ if (_tcsnicmp(languages[i]->language, lang_name, len) == 0)
+ {
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+void GetUserProtoLanguageSetting(Dialog *dlg, HANDLE hContact, char *group, char *setting, BOOL isProtocol = TRUE)
+{
+ DBVARIANT dbv = {0};
+ dbv.type = DBVT_TCHAR;
+
+ DBCONTACTGETSETTING cgs = {0};
+ cgs.szModule = group;
+ cgs.szSetting = setting;
+ cgs.pValue = &dbv;
+
+ INT_PTR rc;
+
+ int caps = (isProtocol ? CallProtoService(group, PS_GETCAPS, PFLAGNUM_4, 0) : 0);
+ if (caps & PF4_INFOSETTINGSVC)
+ {
+ rc = CallProtoService(group, PS_GETINFOSETTING, (WPARAM) hContact, (LPARAM) &cgs);
+ }
+ else
+ {
+ rc = CallService(MS_DB_CONTACT_GETSETTING_STR_EX, (WPARAM)hContact, (LPARAM)&cgs);
+ if (rc == CALLSERVICE_NOTFOUND)
+ {
+ rc = CallService(MS_DB_CONTACT_GETSETTING_STR, (WPARAM)hContact, (LPARAM)&cgs);
+ }
+ }
+
+ if (!rc && dbv.type == DBVT_TCHAR && dbv.ptszVal != NULL)
+ {
+ TCHAR *lang = dbv.ptszVal;
+
+ for (int i = 0; i < languages.getCount(); i++)
+ {
+ Dictionary *dict = languages[i];
+ if (lstrcmpi(dict->localized_name, lang) == 0
+ || lstrcmpi(dict->english_name, lang) == 0
+ || lstrcmpi(dict->language, lang) == 0)
+ {
+ lstrcpyn(dlg->lang_name, dict->language, MAX_REGS(dlg->lang_name));
+ break;
+ }
+ }
+ }
+
+ if (!rc)
+ DBFreeVariant(&dbv);
+}
+
+void GetUserLanguageSetting(Dialog *dlg, char *setting)
+{
+ char *proto = (char *) CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM) dlg->hContact, 0);
+ if (proto == NULL)
+ return;
+
+ GetUserProtoLanguageSetting(dlg, dlg->hContact, proto, setting);
+ if (dlg->lang_name[0] != _T('\0'))
+ return;
+
+ GetUserProtoLanguageSetting(dlg, dlg->hContact, "UserInfo", setting, FALSE);
+ if (dlg->lang_name[0] != _T('\0'))
+ return;
+
+ // If not found and is inside meta, try to get from the meta
+ INT_PTR mc = CallService(MS_MC_GETPROTOCOLNAME, 0, 0);
+ if (mc != CALLSERVICE_NOTFOUND)
+ {
+ char* metacontacts_proto = (char *) mc;
+ if (metacontacts_proto != NULL)
+ {
+ mc = CallService(MS_MC_GETMETACONTACT, (WPARAM) dlg->hContact, 0);
+ if (mc != CALLSERVICE_NOTFOUND)
+ {
+ HANDLE hMetaContact = (HANDLE) mc;
+ if (hMetaContact != NULL)
+ {
+ GetUserProtoLanguageSetting(dlg, hMetaContact, metacontacts_proto, setting);
+ if (dlg->lang_name[0] != _T('\0'))
+ return;
+
+ GetUserProtoLanguageSetting(dlg, hMetaContact, "UserInfo", setting, FALSE);
+ }
+ }
+ }
+ }
+}
+
+void GetContactLanguage(Dialog *dlg)
+{
+ DBVARIANT dbv = {0};
+
+ dlg->lang_name[0] = _T('\0');
+
+ if (dlg->hContact == NULL)
+ {
+ if (!DBGetContactSettingTString(NULL, MODULE_NAME, dlg->name, &dbv))
+ {
+ lstrcpyn(dlg->lang_name, dbv.ptszVal, MAX_REGS(dlg->lang_name));
+ DBFreeVariant(&dbv);
+ }
+ }
+ else
+ {
+ if (!DBGetContactSettingTString(dlg->hContact, MODULE_NAME, "TalkLanguage", &dbv))
+ {
+ lstrcpyn(dlg->lang_name, dbv.ptszVal, MAX_REGS(dlg->lang_name));
+ DBFreeVariant(&dbv);
+ }
+
+ if (dlg->lang_name[0] == _T('\0') && !DBGetContactSettingTString(dlg->hContact, "eSpeak", "TalkLanguage", &dbv))
+ {
+ lstrcpyn(dlg->lang_name, dbv.ptszVal, MAX_REGS(dlg->lang_name));
+ DBFreeVariant(&dbv);
+ }
+
+ // Try from metacontact
+ if (dlg->lang_name[0] == _T('\0'))
+ {
+ INT_PTR mc = CallService(MS_MC_GETMETACONTACT, (WPARAM) dlg->hContact, 0);
+ if (mc != CALLSERVICE_NOTFOUND)
+ {
+ HANDLE hMetaContact = (HANDLE) mc;
+ if (hMetaContact != NULL)
+ {
+ if (!DBGetContactSettingTString(hMetaContact, MODULE_NAME, "TalkLanguage", &dbv))
+ {
+ lstrcpyn(dlg->lang_name, dbv.ptszVal, MAX_REGS(dlg->lang_name));
+ DBFreeVariant(&dbv);
+ }
+
+ if (dlg->lang_name[0] == _T('\0') && !DBGetContactSettingTString(hMetaContact, "eSpeak", "TalkLanguage", &dbv))
+ {
+ lstrcpyn(dlg->lang_name, dbv.ptszVal, MAX_REGS(dlg->lang_name));
+ DBFreeVariant(&dbv);
+ }
+ }
+ }
+ }
+
+ // Try to get from Language info
+ if (dlg->lang_name[0] == _T('\0'))
+ GetUserLanguageSetting(dlg, "Language");
+ if (dlg->lang_name[0] == _T('\0'))
+ GetUserLanguageSetting(dlg, "Language1");
+ if (dlg->lang_name[0] == _T('\0'))
+ GetUserLanguageSetting(dlg, "Language2");
+ if (dlg->lang_name[0] == _T('\0'))
+ GetUserLanguageSetting(dlg, "Language3");
+
+ // Use default lang
+ if (dlg->lang_name[0] == _T('\0'))
+ lstrcpyn(dlg->lang_name, opts.default_language, MAX_REGS(dlg->lang_name));
+ }
+
+ int i = GetClosestLanguage(dlg->lang_name);
+ if (i < 0)
+ {
+ // Lost a dict?
+ lstrcpyn(dlg->lang_name, opts.default_language, MAX_REGS(dlg->lang_name));
+ i = GetClosestLanguage(dlg->lang_name);
+ }
+
+ if (i >= 0)
+ {
+ dlg->lang = languages[i];
+ dlg->lang->load();
+ }
+ else
+ {
+ dlg->lang = NULL;
+ }
+}
+
+void ModifyIcon(Dialog *dlg)
+{
+ if (ServiceExists(MS_MSG_MODIFYICON))
+ {
+ StatusIconData sid = {0};
+ sid.cbSize = sizeof(sid);
+ sid.szModule = MODULE_NAME;
+
+ for (int i = 0; i < languages.getCount(); i++)
+ {
+ sid.dwId = i;
+
+ if (languages[i] == dlg->lang)
+ sid.flags = (dlg->enabled ? 0 : MBF_DISABLED);
+ else
+ sid.flags = MBF_HIDDEN;
+
+ CallService(MS_MSG_MODIFYICON, (WPARAM) dlg->hContact, (LPARAM) &sid);
+ }
+ }
+}
+
+INT_PTR AddContactTextBoxService(WPARAM wParam, LPARAM lParam)
+{
+ SPELLCHECKER_ITEM *sci = (SPELLCHECKER_ITEM *) wParam;
+ if (sci == NULL || sci->cbSize != sizeof(SPELLCHECKER_ITEM))
+ return -1;
+
+ return AddContactTextBox(sci->hContact, sci->hwnd, sci->window_name, FALSE, NULL);
+}
+
+
+void NotifyWrongSRMM()
+{
+ static BOOL notified = FALSE;
+
+ if (notified)
+ return;
+
+ MessageBox(NULL,
+ TranslateT("Your message window does not support SpellChecker Plugin.\nIf you use SRMM, tabSRMM or Scriver, please update them to the latest version,\notherwise ask the author of your message window plugin to add support for Spell Checker."),
+ TranslateT("Spell Checker"), MB_ICONERROR | MB_OK);
+
+ notified = TRUE;
+}
+
+
+int AddContactTextBox(HANDLE hContact, HWND hwnd, char *name, BOOL srmm, HWND hwndOwner)
+{
+ if (languages.getCount() <= 0)
+ return 0;
+
+ if (dialogs.find(hwnd) == dialogs.end())
+ {
+ // Fill dialog data
+ Dialog *dlg = (Dialog *) malloc(sizeof(Dialog));
+ ZeroMemory(dlg, sizeof(Dialog));
+
+ dlg->re = new RichEdit(hwnd);
+ if (!dlg->re->IsValid())
+ {
+ delete dlg->re;
+ free(dlg);
+
+ if (srmm)
+ NotifyWrongSRMM();
+
+ return 0;
+ }
+
+ dlg->hContact = hContact;
+ dlg->hwnd = hwnd;
+ strncpy(dlg->name, name, sizeof(dlg->name));
+ dlg->enabled = DBGetContactSettingByte(dlg->hContact, MODULE_NAME, dlg->name, 1);
+ dlg->srmm = srmm;
+
+ GetContactLanguage(dlg);
+
+ if (opts.auto_locale)
+ LoadDictFromKbdl(dlg);
+
+ dlg->old_edit_proc = (WNDPROC) SetWindowLongPtr(dlg->hwnd, GWLP_WNDPROC, (LONG_PTR) EditProc);
+ dialogs[hwnd] = dlg;
+
+ if (dlg->srmm && hwndOwner != NULL)
+ {
+ dlg->hwnd_owner = hwndOwner;
+ dlg->owner_old_edit_proc = (WNDPROC) SetWindowLongPtr(dlg->hwnd_owner, GWLP_WNDPROC, (LONG_PTR) OwnerProc);
+ dialogs[dlg->hwnd_owner] = dlg;
+
+ ModifyIcon(dlg);
+ }
+
+ if (dlg->lang != NULL)
+ dlg->lang->load();
+
+ SetTimer(hwnd, TIMER_ID, 1000, NULL);
+ }
+
+ return 0;
+}
+
+#define DESTROY_MENY(_m_) if (_m_ != NULL) { DestroyMenu(_m_); _m_ = NULL; }
+
+void FreePopupData(Dialog *dlg)
+{
+ DESTROY_MENY(dlg->hLanguageSubMenu)
+ DESTROY_MENY(dlg->hWrongWordsSubMenu)
+
+ if (dlg->old_menu_proc != NULL)
+ SetWindowLongPtr(dlg->hwnd_menu_owner, GWLP_WNDPROC, (LONG_PTR) dlg->old_menu_proc);
+ dlg->old_menu_proc = NULL;
+
+ if (dlg->hwnd_menu_owner != NULL)
+ menus.erase(dlg->hwnd_menu_owner);
+ dlg->hwnd_menu_owner = NULL;
+
+ if (dlg->wrong_words != NULL)
+ {
+ for (unsigned i = 0; i < dlg->wrong_words->size(); i++)
+ {
+ FREE((*dlg->wrong_words)[i].word)
+
+ DESTROY_MENY((*dlg->wrong_words)[i].hMeSubMenu)
+ DESTROY_MENY((*dlg->wrong_words)[i].hCorrectSubMenu)
+ DESTROY_MENY((*dlg->wrong_words)[i].hReplaceSubMenu)
+
+ FreeSuggestions((*dlg->wrong_words)[i].suggestions);
+ }
+
+ delete dlg->wrong_words;
+ dlg->wrong_words = NULL;
+ }
+}
+
+
+INT_PTR RemoveContactTextBoxService(WPARAM wParam, LPARAM lParam)
+{
+ HWND hwnd = (HWND) wParam;
+ if (hwnd == NULL)
+ return -1;
+
+ return RemoveContactTextBox(hwnd);
+}
+
+
+int RemoveContactTextBox(HWND hwnd)
+{
+ DialogMapType::iterator dlgit = dialogs.find(hwnd);
+ if (dlgit != dialogs.end())
+ {
+ Dialog *dlg = dlgit->second;
+
+ KillTimer(hwnd, TIMER_ID);
+
+ if (dlg->old_edit_proc != NULL)
+ SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR) dlg->old_edit_proc);
+ dialogs.erase(hwnd);
+
+ if (dlg->hwnd_owner != NULL)
+ {
+ if (dlg->owner_old_edit_proc != NULL)
+ SetWindowLongPtr(dlg->hwnd_owner, GWLP_WNDPROC, (LONG_PTR) dlg->owner_old_edit_proc);
+ dialogs.erase(dlg->hwnd_owner);
+ }
+
+ delete dlg->re;
+ FreePopupData(dlg);
+ free(dlg);
+ }
+
+ return 0;
+}
+
+
+// TODO Make this better
+BOOL GetWordCharRange(Dialog *dlg, CHARRANGE &sel, TCHAR *text, size_t text_len, int &first_char)
+{
+ // Get line
+ int line = dlg->re->GetLineFromChar(sel.cpMin);
+
+ // Get text
+ dlg->re->GetLine(line, text, text_len);
+ first_char = dlg->re->GetFirstCharOfLine(line);
+
+ // Find the word
+ sel.cpMin--;
+ while (sel.cpMin >= first_char && (dlg->lang->isWordChar(text[sel.cpMin - first_char])
+ || IsNumber(text[sel.cpMin - first_char])))
+ sel.cpMin--;
+ sel.cpMin++;
+
+ while (text[sel.cpMax - first_char] != _T('\0') && (dlg->lang->isWordChar(text[sel.cpMax - first_char])
+ || IsNumber(text[sel.cpMax - first_char])))
+ sel.cpMax++;
+
+ // Has a word?
+ if (sel.cpMin >= sel.cpMax)
+ return FALSE;
+
+ // See if it has only '-'s
+ BOOL has_valid_char = FALSE;
+ for (int i = sel.cpMin; i < sel.cpMax && !has_valid_char; i++)
+ has_valid_char = ( text[i - first_char] != _T('-') );
+
+ return has_valid_char;
+}
+
+TCHAR *GetWordUnderPoint(Dialog *dlg, POINT pt, CHARRANGE &sel)
+{
+ // Get text
+ if (dlg->re->GetTextLength() <= 0)
+ return NULL;
+
+ // Get pos
+ sel.cpMin = sel.cpMax = dlg->re->GetCharFromPos(pt);
+
+ // Get text
+ TCHAR text[1024];
+ int first_char;
+
+ if (!GetWordCharRange(dlg, sel, text, MAX_REGS(text), first_char))
+ return NULL;
+
+ // copy the word
+ text[sel.cpMax - first_char] = _T('\0');
+ return _tcsdup(&text[sel.cpMin - first_char]);
+}
+
+
+void AppendSubmenu(HMENU hMenu, HMENU hSubMenu, TCHAR *name)
+{
+ MENUITEMINFO mii = {0};
+ mii.cbSize = sizeof(MENUITEMINFO);
+ mii.fMask = MIIM_SUBMENU | MIIM_TYPE;
+ mii.fType = MFT_STRING;
+ mii.hSubMenu = hSubMenu;
+ mii.dwTypeData = name;
+ mii.cch = lstrlen(name);
+ int ret = InsertMenuItem(hMenu, 0, TRUE, &mii);
+
+}
+
+void AppendMenuItem(HMENU hMenu, int id, TCHAR *name, HICON hIcon, BOOL checked)
+{
+ ICONINFO iconInfo;
+ GetIconInfo(hIcon, & iconInfo);
+
+ MENUITEMINFO mii = {0};
+ mii.cbSize = sizeof(MENUITEMINFO);
+ mii.fMask = MIIM_CHECKMARKS | MIIM_TYPE | MIIM_STATE;
+ mii.fType = MFT_STRING;
+ mii.fState = (checked ? MFS_CHECKED : 0);
+ mii.wID = id;
+ mii.hbmpChecked = iconInfo.hbmColor;
+ mii.hbmpUnchecked = iconInfo.hbmColor;
+ mii.dwTypeData = name;
+ mii.cch = lstrlen(name);
+ int ret = InsertMenuItem(hMenu, 0, TRUE, &mii);
+}
+
+
+
+
+#define LANGUAGE_MENU_ID_BASE 10
+#define WORD_MENU_ID_BASE 100
+#define AUTOREPLACE_MENU_ID_BASE 50
+
+void AddMenuForWord(Dialog *dlg, TCHAR *word, CHARRANGE &pos, HMENU hMenu, BOOL in_submenu, UINT base)
+{
+ if (dlg->wrong_words == NULL)
+ dlg->wrong_words = new vector<WrongWordPopupMenuData>(1);
+ else
+ dlg->wrong_words->resize(dlg->wrong_words->size() + 1);
+
+ WrongWordPopupMenuData &data = (*dlg->wrong_words)[dlg->wrong_words->size() - 1];
+ ZeroMemory(&data, sizeof(WrongWordPopupMenuData));
+
+ // Get suggestions
+ data.word = word;
+ data.pos = pos;
+ data.suggestions = dlg->lang->suggest(word);
+
+ Suggestions &suggestions = data.suggestions;
+
+ if (in_submenu)
+ {
+ data.hMeSubMenu = CreatePopupMenu();
+ AppendSubmenu(hMenu, data.hMeSubMenu, word);
+ hMenu = data.hMeSubMenu;
+ }
+
+ data.hReplaceSubMenu = CreatePopupMenu();
+
+ InsertMenu(data.hReplaceSubMenu, 0, MF_BYPOSITION,
+ base + AUTOREPLACE_MENU_ID_BASE + suggestions.count, TranslateT("Other..."));
+ if (suggestions.count > 0)
+ {
+ InsertMenu(data.hReplaceSubMenu, 0, MF_BYPOSITION | MF_SEPARATOR, 0, 0);
+ for (int i = suggestions.count - 1; i >= 0; i--)
+ InsertMenu(data.hReplaceSubMenu, 0, MF_BYPOSITION,
+ base + AUTOREPLACE_MENU_ID_BASE + i, suggestions.words[i]);
+ }
+
+ AppendSubmenu(hMenu, data.hReplaceSubMenu, TranslateT("Always replace with"));
+
+ InsertMenu(hMenu, 0, MF_BYPOSITION, base + suggestions.count + 1, TranslateT("Ignore all"));
+ InsertMenu(hMenu, 0, MF_BYPOSITION, base + suggestions.count, TranslateT("Add to dictionary"));
+
+ if (suggestions.count > 0)
+ {
+ HMENU hSubMenu;
+ if (opts.cascade_corrections)
+ {
+ hSubMenu = data.hCorrectSubMenu = CreatePopupMenu();
+ AppendSubmenu(hMenu, hSubMenu, TranslateT("Corrections"));
+ }
+ else
+ {
+ InsertMenu(hMenu, 0, MF_BYPOSITION | MF_SEPARATOR, 0, 0);
+ hSubMenu = hMenu;
+ }
+
+ for (int i = suggestions.count - 1; i >= 0; i--)
+ InsertMenu(hSubMenu, 0, MF_BYPOSITION, base + i, suggestions.words[i]);
+ }
+
+ if (!in_submenu && opts.show_wrong_word)
+ {
+ InsertMenu(hMenu, 0, MF_BYPOSITION | MF_SEPARATOR, 0, 0);
+
+ TCHAR text[128];
+ mir_sntprintf(text, MAX_REGS(text), TranslateT("Wrong word: %s"), word);
+ InsertMenu(hMenu, 0, MF_BYPOSITION, 0, text);
+ }
+}
+
+
+struct FoundWrongWordParam {
+ Dialog *dlg;
+ int count;
+};
+
+void FoundWrongWord(TCHAR *word, CHARRANGE pos, void *param)
+{
+ FoundWrongWordParam *p = (FoundWrongWordParam*) param;
+
+ p->count ++;
+
+ AddMenuForWord(p->dlg, _tcsdup(word), pos, p->dlg->hWrongWordsSubMenu, TRUE, WORD_MENU_ID_BASE * p->count);
+}
+
+void AddItemsToMenu(Dialog *dlg, HMENU hMenu, POINT pt, HWND hwndOwner)
+{
+ FreePopupData(dlg);
+ if (opts.use_flags)
+ {
+ dlg->hwnd_menu_owner = hwndOwner;
+ menus[hwndOwner] = dlg;
+ }
+
+ BOOL wrong_word = FALSE;
+
+ // Make menu
+ if (GetMenuItemCount(hMenu) > 0)
+ InsertMenu(hMenu, 0, MF_BYPOSITION | MF_SEPARATOR, 0, 0);
+
+ if (languages.getCount() > 0 && dlg->enabled)
+ {
+ dlg->hLanguageSubMenu = CreatePopupMenu();
+
+ if (dlg->hwnd_menu_owner != NULL)
+ dlg->old_menu_proc = (WNDPROC) SetWindowLongPtr(dlg->hwnd_menu_owner, GWLP_WNDPROC, (LONG_PTR) MenuWndProc);
+
+ // First add languages
+ for (int i = 0; i < languages.getCount(); i++)
+ {
+ AppendMenu(dlg->hLanguageSubMenu, MF_STRING | (languages[i] == dlg->lang ? MF_CHECKED : 0),
+ LANGUAGE_MENU_ID_BASE + i, languages[i]->full_name);
+ }
+
+ AppendSubmenu(hMenu, dlg->hLanguageSubMenu, TranslateT("Language"));
+ }
+
+ InsertMenu(hMenu, 0, MF_BYPOSITION, 1, TranslateT("Enable spell checking"));
+ CheckMenuItem(hMenu, 1, MF_BYCOMMAND | (dlg->enabled ? MF_CHECKED : MF_UNCHECKED));
+
+ // Get text
+ if (dlg->lang != NULL && dlg->enabled)
+ {
+ if (opts.show_all_corrections)
+ {
+ dlg->hWrongWordsSubMenu = CreatePopupMenu();
+
+ FoundWrongWordParam p = { dlg, 0 };
+ CheckText(dlg, TRUE, FoundWrongWord, &p);
+
+ if (p.count > 0)
+ AppendSubmenu(hMenu, dlg->hWrongWordsSubMenu, TranslateT("Wrong words"));
+ }
+ else
+ {
+ CHARRANGE sel;
+ TCHAR *word = GetWordUnderPoint(dlg, pt, sel);
+ if (word != NULL && !dlg->lang->spell(word))
+ {
+ InsertMenu(hMenu, 0, MF_BYPOSITION | MF_SEPARATOR, 0, 0);
+
+ AddMenuForWord(dlg, word, sel, hMenu, FALSE, WORD_MENU_ID_BASE);
+ }
+ }
+ }
+}
+
+
+static void AddWordToDictCallback(BOOL canceled, Dictionary *dict,
+ const TCHAR *find, const TCHAR *replace, BOOL useVariables,
+ const TCHAR *original_find, void *param)
+{
+ if (canceled)
+ return;
+
+ dict->autoReplace->add(find, replace, useVariables);
+
+ HWND hwndParent = (HWND) param;
+ if (hwndParent != NULL)
+ PostMessage(hwndParent, WMU_DICT_CHANGED, 0, 0);
+}
+
+
+BOOL HandleMenuSelection(Dialog *dlg, POINT pt, unsigned selection)
+{
+ BOOL ret = FALSE;
+
+ if (selection == 1)
+ {
+ ToggleEnabled(dlg);
+
+ ret = TRUE;
+ }
+ else if (selection >= LANGUAGE_MENU_ID_BASE && selection < LANGUAGE_MENU_ID_BASE + (unsigned) languages.getCount())
+ {
+ SetNoUnderline(dlg);
+
+ if (dlg->hContact == NULL)
+ DBWriteContactSettingTString(NULL, MODULE_NAME, dlg->name,
+ languages[selection - LANGUAGE_MENU_ID_BASE]->language);
+ else
+ DBWriteContactSettingTString(dlg->hContact, MODULE_NAME, "TalkLanguage",
+ languages[selection - LANGUAGE_MENU_ID_BASE]->language);
+
+ GetContactLanguage(dlg);
+
+ if (dlg->srmm)
+ ModifyIcon(dlg);
+
+ ret = TRUE;
+ }
+ else if (selection > 0 && dlg->wrong_words != NULL
+ && selection >= WORD_MENU_ID_BASE
+ && selection < (dlg->wrong_words->size() + 1) * WORD_MENU_ID_BASE)
+ {
+ int pos = selection / WORD_MENU_ID_BASE;
+ selection -= pos * WORD_MENU_ID_BASE;
+ pos--; // 0 based
+ WrongWordPopupMenuData &data = (*dlg->wrong_words)[pos];
+
+ if (selection < data.suggestions.count)
+ {
+ // TODO Assert that text hasn't changed
+ ReplaceWord(dlg, data.pos, data.suggestions.words[selection]);
+
+ ret = TRUE;
+ }
+ else if (selection == data.suggestions.count)
+ {
+ dlg->lang->addWord(data.word);
+
+ ret = TRUE;
+ }
+ else if (selection == data.suggestions.count + 1)
+ {
+ dlg->lang->ignoreWord(data.word);
+
+ ret = TRUE;
+ }
+ else if (selection >= AUTOREPLACE_MENU_ID_BASE
+ && selection < AUTOREPLACE_MENU_ID_BASE + data.suggestions.count + 1)
+ {
+ selection -= AUTOREPLACE_MENU_ID_BASE;
+
+ if (selection == data.suggestions.count)
+ {
+ ShowAutoReplaceDialog(dlg->hwnd_owner != NULL ? dlg->hwnd_owner : dlg->hwnd, FALSE,
+ dlg->lang, data.word, NULL, FALSE,
+ TRUE, &AddWordToDictCallback, dlg->hwnd);
+ }
+ else
+ {
+ // TODO Assert that text hasn't changed
+ ReplaceWord(dlg, data.pos, data.suggestions.words[selection]);
+ dlg->lang->autoReplace->add(data.word, data.suggestions.words[selection]);
+ ret = TRUE;
+ }
+ }
+ }
+
+ if (ret)
+ {
+ KillTimer(dlg->hwnd, TIMER_ID);
+ SetTimer(dlg->hwnd, TIMER_ID, 100, NULL);
+
+ dlg->changed = TRUE;
+ }
+
+ FreePopupData(dlg);
+
+ return ret;
+}
+
+
+int MsgWindowPopup(WPARAM wParam, LPARAM lParam)
+{
+ MessageWindowPopupData *mwpd = (MessageWindowPopupData *) lParam;
+ if (mwpd == NULL || mwpd->cbSize < sizeof(MessageWindowPopupData)
+ || mwpd->uFlags != MSG_WINDOWPOPUP_INPUT)
+ return 0;
+
+ DialogMapType::iterator dlgit = dialogs.find(mwpd->hwnd);
+ if (dlgit == dialogs.end())
+ return -1;
+
+ Dialog *dlg = dlgit->second;
+
+ POINT pt = mwpd->pt;
+ ScreenToClient(dlg->hwnd, &pt);
+
+ if (mwpd->uType == MSG_WINDOWPOPUP_SHOWING)
+ {
+ AddItemsToMenu(dlg, mwpd->hMenu, pt, dlg->hwnd_owner);
+ }
+ else if (mwpd->uType == MSG_WINDOWPOPUP_SELECTED)
+ {
+ HandleMenuSelection(dlg, pt, mwpd->selection);
+ }
+ return 0;
+}
+
+
+INT_PTR ShowPopupMenuService(WPARAM wParam, LPARAM lParam)
+{
+ SPELLCHECKER_POPUPMENU *scp = (SPELLCHECKER_POPUPMENU *) wParam;
+ if (scp == NULL || scp->cbSize != sizeof(SPELLCHECKER_POPUPMENU))
+ return -1;
+
+ return ShowPopupMenu(scp->hwnd, scp->hMenu, scp->pt, scp->hwndOwner == NULL ? scp->hwnd : scp->hwndOwner);
+}
+
+
+int ShowPopupMenu(HWND hwnd, HMENU hMenu, POINT pt, HWND hwndOwner)
+{
+ DialogMapType::iterator dlgit = dialogs.find(hwnd);
+ if (dlgit == dialogs.end())
+ return -1;
+
+ Dialog *dlg = dlgit->second;
+
+ if (pt.x == 0xFFFF && pt.y == 0xFFFF)
+ {
+ CHARRANGE sel;
+ SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM) &sel);
+
+ // Get current cursor pos
+ SendMessage(hwnd, EM_POSFROMCHAR, (WPARAM)&pt, (LPARAM) sel.cpMax);
+ }
+ else
+ {
+ ScreenToClient(hwnd, &pt);
+ }
+
+ BOOL create_menu = (hMenu == NULL);
+ if (create_menu)
+ hMenu = CreatePopupMenu();
+
+ // Make menu
+ AddItemsToMenu(dlg, hMenu, pt, hwndOwner);
+
+ // Show menu
+ POINT client = pt;
+ ClientToScreen(hwnd, &pt);
+ int selection = TrackPopupMenu(hMenu, TPM_RETURNCMD, pt.x, pt.y, 0, hwndOwner, NULL);
+
+ // Do action
+ if (HandleMenuSelection(dlg, client, selection))
+ selection = 0;
+
+ if (create_menu)
+ DestroyMenu(hMenu);
+
+ return selection;
+}
+
+
+int MsgWindowEvent(WPARAM wParam, LPARAM lParam)
+{
+ MessageWindowEventData *event = (MessageWindowEventData *)lParam;
+ if (event == NULL)
+ return 0;
+
+ if (event->cbSize < sizeof(MessageWindowEventData))
+ return 0;
+
+ if (event->uType == MSG_WINDOW_EVT_OPEN)
+ {
+ AddContactTextBox(event->hContact, event->hwndInput, "DefaultSRMM", TRUE, event->hwndWindow);
+ }
+ else if (event->uType == MSG_WINDOW_EVT_CLOSING)
+ {
+ RemoveContactTextBox(event->hwndInput);
+ }
+
+ return 0;
+}
+
+
+int IconPressed(WPARAM wParam, LPARAM lParam)
+{
+ StatusIconClickData *sicd = (StatusIconClickData *) lParam;
+ if (sicd == NULL || strcmp(sicd->szModule, MODULE_NAME) != 0)
+ return 0;
+
+ HANDLE hContact = (HANDLE) wParam;
+ if (hContact == NULL)
+ return 0;
+
+ // Find the dialog
+ HWND hwnd = NULL;
+ Dialog *dlg;
+ for(DialogMapType::iterator it = dialogs.begin(); it != dialogs.end(); it++)
+ {
+ dlg = it->second;
+ if (dlg->srmm && dlg->hContact == hContact)
+ {
+ hwnd = it->first;
+ break;
+ }
+ }
+
+ if (hwnd == NULL)
+ {
+ NotifyWrongSRMM();
+ return 0;
+ }
+
+ if ((sicd->flags & MBCF_RIGHTBUTTON) == 0)
+ {
+ FreePopupData(dlg);
+
+ // Show the menu
+ HMENU hMenu = CreatePopupMenu();
+
+ if (languages.getCount() > 0)
+ {
+ if (opts.use_flags)
+ {
+ menus[dlg->hwnd] = dlg;
+ dlg->hwnd_menu_owner = dlg->hwnd;
+ dlg->old_menu_proc = (WNDPROC) SetWindowLongPtr(dlg->hwnd_menu_owner, GWLP_WNDPROC, (LONG_PTR) MenuWndProc);
+ }
+
+ // First add languages
+ for (int i = 0; i < languages.getCount(); i++)
+ {
+ AppendMenu(hMenu, MF_STRING | (languages[i] == dlg->lang ? MF_CHECKED : 0),
+ LANGUAGE_MENU_ID_BASE + i, languages[i]->full_name);
+ }
+
+ InsertMenu(hMenu, 0, MF_BYPOSITION | MF_SEPARATOR, 0, 0);
+ }
+
+ InsertMenu(hMenu, 0, MF_BYPOSITION, 1, TranslateT("Enable spell checking"));
+ CheckMenuItem(hMenu, 1, MF_BYCOMMAND | (dlg->enabled ? MF_CHECKED : MF_UNCHECKED));
+
+ // Show menu
+ int selection = TrackPopupMenu(hMenu, TPM_RETURNCMD, sicd->clickLocation.x, sicd->clickLocation.y, 0,
+ dlg->hwnd, NULL);
+
+ HandleMenuSelection(dlg, sicd->clickLocation, selection);
+
+ DestroyMenu(hMenu);
+ }
+ else
+ {
+ // Enable / disable
+ HandleMenuSelection(dlg, sicd->clickLocation, 1);
+ }
+
+ return 0;
+}
+
+
+LRESULT CALLBACK MenuWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ DialogMapType::iterator dlgit = menus.find(hwnd);
+ if (dlgit == menus.end())
+ return -1;
+
+ Dialog *dlg = dlgit->second;
+
+ switch (msg)
+ {
+ case WM_INITMENUPOPUP:
+ {
+ HMENU hMenu = (HMENU) wParam;
+
+ int count = GetMenuItemCount(hMenu);
+ for(int i = 0; i < count; i++)
+ {
+ unsigned id = GetMenuItemID(hMenu, i);
+ if (id < LANGUAGE_MENU_ID_BASE || id >= LANGUAGE_MENU_ID_BASE + (unsigned) languages.getCount())
+ continue;
+
+ MENUITEMINFO mii = {0};
+ mii.cbSize = sizeof(MENUITEMINFO);
+ mii.fMask = MIIM_STATE;
+ GetMenuItemInfo(hMenu, id, FALSE, &mii);
+
+ // Make ownerdraw
+ ModifyMenu(hMenu, id, mii.fState | MF_BYCOMMAND | MF_OWNERDRAW, id, NULL);
+ }
+
+ break;
+ }
+ case WM_DRAWITEM:
+ {
+ LPDRAWITEMSTRUCT lpdis = (LPDRAWITEMSTRUCT)lParam;
+ if (lpdis->CtlType != ODT_MENU || lpdis->itemID < LANGUAGE_MENU_ID_BASE || lpdis->itemID >= LANGUAGE_MENU_ID_BASE + (unsigned) languages.getCount())
+ break;
+
+ int pos = lpdis->itemID - LANGUAGE_MENU_ID_BASE;
+
+ Dictionary *dict = languages[pos];
+
+ COLORREF clrfore = SetTextColor(lpdis->hDC,
+ GetSysColor(lpdis->itemState & ODS_SELECTED ? COLOR_HIGHLIGHTTEXT : COLOR_MENUTEXT));
+ COLORREF clrback = SetBkColor(lpdis->hDC,
+ GetSysColor(lpdis->itemState & ODS_SELECTED ? COLOR_HIGHLIGHT : COLOR_MENU));
+
+ FillRect(lpdis->hDC, &lpdis->rcItem, GetSysColorBrush(lpdis->itemState & ODS_SELECTED ? COLOR_HIGHLIGHT : COLOR_MENU));
+
+ RECT rc = lpdis->rcItem;
+ rc.left += 2;
+
+ // Checked?
+ rc.right = rc.left + bmpChecked.bmWidth;
+
+ if (lpdis->itemState & ODS_CHECKED)
+ {
+ rc.top = (lpdis->rcItem.bottom + lpdis->rcItem.top - bmpChecked.bmHeight) / 2;
+ rc.bottom = rc.top + bmpChecked.bmHeight;
+
+ HDC hdcTemp = CreateCompatibleDC(lpdis->hDC);
+ HBITMAP oldBmp = (HBITMAP) SelectObject(hdcTemp, hCheckedBmp);
+
+ BitBlt(lpdis->hDC, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, hdcTemp, 0, 0, SRCCOPY);
+
+ SelectObject(hdcTemp, oldBmp);
+ DeleteDC(hdcTemp);
+ }
+
+ rc.left += bmpChecked.bmWidth + 2;
+
+ // Draw icon
+ HICON hFlag = IcoLib_LoadIcon(dict);
+
+ rc.top = (lpdis->rcItem.bottom + lpdis->rcItem.top - ICON_SIZE) / 2;
+ DrawIconEx(lpdis->hDC, rc.left, rc.top, hFlag, 16, 16, 0, NULL, DI_NORMAL);
+
+ IcoLib_ReleaseIcon(hFlag);
+
+ rc.left += ICON_SIZE + 4;
+
+ // Draw text
+ RECT rc_text = { 0, 0, 0xFFFF, 0xFFFF };
+ DrawText(lpdis->hDC, dict->full_name, lstrlen(dict->full_name), &rc_text, DT_END_ELLIPSIS | DT_NOPREFIX | DT_SINGLELINE | DT_LEFT | DT_TOP | DT_CALCRECT);
+
+ rc.right = lpdis->rcItem.right - 2;
+ rc.top = (lpdis->rcItem.bottom + lpdis->rcItem.top - (rc_text.bottom - rc_text.top)) / 2;
+ rc.bottom = rc.top + rc_text.bottom - rc_text.top;
+ DrawText(lpdis->hDC, dict->full_name, lstrlen(dict->full_name), &rc, DT_END_ELLIPSIS | DT_NOPREFIX | DT_LEFT | DT_TOP | DT_SINGLELINE);
+
+ // Restore old colors
+ SetTextColor(lpdis->hDC, clrfore);
+ SetBkColor(lpdis->hDC, clrback);
+
+ return TRUE;
+ }
+
+ case WM_MEASUREITEM:
+ {
+ LPMEASUREITEMSTRUCT lpmis = (LPMEASUREITEMSTRUCT)lParam;
+ if (lpmis->CtlType != ODT_MENU || lpmis->itemID < LANGUAGE_MENU_ID_BASE || lpmis->itemID >= LANGUAGE_MENU_ID_BASE + (unsigned) languages.getCount())
+ break;
+
+ int pos = lpmis->itemID - LANGUAGE_MENU_ID_BASE;
+
+ Dictionary *dict = languages[pos];
+
+ HDC hdc = GetDC(hwnd);
+
+ NONCLIENTMETRICS info;
+ ZeroMemory(&info, sizeof(info));
+ info.cbSize = sizeof(info);
+ SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(info), &info, 0);
+ HFONT hFont = CreateFontIndirect(&info.lfMenuFont);
+ HFONT hFontOld = (HFONT) SelectObject(hdc, hFont);
+
+ RECT rc = { 0, 0, 0xFFFF, 0xFFFF };
+
+ DrawText(hdc, dict->full_name, lstrlen(dict->full_name), &rc, DT_NOPREFIX | DT_SINGLELINE | DT_LEFT | DT_TOP | DT_CALCRECT);
+
+ lpmis->itemHeight = max(ICON_SIZE, max(bmpChecked.bmHeight, rc.bottom));
+ lpmis->itemWidth = 2 + bmpChecked.bmWidth + 2 + ICON_SIZE + 4 + rc.right + 2;
+
+ SelectObject(hdc, hFontOld);
+ DeleteObject(hFont);
+ ReleaseDC(hwnd, hdc);
+
+ return TRUE;
+ }
+ }
+
+ return CallWindowProc(dlg->old_menu_proc, hwnd, msg, wParam, lParam);
+}
+
+TCHAR *lstrtrim(TCHAR *str)
+{
+ int len = lstrlen(str);
+
+ int i;
+ for(i = len - 1; i >= 0 && (str[i] == ' ' || str[i] == '\t'); --i) ;
+ if (i < len - 1)
+ {
+ ++i;
+ str[i] = _T('\0');
+ len = i;
+ }
+
+ for(i = 0; i < len && (str[i] == ' ' || str[i] == '\t'); ++i) ;
+ if (i > 0)
+ memmove(str, &str[i], (len - i + 1) * sizeof(TCHAR));
+
+ return str;
+}
+
+BOOL lstreq(TCHAR *a, TCHAR *b, size_t len)
+{
+#ifdef UNICODE
+ a = CharLower(_tcsdup(a));
+ b = CharLower(_tcsdup(b));
+ BOOL ret;
+ if (len >= 0)
+ ret = !_tcsncmp(a, b, len);
+ else
+ ret = !_tcscmp(a, b);
+ free(a);
+ free(b);
+ return ret;
+#else
+ if (len > 0)
+ return !_tcsnicmp(a, b, len);
+ else
+ return !_tcsicmp(a, b);
+#endif
+}
+
+
+BOOL CreatePath(const TCHAR *path)
+{
+ TCHAR folder[1024];
+ lstrcpyn(folder, path, MAX_REGS(folder));
+
+ TCHAR *p = folder;
+ if (p[0] && p[1] && p[1] == _T(':') && p[2] == _T('\\')) p += 3; // skip drive letter
+
+ SetLastError(ERROR_SUCCESS);
+ while(p = _tcschr(p, '\\'))
+ {
+ *p = _T('\0');
+ CreateDirectory(folder, 0);
+ *p = _T('\\');
+ p++;
+ }
+ CreateDirectory(folder, 0);
+
+ DWORD lerr = GetLastError();
+ return (lerr == ERROR_SUCCESS || lerr == ERROR_ALREADY_EXISTS);
+}
diff --git a/Plugins/spellchecker/spellchecker.dsp b/Plugins/spellchecker/spellchecker.dsp
new file mode 100644
index 0000000..7445774
--- /dev/null
+++ b/Plugins/spellchecker/spellchecker.dsp
@@ -0,0 +1,407 @@
+# Microsoft Developer Studio Project File - Name="spellchecker" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=spellchecker - Win32 Release
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "spellchecker.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "spellchecker.mak" CFG="spellchecker - Win32 Release"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "spellchecker - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "spellchecker - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "spellchecker - Win32 Unicode Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "spellchecker - Win32 Unicode Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "spellchecker - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MD /W3 /GX /O1 /YX /FD /c
+# SUBTRACT BASE CPP /Fr
+# ADD CPP /nologo /G4 /MT /W3 /GX /O2 /Ob0 /I "../../include" /I "sdk" /D "WIN32" /D "W32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /Fr /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x417 /d "NDEBUG"
+# ADD RSC /l 0x417 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 user32.lib shell32.lib wininet.lib gdi32.lib /nologo /base:"0x67100000" /dll /machine:I386 /filealign:0x200
+# SUBTRACT BASE LINK32 /pdb:none /map
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib advapi32.lib oleaut32.lib /nologo /base:"0x3EC20000" /dll /map /debug /debugtype:both /machine:I386 /out:"..\..\bin\release\Plugins\spellchecker.dll" /pdbtype:sept /filealign:0x200 /ALIGN:4096 /ignore:4108
+# SUBTRACT LINK32 /profile /pdb:none
+
+!ELSEIF "$(CFG)" == "spellchecker - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Ignore_Export_Lib 0
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /G4 /MT /W3 /GX /O2 /Ob0 /I "../../include" /FR /YX /FD /c
+# ADD CPP /nologo /G4 /MTd /W3 /GX /ZI /Od /I "../../include" /I "sdk" /D "WIN32" /D "W32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /FR /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x417 /d "NDEBUG"
+# ADD RSC /l 0x417 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 comctl32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 /out:"..\..bin\release\Plugins\spellchecker.dll" /filealign:0x200 /ALIGN:4096 /ignore:4108
+# SUBTRACT BASE LINK32 /profile /pdb:none
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib advapi32.lib oleaut32.lib /nologo /base:"0x3EC20000" /dll /incremental:yes /debug /machine:I386 /out:"..\..\bin\debug\Plugins\spellchecker.dll" /filealign:0x200 /ALIGN:4096 /ignore:4108
+# SUBTRACT LINK32 /profile /pdb:none
+
+!ELSEIF "$(CFG)" == "spellchecker - Win32 Unicode Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "spellchecker___Win32_Unicode_Debug"
+# PROP BASE Intermediate_Dir "spellchecker___Win32_Unicode_Debug"
+# PROP BASE Ignore_Export_Lib 0
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Unicode_Debug"
+# PROP Intermediate_Dir "Unicode_Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /G4 /MTd /W3 /GX /ZI /Od /I "../../include" /FR /YX /FD /c
+# ADD CPP /nologo /G4 /MTd /W3 /GX /ZI /Od /I "../../include" /I "sdk" /D "WIN32" /D "W32" /D "_DEBUG" /D "_WINDOWS" /D "_UNICODE" /D "UNICODE" /D "_USRDLL" /FR /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x417 /d "NDEBUG"
+# ADD RSC /l 0x417 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 comctl32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /base:"0x32100000" /dll /incremental:yes /debug /machine:I386 /out:"..\..\bin\debug\Plugins\spellchecker.dll" /filealign:0x200 /ALIGN:4096 /ignore:4108
+# SUBTRACT BASE LINK32 /profile /pdb:none
+# ADD LINK32 oleaut32.lib kernel32.lib user32.lib gdi32.lib advapi32.lib /nologo /base:"0x3EC20000" /dll /incremental:yes /debug /machine:I386 /out:"..\..\bin\debug unicode\Plugins\spellcheckerW.dll" /filealign:0x200 /ALIGN:4096 /ignore:4108
+# SUBTRACT LINK32 /profile /pdb:none
+
+!ELSEIF "$(CFG)" == "spellchecker - Win32 Unicode Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "spellchecker___Win32_Unicode_Release"
+# PROP BASE Intermediate_Dir "spellchecker___Win32_Unicode_Release"
+# PROP BASE Ignore_Export_Lib 0
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Unicode_Release"
+# PROP Intermediate_Dir "Unicode_Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /G4 /MT /W3 /GX /O2 /Ob0 /I "../../include" /Fr /YX /FD /c
+# ADD CPP /nologo /G4 /MT /W3 /GX /O2 /Ob0 /I "../../include" /I "sdk" /D "WIN32" /D "W32" /D "NDEBUG" /D "_WINDOWS" /D "_UNICODE" /D "UNICODE" /D "_USRDLL" /Fr /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x417 /d "NDEBUG"
+# ADD RSC /l 0x417 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 comctl32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /base:"0x32100000" /dll /map /machine:I386 /out:"..\..\bin\release\Plugins\spellchecker.dll" /filealign:0x200 /ALIGN:4096 /ignore:4108
+# SUBTRACT BASE LINK32 /profile /pdb:none
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib advapi32.lib oleaut32.lib /nologo /base:"0x3EC20000" /dll /map /debug /debugtype:both /machine:I386 /out:"..\..\bin\release\Plugins\spellcheckerW.dll" /pdbtype:sept /filealign:0x200 /ALIGN:4096 /ignore:4108
+# SUBTRACT LINK32 /profile /pdb:none
+
+!ENDIF
+
+# Begin Target
+
+# Name "spellchecker - Win32 Release"
+# Name "spellchecker - Win32 Debug"
+# Name "spellchecker - Win32 Unicode Debug"
+# Name "spellchecker - Win32 Unicode Release"
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Group "hunspell Header Files"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=.\hunspell\affentry.hxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\hunspell\affixmgr.hxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\hunspell\atypes.hxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\hunspell\baseaffix.hxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\hunspell\config.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\hunspell\csutil.hxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\hunspell\dictmgr.hxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\hunspell\filemgr.hxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\hunspell\hashmgr.hxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\hunspell\htypes.hxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\hunspell\hunspell.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\hunspell\hunspell.hxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\hunspell\hunzip.hxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\hunspell\langnum.hxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\hunspell\replist.hxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\hunspell\suggestmgr.hxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\hunspell\w_char.hxx
+# End Source File
+# End Group
+# Begin Source File
+
+SOURCE=.\ardialog.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\autoreplace.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\commons.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\dictionary.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\m_spellchecker.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\utils\mir_icons.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\utils\mir_memory.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\utils\mir_options.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\options.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\resource.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\RichEdit.h
+# End Source File
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
+# Begin Source File
+
+SOURCE=.\res\no_spellcheck.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\resource.rc
+# End Source File
+# Begin Source File
+
+SOURCE=.\res\spellcheck.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\res\unknown.ico
+# End Source File
+# End Group
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Group "hunspell Source Files"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=.\hunspell\affentry.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\hunspell\affixmgr.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\hunspell\csutil.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\hunspell\dictmgr.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\hunspell\filemgr.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\hunspell\hashmgr.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\hunspell\hunspell.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\hunspell\hunzip.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\hunspell\phonet.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\hunspell\replist.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\hunspell\suggestmgr.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\hunspell\utf_info.cxx
+# End Source File
+# End Group
+# Begin Source File
+
+SOURCE=.\ardialog.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\autoreplace.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\dictionary.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\utils\mir_icons.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\utils\mir_options.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\options.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\RichEdit.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\spellchecker.cpp
+# End Source File
+# End Group
+# Begin Group "Docs"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=.\Docs\langpack_spellchecker.txt
+# End Source File
+# Begin Source File
+
+SOURCE=.\Docs\spellchecker_changelog.txt
+# End Source File
+# Begin Source File
+
+SOURCE=.\Docs\spellchecker_readme.txt
+# End Source File
+# Begin Source File
+
+SOURCE=.\Docs\spellchecker_version.txt
+# End Source File
+# End Group
+# End Target
+# End Project
diff --git a/Plugins/spellchecker/spellchecker.dsw b/Plugins/spellchecker/spellchecker.dsw
new file mode 100644
index 0000000..31b749f
--- /dev/null
+++ b/Plugins/spellchecker/spellchecker.dsw
@@ -0,0 +1,29 @@
+Microsoft Developer Studio Workspace File, Format Version 6.00
+# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
+
+###############################################################################
+
+Project: "spellchecker"=.\spellchecker.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Global:
+
+Package=<5>
+{{{
+}}}
+
+Package=<3>
+{{{
+}}}
+
+###############################################################################
+
diff --git a/Plugins/spellchecker/spellchecker.sln b/Plugins/spellchecker/spellchecker.sln
new file mode 100644
index 0000000..63e084a
--- /dev/null
+++ b/Plugins/spellchecker/spellchecker.sln
@@ -0,0 +1,37 @@
+Microsoft Visual Studio Solution File, Format Version 11.00
+# Visual Studio 2010
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "spellchecker", "spellchecker.vcxproj", "{5DB86086-10E9-42E7-AC90-503D2678C2A2}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Win32 = Debug|Win32
+ Debug|x64 = Debug|x64
+ Release|Win32 = Release|Win32
+ Release|x64 = Release|x64
+ Unicode Debug|Win32 = Unicode Debug|Win32
+ Unicode Debug|x64 = Unicode Debug|x64
+ Unicode Release|Win32 = Unicode Release|Win32
+ Unicode Release|x64 = Unicode Release|x64
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {5DB86086-10E9-42E7-AC90-503D2678C2A2}.Debug|Win32.ActiveCfg = Debug|Win32
+ {5DB86086-10E9-42E7-AC90-503D2678C2A2}.Debug|Win32.Build.0 = Debug|Win32
+ {5DB86086-10E9-42E7-AC90-503D2678C2A2}.Debug|x64.ActiveCfg = Debug|x64
+ {5DB86086-10E9-42E7-AC90-503D2678C2A2}.Debug|x64.Build.0 = Debug|x64
+ {5DB86086-10E9-42E7-AC90-503D2678C2A2}.Release|Win32.ActiveCfg = Release|Win32
+ {5DB86086-10E9-42E7-AC90-503D2678C2A2}.Release|Win32.Build.0 = Release|Win32
+ {5DB86086-10E9-42E7-AC90-503D2678C2A2}.Release|x64.ActiveCfg = Release|x64
+ {5DB86086-10E9-42E7-AC90-503D2678C2A2}.Release|x64.Build.0 = Release|x64
+ {5DB86086-10E9-42E7-AC90-503D2678C2A2}.Unicode Debug|Win32.ActiveCfg = Unicode Debug|Win32
+ {5DB86086-10E9-42E7-AC90-503D2678C2A2}.Unicode Debug|Win32.Build.0 = Unicode Debug|Win32
+ {5DB86086-10E9-42E7-AC90-503D2678C2A2}.Unicode Debug|x64.ActiveCfg = Unicode Debug|x64
+ {5DB86086-10E9-42E7-AC90-503D2678C2A2}.Unicode Debug|x64.Build.0 = Unicode Debug|x64
+ {5DB86086-10E9-42E7-AC90-503D2678C2A2}.Unicode Release|Win32.ActiveCfg = Unicode Release|Win32
+ {5DB86086-10E9-42E7-AC90-503D2678C2A2}.Unicode Release|Win32.Build.0 = Unicode Release|Win32
+ {5DB86086-10E9-42E7-AC90-503D2678C2A2}.Unicode Release|x64.ActiveCfg = Unicode Release|x64
+ {5DB86086-10E9-42E7-AC90-503D2678C2A2}.Unicode Release|x64.Build.0 = Unicode Release|x64
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/Plugins/spellchecker/spellchecker.vcproj b/Plugins/spellchecker/spellchecker.vcproj
new file mode 100644
index 0000000..15b5c1a
--- /dev/null
+++ b/Plugins/spellchecker/spellchecker.vcproj
@@ -0,0 +1,2570 @@
+<?xml version="1.0" encoding="windows-1250"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="8,00"
+ Name="spellchecker"
+ ProjectGUID="{5DB86086-10E9-42E7-AC90-503D2678C2A2}"
+ RootNamespace="spellchecker"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ <Platform
+ Name="x64"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Unicode Release|Win32"
+ OutputDirectory=".\Unicode_Release"
+ IntermediateDirectory=".\Unicode_Release"
+ ConfigurationType="2"
+ InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC60.vsprops"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ PreprocessorDefinitions="NDEBUG"
+ MkTypLibCompatible="true"
+ SuppressStartupBanner="true"
+ TargetEnvironment="1"
+ TypeLibraryName=".\Unicode_Release/spellchecker.tlb"
+ HeaderFileName=""
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ InlineFunctionExpansion="0"
+ AdditionalIncludeDirectories="../../include,sdk"
+ PreprocessorDefinitions="WIN32;W32;NDEBUG;_WINDOWS;UNICODE;_USRDLL"
+ StringPooling="true"
+ RuntimeLibrary="0"
+ EnableFunctionLevelLinking="true"
+ PrecompiledHeaderFile=".\Unicode_Release/spellchecker.pch"
+ AssemblerListingLocation=".\Unicode_Release/"
+ ObjectFile=".\Unicode_Release/"
+ ProgramDataBaseFileName=".\Unicode_Release/"
+ BrowseInformation="2"
+ BrowseInformationFile=".\Unicode_Release/"
+ WarningLevel="3"
+ SuppressStartupBanner="true"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="NDEBUG"
+ Culture="1047"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalOptions="/ALIGN:4096 /filealign:0x200 /ignore:4108 "
+ OutputFile="..\..\bin\release\Plugins\spellcheckerW.dll"
+ LinkIncremental="1"
+ SuppressStartupBanner="true"
+ GenerateDebugInformation="true"
+ ProgramDatabaseFile=".\Unicode_Release/spellcheckerW.pdb"
+ GenerateMapFile="true"
+ MapFileName=".\Unicode_Release/spellcheckerW.map"
+ BaseAddress="0x3EC20000"
+ ImportLibrary=".\Unicode_Release/spellcheckerW.lib"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ SuppressStartupBanner="true"
+ OutputFile=".\Unicode_Release/spellchecker.bsc"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Unicode Release|x64"
+ OutputDirectory=".\Release64"
+ IntermediateDirectory=".\Release64"
+ ConfigurationType="2"
+ InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC60.vsprops"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ PreprocessorDefinitions="NDEBUG"
+ MkTypLibCompatible="true"
+ SuppressStartupBanner="true"
+ TargetEnvironment="3"
+ TypeLibraryName=".\Unicode_Release/spellchecker.tlb"
+ HeaderFileName=""
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ InlineFunctionExpansion="0"
+ AdditionalIncludeDirectories="../../include,sdk"
+ PreprocessorDefinitions="WIN64;WIN32;W32;NDEBUG;_WINDOWS;UNICODE;_USRDLL"
+ StringPooling="true"
+ RuntimeLibrary="0"
+ EnableFunctionLevelLinking="true"
+ PrecompiledHeaderFile=".\Release64/spellchecker.pch"
+ AssemblerListingLocation=".\Release64/"
+ ObjectFile=".\Release64/"
+ ProgramDataBaseFileName=".\Release64/"
+ BrowseInformation="2"
+ BrowseInformationFile=".\Release64/"
+ WarningLevel="3"
+ SuppressStartupBanner="true"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="NDEBUG"
+ Culture="1047"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalOptions="/ALIGN:4096 /filealign:0x200 /ignore:4108 "
+ OutputFile="..\..\bin\release\Plugins\spellchecker64.dll"
+ LinkIncremental="1"
+ SuppressStartupBanner="true"
+ GenerateDebugInformation="true"
+ ProgramDatabaseFile=".\Release64/spellchecker64.pdb"
+ GenerateMapFile="true"
+ MapFileName=".\Release64/spellchecker64.map"
+ BaseAddress="0x3EC20000"
+ ImportLibrary=".\Release64/spellchecker64.lib"
+ TargetMachine="17"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ SuppressStartupBanner="true"
+ OutputFile=".\Release64/spellchecker.bsc"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Unicode Debug|Win32"
+ OutputDirectory=".\Unicode_Debug"
+ IntermediateDirectory=".\Unicode_Debug"
+ ConfigurationType="2"
+ InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC60.vsprops"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ PreprocessorDefinitions="NDEBUG"
+ MkTypLibCompatible="true"
+ SuppressStartupBanner="true"
+ TargetEnvironment="1"
+ TypeLibraryName=".\Unicode_Debug/spellchecker.tlb"
+ HeaderFileName=""
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories="../../include,sdk"
+ PreprocessorDefinitions="WIN32;W32;_DEBUG;_WINDOWS;UNICODE;_USRDLL"
+ RuntimeLibrary="1"
+ PrecompiledHeaderFile=".\Unicode_Debug/spellchecker.pch"
+ AssemblerListingLocation=".\Unicode_Debug/"
+ ObjectFile=".\Unicode_Debug/"
+ ProgramDataBaseFileName=".\Unicode_Debug/"
+ BrowseInformation="1"
+ WarningLevel="3"
+ SuppressStartupBanner="true"
+ DebugInformationFormat="4"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="NDEBUG"
+ Culture="1047"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalOptions="/ALIGN:4096 /filealign:0x200 /ignore:4108 "
+ OutputFile="..\..\bin\debug unicode\Plugins\spellcheckerW.dll"
+ LinkIncremental="2"
+ SuppressStartupBanner="true"
+ GenerateDebugInformation="true"
+ ProgramDatabaseFile=".\Unicode_Debug/spellcheckerW.pdb"
+ BaseAddress="0x3EC20000"
+ ImportLibrary=".\Unicode_Debug/spellcheckerW.lib"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ SuppressStartupBanner="true"
+ OutputFile=".\Unicode_Debug/spellchecker.bsc"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Unicode Debug|x64"
+ OutputDirectory=".\Debug64"
+ IntermediateDirectory=".\Debug64"
+ ConfigurationType="2"
+ InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC60.vsprops"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ PreprocessorDefinitions="NDEBUG"
+ MkTypLibCompatible="true"
+ SuppressStartupBanner="true"
+ TargetEnvironment="3"
+ TypeLibraryName=".\Unicode_Debug/spellchecker.tlb"
+ HeaderFileName=""
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories="../../include,sdk"
+ PreprocessorDefinitions="WIN64;WIN32;W32;_DEBUG;_WINDOWS;UNICODE;_USRDLL"
+ RuntimeLibrary="1"
+ PrecompiledHeaderFile=".\Debug64/spellchecker.pch"
+ AssemblerListingLocation=".\Debug64/"
+ ObjectFile=".\Debug64/"
+ ProgramDataBaseFileName=".\Debug64/"
+ BrowseInformation="1"
+ WarningLevel="3"
+ SuppressStartupBanner="true"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="NDEBUG"
+ Culture="1047"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalOptions="/ALIGN:4096 /filealign:0x200 /ignore:4108 "
+ OutputFile="..\..\bin\debug unicode\Plugins\spellchecker64.dll"
+ LinkIncremental="2"
+ SuppressStartupBanner="true"
+ GenerateDebugInformation="true"
+ ProgramDatabaseFile=".\Debug64/spellchecker64.pdb"
+ BaseAddress="0x3EC20000"
+ ImportLibrary=".\Debug64/spellchecker64.lib"
+ TargetMachine="17"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ SuppressStartupBanner="true"
+ OutputFile=".\Debug64/spellchecker.bsc"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory=".\Debug"
+ IntermediateDirectory=".\Debug"
+ ConfigurationType="2"
+ InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC60.vsprops"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="2"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ PreprocessorDefinitions="NDEBUG"
+ MkTypLibCompatible="true"
+ SuppressStartupBanner="true"
+ TargetEnvironment="1"
+ TypeLibraryName=".\Debug/spellchecker.tlb"
+ HeaderFileName=""
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories="../../include,sdk"
+ PreprocessorDefinitions="WIN32;W32;_DEBUG;_WINDOWS;_USRDLL"
+ RuntimeLibrary="1"
+ PrecompiledHeaderFile=".\Debug/spellchecker.pch"
+ AssemblerListingLocation=".\Debug/"
+ ObjectFile=".\Debug/"
+ ProgramDataBaseFileName=".\Debug/"
+ BrowseInformation="1"
+ WarningLevel="3"
+ SuppressStartupBanner="true"
+ DebugInformationFormat="4"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="NDEBUG"
+ Culture="1047"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalOptions="/ALIGN:4096 /filealign:0x200 /ignore:4108 "
+ OutputFile="..\..\bin\debug\Plugins\spellchecker.dll"
+ LinkIncremental="2"
+ SuppressStartupBanner="true"
+ GenerateDebugInformation="true"
+ ProgramDatabaseFile=".\Debug/spellchecker.pdb"
+ BaseAddress="0x3EC20000"
+ ImportLibrary=".\Debug/spellchecker.lib"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ SuppressStartupBanner="true"
+ OutputFile=".\Debug/spellchecker.bsc"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Debug|x64"
+ OutputDirectory="$(PlatformName)\$(ConfigurationName)"
+ IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"
+ ConfigurationType="2"
+ InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC60.vsprops"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ PreprocessorDefinitions="NDEBUG"
+ MkTypLibCompatible="true"
+ SuppressStartupBanner="true"
+ TargetEnvironment="3"
+ TypeLibraryName=".\Debug/spellchecker.tlb"
+ HeaderFileName=""
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories="../../include,sdk"
+ PreprocessorDefinitions="WIN32;W32;_DEBUG;_WINDOWS;_USRDLL"
+ RuntimeLibrary="1"
+ PrecompiledHeaderFile=".\Debug/spellchecker.pch"
+ AssemblerListingLocation=".\Debug/"
+ ObjectFile=".\Debug/"
+ ProgramDataBaseFileName=".\Debug/"
+ BrowseInformation="1"
+ WarningLevel="3"
+ SuppressStartupBanner="true"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="NDEBUG"
+ Culture="1047"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalOptions="/ALIGN:4096 /filealign:0x200 /ignore:4108 "
+ OutputFile="..\..\bin\debug\Plugins\spellchecker.dll"
+ LinkIncremental="2"
+ SuppressStartupBanner="true"
+ GenerateDebugInformation="true"
+ ProgramDatabaseFile=".\Debug/spellchecker.pdb"
+ BaseAddress="0x3EC20000"
+ ImportLibrary=".\Debug/spellchecker.lib"
+ TargetMachine="17"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ SuppressStartupBanner="true"
+ OutputFile=".\Debug/spellchecker.bsc"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory=".\Release"
+ IntermediateDirectory=".\Release"
+ ConfigurationType="2"
+ InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC60.vsprops"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="2"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ PreprocessorDefinitions="NDEBUG"
+ MkTypLibCompatible="true"
+ SuppressStartupBanner="true"
+ TargetEnvironment="1"
+ TypeLibraryName=".\Release/spellchecker.tlb"
+ HeaderFileName=""
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ InlineFunctionExpansion="0"
+ AdditionalIncludeDirectories="../../include,sdk"
+ PreprocessorDefinitions="WIN32;W32;NDEBUG;_WINDOWS;_USRDLL"
+ StringPooling="true"
+ RuntimeLibrary="0"
+ EnableFunctionLevelLinking="true"
+ PrecompiledHeaderFile=".\Release/spellchecker.pch"
+ AssemblerListingLocation=".\Release/"
+ ObjectFile=".\Release/"
+ ProgramDataBaseFileName=".\Release/"
+ BrowseInformation="2"
+ BrowseInformationFile=".\Release/"
+ WarningLevel="3"
+ SuppressStartupBanner="true"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="NDEBUG"
+ Culture="1047"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalOptions="/ALIGN:4096 /filealign:0x200 /ignore:4108 "
+ OutputFile="..\..\bin\release\Plugins\spellchecker.dll"
+ LinkIncremental="1"
+ SuppressStartupBanner="true"
+ GenerateDebugInformation="true"
+ ProgramDatabaseFile=".\Release/spellchecker.pdb"
+ GenerateMapFile="true"
+ MapFileName=".\Release/spellchecker.map"
+ BaseAddress="0x3EC20000"
+ ImportLibrary=".\Release/spellchecker.lib"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ SuppressStartupBanner="true"
+ OutputFile=".\Release/spellchecker.bsc"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|x64"
+ OutputDirectory="$(PlatformName)\$(ConfigurationName)"
+ IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"
+ ConfigurationType="2"
+ InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC60.vsprops"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ PreprocessorDefinitions="NDEBUG"
+ MkTypLibCompatible="true"
+ SuppressStartupBanner="true"
+ TargetEnvironment="3"
+ TypeLibraryName=".\Release/spellchecker.tlb"
+ HeaderFileName=""
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ InlineFunctionExpansion="0"
+ AdditionalIncludeDirectories="../../include,sdk"
+ PreprocessorDefinitions="WIN32;W32;NDEBUG;_WINDOWS;_USRDLL"
+ StringPooling="true"
+ RuntimeLibrary="0"
+ EnableFunctionLevelLinking="true"
+ PrecompiledHeaderFile=".\Release/spellchecker.pch"
+ AssemblerListingLocation=".\Release/"
+ ObjectFile=".\Release/"
+ ProgramDataBaseFileName=".\Release/"
+ BrowseInformation="2"
+ BrowseInformationFile=".\Release/"
+ WarningLevel="3"
+ SuppressStartupBanner="true"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="NDEBUG"
+ Culture="1047"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalOptions="/ALIGN:4096 /filealign:0x200 /ignore:4108 "
+ OutputFile="..\..\bin\release\Plugins\spellchecker.dll"
+ LinkIncremental="1"
+ SuppressStartupBanner="true"
+ GenerateDebugInformation="true"
+ ProgramDatabaseFile=".\Release/spellchecker.pdb"
+ GenerateMapFile="true"
+ MapFileName=".\Release/spellchecker.map"
+ BaseAddress="0x3EC20000"
+ ImportLibrary=".\Release/spellchecker.lib"
+ TargetMachine="17"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ SuppressStartupBanner="true"
+ OutputFile=".\Release/spellchecker.bsc"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Header Files"
+ Filter="h;hpp;hxx;hm;inl"
+ >
+ <File
+ RelativePath="ardialog.h"
+ >
+ </File>
+ <File
+ RelativePath="autoreplace.h"
+ >
+ </File>
+ <File
+ RelativePath="commons.h"
+ >
+ </File>
+ <File
+ RelativePath="dictionary.h"
+ >
+ </File>
+ <File
+ RelativePath="m_spellchecker.h"
+ >
+ </File>
+ <File
+ RelativePath="..\utils\mir_icons.h"
+ >
+ </File>
+ <File
+ RelativePath="..\utils\mir_memory.h"
+ >
+ </File>
+ <File
+ RelativePath="..\utils\mir_options.h"
+ >
+ </File>
+ <File
+ RelativePath="options.h"
+ >
+ </File>
+ <File
+ RelativePath="resource.h"
+ >
+ </File>
+ <File
+ RelativePath="RichEdit.h"
+ >
+ </File>
+ <Filter
+ Name="hunspell Header Files"
+ >
+ <File
+ RelativePath="hunspell\affentry.hxx"
+ >
+ </File>
+ <File
+ RelativePath="hunspell\affixmgr.hxx"
+ >
+ </File>
+ <File
+ RelativePath="hunspell\atypes.hxx"
+ >
+ </File>
+ <File
+ RelativePath="hunspell\baseaffix.hxx"
+ >
+ </File>
+ <File
+ RelativePath="hunspell\config.h"
+ >
+ </File>
+ <File
+ RelativePath="hunspell\csutil.hxx"
+ >
+ </File>
+ <File
+ RelativePath="hunspell\dictmgr.hxx"
+ >
+ </File>
+ <File
+ RelativePath="hunspell\filemgr.hxx"
+ >
+ </File>
+ <File
+ RelativePath="hunspell\hashmgr.hxx"
+ >
+ </File>
+ <File
+ RelativePath="hunspell\htypes.hxx"
+ >
+ </File>
+ <File
+ RelativePath="hunspell\hunspell.h"
+ >
+ </File>
+ <File
+ RelativePath="hunspell\hunspell.hxx"
+ >
+ </File>
+ <File
+ RelativePath="hunspell\hunzip.hxx"
+ >
+ </File>
+ <File
+ RelativePath="hunspell\langnum.hxx"
+ >
+ </File>
+ <File
+ RelativePath="hunspell\replist.hxx"
+ >
+ </File>
+ <File
+ RelativePath="hunspell\suggestmgr.hxx"
+ >
+ </File>
+ <File
+ RelativePath="hunspell\w_char.hxx"
+ >
+ </File>
+ </Filter>
+ </Filter>
+ <Filter
+ Name="Resource Files"
+ Filter="ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
+ >
+ <File
+ RelativePath="res\no_spellcheck.ico"
+ >
+ </File>
+ <File
+ RelativePath="resource.rc"
+ >
+ <FileConfiguration
+ Name="Unicode Release|Win32"
+ >
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Unicode Release|x64"
+ >
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Unicode Debug|Win32"
+ >
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Unicode Debug|x64"
+ >
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|x64"
+ >
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|x64"
+ >
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="res\spellcheck.ico"
+ >
+ </File>
+ <File
+ RelativePath="res\unknown.ico"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Source Files"
+ Filter="cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+ >
+ <File
+ RelativePath="ardialog.cpp"
+ >
+ <FileConfiguration
+ Name="Unicode Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Unicode Release|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Unicode Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Unicode Debug|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="autoreplace.cpp"
+ >
+ <FileConfiguration
+ Name="Unicode Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Unicode Release|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Unicode Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Unicode Debug|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="dictionary.cpp"
+ >
+ <FileConfiguration
+ Name="Unicode Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Unicode Release|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Unicode Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Unicode Debug|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="..\utils\mir_icons.cpp"
+ >
+ <FileConfiguration
+ Name="Unicode Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Unicode Release|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Unicode Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Unicode Debug|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="..\utils\mir_options.cpp"
+ >
+ <FileConfiguration
+ Name="Unicode Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Unicode Release|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Unicode Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Unicode Debug|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="options.cpp"
+ >
+ <FileConfiguration
+ Name="Unicode Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Unicode Release|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Unicode Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Unicode Debug|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="RichEdit.cpp"
+ >
+ <FileConfiguration
+ Name="Unicode Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Unicode Release|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Unicode Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Unicode Debug|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="spellchecker.cpp"
+ >
+ <FileConfiguration
+ Name="Unicode Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Unicode Release|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Unicode Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Unicode Debug|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ </File>
+ <Filter
+ Name="hunspell Source Files"
+ >
+ <File
+ RelativePath="hunspell\affentry.cxx"
+ >
+ <FileConfiguration
+ Name="Unicode Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Unicode Release|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Unicode Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Unicode Debug|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="hunspell\affixmgr.cxx"
+ >
+ <FileConfiguration
+ Name="Unicode Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Unicode Release|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Unicode Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Unicode Debug|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="hunspell\csutil.cxx"
+ >
+ <FileConfiguration
+ Name="Unicode Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Unicode Release|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Unicode Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Unicode Debug|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="hunspell\dictmgr.cxx"
+ >
+ <FileConfiguration
+ Name="Unicode Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Unicode Release|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Unicode Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Unicode Debug|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="hunspell\filemgr.cxx"
+ >
+ <FileConfiguration
+ Name="Unicode Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Unicode Release|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Unicode Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Unicode Debug|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="hunspell\hashmgr.cxx"
+ >
+ <FileConfiguration
+ Name="Unicode Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Unicode Release|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Unicode Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Unicode Debug|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="hunspell\hunspell.cxx"
+ >
+ <FileConfiguration
+ Name="Unicode Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Unicode Release|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Unicode Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Unicode Debug|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="hunspell\hunzip.cxx"
+ >
+ <FileConfiguration
+ Name="Unicode Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Unicode Release|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Unicode Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Unicode Debug|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="hunspell\phonet.cxx"
+ >
+ <FileConfiguration
+ Name="Unicode Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Unicode Release|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Unicode Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Unicode Debug|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="hunspell\replist.cxx"
+ >
+ <FileConfiguration
+ Name="Unicode Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Unicode Release|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Unicode Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Unicode Debug|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="hunspell\suggestmgr.cxx"
+ >
+ <FileConfiguration
+ Name="Unicode Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Unicode Release|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Unicode Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Unicode Debug|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="hunspell\utf_info.cxx"
+ >
+ <FileConfiguration
+ Name="Unicode Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Unicode Release|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Unicode Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Unicode Debug|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ </File>
+ </Filter>
+ </Filter>
+ <Filter
+ Name="Docs"
+ >
+ <File
+ RelativePath="Docs\langpack_spellchecker.txt"
+ >
+ </File>
+ <File
+ RelativePath="Docs\spellchecker_changelog.txt"
+ >
+ </File>
+ <File
+ RelativePath="Docs\spellchecker_readme.txt"
+ >
+ </File>
+ <File
+ RelativePath="Docs\spellchecker_version.txt"
+ >
+ </File>
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject> \ No newline at end of file
diff --git a/Plugins/spellchecker/spellchecker.vcxproj b/Plugins/spellchecker/spellchecker.vcxproj
new file mode 100644
index 0000000..cb0bdc3
--- /dev/null
+++ b/Plugins/spellchecker/spellchecker.vcxproj
@@ -0,0 +1,883 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Unicode Debug|Win32">
+ <Configuration>Unicode Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Unicode Debug|x64">
+ <Configuration>Unicode Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Unicode Release|Win32">
+ <Configuration>Unicode Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Unicode Release|x64">
+ <Configuration>Unicode Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{5DB86086-10E9-42E7-AC90-503D2678C2A2}</ProjectGuid>
+ <RootNamespace>spellchecker</RootNamespace>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <UseOfMfc>false</UseOfMfc>
+ <CharacterSet>MultiByte</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <UseOfMfc>false</UseOfMfc>
+ <CharacterSet>MultiByte</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Unicode Debug|Win32'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <UseOfMfc>false</UseOfMfc>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Unicode Release|Win32'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <UseOfMfc>false</UseOfMfc>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <UseOfMfc>false</UseOfMfc>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <UseOfMfc>false</UseOfMfc>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Unicode Debug|x64'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <UseOfMfc>false</UseOfMfc>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Unicode Release|x64'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <UseOfMfc>false</UseOfMfc>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ <Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC60.props" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ <Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC60.props" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Unicode Debug|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ <Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC60.props" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Unicode Release|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ <Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC60.props" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ <Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC60.props" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ <Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC60.props" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Unicode Debug|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ <Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC60.props" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Unicode Release|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ <Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC60.props" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup>
+ <_ProjectFileVersion>10.0.30319.1</_ProjectFileVersion>
+ <OutDir Condition="'$(Configuration)|$(Platform)'=='Unicode Release|Win32'">bin\$(Platform)\$(Configuration)\</OutDir>
+ <IntDir Condition="'$(Configuration)|$(Platform)'=='Unicode Release|Win32'">obj\$(Platform)\$(Configuration)\</IntDir>
+ <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Unicode Release|Win32'">false</LinkIncremental>
+ <OutDir Condition="'$(Configuration)|$(Platform)'=='Unicode Release|x64'">bin\$(Platform)\$(Configuration)\</OutDir>
+ <IntDir Condition="'$(Configuration)|$(Platform)'=='Unicode Release|x64'">obj\$(Platform)\$(Configuration)\</IntDir>
+ <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Unicode Release|x64'">false</LinkIncremental>
+ <OutDir Condition="'$(Configuration)|$(Platform)'=='Unicode Debug|Win32'">bin\$(Platform)\$(Configuration)\</OutDir>
+ <IntDir Condition="'$(Configuration)|$(Platform)'=='Unicode Debug|Win32'">obj\$(Platform)\$(Configuration)\</IntDir>
+ <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Unicode Debug|Win32'">true</LinkIncremental>
+ <OutDir Condition="'$(Configuration)|$(Platform)'=='Unicode Debug|x64'">bin\$(Platform)\$(Configuration)\</OutDir>
+ <IntDir Condition="'$(Configuration)|$(Platform)'=='Unicode Debug|x64'">obj\$(Platform)\$(Configuration)\</IntDir>
+ <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Unicode Debug|x64'">true</LinkIncremental>
+ <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">bin\$(Platform)\$(Configuration)\</OutDir>
+ <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">obj\$(Platform)\$(Configuration)\</IntDir>
+ <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</LinkIncremental>
+ <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">bin\$(Platform)\$(Configuration)\</OutDir>
+ <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">obj\$(Platform)\$(Configuration)\</IntDir>
+ <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</LinkIncremental>
+ <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">bin\$(Platform)\$(Configuration)\</OutDir>
+ <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">obj\$(Platform)\$(Configuration)\</IntDir>
+ <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</LinkIncremental>
+ <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">bin\$(Platform)\$(Configuration)\</OutDir>
+ <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">obj\$(Platform)\$(Configuration)\</IntDir>
+ <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</LinkIncremental>
+ <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">AllRules.ruleset</CodeAnalysisRuleSet>
+ <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" />
+ <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" />
+ <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">AllRules.ruleset</CodeAnalysisRuleSet>
+ <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" />
+ <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" />
+ <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">AllRules.ruleset</CodeAnalysisRuleSet>
+ <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" />
+ <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" />
+ <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release|x64'">AllRules.ruleset</CodeAnalysisRuleSet>
+ <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release|x64'" />
+ <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|x64'" />
+ <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Unicode Debug|Win32'">AllRules.ruleset</CodeAnalysisRuleSet>
+ <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Unicode Debug|Win32'" />
+ <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Unicode Debug|Win32'" />
+ <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Unicode Debug|x64'">AllRules.ruleset</CodeAnalysisRuleSet>
+ <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Unicode Debug|x64'" />
+ <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Unicode Debug|x64'" />
+ <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Unicode Release|Win32'">AllRules.ruleset</CodeAnalysisRuleSet>
+ <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Unicode Release|Win32'" />
+ <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Unicode Release|Win32'" />
+ <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Unicode Release|x64'">AllRules.ruleset</CodeAnalysisRuleSet>
+ <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Unicode Release|x64'" />
+ <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Unicode Release|x64'" />
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Unicode Release|Win32'">
+ <Midl>
+ <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MkTypLibCompatible>true</MkTypLibCompatible>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <TargetEnvironment>Win32</TargetEnvironment>
+ <TypeLibraryName>.\Unicode_Release/spellchecker.tlb</TypeLibraryName>
+ <HeaderFileName>
+ </HeaderFileName>
+ </Midl>
+ <ClCompile>
+ <Optimization>MaxSpeed</Optimization>
+ <InlineFunctionExpansion>Default</InlineFunctionExpansion>
+ <AdditionalIncludeDirectories>../../include;sdk;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>HUNSPELL_STATIC;_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_WARNINGS;WIN32;W32;NDEBUG;_WINDOWS;UNICODE;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <StringPooling>true</StringPooling>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <BrowseInformation>true</BrowseInformation>
+ <BrowseInformationFile>$(IntDir)</BrowseInformationFile>
+ <WarningLevel>Level3</WarningLevel>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ResourceCompile>
+ <Link>
+ <AdditionalOptions>/ALIGN:4096 /filealign:0x200 /ignore:4108 %(AdditionalOptions)</AdditionalOptions>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <GenerateMapFile>true</GenerateMapFile>
+ <MapFileName>.\Unicode_Release/spellcheckerW.map</MapFileName>
+ <BaseAddress>0x3EC20000</BaseAddress>
+ <TargetMachine>MachineX86</TargetMachine>
+ <OutputFile>$(OutDir)$(TargetName)W$(TargetExt)</OutputFile>
+ <ProgramDatabaseFile>$(TargetDir)$(TargetName)W.pdb</ProgramDatabaseFile>
+ </Link>
+ <Bscmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <OutputFile>.\Unicode_Release/spellchecker.bsc</OutputFile>
+ </Bscmake>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Unicode Release|x64'">
+ <Midl>
+ <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MkTypLibCompatible>true</MkTypLibCompatible>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <TargetEnvironment>X64</TargetEnvironment>
+ <TypeLibraryName>.\Unicode_Release/spellchecker.tlb</TypeLibraryName>
+ <HeaderFileName>
+ </HeaderFileName>
+ </Midl>
+ <ClCompile>
+ <Optimization>MaxSpeed</Optimization>
+ <InlineFunctionExpansion>Default</InlineFunctionExpansion>
+ <AdditionalIncludeDirectories>../../include;sdk;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>HUNSPELL_STATIC;_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_WARNINGS;WIN64;WIN32;W32;NDEBUG;_WINDOWS;UNICODE;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <StringPooling>true</StringPooling>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <BrowseInformation>true</BrowseInformation>
+ <BrowseInformationFile>$(IntDir)</BrowseInformationFile>
+ <WarningLevel>Level3</WarningLevel>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ResourceCompile>
+ <Link>
+ <AdditionalOptions>/ALIGN:4096 /filealign:0x200 /ignore:4108 %(AdditionalOptions)</AdditionalOptions>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <GenerateMapFile>true</GenerateMapFile>
+ <MapFileName>.\Release64/spellchecker64.map</MapFileName>
+ <BaseAddress>0x3EC20000</BaseAddress>
+ <TargetMachine>MachineX64</TargetMachine>
+ <OutputFile>$(OutDir)$(TargetName)64$(TargetExt)</OutputFile>
+ <ProgramDatabaseFile>$(TargetDir)$(TargetName)64.pdb</ProgramDatabaseFile>
+ </Link>
+ <Bscmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <OutputFile>.\Release64/spellchecker.bsc</OutputFile>
+ </Bscmake>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Unicode Debug|Win32'">
+ <Midl>
+ <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MkTypLibCompatible>true</MkTypLibCompatible>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <TargetEnvironment>Win32</TargetEnvironment>
+ <TypeLibraryName>.\Unicode_Debug/spellchecker.tlb</TypeLibraryName>
+ <HeaderFileName>
+ </HeaderFileName>
+ </Midl>
+ <ClCompile>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>../../include;sdk;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>HUNSPELL_STATIC;_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_WARNINGS;WIN32;W32;_DEBUG;_WINDOWS;UNICODE;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+ <BrowseInformation>true</BrowseInformation>
+ <WarningLevel>Level3</WarningLevel>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <DebugInformationFormat>EditAndContinue</DebugInformationFormat>
+ <BrowseInformationFile>$(IntDir)</BrowseInformationFile>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ResourceCompile>
+ <Link>
+ <AdditionalOptions>/ALIGN:4096 /filealign:0x200 /ignore:4108 %(AdditionalOptions)</AdditionalOptions>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <BaseAddress>0x3EC20000</BaseAddress>
+ <TargetMachine>MachineX86</TargetMachine>
+ <OutputFile>$(OutDir)$(TargetName)W$(TargetExt)</OutputFile>
+ <ProgramDatabaseFile>$(TargetDir)$(TargetName)W.pdb</ProgramDatabaseFile>
+ </Link>
+ <Bscmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <OutputFile>.\Unicode_Debug/spellchecker.bsc</OutputFile>
+ </Bscmake>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Unicode Debug|x64'">
+ <Midl>
+ <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MkTypLibCompatible>true</MkTypLibCompatible>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <TargetEnvironment>X64</TargetEnvironment>
+ <TypeLibraryName>.\Unicode_Debug/spellchecker.tlb</TypeLibraryName>
+ <HeaderFileName>
+ </HeaderFileName>
+ </Midl>
+ <ClCompile>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>../../include;sdk;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>HUNSPELL_STATIC;_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_WARNINGS;WIN64;WIN32;W32;_DEBUG;_WINDOWS;UNICODE;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+ <BrowseInformation>true</BrowseInformation>
+ <WarningLevel>Level3</WarningLevel>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <BrowseInformationFile>$(IntDir)</BrowseInformationFile>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ResourceCompile>
+ <Link>
+ <AdditionalOptions>/ALIGN:4096 /filealign:0x200 /ignore:4108 %(AdditionalOptions)</AdditionalOptions>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <BaseAddress>0x3EC20000</BaseAddress>
+ <TargetMachine>MachineX64</TargetMachine>
+ <OutputFile>$(OutDir)$(TargetName)64$(TargetExt)</OutputFile>
+ <ProgramDatabaseFile>$(TargetDir)$(TargetName)64.pdb</ProgramDatabaseFile>
+ </Link>
+ <Bscmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <OutputFile>.\Debug64/spellchecker.bsc</OutputFile>
+ </Bscmake>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Midl>
+ <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MkTypLibCompatible>true</MkTypLibCompatible>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <TargetEnvironment>Win32</TargetEnvironment>
+ <TypeLibraryName>.\Debug/spellchecker.tlb</TypeLibraryName>
+ <HeaderFileName>
+ </HeaderFileName>
+ </Midl>
+ <ClCompile>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>../../include;sdk;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>HUNSPELL_STATIC;_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_WARNINGS;WIN32;W32;_DEBUG;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+ <BrowseInformation>true</BrowseInformation>
+ <WarningLevel>Level3</WarningLevel>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <DebugInformationFormat>EditAndContinue</DebugInformationFormat>
+ <BrowseInformationFile>$(IntDir)</BrowseInformationFile>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ResourceCompile>
+ <Link>
+ <AdditionalOptions>/ALIGN:4096 /filealign:0x200 /ignore:4108 %(AdditionalOptions)</AdditionalOptions>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <BaseAddress>0x3EC20000</BaseAddress>
+ <TargetMachine>MachineX86</TargetMachine>
+ </Link>
+ <Bscmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <OutputFile>.\Debug/spellchecker.bsc</OutputFile>
+ </Bscmake>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <Midl>
+ <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MkTypLibCompatible>true</MkTypLibCompatible>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <TargetEnvironment>X64</TargetEnvironment>
+ <TypeLibraryName>.\Debug/spellchecker.tlb</TypeLibraryName>
+ <HeaderFileName>
+ </HeaderFileName>
+ </Midl>
+ <ClCompile>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>../../include;sdk;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>HUNSPELL_STATIC;_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_WARNINGS;WIN32;W32;_DEBUG;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+ <BrowseInformation>true</BrowseInformation>
+ <WarningLevel>Level3</WarningLevel>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <BrowseInformationFile>$(IntDir)</BrowseInformationFile>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ResourceCompile>
+ <Link>
+ <AdditionalOptions>/ALIGN:4096 /filealign:0x200 /ignore:4108 %(AdditionalOptions)</AdditionalOptions>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <BaseAddress>0x3EC20000</BaseAddress>
+ <TargetMachine>MachineX64</TargetMachine>
+ <OutputFile>$(OutDir)$(TargetName)64$(TargetExt)</OutputFile>
+ <ProgramDatabaseFile>$(TargetDir)$(TargetName)64.pdb</ProgramDatabaseFile>
+ </Link>
+ <Bscmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <OutputFile>.\Debug/spellchecker.bsc</OutputFile>
+ </Bscmake>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Midl>
+ <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MkTypLibCompatible>true</MkTypLibCompatible>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <TargetEnvironment>Win32</TargetEnvironment>
+ <TypeLibraryName>.\Release/spellchecker.tlb</TypeLibraryName>
+ <HeaderFileName>
+ </HeaderFileName>
+ </Midl>
+ <ClCompile>
+ <Optimization>MaxSpeed</Optimization>
+ <InlineFunctionExpansion>Default</InlineFunctionExpansion>
+ <AdditionalIncludeDirectories>../../include;sdk;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>HUNSPELL_STATIC;_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_WARNINGS;WIN32;W32;NDEBUG;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <StringPooling>true</StringPooling>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <BrowseInformation>true</BrowseInformation>
+ <BrowseInformationFile>$(IntDir)</BrowseInformationFile>
+ <WarningLevel>Level3</WarningLevel>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ResourceCompile>
+ <Link>
+ <AdditionalOptions>/ALIGN:4096 /filealign:0x200 /ignore:4108 %(AdditionalOptions)</AdditionalOptions>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <GenerateMapFile>true</GenerateMapFile>
+ <MapFileName>.\Release/spellchecker.map</MapFileName>
+ <BaseAddress>0x3EC20000</BaseAddress>
+ <TargetMachine>MachineX86</TargetMachine>
+ </Link>
+ <Bscmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <OutputFile>.\Release/spellchecker.bsc</OutputFile>
+ </Bscmake>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <Midl>
+ <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MkTypLibCompatible>true</MkTypLibCompatible>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <TargetEnvironment>X64</TargetEnvironment>
+ <TypeLibraryName>.\Release/spellchecker.tlb</TypeLibraryName>
+ <HeaderFileName>
+ </HeaderFileName>
+ </Midl>
+ <ClCompile>
+ <Optimization>MaxSpeed</Optimization>
+ <InlineFunctionExpansion>Default</InlineFunctionExpansion>
+ <AdditionalIncludeDirectories>../../include;sdk;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>HUNSPELL_STATIC;_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_WARNINGS;WIN32;W32;NDEBUG;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <StringPooling>true</StringPooling>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <BrowseInformation>true</BrowseInformation>
+ <BrowseInformationFile>$(IntDir)</BrowseInformationFile>
+ <WarningLevel>Level3</WarningLevel>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ResourceCompile>
+ <Link>
+ <AdditionalOptions>/ALIGN:4096 /filealign:0x200 /ignore:4108 %(AdditionalOptions)</AdditionalOptions>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <GenerateMapFile>true</GenerateMapFile>
+ <MapFileName>.\Release/spellchecker.map</MapFileName>
+ <BaseAddress>0x3EC20000</BaseAddress>
+ <TargetMachine>MachineX64</TargetMachine>
+ <OutputFile>$(OutDir)$(TargetName)64$(TargetExt)</OutputFile>
+ <ProgramDatabaseFile>$(TargetDir)$(TargetName)64.pdb</ProgramDatabaseFile>
+ </Link>
+ <Bscmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <OutputFile>.\Release/spellchecker.bsc</OutputFile>
+ </Bscmake>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClInclude Include="ardialog.h" />
+ <ClInclude Include="autoreplace.h" />
+ <ClInclude Include="commons.h" />
+ <ClInclude Include="dictionary.h" />
+ <ClInclude Include="m_spellchecker.h" />
+ <ClInclude Include="..\utils\mir_icons.h" />
+ <ClInclude Include="..\utils\mir_memory.h" />
+ <ClInclude Include="..\utils\mir_options.h" />
+ <ClInclude Include="options.h" />
+ <ClInclude Include="resource.h" />
+ <ClInclude Include="RichEdit.h" />
+ <ClInclude Include="hunspell\affentry.hxx" />
+ <ClInclude Include="hunspell\affixmgr.hxx" />
+ <ClInclude Include="hunspell\atypes.hxx" />
+ <ClInclude Include="hunspell\baseaffix.hxx" />
+ <ClInclude Include="hunspell\config.h" />
+ <ClInclude Include="hunspell\csutil.hxx" />
+ <ClInclude Include="hunspell\dictmgr.hxx" />
+ <ClInclude Include="hunspell\filemgr.hxx" />
+ <ClInclude Include="hunspell\hashmgr.hxx" />
+ <ClInclude Include="hunspell\htypes.hxx" />
+ <ClInclude Include="hunspell\hunspell.h" />
+ <ClInclude Include="hunspell\hunspell.hxx" />
+ <ClInclude Include="hunspell\hunzip.hxx" />
+ <ClInclude Include="hunspell\langnum.hxx" />
+ <ClInclude Include="hunspell\replist.hxx" />
+ <ClInclude Include="hunspell\suggestmgr.hxx" />
+ <ClInclude Include="hunspell\w_char.hxx" />
+ </ItemGroup>
+ <ItemGroup>
+ <None Include="res\no_spellcheck.ico" />
+ <None Include="res\spellcheck.ico" />
+ <None Include="res\unknown.ico" />
+ <None Include="Docs\langpack_spellchecker.txt" />
+ <None Include="Docs\spellchecker_changelog.txt" />
+ <None Include="Docs\spellchecker_readme.txt" />
+ <None Include="Docs\spellchecker_version.txt" />
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="resource.rc">
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Unicode Debug|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Unicode Debug|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Unicode Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Unicode Release|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ResourceCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="ardialog.cpp">
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Unicode Debug|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Unicode Debug|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Unicode Debug|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Unicode Debug|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Unicode Release|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Unicode Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Unicode Release|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Unicode Release|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ClCompile>
+ <ClCompile Include="autoreplace.cpp">
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Unicode Debug|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Unicode Debug|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Unicode Debug|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Unicode Debug|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Unicode Release|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Unicode Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Unicode Release|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Unicode Release|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ClCompile>
+ <ClCompile Include="dictionary.cpp">
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Unicode Debug|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Unicode Debug|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Unicode Debug|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Unicode Debug|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Unicode Release|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Unicode Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Unicode Release|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Unicode Release|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ClCompile>
+ <ClCompile Include="..\utils\mir_icons.cpp">
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Unicode Debug|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Unicode Debug|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Unicode Debug|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Unicode Debug|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Unicode Release|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Unicode Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Unicode Release|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Unicode Release|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ClCompile>
+ <ClCompile Include="..\utils\mir_options.cpp">
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Unicode Debug|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Unicode Debug|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Unicode Debug|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Unicode Debug|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Unicode Release|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Unicode Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Unicode Release|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Unicode Release|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ClCompile>
+ <ClCompile Include="options.cpp">
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Unicode Debug|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Unicode Debug|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Unicode Debug|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Unicode Debug|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Unicode Release|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Unicode Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Unicode Release|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Unicode Release|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ClCompile>
+ <ClCompile Include="RichEdit.cpp">
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Unicode Debug|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Unicode Debug|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Unicode Debug|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Unicode Debug|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Unicode Release|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Unicode Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Unicode Release|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Unicode Release|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ClCompile>
+ <ClCompile Include="spellchecker.cpp">
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Unicode Debug|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Unicode Debug|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Unicode Debug|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Unicode Debug|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Unicode Release|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Unicode Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Unicode Release|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Unicode Release|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ClCompile>
+ <ClCompile Include="hunspell\affentry.cxx">
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Unicode Debug|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Unicode Debug|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Unicode Debug|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Unicode Debug|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Unicode Release|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Unicode Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Unicode Release|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Unicode Release|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ClCompile>
+ <ClCompile Include="hunspell\affixmgr.cxx">
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Unicode Debug|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Unicode Debug|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Unicode Debug|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Unicode Debug|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Unicode Release|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Unicode Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Unicode Release|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Unicode Release|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ClCompile>
+ <ClCompile Include="hunspell\csutil.cxx">
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Unicode Debug|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Unicode Debug|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Unicode Debug|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Unicode Debug|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Unicode Release|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Unicode Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Unicode Release|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Unicode Release|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ClCompile>
+ <ClCompile Include="hunspell\dictmgr.cxx">
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Unicode Debug|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Unicode Debug|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Unicode Debug|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Unicode Debug|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Unicode Release|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Unicode Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Unicode Release|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Unicode Release|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ClCompile>
+ <ClCompile Include="hunspell\filemgr.cxx">
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Unicode Debug|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Unicode Debug|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Unicode Debug|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Unicode Debug|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Unicode Release|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Unicode Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Unicode Release|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Unicode Release|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ClCompile>
+ <ClCompile Include="hunspell\hashmgr.cxx">
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Unicode Debug|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Unicode Debug|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Unicode Debug|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Unicode Debug|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Unicode Release|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Unicode Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Unicode Release|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Unicode Release|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ClCompile>
+ <ClCompile Include="hunspell\hunspell.cxx">
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Unicode Debug|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Unicode Debug|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Unicode Debug|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Unicode Debug|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Unicode Release|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Unicode Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Unicode Release|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Unicode Release|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ClCompile>
+ <ClCompile Include="hunspell\hunzip.cxx">
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Unicode Debug|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Unicode Debug|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Unicode Debug|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Unicode Debug|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Unicode Release|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Unicode Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Unicode Release|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Unicode Release|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ClCompile>
+ <ClCompile Include="hunspell\phonet.cxx">
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Unicode Debug|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Unicode Debug|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Unicode Debug|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Unicode Debug|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Unicode Release|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Unicode Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Unicode Release|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Unicode Release|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ClCompile>
+ <ClCompile Include="hunspell\replist.cxx">
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Unicode Debug|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Unicode Debug|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Unicode Debug|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Unicode Debug|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Unicode Release|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Unicode Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Unicode Release|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Unicode Release|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ClCompile>
+ <ClCompile Include="hunspell\suggestmgr.cxx">
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Unicode Debug|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Unicode Debug|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Unicode Debug|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Unicode Debug|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Unicode Release|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Unicode Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Unicode Release|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Unicode Release|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ClCompile>
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project> \ No newline at end of file
diff --git a/Plugins/spellchecker/spellchecker.vcxproj.filters b/Plugins/spellchecker/spellchecker.vcxproj.filters
new file mode 100644
index 0000000..06fc8df
--- /dev/null
+++ b/Plugins/spellchecker/spellchecker.vcxproj.filters
@@ -0,0 +1,199 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{1e567d1e-0703-4ad7-8be9-0bf36e6d903e}</UniqueIdentifier>
+ <Extensions>h;hpp;hxx;hm;inl</Extensions>
+ </Filter>
+ <Filter Include="Header Files\hunspell Header Files">
+ <UniqueIdentifier>{6ac01ae1-9db3-41f9-abaa-4e05175e9502}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Resource Files">
+ <UniqueIdentifier>{7168cb7a-2d93-454f-a2e8-d3697486751a}</UniqueIdentifier>
+ <Extensions>ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe</Extensions>
+ </Filter>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{f6737d81-ea60-4b9d-b7d7-76da94729a18}</UniqueIdentifier>
+ <Extensions>cpp;c;cxx;rc;def;r;odl;idl;hpj;bat</Extensions>
+ </Filter>
+ <Filter Include="Source Files\hunspell Source Files">
+ <UniqueIdentifier>{a28a5de0-489b-4a0c-8e4b-87848ecfc338}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Docs">
+ <UniqueIdentifier>{437a04fa-5422-4a59-bf81-a7a42e195870}</UniqueIdentifier>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="ardialog.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="autoreplace.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="commons.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="dictionary.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="m_spellchecker.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\utils\mir_icons.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\utils\mir_memory.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\utils\mir_options.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="options.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="resource.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="RichEdit.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="hunspell\affentry.hxx">
+ <Filter>Header Files\hunspell Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="hunspell\affixmgr.hxx">
+ <Filter>Header Files\hunspell Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="hunspell\atypes.hxx">
+ <Filter>Header Files\hunspell Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="hunspell\baseaffix.hxx">
+ <Filter>Header Files\hunspell Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="hunspell\config.h">
+ <Filter>Header Files\hunspell Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="hunspell\csutil.hxx">
+ <Filter>Header Files\hunspell Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="hunspell\dictmgr.hxx">
+ <Filter>Header Files\hunspell Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="hunspell\filemgr.hxx">
+ <Filter>Header Files\hunspell Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="hunspell\hashmgr.hxx">
+ <Filter>Header Files\hunspell Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="hunspell\htypes.hxx">
+ <Filter>Header Files\hunspell Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="hunspell\hunspell.h">
+ <Filter>Header Files\hunspell Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="hunspell\hunspell.hxx">
+ <Filter>Header Files\hunspell Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="hunspell\hunzip.hxx">
+ <Filter>Header Files\hunspell Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="hunspell\langnum.hxx">
+ <Filter>Header Files\hunspell Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="hunspell\replist.hxx">
+ <Filter>Header Files\hunspell Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="hunspell\suggestmgr.hxx">
+ <Filter>Header Files\hunspell Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="hunspell\w_char.hxx">
+ <Filter>Header Files\hunspell Header Files</Filter>
+ </ClInclude>
+ </ItemGroup>
+ <ItemGroup>
+ <None Include="res\no_spellcheck.ico">
+ <Filter>Resource Files</Filter>
+ </None>
+ <None Include="res\spellcheck.ico">
+ <Filter>Resource Files</Filter>
+ </None>
+ <None Include="res\unknown.ico">
+ <Filter>Resource Files</Filter>
+ </None>
+ <None Include="Docs\langpack_spellchecker.txt">
+ <Filter>Docs</Filter>
+ </None>
+ <None Include="Docs\spellchecker_changelog.txt">
+ <Filter>Docs</Filter>
+ </None>
+ <None Include="Docs\spellchecker_readme.txt">
+ <Filter>Docs</Filter>
+ </None>
+ <None Include="Docs\spellchecker_version.txt">
+ <Filter>Docs</Filter>
+ </None>
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="resource.rc">
+ <Filter>Resource Files</Filter>
+ </ResourceCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="ardialog.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="autoreplace.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="dictionary.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\utils\mir_icons.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\utils\mir_options.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="options.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="RichEdit.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="spellchecker.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="hunspell\affentry.cxx">
+ <Filter>Source Files\hunspell Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="hunspell\affixmgr.cxx">
+ <Filter>Source Files\hunspell Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="hunspell\csutil.cxx">
+ <Filter>Source Files\hunspell Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="hunspell\dictmgr.cxx">
+ <Filter>Source Files\hunspell Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="hunspell\filemgr.cxx">
+ <Filter>Source Files\hunspell Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="hunspell\hashmgr.cxx">
+ <Filter>Source Files\hunspell Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="hunspell\hunspell.cxx">
+ <Filter>Source Files\hunspell Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="hunspell\hunzip.cxx">
+ <Filter>Source Files\hunspell Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="hunspell\phonet.cxx">
+ <Filter>Source Files\hunspell Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="hunspell\replist.cxx">
+ <Filter>Source Files\hunspell Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="hunspell\suggestmgr.cxx">
+ <Filter>Source Files\hunspell Source Files</Filter>
+ </ClCompile>
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/Plugins/spellchecker/srmm.spellchecker.patch b/Plugins/spellchecker/srmm.spellchecker.patch
new file mode 100644
index 0000000..be603f3
--- /dev/null
+++ b/Plugins/spellchecker/srmm.spellchecker.patch
@@ -0,0 +1,266 @@
+Index: msgdialog.c
+===================================================================
+--- msgdialog.c (revision 6386)
++++ msgdialog.c (working copy)
+@@ -39,6 +39,7 @@
+
+ extern HCURSOR hCurSplitNS, hCurSplitWE, hCurHyperlinkHand;
+ extern HANDLE hHookWinEvt;
++extern HANDLE hHookWinPopup;
+ extern struct CREOleCallback reOleCallback;
+ extern HINSTANCE g_hInst;
+
+@@ -318,22 +319,6 @@
+ case WM_CHAR:
+ if (GetWindowLong(hwnd, GWL_STYLE) & ES_READONLY)
+ break;
+- //for saved msg queue the keyup/keydowns generate wm_chars themselves
+- if (wParam == '\n' || wParam == '\r') {
+- if (((GetKeyState(VK_CONTROL) & 0x8000) != 0) ^ (0 != DBGetContactSettingByte(NULL, SRMMMOD, SRMSGSET_SENDONENTER, SRMSGDEFSET_SENDONENTER))) {
+- PostMessage(GetParent(hwnd), WM_COMMAND, IDOK, 0);
+- return 0;
+- }
+- if (DBGetContactSettingByte(NULL, SRMMMOD, SRMSGSET_SENDONDBLENTER, SRMSGDEFSET_SENDONDBLENTER)) {
+- if (dat->lastEnterTime + ENTERCLICKTIME < GetTickCount())
+- dat->lastEnterTime = GetTickCount();
+- else {
+- SendMessage(hwnd, WM_CHAR, '\b', 0);
+- PostMessage(GetParent(hwnd), WM_COMMAND, IDOK, 0);
+- return 0;
+- } }
+- }
+- else dat->lastEnterTime = 0;
+
+ if (wParam == 1 && GetKeyState(VK_CONTROL) & 0x8000) { //ctrl-a
+ SendMessage(hwnd, EM_SETSEL, 0, -1);
+@@ -403,6 +388,25 @@
+ SaveKeyboardMessage(dat, msg, wParam, lParam);
+ return 0;
+ }
++
++ if (wParam == VK_RETURN) {
++ if (((GetKeyState(VK_CONTROL) & 0x8000) != 0) ^ (0 != DBGetContactSettingByte(NULL, SRMMMOD, SRMSGSET_SENDONENTER, SRMSGDEFSET_SENDONENTER))) {
++ PostMessage(GetParent(hwnd), WM_COMMAND, IDOK, 0);
++ return 0;
++ }
++ if (DBGetContactSettingByte(NULL, SRMMMOD, SRMSGSET_SENDONDBLENTER, SRMSGDEFSET_SENDONDBLENTER)) {
++ if (dat->lastEnterTime + ENTERCLICKTIME < GetTickCount())
++ dat->lastEnterTime = GetTickCount();
++ else {
++ SendMessage(hwnd, WM_KEYDOWN, VK_BACK, 0);
++ SendMessage(hwnd, WM_KEYUP, VK_BACK, 0);
++ PostMessage(GetParent(hwnd), WM_COMMAND, IDOK, 0);
++ return 0;
++ }
++ }
++ }
++ else
++ dat->lastEnterTime = 0;
+
+ if (wParam == VK_UP && (GetKeyState(VK_CONTROL) & 0x8000) && DBGetContactSettingByte(NULL, SRMMMOD, SRMSGSET_CTRLSUPPORT, SRMSGDEFSET_CTRLSUPPORT) && !DBGetContactSettingByte(NULL, SRMMMOD, SRMSGSET_AUTOCLOSE, SRMSGDEFSET_AUTOCLOSE)) {
+ if (pdat->cmdList) {
+@@ -446,9 +450,7 @@
+ EnableWindow(GetDlgItem(GetParent(hwnd), IDOK), GetWindowTextLength(GetDlgItem(GetParent(hwnd), IDC_MESSAGE)) != 0);
+ UpdateReadChars(GetParent(hwnd), pdat->hwndStatus);
+ }
+- if (wParam == VK_RETURN)
+- break;
+- //fall through
++ break;
+ case WM_LBUTTONDOWN:
+ case WM_RBUTTONDOWN:
+ case WM_MBUTTONDOWN:
+@@ -473,6 +475,45 @@
+ free(dat->keyboardMsgQueue);
+ free(dat);
+ return 0;
++ case WM_CONTEXTMENU:
++ {
++ MessageWindowPopupData mwpd;
++ mwpd.cbSize = sizeof(mwpd);
++ mwpd.uType = MSG_WINDOWPOPUP_SHOWING;
++ mwpd.uFlags = MSG_WINDOWPOPUP_INPUT;
++ mwpd.hContact = pdat->hContact;
++ mwpd.hwnd = hwnd;
++ mwpd.hMenu = CreatePopupMenu();
++ mwpd.selection = 0;
++
++ if (lParam == 0xFFFFFFFF) {
++ CHARRANGE sel;
++ SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM) &sel);
++ SendMessage(hwnd, EM_POSFROMCHAR, (WPARAM)&mwpd.pt, (LPARAM) sel.cpMax);
++
++ ClientToScreen(hwnd, &mwpd.pt);
++ }
++ else {
++ mwpd.pt.x = LOWORD(lParam);
++ mwpd.pt.y = HIWORD(lParam);
++ }
++
++ // First notification
++ NotifyEventHooks(hHookWinPopup, 0, (LPARAM)&mwpd);
++
++ // Someone added items?
++ if (GetMenuItemCount(mwpd.hMenu) > 0) {
++ mwpd.selection = TrackPopupMenu(mwpd.hMenu, TPM_RETURNCMD, mwpd.pt.x, mwpd.pt.y, 0, hwnd, NULL);
++ }
++
++ // Second notification
++ mwpd.uType = MSG_WINDOWPOPUP_SELECTED;
++ NotifyEventHooks(hHookWinPopup, 0, (LPARAM)&mwpd);
++ return 0;
++ }
++ case WM_PASTE:
++ SendMessage(hwnd, EM_PASTESPECIAL, CF_TEXT, 0);
++ return 0;
+ }
+ return CallWindowProc(OldMessageEditProc, hwnd, msg, wParam, lParam);
+ }
+@@ -778,6 +819,7 @@
+ EnableWindow(GetDlgItem(hwndDlg, IDC_AVATAR), FALSE);
+ SendDlgItemMessage(hwndDlg, IDC_LOG, EM_SETOLECALLBACK, 0, (LPARAM) & reOleCallback);
+ SendDlgItemMessage(hwndDlg, IDC_LOG, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_LINK);
++ SendDlgItemMessage(hwndDlg, IDC_MESSAGE, EM_SETEVENTMASK, 0, ENM_CHANGE);
+ /* duh, how come we didnt use this from the start? */
+ SendDlgItemMessage(hwndDlg, IDC_LOG, EM_AUTOURLDETECT, (WPARAM) TRUE, 0);
+ if (dat->hContact && dat->szProto) {
+@@ -1120,6 +1162,7 @@
+ COLORREF colour = DBGetContactSettingDword(NULL, SRMMMOD, SRMSGSET_BKGCOLOUR, SRMSGDEFSET_BKGCOLOUR);
+ dat->hBkgBrush = CreateSolidBrush(colour);
+ SendDlgItemMessage(hwndDlg, IDC_LOG, EM_SETBKGNDCOLOR, 0, colour);
++ SendDlgItemMessage(hwndDlg, IDC_MESSAGE, EM_SETBKGNDCOLOR, 0, colour);
+ }
+ { // avatar stuff
+ dat->avatarPic = 0;
+@@ -1133,12 +1176,17 @@
+ {
+ HFONT hFont;
+ LOGFONT lf;
++ CHARFORMAT cf = {0};
+ hFont = (HFONT) SendDlgItemMessage(hwndDlg, IDC_MESSAGE, WM_GETFONT, 0, 0);
+ if (hFont != NULL && hFont != (HFONT) SendDlgItemMessage(hwndDlg, IDOK, WM_GETFONT, 0, 0))
+ DeleteObject(hFont);
+- LoadMsgDlgFont(MSGFONTID_MESSAGEAREA, &lf, NULL);
++ LoadMsgDlgFont(MSGFONTID_MESSAGEAREA, &lf, &cf.crTextColor);
+ hFont = CreateFontIndirect(&lf);
+ SendDlgItemMessage(hwndDlg, IDC_MESSAGE, WM_SETFONT, (WPARAM) hFont, MAKELPARAM(TRUE, 0));
++
++ cf.cbSize = sizeof(CHARFORMAT);
++ cf.dwMask = CFM_COLOR;
++ SendDlgItemMessage(hwndDlg, IDC_MESSAGE, EM_SETCHARFORMAT, SCF_ALL, (WPARAM) &cf);
+ }
+
+ /*
+@@ -1528,20 +1576,10 @@
+ SetTimer(hwndDlg, TIMERID_MSGSEND, DBGetContactSettingDword(NULL, SRMMMOD, SRMSGSET_MSGTIMEOUT, SRMSGDEFSET_MSGTIMEOUT), NULL);
+ break;
+ }
+- break;
+
+- case WM_CTLCOLOREDIT:
+- {
+- COLORREF colour;
+- if ((HWND) lParam != GetDlgItem(hwndDlg, IDC_MESSAGE))
++ case WM_MEASUREITEM:
++ if (wParam == 0 || lParam == 0)
+ break;
+- LoadMsgDlgFont(MSGFONTID_MESSAGEAREA, NULL, &colour);
+- SetTextColor((HDC) wParam, colour);
+- SetBkColor((HDC) wParam, DBGetContactSettingDword(NULL, SRMMMOD, SRMSGSET_BKGCOLOUR, SRMSGDEFSET_BKGCOLOUR));
+- return (BOOL) dat->hBkgBrush;
+- }
+-
+- case WM_MEASUREITEM:
+ return CallService(MS_CLIST_MENUMEASUREITEM, wParam, lParam);
+
+ case WM_DRAWITEM:
+Index: msgs.c
+===================================================================
+--- msgs.c (revision 6386)
++++ msgs.c (working copy)
+@@ -28,7 +28,7 @@
+
+ HCURSOR hCurSplitNS, hCurSplitWE, hCurHyperlinkHand;
+ static HANDLE hEventDbEventAdded, hEventDbSettingChange, hEventContactDeleted;
+-HANDLE hHookWinEvt = NULL;
++HANDLE hHookWinEvt = NULL, hHookWinPopup=NULL;
+
+ extern HINSTANCE g_hInst;
+
+@@ -451,6 +451,7 @@
+ CreateServiceFunction("SRMsg/ReadMessage", ReadMessageCommand);
+ CreateServiceFunction("SRMsg/TypingMessage", TypingMessageCommand);
+ hHookWinEvt=CreateHookableEvent(ME_MSG_WINDOWEVENT);
++ hHookWinPopup=CreateHookableEvent(ME_MSG_WINDOWPOPUP);
+ SkinAddNewSoundEx("RecvMsgActive", Translate("Messages"), Translate("Incoming (Focused Window)"));
+ SkinAddNewSoundEx("RecvMsgInactive", Translate("Messages"), Translate("Incoming (Unfocused Window)"));
+ SkinAddNewSoundEx("AlertMsg", Translate("Messages"), Translate("Incoming (New Session)"));
+Index: resource.rc
+===================================================================
+--- resource.rc (revision 6386)
++++ resource.rc (working copy)
+@@ -9,6 +9,7 @@
+ //
+ #include <windows.h>
+ #include <winres.h>
++#include "richedit.h"
+ #include "../../include/statusmodes.h"
+
+ /////////////////////////////////////////////////////////////////////////////
+@@ -105,8 +106,17 @@
+ CAPTION "Message Session"
+ FONT 8, "MS Shell Dlg", 0, 0, 0x1
+ BEGIN
+- EDITTEXT IDC_MESSAGE,1,49,141,13,ES_MULTILINE | ES_AUTOVSCROLL |
+- ES_WANTRETURN | WS_VSCROLL,WS_EX_ACCEPTFILES
++#if defined(UNICODE)
++ CONTROL "",IDC_MESSAGE,"RichEdit20W",ES_MULTILINE |
++ ES_AUTOVSCROLL | ES_NOHIDESEL | ES_WANTRETURN |
++ WS_VSCROLL | WS_TABSTOP,1,49,141,13,WS_EX_ACCEPTFILES |
++ WS_EX_STATICEDGE
++#else
++ CONTROL "",IDC_MESSAGE,"RichEdit20A",ES_MULTILINE |
++ ES_AUTOVSCROLL | ES_NOHIDESEL | ES_WANTRETURN |
++ WS_VSCROLL | WS_TABSTOP,1,49,141,13,WS_EX_ACCEPTFILES |
++ WS_EX_STATICEDGE
++#endif
+ DEFPUSHBUTTON "&Send",IDOK,143,48,39,15
+ PUSHBUTTON "Close",IDCANCEL,129,0,54,15,NOT WS_VISIBLE
+ CONTROL "",IDC_PROTOCOL,"Button",BS_OWNERDRAW,2,5,12,12
+@@ -265,6 +275,7 @@
+ BEGIN
+ "#include <windows.h>\r\n"
+ "#include <winres.h>\r\n"
++ "#include ""richedit.h""\r\n"
+ "#include ""../../include/statusmodes.h""\r\n"
+ "\0"
+ END
+Index: srmm.c
+===================================================================
+--- srmm.c (revision 6386)
++++ srmm.c (working copy)
+@@ -34,7 +34,11 @@
+
+ PLUGININFOEX pluginInfo = {
+ sizeof(PLUGININFOEX),
+- "Send/Receive Messages",
++#ifdef _UNICODE
++ "Send/Receive Messages (Unicode) - RichEdit mod",
++#else
++ "Send/Receive Messages - RichEdit mod",
++#endif
+ PLUGIN_MAKE_VERSION(0, 7, 0, 0),
+ "Send and receive instant messages",
+ "Miranda IM Development Team",
+@@ -44,9 +48,9 @@
+ UNICODE_AWARE,
+ DEFMOD_SRMESSAGE, // replace internal version (if any)
+ #ifdef _UNICODE
+- {0x657fe89b, 0xd121, 0x40c2, { 0x8a, 0xc9, 0xb9, 0xfa, 0x57, 0x55, 0xb3, 0xc }} //{657FE89B-D121-40c2-8AC9-B9FA5755B30C}
++ { 0x8219e097, 0xb94c, 0x4109, { 0x90, 0x71, 0xd9, 0xee, 0x3c, 0xf6, 0x60, 0xcb } } // {8219E097-B94C-4109-9071-D9EE3CF660CB}
+ #else
+- {0xd53dd778, 0x16d2, 0x49ac, { 0x8f, 0xb3, 0x6f, 0x9a, 0x96, 0x1c, 0x9f, 0xd2 }} //{D53DD778-16D2-49ac-8FB3-6F9A961C9FD2}
++ { 0x572029ef, 0x2ecb, 0x40d4, { 0x99, 0xee, 0xbb, 0xb0, 0x74, 0x99, 0xbb, 0xe8 } } // {572029EF-2ECB-40d4-99EE-BBB07499BBE8}
+ #endif
+ };
+
diff --git a/Plugins/utils/mir_options_notify.cpp b/Plugins/utils/mir_options_notify.cpp
new file mode 100644
index 0000000..ddc0a54
--- /dev/null
+++ b/Plugins/utils/mir_options_notify.cpp
@@ -0,0 +1,199 @@
+/*
+Copyright (C) 2005 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#include "mir_options_notify.h"
+
+#include <stdio.h>
+#include <commctrl.h>
+#include <tchar.h>
+
+extern "C"
+{
+#include <newpluginapi.h>
+#include <m_database.h>
+#include <m_utils.h>
+#include <m_langpack.h>
+#include <m_notify.h>
+#include "templates.h"
+}
+
+
+#define MAX_REGS(_A_) ( sizeof(_A_) / sizeof(_A_[0]) )
+
+
+
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Dialog to save options
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+
+BOOL CALLBACK SaveOptsDlgProc(OptPageControl *controls, int controlsSize, HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg)
+ {
+ case WM_USER+100:
+ {
+ HANDLE hNotify = (HANDLE)lParam;
+ SetWindowLong(hwndDlg, GWL_USERDATA, lParam);
+
+ TranslateDialogDefault(hwndDlg);
+
+ for (int i = 0 ; i < controlsSize ; i++)
+ {
+ OptPageControl *ctrl = &controls[i];
+
+ switch(ctrl->type)
+ {
+ case CONTROL_CHECKBOX:
+ {
+ CheckDlgButton(hwndDlg, ctrl->nID, MNotifyGetByte(hNotify, ctrl->setting, (BYTE)ctrl->dwDefValue) == 1 ? BST_CHECKED : BST_UNCHECKED);
+ break;
+ }
+ case CONTROL_SPIN:
+ {
+ SendDlgItemMessage(hwndDlg, ctrl->nIDSpin, UDM_SETBUDDY, (WPARAM)GetDlgItem(hwndDlg, ctrl->nID),0);
+ SendDlgItemMessage(hwndDlg, ctrl->nIDSpin, UDM_SETRANGE, 0, MAKELONG(ctrl->max, ctrl->min));
+ SendDlgItemMessage(hwndDlg, ctrl->nIDSpin, UDM_SETPOS,0, MAKELONG(MNotifyGetWord(hNotify, ctrl->setting, (WORD)ctrl->dwDefValue), 0));
+
+ break;
+ }
+ case CONTROL_COLOR:
+ {
+ SendDlgItemMessage(hwndDlg, ctrl->nID, CPM_SETCOLOUR, 0, (COLORREF) MNotifyGetDWord(hNotify, ctrl->setting, (DWORD)ctrl->dwDefValue));
+
+ break;
+ }
+ case CONTROL_RADIO:
+ {
+ CheckDlgButton(hwndDlg, ctrl->nID, MNotifyGetWord(hNotify, ctrl->setting, (WORD)ctrl->dwDefValue) == ctrl->value ? BST_CHECKED : BST_UNCHECKED);
+ break;
+ }
+ case CONTROL_TEXT:
+ {
+ SetDlgItemText(hwndDlg, ctrl->nID, MNotifyGetTString(hNotify, ctrl->setting, ctrl->szDefVale));
+
+ if (ctrl->max > 0)
+ SendDlgItemMessage(hwndDlg, ctrl->nID, EM_LIMITTEXT, min(ctrl->max, 1024), 0);
+ else
+ SendDlgItemMessage(hwndDlg, ctrl->nID, EM_LIMITTEXT, 1024, 0);
+
+ break;
+ }
+ case CONTROL_TEMPLATE:
+ {
+ SetDlgItemText(hwndDlg, ctrl->nID, MNotifyGetTTemplate(hNotify, ctrl->setting, ctrl->szDefVale));
+
+ if (ctrl->max > 0)
+ SendDlgItemMessage(hwndDlg, ctrl->nID, EM_LIMITTEXT, min(ctrl->max, 1024), 0);
+ else
+ SendDlgItemMessage(hwndDlg, ctrl->nID, EM_LIMITTEXT, 1024, 0);
+
+ break;
+ }
+ }
+ }
+
+ return TRUE;
+ break;
+ }
+ case WM_COMMAND:
+ {
+ // Don't make apply enabled during buddy set crap
+ if (HIWORD(wParam) != EN_CHANGE || (HWND)lParam != GetFocus())
+ {
+ for (int i = 0 ; i < controlsSize ; i++)
+ {
+ if (controls[i].type == CONTROL_SPIN && LOWORD(wParam) == controls[i].nID)
+ {
+ return 0;
+ }
+ }
+ }
+
+ SendMessage(GetParent(GetParent(hwndDlg)), PSM_CHANGED, 0, 0);
+ break;
+ }
+ case WM_NOTIFY:
+ {
+ switch (((LPNMHDR)lParam)->idFrom)
+ {
+ case 0:
+ {
+ switch (((LPNMHDR)lParam)->code)
+ {
+ case PSN_APPLY:
+ {
+ HANDLE hNotify = (HANDLE)GetWindowLong(hwndDlg, GWL_USERDATA);
+ TCHAR tmp[1024];
+
+ for (int i = 0 ; i < controlsSize ; i++)
+ {
+ OptPageControl *ctrl = &controls[i];
+
+ switch(ctrl->type)
+ {
+ case CONTROL_CHECKBOX:
+ {
+ MNotifySetByte(hNotify, ctrl->setting, (BYTE)IsDlgButtonChecked(hwndDlg, ctrl->nID));
+ break;
+ }
+ case CONTROL_SPIN:
+ {
+ MNotifySetWord(hNotify, ctrl->setting, (WORD)SendDlgItemMessage(hwndDlg, ctrl->nIDSpin, UDM_GETPOS, 0, 0));
+ break;
+ }
+ case CONTROL_COLOR:
+ {
+ MNotifySetDWord(hNotify, ctrl->setting, (DWORD)SendDlgItemMessage(hwndDlg, ctrl->nID, CPM_GETCOLOUR, 0, 0));
+ break;
+ }
+ case CONTROL_RADIO:
+ {
+ if (IsDlgButtonChecked(hwndDlg, ctrl->nID))
+ MNotifySetWord(hNotify, ctrl->setting, (BYTE)ctrl->value);
+ break;
+ }
+ case CONTROL_TEXT:
+ {
+ GetDlgItemText(hwndDlg, ctrl->nID, tmp, MAX_REGS(tmp));
+ MNotifySetTString(hNotify, ctrl->setting, tmp);
+ break;
+ }
+ case CONTROL_TEMPLATE:
+ {
+ GetDlgItemText(hwndDlg, ctrl->nID, tmp, MAX_REGS(tmp));
+ MNotifySetTTemplate(hNotify, ctrl->setting, tmp);
+ break;
+ }
+ }
+ }
+
+ return TRUE;
+ }
+ }
+ break;
+ }
+ }
+ break;
+ }
+ }
+
+ return 0;
+}
diff --git a/Plugins/utils/mir_options_notify.h b/Plugins/utils/mir_options_notify.h
new file mode 100644
index 0000000..1f54948
--- /dev/null
+++ b/Plugins/utils/mir_options_notify.h
@@ -0,0 +1,69 @@
+/*
+Copyright (C) 2005 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#ifndef __MIR_OPTIONS_NOTIFY_H__
+# define __MIR_OPTIONS_NOTIFY_H__
+
+#include <windows.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+
+
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Dialog to save options
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+#define CONTROL_CHECKBOX 0 // Stored as BYTE
+#define CONTROL_SPIN 1 // Stored as WORD
+#define CONTROL_COLOR 2 // Stored as DWORD
+#define CONTROL_RADIO 3 // Stored as WORD
+#define CONTROL_TEXT 4 // Stored as char *
+#define CONTROL_TEMPLATE 5 // Stored as Template
+
+struct OptPageControl {
+ int type;
+ int nID;
+ char *setting;
+ union {
+ DWORD dwDefValue;
+ const TCHAR *szDefVale;
+ };
+ union {
+ int nIDSpin;
+ int value;
+ };
+ WORD min;
+ WORD max;
+};
+
+BOOL CALLBACK SaveOptsDlgProc(OptPageControl *controls, int controlsSize, HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam);
+
+
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // __MIR_OPTIONS_NOTIFY_H__
diff --git a/Plugins/voiceservice/Docs/langpack_voiceservice.txt b/Plugins/voiceservice/Docs/langpack_voiceservice.txt
new file mode 100644
index 0000000..7d3a59c
--- /dev/null
+++ b/Plugins/voiceservice/Docs/langpack_voiceservice.txt
@@ -0,0 +1,74 @@
+; Voice Service
+; Author: Pescuma
+
+[Voice Calls]
+
+; Dialog
+[New Voice Call]
+[%s wants to start a voice call with you. What you want to do?]
+[Answer]
+[Drop]
+[From now on, repeat this action for this contact]
+
+; Options
+[Background]
+
+[Popups]
+[Enable popups]
+[ Colours ]
+[Background colour]
+[Text colour]
+[Use Windows colours]
+[Use default colours]
+[ Delay ]
+[From popup plugin]
+[Custom]
+[Permanent]
+[ Actions ]
+[On right click:]
+[On left click:]
+[Do nothing]
+[Close popup]
+[Preview]
+
+[Auto actions]
+[ Automatic Actions ]
+[Auto accept this contact calls]
+[Auto drop this contacts calls]
+
+[ Frame ]
+[Auto-size frame]
+
+; Test popup
+[Test Contact]
+[Test description]
+
+; Status
+[Talking]
+[Calling]
+[Ended]
+[On Hold]
+[Ringing]
+
+; Actions
+[Voice Call]
+[Answer Voice Call]
+[Hold Voice Call]
+[Drop Voice Call]
+
+; Icons
+[Main]
+
+; Popups
+[Calling %s]
+[Ringing call from %s]
+[Voice call ended]
+[Call from %s started]
+[Voice call started]
+[Call from %s is on hold]
+[Voice call on hold]
+
+; Frame popup menu
+[Answer call]
+[Drop call]
+[Hold call]
diff --git a/Plugins/voiceservice/Docs/voiceservice_changelog.txt b/Plugins/voiceservice/Docs/voiceservice_changelog.txt
new file mode 100644
index 0000000..861627a
--- /dev/null
+++ b/Plugins/voiceservice/Docs/voiceservice_changelog.txt
@@ -0,0 +1,35 @@
+Voice Service
+
+Changelog:
+
+. 0.1.0.0
+ + Allow selection of input/output devices
+ + Dialpad
+ + USe history events
+ + Internal improviments and fixes
+ * Changed API (old plugins using it will no longer work)
+
+. 0.0.0.8
+ + Auto-size frame
+ * Fix for background color of list control
+
+. 0.0.0.7
+ + Added dialog on tray icon doubleclick
+ + Handle more than one notification per status
+
+. 0.0.0.6
+ + Added sound support
+
+. 0.0.0.5
+ + Added services for call a contact
+ + Added initial metacontact support
+
+. 0.0.0.4
+ + Added support for miranda 0.8
+ * Fixed some bugs
+
+. 0.0.0.2
+ + Added background color for frame
+
+. 0.0.0.1
+ + Initial version \ No newline at end of file
diff --git a/Plugins/voiceservice/Docs/voiceservice_readme.txt b/Plugins/voiceservice/Docs/voiceservice_readme.txt
new file mode 100644
index 0000000..ec4b913
--- /dev/null
+++ b/Plugins/voiceservice/Docs/voiceservice_readme.txt
@@ -0,0 +1,18 @@
+Voice Service plugin
+--------------------
+
+CAUTION: THIS IS AN ALPHA STAGE PLUGIN. IT CAN DO VERY BAD THINGS. USE AT YOUR OWN RISK.
+
+This is a service to provide a common user interface for voice calls to protocols/plugins that support it.
+
+For it to work, protocols/plugins need to support it. By now, only a mod of MirandaComm2 and a mod of JGmail does that (to check it out, look at its thread in miranda forum).
+
+Many thanks to Angeli-Ka for creating the icons.
+
+To report bugs/make suggestions, go to the forum thread: http://forums.miranda-im.org/showthread.php?p=114862
+
+This plugin needs Miranda 0.9 to work.
+
+Todo:
+- Add meSpeak support
+- Handle more than one way to call a contact \ No newline at end of file
diff --git a/Plugins/voiceservice/Docs/voiceservice_version.txt b/Plugins/voiceservice/Docs/voiceservice_version.txt
new file mode 100644
index 0000000..3a48c76
--- /dev/null
+++ b/Plugins/voiceservice/Docs/voiceservice_version.txt
@@ -0,0 +1 @@
+Voice Service 0.1.0.0 \ No newline at end of file
diff --git a/Plugins/voiceservice/ZIP/doit.bat b/Plugins/voiceservice/ZIP/doit.bat
new file mode 100644
index 0000000..e26ff60
--- /dev/null
+++ b/Plugins/voiceservice/ZIP/doit.bat
@@ -0,0 +1,82 @@
+@echo off
+
+rem Batch file to build and upload files
+rem
+rem TODO: Integration with FL
+
+set name=voiceservice
+
+rem To upload, this var must be set here or in other batch
+rem set ftp=ftp://<user>:<password>@<ftp>/<path>
+
+echo Building %name% ...
+
+msdev ..\%name%.dsp /MAKE "%name% - Win32 Release" /REBUILD
+msdev ..\%name%.dsp /MAKE "%name% - Win32 Unicode Release" /REBUILD
+
+echo Generating files for %name% ...
+
+del *.zip
+del *.dll
+copy ..\..\..\bin\release\Plugins\%name%.dll
+copy ..\..\..\bin\release\Plugins\%name%W.dll
+copy ..\Docs\%name%_changelog.txt
+copy ..\Docs\%name%_version.txt
+copy ..\Docs\%name%_readme.txt
+mkdir Docs
+cd Docs
+del /Q *.*
+copy ..\..\Docs\%name%_readme.txt
+copy ..\..\Docs\langpack_%name%.txt
+rem copy ..\..\Docs\helppack_%name%.txt
+copy ..\..\m_%name%.h
+copy ..\..\m_voice.h
+cd ..
+mkdir src
+cd src
+del /Q *.*
+copy ..\..\*.h
+copy ..\..\*.cpp
+copy ..\..\*.
+copy ..\..\*.rc
+copy ..\..\*.dsp
+copy ..\..\*.dsw
+mkdir Docs
+cd Docs
+del /Q *.*
+copy ..\..\..\Docs\*.*
+cd ..
+cd ..
+
+"C:\Program Files\Filzip\Filzip.exe" -a -rp %name%.zip %name%.dll Docs
+"C:\Program Files\Filzip\Filzip.exe" -a -rp %name%W.zip %name%W.dll Docs
+"C:\Program Files\Filzip\Filzip.exe" -a -rp %name%_src.zip src\*.*
+
+del *.dll
+cd Docs
+del /Q *.*
+cd ..
+rmdir Docs
+cd src
+del /Q *.*
+cd Docs
+del /Q *.*
+cd ..
+rmdir Docs
+cd ..
+rmdir src
+
+if "%ftp%"=="" GOTO END
+
+echo Going to upload files...
+pause
+
+"C:\Program Files\FileZilla\FileZilla.exe" -u .\%name%.zip %ftp% -overwrite -close
+"C:\Program Files\FileZilla\FileZilla.exe" -u .\%name%W.zip %ftp% -overwrite -close
+"C:\Program Files\FileZilla\FileZilla.exe" -u .\%name%_changelog.txt %ftp% -overwrite -close
+"C:\Program Files\FileZilla\FileZilla.exe" -u .\%name%_version.txt %ftp% -overwrite -close
+"C:\Program Files\FileZilla\FileZilla.exe" -u .\%name%_readme.txt %ftp% -overwrite -close
+
+:END
+
+echo Done.
diff --git a/Plugins/voiceservice/codepages.cpp b/Plugins/voiceservice/codepages.cpp
new file mode 100644
index 0000000..6441cda
--- /dev/null
+++ b/Plugins/voiceservice/codepages.cpp
@@ -0,0 +1,307 @@
+
+struct {
+ char *name;
+ UINT codepage;
+} codepages[] = {
+ { "ISO8859-1", 28591 },
+ { "UTF-7", CP_UTF7 },
+ { "UTF-8", CP_UTF8 },
+ { "UTF7", CP_UTF7 },
+ { "UTF8", CP_UTF8 },
+ { "ISO8859-2", 28592 },
+ { "ISO8859-3", 28593 },
+ { "ISO8859-4", 28594 },
+ { "ISO8859-5", 28595 },
+ { "ISO8859-6", 28596 },
+ { "ISO8859-7", 28597 },
+ { "ISO8859-8", 28598 },
+ { "ISO8859-9", 28599 },
+ { "ASMO-708", 708 },
+ { "DOS-720", 720 },
+ { "iso-8859-6", 28596 },
+ { "arabic", 28596 },
+ { "csISOLatinArabic", 28596 },
+ { "ECMA-114", 28596 },
+ { "ISO_8859-6", 28596 },
+ { "ISO_8859-6:1987", 28596 },
+ { "iso-ir-127", 28596 },
+ { "x-mac-arabic", 10004 },
+ { "windows-1256", 1256 },
+ { "cp1256", 1256 },
+ { "ibm775", 775 },
+ { "CP500", 775 },
+ { "iso-8859-4", 28594 },
+ { "csISOLatin4", 28594 },
+ { "ISO_8859-4", 28594 },
+ { "ISO_8859-4:1988", 28594 },
+ { "iso-ir-110", 28594 },
+ { "l4", 28594 },
+ { "latin4", 28594 },
+ { "windows-1257", 1257 },
+ { "ibm852", 852 },
+ { "cp852", 852 },
+ { "iso-8859-2", 28592 },
+ { "csISOLatin2", 28592 },
+ { "iso_8859-2", 28592 },
+ { "iso_8859-2:1987", 28592 },
+ { "iso-ir-101", 28592 },
+ { "l2", 28592 },
+ { "latin2", 28592 },
+ { "x-mac-ce", 10029 },
+ { "windows-1250", 1250 },
+ { "x-cp1250", 1250 },
+ { "EUC-CN", 51936 },
+ { "x-euc-cn", 51936 },
+ { "gb2312", 936 },
+ { "chinese", 936 },
+ { "CN-GB", 936 },
+ { "csGB2312", 936 },
+ { "csGB231280", 936 },
+ { "csISO58GB231280", 936 },
+ { "GB_2312-80", 936 },
+ { "GB231280", 936 },
+ { "GB2312-80", 936 },
+ { "GBK", 936 },
+ { "iso-ir-58", 936 },
+ { "hz-gb-2312", 52936 },
+ { "x-mac-chinesesimp", 10008 },
+ { "big5", 950 },
+ { "cn-big5", 950 },
+ { "csbig5", 950 },
+ { "x-x-big5", 950 },
+ { "x-Chinese-CNS", 20000 },
+ { "x-Chinese-Eten", 20002 },
+ { "x-mac-chinesetrad", 10002 },
+ { "cp866", 866 },
+ { "ibm866", 866 },
+ { "iso-8859-5", 28595 },
+ { "csISOLatin5", 28595 },
+ { "csISOLatinCyrillic", 28595 },
+ { "cyrillic", 28595 },
+ { "ISO_8859-5", 28595 },
+ { "ISO_8859-5:1988", 28595 },
+ { "iso-ir-144", 28595 },
+ { "l5", 28595 },
+ { "KOI8-R", 20866 },
+ { "csKOI8R", 20866 },
+ { "koi", 20866 },
+ { "koi8", 20866 },
+ { "koi8r", 20866 },
+ { "KOI8-U", 21866 },
+ { "koi8-ru", 21866 },
+ { "x-mac-cyrillic", 10007 },
+ { "windows-1251", 1251 },
+ { "Win1251", 1251 },
+ { "x-cp1251", 1251 },
+ { "x-Europa", 29001 },
+ { "x-IA5-German", 20106 },
+ { "ibm737", 737 },
+ { "iso-8859-7", 28597 },
+ { "csISOLatinGreek", 28597 },
+ { "ECMA-118", 28597 },
+ { "ELOT_928", 28597 },
+ { "greek", 28597 },
+ { "greek8", 28597 },
+ { "ISO_8859-7", 28597 },
+ { "ISO_8859-7:1987", 28597 },
+ { "iso-ir-126", 28597 },
+ { "x-mac-greek", 10006 },
+ { "windows-1253", 1253 },
+ { "ibm869", 869 },
+ { "DOS-862", 862 },
+ { "iso-8859-8-i", 38598 },
+ { "logical", 38598 },
+ { "iso-8859-8", 28598 },
+ { "csISOLatinHebrew", 28598 },
+ { "hebrew", 28598 },
+ { "ISO_8859-8", 28598 },
+ { "ISO_8859-8:1988", 28598 },
+ { "ISO-8859-8", 28598 },
+ { "iso-ir-138", 28598 },
+ { "visual", 28598 },
+ { "x-mac-hebrew", 10005 },
+ { "windows-1255", 1255 },
+ { "ISO_8859-8-I", 1255 },
+ { "ISO-8859-8", 1255 },
+ { "x-EBCDIC-Arabic", 20420 },
+ { "x-EBCDIC-CyrillicRussian", 20880 },
+ { "x-EBCDIC-CyrillicSerbianBulgarian", 21025 },
+ { "x-EBCDIC-DenmarkNorway", 20277 },
+ { "x-ebcdic-denmarknorway-euro", 1142 },
+ { "x-EBCDIC-FinlandSweden", 20278 },
+ { "x-ebcdic-finlandsweden-euro", 1143 },
+ { "X-EBCDIC-France", 1143 },
+ { "X-EBCDIC-France", 1143 },
+ { "x-ebcdic-france-euro", 1147 },
+ { "x-EBCDIC-Germany", 20273 },
+ { "x-ebcdic-germany-euro", 1141 },
+ { "x-EBCDIC-GreekModern", 875 },
+ { "x-EBCDIC-Greek", 20423 },
+ { "x-EBCDIC-Hebrew", 20424 },
+ { "x-EBCDIC-Icelandic", 20871 },
+ { "x-ebcdic-icelandic-euro", 1149 },
+ { "x-ebcdic-international-euro", 1148 },
+ { "x-EBCDIC-Italy", 20280 },
+ { "x-ebcdic-italy-euro", 1144 },
+ { "x-EBCDIC-JapaneseAndKana", 50930 },
+ { "x-EBCDIC-JapaneseAndJapaneseLatin", 50939 },
+ { "x-EBCDIC-JapaneseAndUSCanada", 50931 },
+ { "x-EBCDIC-JapaneseKatakana", 20290 },
+ { "x-EBCDIC-KoreanAndKoreanExtended", 50933 },
+ { "x-EBCDIC-KoreanExtended", 20833 },
+ { "CP870", 870 },
+ { "x-EBCDIC-SimplifiedChinese", 50935 },
+ { "X-EBCDIC-Spain", 20284 },
+ { "x-ebcdic-spain-euro", 1145 },
+ { "x-EBCDIC-Thai", 20838 },
+ { "x-EBCDIC-TraditionalChinese", 50937 },
+ { "CP1026", 1026 },
+ { "x-EBCDIC-Turkish", 20905 },
+ { "x-EBCDIC-UK", 20285 },
+ { "x-ebcdic-uk-euro", 1146 },
+ { "ebcdic-cp-us", 37 },
+ { "x-ebcdic-cp-us-euro", 1140 },
+ { "ibm861", 861 },
+ { "x-mac-icelandic", 10079 },
+ { "x-iscii-as", 57006 },
+ { "x-iscii-be", 57003 },
+ { "x-iscii-de", 57002 },
+ { "x-iscii-gu", 57010 },
+ { "x-iscii-ka", 57008 },
+ { "x-iscii-ma", 57009 },
+ { "x-iscii-or", 57007 },
+ { "x-iscii-pa", 57011 },
+ { "x-iscii-ta", 57004 },
+ { "x-iscii-te", 57005 },
+ { "euc-jp", 51932 },
+ { "csEUCPkdFmtJapanese", 51932 },
+ { "Extended_UNIX_Code_Packed_Format_for_Japanese", 51932 },
+ { "x-euc", 51932 },
+ { "x-euc-jp", 51932 },
+ { "iso-2022-jp", 50220 },
+ { "iso-2022-jp", 50222 },
+ { "_iso-2022-jp$SIO", 50222 },
+ { "csISO2022JP", 50221 },
+ { "_iso-2022-jp", 50221 },
+ { "x-mac-japanese", 10001 },
+ { "shift_jis", 932 },
+ { "csShiftJIS", 932 },
+ { "csWindows31J", 932 },
+ { "ms_Kanji", 932 },
+ { "shift-jis", 932 },
+ { "x-ms-cp932", 932 },
+ { "x-sjis", 932 },
+ { "ks_c_5601-1987", 949 },
+ { "csKSC56011987", 949 },
+ { "euc-kr", 949 },
+ { "iso-ir-149", 949 },
+ { "korean", 949 },
+ { "ks_c_5601", 949 },
+ { "ks_c_5601_1987", 949 },
+ { "ks_c_5601-1989", 949 },
+ { "KSC_5601", 949 },
+ { "KSC5601", 949 },
+ { "euc-kr", 51949 },
+ { "csEUCKR", 51949 },
+ { "iso-2022-kr", 50225 },
+ { "csISO2022KR", 50225 },
+ { "Johab", 1361 },
+ { "x-mac-korean", 10003 },
+ { "iso-8859-3", 28593 },
+ { "csISO", 28593 },
+ { "Latin3", 28593 },
+ { "ISO_8859-3", 28593 },
+ { "ISO_8859-3:1988", 28593 },
+ { "iso-ir-109", 28593 },
+ { "l3", 28593 },
+ { "latin3", 28593 },
+ { "iso-8859-15", 28605 },
+ { "csISO", 28605 },
+ { "Latin9", 28605 },
+ { "ISO_8859-15", 28605 },
+ { "l9", 28605 },
+ { "latin9", 28605 },
+ { "x-IA5-Norwegian", 20108 },
+ { "IBM437", 437 },
+ { "437", 437 },
+ { "cp437", 437 },
+ { "csPC8", 437 },
+ { "CodePage437", 437 },
+ { "x-IA5-Swedish", 20107 },
+ { "windows-874", 874 },
+ { "DOS-874", 874 },
+ { "iso-8859-11", 874 },
+ { "TIS-620", 874 },
+ { "ibm857", 857 },
+ { "iso-8859-9", 28599 },
+ { "csISO", 28599 },
+ { "Latin5", 28599 },
+ { "ISO_8859-9", 28599 },
+ { "ISO_8859-9:1989", 28599 },
+ { "iso-ir-148", 28599 },
+ { "l5", 28599 },
+ { "latin5", 28599 },
+ { "x-mac-turkish", 10081 },
+ { "windows-1254", 1254 },
+ { "ISO_8859-9", 1254 },
+ { "ISO_8859-9:1989", 1254 },
+ { "iso-8859-9", 1254 },
+ { "iso-ir-148", 1254 },
+ { "latin5", 1254 },
+ { "unicode", 1200 },
+ { "utf-16", 1200 },
+ { "unicodeFFFE", 1201 },
+ { "utf-7", 65000 },
+ { "csUnicode11UTF7", 65000 },
+ { "unicode-1-1-utf-7", 65000 },
+ { "x-unicode-2-0-utf-7", 65000 },
+ { "utf-8", 65001 },
+ { "unicode-1-1-utf-8", 65001 },
+ { "unicode-2-0-utf-8", 65001 },
+ { "x-unicode-2-0-utf-8", 65001 },
+ { "us-ascii", 20127 },
+ { "ANSI_X3.4-1968", 20127 },
+ { "ANSI_X3.4-1986", 20127 },
+ { "ascii", 20127 },
+ { "cp367", 20127 },
+ { "csASCII", 20127 },
+ { "IBM367", 20127 },
+ { "ISO_646.irv:1991", 20127 },
+ { "ISO646-US", 20127 },
+ { "iso-ir-6us", 20127 },
+ { "windows-1258", 1258 },
+ { "ibm850", 850 },
+ { "x-IA5", 20105 },
+ { "iso-8859-1", 28591 },
+ { "cp819", 28591 },
+ { "csISO", 28591 },
+ { "Latin1", 28591 },
+ { "ibm819", 28591 },
+ { "iso_8859-1", 28591 },
+ { "iso_8859-1:1987", 28591 },
+ { "iso-ir-100", 28591 },
+ { "l1", 28591 },
+ { "latin1", 28591 },
+ { "macintosh", 10000 },
+ { "Windows-1252", 1252 },
+ { "ANSI_X3.4-1968", 1252 },
+ { "ANSI_X3.4-1986", 1252 },
+ { "ascii", 1252 },
+ { "cp367", 1252 },
+ { "cp819", 1252 },
+ { "csASCII", 1252 },
+ { "IBM367", 1252 },
+ { "ibm819", 1252 },
+ { "ISO_646.irv:1991", 1252 },
+ { "iso_8859-1", 1252 },
+ { "iso_8859-1:1987", 1252 },
+ { "ISO646-US", 1252 },
+ { "iso-ir-100", 1252 },
+ { "iso-ir-6", 1252 },
+ { "latin1", 1252 },
+ { "us", 1252 },
+ { "us-ascii", 1252 },
+ { "x-ansi", 1252 },
+ { "microsoft-cp1251", 1251 }
+};
+
diff --git a/Plugins/voiceservice/commons.h b/Plugins/voiceservice/commons.h
new file mode 100644
index 0000000..ffdb622
--- /dev/null
+++ b/Plugins/voiceservice/commons.h
@@ -0,0 +1,337 @@
+/*
+Copyright (C) 2006 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#ifndef __COMMONS_H__
+# define __COMMONS_H__
+
+
+#include <windows.h>
+#include <tchar.h>
+#include <stdio.h>
+#include <time.h>
+#include <commctrl.h>
+
+
+// Disable "...truncated to '255' characters in the debug information" warnings
+#pragma warning(disable: 4786)
+
+#include <vector>
+using namespace std;
+
+
+
+// Miranda headers
+#define MIRANDA_VER 0x0800
+#include <win2k.h>
+#include <newpluginapi.h>
+#include <m_system.h>
+#include <m_system_cpp.h>
+#include <m_protocols.h>
+#include <m_protosvc.h>
+#include <m_clist.h>
+#include <m_clui.h>
+#include <m_clc.h>
+#include <m_contacts.h>
+#include <m_langpack.h>
+#include <m_database.h>
+#include <m_options.h>
+#include <m_utils.h>
+#include <m_button.h>
+#include <m_updater.h>
+#include <m_popup.h>
+#include <m_cluiframes.h>
+#include <m_icolib.h>
+#include <m_metacontacts.h>
+#include <m_fontservice.h>
+#include <m_skin.h>
+#include <m_historyevents.h>
+#include <portaudio.h>
+
+#include "../utils/mir_memory.h"
+#include "../utils/mir_icons.h"
+#include "../utils/mir_options.h"
+#include "../utils/mir_dbutils.h"
+#include "../utils/utf8_helpers.h"
+
+#include "m_voice.h"
+#include "m_voiceservice.h"
+
+#include "resource.h"
+#include "options.h"
+#include "frame.h"
+#include "popup.h"
+
+
+#define MODULE_NAME "VoiceService"
+
+
+// Global Variables
+extern HINSTANCE hInst;
+extern PLUGINLINK *pluginLink;
+
+
+#define MAX_REGS(_A_) ( sizeof(_A_) / sizeof(_A_[0]) )
+
+
+#define ACTION_CALL 0
+#define ACTION_ANSWER 1
+#define ACTION_HOLD 2
+#define ACTION_DROP 3
+
+#define NUM_STATES 6
+#define NUM_FONTS NUM_STATES
+
+#define AUTO_NOTHING 0
+#define AUTO_ACCEPT 1
+#define AUTO_DROP 2
+
+extern HFONT fonts[NUM_FONTS];
+extern COLORREF font_colors[NUM_FONTS];
+extern int font_max_height;
+extern COLORREF bkg_color;
+extern HBRUSH bk_brush;
+
+
+
+
+class VoiceProvider
+{
+public:
+ TCHAR description[256];
+ char name[256];
+ char icon[256];
+ int flags;
+ bool is_protocol;
+
+ VoiceProvider(const char *name, const TCHAR *description, int flags, const char *icon);
+ ~VoiceProvider();
+
+ bool CanCall(const TCHAR *number);
+ bool CanCall(HANDLE hContact, BOOL now = TRUE);
+ void Call(HANDLE hContact, const TCHAR *number);
+
+ bool CanHold();
+
+ bool CanSendDTMF();
+
+ HICON GetIcon();
+ void ReleaseIcon(HICON hIcon);
+
+private:
+ bool canHold;
+ HANDLE state_hook;
+};
+
+
+class VoiceCall
+{
+public:
+ VoiceProvider *module;
+ char *id; // Protocol especific ID for this call
+ HANDLE hContact;
+ TCHAR name[256];
+ TCHAR number[256];
+ TCHAR displayName[256];
+ int state;
+ DWORD end_time;
+ bool incoming;
+ bool secure;
+
+ VoiceCall(VoiceProvider *module, const char *id);
+ ~VoiceCall();
+
+ void AppendCallerID(HANDLE hContact, const TCHAR *name, const TCHAR *number);
+
+ void SetState(int state);
+
+ void Drop();
+ void Answer();
+ void Hold();
+
+ bool CanDrop();
+ bool CanAnswer();
+ bool CanHold();
+
+ bool CanSendDTMF();
+ void SendDTMF(TCHAR c);
+
+ bool IsFinished();
+
+ void Notify(bool history = true, bool popup = true, bool sound = true, bool clist = true);
+ void SetNewCallHWND(HWND hwnd);
+
+private:
+ HWND hwnd;
+ bool clistBlinking;
+
+ void RemoveNotifications();
+ void CreateDisplayName();
+
+};
+
+
+extern OBJLIST<VoiceProvider> modules;
+extern OBJLIST<VoiceCall> calls;
+
+void Answer(VoiceCall *call);
+bool CanCall(HANDLE hContact, BOOL now = TRUE);
+bool CanCall(const TCHAR *number);
+bool CanCallNumber();
+VoiceCall * GetTalkingCall();
+bool IsFinalState(int state);
+
+
+// See if a protocol service exists
+__inline static int ProtoServiceExists(const char *szModule,const char *szService)
+{
+ char str[MAXMODULELABELLENGTH];
+ mir_snprintf(str, MAX_REGS(str), "%s%s", szModule, szService);
+ return ServiceExists(str);
+}
+
+
+static TCHAR *lstrtrim(TCHAR *str)
+{
+ int len = lstrlen(str);
+
+ int i;
+ for(i = len - 1; i >= 0 && (str[i] == ' ' || str[i] == '\t'); --i) ;
+ if (i < len - 1)
+ {
+ ++i;
+ str[i] = _T('\0');
+ len = i;
+ }
+
+ for(i = 0; i < len && (str[i] == ' ' || str[i] == '\t'); ++i) ;
+ if (i > 0)
+ memmove(str, &str[i], (len - i + 1) * sizeof(TCHAR));
+
+ return str;
+}
+
+
+static BOOL IsEmptyA(const char *str)
+{
+ return str == NULL || str[0] == 0;
+}
+
+static BOOL IsEmptyW(const WCHAR *str)
+{
+ return str == NULL || str[0] == 0;
+}
+
+#ifdef UNICODE
+# define IsEmpty IsEmptyW
+#else
+# define IsEmpty IsEmptyA
+#endif
+
+
+
+#define ICON_SIZE 16
+
+#define TIME_TO_SHOW_ENDED_CALL 5000 // ms
+
+
+#define MS_VOICESERVICE_CLIST_DBLCLK "VoiceService/CList/RingingDblClk"
+
+#define MS_VOICESERVICE_CM_CALL "VoiceService/ContactMenu/Call"
+#define MS_VOICESERVICE_CM_ANSWER "VoiceService/ContactMenu/Answer"
+#define MS_VOICESERVICE_CM_HOLD "VoiceService/ContactMenu/Hold"
+#define MS_VOICESERVICE_CM_DROP "VoiceService/ContactMenu/Drop"
+
+
+
+static struct {
+ char *name;
+ char *description;
+} sounds[] = {
+ { "voice_started", "Started talking"},
+ { "voice_ringing", "Ringing"},
+ { "voice_calling", "Calling a contact"},
+ { "voice_holded", "Put a call on Hold"},
+ { "voice_ended", "End of call"},
+ { "voice_busy", "Busy signal"},
+ { "voice_dialpad", "Dialpad press"},
+};
+
+static TCHAR *stateTexts[] = {
+ _T("Call from %s has started"),
+ _T("Call from %s is ringing"),
+ _T("Calling %s"),
+ _T("Call from %s is on hold"),
+ _T("Call from %s has ended"),
+ _T("%s is busy"),
+};
+
+static TCHAR *popupTitles[] = {
+ _T("Voice call started"),
+ _T("Voice call ringing"),
+ _T("Voice call"),
+ _T("Voice call on hold"),
+ _T("Voice call ended"),
+ _T("Voice call busy"),
+};
+
+static TCHAR *stateNames[] = {
+ _T("Talking"),
+ _T("Ringing"),
+ _T("Calling"),
+ _T("On Hold"),
+ _T("Ended"),
+ _T("Busy"),
+};
+
+static TCHAR *actionNames[] = {
+ _T("Make Voice Call"),
+ _T("Answer Voice Call"),
+ _T("Hold Voice Call"),
+ _T("Drop Voice Call"),
+};
+
+static char *stateIcons[] = {
+ "vc_talking",
+ "vc_ringing",
+ "vc_calling",
+ "vc_on_hold",
+ "vc_ended",
+ "vc_busy"
+};
+
+static char *actionIcons[] = {
+ "vca_call",
+ "vca_answer" ,
+ "vca_hold",
+ "vca_drop",
+ "vc_main",
+ "vc_dialpad"
+};
+
+static char *mainIcons[] = {
+ "vc_main",
+ "vc_dialpad",
+ "vc_secure"
+};
+
+
+
+
+#endif // __COMMONS_H__
diff --git a/Plugins/voiceservice/concepts/concepts.txt b/Plugins/voiceservice/concepts/concepts.txt
new file mode 100644
index 0000000..6a4c497
--- /dev/null
+++ b/Plugins/voiceservice/concepts/concepts.txt
@@ -0,0 +1,42 @@
+General ideas about the plugin:
+
+- A voice provider can call contacts from varios protocols (even if it is a protocol). A voice provider is a plugin that registered itself with MS_VOICESERVICE_REGISTER. The voice provider can be an account or just a usual plugin. It is an account if the name send in MS_VOICESERVICE_REGISTER == and account name. Even if it is an account, it can call contacts from any protocol, not just its contacts. For example, iax will be a protocol that can call any contact that has a phone number.
+
+- A voice contact can call a contact with diferent means (by the contact and by number associated with the contact). A call can be made to an HContact (for example using MSN) or to a number associated with an hContact (for example its home phone). More than that, you may be able to call a contact from 3 means: MSN / contact ; SkypeOut / home number ; SkypeOut / work number ; IAX / home number
+
+- A voice provider is a protocol if it has the same name as an account
+
+- Each call can have one contact associated with it (or none)
+
+- More than one call can happen at the same time
+
+- my idea is that there are 3 types of calls:
+ 1. to an hContact. That is a call made thought a protocol, like MSN or skype, that uses the protocol "im network"
+ 2. a call to a number without an hContact. That is a phone call, or a SkypeOut call
+ 3. a call with a hContact and a number. That is almost like 2, but we have more info to show the GUI and were to blink icons. For example in incoming phone call where the call id is the same as a contact, or if we use a phone number stored in a contact
+ 2 and 3 are really "external" calls in the sense they are not using an im network, but instead are identified by a number that may be or may not be associated with a contact
+
+- It is voice service's responsability to handle the call switching/answering/droping logic
+
+- Voice provides should be as simple as possible (they are already complicated by the protocols itself)
+
+- As long as possible voice service should show the options to the user and the providers should only read it from the DB
+
+- All the GUI (as far as possible) is voice service's responsability
+
+
+
+
+Future:
+
+- Voice service will (with some luck) handle video in the future
+
+ - The dialpad will be skinnable
+
+
+
+- Some hookable way to fetch numbers from contacts
+
+- Number formaters
+
+- Record calls?
diff --git a/Plugins/voiceservice/concepts/frame.bmml b/Plugins/voiceservice/concepts/frame.bmml
new file mode 100644
index 0000000..8ce94de
--- /dev/null
+++ b/Plugins/voiceservice/concepts/frame.bmml
@@ -0,0 +1,212 @@
+<mockup version="1.0" skin="sketch" measuredW="999" measuredH="534" mockupW="936" mockupH="445">
+ <controls>
+ <control controlID="0" controlTypeID="com.balsamiq.mockups::FieldSet" x="255" y="191" w="317" h="333" measuredW="200" measuredH="170" zOrder="0" locked="false" isInGroup="-1">
+ <controlProperties>
+ <text/>
+ </controlProperties>
+ </control>
+ <control controlID="2" controlTypeID="com.balsamiq.mockups::Icon" x="262" y="206" w="-1" h="-1" measuredW="24" measuredH="24" zOrder="1" locked="false" isInGroup="-1">
+ <controlProperties>
+ <icon>WirelessIcon%7Csmall</icon>
+ </controlProperties>
+ </control>
+ <control controlID="3" controlTypeID="com.balsamiq.mockups::Label" x="314" y="206" w="-1" h="-1" measuredW="89" measuredH="25" zOrder="2" locked="false" isInGroup="-1">
+ <controlProperties>
+ <text>Contact%20name</text>
+ </controlProperties>
+ </control>
+ <control controlID="4" controlTypeID="com.balsamiq.mockups::Icon" x="286" y="206" w="-1" h="-1" measuredW="24" measuredH="24" zOrder="3" locked="false" isInGroup="-1">
+ <controlProperties>
+ <icon>UserIcon%7Csmall</icon>
+ </controlProperties>
+ </control>
+ <control controlID="5" controlTypeID="com.balsamiq.mockups::Icon" x="533" y="206" w="-1" h="-1" measuredW="24" measuredH="24" zOrder="4" locked="false" isInGroup="-1">
+ <controlProperties>
+ <icon>PauseIcon%7Csmall</icon>
+ </controlProperties>
+ </control>
+ <control controlID="6" controlTypeID="com.balsamiq.mockups::Icon" x="509" y="206" w="-1" h="-1" measuredW="24" measuredH="24" zOrder="5" locked="false" isInGroup="-1">
+ <controlProperties>
+ <icon>TapeIcon%7Csmall</icon>
+ </controlProperties>
+ </control>
+ <control controlID="7" controlTypeID="com.balsamiq.mockups::Icon" x="262" y="230" w="-1" h="-1" measuredW="24" measuredH="24" zOrder="6" locked="false" isInGroup="-1">
+ <controlProperties>
+ <icon>PauseIcon%7Csmall</icon>
+ </controlProperties>
+ </control>
+ <control controlID="8" controlTypeID="com.balsamiq.mockups::Label" x="314" y="230" w="-1" h="-1" measuredW="176" measuredH="25" zOrder="7" locked="false" isInGroup="-1">
+ <controlProperties>
+ <text>1234-5678%20%28Contact%20name%29</text>
+ </controlProperties>
+ </control>
+ <control controlID="9" controlTypeID="com.balsamiq.mockups::Icon" x="286" y="230" w="-1" h="-1" measuredW="24" measuredH="24" zOrder="8" locked="false" isInGroup="-1">
+ <controlProperties>
+ <icon>UserIcon%7Csmall</icon>
+ </controlProperties>
+ </control>
+ <control controlID="10" controlTypeID="com.balsamiq.mockups::Icon" x="533" y="230" w="-1" h="-1" measuredW="24" measuredH="24" zOrder="9" locked="false" isInGroup="-1">
+ <controlProperties>
+ <icon>RightTriangleIcon%7Csmall</icon>
+ </controlProperties>
+ </control>
+ <control controlID="12" controlTypeID="com.balsamiq.mockups::Arrow" x="247" y="105" w="29" h="94" measuredW="150" measuredH="100" zOrder="10" locked="false" isInGroup="-1">
+ <controlProperties>
+ <text/>
+ <direction>top</direction>
+ <leftArrow>false</leftArrow>
+ </controlProperties>
+ </control>
+ <control controlID="13" controlTypeID="com.balsamiq.mockups::Label" x="178" y="80" w="-1" h="-1" measuredW="71" measuredH="25" zOrder="11" locked="false" isInGroup="-1">
+ <controlProperties>
+ <text>Call%20status</text>
+ </controlProperties>
+ </control>
+ <control controlID="14" controlTypeID="com.balsamiq.mockups::Label" x="286" y="79" w="-1" h="-1" measuredW="235" measuredH="25" zOrder="12" locked="false" isInGroup="-1">
+ <controlProperties>
+ <text>Voice%20provider%20%28not%20contact%20protocol%29</text>
+ </controlProperties>
+ </control>
+ <control controlID="15" controlTypeID="com.balsamiq.mockups::Arrow" x="299" y="105" w="83" h="95" measuredW="150" measuredH="100" zOrder="13" locked="false" isInGroup="-1">
+ <controlProperties>
+ <text/>
+ <direction>bottom</direction>
+ <rightArrow>false</rightArrow>
+ </controlProperties>
+ </control>
+ <control controlID="16" controlTypeID="com.balsamiq.mockups::Arrow" x="397" y="123" w="93" h="79" measuredW="150" measuredH="100" zOrder="14" locked="false" isInGroup="-1">
+ <controlProperties>
+ <text/>
+ <direction>bottom</direction>
+ <rightArrow>false</rightArrow>
+ </controlProperties>
+ </control>
+ <control controlID="17" controlTypeID="com.balsamiq.mockups::Label" x="490" y="114" w="-1" h="-1" measuredW="186" measuredH="25" zOrder="15" locked="false" isInGroup="-1">
+ <controlProperties>
+ <text>Contact%20name%20and/or%20number</text>
+ </controlProperties>
+ </control>
+ <control controlID="18" controlTypeID="com.balsamiq.mockups::Arrow" x="536" y="154" w="93" h="46" measuredW="150" measuredH="100" zOrder="16" locked="false" isInGroup="-1">
+ <controlProperties>
+ <text/>
+ <direction>bottom</direction>
+ <rightArrow>false</rightArrow>
+ </controlProperties>
+ </control>
+ <control controlID="19" controlTypeID="com.balsamiq.mockups::Label" x="629" y="149" w="-1" h="-1" measuredW="80" measuredH="25" zOrder="17" locked="false" isInGroup="-1">
+ <controlProperties>
+ <text>Action%20icons</text>
+ </controlProperties>
+ </control>
+ <control controlID="20" controlTypeID="com.balsamiq.mockups::Icon" x="262" y="271" w="-1" h="-1" measuredW="24" measuredH="24" zOrder="18" locked="false" isInGroup="-1">
+ <controlProperties>
+ <icon>DownTriangleIcon%7Csmall</icon>
+ </controlProperties>
+ </control>
+ <control controlID="23" controlTypeID="com.balsamiq.mockups::TextInput" x="291" y="268" w="230" h="-1" measuredW="79" measuredH="29" zOrder="19" locked="false" isInGroup="-1">
+ <controlProperties>
+ <text/>
+ </controlProperties>
+ </control>
+ <control controlID="24" controlTypeID="com.balsamiq.mockups::Button" x="521" y="267" w="-1" h="-1" measuredW="48" measuredH="32" zOrder="20" locked="false" isInGroup="-1">
+ <controlProperties>
+ <text/>
+ <icon>TelephoneIcon%7Csmall</icon>
+ </controlProperties>
+ </control>
+ <control controlID="27" controlTypeID="com.balsamiq.mockups::MultilineButton" x="262" y="301" w="72" h="52" measuredW="43" measuredH="60" zOrder="21" locked="false" isInGroup="-1">
+ <controlProperties>
+ <text>1</text>
+ </controlProperties>
+ </control>
+ <control controlID="28" controlTypeID="com.balsamiq.mockups::MultilineButton" x="377" y="301" w="72" h="52" measuredW="43" measuredH="60" zOrder="22" locked="false" isInGroup="-1">
+ <controlProperties>
+ <text>2</text>
+ </controlProperties>
+ </control>
+ <control controlID="29" controlTypeID="com.balsamiq.mockups::MultilineButton" x="485" y="301" w="72" h="52" measuredW="43" measuredH="60" zOrder="23" locked="false" isInGroup="-1">
+ <controlProperties>
+ <text>3</text>
+ </controlProperties>
+ </control>
+ <control controlID="30" controlTypeID="com.balsamiq.mockups::MultilineButton" x="262" y="353" w="72" h="52" measuredW="43" measuredH="60" zOrder="24" locked="false" isInGroup="-1">
+ <controlProperties>
+ <text>4</text>
+ </controlProperties>
+ </control>
+ <control controlID="31" controlTypeID="com.balsamiq.mockups::MultilineButton" x="377" y="353" w="72" h="52" measuredW="43" measuredH="60" zOrder="25" locked="false" isInGroup="-1">
+ <controlProperties>
+ <text>5</text>
+ </controlProperties>
+ </control>
+ <control controlID="32" controlTypeID="com.balsamiq.mockups::MultilineButton" x="485" y="353" w="72" h="52" measuredW="43" measuredH="60" zOrder="26" locked="false" isInGroup="-1">
+ <controlProperties>
+ <text>6</text>
+ </controlProperties>
+ </control>
+ <control controlID="33" controlTypeID="com.balsamiq.mockups::MultilineButton" x="262" y="405" w="72" h="52" measuredW="43" measuredH="60" zOrder="27" locked="false" isInGroup="-1">
+ <controlProperties>
+ <text>7</text>
+ </controlProperties>
+ </control>
+ <control controlID="34" controlTypeID="com.balsamiq.mockups::MultilineButton" x="377" y="405" w="72" h="52" measuredW="43" measuredH="60" zOrder="28" locked="false" isInGroup="-1">
+ <controlProperties>
+ <text>8</text>
+ </controlProperties>
+ </control>
+ <control controlID="35" controlTypeID="com.balsamiq.mockups::MultilineButton" x="485" y="405" w="72" h="52" measuredW="43" measuredH="60" zOrder="29" locked="false" isInGroup="-1">
+ <controlProperties>
+ <text>9</text>
+ </controlProperties>
+ </control>
+ <control controlID="36" controlTypeID="com.balsamiq.mockups::MultilineButton" x="262" y="457" w="72" h="52" measuredW="42" measuredH="60" zOrder="30" locked="false" isInGroup="-1">
+ <controlProperties>
+ <text>*</text>
+ </controlProperties>
+ </control>
+ <control controlID="37" controlTypeID="com.balsamiq.mockups::MultilineButton" x="377" y="457" w="72" h="52" measuredW="43" measuredH="60" zOrder="31" locked="false" isInGroup="-1">
+ <controlProperties>
+ <text>0</text>
+ </controlProperties>
+ </control>
+ <control controlID="38" controlTypeID="com.balsamiq.mockups::MultilineButton" x="485" y="457" w="72" h="52" measuredW="47" measuredH="60" zOrder="32" locked="false" isInGroup="-1">
+ <controlProperties>
+ <text>%23</text>
+ </controlProperties>
+ </control>
+ <control controlID="39" controlTypeID="com.balsamiq.mockups::VCurly" x="595" y="199" w="180" h="57" measuredW="180" measuredH="140" zOrder="33" locked="false" isInGroup="-1">
+ <controlProperties>
+ <text>Active%20calls</text>
+ </controlProperties>
+ </control>
+ <control controlID="40" controlTypeID="com.balsamiq.mockups::VCurly" x="597" y="264" w="180" h="254" measuredW="180" measuredH="140" zOrder="34" locked="false" isInGroup="-1">
+ <controlProperties>
+ <text>Make%20calls</text>
+ </controlProperties>
+ </control>
+ <control controlID="41" controlTypeID="com.balsamiq.mockups::Arrow" x="105" y="277" w="150" h="24" measuredW="150" measuredH="100" zOrder="35" locked="false" isInGroup="-1">
+ <controlProperties>
+ <direction>bottom</direction>
+ <leftArrow>false</leftArrow>
+ </controlProperties>
+ </control>
+ <control controlID="42" controlTypeID="com.balsamiq.mockups::Label" x="53" y="301" w="-1" h="-1" measuredW="84" measuredH="25" zOrder="36" locked="false" isInGroup="-1">
+ <controlProperties>
+ <text>Hide%20dialpad</text>
+ </controlProperties>
+ </control>
+ <control controlID="43" controlTypeID="com.balsamiq.mockups::Arrow" x="572" y="267" w="214" h="19" measuredW="150" measuredH="100" zOrder="37" locked="false" isInGroup="-1">
+ <controlProperties>
+ <text/>
+ <curvature>0</curvature>
+ <direction>bottom</direction>
+ <rightArrow>false</rightArrow>
+ </controlProperties>
+ </control>
+ <control controlID="46" controlTypeID="com.balsamiq.mockups::Paragraph" x="789" y="247" w="-1" h="-1" measuredW="200" measuredH="140" zOrder="38" locked="false" isInGroup="-1">
+ <controlProperties>
+ <text>Make%20call%0AIf%20more%20than%20one%20provider%20allowed%2C%20%0Ashow%20popup%20with%20provides.%0AChanges%20icon%20to%20X%20if%20no%20provide%20accepts%20that%20number</text>
+ </controlProperties>
+ </control>
+ </controls>
+</mockup> \ No newline at end of file
diff --git a/Plugins/voiceservice/concepts/frame.png b/Plugins/voiceservice/concepts/frame.png
new file mode 100644
index 0000000..8faa84b
--- /dev/null
+++ b/Plugins/voiceservice/concepts/frame.png
Binary files differ
diff --git a/Plugins/voiceservice/concepts/options.bmml b/Plugins/voiceservice/concepts/options.bmml
new file mode 100644
index 0000000..a0368c6
--- /dev/null
+++ b/Plugins/voiceservice/concepts/options.bmml
@@ -0,0 +1,42 @@
+<mockup version="1.0" skin="sketch" measuredW="642" measuredH="420" mockupW="572" mockupH="400">
+ <controls>
+ <control controlID="0" controlTypeID="com.balsamiq.mockups::TitleWindow" x="60" y="10" w="572" h="400" measuredW="450" measuredH="400" zOrder="0" locked="false" isInGroup="-1">
+ <controlProperties>
+ <text>Option</text>
+ <topheight>26</topheight>
+ </controlProperties>
+ </control>
+ <control controlID="1" controlTypeID="com.balsamiq.mockups::Tree" x="75" y="39" w="146" h="342" measuredW="300" measuredH="225" zOrder="1" locked="false" isInGroup="-1">
+ <controlProperties>
+ <text>v%20Voice%0A%3E%20Devices</text>
+ </controlProperties>
+ </control>
+ <control controlID="2" controlTypeID="com.balsamiq.mockups::FieldSet" x="236" y="39" w="379" h="147" measuredW="200" measuredH="170" zOrder="2" locked="false" isInGroup="-1">
+ <controlProperties>
+ <text>Audio</text>
+ </controlProperties>
+ </control>
+ <control controlID="3" controlTypeID="com.balsamiq.mockups::Label" x="251" y="65" w="-1" h="-1" measuredW="46" measuredH="25" zOrder="3" locked="false" isInGroup="-1">
+ <controlProperties>
+ <text>Input%3A</text>
+ </controlProperties>
+ </control>
+ <control controlID="4" controlTypeID="com.balsamiq.mockups::ComboBox" x="312" y="64" w="288" h="-1" measuredW="92" measuredH="25" zOrder="4" locked="false" isInGroup="-1"/>
+ <control controlID="5" controlTypeID="com.balsamiq.mockups::Label" x="251" y="97" w="-1" h="-1" measuredW="57" measuredH="25" zOrder="5" locked="false" isInGroup="-1">
+ <controlProperties>
+ <text>Output%3A</text>
+ </controlProperties>
+ </control>
+ <control controlID="6" controlTypeID="com.balsamiq.mockups::ComboBox" x="312" y="98" w="288" h="-1" measuredW="92" measuredH="25" zOrder="6" locked="false" isInGroup="-1"/>
+ <control controlID="7" controlTypeID="com.balsamiq.mockups::CheckBox" x="251" y="129" w="348" h="20" measuredW="128" measuredH="22" zOrder="7" locked="false" isInGroup="-1">
+ <controlProperties>
+ <text>Echo%20cancelation</text>
+ </controlProperties>
+ </control>
+ <control controlID="8" controlTypeID="com.balsamiq.mockups::CheckBox" x="251" y="151" w="348" h="20" measuredW="125" measuredH="22" zOrder="8" locked="false" isInGroup="-1">
+ <controlProperties>
+ <text>Microfone%20boost</text>
+ </controlProperties>
+ </control>
+ </controls>
+</mockup> \ No newline at end of file
diff --git a/Plugins/voiceservice/concepts/options.png b/Plugins/voiceservice/concepts/options.png
new file mode 100644
index 0000000..cc1eb98
--- /dev/null
+++ b/Plugins/voiceservice/concepts/options.png
Binary files differ
diff --git a/Plugins/voiceservice/concepts/popup.bmml b/Plugins/voiceservice/concepts/popup.bmml
new file mode 100644
index 0000000..7bdee0b
--- /dev/null
+++ b/Plugins/voiceservice/concepts/popup.bmml
@@ -0,0 +1,63 @@
+<mockup version="1.0" skin="sketch" measuredW="1117" measuredH="416" mockupW="1007" mockupH="388">
+ <controls>
+ <control controlID="1" controlTypeID="com.balsamiq.mockups::List" x="397" y="43" w="296" h="363" measuredW="100" measuredH="231" zOrder="0" locked="false" isInGroup="-1">
+ <controlProperties>
+ <text>...%0A...%0A...%0ACall%20with%20MSN%0ACall%201234-5678%0ACall%201234-5678%20with%20SkypeOut%0A...%0A...%0AAnswer%20call%20from%20MSN%0ADrop%20call%20from%201234-5678%20from%20SkypeOut%0A...</text>
+ </controlProperties>
+ </control>
+ <control controlID="3" controlTypeID="com.balsamiq.mockups::Icon" x="663" y="125" w="-1" h="-1" measuredW="24" measuredH="24" zOrder="1" locked="false" isInGroup="-1">
+ <controlProperties>
+ <icon>RightFillTriangleIcon%7Csmall</icon>
+ </controlProperties>
+ </control>
+ <control controlID="4" controlTypeID="com.balsamiq.mockups::List" x="685" y="125" w="125" h="51" measuredW="100" measuredH="126" zOrder="2" locked="false" isInGroup="-1">
+ <controlProperties>
+ <text>With%20IAX%0AWith%20SkypeOut</text>
+ </controlProperties>
+ </control>
+ <control controlID="5" controlTypeID="com.balsamiq.mockups::Arrow" x="247" y="31" w="150" h="79" measuredW="150" measuredH="100" zOrder="3" locked="false" isInGroup="-1">
+ <controlProperties>
+ <text/>
+ <leftArrow>false</leftArrow>
+ </controlProperties>
+ </control>
+ <control controlID="6" controlTypeID="com.balsamiq.mockups::Label" x="100" y="18" w="-1" h="-1" measuredW="147" measuredH="25" zOrder="4" locked="false" isInGroup="-1">
+ <controlProperties>
+ <text>Calls%20for%20the%20hContact</text>
+ </controlProperties>
+ </control>
+ <control controlID="7" controlTypeID="com.balsamiq.mockups::Arrow" x="247" y="133" w="150" h="10" measuredW="150" measuredH="100" zOrder="5" locked="false" isInGroup="-1">
+ <controlProperties>
+ <curvature>0</curvature>
+ <leftArrow>false</leftArrow>
+ </controlProperties>
+ </control>
+ <control controlID="8" controlTypeID="com.balsamiq.mockups::Label" x="100" y="116" w="-1" h="-1" measuredW="234" measuredH="25" zOrder="6" locked="false" isInGroup="-1">
+ <controlProperties>
+ <text>Calls%20from%20numbers%20from%20the%20contact</text>
+ </controlProperties>
+ </control>
+ <control controlID="9" controlTypeID="com.balsamiq.mockups::Arrow" x="794" y="138" w="86" h="26" measuredW="150" measuredH="100" zOrder="7" locked="false" isInGroup="-1">
+ <controlProperties>
+ <text/>
+ <rightArrow>false</rightArrow>
+ </controlProperties>
+ </control>
+ <control controlID="10" controlTypeID="com.balsamiq.mockups::Paragraph" x="886" y="154" w="221" h="140" measuredW="200" measuredH="140" zOrder="8" locked="false" isInGroup="-1">
+ <controlProperties>
+ <text>If%20an%20item%20%28contact%20or%20number%29%20can%20be%20called%20from%20more%20than%20one%20provider%2C%20show%20a%20popup%20menu%20with%20options.%0ABut%20show%20all%20contact/numbers%20in%20the%20initial%20popup</text>
+ </controlProperties>
+ </control>
+ <control controlID="11" controlTypeID="com.balsamiq.mockups::Arrow" x="247" y="227" w="150" h="10" measuredW="150" measuredH="100" zOrder="9" locked="false" isInGroup="-1">
+ <controlProperties>
+ <curvature>0</curvature>
+ <leftArrow>false</leftArrow>
+ </controlProperties>
+ </control>
+ <control controlID="12" controlTypeID="com.balsamiq.mockups::Label" x="100" y="210" w="-1" h="-1" measuredW="287" measuredH="25" zOrder="10" locked="false" isInGroup="-1">
+ <controlProperties>
+ <text>Current%20calls%20can%20be%20managed%20from%20popup%20too</text>
+ </controlProperties>
+ </control>
+ </controls>
+</mockup> \ No newline at end of file
diff --git a/Plugins/voiceservice/concepts/popup.png b/Plugins/voiceservice/concepts/popup.png
new file mode 100644
index 0000000..df1fca5
--- /dev/null
+++ b/Plugins/voiceservice/concepts/popup.png
Binary files differ
diff --git a/Plugins/voiceservice/frame.cpp b/Plugins/voiceservice/frame.cpp
new file mode 100644
index 0000000..6add8f3
--- /dev/null
+++ b/Plugins/voiceservice/frame.cpp
@@ -0,0 +1,829 @@
+/*
+Copyright (C) 2005 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#include "commons.h"
+
+
+// Prototypes /////////////////////////////////////////////////////////////////////////////////////
+
+
+HWND hwnd_frame = NULL;
+HWND hwnd_container = NULL;
+
+int frame_id = -1;
+
+static LRESULT CALLBACK FrameWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
+
+
+#define H_SPACE 2
+
+
+// Functions //////////////////////////////////////////////////////////////////////////////////////
+
+void InitFrames()
+{
+ if (ServiceExists(MS_CLIST_FRAMES_ADDFRAME))
+ {
+ hwnd_frame = CreateDialog(hInst, MAKEINTRESOURCE(IDD_CALLS),
+ (HWND) CallService(MS_CLUI_GETHWND, 0, 0), (DLGPROC) FrameWndProc);
+
+ CLISTFrame Frame = {0};
+ Frame.cbSize = sizeof(CLISTFrame);
+ Frame.tname = TranslateT("Voice Calls");
+ Frame.hWnd = hwnd_frame;
+ Frame.align = alBottom;
+ Frame.Flags = F_VISIBLE | F_NOBORDER | F_LOCKED | F_TCHAR;
+ Frame.height = 0;
+ Frame.hIcon = IcoLib_LoadIcon(mainIcons[0], TRUE);
+
+ frame_id = CallService(MS_CLIST_FRAMES_ADDFRAME, (WPARAM)&Frame, 0);
+ }
+}
+
+
+void DeInitFrames()
+{
+ if (ServiceExists(MS_CLIST_FRAMES_REMOVEFRAME) && frame_id != -1)
+ CallService(MS_CLIST_FRAMES_REMOVEFRAME, (WPARAM)frame_id, 0);
+
+ if (hwnd_frame != NULL)
+ DestroyWindow(hwnd_frame);
+}
+
+
+static int GetMaxLineHeight() {
+ return max(ICON_SIZE, font_max_height) + 1;
+}
+
+
+BOOL FrameIsFloating(int frame_id)
+{
+ if (frame_id == -1)
+ return TRUE; // no frames, always floating
+
+ return CallService(MS_CLIST_FRAMES_GETFRAMEOPTIONS, MAKEWPARAM(FO_FLOATING, frame_id), 0);
+}
+
+void ResizeFrame(int frame_id, HWND hwnd)
+{
+ int height = calls.getCount() * GetMaxLineHeight();
+ if (height > 0)
+ height += 2;
+
+ if (CanCallNumber())
+ {
+ height += 23 + 2;
+
+ if (SendMessage(GetDlgItem(hwnd, IDC_DIALPAD), BM_GETCHECK, 0, 0) == BST_CHECKED)
+ {
+ RECT first, last;
+ GetWindowRect(GetDlgItem(hwnd, IDC_1), &first);
+ GetWindowRect(GetDlgItem(hwnd, IDC_SHARP), &last);
+
+ height += last.bottom - first.top + 1;
+ }
+ }
+
+ if (FrameIsFloating(frame_id))
+ {
+ HWND parent = GetParent(hwnd);
+ if (parent == NULL)
+ return;
+
+ RECT r_client;
+ GetClientRect(hwnd, &r_client);
+
+ if (r_client.bottom - r_client.top == height)
+ return;
+
+ RECT parent_client, parent_window, r_window;
+ GetClientRect(parent, &parent_client);
+ GetWindowRect(parent, &parent_window);
+ GetWindowRect(hwnd, &r_window);
+
+ int diff = (parent_window.bottom - parent_window.top) - (parent_client.bottom - parent_client.top);
+ if(ServiceExists(MS_CLIST_FRAMES_ADDFRAME))
+ diff += (r_window.top - parent_window.top);
+
+ SetWindowPos(parent, 0, 0, 0, parent_window.right - parent_window.left, height + diff, SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE);
+ }
+ else
+ {
+ int old_height = CallService(MS_CLIST_FRAMES_GETFRAMEOPTIONS, MAKEWPARAM(FO_HEIGHT, frame_id), 0);
+ if (old_height == height)
+ return;
+
+ CallService(MS_CLIST_FRAMES_SETFRAMEOPTIONS, MAKEWPARAM(FO_HEIGHT, frame_id), (LPARAM) height);
+ CallService(MS_CLIST_FRAMES_UPDATEFRAME, (WPARAM)frame_id, (LPARAM)(FU_TBREDRAW | FU_FMREDRAW | FU_FMPOS));
+ }
+}
+
+void ShowFrame(int frame_id, HWND hwnd, int show)
+{
+ BOOL is_visible = IsWindowVisible(hwnd);
+ if ((is_visible && show == SW_SHOW) || (!is_visible && show == SW_HIDE))
+ return;
+
+ if (ServiceExists(MS_CLIST_FRAMES_SHFRAME) && frame_id != -1)
+ CallService(MS_CLIST_FRAMES_SHFRAME, (WPARAM)frame_id, 0);
+ else
+ ShowWindow(GetParent(hwnd), show);
+}
+
+
+static int dialCtrls[] = {
+ IDC_DIALPAD, IDC_NUMBER, IDC_CALL,
+ IDC_1, IDC_2, IDC_3,
+ IDC_4, IDC_5, IDC_6,
+ IDC_7, IDC_8, IDC_9,
+ IDC_AST, IDC_0, IDC_SHARP
+};
+
+
+
+static void InvalidateAll(HWND hwnd)
+{
+ InvalidateRect(GetDlgItem(hwnd, IDC_CALLS), NULL, FALSE);
+ for(int i = 0; i < MAX_REGS(dialCtrls); ++i)
+ InvalidateRect(GetDlgItem(hwnd, dialCtrls[i]), NULL, FALSE);
+ InvalidateRect(hwnd, NULL, FALSE);
+
+ if (frame_id != -1)
+ CallService(MS_CLIST_FRAMES_UPDATEFRAME, (WPARAM)frame_id, (LPARAM)(FU_FMREDRAW));
+}
+
+
+static void ShowHideDialpad(HWND hwnd)
+{
+ SendMessage(hwnd, WM_SETREDRAW, FALSE, 0);
+
+ if (!CanCallNumber())
+ {
+ for(int i = 0; i < MAX_REGS(dialCtrls); ++i)
+ ShowWindow(GetDlgItem(hwnd, dialCtrls[i]), SW_HIDE);
+ }
+ else
+ {
+ int i;
+ for(i = 0; i < 3; ++i)
+ ShowWindow(GetDlgItem(hwnd, dialCtrls[i]), SW_SHOW);
+
+ bool showDialpad = (SendMessage(GetDlgItem(hwnd, IDC_DIALPAD), BM_GETCHECK, 0, 0) == BST_CHECKED);
+
+ for(i = 3; i < MAX_REGS(dialCtrls); ++i)
+ ShowWindow(GetDlgItem(hwnd, dialCtrls[i]), showDialpad ? SW_SHOW : SW_HIDE);
+
+ VoiceCall *talking = NULL;
+ bool ringing = false;
+ bool calling = false;
+ for(i = 0; i < calls.getCount(); i++)
+ {
+ VoiceCall *call = &calls[i];
+ if (call->state == VOICE_STATE_TALKING)
+ talking = call;
+ else if (call->state == VOICE_STATE_CALLING)
+ calling = true;
+ else if (call->state == VOICE_STATE_RINGING)
+ ringing = true;
+ }
+
+ TCHAR number[1024];
+ GetDlgItemText(hwnd, IDC_NUMBER, number, MAX_REGS(number));
+ lstrtrim(number);
+
+ if (ringing && number[0] != 0)
+ {
+ SetWindowText(GetDlgItem(hwnd, IDC_NUMBER), _T(""));
+ number[0] = 0;
+ }
+
+ if (ringing || calling)
+ {
+ for(i = 0; i < MAX_REGS(dialCtrls); ++i)
+ EnableWindow(GetDlgItem(hwnd, dialCtrls[i]), FALSE);
+ }
+ else if (talking)
+ {
+ if (!showDialpad || !talking->CanSendDTMF())
+ {
+ for(i = 0; i < MAX_REGS(dialCtrls); ++i)
+ EnableWindow(GetDlgItem(hwnd, dialCtrls[i]), FALSE);
+
+ EnableWindow(GetDlgItem(hwnd, IDC_DIALPAD), TRUE);
+ }
+ else
+ {
+ for(i = 0; i < MAX_REGS(dialCtrls); ++i)
+ EnableWindow(GetDlgItem(hwnd, dialCtrls[i]), TRUE);
+
+ EnableWindow(GetDlgItem(hwnd, IDC_NUMBER), FALSE);
+ EnableWindow(GetDlgItem(hwnd, IDC_CALL), FALSE);
+ }
+ }
+ else
+ {
+ for(i = 0; i < MAX_REGS(dialCtrls); ++i)
+ EnableWindow(GetDlgItem(hwnd, dialCtrls[i]), TRUE);
+
+ EnableWindow(GetDlgItem(hwnd, IDC_CALL), CanCall(number));
+ }
+ }
+
+ SendMessage(hwnd, WM_SETREDRAW, TRUE, 0);
+
+ InvalidateAll(hwnd);
+}
+
+
+static int sttCompareProvidesByDescription(const VoiceProvider *p1, const VoiceProvider *p2)
+{
+ return lstrcmp(p2->description, p1->description);
+}
+
+
+static void DrawIconLib(HDC hDC, const RECT &rc, const char *icon)
+{
+ HICON hIcon = IcoLib_LoadIcon(icon);
+ if (hIcon == NULL)
+ return;
+
+ DrawIconEx(hDC, rc.left, (rc.top + rc.bottom - ICON_SIZE)/2, hIcon, ICON_SIZE, ICON_SIZE, 0, NULL, DI_NORMAL);
+ IcoLib_ReleaseIcon(hIcon);
+}
+
+
+static LRESULT CALLBACK FrameWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
+
+ switch(msg)
+ {
+ case WM_CREATE:
+ case WM_INITDIALOG:
+ {
+ SendDlgItemMessage(hwnd, IDC_DIALPAD, BUTTONSETASFLATBTN, TRUE, 0);
+ SendDlgItemMessage(hwnd, IDC_DIALPAD, BUTTONSETASPUSHBTN, TRUE, 0);
+ SendDlgItemMessageA(hwnd, IDC_DIALPAD, BUTTONADDTOOLTIP, (LPARAM) Translate("Show dialpad"), 0);
+ SendDlgItemMessage(hwnd, IDC_DIALPAD, BM_SETIMAGE, IMAGE_ICON, (LPARAM) IcoLib_LoadIcon("vc_dialpad", TRUE));
+
+ SendDlgItemMessage(hwnd, IDC_CALL, BUTTONSETASFLATBTN, TRUE, 0);
+ SendDlgItemMessageA(hwnd, IDC_CALL, BUTTONADDTOOLTIP, (LPARAM) Translate("Make call"), 0);
+ SendDlgItemMessage(hwnd, IDC_CALL, BM_SETIMAGE, IMAGE_ICON, (LPARAM) IcoLib_LoadIcon("vca_call", TRUE));
+
+ PostMessage(hwnd, WMU_RESIZE_FRAME, 0, 1);
+ break;
+ }
+
+ case WM_DESTROY:
+ {
+ break;
+ }
+
+ case WM_SIZE:
+ {
+ SendMessage(hwnd, WM_SETREDRAW, FALSE, 0);
+
+ RECT rc;
+ GetClientRect(hwnd, &rc);
+
+ int width = rc.right - rc.left;
+ int height = rc.bottom - rc.top;
+
+ if (CanCallNumber())
+ {
+ bool showDialpad = (SendMessage(GetDlgItem(hwnd, IDC_DIALPAD), BM_GETCHECK, 0, 0) == BST_CHECKED);
+
+ RECT rc;
+ GetWindowRect(hwnd, &rc);
+
+ RECT first = {0}, last = {0};
+ GetWindowRect(GetDlgItem(hwnd, IDC_1), &first);
+ GetWindowRect(GetDlgItem(hwnd, IDC_SHARP), &last);
+
+ int dialpad_height = last.bottom - first.top;
+ int dialpad_width = last.right - first.left;
+
+
+ int call_height = 23;
+ int call_width = 25;
+ int top = height - call_height - 1;
+
+ if (showDialpad)
+ top -= dialpad_height + 1;
+
+ MoveWindow(GetDlgItem(hwnd, IDC_DIALPAD), 1, top, call_width -2, call_height, FALSE);
+ MoveWindow(GetDlgItem(hwnd, IDC_NUMBER), call_width, top, width - 2 * call_width, call_height, FALSE);
+ MoveWindow(GetDlgItem(hwnd, IDC_CALL), width - call_width, top, call_width, call_height +1, FALSE);
+
+
+ int dialpad_top = top + call_height + 1;
+ int dialpad_left = ((rc.right - rc.left) - dialpad_width) / 2;
+ int deltaX = dialpad_left - first.left;
+ int deltaY = dialpad_top - first.top;
+ for(int i = 3; i < MAX_REGS(dialCtrls); ++i)
+ {
+ GetWindowRect(GetDlgItem(hwnd, dialCtrls[i]), &rc);
+ MoveWindow(GetDlgItem(hwnd, dialCtrls[i]), rc.left + deltaX, rc.top + deltaY,
+ rc.right - rc.left, rc.bottom - rc.top, FALSE);
+ }
+
+
+ height -= call_height + 2;
+ if (showDialpad)
+ height -= dialpad_height + 1;
+ }
+
+ if (height <= 2)
+ {
+ ShowWindow(GetDlgItem(hwnd, IDC_CALLS), SW_HIDE);
+ }
+ else
+ {
+ MoveWindow(GetDlgItem(hwnd, IDC_CALLS), 1, 1, width-2, height-2, FALSE);
+ ShowWindow(GetDlgItem(hwnd, IDC_CALLS), SW_SHOW);
+ }
+
+ SendMessage(hwnd, WM_SETREDRAW, TRUE, 0);
+
+ InvalidateAll(hwnd);
+ break;
+ }
+
+ case WMU_REFRESH:
+ {
+ HWND list = GetDlgItem(hwnd, IDC_CALLS);
+
+ SendMessage(list, WM_SETREDRAW, FALSE, 0);
+ SendMessage(list, LB_RESETCONTENT, 0, 0);
+ for(int i = 0; i < calls.getCount(); i++)
+ {
+ VoiceCall *call = &calls[i];
+
+ TCHAR text[512];
+ mir_sntprintf(text, MAX_REGS(text), _T("%d %s"), call->state, call->displayName);
+
+ int pos = SendMessage(list, LB_ADDSTRING, 0, (LPARAM) text);
+ if (pos == LB_ERR)
+ // TODO Show error
+ continue;
+
+ SendMessage(list, LB_SETITEMDATA, pos, (LPARAM) call);
+ }
+ SendMessage(list, WM_SETREDRAW, TRUE, 0);
+
+ // Fall throught
+ }
+
+ case WMU_RESIZE_FRAME:
+ {
+ ShowHideDialpad(hwnd);
+
+ if (lParam)
+ SendMessage(hwnd, WM_SIZE, 0, 0);
+
+ if (opts.resize_frame)
+ {
+ if (calls.getCount() == 0 && !CanCallNumber())
+ {
+ ShowFrame(frame_id, hwnd, SW_HIDE);
+ }
+ else
+ {
+ ResizeFrame(frame_id, hwnd);
+ ShowFrame(frame_id, hwnd, SW_SHOW);
+ }
+ }
+
+ break;
+ }
+
+ case WM_COMMAND:
+ {
+ switch(LOWORD(wParam))
+ {
+ case IDC_CALL:
+ {
+ TCHAR number[1024];
+ GetDlgItemText(hwnd, IDC_NUMBER, number, MAX_REGS(number));
+ lstrtrim(number);
+
+ LIST<VoiceProvider> candidates(10, &sttCompareProvidesByDescription);
+
+ for(int i = 0; i < modules.getCount(); i++)
+ {
+ if (!modules[i].CanCall(number))
+ continue;
+
+ candidates.insert(&modules[i]);
+ }
+
+ int selected;
+
+ if (candidates.getCount() < 1)
+ {
+ break;
+ }
+ else if (candidates.getCount() == 1)
+ {
+ selected = 0;
+ }
+ else
+ {
+ HMENU menu = CreatePopupMenu();
+
+ for (int i = 0; i < candidates.getCount(); ++i)
+ {
+ TCHAR text[1024];
+ mir_sntprintf(text, MAX_REGS(text), TranslateT("Call with %s"),
+ candidates[i]->description);
+
+ MENUITEMINFO mii = {0};
+ mii.cbSize = sizeof(mii);
+ mii.fMask = MIIM_ID | MIIM_TYPE;
+ mii.fType = MFT_STRING;
+ mii.dwTypeData = text;
+ mii.cch = lstrlen(text);
+ mii.wID = i + 1;
+
+ // TODO: Add icon to menu
+
+ InsertMenuItem(menu, 0, TRUE, &mii);
+ }
+
+ RECT rc;
+ GetWindowRect(GetDlgItem(hwnd, IDC_CALL), &rc);
+
+ POINT p;
+ p.x = rc.right;
+ p.y = rc.bottom+1;
+
+ selected = TrackPopupMenu(menu, TPM_TOPALIGN|TPM_RIGHTBUTTON|TPM_RETURNCMD|TPM_RIGHTALIGN,
+ p.x, p.y, 0, hwnd, NULL);
+
+ DestroyMenu(menu);
+
+ if (selected == 0)
+ break;
+
+ selected--;
+ }
+
+ candidates[selected]->Call(NULL, number);
+
+ break;
+ }
+ case IDC_NUMBER:
+ {
+ if (HIWORD(wParam) == EN_CHANGE)
+ {
+ ShowHideDialpad(hwnd);
+ }
+ break;
+ }
+ case IDC_DIALPAD:
+ {
+ ShowHideDialpad(hwnd);
+ SendMessage(hwnd, WMU_RESIZE_FRAME, 0, 0);
+ break;
+ }
+ case IDC_1:
+ case IDC_2:
+ case IDC_3:
+ case IDC_4:
+ case IDC_5:
+ case IDC_6:
+ case IDC_7:
+ case IDC_8:
+ case IDC_9:
+ case IDC_AST:
+ case IDC_0:
+ case IDC_SHARP:
+ {
+ TCHAR text[2];
+ switch(LOWORD(wParam))
+ {
+ case IDC_1: text[0] = _T('1'); break;
+ case IDC_2: text[0] = _T('2'); break;
+ case IDC_3: text[0] = _T('3'); break;
+ case IDC_4: text[0] = _T('4'); break;
+ case IDC_5: text[0] = _T('5'); break;
+ case IDC_6: text[0] = _T('6'); break;
+ case IDC_7: text[0] = _T('7'); break;
+ case IDC_8: text[0] = _T('8'); break;
+ case IDC_9: text[0] = _T('9'); break;
+ case IDC_AST: text[0] = _T('*'); break;
+ case IDC_0: text[0] = _T('0'); break;
+ case IDC_SHARP: text[0] = _T('#'); break;
+ }
+ text[1] = 0;
+
+ SkinPlaySound("voice_dialpad");
+
+ VoiceCall *call = GetTalkingCall();
+ if (call == NULL)
+ {
+ SendMessage(GetDlgItem(hwnd, IDC_NUMBER), EM_REPLACESEL, TRUE, (LPARAM) text);
+ }
+ else
+ {
+ TCHAR tmp[1024];
+
+ GetWindowText(GetDlgItem(hwnd, IDC_NUMBER), tmp, MAX_REGS(tmp));
+
+ tmp[MAX_REGS(tmp)-2] = 0;
+ lstrcat(tmp, text);
+
+ SetWindowText(GetDlgItem(hwnd, IDC_NUMBER), tmp);
+
+ call->SendDTMF(text[0]);
+ }
+ break;
+ }
+ case IDC_CALLS:
+ {
+ if (HIWORD(wParam) != LBN_SELCHANGE)
+ break;
+
+ HWND list = GetDlgItem(hwnd, IDC_CALLS);
+
+ int pos = SendMessage(list, LB_GETCURSEL, 0, 0);
+ if (pos == LB_ERR)
+ break;
+
+ POINT p;
+ GetCursorPos(&p);
+ ScreenToClient(list, &p);
+
+ int ret = SendMessage(list, LB_ITEMFROMPOINT, 0, MAKELONG(p.x, p.y));
+ if (HIWORD(ret))
+ break;
+ if (pos != LOWORD(ret))
+ break;
+
+ RECT rc;
+ SendMessage(list, LB_GETITEMRECT, pos, (LPARAM) &rc);
+ int x = rc.right - p.x;
+
+ int action;
+ if (x >= H_SPACE && x <= ICON_SIZE + H_SPACE)
+ action = 2;
+ else if (x >= ICON_SIZE + 2 * H_SPACE && x <= 2 * (ICON_SIZE + H_SPACE))
+ action = 1;
+ else
+ break;
+
+ VoiceCall *call = (VoiceCall *) SendMessage(list, LB_GETITEMDATA, pos, 0);
+ switch (call->state)
+ {
+ case VOICE_STATE_TALKING:
+ {
+ if (action == 1)
+ call->Hold();
+ else
+ call->Drop();
+ break;
+ }
+ case VOICE_STATE_RINGING:
+ case VOICE_STATE_ON_HOLD:
+ {
+ if (action == 1)
+ Answer(call);
+ else
+ call->Drop();
+ break;
+ }
+ case VOICE_STATE_CALLING:
+ {
+ if (action == 2)
+ call->Drop();
+ break;
+ }
+ }
+ break;
+ }
+ }
+
+ break;
+ }
+
+ case WM_CONTEXTMENU:
+ {
+ HWND list = GetDlgItem(hwnd, IDC_CALLS);
+ if ((HANDLE) wParam != list)
+ break;
+
+ POINT p;
+ p.x = LOWORD(lParam);
+ p.y = HIWORD(lParam);
+ ScreenToClient(list, &p);
+
+ int pos = SendMessage(list, LB_ITEMFROMPOINT, 0, MAKELONG(p.x, p.y));
+ if (HIWORD(pos))
+ break;
+ pos = LOWORD(pos);
+
+ if (pos >= calls.getCount())
+ break;
+
+ if (IsFinalState(calls[pos].state))
+ break;
+
+ // Just to get things strait
+ SendMessage(list, LB_SETCURSEL, pos, 0);
+
+ HMENU menu = LoadMenu(hInst, MAKEINTRESOURCE(IDR_MENUS));
+ HMENU submenu = GetSubMenu(menu, 0);
+ CallService(MS_LANGPACK_TRANSLATEMENU, (WPARAM)submenu, 0);
+
+ switch (calls[pos].state)
+ {
+ case VOICE_STATE_CALLING:
+ {
+ DeleteMenu(menu, ID_FRAMEPOPUP_ANSWERCALL, MF_BYCOMMAND);
+ DeleteMenu(menu, ID_FRAMEPOPUP_HOLDCALL, MF_BYCOMMAND);
+ break;
+ }
+ case VOICE_STATE_TALKING:
+ {
+ DeleteMenu(menu, ID_FRAMEPOPUP_ANSWERCALL, MF_BYCOMMAND);
+ if (!calls[pos].module->CanHold())
+ DeleteMenu(menu, ID_FRAMEPOPUP_HOLDCALL, MF_BYCOMMAND);
+ break;
+ }
+ }
+
+ p.x = LOWORD(lParam);
+ p.y = HIWORD(lParam);
+ int ret = TrackPopupMenu(submenu, TPM_TOPALIGN|TPM_LEFTALIGN|TPM_RIGHTBUTTON|TPM_RETURNCMD, p.x, p.y, 0, hwnd, NULL);
+ DestroyMenu(menu);
+
+ switch(ret)
+ {
+ case ID_FRAMEPOPUP_DROPCALL:
+ {
+ calls[pos].Drop();
+ break;
+ }
+ case ID_FRAMEPOPUP_ANSWERCALL:
+ {
+ Answer(&calls[pos]);
+ break;
+ }
+ case ID_FRAMEPOPUP_HOLDCALL:
+ {
+ calls[pos].Hold();
+ break;
+ }
+ }
+
+ break;
+ }
+
+ case WM_MEASUREITEM:
+ {
+ LPMEASUREITEMSTRUCT mis = (LPMEASUREITEMSTRUCT) lParam;
+ if (mis->CtlID != IDC_CALLS)
+ break;
+
+ mis->itemHeight = GetMaxLineHeight();
+
+ return TRUE;
+ }
+
+ case WM_CTLCOLORLISTBOX:
+ {
+ return (LRESULT) bk_brush;
+ }
+
+ case WM_DRAWITEM:
+ {
+ DRAWITEMSTRUCT *dis = (DRAWITEMSTRUCT *)lParam;
+
+ if (dis->CtlID != IDC_CALLS || dis->itemID == -1)
+ break;
+
+ VoiceCall *call = (VoiceCall *) dis->itemData;
+ if (call == NULL)
+ break;
+
+ RECT rc = dis->rcItem;
+
+ FillRect(dis->hDC, &rc, bk_brush);
+
+ rc.left += H_SPACE;
+ rc.right -= H_SPACE;
+ rc.bottom --;
+
+ int old_bk_mode = SetBkMode(dis->hDC, TRANSPARENT);
+
+ // Draw status
+ DrawIconLib(dis->hDC, rc, stateIcons[call->state]);
+
+ if (call->secure)
+ DrawIconLib(dis->hDC, rc, "vc_secure");
+
+ // Draw voice provider icon
+ rc.left += ICON_SIZE + H_SPACE;
+
+ HICON hIcon = call->module->GetIcon();
+ if (hIcon != NULL)
+ {
+ DrawIconEx(dis->hDC, rc.left, (rc.top + rc.bottom - ICON_SIZE)/2, hIcon, ICON_SIZE, ICON_SIZE, 0, NULL, DI_NORMAL);
+ call->module->ReleaseIcon(hIcon);
+ }
+
+ // Draw contact
+ rc.left += ICON_SIZE + H_SPACE;
+
+ int numIcons = 0;
+ switch (call->state)
+ {
+ case VOICE_STATE_CALLING:
+ numIcons = 1;
+ break;
+ case VOICE_STATE_TALKING:
+ if (call->module->CanHold())
+ numIcons = 2;
+ else
+ numIcons = 1;
+ break;
+ case VOICE_STATE_RINGING:
+ case VOICE_STATE_ON_HOLD:
+ numIcons = 2;
+ break;
+ }
+
+ rc.right -= numIcons * (ICON_SIZE + H_SPACE);
+
+ HFONT old_font = (HFONT) SelectObject(dis->hDC, fonts[call->state]);
+ COLORREF old_color = SetTextColor(dis->hDC, font_colors[call->state]);
+
+ DrawText(dis->hDC, call->displayName, -1, &rc, DT_SINGLELINE | DT_NOPREFIX | DT_END_ELLIPSIS | DT_VCENTER);
+
+ SelectObject(dis->hDC, old_font);
+ SetTextColor(dis->hDC, old_color);
+
+ // Draw action icons
+ rc = dis->rcItem;
+ rc.right -= H_SPACE;
+ rc.bottom --;
+
+ switch (call->state)
+ {
+ case VOICE_STATE_CALLING:
+ {
+ rc.left = rc.right - ICON_SIZE;
+ DrawIconLib(dis->hDC, rc, actionIcons[ACTION_DROP]);
+ break;
+ }
+ case VOICE_STATE_TALKING:
+ {
+ rc.left = rc.right - ICON_SIZE;
+ DrawIconLib(dis->hDC, rc, actionIcons[ACTION_DROP]);
+
+ if (call->module->CanHold())
+ {
+ rc.right -= ICON_SIZE + H_SPACE;
+ rc.left = rc.right - ICON_SIZE;
+ DrawIconLib(dis->hDC, rc, actionIcons[ACTION_HOLD]);
+ }
+
+ break;
+ }
+ case VOICE_STATE_RINGING:
+ case VOICE_STATE_ON_HOLD:
+ {
+ rc.left = rc.right - ICON_SIZE;
+ DrawIconLib(dis->hDC, rc, actionIcons[ACTION_DROP]);
+
+ rc.right -= ICON_SIZE + H_SPACE;
+ rc.left = rc.right - ICON_SIZE;
+ DrawIconLib(dis->hDC, rc, actionIcons[ACTION_ANSWER]);
+
+ break;
+ }
+ }
+
+ SetBkMode(dis->hDC, old_bk_mode);
+ return TRUE;
+ }
+
+ }
+ return DefWindowProc(hwnd, msg, wParam, lParam);
+}
+
diff --git a/Plugins/voiceservice/frame.h b/Plugins/voiceservice/frame.h
new file mode 100644
index 0000000..c0221cd
--- /dev/null
+++ b/Plugins/voiceservice/frame.h
@@ -0,0 +1,36 @@
+/*
+Copyright (C) 2006 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#ifndef __FRAME_H__
+# define __FRAME_H__
+
+
+void InitFrames();
+void DeInitFrames();
+
+
+extern HWND hwnd_frame;
+
+
+#define WMU_REFRESH (WM_USER + 25)
+#define WMU_RESIZE_FRAME (WM_USER + 26)
+
+
+#endif // __FRAME_H__ \ No newline at end of file
diff --git a/Plugins/voiceservice/lib/portaudio/Debug/libportaudio.lib b/Plugins/voiceservice/lib/portaudio/Debug/libportaudio.lib
new file mode 100644
index 0000000..461b78d
--- /dev/null
+++ b/Plugins/voiceservice/lib/portaudio/Debug/libportaudio.lib
Binary files differ
diff --git a/Plugins/voiceservice/lib/portaudio/LICENSE.txt b/Plugins/voiceservice/lib/portaudio/LICENSE.txt
new file mode 100644
index 0000000..e0ac4e8
--- /dev/null
+++ b/Plugins/voiceservice/lib/portaudio/LICENSE.txt
@@ -0,0 +1,81 @@
+Portable header file to contain:
+>>>>>
+/*
+ * PortAudio Portable Real-Time Audio Library
+ * PortAudio API Header File
+ * Latest version available at: http://www.portaudio.com
+ *
+ * Copyright (c) 1999-2006 Ross Bencina and Phil Burk
+ *
+ * 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.
+ */
+
+/*
+ * The text above constitutes the entire PortAudio license; however,
+ * the PortAudio community also makes the following non-binding requests:
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version. It is also
+ * requested that these non-binding requests be included along with the
+ * license above.
+ */
+<<<<<
+
+
+Implementation files to contain:
+>>>>>
+/*
+ * PortAudio Portable Real-Time Audio Library
+ * Latest version at: http://www.portaudio.com
+ * <platform> Implementation
+ * Copyright (c) 1999-2000 <author(s)>
+ *
+ * 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.
+ */
+
+/*
+ * The text above constitutes the entire PortAudio license; however,
+ * the PortAudio community also makes the following non-binding requests:
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version. It is also
+ * requested that these non-binding requests be included along with the
+ * license above.
+ */
+<<<<< \ No newline at end of file
diff --git a/Plugins/voiceservice/lib/portaudio/Release/libportaudio.lib b/Plugins/voiceservice/lib/portaudio/Release/libportaudio.lib
new file mode 100644
index 0000000..88ccf54
--- /dev/null
+++ b/Plugins/voiceservice/lib/portaudio/Release/libportaudio.lib
Binary files differ
diff --git a/Plugins/voiceservice/lib/portaudio/portaudio.h b/Plugins/voiceservice/lib/portaudio/portaudio.h
new file mode 100644
index 0000000..89964ab
--- /dev/null
+++ b/Plugins/voiceservice/lib/portaudio/portaudio.h
@@ -0,0 +1,1134 @@
+#ifndef PORTAUDIO_H
+#define PORTAUDIO_H
+/*
+ * $Id: portaudio.h 1337 2008-02-15 07:32:09Z rossb $
+ * PortAudio Portable Real-Time Audio Library
+ * PortAudio API Header File
+ * Latest version available at: http://www.portaudio.com/
+ *
+ * Copyright (c) 1999-2002 Ross Bencina and Phil Burk
+ *
+ * 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.
+ */
+
+/*
+ * The text above constitutes the entire PortAudio license; however,
+ * the PortAudio community also makes the following non-binding requests:
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version. It is also
+ * requested that these non-binding requests be included along with the
+ * license above.
+ */
+
+/** @file
+ @ingroup public_header
+ @brief The portable PortAudio API.
+*/
+
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+
+/** Retrieve the release number of the currently running PortAudio build,
+ eg 1900.
+*/
+int Pa_GetVersion( void );
+
+
+/** Retrieve a textual description of the current PortAudio build,
+ eg "PortAudio V19-devel 13 October 2002".
+*/
+const char* Pa_GetVersionText( void );
+
+
+/** Error codes returned by PortAudio functions.
+ Note that with the exception of paNoError, all PaErrorCodes are negative.
+*/
+
+typedef int PaError;
+typedef enum PaErrorCode
+{
+ paNoError = 0,
+
+ paNotInitialized = -10000,
+ paUnanticipatedHostError,
+ paInvalidChannelCount,
+ paInvalidSampleRate,
+ paInvalidDevice,
+ paInvalidFlag,
+ paSampleFormatNotSupported,
+ paBadIODeviceCombination,
+ paInsufficientMemory,
+ paBufferTooBig,
+ paBufferTooSmall,
+ paNullCallback,
+ paBadStreamPtr,
+ paTimedOut,
+ paInternalError,
+ paDeviceUnavailable,
+ paIncompatibleHostApiSpecificStreamInfo,
+ paStreamIsStopped,
+ paStreamIsNotStopped,
+ paInputOverflowed,
+ paOutputUnderflowed,
+ paHostApiNotFound,
+ paInvalidHostApi,
+ paCanNotReadFromACallbackStream, /**< @todo review error code name */
+ paCanNotWriteToACallbackStream, /**< @todo review error code name */
+ paCanNotReadFromAnOutputOnlyStream, /**< @todo review error code name */
+ paCanNotWriteToAnInputOnlyStream, /**< @todo review error code name */
+ paIncompatibleStreamHostApi,
+ paBadBufferPtr
+} PaErrorCode;
+
+
+/** Translate the supplied PortAudio error code into a human readable
+ message.
+*/
+const char *Pa_GetErrorText( PaError errorCode );
+
+
+/** Library initialization function - call this before using PortAudio.
+ This function initialises internal data structures and prepares underlying
+ host APIs for use. With the exception of Pa_GetVersion(), Pa_GetVersionText(),
+ and Pa_GetErrorText(), this function MUST be called before using any other
+ PortAudio API functions.
+
+ If Pa_Initialize() is called multiple times, each successful
+ call must be matched with a corresponding call to Pa_Terminate().
+ Pairs of calls to Pa_Initialize()/Pa_Terminate() may overlap, and are not
+ required to be fully nested.
+
+ Note that if Pa_Initialize() returns an error code, Pa_Terminate() should
+ NOT be called.
+
+ @return paNoError if successful, otherwise an error code indicating the cause
+ of failure.
+
+ @see Pa_Terminate
+*/
+PaError Pa_Initialize( void );
+
+
+/** Library termination function - call this when finished using PortAudio.
+ This function deallocates all resources allocated by PortAudio since it was
+ initializied by a call to Pa_Initialize(). In cases where Pa_Initialise() has
+ been called multiple times, each call must be matched with a corresponding call
+ to Pa_Terminate(). The final matching call to Pa_Terminate() will automatically
+ close any PortAudio streams that are still open.
+
+ Pa_Terminate() MUST be called before exiting a program which uses PortAudio.
+ Failure to do so may result in serious resource leaks, such as audio devices
+ not being available until the next reboot.
+
+ @return paNoError if successful, otherwise an error code indicating the cause
+ of failure.
+
+ @see Pa_Initialize
+*/
+PaError Pa_Terminate( void );
+
+
+
+/** The type used to refer to audio devices. Values of this type usually
+ range from 0 to (Pa_GetDeviceCount()-1), and may also take on the PaNoDevice
+ and paUseHostApiSpecificDeviceSpecification values.
+
+ @see Pa_GetDeviceCount, paNoDevice, paUseHostApiSpecificDeviceSpecification
+*/
+typedef int PaDeviceIndex;
+
+
+/** A special PaDeviceIndex value indicating that no device is available,
+ or should be used.
+
+ @see PaDeviceIndex
+*/
+#define paNoDevice ((PaDeviceIndex)-1)
+
+
+/** A special PaDeviceIndex value indicating that the device(s) to be used
+ are specified in the host api specific stream info structure.
+
+ @see PaDeviceIndex
+*/
+#define paUseHostApiSpecificDeviceSpecification ((PaDeviceIndex)-2)
+
+
+/* Host API enumeration mechanism */
+
+/** The type used to enumerate to host APIs at runtime. Values of this type
+ range from 0 to (Pa_GetHostApiCount()-1).
+
+ @see Pa_GetHostApiCount
+*/
+typedef int PaHostApiIndex;
+
+
+/** Retrieve the number of available host APIs. Even if a host API is
+ available it may have no devices available.
+
+ @return A non-negative value indicating the number of available host APIs
+ or, a PaErrorCode (which are always negative) if PortAudio is not initialized
+ or an error is encountered.
+
+ @see PaHostApiIndex
+*/
+PaHostApiIndex Pa_GetHostApiCount( void );
+
+
+/** Retrieve the index of the default host API. The default host API will be
+ the lowest common denominator host API on the current platform and is
+ unlikely to provide the best performance.
+
+ @return A non-negative value ranging from 0 to (Pa_GetHostApiCount()-1)
+ indicating the default host API index or, a PaErrorCode (which are always
+ negative) if PortAudio is not initialized or an error is encountered.
+*/
+PaHostApiIndex Pa_GetDefaultHostApi( void );
+
+
+/** Unchanging unique identifiers for each supported host API. This type
+ is used in the PaHostApiInfo structure. The values are guaranteed to be
+ unique and to never change, thus allowing code to be written that
+ conditionally uses host API specific extensions.
+
+ New type ids will be allocated when support for a host API reaches
+ "public alpha" status, prior to that developers should use the
+ paInDevelopment type id.
+
+ @see PaHostApiInfo
+*/
+typedef enum PaHostApiTypeId
+{
+ paInDevelopment=0, /* use while developing support for a new host API */
+ paDirectSound=1,
+ paMME=2,
+ paASIO=3,
+ paSoundManager=4,
+ paCoreAudio=5,
+ paOSS=7,
+ paALSA=8,
+ paAL=9,
+ paBeOS=10,
+ paWDMKS=11,
+ paJACK=12,
+ paWASAPI=13,
+ paAudioScienceHPI=14
+} PaHostApiTypeId;
+
+
+/** A structure containing information about a particular host API. */
+
+typedef struct PaHostApiInfo
+{
+ /** this is struct version 1 */
+ int structVersion;
+ /** The well known unique identifier of this host API @see PaHostApiTypeId */
+ PaHostApiTypeId type;
+ /** A textual description of the host API for display on user interfaces. */
+ const char *name;
+
+ /** The number of devices belonging to this host API. This field may be
+ used in conjunction with Pa_HostApiDeviceIndexToDeviceIndex() to enumerate
+ all devices for this host API.
+ @see Pa_HostApiDeviceIndexToDeviceIndex
+ */
+ int deviceCount;
+
+ /** The default input device for this host API. The value will be a
+ device index ranging from 0 to (Pa_GetDeviceCount()-1), or paNoDevice
+ if no default input device is available.
+ */
+ PaDeviceIndex defaultInputDevice;
+
+ /** The default output device for this host API. The value will be a
+ device index ranging from 0 to (Pa_GetDeviceCount()-1), or paNoDevice
+ if no default output device is available.
+ */
+ PaDeviceIndex defaultOutputDevice;
+
+} PaHostApiInfo;
+
+
+/** Retrieve a pointer to a structure containing information about a specific
+ host Api.
+
+ @param hostApi A valid host API index ranging from 0 to (Pa_GetHostApiCount()-1)
+
+ @return A pointer to an immutable PaHostApiInfo structure describing
+ a specific host API. If the hostApi parameter is out of range or an error
+ is encountered, the function returns NULL.
+
+ The returned structure is owned by the PortAudio implementation and must not
+ be manipulated or freed. The pointer is only guaranteed to be valid between
+ calls to Pa_Initialize() and Pa_Terminate().
+*/
+const PaHostApiInfo * Pa_GetHostApiInfo( PaHostApiIndex hostApi );
+
+
+/** Convert a static host API unique identifier, into a runtime
+ host API index.
+
+ @param type A unique host API identifier belonging to the PaHostApiTypeId
+ enumeration.
+
+ @return A valid PaHostApiIndex ranging from 0 to (Pa_GetHostApiCount()-1) or,
+ a PaErrorCode (which are always negative) if PortAudio is not initialized
+ or an error is encountered.
+
+ The paHostApiNotFound error code indicates that the host API specified by the
+ type parameter is not available.
+
+ @see PaHostApiTypeId
+*/
+PaHostApiIndex Pa_HostApiTypeIdToHostApiIndex( PaHostApiTypeId type );
+
+
+/** Convert a host-API-specific device index to standard PortAudio device index.
+ This function may be used in conjunction with the deviceCount field of
+ PaHostApiInfo to enumerate all devices for the specified host API.
+
+ @param hostApi A valid host API index ranging from 0 to (Pa_GetHostApiCount()-1)
+
+ @param hostApiDeviceIndex A valid per-host device index in the range
+ 0 to (Pa_GetHostApiInfo(hostApi)->deviceCount-1)
+
+ @return A non-negative PaDeviceIndex ranging from 0 to (Pa_GetDeviceCount()-1)
+ or, a PaErrorCode (which are always negative) if PortAudio is not initialized
+ or an error is encountered.
+
+ A paInvalidHostApi error code indicates that the host API index specified by
+ the hostApi parameter is out of range.
+
+ A paInvalidDevice error code indicates that the hostApiDeviceIndex parameter
+ is out of range.
+
+ @see PaHostApiInfo
+*/
+PaDeviceIndex Pa_HostApiDeviceIndexToDeviceIndex( PaHostApiIndex hostApi,
+ int hostApiDeviceIndex );
+
+
+
+/** Structure used to return information about a host error condition.
+*/
+typedef struct PaHostErrorInfo{
+ PaHostApiTypeId hostApiType; /**< the host API which returned the error code */
+ long errorCode; /**< the error code returned */
+ const char *errorText; /**< a textual description of the error if available, otherwise a zero-length string */
+}PaHostErrorInfo;
+
+
+/** Return information about the last host error encountered. The error
+ information returned by Pa_GetLastHostErrorInfo() will never be modified
+ asyncronously by errors occurring in other PortAudio owned threads
+ (such as the thread that manages the stream callback.)
+
+ This function is provided as a last resort, primarily to enhance debugging
+ by providing clients with access to all available error information.
+
+ @return A pointer to an immutable structure constaining information about
+ the host error. The values in this structure will only be valid if a
+ PortAudio function has previously returned the paUnanticipatedHostError
+ error code.
+*/
+const PaHostErrorInfo* Pa_GetLastHostErrorInfo( void );
+
+
+
+/* Device enumeration and capabilities */
+
+/** Retrieve the number of available devices. The number of available devices
+ may be zero.
+
+ @return A non-negative value indicating the number of available devices or,
+ a PaErrorCode (which are always negative) if PortAudio is not initialized
+ or an error is encountered.
+*/
+PaDeviceIndex Pa_GetDeviceCount( void );
+
+
+/** Retrieve the index of the default input device. The result can be
+ used in the inputDevice parameter to Pa_OpenStream().
+
+ @return The default input device index for the default host API, or paNoDevice
+ if no default input device is available or an error was encountered.
+*/
+PaDeviceIndex Pa_GetDefaultInputDevice( void );
+
+
+/** Retrieve the index of the default output device. The result can be
+ used in the outputDevice parameter to Pa_OpenStream().
+
+ @return The default output device index for the defualt host API, or paNoDevice
+ if no default output device is available or an error was encountered.
+
+ @note
+ On the PC, the user can specify a default device by
+ setting an environment variable. For example, to use device #1.
+<pre>
+ set PA_RECOMMENDED_OUTPUT_DEVICE=1
+</pre>
+ The user should first determine the available device ids by using
+ the supplied application "pa_devs".
+*/
+PaDeviceIndex Pa_GetDefaultOutputDevice( void );
+
+
+/** The type used to represent monotonic time in seconds that can be used
+ for syncronisation. The type is used for the outTime argument to the
+ PaStreamCallback and as the result of Pa_GetStreamTime().
+
+ @see PaStreamCallback, Pa_GetStreamTime
+*/
+typedef double PaTime;
+
+
+/** A type used to specify one or more sample formats. Each value indicates
+ a possible format for sound data passed to and from the stream callback,
+ Pa_ReadStream and Pa_WriteStream.
+
+ The standard formats paFloat32, paInt16, paInt32, paInt24, paInt8
+ and aUInt8 are usually implemented by all implementations.
+
+ The floating point representation (paFloat32) uses +1.0 and -1.0 as the
+ maximum and minimum respectively.
+
+ paUInt8 is an unsigned 8 bit format where 128 is considered "ground"
+
+ The paNonInterleaved flag indicates that a multichannel buffer is passed
+ as a set of non-interleaved pointers.
+
+ @see Pa_OpenStream, Pa_OpenDefaultStream, PaDeviceInfo
+ @see paFloat32, paInt16, paInt32, paInt24, paInt8
+ @see paUInt8, paCustomFormat, paNonInterleaved
+*/
+typedef unsigned long PaSampleFormat;
+
+
+#define paFloat32 ((PaSampleFormat) 0x00000001) /**< @see PaSampleFormat */
+#define paInt32 ((PaSampleFormat) 0x00000002) /**< @see PaSampleFormat */
+#define paInt24 ((PaSampleFormat) 0x00000004) /**< Packed 24 bit format. @see PaSampleFormat */
+#define paInt16 ((PaSampleFormat) 0x00000008) /**< @see PaSampleFormat */
+#define paInt8 ((PaSampleFormat) 0x00000010) /**< @see PaSampleFormat */
+#define paUInt8 ((PaSampleFormat) 0x00000020) /**< @see PaSampleFormat */
+#define paCustomFormat ((PaSampleFormat) 0x00010000)/**< @see PaSampleFormat */
+
+#define paNonInterleaved ((PaSampleFormat) 0x80000000)
+
+/** A structure providing information and capabilities of PortAudio devices.
+ Devices may support input, output or both input and output.
+*/
+typedef struct PaDeviceInfo
+{
+ int structVersion; /* this is struct version 2 */
+ const char *name;
+ PaHostApiIndex hostApi; /* note this is a host API index, not a type id*/
+
+ int maxInputChannels;
+ int maxOutputChannels;
+
+ /* Default latency values for interactive performance. */
+ PaTime defaultLowInputLatency;
+ PaTime defaultLowOutputLatency;
+ /* Default latency values for robust non-interactive applications (eg. playing sound files). */
+ PaTime defaultHighInputLatency;
+ PaTime defaultHighOutputLatency;
+
+ double defaultSampleRate;
+} PaDeviceInfo;
+
+
+/** Retrieve a pointer to a PaDeviceInfo structure containing information
+ about the specified device.
+ @return A pointer to an immutable PaDeviceInfo structure. If the device
+ parameter is out of range the function returns NULL.
+
+ @param device A valid device index in the range 0 to (Pa_GetDeviceCount()-1)
+
+ @note PortAudio manages the memory referenced by the returned pointer,
+ the client must not manipulate or free the memory. The pointer is only
+ guaranteed to be valid between calls to Pa_Initialize() and Pa_Terminate().
+
+ @see PaDeviceInfo, PaDeviceIndex
+*/
+const PaDeviceInfo* Pa_GetDeviceInfo( PaDeviceIndex device );
+
+
+/** Parameters for one direction (input or output) of a stream.
+*/
+typedef struct PaStreamParameters
+{
+ /** A valid device index in the range 0 to (Pa_GetDeviceCount()-1)
+ specifying the device to be used or the special constant
+ paUseHostApiSpecificDeviceSpecification which indicates that the actual
+ device(s) to use are specified in hostApiSpecificStreamInfo.
+ This field must not be set to paNoDevice.
+ */
+ PaDeviceIndex device;
+
+ /** The number of channels of sound to be delivered to the
+ stream callback or accessed by Pa_ReadStream() or Pa_WriteStream().
+ It can range from 1 to the value of maxInputChannels in the
+ PaDeviceInfo record for the device specified by the device parameter.
+ */
+ int channelCount;
+
+ /** The sample format of the buffer provided to the stream callback,
+ a_ReadStream() or Pa_WriteStream(). It may be any of the formats described
+ by the PaSampleFormat enumeration.
+ */
+ PaSampleFormat sampleFormat;
+
+ /** The desired latency in seconds. Where practical, implementations should
+ configure their latency based on these parameters, otherwise they may
+ choose the closest viable latency instead. Unless the suggested latency
+ is greater than the absolute upper limit for the device implementations
+ should round the suggestedLatency up to the next practial value - ie to
+ provide an equal or higher latency than suggestedLatency wherever possibe.
+ Actual latency values for an open stream may be retrieved using the
+ inputLatency and outputLatency fields of the PaStreamInfo structure
+ returned by Pa_GetStreamInfo().
+ @see default*Latency in PaDeviceInfo, *Latency in PaStreamInfo
+ */
+ PaTime suggestedLatency;
+
+ /** An optional pointer to a host api specific data structure
+ containing additional information for device setup and/or stream processing.
+ hostApiSpecificStreamInfo is never required for correct operation,
+ if not used it should be set to NULL.
+ */
+ void *hostApiSpecificStreamInfo;
+
+} PaStreamParameters;
+
+
+/** Return code for Pa_IsFormatSupported indicating success. */
+#define paFormatIsSupported (0)
+
+/** Determine whether it would be possible to open a stream with the specified
+ parameters.
+
+ @param inputParameters A structure that describes the input parameters used to
+ open a stream. The suggestedLatency field is ignored. See PaStreamParameters
+ for a description of these parameters. inputParameters must be NULL for
+ output-only streams.
+
+ @param outputParameters A structure that describes the output parameters used
+ to open a stream. The suggestedLatency field is ignored. See PaStreamParameters
+ for a description of these parameters. outputParameters must be NULL for
+ input-only streams.
+
+ @param sampleRate The required sampleRate. For full-duplex streams it is the
+ sample rate for both input and output
+
+ @return Returns 0 if the format is supported, and an error code indicating why
+ the format is not supported otherwise. The constant paFormatIsSupported is
+ provided to compare with the return value for success.
+
+ @see paFormatIsSupported, PaStreamParameters
+*/
+PaError Pa_IsFormatSupported( const PaStreamParameters *inputParameters,
+ const PaStreamParameters *outputParameters,
+ double sampleRate );
+
+
+
+/* Streaming types and functions */
+
+
+/**
+ A single PaStream can provide multiple channels of real-time
+ streaming audio input and output to a client application. A stream
+ provides access to audio hardware represented by one or more
+ PaDevices. Depending on the underlying Host API, it may be possible
+ to open multiple streams using the same device, however this behavior
+ is implementation defined. Portable applications should assume that
+ a PaDevice may be simultaneously used by at most one PaStream.
+
+ Pointers to PaStream objects are passed between PortAudio functions that
+ operate on streams.
+
+ @see Pa_OpenStream, Pa_OpenDefaultStream, Pa_OpenDefaultStream, Pa_CloseStream,
+ Pa_StartStream, Pa_StopStream, Pa_AbortStream, Pa_IsStreamActive,
+ Pa_GetStreamTime, Pa_GetStreamCpuLoad
+
+*/
+typedef void PaStream;
+
+
+/** Can be passed as the framesPerBuffer parameter to Pa_OpenStream()
+ or Pa_OpenDefaultStream() to indicate that the stream callback will
+ accept buffers of any size.
+*/
+#define paFramesPerBufferUnspecified (0)
+
+
+/** Flags used to control the behavior of a stream. They are passed as
+ parameters to Pa_OpenStream or Pa_OpenDefaultStream. Multiple flags may be
+ ORed together.
+
+ @see Pa_OpenStream, Pa_OpenDefaultStream
+ @see paNoFlag, paClipOff, paDitherOff, paNeverDropInput,
+ paPrimeOutputBuffersUsingStreamCallback, paPlatformSpecificFlags
+*/
+typedef unsigned long PaStreamFlags;
+
+/** @see PaStreamFlags */
+#define paNoFlag ((PaStreamFlags) 0)
+
+/** Disable default clipping of out of range samples.
+ @see PaStreamFlags
+*/
+#define paClipOff ((PaStreamFlags) 0x00000001)
+
+/** Disable default dithering.
+ @see PaStreamFlags
+*/
+#define paDitherOff ((PaStreamFlags) 0x00000002)
+
+/** Flag requests that where possible a full duplex stream will not discard
+ overflowed input samples without calling the stream callback. This flag is
+ only valid for full duplex callback streams and only when used in combination
+ with the paFramesPerBufferUnspecified (0) framesPerBuffer parameter. Using
+ this flag incorrectly results in a paInvalidFlag error being returned from
+ Pa_OpenStream and Pa_OpenDefaultStream.
+
+ @see PaStreamFlags, paFramesPerBufferUnspecified
+*/
+#define paNeverDropInput ((PaStreamFlags) 0x00000004)
+
+/** Call the stream callback to fill initial output buffers, rather than the
+ default behavior of priming the buffers with zeros (silence). This flag has
+ no effect for input-only and blocking read/write streams.
+
+ @see PaStreamFlags
+*/
+#define paPrimeOutputBuffersUsingStreamCallback ((PaStreamFlags) 0x00000008)
+
+/** A mask specifying the platform specific bits.
+ @see PaStreamFlags
+*/
+#define paPlatformSpecificFlags ((PaStreamFlags)0xFFFF0000)
+
+/**
+ Timing information for the buffers passed to the stream callback.
+*/
+typedef struct PaStreamCallbackTimeInfo{
+ PaTime inputBufferAdcTime;
+ PaTime currentTime;
+ PaTime outputBufferDacTime;
+} PaStreamCallbackTimeInfo;
+
+
+/**
+ Flag bit constants for the statusFlags to PaStreamCallback.
+
+ @see paInputUnderflow, paInputOverflow, paOutputUnderflow, paOutputOverflow,
+ paPrimingOutput
+*/
+typedef unsigned long PaStreamCallbackFlags;
+
+/** In a stream opened with paFramesPerBufferUnspecified, indicates that
+ input data is all silence (zeros) because no real data is available. In a
+ stream opened without paFramesPerBufferUnspecified, it indicates that one or
+ more zero samples have been inserted into the input buffer to compensate
+ for an input underflow.
+ @see PaStreamCallbackFlags
+*/
+#define paInputUnderflow ((PaStreamCallbackFlags) 0x00000001)
+
+/** In a stream opened with paFramesPerBufferUnspecified, indicates that data
+ prior to the first sample of the input buffer was discarded due to an
+ overflow, possibly because the stream callback is using too much CPU time.
+ Otherwise indicates that data prior to one or more samples in the
+ input buffer was discarded.
+ @see PaStreamCallbackFlags
+*/
+#define paInputOverflow ((PaStreamCallbackFlags) 0x00000002)
+
+/** Indicates that output data (or a gap) was inserted, possibly because the
+ stream callback is using too much CPU time.
+ @see PaStreamCallbackFlags
+*/
+#define paOutputUnderflow ((PaStreamCallbackFlags) 0x00000004)
+
+/** Indicates that output data will be discarded because no room is available.
+ @see PaStreamCallbackFlags
+*/
+#define paOutputOverflow ((PaStreamCallbackFlags) 0x00000008)
+
+/** Some of all of the output data will be used to prime the stream, input
+ data may be zero.
+ @see PaStreamCallbackFlags
+*/
+#define paPrimingOutput ((PaStreamCallbackFlags) 0x00000010)
+
+/**
+ Allowable return values for the PaStreamCallback.
+ @see PaStreamCallback
+*/
+typedef enum PaStreamCallbackResult
+{
+ paContinue=0,
+ paComplete=1,
+ paAbort=2
+} PaStreamCallbackResult;
+
+
+/**
+ Functions of type PaStreamCallback are implemented by PortAudio clients.
+ They consume, process or generate audio in response to requests from an
+ active PortAudio stream.
+
+ @param input and @param output are arrays of interleaved samples,
+ the format, packing and number of channels used by the buffers are
+ determined by parameters to Pa_OpenStream().
+
+ @param frameCount The number of sample frames to be processed by
+ the stream callback.
+
+ @param timeInfo The time in seconds when the first sample of the input
+ buffer was received at the audio input, the time in seconds when the first
+ sample of the output buffer will begin being played at the audio output, and
+ the time in seconds when the stream callback was called.
+ See also Pa_GetStreamTime()
+
+ @param statusFlags Flags indicating whether input and/or output buffers
+ have been inserted or will be dropped to overcome underflow or overflow
+ conditions.
+
+ @param userData The value of a user supplied pointer passed to
+ Pa_OpenStream() intended for storing synthesis data etc.
+
+ @return
+ The stream callback should return one of the values in the
+ PaStreamCallbackResult enumeration. To ensure that the callback continues
+ to be called, it should return paContinue (0). Either paComplete or paAbort
+ can be returned to finish stream processing, after either of these values is
+ returned the callback will not be called again. If paAbort is returned the
+ stream will finish as soon as possible. If paComplete is returned, the stream
+ will continue until all buffers generated by the callback have been played.
+ This may be useful in applications such as soundfile players where a specific
+ duration of output is required. However, it is not necessary to utilise this
+ mechanism as Pa_StopStream(), Pa_AbortStream() or Pa_CloseStream() can also
+ be used to stop the stream. The callback must always fill the entire output
+ buffer irrespective of its return value.
+
+ @see Pa_OpenStream, Pa_OpenDefaultStream
+
+ @note With the exception of Pa_GetStreamCpuLoad() it is not permissable to call
+ PortAudio API functions from within the stream callback.
+*/
+typedef int PaStreamCallback(
+ const void *input, void *output,
+ unsigned long frameCount,
+ const PaStreamCallbackTimeInfo* timeInfo,
+ PaStreamCallbackFlags statusFlags,
+ void *userData );
+
+
+/** Opens a stream for either input, output or both.
+
+ @param stream The address of a PaStream pointer which will receive
+ a pointer to the newly opened stream.
+
+ @param inputParameters A structure that describes the input parameters used by
+ the opened stream. See PaStreamParameters for a description of these parameters.
+ inputParameters must be NULL for output-only streams.
+
+ @param outputParameters A structure that describes the output parameters used by
+ the opened stream. See PaStreamParameters for a description of these parameters.
+ outputParameters must be NULL for input-only streams.
+
+ @param sampleRate The desired sampleRate. For full-duplex streams it is the
+ sample rate for both input and output
+
+ @param framesPerBuffer The number of frames passed to the stream callback
+ function, or the preferred block granularity for a blocking read/write stream.
+ The special value paFramesPerBufferUnspecified (0) may be used to request that
+ the stream callback will recieve an optimal (and possibly varying) number of
+ frames based on host requirements and the requested latency settings.
+ Note: With some host APIs, the use of non-zero framesPerBuffer for a callback
+ stream may introduce an additional layer of buffering which could introduce
+ additional latency. PortAudio guarantees that the additional latency
+ will be kept to the theoretical minimum however, it is strongly recommended
+ that a non-zero framesPerBuffer value only be used when your algorithm
+ requires a fixed number of frames per stream callback.
+
+ @param streamFlags Flags which modify the behaviour of the streaming process.
+ This parameter may contain a combination of flags ORed together. Some flags may
+ only be relevant to certain buffer formats.
+
+ @param streamCallback A pointer to a client supplied function that is responsible
+ for processing and filling input and output buffers. If this parameter is NULL
+ the stream will be opened in 'blocking read/write' mode. In blocking mode,
+ the client can receive sample data using Pa_ReadStream and write sample data
+ using Pa_WriteStream, the number of samples that may be read or written
+ without blocking is returned by Pa_GetStreamReadAvailable and
+ Pa_GetStreamWriteAvailable respectively.
+
+ @param userData A client supplied pointer which is passed to the stream callback
+ function. It could for example, contain a pointer to instance data necessary
+ for processing the audio buffers. This parameter is ignored if streamCallback
+ is NULL.
+
+ @return
+ Upon success Pa_OpenStream() returns paNoError and places a pointer to a
+ valid PaStream in the stream argument. The stream is inactive (stopped).
+ If a call to Pa_OpenStream() fails, a non-zero error code is returned (see
+ PaError for possible error codes) and the value of stream is invalid.
+
+ @see PaStreamParameters, PaStreamCallback, Pa_ReadStream, Pa_WriteStream,
+ Pa_GetStreamReadAvailable, Pa_GetStreamWriteAvailable
+*/
+PaError Pa_OpenStream( PaStream** stream,
+ const PaStreamParameters *inputParameters,
+ const PaStreamParameters *outputParameters,
+ double sampleRate,
+ unsigned long framesPerBuffer,
+ PaStreamFlags streamFlags,
+ PaStreamCallback *streamCallback,
+ void *userData );
+
+
+/** A simplified version of Pa_OpenStream() that opens the default input
+ and/or output devices.
+
+ @param stream The address of a PaStream pointer which will receive
+ a pointer to the newly opened stream.
+
+ @param numInputChannels The number of channels of sound that will be supplied
+ to the stream callback or returned by Pa_ReadStream. It can range from 1 to
+ the value of maxInputChannels in the PaDeviceInfo record for the default input
+ device. If 0 the stream is opened as an output-only stream.
+
+ @param numOutputChannels The number of channels of sound to be delivered to the
+ stream callback or passed to Pa_WriteStream. It can range from 1 to the value
+ of maxOutputChannels in the PaDeviceInfo record for the default output dvice.
+ If 0 the stream is opened as an output-only stream.
+
+ @param sampleFormat The sample format of both the input and output buffers
+ provided to the callback or passed to and from Pa_ReadStream and Pa_WriteStream.
+ sampleFormat may be any of the formats described by the PaSampleFormat
+ enumeration.
+
+ @param sampleRate Same as Pa_OpenStream parameter of the same name.
+ @param framesPerBuffer Same as Pa_OpenStream parameter of the same name.
+ @param streamCallback Same as Pa_OpenStream parameter of the same name.
+ @param userData Same as Pa_OpenStream parameter of the same name.
+
+ @return As for Pa_OpenStream
+
+ @see Pa_OpenStream, PaStreamCallback
+*/
+PaError Pa_OpenDefaultStream( PaStream** stream,
+ int numInputChannels,
+ int numOutputChannels,
+ PaSampleFormat sampleFormat,
+ double sampleRate,
+ unsigned long framesPerBuffer,
+ PaStreamCallback *streamCallback,
+ void *userData );
+
+
+/** Closes an audio stream. If the audio stream is active it
+ discards any pending buffers as if Pa_AbortStream() had been called.
+*/
+PaError Pa_CloseStream( PaStream *stream );
+
+
+/** Functions of type PaStreamFinishedCallback are implemented by PortAudio
+ clients. They can be registered with a stream using the Pa_SetStreamFinishedCallback
+ function. Once registered they are called when the stream becomes inactive
+ (ie once a call to Pa_StopStream() will not block).
+ A stream will become inactive after the stream callback returns non-zero,
+ or when Pa_StopStream or Pa_AbortStream is called. For a stream providing audio
+ output, if the stream callback returns paComplete, or Pa_StopStream is called,
+ the stream finished callback will not be called until all generated sample data
+ has been played.
+
+ @param userData The userData parameter supplied to Pa_OpenStream()
+
+ @see Pa_SetStreamFinishedCallback
+*/
+typedef void PaStreamFinishedCallback( void *userData );
+
+
+/** Register a stream finished callback function which will be called when the
+ stream becomes inactive. See the description of PaStreamFinishedCallback for
+ further details about when the callback will be called.
+
+ @param stream a pointer to a PaStream that is in the stopped state - if the
+ stream is not stopped, the stream's finished callback will remain unchanged
+ and an error code will be returned.
+
+ @param streamFinishedCallback a pointer to a function with the same signature
+ as PaStreamFinishedCallback, that will be called when the stream becomes
+ inactive. Passing NULL for this parameter will un-register a previously
+ registered stream finished callback function.
+
+ @return on success returns paNoError, otherwise an error code indicating the cause
+ of the error.
+
+ @see PaStreamFinishedCallback
+*/
+PaError Pa_SetStreamFinishedCallback( PaStream *stream, PaStreamFinishedCallback* streamFinishedCallback );
+
+
+/** Commences audio processing.
+*/
+PaError Pa_StartStream( PaStream *stream );
+
+
+/** Terminates audio processing. It waits until all pending
+ audio buffers have been played before it returns.
+*/
+PaError Pa_StopStream( PaStream *stream );
+
+
+/** Terminates audio processing immediately without waiting for pending
+ buffers to complete.
+*/
+PaError Pa_AbortStream( PaStream *stream );
+
+
+/** Determine whether the stream is stopped.
+ A stream is considered to be stopped prior to a successful call to
+ Pa_StartStream and after a successful call to Pa_StopStream or Pa_AbortStream.
+ If a stream callback returns a value other than paContinue the stream is NOT
+ considered to be stopped.
+
+ @return Returns one (1) when the stream is stopped, zero (0) when
+ the stream is running or, a PaErrorCode (which are always negative) if
+ PortAudio is not initialized or an error is encountered.
+
+ @see Pa_StopStream, Pa_AbortStream, Pa_IsStreamActive
+*/
+PaError Pa_IsStreamStopped( PaStream *stream );
+
+
+/** Determine whether the stream is active.
+ A stream is active after a successful call to Pa_StartStream(), until it
+ becomes inactive either as a result of a call to Pa_StopStream() or
+ Pa_AbortStream(), or as a result of a return value other than paContinue from
+ the stream callback. In the latter case, the stream is considered inactive
+ after the last buffer has finished playing.
+
+ @return Returns one (1) when the stream is active (ie playing or recording
+ audio), zero (0) when not playing or, a PaErrorCode (which are always negative)
+ if PortAudio is not initialized or an error is encountered.
+
+ @see Pa_StopStream, Pa_AbortStream, Pa_IsStreamStopped
+*/
+PaError Pa_IsStreamActive( PaStream *stream );
+
+
+
+/** A structure containing unchanging information about an open stream.
+ @see Pa_GetStreamInfo
+*/
+
+typedef struct PaStreamInfo
+{
+ /** this is struct version 1 */
+ int structVersion;
+
+ /** The input latency of the stream in seconds. This value provides the most
+ accurate estimate of input latency available to the implementation. It may
+ differ significantly from the suggestedLatency value passed to Pa_OpenStream().
+ The value of this field will be zero (0.) for output-only streams.
+ @see PaTime
+ */
+ PaTime inputLatency;
+
+ /** The output latency of the stream in seconds. This value provides the most
+ accurate estimate of output latency available to the implementation. It may
+ differ significantly from the suggestedLatency value passed to Pa_OpenStream().
+ The value of this field will be zero (0.) for input-only streams.
+ @see PaTime
+ */
+ PaTime outputLatency;
+
+ /** The sample rate of the stream in Hertz (samples per second). In cases
+ where the hardware sample rate is inaccurate and PortAudio is aware of it,
+ the value of this field may be different from the sampleRate parameter
+ passed to Pa_OpenStream(). If information about the actual hardware sample
+ rate is not available, this field will have the same value as the sampleRate
+ parameter passed to Pa_OpenStream().
+ */
+ double sampleRate;
+
+} PaStreamInfo;
+
+
+/** Retrieve a pointer to a PaStreamInfo structure containing information
+ about the specified stream.
+ @return A pointer to an immutable PaStreamInfo structure. If the stream
+ parameter invalid, or an error is encountered, the function returns NULL.
+
+ @param stream A pointer to an open stream previously created with Pa_OpenStream.
+
+ @note PortAudio manages the memory referenced by the returned pointer,
+ the client must not manipulate or free the memory. The pointer is only
+ guaranteed to be valid until the specified stream is closed.
+
+ @see PaStreamInfo
+*/
+const PaStreamInfo* Pa_GetStreamInfo( PaStream *stream );
+
+
+/** Determine the current time for the stream according to the same clock used
+ to generate buffer timestamps. This time may be used for syncronising other
+ events to the audio stream, for example synchronizing audio to MIDI.
+
+ @return The stream's current time in seconds, or 0 if an error occurred.
+
+ @see PaTime, PaStreamCallback
+*/
+PaTime Pa_GetStreamTime( PaStream *stream );
+
+
+/** Retrieve CPU usage information for the specified stream.
+ The "CPU Load" is a fraction of total CPU time consumed by a callback stream's
+ audio processing routines including, but not limited to the client supplied
+ stream callback. This function does not work with blocking read/write streams.
+
+ This function may be called from the stream callback function or the
+ application.
+
+ @return
+ A floating point value, typically between 0.0 and 1.0, where 1.0 indicates
+ that the stream callback is consuming the maximum number of CPU cycles possible
+ to maintain real-time operation. A value of 0.5 would imply that PortAudio and
+ the stream callback was consuming roughly 50% of the available CPU time. The
+ return value may exceed 1.0. A value of 0.0 will always be returned for a
+ blocking read/write stream, or if an error occurrs.
+*/
+double Pa_GetStreamCpuLoad( PaStream* stream );
+
+
+/** Read samples from an input stream. The function doesn't return until
+ the entire buffer has been filled - this may involve waiting for the operating
+ system to supply the data.
+
+ @param stream A pointer to an open stream previously created with Pa_OpenStream.
+
+ @param buffer A pointer to a buffer of sample frames. The buffer contains
+ samples in the format specified by the inputParameters->sampleFormat field
+ used to open the stream, and the number of channels specified by
+ inputParameters->numChannels. If non-interleaved samples were requested,
+ buffer is a pointer to the first element of an array of non-interleaved
+ buffer pointers, one for each channel.
+
+ @param frames The number of frames to be read into buffer. This parameter
+ is not constrained to a specific range, however high performance applications
+ will want to match this parameter to the framesPerBuffer parameter used
+ when opening the stream.
+
+ @return On success PaNoError will be returned, or PaInputOverflowed if input
+ data was discarded by PortAudio after the previous call and before this call.
+*/
+PaError Pa_ReadStream( PaStream* stream,
+ void *buffer,
+ unsigned long frames );
+
+
+/** Write samples to an output stream. This function doesn't return until the
+ entire buffer has been consumed - this may involve waiting for the operating
+ system to consume the data.
+
+ @param stream A pointer to an open stream previously created with Pa_OpenStream.
+
+ @param buffer A pointer to a buffer of sample frames. The buffer contains
+ samples in the format specified by the outputParameters->sampleFormat field
+ used to open the stream, and the number of channels specified by
+ outputParameters->numChannels. If non-interleaved samples were requested,
+ buffer is a pointer to the first element of an array of non-interleaved
+ buffer pointers, one for each channel.
+
+ @param frames The number of frames to be written from buffer. This parameter
+ is not constrained to a specific range, however high performance applications
+ will want to match this parameter to the framesPerBuffer parameter used
+ when opening the stream.
+
+ @return On success PaNoError will be returned, or paOutputUnderflowed if
+ additional output data was inserted after the previous call and before this
+ call.
+*/
+PaError Pa_WriteStream( PaStream* stream,
+ const void *buffer,
+ unsigned long frames );
+
+
+/** Retrieve the number of frames that can be read from the stream without
+ waiting.
+
+ @return Returns a non-negative value representing the maximum number of frames
+ that can be read from the stream without blocking or busy waiting or, a
+ PaErrorCode (which are always negative) if PortAudio is not initialized or an
+ error is encountered.
+*/
+signed long Pa_GetStreamReadAvailable( PaStream* stream );
+
+
+/** Retrieve the number of frames that can be written to the stream without
+ waiting.
+
+ @return Returns a non-negative value representing the maximum number of frames
+ that can be written to the stream without blocking or busy waiting or, a
+ PaErrorCode (which are always negative) if PortAudio is not initialized or an
+ error is encountered.
+*/
+signed long Pa_GetStreamWriteAvailable( PaStream* stream );
+
+
+/* Miscellaneous utilities */
+
+
+/** Retrieve the size of a given sample format in bytes.
+
+ @return The size in bytes of a single sample in the specified format,
+ or paSampleFormatNotSupported if the format is not supported.
+*/
+PaError Pa_GetSampleSize( PaSampleFormat format );
+
+
+/** Put the caller to sleep for at least 'msec' milliseconds. This function is
+ provided only as a convenience for authors of portable code (such as the tests
+ and examples in the PortAudio distribution.)
+
+ The function may sleep longer than requested so don't rely on this for accurate
+ musical timing.
+*/
+void Pa_Sleep( long msec );
+
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* PORTAUDIO_H */
diff --git a/Plugins/voiceservice/m_voice.h b/Plugins/voiceservice/m_voice.h
new file mode 100644
index 0000000..e4ea883
--- /dev/null
+++ b/Plugins/voiceservice/m_voice.h
@@ -0,0 +1,180 @@
+/*
+Copyright (C) 2006-2009 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#ifndef __M_VOICE_H__
+# define __M_VOICE_H__
+
+
+#define EVENTTYPE_VOICE_CALL 8739
+
+
+#define VOICE_CAPS_VOICE (1<<0) // Voice is supported for this protocol. You need to set this one.
+#define VOICE_CAPS_CALL_CONTACT (1<<1) // Set if a call can be made to a hContact (PS_VOICE_CALL_CONTACT_VALID is used to validate the string)
+#define VOICE_CAPS_CALL_STRING (1<<3) // Set if a call can be made to some string (PS_VOICE_CALL_STRING_VALID is used to validate the string)
+/*
+Request to the protocol capabilities relative to voice.
+
+wParam: 0
+lParam: 0
+return: VOICE_CAPS_*
+*/
+#define PS_VOICE_CAPS "/Voice/Caps"
+
+
+
+#define VOICE_SECURE 0x00000001
+#define VOICE_UNICODE 0x80000000
+
+#ifdef UNICODE
+# define VOICE_TCHAR VOICE_UNICODE
+#else
+# define VOICE_TCHAR 0
+#endif
+
+#define VOICE_STATE_TALKING 0
+#define VOICE_STATE_RINGING 1
+#define VOICE_STATE_CALLING 2
+#define VOICE_STATE_ON_HOLD 3
+#define VOICE_STATE_ENDED 4
+#define VOICE_STATE_BUSY 5
+
+typedef struct {
+ int cbSize; // Struct size
+ const char *moduleName; // The name of the module (the same as VOICE_MODULE.name or the protocol szModule)
+ char *id; // Protocol especific ID for this call
+ int flags; // VOICE_UNICODE to say the string is unicode or 0. VOICE_SECURE to say this is a
+ // encrypted call
+
+ HANDLE hContact; // Contact associated with the call (can be NULL)
+
+ union { // Number to call (can be NULL)
+ const TCHAR *ptszNumber;// Or the contact or the number must be != NULL
+ const char *pszNumber; // If both are != NULL the call will be made to the number
+ const WCHAR *pwszNumber;// and will be associated with the contact
+ }; // This fields are only needed in first notification for a call id
+
+ union { // Name of the caller. This makes sense only on incoming calls,
+ const TCHAR *ptszName; // where no contact is associated and the caller has a name and a number.
+ const char *pszName;
+ const WCHAR *pwszName;
+ };
+
+
+ int state; // VOICE_STATE_*
+
+} VOICE_CALL;
+
+
+/*
+Notifies that a voice call changed state
+
+wParam: const VOICE_CALL *
+lParam: ignored
+return: 0 on success
+*/
+#define PE_VOICE_CALL_STATE "/Voice/State"
+
+
+
+/*
+Request the protocol to make a voice call
+
+wParam: (HANDLE) hContact
+lParam: (const TCHAR *) number
+return: 0 on success
+Or the contact or the number must be != NULL. If both are != NULL the call will be
+made to the number and will be associated with the contact.
+*/
+#define PS_VOICE_CALL "/Voice/Call"
+
+/*
+Service called to make the protocol answer a call or restore a hold call.
+It is an async call. If the call was answered, the PE_VOICE_CALL_STATE
+notification will be fired.
+
+wParam: (const char *) id
+lParam: ignored
+return: 0 on success
+*/
+#define PS_VOICE_ANSWERCALL "/Voice/AnswerCall"
+
+/*
+Service called to make the protocol answer a call. This can be called if the
+call is ringing or has started. If called any other time it should be ignored.
+It is an async call. If the call was droped, the PE_VOICE_CALL_STATE
+notification will be fired.
+
+wParam: (const char *) id
+lParam: ignored
+return: 0 on success
+*/
+#define PS_VOICE_DROPCALL "/Voice/DropCall"
+
+/*
+Service called to make the protocol hold a call. This means that the call should not
+be droped, but it should be muted and put in a hold, to allow other call to be answered.
+If the protocol can't hold a cal, it should be droped.
+
+This can be called if the call has started. If called any other time it should be ignored.
+It is an async call. If the call was droped, the PE_VOICE_CALL_STATE
+notification will be fired.
+
+wParam: (const char *) id
+lParam: ignored
+return: 0 on success
+*/
+#define PS_VOICE_HOLDCALL "/Voice/HoldCall"
+
+/*
+Send a DTMF (one digit text) to a talking call.
+
+wParam: (const char *) id
+lParam: (TCHAR) dtmf
+return: 0 on success
+*/
+#define PS_VOICE_SEND_DTMF "/Voice/SendDTMF"
+
+/*
+Used if protocol support VOICE_CALL_STRING. The call string is passed as
+wParam and the proto should validate it. If this service does not exist all numbers can be called.
+
+wParam: (const TCHAR *) call string
+lParam: ignored
+return: 0 if wrong, 1 if correct
+*/
+#define PS_VOICE_CALL_STRING_VALID "/Voice/CallStringValid"
+
+/*
+Used if protocol support VOICE_CALL_CONTACT.
+The hContact is passed as wParam and the proto should tell if this contact can be
+called. If this service does not exist all contacts can be called (or, if it is a protocol,
+all contacts from the protocol can be called).
+
+wParam: (HANDLE) hContact
+lParam: (BOOL) TRUE if it is a test for 'can call now?', FALSE if is a test for 'will be possible to call someday?'
+return: 0 if can't be called, 1 if can
+*/
+#define PS_VOICE_CALL_CONTACT_VALID "/Voice/CallContactValid"
+
+
+
+
+
+#endif // __M_VOICE_H__
diff --git a/Plugins/voiceservice/m_voiceservice.h b/Plugins/voiceservice/m_voiceservice.h
new file mode 100644
index 0000000..2d55d9a
--- /dev/null
+++ b/Plugins/voiceservice/m_voiceservice.h
@@ -0,0 +1,90 @@
+/*
+Copyright (C) 2007-2009 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#ifndef __M_VOICESERVICE_H__
+# define __M_VOICESERVICE_H__
+
+
+#define MIID_VOICESERVICE { 0x7d64437, 0xef2e, 0x4f60, { 0xbb, 0x2d, 0x3c, 0x51, 0x8f, 0xe2, 0x4d, 0x63 } }
+
+
+/*
+This services are a mirror of the services/notifications in m_voice.h,
+with the difference that that ones are to be used by protocols, and this ones
+are to be used by plugins that can make calls to contacts in multiple protocols.
+
+To get the devices for input/output and some options, query the db directly:
+ VoiceService/EchoCancelation BYTE default: TRUE
+ VoiceService/MicBoost BYTE default: TRUE
+ VoiceService/Input TString default: windows default
+ VoiceService/Output TString default: windows default
+*/
+
+
+struct VOICE_MODULE
+{
+ int cbSize; // sizeof(VOICE_MODULE)
+ TCHAR *description; // The description of the voice provider. This is the name that will be shown
+ // to the user
+ char *name; // The internal name of the voice provider. All PS_* serivces
+ // defined in m_voide.h need to be created based in this name. For example,
+ // PS_VOICE_CALL (/Voice/Call) need to be created as <name>/Voice/Call
+ char *icon; // Icon to identify provider (from icolib)
+ int flags; // or of VOICE_CAPS_*. You don't need to send VOICE_CAPS_VOICE.
+};
+/*
+Register a new plugin that can make/receive voice calls (a voice provider).
+
+wParam: const VOICE_MODULE *
+lParam: ignored
+return: 0 on success
+*/
+#define MS_VOICESERVICE_REGISTER "VoiceService/Register"
+
+/*
+Unregister a plugin that can make/receive voice calls (a voice provider).
+
+wParam: (const char *) Provider name
+lParam: ignored
+return: 0 on success
+*/
+#define MS_VOICESERVICE_UNREGISTER "VoiceService/Unregister"
+
+/*
+Request a voice call to hContact.
+
+wParam: (HANDLE) hContact
+lParam: ignored
+return: the number of option calls for a contact. If > 0, it can be called
+*/
+#define MS_VOICESERVICE_CAN_CALL "VoiceService/CanCall"
+
+/*
+Request a voice call to hContact.
+
+wParam: (HANDLE) hContact
+lParam: (char *) Voice provider or NULL to use any provider avaiable
+return: 0 on success
+*/
+#define MS_VOICESERVICE_CALL "VoiceService/Call"
+
+
+
+#endif // __M_VOICESERVICE_H__
diff --git a/Plugins/voiceservice/options.cpp b/Plugins/voiceservice/options.cpp
new file mode 100644
index 0000000..78f2de7
--- /dev/null
+++ b/Plugins/voiceservice/options.cpp
@@ -0,0 +1,616 @@
+/*
+Copyright (C) 2006 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#include "commons.h"
+
+#include "options.h"
+
+
+
+// Prototypes /////////////////////////////////////////////////////////////////////////////////////
+
+
+HANDLE hOptHook = NULL;
+
+Options opts;
+
+
+static BOOL CALLBACK OptionsDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam);
+static BOOL CALLBACK DevicesDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam);
+static BOOL CALLBACK PopupsDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam);
+static BOOL CALLBACK AutoDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam);
+
+
+static OptPageControl optionsControls[] = {
+ { &opts.resize_frame, CONTROL_CHECKBOX, IDC_FRAME_AUTOSIZE, "FrameAutoSize", TRUE }
+};
+
+static OptPageControl devicesControls[] = {
+ { NULL, CONTROL_CHECKBOX, IDC_ECHO, "EchoCancelation", TRUE },
+ { NULL, CONTROL_CHECKBOX, IDC_MIC_BOOST, "MicBoost", TRUE },
+};
+
+static OptPageControl popupsControls[] = {
+ { &opts.popup_enable, CONTROL_CHECKBOX, IDC_POPUPS, "PopupsEnable", FALSE },
+ { &opts.popup_bkg_color, CONTROL_COLOR, IDC_BGCOLOR, "PopupsBgColor", RGB(255,255,255) },
+ { &opts.popup_text_color, CONTROL_COLOR, IDC_TEXTCOLOR, "PopupsTextColor", RGB(0,0,0) },
+ { &opts.popup_use_win_colors, CONTROL_CHECKBOX, IDC_WINCOLORS, "PopupsWinColors", FALSE },
+ { &opts.popup_use_default_colors, CONTROL_CHECKBOX, IDC_DEFAULTCOLORS, "PopupsDefaultColors", FALSE },
+ { &opts.popup_delay_type, CONTROL_RADIO, IDC_DELAYFROMPU, "PopupsDelayType", POPUP_DELAY_DEFAULT, POPUP_DELAY_DEFAULT },
+ { NULL, CONTROL_RADIO, IDC_DELAYCUSTOM, "PopupsDelayType", POPUP_DELAY_DEFAULT, POPUP_DELAY_CUSTOM },
+ { NULL, CONTROL_RADIO, IDC_DELAYPERMANENT, "PopupsDelayType", POPUP_DELAY_DEFAULT, POPUP_DELAY_PERMANENT },
+ { &opts.popup_timeout, CONTROL_SPIN, IDC_DELAY, "PopupsTimeout", 10, IDC_DELAY_SPIN, (WORD) 1, (WORD) 255 },
+ { &opts.popup_right_click_action, CONTROL_COMBO, IDC_RIGHT_ACTION, "PopupsRightClick", POPUP_ACTION_CLOSEPOPUP },
+ { &opts.popup_left_click_action, CONTROL_COMBO, IDC_LEFT_ACTION, "PopupsLeftClick", POPUP_ACTION_CLOSEPOPUP }
+};
+
+static UINT popupsExpertControls[] = {
+ IDC_COLOURS_G, IDC_BGCOLOR, IDC_BGCOLOR_L, IDC_TEXTCOLOR, IDC_TEXTCOLOR_L, IDC_WINCOLORS, IDC_DEFAULTCOLORS,
+ IDC_DELAY_G, IDC_DELAYFROMPU, IDC_DELAYCUSTOM, IDC_DELAYPERMANENT, IDC_DELAY, IDC_DELAY_SPIN,
+ IDC_ACTIONS_G, IDC_RIGHT_ACTION_L, IDC_RIGHT_ACTION, IDC_LEFT_ACTION_L, IDC_LEFT_ACTION,
+ IDC_PREV
+};
+
+
+// Functions //////////////////////////////////////////////////////////////////////////////////////
+
+int InitOptionsCallback(WPARAM wParam,LPARAM lParam)
+{
+ OPTIONSDIALOGPAGE odp;
+
+ if(ServiceExists(MS_POPUP_ADDPOPUPEX)
+#ifdef UNICODE
+ || ServiceExists(MS_POPUP_ADDPOPUPW)
+#endif
+ )
+ {
+ ZeroMemory(&odp, sizeof(odp));
+ odp.cbSize = sizeof(odp);
+ odp.hInstance = hInst;
+ odp.ptszGroup = LPGENT("Popups");
+ odp.ptszTitle = LPGENT("Voice Calls");
+ odp.pfnDlgProc = PopupsDlgProc;
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_POPUPS);
+ odp.flags = ODPF_BOLDGROUPS | ODPF_TCHAR;
+ odp.expertOnlyControls = popupsExpertControls;
+ odp.nExpertOnlyControls = MAX_REGS(popupsExpertControls);
+ CallService(MS_OPT_ADDPAGE, wParam, (LPARAM) &odp);
+ }
+
+ ZeroMemory(&odp, sizeof(odp));
+ odp.cbSize = sizeof(odp);
+ odp.hInstance = hInst;
+ odp.ptszTitle = LPGENT("Voice Calls");
+ odp.pfnDlgProc = OptionsDlgProc;
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPTS);
+ odp.flags = ODPF_BOLDGROUPS | ODPF_TCHAR;
+ CallService(MS_OPT_ADDPAGE, wParam, (LPARAM) &odp);
+
+ ZeroMemory(&odp, sizeof(odp));
+ odp.cbSize = sizeof(odp);
+ odp.hInstance = hInst;
+ odp.ptszGroup = LPGENT("Voice Calls");
+ odp.ptszTitle = LPGENT("Auto actions");
+ odp.pfnDlgProc = AutoDlgProc;
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT_AUTO);
+ odp.flags = ODPF_BOLDGROUPS | ODPF_TCHAR;
+ CallService(MS_OPT_ADDPAGE, wParam, (LPARAM) &odp);
+
+ ZeroMemory(&odp, sizeof(odp));
+ odp.cbSize = sizeof(odp);
+ odp.hInstance = hInst;
+ odp.ptszGroup = LPGENT("Voice Calls");
+ odp.ptszTitle = LPGENT("Devices");
+ odp.pfnDlgProc = DevicesDlgProc;
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT_DEVICES);
+ odp.flags = ODPF_BOLDGROUPS | ODPF_TCHAR;
+ CallService(MS_OPT_ADDPAGE, wParam, (LPARAM) &odp);
+
+ return 0;
+}
+
+
+void InitOptions()
+{
+ LoadOptions();
+
+ hOptHook = HookEvent(ME_OPT_INITIALISE, InitOptionsCallback);
+}
+
+
+void DeInitOptions()
+{
+ UnhookEvent(hOptHook);
+}
+
+
+void LoadOptions()
+{
+ LoadOpts(optionsControls, MAX_REGS(optionsControls), MODULE_NAME);
+ LoadOpts(popupsControls, MAX_REGS(popupsControls), MODULE_NAME);
+}
+
+
+static BOOL CALLBACK OptionsDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ return SaveOptsDlgProc(optionsControls, MAX_REGS(optionsControls), MODULE_NAME, hwndDlg, msg, wParam, lParam);
+}
+
+
+static BOOL CALLBACK DevicesDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg)
+ {
+ case WM_INITDIALOG:
+ {
+ int defaultInput = Pa_GetDefaultInputDevice();
+ const char *defaultInputName = NULL;
+
+ int defaultOutput = Pa_GetDefaultOutputDevice();
+ const char *defaultOutputName = NULL;
+
+ int devices = Pa_GetDeviceCount();
+ for(int i = 0; i < devices; i++)
+ {
+ const PaDeviceInfo *device = Pa_GetDeviceInfo(i);
+
+ if (device->maxInputChannels > 0)
+ {
+ SendDlgItemMessage(hwndDlg, IDC_INPUT, CB_ADDSTRING, 0, (WPARAM) CharToTchar(device->name).get());
+ if (i == defaultInput)
+ defaultInputName = device->name;
+ }
+
+ if (device->maxOutputChannels > 0)
+ {
+ SendDlgItemMessage(hwndDlg, IDC_OUTPUT, CB_ADDSTRING, 0, (WPARAM) CharToTchar(device->name).get());
+ if (i == defaultOutput)
+ defaultOutputName = device->name;
+ }
+ }
+
+ DBVARIANT dbvInput = {0};
+ if (DBGetContactSettingString(NULL, MODULE_NAME, "Input", &dbvInput) == 0 && dbvInput.ptszVal != NULL && dbvInput.ptszVal[0] != 0)
+ defaultInputName = dbvInput.pszVal;
+
+ DBVARIANT dbvOutput = {0};
+ if (DBGetContactSettingString(NULL, MODULE_NAME, "Output", &dbvOutput) == 0 && dbvOutput.ptszVal != NULL && dbvOutput.ptszVal[0] != 0)
+ defaultOutputName = dbvOutput.pszVal;
+
+ if (defaultInputName != NULL)
+ SendDlgItemMessageA(hwndDlg, IDC_INPUT, CB_SELECTSTRING, -1, (WPARAM) defaultInputName);
+
+ if (defaultOutputName != NULL)
+ SendDlgItemMessageA(hwndDlg, IDC_OUTPUT, CB_SELECTSTRING, -1, (WPARAM) defaultOutputName);
+
+ DBFreeVariant(&dbvOutput);
+ DBFreeVariant(&dbvInput);
+
+ break;
+ }
+ case WM_COMMAND:
+ {
+ if (LOWORD(wParam) == IDC_INPUT || LOWORD(wParam) == IDC_OUTPUT)
+ {
+ if (HIWORD(wParam) != CBN_SELCHANGE || (HWND)lParam != GetFocus())
+ return 0;
+
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ }
+
+ break;
+ }
+ case WM_NOTIFY:
+ {
+ LPNMHDR lpnmhdr = (LPNMHDR)lParam;
+
+ if (lpnmhdr->idFrom == 0 && lpnmhdr->code == PSN_APPLY)
+ {
+ TCHAR tmp[1024];
+
+ GetWindowText(GetDlgItem(hwndDlg, IDC_INPUT), tmp, MAX_REGS(tmp));
+ DBWriteContactSettingTString(NULL, MODULE_NAME, "Input", tmp);
+
+ GetWindowText(GetDlgItem(hwndDlg, IDC_OUTPUT), tmp, MAX_REGS(tmp));
+ DBWriteContactSettingTString(NULL, MODULE_NAME, "Output", tmp);
+ }
+ break;
+ }
+ }
+
+ return SaveOptsDlgProc(devicesControls, MAX_REGS(devicesControls), MODULE_NAME, hwndDlg, msg, wParam, lParam);
+}
+
+
+static void PopupsEnableDisableCtrls(HWND hwndDlg)
+{
+ BOOL enabled = IsDlgButtonChecked(hwndDlg, IDC_POPUPS);
+
+ EnableWindow(GetDlgItem(hwndDlg, IDC_COLOURS_G), enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_BGCOLOR_L), enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_TEXTCOLOR_L), enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_DELAY_G), enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_DELAYFROMPU), enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_DELAYCUSTOM), enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_DELAYPERMANENT), enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_ACTIONS_G), enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_RIGHT_ACTION_L), enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_RIGHT_ACTION), enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_LEFT_ACTION_L), enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_LEFT_ACTION), enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_PREV), enabled);
+
+ EnableWindow(GetDlgItem(hwndDlg, IDC_BGCOLOR), enabled &&
+ !IsDlgButtonChecked(hwndDlg, IDC_WINCOLORS) &&
+ !IsDlgButtonChecked(hwndDlg, IDC_DEFAULTCOLORS));
+ EnableWindow(GetDlgItem(hwndDlg, IDC_TEXTCOLOR), enabled &&
+ !IsDlgButtonChecked(hwndDlg, IDC_WINCOLORS) &&
+ !IsDlgButtonChecked(hwndDlg, IDC_DEFAULTCOLORS));
+ EnableWindow(GetDlgItem(hwndDlg, IDC_DEFAULTCOLORS), enabled &&
+ !IsDlgButtonChecked(hwndDlg, IDC_WINCOLORS));
+ EnableWindow(GetDlgItem(hwndDlg, IDC_WINCOLORS), enabled &&
+ !IsDlgButtonChecked(hwndDlg, IDC_DEFAULTCOLORS));
+
+ EnableWindow(GetDlgItem(hwndDlg, IDC_DELAY), enabled &&
+ IsDlgButtonChecked(hwndDlg, IDC_DELAYCUSTOM));
+}
+
+
+static BOOL CALLBACK PopupsDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg)
+ {
+ case WM_INITDIALOG:
+ {
+ SendDlgItemMessage(hwndDlg, IDC_RIGHT_ACTION, CB_ADDSTRING, 0, (LONG) TranslateT("Do nothing"));
+ SendDlgItemMessage(hwndDlg, IDC_RIGHT_ACTION, CB_ADDSTRING, 0, (LONG) TranslateT("Close popup"));
+
+ SendDlgItemMessage(hwndDlg, IDC_LEFT_ACTION, CB_ADDSTRING, 0, (LONG) TranslateT("Do nothing"));
+ SendDlgItemMessage(hwndDlg, IDC_LEFT_ACTION, CB_ADDSTRING, 0, (LONG) TranslateT("Close popup"));
+
+ // Needs to be called here in this case
+ BOOL ret = SaveOptsDlgProc(popupsControls, MAX_REGS(popupsControls), MODULE_NAME, hwndDlg, msg, wParam, lParam);
+
+ PopupsEnableDisableCtrls(hwndDlg);
+
+ return ret;
+ }
+ case WM_COMMAND:
+ {
+ switch (LOWORD(wParam))
+ {
+ case IDC_POPUPS:
+ case IDC_WINCOLORS:
+ case IDC_DEFAULTCOLORS:
+ case IDC_DELAYFROMPU:
+ case IDC_DELAYPERMANENT:
+ case IDC_DELAYCUSTOM:
+ {
+ if (HIWORD(wParam) == BN_CLICKED)
+ PopupsEnableDisableCtrls(hwndDlg);
+
+ break;
+ }
+ case IDC_PREV:
+ {
+ Options op = opts;
+
+ if (IsDlgButtonChecked(hwndDlg, IDC_DELAYFROMPU))
+ op.popup_delay_type = POPUP_DELAY_DEFAULT;
+ else if (IsDlgButtonChecked(hwndDlg, IDC_DELAYCUSTOM))
+ op.popup_delay_type = POPUP_DELAY_CUSTOM;
+ else if (IsDlgButtonChecked(hwndDlg, IDC_DELAYPERMANENT))
+ op.popup_delay_type = POPUP_DELAY_PERMANENT;
+
+ op.popup_timeout = GetDlgItemInt(hwndDlg,IDC_DELAY, NULL, FALSE);
+ op.popup_bkg_color = SendDlgItemMessage(hwndDlg,IDC_BGCOLOR,CPM_GETCOLOUR,0,0);
+ op.popup_text_color = SendDlgItemMessage(hwndDlg,IDC_TEXTCOLOR,CPM_GETCOLOUR,0,0);
+ op.popup_use_win_colors = IsDlgButtonChecked(hwndDlg, IDC_WINCOLORS) != 0;
+ op.popup_use_default_colors = IsDlgButtonChecked(hwndDlg, IDC_DEFAULTCOLORS) != 0;
+
+ ShowTestPopup(TranslateT("Test Contact"), TranslateT("Test description"), &op);
+
+ break;
+ }
+ }
+ break;
+ }
+ }
+
+ return SaveOptsDlgProc(popupsControls, MAX_REGS(popupsControls), MODULE_NAME, hwndDlg, msg, wParam, lParam);
+}
+
+
+static void SetAllContactIcons(HWND hwndList)
+{
+ HANDLE hContact,hItem;
+
+ hContact=(HANDLE)CallService(MS_DB_CONTACT_FINDFIRST,0,0);
+ do
+ {
+ hItem=(HANDLE)SendMessage(hwndList,CLM_FINDCONTACT,(WPARAM)hContact,0);
+ if(hItem)
+ {
+ // Some Module can handle it?
+ if (!CanCall(hContact, FALSE))
+ {
+ SendMessage(hwndList,CLM_DELETEITEM,(WPARAM)hItem,0);
+ }
+ else
+ {
+ SendMessage(hwndList,CLM_SETEXTRAIMAGE,(WPARAM)hItem,MAKELPARAM(0,
+ DBGetContactSettingWord(hContact, MODULE_NAME, "AutoAccept", AUTO_NOTHING) == AUTO_ACCEPT ? 1 : 0));
+ SendMessage(hwndList,CLM_SETEXTRAIMAGE,(WPARAM)hItem,MAKELPARAM(1,
+ DBGetContactSettingWord(hContact, MODULE_NAME, "AutoAccept", AUTO_NOTHING) == AUTO_DROP ? 2 : 0));
+ }
+ }
+ } while(hContact=(HANDLE)CallService(MS_DB_CONTACT_FINDNEXT,(WPARAM)hContact,0));
+}
+
+
+static void SetListGroupIcons(HWND hwndList,HANDLE hFirstItem,HANDLE hParentItem,int *groupChildCount)
+{
+ int typeOfFirst;
+ int iconOn[2]={1,1};
+ int childCount[2]={0,0},i;
+ int iImage;
+ HANDLE hItem,hChildItem;
+
+ typeOfFirst=SendMessage(hwndList,CLM_GETITEMTYPE,(WPARAM)hFirstItem,0);
+ //check groups
+ if(typeOfFirst==CLCIT_GROUP) hItem=hFirstItem;
+ else hItem=(HANDLE)SendMessage(hwndList,CLM_GETNEXTITEM,CLGN_NEXTGROUP,(LPARAM)hFirstItem);
+ while(hItem) {
+ hChildItem=(HANDLE)SendMessage(hwndList,CLM_GETNEXTITEM,CLGN_CHILD,(LPARAM)hItem);
+ if(hChildItem) SetListGroupIcons(hwndList,hChildItem,hItem,childCount);
+ for( i=0; i < MAX_REGS(iconOn); i++)
+ if(iconOn[i] && SendMessage(hwndList,CLM_GETEXTRAIMAGE,(WPARAM)hItem,i)==0) iconOn[i]=0;
+ hItem=(HANDLE)SendMessage(hwndList,CLM_GETNEXTITEM,CLGN_NEXTGROUP,(LPARAM)hItem);
+ }
+ //check contacts
+ if(typeOfFirst==CLCIT_CONTACT) hItem=hFirstItem;
+ else hItem=(HANDLE)SendMessage(hwndList,CLM_GETNEXTITEM,CLGN_NEXTCONTACT,(LPARAM)hFirstItem);
+ while(hItem) {
+ for ( i=0; i < MAX_REGS(iconOn); i++) {
+ iImage=SendMessage(hwndList,CLM_GETEXTRAIMAGE,(WPARAM)hItem,i);
+ if(iconOn[i] && iImage==0) iconOn[i]=0;
+ if(iImage!=0xFF) childCount[i]++;
+ }
+ hItem=(HANDLE)SendMessage(hwndList,CLM_GETNEXTITEM,CLGN_NEXTCONTACT,(LPARAM)hItem);
+ }
+ //set icons
+ if (hParentItem != NULL) {
+ for( i=0; i < MAX_REGS(iconOn); i++) {
+ SendMessage(hwndList,CLM_SETEXTRAIMAGE,(WPARAM)hParentItem,MAKELPARAM(i,childCount[i]?(iconOn[i]?i+1:0):0xFF));
+ if(groupChildCount) groupChildCount[i]+=childCount[i];
+ }
+ }
+}
+
+
+static void SetAllChildIcons(HWND hwndList,HANDLE hFirstItem,int iColumn,int iImage)
+{
+ int typeOfFirst,iOldIcon;
+ HANDLE hItem,hChildItem;
+
+ typeOfFirst=SendMessage(hwndList,CLM_GETITEMTYPE,(WPARAM)hFirstItem,0);
+ //check groups
+ if(typeOfFirst==CLCIT_GROUP) hItem=hFirstItem;
+ else hItem=(HANDLE)SendMessage(hwndList,CLM_GETNEXTITEM,CLGN_NEXTGROUP,(LPARAM)hFirstItem);
+ while(hItem) {
+ hChildItem=(HANDLE)SendMessage(hwndList,CLM_GETNEXTITEM,CLGN_CHILD,(LPARAM)hItem);
+ if(hChildItem) SetAllChildIcons(hwndList,hChildItem,iColumn,iImage);
+ hItem=(HANDLE)SendMessage(hwndList,CLM_GETNEXTITEM,CLGN_NEXTGROUP,(LPARAM)hItem);
+ }
+ //check contacts
+ if(typeOfFirst==CLCIT_CONTACT) hItem=hFirstItem;
+ else hItem=(HANDLE)SendMessage(hwndList,CLM_GETNEXTITEM,CLGN_NEXTCONTACT,(LPARAM)hFirstItem);
+ while(hItem) {
+ iOldIcon=SendMessage(hwndList,CLM_GETEXTRAIMAGE,(WPARAM)hItem,iColumn);
+ if(iOldIcon!=0xFF && iOldIcon!=iImage) SendMessage(hwndList,CLM_SETEXTRAIMAGE,(WPARAM)hItem,MAKELPARAM(iColumn,iImage));
+ hItem=(HANDLE)SendMessage(hwndList,CLM_GETNEXTITEM,CLGN_NEXTCONTACT,(LPARAM)hItem);
+ }
+}
+
+
+static void ResetListOptions(HWND hwndList)
+{
+ SendMessage(hwndList,CLM_SETBKBITMAP, 0, 0);
+ SendMessage(hwndList,CLM_SETBKCOLOR, GetSysColor(COLOR_WINDOW), 0);
+ SendMessage(hwndList,CLM_SETGREYOUTFLAGS, 0, 0);
+ SendMessage(hwndList,CLM_SETLEFTMARGIN, 2, 0);
+ SendMessage(hwndList,CLM_SETINDENT, 10, 0);
+
+ for(int i=0;i<=FONTID_MAX;i++)
+ SendMessage(hwndList, CLM_SETTEXTCOLOR, i, GetSysColor(COLOR_WINDOWTEXT));
+
+// SetWindowLong(hwndList,GWL_STYLE,GetWindowLong(hwndList,GWL_STYLE)|CLS_SHOWHIDDEN|CLS_NOHIDEOFFLINE|CLS_GREYALTERNATE);
+}
+
+
+int ImageList_AddIcon_NotShared(HIMAGELIST hIml, HINSTANCE hInstance, LPCTSTR szResource)
+{
+ HICON hIcon = LoadIcon(hInstance, szResource);
+ int res = ImageList_AddIcon(hIml, hIcon);
+ DestroyIcon(hIcon);
+ return res;
+}
+
+
+int ImageList_AddIcon_NotShared(HIMAGELIST hIml, char *iconName)
+{
+ HICON hIcon = IcoLib_LoadIcon(iconName);
+ int res = ImageList_AddIcon(hIml, hIcon);
+ IcoLib_ReleaseIcon(hIcon);
+ return res;
+}
+
+
+static BOOL CALLBACK AutoDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ static HICON hAnswerIcon, hDropIcon;
+
+ switch (msg)
+ {
+ case WM_INITDIALOG:
+ {
+ TranslateDialogDefault(hwndDlg);
+
+ {
+ HIMAGELIST hIml = ImageList_Create(GetSystemMetrics(SM_CXSMICON),GetSystemMetrics(SM_CYSMICON),
+ (IsWinVerXPPlus()?ILC_COLOR32:ILC_COLOR16)|ILC_MASK,3,3);
+
+ ImageList_AddIcon_NotShared(hIml, GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_SMALLDOT));
+ ImageList_AddIcon_NotShared(hIml, actionIcons[ACTION_ANSWER]);
+ ImageList_AddIcon_NotShared(hIml, actionIcons[ACTION_DROP]);
+ SendDlgItemMessage(hwndDlg,IDC_LIST,CLM_SETEXTRAIMAGELIST,0,(LPARAM)hIml);
+
+ hAnswerIcon=ImageList_GetIcon(hIml,1,ILD_NORMAL);
+ SendDlgItemMessage(hwndDlg,IDC_ANSWER,STM_SETICON,(WPARAM)hAnswerIcon,0);
+
+ hDropIcon=ImageList_GetIcon(hIml,2,ILD_NORMAL);
+ SendDlgItemMessage(hwndDlg,IDC_DROP,STM_SETICON,(WPARAM)hDropIcon,0);
+ }
+
+ ResetListOptions(GetDlgItem(hwndDlg,IDC_LIST));
+ SendDlgItemMessage(hwndDlg,IDC_LIST,CLM_SETEXTRACOLUMNS,2,0);
+
+ SetAllContactIcons(GetDlgItem(hwndDlg,IDC_LIST));
+ SetListGroupIcons(GetDlgItem(hwndDlg,IDC_LIST),(HANDLE)SendDlgItemMessage(hwndDlg,IDC_LIST,CLM_GETNEXTITEM,CLGN_ROOT,0),NULL,NULL);
+ return TRUE;
+ }
+ case WM_SETFOCUS:
+ SetFocus(GetDlgItem(hwndDlg,IDC_LIST));
+ break;
+ case WM_NOTIFY:
+ switch(((LPNMHDR)lParam)->idFrom)
+ {
+ case IDC_LIST:
+ switch (((LPNMHDR)lParam)->code)
+ {
+ case CLN_NEWCONTACT:
+ case CLN_LISTREBUILT:
+ SetAllContactIcons(GetDlgItem(hwndDlg,IDC_LIST));
+ //fall through
+
+ case CLN_CONTACTMOVED:
+ SetListGroupIcons(GetDlgItem(hwndDlg,IDC_LIST),(HANDLE)SendDlgItemMessage(hwndDlg,IDC_LIST,CLM_GETNEXTITEM,CLGN_ROOT,0),NULL,NULL);
+ break;
+
+ case CLN_OPTIONSCHANGED:
+ ResetListOptions(GetDlgItem(hwndDlg,IDC_LIST));
+ break;
+
+ case NM_CLICK:
+ {
+ HANDLE hItem;
+ NMCLISTCONTROL *nm=(NMCLISTCONTROL*)lParam;
+ DWORD hitFlags;
+ int iImage;
+ int itemType;
+
+ // Make sure we have an extra column
+ if (nm->iColumn == -1)
+ break;
+
+ // Find clicked item
+ hItem = (HANDLE)SendDlgItemMessage(hwndDlg, IDC_LIST, CLM_HITTEST, (WPARAM)&hitFlags, MAKELPARAM(nm->pt.x,nm->pt.y));
+ // Nothing was clicked
+ if (hItem == NULL) break;
+ // It was not a visbility icon
+ if (!(hitFlags & CLCHT_ONITEMEXTRA)) break;
+
+ // Get image in clicked column (0=none, 1=visible, 2=invisible)
+ iImage = SendDlgItemMessage(hwndDlg, IDC_LIST, CLM_GETEXTRAIMAGE, (WPARAM)hItem, MAKELPARAM(nm->iColumn, 0));
+ if (iImage == 0)
+ iImage=nm->iColumn + 1;
+ else
+ if (iImage == 1 || iImage == 2)
+ iImage = 0;
+
+ // Get item type (contact, group, etc...)
+ itemType = SendDlgItemMessage(hwndDlg, IDC_LIST, CLM_GETITEMTYPE, (WPARAM)hItem, 0);
+
+ // Update list, making sure that the options are mutually exclusive
+ if (itemType == CLCIT_CONTACT) { // A contact
+ SendDlgItemMessage(hwndDlg, IDC_LIST, CLM_SETEXTRAIMAGE, (WPARAM)hItem, MAKELPARAM(nm->iColumn, iImage));
+ if (iImage && SendDlgItemMessage(hwndDlg,IDC_LIST,CLM_GETEXTRAIMAGE,(WPARAM)hItem,MAKELPARAM(nm->iColumn?0:1,0))!=0xFF)
+ SendDlgItemMessage(hwndDlg, IDC_LIST, CLM_SETEXTRAIMAGE, (WPARAM)hItem, MAKELPARAM(nm->iColumn?0:1, 0));
+ }
+ else if (itemType == CLCIT_GROUP) { // A group
+ hItem = (HANDLE)SendDlgItemMessage(hwndDlg, IDC_LIST, CLM_GETNEXTITEM, CLGN_CHILD, (LPARAM)hItem);
+ if (hItem) {
+ SetAllChildIcons(GetDlgItem(hwndDlg, IDC_LIST), hItem, nm->iColumn, iImage);
+ if (iImage)
+ SetAllChildIcons(GetDlgItem(hwndDlg, IDC_LIST), hItem, nm->iColumn?0:1, 0);
+ }
+ }
+ // Update the all/none icons
+ SetListGroupIcons(GetDlgItem(hwndDlg, IDC_LIST), (HANDLE)SendDlgItemMessage(hwndDlg, IDC_LIST, CLM_GETNEXTITEM, CLGN_ROOT, 0), NULL, NULL);
+
+ // Activate Apply button
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ break;
+ }
+ }
+ break;
+ case 0:
+ switch (((LPNMHDR)lParam)->code)
+ {
+ case PSN_APPLY:
+ {
+ HANDLE hContact,hItem;
+ int set,i,iImage;
+
+ hContact=(HANDLE)CallService(MS_DB_CONTACT_FINDFIRST,0,0);
+ do {
+ hItem=(HANDLE)SendDlgItemMessage(hwndDlg,IDC_LIST,CLM_FINDCONTACT,(WPARAM)hContact,0);
+ if(hItem)
+ {
+ set=0;
+ for(i=0;i<2;i++) {
+ iImage=SendDlgItemMessage(hwndDlg,IDC_LIST,CLM_GETEXTRAIMAGE,(WPARAM)hItem,MAKELPARAM(i,0));
+ if(iImage==i+1) {
+ DBWriteContactSettingWord(hContact, MODULE_NAME, "AutoAccept", iImage==1 ? AUTO_ACCEPT : AUTO_DROP);
+ set=1;
+ break;
+ }
+ }
+ if(!set) DBWriteContactSettingWord(hContact, MODULE_NAME, "AutoAccept", AUTO_NOTHING);
+ }
+ } while(hContact=(HANDLE)CallService(MS_DB_CONTACT_FINDNEXT,(WPARAM)hContact,0));
+ return TRUE;
+ }
+ }
+ break;
+ }
+ break;
+ case WM_DESTROY:
+ DestroyIcon(hAnswerIcon);
+ DestroyIcon(hDropIcon);
+ {
+ HIMAGELIST hIml=(HIMAGELIST)SendDlgItemMessage(hwndDlg,IDC_LIST,CLM_GETEXTRAIMAGELIST,0,0);
+ ImageList_Destroy(hIml);
+ }
+ break;
+ }
+ return FALSE;
+
+} \ No newline at end of file
diff --git a/Plugins/voiceservice/options.h b/Plugins/voiceservice/options.h
new file mode 100644
index 0000000..ff5d649
--- /dev/null
+++ b/Plugins/voiceservice/options.h
@@ -0,0 +1,67 @@
+/*
+Copyright (C) 2006 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#ifndef __OPTIONS_H__
+# define __OPTIONS_H__
+
+
+#include <windows.h>
+
+#define POPUP_ACTION_DONOTHING 0
+#define POPUP_ACTION_CLOSEPOPUP 1
+
+#define POPUP_DELAY_DEFAULT 0
+#define POPUP_DELAY_CUSTOM 1
+#define POPUP_DELAY_PERMANENT 2
+
+
+struct Options {
+ // Popup
+ BYTE popup_enable;
+ WORD popup_delay_type;
+ WORD popup_timeout;
+ BYTE popup_use_win_colors;
+ BYTE popup_use_default_colors;
+ COLORREF popup_bkg_color;
+ COLORREF popup_text_color;
+ WORD popup_left_click_action;
+ WORD popup_right_click_action;
+
+ // Frame
+ BOOL resize_frame;
+};
+
+extern Options opts;
+
+
+// Initializations needed by options
+void InitOptions();
+
+// Deinitializations needed by options
+void DeInitOptions();
+
+
+// Loads the options from DB
+// It don't need to be called, except in some rare cases
+void LoadOptions();
+
+
+
+#endif // __OPTIONS_H__
diff --git a/Plugins/voiceservice/popup.cpp b/Plugins/voiceservice/popup.cpp
new file mode 100644
index 0000000..45d06f6
--- /dev/null
+++ b/Plugins/voiceservice/popup.cpp
@@ -0,0 +1,340 @@
+/*
+Copyright (C) 2005 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#include "commons.h"
+
+#include <stdarg.h>
+
+
+
+// Prototypes /////////////////////////////////////////////////////////////////////////////////////
+
+#define WMU_ACTION (WM_USER + 1)
+
+
+LRESULT CALLBACK PopupWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
+
+HWND hPopupWindow = NULL;
+
+
+static LRESULT CALLBACK PopupDlgProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
+static LRESULT CALLBACK DumbPopupDlgProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
+
+
+
+// Functions //////////////////////////////////////////////////////////////////////////////////////
+
+
+// Initializations needed by popups
+void InitPopups()
+{
+ // window needed for popup commands
+ hPopupWindow = CreateWindowEx(WS_EX_TOOLWINDOW, _T("static"), _T(MODULE_NAME) _T("_PopupWindow"),
+ 0, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, HWND_DESKTOP,
+ NULL, hInst, NULL);
+ SetWindowLong(hPopupWindow, GWL_WNDPROC, (LONG)(WNDPROC)PopupWndProc);
+}
+
+
+// Deinitializations needed by popups
+void DeInitPopups()
+{
+}
+
+
+// Show an error popup
+void ShowErrPopup(const TCHAR *description, const TCHAR *title)
+{
+ ShowPopupEx(NULL, title == NULL ? _T(MODULE_NAME) _T(" Error") : title, description,
+ NULL, POPUP_TYPE_ERROR, NULL);
+}
+
+
+void ShowTestPopup(const TCHAR *title, const TCHAR *description, const Options *op)
+{
+ ShowPopupEx(NULL, title, description, NULL, POPUP_TYPE_TEST, op);
+}
+
+
+void ShowPopup(HANDLE hContact, const TCHAR *title, const TCHAR *description, ...)
+{
+ if (!opts.popup_enable)
+ return;
+
+ TCHAR text[1024];
+
+ va_list va;
+ va_start(va, description);
+ _vsntprintf(text, MAX_REGS(text), description, va);
+ va_end(va);
+
+
+ ShowPopupEx(hContact, title, text, hContact, POPUP_TYPE_NORMAL, &opts);
+}
+
+
+// Show an popup
+void ShowPopupEx(HANDLE hContact, const TCHAR *title, const TCHAR *description,
+ void *plugin_data, int type, const Options *op)
+{
+#ifdef UNICODE
+ if(ServiceExists(MS_POPUP_ADDPOPUPW))
+ {
+ // Make popup
+ POPUPDATAW ppd;
+
+ ZeroMemory(&ppd, sizeof(ppd));
+ ppd.lchContact = hContact;
+ ppd.lchIcon = (HICON) LoadImage(GetModuleHandle(NULL),MAKEINTRESOURCE(174),IMAGE_ICON,16,16,LR_DEFAULTCOLOR);
+
+ if (title != NULL)
+ lstrcpyn(ppd.lpwzContactName, title, MAX_REGS(ppd.lpwzContactName));
+ else if (hContact != NULL)
+ lstrcpyn(ppd.lpwzContactName, (TCHAR *) CallService(MS_CLIST_GETCONTACTDISPLAYNAME, (WPARAM)hContact, GCDNF_TCHAR),
+ MAX_REGS(ppd.lpwzContactName));
+
+ if (description != NULL)
+ lstrcpyn(ppd.lpwzText, description, MAX_REGS(ppd.lpwzText));
+
+ if (type == POPUP_TYPE_NORMAL || type == POPUP_TYPE_TEST)
+ {
+ if (op->popup_use_default_colors)
+ {
+ ppd.colorBack = 0;
+ ppd.colorText = 0;
+ }
+ else if (op->popup_use_win_colors)
+ {
+ ppd.colorBack = GetSysColor(COLOR_BTNFACE);
+ ppd.colorText = GetSysColor(COLOR_WINDOWTEXT);
+ }
+ else
+ {
+ ppd.colorBack = op->popup_bkg_color;
+ ppd.colorText = op->popup_text_color;
+ }
+ }
+ else // if (type == POPUP_TYPE_ERROR)
+ {
+ ppd.colorBack = RGB(200,0,0);
+ ppd.colorText = RGB(255,255,255);
+ }
+
+ if (type == POPUP_TYPE_NORMAL)
+ {
+ ppd.PluginWindowProc = PopupDlgProc;
+ ppd.PluginData = plugin_data;
+ }
+ else // if (type == POPUP_TYPE_TEST || type == POPUP_TYPE_ERROR)
+ {
+ ppd.PluginWindowProc = DumbPopupDlgProc;
+ }
+
+ if (type == POPUP_TYPE_NORMAL || type == POPUP_TYPE_TEST)
+ {
+ switch (op->popup_delay_type)
+ {
+ case POPUP_DELAY_CUSTOM:
+ ppd.iSeconds = opts.popup_timeout;
+ break;
+
+ case POPUP_DELAY_PERMANENT:
+ ppd.iSeconds = -1;
+ break;
+
+ //case POPUP_DELAY_DEFAULT:
+ default:
+ ppd.iSeconds = 0;
+ break;
+ }
+ }
+ else // if (type == POPUP_TYPE_ERROR)
+ {
+ ppd.iSeconds = 0;
+ }
+
+ // Now that every field has been filled, we want to see the popup.
+ CallService(MS_POPUP_ADDPOPUPW, (WPARAM)&ppd,0);
+ }
+ else
+#endif
+ if(ServiceExists(MS_POPUP_ADDPOPUPEX))
+ {
+ // Make popup
+ POPUPDATAEX ppd;
+
+ ZeroMemory(&ppd, sizeof(ppd));
+ ppd.lchContact = hContact;
+ ppd.lchIcon = (HICON) LoadImage(GetModuleHandle(NULL),MAKEINTRESOURCE(174),IMAGE_ICON,16,16,LR_DEFAULTCOLOR);
+
+ if (title != NULL)
+#ifdef UNICODE
+ mir_snprintf(ppd.lpzContactName, MAX_REGS(ppd.lpzContactName), "%S", title);
+#else
+ lstrcpynA(ppd.lpzContactName, title, MAX_REGS(ppd.lpzContactName));
+#endif
+ else
+ lstrcpynA(ppd.lpzContactName, (char *) CallService(MS_CLIST_GETCONTACTDISPLAYNAME, (WPARAM)hContact, 0),
+ MAX_REGS(ppd.lpzContactName));
+
+ if (description != NULL)
+#ifdef UNICODE
+ mir_snprintf(ppd.lpzText, MAX_REGS(ppd.lpzText), "%S", description);
+#else
+ lstrcpynA(ppd.lpzText, description, MAX_REGS(ppd.lpzText));
+#endif
+
+ if (type == POPUP_TYPE_NORMAL || type == POPUP_TYPE_TEST)
+ {
+ if (op->popup_use_default_colors)
+ {
+ ppd.colorBack = 0;
+ ppd.colorText = 0;
+ }
+ else if (op->popup_use_win_colors)
+ {
+ ppd.colorBack = GetSysColor(COLOR_BTNFACE);
+ ppd.colorText = GetSysColor(COLOR_WINDOWTEXT);
+ }
+ else
+ {
+ ppd.colorBack = op->popup_bkg_color;
+ ppd.colorText = op->popup_text_color;
+ }
+ }
+ else // if (type == POPUP_TYPE_ERROR)
+ {
+ ppd.colorBack = RGB(200,0,0);
+ ppd.colorText = RGB(255,255,255);
+ }
+
+ if (type == POPUP_TYPE_NORMAL)
+ {
+ ppd.PluginWindowProc = PopupDlgProc;
+ ppd.PluginData = plugin_data;
+ }
+ else // if (type == POPUP_TYPE_TEST || type == POPUP_TYPE_ERROR)
+ {
+ ppd.PluginWindowProc = DumbPopupDlgProc;
+ }
+
+ if (type == POPUP_TYPE_NORMAL || type == POPUP_TYPE_TEST)
+ {
+ switch (op->popup_delay_type)
+ {
+ case POPUP_DELAY_CUSTOM:
+ ppd.iSeconds = opts.popup_timeout;
+ break;
+
+ case POPUP_DELAY_PERMANENT:
+ ppd.iSeconds = -1;
+ break;
+
+ case POPUP_DELAY_DEFAULT:
+ default:
+ ppd.iSeconds = 0;
+ break;
+ }
+ }
+ else // if (type == POPUP_TYPE_ERROR)
+ {
+ ppd.iSeconds = 0;
+ }
+
+ // Now that every field has been filled, we want to see the popup.
+ CallService(MS_POPUP_ADDPOPUPEX, (WPARAM)&ppd,0);
+ }
+}
+
+
+// Handle to the hidden windows to handle actions for popup clicks
+// wParam has the number of MOTD in case of WMU_SHOW_MOTD_DETAILS
+LRESULT CALLBACK PopupWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ if (uMsg == WMU_ACTION)
+ {
+// if (lParam == POPUP_ACTION_OPENHISTORY)
+// {
+// CallService(MS_HISTORY_SHOWCONTACTHISTORY, wParam, 0);
+// }
+ }
+ return DefWindowProc(hWnd, uMsg, wParam, lParam);
+}
+
+
+// Handle to popup events
+static LRESULT CALLBACK PopupDlgProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ switch(message) {
+ case WM_COMMAND:
+ {
+ SendMessage(hPopupWindow, WMU_ACTION, (WPARAM)PUGetPluginData(hWnd), opts.popup_left_click_action);
+
+ if (opts.popup_left_click_action != POPUP_ACTION_DONOTHING)
+ PUDeletePopUp(hWnd);
+
+ return TRUE;
+ }
+
+ case WM_CONTEXTMENU:
+ {
+ SendMessage(hPopupWindow, WMU_ACTION, (WPARAM)PUGetPluginData(hWnd), opts.popup_right_click_action);
+
+ if (opts.popup_right_click_action != POPUP_ACTION_DONOTHING)
+ PUDeletePopUp(hWnd);
+
+ return TRUE;
+ }
+
+ case UM_FREEPLUGINDATA:
+ {
+ return TRUE;
+ }
+ }
+
+ return DefWindowProc(hWnd, message, wParam, lParam);
+}
+
+
+// Handle to popup events
+static LRESULT CALLBACK DumbPopupDlgProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ switch(message) {
+ case WM_COMMAND:
+ {
+ PUDeletePopUp(hWnd);
+ return TRUE;
+ }
+
+ case WM_CONTEXTMENU:
+ {
+ PUDeletePopUp(hWnd);
+ return TRUE;
+ }
+
+ case UM_FREEPLUGINDATA:
+ {
+ return TRUE;
+ }
+ }
+
+ return DefWindowProc(hWnd, message, wParam, lParam);
+}
+
diff --git a/Plugins/voiceservice/popup.h b/Plugins/voiceservice/popup.h
new file mode 100644
index 0000000..91de52a
--- /dev/null
+++ b/Plugins/voiceservice/popup.h
@@ -0,0 +1,49 @@
+/*
+Copyright (C) 2005 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#ifndef __POPUP_H__
+# define __POPUP_H__
+
+// Initializations needed by popups
+void InitPopups();
+
+// Deinitializations needed by popups
+void DeInitPopups();
+
+
+#define POPUP_TYPE_NORMAL 0
+#define POPUP_TYPE_TEST 1
+#define POPUP_TYPE_ERROR 2
+
+// Show an popup
+void ShowPopup(HANDLE hContact, const TCHAR *title, const TCHAR *description, ...);
+
+// Show an test
+void ShowTestPopup(const TCHAR *title, const TCHAR *description, const Options *op);
+
+// Show an error popup
+void ShowErrPopup(const char *description, const char *title = NULL);
+
+void ShowPopupEx(HANDLE hContact, const TCHAR *title, const TCHAR *description,
+ void *plugin_data, int type, const Options *op);
+
+
+
+#endif // __POPUP_H__
diff --git a/Plugins/voiceservice/res/Answer.ico b/Plugins/voiceservice/res/Answer.ico
new file mode 100644
index 0000000..1f1c4ee
--- /dev/null
+++ b/Plugins/voiceservice/res/Answer.ico
Binary files differ
diff --git a/Plugins/voiceservice/res/Busy.ico b/Plugins/voiceservice/res/Busy.ico
new file mode 100644
index 0000000..397ecb2
--- /dev/null
+++ b/Plugins/voiceservice/res/Busy.ico
Binary files differ
diff --git a/Plugins/voiceservice/res/Call.ico b/Plugins/voiceservice/res/Call.ico
new file mode 100644
index 0000000..6559874
--- /dev/null
+++ b/Plugins/voiceservice/res/Call.ico
Binary files differ
diff --git a/Plugins/voiceservice/res/Calling.ico b/Plugins/voiceservice/res/Calling.ico
new file mode 100644
index 0000000..6559874
--- /dev/null
+++ b/Plugins/voiceservice/res/Calling.ico
Binary files differ
diff --git a/Plugins/voiceservice/res/Drop.ico b/Plugins/voiceservice/res/Drop.ico
new file mode 100644
index 0000000..48cfa14
--- /dev/null
+++ b/Plugins/voiceservice/res/Drop.ico
Binary files differ
diff --git a/Plugins/voiceservice/res/Hold.ico b/Plugins/voiceservice/res/Hold.ico
new file mode 100644
index 0000000..b789a7b
--- /dev/null
+++ b/Plugins/voiceservice/res/Hold.ico
Binary files differ
diff --git a/Plugins/voiceservice/res/Main.ico b/Plugins/voiceservice/res/Main.ico
new file mode 100644
index 0000000..952d87e
--- /dev/null
+++ b/Plugins/voiceservice/res/Main.ico
Binary files differ
diff --git a/Plugins/voiceservice/res/On hold.ico b/Plugins/voiceservice/res/On hold.ico
new file mode 100644
index 0000000..357bf07
--- /dev/null
+++ b/Plugins/voiceservice/res/On hold.ico
Binary files differ
diff --git a/Plugins/voiceservice/res/Rinning.ico b/Plugins/voiceservice/res/Rinning.ico
new file mode 100644
index 0000000..0f2a5b5
--- /dev/null
+++ b/Plugins/voiceservice/res/Rinning.ico
Binary files differ
diff --git a/Plugins/voiceservice/res/Talking.ico b/Plugins/voiceservice/res/Talking.ico
new file mode 100644
index 0000000..b73bc5d
--- /dev/null
+++ b/Plugins/voiceservice/res/Talking.ico
Binary files differ
diff --git a/Plugins/voiceservice/res/dialpad.ico b/Plugins/voiceservice/res/dialpad.ico
new file mode 100644
index 0000000..45258ed
--- /dev/null
+++ b/Plugins/voiceservice/res/dialpad.ico
Binary files differ
diff --git a/Plugins/voiceservice/res/ended.ico b/Plugins/voiceservice/res/ended.ico
new file mode 100644
index 0000000..397ecb2
--- /dev/null
+++ b/Plugins/voiceservice/res/ended.ico
Binary files differ
diff --git a/Plugins/voiceservice/res/secure.ico b/Plugins/voiceservice/res/secure.ico
new file mode 100644
index 0000000..7386501
--- /dev/null
+++ b/Plugins/voiceservice/res/secure.ico
Binary files differ
diff --git a/Plugins/voiceservice/res/smalldot.ico b/Plugins/voiceservice/res/smalldot.ico
new file mode 100644
index 0000000..47f0556
--- /dev/null
+++ b/Plugins/voiceservice/res/smalldot.ico
Binary files differ
diff --git a/Plugins/voiceservice/resource.h b/Plugins/voiceservice/resource.h
new file mode 100644
index 0000000..cfdbccb
--- /dev/null
+++ b/Plugins/voiceservice/resource.h
@@ -0,0 +1,87 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Developer Studio generated include file.
+// Used by resource.rc
+//
+#define IDR_MENUS 101
+#define IDD_CALLS 102
+#define IDI_TALKING 102
+#define IDI_RINGING 103
+#define IDI_CALLING 104
+#define IDI_ON_HOLD 105
+#define IDI_ENDED 106
+#define IDI_BUSY 107
+#define IDI_ACTION_CALL 108
+#define IDI_ACTION_ANSWER 109
+#define IDI_ACTION_HOLD 110
+#define IDI_ACTION_DROP 111
+#define IDI_MAIN 112
+#define IDI_DIALPAD 113
+#define IDD_NEW_CALL 113
+#define IDI_SECURE 114
+#define IDD_POPUPS 120
+#define IDI_SMALLDOT 211
+#define IDD_OPT_AUTO 215
+#define IDD_OPTS 216
+#define IDD_OPT_DEVICES 217
+#define IDC_CALLS 1000
+#define IDC_DELAY 1001
+#define IDC_TEXT 1001
+#define IDC_WINCOLORS 1002
+#define ID_ANSWER 1002
+#define IDC_DEFAULTCOLORS 1003
+#define ID_DROP 1003
+#define IDC_BGCOLOR 1004
+#define IDC_AUTO 1004
+#define IDC_TEXTCOLOR 1005
+#define IDC_FRAME_AUTOSIZE 1005
+#define IDC_PREV 1006
+#define IDC_NUMBER 1006
+#define IDC_DELAYFROMPU 1007
+#define IDC_CALL 1007
+#define IDC_DELAYCUSTOM 1008
+#define IDC_DIALPAD 1008
+#define IDC_DELAYPERMANENT 1009
+#define IDC_INPUT 1009
+#define IDC_OUTPUT 1010
+#define IDC_ECHO 1011
+#define IDC_MIC_BOOST 1012
+#define IDC_1 1012
+#define IDC_2 1013
+#define IDC_3 1014
+#define IDC_4 1015
+#define IDC_5 1016
+#define IDC_6 1017
+#define IDC_AST 1018
+#define IDC_0 1019
+#define IDC_SHARP 1020
+#define IDC_7 1021
+#define IDC_RIGHT_ACTION 1022
+#define IDC_8 1022
+#define IDC_LEFT_ACTION 1023
+#define IDC_9 1023
+#define IDC_POPUPS 1060
+#define IDC_DELAY_SPIN 1061
+#define IDC_COLOURS_G 1068
+#define IDC_BGCOLOR_L 1069
+#define IDC_TEXTCOLOR_L 1070
+#define IDC_DELAY_G 1071
+#define IDC_ACTIONS_G 1072
+#define IDC_RIGHT_ACTION_L 1073
+#define IDC_LEFT_ACTION_L 1074
+#define IDC_LIST 1079
+#define IDC_ANSWER 1204
+#define IDC_DROP 1205
+#define ID_FRAMEPOPUP_ANSWERCALL 40001
+#define ID_FRAMEPOPUP_DROPCALL 40002
+#define ID_FRAMEPOPUP_HOLDCALL 40003
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 115
+#define _APS_NEXT_COMMAND_VALUE 40004
+#define _APS_NEXT_CONTROL_VALUE 1013
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/Plugins/voiceservice/resource.rc b/Plugins/voiceservice/resource.rc
new file mode 100644
index 0000000..83f3435
--- /dev/null
+++ b/Plugins/voiceservice/resource.rc
@@ -0,0 +1,290 @@
+//Microsoft Developer Studio generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "afxres.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (U.S.) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page(1252)
+#endif //_WIN32
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Dialog
+//
+
+IDD_CALLS DIALOGEX 0, 0, 195, 225
+STYLE DS_FIXEDSYS | WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg", 0, 0, 0x1
+BEGIN
+ LISTBOX IDC_CALLS,0,0,195,126,LBS_SORT | LBS_OWNERDRAWFIXED |
+ LBS_HASSTRINGS | LBS_NOINTEGRALHEIGHT | LBS_NOSEL | NOT
+ WS_BORDER | WS_VSCROLL | WS_TABSTOP
+ CONTROL "",IDC_DIALPAD,"MButtonClass",0x0,0,126,20,14,
+ 0x18000000L
+ EDITTEXT IDC_NUMBER,20,126,155,14,ES_AUTOHSCROLL
+ CONTROL "",IDC_CALL,"MButtonClass",0x0,175,126,20,14,0x18000000L
+ PUSHBUTTON "1",IDC_1,67,143,18,18
+ PUSHBUTTON "2",IDC_2,87,143,18,18
+ PUSHBUTTON "3",IDC_3,107,143,18,18
+ PUSHBUTTON "4",IDC_4,67,163,18,18
+ PUSHBUTTON "5",IDC_5,87,163,18,18
+ PUSHBUTTON "6",IDC_6,107,163,18,18
+ PUSHBUTTON "7",IDC_7,67,183,18,18
+ PUSHBUTTON "8",IDC_8,87,183,18,18
+ PUSHBUTTON "9",IDC_9,107,183,18,18
+ PUSHBUTTON "*",IDC_AST,67,203,18,18
+ PUSHBUTTON "0",IDC_0,87,203,18,18
+ PUSHBUTTON "#",IDC_SHARP,107,203,18,18
+END
+
+IDD_POPUPS DIALOGEX 0, 0, 314, 240
+STYLE DS_FIXEDSYS | WS_CHILD | WS_VISIBLE
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg", 0, 0, 0x1
+BEGIN
+ CONTROL "Enable popups",IDC_POPUPS,"Button",BS_AUTOCHECKBOX |
+ WS_TABSTOP,3,3,308,12
+ GROUPBOX " Colours ",IDC_COLOURS_G,3,25,158,75
+ CONTROL "",IDC_BGCOLOR,"ColourPicker",WS_TABSTOP,11,37,35,14
+ LTEXT "Background colour",IDC_BGCOLOR_L,55,41,66,8
+ CONTROL "",IDC_TEXTCOLOR,"ColourPicker",WS_TABSTOP,11,55,35,14
+ LTEXT "Text colour",IDC_TEXTCOLOR_L,55,59,66,8
+ CONTROL "Use Windows colours",IDC_WINCOLORS,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,11,74,108,10
+ CONTROL "Use default colours",IDC_DEFAULTCOLORS,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,11,84,107,10
+ GROUPBOX " Delay ",IDC_DELAY_G,166,25,145,75
+ CONTROL "From popup plugin",IDC_DELAYFROMPU,"Button",
+ BS_AUTORADIOBUTTON | WS_GROUP,175,38,122,10
+ CONTROL "Custom",IDC_DELAYCUSTOM,"Button",BS_AUTORADIOBUTTON,175,
+ 52,54,10
+ CONTROL "Permanent",IDC_DELAYPERMANENT,"Button",
+ BS_AUTORADIOBUTTON,175,66,122,10
+ EDITTEXT IDC_DELAY,233,50,31,14,ES_AUTOHSCROLL | ES_NUMBER
+ CONTROL "Spin1",IDC_DELAY_SPIN,"msctls_updown32",UDS_SETBUDDYINT |
+ UDS_ALIGNRIGHT | UDS_ARROWKEYS | UDS_NOTHOUSANDS |
+ UDS_HOTTRACK,268,51,11,11
+ GROUPBOX " Actions ",IDC_ACTIONS_G,3,103,308,47
+ RTEXT "On right click:",IDC_RIGHT_ACTION_L,13,118,62,9
+ COMBOBOX IDC_RIGHT_ACTION,83,116,156,60,CBS_DROPDOWNLIST |
+ WS_VSCROLL | WS_TABSTOP
+ RTEXT "On left click:",IDC_LEFT_ACTION_L,13,132,62,9
+ COMBOBOX IDC_LEFT_ACTION,83,132,156,60,CBS_DROPDOWNLIST |
+ WS_VSCROLL | WS_TABSTOP
+ PUSHBUTTON "Preview",IDC_PREV,131,161,50,14
+END
+
+IDD_NEW_CALL DIALOG DISCARDABLE 0, 0, 229, 87
+STYLE DS_MODALFRAME | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_CAPTION |
+ WS_SYSMENU
+CAPTION "New Voice Call"
+FONT 8, "MS Shell Dlg"
+BEGIN
+ CTEXT "%s wants to start a voice call with you. What you want to do?\n\nIf you answer the call, the current call will be put on hold.",
+ IDC_TEXT,7,7,215,36
+ DEFPUSHBUTTON "Answer",ID_ANSWER,58,47,50,14
+ PUSHBUTTON "Drop",ID_DROP,121,47,50,14
+ CONTROL "From now on, repeat this action for this contact",
+ IDC_AUTO,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,67,215,
+ 12
+END
+
+IDD_OPTS DIALOGEX 0, 0, 233, 41
+STYLE DS_FIXEDSYS | WS_CHILD | WS_VISIBLE
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg", 0, 0, 0x1
+BEGIN
+ GROUPBOX "Frame",IDC_STATIC,3,3,227,30
+ CONTROL "Auto-size frame",IDC_FRAME_AUTOSIZE,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,11,15,214,12
+END
+
+IDD_OPT_AUTO DIALOGEX 0, 0, 313, 240
+STYLE DS_FIXEDSYS | WS_CHILD | WS_VISIBLE
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg", 0, 0, 0x1
+BEGIN
+ GROUPBOX "Automatic Actions",IDC_STATIC,0,0,313,240
+ CONTROL "",IDC_LIST,"CListControl",WS_TABSTOP | 0x1f0,8,14,297,
+ 183,WS_EX_CLIENTEDGE
+ ICON IDI_ACTION_ANSWER,IDC_ANSWER,8,204,20,20
+ LTEXT "Auto accept this contact calls",IDC_STATIC,26,205,279,8,
+ SS_NOPREFIX | SS_CENTERIMAGE
+ ICON IDI_ACTION_DROP,IDC_DROP,8,219,20,20
+ LTEXT "Auto drop this contacts calls",IDC_STATIC,26,220,279,8,
+ SS_NOPREFIX | SS_CENTERIMAGE
+END
+
+IDD_OPT_DEVICES DIALOGEX 0, 0, 295, 221
+STYLE DS_FIXEDSYS | WS_CHILD | WS_VISIBLE
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg", 0, 0, 0x1
+BEGIN
+ GROUPBOX "Audio",IDC_STATIC,3,3,289,78
+ LTEXT "Input:",IDC_STATIC,11,16,42,12
+ COMBOBOX IDC_INPUT,58,15,228,13,CBS_DROPDOWNLIST | CBS_SORT |
+ WS_VSCROLL | WS_TABSTOP
+ LTEXT "Output:",IDC_STATIC,11,33,42,12
+ COMBOBOX IDC_OUTPUT,58,32,228,13,CBS_DROPDOWNLIST | CBS_SORT |
+ WS_VSCROLL | WS_TABSTOP
+ CONTROL "Echo cancelation",IDC_ECHO,"Button",BS_AUTOCHECKBOX |
+ WS_TABSTOP,11,49,275,12
+ CONTROL "Microphone boost",IDC_MIC_BOOST,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,11,64,275,12
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// DESIGNINFO
+//
+
+#ifdef APSTUDIO_INVOKED
+GUIDELINES DESIGNINFO DISCARDABLE
+BEGIN
+ IDD_POPUPS, DIALOG
+ BEGIN
+ LEFTMARGIN, 3
+ RIGHTMARGIN, 311
+ TOPMARGIN, 3
+ BOTTOMMARGIN, 237
+ END
+
+ IDD_NEW_CALL, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 222
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 80
+ END
+
+ IDD_OPTS, DIALOG
+ BEGIN
+ LEFTMARGIN, 3
+ RIGHTMARGIN, 230
+ TOPMARGIN, 3
+ BOTTOMMARGIN, 38
+ END
+
+ IDD_OPT_AUTO, DIALOG
+ BEGIN
+ VERTGUIDE, 8
+ VERTGUIDE, 305
+ END
+
+ IDD_OPT_DEVICES, DIALOG
+ BEGIN
+ LEFTMARGIN, 3
+ RIGHTMARGIN, 292
+ TOPMARGIN, 3
+ BOTTOMMARGIN, 218
+ END
+END
+#endif // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Menu
+//
+
+IDR_MENUS MENU DISCARDABLE
+BEGIN
+ POPUP "Frame Popup"
+ BEGIN
+ MENUITEM "Answer call", ID_FRAMEPOPUP_ANSWERCALL
+ MENUITEM "Drop call", ID_FRAMEPOPUP_DROPCALL
+ MENUITEM "Hold call", ID_FRAMEPOPUP_HOLDCALL
+ END
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Icon
+//
+
+// Icon with lowest ID value placed first to ensure application icon
+// remains consistent on all systems.
+IDI_TALKING ICON DISCARDABLE "res\\Talking.ico"
+IDI_RINGING ICON DISCARDABLE "res\\Rinning.ico"
+IDI_ENDED ICON DISCARDABLE "res\\ended.ico"
+IDI_BUSY ICON DISCARDABLE "res\\Busy.ico"
+IDI_ON_HOLD ICON DISCARDABLE "res\\On hold.ico"
+IDI_ACTION_CALL ICON DISCARDABLE "res\\Call.ico"
+IDI_ACTION_ANSWER ICON DISCARDABLE "res\\Answer.ico"
+IDI_ACTION_HOLD ICON DISCARDABLE "res\\Hold.ico"
+IDI_ACTION_DROP ICON DISCARDABLE "res\\Drop.ico"
+IDI_MAIN ICON DISCARDABLE "res\\Main.ico"
+IDI_CALLING ICON DISCARDABLE "res\\Calling.ico"
+IDI_DIALPAD ICON DISCARDABLE "res\\dialpad.ico"
+IDI_SMALLDOT ICON DISCARDABLE "res/smalldot.ico"
+IDI_SECURE ICON DISCARDABLE "res\\secure.ico"
+#endif // English (U.S.) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+/////////////////////////////////////////////////////////////////////////////
+// Portuguese (Brazil) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_PTB)
+#ifdef _WIN32
+LANGUAGE LANG_PORTUGUESE, SUBLANG_PORTUGUESE_BRAZILIAN
+#pragma code_page(1252)
+#endif //_WIN32
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "resource.h\0"
+END
+
+2 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "#include ""afxres.h""\r\n"
+ "\0"
+END
+
+3 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "\r\n"
+ "\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+#endif // Portuguese (Brazil) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
+
diff --git a/Plugins/voiceservice/sdk/m_cluiframes.h b/Plugins/voiceservice/sdk/m_cluiframes.h
new file mode 100644
index 0000000..1d6cd4b
--- /dev/null
+++ b/Plugins/voiceservice/sdk/m_cluiframes.h
@@ -0,0 +1,254 @@
+/*
+Miranda ICQ: the free icq client for MS Windows
+Copyright (C) 2000-2 Richard Hughes, Roland Rabien & Tristan Van de Vreede
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+///#include "commonheaders.h"
+//#pragma hdrstop
+
+////////////////////////////////////
+//Extra Image Column Support +0.5.0.0
+
+//Extra columns type.
+//column arranged in this way
+//
+// [statusicon] ContactName [WEB][ADV1][ADV2][SMS][EMAIL][PROTO][CLIENT]
+//
+#define EXTRA_ICON_EMAIL 1
+#define EXTRA_ICON_PROTO 2
+#define EXTRA_ICON_SMS 3
+#define EXTRA_ICON_ADV1 4
+#define EXTRA_ICON_ADV2 5
+#define EXTRA_ICON_WEB 6
+#define EXTRA_ICON_CLIENT 7
+#define EXTRA_ICON_VISMODE 8
+#define EXTRA_ICON_ADV3 9
+#define EXTRA_ICON_ADV4 10
+
+#define EXTRA_ICON_COUNT 10
+
+typedef struct
+{
+int cbSize; //must be sizeof(IconExtraColumn)
+int ColumnType;
+HANDLE hImage; //return value from MS_CLIST_EXTRA_ADD_ICON
+}IconExtraColumn,*pIconExtraColumn;
+
+
+//Set icon for contact at needed column
+//wparam=hContact
+//lparam=pIconExtraColumn
+//return 0 on success,-1 on failure
+//
+//See above for supported columns
+#define MS_CLIST_EXTRA_SET_ICON "CListFrames/SetIconForExraColumn"
+
+//Adding icon to extra image list.
+//Call this in ME_CLIST_EXTRA_LIST_REBUILD event
+//
+//wparam=hIcon
+//lparam=0
+//return hImage on success,-1 on failure
+#define MS_CLIST_EXTRA_ADD_ICON "CListFrames/AddIconToExtraImageList"
+
+
+
+#define ME_CLIST_EXTRA_LIST_REBUILD "CListFrames/OnExtraListRebuild"
+
+//called with wparam=hContact
+#define ME_CLIST_EXTRA_IMAGE_APPLY "CListFrames/OnExtraImageApply"
+
+
+///////////////////////////////////
+
+
+
+
+//
+//want show tooltip for statusbar
+//wparam=(char *)protocolname
+//lparam=0
+#define ME_CLIST_FRAMES_SB_SHOW_TOOLTIP "CListFrames/StatusBarShowToolTip"
+
+
+//want hide tooltip for statusbar
+//wparam=lparam=0
+
+#define ME_CLIST_FRAMES_SB_HIDE_TOOLTIP "CListFrames/StatusBarHideToolTip"
+
+//
+
+
+
+//adds a frame window
+//wParam=(CLISTFrame*)
+//lParam=0
+//returns an integer, the frame id.
+typedef struct tagCLISTFrame {
+ DWORD cbSize;
+ HWND hWnd ;
+ HICON hIcon;
+ int align; //al flags below
+ int height;
+ int Flags; //F_flags below
+ char *name; //frame window name,will be shown in menu
+ char *TBname; //titlebar caption
+ //COLORREF TBBackColour; //titlebar background colour
+} CLISTFrame;
+#define F_VISIBLE 1 //Frame visible
+#define F_SHOWTB 2 //Show TitleBar
+#define F_UNCOLLAPSED 4 //UnCollapse frame
+#define F_LOCKED 8 //Lock Frame
+#define F_NOBORDER 16 //Dont apply WS_BORDER style for window
+#define F_SHOWTBTIP 32 //Show titlebar tooltip
+#define F_NO_SUBCONTAINER 1024 //Support skining no subcontainer needed
+
+
+// frame alignment
+#define alTop 0x00000001
+#define alBottom 0x00000002
+#define alClient 0x00000004 //only one alClient frame
+#define MS_CLIST_FRAMES_ADDFRAME "CListFrames/AddFrame"
+
+#define MS_CLIST_FRAMES_REMOVEFRAME "CListFrames/RemoveFrame"
+
+//shows all frames
+//wParam=lParam=0
+//returns 0 on success, -1 on failure
+#define MS_CLIST_FRAMES_SHOWALLFRAMES "CListFrames/ShowALLFrames"
+
+//shows the titlebars of all frames
+//wParam=lParam=0
+//returns 0 on success, -1 on failure
+#define MS_CLIST_FRAMES_SHOWALLFRAMESTB "CListFrames/ShowALLFramesTB"
+
+//hides the titlebars of all frames
+//wParam=lParam=0
+//returns 0 on success, -1 on failure
+#define MS_CLIST_FRAMES_HIDEALLFRAMESTB "CListFrames/HideALLFramesTB"
+
+//shows the frame if it is hidden,
+//hides the frame if it is shown
+//wParam=FrameId
+//lParam=0
+//returns 0 on success, -1 on failure
+#define MS_CLIST_FRAMES_SHFRAME "CListFrames/SHFrame"
+
+//shows the frame titlebar if it is hidden,
+//hides the frame titlebar if it is shown
+//wParam=FrameId
+//lParam=0
+//returns 0 on success, -1 on failure
+#define MS_CLIST_FRAMES_SHFRAMETITLEBAR "CListFrame/SHFrameTitleBar"
+
+//locks the frame if it is unlocked,
+//unlock the frame if it is locked
+//wParam=FrameId
+//lParam=0
+//returns 0 on success, -1 on failure
+#define MS_CLIST_FRAMES_ULFRAME "CListFrame/ULFrame"
+
+//collapses the frame if it is uncollapsed,
+//uncollapses the frame if it is collapsed
+//wParam=FrameId
+//lParam=0
+//returns 0 on success, -1 on failure
+#define MS_CLIST_FRAMES_UCOLLFRAME "CListFrame/UCOLLFrame"
+
+//trigger border flags
+//wparam=frameid
+//lparam=0
+#define MS_CLIST_FRAMES_SETUNBORDER "CListFrame/SetUnBorder"
+
+//redraws the frame
+//wParam=FrameId, -1 for all frames
+//lparam=FU_flags
+//returns a pointer to option, -1 on failure
+#define FU_TBREDRAW 1 //redraw titlebar
+#define FU_FMREDRAW 2 //redraw Frame
+#define FU_FMPOS 4 //update Frame position
+#define MS_CLIST_FRAMES_UPDATEFRAME "CListFrame/UpdateFrame"
+
+//gets the frame options
+//(HIWORD)wParam=FrameId
+//(LOWORD)wParam=FO_flag
+//lParam=0
+//returns a pointer to option, -1 on failure
+#define FO_FLAGS 0x0001 //return set of F_VISIBLE,F_SHOWTB,F_UNCOLLAPSED,F_LOCKED,F_NOBORDER,F_SHOWTBTIP
+#define FO_NAME 0x0002 //Change name
+#define FO_TBNAME 0x0003 //Change TB caption
+#define FO_TBSTYLE 0x0004 //Change TB style
+#define FO_TBEXSTYLE 0x0005 //Change TB exstyle
+#define FO_ICON 0x0006 //Change icon
+#define FO_HEIGHT 0x0007 //Change height
+#define FO_ALIGN 0x0008 //Change align
+#define FO_TBTIPNAME 0x0009 //Change TB tooltip
+#define FO_FLOATING 0x000a //Change floating mode
+
+#define MS_CLIST_FRAMES_GETFRAMEOPTIONS "CListFrame/GetFrameOptions"
+
+//sets the frame options
+//(HIWORLD)wParam=FrameId
+//(LOWORD)wParam=FO_flag
+//lParam=value
+//returns 0 on success, -1 on failure
+#define MS_CLIST_FRAMES_SETFRAMEOPTIONS "CListFrame/SetFrameOptions"
+
+
+//menu stuff
+
+//add a new item to the context frame menu
+//wParam=0
+//lParam=(LPARAM)(CLISTMENUITEM*)&mi
+//returns a handle to the new item
+//popupposition=frameid
+//contactowner=advanced parameter
+#define MS_CLIST_ADDCONTEXTFRAMEMENUITEM "CList/AddContextFrameMenuItem"
+
+//remove a item from context frame menu
+//wParam=hMenuItem returned by MS_CLIST_ADDCONTACTMENUITEM
+//lParam=0
+//returns 0 on success, nonzero on failure
+#define MS_CLIST_REMOVECONTEXTFRAMEMENUITEM "CList/RemoveContextFrameMenuItem"
+
+//builds the context menu for a frame
+//wparam=frameid
+//lParam=0
+//returns a HMENU on success, or NULL on failure
+#define MS_CLIST_MENUBUILDFRAMECONTEXT "CList/BuildContextFrameMenu"
+
+/*
+//the frame menu is about to be built
+wparam=frameid
+lparam=
+-1 for build from titlebar,
+ use
+ MS_CLIST_ADDCONTEXTFRAMEMENUITEM
+ MS_CLIST_REMOVECONTEXTFRAMEMENUITEM
+
+>0 for build in main menu,
+must be popupname=lparam to place your items in right popup of main menu.
+ use
+ MS_CLIST_ADDMAINMENUITEM
+ MS_CLIST_REMOVEMAINMENUITEM
+
+*/
+#define ME_CLIST_PREBUILDFRAMEMENU "CList/PreBuildFrameMenu"
+
+//needed by cluiframes module to add frames menu to main menu.
+//it just calls NotifyEventHooks(hPreBuildFrameMenuEvent,wParam,lParam);
+#define MS_CLIST_FRAMEMENUNOTIFY "CList/ContextFrameMenuNotify"
diff --git a/Plugins/voiceservice/sdk/m_folders.h b/Plugins/voiceservice/sdk/m_folders.h
new file mode 100644
index 0000000..c232410
--- /dev/null
+++ b/Plugins/voiceservice/sdk/m_folders.h
@@ -0,0 +1,205 @@
+/*
+Custom profile folders plugin for Miranda IM
+
+Copyright © 2005 Cristian Libotean
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef M_CUSTOM_FOLDERS_H
+#define M_CUSTOM_FOLDERS_H
+
+#define FOLDERS_API 501 //dunno why it's here but it is :)
+
+#define PROFILE_PATH "%profile_path%"
+#define CURRENT_PROFILE "%current_profile%"
+#define MIRANDA_PATH "%miranda_path%"
+#define PLUGINS_PATH "%miranda_path%" "\\plugins"
+
+#define TO_WIDE(x) L ## x
+
+#define PROFILE_PATHW L"%profile_path%"
+#define CURRENT_PROFILEW L"%current_profile%"
+#define MIRANDA_PATHW L"%miranda_path%"
+
+#define FOLDER_AVATARS PROFILE_PATH "\\" CURRENT_PROFILE "\\avatars"
+#define FOLDER_VCARDS PROFILE_PATH "\\" CURRENT_PROFILE "\\vcards"
+#define FOLDER_LOGS PROFILE_PATH "\\" CURRENT_PROFILE "\\logs"
+#define FOLDER_RECEIVED_FILES PROFILE_PATH "\\" CURRENT_PROFILE "\\received files"
+#define FOLDER_DOCS MIRANDA_PATH "\\" "docs"
+
+#define FOLDER_CONFIG PLUGINS_PATH "\\" "config"
+
+#define FOLDER_SCRIPTS MIRANDA_PATH "\\" "scripts"
+
+#define FOLDER_UPDATES MIRANDA_PATH "\\" "updates"
+
+#define FOLDER_CUSTOMIZE MIRANDA_PATH "\\" "customize"
+#define FOLDER_CUSTOMIZE_SOUNDS FOLDER_CUSTOMIZE "\\sounds"
+#define FOLDER_CUSTOMIZE_ICONS FOLDER_CUSTOMIZE "\\icons"
+#define FOLDER_CUSTOMIZE_SMILEYS FOLDER_CUSTOMIZE "\\smileys"
+#define FOLDER_CUSTOMIZE_SKINS FOLDER_CUSTOMIZE "\\skins"
+#define FOLDER_CUSTOMIZE_THEMES FOLDER_CUSTOMIZE "\\themes"
+
+
+#define FOLDERS_NAME_MAX_SIZE 64 //maximum name and section size
+
+#define FF_UNICODE 0x00000001
+
+typedef struct{
+ int cbSize; //size of struct
+ char szSection[FOLDERS_NAME_MAX_SIZE]; //section name, if it doesn't exist it will be created otherwise it will just add this entry to it
+ char szName[FOLDERS_NAME_MAX_SIZE]; //entry name - will be shown in options
+ union{
+ const char *szFormat; //default string format. Fallback string in case there's no entry in the database for this folder. This should be the initial value for the path, users will be able to change it later.
+ const wchar_t *szFormatW; //String is dup()'d so you can free it later. If you set the unicode string don't forget to set the flag accordingly.
+ const TCHAR *szFormatT;
+ };
+ DWORD flags; //FF_* flags
+} FOLDERSDATA;
+
+/*Folders/Register/Path service
+ wParam - not used, must be 0
+ lParam - (LPARAM) (const FOLDERDATA *) - Data structure filled with
+ the necessary information.
+ Returns a handle to the registered path or 0 on error.
+ You need to use this to call the other services.
+*/
+#define MS_FOLDERS_REGISTER_PATH "Folders/Register/Path"
+
+/*Folders/Get/PathSize service
+ wParam - (WPARAM) (int) - handle to registered path
+ lParam - (LPARAM) (int *) - pointer to the variable that receives the size of the path
+ string (not including the null character). Depending on the flags set when creating the path
+ it will either call strlen() or wcslen() to get the length of the string.
+ Returns the size of the buffer.
+*/
+#define MS_FOLDERS_GET_SIZE "Folders/Get/PathSize"
+
+typedef struct{
+ int cbSize;
+ int nMaxPathSize; //maximum size of buffer. This represents the number of characters that can be copied to it (so for unicode strings you don't send the number of bytes but the length of the string).
+ union{
+ char *szPath; //pointer to the buffer that receives the path without the last "\\"
+ wchar_t *szPathW; //unicode version of the buffer.
+ TCHAR *szPathT;
+ };
+} FOLDERSGETDATA;
+
+/*Folders/Get/Path service
+ wParam - (WPARAM) (int) - handle to registered path
+ lParam - (LPARAM) (FOLDERSGETDATA *) pointer to a FOLDERSGETDATA that has all the relevant fields filled.
+ Should return 0 on success, or nonzero otherwise.
+*/
+#define MS_FOLDERS_GET_PATH "Folders/Get/Path"
+
+typedef struct{
+ int cbSize;
+ union{
+ char **szPath; //address of a string variable (char *) or (wchar_t*) where the path should be stored (the last \ won't be copied).
+ wchar_t **szPathW; //unicode version of string.
+ TCHAR **szPathT;
+ };
+} FOLDERSGETALLOCDATA;
+
+/*Folders/GetRelativePath/Alloc service
+ wParam - (WPARAM) (int) - Handle to registered path
+ lParam - (LPARAM) (FOLDERSALLOCDATA *) data
+ This service is the same as MS_FOLDERS_GET_PATH with the difference that this service
+ allocates the needed space for the buffer. It uses miranda's memory functions for that and you need
+ to use those to free the resulting buffer.
+ Should return 0 on success, or nonzero otherwise. Currently it only returns 0.
+*/
+#define MS_FOLDERS_GET_PATH_ALLOC "Folders/Get/Path/Alloc"
+
+
+/*Folders/On/Path/Changed
+ wParam - (WPARAM) 0
+ lParam - (LPARAM) 0
+ Triggered when the folders change, you should reget the paths you registered.
+*/
+#define ME_FOLDERS_PATH_CHANGED "Folders/On/Path/Changed"
+
+#ifndef FOLDERS_NO_HELPER_FUNCTIONS
+//#include "../../../include/newpluginapi.h"
+
+__inline static int FoldersRegisterCustomPath(const char *section, const char *name, const char *defaultPath)
+{
+ FOLDERSDATA fd = {0};
+ if (!ServiceExists(MS_FOLDERS_REGISTER_PATH)) return 1;
+ fd.cbSize = sizeof(FOLDERSDATA);
+ strncpy(fd.szSection, section, FOLDERS_NAME_MAX_SIZE);
+ fd.szSection[FOLDERS_NAME_MAX_SIZE - 1] = '\0';
+ strncpy(fd.szName, name, FOLDERS_NAME_MAX_SIZE);
+ fd.szName[FOLDERS_NAME_MAX_SIZE - 1] = '\0';
+ fd.szFormat = defaultPath;
+ return CallService(MS_FOLDERS_REGISTER_PATH, 0, (LPARAM) &fd);
+}
+
+__inline static int FoldersRegisterCustomPathW(const char *section, const char *name, const wchar_t *defaultPathW)
+{
+ FOLDERSDATA fd = {0};
+ if (!ServiceExists(MS_FOLDERS_REGISTER_PATH)) return 1;
+ fd.cbSize = sizeof(FOLDERSDATA);
+ strncpy(fd.szSection, section, FOLDERS_NAME_MAX_SIZE);
+ fd.szSection[FOLDERS_NAME_MAX_SIZE - 1] = '\0'; //make sure it's NULL terminated
+ strncpy(fd.szName, name, FOLDERS_NAME_MAX_SIZE);
+ fd.szName[FOLDERS_NAME_MAX_SIZE - 1] = '\0'; //make sure it's NULL terminated
+ fd.szFormatW = defaultPathW;
+ fd.flags = FF_UNICODE;
+ return CallService(MS_FOLDERS_REGISTER_PATH, 0, (LPARAM) &fd);
+}
+
+__inline static int FoldersGetCustomPath(HANDLE hFolderEntry, char *path, const int size, char *notFound)
+{
+ FOLDERSGETDATA fgd = {0};
+ fgd.cbSize = sizeof(FOLDERSGETDATA);
+ fgd.nMaxPathSize = size;
+ fgd.szPath = path;
+ int res = CallService(MS_FOLDERS_GET_PATH, (WPARAM) hFolderEntry, (LPARAM) &fgd);
+ if (res)
+ {
+ strncpy(path, notFound, size);
+ path[size - 1] = '\0'; //make sure it's NULL terminated
+ }
+ return res;
+}
+
+__inline static int FoldersGetCustomPathW(HANDLE hFolderEntry, wchar_t *pathW, const int count, wchar_t *notFoundW)
+{
+ FOLDERSGETDATA fgd = {0};
+ fgd.cbSize = sizeof(FOLDERSGETDATA);
+ fgd.nMaxPathSize = count;
+ fgd.szPathW = pathW;
+ int res = CallService(MS_FOLDERS_GET_PATH, (WPARAM) hFolderEntry, (LPARAM) &fgd);
+ if (res)
+ {
+ wcsncpy(pathW, notFoundW, count);
+ pathW[count - 1] = '\0';
+ }
+ return res;
+}
+
+# ifdef _UNICODE
+# define FoldersGetCustomPathT FoldersGetCustomPathW
+# define FoldersRegisterCustomPathT FoldersRegisterCustomPathW
+#else
+# define FoldersGetCustomPathT FoldersGetCustomPath
+# define FoldersRegisterCustomPathT FoldersRegisterCustomPath
+#endif
+
+#endif
+
+#endif //M_CUSTOM_FOLDERS_H \ No newline at end of file
diff --git a/Plugins/voiceservice/sdk/m_historyevents.h b/Plugins/voiceservice/sdk/m_historyevents.h
new file mode 100644
index 0000000..f31f31e
--- /dev/null
+++ b/Plugins/voiceservice/sdk/m_historyevents.h
@@ -0,0 +1,459 @@
+/*
+Copyright (C) 2006 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#ifndef __M_HISTORYEVENTS_H__
+# define __M_HISTORYEVENTS_H__
+
+
+#define MIID_HISTORYEVENTS { 0xc8be8543, 0x6618, 0x4030, { 0x85, 0xcf, 0x90, 0x82, 0xc7, 0xde, 0x7f, 0xf7 } }
+
+
+#define HISTORYEVENTS_FORMAT_CHAR 1
+#define HISTORYEVENTS_FORMAT_WCHAR 2
+#define HISTORYEVENTS_FORMAT_RICH_TEXT 4
+#define HISTORYEVENTS_FORMAT_HTML 8
+
+#ifdef UNICODE
+# define HISTORYEVENTS_FORMAT_TCHAR HISTORYEVENTS_FORMAT_WCHAR
+#else
+# define HISTORYEVENTS_FORMAT_TCHAR HISTORYEVENTS_FORMAT_CHAR
+#endif
+
+#define HISTORYEVENTS_FLAG_DEFAULT (1 << 0) // Is a miranda core event type
+#define HISTORYEVENTS_FLAG_SHOW_IM_SRMM (1 << 1) // If this event has to be shown in srmm dialog
+#define HISTORYEVENTS_FLAG_USE_SENT_FLAG (1 << 2) // Means that it can be a sent or received and uses DBEF_SENT to mark that
+#define HISTORYEVENTS_FLAG_EXPECT_CONTACT_NAME_BEFORE (1 << 3) // Means that who is drawing this should draw the contact name before the text
+#define HISTORYEVENTS_FLAG_ONLY_LOG_IF_SRMM_OPEN (1 << 4) // If this event will be logged only if the message window is open
+#define HISTORYEVENTS_FLAG_FLASH_MSG_WINDOW (1 << 5) // If this event will trigger the openning/flashing of the message window
+#define HISTORYEVENTS_REGISTERED_IN_ICOLIB (9 << 16) // If the icon is a name already registered in icolib
+#define HISTORYEVENTS_FLAG_KEEP_ONE_YEAR (1 << 8) // By default store in db for 1 year
+#define HISTORYEVENTS_FLAG_KEEP_SIX_MONTHS (2 << 8) // By default store in db for 6 months
+#define HISTORYEVENTS_FLAG_KEEP_ONE_MONTH (3 << 8) // By default store in db for 1 month
+#define HISTORYEVENTS_FLAG_KEEP_ONE_WEEK (4 << 8) // By default store in db for 1 week
+#define HISTORYEVENTS_FLAG_KEEP_ONE_DAY (5 << 8) // By default store in db for 1 day
+#define HISTORYEVENTS_FLAG_KEEP_FOR_SRMM (6 << 8) // By default store in db only enought for message log
+#define HISTORYEVENTS_FLAG_KEEP_MAX_TEN (7 << 8) // By default store in db max 10 entries
+#define HISTORYEVENTS_FLAG_KEEP_MAX_HUNDRED (8 << 8) // By default store in db for 100 entries
+#define HISTORYEVENTS_FLAG_KEEP_DONT (9 << 8) // By default don't store in db (aka ignore it)
+
+
+// This function must be implemented by subscribers. It must return a pointer or NULL
+// to say it can't handle the text
+typedef void * (*fGetHistoryEventText)(HANDLE hContact, HANDLE hDbEvent, DBEVENTINFO *dbe, int format);
+
+typedef struct {
+ int cbSize;
+ char *module;
+ char *name; // Internal event name
+ char *description; // Will be translated. When retrieving it is already translated
+ WORD eventType; // The event type it can handle
+ union {
+ HICON defaultIcon;
+ char * defaultIconName; // if HISTORYEVENTS_REGISTERED_IN_ICOLIB is set. Always use this one when retrieving
+ };
+ int supports; // What kind of return is supported - or of HISTORYEVENTS_FORMAT_*
+ int flags; // or of HISTORYEVENTS_FLAG_*
+ fGetHistoryEventText pfGetHistoryEventText; // NULL to use default get text (similar to message, without extra format)
+
+ // Aditional data if wants to use add to history services
+ char **templates; // Each entry is: "Name\nDefault\n%var%\tDescription\n%var%\tDescription\n%var%\tDescription"
+ int numTemplates;
+
+} HISTORY_EVENT_HANDLER;
+
+
+/*
+Get the number of registered events
+
+wParam: ignored
+lParam: ignored
+Return: The number of events registered with the plugin
+*/
+#define MS_HISTORYEVENTS_GET_COUNT "HistoryEvents/GetCount"
+
+
+/*
+Get an event by number or by type.
+To retrieve by number, pass -1 as type. To retrieve by type, pass -1 as number.
+
+wParam: (int) event number
+lParam: (int) event type
+Return: (const HISTORY_EVENT_HANDLER *) if the event exists, NULL otherwise. Don't change the
+ returned strunc: it is a pointer to the internall struct.
+*/
+#define MS_HISTORYEVENTS_GET_EVENT "HistoryEvents/GetEvent"
+
+
+/*
+Register a plugin that can handle an event type. This must be called during the call to the
+Load function of the plugin. In ModulesLoaded callback all plugins have to be already registered,
+so srmm and history modules can query then.
+
+wParam: HISTORY_EVENT_HANDLER *
+lParam: ignored
+Return: 0 for success
+*/
+#define MS_HISTORYEVENTS_REGISTER "HistoryEvents/Register"
+
+
+typedef struct {
+ int cbSize;
+ HANDLE hDbEvent;
+ DBEVENTINFO *dbe; // Optional
+ int format; // one of HISTORYEVENTS_FORMAT_*
+
+} HISTORY_EVENT_PARAM;
+
+/*
+Check if an event can be handled by any subscribers
+
+wParam: WORD - event type
+lParam: ignored
+Return: BOOL
+*/
+#define MS_HISTORYEVENTS_CAN_HANDLE "HistoryEvents/CanHandle"
+
+/*
+Get the icon for a history event type
+
+wParam: WORD - event type
+lParam: BOOL - TRUE to copy the icon (should be released with DestroyObject),
+ FALSE to use icolib one (should be released with MS_HISTORYEVENTS_RELEASE_ICON)
+Return: HICON
+*/
+#define MS_HISTORYEVENTS_GET_ICON "HistoryEvents/GetIcon"
+
+/*
+Get the flags for a history event type
+
+wParam: WORD - event type
+lParam: ignored
+Return: int - or of HISTORYEVENTS_FLAG_* or -1 if error
+*/
+#define MS_HISTORYEVENTS_GET_FLAGS "HistoryEvents/GetFlags"
+
+/*
+Release the icon for a history event type. This is really just a forward to icolib
+
+wParam: HICON
+lParam: ignored
+*/
+#define MS_HISTORYEVENTS_RELEASE_ICON "Skin2/Icons/ReleaseIcon"
+
+/*
+Get the text for a history event type
+
+wParam: HISTORY_EVENT_PARAM *
+lParam: ignored
+Return: char * or wchar * depending on sent flags. Free with mir_free or MS_HISTORYEVENTS_RELEASE_TEXT
+*/
+#define MS_HISTORYEVENTS_GET_TEXT "HistoryEvents/GetText"
+
+/*
+Release the text for a history event type. Internally is just a call to mir_free
+
+wParam: char * or wchar *
+lParam: ignored
+*/
+#define MS_HISTORYEVENTS_RELEASE_TEXT "HistoryEvents/ReleaseText"
+
+
+
+typedef struct {
+ int cbSize;
+ HANDLE hContact;
+ WORD eventType;
+ int templateNum;
+ TCHAR **variables;
+ int numVariables;
+ PBYTE additionalData;
+ int additionalDataSize;
+ int flags; // Flags for the event type
+ DWORD timestamp; // 0 for now
+ BOOL addToMetaToo;
+} HISTORY_EVENT_ADD;
+
+/*
+Add an registered event to the history. This is a helper service
+
+wParam: HISTORY_EVENT_ADD
+lParam: ignored
+Return: HANDLE to the db event
+*/
+#define MS_HISTORYEVENTS_ADD_TO_HISTORY "HistoryEvents/AddToHistory"
+
+/*
+Check if a template is enabled
+
+wParam: event type
+lParam: template num
+Return: TRUE or FALSE
+*/
+#define MS_HISTORYEVENTS_IS_ENABLED_TEMPLATE "HistoryEvents/IsEnabledTemplate"
+
+
+
+// Helper functions //////////////////////////////////////////////////////////////////////////////
+
+
+
+
+static int HistoryEvents_Register(char *module, char *name, char *description, int eventType, HICON defaultIcon,
+ int supports, int flags, fGetHistoryEventText pfGetHistoryEventText)
+{
+ HISTORY_EVENT_HANDLER heh = {0};
+
+ if (!ServiceExists(MS_HISTORYEVENTS_REGISTER))
+ return 1;
+
+ heh.cbSize = sizeof(heh);
+ heh.module = module;
+ heh.name = name;
+ heh.description = description;
+ heh.eventType = eventType;
+ heh.defaultIcon = defaultIcon;
+ heh.supports = supports;
+ heh.flags = flags;
+ heh.pfGetHistoryEventText = pfGetHistoryEventText;
+ return CallService(MS_HISTORYEVENTS_REGISTER, (WPARAM) &heh, 0);
+}
+
+static int HistoryEvents_RegisterWithTemplates(char *module, char *name, char *description, int eventType, HICON defaultIcon,
+ int supports, int flags, fGetHistoryEventText pfGetHistoryEventText,
+ char **templates, int numTemplates)
+{
+ HISTORY_EVENT_HANDLER heh = {0};
+
+ if (!ServiceExists(MS_HISTORYEVENTS_REGISTER))
+ return 1;
+
+ heh.cbSize = sizeof(heh);
+ heh.module = module;
+ heh.name = name;
+ heh.description = description;
+ heh.eventType = eventType;
+ heh.defaultIcon = defaultIcon;
+ heh.supports = supports;
+ heh.flags = flags;
+ heh.pfGetHistoryEventText = pfGetHistoryEventText;
+ heh.templates = templates;
+ heh.numTemplates = numTemplates;
+ return CallService(MS_HISTORYEVENTS_REGISTER, (WPARAM) &heh, 0);
+}
+
+static int HistoryEvents_RegisterMessageStyle(char *module, char *name, char *description, int eventType, HICON defaultIcon,
+ int flags, char **templates, int numTemplates)
+{
+ HISTORY_EVENT_HANDLER heh = {0};
+
+ if (!ServiceExists(MS_HISTORYEVENTS_REGISTER))
+ return 1;
+
+ heh.cbSize = sizeof(heh);
+ heh.module = module;
+ heh.name = name;
+ heh.description = description;
+ heh.eventType = eventType;
+ heh.defaultIcon = defaultIcon;
+ heh.flags = flags;
+ heh.templates = templates;
+ heh.numTemplates = numTemplates;
+ return CallService(MS_HISTORYEVENTS_REGISTER, (WPARAM) &heh, 0);
+}
+
+static BOOL HistoryEvents_CanHandle(WORD eventType)
+{
+ if (!ServiceExists(MS_HISTORYEVENTS_CAN_HANDLE))
+ return FALSE;
+
+ return (BOOL) CallService(MS_HISTORYEVENTS_CAN_HANDLE, (WPARAM) eventType, 0);
+}
+
+static HICON HistoryEvents_GetIcon(WORD eventType)
+{
+ if (!ServiceExists(MS_HISTORYEVENTS_GET_ICON))
+ return NULL;
+
+ return (HICON) CallService(MS_HISTORYEVENTS_GET_ICON, (WPARAM) eventType, 0);
+}
+
+static int HistoryEvents_GetFlags(WORD eventType)
+{
+ if (!ServiceExists(MS_HISTORYEVENTS_GET_FLAGS))
+ return -1;
+
+ return (int) CallService(MS_HISTORYEVENTS_GET_FLAGS, (WPARAM) eventType, 0);
+}
+
+static void HistoryEvents_ReleaseIcon(HICON icon)
+{
+ CallService(MS_HISTORYEVENTS_RELEASE_ICON, (WPARAM) icon, 0);
+}
+
+static char * HistoryEvents_GetTextA(HANDLE hDbEvent, DBEVENTINFO *dbe)
+{
+ HISTORY_EVENT_PARAM hep = {0};
+
+ if (!ServiceExists(MS_HISTORYEVENTS_GET_TEXT))
+ return NULL;
+
+ hep.cbSize = sizeof(hep);
+ hep.hDbEvent = hDbEvent;
+ hep.dbe = dbe;
+ hep.format = HISTORYEVENTS_FORMAT_CHAR;
+ return (char *) CallService(MS_HISTORYEVENTS_GET_TEXT, (WPARAM) &hep, 0);
+}
+
+static wchar_t * HistoryEvents_GetTextW(HANDLE hDbEvent, DBEVENTINFO *dbe)
+{
+ HISTORY_EVENT_PARAM hep = {0};
+
+ if (!ServiceExists(MS_HISTORYEVENTS_GET_TEXT))
+ return NULL;
+
+ hep.cbSize = sizeof(hep);
+ hep.hDbEvent = hDbEvent;
+ hep.dbe = dbe;
+ hep.format = HISTORYEVENTS_FORMAT_WCHAR;
+ return (wchar_t *) CallService(MS_HISTORYEVENTS_GET_TEXT, (WPARAM) &hep, 0);
+}
+
+static char * HistoryEvents_GetRichText(HANDLE hDbEvent, DBEVENTINFO *dbe)
+{
+ HISTORY_EVENT_PARAM hep = {0};
+
+ if (!ServiceExists(MS_HISTORYEVENTS_GET_TEXT))
+ return NULL;
+
+ hep.cbSize = sizeof(hep);
+ hep.hDbEvent = hDbEvent;
+ hep.dbe = dbe;
+ hep.format = HISTORYEVENTS_FORMAT_RICH_TEXT;
+ return (char *) CallService(MS_HISTORYEVENTS_GET_TEXT, (WPARAM) &hep, 0);
+}
+
+#define HistoryEvents_ReleaseText mir_free
+//static void HistoryEvents_ReleaseText(void *str)
+//{
+// if (!ServiceExists(MS_HISTORYEVENTS_RELEASE_TEXT))
+// return;
+//
+// CallService(MS_HISTORYEVENTS_RELEASE_TEXT, (WPARAM) str, 0);
+//}
+
+
+#ifdef __cplusplus
+static HANDLE HistoryEvents_AddToHistoryEx(HANDLE hContact, WORD eventType, int templateNum,
+ TCHAR **variables, int numVariables,
+ PBYTE additionalData, int additionalDataSize,
+ int flags = 0, DWORD timestamp = 0, BOOL addToMetaToo = FALSE)
+#else
+static HANDLE HistoryEvents_AddToHistoryEx(HANDLE hContact, WORD eventType, int templateNum,
+ TCHAR **variables, int numVariables,
+ PBYTE additionalData, int additionalDataSize,
+ int flags, DWORD timestamp, BOOL addToMetaToo)
+#endif
+{
+ HISTORY_EVENT_ADD hea = {0};
+
+ if (!ServiceExists(MS_HISTORYEVENTS_ADD_TO_HISTORY))
+ return NULL;
+
+ hea.cbSize = sizeof(hea);
+ hea.hContact = hContact;
+ hea.eventType = eventType;
+ hea.templateNum = templateNum;
+ hea.numVariables = numVariables;
+ hea.variables = variables;
+ hea.additionalData = additionalData;
+ hea.additionalDataSize = additionalDataSize;
+ hea.flags = flags;
+ hea.timestamp = timestamp;
+ hea.addToMetaToo = addToMetaToo;
+
+ return (HANDLE) CallService(MS_HISTORYEVENTS_ADD_TO_HISTORY, (WPARAM) &hea, 0);
+}
+
+#ifdef __cplusplus
+static HANDLE HistoryEvents_AddToHistoryVars(HANDLE hContact, WORD eventType, int templateNum,
+ TCHAR **variables, int numVariables,
+ int flags = 0, DWORD timestamp = 0, BOOL addToMetaToo = FALSE)
+#else
+static HANDLE HistoryEvents_AddToHistoryVars(HANDLE hContact, WORD eventType, int templateNum,
+ TCHAR **variables, int numVariables,
+ int flags, DWORD timestamp, BOOL addToMetaToo)
+#endif
+{
+ HISTORY_EVENT_ADD hea = {0};
+
+ if (!ServiceExists(MS_HISTORYEVENTS_ADD_TO_HISTORY))
+ return NULL;
+
+ hea.cbSize = sizeof(hea);
+ hea.hContact = hContact;
+ hea.eventType = eventType;
+ hea.templateNum = templateNum;
+ hea.numVariables = numVariables;
+ hea.variables = variables;
+ hea.flags = flags;
+ hea.timestamp = timestamp;
+ hea.addToMetaToo = addToMetaToo;
+
+ return (HANDLE) CallService(MS_HISTORYEVENTS_ADD_TO_HISTORY, (WPARAM) &hea, 0);
+}
+
+#ifdef __cplusplus
+static HANDLE HistoryEvents_AddToHistorySimple(HANDLE hContact, WORD eventType, int templateNum,
+ int flags = 0, DWORD timestamp = 0, BOOL addToMetaToo = FALSE)
+#else
+static HANDLE HistoryEvents_AddToHistorySimple(HANDLE hContact, WORD eventType, int templateNum,
+ int flags, DWORD timestamp, BOOL addToMetaToo)
+#endif
+{
+ HISTORY_EVENT_ADD hea = {0};
+
+ if (!ServiceExists(MS_HISTORYEVENTS_ADD_TO_HISTORY))
+ return NULL;
+
+ hea.cbSize = sizeof(hea);
+ hea.hContact = hContact;
+ hea.eventType = eventType;
+ hea.templateNum = templateNum;
+ hea.flags = flags;
+ hea.timestamp = timestamp;
+ hea.addToMetaToo = addToMetaToo;
+
+ return (HANDLE) CallService(MS_HISTORYEVENTS_ADD_TO_HISTORY, (WPARAM) &hea, 0);
+}
+
+static BOOL HistoryEvents_IsEnabledTemplate(WORD eventType, int templateNum)
+{
+ return (BOOL) CallService(MS_HISTORYEVENTS_IS_ENABLED_TEMPLATE, eventType, templateNum);
+}
+
+#ifdef UNICODE
+# define HistoryEvents_GetTextT HistoryEvents_GetTextW
+#else
+# define HistoryEvents_GetTextT HistoryEvents_GetTextA
+#endif
+
+
+
+#endif // __M_HISTORYEVENTS_H__
diff --git a/Plugins/voiceservice/sdk/m_metacontacts.h b/Plugins/voiceservice/sdk/m_metacontacts.h
new file mode 100644
index 0000000..1da12b9
--- /dev/null
+++ b/Plugins/voiceservice/sdk/m_metacontacts.h
@@ -0,0 +1,162 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright © 2004 Universite Louis PASTEUR, STRASBOURG.
+Copyright © 2004 Scott Ellis (www.scottellis.com.au mail@scottellis.com.au)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef M_METACONTACTS_H__
+#define M_METACONTACTS_H__ 1
+
+//get the handle for a contact's parent metacontact
+//wParam=(HANDLE)hSubContact
+//lParam=0
+//returns a handle to the parent metacontact, or null if this contact is not a subcontact
+#define MS_MC_GETMETACONTACT "MetaContacts/GetMeta"
+
+//gets the handle for the default contact
+//wParam=(HANDLE)hMetaContact
+//lParam=0
+//returns a handle to the default contact, or null on failure
+#define MS_MC_GETDEFAULTCONTACT "MetaContacts/GetDefault"
+
+//gets the contact number for the default contact
+//wParam=(HANDLE)hMetaContact
+//lParam=0
+//returns a DWORD contact number, or -1 on failure
+#define MS_MC_GETDEFAULTCONTACTNUM "MetaContacts/GetDefaultNum"
+
+//gets the handle for the 'most online' contact
+//wParam=(HANDLE)hMetaContact
+//lParam=0
+//returns a handle to the 'most online' contact
+#define MS_MC_GETMOSTONLINECONTACT "MetaContacts/GetMostOnline"
+
+//gets the number of subcontacts for a metacontact
+//wParam=(HANDLE)hMetaContact
+//lParam=0
+//returns a DWORD representing the number of subcontacts for the given metacontact
+#define MS_MC_GETNUMCONTACTS "MetaContacts/GetNumContacts"
+
+//gets the handle of a subcontact, using the subcontact's number
+//wParam=(HANDLE)hMetaContact
+//lParam=(DWORD)contact number
+//returns a handle to the specified subcontact
+#define MS_MC_GETSUBCONTACT "MetaContacts/GetSubContact"
+
+//sets the default contact, using the subcontact's contact number
+//wParam=(HANDLE)hMetaContact
+//lParam=(DWORD)contact number
+//returns 0 on success
+#define MS_MC_SETDEFAULTCONTACTNUM "MetaContacts/SetDefault"
+
+//sets the default contact, using the subcontact's handle
+//wParam=(HANDLE)hMetaContact
+//lParam=(HANDLE)hSubcontact
+//returns 0 on success
+#define MS_MC_SETDEFAULTCONTACT "MetaContacts/SetDefaultByHandle"
+
+//forces the metacontact to send using a specific subcontact, using the subcontact's contact number
+//wParam=(HANDLE)hMetaContact
+//lParam=(DWORD)contact number
+//returns 0 on success
+#define MS_MC_FORCESENDCONTACTNUM "MetaContacts/ForceSendContact"
+
+//forces the metacontact to send using a specific subcontact, using the subcontact's handle
+//wParam=(HANDLE)hMetaContact
+//lParam=(HANDLE)hSubcontact
+//returns 0 on success (will fail if 'force default' is in effect)
+#define MS_MC_FORCESENDCONTACT "MetaContacts/ForceSendContactByHandle"
+
+//'unforces' the metacontact to send using a specific subcontact
+//wParam=(HANDLE)hMetaContact
+//lParam=0
+//returns 0 on success (will fail if 'force default' is in effect)
+#define MS_MC_UNFORCESENDCONTACT "MetaContacts/UnforceSendContact"
+
+//'forces' or 'unforces' (i.e. toggles) the metacontact to send using it's default contact
+// overrides (and clears) 'force send' above, and will even force use of offline contacts
+// will send ME_MC_FORCESEND or ME_MC_UNFORCESEND event
+//wParam=(HANDLE)hMetaContact
+//lParam=0
+//returns 1(true) or 0(false) representing new state of 'force default'
+#define MS_MC_FORCEDEFAULT "MetaContacts/ForceSendDefault"
+
+// method to get state of 'force' for a metacontact
+// wParam=(HANDLE)hMetaContact
+// lParam= (DWORD)&contact_number or NULL
+//
+// if lparam supplied, the contact_number of the contatct 'in force' will be copied to the address it points to,
+// or if none is in force, the value (DWORD)-1 will be copied
+// (v0.8.0.8+ returns 1 if 'force default' is true with *lParam == default contact number, else returns 0 with *lParam as above)
+#define MS_MC_GETFORCESTATE "MetaContacts/GetForceState"
+
+// fired when a metacontact's default contact changes (fired upon creation of metacontact also, when default is initially set)
+// wParam=(HANDLE)hMetaContact
+// lParam=(HANDLE)hDefaultContact
+#define ME_MC_DEFAULTTCHANGED "MetaContacts/DefaultChanged"
+
+// fired when a metacontact's subcontacts change (fired upon creation of metacontact, when contacts are added or removed, and when
+// contacts are reordered) - a signal to re-read metacontact data
+// wParam=(HANDLE)hMetaContact
+// lParam=0
+#define ME_MC_SUBCONTACTSCHANGED "MetaContacts/SubcontactsChanged"
+
+// fired when a metacontact is forced to send using a specific subcontact
+// wParam=(HANDLE)hMetaContact
+// lParam=(HANDLE)hForceContact
+#define ME_MC_FORCESEND "MetaContacts/ForceSend"
+
+// fired when a metacontact is 'unforced' to send using a specific subcontact
+// wParam=(HANDLE)hMetaContact
+// lParam=0
+#define ME_MC_UNFORCESEND "MetaContacts/UnforceSend"
+
+// method to get protocol name - used to be sure you're dealing with a "real" metacontacts plugin :)
+// wParam=lParam=0
+#define MS_MC_GETPROTOCOLNAME "MetaContacts/GetProtoName"
+
+
+// added 0.9.5.0 (22/3/05)
+// wParam=(HANDLE)hContact
+// lParam=0
+// convert a given contact into a metacontact
+#define MS_MC_CONVERTTOMETA "MetaContacts/ConvertToMetacontact"
+
+// added 0.9.5.0 (22/3/05)
+// wParam=(HANDLE)hContact
+// lParam=(HANDLE)hMeta
+// add an existing contact to a metacontact
+#define MS_MC_ADDTOMETA "MetaContacts/AddToMetacontact"
+
+// added 0.9.5.0 (22/3/05)
+// wParam=0
+// lParam=(HANDLE)hContact
+// remove a contact from a metacontact
+#define MS_MC_REMOVEFROMMETA "MetaContacts/RemoveFromMetacontact"
+
+
+// added 0.9.13.2 (6/10/05)
+// wParam=(BOOL)disable
+// lParam=0
+// enable/disable the 'hidden group hack' - for clists that support subcontact hiding using 'IsSubcontact' setting
+// should be called once in the clist 'onmodulesloaded' event handler (which, since it's loaded after the db, will be called
+// before the metacontact onmodulesloaded handler where the subcontact hiding is usually done)
+#define MS_MC_DISABLEHIDDENGROUP "MetaContacts/DisableHiddenGroup"
+
+#endif
diff --git a/Plugins/voiceservice/sdk/m_updater.h b/Plugins/voiceservice/sdk/m_updater.h
new file mode 100644
index 0000000..371b743
--- /dev/null
+++ b/Plugins/voiceservice/sdk/m_updater.h
@@ -0,0 +1,146 @@
+#ifndef _M_UPDATER_H
+#define _M_UPDATER_H
+
+// NOTES:
+// - For langpack updates, include a string of the following format in the langpack text file:
+// ";FLID: <file listing name> <version>"
+// version must be four numbers seperated by '.', in the range 0-255 inclusive
+// - Updater will disable plugins that are downloaded but were not active prior to the update (this is so that, if an archive contains e.g. ansi and
+// unicode versions, the correct plugin will be the only one active after the new version is installed)...so if you add a support plugin, you may need
+// to install an ini file to make the plugin activate when miranda restarts after the update
+// - Updater will replace all dlls that have the same internal shortName as a downloaded update dll (this is so that msn1.dll and msn2.dll, for example,
+// will both be updated) - so if you have a unicode and a non-unicode version of a plugin in your archive, you should make the internal names different (which will break automatic
+// updates from the file listing if there is only one file listing entry for both versions, unless you use the 'MS_UPDATE_REGISTER' service below)
+// - Updater will install all files in the root of the archive into the plugins folder, except for langpack files that contain the FLID string which go into the root folder (same
+// folder as miranda32.exe)...all folders in the archive will also be copied to miranda's root folder, and their contents transferred into the new folders. The only exception is a
+// special folder called 'root_files' - if there is a folder by that name in the archive, it's contents will also be copied into miranda's root folder - this is intended to be used
+// to install additional dlls etc that a plugin may require)
+
+// if you set Update.szUpdateURL to the following value when registering, as well as setting your beta site and version data,
+// Updater will ignore szVersionURL and pbVersionPrefix, and attempt to find the file listing URL's from the backend XML data.
+// for this to work, the plugin name in pluginInfo.shortName must match the file listing exactly (except for case)
+#define UPDATER_AUTOREGISTER "UpdaterAUTOREGISTER"
+// Updater will also use the backend xml data if you provide URL's that reference the miranda file listing for updates (so you can use that method
+// if e.g. your plugin shortName does not match the file listing) - it will grab the file listing id from the end of these URLs
+
+typedef struct Update_tag {
+ int cbSize;
+ char *szComponentName; // component name as it will appear in the UI (will be translated before displaying)
+
+ char *szVersionURL; // URL where the current version can be found (NULL to disable)
+ BYTE *pbVersionPrefix; // bytes occuring in VersionURL before the version, used to locate the version information within the URL data
+ // (note that this URL could point at a binary file - dunno why, but it could :)
+ int cpbVersionPrefix; // number of bytes pointed to by pbVersionPrefix
+ char *szUpdateURL; // URL where dll/zip is located
+ // set to UPDATER_AUTOREGISTER if you want Updater to find the file listing URLs (ensure plugin shortName matches file listing!)
+
+ char *szBetaVersionURL; // URL where the beta version can be found (NULL to disable betas)
+ BYTE *pbBetaVersionPrefix; // bytes occuring in VersionURL before the version, used to locate the version information within the URL data
+ int cpbBetaVersionPrefix; // number of bytes pointed to by pbVersionPrefix
+ char *szBetaUpdateURL; // URL where dll/zip is located
+
+ BYTE *pbVersion; // bytes of current version, used for comparison with those in VersionURL
+ int cpbVersion; // number of bytes pointed to by pbVersion
+
+ char *szBetaChangelogURL; // url for displaying changelog for beta versions
+} Update;
+
+// register a comonent with Updater
+//
+// wparam = 0
+// lparam = (LPARAM)&Update
+#define MS_UPDATE_REGISTER "Update/Register"
+
+// utility functions to create a version string from a DWORD or from pluginInfo
+// point buf at a buffer at least 16 chars wide - but note the version string returned may be shorter
+//
+__inline static char *CreateVersionString(DWORD version, char *buf) {
+ mir_snprintf(buf, 16, "%d.%d.%d.%d", (version >> 24) & 0xFF, (version >> 16) & 0xFF, (version >> 8) & 0xFF, version & 0xFF);
+ return buf;
+}
+
+__inline static char *CreateVersionStringPlugin(PLUGININFO *pluginInfo, char *buf) {
+ return CreateVersionString(pluginInfo->version, buf);
+}
+
+
+// register the 'easy' way - use this method if you have no beta URL and the plugin is on the miranda file listing
+// NOTE: the plugin version string on the file listing must be the string version of the version in pluginInfo (i.e. 0.0.0.1,
+// four numbers between 0 and 255 inclusivem, so no letters, brackets, etc.)
+//
+// wParam = (int)fileID - this is the file ID from the file listing (i.e. the number at the end of the download link)
+// lParam = (PLUGININFO*)&pluginInfo
+#define MS_UPDATE_REGISTERFL "Update/RegisterFL"
+
+// this function can be used to 'unregister' components - useful for plugins that register non-plugin/langpack components and
+// may need to change those components on the fly
+// lParam = (char *)szComponentName
+#define MS_UPDATE_UNREGISTER "Update/Unregister"
+
+// this event is fired when the startup process is complete, but NOT if a restart is imminent
+// it is designed for status managment plugins to use as a trigger for beggining their own startup process
+// wParam = lParam = 0 (unused)
+// (added in version 0.1.6.0)
+#define ME_UPDATE_STARTUPDONE "Update/StartupDone"
+
+// this service can be used to enable/disable Updater's global status control
+// it can be called from the StartupDone event handler
+// wParam = (BOOL)enable
+// lParam = 0
+// (added in version 0.1.6.0)
+#define MS_UPDATE_ENABLESTATUSCONTROL "Update/EnableStatusControl"
+
+// An description of usage of the above service and event:
+// Say you are a status control plugin that normally sets protocol or global statuses in your ModulesLoaded event handler.
+// In order to make yourself 'Updater compatible', you would move the status control code from ModulesLoaded to another function,
+// say DoStartup. Then, in ModulesLoaded you would check for the existence of the MS_UPDATE_ENABLESTATUSCONTROL service.
+// If it does not exist, call DoStartup. If it does exist, hook the ME_UPDATE_STARTUPDONE event and call DoStartup from there. You may
+// also wish to call MS_UPDATE_ENABLESTATUSCONTROL with wParam == FALSE at this time, to disable Updater's own status control feature.
+
+// this service can be used to determine whether updates are possible for a component with the given name
+// wParam = 0
+// lParam = (char *)szComponentName
+// returns TRUE if updates are supported, FALSE otherwise
+#define MS_UPDATE_ISUPDATESUPPORTED "Update/IsUpdateSupported"
+
+#endif
+
+
+/////////////// Usage Example ///////////////
+
+#ifdef EXAMPLE_CODE
+
+// you need to #include "m_updater.h" and HookEvent(ME_SYSTEM_MODULESLOADED, OnModulesLoaded) in your Load function...
+
+int OnModulesLoaded(WPARAM wParam, LPARAM lParam) {
+
+ Update update = {0}; // for c you'd use memset or ZeroMemory...
+ char szVersion[16];
+
+ update.cbSize = sizeof(Update);
+
+ update.szComponentName = pluginInfo.shortName;
+ update.pbVersion = (BYTE *)CreateVersionString(&pluginInfo, szVersion);
+ update.cpbVersion = strlen((char *)update.pbVersion);
+
+ // these are the three lines that matter - the archive, the page containing the version string, and the text (or data)
+ // before the version that we use to locate it on the page
+ // (note that if the update URL and the version URL point to standard file listing entries, the backend xml
+ // data will be used to check for updates rather than the actual web page - this is not true for beta urls)
+ update.szUpdateURL = "http://scottellis.com.au:81/test/updater.zip";
+ update.szVersionURL = "http://scottellis.com.au:81/test/updater_test.html";
+ update.pbVersionPrefix = (BYTE *)"Updater version ";
+
+ update.cpbVersionPrefix = strlen((char *)update.pbVersionPrefix);
+
+ // do the same for the beta versions of the above struct members if you wish to allow beta updates from another URL
+
+ CallService(MS_UPDATE_REGISTER, 0, (WPARAM)&update);
+
+ // Alternatively, to register a plugin with e.g. file ID 2254 on the file listing...
+ // CallService(MS_UPDATE_REGISTERFL, (WPARAM)2254, (LPARAM)&pluginInfo);
+
+ return 0;
+}
+
+#endif
diff --git a/Plugins/voiceservice/voiceservice.cpp b/Plugins/voiceservice/voiceservice.cpp
new file mode 100644
index 0000000..1b43795
--- /dev/null
+++ b/Plugins/voiceservice/voiceservice.cpp
@@ -0,0 +1,1666 @@
+/*
+Copyright (C) 2006 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+#include "commons.h"
+
+
+// Prototypes ///////////////////////////////////////////////////////////////////////////
+
+
+PLUGININFOEX pluginInfo = {
+ sizeof(PLUGININFOEX),
+#ifdef UNICODE
+ "Voice Service (Unicode)",
+#else
+ "Voice Service",
+#endif
+ PLUGIN_MAKE_VERSION(0,1,2,0),
+ "Provide services for protocols that support voice calls",
+ "Ricardo Pescuma Domenecci",
+ "pescuma@miranda-im.org",
+ "© 2007-2009 Ricardo Pescuma Domenecci",
+ "http://pescuma.org/miranda/voiceservice",
+ UNICODE_AWARE,
+ 0, //doesn't replace anything built-in
+#ifdef UNICODE
+ { 0x1bfc449d, 0x8f6f, 0x4080, { 0x8f, 0x35, 0xf9, 0x40, 0xb3, 0xde, 0x12, 0x84 } } // {1BFC449D-8F6F-4080-8F35-F940B3DE1284}
+#else
+ { 0x1bbe5b21, 0x238d, 0x4cbc, { 0xaf, 0xb8, 0xe, 0xef, 0xab, 0x1b, 0xf2, 0x69 } } // {1BBE5B21-238D-4cbc-AFB8-0EEFAB1BF269}
+#endif
+};
+
+
+HINSTANCE hInst;
+PLUGINLINK *pluginLink;
+MM_INTERFACE mmi;
+UTF8_INTERFACE utfi;
+LIST_INTERFACE li;
+
+static vector<HANDLE> hHooks;
+
+static vector<HANDLE> hCMCalls;
+static HANDLE hCMCall = NULL;
+static HANDLE hCMAnswer = NULL;
+static HANDLE hCMDrop = NULL;
+static HANDLE hCMHold = NULL;
+
+char *metacontacts_proto = NULL;
+
+
+static INT_PTR ModulesLoaded(WPARAM wParam, LPARAM lParam);
+static INT_PTR PreShutdown(WPARAM wParam, LPARAM lParam);
+static INT_PTR ProtoAck(WPARAM wParam, LPARAM lParam);
+static INT_PTR PreBuildContactMenu(WPARAM wParam, LPARAM lParam);
+static INT_PTR AccListChanged(WPARAM wParam, LPARAM lParam);
+
+static INT_PTR VoiceRegister(WPARAM wParam, LPARAM lParam);
+static INT_PTR VoiceUnregister(WPARAM wParam, LPARAM lParam);
+static INT_PTR VoiceState(WPARAM wParam, LPARAM lParam);
+
+VoiceProvider * FindModule(const char *szModule);
+VoiceCall * FindVoiceCall(const char *szModule, const char *id, BOOL add);
+VoiceCall * FindVoiceCall(HANDLE hContact);
+
+
+template<class TYPE>
+static int sttCompare(const TYPE *p1, const TYPE *p2)
+{
+ return (int) p1 - (int) p2;
+}
+OBJLIST<VoiceProvider> modules(1, &sttCompare<VoiceProvider>);
+OBJLIST<VoiceCall> calls(1, &sttCompare<VoiceCall>);
+
+HFONT fonts[NUM_FONTS] = {0};
+COLORREF font_colors[NUM_FONTS] = {0};
+int font_max_height;
+
+COLORREF bkg_color = {0};
+HBRUSH bk_brush = NULL;
+
+
+
+static INT_PTR CListDblClick(WPARAM wParam, LPARAM lParam);
+
+static INT_PTR Service_CanCall(WPARAM wParam, LPARAM lParam);
+static INT_PTR Service_Call(WPARAM wParam, LPARAM lParam);
+static INT_PTR CMAnswer(WPARAM wParam, LPARAM lParam);
+static INT_PTR CMHold(WPARAM wParam, LPARAM lParam);
+static INT_PTR CMDrop(WPARAM wParam, LPARAM lParam);
+
+static INT_PTR IconsChanged(WPARAM wParam, LPARAM lParam);
+static INT_PTR ReloadFont(WPARAM wParam, LPARAM lParam);
+static INT_PTR ReloadColor(WPARAM wParam, LPARAM lParam);
+static VOID CALLBACK ClearOldVoiceCalls(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime);
+
+static BOOL CALLBACK DlgProcNewCall(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam);
+
+
+
+class CallingMethod
+{
+public:
+ VoiceProvider *provider;
+ HANDLE hContact;
+ TCHAR number[128];
+
+ CallingMethod(VoiceProvider *provider, HANDLE hContact, const TCHAR *number = NULL)
+ : provider(provider), hContact(hContact)
+ {
+ if (number == NULL)
+ this->number[0] = 0;
+ else
+ lstrcpyn(this->number, number, MAX_REGS(this->number));
+ }
+
+ void Call()
+ {
+ provider->Call(hContact, number);
+ }
+};
+
+
+static int sttCompareCallingMethods(const CallingMethod *p1, const CallingMethod *p2)
+{
+ if (p1->hContact != p2->hContact)
+ return (int)p2->hContact - (int)p1->hContact;
+
+ BOOL noNum1 = (IsEmpty(p1->number) ? 1 : 0);
+ BOOL noNum2 = (IsEmpty(p2->number) ? 1 : 0);
+ if (noNum1 != noNum2)
+ return noNum2 - noNum1;
+
+ if (!noNum1)
+ {
+ int numDif = lstrcmp(p1->number, p2->number);
+ if (numDif != 0)
+ return numDif;
+ }
+
+ BOOL isProto1 = (CallService(MS_PROTO_ISPROTOONCONTACT, (WPARAM) p1->hContact, (LPARAM) p1->provider->name) ? 1 : 0);
+ BOOL isProto2 = (CallService(MS_PROTO_ISPROTOONCONTACT, (WPARAM) p2->hContact, (LPARAM) p2->provider->name) ? 1 : 0);
+ if (isProto1 != isProto2)
+ return isProto2 - isProto1;
+
+ return lstrcmp(p1->provider->description, p2->provider->description);
+}
+
+
+static void AddMethodsFrom(OBJLIST<CallingMethod> *list, HANDLE hContact)
+{
+ for(int i = 0; i < modules.getCount(); i++)
+ {
+ VoiceProvider *provider = &modules[i];
+ if (provider->CanCall(hContact))
+ list->insert(new CallingMethod(provider, hContact));
+ }
+}
+
+static void AddMethodsFrom(OBJLIST<CallingMethod> *list, HANDLE hContact, const TCHAR *number)
+{
+ for(int i = 0; i < modules.getCount(); i++)
+ {
+ VoiceProvider *provider = &modules[i];
+ if (provider->CanCall(number))
+ list->insert(new CallingMethod(provider, hContact, number));
+ }
+}
+
+static void BuildCallingMethodsList(OBJLIST<CallingMethod> *list, HANDLE hContact)
+{
+ AddMethodsFrom(list, hContact);
+
+ // Fetch contact number
+ char *proto = (char *) CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM) hContact, 0);
+
+ DBTString protoNumber(hContact, proto, "Number");
+ if (protoNumber != NULL)
+ AddMethodsFrom(list, hContact, protoNumber);
+
+ for(int i = 0; ;i++)
+ {
+ char tmp[128];
+ mir_snprintf(tmp, MAX_REGS(tmp), "MyPhone%d", i);
+
+ DBTString number(hContact, "UserInfo", tmp);
+ if (number != NULL)
+ AddMethodsFrom(list, hContact, number);
+
+ if (number == NULL && i >= 4)
+ break;
+ }
+}
+
+
+// Functions ////////////////////////////////////////////////////////////////////////////
+
+
+extern "C" BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
+{
+ hInst = hinstDLL;
+ return TRUE;
+}
+
+
+extern "C" __declspec(dllexport) PLUGININFO* MirandaPluginInfo(DWORD mirandaVersion)
+{
+ pluginInfo.cbSize = sizeof(PLUGININFO);
+ return (PLUGININFO*) &pluginInfo;
+}
+
+
+extern "C" __declspec(dllexport) PLUGININFOEX* MirandaPluginInfoEx(DWORD mirandaVersion)
+{
+ pluginInfo.cbSize = sizeof(PLUGININFOEX);
+ return &pluginInfo;
+}
+
+
+static const MUUID interfaces[] = { MIID_VOICESERVICE, MIID_LAST };
+extern "C" __declspec(dllexport) const MUUID* MirandaPluginInterfaces(void)
+{
+ return interfaces;
+}
+
+
+extern "C" int __declspec(dllexport) Load(PLUGINLINK *link)
+{
+ pluginLink = link;
+
+ CHECK_VERSION("Voice Service")
+
+ if (Pa_Initialize() != paNoError)
+ MessageBox(NULL, TranslateT("Error initializing portaudio."), TranslateT("Voice Service"), MB_OK | MB_ICONERROR);
+
+ // TODO Assert results here
+ mir_getMMI(&mmi);
+ mir_getUTFI(&utfi);
+ mir_getLI(&li);
+
+ CreateServiceFunction(MS_VOICESERVICE_CLIST_DBLCLK, CListDblClick);
+ CreateServiceFunction(MS_VOICESERVICE_REGISTER, VoiceRegister);
+ CreateServiceFunction(MS_VOICESERVICE_UNREGISTER, VoiceUnregister);
+
+ // Hooks
+ hHooks.push_back( HookEvent(ME_SYSTEM_MODULESLOADED, ModulesLoaded) );
+ hHooks.push_back( HookEvent(ME_SYSTEM_PRESHUTDOWN, PreShutdown) );
+ hHooks.push_back( HookEvent(ME_PROTO_ACK, ProtoAck) );
+
+ return 0;
+}
+
+
+extern "C" int __declspec(dllexport) Unload(void)
+{
+ if (bk_brush != NULL)
+ DeleteObject(bk_brush);
+
+ Pa_Terminate();
+
+ return 0;
+}
+
+
+static void AddAccount(PROTOACCOUNT *acc)
+{
+ if (!IsAccountEnabled(acc))
+ return;
+ if (IsEmptyA(acc->szModuleName))
+ return;
+ if (!ProtoServiceExists(acc->szModuleName, PS_VOICE_CAPS))
+ return;
+
+ int flags = CallProtoService(acc->szModuleName, PS_VOICE_CAPS, 0, 0);
+
+ if ((flags & VOICE_CAPS_VOICE) == 0)
+ return;
+
+ VOICE_MODULE vm = {0};
+ vm.cbSize = sizeof(vm);
+ vm.name = acc->szModuleName;
+ vm.description = acc->tszAccountName;
+ vm.flags = flags;
+ VoiceRegister((WPARAM) &vm, 0);
+}
+
+
+// Called when all the modules are loaded
+static INT_PTR ModulesLoaded(WPARAM wParam, LPARAM lParam)
+{
+ // add our modules to the KnownModules list
+ CallService("DBEditorpp/RegisterSingleModule", (WPARAM) MODULE_NAME, 0);
+
+ if (ServiceExists(MS_MC_GETPROTOCOLNAME))
+ metacontacts_proto = (char *) CallService(MS_MC_GETPROTOCOLNAME, 0, 0);
+
+ // updater plugin support
+ if(ServiceExists(MS_UPDATE_REGISTER))
+ {
+ Update upd = {0};
+ char szCurrentVersion[30];
+
+ upd.cbSize = sizeof(upd);
+ upd.szComponentName = pluginInfo.shortName;
+
+ upd.szUpdateURL = UPDATER_AUTOREGISTER;
+
+ upd.szBetaVersionURL = "http://pescuma.org/miranda/voiceservice_version.txt";
+ upd.szBetaChangelogURL = "http://pescuma.org/miranda/voiceservice#Changelog";
+ upd.pbBetaVersionPrefix = (BYTE *)"Voice Service ";
+ upd.cpbBetaVersionPrefix = strlen((char *)upd.pbBetaVersionPrefix);
+#ifdef UNICODE
+ upd.szBetaUpdateURL = "http://pescuma.org/miranda/voiceserviceW.zip";
+#else
+ upd.szBetaUpdateURL = "http://pescuma.org/miranda/voiceservice.zip";
+#endif
+
+ upd.pbVersion = (BYTE *)CreateVersionStringPlugin((PLUGININFO*) &pluginInfo, szCurrentVersion);
+ upd.cpbVersion = strlen((char *)upd.pbVersion);
+
+ CallService(MS_UPDATE_REGISTER, 0, (LPARAM)&upd);
+ }
+
+ // Init icons
+ {
+ IcoLib_Register(mainIcons[0], _T("Voice Calls"), _T("Main"), IDI_MAIN);
+ IcoLib_Register(mainIcons[1], _T("Voice Calls"), _T("Dialpad"), IDI_DIALPAD);
+ IcoLib_Register(mainIcons[2], _T("Voice Calls"), _T("Secure Overlay"), IDI_SECURE);
+
+ int i;
+ for(i = 0; i < MAX_REGS(stateNames); i++)
+ IcoLib_Register(stateIcons[i], _T("Voice Calls"), stateNames[i], IDI_TALKING + i);
+
+ for(i = 0; i < MAX_REGS(actionNames); i++)
+ IcoLib_Register(actionIcons[i], _T("Voice Calls"), actionNames[i], IDI_ACTION_CALL + i);
+
+ hHooks.push_back( HookEvent(ME_SKIN2_ICONSCHANGED, IconsChanged) );
+ }
+
+ // Init fonts
+ {
+ FontIDT fi = {0};
+ fi.cbSize = sizeof(fi);
+ lstrcpyn(fi.group, TranslateT("Voice Calls"), MAX_REGS(fi.group));
+ strncpy(fi.dbSettingsGroup, MODULE_NAME, MAX_REGS(fi.dbSettingsGroup));
+
+ for (int i = 0; i < NUM_FONTS; i++)
+ {
+ fi.order = i;
+ lstrcpyn(fi.name, stateNames[i], MAX_REGS(fi.name));
+ strncpy(fi.prefix, stateIcons[i], MAX_REGS(fi.prefix));
+
+ CallService(MS_FONT_REGISTERT, (WPARAM) &fi, 0);
+ }
+
+ ReloadFont(0,0);
+ hHooks.push_back( HookEvent(ME_FONT_RELOAD, ReloadFont) );
+ }
+
+ // Init bkg color
+ {
+ ColourIDT ci = {0};
+ ci.cbSize = sizeof(ci);
+ lstrcpyn(ci.group, TranslateT("Voice Calls"), MAX_REGS(ci.group));
+ lstrcpyn(ci.name, TranslateT("Background"), MAX_REGS(ci.name));
+ strncpy(ci.dbSettingsGroup, MODULE_NAME, MAX_REGS(ci.dbSettingsGroup));
+ strncpy(ci.setting, "BkgColor", MAX_REGS(ci.setting));
+ ci.defcolour = GetSysColor(COLOR_BTNFACE);
+
+ CallService(MS_COLOUR_REGISTERT, (WPARAM) &ci, 0);
+
+ ReloadColor(0,0);
+ hHooks.push_back( HookEvent(ME_COLOUR_RELOAD, ReloadColor) );
+ }
+
+ // Init history
+ if (ServiceExists(MS_HISTORYEVENTS_REGISTER))
+ {
+ char *templates[] = {
+ "Talking\nCall from %number% has started\n%number%\tOther side of the call",
+ "Ringing\nCall from %number% is ringing\n%number%\tOther side of the call",
+ "Calling\nCalling %number%\n%number%\tOther side of the call",
+ "On Hold\nCall from %number% is on hold\n%number%\tOther side of the call",
+ "Ended\nCall from %number% has ended\n%number%\tOther side of the call",
+ "Busy\n%number% is busy\n%number%\tOther side of the call",
+ };
+
+ HISTORY_EVENT_HANDLER heh = {0};
+ heh.cbSize = sizeof(heh);
+ heh.module = MODULE_NAME;
+ heh.name = "VoiceCall";
+ heh.description = "Voice calls";
+ heh.eventType = EVENTTYPE_VOICE_CALL;
+ heh.defaultIconName = "vca_call";
+ heh.supports = HISTORYEVENTS_FORMAT_TCHAR;
+ heh.flags = HISTORYEVENTS_FLAG_SHOW_IM_SRMM
+ | HISTORYEVENTS_FLAG_USE_SENT_FLAG
+ | HISTORYEVENTS_REGISTERED_IN_ICOLIB;
+ heh.templates = templates;
+ heh.numTemplates = MAX_REGS(templates);
+ CallService(MS_HISTORYEVENTS_REGISTER, (WPARAM) &heh, 0);
+ }
+
+ InitOptions();
+ InitFrames();
+
+ // Add menu items
+ CLISTMENUITEM mi = {0};
+ mi.cbSize = sizeof(mi);
+ mi.position = -2000020000;
+ mi.flags = CMIF_TCHAR;
+
+ HICON icons[MAX_REGS(actionIcons)];
+ for(int i = 0; i < MAX_REGS(actionIcons); ++i)
+ icons[i] = IcoLib_LoadIcon(actionIcons[i]);
+
+ CreateServiceFunction(MS_VOICESERVICE_CM_CALL, Service_Call);
+ mi.ptszName = actionNames[ACTION_CALL];
+ mi.hIcon = icons[ACTION_CALL];
+ mi.pszService = MS_VOICESERVICE_CM_CALL;
+ hCMCall = (HANDLE) CallService(MS_CLIST_ADDCONTACTMENUITEM, 0, (LPARAM) &mi);
+
+ CreateServiceFunction(MS_VOICESERVICE_CM_ANSWER, CMAnswer);
+ mi.position++;
+ mi.ptszName = actionNames[ACTION_ANSWER];
+ mi.hIcon = icons[ACTION_ANSWER];
+ mi.pszService = MS_VOICESERVICE_CM_ANSWER;
+ hCMAnswer = (HANDLE) CallService(MS_CLIST_ADDCONTACTMENUITEM, 0, (LPARAM) &mi);
+
+ CreateServiceFunction(MS_VOICESERVICE_CM_HOLD, CMHold);
+ mi.position++;
+ mi.position++;
+ mi.ptszName = actionNames[ACTION_HOLD];
+ mi.hIcon = icons[ACTION_HOLD];
+ mi.pszService = MS_VOICESERVICE_CM_HOLD;
+ hCMHold = (HANDLE) CallService(MS_CLIST_ADDCONTACTMENUITEM, 0, (LPARAM) &mi);
+
+ CreateServiceFunction(MS_VOICESERVICE_CM_DROP, CMDrop);
+ mi.position++;
+ mi.ptszName = actionNames[ACTION_DROP];
+ mi.hIcon = icons[ACTION_DROP];
+ mi.pszService = MS_VOICESERVICE_CM_DROP;
+ hCMDrop = (HANDLE) CallService(MS_CLIST_ADDCONTACTMENUITEM, 0, (LPARAM) &mi);
+
+ for(i = 0; i < MAX_REGS(actionIcons); ++i)
+ IcoLib_ReleaseIcon(icons[i]);
+
+ hHooks.push_back( HookEvent(ME_CLIST_PREBUILDCONTACTMENU, PreBuildContactMenu) );
+
+ // Util services
+ CreateServiceFunction(MS_VOICESERVICE_CALL, Service_Call);
+ CreateServiceFunction(MS_VOICESERVICE_CAN_CALL, Service_CanCall);
+
+ // Sounds
+ SKINSOUNDDESCEX ssd = {0};
+ ssd.cbSize = sizeof(ssd);
+ ssd.pszSection = "Voice Calls";
+
+ {
+ for(int i = 0; i < MAX_REGS(sounds); ++i)
+ {
+ ssd.pszName = sounds[i].name;
+ ssd.pszDescription = sounds[i].description;
+ CallService(MS_SKIN_ADDNEWSOUND, 0, (LPARAM)&ssd);
+ }
+ }
+
+ SetTimer(NULL, 0, 1000, ClearOldVoiceCalls);
+
+ // Accounts
+ int numAccounts;
+ PROTOACCOUNT **accounts;
+ ProtoEnumAccounts(&numAccounts, &accounts);
+ for(i = 0; i < numAccounts; ++i)
+ AddAccount(accounts[i]);
+
+ hHooks.push_back( HookEvent(ME_PROTO_ACCLISTCHANGED, AccListChanged) );
+
+ return 0;
+}
+
+
+static INT_PTR AccListChanged(WPARAM wParam, LPARAM lParam)
+{
+ PROTOACCOUNT *acc = (PROTOACCOUNT *) lParam;
+ if (acc == NULL)
+ return 0;
+
+ VoiceProvider *provider = FindModule(acc->szModuleName);
+
+ switch(wParam)
+ {
+ case PRAC_ADDED:
+ {
+ AddAccount(acc);
+ break;
+ }
+ case PRAC_CHANGED:
+ {
+ if (provider != NULL)
+ lstrcpyn(provider->description, acc->tszAccountName, MAX_REGS(provider->description));
+ break;
+ }
+ case PRAC_CHECKED:
+ {
+ BOOL enabled = IsAccountEnabled(acc);
+
+ if (!enabled)
+ {
+ if (provider != NULL)
+ VoiceUnregister((WPARAM) acc->szModuleName, 0);
+ }
+ else
+ {
+ if (provider == NULL)
+ AddAccount(acc);
+ }
+ break;
+ }
+ case PRAC_REMOVED:
+ {
+ if (provider != NULL)
+ VoiceUnregister((WPARAM) acc->szModuleName, 0);
+ break;
+ }
+ }
+
+ return 0;
+}
+
+
+static INT_PTR PreShutdown(WPARAM wParam, LPARAM lParam)
+{
+ DeInitFrames();
+ DeInitOptions();
+
+ for(unsigned int i = 0; i < hHooks.size(); ++i)
+ UnhookEvent(hHooks[i]);
+ hHooks.clear();
+
+ return 0;
+}
+
+
+static INT_PTR ProtoAck(WPARAM wParam, LPARAM lParam)
+{
+ ACKDATA *ack = (ACKDATA*)lParam;
+
+ if (ack->type == ACKTYPE_STATUS)
+ {
+ if (hwnd_frame != NULL)
+ PostMessage(hwnd_frame, WMU_REFRESH, 0, 0);
+ }
+
+ return 0;
+}
+
+
+HANDLE ConvertMetacontact(HANDLE hContact)
+{
+ if (ServiceExists(MS_MC_GETMOSTONLINECONTACT))
+ {
+ HANDLE hTmp = (HANDLE) CallService(MS_MC_GETMOSTONLINECONTACT, (WPARAM) hContact, NULL);
+ if (hTmp != NULL)
+ return hTmp;
+ }
+ return hContact;
+}
+
+
+VoiceProvider * FindModule(const char *szModule)
+{
+ for(int i = 0; i < modules.getCount(); i++)
+ if (strcmp(modules[i].name, szModule) == 0)
+ return &modules[i];
+
+ return NULL;
+}
+
+
+static bool IsCall(VoiceCall *call, const char *szModule, const char *id)
+{
+ return strcmp(call->module->name, szModule) == 0
+ && call->id != NULL && strcmp(call->id, id) == 0;
+}
+
+
+VoiceCall * FindVoiceCall(const char *szModule, const char *id, bool add)
+{
+ for(int i = 0; i < calls.getCount(); i++)
+ {
+ if (IsCall(&calls[i], szModule, id))
+ {
+ return &calls[i];
+ }
+ }
+
+ if (add)
+ {
+ VoiceProvider *module = FindModule(szModule);
+ if (module == NULL)
+ return NULL;
+
+ VoiceCall *tmp = new VoiceCall(module, id);
+ calls.insert(tmp);
+ return tmp;
+ }
+
+ return NULL;
+}
+
+
+VoiceCall * FindVoiceCall(HANDLE hContact)
+{
+ for(int i = 0; i < calls.getCount(); i++)
+ {
+ if (calls[i].state != VOICE_STATE_ENDED && calls[i].hContact == hContact)
+ {
+ return &calls[i];
+ }
+ }
+
+ return NULL;
+}
+
+
+static void RemoveVoiceCall(const char *szModule, const char *id)
+{
+ for(int i = calls.getCount() - 1; i >= 0; --i)
+ {
+ if (IsCall(&calls[i], szModule, id))
+ calls.remove(i);
+ }
+}
+
+
+static VOID CALLBACK ClearOldVoiceCalls(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
+{
+ DWORD now = GetTickCount();
+ BOOL refresh = FALSE;
+ for(int i = calls.getCount() - 1; i >= 0; --i)
+ {
+ VoiceCall *call = &calls[i];
+
+ if (call->state == VOICE_STATE_ENDED && call->end_time + TIME_TO_SHOW_ENDED_CALL < now)
+ {
+ calls.remove(i);
+ refresh = TRUE;
+ }
+ }
+
+ if (refresh && hwnd_frame != NULL)
+ PostMessage(hwnd_frame, WMU_REFRESH, 0, 0);
+}
+
+
+
+static bool IsProtocol(const char *module)
+{
+ PROTOACCOUNT **protos;
+ int count;
+
+ BOOL hasAccounts = ServiceExists(MS_PROTO_ENUMACCOUNTS);
+
+ if (hasAccounts)
+ CallService(MS_PROTO_ENUMACCOUNTS, (WPARAM)&count, (LPARAM)&protos);
+ else
+ CallService(MS_PROTO_ENUMPROTOCOLS, (WPARAM)&count, (LPARAM)&protos);
+
+ for (int i = 0; i < count; i++)
+ {
+ if (protos[i]->type != PROTOTYPE_PROTOCOL)
+ continue;
+
+ if (protos[i]->szModuleName == NULL || protos[i]->szModuleName[0] == '\0')
+ continue;
+
+ if (strcmp(module, protos[i]->szModuleName) == 0)
+ return true;
+ }
+
+ return false;
+}
+
+
+static INT_PTR VoiceRegister(WPARAM wParam, LPARAM lParam)
+{
+ VOICE_MODULE *in = (VOICE_MODULE *) wParam;
+ if (in == NULL || in->cbSize < sizeof(VOICE_MODULE) || in->name == NULL || in->description == NULL)
+ return -1;
+
+ if (FindModule(in->name) != NULL)
+ return -2;
+
+ if (!ProtoServiceExists(in->name, PS_VOICE_CALL)
+ || !ProtoServiceExists(in->name, PS_VOICE_ANSWERCALL)
+ || !ProtoServiceExists(in->name, PS_VOICE_DROPCALL))
+ return -3;
+
+ modules.insert(new VoiceProvider(in->name, in->description, in->flags, in->icon));
+
+ if (hwnd_frame != NULL)
+ PostMessage(hwnd_frame, WMU_REFRESH, 0, 0);
+
+ return 0;
+}
+
+
+static INT_PTR VoiceUnregister(WPARAM wParam, LPARAM lParam)
+{
+ char *moduleName = (char *) wParam;
+ if (moduleName == NULL || moduleName[0] == 0)
+ return -1;
+
+ VoiceProvider *module = FindModule(moduleName);
+ if (module == NULL)
+ return -2;
+
+ for(int i = calls.getCount() - 1; i >= 0; --i)
+ {
+ VoiceCall *call = &calls[i];
+
+ if (call->module == module)
+ {
+ call->Drop();
+ call->SetState(VOICE_STATE_ENDED);
+
+ calls.remove(i);
+ }
+ }
+
+ modules.remove(module);
+
+ if (hwnd_frame != NULL)
+ PostMessage(hwnd_frame, WMU_REFRESH, 0, 0);
+
+ return 0;
+}
+
+
+bool CanCall(HANDLE hContact, BOOL now)
+{
+ for(int i = 0; i < modules.getCount(); i++)
+ {
+ if (modules[i].CanCall(hContact, now))
+ return true;
+ }
+
+ return false;
+}
+
+
+bool CanCall(const TCHAR *number)
+{
+ for(int i = 0; i < modules.getCount(); i++)
+ {
+ if (modules[i].CanCall(number))
+ return true;
+ }
+
+ return false;
+}
+
+
+bool CanCallNumber()
+{
+ for(int i = 0; i < modules.getCount(); i++)
+ {
+ if (modules[i].flags & VOICE_CAPS_CALL_STRING)
+ return true;
+ }
+
+ return false;
+}
+
+
+bool IsFinalState(int state)
+{
+ return state == VOICE_STATE_ENDED || state == VOICE_STATE_BUSY;
+}
+
+
+VoiceCall * GetTalkingCall()
+{
+ for(int i = 0; i < calls.getCount(); ++i)
+ {
+ VoiceCall *call = &calls[i];
+
+ if (call->state == VOICE_STATE_TALKING)
+ return call;
+ }
+
+ return NULL;
+}
+
+
+static void HoldOtherCalls(VoiceCall *call)
+{
+ for(int i = 0; i < calls.getCount(); ++i)
+ {
+ VoiceCall *other = &calls[i];
+
+ if (other == call || other->state != VOICE_STATE_TALKING)
+ continue;
+
+ if (other->CanHold())
+ other->Hold();
+ else
+ other->Drop();
+ }
+}
+
+
+void Answer(VoiceCall *call)
+{
+ if (!call->CanAnswer())
+ return;
+
+ HoldOtherCalls(call);
+
+ // Now annswer it
+ call->Answer();
+}
+
+
+static INT_PTR VoiceState(WPARAM wParam, LPARAM lParam)
+{
+ VOICE_CALL *in = (VOICE_CALL *) wParam;
+ if (in == NULL || in->cbSize < sizeof(VOICE_CALL) || in->moduleName == NULL || in->id == NULL)
+ return 0;
+
+ // Check if the call is aready in list
+ VoiceCall *call = FindVoiceCall(in->moduleName, in->id, !IsFinalState(in->state));
+ if (call == NULL)
+ return 0;
+
+ call->AppendCallerID(in->hContact,
+ (in->flags & VOICE_UNICODE) ? WcharToTchar(in->pwszName).get() : CharToTchar(in->pszName).get(),
+ (in->flags & VOICE_UNICODE) ? WcharToTchar(in->pwszNumber).get() : CharToTchar(in->pszNumber).get());
+ call->secure = (in->flags & VOICE_SECURE) != 0;
+
+ if (in->state == VOICE_STATE_RINGING && call->hContact != NULL)
+ {
+ int aut = DBGetContactSettingWord(call->hContact, MODULE_NAME, "AutoAccept", AUTO_NOTHING);
+ if (aut == AUTO_ACCEPT || aut == AUTO_DROP)
+ {
+ call->state = VOICE_STATE_RINGING;
+ call->Notify(true, false, false, false);
+
+ if (aut == AUTO_ACCEPT)
+ Answer(call);
+ else
+ call->Drop();
+
+ return 0;
+ }
+ }
+
+ if (in->state == VOICE_STATE_TALKING)
+ HoldOtherCalls(call);
+
+ call->SetState(in->state);
+
+ return 0;
+}
+
+
+static INT_PTR IconsChanged(WPARAM wParam, LPARAM lParam)
+{
+ if (hwnd_frame != NULL)
+ PostMessage(hwnd_frame, WMU_REFRESH, 0, 0);
+
+ return 0;
+}
+
+
+static INT_PTR ReloadFont(WPARAM wParam, LPARAM lParam)
+{
+ FontIDT fi = {0};
+ fi.cbSize = sizeof(fi);
+ lstrcpyn(fi.group, TranslateT("Voice Calls"), MAX_REGS(fi.group));
+
+ font_max_height = 0;
+ for (int i = 0; i < NUM_FONTS; i++)
+ {
+ if (fonts[i] != 0) DeleteObject(fonts[i]);
+
+ lstrcpyn(fi.name, stateNames[i], MAX_REGS(fi.name));
+
+ LOGFONT log_font = {0};
+ font_colors[i] = CallService(MS_FONT_GETT, (WPARAM) &fi, (LPARAM) &log_font);
+ fonts[i] = CreateFontIndirect(&log_font);
+
+ font_max_height = max(font_max_height, log_font.lfHeight);
+ }
+
+ if (hwnd_frame != NULL)
+ PostMessage(hwnd_frame, WMU_REFRESH, 0, 0);
+
+ return 0;
+}
+
+
+static INT_PTR ReloadColor(WPARAM wParam, LPARAM lParam)
+{
+ ColourIDT ci = {0};
+ ci.cbSize = sizeof(ci);
+ lstrcpyn(ci.group, TranslateT("Voice Calls"), MAX_REGS(ci.group));
+ lstrcpyn(ci.name, TranslateT("Background"), MAX_REGS(ci.name));
+
+ bkg_color = CallService(MS_COLOUR_GETT, (WPARAM) &ci, 0);
+
+ if (bk_brush != NULL)
+ DeleteObject(bk_brush);
+ bk_brush = CreateSolidBrush(bkg_color);
+
+ if (hwnd_frame != NULL)
+ InvalidateRect(hwnd_frame, NULL, TRUE);
+
+ return 0;
+}
+
+
+static INT_PTR CListDblClick(WPARAM wParam, LPARAM lParam)
+{
+ CLISTEVENT *ce = (CLISTEVENT *) lParam;
+
+ VoiceCall *call = (VoiceCall *) ce->lParam;
+
+ HWND hwnd = CreateDialogParam(hInst, MAKEINTRESOURCE(IDD_NEW_CALL), NULL, DlgProcNewCall, (LPARAM) call);
+
+ ShowWindow(hwnd, SW_SHOWNORMAL);
+
+ call->SetNewCallHWND(hwnd);
+
+ return 0;
+}
+
+
+static INT_PTR Service_CanCall(WPARAM wParam, LPARAM lParam)
+{
+ HANDLE hContact = (HANDLE) wParam;
+ if (hContact == NULL)
+ return -1;
+
+ hContact = ConvertMetacontact(hContact);
+
+ return CanCall(hContact) ? 1 : 0;
+}
+
+
+static INT_PTR Service_Call(WPARAM wParam, LPARAM lParam)
+{
+ HANDLE hContact = (HANDLE) wParam;
+ if (hContact == NULL)
+ return -1;
+
+ hContact = ConvertMetacontact(hContact);
+
+ OBJLIST<CallingMethod> methods(10, sttCompareCallingMethods);
+ BuildCallingMethodsList(&methods, hContact);
+
+ if (methods.getCount() < 1)
+ return -2;
+
+ CallingMethod *method = &methods[0];
+ if (!IsEmpty(method->number))
+ return -2;
+
+ method->Call();
+
+ return 0;
+}
+
+
+static INT_PTR Service_CallItem(WPARAM wParam, LPARAM lParam, LPARAM param)
+{
+ HANDLE hContact = (HANDLE) wParam;
+ int index = (int) param;
+
+ if (hContact == NULL)
+ return -1;
+
+ hContact = ConvertMetacontact(hContact);
+
+ OBJLIST<CallingMethod> methods(10, sttCompareCallingMethods);
+ BuildCallingMethodsList(&methods, hContact);
+
+ if (index < 0 || index >= methods.getCount())
+ return -2;
+
+ methods[index].Call();
+
+ return 0;
+}
+
+
+static INT_PTR CMAnswer(WPARAM wParam, LPARAM lParam)
+{
+ HANDLE hContact = (HANDLE) wParam;
+ if (hContact == NULL)
+ return -1;
+
+ hContact = ConvertMetacontact(hContact);
+
+ VoiceCall *call = FindVoiceCall(hContact);
+ if (call != NULL)
+ Answer(call);
+
+ return 0;
+}
+
+
+static INT_PTR CMHold(WPARAM wParam, LPARAM lParam)
+{
+ HANDLE hContact = (HANDLE) wParam;
+ if (hContact == NULL)
+ return -1;
+
+ hContact = ConvertMetacontact(hContact);
+
+ VoiceCall *call = FindVoiceCall(hContact);
+ if (call != NULL)
+ call->Hold();
+
+ return 0;
+}
+
+
+static INT_PTR CMDrop(WPARAM wParam, LPARAM lParam)
+{
+ HANDLE hContact = (HANDLE) wParam;
+ if (hContact == NULL)
+ return -1;
+
+ hContact = ConvertMetacontact(hContact);
+
+ VoiceCall *call = FindVoiceCall(hContact);
+ if (call != NULL)
+ call->Drop();
+
+ return 0;
+}
+
+
+static void HideMenuItem(HANDLE hMenu)
+{
+ CLISTMENUITEM mi = {0};
+ mi.cbSize = sizeof(mi);
+ mi.flags = CMIM_FLAGS | CMIF_HIDDEN | CMIF_TCHAR;
+
+ CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM) hMenu, (LPARAM) &mi);
+}
+
+
+static void ShowMenuItem(HANDLE hMenu)
+{
+ CLISTMENUITEM mi = {0};
+ mi.cbSize = sizeof(mi);
+ mi.flags = CMIM_FLAGS | CMIF_TCHAR;
+
+ CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM) hMenu, (LPARAM) &mi);
+}
+
+
+static INT_PTR PreBuildContactMenu(WPARAM wParam, LPARAM lParam)
+{
+ HideMenuItem(hCMCall);
+ HideMenuItem(hCMAnswer);
+ HideMenuItem(hCMHold);
+ HideMenuItem(hCMDrop);
+ for(unsigned int i = 0; i < hCMCalls.size(); ++i)
+ HideMenuItem(hCMCalls[i]);
+
+ HANDLE hContact = (HANDLE) wParam;
+ if (hContact == NULL)
+ return -1;
+
+ hContact = ConvertMetacontact(hContact);
+
+ // There is a current call already?
+ VoiceCall *call = FindVoiceCall(hContact);
+ if (call == NULL)
+ {
+ OBJLIST<CallingMethod> methods(10, sttCompareCallingMethods);
+ BuildCallingMethodsList(&methods, hContact);
+
+ if (methods.getCount() == 1)
+ {
+ CallingMethod *method = &methods[0];
+
+ TCHAR name[128];
+ if (!IsEmpty(method->number))
+ mir_sntprintf(name, MAX_REGS(name), TranslateT("Call %s with %s"),
+ method->number, method->provider->description);
+ else
+ mir_sntprintf(name, MAX_REGS(name), TranslateT("Call with %s"),
+ method->provider->description);
+
+ CLISTMENUITEM mi = {0};
+ mi.cbSize = sizeof(mi);
+ mi.flags = CMIM_FLAGS | CMIF_TCHAR | CMIM_NAME;
+ mi.ptszName = name;
+ CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM) hCMCall, (LPARAM) &mi);
+ }
+ else if (methods.getCount() > 1)
+ {
+ CLISTMENUITEM mi = {0};
+ mi.cbSize = sizeof(mi);
+ mi.flags = CMIM_FLAGS | CMIF_TCHAR | CMIM_NAME;
+ mi.ptszName = _T("Call");
+ CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM) hCMCall, (LPARAM) &mi);
+
+ for(int i = 0; i < methods.getCount(); ++i)
+ {
+ CallingMethod *method = &methods[i];
+
+ HICON hIcon = method->provider->GetIcon();
+
+ TCHAR name[128];
+ if (!IsEmpty(method->number))
+ mir_sntprintf(name, MAX_REGS(name), TranslateT("%s with %s"),
+ method->number, method->provider->description);
+ else
+ mir_sntprintf(name, MAX_REGS(name), TranslateT("with %s"),
+ method->provider->description);
+
+ char service[128];
+ mir_snprintf(service, MAX_REGS(service), "VoiceService/ContactMenu/Call_%d", i);
+
+ CLISTMENUITEM mi = {0};
+ mi.cbSize = sizeof(mi);
+ mi.position = i;
+ mi.flags = CMIF_TCHAR | CMIF_ROOTHANDLE;
+ mi.ptszName = name;
+ mi.hIcon = hIcon;
+ mi.pszService = service;
+ mi.hParentMenu = (HGENMENU) hCMCall;
+
+ if (i == hCMCalls.size())
+ {
+ CreateServiceFunctionParam(service, Service_CallItem, i);
+
+ HANDLE hMenu = (HANDLE) CallService(MS_CLIST_ADDCONTACTMENUITEM, 0, (LPARAM) &mi);
+
+ hCMCalls.push_back(hMenu);
+ }
+ else
+ {
+ HANDLE hMenu = hCMCalls[i];
+
+ mi.flags = mi.flags | CMIM_ALL;
+
+ CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM) hMenu, (LPARAM) &mi);
+ }
+
+ method->provider->ReleaseIcon(hIcon);
+ }
+ }
+ }
+ else
+ {
+ switch (call->state)
+ {
+ case VOICE_STATE_CALLING:
+ {
+ ShowMenuItem(hCMDrop);
+ break;
+ }
+ case VOICE_STATE_TALKING:
+ {
+ if (call->module->CanHold())
+ ShowMenuItem(hCMHold);
+ ShowMenuItem(hCMDrop);
+ break;
+ }
+ case VOICE_STATE_RINGING:
+ case VOICE_STATE_ON_HOLD:
+ {
+ ShowMenuItem(hCMAnswer);
+ ShowMenuItem(hCMDrop);
+ break;
+ }
+ }
+ }
+
+ return 0;
+}
+
+
+static BOOL CALLBACK DlgProcNewCall(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg)
+ {
+ case WM_INITDIALOG:
+ {
+ VoiceCall *call = (VoiceCall *) lParam;
+
+ TranslateDialogDefault(hwndDlg);
+
+ TCHAR text[1024];
+
+ VoiceCall *currentCall = GetTalkingCall();
+ if (currentCall == NULL)
+ {
+ mir_sntprintf(text, MAX_REGS(text), TranslateT("%s wants to start a voice call with you. What you want to do?"),
+ call->displayName);
+ }
+ else if (currentCall->CanHold())
+ {
+ mir_sntprintf(text, MAX_REGS(text), TranslateT("%s wants to start a voice call with you. What you want to do?\n\nIf you answer the call, the current call will be put on hold."),
+ call->displayName);
+ }
+ else
+ {
+ mir_sntprintf(text, MAX_REGS(text), TranslateT("%s wants to start a voice call with you. What you want to do?\n\nIf you answer the call, the current call will be dropped."),
+ call->displayName);
+ }
+
+ SendMessage(GetDlgItem(hwndDlg, IDC_TEXT), WM_SETTEXT, 0, (LPARAM) text);
+
+ HICON hIcon = IcoLib_LoadIcon(stateIcons[VOICE_STATE_RINGING]);
+ SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM) hIcon);
+ IcoLib_ReleaseIcon(hIcon);
+
+ if (call->hContact == NULL)
+ ShowWindow(GetDlgItem(hwndDlg, IDC_AUTO), SW_HIDE);
+
+ SetWindowLong(hwndDlg, GWL_USERDATA, (LONG) call);
+
+ return TRUE;
+ }
+
+ case WM_COMMAND:
+ {
+ switch(wParam)
+ {
+ case ID_ANSWER:
+ {
+ VoiceCall *call = (VoiceCall *) GetWindowLong(hwndDlg, GWL_USERDATA);
+
+ if (call->hContact != NULL && IsDlgButtonChecked(hwndDlg, IDC_AUTO))
+ DBWriteContactSettingWord(call->hContact, MODULE_NAME, "AutoAccept", AUTO_ACCEPT);
+
+ Answer(call);
+
+ DestroyWindow(hwndDlg);
+ break;
+ }
+ case ID_DROP:
+ {
+ VoiceCall *call = (VoiceCall *) GetWindowLong(hwndDlg, GWL_USERDATA);
+
+ if (call->hContact != NULL && IsDlgButtonChecked(hwndDlg, IDC_AUTO))
+ DBWriteContactSettingWord(call->hContact, MODULE_NAME, "AutoAccept", AUTO_DROP);
+
+ call->Drop();
+
+ DestroyWindow(hwndDlg);
+ break;
+ }
+ }
+ break;
+ }
+
+ case WM_CLOSE:
+ {
+ VoiceCall *call = (VoiceCall *) GetWindowLong(hwndDlg, GWL_USERDATA);
+ call->Notify(false, false, false, true);
+
+ DestroyWindow(hwndDlg);
+ break;
+ }
+
+ case WM_DESTROY:
+ {
+ VoiceCall *call = (VoiceCall *) GetWindowLong(hwndDlg, GWL_USERDATA);
+ call->SetNewCallHWND(NULL);
+ break;
+ }
+ }
+
+ return FALSE;
+}
+
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+
+VoiceProvider::VoiceProvider(const char *name, const TCHAR *description, int flags, const char *icon)
+{
+ strncpy(this->name, name, MAX_REGS(this->name));
+ this->name[MAX_REGS(this->name)-1] = 0;
+
+ lstrcpyn(this->description, description, MAX_REGS(this->description));
+
+ if (icon == NULL)
+ this->icon[0] = 0;
+ else
+ lstrcpynA(this->icon, icon, MAX_REGS(this->icon));
+
+ this->flags = flags;
+ is_protocol = IsProtocol(name);
+ canHold = (ProtoServiceExists(name, PS_VOICE_HOLDCALL) != 0);
+
+ char str[MAXMODULELABELLENGTH];
+ mir_snprintf(str, MAX_REGS(str), "%s%s", name, PE_VOICE_CALL_STATE);
+ state_hook = HookEvent(str, VoiceState);
+}
+
+
+VoiceProvider::~VoiceProvider()
+{
+ UnhookEvent(state_hook);
+ state_hook = NULL;
+}
+
+
+bool VoiceProvider::CanCall(HANDLE hContact, BOOL now)
+{
+ if (hContact == NULL)
+ return false;
+
+ if ((flags & VOICE_CAPS_CALL_CONTACT) == 0)
+ return false;
+
+ if (ProtoServiceExists(name, PS_VOICE_CALL_CONTACT_VALID))
+ return CallProtoService(name, PS_VOICE_CALL_CONTACT_VALID, (WPARAM) hContact, now) != FALSE;
+
+ if (is_protocol)
+ {
+ if (now && CallProtoService(name, PS_GETSTATUS, 0, 0) <= ID_STATUS_OFFLINE)
+ return false;
+
+ if (!CallService(MS_PROTO_ISPROTOONCONTACT, (WPARAM) hContact, (LPARAM) name))
+ return false;
+
+ return DBGetContactSettingWord(hContact, name, "Status", ID_STATUS_OFFLINE) > ID_STATUS_OFFLINE;
+ }
+
+ return true;
+}
+
+bool VoiceProvider::CanCall(const TCHAR *number)
+{
+ if (number == NULL || number[0] == 0)
+ return false;
+
+ if ((flags & VOICE_CAPS_CALL_STRING) == 0)
+ return false;
+
+ if (ProtoServiceExists(name, PS_VOICE_CALL_STRING_VALID))
+ return CallProtoService(name, PS_VOICE_CALL_STRING_VALID, (WPARAM) number, 0) != FALSE;
+
+ if (is_protocol)
+ {
+ return CallProtoService(name, PS_GETSTATUS, 0, 0) > ID_STATUS_OFFLINE;
+ }
+
+ return true;
+}
+
+bool VoiceProvider::CanHold()
+{
+ return canHold;
+}
+
+bool VoiceProvider::CanSendDTMF()
+{
+ return ProtoServiceExists(name, PS_VOICE_SEND_DTMF) != FALSE;
+}
+
+void VoiceProvider::Call(HANDLE hContact, const TCHAR *number)
+{
+ CallProtoService(name, PS_VOICE_CALL, (WPARAM) hContact, (LPARAM) number);
+}
+
+HICON VoiceProvider::GetIcon()
+{
+ if (!IsEmptyA(icon))
+ {
+ return IcoLib_LoadIcon(icon);
+ }
+ else if (is_protocol)
+ {
+ return LoadSkinnedProtoIcon(name, ID_STATUS_ONLINE);
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+void VoiceProvider::ReleaseIcon(HICON hIcon)
+{
+ if (hIcon == NULL)
+ return;
+
+ if (!IsEmptyA(icon))
+ IcoLib_ReleaseIcon(hIcon);
+}
+
+
+
+
+VoiceCall::VoiceCall(VoiceProvider *module, const char *id)
+ : module(module), id(mir_strdup(id))
+{
+ hContact = NULL;
+ name[0] = 0;
+ number[0] = 0;
+ displayName[0] = 0;
+ state = -1;
+ end_time = 0;
+ clistBlinking = false;
+ incoming = false;
+ secure = false;
+ hwnd = NULL;
+ CreateDisplayName();
+}
+
+VoiceCall::~VoiceCall()
+{
+ RemoveNotifications();
+ mir_free(id);
+ id = NULL;
+}
+
+void VoiceCall::AppendCallerID(HANDLE aHContact, const TCHAR *aName, const TCHAR *aNumber)
+{
+ bool changed = false;
+
+ if (aHContact != NULL)
+ {
+ hContact = aHContact;
+ changed = true;
+ }
+
+ if (!IsEmpty(aName))
+ {
+ lstrcpyn(name, aName, MAX_REGS(name));
+ changed = true;
+ }
+
+ if (!IsEmpty(aNumber))
+ {
+ lstrcpyn(number, aNumber, MAX_REGS(number));
+ changed = true;
+ }
+
+ if (changed)
+ CreateDisplayName();
+}
+
+void VoiceCall::CreateDisplayName()
+{
+ TCHAR *contact = NULL;
+ if (hContact != NULL)
+ contact = (TCHAR *) CallService(MS_CLIST_GETCONTACTDISPLAYNAME, (WPARAM) hContact, GCDNF_TCHAR);
+
+ TCHAR *nameTmp = NULL;
+ if (lstrcmp(name, number) != 0)
+ nameTmp = name;
+
+ if (contact != NULL)
+ {
+ if (!IsEmpty(number))
+ mir_sntprintf(displayName, MAX_REGS(displayName), _T("%s <%s>"), contact, number);
+ else
+ lstrcpyn(displayName, contact, MAX_REGS(displayName));
+ }
+ else if (!IsEmpty(nameTmp) && !IsEmpty(number))
+ {
+ mir_sntprintf(displayName, MAX_REGS(displayName), _T("%s <%s>"), name, number);
+ }
+ else if (!IsEmpty(nameTmp))
+ {
+ lstrcpyn(displayName, name, MAX_REGS(displayName));
+ }
+ else if (!IsEmpty(number))
+ {
+ lstrcpyn(displayName, number, MAX_REGS(displayName));
+ }
+ else
+ {
+ lstrcpyn(displayName, TranslateT("Unknown number"), MAX_REGS(displayName));
+ }
+}
+
+void VoiceCall::RemoveNotifications()
+{
+ if (hwnd != NULL)
+ {
+ DestroyWindow(hwnd);
+ hwnd = NULL;
+ }
+
+ if (clistBlinking)
+ {
+ CallService(MS_CLIST_REMOVEEVENT, (WPARAM) hContact, (LPARAM) this);
+ clistBlinking = false;
+ }
+}
+
+void VoiceCall::SetState(int aState)
+{
+ if (state == aState)
+ return;
+
+ if (aState == VOICE_STATE_RINGING)
+ incoming = true;
+ else if (aState == VOICE_STATE_CALLING)
+ incoming = false;
+
+ RemoveNotifications();
+
+ state = aState;
+
+ if (IsFinished())
+ {
+ if (end_time == 0)
+ end_time = GetTickCount();
+
+ // Remove id because providers can re-use them
+ mir_free(id);
+ id = NULL;
+ }
+
+ Notify();
+}
+
+
+void VoiceCall::Notify(bool history, bool popup, bool sound, bool clist)
+{
+ if (history)
+ {
+ TCHAR *variables[] = {
+ _T("number"), displayName
+ };
+ HistoryEvents_AddToHistoryVars(hContact, EVENTTYPE_VOICE_CALL, state, variables, MAX_REGS(variables),
+ DBEF_READ | (incoming ? 0 : DBEF_SENT));
+ }
+
+ if (popup)
+ {
+ TCHAR text[512];
+ mir_sntprintf(text, MAX_REGS(text), TranslateTS(stateTexts[state]), displayName);
+
+ ShowPopup(NULL, TranslateTS(popupTitles[state]), text);
+ }
+
+ if (sound)
+ SkinPlaySound(sounds[state].name);
+
+ if (clist && state == VOICE_STATE_RINGING)
+ {
+ HICON hIcon = IcoLib_LoadIcon(stateIcons[VOICE_STATE_RINGING]);
+
+ CLISTEVENT ce = {0};
+ ce.cbSize = sizeof(ce);
+ ce.hContact = hContact;
+ ce.hIcon = hIcon;
+ ce.hDbEvent = (HANDLE) this;
+ ce.pszService = MS_VOICESERVICE_CLIST_DBLCLK;
+ ce.lParam = (LPARAM) this;
+ CallService(MS_CLIST_ADDEVENT, 0, (LPARAM) &ce);
+
+ IcoLib_ReleaseIcon(hIcon);
+
+ clistBlinking = true;
+ }
+
+ if (hwnd_frame != NULL)
+ PostMessage(hwnd_frame, WMU_REFRESH, 0, 0);
+}
+
+
+bool VoiceCall::IsFinished()
+{
+ return IsFinalState(state);
+}
+
+
+bool VoiceCall::CanDrop()
+{
+ return !IsFinished();
+}
+
+void VoiceCall::Drop()
+{
+ if (!CanDrop())
+ return;
+
+ RemoveNotifications();
+
+ CallProtoService(module->name, PS_VOICE_DROPCALL, (WPARAM) id, 0);
+}
+
+
+bool VoiceCall::CanAnswer()
+{
+ return state == -1 || state == VOICE_STATE_RINGING || state == VOICE_STATE_ON_HOLD;
+}
+
+void VoiceCall::Answer()
+{
+ if (!CanAnswer())
+ return;
+
+ RemoveNotifications();
+
+ CallProtoService(module->name, PS_VOICE_ANSWERCALL, (WPARAM) id, 0);
+}
+
+
+bool VoiceCall::CanHold()
+{
+ return module->CanHold() && (state == -1 || state == VOICE_STATE_TALKING);
+}
+
+void VoiceCall::Hold()
+{
+ if (!CanHold())
+ return;
+
+ RemoveNotifications();
+
+ CallProtoService(module->name, PS_VOICE_HOLDCALL, (WPARAM) id, 0);
+}
+
+bool VoiceCall::CanSendDTMF()
+{
+ return module->CanSendDTMF() && state == VOICE_STATE_TALKING;
+}
+
+void VoiceCall::SendDTMF(TCHAR c)
+{
+ if (!CanSendDTMF())
+ return;
+
+ CallProtoService(module->name, PS_VOICE_SEND_DTMF, (WPARAM) id, (LPARAM) c);
+}
+
+
+
+void VoiceCall::SetNewCallHWND(HWND hwnd)
+{
+ if (hwnd != NULL)
+ RemoveNotifications();
+
+ this->hwnd = hwnd;
+} \ No newline at end of file
diff --git a/Plugins/voiceservice/voiceservice.dsp b/Plugins/voiceservice/voiceservice.dsp
new file mode 100644
index 0000000..ba34588
--- /dev/null
+++ b/Plugins/voiceservice/voiceservice.dsp
@@ -0,0 +1,319 @@
+# Microsoft Developer Studio Project File - Name="voiceservice" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=voiceservice - Win32 Release
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "voiceservice.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "voiceservice.mak" CFG="voiceservice - Win32 Release"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "voiceservice - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "voiceservice - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "voiceservice - Win32 Unicode Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "voiceservice - Win32 Unicode Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "voiceservice - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MD /W3 /GX /O1 /YX /FD /c
+# SUBTRACT BASE CPP /Fr
+# ADD CPP /nologo /G5 /MT /W3 /GX /O2 /Ob0 /I "../../include" /I "sdk" /I "lib/portaudio" /D "WIN32" /D "W32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /Fr /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x417 /d "NDEBUG"
+# ADD RSC /l 0x417 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 user32.lib shell32.lib wininet.lib gdi32.lib /nologo /base:"0x67100000" /dll /machine:I386 /filealign:0x200
+# SUBTRACT BASE LINK32 /pdb:none /map
+# ADD LINK32 libportaudio.lib kernel32.lib user32.lib gdi32.lib comctl32.lib /nologo /base:"0x3EC40000" /dll /map /debug /debugtype:both /machine:I386 /out:"..\..\bin\release\Plugins\voiceservice.dll" /pdbtype:sept /libpath:"lib\portaudio\Release" /filealign:0x200 /ALIGN:4096 /ignore:4108
+# SUBTRACT LINK32 /profile /pdb:none
+
+!ELSEIF "$(CFG)" == "voiceservice - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Ignore_Export_Lib 0
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /G4 /MT /W3 /GX /O2 /Ob0 /I "../../include" /FR /YX /FD /c
+# ADD CPP /nologo /G5 /MDd /W3 /GX /ZI /Od /I "../../include" /I "sdk" /I "lib/portaudio" /D "WIN32" /D "W32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /FR /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x417 /d "NDEBUG"
+# ADD RSC /l 0x417 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 comctl32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 /out:"..\..bin\release\Plugins\voiceservice.dll" /filealign:0x200 /ALIGN:4096 /ignore:4108
+# SUBTRACT BASE LINK32 /profile /pdb:none
+# ADD LINK32 libportaudio.lib kernel32.lib user32.lib gdi32.lib comctl32.lib /nologo /base:"0x3EC40000" /dll /incremental:yes /debug /machine:I386 /out:"..\..\bin\debug\Plugins\voiceservice.dll" /libpath:"lib\portaudio\Debug" /filealign:0x200 /ALIGN:4096 /ignore:4108
+# SUBTRACT LINK32 /profile /pdb:none
+
+!ELSEIF "$(CFG)" == "voiceservice - Win32 Unicode Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "voiceservice___Win32_Unicode_Debug"
+# PROP BASE Intermediate_Dir "voiceservice___Win32_Unicode_Debug"
+# PROP BASE Ignore_Export_Lib 0
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Unicode_Debug"
+# PROP Intermediate_Dir "Unicode_Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /G4 /MTd /W3 /GX /ZI /Od /I "../../include" /FR /YX /FD /c
+# ADD CPP /nologo /G5 /MDd /W3 /GX /ZI /Od /I "../../include" /I "sdk" /I "lib/portaudio" /D "WIN32" /D "W32" /D "_DEBUG" /D "_WINDOWS" /D "_UNICODE" /D "UNICODE" /D "_USRDLL" /FR /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x417 /d "NDEBUG"
+# ADD RSC /l 0x417 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 comctl32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /base:"0x32100000" /dll /incremental:yes /debug /machine:I386 /out:"..\..\bin\debug\Plugins\voiceservice.dll" /filealign:0x200 /ALIGN:4096 /ignore:4108
+# SUBTRACT BASE LINK32 /profile /pdb:none
+# ADD LINK32 libportaudio.lib kernel32.lib user32.lib gdi32.lib comctl32.lib /nologo /base:"0x3EC40000" /dll /incremental:yes /debug /machine:I386 /out:"..\..\bin\debug unicode\Plugins\voiceserviceW.dll" /libpath:"lib\portaudio\Debug" /filealign:0x200 /ALIGN:4096 /ignore:4108
+# SUBTRACT LINK32 /profile /pdb:none
+
+!ELSEIF "$(CFG)" == "voiceservice - Win32 Unicode Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "voiceservice___Win32_Unicode_Release"
+# PROP BASE Intermediate_Dir "voiceservice___Win32_Unicode_Release"
+# PROP BASE Ignore_Export_Lib 0
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Unicode_Release"
+# PROP Intermediate_Dir "Unicode_Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /G4 /MT /W3 /GX /O2 /Ob0 /I "../../include" /Fr /YX /FD /c
+# ADD CPP /nologo /G5 /MT /W3 /GX /O2 /Ob0 /I "../../include" /I "sdk" /I "lib/portaudio" /D "WIN32" /D "W32" /D "NDEBUG" /D "_WINDOWS" /D "_UNICODE" /D "UNICODE" /D "_USRDLL" /Fr /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x417 /d "NDEBUG"
+# ADD RSC /l 0x417 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 comctl32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /base:"0x32100000" /dll /map /machine:I386 /out:"..\..\bin\release\Plugins\voiceservice.dll" /filealign:0x200 /ALIGN:4096 /ignore:4108
+# SUBTRACT BASE LINK32 /profile /pdb:none
+# ADD LINK32 libportaudio.lib kernel32.lib user32.lib gdi32.lib comctl32.lib /nologo /base:"0x3EC40000" /dll /map /debug /debugtype:both /machine:I386 /out:"..\..\bin\release\Plugins\voiceserviceW.dll" /pdbtype:sept /libpath:"lib\portaudio\Release" /filealign:0x200 /ALIGN:4096 /ignore:4108
+# SUBTRACT LINK32 /profile /pdb:none
+
+!ENDIF
+
+# Begin Target
+
+# Name "voiceservice - Win32 Release"
+# Name "voiceservice - Win32 Debug"
+# Name "voiceservice - Win32 Unicode Debug"
+# Name "voiceservice - Win32 Unicode Release"
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=.\commons.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\frame.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\m_voice.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\m_voiceservice.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\utils\mir_dbutils.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\utils\mir_icons.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\utils\mir_memory.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\utils\mir_options.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\options.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\popup.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\resource.h
+# End Source File
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
+# Begin Source File
+
+SOURCE=.\res\Answer.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\res\Busy.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\res\Call.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\res\Calling.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\res\dialpad.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\res\Drop.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\res\ended.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\res\Hold.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\res\Main.ico
+# End Source File
+# Begin Source File
+
+SOURCE=".\res\On hold.ico"
+# End Source File
+# Begin Source File
+
+SOURCE=.\resource.rc
+# End Source File
+# Begin Source File
+
+SOURCE=.\res\Rinning.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\res\secure.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\res\smalldot.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\res\Talking.ico
+# End Source File
+# End Group
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=.\frame.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\utils\mir_icons.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\utils\mir_options.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\options.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\popup.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\voiceservice.cpp
+# End Source File
+# End Group
+# Begin Group "Docs"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=.\Docs\langpack_voiceservice.txt
+# End Source File
+# Begin Source File
+
+SOURCE=.\Docs\voiceservice_changelog.txt
+# End Source File
+# Begin Source File
+
+SOURCE=.\Docs\voiceservice_readme.txt
+# End Source File
+# Begin Source File
+
+SOURCE=.\Docs\voiceservice_version.txt
+# End Source File
+# End Group
+# End Target
+# End Project
diff --git a/Plugins/voiceservice/voiceservice.dsw b/Plugins/voiceservice/voiceservice.dsw
new file mode 100644
index 0000000..593eba5
--- /dev/null
+++ b/Plugins/voiceservice/voiceservice.dsw
@@ -0,0 +1,29 @@
+Microsoft Developer Studio Workspace File, Format Version 6.00
+# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
+
+###############################################################################
+
+Project: "voiceservice"=.\voiceservice.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Global:
+
+Package=<5>
+{{{
+}}}
+
+Package=<3>
+{{{
+}}}
+
+###############################################################################
+